devflare 1.0.0-next.3 → 1.0.0-next.31

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 (537) hide show
  1. package/LLM.md +15964 -1751
  2. package/README.md +392 -605
  3. package/bin/devflare.js +24 -14
  4. package/dist/account-g99mzrqp.js +475 -0
  5. package/dist/api-2n6dd2ws.js +33 -0
  6. package/dist/bridge/client.d.ts +48 -6
  7. package/dist/bridge/client.d.ts.map +1 -1
  8. package/dist/bridge/gateway-runtime.d.ts +8 -0
  9. package/dist/bridge/gateway-runtime.d.ts.map +1 -0
  10. package/dist/bridge/index.d.ts +3 -3
  11. package/dist/bridge/index.d.ts.map +1 -1
  12. package/dist/bridge/log.d.ts +5 -0
  13. package/dist/bridge/log.d.ts.map +1 -0
  14. package/dist/bridge/miniflare.d.ts +74 -1
  15. package/dist/bridge/miniflare.d.ts.map +1 -1
  16. package/dist/bridge/proxy.d.ts +11 -10
  17. package/dist/bridge/proxy.d.ts.map +1 -1
  18. package/dist/bridge/server.d.ts +2 -1
  19. package/dist/bridge/server.d.ts.map +1 -1
  20. package/dist/bridge/v2/body-streams.d.ts +53 -0
  21. package/dist/bridge/v2/body-streams.d.ts.map +1 -0
  22. package/dist/bridge/v2/codec.d.ts +106 -0
  23. package/dist/bridge/v2/codec.d.ts.map +1 -0
  24. package/dist/bridge/v2/control-messages.d.ts +51 -0
  25. package/dist/bridge/v2/control-messages.d.ts.map +1 -0
  26. package/dist/bridge/v2/frames.d.ts +109 -0
  27. package/dist/bridge/v2/frames.d.ts.map +1 -0
  28. package/dist/bridge/v2/index.d.ts +11 -0
  29. package/dist/bridge/v2/index.d.ts.map +1 -0
  30. package/dist/bridge/v2/serialization.d.ts +48 -0
  31. package/dist/bridge/v2/serialization.d.ts.map +1 -0
  32. package/dist/bridge/v2/transport.d.ts +31 -0
  33. package/dist/bridge/v2/transport.d.ts.map +1 -0
  34. package/dist/bridge/v2/value-codec.d.ts +61 -0
  35. package/dist/bridge/v2/value-codec.d.ts.map +1 -0
  36. package/dist/bridge/{serialization.d.ts → v2/value-serialization.d.ts} +40 -18
  37. package/dist/bridge/v2/value-serialization.d.ts.map +1 -0
  38. package/dist/bridge/{protocol.d.ts → v2/wire.d.ts} +28 -4
  39. package/dist/bridge/v2/wire.d.ts.map +1 -0
  40. package/dist/bridge/v2/ws-relay.d.ts +53 -0
  41. package/dist/bridge/v2/ws-relay.d.ts.map +1 -0
  42. package/dist/browser-shim/handler.d.ts +1 -1
  43. package/dist/browser-shim/handler.d.ts.map +1 -1
  44. package/dist/browser-shim/server.d.ts +63 -0
  45. package/dist/browser-shim/server.d.ts.map +1 -1
  46. package/dist/browser.d.ts +4621 -0
  47. package/dist/browser.d.ts.map +1 -0
  48. package/dist/browser.js +147 -0
  49. package/dist/build-4tyjywn9.js +54 -0
  50. package/dist/bundler/defaults.d.ts +23 -0
  51. package/dist/bundler/defaults.d.ts.map +1 -0
  52. package/dist/bundler/do-bundler.d.ts +7 -0
  53. package/dist/bundler/do-bundler.d.ts.map +1 -1
  54. package/dist/bundler/index.d.ts +2 -0
  55. package/dist/bundler/index.d.ts.map +1 -1
  56. package/dist/bundler/rolldown-shared.d.ts +53 -0
  57. package/dist/bundler/rolldown-shared.d.ts.map +1 -0
  58. package/dist/bundler/worker-bundler.d.ts +13 -0
  59. package/dist/bundler/worker-bundler.d.ts.map +1 -0
  60. package/dist/bundler/worker-compat.d.ts +4 -0
  61. package/dist/bundler/worker-compat.d.ts.map +1 -0
  62. package/dist/cli/build-manifest.d.ts +66 -0
  63. package/dist/cli/build-manifest.d.ts.map +1 -0
  64. package/dist/cli/command-utils.d.ts +18 -0
  65. package/dist/cli/command-utils.d.ts.map +1 -0
  66. package/dist/cli/commands/account.d.ts +1 -1
  67. package/dist/cli/commands/account.d.ts.map +1 -1
  68. package/dist/cli/commands/build-artifacts.d.ts +40 -0
  69. package/dist/cli/commands/build-artifacts.d.ts.map +1 -0
  70. package/dist/cli/commands/build.d.ts.map +1 -1
  71. package/dist/cli/commands/config.d.ts +4 -0
  72. package/dist/cli/commands/config.d.ts.map +1 -0
  73. package/dist/cli/commands/deploy/metadata.d.ts +18 -0
  74. package/dist/cli/commands/deploy/metadata.d.ts.map +1 -0
  75. package/dist/cli/commands/deploy/prepare.d.ts +23 -0
  76. package/dist/cli/commands/deploy/prepare.d.ts.map +1 -0
  77. package/dist/cli/commands/deploy/runtime.d.ts +4 -0
  78. package/dist/cli/commands/deploy/runtime.d.ts.map +1 -0
  79. package/dist/cli/commands/deploy/verification.d.ts +36 -0
  80. package/dist/cli/commands/deploy/verification.d.ts.map +1 -0
  81. package/dist/cli/commands/deploy.d.ts +2 -2
  82. package/dist/cli/commands/deploy.d.ts.map +1 -1
  83. package/dist/cli/commands/dev.d.ts +8 -0
  84. package/dist/cli/commands/dev.d.ts.map +1 -1
  85. package/dist/cli/commands/doctor.d.ts.map +1 -1
  86. package/dist/cli/commands/init.d.ts.map +1 -1
  87. package/dist/cli/commands/login.d.ts +4 -0
  88. package/dist/cli/commands/login.d.ts.map +1 -0
  89. package/dist/cli/commands/previews-support/cleanup.d.ts +9 -0
  90. package/dist/cli/commands/previews-support/cleanup.d.ts.map +1 -0
  91. package/dist/cli/commands/previews-support/family.d.ts +10 -0
  92. package/dist/cli/commands/previews-support/family.d.ts.map +1 -0
  93. package/dist/cli/commands/previews-support/render.d.ts +8 -0
  94. package/dist/cli/commands/previews-support/render.d.ts.map +1 -0
  95. package/dist/cli/commands/previews-support/theme.d.ts +10 -0
  96. package/dist/cli/commands/previews-support/theme.d.ts.map +1 -0
  97. package/dist/cli/commands/previews-support/types.d.ts +70 -0
  98. package/dist/cli/commands/previews-support/types.d.ts.map +1 -0
  99. package/dist/cli/commands/previews.d.ts +4 -0
  100. package/dist/cli/commands/previews.d.ts.map +1 -0
  101. package/dist/cli/commands/productions.d.ts +4 -0
  102. package/dist/cli/commands/productions.d.ts.map +1 -0
  103. package/dist/cli/commands/secrets.d.ts +4 -0
  104. package/dist/cli/commands/secrets.d.ts.map +1 -0
  105. package/dist/cli/commands/token.d.ts +4 -0
  106. package/dist/cli/commands/token.d.ts.map +1 -0
  107. package/dist/cli/commands/type-generation/discovery.d.ts +7 -0
  108. package/dist/cli/commands/type-generation/discovery.d.ts.map +1 -0
  109. package/dist/cli/commands/type-generation/generator.d.ts +112 -0
  110. package/dist/cli/commands/type-generation/generator.d.ts.map +1 -0
  111. package/dist/cli/commands/type-generation/models.d.ts +27 -0
  112. package/dist/cli/commands/type-generation/models.d.ts.map +1 -0
  113. package/dist/cli/commands/types.d.ts.map +1 -1
  114. package/dist/cli/commands/worker.d.ts +4 -0
  115. package/dist/cli/commands/worker.d.ts.map +1 -0
  116. package/dist/cli/config-path.d.ts +6 -0
  117. package/dist/cli/config-path.d.ts.map +1 -0
  118. package/dist/cli/dependencies.d.ts +1 -0
  119. package/dist/cli/dependencies.d.ts.map +1 -1
  120. package/dist/cli/deploy-strategy.d.ts +17 -0
  121. package/dist/cli/deploy-strategy.d.ts.map +1 -0
  122. package/dist/cli/deploy-target.d.ts +17 -0
  123. package/dist/cli/deploy-target.d.ts.map +1 -0
  124. package/dist/cli/generated-artifacts.d.ts +12 -0
  125. package/dist/cli/generated-artifacts.d.ts.map +1 -0
  126. package/dist/cli/help-pages/pages/account.d.ts +3 -0
  127. package/dist/cli/help-pages/pages/account.d.ts.map +1 -0
  128. package/dist/cli/help-pages/pages/core.d.ts +4 -0
  129. package/dist/cli/help-pages/pages/core.d.ts.map +1 -0
  130. package/dist/cli/help-pages/pages/index.d.ts +3 -0
  131. package/dist/cli/help-pages/pages/index.d.ts.map +1 -0
  132. package/dist/cli/help-pages/pages/misc.d.ts +3 -0
  133. package/dist/cli/help-pages/pages/misc.d.ts.map +1 -0
  134. package/dist/cli/help-pages/pages/previews.d.ts +3 -0
  135. package/dist/cli/help-pages/pages/previews.d.ts.map +1 -0
  136. package/dist/cli/help-pages/pages/productions.d.ts +3 -0
  137. package/dist/cli/help-pages/pages/productions.d.ts.map +1 -0
  138. package/dist/cli/help-pages/render.d.ts +12 -0
  139. package/dist/cli/help-pages/render.d.ts.map +1 -0
  140. package/dist/cli/help-pages/shared.d.ts +15 -0
  141. package/dist/cli/help-pages/shared.d.ts.map +1 -0
  142. package/dist/cli/help-pages/types.d.ts +23 -0
  143. package/dist/cli/help-pages/types.d.ts.map +1 -0
  144. package/dist/cli/help.d.ts +6 -0
  145. package/dist/cli/help.d.ts.map +1 -0
  146. package/dist/cli/index.d.ts +1 -0
  147. package/dist/cli/index.d.ts.map +1 -1
  148. package/dist/cli/index.js +12 -0
  149. package/dist/cli/package-metadata.d.ts +16 -0
  150. package/dist/cli/package-metadata.d.ts.map +1 -0
  151. package/dist/cli/preview-bindings.d.ts +49 -0
  152. package/dist/cli/preview-bindings.d.ts.map +1 -0
  153. package/dist/cli/preview.d.ts +10 -0
  154. package/dist/cli/preview.d.ts.map +1 -0
  155. package/dist/cli/ui.d.ts +37 -0
  156. package/dist/cli/ui.d.ts.map +1 -0
  157. package/dist/cli/workspace-build-guard.d.ts +14 -0
  158. package/dist/cli/workspace-build-guard.d.ts.map +1 -0
  159. package/dist/cloudflare/account-core.d.ts +6 -0
  160. package/dist/cloudflare/account-core.d.ts.map +1 -0
  161. package/dist/cloudflare/account-resources.d.ts +40 -0
  162. package/dist/cloudflare/account-resources.d.ts.map +1 -0
  163. package/dist/cloudflare/account-status.d.ts +11 -0
  164. package/dist/cloudflare/account-status.d.ts.map +1 -0
  165. package/dist/cloudflare/account-workers.d.ts +14 -0
  166. package/dist/cloudflare/account-workers.d.ts.map +1 -0
  167. package/dist/cloudflare/account.d.ts +7 -64
  168. package/dist/cloudflare/account.d.ts.map +1 -1
  169. package/dist/cloudflare/api.d.ts +71 -3
  170. package/dist/cloudflare/api.d.ts.map +1 -1
  171. package/dist/cloudflare/auth.d.ts.map +1 -1
  172. package/dist/cloudflare/index.d.ts +57 -2
  173. package/dist/cloudflare/index.d.ts.map +1 -1
  174. package/dist/cloudflare/index.js +49 -2
  175. package/dist/cloudflare/known-permission-group-ids.generated.d.ts +10 -0
  176. package/dist/cloudflare/known-permission-group-ids.generated.d.ts.map +1 -0
  177. package/dist/cloudflare/kv-namespace.d.ts +4 -0
  178. package/dist/cloudflare/kv-namespace.d.ts.map +1 -0
  179. package/dist/cloudflare/preferences.d.ts +7 -0
  180. package/dist/cloudflare/preferences.d.ts.map +1 -1
  181. package/dist/cloudflare/preview-registry-cache.d.ts +6 -0
  182. package/dist/cloudflare/preview-registry-cache.d.ts.map +1 -0
  183. package/dist/cloudflare/preview-registry-inference.d.ts +14 -0
  184. package/dist/cloudflare/preview-registry-inference.d.ts.map +1 -0
  185. package/dist/cloudflare/preview-registry-records.d.ts +4 -0
  186. package/dist/cloudflare/preview-registry-records.d.ts.map +1 -0
  187. package/dist/cloudflare/preview-registry-shape.d.ts +47 -0
  188. package/dist/cloudflare/preview-registry-shape.d.ts.map +1 -0
  189. package/dist/cloudflare/preview-registry-store.d.ts +14 -0
  190. package/dist/cloudflare/preview-registry-store.d.ts.map +1 -0
  191. package/dist/cloudflare/preview-registry-transport.d.ts +4 -0
  192. package/dist/cloudflare/preview-registry-transport.d.ts.map +1 -0
  193. package/dist/cloudflare/preview-registry-types.d.ts +103 -0
  194. package/dist/cloudflare/preview-registry-types.d.ts.map +1 -0
  195. package/dist/cloudflare/preview-registry.d.ts +42 -0
  196. package/dist/cloudflare/preview-registry.d.ts.map +1 -0
  197. package/dist/cloudflare/preview-urls.d.ts +3 -0
  198. package/dist/cloudflare/preview-urls.d.ts.map +1 -0
  199. package/dist/cloudflare/registry-schema.d.ts +253 -0
  200. package/dist/cloudflare/registry-schema.d.ts.map +1 -0
  201. package/dist/cloudflare/tokens.d.ts +44 -0
  202. package/dist/cloudflare/tokens.d.ts.map +1 -0
  203. package/dist/cloudflare/types.d.ts +124 -7
  204. package/dist/cloudflare/types.d.ts.map +1 -1
  205. package/dist/cloudflare/usage.d.ts +30 -2
  206. package/dist/cloudflare/usage.d.ts.map +1 -1
  207. package/dist/config/binding-resolution-helpers.d.ts +41 -0
  208. package/dist/config/binding-resolution-helpers.d.ts.map +1 -0
  209. package/dist/config/compatibility.d.ts +3 -0
  210. package/dist/config/compatibility.d.ts.map +1 -0
  211. package/dist/config/compiler/bindings.d.ts +14 -0
  212. package/dist/config/compiler/bindings.d.ts.map +1 -0
  213. package/dist/config/compiler/core-helpers.d.ts +6 -0
  214. package/dist/config/compiler/core-helpers.d.ts.map +1 -0
  215. package/dist/config/compiler/do-workers.d.ts +34 -0
  216. package/dist/config/compiler/do-workers.d.ts.map +1 -0
  217. package/dist/config/compiler/paths.d.ts +18 -0
  218. package/dist/config/compiler/paths.d.ts.map +1 -0
  219. package/dist/config/compiler/types.d.ts +270 -0
  220. package/dist/config/compiler/types.d.ts.map +1 -0
  221. package/dist/config/compiler.d.ts +22 -134
  222. package/dist/config/compiler.d.ts.map +1 -1
  223. package/dist/config/define.d.ts +7 -4
  224. package/dist/config/define.d.ts.map +1 -1
  225. package/dist/config/deploy-resources.d.ts +58 -0
  226. package/dist/config/deploy-resources.d.ts.map +1 -0
  227. package/dist/config/env-vars.d.ts +309 -0
  228. package/dist/config/env-vars.d.ts.map +1 -0
  229. package/dist/config/framework-providers.d.ts +9 -0
  230. package/dist/config/framework-providers.d.ts.map +1 -0
  231. package/dist/config/index.d.ts +12 -5
  232. package/dist/config/index.d.ts.map +1 -1
  233. package/dist/config/loader.d.ts +1 -0
  234. package/dist/config/loader.d.ts.map +1 -1
  235. package/dist/config/local-dev-vars.d.ts +15 -0
  236. package/dist/config/local-dev-vars.d.ts.map +1 -0
  237. package/dist/config/preview-resources.d.ts +83 -0
  238. package/dist/config/preview-resources.d.ts.map +1 -0
  239. package/dist/config/preview.d.ts +31 -0
  240. package/dist/config/preview.d.ts.map +1 -0
  241. package/dist/config/ref.d.ts +14 -23
  242. package/dist/config/ref.d.ts.map +1 -1
  243. package/dist/config/resolve-phased.d.ts +72 -0
  244. package/dist/config/resolve-phased.d.ts.map +1 -0
  245. package/dist/config/resolve.d.ts +11 -0
  246. package/dist/config/resolve.d.ts.map +1 -0
  247. package/dist/config/resource-resolution.d.ts +80 -0
  248. package/dist/config/resource-resolution.d.ts.map +1 -0
  249. package/dist/config/schema-bindings.d.ts +1495 -0
  250. package/dist/config/schema-bindings.d.ts.map +1 -0
  251. package/dist/config/schema-build.d.ts +67 -0
  252. package/dist/config/schema-build.d.ts.map +1 -0
  253. package/dist/config/schema-env.d.ts +3023 -0
  254. package/dist/config/schema-env.d.ts.map +1 -0
  255. package/dist/config/schema-normalization.d.ts +184 -0
  256. package/dist/config/schema-normalization.d.ts.map +1 -0
  257. package/dist/config/schema-runtime.d.ts +500 -0
  258. package/dist/config/schema-runtime.d.ts.map +1 -0
  259. package/dist/config/schema-types-bindings-platform.d.ts +378 -0
  260. package/dist/config/schema-types-bindings-platform.d.ts.map +1 -0
  261. package/dist/config/schema-types-bindings-resources.d.ts +551 -0
  262. package/dist/config/schema-types-bindings-resources.d.ts.map +1 -0
  263. package/dist/config/schema-types-bindings.d.ts +254 -0
  264. package/dist/config/schema-types-bindings.d.ts.map +1 -0
  265. package/dist/config/schema-types-build.d.ts +86 -0
  266. package/dist/config/schema-types-build.d.ts.map +1 -0
  267. package/dist/config/schema-types-runtime.d.ts +917 -0
  268. package/dist/config/schema-types-runtime.d.ts.map +1 -0
  269. package/dist/config/schema-types.d.ts +424 -0
  270. package/dist/config/schema-types.d.ts.map +1 -0
  271. package/dist/config/schema.d.ts +4537 -2214
  272. package/dist/config/schema.d.ts.map +1 -1
  273. package/dist/config/service-bindings-validation.d.ts +36 -0
  274. package/dist/config/service-bindings-validation.d.ts.map +1 -0
  275. package/dist/config-88sdhx3w.js +105 -0
  276. package/dist/config-entry.d.ts +7 -0
  277. package/dist/config-entry.d.ts.map +1 -0
  278. package/dist/config-entry.js +16 -0
  279. package/dist/decorators/durable-object.d.ts.map +1 -1
  280. package/dist/deploy-8fpav2cw.js +1066 -0
  281. package/dist/dev-nxevkfdv.js +2609 -0
  282. package/dist/dev-server/d1-migrations.d.ts +36 -0
  283. package/dist/dev-server/d1-migrations.d.ts.map +1 -0
  284. package/dist/dev-server/dev-server-state.d.ts +59 -0
  285. package/dist/dev-server/dev-server-state.d.ts.map +1 -0
  286. package/dist/dev-server/gateway-script.d.ts +21 -0
  287. package/dist/dev-server/gateway-script.d.ts.map +1 -0
  288. package/dist/dev-server/miniflare-bindings.d.ts +59 -0
  289. package/dist/dev-server/miniflare-bindings.d.ts.map +1 -0
  290. package/dist/dev-server/miniflare-dev-config.d.ts +36 -0
  291. package/dist/dev-server/miniflare-dev-config.d.ts.map +1 -0
  292. package/dist/dev-server/miniflare-log.d.ts +20 -0
  293. package/dist/dev-server/miniflare-log.d.ts.map +1 -0
  294. package/dist/dev-server/miniflare-worker-config.d.ts +74 -0
  295. package/dist/dev-server/miniflare-worker-config.d.ts.map +1 -0
  296. package/dist/dev-server/reload-queue.d.ts +13 -0
  297. package/dist/dev-server/reload-queue.d.ts.map +1 -0
  298. package/dist/dev-server/runtime-stdio.d.ts +8 -0
  299. package/dist/dev-server/runtime-stdio.d.ts.map +1 -0
  300. package/dist/dev-server/server-startup-helpers.d.ts +84 -0
  301. package/dist/dev-server/server-startup-helpers.d.ts.map +1 -0
  302. package/dist/dev-server/server.d.ts +2 -0
  303. package/dist/dev-server/server.d.ts.map +1 -1
  304. package/dist/dev-server/vite-process.d.ts +15 -0
  305. package/dist/dev-server/vite-process.d.ts.map +1 -0
  306. package/dist/dev-server/vite-utils.d.ts +19 -1
  307. package/dist/dev-server/vite-utils.d.ts.map +1 -1
  308. package/dist/dev-server/worker-source-watcher.d.ts +17 -0
  309. package/dist/dev-server/worker-source-watcher.d.ts.map +1 -0
  310. package/dist/dev-server/worker-surface-paths.d.ts +6 -0
  311. package/dist/dev-server/worker-surface-paths.d.ts.map +1 -0
  312. package/dist/{doctor-fmgb3d28.js → doctor-ztwx1y95.js} +104 -35
  313. package/dist/{durable-object-t4kbb0yt.js → durable-object-v3gsnybk.js} +1 -1
  314. package/dist/env.d.ts +10 -0
  315. package/dist/env.d.ts.map +1 -1
  316. package/dist/index-11p28kxx.js +256 -0
  317. package/dist/index-15fpa5tx.js +232 -0
  318. package/dist/index-1d4jg11n.js +542 -0
  319. package/dist/index-3edvz3hs.js +124 -0
  320. package/dist/index-3jme4hgw.js +1234 -0
  321. package/dist/index-627srx16.js +45 -0
  322. package/dist/index-62b3gt2g.js +12 -0
  323. package/dist/index-67gr49zr.js +109 -0
  324. package/dist/index-69c0ed4a.js +895 -0
  325. package/dist/index-6sj0v0dk.js +2284 -0
  326. package/dist/index-7qd7nmdd.js +74 -0
  327. package/dist/index-85yjb686.js +413 -0
  328. package/dist/index-aabgympv.js +39 -0
  329. package/dist/{index-z14anrqp.js → index-aqrwyy57.js} +93 -31
  330. package/dist/index-c8p4njqy.js +479 -0
  331. package/dist/index-dfcp9hsd.js +1581 -0
  332. package/dist/index-dm9q84c7.js +360 -0
  333. package/dist/index-fcmj6641.js +147 -0
  334. package/dist/index-ft3trvra.js +476 -0
  335. package/dist/{index-18hvb6gb.js → index-gn5wy09x.js} +13 -2
  336. package/dist/index-hjjfxv02.js +708 -0
  337. package/dist/index-hpwa6vsw.js +239 -0
  338. package/dist/index-k9x5v335.js +1033 -0
  339. package/dist/index-mg8vwqxf.js +514 -0
  340. package/dist/index-ptwem1nt.js +1428 -0
  341. package/dist/{index-07q6yxyc.js → index-q8f4kawk.js} +68 -32
  342. package/dist/index-qmmqww7h.js +133 -0
  343. package/dist/{index-rbht7m9r.js → index-qwgr4q7s.js} +2 -1
  344. package/dist/index-qz6nbwpw.js +111 -0
  345. package/dist/index-rzp9ejrh.js +185 -0
  346. package/dist/index-s3km3y34.js +1205 -0
  347. package/dist/index-stgn34cr.js +148 -0
  348. package/dist/index-t9y0sve5.js +155 -0
  349. package/dist/index-vez803pp.js +52 -0
  350. package/dist/index-vhqww6tt.js +307 -0
  351. package/dist/index-xz18tstn.js +581 -0
  352. package/dist/{index-1xpj0m4r.js → index-z9gy8w6b.js} +1 -1
  353. package/dist/index-za0r01bx.js +588 -0
  354. package/dist/index.d.ts +3 -7
  355. package/dist/index.d.ts.map +1 -1
  356. package/dist/index.js +34 -269
  357. package/dist/{init-f9mgmew3.js → init-cwpergap.js} +63 -69
  358. package/dist/login-r5ve73kf.js +77 -0
  359. package/dist/previews-qjj98rpp.js +1337 -0
  360. package/dist/productions-gzdtge0e.js +505 -0
  361. package/dist/runtime/context-events.d.ts +13 -0
  362. package/dist/runtime/context-events.d.ts.map +1 -0
  363. package/dist/runtime/context-types.d.ts +82 -0
  364. package/dist/runtime/context-types.d.ts.map +1 -0
  365. package/dist/runtime/context.d.ts +20 -44
  366. package/dist/runtime/context.d.ts.map +1 -1
  367. package/dist/runtime/exports.d.ts +70 -55
  368. package/dist/runtime/exports.d.ts.map +1 -1
  369. package/dist/runtime/index.d.ts +8 -1
  370. package/dist/runtime/index.d.ts.map +1 -1
  371. package/dist/runtime/index.js +108 -99
  372. package/dist/runtime/middleware.d.ts +102 -60
  373. package/dist/runtime/middleware.d.ts.map +1 -1
  374. package/dist/runtime/router/index.d.ts +7 -0
  375. package/dist/runtime/router/index.d.ts.map +1 -0
  376. package/dist/runtime/router/types.d.ts +24 -0
  377. package/dist/runtime/router/types.d.ts.map +1 -0
  378. package/dist/runtime/validation.d.ts +17 -3
  379. package/dist/runtime/validation.d.ts.map +1 -1
  380. package/dist/secrets/local-secrets.d.ts +46 -0
  381. package/dist/secrets/local-secrets.d.ts.map +1 -0
  382. package/dist/secrets-4w5166km.js +91 -0
  383. package/dist/shims/local-hyperdrive.d.ts +12 -0
  384. package/dist/shims/local-hyperdrive.d.ts.map +1 -0
  385. package/dist/shims/local-media-bindings.d.ts +19 -0
  386. package/dist/shims/local-media-bindings.d.ts.map +1 -0
  387. package/dist/shims/local-worker-loader.d.ts +3 -0
  388. package/dist/shims/local-worker-loader.d.ts.map +1 -0
  389. package/dist/sveltekit/index.js +169 -75
  390. package/dist/sveltekit/local-bindings.d.ts +4 -0
  391. package/dist/sveltekit/local-bindings.d.ts.map +1 -0
  392. package/dist/sveltekit/platform.d.ts +19 -1
  393. package/dist/sveltekit/platform.d.ts.map +1 -1
  394. package/dist/test/ai-search.d.ts +39 -0
  395. package/dist/test/ai-search.d.ts.map +1 -0
  396. package/dist/test/binding-hints.d.ts +11 -0
  397. package/dist/test/binding-hints.d.ts.map +1 -0
  398. package/dist/test/cf.d.ts +35 -21
  399. package/dist/test/cf.d.ts.map +1 -1
  400. package/dist/test/containers.d.ts +87 -0
  401. package/dist/test/containers.d.ts.map +1 -0
  402. package/dist/test/email.d.ts +16 -7
  403. package/dist/test/email.d.ts.map +1 -1
  404. package/dist/test/index.d.ts +5 -8
  405. package/dist/test/index.d.ts.map +1 -1
  406. package/dist/test/index.js +4641 -43
  407. package/dist/test/offline-bindings.d.ts +65 -0
  408. package/dist/test/offline-bindings.d.ts.map +1 -0
  409. package/dist/test/queue.d.ts.map +1 -1
  410. package/dist/test/remote-ai.d.ts.map +1 -1
  411. package/dist/test/remote-cloudflare.d.ts +13 -0
  412. package/dist/test/remote-cloudflare.d.ts.map +1 -0
  413. package/dist/test/remote-vectorize.d.ts.map +1 -1
  414. package/dist/test/resolve-service-bindings.d.ts +63 -3
  415. package/dist/test/resolve-service-bindings.d.ts.map +1 -1
  416. package/dist/test/scheduled.d.ts.map +1 -1
  417. package/dist/test/should-skip.d.ts +14 -18
  418. package/dist/test/should-skip.d.ts.map +1 -1
  419. package/dist/test/simple-context-bindings.d.ts +13 -0
  420. package/dist/test/simple-context-bindings.d.ts.map +1 -0
  421. package/dist/test/simple-context-durable-objects.d.ts +6 -0
  422. package/dist/test/simple-context-durable-objects.d.ts.map +1 -0
  423. package/dist/test/simple-context-env.d.ts +35 -0
  424. package/dist/test/simple-context-env.d.ts.map +1 -0
  425. package/dist/test/simple-context-gateway-script.d.ts +2 -0
  426. package/dist/test/simple-context-gateway-script.d.ts.map +1 -0
  427. package/dist/test/simple-context-handlers.d.ts +12 -0
  428. package/dist/test/simple-context-handlers.d.ts.map +1 -0
  429. package/dist/test/simple-context-lifecycle.d.ts +32 -0
  430. package/dist/test/simple-context-lifecycle.d.ts.map +1 -0
  431. package/dist/test/simple-context-mfconfig.d.ts +10 -0
  432. package/dist/test/simple-context-mfconfig.d.ts.map +1 -0
  433. package/dist/test/simple-context-multi-worker.d.ts +16 -0
  434. package/dist/test/simple-context-multi-worker.d.ts.map +1 -0
  435. package/dist/test/simple-context-paths.d.ts +40 -0
  436. package/dist/test/simple-context-paths.d.ts.map +1 -0
  437. package/dist/test/simple-context-runtime.d.ts +19 -0
  438. package/dist/test/simple-context-runtime.d.ts.map +1 -0
  439. package/dist/test/simple-context-startup.d.ts +11 -0
  440. package/dist/test/simple-context-startup.d.ts.map +1 -0
  441. package/dist/test/simple-context-transport.d.ts +14 -0
  442. package/dist/test/simple-context-transport.d.ts.map +1 -0
  443. package/dist/test/simple-context.d.ts +7 -26
  444. package/dist/test/simple-context.d.ts.map +1 -1
  445. package/dist/test/tail.d.ts +2 -1
  446. package/dist/test/tail.d.ts.map +1 -1
  447. package/dist/test/utilities/artifacts.d.ts +11 -0
  448. package/dist/test/utilities/artifacts.d.ts.map +1 -0
  449. package/dist/test/utilities/context.d.ts +39 -0
  450. package/dist/test/utilities/context.d.ts.map +1 -0
  451. package/dist/test/utilities/d1.d.ts +21 -0
  452. package/dist/test/utilities/d1.d.ts.map +1 -0
  453. package/dist/test/utilities/env.d.ts +40 -0
  454. package/dist/test/utilities/env.d.ts.map +1 -0
  455. package/dist/test/utilities/kv.d.ts +11 -0
  456. package/dist/test/utilities/kv.d.ts.map +1 -0
  457. package/dist/test/utilities/media.d.ts +16 -0
  458. package/dist/test/utilities/media.d.ts.map +1 -0
  459. package/dist/test/utilities/platform.d.ts +41 -0
  460. package/dist/test/utilities/platform.d.ts.map +1 -0
  461. package/dist/test/utilities/queue.d.ts +5 -0
  462. package/dist/test/utilities/queue.d.ts.map +1 -0
  463. package/dist/test/utilities/r2.d.ts +2 -0
  464. package/dist/test/utilities/r2.d.ts.map +1 -0
  465. package/dist/test/utilities/workflows.d.ts +26 -0
  466. package/dist/test/utilities/workflows.d.ts.map +1 -0
  467. package/dist/test/utilities.d.ts +10 -98
  468. package/dist/test/utilities.d.ts.map +1 -1
  469. package/dist/test/worker.d.ts +6 -0
  470. package/dist/test/worker.d.ts.map +1 -1
  471. package/dist/token-35jcdqq8.js +419 -0
  472. package/dist/transform/durable-object.d.ts.map +1 -1
  473. package/dist/transform/worker-entrypoint.d.ts +5 -0
  474. package/dist/transform/worker-entrypoint.d.ts.map +1 -1
  475. package/dist/types-6vc02evc.js +705 -0
  476. package/dist/utils/entrypoint-discovery.d.ts +6 -3
  477. package/dist/utils/entrypoint-discovery.d.ts.map +1 -1
  478. package/dist/utils/resolve-package.d.ts.map +1 -1
  479. package/dist/utils/send-email.d.ts +15 -0
  480. package/dist/utils/send-email.d.ts.map +1 -0
  481. package/dist/utils/send-email.js +19 -0
  482. package/dist/vite/config-file.d.ts +25 -0
  483. package/dist/vite/config-file.d.ts.map +1 -0
  484. package/dist/vite/index.d.ts +1 -0
  485. package/dist/vite/index.d.ts.map +1 -1
  486. package/dist/vite/index.js +25 -331
  487. package/dist/vite/plugin-config-hook.d.ts +28 -0
  488. package/dist/vite/plugin-config-hook.d.ts.map +1 -0
  489. package/dist/vite/plugin-context.d.ts +27 -0
  490. package/dist/vite/plugin-context.d.ts.map +1 -0
  491. package/dist/vite/plugin-durable-objects.d.ts +18 -0
  492. package/dist/vite/plugin-durable-objects.d.ts.map +1 -0
  493. package/dist/vite/plugin-programmatic.d.ts +44 -0
  494. package/dist/vite/plugin-programmatic.d.ts.map +1 -0
  495. package/dist/vite/plugin-service-bindings.d.ts +13 -0
  496. package/dist/vite/plugin-service-bindings.d.ts.map +1 -0
  497. package/dist/vite/plugin-transform.d.ts +41 -0
  498. package/dist/vite/plugin-transform.d.ts.map +1 -0
  499. package/dist/vite/plugin.d.ts +10 -41
  500. package/dist/vite/plugin.d.ts.map +1 -1
  501. package/dist/worker-entry/composed-worker.d.ts +7 -0
  502. package/dist/worker-entry/composed-worker.d.ts.map +1 -0
  503. package/dist/worker-entry/durable-object-discovery.d.ts +20 -0
  504. package/dist/worker-entry/durable-object-discovery.d.ts.map +1 -0
  505. package/dist/worker-entry/extensions.d.ts +5 -0
  506. package/dist/worker-entry/extensions.d.ts.map +1 -0
  507. package/dist/worker-entry/routes.d.ts +22 -0
  508. package/dist/worker-entry/routes.d.ts.map +1 -0
  509. package/dist/worker-entry/surface-paths.d.ts +26 -0
  510. package/dist/worker-entry/surface-paths.d.ts.map +1 -0
  511. package/dist/{worker-entrypoint-m9th0rg0.js → worker-entrypoint-3rmzd4c1.js} +3 -1
  512. package/dist/worker-f2v85f7d.js +513 -0
  513. package/dist/workflows/local-workflow-entrypoints.d.ts +7 -0
  514. package/dist/workflows/local-workflow-entrypoints.d.ts.map +1 -0
  515. package/package.json +154 -115
  516. package/dist/account-rvrj687w.js +0 -397
  517. package/dist/bridge/protocol.d.ts.map +0 -1
  518. package/dist/bridge/serialization.d.ts.map +0 -1
  519. package/dist/browser-shim/worker.d.ts +0 -14
  520. package/dist/browser-shim/worker.d.ts.map +0 -1
  521. package/dist/build-mnf6v8gd.js +0 -53
  522. package/dist/deploy-nhceck39.js +0 -70
  523. package/dist/dev-pa8dhm20.js +0 -2162
  524. package/dist/index-67qcae0f.js +0 -183
  525. package/dist/index-ep3445yc.js +0 -2225
  526. package/dist/index-gz1gndna.js +0 -307
  527. package/dist/index-hcex3rgh.js +0 -266
  528. package/dist/index-m2q41jwa.js +0 -462
  529. package/dist/index-n7rs26ft.js +0 -77
  530. package/dist/index-pf5s73n9.js +0 -1413
  531. package/dist/index-tfyxa77h.js +0 -850
  532. package/dist/index-tk6ej9dj.js +0 -94
  533. package/dist/test/bridge-context.d.ts +0 -76
  534. package/dist/test/bridge-context.d.ts.map +0 -1
  535. package/dist/test/multi-worker-context.d.ts +0 -114
  536. package/dist/test/multi-worker-context.d.ts.map +0 -1
  537. package/dist/types-5nyrz1sz.js +0 -454
@@ -1,71 +1,4669 @@
1
1
  import {
2
- cf,
2
+ getRemoteModeStatus,
3
+ isRemoteModeActive
4
+ } from "../index-d8bdkx2h.js";
5
+ import {
6
+ canProceedWithTest
7
+ } from "../index-15fpa5tx.js";
8
+ import {
9
+ bundleWorkflowEntrypointScript
10
+ } from "../index-67gr49zr.js";
11
+ import"../index-69c0ed4a.js";
12
+ import {
13
+ createRouteResolve,
14
+ invokeFetchModule,
15
+ matchFetchRoute,
16
+ resolveFetchHandler
17
+ } from "../index-85yjb686.js";
18
+ import {
19
+ __clearTestContext,
20
+ __setTestContext,
21
+ env
22
+ } from "../index-qz6nbwpw.js";
23
+ import"../index-a855bdsx.js";
24
+ import {
25
+ buildHyperdrivesConfig,
3
26
  clearBundleCache,
4
- createBridgeTestContext,
5
- createEntrypointScript,
6
- createMockD1,
7
- createMockEnv,
8
- createMockKV,
9
- createMockQueue,
10
- createMockR2,
11
- createMockTestContext,
12
- createMultiWorkerContext,
13
- createTestContext,
14
- email,
15
- env,
16
- getBridgeTestContext,
27
+ discoverRoutes,
17
28
  hasCrossWorkerDOs,
18
29
  hasServiceBindings,
19
- isRemoteModeEnabled,
20
- queue,
21
30
  resolveDOBindings,
22
- resolveServiceBindings,
31
+ resolveServiceBindings
32
+ } from "../index-k9x5v335.js";
33
+ import"../index-3edvz3hs.js";
34
+ import {
35
+ DEFAULT_DO_PATTERN,
36
+ findFiles
37
+ } from "../index-qwgr4q7s.js";
38
+ import"../index-aqrwyy57.js";
39
+ import"../index-vhqww6tt.js";
40
+ import {
41
+ createLocalHyperdrive,
42
+ createLocalWorkerLoaderBinding,
43
+ disposeLocalWorkerLoaderBindings,
44
+ extractBindingHints
45
+ } from "../index-t9y0sve5.js";
46
+ import {
47
+ buildLocalBindingShimServiceConfig
48
+ } from "../index-dm9q84c7.js";
49
+ import {
50
+ buildLocalSecretWrappedBindingConfig,
51
+ resolveLocalSecretValuesForBindings
52
+ } from "../index-fcmj6641.js";
53
+ import {
54
+ BridgeClient,
55
+ createEmailEvent,
56
+ createEnvProxy,
57
+ createFetchEvent,
58
+ createQueueEvent,
59
+ createScheduledEvent,
60
+ createTailEvent,
61
+ runWithContext,
62
+ runWithEventContext,
63
+ setBindingHints
64
+ } from "../index-6sj0v0dk.js";
65
+ import {
66
+ createLocalSendEmailBinding,
67
+ wrapEnvSendEmailBindings
68
+ } from "../index-hpwa6vsw.js";
69
+ import {
70
+ applyLocalDevVarsToConfig
71
+ } from "../index-hjjfxv02.js";
72
+ import {
73
+ getLocalD1DatabaseIdentifier,
74
+ loadConfig,
75
+ normalizeArtifactsBinding,
76
+ normalizeDOBinding,
77
+ normalizeDispatchNamespaceBinding,
78
+ normalizeHyperdriveBinding,
79
+ normalizeImagesBinding,
80
+ normalizeMediaBinding,
81
+ normalizeMtlsCertificateBinding,
82
+ normalizePipelineBinding,
83
+ normalizeSecretsStoreBinding,
84
+ normalizeWorkflowBinding,
85
+ resolveConfigPath
86
+ } from "../index-dfcp9hsd.js";
87
+ import {
88
+ getEffectiveAccountId,
89
+ getPrimaryAccount
90
+ } from "../index-1d4jg11n.js";
91
+ import {
92
+ getApiToken,
93
+ isAuthenticated
94
+ } from "../index-mg8vwqxf.js";
95
+ import {
96
+ resolveConfigEnvVars
97
+ } from "../index-c8p4njqy.js";
98
+ import"../index-q8f4kawk.js";
99
+ import {
100
+ __require
101
+ } from "../index-37x76zdn.js";
102
+
103
+ // src/test/simple-context-durable-objects.ts
104
+ import { mkdirSync, writeFileSync } from "fs";
105
+ import { readFile } from "fs/promises";
106
+ import { dirname as dirname2, join as join2 } from "path";
107
+
108
+ // src/test/simple-context-gateway-script.ts
109
+ function buildGatewayScript(bundledCode, wrappers, nativeRpcBindingNames = []) {
110
+ const nativeRpcBindingsLiteral = JSON.stringify(nativeRpcBindingNames);
111
+ return `
112
+ // Bundled transport + DO classes
113
+ ${bundledCode}
114
+
115
+ // DO Wrappers with RPC
116
+ ${wrappers}
117
+
118
+ const __nativeRpcBindings = new Set(${nativeRpcBindingsLiteral})
119
+
120
+ // Transport encoding helper
121
+ const __transportEncoders = typeof transport !== 'undefined' ? transport : {}
122
+
123
+ function __encodeTransport(value) {
124
+ if (value === null || value === undefined) return value
125
+
126
+ // Try each encoder
127
+ for (const [typeName, transporter] of Object.entries(__transportEncoders)) {
128
+ const encoded = transporter.encode(value)
129
+ if (encoded !== false && encoded !== undefined) {
130
+ return { __transport: typeName, value: encoded }
131
+ }
132
+ }
133
+
134
+ // Recursively encode arrays and objects
135
+ if (Array.isArray(value)) {
136
+ return value.map(__encodeTransport)
137
+ }
138
+ if (typeof value === 'object') {
139
+ const result = {}
140
+ for (const [k, v] of Object.entries(value)) {
141
+ result[k] = __encodeTransport(v)
142
+ }
143
+ return result
144
+ }
145
+
146
+ return value
147
+ }
148
+
149
+ // Gateway with WebSocket RPC
150
+ export default {
151
+ async fetch(request, env) {
152
+ if (request.headers.get('Upgrade') === 'websocket') {
153
+ const { 0: client, 1: server } = new WebSocketPair()
154
+ server.accept()
155
+ server.addEventListener('message', async (e) => {
156
+ try {
157
+ const m = JSON.parse(e.data)
158
+ if (m.t === 'rpc.call') {
159
+ const result = await executeRpc(env, m.method, m.params)
160
+ server.send(JSON.stringify({ t: 'rpc.ok', id: m.id, result }))
161
+ }
162
+ } catch (error) {
163
+ server.send(JSON.stringify({ t: 'rpc.err', id: 'unknown', error: { code: 'RPC_ERROR', message: error.message } }))
164
+ }
165
+ })
166
+ return new Response(null, { status: 101, webSocket: client })
167
+ }
168
+ return new Response('Gateway')
169
+ }
170
+ }
171
+
172
+ async function executeRpc(env, method, params) {
173
+ const [bindingName, ...rest] = method.split('.')
174
+ let op = rest.join('.')
175
+ const binding = env[bindingName]
176
+ const RAW_EMAIL = 'EmailMessage::raw'
177
+ if (!binding) throw new Error('Binding not found: ' + bindingName)
178
+
179
+ // Normalize namespaced op names (kv.*, r2.*, d1.*, do.*, queue.*, ai.*, var.*)
180
+ // down to the legacy verbs this dispatcher historically used. The bridge
181
+ // proxy always emits namespaced forms (see src/bridge/proxy.ts and B3 in
182
+ // REMAINING.md); this keeps the dispatcher backwards-compatible while the
183
+ // rest of the codebase converges on the namespaced convention.
184
+ if (op.indexOf('kv.') === 0) op = op.slice(3)
185
+ else if (op.indexOf('do.') === 0) {
186
+ const tail = op.slice(3)
187
+ if (tail === 'fetch') op = 'stub.fetch'
188
+ else if (tail === 'rpc') op = 'stub.rpc'
189
+ else op = tail
190
+ }
191
+ else if (op.indexOf('queue.') === 0) op = op.slice(6)
192
+ else if (op.indexOf('ai.') === 0) op = op.slice(3)
193
+ else if (op.indexOf('var.') === 0) op = op.slice(4)
194
+ else if (op.indexOf('d1.stmt.') === 0) op = 'prepare.' + op.slice('d1.stmt.'.length)
195
+ else if (op.indexOf('d1.') === 0) op = op.slice(3)
196
+ // r2.* and email.* keep their existing prefixes
197
+
198
+ // KV operations
199
+ if (op === 'get') return binding.get(params[0], params[1])
200
+ if (op === 'put') return binding.put(params[0], params[1], params[2])
201
+ if (op === 'delete') return binding.delete(params[0])
202
+ if (op === 'list') return binding.list(params[0])
203
+ if (op === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
204
+
205
+ // R2 operations
206
+ if (op === 'r2.get') return binding.get(params[0], params[1])
207
+ if (op === 'r2.put') return binding.put(params[0], params[1], params[2])
208
+ if (op === 'r2.delete') return binding.delete(params[0])
209
+ if (op === 'r2.list') return binding.list(params[0])
210
+ if (op === 'r2.head' || op === 'head') return binding.head(params[0])
211
+
212
+ // D1 operations
213
+ if (op === 'exec') return binding.exec(params[0])
214
+ if (op === 'dump') return binding.dump()
215
+ if (op === 'batch') {
216
+ const stmts = params[0].map(s => {
217
+ const stmt = binding.prepare(s.sql)
218
+ return s.bindings?.length ? stmt.bind(...s.bindings) : stmt
219
+ })
220
+ return binding.batch(stmts)
221
+ }
222
+ if (op === 'prepare.run') return binding.prepare(params[0]).bind(...(params[1] || [])).run()
223
+ if (op === 'prepare.all') return binding.prepare(params[0]).bind(...(params[1] || [])).all()
224
+ if (op === 'prepare.first') return binding.prepare(params[0]).bind(...(params[1] || [])).first(params[2])
225
+ if (op === 'prepare.raw') return binding.prepare(params[0]).bind(...(params[1] || [])).raw({ columnNames: params[2] })
226
+
227
+ // Send email operations
228
+ if (op === 'email.send') {
229
+ return binding.send(__normalizeEmailMessage(params[0]))
230
+ }
231
+
232
+ // DO operations
233
+ if (op === 'idFromName') {
234
+ return { __type: 'DOId', hex: binding.idFromName(params[0]).toString() }
235
+ }
236
+ if (op === 'stub.rpc') {
237
+ const [, idSerialized, rpcMethod, rpcParams] = params
238
+ const stub = binding.get(binding.idFromString(idSerialized.hex))
239
+
240
+ if (__nativeRpcBindings.has(bindingName) && typeof stub[rpcMethod] === 'function') {
241
+ let result = await stub[rpcMethod](...(rpcParams || []))
242
+ result = __encodeTransport(result)
243
+ return result
244
+ }
245
+
246
+ const response = await stub.fetch(new Request('http://do/_rpc', {
247
+ method: 'POST',
248
+ headers: { 'Content-Type': 'application/json' },
249
+ body: JSON.stringify({ method: rpcMethod, params: rpcParams || [] })
250
+ }))
251
+
252
+ const payload = await response.json()
253
+ if (!response.ok || !payload?.ok) {
254
+ throw new Error(payload?.error?.message || ('DO RPC failed with status ' + response.status))
255
+ }
256
+
257
+ return payload.result
258
+ }
259
+
260
+ throw new Error('Unknown operation: ' + method)
261
+ }
262
+
263
+ function __createEmailMessageRaw(raw) {
264
+ if (typeof raw === 'string' || raw instanceof ReadableStream) {
265
+ return raw
266
+ }
267
+ if (raw instanceof Uint8Array || raw instanceof ArrayBuffer) {
268
+ return new Response(raw).body
269
+ }
270
+ throw new Error('Unsupported EmailMessage raw payload')
271
+ }
272
+
273
+ function __buildRawEmail(message) {
274
+ const lines = []
275
+ const messageId = '<' + Date.now() + '-' + Math.random().toString(36).slice(2) + '@devflare.dev>'
276
+
277
+ lines.push('From: ' + message.from)
278
+ lines.push('To: ' + (Array.isArray(message.to) ? message.to.join(', ') : message.to))
279
+ lines.push('Date: ' + new Date().toUTCString())
280
+ lines.push('Message-ID: ' + messageId)
281
+
282
+ if (message.subject) lines.push('Subject: ' + message.subject)
283
+ if (message.replyTo) lines.push('Reply-To: ' + String(message.replyTo))
284
+ if (message.cc) lines.push('Cc: ' + (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc))
285
+ if (message.bcc) lines.push('Bcc: ' + (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc))
286
+
287
+ for (const [key, value] of Object.entries(message.headers || {})) {
288
+ lines.push(key + ': ' + value)
289
+ }
290
+
291
+ lines.push('MIME-Version: 1.0')
292
+ lines.push('Content-Type: ' + (message.html ? 'text/html' : 'text/plain') + '; charset=UTF-8')
293
+ lines.push('')
294
+ lines.push(String(message.html ?? message.text ?? '').replace(/\\r?\\n/g, '\\r\\n'))
295
+
296
+ return lines.join('\\r\\n')
297
+ }
298
+
299
+ function __normalizeEmailMessage(message) {
300
+ if (!message || typeof message !== 'object' || !('from' in message) || !('to' in message)) {
301
+ return message
302
+ }
303
+ if ('EmailMessage::raw' in message) {
304
+ return message
305
+ }
306
+ if ('raw' in message && message.raw !== undefined) {
307
+ return {
308
+ from: message.from,
309
+ to: message.to,
310
+ [RAW_EMAIL]: __createEmailMessageRaw(message.raw)
311
+ }
312
+ }
313
+ return {
314
+ from: message.from,
315
+ to: message.to,
316
+ [RAW_EMAIL]: __createEmailMessageRaw(__buildRawEmail(message))
317
+ }
318
+ }
319
+ `;
320
+ }
321
+
322
+ // src/test/simple-context-paths.ts
323
+ import { existsSync } from "fs";
324
+ import { createServer } from "net";
325
+ import { dirname, join } from "path";
326
+ import { fileURLToPath } from "url";
327
+ var DEFAULT_TRANSPORT_ENTRY_FILES = [
328
+ "src/transport.ts",
329
+ "src/transport.js",
330
+ "src/transport.mts",
331
+ "src/transport.mjs"
332
+ ];
333
+ var CURRENT_PACKAGE_ROOT = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
334
+ function getBunRuntime() {
335
+ const g = globalThis;
336
+ if (typeof g.Bun === "object" && g.Bun !== null) {
337
+ return g.Bun;
338
+ }
339
+ return;
340
+ }
341
+ function getCallerDirectory() {
342
+ const stackCallerDirectory = getStackCallerDirectory();
343
+ if (stackCallerDirectory) {
344
+ return stackCallerDirectory;
345
+ }
346
+ return process.cwd();
347
+ }
348
+ function getStackCallerDirectory() {
349
+ const originalPrepare = Error.prepareStackTrace;
350
+ Error.prepareStackTrace = (_, stack) => stack;
351
+ try {
352
+ const err = new Error;
353
+ const stack = err.stack;
354
+ for (const site of stack ?? []) {
355
+ const filename = site.getFileName?.();
356
+ if (filename && !isInsideCurrentPackage(filename) && !filename.includes("simple-context") && !filename.includes("node_modules") && !filename.includes("[") && existsSync(filename)) {
357
+ return dirname(filename);
358
+ }
359
+ }
360
+ } finally {
361
+ Error.prepareStackTrace = originalPrepare;
362
+ }
363
+ return null;
364
+ }
365
+ function findPackageRoot(startDir) {
366
+ let currentDir = startDir;
367
+ while (true) {
368
+ if (existsSync(join(currentDir, "package.json"))) {
369
+ return currentDir;
370
+ }
371
+ const parentDir = dirname(currentDir);
372
+ if (parentDir === currentDir) {
373
+ return startDir;
374
+ }
375
+ currentDir = parentDir;
376
+ }
377
+ }
378
+ function isInsideCurrentPackage(filePath) {
379
+ const normalizedFilePath = filePath.replace(/\\/g, "/");
380
+ const normalizedPackageRoot = CURRENT_PACKAGE_ROOT.replace(/\\/g, "/");
381
+ return normalizedFilePath === normalizedPackageRoot || normalizedFilePath.startsWith(`${normalizedPackageRoot}/`);
382
+ }
383
+ async function findNearestConfig(startDir) {
384
+ let currentDir = startDir;
385
+ while (true) {
386
+ const configPath = await resolveConfigPath(currentDir);
387
+ if (configPath) {
388
+ return configPath;
389
+ }
390
+ const parentDir = dirname(currentDir);
391
+ if (parentDir === currentDir) {
392
+ return null;
393
+ }
394
+ currentDir = parentDir;
395
+ }
396
+ }
397
+ async function getAvailablePort() {
398
+ return await new Promise((resolvePort, reject) => {
399
+ const server = createServer();
400
+ server.once("error", reject);
401
+ server.listen(0, "127.0.0.1", () => {
402
+ const address = server.address();
403
+ if (!address || typeof address === "string") {
404
+ server.close(() => reject(new Error("Could not determine an available port")));
405
+ return;
406
+ }
407
+ const { port } = address;
408
+ server.close((error) => {
409
+ if (error) {
410
+ reject(error);
411
+ return;
412
+ }
413
+ resolvePort(port);
414
+ });
415
+ });
416
+ });
417
+ }
418
+ function resolveTransportFile(configDir, configuredPath) {
419
+ if (typeof configuredPath === "string") {
420
+ return configuredPath;
421
+ }
422
+ if (configuredPath === null) {
423
+ return null;
424
+ }
425
+ for (const defaultEntry of DEFAULT_TRANSPORT_ENTRY_FILES) {
426
+ if (existsSync(join(configDir, defaultEntry))) {
427
+ return defaultEntry;
428
+ }
429
+ }
430
+ return null;
431
+ }
432
+
433
+ // src/test/simple-context-durable-objects.ts
434
+ function findExportedClasses(code) {
435
+ const classes = [];
436
+ const classPattern = /export\s+class\s+(\w+)/g;
437
+ let match;
438
+ while ((match = classPattern.exec(code)) !== null) {
439
+ classes.push(match[1]);
440
+ }
441
+ return classes;
442
+ }
443
+ function classSupportsNativeDurableObjectRpc(code, className) {
444
+ const nativeRpcPattern = new RegExp(`export\\s+class\\s+${className}\\s+extends\\s+DurableObject\\b`);
445
+ return nativeRpcPattern.test(code);
446
+ }
447
+ function toGeneratedIdentifier(value) {
448
+ const normalized = value.replace(/[^A-Za-z0-9_$]/g, "_");
449
+ return /^[A-Za-z_$]/.test(normalized) ? normalized : `_${normalized}`;
450
+ }
451
+ async function discoverLocalDurableObjectClasses(config, configDir) {
452
+ const classToFilePath = new Map;
453
+ const doPatternConfig = config.files?.durableObjects;
454
+ const doPattern = typeof doPatternConfig === "string" ? doPatternConfig : DEFAULT_DO_PATTERN;
455
+ if (doPatternConfig === false) {
456
+ return classToFilePath;
457
+ }
458
+ const doFiles = await findFiles(doPattern, { cwd: configDir });
459
+ for (const filePath of doFiles) {
460
+ try {
461
+ const code = await readFile(filePath, "utf-8");
462
+ const classNames = findExportedClasses(code);
463
+ for (const className of classNames) {
464
+ classToFilePath.set(className, {
465
+ filePath,
466
+ nativeRpc: classSupportsNativeDurableObjectRpc(code, className)
467
+ });
468
+ }
469
+ } catch {}
470
+ }
471
+ return classToFilePath;
472
+ }
473
+ async function resolveLocalDurableObjects(config, configDir) {
474
+ const doConfig = {};
475
+ const doInfos = [];
476
+ const classToFilePath = await discoverLocalDurableObjectClasses(config, configDir);
477
+ for (const [name, rawDoInfo] of Object.entries(config.bindings?.durableObjects ?? {})) {
478
+ const doInfo = normalizeDOBinding(rawDoInfo);
479
+ if (doInfo.__ref) {
480
+ continue;
481
+ }
482
+ let scriptPath;
483
+ let nativeRpc = false;
484
+ if (doInfo.kind === "cross-worker" && doInfo.scriptName) {
485
+ scriptPath = join2(configDir, "src", doInfo.scriptName);
486
+ try {
487
+ const code = await readFile(scriptPath, "utf-8");
488
+ nativeRpc = classSupportsNativeDurableObjectRpc(code, doInfo.className);
489
+ } catch {
490
+ nativeRpc = false;
491
+ }
492
+ } else {
493
+ const discoveredClass = classToFilePath.get(doInfo.className);
494
+ if (!discoveredClass) {
495
+ throw new Error(`Durable object ${name} (className: '${doInfo.className}') not found.
496
+ ` + `Either:
497
+ ` + ` 1. Set files.durableObjects pattern in config (e.g., 'src/do.*.ts')
498
+ ` + ` 2. Use explicit scriptName: { className: '${doInfo.className}', scriptName: 'do.file.ts' }`);
499
+ }
500
+ scriptPath = discoveredClass.filePath;
501
+ nativeRpc = discoveredClass.nativeRpc;
502
+ }
503
+ const runtimeClassName = nativeRpc ? doInfo.className : `__Devflare${toGeneratedIdentifier(name)}RpcWrapper`;
504
+ doConfig[name] = runtimeClassName;
505
+ doInfos.push({
506
+ name,
507
+ className: doInfo.className,
508
+ scriptPath,
509
+ nativeRpc,
510
+ runtimeClassName
511
+ });
512
+ }
513
+ return {
514
+ doConfig,
515
+ doInfos
516
+ };
517
+ }
518
+ function buildWrapperCode(doInfos) {
519
+ return doInfos.filter((info) => !info.nativeRpc).map((info) => `
520
+ export class ${info.runtimeClassName} {
521
+ constructor(state, env) {
522
+ this.__instance = new ${info.className}(state, env)
523
+ }
524
+
525
+ async fetch(request) {
526
+ const url = new URL(request.url)
527
+ if (request.method !== 'POST' || url.pathname !== '/_rpc') {
528
+ return new Response('Not found', { status: 404 })
529
+ }
530
+
531
+ try {
532
+ const payload = await request.json()
533
+ const method = payload?.method
534
+ const params = Array.isArray(payload?.params) ? payload.params : []
535
+ const target = this.__instance?.[method]
536
+
537
+ if (typeof target !== 'function') {
538
+ return new Response(JSON.stringify({
539
+ ok: false,
540
+ error: { message: 'Method not found: ' + String(method) }
541
+ }), {
542
+ status: 404,
543
+ headers: { 'Content-Type': 'application/json' }
544
+ })
545
+ }
546
+
547
+ let result = await target.apply(this.__instance, params)
548
+ result = __encodeTransport(result)
549
+
550
+ return new Response(JSON.stringify({ ok: true, result }), {
551
+ headers: { 'Content-Type': 'application/json' }
552
+ })
553
+ } catch (error) {
554
+ return new Response(JSON.stringify({
555
+ ok: false,
556
+ error: { message: error instanceof Error ? error.message : String(error) }
557
+ }), {
558
+ status: 500,
559
+ headers: { 'Content-Type': 'application/json' }
560
+ })
561
+ }
562
+ }
563
+ }`.trim()).join(`
564
+
565
+ `);
566
+ }
567
+ async function bundleDurableObjectModules(configDir, doInfos, transportFile) {
568
+ const virtualImports = [];
569
+ const virtualExports = [];
570
+ if (transportFile) {
571
+ const transportPath = join2(configDir, transportFile);
572
+ virtualImports.push(`import { transport } from '${transportPath.replace(/\\/g, "/")}'`);
573
+ virtualExports.push("export { transport }");
574
+ }
575
+ for (const info of doInfos) {
576
+ virtualImports.push(`import { ${info.className} } from '${info.scriptPath.replace(/\\/g, "/")}'`);
577
+ virtualExports.push(`export { ${info.className} }`);
578
+ }
579
+ if (virtualImports.length === 0) {
580
+ return "";
581
+ }
582
+ const virtualEntry = [...virtualImports, "", ...virtualExports].join(`
583
+ `);
584
+ const virtualPath = join2(configDir, ".devflare", "__test_entry.ts");
585
+ mkdirSync(dirname2(virtualPath), { recursive: true });
586
+ writeFileSync(virtualPath, virtualEntry);
587
+ const bun = getBunRuntime();
588
+ if (!bun) {
589
+ throw new Error("Bun runtime is required for createTestContext with Durable Objects");
590
+ }
591
+ const result = await bun.build({
592
+ entrypoints: [virtualPath],
593
+ target: "browser",
594
+ format: "esm",
595
+ minify: false,
596
+ external: ["cloudflare:workers", "cloudflare:*"]
597
+ });
598
+ if (!result.success) {
599
+ throw new Error(`Failed to bundle test entry: ${result.logs.join(`
600
+ `)}`);
601
+ }
602
+ return await result.outputs[0].text();
603
+ }
604
+ async function buildDurableObjectGateway(config, configDir, transportFile) {
605
+ const workflowEntrypointScript = await bundleWorkflowEntrypointScript(config, configDir);
606
+ if (!config.bindings?.durableObjects) {
607
+ return {
608
+ script: buildGatewayScript(workflowEntrypointScript, "")
609
+ };
610
+ }
611
+ const { doConfig, doInfos } = await resolveLocalDurableObjects(config, configDir);
612
+ const bundledCode = await bundleDurableObjectModules(configDir, doInfos, transportFile);
613
+ const wrapperCode = buildWrapperCode(doInfos);
614
+ const entrypointCode = [workflowEntrypointScript, bundledCode].filter(Boolean).join(`
615
+
616
+ `);
617
+ const nativeRpcBindingNames = doInfos.filter((info) => info.nativeRpc).map((info) => info.name);
618
+ return {
619
+ durableObjects: doConfig,
620
+ script: buildGatewayScript(entrypointCode, wrapperCode, nativeRpcBindingNames)
621
+ };
622
+ }
623
+
624
+ // src/test/remote-cloudflare.ts
625
+ function createRemoteCloudflareClient(accountId) {
626
+ let resolvedAccountId = null;
627
+ async function getAccountId() {
628
+ if (accountId) {
629
+ return accountId;
630
+ }
631
+ if (resolvedAccountId) {
632
+ return resolvedAccountId;
633
+ }
634
+ const primary = await getPrimaryAccount();
635
+ if (!primary) {
636
+ throw new Error("No Cloudflare account found. Run: bunx wrangler login");
637
+ }
638
+ const { accountId: effectiveId } = await getEffectiveAccountId(primary.id);
639
+ resolvedAccountId = effectiveId;
640
+ return effectiveId;
641
+ }
642
+ async function getToken() {
643
+ const token = await getApiToken();
644
+ if (!token) {
645
+ throw new Error("Not authenticated. Run: bunx wrangler login");
646
+ }
647
+ return token;
648
+ }
649
+ async function jsonRequest(options) {
650
+ const [acctId, token] = await Promise.all([getAccountId(), getToken()]);
651
+ const response = await fetch(`https://api.cloudflare.com/client/v4/accounts/${acctId}${options.path}`, {
652
+ method: options.method,
653
+ headers: {
654
+ Authorization: `Bearer ${token}`,
655
+ "Content-Type": options.contentType ?? "application/json"
656
+ },
657
+ body: options.body
658
+ });
659
+ if (!response.ok) {
660
+ const errorText = await response.text();
661
+ throw new Error(`${options.serviceLabel} API error (${response.status}): ${errorText}`);
662
+ }
663
+ const result = await response.json();
664
+ if (!result.success) {
665
+ const message = result.errors?.[0]?.message || `Unknown ${options.serviceLabel} error`;
666
+ throw new Error(`${options.serviceLabel} API error: ${message}`);
667
+ }
668
+ return result.result;
669
+ }
670
+ return {
671
+ getAccountId,
672
+ getToken,
673
+ jsonRequest
674
+ };
675
+ }
676
+
677
+ // src/test/remote-ai.ts
678
+ function encodePathSegment(value) {
679
+ return encodeURIComponent(value);
680
+ }
681
+ function applyExtraHeaders(headers, extraHeaders) {
682
+ if (!extraHeaders) {
683
+ return;
684
+ }
685
+ for (const [key, value] of Object.entries(extraHeaders)) {
686
+ headers.set(key, String(value));
687
+ }
688
+ }
689
+ function createRemoteAIGateway(cloudflare, gatewayId, owner) {
690
+ const encodedGatewayId = encodePathSegment(gatewayId);
691
+ const gateway = {
692
+ async patchLog(logId, data) {
693
+ await cloudflare.jsonRequest({
694
+ method: "PATCH",
695
+ path: `/ai-gateway/gateways/${encodedGatewayId}/logs/${encodePathSegment(logId)}`,
696
+ serviceLabel: "AI Gateway",
697
+ body: JSON.stringify(data)
698
+ });
699
+ },
700
+ async getLog(logId) {
701
+ return cloudflare.jsonRequest({
702
+ method: "GET",
703
+ path: `/ai-gateway/gateways/${encodedGatewayId}/logs/${encodePathSegment(logId)}`,
704
+ serviceLabel: "AI Gateway"
705
+ });
706
+ },
707
+ async getUrl(provider) {
708
+ const accountId = await cloudflare.getAccountId();
709
+ const baseUrl = `https://gateway.ai.cloudflare.com/v1/${encodePathSegment(accountId)}/${encodedGatewayId}`;
710
+ return provider ? `${baseUrl}/${encodePathSegment(provider)}` : `${baseUrl}/`;
711
+ },
712
+ async run(data, options) {
713
+ const [url, token] = await Promise.all([gateway.getUrl(), cloudflare.getToken()]);
714
+ const headers = new Headers({
715
+ Authorization: `Bearer ${token}`,
716
+ "Content-Type": "application/json"
717
+ });
718
+ applyExtraHeaders(headers, options?.extraHeaders);
719
+ const response = await fetch(url, {
720
+ method: "POST",
721
+ headers,
722
+ body: JSON.stringify(data),
723
+ signal: options?.signal
724
+ });
725
+ const logId = response.headers.get("cf-aig-log-id") ?? response.headers.get("cf-ai-gateway-log-id");
726
+ if (logId) {
727
+ owner.aiGatewayLogId = logId;
728
+ }
729
+ if (!response.ok) {
730
+ const errorText = await response.text();
731
+ throw new Error(`AI Gateway API error (${response.status}): ${errorText}`);
732
+ }
733
+ return response;
734
+ }
735
+ };
736
+ return gateway;
737
+ }
738
+ function createRemoteAI(accountId) {
739
+ const cloudflare = createRemoteCloudflareClient(accountId);
740
+ const ai = {
741
+ aiGatewayLogId: null,
742
+ async run(model, inputs) {
743
+ return cloudflare.jsonRequest({
744
+ method: "POST",
745
+ path: `/ai/run/${model}`,
746
+ serviceLabel: "AI",
747
+ body: JSON.stringify(inputs)
748
+ });
749
+ },
750
+ gateway(gatewayId) {
751
+ return createRemoteAIGateway(cloudflare, gatewayId, ai);
752
+ }
753
+ };
754
+ return ai;
755
+ }
756
+
757
+ // src/test/remote-vectorize.ts
758
+ function createRemoteVectorize(indexName, accountId) {
759
+ const cloudflare = createRemoteCloudflareClient(accountId);
760
+ async function apiRequest(method, endpoint, body) {
761
+ return cloudflare.jsonRequest({
762
+ method,
763
+ path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
764
+ serviceLabel: "Vectorize",
765
+ body: body ? JSON.stringify(body) : undefined
766
+ });
767
+ }
768
+ async function ndjsonRequest(endpoint, vectors) {
769
+ const ndjson = vectors.map((v) => JSON.stringify(v)).join(`
770
+ `);
771
+ return cloudflare.jsonRequest({
772
+ method: "POST",
773
+ path: `/vectorize/v2/indexes/${indexName}${endpoint}`,
774
+ serviceLabel: "Vectorize",
775
+ contentType: "application/x-ndjson",
776
+ body: ndjson
777
+ });
778
+ }
779
+ const vectorize = {
780
+ async describe() {
781
+ return apiRequest("GET", "");
782
+ },
783
+ async query(vector, options) {
784
+ const vectorArray = Array.isArray(vector) ? vector : Array.from(vector);
785
+ return apiRequest("POST", "/query", {
786
+ vector: vectorArray,
787
+ topK: options?.topK ?? 10,
788
+ returnValues: options?.returnValues ?? false,
789
+ returnMetadata: options?.returnMetadata ?? "none",
790
+ namespace: options?.namespace,
791
+ filter: options?.filter
792
+ });
793
+ },
794
+ async insert(vectors) {
795
+ const result = await ndjsonRequest("/insert", vectors);
796
+ return {
797
+ count: result.count,
798
+ ids: result.ids || vectors.map((v) => v.id)
799
+ };
800
+ },
801
+ async upsert(vectors) {
802
+ const result = await ndjsonRequest("/upsert", vectors);
803
+ return {
804
+ count: result.count,
805
+ ids: result.ids || vectors.map((v) => v.id)
806
+ };
807
+ },
808
+ async deleteByIds(ids) {
809
+ const result = await apiRequest("POST", "/delete-by-ids", { ids });
810
+ return {
811
+ count: result.count,
812
+ ids
813
+ };
814
+ },
815
+ async getByIds(ids) {
816
+ return apiRequest("POST", "/get-by-ids", { ids });
817
+ }
818
+ };
819
+ return vectorize;
820
+ }
821
+
822
+ // src/test/utilities/context.ts
823
+ function createMockTestContext(options = {}) {
824
+ const waitUntilPromises = [];
825
+ const ctx = {
826
+ waitUntil(promise) {
827
+ waitUntilPromises.push(promise);
828
+ },
829
+ passThroughOnException() {},
830
+ props: {}
831
+ };
832
+ return {
833
+ env: options.env ?? {},
834
+ ctx,
835
+ request: options.request ?? null,
836
+ waitUntilPromises
837
+ };
838
+ }
839
+ async function withTestContext(options, handler) {
840
+ const testCtx = createMockTestContext(options);
841
+ return runWithContext(testCtx.env, testCtx.ctx, options.request ?? null, handler, options.type ?? "fetch");
842
+ }
843
+ // src/test/utilities/kv.ts
844
+ function createMockKV(initialData = {}) {
845
+ const store = new Map;
846
+ const metadata = new Map;
847
+ const encoder = new TextEncoder;
848
+ const decoder = new TextDecoder;
849
+ for (const [key, value] of Object.entries(initialData)) {
850
+ store.set(key, encoder.encode(value));
851
+ }
852
+ const toBytes = async (value) => {
853
+ if (typeof value === "string") {
854
+ return encoder.encode(value);
855
+ }
856
+ if (value instanceof ArrayBuffer) {
857
+ return new Uint8Array(value.slice(0));
858
+ }
859
+ if (ArrayBuffer.isView(value)) {
860
+ const view = value;
861
+ const copy = new Uint8Array(view.byteLength);
862
+ copy.set(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
863
+ return copy;
864
+ }
865
+ const reader = value.getReader();
866
+ const chunks = [];
867
+ let total = 0;
868
+ while (true) {
869
+ const result = await reader.read();
870
+ if (result.done)
871
+ break;
872
+ if (result.value) {
873
+ const chunk = result.value instanceof Uint8Array ? result.value : new Uint8Array(result.value);
874
+ chunks.push(chunk);
875
+ total += chunk.length;
876
+ }
877
+ }
878
+ const combined = new Uint8Array(total);
879
+ let offset = 0;
880
+ for (const chunk of chunks) {
881
+ combined.set(chunk, offset);
882
+ offset += chunk.length;
883
+ }
884
+ return combined;
885
+ };
886
+ const decodeBytes = (bytes, type) => {
887
+ switch (type) {
888
+ case "json":
889
+ return JSON.parse(decoder.decode(bytes));
890
+ case "arrayBuffer": {
891
+ const copy = new Uint8Array(bytes.length);
892
+ copy.set(bytes);
893
+ return copy.buffer;
894
+ }
895
+ case "stream": {
896
+ const copy = new Uint8Array(bytes.length);
897
+ copy.set(bytes);
898
+ return new ReadableStream({
899
+ start(controller) {
900
+ controller.enqueue(copy);
901
+ controller.close();
902
+ }
903
+ });
904
+ }
905
+ default:
906
+ return decoder.decode(bytes);
907
+ }
908
+ };
909
+ const resolveType = (options) => {
910
+ const type = typeof options === "string" ? options : options?.type ?? "text";
911
+ return type;
912
+ };
913
+ return {
914
+ async get(key, options) {
915
+ const bytes = store.get(key);
916
+ if (bytes === undefined)
917
+ return null;
918
+ return decodeBytes(bytes, resolveType(options));
919
+ },
920
+ async put(key, value, _options) {
921
+ const bytes = await toBytes(value);
922
+ store.set(key, bytes);
923
+ },
924
+ async delete(key) {
925
+ store.delete(key);
926
+ metadata.delete(key);
927
+ },
928
+ async list(options) {
929
+ const prefix = options?.prefix ?? "";
930
+ const limit = options?.limit ?? 1000;
931
+ const keys = Array.from(store.keys()).filter((key) => key.startsWith(prefix)).slice(0, limit).map((name) => ({ name }));
932
+ return {
933
+ keys,
934
+ list_complete: keys.length < limit,
935
+ cursor: undefined
936
+ };
937
+ },
938
+ async getWithMetadata(key, options) {
939
+ const bytes = store.get(key);
940
+ return {
941
+ value: bytes === undefined ? null : decodeBytes(bytes, resolveType(options)),
942
+ metadata: metadata.get(key) ?? null
943
+ };
944
+ }
945
+ };
946
+ }
947
+ // src/test/utilities/d1.ts
948
+ var TABLE_NAME_RE = /(?:from|into|update)\s+["'`]?([a-zA-Z_][a-zA-Z0-9_]*)["'`]?/i;
949
+ var extractTable = (sql) => {
950
+ const match = TABLE_NAME_RE.exec(sql);
951
+ return match ? match[1] : null;
952
+ };
953
+ var detectOp = (sql) => {
954
+ const trimmed = sql.trimStart().toLowerCase();
955
+ if (trimmed.startsWith("select"))
956
+ return "select";
957
+ if (trimmed.startsWith("insert"))
958
+ return "insert";
959
+ if (trimmed.startsWith("update"))
960
+ return "update";
961
+ if (trimmed.startsWith("delete"))
962
+ return "delete";
963
+ return "other";
964
+ };
965
+ function createMockD1(mockResultsOrOptions = []) {
966
+ const options = Array.isArray(mockResultsOrOptions) ? { results: mockResultsOrOptions } : mockResultsOrOptions;
967
+ const tables = new Map;
968
+ for (const [name, rows] of Object.entries(options.fixtures ?? {})) {
969
+ tables.set(name, [...rows]);
970
+ }
971
+ const fallback = options.results ?? [];
972
+ const resolveRows = (sql) => {
973
+ const op = detectOp(sql);
974
+ const table = extractTable(sql);
975
+ if (table && tables.has(table)) {
976
+ return { rows: tables.get(table) ?? [], op, table };
977
+ }
978
+ return { rows: [...fallback], op, table };
979
+ };
980
+ const createStatement = (sql) => {
981
+ let boundValues = [];
982
+ const statement = {
983
+ bind(...values) {
984
+ boundValues = values;
985
+ return statement;
986
+ },
987
+ async first(column) {
988
+ const { rows } = resolveRows(sql);
989
+ const row = rows[0];
990
+ if (!row)
991
+ return null;
992
+ if (column)
993
+ return row[column];
994
+ return row;
995
+ },
996
+ async all() {
997
+ const { rows } = resolveRows(sql);
998
+ return {
999
+ results: rows,
1000
+ success: true,
1001
+ meta: { duration: 0, changes: 0, last_row_id: 0 }
1002
+ };
1003
+ },
1004
+ async run() {
1005
+ const { op, table } = resolveRows(sql);
1006
+ let changes = 0;
1007
+ let lastRowId = 0;
1008
+ if (op === "insert" && table) {
1009
+ const rows = tables.get(table) ?? [];
1010
+ const bound = boundValues.length > 0 ? Object.fromEntries(boundValues.map((v, i) => [`col${i}`, v])) : {};
1011
+ rows.push(bound);
1012
+ tables.set(table, rows);
1013
+ changes = 1;
1014
+ lastRowId = rows.length;
1015
+ } else if (op === "delete" && table) {
1016
+ const rows = tables.get(table) ?? [];
1017
+ changes = rows.length;
1018
+ tables.set(table, []);
1019
+ } else if (op === "update" && table) {
1020
+ changes = (tables.get(table) ?? []).length;
1021
+ }
1022
+ return {
1023
+ results: [],
1024
+ success: true,
1025
+ meta: { duration: 0, changes, last_row_id: lastRowId }
1026
+ };
1027
+ },
1028
+ async raw(_options) {
1029
+ const { rows } = resolveRows(sql);
1030
+ return rows.map((row) => Object.values(row));
1031
+ }
1032
+ };
1033
+ return statement;
1034
+ };
1035
+ return {
1036
+ prepare(query) {
1037
+ return createStatement(query);
1038
+ },
1039
+ async exec(_query) {
1040
+ return {
1041
+ results: [],
1042
+ success: true,
1043
+ meta: { duration: 0, changes: 0, last_row_id: 0 }
1044
+ };
1045
+ },
1046
+ async batch(statements) {
1047
+ return statements.map(() => ({
1048
+ results: [],
1049
+ success: true,
1050
+ meta: { duration: 0, changes: 0, last_row_id: 0 }
1051
+ }));
1052
+ },
1053
+ async dump() {
1054
+ return new ArrayBuffer(0);
1055
+ },
1056
+ withSession(_constraintOrBookmark) {
1057
+ return this;
1058
+ }
1059
+ };
1060
+ }
1061
+ // src/test/utilities/r2.ts
1062
+ async function coerceToString(value) {
1063
+ if (typeof value === "string") {
1064
+ return value;
1065
+ }
1066
+ if (value === null) {
1067
+ return "";
1068
+ }
1069
+ if (value instanceof ArrayBuffer) {
1070
+ return new TextDecoder().decode(value);
1071
+ }
1072
+ if (ArrayBuffer.isView(value)) {
1073
+ return new TextDecoder().decode(value);
1074
+ }
1075
+ if (value instanceof Blob) {
1076
+ return await value.text();
1077
+ }
1078
+ const reader = value.getReader();
1079
+ const chunks = [];
1080
+ let done = false;
1081
+ while (!done) {
1082
+ const result = await reader.read();
1083
+ done = result.done;
1084
+ if (result.value) {
1085
+ chunks.push(new TextDecoder().decode(result.value));
1086
+ }
1087
+ }
1088
+ return chunks.join("");
1089
+ }
1090
+ function createMockR2() {
1091
+ const store = new Map;
1092
+ const uploads = new Map;
1093
+ const createR2Object = (key, content, metadata) => {
1094
+ const encoder = new TextEncoder;
1095
+ const data = encoder.encode(content);
1096
+ return {
1097
+ key,
1098
+ version: "1",
1099
+ size: data.length,
1100
+ etag: `"${key}-etag"`,
1101
+ httpEtag: `"${key}-etag"`,
1102
+ uploaded: new Date,
1103
+ httpMetadata: metadata ?? {},
1104
+ customMetadata: {},
1105
+ checksums: {},
1106
+ storageClass: "Standard",
1107
+ body: new ReadableStream({
1108
+ start(controller) {
1109
+ controller.enqueue(data);
1110
+ controller.close();
1111
+ }
1112
+ }),
1113
+ bodyUsed: false,
1114
+ async arrayBuffer() {
1115
+ return new Uint8Array(data).buffer;
1116
+ },
1117
+ async text() {
1118
+ return content;
1119
+ },
1120
+ async json() {
1121
+ return JSON.parse(content);
1122
+ },
1123
+ async blob() {
1124
+ return new Blob([data]);
1125
+ },
1126
+ writeHttpMetadata(headers) {}
1127
+ };
1128
+ };
1129
+ return {
1130
+ async put(key, value, options) {
1131
+ const content = await coerceToString(value);
1132
+ store.set(key, { content });
1133
+ return createR2Object(key, content);
1134
+ },
1135
+ async get(key, options) {
1136
+ const item = store.get(key);
1137
+ if (!item)
1138
+ return null;
1139
+ return createR2Object(key, item.content, item.metadata);
1140
+ },
1141
+ async head(key) {
1142
+ const item = store.get(key);
1143
+ if (!item)
1144
+ return null;
1145
+ return {
1146
+ key,
1147
+ version: "1",
1148
+ size: new TextEncoder().encode(item.content).length,
1149
+ etag: `"${key}-etag"`,
1150
+ httpEtag: `"${key}-etag"`,
1151
+ uploaded: new Date,
1152
+ httpMetadata: item.metadata ?? {},
1153
+ customMetadata: {},
1154
+ checksums: {},
1155
+ storageClass: "Standard",
1156
+ writeHttpMetadata(headers) {}
1157
+ };
1158
+ },
1159
+ async delete(keys) {
1160
+ const keyArray = Array.isArray(keys) ? keys : [keys];
1161
+ for (const key of keyArray) {
1162
+ store.delete(key);
1163
+ }
1164
+ },
1165
+ async list(options) {
1166
+ const objects = Array.from(store.entries()).map(([key, { content, metadata }]) => createR2Object(key, content, metadata));
1167
+ return {
1168
+ objects,
1169
+ truncated: false,
1170
+ delimitedPrefixes: []
1171
+ };
1172
+ },
1173
+ async createMultipartUpload(key, options) {
1174
+ const uploadId = crypto.randomUUID();
1175
+ uploads.set(uploadId, { key, parts: new Map, options });
1176
+ return createMultipart(key, uploadId);
1177
+ },
1178
+ resumeMultipartUpload(key, uploadId) {
1179
+ if (!uploads.has(uploadId)) {
1180
+ uploads.set(uploadId, { key, parts: new Map });
1181
+ }
1182
+ return createMultipart(key, uploadId);
1183
+ }
1184
+ };
1185
+ function createMultipart(key, uploadId) {
1186
+ const requireEntry = () => {
1187
+ const entry = uploads.get(uploadId);
1188
+ if (!entry) {
1189
+ throw new Error(`Mock R2 multipart upload "${uploadId}" for key "${key}" is not active (it was aborted or completed).`);
1190
+ }
1191
+ return entry;
1192
+ };
1193
+ return {
1194
+ key,
1195
+ uploadId,
1196
+ async uploadPart(partNumber, value, _options) {
1197
+ const entry = requireEntry();
1198
+ const str = await coerceToString(value);
1199
+ entry.parts.set(partNumber, str);
1200
+ return { partNumber, etag: `"${key}-part-${partNumber}-etag"` };
1201
+ },
1202
+ async abort() {
1203
+ uploads.delete(uploadId);
1204
+ },
1205
+ async complete(uploadedParts) {
1206
+ const entry = requireEntry();
1207
+ const ordered = [...uploadedParts].sort((a, b) => a.partNumber - b.partNumber);
1208
+ const content = ordered.map((part) => {
1209
+ const chunk = entry.parts.get(part.partNumber);
1210
+ if (chunk === undefined) {
1211
+ throw new Error(`Mock R2 multipart complete() references part ${part.partNumber} that was never uploaded for upload "${uploadId}".`);
1212
+ }
1213
+ return chunk;
1214
+ }).join("");
1215
+ const metadata = entry.options?.customMetadata;
1216
+ store.set(key, { content, metadata });
1217
+ uploads.delete(uploadId);
1218
+ return createR2Object(key, content, metadata);
1219
+ }
1220
+ };
1221
+ }
1222
+ }
1223
+ // src/test/utilities/queue.ts
1224
+ function createMockQueue() {
1225
+ const messages = [];
1226
+ const metrics = {
1227
+ backlogCount: 0,
1228
+ backlogBytes: 0
1229
+ };
1230
+ const response = { metadata: { metrics } };
1231
+ return {
1232
+ async metrics() {
1233
+ return metrics;
1234
+ },
1235
+ async send(message, options) {
1236
+ messages.push({ body: message, options });
1237
+ return response;
1238
+ },
1239
+ async sendBatch(batch, options) {
1240
+ for (const message of batch) {
1241
+ messages.push({
1242
+ body: message.body,
1243
+ options: {
1244
+ contentType: message.contentType,
1245
+ delaySeconds: message.delaySeconds ?? options?.delaySeconds
1246
+ }
1247
+ });
1248
+ }
1249
+ return response;
1250
+ },
1251
+ _getMessages() {
1252
+ return messages;
1253
+ }
1254
+ };
1255
+ }
1256
+ // src/test/utilities/platform.ts
1257
+ function createMockRateLimit(options = {}) {
1258
+ const limit = options.limit ?? Number.MAX_SAFE_INTEGER;
1259
+ const periodMs = (options.period ?? 60) * 1000;
1260
+ const windows = new Map;
1261
+ return {
1262
+ async limit({ key }) {
1263
+ const now = Date.now();
1264
+ const existing = windows.get(key);
1265
+ if (!existing || existing.resetAt <= now) {
1266
+ windows.set(key, { count: 1, resetAt: now + periodMs });
1267
+ return { success: limit >= 1 };
1268
+ }
1269
+ existing.count += 1;
1270
+ return { success: existing.count <= limit };
1271
+ }
1272
+ };
1273
+ }
1274
+ function createMockVersionMetadata(metadata = {}) {
1275
+ return {
1276
+ id: metadata.id ?? "devflare-local-version",
1277
+ tag: metadata.tag ?? "local",
1278
+ timestamp: metadata.timestamp ?? "1970-01-01T00:00:00.000Z"
1279
+ };
1280
+ }
1281
+ function createMockSecretsStoreSecret(value) {
1282
+ return {
1283
+ async get() {
1284
+ return value;
1285
+ }
1286
+ };
1287
+ }
1288
+ function createMockHyperdrive(connectionString) {
1289
+ return createLocalHyperdrive(connectionString);
1290
+ }
1291
+ function createDefaultWorkerStub() {
1292
+ return {
1293
+ getEntrypoint() {
1294
+ throw new Error("Mock WorkerLoader stub has no entrypoint. Pass createMockWorkerLoader({ stub }) for behavior.");
1295
+ },
1296
+ getDurableObjectClass() {
1297
+ throw new Error("Mock WorkerLoader stub cannot materialise a Durable Object class. " + "getDurableObjectClass() returns an opaque facet-spawning reference that workerd " + "only exposes inside the runtime. Pass createMockWorkerLoader({ stub }) to inject a stub, " + "or use createTestContext() (a real Miniflare worker) for Durable Object behavior.");
1298
+ }
1299
+ };
1300
+ }
1301
+ function createMockWorkerLoader(options = {}) {
1302
+ const stub = options.stub ?? createDefaultWorkerStub();
1303
+ return {
1304
+ get(_name, _getCode) {
1305
+ return stub;
1306
+ },
1307
+ load(_code) {
1308
+ return stub;
1309
+ }
1310
+ };
1311
+ }
1312
+ function defaultMTLSCertificateHandler() {
1313
+ throw new Error("Mock mTLS Certificate Fetcher has no handler. Pass createMockMTLSCertificate(handler) for behavior.");
1314
+ }
1315
+ function createMockMTLSCertificate(handler = defaultMTLSCertificateHandler) {
1316
+ return {
1317
+ async fetch(input, init) {
1318
+ return handler(input, init);
1319
+ }
1320
+ };
1321
+ }
1322
+ function createMockDispatchNamespace(options = {}) {
1323
+ return {
1324
+ get(name) {
1325
+ const worker = options.workers?.[name];
1326
+ if (!worker) {
1327
+ throw new Error(`Mock DispatchNamespace has no worker named "${name}".`);
1328
+ }
1329
+ return typeof worker === "function" ? createMockMTLSCertificate(worker) : worker;
1330
+ }
1331
+ };
1332
+ }
1333
+ // src/test/utilities/workflows.ts
1334
+ function createMockWorkflowInstance(id, options = {}) {
1335
+ let status = options.status ?? "queued";
1336
+ let output = options.output;
1337
+ let error = options.error;
1338
+ return {
1339
+ id,
1340
+ async pause() {
1341
+ status = "paused";
1342
+ },
1343
+ async resume() {
1344
+ status = "running";
1345
+ },
1346
+ async terminate() {
1347
+ status = "terminated";
1348
+ },
1349
+ async restart() {
1350
+ status = "queued";
1351
+ error = undefined;
1352
+ output = undefined;
1353
+ },
1354
+ async status() {
1355
+ return {
1356
+ status,
1357
+ ...error && { error },
1358
+ ...output !== undefined && { output }
1359
+ };
1360
+ },
1361
+ async sendEvent(_event) {}
1362
+ };
1363
+ }
1364
+ function createMockWorkflow(options = {}) {
1365
+ const instances = new Map;
1366
+ let sequence = 0;
1367
+ for (const [id, instanceOptions] of Object.entries(options.instances ?? {})) {
1368
+ instances.set(id, createMockWorkflowInstance(id, instanceOptions));
1369
+ }
1370
+ const createInstance = (id) => {
1371
+ if (instances.has(id)) {
1372
+ throw new Error(`Mock Workflow already has an instance named "${id}".`);
1373
+ }
1374
+ const instance = createMockWorkflowInstance(id);
1375
+ instances.set(id, instance);
1376
+ return instance;
1377
+ };
1378
+ return {
1379
+ async get(id) {
1380
+ const instance = instances.get(id);
1381
+ if (!instance) {
1382
+ throw new Error(`Mock Workflow has no instance named "${id}".`);
1383
+ }
1384
+ return instance;
1385
+ },
1386
+ async create(options2) {
1387
+ const id = options2?.id ?? `mock-workflow-${++sequence}`;
1388
+ return createInstance(id);
1389
+ },
1390
+ async createBatch(batch) {
1391
+ return batch.map((options2) => {
1392
+ const id = options2.id ?? `mock-workflow-${++sequence}`;
1393
+ return createInstance(id);
1394
+ });
1395
+ }
1396
+ };
1397
+ }
1398
+ function createMockPipeline() {
1399
+ const records = [];
1400
+ return {
1401
+ async send(batch) {
1402
+ records.push(...batch);
1403
+ },
1404
+ _getRecords() {
1405
+ return [...records];
1406
+ }
1407
+ };
1408
+ }
1409
+ // src/test/utilities/media.ts
1410
+ function createEmptyImageStream() {
1411
+ return new ReadableStream({
1412
+ start(controller) {
1413
+ controller.close();
1414
+ }
1415
+ });
1416
+ }
1417
+ function createMockImageTransformationResult(response) {
1418
+ return {
1419
+ response() {
1420
+ return response.clone();
1421
+ },
1422
+ contentType() {
1423
+ return response.headers.get("Content-Type") ?? "image/png";
1424
+ },
1425
+ image() {
1426
+ const cloned = response.clone();
1427
+ return cloned.body ?? createEmptyImageStream();
1428
+ }
1429
+ };
1430
+ }
1431
+ function createMockImageTransformer(response) {
1432
+ const transformer = {
1433
+ transform(_transform) {
1434
+ return transformer;
1435
+ },
1436
+ draw(_image, _options) {
1437
+ return transformer;
1438
+ },
1439
+ async output(_options) {
1440
+ return createMockImageTransformationResult(response);
1441
+ }
1442
+ };
1443
+ return transformer;
1444
+ }
1445
+ function createMockHostedImagesBinding() {
1446
+ const unsupported = () => {
1447
+ throw new Error("Mock Images hosted API is not implemented. Pass a custom ImagesBinding through createMockEnv({ images }) if your test needs hosted image behavior.");
1448
+ };
1449
+ return {
1450
+ image(_imageId) {
1451
+ return {
1452
+ details: unsupported,
1453
+ bytes: unsupported,
1454
+ update: unsupported,
1455
+ delete: unsupported
1456
+ };
1457
+ },
1458
+ upload: unsupported,
1459
+ list: unsupported
1460
+ };
1461
+ }
1462
+ function createMockImagesBinding(options = {}) {
1463
+ const response = options.response ?? new Response("", {
1464
+ headers: { "Content-Type": "image/png" }
1465
+ });
1466
+ const info = options.info ?? {
1467
+ format: response.headers.get("Content-Type") ?? "image/png",
1468
+ fileSize: 0,
1469
+ width: 0,
1470
+ height: 0
1471
+ };
1472
+ return {
1473
+ async info(_stream, _options) {
1474
+ return info;
1475
+ },
1476
+ input(_stream, _options) {
1477
+ return createMockImageTransformer(response);
1478
+ },
1479
+ hosted: createMockHostedImagesBinding()
1480
+ };
1481
+ }
1482
+ function createEmptyMediaStream() {
1483
+ return new ReadableStream({
1484
+ start(controller) {
1485
+ controller.close();
1486
+ }
1487
+ });
1488
+ }
1489
+ function createMockMediaTransformationResult(response) {
1490
+ return {
1491
+ async media() {
1492
+ const cloned = response.clone();
1493
+ return cloned.body ?? createEmptyMediaStream();
1494
+ },
1495
+ async response() {
1496
+ return response.clone();
1497
+ },
1498
+ async contentType() {
1499
+ return response.headers.get("Content-Type") ?? "video/mp4";
1500
+ }
1501
+ };
1502
+ }
1503
+ function createMockMediaTransformer(response) {
1504
+ const transformer = {
1505
+ transform(_transform) {
1506
+ return {
1507
+ output(_output) {
1508
+ return createMockMediaTransformationResult(response);
1509
+ }
1510
+ };
1511
+ },
1512
+ output(_output) {
1513
+ return createMockMediaTransformationResult(response);
1514
+ }
1515
+ };
1516
+ return transformer;
1517
+ }
1518
+ function createMockMediaBinding(options = {}) {
1519
+ const response = options.response ?? new Response("", {
1520
+ headers: { "Content-Type": "video/mp4" }
1521
+ });
1522
+ return {
1523
+ input(_media) {
1524
+ return createMockMediaTransformer(response);
1525
+ }
1526
+ };
1527
+ }
1528
+ // src/test/utilities/artifacts.ts
1529
+ function createArtifactTimestamp() {
1530
+ return new Date("2026-04-26T00:00:00.000Z").toISOString();
1531
+ }
1532
+ function createArtifactsRepoInfo(name, options = {}) {
1533
+ const now = createArtifactTimestamp();
1534
+ return {
1535
+ id: `repo-${name}`,
1536
+ name,
1537
+ description: options.description ?? null,
1538
+ defaultBranch: options.defaultBranch ?? "main",
1539
+ createdAt: now,
1540
+ updatedAt: now,
1541
+ lastPushAt: null,
1542
+ source: options.source ?? null,
1543
+ readOnly: options.readOnly ?? false,
1544
+ remote: `https://example.com/artifacts/default/${name}.git`
1545
+ };
1546
+ }
1547
+ function isArtifactsBinding(value) {
1548
+ return typeof value.create === "function";
1549
+ }
1550
+ function createMockArtifacts(options = {}) {
1551
+ const repos = new Map;
1552
+ const tokens = new Map;
1553
+ const addRepo = (info) => {
1554
+ repos.set(info.name, info);
1555
+ if (!tokens.has(info.name)) {
1556
+ tokens.set(info.name, []);
1557
+ }
1558
+ };
1559
+ for (const repo of options.repos ?? []) {
1560
+ addRepo({
1561
+ ...createArtifactsRepoInfo(repo.name),
1562
+ ...repo
1563
+ });
1564
+ }
1565
+ const createToken = (repoName, scope = "write", ttl = 86400) => {
1566
+ const existing = tokens.get(repoName) ?? [];
1567
+ const id = `token-${repoName}-${existing.length + 1}`;
1568
+ const expiresAt = new Date(Date.parse(createArtifactTimestamp()) + ttl * 1000).toISOString();
1569
+ const token = {
1570
+ id,
1571
+ scope,
1572
+ state: "active",
1573
+ createdAt: createArtifactTimestamp(),
1574
+ expiresAt
1575
+ };
1576
+ tokens.set(repoName, [...existing, token]);
1577
+ return {
1578
+ id,
1579
+ plaintext: `${id}-plaintext`,
1580
+ scope,
1581
+ expiresAt
1582
+ };
1583
+ };
1584
+ const createRepoHandle = (info) => ({
1585
+ ...info,
1586
+ async createToken(scope, ttl) {
1587
+ return createToken(info.name, scope, ttl);
1588
+ },
1589
+ async listTokens() {
1590
+ const repoTokens = tokens.get(info.name) ?? [];
1591
+ return {
1592
+ tokens: repoTokens,
1593
+ total: repoTokens.length
1594
+ };
1595
+ },
1596
+ async revokeToken(tokenOrId) {
1597
+ const repoTokens = tokens.get(info.name) ?? [];
1598
+ const index = repoTokens.findIndex((token) => token.id === tokenOrId);
1599
+ if (index === -1) {
1600
+ return false;
1601
+ }
1602
+ repoTokens[index] = {
1603
+ ...repoTokens[index],
1604
+ state: "revoked"
1605
+ };
1606
+ tokens.set(info.name, repoTokens);
1607
+ return true;
1608
+ },
1609
+ async fork(name, forkOptions) {
1610
+ return createRepo(name, {
1611
+ description: forkOptions?.description ?? info.description ?? undefined,
1612
+ readOnly: forkOptions?.readOnly ?? info.readOnly,
1613
+ setDefaultBranch: info.defaultBranch,
1614
+ source: `artifacts:default/${info.name}`
1615
+ });
1616
+ }
1617
+ });
1618
+ const createRepo = async (name, createOptions = {}) => {
1619
+ const info = createArtifactsRepoInfo(name, {
1620
+ description: createOptions.description,
1621
+ readOnly: createOptions.readOnly,
1622
+ defaultBranch: createOptions.setDefaultBranch,
1623
+ source: createOptions.source
1624
+ });
1625
+ addRepo(info);
1626
+ const token = createToken(name);
1627
+ return {
1628
+ id: info.id,
1629
+ name: info.name,
1630
+ description: info.description,
1631
+ defaultBranch: info.defaultBranch,
1632
+ remote: info.remote,
1633
+ token: token.plaintext,
1634
+ tokenExpiresAt: token.expiresAt
1635
+ };
1636
+ };
1637
+ return {
1638
+ create: createRepo,
1639
+ async get(name) {
1640
+ const repo = repos.get(name);
1641
+ return repo ? createRepoHandle(repo) : null;
1642
+ },
1643
+ async import(params) {
1644
+ return createRepo(params.target.name, {
1645
+ description: params.target.opts?.description,
1646
+ readOnly: params.target.opts?.readOnly,
1647
+ source: params.source.url
1648
+ });
1649
+ },
1650
+ async list(opts) {
1651
+ const limit = opts?.limit ?? 50;
1652
+ const repoList = Array.from(repos.values()).slice(0, limit).map((repo) => {
1653
+ const { remote: _remote, ...rest } = repo;
1654
+ return rest;
1655
+ });
1656
+ return {
1657
+ repos: repoList,
1658
+ total: repos.size,
1659
+ ...repos.size > repoList.length && { cursor: String(repoList.length) }
1660
+ };
1661
+ },
1662
+ async delete(name) {
1663
+ tokens.delete(name);
1664
+ return repos.delete(name);
1665
+ }
1666
+ };
1667
+ }
1668
+ // src/test/utilities/env.ts
1669
+ function createMockEnv(options = {}) {
1670
+ const env2 = {};
1671
+ if (options.kv) {
1672
+ for (const name of options.kv) {
1673
+ env2[name] = createMockKV();
1674
+ }
1675
+ }
1676
+ if (options.d1) {
1677
+ for (const name of options.d1) {
1678
+ env2[name] = createMockD1();
1679
+ }
1680
+ }
1681
+ if (options.r2) {
1682
+ for (const name of options.r2) {
1683
+ env2[name] = createMockR2();
1684
+ }
1685
+ }
1686
+ if (options.queues) {
1687
+ for (const name of options.queues) {
1688
+ env2[name] = createMockQueue();
1689
+ }
1690
+ }
1691
+ if (options.rateLimits) {
1692
+ for (const [name, rateLimitOptions] of Object.entries(options.rateLimits)) {
1693
+ env2[name] = createMockRateLimit(rateLimitOptions);
1694
+ }
1695
+ }
1696
+ if (options.versionMetadata) {
1697
+ env2[options.versionMetadata] = createMockVersionMetadata();
1698
+ }
1699
+ if (options.hyperdrive) {
1700
+ for (const [name, binding] of Object.entries(options.hyperdrive)) {
1701
+ env2[name] = typeof binding === "string" ? createMockHyperdrive(binding) : binding;
1702
+ }
1703
+ }
1704
+ if (Array.isArray(options.workerLoaders)) {
1705
+ for (const name of options.workerLoaders) {
1706
+ env2[name] = createMockWorkerLoader();
1707
+ }
1708
+ } else if (options.workerLoaders) {
1709
+ for (const [name, workerLoaderOptions] of Object.entries(options.workerLoaders)) {
1710
+ env2[name] = createMockWorkerLoader(workerLoaderOptions);
1711
+ }
1712
+ }
1713
+ if (Array.isArray(options.mtlsCertificates)) {
1714
+ for (const name of options.mtlsCertificates) {
1715
+ env2[name] = createMockMTLSCertificate();
1716
+ }
1717
+ } else if (options.mtlsCertificates) {
1718
+ for (const [name, handler] of Object.entries(options.mtlsCertificates)) {
1719
+ env2[name] = createMockMTLSCertificate(handler);
1720
+ }
1721
+ }
1722
+ if (Array.isArray(options.dispatchNamespaces)) {
1723
+ for (const name of options.dispatchNamespaces) {
1724
+ env2[name] = createMockDispatchNamespace();
1725
+ }
1726
+ } else if (options.dispatchNamespaces) {
1727
+ for (const [name, dispatchNamespaceOptions] of Object.entries(options.dispatchNamespaces)) {
1728
+ env2[name] = createMockDispatchNamespace(dispatchNamespaceOptions);
1729
+ }
1730
+ }
1731
+ if (Array.isArray(options.workflows)) {
1732
+ for (const name of options.workflows) {
1733
+ env2[name] = createMockWorkflow();
1734
+ }
1735
+ } else if (options.workflows) {
1736
+ for (const [name, workflowOptions] of Object.entries(options.workflows)) {
1737
+ env2[name] = "create" in workflowOptions ? workflowOptions : createMockWorkflow(workflowOptions);
1738
+ }
1739
+ }
1740
+ if (Array.isArray(options.pipelines)) {
1741
+ for (const name of options.pipelines) {
1742
+ env2[name] = createMockPipeline();
1743
+ }
1744
+ } else if (options.pipelines) {
1745
+ for (const [name, pipeline] of Object.entries(options.pipelines)) {
1746
+ env2[name] = pipeline;
1747
+ }
1748
+ }
1749
+ if (typeof options.images === "string") {
1750
+ env2[options.images] = createMockImagesBinding();
1751
+ } else if (options.images) {
1752
+ env2.IMAGES = options.images;
1753
+ }
1754
+ if (typeof options.media === "string") {
1755
+ env2[options.media] = createMockMediaBinding();
1756
+ } else if (options.media) {
1757
+ env2.MEDIA = options.media;
1758
+ }
1759
+ if (Array.isArray(options.artifacts)) {
1760
+ for (const name of options.artifacts) {
1761
+ env2[name] = createMockArtifacts();
1762
+ }
1763
+ } else if (options.artifacts) {
1764
+ for (const [name, artifactsOptions] of Object.entries(options.artifacts)) {
1765
+ env2[name] = isArtifactsBinding(artifactsOptions) ? artifactsOptions : createMockArtifacts(artifactsOptions);
1766
+ }
1767
+ }
1768
+ if (options.secretsStore) {
1769
+ for (const [name, value] of Object.entries(options.secretsStore)) {
1770
+ env2[name] = createMockSecretsStoreSecret(value);
1771
+ }
1772
+ }
1773
+ if (options.vars) {
1774
+ Object.assign(env2, options.vars);
1775
+ }
1776
+ if (options.secrets) {
1777
+ Object.assign(env2, options.secrets);
1778
+ }
1779
+ if (options.custom) {
1780
+ Object.assign(env2, options.custom);
1781
+ }
1782
+ return env2;
1783
+ }
1784
+ // src/test/simple-context-bindings.ts
1785
+ function buildRemoteAndStaticBindings(config) {
1786
+ const remoteBindings = {};
1787
+ if (isRemoteModeActive()) {
1788
+ if (config.bindings?.ai) {
1789
+ const aiBindingName = config.bindings.ai.binding || "AI";
1790
+ remoteBindings[aiBindingName] = createRemoteAI(config.accountId);
1791
+ }
1792
+ if (config.bindings?.vectorize) {
1793
+ for (const [name, vectorConfig] of Object.entries(config.bindings.vectorize)) {
1794
+ remoteBindings[name] = createRemoteVectorize(vectorConfig.indexName, config.accountId);
1795
+ }
1796
+ }
1797
+ }
1798
+ if (config.vars) {
1799
+ for (const [key, value] of Object.entries(config.vars)) {
1800
+ remoteBindings[key] = value;
1801
+ }
1802
+ }
1803
+ if (config.bindings?.sendEmail) {
1804
+ for (const [name, binding] of Object.entries(config.bindings.sendEmail)) {
1805
+ remoteBindings[name] = createLocalSendEmailBinding(binding);
1806
+ }
1807
+ }
1808
+ if (config.bindings?.workerLoaders) {
1809
+ for (const name of Object.keys(config.bindings.workerLoaders)) {
1810
+ remoteBindings[name] = createLocalWorkerLoaderBinding();
1811
+ }
1812
+ }
1813
+ if (config.bindings?.versionMetadata) {
1814
+ remoteBindings[config.bindings.versionMetadata.binding] = createMockVersionMetadata();
1815
+ }
1816
+ return remoteBindings;
1817
+ }
1818
+
1819
+ // src/test/email.ts
1820
+ import { join as join3 } from "path";
1821
+ var miniflarePort = 8787;
1822
+ var emailListeners = [];
1823
+ var sentEmails = [];
1824
+ var emailHandlerPath = null;
1825
+ var configDir = null;
1826
+ var testEnvGetter = null;
1827
+ function configureEmail(options = {}) {
1828
+ if (options.port) {
1829
+ miniflarePort = options.port;
1830
+ }
1831
+ emailHandlerPath = options.handlerPath ?? emailHandlerPath;
1832
+ configDir = options.configDir ?? configDir;
1833
+ testEnvGetter = options.getEnv ?? testEnvGetter;
1834
+ }
1835
+ function buildRawEmail(options) {
1836
+ if (options.raw) {
1837
+ return options.raw;
1838
+ }
1839
+ const lines = [];
1840
+ const messageId = `<${Date.now()}-${Math.random().toString(36).slice(2)}@devflare.dev>`;
1841
+ const date = new Date().toUTCString();
1842
+ lines.push(`From: ${options.from}`);
1843
+ lines.push(`To: ${options.to}`);
1844
+ lines.push(`Date: ${date}`);
1845
+ lines.push(`Message-ID: ${messageId}`);
1846
+ if (options.subject) {
1847
+ lines.push(`Subject: ${options.subject}`);
1848
+ }
1849
+ if (options.headers) {
1850
+ for (const [key, value] of Object.entries(options.headers)) {
1851
+ lines.push(`${key}: ${value}`);
1852
+ }
1853
+ }
1854
+ lines.push("MIME-Version: 1.0");
1855
+ lines.push("Content-Type: text/plain; charset=UTF-8");
1856
+ lines.push("");
1857
+ lines.push(options.body ?? "");
1858
+ return lines.join(`\r
1859
+ `);
1860
+ }
1861
+ function createEmailHeaders(rawEmail) {
1862
+ const headers = new Headers;
1863
+ const lines = rawEmail.split(/\r?\n/);
1864
+ for (const line of lines) {
1865
+ if (!line.trim()) {
1866
+ break;
1867
+ }
1868
+ const colonIndex = line.indexOf(":");
1869
+ if (colonIndex <= 0) {
1870
+ continue;
1871
+ }
1872
+ headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim());
1873
+ }
1874
+ return headers;
1875
+ }
1876
+ function createRawEmailStream(rawEmail) {
1877
+ return new ReadableStream({
1878
+ start(controller) {
1879
+ controller.enqueue(new TextEncoder().encode(rawEmail));
1880
+ controller.close();
1881
+ }
1882
+ });
1883
+ }
1884
+ function resolveEmailHandler(module) {
1885
+ if (typeof module.default === "function") {
1886
+ return module.default;
1887
+ }
1888
+ if (module.default && typeof module.default.email === "function") {
1889
+ return module.default.email.bind(module.default);
1890
+ }
1891
+ if (typeof module.email === "function") {
1892
+ return module.email;
1893
+ }
1894
+ return null;
1895
+ }
1896
+ function getRecordedRawContent(raw) {
1897
+ if (typeof raw === "string") {
1898
+ return raw;
1899
+ }
1900
+ return;
1901
+ }
1902
+ async function send(options) {
1903
+ const raw = buildRawEmail(options);
1904
+ if (emailHandlerPath && configDir && testEnvGetter) {
1905
+ const absolutePath = join3(configDir, emailHandlerPath);
1906
+ const handlerModule = await import(absolutePath);
1907
+ const emailHandler = resolveEmailHandler(handlerModule);
1908
+ if (!emailHandler) {
1909
+ throw new Error(`Email handler at "${emailHandlerPath}" must export a default function or named "email" export.
1910
+ NaN`);
1911
+ }
1912
+ const waitUntilPromises = [];
1913
+ const ctx = {
1914
+ waitUntil(promise) {
1915
+ waitUntilPromises.push(promise);
1916
+ },
1917
+ passThroughOnException() {},
1918
+ props: {}
1919
+ };
1920
+ const runtimeEnv = testEnvGetter();
1921
+ const timestamp = new Date;
1922
+ const message = {
1923
+ from: options.from,
1924
+ to: options.to,
1925
+ headers: createEmailHeaders(raw),
1926
+ raw: createRawEmailStream(raw),
1927
+ rawSize: raw.length,
1928
+ setReject(reason) {
1929
+ throw new Error(`Email rejected: ${reason}`);
1930
+ },
1931
+ async forward(rcptTo) {
1932
+ recordSentEmail({
1933
+ type: "forward",
1934
+ from: options.from,
1935
+ to: rcptTo,
1936
+ raw,
1937
+ timestamp
1938
+ });
1939
+ },
1940
+ async reply(message2) {
1941
+ recordSentEmail({
1942
+ type: "reply",
1943
+ from: message2.from ?? options.to,
1944
+ to: message2.to ?? options.from,
1945
+ raw: getRecordedRawContent(message2.raw),
1946
+ timestamp
1947
+ });
1948
+ }
1949
+ };
1950
+ const emailEvent = createEmailEvent(message, runtimeEnv, ctx);
1951
+ await runWithEventContext(emailEvent, () => emailHandler(emailEvent));
1952
+ await Promise.all(waitUntilPromises);
1953
+ return new Response(JSON.stringify({ ok: true, from: options.from, to: options.to }), {
1954
+ headers: { "Content-Type": "application/json" }
1955
+ });
1956
+ }
1957
+ const url = new URL(`http://localhost:${miniflarePort}/cdn-cgi/handler/email`);
1958
+ url.searchParams.set("from", options.from);
1959
+ url.searchParams.set("to", options.to);
1960
+ const response = await fetch(url.toString(), {
1961
+ method: "POST",
1962
+ headers: {
1963
+ "Content-Type": "text/plain"
1964
+ },
1965
+ body: raw
1966
+ });
1967
+ return response;
1968
+ }
1969
+ function onReceive(callback) {
1970
+ emailListeners.push(callback);
1971
+ return () => {
1972
+ emailListeners = emailListeners.filter((cb) => cb !== callback);
1973
+ };
1974
+ }
1975
+ function getSentEmails() {
1976
+ return [...sentEmails];
1977
+ }
1978
+ function clearSentEmails() {
1979
+ sentEmails = [];
1980
+ }
1981
+ function recordSentEmail(email) {
1982
+ sentEmails.push(email);
1983
+ for (const listener of emailListeners) {
1984
+ try {
1985
+ listener(email);
1986
+ } catch (error) {
1987
+ console.error("[devflare/test] Email listener error:", error);
1988
+ }
1989
+ }
1990
+ }
1991
+ function resetEmailState() {
1992
+ miniflarePort = 8787;
1993
+ emailHandlerPath = null;
1994
+ configDir = null;
1995
+ testEnvGetter = null;
1996
+ emailListeners = [];
1997
+ sentEmails = [];
1998
+ }
1999
+ var email = {
2000
+ send,
2001
+ onReceive,
2002
+ getSentEmails,
2003
+ clearSentEmails
2004
+ };
2005
+
2006
+ // src/test/queue.ts
2007
+ import { join as join4 } from "path";
2008
+ var queueHandlerPath = null;
2009
+ var configDir2 = null;
2010
+ var testEnvGetter2 = null;
2011
+ function configureQueue(options) {
2012
+ queueHandlerPath = options.handlerPath;
2013
+ configDir2 = options.configDir;
2014
+ testEnvGetter2 = options.getEnv;
2015
+ }
2016
+ function resetQueueState() {
2017
+ queueHandlerPath = null;
2018
+ configDir2 = null;
2019
+ testEnvGetter2 = null;
2020
+ }
2021
+ var EMPTY_QUEUE_METADATA = {
2022
+ metrics: {
2023
+ backlogCount: 0,
2024
+ backlogBytes: 0
2025
+ }
2026
+ };
2027
+ function createMessage(options) {
2028
+ const id = options.id ?? crypto.randomUUID();
2029
+ const timestamp = options.timestamp ?? new Date;
2030
+ const attempts = options.attempts ?? 1;
2031
+ let state = "pending";
2032
+ return {
2033
+ id,
2034
+ timestamp,
2035
+ body: options.body,
2036
+ attempts,
2037
+ ack() {
2038
+ state = "acked";
2039
+ },
2040
+ retry(opts) {
2041
+ state = "retried";
2042
+ },
2043
+ retryAll() {
2044
+ state = "retried";
2045
+ },
2046
+ get _state() {
2047
+ return state;
2048
+ }
2049
+ };
2050
+ }
2051
+ function createMessageBatch(messages) {
2052
+ return {
2053
+ queue: "test-queue",
2054
+ metadata: EMPTY_QUEUE_METADATA,
2055
+ messages,
2056
+ ackAll() {
2057
+ for (const msg of messages) {
2058
+ msg.ack();
2059
+ }
2060
+ },
2061
+ retryAll() {
2062
+ for (const msg of messages) {
2063
+ msg.retry();
2064
+ }
2065
+ }
2066
+ };
2067
+ }
2068
+ async function trigger(messages) {
2069
+ if (!queueHandlerPath) {
2070
+ 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)");
2071
+ }
2072
+ if (!configDir2 || !testEnvGetter2) {
2073
+ throw new Error("Queue helper not initialized. Call createTestContext() before using cf.queue.trigger()");
2074
+ }
2075
+ const absolutePath = join4(configDir2, queueHandlerPath);
2076
+ const handlerModule = await import(absolutePath);
2077
+ const queueHandler = handlerModule.default ?? handlerModule.queue;
2078
+ if (typeof queueHandler !== "function") {
2079
+ throw new Error(`Queue handler at "${queueHandlerPath}" must export a default function or named "queue" export.
2080
+ NaN`);
2081
+ }
2082
+ const normalizedMessages = messages.map((msg) => {
2083
+ if (typeof msg === "object" && msg !== null && "body" in msg) {
2084
+ return msg;
2085
+ }
2086
+ return { body: msg };
2087
+ });
2088
+ const mockMessages = normalizedMessages.map((opts) => createMessage(opts));
2089
+ const batch = createMessageBatch(mockMessages);
2090
+ const waitUntilPromises = [];
2091
+ const ctx = {
2092
+ waitUntil(promise) {
2093
+ waitUntilPromises.push(promise);
2094
+ },
2095
+ passThroughOnException() {},
2096
+ props: {}
2097
+ };
2098
+ const env3 = testEnvGetter2();
2099
+ const queueEvent = createQueueEvent(batch, env3, ctx);
2100
+ await runWithEventContext(queueEvent, () => queueHandler(queueEvent));
2101
+ await Promise.all(waitUntilPromises);
2102
+ const acked = [];
2103
+ const retried = [];
2104
+ const failed = [];
2105
+ for (const msg of mockMessages) {
2106
+ switch (msg._state) {
2107
+ case "acked":
2108
+ acked.push(msg.id);
2109
+ break;
2110
+ case "retried":
2111
+ retried.push(msg.id);
2112
+ break;
2113
+ case "failed":
2114
+ failed.push(msg.id);
2115
+ break;
2116
+ }
2117
+ }
2118
+ return {
2119
+ acked,
2120
+ retried,
2121
+ failed,
2122
+ total: mockMessages.length
2123
+ };
2124
+ }
2125
+ async function send2(message) {
2126
+ return trigger([message]);
2127
+ }
2128
+ var queue2 = {
2129
+ trigger,
2130
+ send: send2
2131
+ };
2132
+
2133
+ // src/test/scheduled.ts
2134
+ import { join as join5 } from "path";
2135
+ var scheduledHandlerPath = null;
2136
+ var configDir3 = null;
2137
+ var testEnvGetter3 = null;
2138
+ function configureScheduled(options) {
2139
+ scheduledHandlerPath = options.handlerPath;
2140
+ configDir3 = options.configDir;
2141
+ testEnvGetter3 = options.getEnv;
2142
+ }
2143
+ function resetScheduledState() {
2144
+ scheduledHandlerPath = null;
2145
+ configDir3 = null;
2146
+ testEnvGetter3 = null;
2147
+ }
2148
+ async function trigger2(cronOrOptions) {
2149
+ if (!scheduledHandlerPath) {
2150
+ 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)");
2151
+ }
2152
+ if (!configDir3 || !testEnvGetter3) {
2153
+ throw new Error("Scheduled helper not initialized. Call createTestContext() before using cf.scheduled.trigger()");
2154
+ }
2155
+ const options = typeof cronOrOptions === "string" ? { cron: cronOrOptions } : cronOrOptions ?? {};
2156
+ const cron = options.cron ?? "* * * * *";
2157
+ const scheduledTime = options.scheduledTime instanceof Date ? options.scheduledTime.getTime() : options.scheduledTime ?? Date.now();
2158
+ const absolutePath = join5(configDir3, scheduledHandlerPath);
2159
+ const handlerModule = await import(absolutePath);
2160
+ const scheduledHandler = handlerModule.default ?? handlerModule.scheduled;
2161
+ if (typeof scheduledHandler !== "function") {
2162
+ throw new Error(`Scheduled handler at "${scheduledHandlerPath}" must export a default function or named "scheduled" export.
2163
+ NaN`);
2164
+ }
2165
+ const controller = {
2166
+ scheduledTime,
2167
+ cron,
2168
+ noRetry() {}
2169
+ };
2170
+ const waitUntilPromises = [];
2171
+ const ctx = {
2172
+ waitUntil(promise) {
2173
+ waitUntilPromises.push(promise);
2174
+ },
2175
+ passThroughOnException() {},
2176
+ props: {}
2177
+ };
2178
+ const env3 = testEnvGetter3();
2179
+ const scheduledEvent = createScheduledEvent(controller, env3, ctx);
2180
+ try {
2181
+ await runWithEventContext(scheduledEvent, () => scheduledHandler(scheduledEvent));
2182
+ await Promise.all(waitUntilPromises);
2183
+ return {
2184
+ success: true,
2185
+ cron,
2186
+ scheduledTime
2187
+ };
2188
+ } catch (error) {
2189
+ return {
2190
+ success: false,
2191
+ error: error instanceof Error ? error.message : String(error),
2192
+ cron,
2193
+ scheduledTime
2194
+ };
2195
+ }
2196
+ }
2197
+ var scheduled = {
2198
+ trigger: trigger2
2199
+ };
2200
+
2201
+ // src/test/tail.ts
2202
+ import { join as join6 } from "path";
2203
+ var tailHandlerPath = null;
2204
+ var configDir4 = null;
2205
+ var testEnvGetter4 = null;
2206
+ function configureTail(options) {
2207
+ tailHandlerPath = options.handlerPath;
2208
+ configDir4 = options.configDir;
2209
+ testEnvGetter4 = options.getEnv;
2210
+ }
2211
+ function resetTailState() {
2212
+ tailHandlerPath = null;
2213
+ configDir4 = null;
2214
+ testEnvGetter4 = null;
2215
+ }
2216
+ function createTraceItem(options) {
2217
+ return {
2218
+ scriptName: options.scriptName ?? "test-worker",
2219
+ outcome: options.outcome ?? "ok",
2220
+ eventTimestamp: options.eventTimestamp ?? Date.now(),
2221
+ event: options.event ?? {
2222
+ request: {
2223
+ url: "https://example.com/",
2224
+ method: "GET"
2225
+ }
2226
+ },
2227
+ logs: options.logs ?? [],
2228
+ exceptions: options.exceptions ?? [],
2229
+ diagnosticsChannelEvents: options.diagnosticsChannelEvents ?? [],
2230
+ scriptVersion: options.scriptVersion ?? { id: "test-version" },
2231
+ dispatchNamespace: options.dispatchNamespace,
2232
+ scriptTags: options.scriptTags ?? []
2233
+ };
2234
+ }
2235
+ async function trigger3(items) {
2236
+ if (!tailHandlerPath) {
2237
+ throw new Error("Tail handler not configured. Add a src/tail.ts file exporting tail(), " + "or configure a tail handler before calling cf.tail.trigger().");
2238
+ }
2239
+ if (!configDir4 || !testEnvGetter4) {
2240
+ throw new Error("Tail helper not initialized. Call createTestContext() before using cf.tail.trigger()");
2241
+ }
2242
+ const traceItems = items.map((item) => {
2243
+ if ("eventTimestamp" in item && "outcome" in item && "scriptName" in item) {
2244
+ return item;
2245
+ }
2246
+ return createTraceItem(item);
2247
+ });
2248
+ const absolutePath = join6(configDir4, tailHandlerPath);
2249
+ const handlerModule = await import(absolutePath);
2250
+ const defaultExport = handlerModule.default;
2251
+ const tailHandler = typeof defaultExport === "function" ? defaultExport : defaultExport && typeof defaultExport.tail === "function" ? defaultExport.tail.bind(defaultExport) : handlerModule.tail;
2252
+ if (typeof tailHandler !== "function") {
2253
+ throw new Error(`Tail handler at "${tailHandlerPath}" must export a default function or named "tail" export.
2254
+ NaN`);
2255
+ }
2256
+ const waitUntilPromises = [];
2257
+ const ctx = {
2258
+ waitUntil(promise) {
2259
+ waitUntilPromises.push(promise);
2260
+ },
2261
+ passThroughOnException() {},
2262
+ props: {}
2263
+ };
2264
+ const env3 = testEnvGetter4();
2265
+ const tailEvent = createTailEvent(traceItems, env3, ctx);
2266
+ try {
2267
+ await runWithEventContext(tailEvent, () => tailHandler.length >= 2 ? tailHandler(traceItems, env3, ctx) : tailHandler(tailEvent, env3, ctx));
2268
+ await Promise.all(waitUntilPromises);
2269
+ return {
2270
+ success: true,
2271
+ itemCount: traceItems.length
2272
+ };
2273
+ } catch (error) {
2274
+ return {
2275
+ success: false,
2276
+ error: error instanceof Error ? error.message : String(error),
2277
+ itemCount: traceItems.length
2278
+ };
2279
+ }
2280
+ }
2281
+ function create(options = {}) {
2282
+ return createTraceItem(options);
2283
+ }
2284
+ var tail = {
2285
+ trigger: trigger3,
2286
+ create
2287
+ };
2288
+
2289
+ // src/test/worker.ts
2290
+ import { join as join7 } from "path";
2291
+ var fetchHandlerPath = null;
2292
+ var configDir5 = null;
2293
+ var testEnvGetter5 = null;
2294
+ var fileRoutes = [];
2295
+ function configureWorker(options) {
2296
+ fetchHandlerPath = options.handlerPath;
2297
+ fileRoutes = options.routes ?? [];
2298
+ configDir5 = options.configDir;
2299
+ testEnvGetter5 = options.getEnv;
2300
+ }
2301
+ function resetWorkerState() {
2302
+ fetchHandlerPath = null;
2303
+ fileRoutes = [];
2304
+ configDir5 = null;
2305
+ testEnvGetter5 = null;
2306
+ }
2307
+ async function fetch2(request, options) {
2308
+ if (!fetchHandlerPath && fileRoutes.length === 0) {
2309
+ 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/**).");
2310
+ }
2311
+ if (!configDir5 || !testEnvGetter5) {
2312
+ throw new Error("Worker helper not initialized. Call createTestContext() before using cf.worker.fetch()");
2313
+ }
2314
+ const workerConfigDir = configDir5;
2315
+ const getEnv = testEnvGetter5;
2316
+ let req;
2317
+ if (typeof request === "string") {
2318
+ const url = request.startsWith("http") ? request : `http://localhost${request.startsWith("/") ? "" : "/"}${request}`;
2319
+ const headers = new Headers(options?.headers);
2320
+ let body;
2321
+ if (options?.body !== undefined) {
2322
+ if (typeof options.body === "string") {
2323
+ body = options.body;
2324
+ } else {
2325
+ body = JSON.stringify(options.body);
2326
+ if (!headers.has("Content-Type")) {
2327
+ headers.set("Content-Type", "application/json");
2328
+ }
2329
+ }
2330
+ }
2331
+ req = new Request(url, {
2332
+ method: options?.method ?? "GET",
2333
+ headers,
2334
+ body
2335
+ });
2336
+ } else {
2337
+ req = request;
2338
+ }
2339
+ const handlerModule = fetchHandlerPath ? await import(join7(workerConfigDir, fetchHandlerPath)) : {};
2340
+ const routeModules = await Promise.all(fileRoutes.map(async (route) => {
2341
+ return {
2342
+ ...route,
2343
+ module: await import(join7(workerConfigDir, route.filePath))
2344
+ };
2345
+ }));
2346
+ const methodExports = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "ALL"];
2347
+ const hasMethodHandler = methodExports.some((method) => {
2348
+ return typeof handlerModule[method] === "function" || typeof handlerModule.default?.[method] === "function";
2349
+ });
2350
+ if (!resolveFetchHandler(handlerModule) && !hasMethodHandler && routeModules.length === 0) {
2351
+ throw new Error(`Fetch handler at "${fetchHandlerPath}" must export one of:
2352
+ - request-wide "handle" middleware
2353
+ - named "fetch"
2354
+ - default fetch handler
2355
+ - HTTP method exports such as "GET" or "POST"`);
2356
+ }
2357
+ const waitUntilPromises = [];
2358
+ const ctx = {
2359
+ waitUntil(promise) {
2360
+ waitUntilPromises.push(promise);
2361
+ },
2362
+ passThroughOnException() {},
2363
+ props: {}
2364
+ };
2365
+ const env3 = getEnv();
2366
+ const initialRouteMatch = routeModules.length > 0 ? matchFetchRoute(routeModules, req) : null;
2367
+ const fetchEvent = createFetchEvent(req, env3, ctx, {
2368
+ params: initialRouteMatch?.params ?? {}
2369
+ });
2370
+ const response = await runWithEventContext(fetchEvent, () => invokeFetchModule(handlerModule, fetchEvent, routeModules.length > 0 ? createRouteResolve(routeModules, fetchEvent) : undefined));
2371
+ return response;
2372
+ }
2373
+ async function get(path, headers) {
2374
+ return fetch2(path, { method: "GET", headers });
2375
+ }
2376
+ async function post(path, body, headers) {
2377
+ return fetch2(path, { method: "POST", body, headers });
2378
+ }
2379
+ async function put(path, body, headers) {
2380
+ return fetch2(path, { method: "PUT", body, headers });
2381
+ }
2382
+ async function del(path, headers) {
2383
+ return fetch2(path, { method: "DELETE", headers });
2384
+ }
2385
+ async function patch(path, body, headers) {
2386
+ return fetch2(path, { method: "PATCH", body, headers });
2387
+ }
2388
+ var worker = {
2389
+ fetch: fetch2,
2390
+ get,
2391
+ post,
2392
+ put,
2393
+ delete: del,
2394
+ patch
2395
+ };
2396
+
2397
+ // src/test/simple-context-env.ts
2398
+ function configureSurfaceHandlers(input) {
2399
+ const { handlerPaths, configDir: configDir6, activePort, getEnv } = input;
2400
+ configureQueue({
2401
+ handlerPath: handlerPaths.queue,
2402
+ configDir: configDir6,
2403
+ getEnv
2404
+ });
2405
+ configureScheduled({
2406
+ handlerPath: handlerPaths.scheduled,
2407
+ configDir: configDir6,
2408
+ getEnv
2409
+ });
2410
+ configureWorker({
2411
+ handlerPath: handlerPaths.fetch,
2412
+ routes: handlerPaths.routes?.routes.map((route) => ({
2413
+ filePath: route.filePath,
2414
+ routePath: route.routePath,
2415
+ segments: route.segments
2416
+ })) ?? [],
2417
+ configDir: configDir6,
2418
+ getEnv
2419
+ });
2420
+ configureTail({
2421
+ handlerPath: handlerPaths.tail,
2422
+ configDir: configDir6,
2423
+ getEnv
2424
+ });
2425
+ configureEmail({
2426
+ port: activePort,
2427
+ handlerPath: handlerPaths.email,
2428
+ configDir: configDir6,
2429
+ getEnv
2430
+ });
2431
+ }
2432
+ function createBridgeEnvAccessor(state, hints, shouldPreferBridgeBinding) {
2433
+ return new Proxy({}, {
2434
+ get(_, prop) {
2435
+ const hint = hints[prop];
2436
+ const prefersBridgeBinding = shouldPreferBridgeBinding(hint);
2437
+ if (state.remoteBindings && prop in state.remoteBindings) {
2438
+ return state.remoteBindings[prop];
2439
+ }
2440
+ if (!prefersBridgeBinding && state.miniflareBindings && prop in state.miniflareBindings) {
2441
+ return state.miniflareBindings[prop];
2442
+ }
2443
+ if (state.envProxy) {
2444
+ return state.envProxy[prop];
2445
+ }
2446
+ if (prefersBridgeBinding && state.miniflareBindings && prop in state.miniflareBindings) {
2447
+ return state.miniflareBindings[prop];
2448
+ }
2449
+ return;
2450
+ },
2451
+ has(_, prop) {
2452
+ return Boolean(state.remoteBindings && prop in state.remoteBindings || state.miniflareBindings && prop in state.miniflareBindings || state.envProxy !== null);
2453
+ }
2454
+ });
2455
+ }
2456
+ function createMultiWorkerEnvAccessor(state) {
2457
+ return new Proxy({}, {
2458
+ get(_, prop) {
2459
+ if (state.remoteBindings && prop in state.remoteBindings) {
2460
+ return state.remoteBindings[prop];
2461
+ }
2462
+ if (state.miniflareBindings && prop in state.miniflareBindings) {
2463
+ return state.miniflareBindings[prop];
2464
+ }
2465
+ return;
2466
+ },
2467
+ has(_, prop) {
2468
+ return Boolean(state.remoteBindings && prop in state.remoteBindings || state.miniflareBindings && prop in state.miniflareBindings);
2469
+ }
2470
+ });
2471
+ }
2472
+
2473
+ // src/test/simple-context-handlers.ts
2474
+ import { join as join8 } from "path";
2475
+ var DEFAULT_FETCH_PATH = "src/fetch.ts";
2476
+ var DEFAULT_QUEUE_PATH = "src/queue.ts";
2477
+ var DEFAULT_SCHEDULED_PATH = "src/scheduled.ts";
2478
+ var DEFAULT_EMAIL_PATH = "src/email.ts";
2479
+ var DEFAULT_TAIL_PATH = "src/tail.ts";
2480
+ async function resolveHandlerPath(configDir6, configValue, defaultPath) {
2481
+ if (typeof configValue === "string") {
2482
+ return configValue;
2483
+ }
2484
+ if (configValue === false) {
2485
+ return null;
2486
+ }
2487
+ const defaultAbsolute = join8(configDir6, defaultPath);
2488
+ try {
2489
+ const fs = await import("fs/promises");
2490
+ await fs.access(defaultAbsolute);
2491
+ return defaultPath;
2492
+ } catch {
2493
+ return null;
2494
+ }
2495
+ }
2496
+ async function resolveHandlerPaths(configDir6, config) {
2497
+ const [fetch3, queue3, scheduled2, email2, tail2, routes] = await Promise.all([
2498
+ resolveHandlerPath(configDir6, config.files?.fetch, DEFAULT_FETCH_PATH),
2499
+ resolveHandlerPath(configDir6, config.files?.queue, DEFAULT_QUEUE_PATH),
2500
+ resolveHandlerPath(configDir6, config.files?.scheduled, DEFAULT_SCHEDULED_PATH),
2501
+ resolveHandlerPath(configDir6, config.files?.email, DEFAULT_EMAIL_PATH),
2502
+ resolveHandlerPath(configDir6, config.files?.tail, DEFAULT_TAIL_PATH),
2503
+ discoverRoutes(configDir6, config)
2504
+ ]);
2505
+ return { fetch: fetch3, queue: queue3, scheduled: scheduled2, email: email2, tail: tail2, routes };
2506
+ }
2507
+
2508
+ // src/test/simple-context-lifecycle.ts
2509
+ import { dirname as dirname4, resolve as resolve2 } from "path";
2510
+
2511
+ // src/test/containers.ts
2512
+ import { createHash, randomUUID } from "node:crypto";
2513
+ import { existsSync as existsSync2 } from "node:fs";
2514
+ import { stat } from "node:fs/promises";
2515
+ import { createConnection } from "node:net";
2516
+ import { basename, dirname as dirname3, isAbsolute, join as join9, resolve } from "node:path";
2517
+ import { setTimeout as delay } from "node:timers/promises";
2518
+ import { execa } from "execa";
2519
+ var realContainerCommandRunner = {
2520
+ async exec(command, args, options = {}) {
2521
+ try {
2522
+ const result = await execa(command, args, {
2523
+ cwd: options.cwd,
2524
+ env: options.env,
2525
+ reject: false,
2526
+ timeout: options.timeoutMs ?? 15000
2527
+ });
2528
+ return {
2529
+ exitCode: result.exitCode ?? 0,
2530
+ stdout: String(result.stdout ?? ""),
2531
+ stderr: String(result.stderr ?? "")
2532
+ };
2533
+ } catch (error) {
2534
+ const err = error;
2535
+ return {
2536
+ exitCode: err.exitCode ?? 1,
2537
+ stdout: String(err.stdout ?? ""),
2538
+ stderr: String(err.stderr ?? err.message ?? "Container command failed")
2539
+ };
2540
+ }
2541
+ }
2542
+ };
2543
+ async function detectContainerEngine(options = {}) {
2544
+ const runner = options.runner ?? realContainerCommandRunner;
2545
+ const candidates = options.engine && options.engine !== "auto" ? [options.engine] : ["docker", "podman"];
2546
+ const checked = [];
2547
+ for (const engine of candidates) {
2548
+ let result;
2549
+ try {
2550
+ result = await runner.exec(engine, ["info"], { timeoutMs: 1e4 });
2551
+ } catch (error) {
2552
+ result = {
2553
+ exitCode: 1,
2554
+ stdout: "",
2555
+ stderr: error instanceof Error ? error.message : String(error)
2556
+ };
2557
+ }
2558
+ if (result.exitCode === 0) {
2559
+ checked.push({ engine, available: true });
2560
+ return { available: true, engine, checked };
2561
+ }
2562
+ checked.push({
2563
+ engine,
2564
+ available: false,
2565
+ reason: formatCommandFailure(result)
2566
+ });
2567
+ }
2568
+ return {
2569
+ available: false,
2570
+ reason: checked.map((check) => `${check.engine}: ${check.reason ?? "not available"}`).join("; "),
2571
+ checked
2572
+ };
2573
+ }
2574
+ async function getContainerSkipReason(options = {}) {
2575
+ const env3 = options.env ?? process.env;
2576
+ if (!isTruthyEnvFlag(env3.DEVFLARE_CONTAINER_TESTS)) {
2577
+ return "Container tests require DEVFLARE_CONTAINER_TESTS=1 because they launch local Docker/Podman containers.";
2578
+ }
2579
+ const status = await detectContainerEngine(options);
2580
+ if (!status.available) {
2581
+ return `No reachable Docker or Podman engine found: ${status.reason}`;
2582
+ }
2583
+ return null;
2584
+ }
2585
+ function createContainerManager(options = {}) {
2586
+ const active = new Set;
2587
+ const getRunner = () => options.runner ?? realContainerCommandRunner;
2588
+ const manager = {
2589
+ detectEngine(engineOptions = {}) {
2590
+ return detectContainerEngine({
2591
+ engine: engineOptions.engine ?? options.engine,
2592
+ runner: engineOptions.runner ?? getRunner()
2593
+ });
2594
+ },
2595
+ async start(className, startOptions) {
2596
+ const cwd = options.cwd ?? getCallerDirectory();
2597
+ const resolved = await resolveContainerStart(className, startOptions, cwd);
2598
+ const status = await manager.detectEngine();
2599
+ const engine = getAvailableContainerEngine(status, className);
2600
+ const runner = getRunner();
2601
+ const offline = startOptions.offline ?? true;
2602
+ const prepared = await prepareImage({
2603
+ engine,
2604
+ runner,
2605
+ image: resolved.image,
2606
+ className,
2607
+ configDir: resolved.configDir,
2608
+ imageBuildContext: resolved.config?.imageBuildContext,
2609
+ offline
2610
+ });
2611
+ const host = startOptions.host ?? "127.0.0.1";
2612
+ const hostPort = startOptions.hostPort ?? await (options.allocatePort ?? getAvailablePort)();
2613
+ const containerName = makeContainerName(className, startOptions.instance);
2614
+ const runArgs = buildRunArgs({
2615
+ name: containerName,
2616
+ className,
2617
+ image: prepared.image,
2618
+ host,
2619
+ hostPort,
2620
+ containerPort: startOptions.port,
2621
+ envVars: startOptions.envVars,
2622
+ entrypoint: startOptions.entrypoint,
2623
+ command: startOptions.command
2624
+ });
2625
+ const runResult = await runner.exec(engine, runArgs, {
2626
+ cwd: resolved.configDir,
2627
+ env: options.env ?? process.env
2628
+ });
2629
+ if (runResult.exitCode !== 0) {
2630
+ throw new Error(`Failed to start Devflare container "${className}": ${formatCommandFailure(runResult)}`);
2631
+ }
2632
+ const instance = new LocalDevflareContainer({
2633
+ id: runResult.stdout.trim() || containerName,
2634
+ name: containerName,
2635
+ className,
2636
+ engine,
2637
+ host,
2638
+ hostPort,
2639
+ port: startOptions.port,
2640
+ runner,
2641
+ fetchImpl: options.fetch ?? fetch,
2642
+ onDispose: (container) => active.delete(container)
2643
+ });
2644
+ active.add(instance);
2645
+ await waitForContainerReadiness(instance, startOptions, options.waitForPort ?? waitForTcpPort);
2646
+ return instance;
2647
+ },
2648
+ async stopAll() {
2649
+ await Promise.all([...active].map((container) => container.stop()));
2650
+ }
2651
+ };
2652
+ return manager;
2653
+ }
2654
+ var defaultContainerManager = createContainerManager();
2655
+ var containers = defaultContainerManager;
2656
+ async function stopActiveContainers() {
2657
+ await defaultContainerManager.stopAll();
2658
+ }
2659
+ function getAvailableContainerEngine(status, className) {
2660
+ if (!status.available) {
2661
+ throw new Error(`Cannot start Devflare container "${className}": ${status.reason}`);
2662
+ }
2663
+ return status.engine;
2664
+ }
2665
+ async function waitForContainerReadiness(instance, options, waitForPort) {
2666
+ if (!(options.waitForReady ?? true)) {
2667
+ return;
2668
+ }
2669
+ try {
2670
+ await waitForPort(instance.host, instance.hostPort, options.readyTimeoutMs ?? 20000);
2671
+ } catch (error) {
2672
+ await instance.destroy();
2673
+ throw error;
2674
+ }
2675
+ }
2676
+ async function resolveContainerStart(className, options, cwd) {
2677
+ if (options.image) {
2678
+ return {
2679
+ configDir: cwd,
2680
+ image: options.image
2681
+ };
2682
+ }
2683
+ const { config, configDir: configDir6 } = await loadContainerConfig(options.configPath, cwd);
2684
+ const container = config.containers?.find((candidate) => candidate.className === className || candidate.name === className);
2685
+ if (!container) {
2686
+ throw new Error(`Container "${className}" was not found in devflare config.`);
2687
+ }
2688
+ return {
2689
+ configDir: configDir6,
2690
+ image: container.image,
2691
+ config: container
2692
+ };
2693
+ }
2694
+ async function loadContainerConfig(configPath, cwd) {
2695
+ const absolutePath = configPath ? resolve(cwd, configPath) : await findNearestConfig(cwd);
2696
+ if (!absolutePath) {
2697
+ throw new Error(`Could not find a devflare config file for container lookup. Searched upward from: ${cwd}`);
2698
+ }
2699
+ const configDir6 = dirname3(absolutePath);
2700
+ const loadedConfig = await loadConfig({
2701
+ cwd: configDir6,
2702
+ configFile: basename(absolutePath)
2703
+ });
2704
+ const envResolvedConfig = await resolveConfigEnvVars(loadedConfig, {
2705
+ cwd: configDir6,
2706
+ configPath: absolutePath,
2707
+ mode: "dev"
2708
+ });
2709
+ const config = await applyLocalDevVarsToConfig(envResolvedConfig, {
2710
+ cwd: configDir6,
2711
+ configPath: absolutePath
2712
+ });
2713
+ return { config, configDir: configDir6 };
2714
+ }
2715
+ async function prepareImage(options) {
2716
+ if (looksLikeLocalPath(options.image)) {
2717
+ return {
2718
+ image: await buildLocalImage(options)
2719
+ };
2720
+ }
2721
+ const inspect = await options.runner.exec(options.engine, ["image", "inspect", options.image], {
2722
+ cwd: options.configDir
2723
+ });
2724
+ if (inspect.exitCode === 0) {
2725
+ return { image: options.image };
2726
+ }
2727
+ if (options.offline) {
2728
+ throw new Error(`Container image "${options.image}" is not present locally. Devflare container tests are offline-first; pull/build the image ahead of time or pass offline: false.`);
2729
+ }
2730
+ const pull = await options.runner.exec(options.engine, ["pull", options.image], {
2731
+ cwd: options.configDir
2732
+ });
2733
+ if (pull.exitCode !== 0) {
2734
+ throw new Error(`Failed to pull container image "${options.image}": ${formatCommandFailure(pull)}`);
2735
+ }
2736
+ return { image: options.image };
2737
+ }
2738
+ async function buildLocalImage(options) {
2739
+ const imagePath = resolve(options.configDir, options.image);
2740
+ const imageStat = await stat(imagePath);
2741
+ const dockerfile = imageStat.isDirectory() ? join9(imagePath, "Dockerfile") : imagePath;
2742
+ const context2 = resolve(options.configDir, options.imageBuildContext ?? (imageStat.isDirectory() ? options.image : dirname3(options.image)));
2743
+ if (!existsSync2(dockerfile)) {
2744
+ throw new Error(`Container Dockerfile does not exist: ${dockerfile}`);
2745
+ }
2746
+ const tag = makeLocalImageTag(options.className, options.configDir, options.image);
2747
+ const args = [
2748
+ "build",
2749
+ ...options.offline ? [getOfflineBuildPullArg(options.engine)] : [],
2750
+ "-t",
2751
+ tag,
2752
+ "-f",
2753
+ dockerfile,
2754
+ context2
2755
+ ];
2756
+ const result = await options.runner.exec(options.engine, args, {
2757
+ cwd: options.configDir
2758
+ });
2759
+ if (result.exitCode !== 0) {
2760
+ throw new Error(`Failed to build local container image "${options.image}": ${formatCommandFailure(result)}`);
2761
+ }
2762
+ return tag;
2763
+ }
2764
+ function buildRunArgs(options) {
2765
+ const args = [
2766
+ "run",
2767
+ "-d",
2768
+ "--name",
2769
+ options.name,
2770
+ "--label",
2771
+ "devflare.managed=true",
2772
+ "--label",
2773
+ `devflare.container.class=${options.className}`,
2774
+ "-p",
2775
+ `${options.host}:${options.hostPort}:${options.containerPort}`
2776
+ ];
2777
+ for (const [key, value] of Object.entries(options.envVars ?? {})) {
2778
+ args.push("-e", `${key}=${value}`);
2779
+ }
2780
+ const command = [...options.command ?? []];
2781
+ if (options.entrypoint && options.entrypoint.length > 0) {
2782
+ args.push("--entrypoint", options.entrypoint[0]);
2783
+ command.unshift(...options.entrypoint.slice(1));
2784
+ }
2785
+ args.push(options.image, ...command);
2786
+ return args;
2787
+ }
2788
+
2789
+ class LocalDevflareContainer {
2790
+ id;
2791
+ name;
2792
+ className;
2793
+ engine;
2794
+ host;
2795
+ hostPort;
2796
+ port;
2797
+ runner;
2798
+ fetchImpl;
2799
+ onDispose;
2800
+ disposed = false;
2801
+ constructor(options) {
2802
+ this.id = options.id;
2803
+ this.name = options.name;
2804
+ this.className = options.className;
2805
+ this.engine = options.engine;
2806
+ this.host = options.host;
2807
+ this.hostPort = options.hostPort;
2808
+ this.port = options.port;
2809
+ this.runner = options.runner;
2810
+ this.fetchImpl = options.fetchImpl;
2811
+ this.onDispose = options.onDispose;
2812
+ }
2813
+ fetch(input, init) {
2814
+ return fetchWithStartupRetries(this.fetchImpl, this.toLocalRequest(input, init));
2815
+ }
2816
+ async logs() {
2817
+ const result = await this.runner.exec(this.engine, ["logs", this.name]);
2818
+ if (result.exitCode !== 0) {
2819
+ throw new Error(`Failed to read logs for Devflare container "${this.name}": ${formatCommandFailure(result)}`);
2820
+ }
2821
+ return result.stdout;
2822
+ }
2823
+ async getState() {
2824
+ const result = await this.runner.exec(this.engine, [
2825
+ "inspect",
2826
+ "--format",
2827
+ "{{json .State}}",
2828
+ this.name
2829
+ ]);
2830
+ if (result.exitCode !== 0) {
2831
+ throw new Error(`Failed to inspect Devflare container "${this.name}": ${formatCommandFailure(result)}`);
2832
+ }
2833
+ const parsed = JSON.parse(result.stdout || "{}");
2834
+ return {
2835
+ status: parsed.Status ?? (parsed.Running ? "running" : "stopped"),
2836
+ running: Boolean(parsed.Running),
2837
+ ...typeof parsed.ExitCode === "number" && { exitCode: parsed.ExitCode }
2838
+ };
2839
+ }
2840
+ async stop() {
2841
+ if (this.disposed)
2842
+ return;
2843
+ try {
2844
+ await this.runner.exec(this.engine, ["stop", this.name]);
2845
+ } finally {
2846
+ await this.runner.exec(this.engine, ["rm", "-f", this.name]);
2847
+ this.disposed = true;
2848
+ this.onDispose(this);
2849
+ }
2850
+ }
2851
+ async destroy() {
2852
+ if (this.disposed)
2853
+ return;
2854
+ await this.runner.exec(this.engine, ["rm", "-f", this.name]);
2855
+ this.disposed = true;
2856
+ this.onDispose(this);
2857
+ }
2858
+ toLocalRequest(input, init) {
2859
+ const base = `http://${this.host}:${this.hostPort}/`;
2860
+ if (input instanceof Request) {
2861
+ const source = init ? new Request(input, init) : input;
2862
+ const sourceUrl2 = new URL(source.url);
2863
+ const localUrl2 = new URL(`${sourceUrl2.pathname}${sourceUrl2.search}`, base);
2864
+ return new Request(localUrl2, {
2865
+ method: source.method,
2866
+ headers: source.headers,
2867
+ body: source.body,
2868
+ redirect: source.redirect,
2869
+ signal: source.signal
2870
+ });
2871
+ }
2872
+ const sourceUrl = new URL(String(input), base);
2873
+ const localUrl = new URL(`${sourceUrl.pathname}${sourceUrl.search}`, base);
2874
+ return new Request(localUrl, init);
2875
+ }
2876
+ }
2877
+ async function fetchWithStartupRetries(fetchImpl, request) {
2878
+ if (!canRetryRequest(request)) {
2879
+ return fetchImpl(request);
2880
+ }
2881
+ const deadline = Date.now() + 5000;
2882
+ let lastError;
2883
+ while (Date.now() < deadline) {
2884
+ try {
2885
+ return await fetchImpl(request.clone());
2886
+ } catch (error) {
2887
+ if (!isTransientContainerFetchError(error)) {
2888
+ throw error;
2889
+ }
2890
+ lastError = error;
2891
+ await delay(100);
2892
+ }
2893
+ }
2894
+ throw lastError;
2895
+ }
2896
+ function canRetryRequest(request) {
2897
+ return (request.method === "GET" || request.method === "HEAD") && request.body === null;
2898
+ }
2899
+ function isTransientContainerFetchError(error) {
2900
+ const value = error;
2901
+ const code = value.code ?? value.cause?.code;
2902
+ if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "UND_ERR_SOCKET") {
2903
+ return true;
2904
+ }
2905
+ return typeof value.message === "string" && (value.message.includes("socket connection was closed") || value.message.includes("fetch failed"));
2906
+ }
2907
+ async function waitForTcpPort(host, port, timeoutMs) {
2908
+ const start = Date.now();
2909
+ let lastError;
2910
+ while (Date.now() - start < timeoutMs) {
2911
+ try {
2912
+ await connectOnce(host, port);
2913
+ return;
2914
+ } catch (error) {
2915
+ lastError = error;
2916
+ await delay(100);
2917
+ }
2918
+ }
2919
+ const message = lastError instanceof Error ? lastError.message : "timed out";
2920
+ throw new Error(`Timed out waiting for container port ${host}:${port}: ${message}`);
2921
+ }
2922
+ function connectOnce(host, port) {
2923
+ return new Promise((resolveConnection, rejectConnection) => {
2924
+ const socket = createConnection({ host, port });
2925
+ socket.once("connect", () => {
2926
+ socket.destroy();
2927
+ resolveConnection();
2928
+ });
2929
+ socket.once("error", rejectConnection);
2930
+ });
2931
+ }
2932
+ function looksLikeLocalPath(image) {
2933
+ return image === "Dockerfile" || image.startsWith(".") || image.startsWith("/") || image.startsWith("\\") || isAbsolute(image) || image.endsWith("/Dockerfile") || image.endsWith("\\Dockerfile");
2934
+ }
2935
+ function getOfflineBuildPullArg(engine) {
2936
+ return engine === "podman" ? "--pull=never" : "--pull=false";
2937
+ }
2938
+ function makeLocalImageTag(className, configDir6, image) {
2939
+ const hash = createHash("sha256").update(`${configDir6}:${image}`).digest("hex").slice(0, 12);
2940
+ return `devflare-local-${sanitizeName(className)}:${hash}`;
2941
+ }
2942
+ function makeContainerName(className, instance) {
2943
+ return `devflare-${sanitizeName(className)}-${sanitizeName(instance ?? "default")}-${randomUUID().slice(0, 8)}`;
2944
+ }
2945
+ function sanitizeName(value) {
2946
+ return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "container";
2947
+ }
2948
+ function formatCommandFailure(result) {
2949
+ return (result.stderr || result.stdout || `exit code ${result.exitCode}`).trim();
2950
+ }
2951
+ function isTruthyEnvFlag(value) {
2952
+ return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
2953
+ }
2954
+
2955
+ // src/test/simple-context-lifecycle.ts
2956
+ async function resolveTestContextConfig(configPath, callerDir = getCallerDirectory()) {
2957
+ let absolutePath;
2958
+ if (configPath) {
2959
+ absolutePath = resolve2(callerDir, configPath);
2960
+ } else {
2961
+ const found = await findNearestConfig(callerDir);
2962
+ if (!found) {
2963
+ throw new Error(`Could not find a devflare config file. Searched upward from: ${callerDir}
2964
+ ` + `Expected one of: devflare.config.ts, devflare.config.mts, devflare.config.js, devflare.config.mjs
2965
+ ` + `Either create a config file or provide an explicit path: createTestContext('./path/to/config.ts')`);
2966
+ }
2967
+ absolutePath = found;
2968
+ }
2969
+ const configDir6 = dirname4(absolutePath);
2970
+ const loadedConfig = await loadConfig({
2971
+ cwd: configDir6,
2972
+ configFile: absolutePath.split(/[/\\]/).pop()
2973
+ });
2974
+ const envResolvedConfig = await resolveConfigEnvVars(loadedConfig, {
2975
+ cwd: configDir6,
2976
+ configPath: absolutePath,
2977
+ mode: "dev"
2978
+ });
2979
+ const config = await applyLocalDevVarsToConfig(envResolvedConfig, {
2980
+ cwd: configDir6,
2981
+ configPath: absolutePath
2982
+ });
2983
+ return { absolutePath, configDir: configDir6, config };
2984
+ }
2985
+ function createDisposeContext(state) {
2986
+ return async () => {
2987
+ if (state.client) {
2988
+ await state.client.disconnect();
2989
+ state.client = null;
2990
+ }
2991
+ if (state.miniflare) {
2992
+ await state.miniflare.dispose();
2993
+ state.miniflare = null;
2994
+ }
2995
+ await disposeLocalWorkerLoaderBindings();
2996
+ await stopActiveContainers();
2997
+ state.envProxy = null;
2998
+ state.transportDecode = null;
2999
+ state.remoteBindings = null;
3000
+ state.miniflareBindings = null;
3001
+ resetQueueState();
3002
+ resetScheduledState();
3003
+ resetWorkerState();
3004
+ resetTailState();
3005
+ resetEmailState();
3006
+ __clearTestContext();
3007
+ };
3008
+ }
3009
+
3010
+ // src/test/simple-context-startup.ts
3011
+ var TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS = 3;
3012
+ var TEST_CONTEXT_STARTUP_RETRY_DELAY_MS = 75;
3013
+ var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS = 8;
3014
+ var TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS = 150;
3015
+ function isRetriableTestContextStartupError(error) {
3016
+ if (!(error instanceof Error)) {
3017
+ return false;
3018
+ }
3019
+ const message = error.message.toLowerCase();
3020
+ return message.includes("websocket connection failed") || message.includes("connection timeout: ws://") || message.includes("econnrefused") || message.includes("eaddrinuse") || message.includes("address already in use");
3021
+ }
3022
+ async function waitForTestContextStartupRetry() {
3023
+ await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_STARTUP_RETRY_DELAY_MS));
3024
+ }
3025
+ async function waitForBridgeClientRetry() {
3026
+ await new Promise((resolve3) => setTimeout(resolve3, TEST_CONTEXT_BRIDGE_CONNECT_RETRY_DELAY_MS));
3027
+ }
3028
+ async function connectBridgeClientWithRetry(url) {
3029
+ let lastError;
3030
+ for (let attempt = 1;attempt <= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS; attempt++) {
3031
+ const client = new BridgeClient({ url });
3032
+ try {
3033
+ await client.connect();
3034
+ return client;
3035
+ } catch (error) {
3036
+ lastError = error;
3037
+ client.disconnect();
3038
+ if (attempt >= TEST_CONTEXT_BRIDGE_CONNECT_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
3039
+ throw error;
3040
+ }
3041
+ await waitForBridgeClientRetry();
3042
+ }
3043
+ }
3044
+ throw lastError instanceof Error ? lastError : new Error("Bridge-backed test context could not connect to the WebSocket gateway.");
3045
+ }
3046
+ function expandLocalBindingWorkers(mfConfig) {
3047
+ const auxiliaryWorkers = [
3048
+ ...mfConfig.__devflareLocalSecretWorkers ?? [],
3049
+ ...mfConfig.__devflareLocalBindingWorkers ?? []
3050
+ ];
3051
+ if (!Array.isArray(auxiliaryWorkers) || auxiliaryWorkers.length === 0) {
3052
+ return mfConfig;
3053
+ }
3054
+ const {
3055
+ __devflareLocalSecretWorkers,
3056
+ __devflareLocalBindingWorkers,
3057
+ port,
3058
+ host,
3059
+ log,
3060
+ kvPersist,
3061
+ r2Persist,
3062
+ d1Persist,
3063
+ durableObjectsPersist,
3064
+ workflowsPersist,
3065
+ imagesPersist,
3066
+ ...primaryWorker
3067
+ } = mfConfig;
3068
+ const primaryWorkerName = typeof primaryWorker.name === "string" ? primaryWorker.name : "primary";
3069
+ return {
3070
+ ...port !== undefined && { port },
3071
+ ...host && { host },
3072
+ ...log && { log },
3073
+ ...kvPersist && { kvPersist },
3074
+ ...r2Persist && { r2Persist },
3075
+ ...d1Persist && { d1Persist },
3076
+ ...durableObjectsPersist && { durableObjectsPersist },
3077
+ ...workflowsPersist && { workflowsPersist },
3078
+ ...imagesPersist && { imagesPersist },
3079
+ workers: [
3080
+ {
3081
+ ...primaryWorker,
3082
+ name: primaryWorkerName
3083
+ },
3084
+ ...auxiliaryWorkers
3085
+ ]
3086
+ };
3087
+ }
3088
+ async function startBridgeBackedTestContext(mfConfig) {
3089
+ const { Miniflare } = await import("miniflare");
3090
+ for (let attempt = 1;attempt <= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS; attempt++) {
3091
+ const port = await getAvailablePort();
3092
+ let miniflare = null;
3093
+ let client = null;
3094
+ try {
3095
+ miniflare = new Miniflare(expandLocalBindingWorkers({
3096
+ ...mfConfig,
3097
+ port
3098
+ }));
3099
+ await miniflare.ready;
3100
+ const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
3101
+ client = await connectBridgeClientWithRetry(`ws://localhost:${port}`);
3102
+ return {
3103
+ port,
3104
+ client,
3105
+ miniflare,
3106
+ miniflareBindings
3107
+ };
3108
+ } catch (error) {
3109
+ client?.disconnect();
3110
+ if (miniflare) {
3111
+ try {
3112
+ await miniflare.dispose();
3113
+ } catch {}
3114
+ }
3115
+ if (attempt >= TEST_CONTEXT_STARTUP_RETRY_ATTEMPTS || !isRetriableTestContextStartupError(error)) {
3116
+ throw error;
3117
+ }
3118
+ await waitForTestContextStartupRetry();
3119
+ }
3120
+ }
3121
+ throw new Error("Bridge-backed test context startup exhausted all retry attempts.");
3122
+ }
3123
+
3124
+ // src/test/simple-context-runtime.ts
3125
+ async function bootTestRuntime(mfConfig, usesMultiWorker) {
3126
+ if (usesMultiWorker) {
3127
+ const { Miniflare } = await import("miniflare");
3128
+ const activePort = await getAvailablePort();
3129
+ const miniflare = new Miniflare({
3130
+ ...mfConfig,
3131
+ port: activePort
3132
+ });
3133
+ await miniflare.ready;
3134
+ const miniflareBindings = wrapEnvSendEmailBindings(await miniflare.getBindings());
3135
+ return { activePort, miniflare, miniflareBindings, client: null };
3136
+ }
3137
+ const started = await startBridgeBackedTestContext(mfConfig);
3138
+ return {
3139
+ activePort: started.port,
3140
+ miniflare: started.miniflare,
3141
+ miniflareBindings: started.miniflareBindings,
3142
+ client: started.client
3143
+ };
3144
+ }
3145
+
3146
+ // src/test/simple-context-transport.ts
3147
+ import { join as join10 } from "path";
3148
+ async function loadTransportDecoders(configDir6, transportFile) {
3149
+ const transportPath = join10(configDir6, transportFile);
3150
+ const transportModule = await import(transportPath);
3151
+ if (!transportModule.transport) {
3152
+ console.warn(`[devflare] Warning: Transport file "${transportFile}" does not export a named "transport" object.
3153
+ Expected: export const transport = { ... }
3154
+ Transport encoding/decoding will be disabled.`);
3155
+ return null;
3156
+ }
3157
+ const decoders = new Map;
3158
+ for (const [typeName, transporter] of Object.entries(transportModule.transport)) {
3159
+ const t = transporter;
3160
+ decoders.set(typeName, t.decode);
3161
+ }
3162
+ return decoders;
3163
+ }
3164
+ function decodeTransportValue(decoders, value) {
3165
+ if (!decoders || value === null || typeof value !== "object") {
3166
+ return value;
3167
+ }
3168
+ if ("__transport" in value) {
3169
+ const encoded = value;
3170
+ const decoder = decoders.get(encoded.__transport);
3171
+ if (decoder) {
3172
+ return decoder(encoded.value);
3173
+ }
3174
+ }
3175
+ if (Array.isArray(value)) {
3176
+ return value.map((item) => decodeTransportValue(decoders, item));
3177
+ }
3178
+ const result = {};
3179
+ for (const [k, v] of Object.entries(value)) {
3180
+ result[k] = decodeTransportValue(decoders, v);
3181
+ }
3182
+ return result;
3183
+ }
3184
+
3185
+ // src/test/simple-context-multi-worker.ts
3186
+ function applyMultiWorkerConfig(mfConfig, config, serviceBindingResolution, doBindingResolution) {
3187
+ const primaryDurableObjects = {
3188
+ ...mfConfig.durableObjects || {},
3189
+ ...doBindingResolution?.crossWorkerDOBindings || {}
3190
+ };
3191
+ const primaryWorker = {
3192
+ name: config.name ?? "primary",
3193
+ modules: true,
3194
+ script: mfConfig.script,
3195
+ compatibilityDate: config.compatibilityDate ?? "2025-01-01",
3196
+ ...mfConfig.kvNamespaces && { kvNamespaces: mfConfig.kvNamespaces },
3197
+ ...mfConfig.r2Buckets && { r2Buckets: mfConfig.r2Buckets },
3198
+ ...mfConfig.d1Databases && { d1Databases: mfConfig.d1Databases },
3199
+ ...mfConfig.ratelimits && { ratelimits: mfConfig.ratelimits },
3200
+ ...mfConfig.versionMetadata && { versionMetadata: mfConfig.versionMetadata },
3201
+ ...mfConfig.workerLoaders && { workerLoaders: mfConfig.workerLoaders },
3202
+ ...mfConfig.mtlsCertificates && { mtlsCertificates: mfConfig.mtlsCertificates },
3203
+ ...mfConfig.dispatchNamespaces && { dispatchNamespaces: mfConfig.dispatchNamespaces },
3204
+ ...mfConfig.workflows && { workflows: mfConfig.workflows },
3205
+ ...mfConfig.pipelines && { pipelines: mfConfig.pipelines },
3206
+ ...mfConfig.images && { images: mfConfig.images },
3207
+ ...mfConfig.media && { media: mfConfig.media },
3208
+ ...mfConfig.artifacts && { artifacts: mfConfig.artifacts },
3209
+ ...mfConfig.secretsStoreSecrets && { secretsStoreSecrets: mfConfig.secretsStoreSecrets },
3210
+ ...mfConfig.wrappedBindings && { wrappedBindings: mfConfig.wrappedBindings },
3211
+ ...mfConfig.email && { email: mfConfig.email },
3212
+ ...Object.keys(primaryDurableObjects).length > 0 && { durableObjects: primaryDurableObjects },
3213
+ ...mfConfig.serviceBindings || serviceBindingResolution?.primaryServiceBindings ? {
3214
+ serviceBindings: {
3215
+ ...mfConfig.serviceBindings ?? {},
3216
+ ...serviceBindingResolution?.primaryServiceBindings ?? {}
3217
+ }
3218
+ } : {}
3219
+ };
3220
+ const additionalWorkers = [
3221
+ ...serviceBindingResolution?.workers || [],
3222
+ ...doBindingResolution?.workers || [],
3223
+ ...mfConfig.__devflareLocalSecretWorkers || [],
3224
+ ...mfConfig.__devflareLocalBindingWorkers || []
3225
+ ];
3226
+ const workersByName = new Map;
3227
+ for (const worker2 of additionalWorkers) {
3228
+ if (!workersByName.has(worker2.name)) {
3229
+ workersByName.set(worker2.name, worker2);
3230
+ continue;
3231
+ }
3232
+ const existing = workersByName.get(worker2.name);
3233
+ if (worker2.durableObjects) {
3234
+ existing.durableObjects = {
3235
+ ...existing.durableObjects || {},
3236
+ ...worker2.durableObjects
3237
+ };
3238
+ }
3239
+ }
3240
+ const workers = [primaryWorker, ...workersByName.values()];
3241
+ delete mfConfig.script;
3242
+ delete mfConfig.modules;
3243
+ delete mfConfig.kvNamespaces;
3244
+ delete mfConfig.r2Buckets;
3245
+ delete mfConfig.d1Databases;
3246
+ delete mfConfig.ratelimits;
3247
+ delete mfConfig.versionMetadata;
3248
+ delete mfConfig.workerLoaders;
3249
+ delete mfConfig.mtlsCertificates;
3250
+ delete mfConfig.dispatchNamespaces;
3251
+ delete mfConfig.workflows;
3252
+ delete mfConfig.pipelines;
3253
+ delete mfConfig.images;
3254
+ delete mfConfig.media;
3255
+ delete mfConfig.artifacts;
3256
+ delete mfConfig.secretsStoreSecrets;
3257
+ delete mfConfig.wrappedBindings;
3258
+ delete mfConfig.serviceBindings;
3259
+ delete mfConfig.__devflareLocalSecretWorkers;
3260
+ delete mfConfig.__devflareLocalBindingWorkers;
3261
+ delete mfConfig.durableObjects;
3262
+ mfConfig.workers = workers;
3263
+ }
3264
+
3265
+ // src/test/simple-context-mfconfig.ts
3266
+ function buildInlineBridgeMfConfig(config, options = {}) {
3267
+ const localWorkerBindings = config.vars ?? {};
3268
+ const localSecretWrappedBindingConfig = options.cwd ? buildLocalSecretWrappedBindingConfig(config, options.cwd) : undefined;
3269
+ const localBindingShimServiceConfig = buildLocalBindingShimServiceConfig(config);
3270
+ const localSecretBindingNames = new Set(localSecretWrappedBindingConfig?.localBindingNames ?? []);
3271
+ const mfConfig = {
3272
+ modules: true,
3273
+ compatibilityDate: config.compatibilityDate ?? "2025-01-01",
3274
+ ...config.compatibilityFlags && { compatibilityFlags: config.compatibilityFlags }
3275
+ };
3276
+ if (config.bindings?.kv) {
3277
+ mfConfig.kvNamespaces = Object.keys(config.bindings.kv);
3278
+ }
3279
+ if (config.bindings?.r2) {
3280
+ mfConfig.r2Buckets = Object.keys(config.bindings.r2);
3281
+ }
3282
+ if (config.bindings?.d1) {
3283
+ mfConfig.d1Databases = Object.fromEntries(Object.entries(config.bindings.d1).map(([bindingName, bindingConfig]) => {
3284
+ return [bindingName, getLocalD1DatabaseIdentifier(bindingConfig)];
3285
+ }));
3286
+ }
3287
+ const hyperdrivesConfig = buildHyperdrivesConfig(config.bindings ?? {});
3288
+ if (hyperdrivesConfig) {
3289
+ mfConfig.hyperdrives = hyperdrivesConfig;
3290
+ }
3291
+ if (config.bindings?.queues?.producers) {
3292
+ const queueProducers = {};
3293
+ for (const [bindingName, queueName] of Object.entries(config.bindings.queues.producers)) {
3294
+ queueProducers[bindingName] = { queueName };
3295
+ }
3296
+ mfConfig.queueProducers = queueProducers;
3297
+ }
3298
+ if (config.bindings?.rateLimits) {
3299
+ mfConfig.ratelimits = Object.fromEntries(Object.entries(config.bindings.rateLimits).map(([bindingName, binding]) => [
3300
+ bindingName,
3301
+ {
3302
+ simple: {
3303
+ limit: binding.simple.limit,
3304
+ period: binding.simple.period
3305
+ }
3306
+ }
3307
+ ]));
3308
+ }
3309
+ if (config.bindings?.versionMetadata) {
3310
+ mfConfig.versionMetadata = config.bindings.versionMetadata.binding;
3311
+ }
3312
+ if (config.bindings?.workerLoaders) {
3313
+ mfConfig.workerLoaders = Object.fromEntries(Object.keys(config.bindings.workerLoaders).map((bindingName) => [bindingName, {}]));
3314
+ }
3315
+ if (config.bindings?.mtlsCertificates) {
3316
+ mfConfig.mtlsCertificates = Object.fromEntries(Object.entries(config.bindings.mtlsCertificates).map(([bindingName, binding]) => {
3317
+ const normalized = normalizeMtlsCertificateBinding(binding);
3318
+ return [
3319
+ bindingName,
3320
+ {
3321
+ certificate_id: normalized.certificateId
3322
+ }
3323
+ ];
3324
+ }));
3325
+ }
3326
+ if (config.bindings?.dispatchNamespaces) {
3327
+ mfConfig.dispatchNamespaces = Object.fromEntries(Object.entries(config.bindings.dispatchNamespaces).map(([bindingName, binding]) => {
3328
+ const normalized = normalizeDispatchNamespaceBinding(binding);
3329
+ return [
3330
+ bindingName,
3331
+ {
3332
+ namespace: normalized.namespace
3333
+ }
3334
+ ];
3335
+ }));
3336
+ }
3337
+ if (config.bindings?.workflows) {
3338
+ mfConfig.workflows = Object.fromEntries(Object.entries(config.bindings.workflows).map(([bindingName, binding]) => {
3339
+ const normalized = normalizeWorkflowBinding(binding);
3340
+ return [
3341
+ bindingName,
3342
+ {
3343
+ name: normalized.name,
3344
+ className: normalized.className,
3345
+ ...normalized.scriptName && { scriptName: normalized.scriptName },
3346
+ ...normalized.limits && { stepLimit: normalized.limits.steps }
3347
+ }
3348
+ ];
3349
+ }));
3350
+ }
3351
+ if (config.bindings?.pipelines) {
3352
+ mfConfig.pipelines = Object.fromEntries(Object.entries(config.bindings.pipelines).map(([bindingName, binding]) => {
3353
+ const normalized = normalizePipelineBinding(binding);
3354
+ return [
3355
+ bindingName,
3356
+ typeof binding === "string" ? normalized.pipeline : { pipeline: normalized.pipeline }
3357
+ ];
3358
+ }));
3359
+ }
3360
+ if (config.bindings?.images && localBindingShimServiceConfig.localBindingNames.length === 0) {
3361
+ const [entry] = Object.entries(config.bindings.images);
3362
+ if (entry) {
3363
+ const [bindingName, binding] = entry;
3364
+ const normalized = normalizeImagesBinding(bindingName, binding);
3365
+ mfConfig.images = {
3366
+ binding: normalized.binding
3367
+ };
3368
+ }
3369
+ }
3370
+ if (config.bindings?.media && localBindingShimServiceConfig.localBindingNames.length === 0) {
3371
+ const [entry] = Object.entries(config.bindings.media);
3372
+ if (entry) {
3373
+ const [bindingName, binding] = entry;
3374
+ const normalized = normalizeMediaBinding(bindingName, binding);
3375
+ mfConfig.media = {
3376
+ binding: normalized.binding
3377
+ };
3378
+ }
3379
+ }
3380
+ if (config.bindings?.artifacts) {
3381
+ mfConfig.artifacts = Object.fromEntries(Object.entries(config.bindings.artifacts).map(([bindingName, binding]) => {
3382
+ const normalized = normalizeArtifactsBinding(binding);
3383
+ return [
3384
+ bindingName,
3385
+ {
3386
+ namespace: normalized.namespace
3387
+ }
3388
+ ];
3389
+ }));
3390
+ }
3391
+ if (config.bindings?.aiSearchNamespaces) {
3392
+ mfConfig.aiSearchNamespaces = Object.fromEntries(Object.entries(config.bindings.aiSearchNamespaces).map(([bindingName, binding]) => [
3393
+ bindingName,
3394
+ {
3395
+ namespace: binding.namespace
3396
+ }
3397
+ ]));
3398
+ }
3399
+ if (config.bindings?.aiSearch) {
3400
+ mfConfig.aiSearchInstances = Object.fromEntries(Object.entries(config.bindings.aiSearch).map(([bindingName, binding]) => [
3401
+ bindingName,
3402
+ {
3403
+ instance_name: binding.instanceName
3404
+ }
3405
+ ]));
3406
+ }
3407
+ if (config.bindings?.secretsStore) {
3408
+ const secretsStoreEntries = Object.entries(config.bindings.secretsStore).flatMap(([bindingName, binding]) => {
3409
+ if (localSecretBindingNames.has(bindingName)) {
3410
+ return [];
3411
+ }
3412
+ const normalized = normalizeSecretsStoreBinding(binding, config.secretsStoreId, bindingName);
3413
+ return [[
3414
+ bindingName,
3415
+ {
3416
+ store_id: normalized.storeId,
3417
+ secret_name: normalized.secretName
3418
+ }
3419
+ ]];
3420
+ });
3421
+ if (secretsStoreEntries.length > 0) {
3422
+ mfConfig.secretsStoreSecrets = Object.fromEntries(secretsStoreEntries);
3423
+ }
3424
+ }
3425
+ const wrappedBindings = {
3426
+ ...localSecretWrappedBindingConfig?.wrappedBindings ?? {}
3427
+ };
3428
+ const localBindingWorkers = [
3429
+ ...localSecretWrappedBindingConfig?.workers ?? [],
3430
+ ...localBindingShimServiceConfig.workers
3431
+ ];
3432
+ if (Object.keys(wrappedBindings).length > 0) {
3433
+ mfConfig.wrappedBindings = wrappedBindings;
3434
+ }
3435
+ if (localBindingShimServiceConfig.localBindingNames.length > 0) {
3436
+ mfConfig.serviceBindings = {
3437
+ ...mfConfig.serviceBindings ?? {},
3438
+ ...localBindingShimServiceConfig.serviceBindings
3439
+ };
3440
+ }
3441
+ if (localBindingWorkers.length > 0) {
3442
+ mfConfig.__devflareLocalBindingWorkers = localBindingWorkers;
3443
+ }
3444
+ if (Object.keys(localWorkerBindings).length > 0) {
3445
+ mfConfig.bindings = localWorkerBindings;
3446
+ }
3447
+ if (config.bindings?.sendEmail) {
3448
+ mfConfig.email = {
3449
+ send_email: Object.entries(config.bindings.sendEmail).map(([name, binding]) => ({
3450
+ name,
3451
+ ...binding.destinationAddress && {
3452
+ destination_address: binding.destinationAddress
3453
+ },
3454
+ ...binding.allowedDestinationAddresses && {
3455
+ allowed_destination_addresses: binding.allowedDestinationAddresses
3456
+ },
3457
+ ...binding.allowedSenderAddresses && {
3458
+ allowed_sender_addresses: binding.allowedSenderAddresses
3459
+ }
3460
+ }))
3461
+ };
3462
+ }
3463
+ return mfConfig;
3464
+ }
3465
+
3466
+ // src/test/simple-context.ts
3467
+ function createTestContextState() {
3468
+ return {
3469
+ client: null,
3470
+ miniflare: null,
3471
+ envProxy: null,
3472
+ transportDecode: null,
3473
+ remoteBindings: null,
3474
+ miniflareBindings: null
3475
+ };
3476
+ }
3477
+ function shouldPreferBridgeBinding(hint) {
3478
+ return hint === "do" || hint === "service";
3479
+ }
3480
+ async function createTestContext(configPath) {
3481
+ const state = createTestContextState();
3482
+ const { configDir: configDir6, config } = await resolveTestContextConfig(configPath);
3483
+ state.remoteBindings = buildRemoteAndStaticBindings(config);
3484
+ const hints = extractBindingHints(config);
3485
+ const decodeTransport = (value) => decodeTransportValue(state.transportDecode, value);
3486
+ const needsMultiWorkerForServices = hasServiceBindings(config);
3487
+ const needsMultiWorkerForDOs = hasCrossWorkerDOs(config);
3488
+ const needsMultiWorker = needsMultiWorkerForServices || needsMultiWorkerForDOs;
3489
+ let serviceBindingResolution = null;
3490
+ let doBindingResolution = null;
3491
+ if (needsMultiWorkerForServices) {
3492
+ serviceBindingResolution = await resolveServiceBindings(config, configDir6);
3493
+ }
3494
+ if (needsMultiWorkerForDOs) {
3495
+ doBindingResolution = await resolveDOBindings(config, configDir6);
3496
+ }
3497
+ const mfConfig = buildInlineBridgeMfConfig(config, { cwd: configDir6 });
3498
+ const transportFile = resolveTransportFile(configDir6, config.files?.transport);
3499
+ if (transportFile) {
3500
+ state.transportDecode = await loadTransportDecoders(configDir6, transportFile);
3501
+ }
3502
+ const gateway = await buildDurableObjectGateway(config, configDir6, transportFile);
3503
+ mfConfig.script = gateway.script;
3504
+ if (gateway.durableObjects) {
3505
+ mfConfig.durableObjects = gateway.durableObjects;
3506
+ }
3507
+ const hasMultiWorkerServices = serviceBindingResolution && serviceBindingResolution.workers.length > 0;
3508
+ const hasMultiWorkerDOs = doBindingResolution && doBindingResolution.workers.length > 0;
3509
+ if (hasMultiWorkerServices || hasMultiWorkerDOs) {
3510
+ applyMultiWorkerConfig(mfConfig, config, serviceBindingResolution, doBindingResolution);
3511
+ }
3512
+ const usesMultiWorker = Boolean(hasMultiWorkerServices || hasMultiWorkerDOs);
3513
+ const runtime = await bootTestRuntime(mfConfig, usesMultiWorker);
3514
+ const activePort = runtime.activePort;
3515
+ state.miniflare = runtime.miniflare;
3516
+ state.miniflareBindings = runtime.miniflareBindings;
3517
+ state.client = runtime.client;
3518
+ const disposeContext = createDisposeContext(state);
3519
+ const getTestEnv = () => {
3520
+ return new Proxy({}, {
3521
+ get(_, prop) {
3522
+ if (state.remoteBindings && prop in state.remoteBindings) {
3523
+ return state.remoteBindings[prop];
3524
+ }
3525
+ if (hints[prop] === "sendEmail" && state.envProxy && prop in state.envProxy) {
3526
+ return state.envProxy[prop];
3527
+ }
3528
+ if (state.miniflareBindings && prop in state.miniflareBindings) {
3529
+ return state.miniflareBindings[prop];
3530
+ }
3531
+ if (state.envProxy && prop in state.envProxy) {
3532
+ return state.envProxy[prop];
3533
+ }
3534
+ return;
3535
+ },
3536
+ has(_, prop) {
3537
+ return Boolean(state.remoteBindings && prop in state.remoteBindings || state.miniflareBindings && prop in state.miniflareBindings || state.envProxy && prop in state.envProxy);
3538
+ }
3539
+ });
3540
+ };
3541
+ const handlerPaths = await resolveHandlerPaths(configDir6, config);
3542
+ configureSurfaceHandlers({
3543
+ handlerPaths,
3544
+ configDir: configDir6,
3545
+ activePort,
3546
+ getEnv: getTestEnv
3547
+ });
3548
+ if (usesMultiWorker) {
3549
+ setBindingHints(hints);
3550
+ __setTestContext(createMultiWorkerEnvAccessor(state), disposeContext);
3551
+ return;
3552
+ }
3553
+ const bridgeClient = state.client;
3554
+ if (!bridgeClient) {
3555
+ throw new Error("Bridge-backed test context did not initialize a client.");
3556
+ }
3557
+ setBindingHints(hints);
3558
+ state.envProxy = createEnvProxy({
3559
+ client: bridgeClient,
3560
+ transformResult: (result) => decodeTransport(result)
3561
+ });
3562
+ __setTestContext(createBridgeEnvAccessor(state, hints, shouldPreferBridgeBinding), disposeContext);
3563
+ }
3564
+ // src/test/cf.ts
3565
+ var cf = {
3566
+ email,
3567
+ queue: queue2,
23
3568
  scheduled,
24
- shouldSkip,
25
- stopBridgeTestContext,
26
- tail,
27
- testEnv,
28
- withTestContext,
29
- worker
30
- } from "../index-ep3445yc.js";
31
- import"../index-z14anrqp.js";
32
- import"../index-tk6ej9dj.js";
33
- import"../index-d8bdkx2h.js";
34
- import"../index-rbht7m9r.js";
35
- import"../index-gz1gndna.js";
36
- import"../index-pf5s73n9.js";
37
- import"../index-07q6yxyc.js";
38
- import"../index-67qcae0f.js";
39
- import"../index-hcex3rgh.js";
40
- import"../index-tfyxa77h.js";
41
- import"../index-37x76zdn.js";
3569
+ worker,
3570
+ tail
3571
+ };
3572
+ // src/test/should-skip.ts
3573
+ var REMOTE_ONLY_SERVICES = new Set([
3574
+ "ai",
3575
+ "ai_search",
3576
+ "ai_gateway",
3577
+ "media",
3578
+ "mtls_certificates",
3579
+ "artifacts",
3580
+ "builds",
3581
+ "vectorize"
3582
+ ]);
3583
+ var skipResults = new Map;
3584
+ var containerSkipResult = null;
3585
+ var EXPECTED_ERROR_PATTERNS = [
3586
+ "ECONNREFUSED",
3587
+ "ETIMEDOUT",
3588
+ "ENOTFOUND",
3589
+ "fetch failed",
3590
+ "network",
3591
+ "401",
3592
+ "403",
3593
+ "429",
3594
+ "500",
3595
+ "502",
3596
+ "503",
3597
+ "504",
3598
+ "rate limit",
3599
+ "unauthorized",
3600
+ "forbidden",
3601
+ "timeout"
3602
+ ];
3603
+ function isExpectedError(error) {
3604
+ if (!(error instanceof Error))
3605
+ return false;
3606
+ const msg = error.message.toLowerCase();
3607
+ return EXPECTED_ERROR_PATTERNS.some((pattern) => msg.includes(pattern.toLowerCase()));
3608
+ }
3609
+ async function computeSkip(service) {
3610
+ try {
3611
+ if (REMOTE_ONLY_SERVICES.has(service) && !isRemoteModeActive()) {
3612
+ const status = getRemoteModeStatus();
3613
+ console.log(`⏭️ ${service.toUpperCase()} tests skipped: Remote-only service.
3614
+ ` + ` Enable with: ${status.isEnabled ? "" : "devflare remote enable"}
3615
+ ` + ` Or set: DEVFLARE_REMOTE=1
3616
+ ` + ` See: https://github.com/ArthurvdVenne/devflare#remote-testing`);
3617
+ return true;
3618
+ }
3619
+ const isAuth = await isAuthenticated();
3620
+ if (!isAuth) {
3621
+ console.log(`⏭️ ${service.toUpperCase()} tests skipped: Not authenticated. Run: bunx wrangler login
3622
+ ` + ` See: https://github.com/ArthurvdVenne/devflare#authentication`);
3623
+ return true;
3624
+ }
3625
+ const primary = await getPrimaryAccount();
3626
+ if (!primary) {
3627
+ console.log(`⏭️ ${service.toUpperCase()} tests skipped: No Cloudflare account found
3628
+ ` + ` See: https://github.com/ArthurvdVenne/devflare#authentication`);
3629
+ return true;
3630
+ }
3631
+ const { accountId } = await getEffectiveAccountId(primary.id);
3632
+ try {
3633
+ const { allowed, reason } = await canProceedWithTest(accountId, service);
3634
+ if (!allowed) {
3635
+ console.log(`⏭️ ${service.toUpperCase()} tests skipped: ${reason}`);
3636
+ return true;
3637
+ }
3638
+ } catch {}
3639
+ return false;
3640
+ } catch (error) {
3641
+ if (isExpectedError(error)) {
3642
+ const message = error instanceof Error ? error.message : "Unknown error";
3643
+ console.log(`⏭️ ${service.toUpperCase()} tests skipped: ${message}`);
3644
+ return true;
3645
+ }
3646
+ throw error;
3647
+ }
3648
+ }
3649
+ function getSkipResult(service) {
3650
+ let result = skipResults.get(service);
3651
+ if (!result) {
3652
+ result = computeSkip(service);
3653
+ skipResults.set(service, result);
3654
+ }
3655
+ return result;
3656
+ }
3657
+ function getContainerSkipResult() {
3658
+ if (!containerSkipResult) {
3659
+ containerSkipResult = getContainerSkipReason().then((reason) => {
3660
+ if (reason) {
3661
+ console.log(`CONTAINERS tests skipped: ${reason}`);
3662
+ return true;
3663
+ }
3664
+ return false;
3665
+ });
3666
+ }
3667
+ return containerSkipResult;
3668
+ }
3669
+ var shouldSkip = {
3670
+ get ai() {
3671
+ return getSkipResult("ai");
3672
+ },
3673
+ get aiSearch() {
3674
+ return getSkipResult("ai_search");
3675
+ },
3676
+ get aiGateway() {
3677
+ return getSkipResult("ai_gateway");
3678
+ },
3679
+ get vectorize() {
3680
+ return getSkipResult("vectorize");
3681
+ },
3682
+ get workers() {
3683
+ return getSkipResult("workers");
3684
+ },
3685
+ get kv() {
3686
+ return getSkipResult("kv");
3687
+ },
3688
+ get d1() {
3689
+ return getSkipResult("d1");
3690
+ },
3691
+ get r2() {
3692
+ return getSkipResult("r2");
3693
+ },
3694
+ get queues() {
3695
+ return getSkipResult("queues");
3696
+ },
3697
+ get durableObjects() {
3698
+ return getSkipResult("durable_objects");
3699
+ },
3700
+ get media() {
3701
+ return getSkipResult("media");
3702
+ },
3703
+ get mtlsCertificates() {
3704
+ return getSkipResult("mtls_certificates");
3705
+ },
3706
+ get artifacts() {
3707
+ return getSkipResult("artifacts");
3708
+ },
3709
+ get builds() {
3710
+ return getSkipResult("builds");
3711
+ },
3712
+ get containers() {
3713
+ return getContainerSkipResult();
3714
+ }
3715
+ };
3716
+ // src/test/ai-search.ts
3717
+ var MOCK_TIMESTAMP = "2026-04-26T00:00:00.000Z";
3718
+ function cloneInfo(value) {
3719
+ return { ...value };
3720
+ }
3721
+ function extractSearchQuery(params) {
3722
+ if ("query" in params && typeof params.query === "string") {
3723
+ return params.query;
3724
+ }
3725
+ const messages = "messages" in params ? params.messages : [];
3726
+ return messages.filter((message) => message.role === "user" && message.content).map((message) => message.content).join(" ");
3727
+ }
3728
+ function tokenize(value) {
3729
+ return value.toLowerCase().split(/[^a-z0-9_]+/).filter((token) => token.length > 2);
3730
+ }
3731
+ function scoreText(query, text) {
3732
+ const tokens = tokenize(query);
3733
+ if (tokens.length === 0) {
3734
+ return 1;
3735
+ }
3736
+ const haystack = text.toLowerCase();
3737
+ const matches = tokens.filter((token) => haystack.includes(token)).length;
3738
+ return matches === 0 ? 0 : Math.max(0.1, matches / tokens.length);
3739
+ }
3740
+ function streamFromText(text) {
3741
+ const encoded = new TextEncoder().encode(text);
3742
+ return new ReadableStream({
3743
+ start(controller) {
3744
+ controller.enqueue(encoded);
3745
+ controller.close();
3746
+ }
3747
+ });
3748
+ }
3749
+ async function contentToText(content) {
3750
+ if (typeof content === "string") {
3751
+ return content;
3752
+ }
3753
+ if (content instanceof Blob) {
3754
+ return content.text();
3755
+ }
3756
+ return new Response(content).text();
3757
+ }
3758
+ function createItemInfo(id, key, content, options = {}) {
3759
+ return {
3760
+ id,
3761
+ key,
3762
+ status: options.status ?? "completed",
3763
+ next_action: null,
3764
+ namespace: options.namespace,
3765
+ chunks_count: content.length > 0 ? 1 : 0,
3766
+ file_size: new TextEncoder().encode(content).byteLength,
3767
+ source_id: options.sourceId ?? null,
3768
+ last_seen_at: MOCK_TIMESTAMP,
3769
+ created_at: MOCK_TIMESTAMP,
3770
+ metadata: options.metadata
3771
+ };
3772
+ }
3773
+ function createItemChunks(item, chunks) {
3774
+ let offset = 0;
3775
+ return chunks.map((text, index) => {
3776
+ const start = offset;
3777
+ const end = start + new TextEncoder().encode(text).byteLength;
3778
+ offset = end;
3779
+ return {
3780
+ id: `${item.id}:chunk-${index + 1}`,
3781
+ text,
3782
+ start_byte: start,
3783
+ end_byte: end,
3784
+ item: {
3785
+ timestamp: Date.parse(MOCK_TIMESTAMP),
3786
+ key: item.key,
3787
+ metadata: item.metadata
3788
+ }
3789
+ };
3790
+ });
3791
+ }
3792
+ function createStoredItem(sequence, fixture, namespace) {
3793
+ const content = fixture.content ?? fixture.chunks?.join(`
3794
+ `) ?? "";
3795
+ const id = fixture.id ?? `item-${sequence}`;
3796
+ const info = createItemInfo(id, fixture.key, content, {
3797
+ metadata: fixture.metadata,
3798
+ status: fixture.status,
3799
+ namespace
3800
+ });
3801
+ const chunks = createItemChunks(info, fixture.chunks ?? [content]);
3802
+ return {
3803
+ info,
3804
+ content,
3805
+ contentType: fixture.contentType ?? "text/plain",
3806
+ chunks,
3807
+ logs: [
3808
+ {
3809
+ timestamp: MOCK_TIMESTAMP,
3810
+ action: "index",
3811
+ message: `Indexed ${fixture.key}`,
3812
+ fileKey: fixture.key,
3813
+ chunkCount: chunks.length,
3814
+ processingTimeMs: 0
3815
+ }
3816
+ ]
3817
+ };
3818
+ }
3819
+ function isAISearchInstance(value) {
3820
+ return typeof value.search === "function";
3821
+ }
3822
+ function createMockAISearchInstance(options = {}) {
3823
+ const instanceId = options.id ?? options.info?.id ?? "mock-ai-search";
3824
+ const namespace = options.namespace ?? options.info?.namespace;
3825
+ const items = new Map;
3826
+ const jobs = new Map;
3827
+ const searches = [];
3828
+ let itemSequence = 0;
3829
+ let jobSequence = 0;
3830
+ let info = {
3831
+ id: instanceId,
3832
+ type: "builtin",
3833
+ namespace,
3834
+ status: "ready",
3835
+ created_at: MOCK_TIMESTAMP,
3836
+ modified_at: MOCK_TIMESTAMP,
3837
+ index_method: {
3838
+ vector: true,
3839
+ keyword: true
3840
+ },
3841
+ fusion_method: "rrf",
3842
+ ...options.info
3843
+ };
3844
+ for (const fixture of options.items ?? []) {
3845
+ const stored = createStoredItem(++itemSequence, fixture, namespace);
3846
+ items.set(stored.info.id, stored);
3847
+ }
3848
+ const findMatches = (params) => {
3849
+ searches.push(params);
3850
+ const query = extractSearchQuery(params);
3851
+ const maxResults = params.ai_search_options?.retrieval?.max_num_results ?? 10;
3852
+ const chunks = [];
3853
+ for (const item of items.values()) {
3854
+ for (const chunk of item.chunks) {
3855
+ const score = scoreText(query, `${item.info.key} ${chunk.text}`);
3856
+ if (score === 0) {
3857
+ continue;
3858
+ }
3859
+ chunks.push({
3860
+ id: chunk.id,
3861
+ type: "text",
3862
+ score,
3863
+ text: chunk.text,
3864
+ item: {
3865
+ timestamp: Date.parse(MOCK_TIMESTAMP),
3866
+ key: item.info.key,
3867
+ metadata: item.info.metadata
3868
+ },
3869
+ scoring_details: {
3870
+ keyword_score: score,
3871
+ vector_score: score,
3872
+ keyword_rank: chunks.length + 1,
3873
+ vector_rank: chunks.length + 1,
3874
+ fusion_method: "rrf"
3875
+ }
3876
+ });
3877
+ }
3878
+ }
3879
+ return {
3880
+ search_query: query,
3881
+ chunks: chunks.slice(0, maxResults)
3882
+ };
3883
+ };
3884
+ const createItemHandle = (itemId) => ({
3885
+ async info() {
3886
+ const item = items.get(itemId);
3887
+ if (!item) {
3888
+ throw new Error(`Mock AI Search item "${itemId}" was not found.`);
3889
+ }
3890
+ return cloneInfo(item.info);
3891
+ },
3892
+ async download() {
3893
+ const item = items.get(itemId);
3894
+ if (!item) {
3895
+ throw new Error(`Mock AI Search item "${itemId}" was not found.`);
3896
+ }
3897
+ return {
3898
+ body: streamFromText(item.content),
3899
+ contentType: item.contentType,
3900
+ filename: item.info.key,
3901
+ size: new TextEncoder().encode(item.content).byteLength
3902
+ };
3903
+ },
3904
+ async sync() {
3905
+ const item = items.get(itemId);
3906
+ if (!item) {
3907
+ throw new Error(`Mock AI Search item "${itemId}" was not found.`);
3908
+ }
3909
+ item.info.status = "completed";
3910
+ item.info.last_seen_at = MOCK_TIMESTAMP;
3911
+ return cloneInfo(item.info);
3912
+ },
3913
+ async logs(params) {
3914
+ const item = items.get(itemId);
3915
+ if (!item) {
3916
+ throw new Error(`Mock AI Search item "${itemId}" was not found.`);
3917
+ }
3918
+ const limit = params?.limit ?? 50;
3919
+ const result = item.logs.slice(0, limit);
3920
+ return {
3921
+ result,
3922
+ result_info: {
3923
+ count: result.length,
3924
+ per_page: limit,
3925
+ cursor: null,
3926
+ truncated: item.logs.length > result.length
3927
+ }
3928
+ };
3929
+ },
3930
+ async chunks(params) {
3931
+ const item = items.get(itemId);
3932
+ if (!item) {
3933
+ throw new Error(`Mock AI Search item "${itemId}" was not found.`);
3934
+ }
3935
+ const offset = params?.offset ?? 0;
3936
+ const limit = params?.limit ?? 20;
3937
+ const result = item.chunks.slice(offset, offset + limit);
3938
+ return {
3939
+ result,
3940
+ result_info: {
3941
+ count: result.length,
3942
+ total: item.chunks.length,
3943
+ limit,
3944
+ offset
3945
+ }
3946
+ };
3947
+ }
3948
+ });
3949
+ const uploadItem = async (name, content, uploadOptions) => {
3950
+ const text = await contentToText(content);
3951
+ const existing = Array.from(items.values()).find((item) => item.info.key === name);
3952
+ const stored = createStoredItem(existing ? Number(existing.info.id.replace(/^item-/, "")) || ++itemSequence : ++itemSequence, {
3953
+ id: existing?.info.id,
3954
+ key: name,
3955
+ content: text,
3956
+ metadata: uploadOptions?.metadata
3957
+ }, namespace);
3958
+ items.set(stored.info.id, stored);
3959
+ return cloneInfo(stored.info);
3960
+ };
3961
+ const itemsApi = {
3962
+ async list(params) {
3963
+ let result = Array.from(items.values()).map((item) => cloneInfo(item.info));
3964
+ if (params?.search) {
3965
+ const search = params.search.toLowerCase();
3966
+ result = result.filter((item) => item.key.toLowerCase().includes(search));
3967
+ }
3968
+ if (params?.status) {
3969
+ result = result.filter((item) => item.status === params.status);
3970
+ }
3971
+ const page = params?.page ?? 1;
3972
+ const perPage = (params?.per_page ?? result.length) || 50;
3973
+ const start = (page - 1) * perPage;
3974
+ const paged = result.slice(start, start + perPage);
3975
+ return {
3976
+ result: paged,
3977
+ result_info: {
3978
+ count: paged.length,
3979
+ page,
3980
+ per_page: perPage,
3981
+ total_count: result.length
3982
+ }
3983
+ };
3984
+ },
3985
+ upload: uploadItem,
3986
+ async uploadAndPoll(name, content, uploadOptions) {
3987
+ return uploadItem(name, content, uploadOptions);
3988
+ },
3989
+ get(itemId) {
3990
+ return createItemHandle(itemId);
3991
+ },
3992
+ async delete(itemId) {
3993
+ items.delete(itemId);
3994
+ }
3995
+ };
3996
+ const createJobHandle = (jobId) => ({
3997
+ async info() {
3998
+ const job = jobs.get(jobId);
3999
+ if (!job) {
4000
+ throw new Error(`Mock AI Search job "${jobId}" was not found.`);
4001
+ }
4002
+ return cloneInfo(job);
4003
+ },
4004
+ async logs(params) {
4005
+ const perPage = params?.per_page ?? 50;
4006
+ return {
4007
+ result: [
4008
+ {
4009
+ id: 1,
4010
+ message: `Mock job ${jobId}`,
4011
+ message_type: 0,
4012
+ created_at: Date.parse(MOCK_TIMESTAMP)
4013
+ }
4014
+ ].slice(0, perPage),
4015
+ result_info: {
4016
+ count: 1,
4017
+ page: params?.page ?? 1,
4018
+ per_page: perPage,
4019
+ total_count: 1
4020
+ }
4021
+ };
4022
+ },
4023
+ async cancel() {
4024
+ const job = jobs.get(jobId);
4025
+ if (!job) {
4026
+ throw new Error(`Mock AI Search job "${jobId}" was not found.`);
4027
+ }
4028
+ const updated = {
4029
+ ...job,
4030
+ ended_at: MOCK_TIMESTAMP,
4031
+ end_reason: "cancelled"
4032
+ };
4033
+ jobs.set(jobId, updated);
4034
+ return cloneInfo(updated);
4035
+ }
4036
+ });
4037
+ const jobsApi = {
4038
+ async list(params) {
4039
+ const allJobs = Array.from(jobs.values()).map((job) => cloneInfo(job));
4040
+ const page = params?.page ?? 1;
4041
+ const perPage = (params?.per_page ?? allJobs.length) || 50;
4042
+ const start = (page - 1) * perPage;
4043
+ const result = allJobs.slice(start, start + perPage);
4044
+ return {
4045
+ result,
4046
+ result_info: {
4047
+ count: result.length,
4048
+ page,
4049
+ per_page: perPage,
4050
+ total_count: allJobs.length
4051
+ }
4052
+ };
4053
+ },
4054
+ async create(params) {
4055
+ const id = `job-${++jobSequence}`;
4056
+ const job = {
4057
+ id,
4058
+ source: "user",
4059
+ description: params?.description,
4060
+ started_at: MOCK_TIMESTAMP
4061
+ };
4062
+ jobs.set(id, job);
4063
+ return cloneInfo(job);
4064
+ },
4065
+ get(jobId) {
4066
+ return createJobHandle(jobId);
4067
+ }
4068
+ };
4069
+ return {
4070
+ async search(params) {
4071
+ return findMatches(params);
4072
+ },
4073
+ async chatCompletions(params) {
4074
+ const search = findMatches({
4075
+ messages: params.messages,
4076
+ ai_search_options: params.ai_search_options
4077
+ });
4078
+ const content = typeof options.chatMessage === "function" ? options.chatMessage(search.chunks, params) : options.chatMessage ?? (search.chunks.map((chunk) => chunk.text).join(`
4079
+ `) || "No matching offline AI Search chunks.");
4080
+ if (params.stream) {
4081
+ const payload = JSON.stringify({
4082
+ choices: [{ delta: { content } }],
4083
+ chunks: search.chunks
4084
+ });
4085
+ return streamFromText(`data: ${payload}
4086
+
4087
+ `);
4088
+ }
4089
+ return {
4090
+ id: "mock-ai-search-chat",
4091
+ object: "chat.completion",
4092
+ model: params.model ?? "mock-ai-search",
4093
+ choices: [
4094
+ {
4095
+ index: 0,
4096
+ message: {
4097
+ role: "assistant",
4098
+ content
4099
+ }
4100
+ }
4101
+ ],
4102
+ chunks: search.chunks
4103
+ };
4104
+ },
4105
+ async update(config) {
4106
+ info = {
4107
+ ...info,
4108
+ ...config,
4109
+ modified_at: MOCK_TIMESTAMP
4110
+ };
4111
+ return cloneInfo(info);
4112
+ },
4113
+ async info() {
4114
+ return cloneInfo(info);
4115
+ },
4116
+ async stats() {
4117
+ const stats = {
4118
+ queued: 0,
4119
+ running: 0,
4120
+ completed: 0,
4121
+ error: 0,
4122
+ skipped: 0,
4123
+ outdated: 0,
4124
+ last_activity: MOCK_TIMESTAMP,
4125
+ engine: {
4126
+ vectorize: {
4127
+ vectorsCount: items.size,
4128
+ dimensions: 0
4129
+ },
4130
+ r2: {
4131
+ payloadSizeBytes: Array.from(items.values()).reduce((sum, item) => sum + item.info.file_size, 0),
4132
+ metadataSizeBytes: 0,
4133
+ objectCount: items.size
4134
+ }
4135
+ }
4136
+ };
4137
+ for (const item of items.values()) {
4138
+ stats[item.info.status] = (stats[item.info.status] ?? 0) + 1;
4139
+ }
4140
+ return stats;
4141
+ },
4142
+ get items() {
4143
+ return itemsApi;
4144
+ },
4145
+ get jobs() {
4146
+ return jobsApi;
4147
+ },
4148
+ _getSearches() {
4149
+ return [...searches];
4150
+ },
4151
+ _getItems() {
4152
+ return Array.from(items.values()).map((item) => cloneInfo(item.info));
4153
+ },
4154
+ _getJobs() {
4155
+ return Array.from(jobs.values()).map((job) => cloneInfo(job));
4156
+ }
4157
+ };
4158
+ }
4159
+ function createMockAISearchNamespace(options = {}) {
4160
+ const namespaceName = options.namespace ?? "default";
4161
+ const instances = new Map;
4162
+ for (const [name, instanceOptions] of Object.entries(options.instances ?? {})) {
4163
+ instances.set(name, isAISearchInstance(instanceOptions) ? instanceOptions : createMockAISearchInstance({
4164
+ id: name,
4165
+ namespace: namespaceName,
4166
+ ...instanceOptions
4167
+ }));
4168
+ }
4169
+ const getInstance = (name) => {
4170
+ const instance = instances.get(name);
4171
+ if (!instance) {
4172
+ throw new Error(`Mock AI Search namespace has no instance named "${name}".`);
4173
+ }
4174
+ return instance;
4175
+ };
4176
+ return {
4177
+ get(name) {
4178
+ return getInstance(name);
4179
+ },
4180
+ async list(params) {
4181
+ let result = await Promise.all(Array.from(instances.entries()).map(async ([name, instance]) => ({
4182
+ ...await instance.info(),
4183
+ namespace: namespaceName,
4184
+ id: name
4185
+ })));
4186
+ if (params?.search) {
4187
+ const search = params.search.toLowerCase();
4188
+ result = result.filter((instance) => instance.id.toLowerCase().includes(search));
4189
+ }
4190
+ const page = params?.page ?? 1;
4191
+ const perPage = (params?.per_page ?? result.length) || 50;
4192
+ const start = (page - 1) * perPage;
4193
+ const paged = result.slice(start, start + perPage);
4194
+ return {
4195
+ result: paged,
4196
+ result_info: {
4197
+ count: paged.length,
4198
+ page,
4199
+ per_page: perPage,
4200
+ total_count: result.length
4201
+ }
4202
+ };
4203
+ },
4204
+ async create(config) {
4205
+ const instance = createMockAISearchInstance({
4206
+ id: config.id,
4207
+ namespace: namespaceName,
4208
+ info: config
4209
+ });
4210
+ instances.set(config.id, instance);
4211
+ return instance;
4212
+ },
4213
+ async delete(name) {
4214
+ instances.delete(name);
4215
+ },
4216
+ async search(params) {
4217
+ const query = extractSearchQuery(params);
4218
+ const chunks = [];
4219
+ const errors = [];
4220
+ for (const instanceId of params.ai_search_options.instance_ids) {
4221
+ const instance = instances.get(instanceId);
4222
+ if (!instance) {
4223
+ errors.push({
4224
+ instance_id: instanceId,
4225
+ message: `Mock AI Search namespace has no instance named "${instanceId}".`
4226
+ });
4227
+ continue;
4228
+ }
4229
+ const response = "query" in params ? await instance.search({ query, ai_search_options: params.ai_search_options }) : await instance.search({
4230
+ messages: params.messages,
4231
+ ai_search_options: params.ai_search_options
4232
+ });
4233
+ chunks.push(...response.chunks.map((chunk) => ({
4234
+ ...chunk,
4235
+ instance_id: instanceId
4236
+ })));
4237
+ }
4238
+ return {
4239
+ search_query: query,
4240
+ chunks,
4241
+ ...errors.length > 0 && { errors }
4242
+ };
4243
+ },
4244
+ async chatCompletions(params) {
4245
+ const search = await this.search({
4246
+ messages: params.messages,
4247
+ ai_search_options: params.ai_search_options
4248
+ });
4249
+ const content = search.chunks.map((chunk) => chunk.text).join(`
4250
+ `) || "No matching offline AI Search chunks.";
4251
+ if (params.stream) {
4252
+ return streamFromText(`data: ${JSON.stringify({ chunks: search.chunks })}
4253
+
4254
+ `);
4255
+ }
4256
+ return {
4257
+ id: "mock-ai-search-namespace-chat",
4258
+ object: "chat.completion",
4259
+ model: params.model ?? "mock-ai-search",
4260
+ choices: [
4261
+ {
4262
+ index: 0,
4263
+ message: {
4264
+ role: "assistant",
4265
+ content
4266
+ }
4267
+ }
4268
+ ],
4269
+ chunks: search.chunks,
4270
+ errors: search.errors
4271
+ };
4272
+ },
4273
+ _getInstances() {
4274
+ return Array.from(instances.keys());
4275
+ }
4276
+ };
4277
+ }
4278
+
4279
+ // src/test/offline-bindings.ts
4280
+ var SUPPORT_MATRIX = {
4281
+ rateLimits: {
4282
+ service: "rateLimits",
4283
+ tier: "offline-native",
4284
+ reason: "Miniflare and devflare/test can simulate fixed-window RateLimit bindings locally.",
4285
+ recommendation: "Use createOfflineEnv() or createMockRateLimit() for pure tests; use createTestContext() for Miniflare-backed tests."
4286
+ },
4287
+ versionMetadata: {
4288
+ service: "versionMetadata",
4289
+ tier: "offline-native",
4290
+ reason: "Version metadata can be deterministic in tests without Cloudflare state.",
4291
+ recommendation: "Use createOfflineEnv() or createMockVersionMetadata() when asserting version-aware code."
4292
+ },
4293
+ secretsStore: {
4294
+ service: "secretsStore",
4295
+ tier: "offline-native",
4296
+ reason: "Secrets Store binding shape is local-testable with local secret-store values or fixed fixtures.",
4297
+ recommendation: "Use devflare secrets --local for dev/test values, or pass fixtures.secretsStore values for pure tests."
4298
+ },
4299
+ hyperdrive: {
4300
+ service: "hyperdrive",
4301
+ tier: "offline-native",
4302
+ reason: "Hyperdrive can run locally through Miniflare when Devflare has a local connection string or test fixture for the target database.",
4303
+ recommendation: "Use bindings.hyperdrive.*.localConnectionString, CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING>, or fixtures.hyperdrive for offline tests."
4304
+ },
4305
+ workerLoaders: {
4306
+ service: "workerLoaders",
4307
+ tier: "offline-native",
4308
+ reason: "Worker Loader bindings run locally through Miniflare and can use explicit Worker stubs for pure tests.",
4309
+ recommendation: "Use createTestContext() for local WorkerLoader execution; pass fixtures.workerLoaders with a WorkerStub for pure tests."
4310
+ },
4311
+ mtlsCertificates: {
4312
+ service: "mtlsCertificates",
4313
+ tier: "offline-fixture",
4314
+ reason: "Fetcher call paths can be tested locally, but real certificate presentation is Cloudflare/Wrangler remote behavior.",
4315
+ recommendation: "Pass fixtures.mtlsCertificates handlers for unit tests; use remote/deployed tests for certificate presentation."
4316
+ },
4317
+ dispatchNamespaces: {
4318
+ service: "dispatchNamespaces",
4319
+ tier: "offline-fixture",
4320
+ reason: "Tenant dispatch can be backed by explicit test fetchers, but namespace uploads and lifecycle are Cloudflare-managed.",
4321
+ recommendation: "Pass fixtures.dispatchNamespaces workers for deterministic tenant routing tests."
4322
+ },
4323
+ workflows: {
4324
+ service: "workflows",
4325
+ tier: "offline-native",
4326
+ reason: "Workflow binding calls can run through Miniflare or a deterministic local Workflow mock for app-level tests.",
4327
+ recommendation: "Use createOfflineEnv() for pure tests and createTestContext() when Miniflare execution semantics matter."
4328
+ },
4329
+ pipelines: {
4330
+ service: "pipelines",
4331
+ tier: "offline-native",
4332
+ reason: "Pipeline sends can be recorded locally; Cloudflare owns production batching and sinks.",
4333
+ recommendation: "Use createOfflineEnv() or createMockPipeline() to assert records sent by application code."
4334
+ },
4335
+ images: {
4336
+ service: "images",
4337
+ tier: "offline-native",
4338
+ reason: "Images has local development support and Devflare provides a low-fidelity deterministic pure mock.",
4339
+ recommendation: "Use createOfflineEnv() for chain-shape tests; use Cloudflare for hosted image APIs and transform fidelity."
4340
+ },
4341
+ media: {
4342
+ service: "media",
4343
+ tier: "offline-native",
4344
+ reason: "Media Transformations can run through Miniflare wiring locally, and Devflare provides a deterministic pure mock for app-level chain tests.",
4345
+ recommendation: "Use createTestContext() for local Worker binding tests and createMockMediaBinding() for pure tests; use Cloudflare for codec/output fidelity."
4346
+ },
4347
+ artifacts: {
4348
+ service: "artifacts",
4349
+ tier: "offline-fixture",
4350
+ reason: "Artifacts repo metadata and token flows can be modeled in memory; real Git remotes are Cloudflare-managed.",
4351
+ recommendation: "Use createMockArtifacts() for unit tests; use Cloudflare for Git protocol and namespace access."
4352
+ },
4353
+ aiSearch: {
4354
+ service: "aiSearch",
4355
+ tier: "offline-fixture",
4356
+ reason: "AI Search application flows can use deterministic in-memory instances and namespaces, but indexing/ranking/crawling are hosted Cloudflare behavior.",
4357
+ recommendation: "Use createMockAISearchInstance(), createMockAISearchNamespace(), or createOfflineEnv(); use remote tests for real relevance behavior."
4358
+ },
4359
+ aiSearchNamespaces: {
4360
+ service: "aiSearchNamespaces",
4361
+ tier: "offline-fixture",
4362
+ reason: "AI Search namespace management can be backed by an explicit in-memory instance registry for tests.",
4363
+ recommendation: "Use fixtures.aiSearchNamespaces to model tenant instances and multi-instance searches."
4364
+ },
4365
+ containers: {
4366
+ service: "containers",
4367
+ tier: "offline-native",
4368
+ reason: "Devflare can launch local Docker/Podman containers in explicit tests when an engine and cached images are available.",
4369
+ recommendation: "Use devflare/test containers helpers and shouldSkip.containers; keep ordinary unit tests engine-free."
4370
+ },
4371
+ browser: {
4372
+ service: "browser",
4373
+ tier: "offline-native",
4374
+ reason: "Cloudflare lists Browser Run as locally simulatable, while live view, HITL, recordings, and external CDP remain hosted features.",
4375
+ recommendation: "Use Cloudflare/Wrangler Browser local development for browser execution and remote/deployed tests for hosted-only features."
4376
+ },
4377
+ ai: {
4378
+ service: "ai",
4379
+ tier: "remote-boundary",
4380
+ reason: "Workers AI inference has no local simulation in Cloudflare local development.",
4381
+ recommendation: "Use DEVFLARE_REMOTE=1/devflare remote enable for real calls, or inject a custom fake for pure tests."
4382
+ },
4383
+ aiGateway: {
4384
+ service: "aiGateway",
4385
+ tier: "remote-boundary",
4386
+ reason: "AI Gateway routing and logs are Cloudflare account resources reached through the Workers AI binding.",
4387
+ recommendation: "Use remote-mode AI Gateway helpers for integration tests and custom fakes for offline unit tests."
4388
+ },
4389
+ vectorize: {
4390
+ service: "vectorize",
4391
+ tier: "remote-boundary",
4392
+ reason: "Cloudflare lists Vectorize with no local simulation.",
4393
+ recommendation: "Use DEVFLARE_REMOTE=1/devflare remote enable for real indexes, or inject a fake Vectorize binding for pure tests."
4394
+ },
4395
+ builds: {
4396
+ service: "builds",
4397
+ tier: "remote-boundary",
4398
+ reason: "Cloudflare Builds/Git-connected Workers are CI/CD orchestration, not a Worker runtime binding.",
4399
+ recommendation: "Run Devflare commands inside your CI; validate Cloudflare build integration with Cloudflare/Wrangler tests."
4400
+ }
4401
+ };
4402
+ function copySupport(entry) {
4403
+ return { ...entry };
4404
+ }
4405
+ function getOfflineSupportMatrix() {
4406
+ return Object.fromEntries(Object.entries(SUPPORT_MATRIX).map(([service, entry]) => [service, copySupport(entry)]));
4407
+ }
4408
+ function describeOfflineSupport(service) {
4409
+ const entry = SUPPORT_MATRIX[service];
4410
+ if (entry) {
4411
+ return copySupport(entry);
4412
+ }
4413
+ return {
4414
+ service,
4415
+ tier: "remote-boundary",
4416
+ reason: `No offline support classification exists for "${service}".`,
4417
+ recommendation: "Treat this as a remote Cloudflare boundary until Devflare documents a local simulator or fixture."
4418
+ };
4419
+ }
4420
+ function createMissingSecret(binding) {
4421
+ return {
4422
+ async get() {
4423
+ throw new Error(`Offline Secrets Store binding "${binding}" has no value. Pass fixtures.secretsStore.${binding} or write a local secret with devflare secrets --local.`);
4424
+ }
4425
+ };
4426
+ }
4427
+ function isWorkflowBinding(value) {
4428
+ return typeof value.create === "function";
4429
+ }
4430
+ function isPipelineBinding(value) {
4431
+ return typeof value?.send === "function";
4432
+ }
4433
+ function isImagesBinding(value) {
4434
+ return typeof value?.input === "function";
4435
+ }
4436
+ function isHyperdriveBinding(value) {
4437
+ return typeof value?.connectionString === "string";
4438
+ }
4439
+ function isMediaBinding(value) {
4440
+ return typeof value?.input === "function";
4441
+ }
4442
+ function isArtifactsBinding2(value) {
4443
+ return typeof value?.create === "function";
4444
+ }
4445
+ function isAISearchInstance2(value) {
4446
+ return typeof value?.search === "function";
4447
+ }
4448
+ function isAISearchNamespace(value) {
4449
+ return typeof value?.get === "function";
4450
+ }
4451
+ function addBoundary(remoteBoundaries, service, binding, reason) {
4452
+ remoteBoundaries.push({
4453
+ service,
4454
+ binding,
4455
+ reason,
4456
+ recommendation: describeOfflineSupport(service).recommendation
4457
+ });
4458
+ }
4459
+ function addStaticBindings(env3, config) {
4460
+ if (config.vars) {
4461
+ Object.assign(env3, config.vars);
4462
+ }
4463
+ }
4464
+ function addRateLimitBindings(env3, bindings) {
4465
+ for (const [name, binding] of Object.entries(bindings?.rateLimits ?? {})) {
4466
+ env3[name] = createMockRateLimit(binding.simple);
4467
+ }
4468
+ }
4469
+ function addVersionMetadataBinding(env3, bindings) {
4470
+ if (bindings?.versionMetadata) {
4471
+ env3[bindings.versionMetadata.binding] = createMockVersionMetadata();
4472
+ }
4473
+ }
4474
+ function getHyperdriveConnectionString(name, binding, fixture) {
4475
+ if (typeof fixture === "string") {
4476
+ return fixture;
4477
+ }
4478
+ const envValue = process.env[`CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_${name}`] ?? process.env[`WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_${name}`];
4479
+ if (envValue?.trim()) {
4480
+ return envValue;
4481
+ }
4482
+ return normalizeHyperdriveBinding(binding).localConnectionString;
4483
+ }
4484
+ function addHyperdriveBindings(env3, bindings, fixtures, missingFixtures) {
4485
+ for (const [name, binding] of Object.entries(bindings?.hyperdrive ?? {})) {
4486
+ const fixture = fixtures.hyperdrive?.[name];
4487
+ if (isHyperdriveBinding(fixture)) {
4488
+ env3[name] = fixture;
4489
+ continue;
4490
+ }
4491
+ const connectionString = getHyperdriveConnectionString(name, binding, fixture);
4492
+ if (connectionString) {
4493
+ env3[name] = createMockHyperdrive(connectionString);
4494
+ continue;
4495
+ }
4496
+ missingFixtures.push({
4497
+ service: "hyperdrive",
4498
+ binding: name,
4499
+ reason: `Hyperdrive binding "${name}" has no local connection string. Configure bindings.hyperdrive.${name}.localConnectionString or pass fixtures.hyperdrive.${name}.`
4500
+ });
4501
+ }
4502
+ }
4503
+ function addWorkerLoaderBindings(env3, bindings, fixtures) {
4504
+ for (const name of Object.keys(bindings?.workerLoaders ?? {})) {
4505
+ env3[name] = createMockWorkerLoader(fixtures.workerLoaders?.[name]);
4506
+ }
4507
+ }
4508
+ function addMTLSCertificateBindings(env3, bindings, fixtures) {
4509
+ for (const name of Object.keys(bindings?.mtlsCertificates ?? {})) {
4510
+ env3[name] = createMockMTLSCertificate(fixtures.mtlsCertificates?.[name]);
4511
+ }
4512
+ }
4513
+ function addDispatchNamespaceBindings(env3, bindings, fixtures) {
4514
+ for (const name of Object.keys(bindings?.dispatchNamespaces ?? {})) {
4515
+ env3[name] = createMockDispatchNamespace(fixtures.dispatchNamespaces?.[name]);
4516
+ }
4517
+ }
4518
+ function addWorkflowBindings(env3, bindings, fixtures) {
4519
+ for (const name of Object.keys(bindings?.workflows ?? {})) {
4520
+ const fixture = fixtures.workflows?.[name];
4521
+ env3[name] = fixture && isWorkflowBinding(fixture) ? fixture : createMockWorkflow(fixture);
4522
+ }
4523
+ }
4524
+ function addPipelineBindings(env3, bindings, fixtures) {
4525
+ for (const name of Object.keys(bindings?.pipelines ?? {})) {
4526
+ const fixture = fixtures.pipelines?.[name];
4527
+ env3[name] = isPipelineBinding(fixture) ? fixture : createMockPipeline();
4528
+ }
4529
+ }
4530
+ function addImagesBindings(env3, bindings, fixtures) {
4531
+ for (const name of Object.keys(bindings?.images ?? {})) {
4532
+ const fixture = fixtures.images?.[name];
4533
+ env3[name] = isImagesBinding(fixture) ? fixture : createMockImagesBinding(fixture);
4534
+ }
4535
+ }
4536
+ function addMediaBindings(env3, bindings, fixtures) {
4537
+ for (const name of Object.keys(bindings?.media ?? {})) {
4538
+ const fixture = fixtures.media?.[name];
4539
+ env3[name] = isMediaBinding(fixture) ? fixture : createMockMediaBinding(fixture);
4540
+ }
4541
+ }
4542
+ function addArtifactsBindings(env3, bindings, fixtures) {
4543
+ for (const name of Object.keys(bindings?.artifacts ?? {})) {
4544
+ const fixture = fixtures.artifacts?.[name];
4545
+ env3[name] = isArtifactsBinding2(fixture) ? fixture : createMockArtifacts(fixture);
4546
+ }
4547
+ }
4548
+ function addSecretsStoreBindings(env3, bindings, fixtures, localSecretValues, missingFixtures) {
4549
+ for (const name of Object.keys(bindings?.secretsStore ?? {})) {
4550
+ const value = fixtures.secretsStore?.[name] ?? localSecretValues[name];
4551
+ if (value === undefined) {
4552
+ missingFixtures.push({
4553
+ service: "secretsStore",
4554
+ binding: name,
4555
+ reason: `Secrets Store values are not present in fixtures or the local secret store; pass fixtures.secretsStore.${name} or run devflare secrets --local.`
4556
+ });
4557
+ env3[name] = createMissingSecret(name);
4558
+ } else {
4559
+ env3[name] = createMockSecretsStoreSecret(value);
4560
+ }
4561
+ }
4562
+ }
4563
+ function addAISearchBindings(env3, bindings, fixtures) {
4564
+ for (const [name, binding] of Object.entries(bindings?.aiSearch ?? {})) {
4565
+ const fixture = fixtures.aiSearch?.[name];
4566
+ env3[name] = isAISearchInstance2(fixture) ? fixture : createMockAISearchInstance({
4567
+ id: binding.instanceName,
4568
+ ...fixture
4569
+ });
4570
+ }
4571
+ }
4572
+ function addAISearchNamespaceBindings(env3, bindings, fixtures) {
4573
+ for (const [name, binding] of Object.entries(bindings?.aiSearchNamespaces ?? {})) {
4574
+ const fixture = fixtures.aiSearchNamespaces?.[name];
4575
+ env3[name] = isAISearchNamespace(fixture) ? fixture : createMockAISearchNamespace({
4576
+ namespace: binding.namespace,
4577
+ ...fixture
4578
+ });
4579
+ }
4580
+ }
4581
+ function addRemoteBoundaries(remoteBoundaries, bindings) {
4582
+ if (bindings?.ai) {
4583
+ addBoundary(remoteBoundaries, "ai", bindings.ai.binding || "AI", "Workers AI inference is not available in offline local simulations.");
4584
+ }
4585
+ for (const name of Object.keys(bindings?.vectorize ?? {})) {
4586
+ addBoundary(remoteBoundaries, "vectorize", name, "Vectorize has no offline local simulation in Cloudflare local development.");
4587
+ }
4588
+ }
4589
+ function createOfflineBindings(config, fixtures = {}, options = {}) {
4590
+ const env3 = {};
4591
+ const remoteBoundaries = [];
4592
+ const missingFixtures = [];
4593
+ const bindings = config.bindings;
4594
+ const localSecretValues = options.cwd && options.useLocalSecrets !== false ? resolveLocalSecretValuesForBindings(config, options.cwd) : {};
4595
+ addStaticBindings(env3, config);
4596
+ addRateLimitBindings(env3, bindings);
4597
+ addVersionMetadataBinding(env3, bindings);
4598
+ addHyperdriveBindings(env3, bindings, fixtures, missingFixtures);
4599
+ addWorkerLoaderBindings(env3, bindings, fixtures);
4600
+ addMTLSCertificateBindings(env3, bindings, fixtures);
4601
+ addDispatchNamespaceBindings(env3, bindings, fixtures);
4602
+ addWorkflowBindings(env3, bindings, fixtures);
4603
+ addPipelineBindings(env3, bindings, fixtures);
4604
+ addImagesBindings(env3, bindings, fixtures);
4605
+ addMediaBindings(env3, bindings, fixtures);
4606
+ addArtifactsBindings(env3, bindings, fixtures);
4607
+ addSecretsStoreBindings(env3, bindings, fixtures, localSecretValues, missingFixtures);
4608
+ addAISearchBindings(env3, bindings, fixtures);
4609
+ addAISearchNamespaceBindings(env3, bindings, fixtures);
4610
+ addRemoteBoundaries(remoteBoundaries, bindings);
4611
+ if (fixtures.custom) {
4612
+ Object.assign(env3, fixtures.custom);
4613
+ }
4614
+ return {
4615
+ env: env3,
4616
+ support: getOfflineSupportMatrix(),
4617
+ remoteBoundaries,
4618
+ missingFixtures
4619
+ };
4620
+ }
4621
+ function createOfflineEnv(config, fixtures = {}, options = {}) {
4622
+ return createOfflineBindings(config, fixtures, options).env;
4623
+ }
42
4624
  export {
43
4625
  worker,
44
4626
  withTestContext,
45
- testEnv,
46
4627
  tail,
47
- stopBridgeTestContext,
4628
+ stopActiveContainers,
48
4629
  shouldSkip,
49
4630
  scheduled,
50
4631
  resolveServiceBindings,
51
4632
  resolveDOBindings,
52
- queue,
53
- isRemoteModeEnabled,
4633
+ queue2 as queue,
54
4634
  hasServiceBindings,
55
4635
  hasCrossWorkerDOs,
56
- getBridgeTestContext,
4636
+ getOfflineSupportMatrix,
4637
+ getContainerSkipReason,
57
4638
  env,
58
4639
  email,
4640
+ detectContainerEngine,
4641
+ describeOfflineSupport,
59
4642
  createTestContext,
60
- createMultiWorkerContext,
4643
+ createOfflineEnv,
4644
+ createOfflineBindings,
4645
+ createMockWorkflow,
4646
+ createMockWorkerLoader,
4647
+ createMockVersionMetadata,
61
4648
  createMockTestContext,
4649
+ createMockSecretsStoreSecret,
4650
+ createMockRateLimit,
62
4651
  createMockR2,
63
4652
  createMockQueue,
4653
+ createMockPipeline,
4654
+ createMockMediaBinding,
4655
+ createMockMTLSCertificate,
64
4656
  createMockKV,
4657
+ createMockImagesBinding,
4658
+ createMockHyperdrive,
65
4659
  createMockEnv,
4660
+ createMockDispatchNamespace,
66
4661
  createMockD1,
67
- createEntrypointScript,
68
- createBridgeTestContext,
4662
+ createMockArtifacts,
4663
+ createMockAISearchNamespace,
4664
+ createMockAISearchInstance,
4665
+ createContainerManager,
4666
+ containers,
69
4667
  clearBundleCache,
70
4668
  cf
71
4669
  };