From edad497fd111ea518144230c43687107ea4746e7 Mon Sep 17 00:00:00 2001 From: Ge Gao Date: Mon, 19 Oct 2026 06:18:36 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9E=20fix:=20Restore=20enclosing=20def?= =?UTF-8?q?ault=20handler=20frames?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/effected.ts | 3 +- test/effected.spec.ts | 93 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/effected.ts b/src/effected.ts index 3b1bc99..1b6ae86 100644 --- a/src/effected.ts +++ b/src/effected.ts @@ -2825,11 +2825,12 @@ const handleEffect = ( return { done: false, value: constructHandledEffect() } as never; const iter = Symbol.iterator in handlerResult ? handlerResult[Symbol.iterator]() : handlerResult; + const parentInterceptIterator = context.interceptIterator; context.interceptIterator = { next: (...args: [] | [unknown]) => { const result = iter.next(...args); if (result.done) { - context.interceptIterator = null; + context.interceptIterator = parentInterceptIterator; return { done: false, value: constructHandledEffect() } as never; } return result as never; diff --git a/test/effected.spec.ts b/test/effected.spec.ts index 6f39193..377cf01 100644 --- a/test/effected.spec.ts +++ b/test/effected.spec.ts @@ -621,6 +621,99 @@ describe("effected", () => { }); describe("Default handlers", () => { + it.each(["runSync", "runAsync"] as const)( + "should restore a parent default handler after a nested handler completes with %s", + async (run) => { + const events: string[] = []; + const inner = effect<[], number>()("inner", { + *defaultHandler({ resume }) { + resume(2); + events.push("inner:end"); + }, + }); + const outer = effect<[], number>()("outer", { + *defaultHandler({ resume }) { + try { + resume(1); + } finally { + events.push("outer:finally:start"); + const value = yield* inner(); + events.push(`outer:finally:end:${value}`); + } + events.push("outer:end"); + }, + }); + + expect(await outer()[run]()).toBe(1); + expect(events).toEqual([ + "outer:finally:start", + "inner:end", + "outer:finally:end:2", + "outer:end", + ]); + }, + ); + + it("should restore nested default handlers across asynchronous waits", async () => { + const events: string[] = []; + const inner = effect<[], number>()("inner", { + *defaultHandler({ resume }) { + const value = yield* effectify(Promise.resolve(3)); + resume(value); + events.push("inner:end"); + }, + }); + const middle = effect<[], number>()("middle", { + *defaultHandler({ resume }) { + const value = yield* inner(); + yield* effectify(Promise.resolve()); + resume(value + 2); + events.push("middle:end"); + }, + }); + const outer = effect<[], number>()("outer", { + *defaultHandler({ resume }) { + yield* effectify(Promise.resolve()); + const value = yield* middle(); + resume(value + 1); + events.push("outer:end"); + }, + }); + const program = effected(function* () { + const first = yield* outer(); + const second = yield* outer(); + return [first, second]; + }); + + expect(await program.runAsync()).toEqual([6, 6]); + expect(events).toEqual([ + "inner:end", + "middle:end", + "outer:end", + "inner:end", + "middle:end", + "outer:end", + ]); + }); + + it("should restore default handlers that return effected programs", () => { + const inner = effect<[], number>()("inner", { + defaultHandler: ({ resume }) => + effected(function* () { + resume(2); + }), + }); + const outer = effect<[], number>()("outer", { + defaultHandler: ({ resume }) => + effected(function* () { + const value = yield* inner(); + resume(value + 1); + }), + }); + + expect(outer().runSync()).toBe(3); + }); + it("should apply default handlers when no explicit handler is provided", () => { const logs: string[] = [];