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