void 0.25.0 → 0.26.1
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-DcR1ywt8.mjs → account-cmd-CfUXh_nU.mjs} +8 -7
- package/dist/{application-analysis-BVsVqc11.mjs → application-analysis-DKy02UY0.mjs} +21 -32
- package/dist/{application-routing-B7QEjqSR.mjs → application-routing-DQyUh_5S.mjs} +1 -1
- package/dist/{auth-7wuZ5ob8.mjs → auth-B5_tZLxT.mjs} +9 -9
- package/dist/{auth-CdgZ9uag.mjs → auth-BgX2qYSY.mjs} +1 -1
- package/dist/{auth-link-Z6E5DYvM.mjs → auth-link-D5RmKnzh.mjs} +5 -5
- package/dist/{auth-migrations-9vif1uj8.mjs → auth-migrations-BwHgeEsD.mjs} +8 -18
- package/dist/{auth-router-BdM4avI8.mjs → auth-router-Bap-pvbn.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-CEE7D-hM.mjs → build-cmd-B58c8iS8.mjs} +8 -7
- package/dist/{cache-TcZnBLtH.mjs → cache-Bo52trKF.mjs} +8 -8
- package/dist/{cancel-deploy-CYoZmcmR.mjs → cancel-deploy-BZNz3Q26.mjs} +6 -5
- package/dist/{cf-access-AHKBzmuu.mjs → cf-access-ngGqZpHZ.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/cli/cli.mjs +48 -83
- package/dist/cli/cloudflare-operation-process.mjs +71 -77
- package/dist/cli/env-schema-probe.d.mts +1 -1
- package/dist/cli/env-schema-probe.mjs +5 -6
- package/dist/client-By4JjMw_.mjs +2 -0
- package/dist/{client-RsPo8wsP.mjs → client-C2Prx_Bi.mjs} +30 -8
- 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-BhbK6yTq.mjs → cloudflare-operations-BlYLlowZ.mjs} +1 -1
- package/dist/{cloudflare-operations-CjaV4lZi.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-DCvUmPXY.mjs → connect-X0MUA1gk.mjs} +12 -11
- package/dist/{create-project-eAPgYq35.mjs → create-project-DxiyW6iX.mjs} +7 -7
- package/dist/{create-project-C734I-Mu.mjs → create-project-iwkbqiBU.mjs} +1 -1
- package/dist/{db-C07GEdKF.mjs → db-XnVMHzoV.mjs} +101 -133
- package/dist/{delete-pKMVsrpJ.mjs → delete-esYlMiSJ.mjs} +8 -8
- package/dist/{deploy-DfsAe-T0.mjs → deploy-BsEvnlJl.mjs} +2377 -2512
- package/dist/deploy-C1d-YTfp.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-D3uA9ize.mjs → domain-CbbBWhWE.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-z3o2cwjH.mjs → email-CpuT42g7.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-D5liuQYR.mjs → env-DMA3cXZt.mjs} +1 -1
- package/dist/{env-public-BX_r8HR6.d.mts → env-public-DR7ZM6d-.d.mts} +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/{env-3Q8-_2IH.mjs → env-w9jOyGZn.mjs} +10 -9
- 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-IvCYKyQL.mjs → fetch-stream-PGgz4uGz.mjs} +26 -24
- package/dist/{fetch-stream-Bjf0hoZb.d.mts → fetch-stream-obvZ7fvO.d.mts} +1 -16
- package/dist/{gen-NjwoG9FK.mjs → gen-DtkQhvQg.mjs} +40 -57
- package/dist/gen-dA9hW7AV.mjs +2 -0
- package/dist/{github-cmd-DquUq-b1.mjs → github-cmd-D_FsOFxq.mjs} +65 -164
- package/dist/{handler-DghKr6dU.mjs → handler-BHAn2fZJ.mjs} +1 -0
- package/dist/{handler-CZ4nAylQ.d.mts → handler-CNAwjRcW.d.mts} +5 -12
- 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-DRZXC4fj.mjs → help-CVFBTmBx.mjs} +23 -38
- package/dist/help-DzKUrQvk.mjs +2 -0
- package/dist/{inbound-fRkqcXaC.mjs → inbound-PfQDLXc5.mjs} +23 -24
- package/dist/index.d.mts +19 -2
- package/dist/index.mjs +632 -532
- package/dist/{info-BmA3_IFZ.mjs → info-PYWwkWyt.mjs} +4 -4
- package/dist/{init-DT7yuuyl.mjs → init-Bw-fRKGp.mjs} +161 -197
- package/dist/{limits-Bq5LG8Id.d.mts → limits-CFsDKQvU.d.mts} +1 -10
- package/dist/{limits-Cjuk2VPm.mjs → limits-D0vD1M9A.mjs} +1 -22
- package/dist/{link-CXOlqh2F.mjs → link-DlPG8iX3.mjs} +10 -10
- package/dist/{list-eEWVJEPn.mjs → list-2GN7_ueV.mjs} +11 -9
- package/dist/{live-CB1y5IuC.mjs → live-CgQuTpqD.mjs} +15 -16
- package/dist/{live-Chw1eIMv.d.mts → live-Du8jXwOW.d.mts} +4 -2
- package/dist/{local-d1-Df4XpwhW.mjs → local-d1-ga5O_idV.mjs} +2 -2
- package/dist/{login-pWQHYVu2.mjs → login-D-Q4xw3Q.mjs} +7 -7
- package/dist/login-Dc-wuTDq.mjs +2 -0
- package/dist/{logs-DnxVX7TR.mjs → logs-rd8J-OFh.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-BkyRWRx8.mjs → node-DJicPvPj.mjs} +5 -5
- package/dist/{operator-args-CAVcmwUt.mjs → operator-args-BNsW5gfp.mjs} +10 -1
- package/dist/{operator-auth-CkmTE3rt.mjs → operator-auth-AzVLbrr3.mjs} +4 -8
- package/dist/{operator-client-LmwjxT6q.mjs → operator-client-BQtzyG-g.mjs} +3 -3
- package/dist/{operator-cmd-d-IEKgNk.mjs → operator-cmd-B6f6Cg9Z.mjs} +45 -14
- package/dist/operator-cmd-Cwn50lOV.mjs +2 -0
- package/dist/{output-BBsUwdyT.mjs → output-C50ag2r3.mjs} +44 -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-NPpwZ_kc.mjs → plan-BhLO2p4w.mjs} +4 -3
- package/dist/plan-CH2G51je.mjs +2 -0
- package/dist/{platform-args-Bzf8JPc2.mjs → platform-args-BoZkWQnJ.mjs} +4 -2
- package/dist/platform-args-DYgITk0D.mjs +2 -0
- package/dist/{platform-auth-config-0V0lA3pF.mjs → platform-auth-config-n603OANg.mjs} +45 -40
- package/dist/{platform-auth-protection-DFeJ-JkK.mjs → platform-auth-protection-zxDr0hWW.mjs} +9 -8
- package/dist/{platform-auth-recovery-ICy4muq6.mjs → platform-auth-recovery-tw9lgtgE.mjs} +8 -7
- package/dist/{platform-cmd-D7vSWMvV.mjs → platform-cmd-B8-bCb7t.mjs} +48 -23
- package/dist/{platform-cmd-BPySFOew.mjs → platform-cmd-C00YM14r.mjs} +1 -1
- package/dist/{platform-discovery-DDDUvnqp.mjs → platform-discovery-BC0HCKYt.mjs} +18 -4
- package/dist/{platform-domain-CUdY064e.mjs → platform-domain-p0kobLV8.mjs} +6 -5
- package/dist/{platform-lifecycle-Dm09JNkn.mjs → platform-lifecycle-D_QEY32D.mjs} +906 -389
- package/dist/{platform-lifecycle-BaUdTRPU.mjs → platform-lifecycle-vFqtNPPa.mjs} +1 -1
- package/dist/{platform-management-BSEc_ZeG.mjs → platform-management-BdYblvvE.mjs} +175 -32
- package/dist/{platform-management-Bp6aoH2C.mjs → platform-management-oi-SvI3f.mjs} +1 -1
- package/dist/{platform-plans-config-B0pOVQyW.mjs → platform-plans-config-CjiFv5J4.mjs} +5 -4
- package/dist/{platform-recovery-WOZ95Uze.mjs → platform-recovery-PvNS9Nl2.mjs} +3 -3
- package/dist/{platform-registry-KfNEF84O.mjs → platform-registry-P387v42A.mjs} +17 -10
- package/dist/platform-setup-DVe42Ws7.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-DnaKBi--.mjs → prepare-Do96654b.mjs} +8 -8
- package/dist/{prepare-39EvOhI6.mjs → prepare-Dq9hTIbo.mjs} +4 -3
- package/dist/{prerender-render-D9qbLs2f.mjs → prerender-render-Ct20jftc.mjs} +17 -3
- package/dist/prerender-render.mjs +1 -1
- package/dist/{project-IOjBU8R6.mjs → project-BiaPQ7ur.mjs} +1 -1
- package/dist/{project-BkbfDs0V.mjs → project-DQstYTxO.mjs} +1 -1
- package/dist/{project-cmd-Bs5A392E.mjs → project-cmd-ti8_yTcA.mjs} +19 -19
- package/dist/{project-team-ohAWWaew.mjs → project-team-CDPE1Tsg.mjs} +15 -18
- package/dist/{project-token-CP01Jna6.mjs → project-token-CkZ1cN0Z.mjs} +14 -17
- package/dist/{project-tsconfig-B8GeM0RM.mjs → project-tsconfig-CVJhNMxs.mjs} +2 -1
- package/dist/{project-zero-trust-BzkVgx35.mjs → project-zero-trust-DdjLR61k.mjs} +12 -9
- package/dist/{protocol-CjF_iI9X.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-Btj1jppf.mjs → provision-BfaLiWbj.mjs} +51 -42
- package/dist/{provision-BEv0k-qz.mjs → provision-Wu9ZBk9L.mjs} +2 -3
- package/dist/{requests-B3PBuGlX.mjs → requests-DFAqyYy8.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-DDub9GsO.mjs → rollback-BKYXBduI.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-Ol0TNjk6.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 +11 -12
- package/dist/runtime/ai.mjs +47 -113
- 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.d.mts +1 -1
- 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/limits.d.mts +2 -2
- package/dist/runtime/limits.mjs +2 -2
- 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 +35 -86
- 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.d.mts +1 -1
- package/dist/runtime/sandbox.mjs +14 -24
- 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/{sandbox-XZAqzFlG.d.mts → sandbox-C5LFoJMf.d.mts} +7 -7
- package/dist/{scan-C7okrLyM.mjs → scan-C3uDzz6J.mjs} +21 -18
- package/dist/{secret-3c72lY1U.mjs → secret-BAt2KMmO.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-Dhom39bd.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-ctOrgiS3.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-CL1w7GXU.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 +69 -64
- package/{skills/void/docs → docs}/guide/app-types.md +1 -1
- 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/database.md +23 -8
- 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 +12 -5
- package/{skills/void/docs → docs}/guide/pages-routing/actions-and-forms.md +21 -9
- 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 +2 -10
- 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/credentials.md +2 -2
- package/{skills/void/docs → docs}/guide/platform/installation/domains.md +3 -1
- package/{skills/void/docs → docs}/guide/platform/installation/first-deployment.md +8 -2
- package/{skills/void/docs → docs}/guide/platform/installation/maintenance.md +7 -1
- package/{skills/void/docs → docs}/guide/platform/installation/setup.md +1 -1
- package/docs/guide/platform/installation/uninstall.md +102 -0
- package/{skills/void/docs → docs}/guide/project-collaboration.md +6 -2
- 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 +41 -22
- 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/api/resources.md +11 -14
- package/{skills/void/docs → docs}/reference/api.md +35 -34
- package/{skills/void/docs → docs}/reference/cli/auth.md +3 -6
- package/{skills/void/docs → docs}/reference/cli/database.md +3 -4
- 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 +3 -3
- package/{skills/void/docs → docs}/reference/cli/platform-email.md +2 -3
- package/{skills/void/docs → docs}/reference/cli/platform-installation.md +15 -13
- 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/platform.md +4 -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 +2 -2
- package/{skills/void/docs → docs}/reference/config.md +13 -13
- package/{skills/void/docs → docs}/reference/resource-inference.md +2 -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 +11 -5
- package/dist/cf-build-output-ChY6-XWP.mjs +0 -2
- package/dist/client-B4Me-9HE.mjs +0 -2
- package/dist/config-0acf1oys.mjs +0 -2
- package/dist/config-D8MOgiQV.mjs +0 -32
- package/dist/deploy-BDu1vhSH.mjs +0 -2
- package/dist/discover-AMCBXivQ.mjs +0 -2
- package/dist/fetch-error-Sp1R4mZv.d.mts +0 -20
- package/dist/gen-ById9r2j.mjs +0 -2
- package/dist/help-DPGBwomV.mjs +0 -2
- package/dist/login-C98-sw79.mjs +0 -2
- package/dist/migrate-CEu2X5DB.mjs +0 -2
- package/dist/plan-D1Q5rf-r.mjs +0 -2
- package/dist/platform-args-Cq6EjQro.mjs +0 -2
- package/dist/prepare-CnjcTV2s.mjs +0 -2
- package/dist/resolve-project-DtbidV4y.mjs +0 -2
- package/dist/route-url-CG7U-cRN.mjs +0 -15
- package/dist/runner-CXA9Fh8h.mjs +0 -2
- package/dist/validate-Dq_L3s0S.mjs +0 -2
- package/skills/void/docs/guide/platform/installation/uninstall.md +0 -69
- 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/database/d1.md +2 -2
- package/{skills/void/docs → docs}/guide/database/mysql.md +0 -0
- package/{skills/void/docs → docs}/guide/deployment.md +0 -0
- 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/prerequisites.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/rewrites.md +0 -0
- /package/{skills/void/docs → docs}/reference/api/types.md +0 -0
- /package/{skills/void/docs → docs}/reference/cli/deploy.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/structure.md +0 -0
|
@@ -34,15 +34,14 @@ import { ai } from 'void/ai';
|
|
|
34
34
|
export const POST = defineHandler(async (c) => {
|
|
35
35
|
const { prompt } = await c.req.json();
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.match({ ok: (result) => c.json(result), limited: (limit) => limit.response() });
|
|
37
|
+
const result = await ai.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
|
|
38
|
+
messages: [{ role: 'user', content: prompt }],
|
|
39
|
+
});
|
|
40
|
+
return c.json(result);
|
|
42
41
|
});
|
|
43
42
|
```
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
AI methods return promises. Await them to receive the result, or return the promise directly from a handler. See [Usage Limits](#usage-limits) for limit errors.
|
|
46
45
|
|
|
47
46
|
You can use any model available through Cloudflare's AI binding, including Workers AI models such as `@cf/meta/llama-3.3-70b-instruct-fp8-fast` and Cloudflare Gateway models such as `google/gemini-2.5-flash` or `openai/gpt-4.1-mini`. The input object must match the selected Cloudflare model's schema. Models that return binary data, such as generated images, are returned as a `Blob` from `ai.run()`.
|
|
48
47
|
|
|
@@ -57,29 +56,23 @@ import { ai } from 'void/ai';
|
|
|
57
56
|
export const POST = defineHandler(async (c) => {
|
|
58
57
|
const { prompt } = await c.req.json();
|
|
59
58
|
|
|
60
|
-
return ai
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
})
|
|
64
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
59
|
+
return ai.stream('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
|
|
60
|
+
messages: [{ role: 'user', content: prompt }],
|
|
61
|
+
});
|
|
65
62
|
});
|
|
66
63
|
```
|
|
67
64
|
|
|
68
|
-
Streaming responses end with a `void:error` SSE event if generation is interrupted after the response starts
|
|
65
|
+
Streaming responses end with a `void:error` SSE event if generation is interrupted after the response starts, or `void:limit` for a usage limit. `fetchStream()` from `void/client` throws `UsageLimit` for limits before or during streaming and `StreamError` for other terminal errors. Catch the error in your UI to stop its generating state, retain partial output, and offer retry. This also applies to streaming provider responses and `ai.run()` with `stream: true`.
|
|
69
66
|
|
|
70
67
|
## Listing Models
|
|
71
68
|
|
|
72
69
|
Use `ai.models()` to list available models:
|
|
73
70
|
|
|
74
71
|
```ts
|
|
75
|
-
const models = await ai
|
|
76
|
-
.models()
|
|
77
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
72
|
+
const models = await ai.models();
|
|
78
73
|
|
|
79
74
|
// Filter by task
|
|
80
|
-
const textModels = await ai
|
|
81
|
-
.models({ task: 'Text Generation' })
|
|
82
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
75
|
+
const textModels = await ai.models({ task: 'Text Generation' });
|
|
83
76
|
```
|
|
84
77
|
|
|
85
78
|
## Markdown Conversion
|
|
@@ -87,9 +80,7 @@ const textModels = await ai
|
|
|
87
80
|
Use `ai.toMarkdown()` to convert documents to markdown:
|
|
88
81
|
|
|
89
82
|
```ts
|
|
90
|
-
const result = await ai
|
|
91
|
-
.toMarkdown([{ name: 'document.pdf', blob: pdfBytes }])
|
|
92
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
83
|
+
const result = await ai.toMarkdown([{ name: 'document.pdf', blob: pdfBytes }]);
|
|
93
84
|
```
|
|
94
85
|
|
|
95
86
|
## Usage Limits
|
|
@@ -100,24 +91,49 @@ requests also use your provider credentials and their billing terms.
|
|
|
100
91
|
|
|
101
92
|
A team platform uses its own Cloudflare account and may apply per-user limits. Ask your administrator about the available allowance.
|
|
102
93
|
|
|
94
|
+
Usage limits reject the operation's promise with `UsageLimit` from `void/limits`. It includes `resource`, `reason`, and an optional `retryAt` timestamp. In Void route handlers and Pages actions, an uncaught limit produces a structured HTTP 429 with retry information. In queues, scheduled jobs, and other server code, it remains a rejected promise.
|
|
95
|
+
|
|
96
|
+
Handling a limit is optional. Use `instanceof UsageLimit` when you want a fallback or a message tailored to your app:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { defineHandler } from 'void';
|
|
100
|
+
import { ai } from 'void/ai';
|
|
101
|
+
import { UsageLimit } from 'void/limits';
|
|
102
|
+
|
|
103
|
+
export const POST = defineHandler(async (c) => {
|
|
104
|
+
const { prompt } = await c.req.json();
|
|
105
|
+
try {
|
|
106
|
+
const result = await ai.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
|
|
107
|
+
messages: [{ role: 'user', content: prompt }],
|
|
108
|
+
});
|
|
109
|
+
return c.json(result);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (error instanceof UsageLimit) {
|
|
112
|
+
return error.response({ message: 'Try again shortly. Your prompt has been kept.' });
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`error.response()` creates the structured HTTP 429 and accepts an optional custom `message`. Other failures reject normally. When using `fetchStream()`, the same `instanceof UsageLimit` check gives your client access to the message and retry information.
|
|
120
|
+
|
|
103
121
|
## Cloudflare Gateway Models
|
|
104
122
|
|
|
105
123
|
`ai.run()` mirrors Cloudflare's `env.AI.run()` model naming and input schemas. Third-party models use Cloudflare model IDs and Cloudflare-managed credentials.
|
|
106
124
|
|
|
107
125
|
```ts
|
|
108
|
-
const result = await ai
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
})
|
|
117
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
126
|
+
const result = await ai.run('google/gemini-2.5-flash', {
|
|
127
|
+
contents: [
|
|
128
|
+
{
|
|
129
|
+
role: 'user',
|
|
130
|
+
parts: [{ text: 'Explain Durable Objects in one paragraph.' }],
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
});
|
|
118
134
|
```
|
|
119
135
|
|
|
120
|
-
Pass AI Gateway options as the third argument: `ai.run(model, inputs, { gateway: { skipCache: true } })`.
|
|
136
|
+
Pass AI Gateway options as the third argument: `ai.run(model, inputs, { gateway: { skipCache: true } })`. On a team platform, the platform chooses the gateway and sets its metadata and logging; options such as `skipCache` and `cacheTtl` still apply.
|
|
121
137
|
|
|
122
138
|
For provider-native requests on a direct deployment, configure [`ai.gateway`](../integrations/cloudflare.md#ai-self-host).
|
|
123
139
|
|
|
@@ -126,7 +142,9 @@ For provider-native requests on a direct deployment, configure [`ai.gateway`](..
|
|
|
126
142
|
Use `ai.provider(provider).fetch(path, init)` for a provider-native API with your
|
|
127
143
|
own provider key. The request goes through the configured Cloudflare AI Gateway
|
|
128
144
|
and retains the provider's native HTTP shape. On a team platform, it also passes
|
|
129
|
-
through that installation's proxy.
|
|
145
|
+
through that installation's proxy. AI Gateway cache, timeout and retry headers,
|
|
146
|
+
such as `cf-aig-cache-ttl` and `cf-aig-max-attempts`, still apply there; the
|
|
147
|
+
platform ignores logging, metadata and cost headers.
|
|
130
148
|
|
|
131
149
|
### OpenAI
|
|
132
150
|
|
|
@@ -137,16 +155,13 @@ import { ai } from 'void/ai';
|
|
|
137
155
|
export const POST = defineHandler(async (c) => {
|
|
138
156
|
const { prompt } = await c.req.json();
|
|
139
157
|
|
|
140
|
-
return ai
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
},
|
|
148
|
-
})
|
|
149
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
158
|
+
return ai.provider('openai').fetch('/chat/completions', {
|
|
159
|
+
body: {
|
|
160
|
+
model: 'gpt-4o',
|
|
161
|
+
messages: [{ role: 'user', content: prompt }],
|
|
162
|
+
max_tokens: 512,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
150
165
|
});
|
|
151
166
|
```
|
|
152
167
|
|
|
@@ -164,8 +179,7 @@ const response = await ai
|
|
|
164
179
|
},
|
|
165
180
|
],
|
|
166
181
|
},
|
|
167
|
-
})
|
|
168
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
182
|
+
});
|
|
169
183
|
```
|
|
170
184
|
|
|
171
185
|
### Custom Providers
|
|
@@ -181,8 +195,7 @@ const response = await ai
|
|
|
181
195
|
})
|
|
182
196
|
.fetch('/v1/respond', {
|
|
183
197
|
body: { prompt: 'Hello' },
|
|
184
|
-
})
|
|
185
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
198
|
+
});
|
|
186
199
|
```
|
|
187
200
|
|
|
188
201
|
### Image Generation
|
|
@@ -192,9 +205,7 @@ Use `ai.run()` or `ai.image()` for Cloudflare-native image models:
|
|
|
192
205
|
```ts
|
|
193
206
|
export const POST = defineHandler(async (c) => {
|
|
194
207
|
const { prompt } = await c.req.json();
|
|
195
|
-
return ai
|
|
196
|
-
.image('@cf/black-forest-labs/flux-1-schnell', { prompt })
|
|
197
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
208
|
+
return ai.image('@cf/black-forest-labs/flux-1-schnell', { prompt });
|
|
198
209
|
});
|
|
199
210
|
```
|
|
200
211
|
|
|
@@ -204,16 +215,13 @@ Use `ai.provider().fetch()` for provider-native image APIs. Match the [provider'
|
|
|
204
215
|
export const POST = defineHandler(async (c) => {
|
|
205
216
|
const { prompt } = await c.req.json();
|
|
206
217
|
|
|
207
|
-
return ai
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
},
|
|
215
|
-
})
|
|
216
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
218
|
+
return ai.provider('openai').fetch('/images/generations', {
|
|
219
|
+
body: {
|
|
220
|
+
model: 'gpt-image-2.5-sunburst',
|
|
221
|
+
prompt,
|
|
222
|
+
size: '1024x1024',
|
|
223
|
+
},
|
|
224
|
+
});
|
|
217
225
|
});
|
|
218
226
|
```
|
|
219
227
|
|
|
@@ -227,10 +235,7 @@ export const POST = defineHandler(async (c) => {
|
|
|
227
235
|
form.set('prompt', String(body.prompt));
|
|
228
236
|
form.set('image', body.image as Blob, 'source.png');
|
|
229
237
|
|
|
230
|
-
return ai
|
|
231
|
-
.provider('openai')
|
|
232
|
-
.fetch('/images/edits', { body: form })
|
|
233
|
-
.match({ ok: (result) => result, limited: (limit) => limit.response() });
|
|
238
|
+
return ai.provider('openai').fetch('/images/edits', { body: form });
|
|
234
239
|
});
|
|
235
240
|
```
|
|
236
241
|
|
|
@@ -45,7 +45,7 @@ Add `voidPlugin()` to the framework's Vite config to get binding inference, type
|
|
|
45
45
|
|
|
46
46
|
Any project that produces static files, whether that is an SPA, a static site, or a plain directory. Void serves the assets without requiring application code.
|
|
47
47
|
|
|
48
|
-
**SPAs** (client-side single-page apps)
|
|
48
|
+
**SPAs** (client-side single-page apps) serve `index.html` with a 200 status for paths that match no file, so client-side routing works out of the box. On a Void platform, missing files under the hashed assets folder (`/assets/`) return 404 instead. Detected when `vite` is a dependency and no backend files exist.
|
|
49
49
|
|
|
50
50
|
**Static sites** (static site generators like VitePress, Docusaurus, or any pre-built directory) produce per-page HTML files. Detected when a known SSG is a dependency, or when using the `--dir` flag.
|
|
51
51
|
|
|
@@ -92,7 +92,7 @@ Email/password is enabled by default. To add social providers:
|
|
|
92
92
|
}
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
Provider credentials come from `AUTH_<PROVIDER>_CLIENT_ID` and `AUTH_<PROVIDER>_CLIENT_SECRET`.
|
|
96
96
|
|
|
97
97
|
See the full provider list in the [config reference](../reference/config.md#auth).
|
|
98
98
|
|
|
@@ -127,6 +127,42 @@ Auth sessions use the same database as your app. Void creates `BETTER_AUTH_SECRE
|
|
|
127
127
|
|
|
128
128
|
Local development needs no secret setup. To preview a production build locally, provide `BETTER_AUTH_SECRET`.
|
|
129
129
|
|
|
130
|
+
`POST`, `PUT`, `PATCH` and `DELETE` requests and WebSocket connections from another origin don't use the signed-in session unless that origin is in `trustedOrigins`. If a separate frontend calls your app, add its origin there (see [Customization](#customization)). If your routes use their own cookies, check the request `Origin` yourself, for example with Hono's [`csrf()`](https://hono.dev/docs/middleware/builtin/csrf) middleware.
|
|
131
|
+
|
|
132
|
+
Deployed apps on Cloudflare limit auth requests per client IP with Better Auth's defaults, for example 3 sign-in attempts per 10 seconds. Each Worker instance keeps its own count. Change or turn off the limits with `rateLimit` in `auth.ts`. To share one count across instances, set `rateLimit: { storage: 'database' }`, then run `void db generate` for a direct Cloudflare deploy.
|
|
133
|
+
|
|
134
|
+
### Proxies and end-to-end tests
|
|
135
|
+
|
|
136
|
+
All requests from one IP share one count. Two common setups reach the limits:
|
|
137
|
+
|
|
138
|
+
- A server calls your auth endpoints for your users, such as a backend-for-frontend, a rewrite from Vercel or nginx, or another Worker's `fetch`. All of those users share that server's IP.
|
|
139
|
+
- End-to-end tests sign up or sign in many times against a deployed app.
|
|
140
|
+
|
|
141
|
+
Don't read the client IP from `x-forwarded-for` instead: any client can send that header. Give the trusted caller a secret header, and skip the limits only for requests that carry it:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { defineAuth } from 'void/auth';
|
|
145
|
+
|
|
146
|
+
export default defineAuth(({ defaults, env }) => ({
|
|
147
|
+
...defaults,
|
|
148
|
+
rateLimit: {
|
|
149
|
+
customRules: {
|
|
150
|
+
'/**': (request, rule) =>
|
|
151
|
+
env.AUTH_TRUSTED_KEY && request.headers.get('x-auth-trusted-key') === env.AUTH_TRUSTED_KEY
|
|
152
|
+
? false
|
|
153
|
+
: rule,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
}));
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`'/**'` matches every auth endpoint. Returning `false` skips the limit, and returning `rule` keeps the default limit for everyone else. Declare `AUTH_TRUSTED_KEY: string().optional()` in `env.ts` and set it with `void secret put AUTH_TRUSTED_KEY` (see [Environment variables](./env-vars.md#production-server-values)).
|
|
160
|
+
|
|
161
|
+
Your app no longer limits requests that carry the key, so:
|
|
162
|
+
|
|
163
|
+
- A proxy that sends it must limit every auth request it forwards for each of its users, including sign-up, password reset and verification emails.
|
|
164
|
+
- A test runner should send it only to your app. Never put the key in code that runs in your users' browsers.
|
|
165
|
+
|
|
130
166
|
## Customization
|
|
131
167
|
|
|
132
168
|
For advanced configuration, create `auth.ts` at the project root and export `defineAuth(...)`:
|
|
@@ -59,7 +59,7 @@ export const users = pgTable('users', {
|
|
|
59
59
|
});
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
`void gen model` generates PostgreSQL-appropriate code when
|
|
62
|
+
`void gen model` generates PostgreSQL-appropriate code when `database` is set to `"pg"`.
|
|
63
63
|
|
|
64
64
|
## Migrations
|
|
65
65
|
|
|
@@ -88,4 +88,4 @@ To update a linked Void project's connection, for example after moving the datab
|
|
|
88
88
|
void db set-url
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
This prompts for the new connection string and updates the Hyperdrive configuration.
|
|
91
|
+
This prompts for the new connection string and updates the Hyperdrive configuration.
|
|
@@ -54,9 +54,9 @@ Define your tables in `db/schema.ts` using column helpers from `void/schema-d1`,
|
|
|
54
54
|
::: code-group
|
|
55
55
|
|
|
56
56
|
```ts [SQLite (D1)]
|
|
57
|
+
import { sql } from 'void/db';
|
|
57
58
|
// db/schema.ts
|
|
58
59
|
import { sqliteTable, text, integer } from 'void/schema-d1';
|
|
59
|
-
import { sql } from 'void/db';
|
|
60
60
|
|
|
61
61
|
export const users = sqliteTable('users', {
|
|
62
62
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
@@ -117,8 +117,8 @@ export * from "./schema/posts";
|
|
|
117
117
|
Void re-exports all common [Drizzle query operators](https://orm.drizzle.team/docs/select) from `void/db` so you don't need to install `drizzle-orm` separately. Import `db` from `void/db` and your tables from `@schema`:
|
|
118
118
|
|
|
119
119
|
```ts
|
|
120
|
-
import { db } from 'void/db';
|
|
121
120
|
import { users } from '@schema';
|
|
121
|
+
import { db } from 'void/db';
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
::: info What is `@schema`?
|
|
@@ -134,8 +134,8 @@ const allUsers = await db.select().from(users);
|
|
|
134
134
|
### Filter with `where`
|
|
135
135
|
|
|
136
136
|
```ts
|
|
137
|
-
import { db, eq, and, or } from 'void/db';
|
|
138
137
|
import { users } from '@schema';
|
|
138
|
+
import { db, eq, and, or } from 'void/db';
|
|
139
139
|
|
|
140
140
|
// Single condition
|
|
141
141
|
const user = await db.select().from(users).where(eq(users.id, 1));
|
|
@@ -200,8 +200,8 @@ const names = await db.select({ name: users.name, email: users.email }).from(use
|
|
|
200
200
|
### Ordering and pagination
|
|
201
201
|
|
|
202
202
|
```ts
|
|
203
|
-
import { db, desc } from 'void/db';
|
|
204
203
|
import { users } from '@schema';
|
|
204
|
+
import { db, desc } from 'void/db';
|
|
205
205
|
|
|
206
206
|
const page = await db.select().from(users).orderBy(desc(users.createdAt)).limit(10).offset(20);
|
|
207
207
|
```
|
|
@@ -209,8 +209,8 @@ const page = await db.select().from(users).orderBy(desc(users.createdAt)).limit(
|
|
|
209
209
|
### Joins
|
|
210
210
|
|
|
211
211
|
```ts
|
|
212
|
-
import { db, eq } from 'void/db';
|
|
213
212
|
import { users, posts } from '@schema';
|
|
213
|
+
import { db, eq } from 'void/db';
|
|
214
214
|
|
|
215
215
|
const results = await db
|
|
216
216
|
.select({
|
|
@@ -290,9 +290,9 @@ export const insertUserSchema = createInsertSchema(users, {
|
|
|
290
290
|
```
|
|
291
291
|
|
|
292
292
|
```ts [Valibot]
|
|
293
|
+
import { pipe, minLength, email } from 'valibot';
|
|
293
294
|
// db/schema.ts
|
|
294
295
|
import { createInsertSchema } from 'void/drizzle-valibot';
|
|
295
|
-
import { pipe, minLength, email } from 'valibot';
|
|
296
296
|
|
|
297
297
|
export const insertUserSchema = createInsertSchema(users, {
|
|
298
298
|
name: (schema) => pipe(schema, minLength(1)),
|
|
@@ -301,9 +301,9 @@ export const insertUserSchema = createInsertSchema(users, {
|
|
|
301
301
|
```
|
|
302
302
|
|
|
303
303
|
```ts [ArkType]
|
|
304
|
+
import { type } from 'arktype';
|
|
304
305
|
// db/schema.ts
|
|
305
306
|
import { createInsertSchema } from 'void/drizzle-arktype';
|
|
306
|
-
import { type } from 'arktype';
|
|
307
307
|
|
|
308
308
|
export const insertUserSchema = createInsertSchema(users, {
|
|
309
309
|
name: type('string > 0'),
|
|
@@ -318,10 +318,10 @@ export const insertUserSchema = createInsertSchema(users, {
|
|
|
318
318
|
Use the derived schema in your route handlers with [`withValidator()`](./server-routing.md#validation):
|
|
319
319
|
|
|
320
320
|
```ts
|
|
321
|
+
import { users, insertUserSchema } from '@schema';
|
|
321
322
|
// routes/api/users/index.ts
|
|
322
323
|
import { defineHandler } from 'void';
|
|
323
324
|
import { db } from 'void/db';
|
|
324
|
-
import { users, insertUserSchema } from '@schema';
|
|
325
325
|
|
|
326
326
|
export const POST = defineHandler.withValidator({
|
|
327
327
|
body: insertUserSchema,
|
|
@@ -361,6 +361,21 @@ npm install arktype
|
|
|
361
361
|
|
|
362
362
|
See the [CLI reference](../reference/cli/database.md#database) for connection setup, SQL execution, Studio, exports, and other commands.
|
|
363
363
|
|
|
364
|
+
## Hand-written migrations {#hand-written-migrations}
|
|
365
|
+
|
|
366
|
+
Deploy compares your Drizzle schema with the snapshots in `db/migrations/meta/`. If you have SQL migrations without this metadata, running `void db generate` would create another migration for the same tables.
|
|
367
|
+
|
|
368
|
+
To keep an existing migration history:
|
|
369
|
+
|
|
370
|
+
1. Make your Drizzle schema match the tables your SQL migrations create.
|
|
371
|
+
2. In a scratch copy of the project, remove `db/migrations/` and run `void db generate`.
|
|
372
|
+
3. Copy the generated `meta/` folder into your original migration directory. In `_journal.json`, replace `entries` with an entry for each existing SQL file in order. Use consecutive `idx` values starting at `0`, the file name without `.sql` as `tag`, increasing timestamps as `when`, and the generated entry's `version`. Set `breakpoints` to whether the file uses `--> statement-breakpoint` lines.
|
|
373
|
+
4. Rename the generated snapshot to `<prefix>_snapshot.json`, where `<prefix>` is the part of the last migration tag before its first `_`. For `001_create_users.sql`, use `001_snapshot.json`.
|
|
374
|
+
|
|
375
|
+
Review and commit the metadata, then run `void deploy` again. If no database has applied the SQL yet, you can instead replace it with migrations from `void db generate`.
|
|
376
|
+
|
|
377
|
+
For D1 migrations containing triggers, add a `--> statement-breakpoint` line after each complete SQL statement, including the trigger's closing `END;`. Keep the trigger body together.
|
|
378
|
+
|
|
364
379
|
## Scaffolding
|
|
365
380
|
|
|
366
381
|
The `void gen model` command generates dialect-appropriate `sqliteTable`, `pgTable`, or `mysqlTable` definitions.
|
|
@@ -108,7 +108,7 @@ Requests with `Cookie`, `Authorization`, or Cloudflare Access's `Cf-Access-Jwt-A
|
|
|
108
108
|
|
|
109
109
|
For a dispatch rewrite, the cache key includes both the destination and original pathname. A direct request to `/en/docs/foo` therefore uses a different entry from `/docs/foo` rewritten to that destination.
|
|
110
110
|
|
|
111
|
-
Query parameters are excluded by default. Add `routing.revalidateQueryAllowlist` when specific parameters should produce separate cached responses. Middleware `c.rewrite()` runs after the ISR lookup, so it doesn't create an additional cache variant.
|
|
111
|
+
Query parameters are excluded by default. Add `routing.revalidateQueryAllowlist` when specific parameters should produce separate cached responses. Requests with other query parameters get the cached page. If nothing is cached yet, they render live and are not stored, so add every parameter a page reads to `routing.revalidateQueryAllowlist`. Middleware `c.rewrite()` runs after the ISR lookup, so it doesn't create an additional cache variant.
|
|
112
112
|
|
|
113
113
|
Purge the destination pathname with `revalidate({ paths })`. Purging `/en/docs/foo` clears both direct and rewritten variants. Purging only `/docs/foo` won't clear them, because the entries are stored under the destination.
|
|
114
114
|
|
|
@@ -109,15 +109,13 @@ The file can include [3xx redirects](./redirects) too. Rules in `void.config.ts`
|
|
|
109
109
|
|
|
110
110
|
## Client navigation
|
|
111
111
|
|
|
112
|
-
Rewrites run on the server. In Pages mode, a client-side `<Link
|
|
112
|
+
Rewrites run on the server. In Pages mode, a client-side `<Link href="/docs">` navigation may fetch loader data for `/docs` rather than the rewritten route `/en/docs`.
|
|
113
113
|
|
|
114
114
|
Use `<a href="/docs">` to make a full server request when the destination has a different loader. Use a redirect if the URL should change.
|
|
115
115
|
|
|
116
116
|
## ISR cache keys with rewrites
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Purge the destination with `revalidate({ paths: ['/en/docs/foo'] })` to clear direct and rewritten requests. Purging only `/docs/foo` does not clear that entry. See [Revalidation](./revalidation).
|
|
118
|
+
Purge the destination path, such as `/en/docs/foo`, to clear rewritten cache entries. See [Cache keys and rewrites](./revalidation#cache-keys-and-rewrites).
|
|
121
119
|
|
|
122
120
|
## Debugging with `X-Void-Routing`
|
|
123
121
|
|
|
@@ -100,13 +100,13 @@ There is no local inbound trigger yet — `void dev` serves the outbound dev inb
|
|
|
100
100
|
|
|
101
101
|
## Testing inbound handlers {#testing-inbound-handlers}
|
|
102
102
|
|
|
103
|
-
Use `createInboundTestHarness` to capture replies, forwards, and rejections. For shared platform mail, pass the full address including the project prefix, such as `acme+support+abc-123@acme.dev
|
|
103
|
+
Use `createInboundTestHarness` to capture replies, forwards, and rejections. For shared platform mail, pass the full address including the project prefix, such as [`acme+support+abc-123@acme.dev`](mailto:acme+support+abc-123@acme.dev). For direct Cloudflare mail, set `slug: null` and use [`support+abc-123@mail.acme.com`](mailto:support+abc-123@mail.acme.com).
|
|
104
104
|
|
|
105
105
|
```ts
|
|
106
106
|
import { describe, it, expect } from 'vitest';
|
|
107
107
|
import { createInboundTestHarness } from 'void/email/testing';
|
|
108
|
-
import support from './email/support+[ticket]';
|
|
109
108
|
import defaultHandler from './email/_default';
|
|
109
|
+
import support from './email/support+[ticket]';
|
|
110
110
|
|
|
111
111
|
describe('email handlers', () => {
|
|
112
112
|
it('routes to support+[ticket] and extracts the tag', async () => {
|
|
@@ -59,8 +59,6 @@ Cloudflare emails the recipient with a verification link. Once they click it and
|
|
|
59
59
|
void email destinations
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
The project owner’s email is added automatically, but still needs verification as described in [Setup](#setup).
|
|
63
|
-
|
|
64
62
|
::: warning When this is the right fit
|
|
65
63
|
The shared sender suits team alerts, project notifications, and replies to inbound mail.
|
|
66
64
|
|
|
@@ -39,8 +39,12 @@ STRIPE_KEY=sk_test_example
|
|
|
39
39
|
VITE_API_ORIGIN=http://localhost:5173
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
An unquoted value ends at `#`, which starts a comment. Quote a value that contains `#`, such as `DATABASE_URL="postgres://user:p#ss@localhost/my-app"`.
|
|
43
|
+
|
|
42
44
|
Keep `.env` out of Git. Other dotenv files, including `.env.local`, `.env.production`, `.env.example`, and Wrangler’s `.dev.vars*`, are not supported.
|
|
43
45
|
|
|
46
|
+
Names starting with `__VOID_` are reserved for Void. Void stops with an error if `.env` or `env.ts` defines one.
|
|
47
|
+
|
|
44
48
|
Run the local check at any time:
|
|
45
49
|
|
|
46
50
|
```sh
|
|
@@ -70,10 +70,10 @@ curl -X POST http://localhost:5173/__void/scheduled \
|
|
|
70
70
|
-d '{"cron":"0 * * * *","scheduledTime":'"$(date +%s000)"'}'
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
If you set `__VOID_PROXY_TOKEN` in `.env`, that explicit token takes precedence and the printed curl command uses `x-void-internal: <your-token>` instead.
|
|
74
|
-
|
|
75
73
|
The endpoint works in native Void apps and supported frameworks. Framework jobs run in the adapter's development runtime and use the bindings it provides.
|
|
76
74
|
|
|
77
75
|
## Deployment behavior
|
|
78
76
|
|
|
79
77
|
Native scheduled events do not require a manual HTTP token. For framework deployments, HTTP requests to `/__void/scheduled` require a matching `x-void-internal` token: the managed platform supplies its proxy token, or you can explicitly configure `CRON_SECRET` for manual calls. An absent or mismatched token returns `401` without running a job.
|
|
78
|
+
|
|
79
|
+
On a Void platform, each project can use up to 100 different cron expressions and 1,000 schedules (each expression a job exports counts as one schedule). Jobs that share an expression count once toward the expression limit. When you deploy to your own Cloudflare account, Cloudflare's [Cron Trigger limits](https://developers.cloudflare.com/workers/platform/limits/) apply instead.
|
|
@@ -22,6 +22,10 @@ export const live = defineLiveStream({
|
|
|
22
22
|
});
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
This example accepts visitors who are not signed in. Without `onSubscribe`, any connection can subscribe to any topic, so read [Authorization](#authorization) before you publish private data.
|
|
26
|
+
|
|
27
|
+
Void enables live streams when a [scanned file](../reference/resource-inference.md#scanned-directories) imports `void/live`, so keep this module in `src/` or another scanned directory. To keep it in an unscanned directory such as `lib/`, add that directory to [`inference.scanDirs`](../reference/resource-inference.md#custom-scan-directories) and list the default directories you still use, because `scanDirs` replaces them.
|
|
28
|
+
|
|
25
29
|
Expose it from a normal route:
|
|
26
30
|
|
|
27
31
|
```ts
|
|
@@ -99,6 +103,8 @@ await live.withEnv(env).publish('post:12', { title: 'Updated title' });
|
|
|
99
103
|
|
|
100
104
|
Only a connection's owner can change its subscriptions. The example above allows anonymous connections; authenticated streams use the current Void user by default.
|
|
101
105
|
|
|
106
|
+
The owner decides who can change a connection's subscriptions, not which topics the connection can read.
|
|
107
|
+
|
|
102
108
|
For authenticated streams, pass the same owner key to `connect()` and
|
|
103
109
|
`control()`:
|
|
104
110
|
|
|
@@ -125,16 +131,17 @@ current authenticated user and uses `user:${user.id}` when the user has a string
|
|
|
125
131
|
`id`. If neither path produces an owner and `allowAnonymousControl` is not set,
|
|
126
132
|
the request is rejected with `403`.
|
|
127
133
|
|
|
128
|
-
|
|
134
|
+
Without `onSubscribe`, any connection that passes the owner check (any signed-in user, or any visitor when `allowAnonymousControl` is set) can subscribe to any topic and receives everything published to it. Topic names are easy to guess. Use `onSubscribe` to decide who can read each topic: return a `Response` to reject the subscription, or return nothing to allow it.
|
|
129
135
|
|
|
130
136
|
```ts
|
|
131
137
|
export const live = defineLiveStream({
|
|
132
138
|
id: 'app',
|
|
133
139
|
async onSubscribe(ctx) {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
}
|
|
140
|
+
const post = /^post:(.+)$/.exec(ctx.topic);
|
|
141
|
+
if (post && (await canReadPost(ctx.env, ctx.user, post[1]))) return;
|
|
142
|
+
const inbox = /^user:(.+):inbox$/.exec(ctx.topic);
|
|
143
|
+
if (inbox && ctx.owner === `user:${inbox[1]}`) return;
|
|
144
|
+
return new Response('Forbidden', { status: 403 });
|
|
138
145
|
},
|
|
139
146
|
});
|
|
140
147
|
```
|
|
@@ -11,10 +11,10 @@ Use an action to change server data from a form or button. Define it in the page
|
|
|
11
11
|
Export `action` from a `.server.ts` file. Use `withValidator()` with a [schema-derived validator](../database.md#schema-derived-validators) to validate the request body:
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
+
import { users, insertUserSchema } from '@schema';
|
|
14
15
|
// pages/users/index.server.ts
|
|
15
16
|
import { defineHandler } from 'void';
|
|
16
17
|
import { db } from 'void/db';
|
|
17
|
-
import { users, insertUserSchema } from '@schema';
|
|
18
18
|
|
|
19
19
|
export const action = defineHandler.withValidator({
|
|
20
20
|
body: insertUserSchema,
|
|
@@ -176,10 +176,10 @@ const form = useForm('/users/:id', { name: '' }, { params: { id: '42' } });
|
|
|
176
176
|
When a page needs multiple mutations, such as updating and deleting a user on the same page, export `actions` (plural) instead of `action`:
|
|
177
177
|
|
|
178
178
|
```ts
|
|
179
|
+
import { users, updateUserSchema } from '@schema';
|
|
179
180
|
// pages/users/[id].server.ts
|
|
180
181
|
import { defineHandler } from 'void';
|
|
181
182
|
import { db, eq } from 'void/db';
|
|
182
|
-
import { users, updateUserSchema } from '@schema';
|
|
183
183
|
|
|
184
184
|
export const actions = {
|
|
185
185
|
update: defineHandler.withValidator({
|
|
@@ -269,7 +269,7 @@ export const action = defineHandler(async (c) => {
|
|
|
269
269
|
|
|
270
270
|
### Usage limits
|
|
271
271
|
|
|
272
|
-
For [AI](../ai.md) and [Sandbox](../sandboxes.md) actions, return `
|
|
272
|
+
For [AI](../ai.md#usage-limits) and [Sandbox](../sandboxes.md#usage-limits) actions, an uncaught `UsageLimit` automatically produces a structured HTTP 429. To customize the message, catch the error with `instanceof UsageLimit` and return `error.response({ message: 'Try again shortly. Your draft is still editable.' })`. `Form` displays the message and waits until a known retry deadline before allowing resubmission. It keeps the fields editable and never retries automatically.
|
|
273
273
|
|
|
274
274
|
## Authentication and origin checks
|
|
275
275
|
|
|
@@ -355,20 +355,32 @@ On the server, use `c.req.parseBody()` to access the uploaded file:
|
|
|
355
355
|
|
|
356
356
|
```ts
|
|
357
357
|
// pages/photos.server.ts
|
|
358
|
-
import { defineHandler } from 'void';
|
|
358
|
+
import { defineHandler, ValidationError } from 'void/handler';
|
|
359
359
|
import { storage } from 'void/storage';
|
|
360
360
|
|
|
361
|
+
// file.name and file.type come from the browser. Don't use them as the key or trust the type.
|
|
362
|
+
const imageTypes = new Map([
|
|
363
|
+
['image/jpeg', 'jpg'],
|
|
364
|
+
['image/png', 'png'],
|
|
365
|
+
['image/webp', 'webp'],
|
|
366
|
+
]);
|
|
367
|
+
|
|
361
368
|
export const action = defineHandler(async (c) => {
|
|
362
369
|
const body = await c.req.parseBody();
|
|
363
|
-
const file = body['photo']
|
|
364
|
-
if (file
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
});
|
|
370
|
+
const file = body['photo'];
|
|
371
|
+
if (!(file instanceof File) || file.size === 0) return;
|
|
372
|
+
const ext = imageTypes.get(file.type);
|
|
373
|
+
if (!ext) {
|
|
374
|
+
throw new ValidationError({ photo: 'Upload a JPEG, PNG, or WebP image.' });
|
|
368
375
|
}
|
|
376
|
+
await storage.put(`uploads/${crypto.randomUUID()}.${ext}`, file.stream(), {
|
|
377
|
+
httpMetadata: { contentType: file.type },
|
|
378
|
+
});
|
|
369
379
|
});
|
|
370
380
|
```
|
|
371
381
|
|
|
382
|
+
Pick the storage key on the server and allow only the file types you expect. See [Serving Files](../storage.md#serving-files) to send them back safely.
|
|
383
|
+
|
|
372
384
|
## Choosing a Primitive
|
|
373
385
|
|
|
374
386
|
| Primitive | Page update | Form state | Framework-specific |
|