devflare 1.0.0-next.14 → 1.0.0-next.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/LLM.md +9360 -1784
  2. package/README.md +391 -32
  3. package/bin/devflare.js +17 -7
  4. package/dist/account-0w8wdzjv.js +475 -0
  5. package/dist/account-eygq6qx7.js +475 -0
  6. package/dist/account-fw8nafav.js +475 -0
  7. package/dist/account-pzq69nys.js +475 -0
  8. package/dist/account-s66jb15j.js +475 -0
  9. package/dist/api-d6ekexs5.js +25 -0
  10. package/dist/bridge/index.d.ts +1 -1
  11. package/dist/bridge/index.d.ts.map +1 -1
  12. package/dist/bridge/miniflare.d.ts.map +1 -1
  13. package/dist/bridge/protocol.d.ts +1 -1
  14. package/dist/bridge/protocol.d.ts.map +1 -1
  15. package/dist/bridge/proxy.d.ts +0 -4
  16. package/dist/bridge/proxy.d.ts.map +1 -1
  17. package/dist/bridge/serialization.d.ts.map +1 -1
  18. package/dist/bridge/server.d.ts +1 -1
  19. package/dist/bridge/server.d.ts.map +1 -1
  20. package/dist/browser-shim/handler.d.ts +1 -1
  21. package/dist/browser-shim/handler.d.ts.map +1 -1
  22. package/dist/browser.d.ts +1651 -34
  23. package/dist/browser.d.ts.map +1 -1
  24. package/dist/build-1kmkwqgh.js +53 -0
  25. package/dist/build-506kjhcm.js +53 -0
  26. package/dist/build-66866ahs.js +53 -0
  27. package/dist/build-g1adm3ww.js +53 -0
  28. package/dist/build-p3r3117t.js +53 -0
  29. package/dist/bundler/do-bundler.d.ts.map +1 -1
  30. package/dist/bundler/rolldown-shared.d.ts +24 -0
  31. package/dist/bundler/rolldown-shared.d.ts.map +1 -0
  32. package/dist/bundler/worker-bundler.d.ts +0 -1
  33. package/dist/bundler/worker-bundler.d.ts.map +1 -1
  34. package/dist/cli/command-utils.d.ts +18 -0
  35. package/dist/cli/command-utils.d.ts.map +1 -0
  36. package/dist/cli/commands/account.d.ts +1 -1
  37. package/dist/cli/commands/account.d.ts.map +1 -1
  38. package/dist/cli/commands/build-artifacts.d.ts +27 -0
  39. package/dist/cli/commands/build-artifacts.d.ts.map +1 -0
  40. package/dist/cli/commands/build.d.ts.map +1 -1
  41. package/dist/cli/commands/config.d.ts +4 -0
  42. package/dist/cli/commands/config.d.ts.map +1 -0
  43. package/dist/cli/commands/deploy.d.ts.map +1 -1
  44. package/dist/cli/commands/dev.d.ts.map +1 -1
  45. package/dist/cli/commands/doctor.d.ts.map +1 -1
  46. package/dist/cli/commands/init.d.ts.map +1 -1
  47. package/dist/cli/commands/login.d.ts +4 -0
  48. package/dist/cli/commands/login.d.ts.map +1 -0
  49. package/dist/cli/commands/previews-support/cleanup.d.ts +9 -0
  50. package/dist/cli/commands/previews-support/cleanup.d.ts.map +1 -0
  51. package/dist/cli/commands/previews-support/family.d.ts +10 -0
  52. package/dist/cli/commands/previews-support/family.d.ts.map +1 -0
  53. package/dist/cli/commands/previews-support/render.d.ts +8 -0
  54. package/dist/cli/commands/previews-support/render.d.ts.map +1 -0
  55. package/dist/cli/commands/previews-support/theme.d.ts +10 -0
  56. package/dist/cli/commands/previews-support/theme.d.ts.map +1 -0
  57. package/dist/cli/commands/previews-support/types.d.ts +70 -0
  58. package/dist/cli/commands/previews-support/types.d.ts.map +1 -0
  59. package/dist/cli/commands/previews.d.ts +4 -0
  60. package/dist/cli/commands/previews.d.ts.map +1 -0
  61. package/dist/cli/commands/productions.d.ts +4 -0
  62. package/dist/cli/commands/productions.d.ts.map +1 -0
  63. package/dist/cli/commands/token.d.ts +4 -0
  64. package/dist/cli/commands/token.d.ts.map +1 -0
  65. package/dist/cli/commands/type-generation/discovery.d.ts +7 -0
  66. package/dist/cli/commands/type-generation/discovery.d.ts.map +1 -0
  67. package/dist/cli/commands/type-generation/generator.d.ts +44 -0
  68. package/dist/cli/commands/type-generation/generator.d.ts.map +1 -0
  69. package/dist/cli/commands/type-generation/models.d.ts +27 -0
  70. package/dist/cli/commands/type-generation/models.d.ts.map +1 -0
  71. package/dist/cli/commands/types.d.ts.map +1 -1
  72. package/dist/cli/commands/worker.d.ts +4 -0
  73. package/dist/cli/commands/worker.d.ts.map +1 -0
  74. package/dist/cli/config-path.d.ts +2 -1
  75. package/dist/cli/config-path.d.ts.map +1 -1
  76. package/dist/cli/deploy-strategy.d.ts +17 -0
  77. package/dist/cli/deploy-strategy.d.ts.map +1 -0
  78. package/dist/cli/deploy-target.d.ts +17 -0
  79. package/dist/cli/deploy-target.d.ts.map +1 -0
  80. package/dist/cli/generated-artifacts.d.ts +12 -0
  81. package/dist/cli/generated-artifacts.d.ts.map +1 -0
  82. package/dist/cli/help-pages/pages/account.d.ts +3 -0
  83. package/dist/cli/help-pages/pages/account.d.ts.map +1 -0
  84. package/dist/cli/help-pages/pages/core.d.ts +4 -0
  85. package/dist/cli/help-pages/pages/core.d.ts.map +1 -0
  86. package/dist/cli/help-pages/pages/index.d.ts +3 -0
  87. package/dist/cli/help-pages/pages/index.d.ts.map +1 -0
  88. package/dist/cli/help-pages/pages/misc.d.ts +3 -0
  89. package/dist/cli/help-pages/pages/misc.d.ts.map +1 -0
  90. package/dist/cli/help-pages/pages/previews.d.ts +3 -0
  91. package/dist/cli/help-pages/pages/previews.d.ts.map +1 -0
  92. package/dist/cli/help-pages/pages/productions.d.ts +3 -0
  93. package/dist/cli/help-pages/pages/productions.d.ts.map +1 -0
  94. package/dist/cli/help-pages/render.d.ts +12 -0
  95. package/dist/cli/help-pages/render.d.ts.map +1 -0
  96. package/dist/cli/help-pages/shared.d.ts +15 -0
  97. package/dist/cli/help-pages/shared.d.ts.map +1 -0
  98. package/dist/cli/help-pages/types.d.ts +23 -0
  99. package/dist/cli/help-pages/types.d.ts.map +1 -0
  100. package/dist/cli/help.d.ts +6 -0
  101. package/dist/cli/help.d.ts.map +1 -0
  102. package/dist/cli/index.d.ts +1 -0
  103. package/dist/cli/index.d.ts.map +1 -1
  104. package/dist/cli/preview-bindings.d.ts +42 -0
  105. package/dist/cli/preview-bindings.d.ts.map +1 -0
  106. package/dist/cli/preview.d.ts +11 -0
  107. package/dist/cli/preview.d.ts.map +1 -0
  108. package/dist/cli/ui.d.ts +37 -0
  109. package/dist/cli/ui.d.ts.map +1 -0
  110. package/dist/cli/workspace-build-guard.d.ts +14 -0
  111. package/dist/cli/workspace-build-guard.d.ts.map +1 -0
  112. package/dist/cloudflare/account-core.d.ts +6 -0
  113. package/dist/cloudflare/account-core.d.ts.map +1 -0
  114. package/dist/cloudflare/account-resources.d.ts +40 -0
  115. package/dist/cloudflare/account-resources.d.ts.map +1 -0
  116. package/dist/cloudflare/account-status.d.ts +11 -0
  117. package/dist/cloudflare/account-status.d.ts.map +1 -0
  118. package/dist/cloudflare/account-workers.d.ts +14 -0
  119. package/dist/cloudflare/account-workers.d.ts.map +1 -0
  120. package/dist/cloudflare/account.d.ts +7 -64
  121. package/dist/cloudflare/account.d.ts.map +1 -1
  122. package/dist/cloudflare/api.d.ts +4 -0
  123. package/dist/cloudflare/api.d.ts.map +1 -1
  124. package/dist/cloudflare/index.d.ts +57 -2
  125. package/dist/cloudflare/index.d.ts.map +1 -1
  126. package/dist/cloudflare/kv-namespace.d.ts +3 -0
  127. package/dist/cloudflare/kv-namespace.d.ts.map +1 -0
  128. package/dist/cloudflare/preferences.d.ts.map +1 -1
  129. package/dist/cloudflare/preview-registry-cache.d.ts +6 -0
  130. package/dist/cloudflare/preview-registry-cache.d.ts.map +1 -0
  131. package/dist/cloudflare/preview-registry-records.d.ts +61 -0
  132. package/dist/cloudflare/preview-registry-records.d.ts.map +1 -0
  133. package/dist/cloudflare/preview-registry-store.d.ts +14 -0
  134. package/dist/cloudflare/preview-registry-store.d.ts.map +1 -0
  135. package/dist/cloudflare/preview-registry-types.d.ts +103 -0
  136. package/dist/cloudflare/preview-registry-types.d.ts.map +1 -0
  137. package/dist/cloudflare/preview-registry.d.ts +42 -0
  138. package/dist/cloudflare/preview-registry.d.ts.map +1 -0
  139. package/dist/cloudflare/registry-schema.d.ts +253 -0
  140. package/dist/cloudflare/registry-schema.d.ts.map +1 -0
  141. package/dist/cloudflare/tokens.d.ts +18 -0
  142. package/dist/cloudflare/tokens.d.ts.map +1 -0
  143. package/dist/cloudflare/types.d.ts +122 -5
  144. package/dist/cloudflare/types.d.ts.map +1 -1
  145. package/dist/cloudflare/usage.d.ts.map +1 -1
  146. package/dist/config/compiler.d.ts +4 -0
  147. package/dist/config/compiler.d.ts.map +1 -1
  148. package/dist/config/framework-providers.d.ts +9 -0
  149. package/dist/config/framework-providers.d.ts.map +1 -0
  150. package/dist/config/index.d.ts +5 -3
  151. package/dist/config/index.d.ts.map +1 -1
  152. package/dist/config/loader.d.ts +1 -0
  153. package/dist/config/loader.d.ts.map +1 -1
  154. package/dist/config/preview-resources.d.ts +77 -0
  155. package/dist/config/preview-resources.d.ts.map +1 -0
  156. package/dist/config/preview.d.ts +31 -0
  157. package/dist/config/preview.d.ts.map +1 -0
  158. package/dist/config/ref.d.ts +0 -22
  159. package/dist/config/ref.d.ts.map +1 -1
  160. package/dist/config/resolve.d.ts +1 -0
  161. package/dist/config/resolve.d.ts.map +1 -1
  162. package/dist/config/resource-resolution.d.ts +60 -0
  163. package/dist/config/resource-resolution.d.ts.map +1 -0
  164. package/dist/config/schema-bindings.d.ts +693 -0
  165. package/dist/config/schema-bindings.d.ts.map +1 -0
  166. package/dist/config/schema-build.d.ts +67 -0
  167. package/dist/config/schema-build.d.ts.map +1 -0
  168. package/dist/config/schema-env.d.ts +1341 -0
  169. package/dist/config/schema-env.d.ts.map +1 -0
  170. package/dist/config/schema-normalization.d.ts +64 -0
  171. package/dist/config/schema-normalization.d.ts.map +1 -0
  172. package/dist/config/schema-runtime.d.ts +230 -0
  173. package/dist/config/schema-runtime.d.ts.map +1 -0
  174. package/dist/config/schema.d.ts +640 -3669
  175. package/dist/config/schema.d.ts.map +1 -1
  176. package/dist/config-entry.d.ts +5 -0
  177. package/dist/config-entry.d.ts.map +1 -0
  178. package/dist/config-fjwke42y.js +59 -0
  179. package/dist/config-hwdqjse7.js +59 -0
  180. package/dist/config-pxvewrhv.js +59 -0
  181. package/dist/config-q0g5qdga.js +59 -0
  182. package/dist/decorators/durable-object.d.ts.map +1 -1
  183. package/dist/deploy-7nmzc9r8.js +609 -0
  184. package/dist/deploy-csfhdr64.js +691 -0
  185. package/dist/deploy-ex4g5avz.js +621 -0
  186. package/dist/deploy-jnb0bhka.js +609 -0
  187. package/dist/deploy-tp0g6qdp.js +609 -0
  188. package/dist/deploy-ykpcjkc2.js +690 -0
  189. package/dist/{dev-c1xc1gq9.js → dev-2pd33m28.js} +392 -348
  190. package/dist/dev-7ef5e2j1.js +2409 -0
  191. package/dist/dev-8nssqatr.js +2409 -0
  192. package/dist/dev-grznx8fn.js +2409 -0
  193. package/dist/dev-server/d1-migrations.d.ts +14 -0
  194. package/dist/dev-server/d1-migrations.d.ts.map +1 -0
  195. package/dist/dev-server/gateway-script.d.ts +8 -0
  196. package/dist/dev-server/gateway-script.d.ts.map +1 -0
  197. package/dist/dev-server/runtime-stdio.d.ts.map +1 -1
  198. package/dist/dev-server/server.d.ts.map +1 -1
  199. package/dist/dev-server/vite-process.d.ts +14 -0
  200. package/dist/dev-server/vite-process.d.ts.map +1 -0
  201. package/dist/dev-server/vite-utils.d.ts +1 -1
  202. package/dist/dev-server/vite-utils.d.ts.map +1 -1
  203. package/dist/dev-server/worker-source-watcher.d.ts +11 -0
  204. package/dist/dev-server/worker-source-watcher.d.ts.map +1 -0
  205. package/dist/dev-server/worker-surface-paths.d.ts +6 -0
  206. package/dist/dev-server/worker-surface-paths.d.ts.map +1 -0
  207. package/dist/{doctor-z4ffybce.js → doctor-04ammrrh.js} +67 -31
  208. package/dist/doctor-fmjj65mc.js +245 -0
  209. package/dist/doctor-fzkznce1.js +245 -0
  210. package/dist/doctor-sa5xv1bz.js +245 -0
  211. package/dist/index-091sh1ma.js +1229 -0
  212. package/dist/index-0apbm26n.js +788 -0
  213. package/dist/index-0eqksag4.js +418 -0
  214. package/dist/{index-dr6sbp8d.js → index-0kfzdywd.js} +15 -2
  215. package/dist/index-0w826dsr.js +379 -0
  216. package/dist/{index-rfhx0yd5.js → index-11m5a8wd.js} +110 -32
  217. package/dist/{index-xxwbb2nt.js → index-1sp39f2f.js} +114 -58
  218. package/dist/index-2jnrqbny.js +1301 -0
  219. package/dist/index-2pb7b9mw.js +378 -0
  220. package/dist/{index-0kzg8wed.js → index-2x53aqjm.js} +1071 -890
  221. package/dist/index-3ke5d2vn.js +1229 -0
  222. package/dist/index-43dq8yx8.js +788 -0
  223. package/dist/index-4rrttqj5.js +378 -0
  224. package/dist/index-4v9bc2pc.js +1367 -0
  225. package/dist/index-61jsjnsv.js +280 -0
  226. package/dist/index-6jef5emv.js +176 -0
  227. package/dist/index-6psz1h4c.js +788 -0
  228. package/dist/index-72mve6vh.js +168 -0
  229. package/dist/{index-zbvmtcn2.js → index-74198nxd.js} +179 -77
  230. package/dist/index-7g8zyws4.js +192 -0
  231. package/dist/index-7kcxjhta.js +456 -0
  232. package/dist/index-7v583xan.js +418 -0
  233. package/dist/index-7x0ybbtx.js +133 -0
  234. package/dist/index-816krz9p.js +52 -0
  235. package/dist/index-82f1z98k.js +41 -0
  236. package/dist/index-8t5nb4qx.js +133 -0
  237. package/dist/index-9az6s7ad.js +52 -0
  238. package/dist/{index-59df49vn.js → index-9ba1etyz.js} +29 -51
  239. package/dist/{index-001mw014.js → index-9fbtk7gv.js} +134 -248
  240. package/dist/index-9n6djthj.js +490 -0
  241. package/dist/index-aabgympv.js +39 -0
  242. package/dist/index-b8m6883k.js +74 -0
  243. package/dist/{index-5yxg30va.js → index-cgbvmse6.js} +15 -6
  244. package/dist/index-d8etnfef.js +1229 -0
  245. package/dist/index-e9yw4d6y.js +133 -0
  246. package/dist/index-epw1jxz5.js +1204 -0
  247. package/dist/index-f85s8gj3.js +2649 -0
  248. package/dist/index-fe2ngvh7.js +1229 -0
  249. package/dist/index-fvsadj32.js +192 -0
  250. package/dist/index-gs4y9gdf.js +456 -0
  251. package/dist/{index-fef08w43.js → index-h18pxvzs.js} +7 -6
  252. package/dist/index-hfj1a2c4.js +2649 -0
  253. package/dist/{index-8gtqgb3q.js → index-hjy8ctpc.js} +14 -92
  254. package/dist/index-htzf0py1.js +1204 -0
  255. package/dist/index-j185x270.js +897 -0
  256. package/dist/index-jb75kwa4.js +519 -0
  257. package/dist/index-jwd8pcb2.js +897 -0
  258. package/dist/index-k29yjhv0.js +52 -0
  259. package/dist/index-k6vq6kkt.js +456 -0
  260. package/dist/{index-vky23txa.js → index-m3fmw6mx.js} +2 -2
  261. package/dist/index-maxpsfk8.js +402 -0
  262. package/dist/index-mbdmrner.js +402 -0
  263. package/dist/index-mea5bc45.js +418 -0
  264. package/dist/index-mqekt778.js +185 -0
  265. package/dist/index-na3mnm1k.js +74 -0
  266. package/dist/index-p03n4qet.js +1367 -0
  267. package/dist/index-p296ban8.js +191 -0
  268. package/dist/index-pnbs1b8k.js +280 -0
  269. package/dist/index-q4kaz181.js +1207 -0
  270. package/dist/index-ry131z23.js +378 -0
  271. package/dist/index-sgb7c8nm.js +402 -0
  272. package/dist/index-sqrksgb2.js +133 -0
  273. package/dist/index-stgn34cr.js +148 -0
  274. package/dist/{index-v8vvsn9x.js → index-t08te69w.js} +1 -18
  275. package/dist/index-thna1tkd.js +280 -0
  276. package/dist/index-v5nmqthy.js +74 -0
  277. package/dist/{index-n932ytmq.js → index-vt4yxkmf.js} +2 -2
  278. package/dist/index-wyq6c6yj.js +402 -0
  279. package/dist/index-wztc9stx.js +418 -0
  280. package/dist/index-x9cwdxw5.js +456 -0
  281. package/dist/index-xk9djfjp.js +519 -0
  282. package/dist/index-yc0gcchc.js +418 -0
  283. package/dist/index-yqbxjysa.js +897 -0
  284. package/dist/index-yzddwp02.js +788 -0
  285. package/dist/index-zfhq6s96.js +74 -0
  286. package/dist/index-zt22fe2j.js +54 -0
  287. package/dist/index-zyt5byt6.js +2649 -0
  288. package/dist/index.d.ts +2 -2
  289. package/dist/index.d.ts.map +1 -1
  290. package/dist/{init-na2atvz2.js → init-r4hnxan3.js} +24 -17
  291. package/dist/login-2hnz4m4n.js +77 -0
  292. package/dist/login-5bsxxpvc.js +77 -0
  293. package/dist/login-6tzvczw2.js +77 -0
  294. package/dist/login-bhaw72zc.js +77 -0
  295. package/dist/login-x8tgckqm.js +77 -0
  296. package/dist/previews-3rn8mz2c.js +1168 -0
  297. package/dist/previews-d487qde5.js +1200 -0
  298. package/dist/previews-gm3z0syj.js +1168 -0
  299. package/dist/previews-j9ymq4ys.js +1169 -0
  300. package/dist/previews-q031mx34.js +1168 -0
  301. package/dist/productions-120xg0aq.js +505 -0
  302. package/dist/productions-5ev5qweg.js +505 -0
  303. package/dist/productions-me3tdvr9.js +505 -0
  304. package/dist/productions-p5rbgp2f.js +505 -0
  305. package/dist/productions-x9p0pym1.js +505 -0
  306. package/dist/runtime/context-events.d.ts +13 -0
  307. package/dist/runtime/context-events.d.ts.map +1 -0
  308. package/dist/runtime/context-types.d.ts +82 -0
  309. package/dist/runtime/context-types.d.ts.map +1 -0
  310. package/dist/runtime/context.d.ts +6 -267
  311. package/dist/runtime/context.d.ts.map +1 -1
  312. package/dist/runtime/exports.d.ts +3 -3
  313. package/dist/runtime/index.d.ts +1 -1
  314. package/dist/runtime/index.d.ts.map +1 -1
  315. package/dist/runtime/middleware.d.ts +8 -38
  316. package/dist/runtime/middleware.d.ts.map +1 -1
  317. package/dist/src/browser.js +23 -14
  318. package/dist/src/cli/index.js +3 -1
  319. package/dist/src/cloudflare/index.js +49 -2
  320. package/dist/src/config-entry.js +14 -0
  321. package/dist/src/index.js +33 -20
  322. package/dist/src/runtime/index.js +3 -9
  323. package/dist/src/sveltekit/index.js +10 -7
  324. package/dist/src/test/index.js +16 -18
  325. package/dist/src/vite/index.js +7 -4
  326. package/dist/sveltekit/platform.d.ts +1 -1
  327. package/dist/sveltekit/platform.d.ts.map +1 -1
  328. package/dist/test/cf.d.ts +10 -10
  329. package/dist/test/email.d.ts.map +1 -1
  330. package/dist/test/index.d.ts +1 -6
  331. package/dist/test/index.d.ts.map +1 -1
  332. package/dist/test/queue.d.ts.map +1 -1
  333. package/dist/test/remote-ai.d.ts.map +1 -1
  334. package/dist/test/remote-cloudflare.d.ts +13 -0
  335. package/dist/test/remote-cloudflare.d.ts.map +1 -0
  336. package/dist/test/remote-vectorize.d.ts.map +1 -1
  337. package/dist/test/resolve-service-bindings.d.ts.map +1 -1
  338. package/dist/test/scheduled.d.ts.map +1 -1
  339. package/dist/test/should-skip.d.ts +0 -18
  340. package/dist/test/should-skip.d.ts.map +1 -1
  341. package/dist/test/simple-context-durable-objects.d.ts +6 -0
  342. package/dist/test/simple-context-durable-objects.d.ts.map +1 -0
  343. package/dist/test/simple-context-gateway-script.d.ts +2 -0
  344. package/dist/test/simple-context-gateway-script.d.ts.map +1 -0
  345. package/dist/test/simple-context-paths.d.ts +40 -0
  346. package/dist/test/simple-context-paths.d.ts.map +1 -0
  347. package/dist/test/simple-context.d.ts +1 -23
  348. package/dist/test/simple-context.d.ts.map +1 -1
  349. package/dist/test/tail.d.ts.map +1 -1
  350. package/dist/test/worker.d.ts.map +1 -1
  351. package/dist/token-kedhcret.js +419 -0
  352. package/dist/token-m8jmnjwk.js +419 -0
  353. package/dist/{types-sffr9681.js → types-0sqwkp7x.js} +244 -139
  354. package/dist/types-1gwr2ex6.js +572 -0
  355. package/dist/types-6e5yx6km.js +572 -0
  356. package/dist/types-p0gckpn6.js +572 -0
  357. package/dist/utils/send-email.d.ts.map +1 -1
  358. package/dist/vite/config-file.d.ts.map +1 -1
  359. package/dist/vite/plugin.d.ts.map +1 -1
  360. package/dist/worker-0srh2jfr.js +513 -0
  361. package/dist/worker-4xrfd10a.js +513 -0
  362. package/dist/worker-entry/composed-worker.d.ts +0 -7
  363. package/dist/worker-entry/composed-worker.d.ts.map +1 -1
  364. package/dist/worker-entry/surface-paths.d.ts +15 -0
  365. package/dist/worker-entry/surface-paths.d.ts.map +1 -0
  366. package/dist/worker-qtam8grz.js +513 -0
  367. package/dist/worker-qzm0b7br.js +513 -0
  368. package/dist/worker-y9ha6g44.js +513 -0
  369. package/package.json +17 -10
  370. package/R2.md +0 -200
  371. package/dist/account-8psavtg6.js +0 -420
  372. package/dist/build-n639efmn.js +0 -101
  373. package/dist/deploy-zvnq6xh7.js +0 -117
  374. package/dist/index-2q3pmzrx.js +0 -90
  375. package/dist/index-f4q0jbnj.js +0 -195
  376. package/dist/index-n7rs26ft.js +0 -77
  377. package/dist/index-tfyxa77h.js +0 -850
  378. package/dist/index-wyf3s77s.js +0 -343
  379. package/dist/test/multi-worker-context.d.ts +0 -114
  380. package/dist/test/multi-worker-context.d.ts.map +0 -1
@@ -0,0 +1,2409 @@
1
+ import {
2
+ bundleWorkerEntry,
3
+ createDOBundler
4
+ } from "./index-9fbtk7gv.js";
5
+ import {
6
+ detectViteProject,
7
+ stopSpawnedProcessTree,
8
+ waitForViteReady
9
+ } from "./index-y1d8za14.js";
10
+ import {
11
+ createCliTheme,
12
+ cyanBold,
13
+ dim,
14
+ logLine,
15
+ yellow
16
+ } from "./index-stgn34cr.js";
17
+ import"./index-3t6rypgc.js";
18
+ import {
19
+ DEFAULT_EMAIL_ENTRY_FILES,
20
+ DEFAULT_FETCH_ENTRY_FILES,
21
+ DEFAULT_QUEUE_ENTRY_FILES,
22
+ DEFAULT_SCHEDULED_ENTRY_FILES,
23
+ hasWorkerSurfacePaths,
24
+ prepareComposedWorkerEntrypoint,
25
+ resolveEffectiveViteProject,
26
+ resolveWorkerSurfacePaths,
27
+ writeGeneratedViteConfig
28
+ } from "./index-74198nxd.js";
29
+ import {
30
+ discoverRoutes,
31
+ getRouteDirectoryCandidate
32
+ } from "./index-1p814k7s.js";
33
+ import"./index-rbht7m9r.js";
34
+ import"./index-9wt9x09k.js";
35
+ import {
36
+ clearLocalSendEmailBindings,
37
+ setLocalSendEmailBindings
38
+ } from "./index-h18pxvzs.js";
39
+ import"./index-pnbs1b8k.js";
40
+ import {
41
+ getLocalD1DatabaseIdentifier,
42
+ getLocalKVNamespaceIdentifier,
43
+ getSingleBrowserBindingName,
44
+ loadConfig,
45
+ resolveConfigPath
46
+ } from "./index-6psz1h4c.js";
47
+ import"./index-xk9djfjp.js";
48
+ import"./index-0w826dsr.js";
49
+ import"./index-6jef5emv.js";
50
+ import"./index-t08te69w.js";
51
+ import {
52
+ __require
53
+ } from "./index-37x76zdn.js";
54
+
55
+ // src/cli/commands/dev.ts
56
+ import { createConsola } from "consola";
57
+ import { relative, resolve as resolve4 } from "pathe";
58
+
59
+ // src/dev-server/server.ts
60
+ import { resolve as resolve3 } from "pathe";
61
+
62
+ // src/browser-shim/server.ts
63
+ import { homedir } from "node:os";
64
+ import { join } from "node:path";
65
+ import { existsSync } from "node:fs";
66
+ import { createServer } from "node:http";
67
+ import puppeteerCore from "puppeteer-core";
68
+ import {
69
+ install,
70
+ resolveBuildId,
71
+ detectBrowserPlatform,
72
+ Browser as BrowserType
73
+ } from "@puppeteer/browsers";
74
+ var sessions = new Map;
75
+ var history = [];
76
+ var cachedExecutablePath = null;
77
+ async function ensureChrome(cacheDir, logger) {
78
+ if (cachedExecutablePath && existsSync(cachedExecutablePath)) {
79
+ return cachedExecutablePath;
80
+ }
81
+ const platform = detectBrowserPlatform();
82
+ if (!platform) {
83
+ throw new Error("Could not detect browser platform");
84
+ }
85
+ const buildId = await resolveBuildId(BrowserType.CHROMEHEADLESSSHELL, platform, "stable");
86
+ logger?.debug(`[BrowserShim] Resolved Chrome Headless Shell build: ${buildId}`);
87
+ const installedBrowser = await install({
88
+ browser: BrowserType.CHROMEHEADLESSSHELL,
89
+ buildId,
90
+ cacheDir,
91
+ downloadProgressCallback: (downloadedBytes, totalBytes) => {
92
+ if (totalBytes > 0) {
93
+ const percent = Math.round(downloadedBytes / totalBytes * 100);
94
+ if (percent % 20 === 0) {
95
+ logger?.info(`[BrowserShim] Downloading Chrome... ${percent}%`);
96
+ }
97
+ }
98
+ }
99
+ });
100
+ cachedExecutablePath = installedBrowser.executablePath;
101
+ logger?.success(`[BrowserShim] Chrome ready: ${installedBrowser.executablePath}`);
102
+ return installedBrowser.executablePath;
103
+ }
104
+ function createBrowserShim(options = {}) {
105
+ const {
106
+ port = 8788,
107
+ host = "127.0.0.1",
108
+ logger,
109
+ verbose = false,
110
+ keepAlive = 60000,
111
+ cacheDir = join(homedir(), ".devflare", "chrome")
112
+ } = options;
113
+ let server = null;
114
+ let executablePath = null;
115
+ let WebSocketServerClass = null;
116
+ let WebSocketClass = null;
117
+ async function acquireSession(acquireOptions) {
118
+ if (!executablePath) {
119
+ throw new Error("Chrome not initialized");
120
+ }
121
+ const browser = await puppeteerCore.launch({
122
+ executablePath,
123
+ headless: true,
124
+ protocolTimeout: 120000,
125
+ args: [
126
+ "--no-sandbox",
127
+ "--disable-setuid-sandbox",
128
+ "--disable-dev-shm-usage",
129
+ "--disable-gpu",
130
+ "--disable-software-rasterizer",
131
+ "--disable-extensions",
132
+ "--disable-background-networking",
133
+ "--disable-background-timer-throttling",
134
+ "--disable-backgrounding-occluded-windows",
135
+ "--disable-renderer-backgrounding",
136
+ "--disable-features=TranslateUI",
137
+ "--disable-ipc-flooding-protection",
138
+ "--disable-default-apps",
139
+ "--mute-audio",
140
+ "--js-flags=--max-old-space-size=4096"
141
+ ]
142
+ });
143
+ const wsEndpoint = browser.wsEndpoint();
144
+ const sessionId = crypto.randomUUID();
145
+ const session = {
146
+ sessionId,
147
+ browser,
148
+ wsEndpoint,
149
+ startTime: Date.now()
150
+ };
151
+ sessions.set(sessionId, session);
152
+ const timeout = acquireOptions?.keep_alive ?? keepAlive;
153
+ if (timeout > 0) {
154
+ session.idleTimeout = setTimeout(async () => {
155
+ const s = sessions.get(sessionId);
156
+ if (s && !s.connectionId) {
157
+ await closeSession(sessionId, 2, "BrowserIdle");
158
+ }
159
+ }, timeout);
160
+ }
161
+ if (verbose) {
162
+ logger?.debug(`[BrowserShim] Acquired session ${sessionId}`);
163
+ }
164
+ return { sessionId };
165
+ }
166
+ async function closeSession(sessionId, closeReason = 1, closeReasonText = "NormalClosure") {
167
+ const session = sessions.get(sessionId);
168
+ if (!session)
169
+ return;
170
+ if (session.idleTimeout) {
171
+ clearTimeout(session.idleTimeout);
172
+ }
173
+ try {
174
+ await session.browser.close();
175
+ } catch {}
176
+ sessions.delete(sessionId);
177
+ history.unshift({
178
+ sessionId,
179
+ startTime: session.startTime,
180
+ endTime: Date.now(),
181
+ closeReason,
182
+ closeReasonText
183
+ });
184
+ if (history.length > 100) {
185
+ history.pop();
186
+ }
187
+ if (verbose) {
188
+ logger?.debug(`[BrowserShim] Closed session ${sessionId}: ${closeReasonText}`);
189
+ }
190
+ }
191
+ async function handleRequest(req, res) {
192
+ const url = new URL(req.url || "/", `http://${host}:${port}`);
193
+ const method = req.method || "GET";
194
+ logger?.debug(`[BrowserShim] ${method} ${url.pathname}${url.search ? url.search : ""}`);
195
+ res.setHeader("Access-Control-Allow-Origin", "*");
196
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
197
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
198
+ if (method === "OPTIONS") {
199
+ res.writeHead(204);
200
+ res.end();
201
+ return;
202
+ }
203
+ if (url.pathname === "/v1/acquire" && (method === "POST" || method === "GET")) {
204
+ try {
205
+ let acquireOptions = {};
206
+ if (method === "GET") {
207
+ const keepAlive2 = url.searchParams.get("keep_alive");
208
+ if (keepAlive2) {
209
+ acquireOptions.keep_alive = parseInt(keepAlive2, 10);
210
+ }
211
+ } else {
212
+ try {
213
+ const body = await readBody(req);
214
+ acquireOptions = JSON.parse(body);
215
+ } catch {}
216
+ }
217
+ const result = await acquireSession(acquireOptions);
218
+ sendJson(res, 200, result);
219
+ } catch (error) {
220
+ const msg = error instanceof Error ? error.message : "Failed to acquire browser";
221
+ logger?.error(`[BrowserShim] Acquire failed: ${msg}`);
222
+ sendJson(res, 500, { error: msg });
223
+ }
224
+ return;
225
+ }
226
+ if (url.pathname === "/v1/sessions" && method === "GET") {
227
+ const activeSessions = Array.from(sessions.values()).map((s) => ({
228
+ sessionId: s.sessionId,
229
+ startTime: s.startTime,
230
+ connectionId: s.connectionId,
231
+ connectionStartTime: s.connectionStartTime
232
+ }));
233
+ sendJson(res, 200, activeSessions);
234
+ return;
235
+ }
236
+ if (url.pathname === "/v1/history" && method === "GET") {
237
+ sendJson(res, 200, history.slice(0, 50));
238
+ return;
239
+ }
240
+ if (url.pathname === "/v1/limits" && method === "GET") {
241
+ sendJson(res, 200, {
242
+ activeSessions: Array.from(sessions.keys()).map((id) => ({ id })),
243
+ allowedBrowserAcquisitions: 10,
244
+ maxConcurrentSessions: 10,
245
+ timeUntilNextAllowedBrowserAcquisition: 0
246
+ });
247
+ return;
248
+ }
249
+ if (url.pathname.startsWith("/v1/session/") && method === "GET") {
250
+ const sessionId = url.pathname.slice("/v1/session/".length);
251
+ const session = sessions.get(sessionId);
252
+ if (!session) {
253
+ sendJson(res, 404, { error: "Session not found" });
254
+ return;
255
+ }
256
+ sendJson(res, 200, {
257
+ sessionId: session.sessionId,
258
+ wsEndpoint: session.wsEndpoint,
259
+ startTime: session.startTime,
260
+ connectionId: session.connectionId,
261
+ connectionStartTime: session.connectionStartTime
262
+ });
263
+ return;
264
+ }
265
+ if (url.pathname === "/_devflare/browser/health") {
266
+ sendJson(res, 200, {
267
+ ok: true,
268
+ activeSessions: sessions.size,
269
+ historySize: history.length,
270
+ executablePath
271
+ });
272
+ return;
273
+ }
274
+ if (url.pathname === "/v1/connectDevtools") {
275
+ res.writeHead(426, { "Content-Type": "text/plain" });
276
+ res.end("WebSocket upgrade required");
277
+ return;
278
+ }
279
+ res.writeHead(404, { "Content-Type": "text/plain" });
280
+ res.end("Not found");
281
+ }
282
+ function readBody(req) {
283
+ return new Promise((resolve, reject) => {
284
+ const chunks = [];
285
+ req.on("data", (chunk) => chunks.push(chunk));
286
+ req.on("end", () => resolve(Buffer.concat(chunks).toString()));
287
+ req.on("error", reject);
288
+ });
289
+ }
290
+ function sendJson(res, status, data) {
291
+ const body = JSON.stringify(data);
292
+ res.writeHead(status, {
293
+ "Content-Type": "application/json",
294
+ "Content-Length": Buffer.byteLength(body)
295
+ });
296
+ res.end(body);
297
+ }
298
+ async function start() {
299
+ logger?.info("[BrowserShim] Ensuring Chrome Headless Shell is available...");
300
+ executablePath = await ensureChrome(cacheDir, logger);
301
+ try {
302
+ const wsModule = await import("ws");
303
+ WebSocketServerClass = wsModule.WebSocketServer || wsModule.default?.WebSocketServer;
304
+ WebSocketClass = wsModule.WebSocket || wsModule.default?.WebSocket || wsModule.default;
305
+ } catch {
306
+ logger?.warn("[BrowserShim] ws package not found, WebSocket proxy disabled");
307
+ logger?.warn("[BrowserShim] Install with: npm install ws");
308
+ }
309
+ server = createServer((req, res) => {
310
+ handleRequest(req, res).catch((error) => {
311
+ logger?.error("[BrowserShim] Request error:", error);
312
+ res.writeHead(500);
313
+ res.end("Internal server error");
314
+ });
315
+ });
316
+ if (WebSocketServerClass) {
317
+ const wss = new WebSocketServerClass({ noServer: true });
318
+ server.on("upgrade", (request, socket, head) => {
319
+ const url = new URL(request.url || "/", `http://${host}:${port}`);
320
+ if (url.pathname !== "/v1/connectDevtools") {
321
+ socket.destroy();
322
+ return;
323
+ }
324
+ const sessionId = url.searchParams.get("browser_session");
325
+ if (!sessionId) {
326
+ socket.write(`HTTP/1.1 400 Bad Request\r
327
+ \r
328
+ `);
329
+ socket.destroy();
330
+ return;
331
+ }
332
+ const session = sessions.get(sessionId);
333
+ if (!session) {
334
+ socket.write(`HTTP/1.1 404 Not Found\r
335
+ \r
336
+ `);
337
+ socket.destroy();
338
+ return;
339
+ }
340
+ const connectionId = crypto.randomUUID();
341
+ session.connectionId = connectionId;
342
+ session.connectionStartTime = Date.now();
343
+ if (session.idleTimeout) {
344
+ clearTimeout(session.idleTimeout);
345
+ session.idleTimeout = undefined;
346
+ }
347
+ wss.handleUpgrade(request, socket, head, (ws) => {
348
+ if (verbose) {
349
+ logger?.debug(`[BrowserShim] WebSocket connected for session ${sessionId}`);
350
+ }
351
+ const chromeWs = new WebSocketClass(session.wsEndpoint);
352
+ let chromeConnected = false;
353
+ const connectTimeout = setTimeout(() => {
354
+ if (!chromeConnected) {
355
+ logger?.error("[BrowserShim] Chrome connection timeout");
356
+ try {
357
+ ws.close(1011, "Chrome connection timeout");
358
+ chromeWs.close();
359
+ } catch {}
360
+ closeSession(sessionId, 5, "ChromeConnectionTimeout").catch(() => {});
361
+ }
362
+ }, 1e4);
363
+ chromeWs.on("open", () => {
364
+ chromeConnected = true;
365
+ clearTimeout(connectTimeout);
366
+ if (verbose) {
367
+ logger?.debug("[BrowserShim] Connected to Chrome DevTools");
368
+ }
369
+ });
370
+ chromeWs.on("message", (data) => {
371
+ if (ws.readyState === 1) {
372
+ ws.send(data);
373
+ }
374
+ });
375
+ chromeWs.on("close", (code, reason) => {
376
+ if (verbose) {
377
+ logger?.debug(`[BrowserShim] Chrome WS closed: ${code}`);
378
+ }
379
+ const validCode = typeof code === "number" && code >= 1000 && code <= 4999 ? code : 1000;
380
+ try {
381
+ ws.close(validCode, reason?.toString?.() || "");
382
+ } catch {}
383
+ closeSession(sessionId, 2, "ChromeDisconnected").catch((err) => {
384
+ logger?.error("[BrowserShim] Error closing session after Chrome disconnect:", err);
385
+ });
386
+ });
387
+ chromeWs.on("error", (error) => {
388
+ logger?.error("[BrowserShim] Chrome WS error:", error.message);
389
+ try {
390
+ ws.close(1011, "Chrome WebSocket error");
391
+ } catch {}
392
+ closeSession(sessionId, 4, "ChromeError").catch((err) => {
393
+ logger?.error("[BrowserShim] Error closing session after Chrome error:", err);
394
+ });
395
+ });
396
+ ws.on("message", (data) => {
397
+ if (chromeWs.readyState === 1) {
398
+ chromeWs.send(data);
399
+ }
400
+ });
401
+ ws.on("close", (code, reason) => {
402
+ if (verbose) {
403
+ logger?.debug(`[BrowserShim] Client WS closed for session ${sessionId}`);
404
+ }
405
+ const validCode = typeof code === "number" && code >= 1000 && code <= 4999 ? code : 1000;
406
+ try {
407
+ chromeWs.close(validCode, reason?.toString?.() || "");
408
+ } catch {}
409
+ const s = sessions.get(sessionId);
410
+ if (s && s.connectionId === connectionId) {
411
+ s.connectionId = undefined;
412
+ s.connectionStartTime = undefined;
413
+ closeSession(sessionId, 1, "ClientDisconnected").catch((err) => {
414
+ logger?.error("[BrowserShim] Error closing session after disconnect:", err);
415
+ });
416
+ }
417
+ });
418
+ ws.on("error", (error) => {
419
+ logger?.error("[BrowserShim] Client WS error:", error.message);
420
+ try {
421
+ chromeWs.close();
422
+ } catch {}
423
+ });
424
+ });
425
+ });
426
+ }
427
+ await new Promise((resolve, reject) => {
428
+ server.on("error", reject);
429
+ server.listen(port, host, () => {
430
+ resolve();
431
+ });
432
+ });
433
+ logger?.success(`Browser shim server ready on http://${host}:${port}`);
434
+ }
435
+ async function stop() {
436
+ for (const sessionId of sessions.keys()) {
437
+ await closeSession(sessionId, 3, "ServerShutdown");
438
+ }
439
+ if (server) {
440
+ await new Promise((resolve) => {
441
+ server.close(() => resolve());
442
+ });
443
+ server = null;
444
+ }
445
+ logger?.info("Browser shim server stopped");
446
+ }
447
+ function getUrl() {
448
+ return `http://${host}:${port}`;
449
+ }
450
+ return {
451
+ start,
452
+ stop,
453
+ getUrl
454
+ };
455
+ }
456
+ // src/browser-shim/binding-worker.ts
457
+ var MAX_CHUNK_SIZE = 1048575;
458
+ function getBrowserBindingScript(browserShimUrl, debug = false) {
459
+ const safeUrl = JSON.stringify(browserShimUrl);
460
+ return `
461
+ // Browser Binding Worker — Proxies puppeteer requests to external browser shim
462
+ // Handles WebSocket upgrades using WebSocketPair for @cloudflare/puppeteer compatibility
463
+
464
+ const BROWSER_SHIM_URL = ${safeUrl}
465
+ const MAX_CHUNK_SIZE = ${MAX_CHUNK_SIZE}
466
+ const DEBUG = ${debug}
467
+ const log = (...args) => DEBUG && console.log('[BrowserBinding]', ...args)
468
+
469
+ export default {
470
+ async fetch(request, env, ctx) {
471
+ const url = new URL(request.url)
472
+ const upgradeHeader = request.headers.get('Upgrade')
473
+ const isWebSocket = upgradeHeader && upgradeHeader.toLowerCase() === 'websocket'
474
+
475
+ log('Request:', url.pathname, isWebSocket ? '(WebSocket)' : '(HTTP)')
476
+
477
+ // Handle WebSocket upgrade for DevTools connection
478
+ if (url.pathname === '/v1/connectDevtools' && isWebSocket) {
479
+ return handleDevToolsWebSocket(request, url)
480
+ }
481
+
482
+ // Proxy all other requests to the browser shim server
483
+ return proxyToBrowserShim(request, url)
484
+ }
485
+ }
486
+
487
+ // Proxy HTTP requests to the external browser shim server
488
+ async function proxyToBrowserShim(request, url) {
489
+ const shimUrl = new URL(url.pathname + url.search, BROWSER_SHIM_URL)
490
+
491
+ log('Proxying to:', shimUrl.toString())
492
+
493
+ const response = await fetch(shimUrl.toString(), {
494
+ method: request.method,
495
+ headers: request.headers,
496
+ body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : undefined
497
+ })
498
+
499
+ log('Response:', response.status)
500
+
501
+ // Return the response as-is
502
+ return new Response(response.body, {
503
+ status: response.status,
504
+ statusText: response.statusText,
505
+ headers: response.headers
506
+ })
507
+ }
508
+
509
+ // Validate WebSocket close code to be in valid range
510
+ function validateCloseCode(code) {
511
+ if (typeof code !== 'number' || isNaN(code)) return 1000
512
+ if (code < 1000 || code > 4999) return 1000
513
+ return code
514
+ }
515
+
516
+ // Split a message into chunks following @cloudflare/puppeteer protocol
517
+ // First chunk has 4-byte LE length header, subsequent chunks are raw payload
518
+ function messageToChunks(message) {
519
+ const data = typeof message === 'string'
520
+ ? new TextEncoder().encode(message)
521
+ : new Uint8Array(message)
522
+
523
+ const chunks = []
524
+ const totalLength = data.length
525
+ let offset = 0
526
+ let isFirst = true
527
+
528
+ while (offset < totalLength) {
529
+ const remaining = totalLength - offset
530
+ let chunkSize
531
+
532
+ if (isFirst) {
533
+ // First chunk: 4-byte header + payload
534
+ chunkSize = Math.min(remaining, MAX_CHUNK_SIZE - 4)
535
+ const chunk = new Uint8Array(chunkSize + 4)
536
+ new DataView(chunk.buffer).setUint32(0, totalLength, true) // little-endian
537
+ chunk.set(data.subarray(offset, offset + chunkSize), 4)
538
+ chunks.push(chunk)
539
+ isFirst = false
540
+ } else {
541
+ // Subsequent chunks: raw payload only
542
+ chunkSize = Math.min(remaining, MAX_CHUNK_SIZE)
543
+ const chunk = data.subarray(offset, offset + chunkSize)
544
+ chunks.push(chunk)
545
+ }
546
+
547
+ offset += chunkSize
548
+ }
549
+
550
+ return chunks
551
+ }
552
+
553
+ // Reassemble chunks back into a complete message
554
+ // Returns null if more chunks are needed
555
+ function chunksToMessage(chunks) {
556
+ if (chunks.length === 0) return null
557
+
558
+ // First chunk must have 4-byte header
559
+ const firstChunk = chunks[0]
560
+ if (firstChunk.length < 4) return null
561
+
562
+ const expectedLength = new DataView(firstChunk.buffer, firstChunk.byteOffset).getUint32(0, true)
563
+
564
+ // Calculate total received payload
565
+ let totalReceived = firstChunk.length - 4 // first chunk payload (minus header)
566
+ for (let i = 1; i < chunks.length; i++) {
567
+ totalReceived += chunks[i].length
568
+ }
569
+
570
+ if (totalReceived < expectedLength) {
571
+ return null // Need more chunks
572
+ }
573
+
574
+ // Reassemble the message
575
+ const assembled = new Uint8Array(expectedLength)
576
+ let offset = 0
577
+
578
+ // Copy first chunk payload (skip 4-byte header)
579
+ const firstPayload = firstChunk.subarray(4)
580
+ assembled.set(firstPayload, offset)
581
+ offset += firstPayload.length
582
+
583
+ // Copy remaining chunks
584
+ for (let i = 1; i < chunks.length; i++) {
585
+ const chunk = chunks[i]
586
+ const toCopy = Math.min(chunk.length, expectedLength - offset)
587
+ assembled.set(chunk.subarray(0, toCopy), offset)
588
+ offset += toCopy
589
+ }
590
+
591
+ return new TextDecoder().decode(assembled)
592
+ }
593
+
594
+ // Handle WebSocket upgrade for DevTools connection
595
+ // Creates a WebSocketPair and proxies to Chrome's DevTools WebSocket
596
+ async function handleDevToolsWebSocket(request, url) {
597
+ const sessionId = url.searchParams.get('browser_session')
598
+ if (!sessionId) {
599
+ return new Response('browser_session parameter required', { status: 400 })
600
+ }
601
+
602
+ log('DevTools WebSocket request for session:', sessionId)
603
+
604
+ // Get session info from browser shim (includes Chrome's wsEndpoint)
605
+ const sessionUrl = new URL('/v1/session/' + sessionId, BROWSER_SHIM_URL)
606
+
607
+ // Add timeout for session fetch
608
+ const controller = new AbortController()
609
+ const timeout = setTimeout(() => controller.abort(), 5000)
610
+
611
+ let sessionRes
612
+ try {
613
+ sessionRes = await fetch(sessionUrl.toString(), { signal: controller.signal })
614
+ } catch (e) {
615
+ DEBUG && console.error('[BrowserBinding] Session fetch timeout or error:', e.message)
616
+ return new Response('Session fetch timeout', { status: 504 })
617
+ } finally {
618
+ clearTimeout(timeout)
619
+ }
620
+
621
+ if (!sessionRes.ok) {
622
+ DEBUG && console.error('[BrowserBinding] Session not found:', sessionId)
623
+ return new Response('Session not found', { status: 404 })
624
+ }
625
+
626
+ const sessionInfo = await sessionRes.json()
627
+ const wsEndpoint = sessionInfo.wsEndpoint
628
+
629
+ if (!wsEndpoint) {
630
+ DEBUG && console.error('[BrowserBinding] No wsEndpoint in session info')
631
+ return new Response('No wsEndpoint for session', { status: 500 })
632
+ }
633
+
634
+ log('Connecting to Chrome DevTools:', wsEndpoint)
635
+
636
+ // Connect to Chrome's DevTools WebSocket
637
+ // Chrome uses ws:// but fetch expects http:// for WebSocket upgrade
638
+ const chromeUrl = wsEndpoint.replace('ws://', 'http://').replace('wss://', 'https://')
639
+
640
+ const chromeRes = await fetch(chromeUrl, {
641
+ headers: { Upgrade: 'websocket' }
642
+ })
643
+
644
+ if (!chromeRes.webSocket) {
645
+ DEBUG && console.error('[BrowserBinding] Failed to connect to Chrome DevTools')
646
+ return new Response('Failed to connect to Chrome DevTools', { status: 502 })
647
+ }
648
+
649
+ const chromeWs = chromeRes.webSocket
650
+ chromeWs.accept()
651
+
652
+ log('Connected to Chrome DevTools')
653
+
654
+ // Create WebSocketPair for client connection
655
+ const { 0: client, 1: server } = new WebSocketPair()
656
+ server.accept()
657
+
658
+ // Chunk buffer for reassembling multi-chunk messages from puppeteer
659
+ let chunks = []
660
+ const MAX_BUFFER_SIZE = 50 * 1024 * 1024 // 50MB max buffer
661
+ let bufferSize = 0
662
+
663
+ // Proxy messages from client (puppeteer) to Chrome
664
+ // Handle multi-chunk framing protocol
665
+ server.addEventListener('message', (event) => {
666
+ // Keep-alive ping from puppeteer
667
+ if (event.data === 'ping') {
668
+ return
669
+ }
670
+
671
+ // Handle binary data (chunked protocol)
672
+ if (event.data instanceof ArrayBuffer) {
673
+ const chunk = new Uint8Array(event.data)
674
+ bufferSize += chunk.length
675
+
676
+ // Prevent unbounded buffering
677
+ if (bufferSize > MAX_BUFFER_SIZE) {
678
+ DEBUG && console.error('[BrowserBinding] Buffer overflow, closing connection')
679
+ server.close(1009, 'Message too big')
680
+ chromeWs.close(1009, 'Message too big')
681
+ return
682
+ }
683
+
684
+ chunks.push(chunk)
685
+
686
+ // Try to reassemble complete message
687
+ const message = chunksToMessage(chunks)
688
+ if (message !== null) {
689
+ // Send complete message to Chrome
690
+ if (chromeWs.readyState === 1) { // OPEN
691
+ chromeWs.send(message)
692
+ }
693
+ // Clear buffer
694
+ chunks = []
695
+ bufferSize = 0
696
+ }
697
+ } else if (typeof event.data === 'string') {
698
+ // Shouldn't happen in normal protocol, but handle it
699
+ if (chromeWs.readyState === 1) {
700
+ chromeWs.send(event.data)
701
+ }
702
+ }
703
+ })
704
+
705
+ // Proxy messages from Chrome to client (puppeteer)
706
+ // Split into chunks following the multi-chunk protocol
707
+ chromeWs.addEventListener('message', (event) => {
708
+ if (server.readyState !== 1) return // Not OPEN
709
+
710
+ // Split message into chunks
711
+ const outChunks = messageToChunks(event.data)
712
+ for (const chunk of outChunks) {
713
+ server.send(chunk)
714
+ }
715
+ })
716
+
717
+ // Handle close events with validated codes
718
+ server.addEventListener('close', (event) => {
719
+ log('Client WebSocket closed:', event.code)
720
+ const code = validateCloseCode(event.code)
721
+ try {
722
+ if (chromeWs.readyState === 1 || chromeWs.readyState === 0) {
723
+ chromeWs.close(code, event.reason || '')
724
+ }
725
+ } catch {}
726
+ })
727
+
728
+ chromeWs.addEventListener('close', (event) => {
729
+ log('Chrome WebSocket closed:', event.code)
730
+ const code = validateCloseCode(event.code)
731
+ try {
732
+ if (server.readyState === 1 || server.readyState === 0) {
733
+ server.close(code, event.reason || '')
734
+ }
735
+ } catch {}
736
+ })
737
+
738
+ // Handle errors
739
+ server.addEventListener('error', (event) => {
740
+ DEBUG && console.error('[BrowserBinding] Client WebSocket error')
741
+ try { chromeWs.close(1011, 'Client error') } catch {}
742
+ })
743
+
744
+ chromeWs.addEventListener('error', (event) => {
745
+ DEBUG && console.error('[BrowserBinding] Chrome WebSocket error')
746
+ try { server.close(1011, 'Chrome error') } catch {}
747
+ })
748
+
749
+ log('WebSocket proxy established')
750
+
751
+ // Return Cloudflare-style WebSocket response
752
+ return new Response(null, {
753
+ status: 101,
754
+ webSocket: client
755
+ })
756
+ }
757
+ `;
758
+ }
759
+
760
+ // src/cli/wrangler-auth.ts
761
+ import { exec } from "node:child_process";
762
+ import { promisify } from "node:util";
763
+ var execAsync = promisify(exec);
764
+ function detectRemoteBindings(config) {
765
+ const remoteBindings = [];
766
+ const bindings = config.bindings;
767
+ if (!bindings)
768
+ return remoteBindings;
769
+ if (bindings.ai) {
770
+ remoteBindings.push(`AI (binding: ${bindings.ai.binding})`);
771
+ }
772
+ if (bindings.vectorize) {
773
+ for (const [name] of Object.entries(bindings.vectorize)) {
774
+ remoteBindings.push(`Vectorize (binding: ${name})`);
775
+ }
776
+ }
777
+ return remoteBindings;
778
+ }
779
+ async function checkWranglerAuth() {
780
+ try {
781
+ const { stdout, stderr } = await execAsync("bunx wrangler whoami", {
782
+ timeout: 15000
783
+ });
784
+ const output = stdout + stderr;
785
+ if (output.includes("not authenticated") || output.includes("Not logged in") || output.includes("wrangler login")) {
786
+ return {
787
+ loggedIn: false,
788
+ error: "Not logged in to Wrangler"
789
+ };
790
+ }
791
+ const emailMatch = output.match(/email[:\s]+([^\s!]+)/i);
792
+ const accountMatch = output.match(/Account\s+ID[:\s]+([a-f0-9]+)/i);
793
+ return {
794
+ loggedIn: true,
795
+ email: emailMatch?.[1],
796
+ accountId: accountMatch?.[1]
797
+ };
798
+ } catch (error) {
799
+ const msg = error instanceof Error ? error.message : String(error);
800
+ if (msg.includes("ENOENT") || msg.includes("not found")) {
801
+ return {
802
+ loggedIn: false,
803
+ error: "Wrangler not installed. Run: npm install -g wrangler"
804
+ };
805
+ }
806
+ return {
807
+ loggedIn: false,
808
+ error: msg
809
+ };
810
+ }
811
+ }
812
+ async function checkRemoteBindingRequirements(config) {
813
+ const remoteBindings = detectRemoteBindings(config);
814
+ if (remoteBindings.length === 0) {
815
+ return {
816
+ hasRemoteBindings: false,
817
+ remoteBindings: [],
818
+ missingAccountId: false,
819
+ notLoggedIn: false
820
+ };
821
+ }
822
+ const missingAccountId = !config.accountId;
823
+ const authStatus = await checkWranglerAuth();
824
+ const notLoggedIn = !authStatus.loggedIn;
825
+ return {
826
+ hasRemoteBindings: true,
827
+ remoteBindings,
828
+ missingAccountId,
829
+ notLoggedIn
830
+ };
831
+ }
832
+
833
+ // src/dev-server/d1-migrations.ts
834
+ import { resolve } from "pathe";
835
+ var MIGRATION_RETRY_DELAYS_MS = [500, 1000, 1500, 2000];
836
+ function collectMigrationStatements(sql) {
837
+ const cleanedSql = sql.split(`
838
+ `).filter((line) => !line.trim().startsWith("--")).join(`
839
+ `);
840
+ return cleanedSql.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
841
+ }
842
+ function getErrorMessage(error) {
843
+ return error instanceof Error ? error.message : String(error);
844
+ }
845
+ async function waitForRetry(delayMs) {
846
+ await new Promise((resolvePromise) => setTimeout(resolvePromise, delayMs));
847
+ }
848
+ async function applyMigrationsToBinding(options) {
849
+ const { bindingName, statements, miniflarePort, logger } = options;
850
+ let lastError;
851
+ for (let attempt = 0;attempt <= MIGRATION_RETRY_DELAYS_MS.length; attempt++) {
852
+ if (attempt > 0) {
853
+ await waitForRetry(MIGRATION_RETRY_DELAYS_MS[attempt - 1]);
854
+ }
855
+ try {
856
+ const response = await fetch(`http://127.0.0.1:${miniflarePort}/_devflare/migrate`, {
857
+ method: "POST",
858
+ headers: { "Content-Type": "application/json" },
859
+ body: JSON.stringify({ bindingName, statements })
860
+ });
861
+ if (!response.ok) {
862
+ const text = await response.text();
863
+ throw new Error(`HTTP ${response.status}: ${text}`);
864
+ }
865
+ const result = await response.json();
866
+ if (result.success) {
867
+ logger?.success(`D1 migrations applied to ${bindingName}`);
868
+ return;
869
+ }
870
+ throw new Error(result.error || "Unknown error");
871
+ } catch (error) {
872
+ lastError = error;
873
+ }
874
+ }
875
+ logger?.warn(`Failed to apply migrations to ${bindingName}: ${getErrorMessage(lastError)}`);
876
+ }
877
+ async function runD1Migrations(options) {
878
+ const { cwd, config, miniflarePort, logger } = options;
879
+ if (!config?.bindings?.d1) {
880
+ return;
881
+ }
882
+ const { existsSync: existsSync2, readdirSync, readFileSync } = await import("node:fs");
883
+ const migrationsDir = resolve(cwd, "migrations");
884
+ if (!existsSync2(migrationsDir)) {
885
+ logger?.debug("No migrations/ directory found, skipping D1 migrations");
886
+ return;
887
+ }
888
+ const files = readdirSync(migrationsDir).filter((file) => file.endsWith(".sql")).sort();
889
+ if (files.length === 0) {
890
+ logger?.debug("No SQL migration files found");
891
+ return;
892
+ }
893
+ logger?.info(`Running ${files.length} D1 migration(s)...`);
894
+ const allStatements = [];
895
+ for (const file of files) {
896
+ const sql = readFileSync(resolve(migrationsDir, file), "utf-8");
897
+ const statements = collectMigrationStatements(sql);
898
+ allStatements.push(...statements);
899
+ logger?.debug(`File ${file}: ${statements.length} statement(s)`);
900
+ }
901
+ if (allStatements.length === 0) {
902
+ logger?.debug("No executable D1 migration statements found");
903
+ return;
904
+ }
905
+ for (const [bindingName] of Object.entries(config.bindings.d1)) {
906
+ await applyMigrationsToBinding({
907
+ bindingName,
908
+ statements: allStatements,
909
+ miniflarePort,
910
+ logger
911
+ });
912
+ }
913
+ }
914
+
915
+ // src/dev-server/gateway-script.ts
916
+ function getGatewayScript(wsRoutes = [], debug = false, appServiceBindingName = null) {
917
+ const wsRoutesJson = JSON.stringify(wsRoutes);
918
+ const appServiceBindingJson = JSON.stringify(appServiceBindingName);
919
+ return `
920
+ // Bridge Gateway Worker — RPC Handler
921
+ // Handles all binding operations via WebSocket RPC
922
+ // Also handles WebSocket proxying to Durable Objects
923
+
924
+ const DEBUG = ${debug}
925
+ const log = (...args) => DEBUG && console.log('[Gateway]', ...args)
926
+
927
+ const activeStreams = new Map()
928
+ const wsProxies = new Map()
929
+ const incomingStreams = new Map()
930
+
931
+ // WebSocket routes configuration (injected at build time)
932
+ const WS_ROUTES = ${wsRoutesJson}
933
+ const APP_SERVICE_BINDING = ${appServiceBindingJson}
934
+
935
+ export default {
936
+ async fetch(request, env, ctx) {
937
+ const url = new URL(request.url)
938
+ const isWebSocket = request.headers.get('Upgrade') === 'websocket'
939
+
940
+ // Check if this is a WebSocket request matching a DO route
941
+ if (isWebSocket) {
942
+ const matchedRoute = matchWsRoute(url.pathname)
943
+ if (matchedRoute) {
944
+ return handleDoWebSocket(request, env, url, matchedRoute)
945
+ }
946
+ // Otherwise handle as bridge RPC WebSocket
947
+ return handleBridgeWebSocket(request, env, ctx)
948
+ }
949
+
950
+ // HTTP endpoint for large file transfers
951
+ if (url.pathname.startsWith('/_devflare/transfer/')) {
952
+ return handleHttpTransfer(request, env, url)
953
+ }
954
+
955
+ // D1 migration endpoint
956
+ if (url.pathname === '/_devflare/migrate' && request.method === 'POST') {
957
+ return handleMigration(request, env)
958
+ }
959
+
960
+ // Email handler endpoint (simulates incoming email)
961
+ if (url.pathname === '/cdn-cgi/handler/email' && request.method === 'POST') {
962
+ return handleEmailIncoming(request, env, ctx, url)
963
+ }
964
+
965
+ // Health check
966
+ if (url.pathname === '/_devflare/health') {
967
+ return new Response(JSON.stringify({
968
+ ok: true,
969
+ bindings: Object.keys(env),
970
+ wsRoutes: WS_ROUTES
971
+ }), { headers: { 'Content-Type': 'application/json' } })
972
+ }
973
+
974
+ if (APP_SERVICE_BINDING) {
975
+ const appWorker = env[APP_SERVICE_BINDING]
976
+ if (appWorker && typeof appWorker.fetch === 'function') {
977
+ return appWorker.fetch(request)
978
+ }
979
+ }
980
+
981
+ return new Response('Devflare Bridge Gateway', { status: 200 })
982
+ }
983
+ }
984
+
985
+ // Handle D1 migrations
986
+ async function handleMigration(request, env) {
987
+ try {
988
+ const { bindingName, statements } = await request.json()
989
+ log('Migration request for binding:', bindingName, 'statements count:', statements?.length, 'bindings:', Object.keys(env))
990
+ const db = env[bindingName]
991
+ if (!db) {
992
+ return Response.json({ error: 'Binding not found: ' + bindingName }, { status: 404 })
993
+ }
994
+
995
+ const results = []
996
+ for (const sql of statements) {
997
+ try {
998
+ log('Running migration SQL:', sql.slice(0, 80))
999
+ await db.prepare(sql).run()
1000
+ results.push({ sql: sql.slice(0, 50), success: true })
1001
+ log('Migration SQL succeeded')
1002
+ } catch (error) {
1003
+ const msg = error?.message || String(error)
1004
+ log('Migration SQL error:', msg)
1005
+ if (msg.includes('already exists')) {
1006
+ results.push({ sql: sql.slice(0, 50), success: true, skipped: true })
1007
+ } else {
1008
+ results.push({ sql: sql.slice(0, 50), success: false, error: msg })
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ // Verify table exists after migration
1014
+ try {
1015
+ const tables = await db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all()
1016
+ log('Tables after migration:', JSON.stringify(tables))
1017
+ } catch (e) {
1018
+ log('Error listing tables:', e.message)
1019
+ }
1020
+
1021
+ return Response.json({ success: true, results })
1022
+ } catch (error) {
1023
+ return Response.json({ error: error?.message || String(error) }, { status: 500 })
1024
+ }
1025
+ }
1026
+
1027
+ // Handle incoming email (simulates email() handler)
1028
+ async function handleEmailIncoming(request, env, ctx, url) {
1029
+ try {
1030
+ const from = url.searchParams.get('from') || 'unknown@example.com'
1031
+ const to = url.searchParams.get('to') || 'worker@example.com'
1032
+ const rawBody = await request.text()
1033
+
1034
+ log('Email incoming:', { from, to, bodyLength: rawBody.length })
1035
+
1036
+ if (APP_SERVICE_BINDING) {
1037
+ const appWorker = env[APP_SERVICE_BINDING]
1038
+ if (appWorker && typeof appWorker.fetch === 'function') {
1039
+ const response = await appWorker.fetch(new Request('http://devflare.internal/_devflare/internal/email', {
1040
+ method: 'POST',
1041
+ headers: {
1042
+ 'x-devflare-event': 'email',
1043
+ 'x-devflare-email-from': from,
1044
+ 'x-devflare-email-to': to,
1045
+ 'content-type': request.headers.get('content-type') || 'text/plain'
1046
+ },
1047
+ body: rawBody
1048
+ }))
1049
+
1050
+ if (!response.ok) {
1051
+ return response
1052
+ }
1053
+ }
1054
+ }
1055
+
1056
+ return new Response(JSON.stringify({ ok: true, from, to }), {
1057
+ headers: { 'Content-Type': 'application/json' }
1058
+ })
1059
+ } catch (error) {
1060
+ console.error('[Gateway] Email handler error:', error)
1061
+ return Response.json({ error: error?.message || String(error) }, { status: 500 })
1062
+ }
1063
+ }
1064
+
1065
+ // Match URL path against configured WS routes
1066
+ function matchWsRoute(pathname) {
1067
+ for (const route of WS_ROUTES) {
1068
+ // Simple exact match for now (could add glob/regex later)
1069
+ if (pathname === route.pattern || pathname.startsWith(route.pattern + '?')) {
1070
+ return route
1071
+ }
1072
+ }
1073
+ return null
1074
+ }
1075
+
1076
+ // Handle WebSocket upgrade that should go to a Durable Object
1077
+ async function handleDoWebSocket(request, env, url, route) {
1078
+ try {
1079
+ // Get the DO namespace
1080
+ const namespace = env[route.doNamespace]
1081
+ if (!namespace) {
1082
+ console.error('[Gateway] DO namespace not found:', route.doNamespace)
1083
+ return new Response('DO namespace not found: ' + route.doNamespace, { status: 500 })
1084
+ }
1085
+
1086
+ // Get the instance ID from query params
1087
+ const idValue = url.searchParams.get(route.idParam) || 'default'
1088
+
1089
+ // Get or create DO instance
1090
+ const doId = namespace.idFromName(idValue)
1091
+ const stub = namespace.get(doId)
1092
+
1093
+ // Construct the forward URL for the DO
1094
+ const forwardUrl = new URL(route.forwardPath, url.origin)
1095
+ // Forward all query params
1096
+ url.searchParams.forEach((v, k) => forwardUrl.searchParams.set(k, v))
1097
+
1098
+ log('Forwarding WebSocket to DO:', route.doNamespace, 'id:', idValue, 'path:', forwardUrl.pathname)
1099
+
1100
+ // Forward the request to the DO
1101
+ return stub.fetch(forwardUrl.toString(), {
1102
+ method: request.method,
1103
+ headers: request.headers
1104
+ })
1105
+ } catch (error) {
1106
+ console.error('[Gateway] Error forwarding to DO:', error)
1107
+ return new Response('Error forwarding to DO: ' + error.message, { status: 500 })
1108
+ }
1109
+ }
1110
+
1111
+ // Handle bridge RPC WebSocket (for Node.js Vite server communication)
1112
+ function handleBridgeWebSocket(request, env, ctx) {
1113
+ const { 0: client, 1: server } = new WebSocketPair()
1114
+ server.accept()
1115
+
1116
+ server.addEventListener('message', async (event) => {
1117
+ try {
1118
+ if (typeof event.data === 'string') {
1119
+ await handleJsonMessage(event.data, server, env, ctx)
1120
+ }
1121
+ } catch (error) {
1122
+ console.error('[Gateway] Error:', error)
1123
+ }
1124
+ })
1125
+
1126
+ server.addEventListener('close', () => {
1127
+ activeStreams.clear()
1128
+ wsProxies.clear()
1129
+ })
1130
+
1131
+ return new Response(null, { status: 101, webSocket: client })
1132
+ }
1133
+
1134
+ async function handleJsonMessage(data, ws, env, ctx) {
1135
+ const msg = JSON.parse(data)
1136
+
1137
+ switch (msg.t) {
1138
+ case 'rpc.call':
1139
+ await handleRpcCall(msg, ws, env, ctx)
1140
+ break
1141
+ case 'ws.open':
1142
+ await handleWsOpen(msg, ws, env)
1143
+ break
1144
+ case 'ws.close':
1145
+ handleWsClose(msg)
1146
+ break
1147
+ }
1148
+ }
1149
+
1150
+ async function handleRpcCall(msg, ws, env, ctx) {
1151
+ try {
1152
+ const result = await executeRpcMethod(msg.method, msg.params, env, ctx)
1153
+ ws.send(JSON.stringify({ t: 'rpc.ok', id: msg.id, result }))
1154
+ } catch (error) {
1155
+ ws.send(JSON.stringify({
1156
+ t: 'rpc.err',
1157
+ id: msg.id,
1158
+ error: { code: error.code || 'INTERNAL_ERROR', message: error.message }
1159
+ }))
1160
+ }
1161
+ }
1162
+
1163
+ async function executeRpcMethod(method, params, env, ctx) {
1164
+ const parts = method.split('.')
1165
+ const bindingName = parts[0]
1166
+ const operation = parts.slice(1).join('.')
1167
+ const binding = env[bindingName]
1168
+ const RAW_EMAIL = 'EmailMessage::raw'
1169
+
1170
+ if (!binding) throw new Error('Binding not found: ' + bindingName)
1171
+
1172
+ // KV operations
1173
+ if (operation === 'get') return binding.get(params[0], params[1])
1174
+ if (operation === 'put') return binding.put(params[0], params[1], params[2])
1175
+ if (operation === 'delete') return binding.delete(params[0])
1176
+ if (operation === 'list') return binding.list(params[0])
1177
+ if (operation === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
1178
+
1179
+ // R2 operations
1180
+ if (operation === 'head') return serializeR2Object(await binding.head(params[0]))
1181
+ if (operation === 'r2.get') {
1182
+ const obj = await binding.get(params[0], params[1])
1183
+ if (!obj) return null
1184
+ const body = await obj.arrayBuffer()
1185
+ return serializeR2ObjectBody(obj, arrayBufferToBase64(body))
1186
+ }
1187
+ if (operation === 'r2.put') {
1188
+ // Deserialize the value if it's a serialized ArrayBuffer/Uint8Array
1189
+ let value = params[1]
1190
+ if (value && typeof value === 'object') {
1191
+ if (value.__type === 'ArrayBuffer') {
1192
+ value = base64ToArrayBuffer(value.data)
1193
+ } else if (value.__type === 'Uint8Array') {
1194
+ value = base64ToArrayBuffer(value.data)
1195
+ }
1196
+ }
1197
+ return serializeR2Object(await binding.put(params[0], value, params[2]))
1198
+ }
1199
+ if (operation === 'r2.delete') return binding.delete(params[0])
1200
+ if (operation === 'r2.list') return serializeR2Objects(await binding.list(params[0]))
1201
+
1202
+ // D1 operations
1203
+ if (operation === 'exec') return binding.exec(params[0])
1204
+ if (operation.startsWith('stmt.')) {
1205
+ log('D1 RPC:', bindingName, operation, 'sql:', String(params[0]).slice(0, 60))
1206
+ const mode = operation.split('.')[1]
1207
+ const [sql, ...rest] = params
1208
+
1209
+ // For first/raw, the last element is the column/options parameter (may be undefined)
1210
+ // For all/run, rest contains only bindings
1211
+ let bindings = rest
1212
+ let extraParam = undefined
1213
+
1214
+ if (mode === 'first' || mode === 'raw') {
1215
+ // Last element is the column/options (may be undefined)
1216
+ extraParam = rest[rest.length - 1]
1217
+ bindings = rest.slice(0, -1)
1218
+ }
1219
+
1220
+ let stmt = binding.prepare(sql)
1221
+ if (bindings.length > 0) stmt = stmt.bind(...bindings)
1222
+
1223
+ if (mode === 'first') {
1224
+ // Only pass column if it's a non-empty string
1225
+ if (typeof extraParam === 'string' && extraParam.length > 0) {
1226
+ return stmt.first(extraParam)
1227
+ }
1228
+ return stmt.first()
1229
+ }
1230
+ if (mode === 'all') return stmt.all()
1231
+ if (mode === 'run') return stmt.run()
1232
+ if (mode === 'raw') return stmt.raw(extraParam)
1233
+ }
1234
+
1235
+ // DO operations
1236
+ if (operation === 'idFromName') {
1237
+ const id = binding.idFromName(params[0])
1238
+ return { __type: 'DOId', hex: id.toString() }
1239
+ }
1240
+ if (operation === 'idFromString') {
1241
+ const id = binding.idFromString(params[0])
1242
+ return { __type: 'DOId', hex: id.toString() }
1243
+ }
1244
+ if (operation === 'newUniqueId') {
1245
+ const id = binding.newUniqueId(params[0])
1246
+ return { __type: 'DOId', hex: id.toString() }
1247
+ }
1248
+ if (operation === 'stub.fetch') {
1249
+ const [, serializedId, serializedReq] = params
1250
+ log('stub.fetch request:', {
1251
+ url: serializedReq.url,
1252
+ method: serializedReq.method,
1253
+ headers: serializedReq.headers,
1254
+ hasBody: !!serializedReq.body
1255
+ })
1256
+ const id = binding.idFromString(serializedId.hex)
1257
+ const stub = binding.get(id)
1258
+ try {
1259
+ const response = await stub.fetch(new Request(serializedReq.url, {
1260
+ method: serializedReq.method,
1261
+ headers: serializedReq.headers,
1262
+ body: serializedReq.body?.type === 'bytes' ? base64ToArrayBuffer(serializedReq.body.data) : undefined
1263
+ }))
1264
+ // Clone to read body for logging if there's an error
1265
+ const cloned = response.clone()
1266
+ const serialized = await serializeResponse(response)
1267
+ log('stub.fetch response:', {
1268
+ status: serialized.status,
1269
+ headers: serialized.headers,
1270
+ bodyLength: serialized.body?.data?.length || 0
1271
+ })
1272
+ // If 500, log the body content
1273
+ if (response.status >= 400) {
1274
+ const errBody = await cloned.text()
1275
+ log('Error response body:', errBody)
1276
+ }
1277
+ return serialized
1278
+ } catch (err) {
1279
+ console.error('[Gateway] stub.fetch error:', err)
1280
+ throw err
1281
+ }
1282
+ }
1283
+ if (operation === 'stub.rpc') {
1284
+ const [, serializedId, methodName, args] = params
1285
+ const id = binding.idFromString(serializedId.hex)
1286
+ const stub = binding.get(id)
1287
+ const response = await stub.fetch(new Request('http://do/_rpc', {
1288
+ method: 'POST',
1289
+ headers: { 'Content-Type': 'application/json' },
1290
+ body: JSON.stringify({ method: methodName, params: args })
1291
+ }))
1292
+ const result = await response.json()
1293
+ if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
1294
+ return result.result
1295
+ }
1296
+
1297
+ // Queue operations
1298
+ if (operation === 'send') return binding.send(params[0], params[1])
1299
+ if (operation === 'sendBatch') return binding.sendBatch(params[0], params[1])
1300
+
1301
+ // Email send operations (send_email binding)
1302
+ if (operation === 'email.send') {
1303
+ const message = params[0]
1304
+ log('Email send:', { from: message?.from, to: message?.to })
1305
+ if (binding && typeof binding.send === 'function') {
1306
+ if (message && typeof message === 'object' && 'from' in message && 'to' in message && 'raw' in message) {
1307
+ return binding.send({
1308
+ from: message.from,
1309
+ to: message.to,
1310
+ [RAW_EMAIL]: createEmailMessageRaw(message.raw)
1311
+ })
1312
+ }
1313
+ return binding.send(message)
1314
+ }
1315
+ // Return success even if no real binding (simulated)
1316
+ return { ok: true, simulated: true }
1317
+ }
1318
+
1319
+ throw new Error('Unknown operation: ' + method)
1320
+ }
1321
+
1322
+ function createEmailMessageRaw(raw) {
1323
+ if (typeof raw === 'string' || raw instanceof ReadableStream) {
1324
+ return raw
1325
+ }
1326
+ if (raw instanceof ArrayBuffer || raw instanceof Uint8Array) {
1327
+ return new Response(raw).body
1328
+ }
1329
+ throw new Error('Unsupported EmailMessage raw payload')
1330
+ }
1331
+
1332
+ async function handleWsOpen(msg, ws, env) {
1333
+ try {
1334
+ const binding = env[msg.target.binding]
1335
+ const id = binding.idFromString(msg.target.id)
1336
+ const stub = binding.get(id)
1337
+
1338
+ const headers = new Headers(msg.target.headers || [])
1339
+ headers.set('Upgrade', 'websocket')
1340
+
1341
+ const response = await stub.fetch(new Request(msg.target.url, { method: 'GET', headers }))
1342
+ const doWs = response.webSocket
1343
+
1344
+ if (!doWs) {
1345
+ ws.send(JSON.stringify({ t: 'rpc.err', id: 'ws_' + msg.wid, error: { code: 'WS_FAILED', message: 'No WebSocket returned' } }))
1346
+ return
1347
+ }
1348
+
1349
+ doWs.accept()
1350
+ wsProxies.set(msg.wid, { doWs })
1351
+
1352
+ doWs.addEventListener('message', (event) => {
1353
+ const isText = typeof event.data === 'string'
1354
+ const data = isText ? event.data : arrayBufferToBase64(event.data)
1355
+ ws.send(JSON.stringify({ t: 'ws.data', wid: msg.wid, data, isText }))
1356
+ })
1357
+
1358
+ doWs.addEventListener('close', (event) => {
1359
+ ws.send(JSON.stringify({ t: 'ws.close', wid: msg.wid, code: event.code, reason: event.reason }))
1360
+ wsProxies.delete(msg.wid)
1361
+ })
1362
+
1363
+ ws.send(JSON.stringify({ t: 'ws.opened', wid: msg.wid }))
1364
+ } catch (error) {
1365
+ ws.send(JSON.stringify({ t: 'rpc.err', id: 'ws_' + msg.wid, error: { code: 'WS_FAILED', message: error.message } }))
1366
+ }
1367
+ }
1368
+
1369
+ function handleWsClose(msg) {
1370
+ const proxy = wsProxies.get(msg.wid)
1371
+ if (proxy) {
1372
+ proxy.doWs.close(msg.code, msg.reason)
1373
+ wsProxies.delete(msg.wid)
1374
+ }
1375
+ }
1376
+
1377
+ async function handleHttpTransfer(request, env, url) {
1378
+ const transferIdEncoded = url.pathname.split('/').pop()
1379
+ const transferId = decodeURIComponent(transferIdEncoded || '')
1380
+ const [binding, ...keyParts] = transferId.split(':')
1381
+ const key = keyParts.join(':')
1382
+ const bucket = env[binding]
1383
+
1384
+ if (!bucket) return new Response('Bucket not found: ' + binding, { status: 404 })
1385
+
1386
+ if (request.method === 'PUT' || request.method === 'POST') {
1387
+ const result = await bucket.put(key, request.body)
1388
+ return new Response(JSON.stringify(result), { headers: { 'Content-Type': 'application/json' } })
1389
+ }
1390
+
1391
+ if (request.method === 'GET') {
1392
+ const object = await bucket.get(key)
1393
+ if (!object) return new Response('Not found', { status: 404 })
1394
+ return new Response(object.body, {
1395
+ headers: {
1396
+ 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
1397
+ 'Content-Length': String(object.size)
1398
+ }
1399
+ })
1400
+ }
1401
+
1402
+ return new Response('Method not allowed', { status: 405 })
1403
+ }
1404
+
1405
+ // Helpers
1406
+ function serializeR2Object(obj) {
1407
+ if (!obj) return null
1408
+ return {
1409
+ __type: 'R2Object',
1410
+ key: obj.key,
1411
+ version: obj.version,
1412
+ size: obj.size,
1413
+ etag: obj.etag,
1414
+ httpEtag: obj.httpEtag,
1415
+ checksums: obj.checksums,
1416
+ uploaded: obj.uploaded?.toISOString(),
1417
+ httpMetadata: obj.httpMetadata,
1418
+ customMetadata: obj.customMetadata,
1419
+ range: obj.range,
1420
+ storageClass: obj.storageClass
1421
+ }
1422
+ }
1423
+ function serializeR2ObjectBody(obj, bodyData) {
1424
+ if (!obj) return null
1425
+ return {
1426
+ __type: 'R2ObjectBody',
1427
+ key: obj.key,
1428
+ version: obj.version,
1429
+ size: obj.size,
1430
+ etag: obj.etag,
1431
+ httpEtag: obj.httpEtag,
1432
+ checksums: obj.checksums,
1433
+ uploaded: obj.uploaded?.toISOString(),
1434
+ httpMetadata: obj.httpMetadata,
1435
+ customMetadata: obj.customMetadata,
1436
+ range: obj.range,
1437
+ storageClass: obj.storageClass,
1438
+ bodyData
1439
+ }
1440
+ }
1441
+ function serializeR2Objects(result) {
1442
+ if (!result) return null
1443
+ return { objects: result.objects.map(serializeR2Object), truncated: result.truncated, cursor: result.cursor }
1444
+ }
1445
+ async function serializeResponse(response) {
1446
+ // Read body as bytes and encode as base64
1447
+ let body = null
1448
+ if (response.body) {
1449
+ const bytes = await response.arrayBuffer()
1450
+ if (bytes.byteLength > 0) {
1451
+ body = { type: 'bytes', data: arrayBufferToBase64(bytes) }
1452
+ }
1453
+ }
1454
+ return { status: response.status, statusText: response.statusText, headers: [...response.headers.entries()], body }
1455
+ }
1456
+ function arrayBufferToBase64(buffer) {
1457
+ const bytes = new Uint8Array(buffer)
1458
+ let binary = ''
1459
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i])
1460
+ return btoa(binary)
1461
+ }
1462
+ function base64ToArrayBuffer(base64) {
1463
+ const binary = atob(base64)
1464
+ const bytes = new Uint8Array(binary.length)
1465
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
1466
+ return bytes.buffer
1467
+ }
1468
+ `;
1469
+ }
1470
+
1471
+ // src/dev-server/miniflare-log.ts
1472
+ var ANSI_ESCAPE_REGEX = /\u001B\[[0-9;]*m/g;
1473
+ var COMPATIBILITY_DATE_FALLBACK_REGEX = /^The latest compatibility date supported by the installed Cloudflare Workers Runtime is "([^"]+)", but you've requested "([^"]+)"\. Falling back to "([^"]+)"\.\.\.$/;
1474
+ function normalizeMiniflareMessage(message) {
1475
+ return message.replace(ANSI_ESCAPE_REGEX, "").replace(/\s+/g, " ").trim();
1476
+ }
1477
+ function formatCompatibilityDateFallbackNotice(message) {
1478
+ const normalizedMessage = normalizeMiniflareMessage(message);
1479
+ const match = COMPATIBILITY_DATE_FALLBACK_REGEX.exec(normalizedMessage);
1480
+ if (!match) {
1481
+ return null;
1482
+ }
1483
+ const [, _supportedDate, requestedDate, fallbackDate] = match;
1484
+ return `Using latest supported Cloudflare Workers Runtime compatibility date ${fallbackDate} (requested ${requestedDate})`;
1485
+ }
1486
+ function createCompatibilityAwareMiniflareLog(BaseLog, level, logger) {
1487
+ const log = new BaseLog(level);
1488
+ const originalWarn = log.warn.bind(log);
1489
+ const originalInfo = log.info.bind(log);
1490
+ log.warn = (message) => {
1491
+ const notice = formatCompatibilityDateFallbackNotice(message);
1492
+ if (!notice) {
1493
+ originalWarn(message);
1494
+ return;
1495
+ }
1496
+ if (logger) {
1497
+ logger.info(notice);
1498
+ return;
1499
+ }
1500
+ originalInfo(notice);
1501
+ };
1502
+ return log;
1503
+ }
1504
+
1505
+ // src/dev-server/runtime-stdio.ts
1506
+ import { createInterface } from "node:readline";
1507
+ function writeStdout(logger, message) {
1508
+ if (typeof logger?.log === "function") {
1509
+ logger.log(message);
1510
+ return;
1511
+ }
1512
+ if (typeof logger?.info === "function") {
1513
+ logger.info(message);
1514
+ return;
1515
+ }
1516
+ console.log(message);
1517
+ }
1518
+ function writeStderr(logger, message) {
1519
+ if (typeof logger?.error === "function") {
1520
+ logger.error(message);
1521
+ return;
1522
+ }
1523
+ console.error(message);
1524
+ }
1525
+ function createRuntimeStdioForwarder(logger) {
1526
+ return (stdout, stderr) => {
1527
+ createInterface({ input: stdout }).on("line", (data) => {
1528
+ writeStdout(logger, data);
1529
+ });
1530
+ createInterface({ input: stderr }).on("line", (data) => {
1531
+ writeStderr(logger, data);
1532
+ });
1533
+ };
1534
+ }
1535
+
1536
+ // src/dev-server/vite-process.ts
1537
+ import { spawn } from "node:child_process";
1538
+ async function startViteProcess(options) {
1539
+ const {
1540
+ cwd,
1541
+ vitePort,
1542
+ miniflarePort,
1543
+ generatedViteConfigPath,
1544
+ logger
1545
+ } = options;
1546
+ const args = ["vite", "dev", "--port", String(vitePort)];
1547
+ if (generatedViteConfigPath) {
1548
+ args.push("--config", generatedViteConfigPath);
1549
+ }
1550
+ const viteProcess = spawn("bunx", args, {
1551
+ cwd,
1552
+ stdio: ["inherit", "pipe", "pipe"],
1553
+ windowsHide: true,
1554
+ env: {
1555
+ ...process.env,
1556
+ DEVFLARE_DEV: "true",
1557
+ DEVFLARE_BRIDGE_PORT: String(miniflarePort),
1558
+ FORCE_COLOR: "1"
1559
+ }
1560
+ });
1561
+ const readyUrl = await waitForViteReady(viteProcess, {
1562
+ onStdout(chunk) {
1563
+ process.stdout.write(chunk);
1564
+ },
1565
+ onStderr(chunk) {
1566
+ process.stderr.write(chunk);
1567
+ }
1568
+ });
1569
+ if (readyUrl) {
1570
+ logger?.success(`Vite dev server started on ${readyUrl}`);
1571
+ return viteProcess;
1572
+ }
1573
+ logger?.warn("Vite process started, but the final local URL could not be confirmed yet");
1574
+ return viteProcess;
1575
+ }
1576
+
1577
+ // src/dev-server/worker-surface-paths.ts
1578
+ import { dirname, resolve as resolve2 } from "pathe";
1579
+ var DEFAULT_TRANSPORT_ENTRY_FILES = [
1580
+ "src/transport.ts",
1581
+ "src/transport.js",
1582
+ "src/transport.mts",
1583
+ "src/transport.mjs"
1584
+ ];
1585
+ var resolveMainWorkerSurfacePaths = resolveWorkerSurfacePaths;
1586
+ function addWorkerWatchRoots(roots, cwd, configuredPath, defaultEntries) {
1587
+ if (configuredPath === false || configuredPath === null) {
1588
+ return;
1589
+ }
1590
+ if (typeof configuredPath === "string" && configuredPath) {
1591
+ roots.add(dirname(resolve2(cwd, configuredPath)));
1592
+ return;
1593
+ }
1594
+ for (const defaultEntry of defaultEntries) {
1595
+ roots.add(dirname(resolve2(cwd, defaultEntry)));
1596
+ }
1597
+ }
1598
+ function collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths) {
1599
+ const roots = new Set;
1600
+ for (const surfacePath of Object.values(mainWorkerSurfacePaths)) {
1601
+ if (surfacePath) {
1602
+ roots.add(dirname(surfacePath));
1603
+ }
1604
+ }
1605
+ addWorkerWatchRoots(roots, cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES);
1606
+ addWorkerWatchRoots(roots, cwd, config.files?.queue, DEFAULT_QUEUE_ENTRY_FILES);
1607
+ addWorkerWatchRoots(roots, cwd, config.files?.scheduled, DEFAULT_SCHEDULED_ENTRY_FILES);
1608
+ addWorkerWatchRoots(roots, cwd, config.files?.email, DEFAULT_EMAIL_ENTRY_FILES);
1609
+ addWorkerWatchRoots(roots, cwd, config.files?.transport, DEFAULT_TRANSPORT_ENTRY_FILES);
1610
+ const routeDirectory = getRouteDirectoryCandidate(cwd, config);
1611
+ if (routeDirectory) {
1612
+ roots.add(routeDirectory.absoluteDir);
1613
+ }
1614
+ return [...roots];
1615
+ }
1616
+
1617
+ // src/dev-server/worker-source-watcher.ts
1618
+ async function startWorkerSourceWatcher(options) {
1619
+ const { watchTargets, resolvedWorkerConfigPath, logger, onConfigChange, onWorkerChange } = options;
1620
+ if (watchTargets.length === 0) {
1621
+ return null;
1622
+ }
1623
+ const chokidar = await import("chokidar");
1624
+ const isWindows = process.platform === "win32";
1625
+ const ignoredSegments = ["/node_modules/", "/.git/", "/.devflare/", "/dist/"];
1626
+ const normalizePath = (filePath) => filePath.replace(/\\/g, "/");
1627
+ const isIgnoredPath = (filePath) => {
1628
+ const normalizedPath = normalizePath(filePath);
1629
+ return ignoredSegments.some((segment) => normalizedPath.includes(segment));
1630
+ };
1631
+ let reloadTimeout = null;
1632
+ let reloadInProgress = false;
1633
+ let pendingReloadPath = null;
1634
+ const flushPendingReload = async () => {
1635
+ if (!pendingReloadPath) {
1636
+ return;
1637
+ }
1638
+ const nextPath = pendingReloadPath;
1639
+ pendingReloadPath = null;
1640
+ await triggerReload(nextPath);
1641
+ };
1642
+ const triggerReload = async (filePath) => {
1643
+ if (reloadInProgress) {
1644
+ pendingReloadPath = filePath;
1645
+ return;
1646
+ }
1647
+ reloadInProgress = true;
1648
+ try {
1649
+ const normalizedConfigPath = resolvedWorkerConfigPath ? normalizePath(resolvedWorkerConfigPath) : null;
1650
+ if (normalizedConfigPath && normalizePath(filePath) === normalizedConfigPath) {
1651
+ logger?.info(`Devflare config changed: ${filePath}`);
1652
+ await onConfigChange();
1653
+ return;
1654
+ }
1655
+ logger?.info(`Worker source changed: ${filePath}`);
1656
+ await onWorkerChange();
1657
+ } catch (error) {
1658
+ logger?.error("Worker source reload failed:", error);
1659
+ } finally {
1660
+ reloadInProgress = false;
1661
+ await flushPendingReload();
1662
+ }
1663
+ };
1664
+ const scheduleReload = (filePath) => {
1665
+ if (reloadTimeout) {
1666
+ clearTimeout(reloadTimeout);
1667
+ }
1668
+ reloadTimeout = setTimeout(() => {
1669
+ reloadTimeout = null;
1670
+ triggerReload(filePath);
1671
+ }, 150);
1672
+ };
1673
+ const watcher = chokidar.watch(watchTargets, {
1674
+ ignoreInitial: true,
1675
+ usePolling: isWindows,
1676
+ interval: isWindows ? 300 : undefined,
1677
+ awaitWriteFinish: {
1678
+ stabilityThreshold: 100,
1679
+ pollInterval: 50
1680
+ },
1681
+ ignored: (filePath) => isIgnoredPath(filePath)
1682
+ });
1683
+ const onFileEvent = (filePath) => {
1684
+ if (isIgnoredPath(filePath)) {
1685
+ return;
1686
+ }
1687
+ scheduleReload(filePath);
1688
+ };
1689
+ watcher.on("change", onFileEvent);
1690
+ watcher.on("add", onFileEvent);
1691
+ watcher.on("unlink", onFileEvent);
1692
+ watcher.on("error", (error) => {
1693
+ logger?.error("Worker source watcher error:", error);
1694
+ });
1695
+ await new Promise((resolvePromise, rejectPromise) => {
1696
+ const handleReady = () => {
1697
+ watcher.off("error", handleInitialError);
1698
+ logger?.info(`Worker source watcher ready (${watchTargets.length} target(s))`);
1699
+ resolvePromise();
1700
+ };
1701
+ const handleInitialError = (error) => {
1702
+ watcher.off("ready", handleReady);
1703
+ rejectPromise(error instanceof Error ? error : new Error(String(error)));
1704
+ };
1705
+ watcher.once("ready", handleReady);
1706
+ watcher.once("error", handleInitialError);
1707
+ });
1708
+ return watcher;
1709
+ }
1710
+
1711
+ // src/dev-server/server.ts
1712
+ var INTERNAL_APP_SERVICE_BINDING = "__DEVFLARE_APP";
1713
+ function formatErrorMessage(error) {
1714
+ return error instanceof Error ? error.message : String(error);
1715
+ }
1716
+ function createDevServer(options) {
1717
+ const {
1718
+ cwd,
1719
+ configPath,
1720
+ vitePort = 5173,
1721
+ miniflarePort = 8787,
1722
+ enableVite = true,
1723
+ persist = true,
1724
+ logger,
1725
+ verbose = false,
1726
+ debug = process.env.DEVFLARE_DEBUG === "true"
1727
+ } = options;
1728
+ let miniflare = null;
1729
+ let doBundler = null;
1730
+ let workerSourceWatcher = null;
1731
+ let workerWatchTargets = [];
1732
+ let viteProcess = null;
1733
+ let config = null;
1734
+ let browserShim = null;
1735
+ let browserShimPort = 8788;
1736
+ let mainWorkerSurfacePaths = {
1737
+ fetch: null,
1738
+ queue: null,
1739
+ scheduled: null,
1740
+ email: null
1741
+ };
1742
+ let resolvedWorkerConfigPath = null;
1743
+ let mainWorkerScriptPath = null;
1744
+ let bundledMainWorkerScriptPath = null;
1745
+ let currentDoResult = null;
1746
+ let mainWorkerRoutes = null;
1747
+ let generatedViteConfigPath = null;
1748
+ let reloadChain = Promise.resolve();
1749
+ async function bundleMainWorker() {
1750
+ if (!mainWorkerScriptPath || !config) {
1751
+ bundledMainWorkerScriptPath = null;
1752
+ return;
1753
+ }
1754
+ bundledMainWorkerScriptPath = await bundleWorkerEntry({
1755
+ cwd,
1756
+ inputFile: mainWorkerScriptPath,
1757
+ outFile: resolve3(cwd, ".devflare", "worker-entrypoints", "main.js"),
1758
+ rolldownOptions: config.rolldown?.options,
1759
+ sourcemap: config.rolldown?.sourcemap,
1760
+ minify: config.rolldown?.minify,
1761
+ logger
1762
+ });
1763
+ logger?.debug(`Bundled main worker → ${bundledMainWorkerScriptPath}`);
1764
+ }
1765
+ function buildMiniflareConfig(doResult) {
1766
+ if (!config)
1767
+ throw new Error("Config not loaded");
1768
+ const loadedConfig = config;
1769
+ const bindings = loadedConfig.bindings ?? {};
1770
+ const persistPath = resolve3(cwd, ".devflare/data");
1771
+ const appWorkerName = loadedConfig.name;
1772
+ const shouldRunMainWorker = !enableVite && (hasWorkerSurfacePaths(mainWorkerSurfacePaths) || Boolean(mainWorkerRoutes?.routes.length));
1773
+ const queueProducers = (() => {
1774
+ if (!bindings.queues?.producers) {
1775
+ return;
1776
+ }
1777
+ const producers = {};
1778
+ for (const [bindingName, queueName] of Object.entries(bindings.queues.producers)) {
1779
+ producers[bindingName] = { queueName };
1780
+ }
1781
+ return producers;
1782
+ })();
1783
+ const queueConsumers = (() => {
1784
+ if (!bindings.queues?.consumers || bindings.queues.consumers.length === 0) {
1785
+ return;
1786
+ }
1787
+ const consumers = {};
1788
+ for (const consumer of bindings.queues.consumers) {
1789
+ consumers[consumer.queue] = {
1790
+ ...consumer.maxBatchSize !== undefined && { maxBatchSize: consumer.maxBatchSize },
1791
+ ...consumer.maxBatchTimeout !== undefined && { maxBatchTimeout: consumer.maxBatchTimeout },
1792
+ ...consumer.maxRetries !== undefined && { maxRetries: consumer.maxRetries },
1793
+ ...consumer.deadLetterQueue && { deadLetterQueue: consumer.deadLetterQueue },
1794
+ ...consumer.maxConcurrency !== undefined && { maxConcurrency: consumer.maxConcurrency },
1795
+ ...consumer.retryDelay !== undefined && { retryDelay: consumer.retryDelay }
1796
+ };
1797
+ }
1798
+ return consumers;
1799
+ })();
1800
+ const sharedOptions = {
1801
+ port: miniflarePort,
1802
+ host: "127.0.0.1",
1803
+ kvPersist: persist ? `${persistPath}/kv` : undefined,
1804
+ r2Persist: persist ? `${persistPath}/r2` : undefined,
1805
+ d1Persist: persist ? `${persistPath}/d1` : undefined,
1806
+ durableObjectsPersist: persist ? `${persistPath}/do` : undefined
1807
+ };
1808
+ const createServiceBindings = (extraBindings = {}) => {
1809
+ const serviceBindings = {};
1810
+ if (bindings.services) {
1811
+ for (const [bindingName, serviceConfig] of Object.entries(bindings.services)) {
1812
+ serviceBindings[bindingName] = {
1813
+ name: serviceConfig.service,
1814
+ ...serviceConfig.entrypoint && { entrypoint: serviceConfig.entrypoint }
1815
+ };
1816
+ }
1817
+ }
1818
+ for (const [bindingName, target] of Object.entries(extraBindings)) {
1819
+ serviceBindings[bindingName] = target;
1820
+ }
1821
+ return Object.keys(serviceBindings).length > 0 ? serviceBindings : undefined;
1822
+ };
1823
+ const sendEmailConfig = bindings.sendEmail ? {
1824
+ send_email: Object.entries(bindings.sendEmail).map(([name, binding]) => ({
1825
+ name,
1826
+ ...binding.destinationAddress && {
1827
+ destination_address: binding.destinationAddress
1828
+ },
1829
+ ...binding.allowedDestinationAddresses && {
1830
+ allowed_destination_addresses: binding.allowedDestinationAddresses
1831
+ },
1832
+ ...binding.allowedSenderAddresses && {
1833
+ allowed_sender_addresses: binding.allowedSenderAddresses
1834
+ }
1835
+ }))
1836
+ } : undefined;
1837
+ const createWorkerConfig = (options2) => {
1838
+ const baseFlags = loadedConfig.compatibilityFlags ?? [];
1839
+ const compatFlags = baseFlags.includes("nodejs_compat") ? baseFlags : [...baseFlags, "nodejs_compat"];
1840
+ const workerBindings = loadedConfig.vars ?? {};
1841
+ const workerConfig = {
1842
+ name: options2.name,
1843
+ modules: true,
1844
+ compatibilityDate: loadedConfig.compatibilityDate,
1845
+ compatibilityFlags: compatFlags,
1846
+ ...bindings.kv && {
1847
+ kvNamespaces: Object.fromEntries(Object.entries(bindings.kv).map(([bindingName, bindingConfig]) => {
1848
+ return [bindingName, getLocalKVNamespaceIdentifier(bindingConfig)];
1849
+ }))
1850
+ },
1851
+ ...bindings.r2 && { r2Buckets: bindings.r2 },
1852
+ ...bindings.d1 && {
1853
+ d1Databases: Object.fromEntries(Object.entries(bindings.d1).map(([bindingName, bindingConfig]) => {
1854
+ return [bindingName, getLocalD1DatabaseIdentifier(bindingConfig)];
1855
+ }))
1856
+ },
1857
+ ...Object.keys(workerBindings).length > 0 && { bindings: workerBindings },
1858
+ ...sendEmailConfig && { email: sendEmailConfig },
1859
+ ...queueProducers && { queueProducers },
1860
+ ...options2.queueConsumers && { queueConsumers: options2.queueConsumers },
1861
+ ...options2.triggers && { triggers: options2.triggers }
1862
+ };
1863
+ if (options2.scriptPath) {
1864
+ workerConfig.scriptPath = options2.scriptPath;
1865
+ workerConfig.modulesRoot = cwd;
1866
+ workerConfig.modulesRules = [
1867
+ { type: "ESModule", include: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.mjs"] },
1868
+ { type: "CommonJS", include: ["**/*.js", "**/*.cjs"] },
1869
+ { type: "ESModule", include: ["**/*.jsx"] }
1870
+ ];
1871
+ }
1872
+ if (options2.script) {
1873
+ workerConfig.script = options2.script;
1874
+ }
1875
+ if (options2.durableObjects && Object.keys(options2.durableObjects).length > 0) {
1876
+ workerConfig.durableObjects = options2.durableObjects;
1877
+ }
1878
+ if (options2.serviceBindings && Object.keys(options2.serviceBindings).length > 0) {
1879
+ workerConfig.serviceBindings = options2.serviceBindings;
1880
+ }
1881
+ return workerConfig;
1882
+ };
1883
+ const gatewayWorker = createWorkerConfig({
1884
+ name: "gateway",
1885
+ script: getGatewayScript(loadedConfig.wsRoutes, debug, shouldRunMainWorker ? INTERNAL_APP_SERVICE_BINDING : null),
1886
+ serviceBindings: shouldRunMainWorker ? createServiceBindings({
1887
+ [INTERNAL_APP_SERVICE_BINDING]: { name: appWorkerName }
1888
+ }) : createServiceBindings()
1889
+ });
1890
+ gatewayWorker.routes = ["*"];
1891
+ const hasDurableObjectBundles = !!doResult && doResult.bundles.size > 0;
1892
+ const browserBindingName = getSingleBrowserBindingName(bindings.browser);
1893
+ const needsBrowserWorker = Boolean(browserBindingName && (hasDurableObjectBundles || shouldRunMainWorker));
1894
+ if (!shouldRunMainWorker && !hasDurableObjectBundles && !needsBrowserWorker) {
1895
+ return {
1896
+ ...sharedOptions,
1897
+ ...gatewayWorker
1898
+ };
1899
+ }
1900
+ const workers = [];
1901
+ const durableObjects = {};
1902
+ const browserShimUrl = `http://127.0.0.1:${browserShimPort}`;
1903
+ const browserWorkerName = "browser-binding";
1904
+ if (shouldRunMainWorker && mainWorkerScriptPath) {
1905
+ const mainWorkerServiceBindings = createServiceBindings(browserBindingName ? {
1906
+ [browserBindingName]: { name: browserWorkerName }
1907
+ } : {});
1908
+ const mainWorkerConfig = createWorkerConfig({
1909
+ name: appWorkerName,
1910
+ scriptPath: bundledMainWorkerScriptPath ?? mainWorkerScriptPath,
1911
+ serviceBindings: mainWorkerServiceBindings,
1912
+ queueConsumers,
1913
+ triggers: loadedConfig.triggers?.crons?.length ? { crons: loadedConfig.triggers.crons } : undefined
1914
+ });
1915
+ workers.push(mainWorkerConfig);
1916
+ }
1917
+ if (doResult) {
1918
+ for (const [bindingName, bundlePath] of doResult.bundles) {
1919
+ const className = doResult.classes.get(bindingName);
1920
+ if (!className)
1921
+ continue;
1922
+ const workerName = `do-${bindingName.toLowerCase()}`;
1923
+ const workerConfig = createWorkerConfig({
1924
+ name: workerName,
1925
+ scriptPath: bundlePath,
1926
+ durableObjects: {
1927
+ [bindingName]: className
1928
+ },
1929
+ serviceBindings: createServiceBindings(browserBindingName ? {
1930
+ [browserBindingName]: { name: browserWorkerName }
1931
+ } : {})
1932
+ });
1933
+ if (browserBindingName) {
1934
+ logger?.debug(`DO ${workerName} has browser service binding: ${browserBindingName} → ${browserWorkerName}`);
1935
+ }
1936
+ logger?.debug(`DO ${workerName} config:`, JSON.stringify(workerConfig, null, 2));
1937
+ workers.push(workerConfig);
1938
+ durableObjects[bindingName] = {
1939
+ className,
1940
+ scriptName: workerName
1941
+ };
1942
+ }
1943
+ }
1944
+ if (needsBrowserWorker) {
1945
+ const browserWorker = createWorkerConfig({
1946
+ name: browserWorkerName,
1947
+ script: getBrowserBindingScript(browserShimUrl, debug)
1948
+ });
1949
+ workers.push(browserWorker);
1950
+ logger?.info(`Browser binding worker configured: ${browserBindingName} → ${browserShimUrl}`);
1951
+ }
1952
+ if (Object.keys(durableObjects).length > 0) {
1953
+ gatewayWorker.durableObjects = durableObjects;
1954
+ if (shouldRunMainWorker) {
1955
+ const mainWorker = workers.find((worker) => worker.name === appWorkerName);
1956
+ if (mainWorker) {
1957
+ mainWorker.durableObjects = durableObjects;
1958
+ }
1959
+ }
1960
+ }
1961
+ return {
1962
+ ...sharedOptions,
1963
+ workers: [gatewayWorker, ...workers]
1964
+ };
1965
+ }
1966
+ async function startMiniflare(doResult) {
1967
+ const { Miniflare, Log, LogLevel } = await import("miniflare");
1968
+ const mfConfig = buildMiniflareConfig(doResult);
1969
+ mfConfig.log = createCompatibilityAwareMiniflareLog(Log, LogLevel.DEBUG, logger);
1970
+ mfConfig.handleRuntimeStdio = createRuntimeStdioForwarder(logger);
1971
+ const shouldLogMiniflareDiagnostics = verbose || debug;
1972
+ if (shouldLogMiniflareDiagnostics) {
1973
+ logger?.info("=== MINIFLARE CONFIG DEBUG ===");
1974
+ logger?.info("Full config:", JSON.stringify(mfConfig, (key, value) => {
1975
+ if (key === "script" && typeof value === "string" && value.length > 200) {
1976
+ return value.substring(0, 200) + "...[truncated]";
1977
+ }
1978
+ return value;
1979
+ }, 2));
1980
+ if (mfConfig.workers) {
1981
+ logger?.info("Workers order:");
1982
+ for (const w of mfConfig.workers) {
1983
+ logger?.info(` → ${w.name}:`);
1984
+ logger?.info(` script: ${w.script ? "inline" : w.scriptPath}`);
1985
+ logger?.info(` browserRendering: ${JSON.stringify(w.browserRendering)}`);
1986
+ logger?.info(` durableObjects: ${JSON.stringify(w.durableObjects)}`);
1987
+ }
1988
+ }
1989
+ }
1990
+ miniflare = new Miniflare(mfConfig);
1991
+ await miniflare.ready;
1992
+ logger?.success(`Miniflare ready on http://localhost:${miniflarePort}`);
1993
+ if (shouldLogMiniflareDiagnostics) {
1994
+ try {
1995
+ const gatewayBindings = await miniflare.getBindings("gateway");
1996
+ logger?.info("Gateway worker bindings:", Object.keys(gatewayBindings));
1997
+ if (mfConfig.workers) {
1998
+ for (const w of mfConfig.workers) {
1999
+ if (w.name !== "gateway") {
2000
+ try {
2001
+ const doBindings = await miniflare.getBindings(w.name);
2002
+ logger?.info(`${w.name} worker bindings:`, Object.keys(doBindings));
2003
+ if ("BROWSER" in doBindings) {
2004
+ logger?.success(`${w.name} has BROWSER binding!`);
2005
+ } else {
2006
+ logger?.warn(`${w.name} is MISSING BROWSER binding`);
2007
+ }
2008
+ } catch (error) {
2009
+ logger?.debug(`Skipping binding diagnostics for ${w.name}: ${formatErrorMessage(error)}`);
2010
+ }
2011
+ }
2012
+ }
2013
+ }
2014
+ } catch (error) {
2015
+ logger?.debug(`Skipping Miniflare binding diagnostics: ${formatErrorMessage(error)}`);
2016
+ }
2017
+ }
2018
+ }
2019
+ async function reloadMiniflare(doResult) {
2020
+ currentDoResult = doResult;
2021
+ const queuedReload = reloadChain.then(async () => {
2022
+ if (!miniflare)
2023
+ return;
2024
+ const { Log, LogLevel } = await import("miniflare");
2025
+ const mfConfig = buildMiniflareConfig(currentDoResult);
2026
+ mfConfig.log = createCompatibilityAwareMiniflareLog(Log, LogLevel.DEBUG, logger);
2027
+ mfConfig.handleRuntimeStdio = createRuntimeStdioForwarder(logger);
2028
+ logger?.info("Reloading Miniflare...");
2029
+ await miniflare.setOptions(mfConfig);
2030
+ logger?.success("Miniflare reloaded");
2031
+ });
2032
+ reloadChain = queuedReload.catch(() => {});
2033
+ await queuedReload;
2034
+ }
2035
+ async function resolveWorkerConfigWatchPath() {
2036
+ if (configPath) {
2037
+ const explicitPath = resolve3(cwd, configPath);
2038
+ const fs = await import("node:fs/promises");
2039
+ try {
2040
+ await fs.access(explicitPath);
2041
+ return explicitPath;
2042
+ } catch {}
2043
+ }
2044
+ return await resolveConfigPath(cwd) ?? null;
2045
+ }
2046
+ async function refreshWorkerOnlySurfaceState() {
2047
+ if (!config) {
2048
+ return;
2049
+ }
2050
+ mainWorkerSurfacePaths = await resolveMainWorkerSurfacePaths(cwd, config);
2051
+ mainWorkerRoutes = await discoverRoutes(cwd, config);
2052
+ const composedMainEntry = await prepareComposedWorkerEntrypoint(cwd, config, undefined, {
2053
+ devInternalEmail: true
2054
+ });
2055
+ mainWorkerScriptPath = composedMainEntry ? resolve3(cwd, composedMainEntry) : null;
2056
+ if (mainWorkerScriptPath) {
2057
+ await bundleMainWorker();
2058
+ } else {
2059
+ bundledMainWorkerScriptPath = null;
2060
+ }
2061
+ await syncWorkerWatchTargets();
2062
+ }
2063
+ function getWorkerWatchTargets() {
2064
+ if (enableVite || !config) {
2065
+ return [];
2066
+ }
2067
+ const targets = collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths);
2068
+ if (resolvedWorkerConfigPath) {
2069
+ targets.push(resolvedWorkerConfigPath);
2070
+ }
2071
+ return [...new Set(targets)];
2072
+ }
2073
+ async function syncWorkerWatchTargets() {
2074
+ if (!workerSourceWatcher) {
2075
+ return;
2076
+ }
2077
+ const nextWatchTargets = getWorkerWatchTargets();
2078
+ const nextWatchTargetSet = new Set(nextWatchTargets);
2079
+ const targetsToRemove = workerWatchTargets.filter((target) => !nextWatchTargetSet.has(target));
2080
+ const targetsToAdd = nextWatchTargets.filter((target) => !workerWatchTargets.includes(target));
2081
+ if (targetsToRemove.length > 0) {
2082
+ await workerSourceWatcher.unwatch(targetsToRemove);
2083
+ }
2084
+ if (targetsToAdd.length > 0) {
2085
+ workerSourceWatcher.add(targetsToAdd);
2086
+ }
2087
+ workerWatchTargets = nextWatchTargets;
2088
+ }
2089
+ async function reloadWorkerOnlyConfig() {
2090
+ config = await loadConfig({ cwd, configFile: configPath });
2091
+ setLocalSendEmailBindings(config.bindings?.sendEmail ?? {});
2092
+ resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
2093
+ await refreshWorkerOnlySurfaceState();
2094
+ await reloadMiniflare(currentDoResult);
2095
+ }
2096
+ async function startWorkerSourceWatcher2() {
2097
+ if (enableVite || !config) {
2098
+ return;
2099
+ }
2100
+ const watchTargets = getWorkerWatchTargets();
2101
+ if (watchTargets.length === 0) {
2102
+ return;
2103
+ }
2104
+ workerWatchTargets = watchTargets;
2105
+ workerSourceWatcher = await startWorkerSourceWatcher({
2106
+ watchTargets,
2107
+ resolvedWorkerConfigPath,
2108
+ logger,
2109
+ onConfigChange: reloadWorkerOnlyConfig,
2110
+ onWorkerChange: async () => {
2111
+ await refreshWorkerOnlySurfaceState();
2112
+ await reloadMiniflare(currentDoResult);
2113
+ }
2114
+ });
2115
+ }
2116
+ async function start() {
2117
+ logger?.info("Starting unified dev server...");
2118
+ config = await loadConfig({ cwd, configFile: configPath });
2119
+ setLocalSendEmailBindings(config.bindings?.sendEmail ?? {});
2120
+ resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
2121
+ logger?.debug("Loaded config:", config.name);
2122
+ if (enableVite) {
2123
+ const viteProject = await detectViteProject(cwd);
2124
+ generatedViteConfigPath = await writeGeneratedViteConfig({
2125
+ cwd,
2126
+ configPath,
2127
+ localConfigPath: viteProject.viteConfigPath,
2128
+ bridgePort: miniflarePort
2129
+ });
2130
+ logger?.debug(`Generated Vite config → ${generatedViteConfigPath}`);
2131
+ }
2132
+ await refreshWorkerOnlySurfaceState();
2133
+ if (!enableVite && (hasWorkerSurfacePaths(mainWorkerSurfacePaths) || Boolean(mainWorkerRoutes?.routes.length))) {
2134
+ const detectedWorkerHandlers = Object.entries(mainWorkerSurfacePaths).filter(([, surfacePath]) => !!surfacePath).map(([surfaceName, surfacePath]) => `${surfaceName}=${surfacePath}`);
2135
+ const detectedRouteHandlers = mainWorkerRoutes?.routes.map((route) => `route=${route.filePath}`) ?? [];
2136
+ logger?.info(`Worker handlers detected: ${[...detectedWorkerHandlers, ...detectedRouteHandlers].join(", ")}`);
2137
+ } else if (!enableVite) {
2138
+ logger?.warn("No local worker handler entry was found for worker-only mode");
2139
+ }
2140
+ const remoteCheck = await checkRemoteBindingRequirements(config);
2141
+ if (remoteCheck.hasRemoteBindings) {
2142
+ logger?.info("");
2143
+ logger?.warn("⚠️ Remote-only bindings detected:");
2144
+ for (const binding of remoteCheck.remoteBindings) {
2145
+ logger?.warn(` • ${binding}`);
2146
+ }
2147
+ logger?.info("");
2148
+ if (remoteCheck.missingAccountId) {
2149
+ logger?.warn("⚠️ WARN: accountId is not set in devflare.config.ts");
2150
+ logger?.warn(" Remote bindings (AI, Vectorize) require accountId to charge the correct account.");
2151
+ logger?.warn(" Add: accountId: 'your-cloudflare-account-id'");
2152
+ logger?.info("");
2153
+ }
2154
+ if (remoteCheck.notLoggedIn) {
2155
+ logger?.warn("⚠️ WARN: Not logged in to Wrangler");
2156
+ logger?.warn(" Remote bindings require authentication.");
2157
+ logger?.warn(" Run: bunx wrangler login");
2158
+ logger?.info("");
2159
+ }
2160
+ if (!remoteCheck.missingAccountId && !remoteCheck.notLoggedIn) {
2161
+ logger?.success("✓ Remote binding requirements met");
2162
+ logger?.info("");
2163
+ }
2164
+ }
2165
+ const browserBinding = getSingleBrowserBindingName(config.bindings?.browser);
2166
+ if (browserBinding) {
2167
+ logger?.info(`Starting Browser Rendering shim (binding: ${browserBinding})...`);
2168
+ browserShim = createBrowserShim({
2169
+ port: browserShimPort,
2170
+ host: "127.0.0.1",
2171
+ logger,
2172
+ verbose
2173
+ });
2174
+ await browserShim.start();
2175
+ }
2176
+ const doPattern = config.files?.durableObjects;
2177
+ let doResult = null;
2178
+ if (typeof doPattern === "string" && doPattern) {
2179
+ const outDir = resolve3(cwd, ".devflare/do-bundles");
2180
+ doBundler = createDOBundler({
2181
+ cwd,
2182
+ pattern: doPattern,
2183
+ outDir,
2184
+ rolldownOptions: config.rolldown?.options,
2185
+ sourcemap: config.rolldown?.sourcemap,
2186
+ minify: config.rolldown?.minify,
2187
+ logger,
2188
+ onRebuild: async (result) => {
2189
+ await reloadMiniflare(result);
2190
+ }
2191
+ });
2192
+ doResult = await doBundler.build();
2193
+ currentDoResult = doResult;
2194
+ await doBundler.watch();
2195
+ }
2196
+ currentDoResult = doResult;
2197
+ await startMiniflare(doResult);
2198
+ await startWorkerSourceWatcher2();
2199
+ if (enableVite) {
2200
+ viteProcess = await startViteProcess({
2201
+ cwd,
2202
+ vitePort,
2203
+ miniflarePort,
2204
+ generatedViteConfigPath,
2205
+ logger
2206
+ });
2207
+ } else {
2208
+ logger?.info("Vite startup skipped (no effective Vite config found for this package)");
2209
+ }
2210
+ await new Promise((r) => setTimeout(r, 1000));
2211
+ await runD1Migrations({ cwd, config, miniflarePort, logger });
2212
+ }
2213
+ async function stop() {
2214
+ if (doBundler) {
2215
+ await doBundler.close();
2216
+ doBundler = null;
2217
+ }
2218
+ if (workerSourceWatcher) {
2219
+ await workerSourceWatcher.close();
2220
+ workerSourceWatcher = null;
2221
+ }
2222
+ if (miniflare) {
2223
+ await miniflare.dispose();
2224
+ miniflare = null;
2225
+ }
2226
+ if (viteProcess) {
2227
+ await stopSpawnedProcessTree(viteProcess);
2228
+ viteProcess = null;
2229
+ }
2230
+ if (browserShim) {
2231
+ await browserShim.stop();
2232
+ browserShim = null;
2233
+ }
2234
+ clearLocalSendEmailBindings();
2235
+ }
2236
+ function getMiniflare() {
2237
+ return miniflare;
2238
+ }
2239
+ return {
2240
+ start,
2241
+ stop,
2242
+ getMiniflare
2243
+ };
2244
+ }
2245
+ // src/cli/commands/dev.ts
2246
+ async function createLogWriter(cwd, options) {
2247
+ if (!options.log && !options.logTemp) {
2248
+ return null;
2249
+ }
2250
+ const fs = await import("node:fs");
2251
+ let logPath;
2252
+ if (options.logTemp) {
2253
+ logPath = resolve4(cwd, ".log");
2254
+ } else {
2255
+ const now = new Date;
2256
+ const timestamp = now.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
2257
+ logPath = resolve4(cwd, `.log-${timestamp}`);
2258
+ }
2259
+ const fileStream = fs.createWriteStream(logPath, { flags: "w" });
2260
+ const ansiRegex = /\x1b\[[0-9;]*m/g;
2261
+ return {
2262
+ path: logPath,
2263
+ write(data, source) {
2264
+ const str = typeof data === "string" ? data : data.toString();
2265
+ if (!str.trim())
2266
+ return;
2267
+ const timestamp = new Date().toISOString().slice(11, 23);
2268
+ const prefix = source ? `[${timestamp}][${source.toUpperCase()}] ` : `[${timestamp}] `;
2269
+ const cleanStr = str.replace(ansiRegex, "");
2270
+ fileStream.write(prefix + cleanStr + (cleanStr.endsWith(`
2271
+ `) ? "" : `
2272
+ `));
2273
+ },
2274
+ close() {
2275
+ fileStream.end();
2276
+ }
2277
+ };
2278
+ }
2279
+ async function runDevCommand(parsed, logger, options) {
2280
+ const cwd = options.cwd || parsed.options.cwd || process.cwd();
2281
+ const configPath = parsed.options.config;
2282
+ const port = parsed.options.port;
2283
+ const logEnabled = parsed.options.log === true;
2284
+ const logTempEnabled = parsed.options["log-temp"] === true;
2285
+ const persistEnabled = parsed.options.persist === true;
2286
+ const debugEnabled = parsed.options.debug === true || process.env.DEVFLARE_DEBUG === "true";
2287
+ const verbose = parsed.options.verbose === true || debugEnabled;
2288
+ const theme = createCliTheme(parsed.options);
2289
+ const config = await loadConfig({ cwd, configFile: configPath });
2290
+ const viteProject = resolveEffectiveViteProject(await detectViteProject(cwd), config);
2291
+ const logWriter = await createLogWriter(cwd, {
2292
+ log: logEnabled,
2293
+ logTemp: logTempEnabled
2294
+ });
2295
+ if (logWriter) {
2296
+ const logFile = relative(cwd, logWriter.path) || ".log";
2297
+ logLine(logger, `${dim("logging", theme)} ${logFile}`);
2298
+ }
2299
+ const devLogger = createConsola({
2300
+ level: verbose ? 4 : 3
2301
+ });
2302
+ if (logWriter) {
2303
+ const wrapLog = (original, prefix = "") => {
2304
+ return (message, ...args) => {
2305
+ original(message, ...args);
2306
+ const formatted = prefix ? `${prefix} ${[message, ...args].join(" ")}` : [message, ...args].join(" ");
2307
+ logWriter.write(formatted);
2308
+ };
2309
+ };
2310
+ Object.assign(devLogger.log, wrapLog(devLogger.log.bind(devLogger)));
2311
+ Object.assign(devLogger.info, wrapLog(devLogger.info.bind(devLogger)));
2312
+ Object.assign(devLogger.error, wrapLog(devLogger.error.bind(devLogger), "[ERROR]"));
2313
+ Object.assign(devLogger.warn, wrapLog(devLogger.warn.bind(devLogger), "[WARN]"));
2314
+ Object.assign(devLogger.success, wrapLog(devLogger.success.bind(devLogger), "[OK]"));
2315
+ Object.assign(devLogger.debug, wrapLog(devLogger.debug.bind(devLogger), "[DEBUG]"));
2316
+ }
2317
+ try {
2318
+ logLine(logger);
2319
+ if (viteProject.shouldStartVite) {
2320
+ logLine(logger, `${cyanBold("dev", theme)} ${dim("Unified Dev Server", theme)}`);
2321
+ logLine(logger, " ├─ Vite: Full HMR for frontend");
2322
+ logLine(logger, " ├─ Miniflare: All Cloudflare bindings");
2323
+ logLine(logger, " ├─ Rolldown: Worker + DO bundling with watch");
2324
+ logLine(logger, " └─ Bridge: WebSocket RPC connection");
2325
+ } else {
2326
+ logLine(logger, `${cyanBold("dev", theme)} ${dim("Worker Dev Server", theme)}`);
2327
+ logLine(logger, " ├─ Miniflare: All Cloudflare bindings");
2328
+ logLine(logger, " ├─ Rolldown: Worker + DO bundling with watch");
2329
+ logLine(logger, " └─ Vite: Disabled (no effective Vite config found)");
2330
+ if (viteProject.wantsViteIntegration) {
2331
+ logger.warn("Vite-related settings were detected, but no effective Vite config was available");
2332
+ logger.warn("Skipping Vite startup and running in worker-only mode");
2333
+ }
2334
+ }
2335
+ logLine(logger);
2336
+ const devServer = createDevServer({
2337
+ cwd,
2338
+ configPath,
2339
+ vitePort: port ? parseInt(port, 10) : 5173,
2340
+ miniflarePort: 8787,
2341
+ enableVite: viteProject.shouldStartVite,
2342
+ persist: persistEnabled,
2343
+ logger: devLogger,
2344
+ verbose,
2345
+ debug: debugEnabled
2346
+ });
2347
+ let isCleaningUp = false;
2348
+ const cleanupHandlers = new Map;
2349
+ const removeCleanupHandlers = () => {
2350
+ for (const [event, handler] of cleanupHandlers) {
2351
+ process.off(event, handler);
2352
+ }
2353
+ cleanupHandlers.clear();
2354
+ };
2355
+ const cleanup = async (exitCode, reason) => {
2356
+ if (isCleaningUp) {
2357
+ return;
2358
+ }
2359
+ isCleaningUp = true;
2360
+ removeCleanupHandlers();
2361
+ if (reason) {
2362
+ const message = reason instanceof Error ? reason.stack ?? reason.message : String(reason);
2363
+ logger.error(message);
2364
+ }
2365
+ logLine(logger);
2366
+ logLine(logger, `${yellow("dev", theme)} ${dim("Shutting down…", theme)}`);
2367
+ try {
2368
+ await devServer.stop();
2369
+ } finally {
2370
+ logWriter?.close();
2371
+ process.exit(exitCode);
2372
+ }
2373
+ };
2374
+ const registerCleanupHandler = (event, handler) => {
2375
+ cleanupHandlers.set(event, handler);
2376
+ process.on(event, handler);
2377
+ };
2378
+ registerCleanupHandler("SIGINT", () => {
2379
+ cleanup(0);
2380
+ });
2381
+ registerCleanupHandler("SIGTERM", () => {
2382
+ cleanup(0);
2383
+ });
2384
+ registerCleanupHandler("SIGHUP", () => {
2385
+ cleanup(0);
2386
+ });
2387
+ registerCleanupHandler("uncaughtException", (error) => {
2388
+ cleanup(1, error);
2389
+ });
2390
+ registerCleanupHandler("unhandledRejection", (reason) => {
2391
+ cleanup(1, reason);
2392
+ });
2393
+ await devServer.start();
2394
+ await new Promise(() => {});
2395
+ return { exitCode: 0 };
2396
+ } catch (error) {
2397
+ logWriter?.close();
2398
+ if (error instanceof Error) {
2399
+ logger.error("Dev server failed:", error.message);
2400
+ if (verbose) {
2401
+ logger.error(error.stack);
2402
+ }
2403
+ }
2404
+ return { exitCode: 1 };
2405
+ }
2406
+ }
2407
+ export {
2408
+ runDevCommand
2409
+ };