devflare 1.0.0-next.15 → 1.0.0-next.16

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 (377) hide show
  1. package/LLM.md +9360 -1819
  2. package/README.md +313 -39
  3. package/bin/devflare.js +17 -7
  4. package/dist/account-0w8wdzjv.js +475 -0
  5. package/dist/account-eygq6qx7.js +475 -0
  6. package/dist/account-fw8nafav.js +475 -0
  7. package/dist/account-pzq69nys.js +475 -0
  8. package/dist/account-s66jb15j.js +475 -0
  9. package/dist/api-d6ekexs5.js +25 -0
  10. package/dist/bridge/index.d.ts +1 -1
  11. package/dist/bridge/index.d.ts.map +1 -1
  12. package/dist/bridge/miniflare.d.ts.map +1 -1
  13. package/dist/bridge/protocol.d.ts +1 -1
  14. package/dist/bridge/protocol.d.ts.map +1 -1
  15. package/dist/bridge/proxy.d.ts +0 -4
  16. package/dist/bridge/proxy.d.ts.map +1 -1
  17. package/dist/bridge/serialization.d.ts.map +1 -1
  18. package/dist/bridge/server.d.ts +1 -1
  19. package/dist/bridge/server.d.ts.map +1 -1
  20. package/dist/browser-shim/handler.d.ts +1 -1
  21. package/dist/browser-shim/handler.d.ts.map +1 -1
  22. package/dist/browser.d.ts +1648 -38
  23. package/dist/browser.d.ts.map +1 -1
  24. package/dist/build-1kmkwqgh.js +53 -0
  25. package/dist/build-506kjhcm.js +53 -0
  26. package/dist/build-66866ahs.js +53 -0
  27. package/dist/build-g1adm3ww.js +53 -0
  28. package/dist/build-p3r3117t.js +53 -0
  29. package/dist/bundler/do-bundler.d.ts.map +1 -1
  30. package/dist/bundler/rolldown-shared.d.ts +24 -0
  31. package/dist/bundler/rolldown-shared.d.ts.map +1 -0
  32. package/dist/bundler/worker-bundler.d.ts +0 -1
  33. package/dist/bundler/worker-bundler.d.ts.map +1 -1
  34. package/dist/cli/command-utils.d.ts +18 -0
  35. package/dist/cli/command-utils.d.ts.map +1 -0
  36. package/dist/cli/commands/account.d.ts +1 -1
  37. package/dist/cli/commands/account.d.ts.map +1 -1
  38. package/dist/cli/commands/build-artifacts.d.ts +27 -0
  39. package/dist/cli/commands/build-artifacts.d.ts.map +1 -0
  40. package/dist/cli/commands/build.d.ts.map +1 -1
  41. package/dist/cli/commands/deploy.d.ts.map +1 -1
  42. package/dist/cli/commands/dev.d.ts.map +1 -1
  43. package/dist/cli/commands/doctor.d.ts.map +1 -1
  44. package/dist/cli/commands/init.d.ts.map +1 -1
  45. package/dist/cli/commands/login.d.ts +4 -0
  46. package/dist/cli/commands/login.d.ts.map +1 -0
  47. package/dist/cli/commands/previews-support/cleanup.d.ts +9 -0
  48. package/dist/cli/commands/previews-support/cleanup.d.ts.map +1 -0
  49. package/dist/cli/commands/previews-support/family.d.ts +10 -0
  50. package/dist/cli/commands/previews-support/family.d.ts.map +1 -0
  51. package/dist/cli/commands/previews-support/render.d.ts +8 -0
  52. package/dist/cli/commands/previews-support/render.d.ts.map +1 -0
  53. package/dist/cli/commands/previews-support/theme.d.ts +10 -0
  54. package/dist/cli/commands/previews-support/theme.d.ts.map +1 -0
  55. package/dist/cli/commands/previews-support/types.d.ts +70 -0
  56. package/dist/cli/commands/previews-support/types.d.ts.map +1 -0
  57. package/dist/cli/commands/previews.d.ts +4 -0
  58. package/dist/cli/commands/previews.d.ts.map +1 -0
  59. package/dist/cli/commands/productions.d.ts +4 -0
  60. package/dist/cli/commands/productions.d.ts.map +1 -0
  61. package/dist/cli/commands/token.d.ts +4 -0
  62. package/dist/cli/commands/token.d.ts.map +1 -0
  63. package/dist/cli/commands/type-generation/discovery.d.ts +7 -0
  64. package/dist/cli/commands/type-generation/discovery.d.ts.map +1 -0
  65. package/dist/cli/commands/type-generation/generator.d.ts +44 -0
  66. package/dist/cli/commands/type-generation/generator.d.ts.map +1 -0
  67. package/dist/cli/commands/type-generation/models.d.ts +27 -0
  68. package/dist/cli/commands/type-generation/models.d.ts.map +1 -0
  69. package/dist/cli/commands/types.d.ts.map +1 -1
  70. package/dist/cli/commands/worker.d.ts +4 -0
  71. package/dist/cli/commands/worker.d.ts.map +1 -0
  72. package/dist/cli/config-path.d.ts +2 -1
  73. package/dist/cli/config-path.d.ts.map +1 -1
  74. package/dist/cli/deploy-strategy.d.ts +17 -0
  75. package/dist/cli/deploy-strategy.d.ts.map +1 -0
  76. package/dist/cli/deploy-target.d.ts +17 -0
  77. package/dist/cli/deploy-target.d.ts.map +1 -0
  78. package/dist/cli/generated-artifacts.d.ts +12 -0
  79. package/dist/cli/generated-artifacts.d.ts.map +1 -0
  80. package/dist/cli/help-pages/pages/account.d.ts +3 -0
  81. package/dist/cli/help-pages/pages/account.d.ts.map +1 -0
  82. package/dist/cli/help-pages/pages/core.d.ts +4 -0
  83. package/dist/cli/help-pages/pages/core.d.ts.map +1 -0
  84. package/dist/cli/help-pages/pages/index.d.ts +3 -0
  85. package/dist/cli/help-pages/pages/index.d.ts.map +1 -0
  86. package/dist/cli/help-pages/pages/misc.d.ts +3 -0
  87. package/dist/cli/help-pages/pages/misc.d.ts.map +1 -0
  88. package/dist/cli/help-pages/pages/previews.d.ts +3 -0
  89. package/dist/cli/help-pages/pages/previews.d.ts.map +1 -0
  90. package/dist/cli/help-pages/pages/productions.d.ts +3 -0
  91. package/dist/cli/help-pages/pages/productions.d.ts.map +1 -0
  92. package/dist/cli/help-pages/render.d.ts +12 -0
  93. package/dist/cli/help-pages/render.d.ts.map +1 -0
  94. package/dist/cli/help-pages/shared.d.ts +15 -0
  95. package/dist/cli/help-pages/shared.d.ts.map +1 -0
  96. package/dist/cli/help-pages/types.d.ts +23 -0
  97. package/dist/cli/help-pages/types.d.ts.map +1 -0
  98. package/dist/cli/help.d.ts +6 -0
  99. package/dist/cli/help.d.ts.map +1 -0
  100. package/dist/cli/index.d.ts +1 -0
  101. package/dist/cli/index.d.ts.map +1 -1
  102. package/dist/cli/preview-bindings.d.ts +42 -0
  103. package/dist/cli/preview-bindings.d.ts.map +1 -0
  104. package/dist/cli/preview.d.ts +11 -0
  105. package/dist/cli/preview.d.ts.map +1 -0
  106. package/dist/cli/ui.d.ts +37 -0
  107. package/dist/cli/ui.d.ts.map +1 -0
  108. package/dist/cli/workspace-build-guard.d.ts +14 -0
  109. package/dist/cli/workspace-build-guard.d.ts.map +1 -0
  110. package/dist/cloudflare/account-core.d.ts +6 -0
  111. package/dist/cloudflare/account-core.d.ts.map +1 -0
  112. package/dist/cloudflare/account-resources.d.ts +40 -0
  113. package/dist/cloudflare/account-resources.d.ts.map +1 -0
  114. package/dist/cloudflare/account-status.d.ts +11 -0
  115. package/dist/cloudflare/account-status.d.ts.map +1 -0
  116. package/dist/cloudflare/account-workers.d.ts +14 -0
  117. package/dist/cloudflare/account-workers.d.ts.map +1 -0
  118. package/dist/cloudflare/account.d.ts +7 -64
  119. package/dist/cloudflare/account.d.ts.map +1 -1
  120. package/dist/cloudflare/api.d.ts +4 -0
  121. package/dist/cloudflare/api.d.ts.map +1 -1
  122. package/dist/cloudflare/index.d.ts +57 -2
  123. package/dist/cloudflare/index.d.ts.map +1 -1
  124. package/dist/cloudflare/kv-namespace.d.ts +3 -0
  125. package/dist/cloudflare/kv-namespace.d.ts.map +1 -0
  126. package/dist/cloudflare/preferences.d.ts.map +1 -1
  127. package/dist/cloudflare/preview-registry-cache.d.ts +6 -0
  128. package/dist/cloudflare/preview-registry-cache.d.ts.map +1 -0
  129. package/dist/cloudflare/preview-registry-records.d.ts +61 -0
  130. package/dist/cloudflare/preview-registry-records.d.ts.map +1 -0
  131. package/dist/cloudflare/preview-registry-store.d.ts +14 -0
  132. package/dist/cloudflare/preview-registry-store.d.ts.map +1 -0
  133. package/dist/cloudflare/preview-registry-types.d.ts +103 -0
  134. package/dist/cloudflare/preview-registry-types.d.ts.map +1 -0
  135. package/dist/cloudflare/preview-registry.d.ts +42 -0
  136. package/dist/cloudflare/preview-registry.d.ts.map +1 -0
  137. package/dist/cloudflare/registry-schema.d.ts +253 -0
  138. package/dist/cloudflare/registry-schema.d.ts.map +1 -0
  139. package/dist/cloudflare/tokens.d.ts +18 -0
  140. package/dist/cloudflare/tokens.d.ts.map +1 -0
  141. package/dist/cloudflare/types.d.ts +122 -5
  142. package/dist/cloudflare/types.d.ts.map +1 -1
  143. package/dist/cloudflare/usage.d.ts.map +1 -1
  144. package/dist/config/compiler.d.ts +4 -0
  145. package/dist/config/compiler.d.ts.map +1 -1
  146. package/dist/config/framework-providers.d.ts +9 -0
  147. package/dist/config/framework-providers.d.ts.map +1 -0
  148. package/dist/config/index.d.ts +4 -3
  149. package/dist/config/index.d.ts.map +1 -1
  150. package/dist/config/loader.d.ts.map +1 -1
  151. package/dist/config/preview-resources.d.ts +77 -0
  152. package/dist/config/preview-resources.d.ts.map +1 -0
  153. package/dist/config/preview.d.ts +31 -0
  154. package/dist/config/preview.d.ts.map +1 -0
  155. package/dist/config/ref.d.ts +0 -22
  156. package/dist/config/ref.d.ts.map +1 -1
  157. package/dist/config/resolve.d.ts +1 -0
  158. package/dist/config/resolve.d.ts.map +1 -1
  159. package/dist/config/resource-resolution.d.ts +21 -5
  160. package/dist/config/resource-resolution.d.ts.map +1 -1
  161. package/dist/config/schema-bindings.d.ts +693 -0
  162. package/dist/config/schema-bindings.d.ts.map +1 -0
  163. package/dist/config/schema-build.d.ts +67 -0
  164. package/dist/config/schema-build.d.ts.map +1 -0
  165. package/dist/config/schema-env.d.ts +1341 -0
  166. package/dist/config/schema-env.d.ts.map +1 -0
  167. package/dist/config/schema-normalization.d.ts +64 -0
  168. package/dist/config/schema-normalization.d.ts.map +1 -0
  169. package/dist/config/schema-runtime.d.ts +230 -0
  170. package/dist/config/schema-runtime.d.ts.map +1 -0
  171. package/dist/config/schema.d.ts +542 -3736
  172. package/dist/config/schema.d.ts.map +1 -1
  173. package/dist/config-entry.d.ts +5 -0
  174. package/dist/config-entry.d.ts.map +1 -0
  175. package/dist/{config-v9tr4rts.js → config-fjwke42y.js} +6 -4
  176. package/dist/config-hwdqjse7.js +59 -0
  177. package/dist/config-pxvewrhv.js +59 -0
  178. package/dist/config-q0g5qdga.js +59 -0
  179. package/dist/decorators/durable-object.d.ts.map +1 -1
  180. package/dist/deploy-7nmzc9r8.js +609 -0
  181. package/dist/deploy-csfhdr64.js +691 -0
  182. package/dist/deploy-ex4g5avz.js +621 -0
  183. package/dist/deploy-jnb0bhka.js +609 -0
  184. package/dist/deploy-tp0g6qdp.js +609 -0
  185. package/dist/deploy-ykpcjkc2.js +690 -0
  186. package/dist/{dev-ymtphbkg.js → dev-2pd33m28.js} +386 -348
  187. package/dist/dev-7ef5e2j1.js +2409 -0
  188. package/dist/dev-8nssqatr.js +2409 -0
  189. package/dist/dev-grznx8fn.js +2409 -0
  190. package/dist/dev-server/d1-migrations.d.ts +14 -0
  191. package/dist/dev-server/d1-migrations.d.ts.map +1 -0
  192. package/dist/dev-server/gateway-script.d.ts +8 -0
  193. package/dist/dev-server/gateway-script.d.ts.map +1 -0
  194. package/dist/dev-server/runtime-stdio.d.ts.map +1 -1
  195. package/dist/dev-server/server.d.ts.map +1 -1
  196. package/dist/dev-server/vite-process.d.ts +14 -0
  197. package/dist/dev-server/vite-process.d.ts.map +1 -0
  198. package/dist/dev-server/vite-utils.d.ts +1 -1
  199. package/dist/dev-server/vite-utils.d.ts.map +1 -1
  200. package/dist/dev-server/worker-source-watcher.d.ts +11 -0
  201. package/dist/dev-server/worker-source-watcher.d.ts.map +1 -0
  202. package/dist/dev-server/worker-surface-paths.d.ts +6 -0
  203. package/dist/dev-server/worker-surface-paths.d.ts.map +1 -0
  204. package/dist/{doctor-xv4gm1h4.js → doctor-04ammrrh.js} +67 -32
  205. package/dist/doctor-fmjj65mc.js +245 -0
  206. package/dist/doctor-fzkznce1.js +245 -0
  207. package/dist/doctor-sa5xv1bz.js +245 -0
  208. package/dist/index-091sh1ma.js +1229 -0
  209. package/dist/index-0apbm26n.js +788 -0
  210. package/dist/index-0eqksag4.js +418 -0
  211. package/dist/{index-k8vh558d.js → index-0kfzdywd.js} +15 -2
  212. package/dist/index-0w826dsr.js +379 -0
  213. package/dist/{index-5s1bz1e0.js → index-11m5a8wd.js} +100 -22
  214. package/dist/{index-0rsa2c1t.js → index-1sp39f2f.js} +110 -57
  215. package/dist/index-2jnrqbny.js +1301 -0
  216. package/dist/index-2pb7b9mw.js +378 -0
  217. package/dist/{index-3a4mmn57.js → index-2x53aqjm.js} +1065 -890
  218. package/dist/index-3ke5d2vn.js +1229 -0
  219. package/dist/index-43dq8yx8.js +788 -0
  220. package/dist/index-4rrttqj5.js +378 -0
  221. package/dist/index-4v9bc2pc.js +1367 -0
  222. package/dist/index-61jsjnsv.js +280 -0
  223. package/dist/index-6jef5emv.js +176 -0
  224. package/dist/index-6psz1h4c.js +788 -0
  225. package/dist/index-72mve6vh.js +168 -0
  226. package/dist/{index-zvgc3e0c.js → index-74198nxd.js} +159 -63
  227. package/dist/{index-7bq4xq84.js → index-7g8zyws4.js} +7 -12
  228. package/dist/index-7kcxjhta.js +456 -0
  229. package/dist/index-7v583xan.js +418 -0
  230. package/dist/index-7x0ybbtx.js +133 -0
  231. package/dist/index-816krz9p.js +52 -0
  232. package/dist/index-82f1z98k.js +41 -0
  233. package/dist/index-8t5nb4qx.js +133 -0
  234. package/dist/index-9az6s7ad.js +52 -0
  235. package/dist/{index-59df49vn.js → index-9ba1etyz.js} +29 -51
  236. package/dist/{index-001mw014.js → index-9fbtk7gv.js} +134 -248
  237. package/dist/index-9n6djthj.js +490 -0
  238. package/dist/index-aabgympv.js +39 -0
  239. package/dist/index-b8m6883k.js +74 -0
  240. package/dist/{index-5yxg30va.js → index-cgbvmse6.js} +15 -6
  241. package/dist/index-d8etnfef.js +1229 -0
  242. package/dist/index-e9yw4d6y.js +133 -0
  243. package/dist/index-epw1jxz5.js +1204 -0
  244. package/dist/index-f85s8gj3.js +2649 -0
  245. package/dist/index-fe2ngvh7.js +1229 -0
  246. package/dist/index-fvsadj32.js +192 -0
  247. package/dist/index-gs4y9gdf.js +456 -0
  248. package/dist/{index-fef08w43.js → index-h18pxvzs.js} +7 -6
  249. package/dist/index-hfj1a2c4.js +2649 -0
  250. package/dist/{index-8gtqgb3q.js → index-hjy8ctpc.js} +14 -92
  251. package/dist/index-htzf0py1.js +1204 -0
  252. package/dist/index-j185x270.js +897 -0
  253. package/dist/index-jb75kwa4.js +519 -0
  254. package/dist/index-jwd8pcb2.js +897 -0
  255. package/dist/index-k29yjhv0.js +52 -0
  256. package/dist/index-k6vq6kkt.js +456 -0
  257. package/dist/{index-vky23txa.js → index-m3fmw6mx.js} +2 -2
  258. package/dist/index-maxpsfk8.js +402 -0
  259. package/dist/index-mbdmrner.js +402 -0
  260. package/dist/index-mea5bc45.js +418 -0
  261. package/dist/index-mqekt778.js +185 -0
  262. package/dist/index-na3mnm1k.js +74 -0
  263. package/dist/index-p03n4qet.js +1367 -0
  264. package/dist/index-p296ban8.js +191 -0
  265. package/dist/index-pnbs1b8k.js +280 -0
  266. package/dist/index-q4kaz181.js +1207 -0
  267. package/dist/index-ry131z23.js +378 -0
  268. package/dist/index-sgb7c8nm.js +402 -0
  269. package/dist/index-sqrksgb2.js +133 -0
  270. package/dist/index-stgn34cr.js +148 -0
  271. package/dist/{index-v8vvsn9x.js → index-t08te69w.js} +1 -18
  272. package/dist/index-thna1tkd.js +280 -0
  273. package/dist/index-v5nmqthy.js +74 -0
  274. package/dist/{index-n932ytmq.js → index-vt4yxkmf.js} +2 -2
  275. package/dist/index-wyq6c6yj.js +402 -0
  276. package/dist/index-wztc9stx.js +418 -0
  277. package/dist/index-x9cwdxw5.js +456 -0
  278. package/dist/index-xk9djfjp.js +519 -0
  279. package/dist/index-yc0gcchc.js +418 -0
  280. package/dist/index-yqbxjysa.js +897 -0
  281. package/dist/index-yzddwp02.js +788 -0
  282. package/dist/index-zfhq6s96.js +74 -0
  283. package/dist/index-zt22fe2j.js +54 -0
  284. package/dist/index-zyt5byt6.js +2649 -0
  285. package/dist/index.d.ts +2 -2
  286. package/dist/index.d.ts.map +1 -1
  287. package/dist/{init-na2atvz2.js → init-r4hnxan3.js} +24 -17
  288. package/dist/login-2hnz4m4n.js +77 -0
  289. package/dist/login-5bsxxpvc.js +77 -0
  290. package/dist/login-6tzvczw2.js +77 -0
  291. package/dist/login-bhaw72zc.js +77 -0
  292. package/dist/login-x8tgckqm.js +77 -0
  293. package/dist/previews-3rn8mz2c.js +1168 -0
  294. package/dist/previews-d487qde5.js +1200 -0
  295. package/dist/previews-gm3z0syj.js +1168 -0
  296. package/dist/previews-j9ymq4ys.js +1169 -0
  297. package/dist/previews-q031mx34.js +1168 -0
  298. package/dist/productions-120xg0aq.js +505 -0
  299. package/dist/productions-5ev5qweg.js +505 -0
  300. package/dist/productions-me3tdvr9.js +505 -0
  301. package/dist/productions-p5rbgp2f.js +505 -0
  302. package/dist/productions-x9p0pym1.js +505 -0
  303. package/dist/runtime/context-events.d.ts +13 -0
  304. package/dist/runtime/context-events.d.ts.map +1 -0
  305. package/dist/runtime/context-types.d.ts +82 -0
  306. package/dist/runtime/context-types.d.ts.map +1 -0
  307. package/dist/runtime/context.d.ts +6 -267
  308. package/dist/runtime/context.d.ts.map +1 -1
  309. package/dist/runtime/exports.d.ts +3 -3
  310. package/dist/runtime/index.d.ts +1 -1
  311. package/dist/runtime/index.d.ts.map +1 -1
  312. package/dist/runtime/middleware.d.ts +8 -38
  313. package/dist/runtime/middleware.d.ts.map +1 -1
  314. package/dist/src/browser.js +9 -17
  315. package/dist/src/cli/index.js +3 -1
  316. package/dist/src/cloudflare/index.js +49 -3
  317. package/dist/src/config-entry.js +14 -0
  318. package/dist/src/index.js +24 -20
  319. package/dist/src/runtime/index.js +3 -9
  320. package/dist/src/sveltekit/index.js +10 -8
  321. package/dist/src/test/index.js +16 -19
  322. package/dist/src/vite/index.js +7 -5
  323. package/dist/sveltekit/platform.d.ts +1 -1
  324. package/dist/sveltekit/platform.d.ts.map +1 -1
  325. package/dist/test/cf.d.ts +10 -10
  326. package/dist/test/email.d.ts.map +1 -1
  327. package/dist/test/index.d.ts +1 -6
  328. package/dist/test/index.d.ts.map +1 -1
  329. package/dist/test/queue.d.ts.map +1 -1
  330. package/dist/test/remote-ai.d.ts.map +1 -1
  331. package/dist/test/remote-cloudflare.d.ts +13 -0
  332. package/dist/test/remote-cloudflare.d.ts.map +1 -0
  333. package/dist/test/remote-vectorize.d.ts.map +1 -1
  334. package/dist/test/resolve-service-bindings.d.ts.map +1 -1
  335. package/dist/test/scheduled.d.ts.map +1 -1
  336. package/dist/test/should-skip.d.ts +0 -18
  337. package/dist/test/should-skip.d.ts.map +1 -1
  338. package/dist/test/simple-context-durable-objects.d.ts +6 -0
  339. package/dist/test/simple-context-durable-objects.d.ts.map +1 -0
  340. package/dist/test/simple-context-gateway-script.d.ts +2 -0
  341. package/dist/test/simple-context-gateway-script.d.ts.map +1 -0
  342. package/dist/test/simple-context-paths.d.ts +40 -0
  343. package/dist/test/simple-context-paths.d.ts.map +1 -0
  344. package/dist/test/simple-context.d.ts +1 -23
  345. package/dist/test/simple-context.d.ts.map +1 -1
  346. package/dist/test/tail.d.ts.map +1 -1
  347. package/dist/test/worker.d.ts.map +1 -1
  348. package/dist/token-kedhcret.js +419 -0
  349. package/dist/token-m8jmnjwk.js +419 -0
  350. package/dist/{types-158m16vd.js → types-0sqwkp7x.js} +244 -140
  351. package/dist/types-1gwr2ex6.js +572 -0
  352. package/dist/types-6e5yx6km.js +572 -0
  353. package/dist/types-p0gckpn6.js +572 -0
  354. package/dist/utils/send-email.d.ts.map +1 -1
  355. package/dist/vite/config-file.d.ts.map +1 -1
  356. package/dist/vite/plugin.d.ts.map +1 -1
  357. package/dist/worker-0srh2jfr.js +513 -0
  358. package/dist/worker-4xrfd10a.js +513 -0
  359. package/dist/worker-entry/composed-worker.d.ts +0 -7
  360. package/dist/worker-entry/composed-worker.d.ts.map +1 -1
  361. package/dist/worker-entry/surface-paths.d.ts +15 -0
  362. package/dist/worker-entry/surface-paths.d.ts.map +1 -0
  363. package/dist/worker-qtam8grz.js +513 -0
  364. package/dist/worker-qzm0b7br.js +513 -0
  365. package/dist/worker-y9ha6g44.js +513 -0
  366. package/package.json +17 -10
  367. package/R2.md +0 -200
  368. package/dist/account-spa7gzsn.js +0 -421
  369. package/dist/build-zv25ke4s.js +0 -102
  370. package/dist/deploy-6xmqvv06.js +0 -118
  371. package/dist/index-2q3pmzrx.js +0 -90
  372. package/dist/index-6nb7w45m.js +0 -79
  373. package/dist/index-tksw7gpy.js +0 -503
  374. package/dist/index-v43z02tr.js +0 -205
  375. package/dist/index-xdq9ery1.js +0 -664
  376. package/dist/test/multi-worker-context.d.ts +0 -114
  377. package/dist/test/multi-worker-context.d.ts.map +0 -1
@@ -2,23 +2,28 @@ import {
2
2
  getRemoteModeStatus,
3
3
  isRemoteModeActive
4
4
  } from "./index-d8bdkx2h.js";
5
+ import {
6
+ discoverEntrypointsSync
7
+ } from "./index-zt22fe2j.js";
8
+ import {
9
+ canProceedWithTest
10
+ } from "./index-7g8zyws4.js";
5
11
  import {
6
12
  transformWorkerEntrypoint
7
13
  } from "./index-wfbfz02q.js";
8
14
  import {
9
- discoverEntrypointsSync,
10
15
  resolvePackageSpecifier
11
- } from "./index-2q3pmzrx.js";
16
+ } from "./index-82f1z98k.js";
12
17
  import {
13
18
  __clearTestContext,
14
19
  __setTestContext
15
- } from "./index-vky23txa.js";
20
+ } from "./index-m3fmw6mx.js";
16
21
  import {
17
22
  createRouteResolve,
18
23
  invokeFetchModule,
19
24
  matchFetchRoute,
20
25
  resolveFetchHandler
21
- } from "./index-8gtqgb3q.js";
26
+ } from "./index-hjy8ctpc.js";
22
27
  import {
23
28
  createEmailEvent,
24
29
  createFetchEvent,
@@ -27,7 +32,7 @@ import {
27
32
  createTailEvent,
28
33
  runWithContext,
29
34
  runWithEventContext
30
- } from "./index-5yxg30va.js";
35
+ } from "./index-cgbvmse6.js";
31
36
  import {
32
37
  discoverRoutes
33
38
  } from "./index-1p814k7s.js";
@@ -42,47 +47,47 @@ import {
42
47
  import {
43
48
  startMiniflare,
44
49
  startMiniflareFromConfig
45
- } from "./index-0rsa2c1t.js";
50
+ } from "./index-ry131z23.js";
46
51
  import {
47
52
  BridgeClient,
48
53
  createEnvProxy,
49
54
  setBindingHints
50
- } from "./index-59df49vn.js";
55
+ } from "./index-9ba1etyz.js";
51
56
  import {
52
57
  createLocalSendEmailBinding,
53
58
  wrapEnvSendEmailBindings
54
- } from "./index-fef08w43.js";
59
+ } from "./index-h18pxvzs.js";
55
60
  import {
56
61
  getLocalD1DatabaseIdentifier,
57
62
  loadConfig,
58
63
  normalizeDOBinding,
59
64
  resolveConfigPath
60
- } from "./index-tksw7gpy.js";
65
+ } from "./index-6psz1h4c.js";
61
66
  import {
62
- canProceedWithTest
63
- } from "./index-7bq4xq84.js";
67
+ getEffectiveAccountId,
68
+ getPrimaryAccount
69
+ } from "./index-xk9djfjp.js";
64
70
  import {
65
71
  getApiToken,
66
- getEffectiveAccountId,
67
- getPrimaryAccount,
68
72
  isAuthenticated
69
- } from "./index-xdq9ery1.js";
73
+ } from "./index-0w826dsr.js";
70
74
  import {
71
75
  __require
72
76
  } from "./index-37x76zdn.js";
73
77
 
74
78
  // src/test/simple-context.ts
75
- import { resolve as resolve2, dirname as dirname2, join as join7 } from "path";
76
- import { existsSync as existsSync2 } from "fs";
79
+ import { dirname as dirname4, join as join9, resolve as resolve2 } from "path";
77
80
 
78
- // src/test/remote-ai.ts
79
- function createRemoteAI(accountId) {
81
+ // src/test/remote-cloudflare.ts
82
+ function createRemoteCloudflareClient(accountId) {
80
83
  let resolvedAccountId = null;
81
84
  async function getAccountId() {
82
- if (accountId)
85
+ if (accountId) {
83
86
  return accountId;
84
- if (resolvedAccountId)
87
+ }
88
+ if (resolvedAccountId) {
85
89
  return resolvedAccountId;
90
+ }
86
91
  const primary = await getPrimaryAccount();
87
92
  if (!primary) {
88
93
  throw new Error("No Cloudflare account found. Run: bunx wrangler login");
@@ -98,28 +103,45 @@ function createRemoteAI(accountId) {
98
103
  }
99
104
  return token;
100
105
  }
106
+ async function jsonRequest(options) {
107
+ const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
108
+ const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${acctId}${options.path}`, {
109
+ method: options.method,
110
+ headers: {
111
+ Authorization: `Bearer ${token}`,
112
+ "Content-Type": options.contentType ?? "application/json"
113
+ },
114
+ body: options.body
115
+ });
116
+ if (!response.ok) {
117
+ const errorText = await response.text();
118
+ throw new Error(`${options.serviceLabel} API error (${response.status}): ${errorText}`);
119
+ }
120
+ const result = await response.json();
121
+ if (!result.success) {
122
+ const message = result.errors?.[0]?.message || `Unknown ${options.serviceLabel} error`;
123
+ throw new Error(`${options.serviceLabel} API error: ${message}`);
124
+ }
125
+ return result.result;
126
+ }
127
+ return {
128
+ getAccountId,
129
+ getToken,
130
+ jsonRequest
131
+ };
132
+ }
133
+
134
+ // src/test/remote-ai.ts
135
+ function createRemoteAI(accountId) {
136
+ const cloudflare = createRemoteCloudflareClient(accountId);
101
137
  const ai = {
102
138
  async run(model, inputs) {
103
- const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
104
- const url = `https://api.cloudflare.com/client/v4/accounts/${acctId}/ai/run/${model}`;
105
- const response = await fetch(url, {
139
+ return cloudflare.jsonRequest({
106
140
  method: "POST",
107
- headers: {
108
- Authorization: `Bearer ${token}`,
109
- "Content-Type": "application/json"
110
- },
141
+ path: `/ai/run/${model}`,
142
+ serviceLabel: "AI",
111
143
  body: JSON.stringify(inputs)
112
144
  });
113
- if (!response.ok) {
114
- const errorText = await response.text();
115
- throw new Error(`AI API error (${response.status}): ${errorText}`);
116
- }
117
- const result = await response.json();
118
- if (!result.success) {
119
- const message = result.errors?.[0]?.message || "Unknown AI error";
120
- throw new Error(`AI API error: ${message}`);
121
- }
122
- return result.result;
123
145
  },
124
146
  gateway(_gatewayId) {
125
147
  console.warn("AI Gateway is not supported in remote test mode");
@@ -131,72 +153,25 @@ function createRemoteAI(accountId) {
131
153
 
132
154
  // src/test/remote-vectorize.ts
133
155
  function createRemoteVectorize(indexName, accountId) {
134
- let resolvedAccountId = null;
135
- async function getAccountId() {
136
- if (accountId)
137
- return accountId;
138
- if (resolvedAccountId)
139
- return resolvedAccountId;
140
- const primary = await getPrimaryAccount();
141
- if (!primary) {
142
- throw new Error("No Cloudflare account found. Run: bunx wrangler login");
143
- }
144
- const { accountId: effectiveId } = await getEffectiveAccountId(primary.id);
145
- resolvedAccountId = effectiveId;
146
- return effectiveId;
147
- }
148
- async function getToken() {
149
- const token = await getApiToken();
150
- if (!token) {
151
- throw new Error("Not authenticated. Run: bunx wrangler login");
152
- }
153
- return token;
154
- }
156
+ const cloudflare = createRemoteCloudflareClient(accountId);
155
157
  async function apiRequest(method, endpoint, body) {
156
- const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
157
- const url = `https://api.cloudflare.com/client/v4/accounts/${acctId}/vectorize/v2/indexes/${indexName}${endpoint}`;
158
- const response = await fetch(url, {
158
+ return cloudflare.jsonRequest({
159
159
  method,
160
- headers: {
161
- Authorization: `Bearer ${token}`,
162
- "Content-Type": "application/json"
163
- },
160
+ path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
161
+ serviceLabel: "Vectorize",
164
162
  body: body ? JSON.stringify(body) : undefined
165
163
  });
166
- if (!response.ok) {
167
- const errorText = await response.text();
168
- throw new Error(`Vectorize API error (${response.status}): ${errorText}`);
169
- }
170
- const result = await response.json();
171
- if (!result.success) {
172
- const message = result.errors?.[0]?.message || "Unknown Vectorize error";
173
- throw new Error(`Vectorize API error: ${message}`);
174
- }
175
- return result.result;
176
164
  }
177
165
  async function ndjsonRequest(endpoint, vectors) {
178
- const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
179
- const url = `https://api.cloudflare.com/client/v4/accounts/${acctId}/vectorize/v2/indexes/${indexName}${endpoint}`;
180
166
  const ndjson = vectors.map((v) => JSON.stringify(v)).join(`
181
167
  `);
182
- const response = await fetch(url, {
168
+ return cloudflare.jsonRequest({
183
169
  method: "POST",
184
- headers: {
185
- Authorization: `Bearer ${token}`,
186
- "Content-Type": "application/x-ndjson"
187
- },
170
+ path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
171
+ serviceLabel: "Vectorize",
172
+ contentType: "application/x-ndjson",
188
173
  body: ndjson
189
174
  });
190
- if (!response.ok) {
191
- const errorText = await response.text();
192
- throw new Error(`Vectorize API error (${response.status}): ${errorText}`);
193
- }
194
- const result = await response.json();
195
- if (!result.success) {
196
- const message = result.errors?.[0]?.message || "Unknown Vectorize error";
197
- throw new Error(`Vectorize API error: ${message}`);
198
- }
199
- return result.result;
200
175
  }
201
176
  const vectorize = {
202
177
  async describe() {
@@ -613,75 +588,755 @@ export default {
613
588
  }
614
589
  }
615
590
 
616
- // src/test/queue.ts
617
- import { join as join2 } from "path";
618
- var queueHandlerPath = null;
619
- var configDir = null;
620
- var testEnvGetter = null;
621
- function configureQueue(options) {
622
- queueHandlerPath = options.handlerPath;
623
- configDir = options.configDir;
624
- testEnvGetter = options.getEnv;
591
+ // src/test/simple-context-durable-objects.ts
592
+ import { mkdirSync, writeFileSync } from "fs";
593
+ import { readFile } from "fs/promises";
594
+ import { dirname as dirname3, join as join3 } from "path";
595
+
596
+ // src/test/simple-context-gateway-script.ts
597
+ function buildGatewayScript(bundledCode, wrappers) {
598
+ return `
599
+ // Bundled transport + DO classes
600
+ ${bundledCode}
601
+
602
+ // DO Wrappers with RPC
603
+ ${wrappers}
604
+
605
+ // Transport encoding helper
606
+ const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
607
+
608
+ function __encodeTransport(value) {
609
+ if (value === null || value === undefined) return value
610
+
611
+ // Try each encoder
612
+ for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
613
+ const encoded = transporter.encode(value)
614
+ if (encoded !== false && encoded !== undefined) {
615
+ return { __transport: typeName, value: encoded }
616
+ }
617
+ }
618
+
619
+ // Recursively encode arrays and objects
620
+ if (Array.isArray(value)) {
621
+ return value.map(__encodeTransport)
622
+ }
623
+ if (typeof value === 'object') {
624
+ const result = {}
625
+ for (const [k, v] of Object.entries(value)) {
626
+ result[k] = __encodeTransport(v)
627
+ }
628
+ return result
629
+ }
630
+
631
+ return value
625
632
  }
626
- function resetQueueState() {
627
- queueHandlerPath = null;
628
- configDir = null;
629
- testEnvGetter = null;
633
+
634
+ // Gateway with WebSocket RPC
635
+ export default {
636
+ async fetch(request, env) {
637
+ if (request.headers.get('Upgrade') === 'websocket') {
638
+ const { 0: client, 1: server } = new WebSocketPair()
639
+ server.accept()
640
+ server.addEventListener('message', async (e) => {
641
+ try {
642
+ const m = JSON.parse(e.data)
643
+ if (m.t === 'rpc.call') {
644
+ const result = await executeRpc(env, m.method, m.params)
645
+ server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
646
+ }
647
+ } catch (error) {
648
+ server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
649
+ }
650
+ })
651
+ return new Response(null, { status: 101, webSocket: client })
652
+ }
653
+ return new Response('Gateway')
654
+ }
630
655
  }
631
- function createMessage(options) {
632
- const id = options.id ?? crypto.randomUUID();
633
- const timestamp = options.timestamp ?? new Date;
634
- const attempts = options.attempts ?? 1;
635
- let state = "pending";
636
- return {
637
- id,
638
- timestamp,
639
- body: options.body,
640
- attempts,
641
- ack() {
642
- state = "acked";
643
- },
644
- retry(opts) {
645
- state = "retried";
646
- },
647
- retryAll() {
648
- state = "retried";
649
- },
650
- get _state() {
651
- return state;
652
- }
653
- };
656
+
657
+ async function executeRpc(env, method, params) {
658
+ const [bindingName, ...rest] = method.split('.')
659
+ const op = rest.join('.')
660
+ const binding = env[bindingName]
661
+ const RAW_EMAIL = 'EmailMessage::raw'
662
+ if (!binding) throw new Error('Binding not found: ' + bindingName)
663
+
664
+ // KV operations
665
+ if (op === 'get') return binding.get(params[0], params[1])
666
+ if (op === 'put') return binding.put(params[0], params[1], params[2])
667
+ if (op === 'delete') return binding.delete(params[0])
668
+ if (op === 'list') return binding.list(params[0])
669
+ if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
670
+
671
+ // R2 operations
672
+ if (op === 'r2.get') return binding.get(params[0], params[1])
673
+ if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
674
+ if (op === 'r2.delete') return binding.delete(params[0])
675
+ if (op === 'r2.list') return binding.list(params[0])
676
+ if (op === 'head') return binding.head(params[0])
677
+
678
+ // D1 operations
679
+ if (op === 'exec') return binding.exec(params[0])
680
+ if (op === 'dump') return binding.dump()
681
+ if (op === 'batch') {
682
+ const stmts = params[0].map(s => {
683
+ const stmt = binding.prepare(s.sql)
684
+ return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
685
+ })
686
+ return binding.batch(stmts)
687
+ }
688
+ if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
689
+ if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
690
+ if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
691
+ if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
692
+
693
+ // Send email operations
694
+ if (op === 'email.send') {
695
+ return binding.send(__normalizeEmailMessage(params[0]))
696
+ }
697
+
698
+ // DO operations
699
+ if (op === 'idFromName') {
700
+ return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
701
+ }
702
+ if (op === 'stub.rpc') {
703
+ const [, idSerialized, rpcMethod, rpcParams] = params
704
+ const stub = binding.get(binding.idFromString(idSerialized.hex))
705
+
706
+ if (typeof stub[rpcMethod] === 'function') {
707
+ let result = await stub[rpcMethod](...(rpcParams || []))
708
+ result = __encodeTransport(result)
709
+ return result
710
+ }
711
+
712
+ const response = await stub.fetch(new Request('http://do/_rpc', {
713
+ method: 'POST',
714
+ headers: { 'Content-Type': 'application/json' },
715
+ body: JSON.stringify({ method: rpcMethod, params: rpcParams || [] })
716
+ }))
717
+
718
+ const payload = await response.json()
719
+ if (!response.ok || !payload?.ok) {
720
+ throw new Error(payload?.error?.message || ('DO RPC failed with status ' + response.status))
721
+ }
722
+
723
+ return payload.result
724
+ }
725
+
726
+ throw new Error('Unknown operation: ' + method)
654
727
  }
655
- function createMessageBatch(messages) {
656
- return {
657
- queue: "test-queue",
658
- messages,
659
- ackAll() {
660
- for (const msg of messages) {
661
- msg.ack();
662
- }
663
- },
664
- retryAll() {
665
- for (const msg of messages) {
666
- msg.retry();
667
- }
668
- }
669
- };
728
+
729
+ function __createEmailMessageRaw(raw) {
730
+ if (typeof raw === 'string' || raw instanceof ReadableStream) {
731
+ return raw
732
+ }
733
+ if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
734
+ return new Response(raw).body
735
+ }
736
+ throw new Error('Unsupported EmailMessage raw payload')
670
737
  }
671
- async function trigger(messages) {
672
- if (!queueHandlerPath) {
673
- throw new Error("Queue handler not configured. Make sure your devflare.config.ts has files.queue set, " + "and the file exists at the specified path (default: src/queue.ts)");
674
- }
675
- if (!configDir || !testEnvGetter) {
676
- throw new Error("Queue helper not initialized. Call createTestContext() before using cf.queue.trigger()");
677
- }
678
- const absolutePath = join2(configDir, queueHandlerPath);
679
- const handlerModule = await import(absolutePath);
680
- const queueHandler = handlerModule.default ?? handlerModule.queue;
738
+
739
+ function __buildRawEmail(message) {
740
+ const lines = []
741
+ const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
742
+
743
+ lines.push('From: ' + message.from)
744
+ lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
745
+ lines.push('Date: ' + new Date().toUTCString())
746
+ lines.push('Message-ID: ' + messageId)
747
+
748
+ if (message.subject) lines.push('Subject: ' + message.subject)
749
+ if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
750
+ if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
751
+ if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
752
+
753
+ for (const [key, value] of Object.entries(message.headers || {})) {
754
+ lines.push(key + ': ' + value)
755
+ }
756
+
757
+ lines.push('MIME-Version: 1.0')
758
+ lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
759
+ lines.push('')
760
+ lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
761
+
762
+ return lines.join('\\r\\n')
763
+ }
764
+
765
+ function __normalizeEmailMessage(message) {
766
+ if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
767
+ return message
768
+ }
769
+ if ('EmailMessage::raw' in message) {
770
+ return message
771
+ }
772
+ if ('raw' in message && message.raw !== undefined) {
773
+ return {
774
+ from: message.from,
775
+ to: message.to,
776
+ [RAW_EMAIL]: __createEmailMessageRaw(message.raw)
777
+ }
778
+ }
779
+ return {
780
+ from: message.from,
781
+ to: message.to,
782
+ [RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
783
+ }
784
+ }
785
+ `;
786
+ }
787
+
788
+ // src/test/simple-context-paths.ts
789
+ import { existsSync as existsSync2 } from "fs";
790
+ import { createServer } from "net";
791
+ import { dirname as dirname2, join as join2 } from "path";
792
+ import { fileURLToPath } from "url";
793
+ var DEFAULT_TRANSPORT_ENTRY_FILES = [
794
+ "src/transport.ts",
795
+ "src/transport.js",
796
+ "src/transport.mts",
797
+ "src/transport.mjs"
798
+ ];
799
+ var CURRENT_PACKAGE_ROOT = findPackageRoot(dirname2(fileURLToPath(import.meta.url)));
800
+ function getBunRuntime2() {
801
+ const g = globalThis;
802
+ if (typeof g.Bun === "object" && g.Bun !== null) {
803
+ return g.Bun;
804
+ }
805
+ return;
806
+ }
807
+ function getCallerDirectory() {
808
+ const stackCallerDirectory = getStackCallerDirectory();
809
+ if (stackCallerDirectory) {
810
+ return stackCallerDirectory;
811
+ }
812
+ return process.cwd();
813
+ }
814
+ function getStackCallerDirectory() {
815
+ const originalPrepare = Error.prepareStackTrace;
816
+ Error.prepareStackTrace = (_, stack) => stack;
817
+ try {
818
+ const err = new Error;
819
+ const stack = err.stack;
820
+ for (const site of stack ?? []) {
821
+ const filename = site.getFileName?.();
822
+ if (filename && !isInsideCurrentPackage(filename) && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync2(filename)) {
823
+ return dirname2(filename);
824
+ }
825
+ }
826
+ } finally {
827
+ Error.prepareStackTrace = originalPrepare;
828
+ }
829
+ return null;
830
+ }
831
+ function findPackageRoot(startDir) {
832
+ let currentDir = startDir;
833
+ while (true) {
834
+ if (existsSync2(join2(currentDir, "package.json"))) {
835
+ return currentDir;
836
+ }
837
+ const parentDir = dirname2(currentDir);
838
+ if (parentDir === currentDir) {
839
+ return startDir;
840
+ }
841
+ currentDir = parentDir;
842
+ }
843
+ }
844
+ function isInsideCurrentPackage(filePath) {
845
+ const normalizedFilePath = filePath.replace(/\\/g, "/");
846
+ const normalizedPackageRoot = CURRENT_PACKAGE_ROOT.replace(/\\/g, "/");
847
+ return normalizedFilePath === normalizedPackageRoot || normalizedFilePath.startsWith(`${normalizedPackageRoot}/`);
848
+ }
849
+ async function findNearestConfig(startDir) {
850
+ let currentDir = startDir;
851
+ while (true) {
852
+ const configPath = await resolveConfigPath(currentDir);
853
+ if (configPath) {
854
+ return configPath;
855
+ }
856
+ const parentDir = dirname2(currentDir);
857
+ if (parentDir === currentDir) {
858
+ return null;
859
+ }
860
+ currentDir = parentDir;
861
+ }
862
+ }
863
+ async function getAvailablePort() {
864
+ return await new Promise((resolvePort, reject) => {
865
+ const server = createServer();
866
+ server.once("error", reject);
867
+ server.listen(0, "127.0.0.1", () => {
868
+ const address = server.address();
869
+ if (!address || typeof address === "string") {
870
+ server.close(() => reject(new Error("Could not determine an available port")));
871
+ return;
872
+ }
873
+ const { port } = address;
874
+ server.close((error) => {
875
+ if (error) {
876
+ reject(error);
877
+ return;
878
+ }
879
+ resolvePort(port);
880
+ });
881
+ });
882
+ });
883
+ }
884
+ function resolveTransportFile(configDir, configuredPath) {
885
+ if (typeof configuredPath === "string") {
886
+ return configuredPath;
887
+ }
888
+ if (configuredPath === null) {
889
+ return null;
890
+ }
891
+ for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
892
+ if (existsSync2(join2(configDir, defaultEntry))) {
893
+ return defaultEntry;
894
+ }
895
+ }
896
+ return null;
897
+ }
898
+
899
+ // src/test/simple-context-durable-objects.ts
900
+ function findExportedClasses(code) {
901
+ const classes = [];
902
+ const classPattern = /export\s+class\s+(\w+)/g;
903
+ let match;
904
+ while ((match = classPattern.exec(code)) !== null) {
905
+ classes.push(match[1]);
906
+ }
907
+ return classes;
908
+ }
909
+ function classSupportsNativeDurableObjectRpc(code, className) {
910
+ const nativeRpcPattern = new RegExp(`export\\s+class\\s+${className}\\s+extends\\s+DurableObject\\b`);
911
+ return nativeRpcPattern.test(code);
912
+ }
913
+ function toGeneratedIdentifier(value) {
914
+ const normalized = value.replace(/[^A-Za-z0-9_$]/g, "_");
915
+ return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
916
+ }
917
+ async function discoverLocalDurableObjectClasses(config, configDir) {
918
+ const classToFilePath = new Map;
919
+ const doPatternConfig = config.files?.durableObjects;
920
+ const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
921
+ if (doPatternConfig === false) {
922
+ return classToFilePath;
923
+ }
924
+ const doFiles = await findFiles(doPattern, { cwd: configDir });
925
+ for (const filePath of doFiles) {
926
+ try {
927
+ const code = await readFile(filePath, "utf-8");
928
+ const classNames = findExportedClasses(code);
929
+ for (const className of classNames) {
930
+ classToFilePath.set(className, {
931
+ filePath,
932
+ nativeRpc: classSupportsNativeDurableObjectRpc(code, className)
933
+ });
934
+ }
935
+ } catch {}
936
+ }
937
+ return classToFilePath;
938
+ }
939
+ async function resolveLocalDurableObjects(config, configDir) {
940
+ const doConfig = {};
941
+ const doInfos = [];
942
+ const classToFilePath = await discoverLocalDurableObjectClasses(config, configDir);
943
+ for (const [name, rawDoInfo] of Object.entries(config.bindings?.durableObjects ?? {})) {
944
+ const doInfo = normalizeDOBinding(rawDoInfo);
945
+ if (doInfo.__ref) {
946
+ continue;
947
+ }
948
+ let scriptPath;
949
+ let nativeRpc = false;
950
+ if (doInfo.scriptName) {
951
+ scriptPath = join3(configDir, "src", doInfo.scriptName);
952
+ try {
953
+ const code = await readFile(scriptPath, "utf-8");
954
+ nativeRpc = classSupportsNativeDurableObjectRpc(code, doInfo.className);
955
+ } catch {
956
+ nativeRpc = false;
957
+ }
958
+ } else {
959
+ const discoveredClass = classToFilePath.get(doInfo.className);
960
+ if (!discoveredClass) {
961
+ throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
962
+ ` + `Either:
963
+ ` + ` 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
964
+ ` + ` 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
965
+ }
966
+ scriptPath = discoveredClass.filePath;
967
+ nativeRpc = discoveredClass.nativeRpc;
968
+ }
969
+ const runtimeClassName = nativeRpc ? doInfo.className : `__Devflare${toGeneratedIdentifier(name)}RpcWrapper`;
970
+ doConfig[name] = runtimeClassName;
971
+ doInfos.push({
972
+ name,
973
+ className: doInfo.className,
974
+ scriptPath,
975
+ nativeRpc,
976
+ runtimeClassName
977
+ });
978
+ }
979
+ return {
980
+ doConfig,
981
+ doInfos
982
+ };
983
+ }
984
+ function buildWrapperCode(doInfos) {
985
+ return doInfos.filter((info) => !info.nativeRpc).map((info) => `
986
+ export class ${info.runtimeClassName} {
987
+ constructor(state, env) {
988
+ this.__instance = new ${info.className}(state, env)
989
+ }
990
+
991
+ async fetch(request) {
992
+ const url = new URL(request.url)
993
+ if (request.method !== 'POST' || url.pathname !== '/_rpc') {
994
+ return new Response('Not found', { status: 404 })
995
+ }
996
+
997
+ try {
998
+ const payload = await request.json()
999
+ const method = payload?.method
1000
+ const params = Array.isArray(payload?.params) ? payload.params : []
1001
+ const target = this.__instance?.[method]
1002
+
1003
+ if (typeof target !== 'function') {
1004
+ return new Response(JSON.stringify({
1005
+ ok: false,
1006
+ error: { message: 'Method not found: ' + String(method) }
1007
+ }), {
1008
+ status: 404,
1009
+ headers: { 'Content-Type': 'application/json' }
1010
+ })
1011
+ }
1012
+
1013
+ let result = await target.apply(this.__instance, params)
1014
+ result = __encodeTransport(result)
1015
+
1016
+ return new Response(JSON.stringify({ ok: true, result }), {
1017
+ headers: { 'Content-Type': 'application/json' }
1018
+ })
1019
+ } catch (error) {
1020
+ return new Response(JSON.stringify({
1021
+ ok: false,
1022
+ error: { message: error instanceof Error ? error.message : String(error) }
1023
+ }), {
1024
+ status: 500,
1025
+ headers: { 'Content-Type': 'application/json' }
1026
+ })
1027
+ }
1028
+ }
1029
+ }`.trim()).join(`
1030
+
1031
+ `);
1032
+ }
1033
+ async function bundleDurableObjectModules(configDir, doInfos, transportFile) {
1034
+ const virtualImports = [];
1035
+ const virtualExports = [];
1036
+ if (transportFile) {
1037
+ const transportPath = join3(configDir, transportFile);
1038
+ virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
1039
+ virtualExports.push("export { transport }");
1040
+ }
1041
+ for (const info of doInfos) {
1042
+ virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
1043
+ virtualExports.push(`export { ${info.className} }`);
1044
+ }
1045
+ if (virtualImports.length === 0) {
1046
+ return "";
1047
+ }
1048
+ const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
1049
+ `);
1050
+ const virtualPath = join3(configDir, ".devflare", "__test_entry.ts");
1051
+ mkdirSync(dirname3(virtualPath), { recursive: true });
1052
+ writeFileSync(virtualPath, virtualEntry);
1053
+ const bun = getBunRuntime2();
1054
+ if (!bun) {
1055
+ throw new Error("Bun runtime is required for createTestContext with Durable Objects");
1056
+ }
1057
+ const result = await bun.build({
1058
+ entrypoints: [virtualPath],
1059
+ target: "browser",
1060
+ format: "esm",
1061
+ minify: false,
1062
+ external: ["cloudflare:workers", "cloudflare:*"]
1063
+ });
1064
+ if (!result.success) {
1065
+ throw new Error(`Failed to bundle test entry: ${result.logs.join(`
1066
+ `)}`);
1067
+ }
1068
+ return await result.outputs[0].text();
1069
+ }
1070
+ async function buildDurableObjectGateway(config, configDir, transportFile) {
1071
+ if (!config.bindings?.durableObjects) {
1072
+ return {
1073
+ script: buildGatewayScript("", "")
1074
+ };
1075
+ }
1076
+ const { doConfig, doInfos } = await resolveLocalDurableObjects(config, configDir);
1077
+ const bundledCode = await bundleDurableObjectModules(configDir, doInfos, transportFile);
1078
+ const wrapperCode = buildWrapperCode(doInfos);
1079
+ return {
1080
+ durableObjects: doConfig,
1081
+ script: buildGatewayScript(bundledCode, wrapperCode)
1082
+ };
1083
+ }
1084
+
1085
+ // src/test/email.ts
1086
+ import { join as join4 } from "path";
1087
+ var miniflarePort = 8787;
1088
+ var emailListeners = [];
1089
+ var sentEmails = [];
1090
+ var emailHandlerPath = null;
1091
+ var configDir = null;
1092
+ var testEnvGetter = null;
1093
+ function configureEmail(options = {}) {
1094
+ if (options.port) {
1095
+ miniflarePort = options.port;
1096
+ }
1097
+ emailHandlerPath = options.handlerPath ?? emailHandlerPath;
1098
+ configDir = options.configDir ?? configDir;
1099
+ testEnvGetter = options.getEnv ?? testEnvGetter;
1100
+ }
1101
+ function buildRawEmail(options) {
1102
+ if (options.raw) {
1103
+ return options.raw;
1104
+ }
1105
+ const lines = [];
1106
+ const messageId = `<${Date.now()}-${Math.random().toString(36).slice(2)}@devflare.dev>`;
1107
+ const date = new Date().toUTCString();
1108
+ lines.push(`From: ${options.from}`);
1109
+ lines.push(`To: ${options.to}`);
1110
+ lines.push(`Date: ${date}`);
1111
+ lines.push(`Message-ID: ${messageId}`);
1112
+ if (options.subject) {
1113
+ lines.push(`Subject: ${options.subject}`);
1114
+ }
1115
+ if (options.headers) {
1116
+ for (const [key, value] of Object.entries(options.headers)) {
1117
+ lines.push(`${key}: ${value}`);
1118
+ }
1119
+ }
1120
+ lines.push("MIME-Version: 1.0");
1121
+ lines.push("Content-Type: text/plain; charset=UTF-8");
1122
+ lines.push("");
1123
+ lines.push(options.body ?? "");
1124
+ return lines.join(`\r
1125
+ `);
1126
+ }
1127
+ function createEmailHeaders(rawEmail) {
1128
+ const headers = new Headers;
1129
+ const lines = rawEmail.split(/\r?\n/);
1130
+ for (const line of lines) {
1131
+ if (!line.trim()) {
1132
+ break;
1133
+ }
1134
+ const colonIndex = line.indexOf(":");
1135
+ if (colonIndex <= 0) {
1136
+ continue;
1137
+ }
1138
+ headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim());
1139
+ }
1140
+ return headers;
1141
+ }
1142
+ function createRawEmailStream(rawEmail) {
1143
+ return new ReadableStream({
1144
+ start(controller) {
1145
+ controller.enqueue(new TextEncoder().encode(rawEmail));
1146
+ controller.close();
1147
+ }
1148
+ });
1149
+ }
1150
+ function resolveEmailHandler(module) {
1151
+ if (typeof module.default === "function") {
1152
+ return module.default;
1153
+ }
1154
+ if (module.default && typeof module.default.email === "function") {
1155
+ return module.default.email.bind(module.default);
1156
+ }
1157
+ if (typeof module.email === "function") {
1158
+ return module.email;
1159
+ }
1160
+ return null;
1161
+ }
1162
+ function getRecordedRawContent(raw) {
1163
+ if (typeof raw === "string") {
1164
+ return raw;
1165
+ }
1166
+ return;
1167
+ }
1168
+ async function send(options) {
1169
+ const raw = buildRawEmail(options);
1170
+ if (emailHandlerPath && configDir && testEnvGetter) {
1171
+ const absolutePath = join4(configDir, emailHandlerPath);
1172
+ const handlerModule = await import(absolutePath);
1173
+ const emailHandler = resolveEmailHandler(handlerModule);
1174
+ if (!emailHandler) {
1175
+ throw new Error(`Email handler at "${emailHandlerPath}" must export a default function or named "email" export.
1176
+ NaN`);
1177
+ }
1178
+ const waitUntilPromises = [];
1179
+ const ctx = {
1180
+ waitUntil(promise) {
1181
+ waitUntilPromises.push(promise);
1182
+ },
1183
+ passThroughOnException() {},
1184
+ props: {}
1185
+ };
1186
+ const runtimeEnv = testEnvGetter();
1187
+ const timestamp = new Date;
1188
+ const message = {
1189
+ from: options.from,
1190
+ to: options.to,
1191
+ headers: createEmailHeaders(raw),
1192
+ raw: createRawEmailStream(raw),
1193
+ rawSize: raw.length,
1194
+ setReject(reason) {
1195
+ throw new Error(`Email rejected: ${reason}`);
1196
+ },
1197
+ async forward(rcptTo) {
1198
+ recordSentEmail({
1199
+ type: "forward",
1200
+ from: options.from,
1201
+ to: rcptTo,
1202
+ raw,
1203
+ timestamp
1204
+ });
1205
+ },
1206
+ async reply(message2) {
1207
+ recordSentEmail({
1208
+ type: "reply",
1209
+ from: message2.from ?? options.to,
1210
+ to: message2.to ?? options.from,
1211
+ raw: getRecordedRawContent(message2.raw),
1212
+ timestamp
1213
+ });
1214
+ }
1215
+ };
1216
+ const emailEvent = createEmailEvent(message, runtimeEnv, ctx);
1217
+ await runWithEventContext(emailEvent, () => emailHandler(emailEvent));
1218
+ await Promise.all(waitUntilPromises);
1219
+ return new Response(JSON.stringify({ ok: true, from: options.from, to: options.to }), {
1220
+ headers: { "Content-Type": "application/json" }
1221
+ });
1222
+ }
1223
+ const url = new URL(`http://localhost:${miniflarePort}/cdn-cgi/handler/email`);
1224
+ url.searchParams.set("from", options.from);
1225
+ url.searchParams.set("to", options.to);
1226
+ const response = await fetch(url.toString(), {
1227
+ method: "POST",
1228
+ headers: {
1229
+ "Content-Type": "text/plain"
1230
+ },
1231
+ body: raw
1232
+ });
1233
+ return response;
1234
+ }
1235
+ function onReceive(callback) {
1236
+ emailListeners.push(callback);
1237
+ return () => {
1238
+ emailListeners = emailListeners.filter((cb) => cb !== callback);
1239
+ };
1240
+ }
1241
+ function getSentEmails() {
1242
+ return [...sentEmails];
1243
+ }
1244
+ function clearSentEmails() {
1245
+ sentEmails = [];
1246
+ }
1247
+ function recordSentEmail(email) {
1248
+ sentEmails.push(email);
1249
+ for (const listener of emailListeners) {
1250
+ try {
1251
+ listener(email);
1252
+ } catch (error) {
1253
+ console.error("[devflare/test] Email listener error:", error);
1254
+ }
1255
+ }
1256
+ }
1257
+ function resetEmailState() {
1258
+ miniflarePort = 8787;
1259
+ emailHandlerPath = null;
1260
+ configDir = null;
1261
+ testEnvGetter = null;
1262
+ emailListeners = [];
1263
+ sentEmails = [];
1264
+ }
1265
+ var email = {
1266
+ send,
1267
+ onReceive,
1268
+ getSentEmails,
1269
+ clearSentEmails
1270
+ };
1271
+
1272
+ // src/test/queue.ts
1273
+ import { join as join5 } from "path";
1274
+ var queueHandlerPath = null;
1275
+ var configDir2 = null;
1276
+ var testEnvGetter2 = null;
1277
+ function configureQueue(options) {
1278
+ queueHandlerPath = options.handlerPath;
1279
+ configDir2 = options.configDir;
1280
+ testEnvGetter2 = options.getEnv;
1281
+ }
1282
+ function resetQueueState() {
1283
+ queueHandlerPath = null;
1284
+ configDir2 = null;
1285
+ testEnvGetter2 = null;
1286
+ }
1287
+ function createMessage(options) {
1288
+ const id = options.id ?? crypto.randomUUID();
1289
+ const timestamp = options.timestamp ?? new Date;
1290
+ const attempts = options.attempts ?? 1;
1291
+ let state = "pending";
1292
+ return {
1293
+ id,
1294
+ timestamp,
1295
+ body: options.body,
1296
+ attempts,
1297
+ ack() {
1298
+ state = "acked";
1299
+ },
1300
+ retry(opts) {
1301
+ state = "retried";
1302
+ },
1303
+ retryAll() {
1304
+ state = "retried";
1305
+ },
1306
+ get _state() {
1307
+ return state;
1308
+ }
1309
+ };
1310
+ }
1311
+ function createMessageBatch(messages) {
1312
+ return {
1313
+ queue: "test-queue",
1314
+ messages,
1315
+ ackAll() {
1316
+ for (const msg of messages) {
1317
+ msg.ack();
1318
+ }
1319
+ },
1320
+ retryAll() {
1321
+ for (const msg of messages) {
1322
+ msg.retry();
1323
+ }
1324
+ }
1325
+ };
1326
+ }
1327
+ async function trigger(messages) {
1328
+ if (!queueHandlerPath) {
1329
+ throw new Error("Queue handler not configured. Make sure your devflare.config.ts has files.queue set, " + "and the file exists at the specified path (default: src/queue.ts)");
1330
+ }
1331
+ if (!configDir2 || !testEnvGetter2) {
1332
+ throw new Error("Queue helper not initialized. Call createTestContext() before using cf.queue.trigger()");
1333
+ }
1334
+ const absolutePath = join5(configDir2, queueHandlerPath);
1335
+ const handlerModule = await import(absolutePath);
1336
+ const queueHandler = handlerModule.default ?? handlerModule.queue;
681
1337
  if (typeof queueHandler !== "function") {
682
1338
  throw new Error(`Queue handler at "${queueHandlerPath}" must export a default function or named "queue" export.
683
- Expected: export async function queue(event) { ... }
684
- Legacy compatibility is still supported for queue(batch, env, ctx).`);
1339
+ NaN`);
685
1340
  }
686
1341
  const normalizedMessages = messages.map((msg) => {
687
1342
  if (typeof msg === "object" && msg !== null && "body" in msg) {
@@ -699,9 +1354,9 @@ Legacy compatibility is still supported for queue(batch, env, ctx).`);
699
1354
  passThroughOnException() {},
700
1355
  props: {}
701
1356
  };
702
- const env = testEnvGetter();
1357
+ const env = testEnvGetter2();
703
1358
  const queueEvent = createQueueEvent(batch, env, ctx);
704
- await runWithEventContext(queueEvent, () => queueHandler(queueEvent, env, ctx));
1359
+ await runWithEventContext(queueEvent, () => queueHandler(queueEvent));
705
1360
  await Promise.all(waitUntilPromises);
706
1361
  const acked = [];
707
1362
  const retried = [];
@@ -726,46 +1381,45 @@ Legacy compatibility is still supported for queue(batch, env, ctx).`);
726
1381
  total: mockMessages.length
727
1382
  };
728
1383
  }
729
- async function send(message) {
1384
+ async function send2(message) {
730
1385
  return trigger([message]);
731
1386
  }
732
1387
  var queue = {
733
1388
  trigger,
734
- send
1389
+ send: send2
735
1390
  };
736
1391
 
737
1392
  // src/test/scheduled.ts
738
- import { join as join3 } from "path";
1393
+ import { join as join6 } from "path";
739
1394
  var scheduledHandlerPath = null;
740
- var configDir2 = null;
741
- var testEnvGetter2 = null;
1395
+ var configDir3 = null;
1396
+ var testEnvGetter3 = null;
742
1397
  function configureScheduled(options) {
743
1398
  scheduledHandlerPath = options.handlerPath;
744
- configDir2 = options.configDir;
745
- testEnvGetter2 = options.getEnv;
1399
+ configDir3 = options.configDir;
1400
+ testEnvGetter3 = options.getEnv;
746
1401
  }
747
1402
  function resetScheduledState() {
748
1403
  scheduledHandlerPath = null;
749
- configDir2 = null;
750
- testEnvGetter2 = null;
1404
+ configDir3 = null;
1405
+ testEnvGetter3 = null;
751
1406
  }
752
1407
  async function trigger2(cronOrOptions) {
753
1408
  if (!scheduledHandlerPath) {
754
1409
  throw new Error("Scheduled handler not configured. Make sure your devflare.config.ts has files.scheduled set, " + "and the file exists at the specified path (default: src/scheduled.ts)");
755
1410
  }
756
- if (!configDir2 || !testEnvGetter2) {
1411
+ if (!configDir3 || !testEnvGetter3) {
757
1412
  throw new Error("Scheduled helper not initialized. Call createTestContext() before using cf.scheduled.trigger()");
758
1413
  }
759
1414
  const options = typeof cronOrOptions === "string" ? { cron: cronOrOptions } : cronOrOptions ?? {};
760
1415
  const cron = options.cron ?? "* * * * *";
761
1416
  const scheduledTime = options.scheduledTime instanceof Date ? options.scheduledTime.getTime() : options.scheduledTime ?? Date.now();
762
- const absolutePath = join3(configDir2, scheduledHandlerPath);
1417
+ const absolutePath = join6(configDir3, scheduledHandlerPath);
763
1418
  const handlerModule = await import(absolutePath);
764
1419
  const scheduledHandler = handlerModule.default ?? handlerModule.scheduled;
765
1420
  if (typeof scheduledHandler !== "function") {
766
1421
  throw new Error(`Scheduled handler at "${scheduledHandlerPath}" must export a default function or named "scheduled" export.
767
- Expected: export async function scheduled(event) { ... }
768
- Legacy compatibility is still supported for scheduled(controller, env, ctx).`);
1422
+ NaN`);
769
1423
  }
770
1424
  const controller = {
771
1425
  scheduledTime,
@@ -780,10 +1434,10 @@ Legacy compatibility is still supported for scheduled(controller, env, ctx).`);
780
1434
  passThroughOnException() {},
781
1435
  props: {}
782
1436
  };
783
- const env = testEnvGetter2();
1437
+ const env = testEnvGetter3();
784
1438
  const scheduledEvent = createScheduledEvent(controller, env, ctx);
785
1439
  try {
786
- await runWithEventContext(scheduledEvent, () => scheduledHandler(scheduledEvent, env, ctx));
1440
+ await runWithEventContext(scheduledEvent, () => scheduledHandler(scheduledEvent));
787
1441
  await Promise.all(waitUntilPromises);
788
1442
  return {
789
1443
  success: true,
@@ -803,117 +1457,8 @@ var scheduled = {
803
1457
  trigger: trigger2
804
1458
  };
805
1459
 
806
- // src/test/worker.ts
807
- import { join as join4 } from "path";
808
- var fetchHandlerPath = null;
809
- var configDir3 = null;
810
- var testEnvGetter3 = null;
811
- var fileRoutes = [];
812
- function configureWorker(options) {
813
- fetchHandlerPath = options.handlerPath;
814
- fileRoutes = options.routes ?? [];
815
- configDir3 = options.configDir;
816
- testEnvGetter3 = options.getEnv;
817
- }
818
- function resetWorkerState() {
819
- fetchHandlerPath = null;
820
- fileRoutes = [];
821
- configDir3 = null;
822
- testEnvGetter3 = null;
823
- }
824
- async function fetch2(request, options) {
825
- if (!fetchHandlerPath && fileRoutes.length === 0) {
826
- throw new Error("Fetch handler not configured. Make sure your devflare.config.ts has files.fetch set or a routes directory is available, " + "and that the corresponding files exist (defaults: src/fetch.ts and src/routes/**).");
827
- }
828
- if (!configDir3 || !testEnvGetter3) {
829
- throw new Error("Worker helper not initialized. Call createTestContext() before using cf.worker.fetch()");
830
- }
831
- const workerConfigDir = configDir3;
832
- const getEnv = testEnvGetter3;
833
- let req;
834
- if (typeof request === "string") {
835
- const url = request.startsWith("http") ? request : `http://localhost${request.startsWith("/") ? "" : "/"}${request}`;
836
- const headers = new Headers(options?.headers);
837
- let body;
838
- if (options?.body !== undefined) {
839
- if (typeof options.body === "string") {
840
- body = options.body;
841
- } else {
842
- body = JSON.stringify(options.body);
843
- if (!headers.has("Content-Type")) {
844
- headers.set("Content-Type", "application/json");
845
- }
846
- }
847
- }
848
- req = new Request(url, {
849
- method: options?.method ?? "GET",
850
- headers,
851
- body
852
- });
853
- } else {
854
- req = request;
855
- }
856
- const handlerModule = fetchHandlerPath ? await import(join4(workerConfigDir, fetchHandlerPath)) : {};
857
- const routeModules = await Promise.all(fileRoutes.map(async (route) => {
858
- return {
859
- ...route,
860
- module: await import(join4(workerConfigDir, route.filePath))
861
- };
862
- }));
863
- const methodExports = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "ALL"];
864
- const hasMethodHandler = methodExports.some((method) => {
865
- return typeof handlerModule[method] === "function" || typeof handlerModule.default?.[method] === "function";
866
- });
867
- if (!resolveFetchHandler(handlerModule) && !hasMethodHandler && routeModules.length === 0) {
868
- throw new Error(`Fetch handler at "${fetchHandlerPath}" must export one of:
869
- - request-wide "handle" middleware
870
- - named "fetch"
871
- - default fetch handler
872
- - HTTP method exports such as "GET" or "POST"
873
- Legacy compatibility is still supported for fetch(request, env, ctx).`);
874
- }
875
- const waitUntilPromises = [];
876
- const ctx = {
877
- waitUntil(promise) {
878
- waitUntilPromises.push(promise);
879
- },
880
- passThroughOnException() {},
881
- props: {}
882
- };
883
- const env = getEnv();
884
- const initialRouteMatch = routeModules.length > 0 ? matchFetchRoute(routeModules, req) : null;
885
- const fetchEvent = createFetchEvent(req, env, ctx, {
886
- params: initialRouteMatch?.params ?? {}
887
- });
888
- const response = await runWithEventContext(fetchEvent, () => invokeFetchModule(handlerModule, fetchEvent, routeModules.length > 0 ? createRouteResolve(routeModules, fetchEvent) : undefined));
889
- return response;
890
- }
891
- async function get(path, headers) {
892
- return fetch2(path, { method: "GET", headers });
893
- }
894
- async function post(path, body, headers) {
895
- return fetch2(path, { method: "POST", body, headers });
896
- }
897
- async function put(path, body, headers) {
898
- return fetch2(path, { method: "PUT", body, headers });
899
- }
900
- async function del(path, headers) {
901
- return fetch2(path, { method: "DELETE", headers });
902
- }
903
- async function patch(path, body, headers) {
904
- return fetch2(path, { method: "PATCH", body, headers });
905
- }
906
- var worker = {
907
- fetch: fetch2,
908
- get,
909
- post,
910
- put,
911
- delete: del,
912
- patch
913
- };
914
-
915
1460
  // src/test/tail.ts
916
- import { join as join5 } from "path";
1461
+ import { join as join7 } from "path";
917
1462
  var tailHandlerPath = null;
918
1463
  var configDir4 = null;
919
1464
  var testEnvGetter4 = null;
@@ -959,13 +1504,12 @@ async function trigger3(items) {
959
1504
  }
960
1505
  return createTraceItem(item);
961
1506
  });
962
- const absolutePath = join5(configDir4, tailHandlerPath);
1507
+ const absolutePath = join7(configDir4, tailHandlerPath);
963
1508
  const handlerModule = await import(absolutePath);
964
1509
  const tailHandler = handlerModule.default ?? handlerModule.tail;
965
1510
  if (typeof tailHandler !== "function") {
966
1511
  throw new Error(`Tail handler at "${tailHandlerPath}" must export a default function or named "tail" export.
967
- Expected: export async function tail(event) { ... }
968
- Legacy compatibility is still supported for tail(events, env, ctx).`);
1512
+ NaN`);
969
1513
  }
970
1514
  const waitUntilPromises = [];
971
1515
  const ctx = {
@@ -978,7 +1522,7 @@ Legacy compatibility is still supported for tail(events, env, ctx).`);
978
1522
  const env = testEnvGetter4();
979
1523
  const tailEvent = createTailEvent(traceItems, env, ctx);
980
1524
  try {
981
- await runWithEventContext(tailEvent, () => tailHandler(tailEvent, env, ctx));
1525
+ await runWithEventContext(tailEvent, () => tailHandler(tailEvent));
982
1526
  await Promise.all(waitUntilPromises);
983
1527
  return {
984
1528
  success: true,
@@ -987,284 +1531,206 @@ Legacy compatibility is still supported for tail(events, env, ctx).`);
987
1531
  } catch (error) {
988
1532
  return {
989
1533
  success: false,
990
- error: error instanceof Error ? error.message : String(error),
991
- itemCount: traceItems.length
992
- };
993
- }
994
- }
995
- function create(options = {}) {
996
- return createTraceItem(options);
997
- }
998
- var tail = {
999
- trigger: trigger3,
1000
- create
1001
- };
1002
-
1003
- // src/test/email.ts
1004
- import { join as join6 } from "path";
1005
- var miniflarePort = 8787;
1006
- var emailListeners = [];
1007
- var sentEmails = [];
1008
- var emailHandlerPath = null;
1009
- var configDir5 = null;
1010
- var testEnvGetter5 = null;
1011
- function configureEmail(options = {}) {
1012
- if (options.port) {
1013
- miniflarePort = options.port;
1014
- }
1015
- emailHandlerPath = options.handlerPath ?? emailHandlerPath;
1016
- configDir5 = options.configDir ?? configDir5;
1017
- testEnvGetter5 = options.getEnv ?? testEnvGetter5;
1018
- }
1019
- function buildRawEmail(options) {
1020
- if (options.raw) {
1021
- return options.raw;
1022
- }
1023
- const lines = [];
1024
- const messageId = `<${Date.now()}-${Math.random().toString(36).slice(2)}@devflare.dev>`;
1025
- const date = new Date().toUTCString();
1026
- lines.push(`From: ${options.from}`);
1027
- lines.push(`To: ${options.to}`);
1028
- lines.push(`Date: ${date}`);
1029
- lines.push(`Message-ID: ${messageId}`);
1030
- if (options.subject) {
1031
- lines.push(`Subject: ${options.subject}`);
1032
- }
1033
- if (options.headers) {
1034
- for (const [key, value] of Object.entries(options.headers)) {
1035
- lines.push(`${key}: ${value}`);
1036
- }
1037
- }
1038
- lines.push("MIME-Version: 1.0");
1039
- lines.push("Content-Type: text/plain; charset=UTF-8");
1040
- lines.push("");
1041
- lines.push(options.body ?? "");
1042
- return lines.join(`\r
1043
- `);
1044
- }
1045
- function createEmailHeaders(rawEmail) {
1046
- const headers = new Headers;
1047
- const lines = rawEmail.split(/\r?\n/);
1048
- for (const line of lines) {
1049
- if (!line.trim()) {
1050
- break;
1051
- }
1052
- const colonIndex = line.indexOf(":");
1053
- if (colonIndex <= 0) {
1054
- continue;
1055
- }
1056
- headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim());
1534
+ error: error instanceof Error ? error.message : String(error),
1535
+ itemCount: traceItems.length
1536
+ };
1057
1537
  }
1058
- return headers;
1059
1538
  }
1060
- function createRawEmailStream(rawEmail) {
1061
- return new ReadableStream({
1062
- start(controller) {
1063
- controller.enqueue(new TextEncoder().encode(rawEmail));
1064
- controller.close();
1065
- }
1066
- });
1539
+ function create(options = {}) {
1540
+ return createTraceItem(options);
1067
1541
  }
1068
- function resolveEmailHandler(module) {
1069
- if (typeof module.default === "function") {
1070
- return module.default;
1071
- }
1072
- if (module.default && typeof module.default.email === "function") {
1073
- return module.default.email.bind(module.default);
1074
- }
1075
- if (typeof module.email === "function") {
1076
- return module.email;
1077
- }
1078
- return null;
1542
+ var tail = {
1543
+ trigger: trigger3,
1544
+ create
1545
+ };
1546
+
1547
+ // src/test/worker.ts
1548
+ import { join as join8 } from "path";
1549
+ var fetchHandlerPath = null;
1550
+ var configDir5 = null;
1551
+ var testEnvGetter5 = null;
1552
+ var fileRoutes = [];
1553
+ function configureWorker(options) {
1554
+ fetchHandlerPath = options.handlerPath;
1555
+ fileRoutes = options.routes ?? [];
1556
+ configDir5 = options.configDir;
1557
+ testEnvGetter5 = options.getEnv;
1079
1558
  }
1080
- function getRecordedRawContent(raw) {
1081
- if (typeof raw === "string") {
1082
- return raw;
1083
- }
1084
- return;
1559
+ function resetWorkerState() {
1560
+ fetchHandlerPath = null;
1561
+ fileRoutes = [];
1562
+ configDir5 = null;
1563
+ testEnvGetter5 = null;
1085
1564
  }
1086
- async function send2(options) {
1087
- const raw = buildRawEmail(options);
1088
- if (emailHandlerPath && configDir5 && testEnvGetter5) {
1089
- const absolutePath = join6(configDir5, emailHandlerPath);
1090
- const handlerModule = await import(absolutePath);
1091
- const emailHandler = resolveEmailHandler(handlerModule);
1092
- if (!emailHandler) {
1093
- throw new Error(`Email handler at "${emailHandlerPath}" must export a default function or named "email" export.
1094
- Expected: export async function email(message) { ... }
1095
- Legacy compatibility is still supported for email(message, env, ctx).`);
1096
- }
1097
- const waitUntilPromises = [];
1098
- const ctx = {
1099
- waitUntil(promise) {
1100
- waitUntilPromises.push(promise);
1101
- },
1102
- passThroughOnException() {},
1103
- props: {}
1104
- };
1105
- const runtimeEnv = testEnvGetter5();
1106
- const timestamp = new Date;
1107
- const message = {
1108
- from: options.from,
1109
- to: options.to,
1110
- headers: createEmailHeaders(raw),
1111
- raw: createRawEmailStream(raw),
1112
- rawSize: raw.length,
1113
- setReject(reason) {
1114
- throw new Error(`Email rejected: ${reason}`);
1115
- },
1116
- async forward(rcptTo) {
1117
- recordSentEmail({
1118
- type: "forward",
1119
- from: options.from,
1120
- to: rcptTo,
1121
- raw,
1122
- timestamp
1123
- });
1124
- },
1125
- async reply(message2) {
1126
- recordSentEmail({
1127
- type: "reply",
1128
- from: message2.from ?? options.to,
1129
- to: message2.to ?? options.from,
1130
- raw: getRecordedRawContent(message2.raw),
1131
- timestamp
1132
- });
1565
+ async function fetch2(request, options) {
1566
+ if (!fetchHandlerPath && fileRoutes.length === 0) {
1567
+ throw new Error("Fetch handler not configured. Make sure your devflare.config.ts has files.fetch set or a routes directory is available, " + "and that the corresponding files exist (defaults: src/fetch.ts and src/routes/**).");
1568
+ }
1569
+ if (!configDir5 || !testEnvGetter5) {
1570
+ throw new Error("Worker helper not initialized. Call createTestContext() before using cf.worker.fetch()");
1571
+ }
1572
+ const workerConfigDir = configDir5;
1573
+ const getEnv = testEnvGetter5;
1574
+ let req;
1575
+ if (typeof request === "string") {
1576
+ const url = request.startsWith("http") ? request : `http://localhost${request.startsWith("/") ? "" : "/"}${request}`;
1577
+ const headers = new Headers(options?.headers);
1578
+ let body;
1579
+ if (options?.body !== undefined) {
1580
+ if (typeof options.body === "string") {
1581
+ body = options.body;
1582
+ } else {
1583
+ body = JSON.stringify(options.body);
1584
+ if (!headers.has("Content-Type")) {
1585
+ headers.set("Content-Type", "application/json");
1586
+ }
1133
1587
  }
1134
- };
1135
- const emailEvent = createEmailEvent(message, runtimeEnv, ctx);
1136
- await runWithEventContext(emailEvent, () => emailHandler(emailEvent, runtimeEnv, ctx));
1137
- await Promise.all(waitUntilPromises);
1138
- return new Response(JSON.stringify({ ok: true, from: options.from, to: options.to }), {
1139
- headers: { "Content-Type": "application/json" }
1588
+ }
1589
+ req = new Request(url, {
1590
+ method: options?.method ?? "GET",
1591
+ headers,
1592
+ body
1140
1593
  });
1594
+ } else {
1595
+ req = request;
1141
1596
  }
1142
- const url = new URL(`http://localhost:${miniflarePort}/cdn-cgi/handler/email`);
1143
- url.searchParams.set("from", options.from);
1144
- url.searchParams.set("to", options.to);
1145
- const response = await fetch(url.toString(), {
1146
- method: "POST",
1147
- headers: {
1148
- "Content-Type": "text/plain"
1597
+ const handlerModule = fetchHandlerPath ? await import(join8(workerConfigDir, fetchHandlerPath)) : {};
1598
+ const routeModules = await Promise.all(fileRoutes.map(async (route) => {
1599
+ return {
1600
+ ...route,
1601
+ module: await import(join8(workerConfigDir, route.filePath))
1602
+ };
1603
+ }));
1604
+ const methodExports = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "ALL"];
1605
+ const hasMethodHandler = methodExports.some((method) => {
1606
+ return typeof handlerModule[method] === "function" || typeof handlerModule.default?.[method] === "function";
1607
+ });
1608
+ if (!resolveFetchHandler(handlerModule) && !hasMethodHandler && routeModules.length === 0) {
1609
+ throw new Error(`Fetch handler at "${fetchHandlerPath}" must export one of:
1610
+ - request-wide "handle" middleware
1611
+ - named "fetch"
1612
+ - default fetch handler
1613
+ - HTTP method exports such as "GET" or "POST"`);
1614
+ }
1615
+ const waitUntilPromises = [];
1616
+ const ctx = {
1617
+ waitUntil(promise) {
1618
+ waitUntilPromises.push(promise);
1149
1619
  },
1150
- body: raw
1620
+ passThroughOnException() {},
1621
+ props: {}
1622
+ };
1623
+ const env = getEnv();
1624
+ const initialRouteMatch = routeModules.length > 0 ? matchFetchRoute(routeModules, req) : null;
1625
+ const fetchEvent = createFetchEvent(req, env, ctx, {
1626
+ params: initialRouteMatch?.params ?? {}
1151
1627
  });
1628
+ const response = await runWithEventContext(fetchEvent, () => invokeFetchModule(handlerModule, fetchEvent, routeModules.length > 0 ? createRouteResolve(routeModules, fetchEvent) : undefined));
1152
1629
  return response;
1153
1630
  }
1154
- function onReceive(callback) {
1155
- emailListeners.push(callback);
1156
- return () => {
1157
- emailListeners = emailListeners.filter((cb) => cb !== callback);
1158
- };
1631
+ async function get(path, headers) {
1632
+ return fetch2(path, { method: "GET", headers });
1159
1633
  }
1160
- function getSentEmails() {
1161
- return [...sentEmails];
1634
+ async function post(path, body, headers) {
1635
+ return fetch2(path, { method: "POST", body, headers });
1162
1636
  }
1163
- function clearSentEmails() {
1164
- sentEmails = [];
1637
+ async function put(path, body, headers) {
1638
+ return fetch2(path, { method: "PUT", body, headers });
1165
1639
  }
1166
- function recordSentEmail(email) {
1167
- sentEmails.push(email);
1168
- for (const listener of emailListeners) {
1169
- try {
1170
- listener(email);
1171
- } catch (error) {
1172
- console.error("[devflare/test] Email listener error:", error);
1173
- }
1174
- }
1640
+ async function del(path, headers) {
1641
+ return fetch2(path, { method: "DELETE", headers });
1175
1642
  }
1176
- function resetEmailState() {
1177
- miniflarePort = 8787;
1178
- emailHandlerPath = null;
1179
- configDir5 = null;
1180
- testEnvGetter5 = null;
1181
- emailListeners = [];
1182
- sentEmails = [];
1643
+ async function patch(path, body, headers) {
1644
+ return fetch2(path, { method: "PATCH", body, headers });
1183
1645
  }
1184
- var email = {
1185
- send: send2,
1186
- onReceive,
1187
- getSentEmails,
1188
- clearSentEmails
1646
+ var worker = {
1647
+ fetch: fetch2,
1648
+ get,
1649
+ post,
1650
+ put,
1651
+ delete: del,
1652
+ patch
1189
1653
  };
1190
1654
 
1191
1655
  // src/test/simple-context.ts
1192
- function findExportedClasses(code) {
1193
- const classes = [];
1194
- const classPattern = /export\s+class\s+(\w+)/g;
1195
- let match;
1196
- while ((match = classPattern.exec(code)) !== null) {
1197
- classes.push(match[1]);
1198
- }
1199
- return classes;
1200
- }
1201
- function getBunRuntime2() {
1202
- const g = globalThis;
1203
- if (typeof g.Bun === "object" && g.Bun !== null) {
1204
- return g.Bun;
1205
- }
1206
- return;
1207
- }
1208
1656
  var globalClient = null;
1209
1657
  var globalMiniflare = null;
1210
1658
  var globalEnvProxy = null;
1211
1659
  var globalTransportDecode = null;
1212
1660
  var globalRemoteBindings = null;
1213
1661
  var globalMiniflareBindings = null;
1214
- var DEFAULT_TRANSPORT_ENTRY_FILES = [
1215
- "src/transport.ts",
1216
- "src/transport.js",
1217
- "src/transport.mts",
1218
- "src/transport.mjs"
1219
- ];
1220
- function getCallerDirectory() {
1221
- const bun = getBunRuntime2();
1222
- if (bun?.main) {
1223
- const mainPath = bun.main;
1224
- if (!mainPath.includes("[") && existsSync2(mainPath)) {
1225
- return dirname2(mainPath);
1226
- }
1227
- }
1228
- const originalPrepare = Error.prepareStackTrace;
1229
- Error.prepareStackTrace = (_, stack2) => stack2;
1230
- const err = new Error;
1231
- const stack = err.stack;
1232
- Error.prepareStackTrace = originalPrepare;
1233
- for (const site of stack) {
1234
- const filename = site.getFileName?.();
1235
- if (filename && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync2(filename)) {
1236
- return dirname2(filename);
1237
- }
1662
+ var TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS = 3;
1663
+ var TEST_CONTEXT_STARTUP_RETRY_DELAY_MS = 75;
1664
+ var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS = 8;
1665
+ var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS = 150;
1666
+ function isRetriableTestContextStartupError(error) {
1667
+ if (!(error instanceof Error)) {
1668
+ return false;
1238
1669
  }
1239
- return process.cwd();
1670
+ const message = error.message.toLowerCase();
1671
+ return message.includes("websocket connection failed") || message.includes("connection timeout: ws://") || message.includes("econnrefused") || message.includes("eaddrinuse") || message.includes("address already in use");
1240
1672
  }
1241
- async function findNearestConfig(startDir) {
1242
- let currentDir = startDir;
1243
- while (true) {
1244
- const configPath = await resolveConfigPath(currentDir);
1245
- if (configPath) {
1246
- return configPath;
1247
- }
1248
- const parentDir = dirname2(currentDir);
1249
- if (parentDir === currentDir) {
1250
- return null;
1673
+ async function waitForTestContextStartupRetry() {
1674
+ await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_STARTUP_RETRY_DELAY_MS));
1675
+ }
1676
+ async function waitForBridgeClientRetry() {
1677
+ await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS));
1678
+ }
1679
+ function shouldPreferBridgeBinding(hint) {
1680
+ return hint === "do" || hint === "service";
1681
+ }
1682
+ async function connectBridgeClientWithRetry(url) {
1683
+ let lastError;
1684
+ for (let attempt = 1;attempt <= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS; attempt++) {
1685
+ const client = new BridgeClient({ url });
1686
+ try {
1687
+ await client.connect();
1688
+ return client;
1689
+ } catch (error) {
1690
+ lastError = error;
1691
+ client.disconnect();
1692
+ if (attempt >= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
1693
+ throw error;
1694
+ }
1695
+ await waitForBridgeClientRetry();
1251
1696
  }
1252
- currentDir = parentDir;
1253
1697
  }
1698
+ throw lastError instanceof Error ? lastError : new Error("Bridge-backed test context could not connect to the WebSocket gateway.");
1254
1699
  }
1255
- function resolveTransportFile(configDir6, configuredPath) {
1256
- if (typeof configuredPath === "string") {
1257
- return configuredPath;
1258
- }
1259
- if (configuredPath === null) {
1260
- return null;
1261
- }
1262
- for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
1263
- if (existsSync2(join7(configDir6, defaultEntry))) {
1264
- return defaultEntry;
1700
+ async function startBridgeBackedTestContext(mfConfig) {
1701
+ const { Miniflare } = await import("miniflare");
1702
+ for (let attempt = 1;attempt <= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS; attempt++) {
1703
+ const port = await getAvailablePort();
1704
+ let miniflare = null;
1705
+ let client = null;
1706
+ try {
1707
+ miniflare = new Miniflare({
1708
+ ...mfConfig,
1709
+ port
1710
+ });
1711
+ await miniflare.ready;
1712
+ const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
1713
+ client = await connectBridgeClientWithRetry(`ws://localhost:${port}`);
1714
+ return {
1715
+ port,
1716
+ client,
1717
+ miniflare,
1718
+ miniflareBindings
1719
+ };
1720
+ } catch (error) {
1721
+ client?.disconnect();
1722
+ if (miniflare) {
1723
+ try {
1724
+ await miniflare.dispose();
1725
+ } catch {}
1726
+ }
1727
+ if (attempt >= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
1728
+ throw error;
1729
+ }
1730
+ await waitForTestContextStartupRetry();
1265
1731
  }
1266
1732
  }
1267
- return null;
1733
+ throw new Error("Bridge-backed test context startup exhausted all retry attempts.");
1268
1734
  }
1269
1735
  async function createTestContext(configPath) {
1270
1736
  const callerDir = getCallerDirectory();
@@ -1275,12 +1741,12 @@ async function createTestContext(configPath) {
1275
1741
  const found = await findNearestConfig(callerDir);
1276
1742
  if (!found) {
1277
1743
  throw new Error(`Could not find a devflare config file. Searched upward from: ${callerDir}
1278
- ` + `Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
1279
- ` + `Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
1744
+ Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
1745
+ Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
1280
1746
  }
1281
1747
  absolutePath = found;
1282
1748
  }
1283
- const configDir6 = dirname2(absolutePath);
1749
+ const configDir6 = dirname4(absolutePath);
1284
1750
  const config = await loadConfig({
1285
1751
  cwd: configDir6,
1286
1752
  configFile: absolutePath.split(/[/\\]/).pop()
@@ -1309,28 +1775,34 @@ async function createTestContext(configPath) {
1309
1775
  }
1310
1776
  const hints = {};
1311
1777
  if (config.bindings?.kv) {
1312
- for (const name of Object.keys(config.bindings.kv))
1778
+ for (const name of Object.keys(config.bindings.kv)) {
1313
1779
  hints[name] = "kv";
1780
+ }
1314
1781
  }
1315
1782
  if (config.bindings?.r2) {
1316
- for (const name of Object.keys(config.bindings.r2))
1783
+ for (const name of Object.keys(config.bindings.r2)) {
1317
1784
  hints[name] = "r2";
1785
+ }
1318
1786
  }
1319
1787
  if (config.bindings?.d1) {
1320
- for (const name of Object.keys(config.bindings.d1))
1788
+ for (const name of Object.keys(config.bindings.d1)) {
1321
1789
  hints[name] = "d1";
1790
+ }
1322
1791
  }
1323
1792
  if (config.bindings?.durableObjects) {
1324
- for (const name of Object.keys(config.bindings.durableObjects))
1793
+ for (const name of Object.keys(config.bindings.durableObjects)) {
1325
1794
  hints[name] = "do";
1795
+ }
1326
1796
  }
1327
1797
  if (config.bindings?.services) {
1328
- for (const name of Object.keys(config.bindings.services))
1798
+ for (const name of Object.keys(config.bindings.services)) {
1329
1799
  hints[name] = "service";
1800
+ }
1330
1801
  }
1331
1802
  if (config.bindings?.sendEmail) {
1332
- for (const name of Object.keys(config.bindings.sendEmail))
1803
+ for (const name of Object.keys(config.bindings.sendEmail)) {
1333
1804
  hints[name] = "sendEmail";
1805
+ }
1334
1806
  }
1335
1807
  const needsMultiWorkerForServices = hasServiceBindings(config);
1336
1808
  const needsMultiWorkerForDOs = hasCrossWorkerDOs(config);
@@ -1343,16 +1815,16 @@ async function createTestContext(configPath) {
1343
1815
  if (needsMultiWorkerForDOs) {
1344
1816
  doBindingResolution = await resolveDOBindings(config, configDir6);
1345
1817
  }
1346
- const randomPort = 1e4 + Math.floor(Math.random() * 50000);
1347
1818
  const localWorkerBindings = config.vars ?? {};
1348
1819
  const mfConfig = {
1349
- modules: true,
1350
- port: randomPort
1820
+ modules: true
1351
1821
  };
1352
- if (config.bindings?.kv)
1822
+ if (config.bindings?.kv) {
1353
1823
  mfConfig.kvNamespaces = Object.keys(config.bindings.kv);
1354
- if (config.bindings?.r2)
1824
+ }
1825
+ if (config.bindings?.r2) {
1355
1826
  mfConfig.r2Buckets = Object.keys(config.bindings.r2);
1827
+ }
1356
1828
  if (config.bindings?.d1) {
1357
1829
  mfConfig.d1Databases = Object.fromEntries(Object.entries(config.bindings.d1).map(([bindingName, bindingConfig]) => {
1358
1830
  return [bindingName, getLocalD1DatabaseIdentifier(bindingConfig)];
@@ -1386,7 +1858,7 @@ async function createTestContext(configPath) {
1386
1858
  }
1387
1859
  const transportFile = resolveTransportFile(configDir6, config.files?.transport);
1388
1860
  if (transportFile) {
1389
- const transportPath = join7(configDir6, transportFile);
1861
+ const transportPath = join9(configDir6, transportFile);
1390
1862
  const transportModule = await import(transportPath);
1391
1863
  if (!transportModule.transport) {
1392
1864
  console.warn(`[devflare] Warning: Transport file "${transportFile}" does not export a named "transport" object.
@@ -1400,86 +1872,10 @@ Transport encoding/decoding will be disabled.`);
1400
1872
  }
1401
1873
  }
1402
1874
  }
1403
- if (config.bindings?.durableObjects) {
1404
- const doConfig = {};
1405
- const doInfos = [];
1406
- const classToFilePath = new Map;
1407
- const doPatternConfig = config.files?.durableObjects;
1408
- const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
1409
- if (doPatternConfig !== false) {
1410
- const fs = await import("fs/promises");
1411
- const doFiles = await findFiles(doPattern, { cwd: configDir6 });
1412
- for (const filePath of doFiles) {
1413
- try {
1414
- const code = await fs.readFile(filePath, "utf-8");
1415
- const classNames = findExportedClasses(code);
1416
- for (const className of classNames) {
1417
- classToFilePath.set(className, filePath);
1418
- }
1419
- } catch {}
1420
- }
1421
- }
1422
- for (const [name, rawDoInfo] of Object.entries(config.bindings.durableObjects)) {
1423
- const doInfo = normalizeDOBinding(rawDoInfo);
1424
- if (doInfo.__ref) {
1425
- continue;
1426
- }
1427
- let scriptPath;
1428
- if (doInfo.scriptName) {
1429
- scriptPath = join7(configDir6, "src", doInfo.scriptName);
1430
- } else {
1431
- const discoveredPath = classToFilePath.get(doInfo.className);
1432
- if (!discoveredPath) {
1433
- throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
1434
- Either:
1435
- 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
1436
- 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
1437
- }
1438
- scriptPath = discoveredPath;
1439
- }
1440
- doConfig[name] = doInfo.className;
1441
- doInfos.push({ name, className: doInfo.className, scriptPath });
1442
- }
1443
- const virtualImports = [];
1444
- const virtualExports = [];
1445
- if (transportFile) {
1446
- const transportPath = join7(configDir6, transportFile);
1447
- virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
1448
- virtualExports.push("export { transport }");
1449
- }
1450
- for (const info of doInfos) {
1451
- virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
1452
- virtualExports.push(`export { ${info.className} }`);
1453
- }
1454
- let bundledCode = "";
1455
- if (virtualImports.length > 0) {
1456
- const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
1457
- `);
1458
- const virtualPath = join7(configDir6, ".devflare", "__test_entry.ts");
1459
- const { writeFileSync, mkdirSync } = await import("fs");
1460
- mkdirSync(dirname2(virtualPath), { recursive: true });
1461
- writeFileSync(virtualPath, virtualEntry);
1462
- const bun = getBunRuntime2();
1463
- if (!bun) {
1464
- throw new Error("Bun runtime is required for createTestContext with Durable Objects");
1465
- }
1466
- const result = await bun.build({
1467
- entrypoints: [virtualPath],
1468
- target: "browser",
1469
- format: "esm",
1470
- minify: false,
1471
- external: ["cloudflare:workers", "cloudflare:*"]
1472
- });
1473
- if (!result.success) {
1474
- throw new Error(`Failed to bundle test entry: ${result.logs.join(`
1475
- `)}`);
1476
- }
1477
- bundledCode = await result.outputs[0].text();
1478
- }
1479
- mfConfig.durableObjects = doConfig;
1480
- mfConfig.script = buildGatewayScript(bundledCode, "");
1481
- } else {
1482
- mfConfig.script = buildGatewayScript("", "");
1875
+ const gateway = await buildDurableObjectGateway(config, configDir6, transportFile);
1876
+ mfConfig.script = gateway.script;
1877
+ if (gateway.durableObjects) {
1878
+ mfConfig.durableObjects = gateway.durableObjects;
1483
1879
  }
1484
1880
  const hasMultiWorkerServices = serviceBindingResolution && serviceBindingResolution.workers.length > 0;
1485
1881
  const hasMultiWorkerDOs = doBindingResolution && doBindingResolution.workers.length > 0;
@@ -1508,14 +1904,14 @@ Either:
1508
1904
  for (const worker2 of additionalWorkers) {
1509
1905
  if (!workersByName.has(worker2.name)) {
1510
1906
  workersByName.set(worker2.name, worker2);
1511
- } else {
1512
- const existing = workersByName.get(worker2.name);
1513
- if (worker2.durableObjects) {
1514
- existing.durableObjects = {
1515
- ...existing.durableObjects || {},
1516
- ...worker2.durableObjects
1517
- };
1518
- }
1907
+ continue;
1908
+ }
1909
+ const existing = workersByName.get(worker2.name);
1910
+ if (worker2.durableObjects) {
1911
+ existing.durableObjects = {
1912
+ ...existing.durableObjects || {},
1913
+ ...worker2.durableObjects
1914
+ };
1519
1915
  }
1520
1916
  }
1521
1917
  const workers = [primaryWorker, ...workersByName.values()];
@@ -1527,10 +1923,23 @@ Either:
1527
1923
  delete mfConfig.durableObjects;
1528
1924
  mfConfig.workers = workers;
1529
1925
  }
1530
- const { Miniflare } = await import("miniflare");
1531
- globalMiniflare = new Miniflare(mfConfig);
1532
- await globalMiniflare.ready;
1533
- globalMiniflareBindings = wrapEnvSendEmailBindings(await globalMiniflare.getBindings());
1926
+ let activePort;
1927
+ if (hasMultiWorkerServices || hasMultiWorkerDOs) {
1928
+ const { Miniflare } = await import("miniflare");
1929
+ activePort = await getAvailablePort();
1930
+ globalMiniflare = new Miniflare({
1931
+ ...mfConfig,
1932
+ port: activePort
1933
+ });
1934
+ await globalMiniflare.ready;
1935
+ globalMiniflareBindings = wrapEnvSendEmailBindings(await globalMiniflare.getBindings());
1936
+ } else {
1937
+ const startedBridgeBackedTestContext = await startBridgeBackedTestContext(mfConfig);
1938
+ activePort = startedBridgeBackedTestContext.port;
1939
+ globalMiniflare = startedBridgeBackedTestContext.miniflare;
1940
+ globalMiniflareBindings = startedBridgeBackedTestContext.miniflareBindings;
1941
+ globalClient = startedBridgeBackedTestContext.client;
1942
+ }
1534
1943
  const disposeContext = async () => {
1535
1944
  if (globalClient) {
1536
1945
  await globalClient.disconnect();
@@ -1583,11 +1992,13 @@ Either:
1583
1992
  const DEFAULT_EMAIL_PATH = "src/email.ts";
1584
1993
  const DEFAULT_TAIL_PATH = "src/tail.ts";
1585
1994
  const resolvePath = async (configValue, defaultPath) => {
1586
- if (typeof configValue === "string")
1995
+ if (typeof configValue === "string") {
1587
1996
  return configValue;
1588
- if (configValue === false)
1997
+ }
1998
+ if (configValue === false) {
1589
1999
  return null;
1590
- const defaultAbsolute = join7(configDir6, defaultPath);
2000
+ }
2001
+ const defaultAbsolute = join9(configDir6, defaultPath);
1591
2002
  try {
1592
2003
  const fs = await import("fs/promises");
1593
2004
  await fs.access(defaultAbsolute);
@@ -1630,7 +2041,7 @@ Either:
1630
2041
  getEnv: getTestEnv
1631
2042
  });
1632
2043
  configureEmail({
1633
- port: randomPort,
2044
+ port: activePort,
1634
2045
  handlerPath: resolvedEmailPath,
1635
2046
  configDir: configDir6,
1636
2047
  getEnv: getTestEnv
@@ -1654,29 +2065,31 @@ Either:
1654
2065
  __setTestContext(envAccessor2, disposeContext);
1655
2066
  return;
1656
2067
  }
1657
- globalClient = new BridgeClient({
1658
- url: `ws://localhost:${randomPort}`
1659
- });
1660
- await globalClient.connect();
2068
+ const bridgeClient = globalClient;
2069
+ if (!bridgeClient) {
2070
+ throw new Error("Bridge-backed test context did not initialize a client.");
2071
+ }
1661
2072
  setBindingHints(hints);
1662
2073
  globalEnvProxy = createEnvProxy({
1663
- client: globalClient,
2074
+ client: bridgeClient,
1664
2075
  transformResult: (result) => decodeTransport(result)
1665
2076
  });
1666
2077
  const envAccessor = new Proxy({}, {
1667
2078
  get(_, prop) {
2079
+ const hint = hints[prop];
2080
+ const prefersBridgeBinding = shouldPreferBridgeBinding(hint);
1668
2081
  if (globalRemoteBindings && prop in globalRemoteBindings) {
1669
2082
  return globalRemoteBindings[prop];
1670
2083
  }
1671
- if (hints[prop] === "sendEmail" && globalEnvProxy && prop in globalEnvProxy) {
1672
- return globalEnvProxy[prop];
1673
- }
1674
- if (globalMiniflareBindings && prop in globalMiniflareBindings) {
2084
+ if (!prefersBridgeBinding && globalMiniflareBindings && prop in globalMiniflareBindings) {
1675
2085
  return globalMiniflareBindings[prop];
1676
2086
  }
1677
2087
  if (globalEnvProxy) {
1678
2088
  return globalEnvProxy[prop];
1679
2089
  }
2090
+ if (prefersBridgeBinding && globalMiniflareBindings && prop in globalMiniflareBindings) {
2091
+ return globalMiniflareBindings[prop];
2092
+ }
1680
2093
  return;
1681
2094
  },
1682
2095
  has(_, prop) {
@@ -1705,184 +2118,6 @@ function decodeTransport(value) {
1705
2118
  }
1706
2119
  return result;
1707
2120
  }
1708
- function buildGatewayScript(bundledCode, wrappers) {
1709
- return `
1710
- // Bundled transport + DO classes
1711
- ${bundledCode}
1712
-
1713
- // DO Wrappers with RPC
1714
- ${wrappers}
1715
-
1716
- // Transport encoding helper
1717
- const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
1718
-
1719
- function __encodeTransport(value) {
1720
- if (value === null || value === undefined) return value
1721
-
1722
- // Try each encoder
1723
- for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
1724
- const encoded = transporter.encode(value)
1725
- if (encoded !== false && encoded !== undefined) {
1726
- return { __transport: typeName, value: encoded }
1727
- }
1728
- }
1729
-
1730
- // Recursively encode arrays and objects
1731
- if (Array.isArray(value)) {
1732
- return value.map(__encodeTransport)
1733
- }
1734
- if (typeof value === 'object') {
1735
- const result = {}
1736
- for (const [k, v] of Object.entries(value)) {
1737
- result[k] = __encodeTransport(v)
1738
- }
1739
- return result
1740
- }
1741
-
1742
- return value
1743
- }
1744
-
1745
- // Gateway with WebSocket RPC
1746
- export default {
1747
- async fetch(request, env) {
1748
- if (request.headers.get('Upgrade') === 'websocket') {
1749
- const { 0: client, 1: server } = new WebSocketPair()
1750
- server.accept()
1751
- server.addEventListener('message', async (e) => {
1752
- try {
1753
- const m = JSON.parse(e.data)
1754
- if (m.t === 'rpc.call') {
1755
- const result = await executeRpc(env, m.method, m.params)
1756
- server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
1757
- }
1758
- } catch (error) {
1759
- server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
1760
- }
1761
- })
1762
- return new Response(null, { status: 101, webSocket: client })
1763
- }
1764
- return new Response('Gateway')
1765
- }
1766
- }
1767
-
1768
- async function executeRpc(env, method, params) {
1769
- const [bindingName, ...rest] = method.split('.')
1770
- const op = rest.join('.')
1771
- const binding = env[bindingName]
1772
- const RAW_EMAIL = 'EmailMessage::raw'
1773
- if (!binding) throw new Error('Binding not found: ' + bindingName)
1774
-
1775
- // KV operations
1776
- if (op === 'get') return binding.get(params[0], params[1])
1777
- if (op === 'put') return binding.put(params[0], params[1], params[2])
1778
- if (op === 'delete') return binding.delete(params[0])
1779
- if (op === 'list') return binding.list(params[0])
1780
- if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
1781
-
1782
- // R2 operations
1783
- if (op === 'r2.get') return binding.get(params[0], params[1])
1784
- if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
1785
- if (op === 'r2.delete') return binding.delete(params[0])
1786
- if (op === 'r2.list') return binding.list(params[0])
1787
- if (op === 'head') return binding.head(params[0])
1788
-
1789
- // D1 operations
1790
- if (op === 'exec') return binding.exec(params[0])
1791
- if (op === 'dump') return binding.dump()
1792
- if (op === 'batch') {
1793
- const stmts = params[0].map(s => {
1794
- const stmt = binding.prepare(s.sql)
1795
- return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
1796
- })
1797
- return binding.batch(stmts)
1798
- }
1799
- if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
1800
- if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
1801
- if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
1802
- if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
1803
-
1804
- // Send email operations
1805
- if (op === 'email.send') {
1806
- return binding.send(__normalizeEmailMessage(params[0]))
1807
- }
1808
-
1809
- // DO operations
1810
- if (op === 'idFromName') {
1811
- return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
1812
- }
1813
- if (op === 'stub.rpc') {
1814
- const [, idSerialized, rpcMethod, rpcParams] = params
1815
- const stub = binding.get(binding.idFromString(idSerialized.hex))
1816
-
1817
- // Use native RPC: call method directly on stub
1818
- // This works for DOs extending DurableObject from cloudflare:workers
1819
- let result = await stub[rpcMethod](...(rpcParams || []))
1820
- // Apply transport encoding
1821
- result = __encodeTransport(result)
1822
- return result
1823
- }
1824
-
1825
- throw new Error('Unknown operation: ' + method)
1826
- }
1827
-
1828
- function __createEmailMessageRaw(raw) {
1829
- if (typeof raw === 'string' || raw instanceof ReadableStream) {
1830
- return raw
1831
- }
1832
- if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
1833
- return new Response(raw).body
1834
- }
1835
- throw new Error('Unsupported EmailMessage raw payload')
1836
- }
1837
-
1838
- function __buildRawEmail(message) {
1839
- const lines = []
1840
- const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
1841
-
1842
- lines.push('From: ' + message.from)
1843
- lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
1844
- lines.push('Date: ' + new Date().toUTCString())
1845
- lines.push('Message-ID: ' + messageId)
1846
-
1847
- if (message.subject) lines.push('Subject: ' + message.subject)
1848
- if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
1849
- if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
1850
- if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
1851
-
1852
- for (const [key, value] of Object.entries(message.headers || {})) {
1853
- lines.push(key + ': ' + value)
1854
- }
1855
-
1856
- lines.push('MIME-Version: 1.0')
1857
- lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
1858
- lines.push('')
1859
- lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
1860
-
1861
- return lines.join('\\r\\n')
1862
- }
1863
-
1864
- function __normalizeEmailMessage(message) {
1865
- if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
1866
- return message
1867
- }
1868
- if ('EmailMessage::raw' in message) {
1869
- return message
1870
- }
1871
- if ('raw' in message && message.raw !== undefined) {
1872
- return {
1873
- from: message.from,
1874
- to: message.to,
1875
- [RAW_EMAIL]: __createEmailMessageRaw(message.raw)
1876
- }
1877
- }
1878
- return {
1879
- from: message.from,
1880
- to: message.to,
1881
- [RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
1882
- }
1883
- }
1884
- `;
1885
- }
1886
2121
  // src/test/cf.ts
1887
2122
  var cf = {
1888
2123
  email,
@@ -1891,71 +2126,11 @@ var cf = {
1891
2126
  worker,
1892
2127
  tail
1893
2128
  };
1894
- // src/test/multi-worker-context.ts
1895
- async function createMultiWorkerContext(options) {
1896
- const { workers, primary } = options;
1897
- const primaryName = primary ?? workers[0]?.name;
1898
- if (!primaryName) {
1899
- throw new Error("At least one worker must be configured");
1900
- }
1901
- const { Miniflare } = await import("miniflare");
1902
- const mfWorkers = workers.map((worker2) => {
1903
- const serviceBindings = {};
1904
- if (worker2.serviceBindings) {
1905
- for (const [bindingName, target] of Object.entries(worker2.serviceBindings)) {
1906
- if (typeof target === "string") {
1907
- serviceBindings[bindingName] = { name: target };
1908
- } else {
1909
- serviceBindings[bindingName] = target;
1910
- }
1911
- }
1912
- }
1913
- return {
1914
- name: worker2.name,
1915
- modules: worker2.modules ?? true,
1916
- script: worker2.script,
1917
- compatibilityDate: worker2.compatibilityDate ?? "2025-01-01",
1918
- ...Object.keys(serviceBindings).length > 0 ? { serviceBindings } : {}
1919
- };
1920
- });
1921
- const mf = new Miniflare({
1922
- workers: mfWorkers
1923
- });
1924
- const env2 = await mf.getBindings();
1925
- const fetch3 = async (input, init) => {
1926
- const response = await mf.dispatchFetch(input, init);
1927
- return response;
1928
- };
1929
- const dispose = async () => {
1930
- await new Promise((r) => setTimeout(r, 50));
1931
- await mf.dispose();
1932
- };
1933
- return {
1934
- mf,
1935
- env: env2,
1936
- fetch: fetch3,
1937
- dispose
1938
- };
1939
- }
1940
- function createEntrypointScript(className, methods) {
1941
- const methodDefs = Object.entries(methods).map(([name, body]) => ` ${name}${body}`).join(`
1942
-
1943
- `);
1944
- return `import { WorkerEntrypoint } from 'cloudflare:workers'
1945
-
1946
- export class ${className} extends WorkerEntrypoint {
1947
- ${methodDefs}
1948
- }
1949
- `;
1950
- }
1951
2129
  // src/test/should-skip.ts
1952
2130
  var REMOTE_ONLY_SERVICES = new Set([
1953
2131
  "ai",
1954
2132
  "vectorize"
1955
2133
  ]);
1956
- function isRemoteModeEnabled() {
1957
- return isRemoteModeActive();
1958
- }
1959
2134
  var skipResults = new Map;
1960
2135
  var EXPECTED_ERROR_PATTERNS = [
1961
2136
  "ECONNREFUSED",
@@ -2471,4 +2646,4 @@ var testEnv = new Proxy({}, {
2471
2646
  function isKVNamespace(binding) {
2472
2647
  return typeof binding === "object" && binding !== null && "get" in binding && "put" in binding && "delete" in binding && "list" in binding;
2473
2648
  }
2474
- export { clearBundleCache, hasServiceBindings, resolveServiceBindings, hasCrossWorkerDOs, resolveDOBindings, queue, scheduled, worker, tail, email, createTestContext, cf, createMultiWorkerContext, createEntrypointScript, isRemoteModeEnabled, shouldSkip, createMockTestContext, withTestContext, createMockKV, createMockD1, createMockR2, createMockQueue, createMockEnv, createBridgeTestContext, stopBridgeTestContext, getBridgeTestContext, testEnv };
2649
+ export { clearBundleCache, hasServiceBindings, resolveServiceBindings, hasCrossWorkerDOs, resolveDOBindings, email, queue, scheduled, tail, worker, createTestContext, cf, shouldSkip, createMockTestContext, withTestContext, createMockKV, createMockD1, createMockR2, createMockQueue, createMockEnv, createBridgeTestContext, stopBridgeTestContext, getBridgeTestContext, testEnv };