@@ -250,6 +250,86 @@ describe('SFC <script setup> helpers', () => {
250250 expect ( serializeInner ( root ) ) . toBe ( 'hello' )
251251 } )
252252
253+ test ( 'should not leak instance to user microtasks after restore' , async ( ) => {
254+ let leakedToUserMicrotask = false
255+
256+ const Comp = defineComponent ( {
257+ async setup ( ) {
258+ let __temp : any , __restore : any
259+ ; [ __temp , __restore ] = withAsyncContext ( ( ) => Promise . resolve ( ) )
260+ __temp = await __temp
261+ __restore ( )
262+
263+ Promise . resolve ( ) . then ( ( ) => {
264+ leakedToUserMicrotask = getCurrentInstance ( ) !== null
265+ } )
266+
267+ return ( ) => ''
268+ } ,
269+ } )
270+
271+ const root = nodeOps . createElement ( 'div' )
272+ render (
273+ h ( ( ) => h ( Suspense , ( ) => h ( Comp ) ) ) ,
274+ root ,
275+ )
276+
277+ await new Promise ( r => setTimeout ( r ) )
278+ expect ( leakedToUserMicrotask ) . toBe ( false )
279+ } )
280+
281+ test ( 'should not leak sibling instance in concurrent restores' , async ( ) => {
282+ let resolveOne : ( ) => void
283+ let resolveTwo : ( ) => void
284+ let done ! : ( ) => void
285+ let pending = 2
286+ const ready = new Promise < void > ( r => {
287+ done = r
288+ } )
289+ const seenUid : Record < 'one' | 'two' , number | null > = {
290+ one : null ,
291+ two : null ,
292+ }
293+
294+ const makeComp = ( name : 'one' | 'two' , wait : Promise < void > ) =>
295+ defineComponent ( {
296+ async setup ( ) {
297+ let __temp : any , __restore : any
298+ ; [ __temp , __restore ] = withAsyncContext ( ( ) => wait )
299+ __temp = await __temp
300+ __restore ( )
301+
302+ Promise . resolve ( ) . then ( ( ) => {
303+ seenUid [ name ] = getCurrentInstance ( ) ?. uid ?? null
304+ if ( -- pending === 0 ) done ( )
305+ } )
306+
307+ return ( ) => ''
308+ } ,
309+ } )
310+
311+ const oneReady = new Promise < void > ( r => {
312+ resolveOne = r
313+ } )
314+ const twoReady = new Promise < void > ( r => {
315+ resolveTwo = r
316+ } )
317+ const CompOne = makeComp ( 'one' , oneReady )
318+ const CompTwo = makeComp ( 'two' , twoReady )
319+
320+ const root = nodeOps . createElement ( 'div' )
321+ render (
322+ h ( ( ) => h ( Suspense , ( ) => h ( 'div' , [ h ( CompOne ) , h ( CompTwo ) ] ) ) ) ,
323+ root ,
324+ )
325+
326+ resolveOne ! ( )
327+ resolveTwo ! ( )
328+ await ready
329+ expect ( seenUid . one ) . toBeNull ( )
330+ expect ( seenUid . two ) . toBeNull ( )
331+ } )
332+
253333 test ( 'error handling' , async ( ) => {
254334 const spy = vi . fn ( )
255335
@@ -295,6 +375,8 @@ describe('SFC <script setup> helpers', () => {
295375 expect ( spy ) . toHaveBeenCalled ( )
296376 // should retain same instance before/after the await call
297377 expect ( beforeInstance ) . toBe ( afterInstance )
378+ // instance scope should be fully restored/cleaned after async ticks
379+ expect ( ( beforeInstance ! . scope as any ) . _on ) . toBe ( 0 )
298380 } )
299381
300382 test ( 'should not leak instance on multiple awaits' , async ( ) => {
0 commit comments