devflare 1.0.0-next.7 → 1.0.0-next.71

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 (635) hide show
  1. package/LICENSE +21 -0
  2. package/LLM.md +17316 -2144
  3. package/README.md +398 -607
  4. package/bin/devflare.js +30 -14
  5. package/dist/_chunks/account-B9ULT9HA.js +462 -0
  6. package/dist/_chunks/account-resources-BdKj8q1h.js +159 -0
  7. package/dist/_chunks/ai-DKDn1iys.js +322 -0
  8. package/dist/_chunks/api-BKClUlUf.js +568 -0
  9. package/dist/_chunks/build-B9jQtUny.js +24 -0
  10. package/dist/_chunks/build-artifacts-C7AbI34o.js +484 -0
  11. package/dist/_chunks/cli-CBRR2-Xr.js +1214 -0
  12. package/dist/_chunks/cloudflare-D_5UWT84.js +157 -0
  13. package/dist/_chunks/colors-D0NVzAFN.js +13 -0
  14. package/dist/_chunks/command-utils-qlsHELwW.js +45 -0
  15. package/dist/_chunks/compiler-UxXQgEPq.js +521 -0
  16. package/dist/_chunks/config-CHdi5-5R.js +78 -0
  17. package/dist/_chunks/config-path-DzZbchb6.js +42 -0
  18. package/dist/_chunks/context-DGMev2AF.js +2515 -0
  19. package/dist/_chunks/decorators-eXNZ1S0N.js +19 -0
  20. package/dist/_chunks/dependencies-HLIhxbIe.js +57 -0
  21. package/dist/_chunks/deploy-dIZuouRW.js +1033 -0
  22. package/dist/_chunks/dev-CRW_hBA3.js +953 -0
  23. package/dist/_chunks/doctor-t8hKA53t.js +219 -0
  24. package/dist/_chunks/durable-object-DuMPM8re.js +234 -0
  25. package/dist/_chunks/env-D4uJqqgu.js +125 -0
  26. package/dist/_chunks/family-DHkROYgX.js +143 -0
  27. package/dist/_chunks/generated-artifacts-D_VIzDOi.js +31 -0
  28. package/dist/_chunks/generated-dir-DC2kM-mt.js +33 -0
  29. package/dist/_chunks/glob-DRw9sbfF.js +56 -0
  30. package/dist/_chunks/init-B9_QIZcO.js +165 -0
  31. package/dist/_chunks/loader-BC0skbnH.js +1330 -0
  32. package/dist/_chunks/local-dev-vars-BFLS-HoI.js +81 -0
  33. package/dist/_chunks/local-hyperdrive-CcORzo76.js +119 -0
  34. package/dist/_chunks/local-media-bindings-ZQe-epuw.js +335 -0
  35. package/dist/_chunks/local-secrets-C0n5oRgI.js +123 -0
  36. package/dist/_chunks/login-Ctw59McM.js +51 -0
  37. package/dist/_chunks/package-metadata-CLI488gB.js +39 -0
  38. package/dist/_chunks/preferences-CbYERA_m.js +241 -0
  39. package/dist/_chunks/preview-j9Kbx4-G.js +462 -0
  40. package/dist/_chunks/preview-registry-CTXU4mBc.js +1186 -0
  41. package/dist/_chunks/preview-resources-BNvfkfqP.js +359 -0
  42. package/dist/_chunks/previews-DXlf8SaQ.js +1133 -0
  43. package/dist/_chunks/productions-TkIy7b1Q.js +504 -0
  44. package/dist/_chunks/r2-presign-runtime-DlgH_FY8.js +291 -0
  45. package/dist/_chunks/ref-CYJNAAe_.js +178 -0
  46. package/dist/_chunks/remote-DB8SU7ea.js +79 -0
  47. package/dist/_chunks/remote-config-Bw6qIVM4.js +132 -0
  48. package/dist/_chunks/resolve-package-DeJUGxoE.js +152 -0
  49. package/dist/_chunks/rolldown-runtime-CvQ6eiug.js +14 -0
  50. package/dist/_chunks/routes-CRXB3-iw.js +851 -0
  51. package/dist/_chunks/runtime-BlKTWIgD.js +874 -0
  52. package/dist/_chunks/schema-normalization-CTdiY17P.js +667 -0
  53. package/dist/_chunks/secrets-CzSWX4-i.js +95 -0
  54. package/dist/_chunks/tail-DXKaqcPB.js +239 -0
  55. package/dist/_chunks/token-CEIRtse8.js +333 -0
  56. package/dist/_chunks/tokens-CE7-gV5E.js +221 -0
  57. package/dist/_chunks/types-BB15NBqw.js +484 -0
  58. package/dist/_chunks/ui-BhCdDC2N.js +103 -0
  59. package/dist/_chunks/usage-rRusKejc.js +272 -0
  60. package/dist/_chunks/vite-DTz2Pbtm.js +1267 -0
  61. package/dist/_chunks/vite-process-BhGXJ764.js +2928 -0
  62. package/dist/_chunks/vite-utils-D_VR07Ks.js +183 -0
  63. package/dist/_chunks/worker-CgIvLp-D.js +410 -0
  64. package/dist/_chunks/worker-bundler-CeMqQtBQ.js +457 -0
  65. package/dist/_chunks/worker-entrypoint-C8j23t6d.js +247 -0
  66. package/dist/_chunks/workerName-kEXwmV2m.js +23 -0
  67. package/dist/_chunks/workspace-CbollZBv.js +518 -0
  68. package/dist/_chunks/workspace-Ds3xMjOE.js +175 -0
  69. package/dist/bridge/client.d.ts +93 -8
  70. package/dist/bridge/client.d.ts.map +1 -1
  71. package/dist/bridge/do-rpc-dispatch.d.ts +23 -0
  72. package/dist/bridge/do-rpc-dispatch.d.ts.map +1 -0
  73. package/dist/bridge/do-websocket-connect.d.ts +33 -0
  74. package/dist/bridge/do-websocket-connect.d.ts.map +1 -0
  75. package/dist/bridge/gateway-runtime.d.ts +8 -0
  76. package/dist/bridge/gateway-runtime.d.ts.map +1 -0
  77. package/dist/bridge/index.d.ts +6 -6
  78. package/dist/bridge/index.d.ts.map +1 -1
  79. package/dist/bridge/log.d.ts +5 -0
  80. package/dist/bridge/log.d.ts.map +1 -0
  81. package/dist/bridge/miniflare-gateway.d.ts +45 -0
  82. package/dist/bridge/miniflare-gateway.d.ts.map +1 -0
  83. package/dist/bridge/miniflare.d.ts +92 -2
  84. package/dist/bridge/miniflare.d.ts.map +1 -1
  85. package/dist/bridge/proxy.d.ts +12 -11
  86. package/dist/bridge/proxy.d.ts.map +1 -1
  87. package/dist/bridge/r2-presign-runtime.d.ts +9 -0
  88. package/dist/bridge/r2-presign-runtime.d.ts.map +1 -0
  89. package/dist/bridge/server.d.ts +2 -1
  90. package/dist/bridge/server.d.ts.map +1 -1
  91. package/dist/bridge/v2/body-streams.d.ts +53 -0
  92. package/dist/bridge/v2/body-streams.d.ts.map +1 -0
  93. package/dist/bridge/v2/codec.d.ts +106 -0
  94. package/dist/bridge/v2/codec.d.ts.map +1 -0
  95. package/dist/bridge/v2/control-messages.d.ts +46 -0
  96. package/dist/bridge/v2/control-messages.d.ts.map +1 -0
  97. package/dist/bridge/v2/frames.d.ts +109 -0
  98. package/dist/bridge/v2/frames.d.ts.map +1 -0
  99. package/dist/bridge/v2/index.d.ts +11 -0
  100. package/dist/bridge/v2/index.d.ts.map +1 -0
  101. package/dist/bridge/v2/serialization.d.ts +48 -0
  102. package/dist/bridge/v2/serialization.d.ts.map +1 -0
  103. package/dist/bridge/v2/transport.d.ts +31 -0
  104. package/dist/bridge/v2/transport.d.ts.map +1 -0
  105. package/dist/bridge/v2/value-codec.d.ts +61 -0
  106. package/dist/bridge/v2/value-codec.d.ts.map +1 -0
  107. package/dist/bridge/v2/value-serialization.d.ts +139 -0
  108. package/dist/bridge/v2/value-serialization.d.ts.map +1 -0
  109. package/dist/bridge/{protocol.d.ts → v2/wire.d.ts} +32 -8
  110. package/dist/bridge/v2/wire.d.ts.map +1 -0
  111. package/dist/bridge/v2/ws-relay.d.ts +53 -0
  112. package/dist/bridge/v2/ws-relay.d.ts.map +1 -0
  113. package/dist/bridge/websocket-constructor.d.ts +38 -0
  114. package/dist/bridge/websocket-constructor.d.ts.map +1 -0
  115. package/dist/browser-shim/handler.d.ts +2 -2
  116. package/dist/browser-shim/handler.d.ts.map +1 -1
  117. package/dist/browser-shim/index.d.ts +2 -2
  118. package/dist/browser-shim/server.d.ts +63 -0
  119. package/dist/browser-shim/server.d.ts.map +1 -1
  120. package/dist/browser.d.ts +5778 -43
  121. package/dist/browser.d.ts.map +1 -1
  122. package/dist/browser.js +73 -142
  123. package/dist/bundler/defaults.d.ts +23 -0
  124. package/dist/bundler/defaults.d.ts.map +1 -0
  125. package/dist/bundler/do-bundler.d.ts +7 -0
  126. package/dist/bundler/do-bundler.d.ts.map +1 -1
  127. package/dist/bundler/index.d.ts +3 -1
  128. package/dist/bundler/index.d.ts.map +1 -1
  129. package/dist/bundler/rolldown-shared.d.ts +52 -0
  130. package/dist/bundler/rolldown-shared.d.ts.map +1 -0
  131. package/dist/bundler/worker-bundler.d.ts +13 -0
  132. package/dist/bundler/worker-bundler.d.ts.map +1 -0
  133. package/dist/bundler/worker-compat.d.ts +4 -0
  134. package/dist/bundler/worker-compat.d.ts.map +1 -0
  135. package/dist/cli/build-manifest.d.ts +66 -0
  136. package/dist/cli/build-manifest.d.ts.map +1 -0
  137. package/dist/cli/command-utils.d.ts +18 -0
  138. package/dist/cli/command-utils.d.ts.map +1 -0
  139. package/dist/cli/commands/account.d.ts +2 -2
  140. package/dist/cli/commands/account.d.ts.map +1 -1
  141. package/dist/cli/commands/ai.d.ts +1 -1
  142. package/dist/cli/commands/ai.d.ts.map +1 -1
  143. package/dist/cli/commands/build-artifacts.d.ts +40 -0
  144. package/dist/cli/commands/build-artifacts.d.ts.map +1 -0
  145. package/dist/cli/commands/build.d.ts +2 -2
  146. package/dist/cli/commands/build.d.ts.map +1 -1
  147. package/dist/cli/commands/config.d.ts +4 -0
  148. package/dist/cli/commands/config.d.ts.map +1 -0
  149. package/dist/cli/commands/deploy/metadata.d.ts +18 -0
  150. package/dist/cli/commands/deploy/metadata.d.ts.map +1 -0
  151. package/dist/cli/commands/deploy/prepare.d.ts +23 -0
  152. package/dist/cli/commands/deploy/prepare.d.ts.map +1 -0
  153. package/dist/cli/commands/deploy/runtime.d.ts +4 -0
  154. package/dist/cli/commands/deploy/runtime.d.ts.map +1 -0
  155. package/dist/cli/commands/deploy/verification.d.ts +36 -0
  156. package/dist/cli/commands/deploy/verification.d.ts.map +1 -0
  157. package/dist/cli/commands/deploy.d.ts +2 -2
  158. package/dist/cli/commands/deploy.d.ts.map +1 -1
  159. package/dist/cli/commands/dev.d.ts +9 -1
  160. package/dist/cli/commands/dev.d.ts.map +1 -1
  161. package/dist/cli/commands/doctor.d.ts +2 -2
  162. package/dist/cli/commands/doctor.d.ts.map +1 -1
  163. package/dist/cli/commands/init.d.ts +2 -2
  164. package/dist/cli/commands/init.d.ts.map +1 -1
  165. package/dist/cli/commands/login.d.ts +4 -0
  166. package/dist/cli/commands/login.d.ts.map +1 -0
  167. package/dist/cli/commands/previews-support/cleanup.d.ts +9 -0
  168. package/dist/cli/commands/previews-support/cleanup.d.ts.map +1 -0
  169. package/dist/cli/commands/previews-support/family.d.ts +10 -0
  170. package/dist/cli/commands/previews-support/family.d.ts.map +1 -0
  171. package/dist/cli/commands/previews-support/render.d.ts +8 -0
  172. package/dist/cli/commands/previews-support/render.d.ts.map +1 -0
  173. package/dist/cli/commands/previews-support/theme.d.ts +10 -0
  174. package/dist/cli/commands/previews-support/theme.d.ts.map +1 -0
  175. package/dist/cli/commands/previews-support/types.d.ts +70 -0
  176. package/dist/cli/commands/previews-support/types.d.ts.map +1 -0
  177. package/dist/cli/commands/previews.d.ts +4 -0
  178. package/dist/cli/commands/previews.d.ts.map +1 -0
  179. package/dist/cli/commands/productions.d.ts +4 -0
  180. package/dist/cli/commands/productions.d.ts.map +1 -0
  181. package/dist/cli/commands/remote.d.ts +1 -1
  182. package/dist/cli/commands/remote.d.ts.map +1 -1
  183. package/dist/cli/commands/secrets.d.ts +4 -0
  184. package/dist/cli/commands/secrets.d.ts.map +1 -0
  185. package/dist/cli/commands/tail.d.ts +44 -0
  186. package/dist/cli/commands/tail.d.ts.map +1 -0
  187. package/dist/cli/commands/token.d.ts +4 -0
  188. package/dist/cli/commands/token.d.ts.map +1 -0
  189. package/dist/cli/commands/type-generation/discovery.d.ts +7 -0
  190. package/dist/cli/commands/type-generation/discovery.d.ts.map +1 -0
  191. package/dist/cli/commands/type-generation/generator.d.ts +121 -0
  192. package/dist/cli/commands/type-generation/generator.d.ts.map +1 -0
  193. package/dist/cli/commands/type-generation/models.d.ts +27 -0
  194. package/dist/cli/commands/type-generation/models.d.ts.map +1 -0
  195. package/dist/cli/commands/types.d.ts +2 -2
  196. package/dist/cli/commands/types.d.ts.map +1 -1
  197. package/dist/cli/commands/worker.d.ts +4 -0
  198. package/dist/cli/commands/worker.d.ts.map +1 -0
  199. package/dist/cli/commands/workspace.d.ts +8 -0
  200. package/dist/cli/commands/workspace.d.ts.map +1 -0
  201. package/dist/cli/config-path.d.ts +6 -0
  202. package/dist/cli/config-path.d.ts.map +1 -0
  203. package/dist/cli/dependencies.d.ts +2 -1
  204. package/dist/cli/dependencies.d.ts.map +1 -1
  205. package/dist/cli/deploy-strategy.d.ts +17 -0
  206. package/dist/cli/deploy-strategy.d.ts.map +1 -0
  207. package/dist/cli/deploy-target.d.ts +17 -0
  208. package/dist/cli/deploy-target.d.ts.map +1 -0
  209. package/dist/cli/generated-artifacts.d.ts +12 -0
  210. package/dist/cli/generated-artifacts.d.ts.map +1 -0
  211. package/dist/cli/gradual-deploy.d.ts +49 -0
  212. package/dist/cli/gradual-deploy.d.ts.map +1 -0
  213. package/dist/cli/help-pages/pages/account.d.ts +3 -0
  214. package/dist/cli/help-pages/pages/account.d.ts.map +1 -0
  215. package/dist/cli/help-pages/pages/core.d.ts +4 -0
  216. package/dist/cli/help-pages/pages/core.d.ts.map +1 -0
  217. package/dist/cli/help-pages/pages/index.d.ts +3 -0
  218. package/dist/cli/help-pages/pages/index.d.ts.map +1 -0
  219. package/dist/cli/help-pages/pages/misc.d.ts +3 -0
  220. package/dist/cli/help-pages/pages/misc.d.ts.map +1 -0
  221. package/dist/cli/help-pages/pages/previews.d.ts +3 -0
  222. package/dist/cli/help-pages/pages/previews.d.ts.map +1 -0
  223. package/dist/cli/help-pages/pages/productions.d.ts +3 -0
  224. package/dist/cli/help-pages/pages/productions.d.ts.map +1 -0
  225. package/dist/cli/help-pages/render.d.ts +12 -0
  226. package/dist/cli/help-pages/render.d.ts.map +1 -0
  227. package/dist/cli/help-pages/shared.d.ts +15 -0
  228. package/dist/cli/help-pages/shared.d.ts.map +1 -0
  229. package/dist/cli/help-pages/types.d.ts +23 -0
  230. package/dist/cli/help-pages/types.d.ts.map +1 -0
  231. package/dist/cli/help.d.ts +6 -0
  232. package/dist/cli/help.d.ts.map +1 -0
  233. package/dist/cli/index.d.ts +1 -0
  234. package/dist/cli/index.d.ts.map +1 -1
  235. package/dist/cli/index.js +2 -0
  236. package/dist/cli/package-metadata.d.ts +16 -0
  237. package/dist/cli/package-metadata.d.ts.map +1 -0
  238. package/dist/cli/preview-bindings.d.ts +49 -0
  239. package/dist/cli/preview-bindings.d.ts.map +1 -0
  240. package/dist/cli/preview.d.ts +10 -0
  241. package/dist/cli/preview.d.ts.map +1 -0
  242. package/dist/cli/ui.d.ts +37 -0
  243. package/dist/cli/ui.d.ts.map +1 -0
  244. package/dist/cli/workspace-build-guard.d.ts +14 -0
  245. package/dist/cli/workspace-build-guard.d.ts.map +1 -0
  246. package/dist/cli/wrangler-auth.d.ts +1 -1
  247. package/dist/cloudflare/account-core.d.ts +6 -0
  248. package/dist/cloudflare/account-core.d.ts.map +1 -0
  249. package/dist/cloudflare/account-resources.d.ts +40 -0
  250. package/dist/cloudflare/account-resources.d.ts.map +1 -0
  251. package/dist/cloudflare/account-status.d.ts +11 -0
  252. package/dist/cloudflare/account-status.d.ts.map +1 -0
  253. package/dist/cloudflare/account-workers.d.ts +14 -0
  254. package/dist/cloudflare/account-workers.d.ts.map +1 -0
  255. package/dist/cloudflare/account.d.ts +7 -64
  256. package/dist/cloudflare/account.d.ts.map +1 -1
  257. package/dist/cloudflare/api.d.ts +71 -3
  258. package/dist/cloudflare/api.d.ts.map +1 -1
  259. package/dist/cloudflare/auth.d.ts +1 -1
  260. package/dist/cloudflare/auth.d.ts.map +1 -1
  261. package/dist/cloudflare/index.d.ts +61 -6
  262. package/dist/cloudflare/index.d.ts.map +1 -1
  263. package/dist/cloudflare/index.js +4 -13
  264. package/dist/cloudflare/known-permission-group-ids.generated.d.ts +10 -0
  265. package/dist/cloudflare/known-permission-group-ids.generated.d.ts.map +1 -0
  266. package/dist/cloudflare/kv-namespace.d.ts +4 -0
  267. package/dist/cloudflare/kv-namespace.d.ts.map +1 -0
  268. package/dist/cloudflare/preferences.d.ts +7 -0
  269. package/dist/cloudflare/preferences.d.ts.map +1 -1
  270. package/dist/cloudflare/preview-registry-cache.d.ts +6 -0
  271. package/dist/cloudflare/preview-registry-cache.d.ts.map +1 -0
  272. package/dist/cloudflare/preview-registry-inference.d.ts +14 -0
  273. package/dist/cloudflare/preview-registry-inference.d.ts.map +1 -0
  274. package/dist/cloudflare/preview-registry-records.d.ts +4 -0
  275. package/dist/cloudflare/preview-registry-records.d.ts.map +1 -0
  276. package/dist/cloudflare/preview-registry-shape.d.ts +47 -0
  277. package/dist/cloudflare/preview-registry-shape.d.ts.map +1 -0
  278. package/dist/cloudflare/preview-registry-store.d.ts +14 -0
  279. package/dist/cloudflare/preview-registry-store.d.ts.map +1 -0
  280. package/dist/cloudflare/preview-registry-transport.d.ts +4 -0
  281. package/dist/cloudflare/preview-registry-transport.d.ts.map +1 -0
  282. package/dist/cloudflare/preview-registry-types.d.ts +103 -0
  283. package/dist/cloudflare/preview-registry-types.d.ts.map +1 -0
  284. package/dist/cloudflare/preview-registry.d.ts +42 -0
  285. package/dist/cloudflare/preview-registry.d.ts.map +1 -0
  286. package/dist/cloudflare/preview-urls.d.ts +3 -0
  287. package/dist/cloudflare/preview-urls.d.ts.map +1 -0
  288. package/dist/cloudflare/registry-schema.d.ts +253 -0
  289. package/dist/cloudflare/registry-schema.d.ts.map +1 -0
  290. package/dist/cloudflare/remote-config.d.ts.map +1 -1
  291. package/dist/cloudflare/tokens.d.ts +44 -0
  292. package/dist/cloudflare/tokens.d.ts.map +1 -0
  293. package/dist/cloudflare/types.d.ts +124 -7
  294. package/dist/cloudflare/types.d.ts.map +1 -1
  295. package/dist/cloudflare/usage.d.ts +31 -3
  296. package/dist/cloudflare/usage.d.ts.map +1 -1
  297. package/dist/config/binding-resolution-helpers.d.ts +41 -0
  298. package/dist/config/binding-resolution-helpers.d.ts.map +1 -0
  299. package/dist/config/compatibility.d.ts +3 -0
  300. package/dist/config/compatibility.d.ts.map +1 -0
  301. package/dist/config/compiler/bindings.d.ts +14 -0
  302. package/dist/config/compiler/bindings.d.ts.map +1 -0
  303. package/dist/config/compiler/core-helpers.d.ts +6 -0
  304. package/dist/config/compiler/core-helpers.d.ts.map +1 -0
  305. package/dist/config/compiler/do-workers.d.ts +34 -0
  306. package/dist/config/compiler/do-workers.d.ts.map +1 -0
  307. package/dist/config/compiler/paths.d.ts +18 -0
  308. package/dist/config/compiler/paths.d.ts.map +1 -0
  309. package/dist/config/compiler/types.d.ts +308 -0
  310. package/dist/config/compiler/types.d.ts.map +1 -0
  311. package/dist/config/compiler.d.ts +22 -134
  312. package/dist/config/compiler.d.ts.map +1 -1
  313. package/dist/config/cron.d.ts +19 -0
  314. package/dist/config/cron.d.ts.map +1 -0
  315. package/dist/config/define.d.ts +8 -5
  316. package/dist/config/define.d.ts.map +1 -1
  317. package/dist/config/deploy-resources.d.ts +58 -0
  318. package/dist/config/deploy-resources.d.ts.map +1 -0
  319. package/dist/config/env-vars.d.ts +309 -0
  320. package/dist/config/env-vars.d.ts.map +1 -0
  321. package/dist/config/framework-providers.d.ts +9 -0
  322. package/dist/config/framework-providers.d.ts.map +1 -0
  323. package/dist/config/index.d.ts +13 -5
  324. package/dist/config/index.d.ts.map +1 -1
  325. package/dist/config/loader.d.ts +2 -1
  326. package/dist/config/loader.d.ts.map +1 -1
  327. package/dist/config/local-dev-vars.d.ts +15 -0
  328. package/dist/config/local-dev-vars.d.ts.map +1 -0
  329. package/dist/config/preview-resources.d.ts +83 -0
  330. package/dist/config/preview-resources.d.ts.map +1 -0
  331. package/dist/config/preview.d.ts +29 -0
  332. package/dist/config/preview.d.ts.map +1 -0
  333. package/dist/config/ref.d.ts +16 -25
  334. package/dist/config/ref.d.ts.map +1 -1
  335. package/dist/config/resolve-phased.d.ts +72 -0
  336. package/dist/config/resolve-phased.d.ts.map +1 -0
  337. package/dist/config/resolve.d.ts +11 -0
  338. package/dist/config/resolve.d.ts.map +1 -0
  339. package/dist/config/resource-resolution.d.ts +80 -0
  340. package/dist/config/resource-resolution.d.ts.map +1 -0
  341. package/dist/config/schema-bindings.d.ts +2124 -0
  342. package/dist/config/schema-bindings.d.ts.map +1 -0
  343. package/dist/config/schema-build.d.ts +67 -0
  344. package/dist/config/schema-build.d.ts.map +1 -0
  345. package/dist/config/schema-env.d.ts +3821 -0
  346. package/dist/config/schema-env.d.ts.map +1 -0
  347. package/dist/config/schema-normalization.d.ts +272 -0
  348. package/dist/config/schema-normalization.d.ts.map +1 -0
  349. package/dist/config/schema-runtime.d.ts +597 -0
  350. package/dist/config/schema-runtime.d.ts.map +1 -0
  351. package/dist/config/schema-types-bindings-platform.d.ts +520 -0
  352. package/dist/config/schema-types-bindings-platform.d.ts.map +1 -0
  353. package/dist/config/schema-types-bindings-resources.d.ts +802 -0
  354. package/dist/config/schema-types-bindings-resources.d.ts.map +1 -0
  355. package/dist/config/schema-types-bindings.d.ts +293 -0
  356. package/dist/config/schema-types-bindings.d.ts.map +1 -0
  357. package/dist/config/schema-types-build.d.ts +86 -0
  358. package/dist/config/schema-types-build.d.ts.map +1 -0
  359. package/dist/config/schema-types-runtime-server.d.ts +196 -0
  360. package/dist/config/schema-types-runtime-server.d.ts.map +1 -0
  361. package/dist/config/schema-types-runtime.d.ts +913 -0
  362. package/dist/config/schema-types-runtime.d.ts.map +1 -0
  363. package/dist/config/schema-types.d.ts +458 -0
  364. package/dist/config/schema-types.d.ts.map +1 -0
  365. package/dist/config/schema.d.ts +5796 -2023
  366. package/dist/config/schema.d.ts.map +1 -1
  367. package/dist/config/service-bindings-validation.d.ts +36 -0
  368. package/dist/config/service-bindings-validation.d.ts.map +1 -0
  369. package/dist/config/workspace.d.ts +282 -0
  370. package/dist/config/workspace.d.ts.map +1 -0
  371. package/dist/config-entry.d.ts +7 -0
  372. package/dist/config-entry.d.ts.map +1 -0
  373. package/dist/config-entry.js +3 -0
  374. package/dist/decorators/durable-object.d.ts.map +1 -1
  375. package/dist/decorators/index.d.ts +2 -2
  376. package/dist/decorators/index.js +2 -9
  377. package/dist/dev-server/d1-migrations.d.ts +36 -0
  378. package/dist/dev-server/d1-migrations.d.ts.map +1 -0
  379. package/dist/dev-server/dev-server-state.d.ts +59 -0
  380. package/dist/dev-server/dev-server-state.d.ts.map +1 -0
  381. package/dist/dev-server/gateway-script.d.ts +22 -0
  382. package/dist/dev-server/gateway-script.d.ts.map +1 -0
  383. package/dist/dev-server/index.d.ts +1 -1
  384. package/dist/dev-server/miniflare-bindings.d.ts +90 -0
  385. package/dist/dev-server/miniflare-bindings.d.ts.map +1 -0
  386. package/dist/dev-server/miniflare-dev-config.d.ts +57 -0
  387. package/dist/dev-server/miniflare-dev-config.d.ts.map +1 -0
  388. package/dist/dev-server/miniflare-log.d.ts +20 -0
  389. package/dist/dev-server/miniflare-log.d.ts.map +1 -0
  390. package/dist/dev-server/miniflare-worker-config.d.ts +100 -0
  391. package/dist/dev-server/miniflare-worker-config.d.ts.map +1 -0
  392. package/dist/dev-server/reload-queue.d.ts +13 -0
  393. package/dist/dev-server/reload-queue.d.ts.map +1 -0
  394. package/dist/dev-server/runtime-health.d.ts +79 -0
  395. package/dist/dev-server/runtime-health.d.ts.map +1 -0
  396. package/dist/dev-server/runtime-stdio.d.ts +8 -0
  397. package/dist/dev-server/runtime-stdio.d.ts.map +1 -0
  398. package/dist/dev-server/server-startup-helpers.d.ts +84 -0
  399. package/dist/dev-server/server-startup-helpers.d.ts.map +1 -0
  400. package/dist/dev-server/server.d.ts +2 -0
  401. package/dist/dev-server/server.d.ts.map +1 -1
  402. package/dist/dev-server/vite-process.d.ts +24 -0
  403. package/dist/dev-server/vite-process.d.ts.map +1 -0
  404. package/dist/dev-server/vite-utils.d.ts +19 -1
  405. package/dist/dev-server/vite-utils.d.ts.map +1 -1
  406. package/dist/dev-server/worker-source-watcher.d.ts +17 -0
  407. package/dist/dev-server/worker-source-watcher.d.ts.map +1 -0
  408. package/dist/dev-server/worker-surface-paths.d.ts +6 -0
  409. package/dist/dev-server/worker-surface-paths.d.ts.map +1 -0
  410. package/dist/dev-server/workspace/merge-config.d.ts +81 -0
  411. package/dist/dev-server/workspace/merge-config.d.ts.map +1 -0
  412. package/dist/dev-server/workspace/prepare-app.d.ts +84 -0
  413. package/dist/dev-server/workspace/prepare-app.d.ts.map +1 -0
  414. package/dist/dev-server/workspace/server.d.ts +47 -0
  415. package/dist/dev-server/workspace/server.d.ts.map +1 -0
  416. package/dist/env.d.ts +10 -0
  417. package/dist/env.d.ts.map +1 -1
  418. package/dist/index.d.ts +8 -12
  419. package/dist/index.d.ts.map +1 -1
  420. package/dist/index.js +10 -297
  421. package/dist/runtime/context-events.d.ts +13 -0
  422. package/dist/runtime/context-events.d.ts.map +1 -0
  423. package/dist/runtime/context-types.d.ts +82 -0
  424. package/dist/runtime/context-types.d.ts.map +1 -0
  425. package/dist/runtime/context.d.ts +20 -44
  426. package/dist/runtime/context.d.ts.map +1 -1
  427. package/dist/runtime/exports.d.ts +70 -55
  428. package/dist/runtime/exports.d.ts.map +1 -1
  429. package/dist/runtime/index.d.ts +11 -5
  430. package/dist/runtime/index.d.ts.map +1 -1
  431. package/dist/runtime/index.js +5 -184
  432. package/dist/runtime/middleware.d.ts +119 -67
  433. package/dist/runtime/middleware.d.ts.map +1 -1
  434. package/dist/runtime/r2-presign.d.ts +164 -0
  435. package/dist/runtime/r2-presign.d.ts.map +1 -0
  436. package/dist/runtime/router/index.d.ts +7 -0
  437. package/dist/runtime/router/index.d.ts.map +1 -0
  438. package/dist/runtime/router/types.d.ts +24 -0
  439. package/dist/runtime/router/types.d.ts.map +1 -0
  440. package/dist/runtime/validation.d.ts +18 -4
  441. package/dist/runtime/validation.d.ts.map +1 -1
  442. package/dist/secrets/local-secrets.d.ts +46 -0
  443. package/dist/secrets/local-secrets.d.ts.map +1 -0
  444. package/dist/shims/local-hyperdrive.d.ts +12 -0
  445. package/dist/shims/local-hyperdrive.d.ts.map +1 -0
  446. package/dist/shims/local-media-bindings.d.ts +19 -0
  447. package/dist/shims/local-media-bindings.d.ts.map +1 -0
  448. package/dist/shims/local-worker-loader.d.ts +3 -0
  449. package/dist/shims/local-worker-loader.d.ts.map +1 -0
  450. package/dist/sveltekit/index.d.ts +1 -1
  451. package/dist/sveltekit/index.d.ts.map +1 -1
  452. package/dist/sveltekit/index.js +377 -169
  453. package/dist/sveltekit/local-bindings.d.ts +4 -0
  454. package/dist/sveltekit/local-bindings.d.ts.map +1 -0
  455. package/dist/sveltekit/platform.d.ts +42 -2
  456. package/dist/sveltekit/platform.d.ts.map +1 -1
  457. package/dist/test/ai-search.d.ts +39 -0
  458. package/dist/test/ai-search.d.ts.map +1 -0
  459. package/dist/test/alarm.d.ts +55 -0
  460. package/dist/test/alarm.d.ts.map +1 -0
  461. package/dist/test/binding-hints.d.ts +10 -0
  462. package/dist/test/binding-hints.d.ts.map +1 -0
  463. package/dist/test/cf.d.ts +71 -35
  464. package/dist/test/cf.d.ts.map +1 -1
  465. package/dist/test/containers.d.ts +87 -0
  466. package/dist/test/containers.d.ts.map +1 -0
  467. package/dist/test/email.d.ts +16 -7
  468. package/dist/test/email.d.ts.map +1 -1
  469. package/dist/test/index.d.ts +20 -21
  470. package/dist/test/index.d.ts.map +1 -1
  471. package/dist/test/index.js +5115 -75
  472. package/dist/test/offline-bindings.d.ts +124 -0
  473. package/dist/test/offline-bindings.d.ts.map +1 -0
  474. package/dist/test/queue.d.ts.map +1 -1
  475. package/dist/test/remote-ai.d.ts.map +1 -1
  476. package/dist/test/remote-cloudflare.d.ts +13 -0
  477. package/dist/test/remote-cloudflare.d.ts.map +1 -0
  478. package/dist/test/remote-vectorize.d.ts.map +1 -1
  479. package/dist/test/resolve-service-bindings.d.ts +64 -4
  480. package/dist/test/resolve-service-bindings.d.ts.map +1 -1
  481. package/dist/test/scheduled.d.ts.map +1 -1
  482. package/dist/test/should-skip.d.ts +14 -18
  483. package/dist/test/should-skip.d.ts.map +1 -1
  484. package/dist/test/simple-context-bindings.d.ts +13 -0
  485. package/dist/test/simple-context-bindings.d.ts.map +1 -0
  486. package/dist/test/simple-context-durable-objects.d.ts +6 -0
  487. package/dist/test/simple-context-durable-objects.d.ts.map +1 -0
  488. package/dist/test/simple-context-env.d.ts +35 -0
  489. package/dist/test/simple-context-env.d.ts.map +1 -0
  490. package/dist/test/simple-context-gateway-script.d.ts +2 -0
  491. package/dist/test/simple-context-gateway-script.d.ts.map +1 -0
  492. package/dist/test/simple-context-handlers.d.ts +12 -0
  493. package/dist/test/simple-context-handlers.d.ts.map +1 -0
  494. package/dist/test/simple-context-lifecycle.d.ts +32 -0
  495. package/dist/test/simple-context-lifecycle.d.ts.map +1 -0
  496. package/dist/test/simple-context-mfconfig.d.ts +18 -0
  497. package/dist/test/simple-context-mfconfig.d.ts.map +1 -0
  498. package/dist/test/simple-context-multi-worker.d.ts +16 -0
  499. package/dist/test/simple-context-multi-worker.d.ts.map +1 -0
  500. package/dist/test/simple-context-paths.d.ts +40 -0
  501. package/dist/test/simple-context-paths.d.ts.map +1 -0
  502. package/dist/test/simple-context-runtime.d.ts +19 -0
  503. package/dist/test/simple-context-runtime.d.ts.map +1 -0
  504. package/dist/test/simple-context-startup.d.ts +11 -0
  505. package/dist/test/simple-context-startup.d.ts.map +1 -0
  506. package/dist/test/simple-context-transport.d.ts +14 -0
  507. package/dist/test/simple-context-transport.d.ts.map +1 -0
  508. package/dist/test/simple-context.d.ts +8 -27
  509. package/dist/test/simple-context.d.ts.map +1 -1
  510. package/dist/test/tail.d.ts +3 -2
  511. package/dist/test/tail.d.ts.map +1 -1
  512. package/dist/test/utilities/analytics-engine.d.ts +22 -0
  513. package/dist/test/utilities/analytics-engine.d.ts.map +1 -0
  514. package/dist/test/utilities/artifacts.d.ts +11 -0
  515. package/dist/test/utilities/artifacts.d.ts.map +1 -0
  516. package/dist/test/utilities/context.d.ts +39 -0
  517. package/dist/test/utilities/context.d.ts.map +1 -0
  518. package/dist/test/utilities/d1.d.ts +21 -0
  519. package/dist/test/utilities/d1.d.ts.map +1 -0
  520. package/dist/test/utilities/env.d.ts +45 -0
  521. package/dist/test/utilities/env.d.ts.map +1 -0
  522. package/dist/test/utilities/kv.d.ts +11 -0
  523. package/dist/test/utilities/kv.d.ts.map +1 -0
  524. package/dist/test/utilities/media.d.ts +16 -0
  525. package/dist/test/utilities/media.d.ts.map +1 -0
  526. package/dist/test/utilities/platform.d.ts +41 -0
  527. package/dist/test/utilities/platform.d.ts.map +1 -0
  528. package/dist/test/utilities/queue.d.ts +5 -0
  529. package/dist/test/utilities/queue.d.ts.map +1 -0
  530. package/dist/test/utilities/r2.d.ts +2 -0
  531. package/dist/test/utilities/r2.d.ts.map +1 -0
  532. package/dist/test/utilities/send-email.d.ts +27 -0
  533. package/dist/test/utilities/send-email.d.ts.map +1 -0
  534. package/dist/test/utilities/stream-flagship.d.ts +27 -0
  535. package/dist/test/utilities/stream-flagship.d.ts.map +1 -0
  536. package/dist/test/utilities/vectorize.d.ts +38 -0
  537. package/dist/test/utilities/vectorize.d.ts.map +1 -0
  538. package/dist/test/utilities/workflows.d.ts +26 -0
  539. package/dist/test/utilities/workflows.d.ts.map +1 -0
  540. package/dist/test/utilities.d.ts +14 -98
  541. package/dist/test/utilities.d.ts.map +1 -1
  542. package/dist/test/worker.d.ts +6 -0
  543. package/dist/test/worker.d.ts.map +1 -1
  544. package/dist/transform/durable-object.d.ts.map +1 -1
  545. package/dist/transform/index.d.ts +2 -2
  546. package/dist/transform/worker-entrypoint.d.ts +5 -0
  547. package/dist/transform/worker-entrypoint.d.ts.map +1 -1
  548. package/dist/utils/entrypoint-discovery.d.ts +6 -3
  549. package/dist/utils/entrypoint-discovery.d.ts.map +1 -1
  550. package/dist/utils/generated-dir.d.ts +25 -0
  551. package/dist/utils/generated-dir.d.ts.map +1 -0
  552. package/dist/utils/glob.d.ts.map +1 -1
  553. package/dist/utils/resolve-package.d.ts.map +1 -1
  554. package/dist/utils/send-email.d.ts +15 -0
  555. package/dist/utils/send-email.d.ts.map +1 -0
  556. package/dist/utils/send-email.js +163 -0
  557. package/dist/vite/config-file.d.ts +25 -0
  558. package/dist/vite/config-file.d.ts.map +1 -0
  559. package/dist/vite/index.d.ts +3 -2
  560. package/dist/vite/index.d.ts.map +1 -1
  561. package/dist/vite/index.js +2 -407
  562. package/dist/vite/plugin-config-hook.d.ts +28 -0
  563. package/dist/vite/plugin-config-hook.d.ts.map +1 -0
  564. package/dist/vite/plugin-context.d.ts +27 -0
  565. package/dist/vite/plugin-context.d.ts.map +1 -0
  566. package/dist/vite/plugin-durable-objects.d.ts +18 -0
  567. package/dist/vite/plugin-durable-objects.d.ts.map +1 -0
  568. package/dist/vite/plugin-programmatic.d.ts +44 -0
  569. package/dist/vite/plugin-programmatic.d.ts.map +1 -0
  570. package/dist/vite/plugin-service-bindings.d.ts +13 -0
  571. package/dist/vite/plugin-service-bindings.d.ts.map +1 -0
  572. package/dist/vite/plugin-transform.d.ts +41 -0
  573. package/dist/vite/plugin-transform.d.ts.map +1 -0
  574. package/dist/vite/plugin.d.ts +10 -41
  575. package/dist/vite/plugin.d.ts.map +1 -1
  576. package/dist/worker-entry/composed-worker.d.ts +5 -8
  577. package/dist/worker-entry/composed-worker.d.ts.map +1 -1
  578. package/dist/worker-entry/durable-object-discovery.d.ts +20 -0
  579. package/dist/worker-entry/durable-object-discovery.d.ts.map +1 -0
  580. package/dist/worker-entry/extensions.d.ts +5 -0
  581. package/dist/worker-entry/extensions.d.ts.map +1 -0
  582. package/dist/worker-entry/routes.d.ts +22 -0
  583. package/dist/worker-entry/routes.d.ts.map +1 -0
  584. package/dist/worker-entry/surface-paths.d.ts +26 -0
  585. package/dist/worker-entry/surface-paths.d.ts.map +1 -0
  586. package/dist/worker-entry/validate-fetch-style.d.ts +12 -0
  587. package/dist/worker-entry/validate-fetch-style.d.ts.map +1 -0
  588. package/dist/workflows/local-workflow-entrypoints.d.ts +7 -0
  589. package/dist/workflows/local-workflow-entrypoints.d.ts.map +1 -0
  590. package/package.json +160 -117
  591. package/R2.md +0 -170
  592. package/dist/account-rvrj687w.js +0 -397
  593. package/dist/ai-dx4fr9jh.js +0 -107
  594. package/dist/bridge/protocol.d.ts.map +0 -1
  595. package/dist/bridge/serialization.d.ts +0 -83
  596. package/dist/bridge/serialization.d.ts.map +0 -1
  597. package/dist/browser-shim/worker.d.ts +0 -14
  598. package/dist/browser-shim/worker.d.ts.map +0 -1
  599. package/dist/build-9myaxf07.js +0 -70
  600. package/dist/deploy-h1wz5p7m.js +0 -86
  601. package/dist/dev-rsdssknb.js +0 -2707
  602. package/dist/doctor-v7jy4s3r.js +0 -190
  603. package/dist/durable-object-t4kbb0yt.js +0 -13
  604. package/dist/index-18hvb6gb.js +0 -194
  605. package/dist/index-1xpj0m4r.js +0 -57
  606. package/dist/index-37x76zdn.js +0 -4
  607. package/dist/index-3t6rypgc.js +0 -13
  608. package/dist/index-62b3gt2g.js +0 -12
  609. package/dist/index-67qcae0f.js +0 -183
  610. package/dist/index-9ats0s83.js +0 -70
  611. package/dist/index-a0fjkq68.js +0 -198
  612. package/dist/index-a855bdsx.js +0 -18
  613. package/dist/index-ccrh4w3t.js +0 -1133
  614. package/dist/index-d8bdkx2h.js +0 -109
  615. package/dist/index-f8qh2tyh.js +0 -2152
  616. package/dist/index-gz1gndna.js +0 -307
  617. package/dist/index-hcex3rgh.js +0 -266
  618. package/dist/index-k7r18na8.js +0 -0
  619. package/dist/index-n3np2d6t.js +0 -462
  620. package/dist/index-n7rs26ft.js +0 -77
  621. package/dist/index-npc1c8jx.js +0 -44
  622. package/dist/index-p7g30wd2.js +0 -281
  623. package/dist/index-rbht7m9r.js +0 -36
  624. package/dist/index-tfyxa77h.js +0 -850
  625. package/dist/index-tk6ej9dj.js +0 -94
  626. package/dist/index-v8vvsn9x.js +0 -169
  627. package/dist/index-z14anrqp.js +0 -226
  628. package/dist/init-f9mgmew3.js +0 -186
  629. package/dist/remote-q59qk463.js +0 -97
  630. package/dist/test/bridge-context.d.ts +0 -76
  631. package/dist/test/bridge-context.d.ts.map +0 -1
  632. package/dist/test/multi-worker-context.d.ts +0 -114
  633. package/dist/test/multi-worker-context.d.ts.map +0 -1
  634. package/dist/types-wdcpnfvy.js +0 -487
  635. package/dist/worker-entrypoint-m9th0rg0.js +0 -13
@@ -1,2707 +0,0 @@
1
- import {
2
- detectViteProject,
3
- stopSpawnedProcessTree,
4
- waitForViteReady
5
- } from "./index-18hvb6gb.js";
6
- import {
7
- findFiles
8
- } from "./index-rbht7m9r.js";
9
- import {
10
- findDurableObjectClasses
11
- } from "./index-gz1gndna.js";
12
- import {
13
- loadConfig,
14
- resolveConfigPath
15
- } from "./index-hcex3rgh.js";
16
- import {
17
- __require
18
- } from "./index-37x76zdn.js";
19
-
20
- // src/cli/commands/dev.ts
21
- import { createConsola } from "consola";
22
- import { resolve as resolve3 } from "pathe";
23
-
24
- // src/dev-server/server.ts
25
- import { dirname as dirname2, relative as relative2, resolve as resolve2 } from "pathe";
26
-
27
- // src/bundler/do-bundler.ts
28
- import { resolve, dirname, relative } from "pathe";
29
- import picomatch from "picomatch";
30
- function classToBindingName(className) {
31
- return className.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toUpperCase();
32
- }
33
- async function discoverDOs(cwd, pattern) {
34
- const fs = await import("node:fs/promises");
35
- const discovered = [];
36
- const files = await findFiles(pattern, { cwd });
37
- for (const filePath of files) {
38
- try {
39
- const code = await fs.readFile(filePath, "utf-8");
40
- const classNames = findDurableObjectClasses(code);
41
- for (const className of classNames) {
42
- discovered.push({
43
- filePath,
44
- className,
45
- bindingName: classToBindingName(className)
46
- });
47
- }
48
- } catch {}
49
- }
50
- return discovered;
51
- }
52
- function stripDecoratorSyntax(code) {
53
- let result = code;
54
- result = result.replace(/@durableObject\s*\([^)]*\)\s*\n?\s*(?=export\s+class)/g, "");
55
- result = result.replace(/import\s*\{([^}]*)\bdurableObject\b[^}]*\}\s*from\s*['"]devflare\/runtime['"]\s*;?/g, (match, imports) => {
56
- const cleanedImports = imports.split(",").map((s) => s.trim()).filter((s) => !s.startsWith("durableObject")).join(", ");
57
- if (cleanedImports.trim() === "") {
58
- return "";
59
- }
60
- return `import { ${cleanedImports} } from 'devflare/runtime'`;
61
- });
62
- return result;
63
- }
64
- async function bundleDOFile(sourcePath, className, outDir, cwd) {
65
- const { rolldown } = await import("rolldown");
66
- const fs = await import("node:fs/promises");
67
- await fs.mkdir(outDir, { recursive: true });
68
- const sourceCode = await fs.readFile(sourcePath, "utf-8");
69
- const cleanedCode = stripDecoratorSyntax(sourceCode);
70
- const entryCode = `${cleanedCode}
71
-
72
- // Default export for worker (required by Miniflare)
73
- export default {
74
- async fetch(request) {
75
- return new Response('DO Worker for ${className}', { status: 200 });
76
- }
77
- };
78
- `;
79
- const tempFilePath = resolve(outDir, `_temp_${className}.ts`);
80
- await fs.writeFile(tempFilePath, entryCode, "utf-8");
81
- const classOutDir = resolve(outDir, className);
82
- try {
83
- await fs.rm(classOutDir, { recursive: true, force: true });
84
- } catch {}
85
- await fs.mkdir(classOutDir, { recursive: true });
86
- const debugShimCode = `
87
- // Debug module shim for local development
88
- const createDebug = (namespace) => {
89
- const logger = (...args) => {
90
- if (createDebug.enabled) console.debug(\`[\${namespace}]\`, ...args)
91
- }
92
- logger.enabled = false
93
- logger.namespace = namespace
94
- logger.extend = (sub) => createDebug(\`\${namespace}:\${sub}\`)
95
- return logger
96
- }
97
- createDebug.enabled = false
98
- createDebug.formatters = {}
99
- export default createDebug
100
- `;
101
- const debugShimPath = resolve(outDir, "_debug_shim.js");
102
- await fs.writeFile(debugShimPath, debugShimCode, "utf-8");
103
- const bundle = await rolldown({
104
- input: tempFilePath,
105
- platform: "neutral",
106
- tsconfig: resolve(cwd, "tsconfig.json"),
107
- external: [
108
- /^cloudflare:/,
109
- /^node:/,
110
- "buffer",
111
- "crypto",
112
- "events",
113
- "http",
114
- "https",
115
- "net",
116
- "os",
117
- "path",
118
- "stream",
119
- "tls",
120
- "url",
121
- "util",
122
- "zlib",
123
- "fs",
124
- "child_process",
125
- "async_hooks",
126
- "querystring",
127
- "string_decoder",
128
- "assert",
129
- "dns"
130
- ],
131
- resolve: {
132
- alias: {
133
- debug: debugShimPath
134
- }
135
- }
136
- });
137
- const outFile = resolve(classOutDir, "index.js");
138
- await bundle.write({
139
- file: outFile,
140
- format: "esm",
141
- sourcemap: false,
142
- inlineDynamicImports: true
143
- });
144
- await bundle.close();
145
- try {
146
- await fs.unlink(tempFilePath);
147
- } catch {}
148
- return resolve(classOutDir, "index.js");
149
- }
150
- async function bundleAllDOs(discovered, outDir, cwd, logger) {
151
- const fs = await import("node:fs/promises");
152
- const bundles = new Map;
153
- const classes = new Map;
154
- const sourceFiles = new Map;
155
- const errors = [];
156
- for (const do_ of discovered) {
157
- const existing = sourceFiles.get(do_.filePath) || [];
158
- existing.push(do_.className);
159
- sourceFiles.set(do_.filePath, existing);
160
- }
161
- for (const do_ of discovered) {
162
- try {
163
- logger?.debug(`Bundling ${do_.className} from ${do_.filePath}`);
164
- const outFile = await bundleDOFile(do_.filePath, do_.className, outDir, cwd);
165
- bundles.set(do_.bindingName, outFile);
166
- classes.set(do_.bindingName, do_.className);
167
- logger?.debug(` → ${outFile}`);
168
- } catch (error) {
169
- const err = error instanceof Error ? error : new Error(String(error));
170
- errors.push(err);
171
- logger?.error(`Failed to bundle ${do_.className}:`, err.message);
172
- }
173
- }
174
- return { bundles, classes, sourceFiles, errors };
175
- }
176
- function createDOBundler(options) {
177
- const { cwd, pattern, outDir, logger, onRebuild } = options;
178
- let result = {
179
- bundles: new Map,
180
- classes: new Map,
181
- sourceFiles: new Map,
182
- errors: []
183
- };
184
- let watcher = null;
185
- let chokidarWatcher = null;
186
- async function build() {
187
- const discovered = await discoverDOs(cwd, pattern);
188
- if (discovered.length === 0) {
189
- logger?.debug("No DOs found matching pattern:", pattern);
190
- return result;
191
- }
192
- logger?.info(`Found ${discovered.length} Durable Object(s)`);
193
- for (const do_ of discovered) {
194
- logger?.info(` • ${do_.className} → ${do_.bindingName}`);
195
- }
196
- result = await bundleAllDOs(discovered, outDir, cwd, logger);
197
- if (result.errors.length === 0) {
198
- logger?.success(`Bundled ${result.bundles.size} DO(s) to ${outDir}`);
199
- }
200
- return result;
201
- }
202
- async function watch() {
203
- const chokidar = await import("chokidar");
204
- const files = await findFiles(pattern, { cwd });
205
- let dirsToWatch;
206
- if (files.length > 0) {
207
- dirsToWatch = [...new Set(files.map((f) => dirname(f)))];
208
- } else {
209
- const patternDir = dirname(pattern);
210
- const absolutePatternDir = resolve(cwd, patternDir === "." ? "" : patternDir) || cwd;
211
- dirsToWatch = [absolutePatternDir];
212
- logger?.debug(`No DO files yet, watching pattern directory: ${absolutePatternDir}`);
213
- }
214
- logger?.info(`Watching ${files.length} DO file(s) in ${dirsToWatch.length} director(ies)...`);
215
- const isWindows = process.platform === "win32";
216
- chokidarWatcher = chokidar.watch(dirsToWatch, {
217
- ignoreInitial: true,
218
- usePolling: isWindows,
219
- interval: isWindows ? 300 : undefined,
220
- awaitWriteFinish: {
221
- stabilityThreshold: 100,
222
- pollInterval: 50
223
- },
224
- depth: 0
225
- });
226
- const normalizePath = (p) => {
227
- let normalized = p.replace(/\\/g, "/");
228
- if (isWindows && /^[a-zA-Z]:/.test(normalized)) {
229
- normalized = normalized[0].toLowerCase() + normalized.slice(1);
230
- }
231
- return normalized;
232
- };
233
- const isMatch = picomatch(pattern, {
234
- cwd,
235
- dot: true,
236
- matchBase: false
237
- });
238
- const matchesPattern = (filePath) => {
239
- const normalizedPath = normalizePath(filePath);
240
- const relativePath = relative(normalizePath(cwd), normalizedPath);
241
- return isMatch(relativePath);
242
- };
243
- let isRebuilding = false;
244
- let pendingRebuild = null;
245
- let rebuildTimeout = null;
246
- const scheduleRebuild = (changedPath) => {
247
- if (rebuildTimeout) {
248
- clearTimeout(rebuildTimeout);
249
- }
250
- rebuildTimeout = setTimeout(() => {
251
- triggerRebuild(changedPath);
252
- }, 150);
253
- };
254
- const triggerRebuild = async (changedPath) => {
255
- if (isRebuilding) {
256
- pendingRebuild = changedPath;
257
- logger?.debug(`Rebuild already in progress, queuing: ${changedPath}`);
258
- return;
259
- }
260
- isRebuilding = true;
261
- try {
262
- logger?.info(`DO file changed: ${changedPath}`);
263
- logger?.info("Rebuilding DOs...");
264
- const startTime = Date.now();
265
- result = await build();
266
- const elapsed = Date.now() - startTime;
267
- logger?.success(`DO rebuild complete (${elapsed}ms)`);
268
- await onRebuild?.(result);
269
- } catch (error) {
270
- logger?.error("DO rebuild failed:", error);
271
- } finally {
272
- isRebuilding = false;
273
- if (pendingRebuild) {
274
- const nextPath = pendingRebuild;
275
- pendingRebuild = null;
276
- triggerRebuild(nextPath);
277
- }
278
- }
279
- };
280
- chokidarWatcher.on("change", (filePath) => {
281
- if (matchesPattern(filePath)) {
282
- logger?.debug(`File changed: ${filePath}`);
283
- scheduleRebuild(filePath);
284
- }
285
- });
286
- chokidarWatcher.on("add", (filePath) => {
287
- if (matchesPattern(filePath)) {
288
- logger?.debug(`File added: ${filePath}`);
289
- scheduleRebuild(filePath);
290
- }
291
- });
292
- chokidarWatcher.on("unlink", (filePath) => {
293
- if (matchesPattern(filePath)) {
294
- logger?.debug(`File removed: ${filePath}`);
295
- scheduleRebuild(filePath);
296
- }
297
- });
298
- chokidarWatcher.on("ready", () => {
299
- logger?.info("DO file watcher ready");
300
- });
301
- chokidarWatcher.on("error", (error) => {
302
- logger?.error("DO file watcher error:", error);
303
- });
304
- }
305
- async function close() {
306
- if (watcher) {
307
- await watcher.close();
308
- watcher = null;
309
- }
310
- if (chokidarWatcher) {
311
- await chokidarWatcher.close();
312
- chokidarWatcher = null;
313
- }
314
- }
315
- function getResult() {
316
- return result;
317
- }
318
- return {
319
- build,
320
- watch,
321
- close,
322
- getResult
323
- };
324
- }
325
- // src/browser-shim/server.ts
326
- import { homedir } from "node:os";
327
- import { join } from "node:path";
328
- import { existsSync } from "node:fs";
329
- import { createServer } from "node:http";
330
- import puppeteerCore from "puppeteer-core";
331
- import {
332
- install,
333
- resolveBuildId,
334
- detectBrowserPlatform,
335
- Browser as BrowserType
336
- } from "@puppeteer/browsers";
337
- var sessions = new Map;
338
- var history = [];
339
- var cachedExecutablePath = null;
340
- async function ensureChrome(cacheDir, logger) {
341
- if (cachedExecutablePath && existsSync(cachedExecutablePath)) {
342
- return cachedExecutablePath;
343
- }
344
- const platform = detectBrowserPlatform();
345
- if (!platform) {
346
- throw new Error("Could not detect browser platform");
347
- }
348
- const buildId = await resolveBuildId(BrowserType.CHROMEHEADLESSSHELL, platform, "stable");
349
- logger?.debug(`[BrowserShim] Resolved Chrome Headless Shell build: ${buildId}`);
350
- const installedBrowser = await install({
351
- browser: BrowserType.CHROMEHEADLESSSHELL,
352
- buildId,
353
- cacheDir,
354
- downloadProgressCallback: (downloadedBytes, totalBytes) => {
355
- if (totalBytes > 0) {
356
- const percent = Math.round(downloadedBytes / totalBytes * 100);
357
- if (percent % 20 === 0) {
358
- logger?.info(`[BrowserShim] Downloading Chrome... ${percent}%`);
359
- }
360
- }
361
- }
362
- });
363
- cachedExecutablePath = installedBrowser.executablePath;
364
- logger?.success(`[BrowserShim] Chrome ready: ${installedBrowser.executablePath}`);
365
- return installedBrowser.executablePath;
366
- }
367
- function createBrowserShim(options = {}) {
368
- const {
369
- port = 8788,
370
- host = "127.0.0.1",
371
- logger,
372
- verbose = false,
373
- keepAlive = 60000,
374
- cacheDir = join(homedir(), ".devflare", "chrome")
375
- } = options;
376
- let server = null;
377
- let executablePath = null;
378
- let WebSocketServerClass = null;
379
- let WebSocketClass = null;
380
- async function acquireSession(acquireOptions) {
381
- if (!executablePath) {
382
- throw new Error("Chrome not initialized");
383
- }
384
- const browser = await puppeteerCore.launch({
385
- executablePath,
386
- headless: true,
387
- protocolTimeout: 120000,
388
- args: [
389
- "--no-sandbox",
390
- "--disable-setuid-sandbox",
391
- "--disable-dev-shm-usage",
392
- "--disable-gpu",
393
- "--disable-software-rasterizer",
394
- "--disable-extensions",
395
- "--disable-background-networking",
396
- "--disable-background-timer-throttling",
397
- "--disable-backgrounding-occluded-windows",
398
- "--disable-renderer-backgrounding",
399
- "--disable-features=TranslateUI",
400
- "--disable-ipc-flooding-protection",
401
- "--disable-default-apps",
402
- "--mute-audio",
403
- "--js-flags=--max-old-space-size=4096"
404
- ]
405
- });
406
- const wsEndpoint = browser.wsEndpoint();
407
- const sessionId = crypto.randomUUID();
408
- const session = {
409
- sessionId,
410
- browser,
411
- wsEndpoint,
412
- startTime: Date.now()
413
- };
414
- sessions.set(sessionId, session);
415
- const timeout = acquireOptions?.keep_alive ?? keepAlive;
416
- if (timeout > 0) {
417
- session.idleTimeout = setTimeout(async () => {
418
- const s = sessions.get(sessionId);
419
- if (s && !s.connectionId) {
420
- await closeSession(sessionId, 2, "BrowserIdle");
421
- }
422
- }, timeout);
423
- }
424
- if (verbose) {
425
- logger?.debug(`[BrowserShim] Acquired session ${sessionId}`);
426
- }
427
- return { sessionId };
428
- }
429
- async function closeSession(sessionId, closeReason = 1, closeReasonText = "NormalClosure") {
430
- const session = sessions.get(sessionId);
431
- if (!session)
432
- return;
433
- if (session.idleTimeout) {
434
- clearTimeout(session.idleTimeout);
435
- }
436
- try {
437
- await session.browser.close();
438
- } catch {}
439
- sessions.delete(sessionId);
440
- history.unshift({
441
- sessionId,
442
- startTime: session.startTime,
443
- endTime: Date.now(),
444
- closeReason,
445
- closeReasonText
446
- });
447
- if (history.length > 100) {
448
- history.pop();
449
- }
450
- if (verbose) {
451
- logger?.debug(`[BrowserShim] Closed session ${sessionId}: ${closeReasonText}`);
452
- }
453
- }
454
- async function handleRequest(req, res) {
455
- const url = new URL(req.url || "/", `http://${host}:${port}`);
456
- const method = req.method || "GET";
457
- logger?.debug(`[BrowserShim] ${method} ${url.pathname}${url.search ? url.search : ""}`);
458
- res.setHeader("Access-Control-Allow-Origin", "*");
459
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
460
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
461
- if (method === "OPTIONS") {
462
- res.writeHead(204);
463
- res.end();
464
- return;
465
- }
466
- if (url.pathname === "/v1/acquire" && (method === "POST" || method === "GET")) {
467
- try {
468
- let acquireOptions = {};
469
- if (method === "GET") {
470
- const keepAlive2 = url.searchParams.get("keep_alive");
471
- if (keepAlive2) {
472
- acquireOptions.keep_alive = parseInt(keepAlive2, 10);
473
- }
474
- } else {
475
- try {
476
- const body = await readBody(req);
477
- acquireOptions = JSON.parse(body);
478
- } catch {}
479
- }
480
- const result = await acquireSession(acquireOptions);
481
- sendJson(res, 200, result);
482
- } catch (error) {
483
- const msg = error instanceof Error ? error.message : "Failed to acquire browser";
484
- logger?.error(`[BrowserShim] Acquire failed: ${msg}`);
485
- sendJson(res, 500, { error: msg });
486
- }
487
- return;
488
- }
489
- if (url.pathname === "/v1/sessions" && method === "GET") {
490
- const activeSessions = Array.from(sessions.values()).map((s) => ({
491
- sessionId: s.sessionId,
492
- startTime: s.startTime,
493
- connectionId: s.connectionId,
494
- connectionStartTime: s.connectionStartTime
495
- }));
496
- sendJson(res, 200, activeSessions);
497
- return;
498
- }
499
- if (url.pathname === "/v1/history" && method === "GET") {
500
- sendJson(res, 200, history.slice(0, 50));
501
- return;
502
- }
503
- if (url.pathname === "/v1/limits" && method === "GET") {
504
- sendJson(res, 200, {
505
- activeSessions: Array.from(sessions.keys()).map((id) => ({ id })),
506
- allowedBrowserAcquisitions: 10,
507
- maxConcurrentSessions: 10,
508
- timeUntilNextAllowedBrowserAcquisition: 0
509
- });
510
- return;
511
- }
512
- if (url.pathname.startsWith("/v1/session/") && method === "GET") {
513
- const sessionId = url.pathname.slice("/v1/session/".length);
514
- const session = sessions.get(sessionId);
515
- if (!session) {
516
- sendJson(res, 404, { error: "Session not found" });
517
- return;
518
- }
519
- sendJson(res, 200, {
520
- sessionId: session.sessionId,
521
- wsEndpoint: session.wsEndpoint,
522
- startTime: session.startTime,
523
- connectionId: session.connectionId,
524
- connectionStartTime: session.connectionStartTime
525
- });
526
- return;
527
- }
528
- if (url.pathname === "/_devflare/browser/health") {
529
- sendJson(res, 200, {
530
- ok: true,
531
- activeSessions: sessions.size,
532
- historySize: history.length,
533
- executablePath
534
- });
535
- return;
536
- }
537
- if (url.pathname === "/v1/connectDevtools") {
538
- res.writeHead(426, { "Content-Type": "text/plain" });
539
- res.end("WebSocket upgrade required");
540
- return;
541
- }
542
- res.writeHead(404, { "Content-Type": "text/plain" });
543
- res.end("Not found");
544
- }
545
- function readBody(req) {
546
- return new Promise((resolve2, reject) => {
547
- const chunks = [];
548
- req.on("data", (chunk) => chunks.push(chunk));
549
- req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
550
- req.on("error", reject);
551
- });
552
- }
553
- function sendJson(res, status, data) {
554
- const body = JSON.stringify(data);
555
- res.writeHead(status, {
556
- "Content-Type": "application/json",
557
- "Content-Length": Buffer.byteLength(body)
558
- });
559
- res.end(body);
560
- }
561
- async function start() {
562
- logger?.info("[BrowserShim] Ensuring Chrome Headless Shell is available...");
563
- executablePath = await ensureChrome(cacheDir, logger);
564
- try {
565
- const wsModule = await import("ws");
566
- WebSocketServerClass = wsModule.WebSocketServer || wsModule.default?.WebSocketServer;
567
- WebSocketClass = wsModule.WebSocket || wsModule.default?.WebSocket || wsModule.default;
568
- } catch {
569
- logger?.warn("[BrowserShim] ws package not found, WebSocket proxy disabled");
570
- logger?.warn("[BrowserShim] Install with: npm install ws");
571
- }
572
- server = createServer((req, res) => {
573
- handleRequest(req, res).catch((error) => {
574
- logger?.error("[BrowserShim] Request error:", error);
575
- res.writeHead(500);
576
- res.end("Internal server error");
577
- });
578
- });
579
- if (WebSocketServerClass) {
580
- const wss = new WebSocketServerClass({ noServer: true });
581
- server.on("upgrade", (request, socket, head) => {
582
- const url = new URL(request.url || "/", `http://${host}:${port}`);
583
- if (url.pathname !== "/v1/connectDevtools") {
584
- socket.destroy();
585
- return;
586
- }
587
- const sessionId = url.searchParams.get("browser_session");
588
- if (!sessionId) {
589
- socket.write(`HTTP/1.1 400 Bad Request\r
590
- \r
591
- `);
592
- socket.destroy();
593
- return;
594
- }
595
- const session = sessions.get(sessionId);
596
- if (!session) {
597
- socket.write(`HTTP/1.1 404 Not Found\r
598
- \r
599
- `);
600
- socket.destroy();
601
- return;
602
- }
603
- const connectionId = crypto.randomUUID();
604
- session.connectionId = connectionId;
605
- session.connectionStartTime = Date.now();
606
- if (session.idleTimeout) {
607
- clearTimeout(session.idleTimeout);
608
- session.idleTimeout = undefined;
609
- }
610
- wss.handleUpgrade(request, socket, head, (ws) => {
611
- if (verbose) {
612
- logger?.debug(`[BrowserShim] WebSocket connected for session ${sessionId}`);
613
- }
614
- const chromeWs = new WebSocketClass(session.wsEndpoint);
615
- let chromeConnected = false;
616
- const connectTimeout = setTimeout(() => {
617
- if (!chromeConnected) {
618
- logger?.error("[BrowserShim] Chrome connection timeout");
619
- try {
620
- ws.close(1011, "Chrome connection timeout");
621
- chromeWs.close();
622
- } catch {}
623
- closeSession(sessionId, 5, "ChromeConnectionTimeout").catch(() => {});
624
- }
625
- }, 1e4);
626
- chromeWs.on("open", () => {
627
- chromeConnected = true;
628
- clearTimeout(connectTimeout);
629
- if (verbose) {
630
- logger?.debug("[BrowserShim] Connected to Chrome DevTools");
631
- }
632
- });
633
- chromeWs.on("message", (data) => {
634
- if (ws.readyState === 1) {
635
- ws.send(data);
636
- }
637
- });
638
- chromeWs.on("close", (code, reason) => {
639
- if (verbose) {
640
- logger?.debug(`[BrowserShim] Chrome WS closed: ${code}`);
641
- }
642
- const validCode = typeof code === "number" && code >= 1000 && code <= 4999 ? code : 1000;
643
- try {
644
- ws.close(validCode, reason?.toString?.() || "");
645
- } catch {}
646
- closeSession(sessionId, 2, "ChromeDisconnected").catch((err) => {
647
- logger?.error("[BrowserShim] Error closing session after Chrome disconnect:", err);
648
- });
649
- });
650
- chromeWs.on("error", (error) => {
651
- logger?.error("[BrowserShim] Chrome WS error:", error.message);
652
- try {
653
- ws.close(1011, "Chrome WebSocket error");
654
- } catch {}
655
- closeSession(sessionId, 4, "ChromeError").catch((err) => {
656
- logger?.error("[BrowserShim] Error closing session after Chrome error:", err);
657
- });
658
- });
659
- ws.on("message", (data) => {
660
- if (chromeWs.readyState === 1) {
661
- chromeWs.send(data);
662
- }
663
- });
664
- ws.on("close", (code, reason) => {
665
- if (verbose) {
666
- logger?.debug(`[BrowserShim] Client WS closed for session ${sessionId}`);
667
- }
668
- const validCode = typeof code === "number" && code >= 1000 && code <= 4999 ? code : 1000;
669
- try {
670
- chromeWs.close(validCode, reason?.toString?.() || "");
671
- } catch {}
672
- const s = sessions.get(sessionId);
673
- if (s && s.connectionId === connectionId) {
674
- s.connectionId = undefined;
675
- s.connectionStartTime = undefined;
676
- closeSession(sessionId, 1, "ClientDisconnected").catch((err) => {
677
- logger?.error("[BrowserShim] Error closing session after disconnect:", err);
678
- });
679
- }
680
- });
681
- ws.on("error", (error) => {
682
- logger?.error("[BrowserShim] Client WS error:", error.message);
683
- try {
684
- chromeWs.close();
685
- } catch {}
686
- });
687
- });
688
- });
689
- }
690
- await new Promise((resolve2, reject) => {
691
- server.on("error", reject);
692
- server.listen(port, host, () => {
693
- resolve2();
694
- });
695
- });
696
- logger?.success(`Browser shim server ready on http://${host}:${port}`);
697
- }
698
- async function stop() {
699
- for (const sessionId of sessions.keys()) {
700
- await closeSession(sessionId, 3, "ServerShutdown");
701
- }
702
- if (server) {
703
- await new Promise((resolve2) => {
704
- server.close(() => resolve2());
705
- });
706
- server = null;
707
- }
708
- logger?.info("Browser shim server stopped");
709
- }
710
- function getUrl() {
711
- return `http://${host}:${port}`;
712
- }
713
- return {
714
- start,
715
- stop,
716
- getUrl
717
- };
718
- }
719
- // src/browser-shim/binding-worker.ts
720
- var MAX_CHUNK_SIZE = 1048575;
721
- function getBrowserBindingScript(browserShimUrl, debug = false) {
722
- const safeUrl = JSON.stringify(browserShimUrl);
723
- return `
724
- // Browser Binding Worker — Proxies puppeteer requests to external browser shim
725
- // Handles WebSocket upgrades using WebSocketPair for @cloudflare/puppeteer compatibility
726
-
727
- const BROWSER_SHIM_URL = ${safeUrl}
728
- const MAX_CHUNK_SIZE = ${MAX_CHUNK_SIZE}
729
- const DEBUG = ${debug}
730
- const log = (...args) => DEBUG && console.log('[BrowserBinding]', ...args)
731
-
732
- export default {
733
- async fetch(request, env, ctx) {
734
- const url = new URL(request.url)
735
- const upgradeHeader = request.headers.get('Upgrade')
736
- const isWebSocket = upgradeHeader && upgradeHeader.toLowerCase() === 'websocket'
737
-
738
- log('Request:', url.pathname, isWebSocket ? '(WebSocket)' : '(HTTP)')
739
-
740
- // Handle WebSocket upgrade for DevTools connection
741
- if (url.pathname === '/v1/connectDevtools' && isWebSocket) {
742
- return handleDevToolsWebSocket(request, url)
743
- }
744
-
745
- // Proxy all other requests to the browser shim server
746
- return proxyToBrowserShim(request, url)
747
- }
748
- }
749
-
750
- // Proxy HTTP requests to the external browser shim server
751
- async function proxyToBrowserShim(request, url) {
752
- const shimUrl = new URL(url.pathname + url.search, BROWSER_SHIM_URL)
753
-
754
- log('Proxying to:', shimUrl.toString())
755
-
756
- const response = await fetch(shimUrl.toString(), {
757
- method: request.method,
758
- headers: request.headers,
759
- body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : undefined
760
- })
761
-
762
- log('Response:', response.status)
763
-
764
- // Return the response as-is
765
- return new Response(response.body, {
766
- status: response.status,
767
- statusText: response.statusText,
768
- headers: response.headers
769
- })
770
- }
771
-
772
- // Validate WebSocket close code to be in valid range
773
- function validateCloseCode(code) {
774
- if (typeof code !== 'number' || isNaN(code)) return 1000
775
- if (code < 1000 || code > 4999) return 1000
776
- return code
777
- }
778
-
779
- // Split a message into chunks following @cloudflare/puppeteer protocol
780
- // First chunk has 4-byte LE length header, subsequent chunks are raw payload
781
- function messageToChunks(message) {
782
- const data = typeof message === 'string'
783
- ? new TextEncoder().encode(message)
784
- : new Uint8Array(message)
785
-
786
- const chunks = []
787
- const totalLength = data.length
788
- let offset = 0
789
- let isFirst = true
790
-
791
- while (offset < totalLength) {
792
- const remaining = totalLength - offset
793
- let chunkSize
794
-
795
- if (isFirst) {
796
- // First chunk: 4-byte header + payload
797
- chunkSize = Math.min(remaining, MAX_CHUNK_SIZE - 4)
798
- const chunk = new Uint8Array(chunkSize + 4)
799
- new DataView(chunk.buffer).setUint32(0, totalLength, true) // little-endian
800
- chunk.set(data.subarray(offset, offset + chunkSize), 4)
801
- chunks.push(chunk)
802
- isFirst = false
803
- } else {
804
- // Subsequent chunks: raw payload only
805
- chunkSize = Math.min(remaining, MAX_CHUNK_SIZE)
806
- const chunk = data.subarray(offset, offset + chunkSize)
807
- chunks.push(chunk)
808
- }
809
-
810
- offset += chunkSize
811
- }
812
-
813
- return chunks
814
- }
815
-
816
- // Reassemble chunks back into a complete message
817
- // Returns null if more chunks are needed
818
- function chunksToMessage(chunks) {
819
- if (chunks.length === 0) return null
820
-
821
- // First chunk must have 4-byte header
822
- const firstChunk = chunks[0]
823
- if (firstChunk.length < 4) return null
824
-
825
- const expectedLength = new DataView(firstChunk.buffer, firstChunk.byteOffset).getUint32(0, true)
826
-
827
- // Calculate total received payload
828
- let totalReceived = firstChunk.length - 4 // first chunk payload (minus header)
829
- for (let i = 1; i < chunks.length; i++) {
830
- totalReceived += chunks[i].length
831
- }
832
-
833
- if (totalReceived < expectedLength) {
834
- return null // Need more chunks
835
- }
836
-
837
- // Reassemble the message
838
- const assembled = new Uint8Array(expectedLength)
839
- let offset = 0
840
-
841
- // Copy first chunk payload (skip 4-byte header)
842
- const firstPayload = firstChunk.subarray(4)
843
- assembled.set(firstPayload, offset)
844
- offset += firstPayload.length
845
-
846
- // Copy remaining chunks
847
- for (let i = 1; i < chunks.length; i++) {
848
- const chunk = chunks[i]
849
- const toCopy = Math.min(chunk.length, expectedLength - offset)
850
- assembled.set(chunk.subarray(0, toCopy), offset)
851
- offset += toCopy
852
- }
853
-
854
- return new TextDecoder().decode(assembled)
855
- }
856
-
857
- // Handle WebSocket upgrade for DevTools connection
858
- // Creates a WebSocketPair and proxies to Chrome's DevTools WebSocket
859
- async function handleDevToolsWebSocket(request, url) {
860
- const sessionId = url.searchParams.get('browser_session')
861
- if (!sessionId) {
862
- return new Response('browser_session parameter required', { status: 400 })
863
- }
864
-
865
- log('DevTools WebSocket request for session:', sessionId)
866
-
867
- // Get session info from browser shim (includes Chrome's wsEndpoint)
868
- const sessionUrl = new URL('/v1/session/' + sessionId, BROWSER_SHIM_URL)
869
-
870
- // Add timeout for session fetch
871
- const controller = new AbortController()
872
- const timeout = setTimeout(() => controller.abort(), 5000)
873
-
874
- let sessionRes
875
- try {
876
- sessionRes = await fetch(sessionUrl.toString(), { signal: controller.signal })
877
- } catch (e) {
878
- DEBUG && console.error('[BrowserBinding] Session fetch timeout or error:', e.message)
879
- return new Response('Session fetch timeout', { status: 504 })
880
- } finally {
881
- clearTimeout(timeout)
882
- }
883
-
884
- if (!sessionRes.ok) {
885
- DEBUG && console.error('[BrowserBinding] Session not found:', sessionId)
886
- return new Response('Session not found', { status: 404 })
887
- }
888
-
889
- const sessionInfo = await sessionRes.json()
890
- const wsEndpoint = sessionInfo.wsEndpoint
891
-
892
- if (!wsEndpoint) {
893
- DEBUG && console.error('[BrowserBinding] No wsEndpoint in session info')
894
- return new Response('No wsEndpoint for session', { status: 500 })
895
- }
896
-
897
- log('Connecting to Chrome DevTools:', wsEndpoint)
898
-
899
- // Connect to Chrome's DevTools WebSocket
900
- // Chrome uses ws:// but fetch expects http:// for WebSocket upgrade
901
- const chromeUrl = wsEndpoint.replace('ws://', 'http://').replace('wss://', 'https://')
902
-
903
- const chromeRes = await fetch(chromeUrl, {
904
- headers: { Upgrade: 'websocket' }
905
- })
906
-
907
- if (!chromeRes.webSocket) {
908
- DEBUG && console.error('[BrowserBinding] Failed to connect to Chrome DevTools')
909
- return new Response('Failed to connect to Chrome DevTools', { status: 502 })
910
- }
911
-
912
- const chromeWs = chromeRes.webSocket
913
- chromeWs.accept()
914
-
915
- log('Connected to Chrome DevTools')
916
-
917
- // Create WebSocketPair for client connection
918
- const { 0: client, 1: server } = new WebSocketPair()
919
- server.accept()
920
-
921
- // Chunk buffer for reassembling multi-chunk messages from puppeteer
922
- let chunks = []
923
- const MAX_BUFFER_SIZE = 50 * 1024 * 1024 // 50MB max buffer
924
- let bufferSize = 0
925
-
926
- // Proxy messages from client (puppeteer) to Chrome
927
- // Handle multi-chunk framing protocol
928
- server.addEventListener('message', (event) => {
929
- // Keep-alive ping from puppeteer
930
- if (event.data === 'ping') {
931
- return
932
- }
933
-
934
- // Handle binary data (chunked protocol)
935
- if (event.data instanceof ArrayBuffer) {
936
- const chunk = new Uint8Array(event.data)
937
- bufferSize += chunk.length
938
-
939
- // Prevent unbounded buffering
940
- if (bufferSize > MAX_BUFFER_SIZE) {
941
- DEBUG && console.error('[BrowserBinding] Buffer overflow, closing connection')
942
- server.close(1009, 'Message too big')
943
- chromeWs.close(1009, 'Message too big')
944
- return
945
- }
946
-
947
- chunks.push(chunk)
948
-
949
- // Try to reassemble complete message
950
- const message = chunksToMessage(chunks)
951
- if (message !== null) {
952
- // Send complete message to Chrome
953
- if (chromeWs.readyState === 1) { // OPEN
954
- chromeWs.send(message)
955
- }
956
- // Clear buffer
957
- chunks = []
958
- bufferSize = 0
959
- }
960
- } else if (typeof event.data === 'string') {
961
- // Shouldn't happen in normal protocol, but handle it
962
- if (chromeWs.readyState === 1) {
963
- chromeWs.send(event.data)
964
- }
965
- }
966
- })
967
-
968
- // Proxy messages from Chrome to client (puppeteer)
969
- // Split into chunks following the multi-chunk protocol
970
- chromeWs.addEventListener('message', (event) => {
971
- if (server.readyState !== 1) return // Not OPEN
972
-
973
- // Split message into chunks
974
- const outChunks = messageToChunks(event.data)
975
- for (const chunk of outChunks) {
976
- server.send(chunk)
977
- }
978
- })
979
-
980
- // Handle close events with validated codes
981
- server.addEventListener('close', (event) => {
982
- log('Client WebSocket closed:', event.code)
983
- const code = validateCloseCode(event.code)
984
- try {
985
- if (chromeWs.readyState === 1 || chromeWs.readyState === 0) {
986
- chromeWs.close(code, event.reason || '')
987
- }
988
- } catch {}
989
- })
990
-
991
- chromeWs.addEventListener('close', (event) => {
992
- log('Chrome WebSocket closed:', event.code)
993
- const code = validateCloseCode(event.code)
994
- try {
995
- if (server.readyState === 1 || server.readyState === 0) {
996
- server.close(code, event.reason || '')
997
- }
998
- } catch {}
999
- })
1000
-
1001
- // Handle errors
1002
- server.addEventListener('error', (event) => {
1003
- DEBUG && console.error('[BrowserBinding] Client WebSocket error')
1004
- try { chromeWs.close(1011, 'Client error') } catch {}
1005
- })
1006
-
1007
- chromeWs.addEventListener('error', (event) => {
1008
- DEBUG && console.error('[BrowserBinding] Chrome WebSocket error')
1009
- try { server.close(1011, 'Chrome error') } catch {}
1010
- })
1011
-
1012
- log('WebSocket proxy established')
1013
-
1014
- // Return Cloudflare-style WebSocket response
1015
- return new Response(null, {
1016
- status: 101,
1017
- webSocket: client
1018
- })
1019
- }
1020
- `;
1021
- }
1022
-
1023
- // src/cli/wrangler-auth.ts
1024
- import { exec } from "node:child_process";
1025
- import { promisify } from "node:util";
1026
- var execAsync = promisify(exec);
1027
- function detectRemoteBindings(config) {
1028
- const remoteBindings = [];
1029
- const bindings = config.bindings;
1030
- if (!bindings)
1031
- return remoteBindings;
1032
- if (bindings.ai) {
1033
- remoteBindings.push(`AI (binding: ${bindings.ai.binding})`);
1034
- }
1035
- if (bindings.vectorize) {
1036
- for (const [name] of Object.entries(bindings.vectorize)) {
1037
- remoteBindings.push(`Vectorize (binding: ${name})`);
1038
- }
1039
- }
1040
- return remoteBindings;
1041
- }
1042
- async function checkWranglerAuth() {
1043
- try {
1044
- const { stdout, stderr } = await execAsync("bunx wrangler whoami", {
1045
- timeout: 15000
1046
- });
1047
- const output = stdout + stderr;
1048
- if (output.includes("not authenticated") || output.includes("Not logged in") || output.includes("wrangler login")) {
1049
- return {
1050
- loggedIn: false,
1051
- error: "Not logged in to Wrangler"
1052
- };
1053
- }
1054
- const emailMatch = output.match(/email[:\s]+([^\s!]+)/i);
1055
- const accountMatch = output.match(/Account\s+ID[:\s]+([a-f0-9]+)/i);
1056
- return {
1057
- loggedIn: true,
1058
- email: emailMatch?.[1],
1059
- accountId: accountMatch?.[1]
1060
- };
1061
- } catch (error) {
1062
- const msg = error instanceof Error ? error.message : String(error);
1063
- if (msg.includes("ENOENT") || msg.includes("not found")) {
1064
- return {
1065
- loggedIn: false,
1066
- error: "Wrangler not installed. Run: npm install -g wrangler"
1067
- };
1068
- }
1069
- return {
1070
- loggedIn: false,
1071
- error: msg
1072
- };
1073
- }
1074
- }
1075
- async function checkRemoteBindingRequirements(config) {
1076
- const remoteBindings = detectRemoteBindings(config);
1077
- if (remoteBindings.length === 0) {
1078
- return {
1079
- hasRemoteBindings: false,
1080
- remoteBindings: [],
1081
- missingAccountId: false,
1082
- notLoggedIn: false
1083
- };
1084
- }
1085
- const missingAccountId = !config.accountId;
1086
- const authStatus = await checkWranglerAuth();
1087
- const notLoggedIn = !authStatus.loggedIn;
1088
- return {
1089
- hasRemoteBindings: true,
1090
- remoteBindings,
1091
- missingAccountId,
1092
- notLoggedIn
1093
- };
1094
- }
1095
-
1096
- // src/dev-server/server.ts
1097
- var DEFAULT_FETCH_ENTRY_FILES = [
1098
- "src/fetch.ts",
1099
- "src/fetch.js",
1100
- "src/fetch.mts",
1101
- "src/fetch.mjs"
1102
- ];
1103
- var DEFAULT_QUEUE_ENTRY_FILES = [
1104
- "src/queue.ts",
1105
- "src/queue.js",
1106
- "src/queue.mts",
1107
- "src/queue.mjs"
1108
- ];
1109
- var DEFAULT_SCHEDULED_ENTRY_FILES = [
1110
- "src/scheduled.ts",
1111
- "src/scheduled.js",
1112
- "src/scheduled.mts",
1113
- "src/scheduled.mjs"
1114
- ];
1115
- var DEFAULT_EMAIL_ENTRY_FILES = [
1116
- "src/email.ts",
1117
- "src/email.js",
1118
- "src/email.mts",
1119
- "src/email.mjs"
1120
- ];
1121
- var INTERNAL_APP_SERVICE_BINDING = "__DEVFLARE_APP";
1122
- async function resolveWorkerHandlerPath(cwd, configuredPath, defaultEntries) {
1123
- if (configuredPath === false) {
1124
- return null;
1125
- }
1126
- const fs = await import("node:fs/promises");
1127
- const candidates = new Set;
1128
- if (typeof configuredPath === "string" && configuredPath) {
1129
- candidates.add(configuredPath);
1130
- }
1131
- for (const defaultEntry of defaultEntries) {
1132
- candidates.add(defaultEntry);
1133
- }
1134
- for (const candidate of candidates) {
1135
- const absolutePath = resolve2(cwd, candidate);
1136
- try {
1137
- await fs.access(absolutePath);
1138
- return absolutePath;
1139
- } catch {
1140
- continue;
1141
- }
1142
- }
1143
- return null;
1144
- }
1145
- async function resolveMainWorkerSurfacePaths(cwd, config) {
1146
- return {
1147
- fetch: await resolveWorkerHandlerPath(cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES),
1148
- queue: await resolveWorkerHandlerPath(cwd, config.files?.queue, DEFAULT_QUEUE_ENTRY_FILES),
1149
- scheduled: await resolveWorkerHandlerPath(cwd, config.files?.scheduled, DEFAULT_SCHEDULED_ENTRY_FILES),
1150
- email: await resolveWorkerHandlerPath(cwd, config.files?.email, DEFAULT_EMAIL_ENTRY_FILES)
1151
- };
1152
- }
1153
- function getFirstWorkerSurfacePath(surfacePaths) {
1154
- return surfacePaths.fetch ?? surfacePaths.queue ?? surfacePaths.scheduled ?? surfacePaths.email;
1155
- }
1156
- function hasWorkerSurfacePaths(surfacePaths) {
1157
- return Object.values(surfacePaths).some((surfacePath) => typeof surfacePath === "string" && surfacePath.length > 0);
1158
- }
1159
- function toImportSpecifier(fromFilePath, toFilePath) {
1160
- const specifier = relative2(dirname2(fromFilePath), toFilePath).replace(/\\/g, "/");
1161
- return specifier.startsWith(".") ? specifier : `./${specifier}`;
1162
- }
1163
- function getMainWorkerEntryScript(surfaceImportPaths) {
1164
- const importLines = [`import { runWithContext } from 'devflare/runtime'`];
1165
- const moduleFallbackLines = [];
1166
- const registerSurfaceModule = (identifier, importPath) => {
1167
- if (importPath) {
1168
- importLines.push(`import * as ${identifier} from '${importPath}'`);
1169
- return;
1170
- }
1171
- moduleFallbackLines.push(`const ${identifier} = {}`);
1172
- };
1173
- registerSurfaceModule("__devflareFetchModule", surfaceImportPaths.fetch);
1174
- registerSurfaceModule("__devflareQueueModule", surfaceImportPaths.queue);
1175
- registerSurfaceModule("__devflareScheduledModule", surfaceImportPaths.scheduled);
1176
- registerSurfaceModule("__devflareEmailModule", surfaceImportPaths.email);
1177
- return `
1178
- ${importLines.join(`
1179
- `)}
1180
- ${moduleFallbackLines.join(`
1181
- `)}
1182
-
1183
- const __devflareResolveHandler = (module, namedExport) => {
1184
- const defaultExport = module.default
1185
-
1186
- if (typeof defaultExport === 'function') {
1187
- return defaultExport
1188
- }
1189
-
1190
- if (defaultExport && typeof defaultExport[namedExport] === 'function') {
1191
- return defaultExport[namedExport].bind(defaultExport)
1192
- }
1193
-
1194
- if (typeof module[namedExport] === 'function') {
1195
- return module[namedExport]
1196
- }
1197
-
1198
- return null
1199
- }
1200
-
1201
- const __devflareFetchHandler = __devflareResolveHandler(__devflareFetchModule, 'fetch')
1202
- const __devflareQueueHandler = __devflareResolveHandler(__devflareQueueModule, 'queue')
1203
- const __devflareScheduledHandler = __devflareResolveHandler(__devflareScheduledModule, 'scheduled')
1204
- const __devflareEmailHandler = __devflareResolveHandler(__devflareEmailModule, 'email')
1205
-
1206
- function __devflareCreateEmailHeaders(rawBody) {
1207
- const headers = new Headers()
1208
- const lines = rawBody.split(/\\r?\\n/)
1209
-
1210
- for (const line of lines) {
1211
- if (line.trim() === '') {
1212
- break
1213
- }
1214
-
1215
- const colonIndex = line.indexOf(':')
1216
- if (colonIndex <= 0) {
1217
- continue
1218
- }
1219
-
1220
- headers.append(line.slice(0, colonIndex).trim(), line.slice(colonIndex + 1).trim())
1221
- }
1222
-
1223
- return headers
1224
- }
1225
-
1226
- function __devflareCreateEmailRawStream(rawBody) {
1227
- return new ReadableStream({
1228
- start(controller) {
1229
- controller.enqueue(new TextEncoder().encode(rawBody))
1230
- controller.close()
1231
- }
1232
- })
1233
- }
1234
-
1235
- async function __devflareHandleInternalEmail(request, env, ctx) {
1236
- if (!__devflareEmailHandler) {
1237
- return new Response('Email handler not configured', { status: 501 })
1238
- }
1239
-
1240
- const from = request.headers.get('x-devflare-email-from') || 'unknown@example.com'
1241
- const to = request.headers.get('x-devflare-email-to') || 'worker@example.com'
1242
- const rawBody = await request.text()
1243
- const emailMessage = {
1244
- from,
1245
- to,
1246
- headers: __devflareCreateEmailHeaders(rawBody),
1247
- raw: __devflareCreateEmailRawStream(rawBody),
1248
- rawSize: rawBody.length,
1249
- setReject(reason) {
1250
- console.warn('[Devflare email rejected]', reason)
1251
- },
1252
- async forward(rcptTo) {
1253
- console.log('[Devflare email forwarded]', rcptTo)
1254
- return Promise.resolve()
1255
- },
1256
- async reply(message) {
1257
- console.log('[Devflare email reply sent]', message?.from)
1258
- return Promise.resolve()
1259
- }
1260
- }
1261
-
1262
- await runWithContext(
1263
- env,
1264
- ctx,
1265
- null,
1266
- () => __devflareEmailHandler(emailMessage, env, ctx),
1267
- 'email'
1268
- )
1269
-
1270
- return new Response(JSON.stringify({ ok: true, from, to }), {
1271
- headers: { 'Content-Type': 'application/json' }
1272
- })
1273
- }
1274
-
1275
- export default {
1276
- async fetch(request, env, ctx) {
1277
- const url = new URL(request.url)
1278
-
1279
- if (
1280
- request.headers.get('x-devflare-event') === 'email' &&
1281
- url.pathname === '/_devflare/internal/email'
1282
- ) {
1283
- return __devflareHandleInternalEmail(request, env, ctx)
1284
- }
1285
-
1286
- if (!__devflareFetchHandler) {
1287
- return new Response('Fetch handler not configured', { status: 404 })
1288
- }
1289
-
1290
- return runWithContext(
1291
- env,
1292
- ctx,
1293
- request,
1294
- () => __devflareFetchHandler(request, env, ctx),
1295
- 'fetch'
1296
- )
1297
- },
1298
- ...(__devflareQueueHandler
1299
- ? {
1300
- async queue(batch, env, ctx) {
1301
- return runWithContext(
1302
- env,
1303
- ctx,
1304
- null,
1305
- () => __devflareQueueHandler(batch, env, ctx),
1306
- 'queue'
1307
- )
1308
- }
1309
- }
1310
- : {}),
1311
- ...(__devflareScheduledHandler
1312
- ? {
1313
- async scheduled(controller, env, ctx) {
1314
- return runWithContext(
1315
- env,
1316
- ctx,
1317
- null,
1318
- () => __devflareScheduledHandler(controller, env, ctx),
1319
- 'scheduled'
1320
- )
1321
- }
1322
- }
1323
- : {}),
1324
- ...(__devflareEmailHandler
1325
- ? {
1326
- async email(message, env, ctx) {
1327
- return runWithContext(
1328
- env,
1329
- ctx,
1330
- null,
1331
- () => __devflareEmailHandler(message, env, ctx),
1332
- 'email'
1333
- )
1334
- }
1335
- }
1336
- : {})
1337
- }
1338
- `;
1339
- }
1340
- function addWorkerWatchRoots(roots, cwd, configuredPath, defaultEntries) {
1341
- if (configuredPath === false) {
1342
- return;
1343
- }
1344
- if (typeof configuredPath === "string" && configuredPath) {
1345
- roots.add(dirname2(resolve2(cwd, configuredPath)));
1346
- return;
1347
- }
1348
- for (const defaultEntry of defaultEntries) {
1349
- roots.add(dirname2(resolve2(cwd, defaultEntry)));
1350
- }
1351
- }
1352
- function collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths) {
1353
- const roots = new Set;
1354
- const addFileParent = (filePath) => {
1355
- if (typeof filePath !== "string" || !filePath) {
1356
- return;
1357
- }
1358
- roots.add(dirname2(resolve2(cwd, filePath)));
1359
- };
1360
- for (const surfacePath of Object.values(mainWorkerSurfacePaths)) {
1361
- if (surfacePath) {
1362
- roots.add(dirname2(surfacePath));
1363
- }
1364
- }
1365
- addWorkerWatchRoots(roots, cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES);
1366
- addWorkerWatchRoots(roots, cwd, config.files?.queue, DEFAULT_QUEUE_ENTRY_FILES);
1367
- addWorkerWatchRoots(roots, cwd, config.files?.scheduled, DEFAULT_SCHEDULED_ENTRY_FILES);
1368
- addWorkerWatchRoots(roots, cwd, config.files?.email, DEFAULT_EMAIL_ENTRY_FILES);
1369
- addFileParent(config.files?.transport);
1370
- if (config.files?.routes && typeof config.files.routes === "object") {
1371
- roots.add(resolve2(cwd, config.files.routes.dir));
1372
- }
1373
- return [...roots];
1374
- }
1375
- function getGatewayScript(wsRoutes = [], debug = false, appServiceBindingName = null) {
1376
- const wsRoutesJson = JSON.stringify(wsRoutes);
1377
- const appServiceBindingJson = JSON.stringify(appServiceBindingName);
1378
- return `
1379
- // Bridge Gateway Worker — RPC Handler
1380
- // Handles all binding operations via WebSocket RPC
1381
- // Also handles WebSocket proxying to Durable Objects
1382
-
1383
- const DEBUG = ${debug}
1384
- const log = (...args) => DEBUG && console.log('[Gateway]', ...args)
1385
-
1386
- const activeStreams = new Map()
1387
- const wsProxies = new Map()
1388
- const incomingStreams = new Map()
1389
-
1390
- // WebSocket routes configuration (injected at build time)
1391
- const WS_ROUTES = ${wsRoutesJson}
1392
- const APP_SERVICE_BINDING = ${appServiceBindingJson}
1393
-
1394
- export default {
1395
- async fetch(request, env, ctx) {
1396
- const url = new URL(request.url)
1397
- const isWebSocket = request.headers.get('Upgrade') === 'websocket'
1398
-
1399
- // Check if this is a WebSocket request matching a DO route
1400
- if (isWebSocket) {
1401
- const matchedRoute = matchWsRoute(url.pathname)
1402
- if (matchedRoute) {
1403
- return handleDoWebSocket(request, env, url, matchedRoute)
1404
- }
1405
- // Otherwise handle as bridge RPC WebSocket
1406
- return handleBridgeWebSocket(request, env, ctx)
1407
- }
1408
-
1409
- // HTTP endpoint for large file transfers
1410
- if (url.pathname.startsWith('/_devflare/transfer/')) {
1411
- return handleHttpTransfer(request, env, url)
1412
- }
1413
-
1414
- // D1 migration endpoint
1415
- if (url.pathname === '/_devflare/migrate' && request.method === 'POST') {
1416
- return handleMigration(request, env)
1417
- }
1418
-
1419
- // Email handler endpoint (simulates incoming email)
1420
- if (url.pathname === '/cdn-cgi/handler/email' && request.method === 'POST') {
1421
- return handleEmailIncoming(request, env, ctx, url)
1422
- }
1423
-
1424
- // Health check
1425
- if (url.pathname === '/_devflare/health') {
1426
- return new Response(JSON.stringify({
1427
- ok: true,
1428
- bindings: Object.keys(env),
1429
- wsRoutes: WS_ROUTES
1430
- }), { headers: { 'Content-Type': 'application/json' } })
1431
- }
1432
-
1433
- if (APP_SERVICE_BINDING) {
1434
- const appWorker = env[APP_SERVICE_BINDING]
1435
- if (appWorker && typeof appWorker.fetch === 'function') {
1436
- return appWorker.fetch(request)
1437
- }
1438
- }
1439
-
1440
- return new Response('Devflare Bridge Gateway', { status: 200 })
1441
- }
1442
- }
1443
-
1444
- // Handle D1 migrations
1445
- async function handleMigration(request, env) {
1446
- try {
1447
- const { bindingName, statements } = await request.json()
1448
- log('Migration request for binding:', bindingName, 'statements count:', statements?.length, 'bindings:', Object.keys(env))
1449
- const db = env[bindingName]
1450
- if (!db) {
1451
- return Response.json({ error: 'Binding not found: ' + bindingName }, { status: 404 })
1452
- }
1453
-
1454
- const results = []
1455
- for (const sql of statements) {
1456
- try {
1457
- log('Running migration SQL:', sql.slice(0, 80))
1458
- await db.prepare(sql).run()
1459
- results.push({ sql: sql.slice(0, 50), success: true })
1460
- log('Migration SQL succeeded')
1461
- } catch (error) {
1462
- const msg = error?.message || String(error)
1463
- log('Migration SQL error:', msg)
1464
- if (msg.includes('already exists')) {
1465
- results.push({ sql: sql.slice(0, 50), success: true, skipped: true })
1466
- } else {
1467
- results.push({ sql: sql.slice(0, 50), success: false, error: msg })
1468
- }
1469
- }
1470
- }
1471
-
1472
- // Verify table exists after migration
1473
- try {
1474
- const tables = await db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all()
1475
- log('Tables after migration:', JSON.stringify(tables))
1476
- } catch (e) {
1477
- log('Error listing tables:', e.message)
1478
- }
1479
-
1480
- return Response.json({ success: true, results })
1481
- } catch (error) {
1482
- return Response.json({ error: error?.message || String(error) }, { status: 500 })
1483
- }
1484
- }
1485
-
1486
- // Handle incoming email (simulates email() handler)
1487
- async function handleEmailIncoming(request, env, ctx, url) {
1488
- try {
1489
- const from = url.searchParams.get('from') || 'unknown@example.com'
1490
- const to = url.searchParams.get('to') || 'worker@example.com'
1491
- const rawBody = await request.text()
1492
-
1493
- log('Email incoming:', { from, to, bodyLength: rawBody.length })
1494
-
1495
- if (APP_SERVICE_BINDING) {
1496
- const appWorker = env[APP_SERVICE_BINDING]
1497
- if (appWorker && typeof appWorker.fetch === 'function') {
1498
- const response = await appWorker.fetch(new Request('http://devflare.internal/_devflare/internal/email', {
1499
- method: 'POST',
1500
- headers: {
1501
- 'x-devflare-event': 'email',
1502
- 'x-devflare-email-from': from,
1503
- 'x-devflare-email-to': to,
1504
- 'content-type': request.headers.get('content-type') || 'text/plain'
1505
- },
1506
- body: rawBody
1507
- }))
1508
-
1509
- if (!response.ok) {
1510
- return response
1511
- }
1512
- }
1513
- }
1514
-
1515
- return new Response(JSON.stringify({ ok: true, from, to }), {
1516
- headers: { 'Content-Type': 'application/json' }
1517
- })
1518
- } catch (error) {
1519
- console.error('[Gateway] Email handler error:', error)
1520
- return Response.json({ error: error?.message || String(error) }, { status: 500 })
1521
- }
1522
- }
1523
-
1524
- // Match URL path against configured WS routes
1525
- function matchWsRoute(pathname) {
1526
- for (const route of WS_ROUTES) {
1527
- // Simple exact match for now (could add glob/regex later)
1528
- if (pathname === route.pattern || pathname.startsWith(route.pattern + '?')) {
1529
- return route
1530
- }
1531
- }
1532
- return null
1533
- }
1534
-
1535
- // Handle WebSocket upgrade that should go to a Durable Object
1536
- async function handleDoWebSocket(request, env, url, route) {
1537
- try {
1538
- // Get the DO namespace
1539
- const namespace = env[route.doNamespace]
1540
- if (!namespace) {
1541
- console.error('[Gateway] DO namespace not found:', route.doNamespace)
1542
- return new Response('DO namespace not found: ' + route.doNamespace, { status: 500 })
1543
- }
1544
-
1545
- // Get the instance ID from query params
1546
- const idValue = url.searchParams.get(route.idParam) || 'default'
1547
-
1548
- // Get or create DO instance
1549
- const doId = namespace.idFromName(idValue)
1550
- const stub = namespace.get(doId)
1551
-
1552
- // Construct the forward URL for the DO
1553
- const forwardUrl = new URL(route.forwardPath, url.origin)
1554
- // Forward all query params
1555
- url.searchParams.forEach((v, k) => forwardUrl.searchParams.set(k, v))
1556
-
1557
- log('Forwarding WebSocket to DO:', route.doNamespace, 'id:', idValue, 'path:', forwardUrl.pathname)
1558
-
1559
- // Forward the request to the DO
1560
- return stub.fetch(forwardUrl.toString(), {
1561
- method: request.method,
1562
- headers: request.headers
1563
- })
1564
- } catch (error) {
1565
- console.error('[Gateway] Error forwarding to DO:', error)
1566
- return new Response('Error forwarding to DO: ' + error.message, { status: 500 })
1567
- }
1568
- }
1569
-
1570
- // Handle bridge RPC WebSocket (for Node.js Vite server communication)
1571
- function handleBridgeWebSocket(request, env, ctx) {
1572
- const { 0: client, 1: server } = new WebSocketPair()
1573
- server.accept()
1574
-
1575
- server.addEventListener('message', async (event) => {
1576
- try {
1577
- if (typeof event.data === 'string') {
1578
- await handleJsonMessage(event.data, server, env, ctx)
1579
- }
1580
- } catch (error) {
1581
- console.error('[Gateway] Error:', error)
1582
- }
1583
- })
1584
-
1585
- server.addEventListener('close', () => {
1586
- activeStreams.clear()
1587
- wsProxies.clear()
1588
- })
1589
-
1590
- return new Response(null, { status: 101, webSocket: client })
1591
- }
1592
-
1593
- async function handleJsonMessage(data, ws, env, ctx) {
1594
- const msg = JSON.parse(data)
1595
-
1596
- switch (msg.t) {
1597
- case 'rpc.call':
1598
- await handleRpcCall(msg, ws, env, ctx)
1599
- break
1600
- case 'ws.open':
1601
- await handleWsOpen(msg, ws, env)
1602
- break
1603
- case 'ws.close':
1604
- handleWsClose(msg)
1605
- break
1606
- }
1607
- }
1608
-
1609
- async function handleRpcCall(msg, ws, env, ctx) {
1610
- try {
1611
- const result = await executeRpcMethod(msg.method, msg.params, env, ctx)
1612
- ws.send(JSON.stringify({ t: 'rpc.ok', id: msg.id, result }))
1613
- } catch (error) {
1614
- ws.send(JSON.stringify({
1615
- t: 'rpc.err',
1616
- id: msg.id,
1617
- error: { code: error.code || 'INTERNAL_ERROR', message: error.message }
1618
- }))
1619
- }
1620
- }
1621
-
1622
- async function executeRpcMethod(method, params, env, ctx) {
1623
- const parts = method.split('.')
1624
- const bindingName = parts[0]
1625
- const operation = parts.slice(1).join('.')
1626
- const binding = env[bindingName]
1627
-
1628
- if (!binding) throw new Error('Binding not found: ' + bindingName)
1629
-
1630
- // KV operations
1631
- if (operation === 'get') return binding.get(params[0], params[1])
1632
- if (operation === 'put') return binding.put(params[0], params[1], params[2])
1633
- if (operation === 'delete') return binding.delete(params[0])
1634
- if (operation === 'list') return binding.list(params[0])
1635
- if (operation === 'getWithMetadata') return binding.getWithMetadata(params[0], params[1])
1636
-
1637
- // R2 operations
1638
- if (operation === 'head') return serializeR2Object(await binding.head(params[0]))
1639
- if (operation === 'r2.get') {
1640
- const obj = await binding.get(params[0], params[1])
1641
- if (!obj) return null
1642
- const body = await obj.arrayBuffer()
1643
- return serializeR2ObjectBody(obj, arrayBufferToBase64(body))
1644
- }
1645
- if (operation === 'r2.put') {
1646
- // Deserialize the value if it's a serialized ArrayBuffer/Uint8Array
1647
- let value = params[1]
1648
- if (value && typeof value === 'object') {
1649
- if (value.__type === 'ArrayBuffer') {
1650
- value = base64ToArrayBuffer(value.data)
1651
- } else if (value.__type === 'Uint8Array') {
1652
- value = base64ToArrayBuffer(value.data)
1653
- }
1654
- }
1655
- return serializeR2Object(await binding.put(params[0], value, params[2]))
1656
- }
1657
- if (operation === 'r2.delete') return binding.delete(params[0])
1658
- if (operation === 'r2.list') return serializeR2Objects(await binding.list(params[0]))
1659
-
1660
- // D1 operations
1661
- if (operation === 'exec') return binding.exec(params[0])
1662
- if (operation.startsWith('stmt.')) {
1663
- log('D1 RPC:', bindingName, operation, 'sql:', String(params[0]).slice(0, 60))
1664
- const mode = operation.split('.')[1]
1665
- const [sql, ...rest] = params
1666
-
1667
- // For first/raw, the last element is the column/options parameter (may be undefined)
1668
- // For all/run, rest contains only bindings
1669
- let bindings = rest
1670
- let extraParam = undefined
1671
-
1672
- if (mode === 'first' || mode === 'raw') {
1673
- // Last element is the column/options (may be undefined)
1674
- extraParam = rest[rest.length - 1]
1675
- bindings = rest.slice(0, -1)
1676
- }
1677
-
1678
- let stmt = binding.prepare(sql)
1679
- if (bindings.length > 0) stmt = stmt.bind(...bindings)
1680
-
1681
- if (mode === 'first') {
1682
- // Only pass column if it's a non-empty string
1683
- if (typeof extraParam === 'string' && extraParam.length > 0) {
1684
- return stmt.first(extraParam)
1685
- }
1686
- return stmt.first()
1687
- }
1688
- if (mode === 'all') return stmt.all()
1689
- if (mode === 'run') return stmt.run()
1690
- if (mode === 'raw') return stmt.raw(extraParam)
1691
- }
1692
-
1693
- // DO operations
1694
- if (operation === 'idFromName') {
1695
- const id = binding.idFromName(params[0])
1696
- return { __type: 'DOId', hex: id.toString() }
1697
- }
1698
- if (operation === 'idFromString') {
1699
- const id = binding.idFromString(params[0])
1700
- return { __type: 'DOId', hex: id.toString() }
1701
- }
1702
- if (operation === 'newUniqueId') {
1703
- const id = binding.newUniqueId(params[0])
1704
- return { __type: 'DOId', hex: id.toString() }
1705
- }
1706
- if (operation === 'stub.fetch') {
1707
- const [, serializedId, serializedReq] = params
1708
- log('stub.fetch request:', {
1709
- url: serializedReq.url,
1710
- method: serializedReq.method,
1711
- headers: serializedReq.headers,
1712
- hasBody: !!serializedReq.body
1713
- })
1714
- const id = binding.idFromString(serializedId.hex)
1715
- const stub = binding.get(id)
1716
- try {
1717
- const response = await stub.fetch(new Request(serializedReq.url, {
1718
- method: serializedReq.method,
1719
- headers: serializedReq.headers,
1720
- body: serializedReq.body?.type === 'bytes' ? base64ToArrayBuffer(serializedReq.body.data) : undefined
1721
- }))
1722
- // Clone to read body for logging if there's an error
1723
- const cloned = response.clone()
1724
- const serialized = await serializeResponse(response)
1725
- log('stub.fetch response:', {
1726
- status: serialized.status,
1727
- headers: serialized.headers,
1728
- bodyLength: serialized.body?.data?.length || 0
1729
- })
1730
- // If 500, log the body content
1731
- if (response.status >= 400) {
1732
- const errBody = await cloned.text()
1733
- log('Error response body:', errBody)
1734
- }
1735
- return serialized
1736
- } catch (err) {
1737
- console.error('[Gateway] stub.fetch error:', err)
1738
- throw err
1739
- }
1740
- }
1741
- if (operation === 'stub.rpc') {
1742
- const [, serializedId, methodName, args] = params
1743
- const id = binding.idFromString(serializedId.hex)
1744
- const stub = binding.get(id)
1745
- const response = await stub.fetch(new Request('http://do/_rpc', {
1746
- method: 'POST',
1747
- headers: { 'Content-Type': 'application/json' },
1748
- body: JSON.stringify({ method: methodName, params: args })
1749
- }))
1750
- const result = await response.json()
1751
- if (!result.ok) throw new Error(result.error?.message || 'RPC failed')
1752
- return result.result
1753
- }
1754
-
1755
- // Queue operations
1756
- if (operation === 'send') return binding.send(params[0], params[1])
1757
- if (operation === 'sendBatch') return binding.sendBatch(params[0], params[1])
1758
-
1759
- // Email send operations (send_email binding)
1760
- if (operation === 'email.send') {
1761
- log('Email send:', { from: params[0]?.from, to: params[0]?.to })
1762
- // In local dev, we just log the email - Miniflare handles writing to file
1763
- if (binding && typeof binding.send === 'function') {
1764
- return binding.send(params[0])
1765
- }
1766
- // Return success even if no real binding (simulated)
1767
- return { ok: true, simulated: true }
1768
- }
1769
-
1770
- throw new Error('Unknown operation: ' + method)
1771
- }
1772
-
1773
- async function handleWsOpen(msg, ws, env) {
1774
- try {
1775
- const binding = env[msg.target.binding]
1776
- const id = binding.idFromString(msg.target.id)
1777
- const stub = binding.get(id)
1778
-
1779
- const headers = new Headers(msg.target.headers || [])
1780
- headers.set('Upgrade', 'websocket')
1781
-
1782
- const response = await stub.fetch(new Request(msg.target.url, { method: 'GET', headers }))
1783
- const doWs = response.webSocket
1784
-
1785
- if (!doWs) {
1786
- ws.send(JSON.stringify({ t: 'rpc.err', id: 'ws_' + msg.wid, error: { code: 'WS_FAILED', message: 'No WebSocket returned' } }))
1787
- return
1788
- }
1789
-
1790
- doWs.accept()
1791
- wsProxies.set(msg.wid, { doWs })
1792
-
1793
- doWs.addEventListener('message', (event) => {
1794
- const isText = typeof event.data === 'string'
1795
- const data = isText ? event.data : arrayBufferToBase64(event.data)
1796
- ws.send(JSON.stringify({ t: 'ws.data', wid: msg.wid, data, isText }))
1797
- })
1798
-
1799
- doWs.addEventListener('close', (event) => {
1800
- ws.send(JSON.stringify({ t: 'ws.close', wid: msg.wid, code: event.code, reason: event.reason }))
1801
- wsProxies.delete(msg.wid)
1802
- })
1803
-
1804
- ws.send(JSON.stringify({ t: 'ws.opened', wid: msg.wid }))
1805
- } catch (error) {
1806
- ws.send(JSON.stringify({ t: 'rpc.err', id: 'ws_' + msg.wid, error: { code: 'WS_FAILED', message: error.message } }))
1807
- }
1808
- }
1809
-
1810
- function handleWsClose(msg) {
1811
- const proxy = wsProxies.get(msg.wid)
1812
- if (proxy) {
1813
- proxy.doWs.close(msg.code, msg.reason)
1814
- wsProxies.delete(msg.wid)
1815
- }
1816
- }
1817
-
1818
- async function handleHttpTransfer(request, env, url) {
1819
- const transferIdEncoded = url.pathname.split('/').pop()
1820
- const transferId = decodeURIComponent(transferIdEncoded || '')
1821
- const [binding, ...keyParts] = transferId.split(':')
1822
- const key = keyParts.join(':')
1823
- const bucket = env[binding]
1824
-
1825
- if (!bucket) return new Response('Bucket not found: ' + binding, { status: 404 })
1826
-
1827
- if (request.method === 'PUT' || request.method === 'POST') {
1828
- const result = await bucket.put(key, request.body)
1829
- return new Response(JSON.stringify(result), { headers: { 'Content-Type': 'application/json' } })
1830
- }
1831
-
1832
- if (request.method === 'GET') {
1833
- const object = await bucket.get(key)
1834
- if (!object) return new Response('Not found', { status: 404 })
1835
- return new Response(object.body, {
1836
- headers: {
1837
- 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
1838
- 'Content-Length': String(object.size)
1839
- }
1840
- })
1841
- }
1842
-
1843
- return new Response('Method not allowed', { status: 405 })
1844
- }
1845
-
1846
- // Helpers
1847
- function serializeR2Object(obj) {
1848
- if (!obj) return null
1849
- return {
1850
- __type: 'R2Object',
1851
- key: obj.key,
1852
- version: obj.version,
1853
- size: obj.size,
1854
- etag: obj.etag,
1855
- httpEtag: obj.httpEtag,
1856
- checksums: obj.checksums,
1857
- uploaded: obj.uploaded?.toISOString(),
1858
- httpMetadata: obj.httpMetadata,
1859
- customMetadata: obj.customMetadata,
1860
- range: obj.range,
1861
- storageClass: obj.storageClass
1862
- }
1863
- }
1864
- function serializeR2ObjectBody(obj, bodyData) {
1865
- if (!obj) return null
1866
- return {
1867
- __type: 'R2ObjectBody',
1868
- key: obj.key,
1869
- version: obj.version,
1870
- size: obj.size,
1871
- etag: obj.etag,
1872
- httpEtag: obj.httpEtag,
1873
- checksums: obj.checksums,
1874
- uploaded: obj.uploaded?.toISOString(),
1875
- httpMetadata: obj.httpMetadata,
1876
- customMetadata: obj.customMetadata,
1877
- range: obj.range,
1878
- storageClass: obj.storageClass,
1879
- bodyData
1880
- }
1881
- }
1882
- function serializeR2Objects(result) {
1883
- if (!result) return null
1884
- return { objects: result.objects.map(serializeR2Object), truncated: result.truncated, cursor: result.cursor }
1885
- }
1886
- async function serializeResponse(response) {
1887
- // Read body as bytes and encode as base64
1888
- let body = null
1889
- if (response.body) {
1890
- const bytes = await response.arrayBuffer()
1891
- if (bytes.byteLength > 0) {
1892
- body = { type: 'bytes', data: arrayBufferToBase64(bytes) }
1893
- }
1894
- }
1895
- return { status: response.status, statusText: response.statusText, headers: [...response.headers.entries()], body }
1896
- }
1897
- function arrayBufferToBase64(buffer) {
1898
- const bytes = new Uint8Array(buffer)
1899
- let binary = ''
1900
- for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i])
1901
- return btoa(binary)
1902
- }
1903
- function base64ToArrayBuffer(base64) {
1904
- const binary = atob(base64)
1905
- const bytes = new Uint8Array(binary.length)
1906
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)
1907
- return bytes.buffer
1908
- }
1909
- `;
1910
- }
1911
- function createDevServer(options) {
1912
- const {
1913
- cwd,
1914
- configPath,
1915
- vitePort = 5173,
1916
- miniflarePort = 8787,
1917
- enableVite = true,
1918
- persist = true,
1919
- logger,
1920
- verbose = false,
1921
- debug = process.env.DEVFLARE_DEBUG === "true"
1922
- } = options;
1923
- let miniflare = null;
1924
- let doBundler = null;
1925
- let workerSourceWatcher = null;
1926
- let workerWatchTargets = [];
1927
- let viteProcess = null;
1928
- let config = null;
1929
- let browserShim = null;
1930
- let browserShimPort = 8788;
1931
- let mainWorkerSurfacePaths = {
1932
- fetch: null,
1933
- queue: null,
1934
- scheduled: null,
1935
- email: null
1936
- };
1937
- let resolvedWorkerConfigPath = null;
1938
- let mainWorkerScriptPath = null;
1939
- let bundledMainWorkerScriptPath = null;
1940
- let currentDoResult = null;
1941
- let reloadChain = Promise.resolve();
1942
- async function bundleMainWorker() {
1943
- if (!hasWorkerSurfacePaths(mainWorkerSurfacePaths)) {
1944
- bundledMainWorkerScriptPath = null;
1945
- return;
1946
- }
1947
- if (typeof Bun === "undefined" || typeof Bun.build !== "function") {
1948
- throw new Error("Worker-only dev mode requires the Bun runtime for main worker bundling");
1949
- }
1950
- const fs = await import("node:fs/promises");
1951
- const outDir = resolve2(cwd, ".devflare", "worker-bundles");
1952
- const entryDir = resolve2(cwd, ".devflare", "worker-entrypoints");
1953
- await fs.rm(outDir, { recursive: true, force: true });
1954
- await fs.mkdir(outDir, { recursive: true });
1955
- await fs.mkdir(entryDir, { recursive: true });
1956
- const entryPath = resolve2(entryDir, "fetch-entry.ts");
1957
- const surfaceImportPaths = {
1958
- fetch: mainWorkerSurfacePaths.fetch ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.fetch) : null,
1959
- queue: mainWorkerSurfacePaths.queue ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.queue) : null,
1960
- scheduled: mainWorkerSurfacePaths.scheduled ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.scheduled) : null,
1961
- email: mainWorkerSurfacePaths.email ? toImportSpecifier(entryPath, mainWorkerSurfacePaths.email) : null
1962
- };
1963
- await fs.writeFile(entryPath, getMainWorkerEntryScript(surfaceImportPaths));
1964
- const result = await Bun.build({
1965
- entrypoints: [entryPath],
1966
- outdir: outDir,
1967
- target: "browser",
1968
- conditions: ["browser"],
1969
- format: "esm",
1970
- minify: false,
1971
- splitting: false,
1972
- external: ["cloudflare:workers", "cloudflare:*", "node:*"]
1973
- });
1974
- if (!result.success || result.outputs.length === 0) {
1975
- const logs = result.logs.map((log) => ("message" in log) ? log.message : String(log)).join(`
1976
- `);
1977
- throw new Error(`Failed to bundle main worker:
1978
- ${logs}`);
1979
- }
1980
- bundledMainWorkerScriptPath = result.outputs[0].path;
1981
- logger?.debug(`Bundled main worker → ${bundledMainWorkerScriptPath}`);
1982
- }
1983
- function buildMiniflareConfig(doResult) {
1984
- if (!config)
1985
- throw new Error("Config not loaded");
1986
- const loadedConfig = config;
1987
- const bindings = loadedConfig.bindings ?? {};
1988
- const persistPath = resolve2(cwd, ".devflare/data");
1989
- const appWorkerName = loadedConfig.name;
1990
- const shouldRunMainWorker = !enableVite && hasWorkerSurfacePaths(mainWorkerSurfacePaths);
1991
- const queueProducers = (() => {
1992
- if (!bindings.queues?.producers) {
1993
- return;
1994
- }
1995
- const producers = {};
1996
- for (const [bindingName, queueName] of Object.entries(bindings.queues.producers)) {
1997
- producers[bindingName] = { queueName };
1998
- }
1999
- return producers;
2000
- })();
2001
- const queueConsumers = (() => {
2002
- if (!bindings.queues?.consumers || bindings.queues.consumers.length === 0) {
2003
- return;
2004
- }
2005
- const consumers = {};
2006
- for (const consumer of bindings.queues.consumers) {
2007
- consumers[consumer.queue] = {
2008
- ...consumer.maxBatchSize !== undefined && { maxBatchSize: consumer.maxBatchSize },
2009
- ...consumer.maxBatchTimeout !== undefined && { maxBatchTimeout: consumer.maxBatchTimeout },
2010
- ...consumer.maxRetries !== undefined && { maxRetries: consumer.maxRetries },
2011
- ...consumer.deadLetterQueue && { deadLetterQueue: consumer.deadLetterQueue },
2012
- ...consumer.maxConcurrency !== undefined && { maxConcurrency: consumer.maxConcurrency },
2013
- ...consumer.retryDelay !== undefined && { retryDelay: consumer.retryDelay }
2014
- };
2015
- }
2016
- return consumers;
2017
- })();
2018
- const sharedOptions = {
2019
- port: miniflarePort,
2020
- host: "127.0.0.1",
2021
- kvPersist: persist ? `${persistPath}/kv` : undefined,
2022
- r2Persist: persist ? `${persistPath}/r2` : undefined,
2023
- d1Persist: persist ? `${persistPath}/d1` : undefined,
2024
- durableObjectsPersist: persist ? `${persistPath}/do` : undefined
2025
- };
2026
- const createServiceBindings = (extraBindings = {}) => {
2027
- const serviceBindings = {};
2028
- if (bindings.services) {
2029
- for (const [bindingName, serviceConfig] of Object.entries(bindings.services)) {
2030
- serviceBindings[bindingName] = {
2031
- name: serviceConfig.service,
2032
- ...serviceConfig.entrypoint && { entrypoint: serviceConfig.entrypoint }
2033
- };
2034
- }
2035
- }
2036
- for (const [bindingName, target] of Object.entries(extraBindings)) {
2037
- serviceBindings[bindingName] = target;
2038
- }
2039
- return Object.keys(serviceBindings).length > 0 ? serviceBindings : undefined;
2040
- };
2041
- const createWorkerConfig = (options2) => {
2042
- const baseFlags = loadedConfig.compatibilityFlags ?? [];
2043
- const compatFlags = baseFlags.includes("nodejs_compat") ? baseFlags : [...baseFlags, "nodejs_compat"];
2044
- const workerConfig = {
2045
- name: options2.name,
2046
- modules: true,
2047
- compatibilityDate: loadedConfig.compatibilityDate,
2048
- compatibilityFlags: compatFlags,
2049
- ...bindings.kv && { kvNamespaces: bindings.kv },
2050
- ...bindings.r2 && { r2Buckets: bindings.r2 },
2051
- ...bindings.d1 && { d1Databases: bindings.d1 },
2052
- ...loadedConfig.vars && Object.keys(loadedConfig.vars).length > 0 && { bindings: loadedConfig.vars },
2053
- ...queueProducers && { queueProducers },
2054
- ...options2.queueConsumers && { queueConsumers: options2.queueConsumers },
2055
- ...options2.triggers && { triggers: options2.triggers }
2056
- };
2057
- if (options2.scriptPath) {
2058
- workerConfig.scriptPath = options2.scriptPath;
2059
- workerConfig.modulesRoot = cwd;
2060
- workerConfig.modulesRules = [
2061
- { type: "ESModule", include: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.mjs"] },
2062
- { type: "CommonJS", include: ["**/*.js", "**/*.cjs"] },
2063
- { type: "ESModule", include: ["**/*.jsx"] }
2064
- ];
2065
- }
2066
- if (options2.script) {
2067
- workerConfig.script = options2.script;
2068
- }
2069
- if (options2.durableObjects && Object.keys(options2.durableObjects).length > 0) {
2070
- workerConfig.durableObjects = options2.durableObjects;
2071
- }
2072
- if (options2.serviceBindings && Object.keys(options2.serviceBindings).length > 0) {
2073
- workerConfig.serviceBindings = options2.serviceBindings;
2074
- }
2075
- return workerConfig;
2076
- };
2077
- const gatewayWorker = createWorkerConfig({
2078
- name: "gateway",
2079
- script: getGatewayScript(loadedConfig.wsRoutes, debug, shouldRunMainWorker ? INTERNAL_APP_SERVICE_BINDING : null),
2080
- serviceBindings: shouldRunMainWorker ? createServiceBindings({
2081
- [INTERNAL_APP_SERVICE_BINDING]: { name: appWorkerName }
2082
- }) : createServiceBindings()
2083
- });
2084
- gatewayWorker.routes = ["*"];
2085
- const hasDurableObjectBundles = !!doResult && doResult.bundles.size > 0;
2086
- const browserBindingName = bindings.browser?.binding;
2087
- const needsBrowserWorker = Boolean(browserBindingName && (hasDurableObjectBundles || shouldRunMainWorker));
2088
- if (!shouldRunMainWorker && !hasDurableObjectBundles && !needsBrowserWorker) {
2089
- return {
2090
- ...sharedOptions,
2091
- ...gatewayWorker
2092
- };
2093
- }
2094
- const workers = [];
2095
- const durableObjects = {};
2096
- const browserShimUrl = `http://127.0.0.1:${browserShimPort}`;
2097
- const browserWorkerName = "browser-binding";
2098
- if (shouldRunMainWorker && mainWorkerScriptPath) {
2099
- const mainWorkerServiceBindings = createServiceBindings(browserBindingName ? {
2100
- [browserBindingName]: { name: browserWorkerName }
2101
- } : {});
2102
- const mainWorkerConfig = createWorkerConfig({
2103
- name: appWorkerName,
2104
- scriptPath: bundledMainWorkerScriptPath ?? mainWorkerScriptPath,
2105
- serviceBindings: mainWorkerServiceBindings,
2106
- queueConsumers,
2107
- triggers: loadedConfig.triggers?.crons?.length ? { crons: loadedConfig.triggers.crons } : undefined
2108
- });
2109
- workers.push(mainWorkerConfig);
2110
- }
2111
- if (doResult) {
2112
- for (const [bindingName, bundlePath] of doResult.bundles) {
2113
- const className = doResult.classes.get(bindingName);
2114
- if (!className)
2115
- continue;
2116
- const workerName = `do-${bindingName.toLowerCase()}`;
2117
- const workerConfig = createWorkerConfig({
2118
- name: workerName,
2119
- scriptPath: bundlePath,
2120
- durableObjects: {
2121
- [bindingName]: className
2122
- },
2123
- serviceBindings: createServiceBindings(browserBindingName ? {
2124
- [browserBindingName]: { name: browserWorkerName }
2125
- } : {})
2126
- });
2127
- if (browserBindingName) {
2128
- logger?.debug(`DO ${workerName} has browser service binding: ${browserBindingName} → ${browserWorkerName}`);
2129
- }
2130
- logger?.debug(`DO ${workerName} config:`, JSON.stringify(workerConfig, null, 2));
2131
- workers.push(workerConfig);
2132
- durableObjects[bindingName] = {
2133
- className,
2134
- scriptName: workerName
2135
- };
2136
- }
2137
- }
2138
- if (needsBrowserWorker) {
2139
- const browserWorker = createWorkerConfig({
2140
- name: browserWorkerName,
2141
- script: getBrowserBindingScript(browserShimUrl, debug)
2142
- });
2143
- workers.push(browserWorker);
2144
- logger?.info(`Browser binding worker configured: ${browserBindingName} → ${browserShimUrl}`);
2145
- }
2146
- if (Object.keys(durableObjects).length > 0) {
2147
- gatewayWorker.durableObjects = durableObjects;
2148
- if (shouldRunMainWorker) {
2149
- const mainWorker = workers.find((worker) => worker.name === appWorkerName);
2150
- if (mainWorker) {
2151
- mainWorker.durableObjects = durableObjects;
2152
- }
2153
- }
2154
- }
2155
- return {
2156
- ...sharedOptions,
2157
- workers: [gatewayWorker, ...workers]
2158
- };
2159
- }
2160
- async function startMiniflare(doResult) {
2161
- const { Miniflare, Log, LogLevel } = await import("miniflare");
2162
- const mfConfig = buildMiniflareConfig(doResult);
2163
- mfConfig.log = new Log(LogLevel.DEBUG);
2164
- logger?.info("=== MINIFLARE CONFIG DEBUG ===");
2165
- logger?.info("Full config:", JSON.stringify(mfConfig, (key, value) => {
2166
- if (key === "script" && typeof value === "string" && value.length > 200) {
2167
- return value.substring(0, 200) + "...[truncated]";
2168
- }
2169
- return value;
2170
- }, 2));
2171
- if (mfConfig.workers) {
2172
- logger?.info("Workers order:");
2173
- for (const w of mfConfig.workers) {
2174
- logger?.info(` → ${w.name}:`);
2175
- logger?.info(` script: ${w.script ? "inline" : w.scriptPath}`);
2176
- logger?.info(` browserRendering: ${JSON.stringify(w.browserRendering)}`);
2177
- logger?.info(` durableObjects: ${JSON.stringify(w.durableObjects)}`);
2178
- }
2179
- }
2180
- miniflare = new Miniflare(mfConfig);
2181
- await miniflare.ready;
2182
- logger?.success(`Miniflare ready on http://localhost:${miniflarePort}`);
2183
- try {
2184
- const gatewayBindings = await miniflare.getBindings("gateway");
2185
- logger?.info("Gateway worker bindings:", Object.keys(gatewayBindings));
2186
- if (mfConfig.workers) {
2187
- for (const w of mfConfig.workers) {
2188
- if (w.name !== "gateway") {
2189
- try {
2190
- const doBindings = await miniflare.getBindings(w.name);
2191
- logger?.info(`${w.name} worker bindings:`, Object.keys(doBindings));
2192
- if ("BROWSER" in doBindings) {
2193
- logger?.success(`${w.name} has BROWSER binding!`);
2194
- } else {
2195
- logger?.warn(`${w.name} is MISSING BROWSER binding`);
2196
- }
2197
- } catch (e) {
2198
- logger?.warn(`Could not get bindings for ${w.name}:`, e);
2199
- }
2200
- }
2201
- }
2202
- }
2203
- } catch (e) {
2204
- logger?.warn("Error getting bindings:", e);
2205
- }
2206
- }
2207
- async function reloadMiniflare(doResult) {
2208
- currentDoResult = doResult;
2209
- const queuedReload = reloadChain.then(async () => {
2210
- if (!miniflare)
2211
- return;
2212
- const { Log, LogLevel } = await import("miniflare");
2213
- const mfConfig = buildMiniflareConfig(currentDoResult);
2214
- mfConfig.log = new Log(LogLevel.DEBUG);
2215
- logger?.info("Reloading Miniflare...");
2216
- await miniflare.setOptions(mfConfig);
2217
- logger?.success("Miniflare reloaded");
2218
- });
2219
- reloadChain = queuedReload.catch(() => {});
2220
- await queuedReload;
2221
- }
2222
- async function resolveWorkerConfigWatchPath() {
2223
- if (configPath) {
2224
- const explicitPath = resolve2(cwd, configPath);
2225
- const fs = await import("node:fs/promises");
2226
- try {
2227
- await fs.access(explicitPath);
2228
- return explicitPath;
2229
- } catch {}
2230
- }
2231
- return await resolveConfigPath(cwd) ?? null;
2232
- }
2233
- async function refreshWorkerOnlySurfaceState() {
2234
- if (!config) {
2235
- return;
2236
- }
2237
- mainWorkerSurfacePaths = await resolveMainWorkerSurfacePaths(cwd, config);
2238
- mainWorkerScriptPath = getFirstWorkerSurfacePath(mainWorkerSurfacePaths);
2239
- if (hasWorkerSurfacePaths(mainWorkerSurfacePaths)) {
2240
- await bundleMainWorker();
2241
- } else {
2242
- bundledMainWorkerScriptPath = null;
2243
- }
2244
- await syncWorkerWatchTargets();
2245
- }
2246
- function getWorkerWatchTargets() {
2247
- if (enableVite || !config) {
2248
- return [];
2249
- }
2250
- const targets = collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths);
2251
- if (resolvedWorkerConfigPath) {
2252
- targets.push(resolvedWorkerConfigPath);
2253
- }
2254
- return [...new Set(targets)];
2255
- }
2256
- async function syncWorkerWatchTargets() {
2257
- if (!workerSourceWatcher) {
2258
- return;
2259
- }
2260
- const nextWatchTargets = getWorkerWatchTargets();
2261
- const nextWatchTargetSet = new Set(nextWatchTargets);
2262
- const targetsToRemove = workerWatchTargets.filter((target) => !nextWatchTargetSet.has(target));
2263
- const targetsToAdd = nextWatchTargets.filter((target) => !workerWatchTargets.includes(target));
2264
- if (targetsToRemove.length > 0) {
2265
- await workerSourceWatcher.unwatch(targetsToRemove);
2266
- }
2267
- if (targetsToAdd.length > 0) {
2268
- workerSourceWatcher.add(targetsToAdd);
2269
- }
2270
- workerWatchTargets = nextWatchTargets;
2271
- }
2272
- async function reloadWorkerOnlyConfig() {
2273
- config = await loadConfig({ cwd, configFile: configPath });
2274
- resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
2275
- await refreshWorkerOnlySurfaceState();
2276
- await reloadMiniflare(currentDoResult);
2277
- }
2278
- async function startWorkerSourceWatcher() {
2279
- if (enableVite || !config) {
2280
- return;
2281
- }
2282
- const watchTargets = getWorkerWatchTargets();
2283
- if (watchTargets.length === 0) {
2284
- return;
2285
- }
2286
- const chokidar = await import("chokidar");
2287
- const isWindows = process.platform === "win32";
2288
- const ignoredSegments = ["/node_modules/", "/.git/", "/.devflare/", "/dist/"];
2289
- const normalizePath = (filePath) => filePath.replace(/\\/g, "/");
2290
- const isIgnoredPath = (filePath) => {
2291
- const normalizedPath = normalizePath(filePath);
2292
- return ignoredSegments.some((segment) => normalizedPath.includes(segment));
2293
- };
2294
- let reloadTimeout = null;
2295
- let reloadInProgress = false;
2296
- let pendingReloadPath = null;
2297
- const triggerReload = async (filePath) => {
2298
- if (reloadInProgress) {
2299
- pendingReloadPath = filePath;
2300
- return;
2301
- }
2302
- reloadInProgress = true;
2303
- try {
2304
- const normalizedConfigPath = resolvedWorkerConfigPath ? normalizePath(resolvedWorkerConfigPath) : null;
2305
- if (normalizedConfigPath && normalizePath(filePath) === normalizedConfigPath) {
2306
- logger?.info(`Devflare config changed: ${filePath}`);
2307
- await reloadWorkerOnlyConfig();
2308
- return;
2309
- }
2310
- logger?.info(`Worker source changed: ${filePath}`);
2311
- await refreshWorkerOnlySurfaceState();
2312
- await reloadMiniflare(currentDoResult);
2313
- } catch (error) {
2314
- logger?.error("Worker source reload failed:", error);
2315
- } finally {
2316
- reloadInProgress = false;
2317
- if (pendingReloadPath) {
2318
- const nextPath = pendingReloadPath;
2319
- pendingReloadPath = null;
2320
- await triggerReload(nextPath);
2321
- }
2322
- }
2323
- };
2324
- const scheduleReload = (filePath) => {
2325
- if (reloadTimeout) {
2326
- clearTimeout(reloadTimeout);
2327
- }
2328
- reloadTimeout = setTimeout(() => {
2329
- triggerReload(filePath);
2330
- }, 150);
2331
- };
2332
- workerWatchTargets = watchTargets;
2333
- workerSourceWatcher = chokidar.watch(watchTargets, {
2334
- ignoreInitial: true,
2335
- usePolling: isWindows,
2336
- interval: isWindows ? 300 : undefined,
2337
- awaitWriteFinish: {
2338
- stabilityThreshold: 100,
2339
- pollInterval: 50
2340
- },
2341
- ignored: (filePath) => isIgnoredPath(filePath)
2342
- });
2343
- const onFileEvent = (filePath) => {
2344
- if (isIgnoredPath(filePath)) {
2345
- return;
2346
- }
2347
- scheduleReload(filePath);
2348
- };
2349
- workerSourceWatcher.on("change", onFileEvent);
2350
- workerSourceWatcher.on("add", onFileEvent);
2351
- workerSourceWatcher.on("unlink", onFileEvent);
2352
- workerSourceWatcher.on("ready", () => {
2353
- logger?.info(`Worker source watcher ready (${watchTargets.length} target(s))`);
2354
- });
2355
- workerSourceWatcher.on("error", (error) => {
2356
- logger?.error("Worker source watcher error:", error);
2357
- });
2358
- }
2359
- async function runD1Migrations() {
2360
- if (!miniflare || !config?.bindings?.d1)
2361
- return;
2362
- const { existsSync: existsSync2, readdirSync, readFileSync } = await import("node:fs");
2363
- const migrationsDir = resolve2(cwd, "migrations");
2364
- if (!existsSync2(migrationsDir)) {
2365
- logger?.debug("No migrations/ directory found, skipping D1 migrations");
2366
- return;
2367
- }
2368
- const files = readdirSync(migrationsDir).filter((f) => f.endsWith(".sql")).sort();
2369
- if (files.length === 0) {
2370
- logger?.debug("No SQL migration files found");
2371
- return;
2372
- }
2373
- logger?.info(`Running ${files.length} D1 migration(s)...`);
2374
- const allStatements = [];
2375
- for (const file of files) {
2376
- const sql = readFileSync(resolve2(migrationsDir, file), "utf-8");
2377
- const cleanedSql = sql.split(`
2378
- `).filter((line) => !line.trim().startsWith("--")).join(`
2379
- `);
2380
- const statements = cleanedSql.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
2381
- allStatements.push(...statements);
2382
- logger?.debug(`File ${file}: ${statements.length} statement(s)`);
2383
- }
2384
- for (const [bindingName] of Object.entries(config.bindings.d1)) {
2385
- for (let attempt = 0;attempt < 5; attempt++) {
2386
- await new Promise((r) => setTimeout(r, 500 * (attempt + 1)));
2387
- try {
2388
- const response = await fetch(`http://127.0.0.1:${miniflarePort}/_devflare/migrate`, {
2389
- method: "POST",
2390
- headers: { "Content-Type": "application/json" },
2391
- body: JSON.stringify({ bindingName, statements: allStatements })
2392
- });
2393
- if (!response.ok) {
2394
- const text = await response.text();
2395
- throw new Error(`HTTP ${response.status}: ${text}`);
2396
- }
2397
- const result = await response.json();
2398
- if (result.success) {
2399
- logger?.success(`D1 migrations applied to ${bindingName}`);
2400
- break;
2401
- } else {
2402
- throw new Error(result.error || "Unknown error");
2403
- }
2404
- } catch (error) {
2405
- if (attempt === 4) {
2406
- logger?.warn(`Failed to apply migrations to ${bindingName}: ${error}`);
2407
- }
2408
- }
2409
- }
2410
- }
2411
- }
2412
- async function startVite() {
2413
- const { spawn } = await import("node:child_process");
2414
- const args = ["vite", "dev", "--port", String(vitePort)];
2415
- viteProcess = spawn("bunx", args, {
2416
- cwd,
2417
- stdio: ["inherit", "pipe", "pipe"],
2418
- windowsHide: true,
2419
- env: {
2420
- ...process.env,
2421
- DEVFLARE_DEV: "true",
2422
- DEVFLARE_BRIDGE_PORT: String(miniflarePort),
2423
- FORCE_COLOR: "1"
2424
- }
2425
- });
2426
- const readyUrl = await waitForViteReady(viteProcess, {
2427
- onStdout(chunk) {
2428
- process.stdout.write(chunk);
2429
- },
2430
- onStderr(chunk) {
2431
- process.stderr.write(chunk);
2432
- }
2433
- });
2434
- if (readyUrl) {
2435
- logger?.success(`Vite dev server started on ${readyUrl}`);
2436
- return;
2437
- }
2438
- logger?.warn("Vite process started, but the final local URL could not be confirmed yet");
2439
- }
2440
- async function start() {
2441
- logger?.info("Starting unified dev server...");
2442
- config = await loadConfig({ cwd, configFile: configPath });
2443
- resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
2444
- logger?.debug("Loaded config:", config.name);
2445
- await refreshWorkerOnlySurfaceState();
2446
- if (!enableVite && hasWorkerSurfacePaths(mainWorkerSurfacePaths)) {
2447
- const detectedWorkerHandlers = Object.entries(mainWorkerSurfacePaths).filter(([, surfacePath]) => !!surfacePath).map(([surfaceName, surfacePath]) => `${surfaceName}=${surfacePath}`).join(", ");
2448
- logger?.info(`Worker handlers detected: ${detectedWorkerHandlers}`);
2449
- } else if (!enableVite) {
2450
- logger?.warn("No local worker handler entry was found for worker-only mode");
2451
- }
2452
- const remoteCheck = await checkRemoteBindingRequirements(config);
2453
- if (remoteCheck.hasRemoteBindings) {
2454
- logger?.info("");
2455
- logger?.warn("⚠️ Remote-only bindings detected:");
2456
- for (const binding of remoteCheck.remoteBindings) {
2457
- logger?.warn(` • ${binding}`);
2458
- }
2459
- logger?.info("");
2460
- if (remoteCheck.missingAccountId) {
2461
- logger?.warn("⚠️ WARN: accountId is not set in devflare.config.ts");
2462
- logger?.warn(" Remote bindings (AI, Vectorize) require accountId to charge the correct account.");
2463
- logger?.warn(" Add: accountId: 'your-cloudflare-account-id'");
2464
- logger?.info("");
2465
- }
2466
- if (remoteCheck.notLoggedIn) {
2467
- logger?.warn("⚠️ WARN: Not logged in to Wrangler");
2468
- logger?.warn(" Remote bindings require authentication.");
2469
- logger?.warn(" Run: bunx wrangler login");
2470
- logger?.info("");
2471
- }
2472
- if (!remoteCheck.missingAccountId && !remoteCheck.notLoggedIn) {
2473
- logger?.success("✓ Remote binding requirements met");
2474
- logger?.info("");
2475
- }
2476
- }
2477
- const browserBinding = config.bindings?.browser?.binding;
2478
- if (browserBinding) {
2479
- logger?.info(`Starting Browser Rendering shim (binding: ${browserBinding})...`);
2480
- browserShim = createBrowserShim({
2481
- port: browserShimPort,
2482
- host: "127.0.0.1",
2483
- logger,
2484
- verbose
2485
- });
2486
- await browserShim.start();
2487
- }
2488
- const doPattern = config.files?.durableObjects;
2489
- let doResult = null;
2490
- if (typeof doPattern === "string" && doPattern) {
2491
- const outDir = resolve2(cwd, ".devflare/do-bundles");
2492
- doBundler = createDOBundler({
2493
- cwd,
2494
- pattern: doPattern,
2495
- outDir,
2496
- logger,
2497
- onRebuild: async (result) => {
2498
- await reloadMiniflare(result);
2499
- }
2500
- });
2501
- doResult = await doBundler.build();
2502
- currentDoResult = doResult;
2503
- await doBundler.watch();
2504
- }
2505
- currentDoResult = doResult;
2506
- await startMiniflare(doResult);
2507
- await startWorkerSourceWatcher();
2508
- if (enableVite) {
2509
- await startVite();
2510
- } else {
2511
- logger?.info("Vite startup skipped (no local vite.config.* found for this package)");
2512
- }
2513
- await new Promise((r) => setTimeout(r, 1000));
2514
- await runD1Migrations();
2515
- }
2516
- async function stop() {
2517
- if (doBundler) {
2518
- await doBundler.close();
2519
- doBundler = null;
2520
- }
2521
- if (workerSourceWatcher) {
2522
- await workerSourceWatcher.close();
2523
- workerSourceWatcher = null;
2524
- }
2525
- if (miniflare) {
2526
- await miniflare.dispose();
2527
- miniflare = null;
2528
- }
2529
- if (viteProcess) {
2530
- await stopSpawnedProcessTree(viteProcess);
2531
- viteProcess = null;
2532
- }
2533
- if (browserShim) {
2534
- await browserShim.stop();
2535
- browserShim = null;
2536
- }
2537
- }
2538
- function getMiniflare() {
2539
- return miniflare;
2540
- }
2541
- return {
2542
- start,
2543
- stop,
2544
- getMiniflare
2545
- };
2546
- }
2547
- // src/cli/commands/dev.ts
2548
- async function createLogWriter(cwd, options) {
2549
- if (!options.log && !options.logTemp) {
2550
- return null;
2551
- }
2552
- const fs = await import("node:fs");
2553
- let logPath;
2554
- if (options.logTemp) {
2555
- logPath = resolve3(cwd, ".log");
2556
- } else {
2557
- const now = new Date;
2558
- const timestamp = now.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
2559
- logPath = resolve3(cwd, `.log-${timestamp}`);
2560
- }
2561
- const fileStream = fs.createWriteStream(logPath, { flags: "w" });
2562
- const ansiRegex = /\x1b\[[0-9;]*m/g;
2563
- return {
2564
- write(data, source) {
2565
- const str = typeof data === "string" ? data : data.toString();
2566
- if (!str.trim())
2567
- return;
2568
- const timestamp = new Date().toISOString().slice(11, 23);
2569
- const prefix = source ? `[${timestamp}][${source.toUpperCase()}] ` : `[${timestamp}] `;
2570
- const cleanStr = str.replace(ansiRegex, "");
2571
- fileStream.write(prefix + cleanStr + (cleanStr.endsWith(`
2572
- `) ? "" : `
2573
- `));
2574
- },
2575
- close() {
2576
- fileStream.end();
2577
- }
2578
- };
2579
- }
2580
- async function runDevCommand(parsed, logger, options) {
2581
- const cwd = options.cwd || parsed.options.cwd || process.cwd();
2582
- const configPath = parsed.options.config;
2583
- const port = parsed.options.port;
2584
- const logEnabled = parsed.options.log === true;
2585
- const logTempEnabled = parsed.options["log-temp"] === true;
2586
- const persistEnabled = parsed.options.persist === true;
2587
- const debugEnabled = parsed.options.debug === true || process.env.DEVFLARE_DEBUG === "true";
2588
- const verbose = parsed.options.verbose === true || debugEnabled;
2589
- const viteProject = await detectViteProject(cwd);
2590
- const logWriter = await createLogWriter(cwd, {
2591
- log: logEnabled,
2592
- logTemp: logTempEnabled
2593
- });
2594
- if (logWriter) {
2595
- const logFile = logTempEnabled ? ".log" : `.log-{datetime}`;
2596
- logger.info(`\uD83D\uDCDD Logging enabled → ${logFile}`);
2597
- }
2598
- const devLogger = createConsola({
2599
- level: verbose ? 4 : 3
2600
- });
2601
- if (logWriter) {
2602
- const wrapLog = (original, prefix = "") => {
2603
- return (message, ...args) => {
2604
- original(message, ...args);
2605
- const formatted = prefix ? `${prefix} ${[message, ...args].join(" ")}` : [message, ...args].join(" ");
2606
- logWriter.write(formatted);
2607
- };
2608
- };
2609
- Object.assign(devLogger.info, wrapLog(devLogger.info.bind(devLogger)));
2610
- Object.assign(devLogger.error, wrapLog(devLogger.error.bind(devLogger), "[ERROR]"));
2611
- Object.assign(devLogger.warn, wrapLog(devLogger.warn.bind(devLogger), "[WARN]"));
2612
- Object.assign(devLogger.success, wrapLog(devLogger.success.bind(devLogger), "[OK]"));
2613
- Object.assign(devLogger.debug, wrapLog(devLogger.debug.bind(devLogger), "[DEBUG]"));
2614
- }
2615
- try {
2616
- logger.info("");
2617
- if (viteProject.shouldStartVite) {
2618
- logger.info("\uD83D\uDE80 Devflare Unified Dev Server");
2619
- logger.info(" ├─ Vite: Full HMR for frontend");
2620
- logger.info(" ├─ Miniflare: All Cloudflare bindings");
2621
- logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2622
- logger.info(" └─ Bridge: WebSocket RPC connection");
2623
- } else {
2624
- logger.info("\uD83D\uDE80 Devflare Worker Dev Server");
2625
- logger.info(" ├─ Miniflare: All Cloudflare bindings");
2626
- logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2627
- logger.info(" └─ Vite: Disabled (no local vite.config.* found)");
2628
- if (viteProject.wantsViteIntegration) {
2629
- logger.warn("Vite-related dependencies were found, but no local vite.config.* was detected");
2630
- logger.warn("Skipping Vite startup and running in worker-only mode");
2631
- }
2632
- }
2633
- logger.info("");
2634
- const devServer = createDevServer({
2635
- cwd,
2636
- configPath,
2637
- vitePort: port ? parseInt(port, 10) : 5173,
2638
- miniflarePort: 8787,
2639
- enableVite: viteProject.shouldStartVite,
2640
- persist: persistEnabled,
2641
- logger: devLogger,
2642
- verbose,
2643
- debug: debugEnabled
2644
- });
2645
- let isCleaningUp = false;
2646
- const cleanupHandlers = new Map;
2647
- const removeCleanupHandlers = () => {
2648
- for (const [event, handler] of cleanupHandlers) {
2649
- process.off(event, handler);
2650
- }
2651
- cleanupHandlers.clear();
2652
- };
2653
- const cleanup = async (exitCode, reason) => {
2654
- if (isCleaningUp) {
2655
- return;
2656
- }
2657
- isCleaningUp = true;
2658
- removeCleanupHandlers();
2659
- if (reason) {
2660
- const message = reason instanceof Error ? reason.stack ?? reason.message : String(reason);
2661
- logger.error(message);
2662
- }
2663
- logger.info("");
2664
- logger.info("Shutting down...");
2665
- try {
2666
- await devServer.stop();
2667
- } finally {
2668
- logWriter?.close();
2669
- process.exit(exitCode);
2670
- }
2671
- };
2672
- const registerCleanupHandler = (event, handler) => {
2673
- cleanupHandlers.set(event, handler);
2674
- process.on(event, handler);
2675
- };
2676
- registerCleanupHandler("SIGINT", () => {
2677
- cleanup(0);
2678
- });
2679
- registerCleanupHandler("SIGTERM", () => {
2680
- cleanup(0);
2681
- });
2682
- registerCleanupHandler("SIGHUP", () => {
2683
- cleanup(0);
2684
- });
2685
- registerCleanupHandler("uncaughtException", (error) => {
2686
- cleanup(1, error);
2687
- });
2688
- registerCleanupHandler("unhandledRejection", (reason) => {
2689
- cleanup(1, reason);
2690
- });
2691
- await devServer.start();
2692
- await new Promise(() => {});
2693
- return { exitCode: 0 };
2694
- } catch (error) {
2695
- logWriter?.close();
2696
- if (error instanceof Error) {
2697
- logger.error("Dev server failed:", error.message);
2698
- if (verbose) {
2699
- logger.error(error.stack);
2700
- }
2701
- }
2702
- return { exitCode: 1 };
2703
- }
2704
- }
2705
- export {
2706
- runDevCommand
2707
- };