theokit 0.36.1 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{actions-virtual-module-SYGQE6JQ.js → actions-virtual-module-H3LY5Q22.js} +3 -3
- package/dist/{actions-virtual-module-3KSP3WPJ.js → actions-virtual-module-WLOT2SPR.js} +3 -3
- package/dist/{agent-DX5LHD6X.js → agent-VQMWM5BK.js} +2 -2
- package/dist/{app-typed-client-N4TRX5FI.js → app-typed-client-67GBLYD6.js} +113 -24
- package/dist/app-typed-client-67GBLYD6.js.map +1 -0
- package/dist/{app-typed-client-ESMMLPYK.js → app-typed-client-SPFNGVZL.js} +113 -24
- package/dist/app-typed-client-SPFNGVZL.js.map +1 -0
- package/dist/{build-3C2KQPME.js → build-3BFIACHN.js} +6 -6
- package/dist/{chunk-RVJAM77M.js → chunk-2HKRQUWU.js} +2 -2
- package/dist/{chunk-3KD2O6DC.js → chunk-BKYFOEBK.js} +58 -54
- package/dist/chunk-BKYFOEBK.js.map +1 -0
- package/dist/{chunk-V3X5FWJA.js → chunk-EV33DOG6.js} +1 -1
- package/dist/{chunk-V3X5FWJA.js.map → chunk-EV33DOG6.js.map} +1 -1
- package/dist/{chunk-UDG6JPVP.js → chunk-GW5H4KB4.js} +2 -2
- package/dist/{chunk-DISADTFZ.js → chunk-IPSFSNEJ.js} +2 -2
- package/dist/{chunk-RPMKF6BC.js → chunk-OLZLYLOE.js} +109 -7
- package/dist/chunk-OLZLYLOE.js.map +1 -0
- package/dist/{chunk-CEHXC5NI.js → chunk-Q3C3KH3C.js} +24 -5
- package/dist/chunk-Q3C3KH3C.js.map +1 -0
- package/dist/{chunk-MNQC5XEJ.js → chunk-QCB7ZNR2.js} +94 -37
- package/dist/chunk-QCB7ZNR2.js.map +1 -0
- package/dist/{chunk-T4TCLEFB.js → chunk-QMV3R6D5.js} +91 -36
- package/dist/chunk-QMV3R6D5.js.map +1 -0
- package/dist/chunk-QNFVYST4.js +110 -0
- package/dist/chunk-QNFVYST4.js.map +1 -0
- package/dist/{chunk-WL4AX5FI.js → chunk-QWBXUZAX.js} +57 -54
- package/dist/chunk-QWBXUZAX.js.map +1 -0
- package/dist/cli/index.js +5 -5
- package/dist/client/core.d.ts +12 -1
- package/dist/client/core.js +1 -1
- package/dist/client/index.d.ts +7 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/controller-swc-transform-URELVCBM.js +27 -0
- package/dist/controller-swc-transform-URELVCBM.js.map +1 -0
- package/dist/controller-swc-transform-XV3ETSE3.js +28 -0
- package/dist/controller-swc-transform-XV3ETSE3.js.map +1 -0
- package/dist/{dev-CNPT7COE.js → dev-IBCZYFZZ.js} +8 -8
- package/dist/{index-C3ged4mn.d.ts → index-BeAsvfPr.d.ts} +2 -2
- package/dist/index.js +4 -4
- package/dist/{internal-api-4IXBITVV.js → internal-api-MDCX7A7H.js} +10 -3
- package/dist/{internal-api-5JKDYRGM.js → internal-api-OLNM25HS.js} +9 -3
- package/dist/{mcp-INKDONQS.js → mcp-DKRODZAL.js} +2 -2
- package/dist/{plugin-runner-CMprWWHZ.d.ts → plugin-runner-BFEcgtmJ.d.ts} +1 -1
- package/dist/{plugin-types-L49QYMb5.d.ts → plugin-types-D69-lJZ9.d.ts} +9 -2
- package/dist/{registry-3NB7KOUI.js → registry-FKPH7XEL.js} +1 -1
- package/dist/server/define/index.d.ts +1 -1
- package/dist/server/define/index.js +1 -1
- package/dist/server/http/index.d.ts +3 -3
- package/dist/server/http/index.js +1 -1
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/plugins/index.d.ts +3 -3
- package/dist/{start-LNJZNKF6.js → start-WB7GYXEW.js} +3 -3
- package/dist/vite-plugin/index.js +4 -4
- package/dist/{vite-plugin-NC5ETNMV.js → vite-plugin-5Y3LDTXI.js} +5 -5
- package/package.json +2 -2
- package/dist/app-typed-client-ESMMLPYK.js.map +0 -1
- package/dist/app-typed-client-N4TRX5FI.js.map +0 -1
- package/dist/chunk-3KD2O6DC.js.map +0 -1
- package/dist/chunk-CEHXC5NI.js.map +0 -1
- package/dist/chunk-MNQC5XEJ.js.map +0 -1
- package/dist/chunk-RPMKF6BC.js.map +0 -1
- package/dist/chunk-RSVN727G.js +0 -1
- package/dist/chunk-T4TCLEFB.js.map +0 -1
- package/dist/chunk-WL4AX5FI.js.map +0 -1
- package/dist/vite-plugin-NC5ETNMV.js.map +0 -1
- /package/dist/{actions-virtual-module-SYGQE6JQ.js.map → actions-virtual-module-H3LY5Q22.js.map} +0 -0
- /package/dist/{actions-virtual-module-3KSP3WPJ.js.map → actions-virtual-module-WLOT2SPR.js.map} +0 -0
- /package/dist/{agent-DX5LHD6X.js.map → agent-VQMWM5BK.js.map} +0 -0
- /package/dist/{build-3C2KQPME.js.map → build-3BFIACHN.js.map} +0 -0
- /package/dist/{chunk-RVJAM77M.js.map → chunk-2HKRQUWU.js.map} +0 -0
- /package/dist/{chunk-UDG6JPVP.js.map → chunk-GW5H4KB4.js.map} +0 -0
- /package/dist/{chunk-DISADTFZ.js.map → chunk-IPSFSNEJ.js.map} +0 -0
- /package/dist/{dev-CNPT7COE.js.map → dev-IBCZYFZZ.js.map} +0 -0
- /package/dist/{chunk-RSVN727G.js.map → internal-api-MDCX7A7H.js.map} +0 -0
- /package/dist/{internal-api-4IXBITVV.js.map → internal-api-OLNM25HS.js.map} +0 -0
- /package/dist/{mcp-INKDONQS.js.map → mcp-DKRODZAL.js.map} +0 -0
- /package/dist/{registry-3NB7KOUI.js.map → registry-FKPH7XEL.js.map} +0 -0
- /package/dist/{start-LNJZNKF6.js.map → start-WB7GYXEW.js.map} +0 -0
- /package/dist/{internal-api-5JKDYRGM.js.map → vite-plugin-5Y3LDTXI.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/vite-plugin/app-typed-client.ts","../src/vite-plugin/controller-client-emit.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Codegen-time fs ops: all paths derive from `opts.serverDir` / `opts.distDir`\n * (vite plugin opts — trusted, set by the framework's CLI). The plugin only\n * runs at dev/build time, never serves user HTTP input. No path traversal\n * surface here.\n */\n/**\n * Phase 2 + Phase 4 of G1 (g1-client-codegen-plan.md):\n *\n * 1. `generateClientDts(manifest, dtsOutPath)` — pure function emitting the\n * `.theokit/client.d.ts` shape (declared module `@theo/client`).\n * 2. `appTypedClientPlugin(opts)` — Vite plugin wrapper that calls (1),\n * writes the file atomically, and rewrites on route file changes.\n *\n * Implementation notes:\n * - Pure (1) is unit-testable without Vite or a filesystem (see G1-P2 tests).\n * - EC-2: kebab-case segments → camelCase; unsafe chars → bracket-access.\n * - EC-3: collision between HTTP method name and sub-segment name → method\n * wins; sub-segment ganha suffix `_` + warning comment.\n * - EC-6: write é `writeFileSync(tmp) + renameSync(tmp, final)` (POSIX atomic).\n * - EC-8: imports normalizam `\\\\` → `/`.\n */\n\nimport { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync } from 'node:fs'\nimport { dirname, isAbsolute, posix, relative, resolve } from 'node:path'\n\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport { HTTP_METHODS, type HttpMethod } from '../core/contracts/http-methods.js'\nimport { generateManifest, type ManifestRoute, type TheoManifest } from '../server/internal-api.js'\n\nimport { collectControllerRouteData, type ControllerRouteData } from './controller-client-emit.js'\n\nconst FILE_HEADER = `// AUTO-GENERATED by theokit — do not edit.\n// Source: theokit/packages/theo/src/vite-plugin/app-typed-client.ts (G1 plan).\n// To refresh: \\`pnpm dev\\` or \\`pnpm build\\` regenerates this file on every route change.\n\n/* eslint-disable */\n`\n\ninterface ControllerLeaf {\n /** Import alias of the controller class (`_c0`). */\n classAlias: string\n /** Controller method name backing the route (`findById`). */\n methodName: string\n}\n\ninterface TreeNode {\n /** sub-namespaces — keyed by segment name (already normalized). */\n children: Map<string, TreeNode>\n /** File-based routes: HTTP method → import alias (`InferResponse<typeof alias>`). */\n methods: Map<HttpMethod, string>\n /** Decorator controllers: HTTP method → class alias + method (`Awaited<ReturnType<...>>`). */\n controllerMethods: Map<HttpMethod, ControllerLeaf>\n /** Whether any descendant declares dynamic params (for params type construction). */\n paramNamesAccumulated: string[]\n}\n\nfunction makeNode(): TreeNode {\n return {\n children: new Map(),\n methods: new Map(),\n controllerMethods: new Map(),\n paramNamesAccumulated: [],\n }\n}\n\n/**\n * Normalize a path segment to a valid TS identifier when possible.\n * EC-2: kebab-case → camelCase; pure dynamic (`:name`) → identifier `name`;\n * starts with digit or unsafe char → emit as bracket-access key.\n */\nfunction normalizeSegment(raw: string): { identifier: string; bracket: boolean } {\n // Dynamic segment: leading `:` (e.g. `:id` or `:...slug`).\n let s = raw\n if (s.startsWith(':...')) s = s.slice(4)\n else if (s.startsWith(':')) s = s.slice(1)\n\n // kebab → camel\n const camel = s.replace(/-([a-z0-9])/gi, (_, c: string) => c.toUpperCase())\n\n // Valid TS identifier (cannot start with digit, must be [a-zA-Z_$][a-zA-Z0-9_$]*)\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(camel)) {\n return { identifier: camel, bracket: false }\n }\n // Fallback: bracket-access (keep raw form intact for JSON-string key)\n return { identifier: s, bracket: true }\n}\n\nfunction importPathFor(dtsOutPath: string, routeFileAbs: string): string {\n let rel = relative(dirname(dtsOutPath), routeFileAbs)\n // EC-8: normalize Windows path separators.\n rel = rel.replace(/\\\\/g, '/')\n // Strip extension (`.ts` / `.tsx`); TS module resolution likes extensionless.\n rel = rel.replace(/\\.[jt]sx?$/, '')\n if (!rel.startsWith('.') && !rel.startsWith('/')) {\n rel = './' + rel\n }\n return rel\n}\n\n/**\n * Convert a route path (e.g. `/api/users/:id/posts`) into the tree segments\n * we want under the `client` root. Strips the `/api/` prefix and splits.\n */\nfunction routePathSegments(routePath: string): string[] {\n let p = routePath\n if (p.startsWith('/api/')) p = p.slice(5)\n else if (p.startsWith('/api')) p = p.slice(4)\n if (p.startsWith('/')) p = p.slice(1)\n if (p === '') return []\n return p.split('/').filter(Boolean)\n}\n\nfunction paramNamesFromRoute(routePath: string): string[] {\n const out: string[] = []\n const re = /:(?:\\.\\.\\.)?([^/]+)/g\n let m: RegExpExecArray | null\n while ((m = re.exec(routePath)) !== null) {\n out.push(m[1])\n }\n return out\n}\n\ninterface BuildContext {\n root: TreeNode\n /** Sequential index per route file for aliasing imports. */\n aliasCounter: { value: number }\n /** Lines emitted at the top of the declared module (one per route file). */\n importLines: string[]\n /** Diagnostic warnings rendered as block comments inside the declared module. */\n warnings: string[]\n dtsOutPath: string\n serverDir: string\n}\n\nfunction buildOneRoute(route: ManifestRoute, ctx: BuildContext): void {\n if (!route.methods || route.methods.length === 0) return\n // Build alias prefix once per route file (`_r0`, `_r1`, ...).\n const idx = ctx.aliasCounter.value++\n // Resolve absolute path for the route file (manifest stores it relative to serverDir).\n const absRouteFile = isAbsolute(route.filePath)\n ? route.filePath\n : resolve(ctx.serverDir, route.filePath)\n const importPath = importPathFor(ctx.dtsOutPath, absRouteFile)\n const aliases = route.methods\n .filter((m): m is HttpMethod => (HTTP_METHODS as readonly string[]).includes(m))\n .map((method) => ({ method, alias: `_r${idx}_${method}` }))\n if (aliases.length === 0) return\n const importClause = aliases.map((a) => `${a.method} as ${a.alias}`).join(', ')\n ctx.importLines.push(` import type { ${importClause} } from '${importPath}'`)\n\n // Walk segments + register methods at the leaf node.\n const segments = routePathSegments(route.routePath)\n let node = ctx.root\n for (const seg of segments) {\n const norm = normalizeSegment(seg)\n let child = node.children.get(norm.identifier)\n if (child === undefined) {\n child = makeNode()\n node.children.set(norm.identifier, child)\n }\n node = child\n }\n // Accumulated param names for typed params (e.g. `{ id: string }`).\n node.paramNamesAccumulated = paramNamesFromRoute(route.routePath)\n\n // Register each HTTP method (collision detection runs in a post-pass).\n for (const { method, alias } of aliases) {\n if (node.methods.has(method)) {\n ctx.warnings.push(\n `// WARNING: duplicate ${method} export at /${segments.join('/')} (route file declared twice). Keeping last definition.`,\n )\n }\n node.methods.set(method, alias)\n }\n}\n\n/**\n * #122 (T2.1) — register one decorator-controller route into the tree. The class\n * import alias is shared across the class's methods (resolved by the caller). A\n * file-based route at the same node+verb WINS (file routes are the default); the\n * controller entry is skipped with a warning to keep the emitted `.d.ts` valid.\n */\nfunction buildOneController(cr: ControllerRouteData, classAlias: string, ctx: BuildContext): void {\n const verb = cr.verb.toUpperCase()\n if (!(HTTP_METHODS as readonly string[]).includes(verb)) return\n const method = verb as HttpMethod\n\n const segments = routePathSegments(cr.fullPath)\n let node = ctx.root\n for (const seg of segments) {\n const norm = normalizeSegment(seg)\n let child = node.children.get(norm.identifier)\n if (child === undefined) {\n child = makeNode()\n node.children.set(norm.identifier, child)\n }\n node = child\n }\n node.paramNamesAccumulated = paramNamesFromRoute(cr.fullPath)\n\n if (node.methods.has(method)) {\n ctx.warnings.push(\n `WARNING (#122): controller ${cr.className}.${cr.methodName} (${method} /${segments.join('/')}) collides with a file-based route at the same path; file route wins.`,\n )\n return\n }\n node.controllerMethods.set(method, { classAlias, methodName: cr.methodName })\n}\n\n/**\n * EC-3: walk the tree post-build and resolve any node where a method's\n * lowercase name clashes with a child segment. HTTP method wins (more common);\n * the offending child gets a `_` suffix + a warning entry.\n */\nfunction resolveCollisions(node: TreeNode, pathPrefix: string, warnings: string[]): void {\n const methodLowerNames = new Set<string>()\n for (const method of node.methods.keys()) {\n methodLowerNames.add(method.toLowerCase())\n }\n for (const method of node.controllerMethods.keys()) {\n methodLowerNames.add(method.toLowerCase())\n }\n const renames: { from: string; to: string }[] = []\n for (const childName of node.children.keys()) {\n if (methodLowerNames.has(childName)) {\n renames.push({ from: childName, to: `${childName}_` })\n }\n }\n for (const { from, to } of renames) {\n const old = node.children.get(from)\n if (old !== undefined) {\n node.children.delete(from)\n node.children.set(to, old)\n warnings.push(\n `WARNING (EC-3): path collision — method '${from}' clashed with sub-namespace at ${pathPrefix}/${from}; sub-namespace renamed to '${to}'.`,\n )\n }\n }\n // Recurse into all children (including the renamed ones).\n for (const [name, child] of node.children) {\n resolveCollisions(child, `${pathPrefix}/${name}`, warnings)\n }\n}\n\nfunction renderTreeNode(node: TreeNode, indent: string, _isRoot: boolean): string {\n // _isRoot is reserved for callers; current rendering is uniform across roots\n // and children (both open with `{`). Kept in the signature for future overrides.\n const lines: string[] = ['{']\n // Methods first\n for (const [method, alias] of node.methods) {\n const lower = method.toLowerCase()\n // Routes with path params (`:id`, `:name`) emit a `{ params: {...} }` object\n // intersected with TheoFetchOptions. Without the outer `{...}` wrap the\n // generated signature `(opts: params: {...} & ...)` is invalid TS syntax\n // (parser reads `params:` as a parameter label, then `{...}` as type — invalid).\n const paramsList = node.paramNamesAccumulated.map((p) => `${p}: string`).join('; ')\n const paramsField =\n node.paramNamesAccumulated.length > 0 ? `{ params: { ${paramsList} } } & ` : ''\n const optionalMark = node.paramNamesAccumulated.length === 0 ? '?' : ''\n lines.push(\n `${indent} ${lower}: (opts${optionalMark}: ${paramsField}import('theokit/client').TheoFetchOptions<typeof ${alias}>) => Promise<import('theokit/client').InferResponse<typeof ${alias}>>`,\n )\n }\n // #122 (T2.1) — decorator-controller methods. Response inferred via\n // `Awaited<ReturnType<...>>`; body is `unknown` (ADR-2 checkpoint — @Body types\n // are not recoverable from the class type). params typed from the route pattern.\n for (const [method, leaf] of node.controllerMethods) {\n const lower = method.toLowerCase()\n const paramsList = node.paramNamesAccumulated.map((p) => `${p}: string`).join('; ')\n const paramsField =\n node.paramNamesAccumulated.length > 0 ? `{ params: { ${paramsList} } } & ` : ''\n const optionalMark = node.paramNamesAccumulated.length === 0 ? '?' : ''\n lines.push(\n `${indent} ${lower}: (opts${optionalMark}: ${paramsField}{ body?: unknown; query?: Record<string, string> }) => Promise<Awaited<ReturnType<InstanceType<typeof ${leaf.classAlias}>['${leaf.methodName}']>>>`,\n )\n }\n // Children\n for (const [name, child] of node.children) {\n const propKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)\n ? name\n : `'${name.replace(/'/g, \"\\\\'\")}'`\n lines.push(`${indent} ${propKey}: ${renderTreeNode(child, indent + ' ', false)}`)\n }\n lines.push(`${indent}}`)\n return lines.join('\\n')\n}\n\nexport interface GenerateClientDtsOptions {\n manifest: TheoManifest\n /** Absolute path to the .d.ts that will be emitted. Used to compute import paths. */\n dtsOutPath: string\n /** Absolute serverDir; manifest's filePath entries are relative to this. */\n serverDir: string\n /**\n * #122 (T2.1) — decorator-controller routes (from `collectControllerRouteData`),\n * merged into the same `client.<ns>.<method>()` tree as file routes. Absent/empty\n * → zero controller output (ADR-5 routes-only byte-identity).\n */\n controllerRoutes?: ControllerRouteData[]\n}\n\nexport function generateClientDts({\n manifest,\n dtsOutPath,\n serverDir,\n controllerRoutes,\n}: GenerateClientDtsOptions): string {\n const ctx: BuildContext = {\n root: makeNode(),\n aliasCounter: { value: 0 },\n importLines: [],\n warnings: [],\n dtsOutPath,\n serverDir,\n }\n for (const route of manifest.routes) {\n buildOneRoute(route, ctx)\n }\n // #122 (T2.1) — merge decorator controllers into the same tree. One import\n // alias per class (shared across its methods), keyed by source file.\n if (controllerRoutes?.length) {\n const classAliases = new Map<string, string>()\n for (const cr of controllerRoutes) {\n let alias = classAliases.get(cr.filePath)\n if (alias === undefined) {\n alias = `_c${classAliases.size}`\n classAliases.set(cr.filePath, alias)\n const importPath = importPathFor(dtsOutPath, cr.filePath)\n ctx.importLines.push(` import type { ${cr.className} as ${alias} } from '${importPath}'`)\n }\n buildOneController(cr, alias, ctx)\n }\n }\n // EC-3: resolve collisions in a post-build pass (collisions materialize\n // when two routes are both processed, not during a single route's build).\n resolveCollisions(ctx.root, '', ctx.warnings)\n\n const importsBlock = ctx.importLines.length > 0 ? ctx.importLines.join('\\n') + '\\n\\n' : '\\n'\n const warningsBlock =\n ctx.warnings.length > 0 ? ' // ' + ctx.warnings.join('\\n // ') + '\\n\\n' : ''\n\n // Empty manifest (no routes with methods) → emit a stub interface so consumers\n // still get `client.X` autocomplete-friendly errors instead of \"Module not found\".\n const treeBody =\n ctx.root.children.size === 0 &&\n ctx.root.methods.size === 0 &&\n ctx.root.controllerMethods.size === 0\n ? '{\\n // No routes detected — add `server/routes/*.ts` to populate this client.\\n }'\n : renderTreeNode(ctx.root, ' ', /* isRoot */ true)\n\n return `${FILE_HEADER}\ndeclare module '@theo/client' {\n${importsBlock}${warningsBlock} export interface AppClient ${treeBody}\n export const client: AppClient\n}\n`\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Phase 4 — Vite plugin\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AppTypedClientPluginOptions {\n /** Project root (cwd). */\n cwd: string\n /** Absolute path to the user's `server/` dir. */\n serverDir: string\n /** Absolute path to the `.theokit/` output dir (where `client.d.ts` lands). */\n distDir: string\n /**\n * Additional routes to merge into the typed client alongside file-system routes.\n * Each entry follows ManifestRoute shape (filePath, routePath, methods, paramNames).\n *\n * Used by `@theokit/http` `httpDecoratorsPlugin` to inject\n * decorator-defined routes — zero coupling between theokit core and the\n * decorator package (the walk happens in http-decorators; only data arrives here).\n */\n extraRoutes?: ManifestRoute[]\n}\n\nconst VIRTUAL_APP_CLIENT_ID = '@theo/client'\nconst RESOLVED_APP_CLIENT_ID = '\\0@theo/client'\n\n/**\n * Should a watcher file event under `routes/` or `controllers/` trigger a client\n * re-emit? Ignores the `.__decorated__.mjs` temp files that `loadControllerWithSwc`\n * writes+unlinks during controller codegen — reacting to them would create a\n * perpetual emit→temp-write→watcher→emit loop (#122 T2.1).\n */\nexport function shouldReemitForFile(\n file: string,\n routesGlob: string,\n controllersGlob: string,\n): boolean {\n const normalized = file.replace(/\\\\/g, '/')\n if (normalized.includes('.__decorated__.')) return false\n return normalized.startsWith(routesGlob) || normalized.startsWith(controllersGlob)\n}\n\n/** Atomic write via tmp + rename (EC-6). Skips when content is unchanged. */\nfunction writeFileIfChanged(filePath: string, content: string): { changed: boolean } {\n if (existsSync(filePath)) {\n try {\n const current = readFileSync(filePath, 'utf-8')\n if (current === content) return { changed: false }\n } catch {\n // fall through to write\n }\n }\n mkdirSync(dirname(filePath), { recursive: true })\n const tmp = `${filePath}.${process.pid}.tmp`\n writeFileSync(tmp, content)\n renameSync(tmp, filePath)\n return { changed: true }\n}\n\nasync function emitClientDts(\n opts: AppTypedClientPluginOptions,\n): Promise<{ changed: boolean; path: string }> {\n if (!existsSync(opts.serverDir)) {\n // No server dir — emit empty stub to avoid \"module not found\".\n const stubPath = posix.join(opts.distDir.replace(/\\\\/g, '/'), 'client.d.ts')\n return {\n changed: writeFileIfChanged(\n stubPath,\n generateClientDts({\n manifest: {\n version: 1,\n generatedAt: new Date().toISOString(),\n routes: [],\n actions: [],\n websockets: [],\n },\n dtsOutPath: stubPath,\n serverDir: opts.serverDir,\n }),\n ).changed,\n path: stubPath,\n }\n }\n const manifest = generateManifest(opts.serverDir)\n\n // Decorator-client-bridge: merge extra routes (from @theokit/http\n // or any other source) into the manifest so generateClientDts() produces a\n // SINGLE typed client covering both file-system routes AND injected routes.\n if (opts.extraRoutes?.length) {\n manifest.routes.push(...opts.extraRoutes)\n }\n\n // #122 (T2.1) — merge decorator controllers under `<serverDir>/controllers/`\n // into the same typed client. Zero output when the dir is absent/empty (ADR-5).\n const controllersDir = resolve(opts.serverDir, 'controllers')\n const controllerRoutes = existsSync(controllersDir)\n ? await collectControllerRouteData({ controllersDir })\n : []\n\n const dtsOutPath = posix.join(opts.distDir.replace(/\\\\/g, '/'), 'client.d.ts')\n const content = generateClientDts({\n manifest,\n dtsOutPath,\n serverDir: opts.serverDir,\n controllerRoutes,\n })\n const { changed } = writeFileIfChanged(dtsOutPath, content)\n return { changed, path: dtsOutPath }\n}\n\nexport function appTypedClientPlugin(opts: AppTypedClientPluginOptions): Plugin {\n let debounceTimer: NodeJS.Timeout | undefined\n let viteServer: ViteDevServer | undefined\n\n function scheduleEmit(): void {\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n void (async () => {\n try {\n await emitClientDts(opts)\n // Invalidate the virtual module so re-imports pick up potential new shape.\n if (viteServer) {\n const mod = viteServer.moduleGraph.getModuleById(RESOLVED_APP_CLIENT_ID)\n if (mod) viteServer.moduleGraph.invalidateModule(mod)\n }\n } catch (err) {\n // Surface but don't crash dev server.\n console.error('[theokit:app-typed-client] codegen error:', err)\n }\n })()\n }, 100)\n }\n\n return {\n name: 'theokit:app-typed-client',\n enforce: 'post',\n async configResolved() {\n try {\n await emitClientDts(opts)\n } catch (err) {\n console.error('[theokit:app-typed-client] initial codegen error:', err)\n }\n },\n async buildEnd() {\n try {\n await emitClientDts(opts)\n } catch (err) {\n console.error('[theokit:app-typed-client] buildEnd codegen error:', err)\n }\n },\n configureServer(server) {\n viteServer = server\n const routesGlob = posix.join(opts.serverDir.replace(/\\\\/g, '/'), 'routes')\n const controllersGlob = posix.join(opts.serverDir.replace(/\\\\/g, '/'), 'controllers')\n const onFile = (file: string): void => {\n if (shouldReemitForFile(file, routesGlob, controllersGlob)) scheduleEmit()\n }\n server.watcher.on('add', onFile)\n server.watcher.on('change', onFile)\n server.watcher.on('unlink', onFile)\n },\n resolveId(id) {\n if (id === VIRTUAL_APP_CLIENT_ID) return RESOLVED_APP_CLIENT_ID\n return null\n },\n load(id) {\n if (id === RESOLVED_APP_CLIENT_ID) {\n return [\n `import { createAppClient } from 'theokit/client'`,\n `export const client = createAppClient()`,\n `export default client`,\n ``,\n ].join('\\n')\n }\n return null\n },\n }\n}\n\nexport { VIRTUAL_APP_CLIENT_ID, RESOLVED_APP_CLIENT_ID, emitClientDts }\n","/**\n * #122 (T2.1) — typed-client codegen for decorator controllers.\n *\n * Collects the routing metadata of every `@Controller` under `controllersDir`\n * so the app-typed-client codegen can emit `client.<ns>.<method>()` entries.\n * Codegen loads controllers via `loadControllerWithSwc` (swc-compile + import,\n * no Vite dev server needed) — distinct from the dev SERVING path, which loads\n * through Vite's `ssrLoadModule`.\n *\n * ADR-2 checkpoint (LOCKED — see the plan's Unresolved Questions): only the\n * RESPONSE type is inferred, via `Awaited<ReturnType<InstanceType<typeof C>['m']>>`.\n * Request `@Body` types are NOT inferred from the class — parameter decorators are\n * erased runtime metadata, invisible to the type system, so `Parameters<...>[N]` is\n * positional-only and cannot tell body from param from query. Body falls back to\n * `unknown` (runtime `@Body` Zod validation from Phase 1 still applies). Full body\n * inference is tracked in the follow-up issue.\n */\nimport { loadControllerWithSwc, walkControllerMetadata } from '@theokit/http'\n\nimport {\n scanControllerModules,\n type ControllerModuleLoader,\n} from '../server/http/controller-dispatch.js'\n\n/** One controller route, flattened for the client codegen. */\nexport interface ControllerRouteData {\n /** Absolute path to the `*.controller.ts` source (for the `import type` line). */\n filePath: string\n /** The exported class name (e.g. `TasksController`). */\n className: string\n /** Full route path, e.g. `/api/v2/tasks/:id`. */\n fullPath: string\n /** HTTP verb (`GET`, `POST`, …). */\n verb: string\n /** The controller method name backing this route (e.g. `findById`). */\n methodName: string\n}\n\n/**\n * Scan `controllersDir` and flatten every controller's routes into\n * {@link ControllerRouteData}. Returns `[]` when the directory has no controllers\n * (so the codegen contributes zero bytes — ADR-5 routes-only byte-identity).\n */\nexport async function collectControllerRouteData(opts: {\n controllersDir: string\n loadModule?: ControllerModuleLoader\n}): Promise<ControllerRouteData[]> {\n const loader = opts.loadModule ?? loadControllerWithSwc\n const modules = await scanControllerModules(opts.controllersDir, loader)\n const out: ControllerRouteData[] = []\n for (const { filePath, cls } of modules) {\n const className = (cls as { name: string }).name\n for (const walk of walkControllerMetadata(cls)) {\n out.push({\n filePath,\n className,\n fullPath: walk.fullPath,\n verb: walk.verb,\n methodName: String(walk.propertyKey),\n })\n }\n }\n return out\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import "tsx/esm";
|
|
3
|
-
import {
|
|
4
|
-
validateProjectStructure
|
|
5
|
-
} from "./chunk-GEQJAFSV.js";
|
|
6
3
|
import {
|
|
7
4
|
preflightNodeAndBindings
|
|
8
5
|
} from "./chunk-HNBWZKIQ.js";
|
|
6
|
+
import {
|
|
7
|
+
validateProjectStructure
|
|
8
|
+
} from "./chunk-GEQJAFSV.js";
|
|
9
9
|
import {
|
|
10
10
|
emitOpenApi,
|
|
11
11
|
loadRoutesForOpenApi
|
|
@@ -533,7 +533,7 @@ async function buildCommand(options) {
|
|
|
533
533
|
`);
|
|
534
534
|
}
|
|
535
535
|
async function runAdapterBuild(target, config, cwd) {
|
|
536
|
-
const { theoPluginAsync } = await import("./vite-plugin-
|
|
536
|
+
const { theoPluginAsync } = await import("./vite-plugin-5Y3LDTXI.js");
|
|
537
537
|
const { default: react } = await import("@vitejs/plugin-react");
|
|
538
538
|
const ctx = {
|
|
539
539
|
// `react()` may return Plugin or Plugin[] depending on version; spread the
|
|
@@ -551,7 +551,7 @@ async function runAdapterBuild(target, config, cwd) {
|
|
|
551
551
|
})
|
|
552
552
|
].flat()
|
|
553
553
|
};
|
|
554
|
-
const { resolveAdapter } = await import("./registry-
|
|
554
|
+
const { resolveAdapter } = await import("./registry-FKPH7XEL.js");
|
|
555
555
|
const adapter = await resolveAdapter(target);
|
|
556
556
|
await adapter.build(config, cwd, ctx);
|
|
557
557
|
}
|
|
@@ -619,4 +619,4 @@ function relativize3(absPath, root) {
|
|
|
619
619
|
export {
|
|
620
620
|
buildCommand
|
|
621
621
|
};
|
|
622
|
-
//# sourceMappingURL=build-
|
|
622
|
+
//# sourceMappingURL=build-3BFIACHN.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-M6NMS5LR.js";
|
|
11
11
|
import {
|
|
12
12
|
validateCsrfRequest
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-BKYFOEBK.js";
|
|
14
14
|
|
|
15
15
|
// src/server/transformer.ts
|
|
16
16
|
import superjson from "superjson";
|
|
@@ -773,4 +773,4 @@ export {
|
|
|
773
773
|
serveAgentAuxRoute,
|
|
774
774
|
writeWebResponseToServerResponse
|
|
775
775
|
};
|
|
776
|
-
//# sourceMappingURL=chunk-
|
|
776
|
+
//# sourceMappingURL=chunk-2HKRQUWU.js.map
|
|
@@ -328,6 +328,56 @@ function sendError(res, codeOrInput, message, status, issues, requestId, options
|
|
|
328
328
|
);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
// src/server/http/node-request.ts
|
|
332
|
+
import { Readable } from "stream";
|
|
333
|
+
function pickHeaderString(value) {
|
|
334
|
+
if (typeof value === "string") return value;
|
|
335
|
+
if (Array.isArray(value)) {
|
|
336
|
+
for (const v of value) if (typeof v === "string" && v.length > 0) return v;
|
|
337
|
+
}
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
function synthesizeAbsoluteUrl(req) {
|
|
341
|
+
const host = pickHeaderString(req.headers.host) ?? "localhost";
|
|
342
|
+
return `http://${host}${req.url ?? "/"}`;
|
|
343
|
+
}
|
|
344
|
+
function nodeHeadersToWeb(req) {
|
|
345
|
+
const headers = new Headers();
|
|
346
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
347
|
+
if (value === void 0) continue;
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
headers.set(key, value.join(", "));
|
|
350
|
+
} else {
|
|
351
|
+
headers.set(key, value);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return headers;
|
|
355
|
+
}
|
|
356
|
+
function incomingMessageToWebRequest(req) {
|
|
357
|
+
const url = synthesizeAbsoluteUrl(req);
|
|
358
|
+
const headers = nodeHeadersToWeb(req);
|
|
359
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
360
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
361
|
+
if (!hasBody) {
|
|
362
|
+
return new Request(url, { method, headers });
|
|
363
|
+
}
|
|
364
|
+
const webStream = Readable.toWeb(req);
|
|
365
|
+
return new Request(url, {
|
|
366
|
+
method,
|
|
367
|
+
headers,
|
|
368
|
+
body: webStream,
|
|
369
|
+
// EC-2: Node 18+ requires `duplex: 'half'` when body is a stream.
|
|
370
|
+
// The `RequestInit` type omits it (Web spec gap); cast accordingly.
|
|
371
|
+
...{ duplex: "half" }
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
function incomingMessageToHandlerRequest(req) {
|
|
375
|
+
return new Request(synthesizeAbsoluteUrl(req), {
|
|
376
|
+
method: (req.method ?? "GET").toUpperCase(),
|
|
377
|
+
headers: nodeHeadersToWeb(req)
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
331
381
|
// src/core/contracts/auth-error-guard.ts
|
|
332
382
|
function isAuthRequiredError(err) {
|
|
333
383
|
if (err == null || typeof err !== "object") return false;
|
|
@@ -711,56 +761,6 @@ async function runMiddlewareAndContext(req, res, loadModule, serverDir) {
|
|
|
711
761
|
return { ctx, aborted: false };
|
|
712
762
|
}
|
|
713
763
|
|
|
714
|
-
// src/server/http/node-request.ts
|
|
715
|
-
import { Readable } from "stream";
|
|
716
|
-
function pickHeaderString(value) {
|
|
717
|
-
if (typeof value === "string") return value;
|
|
718
|
-
if (Array.isArray(value)) {
|
|
719
|
-
for (const v of value) if (typeof v === "string" && v.length > 0) return v;
|
|
720
|
-
}
|
|
721
|
-
return void 0;
|
|
722
|
-
}
|
|
723
|
-
function synthesizeAbsoluteUrl(req) {
|
|
724
|
-
const host = pickHeaderString(req.headers.host) ?? "localhost";
|
|
725
|
-
return `http://${host}${req.url ?? "/"}`;
|
|
726
|
-
}
|
|
727
|
-
function nodeHeadersToWeb(req) {
|
|
728
|
-
const headers = new Headers();
|
|
729
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
730
|
-
if (value === void 0) continue;
|
|
731
|
-
if (Array.isArray(value)) {
|
|
732
|
-
headers.set(key, value.join(", "));
|
|
733
|
-
} else {
|
|
734
|
-
headers.set(key, value);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
return headers;
|
|
738
|
-
}
|
|
739
|
-
function incomingMessageToWebRequest(req) {
|
|
740
|
-
const url = synthesizeAbsoluteUrl(req);
|
|
741
|
-
const headers = nodeHeadersToWeb(req);
|
|
742
|
-
const method = (req.method ?? "GET").toUpperCase();
|
|
743
|
-
const hasBody = method !== "GET" && method !== "HEAD";
|
|
744
|
-
if (!hasBody) {
|
|
745
|
-
return new Request(url, { method, headers });
|
|
746
|
-
}
|
|
747
|
-
const webStream = Readable.toWeb(req);
|
|
748
|
-
return new Request(url, {
|
|
749
|
-
method,
|
|
750
|
-
headers,
|
|
751
|
-
body: webStream,
|
|
752
|
-
// EC-2: Node 18+ requires `duplex: 'half'` when body is a stream.
|
|
753
|
-
// The `RequestInit` type omits it (Web spec gap); cast accordingly.
|
|
754
|
-
...{ duplex: "half" }
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
function incomingMessageToHandlerRequest(req) {
|
|
758
|
-
return new Request(synthesizeAbsoluteUrl(req), {
|
|
759
|
-
method: (req.method ?? "GET").toUpperCase(),
|
|
760
|
-
headers: nodeHeadersToWeb(req)
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
|
|
764
764
|
// src/server/http/execute.ts
|
|
765
765
|
var CSRF_PROTECTED_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
766
766
|
async function pipeWebStreamToResponse(body, res, ctx) {
|
|
@@ -809,8 +809,9 @@ async function executeRoute(ctx) {
|
|
|
809
809
|
disallowed,
|
|
810
810
|
jobBackend
|
|
811
811
|
} = ctx;
|
|
812
|
+
const webRequest = incomingMessageToHandlerRequest(req);
|
|
812
813
|
const buildPluginCtx = (ctxObj) => ({
|
|
813
|
-
request:
|
|
814
|
+
request: webRequest,
|
|
814
815
|
response: res,
|
|
815
816
|
ctx: ctxObj,
|
|
816
817
|
requestId: requestId ?? "no-id"
|
|
@@ -906,7 +907,6 @@ async function executeRoute(ctx) {
|
|
|
906
907
|
if (preResult.shortCircuited) return;
|
|
907
908
|
}
|
|
908
909
|
const callableHandler = handler;
|
|
909
|
-
const webRequest = incomingMessageToHandlerRequest(req);
|
|
910
910
|
const handlerResult = await callableHandler({ query, body, params, request: webRequest, ctx: ctx2 });
|
|
911
911
|
if (handlerResult === void 0 || handlerResult === null) {
|
|
912
912
|
sendJson(res, null, rc.status ?? 204, transformer);
|
|
@@ -1544,8 +1544,9 @@ async function executeActionWithOptions(opts) {
|
|
|
1544
1544
|
csrfMode = "strict",
|
|
1545
1545
|
disallowed
|
|
1546
1546
|
} = opts;
|
|
1547
|
+
const webRequest = incomingMessageToHandlerRequest(req);
|
|
1547
1548
|
const buildPluginCtx = (ctxObj) => ({
|
|
1548
|
-
request:
|
|
1549
|
+
request: webRequest,
|
|
1549
1550
|
response: res,
|
|
1550
1551
|
ctx: ctxObj,
|
|
1551
1552
|
requestId: requestId ?? "no-id"
|
|
@@ -2140,10 +2141,13 @@ function generateNonce() {
|
|
|
2140
2141
|
export {
|
|
2141
2142
|
logRequest,
|
|
2142
2143
|
warnOnce,
|
|
2144
|
+
dispatchCsrfWarn,
|
|
2143
2145
|
safeAudit,
|
|
2144
2146
|
validateCsrfRequest,
|
|
2147
|
+
enforceCsrf,
|
|
2145
2148
|
sendError,
|
|
2146
2149
|
incomingMessageToWebRequest,
|
|
2150
|
+
incomingMessageToHandlerRequest,
|
|
2147
2151
|
executeRoute,
|
|
2148
2152
|
executeAction,
|
|
2149
2153
|
findSuggestion,
|
|
@@ -2155,4 +2159,4 @@ export {
|
|
|
2155
2159
|
applySecurityHeaders,
|
|
2156
2160
|
generateNonce
|
|
2157
2161
|
};
|
|
2158
|
-
//# sourceMappingURL=chunk-
|
|
2162
|
+
//# sourceMappingURL=chunk-BKYFOEBK.js.map
|