@theokit/http 0.4.0 → 0.5.1

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.d.ts CHANGED
@@ -29,6 +29,8 @@ interface TheoAppOptions {
29
29
  agentStreamFactory?: (walk: unknown, tools: unknown[], apiKey: string, model?: string) => (message: string, sessionId: string) => AsyncIterable<unknown>;
30
30
  /** HTML string to serve at GET / (inline frontend). */
31
31
  html?: string;
32
+ /** Directory for static files (CSS, JS, images, fonts). Default: 'public'. Set false to disable. */
33
+ staticDir?: string | false;
32
34
  /** Readiness checks for GET /__theo/ready (K8s readiness probe). */
33
35
  readinessChecks?: ReadinessCheck[];
34
36
  /** Custom health endpoint path (default: '/__theo/health'). */
@@ -40,6 +42,7 @@ declare class TheoApp {
40
42
  private serverHandle;
41
43
  private readonly routes;
42
44
  private frontendHtml?;
45
+ private staticHandler?;
43
46
  private readonly startTime;
44
47
  private readonly healthPath;
45
48
  private readonly readyPath;
package/dist/app.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TheoApp
3
- } from "./chunk-TBMGRXH5.js";
4
- import "./chunk-SMWUPP2C.js";
3
+ } from "./chunk-2CI44FAT.js";
4
+ import "./chunk-QGB5YC4T.js";
5
5
  import "./chunk-HLW7YKZE.js";
6
6
  import "./chunk-3PGQVQWG.js";
7
7
  import "./chunk-7QVYU63E.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createExecutionContext,
3
3
  walkControllerMetadata
4
- } from "./chunk-SMWUPP2C.js";
4
+ } from "./chunk-QGB5YC4T.js";
5
5
  import {
6
6
  createNodeAdapter
7
7
  } from "./chunk-HLW7YKZE.js";
@@ -15,6 +15,159 @@ import {
15
15
 
16
16
  // src/app.ts
17
17
  import "reflect-metadata";
18
+
19
+ // src/static.ts
20
+ var MIME_TYPES = {
21
+ // Text
22
+ css: "text/css; charset=utf-8",
23
+ html: "text/html; charset=utf-8",
24
+ htm: "text/html; charset=utf-8",
25
+ js: "text/javascript; charset=utf-8",
26
+ mjs: "text/javascript; charset=utf-8",
27
+ json: "application/json; charset=utf-8",
28
+ txt: "text/plain; charset=utf-8",
29
+ xml: "application/xml; charset=utf-8",
30
+ csv: "text/csv; charset=utf-8",
31
+ md: "text/markdown; charset=utf-8",
32
+ // Images
33
+ png: "image/png",
34
+ jpg: "image/jpeg",
35
+ jpeg: "image/jpeg",
36
+ gif: "image/gif",
37
+ svg: "image/svg+xml; charset=utf-8",
38
+ webp: "image/webp",
39
+ avif: "image/avif",
40
+ ico: "image/x-icon",
41
+ bmp: "image/bmp",
42
+ // Fonts
43
+ woff: "font/woff",
44
+ woff2: "font/woff2",
45
+ ttf: "font/ttf",
46
+ otf: "font/otf",
47
+ eot: "application/vnd.ms-fontobject",
48
+ // Media
49
+ mp3: "audio/mpeg",
50
+ mp4: "video/mp4",
51
+ webm: "video/webm",
52
+ ogg: "audio/ogg",
53
+ wav: "audio/wav",
54
+ // Documents
55
+ pdf: "application/pdf",
56
+ zip: "application/zip",
57
+ gz: "application/gzip",
58
+ wasm: "application/wasm",
59
+ // Manifest
60
+ webmanifest: "application/manifest+json",
61
+ map: "application/json"
62
+ };
63
+ function getMimeType(filename) {
64
+ const match = filename.match(/\.([a-zA-Z0-9]+)$/);
65
+ if (!match) return "application/octet-stream";
66
+ return MIME_TYPES[match[1].toLowerCase()] ?? "application/octet-stream";
67
+ }
68
+ __name(getMimeType, "getMimeType");
69
+ var TRAVERSAL_REGEX = /(?:^|[/\\])\.{1,2}(?:$|[/\\])|[/\\]{2,}/;
70
+ function isSafePath(path) {
71
+ return !TRAVERSAL_REGEX.test(path);
72
+ }
73
+ __name(isSafePath, "isSafePath");
74
+ async function readFileUniversal(path) {
75
+ const g = globalThis;
76
+ if (g.Bun) {
77
+ try {
78
+ const BunRT = g.Bun;
79
+ const file = BunRT.file(path);
80
+ if (await file.exists()) {
81
+ const buf = await file.arrayBuffer();
82
+ return {
83
+ content: new Uint8Array(buf),
84
+ isDirectory: false
85
+ };
86
+ }
87
+ return null;
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+ if (g.Deno) {
93
+ try {
94
+ const DenoRT = g.Deno;
95
+ const stat = await DenoRT.stat(path);
96
+ if (stat.isDirectory) return {
97
+ content: new Uint8Array(),
98
+ isDirectory: true
99
+ };
100
+ const content = await DenoRT.readFile(path);
101
+ return {
102
+ content,
103
+ isDirectory: false
104
+ };
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+ try {
110
+ const fs = await import("fs");
111
+ const stat = fs.statSync(path);
112
+ if (stat.isDirectory()) return {
113
+ content: new Uint8Array(),
114
+ isDirectory: true
115
+ };
116
+ const buf = fs.readFileSync(path);
117
+ return {
118
+ content: new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength),
119
+ isDirectory: false
120
+ };
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+ __name(readFileUniversal, "readFileUniversal");
126
+ function createStaticHandler(options = {}) {
127
+ const root = options.root ?? "public";
128
+ const maxAge = options.maxAge ?? 0;
129
+ const customMimes = options.mimes ?? {};
130
+ return async (request) => {
131
+ if (request.method !== "GET" && request.method !== "HEAD") return null;
132
+ const url = new URL(request.url);
133
+ let pathname = url.pathname;
134
+ try {
135
+ pathname = decodeURIComponent(pathname);
136
+ } catch {
137
+ return null;
138
+ }
139
+ if (!isSafePath(pathname)) return null;
140
+ if (pathname.startsWith("/api/") || pathname.startsWith("/@theo/")) return null;
141
+ const filePath = (root + pathname).replace(/\/+/g, "/");
142
+ let result = await readFileUniversal(filePath);
143
+ if (result?.isDirectory) {
144
+ const indexPath = (filePath + "/index.html").replace(/\/+/g, "/");
145
+ result = await readFileUniversal(indexPath);
146
+ }
147
+ if (!result || result.isDirectory) return null;
148
+ const mimeType = customMimes[pathname.split(".").pop()?.toLowerCase() ?? ""] ?? getMimeType(pathname);
149
+ const headers = {
150
+ "Content-Type": mimeType,
151
+ "Content-Length": String(result.content.byteLength)
152
+ };
153
+ if (maxAge > 0) {
154
+ headers["Cache-Control"] = `public, max-age=${maxAge}`;
155
+ }
156
+ if (request.method === "HEAD") {
157
+ return new Response(null, {
158
+ status: 200,
159
+ headers
160
+ });
161
+ }
162
+ return new Response(result.content, {
163
+ status: 200,
164
+ headers
165
+ });
166
+ };
167
+ }
168
+ __name(createStaticHandler, "createStaticHandler");
169
+
170
+ // src/app.ts
18
171
  var TheoApp = class _TheoApp {
19
172
  static {
20
173
  __name(this, "TheoApp");
@@ -22,6 +175,7 @@ var TheoApp = class _TheoApp {
22
175
  serverHandle;
23
176
  routes = [];
24
177
  frontendHtml;
178
+ staticHandler;
25
179
  startTime = Date.now();
26
180
  healthPath;
27
181
  readyPath;
@@ -111,6 +265,11 @@ var TheoApp = class _TheoApp {
111
265
  entry.needsBody = entry.walk.paramEntries.some((p) => p.source === "body");
112
266
  }
113
267
  if (opts.html) app.frontendHtml = opts.html;
268
+ if (opts.staticDir !== false) {
269
+ app.staticHandler = createStaticHandler({
270
+ root: opts.staticDir ?? "public"
271
+ });
272
+ }
114
273
  if (opts.agents?.length) {
115
274
  await app.autoWireAgents(opts.agents, registry, opts);
116
275
  }
@@ -138,14 +297,14 @@ var TheoApp = class _TheoApp {
138
297
  agentRoutes = [];
139
298
  async autoWireAgents(agentClasses, registry, opts) {
140
299
  const importFn = new Function("specifier", "return import(specifier)");
141
- let walkAgentMetadata, compileAgent, generateAgentRoutes, getMixins, createRealAgentStreamFn;
300
+ let walkAgentMetadata, compileAgent, generateAgentRoutes, getMixins, createSdkAgentStreamFn;
142
301
  try {
143
302
  const mod = await importFn("@theokit/agents");
144
303
  walkAgentMetadata = mod.walkAgentMetadata;
145
304
  compileAgent = mod.compileAgent;
146
305
  generateAgentRoutes = mod.generateAgentRoutes;
147
306
  getMixins = mod.getMixins;
148
- createRealAgentStreamFn = mod.createRealAgentStream;
307
+ createSdkAgentStreamFn = mod.createSdkAgentStream;
149
308
  } catch {
150
309
  throw new Error("[TheoApp] @theokit/agents is required when agents[] is provided. Install: npm install @theokit/agents");
151
310
  }
@@ -174,8 +333,8 @@ var TheoApp = class _TheoApp {
174
333
  let createRun;
175
334
  if (opts.agentStreamFactory) {
176
335
  createRun = opts.agentStreamFactory(walk, compiled.tools, apiKey, opts.llmModel);
177
- } else if (apiKey && createRealAgentStreamFn !== void 0) {
178
- createRun = createRealAgentStreamFn(walk, compiled.tools, apiKey, opts.llmModel);
336
+ } else if (apiKey) {
337
+ createRun = createSdkAgentStreamFn(walk, compiled.tools, apiKey, opts.llmModel);
179
338
  } else {
180
339
  createRun = this.createFallbackStream(walk.agentConfig.name, apiKey);
181
340
  }
@@ -291,6 +450,10 @@ var TheoApp = class _TheoApp {
291
450
  });
292
451
  }
293
452
  }
453
+ if (this.staticHandler && request.method === "GET") {
454
+ const staticResponse = await this.staticHandler(request);
455
+ if (staticResponse) return staticResponse;
456
+ }
294
457
  for (const route of this.agentRoutes) {
295
458
  if (request.method.toUpperCase() === route.method && route.pattern.test(pathname)) {
296
459
  if (route.guards.length > 0) {
@@ -472,6 +635,8 @@ function buildResponse(result, walk, method) {
472
635
  __name(buildResponse, "buildResponse");
473
636
 
474
637
  export {
638
+ getMimeType,
639
+ createStaticHandler,
475
640
  TheoApp
476
641
  };
477
- //# sourceMappingURL=chunk-TBMGRXH5.js.map
642
+ //# sourceMappingURL=chunk-2CI44FAT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app.ts","../src/static.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-regexp, complexity, sonarjs/cognitive-complexity, max-depth, sonarjs/no-collapsible-if */\nimport 'reflect-metadata'\n\nimport { createExecutionContext, type CanActivate } from './bridge/execution-context.js'\nimport { createNodeAdapter } from './bridge/runtime/node.js'\nimport type { ServerHandle } from './bridge/runtime/types.js'\nimport { walkControllerMetadata, type WalkResult } from './bridge/walk-metadata.js'\nimport type { ParamEntry } from './decorators/params.js'\nimport { ForbiddenException, HttpException } from './exceptions/http-exception.js'\nimport { createStaticHandler } from './static.js'\n\n/**\n * TheoApp — NestJS/Spring Boot-style application bootstrap.\n *\n * Internally uses Web Standard Request/Response pipeline.\n * Node adapter converts at the HTTP server boundary.\n */\n\n/** Readiness check — async function returning health status. */\nexport type ReadinessCheck = () => Promise<{ name: string; healthy: boolean; message?: string }>\n\nexport interface TheoAppOptions {\n /** Controller classes decorated with @Controller. */\n controllers: Function[]\n /** Agent classes decorated with @Agent — auto-wired with routes + SSE + tools. */\n agents?: Function[]\n /** @Module class for structured DI. */\n module?: Function\n /** Provider/toolbox classes — instantiated and injected into controllers + agents. */\n providers?: Function[]\n /** LLM API key for agent execution (reads OPENROUTER_API_KEY env if not set). */\n llmApiKey?: string\n /** LLM model override (default: from @Agent({ model }) metadata). */\n llmModel?: string\n /** Agent stream factory override (for testing or custom SDK wiring). */\n agentStreamFactory?: (\n walk: unknown,\n tools: unknown[],\n apiKey: string,\n model?: string,\n ) => (message: string, sessionId: string) => AsyncIterable<unknown>\n /** HTML string to serve at GET / (inline frontend). */\n html?: string\n /** Directory for static files (CSS, JS, images, fonts). Default: 'public'. Set false to disable. */\n staticDir?: string | false\n /** Readiness checks for GET /__theo/ready (K8s readiness probe). */\n readinessChecks?: ReadinessCheck[]\n /** Custom health endpoint path (default: '/__theo/health'). */\n healthPath?: string\n /** Custom readiness endpoint path (default: '/__theo/ready'). */\n readyPath?: string\n}\n\ninterface RouteEntry {\n walk: WalkResult\n instance: object\n /** Pre-compiled regex + param names (Elysia-inspired: compile once at create, not per-request). */\n compiledPattern?: RegExp\n compiledParamNames?: string[]\n /** Whether handler needs body parsing (skip for GET/DELETE without @Body). */\n needsBody?: boolean\n}\n\nexport class TheoApp {\n private serverHandle: ServerHandle\n private readonly routes: RouteEntry[] = []\n private frontendHtml?: string\n private staticHandler?: (request: Request) => Promise<Response | null>\n private readonly startTime = Date.now()\n private readonly healthPath: string\n private readonly readyPath: string\n private readonly readinessChecks: ReadinessCheck[]\n\n private constructor(opts?: Pick<TheoAppOptions, 'readinessChecks' | 'healthPath' | 'readyPath'>) {\n const hp = opts?.healthPath ?? '/__theo/health'\n const rp = opts?.readyPath ?? '/__theo/ready'\n // Security: prevent health paths from colliding with user API routes\n if (hp.startsWith('/api/'))\n throw new Error(\n `[TheoApp] healthPath \"${hp}\" must not start with /api/ — would collide with user routes`,\n )\n if (rp.startsWith('/api/'))\n throw new Error(\n `[TheoApp] readyPath \"${rp}\" must not start with /api/ — would collide with user routes`,\n )\n this.healthPath = hp\n this.readyPath = rp\n this.readinessChecks = opts?.readinessChecks ?? []\n const adapter = createNodeAdapter()\n this.serverHandle = adapter.createServer((request) => this.handleRequest(request))\n }\n\n /**\n * Create a TheoKit application with Spring Boot-style DI.\n *\n * EC-7: async because Container may resolve async factories (Agent.create).\n * EC-2: Registration order guaranteed: providers → controllers → agents.\n */\n static async create(opts: TheoAppOptions): Promise<TheoApp> {\n const app = new TheoApp(opts)\n\n // ── DI Container (replaces manual Map<Function, object>) ──\n // Uses @theokit/di Container for: scopes, lifecycle, async, typed errors.\n // Fallback to manual resolution when @theokit/di is not available.\n const registry = new Map<Function, object>()\n\n // 1. Module resolution (@Module metadata)\n if (opts.module) {\n const moduleMeta = Reflect.getMetadata('usetheo:di:module', opts.module)\n if (moduleMeta) {\n const allModules = [opts.module, ...(moduleMeta.imports ?? [])]\n for (const Mod of allModules) {\n const meta = Reflect.getMetadata('usetheo:di:module', Mod)\n if (!meta) continue\n for (const Prov of (meta.providers ?? []) as Function[]) {\n if (!registry.has(Prov)) {\n registry.set(Prov, new (Prov as new () => object)())\n }\n }\n }\n }\n }\n\n // 2. Inline providers (EC-2: BEFORE controllers and agents)\n if (opts.providers) {\n for (const Prov of opts.providers) {\n if (!registry.has(Prov)) {\n registry.set(Prov, new (Prov as new () => object)())\n }\n }\n }\n\n // 3. Controllers with DI\n for (const Controller of opts.controllers) {\n const paramTypes: Function[] = Reflect.getMetadata('design:paramtypes', Controller) ?? []\n const args = paramTypes.map((pt: Function) => {\n const dep = registry.get(pt)\n if (!dep) {\n throw new Error(\n `[TheoApp] Cannot resolve ${pt.name} for ${Controller.name}. ` +\n `Add ${pt.name} to providers (or to @Module({ providers: [...] })).`,\n )\n }\n return dep\n })\n const instance = new (Controller as new (...a: unknown[]) => object)(...args)\n\n // Lifecycle: call @PostConstruct if present\n const postConstruct = Reflect.getMetadata('usetheo:di:post-construct', Controller)\n if (\n postConstruct &&\n typeof (instance as Record<string, Function>)[postConstruct] === 'function'\n ) {\n const result = (instance as Record<string, Function>)[postConstruct]()\n if (result instanceof Promise) await result // EC-1: await async PostConstruct\n }\n\n const walks = walkControllerMetadata(Controller)\n for (const w of walks) {\n app.routes.push({ walk: w, instance })\n }\n }\n\n // Sort: static before parameterized\n app.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 // Pre-compile route patterns (Elysia-inspired: regex built once, not per-request)\n for (const entry of app.routes) {\n const paramNames: string[] = []\n const regexStr = entry.walk.fullPath.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n entry.compiledPattern = new RegExp(`^${regexStr}$`)\n entry.compiledParamNames = paramNames\n // Determine if handler needs body parsing (skip if no @Body decorator)\n entry.needsBody = entry.walk.paramEntries.some((p) => p.source === 'body')\n }\n\n // Store frontend HTML\n if (opts.html) app.frontendHtml = opts.html\n\n // Static file serving from public/ directory\n if (opts.staticDir !== false) {\n app.staticHandler = createStaticHandler({ root: opts.staticDir ?? 'public' })\n }\n\n // 4. Auto-wire agents (EC-2: AFTER providers and controllers)\n // The framework handles EVERYTHING — walk, compile, mount, stream.\n // Consumer writes: agents: [MyAgent] — and that's it.\n if (opts.agents?.length) {\n await app.autoWireAgents(opts.agents, registry, opts)\n }\n\n return app\n }\n\n async listen(port: number): Promise<void> {\n return new Promise((resolve) => {\n this.serverHandle.listen(port, () => {\n console.log(`TheoKit app listening on http://localhost:${port}`)\n resolve()\n })\n })\n }\n\n getServerHandle(): ServerHandle {\n return this.serverHandle\n }\n\n async close(): Promise<void> {\n return new Promise((resolve) => {\n this.serverHandle.close(() => {\n resolve()\n })\n })\n }\n\n // ── Auto-wire agents (the \"SpringApplication.run()\" moment) ──\n\n private agentRoutes: {\n method: string\n pattern: RegExp\n paramNames: string[]\n handler: (request: Request) => Promise<Response>\n guards: Function[]\n agentClass: Function\n methodName: string | symbol\n }[] = []\n\n private async autoWireAgents(\n agentClasses: Function[],\n registry: Map<Function, object>,\n opts: TheoAppOptions,\n ) {\n // Dynamic import — @theokit/agents is optional peer dependency\n // SECURITY: new Function used for dynamic import of optional peer dep.\n // Argument is HARDCODED ('@theokit/agents'), never user input. Safe.\n // eslint-disable-next-line @typescript-eslint/no-implied-eval -- dynamic import avoids compile-time dependency on optional peer\n const importFn = new Function('specifier', 'return import(specifier)') as (\n s: string,\n ) => Promise<Record<string, Function>>\n let walkAgentMetadata: Function,\n compileAgent: Function,\n generateAgentRoutes: Function,\n getMixins: Function,\n createSdkAgentStreamFn: Function\n try {\n const mod = await importFn('@theokit/agents')\n walkAgentMetadata = mod.walkAgentMetadata\n compileAgent = mod.compileAgent\n generateAgentRoutes = mod.generateAgentRoutes\n getMixins = mod.getMixins\n createSdkAgentStreamFn = mod.createSdkAgentStream\n } catch {\n throw new Error(\n '[TheoApp] @theokit/agents is required when agents[] is provided. Install: npm install @theokit/agents',\n )\n }\n\n const apiKey = opts.llmApiKey ?? process.env.OPENROUTER_API_KEY ?? ''\n\n for (const AgentClass of agentClasses) {\n // 1. Walk metadata (decorators → structured data)\n const mixins = getMixins(AgentClass)\n const allToolboxes = [...mixins]\n\n // Also check providers for @Toolbox classes\n for (const [Cls] of registry) {\n if (Reflect.getMetadata(Symbol.for('theokit:agents:toolbox'), Cls)) {\n if (!allToolboxes.includes(Cls)) allToolboxes.push(Cls)\n }\n }\n\n const walk = walkAgentMetadata(AgentClass, allToolboxes)\n\n // 2. Resolve toolbox instances from registry (DI)\n const toolboxInstances = new Map<Function, object>()\n for (const tb of walk.toolboxes) {\n let instance = registry.get(tb.class)\n if (!instance) {\n instance = new (tb.class as new () => object)()\n registry.set(tb.class, instance)\n }\n toolboxInstances.set(tb.class, instance)\n }\n\n // 3. Compile tools (decorator metadata → defineTool-compatible)\n const compiled = compileAgent(walk, toolboxInstances)\n\n // 4. Create SDK agent stream factory\n let createRun: (message: string, sessionId: string) => AsyncIterable<unknown>\n\n if (opts.agentStreamFactory) {\n createRun = opts.agentStreamFactory(walk, compiled.tools, apiKey, opts.llmModel)\n } else if (apiKey) {\n // SDK adapter: bridges @theokit/agents decorators → @theokit/sdk runtime\n createRun = createSdkAgentStreamFn(walk, compiled.tools, apiKey, opts.llmModel) as (\n m: string,\n s: string,\n ) => AsyncIterable<unknown>\n } else {\n createRun = this.createFallbackStream(walk.agentConfig.name, apiKey)\n }\n\n // 5. Generate routes (POST /chat, GET /runs/:id)\n const routes = generateAgentRoutes({\n walkResult: walk,\n compiledOptions: compiled,\n createRun: createRun as (\n message: string,\n sessionId: string,\n ) => AsyncIterable<{ type: string; [k: string]: unknown }>,\n })\n\n // 6. Mount routes\n for (const route of routes) {\n const paramNames: string[] = []\n const regexStr = route.path.replace(/:(\\w+)/g, (_m: string, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n this.agentRoutes.push({\n method: route.method,\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n handler: route.handler,\n guards: walk.guards ?? [],\n agentClass: AgentClass,\n methodName: walk.mainLoop?.propertyKey ?? 'run',\n })\n }\n\n console.log(\n ` 🤖 Agent \"${walk.agentConfig.name}\" mounted at ${walk.route}/chat (${compiled.tools.length} tools)`,\n )\n }\n }\n\n private async handleReadinessCheck(): Promise<Response> {\n if (this.readinessChecks.length === 0) {\n return jsonResponse(200, { status: 'ready', checks: [] })\n }\n const results = await Promise.all(\n this.readinessChecks.map(async (check) => {\n try {\n return await Promise.race([\n check(),\n new Promise<{ name: string; healthy: boolean; message?: string }>((resolve) =>\n setTimeout(\n () =>\n resolve({\n name: 'unknown',\n healthy: false,\n message: 'Readiness check timed out (5s)',\n }),\n 5000,\n ),\n ),\n ])\n } catch (err) {\n return {\n name: 'unknown',\n healthy: false,\n message: err instanceof Error ? err.message : 'Check failed',\n }\n }\n }),\n )\n const allHealthy = results.every((r) => r.healthy)\n return jsonResponse(allHealthy ? 200 : 503, {\n status: allHealthy ? 'ready' : 'not_ready',\n checks: results,\n })\n }\n\n private createFallbackStream(agentName: string, apiKey: string) {\n const msg = !apiKey\n ? 'Set OPENROUTER_API_KEY environment variable or pass llmApiKey to TheoApp.create()'\n : 'Pass agentStreamFactory to TheoApp.create() to connect your LLM provider'\n return (_message: string, _sessionId: string) => {\n const events = [\n { type: 'run_started', runId: `run-${Date.now()}`, agentName },\n { type: 'error', code: 'AGENT_NOT_WIRED', message: msg, retryable: false },\n ]\n return {\n [Symbol.asyncIterator]: () => {\n let i = 0\n return {\n next: () =>\n Promise.resolve(\n i < events.length\n ? { value: events[i++], done: false as const }\n : { value: undefined, done: true as const },\n ),\n }\n },\n }\n }\n }\n\n // ── Web Standard request handler ──────────────────\n\n private async handleRequest(request: Request): Promise<Response> {\n const pathname = new URL(request.url).pathname\n\n // ── Health & Readiness probes (bypass guards/interceptors) ──\n if (request.method === 'GET') {\n if (pathname === this.healthPath) {\n return jsonResponse(200, {\n status: 'ok',\n uptime: (Date.now() - this.startTime) / 1000,\n timestamp: Date.now(),\n })\n }\n if (pathname === this.readyPath) {\n return this.handleReadinessCheck()\n }\n }\n\n // Bug #8: Serve frontend HTML at GET /\n if (request.method === 'GET') {\n if ((pathname === '/' || pathname === '/index.html') && this.frontendHtml) {\n return new Response(this.frontendHtml, {\n status: 200,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n })\n }\n }\n\n // Static files from public/ (CSS, JS, images, fonts)\n if (this.staticHandler && request.method === 'GET') {\n const staticResponse = await this.staticHandler(request)\n if (staticResponse) return staticResponse\n }\n\n // Agent routes (auto-wired — checked first, with guard enforcement per Bug #4)\n for (const route of this.agentRoutes) {\n if (request.method.toUpperCase() === route.method && route.pattern.test(pathname)) {\n // Bug #4 fix: enforce @UseGuards on agent routes (same pipeline as controllers)\n if (route.guards.length > 0) {\n const ctx = createExecutionContext(request, route.agentClass, route.methodName)\n for (const GuardCtor of route.guards) {\n const guard = new (GuardCtor as new () => CanActivate)()\n if (!(await guard.canActivate(ctx))) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n }\n return route.handler(request)\n }\n }\n\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n\n const match = this.findRoute(method, url.pathname)\n if (!match) {\n return jsonResponse(404, {\n error: { code: 'NOT_FOUND', message: `No route for ${method} ${url.pathname}` },\n })\n }\n\n const { entry, params } = match\n\n try {\n // Guards\n const ctx = createExecutionContext(\n request,\n entry.instance.constructor,\n entry.walk.propertyKey,\n )\n for (const GuardCtor of entry.walk.guards) {\n const guard = new (GuardCtor as new () => CanActivate)()\n if (!(await guard.canActivate(ctx))) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n\n // Body — skip parsing entirely if handler has no @Body decorator (Elysia-inspired lazy parsing)\n let body: unknown\n if (entry.needsBody && ['POST', 'PUT', 'PATCH'].includes(method)) {\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch {\n body = undefined\n }\n\n if (entry.walk.bodySchema && body !== undefined) {\n const result = entry.walk.bodySchema.safeParse(body)\n if (!result.success) {\n return jsonResponse(422, {\n error: { code: 'VALIDATION_ERROR', issues: result.error.issues },\n })\n }\n body = result.data\n }\n }\n\n // Args\n const args = this.buildArgs(\n entry.walk.paramEntries,\n request,\n body,\n params,\n Object.fromEntries(url.searchParams),\n )\n\n // Redirect\n if (entry.walk.redirect) {\n return new Response(null, {\n status: entry.walk.redirect.status,\n headers: { location: entry.walk.redirect.url },\n })\n }\n\n // Handler — wrapped by interceptor chain (Bug #2 fix)\n const handlerFn = (entry.instance as Record<string | symbol, Function>)[\n entry.walk.propertyKey\n ]\n\n let result: unknown\n if (entry.walk.interceptors.length > 0) {\n // Import runInterceptors dynamically to avoid circular dep\n const { runInterceptors } = await import('./bridge/interceptor-chain.js')\n result = await runInterceptors(\n entry.walk.interceptors,\n () => handlerFn.apply(entry.instance, args) as Promise<unknown>,\n request,\n )\n } else {\n result = await handlerFn.apply(entry.instance, args)\n }\n\n return buildResponse(result, entry.walk, method)\n } catch (err) {\n // Exception filters (Bug #2 fix — filters also wired)\n if (entry.walk.filters.length > 0) {\n const { runExceptionFilters } = await import('./bridge/exception-filter-chain.js')\n return runExceptionFilters(err, entry.walk.filters, request)\n }\n // Fallback: HttpException subclasses (Bug #3 fix)\n if (err instanceof HttpException) {\n return jsonResponse(err.statusCode, err.toJSON())\n }\n // Security: never leak raw error messages to clients\n console.error('[theokit] Internal error:', err instanceof Error ? err.message : err)\n return jsonResponse(500, {\n error: { code: 'INTERNAL_SERVER_ERROR', message: 'Internal Server Error' },\n })\n }\n }\n\n private findRoute(method: string, pathname: string) {\n for (const entry of this.routes) {\n if (entry.walk.verb !== 'ALL' && entry.walk.verb !== method) continue\n // Use pre-compiled regex (built once at create time, not per-request)\n const match = entry.compiledPattern!.exec(pathname)\n if (!match) continue\n const params: Record<string, string> = {}\n entry.compiledParamNames!.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return { entry, params }\n }\n return null\n }\n\n private buildArgs(\n entries: ParamEntry[],\n request: Request,\n body: unknown,\n params: Record<string, string>,\n query: Record<string, string>,\n ): unknown[] {\n if (entries.length === 0) return []\n const max = Math.max(...entries.map((p) => p.index))\n const args: unknown[] = Array.from({ length: max + 1 }, () => undefined)\n for (const p of entries) {\n switch (p.source) {\n case 'req':\n args[p.index] = request\n break\n case 'body':\n args[p.index] = p.key ? (body as Record<string, unknown>)[p.key] : body\n break\n case 'param':\n args[p.index] = p.key ? params[p.key] : params\n break\n case 'query':\n args[p.index] = p.key ? query[p.key] : query\n break\n case 'headers':\n args[p.index] = p.key\n ? request.headers.get(p.key.toLowerCase())\n : Object.fromEntries(request.headers.entries())\n break\n case 'ip':\n args[p.index] = request.headers.get('x-forwarded-for') ?? '127.0.0.1'\n break\n default:\n args[p.index] = undefined\n }\n }\n return args\n }\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\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 [n, v] of walk.headers) headers[n.toLowerCase()] = v\n\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\n }\n return new Response(JSON.stringify(result), { status, headers })\n}\n","/**\n * Static file serving — runtime-agnostic (Node/Bun/Deno).\n *\n * Serves files from a root directory with:\n * - MIME type detection from extension\n * - Path traversal prevention (regex from Hono)\n * - Directory → index.html fallback\n * - Web Standard Response (no node:http)\n */\n\n// ── MIME types (subset from Hono — covers web essentials) ──\n\nconst MIME_TYPES: Record<string, string> = {\n // Text\n css: 'text/css; charset=utf-8',\n html: 'text/html; charset=utf-8',\n htm: 'text/html; charset=utf-8',\n js: 'text/javascript; charset=utf-8',\n mjs: 'text/javascript; charset=utf-8',\n json: 'application/json; charset=utf-8',\n txt: 'text/plain; charset=utf-8',\n xml: 'application/xml; charset=utf-8',\n csv: 'text/csv; charset=utf-8',\n md: 'text/markdown; charset=utf-8',\n\n // Images\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n svg: 'image/svg+xml; charset=utf-8',\n webp: 'image/webp',\n avif: 'image/avif',\n ico: 'image/x-icon',\n bmp: 'image/bmp',\n\n // Fonts\n woff: 'font/woff',\n woff2: 'font/woff2',\n ttf: 'font/ttf',\n otf: 'font/otf',\n eot: 'application/vnd.ms-fontobject',\n\n // Media\n mp3: 'audio/mpeg',\n mp4: 'video/mp4',\n webm: 'video/webm',\n ogg: 'audio/ogg',\n wav: 'audio/wav',\n\n // Documents\n pdf: 'application/pdf',\n zip: 'application/zip',\n gz: 'application/gzip',\n wasm: 'application/wasm',\n\n // Manifest\n webmanifest: 'application/manifest+json',\n map: 'application/json',\n}\n\n/** Detect MIME type from file extension. */\nexport function getMimeType(filename: string): string {\n const match = filename.match(/\\.([a-zA-Z0-9]+)$/)\n if (!match) return 'application/octet-stream'\n return MIME_TYPES[match[1].toLowerCase()] ?? 'application/octet-stream'\n}\n\n/**\n * Path traversal prevention — regex from Hono serve-static.\n * Blocks: ../, \\..\\, //, \\\\, and any directory escape attempt.\n */\nconst TRAVERSAL_REGEX = /(?:^|[/\\\\])\\.{1,2}(?:$|[/\\\\])|[/\\\\]{2,}/\n\n/** Check if a path is safe (no traversal attacks). */\nexport function isSafePath(path: string): boolean {\n return !TRAVERSAL_REGEX.test(path)\n}\n\n// ── Runtime-agnostic file reading ──\n\ninterface FileResult {\n content: Uint8Array\n isDirectory: boolean\n}\n\nasync function readFileUniversal(path: string): Promise<FileResult | null> {\n const g = globalThis as Record<string, unknown>\n\n // Bun\n if (g.Bun) {\n try {\n const BunRT = g.Bun as { file: (p: string) => { exists: () => Promise<boolean>; arrayBuffer: () => Promise<ArrayBuffer> } }\n const file = BunRT.file(path)\n if (await file.exists()) {\n const buf = await file.arrayBuffer()\n return { content: new Uint8Array(buf), isDirectory: false }\n }\n return null\n } catch {\n return null\n }\n }\n\n // Deno\n if (g.Deno) {\n try {\n const DenoRT = g.Deno as { stat: (p: string) => Promise<{ isDirectory: boolean }>; readFile: (p: string) => Promise<Uint8Array> }\n const stat = await DenoRT.stat(path)\n if (stat.isDirectory) return { content: new Uint8Array(), isDirectory: true }\n const content = await DenoRT.readFile(path)\n return { content, isDirectory: false }\n } catch {\n return null\n }\n }\n\n // Node.js\n try {\n const fs = await import('node:fs')\n const stat = fs.statSync(path)\n if (stat.isDirectory()) return { content: new Uint8Array(), isDirectory: true }\n const buf = fs.readFileSync(path)\n return { content: new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength), isDirectory: false }\n } catch {\n return null\n }\n}\n\n// ── Static file middleware ──\n\nexport interface StaticOptions {\n /** Root directory for static files. Default: 'public' */\n root?: string\n /** Custom MIME types (merged with defaults). */\n mimes?: Record<string, string>\n /** Max-age for Cache-Control header in seconds. Default: 0 (no cache in dev). */\n maxAge?: number\n}\n\n/**\n * Create a static file handler.\n *\n * Returns a function that takes a Request and returns a Response if the\n * file exists in the root directory, or null if not found.\n *\n * Usage in TheoApp:\n * ```ts\n * const servePublic = createStaticHandler({ root: 'public' })\n * // In request handler: const res = await servePublic(request)\n * ```\n */\nexport function createStaticHandler(options: StaticOptions = {}) {\n const root = options.root ?? 'public'\n const maxAge = options.maxAge ?? 0\n const customMimes = options.mimes ?? {}\n\n return async (request: Request): Promise<Response | null> => {\n // Only serve GET/HEAD\n if (request.method !== 'GET' && request.method !== 'HEAD') return null\n\n const url = new URL(request.url)\n let pathname = url.pathname\n\n // Decode URI\n try {\n pathname = decodeURIComponent(pathname)\n } catch {\n return null\n }\n\n // Security: path traversal check\n if (!isSafePath(pathname)) return null\n\n // Skip API routes and known framework paths\n if (pathname.startsWith('/api/') || pathname.startsWith('/@theo/')) return null\n\n // Build file path: root + pathname\n const filePath = (root + pathname).replace(/\\/+/g, '/')\n\n // Try to read file\n let result = await readFileUniversal(filePath)\n\n // Directory → try index.html\n if (result?.isDirectory) {\n const indexPath = (filePath + '/index.html').replace(/\\/+/g, '/')\n result = await readFileUniversal(indexPath)\n }\n\n if (!result || result.isDirectory) return null\n\n // Detect MIME type\n const mimeType = customMimes[pathname.split('.').pop()?.toLowerCase() ?? '']\n ?? getMimeType(pathname)\n\n // Build response headers\n const headers: Record<string, string> = {\n 'Content-Type': mimeType,\n 'Content-Length': String(result.content.byteLength),\n }\n\n if (maxAge > 0) {\n headers['Cache-Control'] = `public, max-age=${maxAge}`\n }\n\n // HEAD: headers only\n if (request.method === 'HEAD') {\n return new Response(null, { status: 200, headers })\n }\n\n return new Response(result.content as unknown as BodyInit, { status: 200, headers })\n }\n}\n"],"mappings":"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","names":["MIME_TYPES","css","html","htm","js","mjs","json","txt","xml","csv","md","png","jpg","jpeg","gif","svg","webp","avif","ico","bmp","woff","woff2","ttf","otf","eot","mp3","mp4","webm","ogg","wav","pdf","zip","gz","wasm","webmanifest","map","getMimeType","filename","match","toLowerCase","TRAVERSAL_REGEX","isSafePath","path","test","readFileUniversal","g","globalThis","Bun","BunRT","file","exists","buf","arrayBuffer","content","Uint8Array","isDirectory","Deno","DenoRT","stat","readFile","fs","statSync","readFileSync","buffer","byteOffset","byteLength","createStaticHandler","options","root","maxAge","customMimes","mimes","request","method","url","URL","pathname","decodeURIComponent","startsWith","filePath","replace","result","indexPath","mimeType","split","pop","headers","String","Response","status","TheoApp","serverHandle","routes","frontendHtml","staticHandler","startTime","Date","now","healthPath","readyPath","readinessChecks","opts","hp","rp","startsWith","Error","adapter","createNodeAdapter","createServer","request","handleRequest","create","app","registry","Map","module","moduleMeta","Reflect","getMetadata","allModules","imports","Mod","meta","Prov","providers","has","set","Controller","controllers","paramTypes","args","map","pt","dep","get","name","instance","postConstruct","result","Promise","walks","walkControllerMetadata","w","push","walk","sort","a","b","aP","fullPath","includes","bP","entry","paramNames","regexStr","replace","_m","compiledPattern","RegExp","compiledParamNames","needsBody","paramEntries","some","p","source","html","staticDir","createStaticHandler","root","agents","length","autoWireAgents","listen","port","resolve","console","log","getServerHandle","close","agentRoutes","agentClasses","importFn","Function","walkAgentMetadata","compileAgent","generateAgentRoutes","getMixins","createSdkAgentStreamFn","mod","createSdkAgentStream","apiKey","llmApiKey","process","env","OPENROUTER_API_KEY","AgentClass","mixins","allToolboxes","Cls","Symbol","for","toolboxInstances","tb","toolboxes","class","compiled","createRun","agentStreamFactory","tools","llmModel","createFallbackStream","agentConfig","walkResult","compiledOptions","route","path","method","pattern","handler","guards","agentClass","methodName","mainLoop","propertyKey","handleReadinessCheck","jsonResponse","status","checks","results","all","check","race","setTimeout","healthy","message","err","allHealthy","every","r","agentName","msg","_message","_sessionId","events","type","runId","code","retryable","asyncIterator","i","next","value","done","undefined","pathname","URL","url","uptime","timestamp","Response","headers","staticResponse","toUpperCase","test","ctx","createExecutionContext","GuardCtor","guard","canActivate","ex","ForbiddenException","statusCode","toJSON","match","findRoute","error","params","body","text","JSON","parse","bodySchema","safeParse","success","issues","data","buildArgs","Object","fromEntries","searchParams","redirect","location","handlerFn","interceptors","runInterceptors","apply","buildResponse","filters","runExceptionFilters","HttpException","verb","exec","forEach","entries","query","max","Math","index","Array","from","key","toLowerCase","stringify","n","v"]}
@@ -122,4 +122,4 @@ export {
122
122
  joinPath,
123
123
  walkControllerMetadata
124
124
  };
125
- //# sourceMappingURL=chunk-SMWUPP2C.js.map
125
+ //# sourceMappingURL=chunk-QGB5YC4T.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 { 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"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ZodTypeAny, z } from 'zod';
1
+ import { z } from 'zod';
2
2
  import { D as DiContainer, M as MiddlewareConsumerImpl } from './middleware-consumer-ljxK1fU_.js';
3
3
  export { a as MiddlewareConfigProxy, b as MiddlewareFn, N as NestMiddleware, R as ResolvedMiddleware, m as middlewareMatchesPath, r as resolveOrNew, c as runMiddleware } from './middleware-consumer-ljxK1fU_.js';
4
4
  import { S as ServerHandle } from './types-CGthbcon.js';
@@ -13,9 +13,24 @@ interface ControllerMeta {
13
13
  }
14
14
  /**
15
15
  * Class decorator that declares a route-prefix scope.
16
- * Equivalent to NestJS's @Controller('prefix').
16
+ *
17
+ * Convention over configuration:
18
+ * @Controller() → prefix inferred from class name
19
+ * @Controller('api/users') → explicit prefix
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * // Convention: UsersController → /api/users (zero config)
24
+ * @Controller()
25
+ * class UsersController { ... }
26
+ *
27
+ * // Explicit: override when convention doesn't fit
28
+ * @Controller('api/v2/users')
29
+ * class UsersController { ... }
30
+ * ```
17
31
  */
18
32
  declare function Controller(prefix?: string, opts?: ControllerOptions): ClassDecorator;
33
+ declare function Controller(opts?: ControllerOptions): ClassDecorator;
19
34
 
20
35
  type HttpVerb = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'ALL';
21
36
  interface RouteMethodEntry {
@@ -40,16 +55,16 @@ interface ParamEntry {
40
55
  passthrough?: boolean;
41
56
  /** Explicit Zod schema — when set, bypasses design:paramtypes + DTO resolution.
42
57
  * Preferred path: `@Body(zMySchema)` works without emitDecoratorMetadata. */
43
- schema?: ZodTypeAny;
58
+ schema?: z.ZodType;
44
59
  }
45
- declare const Req: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
46
- declare const Body: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
47
- declare const Param: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
48
- declare const Query: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
49
- declare const Headers: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
50
- declare const Session: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
51
- declare const Ip: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
52
- declare const HostParam: (keyOrSchema?: string | ZodTypeAny) => ParameterDecorator;
60
+ declare const Req: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
61
+ declare const Body: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
62
+ declare const Param: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
63
+ declare const Query: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
64
+ declare const Headers: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
65
+ declare const Session: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
66
+ declare const Ip: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
67
+ declare const HostParam: (keyOrSchema?: string | z.ZodType) => ParameterDecorator;
53
68
  declare function Res(opts?: {
54
69
  passthrough?: boolean;
55
70
  }): ParameterDecorator;
@@ -277,7 +292,7 @@ declare function runExceptionFilters(exception: unknown, filters: Function[], re
277
292
  * Resolves a Zod schema from a DTO class via the `static schema` convention (Pattern D2).
278
293
  * Returns undefined when the class doesn't carry a compatible schema.
279
294
  */
280
- declare function resolveDtoSchema(dtoClass: unknown): ZodTypeAny | undefined;
295
+ declare function resolveDtoSchema(dtoClass: unknown): z.ZodType | undefined;
281
296
 
282
297
  /**
283
298
  * Configuration error thrown by the bridge when decorator setup is incomplete.
@@ -317,9 +332,9 @@ interface WalkResult {
317
332
  verb: HttpVerb;
318
333
  fullPath: string;
319
334
  propertyKey: string | symbol;
320
- bodySchema?: ZodTypeAny;
321
- querySchema?: ZodTypeAny;
322
- paramsSchema?: ZodTypeAny;
335
+ bodySchema?: z.ZodType;
336
+ querySchema?: z.ZodType;
337
+ paramsSchema?: z.ZodType;
323
338
  paramEntries: ParamEntry[];
324
339
  status?: number;
325
340
  headers: [string, string][];
@@ -1044,4 +1059,37 @@ declare class TypedClientError extends Error {
1044
1059
  */
1045
1060
  declare function contract<T extends RouteMap>(routes: T): T;
1046
1061
 
1047
- export { All, type ArgumentsHost, BadGatewayException, BadRequestException, Body, CATCH_EXCEPTIONS, CONTROLLER_PREFIX, type CanActivate, Catch, ConflictException, Controller, type ControllerMeta, type ControllerOptions, Delete, DiContainer, 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 RouteDefinition, type RouteMap, type RouteMethodEntry, type RouteRegistration, ServiceUnavailableException, Session, SetMetadata, SkipThrottle, Throttle, type ThrottleOptions, TooManyRequestsException, type TypedClient, TypedClientError, USE_FILTERS, USE_GUARDS, USE_INTERCEPTORS, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UseFilters, UseGuards, UseInterceptors, type WalkResult, contract, createDecorator, createDecoratorServer, createExecutionContext, createTypedClient, getMeta, getThrottleOptions, isThrottleSkipped, joinPath, registerControllers, resolveDtoSchema, runExceptionFilters, runInterceptors, setMeta, walkControllerMetadata };
1062
+ /**
1063
+ * Static file serving — runtime-agnostic (Node/Bun/Deno).
1064
+ *
1065
+ * Serves files from a root directory with:
1066
+ * - MIME type detection from extension
1067
+ * - Path traversal prevention (regex from Hono)
1068
+ * - Directory → index.html fallback
1069
+ * - Web Standard Response (no node:http)
1070
+ */
1071
+ /** Detect MIME type from file extension. */
1072
+ declare function getMimeType(filename: string): string;
1073
+ interface StaticOptions {
1074
+ /** Root directory for static files. Default: 'public' */
1075
+ root?: string;
1076
+ /** Custom MIME types (merged with defaults). */
1077
+ mimes?: Record<string, string>;
1078
+ /** Max-age for Cache-Control header in seconds. Default: 0 (no cache in dev). */
1079
+ maxAge?: number;
1080
+ }
1081
+ /**
1082
+ * Create a static file handler.
1083
+ *
1084
+ * Returns a function that takes a Request and returns a Response if the
1085
+ * file exists in the root directory, or null if not found.
1086
+ *
1087
+ * Usage in TheoApp:
1088
+ * ```ts
1089
+ * const servePublic = createStaticHandler({ root: 'public' })
1090
+ * // In request handler: const res = await servePublic(request)
1091
+ * ```
1092
+ */
1093
+ declare function createStaticHandler(options?: StaticOptions): (request: Request) => Promise<Response | null>;
1094
+
1095
+ export { All, type ArgumentsHost, BadGatewayException, BadRequestException, Body, CATCH_EXCEPTIONS, CONTROLLER_PREFIX, type CanActivate, Catch, ConflictException, Controller, type ControllerMeta, type ControllerOptions, Delete, DiContainer, 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 RouteDefinition, type RouteMap, type RouteMethodEntry, type RouteRegistration, ServiceUnavailableException, Session, SetMetadata, SkipThrottle, type StaticOptions, Throttle, type ThrottleOptions, TooManyRequestsException, type TypedClient, TypedClientError, USE_FILTERS, USE_GUARDS, USE_INTERCEPTORS, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, UseFilters, UseGuards, UseInterceptors, type WalkResult, contract, createDecorator, createDecoratorServer, createExecutionContext, createStaticHandler, createTypedClient, getMeta, getMimeType, getThrottleOptions, isThrottleSkipped, joinPath, registerControllers, resolveDtoSchema, runExceptionFilters, runInterceptors, setMeta, walkControllerMetadata };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
- TheoApp
3
- } from "./chunk-TBMGRXH5.js";
2
+ TheoApp,
3
+ createStaticHandler,
4
+ getMimeType
5
+ } from "./chunk-2CI44FAT.js";
4
6
  import {
5
7
  MiddlewareConsumerImpl,
6
8
  middlewareMatchesPath,
@@ -12,7 +14,7 @@ import {
12
14
  joinPath,
13
15
  resolveDtoSchema,
14
16
  walkControllerMetadata
15
- } from "./chunk-SMWUPP2C.js";
17
+ } from "./chunk-QGB5YC4T.js";
16
18
  import {
17
19
  createNodeAdapter
18
20
  } from "./chunk-HLW7YKZE.js";
@@ -67,8 +69,23 @@ import {
67
69
  } from "./chunk-7QVYU63E.js";
68
70
 
69
71
  // src/decorators/controller.ts
70
- function Controller(prefix = "", opts = {}) {
72
+ function inferPrefix(className) {
73
+ const stripped = className.replace(/Controller$/, "");
74
+ const kebab = stripped.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
75
+ return `api/${kebab}`;
76
+ }
77
+ __name(inferPrefix, "inferPrefix");
78
+ function Controller(prefixOrOpts, maybeOpts) {
71
79
  return (target) => {
80
+ let prefix;
81
+ let opts;
82
+ if (typeof prefixOrOpts === "string") {
83
+ prefix = prefixOrOpts;
84
+ opts = maybeOpts ?? {};
85
+ } else {
86
+ prefix = inferPrefix(target.name);
87
+ opts = prefixOrOpts ?? {};
88
+ }
72
89
  setMeta(CONTROLLER_PREFIX, target, {
73
90
  prefix,
74
91
  host: opts.host
@@ -743,8 +760,10 @@ export {
743
760
  createDecorator,
744
761
  createDecoratorServer,
745
762
  createExecutionContext,
763
+ createStaticHandler,
746
764
  createTypedClient,
747
765
  getMeta,
766
+ getMimeType,
748
767
  getThrottleOptions,
749
768
  isThrottleSkipped,
750
769
  joinPath,
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"],"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 * Class decorator that declares a route-prefix scope.\n * Equivalent to NestJS's @Controller('prefix').\n */\nexport function Controller(prefix = '', opts: ControllerOptions = {}): ClassDecorator {\n return (target) => {\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 { ZodTypeAny } 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?: ZodTypeAny\n}\n\n/** Detect whether a value is a Zod schema (has `.safeParse()` method). */\nfunction isZodSchema(v: unknown): v is ZodTypeAny {\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 | ZodTypeAny): 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'\nimport { runExceptionFilters } from './exception-filter-chain.js'\nimport { createExecutionContext, type CanActivate, type ExecutionContext } 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\nexport function createDecoratorServer(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n) {\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 // Create Web Standard handler + Node adapter\n const adapter = createNodeAdapter()\n const handler = (request: Request) => handleRequest(routes, request, container, middlewareEntries)\n return adapter.createServer(handler)\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> {\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 if (!match) {\n return jsonResponse(404, { error: { code: 'NOT_FOUND', message: `No route for ${method} ${pathname}` } })\n }\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, { request, body, params, query: Object.fromEntries(url.searchParams) })\n\n // Redirect\n if (walk.redirect) {\n return new Response(null, { status: walk.redirect.status, headers: { location: walk.redirect.url } })\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 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) => { params[name] = match[i + 1] })\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 ? ctx.request.headers.get(p.key.toLowerCase()) : 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 & keyof M>(\n path: P,\n opts?: { query?: Record<string, string>; headers?: Record<string, string> },\n ): Promise<InferResponse<M[`GET ${P}`] extends never ? M[P] : 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(method: string, path: string, body?: unknown, opts?: { headers?: Record<string, string>; query?: Record<string, string> }) {\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 as { headers?: Record<string, string>; query?: Record<string, string> }),\n post: (path: string, body?: unknown, opts?: Record<string, unknown>) => request('POST', path, body, opts as { headers?: Record<string, string> }),\n put: (path: string, body?: unknown, opts?: Record<string, unknown>) => request('PUT', path, body, opts as { headers?: Record<string, string> }),\n delete: (path: string, opts?: Record<string, unknown>) => request('DELETE', path, undefined, opts as { headers?: Record<string, string> }),\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeO,SAASA,WAAWC,SAAS,IAAIC,OAA0B,CAAC,GAAC;AAClE,SAAO,CAACC,WAAAA;AACNC,YAAwBC,mBAAmBF,QAAQ;MACjDF;MACAK,MAAMJ,KAAKI;IACb,CAAA;EACF;AACF;AAPgBN;;;ACLhB,SAASO,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,aAAiC;AAChD,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;;;ACvET,SAASG,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,aAAaC,QAAkB;AAC7C,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;AANgBH;AAQT,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;;;AC5BhB,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;AA2BA,SAASoB,sBACdC,mBAA4D;AAE5D,QAAM,EAAEC,aAAaC,WAAWC,UAAS,IAAKC,MAAMC,QAAQL,iBAAAA,IACxD;IAAEC,aAAaD;IAAmBE,WAAWI;IAAWH,WAAWG;EAAU,IAC7EN;AAGJ,QAAMO,qBAAqB,IAAIC,uBAAuBN,SAAAA;AACtD,MAAIC,UAAWA,WAAUI,kBAAAA;AACzB,QAAME,oBAAoBF,mBAAmBG,WAAU;AAGvD,QAAMC,OAAO,oBAAIC,IAAAA;AACjB,QAAMC,SAAqB,CAAA;AAC3B,aAAWC,QAAQb,aAAa;AAC9B,QAAIU,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,MAAMZ,SAAAA;AACpC,UAAMmB,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;AAGA,QAAMI,UAAUC,kBAAAA;AAChB,QAAMC,UAAU,wBAACC,YAAqBC,cAAclB,QAAQiB,SAASjC,WAAWO,iBAAAA,GAAhE;AAChB,SAAOuB,QAAQK,aAAaH,OAAAA;AAC9B;AA3CgBnC;AA+ChB,eAAeqC,cACblB,QACAiB,SACAjC,WACAO,oBAA0C,CAAA,GAAE;AAE5C,QAAM6B,MAAM,IAAIC,IAAIJ,QAAQG,GAAG;AAC/B,QAAME,SAASL,QAAQK,OAAOC,YAAW;AACzC,QAAMC,WAAWJ,IAAII;AAErB,QAAMC,QAAQC,UAAU1B,QAAQsB,QAAQE,QAAAA;AACxC,MAAI,CAACC,OAAO;AACV,WAAOE,aAAa,KAAK;MAAEC,OAAO;QAAEC,MAAM;QAAaC,SAAS,gBAAgBR,MAAAA,IAAUE,QAAAA;MAAW;IAAE,CAAA;EACzG;AAEA,QAAM,EAAElB,MAAML,UAAU8B,OAAM,IAAKN;AAEnC,MAAI;AAEF,UAAMO,aAAa,MAAMC,cAAc1C,mBAAmB0B,SAASO,QAAAA;AACnE,QAAIQ,WAAY,QAAOA;AAGvB,UAAME,MAAMC,uBAAuBlB,SAAShB,SAAS,aAAaK,KAAK8B,WAAW;AAClF,UAAMC,gBAAgB,MAAMC,UAAUhC,KAAKiC,QAAQL,KAAKlD,SAAAA;AACxD,QAAIqD,cAAe,QAAOA;AAG1B,UAAMG,OAAO,MAAMC,YAAYnB,QAAQL,SAASX,IAAAA;AAChD,QAAIkC,gBAAgBE,SAAU,QAAOF;AAGrC,UAAMG,OAAOC,UAAUtC,KAAKuC,cAAc;MAAE5B;MAASuB;MAAMT;MAAQe,OAAOC,OAAOC,YAAY5B,IAAI6B,YAAY;IAAE,CAAA;AAG/G,QAAI3C,KAAK4C,UAAU;AACjB,aAAO,IAAIR,SAAS,MAAM;QAAES,QAAQ7C,KAAK4C,SAASC;QAAQC,SAAS;UAAEC,UAAU/C,KAAK4C,SAAS9B;QAAI;MAAE,CAAA;IACrG;AAEA,UAAMkC,YAAarD,SAA+CK,KAAK8B,WAAW;AAGlF,UAAMmB,SAAS,MAAMC,gBACnBlD,KAAKmD,cACL,MAAMH,UAAUI,MAAMzD,UAAU0C,IAAAA,GAChC1B,SACAjC,SAAAA;AAGF,WAAO2E,cAAcJ,QAAQjD,MAAMgB,MAAAA;EACrC,SAASsC,KAAK;AACZ,WAAOC,oBAAoBD,KAAKtD,KAAKwD,SAAS7C,SAASjC,SAAAA;EACzD;AACF;AArDekC;AAyDf,eAAeoB,UACbC,QACAwB,SACA/E,WAAuB;AAEvB,aAAWgF,aAAazB,QAAQ;AAC9B,UAAM0B,QAAQ/D,aAAa8D,WAAWhF,SAAAA;AACtC,UAAMkF,UAAU,MAAMD,MAAME,YAAYJ,OAAAA;AACxC,QAAI,CAACG,SAAS;AACZ,YAAME,KAAK,IAAIC,mBAAmB,oBAAA;AAClC,aAAO1C,aAAayC,GAAGE,YAAYF,GAAGG,OAAM,CAAA;IAC9C;EACF;AACA,SAAO;AACT;AAdejC;AAkBf,eAAeG,YAAYnB,QAAgBL,SAAkBX,MAAgB;AAC3E,MAAI,CAAC;IAAC;IAAQ;IAAO;IAASM,SAASU,MAAAA,EAAS,QAAOlC;AAEvD,MAAIoD;AACJ,MAAI;AACF,UAAMgC,OAAO,MAAMvD,QAAQuD,KAAI;AAC/BhC,WAAOgC,OAAOC,KAAKC,MAAMF,IAAAA,IAAQpF;EACnC,QAAQ;AACNoD,WAAOpD;EACT;AAEA,MAAIkB,KAAKqE,cAAcnC,SAASpD,QAAW;AACzC,UAAMmE,SAASjD,KAAKqE,WAAWC,UAAUpC,IAAAA;AACzC,QAAI,CAACe,OAAOsB,SAAS;AACnB,aAAOlD,aAAa,KAAK;QAAEC,OAAO;UAAEC,MAAM;UAAoBiD,QAAQvB,OAAO3B,MAAMkD;QAAO;MAAE,CAAA;IAC9F;AACAtC,WAAOe,OAAOwB;EAChB;AACA,SAAOvC;AACT;AAnBeC;AAuBf,SAASkB,cAAcJ,QAAiBjD,MAAkBgB,QAAc;AACtE,QAAM6B,SAAS7C,KAAK6C,WAAW7B,WAAW,SAAS,MAAM;AACzD,QAAM8B,UAAkC;IAAE,gBAAgB;EAAmB;AAC7E,aAAW,CAAC4B,MAAMC,KAAAA,KAAU3E,KAAK8C,SAAS;AACxCA,YAAQ4B,KAAKE,YAAW,CAAA,IAAMD;EAChC;AAEA,MAAI1B,WAAWnE,UAAamE,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,SAAShC,aAAawB,QAAgBX,MAAa;AACjD,SAAO,IAAIE,SAAS+B,KAAKU,UAAU3C,IAAAA,GAAO;IACxCW;IACAC,SAAS;MAAE,gBAAgB;IAAmB;EAChD,CAAA;AACF;AALSzB;AAeT,SAASD,UACP1B,QACAsB,QACAE,UAAgB;AAEhB,aAAW,EAAElB,MAAML,SAAQ,KAAMD,QAAQ;AACvC,QAAIM,KAAK8E,SAAS,SAAS9E,KAAK8E,SAAS9D,OAAQ;AACjD,UAAMS,SAASsD,UAAU/E,KAAKK,UAAUa,QAAAA;AACxC,QAAIO,WAAW,KAAM,QAAO;MAAEzB;MAAML;MAAU8B;IAAO;EACvD;AACA,SAAO;AACT;AAXSL;AAaT,SAAS2D,UAAUC,SAAiB9D,UAAgB;AAClD,QAAM+D,aAAuB,CAAA;AAC7B,QAAMC,WAAWF,QAAQG,QAAQ,WAAW,CAACC,IAAIV,SAAAA;AAC/CO,eAAWxF,KAAKiF,IAAAA;AAChB,WAAO;EACT,CAAA;AACA,QAAMvD,QAAQ,IAAIkE,OAAO,IAAIH,QAAAA,GAAW,EAAEI,KAAKpE,QAAAA;AAC/C,MAAI,CAACC,MAAO,QAAO;AACnB,QAAMM,SAAiC,CAAC;AACxCwD,aAAWM,QAAQ,CAACb,MAAMc,MAAAA;AAAQ/D,WAAOiD,IAAAA,IAAQvD,MAAMqE,IAAI,CAAA;EAAG,CAAA;AAC9D,SAAO/D;AACT;AAXSsD;AAsBT,SAASzC,UAAUC,cAA4BX,KAAe;AAC5D,MAAIW,aAAakD,WAAW,EAAG,QAAO,CAAA;AACtC,QAAMC,WAAWC,KAAKC,IAAG,GAAIrD,aAAasD,IAAI,CAACC,MAAMA,EAAEC,KAAK,CAAA;AAC5D,QAAM1D,OAAkBzD,MAAMoH,KAAK;IAAEP,QAAQC,WAAW;EAAE,GAAG,MAAM5G,MAAAA;AACnE,aAAWgH,KAAKvD,cAAc;AAC5B,YAAQuD,EAAEG,QAAM;MACd,KAAK;AACH5D,aAAKyD,EAAEC,KAAK,IAAInE,IAAIjB;AACpB;MACF,KAAK;AACH0B,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAOtE,IAAIM,KAAiC4D,EAAEI,GAAG,IAAItE,IAAIM;AAC3E;MACF,KAAK;AACHG,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAMtE,IAAIH,OAAOqE,EAAEI,GAAG,IAAItE,IAAIH;AAChD;MACF,KAAK;AACHY,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAMtE,IAAIY,MAAMsD,EAAEI,GAAG,IAAItE,IAAIY;AAC/C;MACF,KAAK;AACHH,aAAKyD,EAAEC,KAAK,IAAID,EAAEI,MAAMtE,IAAIjB,QAAQmC,QAAQqD,IAAIL,EAAEI,IAAItB,YAAW,CAAA,IAAMnC,OAAOC,YAAYd,IAAIjB,QAAQmC,QAAQsD,QAAO,CAAA;AACrH;MACF,KAAK;AACH/D,aAAKyD,EAAEC,KAAK,IAAInE,IAAIjB,QAAQmC,QAAQqD,IAAI,iBAAA,KAAsB;AAC9D;MACF,KAAK;AACH9D,aAAKyD,EAAEC,KAAK,IAAIjH;AAChB;MACF;AACEuD,aAAKyD,EAAEC,KAAK,IAAIjH;IACpB;EACF;AACA,SAAOuD;AACT;AAhCSC;;;ACjMF,SAAS+D,kBACdC,SACAC,gBAAuC;AAEvC,iBAAeC,QAAQC,QAAgBC,MAAcC,MAAgBC,MAA2E;AAC9I,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;AArBevB;AAuBf,SAAO;IACL6B,KAAK,wBAAC3B,MAAcE,SAAmCJ,QAAQ,OAAOE,MAAMa,QAAWX,IAAAA,GAAlF;IACL0B,MAAM,wBAAC5B,MAAcC,MAAgBC,SAAmCJ,QAAQ,QAAQE,MAAMC,MAAMC,IAAAA,GAA9F;IACN2B,KAAK,wBAAC7B,MAAcC,MAAgBC,SAAmCJ,QAAQ,OAAOE,MAAMC,MAAMC,IAAAA,GAA7F;IACL4B,QAAQ,wBAAC9B,MAAcE,SAAmCJ,QAAQ,UAAUE,MAAMa,QAAWX,IAAAA,GAArF;EACV;AACF;AAjCgBP;AAmCT,IAAM8B,mBAAN,cAA+BM,MAAAA;EAxFtC,OAwFsCA;;;;;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;;;ACzDO,SAASC,SAA6BC,QAAS;AACpD,SAAOA;AACT;AAFgBD;","names":["Controller","prefix","opts","target","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","createDecoratorServer","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","adapter","createNodeAdapter","handler","request","handleRequest","createServer","url","URL","method","toUpperCase","pathname","match","findRoute","jsonResponse","error","code","message","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"]}
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"],"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'\nimport { runExceptionFilters } from './exception-filter-chain.js'\nimport { createExecutionContext, type CanActivate, type ExecutionContext } 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\nexport function createDecoratorServer(\n controllersOrOpts: Function[] | CreateDecoratorServerOptions,\n) {\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 // Create Web Standard handler + Node adapter\n const adapter = createNodeAdapter()\n const handler = (request: Request) => handleRequest(routes, request, container, middlewareEntries)\n return adapter.createServer(handler)\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> {\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 if (!match) {\n return jsonResponse(404, { error: { code: 'NOT_FOUND', message: `No route for ${method} ${pathname}` } })\n }\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, { request, body, params, query: Object.fromEntries(url.searchParams) })\n\n // Redirect\n if (walk.redirect) {\n return new Response(null, { status: walk.redirect.status, headers: { location: walk.redirect.url } })\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 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) => { params[name] = match[i + 1] })\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 ? ctx.request.headers.get(p.key.toLowerCase()) : 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 & keyof M>(\n path: P,\n opts?: { query?: Record<string, string>; headers?: Record<string, string> },\n ): Promise<InferResponse<M[`GET ${P}`] extends never ? M[P] : 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"],"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","createDecoratorServer","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","adapter","createNodeAdapter","handler","request","handleRequest","createServer","url","URL","method","toUpperCase","pathname","match","findRoute","jsonResponse","error","code","message","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"]}
@@ -6,7 +6,7 @@ import {
6
6
  HttpDecoratorsConfigError,
7
7
  createExecutionContext,
8
8
  walkControllerMetadata
9
- } from "./chunk-SMWUPP2C.js";
9
+ } from "./chunk-QGB5YC4T.js";
10
10
  import {
11
11
  nodeIncomingToRequest,
12
12
  writeResponseToNode
@@ -96,16 +96,17 @@ esbuild (used by tsx/Vite) cannot parse parameter decorators. SWC handles them c
96
96
  }
97
97
  const source = readFileSync(absoluteFilePath, "utf-8");
98
98
  const consumerSwcrc = readConsumerSwcrc(dirname(absoluteFilePath));
99
+ const consumerJsc = consumerSwcrc?.jsc && typeof consumerSwcrc.jsc === "object" ? consumerSwcrc.jsc : null;
99
100
  const { code } = swc.transformSync(source, {
100
101
  filename: absoluteFilePath,
101
102
  jsc: {
102
103
  parser: {
103
104
  syntax: "typescript",
104
105
  decorators: true,
105
- ...consumerSwcrc?.jsc?.parser
106
+ ...consumerJsc?.parser
106
107
  },
107
108
  transform: {
108
- ...consumerSwcrc?.jsc?.transform,
109
+ ...consumerJsc?.transform,
109
110
  // NON-NEGOTIABLE — always enforce decorator metadata emission
110
111
  // regardless of consumer .swcrc overrides
111
112
  legacyDecorator: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/theokit-plugin.ts","../src/bridge/swc-loader.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-regexp */\n/**\n * TheoKit integration plugin for @theokit/http.\n *\n * Two modes:\n *\n * **Mode 1 — controllersGlob (RECOMMENDED):**\n * Controllers are discovered by glob pattern and loaded via @swc/core at\n * first request. This bypasses the esbuild parameter-decorator limitation\n * because controller files are never imported through tsx/esbuild.\n *\n * ```ts\n * // theo.config.ts — NO controller import needed\n * import { defineConfig } from 'theokit/server'\n * import { httpDecoratorsPlugin } from '@theokit/http/theokit-plugin'\n *\n * export default defineConfig({\n * plugins: [\n * httpDecoratorsPlugin({\n * controllersGlob: 'server/controllers/**\\/*.controller.ts'\n * })\n * ]\n * })\n * ```\n *\n * **Mode 2 — direct controllers array (tests, pre-compiled):**\n * Pass controller classes directly. Only works when the transpiler\n * supports parameter decorators (e.g., SWC in Vitest, or manual\n * Reflect.defineMetadata in tests).\n *\n * ```ts\n * httpDecoratorsPlugin({ controllers: [CatsController] })\n * ```\n */\nimport 'reflect-metadata'\n\nimport { resolveOrNew, type DiContainer } from './bridge/di-resolve.js'\nimport { runExceptionFilters } from './bridge/exception-filter-chain.js'\nimport { createExecutionContext, type CanActivate, type ExecutionContext } from './bridge/execution-context.js'\nimport { runInterceptors } from './bridge/interceptor-chain.js'\nimport {\n MiddlewareConsumerImpl,\n runMiddleware,\n type ResolvedMiddleware,\n} from './bridge/middleware-consumer.js'\nimport { nodeIncomingToRequest, writeResponseToNode } from './bridge/runtime/node.js'\nimport { loadControllersFromGlob } from './bridge/swc-loader.js'\nimport { walkControllerMetadata, type WalkResult } from './bridge/walk-metadata.js'\nimport type { ParamEntry } from './decorators/params.js'\nimport { ForbiddenException } from './exceptions/http-exception.js'\n\nexport interface HttpDecoratorsPluginOptions {\n /** Direct controller class references (Mode 2 — tests, pre-compiled). */\n controllers?: Function[]\n /** Glob pattern relative to project root (Mode 1 — RECOMMENDED).\n * Example: 'server/controllers/**\\/*.controller.ts'\n * Requires @swc/core as devDependency. */\n controllersGlob?: string\n /** Optional DI container (e.g., @theokit/di Container). */\n container?: DiContainer\n /** NestJS-style middleware configuration callback. */\n configure?: (consumer: MiddlewareConsumerImpl) => void\n}\n\ninterface RouteEntry {\n walk: WalkResult\n instance: object\n regex: RegExp\n paramNames: string[]\n}\n\n/**\n * TheoKit plugin that mounts decorated controllers into the `theokit dev`\n * / `theokit start` request pipeline via the `onRequest` hook.\n *\n * Returns a plain `{ name, register }` object compatible with\n * `TheoPlugin` from `theokit/server` (Pattern D6 — we don't import the\n * type to avoid adding theokit as a compile-time dep; the structural\n * shape is sufficient).\n */\nexport function httpDecoratorsPlugin(opts: HttpDecoratorsPluginOptions) {\n const routes: RouteEntry[] = []\n let initialized = false\n let initPromise: Promise<void> | null = null\n\n // Collect middleware via configure() callback (NestJS pattern)\n const middlewareConsumer = new MiddlewareConsumerImpl(opts.container)\n if (opts.configure) opts.configure(middlewareConsumer)\n const middlewareEntries = middlewareConsumer.getEntries()\n\n // Mode 2: Direct class references — initialize eagerly\n if (opts.controllers && opts.controllers.length > 0) {\n buildRouteTable(routes, opts.controllers, opts.container)\n initialized = true\n }\n\n // Mode 1: controllersGlob — defer initialization to first request\n if (opts.controllersGlob && !initialized) {\n const glob = opts.controllersGlob\n initPromise = (async () => {\n // Resolve root from process.cwd() — the TheoKit project root\n const rootDir = process.cwd()\n const controllers = await loadControllersFromGlob(rootDir, glob)\n buildRouteTable(routes, controllers, opts.container)\n initialized = true\n console.log(\n `[@theokit/http] Loaded ${controllers.length} controller(s) ` +\n `with ${routes.length} route(s) via SWC.`,\n )\n })()\n }\n\n return {\n name: '@theokit/http',\n register(app: {\n addHook: (\n name: string,\n fn: (ctx: Record<string, unknown>) => Promise<void>,\n ) => void\n }) {\n app.addHook('onRequest', async (pluginCtx) => {\n if (initPromise && !initialized) await initPromise\n\n // Convert Node types → Web Standard at the boundary\n const _nodeHttp = await import('node:http')\n const request = nodeIncomingToRequest(pluginCtx.request as InstanceType<typeof _nodeHttp.IncomingMessage>)\n const response = await handleDecoratorRoute(routes, request, opts.container, middlewareEntries)\n if (response) {\n await writeResponseToNode(response, pluginCtx.response as InstanceType<typeof _nodeHttp.ServerResponse>)\n }\n // null = not our route, fall through to TheoKit scanner\n })\n },\n }\n}\n\n// ─── Route table builder ─────────────────────────────────\n\nfunction buildRouteTable(\n routes: RouteEntry[],\n controllers: Function[],\n container?: DiContainer,\n): void {\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 for (const Ctor of unique) {\n const instance = resolveOrNew(Ctor, container)\n const walks = walkControllerMetadata(Ctor)\n for (const w of walks) {\n const { regex, paramNames } = compilePattern(w.fullPath)\n routes.push({ walk: w, instance, regex, paramNames })\n }\n }\n\n // Sort: static routes first, parameterized last (prevents /cats/:id matching \"admin\")\n routes.sort((a, b) => {\n const aHasParam = a.walk.fullPath.includes(':')\n const bHasParam = b.walk.fullPath.includes(':')\n if (aHasParam !== bHasParam) return aHasParam ? 1 : -1\n return 0\n })\n}\n\n// ─── Request handler (extracted for complexity budget) ──\n\n/** Returns Response if handled, null if not our route. */\nasync function handleDecoratorRoute(\n routes: RouteEntry[],\n request: Request,\n container?: DiContainer,\n mwEntries: ResolvedMiddleware[] = [],\n): Promise<Response | null> {\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n const match = findMatch(routes, method, url.pathname)\n if (!match) return null\n\n const { entry, params } = match\n const { walk, instance } = entry\n\n try {\n const mwResponse = await runMiddleware(mwEntries, request, url.pathname)\n if (mwResponse) return mwResponse\n\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 const body = await resolveBody(method, request, walk)\n if (body instanceof Response) return body\n\n const args = buildArgs(walk.paramEntries, {\n request,\n body,\n params,\n query: Object.fromEntries(url.searchParams),\n })\n\n if (walk.redirect) {\n return new Response(null, { status: walk.redirect.status, headers: { location: walk.redirect.url } })\n }\n\n const handlerFn = (instance as Record<string | symbol, Function>)[walk.propertyKey]\n const result = await runInterceptors(\n walk.interceptors,\n () => handlerFn.apply(instance, args) as Promise<unknown>,\n request,\n container,\n )\n\n return buildResponse(result, walk, method)\n } catch (err) {\n return runExceptionFilters(err, walk.filters, request, container)\n }\n}\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 if (!(await guard.canActivate(context))) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n return null\n}\n\nasync function resolveBody(method: string, request: Request, walk: WalkResult): Promise<unknown> {\n if (!['POST', 'PUT', 'PATCH'].includes(method)) return undefined\n let body: unknown\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch { body = undefined }\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\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 [n, v] of walk.headers) headers[n.toLowerCase()] = v\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\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), { status, headers: { 'content-type': 'application/json' } })\n}\n\nfunction findMatch(\n routes: RouteEntry[],\n method: string,\n pathname: string,\n): { entry: RouteEntry; params: Record<string, string> } | null {\n for (const entry of routes) {\n if (entry.walk.verb !== 'ALL' && entry.walk.verb !== method) continue\n const match = pathname.match(entry.regex)\n if (!match) continue\n const params: Record<string, string> = {}\n entry.paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return { entry, params }\n }\n return null\n}\n\nfunction compilePattern(pattern: string): { regex: RegExp; paramNames: string[] } {\n const paramNames: string[] = []\n const regexStr = pattern.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n return { regex: new RegExp(`^${regexStr}$`), paramNames }\n}\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 ? ctx.request.headers.get(p.key.toLowerCase()) : 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 default:\n args[p.index] = undefined\n }\n }\n return args\n}\n\n// resolveOrNew imported from ./bridge/di-resolve.ts (DRY — EC-3)\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * The SWC loader reads/writes controller files by absolute path derived\n * from developer-authored glob patterns in theo.config.ts, not from HTTP\n * input. The file paths come from scanControllerFiles() which walks the\n * project's own source tree. No injection vector.\n */\n/**\n * SWC-powered module loader for controller files with parameter decorators.\n *\n * esbuild (used by tsx/Vite SSR) fundamentally cannot parse TypeScript\n * parameter decorators (`@Body()`, `@Param()`, `@Query()`). This loader\n * uses @swc/core to transform controller files with full decorator support\n * (legacyDecorator + decoratorMetadata), then imports them via a temp .mjs\n * file written in the SAME directory (preserving relative import resolution).\n *\n * Pattern: follows Next.js's approach (read tsconfig → configure SWC)\n * but scoped to the http-decorators package, not the framework core.\n *\n * @see references/next.js/packages/next/src/build/swc/options.ts\n */\nimport 'reflect-metadata'\nimport { readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { resolve, dirname, join, relative } from 'node:path'\nimport { pathToFileURL, fileURLToPath } from 'node:url'\n\nimport { HttpDecoratorsConfigError } from './errors.js'\n\n/** Extract jsc.target from parsed .swcrc or fall back to es2022. */\nfunction getSwcrcTarget(swcrc: Record<string, unknown> | null): string {\n if (!swcrc) return 'es2022'\n const jsc = swcrc.jsc\n if (typeof jsc !== 'object' || jsc === null) return 'es2022'\n const target = (jsc as Record<string, unknown>).target\n return typeof target === 'string' ? target : 'es2022'\n}\n\n/** Cached consumer .swcrc — read once per process. */\nlet consumerSwcrcCache: Record<string, unknown> | null | undefined\n\n/**\n * Walk up from the controller file's directory to find a .swcrc.\n * Returns the parsed JSON or null if none found.\n * Caches the result — .swcrc doesn't change at runtime.\n */\nfunction readConsumerSwcrc(startDir: string): Record<string, unknown> | null {\n if (consumerSwcrcCache !== undefined) return consumerSwcrcCache\n\n let dir = startDir\n const root = dirname(dir) // stop at filesystem root\n while (dir !== root) {\n const candidate = join(dir, '.swcrc')\n try {\n const raw = readFileSync(candidate, 'utf-8')\n const parsed = JSON.parse(raw) as Record<string, unknown>\n // Strip $schema — not an SWC transform option\n delete parsed.$schema\n consumerSwcrcCache = parsed\n return parsed\n } catch {\n // File doesn't exist or invalid JSON — walk up\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n\n consumerSwcrcCache = null\n return null\n}\n\ninterface SwcCore {\n transformSync: (src: string, opts: unknown) => { code: string }\n}\n\n/** Cached @swc/core instance — loaded once, reused across all controller files. */\nlet swcCoreCache: SwcCore | null | undefined\n\n/**\n * Dynamically load @swc/core with singleton cache.\n *\n * Handles pnpm strict node_modules: @swc/core is only directly importable\n * from the package that declares it as a dependency. We use createRequire\n * rooted at THIS package's directory to resolve correctly.\n *\n * The cache ensures the ~50ms dynamic import() cost is paid ONCE,\n * not per-controller-file.\n */\nasync function loadSwcCore(): Promise<SwcCore | null> {\n if (swcCoreCache !== undefined) return swcCoreCache\n\n try {\n swcCoreCache = (await import('@swc/core')) as SwcCore\n } catch {\n try {\n const thisDir = dirname(fileURLToPath(import.meta.url))\n const req = createRequire(resolve(thisDir, 'index.js'))\n const resolved = req.resolve('@swc/core')\n swcCoreCache = (await import(pathToFileURL(resolved).href)) as SwcCore\n } catch {\n swcCoreCache = null\n }\n }\n return swcCoreCache\n}\n\n/**\n * Load a TypeScript controller file using @swc/core for decorator support.\n *\n * Strategy:\n * 1. Read source .ts file\n * 2. Transform via @swc/core with legacyDecorator + decoratorMetadata\n * 3. Write temp .mjs in SAME directory (relative imports resolve correctly)\n * 4. Dynamic import() the .mjs — transitive .ts imports go through\n * tsx/Vite's global hook (they don't have parameter decorators)\n * 5. Cleanup temp file\n */\nexport async function loadControllerWithSwc(\n absoluteFilePath: string,\n): Promise<Record<string, unknown>> {\n const swc = await loadSwcCore()\n if (!swc) {\n throw new HttpDecoratorsConfigError(\n `@swc/core is required for parameter decorators (@Body, @Param, @Query). ` +\n `Install it:\\n\\n pnpm add -D @swc/core\\n\\n` +\n `esbuild (used by tsx/Vite) cannot parse parameter decorators. ` +\n `SWC handles them correctly with full metadata emission.`,\n )\n }\n\n const source = readFileSync(absoluteFilePath, 'utf-8')\n const consumerSwcrc = readConsumerSwcrc(dirname(absoluteFilePath))\n const { code } = swc.transformSync(source, {\n filename: absoluteFilePath,\n jsc: {\n parser: {\n syntax: 'typescript',\n decorators: true,\n ...(consumerSwcrc?.jsc?.parser as Record<string, unknown>),\n },\n transform: {\n ...(consumerSwcrc?.jsc?.transform as Record<string, unknown>),\n // NON-NEGOTIABLE — always enforce decorator metadata emission\n // regardless of consumer .swcrc overrides\n legacyDecorator: true,\n decoratorMetadata: true,\n },\n target: getSwcrcTarget(consumerSwcrc),\n },\n module: { type: 'es6' },\n sourceMaps: false,\n })\n\n // Write temp .mjs in SAME directory so relative imports resolve identically.\n // .mjs = Node treats as ESM regardless of package.json type field.\n // Transitive .ts imports go through tsx's global hook (no param decorators there).\n const tmpPath = absoluteFilePath.replace(/\\.ts$/, '.__decorated__.mjs')\n writeFileSync(tmpPath, code, 'utf-8')\n try {\n const url = pathToFileURL(tmpPath).href + `?t=${Date.now()}`\n return (await import(url)) as Record<string, unknown>\n } finally {\n try {\n unlinkSync(tmpPath)\n } catch {\n // Best-effort cleanup\n }\n }\n}\n\n/**\n * Scan a glob pattern for controller files and load them all via SWC.\n * Returns an array of controller class constructors found.\n */\nexport async function loadControllersFromGlob(\n rootDir: string,\n pattern: string,\n): Promise<Function[]> {\n const files = scanControllerFiles(rootDir, pattern)\n if (files.length === 0) {\n console.warn(\n `[@theokit/http] No controller files found matching \"${pattern}\" ` +\n `in ${rootDir}. Ensure files match the pattern and export @Controller classes.`,\n )\n return []\n }\n\n const controllers: Function[] = []\n for (const file of files) {\n const absPath = resolve(rootDir, file)\n const mod = await loadControllerWithSwc(absPath)\n for (const exported of Object.values(mod)) {\n if (typeof exported === 'function' && isControllerClass(exported)) {\n controllers.push(exported)\n }\n }\n }\n\n return controllers\n}\n\n/**\n * Check if a function has @Controller metadata.\n * Uses Symbol.for() global registry key — same Symbol instance across\n * module boundaries (SWC-loaded controllers share the global registry).\n */\nfunction isControllerClass(fn: Function): boolean {\n try {\n return Reflect.hasMetadata(Symbol.for('theokit:http-decorators:controller-prefix'), fn)\n } catch {\n return false\n }\n}\n\n/**\n * Scan for files matching a controller glob pattern.\n * Extracts the static directory prefix and file suffix from the pattern,\n * then recursively walks the directory.\n */\nfunction scanControllerFiles(rootDir: string, pattern: string): string[] {\n const parts = pattern.split('/')\n const dirParts: string[] = []\n for (const part of parts) {\n if (part.includes('*')) break\n dirParts.push(part)\n }\n const baseDir = join(rootDir, ...dirParts)\n\n // Extract file suffix (e.g., '*.controller.ts' → '.controller.ts')\n const lastPart = parts[parts.length - 1]\n const suffix = lastPart.replace(/\\*/g, '')\n\n const files: string[] = []\n function walk(dir: string) {\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) walk(full)\n else if (entry.name.endsWith(suffix)) files.push(relative(rootDir, full))\n }\n } catch {\n // Directory doesn't exist\n }\n }\n walk(baseDir)\n return files\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","unlinkSync","readdirSync","createRequire","resolve","dirname","join","relative","pathToFileURL","fileURLToPath","getSwcrcTarget","swcrc","jsc","target","consumerSwcrcCache","readConsumerSwcrc","startDir","undefined","dir","root","dirname","candidate","join","raw","readFileSync","parsed","JSON","parse","$schema","parent","swcCoreCache","loadSwcCore","thisDir","fileURLToPath","url","req","createRequire","resolve","resolved","pathToFileURL","href","loadControllerWithSwc","absoluteFilePath","swc","HttpDecoratorsConfigError","source","consumerSwcrc","code","transformSync","filename","parser","syntax","decorators","transform","legacyDecorator","decoratorMetadata","module","type","sourceMaps","tmpPath","replace","writeFileSync","Date","now","unlinkSync","loadControllersFromGlob","rootDir","pattern","files","scanControllerFiles","length","console","warn","controllers","file","absPath","mod","exported","Object","values","isControllerClass","push","fn","Reflect","hasMetadata","Symbol","for","parts","split","dirParts","part","includes","baseDir","lastPart","suffix","walk","entry","readdirSync","withFileTypes","full","name","isDirectory","endsWith","relative","httpDecoratorsPlugin","opts","routes","initialized","initPromise","middlewareConsumer","MiddlewareConsumerImpl","container","configure","middlewareEntries","getEntries","controllers","length","buildRouteTable","controllersGlob","glob","rootDir","process","cwd","loadControllersFromGlob","console","log","name","register","app","addHook","pluginCtx","_nodeHttp","request","nodeIncomingToRequest","response","handleDecoratorRoute","writeResponseToNode","seen","Set","unique","Ctor","has","add","push","instance","resolveOrNew","walks","walkControllerMetadata","w","regex","paramNames","compilePattern","fullPath","walk","sort","a","b","aHasParam","includes","bHasParam","mwEntries","url","URL","method","toUpperCase","match","findMatch","pathname","entry","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","canActivate","ex","ForbiddenException","jsonResponse","statusCode","toJSON","undefined","text","JSON","parse","bodySchema","safeParse","success","error","code","issues","data","n","v","toLowerCase","stringify","verb","forEach","i","pattern","regexStr","replace","_m","RegExp","maxIndex","Math","max","map","p","index","Array","from","source","key","get","entries"]}
1
+ {"version":3,"sources":["../src/theokit-plugin.ts","../src/bridge/swc-loader.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-regexp */\n/**\n * TheoKit integration plugin for @theokit/http.\n *\n * Two modes:\n *\n * **Mode 1 — controllersGlob (RECOMMENDED):**\n * Controllers are discovered by glob pattern and loaded via @swc/core at\n * first request. This bypasses the esbuild parameter-decorator limitation\n * because controller files are never imported through tsx/esbuild.\n *\n * ```ts\n * // theo.config.ts — NO controller import needed\n * import { defineConfig } from 'theokit/server'\n * import { httpDecoratorsPlugin } from '@theokit/http/theokit-plugin'\n *\n * export default defineConfig({\n * plugins: [\n * httpDecoratorsPlugin({\n * controllersGlob: 'server/controllers/**\\/*.controller.ts'\n * })\n * ]\n * })\n * ```\n *\n * **Mode 2 — direct controllers array (tests, pre-compiled):**\n * Pass controller classes directly. Only works when the transpiler\n * supports parameter decorators (e.g., SWC in Vitest, or manual\n * Reflect.defineMetadata in tests).\n *\n * ```ts\n * httpDecoratorsPlugin({ controllers: [CatsController] })\n * ```\n */\nimport 'reflect-metadata'\n\nimport { resolveOrNew, type DiContainer } from './bridge/di-resolve.js'\nimport { runExceptionFilters } from './bridge/exception-filter-chain.js'\nimport { createExecutionContext, type CanActivate, type ExecutionContext } from './bridge/execution-context.js'\nimport { runInterceptors } from './bridge/interceptor-chain.js'\nimport {\n MiddlewareConsumerImpl,\n runMiddleware,\n type ResolvedMiddleware,\n} from './bridge/middleware-consumer.js'\nimport { nodeIncomingToRequest, writeResponseToNode } from './bridge/runtime/node.js'\nimport { loadControllersFromGlob } from './bridge/swc-loader.js'\nimport { walkControllerMetadata, type WalkResult } from './bridge/walk-metadata.js'\nimport type { ParamEntry } from './decorators/params.js'\nimport { ForbiddenException } from './exceptions/http-exception.js'\n\nexport interface HttpDecoratorsPluginOptions {\n /** Direct controller class references (Mode 2 — tests, pre-compiled). */\n controllers?: Function[]\n /** Glob pattern relative to project root (Mode 1 — RECOMMENDED).\n * Example: 'server/controllers/**\\/*.controller.ts'\n * Requires @swc/core as devDependency. */\n controllersGlob?: string\n /** Optional DI container (e.g., @theokit/di Container). */\n container?: DiContainer\n /** NestJS-style middleware configuration callback. */\n configure?: (consumer: MiddlewareConsumerImpl) => void\n}\n\ninterface RouteEntry {\n walk: WalkResult\n instance: object\n regex: RegExp\n paramNames: string[]\n}\n\n/**\n * TheoKit plugin that mounts decorated controllers into the `theokit dev`\n * / `theokit start` request pipeline via the `onRequest` hook.\n *\n * Returns a plain `{ name, register }` object compatible with\n * `TheoPlugin` from `theokit/server` (Pattern D6 — we don't import the\n * type to avoid adding theokit as a compile-time dep; the structural\n * shape is sufficient).\n */\nexport function httpDecoratorsPlugin(opts: HttpDecoratorsPluginOptions) {\n const routes: RouteEntry[] = []\n let initialized = false\n let initPromise: Promise<void> | null = null\n\n // Collect middleware via configure() callback (NestJS pattern)\n const middlewareConsumer = new MiddlewareConsumerImpl(opts.container)\n if (opts.configure) opts.configure(middlewareConsumer)\n const middlewareEntries = middlewareConsumer.getEntries()\n\n // Mode 2: Direct class references — initialize eagerly\n if (opts.controllers && opts.controllers.length > 0) {\n buildRouteTable(routes, opts.controllers, opts.container)\n initialized = true\n }\n\n // Mode 1: controllersGlob — defer initialization to first request\n if (opts.controllersGlob && !initialized) {\n const glob = opts.controllersGlob\n initPromise = (async () => {\n // Resolve root from process.cwd() — the TheoKit project root\n const rootDir = process.cwd()\n const controllers = await loadControllersFromGlob(rootDir, glob)\n buildRouteTable(routes, controllers, opts.container)\n initialized = true\n console.log(\n `[@theokit/http] Loaded ${controllers.length} controller(s) ` +\n `with ${routes.length} route(s) via SWC.`,\n )\n })()\n }\n\n return {\n name: '@theokit/http',\n register(app: {\n addHook: (\n name: string,\n fn: (ctx: Record<string, unknown>) => Promise<void>,\n ) => void\n }) {\n app.addHook('onRequest', async (pluginCtx) => {\n if (initPromise && !initialized) await initPromise\n\n // Convert Node types → Web Standard at the boundary\n const _nodeHttp = await import('node:http')\n const request = nodeIncomingToRequest(pluginCtx.request as InstanceType<typeof _nodeHttp.IncomingMessage>)\n const response = await handleDecoratorRoute(routes, request, opts.container, middlewareEntries)\n if (response) {\n await writeResponseToNode(response, pluginCtx.response as InstanceType<typeof _nodeHttp.ServerResponse>)\n }\n // null = not our route, fall through to TheoKit scanner\n })\n },\n }\n}\n\n// ─── Route table builder ─────────────────────────────────\n\nfunction buildRouteTable(\n routes: RouteEntry[],\n controllers: Function[],\n container?: DiContainer,\n): void {\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 for (const Ctor of unique) {\n const instance = resolveOrNew(Ctor, container)\n const walks = walkControllerMetadata(Ctor)\n for (const w of walks) {\n const { regex, paramNames } = compilePattern(w.fullPath)\n routes.push({ walk: w, instance, regex, paramNames })\n }\n }\n\n // Sort: static routes first, parameterized last (prevents /cats/:id matching \"admin\")\n routes.sort((a, b) => {\n const aHasParam = a.walk.fullPath.includes(':')\n const bHasParam = b.walk.fullPath.includes(':')\n if (aHasParam !== bHasParam) return aHasParam ? 1 : -1\n return 0\n })\n}\n\n// ─── Request handler (extracted for complexity budget) ──\n\n/** Returns Response if handled, null if not our route. */\nasync function handleDecoratorRoute(\n routes: RouteEntry[],\n request: Request,\n container?: DiContainer,\n mwEntries: ResolvedMiddleware[] = [],\n): Promise<Response | null> {\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n const match = findMatch(routes, method, url.pathname)\n if (!match) return null\n\n const { entry, params } = match\n const { walk, instance } = entry\n\n try {\n const mwResponse = await runMiddleware(mwEntries, request, url.pathname)\n if (mwResponse) return mwResponse\n\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 const body = await resolveBody(method, request, walk)\n if (body instanceof Response) return body\n\n const args = buildArgs(walk.paramEntries, {\n request,\n body,\n params,\n query: Object.fromEntries(url.searchParams),\n })\n\n if (walk.redirect) {\n return new Response(null, { status: walk.redirect.status, headers: { location: walk.redirect.url } })\n }\n\n const handlerFn = (instance as Record<string | symbol, Function>)[walk.propertyKey]\n const result = await runInterceptors(\n walk.interceptors,\n () => handlerFn.apply(instance, args) as Promise<unknown>,\n request,\n container,\n )\n\n return buildResponse(result, walk, method)\n } catch (err) {\n return runExceptionFilters(err, walk.filters, request, container)\n }\n}\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 if (!(await guard.canActivate(context))) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n return null\n}\n\nasync function resolveBody(method: string, request: Request, walk: WalkResult): Promise<unknown> {\n if (!['POST', 'PUT', 'PATCH'].includes(method)) return undefined\n let body: unknown\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch { body = undefined }\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\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 [n, v] of walk.headers) headers[n.toLowerCase()] = v\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\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), { status, headers: { 'content-type': 'application/json' } })\n}\n\nfunction findMatch(\n routes: RouteEntry[],\n method: string,\n pathname: string,\n): { entry: RouteEntry; params: Record<string, string> } | null {\n for (const entry of routes) {\n if (entry.walk.verb !== 'ALL' && entry.walk.verb !== method) continue\n const match = pathname.match(entry.regex)\n if (!match) continue\n const params: Record<string, string> = {}\n entry.paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return { entry, params }\n }\n return null\n}\n\nfunction compilePattern(pattern: string): { regex: RegExp; paramNames: string[] } {\n const paramNames: string[] = []\n const regexStr = pattern.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n return { regex: new RegExp(`^${regexStr}$`), paramNames }\n}\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 ? ctx.request.headers.get(p.key.toLowerCase()) : 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 default:\n args[p.index] = undefined\n }\n }\n return args\n}\n\n// resolveOrNew imported from ./bridge/di-resolve.ts (DRY — EC-3)\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * The SWC loader reads/writes controller files by absolute path derived\n * from developer-authored glob patterns in theo.config.ts, not from HTTP\n * input. The file paths come from scanControllerFiles() which walks the\n * project's own source tree. No injection vector.\n */\n/**\n * SWC-powered module loader for controller files with parameter decorators.\n *\n * esbuild (used by tsx/Vite SSR) fundamentally cannot parse TypeScript\n * parameter decorators (`@Body()`, `@Param()`, `@Query()`). This loader\n * uses @swc/core to transform controller files with full decorator support\n * (legacyDecorator + decoratorMetadata), then imports them via a temp .mjs\n * file written in the SAME directory (preserving relative import resolution).\n *\n * Pattern: follows Next.js's approach (read tsconfig → configure SWC)\n * but scoped to the http-decorators package, not the framework core.\n *\n * @see references/next.js/packages/next/src/build/swc/options.ts\n */\nimport 'reflect-metadata'\nimport { readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { resolve, dirname, join, relative } from 'node:path'\nimport { pathToFileURL, fileURLToPath } from 'node:url'\n\nimport { HttpDecoratorsConfigError } from './errors.js'\n\n/** Extract jsc.target from parsed .swcrc or fall back to es2022. */\nfunction getSwcrcTarget(swcrc: Record<string, unknown> | null): string {\n if (!swcrc) return 'es2022'\n const jsc = swcrc.jsc\n if (typeof jsc !== 'object' || jsc === null) return 'es2022'\n const target = (jsc as Record<string, unknown>).target\n return typeof target === 'string' ? target : 'es2022'\n}\n\n/** Cached consumer .swcrc — read once per process. */\nlet consumerSwcrcCache: Record<string, unknown> | null | undefined\n\n/**\n * Walk up from the controller file's directory to find a .swcrc.\n * Returns the parsed JSON or null if none found.\n * Caches the result — .swcrc doesn't change at runtime.\n */\nfunction readConsumerSwcrc(startDir: string): Record<string, unknown> | null {\n if (consumerSwcrcCache !== undefined) return consumerSwcrcCache\n\n let dir = startDir\n const root = dirname(dir) // stop at filesystem root\n while (dir !== root) {\n const candidate = join(dir, '.swcrc')\n try {\n const raw = readFileSync(candidate, 'utf-8')\n const parsed = JSON.parse(raw) as Record<string, unknown>\n // Strip $schema — not an SWC transform option\n delete parsed.$schema\n consumerSwcrcCache = parsed\n return parsed\n } catch {\n // File doesn't exist or invalid JSON — walk up\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n\n consumerSwcrcCache = null\n return null\n}\n\ninterface SwcCore {\n transformSync: (src: string, opts: unknown) => { code: string }\n}\n\n/** Cached @swc/core instance — loaded once, reused across all controller files. */\nlet swcCoreCache: SwcCore | null | undefined\n\n/**\n * Dynamically load @swc/core with singleton cache.\n *\n * Handles pnpm strict node_modules: @swc/core is only directly importable\n * from the package that declares it as a dependency. We use createRequire\n * rooted at THIS package's directory to resolve correctly.\n *\n * The cache ensures the ~50ms dynamic import() cost is paid ONCE,\n * not per-controller-file.\n */\nasync function loadSwcCore(): Promise<SwcCore | null> {\n if (swcCoreCache !== undefined) return swcCoreCache\n\n try {\n swcCoreCache = (await import('@swc/core')) as SwcCore\n } catch {\n try {\n const thisDir = dirname(fileURLToPath(import.meta.url))\n const req = createRequire(resolve(thisDir, 'index.js'))\n const resolved = req.resolve('@swc/core')\n swcCoreCache = (await import(pathToFileURL(resolved).href)) as SwcCore\n } catch {\n swcCoreCache = null\n }\n }\n return swcCoreCache\n}\n\n/**\n * Load a TypeScript controller file using @swc/core for decorator support.\n *\n * Strategy:\n * 1. Read source .ts file\n * 2. Transform via @swc/core with legacyDecorator + decoratorMetadata\n * 3. Write temp .mjs in SAME directory (relative imports resolve correctly)\n * 4. Dynamic import() the .mjs — transitive .ts imports go through\n * tsx/Vite's global hook (they don't have parameter decorators)\n * 5. Cleanup temp file\n */\nexport async function loadControllerWithSwc(\n absoluteFilePath: string,\n): Promise<Record<string, unknown>> {\n const swc = await loadSwcCore()\n if (!swc) {\n throw new HttpDecoratorsConfigError(\n `@swc/core is required for parameter decorators (@Body, @Param, @Query). ` +\n `Install it:\\n\\n pnpm add -D @swc/core\\n\\n` +\n `esbuild (used by tsx/Vite) cannot parse parameter decorators. ` +\n `SWC handles them correctly with full metadata emission.`,\n )\n }\n\n const source = readFileSync(absoluteFilePath, 'utf-8')\n const consumerSwcrc = readConsumerSwcrc(dirname(absoluteFilePath))\n const consumerJsc =\n consumerSwcrc?.jsc && typeof consumerSwcrc.jsc === 'object'\n ? (consumerSwcrc.jsc as Record<string, unknown>)\n : null\n const { code } = swc.transformSync(source, {\n filename: absoluteFilePath,\n jsc: {\n parser: {\n syntax: 'typescript',\n decorators: true,\n ...(consumerJsc?.parser as Record<string, unknown> | undefined),\n },\n transform: {\n ...(consumerJsc?.transform as Record<string, unknown> | undefined),\n // NON-NEGOTIABLE — always enforce decorator metadata emission\n // regardless of consumer .swcrc overrides\n legacyDecorator: true,\n decoratorMetadata: true,\n },\n target: getSwcrcTarget(consumerSwcrc),\n },\n module: { type: 'es6' },\n sourceMaps: false,\n })\n\n // Write temp .mjs in SAME directory so relative imports resolve identically.\n // .mjs = Node treats as ESM regardless of package.json type field.\n // Transitive .ts imports go through tsx's global hook (no param decorators there).\n const tmpPath = absoluteFilePath.replace(/\\.ts$/, '.__decorated__.mjs')\n writeFileSync(tmpPath, code, 'utf-8')\n try {\n const url = pathToFileURL(tmpPath).href + `?t=${Date.now()}`\n return (await import(url)) as Record<string, unknown>\n } finally {\n try {\n unlinkSync(tmpPath)\n } catch {\n // Best-effort cleanup\n }\n }\n}\n\n/**\n * Scan a glob pattern for controller files and load them all via SWC.\n * Returns an array of controller class constructors found.\n */\nexport async function loadControllersFromGlob(\n rootDir: string,\n pattern: string,\n): Promise<Function[]> {\n const files = scanControllerFiles(rootDir, pattern)\n if (files.length === 0) {\n console.warn(\n `[@theokit/http] No controller files found matching \"${pattern}\" ` +\n `in ${rootDir}. Ensure files match the pattern and export @Controller classes.`,\n )\n return []\n }\n\n const controllers: Function[] = []\n for (const file of files) {\n const absPath = resolve(rootDir, file)\n const mod = await loadControllerWithSwc(absPath)\n for (const exported of Object.values(mod)) {\n if (typeof exported === 'function' && isControllerClass(exported)) {\n controllers.push(exported)\n }\n }\n }\n\n return controllers\n}\n\n/**\n * Check if a function has @Controller metadata.\n * Uses Symbol.for() global registry key — same Symbol instance across\n * module boundaries (SWC-loaded controllers share the global registry).\n */\nfunction isControllerClass(fn: Function): boolean {\n try {\n return Reflect.hasMetadata(Symbol.for('theokit:http-decorators:controller-prefix'), fn)\n } catch {\n return false\n }\n}\n\n/**\n * Scan for files matching a controller glob pattern.\n * Extracts the static directory prefix and file suffix from the pattern,\n * then recursively walks the directory.\n */\nfunction scanControllerFiles(rootDir: string, pattern: string): string[] {\n const parts = pattern.split('/')\n const dirParts: string[] = []\n for (const part of parts) {\n if (part.includes('*')) break\n dirParts.push(part)\n }\n const baseDir = join(rootDir, ...dirParts)\n\n // Extract file suffix (e.g., '*.controller.ts' → '.controller.ts')\n const lastPart = parts[parts.length - 1]\n const suffix = lastPart.replace(/\\*/g, '')\n\n const files: string[] = []\n function walk(dir: string) {\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) walk(full)\n else if (entry.name.endsWith(suffix)) files.push(relative(rootDir, full))\n }\n } catch {\n // Directory doesn't exist\n }\n }\n walk(baseDir)\n return files\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","unlinkSync","readdirSync","createRequire","resolve","dirname","join","relative","pathToFileURL","fileURLToPath","getSwcrcTarget","swcrc","jsc","target","consumerSwcrcCache","readConsumerSwcrc","startDir","undefined","dir","root","dirname","candidate","join","raw","readFileSync","parsed","JSON","parse","$schema","parent","swcCoreCache","loadSwcCore","thisDir","fileURLToPath","url","req","createRequire","resolve","resolved","pathToFileURL","href","loadControllerWithSwc","absoluteFilePath","swc","HttpDecoratorsConfigError","source","consumerSwcrc","consumerJsc","code","transformSync","filename","parser","syntax","decorators","transform","legacyDecorator","decoratorMetadata","module","type","sourceMaps","tmpPath","replace","writeFileSync","Date","now","unlinkSync","loadControllersFromGlob","rootDir","pattern","files","scanControllerFiles","length","console","warn","controllers","file","absPath","mod","exported","Object","values","isControllerClass","push","fn","Reflect","hasMetadata","Symbol","for","parts","split","dirParts","part","includes","baseDir","lastPart","suffix","walk","entry","readdirSync","withFileTypes","full","name","isDirectory","endsWith","relative","httpDecoratorsPlugin","opts","routes","initialized","initPromise","middlewareConsumer","MiddlewareConsumerImpl","container","configure","middlewareEntries","getEntries","controllers","length","buildRouteTable","controllersGlob","glob","rootDir","process","cwd","loadControllersFromGlob","console","log","name","register","app","addHook","pluginCtx","_nodeHttp","request","nodeIncomingToRequest","response","handleDecoratorRoute","writeResponseToNode","seen","Set","unique","Ctor","has","add","push","instance","resolveOrNew","walks","walkControllerMetadata","w","regex","paramNames","compilePattern","fullPath","walk","sort","a","b","aHasParam","includes","bHasParam","mwEntries","url","URL","method","toUpperCase","match","findMatch","pathname","entry","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","canActivate","ex","ForbiddenException","jsonResponse","statusCode","toJSON","undefined","text","JSON","parse","bodySchema","safeParse","success","error","code","issues","data","n","v","toLowerCase","stringify","verb","forEach","i","pattern","regexStr","replace","_m","RegExp","maxIndex","Math","max","map","p","index","Array","from","source","key","get","entries"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theokit/http",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "NestJS-style decorators (@Controller, @Get, @Post, @Body, @UseGuards) bridging to TheoKit's defineRoute + defineMiddleware",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -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 { ZodTypeAny } 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): ZodTypeAny | 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 ZodTypeAny\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 { ZodTypeAny } 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?: ZodTypeAny\n querySchema?: ZodTypeAny\n paramsSchema?: ZodTypeAny\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): ZodTypeAny | 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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/app.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-regexp, complexity, sonarjs/cognitive-complexity, max-depth, sonarjs/no-collapsible-if */\nimport 'reflect-metadata'\n\nimport { createExecutionContext, type CanActivate } from './bridge/execution-context.js'\nimport { createNodeAdapter } from './bridge/runtime/node.js'\nimport type { ServerHandle } from './bridge/runtime/types.js'\nimport { walkControllerMetadata, type WalkResult } from './bridge/walk-metadata.js'\nimport type { ParamEntry } from './decorators/params.js'\nimport { ForbiddenException, HttpException } from './exceptions/http-exception.js'\n\n/**\n * TheoApp — NestJS/Spring Boot-style application bootstrap.\n *\n * Internally uses Web Standard Request/Response pipeline.\n * Node adapter converts at the HTTP server boundary.\n */\n\n/** Readiness check — async function returning health status. */\nexport type ReadinessCheck = () => Promise<{ name: string; healthy: boolean; message?: string }>\n\nexport interface TheoAppOptions {\n /** Controller classes decorated with @Controller. */\n controllers: Function[]\n /** Agent classes decorated with @Agent — auto-wired with routes + SSE + tools. */\n agents?: Function[]\n /** @Module class for structured DI. */\n module?: Function\n /** Provider/toolbox classes — instantiated and injected into controllers + agents. */\n providers?: Function[]\n /** LLM API key for agent execution (reads OPENROUTER_API_KEY env if not set). */\n llmApiKey?: string\n /** LLM model override (default: from @Agent({ model }) metadata). */\n llmModel?: string\n /** Agent stream factory override (for testing or custom SDK wiring). */\n agentStreamFactory?: (walk: unknown, tools: unknown[], apiKey: string, model?: string) => (message: string, sessionId: string) => AsyncIterable<unknown>\n /** HTML string to serve at GET / (inline frontend). */\n html?: string\n /** Readiness checks for GET /__theo/ready (K8s readiness probe). */\n readinessChecks?: ReadinessCheck[]\n /** Custom health endpoint path (default: '/__theo/health'). */\n healthPath?: string\n /** Custom readiness endpoint path (default: '/__theo/ready'). */\n readyPath?: string\n}\n\ninterface RouteEntry {\n walk: WalkResult\n instance: object\n /** Pre-compiled regex + param names (Elysia-inspired: compile once at create, not per-request). */\n compiledPattern?: RegExp\n compiledParamNames?: string[]\n /** Whether handler needs body parsing (skip for GET/DELETE without @Body). */\n needsBody?: boolean\n}\n\nexport class TheoApp {\n private serverHandle: ServerHandle\n private readonly routes: RouteEntry[] = []\n private frontendHtml?: string\n private readonly startTime = Date.now()\n private readonly healthPath: string\n private readonly readyPath: string\n private readonly readinessChecks: ReadinessCheck[]\n\n private constructor(opts?: Pick<TheoAppOptions, 'readinessChecks' | 'healthPath' | 'readyPath'>) {\n const hp = opts?.healthPath ?? '/__theo/health'\n const rp = opts?.readyPath ?? '/__theo/ready'\n // Security: prevent health paths from colliding with user API routes\n if (hp.startsWith('/api/')) throw new Error(`[TheoApp] healthPath \"${hp}\" must not start with /api/ — would collide with user routes`)\n if (rp.startsWith('/api/')) throw new Error(`[TheoApp] readyPath \"${rp}\" must not start with /api/ — would collide with user routes`)\n this.healthPath = hp\n this.readyPath = rp\n this.readinessChecks = opts?.readinessChecks ?? []\n const adapter = createNodeAdapter()\n this.serverHandle = adapter.createServer((request) => this.handleRequest(request))\n }\n\n /**\n * Create a TheoKit application with Spring Boot-style DI.\n *\n * EC-7: async because Container may resolve async factories (Agent.create).\n * EC-2: Registration order guaranteed: providers → controllers → agents.\n */\n static async create(opts: TheoAppOptions): Promise<TheoApp> {\n const app = new TheoApp(opts)\n\n // ── DI Container (replaces manual Map<Function, object>) ──\n // Uses @theokit/di Container for: scopes, lifecycle, async, typed errors.\n // Fallback to manual resolution when @theokit/di is not available.\n const registry = new Map<Function, object>()\n\n // 1. Module resolution (@Module metadata)\n if (opts.module) {\n const moduleMeta = Reflect.getMetadata('usetheo:di:module', opts.module)\n if (moduleMeta) {\n const allModules = [opts.module, ...(moduleMeta.imports ?? [])]\n for (const Mod of allModules) {\n const meta = Reflect.getMetadata('usetheo:di:module', Mod)\n if (!meta) continue\n for (const Prov of (meta.providers ?? []) as Function[]) {\n if (!registry.has(Prov)) {\n registry.set(Prov, new (Prov as new () => object)())\n }\n }\n }\n }\n }\n\n // 2. Inline providers (EC-2: BEFORE controllers and agents)\n if (opts.providers) {\n for (const Prov of opts.providers) {\n if (!registry.has(Prov)) {\n registry.set(Prov, new (Prov as new () => object)())\n }\n }\n }\n\n // 3. Controllers with DI\n for (const Controller of opts.controllers) {\n const paramTypes: Function[] = Reflect.getMetadata('design:paramtypes', Controller) ?? []\n const args = paramTypes.map((pt: Function) => {\n const dep = registry.get(pt)\n if (!dep) {\n throw new Error(\n `[TheoApp] Cannot resolve ${pt.name} for ${Controller.name}. ` +\n `Add ${pt.name} to providers (or to @Module({ providers: [...] })).`,\n )\n }\n return dep\n })\n const instance = new (Controller as new (...a: unknown[]) => object)(...args)\n\n // Lifecycle: call @PostConstruct if present\n const postConstruct = Reflect.getMetadata('usetheo:di:post-construct', Controller)\n if (postConstruct && typeof (instance as Record<string, Function>)[postConstruct] === 'function') {\n const result = (instance as Record<string, Function>)[postConstruct]()\n if (result instanceof Promise) await result // EC-1: await async PostConstruct\n }\n\n const walks = walkControllerMetadata(Controller)\n for (const w of walks) {\n app.routes.push({ walk: w, instance })\n }\n }\n\n // Sort: static before parameterized\n app.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 // Pre-compile route patterns (Elysia-inspired: regex built once, not per-request)\n for (const entry of app.routes) {\n const paramNames: string[] = []\n const regexStr = entry.walk.fullPath.replace(/:(\\w+)/g, (_m, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n entry.compiledPattern = new RegExp(`^${regexStr}$`)\n entry.compiledParamNames = paramNames\n // Determine if handler needs body parsing (skip if no @Body decorator)\n entry.needsBody = entry.walk.paramEntries.some((p) => p.source === 'body')\n }\n\n // Bug #8: Store frontend HTML\n if (opts.html) app.frontendHtml = opts.html\n\n // 4. Auto-wire agents (EC-2: AFTER providers and controllers)\n // The framework handles EVERYTHING — walk, compile, mount, stream.\n // Consumer writes: agents: [MyAgent] — and that's it.\n if (opts.agents?.length) {\n await app.autoWireAgents(opts.agents, registry, opts)\n }\n\n return app\n }\n\n async listen(port: number): Promise<void> {\n return new Promise((resolve) => {\n this.serverHandle.listen(port, () => {\n console.log(`TheoKit app listening on http://localhost:${port}`)\n resolve()\n })\n })\n }\n\n getServerHandle(): ServerHandle {\n return this.serverHandle\n }\n\n async close(): Promise<void> {\n return new Promise((resolve) => {\n this.serverHandle.close(() => { resolve(); })\n })\n }\n\n // ── Auto-wire agents (the \"SpringApplication.run()\" moment) ──\n\n private agentRoutes: { method: string; pattern: RegExp; paramNames: string[]; handler: (request: Request) => Promise<Response>; guards: Function[]; agentClass: Function; methodName: string | symbol }[] = []\n\n private async autoWireAgents(agentClasses: Function[], registry: Map<Function, object>, opts: TheoAppOptions) {\n // Dynamic import — @theokit/agents is optional peer dependency\n // SECURITY: new Function used for dynamic import of optional peer dep.\n // Argument is HARDCODED ('@theokit/agents'), never user input. Safe.\n // eslint-disable-next-line @typescript-eslint/no-implied-eval -- dynamic import avoids compile-time dependency on optional peer\n const importFn = new Function('specifier', 'return import(specifier)') as (s: string) => Promise<Record<string, Function>>\n let walkAgentMetadata: Function, compileAgent: Function, generateAgentRoutes: Function, getMixins: Function, createRealAgentStreamFn: Function | undefined\n try {\n const mod = await importFn('@theokit/agents')\n walkAgentMetadata = mod.walkAgentMetadata\n compileAgent = mod.compileAgent\n generateAgentRoutes = mod.generateAgentRoutes\n getMixins = mod.getMixins\n createRealAgentStreamFn = mod.createRealAgentStream\n } catch {\n throw new Error('[TheoApp] @theokit/agents is required when agents[] is provided. Install: npm install @theokit/agents')\n }\n\n const apiKey = opts.llmApiKey ?? process.env.OPENROUTER_API_KEY ?? ''\n\n for (const AgentClass of agentClasses) {\n // 1. Walk metadata (decorators → structured data)\n const mixins = getMixins(AgentClass)\n const allToolboxes = [...mixins]\n\n // Also check providers for @Toolbox classes\n for (const [Cls] of registry) {\n if (Reflect.getMetadata(Symbol.for('theokit:agents:toolbox'), Cls)) {\n if (!allToolboxes.includes(Cls)) allToolboxes.push(Cls)\n }\n }\n\n const walk = walkAgentMetadata(AgentClass, allToolboxes)\n\n // 2. Resolve toolbox instances from registry (DI)\n const toolboxInstances = new Map<Function, object>()\n for (const tb of walk.toolboxes) {\n let instance = registry.get(tb.class)\n if (!instance) {\n instance = new (tb.class as new () => object)()\n registry.set(tb.class, instance)\n }\n toolboxInstances.set(tb.class, instance)\n }\n\n // 3. Compile tools (decorator metadata → defineTool-compatible)\n const compiled = compileAgent(walk, toolboxInstances)\n\n // 4. Create LLM stream factory\n let createRun: (message: string, sessionId: string) => AsyncIterable<unknown>\n\n if (opts.agentStreamFactory) {\n createRun = opts.agentStreamFactory(walk, compiled.tools, apiKey, opts.llmModel)\n } else if (apiKey && createRealAgentStreamFn !== undefined) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition -- apiKey can be empty string\n // Bug #5 fix: use built-in LLM runner from @theokit/agents\n createRun = createRealAgentStreamFn(walk, compiled.tools, apiKey, opts.llmModel) as (m: string, s: string) => AsyncIterable<unknown>\n } else {\n createRun = this.createFallbackStream(walk.agentConfig.name, apiKey)\n }\n\n // 5. Generate routes (POST /chat, GET /runs/:id)\n const routes = generateAgentRoutes({\n walkResult: walk,\n compiledOptions: compiled,\n createRun: createRun as (message: string, sessionId: string) => AsyncIterable<{ type: string;[k: string]: unknown }>,\n })\n\n // 6. Mount routes\n for (const route of routes) {\n const paramNames: string[] = []\n const regexStr = route.path.replace(/:(\\w+)/g, (_m: string, name: string) => {\n paramNames.push(name)\n return '([^/]+)'\n })\n this.agentRoutes.push({\n method: route.method,\n pattern: new RegExp(`^${regexStr}$`),\n paramNames,\n handler: route.handler,\n guards: walk.guards ?? [],\n agentClass: AgentClass,\n methodName: walk.mainLoop?.propertyKey ?? 'run',\n })\n }\n\n console.log(` 🤖 Agent \"${walk.agentConfig.name}\" mounted at ${walk.route}/chat (${compiled.tools.length} tools)`)\n }\n }\n\n private async handleReadinessCheck(): Promise<Response> {\n if (this.readinessChecks.length === 0) {\n return jsonResponse(200, { status: 'ready', checks: [] })\n }\n const results = await Promise.all(this.readinessChecks.map(async (check) => {\n try {\n return await Promise.race([\n check(),\n new Promise<{ name: string; healthy: boolean; message?: string }>((resolve) =>\n setTimeout(() => resolve({ name: 'unknown', healthy: false, message: 'Readiness check timed out (5s)' }), 5000),\n ),\n ])\n } catch (err) {\n return { name: 'unknown', healthy: false, message: err instanceof Error ? err.message : 'Check failed' }\n }\n }))\n const allHealthy = results.every((r) => r.healthy)\n return jsonResponse(allHealthy ? 200 : 503, { status: allHealthy ? 'ready' : 'not_ready', checks: results })\n }\n\n private createFallbackStream(agentName: string, apiKey: string) {\n const msg = !apiKey\n ? 'Set OPENROUTER_API_KEY environment variable or pass llmApiKey to TheoApp.create()'\n : 'Pass agentStreamFactory to TheoApp.create() to connect your LLM provider'\n return (_message: string, _sessionId: string) => {\n const events = [\n { type: 'run_started', runId: `run-${Date.now()}`, agentName },\n { type: 'error', code: 'AGENT_NOT_WIRED', message: msg, retryable: false },\n ]\n return {\n [Symbol.asyncIterator]: () => {\n let i = 0\n return { next: () => Promise.resolve(i < events.length ? { value: events[i++], done: false as const } : { value: undefined, done: true as const }) }\n },\n }\n }\n }\n\n // ── Web Standard request handler ──────────────────\n\n private async handleRequest(request: Request): Promise<Response> {\n const pathname = new URL(request.url).pathname\n\n // ── Health & Readiness probes (bypass guards/interceptors) ──\n if (request.method === 'GET') {\n if (pathname === this.healthPath) {\n return jsonResponse(200, { status: 'ok', uptime: (Date.now() - this.startTime) / 1000, timestamp: Date.now() })\n }\n if (pathname === this.readyPath) {\n return this.handleReadinessCheck()\n }\n }\n\n // Bug #8: Serve frontend HTML at GET /\n if (request.method === 'GET') {\n if ((pathname === '/' || pathname === '/index.html') && this.frontendHtml) {\n return new Response(this.frontendHtml, { status: 200, headers: { 'content-type': 'text/html; charset=utf-8' } })\n }\n }\n\n // Agent routes (auto-wired — checked first, with guard enforcement per Bug #4)\n for (const route of this.agentRoutes) {\n if (request.method.toUpperCase() === route.method && route.pattern.test(pathname)) {\n // Bug #4 fix: enforce @UseGuards on agent routes (same pipeline as controllers)\n if (route.guards.length > 0) {\n const ctx = createExecutionContext(request, route.agentClass, route.methodName)\n for (const GuardCtor of route.guards) {\n const guard = new (GuardCtor as new () => CanActivate)()\n if (!(await guard.canActivate(ctx))) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n }\n return route.handler(request)\n }\n }\n\n const url = new URL(request.url)\n const method = request.method.toUpperCase()\n\n const match = this.findRoute(method, url.pathname)\n if (!match) {\n return jsonResponse(404, { error: { code: 'NOT_FOUND', message: `No route for ${method} ${url.pathname}` } })\n }\n\n const { entry, params } = match\n\n try {\n // Guards\n const ctx = createExecutionContext(request, entry.instance.constructor, entry.walk.propertyKey)\n for (const GuardCtor of entry.walk.guards) {\n const guard = new (GuardCtor as new () => CanActivate)()\n if (!(await guard.canActivate(ctx))) {\n const ex = new ForbiddenException('Forbidden resource')\n return jsonResponse(ex.statusCode, ex.toJSON())\n }\n }\n\n // Body — skip parsing entirely if handler has no @Body decorator (Elysia-inspired lazy parsing)\n let body: unknown\n if (entry.needsBody && ['POST', 'PUT', 'PATCH'].includes(method)) {\n try {\n const text = await request.text()\n body = text ? JSON.parse(text) : undefined\n } catch { body = undefined }\n\n if (entry.walk.bodySchema && body !== undefined) {\n const result = entry.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 }\n\n // Args\n const args = this.buildArgs(entry.walk.paramEntries, request, body, params, Object.fromEntries(url.searchParams))\n\n // Redirect\n if (entry.walk.redirect) {\n return new Response(null, { status: entry.walk.redirect.status, headers: { location: entry.walk.redirect.url } })\n }\n\n // Handler — wrapped by interceptor chain (Bug #2 fix)\n const handlerFn = (entry.instance as Record<string | symbol, Function>)[entry.walk.propertyKey]\n\n let result: unknown\n if (entry.walk.interceptors.length > 0) {\n // Import runInterceptors dynamically to avoid circular dep\n const { runInterceptors } = await import('./bridge/interceptor-chain.js')\n result = await runInterceptors(\n entry.walk.interceptors,\n () => handlerFn.apply(entry.instance, args) as Promise<unknown>,\n request,\n )\n } else {\n result = await handlerFn.apply(entry.instance, args)\n }\n\n return buildResponse(result, entry.walk, method)\n } catch (err) {\n // Exception filters (Bug #2 fix — filters also wired)\n if (entry.walk.filters.length > 0) {\n const { runExceptionFilters } = await import('./bridge/exception-filter-chain.js')\n return runExceptionFilters(err, entry.walk.filters, request)\n }\n // Fallback: HttpException subclasses (Bug #3 fix)\n if (err instanceof HttpException) {\n return jsonResponse(err.statusCode, err.toJSON())\n }\n // Security: never leak raw error messages to clients\n console.error('[theokit] Internal error:', err instanceof Error ? err.message : err)\n return jsonResponse(500, { error: { code: 'INTERNAL_SERVER_ERROR', message: 'Internal Server Error' } })\n }\n }\n\n private findRoute(method: string, pathname: string) {\n for (const entry of this.routes) {\n if (entry.walk.verb !== 'ALL' && entry.walk.verb !== method) continue\n // Use pre-compiled regex (built once at create time, not per-request)\n const match = entry.compiledPattern!.exec(pathname)\n if (!match) continue\n const params: Record<string, string> = {}\n entry.compiledParamNames!.forEach((name, i) => { params[name] = match[i + 1] })\n return { entry, params }\n }\n return null\n }\n\n private buildArgs(entries: ParamEntry[], request: Request, body: unknown, params: Record<string, string>, query: Record<string, string>): unknown[] {\n if (entries.length === 0) return []\n const max = Math.max(...entries.map(p => p.index))\n const args: unknown[] = Array.from({ length: max + 1 }, () => undefined)\n for (const p of entries) {\n switch (p.source) {\n case 'req': args[p.index] = request; break\n case 'body': args[p.index] = p.key ? (body as Record<string, unknown>)[p.key] : body; break\n case 'param': args[p.index] = p.key ? params[p.key] : params; break\n case 'query': args[p.index] = p.key ? query[p.key] : query; break\n case 'headers': args[p.index] = p.key ? request.headers.get(p.key.toLowerCase()) : Object.fromEntries(request.headers.entries()); break\n case 'ip': args[p.index] = request.headers.get('x-forwarded-for') ?? '127.0.0.1'; break\n default: args[p.index] = undefined\n }\n }\n return args\n }\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\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 [n, v] of walk.headers) headers[n.toLowerCase()] = v\n\n if (result === undefined || result === null) {\n return new Response(null, { status: status === 200 ? 204 : status, headers })\n }\n if (typeof result === 'string') {\n headers['content-type'] = 'text/plain'\n return new Response(result, { status, headers })\n }\n return new Response(JSON.stringify(result), { status, headers })\n}\n"],"mappings":"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","names":["TheoApp","serverHandle","routes","frontendHtml","startTime","Date","now","healthPath","readyPath","readinessChecks","opts","hp","rp","startsWith","Error","adapter","createNodeAdapter","createServer","request","handleRequest","create","app","registry","Map","module","moduleMeta","Reflect","getMetadata","allModules","imports","Mod","meta","Prov","providers","has","set","Controller","controllers","paramTypes","args","map","pt","dep","get","name","instance","postConstruct","result","Promise","walks","walkControllerMetadata","w","push","walk","sort","a","b","aP","fullPath","includes","bP","entry","paramNames","regexStr","replace","_m","compiledPattern","RegExp","compiledParamNames","needsBody","paramEntries","some","p","source","html","agents","length","autoWireAgents","listen","port","resolve","console","log","getServerHandle","close","agentRoutes","agentClasses","importFn","Function","walkAgentMetadata","compileAgent","generateAgentRoutes","getMixins","createRealAgentStreamFn","mod","createRealAgentStream","apiKey","llmApiKey","process","env","OPENROUTER_API_KEY","AgentClass","mixins","allToolboxes","Cls","Symbol","for","toolboxInstances","tb","toolboxes","class","compiled","createRun","agentStreamFactory","tools","llmModel","undefined","createFallbackStream","agentConfig","walkResult","compiledOptions","route","path","method","pattern","handler","guards","agentClass","methodName","mainLoop","propertyKey","handleReadinessCheck","jsonResponse","status","checks","results","all","check","race","setTimeout","healthy","message","err","allHealthy","every","r","agentName","msg","_message","_sessionId","events","type","runId","code","retryable","asyncIterator","i","next","value","done","pathname","URL","url","uptime","timestamp","Response","headers","toUpperCase","test","ctx","createExecutionContext","GuardCtor","guard","canActivate","ex","ForbiddenException","statusCode","toJSON","match","findRoute","error","params","body","text","JSON","parse","bodySchema","safeParse","success","issues","data","buildArgs","Object","fromEntries","searchParams","redirect","location","handlerFn","interceptors","runInterceptors","apply","buildResponse","filters","runExceptionFilters","HttpException","verb","exec","forEach","entries","query","max","Math","index","Array","from","key","toLowerCase","stringify","n","v"]}