theokit 0.55.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/{actions-virtual-module-PREAJNIS.js → actions-virtual-module-GMRXCAVN.js} +21 -21
  2. package/dist/{actions-virtual-module-TZFLCIIR.js → actions-virtual-module-XAWZOWSY.js} +4 -4
  3. package/dist/adapters/agent-mount.d.ts +1 -1
  4. package/dist/adapters/agent-mount.js +9 -9
  5. package/dist/{agent-2AELELAC.js → agent-LS7I3ZQB.js} +3 -3
  6. package/dist/{agents-typed-client-INR2E7B3.js → agents-typed-client-PVUZU65B.js} +3 -3
  7. package/dist/{app-typed-client-NEKRXPAQ.js → app-typed-client-NMKJ4DR7.js} +4 -4
  8. package/dist/{app-typed-client-KSFGMOQ6.js → app-typed-client-W74QO5LH.js} +21 -21
  9. package/dist/boot/index.js +3 -3
  10. package/dist/{build-ESSQS5F5.js → build-FHE72YXR.js} +5 -5
  11. package/dist/{bun-L7PP6ID5.js → bun-POLTY2HK.js} +118 -3
  12. package/dist/bun-POLTY2HK.js.map +1 -0
  13. package/dist/{chunk-5YPXXSYY.js → chunk-35QIWOHT.js} +9 -8
  14. package/dist/chunk-35QIWOHT.js.map +1 -0
  15. package/dist/{chunk-JDN5SAAD.js → chunk-44E7XIZC.js} +6 -6
  16. package/dist/{chunk-OQCRCXCP.js → chunk-4ERYQBL6.js} +2 -2
  17. package/dist/{chunk-52PDWL5S.js → chunk-5KRMDP46.js} +2 -2
  18. package/dist/{chunk-VQPHPQ5L.js → chunk-7NRFZBAL.js} +3 -3
  19. package/dist/{chunk-3PBAN2YE.js → chunk-CF2DNN5R.js} +9 -8
  20. package/dist/{chunk-3PBAN2YE.js.map → chunk-CF2DNN5R.js.map} +1 -1
  21. package/dist/chunk-CLBJTDI6.js +189 -0
  22. package/dist/chunk-CLBJTDI6.js.map +1 -0
  23. package/dist/{chunk-3DRALT3J.js → chunk-CMCEGW3B.js} +6 -6
  24. package/dist/{chunk-3DRALT3J.js.map → chunk-CMCEGW3B.js.map} +1 -1
  25. package/dist/{chunk-MWWFXYW3.js → chunk-CTRVTBGY.js} +3 -3
  26. package/dist/chunk-CTRVTBGY.js.map +1 -0
  27. package/dist/{chunk-HK4AZBKX.js → chunk-D5KBBTVD.js} +7 -7
  28. package/dist/{chunk-DADMEW4G.js → chunk-G5JYB2LO.js} +3 -3
  29. package/dist/{chunk-BU6RJPPJ.js → chunk-GO7UWJ47.js} +2 -2
  30. package/dist/{chunk-BU6RJPPJ.js.map → chunk-GO7UWJ47.js.map} +1 -1
  31. package/dist/{chunk-VZTYCAO6.js → chunk-IDEZ7FU2.js} +3 -3
  32. package/dist/{chunk-ITSNTHWN.js → chunk-JW5UFKQ3.js} +17 -17
  33. package/dist/{chunk-IRASEXOG.js → chunk-KDP2HURW.js} +6 -6
  34. package/dist/{chunk-VRELW5NF.js → chunk-KEDRCI2L.js} +9 -9
  35. package/dist/{chunk-GWE4MUKQ.js → chunk-KQ27YDLG.js} +8 -8
  36. package/dist/{chunk-WX6NHGKS.js → chunk-M357ILK5.js} +10 -10
  37. package/dist/{chunk-2PSKQWGM.js → chunk-NC2OPE22.js} +3 -3
  38. package/dist/{chunk-QZUIF64G.js → chunk-OKDCBENR.js} +4 -4
  39. package/dist/{chunk-ZQO37YQJ.js → chunk-PELXWPDF.js} +5 -5
  40. package/dist/{chunk-2OEN3YTE.js → chunk-PVNMLZ5G.js} +2 -2
  41. package/dist/{chunk-F6P6S5YS.js → chunk-Q6HS3NFG.js} +63 -63
  42. package/dist/{chunk-5K4TDLRG.js → chunk-SOYOYGWY.js} +3 -3
  43. package/dist/{chunk-4X6H2BXP.js → chunk-U3SPBLJF.js} +3 -3
  44. package/dist/{chunk-BX6EUYHU.js → chunk-V6Y2SMRG.js} +3 -3
  45. package/dist/{chunk-I2UN46ZX.js → chunk-XJOU425Y.js} +17 -17
  46. package/dist/{chunk-BND4YZTQ.js → chunk-ZDCCILMP.js} +2 -2
  47. package/dist/cli/index.js +6 -6
  48. package/dist/client/index.js +8 -5
  49. package/dist/client/index.js.map +1 -1
  50. package/dist/{controller-swc-transform-T7ZTD55V.js → controller-swc-transform-7EZKG2IS.js} +2 -2
  51. package/dist/cost-types-BALTuBpG.d.ts +79 -0
  52. package/dist/{dev-MWGPBGQP.js → dev-WDKROZBS.js} +6 -6
  53. package/dist/{dev-emit-W27J3DLU.js → dev-emit-FIQASO3D.js} +7 -7
  54. package/dist/index.js +23 -23
  55. package/dist/{internal-api-XSUP3NDI.js → internal-api-732K3H75.js} +4 -4
  56. package/dist/{internal-api-J3BYA3VU.js → internal-api-EVJSGGO3.js} +26 -26
  57. package/dist/{mcp-6IPY275L.js → mcp-PBUCHHAS.js} +3 -3
  58. package/dist/{preview-KPAKWBRW.js → preview-VFIFKVP4.js} +3 -3
  59. package/dist/{provider-resolver-Dx6QuWrw.d.ts → provider-resolver-VS61zqsw.d.ts} +0 -11
  60. package/dist/{registry-KBZDCFVQ.js → registry-3LUHO4IB.js} +2 -2
  61. package/dist/server/agent/index.d.ts +1 -1
  62. package/dist/server/agent/index.js +3 -3
  63. package/dist/server/cost/index.d.ts +3 -80
  64. package/dist/server/cost/sqlite/index.d.ts +21 -0
  65. package/dist/server/cost/sqlite/index.js +102 -0
  66. package/dist/server/cost/sqlite/index.js.map +1 -0
  67. package/dist/server/cron/index.js +4 -4
  68. package/dist/server/http/index.js +7 -7
  69. package/dist/server/index.d.ts +5 -4
  70. package/dist/server/index.js +79 -79
  71. package/dist/server/jobs/index.d.ts +2 -2
  72. package/dist/server/jobs/index.js +4 -4
  73. package/dist/server/observability/index.js +2 -2
  74. package/dist/server/scan/index.js +6 -6
  75. package/dist/server/storage/index.d.ts +2 -2
  76. package/dist/server/webhook/index.js +1 -1
  77. package/dist/{server-boundary-PGPFBJBH.js → server-boundary-B6U4DRRH.js} +4 -4
  78. package/dist/{server-boundary-7ZO4FZIV.js → server-boundary-QRVK3OIJ.js} +23 -23
  79. package/dist/{server-routes-hmr-BWN3WIGR.js → server-routes-hmr-TNI3FNGM.js} +2 -2
  80. package/dist/{services-typed-client-IXNOPVZA.js → services-typed-client-ACTJHAEP.js} +2 -2
  81. package/dist/{start-F3T2ZPPI.js → start-UAMJ5TSC.js} +6 -6
  82. package/dist/{storage-manager-C6hmsaer.d.ts → storage-manager-BtG38jYl.d.ts} +1 -1
  83. package/dist/{storage-types-DsDTCPbp.d.ts → storage-types-CFGPFIdB.d.ts} +1 -1
  84. package/dist/vite-plugin/index.js +21 -21
  85. package/dist/{vite-plugin-5O3D472R.js → vite-plugin-ABJIDKE6.js} +6 -6
  86. package/package.json +7 -3
  87. package/dist/bun-L7PP6ID5.js.map +0 -1
  88. package/dist/chunk-5YPXXSYY.js.map +0 -1
  89. package/dist/chunk-MWWFXYW3.js.map +0 -1
  90. package/dist/chunk-VSLY4KB3.js +0 -46
  91. package/dist/chunk-VSLY4KB3.js.map +0 -1
  92. /package/dist/{actions-virtual-module-PREAJNIS.js.map → actions-virtual-module-GMRXCAVN.js.map} +0 -0
  93. /package/dist/{actions-virtual-module-TZFLCIIR.js.map → actions-virtual-module-XAWZOWSY.js.map} +0 -0
  94. /package/dist/{agent-2AELELAC.js.map → agent-LS7I3ZQB.js.map} +0 -0
  95. /package/dist/{agents-typed-client-INR2E7B3.js.map → agents-typed-client-PVUZU65B.js.map} +0 -0
  96. /package/dist/{app-typed-client-NEKRXPAQ.js.map → app-typed-client-NMKJ4DR7.js.map} +0 -0
  97. /package/dist/{app-typed-client-KSFGMOQ6.js.map → app-typed-client-W74QO5LH.js.map} +0 -0
  98. /package/dist/{build-ESSQS5F5.js.map → build-FHE72YXR.js.map} +0 -0
  99. /package/dist/{chunk-JDN5SAAD.js.map → chunk-44E7XIZC.js.map} +0 -0
  100. /package/dist/{chunk-OQCRCXCP.js.map → chunk-4ERYQBL6.js.map} +0 -0
  101. /package/dist/{chunk-52PDWL5S.js.map → chunk-5KRMDP46.js.map} +0 -0
  102. /package/dist/{chunk-VQPHPQ5L.js.map → chunk-7NRFZBAL.js.map} +0 -0
  103. /package/dist/{chunk-HK4AZBKX.js.map → chunk-D5KBBTVD.js.map} +0 -0
  104. /package/dist/{chunk-DADMEW4G.js.map → chunk-G5JYB2LO.js.map} +0 -0
  105. /package/dist/{chunk-VZTYCAO6.js.map → chunk-IDEZ7FU2.js.map} +0 -0
  106. /package/dist/{chunk-ITSNTHWN.js.map → chunk-JW5UFKQ3.js.map} +0 -0
  107. /package/dist/{chunk-IRASEXOG.js.map → chunk-KDP2HURW.js.map} +0 -0
  108. /package/dist/{chunk-VRELW5NF.js.map → chunk-KEDRCI2L.js.map} +0 -0
  109. /package/dist/{chunk-GWE4MUKQ.js.map → chunk-KQ27YDLG.js.map} +0 -0
  110. /package/dist/{chunk-WX6NHGKS.js.map → chunk-M357ILK5.js.map} +0 -0
  111. /package/dist/{chunk-2PSKQWGM.js.map → chunk-NC2OPE22.js.map} +0 -0
  112. /package/dist/{chunk-QZUIF64G.js.map → chunk-OKDCBENR.js.map} +0 -0
  113. /package/dist/{chunk-ZQO37YQJ.js.map → chunk-PELXWPDF.js.map} +0 -0
  114. /package/dist/{chunk-2OEN3YTE.js.map → chunk-PVNMLZ5G.js.map} +0 -0
  115. /package/dist/{chunk-F6P6S5YS.js.map → chunk-Q6HS3NFG.js.map} +0 -0
  116. /package/dist/{chunk-5K4TDLRG.js.map → chunk-SOYOYGWY.js.map} +0 -0
  117. /package/dist/{chunk-4X6H2BXP.js.map → chunk-U3SPBLJF.js.map} +0 -0
  118. /package/dist/{chunk-BX6EUYHU.js.map → chunk-V6Y2SMRG.js.map} +0 -0
  119. /package/dist/{chunk-I2UN46ZX.js.map → chunk-XJOU425Y.js.map} +0 -0
  120. /package/dist/{chunk-BND4YZTQ.js.map → chunk-ZDCCILMP.js.map} +0 -0
  121. /package/dist/{controller-swc-transform-T7ZTD55V.js.map → controller-swc-transform-7EZKG2IS.js.map} +0 -0
  122. /package/dist/{dev-MWGPBGQP.js.map → dev-WDKROZBS.js.map} +0 -0
  123. /package/dist/{dev-emit-W27J3DLU.js.map → dev-emit-FIQASO3D.js.map} +0 -0
  124. /package/dist/{internal-api-XSUP3NDI.js.map → internal-api-732K3H75.js.map} +0 -0
  125. /package/dist/{internal-api-J3BYA3VU.js.map → internal-api-EVJSGGO3.js.map} +0 -0
  126. /package/dist/{mcp-6IPY275L.js.map → mcp-PBUCHHAS.js.map} +0 -0
  127. /package/dist/{preview-KPAKWBRW.js.map → preview-VFIFKVP4.js.map} +0 -0
  128. /package/dist/{registry-KBZDCFVQ.js.map → registry-3LUHO4IB.js.map} +0 -0
  129. /package/dist/{server-boundary-PGPFBJBH.js.map → server-boundary-B6U4DRRH.js.map} +0 -0
  130. /package/dist/{server-boundary-7ZO4FZIV.js.map → server-boundary-QRVK3OIJ.js.map} +0 -0
  131. /package/dist/{server-routes-hmr-BWN3WIGR.js.map → server-routes-hmr-TNI3FNGM.js.map} +0 -0
  132. /package/dist/{services-typed-client-IXNOPVZA.js.map → services-typed-client-ACTJHAEP.js.map} +0 -0
  133. /package/dist/{start-F3T2ZPPI.js.map → start-UAMJ5TSC.js.map} +0 -0
  134. /package/dist/{vite-plugin-5O3D472R.js.map → vite-plugin-ABJIDKE6.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/_internal/process-singleton.ts","../src/server/agent/provider-resolver.ts","../src/server/agent/approval-registry.ts"],"sourcesContent":["/**\n * One object per PROCESS, whatever the bundler did to the module holding it.\n *\n * ## The defect this exists to remove\n *\n * A module-level `let x` gives one instance per **module instance**, and a bundler is free to place\n * the same source in more than one chunk. When it does, each chunk gets its own `x` and any state\n * written through one is invisible through the other.\n *\n * Measured in this repository's own `dist` (usetheokit/theokit#401) by grepping the built chunks for\n * each module's marker: `provider-resolver` lands in two chunks, and `approval-registry` in two.\n * Neither copy is redundant — one of the provider chunks is tree-shaken to `resolveProvider` and\n * does not carry `resetProviderRegistry` at all, which is precisely how the halves diverge: an\n * application calling `registerProvider` — public API, with a documented self-hosting example —\n * mutated the array in one chunk while `theokit start` resolved against the array in the other.\n *\n * The second is worse in kind, because `approval-registry.ts` states the requirement in its own\n * source: *\"the approval a request awaits and the approval the route resolves MUST be the same\n * object, so a single instance per process is not a convenience but a correctness requirement.\"* A\n * chunk boundary can break that silently, and the symptom would be a HITL pause that never resumes.\n *\n * ## Why `globalThis` and not better chunking\n *\n * Chunking is a bundler heuristic that changes between versions and with the shape of the import\n * graph. A fix that depends on tsup continuing to place this module in exactly one chunk is a fix\n * that holds until someone adds an import. `Symbol.for` gives a key that is equal across every\n * module instance in the realm, so identity stops depending on the build at all.\n *\n * @internal\n */\n\n/** Namespaced so a key cannot collide with an unrelated global. */\nconst NAMESPACE = 'theokit.singleton.'\n\n/**\n * The object registered under `key`, creating it with `factory` the first time.\n *\n * `factory` runs at most once per process. A later call with the same key returns what the first\n * call built and never replaces it — replacing it is the same defect wearing a different hat, since\n * two callers would again hold different objects.\n */\nexport function processSingleton<T extends object>(key: string, factory: () => T): T {\n const slot = Symbol.for(NAMESPACE + key)\n const host = globalThis as unknown as Record<symbol, T | undefined>\n\n const existing = host[slot]\n if (existing !== undefined) return existing\n\n const created = factory()\n host[slot] = created\n return created\n}\n","import { processSingleton } from '../_internal/process-singleton.js'\n\n/**\n * Provider Resolver — Strategy + Registry pattern (FAANG-grade).\n *\n * Inspiration: Dapr Conversation Registry (`dapr/pkg/components/conversation/registry.go`)\n * + Encore Manager provider array (`encore/runtimes/go/pubsub/manager_internal.go`).\n *\n * Principle: provider routing is the FRAMEWORK's responsibility, not the consumer's — but the\n * consumer still gets to SAY which provider it wants, and it says so in the model id.\n *\n * `provider/model` is the convention every agent in this ecosystem already writes\n * (`anthropic/claude-sonnet-4-6`, `openrouter/anthropic/claude-haiku-4.5`), and it is the one the\n * SDK routes on. Until theokit#326 this resolver ignored it entirely and picked by env-var\n * priority, so an agent that declared `anthropic/...` was handed an OpenRouter key whenever one\n * happened to be present — and every turn failed with `auth_failed (HTTP 401)` against a provider\n * nobody asked for. The declared provider now wins; priority is the fallback for a bare model id.\n *\n * Wire protocol: OpenAI Chat Completions (universal — implemented by every\n * os providers: OpenRouter, Groq, Mistral, Together, Anthropic via proxy, etc).\n *\n * Resolution by priority (FIRST match wins):\n * 1. OPENROUTER_API_KEY → baseUrl=openrouter.ai (multi-model gateway)\n * 2. OPENAI_API_KEY → baseUrl=api.openai.com\n * 3. ANTHROPIC_API_KEY → direct Anthropic (Messages API, not OpenAI-compat)\n *\n * Escape hatch: an explicit `options.apiKey` OVERRIDES auto-resolution\n * (the consumer can force a specific provider if it wants to).\n */\n\n/**\n * Provider configuration descriptor — Registry entry shape.\n *\n * @public\n */\nexport interface ProviderDescriptor {\n /** Stable name used internally — not exposed on the wire. */\n name: string\n /**\n * Environment variable that holds the API key for this provider.\n *\n * **Absent means the provider needs no credential** — a model running on the developer's own\n * machine has nothing to authenticate against. Such a provider is reachable ONLY when a model id\n * names it (`ollama/llama3.2`); it never participates in the priority walk, because the walk\n * reads \"this variable is set\" as \"a human configured this provider\", and a keyless entry offers\n * no equivalent signal. Including it would route every bare model id to localhost the moment no\n * cloud key was set (usetheokit/theokit#407).\n */\n envKey?: string\n /** Base URL for the provider's OpenAI-compatible (or native) API. */\n baseUrl: string\n /**\n * Environment variable that OVERRIDES {@link baseUrl} when set.\n *\n * Scoped honestly: every caller inside this framework takes `.apiKey` from the resolved provider\n * and discards `.baseUrl` (`cli/commands/start/handlers.ts`, `vite-plugin/api-middleware.ts`,\n * `cli/commands/agent.ts`), so the endpoint a request actually reaches is chosen by the SDK, from\n * its own profile — which reads `OLLAMA_HOST` itself. This field therefore does not redirect\n * traffic today.\n *\n * It exists so that the `baseUrl` this function REPORTS agrees with where the request goes, for\n * the consumers that read it — `resolveProvider` is public API, and a reported endpoint that\n * contradicts the real one is a debugging trap rather than a harmless inaccuracy.\n */\n baseUrlEnv?: string\n /** Resolution priority (lower = higher priority). FIRST match wins. */\n priority: number\n}\n\n/**\n * Resolved provider configuration — output of `resolveProvider()`.\n *\n * @public\n */\nexport interface ResolvedProvider {\n name: string\n apiKey: string\n baseUrl: string\n}\n\n/**\n * Default provider registry. Order = priority (first = highest).\n *\n * Adding a new provider:\n * 1. Append entry below (or register via `registerProvider()`).\n * 2. Set `envKey` matching the user's env var convention.\n * 3. Set `baseUrl` to the OpenAI-compat endpoint (or native if not compat).\n * 4. Provider name used in telemetry/logs only — never wire-exposed.\n */\nconst DEFAULT_REGISTRY: ProviderDescriptor[] = [\n {\n name: 'openrouter',\n envKey: 'OPENROUTER_API_KEY',\n baseUrl: 'https://openrouter.ai/api/v1',\n priority: 1,\n },\n {\n name: 'openai',\n envKey: 'OPENAI_API_KEY',\n baseUrl: 'https://api.openai.com/v1',\n priority: 2,\n },\n {\n name: 'anthropic',\n envKey: 'ANTHROPIC_API_KEY',\n baseUrl: 'https://api.anthropic.com',\n priority: 3,\n },\n {\n // Mirrors the profile `@theokit/sdk` already ships for this provider — same default host, same\n // override variable — so the two cannot disagree about where ollama is listening. The SDK is\n // what dials it (see `baseUrlEnv`); this entry's job is to stop the resolver from rejecting the\n // model before the SDK is ever reached. No `envKey`: see that field's doc comment for why the\n // absence also keeps this entry out of the priority walk.\n name: 'ollama',\n baseUrl: 'http://localhost:11434',\n baseUrlEnv: 'OLLAMA_HOST',\n priority: 100,\n },\n]\n\n/**\n * Runtime registry — copy of DEFAULT_REGISTRY mutable via registerProvider().\n * Sorted by priority on every resolve (stable, O(n log n) — n <= ~10 providers).\n *\n * Held per PROCESS rather than per module instance (usetheokit/theokit#401). As a module-level\n * `const` it was one array per chunk, and the bundler emits this module into two of them: an\n * application calling `registerProvider` mutated one array while `theokit start` resolved against\n * another, so the call had no effect and the resulting error listed the defaults while the\n * registered provider sat in an object nobody read.\n */\nfunction registryOf(): ProviderDescriptor[] {\n return processSingleton('provider-registry', () => [...DEFAULT_REGISTRY])\n}\n\n/**\n * Providers already announced, so a boot line does not become a per-request line.\n *\n * theokit#326 — resolution was silent on success. `resolveProvider` returns `{ name, apiKey,\n * baseUrl }` and every call site consumes only the key, so an operator could only learn which\n * provider was selected from an error message: exactly when it has already failed, and never in\n * the case that hurts most — a stale key that resolves cleanly and 401s at the provider.\n */\nconst announced = new Set<string>()\n\n/** Test-only reset, so announcement state does not leak between cases. @public */\nexport function resetProviderAnnouncements(): void {\n announced.clear()\n}\n\n/** Where the announcement goes, and what it may contain. @public */\nexport interface ResolveOptions {\n /** Receives one line per provider, at most once. NEVER receives the key. */\n announce?: (line: string) => void\n}\n\n/**\n * Register a new provider (Registry pattern — runtime extension point).\n * Useful for self-hosted endpoints or custom providers without touching theokit src.\n *\n * @example\n * registerProvider({\n * name: 'self-hosted',\n * envKey: 'SELF_HOSTED_API_KEY',\n * baseUrl: 'https://llm.internal.acme.com/v1',\n * priority: 0, // highest priority\n * })\n *\n * @public\n */\nexport function registerProvider(descriptor: ProviderDescriptor): void {\n // Idempotent — replace existing by name.\n const registry = registryOf()\n const idx = registry.findIndex((p) => p.name === descriptor.name)\n if (idx >= 0) registry[idx] = descriptor\n else registry.push(descriptor)\n}\n\n/**\n * Reset registry to DEFAULT_REGISTRY (test-only / dev escape hatch).\n *\n * @public\n */\nexport function resetProviderRegistry(): void {\n const registry = registryOf()\n registry.length = 0\n registry.push(...DEFAULT_REGISTRY)\n}\n\n/**\n * Get current registry snapshot (read-only — inspection).\n *\n * @public\n */\nexport function listProviders(): readonly ProviderDescriptor[] {\n return [...registryOf()].sort((a, b) => a.priority - b.priority)\n}\n\n/**\n * Says which provider was selected, once.\n *\n * Carries the provider, HOW it was chosen, and the variable the credential came from — never the\n * credential. Which env var holds it is what an operator needs to fix a mis-selection; the value\n * is what a log must never hold.\n */\nfunction announce(\n desc: ProviderDescriptor,\n how: 'declared by the model' | 'by env priority',\n options: ResolveOptions | undefined,\n): void {\n if (announced.has(desc.name)) return\n announced.add(desc.name)\n const source = desc.envKey ?? 'none (this provider takes no credential)'\n const line = `[theokit] provider=${desc.name} (${how}) source=${source} baseUrl=${baseUrlOf(desc)}`\n if (options?.announce) options.announce(line)\n // `warn` rather than `info` because the repo's lint rule allows only `warn`/`error` — and this\n // line is closer to a warning in spirit anyway: it is the one chance an operator gets to notice\n // that the provider serving their agents is not the one they expected.\n else console.warn(line)\n}\n\n/**\n * Where this provider is listening, after any environment override.\n *\n * One function so the announcement line and the resolved config can never name different hosts —\n * an operator reading a log to find out where requests went must be reading the truth.\n */\nfunction baseUrlOf(desc: ProviderDescriptor): string {\n if (desc.baseUrlEnv === undefined) return desc.baseUrl\n const override = process.env[desc.baseUrlEnv]\n return override !== undefined && override.length > 0 ? override : desc.baseUrl\n}\n\n/**\n * The provider a model id declares, or `undefined` when it declares none.\n *\n * `provider/model` and `gateway/provider/model` both resolve on the FIRST segment: a gateway is a\n * provider from the framework's point of view (it holds the credential), and everything after it\n * is the upstream namespace the gateway itself routes on.\n *\n * A bare id (`gpt-4o-mini`, `qwen2.5:3b`) declares nothing, and an unregistered prefix is treated\n * the same way rather than as an error — a project may legitimately point a custom id at a\n * registered provider's endpoint, and refusing it here would break that before it reached the SDK.\n */\nfunction providerOf(\n modelId: string | undefined,\n registered: readonly ProviderDescriptor[],\n): ProviderDescriptor | undefined {\n const prefix = declaredPrefixOf(modelId)\n if (prefix === undefined) return undefined\n return registered.find((p) => p.name === prefix)\n}\n\n/**\n * The provider name a model id declares, registered or not.\n *\n * Split out of {@link providerOf} because the two questions differ exactly where the error message\n * used to go wrong: \"which registered provider is this?\" answers `undefined` both for a bare id and\n * for `groq/…`, and the resolver needs to tell those apart to say something true about the second.\n */\nfunction declaredPrefixOf(modelId: string | undefined): string | undefined {\n if (modelId === undefined) return undefined\n const slash = modelId.indexOf('/')\n if (slash <= 0) return undefined\n return modelId.slice(0, slash)\n}\n\n/** A provider that authenticates — the only kind the priority walk may select. */\ntype CredentialedProvider = ProviderDescriptor & { envKey: string }\n\nfunction takesCredential(desc: ProviderDescriptor): desc is CredentialedProvider {\n return desc.envKey !== undefined\n}\n\n/**\n * Resolve the provider for a model.\n *\n * When `modelId` declares a registered provider (`anthropic/…`), THAT provider's key is\n * required — no substitution. Otherwise the registry is walked by priority, first match wins.\n *\n * @returns ResolvedProvider with apiKey + baseUrl + name\n * @throws Error if NO provider env var is set (actionable message)\n *\n * @public\n */\n/**\n * What a keyless provider reports as its key.\n *\n * This was `''` until #501, on the reasoning that an empty string is the honest value for a\n * provider that needs no credential. The reasoning was sound and the value was wrong: the SDK's\n * `createLocalAgent` calls `resolveApiKey`, which treats `''` as ABSENT, falls back to\n * `THEOKIT_API_KEY` — which nobody sets for a local model — and throws `missing_api_key` before it\n * ever looks at the provider. So #407 opened the registry door and every keyless provider,\n * including the builtin `ollama`, still could not serve a turn.\n *\n * `'local'` is the SDK's own sentinel (`LOCAL_RUNTIME_MOCK_KEY`, `internal/auth/api-key-validator`).\n * It is not a placeholder credential and does not become one: `validateApiKeyShape` short-circuits\n * on it, and `mergeExplicitApiKey` excludes it from the credential pools, so it never reaches a\n * provider as an `authorization` header. That was the objection the old comment raised against any\n * non-empty value, and it is the reason this specific value is the one that answers it — measured\n * against @theokit/sdk 4.52.1, where `apiKey: ''` throws `missing_api_key` and `apiKey: 'local'`\n * builds the agent.\n */\nconst KEYLESS_API_KEY = 'local'\n\nexport function resolveProvider(modelId?: string, options?: ResolveOptions): ResolvedProvider {\n const sorted = [...registryOf()].sort((a, b) => a.priority - b.priority)\n\n const declared = modelId === undefined ? undefined : providerOf(modelId, sorted)\n if (declared !== undefined && modelId !== undefined) {\n if (declared.envKey === undefined) {\n announce(declared, 'declared by the model', options)\n return { name: declared.name, apiKey: KEYLESS_API_KEY, baseUrl: baseUrlOf(declared) }\n }\n const apiKey = process.env[declared.envKey]\n if (apiKey && apiKey.length > 0) {\n announce(declared, 'declared by the model', options)\n return { name: declared.name, apiKey, baseUrl: baseUrlOf(declared) }\n }\n // Deliberately NOT falling back to another provider's key. Silently substituting one is how\n // theokit#326 produced a 401 nobody could attribute: the request went somewhere the agent\n // never named. Say which variable is missing and stop.\n throw new Error(\n `Model \"${modelId}\" declares provider \"${declared.name}\", but ${declared.envKey} is not set. ` +\n `Set ${declared.envKey}, or change the model's provider prefix.`,\n )\n }\n\n // A prefix the registry does not know is a CHOICE, not a silence — refuse before the walk.\n //\n // `providerOf` returns `undefined` for an unregistered prefix, which is indistinguishable from a\n // bare model id, so the priority walk below used to claim the turn and the \"not registered\" error\n // further down became unreachable for anyone holding a key. The visible cost was a confusing\n // message; the real one was a turn that SUCCEEDED against a provider nobody named — different\n // endpoint, different account billed, prompt delivered to a vendor the operator had routed away\n // from, announced only by a `console.warn` that fires once per process (#503).\n //\n // #326 settled the principle for a registered provider: declared wins, priority is the fallback\n // for a bare model id. This extends it to the case #326 did not reach — an unregistered prefix is\n // not a bare model id either.\n const unregisteredPrefix = declared === undefined ? declaredPrefixOf(modelId) : undefined\n if (unregisteredPrefix !== undefined) {\n throw new Error(\n `Model \"${String(modelId)}\" declares provider \"${unregisteredPrefix}\", which is not registered. ` +\n `Registered providers: ${sorted.map((p) => p.name).join(', ')}. ` +\n `Register it with registerProvider({ name: '${unregisteredPrefix}', … }), or use a registered prefix.`,\n )\n }\n\n // Only providers that take a credential. A keyless entry is reachable exclusively through the\n // declared branch above; see `ProviderDescriptor.envKey` for why the walk cannot include it.\n const credentialed = sorted.filter(takesCredential)\n\n for (const desc of credentialed) {\n const apiKey = process.env[desc.envKey]\n if (apiKey && apiKey.length > 0) {\n announce(desc, 'by env priority', options)\n return {\n name: desc.name,\n apiKey,\n baseUrl: baseUrlOf(desc),\n }\n }\n }\n\n // Nothing resolved, and the model id declared nothing — the unregistered-prefix case was refused\n // above the walk, which is where it belongs (#503). This message is for the bare-id case only.\n //\n // It used to live here and it was the right message in the wrong place: reachable exactly when\n // the operator had no keys at all, which is the case that needed it least (usetheokit/theokit#407\n // is why the message exists; #503 is why it moved).\n const envKeys = credentialed.map((p) => p.envKey).join(' OR ')\n throw new Error(\n `No LLM provider API key found in environment. Set one of: ${envKeys}. ` +\n `Get a free OpenRouter key at https://openrouter.ai/keys (recommended — one key, many models).`,\n )\n}\n\n/**\n * Try to resolve — does NOT throw. Returns null if no provider available.\n * Useful for graceful degradation (e.g., mock mode).\n *\n * @public\n */\nexport function tryResolveProvider(modelId?: string): ResolvedProvider | null {\n try {\n return resolveProvider(modelId)\n } catch {\n return null\n }\n}\n","/**\n * M4 (theokit-ai-first) — the in-process HITL approval registry.\n *\n * The HITL plugin's `awaitApproval` calls `register(approvalId)` and awaits the returned Promise\n * (this is what genuinely PAUSES the SDK run — the SDK `pre_tool_call` hook is awaited). The\n * approve route calls `resolve(approvalId, approved)` to settle it. A per-approval timeout settles\n * the Promise deterministically per the `@HumanInTheLoop` `onTimeout` policy so a hung approval\n * never leaks the paused stream.\n *\n * Single-process contract (ADR 0038 / plan Drawback 2): a multi-instance deploy needs a shared\n * registry — the interface is injectable so a durable impl (Redis, etc.) slots in without touching\n * the harness. We do NOT build a durable store now (YAGNI).\n */\n// The timeout policy vocabulary is owned by the `@HumanInTheLoop` decorator (DRY / G12) — reuse it\n// rather than re-declaring the union, so the two can never drift.\nimport type { TimeoutAction } from '@theokit/agents'\n\nimport { processSingleton } from '../_internal/process-singleton.js'\n\nexport interface RegisterOptions {\n /** Milliseconds before the approval auto-settles per `onTimeout`. */\n timeoutMs: number\n /**\n * What a timeout means. Only `'proceed'` auto-approves; `'abort'` and `'retry'` both deny — the\n * registry does NOT implement retry semantics (a timed-out `'retry'` is a deny, not a re-prompt).\n */\n onTimeout: TimeoutAction\n /** M14 — the gated tool name, surfaced by `list()` (optional; absent for legacy callers). */\n toolName?: string\n /** M14 — the approval question, surfaced by `list()` (optional). */\n question?: string\n /**\n * Who the run belongs to — the `RouteSubject.id` admitted for it (B-016).\n *\n * Absent when the run had no identity to record, which is the `'public'` agent path:\n * `admitAgentRequest` deliberately does not resolve a subject when the policy is absent or\n * `'public'`, so there is nothing to attribute the approval to. Absent therefore means \"no owner\n * was established\", never \"anyone\", and the caller must branch on the difference — a rule that\n * refused when it is absent would start turning public agents away.\n *\n * Deliberately NOT surfaced by `list()`: that listing feeds a UI, and owner ids are identity.\n */\n owner?: string\n /**\n * M20 — an optional JSON-schema descriptor of the custom payload the approver may attach. Carried\n * verbatim into `list()` + the `approval_required` event so the UI knows what to collect. Kept as\n * a plain JSON object (not a live Zod schema) so the registry stays serializable and SDK-free.\n */\n payloadSchema?: Record<string, unknown>\n}\n\n/**\n * M20 — a settled HITL decision. `approved` is the allow/deny bit (backward-compatible with the\n * legacy boolean); `reason` + `payload` are the optional approver-attached extras that surface to\n * the model (on denial, via the veto message) and to the app/UI.\n */\nexport interface ApprovalDecision {\n approved: boolean\n reason?: string\n payload?: unknown\n /**\n * What settled this, when it was not a person (usetheokit/theokit#393).\n *\n * Absent means the decision arrived through `resolve()` — a human, or whatever the application\n * wired to that route. `'timeout'` means the window closed with nobody deciding and `onTimeout`\n * was applied.\n *\n * The distinction is the whole point of a HITL gate: without it an expired approval and an\n * explicit deny are byte-identical on the wire, and the sentence the caller gets — \"denied by\n * human approver\" — asserts a fact that did not happen. It is marked on the ALLOW side too:\n * `onTimeout: 'proceed'` permits the tool BECAUSE nobody answered, and recording that as a plain\n * approval is the same fabrication with the opposite sign.\n *\n * One member rather than `timedOut: boolean`, because the question is what settled it; a second\n * cause (a cancel, a shutdown) joins the union instead of adding another boolean.\n */\n settledBy?: 'timeout'\n}\n\n/** M14 — a pending approval as surfaced by {@link ApprovalRegistry.list}. */\nexport interface PendingApproval {\n approvalId: string\n toolName?: string\n question?: string\n /** Epoch millis when the pending approval auto-settles (registeredAt + timeoutMs). */\n expiresAt: number\n /** M20 — the declared custom-payload schema, if the gated tool declares one. */\n payloadSchema?: Record<string, unknown>\n}\n\nexport interface ApprovalRegistry {\n /**\n * Register a pending approval; the returned Promise settles with the full {@link ApprovalDecision}\n * on `resolve` or timeout (M20 — was a bare boolean pre-M20).\n */\n register(approvalId: string, opts: RegisterOptions): Promise<ApprovalDecision>\n /**\n * Settle a pending approval. Accepts a full {@link ApprovalDecision} OR a bare boolean (coerced to\n * `{ approved }` — backward-compatible). Returns false if the id is unknown or already settled.\n */\n resolve(approvalId: string, decision: boolean | ApprovalDecision): boolean\n /** M14 — list the currently-pending approvals (process-wide; single-process contract). */\n list(): PendingApproval[]\n /**\n * Who owns the pending approval `approvalId`, or `undefined` (B-016).\n *\n * `undefined` covers three cases the caller treats identically — never registered, already\n * settled, and registered without an owner — because in all three there is nothing to compare a\n * caller against.\n */\n ownerOf(approvalId: string): string | undefined\n}\n\ninterface Pending {\n settle: (decision: ApprovalDecision) => void\n timer: ReturnType<typeof setTimeout>\n info: PendingApproval\n /** Held beside `info` rather than inside it, so `list()` cannot leak it (B-016). */\n owner?: string\n}\n\n/**\n * The one process-wide registry the stream mount (`mountAgent`) and the approve route share.\n *\n * The in-process impl holds LIVE Promise resolvers in memory — the approval a request awaits and\n * the approval the route resolves MUST be the same object, so a single instance per process is not\n * a convenience but a correctness requirement. Lazily created; a durable/multi-instance deploy\n * swaps this accessor for a shared-store impl (ADR 0038 / plan Drawback 2) without touching callers.\n * Tests use {@link createInProcessApprovalRegistry} directly — never this singleton.\n */\nexport function getApprovalRegistry(): ApprovalRegistry {\n // Per PROCESS, not per module instance (usetheokit/theokit#401). The paragraph above calls a\n // single instance \"not a convenience but a correctness requirement\", and a module-level `let`\n // does not deliver that: it gives one instance per MODULE INSTANCE, and this module is emitted\n // into two chunks of the published bundle. A run awaiting an approval and the route resolving it\n // could hold different objects, and the symptom would be a HITL pause that never resumes.\n return processSingleton('approval-registry', () => createInProcessApprovalRegistry())\n}\n\nexport function createInProcessApprovalRegistry(): ApprovalRegistry {\n const pending = new Map<string, Pending>()\n\n return {\n register(approvalId, opts) {\n return new Promise<ApprovalDecision>((resolve) => {\n const settle = (decision: ApprovalDecision): void => {\n const entry = pending.get(approvalId)\n if (!entry) return\n clearTimeout(entry.timer)\n pending.delete(approvalId)\n resolve(decision)\n }\n // 'proceed' → allow on timeout; 'abort'/'retry' → deny on timeout.\n const timer = setTimeout(() => {\n settle({\n approved: opts.onTimeout === 'proceed',\n settledBy: 'timeout',\n // Every value in this sentence was already here and none of it used to survive the\n // settle. `onTimeout` is named because all three of its values reach this line and two\n // of them deny — an operator who wrote `'retry'` and got a denial had nothing to read\n // that mentioned retry.\n reason: `no decision within ${String(opts.timeoutMs)} ms; onTimeout: '${opts.onTimeout}' was applied`,\n })\n }, opts.timeoutMs)\n const info: PendingApproval = {\n approvalId,\n toolName: opts.toolName,\n question: opts.question,\n expiresAt: Date.now() + opts.timeoutMs,\n ...(opts.payloadSchema !== undefined ? { payloadSchema: opts.payloadSchema } : {}),\n }\n pending.set(approvalId, {\n settle,\n timer,\n info,\n ...(opts.owner !== undefined ? { owner: opts.owner } : {}),\n })\n })\n },\n resolve(approvalId, decision) {\n const entry = pending.get(approvalId)\n if (!entry) return false\n // M20 — coerce the legacy bare-boolean form to a decision object.\n entry.settle(typeof decision === 'boolean' ? { approved: decision } : decision)\n return true\n },\n list() {\n return [...pending.values()].map((p) => p.info)\n },\n ownerOf(approvalId) {\n // `settle` deletes the entry, so an approval that has been answered or timed out reports no\n // owner — which is what stops a later registration of the same id from inheriting one.\n return pending.get(approvalId)?.owner\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  writeAtomic
3
- } from "./chunk-VZTYCAO6.js";
3
+ } from "./chunk-IDEZ7FU2.js";
4
4
  import {
5
5
  extractTraceContext,
6
6
  generateNewTraceContext
7
7
  } from "./chunk-UMNXCU3K.js";
8
8
  import {
9
9
  importUserModule
10
- } from "./chunk-52PDWL5S.js";
10
+ } from "./chunk-5KRMDP46.js";
11
11
  import {
12
12
  walkSourceFiles
13
- } from "./chunk-5K4TDLRG.js";
13
+ } from "./chunk-SOYOYGWY.js";
14
14
  import {
15
15
  compareByCodeUnit
16
16
  } from "./chunk-ZWN3AODA.js";
@@ -449,8 +449,8 @@ function relativize(absPath, root) {
449
449
  }
450
450
 
451
451
  // src/server/jobs/job-scan.ts
452
- import { existsSync } from "fs";
453
- import { basename } from "path";
452
+ import { existsSync } from "node:fs";
453
+ import { basename } from "node:path";
454
454
  var DuplicateJobNameError = class extends Error {
455
455
  constructor(jobName, filePaths) {
456
456
  super(
@@ -526,4 +526,4 @@ export {
526
526
  DuplicateJobNameError,
527
527
  scanJobs
528
528
  };
529
- //# sourceMappingURL=chunk-JDN5SAAD.js.map
529
+ //# sourceMappingURL=chunk-44E7XIZC.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  importUserModule
3
- } from "./chunk-52PDWL5S.js";
3
+ } from "./chunk-5KRMDP46.js";
4
4
 
5
5
  // src/server/scan/module-loader.ts
6
6
  function createViteLoader(vite) {
@@ -14,4 +14,4 @@ export {
14
14
  createViteLoader,
15
15
  createProductionLoader
16
16
  };
17
- //# sourceMappingURL=chunk-OQCRCXCP.js.map
17
+ //# sourceMappingURL=chunk-4ERYQBL6.js.map
@@ -1,5 +1,5 @@
1
1
  // src/config/import-user-module.ts
2
- import { pathToFileURL } from "url";
2
+ import { pathToFileURL } from "node:url";
3
3
  var CYCLE_PATTERNS = [
4
4
  // Node 22 strict ESM cycle detection when a registered loader hook
5
5
  // re-enters its own evaluation context.
@@ -33,4 +33,4 @@ async function importUserModule(absoluteFilePath, parentURL = import.meta.url) {
33
33
  export {
34
34
  importUserModule
35
35
  };
36
- //# sourceMappingURL=chunk-52PDWL5S.js.map
36
+ //# sourceMappingURL=chunk-5KRMDP46.js.map
@@ -1,6 +1,6 @@
1
1
  // src/vite-plugin/resolve-theo-root.ts
2
- import { existsSync } from "fs";
3
- import { resolve } from "path";
2
+ import { existsSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
4
  function resolveTheoRootDir(currentDir) {
5
5
  if (existsSync(resolve(currentDir, "client"))) {
6
6
  return currentDir;
@@ -11,4 +11,4 @@ function resolveTheoRootDir(currentDir) {
11
11
  export {
12
12
  resolveTheoRootDir
13
13
  };
14
- //# sourceMappingURL=chunk-VQPHPQ5L.js.map
14
+ //# sourceMappingURL=chunk-7NRFZBAL.js.map
@@ -87,13 +87,14 @@ function declaredPrefixOf(modelId) {
87
87
  function takesCredential(desc) {
88
88
  return desc.envKey !== void 0;
89
89
  }
90
+ var KEYLESS_API_KEY = "local";
90
91
  function resolveProvider(modelId, options) {
91
92
  const sorted = [...registryOf()].sort((a, b) => a.priority - b.priority);
92
93
  const declared = modelId === void 0 ? void 0 : providerOf(modelId, sorted);
93
94
  if (declared !== void 0 && modelId !== void 0) {
94
95
  if (declared.envKey === void 0) {
95
96
  announce(declared, "declared by the model", options);
96
- return { name: declared.name, apiKey: "", baseUrl: baseUrlOf(declared) };
97
+ return { name: declared.name, apiKey: KEYLESS_API_KEY, baseUrl: baseUrlOf(declared) };
97
98
  }
98
99
  const apiKey = process.env[declared.envKey];
99
100
  if (apiKey && apiKey.length > 0) {
@@ -104,6 +105,12 @@ function resolveProvider(modelId, options) {
104
105
  `Model "${modelId}" declares provider "${declared.name}", but ${declared.envKey} is not set. Set ${declared.envKey}, or change the model's provider prefix.`
105
106
  );
106
107
  }
108
+ const unregisteredPrefix = declared === void 0 ? declaredPrefixOf(modelId) : void 0;
109
+ if (unregisteredPrefix !== void 0) {
110
+ throw new Error(
111
+ `Model "${String(modelId)}" declares provider "${unregisteredPrefix}", which is not registered. Registered providers: ${sorted.map((p) => p.name).join(", ")}. Register it with registerProvider({ name: '${unregisteredPrefix}', \u2026 }), or use a registered prefix.`
112
+ );
113
+ }
107
114
  const credentialed = sorted.filter(takesCredential);
108
115
  for (const desc of credentialed) {
109
116
  const apiKey = process.env[desc.envKey];
@@ -116,12 +123,6 @@ function resolveProvider(modelId, options) {
116
123
  };
117
124
  }
118
125
  }
119
- const prefix = declaredPrefixOf(modelId);
120
- if (prefix !== void 0) {
121
- throw new Error(
122
- `Model "${modelId}" declares provider "${prefix}", which is not registered. Registered providers: ${sorted.map((p) => p.name).join(", ")}. Register it with registerProvider({ name: '${prefix}', \u2026 }), or use a registered prefix.`
123
- );
124
- }
125
126
  const envKeys = credentialed.map((p) => p.envKey).join(" OR ");
126
127
  throw new Error(
127
128
  `No LLM provider API key found in environment. Set one of: ${envKeys}. Get a free OpenRouter key at https://openrouter.ai/keys (recommended \u2014 one key, many models).`
@@ -143,4 +144,4 @@ export {
143
144
  resolveProvider,
144
145
  tryResolveProvider
145
146
  };
146
- //# sourceMappingURL=chunk-3PBAN2YE.js.map
147
+ //# sourceMappingURL=chunk-CF2DNN5R.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/_internal/process-singleton.ts","../src/server/agent/provider-resolver.ts"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/server/_internal/process-singleton.ts","../src/server/agent/provider-resolver.ts"],"mappings":"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","names":[]}
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ import "tsx/esm";
3
+ import {
4
+ findControllerFiles
5
+ } from "./chunk-CTRVTBGY.js";
6
+
7
+ // src/cli/commands/build/emit-controllers.ts
8
+ import "reflect-metadata";
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
10
+ import { basename, dirname, join, relative, resolve } from "path";
11
+ import { pathToFileURL } from "url";
12
+ import {
13
+ CONTROLLER_PREFIX,
14
+ ROUTE_METHODS,
15
+ USE_GUARDS,
16
+ getMeta,
17
+ isControllerClass,
18
+ transformControllerSource
19
+ } from "@theokit/http";
20
+ var CONTROLLER_MANIFEST_FILE = "controllers.json";
21
+ var CONTROLLER_OUT_DIR = "controllers";
22
+ function rewriteLocalImports(code, sourceFile) {
23
+ const fromDir = dirname(sourceFile);
24
+ return code.replace(
25
+ /(\bfrom\s*|\bimport\s*\(\s*)(['"])(\.[^'"]*)\2/g,
26
+ (whole, lead, quote, spec) => {
27
+ const resolved = resolve(fromDir, spec);
28
+ const onDisk = existingSourcePath(resolved);
29
+ if (onDisk === void 0) return whole;
30
+ return `${lead}${quote}${pathToFileURL(onDisk).href}${quote}`;
31
+ }
32
+ );
33
+ }
34
+ function existingSourcePath(resolved) {
35
+ const withoutExt = resolved.replace(/\.(js|mjs|cjs)$/, "");
36
+ const candidates = [
37
+ ...resolved === withoutExt ? [] : [`${withoutExt}.ts`, `${withoutExt}.tsx`, resolved],
38
+ ...resolved === withoutExt ? [`${resolved}.ts`, `${resolved}.tsx`, `${resolved}.js`] : [],
39
+ join(withoutExt, "index.ts"),
40
+ join(withoutExt, "index.tsx"),
41
+ join(withoutExt, "index.js")
42
+ ];
43
+ return candidates.find((c) => existsSync(c));
44
+ }
45
+ var SERVED_PREFIX = "api";
46
+ var UnreachableControllerPathError = class extends Error {
47
+ constructor(controller, declaredPath, file) {
48
+ super(
49
+ [
50
+ `${controller} declares \`@Controller('${declaredPath}')\`, which this runtime never routes to.`,
51
+ ``,
52
+ ` ${file}`,
53
+ ``,
54
+ ` Controller routes are served from a fall-through that runs only for URLs under`,
55
+ ` \`/${SERVED_PREFIX}/\` \u2014 a file-route miss is what reaches it. A path outside that`,
56
+ ` prefix compiles, emits, and then answers 404 for the life of the app.`,
57
+ ``,
58
+ ` Write it as:`,
59
+ ``,
60
+ ` @Controller('${SERVED_PREFIX}/${declaredPath.replace(/^\/+/, "")}')`,
61
+ ``,
62
+ ` Refused at build rather than left to 404, for the reason this module already refuses a`,
63
+ ` compile error: shipping a route nothing can reach, with nothing pointing back at why.`
64
+ ].join("\n")
65
+ );
66
+ this.controller = controller;
67
+ this.declaredPath = declaredPath;
68
+ this.file = file;
69
+ }
70
+ controller;
71
+ declaredPath;
72
+ file;
73
+ name = "UnreachableControllerPathError";
74
+ };
75
+ function isReachable(prefix) {
76
+ const segments = prefix.split("/").filter((s) => s.length > 0);
77
+ return segments[0] === SERVED_PREFIX;
78
+ }
79
+ function assertControllerPathsReachable(loaded) {
80
+ for (const { cls, sourceFile } of loaded) {
81
+ const meta = getMeta(CONTROLLER_PREFIX, cls);
82
+ const prefix = meta?.prefix ?? "";
83
+ if (isReachable(prefix)) continue;
84
+ throw new UnreachableControllerPathError(cls.name || "a controller", prefix, sourceFile);
85
+ }
86
+ }
87
+ var PUBLIC_ROUTE_METADATA = "theokit:public";
88
+ var UndeclaredControllerAccessError = class extends Error {
89
+ constructor(controller, method, verb, file) {
90
+ super(
91
+ [
92
+ `${controller}.${method}() serves ${verb} and declares no access decision.`,
93
+ ``,
94
+ ` ${file}`,
95
+ ``,
96
+ ` A file route with no \`.policy\` fails this build for the same reason (ADR 0001): a route`,
97
+ ` nobody thought about is the one that ships open. The decorator path now says the same.`,
98
+ ``,
99
+ ` Require a session:`,
100
+ ``,
101
+ ` @UseGuards(AuthGuard) // on the method, or on the @Controller to cover all of them`,
102
+ ``,
103
+ ` Or state that it is open on purpose \u2014 a health check, an OAuth callback:`,
104
+ ``,
105
+ ` @SetMetadata('${PUBLIC_ROUTE_METADATA}', true)`,
106
+ ``,
107
+ ` Both are declarations. What is refused is neither.`
108
+ ].join("\n")
109
+ );
110
+ this.controller = controller;
111
+ this.method = method;
112
+ this.verb = verb;
113
+ this.file = file;
114
+ }
115
+ controller;
116
+ method;
117
+ verb;
118
+ file;
119
+ name = "UndeclaredControllerAccessError";
120
+ };
121
+ function assertControllerAccessDeclared(loaded) {
122
+ for (const { cls, sourceFile } of loaded) {
123
+ if (getMeta(USE_GUARDS, cls) !== void 0) continue;
124
+ if (Reflect.getMetadata(PUBLIC_ROUTE_METADATA, cls) === true) continue;
125
+ const routes = getMeta(ROUTE_METHODS, cls) ?? [];
126
+ for (const { verb, propertyKey } of routes) {
127
+ if (getMeta(USE_GUARDS, cls, propertyKey) !== void 0) continue;
128
+ if (Reflect.getMetadata(PUBLIC_ROUTE_METADATA, cls, propertyKey) === true) continue;
129
+ throw new UndeclaredControllerAccessError(
130
+ cls.name || "a controller",
131
+ propertyKey,
132
+ verb,
133
+ sourceFile
134
+ );
135
+ }
136
+ }
137
+ }
138
+ async function loadEmittedControllers(emitted) {
139
+ const out = [];
140
+ for (const { modulePath, sourceFile } of emitted) {
141
+ const url = `${pathToFileURL(modulePath).href}?t=${Date.now()}`;
142
+ const mod = await import(url);
143
+ for (const exported of Object.values(mod)) {
144
+ if (typeof exported !== "function" || !isControllerClass(exported)) continue;
145
+ out.push({ cls: exported, sourceFile });
146
+ }
147
+ }
148
+ return out;
149
+ }
150
+ async function emitControllerArtifacts(opts) {
151
+ const controllersDir = resolve(opts.serverDir, CONTROLLER_OUT_DIR);
152
+ const files = findControllerFiles(controllersDir);
153
+ if (files.length === 0) return null;
154
+ const outDir = resolve(opts.distDir, CONTROLLER_OUT_DIR);
155
+ mkdirSync(outDir, { recursive: true });
156
+ const modules = [];
157
+ const emitted = [];
158
+ for (const file of files) {
159
+ const compiled = await transformControllerSource(readFileSync(file, "utf-8"), file);
160
+ const code = rewriteLocalImports(compiled, file);
161
+ const rel = relative(controllersDir, file).replace(/\.controller\.ts$/, ".controller.mjs");
162
+ const outPath = join(outDir, rel);
163
+ mkdirSync(dirname(outPath), { recursive: true });
164
+ writeFileSync(outPath, code);
165
+ modules.push(`${CONTROLLER_OUT_DIR}/${rel.split("\\").join("/")}`);
166
+ emitted.push({ modulePath: outPath, sourceFile: file });
167
+ }
168
+ const loaded = await loadEmittedControllers(emitted);
169
+ assertControllerPathsReachable(loaded);
170
+ assertControllerAccessDeclared(loaded);
171
+ const manifest = { version: 1, modules };
172
+ writeFileSync(
173
+ resolve(opts.distDir, CONTROLLER_MANIFEST_FILE),
174
+ JSON.stringify(manifest, null, 2) + "\n"
175
+ );
176
+ return manifest;
177
+ }
178
+ function describeControllerArtifacts(manifest) {
179
+ if (manifest === null) return "controllers: none";
180
+ const names = manifest.modules.map((m) => basename(m, ".mjs")).join(", ");
181
+ return `controllers: ${manifest.modules.length} compiled (${names})`;
182
+ }
183
+
184
+ export {
185
+ CONTROLLER_MANIFEST_FILE,
186
+ emitControllerArtifacts,
187
+ describeControllerArtifacts
188
+ };
189
+ //# sourceMappingURL=chunk-CLBJTDI6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/build/emit-controllers.ts"],"sourcesContent":["import 'reflect-metadata'\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { basename, dirname, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport {\n CONTROLLER_PREFIX,\n ROUTE_METHODS,\n USE_GUARDS,\n getMeta,\n isControllerClass,\n transformControllerSource,\n} from '@theokit/http'\n\nimport { findControllerFiles } from '../../../server/http/controller-dispatch.js'\n\n/**\n * theokit#123 — compile `server/controllers/**` into `dist` so production can serve them.\n *\n * ## Why production needed anything at all\n *\n * #122 made decorator controllers first-class in `theokit dev`, where a Vite `enforce:'pre'` swc\n * transform compiles them on the fly — parameter decorators (`@Body`/`@Param`/`@Query`) emit\n * metadata esbuild cannot produce. `theokit start` has no Vite and no transform, so an uncompiled\n * `.controller.ts` simply cannot load, and controller routes 404'd in production while working in\n * dev. That split is the whole issue.\n *\n * ## Why compile at BUILD time rather than load with swc at runtime\n *\n * `loadControllerWithSwc` would work in production and was the smaller diff. It is refused on\n * purpose: `@swc/core` is a peer dependency and a native binary, so that path makes every deployed\n * app carry a compiler it only needs once, and turns a missing optional peer into a runtime 404\n * instead of a build failure. Compilation belongs where the rest of the build already is — and it\n * is what the issue's root-cause note asks for.\n *\n * ## Why NOT in `generateManifest`\n *\n * ADR-5 keeps controllers out of the manifest, and that is preserved here: the manifest is the\n * deploy-adapter contract, and adding a parallel route source to it would ripple through every\n * adapter. This emits a SEPARATE artifact (`dist/controllers.json` + `dist/controllers/*.mjs`) that\n * only the Node start path reads. An adapter that knows nothing about controllers keeps working\n * exactly as before.\n */\n\n/** The build artifact `theokit start` reads to find compiled controllers. */\ninterface ControllerBuildManifest {\n version: 1\n /** Emitted module paths, relative to `distDir`, in scan order. */\n modules: string[]\n}\n\nexport const CONTROLLER_MANIFEST_FILE = 'controllers.json'\nconst CONTROLLER_OUT_DIR = 'controllers'\n\n/**\n * Compile every controller under `<serverDir>/controllers` into `<distDir>/controllers`.\n *\n * No controllers ⇒ **no artifact at all** (not an empty one). `theokit start` treats a missing\n * manifest as \"this app has no controllers\" and skips the whole branch, so a routes-only app pays\n * nothing and its `dist` is byte-identical to before — the ADR-5 posture, kept.\n */\n/**\n * Point a compiled controller's RELATIVE imports back at the app's own source tree.\n *\n * theokit#516 — controllers are compiled into `<distDir>/controllers` because parameter decorators\n * need metadata only swc emits. File routes are never compiled: in production `importUserModule`\n * loads them straight from the source, which is why THEIR relative imports resolve. A controller's\n * did not — `../auth/index.js` resolved from `dist`, where the app does not exist — so a controller\n * that imported anything of the app's worked under `theokit dev` and failed the build.\n *\n * Rewriting to an ABSOLUTE path into the source tree is the smallest fix that keeps one definition\n * of every module. The alternatives each cost more than they buy: copying the app's modules beside\n * the controller duplicates them, and bundling inlines them, so in both a module with state exists\n * twice and the two copies drift.\n *\n * The extension is mapped back to what is on disk. TypeScript source writes `./x.js` for `./x.ts`\n * (NodeNext), and the emitted module has to name the file that exists, since it is loaded by the\n * same `importUserModule` path a file route takes — `.ts` included.\n *\n * BARE specifiers are left exactly as written. `@theokit/http`, `zod` and every other package\n * resolve through `node_modules` from any directory, so rewriting them would replace something that\n * works with a path that pins a version.\n */\nfunction rewriteLocalImports(code: string, sourceFile: string): string {\n const fromDir = dirname(sourceFile)\n return code.replace(\n /(\\bfrom\\s*|\\bimport\\s*\\(\\s*)(['\"])(\\.[^'\"]*)\\2/g,\n (whole, lead: string, quote: string, spec: string) => {\n const resolved = resolve(fromDir, spec)\n const onDisk = existingSourcePath(resolved)\n if (onDisk === undefined) return whole\n return `${lead}${quote}${pathToFileURL(onDisk).href}${quote}`\n },\n )\n}\n\n/**\n * The file a compiled specifier names, as it exists on disk — or `undefined` when nothing does.\n *\n * `./x.js` may be `x.ts`, `x.tsx`, a literal `x.js`, or a directory holding `index.*`. Returning\n * `undefined` for the rest is deliberate: a specifier this cannot resolve is left UNCHANGED, so the\n * runtime reports the missing module by the name the author wrote. Substituting a guess would move\n * the error somewhere the author never typed.\n */\nfunction existingSourcePath(resolved: string): string | undefined {\n const withoutExt = resolved.replace(/\\.(js|mjs|cjs)$/, '')\n const candidates = [\n ...(resolved === withoutExt ? [] : [`${withoutExt}.ts`, `${withoutExt}.tsx`, resolved]),\n ...(resolved === withoutExt ? [`${resolved}.ts`, `${resolved}.tsx`, `${resolved}.js`] : []),\n join(withoutExt, 'index.ts'),\n join(withoutExt, 'index.tsx'),\n join(withoutExt, 'index.js'),\n ]\n return candidates.find((c) => existsSync(c))\n}\n\n/**\n * The URL prefix both runtimes gate the controller fall-through on.\n *\n * `start/handlers.ts` and `vite-plugin/api-middleware.ts` each return early unless the request URL\n * starts with `/api/`, so a controller declaring anything else describes a path that branch never\n * visits. Written here as a constant rather than a literal in the check, because the day that gate\n * moves this is the line that has to move with it.\n */\nconst SERVED_PREFIX = 'api'\n\n/**\n * A controller compiled successfully and mounted at a path the runtime never routes to.\n *\n * Its own class so a caller can tell it from a compile failure: the source is fine, the decorators\n * are fine, and the app would start — the route simply answers 404 forever. That is the failure\n * this module's docblock already refuses for compile errors, reached by a different road.\n *\n * Measured against `theokit@0.56.0`: `@Controller('probe')` built clean, emitted a module, wrote\n * the manifest, and `/api/probe` returned 404. `/probe` returned **200** — the SPA fallback serving\n * `index.html`, so the status code agreed with success while the body was a web page. A defect that\n * survives a status-code check is exactly the kind a build gate has to catch instead.\n */\nexport class UnreachableControllerPathError extends Error {\n override readonly name = 'UnreachableControllerPathError'\n constructor(\n readonly controller: string,\n readonly declaredPath: string,\n readonly file: string,\n ) {\n super(\n [\n `${controller} declares \\`@Controller('${declaredPath}')\\`, which this runtime never routes to.`,\n ``,\n ` ${file}`,\n ``,\n ` Controller routes are served from a fall-through that runs only for URLs under`,\n ` \\`/${SERVED_PREFIX}/\\` — a file-route miss is what reaches it. A path outside that`,\n ` prefix compiles, emits, and then answers 404 for the life of the app.`,\n ``,\n ` Write it as:`,\n ``,\n ` @Controller('${SERVED_PREFIX}/${declaredPath.replace(/^\\/+/, '')}')`,\n ``,\n ` Refused at build rather than left to 404, for the reason this module already refuses a`,\n ` compile error: shipping a route nothing can reach, with nothing pointing back at why.`,\n ].join('\\n'),\n )\n }\n}\n\n/**\n * Is `prefix` reachable from the served path?\n *\n * `'api'`, `'/api'`, `'api/x'` and `'/api/x'` are the same URL written four ways, so all four pass:\n * the gate is about reachability, not spelling. A prefix that merely STARTS with the letters —\n * `'apiary'` — is not, which is why the check is segment-wise rather than a `startsWith`.\n */\nfunction isReachable(prefix: string): boolean {\n const segments = prefix.split('/').filter((s) => s.length > 0)\n return segments[0] === SERVED_PREFIX\n}\n\n/**\n * Refuse any emitted controller whose declared path the runtime cannot route to.\n *\n * Reads the prefix off the COMPILED module rather than the source: the decorator metadata is what\n * the runtime will actually match on, and a regex over source text would disagree with it the first\n * time someone writes the prefix as anything other than a literal.\n */\nfunction assertControllerPathsReachable(\n loaded: readonly { cls: ControllerClass; sourceFile: string }[],\n): void {\n for (const { cls, sourceFile } of loaded) {\n const meta = getMeta<{ prefix?: string }>(CONTROLLER_PREFIX, cls)\n const prefix = meta?.prefix ?? ''\n if (isReachable(prefix)) continue\n throw new UnreachableControllerPathError(cls.name || 'a controller', prefix, sourceFile)\n }\n}\n\n/**\n * The metadata key an explicitly-open controller route sets.\n *\n * The counterpart of `'public'` on the file path, and it exists for the same routes: a health\n * check a load balancer calls with no session, an OAuth callback the provider redirects to.\n *\n * An app writes the STRING, not this constant: `@SetMetadata('theokit:public', true)`. The docblock\n * used to show `@SetMetadata(PUBLIC_ROUTE_METADATA, true)`, which no consumer can write — this\n * module lives under `cli/commands/build/` and no entry point reaches it. Exporting it so the\n * example became true would publish a new surface for a string that appears once per open route and\n * whose mistyping fails this very gate loudly, so the example is corrected instead. `@theokit/http`\n * already ships `SetMetadata`, so no new decorator is needed either (parsimony-ladder.md rung 4).\n *\n * It is an opt-out, never a default. A route that is open says so in the file, where review sees\n * it; a route nobody thought about is the one that ships open, which is the whole reason ADR 0001\n * made absence stop meaning open on the file path.\n */\nconst PUBLIC_ROUTE_METADATA = 'theokit:public'\n\n/**\n * A controller route that declares no access decision at all.\n *\n * Distinct from a guard REFUSING a request — that is the system working. This is a route where\n * nobody wrote down whether it should be reachable, which on the file path is a build failure and\n * on the decorator path was, until now, silence.\n *\n * Measured against theokit@0.56.0: a `@Controller` with no guard answered 200 to a request with no\n * session, while the file route it would replace answered 403, on the same server in the same\n * second. The mechanism was never missing — `@UseGuards` works and returns `403 FORBIDDEN`. Only\n * the refusal of absence was.\n */\nexport class UndeclaredControllerAccessError extends Error {\n override readonly name = 'UndeclaredControllerAccessError'\n constructor(\n readonly controller: string,\n readonly method: string,\n readonly verb: string,\n readonly file: string,\n ) {\n super(\n [\n `${controller}.${method}() serves ${verb} and declares no access decision.`,\n ``,\n ` ${file}`,\n ``,\n ` A file route with no \\`.policy\\` fails this build for the same reason (ADR 0001): a route`,\n ` nobody thought about is the one that ships open. The decorator path now says the same.`,\n ``,\n ` Require a session:`,\n ``,\n ` @UseGuards(AuthGuard) // on the method, or on the @Controller to cover all of them`,\n ``,\n ` Or state that it is open on purpose — a health check, an OAuth callback:`,\n ``,\n ` @SetMetadata('${PUBLIC_ROUTE_METADATA}', true)`,\n ``,\n ` Both are declarations. What is refused is neither.`,\n ].join('\\n'),\n )\n }\n}\n\n/**\n * Refuse any controller route whose access decision nobody wrote down.\n *\n * A guard on the CLASS covers every method under it, which is the shape most controllers want and\n * mirrors how `@UseGuards` already behaves at runtime — the gate must agree with the dispatcher or\n * it would refuse code that works.\n *\n * Read off the compiled module for the reason the prefix check is: decorator metadata is what the\n * runtime matches on. The per-method key lives on the CLASS with the property name, not on the\n * prototype and not on the descriptor — measured, after both of those returned `undefined`.\n */\nfunction assertControllerAccessDeclared(\n loaded: readonly { cls: ControllerClass; sourceFile: string }[],\n): void {\n for (const { cls, sourceFile } of loaded) {\n // Class level covers every method under it — for BOTH forms. A guard on the class already\n // behaves that way at runtime, and a controller whose routes are all open (a health group, an\n // OAuth callback group) must be declarable once for the same reason. Checking one at class\n // level and not the other would refuse code that is correct.\n if (getMeta<unknown[]>(USE_GUARDS, cls) !== undefined) continue\n if (Reflect.getMetadata(PUBLIC_ROUTE_METADATA, cls) === true) continue\n\n const routes = getMeta<{ verb: string; propertyKey: string }[]>(ROUTE_METHODS, cls) ?? []\n for (const { verb, propertyKey } of routes) {\n if (getMeta<unknown[]>(USE_GUARDS, cls, propertyKey) !== undefined) continue\n // `Reflect` directly, not `getMeta`: `@SetMetadata` writes through `Reflect.defineMetadata`\n // with the key as given, and `getMeta` is typed for the symbol keys this package defines.\n if (Reflect.getMetadata(PUBLIC_ROUTE_METADATA, cls, propertyKey) === true) continue\n throw new UndeclaredControllerAccessError(\n cls.name || 'a controller',\n propertyKey,\n verb,\n sourceFile,\n )\n }\n }\n}\n\n/**\n * Load every emitted controller class once, for both checks below.\n *\n * One pass rather than one per check: the alternative loads each module twice and doubles the\n * surface on which a stale import could fool a gate.\n *\n * The `?t=` is a cache-bust. `emitControllerArtifacts` runs once per build process today, so it\n * changes nothing now — it is here because these are SECURITY gates, and the failure mode if a\n * watch mode is ever added is one that reads metadata from a version of the file that no longer\n * exists and passes it. `parsimony-ladder.md` is explicit that security is not what the ladder\n * trims, and the cost is a query parameter.\n */\nasync function loadEmittedControllers(\n emitted: readonly { modulePath: string; sourceFile: string }[],\n): Promise<{ cls: ControllerClass; sourceFile: string }[]> {\n const out: { cls: ControllerClass; sourceFile: string }[] = []\n for (const { modulePath, sourceFile } of emitted) {\n const url = `${pathToFileURL(modulePath).href}?t=${Date.now()}`\n const mod: Record<string, unknown> = await import(url)\n for (const exported of Object.values(mod)) {\n if (typeof exported !== 'function' || !isControllerClass(exported)) continue\n out.push({ cls: exported as ControllerClass, sourceFile })\n }\n }\n return out\n}\n\n/** A class carrying `@Controller` metadata — narrowed once so both checks read the same shape. */\ntype ControllerClass = (new (...args: never[]) => unknown) & { name: string }\n\nexport async function emitControllerArtifacts(opts: {\n serverDir: string\n distDir: string\n}): Promise<ControllerBuildManifest | null> {\n const controllersDir = resolve(opts.serverDir, CONTROLLER_OUT_DIR)\n const files = findControllerFiles(controllersDir)\n if (files.length === 0) return null\n\n const outDir = resolve(opts.distDir, CONTROLLER_OUT_DIR)\n mkdirSync(outDir, { recursive: true })\n\n const modules: string[] = []\n const emitted: { modulePath: string; sourceFile: string }[] = []\n for (const file of files) {\n // Errors are NOT caught. A controller that fails to compile must fail the BUILD — swallowing it\n // here would ship an app whose routes 404 at runtime with nothing pointing back at the cause,\n // which is the exact failure mode this issue reports (error-handling.md § 2).\n const compiled = await transformControllerSource(readFileSync(file, 'utf-8'), file)\n const code = rewriteLocalImports(compiled, file)\n\n // Mirror the source tree under `dist/controllers` so two files with the same basename in\n // different folders cannot overwrite each other.\n const rel = relative(controllersDir, file).replace(/\\.controller\\.ts$/, '.controller.mjs')\n const outPath = join(outDir, rel)\n mkdirSync(dirname(outPath), { recursive: true })\n writeFileSync(outPath, code)\n modules.push(`${CONTROLLER_OUT_DIR}/${rel.split('\\\\').join('/')}`)\n emitted.push({ modulePath: outPath, sourceFile: file })\n }\n\n // AFTER writing, because reading the prefix means loading the compiled module — and a module that\n // failed to compile has already thrown above. Before the manifest, so a refused build leaves no\n // artifact claiming a route that does not answer.\n const loaded = await loadEmittedControllers(emitted)\n assertControllerPathsReachable(loaded)\n assertControllerAccessDeclared(loaded)\n\n const manifest: ControllerBuildManifest = { version: 1, modules }\n writeFileSync(\n resolve(opts.distDir, CONTROLLER_MANIFEST_FILE),\n JSON.stringify(manifest, null, 2) + '\\n',\n )\n return manifest\n}\n\n/** Human-facing summary for the build log (mirrors the cron/job emitters). */\nexport function describeControllerArtifacts(manifest: ControllerBuildManifest | null): string {\n if (manifest === null) return 'controllers: none'\n const names = manifest.modules.map((m) => basename(m, '.mjs')).join(', ')\n return `controllers: ${manifest.modules.length} compiled (${names})`\n}\n"],"mappings":"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","names":[]}
@@ -2,14 +2,14 @@ import {
2
2
  dispatchCsrfWarn,
3
3
  incomingMessageToWebRequest,
4
4
  sendError
5
- } from "./chunk-ITSNTHWN.js";
5
+ } from "./chunk-JW5UFKQ3.js";
6
6
  import {
7
7
  enforceCsrf
8
8
  } from "./chunk-P6OS7652.js";
9
9
 
10
10
  // src/server/http/controller-dispatch.ts
11
- import { readdirSync } from "fs";
12
- import { join } from "path";
11
+ import { readdirSync } from "node:fs";
12
+ import { join } from "node:path";
13
13
  import { createDecoratorHandler, isControllerClass } from "@theokit/http";
14
14
  var CSRF_PROTECTED_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
15
15
  function findControllerFiles(dir) {
@@ -65,8 +65,8 @@ async function writeControllerResponse(res, response) {
65
65
  const setCookies = response.headers.getSetCookie();
66
66
  if (setCookies.length > 0) res.setHeader("Set-Cookie", setCookies);
67
67
  res.writeHead(response.status, headersBag);
68
- const body = response.body ? await response.text() : "";
69
- res.end(body || void 0);
68
+ const body = response.body ? Buffer.from(await response.arrayBuffer()) : void 0;
69
+ res.end(body !== void 0 && body.length > 0 ? body : void 0);
70
70
  }
71
71
  async function dispatchControllerRequest(args) {
72
72
  const { req, res, csrfMode, disallowed, requestId } = args;
@@ -108,4 +108,4 @@ export {
108
108
  scanControllerModules,
109
109
  dispatchControllerRequest
110
110
  };
111
- //# sourceMappingURL=chunk-3DRALT3J.js.map
111
+ //# sourceMappingURL=chunk-CMCEGW3B.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/http/controller-dispatch.ts"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/server/http/controller-dispatch.ts"],"mappings":"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","names":[]}
@@ -309,8 +309,8 @@ async function writeControllerResponse(res, response) {
309
309
  const setCookies = response.headers.getSetCookie();
310
310
  if (setCookies.length > 0) res.setHeader("Set-Cookie", setCookies);
311
311
  res.writeHead(response.status, headersBag);
312
- const body = response.body ? await response.text() : "";
313
- res.end(body || void 0);
312
+ const body = response.body ? Buffer.from(await response.arrayBuffer()) : void 0;
313
+ res.end(body !== void 0 && body.length > 0 ? body : void 0);
314
314
  }
315
315
  async function dispatchControllerRequest(args) {
316
316
  const { req, res, csrfMode, disallowed, requestId } = args;
@@ -361,4 +361,4 @@ export {
361
361
  scanControllerModules,
362
362
  dispatchControllerRequest
363
363
  };
364
- //# sourceMappingURL=chunk-MWWFXYW3.js.map
364
+ //# sourceMappingURL=chunk-CTRVTBGY.js.map