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.
- package/README.md +5 -1
- package/dist/{account-cmd-DZZGK80W.mjs → account-cmd-C84Ee8cO.mjs} +5 -5
- package/dist/{scan-ClYmX3sa.mjs → application-analysis-BVsVqc11.mjs} +135 -18
- package/dist/application-routing-B7QEjqSR.mjs +27 -0
- package/dist/{auth-CZuiVsFh.mjs → auth-CCjt0hcq.mjs} +1 -1
- package/dist/{auth-DLNN0D3Z.mjs → auth-CSkdO2Bb.mjs} +4 -47
- package/dist/{auth-link-B_CeugBw.mjs → auth-link-CioEg6uY.mjs} +5 -5
- package/dist/{auth-router-BgEFRuvZ.mjs → auth-router-BsR981d4.mjs} +5 -5
- package/dist/{better-auth-shared-rsBGBvWJ.mjs → better-auth-shared-hy6RPh9W.mjs} +13 -2
- package/dist/{build-cmd-Dpt0jd-x.mjs → build-cmd-LzvNJORH.mjs} +19 -7
- package/dist/{cache-7_UeZdTk.mjs → cache-C4MvnrMH.mjs} +4 -4
- package/dist/{cancel-deploy-D23R1RXT.mjs → cancel-deploy-abUxpP2n.mjs} +4 -4
- package/dist/{cf-build-output-CGT03qCD.mjs → cf-build-output-BPqOT964.mjs} +141 -63
- package/dist/cf-build-output-_0HNWysu.mjs +2 -0
- package/dist/cli/cli.mjs +114 -460
- package/dist/cli/cloudflare-operation-process.mjs +1103 -0
- package/dist/cli/env-schema-probe.d.mts +2 -1
- package/dist/cli/env-schema-probe.mjs +3 -3
- package/dist/client-Cu7jWiF1.mjs +2 -0
- package/dist/{client-QTl6ko_D.mjs → client-RV8NVeB8.mjs} +165 -21
- package/dist/{cloudflare-auth-C4_GPZr0.mjs → cloudflare-auth-Qdc7tw8F.mjs} +41 -43
- package/dist/{cloudflare-cmd-bSmZ1d5L.mjs → cloudflare-cmd-BcrVyTmJ.mjs} +9 -10
- package/dist/cloudflare-config-Bktvwtpf.mjs +182 -0
- package/dist/{cloudflare-connect-jAwacxxP.mjs → cloudflare-connect-B8uPZ4nx.mjs} +4 -4
- package/dist/{cloudflare-operations-BipGMJ5O.mjs → cloudflare-operations-AiWashgg.mjs} +1 -1
- package/dist/{cloudflare-operations-sMZXpk_S.mjs → cloudflare-operations-fxHb-byx.mjs} +118 -203
- package/dist/{preset-UHj9ARyP.mjs → cloudflare-process-B-wekeR6.mjs} +93 -6
- package/dist/{config-CF69HgXc.d.mts → config-BMHb8RCj.d.mts} +2 -2
- package/dist/{config-CafTW6Cz.mjs → config-Br_JZD6u.mjs} +2 -7
- package/dist/config-C_XRIPx2.mjs +89 -0
- package/dist/{config-s7Xj7tPb.mjs → config-CyQ-wVd7.mjs} +1 -1
- package/dist/config-entry.d.mts +1 -1
- package/dist/{config-write-BSduPMY8.mjs → config-write-B1f88wJA.mjs} +1 -1
- package/dist/{connect-BsUSRzln.mjs → connect-Bd4kJd9U.mjs} +6 -6
- package/dist/{create-project-BniEV0OW.mjs → create-project-Boczwj5r.mjs} +1 -1
- package/dist/{create-project-DBfFSZKY.mjs → create-project-bMf6ffLZ.mjs} +9 -5
- package/dist/{db-hRrvZaq_.mjs → db-8uG64XLl.mjs} +26 -26
- package/dist/{delete-D6dZ9B6B.mjs → delete-NvbiyeJf.mjs} +4 -4
- package/dist/{deploy-WaAQez1O.mjs → deploy-DzAIwqNU.mjs} +944 -607
- package/dist/{deploy-DDz7c8LK.mjs → deploy-bjXdFCtn.mjs} +1 -1
- package/dist/{dist-BR1quN_w.mjs → dist-AoCzRTJE.mjs} +226 -60
- package/dist/{dist-C5fND3R0.mjs → dist-BuDuKZJv.mjs} +1 -1
- package/dist/{dist-Dn6nn2IU.mjs → dist-CTBk70IR.mjs} +42 -42
- package/dist/{domain-Dmhvb2oU.mjs → domain-y5Tvydvo.mjs} +5 -5
- package/dist/{email-DFi-s2t4.mjs → email-B3umsW75.mjs} +15 -30
- package/dist/{env-Csi-tMbT.mjs → env-BS6qYHDb.mjs} +6 -6
- package/dist/{env-public-D_6u46fX.d.mts → env-public-BX_r8HR6.d.mts} +2 -1
- package/dist/{env-validation-BB4GkLxn.mjs → env-validation-BPn7vk-V.mjs} +18 -71
- package/dist/{env-validation-BXge7uyK.mjs → env-validation-DbTg7-ar.mjs} +1 -1
- package/dist/{fetch-CXDChK7B.mjs → fetch-BIZJh7vR.mjs} +2 -2
- package/dist/{fetch-stream-AOByI7Ki.mjs → fetch-stream-IvCYKyQL.mjs} +24 -4
- package/dist/{gen-Dz3X1Qab.mjs → gen-B580--vC.mjs} +5 -5
- package/dist/gen-BVaUUumi.mjs +2 -0
- package/dist/{github-cmd-BED3_9JD.mjs → github-cmd-v45BdfKX.mjs} +6 -8
- package/dist/{handler-BXJTXd02.d.mts → handler-CZ4nAylQ.d.mts} +2 -1
- package/dist/{headers-BOg_velo.mjs → headers-DWi2IXWx.mjs} +1 -1
- package/dist/help-DofyZuY7.mjs +2 -0
- package/dist/{help-DC7gdz7L.mjs → help-daGKjXGk.mjs} +268 -666
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +305 -1490
- package/dist/info-B97bTX9N.mjs +113 -0
- package/dist/{init-WO0JlPx8.mjs → init-Bvy7zrBo.mjs} +51 -20
- package/dist/limits-Bq5LG8Id.d.mts +27 -0
- package/dist/limits-Cjuk2VPm.mjs +68 -0
- package/dist/{link-CsHOinF7.mjs → link-CDqqCjFl.mjs} +5 -5
- package/dist/{list-CDb-4bZ1.mjs → list-CZj0dzKY.mjs} +5 -5
- package/dist/{live-CKiJilLr.d.mts → live-Chw1eIMv.d.mts} +1 -1
- package/dist/{local-d1-CC8sKFGu.mjs → local-d1-2CMnpuW_.mjs} +2 -2
- package/dist/login-DYt_An22.mjs +2 -0
- package/dist/{login-DvqXsfGs.mjs → login-UZKFM_u7.mjs} +5 -5
- package/dist/{logs-BdfiOezj.mjs → logs-CUZ6t9t3.mjs} +5 -5
- package/dist/migrate-8_2u55MD.mjs +2 -0
- package/dist/{migrate-B8KuoYsO.mjs → migrate-DHul7PRV.mjs} +5 -4
- package/dist/{node-BM43oz4G.mjs → node-BkyRWRx8.mjs} +2 -2
- package/dist/operator-args-CLgKGlwU.mjs +690 -0
- package/dist/{operator-cmd-03819ATr.mjs → operator-cmd-DBA6dl0m.mjs} +69 -23
- package/dist/output-Dm_A4Tbv.mjs +81 -0
- package/dist/pages/client.d.mts +34 -2
- package/dist/pages/client.mjs +59 -3
- package/dist/pages/index.d.mts +1 -1
- package/dist/pages/index.mjs +3 -3
- package/dist/pages/islands-plugin.d.mts +16 -6
- package/dist/pages/islands-plugin.mjs +2 -2
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +2 -308
- package/dist/{parse-filename-DioPHiR9.mjs → parse-filename-CUbj-1MP.mjs} +35 -1
- package/dist/{output-CCH48AMM.mjs → picocolors-BTps1_gs.mjs} +2 -76
- package/dist/plan-D1Q5rf-r.mjs +2 -0
- package/dist/plan-NPpwZ_kc.mjs +58 -0
- package/dist/platform-args-BJdRtlLq.mjs +506 -0
- package/dist/platform-args-D3RXyR6h.mjs +2 -0
- package/dist/{platform-auth-config-z92h63q5.mjs → platform-auth-config-B56E9YP1.mjs} +4 -4
- package/dist/{platform-auth-protection-DeE3yr2R.mjs → platform-auth-protection-BDWO_aER.mjs} +3 -3
- package/dist/{platform-auth-recovery-o_340Ixx.mjs → platform-auth-recovery-1gg4CSRe.mjs} +4 -4
- package/dist/{platform-cmd-C4ZV3Vpy.mjs → platform-cmd-Bt-1w6pY.mjs} +1 -1
- package/dist/{platform-cmd-DNl8WosH.mjs → platform-cmd-DMcQStSc.mjs} +26 -5
- package/dist/{platform-domain-CU1JtJkW.mjs → platform-domain-BNkcz0OB.mjs} +36 -9
- package/dist/{platform-lifecycle-Bc047IeT.mjs → platform-lifecycle-9OyBALhH.mjs} +850 -276
- package/dist/{platform-lifecycle-Bs2qx1E3.mjs → platform-lifecycle-BE6C_jxh.mjs} +1 -1
- package/dist/{platform-management-CVpGI9Y5.mjs → platform-management-CjwLVQwN.mjs} +59 -11
- package/dist/{platform-management-C8tt6D8h.mjs → platform-management-CnyTdcWX.mjs} +1 -1
- package/dist/platform-plans-config-BNGKGr4P.mjs +359 -0
- package/dist/{platform-recovery-dvUaptLr.mjs → platform-recovery-D6KSpuFm.mjs} +2 -2
- package/dist/{plugin-inference-BMfKRSqE.mjs → plugin-inference-CXWnn79A.mjs} +174 -64
- package/dist/{prepare-DyZ-Yok5.mjs → prepare-B5Mkic5u.mjs} +3 -3
- package/dist/{prepare-C3kt3Rst.mjs → prepare-DOsL0CC9.mjs} +4 -20
- package/dist/prepare-cgvDMtSb.mjs +2 -0
- package/dist/{project-cmd-B44I8J_W.mjs → project-cmd-CNzrqvBv.mjs} +30 -16
- package/dist/{project-team-BkbYIsXN.mjs → project-team-DQOWPfQT.mjs} +4 -4
- package/dist/{project-token-C8xEEQnB.mjs → project-token-CTDYU0Cc.mjs} +4 -4
- package/dist/project-zero-trust-BNAkW-_0.mjs +63 -0
- package/dist/{protocol-ZH3jP4a7.d.mts → protocol-CjF_iI9X.d.mts} +2 -2
- package/dist/protocol-U7bfjHmA.mjs +331 -0
- package/dist/{provision-DNtrtaVD.mjs → provision-C4ORE7G7.mjs} +1 -1
- package/dist/{provision-BhreDAOS.mjs → provision-CJFgTZY8.mjs} +111 -215
- package/dist/{requests-DFyhBMaf.mjs → requests-MhxYnau8.mjs} +4 -4
- package/dist/resource-name-C7LVpcRm.mjs +11 -0
- package/dist/{rollback-DHHxXZiS.mjs → rollback-CJ6iSDoU.mjs} +5 -5
- package/dist/route-url-CG7U-cRN.mjs +15 -0
- package/dist/{runner-B8wXwWlo.mjs → runner-CXA9Fh8h.mjs} +1 -1
- package/dist/{runner-p-dMs2UN.mjs → runner-Ol0TNjk6.mjs} +2 -2
- package/dist/runtime/ai.d.mts +12 -8
- package/dist/runtime/ai.mjs +84 -17
- package/dist/runtime/better-auth-mysql.mjs +1 -1
- package/dist/runtime/better-auth-pg.mjs +1 -1
- package/dist/runtime/better-auth.mjs +1 -1
- package/dist/runtime/client-react.mjs +2 -2
- package/dist/runtime/client-solid.mjs +2 -2
- package/dist/runtime/client-svelte.mjs +2 -2
- package/dist/runtime/client-vue.mjs +2 -2
- package/dist/runtime/client.mjs +2 -2
- package/dist/runtime/durable.d.mts +3 -1
- package/dist/runtime/durable.mjs +4 -1
- package/dist/runtime/email/testing.mjs +1 -1
- package/dist/runtime/env-public.d.mts +1 -1
- package/dist/runtime/fetch-stream.mjs +1 -1
- package/dist/runtime/fetch.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/kv.mjs +0 -1
- package/dist/runtime/limits.d.mts +2 -0
- package/dist/runtime/limits.mjs +2 -0
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live-server.mjs +25 -16
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/migration-handler.mjs +62 -42
- package/dist/runtime/route-url.d.mts +4 -0
- package/dist/runtime/route-url.mjs +2 -0
- package/dist/runtime/routing.d.mts +180 -0
- package/dist/runtime/routing.mjs +1082 -0
- package/dist/runtime/sandbox-container.d.mts +3 -0
- package/dist/runtime/sandbox-container.mjs +2 -0
- package/dist/runtime/sandbox.d.mts +4 -32
- package/dist/runtime/sandbox.mjs +151 -75
- package/dist/runtime/sse.mjs +1 -1
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +4 -2
- package/dist/runtime/ws-server.mjs +27 -2
- package/dist/runtime/ws.d.mts +2 -2
- package/dist/runtime/ws.mjs +8 -6
- package/dist/sandbox-XZAqzFlG.d.mts +52 -0
- package/dist/sandbox-container-Bo0eiFKz.d.mts +73 -0
- package/dist/sandbox-container-C6ItmVuN.mjs +281 -0
- package/dist/{scan-4tfN-PSn.mjs → scan-C7okrLyM.mjs} +4 -35
- package/dist/{secret-DN9sSNiV.mjs → secret-Dli5fP0B.mjs} +6 -6
- package/dist/{skills-O6FUaizK.mjs → skills-D1II1Juz.mjs} +1 -1
- package/dist/{sse-BaC1jXko.mjs → sse-CQNaDFFV.mjs} +6 -3
- package/dist/{subcommand-prompt-BuGYkAkC.mjs → subcommand-prompt-CY1C4fvl.mjs} +2 -2
- package/dist/validate-Dq_L3s0S.mjs +2 -0
- package/dist/{validate-CIUwFpjB.mjs → validate-ctOrgiS3.mjs} +2 -1
- package/dist/{wrangler-BymcxrRa.mjs → wrangler-7K-bW_DL.mjs} +14 -235
- package/dist/{ws-BwcqizuH.d.mts → ws-CL1w7GXU.d.mts} +13 -2
- package/package.json +48 -33
- package/sandbox.Dockerfile +4 -0
- package/schema.json +10 -22
- package/skills/migrate-vite-cloudflare-to-void/SKILL.md +34 -157
- package/skills/void/SKILL.md +50 -133
- package/skills/void/docs/guide/ai.md +94 -84
- package/skills/void/docs/guide/app-types.md +3 -32
- package/skills/void/docs/guide/auth.md +12 -116
- package/skills/void/docs/guide/database/d1.md +9 -54
- package/skills/void/docs/guide/database/mysql.md +1 -1
- package/skills/void/docs/guide/database/postgresql.md +5 -26
- package/skills/void/docs/guide/database.md +23 -75
- package/skills/void/docs/guide/deployment.md +27 -113
- package/skills/void/docs/guide/durable-state.md +43 -18
- package/skills/void/docs/guide/edge/headers.md +3 -47
- package/skills/void/docs/guide/edge/prerendering.md +5 -20
- package/skills/void/docs/guide/edge/redirects.md +11 -64
- package/skills/void/docs/guide/edge/revalidation.md +6 -19
- package/skills/void/docs/guide/edge/rewrites.md +56 -284
- package/skills/void/docs/guide/edge/static-assets.md +23 -72
- package/skills/void/docs/guide/email/domains.md +112 -0
- package/skills/void/docs/guide/email/receiving.md +139 -0
- package/skills/void/docs/guide/email/sending.md +231 -0
- package/skills/void/docs/guide/email.md +13 -619
- package/skills/void/docs/guide/env-migration.md +11 -11
- package/skills/void/docs/guide/env-vars.md +9 -29
- package/skills/void/docs/guide/index.md +0 -15
- package/skills/void/docs/guide/jobs.md +3 -18
- package/skills/void/docs/guide/kv.md +5 -11
- package/skills/void/docs/guide/live.md +5 -56
- package/skills/void/docs/guide/pages-routing/actions-and-forms.md +78 -125
- package/skills/void/docs/guide/pages-routing/head.md +10 -10
- package/skills/void/docs/guide/pages-routing/islands.md +6 -36
- package/skills/void/docs/guide/pages-routing/layouts.md +6 -128
- package/skills/void/docs/guide/pages-routing/loaders.md +3 -19
- package/skills/void/docs/guide/pages-routing/markdown.md +13 -171
- package/skills/void/docs/guide/pages-routing/overview.md +7 -17
- package/skills/void/docs/guide/pages-routing/view-transitions.md +1 -1
- package/skills/void/docs/guide/platform/administration/access.md +1 -4
- package/skills/void/docs/guide/platform/administration/email.md +35 -8
- package/skills/void/docs/guide/platform/administration/operations.md +26 -4
- package/skills/void/docs/guide/platform/administration/plans.md +126 -0
- package/skills/void/docs/guide/platform/administration/projects.md +5 -2
- package/skills/void/docs/guide/platform/administration/zero-trust.md +189 -0
- package/skills/void/docs/guide/platform/development/local.md +2 -2
- package/skills/void/docs/guide/platform/development/runtime.md +3 -13
- package/skills/void/docs/guide/platform/development/schema-ci.md +0 -58
- package/skills/void/docs/guide/platform/installation/credentials.md +6 -4
- package/skills/void/docs/guide/platform/installation/domains.md +31 -3
- package/skills/void/docs/guide/platform/installation/first-deployment.md +6 -0
- package/skills/void/docs/guide/platform/installation/maintenance.md +3 -1
- package/skills/void/docs/guide/platform/installation/prerequisites.md +21 -16
- package/skills/void/docs/guide/platform/installation/setup.md +9 -5
- package/skills/void/docs/guide/platform/installation/uninstall.md +17 -2
- package/skills/void/docs/guide/platform-administration.md +2 -0
- package/skills/void/docs/guide/queues.md +7 -9
- package/skills/void/docs/guide/quickstart.md +38 -37
- package/skills/void/docs/guide/remote-dev.md +4 -9
- package/skills/void/docs/guide/sandboxes.md +78 -41
- package/skills/void/docs/guide/server-routing.md +9 -72
- package/skills/void/docs/guide/sse.md +4 -18
- package/skills/void/docs/guide/ssg.md +3 -15
- package/skills/void/docs/guide/ssr.md +14 -62
- package/skills/void/docs/guide/storage.md +9 -4
- package/skills/void/docs/guide/type-safety.md +3 -14
- package/skills/void/docs/guide/typed-fetch.md +3 -7
- package/skills/void/docs/guide/websockets.md +68 -40
- package/skills/void/docs/integrations/agents.md +3 -3
- package/skills/void/docs/integrations/cloudflare.md +85 -316
- package/skills/void/docs/integrations/frameworks/analog.md +5 -64
- package/skills/void/docs/integrations/frameworks/astro.md +4 -73
- package/skills/void/docs/integrations/frameworks/nuxt.md +5 -62
- package/skills/void/docs/integrations/frameworks/overview.md +11 -54
- package/skills/void/docs/integrations/frameworks/react-router.md +5 -60
- package/skills/void/docs/integrations/frameworks/sveltekit.md +6 -65
- package/skills/void/docs/integrations/frameworks/tanstack-start.md +4 -62
- package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -69
- package/skills/void/docs/reference/api/auth.md +156 -0
- package/skills/void/docs/reference/api/client.md +87 -0
- package/skills/void/docs/reference/api/database.md +95 -0
- package/skills/void/docs/reference/api/durable.md +46 -0
- package/skills/void/docs/reference/api/env.md +50 -0
- package/skills/void/docs/reference/api/handlers.md +254 -0
- package/skills/void/docs/reference/api/pages.md +241 -0
- package/skills/void/docs/reference/api/plugin.md +39 -0
- package/skills/void/docs/reference/api/resources.md +109 -0
- package/skills/void/docs/reference/api/rewrites.md +76 -0
- package/skills/void/docs/reference/api/types.md +92 -0
- package/skills/void/docs/reference/api.md +56 -1218
- package/skills/void/docs/reference/cli/auth.md +88 -0
- package/skills/void/docs/reference/cli/database.md +128 -0
- package/skills/void/docs/reference/cli/deploy.md +85 -0
- package/skills/void/docs/reference/cli/domains.md +41 -0
- package/skills/void/docs/reference/cli/email.md +129 -0
- package/skills/void/docs/reference/cli/generate.md +116 -0
- package/skills/void/docs/reference/cli/github.md +189 -0
- package/skills/void/docs/reference/cli/platform-config.md +92 -0
- package/skills/void/docs/reference/cli/platform-email.md +81 -0
- package/skills/void/docs/reference/cli/platform-installation.md +127 -0
- package/skills/void/docs/reference/cli/platform-operations.md +90 -0
- package/skills/void/docs/reference/cli/platform-users.md +89 -0
- package/skills/void/docs/reference/cli/platform-zero-trust.md +45 -0
- package/skills/void/docs/reference/cli/platform.md +70 -0
- package/skills/void/docs/reference/cli/project.md +214 -0
- package/skills/void/docs/reference/cli/secrets.md +76 -0
- package/skills/void/docs/reference/cli/setup.md +70 -0
- package/skills/void/docs/reference/cli.md +33 -1621
- package/skills/void/docs/reference/config.md +26 -32
- package/skills/void/docs/reference/resource-inference.md +3 -58
- package/skills/void/docs/reference/structure.md +14 -41
- package/dist/canonical-json-DuDiiUsQ.mjs +0 -13
- package/dist/cli/cf-compat.mjs +0 -968
- package/dist/client-4cDVv7BO.mjs +0 -2
- package/dist/gen-Vnv2f65C.mjs +0 -2
- package/dist/help-DQMfeKMz.mjs +0 -2
- package/dist/login-DFQk7rbW.mjs +0 -2
- package/dist/migrate-TsHGBnDA.mjs +0 -2
- package/dist/plan-BEZ8VJW0.mjs +0 -256
- package/dist/plan-DpuOr14e.mjs +0 -2
- package/dist/prepare-BZXkjdNe.mjs +0 -2
- package/dist/validate-EKmJWxmy.mjs +0 -2
- /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 {
|
|
4
|
-
import {
|
|
5
|
-
import { t as
|
|
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-
|
|
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 {
|
|
11
|
-
import { A as
|
|
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-
|
|
14
|
-
import { n as
|
|
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-
|
|
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-
|
|
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 {
|
|
27
|
-
import {
|
|
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-
|
|
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 {
|
|
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-
|
|
38
|
-
import { a as
|
|
39
|
-
import {
|
|
40
|
-
import { n as writeVoidProjectConfig } from "./config-write-
|
|
41
|
-
import { r as discoverFrameworkWranglerFiles, t as migrateLegacyVoidConfig } from "./migrate-
|
|
42
|
-
import { a as createCloudflareEmptyDomainPublisher, i as createCloudflareEmptyCronPublisher } from "./cloudflare-operations-
|
|
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 {
|
|
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-
|
|
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(
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
|
|
245
|
-
|
|
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
|
|
253
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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:
|
|
297
|
-
actions
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
357
|
-
|
|
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
|
|
419
|
-
|
|
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(
|
|
483
|
-
const who = isHardFailure(whoami) || whoami.status !== 0 ? null : parseWhoamiJson(whoami.
|
|
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
|
-
|
|
513
|
-
|
|
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 =
|
|
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
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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 =
|
|
624
|
+
const parsed = readSendingSettings(sendingResult.data);
|
|
668
625
|
pre.sending.state = parsed ? parsed.enabled ? "ready" : "pending" : "unknown";
|
|
669
|
-
} else
|
|
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
|
|
851
|
-
|
|
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
|
-
|
|
883
|
-
|
|
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 =
|
|
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
|
-
|
|
924
|
-
|
|
925
|
-
|
|
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.
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
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
|
|
2359
|
-
|
|
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:
|
|
2371
|
-
stderr:
|
|
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) =>
|
|
2386
|
-
|
|
2387
|
-
|
|
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 (
|
|
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
|
-
...
|
|
2408
|
-
...
|
|
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:
|
|
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 (
|
|
2423
|
-
if (
|
|
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 =
|
|
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
|
|
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
|
-
|
|
2839
|
+
data: deployments[0]
|
|
2439
2840
|
};
|
|
2440
2841
|
}
|
|
2441
|
-
if (
|
|
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 =
|
|
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 ||
|
|
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 ||
|
|
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
|
-
|
|
2893
|
+
data: versions.sort((a, b) => a.number - b.number).slice(-10)
|
|
2491
2894
|
};
|
|
2492
2895
|
}
|
|
2493
|
-
if (
|
|
2896
|
+
if (operation.type === "versions.view") {
|
|
2494
2897
|
const result = await run([
|
|
2495
2898
|
"workers",
|
|
2496
2899
|
"versions",
|
|
2497
2900
|
"get",
|
|
2498
|
-
|
|
2901
|
+
operation.versionId,
|
|
2499
2902
|
"--worker-id",
|
|
2500
2903
|
workerName
|
|
2501
2904
|
], true);
|
|
2502
|
-
if (result.status !== 0) return result;
|
|
2503
|
-
const version =
|
|
2504
|
-
if (!record(version) || version.id !==
|
|
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
|
-
|
|
2910
|
+
data: {
|
|
2508
2911
|
...version,
|
|
2509
2912
|
resources: { bindings: version.bindings }
|
|
2510
|
-
}
|
|
2913
|
+
}
|
|
2511
2914
|
};
|
|
2512
2915
|
}
|
|
2513
|
-
if (
|
|
2514
|
-
if (
|
|
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(
|
|
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
|
-
...
|
|
2524
|
-
...
|
|
2525
|
-
...
|
|
2526
|
-
], command.capture);
|
|
2527
|
-
}
|
|
2528
|
-
if (
|
|
2529
|
-
if (
|
|
2530
|
-
const
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
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
|
-
...
|
|
2949
|
+
...operation.message ? ["--body", JSON.stringify({
|
|
2552
2950
|
strategy: "percentage",
|
|
2553
2951
|
versions: parsed,
|
|
2554
|
-
annotations: { "workers/message":
|
|
2952
|
+
annotations: { "workers/message": operation.message }
|
|
2555
2953
|
})] : []
|
|
2556
2954
|
], command.capture);
|
|
2557
2955
|
}
|
|
2558
|
-
if (
|
|
2559
|
-
if (
|
|
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(
|
|
2563
|
-
return run([
|
|
2960
|
+
if (output.activeSecrets) return uploadPinnedVersion(operation, true);
|
|
2961
|
+
return uploadedResult(await run([
|
|
2564
2962
|
"deploy",
|
|
2565
2963
|
"--prebuilt",
|
|
2566
|
-
...
|
|
2567
|
-
...
|
|
2568
|
-
], command.capture);
|
|
2964
|
+
...operation.message ? ["--message", operation.message] : [],
|
|
2965
|
+
...operation.secretsFilePath ? ["--secrets-file", operation.secretsFilePath] : []
|
|
2966
|
+
], command.capture));
|
|
2569
2967
|
}
|
|
2570
|
-
if (
|
|
2571
|
-
const configPath =
|
|
2572
|
-
if (!configPath ||
|
|
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 (
|
|
2623
|
-
const queueName =
|
|
2624
|
-
if (!queueName ||
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 || !
|
|
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: ${
|
|
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
|
|
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
|
-
|
|
2739
|
-
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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(
|
|
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-
|
|
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 ??
|
|
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` (`--
|
|
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
|
|
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
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
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
|
|
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
|
|
5390
|
-
const websocketRoutes =
|
|
5391
|
-
const durableObjects =
|
|
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
|
|
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 =
|
|
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.
|
|
6255
|
-
*
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
6720
|
+
reasons.set(binding, analysis.reasons.r2[0]);
|
|
6319
6721
|
}
|
|
6320
|
-
desired.queues =
|
|
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
|
-
|
|
6331
|
-
|
|
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(
|
|
6343
|
-
const emailHandlers =
|
|
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:
|
|
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
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
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
|
|
6762
|
-
const identity = await options.runWrangler(
|
|
6763
|
-
"whoami",
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
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 =
|
|
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: (
|
|
6784
|
-
|
|
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: (
|
|
6795
|
-
|
|
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
|
-
|
|
6844
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
7082
|
-
|
|
7083
|
-
|
|
7084
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
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
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
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
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
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 (!
|
|
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
|
-
"
|
|
7189
|
-
"
|
|
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: (
|
|
7443
|
-
|
|
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
|
-
|
|
7505
|
-
|
|
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
|
-
|
|
7592
|
-
|
|
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 ?
|
|
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":
|
|
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":
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
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
|
|
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
|
|
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
|
|
8088
|
-
|
|
8089
|
-
|
|
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:
|
|
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
|
|
8156
|
-
|
|
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
|
|
8163
|
-
|
|
8164
|
-
|
|
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
|
-
}) ||
|
|
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: (
|
|
8241
|
-
|
|
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
|
|
9166
|
-
|
|
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
|
-
}
|
|
9173
|
-
|
|
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 =
|
|
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" &&
|
|
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-
|
|
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 =
|
|
9223
|
-
const rawQueues =
|
|
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:
|
|
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
|
|
9390
|
-
|
|
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
|
-
}
|
|
9396
|
-
|
|
9397
|
-
const
|
|
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 =
|
|
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" &&
|
|
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-
|
|
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 =
|
|
9472
|
-
const rawQueues =
|
|
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
|
|
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 =
|
|
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,
|
|
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 };
|