void 0.21.9 → 0.23.0

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 (293) hide show
  1. package/README.md +5 -1
  2. package/dist/{account-cmd-DZZGK80W.mjs → account-cmd-C84Ee8cO.mjs} +5 -5
  3. package/dist/{scan-ClYmX3sa.mjs → application-analysis-BVsVqc11.mjs} +135 -18
  4. package/dist/application-routing-B7QEjqSR.mjs +27 -0
  5. package/dist/{auth-CZuiVsFh.mjs → auth-CCjt0hcq.mjs} +1 -1
  6. package/dist/{auth-DLNN0D3Z.mjs → auth-CSkdO2Bb.mjs} +4 -47
  7. package/dist/{auth-link-B_CeugBw.mjs → auth-link-CioEg6uY.mjs} +5 -5
  8. package/dist/{auth-router-BgEFRuvZ.mjs → auth-router-BsR981d4.mjs} +5 -5
  9. package/dist/{better-auth-shared-rsBGBvWJ.mjs → better-auth-shared-hy6RPh9W.mjs} +13 -2
  10. package/dist/{build-cmd-Dpt0jd-x.mjs → build-cmd-LzvNJORH.mjs} +19 -7
  11. package/dist/{cache-7_UeZdTk.mjs → cache-C4MvnrMH.mjs} +4 -4
  12. package/dist/{cancel-deploy-D23R1RXT.mjs → cancel-deploy-abUxpP2n.mjs} +4 -4
  13. package/dist/{cf-build-output-CGT03qCD.mjs → cf-build-output-BPqOT964.mjs} +141 -63
  14. package/dist/cf-build-output-_0HNWysu.mjs +2 -0
  15. package/dist/cli/cli.mjs +114 -460
  16. package/dist/cli/cloudflare-operation-process.mjs +1103 -0
  17. package/dist/cli/env-schema-probe.d.mts +2 -1
  18. package/dist/cli/env-schema-probe.mjs +3 -3
  19. package/dist/client-Cu7jWiF1.mjs +2 -0
  20. package/dist/{client-QTl6ko_D.mjs → client-RV8NVeB8.mjs} +165 -21
  21. package/dist/{cloudflare-auth-C4_GPZr0.mjs → cloudflare-auth-Qdc7tw8F.mjs} +41 -43
  22. package/dist/{cloudflare-cmd-bSmZ1d5L.mjs → cloudflare-cmd-BcrVyTmJ.mjs} +9 -10
  23. package/dist/cloudflare-config-Bktvwtpf.mjs +182 -0
  24. package/dist/{cloudflare-connect-jAwacxxP.mjs → cloudflare-connect-B8uPZ4nx.mjs} +4 -4
  25. package/dist/{cloudflare-operations-BipGMJ5O.mjs → cloudflare-operations-AiWashgg.mjs} +1 -1
  26. package/dist/{cloudflare-operations-sMZXpk_S.mjs → cloudflare-operations-fxHb-byx.mjs} +118 -203
  27. package/dist/{preset-UHj9ARyP.mjs → cloudflare-process-B-wekeR6.mjs} +93 -6
  28. package/dist/{config-CF69HgXc.d.mts → config-BMHb8RCj.d.mts} +2 -2
  29. package/dist/{config-CafTW6Cz.mjs → config-Br_JZD6u.mjs} +2 -7
  30. package/dist/config-C_XRIPx2.mjs +89 -0
  31. package/dist/{config-s7Xj7tPb.mjs → config-CyQ-wVd7.mjs} +1 -1
  32. package/dist/config-entry.d.mts +1 -1
  33. package/dist/{config-write-BSduPMY8.mjs → config-write-B1f88wJA.mjs} +1 -1
  34. package/dist/{connect-BsUSRzln.mjs → connect-Bd4kJd9U.mjs} +6 -6
  35. package/dist/{create-project-BniEV0OW.mjs → create-project-Boczwj5r.mjs} +1 -1
  36. package/dist/{create-project-DBfFSZKY.mjs → create-project-bMf6ffLZ.mjs} +9 -5
  37. package/dist/{db-hRrvZaq_.mjs → db-8uG64XLl.mjs} +26 -26
  38. package/dist/{delete-D6dZ9B6B.mjs → delete-NvbiyeJf.mjs} +4 -4
  39. package/dist/{deploy-WaAQez1O.mjs → deploy-DzAIwqNU.mjs} +944 -607
  40. package/dist/{deploy-DDz7c8LK.mjs → deploy-bjXdFCtn.mjs} +1 -1
  41. package/dist/{dist-BR1quN_w.mjs → dist-AoCzRTJE.mjs} +226 -60
  42. package/dist/{dist-C5fND3R0.mjs → dist-BuDuKZJv.mjs} +1 -1
  43. package/dist/{dist-Dn6nn2IU.mjs → dist-CTBk70IR.mjs} +42 -42
  44. package/dist/{domain-Dmhvb2oU.mjs → domain-y5Tvydvo.mjs} +5 -5
  45. package/dist/{email-DFi-s2t4.mjs → email-B3umsW75.mjs} +15 -30
  46. package/dist/{env-Csi-tMbT.mjs → env-BS6qYHDb.mjs} +6 -6
  47. package/dist/{env-public-D_6u46fX.d.mts → env-public-BX_r8HR6.d.mts} +2 -1
  48. package/dist/{env-validation-BB4GkLxn.mjs → env-validation-BPn7vk-V.mjs} +18 -71
  49. package/dist/{env-validation-BXge7uyK.mjs → env-validation-DbTg7-ar.mjs} +1 -1
  50. package/dist/{fetch-CXDChK7B.mjs → fetch-BIZJh7vR.mjs} +2 -2
  51. package/dist/{fetch-stream-AOByI7Ki.mjs → fetch-stream-IvCYKyQL.mjs} +24 -4
  52. package/dist/{gen-Dz3X1Qab.mjs → gen-B580--vC.mjs} +5 -5
  53. package/dist/gen-BVaUUumi.mjs +2 -0
  54. package/dist/{github-cmd-BED3_9JD.mjs → github-cmd-v45BdfKX.mjs} +6 -8
  55. package/dist/{handler-BXJTXd02.d.mts → handler-CZ4nAylQ.d.mts} +2 -1
  56. package/dist/{headers-BOg_velo.mjs → headers-DWi2IXWx.mjs} +1 -1
  57. package/dist/help-DofyZuY7.mjs +2 -0
  58. package/dist/{help-DC7gdz7L.mjs → help-daGKjXGk.mjs} +268 -666
  59. package/dist/index.d.mts +1 -1
  60. package/dist/index.mjs +305 -1490
  61. package/dist/info-B97bTX9N.mjs +113 -0
  62. package/dist/{init-WO0JlPx8.mjs → init-Bvy7zrBo.mjs} +51 -20
  63. package/dist/limits-Bq5LG8Id.d.mts +27 -0
  64. package/dist/limits-Cjuk2VPm.mjs +68 -0
  65. package/dist/{link-CsHOinF7.mjs → link-CDqqCjFl.mjs} +5 -5
  66. package/dist/{list-CDb-4bZ1.mjs → list-CZj0dzKY.mjs} +5 -5
  67. package/dist/{live-CKiJilLr.d.mts → live-Chw1eIMv.d.mts} +1 -1
  68. package/dist/{local-d1-CC8sKFGu.mjs → local-d1-2CMnpuW_.mjs} +2 -2
  69. package/dist/login-DYt_An22.mjs +2 -0
  70. package/dist/{login-DvqXsfGs.mjs → login-UZKFM_u7.mjs} +5 -5
  71. package/dist/{logs-BdfiOezj.mjs → logs-CUZ6t9t3.mjs} +5 -5
  72. package/dist/migrate-8_2u55MD.mjs +2 -0
  73. package/dist/{migrate-B8KuoYsO.mjs → migrate-DHul7PRV.mjs} +5 -4
  74. package/dist/{node-BM43oz4G.mjs → node-BkyRWRx8.mjs} +2 -2
  75. package/dist/operator-args-CLgKGlwU.mjs +690 -0
  76. package/dist/{operator-cmd-03819ATr.mjs → operator-cmd-DBA6dl0m.mjs} +69 -23
  77. package/dist/output-Dm_A4Tbv.mjs +81 -0
  78. package/dist/pages/client.d.mts +34 -2
  79. package/dist/pages/client.mjs +59 -3
  80. package/dist/pages/index.d.mts +1 -1
  81. package/dist/pages/index.mjs +3 -3
  82. package/dist/pages/islands-plugin.d.mts +16 -6
  83. package/dist/pages/islands-plugin.mjs +2 -2
  84. package/dist/pages/protocol.d.mts +2 -2
  85. package/dist/pages/protocol.mjs +2 -308
  86. package/dist/{parse-filename-DioPHiR9.mjs → parse-filename-CUbj-1MP.mjs} +35 -1
  87. package/dist/{output-CCH48AMM.mjs → picocolors-BTps1_gs.mjs} +2 -76
  88. package/dist/plan-D1Q5rf-r.mjs +2 -0
  89. package/dist/plan-NPpwZ_kc.mjs +58 -0
  90. package/dist/platform-args-BJdRtlLq.mjs +506 -0
  91. package/dist/platform-args-D3RXyR6h.mjs +2 -0
  92. package/dist/{platform-auth-config-z92h63q5.mjs → platform-auth-config-B56E9YP1.mjs} +4 -4
  93. package/dist/{platform-auth-protection-DeE3yr2R.mjs → platform-auth-protection-BDWO_aER.mjs} +3 -3
  94. package/dist/{platform-auth-recovery-o_340Ixx.mjs → platform-auth-recovery-1gg4CSRe.mjs} +4 -4
  95. package/dist/{platform-cmd-C4ZV3Vpy.mjs → platform-cmd-Bt-1w6pY.mjs} +1 -1
  96. package/dist/{platform-cmd-DNl8WosH.mjs → platform-cmd-DMcQStSc.mjs} +26 -5
  97. package/dist/{platform-domain-CU1JtJkW.mjs → platform-domain-BNkcz0OB.mjs} +36 -9
  98. package/dist/{platform-lifecycle-Bc047IeT.mjs → platform-lifecycle-9OyBALhH.mjs} +850 -276
  99. package/dist/{platform-lifecycle-Bs2qx1E3.mjs → platform-lifecycle-BE6C_jxh.mjs} +1 -1
  100. package/dist/{platform-management-CVpGI9Y5.mjs → platform-management-CjwLVQwN.mjs} +59 -11
  101. package/dist/{platform-management-C8tt6D8h.mjs → platform-management-CnyTdcWX.mjs} +1 -1
  102. package/dist/platform-plans-config-BNGKGr4P.mjs +359 -0
  103. package/dist/{platform-recovery-dvUaptLr.mjs → platform-recovery-D6KSpuFm.mjs} +2 -2
  104. package/dist/{plugin-inference-BMfKRSqE.mjs → plugin-inference-CXWnn79A.mjs} +174 -64
  105. package/dist/{prepare-DyZ-Yok5.mjs → prepare-B5Mkic5u.mjs} +3 -3
  106. package/dist/{prepare-C3kt3Rst.mjs → prepare-DOsL0CC9.mjs} +4 -20
  107. package/dist/prepare-cgvDMtSb.mjs +2 -0
  108. package/dist/{project-cmd-B44I8J_W.mjs → project-cmd-CNzrqvBv.mjs} +30 -16
  109. package/dist/{project-team-BkbYIsXN.mjs → project-team-DQOWPfQT.mjs} +4 -4
  110. package/dist/{project-token-C8xEEQnB.mjs → project-token-CTDYU0Cc.mjs} +4 -4
  111. package/dist/project-zero-trust-BNAkW-_0.mjs +63 -0
  112. package/dist/{protocol-ZH3jP4a7.d.mts → protocol-CjF_iI9X.d.mts} +2 -2
  113. package/dist/protocol-U7bfjHmA.mjs +331 -0
  114. package/dist/{provision-DNtrtaVD.mjs → provision-C4ORE7G7.mjs} +1 -1
  115. package/dist/{provision-BhreDAOS.mjs → provision-CJFgTZY8.mjs} +111 -215
  116. package/dist/{requests-DFyhBMaf.mjs → requests-MhxYnau8.mjs} +4 -4
  117. package/dist/resource-name-C7LVpcRm.mjs +11 -0
  118. package/dist/{rollback-DHHxXZiS.mjs → rollback-CJ6iSDoU.mjs} +5 -5
  119. package/dist/route-url-CG7U-cRN.mjs +15 -0
  120. package/dist/{runner-B8wXwWlo.mjs → runner-CXA9Fh8h.mjs} +1 -1
  121. package/dist/{runner-p-dMs2UN.mjs → runner-Ol0TNjk6.mjs} +2 -2
  122. package/dist/runtime/ai.d.mts +12 -8
  123. package/dist/runtime/ai.mjs +84 -17
  124. package/dist/runtime/better-auth-mysql.mjs +1 -1
  125. package/dist/runtime/better-auth-pg.mjs +1 -1
  126. package/dist/runtime/better-auth.mjs +1 -1
  127. package/dist/runtime/client-react.mjs +2 -2
  128. package/dist/runtime/client-solid.mjs +2 -2
  129. package/dist/runtime/client-svelte.mjs +2 -2
  130. package/dist/runtime/client-vue.mjs +2 -2
  131. package/dist/runtime/client.mjs +2 -2
  132. package/dist/runtime/durable.d.mts +3 -1
  133. package/dist/runtime/durable.mjs +4 -1
  134. package/dist/runtime/email/testing.mjs +1 -1
  135. package/dist/runtime/env-public.d.mts +1 -1
  136. package/dist/runtime/fetch-stream.mjs +1 -1
  137. package/dist/runtime/fetch.mjs +1 -1
  138. package/dist/runtime/handler.d.mts +1 -1
  139. package/dist/runtime/kv.mjs +0 -1
  140. package/dist/runtime/limits.d.mts +2 -0
  141. package/dist/runtime/limits.mjs +2 -0
  142. package/dist/runtime/live-client.d.mts +1 -1
  143. package/dist/runtime/live-server.mjs +25 -16
  144. package/dist/runtime/live.d.mts +1 -1
  145. package/dist/runtime/migration-handler.mjs +62 -42
  146. package/dist/runtime/route-url.d.mts +4 -0
  147. package/dist/runtime/route-url.mjs +2 -0
  148. package/dist/runtime/routing.d.mts +180 -0
  149. package/dist/runtime/routing.mjs +1082 -0
  150. package/dist/runtime/sandbox-container.d.mts +3 -0
  151. package/dist/runtime/sandbox-container.mjs +2 -0
  152. package/dist/runtime/sandbox.d.mts +4 -32
  153. package/dist/runtime/sandbox.mjs +151 -75
  154. package/dist/runtime/sse.mjs +1 -1
  155. package/dist/runtime/validator.d.mts +1 -1
  156. package/dist/runtime/ws-server.d.mts +4 -2
  157. package/dist/runtime/ws-server.mjs +27 -2
  158. package/dist/runtime/ws.d.mts +2 -2
  159. package/dist/runtime/ws.mjs +8 -6
  160. package/dist/sandbox-XZAqzFlG.d.mts +52 -0
  161. package/dist/sandbox-container-Bo0eiFKz.d.mts +73 -0
  162. package/dist/sandbox-container-C6ItmVuN.mjs +281 -0
  163. package/dist/{scan-4tfN-PSn.mjs → scan-C7okrLyM.mjs} +4 -35
  164. package/dist/{secret-DN9sSNiV.mjs → secret-Dli5fP0B.mjs} +6 -6
  165. package/dist/{skills-O6FUaizK.mjs → skills-D1II1Juz.mjs} +1 -1
  166. package/dist/{sse-BaC1jXko.mjs → sse-CQNaDFFV.mjs} +6 -3
  167. package/dist/{subcommand-prompt-BuGYkAkC.mjs → subcommand-prompt-CY1C4fvl.mjs} +2 -2
  168. package/dist/validate-Dq_L3s0S.mjs +2 -0
  169. package/dist/{validate-CIUwFpjB.mjs → validate-ctOrgiS3.mjs} +2 -1
  170. package/dist/{wrangler-BymcxrRa.mjs → wrangler-7K-bW_DL.mjs} +14 -235
  171. package/dist/{ws-BwcqizuH.d.mts → ws-CL1w7GXU.d.mts} +13 -2
  172. package/package.json +48 -33
  173. package/sandbox.Dockerfile +4 -0
  174. package/schema.json +10 -22
  175. package/skills/migrate-vite-cloudflare-to-void/SKILL.md +34 -157
  176. package/skills/void/SKILL.md +50 -133
  177. package/skills/void/docs/guide/ai.md +94 -84
  178. package/skills/void/docs/guide/app-types.md +3 -32
  179. package/skills/void/docs/guide/auth.md +12 -116
  180. package/skills/void/docs/guide/database/d1.md +9 -54
  181. package/skills/void/docs/guide/database/mysql.md +1 -1
  182. package/skills/void/docs/guide/database/postgresql.md +5 -26
  183. package/skills/void/docs/guide/database.md +23 -75
  184. package/skills/void/docs/guide/deployment.md +27 -113
  185. package/skills/void/docs/guide/durable-state.md +43 -18
  186. package/skills/void/docs/guide/edge/headers.md +3 -47
  187. package/skills/void/docs/guide/edge/prerendering.md +5 -20
  188. package/skills/void/docs/guide/edge/redirects.md +11 -64
  189. package/skills/void/docs/guide/edge/revalidation.md +6 -19
  190. package/skills/void/docs/guide/edge/rewrites.md +56 -284
  191. package/skills/void/docs/guide/edge/static-assets.md +23 -72
  192. package/skills/void/docs/guide/email/domains.md +112 -0
  193. package/skills/void/docs/guide/email/receiving.md +139 -0
  194. package/skills/void/docs/guide/email/sending.md +231 -0
  195. package/skills/void/docs/guide/email.md +13 -619
  196. package/skills/void/docs/guide/env-migration.md +11 -11
  197. package/skills/void/docs/guide/env-vars.md +9 -29
  198. package/skills/void/docs/guide/index.md +0 -15
  199. package/skills/void/docs/guide/jobs.md +3 -18
  200. package/skills/void/docs/guide/kv.md +5 -11
  201. package/skills/void/docs/guide/live.md +5 -56
  202. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +78 -125
  203. package/skills/void/docs/guide/pages-routing/head.md +10 -10
  204. package/skills/void/docs/guide/pages-routing/islands.md +6 -36
  205. package/skills/void/docs/guide/pages-routing/layouts.md +6 -128
  206. package/skills/void/docs/guide/pages-routing/loaders.md +3 -19
  207. package/skills/void/docs/guide/pages-routing/markdown.md +13 -171
  208. package/skills/void/docs/guide/pages-routing/overview.md +7 -17
  209. package/skills/void/docs/guide/pages-routing/view-transitions.md +1 -1
  210. package/skills/void/docs/guide/platform/administration/access.md +1 -4
  211. package/skills/void/docs/guide/platform/administration/email.md +35 -8
  212. package/skills/void/docs/guide/platform/administration/operations.md +26 -4
  213. package/skills/void/docs/guide/platform/administration/plans.md +126 -0
  214. package/skills/void/docs/guide/platform/administration/projects.md +5 -2
  215. package/skills/void/docs/guide/platform/administration/zero-trust.md +189 -0
  216. package/skills/void/docs/guide/platform/development/local.md +2 -2
  217. package/skills/void/docs/guide/platform/development/runtime.md +3 -13
  218. package/skills/void/docs/guide/platform/development/schema-ci.md +0 -58
  219. package/skills/void/docs/guide/platform/installation/credentials.md +6 -4
  220. package/skills/void/docs/guide/platform/installation/domains.md +31 -3
  221. package/skills/void/docs/guide/platform/installation/first-deployment.md +6 -0
  222. package/skills/void/docs/guide/platform/installation/maintenance.md +3 -1
  223. package/skills/void/docs/guide/platform/installation/prerequisites.md +21 -16
  224. package/skills/void/docs/guide/platform/installation/setup.md +9 -5
  225. package/skills/void/docs/guide/platform/installation/uninstall.md +17 -2
  226. package/skills/void/docs/guide/platform-administration.md +2 -0
  227. package/skills/void/docs/guide/queues.md +7 -9
  228. package/skills/void/docs/guide/quickstart.md +38 -37
  229. package/skills/void/docs/guide/remote-dev.md +4 -9
  230. package/skills/void/docs/guide/sandboxes.md +78 -41
  231. package/skills/void/docs/guide/server-routing.md +9 -72
  232. package/skills/void/docs/guide/sse.md +4 -18
  233. package/skills/void/docs/guide/ssg.md +3 -15
  234. package/skills/void/docs/guide/ssr.md +14 -62
  235. package/skills/void/docs/guide/storage.md +9 -4
  236. package/skills/void/docs/guide/type-safety.md +3 -14
  237. package/skills/void/docs/guide/typed-fetch.md +3 -7
  238. package/skills/void/docs/guide/websockets.md +68 -40
  239. package/skills/void/docs/integrations/agents.md +3 -3
  240. package/skills/void/docs/integrations/cloudflare.md +85 -316
  241. package/skills/void/docs/integrations/frameworks/analog.md +5 -64
  242. package/skills/void/docs/integrations/frameworks/astro.md +4 -73
  243. package/skills/void/docs/integrations/frameworks/nuxt.md +5 -62
  244. package/skills/void/docs/integrations/frameworks/overview.md +11 -54
  245. package/skills/void/docs/integrations/frameworks/react-router.md +5 -60
  246. package/skills/void/docs/integrations/frameworks/sveltekit.md +6 -65
  247. package/skills/void/docs/integrations/frameworks/tanstack-start.md +4 -62
  248. package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -69
  249. package/skills/void/docs/reference/api/auth.md +156 -0
  250. package/skills/void/docs/reference/api/client.md +87 -0
  251. package/skills/void/docs/reference/api/database.md +95 -0
  252. package/skills/void/docs/reference/api/durable.md +46 -0
  253. package/skills/void/docs/reference/api/env.md +50 -0
  254. package/skills/void/docs/reference/api/handlers.md +254 -0
  255. package/skills/void/docs/reference/api/pages.md +241 -0
  256. package/skills/void/docs/reference/api/plugin.md +39 -0
  257. package/skills/void/docs/reference/api/resources.md +109 -0
  258. package/skills/void/docs/reference/api/rewrites.md +76 -0
  259. package/skills/void/docs/reference/api/types.md +92 -0
  260. package/skills/void/docs/reference/api.md +56 -1218
  261. package/skills/void/docs/reference/cli/auth.md +88 -0
  262. package/skills/void/docs/reference/cli/database.md +128 -0
  263. package/skills/void/docs/reference/cli/deploy.md +85 -0
  264. package/skills/void/docs/reference/cli/domains.md +41 -0
  265. package/skills/void/docs/reference/cli/email.md +129 -0
  266. package/skills/void/docs/reference/cli/generate.md +116 -0
  267. package/skills/void/docs/reference/cli/github.md +189 -0
  268. package/skills/void/docs/reference/cli/platform-config.md +92 -0
  269. package/skills/void/docs/reference/cli/platform-email.md +81 -0
  270. package/skills/void/docs/reference/cli/platform-installation.md +127 -0
  271. package/skills/void/docs/reference/cli/platform-operations.md +90 -0
  272. package/skills/void/docs/reference/cli/platform-users.md +89 -0
  273. package/skills/void/docs/reference/cli/platform-zero-trust.md +45 -0
  274. package/skills/void/docs/reference/cli/platform.md +70 -0
  275. package/skills/void/docs/reference/cli/project.md +214 -0
  276. package/skills/void/docs/reference/cli/secrets.md +76 -0
  277. package/skills/void/docs/reference/cli/setup.md +70 -0
  278. package/skills/void/docs/reference/cli.md +33 -1621
  279. package/skills/void/docs/reference/config.md +26 -32
  280. package/skills/void/docs/reference/resource-inference.md +3 -58
  281. package/skills/void/docs/reference/structure.md +14 -41
  282. package/dist/canonical-json-DuDiiUsQ.mjs +0 -13
  283. package/dist/cli/cf-compat.mjs +0 -968
  284. package/dist/client-4cDVv7BO.mjs +0 -2
  285. package/dist/gen-Vnv2f65C.mjs +0 -2
  286. package/dist/help-DQMfeKMz.mjs +0 -2
  287. package/dist/login-DFQk7rbW.mjs +0 -2
  288. package/dist/migrate-TsHGBnDA.mjs +0 -2
  289. package/dist/plan-BEZ8VJW0.mjs +0 -256
  290. package/dist/plan-DpuOr14e.mjs +0 -2
  291. package/dist/prepare-BZXkjdNe.mjs +0 -2
  292. package/dist/validate-EKmJWxmy.mjs +0 -2
  293. /package/dist/cli/{cf-compat.d.mts → cloudflare-operation-process.d.mts} +0 -0
@@ -1,50 +1,52 @@
1
1
  import { n as __toESM, t as __commonJSMin } from "./rolldown-runtime-DXIUcv95.mjs";
2
2
  import { a as join, l as relative, n as dirname, o as normalize, r as extname, t as basename, u as resolve } from "./dist-BVqXeR_h.mjs";
3
- import { c as import_picocolors$1, d as require_picocolors, i as createSpinner, r as cliTitle } from "./output-CCH48AMM.mjs";
4
- import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-GgcMozYz.mjs";
5
- import { t as findVoidAuthConfig } from "./config-DPByhVck.mjs";
3
+ import { t as require_picocolors } from "./picocolors-BTps1_gs.mjs";
4
+ import { m as needsApplicationDatabase, r as detectFramework } from "./plugin-inference-CXWnn79A.mjs";
5
+ import { n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-GgcMozYz.mjs";
6
+ import { d as isNodeTarget, h as resolveBindingNames, m as readConfig } from "./config-Br_JZD6u.mjs";
7
+ import { n as isIdentifier, r as isLiteral } from "./ast-CPGPjb0V.mjs";
8
+ import { o as scanDurableObjectModules, t as analyzeApplicationSync } from "./application-analysis-BVsVqc11.mjs";
9
+ import { t as collectMigrations } from "./collect-DiTn9yr6.mjs";
10
+ import { o as validateSsrEntry } from "./entry-CF5wvH4w.mjs";
11
+ import { r as hasEmailHandlers } from "./email--tEtMoj_.mjs";
12
+ import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, r as SANDBOX_MIGRATION_TAG, s as requiresSandboxPlatformImage } from "./config-C_XRIPx2.mjs";
6
13
  import { i as trustedCloudflareWorkerAccessHeaders, r as scopedAccessCredentials, t as cfAccessHeaders } from "./cf-access-DRsQRe6k.mjs";
7
- import { a as getApiUrl, c as getToken, l as getTokenSource, o as getProxyUrl, u as isStagingMode } from "./auth-DLNN0D3Z.mjs";
14
+ import { a as getApiUrl, c as getToken, l as getTokenSource, o as getProxyUrl, u as isStagingMode } from "./auth-CSkdO2Bb.mjs";
8
15
  import { r as ensurePlatformProxyAccess, t as ensureCloudflaredToken } from "./cf-access-P0iZnu0q.mjs";
9
16
  import { d as writeProjectConfig, i as readDeploymentPlatform, l as writeDeploymentPlatform, o as readProjectConfig, t as attachSelectedPlatform } from "./project-C0Fqu05O.mjs";
10
- import { d as isNodeTarget, h as resolveBindingNames, m as readConfig, u as getDatabaseDialect } from "./config-CafTW6Cz.mjs";
11
- import { A as writeWranglerPublishedTrafficSnapshot, D as syncWranglerWebSockets, E as syncWranglerDurableObjects, L as materializeCloudflareConfig, M as isSandboxEnabled, N as requiresSandboxPlatformImage, P as resolveSandboxConfig, R as persistCloudflareConfig, S as readWranglerPublishedTrafficSnapshot, T as syncWranglerBindings, V as stashCloudflareDeployPointer, a as LIVE_MIGRATION_TAG, c as bindingEntriesForField, f as findBindingNameOwner, g as readExistingLiveBindingConfig, h as normalizeWranglerCompatibilityFlags, j as SANDBOX_MIGRATION_TAG, k as writeProvisionedWranglerConfig, l as ensureWranglerCompat, m as nextDurableObjectMigrationTag, o as acknowledgeWranglerManagedQueueRemovals, p as hasDurableObjectSqliteMigration, r as FRIENDLY_BINDING_NAMES, w as resolveLiveBindingConfig, x as readWranglerManagedQueueCleanupCandidates } from "./wrangler-BymcxrRa.mjs";
17
+ import { i as persistCloudflareConfig, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-Bktvwtpf.mjs";
18
+ import { A as writeProvisionedWranglerConfig, C as readWranglerPublishedTrafficSnapshot, D as syncWranglerDurableObjects, E as syncWranglerBindings, O as syncWranglerWebSockets, S as readWranglerManagedQueueCleanupCandidates, T as resolveLiveBindingConfig, _ as readExistingLiveBindingConfig, a as LIVE_MIGRATION_TAG, c as bindingEntriesForField, g as normalizeWranglerCompatibilityFlags, h as nextDurableObjectMigrationTag, j as writeWranglerPublishedTrafficSnapshot, l as bindingNamesForField, m as hasDurableObjectSqliteMigration, o as acknowledgeWranglerManagedQueueRemovals, p as findBindingNameOwner, r as FRIENDLY_BINDING_NAMES, u as ensureWranglerCompat } from "./wrangler-7K-bW_DL.mjs";
12
19
  import { i as voidWarn, r as voidLog } from "./log-ChfPKsVd.mjs";
13
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-BOg_velo.mjs";
14
- import { n as isIdentifier, r as isLiteral } from "./ast-CPGPjb0V.mjs";
15
- import { t as scanPages } from "./scan-4tfN-PSn.mjs";
16
- import { a as scanJobsSync, i as scanQueuesSync, r as scanRoutes } from "./scan-ClYmX3sa.mjs";
17
- import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework, s as scanWebSocketRoutesSync } from "./plugin-inference-BMfKRSqE.mjs";
18
- import { t as collectMigrations } from "./collect-DiTn9yr6.mjs";
19
- import { r as buildMigrationPlanBundle } from "./plan-BEZ8VJW0.mjs";
20
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-DWi2IXWx.mjs";
21
+ import { n as buildMigrationPlanBundle } from "./plan-NPpwZ_kc.mjs";
20
22
  import { r as scanWranglerDotenvKeys } from "./dotenv-VQxupEUv.mjs";
21
23
  import { t as discoverSchema } from "./discover-BcPPiF1o.mjs";
22
- import { t as MagicString } from "./dist-BR1quN_w.mjs";
24
+ import { t as MagicString } from "./dist-AoCzRTJE.mjs";
23
25
  import { t as findEnvFile } from "./env-types-C2_bIk_b.mjs";
24
- import { d as formatEnvReport, f as listSelfHostSecretNames, g as validateProdEnv, h as selfHostSchemaFailureMessage, i as SelfHostSecretListError, l as dischargeSelfHostEnvContext, n as SelfHostFirstDeployError, p as readEmittedWorkerVars, r as SelfHostSchemaError, s as deriveSelfHostEnvContextFromVars, t as SelfHostEnvValidationError, u as fetchRemoteSecretNames, v as readGeneratedWranglerConfigFile } from "./env-validation-BB4GkLxn.mjs";
26
+ import { d as formatEnvReport, f as listSelfHostSecretNames, g as validateProdEnv, h as selfHostSchemaFailureMessage, i as SelfHostSecretListError, l as dischargeSelfHostEnvContext, n as SelfHostFirstDeployError, p as readEmittedWorkerVars, r as SelfHostSchemaError, s as deriveSelfHostEnvContextFromVars, t as SelfHostEnvValidationError, u as fetchRemoteSecretNames, v as readGeneratedWranglerConfigFile } from "./env-validation-BPn7vk-V.mjs";
25
27
  import { n as CLOUDFLARE_DEPLOY_BUILD_ENV, o as resolveRemoteMode, s as stripInternalEnvKeys } from "./env-_v349JBd.mjs";
26
- import { o as validateSsrEntry } from "./entry-CF5wvH4w.mjs";
27
- import { E as confirm, I as select, N as note, O as intro, P as outro, W as isCancel, j as log$1 } from "./dist-Dn6nn2IU.mjs";
28
+ import { E as confirm, I as select, N as note, O as intro, P as outro, W as isCancel, j as log$1 } from "./dist-CTBk70IR.mjs";
29
+ import { n as completeApplicationAnalysis, t as analyzeApplication } from "./application-routing-B7QEjqSR.mjs";
28
30
  import { t as writeDrizzleConfig } from "./config-D8MOgiQV.mjs";
29
31
  import { i as runMySqlMigrations } from "./runner-mysql-DmWchSWJ.mjs";
30
32
  import { i as runPgMigrations } from "./runner-pg-D4OPlil-.mjs";
31
33
  import { n as openMigrationValidationDatabase, t as SqliteMigrationError } from "./sqlite-validation-BzKMWnO4.mjs";
32
- import { i as validateMigrations, t as assertJournalCoherence } from "./validate-CIUwFpjB.mjs";
34
+ import { i as validateMigrations, t as assertJournalCoherence } from "./validate-ctOrgiS3.mjs";
33
35
  import { t as renderPrerenderPaths } from "./prerender-render-D9qbLs2f.mjs";
34
- import { a as scanEmailHandlersSync, r as hasEmailHandlers } from "./email--tEtMoj_.mjs";
35
- import { i as migrateBuiltWranglerConfig, n as prepareCfFrameworkBuildOutput, r as prepareCfStaticBuildOutput, t as prepareCfBuildOutput } from "./cf-build-output-CGT03qCD.mjs";
36
+ import { i as migrateBuiltWranglerConfig, n as prepareCfFrameworkBuildOutput, r as prepareCfStaticBuildOutput, t as prepareCfBuildOutput } from "./cf-build-output-BPqOT964.mjs";
36
37
  import { n as getPackageDir } from "./agents-B_pX_HmQ.mjs";
37
- import { i as isExpiredTokenError, n as PlatformClient, o as parsePlatformErrorBody, r as isCliOutdatedError, s as platformErrorMessage, t as PlatformApiError } from "./client-QTl6ko_D.mjs";
38
- import { a as resolveProjectCommand, i as hasBackendFiles, n as detectPreset, o as resolveWranglerArgv, r as formatProjectCommand, t as FRAMEWORK_PRESETS } from "./preset-UHj9ARyP.mjs";
39
- import { i as cloudflareCredentialOverrides, o as ensureCloudflareAuthentication, r as cloudflareAuthNeedsTerminal, s as parseCloudflareAuthSession, t as CloudflareAuthenticationError } from "./cloudflare-auth-C4_GPZr0.mjs";
40
- import { n as writeVoidProjectConfig } from "./config-write-BSduPMY8.mjs";
41
- import { r as discoverFrameworkWranglerFiles, t as migrateLegacyVoidConfig } from "./migrate-B8KuoYsO.mjs";
42
- import { a as createCloudflareEmptyDomainPublisher, i as createCloudflareEmptyCronPublisher } from "./cloudflare-operations-sMZXpk_S.mjs";
38
+ import { i as isExpiredTokenError, n as PlatformClient, o as parsePlatformErrorBody, r as isCliOutdatedError, s as platformErrorMessage, t as PlatformApiError } from "./client-RV8NVeB8.mjs";
39
+ import { a as detectPreset, c as resolveCloudflareOperationArgv, i as FRAMEWORK_PRESETS, l as resolveProjectCommand, o as formatProjectCommand, s as hasBackendFiles, t as createCloudflareProcessRunner } from "./cloudflare-process-B-wekeR6.mjs";
40
+ import { a as parseCloudflareAuthSession, i as ensureCloudflareAuthentication, n as cloudflareCredentialOverrides, t as CloudflareAuthenticationError } from "./cloudflare-auth-Qdc7tw8F.mjs";
41
+ import { n as writeVoidProjectConfig } from "./config-write-B1f88wJA.mjs";
42
+ import { r as discoverFrameworkWranglerFiles, t as migrateLegacyVoidConfig } from "./migrate-DHul7PRV.mjs";
43
+ import { a as createCloudflareEmptyDomainPublisher, i as createCloudflareEmptyCronPublisher } from "./cloudflare-operations-fxHb-byx.mjs";
43
44
  import { t as formatCloudflareUserMessage } from "./cloudflare-user-output-Q-k0QoNQ.mjs";
44
- import { a as promptProjectSetupAction, i as promptProjectSelection, r as promptAndCreateProject } from "./create-project-DBfFSZKY.mjs";
45
+ import { c as import_picocolors$1, i as createSpinner, r as cliTitle } from "./output-Dm_A4Tbv.mjs";
46
+ import { a as promptProjectSelection, i as reportProjectCreationWarnings, o as promptProjectSetupAction, r as promptAndCreateProject } from "./create-project-bMf6ffLZ.mjs";
45
47
  import { t as getRequestedProjectSlug } from "./resolve-project-BG5ORGID.mjs";
46
48
  import { t as collectGitDeployMetadata } from "./git-metadata-D-gurjzG.mjs";
47
- import { t as promptForLoginToken } from "./login-DvqXsfGs.mjs";
49
+ import { t as promptForLoginToken } from "./login-UZKFM_u7.mjs";
48
50
  import { createRequire } from "node:module";
49
51
  import { copyFileSync, cpSync, createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
50
52
  import { fileURLToPath } from "node:url";
@@ -59,6 +61,8 @@ import { Minimatch } from "minimatch";
59
61
  import { CloudflareWorkerInspectionError, activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, encodeCloudflareVersionMessage, extractWorkersDevSubdomain, normalizeWorkersDevSubdomain, parseRootWranglerConfig, parseRootWranglerConfig as parseRootWranglerConfig$1, readBindingFieldPresence, readCloudflareTriggerConfig, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig } from "@void/deploy-cloudflare";
60
62
  import { getWorkerAssetsDir, getWorkerBundleDir, getWorkerConfigPath, readBuildOutput } from "@cloudflare/build-output-utils";
61
63
  import ignore from "ignore";
64
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
65
+ import mime from "mime";
62
66
  import { hash } from "blake3-jit";
63
67
  //#region src/asset-routing.ts
64
68
  function isApiRoutePattern(pattern) {
@@ -238,127 +242,84 @@ function deriveAddresses(handlers, domain, mode) {
238
242
  routes
239
243
  };
240
244
  }
241
- function parseWhoamiJson(stdout) {
242
- const start = stdout.indexOf("{");
243
- if (start < 0) return null;
244
- try {
245
- const parsed = JSON.parse(stdout.slice(start));
246
- if (!parsed || typeof parsed !== "object" || typeof parsed.loggedIn !== "boolean") return null;
247
- return parsed;
248
- } catch {
249
- return null;
250
- }
245
+ function parseWhoamiJson(data) {
246
+ if (!isRecord(data) || typeof data.loggedIn !== "boolean") return null;
247
+ if (data.tokenPermissions !== void 0 && (!Array.isArray(data.tokenPermissions) || data.tokenPermissions.some((scope) => typeof scope !== "string"))) return null;
248
+ if (data.accounts !== void 0 && (!Array.isArray(data.accounts) || data.accounts.some((account) => !isRecord(account) || typeof account.id !== "string" || typeof account.name !== "string"))) return null;
249
+ return data;
251
250
  }
252
- function parseRoutingSettings(stdout) {
253
- const name = /^Email Routing for (\S+):\s*$/m.exec(stdout)?.[1];
254
- const enabled = /^\s*Enabled:\s+(true|false)\s*$/m.exec(stdout)?.[1];
255
- const status = /^\s*Status:\s+(\S+)\s*$/m.exec(stdout)?.[1];
256
- const tag = /^\s*Tag:\s+([0-9a-f]{32})\s*$/m.exec(stdout)?.[1];
257
- if (!name || !enabled || !status || !tag) return null;
258
- return {
259
- name,
260
- enabled: enabled === "true",
261
- status,
262
- tag
263
- };
251
+ function isRecord(value) {
252
+ return value !== null && typeof value === "object" && !Array.isArray(value);
264
253
  }
265
- /**
266
- * `wrangler email routing rules list <zone>` stdout (4.131 `src/email-routing/rules/list.ts`):
267
- *
268
- * Rule: <id>
269
- * Name: <name|(none)>
270
- * Enabled: true
271
- * Matchers: to:support@void.cloud
272
- * Actions: worker:acme-support
273
- * Priority: 0
274
- *
275
- * Catch-all rule: disabled, action: drop
276
- * (use `wrangler email routing rules get catch-all` to view details)
277
- *
278
- * With no rule at all: `No routing rules found.` / no custom rule: `No custom routing
279
- * rules found.`. Both forms are pinned by fixture.
280
- */
281
- function parseRoutingRules(stdout) {
282
- const lines = stdout.split(/\r?\n/);
254
+ function readRoutingSettings(data) {
255
+ return isRecord(data) && typeof data.name === "string" && data.name.length > 0 && typeof data.enabled === "boolean" && typeof data.status === "string" && data.status.length > 0 && typeof data.tag === "string" && /^[0-9a-f]{32}$/.test(data.tag) ? {
256
+ name: data.name,
257
+ enabled: data.enabled,
258
+ status: data.status,
259
+ tag: data.tag
260
+ } : null;
261
+ }
262
+ function readRoutingRules(data) {
263
+ if (!Array.isArray(data)) return null;
283
264
  const rules = [];
284
265
  let catchAll = null;
285
- let sawSentinel = false;
286
- let current = null;
287
- for (const raw of lines) {
288
- const line = raw.trimEnd();
289
- if (/^No (custom )?routing rules found\.$/.test(line)) {
290
- sawSentinel = true;
291
- continue;
292
- }
293
- const catchAllMatch = /^Catch-all rule: (enabled|disabled), action: (.*)$/.exec(line);
294
- if (catchAllMatch) {
266
+ for (const entry of data) {
267
+ if (!isRecord(entry) || typeof entry.enabled !== "boolean" || !Array.isArray(entry.actions) || entry.actions.length === 0 || !Array.isArray(entry.matchers) || entry.matchers.length === 0) return null;
268
+ const actions = [];
269
+ for (const action of entry.actions) {
270
+ if (!isRecord(action) || typeof action.type !== "string") return null;
271
+ if (action.type === "drop") actions.push("drop");
272
+ else if (action.type === "worker" || action.type === "forward") {
273
+ const values = typeof action.value === "string" ? [action.value] : action.value;
274
+ if (!Array.isArray(values) || values.length === 0 || !values.every((value) => typeof value === "string" && value.length > 0)) return null;
275
+ if (action.type === "worker") actions.push("worker:" + values.join(","));
276
+ else actions.push("forward:" + values.join(","));
277
+ } else return null;
278
+ }
279
+ const to = [];
280
+ let all = false;
281
+ for (const matcher of entry.matchers) {
282
+ if (!isRecord(matcher)) return null;
283
+ if (matcher.type === "all") all = true;
284
+ else if (matcher.type === "literal" && matcher.field === "to" || matcher.type === "to") {
285
+ if (typeof matcher.value !== "string" || matcher.value.length === 0) return null;
286
+ to.push(matcher.value.toLowerCase());
287
+ } else return null;
288
+ }
289
+ if (all) {
290
+ if (catchAll || entry.matchers.length !== 1) return null;
295
291
  catchAll = {
296
- enabled: catchAllMatch[1] === "enabled",
297
- actions: splitActionList(catchAllMatch[2] ?? "")
298
- };
299
- current = null;
300
- continue;
301
- }
302
- const ruleMatch = /^Rule: (\S+)$/.exec(line);
303
- if (ruleMatch) {
304
- current = {
305
- id: ruleMatch[1],
306
- name: "",
307
- enabled: false,
308
- to: [],
309
- actions: []
292
+ enabled: entry.enabled,
293
+ actions
310
294
  };
311
- rules.push(current);
312
- continue;
313
- }
314
- if (!current) continue;
315
- const field = /^\s{2}(Name|Enabled|Matchers|Actions|Priority):\s*(.*)$/.exec(line);
316
- if (!field) continue;
317
- const value = (field[2] ?? "").trim();
318
- switch (field[1]) {
319
- case "Name":
320
- current.name = value;
321
- break;
322
- case "Enabled":
323
- current.enabled = value === "true";
324
- break;
325
- case "Matchers":
326
- current.to = value.split(", ").filter((m) => m.startsWith("to:")).map((m) => m.slice(3).toLowerCase());
327
- break;
328
- case "Actions": current.actions = splitActionList(value);
295
+ } else {
296
+ if (typeof entry.id !== "string" || entry.id.length === 0 || entry.name !== void 0 && typeof entry.name !== "string") return null;
297
+ rules.push({
298
+ id: entry.id,
299
+ name: entry.name ?? "",
300
+ enabled: entry.enabled,
301
+ to,
302
+ actions
303
+ });
329
304
  }
330
305
  }
331
- if (!sawSentinel && rules.length === 0 && !catchAll) return null;
332
306
  return {
333
307
  rules,
334
308
  catchAll
335
309
  };
336
310
  }
337
- function splitActionList(value) {
338
- return value.split(", ").map((a) => a.trim()).filter(Boolean);
339
- }
340
- function parseSendingSettings(stdout) {
341
- const name = /^Email Sending for (\S+):\s*$/m.exec(stdout)?.[1];
342
- const enabled = /^\s*Enabled:\s+(true|false)\s*$/m.exec(stdout)?.[1];
343
- const tag = /^\s*Tag:\s+([0-9a-f]{32})\s*$/m.exec(stdout)?.[1];
344
- if (!name || !enabled || !tag) return null;
345
- return {
346
- name,
347
- enabled: enabled === "true",
311
+ function readSendingSettings(data) {
312
+ if (!isRecord(data)) return null;
313
+ const tag = data.tag ?? data.id;
314
+ return typeof data.name === "string" && data.name.length > 0 && typeof data.enabled === "boolean" && typeof tag === "string" && /^[0-9a-f]{32}$/.test(tag) ? {
315
+ name: data.name,
316
+ enabled: data.enabled,
348
317
  tag
349
- };
350
- }
351
- /** wrangler renders an `APIError` note as `<message> [code: <n>]`; extract the code. */
352
- function wranglerApiErrorCode(text) {
353
- const match = /\[code:\s*(\d+)\]/.exec(stripAnsi(text));
354
- return match ? Number(match[1]) : null;
318
+ } : null;
355
319
  }
356
- const ANSI_SGR_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
357
- function stripAnsi(text) {
358
- return text.replace(ANSI_SGR_RE, "");
320
+ function providerErrorCode(result) {
321
+ return result.errorCode ?? result.errors?.find((error) => error.code !== void 0)?.code ?? null;
359
322
  }
360
- const ZONE_NOT_FOUND_RE = /Could not find (?:a )?zone for `([^`]+)`/;
361
- const SENDING_NOT_FOUND_RE = /No sending subdomain found for `([^`]+)`/;
362
323
  function isHardFailure(result) {
363
324
  return Boolean(result.signal) || Boolean(result.error);
364
325
  }
@@ -415,8 +376,10 @@ async function preflightEmail(inputs) {
415
376
  const domain = emailFromDomain(inputs.from);
416
377
  if (!domain) throw new Error(`email.from must be an address like you@mail.example.com (got ${JSON.stringify(inputs.from)})`);
417
378
  const apiBaseUrl = inputs.apiBaseUrl ?? "https://api.cloudflare.com/client/v4";
418
- const extra = inputs.wranglerArgs ?? [];
419
- const run = async (args) => inputs.runWrangler([...args, ...extra], { capture: true });
379
+ const run = async (operation) => inputs.runWrangler({
380
+ ...operation,
381
+ ...inputs.configPath ? { configPath: inputs.configPath } : {}
382
+ }, { capture: true });
420
383
  const usageHandlers = [...inputs.handlers.addresses.map((d) => `email/${d.filePath}`), ...inputs.handlers.defaultHandler ? [`email/${inputs.handlers.defaultHandler}`] : []];
421
384
  const pre = {
422
385
  from: inputs.from,
@@ -479,8 +442,8 @@ async function preflightEmail(inputs) {
479
442
  });
480
443
  };
481
444
  for (const conflict of inputs.configConflicts ?? []) block("all", conflict);
482
- const whoami = await run(["whoami", "--json"]);
483
- const who = isHardFailure(whoami) || whoami.status !== 0 ? null : parseWhoamiJson(whoami.stdout);
445
+ const whoami = await run({ type: "auth.whoami" });
446
+ const who = isHardFailure(whoami) || whoami.status !== 0 ? null : parseWhoamiJson(whoami.data);
484
447
  if (!who) {
485
448
  pre.session.state = "unknown";
486
449
  block("all", "could not read the Cloudflare session — run `void cloudflare login`");
@@ -507,22 +470,20 @@ async function preflightEmail(inputs) {
507
470
  let settings = null;
508
471
  let settingsMiss = false;
509
472
  if (pre.session.state === "ok") for (const candidate of zoneCandidates(domain)) {
510
- const result = await run([
511
- "email",
512
- "routing",
513
- "settings",
514
- candidate
515
- ]);
473
+ const result = await run({
474
+ type: "email.routing.settings",
475
+ domain: candidate
476
+ });
516
477
  if (isHardFailure(result)) {
517
478
  settingsMiss = true;
518
479
  break;
519
480
  }
481
+ if (result.status === 0 && result.data === null) continue;
520
482
  if (result.status !== 0) {
521
- if (ZONE_NOT_FOUND_RE.test(stripAnsi(result.stderr))) continue;
522
483
  settingsMiss = true;
523
484
  break;
524
485
  }
525
- settings = parseRoutingSettings(result.stdout);
486
+ settings = readRoutingSettings(result.data);
526
487
  if (!settings) settingsMiss = true;
527
488
  break;
528
489
  }
@@ -619,14 +580,11 @@ async function preflightEmail(inputs) {
619
580
  else if (pre.domainMx.state === "unknown") block("all", `could not read the MX records of ${domain} — unknown — run \`void email setup --platform cloudflare\``);
620
581
  }
621
582
  pre.derived = deriveAddresses(inputs.handlers, domain, mode);
622
- const rulesResult = await run([
623
- "email",
624
- "routing",
625
- "rules",
626
- "list",
627
- apex
628
- ]);
629
- const rules = isHardFailure(rulesResult) || rulesResult.status !== 0 ? null : parseRoutingRules(rulesResult.stdout);
583
+ const rulesResult = await run({
584
+ type: "email.routing.rules.list",
585
+ domain: apex
586
+ });
587
+ const rules = isHardFailure(rulesResult) || rulesResult.status !== 0 ? null : readRoutingRules(rulesResult.data);
630
588
  if (!rules) {
631
589
  pre.rules.state = "unknown";
632
590
  block("all", `could not read Email Routing rules — unknown — run \`void email setup --platform cloudflare\`${tokenHint}`);
@@ -656,18 +614,16 @@ async function preflightEmail(inputs) {
656
614
  }
657
615
  const foreign = new Set(pre.rules.foreign.map((f) => f.address));
658
616
  pre.addresses = pre.derived.addresses.filter((a) => !foreign.has(a));
659
- const sendingResult = await run([
660
- "email",
661
- "sending",
662
- "settings",
617
+ const sendingResult = await run({
618
+ type: "email.sending.settings",
663
619
  domain
664
- ]);
620
+ });
665
621
  if (isHardFailure(sendingResult)) pre.sending.state = "unknown";
622
+ else if (sendingResult.status === 0 && sendingResult.data === null) pre.sending.state = "pending";
666
623
  else if (sendingResult.status === 0) {
667
- const parsed = parseSendingSettings(sendingResult.stdout);
624
+ const parsed = readSendingSettings(sendingResult.data);
668
625
  pre.sending.state = parsed ? parsed.enabled ? "ready" : "pending" : "unknown";
669
- } else if (SENDING_NOT_FOUND_RE.test(stripAnsi(sendingResult.stderr))) pre.sending.state = "pending";
670
- else pre.sending.state = "unknown";
626
+ } else pre.sending.state = "unknown";
671
627
  if (pre.sending.state === "unknown") block("sending", `could not read Email Sending settings — unknown — run \`void email setup --platform cloudflare\`${tokenHint}`);
672
628
  else if (pre.sending.state === "pending" && pre.sending.dnsConflicts.length > 0) block("sending", `${pre.sending.dnsConflicts.join(", ")} already exists; onboarding writes its own _dmarc (p=reject) and DKIM records and would conflict — remove them or keep sending off`);
673
629
  const names = (inputs.existingSendEmail ?? []).map((e) => typeof e?.name === "string" ? e.name : "");
@@ -847,8 +803,10 @@ async function applyEmailSetup(pre, inputs) {
847
803
  }
848
804
  const apex = pre.apex;
849
805
  const apiBaseUrl = inputs.apiBaseUrl ?? "https://api.cloudflare.com/client/v4";
850
- const extra = inputs.wranglerArgs ?? [];
851
- const run = async (args) => inputs.runWrangler([...args, ...extra], { capture: true });
806
+ const run = async (operation) => inputs.runWrangler({
807
+ ...operation,
808
+ ...inputs.configPath ? { configPath: inputs.configPath } : {}
809
+ }, { capture: true });
852
810
  let bearer;
853
811
  let bearerFailed = false;
854
812
  let bearerObtained = false;
@@ -877,17 +835,15 @@ async function applyEmailSetup(pre, inputs) {
877
835
  const apexRoutingOff = pre.mode === "subdomain" && pre.routing.enabled !== true;
878
836
  if (!result.routingReady) {
879
837
  if (pre.mode === "apex") {
880
- const enable = await run([
881
- "email",
882
- "routing",
883
- "enable",
884
- apex
885
- ]);
838
+ const enable = await run({
839
+ type: "email.routing.enable",
840
+ domain: apex
841
+ });
886
842
  if (!isHardFailure(enable) && enable.status === 0) {
887
843
  result.routingReady = true;
888
844
  lines.push(` ✔ routing ${apex} enabled`);
889
845
  } else {
890
- const code = wranglerApiErrorCode(enable.stderr);
846
+ const code = providerErrorCode(enable);
891
847
  lines.push(` ✘ routing could not enable Email Routing on ${apex}${code ? ` [code: ${code}]${CF_EMAIL_ERROR_CODES[code] ? ` — ${CF_EMAIL_ERROR_CODES[code]}` : ""}` : ""}`);
892
848
  }
893
849
  } else if (apexRoutingOff) {
@@ -918,13 +874,11 @@ async function applyEmailSetup(pre, inputs) {
918
874
  const sendingBlocked = pre.blockers.filter((b) => b.scope === "sending");
919
875
  if (!result.sendingReady && sendingBlocked.length > 0) for (const b of sendingBlocked) lines.push(` ! sending ${b.message}`);
920
876
  else if (!result.sendingReady) {
921
- const enable = await run([
922
- "email",
923
- "sending",
924
- "enable",
925
- pre.domain
926
- ]);
927
- const code = isHardFailure(enable) ? null : wranglerApiErrorCode(enable.stderr);
877
+ const enable = await run({
878
+ type: "email.sending.enable",
879
+ domain: pre.domain
880
+ });
881
+ const code = isHardFailure(enable) ? null : providerErrorCode(enable);
928
882
  if (!isHardFailure(enable) && (enable.status === 0 || code === 2040)) {
929
883
  result.sendingReady = true;
930
884
  lines.push(` ✔ sending ${pre.domain} onboarded`);
@@ -974,37 +928,6 @@ async function createNodeEmailDns() {
974
928
  };
975
929
  }
976
930
  //#endregion
977
- //#region src/durable/scan.ts
978
- const MODULE_PATTERN = /\.(?:[cm]?[jt]s)$/;
979
- function words(value) {
980
- return value.split(/[^a-zA-Z0-9]+/).filter(Boolean);
981
- }
982
- function scanDurableObjectModules(root) {
983
- const directory = join(root, "durable-objects");
984
- if (!existsSync(directory)) return [];
985
- const modules = readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && MODULE_PATTERN.test(entry.name) && !entry.name.endsWith(".d.ts")).sort((a, b) => a.name.localeCompare(b.name)).map((entry) => {
986
- const parts = words(entry.name.replace(MODULE_PATTERN, ""));
987
- if (parts.length === 0) throw new Error(`durable: cannot derive a name from durable-objects/${entry.name}.`);
988
- const classBase = parts.map((part) => part[0].toUpperCase() + part.slice(1)).join("");
989
- const bindingName = parts.join("_").toUpperCase();
990
- const slug = parts.join("-").toLowerCase();
991
- return {
992
- filePath: join(directory, entry.name),
993
- bindingName,
994
- className: `${classBase}DurableObject`,
995
- migrationTag: `void-durable-${slug}-v1`
996
- };
997
- });
998
- const bindings = /* @__PURE__ */ new Set();
999
- const classes = /* @__PURE__ */ new Set();
1000
- for (const module of modules) {
1001
- if (bindings.has(module.bindingName) || classes.has(module.className)) throw new Error(`durable: duplicate generated name for '${module.filePath}'. Rename one of the durable-objects files.`);
1002
- bindings.add(module.bindingName);
1003
- classes.add(module.className);
1004
- }
1005
- return modules;
1006
- }
1007
- //#endregion
1008
931
  //#region ../../node_modules/.pnpm/ci-info@4.4.0/node_modules/ci-info/vendors.json
1009
932
  var require_vendors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1010
933
  module.exports = [
@@ -1857,7 +1780,38 @@ async function prerenderPagesNode(options) {
1857
1780
  return result;
1858
1781
  }
1859
1782
  //#endregion
1783
+ //#region src/shared/version-metadata.ts
1784
+ /** Build-only marker: an existing-site handoff must not add Worker bindings. */
1785
+ const CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV = "VOID_CLOUDFLARE_PRESERVE_BINDINGS";
1786
+ const DEFAULT_VERSION_METADATA_BINDING = "__VOID_VERSION_METADATA";
1787
+ /** Keep the authored singleton name, and reject a cross-type binding collision. */
1788
+ function readVersionMetadataBinding(config) {
1789
+ const name = bindingNamesForField("version_metadata", config.version_metadata)[0];
1790
+ if (!name) return;
1791
+ const owner = findBindingNameOwner(config, "version_metadata", name);
1792
+ if (owner) throw new Error(`Cloudflare version metadata binding ${name} conflicts with ${owner}.`);
1793
+ return name;
1794
+ }
1795
+ /** Self-metadata adds no remote resource or plan entitlement. */
1796
+ function selectNativeVersionMetadataBinding(config, options) {
1797
+ if (!options.nativeBuild) return;
1798
+ const authored = readVersionMetadataBinding(config);
1799
+ if (authored || options.preserveBindings) return authored;
1800
+ const owner = findBindingNameOwner(config, "version_metadata", DEFAULT_VERSION_METADATA_BINDING);
1801
+ if (owner) throw new Error(`Cloudflare version metadata binding ${DEFAULT_VERSION_METADATA_BINDING} conflicts with ${owner}.`);
1802
+ return DEFAULT_VERSION_METADATA_BINDING;
1803
+ }
1804
+ //#endregion
1860
1805
  //#region src/cli/cf-version-api.ts
1806
+ var CloudflareApiError = class extends Error {
1807
+ status;
1808
+ code;
1809
+ constructor(message, status, code) {
1810
+ super(message);
1811
+ this.status = status;
1812
+ this.code = code;
1813
+ }
1814
+ };
1861
1815
  function object(value) {
1862
1816
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1863
1817
  }
@@ -1957,38 +1911,181 @@ function bindingsFromOutput(env, workerName, activeSecrets, explicitSecrets) {
1957
1911
  ...value.worker !== workerName ? { script_name: requiredString(value.worker, name) } : {}
1958
1912
  });
1959
1913
  break;
1914
+ case "unsafe:durable_object_namespace":
1915
+ if (Object.keys(value).some((key) => ![
1916
+ "type",
1917
+ "class_name",
1918
+ "dev"
1919
+ ].includes(key))) throw new Error(`Cloudflare binding ${name} is not a verified local Durable Object.`);
1920
+ bindings.push({
1921
+ name,
1922
+ type: "durable_object_namespace",
1923
+ class_name: requiredString(value.class_name, name)
1924
+ });
1925
+ break;
1926
+ case "queue":
1927
+ bindings.push({
1928
+ name,
1929
+ type: "queue",
1930
+ queue_name: requiredString(value.name, name),
1931
+ ...value.deliveryDelay === void 0 ? {} : { delivery_delay: value.deliveryDelay }
1932
+ });
1933
+ break;
1934
+ case "ai":
1935
+ case "browser":
1936
+ case "images":
1937
+ bindings.push({
1938
+ name,
1939
+ type: value.type
1940
+ });
1941
+ break;
1942
+ case "hyperdrive":
1943
+ bindings.push({
1944
+ name,
1945
+ type: "hyperdrive",
1946
+ id: requiredString(value.id, name)
1947
+ });
1948
+ break;
1949
+ case "worker":
1950
+ bindings.push({
1951
+ name,
1952
+ type: "service",
1953
+ service: requiredString(value.worker, name),
1954
+ ...value.exportName === void 0 ? {} : { entrypoint: value.exportName },
1955
+ ...value.props === void 0 ? {} : { props: value.props }
1956
+ });
1957
+ break;
1958
+ case "workflow":
1959
+ bindings.push({
1960
+ name,
1961
+ type: "workflow",
1962
+ workflow_name: requiredString(value.name, name),
1963
+ ...value.worker === workerName ? { class_name: requiredString(value.exportName, name) } : { script_name: requiredString(value.worker, name) }
1964
+ });
1965
+ break;
1966
+ case "send-email":
1967
+ bindings.push({
1968
+ name,
1969
+ type: "send_email",
1970
+ ...value.destinationAddress === void 0 ? {} : { destination_address: value.destinationAddress },
1971
+ ...value.allowedDestinationAddresses === void 0 ? {} : { allowed_destination_addresses: value.allowedDestinationAddresses },
1972
+ ...value.allowedSenderAddresses === void 0 ? {} : { allowed_sender_addresses: value.allowedSenderAddresses }
1973
+ });
1974
+ break;
1975
+ case "analytics-engine-dataset":
1976
+ bindings.push({
1977
+ name,
1978
+ type: "analytics_engine",
1979
+ ...value.name === void 0 ? {} : { dataset: value.name }
1980
+ });
1981
+ break;
1982
+ case "vectorize":
1983
+ bindings.push({
1984
+ name,
1985
+ type: "vectorize",
1986
+ index_name: requiredString(value.name, name)
1987
+ });
1988
+ break;
1989
+ case "dispatch-namespace": {
1990
+ const outbound = object(value.outbound);
1991
+ bindings.push({
1992
+ name,
1993
+ type: "dispatch_namespace",
1994
+ namespace: requiredString(value.namespace, name),
1995
+ ...outbound ? { outbound: {
1996
+ worker: { service: requiredString(outbound.worker, name) },
1997
+ ...Array.isArray(outbound.parameters) ? { params: outbound.parameters.map((parameter) => ({ name: parameter })) } : {}
1998
+ } } : {}
1999
+ });
2000
+ break;
2001
+ }
1960
2002
  case "assets":
1961
2003
  bindings.push({
1962
2004
  name,
1963
2005
  type: "assets"
1964
2006
  });
1965
2007
  break;
2008
+ case "version-metadata":
2009
+ bindings.push({
2010
+ name,
2011
+ type: "version_metadata"
2012
+ });
2013
+ break;
1966
2014
  default: throw new Error(`Cloudflare binding ${name} cannot be uploaded with pinned secrets.`);
1967
2015
  }
1968
2016
  }
1969
2017
  return bindings;
1970
2018
  }
1971
- function exportsFromOutput(exports) {
2019
+ function exportsFromOutput(exports, bindings) {
1972
2020
  if (exports === void 0) return;
1973
2021
  const entries = object(exports);
1974
2022
  if (!entries) throw new Error("Cloudflare Durable Object exports were not readable.");
1975
2023
  const result = {};
2024
+ const workflows = /* @__PURE__ */ new Map();
1976
2025
  for (const [name, raw] of Object.entries(entries)) {
1977
2026
  const value = object(raw);
2027
+ if (value?.type === "workflow") {
2028
+ const workflowName = requiredString(value.name, `Workflow export ${name} name`);
2029
+ if (workflows.has(workflowName)) throw new Error(`Cloudflare Workflow ${workflowName} has multiple local exports.`);
2030
+ workflows.set(workflowName, name);
2031
+ result[name] = {
2032
+ type: "workflow",
2033
+ name: workflowName
2034
+ };
2035
+ continue;
2036
+ }
2037
+ if (value?.type === "worker") {
2038
+ result[name] = {
2039
+ type: "worker",
2040
+ ...value.cache === void 0 ? {} : { cache: value.cache }
2041
+ };
2042
+ continue;
2043
+ }
1978
2044
  if (!value || value.type !== "durable-object") throw new Error(`Cloudflare export ${name} cannot be uploaded with pinned secrets.`);
1979
2045
  if (value.state === "deleted") result[name] = {
1980
2046
  type: "durable-object",
1981
2047
  state: "deleted"
1982
2048
  };
1983
- else if (value.storage === "sqlite" || value.storage === "legacy-kv") result[name] = {
1984
- type: "durable-object",
1985
- storage: value.storage,
1986
- state: "created"
1987
- };
1988
- else throw new Error(`Cloudflare Durable Object export ${name} has no storage type.`);
2049
+ else if (value.state === "renamed" || value.state === "transferred") {
2050
+ const renamed = value.state === "renamed";
2051
+ result[name] = {
2052
+ type: "durable-object",
2053
+ state: value.state,
2054
+ [renamed ? "renamed_to" : "transferred_to"]: requiredString(value[renamed ? "renamedTo" : "transferredTo"], name)
2055
+ };
2056
+ } else if (value.storage === "sqlite" || value.storage === "legacy-kv") {
2057
+ if (value.state !== void 0 && !["created", "expecting-transfer"].includes(String(value.state))) throw new Error(`Cloudflare Durable Object export ${name} has an unsupported state.`);
2058
+ result[name] = {
2059
+ type: "durable-object",
2060
+ storage: value.storage,
2061
+ state: value.state ?? "created",
2062
+ ...value.container === void 0 ? {} : { container: requiredString(value.container, name) },
2063
+ ...value.state === "expecting-transfer" ? { transfer_from: requiredString(value.transferFrom, name) } : {}
2064
+ };
2065
+ } else throw new Error(`Cloudflare Durable Object export ${name} has no storage type.`);
2066
+ }
2067
+ for (const binding of bindings) {
2068
+ if (binding.type !== "workflow") continue;
2069
+ const localExport = workflows.get(String(binding.workflow_name));
2070
+ if (localExport !== void 0 && (binding.script_name !== void 0 || binding.class_name !== localExport)) throw new Error(`Cloudflare Workflow binding ${binding.name} conflicts with its local export.`);
1989
2071
  }
1990
2072
  return result;
1991
2073
  }
2074
+ async function preflightWorkflowExports(options) {
2075
+ for (const raw of Object.values(options.exports ?? {})) {
2076
+ const entry = object(raw);
2077
+ if (entry?.type !== "workflow") continue;
2078
+ const name = requiredString(entry.name, "Workflow name");
2079
+ let workflow;
2080
+ try {
2081
+ workflow = await apiResult(`${options.accountUrl}/workflows/${encodeURIComponent(name)}`, options.token, "GET", void 0, options.verifyUploader);
2082
+ } catch (cause) {
2083
+ if (cause instanceof CloudflareApiError && cause.status === 404 && cause.code === 10200) continue;
2084
+ throw cause;
2085
+ }
2086
+ if (workflow.name !== name || workflow.script_name !== options.workerName || typeof workflow.class_name !== "string" || !workflow.class_name) throw new Error(`Cloudflare Workflow ${name} is not owned by this Worker.`);
2087
+ }
2088
+ }
1992
2089
  function assetConfigFromOutput(value) {
1993
2090
  if (value === void 0) return;
1994
2091
  const config = object(value);
@@ -2025,11 +2122,211 @@ async function apiResult(url, token, method, body, verifyUploader, timeout = 12e
2025
2122
  if (!response.ok || parsed?.success !== true || !result) {
2026
2123
  const error = Array.isArray(parsed?.errors) ? object(parsed.errors[0]) : null;
2027
2124
  const code = Number.isSafeInteger(error?.code) ? ` [code: ${error.code}]` : "";
2028
- const message = typeof error?.message === "string" ? ` ${error.message}` : "";
2029
- throw new Error(`Cloudflare version upload failed (${response.status})${code}.${message}`);
2125
+ throw new CloudflareApiError(`Cloudflare version upload failed (${response.status})${code}.`, response.status, typeof error?.code === "number" ? error.code : void 0);
2030
2126
  }
2031
2127
  return result;
2032
2128
  }
2129
+ function assertContainerApplicationIdentity(application, plan, namespace) {
2130
+ if (application.id !== namespace || application.name !== plan.name || application.scheduling_policy !== "durable_object" || object(application.durable_objects)?.namespace_id !== namespace) throw new Error(`Cloudflare Container ${plan.name} application identity does not match the uploaded namespace.`);
2131
+ }
2132
+ function assertContainerApplicationSettings(application, plan) {
2133
+ const desired = object(plan.settings.configuration) ?? {};
2134
+ const current = object(application.configuration) ?? {};
2135
+ const changedConfiguration = Object.entries(desired).some(([key, value]) => {
2136
+ if (key === "wrangler_ssh") {
2137
+ const ssh = object(current.wrangler_ssh) ?? {};
2138
+ const expected = object(value) ?? {};
2139
+ return !isDeepStrictEqual(ssh.enabled ?? false, expected.enabled) || expected.enabled === true && expected.port !== void 0 && !isDeepStrictEqual(ssh.port, expected.port);
2140
+ }
2141
+ if (key === "experimental_flags" && Array.isArray(value)) {
2142
+ const flags = current.experimental_flags ?? [];
2143
+ return !Array.isArray(flags) || !isDeepStrictEqual([...flags].sort(), [...value].sort());
2144
+ }
2145
+ return !isDeepStrictEqual(current[key] ?? [], value);
2146
+ });
2147
+ const logs = object(object(plan.settings.observability)?.logs);
2148
+ const currentLogs = object(object(application.observability)?.logs);
2149
+ if (changedConfiguration || logs !== null && !isDeepStrictEqual(currentLogs?.enabled ?? false, logs.enabled)) throw new Error(`Cloudflare Container ${plan.name} settings differ from the live application. Version-aware deployment cannot safely change these shared settings. Keep the existing Container settings in your configuration, or update this Container explicitly in the Cloudflare dashboard before retrying.`);
2150
+ }
2151
+ async function preflightContainerApplications(plans, bindings, accountUrl, workerName, token, verifyUploader) {
2152
+ for (const plan of plans) {
2153
+ const matches = bindings.map(object).filter((binding) => binding?.type === "durable_object_namespace" && binding.class_name === plan.className && (binding.script_name === void 0 || binding.script_name === workerName) && binding.environment === void 0 && binding.dispatch_namespace === void 0 && binding.namespace_id !== void 0);
2154
+ const namespaces = new Set(matches.map((binding) => binding.namespace_id));
2155
+ if (namespaces.size > 1) throw new Error(`Cloudflare Container ${plan.name} active namespace was ambiguous.`);
2156
+ const namespace = [...namespaces][0];
2157
+ if (namespace === void 0) {
2158
+ if (Object.keys(plan.images).length === 0) throw new Error(`Cloudflare Container ${plan.name} has no images and no verified existing namespace application.`);
2159
+ continue;
2160
+ }
2161
+ if (typeof namespace !== "string" || !/^[a-f0-9]{32}$/.test(namespace)) throw new Error("The active Container namespace ID was invalid.");
2162
+ try {
2163
+ const application = await apiResult(`${accountUrl}/containers/applications/${encodeURIComponent(namespace)}`, token, "GET", void 0, verifyUploader);
2164
+ assertContainerApplicationIdentity(application, plan, namespace);
2165
+ assertContainerApplicationSettings(application, plan);
2166
+ } catch (cause) {
2167
+ if (!(cause instanceof CloudflareApiError) || cause.status !== 404 || Object.keys(plan.images).length === 0) throw cause;
2168
+ }
2169
+ }
2170
+ }
2171
+ function containerPlansFromOutput(containers, exports, bindings) {
2172
+ const plans = [];
2173
+ for (const { config } of containers) {
2174
+ if (config.schedulingPolicy !== "durable-object") throw new Error("Scheduler-managed Cloudflare Containers cannot be uploaded with pinned secrets.");
2175
+ const classes = Object.entries(exports ?? {}).filter(([, value]) => object(value)?.container === config.name);
2176
+ if (classes.length !== 1) throw new Error(`Cloudflare Container ${config.name} needs exactly one local SQLite export.`);
2177
+ const [className, raw] = classes[0];
2178
+ const declaration = object(raw);
2179
+ const localBindings = bindings.filter((binding) => binding.type === "durable_object_namespace" && binding.class_name === className && binding.script_name === void 0);
2180
+ if (declaration.storage !== "sqlite" || localBindings.length === 0) throw new Error(`Cloudflare Container ${config.name} needs a local SQLite Durable Object binding.`);
2181
+ const unsafe = object(config.unsafe);
2182
+ const configuration = object(unsafe?.configuration);
2183
+ if (unsafe && Object.keys(unsafe).some((key) => key !== "configuration") || configuration && Object.keys(configuration).some((key) => key !== "experimental_flags")) throw new Error(`Cloudflare Container ${config.name} has an unsupported setting.`);
2184
+ const settings = {
2185
+ ...configuration || config.ssh || config.authorizedKeys ? { configuration: {
2186
+ ...configuration ?? {},
2187
+ ...config.ssh ? { wrangler_ssh: config.ssh } : {},
2188
+ ...config.authorizedKeys ? { authorized_keys: config.authorizedKeys.map(({ name, publicKey }) => ({
2189
+ name,
2190
+ public_key: publicKey
2191
+ })) } : {}
2192
+ } } : {},
2193
+ ...config.observability ? { observability: { logs: { enabled: config.observability.enabled === true || config.observability.logs?.enabled === true } } } : {}
2194
+ };
2195
+ plans.push({
2196
+ name: config.name,
2197
+ className,
2198
+ images: config.images ?? {},
2199
+ settings
2200
+ });
2201
+ }
2202
+ for (const [name, raw] of Object.entries(exports ?? {})) {
2203
+ const container = object(raw)?.container;
2204
+ if (container !== void 0 && !plans.some((plan) => plan.name === container && plan.className === name)) throw new Error(`Cloudflare export ${name} references a Container outside the verified output.`);
2205
+ }
2206
+ return plans;
2207
+ }
2208
+ async function dockerImageDigest(image, timeout, verifyUploader) {
2209
+ if (!verifyUploader()) throw new Error("The Cloudflare uploader changed during deployment.");
2210
+ const stdout = await new Promise((resolve, reject) => {
2211
+ execFile(process.env.WRANGLER_DOCKER_BIN ?? "docker", [
2212
+ "image",
2213
+ "inspect",
2214
+ "--format",
2215
+ "{{json .RepoDigests}}",
2216
+ image
2217
+ ], {
2218
+ encoding: "utf8",
2219
+ timeout,
2220
+ maxBuffer: 1048576,
2221
+ windowsHide: true
2222
+ }, (error, stdout) => error ? reject(/* @__PURE__ */ new Error("Cloudflare Container image digest could not be verified after push.")) : resolve(stdout));
2223
+ });
2224
+ const digests = JSON.parse(stdout);
2225
+ const repository = image.replace(/:[^/:]+$/, "");
2226
+ const matching = Array.isArray(digests) ? digests.filter((digest) => typeof digest === "string" && digest.startsWith(`${repository}@sha256:`) && /^[a-f0-9]{64}$/.test(digest.slice(digest.lastIndexOf(":") + 1))) : [];
2227
+ if (matching.length !== 1) throw new Error("Cloudflare Container image digest could not be verified after push.");
2228
+ return matching[0];
2229
+ }
2230
+ async function prepareContainerMetadata(options) {
2231
+ if (options.plans.length === 0) return [];
2232
+ const deadline = Date.now() + 9e5;
2233
+ const remaining = () => {
2234
+ const duration = deadline - Date.now();
2235
+ if (duration <= 0) throw new Error("Cloudflare Container image preparation timed out.");
2236
+ return duration;
2237
+ };
2238
+ const prepared = /* @__PURE__ */ new Map();
2239
+ const result = [];
2240
+ for (const plan of options.plans) {
2241
+ const images = {};
2242
+ for (const [name, raw] of Object.entries(plan.images)) {
2243
+ const source = object(raw);
2244
+ const reference = requiredString(source.localReference ?? source.reference, `Container ${plan.name} image`);
2245
+ let image = prepared.get(reference);
2246
+ if (!image) {
2247
+ image = reference;
2248
+ if (source.localReference !== void 0) {
2249
+ if (!options.runCf || !options.verifyUploader()) throw new Error("The verified Cloudflare Container image uploader is unavailable.");
2250
+ const pushed = await options.runCf([
2251
+ "containers",
2252
+ "push",
2253
+ "--tag",
2254
+ reference
2255
+ ], {
2256
+ cwd: options.directory,
2257
+ capture: true,
2258
+ timeoutMs: remaining(),
2259
+ maxOutputBytes: 16777216
2260
+ });
2261
+ if (pushed.status !== 0 || pushed.signal || pushed.error) throw new Error("Cloudflare Container image push failed.");
2262
+ const matches = [...pushed.stdout.matchAll(/Pushed image:\s*(\S+)/g)];
2263
+ const remote = matches.length === 1 ? matches[0][1] : "";
2264
+ if (!remote.startsWith(`registry.cloudflare.com/${options.accountId}/`) || [...remote].some((character) => character.charCodeAt(0) <= 32 || character.charCodeAt(0) === 127)) throw new Error("Cloudflare Container image push did not return the verified account registry.");
2265
+ image = await dockerImageDigest(remote, Math.min(12e4, remaining()), options.verifyUploader);
2266
+ }
2267
+ for (;;) {
2268
+ const ready = await apiResult(`${options.accountUrl}/containers/image-preparations`, options.token, "POST", { image }, options.verifyUploader, Math.min(12e4, remaining()));
2269
+ if (ready.image !== image) throw new Error("Cloudflare Container image preparation returned another image.");
2270
+ if (ready.status === "ready") break;
2271
+ if (ready.status !== "pending") throw new Error("Cloudflare Container image preparation failed or returned an unsupported state.");
2272
+ await setTimeout$1(Math.min(2e3, remaining()));
2273
+ }
2274
+ prepared.set(reference, image);
2275
+ }
2276
+ images[name] = image;
2277
+ }
2278
+ result.push({
2279
+ name: plan.name,
2280
+ class_name: plan.className,
2281
+ ...Object.keys(images).length ? { images } : {}
2282
+ });
2283
+ }
2284
+ return result;
2285
+ }
2286
+ async function ensureContainerApplications(options) {
2287
+ if (options.plans.length === 0) return;
2288
+ const deadline = Date.now() + 6e4;
2289
+ let version;
2290
+ for (;;) try {
2291
+ version = await apiResult(`${options.accountUrl}/workers/scripts/${encodeURIComponent(options.workerName)}/versions/${encodeURIComponent(options.versionId)}`, options.token, "GET", void 0, options.verifyUploader, Math.max(1, Math.min(3e4, deadline - Date.now())));
2292
+ break;
2293
+ } catch (cause) {
2294
+ if (!(cause instanceof CloudflareApiError) || cause.code !== 100146 || Date.now() >= deadline) throw cause;
2295
+ await setTimeout$1(Math.min(2e3, deadline - Date.now()));
2296
+ }
2297
+ const versionBindings = object(version.resources)?.bindings;
2298
+ if (version.id !== options.versionId || !Array.isArray(versionBindings)) throw new Error("The uploaded Container namespace could not be verified.");
2299
+ const metadata = object(object(version.resources)?.script_runtime)?.containers;
2300
+ if (!Array.isArray(metadata) || metadata.length !== options.containers.length || options.containers.some((expected) => {
2301
+ const matching = metadata.map(object).filter((entry) => entry !== null && entry.name === expected.name && entry.class_name === expected.class_name);
2302
+ return matching.length !== 1 || !isDeepStrictEqual(matching[0].images ?? {}, expected.images ?? {});
2303
+ })) throw new Error("The uploaded Worker version did not retain the verified Container image metadata.");
2304
+ for (const plan of options.plans) {
2305
+ const matches = versionBindings.map(object).filter((binding) => binding?.type === "durable_object_namespace" && binding.class_name === plan.className && (binding.script_name === void 0 || binding.script_name === options.workerName) && binding.environment === void 0 && binding.dispatch_namespace === void 0);
2306
+ const namespaces = new Set(matches.map((binding) => binding.namespace_id));
2307
+ if (matches.length === 0 || namespaces.size !== 1) throw new Error(`Cloudflare Container ${plan.name} has no unambiguous uploaded local namespace.`);
2308
+ const namespace = requiredString([...namespaces][0], `Container ${plan.name} namespace`);
2309
+ if (!/^[a-f0-9]{32}$/.test(namespace)) throw new Error("The uploaded Container namespace ID was invalid.");
2310
+ const url = `${options.accountUrl}/containers/applications/${encodeURIComponent(namespace)}`;
2311
+ let application;
2312
+ try {
2313
+ application = await apiResult(url, options.token, "GET", void 0, options.verifyUploader);
2314
+ } catch (cause) {
2315
+ if (!(cause instanceof CloudflareApiError) || cause.status !== 404) throw cause;
2316
+ application = await apiResult(`${options.accountUrl}/containers/applications`, options.token, "POST", {
2317
+ name: plan.name,
2318
+ scheduling_policy: "durable_object",
2319
+ durable_objects: { namespace_id: namespace },
2320
+ ...plan.settings
2321
+ }, options.verifyUploader);
2322
+ }
2323
+ assertContainerApplicationIdentity(application, plan, namespace);
2324
+ assertContainerApplicationSettings(application, plan);
2325
+ const confirmed = await apiResult(url, options.token, "GET", void 0, options.verifyUploader);
2326
+ assertContainerApplicationIdentity(confirmed, plan, namespace);
2327
+ assertContainerApplicationSettings(confirmed, plan);
2328
+ }
2329
+ }
2033
2330
  async function uploadAssets(options) {
2034
2331
  if (!options.output.assetsDirectory) return;
2035
2332
  const directory = getWorkerAssetsDir(options.output.directory);
@@ -2081,7 +2378,7 @@ async function uploadAssets(options) {
2081
2378
  for (const hash of bucket) {
2082
2379
  const path = byHash.get(hash);
2083
2380
  if (!path) throw new Error("Cloudflare requested an asset outside the verified Worker output.");
2084
- form.append(hash, readFileSync(path).toString("base64"));
2381
+ form.append(hash, new File([readFileSync(path).toString("base64")], hash, { type: mime.getType(path) ?? "application/null" }));
2085
2382
  }
2086
2383
  const uploaded = await apiResult(`${options.accountUrl}/workers/assets/upload?base64=true`, uploadJwt, "POST", form, options.verifyUploader);
2087
2384
  if (uploaded.jwt !== void 0) completionJwt = requiredString(uploaded.jwt, "asset completion token");
@@ -2092,7 +2389,8 @@ async function uploadAssets(options) {
2092
2389
  async function uploadPinnedCloudflareVersion(options) {
2093
2390
  const activeSecrets = options.output.activeSecrets;
2094
2391
  if (!activeSecrets) throw new Error("The active Worker secret source was not verified.");
2095
- const config = (await readBuildOutput(options.output.directory)).workers.default.config;
2392
+ const built = await readBuildOutput(options.output.directory);
2393
+ const config = built.workers.default.config;
2096
2394
  if (config.name !== options.workerName || !config.manifest || config.manifest.type !== "complete") throw new Error("The verified Cloudflare Worker output changed before upload.");
2097
2395
  const unsupported = Object.keys(config).filter((key) => !(/* @__PURE__ */ new Set([
2098
2396
  "name",
@@ -2116,6 +2414,8 @@ async function uploadPinnedCloudflareVersion(options) {
2116
2414
  const explicitSecrets = options.secretsFilePath ? object(JSON.parse(readFileSync(options.secretsFilePath, "utf8"))) : {};
2117
2415
  if (!explicitSecrets) throw new Error("The explicit Cloudflare secrets file was not readable.");
2118
2416
  const bindings = bindingsFromOutput(config.env ?? {}, options.workerName, activeSecrets, explicitSecrets);
2417
+ const exports = exportsFromOutput(config.exports, bindings);
2418
+ const containerPlans = containerPlansFromOutput(built.containers, exports, bindings);
2119
2419
  const active = await apiResult(`${options.apiBaseUrl.replace(/\/$/, "")}/accounts/${encodeURIComponent(options.accountId)}/workers/workers/${encodeURIComponent(options.workerName)}/versions/${encodeURIComponent(activeSecrets.versionId)}`, options.token, "GET", void 0, options.verifyUploader);
2120
2420
  if (active.id !== activeSecrets.versionId || !Array.isArray(active.bindings)) throw new Error("The active Cloudflare Worker bindings could not be verified.");
2121
2421
  const activeBindingNames = /* @__PURE__ */ new Set();
@@ -2135,7 +2435,6 @@ async function uploadPinnedCloudflareVersion(options) {
2135
2435
  }
2136
2436
  }
2137
2437
  if (activeSecrets.bindings.some(({ name }) => !activeBindingNames.has(name))) throw new Error("The active Cloudflare Worker secrets changed before upload.");
2138
- const exports = exportsFromOutput(config.exports);
2139
2438
  const assetConfig = assetConfigFromOutput(config.assets);
2140
2439
  if (bindings.some((binding) => binding.type === "assets") && !options.output.assetsDirectory) throw new Error("The Cloudflare Worker has an assets binding without verified assets.");
2141
2440
  const modules = [];
@@ -2149,6 +2448,23 @@ async function uploadPinnedCloudflareVersion(options) {
2149
2448
  });
2150
2449
  }
2151
2450
  const accountUrl = `${options.apiBaseUrl.replace(/\/$/, "")}/accounts/${encodeURIComponent(options.accountId)}`;
2451
+ await preflightWorkflowExports({
2452
+ exports,
2453
+ accountUrl,
2454
+ workerName: options.workerName,
2455
+ token: options.token,
2456
+ verifyUploader: options.verifyUploader
2457
+ });
2458
+ await preflightContainerApplications(containerPlans, active.bindings, accountUrl, options.workerName, options.token, options.verifyUploader);
2459
+ const containers = await prepareContainerMetadata({
2460
+ plans: containerPlans,
2461
+ directory: options.output.directory,
2462
+ accountUrl,
2463
+ accountId: options.accountId,
2464
+ token: options.token,
2465
+ verifyUploader: options.verifyUploader,
2466
+ runCf: options.runCf
2467
+ });
2152
2468
  const jwt = await uploadAssets({
2153
2469
  output: options.output,
2154
2470
  accountUrl,
@@ -2163,14 +2479,30 @@ async function uploadPinnedCloudflareVersion(options) {
2163
2479
  compatibility_flags: config.compatibilityFlags,
2164
2480
  bindings,
2165
2481
  ...exports ? { exports } : {},
2482
+ ...containers.length ? { containers } : {},
2166
2483
  ...jwt ? { assets: {
2167
2484
  jwt,
2168
2485
  ...assetConfig ? { config: assetConfig } : {}
2169
2486
  } } : {},
2170
2487
  annotations: { "workers/message": options.message ?? "" }
2171
2488
  }, options.verifyUploader);
2489
+ const versionId = uploaded.id;
2490
+ if (typeof versionId !== "string" || !/^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(versionId)) throw new Error("Cloudflare accepted the Worker upload but returned an invalid version identity. Read back the Worker deployment before retrying.");
2491
+ try {
2492
+ await ensureContainerApplications({
2493
+ plans: containerPlans,
2494
+ containers,
2495
+ accountUrl,
2496
+ workerName: options.workerName,
2497
+ versionId,
2498
+ token: options.token,
2499
+ verifyUploader: options.verifyUploader
2500
+ });
2501
+ } catch {
2502
+ throw new Error(`Cloudflare Worker version ${versionId} was uploaded${options.atomic ? " and may already be published" : ""}, but its Container applications could not be verified. Read back the Worker deployment and Container identities before retrying.`);
2503
+ }
2172
2504
  return {
2173
- id: requiredString(uploaded.id, "Worker Version ID"),
2505
+ id: versionId,
2174
2506
  urls: Array.isArray(uploaded.urls) ? uploaded.urls.filter((url) => typeof url === "string") : []
2175
2507
  };
2176
2508
  }
@@ -2183,10 +2515,6 @@ function failed(message) {
2183
2515
  stderr: message
2184
2516
  };
2185
2517
  }
2186
- function flag(args, name) {
2187
- const index = args.indexOf(name);
2188
- return index < 0 ? void 0 : args[index + 1];
2189
- }
2190
2518
  function parseJson(value) {
2191
2519
  try {
2192
2520
  return JSON.parse(value);
@@ -2255,15 +2583,12 @@ function createCfResourceVerifier(options) {
2255
2583
  const name = identifier("name");
2256
2584
  if (!name) return failed(`Cloudflare Queue binding ${bindingName} has no name.`);
2257
2585
  if (!options.verifyCompatibilityRunner()) return failed("The Cloudflare resource verifier changed during deployment.");
2258
- const found = await Promise.resolve(options.runWrangler([
2259
- "queues",
2260
- "info",
2586
+ const found = await Promise.resolve(options.runWrangler({
2587
+ type: "queues.info",
2261
2588
  name,
2262
- "-c",
2263
- options.configPath,
2264
- "--env="
2265
- ], { capture: true }));
2266
- if (found.status !== 0 || found.signal || found.error || found.stdout.trim() !== `Queue Name: ${name}`) return failed(`Cloudflare Queue ${name} is unavailable; refusing automatic provisioning.`);
2589
+ configPath: options.configPath
2590
+ }, { capture: true }));
2591
+ if (found.status !== 0 || found.signal || found.error || !record(found.data) || found.data.name !== name) return failed(`Cloudflare Queue ${name} is unavailable; refusing automatic provisioning.`);
2267
2592
  continue;
2268
2593
  }
2269
2594
  const resource = value.type === "r2" ? {
@@ -2312,7 +2637,7 @@ function createCfResourceVerifier(options) {
2312
2637
  cwd: directory,
2313
2638
  capture: true
2314
2639
  });
2315
- const body = parseJson(result.stdout);
2640
+ const body = result.data ?? parseJson(result.stdout);
2316
2641
  if (result.status !== 0 || result.signal || result.error || !record(body) || body[resource.response] !== name) return failed(`Cloudflare ${resource.kind} ${name} is unavailable; refusing automatic provisioning.`);
2317
2642
  }
2318
2643
  return {
@@ -2337,6 +2662,8 @@ function createCfCommand(options) {
2337
2662
  if (env.HTTP_PROXY || env.HTTPS_PROXY || env.http_proxy || env.https_proxy) env.NODE_USE_ENV_PROXY = "1";
2338
2663
  if (options.token) env.CLOUDFLARE_API_TOKEN = options.token;
2339
2664
  return await new Promise((resolve) => {
2665
+ const bounded = run.timeoutMs !== void 0;
2666
+ const processGroup = bounded && process.platform !== "win32";
2340
2667
  const child = spawn(process.execPath, [cf, ...args], {
2341
2668
  cwd: run.cwd,
2342
2669
  env,
@@ -2346,17 +2673,49 @@ function createCfCommand(options) {
2346
2673
  "ignore",
2347
2674
  "pipe",
2348
2675
  "pipe"
2349
- ]
2676
+ ],
2677
+ detached: processGroup
2350
2678
  });
2351
2679
  const stdout = [];
2352
2680
  const stderr = [];
2353
2681
  let bytes = 0;
2354
2682
  let error = null;
2683
+ const stop = () => {
2684
+ if (processGroup && child.pid) try {
2685
+ process.kill(-child.pid, "SIGKILL");
2686
+ } catch (cause) {
2687
+ if (cause.code !== "ESRCH") child.kill("SIGKILL");
2688
+ }
2689
+ else if (bounded && process.platform === "win32" && child.pid) execFile("taskkill", [
2690
+ "/PID",
2691
+ String(child.pid),
2692
+ "/T",
2693
+ "/F"
2694
+ ], {
2695
+ windowsHide: true,
2696
+ timeout: 1e4
2697
+ }, (cause) => {
2698
+ if (cause) child.kill("SIGKILL");
2699
+ });
2700
+ else child.kill("SIGKILL");
2701
+ };
2702
+ const interrupted = () => {
2703
+ error = "Cloudflare command interrupted.";
2704
+ stop();
2705
+ };
2706
+ if (bounded) {
2707
+ process.on("SIGINT", interrupted);
2708
+ process.on("SIGTERM", interrupted);
2709
+ }
2710
+ const timer = run.timeoutMs === void 0 ? void 0 : setTimeout(() => {
2711
+ error = "Cloudflare command timed out.";
2712
+ stop();
2713
+ }, run.timeoutMs);
2355
2714
  const collect = (chunks, chunk) => {
2356
2715
  bytes += chunk.length;
2357
- if (bytes > 268435456) {
2358
- error = "Cloudflare command output exceeded 256 MiB.";
2359
- child.kill();
2716
+ if (bytes > (run.maxOutputBytes ?? 268435456)) {
2717
+ error = "Cloudflare command output exceeded its verified limit.";
2718
+ stop();
2360
2719
  } else chunks.push(chunk);
2361
2720
  };
2362
2721
  child.stdout.on("data", (chunk) => collect(stdout, chunk));
@@ -2365,10 +2724,23 @@ function createCfCommand(options) {
2365
2724
  error = cause.message;
2366
2725
  });
2367
2726
  child.on("close", (status, signal) => {
2727
+ clearTimeout(timer);
2728
+ if (bounded) {
2729
+ process.off("SIGINT", interrupted);
2730
+ process.off("SIGTERM", interrupted);
2731
+ }
2732
+ const output = Buffer.concat(stdout).toString("utf8");
2733
+ const diagnostic = Buffer.concat(stderr).toString("utf8");
2734
+ const completed = typeof status === "number" && signal == null && error == null;
2735
+ const code = completed ? (output + "\n" + diagnostic).match(/\[(?:code:\s*)?(\d{4,})\]/)?.[1] : void 0;
2736
+ const missingUploadTarget = completed && status !== 0 && code === void 0 && args[0] === "workers" && args[1] === "versions" && args[2] === "create" && (output + "\n" + diagnostic).replace(/\s+/g, " ").includes("You cannot upload a new version of a Worker that does not yet exist.");
2368
2737
  resolve({
2369
2738
  status: typeof status === "number" ? status : 1,
2370
- stdout: Buffer.concat(stdout).toString("utf8"),
2371
- stderr: Buffer.concat(stderr).toString("utf8").replace(/\[(\d{4,})\]/g, "[code: $1]"),
2739
+ stdout: output,
2740
+ stderr: diagnostic,
2741
+ ...code ? { errorCode: Number(code) } : {},
2742
+ ...missingUploadTarget ? { failure: "not-found" } : {},
2743
+ ...completed && status !== 0 ? { errors: [{ message: output + "\n" + diagnostic }] } : {},
2372
2744
  signal,
2373
2745
  error
2374
2746
  });
@@ -2382,10 +2754,31 @@ function createCfCommand(options) {
2382
2754
  */
2383
2755
  function createCfVersionRunner(options) {
2384
2756
  const { output, workerName, runCf } = options;
2385
- const run = async (args, capture, cwd = output.directory) => await runCf(args, {
2386
- cwd,
2387
- capture
2388
- });
2757
+ const run = async (args, capture, cwd = output.directory) => {
2758
+ const result = await runCf(args, {
2759
+ cwd,
2760
+ capture
2761
+ });
2762
+ return {
2763
+ ...result,
2764
+ data: result.data ?? parseJson(result.stdout)
2765
+ };
2766
+ };
2767
+ const uploadedResult = (result) => {
2768
+ if (result.status !== 0 || result.signal || result.error) return result;
2769
+ if (record(result.data) && typeof result.data.versionId === "string") return result;
2770
+ const versionId = result.stdout.match(/(?:Worker|Current) Version ID:\s*([0-9a-f]{8}-[0-9a-f-]{27})/i)?.[1];
2771
+ const previewUrl = result.stdout.match(/Version Preview(?: Alias)? URL:\s*(https:\/\/\S+)/i)?.[1];
2772
+ const workersDevUrl = result.stdout.match(/(https:\/\/[a-z0-9.-]+\.workers\.dev(?:\/[^\s]*)?)/i)?.[1];
2773
+ return {
2774
+ ...result,
2775
+ data: versionId ? {
2776
+ versionId,
2777
+ ...previewUrl ? { previewUrl } : {},
2778
+ ...workersDevUrl ? { workersDevUrl } : {}
2779
+ } : void 0
2780
+ };
2781
+ };
2389
2782
  const verifyResources = async (directory) => {
2390
2783
  try {
2391
2784
  return await options.verifyResources(directory);
@@ -2393,7 +2786,7 @@ function createCfVersionRunner(options) {
2393
2786
  return failed("Cloudflare resources could not be verified before upload.");
2394
2787
  }
2395
2788
  };
2396
- const uploadPinnedVersion = async (args, atomic) => {
2789
+ const uploadPinnedVersion = async (operation, atomic) => {
2397
2790
  if (!options.pinnedUpload || !options.pinnedUpload.verifyUploader()) return failed("The version-aware Cloudflare uploader is unavailable.");
2398
2791
  const token = await options.pinnedUpload.getToken();
2399
2792
  if (!token) return failed("Cloudflare session has no bearer for version-aware upload.");
@@ -2404,23 +2797,28 @@ function createCfVersionRunner(options) {
2404
2797
  apiBaseUrl: options.pinnedUpload.apiBaseUrl,
2405
2798
  workerName,
2406
2799
  token,
2407
- ...flag(args, "--message") ? { message: flag(args, "--message") } : {},
2408
- ...flag(args, "--secrets-file") ? { secretsFilePath: flag(args, "--secrets-file") } : {},
2800
+ ...operation.message ? { message: operation.message } : {},
2801
+ ...operation.secretsFilePath ? { secretsFilePath: operation.secretsFilePath } : {},
2409
2802
  atomic,
2410
- verifyUploader: options.pinnedUpload.verifyUploader
2803
+ verifyUploader: options.pinnedUpload.verifyUploader,
2804
+ runCf
2411
2805
  });
2412
2806
  const preview = version.urls.find((url) => /^https:\/\//.test(url));
2413
2807
  return {
2414
2808
  status: 0,
2415
- stdout: `Worker Version ID: ${version.id}\n${preview ? `Version Preview URL: ${preview}\n` : ""}`,
2809
+ stdout: "",
2810
+ data: {
2811
+ versionId: version.id,
2812
+ ...preview ? { previewUrl: preview } : {}
2813
+ },
2416
2814
  stderr: ""
2417
2815
  };
2418
2816
  } catch (cause) {
2419
2817
  return failed(cause instanceof Error ? cause.message : "Cloudflare version upload failed.");
2420
2818
  }
2421
2819
  };
2422
- return async (args, command) => {
2423
- if (args[0] === "deployments" && args[1] === "status") {
2820
+ return async (operation, command) => {
2821
+ if (operation.type === "deployments.status") {
2424
2822
  const result = await run([
2425
2823
  "workers",
2426
2824
  "deployments",
@@ -2428,17 +2826,20 @@ function createCfVersionRunner(options) {
2428
2826
  "--worker",
2429
2827
  workerName
2430
2828
  ], true);
2431
- if (result.status !== 0) return result;
2432
- const body = parseJson(result.stdout);
2829
+ if (result.status !== 0 || result.signal || result.error) return result;
2830
+ const body = result.data;
2433
2831
  const deployments = record(body) ? body.deployments : void 0;
2434
- if (Array.isArray(deployments) && deployments.length === 0) return failed("The Cloudflare Worker has no deployments.");
2832
+ if (Array.isArray(deployments) && deployments.length === 0) return {
2833
+ ...failed("The Cloudflare Worker has no deployments."),
2834
+ errorCode: 10007
2835
+ };
2435
2836
  if (!Array.isArray(deployments) || !record(deployments[0])) return failed("The Cloudflare Worker deployment was not readable.");
2436
2837
  return {
2437
2838
  ...result,
2438
- stdout: JSON.stringify(deployments[0])
2839
+ data: deployments[0]
2439
2840
  };
2440
2841
  }
2441
- if (args[0] === "versions" && args[1] === "list") {
2842
+ if (operation.type === "versions.list") {
2442
2843
  const pageSize = 10;
2443
2844
  const readPage = async (page) => {
2444
2845
  const result = await run([
@@ -2452,19 +2853,20 @@ function createCfVersionRunner(options) {
2452
2853
  "--page",
2453
2854
  String(page)
2454
2855
  ], true);
2455
- const body = parseJson(result.stdout);
2856
+ const body = result.data;
2456
2857
  return {
2457
2858
  result,
2458
2859
  versions: Array.isArray(body) && body.length <= pageSize && body.every((item) => record(item) && typeof item.id === "string" && Number.isSafeInteger(item.number)) && new Set(body.map((item) => item.number)).size === body.length ? body : null
2459
2860
  };
2460
2861
  };
2461
2862
  const first = await readPage(1);
2462
- if (first.result.status !== 0) return first.result;
2863
+ if (first.result.status !== 0 || first.result.signal || first.result.error) return first.result;
2463
2864
  if (!first.versions) return failed("The Cloudflare Worker version list was not readable.");
2464
2865
  const versions = [...first.versions];
2465
2866
  if (versions.length === pageSize) {
2466
2867
  const second = await readPage(2);
2467
- if (second.result.status !== 0 || !second.versions) return failed("The Cloudflare Worker version history was not readable.");
2868
+ if (second.result.status !== 0 || second.result.signal || second.result.error) return second.result;
2869
+ if (!second.versions) return failed("The Cloudflare Worker version history was not readable.");
2468
2870
  if (second.versions.length > 0) {
2469
2871
  const firstNumbers = versions.map((item) => item.number);
2470
2872
  const secondNumbers = second.versions.map((item) => item.number);
@@ -2473,7 +2875,8 @@ function createCfVersionRunner(options) {
2473
2875
  let last = Math.max(...secondNumbers);
2474
2876
  for (let page = 3; page <= 1e3 && second.versions.length === pageSize; page++) {
2475
2877
  const next = await readPage(page);
2476
- if (next.result.status !== 0 || !next.versions) return failed("The Cloudflare Worker version history was not readable.");
2878
+ if (next.result.status !== 0 || next.result.signal || next.result.error) return next.result;
2879
+ if (!next.versions) return failed("The Cloudflare Worker version history was not readable.");
2477
2880
  if (next.versions.length === 0) break;
2478
2881
  const numbers = next.versions.map((item) => item.number);
2479
2882
  if (Math.min(...numbers) <= last) return failed("The Cloudflare Worker version history changed during inspection.");
@@ -2487,56 +2890,51 @@ function createCfVersionRunner(options) {
2487
2890
  }
2488
2891
  return {
2489
2892
  ...first.result,
2490
- stdout: JSON.stringify(versions.sort((a, b) => a.number - b.number).slice(-10))
2893
+ data: versions.sort((a, b) => a.number - b.number).slice(-10)
2491
2894
  };
2492
2895
  }
2493
- if (args[0] === "versions" && args[1] === "view" && args[2]) {
2896
+ if (operation.type === "versions.view") {
2494
2897
  const result = await run([
2495
2898
  "workers",
2496
2899
  "versions",
2497
2900
  "get",
2498
- args[2],
2901
+ operation.versionId,
2499
2902
  "--worker-id",
2500
2903
  workerName
2501
2904
  ], true);
2502
- if (result.status !== 0) return result;
2503
- const version = parseJson(result.stdout);
2504
- if (!record(version) || version.id !== args[2] || !Array.isArray(version.bindings)) return failed("The Cloudflare Worker version was not readable.");
2905
+ if (result.status !== 0 || result.signal || result.error) return result;
2906
+ const version = result.data;
2907
+ if (!record(version) || version.id !== operation.versionId || !Array.isArray(version.bindings)) return failed("The Cloudflare Worker version was not readable.");
2505
2908
  return {
2506
2909
  ...result,
2507
- stdout: JSON.stringify({
2910
+ data: {
2508
2911
  ...version,
2509
2912
  resources: { bindings: version.bindings }
2510
- })
2913
+ }
2511
2914
  };
2512
2915
  }
2513
- if (args[0] === "versions" && args[1] === "upload") {
2514
- if (flag(args, "--name") !== workerName || args.includes("--assets") && flag(args, "--assets") !== output.assetsDirectory) return failed("The Cloudflare version upload target changed.");
2916
+ if (operation.type === "versions.upload") {
2917
+ if (operation.workerName !== workerName || operation.assetsDirectory !== void 0 && operation.assetsDirectory !== output.assetsDirectory) return failed("The Cloudflare version upload target changed.");
2515
2918
  const verified = await verifyResources(output.directory);
2516
2919
  if (verified.status !== 0 || verified.signal || verified.error) return verified;
2517
- if (output.activeSecrets) return uploadPinnedVersion(args, false);
2518
- return run([
2920
+ if (output.activeSecrets) return uploadPinnedVersion(operation, false);
2921
+ return uploadedResult(await run([
2519
2922
  "workers",
2520
2923
  "versions",
2521
2924
  "create",
2522
2925
  "--prebuilt",
2523
- ...flag(args, "--message") ? ["--message", flag(args, "--message")] : [],
2524
- ...flag(args, "--secrets-file") ? ["--secrets-file", flag(args, "--secrets-file")] : [],
2525
- ...flag(args, "--preview-alias") ? ["--preview-alias", flag(args, "--preview-alias")] : []
2526
- ], command.capture);
2527
- }
2528
- if (args[0] === "versions" && args[1] === "deploy") {
2529
- if (flag(args, "--name") !== workerName) return failed("The Cloudflare deployment target changed.");
2530
- const flagsStart = args.findIndex((item, index) => index >= 2 && item.startsWith("-"));
2531
- const versions = args.slice(2, flagsStart < 0 ? void 0 : flagsStart);
2532
- if (!versions.every((item) => /^[0-9a-f-]{36}@\d+(?:\.\d+)?$/i.test(item))) return failed("The Cloudflare deployment versions were invalid.");
2533
- const parsed = versions.map((item) => {
2534
- const [version_id, percentage] = item.split("@");
2535
- return {
2536
- version_id,
2537
- percentage: Number(percentage)
2538
- };
2539
- });
2926
+ ...operation.message ? ["--message", operation.message] : [],
2927
+ ...operation.secretsFilePath ? ["--secrets-file", operation.secretsFilePath] : [],
2928
+ ...operation.previewAlias ? ["--preview-alias", operation.previewAlias] : []
2929
+ ], command.capture));
2930
+ }
2931
+ if (operation.type === "versions.deploy") {
2932
+ if (operation.workerName !== workerName) return failed("The Cloudflare deployment target changed.");
2933
+ const parsed = operation.versions.map(({ versionId, percentage }) => ({
2934
+ version_id: versionId,
2935
+ percentage
2936
+ }));
2937
+ if (!parsed.every((version) => /^[0-9a-f-]{36}$/i.test(version.version_id) && Number.isFinite(version.percentage))) return failed("The Cloudflare deployment versions were invalid.");
2540
2938
  if (parsed.length === 0 || new Set(parsed.map((item) => item.version_id)).size !== parsed.length || parsed.some((item) => item.percentage < 0 || item.percentage > 100) || Math.abs(parsed.reduce((sum, item) => sum + item.percentage, 0) - 100) > .001) return failed("The Cloudflare deployment percentages were invalid.");
2541
2939
  return run([
2542
2940
  "workers",
@@ -2548,28 +2946,28 @@ function createCfVersionRunner(options) {
2548
2946
  "percentage",
2549
2947
  "--versions",
2550
2948
  JSON.stringify(parsed),
2551
- ...flag(args, "--message") ? ["--body", JSON.stringify({
2949
+ ...operation.message ? ["--body", JSON.stringify({
2552
2950
  strategy: "percentage",
2553
2951
  versions: parsed,
2554
- annotations: { "workers/message": flag(args, "--message") }
2952
+ annotations: { "workers/message": operation.message }
2555
2953
  })] : []
2556
2954
  ], command.capture);
2557
2955
  }
2558
- if (args[0] === "deploy") {
2559
- if (flag(args, "--name") !== workerName || args.includes("--assets") && flag(args, "--assets") !== output.assetsDirectory) return failed("The Cloudflare bootstrap target changed.");
2956
+ if (operation.type === "worker.deploy") {
2957
+ if (operation.workerName !== workerName || operation.assetsDirectory !== void 0 && operation.assetsDirectory !== output.assetsDirectory) return failed("The Cloudflare bootstrap target changed.");
2560
2958
  const verified = await verifyResources(output.directory);
2561
2959
  if (verified.status !== 0 || verified.signal || verified.error) return verified;
2562
- if (output.activeSecrets) return uploadPinnedVersion(args, true);
2563
- return run([
2960
+ if (output.activeSecrets) return uploadPinnedVersion(operation, true);
2961
+ return uploadedResult(await run([
2564
2962
  "deploy",
2565
2963
  "--prebuilt",
2566
- ...flag(args, "--message") ? ["--message", flag(args, "--message")] : [],
2567
- ...flag(args, "--secrets-file") ? ["--secrets-file", flag(args, "--secrets-file")] : []
2568
- ], command.capture);
2964
+ ...operation.message ? ["--message", operation.message] : [],
2965
+ ...operation.secretsFilePath ? ["--secrets-file", operation.secretsFilePath] : []
2966
+ ], command.capture));
2569
2967
  }
2570
- if (args[0] === "triggers" && args[1] === "deploy") {
2571
- const configPath = flag(args, "-c");
2572
- if (!configPath || flag(args, "--name") !== workerName) return failed("The Cloudflare trigger target changed.");
2968
+ if (operation.type === "triggers.deploy") {
2969
+ const configPath = operation.configPath;
2970
+ if (!configPath || operation.workerName !== workerName) return failed("The Cloudflare trigger target changed.");
2573
2971
  const raw = parseJson(readFileSync(configPath, "utf8"));
2574
2972
  if (!record(raw) || raw.name !== workerName) return failed("The Cloudflare trigger snapshot was not readable.");
2575
2973
  const directory = mkdtempSync(join(tmpdir(), "void-cf-triggers-"));
@@ -2619,12 +3017,12 @@ function createCfVersionRunner(options) {
2619
3017
  });
2620
3018
  }
2621
3019
  }
2622
- if (args[0] === "queues" && args[1] === "consumer" && args[2] === "remove") {
2623
- const queueName = args[3];
2624
- if (!queueName || args[4] !== workerName) return failed("The Cloudflare Queue consumer target changed.");
3020
+ if (operation.type === "queues.consumer.remove") {
3021
+ const queueName = operation.queueName;
3022
+ if (!queueName || operation.workerName !== workerName) return failed("The Cloudflare Queue consumer target changed.");
2625
3023
  const queues = await run(["queues", "list"], true);
2626
- if (queues.status !== 0) return queues;
2627
- const allQueues = parseJson(queues.stdout);
3024
+ if (queues.status !== 0 || queues.signal || queues.error) return queues;
3025
+ const allQueues = queues.data;
2628
3026
  if (!Array.isArray(allQueues)) return failed("The Cloudflare Queue list was not readable.");
2629
3027
  const queue = allQueues.find((item) => record(item) && item.queue_name === queueName);
2630
3028
  if (!record(queue) || typeof queue.queue_id !== "string") return failed(`Cloudflare Queue ${queueName} was not found.`);
@@ -2635,16 +3033,22 @@ function createCfVersionRunner(options) {
2635
3033
  "--queue-id",
2636
3034
  queue.queue_id
2637
3035
  ], true);
2638
- if (consumers.status !== 0) return consumers;
2639
- const allConsumers = parseJson(consumers.stdout);
3036
+ if (consumers.status !== 0 || consumers.signal || consumers.error) return consumers;
3037
+ const allConsumers = consumers.data;
2640
3038
  if (!Array.isArray(allConsumers)) return failed("The Cloudflare Queue consumers were not readable.");
2641
- const matching = allConsumers.filter((item) => record(item) && item.script === workerName);
3039
+ const matching = [];
3040
+ for (const item of allConsumers) {
3041
+ if (!record(item)) return failed("The Cloudflare Queue consumers were not readable.");
3042
+ const name = item.script_name ?? item.script;
3043
+ if (item.script_name !== void 0 && !safePositional(item.script_name) || item.script !== void 0 && !safePositional(item.script) || item.script_name !== void 0 && item.script !== void 0 && item.script_name !== item.script || item.type === "worker" && name === void 0) return failed("The Cloudflare Queue consumer Worker identity was ambiguous.");
3044
+ if (name === workerName) matching.push(item);
3045
+ }
2642
3046
  if (matching.length === 0) return {
2643
3047
  status: 0,
2644
3048
  stdout: "",
2645
3049
  stderr: ""
2646
3050
  };
2647
- if (matching.length !== 1 || !record(matching[0]) || typeof matching[0].consumer_id !== "string") return failed("The Cloudflare Queue consumer identity was ambiguous.");
3051
+ if (matching.length !== 1 || !safePositional(matching[0].consumer_id) || matching[0].type !== void 0 && matching[0].type !== "worker") return failed("The Cloudflare Queue consumer identity was ambiguous.");
2648
3052
  return run([
2649
3053
  "queues",
2650
3054
  "consumers",
@@ -2655,7 +3059,7 @@ function createCfVersionRunner(options) {
2655
3059
  "--force"
2656
3060
  ], command.capture);
2657
3061
  }
2658
- return failed(`Unsupported Cloudflare activation operation: ${args[0] ?? ""} ${args[1] ?? ""}`);
3062
+ return failed(`Unsupported Cloudflare activation operation: ${operation.type}`);
2659
3063
  };
2660
3064
  }
2661
3065
  //#endregion
@@ -2679,8 +3083,8 @@ function assertCloudflareCandidateUnchanged(root, expected) {
2679
3083
  }
2680
3084
  function recordCloudflareCandidateUpload(root, expected, accountId, workerName, parentVersionId, versionId) {
2681
3085
  assertCloudflareCandidateUnchanged(root, expected);
2682
- if (expected && (expected.accountId !== accountId || expected.workerName !== workerName || expected.parentVersionId !== parentVersionId)) throw new Error("The saved Cloudflare candidate belongs to a different Worker or live version. Inspect the deployment before retrying.");
2683
- const versionIds = [...expected?.versionIds ?? [], versionId];
3086
+ if (expected && (expected.accountId !== accountId || expected.workerName !== workerName || expected.parentVersionId !== parentVersionId && expected.versionIds.at(-1) !== parentVersionId)) throw new Error("The saved Cloudflare candidate belongs to a different Worker or live version. Inspect the deployment before retrying.");
3087
+ const versionIds = [...expected?.parentVersionId === parentVersionId ? expected.versionIds : [], versionId];
2684
3088
  mkdirSync(join(root, ".void"), { recursive: true });
2685
3089
  const path = join(root, CANDIDATE_FILE);
2686
3090
  const temp = `${path}.${process.pid}.tmp`;
@@ -2706,13 +3110,13 @@ function clearCloudflareCandidate(root) {
2706
3110
  * Narrow native Cloudflare deploys to app shapes whose emitted Worker artifact
2707
3111
  * the current verifier understands. This runs before any account operation.
2708
3112
  */
2709
- function classifyCloudflareDeployApp(root, voidConfig, options) {
3113
+ function classifyCloudflareDeployApp(root, voidConfig, options, application) {
2710
3114
  if (isNodeTarget(voidConfig.target)) return unsupported([
2711
3115
  `deploy: --platform cloudflare is not supported for the "${voidConfig.target}" target.`,
2712
3116
  "This backend deploys Cloudflare Workers only. Deploy a node/bun/deno build with its",
2713
3117
  "own host/runtime, or use the default Cloudflare target for --platform cloudflare."
2714
3118
  ].join("\n"));
2715
- const framework = detectFramework(root);
3119
+ const framework = application?.framework ?? detectFramework(root);
2716
3120
  if (framework) {
2717
3121
  if (!FRAMEWORK_PRESETS[framework.name]) return unsupported(`deploy: --platform cloudflare has no deployment preset for framework ${framework.name}.`);
2718
3122
  }
@@ -2731,13 +3135,16 @@ function classifyCloudflareDeployApp(root, voidConfig, options) {
2731
3135
  "A full Void app has at least one of: routes/, pages/, middleware, crons/, queues/,",
2732
3136
  "email/, or an SSR entry. Add your server code, or use `void deploy` for a static site."
2733
3137
  ].join("\n"));
2734
- const paths = resolveProjectPaths(root, voidConfig);
2735
3138
  try {
3139
+ const analysis = application ?? analyzeApplicationSync(root, {
3140
+ config: voidConfig,
3141
+ environment: "prod"
3142
+ });
3143
+ const paths = analysis.paths;
2736
3144
  assertNoRootSourceConventionConflicts(paths);
2737
3145
  validateSsrEntry(root, paths);
2738
- scanJobsSync(root, paths.cronsDir);
2739
- syncWranglerDurableObjects(root, scanDurableObjectModules(root));
2740
- syncWranglerWebSockets(root, scanWebSocketRoutesSync(root, paths, "prod"));
3146
+ syncWranglerDurableObjects(root, analysis.durableObjects);
3147
+ syncWranglerWebSockets(root, analysis.websockets);
2741
3148
  } catch (error) {
2742
3149
  return unsupported(`deploy: ${getErrorMessage$1(error)}`);
2743
3150
  }
@@ -2884,7 +3291,7 @@ async function prepareExistingCloudflareDeploy(options) {
2884
3291
  }
2885
3292
  /** Offer the existing destination before the legacy hosted-platform fallback. */
2886
3293
  async function maybeLinkCloudflareDeploy(root, options = {}, deps = {}) {
2887
- if (options.platform || options.backend || options.projectSlug || process.env.VOID_PROJECT || readDeploymentPlatform(root) !== null) return null;
3294
+ if (options.platform || options.projectSlug || process.env.VOID_PROJECT || readDeploymentPlatform(root) !== null) return null;
2888
3295
  const files = [.../* @__PURE__ */ new Set([...[
2889
3296
  "wrangler.jsonc",
2890
3297
  "wrangler.json",
@@ -3970,13 +4377,12 @@ function toWebSocketManifest(routes) {
3970
4377
  }
3971
4378
  function toSandboxManifest(sandbox) {
3972
4379
  return {
3973
- controllerProtocol: 1,
4380
+ controllerProtocol: 2,
3974
4381
  bindingName: sandbox.binding,
3975
4382
  className: sandbox.className,
3976
4383
  containerName: sandbox.containerName,
3977
4384
  image: sandbox.platformImage,
3978
- ...sandbox.instanceType && { instanceType: sandbox.instanceType },
3979
- ...sandbox.maxInstances != null && { maxInstances: sandbox.maxInstances }
4385
+ ...sandbox.instanceType && { instanceType: sandbox.instanceType }
3980
4386
  };
3981
4387
  }
3982
4388
  function toLiveManifest(live) {
@@ -4028,8 +4434,7 @@ function buildVoidManifest(opts) {
4028
4434
  };
4029
4435
  if (opts.hasEmailHandlers) manifest.emailProtocolVersion = 1;
4030
4436
  if (bindings.needsAuth) manifest.auth = true;
4031
- const hasMigrations = migrations && migrations.length > 0;
4032
- if (bindings.needsD1 || bindings.needsAuth || hasMigrations) {
4437
+ if (needsApplicationDatabase(bindings, migrations?.length ?? 0)) {
4033
4438
  if (resolvedDialect !== "sqlite") manifest.bindings.hyperdrive = ["HYPERDRIVE"];
4034
4439
  else manifest.bindings.d1 = [options?.bindingNames?.d1 ?? "DB"];
4035
4440
  }
@@ -4144,8 +4549,7 @@ function buildFrameworkManifest(opts) {
4144
4549
  assetManifestVersion: 2,
4145
4550
  bindings: {}
4146
4551
  };
4147
- const hasMigrations = opts.migrations && opts.migrations.length > 0;
4148
- if (opts.bindings.needsD1 || opts.bindings.needsAuth || hasMigrations) {
4552
+ if (needsApplicationDatabase(opts.bindings, opts.migrations?.length ?? 0)) {
4149
4553
  if (dialect !== "sqlite") manifest.bindings.hyperdrive = ["HYPERDRIVE"];
4150
4554
  else manifest.bindings.d1 = [po?.bindingNames?.d1 ?? "DB"];
4151
4555
  }
@@ -4394,7 +4798,7 @@ async function generateWrapper(workerDir, workerMain, root, features) {
4394
4798
  let migrationPlanBundle;
4395
4799
  if (features.hasMigrations) {
4396
4800
  const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
4397
- const { buildMigrationPlanBundle } = await import("./plan-DpuOr14e.mjs");
4801
+ const { buildMigrationPlanBundle } = await import("./plan-D1Q5rf-r.mjs");
4398
4802
  const migrationsDir = readProjectPaths(root).migrationsDir;
4399
4803
  migrations = collectMigrations(migrationsDir);
4400
4804
  migrationPlanBundle = features.migrationDialect === "sqlite" ? buildMigrationPlanBundle(migrations) : void 0;
@@ -4635,7 +5039,7 @@ function generateWrapperSource(renamedEntry, handlersModule, features, migration
4635
5039
  }
4636
5040
  if (features.readiness || features.crons.length > 0 || features.queues.length > 0 || features.hasMigrations) {
4637
5041
  lines.push(" if (request.method === \"GET\" && url.pathname === \"/__void/ready\") {");
4638
- lines.push(" return Response.json({ ready: true });");
5042
+ lines.push(` return Response.json({ ready: true${features.versionMetadataBinding ? `, versionId: env[${JSON.stringify(features.versionMetadataBinding)}]?.id` : ""} }${features.versionMetadataBinding ? ", { headers: { \"cache-control\": \"no-store\" } }" : ""});`);
4639
5043
  lines.push(" }");
4640
5044
  }
4641
5045
  if (features.queues.length > 0) {
@@ -4805,7 +5209,7 @@ let loadedProvisionModule;
4805
5209
  * so a managed deploy never imports it — and thus never loads `proper-lockfile`.
4806
5210
  */
4807
5211
  async function loadProvisionModule() {
4808
- loadedProvisionModule ??= await import("./provision-DNtrtaVD.mjs");
5212
+ loadedProvisionModule ??= await import("./provision-C4ORE7G7.mjs");
4809
5213
  return loadedProvisionModule;
4810
5214
  }
4811
5215
  /**
@@ -4828,7 +5232,7 @@ const log = {
4828
5232
  };
4829
5233
  var DeployEventError = class extends Error {};
4830
5234
  function resolveDeploymentPlatform(root, options) {
4831
- return options?.platform ?? options?.backend ?? readDeploymentPlatform(root) ?? "void";
5235
+ return options?.platform ?? readDeploymentPlatform(root) ?? "void";
4832
5236
  }
4833
5237
  async function generateDeployWrapperIfNeeded(root, workerDir, workerMain, features) {
4834
5238
  if (!needsWrapper(features)) return false;
@@ -4964,7 +5368,7 @@ async function runDeploy(root, options) {
4964
5368
  * `VOID_DEPLOY_PROJECT_ID` is, so it cannot leak into an unrelated child process or a
4965
5369
  * later `void dev` in the same shell.
4966
5370
  *
4967
- * Deliberately NOT set by `runCloudflareDeploy` (`--backend cloudflare`): that flow
5371
+ * Deliberately NOT set by `runCloudflareDeploy` (`--platform cloudflare`): that flow
4968
5372
  * hands the built output to `wrangler deploy` against the user's own account and IS the
4969
5373
  * self-host path the warning is about. (It rejects framework SSR outright today, so no
4970
5374
  * Class A build reaches it either way.)
@@ -4974,6 +5378,7 @@ const MANAGED_DEPLOY_BUILD_ENV = { VOID_DEPLOY_BUILD: "1" };
4974
5378
  function scrubManagedDeployBuildMarkers(env) {
4975
5379
  const { deleteEnvVarCaseAware } = requireProvisionModule();
4976
5380
  deleteEnvVarCaseAware(env, "VOID_DEPLOY_PROJECT_ID");
5381
+ deleteEnvVarCaseAware(env, CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV);
4977
5382
  for (const name of Object.keys(MANAGED_DEPLOY_BUILD_ENV)) deleteEnvVarCaseAware(env, name);
4978
5383
  return env;
4979
5384
  }
@@ -5356,21 +5761,30 @@ function snapshotPassthroughRawFields(config) {
5356
5761
  * build injected, dropped, or retargeted.
5357
5762
  */
5358
5763
  function reconstructExpectedContainers(config, root, rootConfig, needsSandbox) {
5359
- const rootContainers = Array.isArray(rootConfig.containers) ? rootConfig.containers : [];
5360
- const expected = [...rootContainers];
5764
+ const expected = [...Array.isArray(rootConfig.containers) ? rootConfig.containers : []];
5361
5765
  if (needsSandbox) {
5362
5766
  const sandbox = resolveSandboxConfig(config, root);
5363
- if (!rootContainers.some((container) => readConfigEntryString(container, "class_name") === sandbox.className)) expected.push({
5364
- name: sandbox.containerName,
5365
- class_name: sandbox.className,
5366
- image: sandbox.image,
5367
- ...sandbox.imageBuildContext && { image_build_context: sandbox.imageBuildContext },
5368
- ...sandbox.instanceType && { instance_type: sandbox.instanceType },
5369
- ...sandbox.maxInstances != null && { max_instances: sandbox.maxInstances }
5370
- });
5767
+ const index = expected.findIndex((container) => readConfigEntryString(container, "class_name") === sandbox.className);
5768
+ if (index >= 0) expected.splice(index, 1);
5769
+ expected.push(sandboxContainerConfig(sandbox));
5371
5770
  }
5372
5771
  return expected;
5373
5772
  }
5773
+ /** Normalize only the named-image path fields Vite relocates into its output config. */
5774
+ function normalizeContainerImagePaths(container, base) {
5775
+ if (!container || typeof container !== "object" || Array.isArray(container)) return container;
5776
+ const value = container;
5777
+ if (!value.images || typeof value.images !== "object" || Array.isArray(value.images)) return container;
5778
+ return {
5779
+ ...value,
5780
+ images: Object.fromEntries(Object.entries(value.images).map(([name, source]) => {
5781
+ if (!source || typeof source !== "object" || Array.isArray(source)) return [name, source];
5782
+ const image = { ...source };
5783
+ for (const key of ["dockerfile", "build_context"]) if (typeof image[key] === "string") image[key] = resolve(base, image[key]);
5784
+ return [name, image];
5785
+ }))
5786
+ };
5787
+ }
5374
5788
  /** Reconstruct the Durable Object state emitted after the pre-build root config is synced. */
5375
5789
  function reconstructExpectedDurableObjectState(config, root, rootConfig, options) {
5376
5790
  const bindings = bindingEntriesForField("durable_objects", rootConfig.durable_objects).map(({ entry }) => entry);
@@ -5381,14 +5795,14 @@ function reconstructExpectedDurableObjectState(config, root, rootConfig, options
5381
5795
  name: sandbox.binding,
5382
5796
  class_name: sandbox.className
5383
5797
  });
5384
- if (!migrations.some((migration) => migration.tag === "void-sandbox-v1" || Array.isArray(migration.new_sqlite_classes) && migration.new_sqlite_classes.includes(sandbox.className))) migrations.push({
5798
+ if (!hasSandboxSqliteMigration(migrations, sandbox.className)) migrations.push({
5385
5799
  tag: SANDBOX_MIGRATION_TAG,
5386
5800
  new_sqlite_classes: [sandbox.className]
5387
5801
  });
5388
5802
  }
5389
- const paths = resolveProjectPaths(root, config);
5390
- const websocketRoutes = scanWebSocketRoutesSync(root, paths);
5391
- const durableObjects = scanDurableObjectModules(root);
5803
+ const analysis = options.analysis ?? analyzeApplicationSync(root, { config });
5804
+ const websocketRoutes = analysis.allWebsockets;
5805
+ const durableObjects = analysis.durableObjects;
5392
5806
  const live = options.needsLive ? resolveLiveBindingConfig(root, {
5393
5807
  reservedBindings: [...websocketRoutes.map((route) => route.bindingName), ...durableObjects.map((module) => module.bindingName)],
5394
5808
  reservedClasses: [
@@ -5418,11 +5832,11 @@ function reconstructExpectedDurableObjectState(config, root, rootConfig, options
5418
5832
  * chunks from its own `__dirname` (the proxy workers, the experimental-config module), so the
5419
5833
  * whole directory is the trust surface, not just the entry file. Content hash of the bytes, NOT
5420
5834
  * mtime. Resolves the SAME entry {@link createDeployWranglerRunner} spawns (via
5421
- * {@link resolveWranglerArgv}, whose launcher's LAST argv element is the JS entry). Returns
5835
+ * {@link resolveCloudflareOperationArgv}, whose launcher's LAST argv element is the JS entry). Returns
5422
5836
  * `undefined` when the entry cannot be resolved or a file cannot be read, so the caller FAILS
5423
5837
  * CLOSED — it never deploys a binary it could not verify. The resolver is injectable for tests.
5424
5838
  */
5425
- function hashWranglerUploader(root, resolveArgv = resolveWranglerArgv) {
5839
+ function hashWranglerUploader(root, resolveArgv = resolveCloudflareOperationArgv) {
5426
5840
  let entry;
5427
5841
  try {
5428
5842
  const argv = resolveArgv(root);
@@ -6052,7 +6466,7 @@ function verifyGeneratedDeployConfig(root, expected) {
6052
6466
  ok: false,
6053
6467
  reason: `generated config declares a container application ${JSON.stringify(className)} that Void did not derive from source — refusing to deploy a container application the build injected.`
6054
6468
  };
6055
- if (!deepEqual(container, expectedContainer)) return {
6469
+ if (!deepEqual(normalizeContainerImagePaths(container, dirname(redirectedPath)), normalizeContainerImagePaths(expectedContainer, root))) return {
6056
6470
  ok: false,
6057
6471
  reason: `generated config container application ${JSON.stringify(className)} does not match the pre-build config — refusing to deploy a container application the build retargeted.`
6058
6472
  };
@@ -6251,42 +6665,30 @@ function assertDistinctKvNamespaces(kvNamespaces, config, configPath, isRealKvId
6251
6665
  /**
6252
6666
  * Compute the DESIRED id/name-bearing Cloudflare resources from SOURCE
6253
6667
  * (pre-build, fail-fast) — the single set shared by the provision and drift
6254
- * paths. Reuses the deploy-time inference primitives directly
6255
- * (`inferProjectBindings` + the scanners + the pg-dialect / `hasRevalidate`
6256
- * signals) rather than `assembleVoidManifest`, which runs only inside the
6257
- * managed `runFullDeploy` branches that need a `PlatformClient`.
6668
+ * paths. Consumes the same application analysis as the plugin and managed
6669
+ * packaging; provider identities and stored resource names are resolved here.
6258
6670
  *
6259
6671
  * Resource NAMES prefer the value already stored in the root wrangler config
6260
6672
  * (so a re-provision reuses the same database/bucket and the provisioner's
6261
6673
  * stored-name identity check passes); otherwise they default off the worker
6262
6674
  * name. Returns a per-binding REASON map used to explain drift.
6263
6675
  */
6264
- async function computeDesiredCloudflareResources(root, voidConfig, configPath) {
6676
+ async function computeDesiredCloudflareResources(root, voidConfig, configPath, application) {
6265
6677
  const { isRealD1Id, isRealKvId } = await loadProvisionModule();
6266
- const paths = resolveProjectPaths(root, voidConfig);
6678
+ const analysis = application ? await completeApplicationAnalysis(application) : await analyzeApplication(root, {
6679
+ config: voidConfig,
6680
+ environment: "prod"
6681
+ });
6682
+ const { paths, bindings, authEnabled, dialect, bindingNames, migrations, needsDatabase } = analysis;
6267
6683
  const config = parseRootWranglerConfig(configPath);
6268
6684
  const workerName = readWranglerTopLevelString(config, "name") ?? deriveWorkerName(root);
6269
- const isFrameworkMode = detectFramework(root) != null;
6270
- const scanDirs = voidConfig.inference?.scanDirs ?? (isFrameworkMode ? FRAMEWORK_SCAN_DIRS : void 0);
6271
- const inferenceOptions = voidConfig.inference?.bindings ? { bindings: voidConfig.inference.bindings } : void 0;
6272
- const bindings = inferProjectBindings(root, {
6273
- ...inferenceOptions,
6274
- paths,
6275
- environment: "prod"
6276
- }, scanDirs);
6277
- const authEnabled = bindings.needsAuth || findVoidAuthConfig(root, paths) !== null || voidConfig.auth !== void 0;
6278
- const dialect = getDatabaseDialect(voidConfig);
6279
- const bindingNames = resolveBindingNames(voidConfig.inference?.bindings);
6280
- const migrations = collectMigrations(paths.migrationsDir);
6281
- const hasMigrations = migrations.length > 0;
6282
6685
  const migrationsDirRelative = paths.sourceDir ? `./${paths.sourceDir}/db/migrations` : "./db/migrations";
6283
6686
  const desired = {
6284
6687
  kvNamespaces: [],
6285
6688
  queues: []
6286
6689
  };
6287
6690
  const reasons = /* @__PURE__ */ new Map();
6288
- const needsDatabase = bindings.needsD1 || authEnabled || hasMigrations;
6289
- const databaseReason = bindings.needsD1 ? "app uses env.DB (D1)" : authEnabled ? "auth requires a D1 database" : "checked-in migrations need a D1 database";
6691
+ const databaseReason = analysis.reasons.database[0];
6290
6692
  if (needsDatabase && dialect === "sqlite") {
6291
6693
  const binding = bindingNames?.d1 ?? "DB";
6292
6694
  const storedName = readStoredResourceName(config, "d1_databases", binding, "database_name");
@@ -6305,7 +6707,7 @@ async function computeDesiredCloudflareResources(root, voidConfig, configPath) {
6305
6707
  binding,
6306
6708
  title: suffixedResourceName(workerName, "kv")
6307
6709
  });
6308
- reasons.set(binding, "app uses env.KV");
6710
+ reasons.set(binding, analysis.reasons.kv[0]);
6309
6711
  }
6310
6712
  if (bindings.needsR2) {
6311
6713
  const binding = bindingNames?.r2 ?? "STORAGE";
@@ -6315,9 +6717,9 @@ async function computeDesiredCloudflareResources(root, voidConfig, configPath) {
6315
6717
  binding,
6316
6718
  bucketName: storedName && !generatedDefault ? storedName : suffixedResourceName(workerName, "storage")
6317
6719
  };
6318
- reasons.set(binding, "app uses env.STORAGE (R2)");
6720
+ reasons.set(binding, analysis.reasons.r2[0]);
6319
6721
  }
6320
- desired.queues = scanQueuesSync(root, paths.queuesDir).map((q) => q.name);
6722
+ desired.queues = analysis.queues.map((q) => q.name);
6321
6723
  if (needsDatabase && dialect !== "sqlite") {
6322
6724
  const binding = "HYPERDRIVE";
6323
6725
  desired.hyperdrive = {
@@ -6327,10 +6729,8 @@ async function computeDesiredCloudflareResources(root, voidConfig, configPath) {
6327
6729
  };
6328
6730
  reasons.set(binding, `app uses ${dialect === "mysql" ? "MySQL" : "PostgreSQL"} (needs a Hyperdrive config)`);
6329
6731
  }
6330
- if (hasRevalidate(collectRevalidate(voidConfig, existsSync(paths.pagesDir) ? await scanPages(root, {
6331
- output: voidConfig.output,
6332
- paths
6333
- }) : null))) {
6732
+ const pageScan = analysis.scan.pages;
6733
+ if (hasRevalidate(collectRevalidate(voidConfig, pageScan))) {
6334
6734
  const binding = "ISR_CACHE";
6335
6735
  desired.kvNamespaces.push({
6336
6736
  binding,
@@ -6339,9 +6739,10 @@ async function computeDesiredCloudflareResources(root, voidConfig, configPath) {
6339
6739
  reasons.set(binding, "routes use `revalidate` (ISR) — needs an ISR_CACHE KV namespace");
6340
6740
  }
6341
6741
  assertDistinctKvNamespaces(desired.kvNamespaces, config, configPath, isRealKvId);
6342
- const expectedCrons = [...new Set(scanJobsSync(root, paths.cronsDir).flatMap((job) => job.crons))];
6343
- const emailHandlers = scanEmailHandlersSync(paths.sourceRoot);
6742
+ const expectedCrons = [...new Set(analysis.jobs.flatMap((job) => job.crons))];
6743
+ const emailHandlers = analysis.email;
6344
6744
  return {
6745
+ analysis,
6345
6746
  desired,
6346
6747
  reasons,
6347
6748
  authEnabled,
@@ -6353,7 +6754,7 @@ async function computeDesiredCloudflareResources(root, voidConfig, configPath) {
6353
6754
  expectedCrons,
6354
6755
  needsEmail: bindings.needsEmail === true || hasEmailHandlers(emailHandlers),
6355
6756
  emailHandlers,
6356
- needsSandbox: isSandboxEnabled(voidConfig) || bindings.needsSandbox,
6757
+ needsSandbox: bindings.needsSandbox,
6357
6758
  needsLive: bindings.needsLive === true
6358
6759
  };
6359
6760
  }
@@ -6707,49 +7108,15 @@ async function checkWranglerMigrationApplySet(configPath, config, binding, migra
6707
7108
  };
6708
7109
  return { ok: true };
6709
7110
  }
6710
- /**
6711
- * Default {@link DeployWranglerRun}: spawns the project-local wrangler REAL CLI
6712
- * (via {@link resolveWranglerArgv}, shell-free) with ONE pinned transport in the
6713
- * child env (via {@link deployChildEnv} — reused provisioner scrub/pin: CF Access +
6714
- * every transport selector incl. `CLOUDFLARE_ENV` + the deploy config-override vars
6715
- * scrubbed, one account + API base pinned), so `whoami`/`migrate`/`deploy` can never
6716
- * consult a different ambient account or endpoint. Noninteractive output is captured so Void
6717
- * controls the terminal; browser login still inherits stdio. `opts.env` layers one more pin
6718
- * over the child env for a single spawn (`auth token` gets
6719
- * `WRANGLER_WRITE_LOGS=false`, see {@link runWranglerAuthToken}).
6720
- */
7111
+ /** Run typed provider operations in the pinned account and API context. */
6721
7112
  function createDeployWranglerRunner(root, accountId, apiBaseUrl) {
6722
- const [command, ...prefix] = resolveWranglerArgv(root);
6723
- return (args, opts) => {
6724
- const childEnv = deployChildEnv(accountId, apiBaseUrl);
6725
- const interactiveLogin = cloudflareAuthNeedsTerminal(args);
6726
- if (!opts.capture || interactiveLogin) childEnv.WRANGLER_LOG = "none";
6727
- if (opts.env) Object.assign(childEnv, opts.env);
6728
- const result = spawnSync(command, [...prefix, ...args], {
6729
- cwd: root,
6730
- stdio: interactiveLogin ? "inherit" : opts.input === void 0 ? [
6731
- "ignore",
6732
- "pipe",
6733
- "pipe"
6734
- ] : [
6735
- "pipe",
6736
- "pipe",
6737
- "pipe"
6738
- ],
6739
- input: opts.input,
6740
- encoding: "utf-8",
6741
- env: childEnv,
6742
- shell: false,
6743
- windowsHide: true
6744
- });
6745
- return {
6746
- status: typeof result.status === "number" ? result.status : 1,
6747
- stdout: result.stdout ?? "",
6748
- stderr: result.stderr ?? "",
6749
- signal: result.signal ?? null,
6750
- error: result.error ? result.error.message : null
6751
- };
6752
- };
7113
+ const runner = createCloudflareProcessRunner({
7114
+ root,
7115
+ accountId,
7116
+ apiBaseUrl,
7117
+ env: () => deployChildEnv(accountId, apiBaseUrl)
7118
+ });
7119
+ return (operation, opts) => runner(operation, opts);
6753
7120
  }
6754
7121
  /** Retry an existing-Worker read only when a fresh identity check proves the session is gone. */
6755
7122
  async function prepareExistingCloudflareDeployWithAuth(options, beforeRun) {
@@ -6758,18 +7125,17 @@ async function prepareExistingCloudflareDeployWithAuth(options, beforeRun) {
6758
7125
  } catch (error) {
6759
7126
  if (!(error instanceof CloudflareWorkerInspectionError)) throw error;
6760
7127
  if (!beforeRun()) throw new CloudflareAuthenticationError("The Cloudflare uploader changed during Worker verification.", "uploader-changed");
6761
- const whoamiArgs = [...existingWranglerConfigArgs(options.configPath), "--env="];
6762
- const identity = await options.runWrangler([
6763
- "whoami",
6764
- "--json",
6765
- ...whoamiArgs
6766
- ], { capture: true });
6767
- const session = isCleanWranglerExit(identity) ? parseCloudflareAuthSession(identity.stdout) : null;
7128
+ const configPath = existsSync(options.configPath) ? options.configPath : void 0;
7129
+ const identity = await options.runWrangler({
7130
+ type: "auth.whoami",
7131
+ configPath
7132
+ }, { capture: true });
7133
+ const session = isCleanWranglerExit(identity) ? parseCloudflareAuthSession(identity.data) : null;
6768
7134
  const wrongAccount = session !== null && session.accounts.length > 0 && !session.accounts.some((account) => account.id === options.accountId);
6769
7135
  if (session && !wrongAccount) throw error;
6770
7136
  let loggedOut = false;
6771
7137
  try {
6772
- loggedOut = JSON.parse(identity.stdout).loggedIn === false;
7138
+ loggedOut = identity.data?.loggedIn === false;
6773
7139
  } catch {}
6774
7140
  if (!(loggedOut || wrongAccount || /authentication is required|not authenticated|no api token|invalid.*token|expired.*token|unauthorized/i.test(identity.stderr)) || identity.signal || identity.error) throw new Error("Could not verify the Cloudflare session after the Worker check failed. Run `void cloudflare status` and retry.");
6775
7141
  const overrides = cloudflareCredentialOverrides();
@@ -6780,8 +7146,8 @@ async function prepareExistingCloudflareDeployWithAuth(options, beforeRun) {
6780
7146
  forceLogin: true,
6781
7147
  requireAccounts: true,
6782
7148
  beforeRun,
6783
- runWrangler: (args, capture) => options.runWrangler([...args], { capture }),
6784
- whoamiArgs
7149
+ runWrangler: (operation, capture) => options.runWrangler(operation, { capture }),
7150
+ configPath
6785
7151
  })).accounts.some((account) => account.id === options.accountId)) throw new Error(`The signed-in Cloudflare user cannot access account ${options.accountId}. Sign in with a user who can access it, then retry.`);
6786
7152
  if (!beforeRun()) throw new CloudflareAuthenticationError("The Cloudflare uploader changed during sign-in.", "uploader-changed");
6787
7153
  return await prepareExistingCloudflareDeploy(options);
@@ -6791,8 +7157,8 @@ async function chooseCloudflareDeployAccount(options) {
6791
7157
  const accounts = (await ensureCloudflareAuthentication({
6792
7158
  beforeRun: options.beforeWranglerRun,
6793
7159
  requireAccounts: true,
6794
- runWrangler: (args, capture) => options.runWrangler([...args], { capture }),
6795
- whoamiArgs: [...existingWranglerConfigArgs(options.configPath), "--env="]
7160
+ runWrangler: (operation, capture) => options.runWrangler(operation, { capture }),
7161
+ configPath: existsSync(options.configPath) ? options.configPath : void 0
6796
7162
  })).accounts;
6797
7163
  let account = accounts[0];
6798
7164
  if (accounts.length > 1) {
@@ -6838,14 +7204,10 @@ function isWranglerNonInteractiveOrCI() {
6838
7204
  async function runWranglerAuthToken(runWrangler, configPath) {
6839
7205
  let result;
6840
7206
  try {
6841
- result = await Promise.resolve(runWrangler([
6842
- "auth",
6843
- "token",
6844
- "--json",
6845
- "-c",
6846
- configPath,
6847
- "--env="
6848
- ], {
7207
+ result = await Promise.resolve(runWrangler({
7208
+ type: "auth.token",
7209
+ configPath
7210
+ }, {
6849
7211
  capture: true,
6850
7212
  env: { WRANGLER_WRITE_LOGS: "false" }
6851
7213
  }));
@@ -6853,15 +7215,7 @@ async function runWranglerAuthToken(runWrangler, configPath) {
6853
7215
  return null;
6854
7216
  }
6855
7217
  if (!isCleanWranglerExit(result)) return null;
6856
- const start = result.stdout.indexOf("{");
6857
- const end = result.stdout.lastIndexOf("}");
6858
- if (start === -1 || end <= start) return null;
6859
- let parsed;
6860
- try {
6861
- parsed = JSON.parse(result.stdout.slice(start, end + 1));
6862
- } catch {
6863
- return null;
6864
- }
7218
+ const parsed = result.data;
6865
7219
  const type = parsed?.type;
6866
7220
  const token = parsed?.token;
6867
7221
  return (type === "oauth" || type === "api_token") && typeof token === "string" && token.length > 0 ? token : null;
@@ -6911,11 +7265,7 @@ async function runCloudflareEmailStep(input) {
6911
7265
  existingAddresses: rootConfig.addresses,
6912
7266
  existingVars: rootConfig.vars && typeof rootConfig.vars === "object" && !Array.isArray(rootConfig.vars) ? rootConfig.vars : void 0,
6913
7267
  configConflicts: findEmailConfigConflicts(rootConfig),
6914
- wranglerArgs: [
6915
- "-c",
6916
- input.configPath,
6917
- "--env="
6918
- ],
7268
+ configPath: input.configPath,
6919
7269
  apiBaseUrl: input.apiBaseUrl,
6920
7270
  runWrangler: input.runWrangler,
6921
7271
  getSessionBearer: () => runWranglerAuthToken(input.runWrangler, input.configPath),
@@ -7076,14 +7426,12 @@ async function ensureCloudflareAuthSecret(options) {
7076
7426
  beforeRun();
7077
7427
  let put;
7078
7428
  try {
7079
- put = await Promise.resolve(options.runWrangler([
7080
- "secret",
7081
- "put",
7082
- BETTER_AUTH_SECRET_NAME,
7083
- "-c",
7084
- options.configPath,
7085
- "--env="
7086
- ], {
7429
+ put = await Promise.resolve(options.runWrangler({
7430
+ type: "secret.put",
7431
+ name: BETTER_AUTH_SECRET_NAME,
7432
+ configPath: options.configPath,
7433
+ workerName: options.workerName
7434
+ }, {
7087
7435
  capture: true,
7088
7436
  input: `${secret}\n`
7089
7437
  }));
@@ -7110,13 +7458,23 @@ async function prepareCloudflareDeploySource(root, options, voidConfig) {
7110
7458
  ].join("\n"));
7111
7459
  return null;
7112
7460
  }
7113
- const classification = classifyCloudflareDeployApp(root, voidConfig, options);
7461
+ let application;
7462
+ try {
7463
+ application = analyzeApplicationSync(root, {
7464
+ config: voidConfig,
7465
+ environment: "prod"
7466
+ });
7467
+ } catch (error) {
7468
+ log.error(`deploy: ${getErrorMessage(error)}`);
7469
+ return null;
7470
+ }
7471
+ const classification = classifyCloudflareDeployApp(root, voidConfig, options, application);
7114
7472
  if (!classification.supported) {
7115
7473
  log.error(classification.reason);
7116
7474
  return null;
7117
7475
  }
7118
7476
  const configPath = discoverRootWranglerConfig(root);
7119
- const facts = await computeDesiredCloudflareResources(root, voidConfig, configPath);
7477
+ const facts = await computeDesiredCloudflareResources(root, voidConfig, configPath, application);
7120
7478
  const { desired, dialect, migrations: preBuildMigrations, migrationsDir } = facts;
7121
7479
  if (dialect !== "sqlite" && preBuildMigrations.length > 0 && !process.env.DATABASE_URL) {
7122
7480
  log.error([
@@ -7144,51 +7502,35 @@ async function applyAndVerifyRemoteD1Migrations(d1MigrateToken, configPath, runW
7144
7502
  if (d1MigrateToken) {
7145
7503
  log.step("Applying D1 migrations...");
7146
7504
  if (!assertUploaderStillPinned()) return false;
7147
- const migrate = await Promise.resolve(runWrangler([
7148
- "d1",
7149
- "migrations",
7150
- "apply",
7151
- d1MigrateToken,
7152
- "--remote",
7153
- "-c",
7154
- configPath,
7155
- "--env="
7156
- ], { capture: false }));
7505
+ const migrate = await Promise.resolve(runWrangler({
7506
+ type: "d1.migrations.apply",
7507
+ database: d1MigrateToken,
7508
+ configPath
7509
+ }, { capture: false }));
7157
7510
  if (!isCleanWranglerExit(migrate)) {
7158
7511
  log.error(`deploy: remote D1 migrations failed for "${d1MigrateToken}" (exit ${String(migrate.status)}). Fix the migration and rerun. Note: D1 always enforces foreign_keys=ON — a Drizzle table-rebuild using PRAGMA foreign_keys=OFF must use PRAGMA defer_foreign_keys instead.`);
7159
7512
  return false;
7160
7513
  }
7161
7514
  if (!assertUploaderStillPinned()) return false;
7162
- const migrateList = await Promise.resolve(runWrangler([
7163
- "d1",
7164
- "migrations",
7165
- "list",
7166
- d1MigrateToken,
7167
- "--remote",
7168
- "-c",
7169
- configPath,
7170
- "--env="
7171
- ], { capture: true }));
7515
+ const migrateList = await Promise.resolve(runWrangler({
7516
+ type: "d1.migrations.list",
7517
+ database: d1MigrateToken,
7518
+ configPath
7519
+ }, { capture: true }));
7172
7520
  if (!isCleanWranglerExit(migrateList)) {
7173
7521
  log.error(`deploy: could not verify the remote migration state for "${d1MigrateToken}" (\`d1 migrations list\` exit ${String(migrateList.status)}). Refusing to deploy against an unverified schema — rerun once wrangler can reach your account.`);
7174
7522
  return false;
7175
7523
  }
7176
- if (migrateList.stdout.includes("Migrations to be applied:")) {
7177
- log.error([
7178
- `deploy: remote D1 still has UNAPPLIED migrations for "${d1MigrateToken}" after apply.`,
7179
- "`wrangler d1 migrations apply` exits 0 even when its confirmation is declined, so",
7180
- "the schema may be un-migrated while the code that needs it is about to deploy.",
7181
- "Re-run and confirm the migration (or apply it non-interactively), then rerun."
7182
- ].join("\n"));
7524
+ const migrationState = migrateList.data;
7525
+ if (Array.isArray(migrationState?.pending) && migrationState.pending.length > 0) {
7526
+ log.error([`deploy: remote D1 still has UNAPPLIED migrations for "${d1MigrateToken}" after apply.`, "The schema is not current for this deployment. Fix or apply the pending migrations, then retry."].join("\n"));
7183
7527
  return false;
7184
7528
  }
7185
- if (!migrateList.stdout.includes("No migrations to apply!")) {
7529
+ if (!Array.isArray(migrationState?.pending) || !migrationState.pending.every((name) => typeof name === "string")) {
7186
7530
  log.error([
7187
7531
  `deploy: could not confirm the remote migration state for "${d1MigrateToken}".`,
7188
- "`wrangler d1 migrations list` printed neither the \"no migrations to apply\" nor the",
7189
- "\"migrations to be applied\" marker (WRANGLER_LOG is pinned so they are never",
7190
- "suppressed) — refusing to deploy against an unverified schema. Rerun once wrangler",
7191
- "can reach your account."
7532
+ "Cloudflare returned an invalid pending migration set. Refusing to deploy against an unverified schema.",
7533
+ "Retry once the database migration state can be read."
7192
7534
  ].join("\n"));
7193
7535
  return false;
7194
7536
  }
@@ -7324,6 +7666,12 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7324
7666
  if (!source) return exit(1);
7325
7667
  const { configPath, desired, reasons, authEnabled, dialect, migrationsDir, migrations: preBuildMigrations, needsAI, aiBindingName, needsSandbox, needsLive, expectedCrons, needsEmail, emailHandlers } = source;
7326
7668
  let rootConfig = parseRootWranglerConfig(configPath);
7669
+ if (needsSandbox) try {
7670
+ assertSandboxNamespaceConfig(rootConfig, resolveSandboxConfig(voidConfig, root));
7671
+ } catch (error) {
7672
+ log.error(`deploy: ${getErrorMessage(error)}`);
7673
+ return exit(1);
7674
+ }
7327
7675
  let apiBaseUrl;
7328
7676
  try {
7329
7677
  apiBaseUrl = resolveDeployTransport(root, rootConfig);
@@ -7439,8 +7787,8 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7439
7787
  await ensureCloudflareAuthentication({
7440
7788
  acceptApiToken: true,
7441
7789
  beforeRun: assertUploaderStillPinned,
7442
- runWrangler: (args, capture) => runWrangler([...args], { capture }),
7443
- whoamiArgs: [...existingWranglerConfigArgs(configPath), "--env="]
7790
+ runWrangler: (operation, capture) => runWrangler(operation, { capture }),
7791
+ configPath: existsSync(configPath) ? configPath : void 0
7444
7792
  });
7445
7793
  } catch (error) {
7446
7794
  if (error instanceof CloudflareAuthenticationError && error.reason === "uploader-changed") return exit(1);
@@ -7499,13 +7847,10 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7499
7847
  }
7500
7848
  if (needsSandbox) {
7501
7849
  if (!assertUploaderStillPinned()) return exit(1);
7502
- if (!isCleanWranglerExit(await Promise.resolve(runWrangler([
7503
- "containers",
7504
- "list",
7505
- "--json",
7506
- ...existingWranglerConfigArgs(configPath),
7507
- "--env="
7508
- ], { capture: true })))) {
7850
+ if (!isCleanWranglerExit(await Promise.resolve(runWrangler({
7851
+ type: "containers.list",
7852
+ configPath: existsSync(configPath) ? configPath : void 0
7853
+ }, { capture: true })))) {
7509
7854
  log.error(formatCloudflareSandboxUnavailable({ preflight: true }));
7510
7855
  return exit(1);
7511
7856
  }
@@ -7584,19 +7929,16 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7584
7929
  }
7585
7930
  const absentQueues = [];
7586
7931
  for (const name of desired.queues) {
7587
- const info = await Promise.resolve(runWrangler([
7588
- "queues",
7589
- "info",
7932
+ const info = await Promise.resolve(runWrangler({
7933
+ type: "queues.info",
7590
7934
  name,
7591
- "-c",
7592
- configPath,
7593
- "--env="
7594
- ], { capture: true }));
7935
+ configPath
7936
+ }, { capture: true }));
7595
7937
  if (provisionModule.isQueueConfirmedAbsent(info, name)) absentQueues.push(name);
7596
7938
  }
7597
7939
  if (absentQueues.length > 0) await provisionAndVerify();
7598
7940
  }
7599
- const frameworkQueues = detectedFramework ? scanQueuesSync(root, resolveProjectPaths(root, voidConfig).queuesDir) : [];
7941
+ const frameworkQueues = detectedFramework ? source.analysis.queues : [];
7600
7942
  writeProvisionedWranglerConfig(root, {
7601
7943
  name: workerName,
7602
7944
  accountId,
@@ -7622,12 +7964,15 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7622
7964
  const expectedContainers = reconstructExpectedContainers(voidConfig, root, preBuildRootConfig, needsSandbox);
7623
7965
  const expectedDurableObjectState = reconstructExpectedDurableObjectState(voidConfig, root, preBuildRootConfig, {
7624
7966
  needsSandbox,
7625
- needsLive
7967
+ needsLive,
7968
+ analysis: source.analysis
7626
7969
  });
7627
7970
  removeDeployPointer(root);
7628
7971
  const buildChildEnv = scrubManagedDeployBuildMarkers(scrubWranglerAuthCredentials(deployChildEnv(accountId, apiBaseUrl)));
7629
7972
  requireProvisionModule().deleteEnvVarCaseAware(buildChildEnv, CLOUDFLARE_DEPLOY_BUILD_ENV);
7630
7973
  buildChildEnv[CLOUDFLARE_DEPLOY_BUILD_ENV] = "1";
7974
+ requireProvisionModule().deleteEnvVarCaseAware(buildChildEnv, CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV);
7975
+ if (existingWorker) buildChildEnv[CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV] = "1";
7631
7976
  requireProvisionModule().deleteEnvVarCaseAware(buildChildEnv, "NODE_ENV");
7632
7977
  buildChildEnv.NODE_ENV = "production";
7633
7978
  const runBuild = deps.runBuild ?? ((env) => {
@@ -7646,7 +7991,7 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7646
7991
  }
7647
7992
  log.step("Preparing deployment...");
7648
7993
  if (detectedFramework && frameworkPreset) try {
7649
- await (deps.prepareFrameworkArtifact ?? prepareCloudflareFrameworkArtifact)(root, configPath, accountId, workerName, voidConfig, detectedFramework, frameworkPreset, schemaFacts.requiredKeys, needsAI ? { binding: aiBindingName } : void 0);
7994
+ await (deps.prepareFrameworkArtifact ?? prepareCloudflareFrameworkArtifact)(root, configPath, accountId, workerName, voidConfig, detectedFramework, frameworkPreset, schemaFacts.requiredKeys, needsAI ? { binding: aiBindingName } : void 0, existingWorker !== null);
7650
7995
  } catch (error) {
7651
7996
  log.error(`deploy: could not prepare ${detectedFramework.name} for Cloudflare — ${getErrorMessage(error)}`);
7652
7997
  return exit(1);
@@ -7759,7 +8104,7 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7759
8104
  log.error("deploy: cf could not prepare the built Worker for deployment.");
7760
8105
  return exit(1);
7761
8106
  }
7762
- preflight.dispose();
8107
+ await preflight.dispose();
7763
8108
  }
7764
8109
  let workerWasMissingBeforeSecretProvisioning = false;
7765
8110
  let initialAuthSecret;
@@ -7897,6 +8242,8 @@ async function runCloudflareDeploy(root, options, cliLog, deps = {}) {
7897
8242
  workerName,
7898
8243
  configPath: generatedConfigPath,
7899
8244
  preserveTriggers: existingWorker !== null,
8245
+ requireVersionMetadata: existingWorker === null || readVersionMetadataBinding(parseRootWranglerConfig(generatedConfigPath)) !== void 0,
8246
+ requirePreviewForReadiness: existingWorker !== null && readVersionMetadataBinding(parseRootWranglerConfig(generatedConfigPath)) === void 0,
7900
8247
  expectedPreviousVersionId: existingWorker?.versionId,
7901
8248
  trustedInheritanceVersionIds: existingWorker ? candidateReceipt?.parentVersionId === existingWorker.versionId ? candidateReceipt.versionIds : void 0 : initialCloudflareCandidate?.versionIds,
7902
8249
  validateUploadedVersion: existingWorker ? async (versionId) => {
@@ -7972,10 +8319,6 @@ function discoverRootWranglerConfig(root) {
7972
8319
  if (existsSync(join(root, "wrangler.toml"))) throw new Error("Found wrangler.toml. Direct Cloudflare deployment needs one root wrangler.jsonc or wrangler.json with the same settings. Convert the config before deploying.");
7973
8320
  return materializeCloudflareConfig(root);
7974
8321
  }
7975
- /** Avoid making Wrangler open a config path that Void has not created yet. */
7976
- function existingWranglerConfigArgs(configPath) {
7977
- return existsSync(configPath) ? ["-c", configPath] : [];
7978
- }
7979
8322
  async function reportCloudflareActivationFailure(cliLog, message) {
7980
8323
  cliLog.error("cloudflare_deploy_failed", new Error(message));
7981
8324
  await cliLog.close().catch(() => {});
@@ -8008,7 +8351,9 @@ function formatCloudflareActivationError(result) {
8008
8351
  if (result.rollback === "failed") return "deploy: the verified Wrangler uploader changed after version activation, and automatic traffic restoration failed. Inspect `void project status` immediately.";
8009
8352
  if (result.rollback === "unsafe") return "deploy: the verified Wrangler uploader changed after version activation. Void did not invoke the changed binary to restore traffic; reinstall Wrangler from a trusted lockfile and inspect `void project status` immediately.";
8010
8353
  return "deploy: the verified Wrangler uploader changed before version activation.";
8011
- case "status-failed": return `deploy: could not snapshot the current Cloudflare deployment${status}; no version was uploaded.`;
8354
+ case "status-failed":
8355
+ if (result.versionId) return `deploy: Worker version ${result.versionId} was published, but current deployment status could not be verified${status}. Inspect \`void project status\` before retrying.`;
8356
+ return `deploy: could not snapshot the current Cloudflare deployment${status}; no version was uploaded.`;
8012
8357
  case "deployment-changed": return result.triggerRepair === "succeeded" ? `deploy: another Cloudflare operation superseded this deployment. Void restored triggers for the active version ${result.activeVersionId ?? "(unknown)"}; retry from fresh deployment status.` : `deploy: another Cloudflare operation superseded this deployment, but its trigger state could not be verified. Inspect the active Worker version ${result.activeVersionId ?? "(unknown)"} immediately.`;
8013
8358
  case "inheritance-source-unavailable": return "deploy: could not verify which Worker Version would supply inherited secrets; production traffic is unchanged.";
8014
8359
  case "inheritance-source-changed": return [
@@ -8024,16 +8369,18 @@ function formatCloudflareActivationError(result) {
8024
8369
  ].join("\n");
8025
8370
  case "upload-failed": return `deploy: Cloudflare Worker Version upload failed${status}; production traffic is unchanged.${cloudflareCode}${diagnostic}`;
8026
8371
  case "upload-output-invalid": return "deploy: Wrangler uploaded a version but did not report its version id; production traffic is unchanged.";
8027
- case "preview-unavailable": return [
8028
- "deploy: the uploaded Worker Version has no preview URL, so Void cannot verify readiness.",
8029
- "Set CLOUDFLARE_WORKERS_SUBDOMAIN to your account subdomain (for example \"my-team\")",
8030
- "and retry. Void caches it after a successful workers.dev deployment; production traffic is unchanged."
8031
- ].join("\n");
8372
+ case "preview-unavailable":
8373
+ if (result.diagnostic) return `deploy: candidate readiness cannot be verified.\n\n${result.diagnostic}`;
8374
+ return [
8375
+ "deploy: the uploaded Worker Version has no preview URL, so Void cannot verify readiness.",
8376
+ "Set CLOUDFLARE_WORKERS_SUBDOMAIN to your account subdomain (for example \"my-team\")",
8377
+ "and retry. Void caches it after a successful workers.dev deployment; production traffic is unchanged."
8378
+ ].join("\n");
8032
8379
  case "readiness-staging-failed": return `deploy: Cloudflare could not stage the uploaded Worker Version at zero-percent traffic${status}; production traffic is unchanged.${cloudflareCode}${diagnostic}${result.cloudflareErrorCode === 10210 && /Durable Object migration/i.test(result.diagnostic ?? "") ? "\n\nReview the Durable Object migration, then run `void deploy --platform cloudflare --atomic`. For future deploys, set deploy.cloudflare.mode to \"atomic\" in void.config.ts." : ""}`;
8033
8380
  case "atomic-failed": return `deploy: Cloudflare could not publish the Worker atomically${status}; inspect production status before retrying.${cloudflareCode}${diagnostic}`;
8034
8381
  case "atomic-output-invalid": return "deploy: Cloudflare published atomically but did not report a version id or URL. Inspect `void project status` immediately.";
8035
8382
  case "atomic-readiness-failed":
8036
- case "atomic-readiness-access-denied": return "deploy: the atomic Worker deployment is live, but /__void/ready could not be verified. Inspect `void project status` and Worker logs immediately.";
8383
+ case "atomic-readiness-access-denied": return `deploy: the atomic Worker deployment is live, but /__void/ready could not be verified. Inspect \`void project status\` and Worker logs immediately.${diagnostic}`;
8037
8384
  case "readiness-failed": return ["deploy: the uploaded Worker Version did not pass /__void/ready.", formatCloudflareReadinessRestoration(result)].join("\n");
8038
8385
  case "readiness-access-denied": return [
8039
8386
  "deploy: Cloudflare Access blocked the uploaded Worker Version readiness probe.",
@@ -8048,7 +8395,7 @@ function formatCloudflareActivationError(result) {
8048
8395
  "deploy: the first Worker deployment did not pass /__void/ready.",
8049
8396
  "Cloudflare requires this bootstrap to be non-versioned, so the new Worker is already active;",
8050
8397
  "inspect its logs and `void project status` before retrying."
8051
- ].join("\n");
8398
+ ].join("\n") + diagnostic;
8052
8399
  case "bootstrap-readiness-access-denied": return [
8053
8400
  "deploy: Cloudflare Access blocked the first Worker deployment readiness probe.",
8054
8401
  "For CI, set CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET to an admitted service token;",
@@ -8058,7 +8405,7 @@ function formatCloudflareActivationError(result) {
8058
8405
  "Worker is already active behind Access."
8059
8406
  ].join("\n");
8060
8407
  case "activation-failed": return `deploy: the ready Worker Version could not be activated${status}; production traffic is unchanged.`;
8061
- case "triggers-failed": return `deploy: the Worker activated, but Cloudflare trigger synchronization failed${status}. ${result.rollback === "succeeded" ? "Traffic was restored to the previous version." : result.rollback === "unavailable" ? result.previousVersionId === null ? "This was the first deployment, so no previous version was available to restore." : "Traffic was restored to the previous version, but its trigger configuration was unavailable. Inspect its routes, schedules, queues, and workflows in Cloudflare before retrying." : "Automatic traffic restoration failed; inspect `void project status` immediately."}`;
8408
+ case "triggers-failed": return `deploy: the Worker activated, but Cloudflare trigger synchronization failed${status}. ${result.rollback === "succeeded" ? "Traffic was restored to the previous version." : result.rollback === "unavailable" ? result.previousVersionId === null ? "This was the first deployment, so no previous version was available to restore." : "Traffic or trigger restoration could not be completed. Inspect its routes, schedules, queues, and workflows in Cloudflare before retrying." : "Automatic traffic restoration failed; inspect `void project status` immediately."}`;
8062
8409
  }
8063
8410
  }
8064
8411
  function formatCloudflareReadinessRestoration(result) {
@@ -8079,29 +8426,36 @@ function cloudflareWorkerReadinessHeaders() {
8079
8426
  });
8080
8427
  }
8081
8428
  /** Build the trusted Wrangler pointer for a framework that emits Worker files directly. */
8082
- async function prepareCloudflareFrameworkArtifact(root, configPath, accountId, workerName, voidConfig, framework, preset, requiredSecretNames = [], ai) {
8429
+ async function prepareCloudflareFrameworkArtifact(root, configPath, accountId, workerName, voidConfig, framework, preset, requiredSecretNames = [], ai, preserveBindings = false) {
8083
8430
  const workerMain = join(root, preset.workerDir, preset.workerMain);
8084
8431
  const assetsDirectory = join(root, preset.assetsDir);
8085
8432
  if (!existsSync(workerMain)) throw new Error(`framework ${framework.name} did not emit its Worker entry at ${preset.workerDir}/${preset.workerMain}`);
8086
8433
  if (!existsSync(assetsDirectory)) throw new Error(`framework ${framework.name} did not emit its asset directory at ${preset.assetsDir}`);
8087
- const paths = resolveProjectPaths(root, voidConfig);
8088
- const crons = scanJobsSync(root, paths.cronsDir);
8089
- const queues = scanQueuesSync(root, paths.queuesDir);
8434
+ const analysis = analyzeApplicationSync(root, {
8435
+ config: voidConfig,
8436
+ environment: "prod"
8437
+ });
8438
+ const crons = analysis.jobs;
8439
+ const queues = analysis.queues;
8090
8440
  const revalidate = collectRevalidate(voidConfig, null);
8441
+ const rootConfig = parseRootWranglerConfig(configPath);
8442
+ const versionMetadataBinding = selectNativeVersionMetadataBinding(rootConfig, {
8443
+ nativeBuild: true,
8444
+ preserveBindings
8445
+ });
8091
8446
  await generateDeployWrapperIfNeeded(root, join(root, preset.workerDir), preset.workerMain, {
8092
- auth: null,
8093
8447
  crons,
8094
8448
  queues,
8095
8449
  hasMigrations: false,
8096
- migrationDialect: getDatabaseDialect(voidConfig),
8450
+ migrationDialect: analysis.dialect,
8097
8451
  readiness: true,
8452
+ versionMetadataBinding,
8098
8453
  ...hasRevalidate(revalidate) && { isr: {
8099
8454
  revalidate,
8100
8455
  queryAllowlist: voidConfig.routing?.revalidateQueryAllowlist
8101
8456
  } }
8102
8457
  });
8103
8458
  if (preset.cloudflareArtifact === "vite-plugin") return;
8104
- const rootConfig = parseRootWranglerConfig(configPath);
8105
8459
  writeCloudflareFrameworkDeployConfig({
8106
8460
  configPath,
8107
8461
  deployPointerPath: deployPointerPath(root),
@@ -8113,6 +8467,7 @@ async function prepareCloudflareFrameworkArtifact(root, configPath, accountId, w
8113
8467
  compatibilityFlags: preset.cloudflareCompatibilityFlags,
8114
8468
  crons: [...new Set(crons.flatMap((job) => job.crons))],
8115
8469
  requiredSecrets: requiredSecretNames,
8470
+ versionMetadataBinding,
8116
8471
  ...ai ? { ai: {
8117
8472
  ...ai,
8118
8473
  gateway: voidConfig.ai?.gateway
@@ -8152,23 +8507,14 @@ function resolveCloudflareStaticPreset(root, voidConfig, options) {
8152
8507
  */
8153
8508
  async function resolveCloudflareVoidStaticPreset(root, voidConfig) {
8154
8509
  if (voidConfig.output !== "static") return null;
8155
- const paths = resolveProjectPaths(root, voidConfig);
8156
- if (!existsSync(paths.pagesDir) || validateSsrEntry(root, paths) !== null) return null;
8157
- const routeScan = await scanRoutes(root, {
8158
- output: voidConfig.output,
8159
- paths,
8510
+ const analysis = await analyzeApplication(root, {
8511
+ config: voidConfig,
8160
8512
  environment: "prod"
8161
8513
  });
8162
- const pageScan = routeScan.pages.pages.length > 0 ? routeScan.pages : await scanPages(root, {
8163
- output: voidConfig.output,
8164
- paths
8165
- });
8514
+ const { paths, authEnabled, bindings, scan: routeScan } = analysis;
8515
+ if (!existsSync(paths.pagesDir) || validateSsrEntry(root, paths) !== null) return null;
8516
+ const pageScan = routeScan.pages;
8166
8517
  if (pageScan.pages.length === 0 || !pageScan.pages.every((page) => page.shouldPrerender && (page.params.length === 0 && !page.catchAll || page.hasGetPrerenderPaths))) return null;
8167
- const bindings = inferProjectBindings(root, {
8168
- paths,
8169
- environment: "prod"
8170
- });
8171
- const authEnabled = bindings.needsAuth || findVoidAuthConfig(root, paths) !== null || voidConfig.auth !== void 0;
8172
8518
  if (hasNonPageBackend({
8173
8519
  hasRoutes: routeScan.routes.length > 0,
8174
8520
  hasMiddleware: routeScan.middleware.length > 0,
@@ -8176,7 +8522,7 @@ async function resolveCloudflareVoidStaticPreset(root, voidConfig) {
8176
8522
  hasEmail: routeScan.email !== void 0 && hasEmailHandlers(routeScan.email),
8177
8523
  authEnabled,
8178
8524
  hasLive: bindings.needsLive === true
8179
- }) || scanJobsSync(root, paths.cronsDir).length > 0 || scanQueuesSync(root, paths.queuesDir).length > 0) return null;
8525
+ }) || analysis.jobs.length > 0 || analysis.queues.length > 0) return null;
8180
8526
  return {
8181
8527
  buildCommand: resolveFullDeployBuildCommand(root, voidConfig.inference?.build).command,
8182
8528
  outputDir: join(root, "dist", "client"),
@@ -8237,8 +8583,8 @@ async function runCloudflareStaticDeploy(root, options, voidConfig, preset, cliL
8237
8583
  await ensureCloudflareAuthentication({
8238
8584
  acceptApiToken: true,
8239
8585
  beforeRun: uploaderIsPinned,
8240
- runWrangler: (args, capture) => runWrangler([...args], { capture }),
8241
- whoamiArgs: [...existingWranglerConfigArgs(configPath), "--env="]
8586
+ runWrangler: (operation, capture) => runWrangler(operation, { capture }),
8587
+ configPath: existsSync(configPath) ? configPath : void 0
8242
8588
  });
8243
8589
  } catch (error) {
8244
8590
  if (error instanceof CloudflareAuthenticationError && error.reason === "uploader-changed") return exit(1);
@@ -8306,12 +8652,6 @@ async function runCloudflareStaticDeploy(root, options, voidConfig, preset, cliL
8306
8652
  });
8307
8653
  const entryPath = join(root, ".void", "deploy", "cloudflare", "static-edge.mjs");
8308
8654
  mkdirSync(dirname(entryPath), { recursive: true });
8309
- writeFileSync(entryPath, renderCloudflareStaticEntry({
8310
- runtimeImport: "void/edge",
8311
- headerRules: manifest.headerRules,
8312
- redirectRules: manifest.redirectRules,
8313
- fallbackRules: manifest.fallbackRules
8314
- }));
8315
8655
  const workerName = readWranglerTopLevelString(rootConfig, "name") ?? deriveWorkerName(root);
8316
8656
  const staticConfigPath = join(dirname(entryPath), "wrangler-static.json");
8317
8657
  writeFileSync(staticConfigPath, `${JSON.stringify(rootConfig, null, 2)}\n`, { mode: 384 });
@@ -8325,6 +8665,22 @@ async function runCloudflareStaticDeploy(root, options, voidConfig, preset, cliL
8325
8665
  assetConfig: manifest.assetConfig ?? { not_found_handling: "none" }
8326
8666
  });
8327
8667
  deduplicateGeneratedCompatibilityFlags(staticConfigPath, configPath);
8668
+ const staticConfig = parseRootWranglerConfig(staticConfigPath);
8669
+ const versionMetadataBinding = selectNativeVersionMetadataBinding(staticConfig, {
8670
+ nativeBuild: true,
8671
+ preserveBindings: existingWorker !== null
8672
+ });
8673
+ if (versionMetadataBinding && staticConfig.version_metadata === void 0) writeFileSync(staticConfigPath, `${JSON.stringify({
8674
+ ...staticConfig,
8675
+ version_metadata: { binding: versionMetadataBinding }
8676
+ }, null, 2)}\n`, { mode: 384 });
8677
+ writeFileSync(entryPath, renderCloudflareStaticEntry({
8678
+ runtimeImport: "void/edge",
8679
+ headerRules: manifest.headerRules,
8680
+ redirectRules: manifest.redirectRules,
8681
+ fallbackRules: manifest.fallbackRules,
8682
+ versionMetadataBinding
8683
+ }));
8328
8684
  if (existingWorker) assertExistingCloudflareConfig(existingWorker, parseRootWranglerConfig(staticConfigPath), true);
8329
8685
  const stagedAssetsDirectory = join(root, ".void", "deploy", "cloudflare", "assets");
8330
8686
  stageCloudflareStaticAssets({
@@ -8375,6 +8731,8 @@ async function runCloudflareStaticDeploy(root, options, voidConfig, preset, cliL
8375
8731
  verifyUploader: cfOutput ? cfUploaderIsPinned : uploaderIsPinned,
8376
8732
  configPath: staticConfigPath,
8377
8733
  preserveTriggers: existingWorker !== null,
8734
+ requireVersionMetadata: existingWorker === null || versionMetadataBinding !== void 0,
8735
+ requirePreviewForReadiness: existingWorker !== null && versionMetadataBinding === void 0,
8378
8736
  expectedPreviousVersionId: existingWorker?.versionId,
8379
8737
  trustedInheritanceVersionIds: existingWorker ? candidateReceipt?.parentVersionId === existingWorker.versionId ? candidateReceipt.versionIds : void 0 : initialCloudflareCandidate?.versionIds,
8380
8738
  validateUploadedVersion: existingWorker ? async (versionId) => {
@@ -9132,7 +9490,6 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
9132
9490
  mode: "framework",
9133
9491
  framework: detected.name
9134
9492
  });
9135
- const dialect = getDatabaseDialect(voidConfig);
9136
9493
  const buildCmd = voidConfig.inference?.build ?? formatProjectCommand(root, fwPreset.buildCommand);
9137
9494
  if (!skipBuild) {
9138
9495
  log.step(`Building (${buildCmd})...`);
@@ -9162,15 +9519,12 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
9162
9519
  log.error(`deploy: Worker output '${fwPreset.workerDir}' not found. Run '${buildCmd}' first.`);
9163
9520
  process.exit(1);
9164
9521
  }
9165
- const inferenceOptions = voidConfig.inference?.bindings ? { bindings: voidConfig.inference.bindings } : void 0;
9166
- const paths = resolveProjectPaths(root, voidConfig);
9167
- const scanDirs = voidConfig.inference?.scanDirs ?? FRAMEWORK_SCAN_DIRS;
9168
- const bindings = inferProjectBindings(root, {
9169
- ...inferenceOptions,
9170
- paths,
9522
+ const analysis = analyzeApplicationSync(root, {
9523
+ config: voidConfig,
9171
9524
  environment: "prod"
9172
- }, scanDirs);
9173
- if (bindings.needsAuth || findVoidAuthConfig(root, paths) !== null || voidConfig.auth !== void 0) {
9525
+ });
9526
+ const { paths, bindings, authEnabled, dialect } = analysis;
9527
+ if (authEnabled) {
9174
9528
  log.error("deploy: Void-managed Better Auth is only supported in Void apps. Use Better Auth's official integration for this framework instead.");
9175
9529
  process.exit(1);
9176
9530
  }
@@ -9180,7 +9534,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
9180
9534
  }
9181
9535
  const migrationsDir = paths.migrationsDir;
9182
9536
  assertDeploySchemaDriftFree(root, dialect);
9183
- const rawMigrations = collectMigrations(migrationsDir);
9537
+ const rawMigrations = analysis.migrations;
9184
9538
  let validatedMigrations;
9185
9539
  try {
9186
9540
  assertJournalCoherence(migrationsDir, rawMigrations);
@@ -9195,14 +9549,14 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
9195
9549
  log.error(getErrorMessage(err));
9196
9550
  process.exit(1);
9197
9551
  }
9198
- if (dialect !== "sqlite" && (bindings.needsD1 || rawMigrations.length > 0)) {
9552
+ if (dialect !== "sqlite" && analysis.needsDatabase) {
9199
9553
  let resources = null;
9200
9554
  try {
9201
9555
  resources = await client.getResources(config.projectId);
9202
9556
  } catch {}
9203
9557
  if (resources) {
9204
9558
  if (!resources.some((resource) => resource.type === "hyperdrive")) {
9205
- const { text: promptText, isCancel: isCancelPrompt } = await import("./dist-C5fND3R0.mjs");
9559
+ const { text: promptText, isCancel: isCancelPrompt } = await import("./dist-BuDuKZJv.mjs");
9206
9560
  const connectionString = await promptText({
9207
9561
  message: `Your project uses ${dialect === "mysql" ? "MySQL" : "PostgreSQL"}. Enter your connection string:`,
9208
9562
  placeholder: dialect === "mysql" ? "mysql://user:pass@host:3306/dbname" : "postgresql://user:pass@host:5432/dbname",
@@ -9219,11 +9573,10 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
9219
9573
  }
9220
9574
  }
9221
9575
  }
9222
- const rawJobs = scanJobsSync(root, paths.cronsDir);
9223
- const rawQueues = scanQueuesSync(root, paths.queuesDir);
9576
+ const rawJobs = analysis.jobs;
9577
+ const rawQueues = analysis.queues;
9224
9578
  const originalEntry = join(workerDir, fwPreset.workerMain);
9225
9579
  const wrapperFeatures = {
9226
- auth: null,
9227
9580
  crons: rawJobs,
9228
9581
  queues: rawQueues,
9229
9582
  hasMigrations: validatedMigrations != null && validatedMigrations.length > 0,
@@ -9279,7 +9632,7 @@ async function runFrameworkDeploy(root, config, client, detected, fwPreset, void
9279
9632
  const packageStartedAt = Date.now();
9280
9633
  const neededSet = skipped > 0 ? new Set(needed) : null;
9281
9634
  const packageOptions = {
9282
- bindingNames: resolveBindingNames(voidConfig.inference?.bindings),
9635
+ bindingNames: analysis.bindingNames,
9283
9636
  workerMain: actualWorkerMain,
9284
9637
  compatibilityDate: wranglerCompat?.compatibilityDate,
9285
9638
  compatibilityFlags: wranglerCompat?.compatibilityFlags,
@@ -9386,20 +9739,12 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
9386
9739
  if (result.paths.length === 0) log.info("No pages to prerender");
9387
9740
  else if (result.failed.length > 0) voidWarn(`${result.failed.length} page(s) failed to prerender`);
9388
9741
  }
9389
- const paths = resolveProjectPaths(root, deployConfig);
9390
- const dialect = getDatabaseDialect(deployConfig);
9391
- const deployScanDirs = deployConfig.inference?.scanDirs ?? (isFrameworkMode ? FRAMEWORK_SCAN_DIRS : void 0);
9392
- const bindings = inferProjectBindings(root, {
9393
- paths,
9742
+ const analysis = await analyzeApplication(root, {
9743
+ config: deployConfig,
9394
9744
  environment: "prod"
9395
- }, deployScanDirs);
9396
- if (isSandboxEnabled(deployConfig)) bindings.needsSandbox = true;
9397
- const authEnabled = bindings.needsAuth || findVoidAuthConfig(root, paths) !== null || deployConfig.auth !== void 0;
9398
- const effectiveBindings = authEnabled ? {
9399
- ...bindings,
9400
- needsAuth: true,
9401
- needsD1: true
9402
- } : bindings;
9745
+ });
9746
+ const { paths, dialect, bindings, authEnabled, scan: routeScan } = analysis;
9747
+ const effectiveBindings = bindings;
9403
9748
  if (isFrameworkMode && authEnabled) {
9404
9749
  log.error("Void-managed Better Auth is only supported in Void apps. Use Better Auth's official integration for this framework instead.");
9405
9750
  process.exit(1);
@@ -9429,7 +9774,7 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
9429
9774
  lintDestinationSplats(fallbackRules, "fallbacks (post-asset)");
9430
9775
  const migrationsDir = paths.migrationsDir;
9431
9776
  assertDeploySchemaDriftFree(root, dialect);
9432
- const rawMigrations = collectMigrations(migrationsDir);
9777
+ const rawMigrations = analysis.migrations;
9433
9778
  let validatedMigrations;
9434
9779
  try {
9435
9780
  assertJournalCoherence(migrationsDir, rawMigrations);
@@ -9444,14 +9789,14 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
9444
9789
  log.error(getErrorMessage(err));
9445
9790
  process.exit(1);
9446
9791
  }
9447
- if (dialect !== "sqlite" && (effectiveBindings.needsD1 || rawMigrations.length > 0)) {
9792
+ if (dialect !== "sqlite" && analysis.needsDatabase) {
9448
9793
  let resources = null;
9449
9794
  try {
9450
9795
  resources = await client.getResources(config.projectId);
9451
9796
  } catch {}
9452
9797
  if (resources) {
9453
9798
  if (!resources.some((resource) => resource.type === "hyperdrive")) {
9454
- const { text: promptText, isCancel: isCancelPrompt } = await import("./dist-C5fND3R0.mjs");
9799
+ const { text: promptText, isCancel: isCancelPrompt } = await import("./dist-BuDuKZJv.mjs");
9455
9800
  const connectionString = await promptText({
9456
9801
  message: `Your project uses ${dialect === "mysql" ? "MySQL" : "PostgreSQL"}. Enter your connection string:`,
9457
9802
  placeholder: dialect === "mysql" ? "mysql://user:pass@host:3306/dbname" : "postgresql://user:pass@host:5432/dbname",
@@ -9468,18 +9813,13 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
9468
9813
  }
9469
9814
  }
9470
9815
  }
9471
- const rawJobs = scanJobsSync(root, paths.cronsDir);
9472
- const rawQueues = scanQueuesSync(root, paths.queuesDir);
9816
+ const rawJobs = analysis.jobs;
9817
+ const rawQueues = analysis.queues;
9473
9818
  const schedules = rawJobs.flatMap((job) => job.crons.map((cron) => ({
9474
9819
  name: job.name,
9475
9820
  cron
9476
9821
  })));
9477
- const routeScan = await scanRoutes(root, {
9478
- output: deployConfig.output,
9479
- paths,
9480
- environment: "prod"
9481
- });
9482
- const webSockets = routeScan.websockets ?? scanWebSocketRoutesSync(root, paths, "prod");
9822
+ const webSockets = analysis.websockets;
9483
9823
  const liveConfig = await resolveDeployLiveBindingConfig(root, client, config.projectId, {
9484
9824
  isFrameworkMode,
9485
9825
  needsLive: effectiveBindings.needsLive,
@@ -9501,10 +9841,7 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
9501
9841
  const hasWebSockets = webSockets.length > 0;
9502
9842
  const ssrEntry = isFrameworkMode ? null : validateSsrEntry(root, paths);
9503
9843
  const isSsr = isFrameworkMode ? true : Boolean(ssrEntry) || hasPages && deployConfig.output !== "static";
9504
- const pageScan = existsSync(paths.pagesDir) ? routeScan.pages.pages.length > 0 ? routeScan.pages : await scanPages(root, {
9505
- output: deployConfig.output,
9506
- paths
9507
- }) : null;
9844
+ const pageScan = hasPages ? routeScan.pages : null;
9508
9845
  const revalidate = collectRevalidate(deployConfig, pageScan);
9509
9846
  if (deployConfig.output === "static" && pageScan && !isSsr) {
9510
9847
  const allPrerenderable = pageScan.pages.every((p) => p.shouldPrerender && (p.params.length === 0 && !p.catchAll || p.hasGetPrerenderPaths));
@@ -9628,7 +9965,6 @@ async function runFullDeploy(root, config, client, voidConfig, fwPreset, skipBui
9628
9965
  const workerDir = join(distDir, workerDirName);
9629
9966
  if (isFrameworkMode) {
9630
9967
  const wrapperFeatures = {
9631
- auth: null,
9632
9968
  crons: rawJobs,
9633
9969
  queues: rawQueues,
9634
9970
  hasMigrations: validatedMigrations != null && validatedMigrations.length > 0,
@@ -9672,6 +10008,7 @@ async function createProjectFromSlug(root, client, slug, cliLog) {
9672
10008
  };
9673
10009
  writeProjectConfig(root, attachSelectedPlatform(root, config, client.apiUrl));
9674
10010
  log.success(`Project created: ${import_picocolors$1.default.blue(config.slug)}`);
10011
+ reportProjectCreationWarnings(project);
9675
10012
  cliLog?.info("project_create_success", {
9676
10013
  projectId: config.projectId,
9677
10014
  slug: config.slug
@@ -9898,4 +10235,4 @@ function resolveWorkerDirName(distDir, fwPreset, buildCommand) {
9898
10235
  throw new Error("deploy: Worker output not found. Expected dist/ssr/ or dist/rsc/.");
9899
10236
  }
9900
10237
  //#endregion
9901
- export { snapshotPassthroughRawFields as $, parseRootWranglerConfig$1 as A, resolveDeploymentPlatform as B, generateDeployWrapperIfNeeded as C, resolveAssetConfig as Ct, hashWranglerUploader as D, hasPrerenderSources as E, resolveCloudflareVoidStaticPreset as F, resolveWorkerDirName as G, resolveFullDeployBuildCommand as H, resolveDeployLiveBindingConfig as I, runDeploy as J, resolveWranglerMigrationPattern as K, resolveDeployTransport as L, promptDeployProjectConfig as M, reconstructExpectedContainers as N, isWranglerNonInteractiveOrCI as O, reconstructExpectedDurableObjectState as P, snapshotPassthroughIdentities as Q, resolveDeployWorkerMain as R, formatCloudflareSandboxUnavailable as S, hasDocumentRoutes as St, hasNonPageBackend as T, resolveStaticBuildCommand as U, resolveDistDir as V, resolveStaticDeployPreset as W, runWranglerAuthToken as X, runStaticDeploy as Y, snapshotBindingIdentities as Z, deriveWorkerName as _, findEmailConfigConflicts as _t, applyDeployEvent as a, warnUnpublishedRouting as at, ensurePreviouslyDeployedLiveBindingConfig as b, renderEmailChecklist as bt, assertMigrationsProduceAuthSchema as c, prerenderPagesNode as ct, assertWranglerUploaderUnchanged as d, collectRevalidate as dt, streamDeployEvents as et, checkWranglerMigrationApplySet as f, hasRevalidate as ft, deployChildEnv as g, createNodeEmailDns as gt, createDeployWranglerRunner as h, applyEmailSetup as ht, DEPLOY_WHOLE_OBJECT_PINNED_FIELDS as i, warnRedundantForceOn3xx as it, prepareCloudflareFrameworkArtifact as j, loadProvisionModule as k, assertNoConfigOverrideInProjectDotenv as l, buildEnv as lt, computeDesiredCloudflareResources as m, SEND_EMAIL_BINDING as mt, DEPLOY_PASSTHROUGH_ASSERT_FIELDS as n, verifyGeneratedDeployConfig as nt, assembleVoidManifest as o, generateQueueResolverSource as ot, compareWranglerMigrationPaths as p, scanDurableObjectModules as pt, runCloudflareDeploy as q, DEPLOY_PASSTHROUGH_EXCLUDE_FIELDS as r, warnIgnoredNotFoundForFramework as rt, assertManagedDurableStateSupported as s, prerenderPages as st, CloudflareAuthSecretError as t, validateAuthMigrationSchema as tt, assertRemoteMigrationsDeployable as u, collectPages as ut, discoverRootWranglerConfig as v, preflightEmail as vt, hasDeploySchemaDrift as w, resolveAssetRoutingPolicy as wt, formatCloudflareActivationError as x, hasApiRoutes as xt, ensureCloudflareAuthSecret as y, renderAddressMap as yt, resolveDeployWranglerCompat as z };
10238
+ export { snapshotPassthroughRawFields as $, parseRootWranglerConfig$1 as A, resolveDeploymentPlatform as B, generateDeployWrapperIfNeeded as C, hasDocumentRoutes as Ct, hashWranglerUploader as D, hasPrerenderSources as E, resolveCloudflareVoidStaticPreset as F, resolveWorkerDirName as G, resolveFullDeployBuildCommand as H, resolveDeployLiveBindingConfig as I, runDeploy as J, resolveWranglerMigrationPattern as K, resolveDeployTransport as L, promptDeployProjectConfig as M, reconstructExpectedContainers as N, isWranglerNonInteractiveOrCI as O, reconstructExpectedDurableObjectState as P, snapshotPassthroughIdentities as Q, resolveDeployWorkerMain as R, formatCloudflareSandboxUnavailable as S, hasApiRoutes as St, hasNonPageBackend as T, resolveAssetRoutingPolicy as Tt, resolveStaticBuildCommand as U, resolveDistDir as V, resolveStaticDeployPreset as W, runWranglerAuthToken as X, runStaticDeploy as Y, snapshotBindingIdentities as Z, deriveWorkerName as _, createNodeEmailDns as _t, applyDeployEvent as a, warnUnpublishedRouting as at, ensurePreviouslyDeployedLiveBindingConfig as b, renderAddressMap as bt, assertMigrationsProduceAuthSchema as c, selectNativeVersionMetadataBinding as ct, assertWranglerUploaderUnchanged as d, buildEnv as dt, streamDeployEvents as et, checkWranglerMigrationApplySet as f, collectPages as ft, deployChildEnv as g, applyEmailSetup as gt, createDeployWranglerRunner as h, SEND_EMAIL_BINDING as ht, DEPLOY_WHOLE_OBJECT_PINNED_FIELDS as i, warnRedundantForceOn3xx as it, prepareCloudflareFrameworkArtifact as j, loadProvisionModule as k, assertNoConfigOverrideInProjectDotenv as l, prerenderPages as lt, computeDesiredCloudflareResources as m, hasRevalidate as mt, DEPLOY_PASSTHROUGH_ASSERT_FIELDS as n, verifyGeneratedDeployConfig as nt, assembleVoidManifest as o, generateQueueResolverSource as ot, compareWranglerMigrationPaths as p, collectRevalidate as pt, runCloudflareDeploy as q, DEPLOY_PASSTHROUGH_EXCLUDE_FIELDS as r, warnIgnoredNotFoundForFramework as rt, assertManagedDurableStateSupported as s, CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV as st, CloudflareAuthSecretError as t, validateAuthMigrationSchema as tt, assertRemoteMigrationsDeployable as u, prerenderPagesNode as ut, discoverRootWranglerConfig as v, findEmailConfigConflicts as vt, hasDeploySchemaDrift as w, resolveAssetConfig as wt, formatCloudflareActivationError as x, renderEmailChecklist as xt, ensureCloudflareAuthSecret as y, preflightEmail as yt, resolveDeployWranglerCompat as z };