theokit 0.1.0-alpha.5 → 0.1.0-alpha.8

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 (198) hide show
  1. package/dist/adapters/web-shim.d.ts +21 -2
  2. package/dist/adapters/web-shim.js +30 -10
  3. package/dist/adapters/web-shim.js.map +1 -1
  4. package/dist/adapters/ws-shim.js +35 -23
  5. package/dist/adapters/ws-shim.js.map +1 -1
  6. package/dist/{add-WM3ED75L.js → add-2ZFFGGE4.js} +17 -12
  7. package/dist/add-2ZFFGGE4.js.map +1 -0
  8. package/dist/agent-events-lSIS9uYs.d.ts +76 -0
  9. package/dist/{aws-lambda-ZO4GOYP2.js → aws-lambda-PONVF3UX.js} +16 -13
  10. package/dist/aws-lambda-PONVF3UX.js.map +1 -0
  11. package/dist/broadcast-4SX7R5XE.js +14 -0
  12. package/dist/broadcast-QOQGUNNK.js +16 -0
  13. package/dist/broadcast-QOQGUNNK.js.map +1 -0
  14. package/dist/build-IQQYCA3B.js +571 -0
  15. package/dist/build-IQQYCA3B.js.map +1 -0
  16. package/dist/{bun-7OKHVGC2.js → bun-DITMZMKL.js} +17 -14
  17. package/dist/bun-DITMZMKL.js.map +1 -0
  18. package/dist/{check-JKDIEYLK.js → check-PZR67OY5.js} +10 -5
  19. package/dist/check-PZR67OY5.js.map +1 -0
  20. package/dist/chunk-3Q36SEDK.js +285 -0
  21. package/dist/chunk-3Q36SEDK.js.map +1 -0
  22. package/dist/chunk-4QTKSLTO.js +385 -0
  23. package/dist/chunk-4QTKSLTO.js.map +1 -0
  24. package/dist/chunk-5ODOE6EF.js +129 -0
  25. package/dist/chunk-5ODOE6EF.js.map +1 -0
  26. package/dist/chunk-7BIM27LV.js +126 -0
  27. package/dist/chunk-7BIM27LV.js.map +1 -0
  28. package/dist/chunk-C52GSJPF.js +523 -0
  29. package/dist/chunk-C52GSJPF.js.map +1 -0
  30. package/dist/chunk-DGUM43GV.js +11 -0
  31. package/dist/chunk-DGUM43GV.js.map +1 -0
  32. package/dist/{chunk-6WHMT7FB.js → chunk-FH3DYBAN.js} +2 -2
  33. package/dist/chunk-FH3DYBAN.js.map +1 -0
  34. package/dist/chunk-FRBYJANT.js +2133 -0
  35. package/dist/chunk-FRBYJANT.js.map +1 -0
  36. package/dist/chunk-GQSTRXXB.js +168 -0
  37. package/dist/chunk-GQSTRXXB.js.map +1 -0
  38. package/dist/chunk-GY5Q27BJ.js +107 -0
  39. package/dist/chunk-GY5Q27BJ.js.map +1 -0
  40. package/dist/{chunk-2H6ZSJMK.js → chunk-JZ5WEG3S.js} +4 -6
  41. package/dist/chunk-JZ5WEG3S.js.map +1 -0
  42. package/dist/chunk-KT3MWNZG.js +18 -0
  43. package/dist/chunk-KT3MWNZG.js.map +1 -0
  44. package/dist/chunk-MCP6Y5BA.js +161 -0
  45. package/dist/chunk-MCP6Y5BA.js.map +1 -0
  46. package/dist/chunk-O4BLVPML.js +173 -0
  47. package/dist/chunk-O4BLVPML.js.map +1 -0
  48. package/dist/{chunk-IM6C5SOI.js → chunk-O7335ZGH.js} +12 -14
  49. package/dist/chunk-O7335ZGH.js.map +1 -0
  50. package/dist/chunk-OEB3OIYP.js +2121 -0
  51. package/dist/chunk-OEB3OIYP.js.map +1 -0
  52. package/dist/chunk-PB4GR7EP.js +51 -0
  53. package/dist/chunk-PB4GR7EP.js.map +1 -0
  54. package/dist/chunk-S3JBIFKS.js +202 -0
  55. package/dist/chunk-S3JBIFKS.js.map +1 -0
  56. package/dist/chunk-VRHXOKRP.js +601 -0
  57. package/dist/chunk-VRHXOKRP.js.map +1 -0
  58. package/dist/chunk-WQCPKA2L.js +1535 -0
  59. package/dist/chunk-WQCPKA2L.js.map +1 -0
  60. package/dist/chunk-WZ7CPVQB.js +636 -0
  61. package/dist/chunk-WZ7CPVQB.js.map +1 -0
  62. package/dist/chunk-X2VVCJ4V.js +28 -0
  63. package/dist/chunk-X2VVCJ4V.js.map +1 -0
  64. package/dist/{chunk-BJBRL6MW.js → chunk-XMS5VRIK.js} +2 -5
  65. package/dist/chunk-XMS5VRIK.js.map +1 -0
  66. package/dist/chunk-Y5KE6YFP.js +65 -0
  67. package/dist/chunk-Y5KE6YFP.js.map +1 -0
  68. package/dist/chunk-YLBSSEHX.js +50 -0
  69. package/dist/chunk-YLBSSEHX.js.map +1 -0
  70. package/dist/chunk-ZK5JCE5J.js +2038 -0
  71. package/dist/chunk-ZK5JCE5J.js.map +1 -0
  72. package/dist/cli/index.js +33 -10
  73. package/dist/cli/index.js.map +1 -1
  74. package/dist/client/index.d.ts +6 -2
  75. package/dist/client/index.js +114 -52
  76. package/dist/client/index.js.map +1 -1
  77. package/dist/{cloudflare-LII4TDWG.js → cloudflare-RBANU3SE.js} +21 -19
  78. package/dist/cloudflare-RBANU3SE.js.map +1 -0
  79. package/dist/configure-agent-registry-6YGTJYBC.js +33 -0
  80. package/dist/configure-agent-registry-6YGTJYBC.js.map +1 -0
  81. package/dist/{deno-deploy-HH72FFQ5.js → deno-deploy-J7LI6HNL.js} +16 -13
  82. package/dist/deno-deploy-J7LI6HNL.js.map +1 -0
  83. package/dist/dev-VN6WS3CM.js +84 -0
  84. package/dist/dev-VN6WS3CM.js.map +1 -0
  85. package/dist/devtools/entry.d.ts +5 -0
  86. package/dist/devtools/entry.js +1892 -0
  87. package/dist/devtools/entry.js.map +1 -0
  88. package/dist/dist-UZV7J3YI.js +18637 -0
  89. package/dist/dist-UZV7J3YI.js.map +1 -0
  90. package/dist/{docker-NQ5NM4IW.js → docker-EZDA5FT7.js} +2 -1
  91. package/dist/docker-EZDA5FT7.js.map +1 -0
  92. package/dist/{generate-MYEUR7I7.js → generate-SIO4JSKJ.js} +47 -22
  93. package/dist/generate-SIO4JSKJ.js.map +1 -0
  94. package/dist/index-li83Swxa.d.ts +506 -0
  95. package/dist/index.d.ts +1281 -9
  96. package/dist/index.js +9 -6
  97. package/dist/index.js.map +1 -1
  98. package/dist/{info-QGSURJCC.js → info-B3UOXFUF.js} +10 -7
  99. package/dist/info-B3UOXFUF.js.map +1 -0
  100. package/dist/lib-NST3PNZX.js +781 -0
  101. package/dist/lib-NST3PNZX.js.map +1 -0
  102. package/dist/{netlify-26SFJJPO.js → netlify-IFY6ZGFV.js} +43 -35
  103. package/dist/netlify-IFY6ZGFV.js.map +1 -0
  104. package/dist/node-KWZI3CKV.js +11 -0
  105. package/dist/node-KWZI3CKV.js.map +1 -0
  106. package/dist/proper-lockfile-MVKMQGFK.js +1592 -0
  107. package/dist/proper-lockfile-MVKMQGFK.js.map +1 -0
  108. package/dist/rate-limit-store-BEJnhWdw.d.ts +72 -0
  109. package/dist/react-query/index.js +2 -1
  110. package/dist/registry-BB3UYAOG.js +25 -0
  111. package/dist/registry-BB3UYAOG.js.map +1 -0
  112. package/dist/{routes-A2VOUPII.js → routes-CAGFZKUK.js} +7 -4
  113. package/dist/routes-CAGFZKUK.js.map +1 -0
  114. package/dist/scan-C2BOKLSY.js +10 -0
  115. package/dist/scan-C2BOKLSY.js.map +1 -0
  116. package/dist/schema-Cxq2qqwh.d.ts +232 -0
  117. package/dist/schema-MVB2FVK6.js +29 -0
  118. package/dist/schema-MVB2FVK6.js.map +1 -0
  119. package/dist/server/auth/index.d.ts +374 -0
  120. package/dist/server/auth/index.js +54 -0
  121. package/dist/server/auth/index.js.map +1 -0
  122. package/dist/server/cost/index.d.ts +172 -0
  123. package/dist/server/cost/index.js +12 -0
  124. package/dist/server/cost/index.js.map +1 -0
  125. package/dist/server/cron/index.d.ts +208 -0
  126. package/dist/server/cron/index.js +36 -0
  127. package/dist/server/cron/index.js.map +1 -0
  128. package/dist/server/index.d.ts +1977 -212
  129. package/dist/server/index.js +1995 -252
  130. package/dist/server/index.js.map +1 -1
  131. package/dist/server/jobs/index.d.ts +2 -0
  132. package/dist/server/jobs/index.js +38 -0
  133. package/dist/server/jobs/index.js.map +1 -0
  134. package/dist/services-typed-client-XWYYBWGW.js +46 -0
  135. package/dist/services-typed-client-XWYYBWGW.js.map +1 -0
  136. package/dist/services-typed-client-ZX5JUHH3.js +48 -0
  137. package/dist/services-typed-client-ZX5JUHH3.js.map +1 -0
  138. package/dist/sqlite-vec-54KB4RD3.js +41 -0
  139. package/dist/sqlite-vec-54KB4RD3.js.map +1 -0
  140. package/dist/start-CSLJQXV3.js +609 -0
  141. package/dist/start-CSLJQXV3.js.map +1 -0
  142. package/dist/{static-5VE3U6UW.js → static-BH6JQNM2.js} +21 -25
  143. package/dist/static-BH6JQNM2.js.map +1 -0
  144. package/dist/storage-manager-D5KBXXKB.js +184 -0
  145. package/dist/storage-manager-D5KBXXKB.js.map +1 -0
  146. package/dist/storage-types-DtIVejC1.d.ts +96 -0
  147. package/dist/theo-cloud-3K4DGB3S.js +25 -0
  148. package/dist/theo-cloud-3K4DGB3S.js.map +1 -0
  149. package/dist/upgrade-readiness-OHL36FQL.js +249 -0
  150. package/dist/upgrade-readiness-OHL36FQL.js.map +1 -0
  151. package/dist/{vercel-UYRJLIUO.js → vercel-RHADE5IP.js} +15 -17
  152. package/dist/vercel-RHADE5IP.js.map +1 -0
  153. package/dist/vite-plugin/index.d.ts +26 -2
  154. package/dist/vite-plugin/index.js +12 -4
  155. package/dist/vite-plugin-WA3G45YY.js +27 -0
  156. package/dist/vite-plugin-WA3G45YY.js.map +1 -0
  157. package/package.json +46 -4
  158. package/dist/add-WM3ED75L.js.map +0 -1
  159. package/dist/agent-types-CPKa4aJQ.d.ts +0 -41
  160. package/dist/aws-lambda-ZO4GOYP2.js.map +0 -1
  161. package/dist/build-WZUEDJYM.js +0 -88
  162. package/dist/build-WZUEDJYM.js.map +0 -1
  163. package/dist/bun-7OKHVGC2.js.map +0 -1
  164. package/dist/check-JKDIEYLK.js.map +0 -1
  165. package/dist/chunk-2H6ZSJMK.js.map +0 -1
  166. package/dist/chunk-3FMYMTOZ.js +0 -884
  167. package/dist/chunk-3FMYMTOZ.js.map +0 -1
  168. package/dist/chunk-6WHMT7FB.js.map +0 -1
  169. package/dist/chunk-BJBRL6MW.js.map +0 -1
  170. package/dist/chunk-IM6C5SOI.js.map +0 -1
  171. package/dist/chunk-NXAFLRZX.js +0 -159
  172. package/dist/chunk-NXAFLRZX.js.map +0 -1
  173. package/dist/chunk-RSUGZCW3.js +0 -766
  174. package/dist/chunk-RSUGZCW3.js.map +0 -1
  175. package/dist/chunk-RTLNG5GW.js +0 -758
  176. package/dist/chunk-RTLNG5GW.js.map +0 -1
  177. package/dist/chunk-UB6YKEU6.js +0 -178
  178. package/dist/chunk-UB6YKEU6.js.map +0 -1
  179. package/dist/chunk-YEPCVSSU.js +0 -44
  180. package/dist/chunk-YEPCVSSU.js.map +0 -1
  181. package/dist/chunk-ZJH7C62S.js +0 -1201
  182. package/dist/chunk-ZJH7C62S.js.map +0 -1
  183. package/dist/cloudflare-LII4TDWG.js.map +0 -1
  184. package/dist/deno-deploy-HH72FFQ5.js.map +0 -1
  185. package/dist/dev-GMIWKTOP.js +0 -49
  186. package/dist/dev-GMIWKTOP.js.map +0 -1
  187. package/dist/docker-NQ5NM4IW.js.map +0 -1
  188. package/dist/generate-MYEUR7I7.js.map +0 -1
  189. package/dist/info-QGSURJCC.js.map +0 -1
  190. package/dist/netlify-26SFJJPO.js.map +0 -1
  191. package/dist/rate-limit-C6hHXIj1.d.ts +0 -13
  192. package/dist/routes-A2VOUPII.js.map +0 -1
  193. package/dist/scan-MK37O2RS.js +0 -9
  194. package/dist/start-GWH7GHUC.js +0 -299
  195. package/dist/start-GWH7GHUC.js.map +0 -1
  196. package/dist/static-5VE3U6UW.js.map +0 -1
  197. package/dist/vercel-UYRJLIUO.js.map +0 -1
  198. /package/dist/{scan-MK37O2RS.js.map → broadcast-4SX7R5XE.js.map} +0 -0
@@ -37,12 +37,31 @@ interface ShimContext {
37
37
  /** Resolves to a Web Standard Response once `res.end()` is called. */
38
38
  toResponse: () => Promise<Response>;
39
39
  }
40
+ interface CreateWebShimOptions {
41
+ /**
42
+ * CR-018 fix: how to resolve the client IP from forwarded headers.
43
+ *
44
+ * - `'platform'` (default for `cf-connecting-ip`): trust runtime-injected
45
+ * headers only (`cf-connecting-ip` on Cloudflare, `x-real-ip` on
46
+ * Netlify/Vercel when the platform writes it). Ignores
47
+ * `x-forwarded-for` because clients can spoof it.
48
+ * - `'trusted-proxy'`: read the **rightmost** entry of `x-forwarded-for`.
49
+ * Only safe when the request literally went through a trusted proxy
50
+ * that strips client-set headers and appends the real client IP last.
51
+ * - `'none'`: skip all forwarded-header lookups and report
52
+ * `'0.0.0.0'`. Force this when the adapter has no reliable way to
53
+ * identify the client (rate-limiters then must use a different key).
54
+ *
55
+ * Default: `'platform'`.
56
+ */
57
+ trustedProxy?: 'platform' | 'trusted-proxy' | 'none';
58
+ }
40
59
  /**
41
60
  * Build Node-style req/res objects around a Web Standard Request.
42
61
  * `toResponse()` returns a Promise that resolves when `res.end()` has been
43
62
  * invoked, materializing a Web Standard Response with the accumulated body
44
63
  * + headers + status.
45
64
  */
46
- declare function createWebShim(request: Request): ShimContext;
65
+ declare function createWebShim(request: Request, options?: CreateWebShimOptions): ShimContext;
47
66
 
48
- export { type ShimContext, type ShimRequest, type ShimResponse, createWebShim };
67
+ export { type CreateWebShimOptions, type ShimContext, type ShimRequest, type ShimResponse, createWebShim };
@@ -1,40 +1,60 @@
1
+ import "../chunk-DGUM43GV.js";
2
+
1
3
  // src/adapters/web-shim.ts
2
- function createWebShim(request) {
4
+ function resolveRemoteAddress(headers, policy) {
5
+ if (policy === "none") return "0.0.0.0";
6
+ const cf = headers["cf-connecting-ip"];
7
+ if (cf) return cf;
8
+ if (policy === "trusted-proxy") {
9
+ const xff = headers["x-forwarded-for"];
10
+ if (xff) {
11
+ const parts = xff.split(",").map((p) => p.trim()).filter(Boolean);
12
+ if (parts.length > 0) return parts[parts.length - 1];
13
+ }
14
+ const xri = headers["x-real-ip"];
15
+ if (xri) return xri;
16
+ }
17
+ return "0.0.0.0";
18
+ }
19
+ function createWebShim(request, options) {
3
20
  const url = new URL(request.url);
4
21
  const headers = {};
5
22
  request.headers.forEach((value, key) => {
6
23
  headers[key] = value;
7
24
  });
8
- const bodyChunks = [];
9
- let bodyConsumed = false;
25
+ const pumpState = { consumed: false };
10
26
  const dataListeners = [];
11
27
  const endListeners = [];
12
28
  const errorListeners = [];
13
29
  async function pumpBody() {
14
- if (bodyConsumed) return;
15
- bodyConsumed = true;
30
+ if (pumpState.consumed) return;
31
+ pumpState.consumed = true;
16
32
  if (!request.body) {
17
33
  for (const cb of endListeners) cb();
18
34
  return;
19
35
  }
20
36
  try {
21
37
  const reader = request.body.getReader();
22
- while (true) {
23
- const { done, value } = await reader.read();
24
- if (done) break;
25
- if (value) for (const cb of dataListeners) cb(value);
38
+ let done = false;
39
+ while (!done) {
40
+ const chunk = await reader.read();
41
+ done = chunk.done;
42
+ if (!done && chunk.value) {
43
+ for (const cb of dataListeners) cb(chunk.value);
44
+ }
26
45
  }
27
46
  for (const cb of endListeners) cb();
28
47
  } catch (err) {
29
48
  for (const cb of errorListeners) cb(err);
30
49
  }
31
50
  }
51
+ const trustedProxy = options?.trustedProxy ?? "platform";
32
52
  const req = {
33
53
  method: request.method,
34
54
  url: url.pathname + url.search,
35
55
  headers,
36
56
  socket: {
37
- remoteAddress: headers["cf-connecting-ip"] ?? headers["x-forwarded-for"] ?? headers["x-real-ip"] ?? "0.0.0.0"
57
+ remoteAddress: resolveRemoteAddress(headers, trustedProxy)
38
58
  },
39
59
  on(event, cb) {
40
60
  if (event === "data") dataListeners.push(cb);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/web-shim.ts"],"sourcesContent":["/**\n * Web-to-Node bridge used by adapters whose runtimes provide a `Request`\n * (Web Standard) but where TheoKit's `executeRoute` expects a Node-style\n * IncomingMessage/ServerResponse pair.\n *\n * Used by adapters that target Node-compatible runtimes (Bun, Netlify\n * Functions, AWS Lambda Node). Cloudflare uses an inline version of the same\n * pattern — long-term it should consolidate here too.\n *\n * NOT used by Deno Deploy: Deno's stdlib does not have `Buffer`/`node:http`\n * by default and forcing the shim there bloats the bundle. Deno wiring is\n * tracked separately and will likely require executeRoute to accept Web\n * Standard Request directly.\n */\n\nexport interface ShimRequest {\n method: string\n url: string\n headers: Record<string, string>\n socket: { remoteAddress: string }\n on: (event: 'data' | 'end' | 'error', cb: (chunk?: unknown) => void) => unknown\n}\n\nexport interface ShimResponse {\n statusCode: number\n headersSent: boolean\n writableEnded: boolean\n writeHead: (status: number, headers?: Record<string, string>) => void\n setHeader: (key: string, value: string) => void\n getHeader: (key: string) => string | undefined\n write: (chunk: Uint8Array | string) => boolean\n end: (body?: Uint8Array | string) => void\n}\n\nexport interface ShimContext {\n req: ShimRequest\n res: ShimResponse\n /** Resolves to a Web Standard Response once `res.end()` is called. */\n toResponse: () => Promise<Response>\n}\n\n/**\n * Build Node-style req/res objects around a Web Standard Request.\n * `toResponse()` returns a Promise that resolves when `res.end()` has been\n * invoked, materializing a Web Standard Response with the accumulated body\n * + headers + status.\n */\nexport function createWebShim(request: Request): ShimContext {\n const url = new URL(request.url)\n const headers: Record<string, string> = {}\n request.headers.forEach((value, key) => {\n headers[key] = value\n })\n\n const bodyChunks: Uint8Array[] = []\n let bodyConsumed = false\n\n // Consume the request body once, then dispatch data/end events to whatever\n // listener attaches later (executeRoute's body parser).\n const dataListeners: Array<(chunk: Uint8Array) => void> = []\n const endListeners: Array<() => void> = []\n const errorListeners: Array<(err: unknown) => void> = []\n\n async function pumpBody(): Promise<void> {\n if (bodyConsumed) return\n bodyConsumed = true\n if (!request.body) {\n for (const cb of endListeners) cb()\n return\n }\n try {\n const reader = request.body.getReader()\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n if (value) for (const cb of dataListeners) cb(value)\n }\n for (const cb of endListeners) cb()\n } catch (err) {\n for (const cb of errorListeners) cb(err)\n }\n }\n\n const req: ShimRequest = {\n method: request.method,\n url: url.pathname + url.search,\n headers,\n socket: {\n remoteAddress:\n headers['cf-connecting-ip'] ??\n headers['x-forwarded-for'] ??\n headers['x-real-ip'] ??\n '0.0.0.0',\n },\n on(event, cb) {\n if (event === 'data') dataListeners.push(cb as (c: Uint8Array) => void)\n if (event === 'end') endListeners.push(cb as () => void)\n if (event === 'error') errorListeners.push(cb)\n // Lazily start pumping when end is registered (executeRoute always\n // listens for end before doing anything with the body).\n if (event === 'end') void pumpBody()\n return req\n },\n }\n\n const responseChunks: Uint8Array[] = []\n const responseHeaders: Record<string, string> = {}\n let responseStatus = 200\n let resolveResponse!: (r: Response) => void\n const responsePromise = new Promise<Response>((resolve) => {\n resolveResponse = resolve\n })\n\n function chunkToBytes(chunk: Uint8Array | string): Uint8Array {\n if (typeof chunk === 'string') return new TextEncoder().encode(chunk)\n return chunk\n }\n\n const res: ShimResponse = {\n statusCode: 200,\n headersSent: false,\n writableEnded: false,\n writeHead(status, headers) {\n responseStatus = status\n this.statusCode = status\n if (headers) {\n for (const [k, v] of Object.entries(headers)) {\n responseHeaders[k.toLowerCase()] = v\n }\n }\n this.headersSent = true\n },\n setHeader(key, value) {\n responseHeaders[key.toLowerCase()] = value\n },\n getHeader(key) {\n return responseHeaders[key.toLowerCase()]\n },\n write(chunk) {\n responseChunks.push(chunkToBytes(chunk))\n return true\n },\n end(body) {\n if (body !== undefined) responseChunks.push(chunkToBytes(body))\n this.writableEnded = true\n const totalLength = responseChunks.reduce((n, c) => n + c.length, 0)\n const concat = new Uint8Array(totalLength)\n let offset = 0\n for (const c of responseChunks) {\n concat.set(c, offset)\n offset += c.length\n }\n resolveResponse(\n new Response(concat, {\n status: responseStatus,\n headers: responseHeaders,\n }),\n )\n },\n }\n\n return {\n req,\n res,\n toResponse: () => responsePromise,\n }\n}\n"],"mappings":"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","names":["headers"]}
1
+ {"version":3,"sources":["../../src/adapters/web-shim.ts"],"sourcesContent":["/**\n * Web-to-Node bridge used by adapters whose runtimes provide a `Request`\n * (Web Standard) but where TheoKit's `executeRoute` expects a Node-style\n * IncomingMessage/ServerResponse pair.\n *\n * Used by adapters that target Node-compatible runtimes (Bun, Netlify\n * Functions, AWS Lambda Node). Cloudflare uses an inline version of the same\n * pattern — long-term it should consolidate here too.\n *\n * NOT used by Deno Deploy: Deno's stdlib does not have `Buffer`/`node:http`\n * by default and forcing the shim there bloats the bundle. Deno wiring is\n * tracked separately and will likely require executeRoute to accept Web\n * Standard Request directly.\n */\n\nexport interface ShimRequest {\n method: string\n url: string\n headers: Record<string, string>\n socket: { remoteAddress: string }\n on: (event: 'data' | 'end' | 'error', cb: (chunk?: unknown) => void) => unknown\n}\n\nexport interface ShimResponse {\n statusCode: number\n headersSent: boolean\n writableEnded: boolean\n writeHead: (status: number, headers?: Record<string, string>) => void\n setHeader: (key: string, value: string) => void\n getHeader: (key: string) => string | undefined\n write: (chunk: Uint8Array | string) => boolean\n end: (body?: Uint8Array | string) => void\n}\n\nexport interface ShimContext {\n req: ShimRequest\n res: ShimResponse\n /** Resolves to a Web Standard Response once `res.end()` is called. */\n toResponse: () => Promise<Response>\n}\n\nexport interface CreateWebShimOptions {\n /**\n * CR-018 fix: how to resolve the client IP from forwarded headers.\n *\n * - `'platform'` (default for `cf-connecting-ip`): trust runtime-injected\n * headers only (`cf-connecting-ip` on Cloudflare, `x-real-ip` on\n * Netlify/Vercel when the platform writes it). Ignores\n * `x-forwarded-for` because clients can spoof it.\n * - `'trusted-proxy'`: read the **rightmost** entry of `x-forwarded-for`.\n * Only safe when the request literally went through a trusted proxy\n * that strips client-set headers and appends the real client IP last.\n * - `'none'`: skip all forwarded-header lookups and report\n * `'0.0.0.0'`. Force this when the adapter has no reliable way to\n * identify the client (rate-limiters then must use a different key).\n *\n * Default: `'platform'`.\n */\n trustedProxy?: 'platform' | 'trusted-proxy' | 'none'\n}\n\nfunction resolveRemoteAddress(\n headers: Record<string, string>,\n policy: NonNullable<CreateWebShimOptions['trustedProxy']>,\n): string {\n if (policy === 'none') return '0.0.0.0'\n\n // Runtime-injected headers — these come from the platform itself and\n // cannot be set by the client.\n const cf = headers['cf-connecting-ip']\n if (cf) return cf\n\n if (policy === 'trusted-proxy') {\n // Take the RIGHTMOST entry of x-forwarded-for. The rightmost is the\n // hop nearest the application, which a trusted proxy appended; entries\n // to the left may have been forged by the client.\n const xff = headers['x-forwarded-for']\n if (xff) {\n const parts = xff\n .split(',')\n .map((p) => p.trim())\n .filter(Boolean)\n if (parts.length > 0) return parts[parts.length - 1]\n }\n // x-real-ip is typically platform-set (NGINX/Netlify/Vercel).\n const xri = headers['x-real-ip']\n if (xri) return xri\n }\n\n return '0.0.0.0'\n}\n\n/**\n * Build Node-style req/res objects around a Web Standard Request.\n * `toResponse()` returns a Promise that resolves when `res.end()` has been\n * invoked, materializing a Web Standard Response with the accumulated body\n * + headers + status.\n */\nexport function createWebShim(request: Request, options?: CreateWebShimOptions): ShimContext {\n const url = new URL(request.url)\n const headers: Record<string, string> = {}\n request.headers.forEach((value, key) => {\n headers[key] = value\n })\n\n const pumpState = { consumed: false }\n\n // Consume the request body once, then dispatch data/end events to whatever\n // listener attaches later (executeRoute's body parser).\n const dataListeners: ((chunk: Uint8Array) => void)[] = []\n const endListeners: (() => void)[] = []\n const errorListeners: ((err: unknown) => void)[] = []\n\n async function pumpBody(): Promise<void> {\n if (pumpState.consumed) return\n pumpState.consumed = true\n if (!request.body) {\n for (const cb of endListeners) cb()\n return\n }\n try {\n const reader = request.body.getReader()\n let done = false\n while (!done) {\n const chunk = await reader.read()\n done = chunk.done\n if (!done && chunk.value) {\n for (const cb of dataListeners) cb(chunk.value)\n }\n }\n for (const cb of endListeners) cb()\n } catch (err) {\n for (const cb of errorListeners) cb(err)\n }\n }\n\n const trustedProxy = options?.trustedProxy ?? 'platform'\n\n const req: ShimRequest = {\n method: request.method,\n url: url.pathname + url.search,\n headers,\n socket: {\n remoteAddress: resolveRemoteAddress(headers, trustedProxy),\n },\n on(event, cb) {\n if (event === 'data') dataListeners.push(cb)\n if (event === 'end') endListeners.push(cb)\n if (event === 'error') errorListeners.push(cb)\n // Lazily start pumping when end is registered (executeRoute always\n // listens for end before doing anything with the body).\n if (event === 'end') void pumpBody()\n return req\n },\n }\n\n const responseChunks: Uint8Array[] = []\n const responseHeaders: Record<string, string> = {}\n let responseStatus = 200\n let resolveResponse!: (r: Response) => void\n const responsePromise = new Promise<Response>((resolve) => {\n resolveResponse = resolve\n })\n\n function chunkToBytes(chunk: Uint8Array | string): Uint8Array {\n if (typeof chunk === 'string') return new TextEncoder().encode(chunk)\n return chunk\n }\n\n const res: ShimResponse = {\n statusCode: 200,\n headersSent: false,\n writableEnded: false,\n writeHead(status, headers) {\n responseStatus = status\n this.statusCode = status\n if (headers) {\n for (const [k, v] of Object.entries(headers)) {\n responseHeaders[k.toLowerCase()] = v\n }\n }\n this.headersSent = true\n },\n setHeader(key, value) {\n responseHeaders[key.toLowerCase()] = value\n },\n getHeader(key) {\n return responseHeaders[key.toLowerCase()]\n },\n write(chunk) {\n responseChunks.push(chunkToBytes(chunk))\n return true\n },\n end(body) {\n if (body !== undefined) responseChunks.push(chunkToBytes(body))\n this.writableEnded = true\n const totalLength = responseChunks.reduce((n, c) => n + c.length, 0)\n const concat = new Uint8Array(totalLength)\n let offset = 0\n for (const c of responseChunks) {\n concat.set(c, offset)\n offset += c.length\n }\n resolveResponse(\n new Response(concat, {\n status: responseStatus,\n headers: responseHeaders,\n }),\n )\n },\n }\n\n return {\n req,\n res,\n toResponse: () => responsePromise,\n }\n}\n"],"mappings":"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","names":["headers"]}
@@ -1,19 +1,22 @@
1
+ import "../chunk-DGUM43GV.js";
2
+
1
3
  // src/adapters/ws-shim.ts
2
4
  function createNodeWsBridge(handler) {
3
5
  return {
4
6
  attach(nativeWs) {
5
7
  const wsLike = {
6
- send: (data) => nativeWs.send(data),
7
- close: (code, reason) => nativeWs.close(code, reason),
8
+ send: (data) => {
9
+ nativeWs.send(data);
10
+ },
11
+ close: (code, reason) => {
12
+ nativeWs.close(code, reason);
13
+ },
8
14
  on: (event, cb) => {
9
15
  nativeWs.on(event, cb);
10
16
  }
11
17
  };
12
18
  nativeWs.on("open", () => handler.onOpen?.(wsLike));
13
- nativeWs.on(
14
- "message",
15
- (data) => handler.onMessage?.(wsLike, data)
16
- );
19
+ nativeWs.on("message", (data) => handler.onMessage?.(wsLike, data));
17
20
  nativeWs.on("close", (codeMaybe) => {
18
21
  const code = typeof codeMaybe === "number" ? codeMaybe : void 0;
19
22
  handler.onClose?.(wsLike, code);
@@ -25,8 +28,12 @@ function createNodeWsBridge(handler) {
25
28
  }
26
29
  function bunCtxToWsLike(bunWs) {
27
30
  return {
28
- send: (data) => bunWs.send(data),
29
- close: (code, reason) => bunWs.close(code, reason),
31
+ send: (data) => {
32
+ bunWs.send(data);
33
+ },
34
+ close: (code, reason) => {
35
+ bunWs.close(code, reason);
36
+ },
30
37
  on: () => {
31
38
  }
32
39
  };
@@ -49,8 +56,12 @@ function createDenoWsBridge(handler, denoNs) {
49
56
  handle(request) {
50
57
  const { response, socket } = denoNs.upgradeWebSocket(request);
51
58
  const wsLike = {
52
- send: (data) => socket.send(data),
53
- close: (code, reason) => socket.close(code, reason),
59
+ send: (data) => {
60
+ socket.send(data);
61
+ },
62
+ close: (code, reason) => {
63
+ socket.close(code, reason);
64
+ },
54
65
  on: () => {
55
66
  }
56
67
  };
@@ -63,10 +74,7 @@ function createDenoWsBridge(handler, denoNs) {
63
74
  const ev = e;
64
75
  handler.onClose?.(wsLike, ev.code, ev.reason);
65
76
  });
66
- socket.addEventListener(
67
- "error",
68
- (err) => handler.onError?.(wsLike, err)
69
- );
77
+ socket.addEventListener("error", (err) => handler.onError?.(wsLike, err));
70
78
  return response;
71
79
  }
72
80
  };
@@ -76,17 +84,24 @@ function createCloudflareWsBridge(handler) {
76
84
  handle(_request) {
77
85
  const g = globalThis;
78
86
  if (!g.WebSocketPair) {
79
- throw new Error(
80
- "WebSocketPair is not available \u2014 Cloudflare Workers runtime expected."
81
- );
87
+ throw new Error("WebSocketPair is not available \u2014 Cloudflare Workers runtime expected.");
82
88
  }
83
89
  const pair = new g.WebSocketPair();
84
90
  const client = pair[0];
85
91
  const server = pair[1];
86
92
  server.accept();
87
93
  const wsLike = {
88
- send: (data) => server.send(data),
89
- close: (code, reason) => server.close(code, reason),
94
+ send: (data) => {
95
+ server.send(data);
96
+ },
97
+ close: (code, reason) => {
98
+ server.close(code, reason);
99
+ },
100
+ // Cloudflare's WebSocketPair exposes events via addEventListener;
101
+ // the WebSocketLike `on` contract is for Node-style ws. We leave
102
+ // it as a no-op so user handlers that expect Node-style `on(...)`
103
+ // do not crash — but the events flow through addEventListener
104
+ // below, not through this method.
90
105
  on: () => {
91
106
  }
92
107
  };
@@ -99,10 +114,7 @@ function createCloudflareWsBridge(handler) {
99
114
  const ev = e;
100
115
  handler.onClose?.(wsLike, ev.code, ev.reason);
101
116
  });
102
- server.addEventListener(
103
- "error",
104
- (err) => handler.onError?.(wsLike, err)
105
- );
117
+ server.addEventListener("error", (err) => handler.onError?.(wsLike, err));
106
118
  try {
107
119
  return new Response(null, {
108
120
  status: 101,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/ws-shim.ts"],"sourcesContent":["/**\n * T3.1 — WebSocket cross-runtime bridges.\n *\n * Exports 4 bridges (Node `ws` package, Bun.serve, Deno.upgradeWebSocket,\n * Cloudflare WebSocketPair) that adapt each runtime's native WebSocket API\n * to a common `WebSocketLike` interface that user handlers (`defineWebSocket`)\n * can consume uniformly.\n */\n\nexport interface WebSocketLike {\n send(data: string | Uint8Array | ArrayBuffer): void\n close(code?: number, reason?: string): void\n on(\n event: 'message' | 'close' | 'error',\n cb: (data?: unknown) => void,\n ): void\n}\n\nexport interface WsHandler {\n onOpen?: (ws: WebSocketLike) => void\n onMessage?: (ws: WebSocketLike, data: unknown) => void\n onClose?: (ws: WebSocketLike, code?: number, reason?: string) => void\n onError?: (ws: WebSocketLike, err: unknown) => void\n}\n\n// ============================================================\n// Node bridge — wraps `ws` package WebSocket\n// ============================================================\n\ninterface NodeWsLike {\n on(event: string, cb: (arg?: unknown) => void): unknown\n send(data: unknown): void\n close(code?: number, reason?: string): void\n}\n\nexport function createNodeWsBridge(handler: WsHandler): {\n attach(nativeWs: NodeWsLike): WebSocketLike\n} {\n return {\n attach(nativeWs: NodeWsLike): WebSocketLike {\n const wsLike: WebSocketLike = {\n send: (data) => nativeWs.send(data),\n close: (code, reason) => nativeWs.close(code, reason),\n on: (event, cb) => {\n nativeWs.on(event, cb)\n },\n }\n nativeWs.on('open', () => handler.onOpen?.(wsLike))\n nativeWs.on('message', (data) =>\n handler.onMessage?.(wsLike, data),\n )\n nativeWs.on('close', (codeMaybe) => {\n const code = typeof codeMaybe === 'number' ? codeMaybe : undefined\n handler.onClose?.(wsLike, code)\n })\n nativeWs.on('error', (err) => handler.onError?.(wsLike, err))\n return wsLike\n },\n }\n}\n\n// ============================================================\n// Bun bridge — returns config for Bun.serve({ websocket })\n// ============================================================\n\ninterface BunWsContext {\n send(data: unknown): void\n close(code?: number, reason?: string): void\n}\n\nexport interface BunWebSocketConfig {\n open: (ws: BunWsContext) => void\n message: (ws: BunWsContext, message: unknown) => void\n close: (ws: BunWsContext, code?: number, reason?: string) => void\n}\n\nfunction bunCtxToWsLike(bunWs: BunWsContext): WebSocketLike {\n return {\n send: (data) => bunWs.send(data),\n close: (code, reason) => bunWs.close(code, reason),\n on: () => {\n // Bun pushes events via the config handlers — no per-instance on()\n },\n }\n}\n\nexport function createBunWsBridge(handler: WsHandler): BunWebSocketConfig {\n return {\n open(bunWs) {\n handler.onOpen?.(bunCtxToWsLike(bunWs))\n },\n message(bunWs, msg) {\n handler.onMessage?.(bunCtxToWsLike(bunWs), msg)\n },\n close(bunWs, code, reason) {\n handler.onClose?.(bunCtxToWsLike(bunWs), code, reason)\n },\n }\n}\n\n// ============================================================\n// Deno bridge — uses Deno.upgradeWebSocket(request)\n// ============================================================\n\ninterface DenoLike {\n upgradeWebSocket(req: Request): {\n response: Response\n socket: {\n send(data: unknown): void\n close(code?: number, reason?: string): void\n addEventListener(event: string, cb: (e: unknown) => void): void\n }\n }\n}\n\nexport function createDenoWsBridge(\n handler: WsHandler,\n denoNs: DenoLike,\n): { handle(request: Request): Response } {\n return {\n handle(request: Request): Response {\n const { response, socket } = denoNs.upgradeWebSocket(request)\n const wsLike: WebSocketLike = {\n send: (data) => socket.send(data),\n close: (code, reason) => socket.close(code, reason),\n on: () => {\n // Deno uses addEventListener — adapter dispatches below\n },\n }\n socket.addEventListener('open', () => handler.onOpen?.(wsLike))\n socket.addEventListener('message', (e) => {\n const data = (e as { data?: unknown }).data\n handler.onMessage?.(wsLike, data)\n })\n socket.addEventListener('close', (e) => {\n const ev = e as { code?: number; reason?: string }\n handler.onClose?.(wsLike, ev.code, ev.reason)\n })\n socket.addEventListener('error', (err) =>\n handler.onError?.(wsLike, err),\n )\n return response\n },\n }\n}\n\n// ============================================================\n// Cloudflare bridge — uses globalThis.WebSocketPair\n// ============================================================\n\ninterface CfServerWs {\n accept(): void\n addEventListener(event: string, cb: (e: unknown) => void): void\n send(data: unknown): void\n close(code?: number, reason?: string): void\n}\n\nexport function createCloudflareWsBridge(handler: WsHandler): {\n handle(request: Request): Response\n} {\n return {\n handle(_request: Request): Response {\n const g = globalThis as {\n WebSocketPair?: new () => { 0: unknown; 1: CfServerWs }\n }\n if (!g.WebSocketPair) {\n throw new Error(\n 'WebSocketPair is not available — Cloudflare Workers runtime expected.',\n )\n }\n const pair = new g.WebSocketPair()\n const client = pair[0]\n const server = pair[1]\n server.accept()\n const wsLike: WebSocketLike = {\n send: (data) => server.send(data),\n close: (code, reason) => server.close(code, reason),\n on: () => {},\n }\n server.addEventListener('open', () => handler.onOpen?.(wsLike))\n server.addEventListener('message', (e) => {\n const data = (e as { data?: unknown }).data\n handler.onMessage?.(wsLike, data)\n })\n server.addEventListener('close', (e) => {\n const ev = e as { code?: number; reason?: string }\n handler.onClose?.(wsLike, ev.code, ev.reason)\n })\n server.addEventListener('error', (err) =>\n handler.onError?.(wsLike, err),\n )\n // CF requires returning Response with status 101 and webSocket pair.\n // Node's Response throws on 101; fall back to a plain object that\n // satisfies the CF runtime's protocol but matches our test shape.\n try {\n return new Response(null, {\n status: 101,\n webSocket: client,\n } as ResponseInit & { webSocket: unknown })\n } catch {\n return { status: 101, webSocket: client } as unknown as Response\n }\n },\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/adapters/ws-shim.ts"],"sourcesContent":["/**\n * T3.1 — WebSocket cross-runtime bridges.\n *\n * Exports 4 bridges (Node `ws` package, Bun.serve, Deno.upgradeWebSocket,\n * Cloudflare WebSocketPair) that adapt each runtime's native WebSocket API\n * to a common `WebSocketLike` interface that user handlers (`defineWebSocket`)\n * can consume uniformly.\n */\n\nexport interface WebSocketLike {\n send(data: string | Uint8Array | ArrayBuffer): void\n close(code?: number, reason?: string): void\n on(event: 'message' | 'close' | 'error', cb: (data?: unknown) => void): void\n}\n\nexport interface WsHandler {\n onOpen?: (ws: WebSocketLike) => void\n onMessage?: (ws: WebSocketLike, data: unknown) => void\n onClose?: (ws: WebSocketLike, code?: number, reason?: string) => void\n onError?: (ws: WebSocketLike, err: unknown) => void\n}\n\n// ============================================================\n// Node bridge — wraps `ws` package WebSocket\n// ============================================================\n\ninterface NodeWsLike {\n on(event: string, cb: (arg?: unknown) => void): unknown\n send(data: unknown): void\n close(code?: number, reason?: string): void\n}\n\nexport function createNodeWsBridge(handler: WsHandler): {\n attach(nativeWs: NodeWsLike): WebSocketLike\n} {\n return {\n attach(nativeWs: NodeWsLike): WebSocketLike {\n const wsLike: WebSocketLike = {\n send: (data) => {\n nativeWs.send(data)\n },\n close: (code, reason) => {\n nativeWs.close(code, reason)\n },\n on: (event, cb) => {\n nativeWs.on(event, cb)\n },\n }\n nativeWs.on('open', () => handler.onOpen?.(wsLike))\n nativeWs.on('message', (data) => handler.onMessage?.(wsLike, data))\n nativeWs.on('close', (codeMaybe) => {\n const code = typeof codeMaybe === 'number' ? codeMaybe : undefined\n handler.onClose?.(wsLike, code)\n })\n nativeWs.on('error', (err) => handler.onError?.(wsLike, err))\n return wsLike\n },\n }\n}\n\n// ============================================================\n// Bun bridge — returns config for Bun.serve({ websocket })\n// ============================================================\n\ninterface BunWsContext {\n send(data: unknown): void\n close(code?: number, reason?: string): void\n}\n\nexport interface BunWebSocketConfig {\n open: (ws: BunWsContext) => void\n message: (ws: BunWsContext, message: unknown) => void\n close: (ws: BunWsContext, code?: number, reason?: string) => void\n}\n\nfunction bunCtxToWsLike(bunWs: BunWsContext): WebSocketLike {\n return {\n send: (data) => {\n bunWs.send(data)\n },\n close: (code, reason) => {\n bunWs.close(code, reason)\n },\n on: () => {\n // Bun pushes events via the config handlers — no per-instance on()\n },\n }\n}\n\nexport function createBunWsBridge(handler: WsHandler): BunWebSocketConfig {\n return {\n open(bunWs) {\n handler.onOpen?.(bunCtxToWsLike(bunWs))\n },\n message(bunWs, msg) {\n handler.onMessage?.(bunCtxToWsLike(bunWs), msg)\n },\n close(bunWs, code, reason) {\n handler.onClose?.(bunCtxToWsLike(bunWs), code, reason)\n },\n }\n}\n\n// ============================================================\n// Deno bridge — uses Deno.upgradeWebSocket(request)\n// ============================================================\n\ninterface DenoLike {\n upgradeWebSocket(req: Request): {\n response: Response\n socket: {\n send(data: unknown): void\n close(code?: number, reason?: string): void\n addEventListener(event: string, cb: (e: unknown) => void): void\n }\n }\n}\n\nexport function createDenoWsBridge(\n handler: WsHandler,\n denoNs: DenoLike,\n): { handle(request: Request): Response } {\n return {\n handle(request: Request): Response {\n const { response, socket } = denoNs.upgradeWebSocket(request)\n const wsLike: WebSocketLike = {\n send: (data) => {\n socket.send(data)\n },\n close: (code, reason) => {\n socket.close(code, reason)\n },\n on: () => {\n // Deno uses addEventListener — adapter dispatches below\n },\n }\n socket.addEventListener('open', () => handler.onOpen?.(wsLike))\n socket.addEventListener('message', (e) => {\n const data = (e as { data?: unknown }).data\n handler.onMessage?.(wsLike, data)\n })\n socket.addEventListener('close', (e) => {\n const ev = e as { code?: number; reason?: string }\n handler.onClose?.(wsLike, ev.code, ev.reason)\n })\n socket.addEventListener('error', (err) => handler.onError?.(wsLike, err))\n return response\n },\n }\n}\n\n// ============================================================\n// Cloudflare bridge — uses globalThis.WebSocketPair\n// ============================================================\n\ninterface CfServerWs {\n accept(): void\n addEventListener(event: string, cb: (e: unknown) => void): void\n send(data: unknown): void\n close(code?: number, reason?: string): void\n}\n\nexport function createCloudflareWsBridge(handler: WsHandler): {\n handle(request: Request): Response\n} {\n return {\n handle(_request: Request): Response {\n const g = globalThis as {\n WebSocketPair?: new () => { 0: unknown; 1: CfServerWs }\n }\n if (!g.WebSocketPair) {\n throw new Error('WebSocketPair is not available — Cloudflare Workers runtime expected.')\n }\n const pair = new g.WebSocketPair()\n const client = pair[0]\n const server = pair[1]\n server.accept()\n const wsLike: WebSocketLike = {\n send: (data) => {\n server.send(data)\n },\n close: (code, reason) => {\n server.close(code, reason)\n },\n // Cloudflare's WebSocketPair exposes events via addEventListener;\n // the WebSocketLike `on` contract is for Node-style ws. We leave\n // it as a no-op so user handlers that expect Node-style `on(...)`\n // do not crash — but the events flow through addEventListener\n // below, not through this method.\n on: (): void => {\n /* intentional no-op on Cloudflare; see comment above */\n },\n }\n server.addEventListener('open', () => handler.onOpen?.(wsLike))\n server.addEventListener('message', (e) => {\n const data = (e as { data?: unknown }).data\n handler.onMessage?.(wsLike, data)\n })\n server.addEventListener('close', (e) => {\n const ev = e as { code?: number; reason?: string }\n handler.onClose?.(wsLike, ev.code, ev.reason)\n })\n server.addEventListener('error', (err) => handler.onError?.(wsLike, err))\n // CF requires returning Response with status 101 and webSocket pair.\n // Node's Response throws on 101; fall back to a plain object that\n // satisfies the CF runtime's protocol but matches our test shape.\n try {\n return new Response(null, {\n status: 101,\n webSocket: client,\n } as ResponseInit & { webSocket: unknown })\n } catch {\n return { status: 101, webSocket: client } as unknown as Response\n }\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import "tsx/esm";
3
+ import "./chunk-KT3MWNZG.js";
3
4
 
4
5
  // src/cli/commands/add.ts
6
+ import { spawn } from "child_process";
5
7
  import { existsSync } from "fs";
6
8
  import { resolve } from "path";
7
- import { spawn } from "child_process";
8
9
  var InvalidPackageNameError = class extends Error {
9
10
  constructor(input, reason) {
10
11
  super(`Invalid package name "${input}": ${reason}`);
@@ -25,10 +26,7 @@ function validatePackageInput(input) {
25
26
  throw new InvalidPackageNameError(input, "must be a non-empty string");
26
27
  }
27
28
  if (!PACKAGE_NAME_REGEX.test(input)) {
28
- throw new InvalidPackageNameError(
29
- input,
30
- "allowed: lowercase letters, digits, hyphens only"
31
- );
29
+ throw new InvalidPackageNameError(input, "allowed: lowercase letters, digits, hyphens only");
32
30
  }
33
31
  return true;
34
32
  }
@@ -66,7 +64,10 @@ function levenshtein(a, b) {
66
64
  const n = b.length;
67
65
  if (m === 0) return n;
68
66
  if (n === 0) return m;
69
- const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
67
+ const dp = Array.from(
68
+ { length: m + 1 },
69
+ () => Array.from({ length: n + 1 }).fill(0)
70
+ );
70
71
  for (let i = 0; i <= m; i++) dp[i][0] = i;
71
72
  for (let j = 0; j <= n; j++) dp[0][j] = j;
72
73
  for (let i = 1; i <= m; i++) {
@@ -89,11 +90,11 @@ function findSuggestion(input, candidates) {
89
90
  async function runAdd(deps) {
90
91
  validatePackageInput(deps.input);
91
92
  const registry = deps.registry ?? KNOWN_PACKAGES;
92
- const entry = registry[deps.input];
93
- if (!entry) {
93
+ if (!Object.hasOwn(registry, deps.input)) {
94
94
  const suggestion = findSuggestion(deps.input, Object.keys(registry));
95
95
  throw new UnknownPackageError(deps.input, suggestion);
96
96
  }
97
+ const entry = registry[deps.input];
97
98
  if (entry.kind === "bundled") {
98
99
  return {
99
100
  packageInstalled: `${deps.input} (bundled inside theokit)`,
@@ -127,10 +128,14 @@ async function runAdd(deps) {
127
128
  };
128
129
  }
129
130
  function defaultSpawnPm(cmd, args, opts) {
130
- return new Promise((resolveProm) => {
131
+ return new Promise((resolve2) => {
131
132
  const proc = spawn(cmd, args, { cwd: opts.cwd, shell: opts.shell, stdio: "inherit" });
132
- proc.on("close", (code) => resolveProm({ ok: code === 0, code: code ?? 1 }));
133
- proc.on("error", () => resolveProm({ ok: false, code: 1 }));
133
+ proc.on("close", (code) => {
134
+ resolve2({ ok: code === 0, code: code ?? 1 });
135
+ });
136
+ proc.on("error", () => {
137
+ resolve2({ ok: false, code: 1 });
138
+ });
134
139
  });
135
140
  }
136
141
  async function addCommand(input) {
@@ -157,4 +162,4 @@ export {
157
162
  runAdd,
158
163
  validatePackageInput
159
164
  };
160
- //# sourceMappingURL=add-WM3ED75L.js.map
165
+ //# sourceMappingURL=add-2ZFFGGE4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/add.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * CLI `theo add` — detects pkg manager + writes manifests under user `cwd`.\n * Build-time tool. No HTTP input.\n */\nimport { spawn } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nexport class InvalidPackageNameError extends Error {\n constructor(input: string, reason: string) {\n super(`Invalid package name \"${input}\": ${reason}`)\n this.name = 'InvalidPackageNameError'\n }\n}\n\nexport class UnknownPackageError extends Error {\n constructor(input: string, suggestion?: string) {\n super(\n suggestion\n ? `Unknown package: ${input}. Did you mean: ${suggestion}?`\n : `Unknown package: ${input}.`,\n )\n this.name = 'UnknownPackageError'\n }\n}\n\n/**\n * EC-4 — strict whitelist of characters allowed in package input.\n * Lowercase ASCII letters, digits, hyphens. Must start with letter/digit.\n * Rejects: shell metacharacters, path traversal, scope syntax, uppercase.\n */\nconst PACKAGE_NAME_REGEX = /^[a-z0-9][a-z0-9-]*$/\n\nexport function validatePackageInput(input: string): true {\n if (typeof input !== 'string' || input.length === 0) {\n throw new InvalidPackageNameError(input, 'must be a non-empty string')\n }\n if (!PACKAGE_NAME_REGEX.test(input)) {\n throw new InvalidPackageNameError(input, 'allowed: lowercase letters, digits, hyphens only')\n }\n return true\n}\n\nexport type PackageManager = 'pnpm' | 'bun' | 'yarn' | 'npm'\n\nexport function detectPackageManager(lockfiles: Record<string, boolean>): PackageManager {\n if (lockfiles['pnpm-lock.yaml']) return 'pnpm'\n if (lockfiles['bun.lockb']) return 'bun'\n if (lockfiles['yarn.lock']) return 'yarn'\n if (lockfiles['package-lock.json']) return 'npm'\n return 'npm'\n}\n\nexport interface PackageRegistryEntry {\n /** 'bundled' = adapter already shipped inside theokit; no npm install needed.\n * 'external' = adapter/plugin lives as a separate npm package. */\n kind: 'bundled' | 'external'\n /** Required when kind === 'external'. */\n npm?: string\n /** Usage hint printed to the user (e.g. `theokit build --target=bun`). */\n usage: string\n}\n\nexport const KNOWN_PACKAGES: Record<string, PackageRegistryEntry> = {\n bun: {\n kind: 'bundled',\n usage: 'theokit build --target=bun',\n },\n deno: {\n kind: 'bundled',\n usage: 'theokit build --target=deno-deploy',\n },\n netlify: {\n kind: 'bundled',\n usage: 'theokit build --target=netlify',\n },\n 'aws-lambda': {\n kind: 'bundled',\n usage: 'theokit build --target=aws-lambda',\n },\n static: {\n kind: 'bundled',\n usage: 'theokit build --target=static',\n },\n}\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length\n const n = b.length\n if (m === 0) return n\n if (n === 0) return m\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array.from<number>({ length: n + 1 }).fill(0),\n )\n for (let i = 0; i <= m; i++) dp[i][0] = i\n for (let j = 0; j <= n; j++) dp[0][j] = j\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost)\n }\n }\n return dp[m][n]\n}\n\nfunction findSuggestion(input: string, candidates: string[]): string | undefined {\n let best: { name: string; d: number } | null = null\n for (const c of candidates) {\n const d = levenshtein(input, c)\n if (best === null || d < best.d) best = { name: c, d }\n }\n if (best && best.d <= 3) return best.name\n return undefined\n}\n\nexport interface RunAddDeps {\n input: string\n cwd: string\n existsSync?: (path: string) => boolean\n spawnPm?: (\n cmd: string,\n args: string[],\n opts: { cwd: string; shell: boolean },\n ) => Promise<{ ok: boolean; code: number }>\n registry?: Record<string, PackageRegistryEntry>\n}\n\nexport interface AddResult {\n packageInstalled: string\n packageManager: PackageManager | 'bundled'\n usage: string\n exitCode: 0\n}\n\nexport async function runAdd(deps: RunAddDeps): Promise<AddResult> {\n validatePackageInput(deps.input)\n\n const registry = deps.registry ?? KNOWN_PACKAGES\n if (!Object.hasOwn(registry, deps.input)) {\n const suggestion = findSuggestion(deps.input, Object.keys(registry))\n throw new UnknownPackageError(deps.input, suggestion)\n }\n const entry = registry[deps.input]\n\n // T6.1 — bundled adapters need no install\n if (entry.kind === 'bundled') {\n return {\n packageInstalled: `${deps.input} (bundled inside theokit)`,\n packageManager: 'bundled',\n usage: entry.usage,\n exitCode: 0,\n }\n }\n\n if (!entry.npm) {\n throw new Error(`External package \"${deps.input}\" missing npm field in registry`)\n }\n\n const existsFn = deps.existsSync ?? ((p: string) => existsSync(p))\n const lockfiles = {\n 'pnpm-lock.yaml': existsFn(resolve(deps.cwd, 'pnpm-lock.yaml')),\n 'bun.lockb': existsFn(resolve(deps.cwd, 'bun.lockb')),\n 'yarn.lock': existsFn(resolve(deps.cwd, 'yarn.lock')),\n 'package-lock.json': existsFn(resolve(deps.cwd, 'package-lock.json')),\n }\n const pm = detectPackageManager(lockfiles)\n\n const args = pm === 'npm' ? ['install', entry.npm] : ['add', entry.npm]\n const spawnFn = deps.spawnPm ?? defaultSpawnPm\n const result = await spawnFn(pm, args, { cwd: deps.cwd, shell: false })\n\n if (!result.ok) {\n throw new Error(`Package manager exited with non-zero exit code ${result.code}`)\n }\n\n return {\n packageInstalled: entry.npm,\n packageManager: pm,\n usage: entry.usage,\n exitCode: 0,\n }\n}\n\nfunction defaultSpawnPm(\n cmd: string,\n args: string[],\n opts: { cwd: string; shell: boolean },\n): Promise<{ ok: boolean; code: number }> {\n return new Promise((resolve) => {\n const proc = spawn(cmd, args, { cwd: opts.cwd, shell: opts.shell, stdio: 'inherit' })\n proc.on('close', (code) => {\n resolve({ ok: code === 0, code: code ?? 1 })\n })\n proc.on('error', () => {\n resolve({ ok: false, code: 1 })\n })\n })\n}\n\nexport async function addCommand(input: string): Promise<void> {\n try {\n const result = await runAdd({ input, cwd: process.cwd() })\n console.log(`\\n ✓ Installed ${result.packageInstalled} via ${result.packageManager}\\n`)\n console.log(` Next: import { /* ... */ } from '${result.packageInstalled}'\\n`)\n } catch (err) {\n console.error(`\\n ✗ ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n"],"mappings":"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","names":["resolve"]}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * core/contracts/agent-events.ts
3
+ *
4
+ * Canonical home for the AgentEvent wire-format contract (T2.2 of
5
+ * architecture-cleanup plan; ADR-0001 v3 invariant #3 exception).
6
+ *
7
+ * Server emits via SSE; client consumes. Discriminated union by `type`.
8
+ * Both `server/agent/*` and `client/*` import from here.
9
+ *
10
+ * Per ADR-0001 v3: `cache → core/contracts`, `client → core/contracts`,
11
+ * `devtools → core/contracts` are LEGAL direct imports (this is the
12
+ * documented exception to the `no-cross-module-deep-import` rule).
13
+ */
14
+ /**
15
+ * Discriminated union for AgentRunError codes (Phase 1 — Production-Readiness #3).
16
+ *
17
+ * Mirrors `@usetheo/sdk`'s `AgentRunErrorCode` without hard-importing the SDK
18
+ * type (D2 decoupling). EC-7 forward-compat: `(string & {})` fallback preserves
19
+ * autocompletion for the known codes while accepting future codes the SDK may
20
+ * introduce — autocomplete works AND TS doesn't reject unknown codes.
21
+ */
22
+ type AgentRunErrorCode = 'auth' | 'rate_limit' | 'quota_exceeded' | 'invalid_model' | 'invalid_request' | 'invalid_input' | 'context_too_large' | 'safety_blocked' | 'provider_unreachable' | 'tool_runtime_error' | 'aborted' | 'unknown' | (string & {});
23
+ interface AgentMessageEvent {
24
+ type: 'message';
25
+ content: string;
26
+ /** Optional id for client-side dedup / animation keys. */
27
+ id?: string;
28
+ }
29
+ interface AgentToolCallEvent {
30
+ type: 'tool_call';
31
+ name: string;
32
+ args: Record<string, unknown>;
33
+ id?: string;
34
+ }
35
+ interface AgentToolResultEvent {
36
+ type: 'tool_result';
37
+ name: string;
38
+ data: unknown;
39
+ id?: string;
40
+ }
41
+ /**
42
+ * Error event emitted via SSE.
43
+ *
44
+ * Phase 1 — Production-Readiness #3:
45
+ * New optional fields `code/provider/retriable/retryAfterMs` discriminate
46
+ * error classes for client-side handling (auth → sign-in CTA; rate_limit →
47
+ * countdown; quota_exceeded → upsell; etc.).
48
+ *
49
+ * Backward compat (D4): all new fields are optional. Existing clients that
50
+ * read only `event.message` keep working unchanged. New clients can safely
51
+ * `switch (event.code)` — `undefined` (legacy server) is a valid case.
52
+ *
53
+ * EC-15 (DOCUMENT): `message` is *trusted* to not contain secrets. SDK's
54
+ * `AgentRunError.message` is propagated verbatim. The SDK's `providerError`
55
+ * is NEVER serialized into this event — quarantined to prevent leakage.
56
+ */
57
+ interface AgentErrorEvent {
58
+ type: 'error';
59
+ message: string;
60
+ id?: string;
61
+ /** Discriminated error class — see AgentRunErrorCode. */
62
+ code?: AgentRunErrorCode;
63
+ /** Provider id (e.g., 'openai', 'anthropic', 'openrouter'). */
64
+ provider?: string;
65
+ /** Whether the SAME request can be retried as-is. */
66
+ retriable?: boolean;
67
+ /** Hint from provider's Retry-After header (milliseconds). Zero is valid (immediate retry). */
68
+ retryAfterMs?: number;
69
+ }
70
+ /**
71
+ * Runtime AgentEvent — discriminated union of the 4 variants emitted by
72
+ * agent endpoints. Server produces; client consumes.
73
+ */
74
+ type AgentEvent = AgentMessageEvent | AgentToolCallEvent | AgentToolResultEvent | AgentErrorEvent;
75
+
76
+ export type { AgentEvent as A, AgentErrorEvent as a, AgentMessageEvent as b, AgentToolCallEvent as c, AgentToolResultEvent as d, AgentRunErrorCode as e };
@@ -2,12 +2,12 @@
2
2
  import "tsx/esm";
3
3
  import {
4
4
  nodeAdapter
5
- } from "./chunk-YEPCVSSU.js";
6
- import "./chunk-RTLNG5GW.js";
7
- import "./chunk-RSUGZCW3.js";
8
- import "./chunk-UB6YKEU6.js";
9
- import "./chunk-NXAFLRZX.js";
10
- import "./chunk-IM6C5SOI.js";
5
+ } from "./chunk-Y5KE6YFP.js";
6
+ import {
7
+ assertServicesUnsupported,
8
+ readManifest
9
+ } from "./chunk-WZ7CPVQB.js";
10
+ import "./chunk-KT3MWNZG.js";
11
11
 
12
12
  // src/adapters/aws-lambda.ts
13
13
  import { mkdirSync, writeFileSync } from "fs";
@@ -137,27 +137,30 @@ function renderAwsLambdaEntry() {
137
137
  ` const { req, res, toResponse } = createWebShim(request)`,
138
138
  ` const requestId = randomUUID()`,
139
139
  ` const method = request.method.toUpperCase()`,
140
- ` await executeRoute(match.route, method, match.params, req, res, loaderCache, serverDir, requestId)`,
140
+ ` await executeRoute({ route: match.route, method, params: match.params, req, res, loadModule: loaderCache, serverDir, requestId })`,
141
141
  ` const response = await toResponse()`,
142
142
  ` return responseToV2Result(response)`,
143
143
  `}`
144
144
  ].join("\n");
145
145
  }
146
- async function buildAwsLambda(config, cwd, deps = {}) {
146
+ async function buildAwsLambda(config, cwd, deps = {}, ctx) {
147
+ assertServicesUnsupported("aws-lambda", readManifest(cwd));
147
148
  const runNodeBuild = deps.runNodeBuild ?? nodeAdapter.build.bind(nodeAdapter);
148
- await runNodeBuild(config, cwd);
149
+ await runNodeBuild(config, cwd, ctx);
149
150
  const outputDir = resolve(cwd, ".theo/aws");
150
151
  const ensureDir = deps.ensureDir ?? ((p) => mkdirSync(p, { recursive: true }));
151
152
  ensureDir(outputDir);
152
153
  const entry = renderAwsLambdaEntry();
153
- const write = deps.writeEntry ?? ((p, c) => writeFileSync(p, c));
154
+ const write = deps.writeEntry ?? ((p, c) => {
155
+ writeFileSync(p, c);
156
+ });
154
157
  write(resolve(outputDir, "handler.mjs"), entry);
155
158
  console.log("\n \u2713 AWS Lambda output \u2192 .theo/aws/handler.mjs\n");
156
159
  }
157
160
  var awsLambdaAdapter = {
158
161
  name: "aws-lambda",
159
- build(config, cwd) {
160
- return buildAwsLambda(config, cwd);
162
+ build(config, cwd, ctx) {
163
+ return buildAwsLambda(config, cwd, {}, ctx);
161
164
  }
162
165
  };
163
166
  export {
@@ -167,4 +170,4 @@ export {
167
170
  renderAwsLambdaEntry,
168
171
  responseToLambdaResultV2
169
172
  };
170
- //# sourceMappingURL=aws-lambda-ZO4GOYP2.js.map
173
+ //# sourceMappingURL=aws-lambda-PONVF3UX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/aws-lambda.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * AWS Lambda adapter. Writes to `cwd/.theo/lambda/`. Build-time tool.\n */\nimport { mkdirSync, writeFileSync } 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 { nodeAdapter } from './node.js'\nimport type { AdapterBuildContext, DeployAdapter } from './types.js'\n\nexport interface AwsLambdaBuildDeps {\n runNodeBuild?: (config: TheoConfig, cwd: string, ctx?: AdapterBuildContext) => Promise<void>\n writeEntry?: (path: string, content: string) => void\n ensureDir?: (path: string) => void\n}\n\nexport interface RequestShape {\n method: string\n path: string\n headers: Record<string, string>\n body?: string\n}\n\nexport interface LambdaResultV2 {\n statusCode: number\n headers: Record<string, string>\n body: string\n isBase64Encoded: boolean\n}\n\nconst BINARY_CONTENT_TYPES = [\n 'application/octet-stream',\n 'application/pdf',\n 'application/zip',\n 'image/',\n 'audio/',\n 'video/',\n]\n\nfunction isBinaryContentType(contentType: string | undefined): boolean {\n if (!contentType) return false\n const ct = contentType.toLowerCase()\n return BINARY_CONTENT_TYPES.some((prefix) => ct.startsWith(prefix))\n}\n\ninterface APIGatewayProxyEventV2 {\n version: string\n requestContext: {\n http: { method: string; path: string }\n }\n headers?: Record<string, string | undefined>\n body?: string\n isBase64Encoded?: boolean\n}\n\nexport function eventV2ToRequestShape(event: APIGatewayProxyEventV2): RequestShape {\n const headers: Record<string, string> = {}\n for (const [k, v] of Object.entries(event.headers ?? {})) {\n if (v !== undefined) headers[k] = v\n }\n let body: string | undefined = event.body\n if (body !== undefined && event.isBase64Encoded) {\n body = Buffer.from(body, 'base64').toString('utf-8')\n }\n return {\n method: event.requestContext.http.method,\n path: event.requestContext.http.path,\n headers,\n body,\n }\n}\n\nexport function responseToLambdaResultV2(\n statusCode: number,\n headers: Record<string, string>,\n body: string,\n): LambdaResultV2 {\n const isBinary = isBinaryContentType(headers['content-type'])\n return {\n statusCode,\n headers,\n body: isBinary ? Buffer.from(body, 'binary').toString('base64') : body,\n isBase64Encoded: isBinary,\n }\n}\n\nexport function renderAwsLambdaEntry(): string {\n return [\n `// Generated by Theo — AWS Lambda Adapter`,\n `// Use with API Gateway HTTP API v2 (default).`,\n ``,\n `import { resolve } from 'node:path'`,\n `import { randomUUID } from 'node:crypto'`,\n `import { scanServerRoutes, matchRoute, executeRoute, createProductionLoader } from 'theokit/server'`,\n `import { createWebShim } from 'theokit/adapters/web-shim'`,\n ``,\n `const cwd = process.cwd()`,\n `const serverDir = resolve(cwd, 'server')`,\n `let routesCache = null`,\n `let loaderCache = null`,\n ``,\n `function eventV2ToRequest(event) {`,\n ` const method = event.requestContext?.http?.method ?? 'GET'`,\n ` const path = event.requestContext?.http?.path ?? '/'`,\n ` const host = event.headers?.host ?? 'lambda'`,\n ` const qs = event.rawQueryString ? '?' + event.rawQueryString : ''`,\n ` const url = 'https://' + host + path + qs`,\n ` const headers = new Headers()`,\n ` for (const [k, v] of Object.entries(event.headers ?? {})) {`,\n ` if (v !== undefined) headers.set(k, String(v))`,\n ` }`,\n ` let body = event.body`,\n ` if (body && event.isBase64Encoded) {`,\n ` body = Buffer.from(body, 'base64')`,\n ` }`,\n ` return new Request(url, {`,\n ` method,`,\n ` headers,`,\n ` body: method === 'GET' || method === 'HEAD' ? undefined : body,`,\n ` })`,\n `}`,\n ``,\n `async function responseToV2Result(response) {`,\n ` const headers = {}`,\n ` response.headers.forEach((v, k) => { headers[k] = v })`,\n ` const ct = headers['content-type'] ?? ''`,\n ` const isBinary = /^application\\\\/(octet-stream|pdf|zip)|^(image|audio|video)\\\\//.test(ct)`,\n ` if (isBinary) {`,\n ` const buf = Buffer.from(await response.arrayBuffer())`,\n ` return {`,\n ` statusCode: response.status,`,\n ` headers,`,\n ` body: buf.toString('base64'),`,\n ` isBase64Encoded: true,`,\n ` }`,\n ` }`,\n ` return {`,\n ` statusCode: response.status,`,\n ` headers,`,\n ` body: await response.text(),`,\n ` isBase64Encoded: false,`,\n ` }`,\n `}`,\n ``,\n `export const handler = async (event) => {`,\n ` const path = event.requestContext?.http?.path ?? '/'`,\n ``,\n ` if (!path.startsWith('/api/')) {`,\n ` return {`,\n ` statusCode: 404,`,\n ` headers: { 'content-type': 'text/plain' },`,\n ` body: 'Static assets must be served from CloudFront or a separate static host.',`,\n ` isBase64Encoded: false,`,\n ` }`,\n ` }`,\n ``,\n ` if (!routesCache) routesCache = scanServerRoutes(serverDir)`,\n ` if (!loaderCache) loaderCache = createProductionLoader()`,\n ``,\n ` const match = matchRoute(path, routesCache)`,\n ` if (!match) {`,\n ` return {`,\n ` statusCode: 404,`,\n ` headers: { 'content-type': 'application/json' },`,\n ` body: JSON.stringify({ error: { code: 'NOT_FOUND' } }),`,\n ` isBase64Encoded: false,`,\n ` }`,\n ` }`,\n ``,\n ` const request = eventV2ToRequest(event)`,\n ` const { req, res, toResponse } = createWebShim(request)`,\n ` const requestId = randomUUID()`,\n ` const method = request.method.toUpperCase()`,\n ` await executeRoute({ route: match.route, method, params: match.params, req, res, loadModule: loaderCache, serverDir, requestId })`,\n ` const response = await toResponse()`,\n ` return responseToV2Result(response)`,\n `}`,\n ].join('\\n')\n}\n\nexport async function buildAwsLambda(\n config: TheoConfig,\n cwd: string,\n deps: AwsLambdaBuildDeps = {},\n ctx?: AdapterBuildContext,\n): Promise<void> {\n // Wave 2 (T2.2) — reject polyglot services on this adapter.\n assertServicesUnsupported('aws-lambda', readManifest(cwd))\n\n const runNodeBuild = deps.runNodeBuild ?? nodeAdapter.build.bind(nodeAdapter)\n await runNodeBuild(config, cwd, ctx)\n\n const outputDir = resolve(cwd, '.theo/aws')\n const ensureDir = deps.ensureDir ?? ((p: string) => mkdirSync(p, { recursive: true }))\n ensureDir(outputDir)\n\n const entry = renderAwsLambdaEntry()\n const write =\n deps.writeEntry ??\n ((p, c) => {\n writeFileSync(p, c)\n })\n write(resolve(outputDir, 'handler.mjs'), entry)\n\n // eslint-disable-next-line no-console -- CLI build progress\n console.log('\\n ✓ AWS Lambda output → .theo/aws/handler.mjs\\n')\n}\n\nexport const awsLambdaAdapter: DeployAdapter = {\n name: 'aws-lambda',\n build(config, cwd, ctx) {\n return buildAwsLambda(config, cwd, {}, ctx)\n },\n}\n"],"mappings":";;;;;;;;;;;;AAGA,SAAS,WAAW,qBAAqB;AACzC,SAAS,eAAe;AA4BxB,IAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,oBAAoB,aAA0C;AACrE,MAAI,CAAC,YAAa,QAAO;AACzB,QAAM,KAAK,YAAY,YAAY;AACnC,SAAO,qBAAqB,KAAK,CAAC,WAAW,GAAG,WAAW,MAAM,CAAC;AACpE;AAYO,SAAS,sBAAsB,OAA6C;AACjF,QAAM,UAAkC,CAAC;AACzC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,WAAW,CAAC,CAAC,GAAG;AACxD,QAAI,MAAM,OAAW,SAAQ,CAAC,IAAI;AAAA,EACpC;AACA,MAAI,OAA2B,MAAM;AACrC,MAAI,SAAS,UAAa,MAAM,iBAAiB;AAC/C,WAAO,OAAO,KAAK,MAAM,QAAQ,EAAE,SAAS,OAAO;AAAA,EACrD;AACA,SAAO;AAAA,IACL,QAAQ,MAAM,eAAe,KAAK;AAAA,IAClC,MAAM,MAAM,eAAe,KAAK;AAAA,IAChC;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,yBACd,YACA,SACA,MACgB;AAChB,QAAM,WAAW,oBAAoB,QAAQ,cAAc,CAAC;AAC5D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,WAAW,OAAO,KAAK,MAAM,QAAQ,EAAE,SAAS,QAAQ,IAAI;AAAA,IAClE,iBAAiB;AAAA,EACnB;AACF;AAEO,SAAS,uBAA+B;AAC7C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,eAAsB,eACpB,QACA,KACA,OAA2B,CAAC,GAC5B,KACe;AAEf,4BAA0B,cAAc,aAAa,GAAG,CAAC;AAEzD,QAAM,eAAe,KAAK,gBAAgB,YAAY,MAAM,KAAK,WAAW;AAC5E,QAAM,aAAa,QAAQ,KAAK,GAAG;AAEnC,QAAM,YAAY,QAAQ,KAAK,WAAW;AAC1C,QAAM,YAAY,KAAK,cAAc,CAAC,MAAc,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACpF,YAAU,SAAS;AAEnB,QAAM,QAAQ,qBAAqB;AACnC,QAAM,QACJ,KAAK,eACJ,CAAC,GAAG,MAAM;AACT,kBAAc,GAAG,CAAC;AAAA,EACpB;AACF,QAAM,QAAQ,WAAW,aAAa,GAAG,KAAK;AAG9C,UAAQ,IAAI,6DAAmD;AACjE;AAEO,IAAM,mBAAkC;AAAA,EAC7C,MAAM;AAAA,EACN,MAAM,QAAQ,KAAK,KAAK;AACtB,WAAO,eAAe,QAAQ,KAAK,CAAC,GAAG,GAAG;AAAA,EAC5C;AACF;","names":[]}
@@ -0,0 +1,14 @@
1
+ import {
2
+ broadcastCsrfWarn,
3
+ broadcastError,
4
+ broadcastRequest,
5
+ broadcastToDevtools
6
+ } from "./chunk-7BIM27LV.js";
7
+ import "./chunk-DGUM43GV.js";
8
+ export {
9
+ broadcastCsrfWarn,
10
+ broadcastError,
11
+ broadcastRequest,
12
+ broadcastToDevtools
13
+ };
14
+ //# sourceMappingURL=broadcast-4SX7R5XE.js.map
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import "tsx/esm";
3
+ import {
4
+ broadcastCsrfWarn,
5
+ broadcastError,
6
+ broadcastRequest,
7
+ broadcastToDevtools
8
+ } from "./chunk-5ODOE6EF.js";
9
+ import "./chunk-KT3MWNZG.js";
10
+ export {
11
+ broadcastCsrfWarn,
12
+ broadcastError,
13
+ broadcastRequest,
14
+ broadcastToDevtools
15
+ };
16
+ //# sourceMappingURL=broadcast-QOQGUNNK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}