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,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Static Site Generation (SSG)
|
|
6
|
+
|
|
7
|
+
Set `output: "static"` in `void.json` to prerender all pages at build time:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"output": "static"
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
When `output` is `"static"`:
|
|
16
|
+
|
|
17
|
+
- All pages default to `prerender = true`, so they are rendered during `vite build` and written as HTML files to `dist/client/`.
|
|
18
|
+
- Use `export const prerender = false` in a page's `.server.ts` to opt out. That page will be server-rendered on request.
|
|
19
|
+
- Dynamic pages without `getPrerenderPaths()` are implicitly not prerendered (the paths aren't known at build time).
|
|
20
|
+
- The build output is self-contained and works for `wrangler deploy`, self-hosting, or `void deploy`.
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
During `vite build`, after both the worker and client bundles are written to disk, Void spins up Miniflare with the built worker and fetches each page. The HTML responses are written to `dist/client/` as static files (e.g. `/about` becomes `dist/client/about.html`).
|
|
25
|
+
|
|
26
|
+
## Per-page overrides
|
|
27
|
+
|
|
28
|
+
Individual pages can opt out of prerendering:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// pages/dashboard.server.ts
|
|
32
|
+
export const prerender = false;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Dynamic pages with `getPrerenderPaths()` are prerendered for the returned param combinations:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// pages/blog/[slug].server.ts
|
|
39
|
+
export async function getPrerenderPaths() {
|
|
40
|
+
return [{ slug: 'hello-world' }, { slug: 'getting-started' }];
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Dynamic pages **without** `getPrerenderPaths()` are not prerendered because the paths are not known at build time. These pages are served dynamically by the worker at runtime.
|
|
45
|
+
|
|
46
|
+
## Comparison with edge prerendering
|
|
47
|
+
|
|
48
|
+
| `output` value | Default prerender | Per-page override | Prerender timing |
|
|
49
|
+
| -------------------- | ----------------- | -------------------------------- | -------------------------- |
|
|
50
|
+
| `"server"` (default) | `false` | `export const prerender = true` | Deploy-time (platform ISR) |
|
|
51
|
+
| `"static"` | `true` | `export const prerender = false` | Build-time (`vite build`) |
|
|
52
|
+
|
|
53
|
+
When `output` is omitted or set to `"server"`, behavior is unchanged. `export const prerender = true` opts individual pages into deploy-time [edge prerendering](./edge/prerendering.md).
|
|
54
|
+
|
|
55
|
+
## Deployment behavior
|
|
56
|
+
|
|
57
|
+
When you run `void deploy` with `output: "static"`, Void inspects the build output to decide the optimal deploy strategy:
|
|
58
|
+
|
|
59
|
+
- **Fully static:** if every page is prerendered and there are no API routes, middleware, cron jobs, or queues, Void deploys as a pure static site with no worker.
|
|
60
|
+
- **Hybrid:** if any pages are not prerenderable, such as dynamic pages without `getPrerenderPaths()` or pages with `export const prerender = false`, a worker is deployed to handle those routes at runtime. Prerendered pages are still served as static assets.
|
|
61
|
+
|
|
62
|
+
You do not need to configure this. Void detects it automatically from your pages and project structure.
|
|
63
|
+
|
|
64
|
+
## Relationship to `inference.appType: "static"`
|
|
65
|
+
|
|
66
|
+
The `inference.appType` field describes app type (SPA, static, void), while `output` controls rendering strategy:
|
|
67
|
+
|
|
68
|
+
| | `inference.appType: "static"` | `output: "static"` |
|
|
69
|
+
| ------------ | ----------------------------------------------- | --------------------------------------------- |
|
|
70
|
+
| **What** | Deploy a pre-built static site (no Void plugin) | Prerender a Void app at build time |
|
|
71
|
+
| **Worker** | None (static assets only) | Only if some pages can't be prerendered |
|
|
72
|
+
| **Use case** | VitePress, plain HTML, external SSG tools | Void apps with mostly or fully static content |
|
|
73
|
+
|
|
74
|
+
When all pages are prerendered and there are no backend features, `output: "static"` produces the same deploy result as `inference.appType: "static"`: pure static assets with no worker. The difference is that `output: "static"` figures this out by analyzing your build output, while `inference.appType: "static"` is a manual declaration for non-Void projects.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Custom SSR
|
|
6
|
+
|
|
7
|
+
Void supports framework-agnostic SSR via explicit server and client entries. This is for advanced use cases where you want full control over rendering and hydration.
|
|
8
|
+
|
|
9
|
+
::: tip
|
|
10
|
+
For most apps, [Pages Routing](./pages-routing/overview) handles SSR automatically. You do not need entry files or hydration code.
|
|
11
|
+
:::
|
|
12
|
+
|
|
13
|
+
## Required entries
|
|
14
|
+
|
|
15
|
+
SSR mode is enabled when both of these exist:
|
|
16
|
+
|
|
17
|
+
- `src/main.ssr.ts` or `src/main.ssr.tsx`
|
|
18
|
+
- `src/main.client.ts` or `src/main.client.tsx`
|
|
19
|
+
|
|
20
|
+
Only one server entry and one client entry may exist.
|
|
21
|
+
If only one side is present, build/deploy fails with a clear error.
|
|
22
|
+
|
|
23
|
+
## Render API
|
|
24
|
+
|
|
25
|
+
`src/main.ssr.ts(x)` must export either:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
render(c: CloudContext, assetTags: RenderAssetTags): Response | Promise<Response>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
or:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
export default defineRender((c, assetTags) => Response | Promise<Response>);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The recommended form is `defineRender(...)` for inferred types.
|
|
38
|
+
|
|
39
|
+
`assetTags` contains the HTML tags for your client assets:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
{
|
|
43
|
+
head: string; // styles/modulepreload/Vite client+preamble (dev)
|
|
44
|
+
body: string; // main client entry script tag
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If no render export is found, build/deploy fails with a clear error.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { renderToString } from 'react-dom/server';
|
|
54
|
+
import { defineRender } from 'void';
|
|
55
|
+
import App from './App';
|
|
56
|
+
|
|
57
|
+
export default defineRender(async (c, assetTags) => {
|
|
58
|
+
const url = new URL(c.req.raw.url);
|
|
59
|
+
const html = renderToString(<App url={url.pathname} />);
|
|
60
|
+
return new Response(
|
|
61
|
+
`<!doctype html>
|
|
62
|
+
<html>
|
|
63
|
+
<head>${assetTags.css}${assetTags.preloads}</head>
|
|
64
|
+
<body>
|
|
65
|
+
<div id="root">${html}</div>
|
|
66
|
+
${assetTags.body}
|
|
67
|
+
</body>
|
|
68
|
+
</html>`,
|
|
69
|
+
{ headers: { 'content-type': 'text/html; charset=utf-8' } },
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`src/main.client.ts(x)` should hydrate/mount your app:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { hydrateRoot } from 'react-dom/client';
|
|
78
|
+
import App from './App';
|
|
79
|
+
|
|
80
|
+
hydrateRoot(document.getElementById('root')!, <App url={window.location.pathname} />);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Client Asset Injection
|
|
84
|
+
|
|
85
|
+
Void no longer mutates your rendered HTML automatically.
|
|
86
|
+
You decide whether and where to inject client asset tags.
|
|
87
|
+
|
|
88
|
+
The `assetTags` values are computed by Void:
|
|
89
|
+
|
|
90
|
+
- In production: from `dist/client/.vite/manifest.json` (entry script, CSS, modulepreload)
|
|
91
|
+
- In dev: includes Vite HMR client and React refresh preamble (when React plugin is active), plus the client entry script
|
|
92
|
+
|
|
93
|
+
## Caching
|
|
94
|
+
|
|
95
|
+
See [Revalidation](./edge/revalidation.md) for stale-while-revalidate caching of SSR pages.
|
|
96
|
+
|
|
97
|
+
## Request flow
|
|
98
|
+
|
|
99
|
+
With SSR enabled:
|
|
100
|
+
|
|
101
|
+
1. `/api/*` requests go to worker API routes
|
|
102
|
+
2. static asset hits are served from R2
|
|
103
|
+
3. unmatched non-API requests fall back to `render(c, assetTags)`
|
|
104
|
+
|
|
105
|
+
Without SSR entries, non-API requests keep SPA static fallback behavior.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Object Storage
|
|
6
|
+
|
|
7
|
+
Void provides a typed `storage` export for [Cloudflare R2](https://developers.cloudflare.com/r2/). Import it from `void/storage` and use the full R2 API directly.
|
|
8
|
+
|
|
9
|
+
## Basic Operations
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { storage } from 'void/storage';
|
|
13
|
+
|
|
14
|
+
// Upload
|
|
15
|
+
await storage.put('uploads/photo.jpg', file, {
|
|
16
|
+
httpMetadata: { contentType: 'image/jpeg' },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Download
|
|
20
|
+
const object = await storage.get('uploads/photo.jpg');
|
|
21
|
+
if (object) {
|
|
22
|
+
const data = await object.arrayBuffer();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Delete
|
|
26
|
+
await storage.delete('uploads/photo.jpg');
|
|
27
|
+
|
|
28
|
+
// List
|
|
29
|
+
const listed = await storage.list({ prefix: 'uploads/' });
|
|
30
|
+
for (const obj of listed.objects) {
|
|
31
|
+
console.log(obj.key, obj.size);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Head (metadata only)
|
|
35
|
+
const head = await storage.head('uploads/photo.jpg');
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The `storage` object is a full `R2Bucket`. Every method from the [Cloudflare R2 API](https://developers.cloudflare.com/r2/api/workers/workers-api-reference/) is available directly, with no wrapper layer.
|
|
39
|
+
|
|
40
|
+
## Serving Files
|
|
41
|
+
|
|
42
|
+
A common pattern is serving uploaded files from an API route:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { storage } from 'void/storage';
|
|
46
|
+
|
|
47
|
+
export const GET = defineHandler(async (c) => {
|
|
48
|
+
const key = c.req.param('key');
|
|
49
|
+
const object = await storage.get(key);
|
|
50
|
+
|
|
51
|
+
if (!object) {
|
|
52
|
+
return c.notFound();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const headers = new Headers();
|
|
56
|
+
object.writeHttpMetadata(headers);
|
|
57
|
+
headers.set('etag', object.httpEtag);
|
|
58
|
+
|
|
59
|
+
return new Response(object.body, { headers });
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## How It Works
|
|
64
|
+
|
|
65
|
+
Unlike the [database](./database.md) and [KV](./kv.md) clients, `storage` doesn't add any abstraction over the underlying API. The R2 API is already well-designed for direct use, so `storage` is simply a lazy reference to `env.STORAGE` that you can import without manually accessing bindings.
|
|
66
|
+
|
|
67
|
+
The `createStorage()` factory exists for testing and for frameworks that manage their own routing. It accepts an `R2Bucket` and returns it directly.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Type Safety
|
|
6
|
+
|
|
7
|
+
Void provides end-to-end type safety across the stack. Types come from your source code and Drizzle schema, so you are not hand-writing or duplicating interfaces.
|
|
8
|
+
|
|
9
|
+
## The Type Pipeline
|
|
10
|
+
|
|
11
|
+
<img src="./type-pipeline.svg" alt="Type pipeline: Drizzle schema flows to DB types, branching into route handlers (typed fetch), page loaders (component props), and page actions (useForm / action())" style="max-width: 720px; width: 100%;" />
|
|
12
|
+
|
|
13
|
+
1. **Database types** come from your [Drizzle schema](./database.md#schema-definition). Column types are known at compile time.
|
|
14
|
+
2. **Route handlers:** return types and validator schemas become the generated `RouteMap`, which the [typed fetch](./typed-fetch) client consumes.
|
|
15
|
+
3. **Page loaders:** return types flow into page components as props via `InferProps`.
|
|
16
|
+
4. **Page actions:** validator schemas flow into `useForm` and `action()` for typed data, errors, and URL autocomplete.
|
|
17
|
+
|
|
18
|
+
All generated types live in `.void/` and update automatically when source files or schema change.
|
|
19
|
+
|
|
20
|
+
## Database → Handler → Client
|
|
21
|
+
|
|
22
|
+
Types flow from your Drizzle schema through handlers to the client without any manual annotations:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// routes/api/users.ts
|
|
26
|
+
export const GET = defineHandler(async (c) => {
|
|
27
|
+
return db.select({ name: users.name, email: users.email }).from(users);
|
|
28
|
+
// ↑ return type inferred from schema columns
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const POST = defineHandler.withValidator({
|
|
32
|
+
body: insertUserSchema, // ← derived from Drizzle schema
|
|
33
|
+
})(async (c, { body }) => {
|
|
34
|
+
return db.insert(users).values(body).returning();
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// Client: types inferred from handlers above
|
|
40
|
+
const users = await fetch('/api/users');
|
|
41
|
+
// ↑ { name: string; email: string }[]
|
|
42
|
+
|
|
43
|
+
await fetch('/api/users', {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
body: { name: 'Alice', email: 'alice@example.com' }, // ← typed from validator
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The Drizzle schema defines database columns, runtime validation, handler input types, and client-side type checking all at once. For endpoints that don't map to a table, use any [Standard Schema](./server-routing.md#manual-validators) library.
|
|
50
|
+
|
|
51
|
+
## Loader → Page Props
|
|
52
|
+
|
|
53
|
+
In [pages mode](./pages-routing/overview), a loader's return type flows into the page component via `InferProps`:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
// pages/users/index.server.ts
|
|
57
|
+
export type Props = InferProps<typeof loader>;
|
|
58
|
+
|
|
59
|
+
export const loader = defineHandler(async (c) => {
|
|
60
|
+
return { users: await db.select().from(users) };
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```vue
|
|
65
|
+
<!-- pages/users/index.vue -->
|
|
66
|
+
<script setup lang="ts">
|
|
67
|
+
import type { Props } from './index.server';
|
|
68
|
+
defineProps<Props>(); // ← { users: { id: number; name: string; ... }[] }
|
|
69
|
+
</script>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
No manual interface is needed. Props stay in sync with whatever the loader returns.
|
|
73
|
+
|
|
74
|
+
## Action → useForm
|
|
75
|
+
|
|
76
|
+
When a page action uses `withValidator()`, the body schema flows through to `useForm`. That gives you typed `data`, typed `errors` keys, and URL autocomplete:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// pages/users/create.server.ts
|
|
80
|
+
export const action = defineHandler.withValidator({
|
|
81
|
+
body: insertUserSchema,
|
|
82
|
+
})(async (c, { body }) => {
|
|
83
|
+
await db.insert(users).values(body);
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const form = useForm('/users/create', { name: '', email: '' });
|
|
89
|
+
// ^ autocompletes ^ must match body schema
|
|
90
|
+
|
|
91
|
+
form.errors.email; // ✓ string | undefined
|
|
92
|
+
form.errors.foo; // ✗ TypeScript error
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
[Named actions](./pages-routing/actions-and-forms#named-actions) work the same way. The `?actionName` suffix selects the right types for that action:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const updateForm = useForm('/users/:id?update', { name: '' }, { params: { id } });
|
|
99
|
+
const deleteForm = useForm('/users/:id?delete', { id: '' }, { params: { id } });
|
|
100
|
+
// Each form's data and errors are typed from their respective validators
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The `action()` helper gets the same type checking. See [Actions & Forms](./pages-routing/actions-and-forms) for the full API.
|
|
104
|
+
|
|
105
|
+
## What Gets Checked
|
|
106
|
+
|
|
107
|
+
| Layer | What's type-checked |
|
|
108
|
+
| ------------ | -------------------------------------------------------------------- |
|
|
109
|
+
| Database | Table columns, value types, insert/update shapes from Drizzle schema |
|
|
110
|
+
| Handlers | `c.env` bindings, validator input, return type |
|
|
111
|
+
| Fetch client | Route paths, HTTP methods, `body`/`query`/`params`, response type |
|
|
112
|
+
| `useForm` | Action URLs, `data` fields, `errors` keys, `reset()` args, `params` |
|
|
113
|
+
| `action()` | Action URLs, `data` payload, `params` |
|
|
114
|
+
|
|
115
|
+
## Serialization
|
|
116
|
+
|
|
117
|
+
Handler return types are transformed via `Serialize<T>` so the client sees what actually arrives over the wire:
|
|
118
|
+
|
|
119
|
+
| Source type | Serialized type |
|
|
120
|
+
| --------------------------------- | ------------------------------------------------------ |
|
|
121
|
+
| `string`, `number`, `boolean` | unchanged |
|
|
122
|
+
| `Date` | `string` |
|
|
123
|
+
| `Response` | excluded (passed through at runtime) |
|
|
124
|
+
| `bigint`, `Function`, `undefined` | excluded |
|
|
125
|
+
| `Array<T>` / `{ key: T }` | recursively serialized (function-valued keys stripped) |
|
|
126
|
+
|
|
127
|
+
## Context Variables
|
|
128
|
+
|
|
129
|
+
Middleware and handlers share typed data through Hono's context variables. Augment `CloudContextVariables` to define your own:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
// middleware/01.auth.ts
|
|
133
|
+
declare module 'void' {
|
|
134
|
+
interface CloudContextVariables {
|
|
135
|
+
requestId: string;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export default defineMiddleware(async (c, next) => {
|
|
140
|
+
c.set('requestId', crypto.randomUUID());
|
|
141
|
+
await next();
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Now `c.get("requestId")` returns `string` everywhere, with no assertion needed. Multiple augmentations across files are [merged together](https://www.typescriptlang.org/docs/handbook/declaration-merging.html).
|
|
146
|
+
|
|
147
|
+
## Setup
|
|
148
|
+
|
|
149
|
+
Extend the generated tsconfig in your project:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"extends": "./.void/tsconfig.json",
|
|
154
|
+
"compilerOptions": {
|
|
155
|
+
"target": "ES2022",
|
|
156
|
+
"module": "ES2022",
|
|
157
|
+
"moduleResolution": "bundler",
|
|
158
|
+
"strict": true,
|
|
159
|
+
"jsx": "react-jsx",
|
|
160
|
+
"types": ["void/env"]
|
|
161
|
+
},
|
|
162
|
+
"include": ["src", "routes", "middleware", "crons", "queues"]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If your project already extends another config, use `void init --tsconfig` so Void can patch the file without dropping existing `files` or `compilerOptions.paths` entries. The resulting config may use TypeScript's multi-extends form:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"extends": ["./tsconfig.base.json", "./.void/tsconfig.json"],
|
|
171
|
+
"compilerOptions": {
|
|
172
|
+
"types": ["void/env"]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The `.void/tsconfig.json` uses `"files"` and `compilerOptions.paths` for generated declarations such as `routes.d.ts`, `db.d.ts`, and `queues.d.ts`. TypeScript inherits those fields, but `files` and `paths` are replaced rather than deeply merged when another config defines them. `void init --tsconfig` handles the common existing-config cases by adding Void's generated files and aliases directly to the root config when needed.
|
|
178
|
+
|
|
179
|
+
Run `void prepare` in CI or after a fresh clone, or let `vite dev` / `vite build` generate the `.void/` files during normal app workflows.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Typed Fetch
|
|
6
|
+
|
|
7
|
+
Void ships a typed `fetch` client that knows every route in your app. Import it from `void/client` and get autocomplete for paths, type-checked request bodies, and fully inferred response types.
|
|
8
|
+
|
|
9
|
+
## Basic Usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { fetch } from 'void/client';
|
|
13
|
+
|
|
14
|
+
// GET: return type inferred from your handler
|
|
15
|
+
const users = await fetch('/api/users');
|
|
16
|
+
|
|
17
|
+
// POST: body type-checked against your handler's validator
|
|
18
|
+
await fetch('/api/users', {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
body: { name: 'Alice', email: 'alice@example.com' },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Dynamic params interpolated from the route pattern
|
|
24
|
+
const user = await fetch('/api/users/:id', {
|
|
25
|
+
params: { id: '42' },
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
No type annotations needed. Everything is inferred from your route handlers.
|
|
30
|
+
|
|
31
|
+
## What Gets Type-Checked
|
|
32
|
+
|
|
33
|
+
The client constrains every part of the request:
|
|
34
|
+
|
|
35
|
+
- **Paths:** only routes defined in `routes/` are accepted. Typos fail at compile time.
|
|
36
|
+
- **Methods:** only methods exported from the route file are allowed. If a file only exports `GET`, passing `method: "POST"` is an error.
|
|
37
|
+
- **Body, query, params:** when your handler uses [`defineHandler.withValidator()`](./server-routing.md#validation), validator schemas become the input types and the client enforces them at the call site.
|
|
38
|
+
- **Response type:** the return type matches what your handler returns after [JSON serialization](./type-safety.md#serialization).
|
|
39
|
+
|
|
40
|
+
## Query Parameters
|
|
41
|
+
|
|
42
|
+
Pass `query` for GET requests with search parameters:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// routes/api/search.ts
|
|
46
|
+
export const GET = defineHandler.withValidator({
|
|
47
|
+
query: z.object({ q: z.string(), page: z.string().optional() }),
|
|
48
|
+
})((c, { query }) => {
|
|
49
|
+
return { results: [], term: query.q };
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Client: query is type-checked
|
|
53
|
+
const data = await fetch('/api/search', {
|
|
54
|
+
query: { q: 'hello' },
|
|
55
|
+
});
|
|
56
|
+
// data: { results: never[]; term: string }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Error Handling
|
|
60
|
+
|
|
61
|
+
Non-2xx responses throw a `FetchError`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { fetch, FetchError } from 'void/client';
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await fetch('/api/users/:id', { params: { id: '999' } });
|
|
68
|
+
} catch (e) {
|
|
69
|
+
if (e instanceof FetchError) {
|
|
70
|
+
console.log(e.status); // 404
|
|
71
|
+
console.log(e.data); // parsed response body
|
|
72
|
+
console.log(e.response); // raw Response
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Options
|
|
78
|
+
|
|
79
|
+
| Option | Type | Description |
|
|
80
|
+
| --------- | ------------------------ | ---------------------------------------------------------- |
|
|
81
|
+
| `method` | `string` | HTTP method. Defaults to `"GET"`. |
|
|
82
|
+
| `body` | `object` | Request body (auto-serialized as JSON). |
|
|
83
|
+
| `query` | `Record<string, string>` | Query string parameters. |
|
|
84
|
+
| `params` | `Record<string, string>` | URL path parameters (`:id` segments). |
|
|
85
|
+
| `headers` | `HeadersInit` | Additional request headers. |
|
|
86
|
+
| `signal` | `AbortSignal` | Abort signal. |
|
|
87
|
+
| `baseURL` | `string` | Base URL prepended to the path. Useful for external calls. |
|
|
88
|
+
| `retry` | `number` | Number of retry attempts (default: 1 for GET). |
|
|
89
|
+
| `timeout` | `number` | Request timeout in milliseconds. |
|
|
90
|
+
|
|
91
|
+
## Isomorphic Fetch During SSR
|
|
92
|
+
|
|
93
|
+
`fetch()` from `void/client` works during server-side rendering and inside route handlers without an HTTP round-trip. In the worker environment, it calls your Hono app directly through `app.fetch()` and skips the network entirely.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// src/main.ssr.tsx
|
|
97
|
+
import { fetch } from "void/client";
|
|
98
|
+
import { defineRender } from "void";
|
|
99
|
+
|
|
100
|
+
export default defineRender(async (c, assetTags) => {
|
|
101
|
+
// This calls your API route directly with no HTTP request
|
|
102
|
+
const data = await fetch("/api/users");
|
|
103
|
+
|
|
104
|
+
const html = renderToString(<App users={data} />);
|
|
105
|
+
return new Response(html, {
|
|
106
|
+
headers: { "content-type": "text/html" },
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Automatic header forwarding**: `cookie` and `authorization` headers from the incoming request are automatically forwarded to subrequests, so authentication context is preserved. If you pass these headers explicitly, your values take precedence.
|
|
112
|
+
|
|
113
|
+
**How it works**: In the browser, `fetch()` uses the normal HTTP client. In the worker, Void redirects the import to a virtual module that calls `app.fetch()` directly using the Hono app instance. AsyncLocalStorage threads the outer request context so headers and `waitUntil()` work correctly.
|