devflare 1.0.0-next.31 → 1.0.0-next.33

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 (290) hide show
  1. package/bin/devflare.js +1 -3
  2. package/dist/{account-g99mzrqp.js → account-p9eywryw.js} +27 -16
  3. package/dist/ai-r3zqmxan.js +261 -0
  4. package/dist/{api-2n6dd2ws.js → api-6z01z734.js} +1 -1
  5. package/dist/bridge/client.d.ts.map +1 -1
  6. package/dist/bridge/miniflare.d.ts.map +1 -1
  7. package/dist/bridge/proxy.d.ts.map +1 -1
  8. package/dist/bridge/server.d.ts.map +1 -1
  9. package/dist/bridge/v2/body-streams.d.ts.map +1 -1
  10. package/dist/bridge/v2/codec.d.ts.map +1 -1
  11. package/dist/bridge/v2/frames.d.ts.map +1 -1
  12. package/dist/bridge/v2/value-codec.d.ts.map +1 -1
  13. package/dist/bridge/v2/value-serialization.d.ts.map +1 -1
  14. package/dist/bridge/v2/wire.d.ts.map +1 -1
  15. package/dist/bridge/v2/ws-relay.d.ts +1 -1
  16. package/dist/bridge/v2/ws-relay.d.ts.map +1 -1
  17. package/dist/browser-shim/handler.d.ts +1 -1
  18. package/dist/browser-shim/handler.d.ts.map +1 -1
  19. package/dist/browser-shim/server.d.ts.map +1 -1
  20. package/dist/browser.d.ts.map +1 -1
  21. package/dist/browser.js +5 -5
  22. package/dist/{build-4tyjywn9.js → build-8bn7frg8.js} +16 -16
  23. package/dist/bundler/do-bundler.d.ts.map +1 -1
  24. package/dist/bundler/rolldown-shared.d.ts.map +1 -1
  25. package/dist/bundler/worker-bundler.d.ts.map +1 -1
  26. package/dist/bundler/worker-compat.d.ts.map +1 -1
  27. package/dist/cli/build-manifest.d.ts.map +1 -1
  28. package/dist/cli/command-utils.d.ts.map +1 -1
  29. package/dist/cli/commands/account.d.ts +1 -1
  30. package/dist/cli/commands/account.d.ts.map +1 -1
  31. package/dist/cli/commands/ai.d.ts.map +1 -1
  32. package/dist/cli/commands/build-artifacts.d.ts +3 -3
  33. package/dist/cli/commands/build-artifacts.d.ts.map +1 -1
  34. package/dist/cli/commands/build.d.ts +2 -2
  35. package/dist/cli/commands/build.d.ts.map +1 -1
  36. package/dist/cli/commands/config.d.ts +2 -2
  37. package/dist/cli/commands/config.d.ts.map +1 -1
  38. package/dist/cli/commands/deploy.d.ts.map +1 -1
  39. package/dist/cli/commands/dev.d.ts +1 -1
  40. package/dist/cli/commands/dev.d.ts.map +1 -1
  41. package/dist/cli/commands/doctor.d.ts +2 -2
  42. package/dist/cli/commands/doctor.d.ts.map +1 -1
  43. package/dist/cli/commands/init.d.ts +2 -2
  44. package/dist/cli/commands/init.d.ts.map +1 -1
  45. package/dist/cli/commands/login.d.ts +1 -1
  46. package/dist/cli/commands/login.d.ts.map +1 -1
  47. package/dist/cli/commands/previews-support/cleanup.d.ts.map +1 -1
  48. package/dist/cli/commands/previews-support/family.d.ts.map +1 -1
  49. package/dist/cli/commands/previews-support/render.d.ts +1 -1
  50. package/dist/cli/commands/previews-support/render.d.ts.map +1 -1
  51. package/dist/cli/commands/previews-support/theme.d.ts +1 -1
  52. package/dist/cli/commands/previews-support/theme.d.ts.map +1 -1
  53. package/dist/cli/commands/previews-support/types.d.ts +1 -1
  54. package/dist/cli/commands/previews-support/types.d.ts.map +1 -1
  55. package/dist/cli/commands/previews.d.ts.map +1 -1
  56. package/dist/cli/commands/productions.d.ts +1 -1
  57. package/dist/cli/commands/productions.d.ts.map +1 -1
  58. package/dist/cli/commands/remote.d.ts +1 -1
  59. package/dist/cli/commands/remote.d.ts.map +1 -1
  60. package/dist/cli/commands/secrets.d.ts.map +1 -1
  61. package/dist/cli/commands/token.d.ts +2 -2
  62. package/dist/cli/commands/token.d.ts.map +1 -1
  63. package/dist/cli/commands/type-generation/discovery.d.ts.map +1 -1
  64. package/dist/cli/commands/type-generation/generator.d.ts.map +1 -1
  65. package/dist/cli/commands/types.d.ts +2 -2
  66. package/dist/cli/commands/types.d.ts.map +1 -1
  67. package/dist/cli/commands/worker.d.ts +2 -2
  68. package/dist/cli/commands/worker.d.ts.map +1 -1
  69. package/dist/cli/dependencies.d.ts +1 -1
  70. package/dist/cli/dependencies.d.ts.map +1 -1
  71. package/dist/cli/deploy-strategy.d.ts.map +1 -1
  72. package/dist/cli/deploy-target.d.ts.map +1 -1
  73. package/dist/cli/generated-artifacts.d.ts.map +1 -1
  74. package/dist/cli/help-pages/pages/account.d.ts.map +1 -1
  75. package/dist/cli/help-pages/pages/core.d.ts.map +1 -1
  76. package/dist/cli/help-pages/pages/misc.d.ts.map +1 -1
  77. package/dist/cli/help-pages/pages/previews.d.ts.map +1 -1
  78. package/dist/cli/help-pages/pages/productions.d.ts.map +1 -1
  79. package/dist/cli/help-pages/render.d.ts +1 -1
  80. package/dist/cli/help-pages/render.d.ts.map +1 -1
  81. package/dist/cli/help-pages/shared.d.ts.map +1 -1
  82. package/dist/cli/help.d.ts.map +1 -1
  83. package/dist/cli/index.d.ts.map +1 -1
  84. package/dist/cli/index.js +2 -2
  85. package/dist/cli/preview-bindings.d.ts.map +1 -1
  86. package/dist/cli/preview.d.ts.map +1 -1
  87. package/dist/cli/ui.d.ts.map +1 -1
  88. package/dist/cli/workspace-build-guard.d.ts.map +1 -1
  89. package/dist/cloudflare/account-core.d.ts.map +1 -1
  90. package/dist/cloudflare/account-resources.d.ts.map +1 -1
  91. package/dist/cloudflare/account-status.d.ts.map +1 -1
  92. package/dist/cloudflare/account-workers.d.ts.map +1 -1
  93. package/dist/cloudflare/api.d.ts.map +1 -1
  94. package/dist/cloudflare/auth.d.ts.map +1 -1
  95. package/dist/cloudflare/index.d.ts +6 -6
  96. package/dist/cloudflare/index.d.ts.map +1 -1
  97. package/dist/cloudflare/index.js +6 -6
  98. package/dist/cloudflare/kv-namespace.d.ts.map +1 -1
  99. package/dist/cloudflare/preferences.d.ts.map +1 -1
  100. package/dist/cloudflare/preview-registry-cache.d.ts.map +1 -1
  101. package/dist/cloudflare/preview-registry-inference.d.ts +1 -1
  102. package/dist/cloudflare/preview-registry-inference.d.ts.map +1 -1
  103. package/dist/cloudflare/preview-registry-shape.d.ts +1 -1
  104. package/dist/cloudflare/preview-registry-shape.d.ts.map +1 -1
  105. package/dist/cloudflare/preview-registry-types.d.ts +1 -1
  106. package/dist/cloudflare/preview-registry-types.d.ts.map +1 -1
  107. package/dist/cloudflare/preview-registry.d.ts +1 -1
  108. package/dist/cloudflare/preview-registry.d.ts.map +1 -1
  109. package/dist/cloudflare/preview-urls.d.ts.map +1 -1
  110. package/dist/cloudflare/registry-schema.d.ts.map +1 -1
  111. package/dist/cloudflare/remote-config.d.ts.map +1 -1
  112. package/dist/cloudflare/tokens.d.ts.map +1 -1
  113. package/dist/cloudflare/usage.d.ts.map +1 -1
  114. package/dist/config/binding-resolution-helpers.d.ts.map +1 -1
  115. package/dist/config/define.d.ts +1 -1
  116. package/dist/config/define.d.ts.map +1 -1
  117. package/dist/config/deploy-resources.d.ts +2 -2
  118. package/dist/config/deploy-resources.d.ts.map +1 -1
  119. package/dist/config/env-vars.d.ts.map +1 -1
  120. package/dist/config/framework-providers.d.ts.map +1 -1
  121. package/dist/config/loader.d.ts.map +1 -1
  122. package/dist/config/preview-resources.d.ts.map +1 -1
  123. package/dist/config/preview.d.ts +1 -3
  124. package/dist/config/preview.d.ts.map +1 -1
  125. package/dist/config/ref.d.ts +1 -1
  126. package/dist/config/ref.d.ts.map +1 -1
  127. package/dist/config/resolve-phased.d.ts.map +1 -1
  128. package/dist/config/resource-resolution.d.ts +3 -3
  129. package/dist/config/resource-resolution.d.ts.map +1 -1
  130. package/dist/config/schema-bindings.d.ts.map +1 -1
  131. package/dist/config/schema-build.d.ts.map +1 -1
  132. package/dist/config/schema-env.d.ts.map +1 -1
  133. package/dist/config/schema-normalization.d.ts +1 -1
  134. package/dist/config/schema-normalization.d.ts.map +1 -1
  135. package/dist/config/schema-runtime.d.ts.map +1 -1
  136. package/dist/config/schema-types-bindings-resources.d.ts +1 -2
  137. package/dist/config/schema-types-bindings-resources.d.ts.map +1 -1
  138. package/dist/config/schema-types-bindings.d.ts +1 -1
  139. package/dist/config/schema-types-bindings.d.ts.map +1 -1
  140. package/dist/config/schema-types-runtime.d.ts +2 -2
  141. package/dist/config/schema-types-runtime.d.ts.map +1 -1
  142. package/dist/config/schema-types.d.ts +1 -1
  143. package/dist/config/schema-types.d.ts.map +1 -1
  144. package/dist/config/schema.d.ts.map +1 -1
  145. package/dist/config/service-bindings-validation.d.ts.map +1 -1
  146. package/dist/config-entry.js +2 -2
  147. package/dist/{config-88sdhx3w.js → config-gphd6wx7.js} +6 -6
  148. package/dist/decorators/durable-object.d.ts.map +1 -1
  149. package/dist/decorators/index.js +1 -1
  150. package/dist/{deploy-8fpav2cw.js → deploy-vpcp41fw.js} +19 -19
  151. package/dist/{dev-nxevkfdv.js → dev-9scy9gap.js} +251 -255
  152. package/dist/dev-server/d1-migrations.d.ts.map +1 -1
  153. package/dist/dev-server/dev-server-state.d.ts +2 -2
  154. package/dist/dev-server/dev-server-state.d.ts.map +1 -1
  155. package/dist/dev-server/gateway-script.d.ts.map +1 -1
  156. package/dist/dev-server/miniflare-bindings.d.ts.map +1 -1
  157. package/dist/dev-server/miniflare-dev-config.d.ts +2 -2
  158. package/dist/dev-server/miniflare-dev-config.d.ts.map +1 -1
  159. package/dist/dev-server/miniflare-log.d.ts.map +1 -1
  160. package/dist/dev-server/miniflare-worker-config.d.ts +1 -1
  161. package/dist/dev-server/miniflare-worker-config.d.ts.map +1 -1
  162. package/dist/dev-server/server-startup-helpers.d.ts +2 -2
  163. package/dist/dev-server/server-startup-helpers.d.ts.map +1 -1
  164. package/dist/dev-server/server.d.ts.map +1 -1
  165. package/dist/dev-server/vite-process.d.ts.map +1 -1
  166. package/dist/dev-server/vite-utils.d.ts.map +1 -1
  167. package/dist/dev-server/worker-source-watcher.d.ts.map +1 -1
  168. package/dist/dev-server/worker-surface-paths.d.ts +1 -1
  169. package/dist/dev-server/worker-surface-paths.d.ts.map +1 -1
  170. package/dist/{doctor-ztwx1y95.js → doctor-swfeeqny.js} +12 -12
  171. package/dist/{durable-object-v3gsnybk.js → durable-object-pf6gabpn.js} +1 -1
  172. package/dist/env.d.ts.map +1 -1
  173. package/dist/{index-mg8vwqxf.js → index-04wh9bxx.js} +2 -2
  174. package/dist/{index-fcmj6641.js → index-13sfensw.js} +1 -1
  175. package/dist/{index-6sj0v0dk.js → index-1trss579.js} +1373 -1358
  176. package/dist/{index-3jme4hgw.js → index-2xz4etye.js} +25 -33
  177. package/dist/{index-q8f4kawk.js → index-34dejneh.js} +12 -2
  178. package/dist/{index-rzp9ejrh.js → index-3t0e51a0.js} +1 -1
  179. package/dist/{index-ptwem1nt.js → index-3z20d3kd.js} +160 -208
  180. package/dist/{index-d8bdkx2h.js → index-4xmtkg9g.js} +2 -2
  181. package/dist/{index-qwgr4q7s.js → index-5dkjffqz.js} +2 -14
  182. package/dist/{index-aqrwyy57.js → index-7m557715.js} +2 -7
  183. package/dist/{index-85yjb686.js → index-97rfy7n2.js} +1 -1
  184. package/dist/{index-11p28kxx.js → index-atwc3csp.js} +1 -1
  185. package/dist/{index-ft3trvra.js → index-b20xx2vk.js} +5 -19
  186. package/dist/{index-s3km3y34.js → index-bhxj7ff6.js} +234 -219
  187. package/dist/{index-3edvz3hs.js → index-by27mzra.js} +4 -4
  188. package/dist/{index-t9y0sve5.js → index-c4zd39tc.js} +1 -1
  189. package/dist/{index-69c0ed4a.js → index-dem45895.js} +6 -11
  190. package/dist/{index-k9x5v335.js → index-dgx495pv.js} +30 -27
  191. package/dist/{index-7qd7nmdd.js → index-e7xn30bh.js} +2 -2
  192. package/dist/{index-1d4jg11n.js → index-h7r11y4a.js} +5 -8
  193. package/dist/{index-vez803pp.js → index-ksqth51q.js} +2 -2
  194. package/dist/{index-stgn34cr.js → index-maw0jjnn.js} +0 -1
  195. package/dist/{index-hpwa6vsw.js → index-n4xkaymm.js} +1 -4
  196. package/dist/{index-dfcp9hsd.js → index-pd97hk2j.js} +64 -72
  197. package/dist/{index-67gr49zr.js → index-pmnb7eke.js} +3 -3
  198. package/dist/{index-xz18tstn.js → index-t0r7evpw.js} +9 -9
  199. package/dist/{index-qz6nbwpw.js → index-t23wq5js.js} +1 -1
  200. package/dist/{index-hjjfxv02.js → index-w8c8tjc5.js} +1 -1
  201. package/dist/{index-c8p4njqy.js → index-wxdcrzcp.js} +1 -5
  202. package/dist/{index-15fpa5tx.js → index-x0wpeb3x.js} +2 -2
  203. package/dist/{index-qmmqww7h.js → index-x2fd1361.js} +5 -5
  204. package/dist/{index-a855bdsx.js → index-y7w3x9p1.js} +1 -1
  205. package/dist/{index-gn5wy09x.js → index-zdd0d9qa.js} +3 -9
  206. package/dist/index.js +12 -12
  207. package/dist/{init-cwpergap.js → init-tb8jqgvr.js} +2 -2
  208. package/dist/{login-r5ve73kf.js → login-7vyzeqy4.js} +10 -10
  209. package/dist/{previews-qjj98rpp.js → previews-jfbectpc.js} +25 -22
  210. package/dist/{productions-gzdtge0e.js → productions-49ct4kjm.js} +21 -18
  211. package/dist/{remote-q59qk463.js → remote-6876k6mz.js} +3 -3
  212. package/dist/runtime/context-events.d.ts.map +1 -1
  213. package/dist/runtime/context-types.d.ts.map +1 -1
  214. package/dist/runtime/context.d.ts.map +1 -1
  215. package/dist/runtime/exports.d.ts +21 -21
  216. package/dist/runtime/exports.d.ts.map +1 -1
  217. package/dist/runtime/index.js +5 -5
  218. package/dist/runtime/middleware.d.ts.map +1 -1
  219. package/dist/runtime/router/index.d.ts +1 -1
  220. package/dist/runtime/router/index.d.ts.map +1 -1
  221. package/dist/runtime/router/types.d.ts.map +1 -1
  222. package/dist/secrets/local-secrets.d.ts.map +1 -1
  223. package/dist/{secrets-4w5166km.js → secrets-9ne7armr.js} +7 -7
  224. package/dist/shims/local-hyperdrive.d.ts.map +1 -1
  225. package/dist/shims/local-media-bindings.d.ts.map +1 -1
  226. package/dist/shims/local-worker-loader.d.ts.map +1 -1
  227. package/dist/sveltekit/index.d.ts.map +1 -1
  228. package/dist/sveltekit/index.js +12 -15
  229. package/dist/sveltekit/local-bindings.d.ts.map +1 -1
  230. package/dist/sveltekit/platform.d.ts.map +1 -1
  231. package/dist/test/binding-hints.d.ts +1 -1
  232. package/dist/test/binding-hints.d.ts.map +1 -1
  233. package/dist/test/cf.d.ts +1 -1
  234. package/dist/test/containers.d.ts.map +1 -1
  235. package/dist/test/email.d.ts +4 -4
  236. package/dist/test/email.d.ts.map +1 -1
  237. package/dist/test/index.js +1404 -1402
  238. package/dist/test/offline-bindings.d.ts.map +1 -1
  239. package/dist/test/queue.d.ts.map +1 -1
  240. package/dist/test/remote-ai.d.ts.map +1 -1
  241. package/dist/test/remote-cloudflare.d.ts.map +1 -1
  242. package/dist/test/remote-vectorize.d.ts.map +1 -1
  243. package/dist/test/resolve-service-bindings.d.ts.map +1 -1
  244. package/dist/test/scheduled.d.ts.map +1 -1
  245. package/dist/test/simple-context-bindings.d.ts.map +1 -1
  246. package/dist/test/simple-context-durable-objects.d.ts.map +1 -1
  247. package/dist/test/simple-context-env.d.ts.map +1 -1
  248. package/dist/test/simple-context-handlers.d.ts.map +1 -1
  249. package/dist/test/simple-context-lifecycle.d.ts +1 -1
  250. package/dist/test/simple-context-lifecycle.d.ts.map +1 -1
  251. package/dist/test/simple-context-mfconfig.d.ts.map +1 -1
  252. package/dist/test/simple-context-multi-worker.d.ts +1 -1
  253. package/dist/test/simple-context-multi-worker.d.ts.map +1 -1
  254. package/dist/test/simple-context-paths.d.ts.map +1 -1
  255. package/dist/test/simple-context-runtime.d.ts.map +1 -1
  256. package/dist/test/simple-context-startup.d.ts.map +1 -1
  257. package/dist/test/simple-context.d.ts.map +1 -1
  258. package/dist/test/tail.d.ts +1 -1
  259. package/dist/test/tail.d.ts.map +1 -1
  260. package/dist/test/utilities/env.d.ts.map +1 -1
  261. package/dist/test/worker.d.ts.map +1 -1
  262. package/dist/{token-35jcdqq8.js → token-yp1qa70y.js} +4 -4
  263. package/dist/transform/durable-object.d.ts.map +1 -1
  264. package/dist/transform/worker-entrypoint.d.ts.map +1 -1
  265. package/dist/{types-6vc02evc.js → types-zekmbebk.js} +16 -18
  266. package/dist/utils/glob.d.ts.map +1 -1
  267. package/dist/utils/resolve-package.d.ts.map +1 -1
  268. package/dist/utils/send-email.d.ts.map +1 -1
  269. package/dist/utils/send-email.js +1 -1
  270. package/dist/vite/config-file.d.ts +1 -1
  271. package/dist/vite/config-file.d.ts.map +1 -1
  272. package/dist/vite/index.js +12 -12
  273. package/dist/vite/plugin-config-hook.d.ts.map +1 -1
  274. package/dist/vite/plugin-context.d.ts.map +1 -1
  275. package/dist/vite/plugin-durable-objects.d.ts +1 -1
  276. package/dist/vite/plugin-durable-objects.d.ts.map +1 -1
  277. package/dist/vite/plugin-programmatic.d.ts.map +1 -1
  278. package/dist/vite/plugin-service-bindings.d.ts.map +1 -1
  279. package/dist/vite/plugin-transform.d.ts.map +1 -1
  280. package/dist/vite/plugin.d.ts.map +1 -1
  281. package/dist/{worker-f2v85f7d.js → worker-706g8qeh.js} +14 -14
  282. package/dist/worker-entry/composed-worker.d.ts.map +1 -1
  283. package/dist/worker-entry/extensions.d.ts.map +1 -1
  284. package/dist/worker-entry/routes.d.ts.map +1 -1
  285. package/dist/worker-entry/surface-paths.d.ts.map +1 -1
  286. package/dist/{worker-entrypoint-3rmzd4c1.js → worker-entrypoint-y8s9kdcx.js} +1 -1
  287. package/dist/workflows/local-workflow-entrypoints.d.ts.map +1 -1
  288. package/package.json +2 -1
  289. package/dist/ai-dx4fr9jh.js +0 -107
  290. package/dist/{index-vhqww6tt.js → index-p93cngdp.js} +1 -1
@@ -1,1228 +1,839 @@
1
1
  import {
2
2
  wrapEnvSendEmailBindings
3
- } from "./index-hpwa6vsw.js";
3
+ } from "./index-n4xkaymm.js";
4
4
 
5
- // src/runtime/validation.ts
6
- class ContextAccessError extends Error {
7
- contextName;
8
- propertyName;
9
- constructor(contextName, propertyName, message) {
10
- super(message ?? `Cannot access ${contextName}.${propertyName} outside of an active Devflare handler trail.
11
-
12
- ` + `This typically happens when:
13
- ` + ` 1. Accessing ${contextName} at module top-level (during import)
14
- ` + ` 2. Accessing ${contextName} in a callback that runs after the handler ends
15
- ` + ` 3. Accessing ${contextName} in a setTimeout/setInterval callback
16
-
17
- ` + `Move the access inside your handler function or middleware.`);
18
- this.name = "ContextAccessError";
19
- this.contextName = contextName;
20
- this.propertyName = propertyName;
21
- }
22
- static contextUnavailable(message) {
23
- return new ContextAccessError("context", "<unavailable>", message ?? `Context not available. Devflare uses AsyncLocalStorage to carry the active event through fetch, queue, scheduled, email, tail, and Durable Object handler call chains.
24
-
25
- ` + `This usually means one of:
26
-
27
- ` + `1. Accessing context at module top-level (runs at cold start, not per-request)
28
- ` + `2. Accessing context in setTimeout/setInterval callbacks
29
- ` + `3. Missing 'nodejs_compat' compatibility flag in your worker config
30
-
31
- ` + `Fix: Move the access inside your handler, middleware, or a helper called from that handler trail.
32
- ` + `Learn more: https://devflare.dev/docs/context-errors`);
33
- }
34
- }
35
- function createContextProxy(getter, name, options = {}) {
36
- const mutable = options.mutable ?? true;
37
- return new Proxy({}, {
38
- get(_target, prop) {
39
- const ctx = getter();
40
- if (ctx === undefined || ctx === null) {
41
- throw new ContextAccessError(name, String(prop));
42
- }
43
- return ctx[prop];
44
- },
45
- set(_target, prop, value) {
46
- if (!mutable) {
47
- throw new TypeError(`Cannot assign to '${String(prop)}' on '${name}' because it is read-only.
48
- ` + `Use 'locals' for mutable request-scoped data.`);
49
- }
50
- const ctx = getter();
51
- if (ctx === undefined || ctx === null) {
52
- throw new ContextAccessError(name, String(prop));
53
- }
54
- ctx[prop] = value;
55
- return true;
56
- },
57
- deleteProperty(_target, prop) {
58
- if (!mutable) {
59
- throw new TypeError(`Cannot delete property '${String(prop)}' from '${name}' because it is read-only.`);
60
- }
61
- const ctx = getter();
62
- if (ctx === undefined || ctx === null) {
63
- return true;
64
- }
65
- return Reflect.deleteProperty(ctx, prop);
66
- },
67
- has(_target, prop) {
68
- const ctx = getter();
69
- if (ctx === undefined || ctx === null) {
70
- return false;
71
- }
72
- return prop in ctx;
73
- },
74
- ownKeys(_target) {
75
- const ctx = getter();
76
- if (ctx === undefined || ctx === null) {
77
- return [];
78
- }
79
- return Reflect.ownKeys(ctx);
80
- },
81
- getOwnPropertyDescriptor(_target, prop) {
82
- const ctx = getter();
83
- if (ctx === undefined || ctx === null) {
84
- return;
85
- }
86
- const descriptor = Reflect.getOwnPropertyDescriptor(ctx, prop);
87
- if (!descriptor) {
88
- return;
89
- }
90
- if (!mutable) {
91
- return { ...descriptor, writable: false };
92
- }
93
- return descriptor;
94
- }
95
- });
96
- }
97
-
98
- // src/runtime/context-events.ts
99
- function createLocals(locals) {
100
- return locals ?? {};
101
- }
102
- function prepareEventShell(env, options = {}) {
103
- return {
104
- env: wrapEnvSendEmailBindings(env),
105
- locals: createLocals(options.locals)
106
- };
107
- }
108
- function createAugmentedTarget(target, extra) {
109
- return new Proxy(target, {
110
- get(target2, prop) {
111
- if (prop in extra) {
112
- return extra[prop];
113
- }
114
- const value = Reflect.get(target2, prop, target2);
115
- return typeof value === "function" ? value.bind(target2) : value;
116
- },
117
- has(target2, prop) {
118
- return prop in extra || prop in target2;
119
- },
120
- ownKeys(target2) {
121
- return Array.from(new Set([
122
- ...Reflect.ownKeys(target2),
123
- ...Reflect.ownKeys(extra)
124
- ]));
125
- },
126
- getOwnPropertyDescriptor(target2, prop) {
127
- if (prop in extra) {
128
- return {
129
- configurable: true,
130
- enumerable: true,
131
- writable: false,
132
- value: extra[prop]
133
- };
134
- }
135
- return Reflect.getOwnPropertyDescriptor(target2, prop);
136
- }
137
- });
138
- }
139
- function createBaseEvent(type, env, ctx, options = {}) {
140
- const shell = prepareEventShell(env, { locals: options.locals });
141
- return {
142
- type,
143
- ctx,
144
- ...shell,
145
- request: options.request ?? null,
146
- params: options.params
147
- };
148
- }
149
- function createFetchEvent(request, env, ctx, options = {}) {
150
- const shell = prepareEventShell(env, { locals: options.locals });
151
- return createAugmentedTarget(request, {
152
- type: "fetch",
153
- ctx,
154
- ...shell,
155
- url: new URL(request.url),
156
- request,
157
- params: options.params ?? {}
158
- });
159
- }
160
- function createQueueEvent(batch, env, ctx, options = {}) {
161
- const shell = prepareEventShell(env, { locals: options.locals });
162
- return createAugmentedTarget(batch, {
163
- type: "queue",
164
- ctx,
165
- ...shell,
166
- batch
167
- });
168
- }
169
- function createScheduledEvent(controller, env, ctx, options = {}) {
170
- const shell = prepareEventShell(env, { locals: options.locals });
171
- return createAugmentedTarget(controller, {
172
- type: "scheduled",
173
- ctx,
174
- ...shell,
175
- controller
176
- });
177
- }
178
- function createEmailEvent(message, env, ctx, options = {}) {
179
- const shell = prepareEventShell(env, { locals: options.locals });
180
- return createAugmentedTarget(message, {
181
- type: "email",
182
- ctx,
183
- ...shell,
184
- message
185
- });
186
- }
187
- function createTailEvent(events, env, ctx, options = {}) {
188
- const shell = prepareEventShell(env, { locals: options.locals });
189
- return createAugmentedTarget(events, {
190
- type: "tail",
191
- ctx,
192
- ...shell,
193
- events
194
- });
195
- }
196
- function createDurableObjectFetchEvent(request, env, state, options = {}) {
197
- const shell = prepareEventShell(env, { locals: options.locals });
198
- return createAugmentedTarget(request, {
199
- type: "durable-object-fetch",
200
- ctx: state,
201
- state,
202
- ...shell,
203
- request
204
- });
205
- }
206
- function createDurableObjectAlarmEvent(env, state, options = {}) {
207
- const shell = prepareEventShell(env, { locals: options.locals });
208
- return {
209
- type: "durable-object-alarm",
210
- ctx: state,
211
- state,
212
- ...shell
213
- };
214
- }
215
- function createDurableObjectWebSocketMessageEvent(ws, message, env, state, options = {}) {
216
- const shell = prepareEventShell(env, { locals: options.locals });
217
- return createAugmentedTarget(ws, {
218
- type: "durable-object-websocket-message",
219
- ctx: state,
220
- state,
221
- ...shell,
222
- ws,
223
- message
224
- });
225
- }
226
- function createDurableObjectWebSocketCloseEvent(ws, code, reason, wasClean, env, state, options = {}) {
227
- const shell = prepareEventShell(env, { locals: options.locals });
228
- return createAugmentedTarget(ws, {
229
- type: "durable-object-websocket-close",
230
- ctx: state,
231
- state,
232
- ...shell,
233
- ws,
234
- code,
235
- reason,
236
- wasClean
237
- });
238
- }
239
- function createDurableObjectWebSocketErrorEvent(ws, error, env, state, options = {}) {
240
- const shell = prepareEventShell(env, { locals: options.locals });
241
- return createAugmentedTarget(ws, {
242
- type: "durable-object-websocket-error",
243
- ctx: state,
244
- state,
245
- ...shell,
246
- ws,
247
- error
248
- });
249
- }
250
- function createDefaultEvent(env, ctx, request, type, locals) {
251
- switch (type) {
252
- case "fetch": {
253
- if (request && ctx) {
254
- return createFetchEvent(request, env, ctx, { locals });
255
- }
256
- return createBaseEvent(type, env, ctx, { locals, request });
257
- }
258
- case "durable-object-fetch": {
259
- if (request && ctx) {
260
- return createDurableObjectFetchEvent(request, env, ctx, { locals });
261
- }
262
- return createBaseEvent(type, env, ctx, { locals, request });
263
- }
264
- case "durable-object-alarm": {
265
- if (ctx) {
266
- return createDurableObjectAlarmEvent(env, ctx, { locals });
267
- }
268
- return createBaseEvent(type, env, ctx, { locals });
269
- }
270
- case "queue":
271
- case "scheduled":
272
- case "email":
273
- case "tail":
274
- case "durable-object-websocket-message":
275
- case "durable-object-websocket-close":
276
- case "durable-object-websocket-error":
277
- return createBaseEvent(type, env, ctx, { locals, request });
278
- default: {
279
- const exhaustive = type;
280
- throw new Error(`createDefaultEvent: unknown event type ${String(exhaustive)}`);
281
- }
282
- }
283
- }
284
-
285
- // src/runtime/context.ts
286
- import { AsyncLocalStorage } from "node:async_hooks";
287
- var storage = new AsyncLocalStorage;
288
- function createLocals2() {
289
- return {};
290
- }
291
- function runWithContext(env, ctx, request, fn, type = "fetch") {
292
- const locals = createLocals2();
293
- const event = createDefaultEvent(env, ctx, request, type, locals);
294
- return runWithEventContext(event, fn);
295
- }
296
- function runWithEventContext(event, fn) {
297
- const context = {
298
- env: event.env,
299
- ctx: event.ctx,
300
- request: event.request ?? null,
301
- locals: event.locals,
302
- type: event.type,
303
- event
304
- };
305
- return storage.run(context, fn);
306
- }
307
- function getContext() {
308
- const context = storage.getStore();
309
- if (!context) {
310
- throw ContextAccessError.contextUnavailable();
311
- }
312
- return context;
313
- }
314
- function getContextOrNull() {
315
- const context = storage.getStore();
316
- return context ?? null;
317
- }
318
- function getEventContext() {
319
- return getContext().event;
320
- }
321
- function getEventContextOrNull() {
322
- return getContextOrNull()?.event ?? null;
323
- }
324
- function hasContext() {
325
- return storage.getStore() !== undefined;
326
- }
327
- function createEventAccessor(name, matcher) {
328
- const accessor = () => {
329
- const currentEvent = getEventContextOrNull();
330
- if (!currentEvent) {
331
- throw ContextAccessError.contextUnavailable();
332
- }
333
- if (!matcher(currentEvent)) {
334
- throw ContextAccessError.contextUnavailable(`${name} is not available in the current '${currentEvent.type}' context.
335
-
336
- Devflare stores event objects in AsyncLocalStorage so helpers called within a handler can reach the active event.
337
- Use ${name}.safe() to return null instead of throwing, or call the getter that matches the active surface.`);
338
- }
339
- return currentEvent;
340
- };
341
- accessor.safe = () => {
342
- const currentEvent = getEventContextOrNull();
343
- return currentEvent && matcher(currentEvent) ? currentEvent : null;
344
- };
345
- return accessor;
346
- }
347
- function isFetchEvent(event) {
348
- return event.type === "fetch" && event.request instanceof Request;
349
- }
350
- function isQueueEvent(event) {
351
- return event.type === "queue" && "batch" in event;
352
- }
353
- function isScheduledEvent(event) {
354
- return event.type === "scheduled" && "controller" in event;
355
- }
356
- function isEmailEvent(event) {
357
- return event.type === "email" && "message" in event;
358
- }
359
- function isTailEvent(event) {
360
- return event.type === "tail" && Array.isArray(event) && "events" in event;
361
- }
362
- function isDurableObjectEvent(event) {
363
- return event.type.startsWith("durable-object-") && "state" in event;
364
- }
365
- function isDurableObjectFetchEvent(event) {
366
- return event.type === "durable-object-fetch" && event.request instanceof Request;
367
- }
368
- function isDurableObjectAlarmEvent(event) {
369
- return event.type === "durable-object-alarm" && "state" in event;
370
- }
371
- function isDurableObjectWebSocketMessageEvent(event) {
372
- return event.type === "durable-object-websocket-message" && "ws" in event && "message" in event;
373
- }
374
- function isDurableObjectWebSocketCloseEvent(event) {
375
- return event.type === "durable-object-websocket-close" && "ws" in event && "code" in event;
376
- }
377
- function isDurableObjectWebSocketErrorEvent(event) {
378
- return event.type === "durable-object-websocket-error" && "ws" in event && "error" in event;
379
- }
380
- var getFetchEvent = createEventAccessor("getFetchEvent()", isFetchEvent);
381
- var getQueueEvent = createEventAccessor("getQueueEvent()", isQueueEvent);
382
- var getScheduledEvent = createEventAccessor("getScheduledEvent()", isScheduledEvent);
383
- var getEmailEvent = createEventAccessor("getEmailEvent()", isEmailEvent);
384
- var getTailEvent = createEventAccessor("getTailEvent()", isTailEvent);
385
- var getDurableObjectEvent = createEventAccessor("getDurableObjectEvent()", isDurableObjectEvent);
386
- var getDurableObjectFetchEvent = createEventAccessor("getDurableObjectFetchEvent()", isDurableObjectFetchEvent);
387
- var getDurableObjectAlarmEvent = createEventAccessor("getDurableObjectAlarmEvent()", isDurableObjectAlarmEvent);
388
- var getDurableObjectWebSocketMessageEvent = createEventAccessor("getDurableObjectWebSocketMessageEvent()", isDurableObjectWebSocketMessageEvent);
389
- var getDurableObjectWebSocketCloseEvent = createEventAccessor("getDurableObjectWebSocketCloseEvent()", isDurableObjectWebSocketCloseEvent);
390
- var getDurableObjectWebSocketErrorEvent = createEventAccessor("getDurableObjectWebSocketErrorEvent()", isDurableObjectWebSocketErrorEvent);
5
+ // src/bridge/log.ts
6
+ var isDebugEnabled = () => {
7
+ try {
8
+ const env = globalThis.process?.env;
9
+ return Boolean(env?.DEVFLARE_DEBUG_BRIDGE);
10
+ } catch {
11
+ return false;
12
+ }
13
+ };
14
+ var bridgeLog = {
15
+ warn(message, error) {
16
+ if (!isDebugEnabled())
17
+ return;
18
+ console.warn(`[devflare:bridge] ${message}`, error);
19
+ },
20
+ debug(message, error) {
21
+ if (!isDebugEnabled())
22
+ return;
23
+ console.debug(`[devflare:bridge] ${message}`, error);
24
+ }
25
+ };
391
26
 
392
- // src/bridge/v2/wire.ts
393
- var BinaryKind = {
27
+ // src/bridge/v2/frames.ts
28
+ var TRANSPORT_V2_PROTOCOL_VERSION = 2;
29
+ var TRANSPORT_V2_UNSUPPORTED_VERSION_CLOSE_CODE = 4001;
30
+ var TransportV2BinaryKind = {
394
31
  StreamChunk: 1,
395
- WsData: 2
32
+ WsData: 2,
33
+ BodyChunk: 3
396
34
  };
397
- var BinaryFlags = {
35
+ var TransportV2BinaryFlags = {
398
36
  FIN: 1,
399
- TEXT: 2
37
+ TEXT: 2,
38
+ ABORT: 4
400
39
  };
401
- var BINARY_HEADER_SIZE = 10;
402
- function encodeBinaryFrame(kind, id, seq, flags, payload) {
403
- const frame = new Uint8Array(BINARY_HEADER_SIZE + payload.byteLength);
404
- const view = new DataView(frame.buffer);
405
- view.setUint8(0, kind);
406
- view.setUint32(1, id, true);
407
- view.setUint32(5, seq, true);
408
- view.setUint8(9, flags);
409
- frame.set(payload, BINARY_HEADER_SIZE);
410
- return frame;
40
+ var TRANSPORT_V2_BINARY_HEADER_SIZE = 10;
41
+ function decodeTransportV2BinaryFrame(frame) {
42
+ if (frame.byteLength < TRANSPORT_V2_BINARY_HEADER_SIZE) {
43
+ throw new Error(`Invalid transport v2 binary frame: too short (${frame.byteLength} bytes, need at least ${TRANSPORT_V2_BINARY_HEADER_SIZE})`);
44
+ }
45
+ const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
46
+ const kind = view.getUint8(0);
47
+ if (kind !== TransportV2BinaryKind.StreamChunk && kind !== TransportV2BinaryKind.WsData && kind !== TransportV2BinaryKind.BodyChunk) {
48
+ throw new Error(`Invalid transport v2 binary frame: unknown kind ${kind}`);
49
+ }
50
+ return {
51
+ kind,
52
+ id: view.getUint32(1, true),
53
+ seq: view.getUint32(5, true),
54
+ flags: view.getUint8(9),
55
+ payload: frame.subarray(TRANSPORT_V2_BINARY_HEADER_SIZE)
56
+ };
411
57
  }
412
- function parseJsonMsg(data) {
58
+ function transportV2IsFin(flags) {
59
+ return (flags & TransportV2BinaryFlags.FIN) !== 0;
60
+ }
61
+ function transportV2IsAbort(flags) {
62
+ return (flags & TransportV2BinaryFlags.ABORT) !== 0;
63
+ }
64
+ var KNOWN_V2_CONTROL_TYPES = new Set(["hello", "welcome", "body.open", "body.end", "body.abort"]);
65
+ function parseTransportV2ControlMsg(data) {
413
66
  const msg = JSON.parse(data);
414
67
  if (typeof msg !== "object" || msg === null || !("t" in msg)) {
415
- throw new Error("Invalid message: missing type field");
68
+ throw new Error("Invalid transport v2 control message: missing type field");
69
+ }
70
+ if (!KNOWN_V2_CONTROL_TYPES.has(msg.t)) {
71
+ throw new Error(`Invalid transport v2 control message: unknown type "${msg.t}"`);
72
+ }
73
+ if (msg.t === "hello" || msg.t === "welcome") {
74
+ if (msg.protocolVersion !== TRANSPORT_V2_PROTOCOL_VERSION) {
75
+ throw new Error(`Invalid transport v2 ${msg.t}: protocolVersion ${msg.protocolVersion} != ${TRANSPORT_V2_PROTOCOL_VERSION}`);
76
+ }
77
+ if (!Array.isArray(msg.capabilities)) {
78
+ throw new Error(`Invalid transport v2 ${msg.t}: capabilities must be an array`);
79
+ }
416
80
  }
417
81
  return msg;
418
82
  }
419
- function stringifyJsonMsg(msg) {
83
+ function stringifyTransportV2ControlMsg(msg) {
420
84
  return JSON.stringify(msg);
421
85
  }
422
- var streamIdCounter = 0;
423
- var wsIdCounter = 0;
424
- function nextStreamId() {
425
- return ++streamIdCounter;
426
- }
427
- function nextWsId() {
428
- return ++wsIdCounter;
86
+ function negotiateTransportV2Capabilities(supported, advertised) {
87
+ const supportedSet = new Set(supported);
88
+ const intersection = advertised.filter((cap) => supportedSet.has(cap));
89
+ return [...new Set(intersection)].sort();
429
90
  }
430
- var DEFAULT_CHUNK_SIZE = 256 * 1024;
431
- var HTTP_TRANSFER_THRESHOLD = 512 * 1024;
432
- var DEFAULT_BRIDGE_PORT = 8686;
433
91
 
434
- // src/bridge/v2/value-serialization.ts
435
- async function serializeRequest(request, options) {
436
- const streams = [];
437
- const threshold = options?.httpThreshold ?? HTTP_TRANSFER_THRESHOLD;
438
- const headers = [];
439
- request.headers.forEach((value, key) => {
440
- headers.push([key, value]);
441
- });
442
- let body = null;
443
- if (request.body) {
444
- const bytes = await request.arrayBuffer();
445
- if (bytes.byteLength > threshold) {
446
- throw new Error("Request body exceeds the bridge inline limit (~512 KB) and large request-body streaming is not yet supported over the local bridge. " + "Send the payload in smaller chunks or via an R2 binding for now.");
447
- } else if (bytes.byteLength > 0) {
448
- body = { type: "bytes", data: base64Encode(new Uint8Array(bytes)) };
449
- }
92
+ // src/bridge/v2/body-streams.ts
93
+ var TRANSPORT_V2_DEFAULT_BODY_CHUNK_SIZE = 256 * 1024;
94
+ class TransportV2BodyReaderRegistry {
95
+ #streams = new Map;
96
+ getOrOpen(bid) {
97
+ const existing = this.#streams.get(bid);
98
+ if (existing !== undefined)
99
+ return existing.stream;
100
+ return this.open(bid);
450
101
  }
451
- return {
452
- serialized: {
453
- url: request.url,
454
- method: request.method,
455
- headers,
456
- body,
457
- redirect: request.redirect
458
- },
459
- streams
460
- };
461
- }
462
- function deserializeRequest(serialized, getStream) {
463
- let body = null;
464
- if (serialized.body) {
465
- switch (serialized.body.type) {
466
- case "bytes":
467
- body = base64Decode(serialized.body.data);
468
- break;
469
- case "stream":
470
- if (getStream) {
471
- body = getStream(serialized.body.sid) ?? null;
472
- }
473
- break;
102
+ open(bid) {
103
+ if (this.#streams.has(bid)) {
104
+ throw new Error(`v2 body reader already registered for bid ${bid}`);
474
105
  }
106
+ const state = {
107
+ controller: null,
108
+ stream: null,
109
+ closed: false
110
+ };
111
+ state.stream = new ReadableStream({
112
+ start: (controller) => {
113
+ state.controller = controller;
114
+ },
115
+ cancel: () => {
116
+ state.closed = true;
117
+ this.#streams.delete(bid);
118
+ }
119
+ });
120
+ this.#streams.set(bid, state);
121
+ return state.stream;
475
122
  }
476
- return new Request(serialized.url, {
477
- method: serialized.method,
478
- headers: serialized.headers,
479
- body,
480
- redirect: serialized.redirect
481
- });
482
- }
483
- async function serializeResponse(response, options) {
484
- const streams = [];
485
- const threshold = options?.httpThreshold ?? HTTP_TRANSFER_THRESHOLD;
486
- const headers = [];
487
- response.headers.forEach((value, key) => {
488
- headers.push([key, value]);
489
- });
490
- let body = null;
491
- if (response.body) {
492
- const bytes = await response.arrayBuffer();
493
- if (bytes.byteLength > threshold) {
494
- const sid = nextStreamId();
495
- const payload = new Uint8Array(bytes);
496
- const frameSize = HTTP_TRANSFER_THRESHOLD;
497
- const stream = new ReadableStream({
498
- start(controller) {
499
- for (let offset = 0;offset < payload.byteLength; offset += frameSize) {
500
- controller.enqueue(payload.slice(offset, Math.min(offset + frameSize, payload.byteLength)));
501
- }
502
- controller.close();
503
- }
504
- });
505
- streams.push({ sid, stream });
506
- body = { type: "stream", sid };
507
- } else if (bytes.byteLength > 0) {
508
- body = { type: "bytes", data: base64Encode(new Uint8Array(bytes)) };
123
+ pushChunk(frame) {
124
+ if (frame.kind !== TransportV2BinaryKind.BodyChunk) {
125
+ throw new Error(`v2 body reader received non-BodyChunk frame (kind=${frame.kind})`);
509
126
  }
510
- }
511
- return {
512
- serialized: {
513
- status: response.status,
514
- statusText: response.statusText,
515
- headers,
516
- body
517
- },
518
- streams
519
- };
520
- }
521
- function deserializeResponse(serialized, getStream) {
522
- let body = null;
523
- if (serialized.body) {
524
- switch (serialized.body.type) {
525
- case "bytes":
526
- body = base64Decode(serialized.body.data);
527
- break;
528
- case "stream":
529
- if (getStream) {
530
- body = getStream(serialized.body.sid) ?? null;
531
- }
532
- break;
127
+ const state = this.#streams.get(frame.id);
128
+ if (state === undefined || state.closed)
129
+ return;
130
+ const isFin = transportV2IsFin(frame.flags);
131
+ const isAbort = transportV2IsAbort(frame.flags);
132
+ if (isAbort) {
133
+ state.closed = true;
134
+ state.controller?.error(new Error(`v2 body stream ${frame.id} aborted by writer`));
135
+ this.#streams.delete(frame.id);
136
+ return;
137
+ }
138
+ if (frame.payload.byteLength > 0) {
139
+ state.controller?.enqueue(new Uint8Array(frame.payload));
140
+ }
141
+ if (isFin) {
142
+ state.closed = true;
143
+ state.controller?.close();
144
+ this.#streams.delete(frame.id);
533
145
  }
534
146
  }
535
- return new Response(body, {
536
- status: serialized.status,
537
- statusText: serialized.statusText,
538
- headers: serialized.headers
539
- });
540
- }
541
- async function serializeValue(value, options) {
542
- const streams = [];
543
- const result = await serializeValueInternal(value, streams, options);
544
- return { value: result, streams };
545
- }
546
- async function serializeValueInternal(value, streams, options) {
547
- if (value === null || value === undefined) {
548
- return value;
147
+ end(bid) {
148
+ const state = this.#streams.get(bid);
149
+ if (state === undefined || state.closed)
150
+ return;
151
+ state.closed = true;
152
+ state.controller?.close();
153
+ this.#streams.delete(bid);
549
154
  }
550
- if (value instanceof Request) {
551
- const { serialized, streams: reqStreams } = await serializeRequest(value, options);
552
- streams.push(...reqStreams);
553
- return { __type: "Request", ...serialized };
155
+ abort(bid, reason) {
156
+ const state = this.#streams.get(bid);
157
+ if (state === undefined || state.closed)
158
+ return;
159
+ state.closed = true;
160
+ state.controller?.error(new Error(reason ?? `v2 body stream ${bid} aborted`));
161
+ this.#streams.delete(bid);
554
162
  }
555
- if (value instanceof Response) {
556
- const { serialized, streams: resStreams } = await serializeResponse(value, options);
557
- streams.push(...resStreams);
558
- return { __type: "Response", ...serialized };
163
+ get size() {
164
+ return this.#streams.size;
559
165
  }
560
- if (value instanceof ReadableStream) {
561
- const sid = nextStreamId();
562
- streams.push({ sid, stream: value });
563
- return { __type: "ReadableStream", sid };
166
+ }
167
+
168
+ // src/bridge/v2/codec.ts
169
+ class TransportV2Codec {
170
+ bodyReaders = new TransportV2BodyReaderRegistry;
171
+ #socket;
172
+ #capabilities;
173
+ #options;
174
+ #handshakeResolver = null;
175
+ #handshakePromise;
176
+ #sentHello = false;
177
+ #receivedHello = false;
178
+ #receivedWelcome = false;
179
+ #negotiated = null;
180
+ #pendingRpc = new Map;
181
+ #nextBid = 1;
182
+ #nextRpcId = 1;
183
+ #closed = false;
184
+ constructor(socket, options = {}) {
185
+ this.#socket = socket;
186
+ this.#capabilities = options.capabilities ?? [];
187
+ this.#options = options;
188
+ this.#handshakePromise = new Promise((resolve, reject) => {
189
+ this.#handshakeResolver = { resolve, reject };
190
+ });
191
+ socket.onmessage = (event) => this.#onMessage(event);
192
+ socket.onclose = () => this.#onClose();
193
+ socket.onerror = (event) => this.#onError(event.error);
564
194
  }
565
- if (value instanceof Uint8Array) {
566
- return { __type: "Uint8Array", data: base64Encode(value) };
195
+ allocateBid() {
196
+ return this.#nextBid++;
567
197
  }
568
- if (value instanceof ArrayBuffer) {
569
- return { __type: "ArrayBuffer", data: base64Encode(new Uint8Array(value)) };
198
+ allocateRpcId() {
199
+ return `v2_rpc_${this.#nextRpcId++}`;
570
200
  }
571
- if (value instanceof Date) {
572
- return { __devflare: "date", iso: value.toISOString() };
201
+ sendHello() {
202
+ if (this.#sentHello)
203
+ return;
204
+ this.#sentHello = true;
205
+ const hello = {
206
+ t: "hello",
207
+ protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
208
+ capabilities: [...this.#capabilities]
209
+ };
210
+ this.#socket.send(stringifyTransportV2ControlMsg(hello));
573
211
  }
574
- if (value instanceof URL) {
575
- return { __devflare: "url", href: value.href };
212
+ get handshake() {
213
+ return this.#handshakePromise;
576
214
  }
577
- if (value instanceof Error) {
578
- const encoded = {
579
- __devflare: "error",
580
- name: value.name,
581
- message: value.message
582
- };
583
- if (value.stack)
584
- encoded.stack = value.stack;
585
- return encoded;
215
+ get negotiated() {
216
+ return this.#negotiated;
586
217
  }
587
- if (value instanceof Map) {
588
- const entries = [];
589
- for (const [k, v] of value.entries()) {
590
- entries.push([
591
- await serializeValueInternal(k, streams, options),
592
- await serializeValueInternal(v, streams, options)
593
- ]);
218
+ sendText(message) {
219
+ if (this.#closed) {
220
+ throw new Error("cannot send on a closed v2 codec");
594
221
  }
595
- return { __devflare: "map", entries };
222
+ this.#socket.send(message);
596
223
  }
597
- if (value instanceof Set) {
598
- const values = [];
599
- for (const v of value.values()) {
600
- values.push(await serializeValueInternal(v, streams, options));
224
+ sendBinary(frame) {
225
+ if (this.#closed) {
226
+ throw new Error("cannot send on a closed v2 codec");
601
227
  }
602
- return { __devflare: "set", values };
228
+ this.#socket.send(frame);
603
229
  }
604
- if (Array.isArray(value)) {
605
- return Promise.all(value.map((v) => serializeValueInternal(v, streams, options)));
230
+ call(method, params = []) {
231
+ const id = this.allocateRpcId();
232
+ const call = { t: "rpc.call", id, method, params };
233
+ const promise = new Promise((resolve, reject) => {
234
+ this.#pendingRpc.set(id, { resolve, reject });
235
+ });
236
+ this.sendText(JSON.stringify(call));
237
+ return promise;
606
238
  }
607
- if (typeof value === "object") {
608
- const result = {};
609
- for (const [k, v] of Object.entries(value)) {
610
- result[k] = await serializeValueInternal(v, streams, options);
611
- }
612
- return result;
239
+ respondOk(id, result) {
240
+ const reply = { t: "rpc.ok", id, result };
241
+ this.sendText(JSON.stringify(reply));
613
242
  }
614
- return value;
615
- }
616
- function deserializeValue(value, getStream) {
617
- if (value === null || value === undefined) {
618
- return value;
243
+ respondErr(id, error) {
244
+ const reply = { t: "rpc.err", id, error };
245
+ this.sendText(JSON.stringify(reply));
619
246
  }
620
- if (typeof value === "object" && value !== null) {
621
- const obj = value;
622
- if (typeof obj.__devflare === "string") {
623
- switch (obj.__devflare) {
624
- case "date":
625
- return new Date(obj.iso);
626
- case "url":
627
- return new URL(obj.href);
628
- case "error": {
629
- const err = new Error(obj.message);
630
- if (typeof obj.name === "string")
631
- err.name = obj.name;
632
- if (typeof obj.stack === "string")
633
- err.stack = obj.stack;
634
- return err;
635
- }
636
- case "map": {
637
- const entries = obj.entries ?? [];
638
- const map = new Map;
639
- for (const [k, v] of entries) {
640
- map.set(deserializeValue(k, getStream), deserializeValue(v, getStream));
641
- }
642
- return map;
643
- }
644
- case "set": {
645
- const values = obj.values ?? [];
646
- const set = new Set;
647
- for (const v of values) {
648
- set.add(deserializeValue(v, getStream));
649
- }
650
- return set;
651
- }
652
- }
653
- }
654
- if (obj.__type === "Request") {
655
- return deserializeRequest(obj, getStream);
656
- }
657
- if (obj.__type === "Response") {
658
- return deserializeResponse(obj, getStream);
659
- }
660
- if (obj.__type === "ReadableStream") {
661
- const sid = obj.sid;
662
- return getStream?.(sid) ?? null;
663
- }
664
- if (obj.__type === "Uint8Array") {
665
- return base64Decode(obj.data);
666
- }
667
- if (obj.__type === "ArrayBuffer") {
668
- return base64Decode(obj.data).buffer;
247
+ sendWireError(err) {
248
+ const frame = { t: "error", ...err };
249
+ this.sendText(JSON.stringify(frame));
250
+ }
251
+ openBodyReader(bid) {
252
+ return this.bodyReaders.getOrOpen(bid);
253
+ }
254
+ setRpcCallHandler(handler) {
255
+ this.#options = { ...this.#options, onRpcCall: handler };
256
+ }
257
+ close(code, reason) {
258
+ if (this.#closed)
259
+ return;
260
+ this.#closed = true;
261
+ this.#failPending(new Error("v2 transport closed"));
262
+ this.#socket.close(code, reason);
263
+ }
264
+ get isClosed() {
265
+ return this.#closed;
266
+ }
267
+ #onMessage(event) {
268
+ const { data } = event;
269
+ if (typeof data === "string") {
270
+ this.#onText(data);
271
+ } else if (data instanceof Uint8Array) {
272
+ this.#onBinary(data);
273
+ } else if (data instanceof ArrayBuffer) {
274
+ this.#onBinary(new Uint8Array(data));
669
275
  }
670
- if (obj.__type === "R2Object") {
671
- return deserializeR2Object(obj);
276
+ }
277
+ #onText(data) {
278
+ let msg;
279
+ try {
280
+ msg = parseTransportV2ControlMsg(data);
281
+ } catch {
282
+ const rpc = tryParseRpcMsg(data);
283
+ if (rpc !== null) {
284
+ this.#onRpcMessage(rpc);
285
+ return;
286
+ }
287
+ const wireErr = tryParseWireError(data);
288
+ if (wireErr !== null) {
289
+ this.#options.onWireError?.(wireErr);
290
+ return;
291
+ }
292
+ this.#options.onUnknownControl?.(data);
293
+ return;
672
294
  }
673
- if (obj.__type === "R2ObjectBody") {
674
- return deserializeR2ObjectBody(obj);
295
+ switch (msg.t) {
296
+ case "hello":
297
+ this.#onHello(msg);
298
+ break;
299
+ case "welcome":
300
+ this.#onWelcome(msg);
301
+ break;
302
+ case "body.open":
303
+ this.bodyReaders.getOrOpen(msg.bid);
304
+ break;
305
+ case "body.end":
306
+ this.bodyReaders.end(msg.bid);
307
+ break;
308
+ case "body.abort":
309
+ this.bodyReaders.abort(msg.bid, msg.error);
310
+ break;
675
311
  }
676
- if (Array.isArray(value)) {
677
- return value.map((v) => deserializeValue(v, getStream));
312
+ }
313
+ #onBinary(data) {
314
+ let frame;
315
+ try {
316
+ frame = decodeTransportV2BinaryFrame(data);
317
+ } catch {
318
+ return;
678
319
  }
679
- const result = {};
680
- for (const [k, v] of Object.entries(obj)) {
681
- result[k] = deserializeValue(v, getStream);
320
+ if (frame.kind === TransportV2BinaryKind.BodyChunk) {
321
+ this.bodyReaders.pushChunk(frame);
322
+ return;
682
323
  }
683
- return result;
324
+ this.#options.onUnknownBinary?.(frame);
684
325
  }
685
- return value;
686
- }
687
- function applySerializedHttpMetadata(headers, httpMetadata) {
688
- if (httpMetadata?.contentType) {
689
- headers.set("Content-Type", httpMetadata.contentType);
326
+ #onRpcMessage(msg) {
327
+ switch (msg.t) {
328
+ case "rpc.call":
329
+ this.#options.onRpcCall?.(msg);
330
+ break;
331
+ case "rpc.ok": {
332
+ const pending = this.#pendingRpc.get(msg.id);
333
+ if (pending !== undefined) {
334
+ this.#pendingRpc.delete(msg.id);
335
+ pending.resolve(msg.result);
336
+ }
337
+ break;
338
+ }
339
+ case "rpc.err": {
340
+ const pending = this.#pendingRpc.get(msg.id);
341
+ if (pending !== undefined) {
342
+ this.#pendingRpc.delete(msg.id);
343
+ const err = new Error(msg.error.message);
344
+ Object.assign(err, { code: msg.error.code, details: msg.error.details });
345
+ pending.reject(err);
346
+ }
347
+ break;
348
+ }
349
+ }
690
350
  }
691
- if (httpMetadata?.contentLanguage) {
692
- headers.set("Content-Language", httpMetadata.contentLanguage);
351
+ #onHello(msg) {
352
+ if (this.#receivedHello) {
353
+ this.close(TRANSPORT_V2_UNSUPPORTED_VERSION_CLOSE_CODE, "duplicate hello");
354
+ return;
355
+ }
356
+ this.#receivedHello = true;
357
+ const negotiated = negotiateTransportV2Capabilities(this.#capabilities, msg.capabilities);
358
+ const welcome = {
359
+ t: "welcome",
360
+ protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
361
+ capabilities: negotiated
362
+ };
363
+ this.#socket.send(stringifyTransportV2ControlMsg(welcome));
364
+ this.#completeHandshake({
365
+ protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
366
+ capabilities: negotiated
367
+ });
693
368
  }
694
- if (httpMetadata?.contentDisposition) {
695
- headers.set("Content-Disposition", httpMetadata.contentDisposition);
369
+ #onWelcome(msg) {
370
+ if (this.#receivedWelcome)
371
+ return;
372
+ this.#receivedWelcome = true;
373
+ this.#completeHandshake({
374
+ protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
375
+ capabilities: msg.capabilities
376
+ });
696
377
  }
697
- if (httpMetadata?.contentEncoding) {
698
- headers.set("Content-Encoding", httpMetadata.contentEncoding);
378
+ #completeHandshake(result) {
379
+ if (this.#negotiated !== null)
380
+ return;
381
+ this.#negotiated = result;
382
+ this.#handshakeResolver?.resolve(result);
383
+ this.#handshakeResolver = null;
699
384
  }
700
- if (httpMetadata?.cacheControl) {
701
- headers.set("Cache-Control", httpMetadata.cacheControl);
385
+ #onClose() {
386
+ if (this.#closed) {
387
+ return;
388
+ }
389
+ this.#closed = true;
390
+ this.#failPending(new Error("v2 transport closed"));
702
391
  }
703
- if (httpMetadata?.cacheExpiry) {
704
- headers.set("Expires", new Date(httpMetadata.cacheExpiry).toUTCString());
392
+ #onError(error) {
393
+ const wrapped = error instanceof Error ? error : new Error(String(error));
394
+ this.#failPending(wrapped);
705
395
  }
706
- }
707
- function createSerializedR2Metadata(serialized) {
708
- return {
709
- key: serialized.key,
710
- version: serialized.version,
711
- size: serialized.size,
712
- etag: serialized.etag,
713
- httpEtag: serialized.httpEtag,
714
- checksums: serialized.checksums,
715
- uploaded: serialized.uploaded ? new Date(serialized.uploaded) : new Date,
716
- httpMetadata: serialized.httpMetadata,
717
- customMetadata: serialized.customMetadata,
718
- range: serialized.range,
719
- storageClass: serialized.storageClass,
720
- writeHttpMetadata(headers) {
721
- applySerializedHttpMetadata(headers, serialized.httpMetadata);
396
+ #failPending(error) {
397
+ const resolver = this.#handshakeResolver;
398
+ if (resolver !== null) {
399
+ this.#handshakeResolver = null;
400
+ resolver.reject(error);
722
401
  }
723
- };
724
- }
725
- function deserializeR2Object(obj) {
726
- const serialized = obj;
727
- return {
728
- ...createSerializedR2Metadata(serialized)
729
- };
730
- }
731
- function deserializeR2ObjectBody(obj) {
732
- const serialized = obj;
733
- const bodyBytes = serialized.bodyData ? base64Decode(serialized.bodyData) : new Uint8Array(0);
734
- const r2ObjectBody = {
735
- ...createSerializedR2Metadata(serialized),
736
- body: new ReadableStream({
737
- start(controller) {
738
- controller.enqueue(bodyBytes);
739
- controller.close();
740
- }
741
- }),
742
- bodyUsed: false,
743
- async arrayBuffer() {
744
- const copy = new Uint8Array(bodyBytes.byteLength);
745
- copy.set(bodyBytes);
746
- return copy.buffer;
747
- },
748
- async text() {
749
- return new TextDecoder().decode(bodyBytes);
750
- },
751
- async json() {
752
- const text = new TextDecoder().decode(bodyBytes);
753
- return JSON.parse(text);
754
- },
755
- async blob() {
756
- const contentType = serialized.httpMetadata?.contentType || "application/octet-stream";
757
- const buffer = bodyBytes.buffer.slice(bodyBytes.byteOffset, bodyBytes.byteOffset + bodyBytes.byteLength);
758
- return new Blob([buffer], { type: contentType });
402
+ for (const pending of this.#pendingRpc.values()) {
403
+ pending.reject(error);
759
404
  }
760
- };
761
- return r2ObjectBody;
405
+ this.#pendingRpc.clear();
406
+ }
762
407
  }
763
- function base64Encode(bytes) {
764
- if (typeof Buffer !== "undefined") {
765
- return Buffer.from(bytes).toString("base64");
408
+ function tryParseRpcMsg(data) {
409
+ let parsed;
410
+ try {
411
+ parsed = JSON.parse(data);
412
+ } catch {
413
+ return null;
766
414
  }
767
- let binary = "";
768
- for (let i = 0;i < bytes.byteLength; i++) {
769
- binary += String.fromCharCode(bytes[i]);
415
+ if (typeof parsed !== "object" || parsed === null || !("t" in parsed))
416
+ return null;
417
+ const t = parsed.t;
418
+ if (t === "rpc.call" || t === "rpc.ok" || t === "rpc.err") {
419
+ return parsed;
770
420
  }
771
- return btoa(binary);
421
+ return null;
772
422
  }
773
- function base64Decode(str) {
774
- if (typeof Buffer !== "undefined") {
775
- return new Uint8Array(Buffer.from(str, "base64"));
776
- }
777
- const binary = atob(str);
778
- const bytes = new Uint8Array(binary.length);
779
- for (let i = 0;i < binary.length; i++) {
780
- bytes[i] = binary.charCodeAt(i);
423
+ function tryParseWireError(data) {
424
+ let parsed;
425
+ try {
426
+ parsed = JSON.parse(data);
427
+ } catch {
428
+ return null;
781
429
  }
782
- return bytes;
430
+ if (typeof parsed !== "object" || parsed === null || !("t" in parsed))
431
+ return null;
432
+ const candidate = parsed;
433
+ if (candidate.t !== "error")
434
+ return null;
435
+ const scope = candidate.scope;
436
+ if (scope !== "transport" && scope !== "rpc" && scope !== "stream" && scope !== "ws")
437
+ return null;
438
+ const err = candidate.error;
439
+ if (!err || typeof err.code !== "string" || typeof err.message !== "string")
440
+ return null;
441
+ return parsed;
783
442
  }
784
443
 
785
- // src/bridge/v2/frames.ts
786
- var TRANSPORT_V2_PROTOCOL_VERSION = 2;
787
- var TRANSPORT_V2_UNSUPPORTED_VERSION_CLOSE_CODE = 4001;
788
- var TransportV2BinaryKind = {
444
+ // src/bridge/v2/wire.ts
445
+ var BinaryKind = {
789
446
  StreamChunk: 1,
790
- WsData: 2,
791
- BodyChunk: 3
447
+ WsData: 2
792
448
  };
793
- var TransportV2BinaryFlags = {
449
+ var BinaryFlags = {
794
450
  FIN: 1,
795
- TEXT: 2,
796
- ABORT: 4
797
- };
798
- var TRANSPORT_V2_BINARY_HEADER_SIZE = 10;
799
- function decodeTransportV2BinaryFrame(frame) {
800
- if (frame.byteLength < TRANSPORT_V2_BINARY_HEADER_SIZE) {
801
- throw new Error(`Invalid transport v2 binary frame: too short (${frame.byteLength} bytes, need at least ${TRANSPORT_V2_BINARY_HEADER_SIZE})`);
802
- }
803
- const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
804
- const kind = view.getUint8(0);
805
- if (kind !== TransportV2BinaryKind.StreamChunk && kind !== TransportV2BinaryKind.WsData && kind !== TransportV2BinaryKind.BodyChunk) {
806
- throw new Error(`Invalid transport v2 binary frame: unknown kind ${kind}`);
807
- }
808
- return {
809
- kind,
810
- id: view.getUint32(1, true),
811
- seq: view.getUint32(5, true),
812
- flags: view.getUint8(9),
813
- payload: frame.subarray(TRANSPORT_V2_BINARY_HEADER_SIZE)
814
- };
815
- }
816
- function transportV2IsFin(flags) {
817
- return (flags & TransportV2BinaryFlags.FIN) !== 0;
818
- }
819
- function transportV2IsAbort(flags) {
820
- return (flags & TransportV2BinaryFlags.ABORT) !== 0;
451
+ TEXT: 2
452
+ };
453
+ var BINARY_HEADER_SIZE = 10;
454
+ function encodeBinaryFrame(kind, id, seq, flags, payload) {
455
+ const frame = new Uint8Array(BINARY_HEADER_SIZE + payload.byteLength);
456
+ const view = new DataView(frame.buffer);
457
+ view.setUint8(0, kind);
458
+ view.setUint32(1, id, true);
459
+ view.setUint32(5, seq, true);
460
+ view.setUint8(9, flags);
461
+ frame.set(payload, BINARY_HEADER_SIZE);
462
+ return frame;
821
463
  }
822
- var KNOWN_V2_CONTROL_TYPES = new Set([
823
- "hello",
824
- "welcome",
825
- "body.open",
826
- "body.end",
827
- "body.abort"
828
- ]);
829
- function parseTransportV2ControlMsg(data) {
464
+ function parseJsonMsg(data) {
830
465
  const msg = JSON.parse(data);
831
466
  if (typeof msg !== "object" || msg === null || !("t" in msg)) {
832
- throw new Error("Invalid transport v2 control message: missing type field");
833
- }
834
- if (!KNOWN_V2_CONTROL_TYPES.has(msg.t)) {
835
- throw new Error(`Invalid transport v2 control message: unknown type "${msg.t}"`);
836
- }
837
- if (msg.t === "hello" || msg.t === "welcome") {
838
- if (msg.protocolVersion !== TRANSPORT_V2_PROTOCOL_VERSION) {
839
- throw new Error(`Invalid transport v2 ${msg.t}: protocolVersion ${msg.protocolVersion} != ${TRANSPORT_V2_PROTOCOL_VERSION}`);
840
- }
841
- if (!Array.isArray(msg.capabilities)) {
842
- throw new Error(`Invalid transport v2 ${msg.t}: capabilities must be an array`);
843
- }
467
+ throw new Error("Invalid message: missing type field");
844
468
  }
845
469
  return msg;
846
470
  }
847
- function stringifyTransportV2ControlMsg(msg) {
471
+ function stringifyJsonMsg(msg) {
848
472
  return JSON.stringify(msg);
849
473
  }
850
- function negotiateTransportV2Capabilities(supported, advertised) {
851
- const supportedSet = new Set(supported);
852
- const intersection = advertised.filter((cap) => supportedSet.has(cap));
853
- return [...new Set(intersection)].sort();
474
+ var streamIdCounter = 0;
475
+ var wsIdCounter = 0;
476
+ function nextStreamId() {
477
+ return ++streamIdCounter;
854
478
  }
479
+ function nextWsId() {
480
+ return ++wsIdCounter;
481
+ }
482
+ var DEFAULT_CHUNK_SIZE = 256 * 1024;
483
+ var HTTP_TRANSFER_THRESHOLD = 512 * 1024;
484
+ var DEFAULT_BRIDGE_PORT = 8686;
855
485
 
856
- // src/bridge/v2/body-streams.ts
857
- var TRANSPORT_V2_DEFAULT_BODY_CHUNK_SIZE = 256 * 1024;
858
- class TransportV2BodyReaderRegistry {
859
- #streams = new Map;
860
- getOrOpen(bid) {
861
- const existing = this.#streams.get(bid);
862
- if (existing !== undefined)
863
- return existing.stream;
864
- return this.open(bid);
865
- }
866
- open(bid) {
867
- if (this.#streams.has(bid)) {
868
- throw new Error(`v2 body reader already registered for bid ${bid}`);
869
- }
870
- const state = {
871
- controller: null,
872
- stream: null,
873
- closed: false
874
- };
875
- state.stream = new ReadableStream({
876
- start: (controller) => {
877
- state.controller = controller;
878
- },
879
- cancel: () => {
880
- state.closed = true;
881
- this.#streams.delete(bid);
882
- }
883
- });
884
- this.#streams.set(bid, state);
885
- return state.stream;
886
- }
887
- pushChunk(frame) {
888
- if (frame.kind !== TransportV2BinaryKind.BodyChunk) {
889
- throw new Error(`v2 body reader received non-BodyChunk frame (kind=${frame.kind})`);
890
- }
891
- const state = this.#streams.get(frame.id);
892
- if (state === undefined || state.closed)
893
- return;
894
- const isFin = transportV2IsFin(frame.flags);
895
- const isAbort = transportV2IsAbort(frame.flags);
896
- if (isAbort) {
897
- state.closed = true;
898
- state.controller?.error(new Error(`v2 body stream ${frame.id} aborted by writer`));
899
- this.#streams.delete(frame.id);
900
- return;
901
- }
902
- if (frame.payload.byteLength > 0) {
903
- state.controller?.enqueue(new Uint8Array(frame.payload));
486
+ // src/bridge/v2/value-serialization.ts
487
+ async function serializeRequest(request, options) {
488
+ const streams = [];
489
+ const threshold = options?.httpThreshold ?? HTTP_TRANSFER_THRESHOLD;
490
+ const headers = [];
491
+ request.headers.forEach((value, key) => {
492
+ headers.push([key, value]);
493
+ });
494
+ let body = null;
495
+ if (request.body) {
496
+ const bytes = await request.arrayBuffer();
497
+ if (bytes.byteLength > threshold) {
498
+ throw new Error("Request body exceeds the bridge inline limit (~512 KB) and large request-body streaming is not yet supported over the local bridge. " + "Send the payload in smaller chunks or via an R2 binding for now.");
904
499
  }
905
- if (isFin) {
906
- state.closed = true;
907
- state.controller?.close();
908
- this.#streams.delete(frame.id);
500
+ if (bytes.byteLength > 0) {
501
+ body = { type: "bytes", data: base64Encode(new Uint8Array(bytes)) };
909
502
  }
910
503
  }
911
- end(bid) {
912
- const state = this.#streams.get(bid);
913
- if (state === undefined || state.closed)
914
- return;
915
- state.closed = true;
916
- state.controller?.close();
917
- this.#streams.delete(bid);
918
- }
919
- abort(bid, reason) {
920
- const state = this.#streams.get(bid);
921
- if (state === undefined || state.closed)
922
- return;
923
- state.closed = true;
924
- state.controller?.error(new Error(reason ?? `v2 body stream ${bid} aborted`));
925
- this.#streams.delete(bid);
926
- }
927
- get size() {
928
- return this.#streams.size;
929
- }
504
+ return {
505
+ serialized: {
506
+ url: request.url,
507
+ method: request.method,
508
+ headers,
509
+ body,
510
+ redirect: request.redirect
511
+ },
512
+ streams
513
+ };
930
514
  }
931
-
932
- // src/bridge/v2/codec.ts
933
- class TransportV2Codec {
934
- bodyReaders = new TransportV2BodyReaderRegistry;
935
- #socket;
936
- #capabilities;
937
- #options;
938
- #handshakeResolver = null;
939
- #handshakePromise;
940
- #sentHello = false;
941
- #receivedHello = false;
942
- #receivedWelcome = false;
943
- #negotiated = null;
944
- #pendingRpc = new Map;
945
- #nextBid = 1;
946
- #nextRpcId = 1;
947
- #closed = false;
948
- constructor(socket, options = {}) {
949
- this.#socket = socket;
950
- this.#capabilities = options.capabilities ?? [];
951
- this.#options = options;
952
- this.#handshakePromise = new Promise((resolve, reject) => {
953
- this.#handshakeResolver = { resolve, reject };
954
- });
955
- socket.onmessage = (event) => this.#onMessage(event);
956
- socket.onclose = () => this.#onClose();
957
- socket.onerror = (event) => this.#onError(event.error);
958
- }
959
- allocateBid() {
960
- return this.#nextBid++;
961
- }
962
- allocateRpcId() {
963
- return `v2_rpc_${this.#nextRpcId++}`;
964
- }
965
- sendHello() {
966
- if (this.#sentHello)
967
- return;
968
- this.#sentHello = true;
969
- const hello = {
970
- t: "hello",
971
- protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
972
- capabilities: [...this.#capabilities]
973
- };
974
- this.#socket.send(stringifyTransportV2ControlMsg(hello));
975
- }
976
- get handshake() {
977
- return this.#handshakePromise;
515
+ function deserializeRequest(serialized, getStream) {
516
+ let body = null;
517
+ if (serialized.body) {
518
+ switch (serialized.body.type) {
519
+ case "bytes":
520
+ body = base64Decode(serialized.body.data);
521
+ break;
522
+ case "stream":
523
+ if (getStream) {
524
+ body = getStream(serialized.body.sid) ?? null;
525
+ }
526
+ break;
527
+ }
978
528
  }
979
- get negotiated() {
980
- return this.#negotiated;
529
+ return new Request(serialized.url, {
530
+ method: serialized.method,
531
+ headers: serialized.headers,
532
+ body,
533
+ redirect: serialized.redirect
534
+ });
535
+ }
536
+ async function serializeResponse(response, options) {
537
+ const streams = [];
538
+ const threshold = options?.httpThreshold ?? HTTP_TRANSFER_THRESHOLD;
539
+ const headers = [];
540
+ response.headers.forEach((value, key) => {
541
+ headers.push([key, value]);
542
+ });
543
+ let body = null;
544
+ if (response.body) {
545
+ const bytes = await response.arrayBuffer();
546
+ if (bytes.byteLength > threshold) {
547
+ const sid = nextStreamId();
548
+ const payload = new Uint8Array(bytes);
549
+ const frameSize = HTTP_TRANSFER_THRESHOLD;
550
+ const stream = new ReadableStream({
551
+ start(controller) {
552
+ for (let offset = 0;offset < payload.byteLength; offset += frameSize) {
553
+ controller.enqueue(payload.slice(offset, Math.min(offset + frameSize, payload.byteLength)));
554
+ }
555
+ controller.close();
556
+ }
557
+ });
558
+ streams.push({ sid, stream });
559
+ body = { type: "stream", sid };
560
+ } else if (bytes.byteLength > 0) {
561
+ body = { type: "bytes", data: base64Encode(new Uint8Array(bytes)) };
562
+ }
981
563
  }
982
- sendText(message) {
983
- if (this.#closed) {
984
- throw new Error("cannot send on a closed v2 codec");
564
+ return {
565
+ serialized: {
566
+ status: response.status,
567
+ statusText: response.statusText,
568
+ headers,
569
+ body
570
+ },
571
+ streams
572
+ };
573
+ }
574
+ function deserializeResponse(serialized, getStream) {
575
+ let body = null;
576
+ if (serialized.body) {
577
+ switch (serialized.body.type) {
578
+ case "bytes":
579
+ body = base64Decode(serialized.body.data);
580
+ break;
581
+ case "stream":
582
+ if (getStream) {
583
+ body = getStream(serialized.body.sid) ?? null;
584
+ }
585
+ break;
985
586
  }
986
- this.#socket.send(message);
987
587
  }
988
- sendBinary(frame) {
989
- if (this.#closed) {
990
- throw new Error("cannot send on a closed v2 codec");
991
- }
992
- this.#socket.send(frame);
588
+ return new Response(body, {
589
+ status: serialized.status,
590
+ statusText: serialized.statusText,
591
+ headers: serialized.headers
592
+ });
593
+ }
594
+ async function serializeValue(value, options) {
595
+ const streams = [];
596
+ const result = await serializeValueInternal(value, streams, options);
597
+ return { value: result, streams };
598
+ }
599
+ async function serializeValueInternal(value, streams, options) {
600
+ if (value === null || value === undefined) {
601
+ return value;
993
602
  }
994
- call(method, params = []) {
995
- const id = this.allocateRpcId();
996
- const call = { t: "rpc.call", id, method, params };
997
- const promise = new Promise((resolve, reject) => {
998
- this.#pendingRpc.set(id, { resolve, reject });
999
- });
1000
- this.sendText(JSON.stringify(call));
1001
- return promise;
603
+ if (value instanceof Request) {
604
+ const { serialized, streams: reqStreams } = await serializeRequest(value, options);
605
+ streams.push(...reqStreams);
606
+ return { __type: "Request", ...serialized };
1002
607
  }
1003
- respondOk(id, result) {
1004
- const reply = { t: "rpc.ok", id, result };
1005
- this.sendText(JSON.stringify(reply));
608
+ if (value instanceof Response) {
609
+ const { serialized, streams: resStreams } = await serializeResponse(value, options);
610
+ streams.push(...resStreams);
611
+ return { __type: "Response", ...serialized };
1006
612
  }
1007
- respondErr(id, error) {
1008
- const reply = { t: "rpc.err", id, error };
1009
- this.sendText(JSON.stringify(reply));
613
+ if (value instanceof ReadableStream) {
614
+ const sid = nextStreamId();
615
+ streams.push({ sid, stream: value });
616
+ return { __type: "ReadableStream", sid };
1010
617
  }
1011
- sendWireError(err) {
1012
- const frame = { t: "error", ...err };
1013
- this.sendText(JSON.stringify(frame));
618
+ if (value instanceof Uint8Array) {
619
+ return { __type: "Uint8Array", data: base64Encode(value) };
1014
620
  }
1015
- openBodyReader(bid) {
1016
- return this.bodyReaders.getOrOpen(bid);
621
+ if (value instanceof ArrayBuffer) {
622
+ return { __type: "ArrayBuffer", data: base64Encode(new Uint8Array(value)) };
1017
623
  }
1018
- setRpcCallHandler(handler) {
1019
- this.#options = { ...this.#options, onRpcCall: handler };
624
+ if (value instanceof Date) {
625
+ return { __devflare: "date", iso: value.toISOString() };
1020
626
  }
1021
- close(code, reason) {
1022
- if (this.#closed)
1023
- return;
1024
- this.#closed = true;
1025
- this.#failPending(new Error("v2 transport closed"));
1026
- this.#socket.close(code, reason);
627
+ if (value instanceof URL) {
628
+ return { __devflare: "url", href: value.href };
1027
629
  }
1028
- get isClosed() {
1029
- return this.#closed;
630
+ if (value instanceof Error) {
631
+ const encoded = {
632
+ __devflare: "error",
633
+ name: value.name,
634
+ message: value.message
635
+ };
636
+ if (value.stack)
637
+ encoded.stack = value.stack;
638
+ return encoded;
1030
639
  }
1031
- #onMessage(event) {
1032
- const { data } = event;
1033
- if (typeof data === "string") {
1034
- this.#onText(data);
1035
- } else if (data instanceof Uint8Array) {
1036
- this.#onBinary(data);
1037
- } else if (data instanceof ArrayBuffer) {
1038
- this.#onBinary(new Uint8Array(data));
640
+ if (value instanceof Map) {
641
+ const entries = [];
642
+ for (const [k, v] of value.entries()) {
643
+ entries.push([
644
+ await serializeValueInternal(k, streams, options),
645
+ await serializeValueInternal(v, streams, options)
646
+ ]);
1039
647
  }
648
+ return { __devflare: "map", entries };
1040
649
  }
1041
- #onText(data) {
1042
- let msg;
1043
- try {
1044
- msg = parseTransportV2ControlMsg(data);
1045
- } catch {
1046
- const rpc = tryParseRpcMsg(data);
1047
- if (rpc !== null) {
1048
- this.#onRpcMessage(rpc);
1049
- return;
1050
- }
1051
- const wireErr = tryParseWireError(data);
1052
- if (wireErr !== null) {
1053
- this.#options.onWireError?.(wireErr);
1054
- return;
1055
- }
1056
- this.#options.onUnknownControl?.(data);
1057
- return;
1058
- }
1059
- switch (msg.t) {
1060
- case "hello":
1061
- this.#onHello(msg);
1062
- break;
1063
- case "welcome":
1064
- this.#onWelcome(msg);
1065
- break;
1066
- case "body.open":
1067
- this.bodyReaders.getOrOpen(msg.bid);
1068
- break;
1069
- case "body.end":
1070
- this.bodyReaders.end(msg.bid);
1071
- break;
1072
- case "body.abort":
1073
- this.bodyReaders.abort(msg.bid, msg.error);
1074
- break;
650
+ if (value instanceof Set) {
651
+ const values = [];
652
+ for (const v of value.values()) {
653
+ values.push(await serializeValueInternal(v, streams, options));
1075
654
  }
655
+ return { __devflare: "set", values };
1076
656
  }
1077
- #onBinary(data) {
1078
- let frame;
1079
- try {
1080
- frame = decodeTransportV2BinaryFrame(data);
1081
- } catch {
1082
- return;
1083
- }
1084
- if (frame.kind === TransportV2BinaryKind.BodyChunk) {
1085
- this.bodyReaders.pushChunk(frame);
1086
- return;
657
+ if (Array.isArray(value)) {
658
+ return Promise.all(value.map((v) => serializeValueInternal(v, streams, options)));
659
+ }
660
+ if (typeof value === "object") {
661
+ const result = {};
662
+ for (const [k, v] of Object.entries(value)) {
663
+ result[k] = await serializeValueInternal(v, streams, options);
1087
664
  }
1088
- this.#options.onUnknownBinary?.(frame);
665
+ return result;
1089
666
  }
1090
- #onRpcMessage(msg) {
1091
- switch (msg.t) {
1092
- case "rpc.call":
1093
- this.#options.onRpcCall?.(msg);
1094
- break;
1095
- case "rpc.ok": {
1096
- const pending = this.#pendingRpc.get(msg.id);
1097
- if (pending !== undefined) {
1098
- this.#pendingRpc.delete(msg.id);
1099
- pending.resolve(msg.result);
667
+ return value;
668
+ }
669
+ function deserializeValue(value, getStream) {
670
+ if (value === null || value === undefined) {
671
+ return value;
672
+ }
673
+ if (typeof value === "object" && value !== null) {
674
+ const obj = value;
675
+ if (typeof obj.__devflare === "string") {
676
+ switch (obj.__devflare) {
677
+ case "date":
678
+ return new Date(obj.iso);
679
+ case "url":
680
+ return new URL(obj.href);
681
+ case "error": {
682
+ const err = new Error(obj.message);
683
+ if (typeof obj.name === "string")
684
+ err.name = obj.name;
685
+ if (typeof obj.stack === "string")
686
+ err.stack = obj.stack;
687
+ return err;
1100
688
  }
1101
- break;
1102
- }
1103
- case "rpc.err": {
1104
- const pending = this.#pendingRpc.get(msg.id);
1105
- if (pending !== undefined) {
1106
- this.#pendingRpc.delete(msg.id);
1107
- const err = new Error(msg.error.message);
1108
- Object.assign(err, { code: msg.error.code, details: msg.error.details });
1109
- pending.reject(err);
689
+ case "map": {
690
+ const entries = obj.entries ?? [];
691
+ const map = new Map;
692
+ for (const [k, v] of entries) {
693
+ map.set(deserializeValue(k, getStream), deserializeValue(v, getStream));
694
+ }
695
+ return map;
696
+ }
697
+ case "set": {
698
+ const values = obj.values ?? [];
699
+ const set = new Set;
700
+ for (const v of values) {
701
+ set.add(deserializeValue(v, getStream));
702
+ }
703
+ return set;
1110
704
  }
1111
- break;
1112
705
  }
1113
706
  }
1114
- }
1115
- #onHello(msg) {
1116
- if (this.#receivedHello) {
1117
- this.close(TRANSPORT_V2_UNSUPPORTED_VERSION_CLOSE_CODE, "duplicate hello");
1118
- return;
707
+ if (obj.__type === "Request") {
708
+ return deserializeRequest(obj, getStream);
1119
709
  }
1120
- this.#receivedHello = true;
1121
- const negotiated = negotiateTransportV2Capabilities(this.#capabilities, msg.capabilities);
1122
- const welcome = {
1123
- t: "welcome",
1124
- protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
1125
- capabilities: negotiated
1126
- };
1127
- this.#socket.send(stringifyTransportV2ControlMsg(welcome));
1128
- this.#completeHandshake({ protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION, capabilities: negotiated });
710
+ if (obj.__type === "Response") {
711
+ return deserializeResponse(obj, getStream);
712
+ }
713
+ if (obj.__type === "ReadableStream") {
714
+ const sid = obj.sid;
715
+ return getStream?.(sid) ?? null;
716
+ }
717
+ if (obj.__type === "Uint8Array") {
718
+ return base64Decode(obj.data);
719
+ }
720
+ if (obj.__type === "ArrayBuffer") {
721
+ return base64Decode(obj.data).buffer;
722
+ }
723
+ if (obj.__type === "R2Object") {
724
+ return deserializeR2Object(obj);
725
+ }
726
+ if (obj.__type === "R2ObjectBody") {
727
+ return deserializeR2ObjectBody(obj);
728
+ }
729
+ if (Array.isArray(value)) {
730
+ return value.map((v) => deserializeValue(v, getStream));
731
+ }
732
+ const result = {};
733
+ for (const [k, v] of Object.entries(obj)) {
734
+ result[k] = deserializeValue(v, getStream);
735
+ }
736
+ return result;
737
+ }
738
+ return value;
739
+ }
740
+ function applySerializedHttpMetadata(headers, httpMetadata) {
741
+ if (httpMetadata?.contentType) {
742
+ headers.set("Content-Type", httpMetadata.contentType);
1129
743
  }
1130
- #onWelcome(msg) {
1131
- if (this.#receivedWelcome)
1132
- return;
1133
- this.#receivedWelcome = true;
1134
- this.#completeHandshake({
1135
- protocolVersion: TRANSPORT_V2_PROTOCOL_VERSION,
1136
- capabilities: msg.capabilities
1137
- });
744
+ if (httpMetadata?.contentLanguage) {
745
+ headers.set("Content-Language", httpMetadata.contentLanguage);
1138
746
  }
1139
- #completeHandshake(result) {
1140
- if (this.#negotiated !== null)
1141
- return;
1142
- this.#negotiated = result;
1143
- this.#handshakeResolver?.resolve(result);
1144
- this.#handshakeResolver = null;
747
+ if (httpMetadata?.contentDisposition) {
748
+ headers.set("Content-Disposition", httpMetadata.contentDisposition);
1145
749
  }
1146
- #onClose() {
1147
- if (this.#closed) {
1148
- return;
1149
- }
1150
- this.#closed = true;
1151
- this.#failPending(new Error("v2 transport closed"));
750
+ if (httpMetadata?.contentEncoding) {
751
+ headers.set("Content-Encoding", httpMetadata.contentEncoding);
1152
752
  }
1153
- #onError(error) {
1154
- const wrapped = error instanceof Error ? error : new Error(String(error));
1155
- this.#failPending(wrapped);
753
+ if (httpMetadata?.cacheControl) {
754
+ headers.set("Cache-Control", httpMetadata.cacheControl);
1156
755
  }
1157
- #failPending(error) {
1158
- const resolver = this.#handshakeResolver;
1159
- if (resolver !== null) {
1160
- this.#handshakeResolver = null;
1161
- resolver.reject(error);
756
+ if (httpMetadata?.cacheExpiry) {
757
+ headers.set("Expires", new Date(httpMetadata.cacheExpiry).toUTCString());
758
+ }
759
+ }
760
+ function createSerializedR2Metadata(serialized) {
761
+ return {
762
+ key: serialized.key,
763
+ version: serialized.version,
764
+ size: serialized.size,
765
+ etag: serialized.etag,
766
+ httpEtag: serialized.httpEtag,
767
+ checksums: serialized.checksums,
768
+ uploaded: serialized.uploaded ? new Date(serialized.uploaded) : new Date,
769
+ httpMetadata: serialized.httpMetadata,
770
+ customMetadata: serialized.customMetadata,
771
+ range: serialized.range,
772
+ storageClass: serialized.storageClass,
773
+ writeHttpMetadata(headers) {
774
+ applySerializedHttpMetadata(headers, serialized.httpMetadata);
1162
775
  }
1163
- for (const pending of this.#pendingRpc.values()) {
1164
- pending.reject(error);
776
+ };
777
+ }
778
+ function deserializeR2Object(obj) {
779
+ const serialized = obj;
780
+ return {
781
+ ...createSerializedR2Metadata(serialized)
782
+ };
783
+ }
784
+ function deserializeR2ObjectBody(obj) {
785
+ const serialized = obj;
786
+ const bodyBytes = serialized.bodyData ? base64Decode(serialized.bodyData) : new Uint8Array(0);
787
+ const r2ObjectBody = {
788
+ ...createSerializedR2Metadata(serialized),
789
+ body: new ReadableStream({
790
+ start(controller) {
791
+ controller.enqueue(bodyBytes);
792
+ controller.close();
793
+ }
794
+ }),
795
+ bodyUsed: false,
796
+ async arrayBuffer() {
797
+ const copy = new Uint8Array(bodyBytes.byteLength);
798
+ copy.set(bodyBytes);
799
+ return copy.buffer;
800
+ },
801
+ async text() {
802
+ return new TextDecoder().decode(bodyBytes);
803
+ },
804
+ async json() {
805
+ const text = new TextDecoder().decode(bodyBytes);
806
+ return JSON.parse(text);
807
+ },
808
+ async blob() {
809
+ const contentType = serialized.httpMetadata?.contentType || "application/octet-stream";
810
+ const buffer = bodyBytes.buffer.slice(bodyBytes.byteOffset, bodyBytes.byteOffset + bodyBytes.byteLength);
811
+ return new Blob([buffer], { type: contentType });
1165
812
  }
1166
- this.#pendingRpc.clear();
1167
- }
813
+ };
814
+ return r2ObjectBody;
1168
815
  }
1169
- function tryParseRpcMsg(data) {
1170
- let parsed;
1171
- try {
1172
- parsed = JSON.parse(data);
1173
- } catch {
1174
- return null;
1175
- }
1176
- if (typeof parsed !== "object" || parsed === null || !("t" in parsed))
1177
- return null;
1178
- const t = parsed.t;
1179
- if (t === "rpc.call" || t === "rpc.ok" || t === "rpc.err") {
1180
- return parsed;
816
+ function base64Encode(bytes) {
817
+ if (typeof Buffer !== "undefined") {
818
+ return Buffer.from(bytes).toString("base64");
1181
819
  }
1182
- return null;
1183
- }
1184
- function tryParseWireError(data) {
1185
- let parsed;
1186
- try {
1187
- parsed = JSON.parse(data);
1188
- } catch {
1189
- return null;
820
+ let binary = "";
821
+ for (let i = 0;i < bytes.byteLength; i++) {
822
+ binary += String.fromCharCode(bytes[i]);
1190
823
  }
1191
- if (typeof parsed !== "object" || parsed === null || !("t" in parsed))
1192
- return null;
1193
- const candidate = parsed;
1194
- if (candidate.t !== "error")
1195
- return null;
1196
- const scope = candidate.scope;
1197
- if (scope !== "transport" && scope !== "rpc" && scope !== "stream" && scope !== "ws")
1198
- return null;
1199
- const err = candidate.error;
1200
- if (!err || typeof err.code !== "string" || typeof err.message !== "string")
1201
- return null;
1202
- return parsed;
824
+ return btoa(binary);
1203
825
  }
1204
-
1205
- // src/bridge/log.ts
1206
- var isDebugEnabled = () => {
1207
- try {
1208
- const env = globalThis.process?.env;
1209
- return Boolean(env?.DEVFLARE_DEBUG_BRIDGE);
1210
- } catch {
1211
- return false;
826
+ function base64Decode(str) {
827
+ if (typeof Buffer !== "undefined") {
828
+ return new Uint8Array(Buffer.from(str, "base64"));
1212
829
  }
1213
- };
1214
- var bridgeLog = {
1215
- warn(message, error) {
1216
- if (!isDebugEnabled())
1217
- return;
1218
- console.warn(`[devflare:bridge] ${message}`, error);
1219
- },
1220
- debug(message, error) {
1221
- if (!isDebugEnabled())
1222
- return;
1223
- console.debug(`[devflare:bridge] ${message}`, error);
830
+ const binary = atob(str);
831
+ const bytes = new Uint8Array(binary.length);
832
+ for (let i = 0;i < binary.length; i++) {
833
+ bytes[i] = binary.charCodeAt(i);
1224
834
  }
1225
- };
835
+ return bytes;
836
+ }
1226
837
 
1227
838
  // src/bridge/client.ts
1228
839
  class BridgeWsAdapter {
@@ -1796,10 +1407,16 @@ function createR2Proxy(client, bindingName) {
1796
1407
  return client.call(`${bindingName}.r2.list`, [options]);
1797
1408
  },
1798
1409
  async createMultipartUpload(key, options) {
1799
- return client.call(`${bindingName}.r2.createMultipartUpload`, [key, options]);
1410
+ return client.call(`${bindingName}.r2.createMultipartUpload`, [
1411
+ key,
1412
+ options
1413
+ ]);
1800
1414
  },
1801
1415
  async resumeMultipartUpload(key, uploadId) {
1802
- return client.call(`${bindingName}.r2.resumeMultipartUpload`, [key, uploadId]);
1416
+ return client.call(`${bindingName}.r2.resumeMultipartUpload`, [
1417
+ key,
1418
+ uploadId
1419
+ ]);
1803
1420
  }
1804
1421
  };
1805
1422
  }
@@ -1813,7 +1430,7 @@ function getValueSize(value) {
1813
1430
  if (typeof value === "string")
1814
1431
  return new TextEncoder().encode(value).byteLength;
1815
1432
  if (value instanceof ReadableStream)
1816
- return Infinity;
1433
+ return Number.POSITIVE_INFINITY;
1817
1434
  return 0;
1818
1435
  }
1819
1436
  function createD1Proxy(client, bindingName) {
@@ -1861,14 +1478,27 @@ function createD1StatementProxy(client, bindingName, sql, bindings) {
1861
1478
  function createDOProxy(client, bindingName, proxyOptions = {}) {
1862
1479
  return {
1863
1480
  idFromName(name) {
1864
- return createDOIdProxy(client, bindingName, { type: "name", value: name, jurisdiction: proxyOptions.jurisdiction });
1481
+ return createDOIdProxy(client, bindingName, {
1482
+ type: "name",
1483
+ value: name,
1484
+ jurisdiction: proxyOptions.jurisdiction
1485
+ });
1865
1486
  },
1866
1487
  idFromString(hexId) {
1867
- return createDOIdProxy(client, bindingName, { type: "hex", value: hexId, jurisdiction: proxyOptions.jurisdiction });
1488
+ return createDOIdProxy(client, bindingName, {
1489
+ type: "hex",
1490
+ value: hexId,
1491
+ jurisdiction: proxyOptions.jurisdiction
1492
+ });
1868
1493
  },
1869
1494
  newUniqueId(options) {
1870
1495
  const tempId = crypto.randomUUID().replace(/-/g, "");
1871
- return createDOIdProxy(client, bindingName, { type: "unique", value: tempId, options, jurisdiction: proxyOptions.jurisdiction });
1496
+ return createDOIdProxy(client, bindingName, {
1497
+ type: "unique",
1498
+ value: tempId,
1499
+ options,
1500
+ jurisdiction: proxyOptions.jurisdiction
1501
+ });
1872
1502
  },
1873
1503
  get(id) {
1874
1504
  const idProxy = id;
@@ -1904,13 +1534,19 @@ function createDOStubProxy(client, bindingName, idInfo, proxyOptions = {}) {
1904
1534
  return resolvedId;
1905
1535
  switch (idInfo.type) {
1906
1536
  case "name":
1907
- resolvedId = await client.call(`${bindingName}.do.idFromName`, [idInfo.value, idInfo.jurisdiction]);
1537
+ resolvedId = await client.call(`${bindingName}.do.idFromName`, [
1538
+ idInfo.value,
1539
+ idInfo.jurisdiction
1540
+ ]);
1908
1541
  break;
1909
1542
  case "hex":
1910
1543
  resolvedId = { __type: "DOId", hex: idInfo.value };
1911
1544
  break;
1912
1545
  case "unique":
1913
- resolvedId = await client.call(`${bindingName}.do.newUniqueId`, [idInfo.options, idInfo.jurisdiction]);
1546
+ resolvedId = await client.call(`${bindingName}.do.newUniqueId`, [
1547
+ idInfo.options,
1548
+ idInfo.jurisdiction
1549
+ ]);
1914
1550
  break;
1915
1551
  }
1916
1552
  return resolvedId;
@@ -1987,298 +1623,677 @@ function createDOStubProxy(client, bindingName, idInfo, proxyOptions = {}) {
1987
1623
  }
1988
1624
  };
1989
1625
  },
1990
- get id() {
1991
- return createDOIdProxy(client, bindingName, idInfo);
1626
+ get id() {
1627
+ return createDOIdProxy(client, bindingName, idInfo);
1628
+ },
1629
+ get name() {
1630
+ return idInfo.type === "name" ? idInfo.value : undefined;
1631
+ }
1632
+ };
1633
+ return new Proxy(stubBase, {
1634
+ get(target, prop) {
1635
+ if (prop in target) {
1636
+ return target[prop];
1637
+ }
1638
+ if (typeof prop === "symbol") {
1639
+ return;
1640
+ }
1641
+ if (prop === "then" || prop === "catch" || prop === "finally") {
1642
+ return;
1643
+ }
1644
+ return async (...args) => {
1645
+ const id = await resolveId();
1646
+ let result = await client.call(`${bindingName}.do.rpc`, [bindingName, id, prop, args]);
1647
+ if (transformResult) {
1648
+ result = transformResult(result);
1649
+ }
1650
+ return result;
1651
+ };
1652
+ }
1653
+ });
1654
+ }
1655
+ function isResponseLike(value) {
1656
+ return value instanceof Response;
1657
+ }
1658
+ function createServiceProxy(client, bindingName, proxyOptions = {}) {
1659
+ const { transformResult } = proxyOptions;
1660
+ const serviceBase = {
1661
+ async fetch(input, init) {
1662
+ const request = input instanceof Request ? input : new Request(input, init);
1663
+ const { serialized } = await serializeRequest(request);
1664
+ const result = await client.call(`${bindingName}.service.fetch`, [serialized]);
1665
+ if (isResponseLike(result)) {
1666
+ return result;
1667
+ }
1668
+ return deserializeResponse(result);
1669
+ }
1670
+ };
1671
+ return new Proxy(serviceBase, {
1672
+ get(target, prop) {
1673
+ if (typeof prop !== "string") {
1674
+ return;
1675
+ }
1676
+ if (prop in target) {
1677
+ return target[prop];
1678
+ }
1679
+ if (prop === "then" || prop === "catch" || prop === "finally") {
1680
+ return;
1681
+ }
1682
+ if (prop === "toString") {
1683
+ return () => `[DevflareServiceBinding ${bindingName}]`;
1684
+ }
1685
+ return async (...args) => {
1686
+ let result = await client.call(`${bindingName}.service.rpc`, [prop, args]);
1687
+ if (transformResult) {
1688
+ result = transformResult(result);
1689
+ }
1690
+ return result;
1691
+ };
1692
+ }
1693
+ });
1694
+ }
1695
+ var EMPTY_QUEUE_METRICS = {
1696
+ backlogCount: 0,
1697
+ backlogBytes: 0
1698
+ };
1699
+ function createQueueResponse() {
1700
+ return { metadata: { metrics: EMPTY_QUEUE_METRICS } };
1701
+ }
1702
+ function createQueueProxy(client, bindingName) {
1703
+ return {
1704
+ async metrics() {
1705
+ return EMPTY_QUEUE_METRICS;
1706
+ },
1707
+ async send(message, options) {
1708
+ await client.call(`${bindingName}.queue.send`, [message, options]);
1709
+ return createQueueResponse();
1710
+ },
1711
+ async sendBatch(messages, options) {
1712
+ await client.call(`${bindingName}.queue.sendBatch`, [messages, options]);
1713
+ return createQueueResponse();
1714
+ }
1715
+ };
1716
+ }
1717
+ function createAIProxy(client, bindingName) {
1718
+ return {
1719
+ async run(model, inputs, options) {
1720
+ return client.call(`${bindingName}.ai.run`, [model, inputs, options]);
1721
+ }
1722
+ };
1723
+ }
1724
+ function createSendEmailProxy(client, bindingName) {
1725
+ return {
1726
+ async send(message) {
1727
+ return client.call(`${bindingName}.email.send`, [message]);
1728
+ }
1729
+ };
1730
+ }
1731
+ function createWorkflowInstanceProxy(client, bindingName, id) {
1732
+ return {
1733
+ id,
1734
+ async pause() {
1735
+ await client.call(`${bindingName}.workflow.pause`, [id]);
1992
1736
  },
1993
- get name() {
1994
- return idInfo.type === "name" ? idInfo.value : undefined;
1737
+ async resume() {
1738
+ await client.call(`${bindingName}.workflow.resume`, [id]);
1739
+ },
1740
+ async terminate() {
1741
+ await client.call(`${bindingName}.workflow.terminate`, [id]);
1742
+ },
1743
+ async restart() {
1744
+ await client.call(`${bindingName}.workflow.restart`, [id]);
1745
+ },
1746
+ async status() {
1747
+ return client.call(`${bindingName}.workflow.status`, [id]);
1748
+ },
1749
+ async sendEvent(event) {
1750
+ await client.call(`${bindingName}.workflow.sendEvent`, [id, event]);
1995
1751
  }
1996
1752
  };
1997
- return new Proxy(stubBase, {
1753
+ }
1754
+ function createWorkflowProxy(client, bindingName) {
1755
+ const toInstance = (value) => {
1756
+ const id = value?.id;
1757
+ if (typeof id !== "string") {
1758
+ throw new Error(`Workflow ${bindingName} returned an instance without a string id.`);
1759
+ }
1760
+ return createWorkflowInstanceProxy(client, bindingName, id);
1761
+ };
1762
+ return {
1763
+ async create(options) {
1764
+ return toInstance(await client.call(`${bindingName}.workflow.create`, [options]));
1765
+ },
1766
+ async get(id) {
1767
+ return toInstance(await client.call(`${bindingName}.workflow.get`, [id]));
1768
+ }
1769
+ };
1770
+ }
1771
+ var globalBindingHints = {};
1772
+ function createEnvProxy(options = {}) {
1773
+ const client = options.client ?? getClient();
1774
+ const bindingProxies = new Map;
1775
+ const doProxyOptions = { transformResult: options.transformResult };
1776
+ const serviceProxyOptions = { transformResult: options.transformResult };
1777
+ const strict = options.strict === true;
1778
+ const hints = { ...globalBindingHints, ...options.hints };
1779
+ return new Proxy({}, {
1998
1780
  get(target, prop) {
1999
- if (prop in target) {
2000
- return target[prop];
1781
+ if (typeof prop !== "string")
1782
+ return;
1783
+ if (bindingProxies.has(prop)) {
1784
+ return bindingProxies.get(prop);
2001
1785
  }
2002
- if (typeof prop === "symbol") {
1786
+ const hint = hints[prop];
1787
+ if (!hint && strict) {
2003
1788
  return;
2004
1789
  }
2005
- if (prop === "then" || prop === "catch" || prop === "finally") {
1790
+ let proxy;
1791
+ switch (hint) {
1792
+ case "kv":
1793
+ proxy = createKVProxy(client, prop);
1794
+ break;
1795
+ case "r2":
1796
+ proxy = createR2Proxy(client, prop);
1797
+ break;
1798
+ case "d1":
1799
+ proxy = createD1Proxy(client, prop);
1800
+ break;
1801
+ case "do":
1802
+ proxy = createDOProxy(client, prop, doProxyOptions);
1803
+ break;
1804
+ case "queue":
1805
+ proxy = createQueueProxy(client, prop);
1806
+ break;
1807
+ case "ai":
1808
+ proxy = createAIProxy(client, prop);
1809
+ break;
1810
+ case "service":
1811
+ proxy = createServiceProxy(client, prop, serviceProxyOptions);
1812
+ break;
1813
+ case "sendEmail":
1814
+ proxy = createSendEmailProxy(client, prop);
1815
+ break;
1816
+ case "workflow":
1817
+ proxy = createWorkflowProxy(client, prop);
1818
+ break;
1819
+ case "secret":
1820
+ case "var":
1821
+ proxy = createSimpleBindingProxy(client, prop);
1822
+ break;
1823
+ default:
1824
+ proxy = createGenericBindingProxy(client, prop);
1825
+ }
1826
+ bindingProxies.set(prop, proxy);
1827
+ return proxy;
1828
+ },
1829
+ has(target, prop) {
1830
+ return typeof prop === "string" && (!strict || (prop in hints));
1831
+ },
1832
+ ownKeys() {
1833
+ return Object.keys(hints);
1834
+ },
1835
+ getOwnPropertyDescriptor(target, prop) {
1836
+ if (typeof prop === "string" && (!strict || (prop in hints))) {
1837
+ return { configurable: true, enumerable: true, writable: false };
1838
+ }
1839
+ return;
1840
+ }
1841
+ });
1842
+ }
1843
+ function createGenericBindingProxy(client, bindingName) {
1844
+ return new Proxy({}, {
1845
+ get(target, prop) {
1846
+ if (typeof prop !== "string")
2006
1847
  return;
1848
+ if (["get", "put", "delete", "list", "getWithMetadata"].includes(prop)) {
1849
+ return createKVProxy(client, bindingName)[prop];
1850
+ }
1851
+ if (["idFromName", "idFromString", "newUniqueId", "get"].includes(prop)) {
1852
+ return createDOProxy(client, bindingName)[prop];
1853
+ }
1854
+ if (["prepare", "batch", "exec", "dump"].includes(prop)) {
1855
+ return createD1Proxy(client, bindingName)[prop];
1856
+ }
1857
+ if (["head"].includes(prop)) {
1858
+ return createR2Proxy(client, bindingName)[prop];
2007
1859
  }
2008
1860
  return async (...args) => {
2009
- const id = await resolveId();
2010
- let result = await client.call(`${bindingName}.do.rpc`, [
2011
- bindingName,
2012
- id,
2013
- prop,
2014
- args
2015
- ]);
2016
- if (transformResult) {
2017
- result = transformResult(result);
2018
- }
2019
- return result;
1861
+ return client.call(`${bindingName}.${prop}`, args);
2020
1862
  };
2021
1863
  }
2022
1864
  });
2023
1865
  }
2024
- function isResponseLike(value) {
2025
- return value instanceof Response;
1866
+ function createSimpleBindingProxy(client, bindingName) {
1867
+ let cachedValue;
1868
+ let fetched = false;
1869
+ let pendingValue = null;
1870
+ const loadValue = () => {
1871
+ if (fetched) {
1872
+ return Promise.resolve(cachedValue);
1873
+ }
1874
+ if (!pendingValue) {
1875
+ pendingValue = client.call(`${bindingName}.var.value`, []).then((value) => {
1876
+ cachedValue = value;
1877
+ fetched = true;
1878
+ return value;
1879
+ }).catch((error) => {
1880
+ pendingValue = null;
1881
+ throw error;
1882
+ });
1883
+ }
1884
+ return pendingValue;
1885
+ };
1886
+ return {
1887
+ then(resolve, reject) {
1888
+ return loadValue().then(resolve ?? undefined, reject ?? undefined);
1889
+ },
1890
+ toString() {
1891
+ if (!fetched)
1892
+ throw new Error(`Binding ${bindingName} not yet fetched. Use await.`);
1893
+ return String(cachedValue);
1894
+ }
1895
+ };
2026
1896
  }
2027
- function createServiceProxy(client, bindingName, proxyOptions = {}) {
2028
- const { transformResult } = proxyOptions;
2029
- const serviceBase = {
2030
- async fetch(input, init) {
2031
- const request = input instanceof Request ? input : new Request(input, init);
2032
- const { serialized } = await serializeRequest(request);
2033
- const result = await client.call(`${bindingName}.service.fetch`, [serialized]);
2034
- if (isResponseLike(result)) {
2035
- return result;
2036
- }
2037
- return deserializeResponse(result);
1897
+ var globalEnvProxy = null;
1898
+ var bridgeEnv = new Proxy({}, {
1899
+ get(target, prop) {
1900
+ if (!globalEnvProxy) {
1901
+ globalEnvProxy = createEnvProxy({ lazy: true });
2038
1902
  }
1903
+ return globalEnvProxy[prop];
1904
+ }
1905
+ });
1906
+ function initEnv(options = {}) {
1907
+ globalEnvProxy = createEnvProxy(options);
1908
+ return globalEnvProxy;
1909
+ }
1910
+ function setBindingHints(hints) {
1911
+ globalBindingHints = { ...globalBindingHints, ...hints };
1912
+ globalEnvProxy = null;
1913
+ }
1914
+
1915
+ // src/runtime/context-events.ts
1916
+ function createLocals(locals) {
1917
+ return locals ?? {};
1918
+ }
1919
+ function prepareEventShell(env, options = {}) {
1920
+ return {
1921
+ env: wrapEnvSendEmailBindings(env),
1922
+ locals: createLocals(options.locals)
2039
1923
  };
2040
- return new Proxy(serviceBase, {
2041
- get(target, prop) {
2042
- if (typeof prop !== "string") {
2043
- return;
2044
- }
2045
- if (prop in target) {
2046
- return target[prop];
2047
- }
2048
- if (prop === "then" || prop === "catch" || prop === "finally") {
2049
- return;
1924
+ }
1925
+ function createAugmentedTarget(target, extra) {
1926
+ return new Proxy(target, {
1927
+ get(target2, prop) {
1928
+ if (prop in extra) {
1929
+ return extra[prop];
2050
1930
  }
2051
- if (prop === "toString") {
2052
- return () => `[DevflareServiceBinding ${bindingName}]`;
1931
+ const value = Reflect.get(target2, prop, target2);
1932
+ return typeof value === "function" ? value.bind(target2) : value;
1933
+ },
1934
+ has(target2, prop) {
1935
+ return prop in extra || prop in target2;
1936
+ },
1937
+ ownKeys(target2) {
1938
+ return Array.from(new Set([...Reflect.ownKeys(target2), ...Reflect.ownKeys(extra)]));
1939
+ },
1940
+ getOwnPropertyDescriptor(target2, prop) {
1941
+ if (prop in extra) {
1942
+ return {
1943
+ configurable: true,
1944
+ enumerable: true,
1945
+ writable: false,
1946
+ value: extra[prop]
1947
+ };
2053
1948
  }
2054
- return async (...args) => {
2055
- let result = await client.call(`${bindingName}.service.rpc`, [prop, args]);
2056
- if (transformResult) {
2057
- result = transformResult(result);
2058
- }
2059
- return result;
2060
- };
1949
+ return Reflect.getOwnPropertyDescriptor(target2, prop);
2061
1950
  }
2062
1951
  });
2063
1952
  }
2064
- var EMPTY_QUEUE_METRICS = {
2065
- backlogCount: 0,
2066
- backlogBytes: 0
2067
- };
2068
- function createQueueResponse() {
2069
- return { metadata: { metrics: EMPTY_QUEUE_METRICS } };
2070
- }
2071
- function createQueueProxy(client, bindingName) {
1953
+ function createBaseEvent(type, env, ctx, options = {}) {
1954
+ const shell = prepareEventShell(env, { locals: options.locals });
2072
1955
  return {
2073
- async metrics() {
2074
- return EMPTY_QUEUE_METRICS;
2075
- },
2076
- async send(message, options) {
2077
- await client.call(`${bindingName}.queue.send`, [message, options]);
2078
- return createQueueResponse();
2079
- },
2080
- async sendBatch(messages, options) {
2081
- await client.call(`${bindingName}.queue.sendBatch`, [messages, options]);
2082
- return createQueueResponse();
2083
- }
1956
+ type,
1957
+ ctx,
1958
+ ...shell,
1959
+ request: options.request ?? null,
1960
+ params: options.params
2084
1961
  };
2085
1962
  }
2086
- function createAIProxy(client, bindingName) {
2087
- return {
2088
- async run(model, inputs, options) {
2089
- return client.call(`${bindingName}.ai.run`, [model, inputs, options]);
2090
- }
2091
- };
1963
+ function createFetchEvent(request, env, ctx, options = {}) {
1964
+ const shell = prepareEventShell(env, { locals: options.locals });
1965
+ return createAugmentedTarget(request, {
1966
+ type: "fetch",
1967
+ ctx,
1968
+ ...shell,
1969
+ url: new URL(request.url),
1970
+ request,
1971
+ params: options.params ?? {}
1972
+ });
2092
1973
  }
2093
- function createSendEmailProxy(client, bindingName) {
2094
- return {
2095
- async send(message) {
2096
- return client.call(`${bindingName}.email.send`, [message]);
2097
- }
2098
- };
1974
+ function createQueueEvent(batch, env, ctx, options = {}) {
1975
+ const shell = prepareEventShell(env, { locals: options.locals });
1976
+ return createAugmentedTarget(batch, {
1977
+ type: "queue",
1978
+ ctx,
1979
+ ...shell,
1980
+ batch
1981
+ });
2099
1982
  }
2100
- function createWorkflowInstanceProxy(client, bindingName, id) {
1983
+ function createScheduledEvent(controller, env, ctx, options = {}) {
1984
+ const shell = prepareEventShell(env, { locals: options.locals });
1985
+ return createAugmentedTarget(controller, {
1986
+ type: "scheduled",
1987
+ ctx,
1988
+ ...shell,
1989
+ controller
1990
+ });
1991
+ }
1992
+ function createEmailEvent(message, env, ctx, options = {}) {
1993
+ const shell = prepareEventShell(env, { locals: options.locals });
1994
+ return createAugmentedTarget(message, {
1995
+ type: "email",
1996
+ ctx,
1997
+ ...shell,
1998
+ message
1999
+ });
2000
+ }
2001
+ function createTailEvent(events, env, ctx, options = {}) {
2002
+ const shell = prepareEventShell(env, { locals: options.locals });
2003
+ return createAugmentedTarget(events, {
2004
+ type: "tail",
2005
+ ctx,
2006
+ ...shell,
2007
+ events
2008
+ });
2009
+ }
2010
+ function createDurableObjectFetchEvent(request, env, state, options = {}) {
2011
+ const shell = prepareEventShell(env, { locals: options.locals });
2012
+ return createAugmentedTarget(request, {
2013
+ type: "durable-object-fetch",
2014
+ ctx: state,
2015
+ state,
2016
+ ...shell,
2017
+ request
2018
+ });
2019
+ }
2020
+ function createDurableObjectAlarmEvent(env, state, options = {}) {
2021
+ const shell = prepareEventShell(env, { locals: options.locals });
2101
2022
  return {
2102
- id,
2103
- async pause() {
2104
- await client.call(`${bindingName}.workflow.pause`, [id]);
2105
- },
2106
- async resume() {
2107
- await client.call(`${bindingName}.workflow.resume`, [id]);
2108
- },
2109
- async terminate() {
2110
- await client.call(`${bindingName}.workflow.terminate`, [id]);
2111
- },
2112
- async restart() {
2113
- await client.call(`${bindingName}.workflow.restart`, [id]);
2114
- },
2115
- async status() {
2116
- return client.call(`${bindingName}.workflow.status`, [id]);
2117
- },
2118
- async sendEvent(event) {
2119
- await client.call(`${bindingName}.workflow.sendEvent`, [id, event]);
2120
- }
2023
+ type: "durable-object-alarm",
2024
+ ctx: state,
2025
+ state,
2026
+ ...shell
2121
2027
  };
2122
2028
  }
2123
- function createWorkflowProxy(client, bindingName) {
2124
- const toInstance = (value) => {
2125
- const id = value?.id;
2126
- if (typeof id !== "string") {
2127
- throw new Error(`Workflow ${bindingName} returned an instance without a string id.`);
2029
+ function createDurableObjectWebSocketMessageEvent(ws, message, env, state, options = {}) {
2030
+ const shell = prepareEventShell(env, { locals: options.locals });
2031
+ return createAugmentedTarget(ws, {
2032
+ type: "durable-object-websocket-message",
2033
+ ctx: state,
2034
+ state,
2035
+ ...shell,
2036
+ ws,
2037
+ message
2038
+ });
2039
+ }
2040
+ function createDurableObjectWebSocketCloseEvent(ws, code, reason, wasClean, env, state, options = {}) {
2041
+ const shell = prepareEventShell(env, { locals: options.locals });
2042
+ return createAugmentedTarget(ws, {
2043
+ type: "durable-object-websocket-close",
2044
+ ctx: state,
2045
+ state,
2046
+ ...shell,
2047
+ ws,
2048
+ code,
2049
+ reason,
2050
+ wasClean
2051
+ });
2052
+ }
2053
+ function createDurableObjectWebSocketErrorEvent(ws, error, env, state, options = {}) {
2054
+ const shell = prepareEventShell(env, { locals: options.locals });
2055
+ return createAugmentedTarget(ws, {
2056
+ type: "durable-object-websocket-error",
2057
+ ctx: state,
2058
+ state,
2059
+ ...shell,
2060
+ ws,
2061
+ error
2062
+ });
2063
+ }
2064
+ function createDefaultEvent(env, ctx, request, type, locals) {
2065
+ switch (type) {
2066
+ case "fetch": {
2067
+ if (request && ctx) {
2068
+ return createFetchEvent(request, env, ctx, { locals });
2069
+ }
2070
+ return createBaseEvent(type, env, ctx, { locals, request });
2128
2071
  }
2129
- return createWorkflowInstanceProxy(client, bindingName, id);
2130
- };
2131
- return {
2132
- async create(options) {
2133
- return toInstance(await client.call(`${bindingName}.workflow.create`, [options]));
2134
- },
2135
- async get(id) {
2136
- return toInstance(await client.call(`${bindingName}.workflow.get`, [id]));
2072
+ case "durable-object-fetch": {
2073
+ if (request && ctx) {
2074
+ return createDurableObjectFetchEvent(request, env, ctx, { locals });
2075
+ }
2076
+ return createBaseEvent(type, env, ctx, { locals, request });
2137
2077
  }
2138
- };
2078
+ case "durable-object-alarm": {
2079
+ if (ctx) {
2080
+ return createDurableObjectAlarmEvent(env, ctx, { locals });
2081
+ }
2082
+ return createBaseEvent(type, env, ctx, { locals });
2083
+ }
2084
+ case "queue":
2085
+ case "scheduled":
2086
+ case "email":
2087
+ case "tail":
2088
+ case "durable-object-websocket-message":
2089
+ case "durable-object-websocket-close":
2090
+ case "durable-object-websocket-error":
2091
+ return createBaseEvent(type, env, ctx, { locals, request });
2092
+ default: {
2093
+ const exhaustive = type;
2094
+ throw new Error(`createDefaultEvent: unknown event type ${String(exhaustive)}`);
2095
+ }
2096
+ }
2139
2097
  }
2140
- var globalBindingHints = {};
2141
- function createEnvProxy(options = {}) {
2142
- const client = options.client ?? getClient();
2143
- const bindingProxies = new Map;
2144
- const doProxyOptions = { transformResult: options.transformResult };
2145
- const serviceProxyOptions = { transformResult: options.transformResult };
2146
- const strict = options.strict === true;
2147
- const hints = { ...globalBindingHints, ...options.hints };
2148
- return new Proxy({}, {
2149
- get(target, prop) {
2150
- if (typeof prop !== "string")
2151
- return;
2152
- if (bindingProxies.has(prop)) {
2153
- return bindingProxies.get(prop);
2098
+
2099
+ // src/runtime/validation.ts
2100
+ class ContextAccessError extends Error {
2101
+ contextName;
2102
+ propertyName;
2103
+ constructor(contextName, propertyName, message) {
2104
+ super(message ?? `Cannot access ${contextName}.${propertyName} outside of an active Devflare handler trail.
2105
+
2106
+ ` + `This typically happens when:
2107
+ ` + ` 1. Accessing ${contextName} at module top-level (during import)
2108
+ ` + ` 2. Accessing ${contextName} in a callback that runs after the handler ends
2109
+ ` + ` 3. Accessing ${contextName} in a setTimeout/setInterval callback
2110
+
2111
+ ` + `Move the access inside your handler function or middleware.`);
2112
+ this.name = "ContextAccessError";
2113
+ this.contextName = contextName;
2114
+ this.propertyName = propertyName;
2115
+ }
2116
+ static contextUnavailable(message) {
2117
+ return new ContextAccessError("context", "<unavailable>", message ?? `Context not available. Devflare uses AsyncLocalStorage to carry the active event through fetch, queue, scheduled, email, tail, and Durable Object handler call chains.
2118
+
2119
+ ` + `This usually means one of:
2120
+
2121
+ ` + `1. Accessing context at module top-level (runs at cold start, not per-request)
2122
+ ` + `2. Accessing context in setTimeout/setInterval callbacks
2123
+ ` + `3. Missing 'nodejs_compat' compatibility flag in your worker config
2124
+
2125
+ ` + `Fix: Move the access inside your handler, middleware, or a helper called from that handler trail.
2126
+ ` + `Learn more: https://devflare.dev/docs/context-errors`);
2127
+ }
2128
+ }
2129
+ function createContextProxy(getter, name, options = {}) {
2130
+ const mutable = options.mutable ?? true;
2131
+ return new Proxy({}, {
2132
+ get(_target, prop) {
2133
+ const ctx = getter();
2134
+ if (ctx === undefined || ctx === null) {
2135
+ throw new ContextAccessError(name, String(prop));
2154
2136
  }
2155
- const hint = hints[prop];
2156
- if (!hint && strict) {
2157
- return;
2137
+ return ctx[prop];
2138
+ },
2139
+ set(_target, prop, value) {
2140
+ if (!mutable) {
2141
+ throw new TypeError(`Cannot assign to '${String(prop)}' on '${name}' because it is read-only.
2142
+ ` + `Use 'locals' for mutable request-scoped data.`);
2158
2143
  }
2159
- let proxy;
2160
- switch (hint) {
2161
- case "kv":
2162
- proxy = createKVProxy(client, prop);
2163
- break;
2164
- case "r2":
2165
- proxy = createR2Proxy(client, prop);
2166
- break;
2167
- case "d1":
2168
- proxy = createD1Proxy(client, prop);
2169
- break;
2170
- case "do":
2171
- proxy = createDOProxy(client, prop, doProxyOptions);
2172
- break;
2173
- case "queue":
2174
- proxy = createQueueProxy(client, prop);
2175
- break;
2176
- case "ai":
2177
- proxy = createAIProxy(client, prop);
2178
- break;
2179
- case "service":
2180
- proxy = createServiceProxy(client, prop, serviceProxyOptions);
2181
- break;
2182
- case "sendEmail":
2183
- proxy = createSendEmailProxy(client, prop);
2184
- break;
2185
- case "workflow":
2186
- proxy = createWorkflowProxy(client, prop);
2187
- break;
2188
- case "secret":
2189
- case "var":
2190
- proxy = createSimpleBindingProxy(client, prop);
2191
- break;
2192
- default:
2193
- proxy = createGenericBindingProxy(client, prop);
2144
+ const ctx = getter();
2145
+ if (ctx === undefined || ctx === null) {
2146
+ throw new ContextAccessError(name, String(prop));
2194
2147
  }
2195
- bindingProxies.set(prop, proxy);
2196
- return proxy;
2148
+ ctx[prop] = value;
2149
+ return true;
2197
2150
  },
2198
- has(target, prop) {
2199
- return typeof prop === "string" && (!strict || (prop in hints));
2151
+ deleteProperty(_target, prop) {
2152
+ if (!mutable) {
2153
+ throw new TypeError(`Cannot delete property '${String(prop)}' from '${name}' because it is read-only.`);
2154
+ }
2155
+ const ctx = getter();
2156
+ if (ctx === undefined || ctx === null) {
2157
+ return true;
2158
+ }
2159
+ return Reflect.deleteProperty(ctx, prop);
2200
2160
  },
2201
- ownKeys() {
2202
- return Object.keys(hints);
2161
+ has(_target, prop) {
2162
+ const ctx = getter();
2163
+ if (ctx === undefined || ctx === null) {
2164
+ return false;
2165
+ }
2166
+ return prop in ctx;
2203
2167
  },
2204
- getOwnPropertyDescriptor(target, prop) {
2205
- if (typeof prop === "string" && (!strict || (prop in hints))) {
2206
- return { configurable: true, enumerable: true, writable: false };
2168
+ ownKeys(_target) {
2169
+ const ctx = getter();
2170
+ if (ctx === undefined || ctx === null) {
2171
+ return [];
2207
2172
  }
2208
- return;
2209
- }
2210
- });
2211
- }
2212
- function createGenericBindingProxy(client, bindingName) {
2213
- return new Proxy({}, {
2214
- get(target, prop) {
2215
- if (typeof prop !== "string")
2173
+ return Reflect.ownKeys(ctx);
2174
+ },
2175
+ getOwnPropertyDescriptor(_target, prop) {
2176
+ const ctx = getter();
2177
+ if (ctx === undefined || ctx === null) {
2216
2178
  return;
2217
- if (["get", "put", "delete", "list", "getWithMetadata"].includes(prop)) {
2218
- return createKVProxy(client, bindingName)[prop];
2219
- }
2220
- if (["idFromName", "idFromString", "newUniqueId", "get"].includes(prop)) {
2221
- return createDOProxy(client, bindingName)[prop];
2222
2179
  }
2223
- if (["prepare", "batch", "exec", "dump"].includes(prop)) {
2224
- return createD1Proxy(client, bindingName)[prop];
2180
+ const descriptor = Reflect.getOwnPropertyDescriptor(ctx, prop);
2181
+ if (!descriptor) {
2182
+ return;
2225
2183
  }
2226
- if (["head"].includes(prop)) {
2227
- return createR2Proxy(client, bindingName)[prop];
2184
+ if (!mutable) {
2185
+ return { ...descriptor, writable: false };
2228
2186
  }
2229
- return async (...args) => {
2230
- return client.call(`${bindingName}.${prop}`, args);
2231
- };
2187
+ return descriptor;
2232
2188
  }
2233
2189
  });
2234
2190
  }
2235
- function createSimpleBindingProxy(client, bindingName) {
2236
- let cachedValue;
2237
- let fetched = false;
2238
- let pendingValue = null;
2239
- const loadValue = () => {
2240
- if (fetched) {
2241
- return Promise.resolve(cachedValue);
2191
+
2192
+ // src/runtime/context.ts
2193
+ import { AsyncLocalStorage } from "node:async_hooks";
2194
+ var storage = new AsyncLocalStorage;
2195
+ function createLocals2() {
2196
+ return {};
2197
+ }
2198
+ function runWithContext(env, ctx, request, fn, type = "fetch") {
2199
+ const locals = createLocals2();
2200
+ const event = createDefaultEvent(env, ctx, request, type, locals);
2201
+ return runWithEventContext(event, fn);
2202
+ }
2203
+ function runWithEventContext(event, fn) {
2204
+ const context = {
2205
+ env: event.env,
2206
+ ctx: event.ctx,
2207
+ request: event.request ?? null,
2208
+ locals: event.locals,
2209
+ type: event.type,
2210
+ event
2211
+ };
2212
+ return storage.run(context, fn);
2213
+ }
2214
+ function getContext() {
2215
+ const context = storage.getStore();
2216
+ if (!context) {
2217
+ throw ContextAccessError.contextUnavailable();
2218
+ }
2219
+ return context;
2220
+ }
2221
+ function getContextOrNull() {
2222
+ const context = storage.getStore();
2223
+ return context ?? null;
2224
+ }
2225
+ function getEventContext() {
2226
+ return getContext().event;
2227
+ }
2228
+ function getEventContextOrNull() {
2229
+ return getContextOrNull()?.event ?? null;
2230
+ }
2231
+ function hasContext() {
2232
+ return storage.getStore() !== undefined;
2233
+ }
2234
+ function createEventAccessor(name, matcher) {
2235
+ const accessor = () => {
2236
+ const currentEvent = getEventContextOrNull();
2237
+ if (!currentEvent) {
2238
+ throw ContextAccessError.contextUnavailable();
2242
2239
  }
2243
- if (!pendingValue) {
2244
- pendingValue = client.call(`${bindingName}.var.value`, []).then((value) => {
2245
- cachedValue = value;
2246
- fetched = true;
2247
- return value;
2248
- }).catch((error) => {
2249
- pendingValue = null;
2250
- throw error;
2251
- });
2240
+ if (!matcher(currentEvent)) {
2241
+ throw ContextAccessError.contextUnavailable(`${name} is not available in the current '${currentEvent.type}' context.
2242
+
2243
+ Devflare stores event objects in AsyncLocalStorage so helpers called within a handler can reach the active event.
2244
+ Use ${name}.safe() to return null instead of throwing, or call the getter that matches the active surface.`);
2252
2245
  }
2253
- return pendingValue;
2246
+ return currentEvent;
2254
2247
  };
2255
- return {
2256
- then(resolve, reject) {
2257
- return loadValue().then(resolve ?? undefined, reject ?? undefined);
2258
- },
2259
- toString() {
2260
- if (!fetched)
2261
- throw new Error(`Binding ${bindingName} not yet fetched. Use await.`);
2262
- return String(cachedValue);
2263
- }
2248
+ accessor.safe = () => {
2249
+ const currentEvent = getEventContextOrNull();
2250
+ return currentEvent && matcher(currentEvent) ? currentEvent : null;
2264
2251
  };
2252
+ return accessor;
2265
2253
  }
2266
- var globalEnvProxy = null;
2267
- var bridgeEnv = new Proxy({}, {
2268
- get(target, prop) {
2269
- if (!globalEnvProxy) {
2270
- globalEnvProxy = createEnvProxy({ lazy: true });
2271
- }
2272
- return globalEnvProxy[prop];
2273
- }
2274
- });
2275
- function initEnv(options = {}) {
2276
- globalEnvProxy = createEnvProxy(options);
2277
- return globalEnvProxy;
2254
+ function isFetchEvent(event) {
2255
+ return event.type === "fetch" && event.request instanceof Request;
2278
2256
  }
2279
- function setBindingHints(hints) {
2280
- globalBindingHints = { ...globalBindingHints, ...hints };
2281
- globalEnvProxy = null;
2257
+ function isQueueEvent(event) {
2258
+ return event.type === "queue" && "batch" in event;
2259
+ }
2260
+ function isScheduledEvent(event) {
2261
+ return event.type === "scheduled" && "controller" in event;
2262
+ }
2263
+ function isEmailEvent(event) {
2264
+ return event.type === "email" && "message" in event;
2282
2265
  }
2266
+ function isTailEvent(event) {
2267
+ return event.type === "tail" && Array.isArray(event) && "events" in event;
2268
+ }
2269
+ function isDurableObjectEvent(event) {
2270
+ return event.type.startsWith("durable-object-") && "state" in event;
2271
+ }
2272
+ function isDurableObjectFetchEvent(event) {
2273
+ return event.type === "durable-object-fetch" && event.request instanceof Request;
2274
+ }
2275
+ function isDurableObjectAlarmEvent(event) {
2276
+ return event.type === "durable-object-alarm" && "state" in event;
2277
+ }
2278
+ function isDurableObjectWebSocketMessageEvent(event) {
2279
+ return event.type === "durable-object-websocket-message" && "ws" in event && "message" in event;
2280
+ }
2281
+ function isDurableObjectWebSocketCloseEvent(event) {
2282
+ return event.type === "durable-object-websocket-close" && "ws" in event && "code" in event;
2283
+ }
2284
+ function isDurableObjectWebSocketErrorEvent(event) {
2285
+ return event.type === "durable-object-websocket-error" && "ws" in event && "error" in event;
2286
+ }
2287
+ var getFetchEvent = createEventAccessor("getFetchEvent()", isFetchEvent);
2288
+ var getQueueEvent = createEventAccessor("getQueueEvent()", isQueueEvent);
2289
+ var getScheduledEvent = createEventAccessor("getScheduledEvent()", isScheduledEvent);
2290
+ var getEmailEvent = createEventAccessor("getEmailEvent()", isEmailEvent);
2291
+ var getTailEvent = createEventAccessor("getTailEvent()", isTailEvent);
2292
+ var getDurableObjectEvent = createEventAccessor("getDurableObjectEvent()", isDurableObjectEvent);
2293
+ var getDurableObjectFetchEvent = createEventAccessor("getDurableObjectFetchEvent()", isDurableObjectFetchEvent);
2294
+ var getDurableObjectAlarmEvent = createEventAccessor("getDurableObjectAlarmEvent()", isDurableObjectAlarmEvent);
2295
+ var getDurableObjectWebSocketMessageEvent = createEventAccessor("getDurableObjectWebSocketMessageEvent()", isDurableObjectWebSocketMessageEvent);
2296
+ var getDurableObjectWebSocketCloseEvent = createEventAccessor("getDurableObjectWebSocketCloseEvent()", isDurableObjectWebSocketCloseEvent);
2297
+ var getDurableObjectWebSocketErrorEvent = createEventAccessor("getDurableObjectWebSocketErrorEvent()", isDurableObjectWebSocketErrorEvent);
2283
2298
 
2284
- export { ContextAccessError, createContextProxy, createFetchEvent, createQueueEvent, createScheduledEvent, createEmailEvent, createTailEvent, createDurableObjectFetchEvent, createDurableObjectAlarmEvent, createDurableObjectWebSocketMessageEvent, createDurableObjectWebSocketCloseEvent, createDurableObjectWebSocketErrorEvent, runWithContext, runWithEventContext, getContext, getContextOrNull, getEventContext, getEventContextOrNull, hasContext, getFetchEvent, getQueueEvent, getScheduledEvent, getEmailEvent, getTailEvent, getDurableObjectEvent, getDurableObjectFetchEvent, getDurableObjectAlarmEvent, getDurableObjectWebSocketMessageEvent, getDurableObjectWebSocketCloseEvent, getDurableObjectWebSocketErrorEvent, BridgeClient, getClient, createEnvProxy, bridgeEnv, initEnv, setBindingHints };
2299
+ export { BridgeClient, getClient, createEnvProxy, bridgeEnv, initEnv, setBindingHints, createFetchEvent, createQueueEvent, createScheduledEvent, createEmailEvent, createTailEvent, createDurableObjectFetchEvent, createDurableObjectAlarmEvent, createDurableObjectWebSocketMessageEvent, createDurableObjectWebSocketCloseEvent, createDurableObjectWebSocketErrorEvent, ContextAccessError, createContextProxy, runWithContext, runWithEventContext, getContext, getContextOrNull, getEventContext, getEventContextOrNull, hasContext, getFetchEvent, getQueueEvent, getScheduledEvent, getEmailEvent, getTailEvent, getDurableObjectEvent, getDurableObjectFetchEvent, getDurableObjectAlarmEvent, getDurableObjectWebSocketMessageEvent, getDurableObjectWebSocketCloseEvent, getDurableObjectWebSocketErrorEvent };