void 0.1.6 → 0.7.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/AGENT_PROMPT.md +15 -0
- package/README.md +62 -123
- package/dist/auth-BdsJ0Aff.d.mts +43 -0
- package/dist/auth-cmd-Dx8oPKZC.mjs +43 -0
- package/dist/auth-migrations-BAtAck2g.mjs +117 -0
- package/dist/better-auth-shared-C9_GHSkR.d.mts +71 -0
- package/dist/better-auth-shared-CdYmQGry.mjs +163 -0
- package/dist/cache-W82I8ihI.mjs +47 -0
- package/dist/cancel-deploy-BOBTqqh0.mjs +59 -0
- package/dist/cf-access-Dee5cXxL.mjs +22 -0
- package/dist/chunk-DJd-R1mw.mjs +34 -0
- package/dist/cli/cli.d.mts +1 -0
- package/dist/cli/cli.mjs +1807 -0
- package/dist/client-snXOjrp1.mjs +565 -0
- package/dist/collect-CjeZgz5D.mjs +55 -0
- package/dist/config-BIa9HwVX.mjs +573 -0
- package/dist/config-BzM9Dy7T.mjs +37 -0
- package/dist/config-CvHtTM0q.mjs +30 -0
- package/dist/create-project-BIA15W7z.mjs +90 -0
- package/dist/db-DsRoMcfN.mjs +895 -0
- package/dist/defer-DcxEsVH1.mjs +49 -0
- package/dist/delete-DAP6yDc7.mjs +64 -0
- package/dist/deploy-BPKblFx6.mjs +2424 -0
- package/dist/discover-B7FkXBLB.mjs +40 -0
- package/dist/dist-DUyXJLkq.mjs +2667 -0
- package/dist/dist-Dayj3gCK.mjs +1287 -0
- package/dist/domain-BGofcQ6I.mjs +79 -0
- package/dist/dotenv-DwO4ti0Z.mjs +173 -0
- package/dist/drizzle-NnudE_UN.mjs +232 -0
- package/dist/env-CyG3tvU0.mjs +301 -0
- package/dist/env-helpers-Dr9Y7RnE.d.mts +52 -0
- package/dist/env-raw-BDL4TvdN.mjs +32 -0
- package/dist/env-types-DknSA4SO.mjs +64 -0
- package/dist/env-validation-DJKjR_8q.mjs +163 -0
- package/dist/fetch-error-BQ8sZ5Nd.mjs +266 -0
- package/dist/fetch-error-CVZ5CGA-.d.mts +20 -0
- package/dist/gen-U0Ktr4Zd.mjs +761 -0
- package/dist/handler-B0ds0OHJ.d.mts +269 -0
- package/dist/head-P-egrtFE.d.mts +45 -0
- package/dist/headers-DCXc7mDs.mjs +279 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.mjs +4695 -0
- package/dist/init-C7wS5iGP.mjs +2625 -0
- package/dist/link-p2R6NbgN.mjs +49 -0
- package/dist/list-Bfel-QLc.mjs +113 -0
- package/dist/log-DXdqnmhF.mjs +26 -0
- package/dist/login-CkcXUiIu.mjs +72 -0
- package/dist/logs-DmkrRvx6.mjs +98 -0
- package/dist/magic-string.es-D6g9UnIy.mjs +1011 -0
- package/dist/mcp-CaQzfeUi.mjs +373 -0
- package/dist/node-DDfXj10V.mjs +54 -0
- package/dist/output-BwlcIYSR.mjs +139 -0
- package/dist/pages/client.d.mts +198 -0
- package/dist/pages/client.mjs +980 -0
- package/dist/pages/head-client.d.mts +15 -0
- package/dist/pages/head-client.mjs +90 -0
- package/dist/pages/head.d.mts +2 -0
- package/dist/pages/head.mjs +112 -0
- package/dist/pages/index.d.mts +38 -0
- package/dist/pages/index.mjs +76 -0
- package/dist/pages/islands-plugin.d.mts +50 -0
- package/dist/pages/islands-plugin.mjs +195 -0
- package/dist/pages/prefetch.d.mts +31 -0
- package/dist/pages/prefetch.mjs +90 -0
- package/dist/pages/protocol.d.mts +3 -0
- package/dist/pages/protocol.mjs +193 -0
- package/dist/pages/serialize.d.mts +10 -0
- package/dist/pages/serialize.mjs +14 -0
- package/dist/pathe.M-eThtNZ-D-kmWkCS.mjs +150 -0
- package/dist/plugin-inference-oZ6Ybu2_.mjs +2447 -0
- package/dist/prepare-BAtWufvm.mjs +99 -0
- package/dist/preset-D4I73kT4.mjs +221 -0
- package/dist/project-TqORyHn8.mjs +72 -0
- package/dist/project-cmd-B7lQp3F3.mjs +67 -0
- package/dist/project-slug-CKam8lF9.mjs +11 -0
- package/dist/project-tsconfig-DfkESbDL.mjs +63 -0
- package/dist/protocol-BWzXs2A2.d.mts +34 -0
- package/dist/providers-B3aMxWzP.mjs +67 -0
- package/dist/resolve-project-Br5BR03U.mjs +29 -0
- package/dist/rollback-gyC59l7U.mjs +92 -0
- package/dist/route-types-DReF1gUY.mjs +255 -0
- package/dist/routes-stub.d.mts +55 -0
- package/dist/routes-stub.mjs +1 -0
- package/dist/runner-6Ep3fNQu.mjs +123 -0
- package/dist/runner-pg-D0wWHYnr.mjs +57 -0
- package/dist/runtime/ai.d.mts +127 -0
- package/dist/runtime/ai.mjs +348 -0
- package/dist/runtime/auth-client-react.d.mts +8 -0
- package/dist/runtime/auth-client-react.mjs +6 -0
- package/dist/runtime/auth-client-solid.d.mts +8 -0
- package/dist/runtime/auth-client-solid.mjs +6 -0
- package/dist/runtime/auth-client-svelte.d.mts +8 -0
- package/dist/runtime/auth-client-svelte.mjs +6 -0
- package/dist/runtime/auth-client-vue.d.mts +8 -0
- package/dist/runtime/auth-client-vue.mjs +6 -0
- package/dist/runtime/auth-client.d.mts +8 -0
- package/dist/runtime/auth-client.mjs +6 -0
- package/dist/runtime/auth.d.mts +2 -0
- package/dist/runtime/auth.mjs +22 -0
- package/dist/runtime/better-auth-pg.d.mts +11 -0
- package/dist/runtime/better-auth-pg.mjs +51 -0
- package/dist/runtime/better-auth.d.mts +11 -0
- package/dist/runtime/better-auth.mjs +33 -0
- package/dist/runtime/client.d.mts +6 -0
- package/dist/runtime/client.mjs +5 -0
- package/dist/runtime/db-pg.d.mts +2 -0
- package/dist/runtime/db-pg.mjs +1 -0
- package/dist/runtime/db.d.mts +17 -0
- package/dist/runtime/db.mjs +30 -0
- package/dist/runtime/drizzle-arktype.d.mts +1 -0
- package/dist/runtime/drizzle-arktype.mjs +2 -0
- package/dist/runtime/drizzle-valibot.d.mts +1 -0
- package/dist/runtime/drizzle-valibot.mjs +2 -0
- package/dist/runtime/drizzle-zod.d.mts +1 -0
- package/dist/runtime/drizzle-zod.mjs +2 -0
- package/dist/runtime/env-helpers.d.mts +2 -0
- package/dist/runtime/env-helpers.mjs +173 -0
- package/dist/runtime/env-public-client.d.mts +22 -0
- package/dist/runtime/env-public-client.mjs +54 -0
- package/dist/runtime/env-public.d.mts +143 -0
- package/dist/runtime/env-public.mjs +366 -0
- package/dist/runtime/env.d.mts +13 -0
- package/dist/runtime/env.mjs +51 -0
- package/dist/runtime/fetch-stream.d.mts +51 -0
- package/dist/runtime/fetch-stream.mjs +81 -0
- package/dist/runtime/fetch.d.mts +59 -0
- package/dist/runtime/fetch.mjs +18 -0
- package/dist/runtime/handler.d.mts +3 -0
- package/dist/runtime/handler.mjs +85 -0
- package/dist/runtime/isr.d.mts +26 -0
- package/dist/runtime/isr.mjs +43 -0
- package/dist/runtime/kv.d.mts +48 -0
- package/dist/runtime/kv.mjs +106 -0
- package/dist/runtime/log.d.mts +24 -0
- package/dist/runtime/log.mjs +31 -0
- package/dist/runtime/migration-handler-pg.d.mts +6 -0
- package/dist/runtime/migration-handler-pg.mjs +85 -0
- package/dist/runtime/migration-handler.d.mts +19 -0
- package/dist/runtime/migration-handler.mjs +92 -0
- package/dist/runtime/queues.d.mts +7 -0
- package/dist/runtime/queues.mjs +8 -0
- package/dist/runtime/remote/binding-handler.d.mts +15 -0
- package/dist/runtime/remote/binding-handler.mjs +208 -0
- package/dist/runtime/remote/index.d.mts +8 -0
- package/dist/runtime/remote/index.mjs +461 -0
- package/dist/runtime/response.d.mts +14 -0
- package/dist/runtime/response.mjs +30 -0
- package/dist/runtime/sandbox.d.mts +17 -0
- package/dist/runtime/sandbox.mjs +19 -0
- package/dist/runtime/schema-d1.d.mts +1 -0
- package/dist/runtime/schema-d1.mjs +2 -0
- package/dist/runtime/schema-pg.d.mts +1 -0
- package/dist/runtime/schema-pg.mjs +2 -0
- package/dist/runtime/seed.d.mts +30 -0
- package/dist/runtime/seed.mjs +6 -0
- package/dist/runtime/storage.d.mts +7 -0
- package/dist/runtime/storage.mjs +14 -0
- package/dist/runtime/validator.d.mts +2 -0
- package/dist/runtime/validator.mjs +72 -0
- package/dist/runtime/ws-server.d.mts +26 -0
- package/dist/runtime/ws-server.mjs +296 -0
- package/dist/runtime/ws.d.mts +123 -0
- package/dist/runtime/ws.mjs +103 -0
- package/dist/scan-Ba4hFwlH.mjs +324 -0
- package/dist/scan-C6HMEIdW.mjs +318 -0
- package/dist/secret-CeRSukgM.mjs +109 -0
- package/dist/skills-ipldjlKE.mjs +62 -0
- package/dist/standard-schema-9CRjx-uR.d.mts +42 -0
- package/dist/subcommand-prompt-BKjuNAPb.mjs +349 -0
- package/dist/sveltekit.d.mts +20 -0
- package/dist/sveltekit.mjs +61 -0
- package/dist/types-mHOEwpW4.d.mts +57 -0
- package/dist/validate-CaMavMxu.mjs +146 -0
- package/dist/yarn-pnp-BFqMV_bl.mjs +196 -0
- package/getting-started-prompt.txt +26 -0
- package/package.json +322 -30
- package/schema.json +364 -0
- package/skills/migrate-vite-cloudflare-to-void/SKILL.md +175 -0
- package/skills/void/SKILL.md +75 -0
- package/skills/void/command/void.md +7 -0
- package/skills/void/docs/guide/ai.md +235 -0
- package/skills/void/docs/guide/app-types.md +103 -0
- package/skills/void/docs/guide/auth.md +257 -0
- package/skills/void/docs/guide/database/d1.md +106 -0
- package/skills/void/docs/guide/database/postgresql.md +106 -0
- package/skills/void/docs/guide/database.md +418 -0
- package/skills/void/docs/guide/deployment.md +98 -0
- package/skills/void/docs/guide/edge/headers.md +79 -0
- package/skills/void/docs/guide/edge/prerendering.md +83 -0
- package/skills/void/docs/guide/edge/redirects.md +116 -0
- package/skills/void/docs/guide/edge/revalidation.md +131 -0
- package/skills/void/docs/guide/edge/rewrites.md +354 -0
- package/skills/void/docs/guide/edge/static-assets.md +72 -0
- package/skills/void/docs/guide/env-vars.md +298 -0
- package/skills/void/docs/guide/index.md +80 -0
- package/skills/void/docs/guide/jobs.md +91 -0
- package/skills/void/docs/guide/kv.md +107 -0
- package/skills/void/docs/guide/pages-routing/actions-and-forms.md +419 -0
- package/skills/void/docs/guide/pages-routing/head.md +130 -0
- package/skills/void/docs/guide/pages-routing/islands.md +405 -0
- package/skills/void/docs/guide/pages-routing/layouts.md +362 -0
- package/skills/void/docs/guide/pages-routing/loaders.md +267 -0
- package/skills/void/docs/guide/pages-routing/markdown.md +625 -0
- package/skills/void/docs/guide/pages-routing/overview.md +236 -0
- package/skills/void/docs/guide/pages-routing/view-transitions.md +140 -0
- package/skills/void/docs/guide/queues.md +140 -0
- package/skills/void/docs/guide/quickstart.md +233 -0
- package/skills/void/docs/guide/remote-dev.md +67 -0
- package/skills/void/docs/guide/sandboxes.md +82 -0
- package/skills/void/docs/guide/server-routing.md +246 -0
- package/skills/void/docs/guide/ssg.md +74 -0
- package/skills/void/docs/guide/ssr.md +105 -0
- package/skills/void/docs/guide/storage.md +67 -0
- package/skills/void/docs/guide/type-safety.md +179 -0
- package/skills/void/docs/guide/typed-fetch.md +113 -0
- package/skills/void/docs/guide/websockets.md +190 -0
- package/skills/void/docs/index.md +48 -0
- package/skills/void/docs/integrations/agents.md +84 -0
- package/skills/void/docs/integrations/cloudflare.md +284 -0
- package/skills/void/docs/integrations/frameworks/analog.md +182 -0
- package/skills/void/docs/integrations/frameworks/astro.md +197 -0
- package/skills/void/docs/integrations/frameworks/nuxt.md +164 -0
- package/skills/void/docs/integrations/frameworks/overview.md +136 -0
- package/skills/void/docs/integrations/frameworks/react-router.md +137 -0
- package/skills/void/docs/integrations/frameworks/sveltekit.md +191 -0
- package/skills/void/docs/integrations/frameworks/tanstack-start.md +140 -0
- package/skills/void/docs/integrations/hono.md +97 -0
- package/skills/void/docs/integrations/nodejs-bun-deno.md +210 -0
- package/skills/void/docs/node_modules/@iconify/vue/README.md +408 -0
- package/skills/void/docs/node_modules/@iconify/vue/offline/readme.md +5 -0
- package/skills/void/docs/node_modules/@voidzero-dev/vitepress-theme/README.md +103 -0
- package/skills/void/docs/node_modules/oxc-minify/README.md +78 -0
- package/skills/void/docs/node_modules/reka-ui/README.md +80 -0
- package/skills/void/docs/node_modules/vitepress/README.md +28 -0
- package/skills/void/docs/node_modules/vitepress/template/api-examples.md +49 -0
- package/skills/void/docs/node_modules/vitepress/template/index.md +28 -0
- package/skills/void/docs/node_modules/vitepress/template/markdown-examples.md +85 -0
- package/skills/void/docs/node_modules/vitepress-plugin-group-icons/README.md +101 -0
- package/skills/void/docs/node_modules/void/AGENTS.md +204 -0
- package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +15 -0
- package/skills/void/docs/node_modules/void/README.md +89 -0
- package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/CHANGELOG.md +591 -0
- package/skills/void/docs/node_modules/void/node_modules/@clack/prompts/README.md +375 -0
- package/skills/void/docs/node_modules/void/node_modules/@cloudflare/sandbox/README.md +174 -0
- package/skills/void/docs/node_modules/void/node_modules/@cloudflare/vite-plugin/README.md +37 -0
- package/skills/void/docs/node_modules/void/node_modules/@cloudflare/workers-types/README.md +135 -0
- package/skills/void/docs/node_modules/void/node_modules/@electric-sql/pglite/README.md +189 -0
- package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/CHANGELOG.md +143 -0
- package/skills/void/docs/node_modules/void/node_modules/@hono/oauth-providers/README.md +1272 -0
- package/skills/void/docs/node_modules/void/node_modules/@napi-rs/keyring/README.md +19 -0
- package/skills/void/docs/node_modules/void/node_modules/@types/better-sqlite3/README.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@types/node/README.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@types/pg/README.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/README.md +22 -0
- package/skills/void/docs/node_modules/void/node_modules/@typescript/native-preview/vendor/vscode-jsonrpc/README.md +69 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/README.md +152 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/engine-javascript/README.md +9 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@shikijs/transformers/README.md +9 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/@types/node/README.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/CHANGELOG.md +24 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/gray-matter/README.md +565 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-exit/README.md +124 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-anchor/README.md +600 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-attrs/README.md +386 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-container/README.md +95 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/markdown-it-emoji/README.md +101 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathe/README.md +73 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/shiki/README.md +15 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/tinyglobby/README.md +25 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/tsdown/README.md +55 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite/LICENSE.md +2230 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vite/README.md +20 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/vue/README.md +58 -0
- package/skills/void/docs/node_modules/void/node_modules/arktype/README.md +165 -0
- package/skills/void/docs/node_modules/void/node_modules/better-auth/LICENSE.md +20 -0
- package/skills/void/docs/node_modules/void/node_modules/better-auth/README.md +32 -0
- package/skills/void/docs/node_modules/void/node_modules/better-sqlite3/README.md +99 -0
- package/skills/void/docs/node_modules/void/node_modules/blake3-jit/README.md +108 -0
- package/skills/void/docs/node_modules/void/node_modules/drizzle-arktype/README.md +51 -0
- package/skills/void/docs/node_modules/void/node_modules/drizzle-kit/README.md +79 -0
- package/skills/void/docs/node_modules/void/node_modules/drizzle-orm/README.md +44 -0
- package/skills/void/docs/node_modules/void/node_modules/drizzle-valibot/README.md +51 -0
- package/skills/void/docs/node_modules/void/node_modules/drizzle-zod/README.md +65 -0
- package/skills/void/docs/node_modules/void/node_modules/es-module-lexer/README.md +390 -0
- package/skills/void/docs/node_modules/void/node_modules/estree-walker/README.md +48 -0
- package/skills/void/docs/node_modules/void/node_modules/hono/README.md +85 -0
- package/skills/void/docs/node_modules/void/node_modules/ignore/README.md +452 -0
- package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/CHANGELOG.md +76 -0
- package/{LICENSE → skills/void/docs/node_modules/void/node_modules/jsonc-parser/LICENSE.md} +21 -21
- package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/README.md +364 -0
- package/skills/void/docs/node_modules/void/node_modules/jsonc-parser/SECURITY.md +41 -0
- package/skills/void/docs/node_modules/void/node_modules/magic-string/README.md +325 -0
- package/skills/void/docs/node_modules/void/node_modules/ofetch/README.md +398 -0
- package/skills/void/docs/node_modules/void/node_modules/pathe/README.md +73 -0
- package/skills/void/docs/node_modules/void/node_modules/pg/README.md +95 -0
- package/skills/void/docs/node_modules/void/node_modules/pglite-server/LICENSE.md +21 -0
- package/skills/void/docs/node_modules/void/node_modules/pglite-server/README.md +135 -0
- package/skills/void/docs/node_modules/void/node_modules/picocolors/README.md +21 -0
- package/skills/void/docs/node_modules/void/node_modules/tinyglobby/README.md +25 -0
- package/skills/void/docs/node_modules/void/node_modules/tsdown/README.md +55 -0
- package/skills/void/docs/node_modules/void/node_modules/valibot/LICENSE.md +9 -0
- package/skills/void/docs/node_modules/void/node_modules/valibot/README.md +94 -0
- package/skills/void/docs/node_modules/void/node_modules/vite/LICENSE.md +2230 -0
- package/skills/void/docs/node_modules/void/node_modules/vite/README.md +20 -0
- package/skills/void/docs/node_modules/void/node_modules/wrangler/README.md +63 -0
- package/skills/void/docs/node_modules/void/node_modules/zod/README.md +191 -0
- package/skills/void/docs/node_modules/void/skills/migrate-vite-cloudflare-to-void/SKILL.md +175 -0
- package/skills/void/docs/node_modules/void/skills/void/SKILL.md +75 -0
- package/skills/void/docs/node_modules/void/skills/void/command/void.md +7 -0
- package/skills/void/docs/reference/api.md +917 -0
- package/skills/void/docs/reference/cli.md +561 -0
- package/skills/void/docs/reference/config.md +408 -0
- package/skills/void/docs/reference/resource-inference.md +149 -0
- package/skills/void/docs/reference/structure.md +176 -0
- package/.npmignore +0 -29
- package/.travis.yml +0 -9
- package/favicon.ico +0 -0
- package/index.js +0 -14
- package/lib/Job.js +0 -150
- package/lib/Void.js +0 -99
- package/lib/scan.js +0 -19
- package/test/credentials.js +0 -20
- package/test/job.js +0 -64
- package/test/static/dir1/test6.html +0 -0
- package/test/static/dir2/test7.html +0 -0
- package/test/static/dir2/test8.html +0 -0
- package/test/static/dir2/test9.html +0 -0
- package/test/static/test1.html +0 -0
- package/test/static/test2.html +0 -0
- package/test/static/test3.html +0 -0
- package/test/void.js +0 -31
- /package/{test/static/dir1/test4.html → skills/void/docs/integrations/auth-providers.md} +0 -0
- /package/{test/static/dir1/test5.html → skills/void/docs/integrations/payment-processors.md} +0 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Rewrites
|
|
6
|
+
|
|
7
|
+
Define URL rewrites in [`void.json`](../../reference/config) using the `routing.rewrites` field. Keys are source URL patterns, values are destination paths. A rewrite serves content from the destination path **without changing the URL in the browser** — the user sees the original URL, but the server resolves content from the destination.
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"routing": {
|
|
12
|
+
"rewrites": {
|
|
13
|
+
"/": "/en",
|
|
14
|
+
"/docs": "/en/docs",
|
|
15
|
+
"/docs/*": "/en/docs/:splat"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## When to use rewrites
|
|
22
|
+
|
|
23
|
+
Use rewrites instead of [redirects](./redirects) when you want to decouple the **public URL** from the **internal route** without the user seeing a URL change. Common scenarios:
|
|
24
|
+
|
|
25
|
+
- **i18n routing** — serve the default locale at unprefixed paths (`/docs` serves `/en/docs`)
|
|
26
|
+
- **URL restructuring** — reorganize internal route files without changing public URLs or SEO
|
|
27
|
+
- **Vanity URLs** — map `/pricing` to `/marketing/pricing-page` without exposing the internal structure
|
|
28
|
+
- **API versioning** — route `/api/users` to `/api/v3/users` internally so consumers use clean, unversioned endpoints
|
|
29
|
+
- **Incremental migration** — old URL structure continues working at the edge while route handlers move to new paths
|
|
30
|
+
- **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)
|
|
31
|
+
|
|
32
|
+
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.
|
|
33
|
+
|
|
34
|
+
## Rules
|
|
35
|
+
|
|
36
|
+
- **Source patterns** start with `/`. `*` matches any characters including `/`.
|
|
37
|
+
- **Destinations** are strings starting with `/` (only internal paths are supported).
|
|
38
|
+
- `:splat` in the destination is replaced with the portion of the path matched by `*` in the source pattern.
|
|
39
|
+
- When multiple rules match, the **first match wins**. Put more-specific rules above more-general ones (matches Netlify `_redirects` and Vercel `vercel.json` semantics).
|
|
40
|
+
- 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.
|
|
41
|
+
- Unlike redirects, rewrites do **not** change the URL in the browser or send a `Location` header.
|
|
42
|
+
|
|
43
|
+
## Example: i18n routing
|
|
44
|
+
|
|
45
|
+
Define route files only under `[locale]/` and rewrite the default locale to unprefixed paths:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"routing": {
|
|
50
|
+
"rewrites": {
|
|
51
|
+
"/": "/en",
|
|
52
|
+
"/docs": "/en/docs",
|
|
53
|
+
"/docs/*": "/en/docs/:splat"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
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.
|
|
60
|
+
|
|
61
|
+
## Example: vanity URLs
|
|
62
|
+
|
|
63
|
+
Map short marketing URLs to internal route paths:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"routing": {
|
|
68
|
+
"rewrites": {
|
|
69
|
+
"/pricing": "/marketing/pricing-page",
|
|
70
|
+
"/start": "/onboarding/get-started",
|
|
71
|
+
"/jobs": "/company/careers"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Example: API versioning
|
|
78
|
+
|
|
79
|
+
Route unversioned API paths to the current version internally:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"routing": {
|
|
84
|
+
"rewrites": {
|
|
85
|
+
"/api/users": "/api/v3/users",
|
|
86
|
+
"/api/users/*": "/api/v3/users/:splat"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
When v4 ships, update the rewrite — no client-side changes needed.
|
|
93
|
+
|
|
94
|
+
## Example: path restructuring
|
|
95
|
+
|
|
96
|
+
After reorganizing from `/blog/:slug` to `/posts/:slug`, keep the old URLs working:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"routing": {
|
|
101
|
+
"rewrites": {
|
|
102
|
+
"/blog/*": "/posts/:splat"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Unlike a redirect, users on `/blog/hello-world` won't see the URL change — they'll just see the content from `/posts/hello-world`.
|
|
109
|
+
|
|
110
|
+
## Programmatic rewrites in middleware
|
|
111
|
+
|
|
112
|
+
For dynamic rewrite logic — like i18n locale negotiation based on cookies or headers — use `c.rewrite()` in middleware:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { defineMiddleware } from 'void';
|
|
116
|
+
|
|
117
|
+
export default defineMiddleware(async (c, next) => {
|
|
118
|
+
const locale = detectLocale(c.req);
|
|
119
|
+
if (!c.req.path.match(/^\/(en|zh-CN)\//)) {
|
|
120
|
+
return c.rewrite(`/${locale}${c.req.path}`);
|
|
121
|
+
}
|
|
122
|
+
return next();
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`c.rewrite(path)` re-dispatches the request through the router with the new pathname. You must `return` the result — same shape as `c.redirect()`. A queryless destination preserves the incoming query string; a destination with `?` replaces it, so `c.rewrite('/search')` keeps `?q=...` and `c.rewrite('/search?q=all')` forwards exactly `?q=all`.
|
|
127
|
+
|
|
128
|
+
The `path` argument is typed as [`RewriteDestination`](../../reference/api#rewritedestination), a union of your generated route patterns plus `string`. Known route patterns (e.g. `/posts/[id]`, `/en/docs`) surface as autocomplete entries in your editor, while interpolated strings like ``c.rewrite(`/${locale}${c.req.path}`)`` stay accepted by design. Treat it as autocomplete, not a proof of reachability.
|
|
129
|
+
|
|
130
|
+
### Runtime rewrites cannot reach static assets
|
|
131
|
+
|
|
132
|
+
`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.
|
|
133
|
+
|
|
134
|
+
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.
|
|
135
|
+
|
|
136
|
+
The guarded extensions are:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
.png .jpg .jpeg .gif .webp .avif .svg .ico
|
|
140
|
+
.css .js .mjs .cjs
|
|
141
|
+
.woff .woff2 .ttf .otf .eot
|
|
142
|
+
.mp4 .webm .mp3 .wav
|
|
143
|
+
.pdf .txt .xml .json .wasm .map
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`.html` is deliberately **excluded** from this list. A path like `/about.html` is ambiguous — it may be a real SSR/SPA route handler rather than a static file — so banning `.html` would produce false positives for apps that route on explicit `.html` URLs. `c.rewrite('/about.html')` is allowed; if it 404s, the existing behavior stands and the dev `X-Void-Routing` trace header is your debug hook.
|
|
147
|
+
|
|
148
|
+
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.
|
|
149
|
+
|
|
150
|
+
**Loop prevention:** Single-hop rewrite loops are prevented automatically. When `c.rewrite('/foo')` re-dispatches the request, the runtime records the new `Request` in an internal `WeakMap<Request, URL>` (keyed on the `Request` identity, value = pre-rewrite URL) so static routing rules are skipped on the second pass — even if `/foo` would itself match a rewrite rule, it won't rewrite again. The guard is identity-based on the in-memory `Request` object, so client-supplied headers cannot spoof or bypass it. The pre-rewrite URL is exposed to re-dispatched handlers via [`c.originalUrl()`](#original-url-access).
|
|
151
|
+
|
|
152
|
+
What the guard **doesn't** catch are multi-hop user-written loops across separate middleware: middleware A rewrites `/a → /b`, middleware B rewrites `/b → /c`, middleware C rewrites `/c → /a`. Each hop constructs a fresh `Request`, so the per-request guard can't see the cycle. If you chain rewrites across middleware, write each hop so it only rewrites paths that aren't already in its target shape.
|
|
153
|
+
|
|
154
|
+
Also avoid deep rewrite chains for performance: every hop re-runs all middleware from the top, so `/a → /b → /c → /d` costs four router passes.
|
|
155
|
+
|
|
156
|
+
### Performance notes
|
|
157
|
+
|
|
158
|
+
- 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.
|
|
159
|
+
- 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.
|
|
160
|
+
- The WeakMap loop guard is keyed on the `Request` identity and only suppresses the _static rules_ middleware on re-dispatched requests. User middleware is not guarded: if `c.rewrite('/a')` lands on `/a`, and middleware on `/a` calls `c.rewrite('/b')`, that second hop runs — each hop allocates a new `Request`, so the guard never matches. Chain depth is your responsibility.
|
|
161
|
+
|
|
162
|
+
### Side effects in re-dispatched middleware
|
|
163
|
+
|
|
164
|
+
Because every hop replays the full middleware stack, any side-effectful middleware fires twice (or N times for deep chains): DB lookups, session/auth checks, rate-limiter increments, and request loggers all double-count. For example, an auth middleware that logs every request will log twice for every rewritten request.
|
|
165
|
+
|
|
166
|
+
To skip idempotent-unsafe work on the second pass, use `c.isRewritten()`:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
if (c.isRewritten()) return next();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`c.isRewritten()` returns `true` when the request was re-dispatched by a rewrite (whether from a static rule at the edge or `c.rewrite()` in middleware). Internally the framework tracks rewrites in an in-worker `WeakMap<Request, URL>` keyed on `c.req.raw` — the helper just checks whether the current `Request` has an entry. The `X-Void-Original-URL` header is only the wire format between the edge dispatcher and the worker; entry middleware migrates it into the WeakMap once per request, and from that point on the map is the single source of truth.
|
|
173
|
+
|
|
174
|
+
::: tip
|
|
175
|
+
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.
|
|
176
|
+
:::
|
|
177
|
+
|
|
178
|
+
## Original URL access
|
|
179
|
+
|
|
180
|
+
When a request has been rewritten — either by a static rule at the edge or by `c.rewrite()` in middleware — `c.originalUrl()` returns the URL the user originally requested. This is useful for canonical links, locale detection, and building correct hrefs:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
export default defineHandler((c) => {
|
|
184
|
+
const original = c.originalUrl();
|
|
185
|
+
// original is a `URL` instance for the full URL the user requested
|
|
186
|
+
// before the rewrite, or null if the request was not rewritten.
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Internally this reads the pre-rewrite URL from an in-worker `WeakMap<Request, URL>` keyed on `c.req.raw`. When a request crosses the edge, Void's dispatcher sets `X-Void-Original-URL` on the forwarded request; entry middleware migrates that header into the WeakMap once, and every subsequent `c.rewrite()` hop writes straight into the map. The header on the wire is only the hand-off format — in-worker, the WeakMap is the single source of truth, so there's no per-call header parse.
|
|
191
|
+
|
|
192
|
+
## Fallbacks
|
|
193
|
+
|
|
194
|
+
`routing.fallbacks` shares the same shape as `rewrites` but runs **only when no static asset or route matched** — i.e. the request would otherwise return a 404. This lets you add catch-all rewrites without preempting real routes.
|
|
195
|
+
|
|
196
|
+
In Void apps, production dispatch only treats generated no-route 404s as fallback-eligible. 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`.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"routing": {
|
|
201
|
+
"fallbacks": {
|
|
202
|
+
"/*": "/index.html"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Common uses:
|
|
209
|
+
|
|
210
|
+
- **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).
|
|
211
|
+
- **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.
|
|
212
|
+
|
|
213
|
+
Ordering:
|
|
214
|
+
|
|
215
|
+
- `rewrites` are evaluated **before** the static asset lookup; a matching rule always wins.
|
|
216
|
+
- `fallbacks` are evaluated **after** the static asset lookup, only when it would 404.
|
|
217
|
+
|
|
218
|
+
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.
|
|
219
|
+
|
|
220
|
+
### SPA app type + `routing.fallbacks`
|
|
221
|
+
|
|
222
|
+
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**:
|
|
223
|
+
|
|
224
|
+
1. Your `routing.fallbacks` rules are checked first, in the order they appear (first match wins among user rules).
|
|
225
|
+
2. If none of them match, the implicit `/* → /index.html` SPA fallback still fires.
|
|
226
|
+
|
|
227
|
+
So a SPA app can carve out specific paths without losing the SPA shell behavior for everything else:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"routing": {
|
|
232
|
+
"fallbacks": {
|
|
233
|
+
"/docs/*": "/docs.html"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
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).
|
|
240
|
+
|
|
241
|
+
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.json`. 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.
|
|
242
|
+
|
|
243
|
+
## `_redirects` file
|
|
244
|
+
|
|
245
|
+
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:
|
|
246
|
+
|
|
247
|
+
```text
|
|
248
|
+
# plain 200 = fallback (asset-miss only, equivalent to routing.fallbacks)
|
|
249
|
+
/* /index.html 200
|
|
250
|
+
|
|
251
|
+
# 200! with force suffix = always rewrite (equivalent to routing.rewrites)
|
|
252
|
+
/docs/* /en/docs/:splat 200!
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
| File-based form | `void.json` equivalent | Behavior |
|
|
256
|
+
| --------------- | ---------------------- | ------------------------------------------------------------------------ |
|
|
257
|
+
| `... 200` | `routing.fallbacks` | Fires only when no static asset and no route matched (would have 404'd). |
|
|
258
|
+
| `... 200!` | `routing.rewrites` | Always fires, overriding any static asset that would have served. |
|
|
259
|
+
|
|
260
|
+
- `void.json` rules are applied **before** file-based rules. Since the first match wins, `routing.rewrites` / `routing.fallbacks` in `void.json` take precedence.
|
|
261
|
+
- The `_redirects` file can mix 3xx redirects, `200` fallbacks, and `200!` force rewrites. Ordering is preserved within each bucket.
|
|
262
|
+
- 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.
|
|
263
|
+
|
|
264
|
+
## Precedence: `_redirects` vs `void.json`
|
|
265
|
+
|
|
266
|
+
When the same source pattern appears in both a `_redirects` file and `void.json` (`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.
|
|
267
|
+
|
|
268
|
+
Rules are bucketed by phase before merging:
|
|
269
|
+
|
|
270
|
+
- **Pre-asset phase** (always fires, runs before static asset lookup): `routing.redirects` + `routing.rewrites` + `_redirects` 3xx entries + `_redirects` `200!` entries.
|
|
271
|
+
- **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.
|
|
272
|
+
|
|
273
|
+
Within each phase, the order is always: **`void.json` rules first, then `_redirects` file rules**. Because evaluation is first-match-wins, **`void.json` rules override `_redirects` rules for the same source**. This holds even though `_redirects` is "closer to the build output" — intuitions like "file wins" or "whichever I wrote later in the file wins" are both wrong.
|
|
274
|
+
|
|
275
|
+
### Concrete example
|
|
276
|
+
|
|
277
|
+
```text
|
|
278
|
+
# _redirects
|
|
279
|
+
/docs/* /en/docs/:splat 200!
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```json
|
|
283
|
+
// void.json
|
|
284
|
+
{
|
|
285
|
+
"routing": {
|
|
286
|
+
"rewrites": {
|
|
287
|
+
"/docs/*": "/handbook/:splat"
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
A request to `/docs/intro` matches both rules. Merged order is `[void.json: /docs/* → /handbook/:splat, _redirects: /docs/* → /en/docs/:splat]`, so `void.json` wins and the request resolves to `/handbook/intro`.
|
|
294
|
+
|
|
295
|
+
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.json#routing.rewrites`).
|
|
296
|
+
|
|
297
|
+
## How rewrites work
|
|
298
|
+
|
|
299
|
+
**Static rewrites** (`void.json` and `_redirects` file):
|
|
300
|
+
|
|
301
|
+
1. `void deploy` reads rewrite rules from the `_redirects` file (status `200` entries) and `routing.rewrites` in `void.json`, then includes them in the deploy manifest alongside redirect rules.
|
|
302
|
+
2. The platform stores the rules in the KV routing entry for your project.
|
|
303
|
+
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`.
|
|
304
|
+
|
|
305
|
+
**Middleware rewrites** (`c.rewrite()`):
|
|
306
|
+
|
|
307
|
+
1. The request reaches the worker with its original (or edge-rewritten) pathname.
|
|
308
|
+
2. Your middleware calls `c.rewrite(newPath)`, which constructs a new request with the rewritten pathname and re-dispatches it through the Hono router.
|
|
309
|
+
3. The re-dispatched request runs through all middleware and route handlers as if it were a fresh request to the new path.
|
|
310
|
+
|
|
311
|
+
Static rewrites are evaluated at the edge (zero-cost). Middleware rewrites run inside the worker (adds a re-dispatch but enables dynamic logic).
|
|
312
|
+
|
|
313
|
+
## Caveat: client navigation skips rewrites
|
|
314
|
+
|
|
315
|
+
Rewrites run on the server. A full HTTP request to `/docs` resolves through `routing.rewrites` and serves `/en/docs` content. But a client-side `<Link to="/docs">` navigation in Pages mode fetches loader data directly for `/docs` — the Void Router doesn't know about the server's rewrite table, so there's no lookup against `/en/docs` on that path.
|
|
316
|
+
|
|
317
|
+
In practice this only matters when the source and destination have **different loader behavior**. If `/docs` has no route handler but `/en/docs` does, clicking a `<Link to="/docs">` will fail where a fresh page load would succeed. The first render (server) and a subsequent client nav (CSR) to the same URL can diverge.
|
|
318
|
+
|
|
319
|
+
Two mitigations:
|
|
320
|
+
|
|
321
|
+
- Use a plain `<a href="/docs">` when you need the navigation to round-trip through the server (and therefore through rewrites).
|
|
322
|
+
- 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.
|
|
323
|
+
|
|
324
|
+
## ISR cache keys with rewrites
|
|
325
|
+
|
|
326
|
+
If you use [`routing.revalidate`](./revalidation) on a dispatch rewrite (`routing.rewrites`, `routing.fallbacks`, or `_redirects` 200/200!), the cache slot is keyed on the **rewritten** pathname plus the original request URL's pathname. By default, query parameters are dropped from the rewrite variant key to avoid unbounded cache fanout; add `routing.revalidateQueryAllowlist` when selected query params should vary cached output. So a direct request to `/en/docs/foo` and a rewrite from `/docs/foo → /en/docs/foo` cache independently — useful when your worker reads `c.isRewritten()` or `c.originalUrl()`. Middleware `c.rewrite()` runs after ISR lookup, so it does not create a separate ISR variant.
|
|
327
|
+
|
|
328
|
+
`revalidate({ paths })` operates on the rewritten pathname (the slot's primary key). Purging `/en/docs/foo` removes all variants — direct + every rewrite source — under that path. Purging the source path (`/docs/foo`) invalidates nothing, because no slot is written under the source.
|
|
329
|
+
|
|
330
|
+
## Debugging with `X-Void-Routing`
|
|
331
|
+
|
|
332
|
+
During `vite dev`, every response carries an `X-Void-Routing` header that traces how the request was resolved. Open the Network tab in devtools and inspect the response headers:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
X-Void-Routing: redirect[/old] → /new 301 (_redirects:12)
|
|
336
|
+
X-Void-Routing: rewrite[/api/*] → /backend/:splat (void.json#routing.rewrites)
|
|
337
|
+
X-Void-Routing: fallback[/docs/*] → /docs.html (void.json#routing.fallbacks)
|
|
338
|
+
X-Void-Routing: c.rewrite → /new-path (middleware)
|
|
339
|
+
X-Void-Routing: pass-through
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Phases are separated by `→`. The parenthesised source hint points at the exact declaration — a line number for `_redirects`, a config path for `void.json`, 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.
|
|
343
|
+
|
|
344
|
+
::: info What fires in `vite dev`
|
|
345
|
+
`vite dev` applies the full static routing pipeline on every target — `node`, `bun`, `deno`, and the default target alike. `void.json` 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.
|
|
346
|
+
|
|
347
|
+
A few things still only run in the deployed runtime, not `vite dev`:
|
|
348
|
+
|
|
349
|
+
- [ISR caching](./revalidation) (`routing.revalidate`) — served cold in dev, no cached slot warm-ups.
|
|
350
|
+
- Custom-domain rewriting and per-project asset prefixes — dev always runs against the root.
|
|
351
|
+
- AI Gateway metering for `void/ai` calls — dev hits the provider directly.
|
|
352
|
+
|
|
353
|
+
For everything else, the rule that fires in `vite dev` is the rule that will fire after `void deploy`.
|
|
354
|
+
:::
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Static Assets Caching
|
|
6
|
+
|
|
7
|
+
Void serves static assets through Cloudflare's global edge network with sensible cache defaults. This page covers how static assets are cached at the edge.
|
|
8
|
+
|
|
9
|
+
## Hashed assets
|
|
10
|
+
|
|
11
|
+
Files in Vite's `build.assetsDir` (default `assets/`) are produced with content hashes in the filename, such as `assets/app-Ab3xK9.js`. These files are immutable because the filename changes whenever the content changes, so they get aggressive caching:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Cache-Control: public, max-age=31536000, immutable
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Cached at the edge for up to one year. Browsers cache them indefinitely. Because the cache key is unversioned, hashed assets survive across deploys without re-fetching.
|
|
18
|
+
|
|
19
|
+
If your Vite config customizes `build.assetsDir`, Void automatically detects this and applies the immutable optimization to the configured directory:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// vite.config.ts
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
build: {
|
|
25
|
+
assetsDir: 'static', // hashed assets go to dist/client/static/
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
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
|
+
|
|
32
|
+
Void also includes presets for where supported meta frameworks (Astro, Nuxt, SvelteKit, etc.) place their hashed assets, so framework-generated assets enjoy optimal caching out of the box.
|
|
33
|
+
|
|
34
|
+
## Non-hashed assets
|
|
35
|
+
|
|
36
|
+
Everything else such as `index.html`, `favicon.ico`, and `/about` is edge-cached using deploy-versioned cache keys. On each deploy, the version changes and previous cache entries are invalidated automatically, so there is nothing to purge.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Cache-Control: public, s-maxage=31536000, max-age=0, must-revalidate
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Cached at the edge until the next deploy. Browsers always revalidate on the next request.
|
|
43
|
+
|
|
44
|
+
**What gets cached:**
|
|
45
|
+
|
|
46
|
+
- All `GET` requests for non-SSR projects such as SPAs and static sites
|
|
47
|
+
- GET requests with file extensions (`.ico`, `.png`, `.css`, etc.) in SSR projects
|
|
48
|
+
|
|
49
|
+
**What does NOT get cached:**
|
|
50
|
+
|
|
51
|
+
- `/api/*` routes, which always hit the worker
|
|
52
|
+
- SSR-rendered pages (paths without file extensions in SSR projects)
|
|
53
|
+
- Non-GET requests
|
|
54
|
+
- Non-2xx responses
|
|
55
|
+
|
|
56
|
+
### Opting out
|
|
57
|
+
|
|
58
|
+
If your worker serves dynamic content at a URL that looks static (e.g., a dynamically generated image at `/avatar.jpg`), you can prevent caching by setting `Cache-Control: private` or `Cache-Control: no-store` in your response headers. Any response with `Cache-Control` containing `private`, `no-store`, or `no-cache` will bypass the edge cache.
|
|
59
|
+
|
|
60
|
+
## ETags and 304 Not Modified
|
|
61
|
+
|
|
62
|
+
All static asset responses include an `ETag` header derived from the file's content hash in R2. When a browser revalidates a cached resource, it sends `If-None-Match` with the previous ETag. If the file has not changed, the edge returns **304 Not Modified** with no body. That saves bandwidth and speeds up page loads.
|
|
63
|
+
|
|
64
|
+
This happens automatically for all static assets. No configuration is needed.
|
|
65
|
+
|
|
66
|
+
## Custom headers
|
|
67
|
+
|
|
68
|
+
You can override caching headers or add your own for any static asset path using [Custom Headers](./headers).
|
|
69
|
+
|
|
70
|
+
## API routes and SSR pages
|
|
71
|
+
|
|
72
|
+
API responses (`/api/*`) and SSR-rendered pages without file extensions always hit the worker. They are **not** edge-cached by the dispatch layer.
|