@theokit/http 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/LICENSE +201 -0
  2. package/dist/app.d.ts +16 -0
  3. package/dist/app.js +1 -1
  4. package/dist/{chunk-34KOKJ5M.js → chunk-6W4T4DPJ.js} +2 -2
  5. package/dist/{chunk-U46H4CGF.js → chunk-ELCXHPAD.js} +2 -2
  6. package/dist/{chunk-NDD7ANXZ.js → chunk-GQ2UH554.js} +122 -4
  7. package/dist/chunk-GQ2UH554.js.map +1 -0
  8. package/dist/{chunk-LKNI6QEP.js → chunk-MQAJWR3K.js} +1 -1
  9. package/dist/chunk-MQAJWR3K.js.map +1 -0
  10. package/dist/chunk-RC4V75DI.js +257 -0
  11. package/dist/chunk-RC4V75DI.js.map +1 -0
  12. package/dist/{exception-filter-chain-BCSQ3MZ2.js → exception-filter-chain-O45FXGEB.js} +3 -3
  13. package/dist/index.d.ts +280 -5
  14. package/dist/index.js +196 -18
  15. package/dist/index.js.map +1 -1
  16. package/dist/interceptor-chain-ELSL6KZT.js +9 -0
  17. package/dist/{middleware-consumer-ljxK1fU_.d.ts → middleware-consumer-DcaksawH.d.ts} +1 -1
  18. package/dist/theokit-plugin.d.ts +1 -1
  19. package/dist/theokit-plugin.js +5 -165
  20. package/dist/theokit-plugin.js.map +1 -1
  21. package/package.json +19 -17
  22. package/dist/chunk-LKNI6QEP.js.map +0 -1
  23. package/dist/chunk-LWCNTZN6.js +0 -87
  24. package/dist/chunk-LWCNTZN6.js.map +0 -1
  25. package/dist/chunk-NDD7ANXZ.js.map +0 -1
  26. package/dist/interceptor-chain-6S3PUV7J.js +0 -9
  27. /package/dist/{chunk-34KOKJ5M.js.map → chunk-6W4T4DPJ.js.map} +0 -0
  28. /package/dist/{chunk-U46H4CGF.js.map → chunk-ELCXHPAD.js.map} +0 -0
  29. /package/dist/{exception-filter-chain-BCSQ3MZ2.js.map → exception-filter-chain-O45FXGEB.js.map} +0 -0
  30. /package/dist/{interceptor-chain-6S3PUV7J.js.map → interceptor-chain-ELSL6KZT.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  MiddlewareConsumerImpl,
3
+ loadControllersFromGlob,
3
4
  runMiddleware
4
- } from "./chunk-LWCNTZN6.js";
5
+ } from "./chunk-RC4V75DI.js";
5
6
  import {
6
- HttpDecoratorsConfigError,
7
7
  createExecutionContext,
8
8
  walkControllerMetadata
9
9
  } from "./chunk-QGB5YC4T.js";
@@ -13,182 +13,22 @@ import {
13
13
  } from "./chunk-HLW7YKZE.js";
14
14
  import {
15
15
  runInterceptors
16
- } from "./chunk-U46H4CGF.js";
16
+ } from "./chunk-ELCXHPAD.js";
17
17
  import {
18
18
  runExceptionFilters
19
- } from "./chunk-34KOKJ5M.js";
19
+ } from "./chunk-6W4T4DPJ.js";
20
20
  import {
21
21
  ForbiddenException
22
22
  } from "./chunk-3PGQVQWG.js";
23
23
  import {
24
24
  resolveOrNew
25
- } from "./chunk-LKNI6QEP.js";
25
+ } from "./chunk-MQAJWR3K.js";
26
26
  import {
27
27
  __name
28
28
  } from "./chunk-7QVYU63E.js";
29
29
 
30
30
  // src/theokit-plugin.ts
31
31
  import "reflect-metadata";
32
-
33
- // src/bridge/swc-loader.ts
34
- import "reflect-metadata";
35
- import { readFileSync, writeFileSync, unlinkSync, readdirSync } from "fs";
36
- import { createRequire } from "module";
37
- import { resolve, dirname, join, relative } from "path";
38
- import { pathToFileURL, fileURLToPath } from "url";
39
- function getSwcrcTarget(swcrc) {
40
- if (!swcrc) return "es2022";
41
- const jsc = swcrc.jsc;
42
- if (typeof jsc !== "object" || jsc === null) return "es2022";
43
- const target = jsc.target;
44
- return typeof target === "string" ? target : "es2022";
45
- }
46
- __name(getSwcrcTarget, "getSwcrcTarget");
47
- var consumerSwcrcCache;
48
- function readConsumerSwcrc(startDir) {
49
- if (consumerSwcrcCache !== void 0) return consumerSwcrcCache;
50
- let dir = startDir;
51
- const root = dirname(dir);
52
- while (dir !== root) {
53
- const candidate = join(dir, ".swcrc");
54
- try {
55
- const raw = readFileSync(candidate, "utf-8");
56
- const parsed = JSON.parse(raw);
57
- delete parsed.$schema;
58
- consumerSwcrcCache = parsed;
59
- return parsed;
60
- } catch {
61
- }
62
- const parent = dirname(dir);
63
- if (parent === dir) break;
64
- dir = parent;
65
- }
66
- consumerSwcrcCache = null;
67
- return null;
68
- }
69
- __name(readConsumerSwcrc, "readConsumerSwcrc");
70
- var swcCoreCache;
71
- async function loadSwcCore() {
72
- if (swcCoreCache !== void 0) return swcCoreCache;
73
- try {
74
- swcCoreCache = await import("@swc/core");
75
- } catch {
76
- try {
77
- const thisDir = dirname(fileURLToPath(import.meta.url));
78
- const req = createRequire(resolve(thisDir, "index.js"));
79
- const resolved = req.resolve("@swc/core");
80
- swcCoreCache = await import(pathToFileURL(resolved).href);
81
- } catch {
82
- swcCoreCache = null;
83
- }
84
- }
85
- return swcCoreCache;
86
- }
87
- __name(loadSwcCore, "loadSwcCore");
88
- async function loadControllerWithSwc(absoluteFilePath) {
89
- const swc = await loadSwcCore();
90
- if (!swc) {
91
- throw new HttpDecoratorsConfigError(`@swc/core is required for parameter decorators (@Body, @Param, @Query). Install it:
92
-
93
- pnpm add -D @swc/core
94
-
95
- esbuild (used by tsx/Vite) cannot parse parameter decorators. SWC handles them correctly with full metadata emission.`);
96
- }
97
- const source = readFileSync(absoluteFilePath, "utf-8");
98
- const consumerSwcrc = readConsumerSwcrc(dirname(absoluteFilePath));
99
- const consumerJsc = consumerSwcrc?.jsc && typeof consumerSwcrc.jsc === "object" ? consumerSwcrc.jsc : null;
100
- const { code } = swc.transformSync(source, {
101
- filename: absoluteFilePath,
102
- jsc: {
103
- parser: {
104
- syntax: "typescript",
105
- decorators: true,
106
- ...consumerJsc?.parser
107
- },
108
- transform: {
109
- ...consumerJsc?.transform,
110
- // NON-NEGOTIABLE — always enforce decorator metadata emission
111
- // regardless of consumer .swcrc overrides
112
- legacyDecorator: true,
113
- decoratorMetadata: true
114
- },
115
- target: getSwcrcTarget(consumerSwcrc)
116
- },
117
- module: {
118
- type: "es6"
119
- },
120
- sourceMaps: false
121
- });
122
- const tmpPath = absoluteFilePath.replace(/\.ts$/, ".__decorated__.mjs");
123
- writeFileSync(tmpPath, code, "utf-8");
124
- try {
125
- const url = pathToFileURL(tmpPath).href + `?t=${Date.now()}`;
126
- return await import(url);
127
- } finally {
128
- try {
129
- unlinkSync(tmpPath);
130
- } catch {
131
- }
132
- }
133
- }
134
- __name(loadControllerWithSwc, "loadControllerWithSwc");
135
- async function loadControllersFromGlob(rootDir, pattern) {
136
- const files = scanControllerFiles(rootDir, pattern);
137
- if (files.length === 0) {
138
- console.warn(`[@theokit/http] No controller files found matching "${pattern}" in ${rootDir}. Ensure files match the pattern and export @Controller classes.`);
139
- return [];
140
- }
141
- const controllers = [];
142
- for (const file of files) {
143
- const absPath = resolve(rootDir, file);
144
- const mod = await loadControllerWithSwc(absPath);
145
- for (const exported of Object.values(mod)) {
146
- if (typeof exported === "function" && isControllerClass(exported)) {
147
- controllers.push(exported);
148
- }
149
- }
150
- }
151
- return controllers;
152
- }
153
- __name(loadControllersFromGlob, "loadControllersFromGlob");
154
- function isControllerClass(fn) {
155
- try {
156
- return Reflect.hasMetadata(/* @__PURE__ */ Symbol.for("theokit:http-decorators:controller-prefix"), fn);
157
- } catch {
158
- return false;
159
- }
160
- }
161
- __name(isControllerClass, "isControllerClass");
162
- function scanControllerFiles(rootDir, pattern) {
163
- const parts = pattern.split("/");
164
- const dirParts = [];
165
- for (const part of parts) {
166
- if (part.includes("*")) break;
167
- dirParts.push(part);
168
- }
169
- const baseDir = join(rootDir, ...dirParts);
170
- const lastPart = parts[parts.length - 1];
171
- const suffix = lastPart.replace(/\*/g, "");
172
- const files = [];
173
- function walk(dir) {
174
- try {
175
- for (const entry of readdirSync(dir, {
176
- withFileTypes: true
177
- })) {
178
- const full = join(dir, entry.name);
179
- if (entry.isDirectory()) walk(full);
180
- else if (entry.name.endsWith(suffix)) files.push(relative(rootDir, full));
181
- }
182
- } catch {
183
- }
184
- }
185
- __name(walk, "walk");
186
- walk(baseDir);
187
- return files;
188
- }
189
- __name(scanControllerFiles, "scanControllerFiles");
190
-
191
- // src/theokit-plugin.ts
192
32
  function httpDecoratorsPlugin(opts) {
193
33
  const routes = [];
194
34
  let initialized = false;
@@ -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 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"]}
1
+ {"version":3,"sources":["../src/theokit-plugin.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"],"mappings":"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","names":["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.5.4",
3
+ "version": "0.6.0",
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,
@@ -29,19 +29,12 @@
29
29
  "README.md",
30
30
  "LICENSE"
31
31
  ],
32
- "scripts": {
33
- "build": "tsup",
34
- "test": "vitest run",
35
- "test:watch": "vitest",
36
- "typecheck": "tsc --noEmit"
37
- },
38
32
  "peerDependencies": {
39
33
  "@swc/core": ">=1.3.0",
40
- "reflect-metadata": "^0.2.2",
41
- "theokit": ">=0.2.0",
42
- "zod": "^4.0.0",
43
34
  "react": ">=18.0.0",
44
- "react-dom": ">=18.0.0"
35
+ "react-dom": ">=18.0.0",
36
+ "reflect-metadata": "^0.2.2",
37
+ "zod": "^4.0.0"
45
38
  },
46
39
  "peerDependenciesMeta": {
47
40
  "@swc/core": {
@@ -57,10 +50,19 @@
57
50
  "devDependencies": {
58
51
  "@swc/core": "^1.15.41",
59
52
  "reflect-metadata": "^0.2.2",
60
- "tsup": "^8.0.0",
61
- "typescript": "^5.7.0",
62
- "vitest": "^3.2.0",
63
- "zod": "^4.0.0"
53
+ "tsup": "^8.5.1",
54
+ "typescript": "^5.9.3",
55
+ "vitest": "^3.2.6",
56
+ "zod": "^4.4.3"
64
57
  },
65
- "license": "MIT"
66
- }
58
+ "license": "MIT",
59
+ "engines": {
60
+ "node": ">=22.12.0"
61
+ },
62
+ "scripts": {
63
+ "build": "tsup",
64
+ "test": "vitest run",
65
+ "test:watch": "vitest",
66
+ "typecheck": "tsc --noEmit"
67
+ }
68
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bridge/di-resolve.ts"],"sourcesContent":["/**\n * Shared DI resolution helper for guards, interceptors, and middleware.\n * Extracted to avoid DRY violation (EC-3) — was duplicated in create-server.ts\n * and theokit-plugin.ts.\n */\n\n/** DI container interface — structural match for @theokit/di Container. */\nexport interface DiContainer {\n resolve<T>(token: Function): T\n}\n\n/**\n * Resolve a class via DI container if provided; otherwise fall back to bare `new`.\n * Guards, interceptors, and middleware classes all use this pattern.\n */\nexport function resolveOrNew(Ctor: Function, container?: DiContainer): object {\n if (container) {\n try {\n return container.resolve(Ctor)\n } catch {\n // MissingInjectableError or similar — fall back to bare new\n }\n }\n return new (Ctor as new () => object)()\n}\n"],"mappings":";;;;;AAeO,SAASA,aAAaC,MAAgBC,WAAuB;AAClE,MAAIA,WAAW;AACb,QAAI;AACF,aAAOA,UAAUC,QAAQF,IAAAA;IAC3B,QAAQ;IAER;EACF;AACA,SAAO,IAAKA,KAAAA;AACd;AATgBD;","names":["resolveOrNew","Ctor","container","resolve"]}
@@ -1,87 +0,0 @@
1
- import {
2
- resolveOrNew
3
- } from "./chunk-LKNI6QEP.js";
4
- import {
5
- __name
6
- } from "./chunk-7QVYU63E.js";
7
-
8
- // src/bridge/middleware-consumer.ts
9
- var MiddlewareConsumerImpl = class {
10
- static {
11
- __name(this, "MiddlewareConsumerImpl");
12
- }
13
- entries = [];
14
- container;
15
- constructor(container) {
16
- this.container = container;
17
- }
18
- apply(...middleware) {
19
- const resolved = middleware.map((m) => this.resolveMiddleware(m));
20
- const excludePatterns = [];
21
- const proxy = {
22
- exclude: /* @__PURE__ */ __name((...routes) => {
23
- for (const r of routes) excludePatterns.push(typeof r === "string" ? r : r.path);
24
- return proxy;
25
- }, "exclude"),
26
- forRoutes: /* @__PURE__ */ __name((...routes) => {
27
- const routePatterns = routes.map((r) => typeof r === "string" ? r : r.path);
28
- for (const handler of resolved) {
29
- this.entries.push({
30
- handler,
31
- routePatterns: [
32
- ...routePatterns
33
- ],
34
- excludePatterns: [
35
- ...excludePatterns
36
- ]
37
- });
38
- }
39
- return this;
40
- }, "forRoutes")
41
- };
42
- return proxy;
43
- }
44
- getEntries() {
45
- return this.entries;
46
- }
47
- resolveMiddleware(m) {
48
- if (typeof m === "function" && !m.prototype?.use) {
49
- return m;
50
- }
51
- const instance = resolveOrNew(m, this.container);
52
- return (request, next) => instance.use(request, next);
53
- }
54
- };
55
- function middlewareMatchesPath(entry, requestPath) {
56
- for (const pattern of entry.excludePatterns) {
57
- if (pathMatches(requestPath, pattern)) return false;
58
- }
59
- for (const pattern of entry.routePatterns) {
60
- if (pathMatches(requestPath, pattern)) return true;
61
- }
62
- return false;
63
- }
64
- __name(middlewareMatchesPath, "middlewareMatchesPath");
65
- function pathMatches(requestPath, pattern) {
66
- if (pattern === "*") return true;
67
- const normPath = ("/" + requestPath).replace(/\/+/g, "/").replace(/\/$/, "");
68
- const normPattern = ("/" + pattern).replace(/\/+/g, "/").replace(/\/$/, "");
69
- return normPath === normPattern || normPath.startsWith(normPattern + "/");
70
- }
71
- __name(pathMatches, "pathMatches");
72
- async function runMiddleware(entries, request, requestPath) {
73
- for (const entry of entries) {
74
- if (!middlewareMatchesPath(entry, requestPath)) continue;
75
- const response = await entry.handler(request, () => Promise.resolve(null));
76
- if (response) return response;
77
- }
78
- return null;
79
- }
80
- __name(runMiddleware, "runMiddleware");
81
-
82
- export {
83
- MiddlewareConsumerImpl,
84
- middlewareMatchesPath,
85
- runMiddleware
86
- };
87
- //# sourceMappingURL=chunk-LWCNTZN6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bridge/middleware-consumer.ts"],"sourcesContent":["/**\n * NestJS-style Middleware support — Web Standard Request/Response.\n *\n * Middleware runs BEFORE guards: middleware → guards → interceptors → handler\n *\n * Supports class middleware (NestMiddleware) and functional middleware.\n * Returns a Response to short-circuit, or null to continue.\n */\nimport { resolveOrNew, type DiContainer } from './di-resolve.js'\n\n/** NestJS-compatible middleware interface — Web Standard. */\nexport interface NestMiddleware {\n use(\n request: Request,\n next: () => Promise<Response | null>,\n ): Response | null | Promise<Response | null>\n}\n\n/** Functional middleware. */\nexport type MiddlewareFn = (\n request: Request,\n next: () => Promise<Response | null>,\n) => Response | null | Promise<Response | null>\n\nexport interface ResolvedMiddleware {\n handler: MiddlewareFn\n routePatterns: string[]\n excludePatterns: string[]\n}\n\nexport type RouteInfo = string | { path: string; method?: string }\n\nexport interface MiddlewareConfigProxy {\n forRoutes(...routes: RouteInfo[]): MiddlewareConsumerImpl\n exclude(...routes: RouteInfo[]): MiddlewareConfigProxy\n}\n\nexport class MiddlewareConsumerImpl {\n private entries: ResolvedMiddleware[] = []\n private container?: DiContainer\n\n constructor(container?: DiContainer) {\n this.container = container\n }\n\n apply(...middleware: (Function | MiddlewareFn)[]): MiddlewareConfigProxy {\n const resolved = middleware.map((m) => this.resolveMiddleware(m))\n const excludePatterns: string[] = []\n\n const proxy: MiddlewareConfigProxy = {\n exclude: (...routes: RouteInfo[]) => {\n for (const r of routes) excludePatterns.push(typeof r === 'string' ? r : r.path)\n return proxy\n },\n forRoutes: (...routes: RouteInfo[]) => {\n const routePatterns = routes.map((r) => (typeof r === 'string' ? r : r.path))\n for (const handler of resolved) {\n this.entries.push({ handler, routePatterns: [...routePatterns], excludePatterns: [...excludePatterns] })\n }\n return this\n },\n }\n return proxy\n }\n\n getEntries(): ResolvedMiddleware[] {\n return this.entries\n }\n\n private resolveMiddleware(m: Function | MiddlewareFn): MiddlewareFn {\n if (typeof m === 'function' && !m.prototype?.use) {\n return m as MiddlewareFn\n }\n const instance = resolveOrNew(m, this.container) as NestMiddleware\n return (request, next) => instance.use(request, next)\n }\n}\n\nexport function middlewareMatchesPath(entry: ResolvedMiddleware, requestPath: string): boolean {\n for (const pattern of entry.excludePatterns) {\n if (pathMatches(requestPath, pattern)) return false\n }\n for (const pattern of entry.routePatterns) {\n if (pathMatches(requestPath, pattern)) return true\n }\n return false\n}\n\nfunction pathMatches(requestPath: string, pattern: string): boolean {\n if (pattern === '*') return true\n const normPath = ('/' + requestPath).replace(/\\/+/g, '/').replace(/\\/$/, '')\n const normPattern = ('/' + pattern).replace(/\\/+/g, '/').replace(/\\/$/, '')\n return normPath === normPattern || normPath.startsWith(normPattern + '/')\n}\n\n/**\n * Run all matching middleware. Returns a Response if any short-circuited, null otherwise.\n */\nexport async function runMiddleware(\n entries: ResolvedMiddleware[],\n request: Request,\n requestPath: string,\n): Promise<Response | null> {\n for (const entry of entries) {\n if (!middlewareMatchesPath(entry, requestPath)) continue\n const response = await entry.handler(request, () => Promise.resolve(null))\n if (response) return response // short-circuit\n }\n return null\n}\n"],"mappings":";;;;;;;;AAqCO,IAAMA,yBAAN,MAAMA;EArCb,OAqCaA;;;EACHC,UAAgC,CAAA;EAChCC;EAER,YAAYA,WAAyB;AACnC,SAAKA,YAAYA;EACnB;EAEAC,SAASC,YAAgE;AACvE,UAAMC,WAAWD,WAAWE,IAAI,CAACC,MAAM,KAAKC,kBAAkBD,CAAAA,CAAAA;AAC9D,UAAME,kBAA4B,CAAA;AAElC,UAAMC,QAA+B;MACnCC,SAAS,2BAAIC,WAAAA;AACX,mBAAWC,KAAKD,OAAQH,iBAAgBK,KAAK,OAAOD,MAAM,WAAWA,IAAIA,EAAEE,IAAI;AAC/E,eAAOL;MACT,GAHS;MAITM,WAAW,2BAAIJ,WAAAA;AACb,cAAMK,gBAAgBL,OAAON,IAAI,CAACO,MAAO,OAAOA,MAAM,WAAWA,IAAIA,EAAEE,IAAI;AAC3E,mBAAWG,WAAWb,UAAU;AAC9B,eAAKJ,QAAQa,KAAK;YAAEI;YAASD,eAAe;iBAAIA;;YAAgBR,iBAAiB;iBAAIA;;UAAiB,CAAA;QACxG;AACA,eAAO;MACT,GANW;IAOb;AACA,WAAOC;EACT;EAEAS,aAAmC;AACjC,WAAO,KAAKlB;EACd;EAEQO,kBAAkBD,GAA0C;AAClE,QAAI,OAAOA,MAAM,cAAc,CAACA,EAAEa,WAAWC,KAAK;AAChD,aAAOd;IACT;AACA,UAAMe,WAAWC,aAAahB,GAAG,KAAKL,SAAS;AAC/C,WAAO,CAACsB,SAASC,SAASH,SAASD,IAAIG,SAASC,IAAAA;EAClD;AACF;AAEO,SAASC,sBAAsBC,OAA2BC,aAAmB;AAClF,aAAWC,WAAWF,MAAMlB,iBAAiB;AAC3C,QAAIqB,YAAYF,aAAaC,OAAAA,EAAU,QAAO;EAChD;AACA,aAAWA,WAAWF,MAAMV,eAAe;AACzC,QAAIa,YAAYF,aAAaC,OAAAA,EAAU,QAAO;EAChD;AACA,SAAO;AACT;AARgBH;AAUhB,SAASI,YAAYF,aAAqBC,SAAe;AACvD,MAAIA,YAAY,IAAK,QAAO;AAC5B,QAAME,YAAY,MAAMH,aAAaI,QAAQ,QAAQ,GAAA,EAAKA,QAAQ,OAAO,EAAA;AACzE,QAAMC,eAAe,MAAMJ,SAASG,QAAQ,QAAQ,GAAA,EAAKA,QAAQ,OAAO,EAAA;AACxE,SAAOD,aAAaE,eAAeF,SAASG,WAAWD,cAAc,GAAA;AACvE;AALSH;AAUT,eAAsBK,cACpBlC,SACAuB,SACAI,aAAmB;AAEnB,aAAWD,SAAS1B,SAAS;AAC3B,QAAI,CAACyB,sBAAsBC,OAAOC,WAAAA,EAAc;AAChD,UAAMQ,WAAW,MAAMT,MAAMT,QAAQM,SAAS,MAAMa,QAAQC,QAAQ,IAAA,CAAA;AACpE,QAAIF,SAAU,QAAOA;EACvB;AACA,SAAO;AACT;AAXsBD;","names":["MiddlewareConsumerImpl","entries","container","apply","middleware","resolved","map","m","resolveMiddleware","excludePatterns","proxy","exclude","routes","r","push","path","forRoutes","routePatterns","handler","getEntries","prototype","use","instance","resolveOrNew","request","next","middlewareMatchesPath","entry","requestPath","pattern","pathMatches","normPath","replace","normPattern","startsWith","runMiddleware","response","Promise","resolve"]}
@@ -1 +0,0 @@
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, max-lines, max-lines-per-function */\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 /** React root element — framework renders SSR internally (preferred over html). */\n root?: unknown\n /** Directory for static file serving (default: 'public', 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 // Frontend HTML — from root (React SSR) or html string\n if (opts.root) {\n const { renderToString } = await import('react-dom/server')\n app.frontendHtml = '<!DOCTYPE html>' + renderToString(opts.root as React.ReactElement)\n } else if (opts.html) {\n app.frontendHtml = opts.html\n }\n\n // Static file serving from public/ (default: enabled)\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/ (runtime-agnostic — Node/Bun/Deno)\n if (this.staticHandler && (request.method === 'GET' || request.method === 'HEAD')) {\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 handler — runtime-agnostic (Node/Bun/Deno).\n *\n * Serves files from a root directory (default: `public/`).\n * Derived from Hono's serve-static middleware pattern:\n * - MIME type detection from extension\n * - Path traversal prevention via regex guard\n * - Runtime-agnostic file reading (Bun.file / Deno.readFile / node:fs)\n */\n\n// ── MIME type map (50+ types) ──────────────────────────\n\nconst MIME_TYPES: Record<string, string> = {\n // Text\n '.html': 'text/html; charset=utf-8',\n '.htm': 'text/html; charset=utf-8',\n '.css': 'text/css; 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 '.xml': 'application/xml; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n '.csv': 'text/csv; charset=utf-8',\n '.md': 'text/markdown; charset=utf-8',\n '.yaml': 'text/yaml; charset=utf-8',\n '.yml': 'text/yaml; charset=utf-8',\n '.ts': 'text/typescript; charset=utf-8',\n '.tsx': 'text/tsx; charset=utf-8',\n '.jsx': 'text/jsx; 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',\n '.ico': 'image/x-icon',\n '.webp': 'image/webp',\n '.avif': 'image/avif',\n '.bmp': 'image/bmp',\n '.tiff': 'image/tiff',\n '.tif': 'image/tiff',\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 '.wav': 'audio/wav',\n '.ogg': 'audio/ogg',\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.avi': 'video/x-msvideo',\n\n // Documents\n '.pdf': 'application/pdf',\n '.zip': 'application/zip',\n '.gz': 'application/gzip',\n '.tar': 'application/x-tar',\n '.wasm': 'application/wasm',\n\n // Misc\n '.map': 'application/json; charset=utf-8',\n '.jsonld': 'application/ld+json; charset=utf-8',\n '.manifest': 'application/manifest+json; charset=utf-8',\n '.webmanifest': 'application/manifest+json; charset=utf-8',\n}\n\n// ── Path traversal guard (Hono-derived regex) ──────────\n\n/**\n * Regex that catches path traversal attacks:\n * - `..` segments (beginning, middle, or end of path)\n * - Double slashes `//`\n * - Backslash sequences `\\\\`\n */\nconst UNSAFE_PATH_RE = /(?:^|[/\\\\])\\.{1,2}(?:$|[/\\\\])|[/\\\\]{2,}/\n\n// ── Public API ─────────────────────────────────────────\n\nexport interface StaticOptions {\n /** Root directory to serve from (default: 'public'). */\n root?: string\n /** Cache-Control max-age in seconds (default: 0 — no caching). */\n maxAge?: number\n}\n\n/**\n * Returns the MIME type for a given file path based on its extension.\n * Falls back to 'application/octet-stream' for unknown extensions.\n */\nexport function getMimeType(filePath: string): string {\n const dot = filePath.lastIndexOf('.')\n if (dot === -1) return 'application/octet-stream'\n const ext = filePath.slice(dot).toLowerCase()\n return MIME_TYPES[ext] ?? 'application/octet-stream'\n}\n\n/**\n * Returns true when the path is safe (no traversal, no double slashes).\n */\nexport function isSafePath(pathname: string): boolean {\n return !UNSAFE_PATH_RE.test(pathname)\n}\n\n// ── Runtime-agnostic file reader ───────────────────────\n\ninterface FileResult {\n content: Uint8Array\n size: number\n}\n\nasync function readFileBun(fullPath: string): Promise<FileResult | null> {\n const g = globalThis as Record<string, unknown>\n const BunRT = g.Bun as {\n file: (path: string) => {\n exists: () => Promise<boolean>\n arrayBuffer: () => Promise<ArrayBuffer>\n size: number\n }\n }\n const file = BunRT.file(fullPath)\n if (!(await file.exists())) return null\n const buf = await file.arrayBuffer()\n return { content: new Uint8Array(buf), size: file.size }\n}\n\nasync function readFileDeno(fullPath: string): Promise<FileResult | null> {\n const g = globalThis as Record<string, unknown>\n const DenoRT = g.Deno as {\n readFile: (path: string) => Promise<Uint8Array>\n stat: (path: string) => Promise<{ isDirectory: boolean }>\n }\n try {\n const stat = await DenoRT.stat(fullPath)\n if (stat.isDirectory) return null\n const content = await DenoRT.readFile(fullPath)\n return { content, size: content.byteLength }\n } catch {\n return null\n }\n}\n\nasync function readFileNode(fullPath: string): Promise<FileResult | null> {\n try {\n const fs = await import('node:fs/promises')\n const stat = await fs.stat(fullPath)\n if (stat.isDirectory()) return null\n const content = await fs.readFile(fullPath)\n return { content: new Uint8Array(content), size: stat.size }\n } catch {\n return null\n }\n}\n\nasync function readFile(fullPath: string): Promise<FileResult | null> {\n const g = globalThis as Record<string, unknown>\n if (g.Bun) return readFileBun(fullPath)\n if (g.Deno) return readFileDeno(fullPath)\n return readFileNode(fullPath)\n}\n\n// ── Resolve root to absolute path ──────────────────────\n\nasync function resolveRoot(root: string): Promise<string> {\n const g = globalThis as Record<string, unknown>\n if (g.Deno) {\n const DenoRT = g.Deno as { cwd: () => string }\n const { join } = await import('node:path')\n return join(DenoRT.cwd(), root)\n }\n const { resolve } = await import('node:path')\n return resolve(root)\n}\n\n// ── Factory ────────────────────────────────────────────\n\n/**\n * Creates a static file handler that serves files from `root` directory.\n *\n * Returns `null` when the request does not match a static file (allowing\n * the caller to fall through to the next handler).\n */\nexport function createStaticHandler(\n options?: StaticOptions,\n): (request: Request) => Promise<Response | null> {\n const root = options?.root ?? 'public'\n const maxAge = options?.maxAge ?? 0\n\n let resolvedRoot: string | undefined\n\n return async (request: Request): Promise<Response | null> => {\n // Only serve GET and HEAD\n const method = request.method.toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') return null\n\n // Extract pathname, strip query params\n const url = new URL(request.url)\n let pathname: string\n try {\n pathname = decodeURIComponent(url.pathname)\n } catch {\n return null // Malformed URL encoding\n }\n\n // Skip framework-reserved paths\n if (pathname.startsWith('/api/') || pathname.startsWith('/@theo/')) return null\n\n // Path traversal guard\n if (!isSafePath(pathname)) return null\n\n // Lazy-resolve root to absolute path (once per handler lifetime)\n resolvedRoot ??= await resolveRoot(root)\n\n // Build full file path\n const { join } = await import('node:path')\n let fullPath = join(resolvedRoot, pathname)\n\n // Directory → try index.html\n let result = await readFile(fullPath)\n if (!result && (pathname.endsWith('/') || !pathname.includes('.'))) {\n fullPath = join(fullPath, 'index.html')\n result = await readFile(fullPath)\n }\n\n if (!result) return null\n\n // Build response headers\n const contentType = getMimeType(fullPath)\n const headers: Record<string, string> = {\n 'content-type': contentType,\n 'content-length': String(result.size),\n }\n if (maxAge > 0) {\n headers['cache-control'] = `public, max-age=${String(maxAge)}`\n }\n\n // HEAD: headers only, no body\n if (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","UNSAFE_PATH_RE","getMimeType","filePath","dot","lastIndexOf","ext","slice","toLowerCase","isSafePath","pathname","test","readFileBun","fullPath","g","globalThis","BunRT","Bun","file","exists","buf","arrayBuffer","content","Uint8Array","size","readFileDeno","DenoRT","Deno","stat","isDirectory","readFile","byteLength","readFileNode","fs","resolveRoot","root","join","cwd","resolve","createStaticHandler","options","maxAge","resolvedRoot","request","method","toUpperCase","url","URL","decodeURIComponent","startsWith","result","endsWith","includes","contentType","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","root","renderToString","html","staticDir","createStaticHandler","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"]}
@@ -1,9 +0,0 @@
1
- import {
2
- runInterceptors
3
- } from "./chunk-U46H4CGF.js";
4
- import "./chunk-LKNI6QEP.js";
5
- import "./chunk-7QVYU63E.js";
6
- export {
7
- runInterceptors
8
- };
9
- //# sourceMappingURL=interceptor-chain-6S3PUV7J.js.map