void 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/{account-cmd-CjyxcGkM.mjs → account-cmd-C84Ee8cO.mjs} +4 -4
- package/dist/{scan-ClYmX3sa.mjs → application-analysis-BVsVqc11.mjs} +135 -18
- package/dist/application-routing-B7QEjqSR.mjs +27 -0
- package/dist/{auth-CZuiVsFh.mjs → auth-CCjt0hcq.mjs} +1 -1
- package/dist/{auth-DLNN0D3Z.mjs → auth-CSkdO2Bb.mjs} +4 -47
- package/dist/{auth-link-BLO4ptzo.mjs → auth-link-CioEg6uY.mjs} +4 -4
- package/dist/{auth-router-ZUg9MF_U.mjs → auth-router-BsR981d4.mjs} +4 -4
- package/dist/{better-auth-shared-rsBGBvWJ.mjs → better-auth-shared-hy6RPh9W.mjs} +13 -2
- package/dist/{build-cmd-BxR5FROK.mjs → build-cmd-LzvNJORH.mjs} +17 -5
- package/dist/{cache-D0sWhgKI.mjs → cache-C4MvnrMH.mjs} +2 -2
- package/dist/{cancel-deploy-D4NUqFbi.mjs → cancel-deploy-abUxpP2n.mjs} +2 -2
- package/dist/{cf-build-output-BJ6yGEIS.mjs → cf-build-output-BPqOT964.mjs} +2 -1
- package/dist/cf-build-output-_0HNWysu.mjs +2 -0
- package/dist/cli/cli.mjs +80 -455
- package/dist/cli/{cf-compat.mjs → cloudflare-operation-process.mjs} +442 -421
- package/dist/cli/env-schema-probe.d.mts +2 -1
- package/dist/cli/env-schema-probe.mjs +3 -3
- package/dist/client-Cu7jWiF1.mjs +2 -0
- package/dist/{client-BTZ3XkrB.mjs → client-RV8NVeB8.mjs} +140 -21
- package/dist/{cloudflare-auth-DQkqoMYa.mjs → cloudflare-auth-Qdc7tw8F.mjs} +41 -38
- package/dist/{cloudflare-cmd-BKlfGHAy.mjs → cloudflare-cmd-BcrVyTmJ.mjs} +5 -5
- package/dist/cloudflare-config-Bktvwtpf.mjs +182 -0
- package/dist/{cloudflare-connect-Ctmrw579.mjs → cloudflare-connect-B8uPZ4nx.mjs} +3 -3
- package/dist/{cloudflare-operations-BT6OWFBk.mjs → cloudflare-operations-AiWashgg.mjs} +1 -1
- package/dist/{cloudflare-operations-B9tzgjmf.mjs → cloudflare-operations-fxHb-byx.mjs} +116 -172
- package/dist/{preset-UHj9ARyP.mjs → cloudflare-process-B-wekeR6.mjs} +93 -6
- package/dist/{config-VavjpDnp.d.mts → config-BMHb8RCj.d.mts} +1 -0
- package/dist/config-C_XRIPx2.mjs +89 -0
- package/dist/config-entry.d.mts +1 -1
- package/dist/{connect-Dg-WkW-C.mjs → connect-Bd4kJd9U.mjs} +5 -5
- package/dist/{create-project-DdoqwFLF.mjs → create-project-Boczwj5r.mjs} +1 -1
- package/dist/{create-project-l9J7pbtm.mjs → create-project-bMf6ffLZ.mjs} +2 -2
- package/dist/{db-gp2sCXyN.mjs → db-8uG64XLl.mjs} +21 -21
- package/dist/{delete-TTedbH6B.mjs → delete-NvbiyeJf.mjs} +2 -2
- package/dist/{deploy-CH-o2CaZ.mjs → deploy-DzAIwqNU.mjs} +908 -582
- package/dist/{deploy-B19L2Bra.mjs → deploy-bjXdFCtn.mjs} +1 -1
- package/dist/{domain-JRF59P_r.mjs → domain-y5Tvydvo.mjs} +3 -3
- package/dist/{email-Bo6G9LOZ.mjs → email-B3umsW75.mjs} +13 -28
- package/dist/{env-BYOrWQnv.mjs → env-BS6qYHDb.mjs} +4 -4
- package/dist/{env-public-BxU_0yTL.d.mts → env-public-BX_r8HR6.d.mts} +1 -1
- package/dist/{env-validation-BB4GkLxn.mjs → env-validation-BPn7vk-V.mjs} +18 -71
- package/dist/{env-validation-BXge7uyK.mjs → env-validation-DbTg7-ar.mjs} +1 -1
- package/dist/{fetch-CXDChK7B.mjs → fetch-BIZJh7vR.mjs} +2 -2
- package/dist/{fetch-stream-AOByI7Ki.mjs → fetch-stream-IvCYKyQL.mjs} +24 -4
- package/dist/{gen-sCtlCOdA.mjs → gen-B580--vC.mjs} +2 -2
- package/dist/gen-BVaUUumi.mjs +2 -0
- package/dist/{github-cmd-0-7PexDq.mjs → github-cmd-v45BdfKX.mjs} +2 -2
- package/dist/{handler-HEcZsaij.d.mts → handler-CZ4nAylQ.d.mts} +1 -1
- package/dist/help-DofyZuY7.mjs +2 -0
- package/dist/{help-GKtwl07I.mjs → help-daGKjXGk.mjs} +229 -747
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +288 -1502
- package/dist/info-B97bTX9N.mjs +113 -0
- package/dist/{init-FZx3Elvz.mjs → init-Bvy7zrBo.mjs} +46 -15
- package/dist/limits-Bq5LG8Id.d.mts +27 -0
- package/dist/limits-Cjuk2VPm.mjs +68 -0
- package/dist/{link-BrQfb_CU.mjs → link-CDqqCjFl.mjs} +3 -3
- package/dist/{list-D44jmIAM.mjs → list-CZj0dzKY.mjs} +3 -3
- package/dist/{live-CKJlvlNp.d.mts → live-Chw1eIMv.d.mts} +1 -1
- package/dist/{local-d1-Bg9OzEEO.mjs → local-d1-2CMnpuW_.mjs} +2 -2
- package/dist/login-DYt_An22.mjs +2 -0
- package/dist/{login-CAsAsQ-_.mjs → login-UZKFM_u7.mjs} +3 -3
- package/dist/{logs-BjKvFnVM.mjs → logs-CUZ6t9t3.mjs} +3 -3
- package/dist/migrate-8_2u55MD.mjs +2 -0
- package/dist/{migrate-BPITvDJN.mjs → migrate-DHul7PRV.mjs} +2 -1
- package/dist/{node-Dt7z256D.mjs → node-BkyRWRx8.mjs} +1 -1
- package/dist/operator-args-CLgKGlwU.mjs +690 -0
- package/dist/{operator-cmd-BK5CCiT9.mjs → operator-cmd-DBA6dl0m.mjs} +68 -22
- package/dist/pages/client.d.mts +34 -2
- package/dist/pages/client.mjs +59 -3
- package/dist/pages/index.d.mts +1 -1
- package/dist/pages/index.mjs +1 -1
- package/dist/pages/islands-plugin.mjs +1 -1
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/pages/protocol.mjs +2 -308
- package/dist/{parse-filename-DioPHiR9.mjs → parse-filename-CUbj-1MP.mjs} +35 -1
- package/dist/plan-D1Q5rf-r.mjs +2 -0
- package/dist/plan-NPpwZ_kc.mjs +58 -0
- package/dist/platform-args-BJdRtlLq.mjs +506 -0
- package/dist/platform-args-D3RXyR6h.mjs +2 -0
- package/dist/{platform-auth-config-BlN8xTdD.mjs → platform-auth-config-B56E9YP1.mjs} +3 -3
- package/dist/{platform-auth-protection-7d5aV2Jg.mjs → platform-auth-protection-BDWO_aER.mjs} +2 -2
- package/dist/{platform-auth-recovery-Dxij8ZbR.mjs → platform-auth-recovery-1gg4CSRe.mjs} +3 -3
- package/dist/{platform-cmd-E0FuL212.mjs → platform-cmd-Bt-1w6pY.mjs} +1 -1
- package/dist/{platform-cmd-B3hwKrFK.mjs → platform-cmd-DMcQStSc.mjs} +24 -3
- package/dist/{platform-domain-D6Xcy9ZX.mjs → platform-domain-BNkcz0OB.mjs} +2 -2
- package/dist/{platform-lifecycle-k0E0xoxx.mjs → platform-lifecycle-9OyBALhH.mjs} +327 -215
- package/dist/{platform-lifecycle-DMh_qrry.mjs → platform-lifecycle-BE6C_jxh.mjs} +1 -1
- package/dist/{platform-management-Brz_BDiT.mjs → platform-management-CjwLVQwN.mjs} +6 -3
- package/dist/{platform-management-DOtss0BN.mjs → platform-management-CnyTdcWX.mjs} +1 -1
- package/dist/platform-plans-config-BNGKGr4P.mjs +359 -0
- package/dist/{platform-recovery-BvtcXON5.mjs → platform-recovery-D6KSpuFm.mjs} +2 -2
- package/dist/{plugin-inference-BMfKRSqE.mjs → plugin-inference-CXWnn79A.mjs} +174 -64
- package/dist/{prepare-C-6YZyyg.mjs → prepare-B5Mkic5u.mjs} +1 -1
- package/dist/{prepare-CRhJbVrG.mjs → prepare-DOsL0CC9.mjs} +4 -20
- package/dist/prepare-cgvDMtSb.mjs +2 -0
- package/dist/{project-cmd-BZLCqOqw.mjs → project-cmd-CNzrqvBv.mjs} +16 -16
- package/dist/{project-team-BvgM0WoX.mjs → project-team-DQOWPfQT.mjs} +2 -2
- package/dist/{project-token-l5DqK6Az.mjs → project-token-CTDYU0Cc.mjs} +2 -2
- package/dist/{project-zero-trust-v6Mvpp8y.mjs → project-zero-trust-BNAkW-_0.mjs} +2 -2
- package/dist/{protocol-BBa6cstI.d.mts → protocol-CjF_iI9X.d.mts} +2 -2
- package/dist/protocol-U7bfjHmA.mjs +331 -0
- package/dist/{provision-Fr9pRqce.mjs → provision-C4ORE7G7.mjs} +1 -1
- package/dist/{provision-C4IGqkBf.mjs → provision-CJFgTZY8.mjs} +89 -198
- package/dist/{requests-DN-BbNiM.mjs → requests-MhxYnau8.mjs} +2 -2
- package/dist/resource-name-C7LVpcRm.mjs +11 -0
- package/dist/{rollback-D7rzSaZM.mjs → rollback-CJ6iSDoU.mjs} +3 -3
- package/dist/route-url-CG7U-cRN.mjs +15 -0
- package/dist/{runner-B8wXwWlo.mjs → runner-CXA9Fh8h.mjs} +1 -1
- package/dist/{runner-p-dMs2UN.mjs → runner-Ol0TNjk6.mjs} +2 -2
- package/dist/runtime/ai.d.mts +12 -8
- package/dist/runtime/ai.mjs +84 -17
- package/dist/runtime/better-auth-mysql.mjs +1 -1
- package/dist/runtime/better-auth-pg.mjs +1 -1
- package/dist/runtime/better-auth.mjs +1 -1
- package/dist/runtime/client-react.mjs +2 -2
- package/dist/runtime/client-solid.mjs +2 -2
- package/dist/runtime/client-svelte.mjs +2 -2
- package/dist/runtime/client-vue.mjs +2 -2
- package/dist/runtime/client.mjs +2 -2
- package/dist/runtime/durable.d.mts +3 -1
- package/dist/runtime/durable.mjs +4 -1
- package/dist/runtime/email/testing.mjs +1 -1
- package/dist/runtime/env-public.d.mts +1 -1
- package/dist/runtime/fetch-stream.mjs +1 -1
- package/dist/runtime/fetch.mjs +1 -1
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/kv.mjs +0 -1
- package/dist/runtime/limits.d.mts +2 -0
- package/dist/runtime/limits.mjs +2 -0
- package/dist/runtime/live-client.d.mts +1 -1
- package/dist/runtime/live-server.mjs +25 -16
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/migration-handler.mjs +62 -42
- package/dist/runtime/route-url.d.mts +4 -0
- package/dist/runtime/route-url.mjs +2 -0
- package/dist/runtime/routing.d.mts +180 -0
- package/dist/runtime/routing.mjs +1082 -0
- package/dist/runtime/sandbox-container.d.mts +1 -1
- package/dist/runtime/sandbox-container.mjs +1 -1
- package/dist/runtime/sandbox.d.mts +3 -3
- package/dist/runtime/sandbox.mjs +75 -45
- package/dist/runtime/sse.mjs +1 -1
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +4 -2
- package/dist/runtime/ws-server.mjs +27 -2
- package/dist/runtime/ws.d.mts +2 -2
- package/dist/runtime/ws.mjs +8 -6
- package/dist/{sandbox-qpNBT8a3.d.mts → sandbox-XZAqzFlG.d.mts} +11 -8
- package/dist/{sandbox-container-DdNEBfCc.d.mts → sandbox-container-Bo0eiFKz.d.mts} +2 -1
- package/dist/{sandbox-container-4fdqLnyb.mjs → sandbox-container-C6ItmVuN.mjs} +4 -2
- package/dist/{scan-4tfN-PSn.mjs → scan-C7okrLyM.mjs} +4 -35
- package/dist/{secret-BOtOl_cb.mjs → secret-Dli5fP0B.mjs} +4 -4
- package/dist/{sse-BaC1jXko.mjs → sse-CQNaDFFV.mjs} +6 -3
- package/dist/validate-Dq_L3s0S.mjs +2 -0
- package/dist/{validate-CIUwFpjB.mjs → validate-ctOrgiS3.mjs} +2 -1
- package/dist/{wrangler-DQF1vKyf.mjs → wrangler-7K-bW_DL.mjs} +8 -239
- package/dist/{ws-BoY7vQML.d.mts → ws-CL1w7GXU.d.mts} +13 -2
- package/package.json +15 -8
- package/skills/migrate-vite-cloudflare-to-void/SKILL.md +34 -157
- package/skills/void/SKILL.md +50 -135
- package/skills/void/docs/guide/ai.md +94 -84
- package/skills/void/docs/guide/app-types.md +3 -32
- package/skills/void/docs/guide/auth.md +12 -116
- package/skills/void/docs/guide/database/d1.md +9 -54
- package/skills/void/docs/guide/database/mysql.md +1 -1
- package/skills/void/docs/guide/database/postgresql.md +5 -26
- package/skills/void/docs/guide/database.md +23 -75
- package/skills/void/docs/guide/deployment.md +27 -113
- package/skills/void/docs/guide/durable-state.md +43 -18
- package/skills/void/docs/guide/edge/headers.md +3 -47
- package/skills/void/docs/guide/edge/prerendering.md +5 -20
- package/skills/void/docs/guide/edge/redirects.md +11 -64
- package/skills/void/docs/guide/edge/revalidation.md +5 -18
- package/skills/void/docs/guide/edge/rewrites.md +56 -284
- package/skills/void/docs/guide/edge/static-assets.md +22 -72
- package/skills/void/docs/guide/email/domains.md +112 -0
- package/skills/void/docs/guide/email/receiving.md +139 -0
- package/skills/void/docs/guide/email/sending.md +231 -0
- package/skills/void/docs/guide/email.md +13 -619
- package/skills/void/docs/guide/env-migration.md +11 -11
- package/skills/void/docs/guide/env-vars.md +9 -29
- package/skills/void/docs/guide/index.md +0 -15
- package/skills/void/docs/guide/jobs.md +3 -18
- package/skills/void/docs/guide/kv.md +5 -11
- package/skills/void/docs/guide/live.md +5 -56
- package/skills/void/docs/guide/pages-routing/actions-and-forms.md +78 -125
- package/skills/void/docs/guide/pages-routing/head.md +10 -10
- package/skills/void/docs/guide/pages-routing/islands.md +6 -36
- package/skills/void/docs/guide/pages-routing/layouts.md +6 -128
- package/skills/void/docs/guide/pages-routing/loaders.md +3 -19
- package/skills/void/docs/guide/pages-routing/markdown.md +13 -171
- package/skills/void/docs/guide/pages-routing/overview.md +7 -17
- package/skills/void/docs/guide/pages-routing/view-transitions.md +1 -1
- package/skills/void/docs/guide/platform/administration/access.md +1 -4
- package/skills/void/docs/guide/platform/administration/email.md +35 -8
- package/skills/void/docs/guide/platform/administration/operations.md +26 -4
- package/skills/void/docs/guide/platform/administration/plans.md +126 -0
- package/skills/void/docs/guide/platform/administration/projects.md +5 -2
- package/skills/void/docs/guide/platform/administration/zero-trust.md +23 -152
- package/skills/void/docs/guide/platform/development/runtime.md +3 -13
- package/skills/void/docs/guide/platform/development/schema-ci.md +0 -58
- package/skills/void/docs/guide/platform/installation/credentials.md +6 -4
- package/skills/void/docs/guide/platform/installation/domains.md +30 -2
- package/skills/void/docs/guide/platform/installation/first-deployment.md +2 -0
- package/skills/void/docs/guide/platform/installation/maintenance.md +3 -1
- package/skills/void/docs/guide/platform/installation/prerequisites.md +20 -15
- package/skills/void/docs/guide/platform/installation/setup.md +9 -5
- package/skills/void/docs/guide/platform-administration.md +1 -0
- package/skills/void/docs/guide/queues.md +7 -9
- package/skills/void/docs/guide/quickstart.md +38 -37
- package/skills/void/docs/guide/remote-dev.md +4 -9
- package/skills/void/docs/guide/sandboxes.md +29 -21
- package/skills/void/docs/guide/server-routing.md +9 -72
- package/skills/void/docs/guide/sse.md +4 -18
- package/skills/void/docs/guide/ssg.md +3 -15
- package/skills/void/docs/guide/ssr.md +14 -62
- package/skills/void/docs/guide/storage.md +9 -4
- package/skills/void/docs/guide/type-safety.md +3 -14
- package/skills/void/docs/guide/typed-fetch.md +3 -7
- package/skills/void/docs/guide/websockets.md +68 -40
- package/skills/void/docs/integrations/agents.md +3 -3
- package/skills/void/docs/integrations/cloudflare.md +85 -316
- package/skills/void/docs/integrations/frameworks/analog.md +5 -64
- package/skills/void/docs/integrations/frameworks/astro.md +4 -73
- package/skills/void/docs/integrations/frameworks/nuxt.md +5 -62
- package/skills/void/docs/integrations/frameworks/overview.md +11 -54
- package/skills/void/docs/integrations/frameworks/react-router.md +5 -60
- package/skills/void/docs/integrations/frameworks/sveltekit.md +6 -65
- package/skills/void/docs/integrations/frameworks/tanstack-start.md +4 -62
- package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -69
- package/skills/void/docs/reference/api/auth.md +156 -0
- package/skills/void/docs/reference/api/client.md +87 -0
- package/skills/void/docs/reference/api/database.md +95 -0
- package/skills/void/docs/reference/api/durable.md +46 -0
- package/skills/void/docs/reference/api/env.md +50 -0
- package/skills/void/docs/reference/api/handlers.md +254 -0
- package/skills/void/docs/reference/api/pages.md +241 -0
- package/skills/void/docs/reference/api/plugin.md +39 -0
- package/skills/void/docs/reference/api/resources.md +109 -0
- package/skills/void/docs/reference/api/rewrites.md +76 -0
- package/skills/void/docs/reference/api/types.md +92 -0
- package/skills/void/docs/reference/api.md +56 -1218
- package/skills/void/docs/reference/cli/auth.md +88 -0
- package/skills/void/docs/reference/cli/database.md +128 -0
- package/skills/void/docs/reference/cli/deploy.md +85 -0
- package/skills/void/docs/reference/cli/domains.md +41 -0
- package/skills/void/docs/reference/cli/email.md +129 -0
- package/skills/void/docs/reference/cli/generate.md +116 -0
- package/skills/void/docs/reference/cli/github.md +189 -0
- package/skills/void/docs/reference/cli/platform-config.md +92 -0
- package/skills/void/docs/reference/cli/platform-email.md +81 -0
- package/skills/void/docs/reference/cli/platform-installation.md +127 -0
- package/skills/void/docs/reference/cli/platform-operations.md +90 -0
- package/skills/void/docs/reference/cli/platform-users.md +89 -0
- package/skills/void/docs/reference/cli/platform-zero-trust.md +45 -0
- package/skills/void/docs/reference/cli/platform.md +70 -0
- package/skills/void/docs/reference/cli/project.md +214 -0
- package/skills/void/docs/reference/cli/secrets.md +76 -0
- package/skills/void/docs/reference/cli/setup.md +70 -0
- package/skills/void/docs/reference/cli.md +32 -1682
- package/skills/void/docs/reference/config.md +12 -18
- package/skills/void/docs/reference/resource-inference.md +3 -58
- package/skills/void/docs/reference/structure.md +14 -41
- package/dist/canonical-json-DuDiiUsQ.mjs +0 -13
- package/dist/client-Czz8o5jP.mjs +0 -2
- package/dist/gen-CNJ62MM7.mjs +0 -2
- package/dist/help-CmZzxUba.mjs +0 -2
- package/dist/login-CGcRKEoi.mjs +0 -2
- package/dist/migrate-CYfbKkXh.mjs +0 -2
- package/dist/plan-BEZ8VJW0.mjs +0 -256
- package/dist/plan-DpuOr14e.mjs +0 -2
- package/dist/prepare-Bm3iq-u4.mjs +0 -2
- package/dist/validate-EKmJWxmy.mjs +0 -2
- /package/dist/cli/{cf-compat.d.mts → cloudflare-operation-process.d.mts} +0 -0
|
@@ -4,355 +4,127 @@ outline: deep
|
|
|
4
4
|
|
|
5
5
|
# Rewrites
|
|
6
6
|
|
|
7
|
-
A rewrite serves
|
|
8
|
-
|
|
9
|
-
Define source patterns and destination paths in `routing.rewrites` in [`void.config.ts`](../../reference/config):
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"routing": {
|
|
14
|
-
"rewrites": {
|
|
15
|
-
"/": "/en",
|
|
16
|
-
"/docs": "/en/docs",
|
|
17
|
-
"/docs/*": "/en/docs/:splat"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## When to use rewrites
|
|
24
|
-
|
|
25
|
-
Use rewrites to decouple the **public URL** from the **internal route**. Common scenarios:
|
|
26
|
-
|
|
27
|
-
- **i18n routing** — serve the default locale at unprefixed paths (`/docs` serves `/en/docs`)
|
|
28
|
-
- **URL restructuring** — reorganize internal route files without changing public URLs or SEO
|
|
29
|
-
- **Vanity URLs** — map `/pricing` to `/marketing/pricing-page` without exposing the internal structure
|
|
30
|
-
- **API versioning** — route `/api/users` to `/api/v3/users` internally so consumers use clean, unversioned endpoints
|
|
31
|
-
- **Incremental migration** — old URL structure continues working at the edge while route handlers move to new paths
|
|
32
|
-
- **Multi-app composition** — serve different internal apps under a unified URL namespace (e.g., `/docs/*` rewrites to a docs app, `/app/*` to the main app)
|
|
33
|
-
|
|
34
|
-
If you **do** want the user to see the new URL (e.g., for SEO canonical signals or moving a page permanently), use a [redirect](./redirects) instead.
|
|
35
|
-
|
|
36
|
-
## Rules
|
|
7
|
+
A rewrite serves a route at a different URL without changing the browser's address. For example, `/docs` can serve `/en/docs`. Use a [redirect](./redirects) when the browser should move to the destination instead.
|
|
37
8
|
|
|
38
|
-
|
|
39
|
-
- **Destinations** are strings starting with `/` (only internal paths are supported).
|
|
40
|
-
- `:splat` in the destination is replaced with the portion of the path matched by `*` in the source pattern.
|
|
41
|
-
- When multiple rules match, the **first match wins**. Put more-specific rules above more-general ones (matches Netlify `_redirects` and Vercel `vercel.json` semantics).
|
|
42
|
-
- On the default target, rewrites are evaluated at the edge **before** the request reaches the worker. On `node` / `bun` / `deno` targets they run in-process as Hono middleware, still before route dispatch. Either way, the rewritten path is then used for static asset serving, ISR, and SSR.
|
|
43
|
-
|
|
44
|
-
## Example: i18n routing
|
|
45
|
-
|
|
46
|
-
Define route files only under `[locale]/` and rewrite the default locale to unprefixed paths:
|
|
47
|
-
|
|
48
|
-
```json
|
|
49
|
-
{
|
|
50
|
-
"routing": {
|
|
51
|
-
"rewrites": {
|
|
52
|
-
"/": "/en",
|
|
53
|
-
"/docs": "/en/docs",
|
|
54
|
-
"/docs/*": "/en/docs/:splat"
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
A request to `/docs/getting-started` serves the content from `/en/docs/getting-started`, but the browser URL stays at `/docs/getting-started`. Non-default locales like `/zh-CN/docs/getting-started` work as-is because they match the `[locale]/` route directly.
|
|
61
|
-
|
|
62
|
-
## Example: vanity URLs
|
|
63
|
-
|
|
64
|
-
Map short marketing URLs to internal route paths:
|
|
65
|
-
|
|
66
|
-
```json
|
|
67
|
-
{
|
|
68
|
-
"routing": {
|
|
69
|
-
"rewrites": {
|
|
70
|
-
"/pricing": "/marketing/pricing-page",
|
|
71
|
-
"/start": "/onboarding/get-started",
|
|
72
|
-
"/jobs": "/company/careers"
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
```
|
|
9
|
+
Define rewrites in [`void.config.ts`](../../reference/config):
|
|
77
10
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Route unversioned API paths to the current version internally:
|
|
11
|
+
```ts
|
|
12
|
+
import { defineConfig } from 'void/config';
|
|
81
13
|
|
|
82
|
-
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
14
|
+
export default defineConfig({
|
|
15
|
+
routing: {
|
|
16
|
+
rewrites: {
|
|
17
|
+
'/': '/en',
|
|
18
|
+
'/docs': '/en/docs',
|
|
19
|
+
'/docs/*': '/en/docs/:splat',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
91
23
|
```
|
|
92
24
|
|
|
93
|
-
|
|
25
|
+
A request to `/docs/getting-started` serves `/en/docs/getting-started`. Other locales, such as `/ja/docs/getting-started`, keep their own routes.
|
|
94
26
|
|
|
95
|
-
##
|
|
96
|
-
|
|
97
|
-
After reorganizing from `/blog/:slug` to `/posts/:slug`, keep the old URLs working:
|
|
98
|
-
|
|
99
|
-
```json
|
|
100
|
-
{
|
|
101
|
-
"routing": {
|
|
102
|
-
"rewrites": {
|
|
103
|
-
"/blog/*": "/posts/:splat"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
```
|
|
27
|
+
## Rules
|
|
108
28
|
|
|
109
|
-
|
|
29
|
+
- Sources and destinations start with `/`. Destinations must be paths within the app.
|
|
30
|
+
- `*` matches any characters, including `/`. Use `:splat` in the destination for the matched portion.
|
|
31
|
+
- The first matching rule wins. Put specific patterns before catch-all patterns.
|
|
32
|
+
- Rewrites run before static assets and application routes.
|
|
110
33
|
|
|
111
34
|
## Programmatic rewrites in middleware
|
|
112
35
|
|
|
113
|
-
|
|
36
|
+
Use `c.rewrite()` when the destination depends on the request, such as a locale chosen from a cookie:
|
|
114
37
|
|
|
115
38
|
```ts
|
|
116
39
|
import { defineMiddleware } from 'void';
|
|
117
40
|
|
|
118
41
|
export default defineMiddleware(async (c, next) => {
|
|
42
|
+
if (c.req.path.match(/^\/(en|ja)(\/|$)/)) return next();
|
|
43
|
+
|
|
119
44
|
const locale = detectLocale(c.req);
|
|
120
|
-
|
|
121
|
-
return c.rewrite(`/${locale}${c.req.path}`);
|
|
122
|
-
}
|
|
123
|
-
return next();
|
|
45
|
+
return c.rewrite(`/${locale}${c.req.path}`);
|
|
124
46
|
});
|
|
125
47
|
```
|
|
126
48
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
`path` uses the [`RewriteDestination`](../../reference/api#rewritedestination) type. Your editor suggests known route patterns, and you can still pass dynamic strings. The suggestions don't guarantee that a destination exists.
|
|
130
|
-
|
|
131
|
-
### Runtime rewrites cannot reach static assets
|
|
132
|
-
|
|
133
|
-
`c.rewrite()` can only re-dispatch to paths the worker itself handles — routes, SSR entries, API handlers. It **cannot** re-dispatch into the static asset handler, because the Void platform serves assets in front of your worker. A call like `c.rewrite('/hero.png')` re-enters the worker's route table, doesn't match anything, and 404s.
|
|
134
|
-
|
|
135
|
-
This is enforced at the call site: if the destination's final path segment ends in a known static-asset extension, `c.rewrite()` throws `VoidAssetRewriteError` (exported from `"void"`, catchable by name) before the re-dispatch, with the attempted destination in the message. Query strings and fragments are stripped before matching, so `c.rewrite('/hero.png?v=2')` also throws.
|
|
49
|
+
Return the result of `c.rewrite()`. Middleware runs again at the destination, so skip paths that are already rewritten to avoid a loop. Use `c.isRewritten()` to skip work that should happen only once:
|
|
136
50
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
.png .jpg .jpeg .gif .webp .avif .svg .ico
|
|
141
|
-
.css .js .mjs .cjs
|
|
142
|
-
.woff .woff2 .ttf .otf .eot
|
|
143
|
-
.mp4 .webm .mp3 .wav
|
|
144
|
-
.pdf .txt .xml .json .wasm .map
|
|
51
|
+
```ts
|
|
52
|
+
if (c.isRewritten()) return next();
|
|
145
53
|
```
|
|
146
54
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
Static rewrites are different: `_redirects` `200!` entries and `routing.rewrites` run at the platform layer **before** the asset handler, so they _can_ rewrite into assets. If you need "rewrite into an asset" behavior dynamically, model it as a static rule (possibly with a broader source pattern) rather than doing it from middleware.
|
|
150
|
-
|
|
151
|
-
After `c.rewrite()`, Void skips static rewrite rules on the second router pass. It tracks the request itself, so a client-supplied header can't bypass this check. Use [`c.originalUrl()`](#original-url-access) to read the URL before the rewrite.
|
|
55
|
+
Keep access checks on any destination that needs them.
|
|
152
56
|
|
|
153
|
-
|
|
57
|
+
A destination without `?` preserves the incoming query string. `c.rewrite('/search')` keeps `?q=...`, while `c.rewrite('/search?q=all')` replaces it.
|
|
154
58
|
|
|
155
|
-
|
|
59
|
+
Your editor suggests known routes through the [`RewriteDestination`](../../reference/api/rewrites.md#rewritedestination) type. Dynamic strings are also accepted.
|
|
156
60
|
|
|
157
|
-
###
|
|
158
|
-
|
|
159
|
-
- Static `routing.rewrites` and `routing.fallbacks` rules are evaluated in order, first-match-wins, at O(rules) per request. The list is small in practice, but keep it bounded — don't programmatically generate thousands of entries.
|
|
160
|
-
- Each `c.rewrite()` hop replays the full middleware stack against a fresh `Request`. A chain of three middleware rewrites with four middleware in the stack is roughly twelve middleware invocations, not four.
|
|
161
|
-
- The loop check skips static rules after a rewrite, but it doesn't skip your middleware. Keep middleware rewrite chains short and ensure they terminate.
|
|
162
|
-
|
|
163
|
-
### Side effects in re-dispatched middleware
|
|
164
|
-
|
|
165
|
-
Every rewrite runs middleware again. Database lookups and auth checks repeat, and counters or loggers may record the same request more than once.
|
|
166
|
-
|
|
167
|
-
For work that should happen only before a rewrite, check `c.isRewritten()`. Keep access checks wherever the destination needs them:
|
|
168
|
-
|
|
169
|
-
```ts
|
|
170
|
-
if (c.isRewritten()) return next();
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
`c.isRewritten()` returns `true` after either a static edge rewrite or `c.rewrite()` in middleware. Void records this on the request after accepting the edge's rewrite metadata.
|
|
61
|
+
### Runtime rewrites cannot reach static assets
|
|
174
62
|
|
|
175
|
-
|
|
176
|
-
This is the recommended approach for i18n libraries. The library can export a middleware factory that handles locale detection and rewriting, and users just drop it into their `middleware/` directory.
|
|
177
|
-
:::
|
|
63
|
+
`c.rewrite()` targets application routes. To serve an asset such as `/hero.png` at another URL, use `routing.rewrites` or a `_redirects` rule instead. A middleware rewrite to a recognized asset extension throws `VoidAssetRewriteError`.
|
|
178
64
|
|
|
179
65
|
## Original URL access
|
|
180
66
|
|
|
181
|
-
|
|
67
|
+
`c.originalUrl()` returns the URL requested before a static or middleware rewrite, or `null` when no rewrite occurred. Use it for canonical links or locale detection:
|
|
182
68
|
|
|
183
69
|
```ts
|
|
70
|
+
import { defineHandler } from 'void';
|
|
71
|
+
|
|
184
72
|
export default defineHandler((c) => {
|
|
185
73
|
const original = c.originalUrl();
|
|
186
|
-
|
|
187
|
-
// before the rewrite, or null if the request was not rewritten.
|
|
74
|
+
return c.json({ pathname: original?.pathname ?? c.req.path });
|
|
188
75
|
});
|
|
189
76
|
```
|
|
190
77
|
|
|
191
|
-
On managed Void deployments, the edge passes the original URL to the Worker as trusted request metadata. On direct Cloudflare deployments, the generated Worker records that metadata when it applies the rewrite itself. Further middleware rewrites update the same metadata without requiring you to parse headers.
|
|
192
|
-
|
|
193
78
|
## Fallbacks
|
|
194
79
|
|
|
195
|
-
`routing.fallbacks`
|
|
196
|
-
|
|
197
|
-
Void only treats generated no-route 404s as fallback-eligible, whether the check runs in managed dispatch or in a native Cloudflare Worker. A route handler or API endpoint that intentionally returns `404` is returned as-is, so catch-all fallbacks do not turn missing API resources into HTML. Third-party framework deployments do not expose Void's no-route marker, so their fallback rules still apply after the framework worker returns `404`.
|
|
198
|
-
|
|
199
|
-
```json
|
|
200
|
-
{
|
|
201
|
-
"routing": {
|
|
202
|
-
"fallbacks": {
|
|
203
|
-
"/*": "/index.html"
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Common uses:
|
|
210
|
-
|
|
211
|
-
- **SPA shell** — serve `/index.html` for any unmatched path so client-side routing can take over (for app types that don't already do this automatically).
|
|
212
|
-
- **Default-locale catch-all** — send unmatched paths to `/en/:splat` without stealing requests that already resolve to an existing page under `/zh-CN/…`, `/ja/…`, etc.
|
|
213
|
-
|
|
214
|
-
Ordering:
|
|
215
|
-
|
|
216
|
-
- `rewrites` are evaluated **before** the static asset lookup; a matching rule always wins.
|
|
217
|
-
- `fallbacks` are evaluated **after** the static asset lookup, only when it would 404.
|
|
218
|
-
|
|
219
|
-
Use `rewrites` when you want to force a path mapping regardless of what exists; use `fallbacks` when the rule should only kick in as a safety net.
|
|
220
|
-
|
|
221
|
-
### SPA app type + `routing.fallbacks`
|
|
222
|
-
|
|
223
|
-
For the `spa` app type, the platform already serves `/index.html` for any asset miss by default (`not_found_handling: 'single-page-application'`). Adding `routing.fallbacks` to a SPA app is **additive, not an override**:
|
|
224
|
-
|
|
225
|
-
1. Your `routing.fallbacks` rules are checked first, in the order they appear (first match wins among user rules).
|
|
226
|
-
2. If none of them match, the implicit `/* → /index.html` SPA fallback still fires.
|
|
227
|
-
|
|
228
|
-
So a SPA app can carve out specific paths without losing the SPA shell behavior for everything else:
|
|
229
|
-
|
|
230
|
-
```json
|
|
231
|
-
{
|
|
232
|
-
"routing": {
|
|
233
|
-
"fallbacks": {
|
|
234
|
-
"/docs/*": "/docs.html"
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
With this config, an asset miss under `/docs/getting-started` resolves to `/docs.html`, while an asset miss under `/app/settings` still resolves to `/index.html` (the SPA default).
|
|
241
|
-
|
|
242
|
-
You don't need to write `"/*": "/index.html"` yourself — the CLI **appends** a synthetic `{ source: '/*', destination: '/index.html' }` rule to the fallback list when packaging a SPA deploy that has user fallbacks. Because evaluation is first-match-wins, user rules come before the synthetic entry and take precedence; the synthetic rule only fires when no user rule matched. This is why the shipped manifest may contain more fallback rules than you wrote in `void.config.ts`. If you do write `"/*": "/index.html"` yourself, the CLI emits a warning on `void deploy` noting that the rule duplicates the default and can be omitted.
|
|
243
|
-
|
|
244
|
-
## `_redirects` file
|
|
245
|
-
|
|
246
|
-
Rewrites can also be defined in a `_redirects` file placed in Vite's `publicDir` (defaults to `public/`). Void mirrors Netlify-compat semantics for the `200` status code:
|
|
247
|
-
|
|
248
|
-
```text
|
|
249
|
-
# plain 200 = fallback (asset-miss only, equivalent to routing.fallbacks)
|
|
250
|
-
/* /index.html 200
|
|
251
|
-
|
|
252
|
-
# 200! with force suffix = always rewrite (equivalent to routing.rewrites)
|
|
253
|
-
/docs/* /en/docs/:splat 200!
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
| File-based form | `void.config.ts` equivalent | Behavior |
|
|
257
|
-
| --------------- | --------------------------- | ------------------------------------------------------------------------ |
|
|
258
|
-
| `... 200` | `routing.fallbacks` | Fires only when no static asset and no route matched (would have 404'd). |
|
|
259
|
-
| `... 200!` | `routing.rewrites` | Always fires, overriding any static asset that would have served. |
|
|
260
|
-
|
|
261
|
-
- `void.config.ts` rules are applied **before** file-based rules. Since the first match wins, `routing.rewrites` / `routing.fallbacks` in `void.config.ts` take precedence.
|
|
262
|
-
- The `_redirects` file can mix 3xx redirects, `200` fallbacks, and `200!` force rewrites. Ordering is preserved within each bucket.
|
|
263
|
-
- The `!` force suffix is only meaningful on `200`. On a 3xx entry like `301!`, the `!` is silently stripped — 3xx redirects always "force" by their nature (they change the URL), so the suffix is meaningless. `void deploy` prints a single aggregated warning tallying all `301!` / `302!` / `307!` / `308!` entries so you can clean them up.
|
|
264
|
-
|
|
265
|
-
## Precedence: `_redirects` vs `void.config.ts`
|
|
266
|
-
|
|
267
|
-
When the same source pattern appears in both a `_redirects` file and `void.config.ts` (`routing.redirects` / `routing.rewrites` / `routing.fallbacks`), the rules don't replace each other — they **merge into a single ordered list per phase**, and the first match wins.
|
|
268
|
-
|
|
269
|
-
Rules are bucketed by phase before merging:
|
|
270
|
-
|
|
271
|
-
- **Pre-asset phase** (always fires, runs before static asset lookup): `routing.redirects` + `routing.rewrites` + `_redirects` 3xx entries + `_redirects` `200!` entries.
|
|
272
|
-
- **Post-asset phase** (only fires on an asset miss): `routing.fallbacks` + `_redirects` plain `200` entries. For SPA app types, the synthetic `/* → /index.html` rule is **appended last** in this phase, so user fallbacks evaluated earlier take precedence under first-match-wins.
|
|
273
|
-
|
|
274
|
-
Within each phase, `void.config.ts` rules run first, followed by `_redirects` rules. The first matching rule wins, so a `void.config.ts` rule takes precedence over the same source pattern in `_redirects`.
|
|
275
|
-
|
|
276
|
-
### Concrete example
|
|
277
|
-
|
|
278
|
-
```text
|
|
279
|
-
# _redirects
|
|
280
|
-
/docs/* /en/docs/:splat 200!
|
|
281
|
-
```
|
|
80
|
+
`routing.fallbacks` uses the same patterns as rewrites, but applies only when no asset or route matches:
|
|
282
81
|
|
|
283
82
|
```ts
|
|
284
83
|
import { defineConfig } from 'void/config';
|
|
285
84
|
|
|
286
85
|
export default defineConfig({
|
|
287
86
|
routing: {
|
|
288
|
-
|
|
289
|
-
'/docs/*': '/handbook/:splat',
|
|
290
|
-
},
|
|
87
|
+
fallbacks: { '/*': '/index.html' },
|
|
291
88
|
},
|
|
292
89
|
});
|
|
293
90
|
```
|
|
294
91
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
To confirm precedence in practice, check the [`X-Void-Routing` dev header](#debugging-with-x-void-routing) on any response during `vite dev` — it names the winning rule and its origin (`_redirects:<line>` vs `void.config.ts#routing.rewrites`).
|
|
298
|
-
|
|
299
|
-
## How rewrites work
|
|
92
|
+
Use a fallback for a client-side router or a default-locale catch-all. An intentional `404` from a Void route remains a `404`. For third-party frameworks, fallbacks can also replace the framework's `404` response.
|
|
300
93
|
|
|
301
|
-
|
|
94
|
+
SPA apps already fall back to `/index.html`. Custom fallback rules run first; unmatched paths still use the SPA shell. You don't need to add `'/*': '/index.html'` yourself.
|
|
302
95
|
|
|
303
|
-
|
|
304
|
-
2. The platform stores the rules in the KV routing entry for your project.
|
|
305
|
-
3. The dispatch worker evaluates all routing rules (redirects and rewrites) before any worker invocation. If a rewrite matches, the request pathname is updated internally and the request continues through the normal pipeline (static assets, ISR, worker). The original URL is passed as `X-Void-Original-URL`.
|
|
306
|
-
|
|
307
|
-
On direct Cloudflare deployments of native Void apps, Vite compiles the same merged redirects, rewrites, headers, and fallbacks into the generated Worker. They run there without managed dispatch, and rewrite metadata remains internal to the Worker.
|
|
308
|
-
|
|
309
|
-
**Middleware rewrites** (`c.rewrite()`):
|
|
96
|
+
## `_redirects` file
|
|
310
97
|
|
|
311
|
-
|
|
312
|
-
2. Your middleware calls `c.rewrite(newPath)`, which constructs a new request with the rewritten pathname and re-dispatches it through the Hono router.
|
|
313
|
-
3. The re-dispatched request runs through all middleware and route handlers as if it were a fresh request to the new path.
|
|
98
|
+
You can also place rules in Vite's `publicDir`, which defaults to `public/`:
|
|
314
99
|
|
|
315
|
-
|
|
100
|
+
```text
|
|
101
|
+
# Fallback: only when no asset or route matches
|
|
102
|
+
/* /index.html 200
|
|
316
103
|
|
|
317
|
-
|
|
104
|
+
# Rewrite: before assets and routes
|
|
105
|
+
/docs/* /en/docs/:splat 200!
|
|
106
|
+
```
|
|
318
107
|
|
|
319
|
-
|
|
108
|
+
The file can include [3xx redirects](./redirects) too. Rules in `void.config.ts` take precedence over file rules; within each group, the first match wins.
|
|
320
109
|
|
|
321
|
-
|
|
110
|
+
## Client navigation
|
|
322
111
|
|
|
323
|
-
|
|
112
|
+
Rewrites run on the server. In Pages mode, a client-side `<Link to="/docs">` navigation may fetch loader data for `/docs` rather than the rewritten route `/en/docs`.
|
|
324
113
|
|
|
325
|
-
|
|
326
|
-
- If the URL change is meant to be authoritative, use a [redirect](./redirects) instead — the Void Router follows redirects via HTTP, so behavior is consistent.
|
|
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.
|
|
327
115
|
|
|
328
116
|
## ISR cache keys with rewrites
|
|
329
117
|
|
|
330
|
-
|
|
118
|
+
Static rewrites cache the destination separately for each original pathname. Query parameters vary the cache only when included in `routing.revalidateQueryAllowlist`. Middleware rewrites don't create a separate cache variant.
|
|
331
119
|
|
|
332
|
-
`revalidate({ paths
|
|
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).
|
|
333
121
|
|
|
334
122
|
## Debugging with `X-Void-Routing`
|
|
335
123
|
|
|
336
|
-
During
|
|
124
|
+
During development, inspect the `X-Void-Routing` response header in your browser's Network tab. It shows the matching rule and where it was declared:
|
|
337
125
|
|
|
338
|
-
```
|
|
339
|
-
X-Void-Routing:
|
|
340
|
-
X-Void-Routing: rewrite[/api/*] -> /backend/:splat (void.config.ts#routing.rewrites)
|
|
341
|
-
X-Void-Routing: fallback[/docs/*] -> /docs.html (void.config.ts#routing.fallbacks)
|
|
126
|
+
```text
|
|
127
|
+
X-Void-Routing: rewrite[/docs/*] -> /en/docs/:splat (void.config.ts#routing.rewrites)
|
|
342
128
|
X-Void-Routing: c.rewrite -> /new-path (middleware)
|
|
343
129
|
X-Void-Routing: pass-through
|
|
344
130
|
```
|
|
345
|
-
|
|
346
|
-
Phases are separated by `->` so the diagnostic value stays valid as an HTTP header. The parenthesised source hint points at the exact declaration — a line number for `_redirects`, a config path for `void.config.ts`, or `spa-default` for the synthetic SPA catch-all. The header is **only emitted in dev** — production builds strip both the trace code and the per-rule `origin` metadata from the bundle and manifest.
|
|
347
|
-
|
|
348
|
-
::: info What fires in `vite dev`
|
|
349
|
-
`vite dev` applies the full static routing pipeline on every target — `node`, `bun`, `deno`, and the default target alike. `void.config.ts` rules (`routing.redirects` / `routing.rewrites` / `routing.fallbacks` / `routing.headers`) and file-based rules (`public/_redirects`, `public/_headers`) are merged at plugin load and compiled into the Hono middleware your worker runs behind. Editing `_redirects` or `_headers` during a dev session re-runs the merge and reloads the page — no restart needed. `c.rewrite()` calls in middleware work everywhere because they live inside the worker itself, and the `X-Void-Routing` dev header reports every decision on every target.
|
|
350
|
-
|
|
351
|
-
A few things still only run in the deployed runtime, not `vite dev`:
|
|
352
|
-
|
|
353
|
-
- [ISR caching](./revalidation) (`routing.revalidate`) — served cold in dev, no cached slot warm-ups.
|
|
354
|
-
- Custom-domain rewriting and per-project asset prefixes — dev always runs against the root.
|
|
355
|
-
- AI Gateway metering for `void/ai` calls — dev hits the provider directly.
|
|
356
|
-
|
|
357
|
-
For everything else, the rule that fires in `vite dev` is the rule that will fire after `void deploy`.
|
|
358
|
-
:::
|
|
@@ -14,7 +14,7 @@ Files in Vite's `build.assetsDir` (default `assets/`) are produced with content
|
|
|
14
14
|
Cache-Control: public, max-age=31536000, immutable
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Browsers and the edge can cache these files for one year and reuse them across deploys.
|
|
18
18
|
|
|
19
19
|
If your Vite config customizes `build.assetsDir`, Void automatically detects this and applies the immutable optimization to the configured directory:
|
|
20
20
|
|
|
@@ -29,17 +29,23 @@ export default defineConfig({
|
|
|
29
29
|
|
|
30
30
|
If `build.assetsDir` is set to `""`, meaning hashed files live at the root, the optimization is skipped because there is no directory-based way to distinguish hashed from non-hashed files.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Supported meta-frameworks use their own asset directories automatically.
|
|
33
|
+
|
|
34
|
+
### Native Cloudflare deployments
|
|
35
|
+
|
|
36
|
+
With `void deploy --platform cloudflare`, generated JavaScript bundles with content fingerprints receive immutable caching. Files copied from `public/` and custom build outputs with stable filenames keep normal revalidation, even when they live in the assets directory. CSS and other assets keep Cloudflare's default or your configured `Cache-Control`.
|
|
37
|
+
|
|
38
|
+
Use [Custom Headers](./headers) to choose a cache policy for other files. Apply immutable caching only to URLs that change whenever their content changes.
|
|
33
39
|
|
|
34
40
|
## Non-hashed assets
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
Other static files, such as `index.html` and `favicon.ico`, are cached until the next deploy. You do not need to purge them manually.
|
|
37
43
|
|
|
38
44
|
```
|
|
39
45
|
Cache-Control: public, s-maxage=31536000, max-age=0, must-revalidate
|
|
40
46
|
```
|
|
41
47
|
|
|
42
|
-
|
|
48
|
+
Browsers revalidate these files on each request.
|
|
43
49
|
|
|
44
50
|
**What gets cached:**
|
|
45
51
|
|
|
@@ -60,86 +66,30 @@ If your worker serves dynamic content at a URL that looks static (e.g., a dynami
|
|
|
60
66
|
|
|
61
67
|
## ETags and 304 Not Modified
|
|
62
68
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
This happens automatically for all static assets. No configuration is needed.
|
|
69
|
+
Static assets include an `ETag` header. When a browser revalidates an unchanged file, Void returns `304 Not Modified` without downloading it again. No configuration is needed.
|
|
66
70
|
|
|
67
71
|
## Custom headers
|
|
68
72
|
|
|
69
73
|
You can override caching headers or add your own for any static asset path using [Custom Headers](./headers).
|
|
70
74
|
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
Static assets can run in front of the worker, behind the worker, or without any worker at all. Void chooses the pipeline from the app shape so static pages stay static unless application code must inspect document navigations.
|
|
74
|
-
|
|
75
|
-
### Deploy shapes
|
|
76
|
-
|
|
77
|
-
| Shape | Worker deployed | First handler for assets | First handler for document navigations | Miss behavior |
|
|
78
|
-
| ---------------------------------------------------------------------------------- | --------------- | ------------------------ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
79
|
-
| `inference.appType: "static"` | No | Asset platform | Asset platform | Platform 404 page. |
|
|
80
|
-
| Static SPA deploy | No | Asset platform | Asset platform | Platform serves `/index.html` for unmatched navigations. If user `routing.fallbacks` exist, Void ships fallback rules. |
|
|
81
|
-
| Void app with only `/api` routes or managed auth | Yes | Worker first for `/api` | Asset platform | Static navigations keep the platform SPA fallback; API requests, including document navigations, reach the worker. |
|
|
82
|
-
| Void app with `middleware/`, non-`/api` routes, document WebSockets, live bindings | Yes | Worker first | Worker first | Asset misses stay 404, then the worker serves `/index.html` for HTML requests after routes and middleware run. |
|
|
83
|
-
| Pages, SSR, and framework apps | Yes | Worker first | Worker first | Asset misses stay 404. Pages, SSR, or framework rendering owns HTML responses, including intentional HTML 404s. |
|
|
84
|
-
|
|
85
|
-
### Worker-first Void apps
|
|
86
|
-
|
|
87
|
-
For worker-owned HTML, Void sets Cloudflare assets to `not_found_handling: "none"` and configures `run_worker_first`. The request order is:
|
|
88
|
-
|
|
89
|
-
1. Platform routing rules that always run before assets, such as redirects and forced rewrites.
|
|
90
|
-
2. Worker route table, middleware, auth, WebSocket upgrades, Pages, or SSR.
|
|
91
|
-
3. `env.ASSETS.fetch()` from inside the worker for static files.
|
|
92
|
-
4. For non-Pages, non-SSR Void apps only, a worker-side SPA fallback to `/index.html` when the original request accepts HTML.
|
|
93
|
-
5. The worker's original 404.
|
|
94
|
-
|
|
95
|
-
This is the path needed for preview auth and other middleware. Cloudflare's platform SPA fallback can serve `index.html` directly for browser navigations; when that happens, middleware never sees the request. Worker-owned HTML avoids that by moving fallback HTML behind middleware.
|
|
96
|
-
|
|
97
|
-
### Asset-first Void apps
|
|
98
|
-
|
|
99
|
-
For Void apps with only `/api` routes, Void keeps the platform SPA fallback and scopes `run_worker_first` to `/api` and `/api/*`. Static assets and non-API SPA navigations stay on the asset platform. API requests, including browser document navigations such as OAuth callbacks, reach the worker instead of being rewritten to `index.html`.
|
|
75
|
+
## Navigation and middleware
|
|
100
76
|
|
|
101
|
-
|
|
77
|
+
Void serves static files directly unless application code needs to handle the request first. API requests reach your Worker. Apps with middleware, routes outside `/api`, Pages, or SSR run application code before serving fallback HTML.
|
|
102
78
|
|
|
103
|
-
`
|
|
79
|
+
SPAs serve `index.html` for unmatched HTML navigation so client-side routing works. For a static site with a `404.html` page, set [`routing.notFound`](../../reference/config.md#routing-notfound):
|
|
104
80
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
| Pages or SSR | `none` | The worker's own 404 |
|
|
108
|
-
| Worker owns HTML, no `pages/`, no SSR entry | `none` + worker fallback | `index.html` with status **200** |
|
|
109
|
-
| Asset-first (only `/api` routes, or none) | `single-page-application` | `index.html` with status **200** |
|
|
110
|
-
| Framework deploy (SvelteKit, Nuxt, Analog, Astro) | `none` — pinned, not overridable | The framework worker's own 404 |
|
|
111
|
-
|
|
112
|
-
Apps with middleware, a route outside `/api`, document WebSockets, or Live send requests through the Worker first. If they have no Pages or SSR entry, the Worker can then serve `index.html` for HTML navigation. Middleware and auth run before that fallback.
|
|
113
|
-
|
|
114
|
-
A SPA needs `index.html` for deep links so its client router can load. A generated static site usually needs a real `404.html` instead. Void can't distinguish the two from built files alone. Set [`routing.notFound`](../../reference/config.md#routing-notfound) for the behavior you want:
|
|
81
|
+
```ts
|
|
82
|
+
import { defineConfig } from 'void/config';
|
|
115
83
|
|
|
116
|
-
|
|
117
|
-
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
routing: { notFound: '404-page' },
|
|
86
|
+
});
|
|
118
87
|
```
|
|
119
88
|
|
|
120
|
-
Choose `
|
|
121
|
-
|
|
122
|
-
Choosing anything other than `"single-page-application"` disables the Worker's `index.html` fallback. With `"404-page"`, unmatched HTML navigation uses the asset layer's nearest `404.html`. Intentional API `404` responses keep their body, status, and headers. If no `404.html` exists, the Worker's original `404` is kept.
|
|
123
|
-
|
|
124
|
-
SvelteKit, Nuxt, Analog, and Astro manage their own not-found behavior. Void ignores `routing.notFound` for those deploys and prints a warning. Their prerendered files are served first, and the framework handles unmatched routes without a platform SPA fallback replacing its error page.
|
|
125
|
-
|
|
126
|
-
TanStack Start, React Router, and vinext follow the rows above on a managed `void deploy` — that path resolves the asset config itself and applies it to the uploaded Worker. Void writes no `assets` policy into their generated Worker config:
|
|
127
|
-
|
|
128
|
-
| Framework | Generated worker config |
|
|
129
|
-
| -------------- | ---------------------------- |
|
|
130
|
-
| TanStack Start | `dist/server/wrangler.json` |
|
|
131
|
-
| React Router | `build/server/wrangler.json` |
|
|
132
|
-
| vinext (App) | `dist/server/wrangler.json` |
|
|
133
|
-
| vinext (Pages) | `dist/ssr/wrangler.json` |
|
|
134
|
-
|
|
135
|
-
For direct Cloudflare deployment, these frameworks need a complete `assets` policy in their own config: `binding`, `directory`, `not_found_handling`, and `run_worker_first`. Void preserves that policy. Without one, Cloudflare's default applies. The build warns when `routing.notFound` is set so you know to check the framework's asset configuration.
|
|
136
|
-
|
|
137
|
-
### Generated config
|
|
138
|
-
|
|
139
|
-
Void owns the generated asset routing policy during dev and build for Void apps. If `cloudflare.assets` in `void.config.ts` contains stale `not_found_handling` or `run_worker_first` values, Void replaces those fields so generated config cannot accidentally change which layer sees a request first.
|
|
89
|
+
Choose `single-page-application`, `404-page`, or `none`. These settings preserve which requests reach middleware and API handlers. Intentional API `404` responses keep their status and body.
|
|
140
90
|
|
|
141
|
-
|
|
91
|
+
SvelteKit, Nuxt, Analog, and Astro handle their own error pages and ignore `routing.notFound`. For direct Cloudflare deployment with TanStack Start, React Router, or vinext, configure the asset binding, directory, and routing policy through the framework's Cloudflare config.
|
|
142
92
|
|
|
143
93
|
## API routes and SSR pages
|
|
144
94
|
|
|
145
|
-
API responses
|
|
95
|
+
API responses and SSR pages are not cached as static assets. Use [Revalidation](./revalidation) to cache public rendered pages.
|