@theokit/http 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +3 -3
- package/dist/{chunk-6W4T4DPJ.js → chunk-CMPP4ULU.js} +2 -2
- package/dist/{chunk-GQ2UH554.js → chunk-GXTGOPRU.js} +4 -4
- package/dist/{chunk-QGB5YC4T.js → chunk-JQMJK47T.js} +31 -4
- package/dist/chunk-JQMJK47T.js.map +1 -0
- package/dist/{chunk-3PGQVQWG.js → chunk-KPC7AIVC.js} +3 -1
- package/dist/chunk-KPC7AIVC.js.map +1 -0
- package/dist/{chunk-RC4V75DI.js → chunk-OBHHOS6E.js} +2 -2
- package/dist/{exception-filter-chain-O45FXGEB.js → exception-filter-chain-V2MFO4WV.js} +3 -3
- package/dist/index.d.ts +64 -4
- package/dist/index.js +38 -9
- package/dist/index.js.map +1 -1
- package/dist/theokit-plugin.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-3PGQVQWG.js.map +0 -1
- package/dist/chunk-QGB5YC4T.js.map +0 -1
- /package/dist/{chunk-6W4T4DPJ.js.map → chunk-CMPP4ULU.js.map} +0 -0
- /package/dist/{chunk-GQ2UH554.js.map → chunk-GXTGOPRU.js.map} +0 -0
- /package/dist/{chunk-RC4V75DI.js.map → chunk-OBHHOS6E.js.map} +0 -0
- /package/dist/{exception-filter-chain-O45FXGEB.js.map → exception-filter-chain-V2MFO4WV.js.map} +0 -0
package/dist/app.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TheoApp
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-GXTGOPRU.js";
|
|
4
|
+
import "./chunk-JQMJK47T.js";
|
|
5
5
|
import "./chunk-HLW7YKZE.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-KPC7AIVC.js";
|
|
7
7
|
import "./chunk-7QVYU63E.js";
|
|
8
8
|
export {
|
|
9
9
|
TheoApp
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CATCH_EXCEPTIONS,
|
|
3
3
|
HttpException,
|
|
4
4
|
getMeta
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KPC7AIVC.js";
|
|
6
6
|
import {
|
|
7
7
|
resolveOrNew
|
|
8
8
|
} from "./chunk-MQAJWR3K.js";
|
|
@@ -68,4 +68,4 @@ __name(globalFallback, "globalFallback");
|
|
|
68
68
|
export {
|
|
69
69
|
runExceptionFilters
|
|
70
70
|
};
|
|
71
|
-
//# sourceMappingURL=chunk-
|
|
71
|
+
//# sourceMappingURL=chunk-CMPP4ULU.js.map
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createExecutionContext,
|
|
3
3
|
walkControllerMetadata
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JQMJK47T.js";
|
|
5
5
|
import {
|
|
6
6
|
createNodeAdapter
|
|
7
7
|
} from "./chunk-HLW7YKZE.js";
|
|
8
8
|
import {
|
|
9
9
|
ForbiddenException,
|
|
10
10
|
HttpException
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KPC7AIVC.js";
|
|
12
12
|
import {
|
|
13
13
|
__name
|
|
14
14
|
} from "./chunk-7QVYU63E.js";
|
|
@@ -678,7 +678,7 @@ var TheoApp = class _TheoApp {
|
|
|
678
678
|
return buildResponse(result, entry.walk, method);
|
|
679
679
|
} catch (err) {
|
|
680
680
|
if (entry.walk.filters.length > 0) {
|
|
681
|
-
const { runExceptionFilters } = await import("./exception-filter-chain-
|
|
681
|
+
const { runExceptionFilters } = await import("./exception-filter-chain-V2MFO4WV.js");
|
|
682
682
|
return runExceptionFilters(err, entry.walk.filters, request);
|
|
683
683
|
}
|
|
684
684
|
if (err instanceof HttpException) {
|
|
@@ -787,4 +787,4 @@ export {
|
|
|
787
787
|
streamToResponse,
|
|
788
788
|
TheoApp
|
|
789
789
|
};
|
|
790
|
-
//# sourceMappingURL=chunk-
|
|
790
|
+
//# sourceMappingURL=chunk-GXTGOPRU.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CONTROLLER_PREFIX,
|
|
3
|
+
EXPOSE_AGENT,
|
|
3
4
|
ROUTE_HEADERS,
|
|
4
5
|
ROUTE_METHODS,
|
|
5
6
|
ROUTE_PARAMS,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
USE_GUARDS,
|
|
10
11
|
USE_INTERCEPTORS,
|
|
11
12
|
getMeta
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KPC7AIVC.js";
|
|
13
14
|
import {
|
|
14
15
|
__name
|
|
15
16
|
} from "./chunk-7QVYU63E.js";
|
|
@@ -110,8 +111,34 @@ function walkControllerMetadata(ControllerClass) {
|
|
|
110
111
|
filters: getMeta(USE_FILTERS, ControllerClass, m.propertyKey) ?? classFilters
|
|
111
112
|
};
|
|
112
113
|
});
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
const exposeEntries = getMeta(EXPOSE_AGENT, ControllerClass) ?? [];
|
|
115
|
+
const agentResults = exposeEntries.map((e) => {
|
|
116
|
+
const memberGuards = getMeta(USE_GUARDS, ControllerClass, e.propertyKey) ?? [];
|
|
117
|
+
const verb = "POST";
|
|
118
|
+
return {
|
|
119
|
+
verb,
|
|
120
|
+
fullPath: joinPath(prefix, String(e.propertyKey)),
|
|
121
|
+
propertyKey: e.propertyKey,
|
|
122
|
+
paramEntries: [],
|
|
123
|
+
headers: [],
|
|
124
|
+
guards: [
|
|
125
|
+
...classGuards,
|
|
126
|
+
...memberGuards
|
|
127
|
+
],
|
|
128
|
+
interceptors: [],
|
|
129
|
+
filters: classFilters,
|
|
130
|
+
agent: {
|
|
131
|
+
module: e.agent,
|
|
132
|
+
opts: e.opts
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
const all = [
|
|
137
|
+
...result,
|
|
138
|
+
...agentResults
|
|
139
|
+
];
|
|
140
|
+
walkCache.set(ControllerClass, all);
|
|
141
|
+
return all;
|
|
115
142
|
}
|
|
116
143
|
__name(walkControllerMetadata, "walkControllerMetadata");
|
|
117
144
|
|
|
@@ -122,4 +149,4 @@ export {
|
|
|
122
149
|
joinPath,
|
|
123
150
|
walkControllerMetadata
|
|
124
151
|
};
|
|
125
|
-
//# sourceMappingURL=chunk-
|
|
152
|
+
//# sourceMappingURL=chunk-JQMJK47T.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bridge/execution-context.ts","../src/bridge/dto-zod.ts","../src/bridge/errors.ts","../src/bridge/walk-metadata.ts"],"sourcesContent":["/**\n * ExecutionContext — Web Standard Request-based context passed to guards.\n *\n * Per ADR D460: the pipeline operates on Web Standard Request/Response.\n * node:http types live ONLY in the runtime adapter (runtime/node.ts).\n *\n * Guards access request headers via request.headers.get('x-role'),\n * NOT via req.headers['x-role'].\n */\n\n/**\n * Execution context available in guards during request processing.\n * Uses Web Standard Request (works on Node, Bun, Deno, CF Workers).\n */\nexport interface ExecutionContext {\n /** The Web Standard Request object. */\n getRequest(): Request\n /** Parsed URL (convenience — avoids re-parsing). */\n getUrl(): URL\n /** The controller class constructor. */\n getClass(): Function\n /** The handler method name (property key on the controller). */\n getMethodName(): string | symbol\n}\n\n/**\n * Interface for guard classes (bound via @UseGuards).\n *\n * @example\n * ```ts\n * class RolesGuard implements CanActivate {\n * canActivate(context: ExecutionContext): boolean {\n * const request = context.getRequest()\n * const role = request.headers.get('x-role')\n * return role === 'admin'\n * }\n * }\n * ```\n */\nexport interface CanActivate {\n canActivate(context: ExecutionContext): boolean | Promise<boolean>\n}\n\n/** Create an ExecutionContext from a Web Standard Request. */\nexport function createExecutionContext(\n request: Request,\n controllerClass: Function,\n methodName: string | symbol,\n): ExecutionContext {\n const url = new URL(request.url)\n return {\n getRequest: () => request,\n getUrl: () => url,\n getClass: () => controllerClass,\n getMethodName: () => methodName,\n }\n}\n","import type { z } from 'zod'\n\n/**\n * Resolves a Zod schema from a DTO class via the `static schema` convention (Pattern D2).\n * Returns undefined when the class doesn't carry a compatible schema.\n */\nexport function resolveDtoSchema(dtoClass: unknown): z.ZodType | undefined {\n if (typeof dtoClass !== 'function') return undefined\n const maybe = (dtoClass as unknown as Record<string, unknown>).schema\n if (\n maybe !== null &&\n maybe !== undefined &&\n typeof (maybe as Record<string, unknown>).safeParse === 'function'\n ) {\n return maybe as z.ZodType\n }\n return undefined\n}\n","/**\n * Configuration error thrown by the bridge when decorator setup is incomplete.\n * Carries actionable messages pointing consumers to the migration guide.\n */\nexport class HttpDecoratorsConfigError extends Error {\n override readonly name = 'HttpDecoratorsConfigError'\n\n constructor(message: string) {\n super(message)\n }\n}\n","import 'reflect-metadata'\nimport type { z } from 'zod'\n\nimport type { ControllerMeta } from '../decorators/controller.js'\nimport type { ExposeEntry, ExposeOptions } from '../decorators/expose.js'\nimport type { RouteMethodEntry, HttpVerb } from '../decorators/methods.js'\nimport type { ParamEntry } from '../decorators/params.js'\nimport type { RedirectMeta } from '../decorators/response.js'\nimport {\n getMeta,\n CONTROLLER_PREFIX,\n EXPOSE_AGENT,\n ROUTE_METHODS,\n ROUTE_PARAMS,\n ROUTE_STATUS,\n ROUTE_HEADERS,\n ROUTE_REDIRECT,\n USE_GUARDS,\n USE_INTERCEPTORS,\n USE_FILTERS,\n} from '../metadata/index.js'\n\nimport { resolveDtoSchema } from './dto-zod.js'\nimport { HttpDecoratorsConfigError } from './errors.js'\n\nexport interface WalkResult {\n verb: HttpVerb\n fullPath: string\n propertyKey: string | symbol\n bodySchema?: z.ZodType\n querySchema?: z.ZodType\n paramsSchema?: z.ZodType\n paramEntries: ParamEntry[]\n status?: number\n headers: [string, string][]\n redirect?: RedirectMeta\n guards: Function[]\n interceptors: Function[]\n filters: Function[]\n /**\n * M47 — set when the member is bound via `@Expose(agent, opts)`. The dispatcher delegates such routes to\n * the agent runtime (`mountAgent`) instead of invoking a JSON handler. `undefined` for normal verb routes.\n */\n agent?: { module: unknown; opts: ExposeOptions }\n}\n\n/**\n * Normalize a joined path: strip doubles, trim trailing, ensure leading.\n * (EC-3)\n */\nexport function joinPath(prefix: string, path: string): string {\n return ('/' + prefix + '/' + path).replace(/\\/+/g, '/').replace(/\\/$/, '') || '/'\n}\n\n/**\n * Resolve the Zod body schema for a method's @Body() param entry.\n *\n * Priority: explicit @Body(zodSchema) > design:paramtypes + DTO static schema.\n * EC-4 relaxed: warns (not throws) when paramtypes missing — @Body(zodSchema) is the fix.\n */\nfunction resolveBodySchema(\n paramEntries: ParamEntry[],\n ControllerClass: Function,\n propertyKey: string | symbol,\n): z.ZodType | undefined {\n const bodyParam = paramEntries.find((p) => p.source === 'body' && !p.key)\n if (!bodyParam) return undefined\n\n // Priority 1: explicit Zod schema from @Body(zodSchema)\n if (bodyParam.schema) return bodyParam.schema\n\n // Priority 2: design:paramtypes + DTO static schema (requires emitDecoratorMetadata)\n const paramTypes: Function[] =\n Reflect.getMetadata('design:paramtypes', ControllerClass.prototype, propertyKey) ?? []\n if (paramTypes.length > 0) {\n return resolveDtoSchema(paramTypes[bodyParam.index])\n }\n\n // EC-4 relaxed: warn when @Body() has no schema and no paramtypes\n console.warn(\n `[@theokit/http] method ${String(propertyKey)} on ` +\n `${ControllerClass.name}: @Body() without explicit schema and ` +\n `emitDecoratorMetadata is not active. Body will be passed raw (no validation). ` +\n `Fix: use @Body(zodSchema) for validation without metadata emission.`,\n )\n return undefined\n}\n\n/** WeakMap cache — metadata is immutable; walk once, reuse forever. */\nconst walkCache = new WeakMap<Function, WalkResult[]>()\n\n/**\n * Walk all decorator metadata on a controller class and produce\n * a structured list of route descriptors. Memoized per class via WeakMap.\n */\nexport function walkControllerMetadata(ControllerClass: Function): WalkResult[] {\n const cached = walkCache.get(ControllerClass)\n if (cached) return cached\n // EC-2: throw when @Controller decorator is missing\n const controllerMeta = getMeta<ControllerMeta>(CONTROLLER_PREFIX, ControllerClass)\n if (!controllerMeta) {\n throw new HttpDecoratorsConfigError(\n `Controller class ${ControllerClass.name} is missing @Controller() decorator. ` +\n `Add @Controller('prefix') to the class declaration.`,\n )\n }\n const { prefix, host } = controllerMeta\n\n // Q4: host captured but enforcement deferred to v0.2.0\n if (host) {\n console.warn(\n `[@theokit/http] @Controller host '${host}' captured but enforcement deferred to v0.2.0`,\n )\n }\n\n const methods = getMeta<RouteMethodEntry[]>(ROUTE_METHODS, ControllerClass) ?? []\n const paramsMap =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, ControllerClass) ?? new Map()\n\n // Class-level guards/interceptors\n const classGuards = getMeta<Function[]>(USE_GUARDS, ControllerClass) ?? []\n const classInterceptors = getMeta<Function[]>(USE_INTERCEPTORS, ControllerClass) ?? []\n const classFilters = getMeta<Function[]>(USE_FILTERS, ControllerClass) ?? []\n\n const result = methods.map((m) => {\n const paramEntries = paramsMap.get(m.propertyKey) ?? []\n const bodySchema = resolveBodySchema(paramEntries, ControllerClass, m.propertyKey)\n\n // Method-level guards/interceptors (composed: class FIRST per NestJS convention — EC-9)\n const methodGuards = getMeta<Function[]>(USE_GUARDS, ControllerClass, m.propertyKey) ?? []\n const methodInterceptors =\n getMeta<Function[]>(USE_INTERCEPTORS, ControllerClass, m.propertyKey) ?? []\n\n return {\n verb: m.verb,\n fullPath: joinPath(prefix, m.path),\n propertyKey: m.propertyKey,\n bodySchema,\n paramEntries: [...paramEntries].sort((a, b) => a.index - b.index),\n status: getMeta<number>(ROUTE_STATUS, ControllerClass, m.propertyKey),\n headers: getMeta<[string, string][]>(ROUTE_HEADERS, ControllerClass, m.propertyKey) ?? [],\n redirect: getMeta<RedirectMeta>(ROUTE_REDIRECT, ControllerClass, m.propertyKey),\n guards: [...classGuards, ...methodGuards],\n interceptors: [...classInterceptors, ...methodInterceptors],\n filters: getMeta<Function[]>(USE_FILTERS, ControllerClass, m.propertyKey) ?? classFilters,\n }\n })\n\n // M47 — @Expose-bound members have no verb decorator, so they are absent from `methods`. Produce an\n // agent-serving WalkResult per binding: verb POST (agents are POST), path = prefix + member name (this\n // MUST be the agent's convention route so the generated handle's path matches — see ExposeOptions), the\n // agent module carried for the dispatcher, guards composed class-first (G5 shared guards). Interceptors\n // do NOT run for agent routes — the dispatcher delegates straight to `mountAgent` before the interceptor\n // chain — so they are intentionally not collected here (documented on `@Expose`).\n const exposeEntries = getMeta<ExposeEntry[]>(EXPOSE_AGENT, ControllerClass) ?? []\n const agentResults: WalkResult[] = exposeEntries.map((e) => {\n const memberGuards = getMeta<Function[]>(USE_GUARDS, ControllerClass, e.propertyKey) ?? []\n const verb: HttpVerb = 'POST'\n return {\n verb,\n fullPath: joinPath(prefix, String(e.propertyKey)),\n propertyKey: e.propertyKey,\n paramEntries: [],\n headers: [],\n guards: [...classGuards, ...memberGuards],\n interceptors: [],\n filters: classFilters,\n agent: { module: e.agent, opts: e.opts },\n }\n })\n\n const all = [...result, ...agentResults]\n walkCache.set(ControllerClass, all)\n return all\n}\n"],"mappings":"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","names":["createExecutionContext","request","controllerClass","methodName","url","URL","getRequest","getUrl","getClass","getMethodName","resolveDtoSchema","dtoClass","undefined","maybe","schema","safeParse","HttpDecoratorsConfigError","Error","name","message","joinPath","prefix","path","replace","resolveBodySchema","paramEntries","ControllerClass","propertyKey","bodyParam","find","p","source","key","undefined","schema","paramTypes","Reflect","getMetadata","prototype","length","resolveDtoSchema","index","console","warn","String","name","walkCache","WeakMap","walkControllerMetadata","cached","get","controllerMeta","getMeta","CONTROLLER_PREFIX","HttpDecoratorsConfigError","host","methods","ROUTE_METHODS","paramsMap","ROUTE_PARAMS","Map","classGuards","USE_GUARDS","classInterceptors","USE_INTERCEPTORS","classFilters","USE_FILTERS","result","map","m","bodySchema","methodGuards","methodInterceptors","verb","fullPath","sort","a","b","status","ROUTE_STATUS","headers","ROUTE_HEADERS","redirect","ROUTE_REDIRECT","guards","interceptors","filters","exposeEntries","EXPOSE_AGENT","agentResults","e","memberGuards","agent","module","opts","all","set"]}
|
|
@@ -13,6 +13,7 @@ var USE_GUARDS = /* @__PURE__ */ Symbol.for("theokit:http-decorators:use-guards"
|
|
|
13
13
|
var USE_INTERCEPTORS = /* @__PURE__ */ Symbol.for("theokit:http-decorators:use-interceptors");
|
|
14
14
|
var USE_FILTERS = /* @__PURE__ */ Symbol.for("theokit:http-decorators:use-filters");
|
|
15
15
|
var CATCH_EXCEPTIONS = /* @__PURE__ */ Symbol.for("theokit:http-decorators:catch-exceptions");
|
|
16
|
+
var EXPOSE_AGENT = /* @__PURE__ */ Symbol.for("theokit:http-decorators:expose-agent");
|
|
16
17
|
|
|
17
18
|
// src/metadata/storage.ts
|
|
18
19
|
import "reflect-metadata";
|
|
@@ -247,6 +248,7 @@ export {
|
|
|
247
248
|
USE_INTERCEPTORS,
|
|
248
249
|
USE_FILTERS,
|
|
249
250
|
CATCH_EXCEPTIONS,
|
|
251
|
+
EXPOSE_AGENT,
|
|
250
252
|
setMeta,
|
|
251
253
|
getMeta,
|
|
252
254
|
HttpException,
|
|
@@ -273,4 +275,4 @@ export {
|
|
|
273
275
|
TooManyRequestsException,
|
|
274
276
|
HttpStatus
|
|
275
277
|
};
|
|
276
|
-
//# sourceMappingURL=chunk-
|
|
278
|
+
//# sourceMappingURL=chunk-KPC7AIVC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/metadata/keys.ts","../src/metadata/storage.ts","../src/exceptions/http-exception.ts"],"sourcesContent":["/**\n * Global Symbol-keyed metadata namespace constants for @theokit/http.\n *\n * Uses Symbol.for() (global Symbol registry) instead of Symbol() (local) because\n * the SWC loader imports controller files as separate module instances. With local\n * Symbols, the decorator-set metadata keys would be different Symbol instances from\n * the keys used by walkControllerMetadata — making metadata lookup silently fail.\n *\n * Symbol.for() ensures the SAME Symbol instance across module boundaries, which is\n * exactly how reflect-metadata keys should work in a multi-module decorator system.\n */\n\nexport const CONTROLLER_PREFIX = Symbol.for('theokit:http-decorators:controller-prefix')\nexport const ROUTE_METHODS = Symbol.for('theokit:http-decorators:route-methods')\nexport const ROUTE_PARAMS = Symbol.for('theokit:http-decorators:route-params')\nexport const ROUTE_STATUS = Symbol.for('theokit:http-decorators:route-status')\nexport const ROUTE_HEADERS = Symbol.for('theokit:http-decorators:route-headers')\nexport const ROUTE_REDIRECT = Symbol.for('theokit:http-decorators:route-redirect')\nexport const USE_GUARDS = Symbol.for('theokit:http-decorators:use-guards')\nexport const USE_INTERCEPTORS = Symbol.for('theokit:http-decorators:use-interceptors')\nexport const USE_FILTERS = Symbol.for('theokit:http-decorators:use-filters')\nexport const CATCH_EXCEPTIONS = Symbol.for('theokit:http-decorators:catch-exceptions')\nexport const EXPOSE_AGENT = Symbol.for('theokit:http-decorators:expose-agent')\n","import 'reflect-metadata'\n\n/**\n * Typed facade over Reflect.defineMetadata / Reflect.getMetadata.\n * Centralizes all reflect-metadata calls so decorators + bridge\n * never call Reflect.* directly (single import point for the polyfill).\n */\n\nexport function setMeta<T>(\n key: symbol,\n target: object,\n value: T,\n propertyKey?: string | symbol,\n): void {\n if (propertyKey !== undefined) {\n Reflect.defineMetadata(key, value, target, propertyKey)\n } else {\n Reflect.defineMetadata(key, value, target)\n }\n}\n\nexport function getMeta<T>(\n key: symbol,\n target: object,\n propertyKey?: string | symbol,\n): T | undefined {\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n}\n","/**\n * HttpException hierarchy for @theokit/http.\n *\n * Per ADR D2: response shape {error: {code, message, statusCode}} matches\n * existing guard (401) and validation (422) format.\n */\n\nconst STATUS_CODES: Record<number, string> = {\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 405: 'METHOD_NOT_ALLOWED',\n 406: 'NOT_ACCEPTABLE',\n 408: 'REQUEST_TIMEOUT',\n 409: 'CONFLICT',\n 410: 'GONE',\n 412: 'PRECONDITION_FAILED',\n 413: 'PAYLOAD_TOO_LARGE',\n 415: 'UNSUPPORTED_MEDIA_TYPE',\n 418: 'IM_A_TEAPOT',\n 422: 'UNPROCESSABLE_ENTITY',\n 429: 'TOO_MANY_REQUESTS',\n 500: 'INTERNAL_SERVER_ERROR',\n 501: 'NOT_IMPLEMENTED',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT',\n 505: 'HTTP_VERSION_NOT_SUPPORTED',\n}\n\nexport interface HttpExceptionOptions {\n cause?: Error\n description?: string\n}\n\nexport class HttpException extends Error {\n public readonly statusCode: number\n public readonly code: string\n public readonly description?: string\n\n constructor(message: string, statusCode: number, options?: HttpExceptionOptions) {\n super(message, options?.cause ? { cause: options.cause } : undefined)\n this.name = this.constructor.name\n this.statusCode = statusCode\n this.code = STATUS_CODES[statusCode] ?? 'INTERNAL_SERVER_ERROR'\n this.description = options?.description\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n ...(this.description ? { description: this.description } : {}),\n },\n }\n }\n}\n\nfunction factory(status: number, defaultMsg: string) {\n return class extends HttpException {\n constructor(message = defaultMsg, options?: HttpExceptionOptions) {\n super(message, status, options)\n this.name = this.constructor.name\n }\n }\n}\n\nexport class BadRequestException extends factory(400, 'Bad Request') {}\nexport class UnauthorizedException extends factory(401, 'Unauthorized') {}\nexport class ForbiddenException extends factory(403, 'Forbidden') {}\nexport class NotFoundException extends factory(404, 'Not Found') {}\nexport class MethodNotAllowedException extends factory(405, 'Method Not Allowed') {}\nexport class NotAcceptableException extends factory(406, 'Not Acceptable') {}\nexport class RequestTimeoutException extends factory(408, 'Request Timeout') {}\nexport class ConflictException extends factory(409, 'Conflict') {}\nexport class GoneException extends factory(410, 'Gone') {}\nexport class PreconditionFailedException extends factory(412, 'Precondition Failed') {}\nexport class PayloadTooLargeException extends factory(413, 'Payload Too Large') {}\nexport class UnsupportedMediaTypeException extends factory(415, 'Unsupported Media Type') {}\nexport class ImATeapotException extends factory(418, \"I'm a Teapot\") {}\nexport class UnprocessableEntityException extends factory(422, 'Unprocessable Entity') {}\nexport class InternalServerErrorException extends factory(500, 'Internal Server Error') {}\nexport class NotImplementedException extends factory(501, 'Not Implemented') {}\nexport class BadGatewayException extends factory(502, 'Bad Gateway') {}\nexport class ServiceUnavailableException extends factory(503, 'Service Unavailable') {}\nexport class GatewayTimeoutException extends factory(504, 'Gateway Timeout') {}\nexport class HttpVersionNotSupportedException extends factory(505, 'HTTP Version Not Supported') {}\nexport class TooManyRequestsException extends factory(429, 'Too Many Requests') {}\n\n/**\n * HttpStatus enum — all standard HTTP status codes as named constants.\n *\n * @example\n * ```ts\n * import { HttpStatus } from '@theokit/http'\n *\n * @HttpCode(HttpStatus.CREATED)\n * @Post()\n * create() { ... }\n *\n * if (res.status === HttpStatus.NOT_FOUND) { ... }\n * ```\n */\nexport const HttpStatus = {\n // 2xx Success\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NO_CONTENT: 204,\n\n // 3xx Redirection\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n NOT_MODIFIED: 304,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // 4xx Client Error\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n UNSUPPORTED_MEDIA_TYPE: 415,\n IM_A_TEAPOT: 418,\n UNPROCESSABLE_ENTITY: 422,\n TOO_MANY_REQUESTS: 429,\n\n // 5xx Server Error\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n} as const\n\nexport type HttpStatusCode = (typeof HttpStatus)[keyof typeof HttpStatus]\n"],"mappings":"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","names":["CONTROLLER_PREFIX","Symbol","for","ROUTE_METHODS","ROUTE_PARAMS","ROUTE_STATUS","ROUTE_HEADERS","ROUTE_REDIRECT","USE_GUARDS","USE_INTERCEPTORS","USE_FILTERS","CATCH_EXCEPTIONS","EXPOSE_AGENT","setMeta","key","target","value","propertyKey","undefined","Reflect","defineMetadata","getMeta","getMetadata","STATUS_CODES","HttpException","Error","statusCode","code","description","message","options","cause","undefined","name","toJSON","error","factory","status","defaultMsg","BadRequestException","UnauthorizedException","ForbiddenException","NotFoundException","MethodNotAllowedException","NotAcceptableException","RequestTimeoutException","ConflictException","GoneException","PreconditionFailedException","PayloadTooLargeException","UnsupportedMediaTypeException","ImATeapotException","UnprocessableEntityException","InternalServerErrorException","NotImplementedException","BadGatewayException","ServiceUnavailableException","GatewayTimeoutException","HttpVersionNotSupportedException","TooManyRequestsException","HttpStatus","OK","CREATED","ACCEPTED","NO_CONTENT","MOVED_PERMANENTLY","FOUND","NOT_MODIFIED","TEMPORARY_REDIRECT","PERMANENT_REDIRECT","BAD_REQUEST","UNAUTHORIZED","PAYMENT_REQUIRED","FORBIDDEN","NOT_FOUND","METHOD_NOT_ALLOWED","NOT_ACCEPTABLE","REQUEST_TIMEOUT","CONFLICT","GONE","PRECONDITION_FAILED","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","IM_A_TEAPOT","UNPROCESSABLE_ENTITY","TOO_MANY_REQUESTS","INTERNAL_SERVER_ERROR","NOT_IMPLEMENTED","BAD_GATEWAY","SERVICE_UNAVAILABLE","GATEWAY_TIMEOUT"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HttpDecoratorsConfigError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JQMJK47T.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveOrNew
|
|
6
6
|
} from "./chunk-MQAJWR3K.js";
|
|
@@ -254,4 +254,4 @@ export {
|
|
|
254
254
|
loadControllersFromGlob,
|
|
255
255
|
isControllerClass
|
|
256
256
|
};
|
|
257
|
-
//# sourceMappingURL=chunk-
|
|
257
|
+
//# sourceMappingURL=chunk-OBHHOS6E.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runExceptionFilters
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-CMPP4ULU.js";
|
|
4
|
+
import "./chunk-KPC7AIVC.js";
|
|
5
5
|
import "./chunk-MQAJWR3K.js";
|
|
6
6
|
import "./chunk-7QVYU63E.js";
|
|
7
7
|
export {
|
|
8
8
|
runExceptionFilters
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=exception-filter-chain-
|
|
10
|
+
//# sourceMappingURL=exception-filter-chain-V2MFO4WV.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,50 @@ declare function Res(opts?: {
|
|
|
70
70
|
passthrough?: boolean;
|
|
71
71
|
}): ParameterDecorator;
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* M47 (ADR-M47-1) — `@Expose(agent, opts?)` binds a SEPARATELY-BUILT agent (a pure `agent()…build()` from
|
|
75
|
+
* `agents/<name>.ts`) to a controller PROPERTY, making the exposure (route, auth via `@UseGuards`, streaming)
|
|
76
|
+
* visible in one code review. It mirrors the #122 verb decorators (`@Post`) — storing metadata via the same
|
|
77
|
+
* `Symbol.for()` seam — but records the agent instead of an HTTP verb, so the controller walker turns the
|
|
78
|
+
* property into an agent-serving route (`POST <prefix>/<property>`) delegated to the ONE runtime
|
|
79
|
+
* (`mountAgent`), never a JSON handler. It is a new AUTHORING surface over the existing runtime, not a
|
|
80
|
+
* parallel runtime (G2). CSRF is enforced once at the controller-dispatch boundary. Interceptors do NOT run
|
|
81
|
+
* for agent routes (the dispatcher delegates straight to `mountAgent`); guards DO (G5). The served path is
|
|
82
|
+
* the controller prefix + property name — keep it equal to the agent's convention route (`/api/agents/<name>`
|
|
83
|
+
* for a property named `<name>` under `@Controller('api/agents')`) so the generated `useAgent(handle)` path
|
|
84
|
+
* lines up.
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* Per-binding options for an exposed agent. Reserved for forward-compat — no options are wired yet.
|
|
88
|
+
*
|
|
89
|
+
* Two candidate fields were removed before shipping (M47 review): `path` (a route override) would let the
|
|
90
|
+
* served URL diverge from the generated handle's path — the codegen derives the handle from the agent's
|
|
91
|
+
* convention route (`/api/agents/<name>`), so an overridden route would make `useAgent(handle)` hit the
|
|
92
|
+
* wrong URL. And `csrf` was a no-op — CSRF is enforced exactly once at the controller-dispatch boundary
|
|
93
|
+
* regardless. Both return only when they can be wired end-to-end (codegen reads `@Expose` metadata / a
|
|
94
|
+
* per-route CSRF opt-out threads through the dispatcher). Until then, `@Expose(agent)` is the shape, and the
|
|
95
|
+
* exposure's path MUST be the convention route — put `@Expose` on a property named after the agent under
|
|
96
|
+
* `@Controller('api/agents')` (e.g. `chat` for `agents/chat.ts` → `/api/agents/chat`).
|
|
97
|
+
*/
|
|
98
|
+
type ExposeOptions = Record<string, never>;
|
|
99
|
+
/** Metadata recorded per `@Expose`-decorated controller member. */
|
|
100
|
+
interface ExposeEntry {
|
|
101
|
+
/** The separately-built agent module bound to this member. */
|
|
102
|
+
agent: unknown;
|
|
103
|
+
/** The per-binding options (defaulted to `{}`). */
|
|
104
|
+
opts: ExposeOptions;
|
|
105
|
+
/** The controller member the binding is attached to. */
|
|
106
|
+
propertyKey: string | symbol;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Bind an agent to a controller PROPERTY. Accumulates one {@link ExposeEntry} per member under the
|
|
110
|
+
* `EXPOSE_AGENT` metadata key on the controller constructor — the same accumulation shape `@Post` uses for
|
|
111
|
+
* `ROUTE_METHODS`, so `walkControllerMetadata` reads both alongside each other. Typed as `PropertyDecorator`
|
|
112
|
+
* (the exposure is declared as a property, e.g. `@Expose(chatAgent) chat!: typeof chatAgent`); the agent's
|
|
113
|
+
* behavior lives in its own `agents/<name>.ts`, never in a controller method body.
|
|
114
|
+
*/
|
|
115
|
+
declare function Expose(agent: unknown, opts?: ExposeOptions): PropertyDecorator;
|
|
116
|
+
|
|
73
117
|
declare function HttpCode(status: number): MethodDecorator;
|
|
74
118
|
declare function Header(name: string, value: string): MethodDecorator;
|
|
75
119
|
interface RedirectMeta {
|
|
@@ -78,7 +122,7 @@ interface RedirectMeta {
|
|
|
78
122
|
}
|
|
79
123
|
declare function Redirect(url: string, status?: number): MethodDecorator;
|
|
80
124
|
|
|
81
|
-
declare function UseGuards(...guards: Function[]): ClassDecorator & MethodDecorator;
|
|
125
|
+
declare function UseGuards(...guards: Function[]): ClassDecorator & MethodDecorator & PropertyDecorator;
|
|
82
126
|
declare function UseInterceptors(...interceptors: Function[]): ClassDecorator & MethodDecorator;
|
|
83
127
|
declare function UseFilters(...filters: Function[]): ClassDecorator & MethodDecorator;
|
|
84
128
|
/** @Catch(ExceptionType, ...) — marks which exception types an ExceptionFilter handles.
|
|
@@ -223,6 +267,7 @@ declare const USE_GUARDS: unique symbol;
|
|
|
223
267
|
declare const USE_INTERCEPTORS: unique symbol;
|
|
224
268
|
declare const USE_FILTERS: unique symbol;
|
|
225
269
|
declare const CATCH_EXCEPTIONS: unique symbol;
|
|
270
|
+
declare const EXPOSE_AGENT: unique symbol;
|
|
226
271
|
|
|
227
272
|
/**
|
|
228
273
|
* Typed facade over Reflect.defineMetadata / Reflect.getMetadata.
|
|
@@ -343,6 +388,14 @@ interface WalkResult {
|
|
|
343
388
|
guards: Function[];
|
|
344
389
|
interceptors: Function[];
|
|
345
390
|
filters: Function[];
|
|
391
|
+
/**
|
|
392
|
+
* M47 — set when the member is bound via `@Expose(agent, opts)`. The dispatcher delegates such routes to
|
|
393
|
+
* the agent runtime (`mountAgent`) instead of invoking a JSON handler. `undefined` for normal verb routes.
|
|
394
|
+
*/
|
|
395
|
+
agent?: {
|
|
396
|
+
module: unknown;
|
|
397
|
+
opts: ExposeOptions;
|
|
398
|
+
};
|
|
346
399
|
}
|
|
347
400
|
/**
|
|
348
401
|
* Normalize a joined path: strip doubles, trim trailing, ensure leading.
|
|
@@ -427,13 +480,16 @@ interface RouteRegistration {
|
|
|
427
480
|
declare function registerControllers(controllers: Function[]): RouteRegistration[];
|
|
428
481
|
|
|
429
482
|
/**
|
|
430
|
-
*
|
|
431
|
-
*
|
|
483
|
+
* M47 — serves an `@Expose`-bound agent route. http is agent-runtime agnostic (G1/G2): it invokes this
|
|
484
|
+
* injected callback (theo supplies a `mountAgent`-backed impl) instead of calling a controller method.
|
|
432
485
|
*/
|
|
486
|
+
type ServeAgent = (agent: unknown, request: Request, opts: ExposeOptions) => Promise<Response>;
|
|
433
487
|
interface CreateDecoratorServerOptions {
|
|
434
488
|
controllers: Function[];
|
|
435
489
|
container?: DiContainer;
|
|
436
490
|
configure?: (consumer: MiddlewareConsumerImpl) => void;
|
|
491
|
+
/** M47 — required when any controller `@Expose`-binds an agent; serves the agent route. */
|
|
492
|
+
serveAgent?: ServeAgent;
|
|
437
493
|
}
|
|
438
494
|
/**
|
|
439
495
|
* A pure Web-Standard controller handler: callable as `(request) => Response | null`
|
|
@@ -454,6 +510,10 @@ interface DecoratorHandler {
|
|
|
454
510
|
* controller dispatch (#122) so it never re-implements match/bind/validate.
|
|
455
511
|
*/
|
|
456
512
|
declare function createDecoratorHandler(controllersOrOpts: Function[] | CreateDecoratorServerOptions): DecoratorHandler;
|
|
513
|
+
/**
|
|
514
|
+
* Creates a real HTTP server from decorated controller classes.
|
|
515
|
+
* Uses Web Standard Request/Response internally; Node adapter at the boundary.
|
|
516
|
+
*/
|
|
457
517
|
declare function createDecoratorServer(controllersOrOpts: Function[] | CreateDecoratorServerOptions): ServerHandle;
|
|
458
518
|
|
|
459
519
|
/**
|
|
@@ -1366,4 +1426,4 @@ declare function revalidatePath(path: string): void;
|
|
|
1366
1426
|
*/
|
|
1367
1427
|
declare function getRevalidationSignals(): RevalidationSignal[];
|
|
1368
1428
|
|
|
1369
|
-
export { All, type ArgumentsHost, BadGatewayException, BadRequestException, Body, CATCH_EXCEPTIONS, CONTROLLER_PREFIX, type CanActivate, Catch, ConflictException, Controller, type ControllerMeta, type ControllerOptions, type DecoratorHandler, Delete, DiContainer, type DigestedError, type ErrorContext, type ExceptionFilter, type ExecutionContext, ForbiddenException, GatewayTimeoutException, Get, GoneException, Head, Header, Headers, HostParam, HttpCode, HttpDecoratorsConfigError, HttpException, type HttpExceptionOptions, HttpStatus, type HttpStatusCode, type HttpVerb, HttpVersionNotSupportedException, ImATeapotException, type Interceptor, InternalServerErrorException, Ip, type MetadataKey, MethodNotAllowedException, MiddlewareConsumerImpl, NotAcceptableException, NotFoundException, NotImplementedException, Options, Param, type ParamEntry, type ParamSource, Patch, PayloadTooLargeException, Post, PreconditionFailedException, Put, Query, ROUTE_HEADERS, ROUTE_METHODS, ROUTE_PARAMS, ROUTE_REDIRECT, ROUTE_STATUS, Redirect, type RedirectMeta, Reflector, Req, RequestTimeoutException, Res, type RevalidationSignal, type RouteDefinition, type RouteMap, type RouteMethodEntry, type RouteRegistration, type RouteTree, ServiceUnavailableException, Session, SetMetadata, SkipThrottle, type StaticOptions, type StreamRenderOptions, type StreamRenderResult, type SwcCore, type TheoRequestContext, Throttle, type ThrottleOptions, TooManyRequestsException, type TypedClient, TypedClientError, USE_FILTERS, USE_GUARDS, USE_INTERCEPTORS, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UseFilters, UseGuards, UseInterceptors, type WalkResult, composeComponentTree, contract, createDecorator, createDecoratorHandler, createDecoratorServer, createExecutionContext, createStaticHandler, createTypedClient, digestError, getMeta, getMimeType, getRequestContext, getRevalidationSignals, getThrottleOptions, isControllerClass, isSafePath, isThrottleSkipped, joinPath, loadControllerWithSwc, loadControllersFromGlob, registerControllers, renderToStream, resolveDtoSchema, revalidatePath, revalidateTag, runExceptionFilters, runInterceptors, setMeta, streamToResponse, transformControllerSource, tryGetRequestContext, walkControllerMetadata };
|
|
1429
|
+
export { All, type ArgumentsHost, BadGatewayException, BadRequestException, Body, CATCH_EXCEPTIONS, CONTROLLER_PREFIX, type CanActivate, Catch, ConflictException, Controller, type ControllerMeta, type ControllerOptions, type CreateDecoratorServerOptions, type DecoratorHandler, Delete, DiContainer, type DigestedError, EXPOSE_AGENT, type ErrorContext, type ExceptionFilter, type ExecutionContext, Expose, type ExposeEntry, type ExposeOptions, ForbiddenException, GatewayTimeoutException, Get, GoneException, Head, Header, Headers, HostParam, HttpCode, HttpDecoratorsConfigError, HttpException, type HttpExceptionOptions, HttpStatus, type HttpStatusCode, type HttpVerb, HttpVersionNotSupportedException, ImATeapotException, type Interceptor, InternalServerErrorException, Ip, type MetadataKey, MethodNotAllowedException, MiddlewareConsumerImpl, NotAcceptableException, NotFoundException, NotImplementedException, Options, Param, type ParamEntry, type ParamSource, Patch, PayloadTooLargeException, Post, PreconditionFailedException, Put, Query, ROUTE_HEADERS, ROUTE_METHODS, ROUTE_PARAMS, ROUTE_REDIRECT, ROUTE_STATUS, Redirect, type RedirectMeta, Reflector, Req, RequestTimeoutException, Res, type RevalidationSignal, type RouteDefinition, type RouteMap, type RouteMethodEntry, type RouteRegistration, type RouteTree, type ServeAgent, ServiceUnavailableException, Session, SetMetadata, SkipThrottle, type StaticOptions, type StreamRenderOptions, type StreamRenderResult, type SwcCore, type TheoRequestContext, Throttle, type ThrottleOptions, TooManyRequestsException, type TypedClient, TypedClientError, USE_FILTERS, USE_GUARDS, USE_INTERCEPTORS, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UseFilters, UseGuards, UseInterceptors, type WalkResult, composeComponentTree, contract, createDecorator, createDecoratorHandler, createDecoratorServer, createExecutionContext, createStaticHandler, createTypedClient, digestError, getMeta, getMimeType, getRequestContext, getRevalidationSignals, getThrottleOptions, isControllerClass, isSafePath, isThrottleSkipped, joinPath, loadControllerWithSwc, loadControllersFromGlob, registerControllers, renderToStream, resolveDtoSchema, revalidatePath, revalidateTag, runExceptionFilters, runInterceptors, setMeta, streamToResponse, transformControllerSource, tryGetRequestContext, walkControllerMetadata };
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
renderToStream,
|
|
8
8
|
streamToResponse,
|
|
9
9
|
tryGetRequestContext
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GXTGOPRU.js";
|
|
11
11
|
import {
|
|
12
12
|
MiddlewareConsumerImpl,
|
|
13
13
|
isControllerClass,
|
|
@@ -16,14 +16,14 @@ import {
|
|
|
16
16
|
middlewareMatchesPath,
|
|
17
17
|
runMiddleware,
|
|
18
18
|
transformControllerSource
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-OBHHOS6E.js";
|
|
20
20
|
import {
|
|
21
21
|
HttpDecoratorsConfigError,
|
|
22
22
|
createExecutionContext,
|
|
23
23
|
joinPath,
|
|
24
24
|
resolveDtoSchema,
|
|
25
25
|
walkControllerMetadata
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-JQMJK47T.js";
|
|
27
27
|
import {
|
|
28
28
|
createNodeAdapter
|
|
29
29
|
} from "./chunk-HLW7YKZE.js";
|
|
@@ -32,13 +32,14 @@ import {
|
|
|
32
32
|
} from "./chunk-ELCXHPAD.js";
|
|
33
33
|
import {
|
|
34
34
|
runExceptionFilters
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-CMPP4ULU.js";
|
|
36
36
|
import {
|
|
37
37
|
BadGatewayException,
|
|
38
38
|
BadRequestException,
|
|
39
39
|
CATCH_EXCEPTIONS,
|
|
40
40
|
CONTROLLER_PREFIX,
|
|
41
41
|
ConflictException,
|
|
42
|
+
EXPOSE_AGENT,
|
|
42
43
|
ForbiddenException,
|
|
43
44
|
GatewayTimeoutException,
|
|
44
45
|
GoneException,
|
|
@@ -69,7 +70,7 @@ import {
|
|
|
69
70
|
UnsupportedMediaTypeException,
|
|
70
71
|
getMeta,
|
|
71
72
|
setMeta
|
|
72
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-KPC7AIVC.js";
|
|
73
74
|
import {
|
|
74
75
|
resolveOrNew
|
|
75
76
|
} from "./chunk-MQAJWR3K.js";
|
|
@@ -178,6 +179,20 @@ function Res(opts) {
|
|
|
178
179
|
}
|
|
179
180
|
__name(Res, "Res");
|
|
180
181
|
|
|
182
|
+
// src/decorators/expose.ts
|
|
183
|
+
function Expose(agent, opts = {}) {
|
|
184
|
+
return (target, propertyKey) => {
|
|
185
|
+
const existing = getMeta(EXPOSE_AGENT, target.constructor) ?? [];
|
|
186
|
+
existing.push({
|
|
187
|
+
agent,
|
|
188
|
+
opts,
|
|
189
|
+
propertyKey
|
|
190
|
+
});
|
|
191
|
+
setMeta(EXPOSE_AGENT, target.constructor, existing);
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
__name(Expose, "Expose");
|
|
195
|
+
|
|
181
196
|
// src/decorators/response.ts
|
|
182
197
|
function HttpCode(status) {
|
|
183
198
|
return (target, propertyKey) => {
|
|
@@ -424,10 +439,11 @@ __name(registerControllers, "registerControllers");
|
|
|
424
439
|
// src/bridge/create-server.ts
|
|
425
440
|
import "reflect-metadata";
|
|
426
441
|
function createDecoratorHandler(controllersOrOpts) {
|
|
427
|
-
const { controllers, container, configure } = Array.isArray(controllersOrOpts) ? {
|
|
442
|
+
const { controllers, container, configure, serveAgent } = Array.isArray(controllersOrOpts) ? {
|
|
428
443
|
controllers: controllersOrOpts,
|
|
429
444
|
container: void 0,
|
|
430
|
-
configure: void 0
|
|
445
|
+
configure: void 0,
|
|
446
|
+
serveAgent: void 0
|
|
431
447
|
} : controllersOrOpts;
|
|
432
448
|
const middlewareConsumer = new MiddlewareConsumerImpl(container);
|
|
433
449
|
if (configure) configure(middlewareConsumer);
|
|
@@ -456,7 +472,7 @@ function createDecoratorHandler(controllersOrOpts) {
|
|
|
456
472
|
if (aP !== bP) return aP ? 1 : -1;
|
|
457
473
|
return 0;
|
|
458
474
|
});
|
|
459
|
-
const handler = /* @__PURE__ */ __name((request) => handleRequest(routes, request, container, middlewareEntries), "handler");
|
|
475
|
+
const handler = /* @__PURE__ */ __name((request) => handleRequest(routes, request, container, middlewareEntries, serveAgent), "handler");
|
|
460
476
|
handler.matches = (method, pathname) => findRoute(routes, method.toUpperCase(), pathname) !== null;
|
|
461
477
|
return handler;
|
|
462
478
|
}
|
|
@@ -477,7 +493,7 @@ function createDecoratorServer(controllersOrOpts) {
|
|
|
477
493
|
});
|
|
478
494
|
}
|
|
479
495
|
__name(createDecoratorServer, "createDecoratorServer");
|
|
480
|
-
async function handleRequest(routes, request, container, middlewareEntries = []) {
|
|
496
|
+
async function handleRequest(routes, request, container, middlewareEntries = [], serveAgent) {
|
|
481
497
|
const url = new URL(request.url);
|
|
482
498
|
const method = request.method.toUpperCase();
|
|
483
499
|
const pathname = url.pathname;
|
|
@@ -490,6 +506,17 @@ async function handleRequest(routes, request, container, middlewareEntries = [])
|
|
|
490
506
|
const ctx = createExecutionContext(request, instance.constructor, walk.propertyKey);
|
|
491
507
|
const guardResponse = await runGuards(walk.guards, ctx, container);
|
|
492
508
|
if (guardResponse) return guardResponse;
|
|
509
|
+
if (walk.agent) {
|
|
510
|
+
if (!serveAgent) {
|
|
511
|
+
return jsonResponse(500, {
|
|
512
|
+
error: {
|
|
513
|
+
code: "AGENT_SERVER_NOT_WIRED",
|
|
514
|
+
message: `Controller route ${String(walk.propertyKey)} is @Expose-bound but no serveAgent was provided to createDecoratorHandler. The framework must wire serveAgent (mountAgent).`
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
return await serveAgent(walk.agent.module, request, walk.agent.opts);
|
|
519
|
+
}
|
|
493
520
|
const body = await resolveBody(method, request, walk);
|
|
494
521
|
if (body instanceof Response) return body;
|
|
495
522
|
const args = buildArgs(walk.paramEntries, {
|
|
@@ -865,6 +892,8 @@ export {
|
|
|
865
892
|
ConflictException,
|
|
866
893
|
Controller,
|
|
867
894
|
Delete,
|
|
895
|
+
EXPOSE_AGENT,
|
|
896
|
+
Expose,
|
|
868
897
|
ForbiddenException,
|
|
869
898
|
GatewayTimeoutException,
|
|
870
899
|
Get,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/decorators/controller.ts","../src/decorators/methods.ts","../src/decorators/params.ts","../src/decorators/response.ts","../src/decorators/middleware.ts","../src/decorators/set-metadata.ts","../src/decorators/throttle.ts","../src/bridge/register-controllers.ts","../src/bridge/create-server.ts","../src/typed-client.ts","../src/contract.ts","../src/error-digest.ts","../src/component-tree.ts","../src/cache-signal.ts"],"sourcesContent":["import { setMeta, CONTROLLER_PREFIX } from '../metadata/index.js'\n\nexport interface ControllerOptions {\n host?: string\n}\n\nexport interface ControllerMeta {\n prefix: string\n host?: string\n}\n\n/**\n * Infer route prefix from class name (Rails-style convention naming).\n *\n * UsersController → api/users\n * TasksController → api/tasks\n * AuthController → api/auth\n * HealthCheckController → api/health-check\n *\n * Strips \"Controller\" suffix, converts PascalCase to kebab-case,\n * prepends \"api/\".\n */\nfunction inferPrefix(className: string): string {\n const stripped = className.replace(/Controller$/, '')\n const kebab = stripped\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase()\n return `api/${kebab}`\n}\n\n/**\n * Class decorator that declares a route-prefix scope.\n *\n * Convention over configuration:\n * @Controller() → prefix inferred from class name\n * @Controller('api/users') → explicit prefix\n *\n * @example\n * ```ts\n * // Convention: UsersController → /api/users (zero config)\n * @Controller()\n * class UsersController { ... }\n *\n * // Explicit: override when convention doesn't fit\n * @Controller('api/v2/users')\n * class UsersController { ... }\n * ```\n */\nexport function Controller(prefix?: string, opts?: ControllerOptions): ClassDecorator\nexport function Controller(opts?: ControllerOptions): ClassDecorator\nexport function Controller(\n prefixOrOpts?: string | ControllerOptions,\n maybeOpts?: ControllerOptions,\n): ClassDecorator {\n return (target) => {\n let prefix: string\n let opts: ControllerOptions\n\n if (typeof prefixOrOpts === 'string') {\n prefix = prefixOrOpts\n opts = maybeOpts ?? {}\n } else {\n // Convention naming: infer from class name\n prefix = inferPrefix(target.name)\n opts = prefixOrOpts ?? {}\n }\n\n setMeta<ControllerMeta>(CONTROLLER_PREFIX, target, {\n prefix,\n host: opts.host,\n })\n }\n}\n","import { setMeta, getMeta, ROUTE_METHODS } from '../metadata/index.js'\n\nexport type HttpVerb = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'ALL'\n\nexport interface RouteMethodEntry {\n verb: HttpVerb\n path: string\n propertyKey: string | symbol\n}\n\nfunction makeVerbDecorator(verb: HttpVerb) {\n return function (path = ''): MethodDecorator {\n return (target, propertyKey) => {\n const existing = getMeta<RouteMethodEntry[]>(ROUTE_METHODS, target.constructor) ?? []\n existing.push({ verb, path, propertyKey })\n setMeta(ROUTE_METHODS, target.constructor, existing)\n }\n }\n}\n\nexport const Get = makeVerbDecorator('GET')\nexport const Post = makeVerbDecorator('POST')\nexport const Put = makeVerbDecorator('PUT')\nexport const Patch = makeVerbDecorator('PATCH')\nexport const Delete = makeVerbDecorator('DELETE')\nexport const Options = makeVerbDecorator('OPTIONS')\nexport const Head = makeVerbDecorator('HEAD')\nexport const All = makeVerbDecorator('ALL')\n","import type { z } from 'zod'\n\nimport { setMeta, getMeta, ROUTE_PARAMS } from '../metadata/index.js'\n\nexport type ParamSource =\n | 'req'\n | 'res'\n | 'body'\n | 'param'\n | 'query'\n | 'headers'\n | 'session'\n | 'ip'\n | 'host'\n\nexport interface ParamEntry {\n source: ParamSource\n key?: string\n index: number\n passthrough?: boolean\n /** Explicit Zod schema — when set, bypasses design:paramtypes + DTO resolution.\n * Preferred path: `@Body(zMySchema)` works without emitDecoratorMetadata. */\n schema?: z.ZodType\n}\n\n/** Detect whether a value is a Zod schema (has `.safeParse()` method). */\nfunction isZodSchema(v: unknown): v is z.ZodType {\n return (\n v !== null &&\n v !== undefined &&\n typeof v === 'object' &&\n typeof (v as Record<string, unknown>).safeParse === 'function'\n )\n}\n\nfunction makeParamDecorator(source: ParamSource) {\n /**\n * Overloaded parameter decorator:\n * - `@Body()` / `@Param()` / `@Query()` — whole-object extraction\n * - `@Body('key')` / `@Param('key')` — named-field extraction\n * - `@Body(zodSchema)` — whole-object with explicit Zod validation\n * (works WITHOUT emitDecoratorMetadata — TheoKit \"Zod is SSoT\" alignment)\n */\n return function (keyOrSchema?: string | z.ZodType): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) return\n const map =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, target.constructor) ?? new Map()\n const entries = map.get(propertyKey) ?? []\n\n const entry: ParamEntry = { source, index: parameterIndex }\n if (typeof keyOrSchema === 'string') {\n entry.key = keyOrSchema\n } else if (isZodSchema(keyOrSchema)) {\n entry.schema = keyOrSchema\n }\n\n entries.push(entry)\n map.set(propertyKey, entries)\n setMeta(ROUTE_PARAMS, target.constructor, map)\n }\n }\n}\n\nexport const Req = makeParamDecorator('req')\nexport const Body = makeParamDecorator('body')\nexport const Param = makeParamDecorator('param')\nexport const Query = makeParamDecorator('query')\nexport const Headers = makeParamDecorator('headers')\nexport const Session = makeParamDecorator('session')\nexport const Ip = makeParamDecorator('ip')\nexport const HostParam = makeParamDecorator('host')\n\nexport function Res(opts?: { passthrough?: boolean }): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) return\n const map =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, target.constructor) ?? new Map()\n const entries = map.get(propertyKey) ?? []\n entries.push({\n source: 'res',\n index: parameterIndex,\n passthrough: opts?.passthrough,\n })\n map.set(propertyKey, entries)\n setMeta(ROUTE_PARAMS, target.constructor, map)\n }\n}\n","import { setMeta, getMeta, ROUTE_STATUS, ROUTE_HEADERS, ROUTE_REDIRECT } from '../metadata/index.js'\n\nexport function HttpCode(status: number): MethodDecorator {\n return (target, propertyKey) => {\n setMeta(ROUTE_STATUS, target.constructor, status, propertyKey as string)\n }\n}\n\nexport function Header(name: string, value: string): MethodDecorator {\n return (target, propertyKey) => {\n const existing =\n getMeta<[string, string][]>(ROUTE_HEADERS, target.constructor, propertyKey) ?? []\n existing.push([name, value])\n setMeta(ROUTE_HEADERS, target.constructor, existing, propertyKey as string)\n }\n}\n\nexport interface RedirectMeta {\n url: string\n status: number\n}\n\nexport function Redirect(url: string, status = 302): MethodDecorator {\n return (target, propertyKey) => {\n setMeta<RedirectMeta>(ROUTE_REDIRECT, target.constructor, { url, status }, propertyKey)\n }\n}\n","import {\n setMeta,\n getMeta,\n USE_GUARDS,\n USE_INTERCEPTORS,\n USE_FILTERS,\n CATCH_EXCEPTIONS,\n} from '../metadata/index.js'\n\nexport function UseGuards(...guards: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_GUARDS, actualTarget, propertyKey) ?? []\n setMeta(USE_GUARDS, actualTarget, [...existing, ...guards], propertyKey)\n }\n}\n\nexport function UseInterceptors(...interceptors: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_INTERCEPTORS, actualTarget, propertyKey) ?? []\n setMeta(USE_INTERCEPTORS, actualTarget, [...existing, ...interceptors], propertyKey)\n }\n}\n\nexport function UseFilters(...filters: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_FILTERS, actualTarget, propertyKey) ?? []\n setMeta(USE_FILTERS, actualTarget, [...existing, ...filters], propertyKey)\n }\n}\n\n/** @Catch(ExceptionType, ...) — marks which exception types an ExceptionFilter handles.\n * Empty args = catch-all filter. */\nexport function Catch(...exceptions: Function[]): ClassDecorator {\n return (target: object) => {\n setMeta(CATCH_EXCEPTIONS, target, exceptions)\n }\n}\n","/**\n * @SetMetadata + Reflector — NestJS-style custom metadata for guards.\n *\n * Enables role-based auth pattern:\n * const Roles = createDecorator<string[]>()\n * @Roles(['admin']) → guard reads via reflector.get(Roles, handler)\n */\nimport 'reflect-metadata'\n\n/** Unique key type for type-safe metadata decorators. */\nexport type MetadataKey<T> = symbol & { __type?: T }\n\n/**\n * Create a typed decorator that attaches metadata to a handler or class.\n * NestJS equivalent: `Reflector.createDecorator<T>()`.\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n *\n * @Controller('cats')\n * class CatsController {\n * @Post()\n * @Roles(['admin'])\n * create() { ... }\n * }\n * ```\n */\n/** Monotonic counter for unique decorator keys — deterministic, no randomness. */\nlet decoratorKeyCounter = 0\n\nexport function createDecorator<T>(): (value: T) => MethodDecorator & ClassDecorator {\n const key = Symbol.for(`theokit:custom:${++decoratorKeyCounter}`) as MetadataKey<T>\n\n const decorator = (value: T): MethodDecorator & ClassDecorator => {\n return (target: object, propertyKey?: string | symbol) => {\n const metaTarget = propertyKey !== undefined ? target.constructor : target\n Reflect.defineMetadata(key, value, metaTarget, propertyKey as string)\n }\n }\n\n // Attach the key so Reflector can read it\n ;(decorator as unknown as { key: MetadataKey<T> }).key = key\n return decorator\n}\n\n/**\n * Low-level @SetMetadata decorator — attaches arbitrary metadata.\n * NestJS equivalent: `@SetMetadata(key, value)`.\n *\n * Prefer `createDecorator<T>()` for type-safe metadata.\n */\nexport function SetMetadata<T>(\n metaKey: string | symbol,\n value: T,\n): MethodDecorator & ClassDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const metaTarget = propertyKey !== undefined ? target.constructor : target\n Reflect.defineMetadata(metaKey, value, metaTarget, propertyKey as string)\n }\n}\n\n/**\n * Reflector — reads metadata set by createDecorator or @SetMetadata.\n * NestJS-compatible Reflector with getAllAndOverride/getAllAndMerge.\n * HTTP-only per ADR D1.\n */\nexport class Reflector {\n /**\n * Read metadata set by a typed decorator created via createDecorator<T>().\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n * const reflector = new Reflector()\n * const roles = reflector.get(Roles, handlerFn) // string[] | undefined\n * ```\n */\n get<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n const key = (decorator as { key?: MetadataKey<T> }).key\n if (!key) return undefined\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n }\n\n /**\n * Read metadata set by @SetMetadata(key, value).\n */\n getByKey<T>(\n key: string | symbol,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n }\n\n /**\n * Read metadata checking method-level first, then class-level.\n * Returns the first non-undefined value found.\n *\n * NestJS equivalent: `reflector.getAllAndOverride(ROLES_KEY, [context.getHandler(), context.getClass()])`\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n * // In a guard:\n * const roles = reflector.getAllAndOverride(Roles, context.getClass(), context.getMethodName())\n * // Checks method-level @Roles first, falls back to class-level @Roles\n * ```\n */\n getAllAndOverride<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n const methodLevel = this.get(decorator, target, propertyKey)\n if (methodLevel !== undefined) return methodLevel\n }\n return this.get(decorator, target)\n }\n\n /**\n * Read metadata checking method-level first, then class-level, by raw key.\n * Returns the first non-undefined value found.\n */\n getAllAndOverrideByKey<T>(\n key: string | symbol,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n const methodLevel = this.getByKey<T>(key, target, propertyKey)\n if (methodLevel !== undefined) return methodLevel\n }\n return this.getByKey<T>(key, target)\n }\n\n /**\n * Read metadata from both method-level and class-level, merging arrays.\n * Returns all found values as a flat array.\n *\n * NestJS equivalent: `reflector.getAllAndMerge(ROLES_KEY, [context.getHandler(), context.getClass()])`\n *\n * @example\n * ```ts\n * const Tags = createDecorator<string[]>()\n *\n * @Tags(['api'])\n * @Controller('cats')\n * class CatsCtrl {\n * @Tags(['read'])\n * @Get()\n * findAll() {}\n * }\n *\n * reflector.getAllAndMerge(Tags, CatsCtrl, 'findAll')\n * // → ['read', 'api'] (method + class merged)\n * ```\n */\n getAllAndMerge<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T extends (infer U)[] ? U[] : T[] {\n const result: unknown[] = []\n if (propertyKey !== undefined) {\n const methodLevel = this.get(decorator, target, propertyKey)\n if (methodLevel !== undefined) {\n if (Array.isArray(methodLevel)) result.push(...methodLevel)\n else result.push(methodLevel)\n }\n }\n const classLevel = this.get(decorator, target)\n if (classLevel !== undefined) {\n if (Array.isArray(classLevel)) result.push(...classLevel)\n else result.push(classLevel)\n }\n return result as T extends (infer U)[] ? U[] : T[]\n }\n}\n","/**\n * @Throttle() + @SkipThrottle() — NestJS-style rate limiting decorators.\n *\n * Thin bridge over @theokit/plugin-rate-limit. These decorators store\n * metadata that the rate-limit plugin reads at request time to override\n * or skip the global throttle config per route/controller.\n *\n * Usage:\n * ```ts\n * @Controller('api/tasks')\n * @Throttle({ limit: 100, ttl: 60_000 }) // 100 req/min for all routes\n * class TasksController {\n * @Get()\n * @SkipThrottle() // no rate limit on this route\n * health() { return { ok: true } }\n *\n * @Post()\n * @Throttle({ limit: 5, ttl: 60_000 }) // stricter: 5 req/min\n * create(@Body(schema) body) { ... }\n * }\n * ```\n */\nimport { setMeta, getMeta } from '../metadata/index.js'\n\nconst THROTTLE_KEY = Symbol.for('theokit:http-decorators:throttle')\nconst SKIP_THROTTLE_KEY = Symbol.for('theokit:http-decorators:skip-throttle')\n\nexport interface ThrottleOptions {\n /** Maximum requests within the TTL window. */\n limit: number\n /** Time-to-live in milliseconds. */\n ttl: number\n /** Optional throttle set name (for multiple throttler definitions). */\n name?: string\n}\n\n/**\n * Override the global rate limit for a controller or specific route.\n * NestJS equivalent: `@Throttle({ default: { limit, ttl } })`.\n */\nexport function Throttle(options: ThrottleOptions): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey !== undefined ? target.constructor : target\n setMeta(THROTTLE_KEY, actualTarget, options, propertyKey)\n }\n}\n\n/**\n * Skip rate limiting for a controller or specific route.\n * NestJS equivalent: `@SkipThrottle()`.\n *\n * @param skip — defaults to `true`. Pass `false` to re-enable on a\n * specific route inside a skipped controller.\n */\nexport function SkipThrottle(skip = true): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey !== undefined ? target.constructor : target\n setMeta(SKIP_THROTTLE_KEY, actualTarget, skip, propertyKey)\n }\n}\n\n/**\n * Read throttle metadata for a given class or method.\n * Used by the rate-limit plugin to resolve per-route overrides.\n */\nexport function getThrottleOptions(\n target: Function,\n propertyKey?: string | symbol,\n): ThrottleOptions | undefined {\n return getMeta<ThrottleOptions>(THROTTLE_KEY, target, propertyKey)\n}\n\n/**\n * Check if throttling is skipped for a given class or method.\n */\nexport function isThrottleSkipped(target: Function, propertyKey?: string | symbol): boolean {\n return getMeta<boolean>(SKIP_THROTTLE_KEY, target, propertyKey) ?? false\n}\n","import type { HttpVerb } from '../decorators/methods.js'\n\nimport { walkControllerMetadata, type WalkResult } from './walk-metadata.js'\n\nexport interface RouteRegistration {\n verb: HttpVerb\n fullPath: string\n walkResult: WalkResult\n}\n\n/**\n * Low-level API: walks decorator metadata per controller class and returns\n * structured route descriptors. Used internally by the Vite plugin (ADR D7)\n * and available to advanced consumers who don't use Vite.\n *\n * EC-5: deduplicates by class reference; warns on duplicates.\n */\nexport function registerControllers(controllers: Function[]): RouteRegistration[] {\n const seen = new Set<Function>()\n const unique: Function[] = []\n for (const Ctor of controllers) {\n if (seen.has(Ctor)) {\n console.warn(\n `[@theokit/http] Controller ${Ctor.name} registered multiple times — dropping duplicate`,\n )\n continue\n }\n seen.add(Ctor)\n unique.push(Ctor)\n }\n return unique.flatMap((Ctor) => {\n const walks = walkControllerMetadata(Ctor)\n return walks.map((w) => ({\n verb: w.verb,\n fullPath: w.fullPath,\n walkResult: w,\n }))\n })\n}\n","/* eslint-disable security/detect-non-literal-regexp --\n * Route patterns like /cats/:id are converted to regex at startup —\n * NOT from user HTTP input. The patterns come from decorator metadata\n * authored by the developer. No injection vector.\n */\nimport 'reflect-metadata'\n\nimport type { ParamEntry } from '../decorators/params.js'\nimport { ForbiddenException } from '../exceptions/http-exception.js'\n\nimport { resolveOrNew, type DiContainer } from './di-resolve.js'\nexport type { DiContainer } from './di-resolve.js'\nimport { runExceptionFilters } from './exception-filter-chain.js'\nimport {\n createExecutionContext,\n type CanActivate,\n type ExecutionContext,\n} from './execution-context.js'\nimport { runInterceptors } from './interceptor-chain.js'\nimport {\n MiddlewareConsumerImpl,\n runMiddleware,\n type ResolvedMiddleware,\n} from './middleware-consumer.js'\nimport { createNodeAdapter } from './runtime/node.js'\nimport { walkControllerMetadata, type WalkResult } from './walk-metadata.js'\n\n/**\n * Creates a real HTTP server from decorated controller classes.\n * Uses Web Standard Request/Response internally; Node adapter at the boundary.\n */\nexport interface CreateDecoratorServerOptions {\n controllers: Function[]\n container?: DiContainer\n configure?: (consumer: MiddlewareConsumerImpl) => void\n}\n\n/**\n * A pure Web-Standard controller handler: callable as `(request) => Response | null`\n * plus a non-executing `matches(method, pathname)` route probe (so a host can gate\n * — e.g. CSRF — before dispatch runs a handler).\n */\nexport interface DecoratorHandler {\n (request: Request): Promise<Response | null>\n /** True when a controller route owns `method` + `pathname` (no handler executed). */\n matches(method: string, pathname: string): boolean\n}\n\n/**\n * Build a pure Web-Standard request handler from decorated controller classes,\n * WITHOUT binding a network listener. Returns a {@link DecoratorHandler} whose\n * call returns `null` when no controller route matched — the caller decides the\n * miss (a standalone server answers 404; a host middleware falls through to its\n * own routing). This is the reusable dispatch seam consumed by the framework's\n * controller dispatch (#122) so it never re-implements match/bind/validate.\n */\nexport function createDecoratorHandler(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n): DecoratorHandler {\n const { controllers, container, configure } = Array.isArray(controllersOrOpts)\n ? { controllers: controllersOrOpts, container: undefined, configure: undefined }\n : controllersOrOpts\n\n // Collect middleware\n const middlewareConsumer = new MiddlewareConsumerImpl(container)\n if (configure) configure(middlewareConsumer)\n const middlewareEntries = middlewareConsumer.getEntries()\n\n // Dedupe controllers (EC-5)\n const seen = new Set<Function>()\n const unique: Function[] = []\n for (const Ctor of controllers) {\n if (seen.has(Ctor)) continue\n seen.add(Ctor)\n unique.push(Ctor)\n }\n\n // Walk metadata\n const routes: { walk: WalkResult; instance: object }[] = []\n for (const Ctor of unique) {\n const instance = resolveOrNew(Ctor, container)\n const walks = walkControllerMetadata(Ctor)\n for (const w of walks) {\n routes.push({ walk: w, instance })\n }\n }\n\n // Sort: static routes first\n routes.sort((a, b) => {\n const aP = a.walk.fullPath.includes(':')\n const bP = b.walk.fullPath.includes(':')\n if (aP !== bP) return aP ? 1 : -1\n return 0\n })\n\n const handler = ((request: Request) =>\n handleRequest(routes, request, container, middlewareEntries)) as DecoratorHandler\n handler.matches = (method: string, pathname: string): boolean =>\n findRoute(routes, method.toUpperCase(), pathname) !== null\n return handler\n}\n\nexport function createDecoratorServer(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n) {\n const handle = createDecoratorHandler(controllersOrOpts)\n // Standalone server: a no-match (handler returns null) becomes a 404.\n const adapter = createNodeAdapter()\n return adapter.createServer(async (request: Request) => {\n const res = await handle(request)\n if (res) return res\n const { pathname } = new URL(request.url)\n return jsonResponse(404, {\n error: {\n code: 'NOT_FOUND',\n message: `No route for ${request.method.toUpperCase()} ${pathname}`,\n },\n })\n })\n}\n\n// ─── Web Standard request handler ────────────────────────────\n\nasync function handleRequest(\n routes: { walk: WalkResult; instance: object }[],\n request: Request,\n container?: DiContainer,\n middlewareEntries: ResolvedMiddleware[] = [],\n): Promise<Response | null> {\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n const pathname = url.pathname\n\n const match = findRoute(routes, method, pathname)\n // null = no controller route matched; the caller owns the miss (404 or fall-through).\n if (!match) return null\n\n const { walk, instance, params } = match\n\n try {\n // Middleware\n const mwResponse = await runMiddleware(middlewareEntries, request, pathname)\n if (mwResponse) return mwResponse\n\n // Guards\n const ctx = createExecutionContext(request, instance.constructor, walk.propertyKey)\n const guardResponse = await runGuards(walk.guards, ctx, container)\n if (guardResponse) return guardResponse\n\n // Body\n const body = await resolveBody(method, request, walk)\n if (body instanceof Response) return body // validation error response\n\n // Build args\n const args = buildArgs(walk.paramEntries, {\n request,\n body,\n params,\n query: Object.fromEntries(url.searchParams),\n })\n\n // Redirect\n if (walk.redirect) {\n return new Response(null, {\n status: walk.redirect.status,\n headers: { location: walk.redirect.url },\n })\n }\n\n const handlerFn = (instance as Record<string | symbol, Function>)[walk.propertyKey]\n\n // Interceptors wrap handler\n const result = await runInterceptors(\n walk.interceptors,\n () => handlerFn.apply(instance, args) as Promise<unknown>,\n request,\n container,\n )\n\n // A handler may return a Web `Response` directly (Set-Cookie, custom status /\n // headers) — parity with file-based `route()`. Pass it through untouched\n // instead of JSON-stringifying it into `{}`.\n if (result instanceof Response) return result\n\n return buildResponse(result, walk, method)\n } catch (err) {\n return runExceptionFilters(err, walk.filters, request, container)\n }\n}\n\n// ─── Guards (return Response on rejection, null on pass) ─────\n\nasync function runGuards(\n guards: Function[],\n context: ExecutionContext,\n container?: DiContainer,\n): Promise<Response | null> {\n for (const GuardCtor of guards) {\n const guard = resolveOrNew(GuardCtor, container) as CanActivate\n const allowed = await guard.canActivate(context)\n if (!allowed) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n return null\n}\n\n// ─── Body resolution ─────────────────────────────────────────\n\nasync function resolveBody(method: string, request: Request, walk: WalkResult): Promise<unknown> {\n if (!['POST', 'PUT', 'PATCH'].includes(method)) return undefined\n\n let body: unknown\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch {\n body = undefined\n }\n\n if (walk.bodySchema && body !== undefined) {\n const result = walk.bodySchema.safeParse(body)\n if (!result.success) {\n return jsonResponse(422, { error: { code: 'VALIDATION_ERROR', issues: result.error.issues } })\n }\n body = result.data\n }\n return body\n}\n\n// ─── Response builder ────────────────────────────────────────\n\nfunction buildResponse(result: unknown, walk: WalkResult, method: string): Response {\n const status = walk.status ?? (method === 'POST' ? 201 : 200)\n const headers: Record<string, string> = { 'content-type': 'application/json' }\n for (const [name, value] of walk.headers) {\n headers[name.toLowerCase()] = value\n }\n\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\n }\n\n return new Response(JSON.stringify(result), { status, headers })\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n// ─── Route matching ──────────────────────────────────────────\n\ninterface RouteMatch {\n walk: WalkResult\n instance: object\n params: Record<string, string>\n}\n\nfunction findRoute(\n routes: { walk: WalkResult; instance: object }[],\n method: string,\n pathname: string,\n): RouteMatch | null {\n for (const { walk, instance } of routes) {\n if (walk.verb !== 'ALL' && walk.verb !== method) continue\n const params = matchPath(walk.fullPath, pathname)\n if (params !== null) return { walk, instance, params }\n }\n return null\n}\n\nfunction matchPath(pattern: string, pathname: string): Record<string, string> | null {\n const paramNames: string[] = []\n const regexStr = pattern.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n const match = new RegExp(`^${regexStr}$`).exec(pathname)\n if (!match) return null\n const params: Record<string, string> = {}\n paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return params\n}\n\n// ─── Argument builder ────────────────────────────────────────\n\ninterface ArgContext {\n request: Request\n body: unknown\n params: Record<string, string>\n query: Record<string, string>\n}\n\nfunction buildArgs(paramEntries: ParamEntry[], ctx: ArgContext): unknown[] {\n if (paramEntries.length === 0) return []\n const maxIndex = Math.max(...paramEntries.map((p) => p.index))\n const args: unknown[] = Array.from({ length: maxIndex + 1 }, () => undefined)\n for (const p of paramEntries) {\n switch (p.source) {\n case 'req':\n args[p.index] = ctx.request\n break\n case 'body':\n args[p.index] = p.key ? (ctx.body as Record<string, unknown>)[p.key] : ctx.body\n break\n case 'param':\n args[p.index] = p.key ? ctx.params[p.key] : ctx.params\n break\n case 'query':\n args[p.index] = p.key ? ctx.query[p.key] : ctx.query\n break\n case 'headers':\n args[p.index] = p.key\n ? ctx.request.headers.get(p.key.toLowerCase())\n : Object.fromEntries(ctx.request.headers.entries())\n break\n case 'ip':\n args[p.index] = ctx.request.headers.get('x-forwarded-for') ?? '127.0.0.1'\n break\n case 'session':\n args[p.index] = undefined\n break\n default:\n args[p.index] = undefined\n }\n }\n return args\n}\n","/**\n * Typed Client — end-to-end type inference from route contracts.\n *\n * Zero codegen, zero runtime overhead on types. The developer defines\n * a route map using `contract()`, and `createTypedClient<T>()` infers\n * request body + response types automatically.\n */\nimport type { z } from 'zod'\n\n// ── Route Map types ──\n\nexport interface RouteDefinition {\n body?: z.ZodType\n params?: Record<string, 'string' | 'number'>\n query?: Record<string, 'string' | 'number' | 'boolean'>\n response: unknown\n}\n\nexport type RouteMap = Record<string, RouteDefinition>\n\n// ── Type extraction ──\n\ntype InferBody<D> = D extends { body: z.ZodType } ? z.infer<D['body']> : never\ntype InferResponse<D> = D extends { response: infer R } ? R : unknown\n\n// ── Client interface (simplified — works with strict DTS) ──\n\nexport interface TypedClient<M extends RouteMap> {\n get<P extends string>(\n path: P,\n opts?: { query?: Record<string, string>; headers?: Record<string, string> },\n ): Promise<InferResponse<M[`GET ${P}`]>>\n\n post<P extends string>(\n path: P,\n body?: InferBody<M[`POST ${P}`]>,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`POST ${P}`]>>\n\n put<P extends string>(\n path: P,\n body?: InferBody<M[`PUT ${P}`]>,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`PUT ${P}`]>>\n\n delete<P extends string>(\n path: P,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`DELETE ${P}`]>>\n}\n\n// ── Client factory ──\n\nexport function createTypedClient<M extends RouteMap>(\n baseUrl: string,\n defaultHeaders?: Record<string, string>,\n): TypedClient<M> {\n async function request(\n method: string,\n path: string,\n body?: unknown,\n opts?: { headers?: Record<string, string>; query?: Record<string, string> },\n ) {\n const url = new URL(path, baseUrl)\n if (opts?.query) {\n for (const [k, v] of Object.entries(opts.query)) url.searchParams.set(k, v)\n }\n const headers: Record<string, string> = { ...defaultHeaders, ...opts?.headers }\n if (body !== undefined) headers['content-type'] = 'application/json'\n\n const res = await fetch(url.toString(), {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ message: res.statusText }))\n throw new TypedClientError(res.status, error as Record<string, unknown>)\n }\n\n if (res.status === 204) return undefined\n return res.json()\n }\n\n return {\n get: (path: string, opts?: Record<string, unknown>) => request('GET', path, undefined, opts),\n post: (path: string, body?: unknown, opts?: Record<string, unknown>) =>\n request('POST', path, body, opts),\n put: (path: string, body?: unknown, opts?: Record<string, unknown>) =>\n request('PUT', path, body, opts),\n delete: (path: string, opts?: Record<string, unknown>) =>\n request('DELETE', path, undefined, opts),\n } as TypedClient<M>\n}\n\nexport class TypedClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: Record<string, unknown>,\n ) {\n super(`HTTP ${status}: ${JSON.stringify(body)}`)\n this.name = 'TypedClientError'\n }\n}\n","/**\n * Route Contract — type-level bridge between @Controller and TypedClient.\n *\n * The developer defines a contract object mapping routes to their types.\n * This object is the single source of truth for both server validation\n * and client type inference.\n *\n * @example\n * ```ts\n * // server/contracts.ts — shared between server and client\n * import { z } from 'zod'\n * import { contract } from '@theokit/http'\n *\n * export const zCreateTask = z.object({\n * title: z.string().min(3),\n * priority: z.enum(['low', 'medium', 'high']).default('medium'),\n * })\n *\n * export interface Task { id: number; title: string; priority: string; done: boolean }\n *\n * export const routes = contract({\n * 'GET /api/tasks': { response: [] as Task[] },\n * 'GET /api/tasks/:id': { response: {} as Task },\n * 'POST /api/tasks': { body: zCreateTask, response: {} as Task },\n * 'PUT /api/tasks/:id': { body: z.object({ done: z.boolean() }), response: {} as Task },\n * 'DELETE /api/tasks/:id': { response: undefined as void },\n * })\n * export type AppRoutes = typeof routes\n * ```\n *\n * The `contract()` function is identity at runtime (zero overhead) but\n * enforces the RouteMap type at the type level, enabling full inference.\n */\nimport type { RouteMap } from './typed-client.js'\n\n/**\n * Identity function that enforces RouteMap type constraint.\n * Zero runtime overhead — exists only for type inference.\n */\nexport function contract<T extends RouteMap>(routes: T): T {\n return routes\n}\n","/**\n * Error digestion — converts any thrown value into a stable hash + context.\n *\n * Inspired by Next.js `create-error-handler.tsx`. Produces a deterministic\n * digest ID suitable for logging and client-safe error references without\n * leaking stack traces in production.\n *\n * Uses djb2 hash (sync, no crypto dependency) per ADR D3.\n */\n\nimport { HttpException } from './exceptions/http-exception.js'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ErrorContext {\n route?: string\n phase?: 'guard' | 'interceptor' | 'handler' | 'filter' | 'agent'\n source?: string\n}\n\nexport interface DigestedError {\n digest: string\n message: string\n status: number\n context: ErrorContext\n stack?: string\n}\n\n// ---------------------------------------------------------------------------\n// djb2 hash — deterministic, sync, no crypto dependency\n// ---------------------------------------------------------------------------\n\nfunction djb2(input: string): string {\n let hash = 5381\n for (let i = 0; i < input.length; i++) {\n // hash * 33 + charCode — classic djb2\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0\n }\n // Convert to unsigned 32-bit hex\n return (hash >>> 0).toString(16)\n}\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\n/**\n * Converts any thrown value into a structured {@link DigestedError}.\n *\n * - Sync (never async) — safe to call inside catch blocks.\n * - Stack trace stripped when `process.env.NODE_ENV === 'production'`.\n * - Preserves {@link HttpException} status codes.\n * - Handles non-Error throws (string, number, object).\n */\nexport function digestError(err: unknown, context: ErrorContext = {}): DigestedError {\n const message = extractMessage(err)\n const status = extractStatus(err)\n const rawStack = extractStack(err)\n\n const digest = djb2(message)\n\n const isProduction = process.env.NODE_ENV === 'production'\n\n return {\n digest,\n message,\n status,\n context,\n ...(rawStack && !isProduction ? { stack: rawStack } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractMessage(err: unknown): string {\n if (err instanceof Error) {\n return err.message\n }\n if (typeof err === 'string') {\n return err\n }\n if (typeof err === 'number') {\n return String(err)\n }\n // object / null / undefined / symbol / bigint\n try {\n return JSON.stringify(err)\n } catch {\n return 'Unknown error'\n }\n}\n\nfunction extractStatus(err: unknown): number {\n if (err instanceof HttpException) {\n return err.statusCode\n }\n return 500\n}\n\nfunction extractStack(err: unknown): string | undefined {\n if (err instanceof Error) {\n return err.stack\n }\n return undefined\n}\n","/**\n * Component tree composition — recursive wrapping of file-convention\n * components (layout, page, loading, error, not-found) into a React\n * element tree with Suspense and error boundaries.\n *\n * Inspired by Next.js `create-component-tree.tsx`.\n *\n * React is loaded via dynamic `import('react')` because it is an\n * optional peerDep of @theokit/http (EC-2).\n */\n\nimport type * as ReactTypes from 'react'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface RouteTree {\n layout?: ReactTypes.ComponentType<{ children: ReactTypes.ReactNode }>\n page?: ReactTypes.ComponentType\n loading?: ReactTypes.ComponentType\n error?: ReactTypes.ComponentType\n notFound?: ReactTypes.ComponentType\n children?: Record<string, RouteTree>\n}\n\n// ---------------------------------------------------------------------------\n// Error boundary — minimal class component wrapping error.tsx\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an ErrorBoundary class component using the provided React module.\n * Must be a class component — React has no hook-based error boundary API.\n */\nfunction createErrorBoundary(\n React: typeof ReactTypes,\n FallbackComponent: ReactTypes.ComponentType,\n): ReactTypes.ComponentType<{ children: ReactTypes.ReactNode }> {\n return class ErrorBoundary extends React.Component<\n { children: ReactTypes.ReactNode },\n { hasError: boolean }\n > {\n constructor(props: { children: ReactTypes.ReactNode }) {\n super(props)\n this.state = { hasError: false }\n }\n\n static getDerivedStateFromError(): { hasError: boolean } {\n return { hasError: true }\n }\n\n render(): ReactTypes.ReactElement | null {\n if (this.state.hasError) {\n return React.createElement(FallbackComponent)\n }\n return React.createElement(React.Fragment, null, this.props.children)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\n/**\n * Composes a {@link RouteTree} into a nested React element tree.\n *\n * Wrapping order (outermost → innermost):\n * layout → ErrorBoundary(error) → Suspense(loading) → page\n *\n * Returns `null` when no `page` component is found in the tree.\n *\n * @param tree - The route tree describing file conventions found.\n * @returns A React element or `null`.\n */\nexport async function composeComponentTree(\n tree: RouteTree,\n): Promise<ReactTypes.ReactElement | null> {\n // Dynamic import — React is optional peerDep\n const React = await import('react')\n\n return composeNode(React, tree)\n}\n\nfunction composeNode(React: typeof ReactTypes, node: RouteTree): ReactTypes.ReactElement | null {\n const { layout: Layout, page: Page, loading: Loading, error: ErrorFallback } = node\n\n // No page → nothing to render\n if (!Page) {\n return null\n }\n\n // Start with the page element\n let element: ReactTypes.ReactElement = React.createElement(Page)\n\n // Wrap with Suspense if loading component exists\n if (Loading) {\n element = React.createElement(\n React.Suspense,\n { fallback: React.createElement(Loading) },\n element,\n )\n }\n\n // Wrap with error boundary if error component exists\n if (ErrorFallback) {\n const Boundary = createErrorBoundary(React, ErrorFallback)\n element = React.createElement(Boundary, null, element)\n }\n\n // Wrap with layout if it exists\n if (Layout) {\n element = React.createElement(Layout, null, element)\n }\n\n return element\n}\n","/**\n * Cache revalidation signals — runtime-agnostic intent layer.\n *\n * Controllers/agents call `revalidateTag('tasks')` or `revalidatePath('/api/tasks')`\n * to signal that cached data is stale. The signals are collected in the request\n * context and consumed by the cache engine (when present).\n *\n * In standalone @theokit/http (no full theokit framework), signals are stored\n * but not executed — no cache engine is wired. When theokit is present, the\n * cache engine reads signals from the request context after the handler completes.\n *\n * Inspired by Next.js cache-signal.ts + revalidateTag/revalidatePath API.\n */\nimport { tryGetRequestContext } from './request-context.js'\n\nexport interface RevalidationSignal {\n kind: 'tag' | 'path'\n value: string\n timestamp: number\n}\n\n/**\n * Signal that a cache tag should be revalidated.\n *\n * Safe to call from any request handler (controller, agent, action).\n * Signals are accumulated per-request and consumed by the cache engine.\n *\n * @example\n * ```typescript\n * import { revalidateTag } from '@theokit/http'\n *\n * @Post()\n * async createTask(@Body(schema) body) {\n * const task = await db.tasks.create(body)\n * revalidateTag('tasks') // invalidate cached task lists\n * return task\n * }\n * ```\n */\nexport function revalidateTag(tag: string): void {\n collectSignal({ kind: 'tag', value: tag, timestamp: Date.now() })\n}\n\n/**\n * Signal that a cached path should be revalidated.\n *\n * @example\n * ```typescript\n * import { revalidatePath } from '@theokit/http'\n *\n * @Delete(':id')\n * async removeTask(@Param('id') id: string) {\n * await db.tasks.delete(id)\n * revalidatePath('/api/tasks')\n * }\n * ```\n */\nexport function revalidatePath(path: string): void {\n collectSignal({ kind: 'path', value: path, timestamp: Date.now() })\n}\n\n/**\n * Get all revalidation signals collected during the current request.\n * Called by the cache engine after the handler completes.\n */\nexport function getRevalidationSignals(): RevalidationSignal[] {\n const ctx = tryGetRequestContext()\n if (!ctx) return []\n return (\n (ctx as unknown as { _revalidationSignals?: RevalidationSignal[] })._revalidationSignals ?? []\n )\n}\n\n// ── Internal ──\n\nfunction collectSignal(signal: RevalidationSignal): void {\n const ctx = tryGetRequestContext()\n if (!ctx) {\n console.warn(\n '[theokit] revalidateTag/revalidatePath called outside a request context. Signal ignored.',\n )\n return\n }\n const extended = ctx as unknown as { _revalidationSignals?: RevalidationSignal[] }\n extended._revalidationSignals ??= []\n extended._revalidationSignals.push(signal)\n}\n"],"mappings":"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","names":["inferPrefix","className","stripped","replace","kebab","toLowerCase","Controller","prefixOrOpts","maybeOpts","target","prefix","opts","name","setMeta","CONTROLLER_PREFIX","host","makeVerbDecorator","verb","path","target","propertyKey","existing","getMeta","ROUTE_METHODS","push","setMeta","Get","Post","Put","Patch","Delete","Options","Head","All","isZodSchema","v","undefined","safeParse","makeParamDecorator","source","keyOrSchema","target","propertyKey","parameterIndex","map","getMeta","ROUTE_PARAMS","Map","entries","get","entry","index","key","schema","push","set","setMeta","Req","Body","Param","Query","Headers","Session","Ip","HostParam","Res","opts","passthrough","HttpCode","status","target","propertyKey","setMeta","ROUTE_STATUS","Header","name","value","existing","getMeta","ROUTE_HEADERS","push","Redirect","url","ROUTE_REDIRECT","UseGuards","guards","target","propertyKey","actualTarget","existing","getMeta","USE_GUARDS","setMeta","UseInterceptors","interceptors","USE_INTERCEPTORS","UseFilters","filters","USE_FILTERS","Catch","exceptions","CATCH_EXCEPTIONS","decoratorKeyCounter","createDecorator","key","Symbol","for","decorator","value","target","propertyKey","metaTarget","undefined","Reflect","defineMetadata","SetMetadata","metaKey","Reflector","get","getMetadata","getByKey","getAllAndOverride","methodLevel","getAllAndOverrideByKey","getAllAndMerge","result","Array","isArray","push","classLevel","THROTTLE_KEY","Symbol","for","SKIP_THROTTLE_KEY","Throttle","options","target","propertyKey","actualTarget","undefined","setMeta","SkipThrottle","skip","getThrottleOptions","getMeta","isThrottleSkipped","registerControllers","controllers","seen","Set","unique","Ctor","has","console","warn","name","add","push","flatMap","walks","walkControllerMetadata","map","w","verb","fullPath","walkResult","createDecoratorHandler","controllersOrOpts","controllers","container","configure","Array","isArray","undefined","middlewareConsumer","MiddlewareConsumerImpl","middlewareEntries","getEntries","seen","Set","unique","Ctor","has","add","push","routes","instance","resolveOrNew","walks","walkControllerMetadata","w","walk","sort","a","b","aP","fullPath","includes","bP","handler","request","handleRequest","matches","method","pathname","findRoute","toUpperCase","createDecoratorServer","handle","adapter","createNodeAdapter","createServer","res","URL","url","jsonResponse","error","code","message","match","params","mwResponse","runMiddleware","ctx","createExecutionContext","propertyKey","guardResponse","runGuards","guards","body","resolveBody","Response","args","buildArgs","paramEntries","query","Object","fromEntries","searchParams","redirect","status","headers","location","handlerFn","result","runInterceptors","interceptors","apply","buildResponse","err","runExceptionFilters","filters","context","GuardCtor","guard","allowed","canActivate","ex","ForbiddenException","statusCode","toJSON","text","JSON","parse","bodySchema","safeParse","success","issues","data","name","value","toLowerCase","stringify","verb","matchPath","pattern","paramNames","regexStr","replace","_m","RegExp","exec","forEach","i","length","maxIndex","Math","max","map","p","index","from","source","key","get","entries","createTypedClient","baseUrl","defaultHeaders","request","method","path","body","opts","url","URL","query","k","v","Object","entries","searchParams","set","headers","undefined","res","fetch","toString","JSON","stringify","ok","error","json","catch","message","statusText","TypedClientError","status","get","post","put","delete","Error","name","contract","routes","djb2","input","hash","i","length","charCodeAt","toString","digestError","err","context","message","extractMessage","status","extractStatus","rawStack","extractStack","digest","isProduction","process","env","NODE_ENV","stack","Error","String","JSON","stringify","HttpException","statusCode","undefined","createErrorBoundary","React","FallbackComponent","ErrorBoundary","Component","props","state","hasError","getDerivedStateFromError","render","createElement","Fragment","children","composeComponentTree","tree","composeNode","node","layout","Layout","page","Page","loading","Loading","error","ErrorFallback","element","Suspense","fallback","Boundary","revalidateTag","tag","collectSignal","kind","value","timestamp","Date","now","revalidatePath","path","getRevalidationSignals","ctx","tryGetRequestContext","_revalidationSignals","signal","console","warn","extended","push"]}
|
|
1
|
+
{"version":3,"sources":["../src/decorators/controller.ts","../src/decorators/methods.ts","../src/decorators/params.ts","../src/decorators/expose.ts","../src/decorators/response.ts","../src/decorators/middleware.ts","../src/decorators/set-metadata.ts","../src/decorators/throttle.ts","../src/bridge/register-controllers.ts","../src/bridge/create-server.ts","../src/typed-client.ts","../src/contract.ts","../src/error-digest.ts","../src/component-tree.ts","../src/cache-signal.ts"],"sourcesContent":["import { setMeta, CONTROLLER_PREFIX } from '../metadata/index.js'\n\nexport interface ControllerOptions {\n host?: string\n}\n\nexport interface ControllerMeta {\n prefix: string\n host?: string\n}\n\n/**\n * Infer route prefix from class name (Rails-style convention naming).\n *\n * UsersController → api/users\n * TasksController → api/tasks\n * AuthController → api/auth\n * HealthCheckController → api/health-check\n *\n * Strips \"Controller\" suffix, converts PascalCase to kebab-case,\n * prepends \"api/\".\n */\nfunction inferPrefix(className: string): string {\n const stripped = className.replace(/Controller$/, '')\n const kebab = stripped\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase()\n return `api/${kebab}`\n}\n\n/**\n * Class decorator that declares a route-prefix scope.\n *\n * Convention over configuration:\n * @Controller() → prefix inferred from class name\n * @Controller('api/users') → explicit prefix\n *\n * @example\n * ```ts\n * // Convention: UsersController → /api/users (zero config)\n * @Controller()\n * class UsersController { ... }\n *\n * // Explicit: override when convention doesn't fit\n * @Controller('api/v2/users')\n * class UsersController { ... }\n * ```\n */\nexport function Controller(prefix?: string, opts?: ControllerOptions): ClassDecorator\nexport function Controller(opts?: ControllerOptions): ClassDecorator\nexport function Controller(\n prefixOrOpts?: string | ControllerOptions,\n maybeOpts?: ControllerOptions,\n): ClassDecorator {\n return (target) => {\n let prefix: string\n let opts: ControllerOptions\n\n if (typeof prefixOrOpts === 'string') {\n prefix = prefixOrOpts\n opts = maybeOpts ?? {}\n } else {\n // Convention naming: infer from class name\n prefix = inferPrefix(target.name)\n opts = prefixOrOpts ?? {}\n }\n\n setMeta<ControllerMeta>(CONTROLLER_PREFIX, target, {\n prefix,\n host: opts.host,\n })\n }\n}\n","import { setMeta, getMeta, ROUTE_METHODS } from '../metadata/index.js'\n\nexport type HttpVerb = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'ALL'\n\nexport interface RouteMethodEntry {\n verb: HttpVerb\n path: string\n propertyKey: string | symbol\n}\n\nfunction makeVerbDecorator(verb: HttpVerb) {\n return function (path = ''): MethodDecorator {\n return (target, propertyKey) => {\n const existing = getMeta<RouteMethodEntry[]>(ROUTE_METHODS, target.constructor) ?? []\n existing.push({ verb, path, propertyKey })\n setMeta(ROUTE_METHODS, target.constructor, existing)\n }\n }\n}\n\nexport const Get = makeVerbDecorator('GET')\nexport const Post = makeVerbDecorator('POST')\nexport const Put = makeVerbDecorator('PUT')\nexport const Patch = makeVerbDecorator('PATCH')\nexport const Delete = makeVerbDecorator('DELETE')\nexport const Options = makeVerbDecorator('OPTIONS')\nexport const Head = makeVerbDecorator('HEAD')\nexport const All = makeVerbDecorator('ALL')\n","import type { z } from 'zod'\n\nimport { setMeta, getMeta, ROUTE_PARAMS } from '../metadata/index.js'\n\nexport type ParamSource =\n | 'req'\n | 'res'\n | 'body'\n | 'param'\n | 'query'\n | 'headers'\n | 'session'\n | 'ip'\n | 'host'\n\nexport interface ParamEntry {\n source: ParamSource\n key?: string\n index: number\n passthrough?: boolean\n /** Explicit Zod schema — when set, bypasses design:paramtypes + DTO resolution.\n * Preferred path: `@Body(zMySchema)` works without emitDecoratorMetadata. */\n schema?: z.ZodType\n}\n\n/** Detect whether a value is a Zod schema (has `.safeParse()` method). */\nfunction isZodSchema(v: unknown): v is z.ZodType {\n return (\n v !== null &&\n v !== undefined &&\n typeof v === 'object' &&\n typeof (v as Record<string, unknown>).safeParse === 'function'\n )\n}\n\nfunction makeParamDecorator(source: ParamSource) {\n /**\n * Overloaded parameter decorator:\n * - `@Body()` / `@Param()` / `@Query()` — whole-object extraction\n * - `@Body('key')` / `@Param('key')` — named-field extraction\n * - `@Body(zodSchema)` — whole-object with explicit Zod validation\n * (works WITHOUT emitDecoratorMetadata — TheoKit \"Zod is SSoT\" alignment)\n */\n return function (keyOrSchema?: string | z.ZodType): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) return\n const map =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, target.constructor) ?? new Map()\n const entries = map.get(propertyKey) ?? []\n\n const entry: ParamEntry = { source, index: parameterIndex }\n if (typeof keyOrSchema === 'string') {\n entry.key = keyOrSchema\n } else if (isZodSchema(keyOrSchema)) {\n entry.schema = keyOrSchema\n }\n\n entries.push(entry)\n map.set(propertyKey, entries)\n setMeta(ROUTE_PARAMS, target.constructor, map)\n }\n }\n}\n\nexport const Req = makeParamDecorator('req')\nexport const Body = makeParamDecorator('body')\nexport const Param = makeParamDecorator('param')\nexport const Query = makeParamDecorator('query')\nexport const Headers = makeParamDecorator('headers')\nexport const Session = makeParamDecorator('session')\nexport const Ip = makeParamDecorator('ip')\nexport const HostParam = makeParamDecorator('host')\n\nexport function Res(opts?: { passthrough?: boolean }): ParameterDecorator {\n return (target, propertyKey, parameterIndex) => {\n if (propertyKey === undefined) return\n const map =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, target.constructor) ?? new Map()\n const entries = map.get(propertyKey) ?? []\n entries.push({\n source: 'res',\n index: parameterIndex,\n passthrough: opts?.passthrough,\n })\n map.set(propertyKey, entries)\n setMeta(ROUTE_PARAMS, target.constructor, map)\n }\n}\n","import { setMeta, getMeta, EXPOSE_AGENT } from '../metadata/index.js'\n\n/**\n * M47 (ADR-M47-1) — `@Expose(agent, opts?)` binds a SEPARATELY-BUILT agent (a pure `agent()…build()` from\n * `agents/<name>.ts`) to a controller PROPERTY, making the exposure (route, auth via `@UseGuards`, streaming)\n * visible in one code review. It mirrors the #122 verb decorators (`@Post`) — storing metadata via the same\n * `Symbol.for()` seam — but records the agent instead of an HTTP verb, so the controller walker turns the\n * property into an agent-serving route (`POST <prefix>/<property>`) delegated to the ONE runtime\n * (`mountAgent`), never a JSON handler. It is a new AUTHORING surface over the existing runtime, not a\n * parallel runtime (G2). CSRF is enforced once at the controller-dispatch boundary. Interceptors do NOT run\n * for agent routes (the dispatcher delegates straight to `mountAgent`); guards DO (G5). The served path is\n * the controller prefix + property name — keep it equal to the agent's convention route (`/api/agents/<name>`\n * for a property named `<name>` under `@Controller('api/agents')`) so the generated `useAgent(handle)` path\n * lines up.\n */\n\n/**\n * Per-binding options for an exposed agent. Reserved for forward-compat — no options are wired yet.\n *\n * Two candidate fields were removed before shipping (M47 review): `path` (a route override) would let the\n * served URL diverge from the generated handle's path — the codegen derives the handle from the agent's\n * convention route (`/api/agents/<name>`), so an overridden route would make `useAgent(handle)` hit the\n * wrong URL. And `csrf` was a no-op — CSRF is enforced exactly once at the controller-dispatch boundary\n * regardless. Both return only when they can be wired end-to-end (codegen reads `@Expose` metadata / a\n * per-route CSRF opt-out threads through the dispatcher). Until then, `@Expose(agent)` is the shape, and the\n * exposure's path MUST be the convention route — put `@Expose` on a property named after the agent under\n * `@Controller('api/agents')` (e.g. `chat` for `agents/chat.ts` → `/api/agents/chat`).\n */\nexport type ExposeOptions = Record<string, never>\n\n/** Metadata recorded per `@Expose`-decorated controller member. */\nexport interface ExposeEntry {\n /** The separately-built agent module bound to this member. */\n agent: unknown\n /** The per-binding options (defaulted to `{}`). */\n opts: ExposeOptions\n /** The controller member the binding is attached to. */\n propertyKey: string | symbol\n}\n\n/**\n * Bind an agent to a controller PROPERTY. Accumulates one {@link ExposeEntry} per member under the\n * `EXPOSE_AGENT` metadata key on the controller constructor — the same accumulation shape `@Post` uses for\n * `ROUTE_METHODS`, so `walkControllerMetadata` reads both alongside each other. Typed as `PropertyDecorator`\n * (the exposure is declared as a property, e.g. `@Expose(chatAgent) chat!: typeof chatAgent`); the agent's\n * behavior lives in its own `agents/<name>.ts`, never in a controller method body.\n */\nexport function Expose(agent: unknown, opts: ExposeOptions = {}): PropertyDecorator {\n return (target: object, propertyKey: string | symbol) => {\n const existing = getMeta<ExposeEntry[]>(EXPOSE_AGENT, target.constructor) ?? []\n existing.push({ agent, opts, propertyKey })\n setMeta(EXPOSE_AGENT, target.constructor, existing)\n }\n}\n","import { setMeta, getMeta, ROUTE_STATUS, ROUTE_HEADERS, ROUTE_REDIRECT } from '../metadata/index.js'\n\nexport function HttpCode(status: number): MethodDecorator {\n return (target, propertyKey) => {\n setMeta(ROUTE_STATUS, target.constructor, status, propertyKey as string)\n }\n}\n\nexport function Header(name: string, value: string): MethodDecorator {\n return (target, propertyKey) => {\n const existing =\n getMeta<[string, string][]>(ROUTE_HEADERS, target.constructor, propertyKey) ?? []\n existing.push([name, value])\n setMeta(ROUTE_HEADERS, target.constructor, existing, propertyKey as string)\n }\n}\n\nexport interface RedirectMeta {\n url: string\n status: number\n}\n\nexport function Redirect(url: string, status = 302): MethodDecorator {\n return (target, propertyKey) => {\n setMeta<RedirectMeta>(ROUTE_REDIRECT, target.constructor, { url, status }, propertyKey)\n }\n}\n","import {\n setMeta,\n getMeta,\n USE_GUARDS,\n USE_INTERCEPTORS,\n USE_FILTERS,\n CATCH_EXCEPTIONS,\n} from '../metadata/index.js'\n\nexport function UseGuards(\n ...guards: Function[]\n): ClassDecorator & MethodDecorator & PropertyDecorator {\n // M47 — also a PropertyDecorator so per-agent guards attach to an `@Expose` property (visible auth). The\n // runtime already keys guards by propertyKey; only the type is widened (back-compat — strictly permissive).\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_GUARDS, actualTarget, propertyKey) ?? []\n setMeta(USE_GUARDS, actualTarget, [...existing, ...guards], propertyKey)\n }\n}\n\nexport function UseInterceptors(...interceptors: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_INTERCEPTORS, actualTarget, propertyKey) ?? []\n setMeta(USE_INTERCEPTORS, actualTarget, [...existing, ...interceptors], propertyKey)\n }\n}\n\nexport function UseFilters(...filters: Function[]): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey ? target.constructor : target\n const existing = getMeta<Function[]>(USE_FILTERS, actualTarget, propertyKey) ?? []\n setMeta(USE_FILTERS, actualTarget, [...existing, ...filters], propertyKey)\n }\n}\n\n/** @Catch(ExceptionType, ...) — marks which exception types an ExceptionFilter handles.\n * Empty args = catch-all filter. */\nexport function Catch(...exceptions: Function[]): ClassDecorator {\n return (target: object) => {\n setMeta(CATCH_EXCEPTIONS, target, exceptions)\n }\n}\n","/**\n * @SetMetadata + Reflector — NestJS-style custom metadata for guards.\n *\n * Enables role-based auth pattern:\n * const Roles = createDecorator<string[]>()\n * @Roles(['admin']) → guard reads via reflector.get(Roles, handler)\n */\nimport 'reflect-metadata'\n\n/** Unique key type for type-safe metadata decorators. */\nexport type MetadataKey<T> = symbol & { __type?: T }\n\n/**\n * Create a typed decorator that attaches metadata to a handler or class.\n * NestJS equivalent: `Reflector.createDecorator<T>()`.\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n *\n * @Controller('cats')\n * class CatsController {\n * @Post()\n * @Roles(['admin'])\n * create() { ... }\n * }\n * ```\n */\n/** Monotonic counter for unique decorator keys — deterministic, no randomness. */\nlet decoratorKeyCounter = 0\n\nexport function createDecorator<T>(): (value: T) => MethodDecorator & ClassDecorator {\n const key = Symbol.for(`theokit:custom:${++decoratorKeyCounter}`) as MetadataKey<T>\n\n const decorator = (value: T): MethodDecorator & ClassDecorator => {\n return (target: object, propertyKey?: string | symbol) => {\n const metaTarget = propertyKey !== undefined ? target.constructor : target\n Reflect.defineMetadata(key, value, metaTarget, propertyKey as string)\n }\n }\n\n // Attach the key so Reflector can read it\n ;(decorator as unknown as { key: MetadataKey<T> }).key = key\n return decorator\n}\n\n/**\n * Low-level @SetMetadata decorator — attaches arbitrary metadata.\n * NestJS equivalent: `@SetMetadata(key, value)`.\n *\n * Prefer `createDecorator<T>()` for type-safe metadata.\n */\nexport function SetMetadata<T>(\n metaKey: string | symbol,\n value: T,\n): MethodDecorator & ClassDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const metaTarget = propertyKey !== undefined ? target.constructor : target\n Reflect.defineMetadata(metaKey, value, metaTarget, propertyKey as string)\n }\n}\n\n/**\n * Reflector — reads metadata set by createDecorator or @SetMetadata.\n * NestJS-compatible Reflector with getAllAndOverride/getAllAndMerge.\n * HTTP-only per ADR D1.\n */\nexport class Reflector {\n /**\n * Read metadata set by a typed decorator created via createDecorator<T>().\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n * const reflector = new Reflector()\n * const roles = reflector.get(Roles, handlerFn) // string[] | undefined\n * ```\n */\n get<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n const key = (decorator as { key?: MetadataKey<T> }).key\n if (!key) return undefined\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n }\n\n /**\n * Read metadata set by @SetMetadata(key, value).\n */\n getByKey<T>(\n key: string | symbol,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n }\n\n /**\n * Read metadata checking method-level first, then class-level.\n * Returns the first non-undefined value found.\n *\n * NestJS equivalent: `reflector.getAllAndOverride(ROLES_KEY, [context.getHandler(), context.getClass()])`\n *\n * @example\n * ```ts\n * const Roles = createDecorator<string[]>()\n * // In a guard:\n * const roles = reflector.getAllAndOverride(Roles, context.getClass(), context.getMethodName())\n * // Checks method-level @Roles first, falls back to class-level @Roles\n * ```\n */\n getAllAndOverride<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n const methodLevel = this.get(decorator, target, propertyKey)\n if (methodLevel !== undefined) return methodLevel\n }\n return this.get(decorator, target)\n }\n\n /**\n * Read metadata checking method-level first, then class-level, by raw key.\n * Returns the first non-undefined value found.\n */\n getAllAndOverrideByKey<T>(\n key: string | symbol,\n target: Function,\n propertyKey?: string | symbol,\n ): T | undefined {\n if (propertyKey !== undefined) {\n const methodLevel = this.getByKey<T>(key, target, propertyKey)\n if (methodLevel !== undefined) return methodLevel\n }\n return this.getByKey<T>(key, target)\n }\n\n /**\n * Read metadata from both method-level and class-level, merging arrays.\n * Returns all found values as a flat array.\n *\n * NestJS equivalent: `reflector.getAllAndMerge(ROLES_KEY, [context.getHandler(), context.getClass()])`\n *\n * @example\n * ```ts\n * const Tags = createDecorator<string[]>()\n *\n * @Tags(['api'])\n * @Controller('cats')\n * class CatsCtrl {\n * @Tags(['read'])\n * @Get()\n * findAll() {}\n * }\n *\n * reflector.getAllAndMerge(Tags, CatsCtrl, 'findAll')\n * // → ['read', 'api'] (method + class merged)\n * ```\n */\n getAllAndMerge<T>(\n decorator: (value: T) => MethodDecorator & ClassDecorator,\n target: Function,\n propertyKey?: string | symbol,\n ): T extends (infer U)[] ? U[] : T[] {\n const result: unknown[] = []\n if (propertyKey !== undefined) {\n const methodLevel = this.get(decorator, target, propertyKey)\n if (methodLevel !== undefined) {\n if (Array.isArray(methodLevel)) result.push(...methodLevel)\n else result.push(methodLevel)\n }\n }\n const classLevel = this.get(decorator, target)\n if (classLevel !== undefined) {\n if (Array.isArray(classLevel)) result.push(...classLevel)\n else result.push(classLevel)\n }\n return result as T extends (infer U)[] ? U[] : T[]\n }\n}\n","/**\n * @Throttle() + @SkipThrottle() — NestJS-style rate limiting decorators.\n *\n * Thin bridge over @theokit/plugin-rate-limit. These decorators store\n * metadata that the rate-limit plugin reads at request time to override\n * or skip the global throttle config per route/controller.\n *\n * Usage:\n * ```ts\n * @Controller('api/tasks')\n * @Throttle({ limit: 100, ttl: 60_000 }) // 100 req/min for all routes\n * class TasksController {\n * @Get()\n * @SkipThrottle() // no rate limit on this route\n * health() { return { ok: true } }\n *\n * @Post()\n * @Throttle({ limit: 5, ttl: 60_000 }) // stricter: 5 req/min\n * create(@Body(schema) body) { ... }\n * }\n * ```\n */\nimport { setMeta, getMeta } from '../metadata/index.js'\n\nconst THROTTLE_KEY = Symbol.for('theokit:http-decorators:throttle')\nconst SKIP_THROTTLE_KEY = Symbol.for('theokit:http-decorators:skip-throttle')\n\nexport interface ThrottleOptions {\n /** Maximum requests within the TTL window. */\n limit: number\n /** Time-to-live in milliseconds. */\n ttl: number\n /** Optional throttle set name (for multiple throttler definitions). */\n name?: string\n}\n\n/**\n * Override the global rate limit for a controller or specific route.\n * NestJS equivalent: `@Throttle({ default: { limit, ttl } })`.\n */\nexport function Throttle(options: ThrottleOptions): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey !== undefined ? target.constructor : target\n setMeta(THROTTLE_KEY, actualTarget, options, propertyKey)\n }\n}\n\n/**\n * Skip rate limiting for a controller or specific route.\n * NestJS equivalent: `@SkipThrottle()`.\n *\n * @param skip — defaults to `true`. Pass `false` to re-enable on a\n * specific route inside a skipped controller.\n */\nexport function SkipThrottle(skip = true): ClassDecorator & MethodDecorator {\n return (target: object, propertyKey?: string | symbol) => {\n const actualTarget = propertyKey !== undefined ? target.constructor : target\n setMeta(SKIP_THROTTLE_KEY, actualTarget, skip, propertyKey)\n }\n}\n\n/**\n * Read throttle metadata for a given class or method.\n * Used by the rate-limit plugin to resolve per-route overrides.\n */\nexport function getThrottleOptions(\n target: Function,\n propertyKey?: string | symbol,\n): ThrottleOptions | undefined {\n return getMeta<ThrottleOptions>(THROTTLE_KEY, target, propertyKey)\n}\n\n/**\n * Check if throttling is skipped for a given class or method.\n */\nexport function isThrottleSkipped(target: Function, propertyKey?: string | symbol): boolean {\n return getMeta<boolean>(SKIP_THROTTLE_KEY, target, propertyKey) ?? false\n}\n","import type { HttpVerb } from '../decorators/methods.js'\n\nimport { walkControllerMetadata, type WalkResult } from './walk-metadata.js'\n\nexport interface RouteRegistration {\n verb: HttpVerb\n fullPath: string\n walkResult: WalkResult\n}\n\n/**\n * Low-level API: walks decorator metadata per controller class and returns\n * structured route descriptors. Used internally by the Vite plugin (ADR D7)\n * and available to advanced consumers who don't use Vite.\n *\n * EC-5: deduplicates by class reference; warns on duplicates.\n */\nexport function registerControllers(controllers: Function[]): RouteRegistration[] {\n const seen = new Set<Function>()\n const unique: Function[] = []\n for (const Ctor of controllers) {\n if (seen.has(Ctor)) {\n console.warn(\n `[@theokit/http] Controller ${Ctor.name} registered multiple times — dropping duplicate`,\n )\n continue\n }\n seen.add(Ctor)\n unique.push(Ctor)\n }\n return unique.flatMap((Ctor) => {\n const walks = walkControllerMetadata(Ctor)\n return walks.map((w) => ({\n verb: w.verb,\n fullPath: w.fullPath,\n walkResult: w,\n }))\n })\n}\n","/* eslint-disable security/detect-non-literal-regexp --\n * Route patterns like /cats/:id are converted to regex at startup —\n * NOT from user HTTP input. The patterns come from decorator metadata\n * authored by the developer. No injection vector.\n */\nimport 'reflect-metadata'\n\nimport type { ExposeOptions } from '../decorators/expose.js'\nimport type { ParamEntry } from '../decorators/params.js'\nimport { ForbiddenException } from '../exceptions/http-exception.js'\n\nimport { resolveOrNew, type DiContainer } from './di-resolve.js'\nexport type { DiContainer } from './di-resolve.js'\nimport { runExceptionFilters } from './exception-filter-chain.js'\nimport {\n createExecutionContext,\n type CanActivate,\n type ExecutionContext,\n} from './execution-context.js'\nimport { runInterceptors } from './interceptor-chain.js'\nimport {\n MiddlewareConsumerImpl,\n runMiddleware,\n type ResolvedMiddleware,\n} from './middleware-consumer.js'\nimport { createNodeAdapter } from './runtime/node.js'\nimport { walkControllerMetadata, type WalkResult } from './walk-metadata.js'\n\n/**\n * M47 — serves an `@Expose`-bound agent route. http is agent-runtime agnostic (G1/G2): it invokes this\n * injected callback (theo supplies a `mountAgent`-backed impl) instead of calling a controller method.\n */\nexport type ServeAgent = (\n agent: unknown,\n request: Request,\n opts: ExposeOptions,\n) => Promise<Response>\n\nexport interface CreateDecoratorServerOptions {\n controllers: Function[]\n container?: DiContainer\n configure?: (consumer: MiddlewareConsumerImpl) => void\n /** M47 — required when any controller `@Expose`-binds an agent; serves the agent route. */\n serveAgent?: ServeAgent\n}\n\n/**\n * A pure Web-Standard controller handler: callable as `(request) => Response | null`\n * plus a non-executing `matches(method, pathname)` route probe (so a host can gate\n * — e.g. CSRF — before dispatch runs a handler).\n */\nexport interface DecoratorHandler {\n (request: Request): Promise<Response | null>\n /** True when a controller route owns `method` + `pathname` (no handler executed). */\n matches(method: string, pathname: string): boolean\n}\n\n/**\n * Build a pure Web-Standard request handler from decorated controller classes,\n * WITHOUT binding a network listener. Returns a {@link DecoratorHandler} whose\n * call returns `null` when no controller route matched — the caller decides the\n * miss (a standalone server answers 404; a host middleware falls through to its\n * own routing). This is the reusable dispatch seam consumed by the framework's\n * controller dispatch (#122) so it never re-implements match/bind/validate.\n */\nexport function createDecoratorHandler(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n): DecoratorHandler {\n const { controllers, container, configure, serveAgent } = Array.isArray(controllersOrOpts)\n ? {\n controllers: controllersOrOpts,\n container: undefined,\n configure: undefined,\n serveAgent: undefined,\n }\n : controllersOrOpts\n\n // Collect middleware\n const middlewareConsumer = new MiddlewareConsumerImpl(container)\n if (configure) configure(middlewareConsumer)\n const middlewareEntries = middlewareConsumer.getEntries()\n\n // Dedupe controllers (EC-5)\n const seen = new Set<Function>()\n const unique: Function[] = []\n for (const Ctor of controllers) {\n if (seen.has(Ctor)) continue\n seen.add(Ctor)\n unique.push(Ctor)\n }\n\n // Walk metadata\n const routes: { walk: WalkResult; instance: object }[] = []\n for (const Ctor of unique) {\n const instance = resolveOrNew(Ctor, container)\n const walks = walkControllerMetadata(Ctor)\n for (const w of walks) {\n routes.push({ walk: w, instance })\n }\n }\n\n // Sort: static routes first\n routes.sort((a, b) => {\n const aP = a.walk.fullPath.includes(':')\n const bP = b.walk.fullPath.includes(':')\n if (aP !== bP) return aP ? 1 : -1\n return 0\n })\n\n const handler = ((request: Request) =>\n handleRequest(routes, request, container, middlewareEntries, serveAgent)) as DecoratorHandler\n handler.matches = (method: string, pathname: string): boolean =>\n findRoute(routes, method.toUpperCase(), pathname) !== null\n return handler\n}\n\n/**\n * Creates a real HTTP server from decorated controller classes.\n * Uses Web Standard Request/Response internally; Node adapter at the boundary.\n */\nexport function createDecoratorServer(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n) {\n const handle = createDecoratorHandler(controllersOrOpts)\n // Standalone server: a no-match (handler returns null) becomes a 404.\n const adapter = createNodeAdapter()\n return adapter.createServer(async (request: Request) => {\n const res = await handle(request)\n if (res) return res\n const { pathname } = new URL(request.url)\n return jsonResponse(404, {\n error: {\n code: 'NOT_FOUND',\n message: `No route for ${request.method.toUpperCase()} ${pathname}`,\n },\n })\n })\n}\n\n// ─── Web Standard request handler ────────────────────────────\n\nasync function handleRequest(\n routes: { walk: WalkResult; instance: object }[],\n request: Request,\n container?: DiContainer,\n middlewareEntries: ResolvedMiddleware[] = [],\n serveAgent?: ServeAgent,\n): Promise<Response | null> {\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n const pathname = url.pathname\n\n const match = findRoute(routes, method, pathname)\n // null = no controller route matched; the caller owns the miss (404 or fall-through).\n if (!match) return null\n\n const { walk, instance, params } = match\n\n try {\n // Middleware\n const mwResponse = await runMiddleware(middlewareEntries, request, pathname)\n if (mwResponse) return mwResponse\n\n // Guards\n const ctx = createExecutionContext(request, instance.constructor, walk.propertyKey)\n const guardResponse = await runGuards(walk.guards, ctx, container)\n if (guardResponse) return guardResponse\n\n // M47 — an @Expose-bound route is served by the injected agent runtime (mountAgent, via theo), AFTER\n // guards (auth applies to agents — G5) and NEVER as a JSON controller method. http stays agnostic.\n if (walk.agent) {\n if (!serveAgent) {\n return jsonResponse(500, {\n error: {\n code: 'AGENT_SERVER_NOT_WIRED',\n message:\n `Controller route ${String(walk.propertyKey)} is @Expose-bound but no serveAgent was ` +\n `provided to createDecoratorHandler. The framework must wire serveAgent (mountAgent).`,\n },\n })\n }\n return await serveAgent(walk.agent.module, request, walk.agent.opts)\n }\n\n // Body\n const body = await resolveBody(method, request, walk)\n if (body instanceof Response) return body // validation error response\n\n // Build args\n const args = buildArgs(walk.paramEntries, {\n request,\n body,\n params,\n query: Object.fromEntries(url.searchParams),\n })\n\n // Redirect\n if (walk.redirect) {\n return new Response(null, {\n status: walk.redirect.status,\n headers: { location: walk.redirect.url },\n })\n }\n\n const handlerFn = (instance as Record<string | symbol, Function>)[walk.propertyKey]\n\n // Interceptors wrap handler\n const result = await runInterceptors(\n walk.interceptors,\n () => handlerFn.apply(instance, args) as Promise<unknown>,\n request,\n container,\n )\n\n // A handler may return a Web `Response` directly (Set-Cookie, custom status /\n // headers) — parity with file-based `route()`. Pass it through untouched\n // instead of JSON-stringifying it into `{}`.\n if (result instanceof Response) return result\n\n return buildResponse(result, walk, method)\n } catch (err) {\n return runExceptionFilters(err, walk.filters, request, container)\n }\n}\n\n// ─── Guards (return Response on rejection, null on pass) ─────\n\nasync function runGuards(\n guards: Function[],\n context: ExecutionContext,\n container?: DiContainer,\n): Promise<Response | null> {\n for (const GuardCtor of guards) {\n const guard = resolveOrNew(GuardCtor, container) as CanActivate\n const allowed = await guard.canActivate(context)\n if (!allowed) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n return null\n}\n\n// ─── Body resolution ─────────────────────────────────────────\n\nasync function resolveBody(method: string, request: Request, walk: WalkResult): Promise<unknown> {\n if (!['POST', 'PUT', 'PATCH'].includes(method)) return undefined\n\n let body: unknown\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch {\n body = undefined\n }\n\n if (walk.bodySchema && body !== undefined) {\n const result = walk.bodySchema.safeParse(body)\n if (!result.success) {\n return jsonResponse(422, { error: { code: 'VALIDATION_ERROR', issues: result.error.issues } })\n }\n body = result.data\n }\n return body\n}\n\n// ─── Response builder ────────────────────────────────────────\n\nfunction buildResponse(result: unknown, walk: WalkResult, method: string): Response {\n const status = walk.status ?? (method === 'POST' ? 201 : 200)\n const headers: Record<string, string> = { 'content-type': 'application/json' }\n for (const [name, value] of walk.headers) {\n headers[name.toLowerCase()] = value\n }\n\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\n }\n\n return new Response(JSON.stringify(result), { status, headers })\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n// ─── Route matching ──────────────────────────────────────────\n\ninterface RouteMatch {\n walk: WalkResult\n instance: object\n params: Record<string, string>\n}\n\nfunction findRoute(\n routes: { walk: WalkResult; instance: object }[],\n method: string,\n pathname: string,\n): RouteMatch | null {\n for (const { walk, instance } of routes) {\n if (walk.verb !== 'ALL' && walk.verb !== method) continue\n const params = matchPath(walk.fullPath, pathname)\n if (params !== null) return { walk, instance, params }\n }\n return null\n}\n\nfunction matchPath(pattern: string, pathname: string): Record<string, string> | null {\n const paramNames: string[] = []\n const regexStr = pattern.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n const match = new RegExp(`^${regexStr}$`).exec(pathname)\n if (!match) return null\n const params: Record<string, string> = {}\n paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return params\n}\n\n// ─── Argument builder ────────────────────────────────────────\n\ninterface ArgContext {\n request: Request\n body: unknown\n params: Record<string, string>\n query: Record<string, string>\n}\n\nfunction buildArgs(paramEntries: ParamEntry[], ctx: ArgContext): unknown[] {\n if (paramEntries.length === 0) return []\n const maxIndex = Math.max(...paramEntries.map((p) => p.index))\n const args: unknown[] = Array.from({ length: maxIndex + 1 }, () => undefined)\n for (const p of paramEntries) {\n switch (p.source) {\n case 'req':\n args[p.index] = ctx.request\n break\n case 'body':\n args[p.index] = p.key ? (ctx.body as Record<string, unknown>)[p.key] : ctx.body\n break\n case 'param':\n args[p.index] = p.key ? ctx.params[p.key] : ctx.params\n break\n case 'query':\n args[p.index] = p.key ? ctx.query[p.key] : ctx.query\n break\n case 'headers':\n args[p.index] = p.key\n ? ctx.request.headers.get(p.key.toLowerCase())\n : Object.fromEntries(ctx.request.headers.entries())\n break\n case 'ip':\n args[p.index] = ctx.request.headers.get('x-forwarded-for') ?? '127.0.0.1'\n break\n case 'session':\n args[p.index] = undefined\n break\n default:\n args[p.index] = undefined\n }\n }\n return args\n}\n","/**\n * Typed Client — end-to-end type inference from route contracts.\n *\n * Zero codegen, zero runtime overhead on types. The developer defines\n * a route map using `contract()`, and `createTypedClient<T>()` infers\n * request body + response types automatically.\n */\nimport type { z } from 'zod'\n\n// ── Route Map types ──\n\nexport interface RouteDefinition {\n body?: z.ZodType\n params?: Record<string, 'string' | 'number'>\n query?: Record<string, 'string' | 'number' | 'boolean'>\n response: unknown\n}\n\nexport type RouteMap = Record<string, RouteDefinition>\n\n// ── Type extraction ──\n\ntype InferBody<D> = D extends { body: z.ZodType } ? z.infer<D['body']> : never\ntype InferResponse<D> = D extends { response: infer R } ? R : unknown\n\n// ── Client interface (simplified — works with strict DTS) ──\n\nexport interface TypedClient<M extends RouteMap> {\n get<P extends string>(\n path: P,\n opts?: { query?: Record<string, string>; headers?: Record<string, string> },\n ): Promise<InferResponse<M[`GET ${P}`]>>\n\n post<P extends string>(\n path: P,\n body?: InferBody<M[`POST ${P}`]>,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`POST ${P}`]>>\n\n put<P extends string>(\n path: P,\n body?: InferBody<M[`PUT ${P}`]>,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`PUT ${P}`]>>\n\n delete<P extends string>(\n path: P,\n opts?: { headers?: Record<string, string> },\n ): Promise<InferResponse<M[`DELETE ${P}`]>>\n}\n\n// ── Client factory ──\n\nexport function createTypedClient<M extends RouteMap>(\n baseUrl: string,\n defaultHeaders?: Record<string, string>,\n): TypedClient<M> {\n async function request(\n method: string,\n path: string,\n body?: unknown,\n opts?: { headers?: Record<string, string>; query?: Record<string, string> },\n ) {\n const url = new URL(path, baseUrl)\n if (opts?.query) {\n for (const [k, v] of Object.entries(opts.query)) url.searchParams.set(k, v)\n }\n const headers: Record<string, string> = { ...defaultHeaders, ...opts?.headers }\n if (body !== undefined) headers['content-type'] = 'application/json'\n\n const res = await fetch(url.toString(), {\n method,\n headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ message: res.statusText }))\n throw new TypedClientError(res.status, error as Record<string, unknown>)\n }\n\n if (res.status === 204) return undefined\n return res.json()\n }\n\n return {\n get: (path: string, opts?: Record<string, unknown>) => request('GET', path, undefined, opts),\n post: (path: string, body?: unknown, opts?: Record<string, unknown>) =>\n request('POST', path, body, opts),\n put: (path: string, body?: unknown, opts?: Record<string, unknown>) =>\n request('PUT', path, body, opts),\n delete: (path: string, opts?: Record<string, unknown>) =>\n request('DELETE', path, undefined, opts),\n } as TypedClient<M>\n}\n\nexport class TypedClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: Record<string, unknown>,\n ) {\n super(`HTTP ${status}: ${JSON.stringify(body)}`)\n this.name = 'TypedClientError'\n }\n}\n","/**\n * Route Contract — type-level bridge between @Controller and TypedClient.\n *\n * The developer defines a contract object mapping routes to their types.\n * This object is the single source of truth for both server validation\n * and client type inference.\n *\n * @example\n * ```ts\n * // server/contracts.ts — shared between server and client\n * import { z } from 'zod'\n * import { contract } from '@theokit/http'\n *\n * export const zCreateTask = z.object({\n * title: z.string().min(3),\n * priority: z.enum(['low', 'medium', 'high']).default('medium'),\n * })\n *\n * export interface Task { id: number; title: string; priority: string; done: boolean }\n *\n * export const routes = contract({\n * 'GET /api/tasks': { response: [] as Task[] },\n * 'GET /api/tasks/:id': { response: {} as Task },\n * 'POST /api/tasks': { body: zCreateTask, response: {} as Task },\n * 'PUT /api/tasks/:id': { body: z.object({ done: z.boolean() }), response: {} as Task },\n * 'DELETE /api/tasks/:id': { response: undefined as void },\n * })\n * export type AppRoutes = typeof routes\n * ```\n *\n * The `contract()` function is identity at runtime (zero overhead) but\n * enforces the RouteMap type at the type level, enabling full inference.\n */\nimport type { RouteMap } from './typed-client.js'\n\n/**\n * Identity function that enforces RouteMap type constraint.\n * Zero runtime overhead — exists only for type inference.\n */\nexport function contract<T extends RouteMap>(routes: T): T {\n return routes\n}\n","/**\n * Error digestion — converts any thrown value into a stable hash + context.\n *\n * Inspired by Next.js `create-error-handler.tsx`. Produces a deterministic\n * digest ID suitable for logging and client-safe error references without\n * leaking stack traces in production.\n *\n * Uses djb2 hash (sync, no crypto dependency) per ADR D3.\n */\n\nimport { HttpException } from './exceptions/http-exception.js'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ErrorContext {\n route?: string\n phase?: 'guard' | 'interceptor' | 'handler' | 'filter' | 'agent'\n source?: string\n}\n\nexport interface DigestedError {\n digest: string\n message: string\n status: number\n context: ErrorContext\n stack?: string\n}\n\n// ---------------------------------------------------------------------------\n// djb2 hash — deterministic, sync, no crypto dependency\n// ---------------------------------------------------------------------------\n\nfunction djb2(input: string): string {\n let hash = 5381\n for (let i = 0; i < input.length; i++) {\n // hash * 33 + charCode — classic djb2\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0\n }\n // Convert to unsigned 32-bit hex\n return (hash >>> 0).toString(16)\n}\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\n/**\n * Converts any thrown value into a structured {@link DigestedError}.\n *\n * - Sync (never async) — safe to call inside catch blocks.\n * - Stack trace stripped when `process.env.NODE_ENV === 'production'`.\n * - Preserves {@link HttpException} status codes.\n * - Handles non-Error throws (string, number, object).\n */\nexport function digestError(err: unknown, context: ErrorContext = {}): DigestedError {\n const message = extractMessage(err)\n const status = extractStatus(err)\n const rawStack = extractStack(err)\n\n const digest = djb2(message)\n\n const isProduction = process.env.NODE_ENV === 'production'\n\n return {\n digest,\n message,\n status,\n context,\n ...(rawStack && !isProduction ? { stack: rawStack } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction extractMessage(err: unknown): string {\n if (err instanceof Error) {\n return err.message\n }\n if (typeof err === 'string') {\n return err\n }\n if (typeof err === 'number') {\n return String(err)\n }\n // object / null / undefined / symbol / bigint\n try {\n return JSON.stringify(err)\n } catch {\n return 'Unknown error'\n }\n}\n\nfunction extractStatus(err: unknown): number {\n if (err instanceof HttpException) {\n return err.statusCode\n }\n return 500\n}\n\nfunction extractStack(err: unknown): string | undefined {\n if (err instanceof Error) {\n return err.stack\n }\n return undefined\n}\n","/**\n * Component tree composition — recursive wrapping of file-convention\n * components (layout, page, loading, error, not-found) into a React\n * element tree with Suspense and error boundaries.\n *\n * Inspired by Next.js `create-component-tree.tsx`.\n *\n * React is loaded via dynamic `import('react')` because it is an\n * optional peerDep of @theokit/http (EC-2).\n */\n\nimport type * as ReactTypes from 'react'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface RouteTree {\n layout?: ReactTypes.ComponentType<{ children: ReactTypes.ReactNode }>\n page?: ReactTypes.ComponentType\n loading?: ReactTypes.ComponentType\n error?: ReactTypes.ComponentType\n notFound?: ReactTypes.ComponentType\n children?: Record<string, RouteTree>\n}\n\n// ---------------------------------------------------------------------------\n// Error boundary — minimal class component wrapping error.tsx\n// ---------------------------------------------------------------------------\n\n/**\n * Creates an ErrorBoundary class component using the provided React module.\n * Must be a class component — React has no hook-based error boundary API.\n */\nfunction createErrorBoundary(\n React: typeof ReactTypes,\n FallbackComponent: ReactTypes.ComponentType,\n): ReactTypes.ComponentType<{ children: ReactTypes.ReactNode }> {\n return class ErrorBoundary extends React.Component<\n { children: ReactTypes.ReactNode },\n { hasError: boolean }\n > {\n constructor(props: { children: ReactTypes.ReactNode }) {\n super(props)\n this.state = { hasError: false }\n }\n\n static getDerivedStateFromError(): { hasError: boolean } {\n return { hasError: true }\n }\n\n render(): ReactTypes.ReactElement | null {\n if (this.state.hasError) {\n return React.createElement(FallbackComponent)\n }\n return React.createElement(React.Fragment, null, this.props.children)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\n/**\n * Composes a {@link RouteTree} into a nested React element tree.\n *\n * Wrapping order (outermost → innermost):\n * layout → ErrorBoundary(error) → Suspense(loading) → page\n *\n * Returns `null` when no `page` component is found in the tree.\n *\n * @param tree - The route tree describing file conventions found.\n * @returns A React element or `null`.\n */\nexport async function composeComponentTree(\n tree: RouteTree,\n): Promise<ReactTypes.ReactElement | null> {\n // Dynamic import — React is optional peerDep\n const React = await import('react')\n\n return composeNode(React, tree)\n}\n\nfunction composeNode(React: typeof ReactTypes, node: RouteTree): ReactTypes.ReactElement | null {\n const { layout: Layout, page: Page, loading: Loading, error: ErrorFallback } = node\n\n // No page → nothing to render\n if (!Page) {\n return null\n }\n\n // Start with the page element\n let element: ReactTypes.ReactElement = React.createElement(Page)\n\n // Wrap with Suspense if loading component exists\n if (Loading) {\n element = React.createElement(\n React.Suspense,\n { fallback: React.createElement(Loading) },\n element,\n )\n }\n\n // Wrap with error boundary if error component exists\n if (ErrorFallback) {\n const Boundary = createErrorBoundary(React, ErrorFallback)\n element = React.createElement(Boundary, null, element)\n }\n\n // Wrap with layout if it exists\n if (Layout) {\n element = React.createElement(Layout, null, element)\n }\n\n return element\n}\n","/**\n * Cache revalidation signals — runtime-agnostic intent layer.\n *\n * Controllers/agents call `revalidateTag('tasks')` or `revalidatePath('/api/tasks')`\n * to signal that cached data is stale. The signals are collected in the request\n * context and consumed by the cache engine (when present).\n *\n * In standalone @theokit/http (no full theokit framework), signals are stored\n * but not executed — no cache engine is wired. When theokit is present, the\n * cache engine reads signals from the request context after the handler completes.\n *\n * Inspired by Next.js cache-signal.ts + revalidateTag/revalidatePath API.\n */\nimport { tryGetRequestContext } from './request-context.js'\n\nexport interface RevalidationSignal {\n kind: 'tag' | 'path'\n value: string\n timestamp: number\n}\n\n/**\n * Signal that a cache tag should be revalidated.\n *\n * Safe to call from any request handler (controller, agent, action).\n * Signals are accumulated per-request and consumed by the cache engine.\n *\n * @example\n * ```typescript\n * import { revalidateTag } from '@theokit/http'\n *\n * @Post()\n * async createTask(@Body(schema) body) {\n * const task = await db.tasks.create(body)\n * revalidateTag('tasks') // invalidate cached task lists\n * return task\n * }\n * ```\n */\nexport function revalidateTag(tag: string): void {\n collectSignal({ kind: 'tag', value: tag, timestamp: Date.now() })\n}\n\n/**\n * Signal that a cached path should be revalidated.\n *\n * @example\n * ```typescript\n * import { revalidatePath } from '@theokit/http'\n *\n * @Delete(':id')\n * async removeTask(@Param('id') id: string) {\n * await db.tasks.delete(id)\n * revalidatePath('/api/tasks')\n * }\n * ```\n */\nexport function revalidatePath(path: string): void {\n collectSignal({ kind: 'path', value: path, timestamp: Date.now() })\n}\n\n/**\n * Get all revalidation signals collected during the current request.\n * Called by the cache engine after the handler completes.\n */\nexport function getRevalidationSignals(): RevalidationSignal[] {\n const ctx = tryGetRequestContext()\n if (!ctx) return []\n return (\n (ctx as unknown as { _revalidationSignals?: RevalidationSignal[] })._revalidationSignals ?? []\n )\n}\n\n// ── Internal ──\n\nfunction collectSignal(signal: RevalidationSignal): void {\n const ctx = tryGetRequestContext()\n if (!ctx) {\n console.warn(\n '[theokit] revalidateTag/revalidatePath called outside a request context. Signal ignored.',\n )\n return\n }\n const extended = ctx as unknown as { _revalidationSignals?: RevalidationSignal[] }\n extended._revalidationSignals ??= []\n extended._revalidationSignals.push(signal)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,SAASA,YAAYC,WAAiB;AACpC,QAAMC,WAAWD,UAAUE,QAAQ,eAAe,EAAA;AAClD,QAAMC,QAAQF,SACXC,QAAQ,sBAAsB,OAAA,EAC9BA,QAAQ,wBAAwB,OAAA,EAChCE,YAAW;AACd,SAAO,OAAOD,KAAAA;AAChB;AAPSJ;AA6BF,SAASM,WACdC,cACAC,WAA6B;AAE7B,SAAO,CAACC,WAAAA;AACN,QAAIC;AACJ,QAAIC;AAEJ,QAAI,OAAOJ,iBAAiB,UAAU;AACpCG,eAASH;AACTI,aAAOH,aAAa,CAAC;IACvB,OAAO;AAELE,eAASV,YAAYS,OAAOG,IAAI;AAChCD,aAAOJ,gBAAgB,CAAC;IAC1B;AAEAM,YAAwBC,mBAAmBL,QAAQ;MACjDC;MACAK,MAAMJ,KAAKI;IACb,CAAA;EACF;AACF;AAtBgBT;;;ACzChB,SAASU,kBAAkBC,MAAc;AACvC,SAAO,SAAUC,OAAO,IAAE;AACxB,WAAO,CAACC,QAAQC,gBAAAA;AACd,YAAMC,WAAWC,QAA4BC,eAAeJ,OAAO,WAAW,KAAK,CAAA;AACnFE,eAASG,KAAK;QAAEP;QAAMC;QAAME;MAAY,CAAA;AACxCK,cAAQF,eAAeJ,OAAO,aAAaE,QAAAA;IAC7C;EACF;AACF;AARSL;AAUF,IAAMU,MAAMV,kBAAkB,KAAA;AAC9B,IAAMW,OAAOX,kBAAkB,MAAA;AAC/B,IAAMY,MAAMZ,kBAAkB,KAAA;AAC9B,IAAMa,QAAQb,kBAAkB,OAAA;AAChC,IAAMc,SAASd,kBAAkB,QAAA;AACjC,IAAMe,UAAUf,kBAAkB,SAAA;AAClC,IAAMgB,OAAOhB,kBAAkB,MAAA;AAC/B,IAAMiB,MAAMjB,kBAAkB,KAAA;;;ACDrC,SAASkB,YAAYC,GAAU;AAC7B,SACEA,MAAM,QACNA,MAAMC,UACN,OAAOD,MAAM,YACb,OAAQA,EAA8BE,cAAc;AAExD;AAPSH;AAST,SAASI,mBAAmBC,QAAmB;AAQ7C,SAAO,SAAUC,aAAgC;AAC/C,WAAO,CAACC,QAAQC,aAAaC,mBAAAA;AAC3B,UAAID,gBAAgBN,OAAW;AAC/B,YAAMQ,MACJC,QAA4CC,cAAcL,OAAO,WAAW,KAAK,oBAAIM,IAAAA;AACvF,YAAMC,UAAUJ,IAAIK,IAAIP,WAAAA,KAAgB,CAAA;AAExC,YAAMQ,QAAoB;QAAEX;QAAQY,OAAOR;MAAe;AAC1D,UAAI,OAAOH,gBAAgB,UAAU;AACnCU,cAAME,MAAMZ;MACd,WAAWN,YAAYM,WAAAA,GAAc;AACnCU,cAAMG,SAASb;MACjB;AAEAQ,cAAQM,KAAKJ,KAAAA;AACbN,UAAIW,IAAIb,aAAaM,OAAAA;AACrBQ,cAAQV,cAAcL,OAAO,aAAaG,GAAAA;IAC5C;EACF;AACF;AA3BSN;AA6BF,IAAMmB,MAAMnB,mBAAmB,KAAA;AAC/B,IAAMoB,OAAOpB,mBAAmB,MAAA;AAChC,IAAMqB,QAAQrB,mBAAmB,OAAA;AACjC,IAAMsB,QAAQtB,mBAAmB,OAAA;AACjC,IAAMuB,UAAUvB,mBAAmB,SAAA;AACnC,IAAMwB,UAAUxB,mBAAmB,SAAA;AACnC,IAAMyB,KAAKzB,mBAAmB,IAAA;AAC9B,IAAM0B,YAAY1B,mBAAmB,MAAA;AAErC,SAAS2B,IAAIC,MAAgC;AAClD,SAAO,CAACzB,QAAQC,aAAaC,mBAAAA;AAC3B,QAAID,gBAAgBN,OAAW;AAC/B,UAAMQ,MACJC,QAA4CC,cAAcL,OAAO,WAAW,KAAK,oBAAIM,IAAAA;AACvF,UAAMC,UAAUJ,IAAIK,IAAIP,WAAAA,KAAgB,CAAA;AACxCM,YAAQM,KAAK;MACXf,QAAQ;MACRY,OAAOR;MACPwB,aAAaD,MAAMC;IACrB,CAAA;AACAvB,QAAIW,IAAIb,aAAaM,OAAAA;AACrBQ,YAAQV,cAAcL,OAAO,aAAaG,GAAAA;EAC5C;AACF;AAdgBqB;;;AC1BT,SAASG,OAAOC,OAAgBC,OAAsB,CAAC,GAAC;AAC7D,SAAO,CAACC,QAAgBC,gBAAAA;AACtB,UAAMC,WAAWC,QAAuBC,cAAcJ,OAAO,WAAW,KAAK,CAAA;AAC7EE,aAASG,KAAK;MAAEP;MAAOC;MAAME;IAAY,CAAA;AACzCK,YAAQF,cAAcJ,OAAO,aAAaE,QAAAA;EAC5C;AACF;AANgBL;;;AC7CT,SAASU,SAASC,QAAc;AACrC,SAAO,CAACC,QAAQC,gBAAAA;AACdC,YAAQC,cAAcH,OAAO,aAAaD,QAAQE,WAAAA;EACpD;AACF;AAJgBH;AAMT,SAASM,OAAOC,MAAcC,OAAa;AAChD,SAAO,CAACN,QAAQC,gBAAAA;AACd,UAAMM,WACJC,QAA4BC,eAAeT,OAAO,aAAaC,WAAAA,KAAgB,CAAA;AACjFM,aAASG,KAAK;MAACL;MAAMC;KAAM;AAC3BJ,YAAQO,eAAeT,OAAO,aAAaO,UAAUN,WAAAA;EACvD;AACF;AAPgBG;AAcT,SAASO,SAASC,KAAab,SAAS,KAAG;AAChD,SAAO,CAACC,QAAQC,gBAAAA;AACdC,YAAsBW,gBAAgBb,OAAO,aAAa;MAAEY;MAAKb;IAAO,GAAGE,WAAAA;EAC7E;AACF;AAJgBU;;;ACbT,SAASG,aACXC,QAAkB;AAIrB,SAAO,CAACC,QAAgBC,gBAAAA;AACtB,UAAMC,eAAeD,cAAcD,OAAO,cAAcA;AACxD,UAAMG,WAAWC,QAAoBC,YAAYH,cAAcD,WAAAA,KAAgB,CAAA;AAC/EK,YAAQD,YAAYH,cAAc;SAAIC;SAAaJ;OAASE,WAAAA;EAC9D;AACF;AAVgBH;AAYT,SAASS,mBAAmBC,cAAwB;AACzD,SAAO,CAACR,QAAgBC,gBAAAA;AACtB,UAAMC,eAAeD,cAAcD,OAAO,cAAcA;AACxD,UAAMG,WAAWC,QAAoBK,kBAAkBP,cAAcD,WAAAA,KAAgB,CAAA;AACrFK,YAAQG,kBAAkBP,cAAc;SAAIC;SAAaK;OAAeP,WAAAA;EAC1E;AACF;AANgBM;AAQT,SAASG,cAAcC,SAAmB;AAC/C,SAAO,CAACX,QAAgBC,gBAAAA;AACtB,UAAMC,eAAeD,cAAcD,OAAO,cAAcA;AACxD,UAAMG,WAAWC,QAAoBQ,aAAaV,cAAcD,WAAAA,KAAgB,CAAA;AAChFK,YAAQM,aAAaV,cAAc;SAAIC;SAAaQ;OAAUV,WAAAA;EAChE;AACF;AANgBS;AAUT,SAASG,SAASC,YAAsB;AAC7C,SAAO,CAACd,WAAAA;AACNM,YAAQS,kBAAkBf,QAAQc,UAAAA;EACpC;AACF;AAJgBD;;;AChChB,OAAO;AAsBP,IAAIG,sBAAsB;AAEnB,SAASC,kBAAAA;AACd,QAAMC,MAAMC,uBAAOC,IAAI,kBAAkB,EAAEJ,mBAAAA,EAAqB;AAEhE,QAAMK,YAAY,wBAACC,UAAAA;AACjB,WAAO,CAACC,QAAgBC,gBAAAA;AACtB,YAAMC,aAAaD,gBAAgBE,SAAYH,OAAO,cAAcA;AACpEI,cAAQC,eAAeV,KAAKI,OAAOG,YAAYD,WAAAA;IACjD;EACF,GALkB;AAQhBH,YAAiDH,MAAMA;AACzD,SAAOG;AACT;AAbgBJ;AAqBT,SAASY,YACdC,SACAR,OAAQ;AAER,SAAO,CAACC,QAAgBC,gBAAAA;AACtB,UAAMC,aAAaD,gBAAgBE,SAAYH,OAAO,cAAcA;AACpEI,YAAQC,eAAeE,SAASR,OAAOG,YAAYD,WAAAA;EACrD;AACF;AARgBK;AAeT,IAAME,YAAN,MAAMA;EAnEb,OAmEaA;;;;;;;;;;;;;EAWXC,IACEX,WACAE,QACAC,aACe;AACf,UAAMN,MAAOG,UAAuCH;AACpD,QAAI,CAACA,IAAK,QAAOQ;AACjB,QAAIF,gBAAgBE,QAAW;AAC7B,aAAOC,QAAQM,YAAYf,KAAKK,QAAQC,WAAAA;IAC1C;AACA,WAAOG,QAAQM,YAAYf,KAAKK,MAAAA;EAClC;;;;EAKAW,SACEhB,KACAK,QACAC,aACe;AACf,QAAIA,gBAAgBE,QAAW;AAC7B,aAAOC,QAAQM,YAAYf,KAAKK,QAAQC,WAAAA;IAC1C;AACA,WAAOG,QAAQM,YAAYf,KAAKK,MAAAA;EAClC;;;;;;;;;;;;;;;EAgBAY,kBACEd,WACAE,QACAC,aACe;AACf,QAAIA,gBAAgBE,QAAW;AAC7B,YAAMU,cAAc,KAAKJ,IAAIX,WAAWE,QAAQC,WAAAA;AAChD,UAAIY,gBAAgBV,OAAW,QAAOU;IACxC;AACA,WAAO,KAAKJ,IAAIX,WAAWE,MAAAA;EAC7B;;;;;EAMAc,uBACEnB,KACAK,QACAC,aACe;AACf,QAAIA,gBAAgBE,QAAW;AAC7B,YAAMU,cAAc,KAAKF,SAAYhB,KAAKK,QAAQC,WAAAA;AAClD,UAAIY,gBAAgBV,OAAW,QAAOU;IACxC;AACA,WAAO,KAAKF,SAAYhB,KAAKK,MAAAA;EAC/B;;;;;;;;;;;;;;;;;;;;;;;EAwBAe,eACEjB,WACAE,QACAC,aACmC;AACnC,UAAMe,SAAoB,CAAA;AAC1B,QAAIf,gBAAgBE,QAAW;AAC7B,YAAMU,cAAc,KAAKJ,IAAIX,WAAWE,QAAQC,WAAAA;AAChD,UAAIY,gBAAgBV,QAAW;AAC7B,YAAIc,MAAMC,QAAQL,WAAAA,EAAcG,QAAOG,KAAI,GAAIN,WAAAA;YAC1CG,QAAOG,KAAKN,WAAAA;MACnB;IACF;AACA,UAAMO,aAAa,KAAKX,IAAIX,WAAWE,MAAAA;AACvC,QAAIoB,eAAejB,QAAW;AAC5B,UAAIc,MAAMC,QAAQE,UAAAA,EAAaJ,QAAOG,KAAI,GAAIC,UAAAA;UACzCJ,QAAOG,KAAKC,UAAAA;IACnB;AACA,WAAOJ;EACT;AACF;;;ACrKA,IAAMK,eAAeC,uBAAOC,IAAI,kCAAA;AAChC,IAAMC,oBAAoBF,uBAAOC,IAAI,uCAAA;AAe9B,SAASE,SAASC,SAAwB;AAC/C,SAAO,CAACC,QAAgBC,gBAAAA;AACtB,UAAMC,eAAeD,gBAAgBE,SAAYH,OAAO,cAAcA;AACtEI,YAAQV,cAAcQ,cAAcH,SAASE,WAAAA;EAC/C;AACF;AALgBH;AAcT,SAASO,aAAaC,OAAO,MAAI;AACtC,SAAO,CAACN,QAAgBC,gBAAAA;AACtB,UAAMC,eAAeD,gBAAgBE,SAAYH,OAAO,cAAcA;AACtEI,YAAQP,mBAAmBK,cAAcI,MAAML,WAAAA;EACjD;AACF;AALgBI;AAWT,SAASE,mBACdP,QACAC,aAA6B;AAE7B,SAAOO,QAAyBd,cAAcM,QAAQC,WAAAA;AACxD;AALgBM;AAUT,SAASE,kBAAkBT,QAAkBC,aAA6B;AAC/E,SAAOO,QAAiBX,mBAAmBG,QAAQC,WAAAA,KAAgB;AACrE;AAFgBQ;;;AC1DT,SAASC,oBAAoBC,aAAuB;AACzD,QAAMC,OAAO,oBAAIC,IAAAA;AACjB,QAAMC,SAAqB,CAAA;AAC3B,aAAWC,QAAQJ,aAAa;AAC9B,QAAIC,KAAKI,IAAID,IAAAA,GAAO;AAClBE,cAAQC,KACN,8BAA8BH,KAAKI,IAAI,sDAAiD;AAE1F;IACF;AACAP,SAAKQ,IAAIL,IAAAA;AACTD,WAAOO,KAAKN,IAAAA;EACd;AACA,SAAOD,OAAOQ,QAAQ,CAACP,SAAAA;AACrB,UAAMQ,QAAQC,uBAAuBT,IAAAA;AACrC,WAAOQ,MAAME,IAAI,CAACC,OAAO;MACvBC,MAAMD,EAAEC;MACRC,UAAUF,EAAEE;MACZC,YAAYH;IACd,EAAA;EACF,CAAA;AACF;AArBgBhB;;;ACZhB,OAAO;AA4DA,SAASoB,uBACdC,mBAA4D;AAE5D,QAAM,EAAEC,aAAaC,WAAWC,WAAWC,WAAU,IAAKC,MAAMC,QAAQN,iBAAAA,IACpE;IACEC,aAAaD;IACbE,WAAWK;IACXJ,WAAWI;IACXH,YAAYG;EACd,IACAP;AAGJ,QAAMQ,qBAAqB,IAAIC,uBAAuBP,SAAAA;AACtD,MAAIC,UAAWA,WAAUK,kBAAAA;AACzB,QAAME,oBAAoBF,mBAAmBG,WAAU;AAGvD,QAAMC,OAAO,oBAAIC,IAAAA;AACjB,QAAMC,SAAqB,CAAA;AAC3B,aAAWC,QAAQd,aAAa;AAC9B,QAAIW,KAAKI,IAAID,IAAAA,EAAO;AACpBH,SAAKK,IAAIF,IAAAA;AACTD,WAAOI,KAAKH,IAAAA;EACd;AAGA,QAAMI,SAAmD,CAAA;AACzD,aAAWJ,QAAQD,QAAQ;AACzB,UAAMM,WAAWC,aAAaN,MAAMb,SAAAA;AACpC,UAAMoB,QAAQC,uBAAuBR,IAAAA;AACrC,eAAWS,KAAKF,OAAO;AACrBH,aAAOD,KAAK;QAAEO,MAAMD;QAAGJ;MAAS,CAAA;IAClC;EACF;AAGAD,SAAOO,KAAK,CAACC,GAAGC,MAAAA;AACd,UAAMC,KAAKF,EAAEF,KAAKK,SAASC,SAAS,GAAA;AACpC,UAAMC,KAAKJ,EAAEH,KAAKK,SAASC,SAAS,GAAA;AACpC,QAAIF,OAAOG,GAAI,QAAOH,KAAK,IAAI;AAC/B,WAAO;EACT,CAAA;AAEA,QAAMI,UAAW,wBAACC,YAChBC,cAAchB,QAAQe,SAAShC,WAAWQ,mBAAmBN,UAAAA,GAD9C;AAEjB6B,UAAQG,UAAU,CAACC,QAAgBC,aACjCC,UAAUpB,QAAQkB,OAAOG,YAAW,GAAIF,QAAAA,MAAc;AACxD,SAAOL;AACT;AAjDgBlC;AAuDT,SAAS0C,sBACdzC,mBAA4D;AAE5D,QAAM0C,SAAS3C,uBAAuBC,iBAAAA;AAEtC,QAAM2C,UAAUC,kBAAAA;AAChB,SAAOD,QAAQE,aAAa,OAAOX,YAAAA;AACjC,UAAMY,MAAM,MAAMJ,OAAOR,OAAAA;AACzB,QAAIY,IAAK,QAAOA;AAChB,UAAM,EAAER,SAAQ,IAAK,IAAIS,IAAIb,QAAQc,GAAG;AACxC,WAAOC,aAAa,KAAK;MACvBC,OAAO;QACLC,MAAM;QACNC,SAAS,gBAAgBlB,QAAQG,OAAOG,YAAW,CAAA,IAAMF,QAAAA;MAC3D;IACF,CAAA;EACF,CAAA;AACF;AAjBgBG;AAqBhB,eAAeN,cACbhB,QACAe,SACAhC,WACAQ,oBAA0C,CAAA,GAC1CN,YAAuB;AAEvB,QAAM4C,MAAM,IAAID,IAAIb,QAAQc,GAAG;AAC/B,QAAMX,SAASH,QAAQG,OAAOG,YAAW;AACzC,QAAMF,WAAWU,IAAIV;AAErB,QAAMe,QAAQd,UAAUpB,QAAQkB,QAAQC,QAAAA;AAExC,MAAI,CAACe,MAAO,QAAO;AAEnB,QAAM,EAAE5B,MAAML,UAAUkC,OAAM,IAAKD;AAEnC,MAAI;AAEF,UAAME,aAAa,MAAMC,cAAc9C,mBAAmBwB,SAASI,QAAAA;AACnE,QAAIiB,WAAY,QAAOA;AAGvB,UAAME,MAAMC,uBAAuBxB,SAASd,SAAS,aAAaK,KAAKkC,WAAW;AAClF,UAAMC,gBAAgB,MAAMC,UAAUpC,KAAKqC,QAAQL,KAAKvD,SAAAA;AACxD,QAAI0D,cAAe,QAAOA;AAI1B,QAAInC,KAAKsC,OAAO;AACd,UAAI,CAAC3D,YAAY;AACf,eAAO6C,aAAa,KAAK;UACvBC,OAAO;YACLC,MAAM;YACNC,SACE,oBAAoBY,OAAOvC,KAAKkC,WAAW,CAAA;UAE/C;QACF,CAAA;MACF;AACA,aAAO,MAAMvD,WAAWqB,KAAKsC,MAAME,QAAQ/B,SAAST,KAAKsC,MAAMG,IAAI;IACrE;AAGA,UAAMC,OAAO,MAAMC,YAAY/B,QAAQH,SAAST,IAAAA;AAChD,QAAI0C,gBAAgBE,SAAU,QAAOF;AAGrC,UAAMG,OAAOC,UAAU9C,KAAK+C,cAAc;MACxCtC;MACAiC;MACAb;MACAmB,OAAOC,OAAOC,YAAY3B,IAAI4B,YAAY;IAC5C,CAAA;AAGA,QAAInD,KAAKoD,UAAU;AACjB,aAAO,IAAIR,SAAS,MAAM;QACxBS,QAAQrD,KAAKoD,SAASC;QACtBC,SAAS;UAAEC,UAAUvD,KAAKoD,SAAS7B;QAAI;MACzC,CAAA;IACF;AAEA,UAAMiC,YAAa7D,SAA+CK,KAAKkC,WAAW;AAGlF,UAAMuB,SAAS,MAAMC,gBACnB1D,KAAK2D,cACL,MAAMH,UAAUI,MAAMjE,UAAUkD,IAAAA,GAChCpC,SACAhC,SAAAA;AAMF,QAAIgF,kBAAkBb,SAAU,QAAOa;AAEvC,WAAOI,cAAcJ,QAAQzD,MAAMY,MAAAA;EACrC,SAASkD,KAAK;AACZ,WAAOC,oBAAoBD,KAAK9D,KAAKgE,SAASvD,SAAShC,SAAAA;EACzD;AACF;AAlFeiC;AAsFf,eAAe0B,UACbC,QACA4B,SACAxF,WAAuB;AAEvB,aAAWyF,aAAa7B,QAAQ;AAC9B,UAAM8B,QAAQvE,aAAasE,WAAWzF,SAAAA;AACtC,UAAM2F,UAAU,MAAMD,MAAME,YAAYJ,OAAAA;AACxC,QAAI,CAACG,SAAS;AACZ,YAAME,KAAK,IAAIC,mBAAmB,oBAAA;AAClC,aAAO/C,aAAa8C,GAAGE,YAAYF,GAAGG,OAAM,CAAA;IAC9C;EACF;AACA,SAAO;AACT;AAderC;AAkBf,eAAeO,YAAY/B,QAAgBH,SAAkBT,MAAgB;AAC3E,MAAI,CAAC;IAAC;IAAQ;IAAO;IAASM,SAASM,MAAAA,EAAS,QAAO9B;AAEvD,MAAI4D;AACJ,MAAI;AACF,UAAMgC,OAAO,MAAMjE,QAAQiE,KAAI;AAC/BhC,WAAOgC,OAAOC,KAAKC,MAAMF,IAAAA,IAAQ5F;EACnC,QAAQ;AACN4D,WAAO5D;EACT;AAEA,MAAIkB,KAAK6E,cAAcnC,SAAS5D,QAAW;AACzC,UAAM2E,SAASzD,KAAK6E,WAAWC,UAAUpC,IAAAA;AACzC,QAAI,CAACe,OAAOsB,SAAS;AACnB,aAAOvD,aAAa,KAAK;QAAEC,OAAO;UAAEC,MAAM;UAAoBsD,QAAQvB,OAAOhC,MAAMuD;QAAO;MAAE,CAAA;IAC9F;AACAtC,WAAOe,OAAOwB;EAChB;AACA,SAAOvC;AACT;AAnBeC;AAuBf,SAASkB,cAAcJ,QAAiBzD,MAAkBY,QAAc;AACtE,QAAMyC,SAASrD,KAAKqD,WAAWzC,WAAW,SAAS,MAAM;AACzD,QAAM0C,UAAkC;IAAE,gBAAgB;EAAmB;AAC7E,aAAW,CAAC4B,MAAMC,KAAAA,KAAUnF,KAAKsD,SAAS;AACxCA,YAAQ4B,KAAKE,YAAW,CAAA,IAAMD;EAChC;AAEA,MAAI1B,WAAW3E,UAAa2E,WAAW,MAAM;AAC3C,WAAO,IAAIb,SAAS,MAAM;MAAES,QAAQA,WAAW,MAAM,MAAMA;MAAQC;IAAQ,CAAA;EAC7E;AAEA,MAAI,OAAOG,WAAW,UAAU;AAC9BH,YAAQ,cAAA,IAAkB;AAC1B,WAAO,IAAIV,SAASa,QAAQ;MAAEJ;MAAQC;IAAQ,CAAA;EAChD;AAEA,SAAO,IAAIV,SAAS+B,KAAKU,UAAU5B,MAAAA,GAAS;IAAEJ;IAAQC;EAAQ,CAAA;AAChE;AAjBSO;AAmBT,SAASrC,aAAa6B,QAAgBX,MAAa;AACjD,SAAO,IAAIE,SAAS+B,KAAKU,UAAU3C,IAAAA,GAAO;IACxCW;IACAC,SAAS;MAAE,gBAAgB;IAAmB;EAChD,CAAA;AACF;AALS9B;AAeT,SAASV,UACPpB,QACAkB,QACAC,UAAgB;AAEhB,aAAW,EAAEb,MAAML,SAAQ,KAAMD,QAAQ;AACvC,QAAIM,KAAKsF,SAAS,SAAStF,KAAKsF,SAAS1E,OAAQ;AACjD,UAAMiB,SAAS0D,UAAUvF,KAAKK,UAAUQ,QAAAA;AACxC,QAAIgB,WAAW,KAAM,QAAO;MAAE7B;MAAML;MAAUkC;IAAO;EACvD;AACA,SAAO;AACT;AAXSf;AAaT,SAASyE,UAAUC,SAAiB3E,UAAgB;AAClD,QAAM4E,aAAuB,CAAA;AAC7B,QAAMC,WAAWF,QAAQG,QAAQ,WAAW,CAACC,IAAIV,SAAAA;AAC/CO,eAAWhG,KAAKyF,IAAAA;AAChB,WAAO;EACT,CAAA;AACA,QAAMtD,QAAQ,IAAIiE,OAAO,IAAIH,QAAAA,GAAW,EAAEI,KAAKjF,QAAAA;AAC/C,MAAI,CAACe,MAAO,QAAO;AACnB,QAAMC,SAAiC,CAAC;AACxC4D,aAAWM,QAAQ,CAACb,MAAMc,MAAAA;AACxBnE,WAAOqD,IAAAA,IAAQtD,MAAMoE,IAAI,CAAA;EAC3B,CAAA;AACA,SAAOnE;AACT;AAbS0D;AAwBT,SAASzC,UAAUC,cAA4Bf,KAAe;AAC5D,MAAIe,aAAakD,WAAW,EAAG,QAAO,CAAA;AACtC,QAAMC,WAAWC,KAAKC,IAAG,GAAIrD,aAAasD,IAAI,CAACC,MAAMA,EAAEC,KAAK,CAAA;AAC5D,QAAM1D,OAAkBjE,MAAM4H,KAAK;IAAEP,QAAQC,WAAW;EAAE,GAAG,MAAMpH,MAAAA;AACnE,aAAWwH,KAAKvD,cAAc;AAC5B,YAAQuD,EAAEG,QAAM;MACd,KAAK;AACH5D,aAAKyD,EAAEC,KAAK,IAAIvE,IAAIvB;AACpB;MACF,KAAK;AACHoC,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAO1E,IAAIU,KAAiC4D,EAAEI,GAAG,IAAI1E,IAAIU;AAC3E;MACF,KAAK;AACHG,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAM1E,IAAIH,OAAOyE,EAAEI,GAAG,IAAI1E,IAAIH;AAChD;MACF,KAAK;AACHgB,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAM1E,IAAIgB,MAAMsD,EAAEI,GAAG,IAAI1E,IAAIgB;AAC/C;MACF,KAAK;AACHH,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MACd1E,IAAIvB,QAAQ6C,QAAQqD,IAAIL,EAAEI,IAAItB,YAAW,CAAA,IACzCnC,OAAOC,YAAYlB,IAAIvB,QAAQ6C,QAAQsD,QAAO,CAAA;AAClD;MACF,KAAK;AACH/D,aAAKyD,EAAEC,KAAK,IAAIvE,IAAIvB,QAAQ6C,QAAQqD,IAAI,iBAAA,KAAsB;AAC9D;MACF,KAAK;AACH9D,aAAKyD,EAAEC,KAAK,IAAIzH;AAChB;MACF;AACE+D,aAAKyD,EAAEC,KAAK,IAAIzH;IACpB;EACF;AACA,SAAO+D;AACT;AAlCSC;;;AC9RF,SAAS+D,kBACdC,SACAC,gBAAuC;AAEvC,iBAAeC,QACbC,QACAC,MACAC,MACAC,MAA2E;AAE3E,UAAMC,MAAM,IAAIC,IAAIJ,MAAMJ,OAAAA;AAC1B,QAAIM,MAAMG,OAAO;AACf,iBAAW,CAACC,GAAGC,CAAAA,KAAMC,OAAOC,QAAQP,KAAKG,KAAK,EAAGF,KAAIO,aAAaC,IAAIL,GAAGC,CAAAA;IAC3E;AACA,UAAMK,UAAkC;MAAE,GAAGf;MAAgB,GAAGK,MAAMU;IAAQ;AAC9E,QAAIX,SAASY,OAAWD,SAAQ,cAAA,IAAkB;AAElD,UAAME,MAAM,MAAMC,MAAMZ,IAAIa,SAAQ,GAAI;MACtCjB;MACAa;MACAX,MAAMA,SAASY,SAAYI,KAAKC,UAAUjB,IAAAA,IAAQY;IACpD,CAAA;AAEA,QAAI,CAACC,IAAIK,IAAI;AACX,YAAMC,QAAQ,MAAMN,IAAIO,KAAI,EAAGC,MAAM,OAAO;QAAEC,SAAST,IAAIU;MAAW,EAAA;AACtE,YAAM,IAAIC,iBAAiBX,IAAIY,QAAQN,KAAAA;IACzC;AAEA,QAAIN,IAAIY,WAAW,IAAK,QAAOb;AAC/B,WAAOC,IAAIO,KAAI;EACjB;AA1BevB;AA4Bf,SAAO;IACL6B,KAAK,wBAAC3B,MAAcE,SAAmCJ,QAAQ,OAAOE,MAAMa,QAAWX,IAAAA,GAAlF;IACL0B,MAAM,wBAAC5B,MAAcC,MAAgBC,SACnCJ,QAAQ,QAAQE,MAAMC,MAAMC,IAAAA,GADxB;IAEN2B,KAAK,wBAAC7B,MAAcC,MAAgBC,SAClCJ,QAAQ,OAAOE,MAAMC,MAAMC,IAAAA,GADxB;IAEL4B,QAAQ,wBAAC9B,MAAcE,SACrBJ,QAAQ,UAAUE,MAAMa,QAAWX,IAAAA,GAD7B;EAEV;AACF;AAzCgBP;AA2CT,IAAM8B,mBAAN,cAA+BM,MAAAA;EAhGtC,OAgGsCA;;;;;EACpC,YACkBL,QACAzB,MAChB;AACA,UAAM,QAAQyB,MAAAA,KAAWT,KAAKC,UAAUjB,IAAAA,CAAAA,EAAO,GAAA,KAH/ByB,SAAAA,QAAAA,KACAzB,OAAAA;AAGhB,SAAK+B,OAAO;EACd;AACF;;;ACjEO,SAASC,SAA6BC,QAAS;AACpD,SAAOA;AACT;AAFgBD;;;ACLhB,SAASE,KAAKC,OAAa;AACzB,MAAIC,OAAO;AACX,WAASC,IAAI,GAAGA,IAAIF,MAAMG,QAAQD,KAAK;AAErCD,YAASA,QAAQ,KAAKA,OAAOD,MAAMI,WAAWF,CAAAA,IAAM;EACtD;AAEA,UAAQD,SAAS,GAAGI,SAAS,EAAA;AAC/B;AARSN;AAsBF,SAASO,YAAYC,KAAcC,UAAwB,CAAC,GAAC;AAClE,QAAMC,UAAUC,eAAeH,GAAAA;AAC/B,QAAMI,SAASC,cAAcL,GAAAA;AAC7B,QAAMM,WAAWC,aAAaP,GAAAA;AAE9B,QAAMQ,SAAShB,KAAKU,OAAAA;AAEpB,QAAMO,eAAeC,QAAQC,IAAIC,aAAa;AAE9C,SAAO;IACLJ;IACAN;IACAE;IACAH;IACA,GAAIK,YAAY,CAACG,eAAe;MAAEI,OAAOP;IAAS,IAAI,CAAC;EACzD;AACF;AAhBgBP;AAsBhB,SAASI,eAAeH,KAAY;AAClC,MAAIA,eAAec,OAAO;AACxB,WAAOd,IAAIE;EACb;AACA,MAAI,OAAOF,QAAQ,UAAU;AAC3B,WAAOA;EACT;AACA,MAAI,OAAOA,QAAQ,UAAU;AAC3B,WAAOe,OAAOf,GAAAA;EAChB;AAEA,MAAI;AACF,WAAOgB,KAAKC,UAAUjB,GAAAA;EACxB,QAAQ;AACN,WAAO;EACT;AACF;AAhBSG;AAkBT,SAASE,cAAcL,KAAY;AACjC,MAAIA,eAAekB,eAAe;AAChC,WAAOlB,IAAImB;EACb;AACA,SAAO;AACT;AALSd;AAOT,SAASE,aAAaP,KAAY;AAChC,MAAIA,eAAec,OAAO;AACxB,WAAOd,IAAIa;EACb;AACA,SAAOO;AACT;AALSb;;;ACrET,SAASc,oBACPC,OACAC,mBAA2C;AAE3C,SAAO,MAAMC,sBAAsBF,MAAMG,UAAS;IAtCpD,OAsCoD;;;IAIhD,YAAYC,OAA2C;AACrD,YAAMA,KAAAA;AACN,WAAKC,QAAQ;QAAEC,UAAU;MAAM;IACjC;IAEA,OAAOC,2BAAkD;AACvD,aAAO;QAAED,UAAU;MAAK;IAC1B;IAEAE,SAAyC;AACvC,UAAI,KAAKH,MAAMC,UAAU;AACvB,eAAON,MAAMS,cAAcR,iBAAAA;MAC7B;AACA,aAAOD,MAAMS,cAAcT,MAAMU,UAAU,MAAM,KAAKN,MAAMO,QAAQ;IACtE;EACF;AACF;AAxBSZ;AAyCT,eAAsBa,qBACpBC,MAAe;AAGf,QAAMb,QAAQ,MAAM,OAAO,OAAA;AAE3B,SAAOc,YAAYd,OAAOa,IAAAA;AAC5B;AAPsBD;AAStB,SAASE,YAAYd,OAA0Be,MAAe;AAC5D,QAAM,EAAEC,QAAQC,QAAQC,MAAMC,MAAMC,SAASC,SAASC,OAAOC,cAAa,IAAKR;AAG/E,MAAI,CAACI,MAAM;AACT,WAAO;EACT;AAGA,MAAIK,UAAmCxB,MAAMS,cAAcU,IAAAA;AAG3D,MAAIE,SAAS;AACXG,cAAUxB,MAAMS,cACdT,MAAMyB,UACN;MAAEC,UAAU1B,MAAMS,cAAcY,OAAAA;IAAS,GACzCG,OAAAA;EAEJ;AAGA,MAAID,eAAe;AACjB,UAAMI,WAAW5B,oBAAoBC,OAAOuB,aAAAA;AAC5CC,cAAUxB,MAAMS,cAAckB,UAAU,MAAMH,OAAAA;EAChD;AAGA,MAAIP,QAAQ;AACVO,cAAUxB,MAAMS,cAAcQ,QAAQ,MAAMO,OAAAA;EAC9C;AAEA,SAAOA;AACT;AAhCSV;;;AC7CF,SAASc,cAAcC,KAAW;AACvCC,gBAAc;IAAEC,MAAM;IAAOC,OAAOH;IAAKI,WAAWC,KAAKC,IAAG;EAAG,CAAA;AACjE;AAFgBP;AAkBT,SAASQ,eAAeC,MAAY;AACzCP,gBAAc;IAAEC,MAAM;IAAQC,OAAOK;IAAMJ,WAAWC,KAAKC,IAAG;EAAG,CAAA;AACnE;AAFgBC;AAQT,SAASE,yBAAAA;AACd,QAAMC,MAAMC,qBAAAA;AACZ,MAAI,CAACD,IAAK,QAAO,CAAA;AACjB,SACGA,IAAmEE,wBAAwB,CAAA;AAEhG;AANgBH;AAUhB,SAASR,cAAcY,QAA0B;AAC/C,QAAMH,MAAMC,qBAAAA;AACZ,MAAI,CAACD,KAAK;AACRI,YAAQC,KACN,0FAAA;AAEF;EACF;AACA,QAAMC,WAAWN;AACjBM,WAASJ,yBAAyB,CAAA;AAClCI,WAASJ,qBAAqBK,KAAKJ,MAAAA;AACrC;AAXSZ;","names":["inferPrefix","className","stripped","replace","kebab","toLowerCase","Controller","prefixOrOpts","maybeOpts","target","prefix","opts","name","setMeta","CONTROLLER_PREFIX","host","makeVerbDecorator","verb","path","target","propertyKey","existing","getMeta","ROUTE_METHODS","push","setMeta","Get","Post","Put","Patch","Delete","Options","Head","All","isZodSchema","v","undefined","safeParse","makeParamDecorator","source","keyOrSchema","target","propertyKey","parameterIndex","map","getMeta","ROUTE_PARAMS","Map","entries","get","entry","index","key","schema","push","set","setMeta","Req","Body","Param","Query","Headers","Session","Ip","HostParam","Res","opts","passthrough","Expose","agent","opts","target","propertyKey","existing","getMeta","EXPOSE_AGENT","push","setMeta","HttpCode","status","target","propertyKey","setMeta","ROUTE_STATUS","Header","name","value","existing","getMeta","ROUTE_HEADERS","push","Redirect","url","ROUTE_REDIRECT","UseGuards","guards","target","propertyKey","actualTarget","existing","getMeta","USE_GUARDS","setMeta","UseInterceptors","interceptors","USE_INTERCEPTORS","UseFilters","filters","USE_FILTERS","Catch","exceptions","CATCH_EXCEPTIONS","decoratorKeyCounter","createDecorator","key","Symbol","for","decorator","value","target","propertyKey","metaTarget","undefined","Reflect","defineMetadata","SetMetadata","metaKey","Reflector","get","getMetadata","getByKey","getAllAndOverride","methodLevel","getAllAndOverrideByKey","getAllAndMerge","result","Array","isArray","push","classLevel","THROTTLE_KEY","Symbol","for","SKIP_THROTTLE_KEY","Throttle","options","target","propertyKey","actualTarget","undefined","setMeta","SkipThrottle","skip","getThrottleOptions","getMeta","isThrottleSkipped","registerControllers","controllers","seen","Set","unique","Ctor","has","console","warn","name","add","push","flatMap","walks","walkControllerMetadata","map","w","verb","fullPath","walkResult","createDecoratorHandler","controllersOrOpts","controllers","container","configure","serveAgent","Array","isArray","undefined","middlewareConsumer","MiddlewareConsumerImpl","middlewareEntries","getEntries","seen","Set","unique","Ctor","has","add","push","routes","instance","resolveOrNew","walks","walkControllerMetadata","w","walk","sort","a","b","aP","fullPath","includes","bP","handler","request","handleRequest","matches","method","pathname","findRoute","toUpperCase","createDecoratorServer","handle","adapter","createNodeAdapter","createServer","res","URL","url","jsonResponse","error","code","message","match","params","mwResponse","runMiddleware","ctx","createExecutionContext","propertyKey","guardResponse","runGuards","guards","agent","String","module","opts","body","resolveBody","Response","args","buildArgs","paramEntries","query","Object","fromEntries","searchParams","redirect","status","headers","location","handlerFn","result","runInterceptors","interceptors","apply","buildResponse","err","runExceptionFilters","filters","context","GuardCtor","guard","allowed","canActivate","ex","ForbiddenException","statusCode","toJSON","text","JSON","parse","bodySchema","safeParse","success","issues","data","name","value","toLowerCase","stringify","verb","matchPath","pattern","paramNames","regexStr","replace","_m","RegExp","exec","forEach","i","length","maxIndex","Math","max","map","p","index","from","source","key","get","entries","createTypedClient","baseUrl","defaultHeaders","request","method","path","body","opts","url","URL","query","k","v","Object","entries","searchParams","set","headers","undefined","res","fetch","toString","JSON","stringify","ok","error","json","catch","message","statusText","TypedClientError","status","get","post","put","delete","Error","name","contract","routes","djb2","input","hash","i","length","charCodeAt","toString","digestError","err","context","message","extractMessage","status","extractStatus","rawStack","extractStack","digest","isProduction","process","env","NODE_ENV","stack","Error","String","JSON","stringify","HttpException","statusCode","undefined","createErrorBoundary","React","FallbackComponent","ErrorBoundary","Component","props","state","hasError","getDerivedStateFromError","render","createElement","Fragment","children","composeComponentTree","tree","composeNode","node","layout","Layout","page","Page","loading","Loading","error","ErrorFallback","element","Suspense","fallback","Boundary","revalidateTag","tag","collectSignal","kind","value","timestamp","Date","now","revalidatePath","path","getRevalidationSignals","ctx","tryGetRequestContext","_revalidationSignals","signal","console","warn","extended","push"]}
|
package/dist/theokit-plugin.js
CHANGED
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
MiddlewareConsumerImpl,
|
|
3
3
|
loadControllersFromGlob,
|
|
4
4
|
runMiddleware
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OBHHOS6E.js";
|
|
6
6
|
import {
|
|
7
7
|
createExecutionContext,
|
|
8
8
|
walkControllerMetadata
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JQMJK47T.js";
|
|
10
10
|
import {
|
|
11
11
|
nodeIncomingToRequest,
|
|
12
12
|
writeResponseToNode
|
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
} from "./chunk-ELCXHPAD.js";
|
|
17
17
|
import {
|
|
18
18
|
runExceptionFilters
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-CMPP4ULU.js";
|
|
20
20
|
import {
|
|
21
21
|
ForbiddenException
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-KPC7AIVC.js";
|
|
23
23
|
import {
|
|
24
24
|
resolveOrNew
|
|
25
25
|
} from "./chunk-MQAJWR3K.js";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/metadata/keys.ts","../src/metadata/storage.ts","../src/exceptions/http-exception.ts"],"sourcesContent":["/**\n * Global Symbol-keyed metadata namespace constants for @theokit/http.\n *\n * Uses Symbol.for() (global Symbol registry) instead of Symbol() (local) because\n * the SWC loader imports controller files as separate module instances. With local\n * Symbols, the decorator-set metadata keys would be different Symbol instances from\n * the keys used by walkControllerMetadata — making metadata lookup silently fail.\n *\n * Symbol.for() ensures the SAME Symbol instance across module boundaries, which is\n * exactly how reflect-metadata keys should work in a multi-module decorator system.\n */\n\nexport const CONTROLLER_PREFIX = Symbol.for('theokit:http-decorators:controller-prefix')\nexport const ROUTE_METHODS = Symbol.for('theokit:http-decorators:route-methods')\nexport const ROUTE_PARAMS = Symbol.for('theokit:http-decorators:route-params')\nexport const ROUTE_STATUS = Symbol.for('theokit:http-decorators:route-status')\nexport const ROUTE_HEADERS = Symbol.for('theokit:http-decorators:route-headers')\nexport const ROUTE_REDIRECT = Symbol.for('theokit:http-decorators:route-redirect')\nexport const USE_GUARDS = Symbol.for('theokit:http-decorators:use-guards')\nexport const USE_INTERCEPTORS = Symbol.for('theokit:http-decorators:use-interceptors')\nexport const USE_FILTERS = Symbol.for('theokit:http-decorators:use-filters')\nexport const CATCH_EXCEPTIONS = Symbol.for('theokit:http-decorators:catch-exceptions')\n","import 'reflect-metadata'\n\n/**\n * Typed facade over Reflect.defineMetadata / Reflect.getMetadata.\n * Centralizes all reflect-metadata calls so decorators + bridge\n * never call Reflect.* directly (single import point for the polyfill).\n */\n\nexport function setMeta<T>(\n key: symbol,\n target: object,\n value: T,\n propertyKey?: string | symbol,\n): void {\n if (propertyKey !== undefined) {\n Reflect.defineMetadata(key, value, target, propertyKey)\n } else {\n Reflect.defineMetadata(key, value, target)\n }\n}\n\nexport function getMeta<T>(\n key: symbol,\n target: object,\n propertyKey?: string | symbol,\n): T | undefined {\n if (propertyKey !== undefined) {\n return Reflect.getMetadata(key, target, propertyKey) as T | undefined\n }\n return Reflect.getMetadata(key, target) as T | undefined\n}\n","/**\n * HttpException hierarchy for @theokit/http.\n *\n * Per ADR D2: response shape {error: {code, message, statusCode}} matches\n * existing guard (401) and validation (422) format.\n */\n\nconst STATUS_CODES: Record<number, string> = {\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 405: 'METHOD_NOT_ALLOWED',\n 406: 'NOT_ACCEPTABLE',\n 408: 'REQUEST_TIMEOUT',\n 409: 'CONFLICT',\n 410: 'GONE',\n 412: 'PRECONDITION_FAILED',\n 413: 'PAYLOAD_TOO_LARGE',\n 415: 'UNSUPPORTED_MEDIA_TYPE',\n 418: 'IM_A_TEAPOT',\n 422: 'UNPROCESSABLE_ENTITY',\n 429: 'TOO_MANY_REQUESTS',\n 500: 'INTERNAL_SERVER_ERROR',\n 501: 'NOT_IMPLEMENTED',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT',\n 505: 'HTTP_VERSION_NOT_SUPPORTED',\n}\n\nexport interface HttpExceptionOptions {\n cause?: Error\n description?: string\n}\n\nexport class HttpException extends Error {\n public readonly statusCode: number\n public readonly code: string\n public readonly description?: string\n\n constructor(message: string, statusCode: number, options?: HttpExceptionOptions) {\n super(message, options?.cause ? { cause: options.cause } : undefined)\n this.name = this.constructor.name\n this.statusCode = statusCode\n this.code = STATUS_CODES[statusCode] ?? 'INTERNAL_SERVER_ERROR'\n this.description = options?.description\n }\n\n toJSON() {\n return {\n error: {\n code: this.code,\n message: this.message,\n statusCode: this.statusCode,\n ...(this.description ? { description: this.description } : {}),\n },\n }\n }\n}\n\nfunction factory(status: number, defaultMsg: string) {\n return class extends HttpException {\n constructor(message = defaultMsg, options?: HttpExceptionOptions) {\n super(message, status, options)\n this.name = this.constructor.name\n }\n }\n}\n\nexport class BadRequestException extends factory(400, 'Bad Request') {}\nexport class UnauthorizedException extends factory(401, 'Unauthorized') {}\nexport class ForbiddenException extends factory(403, 'Forbidden') {}\nexport class NotFoundException extends factory(404, 'Not Found') {}\nexport class MethodNotAllowedException extends factory(405, 'Method Not Allowed') {}\nexport class NotAcceptableException extends factory(406, 'Not Acceptable') {}\nexport class RequestTimeoutException extends factory(408, 'Request Timeout') {}\nexport class ConflictException extends factory(409, 'Conflict') {}\nexport class GoneException extends factory(410, 'Gone') {}\nexport class PreconditionFailedException extends factory(412, 'Precondition Failed') {}\nexport class PayloadTooLargeException extends factory(413, 'Payload Too Large') {}\nexport class UnsupportedMediaTypeException extends factory(415, 'Unsupported Media Type') {}\nexport class ImATeapotException extends factory(418, \"I'm a Teapot\") {}\nexport class UnprocessableEntityException extends factory(422, 'Unprocessable Entity') {}\nexport class InternalServerErrorException extends factory(500, 'Internal Server Error') {}\nexport class NotImplementedException extends factory(501, 'Not Implemented') {}\nexport class BadGatewayException extends factory(502, 'Bad Gateway') {}\nexport class ServiceUnavailableException extends factory(503, 'Service Unavailable') {}\nexport class GatewayTimeoutException extends factory(504, 'Gateway Timeout') {}\nexport class HttpVersionNotSupportedException extends factory(505, 'HTTP Version Not Supported') {}\nexport class TooManyRequestsException extends factory(429, 'Too Many Requests') {}\n\n/**\n * HttpStatus enum — all standard HTTP status codes as named constants.\n *\n * @example\n * ```ts\n * import { HttpStatus } from '@theokit/http'\n *\n * @HttpCode(HttpStatus.CREATED)\n * @Post()\n * create() { ... }\n *\n * if (res.status === HttpStatus.NOT_FOUND) { ... }\n * ```\n */\nexport const HttpStatus = {\n // 2xx Success\n OK: 200,\n CREATED: 201,\n ACCEPTED: 202,\n NO_CONTENT: 204,\n\n // 3xx Redirection\n MOVED_PERMANENTLY: 301,\n FOUND: 302,\n NOT_MODIFIED: 304,\n TEMPORARY_REDIRECT: 307,\n PERMANENT_REDIRECT: 308,\n\n // 4xx Client Error\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n PAYMENT_REQUIRED: 402,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n NOT_ACCEPTABLE: 406,\n REQUEST_TIMEOUT: 408,\n CONFLICT: 409,\n GONE: 410,\n PRECONDITION_FAILED: 412,\n PAYLOAD_TOO_LARGE: 413,\n UNSUPPORTED_MEDIA_TYPE: 415,\n IM_A_TEAPOT: 418,\n UNPROCESSABLE_ENTITY: 422,\n TOO_MANY_REQUESTS: 429,\n\n // 5xx Server Error\n INTERNAL_SERVER_ERROR: 500,\n NOT_IMPLEMENTED: 501,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n} as const\n\nexport type HttpStatusCode = (typeof HttpStatus)[keyof typeof HttpStatus]\n"],"mappings":"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","names":["CONTROLLER_PREFIX","Symbol","for","ROUTE_METHODS","ROUTE_PARAMS","ROUTE_STATUS","ROUTE_HEADERS","ROUTE_REDIRECT","USE_GUARDS","USE_INTERCEPTORS","USE_FILTERS","CATCH_EXCEPTIONS","setMeta","key","target","value","propertyKey","undefined","Reflect","defineMetadata","getMeta","getMetadata","STATUS_CODES","HttpException","Error","statusCode","code","description","message","options","cause","undefined","name","toJSON","error","factory","status","defaultMsg","BadRequestException","UnauthorizedException","ForbiddenException","NotFoundException","MethodNotAllowedException","NotAcceptableException","RequestTimeoutException","ConflictException","GoneException","PreconditionFailedException","PayloadTooLargeException","UnsupportedMediaTypeException","ImATeapotException","UnprocessableEntityException","InternalServerErrorException","NotImplementedException","BadGatewayException","ServiceUnavailableException","GatewayTimeoutException","HttpVersionNotSupportedException","TooManyRequestsException","HttpStatus","OK","CREATED","ACCEPTED","NO_CONTENT","MOVED_PERMANENTLY","FOUND","NOT_MODIFIED","TEMPORARY_REDIRECT","PERMANENT_REDIRECT","BAD_REQUEST","UNAUTHORIZED","PAYMENT_REQUIRED","FORBIDDEN","NOT_FOUND","METHOD_NOT_ALLOWED","NOT_ACCEPTABLE","REQUEST_TIMEOUT","CONFLICT","GONE","PRECONDITION_FAILED","PAYLOAD_TOO_LARGE","UNSUPPORTED_MEDIA_TYPE","IM_A_TEAPOT","UNPROCESSABLE_ENTITY","TOO_MANY_REQUESTS","INTERNAL_SERVER_ERROR","NOT_IMPLEMENTED","BAD_GATEWAY","SERVICE_UNAVAILABLE","GATEWAY_TIMEOUT"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/bridge/execution-context.ts","../src/bridge/dto-zod.ts","../src/bridge/errors.ts","../src/bridge/walk-metadata.ts"],"sourcesContent":["/**\n * ExecutionContext — Web Standard Request-based context passed to guards.\n *\n * Per ADR D460: the pipeline operates on Web Standard Request/Response.\n * node:http types live ONLY in the runtime adapter (runtime/node.ts).\n *\n * Guards access request headers via request.headers.get('x-role'),\n * NOT via req.headers['x-role'].\n */\n\n/**\n * Execution context available in guards during request processing.\n * Uses Web Standard Request (works on Node, Bun, Deno, CF Workers).\n */\nexport interface ExecutionContext {\n /** The Web Standard Request object. */\n getRequest(): Request\n /** Parsed URL (convenience — avoids re-parsing). */\n getUrl(): URL\n /** The controller class constructor. */\n getClass(): Function\n /** The handler method name (property key on the controller). */\n getMethodName(): string | symbol\n}\n\n/**\n * Interface for guard classes (bound via @UseGuards).\n *\n * @example\n * ```ts\n * class RolesGuard implements CanActivate {\n * canActivate(context: ExecutionContext): boolean {\n * const request = context.getRequest()\n * const role = request.headers.get('x-role')\n * return role === 'admin'\n * }\n * }\n * ```\n */\nexport interface CanActivate {\n canActivate(context: ExecutionContext): boolean | Promise<boolean>\n}\n\n/** Create an ExecutionContext from a Web Standard Request. */\nexport function createExecutionContext(\n request: Request,\n controllerClass: Function,\n methodName: string | symbol,\n): ExecutionContext {\n const url = new URL(request.url)\n return {\n getRequest: () => request,\n getUrl: () => url,\n getClass: () => controllerClass,\n getMethodName: () => methodName,\n }\n}\n","import type { z } from 'zod'\n\n/**\n * Resolves a Zod schema from a DTO class via the `static schema` convention (Pattern D2).\n * Returns undefined when the class doesn't carry a compatible schema.\n */\nexport function resolveDtoSchema(dtoClass: unknown): z.ZodType | undefined {\n if (typeof dtoClass !== 'function') return undefined\n const maybe = (dtoClass as unknown as Record<string, unknown>).schema\n if (\n maybe !== null &&\n maybe !== undefined &&\n typeof (maybe as Record<string, unknown>).safeParse === 'function'\n ) {\n return maybe as z.ZodType\n }\n return undefined\n}\n","/**\n * Configuration error thrown by the bridge when decorator setup is incomplete.\n * Carries actionable messages pointing consumers to the migration guide.\n */\nexport class HttpDecoratorsConfigError extends Error {\n override readonly name = 'HttpDecoratorsConfigError'\n\n constructor(message: string) {\n super(message)\n }\n}\n","import 'reflect-metadata'\nimport type { z } from 'zod'\n\nimport type { ControllerMeta } from '../decorators/controller.js'\nimport type { RouteMethodEntry, HttpVerb } from '../decorators/methods.js'\nimport type { ParamEntry } from '../decorators/params.js'\nimport type { RedirectMeta } from '../decorators/response.js'\nimport {\n getMeta,\n CONTROLLER_PREFIX,\n ROUTE_METHODS,\n ROUTE_PARAMS,\n ROUTE_STATUS,\n ROUTE_HEADERS,\n ROUTE_REDIRECT,\n USE_GUARDS,\n USE_INTERCEPTORS,\n USE_FILTERS,\n} from '../metadata/index.js'\n\nimport { resolveDtoSchema } from './dto-zod.js'\nimport { HttpDecoratorsConfigError } from './errors.js'\n\nexport interface WalkResult {\n verb: HttpVerb\n fullPath: string\n propertyKey: string | symbol\n bodySchema?: z.ZodType\n querySchema?: z.ZodType\n paramsSchema?: z.ZodType\n paramEntries: ParamEntry[]\n status?: number\n headers: [string, string][]\n redirect?: RedirectMeta\n guards: Function[]\n interceptors: Function[]\n filters: Function[]\n}\n\n/**\n * Normalize a joined path: strip doubles, trim trailing, ensure leading.\n * (EC-3)\n */\nexport function joinPath(prefix: string, path: string): string {\n return ('/' + prefix + '/' + path).replace(/\\/+/g, '/').replace(/\\/$/, '') || '/'\n}\n\n/**\n * Resolve the Zod body schema for a method's @Body() param entry.\n *\n * Priority: explicit @Body(zodSchema) > design:paramtypes + DTO static schema.\n * EC-4 relaxed: warns (not throws) when paramtypes missing — @Body(zodSchema) is the fix.\n */\nfunction resolveBodySchema(\n paramEntries: ParamEntry[],\n ControllerClass: Function,\n propertyKey: string | symbol,\n): z.ZodType | undefined {\n const bodyParam = paramEntries.find((p) => p.source === 'body' && !p.key)\n if (!bodyParam) return undefined\n\n // Priority 1: explicit Zod schema from @Body(zodSchema)\n if (bodyParam.schema) return bodyParam.schema\n\n // Priority 2: design:paramtypes + DTO static schema (requires emitDecoratorMetadata)\n const paramTypes: Function[] =\n Reflect.getMetadata('design:paramtypes', ControllerClass.prototype, propertyKey) ?? []\n if (paramTypes.length > 0) {\n return resolveDtoSchema(paramTypes[bodyParam.index])\n }\n\n // EC-4 relaxed: warn when @Body() has no schema and no paramtypes\n console.warn(\n `[@theokit/http] method ${String(propertyKey)} on ` +\n `${ControllerClass.name}: @Body() without explicit schema and ` +\n `emitDecoratorMetadata is not active. Body will be passed raw (no validation). ` +\n `Fix: use @Body(zodSchema) for validation without metadata emission.`,\n )\n return undefined\n}\n\n/** WeakMap cache — metadata is immutable; walk once, reuse forever. */\nconst walkCache = new WeakMap<Function, WalkResult[]>()\n\n/**\n * Walk all decorator metadata on a controller class and produce\n * a structured list of route descriptors. Memoized per class via WeakMap.\n */\nexport function walkControllerMetadata(ControllerClass: Function): WalkResult[] {\n const cached = walkCache.get(ControllerClass)\n if (cached) return cached\n // EC-2: throw when @Controller decorator is missing\n const controllerMeta = getMeta<ControllerMeta>(CONTROLLER_PREFIX, ControllerClass)\n if (!controllerMeta) {\n throw new HttpDecoratorsConfigError(\n `Controller class ${ControllerClass.name} is missing @Controller() decorator. ` +\n `Add @Controller('prefix') to the class declaration.`,\n )\n }\n const { prefix, host } = controllerMeta\n\n // Q4: host captured but enforcement deferred to v0.2.0\n if (host) {\n console.warn(\n `[@theokit/http] @Controller host '${host}' captured but enforcement deferred to v0.2.0`,\n )\n }\n\n const methods = getMeta<RouteMethodEntry[]>(ROUTE_METHODS, ControllerClass) ?? []\n const paramsMap =\n getMeta<Map<string | symbol, ParamEntry[]>>(ROUTE_PARAMS, ControllerClass) ?? new Map()\n\n // Class-level guards/interceptors\n const classGuards = getMeta<Function[]>(USE_GUARDS, ControllerClass) ?? []\n const classInterceptors = getMeta<Function[]>(USE_INTERCEPTORS, ControllerClass) ?? []\n const classFilters = getMeta<Function[]>(USE_FILTERS, ControllerClass) ?? []\n\n const result = methods.map((m) => {\n const paramEntries = paramsMap.get(m.propertyKey) ?? []\n const bodySchema = resolveBodySchema(paramEntries, ControllerClass, m.propertyKey)\n\n // Method-level guards/interceptors (composed: class FIRST per NestJS convention — EC-9)\n const methodGuards = getMeta<Function[]>(USE_GUARDS, ControllerClass, m.propertyKey) ?? []\n const methodInterceptors =\n getMeta<Function[]>(USE_INTERCEPTORS, ControllerClass, m.propertyKey) ?? []\n\n return {\n verb: m.verb,\n fullPath: joinPath(prefix, m.path),\n propertyKey: m.propertyKey,\n bodySchema,\n paramEntries: [...paramEntries].sort((a, b) => a.index - b.index),\n status: getMeta<number>(ROUTE_STATUS, ControllerClass, m.propertyKey),\n headers: getMeta<[string, string][]>(ROUTE_HEADERS, ControllerClass, m.propertyKey) ?? [],\n redirect: getMeta<RedirectMeta>(ROUTE_REDIRECT, ControllerClass, m.propertyKey),\n guards: [...classGuards, ...methodGuards],\n interceptors: [...classInterceptors, ...methodInterceptors],\n filters: getMeta<Function[]>(USE_FILTERS, ControllerClass, m.propertyKey) ?? classFilters,\n }\n })\n\n walkCache.set(ControllerClass, result)\n return result\n}\n"],"mappings":"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","names":["createExecutionContext","request","controllerClass","methodName","url","URL","getRequest","getUrl","getClass","getMethodName","resolveDtoSchema","dtoClass","undefined","maybe","schema","safeParse","HttpDecoratorsConfigError","Error","name","message","joinPath","prefix","path","replace","resolveBodySchema","paramEntries","ControllerClass","propertyKey","bodyParam","find","p","source","key","undefined","schema","paramTypes","Reflect","getMetadata","prototype","length","resolveDtoSchema","index","console","warn","String","name","walkCache","WeakMap","walkControllerMetadata","cached","get","controllerMeta","getMeta","CONTROLLER_PREFIX","HttpDecoratorsConfigError","host","methods","ROUTE_METHODS","paramsMap","ROUTE_PARAMS","Map","classGuards","USE_GUARDS","classInterceptors","USE_INTERCEPTORS","classFilters","USE_FILTERS","result","map","m","bodySchema","methodGuards","methodInterceptors","verb","fullPath","sort","a","b","status","ROUTE_STATUS","headers","ROUTE_HEADERS","redirect","ROUTE_REDIRECT","guards","interceptors","filters","set"]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/dist/{exception-filter-chain-O45FXGEB.js.map → exception-filter-chain-V2MFO4WV.js.map}
RENAMED
|
File without changes
|