diff --git a/lib/api-internal.ts b/lib/api-internal.ts index b49d5e808..7e79523f6 100644 --- a/lib/api-internal.ts +++ b/lib/api-internal.ts @@ -87,22 +87,27 @@ export function decorateApi( ) { // read existing total and local let current = scope.get(api.context) ?? { total: {}, local: {} }; + let hasOwnContext = scope.hasOwn(api.context); - let local = decorate(current.local, { + let total = hasOwnContext + ? current.total + : decorate(current.total, current.local); + + let local = decorate(hasOwnContext ? current.local : {}, { [options?.at ?? "max"]: decorator, }); - if (!scope.hasOwn(api.context)) { + if (!hasOwnContext) { scope.set(api.context, { local, - total: current.total, + total, handle: api.core, }); } else { current.local = local; } - install(scope, api, current.total); + install(scope, api, total); } function decorate(base: Decorator, next: Decorator): Decorator { diff --git a/test/api.test.ts b/test/api.test.ts index 6e9083445..79a124277 100644 --- a/test/api.test.ts +++ b/test/api.test.ts @@ -289,6 +289,59 @@ describe("api", () => { }); }); + it("does not duplicate existing ancestor middleware when propagating later additions", async () => { + let api = createApi("test", { + *test(order: string[]): Operation { + return order; + }, + }); + + await run(function* () { + yield* api.around({ + *test(args, next) { + let [input] = args; + let output = yield* next(input.concat("parent-before")); + return output.concat("/parent-before"); + }, + }); + + let tester = yield* resource<{ test(): Operation }>( + function* (provide) { + let scope = yield* useScope(); + yield* api.around({ + *test(args, next) { + let [input] = args; + let output = yield* next(input.concat("child")); + return output.concat("/child"); + }, + }); + yield* provide({ + *test() { + return yield* scope.run(() => api.operations.test([])); + }, + }); + }, + ); + + yield* api.around({ + *test(args, next) { + let [input] = args; + let output = yield* next(input.concat("parent-after")); + return output.concat("/parent-after"); + }, + }); + + expect(yield* tester.test()).toEqual([ + "parent-before", + "parent-after", + "child", + "/child", + "/parent-after", + "/parent-before", + ]); + }); + }); + it("isolates sibling scopes from each other's middleware", async () => { let api = createApi("test", { *test(order: string[]): Operation {