theokit 0.2.0 → 0.2.2

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 (130) hide show
  1. package/dist/actions-virtual-module-E6PU47GV.js +175 -0
  2. package/dist/actions-virtual-module-E6PU47GV.js.map +1 -0
  3. package/dist/actions-virtual-module-E7IELVHL.js +174 -0
  4. package/dist/actions-virtual-module-E7IELVHL.js.map +1 -0
  5. package/dist/app-typed-client-RZMVWHJ3.js +289 -0
  6. package/dist/app-typed-client-RZMVWHJ3.js.map +1 -0
  7. package/dist/app-typed-client-YIBP6JWH.js +289 -0
  8. package/dist/app-typed-client-YIBP6JWH.js.map +1 -0
  9. package/dist/{aws-lambda-PONVF3UX.js → aws-lambda-XYCGZH3I.js} +2 -2
  10. package/dist/{build-IQQYCA3B.js → build-NCGIV4LL.js} +41 -14
  11. package/dist/build-NCGIV4LL.js.map +1 -0
  12. package/dist/build-request-body-preview-EOP2Y4F3.js +52 -0
  13. package/dist/build-request-body-preview-EOP2Y4F3.js.map +1 -0
  14. package/dist/build-request-body-preview-II2235E6.js +54 -0
  15. package/dist/build-request-body-preview-II2235E6.js.map +1 -0
  16. package/dist/{bun-DITMZMKL.js → bun-K73TQEWC.js} +2 -2
  17. package/dist/{chunk-GQSTRXXB.js → chunk-2BQYEBCK.js} +77 -68
  18. package/dist/chunk-2BQYEBCK.js.map +1 -0
  19. package/dist/chunk-7DHGV77J.js +140 -0
  20. package/dist/chunk-7DHGV77J.js.map +1 -0
  21. package/dist/{chunk-WQCPKA2L.js → chunk-C27AZN46.js} +570 -165
  22. package/dist/chunk-C27AZN46.js.map +1 -0
  23. package/dist/chunk-ERAZNHHR.js +73 -0
  24. package/dist/chunk-ERAZNHHR.js.map +1 -0
  25. package/dist/{chunk-ZK5JCE5J.js → chunk-EXJWO2HB.js} +201 -43
  26. package/dist/chunk-EXJWO2HB.js.map +1 -0
  27. package/dist/chunk-F3TG5FJV.js +97 -0
  28. package/dist/chunk-F3TG5FJV.js.map +1 -0
  29. package/dist/{chunk-OK2PJOLK.js → chunk-KJVEJILQ.js} +7 -3
  30. package/dist/chunk-KJVEJILQ.js.map +1 -0
  31. package/dist/{chunk-JBC7PFV4.js → chunk-M23FP2AS.js} +2 -2
  32. package/dist/chunk-M23FP2AS.js.map +1 -0
  33. package/dist/{chunk-FRBYJANT.js → chunk-M6MYZSD2.js} +195 -41
  34. package/dist/chunk-M6MYZSD2.js.map +1 -0
  35. package/dist/{chunk-S3JBIFKS.js → chunk-MG7H3LAW.js} +2 -2
  36. package/dist/chunk-MOT42NAH.js +165 -0
  37. package/dist/chunk-MOT42NAH.js.map +1 -0
  38. package/dist/{chunk-OEB3OIYP.js → chunk-OJHO2X7C.js} +497 -168
  39. package/dist/chunk-OJHO2X7C.js.map +1 -0
  40. package/dist/chunk-QLGES2HC.js +189 -0
  41. package/dist/chunk-QLGES2HC.js.map +1 -0
  42. package/dist/{chunk-JZ5WEG3S.js → chunk-SEVYARIU.js} +9 -5
  43. package/dist/chunk-SEVYARIU.js.map +1 -0
  44. package/dist/chunk-WSJKACWB.js +17 -0
  45. package/dist/chunk-WSJKACWB.js.map +1 -0
  46. package/dist/{chunk-3Q36SEDK.js → chunk-ZBWROIIO.js} +43 -2
  47. package/dist/{chunk-3Q36SEDK.js.map → chunk-ZBWROIIO.js.map} +1 -1
  48. package/dist/{chunk-Y5KE6YFP.js → chunk-ZJW5FFYJ.js} +3 -3
  49. package/dist/chunk-ZJW5FFYJ.js.map +1 -0
  50. package/dist/chunk-ZOXNJV2O.js +358 -0
  51. package/dist/chunk-ZOXNJV2O.js.map +1 -0
  52. package/dist/cli/index.js +22 -7
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/client/index.d.ts +31 -1
  55. package/dist/client/index.js +100 -0
  56. package/dist/client/index.js.map +1 -1
  57. package/dist/{cloudflare-RBANU3SE.js → cloudflare-LCAOTRYZ.js} +2 -2
  58. package/dist/{deno-deploy-J7LI6HNL.js → deno-deploy-ZN4RE5HF.js} +2 -2
  59. package/dist/{dev-VN6WS3CM.js → dev-WDXC74CK.js} +15 -7
  60. package/dist/dev-WDXC74CK.js.map +1 -0
  61. package/dist/dev-emit-3CHM73TV.js +390 -0
  62. package/dist/dev-emit-3CHM73TV.js.map +1 -0
  63. package/dist/dev-emit-I3CKOHML.js +44 -0
  64. package/dist/dev-emit-I3CKOHML.js.map +1 -0
  65. package/dist/devtools/entry.js +545 -181
  66. package/dist/devtools/entry.js.map +1 -1
  67. package/dist/{dispatcher-IK3FVMX5.js → dispatcher-AQJGI3HU.js} +2 -2
  68. package/dist/dispatcher-E6QV32BO.js +83 -0
  69. package/dist/dispatcher-E6QV32BO.js.map +1 -0
  70. package/dist/{dist-Q5VBFWVL.js → dist-R2Q7GVDW.js} +10357 -12068
  71. package/dist/dist-R2Q7GVDW.js.map +1 -0
  72. package/dist/{generate-SIO4JSKJ.js → generate-DT4IIAHF.js} +88 -22
  73. package/dist/generate-DT4IIAHF.js.map +1 -0
  74. package/dist/index.d.ts +108 -1
  75. package/dist/index.js +5 -2
  76. package/dist/index.js.map +1 -1
  77. package/dist/{info-B3UOXFUF.js → info-VM7EK4JC.js} +3 -3
  78. package/dist/{netlify-IFY6ZGFV.js → netlify-5VQ22Z2P.js} +2 -2
  79. package/dist/{node-KWZI3CKV.js → node-3APTLGWB.js} +2 -2
  80. package/dist/openapi-CBASKC7W.js +84 -0
  81. package/dist/openapi-CBASKC7W.js.map +1 -0
  82. package/dist/registry-5QFTLOL2.js +25 -0
  83. package/dist/{routes-CAGFZKUK.js → routes-7O4NUDYS.js} +9 -7
  84. package/dist/{routes-CAGFZKUK.js.map → routes-7O4NUDYS.js.map} +1 -1
  85. package/dist/{schema-Cxq2qqwh.d.ts → schema-CjVly9c_.d.ts} +43 -1
  86. package/dist/{schema-MVB2FVK6.js → schema-JAKF23GR.js} +2 -2
  87. package/dist/server/cost/index.js +1 -1
  88. package/dist/server/index.d.ts +219 -47
  89. package/dist/server/index.js +35 -116
  90. package/dist/server/index.js.map +1 -1
  91. package/dist/{start-ZGWW5MOL.js → start-5JTFWF4T.js} +20 -14
  92. package/dist/start-5JTFWF4T.js.map +1 -0
  93. package/dist/{static-BH6JQNM2.js → static-TZBVBDTB.js} +4 -4
  94. package/dist/{upgrade-readiness-OHL36FQL.js → upgrade-readiness-Q46KTG2J.js} +6 -1
  95. package/dist/upgrade-readiness-Q46KTG2J.js.map +1 -0
  96. package/dist/{vercel-RHADE5IP.js → vercel-MWW24ABC.js} +2 -2
  97. package/dist/vite-plugin/index.d.ts +25 -1
  98. package/dist/vite-plugin/index.js +5 -2
  99. package/dist/{vite-plugin-WA3G45YY.js → vite-plugin-4WZ3C2AC.js} +7 -6
  100. package/package.json +4 -2
  101. package/dist/build-IQQYCA3B.js.map +0 -1
  102. package/dist/chunk-FRBYJANT.js.map +0 -1
  103. package/dist/chunk-GQSTRXXB.js.map +0 -1
  104. package/dist/chunk-JBC7PFV4.js.map +0 -1
  105. package/dist/chunk-JZ5WEG3S.js.map +0 -1
  106. package/dist/chunk-OEB3OIYP.js.map +0 -1
  107. package/dist/chunk-OK2PJOLK.js.map +0 -1
  108. package/dist/chunk-WQCPKA2L.js.map +0 -1
  109. package/dist/chunk-Y5KE6YFP.js.map +0 -1
  110. package/dist/chunk-ZK5JCE5J.js.map +0 -1
  111. package/dist/dev-VN6WS3CM.js.map +0 -1
  112. package/dist/dist-Q5VBFWVL.js.map +0 -1
  113. package/dist/generate-SIO4JSKJ.js.map +0 -1
  114. package/dist/registry-BB3UYAOG.js +0 -25
  115. package/dist/start-ZGWW5MOL.js.map +0 -1
  116. package/dist/upgrade-readiness-OHL36FQL.js.map +0 -1
  117. /package/dist/{aws-lambda-PONVF3UX.js.map → aws-lambda-XYCGZH3I.js.map} +0 -0
  118. /package/dist/{bun-DITMZMKL.js.map → bun-K73TQEWC.js.map} +0 -0
  119. /package/dist/{chunk-S3JBIFKS.js.map → chunk-MG7H3LAW.js.map} +0 -0
  120. /package/dist/{cloudflare-RBANU3SE.js.map → cloudflare-LCAOTRYZ.js.map} +0 -0
  121. /package/dist/{deno-deploy-J7LI6HNL.js.map → deno-deploy-ZN4RE5HF.js.map} +0 -0
  122. /package/dist/{dispatcher-IK3FVMX5.js.map → dispatcher-AQJGI3HU.js.map} +0 -0
  123. /package/dist/{info-B3UOXFUF.js.map → info-VM7EK4JC.js.map} +0 -0
  124. /package/dist/{netlify-IFY6ZGFV.js.map → netlify-5VQ22Z2P.js.map} +0 -0
  125. /package/dist/{node-KWZI3CKV.js.map → node-3APTLGWB.js.map} +0 -0
  126. /package/dist/{registry-BB3UYAOG.js.map → registry-5QFTLOL2.js.map} +0 -0
  127. /package/dist/{schema-MVB2FVK6.js.map → schema-JAKF23GR.js.map} +0 -0
  128. /package/dist/{static-BH6JQNM2.js.map → static-TZBVBDTB.js.map} +0 -0
  129. /package/dist/{vercel-RHADE5IP.js.map → vercel-MWW24ABC.js.map} +0 -0
  130. /package/dist/{vite-plugin-WA3G45YY.js.map → vite-plugin-4WZ3C2AC.js.map} +0 -0
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ import "tsx/esm";
3
+ import {
4
+ walkSourceFiles
5
+ } from "./chunk-MOT42NAH.js";
6
+
7
+ // src/core/contracts/http-methods.ts
8
+ var HTTP_METHODS = [
9
+ "GET",
10
+ "POST",
11
+ "PUT",
12
+ "PATCH",
13
+ "DELETE",
14
+ "HEAD",
15
+ "OPTIONS"
16
+ ];
17
+ var HTTP_METHOD_LOWERCASE = HTTP_METHODS.map((m) => m.toLowerCase());
18
+
19
+ // src/server/scan/scan.ts
20
+ import { existsSync, statSync } from "fs";
21
+ import { extname, join, relative } from "path";
22
+
23
+ // src/server/scan/detect-http-methods.ts
24
+ import { readFileSync } from "fs";
25
+ import { createRequire } from "module";
26
+ var require_ = createRequire(import.meta.url);
27
+ var ts = require_("typescript");
28
+ var HTTP_METHOD_NAMES = new Set(HTTP_METHODS);
29
+ function hasExportModifier(modifiers) {
30
+ if (!modifiers) return false;
31
+ for (const m of modifiers) {
32
+ if (m.kind === ts.SyntaxKind.ExportKeyword) return true;
33
+ }
34
+ return false;
35
+ }
36
+ function collectFromStatement(stmt, found) {
37
+ if (ts.isVariableStatement(stmt) && hasExportModifier(ts.getModifiers(stmt))) {
38
+ for (const decl of stmt.declarationList.declarations) {
39
+ if (ts.isIdentifier(decl.name) && HTTP_METHOD_NAMES.has(decl.name.text)) {
40
+ found.add(decl.name.text);
41
+ }
42
+ }
43
+ return;
44
+ }
45
+ if ((ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) && hasExportModifier(ts.getModifiers(stmt))) {
46
+ if (stmt.name && HTTP_METHOD_NAMES.has(stmt.name.text)) {
47
+ found.add(stmt.name.text);
48
+ }
49
+ return;
50
+ }
51
+ if (ts.isExportDeclaration(stmt) && stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
52
+ for (const spec of stmt.exportClause.elements) {
53
+ if (HTTP_METHOD_NAMES.has(spec.name.text)) {
54
+ found.add(spec.name.text);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ function detectExportedHttpMethods(filePath, content) {
60
+ const src = content ?? readFileSync(filePath, "utf-8");
61
+ const sourceFile = ts.createSourceFile(
62
+ filePath,
63
+ src,
64
+ ts.ScriptTarget.Latest,
65
+ /* setParentNodes */
66
+ false,
67
+ ts.ScriptKind.TS
68
+ );
69
+ const found = /* @__PURE__ */ new Set();
70
+ for (const stmt of sourceFile.statements) {
71
+ collectFromStatement(stmt, found);
72
+ }
73
+ return [...found].sort();
74
+ }
75
+
76
+ // src/server/scan/match.ts
77
+ function compilePattern(routePath) {
78
+ const paramNames = [];
79
+ const regexStr = routePath.replace(/:(?:\.\.\.)?([^/]+)/g, (match, name) => {
80
+ paramNames.push(name);
81
+ return match.startsWith(":...") ? "(.+)" : "([^/]+)";
82
+ });
83
+ return { pattern: new RegExp(`^${regexStr}$`), paramNames };
84
+ }
85
+ function matchRoute(url, routes) {
86
+ let path = url.split("?")[0];
87
+ if (path.length > 1 && path.endsWith("/")) {
88
+ path = path.slice(0, -1);
89
+ }
90
+ for (const route of routes) {
91
+ const match = route.pattern.exec(path);
92
+ if (match) {
93
+ const params = {};
94
+ route.paramNames.forEach((name, i) => {
95
+ params[name] = match[i + 1];
96
+ });
97
+ return { route, params };
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+
103
+ // src/server/scan/scan.ts
104
+ var ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
105
+ function fileToRoutePath(filePath, routesDir) {
106
+ let rel = relative(routesDir, filePath);
107
+ const ext = extname(rel);
108
+ rel = rel.slice(0, -ext.length);
109
+ rel = rel.replace(/\\/g, "/");
110
+ if (rel.endsWith("/index")) {
111
+ rel = rel.slice(0, -6);
112
+ } else if (rel === "index") {
113
+ rel = "";
114
+ }
115
+ rel = rel.replace(/\[\.\.\.([^\]]+)\]/g, ":...$1");
116
+ rel = rel.replace(/\[([^\]]+)\]/g, ":$1");
117
+ return `/api/${rel}`;
118
+ }
119
+ function scanServerRoutes(serverDir) {
120
+ const routesDir = join(serverDir, "routes");
121
+ if (!existsSync(routesDir) || !statSync(routesDir).isDirectory()) {
122
+ return [];
123
+ }
124
+ const results = [];
125
+ walkSourceFiles(routesDir, { extensions: ROUTE_EXTENSIONS }, (absPath) => {
126
+ const routePath = fileToRoutePath(absPath, routesDir);
127
+ const { pattern, paramNames } = compilePattern(routePath);
128
+ const methods = detectExportedHttpMethods(absPath);
129
+ results.push({
130
+ filePath: absPath,
131
+ routePath,
132
+ paramNames,
133
+ pattern,
134
+ methods
135
+ });
136
+ });
137
+ const conflicting = results.find((r) => r.routePath === "/api/__theo_batch__");
138
+ if (conflicting) {
139
+ throw new Error(
140
+ `Server route ${conflicting.filePath} resolves to '/api/__theo_batch__' which is reserved for the batch endpoint. Rename the route or disable batching in theo.config.ts.`
141
+ );
142
+ }
143
+ const isCatchAll = (route) => route.routePath.includes(":...");
144
+ results.sort((a, b) => {
145
+ const aStatic = a.paramNames.length === 0;
146
+ const bStatic = b.paramNames.length === 0;
147
+ const aCatchAll = isCatchAll(a);
148
+ const bCatchAll = isCatchAll(b);
149
+ if (aStatic && !bStatic) return -1;
150
+ if (!aStatic && bStatic) return 1;
151
+ if (aCatchAll && !bCatchAll) return 1;
152
+ if (!aCatchAll && bCatchAll) return -1;
153
+ return a.routePath.localeCompare(b.routePath);
154
+ });
155
+ return results;
156
+ }
157
+
158
+ // src/server/scan/ws-scan.ts
159
+ import { existsSync as existsSync2, statSync as statSync2 } from "fs";
160
+ import { extname as extname2, join as join2, relative as relative2 } from "path";
161
+ var WS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
162
+ function scanWebSocketRoutes(serverDir) {
163
+ const wsDir = join2(serverDir, "ws");
164
+ if (!existsSync2(wsDir) || !statSync2(wsDir).isDirectory()) {
165
+ return [];
166
+ }
167
+ const results = [];
168
+ walkSourceFiles(wsDir, { extensions: WS_EXTENSIONS }, (absPath) => {
169
+ let rel = relative2(wsDir, absPath);
170
+ rel = rel.replace(/\\/g, "/");
171
+ rel = rel.slice(0, -extname2(rel).length);
172
+ if (rel.endsWith("/index")) rel = rel.slice(0, -6);
173
+ else if (rel === "index") rel = "";
174
+ results.push({
175
+ filePath: absPath,
176
+ wsPath: `/ws/${rel}`
177
+ });
178
+ });
179
+ return results;
180
+ }
181
+
182
+ export {
183
+ compilePattern,
184
+ matchRoute,
185
+ HTTP_METHODS,
186
+ scanServerRoutes,
187
+ scanWebSocketRoutes
188
+ };
189
+ //# sourceMappingURL=chunk-QLGES2HC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/contracts/http-methods.ts","../src/server/scan/scan.ts","../src/server/scan/detect-http-methods.ts","../src/server/scan/match.ts","../src/server/scan/ws-scan.ts"],"sourcesContent":["/**\n * Canonical HTTP method set used by the typed client codegen + Proxy runtime.\n * Kept here (under `server/scan/`) because the SCAN is the first consumer —\n * `vite-plugin/app-typed-client.ts` (Phase 2) and `client/app-client.ts` (Phase 3)\n * import from here to ensure a single source of truth.\n *\n * Lowercase variants are derived via `.toLowerCase()` at call sites; we do not\n * export them separately to avoid drift between the two lists.\n */\n\nexport const HTTP_METHODS = [\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'HEAD',\n 'OPTIONS',\n] as const\n\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\nexport const HTTP_METHOD_LOWERCASE = HTTP_METHODS.map((m) => m.toLowerCase()) as readonly string[]\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: walks `serverDir/routes/` derived from cwd.\n * No HTTP input ever reaches these fs calls.\n */\nimport { existsSync, statSync } from 'node:fs'\nimport { extname, join, relative } from 'node:path'\n\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nimport { detectExportedHttpMethods } from './detect-http-methods.js'\nimport { compilePattern, type ServerRouteNode } from './match.js'\n\nconst ROUTE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\nfunction fileToRoutePath(filePath: string, routesDir: string): string {\n let rel = relative(routesDir, filePath)\n // Strip extension\n const ext = extname(rel)\n rel = rel.slice(0, -ext.length)\n // Normalize separators\n rel = rel.replace(/\\\\/g, '/')\n // Strip index suffix\n if (rel.endsWith('/index')) {\n rel = rel.slice(0, -6)\n } else if (rel === 'index') {\n rel = ''\n }\n // Replace [...param] with :...param (catch-all, before regular params).\n // Replace [param] with :param. Inputs are file paths bounded by the\n // OS filename limit; the bracket capture is bounded by `]`.\n rel = rel.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, ':...$1')\n // eslint-disable-next-line sonarjs/slow-regex -- bounded by `]`; input is a single filename\n rel = rel.replace(/\\[([^\\]]+)\\]/g, ':$1')\n return `/api/${rel}`\n}\n\nexport function scanServerRoutes(serverDir: string): ServerRouteNode[] {\n const routesDir = join(serverDir, 'routes')\n if (!existsSync(routesDir) || !statSync(routesDir).isDirectory()) {\n return []\n }\n\n const results: ServerRouteNode[] = []\n walkSourceFiles(routesDir, { extensions: ROUTE_EXTENSIONS }, (absPath) => {\n const routePath = fileToRoutePath(absPath, routesDir)\n const { pattern, paramNames } = compilePattern(routePath)\n const methods = detectExportedHttpMethods(absPath)\n results.push({\n filePath: absPath,\n routePath,\n paramNames,\n pattern,\n methods,\n })\n })\n\n // T1.4 / EC-2 — refuse to scan if a user route collides with the reserved\n // batch endpoint path. User must rename or disable batching.\n const conflicting = results.find((r) => r.routePath === '/api/__theo_batch__')\n if (conflicting) {\n throw new Error(\n `Server route ${conflicting.filePath} resolves to '/api/__theo_batch__' which is reserved for the batch endpoint. Rename the route or disable batching in theo.config.ts.`,\n )\n }\n\n // Sort: static first, then dynamic, then catch-all last\n const isCatchAll = (route: ServerRouteNode) => route.routePath.includes(':...')\n results.sort((a, b) => {\n const aStatic = a.paramNames.length === 0\n const bStatic = b.paramNames.length === 0\n const aCatchAll = isCatchAll(a)\n const bCatchAll = isCatchAll(b)\n\n // Static routes first\n if (aStatic && !bStatic) return -1\n if (!aStatic && bStatic) return 1\n // Catch-all routes last\n if (aCatchAll && !bCatchAll) return 1\n if (!aCatchAll && bCatchAll) return -1\n return a.routePath.localeCompare(b.routePath)\n })\n\n return results\n}\n","/**\n * Detect which HTTP-method named exports a route file declares.\n *\n * Uses the TypeScript compiler API (not regex) per G1 edge-case review EC-4:\n * regex over file content emits false positives for `// export const GET = ...`\n * in comments and `` `export const GET = ...` `` in template literals. AST\n * walking avoids both classes of bug.\n *\n * `typescript` ships as CommonJS with internal dynamic `require('fs')`.\n * When loaded via ESM `import`, the dynamic requires fail at module-bootstrap.\n * We use `createRequire(import.meta.url)` to keep the package on its native\n * CJS path. The type-only namespace import gives us the AST helpers shape.\n *\n * Returns the set of HTTP methods (uppercase) the file exports. Empty array\n * means the file has no HTTP exports (the route file is util-only).\n */\n\nimport { readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\n\nimport type * as TS from 'typescript'\n\nimport { HTTP_METHODS, type HttpMethod } from '../../core/contracts/http-methods.js'\n\nconst require_ = createRequire(import.meta.url)\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst ts = require_('typescript') as typeof TS\n\nconst HTTP_METHOD_NAMES = new Set<string>(HTTP_METHODS)\n\nfunction hasExportModifier(modifiers: readonly TS.Modifier[] | undefined): boolean {\n if (!modifiers) return false\n for (const m of modifiers) {\n if (m.kind === ts.SyntaxKind.ExportKeyword) return true\n }\n return false\n}\n\nfunction collectFromStatement(stmt: TS.Statement, found: Set<HttpMethod>): void {\n // `export const GET = ...` / `export function GET ...` / `export async function GET ...`\n if (ts.isVariableStatement(stmt) && hasExportModifier(ts.getModifiers(stmt))) {\n for (const decl of stmt.declarationList.declarations) {\n if (ts.isIdentifier(decl.name) && HTTP_METHOD_NAMES.has(decl.name.text)) {\n found.add(decl.name.text as HttpMethod)\n }\n }\n return\n }\n\n if (\n (ts.isFunctionDeclaration(stmt) || ts.isClassDeclaration(stmt)) &&\n hasExportModifier(ts.getModifiers(stmt))\n ) {\n if (stmt.name && HTTP_METHOD_NAMES.has(stmt.name.text)) {\n found.add(stmt.name.text as HttpMethod)\n }\n return\n }\n\n // `export { GET }` / `export { handler as GET } from './shared'` (EC-5)\n if (ts.isExportDeclaration(stmt) && stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {\n for (const spec of stmt.exportClause.elements) {\n // spec.name is the exported (re-)name; spec.propertyName is the original (when renamed)\n if (HTTP_METHOD_NAMES.has(spec.name.text)) {\n found.add(spec.name.text as HttpMethod)\n }\n }\n }\n}\n\nexport function detectExportedHttpMethods(filePath: string, content?: string): HttpMethod[] {\n const src = content ?? readFileSync(filePath, 'utf-8')\n const sourceFile = ts.createSourceFile(\n filePath,\n src,\n ts.ScriptTarget.Latest,\n /* setParentNodes */ false,\n ts.ScriptKind.TS,\n )\n const found = new Set<HttpMethod>()\n for (const stmt of sourceFile.statements) {\n collectFromStatement(stmt, found)\n }\n return [...found].sort()\n}\n","export interface ServerRouteNode {\n filePath: string\n routePath: string\n paramNames: string[]\n pattern: RegExp\n /** HTTP methods (uppercase) the route file exports. Optional for backward\n * compatibility with manifests generated before G1. Empty array means the\n * file has no HTTP exports (util-only); undefined means \"not detected\". */\n methods?: string[]\n}\n\nexport function compilePattern(routePath: string): {\n pattern: RegExp\n paramNames: string[]\n} {\n const paramNames: string[] = []\n // Single pass: handle both catch-all (:...name) and regular (:name) params\n const regexStr = routePath.replace(/:(?:\\.\\.\\.)?([^/]+)/g, (match: string, name: string) => {\n paramNames.push(name)\n // Catch-all matches across slashes, regular matches single segment\n return match.startsWith(':...') ? '(.+)' : '([^/]+)'\n })\n // `regexStr` is derived from a developer-authored route path (build-time\n // input, not HTTP-controlled). The `security/detect-non-literal-regexp`\n // rule cannot see this constraint — disable narrowly.\n // eslint-disable-next-line security/detect-non-literal-regexp -- route pattern from build-time scan, never HTTP input\n return { pattern: new RegExp(`^${regexStr}$`), paramNames }\n}\n\nexport function matchRoute(\n url: string,\n routes: ServerRouteNode[],\n): { route: ServerRouteNode; params: Record<string, string> } | null {\n // Strip query string and trailing slash\n let path = url.split('?')[0]\n if (path.length > 1 && path.endsWith('/')) {\n path = path.slice(0, -1)\n }\n\n for (const route of routes) {\n const match = route.pattern.exec(path)\n if (match) {\n const params: Record<string, string> = {}\n route.paramNames.forEach((name, i) => {\n params[name] = match[i + 1]\n })\n return { route, params }\n }\n }\n return null\n}\n","/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time scanner: walks `serverDir/ws/` derived from cwd.\n * No HTTP input ever reaches these fs calls.\n */\nimport { existsSync, statSync } from 'node:fs'\nimport { extname, join, relative } from 'node:path'\n\nimport { walkSourceFiles } from '../_internal/scan-walker.js'\n\nconst WS_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx'])\n\nexport interface WebSocketRouteNode {\n filePath: string\n wsPath: string\n}\n\nexport function scanWebSocketRoutes(serverDir: string): WebSocketRouteNode[] {\n const wsDir = join(serverDir, 'ws')\n if (!existsSync(wsDir) || !statSync(wsDir).isDirectory()) {\n return []\n }\n\n const results: WebSocketRouteNode[] = []\n walkSourceFiles(wsDir, { extensions: WS_EXTENSIONS }, (absPath) => {\n let rel = relative(wsDir, absPath)\n rel = rel.replace(/\\\\/g, '/')\n rel = rel.slice(0, -extname(rel).length)\n if (rel.endsWith('/index')) rel = rel.slice(0, -6)\n else if (rel === 'index') rel = ''\n results.push({\n filePath: absPath,\n wsPath: `/ws/${rel}`,\n })\n })\n return results\n}\n"],"mappings":"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","names":["existsSync","statSync","extname","join","relative","join","existsSync","statSync","relative","extname"]}
@@ -2,10 +2,12 @@
2
2
  import "tsx/esm";
3
3
  import {
4
4
  compilePattern,
5
- scanServerActions,
6
5
  scanServerRoutes,
7
6
  scanWebSocketRoutes
8
- } from "./chunk-GQSTRXXB.js";
7
+ } from "./chunk-QLGES2HC.js";
8
+ import {
9
+ scanServerActions
10
+ } from "./chunk-MOT42NAH.js";
9
11
 
10
12
  // src/server/scan/manifest.ts
11
13
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
@@ -20,7 +22,8 @@ function generateManifest(serverDir) {
20
22
  routes: routes.map((r) => ({
21
23
  filePath: relative(serverDir, r.filePath),
22
24
  routePath: r.routePath,
23
- paramNames: r.paramNames
25
+ paramNames: r.paramNames,
26
+ ...r.methods !== void 0 ? { methods: r.methods } : {}
24
27
  })),
25
28
  actions: actions.map((a) => ({
26
29
  filePath: relative(serverDir, a.filePath),
@@ -49,7 +52,8 @@ function loadManifest(distDir, serverDir) {
49
52
  filePath: resolve(serverDir, r.filePath),
50
53
  routePath: r.routePath,
51
54
  paramNames,
52
- pattern
55
+ pattern,
56
+ ...r.methods !== void 0 ? { methods: r.methods } : {}
53
57
  };
54
58
  });
55
59
  const actions = raw.actions.map((a) => ({
@@ -68,4 +72,4 @@ export {
68
72
  writeManifest,
69
73
  loadManifest
70
74
  };
71
- //# sourceMappingURL=chunk-JZ5WEG3S.js.map
75
+ //# sourceMappingURL=chunk-SEVYARIU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/scan/manifest.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Build-time manifest emitter / loader. All paths derived from `distDir`\n * + `serverDir`, themselves resolved from `process.cwd()`. No HTTP input.\n */\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join, resolve, relative } from 'node:path'\n\nimport { scanServerActions } from './action-scan.js'\nimport type { ActionNode } from './action-scan.js'\nimport { compilePattern } from './match.js'\nimport type { ServerRouteNode } from './match.js'\nimport { scanServerRoutes } from './scan.js'\nimport { scanWebSocketRoutes } from './ws-scan.js'\nimport type { WebSocketRouteNode } from './ws-scan.js'\n\n// --- Manifest Types ---\n\nexport interface ManifestRoute {\n filePath: string\n routePath: string\n paramNames: string[]\n /** HTTP methods (uppercase) the route file exports. Optional — manifests\n * generated before G1 omit this; loaders treat absence as \"unknown\". */\n methods?: string[]\n}\n\nexport interface ManifestAction {\n filePath: string\n actionPath: string\n}\n\nexport interface ManifestWebSocket {\n filePath: string\n wsPath: string\n}\n\nexport interface TheoManifest {\n version: 1\n generatedAt: string\n routes: ManifestRoute[]\n actions: ManifestAction[]\n websockets: ManifestWebSocket[]\n}\n\nexport interface LoadedManifest {\n routes: ServerRouteNode[]\n actions: ActionNode[]\n websockets: WebSocketRouteNode[]\n}\n\n// --- Generate ---\n\nexport function generateManifest(serverDir: string): TheoManifest {\n const routes = scanServerRoutes(serverDir)\n const actions = scanServerActions(serverDir)\n const websockets = scanWebSocketRoutes(serverDir)\n\n return {\n version: 1,\n generatedAt: new Date().toISOString(),\n routes: routes.map((r) => ({\n filePath: relative(serverDir, r.filePath),\n routePath: r.routePath,\n paramNames: r.paramNames,\n ...(r.methods !== undefined ? { methods: r.methods } : {}),\n })),\n actions: actions.map((a) => ({\n filePath: relative(serverDir, a.filePath),\n actionPath: a.actionPath,\n })),\n websockets: websockets.map((w) => ({\n filePath: relative(serverDir, w.filePath),\n wsPath: w.wsPath,\n })),\n }\n}\n\n// --- Write ---\n\nexport function writeManifest(manifest: TheoManifest, outputDir: string): void {\n mkdirSync(outputDir, { recursive: true })\n const manifestPath = join(outputDir, 'manifest.json')\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))\n}\n\n// --- Load ---\n\nexport function loadManifest(distDir: string, serverDir: string): LoadedManifest {\n const manifestPath = join(distDir, 'manifest.json')\n\n if (!existsSync(manifestPath)) {\n throw new Error(`No manifest found at ${manifestPath}. Run \"theo build\" first.`)\n }\n\n const raw = JSON.parse(readFileSync(manifestPath, 'utf-8')) as TheoManifest\n\n const routes: ServerRouteNode[] = raw.routes.map((r) => {\n const { pattern, paramNames } = compilePattern(r.routePath)\n return {\n filePath: resolve(serverDir, r.filePath),\n routePath: r.routePath,\n paramNames,\n pattern,\n ...(r.methods !== undefined ? { methods: r.methods } : {}),\n }\n })\n\n const actions: ActionNode[] = raw.actions.map((a) => ({\n filePath: resolve(serverDir, a.filePath),\n actionPath: a.actionPath,\n }))\n\n const websockets: WebSocketRouteNode[] = raw.websockets.map((w) => ({\n filePath: resolve(serverDir, w.filePath),\n wsPath: w.wsPath,\n }))\n\n return { routes, actions, websockets }\n}\n"],"mappings":";;;;;;;;;;;;AAIA,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,MAAM,SAAS,gBAAgB;AA+CjC,SAAS,iBAAiB,WAAiC;AAChE,QAAM,SAAS,iBAAiB,SAAS;AACzC,QAAM,UAAU,kBAAkB,SAAS;AAC3C,QAAM,aAAa,oBAAoB,SAAS;AAEhD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,QAAQ,OAAO,IAAI,CAAC,OAAO;AAAA,MACzB,UAAU,SAAS,WAAW,EAAE,QAAQ;AAAA,MACxC,WAAW,EAAE;AAAA,MACb,YAAY,EAAE;AAAA,MACd,GAAI,EAAE,YAAY,SAAY,EAAE,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1D,EAAE;AAAA,IACF,SAAS,QAAQ,IAAI,CAAC,OAAO;AAAA,MAC3B,UAAU,SAAS,WAAW,EAAE,QAAQ;AAAA,MACxC,YAAY,EAAE;AAAA,IAChB,EAAE;AAAA,IACF,YAAY,WAAW,IAAI,CAAC,OAAO;AAAA,MACjC,UAAU,SAAS,WAAW,EAAE,QAAQ;AAAA,MACxC,QAAQ,EAAE;AAAA,IACZ,EAAE;AAAA,EACJ;AACF;AAIO,SAAS,cAAc,UAAwB,WAAyB;AAC7E,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,eAAe,KAAK,WAAW,eAAe;AACpD,gBAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC/D;AAIO,SAAS,aAAa,SAAiB,WAAmC;AAC/E,QAAM,eAAe,KAAK,SAAS,eAAe;AAElD,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,UAAM,IAAI,MAAM,wBAAwB,YAAY,2BAA2B;AAAA,EACjF;AAEA,QAAM,MAAM,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAE1D,QAAM,SAA4B,IAAI,OAAO,IAAI,CAAC,MAAM;AACtD,UAAM,EAAE,SAAS,WAAW,IAAI,eAAe,EAAE,SAAS;AAC1D,WAAO;AAAA,MACL,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,MACvC,WAAW,EAAE;AAAA,MACb;AAAA,MACA;AAAA,MACA,GAAI,EAAE,YAAY,SAAY,EAAE,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,QAAM,UAAwB,IAAI,QAAQ,IAAI,CAAC,OAAO;AAAA,IACpD,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,YAAY,EAAE;AAAA,EAChB,EAAE;AAEF,QAAM,aAAmC,IAAI,WAAW,IAAI,CAAC,OAAO;AAAA,IAClE,UAAU,QAAQ,WAAW,EAAE,QAAQ;AAAA,IACvC,QAAQ,EAAE;AAAA,EACZ,EAAE;AAEF,SAAO,EAAE,QAAQ,SAAS,WAAW;AACvC;","names":[]}
@@ -0,0 +1,17 @@
1
+ // src/core/contracts/http-methods.ts
2
+ var HTTP_METHODS = [
3
+ "GET",
4
+ "POST",
5
+ "PUT",
6
+ "PATCH",
7
+ "DELETE",
8
+ "HEAD",
9
+ "OPTIONS"
10
+ ];
11
+ var HTTP_METHOD_LOWERCASE = HTTP_METHODS.map((m) => m.toLowerCase());
12
+
13
+ export {
14
+ HTTP_METHODS,
15
+ HTTP_METHOD_LOWERCASE
16
+ };
17
+ //# sourceMappingURL=chunk-WSJKACWB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/contracts/http-methods.ts"],"sourcesContent":["/**\n * Canonical HTTP method set used by the typed client codegen + Proxy runtime.\n * Kept here (under `server/scan/`) because the SCAN is the first consumer —\n * `vite-plugin/app-typed-client.ts` (Phase 2) and `client/app-client.ts` (Phase 3)\n * import from here to ensure a single source of truth.\n *\n * Lowercase variants are derived via `.toLowerCase()` at call sites; we do not\n * export them separately to avoid drift between the two lists.\n */\n\nexport const HTTP_METHODS = [\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'HEAD',\n 'OPTIONS',\n] as const\n\nexport type HttpMethod = (typeof HTTP_METHODS)[number]\n\nexport const HTTP_METHOD_LOWERCASE = HTTP_METHODS.map((m) => m.toLowerCase()) as readonly string[]\n"],"mappings":";AAUO,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,wBAAwB,aAAa,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;","names":[]}
@@ -238,6 +238,21 @@ var theoConfigSchema = z.object({
238
238
  * matching `--target`, `theokit build` emits a cross-reference note.
239
239
  */
240
240
  adapters: z.array(z.string()).optional(),
241
+ /**
242
+ * Extra package names to pre-bundle via Vite's `optimizeDeps.include`.
243
+ *
244
+ * Framework auto-includes `@usetheo/ui` and `lucide-react` when present.
245
+ * Apps adding plugin peer-deps that rely on a runtime `import('<pkg>')`
246
+ * (dynamic specifier) — e.g. `@theokit/plugin-canvas` consumers
247
+ * installing `mermaid` for Mermaid SVG rendering — must declare those
248
+ * here so Vite can resolve the literal in dev mode without
249
+ * "Failed to resolve module specifier" errors. Production builds use
250
+ * tsup/esbuild bundling and are not affected.
251
+ *
252
+ * Example:
253
+ * viteOptimizeDeps: ['mermaid']
254
+ */
255
+ viteOptimizeDeps: z.array(z.string()).optional(),
241
256
  /**
242
257
  * Jobs backend (ADR D3 + T2.1). When configured, every request gets
243
258
  * `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a
@@ -264,7 +279,33 @@ var theoConfigSchema = z.object({
264
279
  * that boot alongside the TheoKit TS app. Empty `services: {}` is the
265
280
  * default and preserves Wave 1 behavior.
266
281
  */
267
- services: servicesConfigSchema
282
+ services: servicesConfigSchema,
283
+ /**
284
+ * G2 — OpenAPI emit (build-time only).
285
+ *
286
+ * When present, `theokit build` writes a generated `openapi.json` from
287
+ * every `defineRoute()` body/query/params Zod schema. `undefined` keeps
288
+ * the framework backward-compatible (no emit). Pass `openapi: {}` to
289
+ * opt in with defaults.
290
+ *
291
+ * Defaults: spec 3.1.0 · servers `http://localhost:3000` · title
292
+ * "TheoKit App" · version "0.0.0" · outDir ".theo" (next to dist).
293
+ *
294
+ * The env var `THEOKIT_OPENAPI_SERVERS` (CSV of URLs) overrides
295
+ * `servers[].url` at emit time without rebuilding the config.
296
+ */
297
+ openapi: z.object({
298
+ servers: z.array(
299
+ z.object({
300
+ url: z.string().url(),
301
+ description: z.string().optional()
302
+ })
303
+ ).default([{ url: "http://localhost:3000", description: "Local development" }]),
304
+ specVersion: z.enum(["3.1.0", "3.0.3"]).default("3.1.0"),
305
+ title: z.string().default("TheoKit App"),
306
+ version: z.string().default("0.0.0"),
307
+ outDir: z.string().default(".theo")
308
+ }).optional()
268
309
  }).refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {
269
310
  message: "service.port collides with TheoKit web port \u2014 change one of them",
270
311
  path: ["services"]
@@ -282,4 +323,4 @@ export {
282
323
  storageSchema,
283
324
  theoConfigSchema
284
325
  };
285
- //# sourceMappingURL=chunk-3Q36SEDK.js.map
326
+ //# sourceMappingURL=chunk-ZBWROIIO.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/schema.ts"],"sourcesContent":["import { z } from 'zod'\n\nimport { servicesConfigSchema } from '../services/index.js'\n\n/**\n * Base bucket config — legacy shape preserved for backwards compatibility.\n */\nconst baseRateLimitSchema = z.object({\n windowMs: z.number().min(1),\n max: z.number().int().min(1),\n})\n\n/**\n * T2.2 — Per-route + per-user rate limit. The new shape adds `routes`\n * (path map), `keyBy`, and `cookieName`. The legacy flat shape is still\n * accepted via the union — see `createRouteRateLimiter` for normalization.\n */\nexport const rateLimitSchema = z.union([\n baseRateLimitSchema,\n z.object({\n default: baseRateLimitSchema.optional(),\n routes: z.record(z.string(), baseRateLimitSchema).optional(),\n keyBy: z\n .union([\n z.enum(['ip', 'session', 'user']),\n z.function().args(z.unknown()).returns(z.string()),\n ])\n .optional(),\n cookieName: z.string().min(1).optional(),\n }),\n])\n\nexport const uploadSchema = z.object({\n maxFileSize: z\n .number()\n .min(1)\n .default(10 * 1024 * 1024), // 10MB\n maxFiles: z.number().int().min(1).default(10),\n maxFieldSize: z\n .number()\n .min(1)\n .default(1 * 1024 * 1024), // 1MB\n})\n\nexport const loggingSchema = z.object({\n level: z.enum(['debug', 'info', 'warn', 'error', 'silent']).default('info'),\n})\n\n/**\n * Cache subsystem config (caching-and-revalidation-plan).\n * Default `cache: undefined` keeps the framework backward-compatible\n * (no engine initialized → no cache primitives available).\n *\n * Setting `cache: {}` opts in with all defaults.\n */\nconst routeRuleSchema = z.object({\n maxAge: z.number().nonnegative().finite().optional(),\n swr: z.number().nonnegative().finite().optional(),\n tags: z.array(z.string()).optional(),\n})\n\nexport const cacheSchema = z.object({\n enabled: z.boolean().default(true),\n /** 'memory' uses InMemoryCacheAdapter; otherwise pass a custom adapter instance. */\n storage: z.union([z.literal('memory'), z.custom<unknown>()]).default('memory'),\n maxEntries: z.number().int().positive().default(1000),\n defaults: z\n .object({\n maxAge: z.number().nonnegative().finite().default(1),\n swr: z.number().nonnegative().finite().optional(),\n cacheErrors: z.boolean().default(false),\n })\n .default({}),\n keyDerivation: z\n .object({\n excludeQuery: z.array(z.string()).optional(),\n sortQuery: z.boolean().default(true),\n lowercaseHost: z.boolean().default(true),\n })\n .default({}),\n routeRules: z.record(z.string(), routeRuleSchema).optional(),\n})\n\n/**\n * Phase 5 — CSRF warn-first (EC-1).\n *\n * 0.2.0 default: `warn`. Existing apps keep working but get structured\n * warnings about every state-mutating request that does not carry the\n * `X-Theo-Action: 1` header. 0.3.0 will flip the default to `strict`.\n *\n * Set explicitly to `strict` to opt into the future default early,\n * or `off` to disable CSRF entirely (only valid when you have another\n * defense — bearer auth, no session cookies, etc).\n */\n/**\n * Phase 6 — Default security headers (D4 / EC-2).\n *\n * 0.2.0 defaults:\n * - CSP in `report-only` mode (EC-2: don't break existing apps)\n * - X-Frame-Options: DENY · X-Content-Type-Options: nosniff\n * - Referrer-Policy: strict-origin-when-cross-origin\n * - HSTS in production only (no TLS on localhost)\n *\n * Users override individual headers, swap CSP to `enforce`, or disable\n * CSP entirely (`csp: false` / `cspMode: 'off'`).\n */\n/**\n * EC-3 — CWE-113 HTTP Response Splitting mitigation.\n *\n * Every string that becomes a header value must reject CR/LF. Apps that\n * derive header values from untrusted input (feature flags, tenant config,\n * URL params) would otherwise let attackers inject Set-Cookie / Location\n * headers via `\\r\\n`. Apply this refinement to every header-bound string\n * field: permissionsPolicy, csp, hsts, referrerPolicy.\n */\nconst headerSafeString = z\n .string()\n .refine((s) => !/[\\r\\n]/.test(s), { message: 'Header value must not contain CR/LF' })\n\nexport const securityHeadersSchema = z.object({\n csp: z.union([headerSafeString, z.literal(false)]).optional(),\n // T6.1 — default flipped from 'report-only' to 'enforce' for 0.3.0.\n // Users who want the old behaviour set `cspMode: 'report-only'`\n // explicitly. See docs/migrating/0.2-to-0.3.md.\n cspMode: z.enum(['enforce', 'report-only', 'off']).default('enforce'),\n hsts: z.union([headerSafeString, z.literal(false)]).optional(),\n frameOptions: z.enum(['DENY', 'SAMEORIGIN']).default('DENY'),\n contentTypeOptions: z.literal('nosniff').default('nosniff'),\n referrerPolicy: headerSafeString.default('strict-origin-when-cross-origin'),\n /**\n * T1.1 — Permissions-Policy directive string. EC-3-refined: rejects CR/LF.\n * Pass `false` to suppress the header.\n */\n permissionsPolicy: z.union([headerSafeString, z.literal(false)]).optional(),\n})\n\n/**\n * T5.1 — Disallowed-routes escalation pattern (Rails-inspired).\n *\n * `routes` accepts string (exact match — trailing slash matters) or\n * RegExp entries. Matched routes that would otherwise emit `csrf.warn`\n * dispatch through `disallowedBehavior` instead:\n * - `'warn'` : no-op vs the default warn-mode behavior\n * - `'raise'` : escalate to 403, even when global `csrf` mode is 'warn'\n *\n * Use to roll out strict mode per-route (e.g., flip /api/auth/* first)\n * without committing the entire surface to strict at once.\n */\nexport const disallowedConfigSchema = z.object({\n routes: z.array(z.union([z.string(), z.instanceof(RegExp)])),\n behavior: z.enum(['warn', 'raise']).default('raise'),\n})\n\n/**\n * T1.2 — CORS configuration.\n *\n * `origins` accepts a single value (`'*'`, string, RegExp, callback) OR an\n * array of (string | RegExp). The spec-violating `origins: '*'` +\n * `credentials: true` combination is rejected at parse time (browsers\n * ignore wildcards when credentials are sent).\n *\n * EC-3 — `allowedHeaders` and `exposedHeaders` entries go through the\n * header-safe refinement (CR/LF rejected — CWE-113 mitigation).\n */\nexport const corsSchema = z\n .object({\n origins: z.union([\n z.literal('*'),\n headerSafeString,\n z.instanceof(RegExp),\n z.array(z.union([headerSafeString, z.instanceof(RegExp)])),\n z.function().args(z.string()).returns(z.boolean()),\n ]),\n methods: z\n .array(z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']))\n .optional(),\n allowedHeaders: z.array(headerSafeString).optional(),\n exposedHeaders: z.array(headerSafeString).optional(),\n credentials: z.boolean().default(false),\n maxAge: z.number().int().min(0).max(86400).default(600),\n })\n .refine((c) => !(c.origins === '*' && c.credentials), {\n message: 'CORS spec forbids origins:\"*\" with credentials:true — browsers ignore the wildcard',\n })\n\nexport const securitySchema = z.object({\n // T6.1 — default flipped from 'warn' to 'strict' for 0.3.0. Apps that\n // grep their warn-mode logs from 0.2.x already know which endpoints\n // break; opt back into 'warn' globally OR use disallowedRoutes for\n // surgical migration. See docs/migrating/0.2-to-0.3.md.\n csrf: z.enum(['off', 'warn', 'strict']).default('strict'),\n headers: securityHeadersSchema.optional(),\n /** T5.1 — per-route escalation (Rails disallowed_warnings pattern). */\n disallowed: disallowedConfigSchema.optional(),\n /** T1.2 — Cross-Origin Resource Sharing. Single global config; runs first in pipeline. */\n cors: corsSchema.optional(),\n})\n\n/**\n * StorageManager schemas (T1.1 / ADR-0007 D4).\n *\n * `theo.config.ts > storage` declares Postgres servers (credentials), the\n * databases on each server (TheoCloud / managed PG / self-host), and Redis\n * servers. The runtime `StorageManager` consumes this block; adapters\n * (PostgresJobBackend, PostgresConversationStorage, etc.) request pools by\n * database name and never see raw credentials at the call site.\n *\n * EC-1 (DOCUMENT in concept doc T4.1): Zod default strip-unknown mode\n * silently drops typo keys (`databasees` instead of `databases`). The\n * concept doc warns users to use exact names; runtime errors are still\n * actionable (`Database \"X\" not configured`).\n *\n * EC-2 (DEFERRED to use-time): `databases.X.server` references a key in\n * `servers`. Cross-field validation is intentionally NOT enforced at parse\n * time — `StorageManager.usePostgres()` throws an actionable error on the\n * first call with the dangling reference, matching Rails / Nitro behavior.\n */\nconst tlsConfigSchema = z.object({\n rejectUnauthorized: z.boolean().optional(),\n caCert: z.string().optional(),\n clientCert: z.string().optional(),\n clientKey: z.string().optional(),\n})\n\nconst serverConfigSchema = z.object({\n host: z.string().min(1),\n port: z.number().int().positive().max(65535).optional(),\n user: z.string().min(1),\n /** Password may be the empty string (some providers permit it). */\n password: z.string(),\n tls: tlsConfigSchema.optional(),\n})\n\nconst postgresPoolConfigSchema = z.object({\n min: z.number().int().nonnegative().optional(),\n max: z.number().int().positive().optional(),\n connectionTimeoutMillis: z.number().int().positive().optional(),\n idleTimeoutMillis: z.number().int().nonnegative().optional(),\n})\n\nconst postgresDatabaseConfigSchema = z.object({\n /** References a key in `storage.servers`. Resolved at `usePostgres()` call time. */\n server: z.string().min(1),\n database: z.string().min(1),\n pool: postgresPoolConfigSchema.optional(),\n})\n\nconst redisServerConfigSchema = serverConfigSchema.extend({\n db: z.number().int().nonnegative().optional(),\n maxRetriesPerRequest: z.number().int().nonnegative().optional(),\n})\n\nexport const storageSchema = z.object({\n servers: z.record(z.string(), serverConfigSchema).optional(),\n databases: z.record(z.string(), postgresDatabaseConfigSchema).optional(),\n redis: z.record(z.string(), redisServerConfigSchema).optional(),\n})\n\nexport type StorageConfig = z.infer<typeof storageSchema>\n\nexport const theoConfigSchema = z\n .object({\n appDir: z.string().default('app'),\n serverDir: z.string().default('server'),\n /**\n * T2.2 / EC-4 — Build output directory. Must be a relative path inside\n * the project root. Refused absolute or parent-relative paths to prevent\n * `cleanOutDir` from wiping arbitrary locations (defense-in-depth on\n * top of cleanOutDir's runtime EC-3 guard).\n */\n distDir: z\n .string()\n .default('.theo')\n .refine(\n (d) => !/^([A-Za-z]:)?[/\\\\]/.test(d) && !d.startsWith('..'),\n 'distDir must be a relative path inside the project root (e.g., \".theo\")',\n ),\n /**\n * Agent runtime configuration.\n *\n * - `maxRegistries` (T2.3, legacy): dev-mode cleanup of stale\n * `.theokit/agents/<id>/` dirs by mtime. Now superseded by SDK's\n * native registry GC but kept for backward-compat.\n * - `registry` (Phase 6, Production-Readiness #2): forwarded to\n * `Agent.registry.configure()` lazily on first request.\n * * `maxAgents` — LRU cap. MUST be ≥ max-concurrent-active-conversations\n * (EC-17 DOCUMENT): with maxAgents:1 and 2 concurrent chats, LRU evicts\n * mid-stream → SDK aborts with code:'aborted'. Default 100 covers\n * indie/small-team; tune up for high-traffic.\n * * `idleTimeoutMs` — auto-evict idle agents (default 30 min). 0 disables\n * idle eviction (LRU only).\n */\n agents: z\n .object({\n maxRegistries: z.number().int().positive().default(100),\n registry: z\n .object({\n /** LRU cap — MUST be ≥ max-concurrent-active-conversations. */\n maxAgents: z.number().int().positive().max(10_000).default(100),\n /** Idle eviction window in ms. 0 disables idle eviction (LRU only). */\n idleTimeoutMs: z\n .number()\n .int()\n .nonnegative()\n .default(30 * 60_000),\n })\n .optional(),\n })\n .optional(),\n port: z.number().int().min(1).max(65535).default(3000),\n ssr: z.boolean().default(false),\n /** When true (and ssr === true), use renderToPipeableStream with progressive\n * shell flush instead of single-shot renderToString. Opt-in for streaming\n * SSR; default false preserves the current behavior. */\n ssrStreaming: z.boolean().default(false),\n rateLimit: rateLimitSchema.optional(),\n upload: uploadSchema.optional(),\n logging: loggingSchema.optional(),\n security: securitySchema.optional(),\n serialization: z.enum(['json', 'superjson']).default('json'),\n // Plugins are validated structurally at runtime by createPluginRunnerFromConfig.\n // Zod only checks the shape minimally (must be array). Type-level safety is\n // provided through defineConfig at the user surface.\n plugins: z.array(z.unknown()).optional(),\n /** Enable client-side batching of theoFetch calls and the\n * /api/__theo_batch__ server endpoint. */\n batching: z\n .union([z.boolean(), z.object({ max: z.number().int().positive().optional() })])\n .optional(),\n /** T4.1 — Audit log. When `logger` is provided, framework events\n * (csrf.warn, rate-limit.exceeded, session.rotated, csp.violation) are\n * emitted to it. Default: noop. */\n audit: z\n .object({\n logger: z.unknown().optional(),\n })\n .optional(),\n /** TheoUI auto-wire (T2.1). `false` = opt-out; object = explicit theme/fonts;\n * undefined = enabled when @usetheo/ui is detected in node_modules. */\n ui: z\n .union([\n z.literal(false),\n z.object({\n theme: z.enum(['violet-forge', 'noir', 'paper']).optional(),\n fonts: z.enum(['bundled', 'cdn']).optional(),\n }),\n ])\n .optional(),\n /**\n * Cache subsystem (caching-and-revalidation-plan).\n * Default `undefined` keeps caching disabled (backward compatible).\n * Pass `cache: {}` to opt in with defaults.\n */\n cache: cacheSchema.optional(),\n /**\n * Devtools overlay (Phase 0.4.0+ — see docs/plans/devtools-plan.md).\n *\n * - `undefined` (default): devtools auto-injects in `pnpm dev`, NEVER in `vite build`.\n * - `false`: devtools disabled entirely (Vite plugin skips injection even in dev).\n * - `{ ... }`: devtools enabled with explicit defaults (position, theme).\n *\n * Tree-shaken to noop in prod via the dual-export pattern in\n * `packages/theo/src/devtools/index.ts` (EC-17 positive prod check).\n */\n devtools: z\n .union([\n z.literal(false),\n z.object({\n position: z.enum(['top-left', 'top-right', 'bottom-left', 'bottom-right']).optional(),\n theme: z.enum(['light', 'dark', 'system']).optional(),\n }),\n ])\n .optional(),\n /**\n * Informative list of deploy adapters the app supports. Does NOT\n * trigger per-build translations — only the `--target` CLI flag does\n * (EC-201 / ADR D2). If `config.adapters` includes a target NOT\n * matching `--target`, `theokit build` emits a cross-reference note.\n */\n adapters: z.array(z.string()).optional(),\n /**\n * Jobs backend (ADR D3 + T2.1). When configured, every request gets\n * `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a\n * `JobBackend` instance (InMemoryJobBackend, PostgresJobBackend, etc.).\n */\n jobs: z\n .object({\n backend: z.custom<unknown>((v) => typeof v === 'object' && v !== null),\n })\n .optional(),\n /**\n * StorageManager configuration (T1.1 / ADR-0007).\n *\n * Declares Postgres servers + databases + Redis servers. Consumed by\n * `getStorageManager().configure()` at boot via `start.ts`.\n *\n * Default `undefined` means manager exists but is not configured — adapters\n * relying on the manager (PostgresJobBackend.fromStorageManager, etc.) will\n * throw with an actionable error on first use.\n */\n storage: storageSchema.optional(),\n /**\n * Wave 2 — Polyglot services orchestration (T1.1 / ADR-0012/0013/0014/0015).\n *\n * Declarative external sidecar processes (Python FastAPI / Node Hono)\n * that boot alongside the TheoKit TS app. Empty `services: {}` is the\n * default and preserves Wave 1 behavior.\n */\n services: servicesConfigSchema,\n })\n // EC-2 fix: cross-config refine — no service may share TheoKit's web port.\n .refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {\n message: 'service.port collides with TheoKit web port — change one of them',\n path: ['services'],\n })\n\nexport type TheoConfig = z.infer<typeof theoConfigSchema>\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/config/schema.ts"],"sourcesContent":["import { z } from 'zod'\n\nimport { servicesConfigSchema } from '../services/index.js'\n\n/**\n * Base bucket config — legacy shape preserved for backwards compatibility.\n */\nconst baseRateLimitSchema = z.object({\n windowMs: z.number().min(1),\n max: z.number().int().min(1),\n})\n\n/**\n * T2.2 — Per-route + per-user rate limit. The new shape adds `routes`\n * (path map), `keyBy`, and `cookieName`. The legacy flat shape is still\n * accepted via the union — see `createRouteRateLimiter` for normalization.\n */\nexport const rateLimitSchema = z.union([\n baseRateLimitSchema,\n z.object({\n default: baseRateLimitSchema.optional(),\n routes: z.record(z.string(), baseRateLimitSchema).optional(),\n keyBy: z\n .union([\n z.enum(['ip', 'session', 'user']),\n z.function().args(z.unknown()).returns(z.string()),\n ])\n .optional(),\n cookieName: z.string().min(1).optional(),\n }),\n])\n\nexport const uploadSchema = z.object({\n maxFileSize: z\n .number()\n .min(1)\n .default(10 * 1024 * 1024), // 10MB\n maxFiles: z.number().int().min(1).default(10),\n maxFieldSize: z\n .number()\n .min(1)\n .default(1 * 1024 * 1024), // 1MB\n})\n\nexport const loggingSchema = z.object({\n level: z.enum(['debug', 'info', 'warn', 'error', 'silent']).default('info'),\n})\n\n/**\n * Cache subsystem config (caching-and-revalidation-plan).\n * Default `cache: undefined` keeps the framework backward-compatible\n * (no engine initialized → no cache primitives available).\n *\n * Setting `cache: {}` opts in with all defaults.\n */\nconst routeRuleSchema = z.object({\n maxAge: z.number().nonnegative().finite().optional(),\n swr: z.number().nonnegative().finite().optional(),\n tags: z.array(z.string()).optional(),\n})\n\nexport const cacheSchema = z.object({\n enabled: z.boolean().default(true),\n /** 'memory' uses InMemoryCacheAdapter; otherwise pass a custom adapter instance. */\n storage: z.union([z.literal('memory'), z.custom<unknown>()]).default('memory'),\n maxEntries: z.number().int().positive().default(1000),\n defaults: z\n .object({\n maxAge: z.number().nonnegative().finite().default(1),\n swr: z.number().nonnegative().finite().optional(),\n cacheErrors: z.boolean().default(false),\n })\n .default({}),\n keyDerivation: z\n .object({\n excludeQuery: z.array(z.string()).optional(),\n sortQuery: z.boolean().default(true),\n lowercaseHost: z.boolean().default(true),\n })\n .default({}),\n routeRules: z.record(z.string(), routeRuleSchema).optional(),\n})\n\n/**\n * Phase 5 — CSRF warn-first (EC-1).\n *\n * 0.2.0 default: `warn`. Existing apps keep working but get structured\n * warnings about every state-mutating request that does not carry the\n * `X-Theo-Action: 1` header. 0.3.0 will flip the default to `strict`.\n *\n * Set explicitly to `strict` to opt into the future default early,\n * or `off` to disable CSRF entirely (only valid when you have another\n * defense — bearer auth, no session cookies, etc).\n */\n/**\n * Phase 6 — Default security headers (D4 / EC-2).\n *\n * 0.2.0 defaults:\n * - CSP in `report-only` mode (EC-2: don't break existing apps)\n * - X-Frame-Options: DENY · X-Content-Type-Options: nosniff\n * - Referrer-Policy: strict-origin-when-cross-origin\n * - HSTS in production only (no TLS on localhost)\n *\n * Users override individual headers, swap CSP to `enforce`, or disable\n * CSP entirely (`csp: false` / `cspMode: 'off'`).\n */\n/**\n * EC-3 — CWE-113 HTTP Response Splitting mitigation.\n *\n * Every string that becomes a header value must reject CR/LF. Apps that\n * derive header values from untrusted input (feature flags, tenant config,\n * URL params) would otherwise let attackers inject Set-Cookie / Location\n * headers via `\\r\\n`. Apply this refinement to every header-bound string\n * field: permissionsPolicy, csp, hsts, referrerPolicy.\n */\nconst headerSafeString = z\n .string()\n .refine((s) => !/[\\r\\n]/.test(s), { message: 'Header value must not contain CR/LF' })\n\nexport const securityHeadersSchema = z.object({\n csp: z.union([headerSafeString, z.literal(false)]).optional(),\n // T6.1 — default flipped from 'report-only' to 'enforce' for 0.3.0.\n // Users who want the old behaviour set `cspMode: 'report-only'`\n // explicitly. See docs/migrating/0.2-to-0.3.md.\n cspMode: z.enum(['enforce', 'report-only', 'off']).default('enforce'),\n hsts: z.union([headerSafeString, z.literal(false)]).optional(),\n frameOptions: z.enum(['DENY', 'SAMEORIGIN']).default('DENY'),\n contentTypeOptions: z.literal('nosniff').default('nosniff'),\n referrerPolicy: headerSafeString.default('strict-origin-when-cross-origin'),\n /**\n * T1.1 — Permissions-Policy directive string. EC-3-refined: rejects CR/LF.\n * Pass `false` to suppress the header.\n */\n permissionsPolicy: z.union([headerSafeString, z.literal(false)]).optional(),\n})\n\n/**\n * T5.1 — Disallowed-routes escalation pattern (Rails-inspired).\n *\n * `routes` accepts string (exact match — trailing slash matters) or\n * RegExp entries. Matched routes that would otherwise emit `csrf.warn`\n * dispatch through `disallowedBehavior` instead:\n * - `'warn'` : no-op vs the default warn-mode behavior\n * - `'raise'` : escalate to 403, even when global `csrf` mode is 'warn'\n *\n * Use to roll out strict mode per-route (e.g., flip /api/auth/* first)\n * without committing the entire surface to strict at once.\n */\nexport const disallowedConfigSchema = z.object({\n routes: z.array(z.union([z.string(), z.instanceof(RegExp)])),\n behavior: z.enum(['warn', 'raise']).default('raise'),\n})\n\n/**\n * T1.2 — CORS configuration.\n *\n * `origins` accepts a single value (`'*'`, string, RegExp, callback) OR an\n * array of (string | RegExp). The spec-violating `origins: '*'` +\n * `credentials: true` combination is rejected at parse time (browsers\n * ignore wildcards when credentials are sent).\n *\n * EC-3 — `allowedHeaders` and `exposedHeaders` entries go through the\n * header-safe refinement (CR/LF rejected — CWE-113 mitigation).\n */\nexport const corsSchema = z\n .object({\n origins: z.union([\n z.literal('*'),\n headerSafeString,\n z.instanceof(RegExp),\n z.array(z.union([headerSafeString, z.instanceof(RegExp)])),\n z.function().args(z.string()).returns(z.boolean()),\n ]),\n methods: z\n .array(z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']))\n .optional(),\n allowedHeaders: z.array(headerSafeString).optional(),\n exposedHeaders: z.array(headerSafeString).optional(),\n credentials: z.boolean().default(false),\n maxAge: z.number().int().min(0).max(86400).default(600),\n })\n .refine((c) => !(c.origins === '*' && c.credentials), {\n message: 'CORS spec forbids origins:\"*\" with credentials:true — browsers ignore the wildcard',\n })\n\nexport const securitySchema = z.object({\n // T6.1 — default flipped from 'warn' to 'strict' for 0.3.0. Apps that\n // grep their warn-mode logs from 0.2.x already know which endpoints\n // break; opt back into 'warn' globally OR use disallowedRoutes for\n // surgical migration. See docs/migrating/0.2-to-0.3.md.\n csrf: z.enum(['off', 'warn', 'strict']).default('strict'),\n headers: securityHeadersSchema.optional(),\n /** T5.1 — per-route escalation (Rails disallowed_warnings pattern). */\n disallowed: disallowedConfigSchema.optional(),\n /** T1.2 — Cross-Origin Resource Sharing. Single global config; runs first in pipeline. */\n cors: corsSchema.optional(),\n})\n\n/**\n * StorageManager schemas (T1.1 / ADR-0007 D4).\n *\n * `theo.config.ts > storage` declares Postgres servers (credentials), the\n * databases on each server (TheoCloud / managed PG / self-host), and Redis\n * servers. The runtime `StorageManager` consumes this block; adapters\n * (PostgresJobBackend, PostgresConversationStorage, etc.) request pools by\n * database name and never see raw credentials at the call site.\n *\n * EC-1 (DOCUMENT in concept doc T4.1): Zod default strip-unknown mode\n * silently drops typo keys (`databasees` instead of `databases`). The\n * concept doc warns users to use exact names; runtime errors are still\n * actionable (`Database \"X\" not configured`).\n *\n * EC-2 (DEFERRED to use-time): `databases.X.server` references a key in\n * `servers`. Cross-field validation is intentionally NOT enforced at parse\n * time — `StorageManager.usePostgres()` throws an actionable error on the\n * first call with the dangling reference, matching Rails / Nitro behavior.\n */\nconst tlsConfigSchema = z.object({\n rejectUnauthorized: z.boolean().optional(),\n caCert: z.string().optional(),\n clientCert: z.string().optional(),\n clientKey: z.string().optional(),\n})\n\nconst serverConfigSchema = z.object({\n host: z.string().min(1),\n port: z.number().int().positive().max(65535).optional(),\n user: z.string().min(1),\n /** Password may be the empty string (some providers permit it). */\n password: z.string(),\n tls: tlsConfigSchema.optional(),\n})\n\nconst postgresPoolConfigSchema = z.object({\n min: z.number().int().nonnegative().optional(),\n max: z.number().int().positive().optional(),\n connectionTimeoutMillis: z.number().int().positive().optional(),\n idleTimeoutMillis: z.number().int().nonnegative().optional(),\n})\n\nconst postgresDatabaseConfigSchema = z.object({\n /** References a key in `storage.servers`. Resolved at `usePostgres()` call time. */\n server: z.string().min(1),\n database: z.string().min(1),\n pool: postgresPoolConfigSchema.optional(),\n})\n\nconst redisServerConfigSchema = serverConfigSchema.extend({\n db: z.number().int().nonnegative().optional(),\n maxRetriesPerRequest: z.number().int().nonnegative().optional(),\n})\n\nexport const storageSchema = z.object({\n servers: z.record(z.string(), serverConfigSchema).optional(),\n databases: z.record(z.string(), postgresDatabaseConfigSchema).optional(),\n redis: z.record(z.string(), redisServerConfigSchema).optional(),\n})\n\nexport type StorageConfig = z.infer<typeof storageSchema>\n\nexport const theoConfigSchema = z\n .object({\n appDir: z.string().default('app'),\n serverDir: z.string().default('server'),\n /**\n * T2.2 / EC-4 — Build output directory. Must be a relative path inside\n * the project root. Refused absolute or parent-relative paths to prevent\n * `cleanOutDir` from wiping arbitrary locations (defense-in-depth on\n * top of cleanOutDir's runtime EC-3 guard).\n */\n distDir: z\n .string()\n .default('.theo')\n .refine(\n (d) => !/^([A-Za-z]:)?[/\\\\]/.test(d) && !d.startsWith('..'),\n 'distDir must be a relative path inside the project root (e.g., \".theo\")',\n ),\n /**\n * Agent runtime configuration.\n *\n * - `maxRegistries` (T2.3, legacy): dev-mode cleanup of stale\n * `.theokit/agents/<id>/` dirs by mtime. Now superseded by SDK's\n * native registry GC but kept for backward-compat.\n * - `registry` (Phase 6, Production-Readiness #2): forwarded to\n * `Agent.registry.configure()` lazily on first request.\n * * `maxAgents` — LRU cap. MUST be ≥ max-concurrent-active-conversations\n * (EC-17 DOCUMENT): with maxAgents:1 and 2 concurrent chats, LRU evicts\n * mid-stream → SDK aborts with code:'aborted'. Default 100 covers\n * indie/small-team; tune up for high-traffic.\n * * `idleTimeoutMs` — auto-evict idle agents (default 30 min). 0 disables\n * idle eviction (LRU only).\n */\n agents: z\n .object({\n maxRegistries: z.number().int().positive().default(100),\n registry: z\n .object({\n /** LRU cap — MUST be ≥ max-concurrent-active-conversations. */\n maxAgents: z.number().int().positive().max(10_000).default(100),\n /** Idle eviction window in ms. 0 disables idle eviction (LRU only). */\n idleTimeoutMs: z\n .number()\n .int()\n .nonnegative()\n .default(30 * 60_000),\n })\n .optional(),\n })\n .optional(),\n port: z.number().int().min(1).max(65535).default(3000),\n ssr: z.boolean().default(false),\n /** When true (and ssr === true), use renderToPipeableStream with progressive\n * shell flush instead of single-shot renderToString. Opt-in for streaming\n * SSR; default false preserves the current behavior. */\n ssrStreaming: z.boolean().default(false),\n rateLimit: rateLimitSchema.optional(),\n upload: uploadSchema.optional(),\n logging: loggingSchema.optional(),\n security: securitySchema.optional(),\n serialization: z.enum(['json', 'superjson']).default('json'),\n // Plugins are validated structurally at runtime by createPluginRunnerFromConfig.\n // Zod only checks the shape minimally (must be array). Type-level safety is\n // provided through defineConfig at the user surface.\n plugins: z.array(z.unknown()).optional(),\n /** Enable client-side batching of theoFetch calls and the\n * /api/__theo_batch__ server endpoint. */\n batching: z\n .union([z.boolean(), z.object({ max: z.number().int().positive().optional() })])\n .optional(),\n /** T4.1 — Audit log. When `logger` is provided, framework events\n * (csrf.warn, rate-limit.exceeded, session.rotated, csp.violation) are\n * emitted to it. Default: noop. */\n audit: z\n .object({\n logger: z.unknown().optional(),\n })\n .optional(),\n /** TheoUI auto-wire (T2.1). `false` = opt-out; object = explicit theme/fonts;\n * undefined = enabled when @usetheo/ui is detected in node_modules. */\n ui: z\n .union([\n z.literal(false),\n z.object({\n theme: z.enum(['violet-forge', 'noir', 'paper']).optional(),\n fonts: z.enum(['bundled', 'cdn']).optional(),\n }),\n ])\n .optional(),\n /**\n * Cache subsystem (caching-and-revalidation-plan).\n * Default `undefined` keeps caching disabled (backward compatible).\n * Pass `cache: {}` to opt in with defaults.\n */\n cache: cacheSchema.optional(),\n /**\n * Devtools overlay (Phase 0.4.0+ — see docs/plans/devtools-plan.md).\n *\n * - `undefined` (default): devtools auto-injects in `pnpm dev`, NEVER in `vite build`.\n * - `false`: devtools disabled entirely (Vite plugin skips injection even in dev).\n * - `{ ... }`: devtools enabled with explicit defaults (position, theme).\n *\n * Tree-shaken to noop in prod via the dual-export pattern in\n * `packages/theo/src/devtools/index.ts` (EC-17 positive prod check).\n */\n devtools: z\n .union([\n z.literal(false),\n z.object({\n position: z.enum(['top-left', 'top-right', 'bottom-left', 'bottom-right']).optional(),\n theme: z.enum(['light', 'dark', 'system']).optional(),\n }),\n ])\n .optional(),\n /**\n * Informative list of deploy adapters the app supports. Does NOT\n * trigger per-build translations — only the `--target` CLI flag does\n * (EC-201 / ADR D2). If `config.adapters` includes a target NOT\n * matching `--target`, `theokit build` emits a cross-reference note.\n */\n adapters: z.array(z.string()).optional(),\n /**\n * Extra package names to pre-bundle via Vite's `optimizeDeps.include`.\n *\n * Framework auto-includes `@usetheo/ui` and `lucide-react` when present.\n * Apps adding plugin peer-deps that rely on a runtime `import('<pkg>')`\n * (dynamic specifier) — e.g. `@theokit/plugin-canvas` consumers\n * installing `mermaid` for Mermaid SVG rendering — must declare those\n * here so Vite can resolve the literal in dev mode without\n * \"Failed to resolve module specifier\" errors. Production builds use\n * tsup/esbuild bundling and are not affected.\n *\n * Example:\n * viteOptimizeDeps: ['mermaid']\n */\n viteOptimizeDeps: z.array(z.string()).optional(),\n /**\n * Jobs backend (ADR D3 + T2.1). When configured, every request gets\n * `ctx.queue.enqueue` auto-wired via the outbox lifecycle. Pass a\n * `JobBackend` instance (InMemoryJobBackend, PostgresJobBackend, etc.).\n */\n jobs: z\n .object({\n backend: z.custom<unknown>((v) => typeof v === 'object' && v !== null),\n })\n .optional(),\n /**\n * StorageManager configuration (T1.1 / ADR-0007).\n *\n * Declares Postgres servers + databases + Redis servers. Consumed by\n * `getStorageManager().configure()` at boot via `start.ts`.\n *\n * Default `undefined` means manager exists but is not configured — adapters\n * relying on the manager (PostgresJobBackend.fromStorageManager, etc.) will\n * throw with an actionable error on first use.\n */\n storage: storageSchema.optional(),\n /**\n * Wave 2 — Polyglot services orchestration (T1.1 / ADR-0012/0013/0014/0015).\n *\n * Declarative external sidecar processes (Python FastAPI / Node Hono)\n * that boot alongside the TheoKit TS app. Empty `services: {}` is the\n * default and preserves Wave 1 behavior.\n */\n services: servicesConfigSchema,\n /**\n * G2 — OpenAPI emit (build-time only).\n *\n * When present, `theokit build` writes a generated `openapi.json` from\n * every `defineRoute()` body/query/params Zod schema. `undefined` keeps\n * the framework backward-compatible (no emit). Pass `openapi: {}` to\n * opt in with defaults.\n *\n * Defaults: spec 3.1.0 · servers `http://localhost:3000` · title\n * \"TheoKit App\" · version \"0.0.0\" · outDir \".theo\" (next to dist).\n *\n * The env var `THEOKIT_OPENAPI_SERVERS` (CSV of URLs) overrides\n * `servers[].url` at emit time without rebuilding the config.\n */\n openapi: z\n .object({\n servers: z\n .array(\n z.object({\n url: z.string().url(),\n description: z.string().optional(),\n }),\n )\n .default([{ url: 'http://localhost:3000', description: 'Local development' }]),\n specVersion: z.enum(['3.1.0', '3.0.3']).default('3.1.0'),\n title: z.string().default('TheoKit App'),\n version: z.string().default('0.0.0'),\n outDir: z.string().default('.theo'),\n })\n .optional(),\n })\n // EC-2 fix: cross-config refine — no service may share TheoKit's web port.\n .refine((cfg) => !Object.values(cfg.services).some((s) => s.port === cfg.port), {\n message: 'service.port collides with TheoKit web port — change one of them',\n path: ['services'],\n })\n\nexport type TheoConfig = z.infer<typeof theoConfigSchema>\n\nexport type OpenApiConfig = NonNullable<TheoConfig['openapi']>\n"],"mappings":"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","names":[]}
@@ -20,7 +20,7 @@ var nodeAdapter = {
20
20
  }
21
21
  await viteBuild({
22
22
  root: cwd,
23
- plugins: ctx.makeVitePlugins({ root: cwd, ssr: config.ssr }),
23
+ plugins: await ctx.makeVitePlugins({ root: cwd, ssr: config.ssr }),
24
24
  build: {
25
25
  outDir: ".theo/client",
26
26
  emptyOutDir: true
@@ -31,7 +31,7 @@ var nodeAdapter = {
31
31
  console.log("\n Building SSR...\n");
32
32
  await viteBuild({
33
33
  root: cwd,
34
- plugins: ctx.makeVitePlugins({ root: cwd, ssr: true }),
34
+ plugins: await ctx.makeVitePlugins({ root: cwd, ssr: true }),
35
35
  build: {
36
36
  ssr: true,
37
37
  outDir: ".theo/server",
@@ -62,4 +62,4 @@ var nodeAdapter = {
62
62
  export {
63
63
  nodeAdapter
64
64
  };
65
- //# sourceMappingURL=chunk-Y5KE6YFP.js.map
65
+ //# sourceMappingURL=chunk-ZJW5FFYJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/node.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Node deploy adapter — derives all write paths from the trusted `cwd`\n * (CLI argument). The `.theo/docker-compose.yml` + `.theo/Caddyfile`\n * outputs use a fixed sub-path. Build-time tool — no HTTP input.\n */\nimport { writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nimport { build as viteBuild } from 'vite'\n\nimport type { TheoConfig } from '../config/schema.js'\nimport { generateCaddyfile, generateComposeYaml, readManifest } from '../services/index.js'\n\nimport type { AdapterBuildContext, DeployAdapter } from './types.js'\n\nexport const nodeAdapter: DeployAdapter = {\n name: 'node',\n\n async build(config: TheoConfig, cwd: string, ctx?: AdapterBuildContext): Promise<void> {\n // T1.1 (architecture-cleanup) — Vite plugin composition is INJECTED via `ctx.makeVitePlugins`.\n // The CLI (which already imports both `adapters/*` and `vite-plugin/*`) composes the\n // Plugin[] and passes it in, eliminating the previous `adapters → vite-plugin` edge\n // (CRITICAL layering inversion per ADR-0001 v2; fixed by ADR-0001 v3).\n if (!ctx?.makeVitePlugins) {\n throw new Error(\n '[adapter-node] build requires ctx.makeVitePlugins (CLI must inject the Vite plugin factory). ' +\n 'This is a framework-internal invariant — see ADR-0001 v3 and docs/plans/architecture-cleanup-plan.md T1.1.',\n )\n }\n\n // Client build — `await` per ADR-0001 v3.1: makeVitePlugins may return\n // a Promise (theokit build uses theoPluginAsync to wire the full chain\n // including @theo/actions virtual module).\n await viteBuild({\n root: cwd,\n plugins: await ctx.makeVitePlugins({ root: cwd, ssr: config.ssr }),\n build: {\n outDir: '.theo/client',\n emptyOutDir: true,\n },\n logLevel: 'info',\n })\n\n // SSR build (only when ssr: true)\n if (config.ssr) {\n // eslint-disable-next-line no-console -- CLI build progress\n console.log('\\n Building SSR...\\n')\n await viteBuild({\n root: cwd,\n plugins: await ctx.makeVitePlugins({ root: cwd, ssr: true }),\n build: {\n ssr: true,\n outDir: '.theo/server',\n emptyOutDir: true,\n rollupOptions: {\n input: '/@theo/entry-server',\n },\n },\n logLevel: 'info',\n })\n }\n\n // Wave 2 (T2.1) — when polyglot services are declared, emit a\n // TheoCloud-shaped docker-compose harness + Caddyfile (with W3C\n // traceparent propagation via Caddy 2.11+ `tracing` directive).\n // Empty manifest → no emission (Wave 1 BC preserved).\n const manifest = readManifest(cwd)\n if (manifest && manifest.services.length > 0) {\n const yaml = generateComposeYaml(manifest, { webPort: config.port })\n const caddyfile = generateCaddyfile(manifest, {\n port: config.port,\n webHost: 'web',\n })\n writeFileSync(join(cwd, '.theo', 'docker-compose.yml'), yaml)\n writeFileSync(join(cwd, '.theo', 'Caddyfile'), caddyfile)\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n ` ✓ TheoCloud-shaped harness: .theo/docker-compose.yml + .theo/Caddyfile ` +\n `(${String(manifest.services.length)} service(s))`,\n )\n }\n },\n}\n"],"mappings":";;;;;;;;;AAKA,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAErB,SAAS,SAAS,iBAAiB;AAO5B,IAAM,cAA6B;AAAA,EACxC,MAAM;AAAA,EAEN,MAAM,MAAM,QAAoB,KAAa,KAA0C;AAKrF,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAKA,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,MACN,SAAS,MAAM,IAAI,gBAAgB,EAAE,MAAM,KAAK,KAAK,OAAO,IAAI,CAAC;AAAA,MACjE,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AAGD,QAAI,OAAO,KAAK;AAEd,cAAQ,IAAI,uBAAuB;AACnC,YAAM,UAAU;AAAA,QACd,MAAM;AAAA,QACN,SAAS,MAAM,IAAI,gBAAgB,EAAE,MAAM,KAAK,KAAK,KAAK,CAAC;AAAA,QAC3D,OAAO;AAAA,UACL,KAAK;AAAA,UACL,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,eAAe;AAAA,YACb,OAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAMA,UAAM,WAAW,aAAa,GAAG;AACjC,QAAI,YAAY,SAAS,SAAS,SAAS,GAAG;AAC5C,YAAM,OAAO,oBAAoB,UAAU,EAAE,SAAS,OAAO,KAAK,CAAC;AACnE,YAAM,YAAY,kBAAkB,UAAU;AAAA,QAC5C,MAAM,OAAO;AAAA,QACb,SAAS;AAAA,MACX,CAAC;AACD,oBAAc,KAAK,KAAK,SAAS,oBAAoB,GAAG,IAAI;AAC5D,oBAAc,KAAK,KAAK,SAAS,WAAW,GAAG,SAAS;AAExD,cAAQ;AAAA,QACN,kFACM,OAAO,SAAS,SAAS,MAAM,CAAC;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}