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,26 +1,26 @@
1
1
  import {
2
2
  getRemoteModeStatus,
3
3
  isRemoteModeActive
4
- } from "../index-d8bdkx2h.js";
4
+ } from "../index-4xmtkg9g.js";
5
5
  import {
6
6
  canProceedWithTest
7
- } from "../index-15fpa5tx.js";
7
+ } from "../index-x0wpeb3x.js";
8
8
  import {
9
9
  bundleWorkflowEntrypointScript
10
- } from "../index-67gr49zr.js";
11
- import"../index-69c0ed4a.js";
10
+ } from "../index-pmnb7eke.js";
11
+ import"../index-dem45895.js";
12
12
  import {
13
13
  createRouteResolve,
14
14
  invokeFetchModule,
15
15
  matchFetchRoute,
16
16
  resolveFetchHandler
17
- } from "../index-85yjb686.js";
17
+ } from "../index-97rfy7n2.js";
18
18
  import {
19
19
  __clearTestContext,
20
20
  __setTestContext,
21
21
  env
22
- } from "../index-qz6nbwpw.js";
23
- import"../index-a855bdsx.js";
22
+ } from "../index-t23wq5js.js";
23
+ import"../index-y7w3x9p1.js";
24
24
  import {
25
25
  buildHyperdrivesConfig,
26
26
  clearBundleCache,
@@ -29,27 +29,27 @@ import {
29
29
  hasServiceBindings,
30
30
  resolveDOBindings,
31
31
  resolveServiceBindings
32
- } from "../index-k9x5v335.js";
33
- import"../index-3edvz3hs.js";
32
+ } from "../index-dgx495pv.js";
33
+ import"../index-by27mzra.js";
34
34
  import {
35
35
  DEFAULT_DO_PATTERN,
36
36
  findFiles
37
- } from "../index-qwgr4q7s.js";
38
- import"../index-aqrwyy57.js";
39
- import"../index-vhqww6tt.js";
37
+ } from "../index-5dkjffqz.js";
38
+ import"../index-7m557715.js";
39
+ import"../index-p93cngdp.js";
40
40
  import {
41
41
  createLocalHyperdrive,
42
42
  createLocalWorkerLoaderBinding,
43
43
  disposeLocalWorkerLoaderBindings,
44
44
  extractBindingHints
45
- } from "../index-t9y0sve5.js";
45
+ } from "../index-c4zd39tc.js";
46
46
  import {
47
47
  buildLocalBindingShimServiceConfig
48
48
  } from "../index-dm9q84c7.js";
49
49
  import {
50
50
  buildLocalSecretWrappedBindingConfig,
51
51
  resolveLocalSecretValuesForBindings
52
- } from "../index-fcmj6641.js";
52
+ } from "../index-13sfensw.js";
53
53
  import {
54
54
  BridgeClient,
55
55
  createEmailEvent,
@@ -61,14 +61,14 @@ import {
61
61
  runWithContext,
62
62
  runWithEventContext,
63
63
  setBindingHints
64
- } from "../index-6sj0v0dk.js";
64
+ } from "../index-1trss579.js";
65
65
  import {
66
66
  createLocalSendEmailBinding,
67
67
  wrapEnvSendEmailBindings
68
- } from "../index-hpwa6vsw.js";
68
+ } from "../index-n4xkaymm.js";
69
69
  import {
70
70
  applyLocalDevVarsToConfig
71
- } from "../index-hjjfxv02.js";
71
+ } from "../index-w8c8tjc5.js";
72
72
  import {
73
73
  getLocalD1DatabaseIdentifier,
74
74
  loadConfig,
@@ -83,544 +83,23 @@ import {
83
83
  normalizeSecretsStoreBinding,
84
84
  normalizeWorkflowBinding,
85
85
  resolveConfigPath
86
- } from "../index-dfcp9hsd.js";
86
+ } from "../index-pd97hk2j.js";
87
87
  import {
88
88
  getEffectiveAccountId,
89
89
  getPrimaryAccount
90
- } from "../index-1d4jg11n.js";
90
+ } from "../index-h7r11y4a.js";
91
91
  import {
92
92
  getApiToken,
93
93
  isAuthenticated
94
- } from "../index-mg8vwqxf.js";
94
+ } from "../index-04wh9bxx.js";
95
95
  import {
96
96
  resolveConfigEnvVars
97
- } from "../index-c8p4njqy.js";
98
- import"../index-q8f4kawk.js";
97
+ } from "../index-wxdcrzcp.js";
98
+ import"../index-34dejneh.js";
99
99
  import {
100
100
  __require
101
101
  } from "../index-37x76zdn.js";
102
102
 
103
- // src/test/simple-context-durable-objects.ts
104
- import { mkdirSync, writeFileSync } from "fs";
105
- import { readFile } from "fs/promises";
106
- import { dirname as dirname2, join as join2 } from "path";
107
-
108
- // src/test/simple-context-gateway-script.ts
109
- function buildGatewayScript(bundledCode, wrappers, nativeRpcBindingNames = []) {
110
- const nativeRpcBindingsLiteral = JSON.stringify(nativeRpcBindingNames);
111
- return `
112
- // Bundled transport + DO classes
113
- ${bundledCode}
114
-
115
- // DO Wrappers with RPC
116
- ${wrappers}
117
-
118
- const __nativeRpcBindings = new Set(${nativeRpcBindingsLiteral})
119
-
120
- // Transport encoding helper
121
- const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
122
-
123
- function __encodeTransport(value) {
124
- if (value === null || value === undefined) return value
125
-
126
- // Try each encoder
127
- for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
128
- const encoded = transporter.encode(value)
129
- if (encoded !== false && encoded !== undefined) {
130
- return { __transport: typeName, value: encoded }
131
- }
132
- }
133
-
134
- // Recursively encode arrays and objects
135
- if (Array.isArray(value)) {
136
- return value.map(__encodeTransport)
137
- }
138
- if (typeof value === 'object') {
139
- const result = {}
140
- for (const [k, v] of Object.entries(value)) {
141
- result[k] = __encodeTransport(v)
142
- }
143
- return result
144
- }
145
-
146
- return value
147
- }
148
-
149
- // Gateway with WebSocket RPC
150
- export default {
151
- async fetch(request, env) {
152
- if (request.headers.get('Upgrade') === 'websocket') {
153
- const { 0: client, 1: server } = new WebSocketPair()
154
- server.accept()
155
- server.addEventListener('message', async (e) => {
156
- try {
157
- const m = JSON.parse(e.data)
158
- if (m.t === 'rpc.call') {
159
- const result = await executeRpc(env, m.method, m.params)
160
- server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
161
- }
162
- } catch (error) {
163
- server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
164
- }
165
- })
166
- return new Response(null, { status: 101, webSocket: client })
167
- }
168
- return new Response('Gateway')
169
- }
170
- }
171
-
172
- async function executeRpc(env, method, params) {
173
- const [bindingName, ...rest] = method.split('.')
174
- let op = rest.join('.')
175
- const binding = env[bindingName]
176
- const RAW_EMAIL = 'EmailMessage::raw'
177
- if (!binding) throw new Error('Binding not found: ' + bindingName)
178
-
179
- // Normalize namespaced op names (kv.*, r2.*, d1.*, do.*, queue.*, ai.*, var.*)
180
- // down to the legacy verbs this dispatcher historically used. The bridge
181
- // proxy always emits namespaced forms (see src/bridge/proxy.ts and B3 in
182
- // REMAINING.md); this keeps the dispatcher backwards-compatible while the
183
- // rest of the codebase converges on the namespaced convention.
184
- if (op.indexOf('kv.') === 0) op = op.slice(3)
185
- else if (op.indexOf('do.') === 0) {
186
- const tail = op.slice(3)
187
- if (tail === 'fetch') op = 'stub.fetch'
188
- else if (tail === 'rpc') op = 'stub.rpc'
189
- else op = tail
190
- }
191
- else if (op.indexOf('queue.') === 0) op = op.slice(6)
192
- else if (op.indexOf('ai.') === 0) op = op.slice(3)
193
- else if (op.indexOf('var.') === 0) op = op.slice(4)
194
- else if (op.indexOf('d1.stmt.') === 0) op = 'prepare.' + op.slice('d1.stmt.'.length)
195
- else if (op.indexOf('d1.') === 0) op = op.slice(3)
196
- // r2.* and email.* keep their existing prefixes
197
-
198
- // KV operations
199
- if (op === 'get') return binding.get(params[0], params[1])
200
- if (op === 'put') return binding.put(params[0], params[1], params[2])
201
- if (op === 'delete') return binding.delete(params[0])
202
- if (op === 'list') return binding.list(params[0])
203
- if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
204
-
205
- // R2 operations
206
- if (op === 'r2.get') return binding.get(params[0], params[1])
207
- if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
208
- if (op === 'r2.delete') return binding.delete(params[0])
209
- if (op === 'r2.list') return binding.list(params[0])
210
- if (op === 'r2.head' || op === 'head') return binding.head(params[0])
211
-
212
- // D1 operations
213
- if (op === 'exec') return binding.exec(params[0])
214
- if (op === 'dump') return binding.dump()
215
- if (op === 'batch') {
216
- const stmts = params[0].map(s => {
217
- const stmt = binding.prepare(s.sql)
218
- return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
219
- })
220
- return binding.batch(stmts)
221
- }
222
- if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
223
- if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
224
- if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
225
- if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
226
-
227
- // Send email operations
228
- if (op === 'email.send') {
229
- return binding.send(__normalizeEmailMessage(params[0]))
230
- }
231
-
232
- // DO operations
233
- if (op === 'idFromName') {
234
- return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
235
- }
236
- if (op === 'stub.rpc') {
237
- const [, idSerialized, rpcMethod, rpcParams] = params
238
- const stub = binding.get(binding.idFromString(idSerialized.hex))
239
-
240
- if (__nativeRpcBindings.has(bindingName) && typeof stub[rpcMethod] === 'function') {
241
- let result = await stub[rpcMethod](...(rpcParams || []))
242
- result = __encodeTransport(result)
243
- return result
244
- }
245
-
246
- const response = await stub.fetch(new Request('http://do/_rpc', {
247
- method: 'POST',
248
- headers: { 'Content-Type': 'application/json' },
249
- body: JSON.stringify({ method: rpcMethod, params: rpcParams || [] })
250
- }))
251
-
252
- const payload = await response.json()
253
- if (!response.ok || !payload?.ok) {
254
- throw new Error(payload?.error?.message || ('DO RPC failed with status ' + response.status))
255
- }
256
-
257
- return payload.result
258
- }
259
-
260
- throw new Error('Unknown operation: ' + method)
261
- }
262
-
263
- function __createEmailMessageRaw(raw) {
264
- if (typeof raw === 'string' || raw instanceof ReadableStream) {
265
- return raw
266
- }
267
- if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
268
- return new Response(raw).body
269
- }
270
- throw new Error('Unsupported EmailMessage raw payload')
271
- }
272
-
273
- function __buildRawEmail(message) {
274
- const lines = []
275
- const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
276
-
277
- lines.push('From: ' + message.from)
278
- lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
279
- lines.push('Date: ' + new Date().toUTCString())
280
- lines.push('Message-ID: ' + messageId)
281
-
282
- if (message.subject) lines.push('Subject: ' + message.subject)
283
- if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
284
- if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
285
- if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
286
-
287
- for (const [key, value] of Object.entries(message.headers || {})) {
288
- lines.push(key + ': ' + value)
289
- }
290
-
291
- lines.push('MIME-Version: 1.0')
292
- lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
293
- lines.push('')
294
- lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
295
-
296
- return lines.join('\\r\\n')
297
- }
298
-
299
- function __normalizeEmailMessage(message) {
300
- if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
301
- return message
302
- }
303
- if ('EmailMessage::raw' in message) {
304
- return message
305
- }
306
- if ('raw' in message && message.raw !== undefined) {
307
- return {
308
- from: message.from,
309
- to: message.to,
310
- [RAW_EMAIL]: __createEmailMessageRaw(message.raw)
311
- }
312
- }
313
- return {
314
- from: message.from,
315
- to: message.to,
316
- [RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
317
- }
318
- }
319
- `;
320
- }
321
-
322
- // src/test/simple-context-paths.ts
323
- import { existsSync } from "fs";
324
- import { createServer } from "net";
325
- import { dirname, join } from "path";
326
- import { fileURLToPath } from "url";
327
- var DEFAULT_TRANSPORT_ENTRY_FILES = [
328
- "src/transport.ts",
329
- "src/transport.js",
330
- "src/transport.mts",
331
- "src/transport.mjs"
332
- ];
333
- var CURRENT_PACKAGE_ROOT = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
334
- function getBunRuntime() {
335
- const g = globalThis;
336
- if (typeof g.Bun === "object" && g.Bun !== null) {
337
- return g.Bun;
338
- }
339
- return;
340
- }
341
- function getCallerDirectory() {
342
- const stackCallerDirectory = getStackCallerDirectory();
343
- if (stackCallerDirectory) {
344
- return stackCallerDirectory;
345
- }
346
- return process.cwd();
347
- }
348
- function getStackCallerDirectory() {
349
- const originalPrepare = Error.prepareStackTrace;
350
- Error.prepareStackTrace = (_, stack) => stack;
351
- try {
352
- const err = new Error;
353
- const stack = err.stack;
354
- for (const site of stack ?? []) {
355
- const filename = site.getFileName?.();
356
- if (filename && !isInsideCurrentPackage(filename) && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync(filename)) {
357
- return dirname(filename);
358
- }
359
- }
360
- } finally {
361
- Error.prepareStackTrace = originalPrepare;
362
- }
363
- return null;
364
- }
365
- function findPackageRoot(startDir) {
366
- let currentDir = startDir;
367
- while (true) {
368
- if (existsSync(join(currentDir, "package.json"))) {
369
- return currentDir;
370
- }
371
- const parentDir = dirname(currentDir);
372
- if (parentDir === currentDir) {
373
- return startDir;
374
- }
375
- currentDir = parentDir;
376
- }
377
- }
378
- function isInsideCurrentPackage(filePath) {
379
- const normalizedFilePath = filePath.replace(/\\/g, "/");
380
- const normalizedPackageRoot = CURRENT_PACKAGE_ROOT.replace(/\\/g, "/");
381
- return normalizedFilePath === normalizedPackageRoot || normalizedFilePath.startsWith(`${normalizedPackageRoot}/`);
382
- }
383
- async function findNearestConfig(startDir) {
384
- let currentDir = startDir;
385
- while (true) {
386
- const configPath = await resolveConfigPath(currentDir);
387
- if (configPath) {
388
- return configPath;
389
- }
390
- const parentDir = dirname(currentDir);
391
- if (parentDir === currentDir) {
392
- return null;
393
- }
394
- currentDir = parentDir;
395
- }
396
- }
397
- async function getAvailablePort() {
398
- return await new Promise((resolvePort, reject) => {
399
- const server = createServer();
400
- server.once("error", reject);
401
- server.listen(0, "127.0.0.1", () => {
402
- const address = server.address();
403
- if (!address || typeof address === "string") {
404
- server.close(() => reject(new Error("Could not determine an available port")));
405
- return;
406
- }
407
- const { port } = address;
408
- server.close((error) => {
409
- if (error) {
410
- reject(error);
411
- return;
412
- }
413
- resolvePort(port);
414
- });
415
- });
416
- });
417
- }
418
- function resolveTransportFile(configDir, configuredPath) {
419
- if (typeof configuredPath === "string") {
420
- return configuredPath;
421
- }
422
- if (configuredPath === null) {
423
- return null;
424
- }
425
- for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
426
- if (existsSync(join(configDir, defaultEntry))) {
427
- return defaultEntry;
428
- }
429
- }
430
- return null;
431
- }
432
-
433
- // src/test/simple-context-durable-objects.ts
434
- function findExportedClasses(code) {
435
- const classes = [];
436
- const classPattern = /export\s+class\s+(\w+)/g;
437
- let match;
438
- while ((match = classPattern.exec(code)) !== null) {
439
- classes.push(match[1]);
440
- }
441
- return classes;
442
- }
443
- function classSupportsNativeDurableObjectRpc(code, className) {
444
- const nativeRpcPattern = new RegExp(`export\\s+class\\s+${className}\\s+extends\\s+DurableObject\\b`);
445
- return nativeRpcPattern.test(code);
446
- }
447
- function toGeneratedIdentifier(value) {
448
- const normalized = value.replace(/[^A-Za-z0-9_$]/g, "_");
449
- return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
450
- }
451
- async function discoverLocalDurableObjectClasses(config, configDir) {
452
- const classToFilePath = new Map;
453
- const doPatternConfig = config.files?.durableObjects;
454
- const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
455
- if (doPatternConfig === false) {
456
- return classToFilePath;
457
- }
458
- const doFiles = await findFiles(doPattern, { cwd: configDir });
459
- for (const filePath of doFiles) {
460
- try {
461
- const code = await readFile(filePath, "utf-8");
462
- const classNames = findExportedClasses(code);
463
- for (const className of classNames) {
464
- classToFilePath.set(className, {
465
- filePath,
466
- nativeRpc: classSupportsNativeDurableObjectRpc(code, className)
467
- });
468
- }
469
- } catch {}
470
- }
471
- return classToFilePath;
472
- }
473
- async function resolveLocalDurableObjects(config, configDir) {
474
- const doConfig = {};
475
- const doInfos = [];
476
- const classToFilePath = await discoverLocalDurableObjectClasses(config, configDir);
477
- for (const [name, rawDoInfo] of Object.entries(config.bindings?.durableObjects ?? {})) {
478
- const doInfo = normalizeDOBinding(rawDoInfo);
479
- if (doInfo.__ref) {
480
- continue;
481
- }
482
- let scriptPath;
483
- let nativeRpc = false;
484
- if (doInfo.kind === "cross-worker" && doInfo.scriptName) {
485
- scriptPath = join2(configDir, "src", doInfo.scriptName);
486
- try {
487
- const code = await readFile(scriptPath, "utf-8");
488
- nativeRpc = classSupportsNativeDurableObjectRpc(code, doInfo.className);
489
- } catch {
490
- nativeRpc = false;
491
- }
492
- } else {
493
- const discoveredClass = classToFilePath.get(doInfo.className);
494
- if (!discoveredClass) {
495
- throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
496
- ` + `Either:
497
- ` + ` 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
498
- ` + ` 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
499
- }
500
- scriptPath = discoveredClass.filePath;
501
- nativeRpc = discoveredClass.nativeRpc;
502
- }
503
- const runtimeClassName = nativeRpc ? doInfo.className : `__Devflare${toGeneratedIdentifier(name)}RpcWrapper`;
504
- doConfig[name] = runtimeClassName;
505
- doInfos.push({
506
- name,
507
- className: doInfo.className,
508
- scriptPath,
509
- nativeRpc,
510
- runtimeClassName
511
- });
512
- }
513
- return {
514
- doConfig,
515
- doInfos
516
- };
517
- }
518
- function buildWrapperCode(doInfos) {
519
- return doInfos.filter((info) => !info.nativeRpc).map((info) => `
520
- export class ${info.runtimeClassName} {
521
- constructor(state, env) {
522
- this.__instance = new ${info.className}(state, env)
523
- }
524
-
525
- async fetch(request) {
526
- const url = new URL(request.url)
527
- if (request.method !== 'POST' || url.pathname !== '/_rpc') {
528
- return new Response('Not found', { status: 404 })
529
- }
530
-
531
- try {
532
- const payload = await request.json()
533
- const method = payload?.method
534
- const params = Array.isArray(payload?.params) ? payload.params : []
535
- const target = this.__instance?.[method]
536
-
537
- if (typeof target !== 'function') {
538
- return new Response(JSON.stringify({
539
- ok: false,
540
- error: { message: 'Method not found: ' + String(method) }
541
- }), {
542
- status: 404,
543
- headers: { 'Content-Type': 'application/json' }
544
- })
545
- }
546
-
547
- let result = await target.apply(this.__instance, params)
548
- result = __encodeTransport(result)
549
-
550
- return new Response(JSON.stringify({ ok: true, result }), {
551
- headers: { 'Content-Type': 'application/json' }
552
- })
553
- } catch (error) {
554
- return new Response(JSON.stringify({
555
- ok: false,
556
- error: { message: error instanceof Error ? error.message : String(error) }
557
- }), {
558
- status: 500,
559
- headers: { 'Content-Type': 'application/json' }
560
- })
561
- }
562
- }
563
- }`.trim()).join(`
564
-
565
- `);
566
- }
567
- async function bundleDurableObjectModules(configDir, doInfos, transportFile) {
568
- const virtualImports = [];
569
- const virtualExports = [];
570
- if (transportFile) {
571
- const transportPath = join2(configDir, transportFile);
572
- virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
573
- virtualExports.push("export { transport }");
574
- }
575
- for (const info of doInfos) {
576
- virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
577
- virtualExports.push(`export { ${info.className} }`);
578
- }
579
- if (virtualImports.length === 0) {
580
- return "";
581
- }
582
- const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
583
- `);
584
- const virtualPath = join2(configDir, ".devflare", "__test_entry.ts");
585
- mkdirSync(dirname2(virtualPath), { recursive: true });
586
- writeFileSync(virtualPath, virtualEntry);
587
- const bun = getBunRuntime();
588
- if (!bun) {
589
- throw new Error("Bun runtime is required for createTestContext with Durable Objects");
590
- }
591
- const result = await bun.build({
592
- entrypoints: [virtualPath],
593
- target: "browser",
594
- format: "esm",
595
- minify: false,
596
- external: ["cloudflare:workers", "cloudflare:*"]
597
- });
598
- if (!result.success) {
599
- throw new Error(`Failed to bundle test entry: ${result.logs.join(`
600
- `)}`);
601
- }
602
- return await result.outputs[0].text();
603
- }
604
- async function buildDurableObjectGateway(config, configDir, transportFile) {
605
- const workflowEntrypointScript = await bundleWorkflowEntrypointScript(config, configDir);
606
- if (!config.bindings?.durableObjects) {
607
- return {
608
- script: buildGatewayScript(workflowEntrypointScript, "")
609
- };
610
- }
611
- const { doConfig, doInfos } = await resolveLocalDurableObjects(config, configDir);
612
- const bundledCode = await bundleDurableObjectModules(configDir, doInfos, transportFile);
613
- const wrapperCode = buildWrapperCode(doInfos);
614
- const entrypointCode = [workflowEntrypointScript, bundledCode].filter(Boolean).join(`
615
-
616
- `);
617
- const nativeRpcBindingNames = doInfos.filter((info) => info.nativeRpc).map((info) => info.name);
618
- return {
619
- durableObjects: doConfig,
620
- script: buildGatewayScript(entrypointCode, wrapperCode, nativeRpcBindingNames)
621
- };
622
- }
623
-
624
103
  // src/test/remote-cloudflare.ts
625
104
  function createRemoteCloudflareClient(accountId) {
626
105
  let resolvedAccountId = null;
@@ -1248,572 +727,1093 @@ function createMockQueue() {
1248
727
  }
1249
728
  return response;
1250
729
  },
1251
- _getMessages() {
1252
- return messages;
730
+ _getMessages() {
731
+ return messages;
732
+ }
733
+ };
734
+ }
735
+ // src/test/utilities/platform.ts
736
+ function createMockRateLimit(options = {}) {
737
+ const limit = options.limit ?? Number.MAX_SAFE_INTEGER;
738
+ const periodMs = (options.period ?? 60) * 1000;
739
+ const windows = new Map;
740
+ return {
741
+ async limit({ key }) {
742
+ const now = Date.now();
743
+ const existing = windows.get(key);
744
+ if (!existing || existing.resetAt <= now) {
745
+ windows.set(key, { count: 1, resetAt: now + periodMs });
746
+ return { success: limit >= 1 };
747
+ }
748
+ existing.count += 1;
749
+ return { success: existing.count <= limit };
750
+ }
751
+ };
752
+ }
753
+ function createMockVersionMetadata(metadata = {}) {
754
+ return {
755
+ id: metadata.id ?? "devflare-local-version",
756
+ tag: metadata.tag ?? "local",
757
+ timestamp: metadata.timestamp ?? "1970-01-01T00:00:00.000Z"
758
+ };
759
+ }
760
+ function createMockSecretsStoreSecret(value) {
761
+ return {
762
+ async get() {
763
+ return value;
764
+ }
765
+ };
766
+ }
767
+ function createMockHyperdrive(connectionString) {
768
+ return createLocalHyperdrive(connectionString);
769
+ }
770
+ function createDefaultWorkerStub() {
771
+ return {
772
+ getEntrypoint() {
773
+ throw new Error("Mock WorkerLoader stub has no entrypoint. Pass createMockWorkerLoader({ stub }) for behavior.");
774
+ },
775
+ getDurableObjectClass() {
776
+ throw new Error("Mock WorkerLoader stub cannot materialise a Durable Object class. " + "getDurableObjectClass() returns an opaque facet-spawning reference that workerd " + "only exposes inside the runtime. Pass createMockWorkerLoader({ stub }) to inject a stub, " + "or use createTestContext() (a real Miniflare worker) for Durable Object behavior.");
777
+ }
778
+ };
779
+ }
780
+ function createMockWorkerLoader(options = {}) {
781
+ const stub = options.stub ?? createDefaultWorkerStub();
782
+ return {
783
+ get(_name, _getCode) {
784
+ return stub;
785
+ },
786
+ load(_code) {
787
+ return stub;
788
+ }
789
+ };
790
+ }
791
+ function defaultMTLSCertificateHandler() {
792
+ throw new Error("Mock mTLS Certificate Fetcher has no handler. Pass createMockMTLSCertificate(handler) for behavior.");
793
+ }
794
+ function createMockMTLSCertificate(handler = defaultMTLSCertificateHandler) {
795
+ return {
796
+ async fetch(input, init) {
797
+ return handler(input, init);
798
+ }
799
+ };
800
+ }
801
+ function createMockDispatchNamespace(options = {}) {
802
+ return {
803
+ get(name) {
804
+ const worker = options.workers?.[name];
805
+ if (!worker) {
806
+ throw new Error(`Mock DispatchNamespace has no worker named "${name}".`);
807
+ }
808
+ return typeof worker === "function" ? createMockMTLSCertificate(worker) : worker;
809
+ }
810
+ };
811
+ }
812
+ // src/test/utilities/workflows.ts
813
+ function createMockWorkflowInstance(id, options = {}) {
814
+ let status = options.status ?? "queued";
815
+ let output = options.output;
816
+ let error = options.error;
817
+ return {
818
+ id,
819
+ async pause() {
820
+ status = "paused";
821
+ },
822
+ async resume() {
823
+ status = "running";
824
+ },
825
+ async terminate() {
826
+ status = "terminated";
827
+ },
828
+ async restart() {
829
+ status = "queued";
830
+ error = undefined;
831
+ output = undefined;
832
+ },
833
+ async status() {
834
+ return {
835
+ status,
836
+ ...error && { error },
837
+ ...output !== undefined && { output }
838
+ };
839
+ },
840
+ async sendEvent(_event) {}
841
+ };
842
+ }
843
+ function createMockWorkflow(options = {}) {
844
+ const instances = new Map;
845
+ let sequence = 0;
846
+ for (const [id, instanceOptions] of Object.entries(options.instances ?? {})) {
847
+ instances.set(id, createMockWorkflowInstance(id, instanceOptions));
848
+ }
849
+ const createInstance = (id) => {
850
+ if (instances.has(id)) {
851
+ throw new Error(`Mock Workflow already has an instance named "${id}".`);
852
+ }
853
+ const instance = createMockWorkflowInstance(id);
854
+ instances.set(id, instance);
855
+ return instance;
856
+ };
857
+ return {
858
+ async get(id) {
859
+ const instance = instances.get(id);
860
+ if (!instance) {
861
+ throw new Error(`Mock Workflow has no instance named "${id}".`);
862
+ }
863
+ return instance;
864
+ },
865
+ async create(options2) {
866
+ const id = options2?.id ?? `mock-workflow-${++sequence}`;
867
+ return createInstance(id);
868
+ },
869
+ async createBatch(batch) {
870
+ return batch.map((options2) => {
871
+ const id = options2.id ?? `mock-workflow-${++sequence}`;
872
+ return createInstance(id);
873
+ });
874
+ }
875
+ };
876
+ }
877
+ function createMockPipeline() {
878
+ const records = [];
879
+ return {
880
+ async send(batch) {
881
+ records.push(...batch);
882
+ },
883
+ _getRecords() {
884
+ return [...records];
885
+ }
886
+ };
887
+ }
888
+ // src/test/utilities/media.ts
889
+ function createEmptyImageStream() {
890
+ return new ReadableStream({
891
+ start(controller) {
892
+ controller.close();
893
+ }
894
+ });
895
+ }
896
+ function createMockImageTransformationResult(response) {
897
+ return {
898
+ response() {
899
+ return response.clone();
900
+ },
901
+ contentType() {
902
+ return response.headers.get("Content-Type") ?? "image/png";
903
+ },
904
+ image() {
905
+ const cloned = response.clone();
906
+ return cloned.body ?? createEmptyImageStream();
907
+ }
908
+ };
909
+ }
910
+ function createMockImageTransformer(response) {
911
+ const transformer = {
912
+ transform(_transform) {
913
+ return transformer;
914
+ },
915
+ draw(_image, _options) {
916
+ return transformer;
917
+ },
918
+ async output(_options) {
919
+ return createMockImageTransformationResult(response);
1253
920
  }
1254
921
  };
922
+ return transformer;
1255
923
  }
1256
- // src/test/utilities/platform.ts
1257
- function createMockRateLimit(options = {}) {
1258
- const limit = options.limit ?? Number.MAX_SAFE_INTEGER;
1259
- const periodMs = (options.period ?? 60) * 1000;
1260
- const windows = new Map;
1261
- return {
1262
- async limit({ key }) {
1263
- const now = Date.now();
1264
- const existing = windows.get(key);
1265
- if (!existing || existing.resetAt <= now) {
1266
- windows.set(key, { count: 1, resetAt: now + periodMs });
1267
- return { success: limit >= 1 };
1268
- }
1269
- existing.count += 1;
1270
- return { success: existing.count <= limit };
1271
- }
924
+ function createMockHostedImagesBinding() {
925
+ const unsupported = () => {
926
+ throw new Error("Mock Images hosted API is not implemented. Pass a custom ImagesBinding through createMockEnv({ images }) if your test needs hosted image behavior.");
1272
927
  };
1273
- }
1274
- function createMockVersionMetadata(metadata = {}) {
1275
928
  return {
1276
- id: metadata.id ?? "devflare-local-version",
1277
- tag: metadata.tag ?? "local",
1278
- timestamp: metadata.timestamp ?? "1970-01-01T00:00:00.000Z"
929
+ image(_imageId) {
930
+ return {
931
+ details: unsupported,
932
+ bytes: unsupported,
933
+ update: unsupported,
934
+ delete: unsupported
935
+ };
936
+ },
937
+ upload: unsupported,
938
+ list: unsupported
1279
939
  };
1280
940
  }
1281
- function createMockSecretsStoreSecret(value) {
941
+ function createMockImagesBinding(options = {}) {
942
+ const response = options.response ?? new Response("", {
943
+ headers: { "Content-Type": "image/png" }
944
+ });
945
+ const info = options.info ?? {
946
+ format: response.headers.get("Content-Type") ?? "image/png",
947
+ fileSize: 0,
948
+ width: 0,
949
+ height: 0
950
+ };
1282
951
  return {
1283
- async get() {
1284
- return value;
1285
- }
952
+ async info(_stream, _options) {
953
+ return info;
954
+ },
955
+ input(_stream, _options) {
956
+ return createMockImageTransformer(response);
957
+ },
958
+ hosted: createMockHostedImagesBinding()
1286
959
  };
1287
960
  }
1288
- function createMockHyperdrive(connectionString) {
1289
- return createLocalHyperdrive(connectionString);
961
+ function createEmptyMediaStream() {
962
+ return new ReadableStream({
963
+ start(controller) {
964
+ controller.close();
965
+ }
966
+ });
1290
967
  }
1291
- function createDefaultWorkerStub() {
968
+ function createMockMediaTransformationResult(response) {
1292
969
  return {
1293
- getEntrypoint() {
1294
- throw new Error("Mock WorkerLoader stub has no entrypoint. Pass createMockWorkerLoader({ stub }) for behavior.");
970
+ async media() {
971
+ const cloned = response.clone();
972
+ return cloned.body ?? createEmptyMediaStream();
1295
973
  },
1296
- getDurableObjectClass() {
1297
- throw new Error("Mock WorkerLoader stub cannot materialise a Durable Object class. " + "getDurableObjectClass() returns an opaque facet-spawning reference that workerd " + "only exposes inside the runtime. Pass createMockWorkerLoader({ stub }) to inject a stub, " + "or use createTestContext() (a real Miniflare worker) for Durable Object behavior.");
974
+ async response() {
975
+ return response.clone();
976
+ },
977
+ async contentType() {
978
+ return response.headers.get("Content-Type") ?? "video/mp4";
1298
979
  }
1299
980
  };
1300
981
  }
1301
- function createMockWorkerLoader(options = {}) {
1302
- const stub = options.stub ?? createDefaultWorkerStub();
1303
- return {
1304
- get(_name, _getCode) {
1305
- return stub;
982
+ function createMockMediaTransformer(response) {
983
+ const transformer = {
984
+ transform(_transform) {
985
+ return {
986
+ output(_output) {
987
+ return createMockMediaTransformationResult(response);
988
+ }
989
+ };
1306
990
  },
1307
- load(_code) {
1308
- return stub;
991
+ output(_output) {
992
+ return createMockMediaTransformationResult(response);
1309
993
  }
1310
994
  };
995
+ return transformer;
1311
996
  }
1312
- function defaultMTLSCertificateHandler() {
1313
- throw new Error("Mock mTLS Certificate Fetcher has no handler. Pass createMockMTLSCertificate(handler) for behavior.");
1314
- }
1315
- function createMockMTLSCertificate(handler = defaultMTLSCertificateHandler) {
997
+ function createMockMediaBinding(options = {}) {
998
+ const response = options.response ?? new Response("", {
999
+ headers: { "Content-Type": "video/mp4" }
1000
+ });
1316
1001
  return {
1317
- async fetch(input, init) {
1318
- return handler(input, init);
1002
+ input(_media) {
1003
+ return createMockMediaTransformer(response);
1319
1004
  }
1320
1005
  };
1321
1006
  }
1322
- function createMockDispatchNamespace(options = {}) {
1007
+ // src/test/utilities/artifacts.ts
1008
+ function createArtifactTimestamp() {
1009
+ return new Date("2026-04-26T00:00:00.000Z").toISOString();
1010
+ }
1011
+ function createArtifactsRepoInfo(name, options = {}) {
1012
+ const now = createArtifactTimestamp();
1323
1013
  return {
1324
- get(name) {
1325
- const worker = options.workers?.[name];
1326
- if (!worker) {
1327
- throw new Error(`Mock DispatchNamespace has no worker named "${name}".`);
1328
- }
1329
- return typeof worker === "function" ? createMockMTLSCertificate(worker) : worker;
1330
- }
1014
+ id: `repo-${name}`,
1015
+ name,
1016
+ description: options.description ?? null,
1017
+ defaultBranch: options.defaultBranch ?? "main",
1018
+ createdAt: now,
1019
+ updatedAt: now,
1020
+ lastPushAt: null,
1021
+ source: options.source ?? null,
1022
+ readOnly: options.readOnly ?? false,
1023
+ remote: `https://example.com/artifacts/default/${name}.git`
1331
1024
  };
1332
1025
  }
1333
- // src/test/utilities/workflows.ts
1334
- function createMockWorkflowInstance(id, options = {}) {
1335
- let status = options.status ?? "queued";
1336
- let output = options.output;
1337
- let error = options.error;
1338
- return {
1339
- id,
1340
- async pause() {
1341
- status = "paused";
1026
+ function isArtifactsBinding(value) {
1027
+ return typeof value.create === "function";
1028
+ }
1029
+ function createMockArtifacts(options = {}) {
1030
+ const repos = new Map;
1031
+ const tokens = new Map;
1032
+ const addRepo = (info) => {
1033
+ repos.set(info.name, info);
1034
+ if (!tokens.has(info.name)) {
1035
+ tokens.set(info.name, []);
1036
+ }
1037
+ };
1038
+ for (const repo of options.repos ?? []) {
1039
+ addRepo({
1040
+ ...createArtifactsRepoInfo(repo.name),
1041
+ ...repo
1042
+ });
1043
+ }
1044
+ const createToken = (repoName, scope = "write", ttl = 86400) => {
1045
+ const existing = tokens.get(repoName) ?? [];
1046
+ const id = `token-${repoName}-${existing.length + 1}`;
1047
+ const expiresAt = new Date(Date.parse(createArtifactTimestamp()) + ttl * 1000).toISOString();
1048
+ const token = {
1049
+ id,
1050
+ scope,
1051
+ state: "active",
1052
+ createdAt: createArtifactTimestamp(),
1053
+ expiresAt
1054
+ };
1055
+ tokens.set(repoName, [...existing, token]);
1056
+ return {
1057
+ id,
1058
+ plaintext: `${id}-plaintext`,
1059
+ scope,
1060
+ expiresAt
1061
+ };
1062
+ };
1063
+ const createRepoHandle = (info) => ({
1064
+ ...info,
1065
+ async createToken(scope, ttl) {
1066
+ return createToken(info.name, scope, ttl);
1342
1067
  },
1343
- async resume() {
1344
- status = "running";
1068
+ async listTokens() {
1069
+ const repoTokens = tokens.get(info.name) ?? [];
1070
+ return {
1071
+ tokens: repoTokens,
1072
+ total: repoTokens.length
1073
+ };
1345
1074
  },
1346
- async terminate() {
1347
- status = "terminated";
1075
+ async revokeToken(tokenOrId) {
1076
+ const repoTokens = tokens.get(info.name) ?? [];
1077
+ const index = repoTokens.findIndex((token) => token.id === tokenOrId);
1078
+ if (index === -1) {
1079
+ return false;
1080
+ }
1081
+ repoTokens[index] = {
1082
+ ...repoTokens[index],
1083
+ state: "revoked"
1084
+ };
1085
+ tokens.set(info.name, repoTokens);
1086
+ return true;
1348
1087
  },
1349
- async restart() {
1350
- status = "queued";
1351
- error = undefined;
1352
- output = undefined;
1088
+ async fork(name, forkOptions) {
1089
+ return createRepo(name, {
1090
+ description: forkOptions?.description ?? info.description ?? undefined,
1091
+ readOnly: forkOptions?.readOnly ?? info.readOnly,
1092
+ setDefaultBranch: info.defaultBranch,
1093
+ source: `artifacts:default/${info.name}`
1094
+ });
1095
+ }
1096
+ });
1097
+ const createRepo = async (name, createOptions = {}) => {
1098
+ const info = createArtifactsRepoInfo(name, {
1099
+ description: createOptions.description,
1100
+ readOnly: createOptions.readOnly,
1101
+ defaultBranch: createOptions.setDefaultBranch,
1102
+ source: createOptions.source
1103
+ });
1104
+ addRepo(info);
1105
+ const token = createToken(name);
1106
+ return {
1107
+ id: info.id,
1108
+ name: info.name,
1109
+ description: info.description,
1110
+ defaultBranch: info.defaultBranch,
1111
+ remote: info.remote,
1112
+ token: token.plaintext,
1113
+ tokenExpiresAt: token.expiresAt
1114
+ };
1115
+ };
1116
+ return {
1117
+ create: createRepo,
1118
+ async get(name) {
1119
+ const repo = repos.get(name);
1120
+ return repo ? createRepoHandle(repo) : null;
1121
+ },
1122
+ async import(params) {
1123
+ return createRepo(params.target.name, {
1124
+ description: params.target.opts?.description,
1125
+ readOnly: params.target.opts?.readOnly,
1126
+ source: params.source.url
1127
+ });
1353
1128
  },
1354
- async status() {
1129
+ async list(opts) {
1130
+ const limit = opts?.limit ?? 50;
1131
+ const repoList = Array.from(repos.values()).slice(0, limit).map((repo) => {
1132
+ const { remote: _remote, ...rest } = repo;
1133
+ return rest;
1134
+ });
1355
1135
  return {
1356
- status,
1357
- ...error && { error },
1358
- ...output !== undefined && { output }
1136
+ repos: repoList,
1137
+ total: repos.size,
1138
+ ...repos.size > repoList.length && { cursor: String(repoList.length) }
1359
1139
  };
1360
1140
  },
1361
- async sendEvent(_event) {}
1141
+ async delete(name) {
1142
+ tokens.delete(name);
1143
+ return repos.delete(name);
1144
+ }
1362
1145
  };
1363
1146
  }
1364
- function createMockWorkflow(options = {}) {
1365
- const instances = new Map;
1366
- let sequence = 0;
1367
- for (const [id, instanceOptions] of Object.entries(options.instances ?? {})) {
1368
- instances.set(id, createMockWorkflowInstance(id, instanceOptions));
1147
+ // src/test/utilities/env.ts
1148
+ function createMockEnv(options = {}) {
1149
+ const env2 = {};
1150
+ if (options.kv) {
1151
+ for (const name of options.kv) {
1152
+ env2[name] = createMockKV();
1153
+ }
1369
1154
  }
1370
- const createInstance = (id) => {
1371
- if (instances.has(id)) {
1372
- throw new Error(`Mock Workflow already has an instance named "${id}".`);
1155
+ if (options.d1) {
1156
+ for (const name of options.d1) {
1157
+ env2[name] = createMockD1();
1373
1158
  }
1374
- const instance = createMockWorkflowInstance(id);
1375
- instances.set(id, instance);
1376
- return instance;
1377
- };
1378
- return {
1379
- async get(id) {
1380
- const instance = instances.get(id);
1381
- if (!instance) {
1382
- throw new Error(`Mock Workflow has no instance named "${id}".`);
1383
- }
1384
- return instance;
1385
- },
1386
- async create(options2) {
1387
- const id = options2?.id ?? `mock-workflow-${++sequence}`;
1388
- return createInstance(id);
1389
- },
1390
- async createBatch(batch) {
1391
- return batch.map((options2) => {
1392
- const id = options2.id ?? `mock-workflow-${++sequence}`;
1393
- return createInstance(id);
1394
- });
1159
+ }
1160
+ if (options.r2) {
1161
+ for (const name of options.r2) {
1162
+ env2[name] = createMockR2();
1395
1163
  }
1396
- };
1397
- }
1398
- function createMockPipeline() {
1399
- const records = [];
1400
- return {
1401
- async send(batch) {
1402
- records.push(...batch);
1403
- },
1404
- _getRecords() {
1405
- return [...records];
1164
+ }
1165
+ if (options.queues) {
1166
+ for (const name of options.queues) {
1167
+ env2[name] = createMockQueue();
1406
1168
  }
1407
- };
1408
- }
1409
- // src/test/utilities/media.ts
1410
- function createEmptyImageStream() {
1411
- return new ReadableStream({
1412
- start(controller) {
1413
- controller.close();
1169
+ }
1170
+ if (options.rateLimits) {
1171
+ for (const [name, rateLimitOptions] of Object.entries(options.rateLimits)) {
1172
+ env2[name] = createMockRateLimit(rateLimitOptions);
1414
1173
  }
1415
- });
1416
- }
1417
- function createMockImageTransformationResult(response) {
1418
- return {
1419
- response() {
1420
- return response.clone();
1421
- },
1422
- contentType() {
1423
- return response.headers.get("Content-Type") ?? "image/png";
1424
- },
1425
- image() {
1426
- const cloned = response.clone();
1427
- return cloned.body ?? createEmptyImageStream();
1174
+ }
1175
+ if (options.versionMetadata) {
1176
+ env2[options.versionMetadata] = createMockVersionMetadata();
1177
+ }
1178
+ if (options.hyperdrive) {
1179
+ for (const [name, binding] of Object.entries(options.hyperdrive)) {
1180
+ env2[name] = typeof binding === "string" ? createMockHyperdrive(binding) : binding;
1428
1181
  }
1429
- };
1430
- }
1431
- function createMockImageTransformer(response) {
1432
- const transformer = {
1433
- transform(_transform) {
1434
- return transformer;
1435
- },
1436
- draw(_image, _options) {
1437
- return transformer;
1438
- },
1439
- async output(_options) {
1440
- return createMockImageTransformationResult(response);
1182
+ }
1183
+ if (Array.isArray(options.workerLoaders)) {
1184
+ for (const name of options.workerLoaders) {
1185
+ env2[name] = createMockWorkerLoader();
1441
1186
  }
1442
- };
1443
- return transformer;
1444
- }
1445
- function createMockHostedImagesBinding() {
1446
- const unsupported = () => {
1447
- throw new Error("Mock Images hosted API is not implemented. Pass a custom ImagesBinding through createMockEnv({ images }) if your test needs hosted image behavior.");
1448
- };
1449
- return {
1450
- image(_imageId) {
1451
- return {
1452
- details: unsupported,
1453
- bytes: unsupported,
1454
- update: unsupported,
1455
- delete: unsupported
1456
- };
1457
- },
1458
- upload: unsupported,
1459
- list: unsupported
1460
- };
1461
- }
1462
- function createMockImagesBinding(options = {}) {
1463
- const response = options.response ?? new Response("", {
1464
- headers: { "Content-Type": "image/png" }
1465
- });
1466
- const info = options.info ?? {
1467
- format: response.headers.get("Content-Type") ?? "image/png",
1468
- fileSize: 0,
1469
- width: 0,
1470
- height: 0
1471
- };
1472
- return {
1473
- async info(_stream, _options) {
1474
- return info;
1475
- },
1476
- input(_stream, _options) {
1477
- return createMockImageTransformer(response);
1478
- },
1479
- hosted: createMockHostedImagesBinding()
1480
- };
1187
+ } else if (options.workerLoaders) {
1188
+ for (const [name, workerLoaderOptions] of Object.entries(options.workerLoaders)) {
1189
+ env2[name] = createMockWorkerLoader(workerLoaderOptions);
1190
+ }
1191
+ }
1192
+ if (Array.isArray(options.mtlsCertificates)) {
1193
+ for (const name of options.mtlsCertificates) {
1194
+ env2[name] = createMockMTLSCertificate();
1195
+ }
1196
+ } else if (options.mtlsCertificates) {
1197
+ for (const [name, handler] of Object.entries(options.mtlsCertificates)) {
1198
+ env2[name] = createMockMTLSCertificate(handler);
1199
+ }
1200
+ }
1201
+ if (Array.isArray(options.dispatchNamespaces)) {
1202
+ for (const name of options.dispatchNamespaces) {
1203
+ env2[name] = createMockDispatchNamespace();
1204
+ }
1205
+ } else if (options.dispatchNamespaces) {
1206
+ for (const [name, dispatchNamespaceOptions] of Object.entries(options.dispatchNamespaces)) {
1207
+ env2[name] = createMockDispatchNamespace(dispatchNamespaceOptions);
1208
+ }
1209
+ }
1210
+ if (Array.isArray(options.workflows)) {
1211
+ for (const name of options.workflows) {
1212
+ env2[name] = createMockWorkflow();
1213
+ }
1214
+ } else if (options.workflows) {
1215
+ for (const [name, workflowOptions] of Object.entries(options.workflows)) {
1216
+ env2[name] = "create" in workflowOptions ? workflowOptions : createMockWorkflow(workflowOptions);
1217
+ }
1218
+ }
1219
+ if (Array.isArray(options.pipelines)) {
1220
+ for (const name of options.pipelines) {
1221
+ env2[name] = createMockPipeline();
1222
+ }
1223
+ } else if (options.pipelines) {
1224
+ for (const [name, pipeline] of Object.entries(options.pipelines)) {
1225
+ env2[name] = pipeline;
1226
+ }
1227
+ }
1228
+ if (typeof options.images === "string") {
1229
+ env2[options.images] = createMockImagesBinding();
1230
+ } else if (options.images) {
1231
+ env2.IMAGES = options.images;
1232
+ }
1233
+ if (typeof options.media === "string") {
1234
+ env2[options.media] = createMockMediaBinding();
1235
+ } else if (options.media) {
1236
+ env2.MEDIA = options.media;
1237
+ }
1238
+ if (Array.isArray(options.artifacts)) {
1239
+ for (const name of options.artifacts) {
1240
+ env2[name] = createMockArtifacts();
1241
+ }
1242
+ } else if (options.artifacts) {
1243
+ for (const [name, artifactsOptions] of Object.entries(options.artifacts)) {
1244
+ env2[name] = isArtifactsBinding(artifactsOptions) ? artifactsOptions : createMockArtifacts(artifactsOptions);
1245
+ }
1246
+ }
1247
+ if (options.secretsStore) {
1248
+ for (const [name, value] of Object.entries(options.secretsStore)) {
1249
+ env2[name] = createMockSecretsStoreSecret(value);
1250
+ }
1251
+ }
1252
+ if (options.vars) {
1253
+ Object.assign(env2, options.vars);
1254
+ }
1255
+ if (options.secrets) {
1256
+ Object.assign(env2, options.secrets);
1257
+ }
1258
+ if (options.custom) {
1259
+ Object.assign(env2, options.custom);
1260
+ }
1261
+ return env2;
1481
1262
  }
1482
- function createEmptyMediaStream() {
1483
- return new ReadableStream({
1484
- start(controller) {
1485
- controller.close();
1263
+ // src/test/simple-context-bindings.ts
1264
+ function buildRemoteAndStaticBindings(config) {
1265
+ const remoteBindings = {};
1266
+ if (isRemoteModeActive()) {
1267
+ if (config.bindings?.ai) {
1268
+ const aiBindingName = config.bindings.ai.binding || "AI";
1269
+ remoteBindings[aiBindingName] = createRemoteAI(config.accountId);
1270
+ }
1271
+ if (config.bindings?.vectorize) {
1272
+ for (const [name, vectorConfig] of Object.entries(config.bindings.vectorize)) {
1273
+ remoteBindings[name] = createRemoteVectorize(vectorConfig.indexName, config.accountId);
1274
+ }
1275
+ }
1276
+ }
1277
+ if (config.vars) {
1278
+ for (const [key, value] of Object.entries(config.vars)) {
1279
+ remoteBindings[key] = value;
1486
1280
  }
1487
- });
1488
- }
1489
- function createMockMediaTransformationResult(response) {
1490
- return {
1491
- async media() {
1492
- const cloned = response.clone();
1493
- return cloned.body ?? createEmptyMediaStream();
1494
- },
1495
- async response() {
1496
- return response.clone();
1497
- },
1498
- async contentType() {
1499
- return response.headers.get("Content-Type") ?? "video/mp4";
1281
+ }
1282
+ if (config.bindings?.sendEmail) {
1283
+ for (const [name, binding] of Object.entries(config.bindings.sendEmail)) {
1284
+ remoteBindings[name] = createLocalSendEmailBinding(binding);
1500
1285
  }
1501
- };
1502
- }
1503
- function createMockMediaTransformer(response) {
1504
- const transformer = {
1505
- transform(_transform) {
1506
- return {
1507
- output(_output) {
1508
- return createMockMediaTransformationResult(response);
1509
- }
1510
- };
1511
- },
1512
- output(_output) {
1513
- return createMockMediaTransformationResult(response);
1286
+ }
1287
+ if (config.bindings?.workerLoaders) {
1288
+ for (const name of Object.keys(config.bindings.workerLoaders)) {
1289
+ remoteBindings[name] = createLocalWorkerLoaderBinding();
1514
1290
  }
1515
- };
1516
- return transformer;
1291
+ }
1292
+ if (config.bindings?.versionMetadata) {
1293
+ remoteBindings[config.bindings.versionMetadata.binding] = createMockVersionMetadata();
1294
+ }
1295
+ return remoteBindings;
1517
1296
  }
1518
- function createMockMediaBinding(options = {}) {
1519
- const response = options.response ?? new Response("", {
1520
- headers: { "Content-Type": "video/mp4" }
1521
- });
1522
- return {
1523
- input(_media) {
1524
- return createMockMediaTransformer(response);
1525
- }
1526
- };
1297
+
1298
+ // src/test/simple-context-durable-objects.ts
1299
+ import { mkdirSync, writeFileSync } from "fs";
1300
+ import { dirname as dirname2, join as join2 } from "path";
1301
+ import { readFile } from "fs/promises";
1302
+
1303
+ // src/test/simple-context-gateway-script.ts
1304
+ function buildGatewayScript(bundledCode, wrappers, nativeRpcBindingNames = []) {
1305
+ const nativeRpcBindingsLiteral = JSON.stringify(nativeRpcBindingNames);
1306
+ return `
1307
+ // Bundled transport + DO classes
1308
+ ${bundledCode}
1309
+
1310
+ // DO Wrappers with RPC
1311
+ ${wrappers}
1312
+
1313
+ const __nativeRpcBindings = new Set(${nativeRpcBindingsLiteral})
1314
+
1315
+ // Transport encoding helper
1316
+ const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
1317
+
1318
+ function __encodeTransport(value) {
1319
+ if (value === null || value === undefined) return value
1320
+
1321
+ // Try each encoder
1322
+ for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
1323
+ const encoded = transporter.encode(value)
1324
+ if (encoded !== false && encoded !== undefined) {
1325
+ return { __transport: typeName, value: encoded }
1326
+ }
1327
+ }
1328
+
1329
+ // Recursively encode arrays and objects
1330
+ if (Array.isArray(value)) {
1331
+ return value.map(__encodeTransport)
1332
+ }
1333
+ if (typeof value === 'object') {
1334
+ const result = {}
1335
+ for (const [k, v] of Object.entries(value)) {
1336
+ result[k] = __encodeTransport(v)
1337
+ }
1338
+ return result
1339
+ }
1340
+
1341
+ return value
1527
1342
  }
1528
- // src/test/utilities/artifacts.ts
1529
- function createArtifactTimestamp() {
1530
- return new Date("2026-04-26T00:00:00.000Z").toISOString();
1343
+
1344
+ // Gateway with WebSocket RPC
1345
+ export default {
1346
+ async fetch(request, env) {
1347
+ if (request.headers.get('Upgrade') === 'websocket') {
1348
+ const { 0: client, 1: server } = new WebSocketPair()
1349
+ server.accept()
1350
+ server.addEventListener('message', async (e) => {
1351
+ try {
1352
+ const m = JSON.parse(e.data)
1353
+ if (m.t === 'rpc.call') {
1354
+ const result = await executeRpc(env, m.method, m.params)
1355
+ server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
1356
+ }
1357
+ } catch (error) {
1358
+ server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
1359
+ }
1360
+ })
1361
+ return new Response(null, { status: 101, webSocket: client })
1362
+ }
1363
+ return new Response('Gateway')
1364
+ }
1531
1365
  }
1532
- function createArtifactsRepoInfo(name, options = {}) {
1533
- const now = createArtifactTimestamp();
1534
- return {
1535
- id: `repo-${name}`,
1536
- name,
1537
- description: options.description ?? null,
1538
- defaultBranch: options.defaultBranch ?? "main",
1539
- createdAt: now,
1540
- updatedAt: now,
1541
- lastPushAt: null,
1542
- source: options.source ?? null,
1543
- readOnly: options.readOnly ?? false,
1544
- remote: `https://example.com/artifacts/default/${name}.git`
1545
- };
1366
+
1367
+ async function executeRpc(env, method, params) {
1368
+ const [bindingName, ...rest] = method.split('.')
1369
+ let op = rest.join('.')
1370
+ const binding = env[bindingName]
1371
+ const RAW_EMAIL = 'EmailMessage::raw'
1372
+ if (!binding) throw new Error('Binding not found: ' + bindingName)
1373
+
1374
+ // Normalize namespaced op names (kv.*, r2.*, d1.*, do.*, queue.*, ai.*, var.*)
1375
+ // down to the legacy verbs this dispatcher historically used. The bridge
1376
+ // proxy always emits namespaced forms (see src/bridge/proxy.ts and B3 in
1377
+ // REMAINING.md); this keeps the dispatcher backwards-compatible while the
1378
+ // rest of the codebase converges on the namespaced convention.
1379
+ if (op.indexOf('kv.') === 0) op = op.slice(3)
1380
+ else if (op.indexOf('do.') === 0) {
1381
+ const tail = op.slice(3)
1382
+ if (tail === 'fetch') op = 'stub.fetch'
1383
+ else if (tail === 'rpc') op = 'stub.rpc'
1384
+ else op = tail
1385
+ }
1386
+ else if (op.indexOf('queue.') === 0) op = op.slice(6)
1387
+ else if (op.indexOf('ai.') === 0) op = op.slice(3)
1388
+ else if (op.indexOf('var.') === 0) op = op.slice(4)
1389
+ else if (op.indexOf('d1.stmt.') === 0) op = 'prepare.' + op.slice('d1.stmt.'.length)
1390
+ else if (op.indexOf('d1.') === 0) op = op.slice(3)
1391
+ // r2.* and email.* keep their existing prefixes
1392
+
1393
+ // KV operations
1394
+ if (op === 'get') return binding.get(params[0], params[1])
1395
+ if (op === 'put') return binding.put(params[0], params[1], params[2])
1396
+ if (op === 'delete') return binding.delete(params[0])
1397
+ if (op === 'list') return binding.list(params[0])
1398
+ if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
1399
+
1400
+ // R2 operations
1401
+ if (op === 'r2.get') return binding.get(params[0], params[1])
1402
+ if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
1403
+ if (op === 'r2.delete') return binding.delete(params[0])
1404
+ if (op === 'r2.list') return binding.list(params[0])
1405
+ if (op === 'r2.head' || op === 'head') return binding.head(params[0])
1406
+
1407
+ // D1 operations
1408
+ if (op === 'exec') return binding.exec(params[0])
1409
+ if (op === 'dump') return binding.dump()
1410
+ if (op === 'batch') {
1411
+ const stmts = params[0].map(s => {
1412
+ const stmt = binding.prepare(s.sql)
1413
+ return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
1414
+ })
1415
+ return binding.batch(stmts)
1416
+ }
1417
+ if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
1418
+ if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
1419
+ if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
1420
+ if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
1421
+
1422
+ // Send email operations
1423
+ if (op === 'email.send') {
1424
+ return binding.send(__normalizeEmailMessage(params[0]))
1425
+ }
1426
+
1427
+ // DO operations
1428
+ if (op === 'idFromName') {
1429
+ return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
1430
+ }
1431
+ if (op === 'stub.rpc') {
1432
+ const [, idSerialized, rpcMethod, rpcParams] = params
1433
+ const stub = binding.get(binding.idFromString(idSerialized.hex))
1434
+
1435
+ if (__nativeRpcBindings.has(bindingName) && typeof stub[rpcMethod] === 'function') {
1436
+ let result = await stub[rpcMethod](...(rpcParams || []))
1437
+ result = __encodeTransport(result)
1438
+ return result
1439
+ }
1440
+
1441
+ const response = await stub.fetch(new Request('http://do/_rpc', {
1442
+ method: 'POST',
1443
+ headers: { 'Content-Type': 'application/json' },
1444
+ body: JSON.stringify({ method: rpcMethod, params: rpcParams || [] })
1445
+ }))
1446
+
1447
+ const payload = await response.json()
1448
+ if (!response.ok || !payload?.ok) {
1449
+ throw new Error(payload?.error?.message || ('DO RPC failed with status ' + response.status))
1450
+ }
1451
+
1452
+ return payload.result
1453
+ }
1454
+
1455
+ throw new Error('Unknown operation: ' + method)
1456
+ }
1457
+
1458
+ function __createEmailMessageRaw(raw) {
1459
+ if (typeof raw === 'string' || raw instanceof ReadableStream) {
1460
+ return raw
1461
+ }
1462
+ if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
1463
+ return new Response(raw).body
1464
+ }
1465
+ throw new Error('Unsupported EmailMessage raw payload')
1466
+ }
1467
+
1468
+ function __buildRawEmail(message) {
1469
+ const lines = []
1470
+ const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
1471
+
1472
+ lines.push('From: ' + message.from)
1473
+ lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
1474
+ lines.push('Date: ' + new Date().toUTCString())
1475
+ lines.push('Message-ID: ' + messageId)
1476
+
1477
+ if (message.subject) lines.push('Subject: ' + message.subject)
1478
+ if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
1479
+ if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
1480
+ if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
1481
+
1482
+ for (const [key, value] of Object.entries(message.headers || {})) {
1483
+ lines.push(key + ': ' + value)
1484
+ }
1485
+
1486
+ lines.push('MIME-Version: 1.0')
1487
+ lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
1488
+ lines.push('')
1489
+ lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
1490
+
1491
+ return lines.join('\\r\\n')
1546
1492
  }
1547
- function isArtifactsBinding(value) {
1548
- return typeof value.create === "function";
1493
+
1494
+ function __normalizeEmailMessage(message) {
1495
+ if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
1496
+ return message
1497
+ }
1498
+ if ('EmailMessage::raw' in message) {
1499
+ return message
1500
+ }
1501
+ if ('raw' in message && message.raw !== undefined) {
1502
+ return {
1503
+ from: message.from,
1504
+ to: message.to,
1505
+ [RAW_EMAIL]: __createEmailMessageRaw(message.raw)
1506
+ }
1507
+ }
1508
+ return {
1509
+ from: message.from,
1510
+ to: message.to,
1511
+ [RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
1512
+ }
1549
1513
  }
1550
- function createMockArtifacts(options = {}) {
1551
- const repos = new Map;
1552
- const tokens = new Map;
1553
- const addRepo = (info) => {
1554
- repos.set(info.name, info);
1555
- if (!tokens.has(info.name)) {
1556
- tokens.set(info.name, []);
1557
- }
1558
- };
1559
- for (const repo of options.repos ?? []) {
1560
- addRepo({
1561
- ...createArtifactsRepoInfo(repo.name),
1562
- ...repo
1563
- });
1564
- }
1565
- const createToken = (repoName, scope = "write", ttl = 86400) => {
1566
- const existing = tokens.get(repoName) ?? [];
1567
- const id = `token-${repoName}-${existing.length + 1}`;
1568
- const expiresAt = new Date(Date.parse(createArtifactTimestamp()) + ttl * 1000).toISOString();
1569
- const token = {
1570
- id,
1571
- scope,
1572
- state: "active",
1573
- createdAt: createArtifactTimestamp(),
1574
- expiresAt
1575
- };
1576
- tokens.set(repoName, [...existing, token]);
1577
- return {
1578
- id,
1579
- plaintext: `${id}-plaintext`,
1580
- scope,
1581
- expiresAt
1582
- };
1583
- };
1584
- const createRepoHandle = (info) => ({
1585
- ...info,
1586
- async createToken(scope, ttl) {
1587
- return createToken(info.name, scope, ttl);
1588
- },
1589
- async listTokens() {
1590
- const repoTokens = tokens.get(info.name) ?? [];
1591
- return {
1592
- tokens: repoTokens,
1593
- total: repoTokens.length
1594
- };
1595
- },
1596
- async revokeToken(tokenOrId) {
1597
- const repoTokens = tokens.get(info.name) ?? [];
1598
- const index = repoTokens.findIndex((token) => token.id === tokenOrId);
1599
- if (index === -1) {
1600
- return false;
1601
- }
1602
- repoTokens[index] = {
1603
- ...repoTokens[index],
1604
- state: "revoked"
1605
- };
1606
- tokens.set(info.name, repoTokens);
1607
- return true;
1608
- },
1609
- async fork(name, forkOptions) {
1610
- return createRepo(name, {
1611
- description: forkOptions?.description ?? info.description ?? undefined,
1612
- readOnly: forkOptions?.readOnly ?? info.readOnly,
1613
- setDefaultBranch: info.defaultBranch,
1614
- source: `artifacts:default/${info.name}`
1615
- });
1616
- }
1617
- });
1618
- const createRepo = async (name, createOptions = {}) => {
1619
- const info = createArtifactsRepoInfo(name, {
1620
- description: createOptions.description,
1621
- readOnly: createOptions.readOnly,
1622
- defaultBranch: createOptions.setDefaultBranch,
1623
- source: createOptions.source
1624
- });
1625
- addRepo(info);
1626
- const token = createToken(name);
1627
- return {
1628
- id: info.id,
1629
- name: info.name,
1630
- description: info.description,
1631
- defaultBranch: info.defaultBranch,
1632
- remote: info.remote,
1633
- token: token.plaintext,
1634
- tokenExpiresAt: token.expiresAt
1635
- };
1636
- };
1637
- return {
1638
- create: createRepo,
1639
- async get(name) {
1640
- const repo = repos.get(name);
1641
- return repo ? createRepoHandle(repo) : null;
1642
- },
1643
- async import(params) {
1644
- return createRepo(params.target.name, {
1645
- description: params.target.opts?.description,
1646
- readOnly: params.target.opts?.readOnly,
1647
- source: params.source.url
1648
- });
1649
- },
1650
- async list(opts) {
1651
- const limit = opts?.limit ?? 50;
1652
- const repoList = Array.from(repos.values()).slice(0, limit).map((repo) => {
1653
- const { remote: _remote, ...rest } = repo;
1654
- return rest;
1655
- });
1656
- return {
1657
- repos: repoList,
1658
- total: repos.size,
1659
- ...repos.size > repoList.length && { cursor: String(repoList.length) }
1660
- };
1661
- },
1662
- async delete(name) {
1663
- tokens.delete(name);
1664
- return repos.delete(name);
1665
- }
1666
- };
1514
+ `;
1667
1515
  }
1668
- // src/test/utilities/env.ts
1669
- function createMockEnv(options = {}) {
1670
- const env2 = {};
1671
- if (options.kv) {
1672
- for (const name of options.kv) {
1673
- env2[name] = createMockKV();
1674
- }
1675
- }
1676
- if (options.d1) {
1677
- for (const name of options.d1) {
1678
- env2[name] = createMockD1();
1679
- }
1680
- }
1681
- if (options.r2) {
1682
- for (const name of options.r2) {
1683
- env2[name] = createMockR2();
1684
- }
1685
- }
1686
- if (options.queues) {
1687
- for (const name of options.queues) {
1688
- env2[name] = createMockQueue();
1689
- }
1690
- }
1691
- if (options.rateLimits) {
1692
- for (const [name, rateLimitOptions] of Object.entries(options.rateLimits)) {
1693
- env2[name] = createMockRateLimit(rateLimitOptions);
1694
- }
1695
- }
1696
- if (options.versionMetadata) {
1697
- env2[options.versionMetadata] = createMockVersionMetadata();
1698
- }
1699
- if (options.hyperdrive) {
1700
- for (const [name, binding] of Object.entries(options.hyperdrive)) {
1701
- env2[name] = typeof binding === "string" ? createMockHyperdrive(binding) : binding;
1702
- }
1703
- }
1704
- if (Array.isArray(options.workerLoaders)) {
1705
- for (const name of options.workerLoaders) {
1706
- env2[name] = createMockWorkerLoader();
1707
- }
1708
- } else if (options.workerLoaders) {
1709
- for (const [name, workerLoaderOptions] of Object.entries(options.workerLoaders)) {
1710
- env2[name] = createMockWorkerLoader(workerLoaderOptions);
1711
- }
1516
+
1517
+ // src/test/simple-context-paths.ts
1518
+ import { existsSync } from "fs";
1519
+ import { createServer } from "net";
1520
+ import { dirname, join } from "path";
1521
+ import { fileURLToPath } from "url";
1522
+ var DEFAULT_TRANSPORT_ENTRY_FILES = [
1523
+ "src/transport.ts",
1524
+ "src/transport.js",
1525
+ "src/transport.mts",
1526
+ "src/transport.mjs"
1527
+ ];
1528
+ var CURRENT_PACKAGE_ROOT = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
1529
+ function getBunRuntime() {
1530
+ const g = globalThis;
1531
+ if (typeof g.Bun === "object" && g.Bun !== null) {
1532
+ return g.Bun;
1712
1533
  }
1713
- if (Array.isArray(options.mtlsCertificates)) {
1714
- for (const name of options.mtlsCertificates) {
1715
- env2[name] = createMockMTLSCertificate();
1716
- }
1717
- } else if (options.mtlsCertificates) {
1718
- for (const [name, handler] of Object.entries(options.mtlsCertificates)) {
1719
- env2[name] = createMockMTLSCertificate(handler);
1720
- }
1534
+ return;
1535
+ }
1536
+ function getCallerDirectory() {
1537
+ const stackCallerDirectory = getStackCallerDirectory();
1538
+ if (stackCallerDirectory) {
1539
+ return stackCallerDirectory;
1721
1540
  }
1722
- if (Array.isArray(options.dispatchNamespaces)) {
1723
- for (const name of options.dispatchNamespaces) {
1724
- env2[name] = createMockDispatchNamespace();
1725
- }
1726
- } else if (options.dispatchNamespaces) {
1727
- for (const [name, dispatchNamespaceOptions] of Object.entries(options.dispatchNamespaces)) {
1728
- env2[name] = createMockDispatchNamespace(dispatchNamespaceOptions);
1541
+ return process.cwd();
1542
+ }
1543
+ function getStackCallerDirectory() {
1544
+ const originalPrepare = Error.prepareStackTrace;
1545
+ Error.prepareStackTrace = (_, stack) => stack;
1546
+ try {
1547
+ const err = new Error;
1548
+ const stack = err.stack;
1549
+ for (const site of stack ?? []) {
1550
+ const filename = site.getFileName?.();
1551
+ if (filename && !isInsideCurrentPackage(filename) && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync(filename)) {
1552
+ return dirname(filename);
1553
+ }
1729
1554
  }
1555
+ } finally {
1556
+ Error.prepareStackTrace = originalPrepare;
1730
1557
  }
1731
- if (Array.isArray(options.workflows)) {
1732
- for (const name of options.workflows) {
1733
- env2[name] = createMockWorkflow();
1558
+ return null;
1559
+ }
1560
+ function findPackageRoot(startDir) {
1561
+ let currentDir = startDir;
1562
+ while (true) {
1563
+ if (existsSync(join(currentDir, "package.json"))) {
1564
+ return currentDir;
1734
1565
  }
1735
- } else if (options.workflows) {
1736
- for (const [name, workflowOptions] of Object.entries(options.workflows)) {
1737
- env2[name] = "create" in workflowOptions ? workflowOptions : createMockWorkflow(workflowOptions);
1566
+ const parentDir = dirname(currentDir);
1567
+ if (parentDir === currentDir) {
1568
+ return startDir;
1738
1569
  }
1570
+ currentDir = parentDir;
1739
1571
  }
1740
- if (Array.isArray(options.pipelines)) {
1741
- for (const name of options.pipelines) {
1742
- env2[name] = createMockPipeline();
1572
+ }
1573
+ function isInsideCurrentPackage(filePath) {
1574
+ const normalizedFilePath = filePath.replace(/\\/g, "/");
1575
+ const normalizedPackageRoot = CURRENT_PACKAGE_ROOT.replace(/\\/g, "/");
1576
+ return normalizedFilePath === normalizedPackageRoot || normalizedFilePath.startsWith(`${normalizedPackageRoot}/`);
1577
+ }
1578
+ async function findNearestConfig(startDir) {
1579
+ let currentDir = startDir;
1580
+ while (true) {
1581
+ const configPath = await resolveConfigPath(currentDir);
1582
+ if (configPath) {
1583
+ return configPath;
1743
1584
  }
1744
- } else if (options.pipelines) {
1745
- for (const [name, pipeline] of Object.entries(options.pipelines)) {
1746
- env2[name] = pipeline;
1585
+ const parentDir = dirname(currentDir);
1586
+ if (parentDir === currentDir) {
1587
+ return null;
1747
1588
  }
1589
+ currentDir = parentDir;
1748
1590
  }
1749
- if (typeof options.images === "string") {
1750
- env2[options.images] = createMockImagesBinding();
1751
- } else if (options.images) {
1752
- env2.IMAGES = options.images;
1753
- }
1754
- if (typeof options.media === "string") {
1755
- env2[options.media] = createMockMediaBinding();
1756
- } else if (options.media) {
1757
- env2.MEDIA = options.media;
1591
+ }
1592
+ async function getAvailablePort() {
1593
+ return await new Promise((resolvePort, reject) => {
1594
+ const server = createServer();
1595
+ server.once("error", reject);
1596
+ server.listen(0, "127.0.0.1", () => {
1597
+ const address = server.address();
1598
+ if (!address || typeof address === "string") {
1599
+ server.close(() => reject(new Error("Could not determine an available port")));
1600
+ return;
1601
+ }
1602
+ const { port } = address;
1603
+ server.close((error) => {
1604
+ if (error) {
1605
+ reject(error);
1606
+ return;
1607
+ }
1608
+ resolvePort(port);
1609
+ });
1610
+ });
1611
+ });
1612
+ }
1613
+ function resolveTransportFile(configDir, configuredPath) {
1614
+ if (typeof configuredPath === "string") {
1615
+ return configuredPath;
1758
1616
  }
1759
- if (Array.isArray(options.artifacts)) {
1760
- for (const name of options.artifacts) {
1761
- env2[name] = createMockArtifacts();
1762
- }
1763
- } else if (options.artifacts) {
1764
- for (const [name, artifactsOptions] of Object.entries(options.artifacts)) {
1765
- env2[name] = isArtifactsBinding(artifactsOptions) ? artifactsOptions : createMockArtifacts(artifactsOptions);
1766
- }
1617
+ if (configuredPath === null) {
1618
+ return null;
1767
1619
  }
1768
- if (options.secretsStore) {
1769
- for (const [name, value] of Object.entries(options.secretsStore)) {
1770
- env2[name] = createMockSecretsStoreSecret(value);
1620
+ for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
1621
+ if (existsSync(join(configDir, defaultEntry))) {
1622
+ return defaultEntry;
1771
1623
  }
1772
1624
  }
1773
- if (options.vars) {
1774
- Object.assign(env2, options.vars);
1625
+ return null;
1626
+ }
1627
+
1628
+ // src/test/simple-context-durable-objects.ts
1629
+ function findExportedClasses(code) {
1630
+ const classes = [];
1631
+ const classPattern = /export\s+class\s+(\w+)/g;
1632
+ let match;
1633
+ while ((match = classPattern.exec(code)) !== null) {
1634
+ classes.push(match[1]);
1775
1635
  }
1776
- if (options.secrets) {
1777
- Object.assign(env2, options.secrets);
1636
+ return classes;
1637
+ }
1638
+ function classSupportsNativeDurableObjectRpc(code, className) {
1639
+ const nativeRpcPattern = new RegExp(`export\\s+class\\s+${className}\\s+extends\\s+DurableObject\\b`);
1640
+ return nativeRpcPattern.test(code);
1641
+ }
1642
+ function toGeneratedIdentifier(value) {
1643
+ const normalized = value.replace(/[^A-Za-z0-9_$]/g, "_");
1644
+ return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
1645
+ }
1646
+ async function discoverLocalDurableObjectClasses(config, configDir) {
1647
+ const classToFilePath = new Map;
1648
+ const doPatternConfig = config.files?.durableObjects;
1649
+ const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
1650
+ if (doPatternConfig === false) {
1651
+ return classToFilePath;
1778
1652
  }
1779
- if (options.custom) {
1780
- Object.assign(env2, options.custom);
1653
+ const doFiles = await findFiles(doPattern, { cwd: configDir });
1654
+ for (const filePath of doFiles) {
1655
+ try {
1656
+ const code = await readFile(filePath, "utf-8");
1657
+ const classNames = findExportedClasses(code);
1658
+ for (const className of classNames) {
1659
+ classToFilePath.set(className, {
1660
+ filePath,
1661
+ nativeRpc: classSupportsNativeDurableObjectRpc(code, className)
1662
+ });
1663
+ }
1664
+ } catch {}
1781
1665
  }
1782
- return env2;
1666
+ return classToFilePath;
1783
1667
  }
1784
- // src/test/simple-context-bindings.ts
1785
- function buildRemoteAndStaticBindings(config) {
1786
- const remoteBindings = {};
1787
- if (isRemoteModeActive()) {
1788
- if (config.bindings?.ai) {
1789
- const aiBindingName = config.bindings.ai.binding || "AI";
1790
- remoteBindings[aiBindingName] = createRemoteAI(config.accountId);
1668
+ async function resolveLocalDurableObjects(config, configDir) {
1669
+ const doConfig = {};
1670
+ const doInfos = [];
1671
+ const classToFilePath = await discoverLocalDurableObjectClasses(config, configDir);
1672
+ for (const [name, rawDoInfo] of Object.entries(config.bindings?.durableObjects ?? {})) {
1673
+ const doInfo = normalizeDOBinding(rawDoInfo);
1674
+ if (doInfo.__ref) {
1675
+ continue;
1791
1676
  }
1792
- if (config.bindings?.vectorize) {
1793
- for (const [name, vectorConfig] of Object.entries(config.bindings.vectorize)) {
1794
- remoteBindings[name] = createRemoteVectorize(vectorConfig.indexName, config.accountId);
1677
+ let scriptPath;
1678
+ let nativeRpc = false;
1679
+ if (doInfo.kind === "cross-worker" && doInfo.scriptName) {
1680
+ scriptPath = join2(configDir, "src", doInfo.scriptName);
1681
+ try {
1682
+ const code = await readFile(scriptPath, "utf-8");
1683
+ nativeRpc = classSupportsNativeDurableObjectRpc(code, doInfo.className);
1684
+ } catch {
1685
+ nativeRpc = false;
1686
+ }
1687
+ } else {
1688
+ const discoveredClass = classToFilePath.get(doInfo.className);
1689
+ if (!discoveredClass) {
1690
+ throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
1691
+ ` + `Either:
1692
+ ` + ` 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
1693
+ ` + ` 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
1795
1694
  }
1695
+ scriptPath = discoveredClass.filePath;
1696
+ nativeRpc = discoveredClass.nativeRpc;
1796
1697
  }
1698
+ const runtimeClassName = nativeRpc ? doInfo.className : `__Devflare${toGeneratedIdentifier(name)}RpcWrapper`;
1699
+ doConfig[name] = runtimeClassName;
1700
+ doInfos.push({
1701
+ name,
1702
+ className: doInfo.className,
1703
+ scriptPath,
1704
+ nativeRpc,
1705
+ runtimeClassName
1706
+ });
1797
1707
  }
1798
- if (config.vars) {
1799
- for (const [key, value] of Object.entries(config.vars)) {
1800
- remoteBindings[key] = value;
1801
- }
1708
+ return {
1709
+ doConfig,
1710
+ doInfos
1711
+ };
1712
+ }
1713
+ function buildWrapperCode(doInfos) {
1714
+ return doInfos.filter((info) => !info.nativeRpc).map((info) => `
1715
+ export class ${info.runtimeClassName} {
1716
+ constructor(state, env) {
1717
+ this.__instance = new ${info.className}(state, env)
1718
+ }
1719
+
1720
+ async fetch(request) {
1721
+ const url = new URL(request.url)
1722
+ if (request.method !== 'POST' || url.pathname !== '/_rpc') {
1723
+ return new Response('Not found', { status: 404 })
1724
+ }
1725
+
1726
+ try {
1727
+ const payload = await request.json()
1728
+ const method = payload?.method
1729
+ const params = Array.isArray(payload?.params) ? payload.params : []
1730
+ const target = this.__instance?.[method]
1731
+
1732
+ if (typeof target !== 'function') {
1733
+ return new Response(JSON.stringify({
1734
+ ok: false,
1735
+ error: { message: 'Method not found: ' + String(method) }
1736
+ }), {
1737
+ status: 404,
1738
+ headers: { 'Content-Type': 'application/json' }
1739
+ })
1740
+ }
1741
+
1742
+ let result = await target.apply(this.__instance, params)
1743
+ result = __encodeTransport(result)
1744
+
1745
+ return new Response(JSON.stringify({ ok: true, result }), {
1746
+ headers: { 'Content-Type': 'application/json' }
1747
+ })
1748
+ } catch (error) {
1749
+ return new Response(JSON.stringify({
1750
+ ok: false,
1751
+ error: { message: error instanceof Error ? error.message : String(error) }
1752
+ }), {
1753
+ status: 500,
1754
+ headers: { 'Content-Type': 'application/json' }
1755
+ })
1756
+ }
1757
+ }
1758
+ }`.trim()).join(`
1759
+
1760
+ `);
1761
+ }
1762
+ async function bundleDurableObjectModules(configDir, doInfos, transportFile) {
1763
+ const virtualImports = [];
1764
+ const virtualExports = [];
1765
+ if (transportFile) {
1766
+ const transportPath = join2(configDir, transportFile);
1767
+ virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
1768
+ virtualExports.push("export { transport }");
1802
1769
  }
1803
- if (config.bindings?.sendEmail) {
1804
- for (const [name, binding] of Object.entries(config.bindings.sendEmail)) {
1805
- remoteBindings[name] = createLocalSendEmailBinding(binding);
1806
- }
1770
+ for (const info of doInfos) {
1771
+ virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
1772
+ virtualExports.push(`export { ${info.className} }`);
1807
1773
  }
1808
- if (config.bindings?.workerLoaders) {
1809
- for (const name of Object.keys(config.bindings.workerLoaders)) {
1810
- remoteBindings[name] = createLocalWorkerLoaderBinding();
1811
- }
1774
+ if (virtualImports.length === 0) {
1775
+ return "";
1776
+ }
1777
+ const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
1778
+ `);
1779
+ const virtualPath = join2(configDir, ".devflare", "__test_entry.ts");
1780
+ mkdirSync(dirname2(virtualPath), { recursive: true });
1781
+ writeFileSync(virtualPath, virtualEntry);
1782
+ const bun = getBunRuntime();
1783
+ if (!bun) {
1784
+ throw new Error("Bun runtime is required for createTestContext with Durable Objects");
1785
+ }
1786
+ const result = await bun.build({
1787
+ entrypoints: [virtualPath],
1788
+ target: "browser",
1789
+ format: "esm",
1790
+ minify: false,
1791
+ external: ["cloudflare:workers", "cloudflare:*"]
1792
+ });
1793
+ if (!result.success) {
1794
+ throw new Error(`Failed to bundle test entry: ${result.logs.join(`
1795
+ `)}`);
1812
1796
  }
1813
- if (config.bindings?.versionMetadata) {
1814
- remoteBindings[config.bindings.versionMetadata.binding] = createMockVersionMetadata();
1797
+ return await result.outputs[0].text();
1798
+ }
1799
+ async function buildDurableObjectGateway(config, configDir, transportFile) {
1800
+ const workflowEntrypointScript = await bundleWorkflowEntrypointScript(config, configDir);
1801
+ if (!config.bindings?.durableObjects) {
1802
+ return {
1803
+ script: buildGatewayScript(workflowEntrypointScript, "")
1804
+ };
1815
1805
  }
1816
- return remoteBindings;
1806
+ const { doConfig, doInfos } = await resolveLocalDurableObjects(config, configDir);
1807
+ const bundledCode = await bundleDurableObjectModules(configDir, doInfos, transportFile);
1808
+ const wrapperCode = buildWrapperCode(doInfos);
1809
+ const entrypointCode = [workflowEntrypointScript, bundledCode].filter(Boolean).join(`
1810
+
1811
+ `);
1812
+ const nativeRpcBindingNames = doInfos.filter((info) => info.nativeRpc).map((info) => info.name);
1813
+ return {
1814
+ durableObjects: doConfig,
1815
+ script: buildGatewayScript(entrypointCode, wrapperCode, nativeRpcBindingNames)
1816
+ };
1817
1817
  }
1818
1818
 
1819
1819
  // src/test/email.ts
@@ -2880,386 +2880,131 @@ async function fetchWithStartupRetries(fetchImpl, request) {
2880
2880
  }
2881
2881
  const deadline = Date.now() + 5000;
2882
2882
  let lastError;
2883
- while (Date.now() < deadline) {
2884
- try {
2885
- return await fetchImpl(request.clone());
2886
- } catch (error) {
2887
- if (!isTransientContainerFetchError(error)) {
2888
- throw error;
2889
- }
2890
- lastError = error;
2891
- await delay(100);
2892
- }
2893
- }
2894
- throw lastError;
2895
- }
2896
- function canRetryRequest(request) {
2897
- return (request.method === "GET" || request.method === "HEAD") && request.body === null;
2898
- }
2899
- function isTransientContainerFetchError(error) {
2900
- const value = error;
2901
- const code = value.code ?? value.cause?.code;
2902
- if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "UND_ERR_SOCKET") {
2903
- return true;
2904
- }
2905
- return typeof value.message === "string" && (value.message.includes("socket connection was closed") || value.message.includes("fetch failed"));
2906
- }
2907
- async function waitForTcpPort(host, port, timeoutMs) {
2908
- const start = Date.now();
2909
- let lastError;
2910
- while (Date.now() - start < timeoutMs) {
2911
- try {
2912
- await connectOnce(host, port);
2913
- return;
2914
- } catch (error) {
2915
- lastError = error;
2916
- await delay(100);
2917
- }
2918
- }
2919
- const message = lastError instanceof Error ? lastError.message : "timed out";
2920
- throw new Error(`Timed out waiting for container port ${host}:${port}: ${message}`);
2921
- }
2922
- function connectOnce(host, port) {
2923
- return new Promise((resolveConnection, rejectConnection) => {
2924
- const socket = createConnection({ host, port });
2925
- socket.once("connect", () => {
2926
- socket.destroy();
2927
- resolveConnection();
2928
- });
2929
- socket.once("error", rejectConnection);
2930
- });
2931
- }
2932
- function looksLikeLocalPath(image) {
2933
- return image === "Dockerfile" || image.startsWith(".") || image.startsWith("/") || image.startsWith("\\") || isAbsolute(image) || image.endsWith("/Dockerfile") || image.endsWith("\\Dockerfile");
2934
- }
2935
- function getOfflineBuildPullArg(engine) {
2936
- return engine === "podman" ? "--pull=never" : "--pull=false";
2937
- }
2938
- function makeLocalImageTag(className, configDir6, image) {
2939
- const hash = createHash("sha256").update(`${configDir6}:${image}`).digest("hex").slice(0, 12);
2940
- return `devflare-local-${sanitizeName(className)}:${hash}`;
2941
- }
2942
- function makeContainerName(className, instance) {
2943
- return `devflare-${sanitizeName(className)}-${sanitizeName(instance ?? "default")}-${randomUUID().slice(0, 8)}`;
2944
- }
2945
- function sanitizeName(value) {
2946
- return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "container";
2947
- }
2948
- function formatCommandFailure(result) {
2949
- return (result.stderr || result.stdout || `exit code ${result.exitCode}`).trim();
2950
- }
2951
- function isTruthyEnvFlag(value) {
2952
- return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
2953
- }
2954
-
2955
- // src/test/simple-context-lifecycle.ts
2956
- async function resolveTestContextConfig(configPath, callerDir = getCallerDirectory()) {
2957
- let absolutePath;
2958
- if (configPath) {
2959
- absolutePath = resolve2(callerDir, configPath);
2960
- } else {
2961
- const found = await findNearestConfig(callerDir);
2962
- if (!found) {
2963
- throw new Error(`Could not find a devflare config file. Searched upward from: ${callerDir}
2964
- ` + `Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
2965
- ` + `Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
2966
- }
2967
- absolutePath = found;
2968
- }
2969
- const configDir6 = dirname4(absolutePath);
2970
- const loadedConfig = await loadConfig({
2971
- cwd: configDir6,
2972
- configFile: absolutePath.split(/[/\\]/).pop()
2973
- });
2974
- const envResolvedConfig = await resolveConfigEnvVars(loadedConfig, {
2975
- cwd: configDir6,
2976
- configPath: absolutePath,
2977
- mode: "dev"
2978
- });
2979
- const config = await applyLocalDevVarsToConfig(envResolvedConfig, {
2980
- cwd: configDir6,
2981
- configPath: absolutePath
2982
- });
2983
- return { absolutePath, configDir: configDir6, config };
2984
- }
2985
- function createDisposeContext(state) {
2986
- return async () => {
2987
- if (state.client) {
2988
- await state.client.disconnect();
2989
- state.client = null;
2990
- }
2991
- if (state.miniflare) {
2992
- await state.miniflare.dispose();
2993
- state.miniflare = null;
2994
- }
2995
- await disposeLocalWorkerLoaderBindings();
2996
- await stopActiveContainers();
2997
- state.envProxy = null;
2998
- state.transportDecode = null;
2999
- state.remoteBindings = null;
3000
- state.miniflareBindings = null;
3001
- resetQueueState();
3002
- resetScheduledState();
3003
- resetWorkerState();
3004
- resetTailState();
3005
- resetEmailState();
3006
- __clearTestContext();
3007
- };
3008
- }
3009
-
3010
- // src/test/simple-context-startup.ts
3011
- var TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS = 3;
3012
- var TEST_CONTEXT_STARTUP_RETRY_DELAY_MS = 75;
3013
- var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS = 8;
3014
- var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS = 150;
3015
- function isRetriableTestContextStartupError(error) {
3016
- if (!(error instanceof Error)) {
3017
- return false;
3018
- }
3019
- const message = error.message.toLowerCase();
3020
- return message.includes("websocket connection failed") || message.includes("connection timeout: ws://") || message.includes("econnrefused") || message.includes("eaddrinuse") || message.includes("address already in use");
3021
- }
3022
- async function waitForTestContextStartupRetry() {
3023
- await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_STARTUP_RETRY_DELAY_MS));
3024
- }
3025
- async function waitForBridgeClientRetry() {
3026
- await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS));
3027
- }
3028
- async function connectBridgeClientWithRetry(url) {
3029
- let lastError;
3030
- for (let attempt = 1;attempt <= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS; attempt++) {
3031
- const client = new BridgeClient({ url });
3032
- try {
3033
- await client.connect();
3034
- return client;
3035
- } catch (error) {
3036
- lastError = error;
3037
- client.disconnect();
3038
- if (attempt >= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
3039
- throw error;
3040
- }
3041
- await waitForBridgeClientRetry();
3042
- }
3043
- }
3044
- throw lastError instanceof Error ? lastError : new Error("Bridge-backed test context could not connect to the WebSocket gateway.");
3045
- }
3046
- function expandLocalBindingWorkers(mfConfig) {
3047
- const auxiliaryWorkers = [
3048
- ...mfConfig.__devflareLocalSecretWorkers ?? [],
3049
- ...mfConfig.__devflareLocalBindingWorkers ?? []
3050
- ];
3051
- if (!Array.isArray(auxiliaryWorkers) || auxiliaryWorkers.length === 0) {
3052
- return mfConfig;
3053
- }
3054
- const {
3055
- __devflareLocalSecretWorkers,
3056
- __devflareLocalBindingWorkers,
3057
- port,
3058
- host,
3059
- log,
3060
- kvPersist,
3061
- r2Persist,
3062
- d1Persist,
3063
- durableObjectsPersist,
3064
- workflowsPersist,
3065
- imagesPersist,
3066
- ...primaryWorker
3067
- } = mfConfig;
3068
- const primaryWorkerName = typeof primaryWorker.name === "string" ? primaryWorker.name : "primary";
3069
- return {
3070
- ...port !== undefined && { port },
3071
- ...host && { host },
3072
- ...log && { log },
3073
- ...kvPersist && { kvPersist },
3074
- ...r2Persist && { r2Persist },
3075
- ...d1Persist && { d1Persist },
3076
- ...durableObjectsPersist && { durableObjectsPersist },
3077
- ...workflowsPersist && { workflowsPersist },
3078
- ...imagesPersist && { imagesPersist },
3079
- workers: [
3080
- {
3081
- ...primaryWorker,
3082
- name: primaryWorkerName
3083
- },
3084
- ...auxiliaryWorkers
3085
- ]
3086
- };
3087
- }
3088
- async function startBridgeBackedTestContext(mfConfig) {
3089
- const { Miniflare } = await import("miniflare");
3090
- for (let attempt = 1;attempt <= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS; attempt++) {
3091
- const port = await getAvailablePort();
3092
- let miniflare = null;
3093
- let client = null;
3094
- try {
3095
- miniflare = new Miniflare(expandLocalBindingWorkers({
3096
- ...mfConfig,
3097
- port
3098
- }));
3099
- await miniflare.ready;
3100
- const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
3101
- client = await connectBridgeClientWithRetry(`ws://localhost:${port}`);
3102
- return {
3103
- port,
3104
- client,
3105
- miniflare,
3106
- miniflareBindings
3107
- };
2883
+ while (Date.now() < deadline) {
2884
+ try {
2885
+ return await fetchImpl(request.clone());
3108
2886
  } catch (error) {
3109
- client?.disconnect();
3110
- if (miniflare) {
3111
- try {
3112
- await miniflare.dispose();
3113
- } catch {}
3114
- }
3115
- if (attempt >= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
2887
+ if (!isTransientContainerFetchError(error)) {
3116
2888
  throw error;
3117
2889
  }
3118
- await waitForTestContextStartupRetry();
2890
+ lastError = error;
2891
+ await delay(100);
3119
2892
  }
3120
2893
  }
3121
- throw new Error("Bridge-backed test context startup exhausted all retry attempts.");
2894
+ throw lastError;
3122
2895
  }
3123
-
3124
- // src/test/simple-context-runtime.ts
3125
- async function bootTestRuntime(mfConfig, usesMultiWorker) {
3126
- if (usesMultiWorker) {
3127
- const { Miniflare } = await import("miniflare");
3128
- const activePort = await getAvailablePort();
3129
- const miniflare = new Miniflare({
3130
- ...mfConfig,
3131
- port: activePort
3132
- });
3133
- await miniflare.ready;
3134
- const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
3135
- return { activePort, miniflare, miniflareBindings, client: null };
3136
- }
3137
- const started = await startBridgeBackedTestContext(mfConfig);
3138
- return {
3139
- activePort: started.port,
3140
- miniflare: started.miniflare,
3141
- miniflareBindings: started.miniflareBindings,
3142
- client: started.client
3143
- };
2896
+ function canRetryRequest(request) {
2897
+ return (request.method === "GET" || request.method === "HEAD") && request.body === null;
3144
2898
  }
3145
-
3146
- // src/test/simple-context-transport.ts
3147
- import { join as join10 } from "path";
3148
- async function loadTransportDecoders(configDir6, transportFile) {
3149
- const transportPath = join10(configDir6, transportFile);
3150
- const transportModule = await import(transportPath);
3151
- if (!transportModule.transport) {
3152
- console.warn(`[devflare] Warning: Transport file "${transportFile}" does not export a named "transport" object.
3153
- Expected: export const transport = { ... }
3154
- Transport encoding/decoding will be disabled.`);
3155
- return null;
3156
- }
3157
- const decoders = new Map;
3158
- for (const [typeName, transporter] of Object.entries(transportModule.transport)) {
3159
- const t = transporter;
3160
- decoders.set(typeName, t.decode);
2899
+ function isTransientContainerFetchError(error) {
2900
+ const value = error;
2901
+ const code = value.code ?? value.cause?.code;
2902
+ if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "UND_ERR_SOCKET") {
2903
+ return true;
3161
2904
  }
3162
- return decoders;
2905
+ return typeof value.message === "string" && (value.message.includes("socket connection was closed") || value.message.includes("fetch failed"));
3163
2906
  }
3164
- function decodeTransportValue(decoders, value) {
3165
- if (!decoders || value === null || typeof value !== "object") {
3166
- return value;
3167
- }
3168
- if ("__transport" in value) {
3169
- const encoded = value;
3170
- const decoder = decoders.get(encoded.__transport);
3171
- if (decoder) {
3172
- return decoder(encoded.value);
2907
+ async function waitForTcpPort(host, port, timeoutMs) {
2908
+ const start = Date.now();
2909
+ let lastError;
2910
+ while (Date.now() - start < timeoutMs) {
2911
+ try {
2912
+ await connectOnce(host, port);
2913
+ return;
2914
+ } catch (error) {
2915
+ lastError = error;
2916
+ await delay(100);
3173
2917
  }
3174
2918
  }
3175
- if (Array.isArray(value)) {
3176
- return value.map((item) => decodeTransportValue(decoders, item));
3177
- }
3178
- const result = {};
3179
- for (const [k, v] of Object.entries(value)) {
3180
- result[k] = decodeTransportValue(decoders, v);
3181
- }
3182
- return result;
2919
+ const message = lastError instanceof Error ? lastError.message : "timed out";
2920
+ throw new Error(`Timed out waiting for container port ${host}:${port}: ${message}`);
2921
+ }
2922
+ function connectOnce(host, port) {
2923
+ return new Promise((resolveConnection, rejectConnection) => {
2924
+ const socket = createConnection({ host, port });
2925
+ socket.once("connect", () => {
2926
+ socket.destroy();
2927
+ resolveConnection();
2928
+ });
2929
+ socket.once("error", rejectConnection);
2930
+ });
2931
+ }
2932
+ function looksLikeLocalPath(image) {
2933
+ return image === "Dockerfile" || image.startsWith(".") || image.startsWith("/") || image.startsWith("\\") || isAbsolute(image) || image.endsWith("/Dockerfile") || image.endsWith("\\Dockerfile");
2934
+ }
2935
+ function getOfflineBuildPullArg(engine) {
2936
+ return engine === "podman" ? "--pull=never" : "--pull=false";
2937
+ }
2938
+ function makeLocalImageTag(className, configDir6, image) {
2939
+ const hash = createHash("sha256").update(`${configDir6}:${image}`).digest("hex").slice(0, 12);
2940
+ return `devflare-local-${sanitizeName(className)}:${hash}`;
2941
+ }
2942
+ function makeContainerName(className, instance) {
2943
+ return `devflare-${sanitizeName(className)}-${sanitizeName(instance ?? "default")}-${randomUUID().slice(0, 8)}`;
2944
+ }
2945
+ function sanitizeName(value) {
2946
+ return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "container";
2947
+ }
2948
+ function formatCommandFailure(result) {
2949
+ return (result.stderr || result.stdout || `exit code ${result.exitCode}`).trim();
2950
+ }
2951
+ function isTruthyEnvFlag(value) {
2952
+ return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
3183
2953
  }
3184
2954
 
3185
- // src/test/simple-context-multi-worker.ts
3186
- function applyMultiWorkerConfig(mfConfig, config, serviceBindingResolution, doBindingResolution) {
3187
- const primaryDurableObjects = {
3188
- ...mfConfig.durableObjects || {},
3189
- ...doBindingResolution?.crossWorkerDOBindings || {}
3190
- };
3191
- const primaryWorker = {
3192
- name: config.name ?? "primary",
3193
- modules: true,
3194
- script: mfConfig.script,
3195
- compatibilityDate: config.compatibilityDate ?? "2025-01-01",
3196
- ...mfConfig.kvNamespaces && { kvNamespaces: mfConfig.kvNamespaces },
3197
- ...mfConfig.r2Buckets && { r2Buckets: mfConfig.r2Buckets },
3198
- ...mfConfig.d1Databases && { d1Databases: mfConfig.d1Databases },
3199
- ...mfConfig.ratelimits && { ratelimits: mfConfig.ratelimits },
3200
- ...mfConfig.versionMetadata && { versionMetadata: mfConfig.versionMetadata },
3201
- ...mfConfig.workerLoaders && { workerLoaders: mfConfig.workerLoaders },
3202
- ...mfConfig.mtlsCertificates && { mtlsCertificates: mfConfig.mtlsCertificates },
3203
- ...mfConfig.dispatchNamespaces && { dispatchNamespaces: mfConfig.dispatchNamespaces },
3204
- ...mfConfig.workflows && { workflows: mfConfig.workflows },
3205
- ...mfConfig.pipelines && { pipelines: mfConfig.pipelines },
3206
- ...mfConfig.images && { images: mfConfig.images },
3207
- ...mfConfig.media && { media: mfConfig.media },
3208
- ...mfConfig.artifacts && { artifacts: mfConfig.artifacts },
3209
- ...mfConfig.secretsStoreSecrets && { secretsStoreSecrets: mfConfig.secretsStoreSecrets },
3210
- ...mfConfig.wrappedBindings && { wrappedBindings: mfConfig.wrappedBindings },
3211
- ...mfConfig.email && { email: mfConfig.email },
3212
- ...Object.keys(primaryDurableObjects).length > 0 && { durableObjects: primaryDurableObjects },
3213
- ...mfConfig.serviceBindings || serviceBindingResolution?.primaryServiceBindings ? {
3214
- serviceBindings: {
3215
- ...mfConfig.serviceBindings ?? {},
3216
- ...serviceBindingResolution?.primaryServiceBindings ?? {}
3217
- }
3218
- } : {}
3219
- };
3220
- const additionalWorkers = [
3221
- ...serviceBindingResolution?.workers || [],
3222
- ...doBindingResolution?.workers || [],
3223
- ...mfConfig.__devflareLocalSecretWorkers || [],
3224
- ...mfConfig.__devflareLocalBindingWorkers || []
3225
- ];
3226
- const workersByName = new Map;
3227
- for (const worker2 of additionalWorkers) {
3228
- if (!workersByName.has(worker2.name)) {
3229
- workersByName.set(worker2.name, worker2);
3230
- continue;
3231
- }
3232
- const existing = workersByName.get(worker2.name);
3233
- if (worker2.durableObjects) {
3234
- existing.durableObjects = {
3235
- ...existing.durableObjects || {},
3236
- ...worker2.durableObjects
3237
- };
2955
+ // src/test/simple-context-lifecycle.ts
2956
+ async function resolveTestContextConfig(configPath, callerDir = getCallerDirectory()) {
2957
+ let absolutePath;
2958
+ if (configPath) {
2959
+ absolutePath = resolve2(callerDir, configPath);
2960
+ } else {
2961
+ const found = await findNearestConfig(callerDir);
2962
+ if (!found) {
2963
+ throw new Error(`Could not find a devflare config file. Searched upward from: ${callerDir}
2964
+ ` + `Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
2965
+ ` + `Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
3238
2966
  }
2967
+ absolutePath = found;
3239
2968
  }
3240
- const workers = [primaryWorker, ...workersByName.values()];
3241
- delete mfConfig.script;
3242
- delete mfConfig.modules;
3243
- delete mfConfig.kvNamespaces;
3244
- delete mfConfig.r2Buckets;
3245
- delete mfConfig.d1Databases;
3246
- delete mfConfig.ratelimits;
3247
- delete mfConfig.versionMetadata;
3248
- delete mfConfig.workerLoaders;
3249
- delete mfConfig.mtlsCertificates;
3250
- delete mfConfig.dispatchNamespaces;
3251
- delete mfConfig.workflows;
3252
- delete mfConfig.pipelines;
3253
- delete mfConfig.images;
3254
- delete mfConfig.media;
3255
- delete mfConfig.artifacts;
3256
- delete mfConfig.secretsStoreSecrets;
3257
- delete mfConfig.wrappedBindings;
3258
- delete mfConfig.serviceBindings;
3259
- delete mfConfig.__devflareLocalSecretWorkers;
3260
- delete mfConfig.__devflareLocalBindingWorkers;
3261
- delete mfConfig.durableObjects;
3262
- mfConfig.workers = workers;
2969
+ const configDir6 = dirname4(absolutePath);
2970
+ const loadedConfig = await loadConfig({
2971
+ cwd: configDir6,
2972
+ configFile: absolutePath.split(/[/\\]/).pop()
2973
+ });
2974
+ const envResolvedConfig = await resolveConfigEnvVars(loadedConfig, {
2975
+ cwd: configDir6,
2976
+ configPath: absolutePath,
2977
+ mode: "dev"
2978
+ });
2979
+ const config = await applyLocalDevVarsToConfig(envResolvedConfig, {
2980
+ cwd: configDir6,
2981
+ configPath: absolutePath
2982
+ });
2983
+ return { absolutePath, configDir: configDir6, config };
2984
+ }
2985
+ function createDisposeContext(state) {
2986
+ return async () => {
2987
+ if (state.client) {
2988
+ await state.client.disconnect();
2989
+ state.client = null;
2990
+ }
2991
+ if (state.miniflare) {
2992
+ await state.miniflare.dispose();
2993
+ state.miniflare = null;
2994
+ }
2995
+ await disposeLocalWorkerLoaderBindings();
2996
+ await stopActiveContainers();
2997
+ state.envProxy = null;
2998
+ state.transportDecode = null;
2999
+ state.remoteBindings = null;
3000
+ state.miniflareBindings = null;
3001
+ resetQueueState();
3002
+ resetScheduledState();
3003
+ resetWorkerState();
3004
+ resetTailState();
3005
+ resetEmailState();
3006
+ __clearTestContext();
3007
+ };
3263
3008
  }
3264
3009
 
3265
3010
  // src/test/simple-context-mfconfig.ts
@@ -3410,13 +3155,15 @@ function buildInlineBridgeMfConfig(config, options = {}) {
3410
3155
  return [];
3411
3156
  }
3412
3157
  const normalized = normalizeSecretsStoreBinding(binding, config.secretsStoreId, bindingName);
3413
- return [[
3414
- bindingName,
3415
- {
3416
- store_id: normalized.storeId,
3417
- secret_name: normalized.secretName
3418
- }
3419
- ]];
3158
+ return [
3159
+ [
3160
+ bindingName,
3161
+ {
3162
+ store_id: normalized.storeId,
3163
+ secret_name: normalized.secretName
3164
+ }
3165
+ ]
3166
+ ];
3420
3167
  });
3421
3168
  if (secretsStoreEntries.length > 0) {
3422
3169
  mfConfig.secretsStoreSecrets = Object.fromEntries(secretsStoreEntries);
@@ -3463,6 +3210,261 @@ function buildInlineBridgeMfConfig(config, options = {}) {
3463
3210
  return mfConfig;
3464
3211
  }
3465
3212
 
3213
+ // src/test/simple-context-multi-worker.ts
3214
+ function applyMultiWorkerConfig(mfConfig, config, serviceBindingResolution, doBindingResolution) {
3215
+ const primaryDurableObjects = {
3216
+ ...mfConfig.durableObjects || {},
3217
+ ...doBindingResolution?.crossWorkerDOBindings || {}
3218
+ };
3219
+ const primaryWorker = {
3220
+ name: config.name ?? "primary",
3221
+ modules: true,
3222
+ script: mfConfig.script,
3223
+ compatibilityDate: config.compatibilityDate ?? "2025-01-01",
3224
+ ...mfConfig.kvNamespaces && { kvNamespaces: mfConfig.kvNamespaces },
3225
+ ...mfConfig.r2Buckets && { r2Buckets: mfConfig.r2Buckets },
3226
+ ...mfConfig.d1Databases && { d1Databases: mfConfig.d1Databases },
3227
+ ...mfConfig.ratelimits && { ratelimits: mfConfig.ratelimits },
3228
+ ...mfConfig.versionMetadata && { versionMetadata: mfConfig.versionMetadata },
3229
+ ...mfConfig.workerLoaders && { workerLoaders: mfConfig.workerLoaders },
3230
+ ...mfConfig.mtlsCertificates && { mtlsCertificates: mfConfig.mtlsCertificates },
3231
+ ...mfConfig.dispatchNamespaces && { dispatchNamespaces: mfConfig.dispatchNamespaces },
3232
+ ...mfConfig.workflows && { workflows: mfConfig.workflows },
3233
+ ...mfConfig.pipelines && { pipelines: mfConfig.pipelines },
3234
+ ...mfConfig.images && { images: mfConfig.images },
3235
+ ...mfConfig.media && { media: mfConfig.media },
3236
+ ...mfConfig.artifacts && { artifacts: mfConfig.artifacts },
3237
+ ...mfConfig.secretsStoreSecrets && { secretsStoreSecrets: mfConfig.secretsStoreSecrets },
3238
+ ...mfConfig.wrappedBindings && { wrappedBindings: mfConfig.wrappedBindings },
3239
+ ...mfConfig.email && { email: mfConfig.email },
3240
+ ...Object.keys(primaryDurableObjects).length > 0 && { durableObjects: primaryDurableObjects },
3241
+ ...mfConfig.serviceBindings || serviceBindingResolution?.primaryServiceBindings ? {
3242
+ serviceBindings: {
3243
+ ...mfConfig.serviceBindings ?? {},
3244
+ ...serviceBindingResolution?.primaryServiceBindings ?? {}
3245
+ }
3246
+ } : {}
3247
+ };
3248
+ const additionalWorkers = [
3249
+ ...serviceBindingResolution?.workers || [],
3250
+ ...doBindingResolution?.workers || [],
3251
+ ...mfConfig.__devflareLocalSecretWorkers || [],
3252
+ ...mfConfig.__devflareLocalBindingWorkers || []
3253
+ ];
3254
+ const workersByName = new Map;
3255
+ for (const worker2 of additionalWorkers) {
3256
+ if (!workersByName.has(worker2.name)) {
3257
+ workersByName.set(worker2.name, worker2);
3258
+ continue;
3259
+ }
3260
+ const existing = workersByName.get(worker2.name);
3261
+ if (worker2.durableObjects) {
3262
+ existing.durableObjects = {
3263
+ ...existing.durableObjects || {},
3264
+ ...worker2.durableObjects
3265
+ };
3266
+ }
3267
+ }
3268
+ const workers = [primaryWorker, ...workersByName.values()];
3269
+ delete mfConfig.script;
3270
+ delete mfConfig.modules;
3271
+ delete mfConfig.kvNamespaces;
3272
+ delete mfConfig.r2Buckets;
3273
+ delete mfConfig.d1Databases;
3274
+ delete mfConfig.ratelimits;
3275
+ delete mfConfig.versionMetadata;
3276
+ delete mfConfig.workerLoaders;
3277
+ delete mfConfig.mtlsCertificates;
3278
+ delete mfConfig.dispatchNamespaces;
3279
+ delete mfConfig.workflows;
3280
+ delete mfConfig.pipelines;
3281
+ delete mfConfig.images;
3282
+ delete mfConfig.media;
3283
+ delete mfConfig.artifacts;
3284
+ delete mfConfig.secretsStoreSecrets;
3285
+ delete mfConfig.wrappedBindings;
3286
+ delete mfConfig.serviceBindings;
3287
+ delete mfConfig.__devflareLocalSecretWorkers;
3288
+ delete mfConfig.__devflareLocalBindingWorkers;
3289
+ delete mfConfig.durableObjects;
3290
+ mfConfig.workers = workers;
3291
+ }
3292
+
3293
+ // src/test/simple-context-startup.ts
3294
+ var TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS = 3;
3295
+ var TEST_CONTEXT_STARTUP_RETRY_DELAY_MS = 75;
3296
+ var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS = 8;
3297
+ var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS = 150;
3298
+ function isRetriableTestContextStartupError(error) {
3299
+ if (!(error instanceof Error)) {
3300
+ return false;
3301
+ }
3302
+ const message = error.message.toLowerCase();
3303
+ return message.includes("websocket connection failed") || message.includes("connection timeout: ws://") || message.includes("econnrefused") || message.includes("eaddrinuse") || message.includes("address already in use");
3304
+ }
3305
+ async function waitForTestContextStartupRetry() {
3306
+ await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_STARTUP_RETRY_DELAY_MS));
3307
+ }
3308
+ async function waitForBridgeClientRetry() {
3309
+ await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS));
3310
+ }
3311
+ async function connectBridgeClientWithRetry(url) {
3312
+ let lastError;
3313
+ for (let attempt = 1;attempt <= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS; attempt++) {
3314
+ const client = new BridgeClient({ url });
3315
+ try {
3316
+ await client.connect();
3317
+ return client;
3318
+ } catch (error) {
3319
+ lastError = error;
3320
+ client.disconnect();
3321
+ if (attempt >= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
3322
+ throw error;
3323
+ }
3324
+ await waitForBridgeClientRetry();
3325
+ }
3326
+ }
3327
+ throw lastError instanceof Error ? lastError : new Error("Bridge-backed test context could not connect to the WebSocket gateway.");
3328
+ }
3329
+ function expandLocalBindingWorkers(mfConfig) {
3330
+ const auxiliaryWorkers = [
3331
+ ...mfConfig.__devflareLocalSecretWorkers ?? [],
3332
+ ...mfConfig.__devflareLocalBindingWorkers ?? []
3333
+ ];
3334
+ if (!Array.isArray(auxiliaryWorkers) || auxiliaryWorkers.length === 0) {
3335
+ return mfConfig;
3336
+ }
3337
+ const {
3338
+ __devflareLocalSecretWorkers,
3339
+ __devflareLocalBindingWorkers,
3340
+ port,
3341
+ host,
3342
+ log,
3343
+ kvPersist,
3344
+ r2Persist,
3345
+ d1Persist,
3346
+ durableObjectsPersist,
3347
+ workflowsPersist,
3348
+ imagesPersist,
3349
+ ...primaryWorker
3350
+ } = mfConfig;
3351
+ const primaryWorkerName = typeof primaryWorker.name === "string" ? primaryWorker.name : "primary";
3352
+ return {
3353
+ ...port !== undefined && { port },
3354
+ ...host && { host },
3355
+ ...log && { log },
3356
+ ...kvPersist && { kvPersist },
3357
+ ...r2Persist && { r2Persist },
3358
+ ...d1Persist && { d1Persist },
3359
+ ...durableObjectsPersist && { durableObjectsPersist },
3360
+ ...workflowsPersist && { workflowsPersist },
3361
+ ...imagesPersist && { imagesPersist },
3362
+ workers: [
3363
+ {
3364
+ ...primaryWorker,
3365
+ name: primaryWorkerName
3366
+ },
3367
+ ...auxiliaryWorkers
3368
+ ]
3369
+ };
3370
+ }
3371
+ async function startBridgeBackedTestContext(mfConfig) {
3372
+ const { Miniflare } = await import("miniflare");
3373
+ for (let attempt = 1;attempt <= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS; attempt++) {
3374
+ const port = await getAvailablePort();
3375
+ let miniflare = null;
3376
+ let client = null;
3377
+ try {
3378
+ miniflare = new Miniflare(expandLocalBindingWorkers({
3379
+ ...mfConfig,
3380
+ port
3381
+ }));
3382
+ await miniflare.ready;
3383
+ const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
3384
+ client = await connectBridgeClientWithRetry(`ws://localhost:${port}`);
3385
+ return {
3386
+ port,
3387
+ client,
3388
+ miniflare,
3389
+ miniflareBindings
3390
+ };
3391
+ } catch (error) {
3392
+ client?.disconnect();
3393
+ if (miniflare) {
3394
+ try {
3395
+ await miniflare.dispose();
3396
+ } catch {}
3397
+ }
3398
+ if (attempt >= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
3399
+ throw error;
3400
+ }
3401
+ await waitForTestContextStartupRetry();
3402
+ }
3403
+ }
3404
+ throw new Error("Bridge-backed test context startup exhausted all retry attempts.");
3405
+ }
3406
+
3407
+ // src/test/simple-context-runtime.ts
3408
+ async function bootTestRuntime(mfConfig, usesMultiWorker) {
3409
+ if (usesMultiWorker) {
3410
+ const { Miniflare } = await import("miniflare");
3411
+ const activePort = await getAvailablePort();
3412
+ const miniflare = new Miniflare({
3413
+ ...mfConfig,
3414
+ port: activePort
3415
+ });
3416
+ await miniflare.ready;
3417
+ const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
3418
+ return { activePort, miniflare, miniflareBindings, client: null };
3419
+ }
3420
+ const started = await startBridgeBackedTestContext(mfConfig);
3421
+ return {
3422
+ activePort: started.port,
3423
+ miniflare: started.miniflare,
3424
+ miniflareBindings: started.miniflareBindings,
3425
+ client: started.client
3426
+ };
3427
+ }
3428
+
3429
+ // src/test/simple-context-transport.ts
3430
+ import { join as join10 } from "path";
3431
+ async function loadTransportDecoders(configDir6, transportFile) {
3432
+ const transportPath = join10(configDir6, transportFile);
3433
+ const transportModule = await import(transportPath);
3434
+ if (!transportModule.transport) {
3435
+ console.warn(`[devflare] Warning: Transport file "${transportFile}" does not export a named "transport" object.
3436
+ Expected: export const transport = { ... }
3437
+ Transport encoding/decoding will be disabled.`);
3438
+ return null;
3439
+ }
3440
+ const decoders = new Map;
3441
+ for (const [typeName, transporter] of Object.entries(transportModule.transport)) {
3442
+ const t = transporter;
3443
+ decoders.set(typeName, t.decode);
3444
+ }
3445
+ return decoders;
3446
+ }
3447
+ function decodeTransportValue(decoders, value) {
3448
+ if (!decoders || value === null || typeof value !== "object") {
3449
+ return value;
3450
+ }
3451
+ if ("__transport" in value) {
3452
+ const encoded = value;
3453
+ const decoder = decoders.get(encoded.__transport);
3454
+ if (decoder) {
3455
+ return decoder(encoded.value);
3456
+ }
3457
+ }
3458
+ if (Array.isArray(value)) {
3459
+ return value.map((item) => decodeTransportValue(decoders, item));
3460
+ }
3461
+ const result = {};
3462
+ for (const [k, v] of Object.entries(value)) {
3463
+ result[k] = decodeTransportValue(decoders, v);
3464
+ }
3465
+ return result;
3466
+ }
3467
+
3466
3468
  // src/test/simple-context.ts
3467
3469
  function createTestContextState() {
3468
3470
  return {