44 loadPlugin ,
55 markChanged ,
66 prepareCopy ,
7+ handleCrossReference ,
78 ProxyArrayState
89} from "../internal"
910
@@ -189,6 +190,30 @@ export function enableArrayMethods() {
189190 return Math . min ( index , length )
190191 }
191192
193+ /**
194+ * Calls handleCrossReference for each value being inserted into the array,
195+ * and marks the corresponding indices as assigned in `assigned_`.
196+ *
197+ * This ensures nested drafts inside inserted values (e.g. from spreading
198+ * a draft object) are properly finalized, matching the behavior of the
199+ * proxy set trap which calls handleCrossReference on every assignment.
200+ *
201+ * Without this, values containing draft proxies (like `{...state[0]}`)
202+ * pushed via the array methods plugin would have their nested drafts
203+ * revoked during finalization without being replaced by final values.
204+ */
205+ function handleInsertedValues (
206+ state : ProxyArrayState ,
207+ startIndex : number ,
208+ values : any [ ]
209+ ) {
210+ for ( let i = 0 ; i < values . length ; i ++ ) {
211+ const index = startIndex + i
212+ state . assigned_ ! . set ( index , true )
213+ handleCrossReference ( state , index , values [ i ] )
214+ }
215+ }
216+
192217 /**
193218 * Handles mutating operations that add/remove elements (push, pop, shift, unshift, splice).
194219 *
@@ -204,13 +229,25 @@ export function enableArrayMethods() {
204229 args : any [ ]
205230 ) {
206231 return executeArrayMethod ( state , ( ) => {
232+ // For push/unshift, capture the length before the operation
233+ // so we can compute insertion indices for handleCrossReference
234+ const lengthBefore = state . copy_ ! . length
235+
207236 const result = ( state . copy_ ! as any ) [ method ] ( ...args )
208237
209238 // Handle index reassignment for shifting methods
210239 if ( SHIFTING_METHODS . has ( method as MutatingArrayMethod ) ) {
211240 markAllIndicesReassigned ( state )
212241 }
213242
243+ // Handle cross-references for newly inserted values.
244+ // push appends at the end, unshift inserts at the beginning.
245+ if ( method === "push" && args . length > 0 ) {
246+ handleInsertedValues ( state , lengthBefore , args )
247+ } else if ( method === "unshift" && args . length > 0 ) {
248+ handleInsertedValues ( state , 0 , args )
249+ }
250+
214251 // Return appropriate value based on method
215252 return RESULT_RETURNING_METHODS . has ( method as MutatingArrayMethod )
216253 ? result
@@ -285,6 +322,14 @@ export function enableArrayMethods() {
285322 state . copy_ ! . splice ( ...( args as [ number , number , ...any [ ] ] ) )
286323 )
287324 markAllIndicesReassigned ( state )
325+ // Handle cross-references for inserted values (args from index 2+)
326+ if ( args . length > 2 ) {
327+ const startIndex = normalizeSliceIndex (
328+ args [ 0 ] ?? 0 ,
329+ state . copy_ ! . length
330+ )
331+ handleInsertedValues ( state , startIndex , args . slice ( 2 ) )
332+ }
288333 return res
289334 }
290335 } else {
0 commit comments