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
|
@@ -0,0 +1,1103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as __toESM } from "../rolldown-runtime-DXIUcv95.mjs";
|
|
3
|
+
import { a as join, n as dirname, u as resolve } from "../dist-BVqXeR_h.mjs";
|
|
4
|
+
import { t as require_picocolors } from "../picocolors-BTps1_gs.mjs";
|
|
5
|
+
import { copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
+
import { parse } from "jsonc-parser";
|
|
10
|
+
import { stripVTControlCharacters } from "node:util";
|
|
11
|
+
import { EnvHttpProxyAgent, fetch as fetch$1 } from "undici";
|
|
12
|
+
import { getCloudflareAuthUseKeyringFromEnv } from "@cloudflare/workers-auth";
|
|
13
|
+
import { createCfAuth, createCfProfileStore, readUserPreferences, setKeyringPreference } from "@cloudflare/workers-auth/cf";
|
|
14
|
+
//#region src/cli/cf-session.ts
|
|
15
|
+
const logger = {
|
|
16
|
+
debug() {},
|
|
17
|
+
info() {},
|
|
18
|
+
log() {},
|
|
19
|
+
warn() {},
|
|
20
|
+
error() {}
|
|
21
|
+
};
|
|
22
|
+
function session(root) {
|
|
23
|
+
const auth = createCfAuth({
|
|
24
|
+
logger,
|
|
25
|
+
userAgent: "void",
|
|
26
|
+
prompt: async () => {
|
|
27
|
+
throw new Error("Cloudflare session reads cannot prompt.");
|
|
28
|
+
},
|
|
29
|
+
select: async () => {
|
|
30
|
+
throw new Error("Cloudflare session reads cannot prompt.");
|
|
31
|
+
},
|
|
32
|
+
isNoDefaultValueProvidedError: () => false
|
|
33
|
+
});
|
|
34
|
+
auth.setProfile(createCfProfileStore({ logger }).resolve({ cwd: root }));
|
|
35
|
+
return auth;
|
|
36
|
+
}
|
|
37
|
+
/** Borrow only the profile just verified by cf, including its encrypted store. */
|
|
38
|
+
function readCfSessionCredential(root, identity) {
|
|
39
|
+
if (identity.authenticated !== true || identity.tokenValid !== true) return null;
|
|
40
|
+
const auth = session(root);
|
|
41
|
+
if (identity.authSource !== `OAuth token from ${auth.getCredentialStore().path()}`) return null;
|
|
42
|
+
const token = auth.readAuthCredentials()?.oauth_token;
|
|
43
|
+
if (typeof token === "string" && token.length > 0) persistCfKeyringPreference(root);
|
|
44
|
+
return typeof token === "string" && token.length > 0 ? token : null;
|
|
45
|
+
}
|
|
46
|
+
/** Keep subsequent cf logins on the encrypted profile adopted by Void. */
|
|
47
|
+
function persistCfKeyringPreference(root) {
|
|
48
|
+
if (getCloudflareAuthUseKeyringFromEnv() === false || readUserPreferences().keyring_enabled === true) return;
|
|
49
|
+
const auth = session(root);
|
|
50
|
+
if (!setKeyringPreference(true, {
|
|
51
|
+
logger,
|
|
52
|
+
getCredentialStore: auth.getCredentialStore
|
|
53
|
+
}).enabled) throw new Error("Cloudflare encrypted credential storage is unavailable.");
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/cli/cloudflare-login-output.ts
|
|
57
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
58
|
+
/** The bundled cf login prints the device code before opening the browser. */
|
|
59
|
+
function cloudflareDeviceCode(output) {
|
|
60
|
+
const plain = stripVTControlCharacters(output);
|
|
61
|
+
return /and enter the code:\s*([A-Za-z0-9-]+)\s/i.exec(plain)?.[1] ?? /[?&]user_code=([A-Za-z0-9-]+)\s/i.exec(plain)?.[1] ?? null;
|
|
62
|
+
}
|
|
63
|
+
function cloudflareVerificationUrl(output) {
|
|
64
|
+
const plain = stripVTControlCharacters(output);
|
|
65
|
+
return /(?:Opening a link in your default browser:|Visit this link to authenticate:)\s*(https?:\/\/\S+)\s/i.exec(plain)?.[1] ?? /please visit:\s*(https?:\/\/\S+)\s/i.exec(plain)?.[1] ?? null;
|
|
66
|
+
}
|
|
67
|
+
function printCloudflareLoginLink(url) {
|
|
68
|
+
console.log(`Open this link if your browser did not open: ${url}`);
|
|
69
|
+
}
|
|
70
|
+
function printCloudflareLoginPrompt(code) {
|
|
71
|
+
console.log(code ? `Opening Cloudflare login in your browser. Confirm the code ${import_picocolors.default.blue(code)} in your browser. Press Ctrl+C to cancel.` : "Opening Cloudflare login in your browser. Confirm the code shown in your browser. Press Ctrl+C to cancel.");
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/cli/cloudflare-operation-process.ts
|
|
75
|
+
const cf = join(dirname(fileURLToPath(import.meta.resolve("cf/package.json"))), "bin", "cf");
|
|
76
|
+
const operation = (() => {
|
|
77
|
+
try {
|
|
78
|
+
return JSON.parse(process.argv[2] ?? "null");
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
process.env.CLOUDFLARE_AUTH_USE_KEYRING ??= "true";
|
|
84
|
+
function cfEnvironment() {
|
|
85
|
+
const env = {
|
|
86
|
+
...process.env,
|
|
87
|
+
CF_DELEGATION: "1",
|
|
88
|
+
CF_SEND_TELEMETRY: "false",
|
|
89
|
+
CF_NO_OSC_PROGRESS: "1",
|
|
90
|
+
CLOUDFLARE_AUTH_USE_KEYRING: process.env.CLOUDFLARE_AUTH_USE_KEYRING
|
|
91
|
+
};
|
|
92
|
+
if (env.HTTP_PROXY || env.HTTPS_PROXY || env.http_proxy || env.https_proxy) env.NODE_USE_ENV_PROXY = "1";
|
|
93
|
+
for (const [canonical, alias] of [
|
|
94
|
+
["CLOUDFLARE_API_TOKEN", "CF_API_TOKEN"],
|
|
95
|
+
["CLOUDFLARE_API_KEY", "CF_API_KEY"],
|
|
96
|
+
["CLOUDFLARE_EMAIL", "CF_EMAIL"],
|
|
97
|
+
["CLOUDFLARE_ACCOUNT_ID", "CF_ACCOUNT_ID"]
|
|
98
|
+
]) if (!env[canonical] && env[alias]) env[canonical] = env[alias];
|
|
99
|
+
return env;
|
|
100
|
+
}
|
|
101
|
+
function result(status, stdout = "", stderr = "", data) {
|
|
102
|
+
const code = /\[(?:code:\s*)?(\d{4,})\]/.exec(stderr);
|
|
103
|
+
const errorCode = code ? Number(code[1]) : void 0;
|
|
104
|
+
return {
|
|
105
|
+
status,
|
|
106
|
+
stdout,
|
|
107
|
+
stderr,
|
|
108
|
+
...data !== void 0 ? { data } : {},
|
|
109
|
+
...errorCode ? { errorCode } : {},
|
|
110
|
+
...[
|
|
111
|
+
10006,
|
|
112
|
+
10007,
|
|
113
|
+
10090
|
|
114
|
+
].includes(errorCode ?? 0) ? { failure: "not-found" } : {}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function runCf(command, cwd = process.cwd()) {
|
|
118
|
+
const env = cfEnvironment();
|
|
119
|
+
env.CF_QUIET = "1";
|
|
120
|
+
const child = spawnSync(process.execPath, [cf, ...command], {
|
|
121
|
+
cwd,
|
|
122
|
+
env,
|
|
123
|
+
encoding: "utf8",
|
|
124
|
+
shell: false,
|
|
125
|
+
windowsHide: true,
|
|
126
|
+
maxBuffer: 33554432,
|
|
127
|
+
stdio: [
|
|
128
|
+
"ignore",
|
|
129
|
+
"pipe",
|
|
130
|
+
"pipe"
|
|
131
|
+
]
|
|
132
|
+
});
|
|
133
|
+
const stderr = [
|
|
134
|
+
child.stderr || "",
|
|
135
|
+
child.error?.message || "",
|
|
136
|
+
child.signal ? `Cloudflare CLI terminated by ${child.signal}.` : ""
|
|
137
|
+
].filter(Boolean).join("\n").replace(/\[(\d{4,})\]/g, "[code: $1]");
|
|
138
|
+
if (child.signal || child.error || child.status === null) return {
|
|
139
|
+
status: 1,
|
|
140
|
+
stdout: "",
|
|
141
|
+
stderr,
|
|
142
|
+
signal: child.signal,
|
|
143
|
+
error: child.error?.message ?? (child.signal ? void 0 : "Cloudflare CLI did not report an exit status.")
|
|
144
|
+
};
|
|
145
|
+
return result(child.status, child.stdout ?? "", stderr);
|
|
146
|
+
}
|
|
147
|
+
function runInteractiveCf(command) {
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
const child = spawn(process.execPath, [
|
|
150
|
+
cf,
|
|
151
|
+
...command,
|
|
152
|
+
"--quiet"
|
|
153
|
+
], {
|
|
154
|
+
cwd: process.cwd(),
|
|
155
|
+
env: {
|
|
156
|
+
...cfEnvironment(),
|
|
157
|
+
CF_QUIET: "1"
|
|
158
|
+
},
|
|
159
|
+
shell: false,
|
|
160
|
+
stdio: [
|
|
161
|
+
"inherit",
|
|
162
|
+
"pipe",
|
|
163
|
+
"pipe"
|
|
164
|
+
],
|
|
165
|
+
windowsHide: true
|
|
166
|
+
});
|
|
167
|
+
let output = "";
|
|
168
|
+
let announced = false;
|
|
169
|
+
let announcedLink = false;
|
|
170
|
+
let settled = false;
|
|
171
|
+
const finish = (value) => {
|
|
172
|
+
if (!settled) {
|
|
173
|
+
settled = true;
|
|
174
|
+
resolve(value);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const collect = (chunk) => {
|
|
178
|
+
output = (output + chunk.toString()).slice(-32768);
|
|
179
|
+
if (!announced) {
|
|
180
|
+
const code = cloudflareDeviceCode(output);
|
|
181
|
+
if (code) {
|
|
182
|
+
announced = true;
|
|
183
|
+
printCloudflareLoginPrompt(code);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!announcedLink) {
|
|
187
|
+
const url = cloudflareVerificationUrl(output);
|
|
188
|
+
if (url) {
|
|
189
|
+
announcedLink = true;
|
|
190
|
+
printCloudflareLoginLink(url);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
child.stdout.on("data", collect);
|
|
195
|
+
child.stderr.on("data", collect);
|
|
196
|
+
child.once("error", (error) => finish(result(1, "", error.message)));
|
|
197
|
+
child.once("close", (status, signal) => {
|
|
198
|
+
if (status === 0) {
|
|
199
|
+
if (!announced) printCloudflareLoginPrompt(null);
|
|
200
|
+
finish(result(0));
|
|
201
|
+
} else finish(result(status ?? 1, "", output.trim() || `Cloudflare login ended${signal ? ` (${signal})` : ""}.`));
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
async function withBuildOutput(raw, sourceMain, action) {
|
|
206
|
+
const scratch = mkdtempSync(join(tmpdir(), "void-cf-command-"));
|
|
207
|
+
try {
|
|
208
|
+
const main = "index.js";
|
|
209
|
+
if (sourceMain) copyFileSync(sourceMain, join(scratch, main));
|
|
210
|
+
else writeFileSync(join(scratch, main), "export default { fetch() { return new Response(\"\"); } };\n");
|
|
211
|
+
writeFileSync(join(scratch, "wrangler.json"), JSON.stringify({
|
|
212
|
+
...raw,
|
|
213
|
+
main,
|
|
214
|
+
compatibility_date: raw.compatibility_date ?? "2025-01-01"
|
|
215
|
+
}), { mode: 384 });
|
|
216
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID ?? raw.account_id;
|
|
217
|
+
if (typeof accountId !== "string" || !accountId) throw new Error("Cloudflare account is not pinned for Worker deployment.");
|
|
218
|
+
const { prepareCfBuildOutput } = await import("../cf-build-output-_0HNWysu.mjs");
|
|
219
|
+
const output = await prepareCfBuildOutput(scratch, accountId);
|
|
220
|
+
if (!output) throw new Error("Could not prepare the Cloudflare Worker build output.");
|
|
221
|
+
try {
|
|
222
|
+
await assertExistingProvisionableResources(output.directory, accountId);
|
|
223
|
+
return action(output.directory);
|
|
224
|
+
} finally {
|
|
225
|
+
await output.dispose();
|
|
226
|
+
}
|
|
227
|
+
} finally {
|
|
228
|
+
rmSync(scratch, {
|
|
229
|
+
recursive: true,
|
|
230
|
+
force: true
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function parsedJson(value) {
|
|
235
|
+
return JSON.parse(value);
|
|
236
|
+
}
|
|
237
|
+
function object(value) {
|
|
238
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Cloudflare returned an unexpected response.");
|
|
239
|
+
return value;
|
|
240
|
+
}
|
|
241
|
+
function configPath() {
|
|
242
|
+
return "configPath" in operation ? operation.configPath : void 0;
|
|
243
|
+
}
|
|
244
|
+
function config() {
|
|
245
|
+
const path = configPath();
|
|
246
|
+
if (!path) throw new Error("Cloudflare config path is required for this operation.");
|
|
247
|
+
return object(parse(readFileSync(path, "utf8")));
|
|
248
|
+
}
|
|
249
|
+
function workerName() {
|
|
250
|
+
const name = ("workerName" in operation ? operation.workerName : void 0) ?? config().name;
|
|
251
|
+
if (typeof name !== "string" || !name) throw new Error("Cloudflare Worker name is missing from the generated config.");
|
|
252
|
+
return name;
|
|
253
|
+
}
|
|
254
|
+
function updateBinding(section, binding, values) {
|
|
255
|
+
const path = configPath();
|
|
256
|
+
if (!path || !binding) throw new Error("Cloudflare resource binding and config are required.");
|
|
257
|
+
const current = config();
|
|
258
|
+
const previous = Array.isArray(current[section]) ? current[section] : [];
|
|
259
|
+
const existing = previous.map(object).find((entry) => entry.binding === binding);
|
|
260
|
+
current[section] = [...previous.filter((entry) => object(entry).binding !== binding), {
|
|
261
|
+
...existing,
|
|
262
|
+
binding,
|
|
263
|
+
...values
|
|
264
|
+
}];
|
|
265
|
+
writeFileSync(path, `${JSON.stringify(current, null, 2)}\n`);
|
|
266
|
+
}
|
|
267
|
+
function d1Database(token) {
|
|
268
|
+
const entries = config().d1_databases;
|
|
269
|
+
if (!Array.isArray(entries)) throw new Error("D1 database binding is unavailable.");
|
|
270
|
+
const selected = entries.map(object).find((entry) => entry.database_id === token || entry.database_name === token || entry.binding === token);
|
|
271
|
+
if (!selected || typeof selected.database_id !== "string") throw new Error("D1 database binding is unavailable.");
|
|
272
|
+
return {
|
|
273
|
+
id: selected.database_id,
|
|
274
|
+
dir: typeof selected.migrations_dir === "string" ? selected.migrations_dir : "./migrations",
|
|
275
|
+
...typeof selected.migrations_pattern === "string" ? { pattern: selected.migrations_pattern } : {},
|
|
276
|
+
table: typeof selected.migrations_table === "string" ? selected.migrations_table : "d1_migrations"
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function cleanJson(command, transform) {
|
|
280
|
+
const called = runCf(command);
|
|
281
|
+
if (called.status !== 0 || !transform) return called;
|
|
282
|
+
return result(0, `${JSON.stringify(transform(parsedJson(called.stdout)))}\n`, called.stderr);
|
|
283
|
+
}
|
|
284
|
+
function accountId() {
|
|
285
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID ?? config().account_id;
|
|
286
|
+
if (typeof accountId !== "string" || !accountId) throw new Error("Cloudflare account is not pinned for API inspection.");
|
|
287
|
+
return accountId;
|
|
288
|
+
}
|
|
289
|
+
async function cloudflareApiGet(path, failure, extraHeaders = {}) {
|
|
290
|
+
const url = `${(process.env.CLOUDFLARE_API_BASE_URL ?? "https://api.cloudflare.com/client/v4").replace(/\/$/, "")}${path}`;
|
|
291
|
+
const proxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy ? new EnvHttpProxyAgent() : null;
|
|
292
|
+
try {
|
|
293
|
+
const response = await fetch$1(url, {
|
|
294
|
+
headers: {
|
|
295
|
+
...await apiHeaders(),
|
|
296
|
+
...extraHeaders
|
|
297
|
+
},
|
|
298
|
+
redirect: "error",
|
|
299
|
+
signal: AbortSignal.timeout(3e4),
|
|
300
|
+
...proxy ? { dispatcher: proxy } : {}
|
|
301
|
+
});
|
|
302
|
+
const body = object(await response.json());
|
|
303
|
+
if (!response.ok || body.success !== true) throw new Error(failure);
|
|
304
|
+
return body;
|
|
305
|
+
} finally {
|
|
306
|
+
await proxy?.close();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async function clearWorkerCronSchedules(worker) {
|
|
310
|
+
const endpoint = `${(process.env.CLOUDFLARE_API_BASE_URL ?? "https://api.cloudflare.com/client/v4").replace(/\/$/, "")}/accounts/${encodeURIComponent(accountId())}/workers/scripts/${encodeURIComponent(worker)}/schedules`;
|
|
311
|
+
const proxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy ? new EnvHttpProxyAgent() : null;
|
|
312
|
+
try {
|
|
313
|
+
const response = await fetch$1(endpoint, {
|
|
314
|
+
method: "PUT",
|
|
315
|
+
headers: {
|
|
316
|
+
...await apiHeaders(),
|
|
317
|
+
"Content-Type": "application/json"
|
|
318
|
+
},
|
|
319
|
+
body: "[]",
|
|
320
|
+
redirect: "error",
|
|
321
|
+
signal: AbortSignal.timeout(3e4),
|
|
322
|
+
...proxy ? { dispatcher: proxy } : {}
|
|
323
|
+
});
|
|
324
|
+
const body = object(await response.json());
|
|
325
|
+
const schedules = object(body.result).schedules;
|
|
326
|
+
if (!response.ok || body.success !== true || !Array.isArray(schedules) || schedules.length !== 0) throw new Error(`Cloudflare did not clear the Worker Cron Triggers (HTTP ${response.status}).`);
|
|
327
|
+
return result(0);
|
|
328
|
+
} finally {
|
|
329
|
+
await proxy?.close();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async function clearWorkerCustomDomains(worker) {
|
|
333
|
+
const endpoint = `${(process.env.CLOUDFLARE_API_BASE_URL ?? "https://api.cloudflare.com/client/v4").replace(/\/$/, "")}/accounts/${encodeURIComponent(accountId())}/workers/scripts/${encodeURIComponent(worker)}/domains/records`;
|
|
334
|
+
const proxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy ? new EnvHttpProxyAgent() : null;
|
|
335
|
+
try {
|
|
336
|
+
const response = await fetch$1(endpoint, {
|
|
337
|
+
method: "PUT",
|
|
338
|
+
headers: {
|
|
339
|
+
...await apiHeaders(),
|
|
340
|
+
"Content-Type": "application/json"
|
|
341
|
+
},
|
|
342
|
+
body: JSON.stringify({
|
|
343
|
+
override_scope: true,
|
|
344
|
+
override_existing_origin: false,
|
|
345
|
+
override_existing_dns_record: false,
|
|
346
|
+
origins: []
|
|
347
|
+
}),
|
|
348
|
+
redirect: "error",
|
|
349
|
+
signal: AbortSignal.timeout(3e4),
|
|
350
|
+
...proxy ? { dispatcher: proxy } : {}
|
|
351
|
+
});
|
|
352
|
+
if (!response.ok) throw new Error(`Cloudflare rejected the empty custom-domain set (HTTP ${response.status}).`);
|
|
353
|
+
let body;
|
|
354
|
+
try {
|
|
355
|
+
body = await response.json();
|
|
356
|
+
} catch {
|
|
357
|
+
throw new Error(`Cloudflare returned HTTP ${response.status} with a non-JSON custom-domain response.`);
|
|
358
|
+
}
|
|
359
|
+
if (object(body).success !== true) throw new Error(`Cloudflare rejected the empty custom-domain set (HTTP ${response.status}).`);
|
|
360
|
+
return result(0);
|
|
361
|
+
} finally {
|
|
362
|
+
await proxy?.close();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async function assertExistingProvisionableResources(directory, account) {
|
|
366
|
+
const { readBuildOutput } = await import("@cloudflare/build-output-utils");
|
|
367
|
+
const bindings = (await readBuildOutput(directory)).workers.default.config.env ?? {};
|
|
368
|
+
const queueNames = /* @__PURE__ */ new Set();
|
|
369
|
+
const accountPath = `/accounts/${encodeURIComponent(account)}`;
|
|
370
|
+
for (const [bindingName, value] of Object.entries(bindings)) {
|
|
371
|
+
const binding = object(value);
|
|
372
|
+
const identifier = (key) => {
|
|
373
|
+
const id = binding[key];
|
|
374
|
+
if (typeof id !== "string" || !id) throw new Error(`Cloudflare binding ${bindingName} has no ${key}; refusing automatic provisioning.`);
|
|
375
|
+
return id;
|
|
376
|
+
};
|
|
377
|
+
if (binding.type === "kv" || binding.type === "d1") identifier("id");
|
|
378
|
+
else if (binding.type === "r2") {
|
|
379
|
+
const name = identifier("name");
|
|
380
|
+
const jurisdiction = binding.jurisdiction;
|
|
381
|
+
if (jurisdiction !== void 0 && typeof jurisdiction !== "string") throw new Error(`Cloudflare R2 binding ${bindingName} has an invalid jurisdiction.`);
|
|
382
|
+
await cloudflareApiGet(`${accountPath}/r2/buckets/${encodeURIComponent(name)}`, `Cloudflare R2 bucket ${name} is unavailable; refusing automatic provisioning.`, jurisdiction ? { "cf-r2-jurisdiction": jurisdiction } : {});
|
|
383
|
+
} else if (binding.type === "queue") queueNames.add(identifier("name"));
|
|
384
|
+
else if (binding.type === "dispatch-namespace") {
|
|
385
|
+
const name = identifier("namespace");
|
|
386
|
+
await cloudflareApiGet(`${accountPath}/workers/dispatch/namespaces/${encodeURIComponent(name)}`, `Cloudflare dispatch namespace ${name} is unavailable; refusing automatic provisioning.`);
|
|
387
|
+
} else if (binding.type === "ai-search-namespace") {
|
|
388
|
+
const name = identifier("namespace");
|
|
389
|
+
await cloudflareApiGet(`${accountPath}/ai-search/namespaces/${encodeURIComponent(name)}`, `Cloudflare AI Search namespace ${name} is unavailable; refusing automatic provisioning.`);
|
|
390
|
+
} else if (binding.type === "agent-memory") {
|
|
391
|
+
const name = identifier("namespace");
|
|
392
|
+
await cloudflareApiGet(`${accountPath}/agent-memory/namespaces/${encodeURIComponent(name)}`, `Cloudflare Agent Memory namespace ${name} is unavailable; refusing automatic provisioning.`);
|
|
393
|
+
} else if (binding.type === "flagship") identifier("id");
|
|
394
|
+
}
|
|
395
|
+
if (queueNames.size > 0) {
|
|
396
|
+
const existing = new Set((await readAllQueues()).map((queue) => queue.queue_name));
|
|
397
|
+
for (const name of queueNames) if (!existing.has(name)) throw new Error(`Cloudflare Queue ${name} is unavailable; refusing automatic provisioning.`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async function readVersionResources(worker, versionId) {
|
|
401
|
+
const resources = object(object((await cloudflareApiGet(`/accounts/${encodeURIComponent(accountId())}/workers/scripts/${encodeURIComponent(worker)}/versions/${encodeURIComponent(versionId)}`, "Cloudflare did not return Worker version resources.")).result).resources);
|
|
402
|
+
if (!Array.isArray(resources.bindings) || !resources.script || !resources.script_runtime) throw new Error("Cloudflare Worker version resources were incomplete.");
|
|
403
|
+
return resources;
|
|
404
|
+
}
|
|
405
|
+
async function apiHeaders() {
|
|
406
|
+
const key = process.env.CLOUDFLARE_API_KEY ?? process.env.CF_API_KEY;
|
|
407
|
+
const email = process.env.CLOUDFLARE_EMAIL ?? process.env.CF_EMAIL;
|
|
408
|
+
if (key && email) return {
|
|
409
|
+
"X-Auth-Key": key,
|
|
410
|
+
"X-Auth-Email": email
|
|
411
|
+
};
|
|
412
|
+
const credential = await sessionCredential();
|
|
413
|
+
if (!credential) throw new Error("Cloudflare session bearer is unavailable.");
|
|
414
|
+
return { Authorization: `Bearer ${credential.token}` };
|
|
415
|
+
}
|
|
416
|
+
async function withSecretFile(content, action) {
|
|
417
|
+
const directory = mkdtempSync(join(tmpdir(), "void-cf-secret-"));
|
|
418
|
+
try {
|
|
419
|
+
const path = join(directory, "body.json");
|
|
420
|
+
writeFileSync(path, content, { mode: 384 });
|
|
421
|
+
return await action(path);
|
|
422
|
+
} finally {
|
|
423
|
+
rmSync(directory, {
|
|
424
|
+
recursive: true,
|
|
425
|
+
force: true
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
async function sessionCredential() {
|
|
430
|
+
const token = process.env.CLOUDFLARE_API_TOKEN || process.env.CF_API_TOKEN;
|
|
431
|
+
if (token) return {
|
|
432
|
+
type: "api_token",
|
|
433
|
+
token
|
|
434
|
+
};
|
|
435
|
+
const identity = runCf(["auth", "whoami"]);
|
|
436
|
+
if (identity.status !== 0) return null;
|
|
437
|
+
const state = object(parsedJson(identity.stdout));
|
|
438
|
+
if (state.authenticated !== true || state.tokenValid !== true) return null;
|
|
439
|
+
const credential = readCfSessionCredential(process.cwd(), state);
|
|
440
|
+
return credential ? {
|
|
441
|
+
type: "oauth",
|
|
442
|
+
token: credential
|
|
443
|
+
} : null;
|
|
444
|
+
}
|
|
445
|
+
async function auth() {
|
|
446
|
+
if (operation.type === "auth.login") {
|
|
447
|
+
const loggedIn = await runInteractiveCf([
|
|
448
|
+
"auth",
|
|
449
|
+
"login",
|
|
450
|
+
"--force"
|
|
451
|
+
]);
|
|
452
|
+
if (loggedIn.status === 0) persistCfKeyringPreference(process.cwd());
|
|
453
|
+
return loggedIn;
|
|
454
|
+
}
|
|
455
|
+
if (operation.type === "auth.logout") return runCf(["auth", "logout"]);
|
|
456
|
+
if (operation.type === "auth.whoami") {
|
|
457
|
+
const called = runCf(["auth", "whoami"]);
|
|
458
|
+
if (called.status !== 0) return called;
|
|
459
|
+
const identity = object(parsedJson(called.stdout));
|
|
460
|
+
if (identity.authenticated !== true || identity.tokenValid !== true) return result(1, "", "Cloudflare authentication is required.", { loggedIn: false });
|
|
461
|
+
if (String(identity.authSource).startsWith("OAuth token from ")) persistCfKeyringPreference(process.cwd());
|
|
462
|
+
return result(0, "", called.stderr, {
|
|
463
|
+
loggedIn: true,
|
|
464
|
+
accounts: identity.accounts,
|
|
465
|
+
email: identity.email,
|
|
466
|
+
tokenPermissions: identity.scopes,
|
|
467
|
+
authType: process.env.CLOUDFLARE_API_KEY || process.env.CF_API_KEY ? "Global API Key" : String(identity.authSource).startsWith("OAuth token") ? "OAuth Token" : "API Token"
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
if (operation.type !== "auth.token") throw new Error("Unsupported Cloudflare authentication operation.");
|
|
471
|
+
const credential = await sessionCredential();
|
|
472
|
+
return credential ? result(0, "", "", credential) : result(1, "", "Cloudflare session has no OAuth bearer.");
|
|
473
|
+
}
|
|
474
|
+
function listAllResourcePages(command, extraFlags = []) {
|
|
475
|
+
const pageSize = 100;
|
|
476
|
+
const entries = [];
|
|
477
|
+
for (let page = 1; page <= 100; page++) {
|
|
478
|
+
const listed = runCf([
|
|
479
|
+
...command,
|
|
480
|
+
"--page",
|
|
481
|
+
String(page),
|
|
482
|
+
"--per-page",
|
|
483
|
+
String(pageSize),
|
|
484
|
+
...extraFlags
|
|
485
|
+
]);
|
|
486
|
+
if (listed.status !== 0) return listed;
|
|
487
|
+
const items = parsedJson(listed.stdout);
|
|
488
|
+
if (!Array.isArray(items) || items.length > pageSize) throw new Error(`Cloudflare returned an invalid ${command.join(" ")} page.`);
|
|
489
|
+
entries.push(...items);
|
|
490
|
+
if (items.length < pageSize) return result(0, `${JSON.stringify(entries)}\n`);
|
|
491
|
+
}
|
|
492
|
+
throw new Error(`Cloudflare ${command.join(" ")} exceeded the supported page limit.`);
|
|
493
|
+
}
|
|
494
|
+
async function readAllQueues() {
|
|
495
|
+
const queues = [];
|
|
496
|
+
const seen = /* @__PURE__ */ new Set();
|
|
497
|
+
for (let page = 1; page <= 1e3; page++) {
|
|
498
|
+
const body = await cloudflareApiGet(`/accounts/${encodeURIComponent(accountId())}/queues?page=${page}`, "Cloudflare did not return the Queue list.");
|
|
499
|
+
const entries = body.result;
|
|
500
|
+
const info = object(body.result_info);
|
|
501
|
+
if (!Array.isArray(entries) || !Number.isSafeInteger(info.page) || info.page !== page || !Number.isSafeInteger(info.total_pages) || info.total_pages < 0 || info.total_pages > 1e3) throw new Error("Cloudflare returned incomplete Queue pagination.");
|
|
502
|
+
for (const entry of entries) {
|
|
503
|
+
const queue = object(entry);
|
|
504
|
+
if (typeof queue.queue_id !== "string" || typeof queue.queue_name !== "string" || seen.has(queue.queue_id)) throw new Error("Cloudflare returned an invalid Queue list.");
|
|
505
|
+
seen.add(queue.queue_id);
|
|
506
|
+
queues.push(queue);
|
|
507
|
+
}
|
|
508
|
+
if (page >= info.total_pages) return queues;
|
|
509
|
+
}
|
|
510
|
+
throw new Error("Cloudflare Queue list exceeded the supported page limit.");
|
|
511
|
+
}
|
|
512
|
+
async function resources() {
|
|
513
|
+
switch (operation.type) {
|
|
514
|
+
case "d1.execute": return runCf([
|
|
515
|
+
"d1",
|
|
516
|
+
"query",
|
|
517
|
+
d1Database(operation.database).id,
|
|
518
|
+
"--sql",
|
|
519
|
+
operation.sql
|
|
520
|
+
]);
|
|
521
|
+
case "d1.migrations.list":
|
|
522
|
+
case "d1.migrations.apply": {
|
|
523
|
+
const database = d1Database(operation.database);
|
|
524
|
+
const verb = operation.type === "d1.migrations.list" ? "list" : "apply";
|
|
525
|
+
const called = runCf([
|
|
526
|
+
"d1",
|
|
527
|
+
"migrations",
|
|
528
|
+
verb,
|
|
529
|
+
database.id,
|
|
530
|
+
"--dir",
|
|
531
|
+
database.dir,
|
|
532
|
+
...database.pattern ? ["--pattern", database.pattern] : [],
|
|
533
|
+
"--table",
|
|
534
|
+
database.table
|
|
535
|
+
]);
|
|
536
|
+
if (called.status !== 0 || verb !== "list") return called;
|
|
537
|
+
const pending = parsedJson(called.stdout);
|
|
538
|
+
if (!Array.isArray(pending) || pending.some((item) => typeof object(item).Name !== "string")) throw new Error("D1 migration list was malformed.");
|
|
539
|
+
return result(0, "", called.stderr, { pending: pending.map((item) => object(item).Name) });
|
|
540
|
+
}
|
|
541
|
+
case "containers.list": return runCf([
|
|
542
|
+
"containers",
|
|
543
|
+
"applications",
|
|
544
|
+
"list"
|
|
545
|
+
]);
|
|
546
|
+
case "d1.list": return listAllResourcePages(["d1", "list"]);
|
|
547
|
+
case "d1.info": {
|
|
548
|
+
const listed = listAllResourcePages(["d1", "list"]);
|
|
549
|
+
if (listed.status !== 0) return listed;
|
|
550
|
+
const list = parsedJson(listed.stdout);
|
|
551
|
+
if (!Array.isArray(list)) throw new Error("D1 list was malformed.");
|
|
552
|
+
const value = list.find((item) => object(item).name === operation.name);
|
|
553
|
+
return value === void 0 ? {
|
|
554
|
+
...result(1, "", "D1 database does not exist."),
|
|
555
|
+
failure: "not-found"
|
|
556
|
+
} : result(0, "", "", value);
|
|
557
|
+
}
|
|
558
|
+
case "d1.create": {
|
|
559
|
+
const created = runCf([
|
|
560
|
+
"d1",
|
|
561
|
+
"create",
|
|
562
|
+
"--name",
|
|
563
|
+
operation.name
|
|
564
|
+
]);
|
|
565
|
+
if (created.status !== 0) return created;
|
|
566
|
+
const database = object(parsedJson(created.stdout));
|
|
567
|
+
if (typeof database.uuid !== "string") throw new Error("D1 creation returned no database ID.");
|
|
568
|
+
updateBinding("d1_databases", operation.binding, {
|
|
569
|
+
database_name: operation.name,
|
|
570
|
+
database_id: database.uuid
|
|
571
|
+
});
|
|
572
|
+
return created;
|
|
573
|
+
}
|
|
574
|
+
case "kv.namespace.list": return listAllResourcePages([
|
|
575
|
+
"kv",
|
|
576
|
+
"namespaces",
|
|
577
|
+
"list"
|
|
578
|
+
], [
|
|
579
|
+
"--order",
|
|
580
|
+
"id",
|
|
581
|
+
"--direction",
|
|
582
|
+
"asc"
|
|
583
|
+
]);
|
|
584
|
+
case "kv.namespace.create": {
|
|
585
|
+
const created = runCf([
|
|
586
|
+
"kv",
|
|
587
|
+
"namespaces",
|
|
588
|
+
"create",
|
|
589
|
+
"--title",
|
|
590
|
+
operation.name
|
|
591
|
+
]);
|
|
592
|
+
if (created.status !== 0) return created;
|
|
593
|
+
const namespace = object(parsedJson(created.stdout));
|
|
594
|
+
if (typeof namespace.id !== "string") throw new Error("KV creation returned no namespace ID.");
|
|
595
|
+
updateBinding("kv_namespaces", operation.binding, { id: namespace.id });
|
|
596
|
+
return created;
|
|
597
|
+
}
|
|
598
|
+
case "r2.bucket.info": return runCf([
|
|
599
|
+
"r2",
|
|
600
|
+
"buckets",
|
|
601
|
+
"get",
|
|
602
|
+
operation.name
|
|
603
|
+
]);
|
|
604
|
+
case "r2.bucket.create": {
|
|
605
|
+
const created = runCf([
|
|
606
|
+
"r2",
|
|
607
|
+
"buckets",
|
|
608
|
+
"create",
|
|
609
|
+
"--name",
|
|
610
|
+
operation.name
|
|
611
|
+
]);
|
|
612
|
+
if (created.status === 0) updateBinding("r2_buckets", operation.binding, { bucket_name: operation.name });
|
|
613
|
+
return created;
|
|
614
|
+
}
|
|
615
|
+
case "hyperdrive.create": {
|
|
616
|
+
const url = new URL(readFileSync(0, "utf8"));
|
|
617
|
+
const name = operation.name;
|
|
618
|
+
const created = await withSecretFile(JSON.stringify({
|
|
619
|
+
name,
|
|
620
|
+
origin: {
|
|
621
|
+
host: url.hostname,
|
|
622
|
+
port: Number(url.port || (url.protocol.startsWith("mysql") ? 3306 : 5432)),
|
|
623
|
+
database: decodeURIComponent(url.pathname.replace(/^\//, "")),
|
|
624
|
+
scheme: url.protocol.replace(":", ""),
|
|
625
|
+
user: decodeURIComponent(url.username),
|
|
626
|
+
password: decodeURIComponent(url.password)
|
|
627
|
+
}
|
|
628
|
+
}), (path) => runCf([
|
|
629
|
+
"hyperdrive",
|
|
630
|
+
"create",
|
|
631
|
+
"--body",
|
|
632
|
+
"@" + path
|
|
633
|
+
]));
|
|
634
|
+
if (created.status !== 0) return {
|
|
635
|
+
...created,
|
|
636
|
+
stdout: "",
|
|
637
|
+
stderr: "Hyperdrive creation failed."
|
|
638
|
+
};
|
|
639
|
+
const hyperdrive = object(parsedJson(created.stdout));
|
|
640
|
+
if (typeof hyperdrive.id !== "string") throw new Error("Hyperdrive creation returned no ID.");
|
|
641
|
+
updateBinding("hyperdrive", operation.binding, { id: hyperdrive.id });
|
|
642
|
+
return result(0, "", "", {
|
|
643
|
+
id: hyperdrive.id,
|
|
644
|
+
name
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
case "queues.create": return runCf([
|
|
648
|
+
"queues",
|
|
649
|
+
"create",
|
|
650
|
+
"--queue-name",
|
|
651
|
+
operation.name
|
|
652
|
+
]);
|
|
653
|
+
case "queues.info": {
|
|
654
|
+
const queue = (await readAllQueues()).find((item) => item.queue_name === operation.name);
|
|
655
|
+
return queue ? result(0, "", "", {
|
|
656
|
+
id: queue.queue_id,
|
|
657
|
+
name: queue.queue_name
|
|
658
|
+
}) : {
|
|
659
|
+
...result(1, "", "Cloudflare Queue does not exist."),
|
|
660
|
+
failure: "not-found"
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
default: throw new Error("Unsupported Cloudflare resource operation.");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
function emailZone(domain) {
|
|
667
|
+
const labels = domain.split(".");
|
|
668
|
+
for (let offset = 0; offset <= labels.length - 2; offset++) {
|
|
669
|
+
const candidate = labels.slice(offset).join(".");
|
|
670
|
+
const read = runCf([
|
|
671
|
+
"email-routing",
|
|
672
|
+
"settings",
|
|
673
|
+
"get",
|
|
674
|
+
"--zone",
|
|
675
|
+
candidate
|
|
676
|
+
]);
|
|
677
|
+
if (read.status === 0) return candidate;
|
|
678
|
+
if (read.signal || read.error || !/Zone not found:/.test(read.stderr)) throw new Error("Cloudflare could not resolve the Email zone.");
|
|
679
|
+
}
|
|
680
|
+
throw new Error(`Cloudflare Email zone for ${domain} was not found.`);
|
|
681
|
+
}
|
|
682
|
+
function email() {
|
|
683
|
+
if (!("domain" in operation)) throw new Error("Cloudflare Email domain is missing.");
|
|
684
|
+
const domain = operation.domain;
|
|
685
|
+
switch (operation.type) {
|
|
686
|
+
case "email.routing.settings": {
|
|
687
|
+
const read = runCf([
|
|
688
|
+
"email-routing",
|
|
689
|
+
"settings",
|
|
690
|
+
"get",
|
|
691
|
+
"--zone",
|
|
692
|
+
domain
|
|
693
|
+
]);
|
|
694
|
+
return read.status !== 0 && !read.signal && !read.error && /Zone not found:/.test(read.stderr) ? result(0, "", "", null) : read;
|
|
695
|
+
}
|
|
696
|
+
case "email.routing.enable": return runCf([
|
|
697
|
+
"email-routing",
|
|
698
|
+
"enable",
|
|
699
|
+
"--zone",
|
|
700
|
+
domain,
|
|
701
|
+
"--name",
|
|
702
|
+
domain
|
|
703
|
+
]);
|
|
704
|
+
case "email.routing.rules.list": return listAllResourcePages([
|
|
705
|
+
"email-routing",
|
|
706
|
+
"rules",
|
|
707
|
+
"list-account"
|
|
708
|
+
], ["--zone", domain]);
|
|
709
|
+
case "email.sending.settings": {
|
|
710
|
+
const listed = runCf([
|
|
711
|
+
"email-sending",
|
|
712
|
+
"subdomains",
|
|
713
|
+
"list",
|
|
714
|
+
"--zone",
|
|
715
|
+
emailZone(domain)
|
|
716
|
+
]);
|
|
717
|
+
if (listed.status !== 0) return listed;
|
|
718
|
+
const values = parsedJson(listed.stdout);
|
|
719
|
+
if (!Array.isArray(values)) throw new Error("Email Sending subdomains were malformed.");
|
|
720
|
+
return result(0, "", "", values.map(object).find((item) => item.name === domain) ?? null);
|
|
721
|
+
}
|
|
722
|
+
case "email.sending.enable": return runCf([
|
|
723
|
+
"email-sending",
|
|
724
|
+
"subdomains",
|
|
725
|
+
"create",
|
|
726
|
+
"--zone",
|
|
727
|
+
emailZone(domain),
|
|
728
|
+
"--name",
|
|
729
|
+
domain
|
|
730
|
+
]);
|
|
731
|
+
default: throw new Error("Unsupported Cloudflare Email operation.");
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
async function secrets() {
|
|
735
|
+
const worker = workerName();
|
|
736
|
+
const apply = async (action) => {
|
|
737
|
+
const first = action();
|
|
738
|
+
if (first.status === 0 || first.errorCode !== 10007) return first;
|
|
739
|
+
const draft = await withBuildOutput({
|
|
740
|
+
name: worker,
|
|
741
|
+
account_id: process.env.CLOUDFLARE_ACCOUNT_ID ?? config().account_id,
|
|
742
|
+
compatibility_date: config().compatibility_date ?? "2025-01-01",
|
|
743
|
+
workers_dev: false,
|
|
744
|
+
preview_urls: false
|
|
745
|
+
}, null, (directory) => runCf([
|
|
746
|
+
"deploy",
|
|
747
|
+
"--prebuilt",
|
|
748
|
+
"--message",
|
|
749
|
+
"void:secret-draft"
|
|
750
|
+
], directory));
|
|
751
|
+
return draft.status === 0 ? action() : draft;
|
|
752
|
+
};
|
|
753
|
+
switch (operation.type) {
|
|
754
|
+
case "secret.list": return runCf([
|
|
755
|
+
"workers",
|
|
756
|
+
"secrets",
|
|
757
|
+
"list",
|
|
758
|
+
"--worker",
|
|
759
|
+
worker
|
|
760
|
+
]);
|
|
761
|
+
case "secret.put": {
|
|
762
|
+
const value = readFileSync(0, "utf8").replace(/\n$/, "");
|
|
763
|
+
const name = operation.name;
|
|
764
|
+
return withSecretFile(JSON.stringify({
|
|
765
|
+
name,
|
|
766
|
+
text: value,
|
|
767
|
+
type: "secret_text"
|
|
768
|
+
}), (path) => apply(() => runCf([
|
|
769
|
+
"workers",
|
|
770
|
+
"secrets",
|
|
771
|
+
"update",
|
|
772
|
+
name,
|
|
773
|
+
"--worker",
|
|
774
|
+
worker,
|
|
775
|
+
"--body",
|
|
776
|
+
"@" + path
|
|
777
|
+
])));
|
|
778
|
+
}
|
|
779
|
+
case "secret.bulk": {
|
|
780
|
+
const values = object(parsedJson(readFileSync(0, "utf8")));
|
|
781
|
+
const patch = Object.fromEntries(Object.entries(values).map(([name, text]) => [name, {
|
|
782
|
+
type: "secret_text",
|
|
783
|
+
text
|
|
784
|
+
}]));
|
|
785
|
+
return withSecretFile(JSON.stringify(patch), (path) => apply(() => runCf([
|
|
786
|
+
"workers",
|
|
787
|
+
"secrets",
|
|
788
|
+
"bulk",
|
|
789
|
+
"--worker",
|
|
790
|
+
worker,
|
|
791
|
+
"--file",
|
|
792
|
+
path
|
|
793
|
+
])));
|
|
794
|
+
}
|
|
795
|
+
case "secret.delete": return runCf([
|
|
796
|
+
"workers",
|
|
797
|
+
"secrets",
|
|
798
|
+
"delete",
|
|
799
|
+
operation.name,
|
|
800
|
+
"--worker",
|
|
801
|
+
worker,
|
|
802
|
+
"--force"
|
|
803
|
+
]);
|
|
804
|
+
default: throw new Error("Unsupported Cloudflare secret operation.");
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function workerHistory() {
|
|
808
|
+
const worker = workerName();
|
|
809
|
+
switch (operation.type) {
|
|
810
|
+
case "deployments.status":
|
|
811
|
+
case "deployments.list": {
|
|
812
|
+
const called = runCf([
|
|
813
|
+
"workers",
|
|
814
|
+
"deployments",
|
|
815
|
+
"list",
|
|
816
|
+
"--worker",
|
|
817
|
+
worker
|
|
818
|
+
]);
|
|
819
|
+
if (called.status !== 0) return called;
|
|
820
|
+
const deployments = object(parsedJson(called.stdout)).deployments;
|
|
821
|
+
if (!Array.isArray(deployments)) throw new Error("Worker deployments were malformed.");
|
|
822
|
+
return operation.type === "deployments.list" ? result(0, "", called.stderr, deployments) : deployments[0] ? result(0, "", called.stderr, deployments[0]) : result(1, "", "Worker has no active deployment.");
|
|
823
|
+
}
|
|
824
|
+
case "versions.list": return cleanJson([
|
|
825
|
+
"workers",
|
|
826
|
+
"versions",
|
|
827
|
+
"list",
|
|
828
|
+
"--worker-id",
|
|
829
|
+
worker
|
|
830
|
+
], (value) => {
|
|
831
|
+
if (!Array.isArray(value)) throw new Error("Worker versions were malformed.");
|
|
832
|
+
return value.map((entry) => {
|
|
833
|
+
const version = object(entry);
|
|
834
|
+
return {
|
|
835
|
+
...version,
|
|
836
|
+
metadata: { created_on: version.created_on }
|
|
837
|
+
};
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
case "versions.view": {
|
|
841
|
+
const versionId = operation.versionId;
|
|
842
|
+
const called = runCf([
|
|
843
|
+
"workers",
|
|
844
|
+
"versions",
|
|
845
|
+
"get",
|
|
846
|
+
versionId,
|
|
847
|
+
"--worker-id",
|
|
848
|
+
worker
|
|
849
|
+
]);
|
|
850
|
+
if (called.status !== 0) return called;
|
|
851
|
+
const version = object(parsedJson(called.stdout));
|
|
852
|
+
if (version.id !== versionId || !Array.isArray(version.bindings)) throw new Error("Worker version bindings were malformed.");
|
|
853
|
+
const resources = await readVersionResources(worker, versionId);
|
|
854
|
+
return result(0, "", called.stderr, {
|
|
855
|
+
...version,
|
|
856
|
+
resources
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
case "versions.deploy": {
|
|
860
|
+
const versions = operation.versions.map((value) => ({
|
|
861
|
+
version_id: value.versionId,
|
|
862
|
+
percentage: value.percentage
|
|
863
|
+
}));
|
|
864
|
+
if (!versions.length || versions.some((value) => !/^[-a-f\d]{36}$/i.test(value.version_id) || !Number.isFinite(value.percentage) || value.percentage < 0 || value.percentage > 100) || Math.abs(versions.reduce((sum, value) => sum + value.percentage, 0) - 100) > .001) throw new Error("Cloudflare deployment percentages must total 100.");
|
|
865
|
+
return runCf([
|
|
866
|
+
"workers",
|
|
867
|
+
"deployments",
|
|
868
|
+
"create",
|
|
869
|
+
"--worker",
|
|
870
|
+
worker,
|
|
871
|
+
"--body",
|
|
872
|
+
JSON.stringify({
|
|
873
|
+
strategy: "percentage",
|
|
874
|
+
versions,
|
|
875
|
+
...operation.message ? { annotations: { "workers/message": operation.message } } : {}
|
|
876
|
+
})
|
|
877
|
+
]);
|
|
878
|
+
}
|
|
879
|
+
default: throw new Error("Unsupported Cloudflare Worker history operation.");
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
async function workerChanges() {
|
|
883
|
+
if (operation.type === "queues.consumer.remove") {
|
|
884
|
+
const queue = (await readAllQueues()).find((item) => item.queue_name === operation.queueName);
|
|
885
|
+
if (!queue || typeof queue.queue_id !== "string") return {
|
|
886
|
+
...result(1, "", "Cloudflare Queue does not exist."),
|
|
887
|
+
failure: "not-found"
|
|
888
|
+
};
|
|
889
|
+
const consumers = runCf([
|
|
890
|
+
"queues",
|
|
891
|
+
"consumers",
|
|
892
|
+
"list",
|
|
893
|
+
"--queue-id",
|
|
894
|
+
queue.queue_id
|
|
895
|
+
]);
|
|
896
|
+
if (consumers.status !== 0) return consumers;
|
|
897
|
+
const members = parsedJson(consumers.stdout);
|
|
898
|
+
if (!Array.isArray(members)) throw new Error("Queue consumer list was malformed.");
|
|
899
|
+
const matching = members.map(object).filter((entry) => {
|
|
900
|
+
const name = entry.script_name ?? entry.script;
|
|
901
|
+
if (entry.script_name !== void 0 && (typeof entry.script_name !== "string" || !entry.script_name) || entry.script !== void 0 && (typeof entry.script !== "string" || !entry.script) || entry.script_name !== void 0 && entry.script !== void 0 && entry.script_name !== entry.script || entry.type === "worker" && name === void 0) throw new Error("Queue consumer Worker identity is ambiguous.");
|
|
902
|
+
return name === operation.workerName;
|
|
903
|
+
});
|
|
904
|
+
if (matching.length > 1) throw new Error("Queue consumer Worker identity is ambiguous.");
|
|
905
|
+
for (const item of matching) {
|
|
906
|
+
if (typeof item.consumer_id !== "string" || !item.consumer_id || item.consumer_id.startsWith("-") || /\s/.test(item.consumer_id) || [...item.consumer_id].some((character) => {
|
|
907
|
+
const code = character.charCodeAt(0);
|
|
908
|
+
return code < 32 || code === 127;
|
|
909
|
+
}) || item.type !== void 0 && item.type !== "worker") throw new Error("Queue consumer ID or type is invalid.");
|
|
910
|
+
const deleted = runCf([
|
|
911
|
+
"queues",
|
|
912
|
+
"consumers",
|
|
913
|
+
"delete",
|
|
914
|
+
item.consumer_id,
|
|
915
|
+
"--queue-id",
|
|
916
|
+
queue.queue_id,
|
|
917
|
+
"--force"
|
|
918
|
+
]);
|
|
919
|
+
if (deleted.status !== 0) return deleted;
|
|
920
|
+
}
|
|
921
|
+
return result(0);
|
|
922
|
+
}
|
|
923
|
+
const raw = config();
|
|
924
|
+
const name = workerName();
|
|
925
|
+
if (raw.name !== name) throw new Error("Cloudflare Worker target changed.");
|
|
926
|
+
if (operation.type === "triggers.clear-crons" || operation.type === "triggers.clear-domains") {
|
|
927
|
+
if (raw.account_id !== void 0 && raw.account_id !== accountId()) throw new Error("Cloudflare trigger target changed.");
|
|
928
|
+
return operation.type === "triggers.clear-crons" ? clearWorkerCronSchedules(name) : clearWorkerCustomDomains(name);
|
|
929
|
+
}
|
|
930
|
+
if (operation.type === "triggers.deploy") return withBuildOutput(raw, null, (directory) => runCf([
|
|
931
|
+
"workers",
|
|
932
|
+
"triggers",
|
|
933
|
+
"deploy",
|
|
934
|
+
"--prebuilt"
|
|
935
|
+
], directory));
|
|
936
|
+
if (operation.type === "versions.upload" || operation.type === "worker.deploy") {
|
|
937
|
+
const path = configPath();
|
|
938
|
+
if (typeof raw.main !== "string" || !raw.main || raw.no_bundle !== true) throw new Error("Cloudflare Worker upload requires a prebuilt platform Worker.");
|
|
939
|
+
const source = resolve(dirname(path), raw.main);
|
|
940
|
+
const request = operation;
|
|
941
|
+
const called = await withBuildOutput(raw, source, (directory) => runCf([
|
|
942
|
+
...request.type === "worker.deploy" ? ["deploy"] : [
|
|
943
|
+
"workers",
|
|
944
|
+
"versions",
|
|
945
|
+
"create"
|
|
946
|
+
],
|
|
947
|
+
"--prebuilt",
|
|
948
|
+
...request.message ? ["--message", request.message] : [],
|
|
949
|
+
...request.secretsFilePath ? ["--secrets-file", request.secretsFilePath] : []
|
|
950
|
+
], directory));
|
|
951
|
+
if (called.status !== 0) return called;
|
|
952
|
+
let uploaded = {};
|
|
953
|
+
try {
|
|
954
|
+
uploaded = object(parsedJson(called.stdout));
|
|
955
|
+
} catch {}
|
|
956
|
+
const output = called.stdout;
|
|
957
|
+
const versionId = uploaded.version_id ?? uploaded.id ?? uploaded.versionId ?? output.match(/(?:Worker|Current) Version ID:\s*([0-9a-f]{8}-[0-9a-f-]{27})/i)?.[1];
|
|
958
|
+
if (typeof versionId !== "string" || !/^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(versionId)) throw new Error("Cloudflare did not return the uploaded Worker version ID.");
|
|
959
|
+
const previewUrl = typeof uploaded.preview_url === "string" ? uploaded.preview_url : output.match(/Version Preview(?: Alias)? URL:\s*(https:\/\/\S+)/i)?.[1];
|
|
960
|
+
const workersDevUrl = typeof uploaded.workers_dev_url === "string" ? uploaded.workers_dev_url : output.match(/(https:\/\/[a-z0-9.-]+\.workers\.dev(?:\/[^\s]*)?)/i)?.[1];
|
|
961
|
+
return result(0, "", called.stderr, {
|
|
962
|
+
versionId,
|
|
963
|
+
...previewUrl ? { previewUrl } : {},
|
|
964
|
+
...workersDevUrl ? { workersDevUrl } : {}
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
throw new Error("Unsupported Cloudflare Worker operation.");
|
|
968
|
+
}
|
|
969
|
+
async function tail() {
|
|
970
|
+
if (operation.type !== "tail") throw new Error("Cloudflare tail operation is required.");
|
|
971
|
+
const worker = workerName();
|
|
972
|
+
const raw = config();
|
|
973
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID ?? raw.account_id;
|
|
974
|
+
if (!worker || typeof accountId !== "string" || !accountId || worker !== raw.name) throw new Error("Cloudflare tail target is not pinned.");
|
|
975
|
+
const filters = [];
|
|
976
|
+
if (operation.search) filters.push({ query: operation.search });
|
|
977
|
+
if (operation.versionId) filters.push({ scriptVersion: operation.versionId });
|
|
978
|
+
if (operation.status === "error") filters.push({ outcome: [
|
|
979
|
+
"exception",
|
|
980
|
+
"exceededCpu",
|
|
981
|
+
"exceededMemory",
|
|
982
|
+
"unknown"
|
|
983
|
+
] });
|
|
984
|
+
const endpoint = `${(process.env.CLOUDFLARE_API_BASE_URL ?? "https://api.cloudflare.com/client/v4").replace(/\/$/, "")}/accounts/${encodeURIComponent(accountId)}/workers/scripts/${encodeURIComponent(worker)}/tails`;
|
|
985
|
+
const headers = {
|
|
986
|
+
...await apiHeaders(),
|
|
987
|
+
"Content-Type": "application/json"
|
|
988
|
+
};
|
|
989
|
+
const stopped = new AbortController();
|
|
990
|
+
let socket;
|
|
991
|
+
let finish;
|
|
992
|
+
const stop = () => {
|
|
993
|
+
stopped.abort();
|
|
994
|
+
socket?.close();
|
|
995
|
+
finish?.();
|
|
996
|
+
};
|
|
997
|
+
process.once("SIGINT", stop);
|
|
998
|
+
process.once("SIGTERM", stop);
|
|
999
|
+
let record;
|
|
1000
|
+
try {
|
|
1001
|
+
const created = await fetch(endpoint, {
|
|
1002
|
+
method: "POST",
|
|
1003
|
+
headers,
|
|
1004
|
+
body: JSON.stringify({ filters }),
|
|
1005
|
+
signal: AbortSignal.any([stopped.signal, AbortSignal.timeout(3e4)])
|
|
1006
|
+
});
|
|
1007
|
+
const body = object(await created.json());
|
|
1008
|
+
if (!created.ok || body.success !== true) return result(1, "", "Could not start the Cloudflare Worker tail.");
|
|
1009
|
+
record = object(body.result);
|
|
1010
|
+
if (typeof record.id !== "string" || typeof record.url !== "string") throw new Error("Cloudflare Worker tail response was incomplete.");
|
|
1011
|
+
const tailUrl = record.url;
|
|
1012
|
+
if (stopped.signal.aborted) return result(0);
|
|
1013
|
+
await new Promise((resolve, reject) => {
|
|
1014
|
+
finish = resolve;
|
|
1015
|
+
let pendingMessages = Promise.resolve();
|
|
1016
|
+
socket = new WebSocket(tailUrl, "trace-v1");
|
|
1017
|
+
socket.addEventListener("open", () => {
|
|
1018
|
+
socket.send(JSON.stringify({ debug: false }));
|
|
1019
|
+
});
|
|
1020
|
+
socket.addEventListener("message", (event) => {
|
|
1021
|
+
pendingMessages = pendingMessages.then(async () => {
|
|
1022
|
+
const data = event.data;
|
|
1023
|
+
const text = typeof data === "string" ? data : data instanceof Blob ? await data.text() : data instanceof ArrayBuffer ? new TextDecoder().decode(data) : null;
|
|
1024
|
+
if (text === null) throw new Error("Unsupported Cloudflare tail frame.");
|
|
1025
|
+
const log = object(parsedJson(text));
|
|
1026
|
+
const detail = object(log.event ?? {});
|
|
1027
|
+
const request = object(detail.request ?? {});
|
|
1028
|
+
const prefix = typeof request.url === "string" ? `${request.method ?? "GET"} ${request.url}` : typeof detail.queue === "string" ? `Queue ${detail.queue}` : typeof detail.mailFrom === "string" ? `Email from ${detail.mailFrom}` : "Worker event";
|
|
1029
|
+
process.stdout.write(`${prefix} - ${log.outcome ?? "unknown"} @ ${new Date(Number(log.eventTimestamp)).toLocaleString()}\n`);
|
|
1030
|
+
if (Array.isArray(log.logs)) for (const line of log.logs.map(object)) process.stdout.write(` (${line.level ?? "log"}) ${Array.isArray(line.message) ? line.message.join(" ") : ""}\n`);
|
|
1031
|
+
if (Array.isArray(log.exceptions)) for (const exception of log.exceptions.map(object)) process.stderr.write(` ${exception.name ?? "Error"}: ${exception.message ?? ""}\n`);
|
|
1032
|
+
}).catch(() => {
|
|
1033
|
+
process.stderr.write("Cloudflare returned a malformed tail event.\n");
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
socket.addEventListener("error", () => reject(/* @__PURE__ */ new Error("Cloudflare Worker tail connection failed.")));
|
|
1037
|
+
socket.addEventListener("close", () => void pendingMessages.then(resolve));
|
|
1038
|
+
if (stopped.signal.aborted) resolve();
|
|
1039
|
+
});
|
|
1040
|
+
} catch (error) {
|
|
1041
|
+
if (stopped.signal.aborted) return result(0);
|
|
1042
|
+
throw error;
|
|
1043
|
+
} finally {
|
|
1044
|
+
process.off("SIGINT", stop);
|
|
1045
|
+
process.off("SIGTERM", stop);
|
|
1046
|
+
socket?.close();
|
|
1047
|
+
if (record && typeof record.id === "string") await fetch(`${endpoint}/${encodeURIComponent(record.id)}`, {
|
|
1048
|
+
method: "DELETE",
|
|
1049
|
+
headers,
|
|
1050
|
+
signal: AbortSignal.timeout(5e3)
|
|
1051
|
+
}).catch(() => {});
|
|
1052
|
+
if (stopped.signal.aborted) process.exit(0);
|
|
1053
|
+
}
|
|
1054
|
+
return result(0);
|
|
1055
|
+
}
|
|
1056
|
+
async function main() {
|
|
1057
|
+
if (process.argv.length !== 3) throw new Error("Exactly one typed Cloudflare operation is required.");
|
|
1058
|
+
if (!operation || typeof operation !== "object" || typeof operation.type !== "string") throw new Error("Cloudflare operation is required.");
|
|
1059
|
+
if (operation.type.startsWith("auth.")) return auth();
|
|
1060
|
+
if (operation.type.startsWith("secret.")) return secrets();
|
|
1061
|
+
if (operation.type === "tail") return tail();
|
|
1062
|
+
if (operation.type.startsWith("email.")) return email();
|
|
1063
|
+
if (operation.type.startsWith("deployments.") || operation.type.startsWith("versions.") && operation.type !== "versions.upload") return workerHistory();
|
|
1064
|
+
if (operation.type.startsWith("triggers.") || operation.type === "worker.deploy" || operation.type === "versions.upload" || operation.type === "queues.consumer.remove") return workerChanges();
|
|
1065
|
+
return resources();
|
|
1066
|
+
}
|
|
1067
|
+
try {
|
|
1068
|
+
const output = await main();
|
|
1069
|
+
if (operation.type !== "auth.login" && operation.type !== "tail") {
|
|
1070
|
+
let data = output.data;
|
|
1071
|
+
let humanOutput = "";
|
|
1072
|
+
if (data === void 0 && output.stdout.trim()) try {
|
|
1073
|
+
data = parsedJson(output.stdout);
|
|
1074
|
+
} catch {
|
|
1075
|
+
humanOutput = output.stdout;
|
|
1076
|
+
}
|
|
1077
|
+
process.stdout.write(JSON.stringify({
|
|
1078
|
+
operationResult: 1,
|
|
1079
|
+
...output.signal ? { signal: output.signal } : {},
|
|
1080
|
+
...output.error ? { error: output.error } : {},
|
|
1081
|
+
...data !== void 0 ? { data } : {},
|
|
1082
|
+
...output.errorCode !== void 0 ? { errorCode: output.errorCode } : {},
|
|
1083
|
+
...output.failure ? { failure: output.failure } : {},
|
|
1084
|
+
...output.stderr ? { errors: [{
|
|
1085
|
+
code: output.errorCode,
|
|
1086
|
+
message: output.stderr
|
|
1087
|
+
}] } : {},
|
|
1088
|
+
...humanOutput ? { output: humanOutput } : {}
|
|
1089
|
+
}) + "\n");
|
|
1090
|
+
}
|
|
1091
|
+
if (output.stderr) process.stderr.write(output.stderr);
|
|
1092
|
+
process.exitCode = output.status;
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
const message = error instanceof Error ? error.message : "Cloudflare operation failed.";
|
|
1095
|
+
if (operation?.type !== "auth.login" && operation?.type !== "tail") process.stdout.write(JSON.stringify({
|
|
1096
|
+
operationResult: 1,
|
|
1097
|
+
errors: [{ message }]
|
|
1098
|
+
}) + "\n");
|
|
1099
|
+
process.stderr.write(message);
|
|
1100
|
+
process.exitCode = 1;
|
|
1101
|
+
}
|
|
1102
|
+
//#endregion
|
|
1103
|
+
export {};
|