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,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Environment Variables
|
|
6
|
+
|
|
7
|
+
Void provides typed, validated environment variables via a single `env.ts` file at the root of your project. The schema serves three purposes:
|
|
8
|
+
|
|
9
|
+
1. **Runtime validation** — declared keys are validated on first access; missing/invalid values produce a clear error
|
|
10
|
+
2. **TypeScript** — `env.X` and `c.env.X` become typed automatically (no manual interface to maintain)
|
|
11
|
+
3. **Deploy safety** — `void deploy` refuses to upload if a required key is missing from `.env*` files or remote secrets
|
|
12
|
+
|
|
13
|
+
## `env.ts` (recommended)
|
|
14
|
+
|
|
15
|
+
Place an `env.ts` at your project root and call `defineEnv` once:
|
|
16
|
+
|
|
17
|
+
::: tip Scaffolded on `void init`
|
|
18
|
+
If you already have `.env` or `.env.example` files when you run `void init`, Void generates an `env.ts` seeded from those keys. Inference is conservative — values that unambiguously parse as a boolean (`true`/`false`), an `http(s)://` URL, or a small integer get typed accordingly; everything else stays `string()`. The generated file carries a banner comment asking you to review and tighten types before running dev.
|
|
19
|
+
:::
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// env.ts
|
|
23
|
+
import { defineEnv, string, number, boolean, oneOf, url } from 'void/env';
|
|
24
|
+
|
|
25
|
+
export default defineEnv({
|
|
26
|
+
STRIPE_KEY: string(),
|
|
27
|
+
PORT: number().default(3000),
|
|
28
|
+
NODE_ENV: oneOf(['development', 'production']),
|
|
29
|
+
WEBHOOK_URL: url(),
|
|
30
|
+
DEBUG: boolean().optional(),
|
|
31
|
+
VITE_APP_TITLE: string(),
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then read values from anywhere in your app:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { env } from 'void/env';
|
|
39
|
+
|
|
40
|
+
console.log(env.STRIPE_KEY); // typed as string
|
|
41
|
+
console.log(env.PORT); // typed as number
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`c.env.STRIPE_KEY` inside Hono handlers is also typed once the schema is declared.
|
|
45
|
+
|
|
46
|
+
### Built-in helpers
|
|
47
|
+
|
|
48
|
+
`string()`, `number()`, `boolean()`, `url()`, `email()`, `oneOf([...])`, `json<T>()` cover the common cases. Each helper returns a Standard Schema-conformant builder with `.optional()` and `.default(value)` modifiers.
|
|
49
|
+
|
|
50
|
+
For richer validation (regex, transforms, branded types), drop in any [Standard Schema](https://standardschema.dev) library — valibot, zod, or arktype work out of the box, and you can mix them with the built-ins:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import * as v from 'valibot';
|
|
54
|
+
|
|
55
|
+
export default defineEnv({
|
|
56
|
+
STRIPE_KEY: string(), // built-in
|
|
57
|
+
WEBHOOK_URL: v.pipe(v.string(), v.url(), v.endsWith('/webhook')), // valibot
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Client vs server
|
|
62
|
+
|
|
63
|
+
Keys matching Vite's `envPrefix` (default `VITE_`) are exposed to client code; Vite enforces this at the bundler level. All other keys are server-only — referencing one from a client module fails the build with a file:line:col error from `void:env-client-guard`, and the runtime proxy still throws as a backstop.
|
|
64
|
+
|
|
65
|
+
If your project sets a custom `envPrefix` (e.g. `'PUBLIC_'` or an array like `['PUBLIC_', 'NEXT_PUBLIC_']`) in `vite.config.ts`, the build guard honours it automatically. Schema keys that match any configured prefix pass the client check.
|
|
66
|
+
|
|
67
|
+
### Build-time constant folding
|
|
68
|
+
|
|
69
|
+
In production client builds, static reads of a client-exposed key are folded into string literals so the bundler can dead-code-eliminate conditional branches — exactly what Vite already does for `import.meta.env.MODE`.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// source
|
|
73
|
+
import { env } from 'void/env';
|
|
74
|
+
if (env.MODE === 'production') {
|
|
75
|
+
initAnalytics();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// production client bundle (after folding + DCE)
|
|
79
|
+
initAnalytics();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Rules:
|
|
83
|
+
|
|
84
|
+
- **Client bundles only.** Server / SSR / worker code keeps the full runtime proxy so secrets, runtime-injected vars, and HMR keep working.
|
|
85
|
+
- **Prefix-gated.** Only keys matching Vite's `envPrefix` (default `VITE_`, honours custom string or array config) are ever folded — server-only keys never appear as literals in client output.
|
|
86
|
+
- **Static access only.** `env.FOO` and `env["FOO"]` get folded. Dynamic access (`env[key]`), destructuring, and reassignment fall back to the runtime proxy.
|
|
87
|
+
- **Values come from** the same source Vite already uses for `import.meta.env`: `.env*` files (respecting `envDir`) plus schema `.default(x)` values for missing keys. Unknown keys are left untouched and the proxy serves them at runtime.
|
|
88
|
+
- **Build-only.** Dev keeps going through the proxy so editing `.env.local` takes effect without a full rebuild.
|
|
89
|
+
- **Zero-config.** No flags, no opt-in — just works.
|
|
90
|
+
|
|
91
|
+
### Vite `define` interop
|
|
92
|
+
|
|
93
|
+
Vite's `define` option performs build-time literal replacement (e.g. `define: { 'process.env.VERSION': '"1.0"' }`). It does **not** flow through schema validation or the typed `env` proxy — using both `define` and `defineEnv` for the same name causes silent skew (compiled client code sees the define value, runtime proxy and validation see the schema source). Void emits a warning at config time when this overlap is detected.
|
|
94
|
+
|
|
95
|
+
### `envDir`
|
|
96
|
+
|
|
97
|
+
Void honours Vite's `envDir` option for `.env` file loading (both the worker `vars` injection and dev-time validation). Defaults to the project root. The deploy CLI still reads from the project root regardless — keep production `.env` files there.
|
|
98
|
+
|
|
99
|
+
### Imports inside `env.ts`
|
|
100
|
+
|
|
101
|
+
`env.ts` is loaded outside Vite's plugin pipeline — the dev plugin and the deploy CLI both read it through Node's native `import()`. That means only **relative imports** (`./shared/env-keys`) and **bare package names** (`zod`, `valibot`) resolve. Known limitations:
|
|
102
|
+
|
|
103
|
+
- **tsconfig path aliases are not resolved.** Writing `import { X } from "@/shared/env-keys"` or `import { Y } from "~/foo"` inside `env.ts` fails to load; Void detects this and re-throws with a hint pointing at the offending specifier. Use a relative import instead.
|
|
104
|
+
- **Custom Vite plugins don't run.** Anything that needs Vite's transform pipeline (SVG-as-component, GraphQL loaders, virtual modules, etc.) won't apply to `env.ts`. Keep this file to pure TS + schema declarations.
|
|
105
|
+
|
|
106
|
+
In practice `env.ts` should only import schema helpers from `void/env` and — at most — a shared constants file via a relative path.
|
|
107
|
+
|
|
108
|
+
## `.env` files
|
|
109
|
+
|
|
110
|
+
Void uses Vite's standard `.env` convention to populate the schema:
|
|
111
|
+
|
|
112
|
+
| File | Loaded in dev | Shipped on deploy |
|
|
113
|
+
| ----------------------- | ------------- | ------------------ |
|
|
114
|
+
| `.env` | yes | yes (`plain_text`) |
|
|
115
|
+
| `.env.local` | yes | no |
|
|
116
|
+
| `.env.production` | yes | yes (`plain_text`) |
|
|
117
|
+
| `.env.production.local` | yes | no |
|
|
118
|
+
|
|
119
|
+
`.local` files are gitignored by convention — use them for secrets you don't want in source control.
|
|
120
|
+
|
|
121
|
+
### Dotenv variable expansion
|
|
122
|
+
|
|
123
|
+
Values can reference other keys defined in the same (or earlier-precedence) `.env` file using `${VAR}` or `$VAR`:
|
|
124
|
+
|
|
125
|
+
```ini
|
|
126
|
+
# .env
|
|
127
|
+
BASE_URL=https://api.example.com
|
|
128
|
+
API_URL=${BASE_URL}/v1 # → "https://api.example.com/v1"
|
|
129
|
+
BUILD=$BASE_URL/build.json # bare form also works
|
|
130
|
+
LITERAL=\$NOT_EXPANDED # → "$NOT_EXPANDED" (backslash-escape)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Nested references resolve transitively (`A=${B}`, `B=${C}`, `C=value` → `A=value`). Cycles are detected and leave the raw literal (Void emits a warning rather than looping). Missing references expand to the empty string.
|
|
134
|
+
|
|
135
|
+
Expansion runs identically across every path that reads your `.env*` files: the runtime typed `env` proxy, `void env check`, `void deploy`, and the `void init` scaffold inference. Importantly, references **only** resolve against values declared in the `.env*` files themselves — shell `process.env` values are not substituted in. This stops a developer-machine variable from silently materialising in committed examples or deploy manifests. (Vite's own `loadEnv` does consult `process.env` during expansion for the dev-server path; Void filters the shell pollution back out via `filterLoadedEnv` so the observable surface stays the same.)
|
|
136
|
+
|
|
137
|
+
## Production secrets
|
|
138
|
+
|
|
139
|
+
For values that should never live in a `.env` file (Stripe keys, OAuth secrets, etc.), upload them as encrypted secret bindings on your deployed worker:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
void secret put STRIPE_KEY # prompts for value
|
|
143
|
+
void secret put STRIPE_KEY < key.txt # from stdin
|
|
144
|
+
void secret sync .env.production # bulk upload from a dotenv file
|
|
145
|
+
void secret list
|
|
146
|
+
void secret delete STRIPE_KEY
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Remote secrets count as "present" for deploy validation — `void deploy` checks both `.env*` and the remote secret list before uploading.
|
|
150
|
+
|
|
151
|
+
## Defaults
|
|
152
|
+
|
|
153
|
+
Schema defaults from `defineEnv({ PORT: number().default(3000) })` flow into both:
|
|
154
|
+
|
|
155
|
+
- The typed `env` proxy: `env.PORT` returns `3000` when no value is set.
|
|
156
|
+
- Worker bindings (`c.env.PORT`, `process.env.PORT` on Node target): the
|
|
157
|
+
default value is injected as a stringified `var` in dev, in `void deploy`
|
|
158
|
+
manifests, and in prerender bindings, so any code path that reads the raw
|
|
159
|
+
env sees the same fallback.
|
|
160
|
+
|
|
161
|
+
User-provided `.env` / shell values always win — defaults only fill gaps.
|
|
162
|
+
|
|
163
|
+
## Validation behavior
|
|
164
|
+
|
|
165
|
+
| Phase | Behavior |
|
|
166
|
+
| -------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
167
|
+
| Dev server start | Warns about missing/invalid keys (does not block). |
|
|
168
|
+
| First runtime access | Throws `EnvValidationError` with the failing key and reason. |
|
|
169
|
+
| `void env check` | Validates `.env` + `.env.production` (and optionally remote secrets with `--remote`); exits non-zero on failure. |
|
|
170
|
+
| `void deploy` | Hard-errors before upload if any required key is missing from the union of `.env*` and remote secrets. |
|
|
171
|
+
|
|
172
|
+
Use `void env check --remote` in CI before deploying — it catches missing prod secrets without running a full build.
|
|
173
|
+
|
|
174
|
+
## Manual type regeneration
|
|
175
|
+
|
|
176
|
+
Types are auto-generated to `.void/env.d.ts` on dev server start and whenever `env.ts` changes. To regenerate manually (e.g. after a fresh clone):
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
void env types
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Scaffolding `.env.example`
|
|
183
|
+
|
|
184
|
+
Generate (or refresh) a `.env.example` from your `env.ts` schema:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
void env example # creates or refreshes the void-managed block
|
|
188
|
+
void env example --force # silences the "appended block" notice (use in CI)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`void env example` manages a marker-delimited block inside `.env.example` — anything outside the markers (custom CI tokens, build flags, comments) is preserved verbatim across refreshes. The block looks like this:
|
|
192
|
+
|
|
193
|
+
```ini
|
|
194
|
+
# >>> void env: managed block — do not edit between markers <<<
|
|
195
|
+
# Run `void env example` to refresh.
|
|
196
|
+
# required
|
|
197
|
+
STRIPE_KEY=
|
|
198
|
+
# with defaults
|
|
199
|
+
PORT=3000
|
|
200
|
+
# >>> end void env <<<
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
On first run, Void writes a fresh `.env.example` containing only the marker block. On subsequent runs, only the lines between the markers are replaced. If the file already exists with no markers, the block is appended at the end and Void prints a one-line notice (suppressed by `--force`).
|
|
204
|
+
|
|
205
|
+
The managed block is grouped into `required`, `with defaults`, and `optional` sections. Keys defined via `oneOf([...])` get a leading `# enum: A | B | C` comment listing the valid values, and keys with a `.default(x)` are prefilled. Commit the `.env.example` — it's the single source of truth for teammates and coding agents setting up the project.
|
|
206
|
+
|
|
207
|
+
## Secret redaction
|
|
208
|
+
|
|
209
|
+
Env validation errors, dev-server warnings, and CLI reports automatically replace secret-looking values with `<redacted>` before they surface. This keeps tokens out of Discord screenshots, GitHub issues, and CI logs when you paste an `EnvValidationError` stack.
|
|
210
|
+
|
|
211
|
+
Redaction is belt-and-braces:
|
|
212
|
+
|
|
213
|
+
1. **Explicit override.** The built-in helpers support `.secret()` and `.public()` modifiers, and they win over every heuristic:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
export default defineEnv({
|
|
217
|
+
STRIPE_KEY: string().secret(), // always redacted
|
|
218
|
+
PUBLIC_KEY: string().public(), // never redacted (opts out of the KEY heuristic)
|
|
219
|
+
PORT: number(),
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Third-party Standard Schema validators (valibot, zod, arktype) can't carry this flag — they fall back to the heuristics below.
|
|
224
|
+
|
|
225
|
+
2. **Key-name heuristic.** Values for keys matching `/KEY|TOKEN|SECRET|PASSWORD|PASS(WD)?|CREDENTIAL|PRIVATE|AUTH|BEARER|APIKEY|DSN/i` are redacted by default.
|
|
226
|
+
|
|
227
|
+
3. **Value-content heuristic.** Even on neutral keys, values that start with `sk_` / `pk_` / `ghp_` / `xoxb-` / `AKIA` or look like a ≥24-char high-entropy string are redacted. This catches wrong-file typos like `PORT=sk_live_abc123`, where the key is innocent but the value is a Stripe key.
|
|
228
|
+
|
|
229
|
+
The **key name itself is never masked** — you need it to locate the offending entry.
|
|
230
|
+
|
|
231
|
+
For local debugging, set `VOID_ENV_UNMASK=1` in your shell to see raw values in error output. On activation Void prints a one-shot notice to stderr so the loosened output is visibly attributed; unset it before sharing logs.
|
|
232
|
+
|
|
233
|
+
## Using `env.ts` with a meta-framework
|
|
234
|
+
|
|
235
|
+
The full env story works on every framework `voidPlugin()` supports — typegen, leak guard, folding, runtime reads on both server and client, plus all CLI-level features.
|
|
236
|
+
|
|
237
|
+
| Framework | `env.ts` typegen | Client leak guard | Constant folding | `env` on server | `env` on client | `void env check` + deploy gate |
|
|
238
|
+
| ----------------- | :--------------: | :---------------: | :--------------: | :-----------------: | :---------------------: | :----------------------------: |
|
|
239
|
+
| Void (Pages mode) | yes | yes | yes | yes | yes (`VITE_*`) | yes |
|
|
240
|
+
| TanStack Start | yes | yes | yes | yes | yes (`VITE_*`) | yes |
|
|
241
|
+
| React Router v7 | yes | yes | yes | yes (in `loader()`) | yes (`VITE_*`) | yes |
|
|
242
|
+
| SvelteKit | yes | yes | yes | yes (runtime proxy) | yes (`VITE_*`) | yes |
|
|
243
|
+
| Nuxt | yes | yes | yes | yes | yes (`VITE_*`) | yes |
|
|
244
|
+
| Analog | yes | yes | yes | yes | yes (`VITE_*`) | yes |
|
|
245
|
+
| Astro | yes | yes | yes | yes | yes (`envPrefix`-gated) | yes |
|
|
246
|
+
|
|
247
|
+
What you always get, regardless of framework:
|
|
248
|
+
|
|
249
|
+
- `void env check [--remote]` validates `.env*` files + remote secret names against the schema.
|
|
250
|
+
- `void deploy` hard-fails before upload when any required key is missing.
|
|
251
|
+
- `void env example` generates the marker-delimited `.env.example` block.
|
|
252
|
+
- `void init` scaffolds `env.ts` from existing dotenv files.
|
|
253
|
+
- Typed `.void/env.d.ts` so `env.X` autocompletes in editors.
|
|
254
|
+
- `void:env-client-guard` fails the build with a file:line:col error when a server-only key is referenced from a client module.
|
|
255
|
+
- Build-time constant folding for `envPrefix`-matched reads in the client bundle.
|
|
256
|
+
|
|
257
|
+
On Nuxt / Analog / Astro, `import { env }` works server-side through the same runtime proxy used on Class A targets — the proxy reads from the worker's `env` binding at request time. You're free to keep using the framework's native mechanism (`useRuntimeConfig()`, `event.context.cloudflare.env`, `import { env } from "cloudflare:workers"`, `Astro.locals.runtime.env`, `astro:env`) wherever it fits better — but you no longer _have_ to.
|
|
258
|
+
|
|
259
|
+
## Migration from untyped `c.env`
|
|
260
|
+
|
|
261
|
+
If you don't have an `env.ts`, `c.env.X` continues to work as `unknown` — no breaking change. To opt in, create `env.ts`, declare your keys, and the existing call sites become typed automatically. The recommended access pattern for new code is `import { env } from "void/env"`.
|
|
262
|
+
|
|
263
|
+
## How it compares
|
|
264
|
+
|
|
265
|
+
`void/env` is a Void-only module — it isn't a standalone env library you'd pull into a Nuxt, SvelteKit, or bare-Vite project. This section exists so you can see what the integrated Void env story includes relative to what you'd otherwise assemble yourself on those stacks, not as a pick-one-of-many benchmark.
|
|
266
|
+
|
|
267
|
+
Most frameworks ship _parts_ of an env story — loading, typing, validation, deploy checks — but stitching them together is left to the user. Here's how Void's single `env.ts` stacks up against the tools people reach for on other stacks today.
|
|
268
|
+
|
|
269
|
+
| Feature | Void `env.ts` | `@t3-oss/env` | Astro `astro:env` | SvelteKit `$env` | Vite `import.meta.env` | Nuxt `runtimeConfig` | `dotenv` + `zod` (DIY) |
|
|
270
|
+
| ----------------------------------------------- | :-----------: | :-----------: | :---------------: | :--------------: | :--------------------: | :------------------: | :--------------------: |
|
|
271
|
+
| Single schema file | yes | yes | yes (config) | no | no | partial | yes |
|
|
272
|
+
| Runtime validation | yes | yes | yes | no | no | no | yes |
|
|
273
|
+
| Auto-generated types | yes | yes | yes | yes | manual `env.d.ts` | yes (aug.) | manual |
|
|
274
|
+
| Bring-your-own validator (Standard Schema) | yes | yes | no | no | no | no | yes |
|
|
275
|
+
| Built-in helpers without installing a validator | yes | no | yes | — | — | — | no |
|
|
276
|
+
| Build-time guard against leaking server env | yes | yes | yes | yes | prefix only | prefix only | no |
|
|
277
|
+
| Custom `envPrefix` honoured | yes | yes | n/a | n/a | yes | n/a | n/a |
|
|
278
|
+
| Schema defaults flow into runtime bindings | yes | partial | yes | no | no | partial | manual |
|
|
279
|
+
| Async validators | yes | no | no | no | no | no | yes |
|
|
280
|
+
| `.env.example` scaffolding from schema | yes | no | no | no | no | no | no |
|
|
281
|
+
| Deploy-time check (local + remote secrets) | yes | no | no | no | no | no | no |
|
|
282
|
+
| Automatic secret redaction in validation errors | yes | no | no | no | no | no | no |
|
|
283
|
+
| Cloudflare secret store integration | yes | no | no | no | no | no | no |
|
|
284
|
+
| Zero-config (auto-discovered, no plugin wiring) | yes | no | yes | yes | yes | yes | no |
|
|
285
|
+
| Schema scaffolded from existing `.env` files | yes | no | no | no | no | no | no |
|
|
286
|
+
| `${VAR}` expansion, no `process.env` leakage | yes | no | no | no | partial (leaks) | no | needs plugin |
|
|
287
|
+
|
|
288
|
+
### Where Void differs
|
|
289
|
+
|
|
290
|
+
- **One file, one call.** `defineEnv({...})` is the whole API surface. `@t3-oss/env` is the closest analogue but splits into `server`/`client`/`runtimeEnv` blocks and expects you to hand it `process.env`. Void auto-discovers `env.ts`, loads `.env*` via Vite's own resolver (honouring `envDir`), and wires the result into the plugin, the worker bundle, the CLI, and generated types without extra config.
|
|
291
|
+
- **Helpers _and_ Standard Schema, mixable.** Most solutions force a choice: either Zod-only (t3-env, many DIY setups), or a bespoke helper DSL with no escape hatch (Astro, SvelteKit). Void ships `string()`, `number()`, `boolean()`, `url()`, `email()`, `oneOf()`, `json<T>()` so a small project pulls in zero validator dependencies, and any [Standard Schema](https://standardschema.dev) library (valibot, zod, arktype) drops into the same object for richer rules.
|
|
292
|
+
- **Build-time leak prevention with source locations.** The `void:env-client-guard` plugin walks client module graphs and fails the build with a file:line:col pointer when a non-prefixed key is imported from the browser bundle. Vite on its own only enforces this through the _prefix convention_ — a renamed variable silently leaks. Void enforces both the prefix rule and the schema's server/client split.
|
|
293
|
+
- **Build-time constant folding for client reads.** Production client builds inline static `env.VITE_FOO` reads as literals so `if (env.MODE === 'production') { … }` branches tree-shake the same way `import.meta.env.MODE` already does. Server / SSR / worker code keeps the runtime proxy untouched, and only prefix-matching keys are ever folded — no schema-required validation is bypassed because the same values still flow through `defineEnv` on boot.
|
|
294
|
+
- **Defaults propagate everywhere.** `PORT: number().default(3000)` fills the typed proxy, the worker `vars` block in dev, the deploy manifest, and the prerender bindings — so `process.env.PORT`, `c.env.PORT`, and `env.PORT` all return `3000` when unset. t3-env and Nuxt only surface defaults in the typed object; the raw `process.env` still reads `undefined`.
|
|
295
|
+
- **Deploy is the enforcement point.** `void deploy` unions `.env*` with the remote secret list and hard-fails on any missing required key _before_ upload. No competing solution in the table treats deploy as a validation gate — the closest substitute is a handwritten CI script calling `zod.parse(process.env)`, which doesn't know about the target platform's remote secrets.
|
|
296
|
+
- **`.env.example` is generated, not maintained by hand.** `void env example` writes a marker-delimited block grouped into `required`, `with defaults`, `optional`, with enum hints for `oneOf` keys. Everything outside the markers (team CI tokens, comments) survives refresh. No other tool in the comparison ships this.
|
|
297
|
+
- **Async validators.** A validator can return a `Promise` — useful for probing a URL, fetching a JWKS, or resolving a secret reference at startup. t3-env and schema-based systems assume synchronous parsing.
|
|
298
|
+
- **Onboarding from an existing `.env`.** `void init` detects pre-existing dotenv files and writes a seeded `env.ts` with conservative type inference (boolean/URL/number/string) and a banner comment prompting you to tighten the guesses. Every other solution in the table requires you to hand-write the schema from scratch even when your team already has a `.env.example` committed.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# What is Void?
|
|
6
|
+
|
|
7
|
+
This guide explains how Void fits together before you dive into individual features. It starts with the mental model, then moves into routing, data, platform features, and deployment. If you want to get something running first, jump to [Quickstart](./quickstart).
|
|
8
|
+
|
|
9
|
+
Void combines a Vite plugin, a backend SDK, and a deployment platform. Add one plugin to your app, and the imports in your code drive the infrastructure around it. A database, key-value store, object storage, AI inference, and deployment all line up without a separate layer of config files or dashboard setup.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install -D vite void
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineConfig } from 'vite';
|
|
17
|
+
import { voidPlugin } from 'void';
|
|
18
|
+
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
plugins: [voidPlugin()],
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
void deploy
|
|
26
|
+
# app live at <your-subdomain>.void.app!
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## The idea
|
|
30
|
+
|
|
31
|
+
With most stacks, your app and its platform do not talk to each other directly. You end up stitching them together with config files, environment setup, resource provisioning, and deployment scripts. Once the app is live, caching, scaling, and limits often live in a separate control plane too.
|
|
32
|
+
|
|
33
|
+
Void closes that gap. It connects your app directly to the platform through Vite. Import `db` from `void/db` and you have a database in local development plus the matching production resource on deploy. The same idea applies to `kv`, `storage`, queues, and AI. In most cases, the code already describes what the platform needs to provision.
|
|
34
|
+
|
|
35
|
+
This is what it unlocks:
|
|
36
|
+
|
|
37
|
+
- **Write code, not config:** no infrastructure files, no dashboard clicks, and no manual resource declarations. Your imports are the contract.
|
|
38
|
+
- **Types from database to frontend:** your Drizzle schema defines DB types, route handlers infer return types, and the [typed fetch client](./typed-fetch.md) checks calls at the usage site. One [Standard Schema](https://standardschema.dev/) validator can drive both runtime validation and compile-time types.
|
|
39
|
+
- **Real runtime in development:** `vite dev` runs your server code in the same runtime used in production, with local database, KV, and storage.
|
|
40
|
+
- **Deploy that understands the app:** `void deploy` reads your migrations, provisions the resources you actually use, and ships the result to the edge.
|
|
41
|
+
|
|
42
|
+
## The platform
|
|
43
|
+
|
|
44
|
+
Void deploys to [Cloudflare Workers](https://developers.cloudflare.com/workers/). Your server code runs at the edge, close to users, and scales without extra platform work on your side.
|
|
45
|
+
|
|
46
|
+
- [Static assets](./edge/static-assets) are served from the edge with proper cache headers. [Incremental revalidation](./edge/revalidation) (ISR) and [prerendering](./edge/prerendering) let you cache dynamic pages while keeping data fresh.
|
|
47
|
+
- Database reads are fast everywhere via D1's read replication.
|
|
48
|
+
- Custom domains with automatic TLS.
|
|
49
|
+
- Secrets and environment variables managed via CLI or dashboard, scoped per project.
|
|
50
|
+
- A dashboard for deployments, usage metrics, and project settings.
|
|
51
|
+
|
|
52
|
+
You do not need a Cloudflare account or Cloudflare-specific knowledge to get started. If you want full control later, every build still produces a standard Cloudflare Worker, so you can [self-host](../integrations/cloudflare#deploy-to-your-own-cloudflare-account) with your own `wrangler.json`.
|
|
53
|
+
|
|
54
|
+
## How it works
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
vite.config.ts → voidPlugin() (works with any Vite app)
|
|
58
|
+
import { db } → D1 database (auto-provisioned)
|
|
59
|
+
import { kv } → KV namespace (auto-provisioned)
|
|
60
|
+
import { storage } → R2 bucket (auto-provisioned)
|
|
61
|
+
import { ai } → Workers AI inference (metered)
|
|
62
|
+
db/schema.ts → Drizzle schema (source of truth for DB types)
|
|
63
|
+
db/migrations/*.sql → Applied to D1 on deploy
|
|
64
|
+
void deploy → Live at https://<slug>.void.app
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The plugin scans your source code at build time, detects which imports you use, and provisions the corresponding Cloudflare bindings on deploy.
|
|
68
|
+
|
|
69
|
+
Void also works with existing frameworks. [TanStack Start](/integrations/frameworks/tanstack-start), [React Router](/integrations/frameworks/react-router), [SvelteKit](/integrations/frameworks/sveltekit), [Nuxt](/integrations/frameworks/nuxt), and [Astro](/integrations/frameworks/astro) can all deploy with the same `voidPlugin()`.
|
|
70
|
+
|
|
71
|
+
If you are building a full-stack app without a meta-framework, Void also gives you [file-based server routing](./server-routing) with method exports, dynamic params, middleware, and validation. It also includes [pages routing](./pages-routing/overview) for server-rendered UI, SPA navigation, co-located data loading, and typed forms across React, Vue, Svelte, and Solid.
|
|
72
|
+
|
|
73
|
+
Void auto-detects your [app type](./app-types) and adapts accordingly.
|
|
74
|
+
|
|
75
|
+
## Next steps
|
|
76
|
+
|
|
77
|
+
- [Quickstart](./quickstart): get a running app in minutes
|
|
78
|
+
- [Server Routing](./server-routing): dynamic params, middleware, and validation
|
|
79
|
+
- [Pages Routing](./pages-routing/overview): full-stack pages with server loaders and actions
|
|
80
|
+
- [Supported App Types](./app-types): full-stack, meta-framework, and static site modes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Cron Jobs
|
|
6
|
+
|
|
7
|
+
Void supports cron-triggered jobs from a top-level `crons/` directory.
|
|
8
|
+
|
|
9
|
+
## Job files
|
|
10
|
+
|
|
11
|
+
Create files in `crons/**/*.ts` (`.mts`, `.js`, `.mjs` also supported).
|
|
12
|
+
|
|
13
|
+
Each job file must export:
|
|
14
|
+
|
|
15
|
+
- `export const cron = "<expression>"` (or an array of expressions)
|
|
16
|
+
- a default handler (recommended: wrapped with `defineScheduled`)
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// crons/hourly-heartbeat.ts
|
|
22
|
+
import { defineScheduled } from 'void';
|
|
23
|
+
|
|
24
|
+
export const cron = '0 * * * *';
|
|
25
|
+
|
|
26
|
+
export default defineScheduled(async (controller, env) => {
|
|
27
|
+
await env.KV.put('jobs:last-heartbeat', controller.scheduledTime.toString());
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Multiple schedules
|
|
32
|
+
|
|
33
|
+
A single job file can export an array of cron expressions:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// crons/cleanup.ts
|
|
37
|
+
import { defineScheduled } from 'void';
|
|
38
|
+
|
|
39
|
+
export const cron = ['0 * * * *', '30 * * * *'];
|
|
40
|
+
|
|
41
|
+
export default defineScheduled(async (controller, env) => {
|
|
42
|
+
// Runs at :00 and :30 every hour
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## `defineScheduled`
|
|
47
|
+
|
|
48
|
+
`defineScheduled(fn)` is a typed identity helper for scheduled jobs.
|
|
49
|
+
|
|
50
|
+
Handler signature:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
(controller: ScheduledController, env: CloudEnv['Bindings'], ctx: ExecutionContext) =>
|
|
54
|
+
unknown | Promise<unknown>;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Notes:
|
|
58
|
+
|
|
59
|
+
- Jobs are matched by exact cron string.
|
|
60
|
+
- Job modules are lazy-loaded at runtime.
|
|
61
|
+
- Files or directories starting with `_` are ignored.
|
|
62
|
+
- Missing `cron` export causes an error during scan/build.
|
|
63
|
+
|
|
64
|
+
## Local development
|
|
65
|
+
|
|
66
|
+
Cron triggers do not fire on their schedule during local dev — this is a Cloudflare Workers / miniflare limitation, not specific to Void. Exercise a job locally by POSTing to the dev endpoint Void exposes:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
POST /__void/scheduled
|
|
70
|
+
Content-Type: application/json
|
|
71
|
+
{ "cron": "<expression>", "scheduledTime": <unix_ms> }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `cron` value must match a string you exported from a `crons/*.ts` file — that's how the dispatcher routes to the right handler. Returns `{ "ok": true }` on success.
|
|
75
|
+
|
|
76
|
+
The endpoint requires a local dev trigger token. Void prints a paste-ready curl command with the current token when the dev server starts.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
curl -X POST http://localhost:5173/__void/scheduled \
|
|
80
|
+
-H "Content-Type: application/json" \
|
|
81
|
+
-H "x-void-dev-trigger: <printed-token>" \
|
|
82
|
+
-d '{"cron":"0 * * * *","scheduledTime":'"$(date +%s000)"'}'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If you set `__VOID_PROXY_TOKEN` in `.dev.vars`, that explicit token takes precedence and the printed curl command uses `x-void-internal: <your-token>` instead.
|
|
86
|
+
|
|
87
|
+
This works the same in default Void mode and every supported framework — SvelteKit, Nuxt, Analog, Astro, TanStack Start, React Router, and vinext. In framework mode the cron handler runs inside the framework adapter's request pipeline (or the dev miniflare for Class A frameworks), so it sees whatever bindings the adapter exposes (D1, KV, R2, queues, AI, etc.).
|
|
88
|
+
|
|
89
|
+
## Deployment behavior
|
|
90
|
+
|
|
91
|
+
On deploy, Void includes all discovered job schedules in the deploy manifest and configures worker cron triggers automatically.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Key-Value Storage
|
|
6
|
+
|
|
7
|
+
Void provides a typed KV client with automatic JSON serialization for [Cloudflare Workers KV](https://developers.cloudflare.com/kv/). Import `kv` from `void/kv` and start reading and writing data.
|
|
8
|
+
|
|
9
|
+
## Basic Operations
|
|
10
|
+
|
|
11
|
+
### Get
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { kv } from 'void/kv';
|
|
15
|
+
|
|
16
|
+
const user = await kv.get<User>('user:123');
|
|
17
|
+
// User | null
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Values are automatically parsed as JSON. If the stored value isn't valid JSON, the raw string is returned.
|
|
21
|
+
|
|
22
|
+
### Put
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
await kv.put('user:123', { name: 'Alice', role: 'admin' });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Objects are automatically JSON-stringified. Strings are stored as-is.
|
|
29
|
+
|
|
30
|
+
Add a TTL (in seconds) or absolute expiration (Unix timestamp):
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
await kv.put('session:abc', sessionData, { ttl: 3600 });
|
|
34
|
+
await kv.put('token:xyz', tokenData, { expiration: 1700000000 });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Attach metadata to a key:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
await kv.put('user:123', userData, {
|
|
41
|
+
metadata: { updatedBy: 'admin' },
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Delete
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
await kv.delete('user:123');
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### List
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const result = await kv.list({ prefix: 'user:' });
|
|
55
|
+
// { keys: [{ name: "user:1" }, { name: "user:2" }], list_complete: true }
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Paginate with `limit` and `cursor`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const page = await kv.list({ prefix: 'user:', limit: 100 });
|
|
62
|
+
if (!page.list_complete) {
|
|
63
|
+
const next = await kv.list({ prefix: 'user:', limit: 100, cursor: page.cursor });
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Get with Metadata
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const result = await kv.getWithMetadata<User, { updatedBy: string }>('user:123');
|
|
71
|
+
// { value: User | null, metadata: { updatedBy: string } | null }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Typed Maps
|
|
75
|
+
|
|
76
|
+
For collections of the same type, use `kv.map()` to create a scoped, typed client with automatic key prefixing:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const sessions = kv.map<Session>('sessions');
|
|
80
|
+
|
|
81
|
+
await sessions.put('abc', { userId: 1, token: '...' }, { ttl: 3600 });
|
|
82
|
+
// KV key: "sessions:abc"
|
|
83
|
+
|
|
84
|
+
const session = await sessions.get('abc');
|
|
85
|
+
// Session | null
|
|
86
|
+
|
|
87
|
+
await sessions.delete('abc');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The map automatically prefixes all keys with `"sessions:"` and strips the prefix when listing:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const result = await sessions.list();
|
|
94
|
+
// keys: [{ name: "abc" }, { name: "def" }] (prefix stripped)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Maps have the same methods as the base client (`get`, `put`, `delete`, `list`, `getWithMetadata`) but with a typed value and automatic prefixing.
|
|
98
|
+
|
|
99
|
+
## How It Works
|
|
100
|
+
|
|
101
|
+
The KV client is a thin wrapper over the Cloudflare KV API that adds two things:
|
|
102
|
+
|
|
103
|
+
1. **Auto serialization:** `put()` calls `JSON.stringify()` on non-string values. `get()` calls `JSON.parse()` with a fallback to the raw string for values that are not JSON. You can store and retrieve objects without manual serialization.
|
|
104
|
+
|
|
105
|
+
2. **Typed maps:** `kv.map<T>(prefix)` returns a scoped client where all keys are prefixed with `"prefix:"` and values are typed as `T`. This is useful for organizing related data such as sessions, cache entries, and feature flags without manually managing prefixes.
|
|
106
|
+
|
|
107
|
+
> **Escape hatch:** The `kv` client covers the most common KV operations. For advanced use cases like `getWithMetadata` with specific cache behaviors, you can access the raw `KVNamespace` binding via `c.env.KV` in a route handler.
|