void 0.26.0 → 0.27.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/AGENTS_TEMPLATE.md +1 -3
- package/README.md +19 -9
- package/dist/{account-cmd-CDry3B5V.mjs → account-cmd-l-FM7Ioq.mjs} +8 -7
- package/dist/{application-analysis-DC2yPvzY.mjs → application-analysis-DKy02UY0.mjs} +21 -32
- package/dist/{application-routing-C9khgLiS.mjs → application-routing-DQyUh_5S.mjs} +1 -1
- package/dist/{auth-D52yG7hl.mjs → auth-Dn2vw-SG.mjs} +9 -9
- package/dist/{auth-B60QDbjg.mjs → auth-LMJcNESe.mjs} +1 -1
- package/dist/{auth-link-DjeOptYi.mjs → auth-link-vz9Z2kmG.mjs} +5 -5
- package/dist/{auth-migrations-9vif1uj8.mjs → auth-migrations-BwHgeEsD.mjs} +8 -18
- package/dist/{auth-router-BRxIuqDi.mjs → auth-router-DvsCUVw4.mjs} +6 -19
- package/dist/{better-auth-shared-BVkjZ3vM.d.mts → better-auth-shared-CPvWD3nG.d.mts} +0 -4
- package/dist/{better-auth-shared-hy6RPh9W.mjs → better-auth-shared-DPpj8zxc.mjs} +94 -16
- package/dist/{build-cmd-Ce3C8huo.mjs → build-cmd-D6we0VjO.mjs} +8 -7
- package/dist/{cache-CS8Yxr5a.mjs → cache-CZRdzKx1.mjs} +8 -8
- package/dist/{cancel-deploy-mZsD88Xa.mjs → cancel-deploy-CQ3NnMvf.mjs} +6 -5
- package/dist/{cf-access-YTlqvAsk.mjs → cf-access-BcIBSKqG.mjs} +11 -12
- package/dist/{cf-build-output-U8o1Gl6X.mjs → cf-build-output-DjvB_872.mjs} +62 -85
- package/dist/cf-build-output-H9a97vME.mjs +2 -0
- package/dist/cf-user-agent-lKblH3zU.mjs +17 -0
- package/dist/cli/cli.mjs +48 -83
- package/dist/cli/cloudflare-operation-process.mjs +78 -78
- package/dist/cli/env-schema-probe.mjs +5 -6
- package/dist/{client-mC-dFGDD.mjs → client-DlDJuY0s.mjs} +30 -8
- package/dist/client-tso2OfcG.mjs +2 -0
- package/dist/{cloudflare-auth-Qdc7tw8F.mjs → cloudflare-auth-CuxfF6OA.mjs} +9 -17
- package/dist/{cloudflare-cmd-bhwCxAN6.mjs → cloudflare-cmd-lHqNlJGW.mjs} +6 -5
- package/dist/{cloudflare-config-Bktvwtpf.mjs → cloudflare-config-BmhBv3HV.mjs} +1 -1
- package/dist/{cloudflare-connect-CuhRgk3k.mjs → cloudflare-connect-2G3rL2rP.mjs} +4 -4
- package/dist/{cloudflare-operations-DoQrVHAt.mjs → cloudflare-operations-BlYLlowZ.mjs} +1 -1
- package/dist/{cloudflare-operations-DYa3coIk.mjs → cloudflare-operations-CwI1bChl.mjs} +41 -70
- package/dist/{cloudflare-process-B-wekeR6.mjs → cloudflare-process-2-TfIEzB.mjs} +20 -22
- package/dist/{config-CyQ-wVd7.mjs → config-5r-YpMC-.mjs} +1 -1
- package/dist/{config-C_XRIPx2.mjs → config-B_ggY-IO.mjs} +3 -3
- package/dist/config-BtSFvXZx.mjs +2 -0
- package/dist/config-CLYAosnQ.mjs +78 -0
- package/dist/{config-BMHb8RCj.d.mts → config-CSa15FvD.d.mts} +1 -1
- package/dist/{config-Br_JZD6u.mjs → config-DiowPT80.mjs} +24 -30
- package/dist/config-entry.d.mts +1 -1
- package/dist/{config-write-B1f88wJA.mjs → config-write-AHGesphx.mjs} +8 -7
- package/dist/{connect-H6EvP62X.mjs → connect-BfPBHHm-.mjs} +12 -11
- package/dist/{create-project-B2kk9Q0o.mjs → create-project-BdXEFKal.mjs} +7 -7
- package/dist/{create-project-Dbb4uiN-.mjs → create-project-qFDC45zg.mjs} +1 -1
- package/dist/{db-DpCKZto0.mjs → db-Xs80hcaA.mjs} +101 -133
- package/dist/{delete-CxlSvsvW.mjs → delete-d2vuou3j.mjs} +7 -7
- package/dist/{deploy-CeEbnv4b.mjs → deploy-Bqe1PYBw.mjs} +2351 -2497
- package/dist/deploy-VsWsVQkw.mjs +2 -0
- package/dist/{dev-inbox-D64ZKUod.mjs → dev-inbox-CK-UuPob.mjs} +7 -16
- package/dist/{discover-BcPPiF1o.mjs → discover-CkCYv9T_.mjs} +1 -2
- package/dist/discover-EDUf6JJw.mjs +2 -0
- package/dist/{prompts-ZAuJlyCj.mjs → dist-Bx1z6Eva.mjs} +1 -1
- package/dist/{dist-AoCzRTJE.mjs → dist-Cegxq8gs.mjs} +8 -8
- package/dist/{prompts-6OSVA7DJ.mjs → dist-D3m88sjf.mjs} +10 -53
- package/dist/{domain-B_9VgOm6.mjs → domain-BsDUBAvt.mjs} +29 -30
- package/dist/{dotenv-VQxupEUv.mjs → dotenv-BfU6eqFt.mjs} +20 -32
- package/dist/{drizzle-Beb2Am5S.mjs → drizzle-CF84FOVM.mjs} +16 -13
- package/dist/{email-BokHgjKj.mjs → email-_lGAHOhp.mjs} +24 -19
- package/dist/{email--tEtMoj_.mjs → email-d_Aux1vM.mjs} +1 -1
- package/dist/{entry-CF5wvH4w.mjs → entry-CMAkqBmU.mjs} +6 -6
- package/dist/{env-_v349JBd.mjs → env-BxocywBB.mjs} +25 -6
- package/dist/{env-BdTc-K2L.mjs → env-C74QepDE.mjs} +10 -10
- package/dist/{env-D5liuQYR.mjs → env-DMA3cXZt.mjs} +1 -1
- package/dist/{env-raw-Cx8ElDdj.mjs → env-raw-jZpP5n47.mjs} +1 -2
- package/dist/{env-types-D5kds_OZ.mjs → env-types-CwXgeMgL.mjs} +1 -1
- package/dist/{env-validation-cZBvGWaP.mjs → env-validation-CrVNjsPm.mjs} +1 -1
- package/dist/{env-validation-vBSQ8gw5.mjs → env-validation-Dxdx_5wo.mjs} +17 -21
- package/dist/{fetch-_SeGZao9.d.mts → fetch-BPCYVi3H.d.mts} +20 -1
- package/dist/{fetch-BIZJh7vR.mjs → fetch-sAcwjzLO.mjs} +1 -1
- package/dist/{fetch-stream-Nm3hkZbT.mjs → fetch-stream-PGgz4uGz.mjs} +18 -23
- package/dist/{fetch-stream-Bjf0hoZb.d.mts → fetch-stream-obvZ7fvO.d.mts} +1 -16
- package/dist/{gen-BhDlTpwW.mjs → gen-DtkQhvQg.mjs} +40 -57
- package/dist/gen-dA9hW7AV.mjs +2 -0
- package/dist/{github-cmd-hhkTzlGS.mjs → github-cmd-DaDdBbSS.mjs} +65 -164
- package/dist/{handler-DghKr6dU.mjs → handler-BHAn2fZJ.mjs} +1 -0
- package/dist/{handler-BeK3weD4.d.mts → handler-CNAwjRcW.d.mts} +4 -11
- package/dist/{head-Do8P4puT.d.mts → head-C8K_iGe6.d.mts} +1 -1
- package/dist/{head-D_QRR5Yd.mjs → head-D2VF4ApL.mjs} +29 -51
- package/dist/{headers-2rKda9Y6.mjs → headers-DlfR78Nu.mjs} +3 -3
- package/dist/{help-Bv44e2tm.mjs → help-0bTe_lID.mjs} +13 -32
- package/dist/help-DfQYVfGY.mjs +2 -0
- package/dist/{inbound-fRkqcXaC.mjs → inbound-PfQDLXc5.mjs} +23 -24
- package/dist/index.d.mts +19 -2
- package/dist/index.mjs +619 -532
- package/dist/{info-DhtTKh65.mjs → info-PYWwkWyt.mjs} +4 -4
- package/dist/{init-BMf4T03q.mjs → init-DaBGmaXC.mjs} +161 -198
- package/dist/{link-BqYsSS_I.mjs → link-C6fjafep.mjs} +10 -10
- package/dist/{list-_kBToO-l.mjs → list-BTFBlqKl.mjs} +9 -8
- package/dist/{live-CB1y5IuC.mjs → live-CgQuTpqD.mjs} +15 -16
- package/dist/{live-iAuEH1hd.d.mts → live-Du8jXwOW.d.mts} +4 -2
- package/dist/{local-d1-fGxYrVD6.mjs → local-d1-ga5O_idV.mjs} +2 -2
- package/dist/{login-DFuo-RWO.mjs → login-GKcNUmtI.mjs} +7 -7
- package/dist/login-mx_ydhmp.mjs +2 -0
- package/dist/{logs-DYgnh6dH.mjs → logs-BiMSULmc.mjs} +28 -11
- package/dist/migrate-BHp07yJE.mjs +2 -0
- package/dist/{migrate-CV1on-yJ.mjs → migrate-NLsjBbuM.mjs} +34 -20
- package/dist/{mime-D5Nmdzf7.mjs → mime-DxCznlJq.mjs} +45 -95
- package/dist/{node-C6Q6e1Es.mjs → node-DJicPvPj.mjs} +5 -5
- package/dist/{operator-args-DdY54Bgb.mjs → operator-args-DOBhIaIg.mjs} +10 -1
- package/dist/{operator-auth-2dsGlTT5.mjs → operator-auth-BEZ_u0Cz.mjs} +2 -2
- package/dist/{operator-client-BFqpp6z0.mjs → operator-client-DbDv4vtE.mjs} +2 -2
- package/dist/{operator-cmd-CUGHXrQf.mjs → operator-cmd-BT4hgavL.mjs} +24 -11
- package/dist/operator-cmd-CwBB-1yb.mjs +2 -0
- package/dist/{output-BvdaehtE.mjs → output-C50ag2r3.mjs} +20 -2
- package/dist/pages/client.d.mts +19 -51
- package/dist/pages/client.mjs +204 -225
- package/dist/pages/head-client.d.mts +1 -1
- package/dist/pages/head.d.mts +1 -1
- package/dist/pages/head.mjs +1 -1
- package/dist/pages/index.d.mts +2 -2
- package/dist/pages/index.mjs +6 -12
- package/dist/pages/islands-plugin.d.mts +1 -1
- package/dist/pages/islands-plugin.mjs +21 -25
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +2 -2
- package/dist/{plan-CJGEeTEI.mjs → plan-BhLO2p4w.mjs} +2 -1
- package/dist/plan-CH2G51je.mjs +2 -0
- package/dist/platform-args-D4E1PjVw.mjs +2 -0
- package/dist/{platform-args-DSV2p4nY.mjs → platform-args-DUeXY9Om.mjs} +1 -1
- package/dist/{platform-auth-config-Bosx7U1h.mjs → platform-auth-config-CkSx3usH.mjs} +40 -36
- package/dist/{platform-auth-protection-DcbL_LW_.mjs → platform-auth-protection-BJimB_p9.mjs} +9 -8
- package/dist/{platform-auth-recovery-WfTZ8K6o.mjs → platform-auth-recovery-BU6izFAU.mjs} +8 -7
- package/dist/{platform-cmd-DbKtQb_4.mjs → platform-cmd-BbY1dWiM.mjs} +14 -13
- package/dist/{platform-cmd-dyFUof0t.mjs → platform-cmd-BbmJiq5O.mjs} +1 -1
- package/dist/{platform-discovery-BC0HCKYt.mjs → platform-discovery-DIYz8V63.mjs} +1 -1
- package/dist/{platform-domain-Bsq43AJp.mjs → platform-domain-CHVS6cV0.mjs} +6 -6
- package/dist/{platform-lifecycle--5gWmDoI.mjs → platform-lifecycle-ByAlpkhV.mjs} +1 -1
- package/dist/{platform-lifecycle-Cfi0o6xM.mjs → platform-lifecycle-DzCwqhFq.mjs} +76 -387
- package/dist/{platform-management-Bbwf6-Dq.mjs → platform-management-CBOT4BSM.mjs} +1 -1
- package/dist/{platform-management-BrF9STZp.mjs → platform-management-CuuVDhyt.mjs} +7 -7
- package/dist/{platform-plans-config-DU-8nC2Y.mjs → platform-plans-config-CN-heS4q.mjs} +5 -4
- package/dist/{platform-recovery-DQAqAQYu.mjs → platform-recovery-CskSJgLh.mjs} +3 -3
- package/dist/{platform-registry-P387v42A.mjs → platform-registry-Cjv2Qf0r.mjs} +5 -1
- package/dist/platform-setup-BBxRxmFC.mjs +299 -0
- package/dist/{plugin-inference-CXWnn79A.mjs → plugin-inference-BlQbOL0P.mjs} +43 -42
- package/dist/prepare-DRUV-g3X.mjs +2 -0
- package/dist/{prepare-WoUubFKL.mjs → prepare-Do96654b.mjs} +8 -8
- package/dist/{prepare-BMrRWLwP.mjs → prepare-Dq9hTIbo.mjs} +4 -4
- package/dist/{prerender-render-D9qbLs2f.mjs → prerender-render-Ct20jftc.mjs} +17 -3
- package/dist/prerender-render.mjs +1 -1
- package/dist/{project-DQstYTxO.mjs → project-BAm2aB6Q.mjs} +1 -1
- package/dist/{project-BiaPQ7ur.mjs → project-DedqCp59.mjs} +1 -1
- package/dist/{project-cmd-BbH2LptN.mjs → project-cmd-DaoRrn-8.mjs} +19 -19
- package/dist/{project-team-Yiww3-go.mjs → project-team-Q_lCA7Io.mjs} +15 -18
- package/dist/{project-token-BrtT2NkY.mjs → project-token-BArgK9o1.mjs} +14 -17
- package/dist/{project-tsconfig-B8GeM0RM.mjs → project-tsconfig-CVJhNMxs.mjs} +2 -1
- package/dist/{project-zero-trust-nYJ2V2nc.mjs → project-zero-trust-KO9Hu2Pq.mjs} +12 -9
- package/dist/{protocol-BX8t2jiw.d.mts → protocol-BPZaNqBo.d.mts} +4 -2
- package/dist/{protocol-U7bfjHmA.mjs → protocol-CfaUwNs8.mjs} +46 -49
- package/dist/{providers-CJlNS3kT.mjs → providers-BXuhmuub.mjs} +1 -12
- package/dist/{provision-VeqAC77A.mjs → provision-BfaLiWbj.mjs} +38 -41
- package/dist/{provision-DMTPG8Rh.mjs → provision-Wu9ZBk9L.mjs} +2 -3
- package/dist/{requests-TIz_jtSu.mjs → requests-C7Oeoh5u.mjs} +5 -5
- package/dist/resolve-project-BIeYs9Cw.mjs +2 -0
- package/dist/{resolve-project-BG5ORGID.mjs → resolve-project-CP8rZs_p.mjs} +6 -1
- package/dist/{rollback-dBqICPzj.mjs → rollback-DVAtrLAl.mjs} +30 -37
- package/dist/{route-types-Clm4WD0B.mjs → route-types-CbtsIeFn.mjs} +1 -1
- package/dist/route-url-BgPja1GS.d.mts +5 -0
- package/dist/route-url-BjGv_KKY.mjs +28 -0
- package/dist/runner-9wjsSZx-.mjs +2 -0
- package/dist/{runner--2KF6Kua.mjs → runner-DMBKSrRk.mjs} +2 -2
- package/dist/{runner-mysql-DmWchSWJ.mjs → runner-mysql-CEzKtmFb.mjs} +8 -8
- package/dist/{runner-mysql-CEG5JozK.mjs → runner-mysql-CkFqZ6CE.mjs} +1 -1
- package/dist/{runner-pg-Bh4AgZdj.mjs → runner-pg-BGYRFdRp.mjs} +1 -1
- package/dist/runtime/ai.d.mts +1 -1
- package/dist/runtime/ai.mjs +34 -95
- package/dist/runtime/better-auth-mysql.d.mts +1 -1
- package/dist/runtime/better-auth-mysql.mjs +4 -9
- package/dist/runtime/better-auth-pg.d.mts +1 -1
- package/dist/runtime/better-auth-pg.mjs +5 -10
- package/dist/runtime/better-auth.d.mts +1 -1
- package/dist/runtime/better-auth.mjs +3 -3
- package/dist/runtime/client-react.d.mts +2 -3
- package/dist/runtime/client-react.mjs +2 -2
- package/dist/runtime/client-solid.d.mts +2 -3
- package/dist/runtime/client-solid.mjs +2 -2
- package/dist/runtime/client-svelte.d.mts +2 -3
- package/dist/runtime/client-svelte.mjs +2 -2
- package/dist/runtime/client-vue.d.mts +2 -3
- package/dist/runtime/client-vue.mjs +2 -2
- package/dist/runtime/client.d.mts +2 -3
- package/dist/runtime/client.mjs +2 -2
- package/dist/runtime/db.mjs +1 -1
- package/dist/runtime/durable.mjs +3 -10
- package/dist/runtime/email/testing.mjs +5 -5
- package/dist/runtime/email.d.mts +2 -1
- package/dist/runtime/email.mjs +11 -27
- package/dist/runtime/env-public.mjs +15 -20
- package/dist/runtime/env.mjs +1 -1
- package/dist/runtime/fetch-stream.d.mts +2 -2
- package/dist/runtime/fetch-stream.mjs +1 -1
- package/dist/runtime/fetch.d.mts +2 -3
- package/dist/runtime/fetch.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/handler.mjs +2 -2
- package/dist/runtime/isr.mjs +1 -1
- package/dist/runtime/kv.mjs +10 -11
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live-client.mjs +12 -22
- package/dist/runtime/live-server.mjs +27 -35
- package/dist/runtime/live.d.mts +2 -2
- package/dist/runtime/live.mjs +2 -2
- package/dist/runtime/migration-handler.mjs +4 -81
- package/dist/runtime/queues.mjs +1 -1
- package/dist/runtime/remote/binding-handler.d.mts +2 -2
- package/dist/runtime/remote/binding-handler.mjs +8 -9
- package/dist/runtime/remote/index.mjs +27 -77
- package/dist/runtime/route-url.d.mts +2 -4
- package/dist/runtime/route-url.mjs +1 -1
- package/dist/runtime/routing.d.mts +1 -1
- package/dist/runtime/routing.mjs +15 -26
- package/dist/runtime/sandbox.mjs +2 -2
- package/dist/runtime/sse.mjs +1 -1
- package/dist/runtime/storage.mjs +1 -1
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +2 -2
- package/dist/runtime/ws-server.mjs +30 -61
- package/dist/runtime/ws.d.mts +1 -1
- package/dist/runtime/ws.mjs +1 -1
- package/dist/{scan-C8dA7iM4.mjs → scan-C3uDzz6J.mjs} +12 -13
- package/dist/{secret-AKnNXNP5.mjs → secret-BvtB7IjY.mjs} +15 -15
- package/dist/{skills-BPQUGClu.mjs → skills-BgVMvQdy.mjs} +2 -2
- package/dist/{sse-CQNaDFFV.mjs → sse-CMvCgjNk.mjs} +1 -1
- package/dist/{subcommand-prompt-B6WS9Aoz.mjs → subcommand-prompt-BSlnD1fI.mjs} +2 -2
- package/dist/sveltekit.d.mts +0 -2
- package/dist/terminal-log-text-D9ACPOuG.mjs +32 -0
- package/dist/validate-BJr9LfXy.mjs +2 -0
- package/dist/{validate-ymhap6M_.mjs → validate-C9qJArv7.mjs} +9 -28
- package/dist/{defer-YsYDUoii.mjs → validation-error-Dg9Recn1.mjs} +11 -11
- package/dist/{wrangler-DhUerFkW.mjs → wrangler-D9KTuqnv.mjs} +89 -194
- package/dist/{ws-C79j4aED.d.mts → ws-CChrrAU-.d.mts} +1 -1
- package/dist/{yarn-pnp-BV4AzNul.mjs → yarn-pnp-CUX_iTPA.mjs} +19 -27
- package/{skills/void/docs → docs}/guide/ai.md +5 -3
- package/{skills/void/docs → docs}/guide/auth.md +37 -1
- package/{skills/void/docs → docs}/guide/database/postgresql.md +2 -2
- package/{skills/void/docs → docs}/guide/deployment.md +2 -2
- package/{skills/void/docs → docs}/guide/edge/revalidation.md +1 -1
- package/{skills/void/docs → docs}/guide/edge/rewrites.md +2 -4
- package/{skills/void/docs → docs}/guide/email/receiving.md +2 -2
- package/{skills/void/docs → docs}/guide/email/sending.md +0 -2
- package/{skills/void/docs → docs}/guide/env-vars.md +4 -0
- package/{skills/void/docs → docs}/guide/jobs.md +2 -2
- package/{skills/void/docs → docs}/guide/live.md +10 -5
- package/{skills/void/docs → docs}/guide/pages-routing/actions-and-forms.md +20 -8
- package/{skills/void/docs → docs}/guide/pages-routing/loaders.md +7 -3
- package/{skills/void/docs → docs}/guide/pages-routing/markdown.md +15 -23
- package/{skills/void/docs → docs}/guide/pages-routing/overview.md +6 -4
- package/{skills/void/docs → docs}/guide/platform/administration/access.md +3 -11
- package/{skills/void/docs → docs}/guide/platform/administration/operations.md +3 -12
- package/{skills/void/docs → docs}/guide/platform/administration/projects.md +1 -1
- package/{skills/void/docs → docs}/guide/platform/development/local.md +4 -2
- package/{skills/void/docs → docs}/guide/platform/development/runtime.md +1 -1
- package/{skills/void/docs → docs}/guide/platform/development/schema-ci.md +1 -1
- package/{skills/void/docs → docs}/guide/platform/installation/domains.md +3 -1
- package/{skills/void/docs → docs}/guide/platform/installation/first-deployment.md +4 -2
- package/{skills/void/docs → docs}/guide/platform/installation/maintenance.md +4 -0
- package/{skills/void/docs → docs}/guide/project-collaboration.md +7 -3
- package/{skills/void/docs → docs}/guide/queues.md +0 -2
- package/{skills/void/docs → docs}/guide/remote-dev.md +5 -5
- package/{skills/void/docs → docs}/guide/sandboxes.md +2 -2
- package/{skills/void/docs → docs}/guide/sse.md +0 -2
- package/{skills/void/docs → docs}/guide/storage.md +7 -2
- package/{skills/void/docs → docs}/guide/websockets.md +4 -0
- package/docs/integrations/agents.md +37 -0
- package/{skills/void/docs → docs}/integrations/frameworks/astro.md +3 -3
- package/{skills/void/docs → docs}/integrations/frameworks/nuxt.md +6 -2
- package/{skills/void/docs → docs}/integrations/hono.md +15 -7
- package/{skills/void/docs → docs}/integrations/nodejs-bun-deno.md +1 -0
- package/{skills/void/docs → docs}/reference/api/auth.md +1 -1
- package/{skills/void/docs → docs}/reference/api/handlers.md +4 -2
- package/{skills/void/docs → docs}/reference/api/pages.md +4 -0
- package/{skills/void/docs → docs}/reference/cli/auth.md +1 -6
- package/{skills/void/docs → docs}/reference/cli/database.md +3 -4
- package/{skills/void/docs → docs}/reference/cli/deploy.md +1 -1
- package/{skills/void/docs → docs}/reference/cli/generate.md +1 -1
- package/{skills/void/docs → docs}/reference/cli/github.md +3 -12
- package/{skills/void/docs → docs}/reference/cli/platform-config.md +1 -1
- package/{skills/void/docs → docs}/reference/cli/platform-email.md +2 -3
- package/{skills/void/docs → docs}/reference/cli/platform-installation.md +1 -1
- package/{skills/void/docs → docs}/reference/cli/platform-operations.md +3 -0
- package/{skills/void/docs → docs}/reference/cli/platform-users.md +2 -2
- package/{skills/void/docs → docs}/reference/cli/project.md +5 -4
- package/{skills/void/docs → docs}/reference/cli/secrets.md +3 -3
- package/{skills/void/docs → docs}/reference/cli/setup.md +9 -11
- package/{skills/void/docs → docs}/reference/cli.md +1 -1
- package/{skills/void/docs → docs}/reference/config.md +13 -13
- package/{skills/void/docs → docs}/reference/resource-inference.md +1 -16
- package/package.json +34 -30
- package/schema.json +1 -1
- package/skills/migrate-vite-cloudflare-to-void/SKILL.md +4 -1
- package/skills/void/SKILL.md +9 -5
- package/dist/cf-build-output-ChY6-XWP.mjs +0 -2
- package/dist/client-BxVixJBI.mjs +0 -2
- package/dist/config-0acf1oys.mjs +0 -2
- package/dist/config-D8MOgiQV.mjs +0 -32
- package/dist/deploy-qmiLE1G1.mjs +0 -2
- package/dist/discover-AMCBXivQ.mjs +0 -2
- package/dist/fetch-error-Sp1R4mZv.d.mts +0 -20
- package/dist/gen-qCBwH3fZ.mjs +0 -2
- package/dist/help-Dw5f9Xrg.mjs +0 -2
- package/dist/login-D7meQg_3.mjs +0 -2
- package/dist/migrate-CEu2X5DB.mjs +0 -2
- package/dist/operator-cmd-DRPvhpjM.mjs +0 -2
- package/dist/plan-COtUvdMA.mjs +0 -2
- package/dist/platform-args-Dcy_5b_Y.mjs +0 -2
- package/dist/prepare-BBknoZI1.mjs +0 -2
- package/dist/resolve-project-DtbidV4y.mjs +0 -2
- package/dist/route-url-CG7U-cRN.mjs +0 -15
- package/dist/runner-voIltZrY.mjs +0 -2
- package/dist/validate-pna7R3Pv.mjs +0 -2
- package/skills/void/docs/integrations/agents.md +0 -26
- package/dist/{runner-pg-D4OPlil-.mjs → runner-pg-DV8p_zjA.mjs} +4 -4
- package/{skills/void/docs → docs}/guide/app-types.md +0 -0
- package/{skills/void/docs → docs}/guide/database/d1.md +2 -2
- package/{skills/void/docs → docs}/guide/database/mysql.md +0 -0
- package/{skills/void/docs → docs}/guide/database.md +8 -8
- package/{skills/void/docs → docs}/guide/durable-state.md +0 -0
- package/{skills/void/docs → docs}/guide/edge/headers.md +0 -0
- package/{skills/void/docs → docs}/guide/edge/prerendering.md +0 -0
- package/{skills/void/docs → docs}/guide/edge/redirects.md +0 -0
- package/{skills/void/docs → docs}/guide/edge/static-assets.md +0 -0
- package/{skills/void/docs → docs}/guide/email/domains.md +0 -0
- package/{skills/void/docs → docs}/guide/email.md +0 -0
- package/{skills/void/docs → docs}/guide/index.md +0 -0
- package/{skills/void/docs → docs}/guide/kv.md +0 -0
- package/{skills/void/docs → docs}/guide/pages-routing/head.md +0 -0
- package/{skills/void/docs → docs}/guide/pages-routing/islands.md +2 -2
- package/{skills/void/docs → docs}/guide/pages-routing/layouts.md +0 -0
- package/{skills/void/docs → docs}/guide/pages-routing/view-transitions.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/administration/email.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/administration/plans.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/administration/zero-trust.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/installation/ci.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/installation/credentials.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/installation/prerequisites.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/installation/setup.md +0 -0
- package/{skills/void/docs → docs}/guide/platform/installation/uninstall.md +0 -0
- package/{skills/void/docs → docs}/guide/platform-administration.md +0 -0
- package/{skills/void/docs → docs}/guide/platform-development.md +0 -0
- package/{skills/void/docs → docs}/guide/quickstart.md +0 -0
- package/{skills/void/docs → docs}/guide/self-hosted-platform.md +0 -0
- package/{skills/void/docs → docs}/guide/server-routing.md +3 -3
- package/{skills/void/docs → docs}/guide/ssg.md +0 -0
- package/{skills/void/docs → docs}/guide/ssr.md +0 -0
- package/{skills/void/docs → docs}/guide/type-safety.md +0 -0
- package/{skills/void/docs → docs}/guide/typed-fetch.md +0 -0
- package/{skills/void/docs → docs}/index.md +0 -0
- package/{skills/void/docs → docs}/integrations/cloudflare.md +0 -0
- package/{skills/void/docs → docs}/integrations/frameworks/analog.md +3 -3
- package/{skills/void/docs → docs}/integrations/frameworks/overview.md +0 -0
- package/{skills/void/docs → docs}/integrations/frameworks/react-router.md +3 -3
- package/{skills/void/docs → docs}/integrations/frameworks/sveltekit.md +1 -1
- package/{skills/void/docs → docs}/integrations/frameworks/tanstack-start.md +2 -2
- package/{skills/void/docs → docs}/reference/api/client.md +0 -0
- package/{skills/void/docs → docs}/reference/api/database.md +2 -2
- /package/{skills/void/docs → docs}/reference/api/durable.md +0 -0
- /package/{skills/void/docs → docs}/reference/api/env.md +0 -0
- /package/{skills/void/docs → docs}/reference/api/plugin.md +0 -0
- /package/{skills/void/docs → docs}/reference/api/resources.md +0 -0
- /package/{skills/void/docs → docs}/reference/api/rewrites.md +0 -0
- /package/{skills/void/docs → docs}/reference/api/types.md +0 -0
- /package/{skills/void/docs → docs}/reference/api.md +0 -0
- /package/{skills/void/docs → docs}/reference/cli/domains.md +0 -0
- /package/{skills/void/docs → docs}/reference/cli/email.md +0 -0
- /package/{skills/void/docs → docs}/reference/cli/platform-zero-trust.md +0 -0
- /package/{skills/void/docs → docs}/reference/cli/platform.md +0 -0
- /package/{skills/void/docs → docs}/reference/structure.md +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,50 +1,51 @@
|
|
|
1
1
|
import { n as __toESM } from "./rolldown-runtime-DXIUcv95.mjs";
|
|
2
2
|
import { a as join, l as relative, n as dirname, u as resolve } from "./dist-BVqXeR_h.mjs";
|
|
3
3
|
import { t as require_picocolors } from "./picocolors-BTps1_gs.mjs";
|
|
4
|
-
import { d as
|
|
4
|
+
import { d as staticModuleReferences, f as walk, m as applyBindingOverrides, n as FRAMEWORK_SCAN_DIRS, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-BlQbOL0P.mjs";
|
|
5
5
|
import { n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-GgcMozYz.mjs";
|
|
6
6
|
import { n as loadVoidAuthConfig } from "./config-DPByhVck.mjs";
|
|
7
|
-
import { d as isNodeTarget, m as readConfig, u as getDatabaseDialect } from "./config-
|
|
7
|
+
import { d as isNodeTarget, m as readConfig, u as getDatabaseDialect } from "./config-DiowPT80.mjs";
|
|
8
8
|
import { i as isMemberExpression, n as isIdentifier, r as isLiteral } from "./ast-CPGPjb0V.mjs";
|
|
9
9
|
import { t as isRouteEnabledForEnvironment } from "./parse-filename-CUbj-1MP.mjs";
|
|
10
|
-
import { r as scanQueuesSync, s as scanJobsSync, t as analyzeApplicationSync } from "./application-analysis-
|
|
11
|
-
import { a as validateSsrEntries, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS } from "./entry-
|
|
12
|
-
import { r as hasEmailHandlers } from "./email
|
|
13
|
-
import { i as resolveLayoutIds } from "./scan-
|
|
14
|
-
import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, n as DEFAULT_SANDBOX_CLASS_NAME, r as SANDBOX_MIGRATION_TAG } from "./config-
|
|
15
|
-
import {
|
|
10
|
+
import { r as scanQueuesSync, s as scanJobsSync, t as analyzeApplicationSync } from "./application-analysis-DKy02UY0.mjs";
|
|
11
|
+
import { a as validateSsrEntries, n as SSR_SERVER_ENTRY_RELATIVE_PATHS, o as validateSsrEntry, t as SSR_CLIENT_ENTRY_RELATIVE_PATHS } from "./entry-CMAkqBmU.mjs";
|
|
12
|
+
import { r as hasEmailHandlers } from "./email-d_Aux1vM.mjs";
|
|
13
|
+
import { i as resolveLayoutIds } from "./scan-C3uDzz6J.mjs";
|
|
14
|
+
import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, n as DEFAULT_SANDBOX_CLASS_NAME, r as SANDBOX_MIGRATION_TAG } from "./config-B_ggY-IO.mjs";
|
|
15
|
+
import { G as hasRevalidate, R as CLOUDFLARE_PRESERVE_BINDINGS_BUILD_ENV, U as collectPages, W as collectRevalidate, _t as hasApiRoutes, et as resolveDistDir, gt as generateQueueResolverSource, it as resolveWorkerDirName, o as prerenderPages, s as prerenderPagesNode, u as SEND_EMAIL_BINDING, vt as hasDocumentRoutes, yt as resolveAssetRoutingPolicy, z as selectNativeVersionMetadataBinding } from "./deploy-Bqe1PYBw.mjs";
|
|
16
16
|
import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-vqY3pqwI.mjs";
|
|
17
|
-
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-
|
|
18
|
-
import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-
|
|
19
|
-
import { a as restoreCloudflarePreviewPointer, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-
|
|
20
|
-
import { D as
|
|
21
|
-
import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-
|
|
17
|
+
import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-DlfR78Nu.mjs";
|
|
18
|
+
import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-BAm2aB6Q.mjs";
|
|
19
|
+
import { a as restoreCloudflarePreviewPointer, r as materializeCloudflareConfig, s as stashCloudflareDeployPointer } from "./cloudflare-config-BmhBv3HV.mjs";
|
|
20
|
+
import { D as syncWranglerBindings, E as resolveLiveBindingConfig, O as syncWranglerDurableObjects, T as readWranglerWebSocketTombstones, a as LIVE_MIGRATION_TAG, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as normalizeWranglerCompatibilityFlags, h as nextDurableObjectMigrationTag, k as syncWranglerWebSockets, m as hasDurableObjectSqliteMigration, p as findBindingNameOwner, u as ensureWranglerCompat, v as readExistingLiveBindingConfig, x as readWranglerCompat } from "./wrangler-D9KTuqnv.mjs";
|
|
21
|
+
import { n as writeLocalD1PersistState, t as resolveLocalD1Target } from "./local-d1-ga5O_idV.mjs";
|
|
22
22
|
import { n as scopedAccessCredentials } from "./cf-access-TWNzXltp.mjs";
|
|
23
|
-
import { r as ensurePlatformProxyAccess } from "./cf-access-
|
|
24
|
-
import { a as getApiUrl, c as getToken, o as getProxyUrl } from "./auth-
|
|
25
|
-
import {
|
|
26
|
-
import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-
|
|
27
|
-
import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-
|
|
28
|
-
import { t as discoverSchema } from "./discover-
|
|
29
|
-
import { _ as
|
|
30
|
-
import { t as MagicString } from "./dist-
|
|
31
|
-
import { n as
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { n as
|
|
36
|
-
import { n as
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
23
|
+
import { r as ensurePlatformProxyAccess } from "./cf-access-BcIBSKqG.mjs";
|
|
24
|
+
import { a as getApiUrl, c as getToken, o as getProxyUrl } from "./auth-Dn2vw-SG.mjs";
|
|
25
|
+
import { d as printSuccess } from "./output-C50ag2r3.mjs";
|
|
26
|
+
import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-CVJhNMxs.mjs";
|
|
27
|
+
import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-CbtsIeFn.mjs";
|
|
28
|
+
import { t as discoverSchema } from "./discover-CkCYv9T_.mjs";
|
|
29
|
+
import { _ as listDevMessages, a as DEV_INBOX_ENV_VAR, b as registerEmailSink, c as DEV_INBOX_TOKEN_ENV_VAR, h as findDevMessage, l as DEV_INBOX_TOKEN_HEADER, n as DEV_INBOX_ACK_VALUE, o as DEV_INBOX_MAX_CAPTURE_BYTES, p as clearDevBuffer, s as DEV_INBOX_PREFIX, t as DEV_INBOX_ACK_HEADER, u as DEV_INBOX_TOKEN_QUERY, v as parseCapturedMessage, y as pushToDevBuffer } from "./dev-inbox-CK-UuPob.mjs";
|
|
30
|
+
import { t as MagicString } from "./dist-Cegxq8gs.mjs";
|
|
31
|
+
import { n as scanWranglerDotenvKeys } from "./dotenv-BfU6eqFt.mjs";
|
|
32
|
+
import { a as isClientEnvKey, c as resolveRemoteMode, i as findReservedEnvKeys, n as LOCAL_ENV_FILE, o as loadLocalEnv, r as findLegacyEnvFiles, s as reservedEnvKeysMessage } from "./env-BxocywBB.mjs";
|
|
33
|
+
import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-CwXgeMgL.mjs";
|
|
34
|
+
import { y as loadUserEnvFile } from "./env-validation-Dxdx_5wo.mjs";
|
|
35
|
+
import { n as INTERNAL_BINDING_PREFIXES, t as INTERNAL_BINDING_EXACT_NAMES } from "./env-raw-jZpP5n47.mjs";
|
|
36
|
+
import { n as completeApplicationAnalysis } from "./application-routing-DQyUh_5S.mjs";
|
|
37
|
+
import { r as defer } from "./validation-error-Dg9Recn1.mjs";
|
|
38
|
+
import { i as safeJsonStringify } from "./protocol-CfaUwNs8.mjs";
|
|
39
|
+
import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-CF84FOVM.mjs";
|
|
40
|
+
import { a as defineMiddleware, c as defineScheduled, i as defineHead, n as basicAuth, o as defineQueue, r as defineHandler, s as defineRender, t as VoidAssetRewriteError } from "./handler-BHAn2fZJ.mjs";
|
|
41
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
|
|
40
42
|
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
41
43
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
42
|
-
import { normalizePath, parseSync } from "vite";
|
|
44
|
+
import { loadEnv, normalizePath, parseSync, resolveEnvPrefix } from "vite";
|
|
43
45
|
import { appendSqliteClassMigration } from "@void/deploy-core";
|
|
44
|
-
import { timingSafeEqual } from "node:crypto";
|
|
46
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
45
47
|
import { homedir } from "node:os";
|
|
46
|
-
import { isDeepStrictEqual } from "node:util";
|
|
47
|
-
import { init, parse } from "es-module-lexer";
|
|
48
|
+
import { isDeepStrictEqual, parseEnv } from "node:util";
|
|
48
49
|
import { compilePattern } from "@void/edge";
|
|
49
50
|
//#region src/pages/dev-css-assets.ts
|
|
50
51
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
@@ -75,7 +76,7 @@ function normalizeDevCssUrl(id, root) {
|
|
|
75
76
|
const { pathname, suffix } = splitQueryHash(normalized);
|
|
76
77
|
const normalizedRoot = normalizePath(root);
|
|
77
78
|
if (pathname === normalizedRoot || pathname.startsWith(normalizedRoot + "/")) return "/" + normalizePath(pathname.slice(normalizedRoot.length + 1)) + suffix;
|
|
78
|
-
if (pathname.startsWith("/@fs/") || pathname.startsWith("/@vite/") || pathname
|
|
79
|
+
if (pathname.startsWith("/@fs/") || pathname.startsWith("/@vite/") || pathname.startsWith("/@react-refresh") || pathname.startsWith("/node_modules/.vite/")) return normalized;
|
|
79
80
|
if (pathname.startsWith("/")) {
|
|
80
81
|
const rootFirstSegment = normalizedRoot.split("/").filter(Boolean)[0];
|
|
81
82
|
if (rootFirstSegment && !pathname.startsWith(`/${rootFirstSegment}/`)) return normalized;
|
|
@@ -86,9 +87,6 @@ function normalizeDevCssUrl(id, root) {
|
|
|
86
87
|
function normalizeRequestUrl(url) {
|
|
87
88
|
return normalizePath(url).replace(/^\/@id\/__x00__/, "\0");
|
|
88
89
|
}
|
|
89
|
-
function toTransformRequestUrl(url) {
|
|
90
|
-
return normalizeRequestUrl(url);
|
|
91
|
-
}
|
|
92
90
|
function isCssRequest(url) {
|
|
93
91
|
return !CSS_BYPASS_RE.test(url) && (CSS_REQUEST_RE.test(url) || STYLE_QUERY_RE.test(url));
|
|
94
92
|
}
|
|
@@ -194,7 +192,7 @@ var DevCssProvider = class {
|
|
|
194
192
|
const addByUrl = async (rawUrl) => {
|
|
195
193
|
const url = normalizeDevCssUrl(rawUrl, this.root);
|
|
196
194
|
if (isOptimizedDepUrl(url)) return;
|
|
197
|
-
const requestUrl =
|
|
195
|
+
const requestUrl = normalizeRequestUrl(url);
|
|
198
196
|
let result;
|
|
199
197
|
try {
|
|
200
198
|
result = await environment.transformRequest(requestUrl);
|
|
@@ -243,16 +241,9 @@ var DevCssProvider = class {
|
|
|
243
241
|
});
|
|
244
242
|
continue;
|
|
245
243
|
}
|
|
246
|
-
const inlineUrl = appendInlineQuery(url);
|
|
247
|
-
let result;
|
|
248
|
-
try {
|
|
249
|
-
result = await environment.transformRequest(inlineUrl);
|
|
250
|
-
} catch (error) {
|
|
251
|
-
this.server.config.logger.warnOnce(`[void] Skipping dev CSS asset for ${url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
if (!result) continue;
|
|
255
244
|
try {
|
|
245
|
+
const result = await environment.transformRequest(appendInlineQuery(url));
|
|
246
|
+
if (!result) continue;
|
|
256
247
|
assets.push({
|
|
257
248
|
url,
|
|
258
249
|
id: cssViteDevIds.get(url) ?? url,
|
|
@@ -396,7 +387,7 @@ export async function getVoidPagesDevCss(request, env, payload) {
|
|
|
396
387
|
res.end("Method Not Allowed");
|
|
397
388
|
return;
|
|
398
389
|
}
|
|
399
|
-
if (req.headers[
|
|
390
|
+
if (req.headers[DEV_CSS_ASSET_GUARD_HEADER.toLowerCase()] !== DEV_CSS_ASSET_GUARD_VALUE) {
|
|
400
391
|
res.statusCode = 403;
|
|
401
392
|
res.end("Forbidden");
|
|
402
393
|
return;
|
|
@@ -428,17 +419,16 @@ export async function getVoidPagesDevCss(request, env, payload) {
|
|
|
428
419
|
async transform(code, id) {
|
|
429
420
|
if (this.environment?.name !== "client") return;
|
|
430
421
|
if (id.includes("/node_modules/.vite/")) return;
|
|
431
|
-
|
|
432
|
-
let imports;
|
|
422
|
+
let parsed;
|
|
433
423
|
try {
|
|
434
|
-
|
|
424
|
+
parsed = parseSync(id, code, { lang: "js" });
|
|
425
|
+
if (parsed.errors.length > 0) return;
|
|
435
426
|
} catch {
|
|
436
427
|
return;
|
|
437
428
|
}
|
|
438
429
|
const edges = [];
|
|
439
|
-
for (const
|
|
440
|
-
|
|
441
|
-
const resolved = await this.resolve(item.specifier, id, { skipSelf: true });
|
|
430
|
+
for (const { source } of staticModuleReferences(parsed, code)) {
|
|
431
|
+
const resolved = await this.resolve(source, id, { skipSelf: true });
|
|
442
432
|
if (resolved?.id) edges.push(normalizeDevCssUrl(resolved.id, root));
|
|
443
433
|
}
|
|
444
434
|
edgeStore.set(id, edges);
|
|
@@ -501,10 +491,10 @@ function readVoidDevCredentials(root) {
|
|
|
501
491
|
* block the server. On completion, writes project.json and restarts the server.
|
|
502
492
|
*/
|
|
503
493
|
async function runAiSetupPrompt(root, token, apiUrl, server) {
|
|
504
|
-
const { select, text, isCancel, log } = await import("./
|
|
505
|
-
const { PlatformClient } = await import("./client-
|
|
506
|
-
const { attachSelectedPlatform, writeProjectConfig } = await import("./project-
|
|
507
|
-
const { inferDefaultSlug } = await import("./resolve-project-
|
|
494
|
+
const { select, text, isCancel, log } = await import("./dist-Bx1z6Eva.mjs");
|
|
495
|
+
const { PlatformClient } = await import("./client-tso2OfcG.mjs");
|
|
496
|
+
const { attachSelectedPlatform, writeProjectConfig } = await import("./project-DedqCp59.mjs");
|
|
497
|
+
const { inferDefaultSlug } = await import("./resolve-project-BIeYs9Cw.mjs");
|
|
508
498
|
const client = new PlatformClient(token, apiUrl);
|
|
509
499
|
let projects;
|
|
510
500
|
try {
|
|
@@ -531,7 +521,7 @@ async function runAiSetupPrompt(root, token, apiUrl, server) {
|
|
|
531
521
|
let slug;
|
|
532
522
|
if (result === "__create__") {
|
|
533
523
|
const { validateProjectSlug } = await import("./project-slug-DofjTFd-.mjs");
|
|
534
|
-
const { createProjectWithSlugRetry } = await import("./create-project-
|
|
524
|
+
const { createProjectWithSlugRetry } = await import("./create-project-qFDC45zg.mjs");
|
|
535
525
|
let created;
|
|
536
526
|
try {
|
|
537
527
|
created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
|
|
@@ -560,8 +550,8 @@ async function runAiSetupPrompt(root, token, apiUrl, server) {
|
|
|
560
550
|
projectId,
|
|
561
551
|
slug
|
|
562
552
|
}, client.apiUrl));
|
|
563
|
-
log.success(`Linked to ${import_picocolors.default.blue(slug)}`);
|
|
564
553
|
await server.restart();
|
|
554
|
+
printSuccess(`Linked to ${slug}.`);
|
|
565
555
|
}
|
|
566
556
|
//#endregion
|
|
567
557
|
//#region src/plugin-binding-runtime.ts
|
|
@@ -691,6 +681,25 @@ function createClientStubsPlugin() {
|
|
|
691
681
|
}
|
|
692
682
|
//#endregion
|
|
693
683
|
//#region src/plugin-dev-config.ts
|
|
684
|
+
/** Run `fn` shortly after Vite prints "ready" so dev messages stay visible. */
|
|
685
|
+
function afterServerReady(server, fn) {
|
|
686
|
+
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(fn, 100));
|
|
687
|
+
else fn();
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Best-effort detection of `vite build` (and `vite preview`) so we can skip
|
|
691
|
+
* dev-only side effects — writing the dev-trigger token and the dispatch
|
|
692
|
+
* file — when no dev server will run. Vite's CLI passes the subcommand as
|
|
693
|
+
* `process.argv[2]`; programmatic API users (and `vite` with no subcommand)
|
|
694
|
+
* default to `dev`, which we detect as the absence of a build-shaped first
|
|
695
|
+
* positional. This is intentionally permissive: when in doubt, we behave as
|
|
696
|
+
* if we're in dev so first-request correctness is preserved.
|
|
697
|
+
*/
|
|
698
|
+
function isViteBuildOrPreview() {
|
|
699
|
+
const subcommand = process.argv[2];
|
|
700
|
+
return subcommand === "build" || subcommand === "preview";
|
|
701
|
+
}
|
|
702
|
+
const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
|
|
694
703
|
/**
|
|
695
704
|
* Persist the dev-trigger token at `<root>/.void/dev-trigger-token` so
|
|
696
705
|
* every voidPlugin() invocation against the same project reads the same
|
|
@@ -701,12 +710,8 @@ function createClientStubsPlugin() {
|
|
|
701
710
|
* hint plugin would print the other, and curls would land on
|
|
702
711
|
* `unauthorized`.
|
|
703
712
|
*
|
|
704
|
-
* The file persists across dev sessions
|
|
705
|
-
* (gitignored)
|
|
706
|
-
* it across its own wipe so saved curl scripts keep working between deploys.
|
|
707
|
-
* The token is local to the project and only honored by a worker bound to
|
|
708
|
-
* localhost; treat it like the rest of `.void/` — disposable scratch state,
|
|
709
|
-
* safe to remove manually if desired.
|
|
713
|
+
* The file persists across dev sessions and deploys so saved curl scripts
|
|
714
|
+
* keep working. It lives under `.void/` (gitignored) and is safe to remove.
|
|
710
715
|
*
|
|
711
716
|
* The file is owner-only (0600). It is the one credential the dev-inbox
|
|
712
717
|
* routes accept, and loopback is reachable by every account on the host,
|
|
@@ -715,20 +720,6 @@ function createClientStubsPlugin() {
|
|
|
715
720
|
* release is tightened in place, never rotated. Windows has no group/other
|
|
716
721
|
* bits — `mode` and `chmod` only toggle write there — so it is left alone.
|
|
717
722
|
*/
|
|
718
|
-
/**
|
|
719
|
-
* Best-effort detection of `vite build` (and `vite preview`) so we can skip
|
|
720
|
-
* dev-only side effects — writing the dev-trigger token and the dispatch
|
|
721
|
-
* file — when no dev server will run. Vite's CLI passes the subcommand as
|
|
722
|
-
* `process.argv[2]`; programmatic API users (and `vite` with no subcommand)
|
|
723
|
-
* default to `dev`, which we detect as the absence of a build-shaped first
|
|
724
|
-
* positional. This is intentionally permissive: when in doubt, we behave as
|
|
725
|
-
* if we're in dev so first-request correctness is preserved.
|
|
726
|
-
*/
|
|
727
|
-
function isViteBuildOrPreview() {
|
|
728
|
-
const subcommand = process.argv[2];
|
|
729
|
-
return subcommand === "build" || subcommand === "preview";
|
|
730
|
-
}
|
|
731
|
-
const DEV_TRIGGER_TOKEN_FILE_MODE = 384;
|
|
732
723
|
function getOrCreateDevTriggerToken(root) {
|
|
733
724
|
const tokenPath = join(root, ".void", "dev-trigger-token");
|
|
734
725
|
const readExisting = () => {
|
|
@@ -767,6 +758,48 @@ function getOrCreateDevTriggerToken(root) {
|
|
|
767
758
|
return fresh;
|
|
768
759
|
}
|
|
769
760
|
}
|
|
761
|
+
const DEV_TRIGGER_SECRET_PATH_RE = /(?:^|\/)\.void[. ]*\/(?:.*\/)?(?:dev-trigger|drizzle\.config\.json)|(?:^|\/)\.(?:nuxt|nitro)[. ]*\/(?:.*\/)?dev\/|(?:^|\/)\.astro[. ]*\/(?:.*\/)?dev\.log/;
|
|
762
|
+
function isDevTriggerSecretRequest(url) {
|
|
763
|
+
if (!url) return false;
|
|
764
|
+
let path = url.split(/[?#]/, 1)[0] ?? "";
|
|
765
|
+
for (let i = 0; i < 3; i++) {
|
|
766
|
+
let decoded;
|
|
767
|
+
try {
|
|
768
|
+
decoded = decodeURIComponent(path);
|
|
769
|
+
} catch {
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
if (decoded === path) break;
|
|
773
|
+
path = decoded;
|
|
774
|
+
}
|
|
775
|
+
return DEV_TRIGGER_SECRET_PATH_RE.test(path.replaceAll("\\", "/").toLowerCase());
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Vite serves every root file that `server.fs.deny` does not list, and its default
|
|
779
|
+
* CORS lets any localhost origin read the reply, so the files above would hand their
|
|
780
|
+
* secrets to `--host` peers, other local accounts and other local dev servers.
|
|
781
|
+
* `server.fs.deny` is not used: a plugin's list replaces Vite's defaults, and the
|
|
782
|
+
* Cloudflare worker environment applies the same check when it imports
|
|
783
|
+
* `.void/dev-triggers.ts`. The middleware is added in configureServer, so it runs
|
|
784
|
+
* before Vite's transform, /@fs/ and static middlewares.
|
|
785
|
+
*/
|
|
786
|
+
function devTriggerSecretGuardPlugin() {
|
|
787
|
+
return {
|
|
788
|
+
name: "void:dev-trigger-secret-guard",
|
|
789
|
+
apply: "serve",
|
|
790
|
+
enforce: "pre",
|
|
791
|
+
configureServer(server) {
|
|
792
|
+
server.middlewares.use((req, res, next) => {
|
|
793
|
+
if (!isDevTriggerSecretRequest(req.url)) {
|
|
794
|
+
next();
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
res.statusCode = 404;
|
|
798
|
+
res.end("Not Found");
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
}
|
|
770
803
|
/**
|
|
771
804
|
* Warn when the project's `package.json` does not declare `"type": "module"`.
|
|
772
805
|
*
|
|
@@ -809,11 +842,7 @@ function devTriggersHintPlugin(root, options) {
|
|
|
809
842
|
const jobs = scanJobsSync(root, paths.cronsDir);
|
|
810
843
|
const queues = scanQueuesSync(root, paths.queuesDir);
|
|
811
844
|
if (jobs.length === 0 && queues.length === 0) return;
|
|
812
|
-
|
|
813
|
-
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(fn, 100));
|
|
814
|
-
else fn();
|
|
815
|
-
};
|
|
816
|
-
afterReady(() => {
|
|
845
|
+
afterServerReady(server, () => {
|
|
817
846
|
const address = server.httpServer?.address();
|
|
818
847
|
const port = typeof address === "object" && address ? address.port : null;
|
|
819
848
|
if (port == null) return;
|
|
@@ -823,8 +852,7 @@ function devTriggersHintPlugin(root, options) {
|
|
|
823
852
|
if (queues.length > 0) summary.push(`${queues.length} queue${queues.length === 1 ? "" : "s"}`);
|
|
824
853
|
console.log();
|
|
825
854
|
voidLog(`Detected ${summary.join(" and ")}. Trigger locally:`);
|
|
826
|
-
const
|
|
827
|
-
const authHeader = configuredProxyToken ? `x-void-internal: ${configuredProxyToken}` : `x-void-dev-trigger: ${devTriggerToken}`;
|
|
855
|
+
const authHeader = `x-void-dev-trigger: ${devTriggerToken}`;
|
|
828
856
|
if (jobs.length > 0) {
|
|
829
857
|
const sample = jobs[0].crons[0];
|
|
830
858
|
console.log(` \x1b[2mcurl -X POST ${baseUrl}/__void/scheduled \\\n -H "${authHeader}" \\\n -d '{"cron":"${sample}","scheduledTime":'"$(date +%s000)"'}'\x1b[22m`);
|
|
@@ -1021,7 +1049,7 @@ function createPrimaryDb(d1) {
|
|
|
1021
1049
|
const migrationsDir = paths.migrationsDir;
|
|
1022
1050
|
if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) {
|
|
1023
1051
|
if (dialect === "sqlite") {
|
|
1024
|
-
const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-
|
|
1052
|
+
const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-9wjsSZx-.mjs");
|
|
1025
1053
|
if ((await getApplicationTables(getMiniflareDatabasePath(options.localD1PersistRoot ?? join(root, ".void"), options.localD1DatabaseId ?? "local")).catch(() => [])).length > 0) return;
|
|
1026
1054
|
}
|
|
1027
1055
|
voidWarn(`Schema found but no migrations in ${paths.sourceRelative("db/migrations")}/.\n \`void db push\` syncs the schema to your local DB without creating migration files.
|
|
@@ -1039,7 +1067,6 @@ function createPrimaryDb(d1) {
|
|
|
1039
1067
|
}
|
|
1040
1068
|
//#endregion
|
|
1041
1069
|
//#region src/plugin-email-dev-inbox.ts
|
|
1042
|
-
const INBOX_PREFIX = DEV_INBOX_PREFIX;
|
|
1043
1070
|
const SECURITY_HEADERS = {
|
|
1044
1071
|
"Cache-Control": "no-store",
|
|
1045
1072
|
"X-Content-Type-Options": "nosniff",
|
|
@@ -1049,18 +1076,14 @@ const SECURITY_HEADERS = {
|
|
|
1049
1076
|
};
|
|
1050
1077
|
function emailDevInboxPlugin(options) {
|
|
1051
1078
|
const { devTriggerToken } = options;
|
|
1052
|
-
let devMode = false;
|
|
1053
1079
|
let unregister;
|
|
1054
1080
|
/** `href` for an inbox path that keeps the browser session authorized. */
|
|
1055
|
-
const inboxHref = (path) => `${
|
|
1081
|
+
const inboxHref = (path) => `${DEV_INBOX_PREFIX}${path}?${DEV_INBOX_TOKEN_QUERY}=${encodeURIComponent(devTriggerToken)}`;
|
|
1056
1082
|
return {
|
|
1057
1083
|
name: "void:email-dev-inbox",
|
|
1058
1084
|
apply: "serve",
|
|
1059
|
-
config() {
|
|
1060
|
-
devMode = true;
|
|
1061
|
-
},
|
|
1062
1085
|
buildStart() {
|
|
1063
|
-
if (
|
|
1086
|
+
if (unregister) return;
|
|
1064
1087
|
unregister = registerEmailSink((m) => pushToDevBuffer(m));
|
|
1065
1088
|
},
|
|
1066
1089
|
buildEnd() {
|
|
@@ -1070,7 +1093,6 @@ function emailDevInboxPlugin(options) {
|
|
|
1070
1093
|
}
|
|
1071
1094
|
},
|
|
1072
1095
|
configureServer(server) {
|
|
1073
|
-
if (server.config.command !== "serve") return;
|
|
1074
1096
|
const printInboxUrl = () => {
|
|
1075
1097
|
const address = server.httpServer?.address();
|
|
1076
1098
|
const port = typeof address === "object" && address ? address.port : null;
|
|
@@ -1078,12 +1100,7 @@ function emailDevInboxPlugin(options) {
|
|
|
1078
1100
|
voidLog(`Email inbox: http://localhost:${port}${inboxHref("")}`);
|
|
1079
1101
|
};
|
|
1080
1102
|
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(printInboxUrl, 100));
|
|
1081
|
-
server.middlewares.use(
|
|
1082
|
-
if (server.config.command !== "serve") {
|
|
1083
|
-
res.writeHead(404, SECURITY_HEADERS);
|
|
1084
|
-
res.end();
|
|
1085
|
-
return;
|
|
1086
|
-
}
|
|
1103
|
+
server.middlewares.use(DEV_INBOX_PREFIX, (req, res) => {
|
|
1087
1104
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
1088
1105
|
const path = url.pathname;
|
|
1089
1106
|
if (!isAuthorized(req.headers["x-void-dev-trigger"], url, devTriggerToken)) {
|
|
@@ -1296,168 +1313,6 @@ function renderDetailHtml(m, href) {
|
|
|
1296
1313
|
</body></html>`;
|
|
1297
1314
|
}
|
|
1298
1315
|
//#endregion
|
|
1299
|
-
//#region src/plugin-env-client-fold.ts
|
|
1300
|
-
let cachedModule$1 = null;
|
|
1301
|
-
async function loadEnvPublic$1() {
|
|
1302
|
-
if (cachedModule$1) return cachedModule$1;
|
|
1303
|
-
const mod = await import("./runtime/env-public.mjs");
|
|
1304
|
-
cachedModule$1 = mod;
|
|
1305
|
-
return mod;
|
|
1306
|
-
}
|
|
1307
|
-
const SKIP_EXT$1 = /\.(?:css|s[ac]ss|less|styl|json|svg|png|jpe?g|gif|webp|avif|ico|woff2?|ttf|otf|eot|wasm|mp[34]|webm|md|mdx)(?:\?|$)/;
|
|
1308
|
-
const SCRIPT_LANG_RE$1 = /[?&]lang\.([a-z0-9]+)/i;
|
|
1309
|
-
function mentionsVoidEnv$1(code) {
|
|
1310
|
-
return code.includes("void/env");
|
|
1311
|
-
}
|
|
1312
|
-
function extractEnvBindings$1(program) {
|
|
1313
|
-
const direct = /* @__PURE__ */ new Set();
|
|
1314
|
-
const namespace = /* @__PURE__ */ new Set();
|
|
1315
|
-
let found = false;
|
|
1316
|
-
for (const node of program.body ?? []) {
|
|
1317
|
-
if (node.type !== "ImportDeclaration") continue;
|
|
1318
|
-
if (node.source.value !== "void/env") continue;
|
|
1319
|
-
found = true;
|
|
1320
|
-
for (const spec of node.specifiers ?? []) if (spec.type === "ImportSpecifier") {
|
|
1321
|
-
const importedName = spec.imported;
|
|
1322
|
-
if (!isIdentifier(importedName)) continue;
|
|
1323
|
-
if (importedName.name === "env") direct.add(spec.local.name);
|
|
1324
|
-
} else if (spec.type === "ImportNamespaceSpecifier") namespace.add(spec.local.name);
|
|
1325
|
-
}
|
|
1326
|
-
if (!found || direct.size === 0 && namespace.size === 0) return null;
|
|
1327
|
-
return {
|
|
1328
|
-
directNames: direct,
|
|
1329
|
-
namespaceNames: namespace
|
|
1330
|
-
};
|
|
1331
|
-
}
|
|
1332
|
-
/**
|
|
1333
|
-
* Walk the AST and collect every static `env.FOO` / `env["FOO"]` (or
|
|
1334
|
-
* `<ns>.env.FOO`) read. Dynamic keys (`env[variable]`) are intentionally
|
|
1335
|
-
* ignored — the Proxy still serves them at runtime, and we can't prove
|
|
1336
|
-
* they'd be folded the same way Vite folds `import.meta.env.FOO`.
|
|
1337
|
-
*/
|
|
1338
|
-
function findFoldHits(program, bindings) {
|
|
1339
|
-
const hits = [];
|
|
1340
|
-
walk(program, { enter(node) {
|
|
1341
|
-
const current = node;
|
|
1342
|
-
if (!isMemberExpression(current)) return;
|
|
1343
|
-
if (isIdentifier(current.object) && bindings.directNames.has(current.object.name)) {
|
|
1344
|
-
pushHit(current, hits);
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
const obj = current.object;
|
|
1348
|
-
if (isMemberExpression(obj) && !obj.computed && isIdentifier(obj.object) && bindings.namespaceNames.has(obj.object.name) && isIdentifier(obj.property) && obj.property.name === "env") pushHit(current, hits);
|
|
1349
|
-
} });
|
|
1350
|
-
return hits;
|
|
1351
|
-
}
|
|
1352
|
-
function pushHit(node, hits) {
|
|
1353
|
-
if (!node.computed && isIdentifier(node.property)) {
|
|
1354
|
-
hits.push({
|
|
1355
|
-
start: node.start,
|
|
1356
|
-
end: node.end,
|
|
1357
|
-
property: node.property.name
|
|
1358
|
-
});
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
if (node.computed && isLiteral(node.property) && typeof node.property.value === "string") {
|
|
1362
|
-
hits.push({
|
|
1363
|
-
start: node.start,
|
|
1364
|
-
end: node.end,
|
|
1365
|
-
property: node.property.value
|
|
1366
|
-
});
|
|
1367
|
-
return;
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
const CLIENT_ENV_PREFIX$1 = "VITE_";
|
|
1371
|
-
/**
|
|
1372
|
-
* Build-time constant folding for client-only `env.FOO` reads.
|
|
1373
|
-
*
|
|
1374
|
-
* Purpose: match what Vite already does for `import.meta.env.FOO` so that
|
|
1375
|
-
* branches like `if (env.MODE === "production") { ... }` get dead-code
|
|
1376
|
-
* eliminated in production client bundles. The runtime `env` Proxy on the
|
|
1377
|
-
* client already resolves keys via `import.meta.env`, so *semantics* don't
|
|
1378
|
-
* change — we just move the lookup from runtime to build time, which
|
|
1379
|
-
* unlocks DCE and bundler minification of guarded blocks.
|
|
1380
|
-
*
|
|
1381
|
-
* Safety properties:
|
|
1382
|
-
* • Runs only when `this.environment.name === 'client'`. Server / SSR /
|
|
1383
|
-
* worker builds keep the full runtime Proxy (secrets, runtime-injected
|
|
1384
|
-
* vars, HMR remain live).
|
|
1385
|
-
* • Only folds keys that match Void's fixed `VITE_` client prefix. A
|
|
1386
|
-
* server-only key is never folded (and the env-client-guard plugin
|
|
1387
|
-
* would have errored out earlier anyway).
|
|
1388
|
-
* • Only folds static member access: `env.FOO` and `env["FOO"]`.
|
|
1389
|
-
* Dynamic access, destructuring, and reassignment fall through to the
|
|
1390
|
-
* runtime Proxy.
|
|
1391
|
-
* • Only folds keys with a known build-time value (build shell or schema
|
|
1392
|
-
* `.default(...)`). Unknown keys are left as-is
|
|
1393
|
-
* and the Proxy handles them at runtime — no "undefined" shoved in.
|
|
1394
|
-
* • Build-only (`apply: 'build'`). Dev runs through the Proxy so local
|
|
1395
|
-
* `.env` edits take effect without a full rebuild.
|
|
1396
|
-
*/
|
|
1397
|
-
function envClientFoldPlugin() {
|
|
1398
|
-
let loadedEnv = {};
|
|
1399
|
-
const schemaDefaults = {};
|
|
1400
|
-
/** The merged view the transform consults for each key. */
|
|
1401
|
-
function resolveValue(key) {
|
|
1402
|
-
if (key in loadedEnv) return loadedEnv[key];
|
|
1403
|
-
if (key in schemaDefaults) return schemaDefaults[key];
|
|
1404
|
-
}
|
|
1405
|
-
function isExposedToClient(key) {
|
|
1406
|
-
return key.startsWith(CLIENT_ENV_PREFIX$1);
|
|
1407
|
-
}
|
|
1408
|
-
return {
|
|
1409
|
-
name: "void:env-client-fold",
|
|
1410
|
-
apply: "build",
|
|
1411
|
-
enforce: "post",
|
|
1412
|
-
async configResolved(config) {
|
|
1413
|
-
loadedEnv = config.env ?? {};
|
|
1414
|
-
try {
|
|
1415
|
-
const mod = await loadEnvPublic$1();
|
|
1416
|
-
if (mod._hasRegisteredSchema()) {
|
|
1417
|
-
const defaults = mod._getSchemaDefaults();
|
|
1418
|
-
for (const [key, value] of Object.entries(defaults)) if (isExposedToClient(key) && !(key in loadedEnv)) schemaDefaults[key] = value;
|
|
1419
|
-
}
|
|
1420
|
-
} catch {}
|
|
1421
|
-
},
|
|
1422
|
-
async transform(code, id) {
|
|
1423
|
-
if (this.environment?.name !== "client") return;
|
|
1424
|
-
if (id.includes("/node_modules/")) return;
|
|
1425
|
-
if (SKIP_EXT$1.test(id)) return;
|
|
1426
|
-
const langMatch = SCRIPT_LANG_RE$1.exec(id);
|
|
1427
|
-
const isScriptQuery = langMatch !== null && /^(?:m?[jt]sx?|cjs)$/i.test(langMatch[1]);
|
|
1428
|
-
const isPlainScript = /\.(?:[mc]?[jt]sx?)(?:\?|$)/.test(id);
|
|
1429
|
-
const isSfc = /\.(?:svelte|vue|astro)(?:\?|$)/.test(id);
|
|
1430
|
-
if (!isScriptQuery && !isPlainScript && !isSfc) return;
|
|
1431
|
-
if (!mentionsVoidEnv$1(code)) return;
|
|
1432
|
-
if (Object.keys(loadedEnv).length === 0 && Object.keys(schemaDefaults).length === 0) return;
|
|
1433
|
-
let parsed;
|
|
1434
|
-
try {
|
|
1435
|
-
parsed = parseSync(id, code);
|
|
1436
|
-
} catch {
|
|
1437
|
-
return;
|
|
1438
|
-
}
|
|
1439
|
-
const program = parsed.program;
|
|
1440
|
-
const bindings = extractEnvBindings$1(program);
|
|
1441
|
-
if (!bindings) return;
|
|
1442
|
-
const hits = findFoldHits(program, bindings);
|
|
1443
|
-
if (hits.length === 0) return;
|
|
1444
|
-
let ms = null;
|
|
1445
|
-
for (const hit of hits) {
|
|
1446
|
-
if (!isExposedToClient(hit.property)) continue;
|
|
1447
|
-
const value = resolveValue(hit.property);
|
|
1448
|
-
if (value === void 0) continue;
|
|
1449
|
-
if (!ms) ms = new MagicString(code);
|
|
1450
|
-
ms.overwrite(hit.start, hit.end, JSON.stringify(value));
|
|
1451
|
-
}
|
|
1452
|
-
if (!ms) return;
|
|
1453
|
-
return {
|
|
1454
|
-
code: ms.toString(),
|
|
1455
|
-
map: ms.generateMap({ hires: "boundary" })
|
|
1456
|
-
};
|
|
1457
|
-
}
|
|
1458
|
-
};
|
|
1459
|
-
}
|
|
1460
|
-
//#endregion
|
|
1461
1316
|
//#region src/plugin-env-client-guard.ts
|
|
1462
1317
|
let cachedModule = null;
|
|
1463
1318
|
async function loadEnvPublic() {
|
|
@@ -1469,12 +1324,22 @@ async function loadEnvPublic() {
|
|
|
1469
1324
|
const SKIP_EXT = /\.(?:css|s[ac]ss|less|styl|json|svg|png|jpe?g|gif|webp|avif|ico|woff2?|ttf|otf|eot|wasm|mp[34]|webm|md|mdx)(?:\?|$)/;
|
|
1470
1325
|
const SCRIPT_LANG_RE = /[?&]lang\.([a-z0-9]+)/i;
|
|
1471
1326
|
/**
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
*
|
|
1327
|
+
* Cheap prefilter shared with the fold plugin: is this a client script
|
|
1328
|
+
* module that mentions `void/env`? Anything we'd want to parse is JS/TS or
|
|
1329
|
+
* has a script sub-resource (Vue/Svelte SFC virtual chunks include a
|
|
1330
|
+
* `lang.tsx` / `lang.ts` / `lang.js` query). SFC files themselves
|
|
1331
|
+
* (`.svelte`, `.vue`, `.astro`) are accepted because both plugins run with
|
|
1332
|
+
* `enforce: 'post'`, after the framework compiler has turned them into
|
|
1333
|
+
* plain JS. The `void/env` substring check lets most files skip the AST
|
|
1334
|
+
* walk; a false positive only means an unneeded parse.
|
|
1476
1335
|
*/
|
|
1477
|
-
function
|
|
1336
|
+
function isVoidEnvScriptCandidate(id, code) {
|
|
1337
|
+
if (id.includes("/node_modules/") || SKIP_EXT.test(id)) return false;
|
|
1338
|
+
const langMatch = SCRIPT_LANG_RE.exec(id);
|
|
1339
|
+
const isScriptQuery = langMatch !== null && /^(?:m?[jt]sx?|cjs)$/i.test(langMatch[1]);
|
|
1340
|
+
const isPlainScript = /\.(?:[mc]?[jt]sx?)(?:\?|$)/.test(id);
|
|
1341
|
+
const isSfc = /\.(?:svelte|vue|astro)(?:\?|$)/.test(id);
|
|
1342
|
+
if (!isScriptQuery && !isPlainScript && !isSfc) return false;
|
|
1478
1343
|
return code.includes("void/env");
|
|
1479
1344
|
}
|
|
1480
1345
|
function extractEnvBindings(program) {
|
|
@@ -1490,7 +1355,6 @@ function extractEnvBindings(program) {
|
|
|
1490
1355
|
if (!isIdentifier(importedName)) continue;
|
|
1491
1356
|
if (importedName.name === "env") direct.add(spec.local.name);
|
|
1492
1357
|
} else if (spec.type === "ImportNamespaceSpecifier") namespace.add(spec.local.name);
|
|
1493
|
-
else if (spec.type === "ImportDefaultSpecifier") {}
|
|
1494
1358
|
}
|
|
1495
1359
|
if (!found || direct.size === 0 && namespace.size === 0) return null;
|
|
1496
1360
|
return {
|
|
@@ -1518,22 +1382,28 @@ function offsetToLineCol(index, offset) {
|
|
|
1518
1382
|
};
|
|
1519
1383
|
}
|
|
1520
1384
|
/**
|
|
1521
|
-
*
|
|
1522
|
-
*
|
|
1523
|
-
* with `property === null`; everything else carries the property name.
|
|
1385
|
+
* Visit every `env.X` (or `<ns>.env.X`) member expression where `env` is
|
|
1386
|
+
* bound to the `void/env` proxy.
|
|
1524
1387
|
*/
|
|
1525
|
-
function
|
|
1526
|
-
const hits = [];
|
|
1388
|
+
function walkEnvReads(program, bindings, visit) {
|
|
1527
1389
|
walk(program, { enter(node) {
|
|
1528
1390
|
const current = node;
|
|
1529
1391
|
if (!isMemberExpression(current)) return;
|
|
1530
1392
|
if (isIdentifier(current.object) && bindings.directNames.has(current.object.name)) {
|
|
1531
|
-
|
|
1393
|
+
visit(current);
|
|
1532
1394
|
return;
|
|
1533
1395
|
}
|
|
1534
1396
|
const obj = current.object;
|
|
1535
|
-
if (isMemberExpression(obj) && !obj.computed && isIdentifier(obj.object) && bindings.namespaceNames.has(obj.object.name) && isIdentifier(obj.property) && obj.property.name === "env")
|
|
1397
|
+
if (isMemberExpression(obj) && !obj.computed && isIdentifier(obj.object) && bindings.namespaceNames.has(obj.object.name) && isIdentifier(obj.property) && obj.property.name === "env") visit(current);
|
|
1536
1398
|
} });
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Collect every `void/env` read. Computed access produces a warning hit
|
|
1402
|
+
* with `property === null`; everything else carries the property name.
|
|
1403
|
+
*/
|
|
1404
|
+
function findHits(program, bindings) {
|
|
1405
|
+
const hits = [];
|
|
1406
|
+
walkEnvReads(program, bindings, (node) => recordHit(node, hits));
|
|
1537
1407
|
return hits;
|
|
1538
1408
|
}
|
|
1539
1409
|
function recordHit(node, hits) {
|
|
@@ -1564,21 +1434,13 @@ function recordHit(node, hits) {
|
|
|
1564
1434
|
* No-op when no `env.ts` is registered. Server / SSR / worker environments
|
|
1565
1435
|
* are skipped — only the `client` environment is checked.
|
|
1566
1436
|
*/
|
|
1567
|
-
const CLIENT_ENV_PREFIX = "VITE_";
|
|
1568
1437
|
function envClientGuardPlugin(root) {
|
|
1569
1438
|
return {
|
|
1570
1439
|
name: "void:env-client-guard",
|
|
1571
1440
|
enforce: "post",
|
|
1572
1441
|
async transform(code, id) {
|
|
1573
1442
|
if (this.environment?.name !== "client") return;
|
|
1574
|
-
if (id
|
|
1575
|
-
if (SKIP_EXT.test(id)) return;
|
|
1576
|
-
const langMatch = SCRIPT_LANG_RE.exec(id);
|
|
1577
|
-
const isScriptQuery = langMatch !== null && /^(?:m?[jt]sx?|cjs)$/i.test(langMatch[1]);
|
|
1578
|
-
const isPlainScript = /\.(?:[mc]?[jt]sx?)(?:\?|$)/.test(id);
|
|
1579
|
-
const isSfc = /\.(?:svelte|vue|astro)(?:\?|$)/.test(id);
|
|
1580
|
-
if (!isScriptQuery && !isPlainScript && !isSfc) return;
|
|
1581
|
-
if (!mentionsVoidEnv(code)) return;
|
|
1443
|
+
if (!isVoidEnvScriptCandidate(id, code)) return;
|
|
1582
1444
|
const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
|
|
1583
1445
|
if (!_hasRegisteredSchema()) return;
|
|
1584
1446
|
const schema = _getRegisteredSchema();
|
|
@@ -1611,7 +1473,7 @@ function envClientGuardPlugin(root) {
|
|
|
1611
1473
|
}
|
|
1612
1474
|
const key = hit.property;
|
|
1613
1475
|
if (!(key in schema)) continue;
|
|
1614
|
-
if (key
|
|
1476
|
+
if (isClientEnvKey(key)) continue;
|
|
1615
1477
|
this.error({
|
|
1616
1478
|
message: `Server-only env "${key}" referenced from client code at ${relPath}:${line}:${column}.\nIf this should be available in the browser, rename it with the VITE_* prefix in env.ts.\nIf it must stay server-only, remove this reference and read the value via a server route or loader.`,
|
|
1617
1479
|
loc: {
|
|
@@ -1625,14 +1487,176 @@ function envClientGuardPlugin(root) {
|
|
|
1625
1487
|
};
|
|
1626
1488
|
}
|
|
1627
1489
|
//#endregion
|
|
1490
|
+
//#region src/plugin-env-client-fold.ts
|
|
1491
|
+
/**
|
|
1492
|
+
* Walk the AST and collect every static `env.FOO` / `env["FOO"]` (or
|
|
1493
|
+
* `<ns>.env.FOO`) read. Dynamic keys (`env[variable]`) are intentionally
|
|
1494
|
+
* ignored — the Proxy still serves them at runtime, and we can't prove
|
|
1495
|
+
* they'd be folded the same way Vite folds `import.meta.env.FOO`.
|
|
1496
|
+
*/
|
|
1497
|
+
function findFoldHits(program, bindings) {
|
|
1498
|
+
const hits = [];
|
|
1499
|
+
walkEnvReads(program, bindings, (node) => pushHit(node, hits));
|
|
1500
|
+
return hits;
|
|
1501
|
+
}
|
|
1502
|
+
function pushHit(node, hits) {
|
|
1503
|
+
if (!node.computed && isIdentifier(node.property)) {
|
|
1504
|
+
hits.push({
|
|
1505
|
+
start: node.start,
|
|
1506
|
+
end: node.end,
|
|
1507
|
+
property: node.property.name
|
|
1508
|
+
});
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
if (node.computed && isLiteral(node.property) && typeof node.property.value === "string") {
|
|
1512
|
+
hits.push({
|
|
1513
|
+
start: node.start,
|
|
1514
|
+
end: node.end,
|
|
1515
|
+
property: node.property.value
|
|
1516
|
+
});
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Build-time constant folding for client-only `env.FOO` reads.
|
|
1522
|
+
*
|
|
1523
|
+
* Purpose: match what Vite already does for `import.meta.env.FOO` so that
|
|
1524
|
+
* branches like `if (env.VITE_FLAG === "on") { ... }` get dead-code
|
|
1525
|
+
* eliminated in production client bundles. The runtime `env` Proxy on the
|
|
1526
|
+
* client already resolves keys via `import.meta.env`, so *semantics* don't
|
|
1527
|
+
* change — we just move the lookup from runtime to build time, which
|
|
1528
|
+
* unlocks DCE and bundler minification of guarded blocks.
|
|
1529
|
+
*
|
|
1530
|
+
* Safety properties:
|
|
1531
|
+
* • Runs only when `this.environment.name === 'client'`. Server / SSR /
|
|
1532
|
+
* worker builds keep the full runtime Proxy (secrets, runtime-injected
|
|
1533
|
+
* vars, HMR remain live).
|
|
1534
|
+
* • Only folds keys that match Void's fixed `VITE_` client prefix. A
|
|
1535
|
+
* server-only key is never folded (and the env-client-guard plugin
|
|
1536
|
+
* would have errored out earlier anyway).
|
|
1537
|
+
* • Only folds static member access: `env.FOO` and `env["FOO"]`.
|
|
1538
|
+
* Dynamic access, destructuring, and reassignment fall through to the
|
|
1539
|
+
* runtime Proxy.
|
|
1540
|
+
* • Only folds keys with a known build-time value (build shell or schema
|
|
1541
|
+
* `.default(...)`). Unknown keys are left as-is
|
|
1542
|
+
* and the Proxy handles them at runtime — no "undefined" shoved in.
|
|
1543
|
+
* • Build-only (`apply: 'build'`). Dev runs through the Proxy so local
|
|
1544
|
+
* `.env` edits take effect without a full rebuild.
|
|
1545
|
+
*/
|
|
1546
|
+
function envClientFoldPlugin() {
|
|
1547
|
+
let loadedEnv = {};
|
|
1548
|
+
const schemaDefaults = {};
|
|
1549
|
+
/** The merged view the transform consults for each key. */
|
|
1550
|
+
function resolveValue(key) {
|
|
1551
|
+
if (key in loadedEnv) return loadedEnv[key];
|
|
1552
|
+
if (key in schemaDefaults) return schemaDefaults[key];
|
|
1553
|
+
}
|
|
1554
|
+
return {
|
|
1555
|
+
name: "void:env-client-fold",
|
|
1556
|
+
apply: "build",
|
|
1557
|
+
enforce: "post",
|
|
1558
|
+
async configResolved(config) {
|
|
1559
|
+
loadedEnv = config.env ?? {};
|
|
1560
|
+
try {
|
|
1561
|
+
const mod = await loadEnvPublic();
|
|
1562
|
+
if (mod._hasRegisteredSchema()) {
|
|
1563
|
+
const defaults = mod._getSchemaDefaults();
|
|
1564
|
+
for (const [key, value] of Object.entries(defaults)) if (isClientEnvKey(key) && !(key in loadedEnv)) schemaDefaults[key] = value;
|
|
1565
|
+
}
|
|
1566
|
+
} catch {}
|
|
1567
|
+
},
|
|
1568
|
+
async transform(code, id) {
|
|
1569
|
+
if (this.environment?.name !== "client") return;
|
|
1570
|
+
if (!isVoidEnvScriptCandidate(id, code)) return;
|
|
1571
|
+
if (Object.keys(loadedEnv).length === 0 && Object.keys(schemaDefaults).length === 0) return;
|
|
1572
|
+
let parsed;
|
|
1573
|
+
try {
|
|
1574
|
+
parsed = parseSync(id, code);
|
|
1575
|
+
} catch {
|
|
1576
|
+
return;
|
|
1577
|
+
}
|
|
1578
|
+
const program = parsed.program;
|
|
1579
|
+
const bindings = extractEnvBindings(program);
|
|
1580
|
+
if (!bindings) return;
|
|
1581
|
+
const hits = findFoldHits(program, bindings);
|
|
1582
|
+
if (hits.length === 0) return;
|
|
1583
|
+
let ms = null;
|
|
1584
|
+
for (const hit of hits) {
|
|
1585
|
+
if (!isClientEnvKey(hit.property)) continue;
|
|
1586
|
+
const value = resolveValue(hit.property);
|
|
1587
|
+
if (value === void 0) continue;
|
|
1588
|
+
if (!ms) ms = new MagicString(code);
|
|
1589
|
+
ms.overwrite(hit.start, hit.end, JSON.stringify(value));
|
|
1590
|
+
}
|
|
1591
|
+
if (!ms) return;
|
|
1592
|
+
return {
|
|
1593
|
+
code: ms.toString(),
|
|
1594
|
+
map: ms.generateMap({ hires: "boundary" })
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
//#endregion
|
|
1628
1600
|
//#region src/plugin-env-schema.ts
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1601
|
+
/**
|
|
1602
|
+
* Variables Vite's `loadEnv` copies from env files into `process.env` when
|
|
1603
|
+
* they are unset, as `[file key, process.env key]`.
|
|
1604
|
+
*/
|
|
1605
|
+
const VITE_PROCESS_ENV_COPIES = [
|
|
1606
|
+
["NODE_ENV", "VITE_USER_NODE_ENV"],
|
|
1607
|
+
["BROWSER", "BROWSER"],
|
|
1608
|
+
["BROWSER_ARGS", "BROWSER_ARGS"]
|
|
1609
|
+
];
|
|
1610
|
+
/** The `process.env` values Vite may overwrite, read before it loads env files. */
|
|
1611
|
+
function readProcessEnvCopies() {
|
|
1612
|
+
return Object.fromEntries(VITE_PROCESS_ENV_COPIES.map(([, target]) => [target, process.env[target]]));
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Delete the client values Vite took from `<root>/.env` during a build.
|
|
1616
|
+
* Keys come from Vite's own parser (`node:util` `parseEnv`), so every key
|
|
1617
|
+
* Vite read is covered. A key stays when the shell or the `vp run` task
|
|
1618
|
+
* runner supplies it: `loadEnv` with `envDir: false` returns exactly those
|
|
1619
|
+
* values. Nothing is stripped when Vite loads env files from another
|
|
1620
|
+
* `envDir` (or none), since `<root>/.env` was not the source.
|
|
1621
|
+
*
|
|
1622
|
+
* Vite also copies a few `.env` values into `process.env` (see
|
|
1623
|
+
* {@link VITE_PROCESS_ENV_COPIES}), where they would pass as shell values.
|
|
1624
|
+
* `before` holds those variables as they were before Vite loaded the file;
|
|
1625
|
+
* a copy Vite made is removed from `process.env` again, so this check and
|
|
1626
|
+
* the next config resolution in the same build (the app builder resolves
|
|
1627
|
+
* once per environment) see only the shell.
|
|
1628
|
+
*/
|
|
1629
|
+
function stripLocalClientEnv(root, config, before) {
|
|
1630
|
+
if (config.envDir === false || realDir(config.envDir) !== realDir(root)) return;
|
|
1631
|
+
const file = join(root, LOCAL_ENV_FILE);
|
|
1632
|
+
if (!existsSync(file)) return;
|
|
1633
|
+
const parsed = parseEnv(readFileSync(file, "utf8"));
|
|
1634
|
+
const keys = new Set(Object.keys(parsed));
|
|
1635
|
+
for (const [source, target] of VITE_PROCESS_ENV_COPIES) {
|
|
1636
|
+
const value = parsed[source];
|
|
1637
|
+
if (value === void 0) continue;
|
|
1638
|
+
keys.add(target);
|
|
1639
|
+
if (before && before[target] === void 0 && process.env[target] === value) delete process.env[target];
|
|
1640
|
+
}
|
|
1641
|
+
const prefixes = resolveEnvPrefix({ envPrefix: config.envPrefix });
|
|
1642
|
+
const shell = loadEnv(config.mode, false, prefixes);
|
|
1643
|
+
for (const key of keys) {
|
|
1644
|
+
if (!prefixes.some((prefix) => key.startsWith(prefix))) continue;
|
|
1645
|
+
const value = shell[key];
|
|
1646
|
+
if (value === void 0) delete config.env[key];
|
|
1647
|
+
else config.env[key] = value;
|
|
1648
|
+
}
|
|
1635
1649
|
}
|
|
1650
|
+
/** Match Vite's canonical paths, including Windows short names and filesystem casing. */
|
|
1651
|
+
function realDir(dir) {
|
|
1652
|
+
try {
|
|
1653
|
+
return realpathSync.native(dir);
|
|
1654
|
+
} catch {
|
|
1655
|
+
return resolve(dir);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
/** Patterns Vite's `define` typically uses for env-shaped constants. */
|
|
1659
|
+
const DEFINE_ENV_PATTERN = /^(?:globalThis\.)?(?:process\.env|import\.meta\.env)\.([A-Z_][A-Z0-9_]*)$/;
|
|
1636
1660
|
/**
|
|
1637
1661
|
* Vite plugin that auto-discovers `env.ts` at the project root and:
|
|
1638
1662
|
* - imports it (registering the user's schema via `defineEnv`)
|
|
@@ -1641,24 +1665,22 @@ async function loadSchemaModule() {
|
|
|
1641
1665
|
* on missing/invalid required keys
|
|
1642
1666
|
* - regenerates types + re-validates when env.ts changes (HMR)
|
|
1643
1667
|
*
|
|
1644
|
-
* Hard errors are reserved for
|
|
1668
|
+
* Hard errors are reserved for builds and deploys; dev keeps moving with warnings
|
|
1645
1669
|
* so unrelated work isn't blocked by a stale local value.
|
|
1646
1670
|
*/
|
|
1647
|
-
/** Patterns Vite's `define` typically uses for env-shaped constants. */
|
|
1648
|
-
const DEFINE_ENV_PATTERN = /^(?:globalThis\.)?(?:process\.env|import\.meta\.env)\.([A-Z_][A-Z0-9_]*)$/;
|
|
1649
|
-
const CLIENT_ENV_PREFIXES = [CLIENT_ENV_PREFIX$2];
|
|
1650
1671
|
function envSchemaPlugin(root, options = {}) {
|
|
1651
1672
|
const outDir = join(root, ".void");
|
|
1652
1673
|
let envFile = null;
|
|
1653
1674
|
let command = "serve";
|
|
1675
|
+
let processEnvCopiesBefore;
|
|
1654
1676
|
const appliedClientDefaults = /* @__PURE__ */ new Map();
|
|
1655
|
-
async function emitSchemaFacts(
|
|
1677
|
+
async function emitSchemaFacts() {
|
|
1656
1678
|
if (!envFile || !options.onSchema) return;
|
|
1657
|
-
const { _hasRegisteredSchema, _getRegisteredSchema } = await
|
|
1679
|
+
const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
|
|
1658
1680
|
if (!_hasRegisteredSchema()) return;
|
|
1659
1681
|
const schema = _getRegisteredSchema() ?? {};
|
|
1660
1682
|
const keys = Object.keys(schema);
|
|
1661
|
-
const clientKeys = keys.filter(
|
|
1683
|
+
const clientKeys = keys.filter(isClientEnvKey);
|
|
1662
1684
|
const clientKeySet = new Set(clientKeys);
|
|
1663
1685
|
const serverKeys = keys.filter((key) => !clientKeySet.has(key));
|
|
1664
1686
|
const requiredServerKeys = [];
|
|
@@ -1673,6 +1695,12 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1673
1695
|
requiredServerKeys
|
|
1674
1696
|
});
|
|
1675
1697
|
}
|
|
1698
|
+
async function assertNoReservedSchemaKeys() {
|
|
1699
|
+
const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
|
|
1700
|
+
if (!_hasRegisteredSchema()) return;
|
|
1701
|
+
const reserved = findReservedEnvKeys(Object.keys(_getRegisteredSchema() ?? {}));
|
|
1702
|
+
if (reserved.length > 0) throw new Error(reservedEnvKeysMessage("env.ts", reserved));
|
|
1703
|
+
}
|
|
1676
1704
|
function regenerateTypes() {
|
|
1677
1705
|
if (!envFile) return;
|
|
1678
1706
|
mkdirSync(outDir, { recursive: true });
|
|
@@ -1689,7 +1717,7 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1689
1717
|
}
|
|
1690
1718
|
}
|
|
1691
1719
|
async function warnOnDefineOverlap(define) {
|
|
1692
|
-
const { _hasRegisteredSchema, _getRegisteredSchema } = await
|
|
1720
|
+
const { _hasRegisteredSchema, _getRegisteredSchema } = await loadEnvPublic();
|
|
1693
1721
|
if (!_hasRegisteredSchema()) return;
|
|
1694
1722
|
const schema = _getRegisteredSchema();
|
|
1695
1723
|
if (!schema) return;
|
|
@@ -1710,7 +1738,7 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1710
1738
|
}
|
|
1711
1739
|
async function validateAndWarn() {
|
|
1712
1740
|
if (!envFile) return;
|
|
1713
|
-
const { _hasRegisteredSchema, validateAllEnv } = await
|
|
1741
|
+
const { _hasRegisteredSchema, validateAllEnv } = await loadEnvPublic();
|
|
1714
1742
|
if (!_hasRegisteredSchema()) return;
|
|
1715
1743
|
const report = await validateAllEnv({
|
|
1716
1744
|
...loadLocalEnv(root, { onWarn: (message) => voidWarn(message) }),
|
|
@@ -1727,7 +1755,7 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1727
1755
|
const configEnv = config.env ?? {};
|
|
1728
1756
|
for (const [key, value] of appliedClientDefaults) if (configEnv[key] === value) delete configEnv[key];
|
|
1729
1757
|
appliedClientDefaults.clear();
|
|
1730
|
-
const { _hasRegisteredSchema, _getRegisteredSchema, _getSchemaDefaults } = await
|
|
1758
|
+
const { _hasRegisteredSchema, _getRegisteredSchema, _getSchemaDefaults } = await loadEnvPublic();
|
|
1731
1759
|
if (!_hasRegisteredSchema()) return;
|
|
1732
1760
|
const schema = _getRegisteredSchema() ?? {};
|
|
1733
1761
|
const readable = {
|
|
@@ -1735,13 +1763,12 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1735
1763
|
...process.env
|
|
1736
1764
|
};
|
|
1737
1765
|
for (const key of Object.keys(schema)) {
|
|
1738
|
-
if (!key
|
|
1766
|
+
if (!isClientEnvKey(key) || configEnv[key] !== void 0) continue;
|
|
1739
1767
|
const value = readable[key];
|
|
1740
1768
|
if (value !== void 0) configEnv[key] = value;
|
|
1741
1769
|
}
|
|
1742
1770
|
for (const [key, value] of Object.entries(_getSchemaDefaults())) {
|
|
1743
|
-
if (!key
|
|
1744
|
-
if (configEnv[key] !== void 0) continue;
|
|
1771
|
+
if (!isClientEnvKey(key) || configEnv[key] !== void 0) continue;
|
|
1745
1772
|
configEnv[key] = value;
|
|
1746
1773
|
appliedClientDefaults.set(key, value);
|
|
1747
1774
|
}
|
|
@@ -1750,31 +1777,31 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1750
1777
|
name: "void:env-schema",
|
|
1751
1778
|
enforce: "pre",
|
|
1752
1779
|
async config(_config, environment) {
|
|
1780
|
+
processEnvCopiesBefore = environment.command === "build" ? readProcessEnvCopies() : void 0;
|
|
1753
1781
|
const legacyFiles = findLegacyEnvFiles(root);
|
|
1754
1782
|
if (legacyFiles.length > 0) throw new Error(`void/env supports one local file: .env. Remove ${legacyFiles.join(", ")} and follow the env migration guide.`);
|
|
1783
|
+
const reservedInDotenv = findReservedEnvKeys(scanWranglerDotenvKeys(join(root, LOCAL_ENV_FILE)));
|
|
1784
|
+
if (reservedInDotenv.length > 0) throw new Error(reservedEnvKeysMessage(".env", reservedInDotenv));
|
|
1755
1785
|
envFile = findEnvFile(root);
|
|
1756
1786
|
if (!envFile) return;
|
|
1757
1787
|
regenerateTypes();
|
|
1758
1788
|
await importUserEnv(environment.command === "build");
|
|
1759
|
-
await emitSchemaFacts(
|
|
1789
|
+
await emitSchemaFacts();
|
|
1790
|
+
await assertNoReservedSchemaKeys();
|
|
1760
1791
|
},
|
|
1761
1792
|
async configResolved(config) {
|
|
1762
1793
|
command = config.command;
|
|
1763
|
-
await emitSchemaFacts(
|
|
1764
|
-
if (command === "build")
|
|
1765
|
-
const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
|
|
1766
|
-
for (const [key, value] of Object.entries(local)) if (process.env[key] === void 0 && config.env[key] === value) delete config.env[key];
|
|
1767
|
-
}
|
|
1794
|
+
await emitSchemaFacts();
|
|
1795
|
+
if (command === "build") stripLocalClientEnv(root, config, processEnvCopiesBefore);
|
|
1768
1796
|
await applyClientValuesToViteEnv(config);
|
|
1769
1797
|
if (command === "build" && envFile) {
|
|
1770
|
-
const { validateAllEnv } = await
|
|
1798
|
+
const { validateAllEnv } = await loadEnvPublic();
|
|
1771
1799
|
const report = await validateAllEnv({
|
|
1772
1800
|
...config.env,
|
|
1773
1801
|
...process.env
|
|
1774
1802
|
});
|
|
1775
|
-
const
|
|
1776
|
-
const
|
|
1777
|
-
const invalid = report.invalid.filter(({ key }) => clientKey(key));
|
|
1803
|
+
const missing = report.missing.filter(isClientEnvKey);
|
|
1804
|
+
const invalid = report.invalid.filter(({ key }) => isClientEnvKey(key));
|
|
1778
1805
|
if (missing.length > 0 || invalid.length > 0) {
|
|
1779
1806
|
const lines = ["Client env validation failed:"];
|
|
1780
1807
|
for (const key of missing) lines.push(` • ${key} is required — set it in the build shell or add a schema default`);
|
|
@@ -1787,18 +1814,16 @@ function envSchemaPlugin(root, options = {}) {
|
|
|
1787
1814
|
async configureServer(server) {
|
|
1788
1815
|
if (!envFile) return;
|
|
1789
1816
|
await importUserEnv();
|
|
1790
|
-
|
|
1817
|
+
afterServerReady(server, () => {
|
|
1791
1818
|
validateAndWarn();
|
|
1792
|
-
};
|
|
1793
|
-
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(run, 100));
|
|
1794
|
-
else run();
|
|
1819
|
+
});
|
|
1795
1820
|
server.watcher.add(envFile);
|
|
1796
1821
|
server.watcher.on("change", async (file) => {
|
|
1797
1822
|
if (file === envFile) {
|
|
1798
1823
|
voidLog("env.ts changed — regenerating types");
|
|
1799
1824
|
regenerateTypes();
|
|
1800
1825
|
await importUserEnv();
|
|
1801
|
-
await emitSchemaFacts(
|
|
1826
|
+
await emitSchemaFacts();
|
|
1802
1827
|
await applyClientValuesToViteEnv(server.config);
|
|
1803
1828
|
await validateAndWarn();
|
|
1804
1829
|
server.ws.send({ type: "full-reload" });
|
|
@@ -1899,12 +1924,9 @@ function __json(body, status = 200) {
|
|
|
1899
1924
|
});
|
|
1900
1925
|
}
|
|
1901
1926
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
const expected = env?.__VOID_PROXY_TOKEN;
|
|
1906
|
-
if (expected) return internal === expected;
|
|
1907
|
-
return dev === __DEV_TRIGGER_TOKEN;
|
|
1927
|
+
// Dev only: production framework Workers dispatch through cli/wrapper.ts.
|
|
1928
|
+
function __authorize(request) {
|
|
1929
|
+
return request.headers.get("x-void-dev-trigger") === __DEV_TRIGGER_TOKEN;
|
|
1908
1930
|
}
|
|
1909
1931
|
|
|
1910
1932
|
function __wrapMessage(m, decisions) {
|
|
@@ -1994,7 +2016,7 @@ export async function handleDevTrigger(request, env, ctx) {
|
|
|
1994
2016
|
if (url.pathname !== "/__void/scheduled" && url.pathname !== "/__void/queue") {
|
|
1995
2017
|
return null;
|
|
1996
2018
|
}
|
|
1997
|
-
if (!__authorize(request
|
|
2019
|
+
if (!__authorize(request)) {
|
|
1998
2020
|
return __json({ error: "unauthorized" }, 401);
|
|
1999
2021
|
}
|
|
2000
2022
|
if (url.pathname === "/__void/scheduled") {
|
|
@@ -2004,6 +2026,17 @@ export async function handleDevTrigger(request, env, ctx) {
|
|
|
2004
2026
|
}
|
|
2005
2027
|
`;
|
|
2006
2028
|
}
|
|
2029
|
+
/** Append any missing `lines` to the project's `.gitignore`. */
|
|
2030
|
+
function ensureGitignoreLines(root, lines) {
|
|
2031
|
+
const gitignorePath = join(root, ".gitignore");
|
|
2032
|
+
let contents = "";
|
|
2033
|
+
if (existsSync(gitignorePath)) contents = readFileSync(gitignorePath, "utf-8");
|
|
2034
|
+
const existing = new Set(contents.split("\n").map((line) => line.trim()));
|
|
2035
|
+
const missing = lines.filter((line) => !existing.has(line));
|
|
2036
|
+
if (missing.length === 0) return;
|
|
2037
|
+
const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
|
|
2038
|
+
writeFileSync(gitignorePath, `${contents}${sep}${missing.join("\n")}\n`);
|
|
2039
|
+
}
|
|
2007
2040
|
const NITRO_MIDDLEWARE_FILENAME = "__void-dev-trigger.ts";
|
|
2008
2041
|
function nitroMiddlewareSource(dispatchPath) {
|
|
2009
2042
|
return `// @ts-nocheck — Auto-generated by Void in dev. Do NOT edit. Removed when the dev server stops.
|
|
@@ -2040,33 +2073,22 @@ export default async function voidDevTrigger(event) {
|
|
|
2040
2073
|
}
|
|
2041
2074
|
/**
|
|
2042
2075
|
* Writes a tiny Nitro server middleware to the user's project so
|
|
2043
|
-
*
|
|
2044
|
-
* request pipeline (where
|
|
2076
|
+
* `/__void/scheduled` and `/__void/queue` are intercepted inside Nitro's
|
|
2077
|
+
* request pipeline (where `event.context.cloudflare?.env` is populated).
|
|
2045
2078
|
*
|
|
2046
|
-
* Nitro discovers middleware from
|
|
2047
|
-
*
|
|
2048
|
-
* To keep the generated file out of git we append a
|
|
2079
|
+
* Nitro discovers middleware from `<serverDir>/middleware/*.ts`. Nuxt scans
|
|
2080
|
+
* `server/`, Analog scans `src/server/`, so the caller specifies which.
|
|
2081
|
+
* To keep the generated file out of git we append a `.gitignore` entry,
|
|
2049
2082
|
* and the file is removed when the dev server stops.
|
|
2050
2083
|
*/
|
|
2051
2084
|
function nitroDevTriggerInjectPlugin(root, options) {
|
|
2052
2085
|
const middlewareRelative = `${options.serverDir}/middleware/${NITRO_MIDDLEWARE_FILENAME}`;
|
|
2053
2086
|
const middlewarePath = join(root, middlewareRelative);
|
|
2054
|
-
const gitignoreLine = middlewareRelative;
|
|
2055
2087
|
const dispatchPath = frameworkTriggersDispatchPath(root);
|
|
2056
2088
|
const ensureFile = () => {
|
|
2057
2089
|
mkdirSync(dirname(middlewarePath), { recursive: true });
|
|
2058
2090
|
writeFileSync(middlewarePath, nitroMiddlewareSource(dispatchPath));
|
|
2059
2091
|
};
|
|
2060
|
-
const ensureGitignore = () => {
|
|
2061
|
-
const gitignorePath = join(root, ".gitignore");
|
|
2062
|
-
let contents = "";
|
|
2063
|
-
if (existsSync(gitignorePath)) {
|
|
2064
|
-
contents = readFileSync(gitignorePath, "utf-8");
|
|
2065
|
-
if (contents.split("\n").some((line) => line.trim() === gitignoreLine)) return;
|
|
2066
|
-
}
|
|
2067
|
-
const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
|
|
2068
|
-
writeFileSync(gitignorePath, `${contents}${sep}${gitignoreLine}\n`);
|
|
2069
|
-
};
|
|
2070
2092
|
const cleanup = () => {
|
|
2071
2093
|
rmSync(middlewarePath, { force: true });
|
|
2072
2094
|
};
|
|
@@ -2075,10 +2097,9 @@ function nitroDevTriggerInjectPlugin(root, options) {
|
|
|
2075
2097
|
apply: "serve",
|
|
2076
2098
|
configureServer(server) {
|
|
2077
2099
|
ensureFile();
|
|
2078
|
-
|
|
2100
|
+
ensureGitignoreLines(root, [middlewareRelative]);
|
|
2079
2101
|
server.httpServer?.once("close", cleanup);
|
|
2080
|
-
|
|
2081
|
-
process.once("exit", onExit);
|
|
2102
|
+
process.once("exit", cleanup);
|
|
2082
2103
|
}
|
|
2083
2104
|
};
|
|
2084
2105
|
}
|
|
@@ -2130,9 +2151,9 @@ export default defineEventHandler(async (event) => {
|
|
|
2130
2151
|
* some routes out of Nitro), so Nitro plugins like nitro-cloudflare-dev never
|
|
2131
2152
|
* run for it and `event.context.cloudflare` is empty there.
|
|
2132
2153
|
*
|
|
2133
|
-
* Workaround: put the actual
|
|
2134
|
-
* — Nitro
|
|
2135
|
-
* to
|
|
2154
|
+
* Workaround: put the actual handlers at `src/server/routes/api/__void/{cron,queue}-trigger.ts`
|
|
2155
|
+
* — Nitro *does* run plugins for routes — and proxy `/__void/{scheduled,queue}`
|
|
2156
|
+
* to `/api/__void/{cron,queue}-trigger` from a small Vite middleware so the
|
|
2136
2157
|
* user-facing URL stays consistent with the other frameworks.
|
|
2137
2158
|
*/
|
|
2138
2159
|
function analogDevTriggerInjectPlugin(root) {
|
|
@@ -2147,16 +2168,6 @@ function analogDevTriggerInjectPlugin(root) {
|
|
|
2147
2168
|
mkdirSync(routeDir, { recursive: true });
|
|
2148
2169
|
for (const { user, path } of routePaths) writeFileSync(path, analogRouteSource(dispatchPath, user));
|
|
2149
2170
|
};
|
|
2150
|
-
const ensureGitignore = () => {
|
|
2151
|
-
const gitignorePath = join(root, ".gitignore");
|
|
2152
|
-
let contents = "";
|
|
2153
|
-
if (existsSync(gitignorePath)) contents = readFileSync(gitignorePath, "utf-8");
|
|
2154
|
-
const existing = new Set(contents.split("\n").map((l) => l.trim()));
|
|
2155
|
-
const missing = gitignoreLines.filter((l) => !existing.has(l));
|
|
2156
|
-
if (missing.length === 0) return;
|
|
2157
|
-
const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
|
|
2158
|
-
writeFileSync(gitignorePath, `${contents}${sep}${missing.join("\n")}\n`);
|
|
2159
|
-
};
|
|
2160
2171
|
const cleanup = () => {
|
|
2161
2172
|
for (const { path } of routePaths) rmSync(path, { force: true });
|
|
2162
2173
|
};
|
|
@@ -2165,7 +2176,7 @@ function analogDevTriggerInjectPlugin(root) {
|
|
|
2165
2176
|
apply: "serve",
|
|
2166
2177
|
configureServer(server) {
|
|
2167
2178
|
ensureFiles();
|
|
2168
|
-
|
|
2179
|
+
ensureGitignoreLines(root, gitignoreLines);
|
|
2169
2180
|
const proxy = async (req, res, next) => {
|
|
2170
2181
|
if (req.method !== "POST") {
|
|
2171
2182
|
next();
|
|
@@ -2177,6 +2188,12 @@ function analogDevTriggerInjectPlugin(root) {
|
|
|
2177
2188
|
next();
|
|
2178
2189
|
return;
|
|
2179
2190
|
}
|
|
2191
|
+
const address = server.httpServer?.address();
|
|
2192
|
+
if (!address || typeof address !== "object") {
|
|
2193
|
+
next();
|
|
2194
|
+
return;
|
|
2195
|
+
}
|
|
2196
|
+
const hostname = address.address === "0.0.0.0" ? "127.0.0.1" : address.address === "::" ? "localhost" : address.address.includes(":") ? `[${address.address}]` : address.address;
|
|
2180
2197
|
try {
|
|
2181
2198
|
const chunks = [];
|
|
2182
2199
|
for await (const chunk of req) chunks.push(chunk);
|
|
@@ -2184,8 +2201,7 @@ function analogDevTriggerInjectPlugin(root) {
|
|
|
2184
2201
|
const headers = new Headers();
|
|
2185
2202
|
for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string") headers.set(key, value);
|
|
2186
2203
|
else if (Array.isArray(value)) headers.set(key, value.join(", "));
|
|
2187
|
-
const
|
|
2188
|
-
const upstream = await fetch(`http://${host}/api/__void/${match.file}`, {
|
|
2204
|
+
const upstream = await fetch(`http://${hostname}:${address.port}/api/__void/${match.file}`, {
|
|
2189
2205
|
method: "POST",
|
|
2190
2206
|
headers,
|
|
2191
2207
|
body
|
|
@@ -2199,8 +2215,7 @@ function analogDevTriggerInjectPlugin(root) {
|
|
|
2199
2215
|
};
|
|
2200
2216
|
server.middlewares.use(proxy);
|
|
2201
2217
|
server.httpServer?.once("close", cleanup);
|
|
2202
|
-
|
|
2203
|
-
process.once("exit", onExit);
|
|
2218
|
+
process.once("exit", cleanup);
|
|
2204
2219
|
}
|
|
2205
2220
|
};
|
|
2206
2221
|
}
|
|
@@ -2221,15 +2236,13 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
2221
2236
|
});
|
|
2222
2237
|
`;
|
|
2223
2238
|
}
|
|
2224
|
-
const ASTRO_GITIGNORE_LINE = "src/middleware.ts";
|
|
2225
2239
|
/**
|
|
2226
2240
|
* Writes a tiny Astro middleware to the user's project so
|
|
2227
|
-
*
|
|
2228
|
-
* request pipeline
|
|
2229
|
-
* \`@astrojs/cloudflare\`).
|
|
2241
|
+
* `/__void/scheduled` and `/__void/queue` are intercepted inside Astro's
|
|
2242
|
+
* request pipeline, with bindings from `cloudflare:workers`.
|
|
2230
2243
|
*
|
|
2231
2244
|
* Astro middleware lives at a single discoverable entry, so if the user
|
|
2232
|
-
* already has
|
|
2245
|
+
* already has `src/middleware.ts` we don't overwrite it — instead we log a
|
|
2233
2246
|
* one-shot snippet they can paste into their existing middleware.
|
|
2234
2247
|
*
|
|
2235
2248
|
* If we wrote the file we remove it on dev-server close so the user's
|
|
@@ -2248,17 +2261,6 @@ function astroDevTriggerInjectPlugin(root) {
|
|
|
2248
2261
|
writeFileSync(middlewarePath, astroMiddlewareSource(frameworkTriggersDispatchPath(root)));
|
|
2249
2262
|
wroteFile = true;
|
|
2250
2263
|
};
|
|
2251
|
-
const ensureGitignore = () => {
|
|
2252
|
-
if (!wroteFile) return;
|
|
2253
|
-
const gitignorePath = join(root, ".gitignore");
|
|
2254
|
-
let contents = "";
|
|
2255
|
-
if (existsSync(gitignorePath)) {
|
|
2256
|
-
contents = readFileSync(gitignorePath, "utf-8");
|
|
2257
|
-
if (contents.split("\n").some((line) => line.trim() === ASTRO_GITIGNORE_LINE)) return;
|
|
2258
|
-
}
|
|
2259
|
-
const sep = contents.length === 0 || contents.endsWith("\n") ? "" : "\n";
|
|
2260
|
-
writeFileSync(gitignorePath, `${contents}${sep}${ASTRO_GITIGNORE_LINE}\n`);
|
|
2261
|
-
};
|
|
2262
2264
|
const cleanup = () => {
|
|
2263
2265
|
if (wroteFile) rmSync(middlewarePath, { force: true });
|
|
2264
2266
|
};
|
|
@@ -2267,10 +2269,9 @@ function astroDevTriggerInjectPlugin(root) {
|
|
|
2267
2269
|
apply: "serve",
|
|
2268
2270
|
configureServer(server) {
|
|
2269
2271
|
ensureFile();
|
|
2270
|
-
|
|
2272
|
+
if (wroteFile) ensureGitignoreLines(root, [ASTRO_MIDDLEWARE_RELATIVE]);
|
|
2271
2273
|
server.httpServer?.once("close", cleanup);
|
|
2272
|
-
|
|
2273
|
-
process.once("exit", onExit);
|
|
2274
|
+
process.once("exit", cleanup);
|
|
2274
2275
|
}
|
|
2275
2276
|
};
|
|
2276
2277
|
}
|
|
@@ -2382,7 +2383,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
|
|
|
2382
2383
|
lines.push("app.use(__routingRuntime.requestMiddleware);");
|
|
2383
2384
|
lines.push("");
|
|
2384
2385
|
const emitEdgeRules = !managedDispatch;
|
|
2385
|
-
if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true
|
|
2386
|
+
if (emitEdgeRules && options.redirectRules?.length) compileRoutingRulesMiddleware(lines, options.redirectRules, options.dev === true);
|
|
2386
2387
|
if (emitEdgeRules && options.headerRules?.length) compileHeaderMiddleware(lines, options.headerRules);
|
|
2387
2388
|
if (auth) {
|
|
2388
2389
|
lines.push("app.use(async (c, next) => {");
|
|
@@ -2475,20 +2476,26 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
|
|
|
2475
2476
|
}
|
|
2476
2477
|
lines.push("");
|
|
2477
2478
|
lines.push("export async function scheduled(controller, env, ctx) {");
|
|
2479
|
+
lines.push(" const run = async (job, loadJob) => {");
|
|
2480
|
+
lines.push(" try {");
|
|
2481
|
+
lines.push(" const mod = await loadJob();");
|
|
2482
|
+
lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
|
|
2483
|
+
lines.push(" } catch (error) {");
|
|
2484
|
+
lines.push(" console.error(`[void] Scheduled job ${job} failed for cron \"${controller.cron}\":`, error);");
|
|
2485
|
+
lines.push(" return { job, error };");
|
|
2486
|
+
lines.push(" }");
|
|
2487
|
+
lines.push(" };");
|
|
2478
2488
|
lines.push(" const tasks = [];");
|
|
2479
2489
|
for (let i = 0; i < scan.jobs.length; i++) {
|
|
2480
2490
|
const job = scan.jobs[i];
|
|
2481
2491
|
const filePath = JSON.stringify(`${jobsDir}/${job.filePath}`);
|
|
2482
|
-
|
|
2483
|
-
lines.push(
|
|
2484
|
-
lines.push(` const mod = await import(${filePath});`);
|
|
2485
|
-
lines.push(" if (typeof mod.default === 'function') await mod.default(controller, env, ctx);");
|
|
2486
|
-
lines.push(" })());");
|
|
2487
|
-
lines.push(" }");
|
|
2492
|
+
const label = JSON.stringify(relative(options.projectRoot, `${jobsDir}/${job.filePath}`));
|
|
2493
|
+
lines.push(` if (job${i}Crons.includes(controller.cron)) tasks.push(run(${label}, () => import(${filePath})));`);
|
|
2488
2494
|
}
|
|
2489
|
-
lines.push(" const
|
|
2490
|
-
lines.push("
|
|
2491
|
-
lines.push("
|
|
2495
|
+
lines.push(" const failures = (await Promise.all(tasks)).filter(Boolean);");
|
|
2496
|
+
lines.push(" if (failures.length) {");
|
|
2497
|
+
lines.push(" throw new AggregateError(failures.map((failure) => failure.error), `Scheduled jobs failed: ${failures.map((failure) => failure.job).join(\", \")}`);");
|
|
2498
|
+
lines.push(" }");
|
|
2492
2499
|
lines.push("}");
|
|
2493
2500
|
lines.push("");
|
|
2494
2501
|
} else {
|
|
@@ -2500,7 +2507,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
|
|
|
2500
2507
|
lines.push(" const __token = c.req.header(\"x-void-internal\");");
|
|
2501
2508
|
lines.push(" const __expected = __getRawEnv()?.__VOID_PROXY_TOKEN;");
|
|
2502
2509
|
lines.push(" const __devToken = c.req.header(\"x-void-dev-trigger\");");
|
|
2503
|
-
lines.push(" if (
|
|
2510
|
+
lines.push(" if (__VOID_SERVE ? __devToken !== __VOID_DEV_TRIGGER_TOKEN : __expected ? __token !== __expected : __token !== __VOID_BUILD_KEY) {");
|
|
2504
2511
|
lines.push(" return c.json({ error: \"unauthorized\" }, 401);");
|
|
2505
2512
|
lines.push(" }");
|
|
2506
2513
|
lines.push(" const { cron, scheduledTime } = await c.req.json();");
|
|
@@ -2556,7 +2563,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
|
|
|
2556
2563
|
lines.push(" const __token = c.req.header(\"x-void-internal\");");
|
|
2557
2564
|
lines.push(" const __expected = __getRawEnv()?.__VOID_PROXY_TOKEN;");
|
|
2558
2565
|
lines.push(" const __devToken = c.req.header(\"x-void-dev-trigger\");");
|
|
2559
|
-
lines.push(" if (
|
|
2566
|
+
lines.push(" if (__VOID_SERVE ? __devToken !== __VOID_DEV_TRIGGER_TOKEN : __expected ? __token !== __expected : __token !== __VOID_BUILD_KEY) {");
|
|
2560
2567
|
lines.push(" return c.json({ error: \"unauthorized\" }, 401);");
|
|
2561
2568
|
lines.push(" }");
|
|
2562
2569
|
lines.push(" const payload = await c.req.json();");
|
|
@@ -2740,6 +2747,7 @@ function compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, s
|
|
|
2740
2747
|
lines.push("function __getRawEnv() { return __rawEnvStore.getStore(); }");
|
|
2741
2748
|
lines.push(`export const __VOID_BUILD_KEY = ${JSON.stringify(crypto.randomUUID())};`);
|
|
2742
2749
|
lines.push(`export const __VOID_DEV_TRIGGER_TOKEN = ${JSON.stringify(options.devTriggerToken ?? crypto.randomUUID())};`);
|
|
2750
|
+
lines.push(`const __VOID_SERVE = ${JSON.stringify(options.dev === true)};`);
|
|
2743
2751
|
lines.push(`export const __VOID_ISR_REVALIDATE = ${JSON.stringify(mergedRevalidate)};`);
|
|
2744
2752
|
lines.push(`export const __VOID_ISR_PAGES = ${JSON.stringify(isrPages)};`);
|
|
2745
2753
|
lines.push(`export const __VOID_ISR_PATH_REVALIDATE = ${JSON.stringify(isrPathRevalidate)};`);
|
|
@@ -2888,7 +2896,10 @@ function compilePageRoutes(lines, scan, pages, options) {
|
|
|
2888
2896
|
}
|
|
2889
2897
|
const renderer = page.renderMode === "island" ? "renderIslandPage" : "renderPage";
|
|
2890
2898
|
const assetTags = page.renderMode === "island" && pages.islandClientEntryId ? "getIslandsAssetTags" : "getPagesAssetTags";
|
|
2891
|
-
const routeOptions = `{ ${serverImportPath ? "load: _pload_" + safeId + ", " : ""}${mdHeadRef ? "head: " + mdHeadRef + ", " : ""}componentId: ${JSON.stringify(page.componentId)}, render: ${renderer}, assets: ${assetTags}, headConfig: __voidHeadConfig, renderOptions: ${JSON.stringify({
|
|
2899
|
+
const routeOptions = `{ ${serverImportPath ? "load: _pload_" + safeId + ", " : ""}${mdHeadRef ? "head: " + mdHeadRef + ", " : ""}componentId: ${JSON.stringify(page.componentId)}, render: ${renderer}, assets: ${assetTags}, headConfig: __voidHeadConfig, renderOptions: ${JSON.stringify({
|
|
2900
|
+
renderMode: page.renderMode,
|
|
2901
|
+
...options.dev === true ? { dev: true } : {}
|
|
2902
|
+
})} }`;
|
|
2892
2903
|
lines.push(`app.get(${JSON.stringify(page.pattern)}, __createPageGetHandler(${routeOptions}));`);
|
|
2893
2904
|
if (page.methods.includes("action") || page.methods.includes("actions")) lines.push(`app.on(["POST", "PUT", "PATCH", "DELETE"], ${JSON.stringify(page.pattern)}, __createPageMutationHandler({ ...${routeOptions}, namedActions: ${JSON.stringify(page.methods.includes("actions"))} }));`);
|
|
2894
2905
|
}
|
|
@@ -2919,26 +2930,25 @@ const serveWithAssets = __createAssetFallback(${JSON.stringify({
|
|
|
2919
2930
|
})});
|
|
2920
2931
|
`;
|
|
2921
2932
|
}
|
|
2933
|
+
function deriveEmailRuntimePath(runtimeEnvPath) {
|
|
2934
|
+
return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
|
|
2935
|
+
}
|
|
2922
2936
|
/**
|
|
2923
2937
|
* Generate the Worker entry module code.
|
|
2924
2938
|
* Re-exports the Hono app from the virtual module with an asset-serving
|
|
2925
2939
|
* fallthrough: when the app returns 404, the entry tries env.ASSETS.fetch()
|
|
2926
2940
|
* and adds proper Cache-Control headers (immutable for Vite-hashed /assets/*).
|
|
2927
2941
|
*
|
|
2928
|
-
* When `hasQueues` is true, also exports a `queue()`
|
|
2929
|
-
*
|
|
2930
|
-
* `/__void/queue`
|
|
2931
|
-
* natively to this export; in production the relay worker does it.
|
|
2942
|
+
* When `hasQueues` is true, also exports a `queue()` method that passes the
|
|
2943
|
+
* native MessageBatch to `__processQueue` and applies its ack/retry
|
|
2944
|
+
* decisions. Managed delivery uses the `/__void/queue` route instead.
|
|
2932
2945
|
*
|
|
2933
|
-
* When `
|
|
2946
|
+
* When `remote` is provided, wraps the fetch handler to detect
|
|
2934
2947
|
* remote mode (`__VOID_REMOTE`) and replace local bindings with
|
|
2935
2948
|
* proxy-backed versions.
|
|
2936
2949
|
*/
|
|
2937
|
-
function deriveEmailRuntimePath(runtimeEnvPath) {
|
|
2938
|
-
return runtimeEnvPath.replace(/_?env(\.[^/]+)?$/, (_m, ext) => "email" + (ext ?? ""));
|
|
2939
|
-
}
|
|
2940
2950
|
function compileWorkerEntry(options = {}) {
|
|
2941
|
-
const { hasQueues = false, versionMetadataBinding, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", runtimeEmailPath, envSchemaModule, sandbox, serveSpaFallback = false, hasEmail = false, live, isr, notFound = "none", durableObjects = [] } = options;
|
|
2951
|
+
const { hasQueues = false, versionMetadataBinding, remote, assetsDir = "assets", webSocketClasses = [], runtimeEnvPath = "void/_env", runtimeEmailPath, envSchemaModule, sandbox, serveSpaFallback = false, hasEmail = false, live, dev = false, isr, notFound = "none", durableObjects = [] } = options;
|
|
2942
2952
|
const remotePath = remote?.remotePath;
|
|
2943
2953
|
const remoteBindingNames = JSON.stringify(remote?.bindingNames);
|
|
2944
2954
|
const createRemoteEnvCall = `createRemoteEnv(env${remote?.bindingNames ? `, ${remoteBindingNames}` : ""})`;
|
|
@@ -2953,8 +2963,10 @@ function compileWorkerEntry(options = {}) {
|
|
|
2953
2963
|
const liveBindingName = typeof live === "object" ? live.bindingName ?? "VOID_LIVE" : "VOID_LIVE";
|
|
2954
2964
|
const liveClassName = typeof live === "object" ? live.className ?? "VoidLiveStreamDurableObject" : "VoidLiveStreamDurableObject";
|
|
2955
2965
|
const liveRouteImport = hasLive ? ", VoidLiveStreamDurableObject" : "";
|
|
2956
|
-
const
|
|
2957
|
-
const
|
|
2966
|
+
const liveDev = hasLive && dev === true;
|
|
2967
|
+
const liveRuntimeImport = hasLive ? `import { __setLiveBindingName${liveDev ? ", __setLiveDev" : ""} } from "void/live";\n` : "";
|
|
2968
|
+
const liveDevCall = liveDev ? "__setLiveDev(true);\n" : "";
|
|
2969
|
+
const liveExport = hasLive ? liveClassName === "VoidLiveStreamDurableObject" ? `export { VoidLiveStreamDurableObject };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n${liveDevCall}` : `export { VoidLiveStreamDurableObject as ${liveClassName} };\n__setLiveBindingName(${JSON.stringify(liveBindingName)});\n${liveDevCall}` : "";
|
|
2958
2970
|
const durableObjectImports = durableObjects.map(({ filePath }, index) => `import __voidDurableObject${index} from ${JSON.stringify(filePath)};`).join("\n");
|
|
2959
2971
|
const durableObjectExports = durableObjects.map(({ bindingName, className, name }, index) => `__voidDurableObject${index}.__bind(${JSON.stringify(bindingName)}${name !== void 0 ? `, ${JSON.stringify(name)}` : ""});\nexport const ${className} = __voidDurableObject${index}.class;`).join("\n");
|
|
2960
2972
|
const runtimeEnvImport = `import { withRuntimeEnv } from ${JSON.stringify(runtimeEnvPath)};\n`;
|
|
@@ -3097,7 +3109,7 @@ ${entryClass}`;
|
|
|
3097
3109
|
function compileNodeApp(target, honoPath, envSchemaModule) {
|
|
3098
3110
|
const staticImport = target === "bun" ? `import { serveStatic } from "hono/bun";` : target === "deno" ? `import { serveStatic } from "hono/deno";` : `import { serveStatic } from "@hono/node-server/serve-static";`;
|
|
3099
3111
|
const envSchemaImport = envSchemaModule ? `import ${JSON.stringify(envSchemaModule.importPath)};\nimport { _validateEnvOnce } from ${JSON.stringify(envSchemaModule.helperPath)};\n` : "";
|
|
3100
|
-
const envMiddleware = envSchemaModule ? `app.use("*", async (c, next) => { if (c.req.method
|
|
3112
|
+
const envMiddleware = envSchemaModule ? `app.use("*", async (c, next) => { if (!(c.req.method === "POST" && c.req.path === "/__void/prerender-paths" && c.env?.__VOID_PRERENDER === "1")) await _validateEnvOnce(${target === "deno" ? "Deno.env.toObject()" : "process.env"}); return next(); });\n` : "";
|
|
3101
3113
|
return `${staticImport}
|
|
3102
3114
|
import { fileURLToPath } from "node:url";
|
|
3103
3115
|
import { Hono } from ${JSON.stringify(honoPath)};
|
|
@@ -3110,11 +3122,7 @@ assetApp.get("*", serveStatic({ root: clientRoot }));
|
|
|
3110
3122
|
const assets = { fetch: (request) => assetApp.fetch(request) };
|
|
3111
3123
|
const app = new Hono();
|
|
3112
3124
|
${envMiddleware}app.post("/__void/prerender-paths", async (c) => {
|
|
3113
|
-
|
|
3114
|
-
if (__expected) {
|
|
3115
|
-
const __token = c.req.header("x-void-internal");
|
|
3116
|
-
if (!__token || __token !== __expected) return c.json({ error: "unauthorized" }, 401);
|
|
3117
|
-
}
|
|
3125
|
+
if (c.env?.__VOID_PRERENDER !== "1") return c.notFound();
|
|
3118
3126
|
const paths = await __voidCollectPrerenderPaths(c.req.raw);
|
|
3119
3127
|
return c.json({ paths });
|
|
3120
3128
|
});
|
|
@@ -3144,16 +3152,17 @@ serve({ fetch: app.fetch, port }, (info) => {
|
|
|
3144
3152
|
console.log("Listening on http://localhost:" + info.port);
|
|
3145
3153
|
});`;
|
|
3146
3154
|
}
|
|
3147
|
-
/**
|
|
3148
|
-
* Generate the server-side fetch client module for non-CF targets.
|
|
3149
|
-
* Same as compileWorkerClient but without cloudflare:workers dependency.
|
|
3150
|
-
* Calls app.fetch() directly (no HTTP round-trip) with ALS context forwarding.
|
|
3151
|
-
*/
|
|
3155
|
+
/** Generate the browser client module: re-exports the HTTP fetch, stream, and auth clients. */
|
|
3152
3156
|
function compileBrowserClient(authClientPath, fetchPath, fetchStreamPath) {
|
|
3153
3157
|
return `export { fetch, FetchError } from ${JSON.stringify(fetchPath)};
|
|
3154
3158
|
export { fetchStream, StreamError } from ${JSON.stringify(fetchStreamPath)};
|
|
3155
3159
|
export { auth, createAuthClient } from ${JSON.stringify(authClientPath)};`;
|
|
3156
3160
|
}
|
|
3161
|
+
/**
|
|
3162
|
+
* Generate the server-side fetch client module for non-CF targets.
|
|
3163
|
+
* Same as compileWorkerClient but without cloudflare:workers dependency.
|
|
3164
|
+
* Calls app.fetch() directly (no HTTP round-trip) with ALS context forwarding.
|
|
3165
|
+
*/
|
|
3157
3166
|
function compileNodeClient(authClientPath) {
|
|
3158
3167
|
return `import app, { requestContext } from "virtual:void-routes";
|
|
3159
3168
|
import { interpolateRouteParams } from ${JSON.stringify(ROUTE_URL_PATH)};
|
|
@@ -3220,7 +3229,7 @@ export async function fetch(path, options) {
|
|
|
3220
3229
|
function stripOriginForProd(rules) {
|
|
3221
3230
|
return rules.map(({ origin: _origin, ...rest }) => rest);
|
|
3222
3231
|
}
|
|
3223
|
-
function compileRoutingRulesMiddleware(lines, rules, dev
|
|
3232
|
+
function compileRoutingRulesMiddleware(lines, rules, dev) {
|
|
3224
3233
|
lines.push("// Routing rules: redirects + rewrites (applied before route handlers)");
|
|
3225
3234
|
lines.push(`const __routingRules = ${JSON.stringify(dev ? rules : stripOriginForProd(rules))};`);
|
|
3226
3235
|
lines.push("app.use(\"*\", __routingRuntime.routingMiddleware(__routingRules));");
|
|
@@ -3304,10 +3313,8 @@ export async function fetch(path, options) {
|
|
|
3304
3313
|
* Resolve the server entry module for a Class A framework. We always wrap
|
|
3305
3314
|
* the framework's own entry with a tiny shim that intercepts
|
|
3306
3315
|
* `/__void/{scheduled,queue}` in dev so cron and queue handlers run with
|
|
3307
|
-
* the worker's full CF bindings.
|
|
3308
|
-
* `
|
|
3309
|
-
* `import()` so the entire dev path (including user cron imports) is
|
|
3310
|
-
* dead-code-eliminated in `vite build`. Production deploys go through
|
|
3316
|
+
* the worker's full CF bindings. `vite build` gets a dispatch-free shim
|
|
3317
|
+
* (see `generateClassATriggerWrapper`). Production deploys go through
|
|
3311
3318
|
* `cli/wrapper.ts` instead.
|
|
3312
3319
|
*/
|
|
3313
3320
|
function getFrameworkEntry(framework, root, devTriggerToken, notFound) {
|
|
@@ -3447,18 +3454,16 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
|
|
|
3447
3454
|
if (hasSqlMigrations && dialect !== "sqlite") {
|
|
3448
3455
|
const url = readDatabaseUrl(root);
|
|
3449
3456
|
if (!url) {
|
|
3450
|
-
|
|
3457
|
+
afterServerReady(server, () => {
|
|
3451
3458
|
console.log();
|
|
3452
3459
|
voidError(`DATABASE_URL not found in .env — skipping ${dialect === "mysql" ? "MySQL" : "PostgreSQL"} migrations.`);
|
|
3453
|
-
};
|
|
3454
|
-
if (server.httpServer) server.httpServer.once("listening", () => setTimeout(warn, 100));
|
|
3455
|
-
else warn();
|
|
3460
|
+
});
|
|
3456
3461
|
return;
|
|
3457
3462
|
}
|
|
3458
3463
|
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
3459
|
-
const { validateMigrations } = await import("./validate-
|
|
3464
|
+
const { validateMigrations } = await import("./validate-BJr9LfXy.mjs");
|
|
3460
3465
|
const allMigrations = collectMigrations(migrationsDir);
|
|
3461
|
-
const appliedNames = dialect === "mysql" ? await import("./runner-mysql-
|
|
3466
|
+
const appliedNames = dialect === "mysql" ? await import("./runner-mysql-CkFqZ6CE.mjs").then((runner) => runner.getMySqlAppliedMigrations(url)) : await import("./runner-pg-BGYRFdRp.mjs").then((runner) => runner.getPgAppliedMigrations(url));
|
|
3462
3467
|
const appliedSet = new Set(appliedNames);
|
|
3463
3468
|
const pending = allMigrations.filter((migration) => !appliedSet.has(migration.name));
|
|
3464
3469
|
if (pending.length > 0) try {
|
|
@@ -3466,12 +3471,12 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
|
|
|
3466
3471
|
} catch (err) {
|
|
3467
3472
|
voidWarn(err instanceof Error ? err.message : String(err));
|
|
3468
3473
|
}
|
|
3469
|
-
const applied = dialect === "mysql" ? await import("./runner-mysql-
|
|
3474
|
+
const applied = dialect === "mysql" ? await import("./runner-mysql-CkFqZ6CE.mjs").then((runner) => runner.runMySqlMigrations(migrationsDir, url)) : await import("./runner-pg-BGYRFdRp.mjs").then((runner) => runner.runPgMigrations(migrationsDir, url));
|
|
3470
3475
|
if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
|
|
3471
3476
|
} else if (hasSqlMigrations) {
|
|
3472
|
-
const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-
|
|
3477
|
+
const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-9wjsSZx-.mjs");
|
|
3473
3478
|
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
3474
|
-
const { validateMigrations } = await import("./validate-
|
|
3479
|
+
const { validateMigrations } = await import("./validate-BJr9LfXy.mjs");
|
|
3475
3480
|
const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
|
|
3476
3481
|
const allMigrations = collectMigrations(migrationsDir);
|
|
3477
3482
|
const appliedRows = await getAppliedMigrations(dbPath);
|
|
@@ -3486,7 +3491,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
|
|
|
3486
3491
|
if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
|
|
3487
3492
|
}
|
|
3488
3493
|
if (auth?.enabled) {
|
|
3489
|
-
const { runLocalVoidAuthMigrations } = await import("./node-
|
|
3494
|
+
const { runLocalVoidAuthMigrations } = await import("./node-DJicPvPj.mjs");
|
|
3490
3495
|
await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
|
|
3491
3496
|
voidLog("Applied Better Auth migrations");
|
|
3492
3497
|
}
|
|
@@ -3686,6 +3691,7 @@ function createFrameworkPlugins(input) {
|
|
|
3686
3691
|
}
|
|
3687
3692
|
};
|
|
3688
3693
|
return [
|
|
3694
|
+
devTriggerSecretGuardPlugin(),
|
|
3689
3695
|
createCloudflareBuiltinsExternalPlugin(),
|
|
3690
3696
|
wranglerCompatPlugin,
|
|
3691
3697
|
wranglerSyncPlugin,
|
|
@@ -3886,8 +3892,7 @@ function prerenderPlugin(root, config) {
|
|
|
3886
3892
|
workerDir,
|
|
3887
3893
|
clientDir,
|
|
3888
3894
|
persistDir,
|
|
3889
|
-
remoteMode
|
|
3890
|
-
projectId: process.env.VOID_DEPLOY_PROJECT_ID || void 0
|
|
3895
|
+
remoteMode
|
|
3891
3896
|
});
|
|
3892
3897
|
}
|
|
3893
3898
|
if (result.paths.length === 0) {
|
|
@@ -3901,7 +3906,7 @@ function prerenderPlugin(root, config) {
|
|
|
3901
3906
|
//#endregion
|
|
3902
3907
|
//#region src/plugin-restart.ts
|
|
3903
3908
|
const BINDING_HINT_RESOLVED = "\0virtual:void/binding-hint";
|
|
3904
|
-
const
|
|
3909
|
+
const RESTARTING_HTML_HEAD = `<!DOCTYPE html>
|
|
3905
3910
|
<html lang="en">
|
|
3906
3911
|
<head>
|
|
3907
3912
|
<meta charset="UTF-8">
|
|
@@ -3919,24 +3924,87 @@ const RESTARTING_HTML = `<!DOCTYPE html>
|
|
|
3919
3924
|
<body>
|
|
3920
3925
|
<div><span class="tag">[void]</span> Restarting dev server<span class="dots"><span>.</span><span>.</span><span>.</span></span></div>
|
|
3921
3926
|
<script>
|
|
3922
|
-
|
|
3927
|
+
var target=`;
|
|
3928
|
+
const RESTARTING_HTML_MIDDLE = `;
|
|
3929
|
+
var token=`;
|
|
3930
|
+
const RESTARTING_HTML_TAIL = `;
|
|
3931
|
+
fetch("/__void/restart",{method:"POST",headers:{"x-void-restart":token}}).catch(function(){});
|
|
3923
3932
|
setTimeout(function p(){
|
|
3924
3933
|
fetch("/__void/ping").then(function(r){
|
|
3925
|
-
if(r.ok)location.href=
|
|
3934
|
+
if(r.ok)location.href=target;
|
|
3926
3935
|
else setTimeout(p,150);
|
|
3927
3936
|
}).catch(function(){setTimeout(p,150)});
|
|
3928
3937
|
},300);
|
|
3929
3938
|
<\/script>
|
|
3930
3939
|
</body>
|
|
3931
3940
|
</html>`;
|
|
3932
|
-
const
|
|
3941
|
+
const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
3942
|
+
/**
|
|
3943
|
+
* Returns `value` when it is a path on the dev server's own origin, else `/`.
|
|
3944
|
+
* The restarting page navigates to it, so a scheme (`javascript:`) or a
|
|
3945
|
+
* protocol-relative form (`//host`, `/\host`) must never get through.
|
|
3946
|
+
*/
|
|
3947
|
+
function sanitizeReturnPath(value) {
|
|
3948
|
+
if (!value || value[0] !== "/" || value[1] === "/" || value[1] === "\\") return "/";
|
|
3949
|
+
if (CONTROL_CHARS.test(value)) return "/";
|
|
3950
|
+
const base = "http://void.invalid";
|
|
3951
|
+
try {
|
|
3952
|
+
if (new URL(value, base).origin !== base) return "/";
|
|
3953
|
+
} catch {
|
|
3954
|
+
return "/";
|
|
3955
|
+
}
|
|
3956
|
+
return value;
|
|
3957
|
+
}
|
|
3958
|
+
/**
|
|
3959
|
+
* `token` is the `t` query value the toast navigated with. The page sends it
|
|
3960
|
+
* back with its POST; `/__void/restart` checks it, not this page.
|
|
3961
|
+
*/
|
|
3962
|
+
function renderRestartingPage(returnPath, token) {
|
|
3963
|
+
return RESTARTING_HTML_HEAD + safeJsonStringify(sanitizeReturnPath(returnPath)) + RESTARTING_HTML_MIDDLE + safeJsonStringify(token ?? "") + RESTARTING_HTML_TAIL;
|
|
3964
|
+
}
|
|
3965
|
+
/** Whether `presented` is this server's restart token, compared in constant time. */
|
|
3966
|
+
function isRestartTokenValid(presented, expected) {
|
|
3967
|
+
if (typeof presented !== "string" || expected.length === 0) return false;
|
|
3968
|
+
const a = Buffer.from(presented);
|
|
3969
|
+
const b = Buffer.from(expected);
|
|
3970
|
+
return a.byteLength === b.byteLength && timingSafeEqual(a, b);
|
|
3971
|
+
}
|
|
3972
|
+
/**
|
|
3973
|
+
* Whether a browser sent `req` from the dev server's own origin. Any page the
|
|
3974
|
+
* developer visits can POST to localhost, so cross-site callers are refused.
|
|
3975
|
+
* `Sec-Fetch-Site` is set by the browser and cannot be forged by a page;
|
|
3976
|
+
* `Origin` is the fallback for browsers that do not send it. Clients that send
|
|
3977
|
+
* neither (curl, scripts) are not browsers acting for another site.
|
|
3978
|
+
*/
|
|
3979
|
+
function isSameOriginRequest(req) {
|
|
3980
|
+
const site = req.headers["sec-fetch-site"];
|
|
3981
|
+
if (site !== void 0) return site === "same-origin";
|
|
3982
|
+
const origin = req.headers.origin;
|
|
3983
|
+
if (origin === void 0) return true;
|
|
3984
|
+
const host = req.headers.host;
|
|
3985
|
+
if (!host) return false;
|
|
3986
|
+
return origin === `${req.socket?.encrypted ? "https" : "http"}://${host}`;
|
|
3987
|
+
}
|
|
3988
|
+
/** Whether a browser navigated here from the dev origin itself or from the address bar. */
|
|
3989
|
+
function isSameOriginNavigation(req) {
|
|
3990
|
+
const site = req.headers["sec-fetch-site"];
|
|
3991
|
+
return site === void 0 || site === "same-origin" || site === "none";
|
|
3992
|
+
}
|
|
3993
|
+
/**
|
|
3994
|
+
* The toast is the only in-app way to reach `/__void/restarting`, so it is
|
|
3995
|
+
* the only place that knows the restart token. Vite's default `server.cors`
|
|
3996
|
+
* lets only localhost origins read this module, the same exposure as the
|
|
3997
|
+
* HMR token in `/@vite/client`.
|
|
3998
|
+
*/
|
|
3999
|
+
const clientModule = (token) => `if (import.meta.hot) {
|
|
4000
|
+
const token = ${JSON.stringify(token)};
|
|
3933
4001
|
import.meta.hot.on("void:new-binding", (d) => {
|
|
3934
4002
|
if (document.getElementById("void-binding-hint")) return;
|
|
3935
4003
|
const e = document.createElement("div");
|
|
3936
4004
|
e.id = "void-binding-hint";
|
|
3937
4005
|
e.style.cssText = "position:fixed;top:16px;right:16px;z-index:99999;padding:12px 16px;background:rgba(26,26,46,0.95);color:#e0def4;font:13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;border-radius:8px;border:1px solid rgba(255,255,255,0.08);box-shadow:0 8px 32px rgba(0,0,0,0.4);backdrop-filter:blur(8px);cursor:pointer;max-width:300px;transform:translateX(calc(100% + 16px));transition:transform .3s ease";
|
|
3938
4006
|
e.innerHTML = "<strong style=\\"color:#c4a7e7\\">[void]</strong> " + d.message + " Click to restart.";
|
|
3939
|
-
e.onclick = () => { location.href = "/__void/restarting?return=" + encodeURIComponent(location.pathname + location.search); };
|
|
4007
|
+
e.onclick = () => { location.href = "/__void/restarting?return=" + encodeURIComponent(location.pathname + location.search) + "&t=" + encodeURIComponent(token); };
|
|
3940
4008
|
document.body.appendChild(e);
|
|
3941
4009
|
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
3942
4010
|
e.style.transform = "translateX(0)";
|
|
@@ -3947,8 +4015,10 @@ const CLIENT_MODULE = `if (import.meta.hot) {
|
|
|
3947
4015
|
* Vite plugin that provides controlled dev-server restart via the browser.
|
|
3948
4016
|
*
|
|
3949
4017
|
* Serves three endpoints:
|
|
3950
|
-
* - `/__void/restarting` — standalone loading page (no Vite HMR client)
|
|
3951
|
-
*
|
|
4018
|
+
* - `/__void/restarting` — standalone loading page (no Vite HMR client);
|
|
4019
|
+
* same-origin navigation only
|
|
4020
|
+
* - `/__void/restart` — same-origin POST carrying this server's restart
|
|
4021
|
+
* token triggers `server.restart()` after a short delay
|
|
3952
4022
|
* - `/__void/ping` — health check; returns 503 on the old server instance
|
|
3953
4023
|
* (after restart is triggered) and 200 on the new one
|
|
3954
4024
|
*
|
|
@@ -3956,6 +4026,7 @@ const CLIENT_MODULE = `if (import.meta.hot) {
|
|
|
3956
4026
|
* `void:new-binding` HMR events and shows a toast with a click-to-restart action.
|
|
3957
4027
|
*/
|
|
3958
4028
|
function restartPlugin() {
|
|
4029
|
+
let restartToken = "";
|
|
3959
4030
|
return {
|
|
3960
4031
|
name: "void:restart",
|
|
3961
4032
|
apply: "serve",
|
|
@@ -3968,11 +4039,13 @@ function restartPlugin() {
|
|
|
3968
4039
|
load: {
|
|
3969
4040
|
filter: { id: /^\0virtual:void\/binding-hint$/ },
|
|
3970
4041
|
handler() {
|
|
3971
|
-
return
|
|
4042
|
+
return clientModule(restartToken);
|
|
3972
4043
|
}
|
|
3973
4044
|
},
|
|
3974
4045
|
configureServer(server) {
|
|
3975
4046
|
let restartPending = false;
|
|
4047
|
+
const token = randomBytes(16).toString("base64url");
|
|
4048
|
+
restartToken = token;
|
|
3976
4049
|
server.middlewares.use("/__void/ping", (_req, res) => {
|
|
3977
4050
|
res.writeHead(restartPending ? 503 : 200);
|
|
3978
4051
|
res.end();
|
|
@@ -3983,14 +4056,35 @@ function restartPlugin() {
|
|
|
3983
4056
|
res.end();
|
|
3984
4057
|
return;
|
|
3985
4058
|
}
|
|
4059
|
+
if (!isSameOriginRequest(req) || !isRestartTokenValid(req.headers["x-void-restart"], token)) {
|
|
4060
|
+
res.writeHead(403);
|
|
4061
|
+
res.end();
|
|
4062
|
+
return;
|
|
4063
|
+
}
|
|
3986
4064
|
restartPending = true;
|
|
3987
4065
|
res.writeHead(200);
|
|
3988
4066
|
res.end();
|
|
3989
4067
|
setTimeout(() => server.restart(), 50);
|
|
3990
4068
|
});
|
|
3991
|
-
server.middlewares.use("/__void/restarting", (
|
|
3992
|
-
|
|
3993
|
-
|
|
4069
|
+
server.middlewares.use("/__void/restarting", (req, res) => {
|
|
4070
|
+
if (!isSameOriginNavigation(req)) {
|
|
4071
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
4072
|
+
res.end("Restart the dev server from its own page.");
|
|
4073
|
+
return;
|
|
4074
|
+
}
|
|
4075
|
+
let returnPath = null;
|
|
4076
|
+
let presented = null;
|
|
4077
|
+
try {
|
|
4078
|
+
const params = new URL(req.url ?? "/", "http://void.invalid").searchParams;
|
|
4079
|
+
returnPath = params.get("return");
|
|
4080
|
+
presented = params.get("t");
|
|
4081
|
+
} catch {}
|
|
4082
|
+
res.writeHead(200, {
|
|
4083
|
+
"Content-Type": "text/html",
|
|
4084
|
+
"Cache-Control": "no-store",
|
|
4085
|
+
"Referrer-Policy": "no-referrer"
|
|
4086
|
+
});
|
|
4087
|
+
res.end(renderRestartingPage(returnPath, presented));
|
|
3994
4088
|
});
|
|
3995
4089
|
}
|
|
3996
4090
|
};
|
|
@@ -4026,9 +4120,16 @@ const PAGE_VIRTUAL_MODULE_IDS = [
|
|
|
4026
4120
|
];
|
|
4027
4121
|
const RESOLVE_ID_FILTER = /^(virtual:void-(routes|client-worker(?::(react|vue|svelte|solid))?|client-browser(?::(react|vue|svelte|solid))?)|void(\/client(\/(react|vue|svelte|solid))?|\/queues)?)$/;
|
|
4028
4122
|
const LOAD_ID_FILTER = /^virtual:void-(routes|client-worker(?::(react|vue|svelte|solid))?|client-browser(?::(react|vue|svelte|solid))?)$/;
|
|
4123
|
+
/**
|
|
4124
|
+
* Vite plugin that provides file-based routing.
|
|
4125
|
+
* - Scans routes/ and middleware/ directories
|
|
4126
|
+
* - Generates virtual modules for the route table and worker entry
|
|
4127
|
+
* - Watches for file changes and triggers HMR
|
|
4128
|
+
*/
|
|
4029
4129
|
function routingPlugin(root, options) {
|
|
4030
4130
|
let scan = null;
|
|
4031
4131
|
let routeEnvironment = "prod";
|
|
4132
|
+
let previewing = false;
|
|
4032
4133
|
let ssr = null;
|
|
4033
4134
|
let authConfigPath = null;
|
|
4034
4135
|
const paths = readProjectPaths(root);
|
|
@@ -4264,24 +4365,48 @@ function routingPlugin(root, options) {
|
|
|
4264
4365
|
notFound: options.routing?.notFound
|
|
4265
4366
|
});
|
|
4266
4367
|
}
|
|
4368
|
+
const sandboxEntry = options.hasSandbox ? {
|
|
4369
|
+
bindingName: options.sandboxBindingName,
|
|
4370
|
+
className: options.sandboxClassName,
|
|
4371
|
+
instanceType: options.sandboxInstanceType,
|
|
4372
|
+
runtimePath: sandboxRuntimePath
|
|
4373
|
+
} : false;
|
|
4374
|
+
function writeNodeEntries(target) {
|
|
4375
|
+
writeFileIfChanged(join(outDir, "app.ts"), compileNodeApp(target, compileOptions.honoPath, getEnvSchemaModule()));
|
|
4376
|
+
writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(target));
|
|
4377
|
+
}
|
|
4378
|
+
/** Write the Cloudflare worker entry from the completed scan. */
|
|
4379
|
+
function writeWorkerEntry(isr) {
|
|
4380
|
+
writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
|
|
4381
|
+
versionMetadataBinding: options.getVersionMetadataBinding?.(),
|
|
4382
|
+
hasQueues: scan.queues.length > 0,
|
|
4383
|
+
remote,
|
|
4384
|
+
assetsDir: resolvedConfig?.build.assetsDir,
|
|
4385
|
+
webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
|
|
4386
|
+
runtimeEnvPath: compileOptions.runtimeEnvPath,
|
|
4387
|
+
runtimeEmailPath: compileOptions.runtimeEmailPath,
|
|
4388
|
+
envSchemaModule: getEnvSchemaModule(),
|
|
4389
|
+
sandbox: sandboxEntry,
|
|
4390
|
+
hasEmail: hasEmailHandlers(),
|
|
4391
|
+
serveSpaFallback: shouldServeSpaFallback(),
|
|
4392
|
+
live: options.live,
|
|
4393
|
+
dev: compileOptions.dev,
|
|
4394
|
+
isr,
|
|
4395
|
+
notFound: currentNotFoundHandling(),
|
|
4396
|
+
durableObjects: options.durableObjects
|
|
4397
|
+
}));
|
|
4398
|
+
}
|
|
4267
4399
|
const outDir = join(root, ".void");
|
|
4268
4400
|
mkdirSync(outDir, { recursive: true });
|
|
4269
|
-
if (options.target)
|
|
4270
|
-
|
|
4271
|
-
writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
|
|
4272
|
-
} else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
|
|
4401
|
+
if (options.target) writeNodeEntries(options.target);
|
|
4402
|
+
else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
|
|
4273
4403
|
versionMetadataBinding: options.getVersionMetadataBinding?.(),
|
|
4274
4404
|
remote,
|
|
4275
4405
|
webSocketClasses: options.websocketTombstones,
|
|
4276
4406
|
runtimeEnvPath: compileOptions.runtimeEnvPath,
|
|
4277
4407
|
runtimeEmailPath: compileOptions.runtimeEmailPath,
|
|
4278
4408
|
envSchemaModule: getEnvSchemaModule(),
|
|
4279
|
-
sandbox:
|
|
4280
|
-
bindingName: options.sandboxBindingName,
|
|
4281
|
-
className: options.sandboxClassName,
|
|
4282
|
-
instanceType: options.sandboxInstanceType,
|
|
4283
|
-
runtimePath: sandboxRuntimePath
|
|
4284
|
-
} : false,
|
|
4409
|
+
sandbox: sandboxEntry,
|
|
4285
4410
|
live: options.live,
|
|
4286
4411
|
durableObjects: options.durableObjects
|
|
4287
4412
|
}));
|
|
@@ -4291,7 +4416,7 @@ function routingPlugin(root, options) {
|
|
|
4291
4416
|
configResolved(config) {
|
|
4292
4417
|
resolvedConfig = config;
|
|
4293
4418
|
authClientPath = resolveAuthClientPath(config);
|
|
4294
|
-
compileOptions.dev = config.command === "serve";
|
|
4419
|
+
compileOptions.dev = config.command === "serve" && !previewing;
|
|
4295
4420
|
compileOptions.assetsDir = config.build.assetsDir;
|
|
4296
4421
|
compileOptions.reactRefresh = config.plugins.some((plugin) => plugin.name === "vite:react-babel" || plugin.name === "vite:react-refresh" || plugin.name === "vite:react-swc");
|
|
4297
4422
|
const resolvedPublicDir = config.publicDir && config.publicDir.length > 0 ? config.publicDir : null;
|
|
@@ -4301,6 +4426,7 @@ function routingPlugin(root, options) {
|
|
|
4301
4426
|
}
|
|
4302
4427
|
},
|
|
4303
4428
|
config(_, env) {
|
|
4429
|
+
previewing = env.isPreview === true;
|
|
4304
4430
|
routeEnvironment = env.command === "serve" && !env.isPreview ? "dev" : "prod";
|
|
4305
4431
|
compileOptions.hasLive = options.live !== void 0;
|
|
4306
4432
|
compileOptions.managedDispatch = !options.target && env.command === "build" && Boolean(process.env.VOID_DEPLOY_PROJECT_ID);
|
|
@@ -4345,7 +4471,7 @@ function routingPlugin(root, options) {
|
|
|
4345
4471
|
const migrationsDir = paths.migrationsDir;
|
|
4346
4472
|
if (existsSync(migrationsDir) || compileOptions.auth) {
|
|
4347
4473
|
const { collectMigrations } = await import("./collect-DLyFDiKW.mjs");
|
|
4348
|
-
const { buildMigrationPlanBundle } = await import("./plan-
|
|
4474
|
+
const { buildMigrationPlanBundle } = await import("./plan-CH2G51je.mjs");
|
|
4349
4475
|
const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
|
|
4350
4476
|
const dialect = getDatabaseDialect(readConfig(root));
|
|
4351
4477
|
compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : dialect === "mysql" ? "#self/runtime/migration-handler-mysql" : "#self/runtime/migration-handler");
|
|
@@ -4354,43 +4480,19 @@ function routingPlugin(root, options) {
|
|
|
4354
4480
|
compileOptions.migrationDialect = dialect;
|
|
4355
4481
|
}
|
|
4356
4482
|
}
|
|
4357
|
-
if (options.target)
|
|
4358
|
-
|
|
4359
|
-
writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
|
|
4360
|
-
} else {
|
|
4483
|
+
if (options.target) writeNodeEntries(options.target);
|
|
4484
|
+
else {
|
|
4361
4485
|
const buildConfig = readConfig(root);
|
|
4362
4486
|
const merged = collectRevalidate(buildConfig, scan.pages);
|
|
4363
4487
|
const configuredRevalidate = buildConfig.routing?.revalidate;
|
|
4364
|
-
const emitIsr =
|
|
4488
|
+
const emitIsr = resolvedConfig?.command === "build" && !process.env.VOID_DEPLOY_PROJECT_ID && hasRevalidate(merged);
|
|
4365
4489
|
isrRevalidate = emitIsr ? merged : void 0;
|
|
4366
4490
|
isrPages = emitIsr ? collectPages(scan.pages, configuredRevalidate) : void 0;
|
|
4367
4491
|
isrPathRevalidate = emitIsr && configuredRevalidate && typeof configuredRevalidate === "object" ? configuredRevalidate : void 0;
|
|
4368
|
-
|
|
4492
|
+
writeWorkerEntry(emitIsr ? {
|
|
4369
4493
|
runtimePath: resolveFile("@void/isr/runtime"),
|
|
4370
4494
|
queryAllowlist: buildConfig.routing?.revalidateQueryAllowlist
|
|
4371
|
-
} : void 0;
|
|
4372
|
-
writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
|
|
4373
|
-
versionMetadataBinding: options.getVersionMetadataBinding?.(),
|
|
4374
|
-
hasQueues: scan.queues.length > 0,
|
|
4375
|
-
remote,
|
|
4376
|
-
assetsDir: resolvedConfig?.build.assetsDir,
|
|
4377
|
-
webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
|
|
4378
|
-
runtimeEnvPath: compileOptions.runtimeEnvPath,
|
|
4379
|
-
runtimeEmailPath: compileOptions.runtimeEmailPath,
|
|
4380
|
-
envSchemaModule: getEnvSchemaModule(),
|
|
4381
|
-
sandbox: options.hasSandbox ? {
|
|
4382
|
-
bindingName: options.sandboxBindingName,
|
|
4383
|
-
className: options.sandboxClassName,
|
|
4384
|
-
instanceType: options.sandboxInstanceType,
|
|
4385
|
-
runtimePath: sandboxRuntimePath
|
|
4386
|
-
} : false,
|
|
4387
|
-
hasEmail: hasEmailHandlers(),
|
|
4388
|
-
serveSpaFallback: shouldServeSpaFallback(),
|
|
4389
|
-
live: options.live,
|
|
4390
|
-
isr,
|
|
4391
|
-
notFound: currentNotFoundHandling(),
|
|
4392
|
-
durableObjects: options.durableObjects
|
|
4393
|
-
}));
|
|
4495
|
+
} : void 0);
|
|
4394
4496
|
}
|
|
4395
4497
|
},
|
|
4396
4498
|
resolveId: {
|
|
@@ -4414,6 +4516,7 @@ function routingPlugin(root, options) {
|
|
|
4414
4516
|
return compileWorkerClient(getClientAuthPath(id));
|
|
4415
4517
|
}
|
|
4416
4518
|
if (id !== VIRTUAL_ROUTES_ID) return;
|
|
4519
|
+
if (this?.environment?.name === "client") throw new Error(`${VIRTUAL_ROUTES_ID} is server-only and cannot be loaded by client code.`);
|
|
4417
4520
|
if (!scan) await rescan();
|
|
4418
4521
|
return compileRouteTable(scan, routesDir, middlewareDir, jobsDir, queuesDir, ssr, compileOptions, getPagesCompileOptions(), isrRevalidate, isrPages, isrPathRevalidate);
|
|
4419
4522
|
}
|
|
@@ -4423,7 +4526,7 @@ function routingPlugin(root, options) {
|
|
|
4423
4526
|
const handleFileChange = async (path, event) => {
|
|
4424
4527
|
const normalizedPath = normalizePath(path);
|
|
4425
4528
|
const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedEmailDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
|
|
4426
|
-
if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir)
|
|
4529
|
+
if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir)) return;
|
|
4427
4530
|
if (!sourceConventionChanged && isInDirectory(normalizedPath, normalizedSsrDir) && !ssrEntryAbsPaths.has(normalizedPath)) return;
|
|
4428
4531
|
await rescan(true);
|
|
4429
4532
|
resolveAuthCompileOptions();
|
|
@@ -4434,31 +4537,8 @@ function routingPlugin(root, options) {
|
|
|
4434
4537
|
await writeAuthDatabaseFiles();
|
|
4435
4538
|
writeRouteTypes();
|
|
4436
4539
|
writeQueueTypes();
|
|
4437
|
-
if (options.target)
|
|
4438
|
-
|
|
4439
|
-
writeFileIfChanged(join(outDir, "index.ts"), compileNodeServer(options.target));
|
|
4440
|
-
} else writeFileIfChanged(join(outDir, "entry.ts"), compileWorkerEntry({
|
|
4441
|
-
versionMetadataBinding: options.getVersionMetadataBinding?.(),
|
|
4442
|
-
hasQueues: scan.queues.length > 0,
|
|
4443
|
-
remote,
|
|
4444
|
-
assetsDir: resolvedConfig?.build.assetsDir,
|
|
4445
|
-
webSocketClasses: [...(scan.websockets ?? []).map((route) => route.className), ...options.websocketTombstones ?? []],
|
|
4446
|
-
runtimeEnvPath: compileOptions.runtimeEnvPath,
|
|
4447
|
-
runtimeEmailPath: compileOptions.runtimeEmailPath,
|
|
4448
|
-
envSchemaModule: getEnvSchemaModule(),
|
|
4449
|
-
sandbox: options.hasSandbox ? {
|
|
4450
|
-
bindingName: options.sandboxBindingName,
|
|
4451
|
-
className: options.sandboxClassName,
|
|
4452
|
-
instanceType: options.sandboxInstanceType,
|
|
4453
|
-
runtimePath: sandboxRuntimePath
|
|
4454
|
-
} : false,
|
|
4455
|
-
hasEmail: hasEmailHandlers(),
|
|
4456
|
-
serveSpaFallback: shouldServeSpaFallback(),
|
|
4457
|
-
live: options.live,
|
|
4458
|
-
isr: void 0,
|
|
4459
|
-
notFound: currentNotFoundHandling(),
|
|
4460
|
-
durableObjects: options.durableObjects
|
|
4461
|
-
}));
|
|
4540
|
+
if (options.target) writeNodeEntries(options.target);
|
|
4541
|
+
else writeWorkerEntry(void 0);
|
|
4462
4542
|
const structural = event !== "change";
|
|
4463
4543
|
const pageManifestChanged = isInDirectory(normalizedPath, normalizedPagesDir) && (structural || normalizedPath.endsWith(".md"));
|
|
4464
4544
|
for (const env of Object.values(devServer.environments)) {
|
|
@@ -4585,6 +4665,15 @@ function shouldInjectEmailDevInboxVar(routeEnvironment, needsEmail, assetsBindin
|
|
|
4585
4665
|
}
|
|
4586
4666
|
//#endregion
|
|
4587
4667
|
//#region src/plugin-worker-bindings.ts
|
|
4668
|
+
/** Durable Object bindings already in the patch, else in the resolved config. */
|
|
4669
|
+
function currentDurableObjectBindings(result, resolved) {
|
|
4670
|
+
return (result.durable_objects ?? resolved.durable_objects)?.bindings ?? [];
|
|
4671
|
+
}
|
|
4672
|
+
/** Migrations already in the patch, else in the resolved config. */
|
|
4673
|
+
function currentMigrations(result, resolved) {
|
|
4674
|
+
const current = result.migrations ?? resolved.migrations;
|
|
4675
|
+
return Array.isArray(current) ? current : [];
|
|
4676
|
+
}
|
|
4588
4677
|
function mergeWorkerBindings(resolved, input) {
|
|
4589
4678
|
const { websocketRoutes, routeEnvironment, workerConfig, envSchemaFacts, authEnabled, viteCommand, bindingNames, dialect, application, bindings, directCloudflareDestination, config, devTriggerToken, queues, durableObjects, sandboxConfig, shouldEmitLive, liveConfig, root, remoteMode } = input;
|
|
4590
4679
|
const environmentWebsocketRoutes = websocketRoutes.filter((route) => isRouteEnabledForEnvironment(route, routeEnvironment));
|
|
@@ -4691,12 +4780,12 @@ function mergeWorkerBindings(resolved, input) {
|
|
|
4691
4780
|
class_name: route.className
|
|
4692
4781
|
}));
|
|
4693
4782
|
if (missingDurableObjectBindings.length > 0) result.durable_objects = { bindings: [...existingDurableObjectBindings, ...missingDurableObjectBindings] };
|
|
4694
|
-
const
|
|
4695
|
-
const planned = appendSqliteClassMigration(Array.isArray(
|
|
4783
|
+
const resolvedMigrations = resolved.migrations;
|
|
4784
|
+
const planned = appendSqliteClassMigration(Array.isArray(resolvedMigrations) ? resolvedMigrations : [], environmentWebsocketRoutes.map((route) => route.className), "void-ws-v1", existingDurableObjectBindings.map((binding) => binding.class_name));
|
|
4696
4785
|
if (planned.added) result.migrations = planned.migrations;
|
|
4697
4786
|
}
|
|
4698
4787
|
if (durableObjects.length > 0) {
|
|
4699
|
-
const existingBindings = (result
|
|
4788
|
+
const existingBindings = currentDurableObjectBindings(result, resolved);
|
|
4700
4789
|
for (const module of durableObjects) {
|
|
4701
4790
|
const conflict = existingBindings.find((binding) => binding.name === module.bindingName && binding.class_name !== module.className);
|
|
4702
4791
|
if (conflict) throw new Error(`durable: binding '${module.bindingName}' is already configured for class '${conflict.class_name}', but durable-objects/ requires '${module.className}'.`);
|
|
@@ -4706,8 +4795,7 @@ function mergeWorkerBindings(resolved, input) {
|
|
|
4706
4795
|
class_name: module.className
|
|
4707
4796
|
}));
|
|
4708
4797
|
if (additions.length > 0) result.durable_objects = { bindings: [...existingBindings, ...additions] };
|
|
4709
|
-
const
|
|
4710
|
-
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4798
|
+
const existingMigrations = currentMigrations(result, resolved);
|
|
4711
4799
|
for (const module of durableObjects) if (existingMigrations.find((migration) => migration.tag === module.migrationTag && !migration.new_sqlite_classes?.includes(module.className))) throw new Error(`durable: migration tag '${module.migrationTag}' is already used by another Durable Object migration.`);
|
|
4712
4800
|
const migrationAdditions = durableObjects.filter((module) => !existingMigrations.some((migration) => migration.tag === module.migrationTag || migration.new_sqlite_classes?.includes(module.className))).map((module) => ({
|
|
4713
4801
|
tag: module.migrationTag,
|
|
@@ -4720,28 +4808,26 @@ function mergeWorkerBindings(resolved, input) {
|
|
|
4720
4808
|
...resolved,
|
|
4721
4809
|
...result
|
|
4722
4810
|
}, sandboxConfig);
|
|
4723
|
-
const existingDurableObjectBindings = (result
|
|
4811
|
+
const existingDurableObjectBindings = currentDurableObjectBindings(result, resolved);
|
|
4724
4812
|
if (!existingDurableObjectBindings.some((binding) => binding.name === sandboxConfig.binding)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
|
|
4725
4813
|
name: sandboxConfig.binding,
|
|
4726
4814
|
class_name: sandboxConfig.className
|
|
4727
4815
|
}] };
|
|
4728
4816
|
result.containers = [...(result.containers ?? resolved.containers ?? []).filter((container) => container.class_name !== sandboxConfig.className), sandboxContainerConfig(sandboxConfig)];
|
|
4729
4817
|
resolved.containers = [];
|
|
4730
|
-
const
|
|
4731
|
-
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4818
|
+
const existingMigrations = currentMigrations(result, resolved);
|
|
4732
4819
|
if (!hasSandboxSqliteMigration(existingMigrations, sandboxConfig.className)) result.migrations = [...existingMigrations, {
|
|
4733
4820
|
tag: SANDBOX_MIGRATION_TAG,
|
|
4734
4821
|
new_sqlite_classes: [sandboxConfig.className]
|
|
4735
4822
|
}];
|
|
4736
4823
|
}
|
|
4737
4824
|
if (shouldEmitLive() && liveConfig) {
|
|
4738
|
-
const existingDurableObjectBindings = (result
|
|
4825
|
+
const existingDurableObjectBindings = currentDurableObjectBindings(result, resolved);
|
|
4739
4826
|
if (!existingDurableObjectBindings.some((binding) => binding.name === liveConfig.bindingName)) result.durable_objects = { bindings: [...existingDurableObjectBindings, {
|
|
4740
4827
|
name: liveConfig.bindingName,
|
|
4741
4828
|
class_name: liveConfig.className
|
|
4742
4829
|
}] };
|
|
4743
|
-
const
|
|
4744
|
-
const existingMigrations = Array.isArray(currentMigrations) ? currentMigrations : [];
|
|
4830
|
+
const existingMigrations = currentMigrations(result, resolved);
|
|
4745
4831
|
if (!hasDurableObjectSqliteMigration(existingMigrations, liveConfig.className)) result.migrations = [...existingMigrations, {
|
|
4746
4832
|
tag: nextDurableObjectMigrationTag(existingMigrations, LIVE_MIGRATION_TAG),
|
|
4747
4833
|
new_sqlite_classes: [liveConfig.className]
|
|
@@ -4801,6 +4887,8 @@ function mergeWorkerBindings(resolved, input) {
|
|
|
4801
4887
|
}
|
|
4802
4888
|
//#endregion
|
|
4803
4889
|
//#region src/index.ts
|
|
4890
|
+
const scheduledPointerCleanups = /* @__PURE__ */ new Set();
|
|
4891
|
+
const scheduledPreviewPointerCleanups = /* @__PURE__ */ new Set();
|
|
4804
4892
|
/**
|
|
4805
4893
|
* Void plugin — the main entry point.
|
|
4806
4894
|
*
|
|
@@ -4818,8 +4906,6 @@ function mergeWorkerBindings(resolved, input) {
|
|
|
4818
4906
|
* that are missing. This allows `vite build && wrangler deploy` to work
|
|
4819
4907
|
* for deploying directly to Cloudflare without the Void platform.
|
|
4820
4908
|
*/
|
|
4821
|
-
const scheduledPointerCleanups = /* @__PURE__ */ new Set();
|
|
4822
|
-
const scheduledPreviewPointerCleanups = /* @__PURE__ */ new Set();
|
|
4823
4909
|
function voidPlugin(options) {
|
|
4824
4910
|
const root = process.cwd();
|
|
4825
4911
|
const directCloudflareDestination = readDeploymentPlatform(root) === "cloudflare";
|
|
@@ -5008,10 +5094,8 @@ function voidPlugin(options) {
|
|
|
5008
5094
|
const ignored = ["**/.void/**"];
|
|
5009
5095
|
if (command === "serve") {
|
|
5010
5096
|
const local = loadLocalEnv(root, { onWarn: (message) => voidWarn(message) });
|
|
5011
|
-
const
|
|
5012
|
-
for (const key of /* @__PURE__ */ new Set([...Object.keys(local), ...envSchemaFacts?.keys ?? []])) if (process.env[key] !== void 0)
|
|
5013
|
-
const { sanitized: env, stripped } = stripInternalEnvKeys(devEnv);
|
|
5014
|
-
if (stripped.length > 0) voidWarn(`Ignoring internal env keys from .env: ${stripped.join(", ")}. These are reserved for Void runtime use.`);
|
|
5097
|
+
const env = { ...local };
|
|
5098
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(local), ...envSchemaFacts?.keys ?? []])) if (process.env[key] !== void 0) env[key] = process.env[key];
|
|
5015
5099
|
workerConfig.vars = {
|
|
5016
5100
|
...workerConfig.vars,
|
|
5017
5101
|
...env
|
|
@@ -5167,6 +5251,7 @@ function voidPlugin(options) {
|
|
|
5167
5251
|
createCloudflareBuiltinsExternalPlugin(),
|
|
5168
5252
|
createWorkerEnvironmentSanitizerPlugin([viteEnvironment.name, ...viteEnvironment.childEnvironments ?? []]),
|
|
5169
5253
|
createWranglerCompatPersistPlugin(root, config),
|
|
5254
|
+
devTriggerSecretGuardPlugin(),
|
|
5170
5255
|
restartPlugin(),
|
|
5171
5256
|
bindingWatchPlugin,
|
|
5172
5257
|
...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
|
|
@@ -5215,6 +5300,7 @@ function voidPlugin(options) {
|
|
|
5215
5300
|
if (hasInboundEmail) throw new Error(`email: inbound email handlers (email/) require target: "cloudflare". They are not available with target: '${config.target}'.`);
|
|
5216
5301
|
if (config.routing?.revalidate) voidWarn("\"routing.revalidate\" in void.config.ts is ignored with target: \"" + config.target + "\" (edge caching is CF-only)");
|
|
5217
5302
|
return [
|
|
5303
|
+
devTriggerSecretGuardPlugin(),
|
|
5218
5304
|
restartPlugin(),
|
|
5219
5305
|
envPlugin,
|
|
5220
5306
|
serverRuntimeOptimizerPlugin,
|
|
@@ -5291,6 +5377,7 @@ function voidPlugin(options) {
|
|
|
5291
5377
|
createDefaultWorkerEnvironmentPlugin(),
|
|
5292
5378
|
createWorkerEnvironmentSanitizerPlugin([DEFAULT_WORKER_ENVIRONMENT_NAME]),
|
|
5293
5379
|
createWranglerCompatPersistPlugin(root, config),
|
|
5380
|
+
devTriggerSecretGuardPlugin(),
|
|
5294
5381
|
restartPlugin(),
|
|
5295
5382
|
bindingWatchPlugin,
|
|
5296
5383
|
...bindings.needsEmail ? [emailDevInboxPlugin({ devTriggerToken })] : [],
|