theokit 0.74.2 → 0.74.4

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 (97) hide show
  1. package/dist/{actions-virtual-module-JV7URROJ.js → actions-virtual-module-4J7Z37Y4.js} +2 -2
  2. package/dist/{actions-virtual-module-J2HYO7QZ.js → actions-virtual-module-MIQJEHKJ.js} +2 -2
  3. package/dist/adapters/agent-mount.js +2 -2
  4. package/dist/{agent-5J7CQAYR.js → agent-QJINXU6U.js} +2 -2
  5. package/dist/{app-typed-client-XD4DZBV7.js → app-typed-client-AZMUYIYV.js} +2 -2
  6. package/dist/{app-typed-client-EOA3A2AR.js → app-typed-client-DSS23KDC.js} +2 -2
  7. package/dist/{aws-lambda-ZYSPTD3O.js → aws-lambda-K5Y6XS5V.js} +4 -4
  8. package/dist/{build-7YZSJGP2.js → build-AKN2NCFW.js} +23 -19
  9. package/dist/build-AKN2NCFW.js.map +1 -0
  10. package/dist/{bun-I7YM3RDB.js → bun-VPXMWFBA.js} +6 -4
  11. package/dist/bun-VPXMWFBA.js.map +1 -0
  12. package/dist/{chunk-SG62LZOJ.js → chunk-25OXNMVO.js} +4 -4
  13. package/dist/{chunk-QX5Q3FBJ.js → chunk-7FIK5NKN.js} +10 -10
  14. package/dist/{chunk-SHIXTMQE.js → chunk-DA3GGICE.js} +16 -2
  15. package/dist/{chunk-SHIXTMQE.js.map → chunk-DA3GGICE.js.map} +1 -1
  16. package/dist/{chunk-ZNZISJDZ.js → chunk-L4V5V2TI.js} +1 -1
  17. package/dist/{chunk-ZNZISJDZ.js.map → chunk-L4V5V2TI.js.map} +1 -1
  18. package/dist/{chunk-UFBBFP75.js → chunk-NLHAYGQQ.js} +2 -2
  19. package/dist/{chunk-X2LFNDJQ.js → chunk-NX3P5MLH.js} +2 -2
  20. package/dist/{chunk-2NG5T77O.js → chunk-R4AMIBZL.js} +16 -2
  21. package/dist/{chunk-2NG5T77O.js.map → chunk-R4AMIBZL.js.map} +1 -1
  22. package/dist/{chunk-NQZIQJFH.js → chunk-SHFZKJCU.js} +7 -7
  23. package/dist/{chunk-QAS5SKZP.js → chunk-VD3BCME6.js} +2 -2
  24. package/dist/{chunk-SZWGXHY3.js → chunk-VOXLI66L.js} +2 -2
  25. package/dist/{chunk-HK57MPSZ.js → chunk-VPK7ABMB.js} +2 -2
  26. package/dist/cli/index.js +9 -9
  27. package/dist/{cloudflare-Q5SUXRXY.js → cloudflare-PRTTYWRD.js} +3 -3
  28. package/dist/{config-support-MOZYHRWG.js → config-support-HUZ4DXR7.js} +3 -3
  29. package/dist/{deno-deploy-XW3Q6OMD.js → deno-deploy-M5V3PYWU.js} +68 -12
  30. package/dist/deno-deploy-M5V3PYWU.js.map +1 -0
  31. package/dist/{dev-FZ4OTCPT.js → dev-LMZGOV4M.js} +7 -7
  32. package/dist/index.js +3 -3
  33. package/dist/{info-SUX7QVA3.js → info-XURFOBRF.js} +4 -4
  34. package/dist/{internal-api-GKPJYL3G.js → internal-api-ATSPOXI2.js} +2 -2
  35. package/dist/{internal-api-ZUBUYGCQ.js → internal-api-XPT4GLYX.js} +2 -2
  36. package/dist/{load-config-52H3EKAA.js → load-config-2DXLI7NW.js} +4 -4
  37. package/dist/{mcp-VUHRRX36.js → mcp-4CXNOZA4.js} +3 -3
  38. package/dist/{netlify-ALPCTQFO.js → netlify-NI4HYDKN.js} +28 -9
  39. package/dist/netlify-NI4HYDKN.js.map +1 -0
  40. package/dist/node-LORYECWI.js +10 -0
  41. package/dist/{openapi-C4UX2SFV.js → openapi-UJSUQPNT.js} +4 -4
  42. package/dist/{preview-II33QNAC.js → preview-WTPUW47O.js} +3 -3
  43. package/dist/registry-SUE4HJXL.js +34 -0
  44. package/dist/{routes-3PTNISSO.js → routes-ENMU4GAN.js} +4 -4
  45. package/dist/{schema-LHNWUR4V.js → schema-DJH3IOVR.js} +3 -3
  46. package/dist/{server-boundary-QCJNECJP.js → server-boundary-JM72DQLA.js} +2 -2
  47. package/dist/{server-boundary-RTRPHX5I.js → server-boundary-K4YQXDQT.js} +2 -2
  48. package/dist/{services-typed-client-XGT5SUUN.js → services-typed-client-FOBFCRD6.js} +2 -2
  49. package/dist/{start-EREUCSN6.js → start-GAULIE5E.js} +7 -7
  50. package/dist/{static-7LP7LPGN.js → static-O2RZ5NLR.js} +3 -3
  51. package/dist/{theo-cloud-GIA67L4Q.js → theo-cloud-B4R7GCWK.js} +3 -3
  52. package/dist/{vercel-VLR52RXT.js → vercel-INHLOCSZ.js} +3 -3
  53. package/dist/vite-plugin/index.js +3 -3
  54. package/dist/{vite-plugin-3LTZJTUM.js → vite-plugin-NXWE2AVM.js} +7 -7
  55. package/package.json +3 -3
  56. package/dist/build-7YZSJGP2.js.map +0 -1
  57. package/dist/bun-I7YM3RDB.js.map +0 -1
  58. package/dist/deno-deploy-XW3Q6OMD.js.map +0 -1
  59. package/dist/netlify-ALPCTQFO.js.map +0 -1
  60. package/dist/node-4HQO6KKZ.js +0 -10
  61. package/dist/registry-WVUO2WNR.js +0 -34
  62. /package/dist/{actions-virtual-module-JV7URROJ.js.map → actions-virtual-module-4J7Z37Y4.js.map} +0 -0
  63. /package/dist/{actions-virtual-module-J2HYO7QZ.js.map → actions-virtual-module-MIQJEHKJ.js.map} +0 -0
  64. /package/dist/{agent-5J7CQAYR.js.map → agent-QJINXU6U.js.map} +0 -0
  65. /package/dist/{app-typed-client-XD4DZBV7.js.map → app-typed-client-AZMUYIYV.js.map} +0 -0
  66. /package/dist/{app-typed-client-EOA3A2AR.js.map → app-typed-client-DSS23KDC.js.map} +0 -0
  67. /package/dist/{aws-lambda-ZYSPTD3O.js.map → aws-lambda-K5Y6XS5V.js.map} +0 -0
  68. /package/dist/{chunk-SG62LZOJ.js.map → chunk-25OXNMVO.js.map} +0 -0
  69. /package/dist/{chunk-QX5Q3FBJ.js.map → chunk-7FIK5NKN.js.map} +0 -0
  70. /package/dist/{chunk-UFBBFP75.js.map → chunk-NLHAYGQQ.js.map} +0 -0
  71. /package/dist/{chunk-X2LFNDJQ.js.map → chunk-NX3P5MLH.js.map} +0 -0
  72. /package/dist/{chunk-NQZIQJFH.js.map → chunk-SHFZKJCU.js.map} +0 -0
  73. /package/dist/{chunk-QAS5SKZP.js.map → chunk-VD3BCME6.js.map} +0 -0
  74. /package/dist/{chunk-SZWGXHY3.js.map → chunk-VOXLI66L.js.map} +0 -0
  75. /package/dist/{chunk-HK57MPSZ.js.map → chunk-VPK7ABMB.js.map} +0 -0
  76. /package/dist/{cloudflare-Q5SUXRXY.js.map → cloudflare-PRTTYWRD.js.map} +0 -0
  77. /package/dist/{config-support-MOZYHRWG.js.map → config-support-HUZ4DXR7.js.map} +0 -0
  78. /package/dist/{dev-FZ4OTCPT.js.map → dev-LMZGOV4M.js.map} +0 -0
  79. /package/dist/{info-SUX7QVA3.js.map → info-XURFOBRF.js.map} +0 -0
  80. /package/dist/{internal-api-GKPJYL3G.js.map → internal-api-ATSPOXI2.js.map} +0 -0
  81. /package/dist/{internal-api-ZUBUYGCQ.js.map → internal-api-XPT4GLYX.js.map} +0 -0
  82. /package/dist/{load-config-52H3EKAA.js.map → load-config-2DXLI7NW.js.map} +0 -0
  83. /package/dist/{mcp-VUHRRX36.js.map → mcp-4CXNOZA4.js.map} +0 -0
  84. /package/dist/{node-4HQO6KKZ.js.map → node-LORYECWI.js.map} +0 -0
  85. /package/dist/{openapi-C4UX2SFV.js.map → openapi-UJSUQPNT.js.map} +0 -0
  86. /package/dist/{preview-II33QNAC.js.map → preview-WTPUW47O.js.map} +0 -0
  87. /package/dist/{registry-WVUO2WNR.js.map → registry-SUE4HJXL.js.map} +0 -0
  88. /package/dist/{routes-3PTNISSO.js.map → routes-ENMU4GAN.js.map} +0 -0
  89. /package/dist/{schema-LHNWUR4V.js.map → schema-DJH3IOVR.js.map} +0 -0
  90. /package/dist/{server-boundary-QCJNECJP.js.map → server-boundary-JM72DQLA.js.map} +0 -0
  91. /package/dist/{server-boundary-RTRPHX5I.js.map → server-boundary-K4YQXDQT.js.map} +0 -0
  92. /package/dist/{services-typed-client-XGT5SUUN.js.map → services-typed-client-FOBFCRD6.js.map} +0 -0
  93. /package/dist/{start-EREUCSN6.js.map → start-GAULIE5E.js.map} +0 -0
  94. /package/dist/{static-7LP7LPGN.js.map → static-O2RZ5NLR.js.map} +0 -0
  95. /package/dist/{theo-cloud-GIA67L4Q.js.map → theo-cloud-B4R7GCWK.js.map} +0 -0
  96. /package/dist/{vercel-VLR52RXT.js.map → vercel-INHLOCSZ.js.map} +0 -0
  97. /package/dist/{vite-plugin-3LTZJTUM.js.map → vite-plugin-NXWE2AVM.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -15,12 +15,12 @@ function cliVersion() {
15
15
  // src/cli/index.ts
16
16
  var cli = cac("theokit");
17
17
  cli.command("dev", "Start development server").option("--port <port>", "Port number").action(async (options) => {
18
- const { devCommand } = await import("../dev-FZ4OTCPT.js");
18
+ const { devCommand } = await import("../dev-LMZGOV4M.js");
19
19
  await devCommand({ port: options.port ? Number(options.port) : void 0 });
20
20
  });
21
21
  cli.command("build", "Build for production").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
22
22
  try {
23
- const { buildCommand } = await import("../build-7YZSJGP2.js");
23
+ const { buildCommand } = await import("../build-AKN2NCFW.js");
24
24
  await buildCommand({ target: options.target });
25
25
  } catch (err) {
26
26
  const msg = err instanceof Error ? err.message : String(err);
@@ -32,7 +32,7 @@ cli.command("build", "Build for production").option("--target <target>", "Deploy
32
32
  });
33
33
  cli.command("start", "Start production server").option("--port <port>", "Port number").action(async (options) => {
34
34
  try {
35
- const { startCommand } = await import("../start-EREUCSN6.js");
35
+ const { startCommand } = await import("../start-GAULIE5E.js");
36
36
  await startCommand({ port: options.port ? Number(options.port) : void 0 });
37
37
  } catch (err) {
38
38
  const msg = err instanceof Error ? err.message : String(err);
@@ -44,7 +44,7 @@ cli.command("start", "Start production server").option("--port <port>", "Port nu
44
44
  });
45
45
  cli.command("preview", "Build for production, then serve it \u2014 one step (B-030)").option("--port <port>", "Port number").option("--target <target>", "Deploy target (node, vercel, cloudflare)").action(async (options) => {
46
46
  try {
47
- const { previewCommand } = await import("../preview-II33QNAC.js");
47
+ const { previewCommand } = await import("../preview-WTPUW47O.js");
48
48
  await previewCommand({
49
49
  port: options.port ? Number(options.port) : void 0,
50
50
  target: options.target
@@ -77,7 +77,7 @@ cli.command(
77
77
  "Run an agent in the terminal (stream + tool calls + approval)"
78
78
  ).action(async (name, message) => {
79
79
  try {
80
- const { agentCommand } = await import("../agent-5J7CQAYR.js");
80
+ const { agentCommand } = await import("../agent-QJINXU6U.js");
81
81
  const { sawError } = await agentCommand(name, message);
82
82
  if (sawError) process.exit(1);
83
83
  } catch (err) {
@@ -108,7 +108,7 @@ cli.command("agent sessions gc", "Collect old transcripts for this project (dry
108
108
  });
109
109
  cli.command("mcp <agent>", "Serve an agent as an MCP server over stdio (for desktop MCP clients)").action(async (agent) => {
110
110
  try {
111
- const { mcpCommand } = await import("../mcp-VUHRRX36.js");
111
+ const { mcpCommand } = await import("../mcp-4CXNOZA4.js");
112
112
  await mcpCommand(agent);
113
113
  } catch (err) {
114
114
  const msg = err instanceof Error ? err.message : String(err);
@@ -120,7 +120,7 @@ cli.command("mcp <agent>", "Serve an agent as an MCP server over stdio (for desk
120
120
  });
121
121
  cli.command("routes", "List all routes, actions, and WebSocket endpoints").action(async () => {
122
122
  try {
123
- const { routesCommand } = await import("../routes-3PTNISSO.js");
123
+ const { routesCommand } = await import("../routes-ENMU4GAN.js");
124
124
  await routesCommand();
125
125
  } catch (err) {
126
126
  const msg = err instanceof Error ? err.message : String(err);
@@ -161,7 +161,7 @@ cli.command("add <package>", "Install a known TheoKit adapter or plugin (whiteli
161
161
  await addCommand(pkg);
162
162
  });
163
163
  cli.command("info", "Print environment info (runtime, config, routes)").action(async () => {
164
- const { infoCommand } = await import("../info-SUX7QVA3.js");
164
+ const { infoCommand } = await import("../info-XURFOBRF.js");
165
165
  await infoCommand();
166
166
  });
167
167
  cli.command("doctor", "Report the RESOLVED state of this installation (never prints a secret)").action(async () => {
@@ -173,7 +173,7 @@ cli.command(
173
173
  "Generate <distDir>/openapi.json from route schemas (opt-in via config.openapi)"
174
174
  ).option("--dry-run", "Print the document to stdout without writing to disk (EC-3)").action(async (options) => {
175
175
  try {
176
- const { openapiCommand } = await import("../openapi-C4UX2SFV.js");
176
+ const { openapiCommand } = await import("../openapi-UJSUQPNT.js");
177
177
  await openapiCommand({ dryRun: Boolean(options.dryRun) });
178
178
  } catch (err) {
179
179
  const msg = err instanceof Error ? err.message : String(err);
@@ -27,13 +27,13 @@ import {
27
27
  } from "./chunk-EQJF2EH5.js";
28
28
  import {
29
29
  nodeAdapter
30
- } from "./chunk-UFBBFP75.js";
30
+ } from "./chunk-NLHAYGQQ.js";
31
31
  import "./chunk-3PWQQWT6.js";
32
32
  import "./chunk-WSMLSIS5.js";
33
33
  import {
34
34
  assertServicesUnsupported,
35
35
  readManifest
36
- } from "./chunk-ZNZISJDZ.js";
36
+ } from "./chunk-L4V5V2TI.js";
37
37
 
38
38
  // src/adapters/cloudflare.ts
39
39
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -516,4 +516,4 @@ export {
516
516
  renderWorkersUnsupportedStub,
517
517
  renderWranglerToml
518
518
  };
519
- //# sourceMappingURL=cloudflare-Q5SUXRXY.js.map
519
+ //# sourceMappingURL=cloudflare-PRTTYWRD.js.map
@@ -2,8 +2,8 @@
2
2
  import "tsx/esm";
3
3
  import {
4
4
  nodeAdapter
5
- } from "./chunk-UFBBFP75.js";
6
- import "./chunk-ZNZISJDZ.js";
5
+ } from "./chunk-NLHAYGQQ.js";
6
+ import "./chunk-L4V5V2TI.js";
7
7
 
8
8
  // src/adapters/config-support.ts
9
9
  var CONFIG_CONCERNS = [
@@ -149,4 +149,4 @@ export {
149
149
  findUnappliedConfig,
150
150
  warnUnappliedConfig
151
151
  };
152
- //# sourceMappingURL=config-support-MOZYHRWG.js.map
152
+ //# sourceMappingURL=config-support-HUZ4DXR7.js.map
@@ -19,16 +19,61 @@ import {
19
19
  } from "./chunk-EQJF2EH5.js";
20
20
  import {
21
21
  nodeAdapter
22
- } from "./chunk-UFBBFP75.js";
22
+ } from "./chunk-NLHAYGQQ.js";
23
23
  import "./chunk-WSMLSIS5.js";
24
24
  import {
25
25
  assertServicesUnsupported,
26
26
  readManifest
27
- } from "./chunk-ZNZISJDZ.js";
27
+ } from "./chunk-L4V5V2TI.js";
28
28
 
29
29
  // src/adapters/deno-deploy.ts
30
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
30
+ import {
31
+ cpSync,
32
+ existsSync,
33
+ mkdirSync,
34
+ readdirSync,
35
+ readFileSync,
36
+ rmSync,
37
+ writeFileSync
38
+ } from "fs";
31
39
  import { resolve } from "path";
40
+ var DENO_DEPLOY_CLIENT_DIR = "theokit-deploy/client";
41
+ var CLIENT_SERVER_FRAGMENT = [
42
+ `const CLIENT_DIR = cwd + '/${DENO_DEPLOY_CLIENT_DIR}'`,
43
+ `const CLIENT_TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.map': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp', '.avif': 'image/avif', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.txt': 'text/plain; charset=utf-8', '.wasm': 'application/wasm' }`,
44
+ ``,
45
+ `async function readClientFile(path) {`,
46
+ ` try {`,
47
+ ` return await Deno.readFile(CLIENT_DIR + path)`,
48
+ ` } catch (err) {`,
49
+ ` if (err instanceof Deno.errors.NotFound || err?.code === 'EISDIR' || err?.name === 'IsADirectory') return null`,
50
+ ` throw err`,
51
+ ` }`,
52
+ `}`,
53
+ ``,
54
+ `async function serveClient(request, url) {`,
55
+ ` if (request.method !== 'GET' && request.method !== 'HEAD') return notFound()`,
56
+ ` let path`,
57
+ ` try { path = decodeURIComponent(url.pathname) } catch { return notFound() }`,
58
+ ` if (path.split('/').includes('..')) return notFound()`,
59
+ ` if (path.endsWith('/')) path += 'index.html'`,
60
+ ` const dot = path.lastIndexOf('.')`,
61
+ ` const ext = dot > path.lastIndexOf('/') ? path.slice(dot).toLowerCase() : ''`,
62
+ ` const body = await readClientFile(path)`,
63
+ ` if (body !== null) {`,
64
+ ` const headers = { 'Content-Type': CLIENT_TYPES[ext] ?? 'application/octet-stream' }`,
65
+ ` if (path.startsWith('/assets/')) headers['Cache-Control'] = 'public, max-age=31536000, immutable'`,
66
+ ` return new Response(request.method === 'HEAD' ? null : body, { status: 200, headers })`,
67
+ ` }`,
68
+ ` if (ext !== '') return notFound()`,
69
+ ` const document = await readClientFile('/index.html')`,
70
+ ` if (document === null) return notFound()`,
71
+ ` return new Response(request.method === 'HEAD' ? null : document, {`,
72
+ ` status: 200,`,
73
+ ` headers: { 'Content-Type': 'text/html; charset=utf-8' },`,
74
+ ` })`,
75
+ `}`
76
+ ];
32
77
  var DEPLOY_INSTRUCTIONS = [
33
78
  `// Deploy from the project root, so the entry finds its own src/server tree:`,
34
79
  `// deno deploy --org <org> --app <name> --prod \\`,
@@ -97,11 +142,12 @@ function renderDenoEntry(port, opts = {}) {
97
142
  ``,
98
143
  `// #410 \u2014 the security baseline \`theokit start\` puts on every response,`,
99
144
  `// carried here as a literal because the deployed isolate has no`,
100
- `// theo.config.ts to read. Non-API paths 404 here and are served by Deno`,
101
- `// Deploy's static asset handler, so this covers the API and not the`,
102
- `// document (usetheokit/theokit#412).`,
145
+ `// theo.config.ts to read. The document is served by this entry too`,
146
+ `// (usetheokit/theokit#951), so it carries the baseline as well.`,
103
147
  ...deployedEntryPreamble(runtimeConfig, agentsFragment, opts, TARGET),
104
148
  ``,
149
+ ...CLIENT_SERVER_FRAGMENT,
150
+ ``,
105
151
  `function notFound() {`,
106
152
  ` return new Response(JSON.stringify({ error: { code: 'NOT_FOUND' } }), {`,
107
153
  ` status: 404,`,
@@ -164,8 +210,7 @@ function renderDenoEntry(port, opts = {}) {
164
210
  ` const url = new URL(request.url)`,
165
211
  ``,
166
212
  ` if (!url.pathname.startsWith('/api/')${agentsFragment.hostBypass}) {`,
167
- ` // Static + SPA fallback are served by Deno Deploy's static asset handler.`,
168
- ` return notFound()`,
213
+ ` return serveClient(request, url)`,
169
214
  ` }`,
170
215
  ``,
171
216
  ` if (!routesCache) routesCache = scanServerRoutes(serverDir)`,
@@ -219,6 +264,7 @@ async function buildDeno(config, cwd, deps = {}, ctx) {
219
264
  write(resolve(outputDir, "theo.plugins.mjs"), pluginsPlan.source);
220
265
  }
221
266
  write(resolve(cwd, DENO_DEPLOY_ENTRY_PATH), entry);
267
+ copyClientBesideEntry(cwd, deps);
222
268
  const readSources = deps.readProjectSources ?? readProjectSourcesFromDisk;
223
269
  const readConfig = deps.readDenoConfig ?? readDenoConfigFromDisk;
224
270
  const denoConfig = renderDenoConfig({
@@ -242,6 +288,15 @@ async function buildDeno(config, cwd, deps = {}, ctx) {
242
288
  );
243
289
  }
244
290
  var DENO_DEPLOY_ENTRY_PATH = "theokit-deploy/server.ts";
291
+ function copyClientBesideEntry(cwd, deps) {
292
+ const copy = deps.copyClient ?? copyClientOnDisk;
293
+ copy(resolve(cwd, ".theokit", "client"), resolve(cwd, DENO_DEPLOY_CLIENT_DIR));
294
+ }
295
+ function copyClientOnDisk(from, to) {
296
+ if (!existsSync(from)) return;
297
+ rmSync(to, { recursive: true, force: true });
298
+ cpSync(from, to, { recursive: true });
299
+ }
245
300
  var DENO_DEPLOY_CONFIG_PATH = "deno.json";
246
301
  function bareSpecifiersOf(sources) {
247
302
  const found = /* @__PURE__ */ new Set();
@@ -306,9 +361,9 @@ var denoDeployAdapter = {
306
361
  // configurable concerns reach it. Declared explicitly rather than omitted so
307
362
  // the gap is a statement in the source and not an absence.
308
363
  //
309
- // `securityHeaders` IS applied -- to every response this isolate returns. The
310
- // document comes from Deno Deploy's static asset handler and does not pass
311
- // through it (usetheokit/theokit#412).
364
+ // `securityHeaders` IS applied -- to every response this isolate returns,
365
+ // the document included, since the entry serves the client build itself
366
+ // (usetheokit/theokit#951).
312
367
  servesAgents: true,
313
368
  appliesConfig: ["securityHeaders", "csrf", "disallowed", "cors", "serialization", "plugins"],
314
369
  // B-257 — per-invocation: an in-process counter does not survive, so a declared limit needs a durable store.
@@ -318,6 +373,7 @@ var denoDeployAdapter = {
318
373
  }
319
374
  };
320
375
  export {
376
+ DENO_DEPLOY_CLIENT_DIR,
321
377
  DENO_DEPLOY_CONFIG_PATH,
322
378
  DENO_DEPLOY_ENTRY_PATH,
323
379
  bareSpecifiersOf,
@@ -326,4 +382,4 @@ export {
326
382
  renderDenoConfig,
327
383
  renderDenoEntry
328
384
  };
329
- //# sourceMappingURL=deno-deploy-XW3Q6OMD.js.map
385
+ //# sourceMappingURL=deno-deploy-M5V3PYWU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/deno-deploy.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Deno Deploy adapter. Writes to `cwd/.theokit/deno-deploy/`. Build-time.\n */\nimport {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport type { TheoConfig } from '../config/schema.js'\nimport { assertServicesUnsupported, readManifest } from '../services/index.js'\n\nimport { deployedAgentsFragment, scannedFromLoaderCache } from './deployed-agents.js'\nimport { planDeployedPlugins } from './deployed-plugins-module.js'\nimport { deployedEntryPreamble } from './deployed-preamble.js'\nimport { deployedRateLimitFragment, rateLimitCheckFragment } from './deployed-rate-limit.js'\nimport {\n agentsDirLiteral,\n deployedRuntimeConfigFragment,\n serverDirLiteral,\n} from './deployed-runtime-config.js'\nimport { deployedTraceFragment } from './deployed-trace.js'\nimport { nodeAdapter } from './node.js'\nimport { describeDeployedSecurityHeaders } from './security-headers.js'\nimport type { AdapterBuildContext, DeployAdapter, DeployedEntryOptions } from './types.js'\n\nexport interface DenoBuildDeps {\n runNodeBuild?: (config: TheoConfig, cwd: string, ctx?: AdapterBuildContext) => Promise<void>\n writeEntry?: (path: string, content: string) => void\n ensureDir?: (path: string) => void\n /** The project's own source texts, whose bare imports the emitted map has to resolve. */\n readProjectSources?: (cwd: string, config: TheoConfig) => string[]\n /** The Deno config already on disk, merged rather than replaced. */\n readDenoConfig?: (cwd: string) => DenoConfig | undefined\n /** Copies the client build to where the entry serves it from. */\n copyClient?: (from: string, to: string) => void\n}\n\n/**\n * The framework imports this entry opens with, as `npm:` specifiers.\n *\n * Extracted from `renderDenoEntry` when adding the configured-directory literal pushed it past the\n * per-function line budget. A cohesive block — every line is one import of the same package family\n * — so lifting it keeps the budget a signal about the renderer rather than about its preamble.\n */\n/**\n * How this entry is deployed, and the one failure whose message names the wrong thing.\n *\n * Extracted for the same reason `FRAMEWORK_IMPORTS` above was: a cohesive block of emitted\n * comment lines that pushed the renderer past its line budget, where the budget is meant to\n * be a signal about the renderer rather than about its preamble.\n */\n/** Where the entry serves the client build from — beside it, inside the upload (#951). */\nexport const DENO_DEPLOY_CLIENT_DIR = 'theokit-deploy/client'\n\n/**\n * The client build, served by the entry itself (#951).\n *\n * Measured 2026-10-03: on a dynamic Deno Deploy app `/`, `/about` and every asset answered this\n * entry's JSON 404, because it returned `notFound()` for each non-API path on the assumption that\n * \"Deno Deploy's static asset handler\" serves them. A dynamic app has no such handler.\n *\n * Served from `theokit-deploy/client`, a copy the build writes, and not from `.theokit/client`.\n * Measured on the platform the same day: the upload honours `.gitignore`, and the scaffold ignores\n * `.theokit/`, so the build output never arrived. Uploading `.theokit` anyway broke every agent\n * route, because its `manifest.json` carries the build machine's absolute paths.\n *\n * `Deno.readFile` rather than `jsr:@std/http`'s `serveDir`: no new specifier for the platform to\n * resolve. An extensionless path is a client route and gets the document; a missing asset stays a\n * 404 so a broken chunk is not answered with HTML. A decoded `..` segment is refused before the\n * path is joined, so nothing outside the build is readable.\n */\nconst CLIENT_SERVER_FRAGMENT: readonly string[] = [\n `const CLIENT_DIR = cwd + '/${DENO_DEPLOY_CLIENT_DIR}'`,\n `const CLIENT_TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.map': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp', '.avif': 'image/avif', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.txt': 'text/plain; charset=utf-8', '.wasm': 'application/wasm' }`,\n ``,\n `async function readClientFile(path) {`,\n ` try {`,\n ` return await Deno.readFile(CLIENT_DIR + path)`,\n ` } catch (err) {`,\n ` if (err instanceof Deno.errors.NotFound || err?.code === 'EISDIR' || err?.name === 'IsADirectory') return null`,\n ` throw err`,\n ` }`,\n `}`,\n ``,\n `async function serveClient(request, url) {`,\n ` if (request.method !== 'GET' && request.method !== 'HEAD') return notFound()`,\n ` let path`,\n ` try { path = decodeURIComponent(url.pathname) } catch { return notFound() }`,\n ` if (path.split('/').includes('..')) return notFound()`,\n ` if (path.endsWith('/')) path += 'index.html'`,\n ` const dot = path.lastIndexOf('.')`,\n ` const ext = dot > path.lastIndexOf('/') ? path.slice(dot).toLowerCase() : ''`,\n ` const body = await readClientFile(path)`,\n ` if (body !== null) {`,\n ` const headers = { 'Content-Type': CLIENT_TYPES[ext] ?? 'application/octet-stream' }`,\n ` if (path.startsWith('/assets/')) headers['Cache-Control'] = 'public, max-age=31536000, immutable'`,\n ` return new Response(request.method === 'HEAD' ? null : body, { status: 200, headers })`,\n ` }`,\n ` if (ext !== '') return notFound()`,\n ` const document = await readClientFile('/index.html')`,\n ` if (document === null) return notFound()`,\n ` return new Response(request.method === 'HEAD' ? null : document, {`,\n ` status: 200,`,\n ` headers: { 'Content-Type': 'text/html; charset=utf-8' },`,\n ` })`,\n `}`,\n]\n\nconst DEPLOY_INSTRUCTIONS: readonly string[] = [\n `// Deploy from the project root, so the entry finds its own src/server tree:`,\n `// deno deploy --org <org> --app <name> --prod \\\\`,\n `// --ignore node_modules --ignore package.json`,\n `// Both exclusions are load-bearing, and both were isolated on the platform by`,\n `// changing one thing at a time. node_modules: pnpm's is a symlink farm the upload`,\n `// cannot carry, and this entry reaches the framework through npm: specifiers, so`,\n `// it needs none of it. package.json: it is what Deno resolves those specifiers`,\n `// against, so its version ranges bring the minimum-dependency-age policy with`,\n `// them — with the manifest in the upload the revision failed, without it the same`,\n `// directory deployed and served.`,\n `//`,\n `// If a deploy that keeps the manifest fails with \"Could not find npm package`,\n `// <name> matching <range>\",`,\n `// read the rest of that message before changing anything: Deno applies a`,\n `// minimum-dependency-age policy (24h by default) and refuses a version published`,\n `// inside that window, naming the PACKAGE rather than the policy. The range comes`,\n `// from this project's package.json, which the upload carries. It clears by itself;`,\n `// to deploy now, pass --min-dep-age=0 or set minimumDependencyAge in deno.json —`,\n `// both lower a supply-chain protection, which is why nothing here sets it for you.`,\n]\n\nconst FRAMEWORK_IMPORTS: readonly string[] = [\n `// Use npm: specifier so Deno resolves theokit from the user's package.json`,\n `// equivalent (works in both local 'deno run' and Deno Deploy).`,\n // Sub-paths, not the umbrella. `theokit/server` is DEPRECATED by the framework itself with a\n // REMOVAL scheduled, so this entry would stop loading on that release, before a request exists —\n // measured on Deno 2.9.5, where the warning printed on every start. This was the only adapter still\n // on the umbrella; the split below is the mapping four siblings already use and Bun proves by\n // running.\n `import { scanServerRoutes, matchRoute, createProductionLoader, scanWebSocketRoutes } from 'npm:theokit/server/scan'`,\n `import { executeRoute, extractTraceIdFromRequest, TRACE_HEADER, createCorsWebHandler } from 'npm:theokit/server/http'`,\n `import { createWebShim } from 'npm:theokit/adapters/web-shim'`,\n `import { buildSecurityHeaders, withSecurityHeaders } from 'npm:theokit/adapters/security-headers'`,\n `// T3.3 — WS bridge for Deno runtime`,\n `import { createDenoWsBridge } from 'npm:theokit/adapters/ws-shim'`,\n]\n\n/** The target's name, in the five places that spell it. Adding a sixth tripped the duplicate-literal gate. */\n/* The name in the places a gate does NOT read. `assertServicesUnsupported` keeps the literal:\n * `services-other-adapters-reject.test.ts:30` requires each adapter to name ITSELF there, so a\n * copy-paste carrying another adapter's name cannot hide behind a constant. */\nconst TARGET = 'deno-deploy'\n\nexport function renderDenoEntry(port: number, opts: DeployedEntryOptions = {}): string {\n const runtimeConfig = deployedRuntimeConfigFragment(opts)\n const agentsFragment = deployedAgentsFragment(scannedFromLoaderCache(agentsDirLiteral(opts)), {\n notFound: 'notFound()',\n importPrefix: 'npm:',\n // B-185 — bound only when the runtime-config fragment declared the const, which is\n // exactly when a plugins module was emitted. Referencing it otherwise would emit an\n // identifier the entry never declares.\n pluginRunnerExpr:\n opts.runtimeConfigModule === undefined ? undefined : 'await THEO_PLUGIN_RUNNER',\n })\n return [\n `// Generated by Theo — Deno Deploy Adapter`,\n ...DEPLOY_INSTRUCTIONS,\n `// Requires Deno >= 1.40 (Deno.serve + npm: specifier support)`,\n ``,\n `// Runtime guard`,\n `if (typeof Deno === 'undefined') {`,\n ` throw new Error('TheoDenoDeployAdapter must run inside Deno.')`,\n `}`,\n ``,\n ...FRAMEWORK_IMPORTS,\n ``,\n `const port = Number(Deno.env.get('PORT') ?? '${port}')`,\n `const cwd = Deno.cwd()`,\n `const serverDir = cwd + '/' + ${serverDirLiteral(opts)}`,\n `let routesCache = null`,\n `let wsRoutesCache = null`,\n `let loaderCache = null`,\n ``,\n `// #410 — the security baseline \\`theokit start\\` puts on every response,`,\n `// carried here as a literal because the deployed isolate has no`,\n `// theo.config.ts to read. The document is served by this entry too`,\n `// (usetheokit/theokit#951), so it carries the baseline as well.`,\n ...deployedEntryPreamble(runtimeConfig, agentsFragment, opts, TARGET),\n ``,\n ...CLIENT_SERVER_FRAGMENT,\n ``,\n `function notFound() {`,\n ` return new Response(JSON.stringify({ error: { code: 'NOT_FOUND' } }), {`,\n ` status: 404,`,\n ` headers: { 'Content-Type': 'application/json' },`,\n ` })`,\n `}`,\n ``,\n // B-027 — `npm:`, like every other specifier this target emits (`:37-43`). A bare one does\n // not resolve on Deno Deploy, and the harness asserts the prefix is here before rewriting it.\n ...(opts.rateLimit === undefined\n ? []\n : [\n `import { createRateLimiterWeb } from 'npm:theokit/server/rate-limit'`,\n `import { resolveClientIpFromRequest } from 'npm:theokit/server/rate-limit'`,\n ``,\n ]),\n ...deployedRateLimitFragment(\n opts.rateLimit,\n TARGET,\n // `info.remoteAddr` is the runtime's own answer. A `UnixAddr` has no `hostname`, so the\n // optional chain is what keeps the read from throwing there rather than a guess.\n `info?.remoteAddr?.hostname ?? resolveClientIpFromRequest(request, TRUST_PROXY)`,\n 'request, info',\n // B-257 — `npm:`, like every other specifier this target emits. A bare one does not resolve\n // on Deno Deploy, and the durable limiter's import is emitted by the fragment rather than\n // here, so the prefix has to travel with it.\n 'npm:',\n ),\n ``,\n // The serve handler's SECOND parameter is bound here for the first time: `Deno.serve` has\n // always passed it and the emitted entry discarded it.\n `Deno.serve({ port }, async (request, info) => {`,\n ` // #409 — the preflight is answered BEFORE anything routes: an OPTIONS the router`,\n ` // handles is an OPTIONS the browser never gets a CORS answer to.`,\n ` const preflight = corsPreflight(request)`,\n ` if (preflight !== null) return withSecurityHeaders(preflight, SECURITY_HEADERS)`,\n ...rateLimitCheckFragment(opts.rateLimit, ' ', 'request, info'),\n ``,\n ` // LCR0103 — the upgrade branch sits BELOW the limiter, and the order is the point.`,\n ` // It used to sit above, under the comment \"a 101 carries no document and no script,`,\n ` // so the security baseline does not apply to it\". That is true of the security`,\n ` // HEADERS — a document concern — and false of the limiter, which is a resource`,\n ` // concern: a long-lived socket is the most expensive thing this entry hands out, so`,\n ` // the upgrade is the path that most needs a budget, not the one that may skip it.`,\n ` // T3.3 — Detect WebSocket upgrade and delegate to the Deno bridge.`,\n ` // A 101 carries no document and no script, so the security baseline does`,\n ` // not apply to it.`,\n ` if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {`,\n ` if (!wsRoutesCache) wsRoutesCache = scanWebSocketRoutes(serverDir)`,\n ` if (wsRoutesCache.length === 0) return notFound()`,\n ` const denoWs = createDenoWsBridge({`,\n ` onOpen: () => {},`,\n ` onMessage: (ws, data) => { ws.send(data) },`,\n ` onClose: () => {},`,\n ` }, Deno)`,\n ` return denoWs.handle(request)`,\n ` }`,\n ` return withCors(request, withSecurityHeaders(await handleRequest(request), SECURITY_HEADERS))`,\n `})`,\n ``,\n `async function handleRequest(request) {`,\n ` const url = new URL(request.url)`,\n ``,\n ` if (!url.pathname.startsWith('/api/')${agentsFragment.hostBypass}) {`,\n ` return serveClient(request, url)`,\n ` }`,\n ``,\n ` if (!routesCache) routesCache = scanServerRoutes(serverDir)`,\n ` if (!loaderCache) loaderCache = createProductionLoader()`,\n ``,\n ...agentsFragment.branch,\n ``,\n ` const match = matchRoute(url.pathname, routesCache)`,\n ` if (!match) return notFound()`,\n ``,\n ` const { req, res, toResponse } = createWebShim(request)`,\n ...deployedTraceFragment('request', ' '),\n ` const method = request.method.toUpperCase()`,\n ` // #382 — not awaited: toResponse() settles at the headers and carries a`,\n ` // live body, so Deno.serve streams while the handler writes.`,\n ` return toResponse(executeRoute({ route: match.route, method, params: match.params, req, res, loadModule: loaderCache, serverDir, requestId, ...CSRF_CONFIG, ${runtimeConfig.executeRouteSpread} }))`,\n `}`,\n ``,\n `console.log('Theo (Deno Deploy) listening on :' + port)`,\n ].join('\\n')\n}\n\nexport async function buildDeno(\n config: TheoConfig,\n cwd: string,\n deps: DenoBuildDeps = {},\n ctx?: AdapterBuildContext,\n): Promise<void> {\n // Wave 2 (T2.2) — reject polyglot services on this adapter.\n assertServicesUnsupported('deno-deploy', readManifest(cwd))\n\n const runNodeBuild = deps.runNodeBuild ?? nodeAdapter.build.bind(nodeAdapter)\n await runNodeBuild(config, cwd, ctx)\n\n const outputDir = resolve(cwd, DENO_DEPLOY_ENTRY_PATH, '..')\n const ensureDir = deps.ensureDir ?? ((p: string) => mkdirSync(p, { recursive: true }))\n ensureDir(outputDir)\n\n const pluginsPlan = planDeployedPlugins(config.plugins, denoDeployAdapter.name)\n const entry = renderDenoEntry(config.port, {\n securityHeaders: config.security?.headers,\n csrf: config.security?.csrf,\n disallowed: config.security?.disallowed,\n cors: config.security?.cors,\n // #425 — a selector, not a transformer, so it rides as a literal like the values above.\n serialization: config.serialization,\n // #425 — the ONE concern that is not a literal. A closure cannot be baked, so a plugin\n // declared by module specifier is imported by the emitted module instead; a constructed one\n // is refused by name at build time rather than dropped in silence.\n runtimeConfigModule: pluginsPlan?.moduleSpecifier,\n // #95 — the configured directories, which `build.ts:210` already threads into the Vite\n // plugins. Without these two lines the literals above fall to their defaults and a project\n // with a custom dir deploys an entry that resolves a directory it does not have.\n serverDir: config.serverDir,\n agentsDir: config.agentsDir,\n })\n const write =\n deps.writeEntry ??\n ((p, c) => {\n writeFileSync(p, c)\n })\n if (pluginsPlan !== undefined) {\n // Beside the entry, so the emitted import is a sibling. Written through the same seam as\n // the entry so a test that captures one captures both (#425).\n write(resolve(outputDir, 'theo.plugins.mjs'), pluginsPlan.source)\n }\n write(resolve(cwd, DENO_DEPLOY_ENTRY_PATH), entry)\n\n copyClientBesideEntry(cwd, deps)\n\n // The import map and `sloppy-imports`, at the upload root. Measured on the platform: a\n // config in a subdirectory failed the revision, so this path is not a preference.\n const readSources = deps.readProjectSources ?? readProjectSourcesFromDisk\n const readConfig = deps.readDenoConfig ?? readDenoConfigFromDisk\n const denoConfig = renderDenoConfig({\n specifiers: bareSpecifiersOf(readSources(cwd, config)),\n existing: readConfig(cwd),\n })\n write(resolve(cwd, DENO_DEPLOY_CONFIG_PATH), `${JSON.stringify(denoConfig, undefined, 2)}\\n`)\n\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n `\\n ✓ Deno Deploy output → ${DENO_DEPLOY_ENTRY_PATH} + ${DENO_DEPLOY_CONFIG_PATH} (${\n Object.keys(denoConfig.imports ?? {}).length\n } specifiers mapped)`,\n )\n // eslint-disable-next-line no-console -- CLI build progress\n console.log(\n `${describeDeployedSecurityHeaders({\n target: TARGET,\n securityHeaders: config.security?.headers,\n mintsNonce: false,\n documentHeaders: 'platform-unmanaged',\n })}\\n`,\n )\n}\n\n/**\n * Where the entry is written, relative to the project root.\n *\n * NOT under `.theokit/`, and that is the whole point: measured against the real platform on\n * 2026-09-30, a dot directory is never uploaded. `./.theokit/deno/probe.ts` failed the\n * revision while the byte-identical file at `./visible/probe.ts` served HTTP 200. An entry\n * the upload cannot see is an entry that cannot run, and the build reported success either\n * way — so nothing short of a deploy could find this.\n *\n * One directory deep, because the upload root has to stay the PROJECT root: the entry scans\n * `<cwd>/src/server` for the project's own route modules, so a deploy rooted anywhere else\n * finds no routes and answers 404 on every path.\n */\nexport const DENO_DEPLOY_ENTRY_PATH = 'theokit-deploy/server.ts'\n\n/** Copy `.theokit/client` to where the entry serves it from. Split out of `buildDeno` for its complexity ceiling. */\nfunction copyClientBesideEntry(cwd: string, deps: DenoBuildDeps): void {\n const copy = deps.copyClient ?? copyClientOnDisk\n copy(resolve(cwd, '.theokit', 'client'), resolve(cwd, DENO_DEPLOY_CLIENT_DIR))\n}\n\n/** Replace the served copy with the current build. No build output means an API-only project. */\nfunction copyClientOnDisk(from: string, to: string): void {\n if (!existsSync(from)) return\n rmSync(to, { recursive: true, force: true })\n cpSync(from, to, { recursive: true })\n}\n\n/** Where the Deno config is written. The upload root, and nowhere else — see below. */\nexport const DENO_DEPLOY_CONFIG_PATH = 'deno.json'\n\n/**\n * The bare specifiers a set of source texts import — the ones needing a registry, in the\n * order they will be emitted.\n *\n * Derived from the project's own sources rather than from `package.json`, because what has\n * to resolve is what is actually imported: a declared dependency nobody imports needs no\n * entry, and a subpath like `theokit/server/define` appears in no manifest at all.\n */\nexport function bareSpecifiersOf(sources: Iterable<string>): string[] {\n const found = new Set<string>()\n for (const source of sources) {\n for (const match of source.matchAll(/(?:from|import)\\s+['\"]([^'\"]+)['\"]/gu)) {\n // `match[1]` and not a guarded read: the capture group is not optional in this pattern,\n // so it participates in every match the loop yields.\n const specifier = match[1]\n // A relative or absolute path resolves on its own; a specifier that already names its\n // registry is not ours to map.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- a regex group's type is wider than this pattern\n if (specifier === undefined) continue\n if (/^(?:[./]|npm:|jsr:|node:|data:|https?:)/u.test(specifier)) continue\n found.add(specifier)\n }\n }\n // An explicit comparator: the emitted map is read in diffs, so its order has to be the\n // same on every machine rather than whatever the default collation is.\n return [...found].sort((a, b) => a.localeCompare(b, 'en'))\n}\n\n/** The shape this adapter reads and writes. Anything else in the file is carried through. */\nexport interface DenoConfig {\n imports?: Record<string, string>\n unstable?: string[]\n [key: string]: unknown\n}\n\n/**\n * The Deno config the platform needs, merged onto whatever the project already wrote.\n *\n * Two measured facts decide its whole content:\n *\n * **Each specifier is mapped exactly, never as a prefix.** The emitted entry imports\n * `npm:theokit/...`; the project's own modules import the bare `theokit/server/define`. A\n * trailing-slash prefix cannot bridge them — Deno refuses `\"theokit/\": \"npm:theokit/\"` with\n * *\"its after-prefix portion could not be URL-parsed relative to the URL prefix\"*, because\n * the part after the prefix is not joinable onto an `npm:` URL. Exact entries resolve; the\n * prefix form fails at the first import, at runtime, with a 500.\n *\n * **`sloppy-imports` is declared.** A generated project's own agent modules import siblings\n * with a `.js` extension against files on disk ending `.ts` — six of them in the default\n * template. Deno refuses that pairing and says so: *\"Maybe change the extension to '.ts' or\n * run with --sloppy-imports\"*.\n */\nexport function renderDenoConfig(opts: {\n specifiers: readonly string[]\n existing?: DenoConfig\n}): DenoConfig {\n const config: DenoConfig = { ...(opts.existing ?? {}) }\n const imports: Record<string, string> = { ...(config.imports ?? {}) }\n for (const specifier of opts.specifiers) imports[specifier] = `npm:${specifier}`\n config.imports = imports\n const unstable = [...(config.unstable ?? [])]\n if (!unstable.includes('sloppy-imports')) unstable.push('sloppy-imports')\n config.unstable = unstable\n return config\n}\n\n/**\n * The project's own TypeScript sources, from the directories the entry scans at runtime.\n *\n * Scoped to those directories plus the config file rather than the whole tree: what the map\n * must resolve is what the DEPLOYED code imports, and a specifier reachable only from a test\n * or a build script never runs on the platform.\n */\nfunction readProjectSourcesFromDisk(cwd: string, config: TheoConfig): string[] {\n // No fallback: `config/schema.ts` gives both a zod `.default()`, so a parsed config always\n // carries them — and the defaults are `server`/`agents`, so the `src/`-prefixed guesses this\n // line used to hold would have read the wrong directories on any project that took them.\n const roots = [config.serverDir, config.agentsDir]\n const texts: string[] = []\n for (const root of roots) {\n const dir = resolve(cwd, root)\n if (!existsSync(dir)) continue\n for (const entry of readdirSync(dir, { recursive: true, withFileTypes: true })) {\n if (!entry.isFile()) continue\n if (!/\\.(?:ts|tsx|mts)$/u.test(entry.name)) continue\n if (/\\.(?:test|spec)\\./u.test(entry.name)) continue\n texts.push(readFileSync(resolve(entry.parentPath, entry.name), 'utf8'))\n }\n }\n const themeConfig = resolve(cwd, 'theo.config.ts')\n if (existsSync(themeConfig)) texts.push(readFileSync(themeConfig, 'utf8'))\n return texts\n}\n\nfunction readDenoConfigFromDisk(cwd: string): DenoConfig | undefined {\n const file = resolve(cwd, DENO_DEPLOY_CONFIG_PATH)\n if (!existsSync(file)) return undefined\n try {\n return JSON.parse(readFileSync(file, 'utf8')) as DenoConfig\n } catch {\n // A config we cannot parse is one we must not overwrite blind: returning undefined would\n // replace whatever the project wrote. Refusing here surfaces it at the write site instead.\n throw new Error(\n `${DENO_DEPLOY_CONFIG_PATH} exists and is not valid JSON. Fix or remove it before building for deno-deploy.`,\n )\n }\n}\n\nexport const denoDeployAdapter: DeployAdapter = {\n // Same as `bun`, plus the runtime compiles the TypeScript it finds — which is why a scan that\n // answered `SyntaxError` on netlify is legitimate here. The last runtime scanner in the fleet.\n readsSourceAtRunTime: true,\n specifiersResolvedBy: 'the-project-at-run-time',\n name: 'deno-deploy',\n streamsResponses: true,\n // #409 / #410 — the generated entry calls `executeRoute` with routes, loader\n // and serverDir only. CSRF, route policy, file middleware and Zod validation\n // still run because they live inside `executeRoute`; none of the remaining\n // configurable concerns reach it. Declared explicitly rather than omitted so\n // the gap is a statement in the source and not an absence.\n //\n // `securityHeaders` IS applied -- to every response this isolate returns,\n // the document included, since the entry serves the client build itself\n // (usetheokit/theokit#951).\n servesAgents: true,\n appliesConfig: ['securityHeaders', 'csrf', 'disallowed', 'cors', 'serialization', 'plugins'],\n // B-257 — per-invocation: an in-process counter does not survive, so a declared limit needs a durable store.\n enforcesRateLimit: 'with-a-store',\n build(config, cwd, ctx) {\n return buildDeno(config, cwd, {}, ctx)\n },\n}\n"],"mappings":"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","names":[]}
@@ -11,12 +11,12 @@ import {
11
11
  } from "./chunk-KTMQUYED.js";
12
12
  import {
13
13
  theoPluginAsync
14
- } from "./chunk-QX5Q3FBJ.js";
15
- import "./chunk-QAS5SKZP.js";
14
+ } from "./chunk-7FIK5NKN.js";
15
+ import "./chunk-VD3BCME6.js";
16
16
  import "./chunk-BDH5GKFZ.js";
17
17
  import "./chunk-3PWQQWT6.js";
18
18
  import "./chunk-FDOOBTXJ.js";
19
- import "./chunk-SHIXTMQE.js";
19
+ import "./chunk-DA3GGICE.js";
20
20
  import "./chunk-BINRCILF.js";
21
21
  import "./chunk-FNJVSFEC.js";
22
22
  import "./chunk-WSMLSIS5.js";
@@ -30,15 +30,15 @@ import "./chunk-4WP7A7RX.js";
30
30
  import "./chunk-Z7CJHDLS.js";
31
31
  import {
32
32
  loadConfig
33
- } from "./chunk-SZWGXHY3.js";
33
+ } from "./chunk-VOXLI66L.js";
34
34
  import {
35
35
  loadEnv
36
36
  } from "./chunk-3QPVEJ7D.js";
37
37
  import "./chunk-U5PWXS3J.js";
38
- import "./chunk-X2LFNDJQ.js";
38
+ import "./chunk-NX3P5MLH.js";
39
39
  import {
40
40
  orchestrateDev
41
- } from "./chunk-ZNZISJDZ.js";
41
+ } from "./chunk-L4V5V2TI.js";
42
42
  import "./chunk-D2R5SJEL.js";
43
43
  import "./chunk-5ODOE6EF.js";
44
44
  import "./chunk-VQIUTCK2.js";
@@ -128,4 +128,4 @@ export {
128
128
  devCommand,
129
129
  startDevServer
130
130
  };
131
- //# sourceMappingURL=dev-FZ4OTCPT.js.map
131
+ //# sourceMappingURL=dev-LMZGOV4M.js.map
package/dist/index.js CHANGED
@@ -8,12 +8,12 @@ import {
8
8
  scanRoutes,
9
9
  theoConfigSchema,
10
10
  theoPlugin
11
- } from "./chunk-NQZIQJFH.js";
11
+ } from "./chunk-SHFZKJCU.js";
12
12
  import "./chunk-7NRFZBAL.js";
13
13
  import "./chunk-SSYDEKWF.js";
14
14
  import "./chunk-D7STTNQN.js";
15
- import "./chunk-HK57MPSZ.js";
16
- import "./chunk-2NG5T77O.js";
15
+ import "./chunk-VPK7ABMB.js";
16
+ import "./chunk-R4AMIBZL.js";
17
17
  import "./chunk-OIGJOYX6.js";
18
18
  import "./chunk-U3SPBLJF.js";
19
19
  import "./chunk-WSOAN4KN.js";
@@ -5,11 +5,11 @@ import {
5
5
  } from "./chunk-47MM2JUK.js";
6
6
  import {
7
7
  loadConfig
8
- } from "./chunk-SZWGXHY3.js";
8
+ } from "./chunk-VOXLI66L.js";
9
9
  import "./chunk-3QPVEJ7D.js";
10
10
  import "./chunk-U5PWXS3J.js";
11
- import "./chunk-X2LFNDJQ.js";
12
- import "./chunk-ZNZISJDZ.js";
11
+ import "./chunk-NX3P5MLH.js";
12
+ import "./chunk-L4V5V2TI.js";
13
13
  import "./chunk-D2R5SJEL.js";
14
14
 
15
15
  // src/cli/commands/info.ts
@@ -103,4 +103,4 @@ export {
103
103
  buildInfo,
104
104
  infoCommand
105
105
  };
106
- //# sourceMappingURL=info-SUX7QVA3.js.map
106
+ //# sourceMappingURL=info-XURFOBRF.js.map
@@ -16,7 +16,7 @@ import {
16
16
  handleCspReport,
17
17
  handleCsrfReadiness,
18
18
  mountAgent
19
- } from "./chunk-SHIXTMQE.js";
19
+ } from "./chunk-DA3GGICE.js";
20
20
  import {
21
21
  resolveProvider
22
22
  } from "./chunk-BINRCILF.js";
@@ -87,4 +87,4 @@ export {
87
87
  scanWebSocketRoutes,
88
88
  sendError
89
89
  };
90
- //# sourceMappingURL=internal-api-GKPJYL3G.js.map
90
+ //# sourceMappingURL=internal-api-ATSPOXI2.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-D7STTNQN.js";
4
4
  import {
5
5
  mountAgent
6
- } from "./chunk-2NG5T77O.js";
6
+ } from "./chunk-R4AMIBZL.js";
7
7
  import {
8
8
  CSP_REPORT_PATH,
9
9
  CsrfReadinessStore,
@@ -108,4 +108,4 @@ export {
108
108
  scanWebSocketRoutes,
109
109
  sendError
110
110
  };
111
- //# sourceMappingURL=internal-api-ZUBUYGCQ.js.map
111
+ //# sourceMappingURL=internal-api-XPT4GLYX.js.map
@@ -3,14 +3,14 @@ import "tsx/esm";
3
3
  import {
4
4
  deepMerge,
5
5
  loadConfig
6
- } from "./chunk-SZWGXHY3.js";
6
+ } from "./chunk-VOXLI66L.js";
7
7
  import "./chunk-3QPVEJ7D.js";
8
8
  import "./chunk-U5PWXS3J.js";
9
- import "./chunk-X2LFNDJQ.js";
10
- import "./chunk-ZNZISJDZ.js";
9
+ import "./chunk-NX3P5MLH.js";
10
+ import "./chunk-L4V5V2TI.js";
11
11
  import "./chunk-D2R5SJEL.js";
12
12
  export {
13
13
  deepMerge,
14
14
  loadConfig
15
15
  };
16
- //# sourceMappingURL=load-config-52H3EKAA.js.map
16
+ //# sourceMappingURL=load-config-2DXLI7NW.js.map
@@ -2,7 +2,7 @@
2
2
  import "tsx/esm";
3
3
  import {
4
4
  createAgentSsrLoader
5
- } from "./chunk-SG62LZOJ.js";
5
+ } from "./chunk-25OXNMVO.js";
6
6
  import {
7
7
  extractAppResources,
8
8
  handleMcpJsonRpc
@@ -46,7 +46,7 @@ async function serveMcpStdio(mod, name, appResources, streams) {
46
46
  // src/cli/commands/mcp.ts
47
47
  async function mcpCommand(name, deps = {}) {
48
48
  const projectRoot = deps.projectRoot ?? process.cwd();
49
- const agentsDir = deps.agentsDir ?? (await (await import("./load-config-52H3EKAA.js")).loadConfig(projectRoot)).agentsDir;
49
+ const agentsDir = deps.agentsDir ?? (await (await import("./load-config-2DXLI7NW.js")).loadConfig(projectRoot)).agentsDir;
50
50
  const agent = scanAgents(projectRoot, agentsDir).find((a) => a.name === name);
51
51
  if (!agent) {
52
52
  const names = scanAgents(projectRoot, agentsDir).map((a) => a.name).join(", ");
@@ -75,4 +75,4 @@ async function mcpCommand(name, deps = {}) {
75
75
  export {
76
76
  mcpCommand
77
77
  };
78
- //# sourceMappingURL=mcp-VUHRRX36.js.map
78
+ //# sourceMappingURL=mcp-4CXNOZA4.js.map
@@ -22,14 +22,14 @@ import {
22
22
  } from "./chunk-EQJF2EH5.js";
23
23
  import {
24
24
  nodeAdapter
25
- } from "./chunk-UFBBFP75.js";
25
+ } from "./chunk-NLHAYGQQ.js";
26
26
  import {
27
27
  buildSecurityHeaders
28
28
  } from "./chunk-WSMLSIS5.js";
29
29
  import {
30
30
  assertServicesUnsupported,
31
31
  readManifest
32
- } from "./chunk-ZNZISJDZ.js";
32
+ } from "./chunk-L4V5V2TI.js";
33
33
 
34
34
  // src/adapters/netlify.ts
35
35
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -188,6 +188,23 @@ var THEO_REDIRECT_TARGET = [
188
188
  " status = 200",
189
189
  " force = true"
190
190
  ].join("\n");
191
+ var THEO_SPA_FALLBACK_FROM = "/*";
192
+ var THEO_SPA_FALLBACK_TARGET = [
193
+ "[[redirects]]",
194
+ ' from = "/*"',
195
+ ' to = "/index.html"',
196
+ " status = 200"
197
+ ].join("\n");
198
+ function withSpaFallback(source) {
199
+ const declared = parseRedirectBlocks(source.split(/\r?\n/)).some(
200
+ (b) => b.from === THEO_SPA_FALLBACK_FROM
201
+ );
202
+ if (declared) return source;
203
+ const sep = source.endsWith("\n") ? "" : "\n";
204
+ return `${source}${sep}
205
+ ${THEO_SPA_FALLBACK_TARGET}
206
+ `;
207
+ }
191
208
  var THEO_HEADERS_MARKER = "# Generated by Theo \u2014 security baseline (usetheokit/theokit#412)";
192
209
  function renderHeadersBlock(securityHeaders) {
193
210
  const values = buildSecurityHeaders(securityHeaders ?? {}, { production: true });
@@ -254,10 +271,10 @@ ${THEO_FUNCTIONS_TARGET}
254
271
  function mergeNetlifyToml(existing, securityHeaders) {
255
272
  const headersBlock = renderHeadersBlock(securityHeaders);
256
273
  if (existing === null || existing.trim().length === 0) {
257
- return withFunctionsDeclared(`${THEO_REDIRECT_TARGET}
274
+ return withFunctionsDeclared(withSpaFallback(`${THEO_REDIRECT_TARGET}
258
275
 
259
276
  ${headersBlock}
260
- `);
277
+ `));
261
278
  }
262
279
  existing = withoutGeneratedHeaders(existing);
263
280
  const blocks = parseRedirectBlocks(existing.split(/\r?\n/));
@@ -265,18 +282,20 @@ ${headersBlock}
265
282
  if (b.from !== THEO_REDIRECT_FROM) continue;
266
283
  if (b.to === THEO_REDIRECT_TO) {
267
284
  const sep2 = existing.endsWith("\n") ? "" : "\n";
268
- return withFunctionsDeclared(`${existing}${sep2}
285
+ return withFunctionsDeclared(withSpaFallback(`${existing}${sep2}
269
286
  ${headersBlock}
270
- `);
287
+ `));
271
288
  }
272
289
  throw new NetlifyConflictError(b.from, b.to ?? "(unknown)");
273
290
  }
274
291
  const sep = existing.endsWith("\n") ? "" : "\n";
275
- return withFunctionsDeclared(`${existing}${sep}
292
+ return withFunctionsDeclared(
293
+ withSpaFallback(`${existing}${sep}
276
294
  ${THEO_REDIRECT_TARGET}
277
295
 
278
296
  ${headersBlock}
279
- `);
297
+ `)
298
+ );
280
299
  }
281
300
  function resolveNetlifyDeps(cwd, deps) {
282
301
  return {
@@ -379,4 +398,4 @@ export {
379
398
  netlifyAdapter,
380
399
  renderNetlifyFunction
381
400
  };
382
- //# sourceMappingURL=netlify-ALPCTQFO.js.map
401
+ //# sourceMappingURL=netlify-NI4HYDKN.js.map