diff --git a/src/implementations/api/index.ts b/src/implementations/api/index.ts index dd38565..074eb0b 100644 --- a/src/implementations/api/index.ts +++ b/src/implementations/api/index.ts @@ -39,7 +39,7 @@ interface PromiseLikeValue { } interface ComputedPropertyResolver { - key: string; + key: PropertyKey; resolve: ParameterResolver; } @@ -144,12 +144,10 @@ function compileController( controllerClass: ControllerClass, properties: Record, ): ControllerPlan { - const computedProperties = Object.entries(properties).map( - ([key, parameter]) => ({ - key, - resolve: compileParameter(parameter), - }), - ); + const computedProperties = Reflect.ownKeys(properties).map((key) => ({ + key, + resolve: compileParameter(properties[key]), + })); const existingPlan = controllerPlans.get(controllerClass); const plan = existingPlan ?? { controllerClass, @@ -324,13 +322,16 @@ function compileHandler(handler: RouteHandler): HandlerPlan { export const routesProxy = { register: (id: string, handler: RouteHandler): void => { registeredRoutes.set(id, handler); - const plan = compileHandler(handler); + let plan: HandlerPlan | undefined; registerHandler( `dev/${id}`, handler.mode, handler.method, handler.location, - (context: RequestContextDev) => invokeHandler(plan, context), + (context: RequestContextDev) => { + plan ??= compileHandler(handler); + return invokeHandler(plan, context); + }, handler.priority, ); }, diff --git a/src/test/controller-resolution.test.ts b/src/test/controller-resolution.test.ts index b1edb6a..f8a0f01 100644 --- a/src/test/controller-resolution.test.ts +++ b/src/test/controller-resolution.test.ts @@ -15,8 +15,10 @@ import { routesProxy } from "../implementations/api"; const TEST_HOST = "127.0.0.1"; const TEST_ORIGIN = `http://${TEST_HOST}`; const THEN_PROPERTY = ["th", "en"].join(""); +const SYMBOL_PROPERTY = Symbol("computed-property"); interface TestController { + [SYMBOL_PROPERTY]?: string; requestId?: string; sequence: number; } @@ -255,6 +257,53 @@ describe("Controller resolution", () => { }); }); + it("compiles handler metadata when the first request arrives", async () => { + const Controller = createController(); + const properties: Record = {}; + const location = "/controller-resolution/late-metadata"; + register( + createHandler( + Controller, + function (this: TestController) { + return this.requestId; + }, + location, + [], + properties, + ), + ); + properties.requestId = computedParameter(() => "late metadata"); + + assert.deepEqual(await get(port, location), { + status: 200, + body: "late metadata", + }); + }); + + it("applies symbol-keyed computed metadata", async () => { + const Controller = createController(); + const properties = { + [SYMBOL_PROPERTY]: computedParameter(() => "symbol value"), + }; + const location = "/controller-resolution/symbol-metadata"; + register( + createHandler( + Controller, + function (this: TestController) { + return this[SYMBOL_PROPERTY]; + }, + location, + [], + properties, + ), + ); + + assert.deepEqual(await get(port, location), { + status: 200, + body: "symbol value", + }); + }); + it("resolves computed values and handler parameters with controller this", async () => { const Controller = createController(); const properties = {