void 0.10.8 → 0.10.11
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 +4 -0
- package/README.md +1 -1
- package/dist/{agents-Bmr5tFFb.mjs → agents-CtgBYqld.mjs} +1 -1
- package/dist/{auth-cmd-DlgNwByu.mjs → auth-cmd-BqsdZJp5.mjs} +3 -3
- package/dist/{better-auth-shared-BvnM9px6.d.mts → better-auth-shared-DealXecJ.d.mts} +1 -1
- package/dist/{build-cmd-Br8qL0rA.mjs → build-cmd-Bujrv5q-.mjs} +3 -3
- package/dist/{cache-wH-mP8UE.mjs → cache-C11V8Fxq.mjs} +3 -3
- package/dist/{cancel-deploy-BEBOEgtu.mjs → cancel-deploy-fwFYF04b.mjs} +2 -2
- package/dist/cli/cli.mjs +175 -43
- package/dist/cli/env-schema-probe.d.mts +96 -0
- package/dist/cli/env-schema-probe.mjs +272 -0
- package/dist/{client-Cj96iiBH.mjs → client-Gb71-XkG.mjs} +20 -2
- package/dist/{config-BdUctCZD.mjs → config-CutEMNGJ.mjs} +3 -3
- package/dist/{config-1twldYCW.mjs → config-E03l1C_h.mjs} +8 -2
- package/dist/{create-project-DD9n8Ho-.mjs → create-project-DsYvl3TB.mjs} +3 -3
- package/dist/{db-DjKE2-A-.mjs → db-PZBsLSGb.mjs} +27 -27
- package/dist/{delete-D2kr3Kmk.mjs → delete-mh6p-zkQ.mjs} +3 -3
- package/dist/deploy-DT8wsPZd.mjs +6990 -0
- package/dist/{discover-BuVVSAum.mjs → discover-CJHyvYfR.mjs} +2 -2
- package/dist/dist-DaKKDf8D.mjs +41 -0
- package/dist/{domain-_luIsM_B.mjs → domain-DiaNQbrl.mjs} +2 -2
- package/dist/entry-D7yy4xVH.mjs +100 -0
- package/dist/{env-CO5XAS9t.mjs → env-AAHU02L6.mjs} +5 -5
- package/dist/env-mask-Dd47NbR6.mjs +90 -0
- package/dist/env-public-BfiLcMBk.d.mts +140 -0
- package/dist/env-raw-Cx8ElDdj.mjs +58 -0
- package/dist/{env-types-D51bnR-c.mjs → env-types-QBj-ndax.mjs} +2 -2
- package/dist/env-validation-BdDlGhbN.mjs +1069 -0
- package/dist/{gen-Cf79J4aw.mjs → gen-oup1xBN0.mjs} +8 -8
- package/dist/{github-cmd-B6OX9c7d.mjs → github-cmd-BdNaOVNa.mjs} +102 -3
- package/dist/{handler-imD0UVDT.d.mts → handler-Cjh8uM3Y.d.mts} +1 -1
- package/dist/{headers-BwvFGhkx.mjs → headers-BQknpzkn.mjs} +2 -2
- package/dist/index.d.mts +14 -2
- package/dist/index.mjs +102 -55
- package/dist/{init-KirOzVDs.mjs → init-Dl2PKuQn.mjs} +13 -14
- package/dist/{link-CUmiosyb.mjs → link-CdGHSIy-.mjs} +4 -4
- package/dist/{list-3GEw7b6m.mjs → list-CPwFDZ_c.mjs} +3 -3
- package/dist/{login-DJReaT_Q.mjs → login-BT3H8PN3.mjs} +2 -2
- package/dist/{logs-D-rQ56Lq.mjs → logs-Bt313ax7.mjs} +3 -2
- package/dist/{mcp-D7yc0dXY.mjs → mcp-DoM3_nhd.mjs} +7 -2
- package/dist/{node-yFFk626c.mjs → node-BDx8pmhq.mjs} +5 -5
- package/dist/{package-json-B0NuUWGd.mjs → package-json-Cx1osYo6.mjs} +1 -1
- package/dist/pages/client.d.mts +1 -1
- package/dist/pages/index.d.mts +1 -23
- package/dist/pages/index.mjs +5 -5
- package/dist/pages/islands-plugin.mjs +2 -2
- package/dist/pages/protocol.d.mts +2 -2
- package/dist/{plugin-inference-CJxi_fWI.mjs → plugin-inference-DMeavIJ6.mjs} +4 -98
- package/dist/{prepare-C_cVurhP.mjs → prepare-DOBTY0o4.mjs} +10 -10
- package/dist/preset-BGrvB4Bl.mjs +539 -0
- package/dist/{project-cmd-DnU7u9QF.mjs → project-cmd-D_w-4w5B.mjs} +13 -9
- package/dist/{project-paths-tpdR1mJR.mjs → project-paths-BQd7OmIo.mjs} +1 -1
- package/dist/project-paths-GpziKeQQ.d.mts +25 -0
- package/dist/{project-tsconfig-D9uSVVpA.mjs → project-tsconfig-B-QtXjLQ.mjs} +2 -2
- package/dist/{protocol-6hTJ04T1.d.mts → protocol-Bnb0LFp3.d.mts} +1 -1
- package/dist/provision-BLrCEBbI.mjs +2557 -0
- package/dist/requests-B8sZxaFM.mjs +50 -0
- package/dist/{resolve-project-D2HI3TrG.mjs → resolve-project-BBMtLLV9.mjs} +1 -1
- package/dist/{rollback-Yh7bCKob.mjs → rollback-CkvTFXx5.mjs} +2 -2
- package/dist/{route-types-CfKfhbIg.mjs → route-types-COI2DsZv.mjs} +2 -2
- package/dist/{runner-h272wcPj.mjs → runner-kapo9aPs.mjs} +3 -3
- package/dist/{runner-pg-waxJOnBb.mjs → runner-pg-CHM76xuC.mjs} +1 -1
- package/dist/runtime/ai.mjs +113 -15
- package/dist/runtime/better-auth-pg.d.mts +1 -1
- package/dist/runtime/better-auth.d.mts +1 -1
- package/dist/runtime/env-public.d.mts +1 -139
- package/dist/runtime/env-public.mjs +10 -93
- package/dist/runtime/env.d.mts +14 -1
- package/dist/runtime/env.mjs +24 -10
- package/dist/runtime/handler.d.mts +1 -1
- package/dist/runtime/isr-cache.d.mts +207 -0
- package/dist/runtime/isr-cache.mjs +523 -0
- package/dist/runtime/isr.d.mts +4 -3
- package/dist/runtime/isr.mjs +44 -5
- package/dist/runtime/live.d.mts +1 -1
- package/dist/runtime/live.mjs +1 -1
- package/dist/runtime/sandbox.mjs +1 -1
- package/dist/runtime/validator.d.mts +1 -1
- package/dist/runtime/ws-server.d.mts +1 -1
- package/dist/runtime/ws.d.mts +2 -2
- package/dist/{scan-Dp_Gyzs3.mjs → scan-DEwlM_Xy.mjs} +2 -2
- package/dist/{scan-i7Yz54fv.mjs → scan-DYXkrasO.mjs} +4 -4
- package/dist/{secret-u7FRvg8d.mjs → secret-Dt32J6RI.mjs} +3 -3
- package/dist/{skills-DsdNDtX3.mjs → skills-CLjN0uUO.mjs} +2 -2
- package/dist/{subcommand-prompt-DtES-oP6.mjs → subcommand-prompt-BzV8iQZo.mjs} +1 -1
- package/dist/sveltekit.mjs +1 -1
- package/dist/{validate-DT7nFMlf.mjs → validate-Cw_RLeTj.mjs} +1 -1
- package/dist/{yarn-pnp-CW8LB6g_.mjs → yarn-pnp-DJn3SAHF.mjs} +1 -1
- package/getting-started-prompt.txt +3 -1
- package/package.json +6 -3
- package/schema.json +11 -0
- package/skills/void/SKILL.md +34 -33
- package/skills/void/docs/guide/auth.md +8 -0
- package/skills/void/docs/guide/deployment.md +1 -1
- package/skills/void/docs/guide/env-vars.md +14 -6
- package/skills/void/docs/guide/queues.md +4 -0
- package/skills/void/docs/integrations/cloudflare.md +171 -5
- package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +4 -0
- package/skills/void/docs/node_modules/void/{AGENTS.md → CLAUDE.md} +14 -0
- package/skills/void/docs/node_modules/void/README.md +1 -1
- package/skills/void/docs/node_modules/void/node_modules/@types/proper-lockfile/README.md +51 -0
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathslash/README.md +64 -0
- package/skills/void/docs/node_modules/void/node_modules/pathslash/README.md +64 -0
- package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/CHANGELOG.md +108 -0
- package/skills/void/docs/node_modules/void/node_modules/proper-lockfile/README.md +183 -0
- package/skills/void/docs/node_modules/void/skills/void/SKILL.md +34 -33
- package/skills/void/docs/node_modules/void/test/e2e/README.md +85 -0
- package/skills/void/docs/reference/cli.md +97 -14
- package/dist/deploy-C4PbkFyE.mjs +0 -3705
- package/dist/dotenv-D_UbC_vc.mjs +0 -173
- package/dist/env-raw-CoS20LHP.mjs +0 -32
- package/dist/env-validation-CeC2FL66.mjs +0 -163
- package/dist/pathe.M-eThtNZ-CQzLbt4c.mjs +0 -150
- package/dist/preset-CVvwCeIy.mjs +0 -208
- package/skills/void/docs/node_modules/void/node_modules/@void/md/node_modules/pathe/README.md +0 -73
- package/skills/void/docs/node_modules/void/node_modules/pathe/README.md +0 -73
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as join, s as relative } from "./
|
|
2
|
-
import { T as log, r as getPackageDir } from "./agents-
|
|
1
|
+
import { a as join, s as relative } from "./dist-DaKKDf8D.mjs";
|
|
2
|
+
import { T as log, r as getPackageDir } from "./agents-CtgBYqld.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, symlinkSync } from "node:fs";
|
|
4
4
|
//#region src/cli/skills.ts
|
|
5
5
|
function parseSkills(skillsDir) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as S_RADIO_ACTIVE, b as S_STEP_SUBMIT, g as S_CHECKBOX_INACTIVE, h as S_CHECKBOX_ACTIVE, m as S_BAR_END, p as S_BAR, u as import_picocolors, v as S_RADIO_INACTIVE, y as S_STEP_ACTIVE } from "./agents-
|
|
1
|
+
import { _ as S_RADIO_ACTIVE, b as S_STEP_SUBMIT, g as S_CHECKBOX_INACTIVE, h as S_CHECKBOX_ACTIVE, m as S_BAR_END, p as S_BAR, u as import_picocolors, v as S_RADIO_INACTIVE, y as S_STEP_ACTIVE } from "./agents-CtgBYqld.mjs";
|
|
2
2
|
//#region src/cli/subcommand-prompt.ts
|
|
3
3
|
function promptSubcommand(command, subcommands) {
|
|
4
4
|
return new Promise((resolve) => {
|
package/dist/sveltekit.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as join, c as resolve, i as isAbsolute, s as relative } from "./
|
|
1
|
+
import { a as join, c as resolve, i as isAbsolute, s as relative } from "./dist-DaKKDf8D.mjs";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
//#region src/sveltekit.ts
|
|
4
4
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as __exportAll } from "./rolldown-runtime-DJK8HYOj.mjs";
|
|
2
|
-
import { a as join, n as dirname } from "./
|
|
2
|
+
import { a as join, n as dirname } from "./dist-DaKKDf8D.mjs";
|
|
3
3
|
import { t as canonicalJson } from "./canonical-json-DuDiiUsQ.mjs";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -15,11 +15,13 @@ At the end of the interactive flow, `void init` can also log you in and link or
|
|
|
15
15
|
|
|
16
16
|
void deploy
|
|
17
17
|
|
|
18
|
+
To deploy to your OWN Cloudflare account instead of the Void platform, use `void deploy --backend cloudflare` (add `--provision` on the first deploy to create the D1/KV/R2/Queues/Hyperdrive resources your app needs). Pin an account first (`account_id` in `wrangler.jsonc` or `CLOUDFLARE_ACCOUNT_ID`) and keep real secrets in `wrangler secret put` — `.env` values ship as plaintext worker vars. v1 deploys full Void apps on the Cloudflare Workers target only (D1/KV/R2/Queues/Hyperdrive; auth and ISR on D1/SQLite); framework SSR of every kind (TanStack Start, React Router, vinext, SvelteKit, Nuxt, Analog, Astro), static/SPA/SSG apps (including output: "static"), WebSocket/Durable Object apps (*.ws.ts), a custom migrations_pattern, node/bun/deno targets, and PostgreSQL apps with auth all fail closed with guidance, and auth apps need checked-in migrations that produce the Better Auth schema. `--provision` is a single-operator, dev-machine step and is disabled in CI.
|
|
19
|
+
|
|
18
20
|
Database workflow: define Drizzle tables in `db/schema.ts`, import `db` from `void/db` and tables from `@schema`. Use `void db push` to prototype (applies schema directly, no migration files). Use `void db generate` when ready to create production migration files in `db/migrations/`. `drizzle-orm` and `drizzle-kit` ship with void — no extra install needed. Scaffold new tables with `void gen model <name> [columns...]`.
|
|
19
21
|
|
|
20
22
|
Routing extras: declare static redirects/rewrites/fallbacks in `void.json` under `routing.*`, or in a `public/_redirects` file. For dynamic rewrites, call `c.rewrite(path)` in a `defineMiddleware`.
|
|
21
23
|
|
|
22
|
-
Logs: surface app-level errors via `import { logger } from "void/log"` and `logger.error(msg, fields?)` (also `.warn` / `.info`) so they appear under `void project logs --level error`. Errors only persisted to your own DB are invisible to Cloudflare Tail.
|
|
24
|
+
Logs: surface app-level errors via `import { logger } from "void/log"` and `logger.error(msg, fields?)` (also `.warn` / `.info`) so they appear under `void project logs --level error`. Errors only persisted to your own DB are invisible to Cloudflare Tail. Logs only exist where a worker ran — for 5xx generated by the edge router or on static/SPA projects, use `void project requests --status 5xx` instead.
|
|
23
25
|
|
|
24
26
|
CI / fresh clones: run `void prepare` after install to generate `.void/routes.d.ts`, `.void/db.d.ts`, `.void/queues.d.ts`, `.void/env.d.ts`, and `.void/tsconfig.json` without booting Vite. `vite dev` / `vite build` populate these during normal app workflows.
|
|
25
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "void",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.11",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/voidzero-dev/void.git",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"#self/runtime/live-server": "./dist/runtime/live-server.mjs",
|
|
39
39
|
"#self/runtime/fetch": "./dist/runtime/fetch.mjs",
|
|
40
40
|
"#self/runtime/fetch-stream": "./dist/runtime/fetch-stream.mjs",
|
|
41
|
+
"#self/runtime/isr-cache": "./dist/runtime/isr-cache.mjs",
|
|
41
42
|
"#self/runtime/migration-handler": "./dist/runtime/migration-handler.mjs",
|
|
42
43
|
"#self/runtime/migration-handler-pg": "./dist/runtime/migration-handler-pg.mjs",
|
|
43
44
|
"#self/runtime/ws-server": "./dist/runtime/ws-server.mjs"
|
|
@@ -326,6 +327,7 @@
|
|
|
326
327
|
"ignore": "^7.0.5",
|
|
327
328
|
"jsonc-parser": "3.3.1",
|
|
328
329
|
"pg": "8.22.0",
|
|
330
|
+
"proper-lockfile": "^4.1.2",
|
|
329
331
|
"wrangler": "^4.107.0"
|
|
330
332
|
},
|
|
331
333
|
"devDependencies": {
|
|
@@ -334,12 +336,13 @@
|
|
|
334
336
|
"@types/better-sqlite3": "^7.6.13",
|
|
335
337
|
"@types/node": "^26.1.0",
|
|
336
338
|
"@types/pg": "^8.20.0",
|
|
339
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
337
340
|
"@typescript/native-preview": "7.0.0-dev.20260507.1",
|
|
338
341
|
"arktype": "^2.2.2",
|
|
339
342
|
"estree-walker": "^3.0.3",
|
|
340
343
|
"magic-string": "^0.30.21",
|
|
341
344
|
"ofetch": "2.0.0-alpha.3",
|
|
342
|
-
"
|
|
345
|
+
"pathslash": "^0.1.0",
|
|
343
346
|
"pglite-server": "0.1.5",
|
|
344
347
|
"picocolors": "^1.1.1",
|
|
345
348
|
"tinyglobby": "^0.2.17",
|
|
@@ -353,7 +356,7 @@
|
|
|
353
356
|
"valibot": ">=1.0.0-beta.7",
|
|
354
357
|
"vite": "^8.0.0",
|
|
355
358
|
"zod": "^3.25.0 || ^4.0.0",
|
|
356
|
-
"@void/md": "0.10.
|
|
359
|
+
"@void/md": "0.10.11"
|
|
357
360
|
},
|
|
358
361
|
"peerDependenciesMeta": {
|
|
359
362
|
"@void/md": {
|
package/schema.json
CHANGED
|
@@ -363,6 +363,17 @@
|
|
|
363
363
|
"type": "string",
|
|
364
364
|
"enum": ["pg"],
|
|
365
365
|
"description": "Database backend. Omit for D1 (SQLite, default). Set to \"pg\" for PostgreSQL via Hyperdrive."
|
|
366
|
+
},
|
|
367
|
+
"ai": {
|
|
368
|
+
"type": "object",
|
|
369
|
+
"description": "AI configuration for self-hosted deploys. Configures the Cloudflare AI Gateway used by ai.provider().fetch() (Workers AI ai.run/ai.image use the env.AI binding directly and are not affected).",
|
|
370
|
+
"properties": {
|
|
371
|
+
"gateway": {
|
|
372
|
+
"type": "string",
|
|
373
|
+
"description": "Cloudflare AI Gateway name/slug to route ai.provider().fetch() through your own account."
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
"additionalProperties": false
|
|
366
377
|
}
|
|
367
378
|
},
|
|
368
379
|
"additionalProperties": false
|
package/skills/void/SKILL.md
CHANGED
|
@@ -32,39 +32,40 @@ Then ask what to do next.
|
|
|
32
32
|
|
|
33
33
|
## Task Routing
|
|
34
34
|
|
|
35
|
-
| User intent
|
|
36
|
-
|
|
|
37
|
-
| CLI command syntax, flags, env vars
|
|
38
|
-
| Initial setup, onboarding, first app
|
|
39
|
-
| App type detection and mode behavior
|
|
40
|
-
| Server/API routing and middleware
|
|
41
|
-
| Pages mode, loader/action, forms, layouts
|
|
42
|
-
| Database and migrations
|
|
43
|
-
| Typed fetch and end-to-end typing
|
|
44
|
-
| Authentication
|
|
45
|
-
| Cloudflare runtime bindings and config
|
|
46
|
-
| AI inference (Workers AI, providers)
|
|
47
|
-
| KV / storage / queues / cron jobs
|
|
48
|
-
| SSR and caching
|
|
49
|
-
| Rewrites, redirects, fallbacks
|
|
50
|
-
| Static site generation
|
|
51
|
-
| Deployment and CI
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
35
|
+
| User intent | Docs file(s) |
|
|
36
|
+
| ------------------------------------------ | ----------------------------------------------------------------------------------------- |
|
|
37
|
+
| CLI command syntax, flags, env vars | `docs/reference/cli.md` |
|
|
38
|
+
| Initial setup, onboarding, first app | `docs/guide/quickstart.md`, `docs/reference/cli.md` |
|
|
39
|
+
| App type detection and mode behavior | `docs/guide/app-types.md`, `docs/reference/config.md` |
|
|
40
|
+
| Server/API routing and middleware | `docs/guide/server-routing.md`, `docs/integrations/hono.md` |
|
|
41
|
+
| Pages mode, loader/action, forms, layouts | `docs/guide/pages-routing/*.md`, `docs/guide/type-safety.md` |
|
|
42
|
+
| Database and migrations | `docs/guide/database.md`, `docs/guide/type-safety.md` |
|
|
43
|
+
| Typed fetch and end-to-end typing | `docs/guide/typed-fetch.md`, `docs/guide/type-safety.md` |
|
|
44
|
+
| Authentication | `docs/guide/auth.md`, `docs/guide/env-vars.md` |
|
|
45
|
+
| Cloudflare runtime bindings and config | `docs/integrations/cloudflare.md`, `docs/reference/config.md`, `docs/guide/env-vars.md` |
|
|
46
|
+
| AI inference (Workers AI, providers) | `docs/guide/ai.md` |
|
|
47
|
+
| KV / storage / queues / cron jobs | `docs/guide/kv.md`, `docs/guide/storage.md`, `docs/guide/queues.md`, `docs/guide/jobs.md` |
|
|
48
|
+
| SSR and caching | `docs/guide/ssr.md`, `docs/guide/edge/*.md` |
|
|
49
|
+
| Rewrites, redirects, fallbacks | `docs/guide/edge/rewrites.md`, `docs/guide/edge/redirects.md`, `docs/reference/config.md` |
|
|
50
|
+
| Static site generation | `docs/guide/ssg.md` |
|
|
51
|
+
| Deployment and CI | `docs/guide/deployment.md`, `docs/reference/cli.md` |
|
|
52
|
+
| Self-host deploy to own Cloudflare account | `docs/integrations/cloudflare.md`, `docs/reference/cli.md` |
|
|
53
|
+
| Project status, deployment history | `docs/reference/cli.md` |
|
|
54
|
+
| Cache purging | `docs/reference/cli.md` |
|
|
55
|
+
| Project logs, runtime errors | `docs/reference/cli.md` |
|
|
56
|
+
| Secrets management (put/sync/delete) | `docs/reference/cli.md`, `docs/guide/env-vars.md` |
|
|
57
|
+
| Typed env vars (`defineEnv`, `env.ts`) | `docs/guide/env-vars.md` |
|
|
58
|
+
| Custom domain setup | `docs/reference/cli.md` |
|
|
59
|
+
| Database status, reset, seed, export | `docs/reference/cli.md`, `docs/guide/database.md` |
|
|
60
|
+
| Auth login/logout/whoami | `docs/reference/cli.md` |
|
|
61
|
+
| Overview / introduction | `docs/guide/index.md` |
|
|
62
|
+
| API surface details | `docs/reference/api.md` |
|
|
63
|
+
| Meta framework integration | `docs/integrations/frameworks/*.md` |
|
|
64
|
+
| Coding agent setup | `docs/integrations/agents.md` |
|
|
65
|
+
| Node.js / Bun / Deno targets | `docs/integrations/nodejs-bun-deno.md` |
|
|
66
|
+
| ORMs and external databases | `docs/integrations/orms-and-external-dbs.md` |
|
|
67
|
+
| Project structure and conventions | `docs/reference/structure.md` |
|
|
68
|
+
| Resource/binding inference | `docs/reference/resource-inference.md` |
|
|
68
69
|
|
|
69
70
|
## Working Rules
|
|
70
71
|
|
|
@@ -249,6 +249,14 @@ Void manages Better Auth migrations as part of the normal Void migration flow:
|
|
|
249
249
|
- deploy runs auth migrations together with app migrations
|
|
250
250
|
- users do not run a separate Better Auth CLI path
|
|
251
251
|
|
|
252
|
+
This applies to `void deploy` (the managed platform), which creates the Better Auth
|
|
253
|
+
tables at runtime after dispatch. Deploying to your own Cloudflare account with
|
|
254
|
+
[`--backend cloudflare`](/integrations/cloudflare) does **not** run that step: there,
|
|
255
|
+
your checked-in `db/migrations/*.sql` must already produce the Better Auth schema, and
|
|
256
|
+
deploy fails closed if they do not. Define the tables in `db/schema.ts` and run
|
|
257
|
+
`void db generate` — Drizzle's `.unique()` and `.references()` are opt-in, and the
|
|
258
|
+
constraints are verified too. See [#274](https://github.com/voidzero-dev/void/issues/274).
|
|
259
|
+
|
|
252
260
|
## Unsupported Modes
|
|
253
261
|
|
|
254
262
|
Void-managed Better Auth is supported only for Cloudflare Void apps in v1.
|
|
@@ -106,6 +106,6 @@ Connect the repository to your project once with `void github connect <project>
|
|
|
106
106
|
|
|
107
107
|
## Other Targets
|
|
108
108
|
|
|
109
|
-
If you prefer to deploy directly to your own Cloudflare account instead of using Void's managed platform,
|
|
109
|
+
If you prefer to deploy directly to your own Cloudflare account instead of using Void's managed platform, run `void deploy --backend cloudflare` (add `--provision` on the first deploy to create the D1/KV/R2/Queues/Hyperdrive resources your app needs). This uses your local wrangler auth and root `wrangler.jsonc` -- pin an account (`account_id` or `CLOUDFLARE_ACCOUNT_ID`) and keep real secrets in `wrangler secret put` rather than `.env`. `wrangler login` is enough to authenticate, with one exception: provisioning a **Hyperdrive** config for the first time needs `CLOUDFLARE_API_TOKEN`, because wrangler exposes no machine-readable Hyperdrive list for Void to check against (an already-provisioned Hyperdrive app deploys fine under `wrangler login`). In v1 it deploys **full Void apps on the Cloudflare Workers target** (D1/KV/R2/Queues/Hyperdrive; auth and ISR on D1/SQLite); framework SSR (TanStack Start, React Router, vinext, SvelteKit, Nuxt, Analog, Astro), static/SPA/SSG apps (including `output: "static"`), WebSocket/Durable Object apps (`*.ws.ts`), a custom `migrations_pattern`, `--skip-build`, `node`/`bun`/`deno` targets, and PostgreSQL apps with auth all fail closed with guidance. Auth apps must ship checked-in migrations that produce the Better Auth schema. Provision is a single-operator, dev-machine step and is disabled in CI. See the [Cloudflare integration guide](../integrations/cloudflare.md#deploy-to-your-own-cloudflare-account) for the full walk-through and scope.
|
|
110
110
|
|
|
111
111
|
To deploy to Node.js, Bun, or Deno instead of Cloudflare, set [`target`](../reference/config.md) in `void.json`. This builds a standalone server you can run anywhere, including Docker, Railway, and Fly.io. These targets do not have access to Void platform features such as D1, KV, R2, built-in auth, Workers AI, or cron scheduling. See the [Node.js, Bun, and Deno guide](../integrations/nodejs-bun-deno.md).
|
|
@@ -109,15 +109,23 @@ In practice `env.ts` should only import schema helpers from `void/env` and — a
|
|
|
109
109
|
|
|
110
110
|
Void uses Vite's standard `.env` convention to populate the schema:
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
| `.env
|
|
117
|
-
| `.env.
|
|
112
|
+
Whether a file's values ship depends on which deploy path you use:
|
|
113
|
+
|
|
114
|
+
| File | Loaded in dev | Shipped by `void deploy` | Shipped by `--backend cloudflare` |
|
|
115
|
+
| ----------------------- | ------------- | ------------------------ | --------------------------------- |
|
|
116
|
+
| `.env` | yes | yes (`plain_text`) | yes (worker `vars`) |
|
|
117
|
+
| `.env.local` | yes | no | **yes** (worker `vars`) |
|
|
118
|
+
| `.env.production` | yes | yes (`plain_text`) | yes (worker `vars`) |
|
|
119
|
+
| `.env.production.local` | yes | no | **yes** (worker `vars`) |
|
|
118
120
|
|
|
119
121
|
`.local` files are gitignored by convention — use them for secrets you don't want in source control.
|
|
120
122
|
|
|
123
|
+
::: warning `.local` files are not deploy-excluded on `--backend cloudflare`
|
|
124
|
+
Managed `void deploy` reads only `.env` and `.env.production`, so a `.local` file keeps secrets off the platform. The self-host `void deploy --backend cloudflare` path instead runs Vite's production env loading, which reads all four files and bakes them into the worker's `vars` as plaintext. A value that is also `export`ed in the shell with the same value is stripped back out, so this bites hardest in CI, where the variable usually only exists in the file.
|
|
125
|
+
|
|
126
|
+
On that path, keep real secrets out of every `.env*` file and use `wrangler secret put <NAME>` instead. See [Cloudflare deploy](/integrations/cloudflare).
|
|
127
|
+
:::
|
|
128
|
+
|
|
121
129
|
### Dotenv variable expansion
|
|
122
130
|
|
|
123
131
|
Values can reference other keys defined in the same (or earlier-precedence) `.env` file using `${VAR}` or `$VAR`:
|
|
@@ -10,6 +10,10 @@ Void supports Cloudflare Queues for asynchronous message processing from a top-l
|
|
|
10
10
|
|
|
11
11
|
Create files in `queues/**/*.ts`; `.mts`, `.js`, and `.mjs` also work. The queue name is inferred from the filename. For example, `queues/emails.ts` creates a queue named `"emails"`, and `queues/order/notifications.ts` creates `"order/notifications"`.
|
|
12
12
|
|
|
13
|
+
::: warning Nested files produce a name that cannot be deployed
|
|
14
|
+
Cloudflare queue names allow only letters, digits and `-`, so a name containing `/` is rejected when the queue is provisioned — on both the managed platform and a self-hosted `--backend cloudflare` deploy. Nested files work in local development, but keep queue files flat (`queues/order-notifications.ts` → `"order-notifications"`) for any app you intend to deploy.
|
|
15
|
+
:::
|
|
16
|
+
|
|
13
17
|
Each queue file should export a default handler wrapped with [`defineQueue`](../reference/api.md#definequeuet-handler). The generic `<T>` parameter defines the message body type. That is the type of each `msg.body` in the batch, and it is also used by the typed `queues` proxy for `send()` calls.
|
|
14
18
|
|
|
15
19
|
```ts
|
|
@@ -214,9 +214,68 @@ When deploying via `void deploy` (to the Void platform), the `wrangler.json` in
|
|
|
214
214
|
|
|
215
215
|
## Deploy to your own Cloudflare account
|
|
216
216
|
|
|
217
|
-
Void's default deployment path is `void deploy`, which uploads to the Void platform. But the generated worker is a standard Cloudflare Worker -- you can deploy it directly to your own account
|
|
217
|
+
Void's default deployment path is `void deploy`, which uploads to the Void platform. But the generated worker is a standard Cloudflare Worker -- you can deploy it directly to your own account. Two paths get you there:
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
- **[`void deploy --backend cloudflare`](#one-command-void-deploy-backend-cloudflare)** -- a first-class flow that provisions bindings, applies remote migrations, checks secrets, builds, and runs `wrangler deploy` for you.
|
|
220
|
+
- **[Manual `vite build && wrangler deploy`](#manual-build-and-deploy-with-wrangler)** -- create resources and edit `wrangler.jsonc` yourself, then build and deploy with wrangler directly.
|
|
221
|
+
|
|
222
|
+
Either way, the [Local development](#local-development), [AI](#ai-self-host), and [ISR](#isr-self-host) notes below apply.
|
|
223
|
+
|
|
224
|
+
### One command: `void deploy --backend cloudflare`
|
|
225
|
+
|
|
226
|
+
`void deploy --backend cloudflare` deploys the built worker to **your own** Cloudflare account. It uses your local wrangler auth and your root `wrangler.jsonc` -- there is no Void login or linked project.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Deploy using the resources already declared in wrangler.jsonc
|
|
230
|
+
void deploy --backend cloudflare
|
|
231
|
+
|
|
232
|
+
# Create any missing resources first, then deploy
|
|
233
|
+
void deploy --backend cloudflare --provision
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Prerequisites**
|
|
237
|
+
|
|
238
|
+
- **Pin an account.** Set `account_id` in your root `wrangler.jsonc`, or export `CLOUDFLARE_ACCOUNT_ID`. A multi-account token otherwise makes wrangler prompt (or error in CI), which Void cannot intercept -- so the account must be pinned first.
|
|
239
|
+
- **Authenticate wrangler.** Run `wrangler login`, or set `CLOUDFLARE_API_TOKEN`. Deploy needs a token with `Workers Scripts:Edit` plus read on the resources you bind. `--provision` additionally needs per-product `*:Edit` (D1, KV, R2, Queues, Hyperdrive).
|
|
240
|
+
- **`CLOUDFLARE_API_TOKEN` is required to provision a Hyperdrive config for the first time.** `wrangler login` covers every other resource, but wrangler exposes no machine-readable Hyperdrive list, so Void checks whether the config already exists over the Cloudflare REST API -- which an OAuth session cannot authenticate. Without a token `--provision` stops **before** touching your account rather than risk minting a duplicate config. Either export `CLOUDFLARE_API_TOKEN` (with Hyperdrive edit permission), or create the Hyperdrive config yourself and add its id to the `hyperdrive` binding in `wrangler.jsonc` -- deploying an **already-provisioned** Hyperdrive app needs no token and works under `wrangler login` alone.
|
|
241
|
+
- **Docker**, if your app uses the sandbox -- the build needs it locally.
|
|
242
|
+
|
|
243
|
+
**What it does** (in order): settles the app class (a full Void app on the Cloudflare Workers target -- see [scope](#scope-and-limitations)) before any account operation; pins the account and confirms wrangler auth; provisions or drift-checks resources; **builds**; then, against the artifact the build actually emitted, warns on plaintext vars, gates on missing required secrets, checks the auth schema, and validates migrations; then applies remote D1 migrations for SQLite apps (and verifies none remain pending); then runs `wrangler deploy` on exactly the artifact it verified.
|
|
244
|
+
|
|
245
|
+
Note that **the build runs before the secret, auth-schema, and migration gates** -- those gates read the real emitted worker, so there has to be one to read. A missing production secret or a bad migration is therefore reported _after_ the build has run, which is worth knowing if your build hooks are slow or have side effects. In exchange, nothing remote is touched until every ground-truth gate has passed: remote D1 migrations are applied only afterwards, so a failed gate leaves your account, your database, and your live worker exactly as they were.
|
|
246
|
+
|
|
247
|
+
**Trust boundary: the build runs your project's code.** Exactly like the manual `vite build && wrangler deploy` below, this backend runs your project's build -- your config, every Vite plugin, and every build dependency -- with filesystem access before the credentialed deploy. Deploy narrows what that build can quietly change: it runs the build with your Cloudflare credentials scrubbed, verifies the emitted `wrangler.json` against a pre-build snapshot of your account, name, and binding identities, and checks that the wrangler CLI it is about to run with your token was not modified during the build. Those checks catch a build that tampers with the deploy target or the uploader. They do not sandbox the build itself, so a fully-compromised build dependency remains a trust boundary -- the same one you accept running `vite build` by hand. Vet your dependencies as you would for any deploy; stronger build isolation is future work.
|
|
248
|
+
|
|
249
|
+
Without `--provision`, deploy is a **drift check**: if a binding your source needs is not yet in `wrangler.jsonc` with a real id, deploy stops and names each missing resource, telling you to run `--provision` once. Queues carry no id in the config, so deploy instead checks each one against your account (`wrangler queues info`) at the same point -- a queue that does not exist stops the deploy before anything is built or migrated.
|
|
250
|
+
|
|
251
|
+
**`--provision`** creates any D1 database, KV namespace, R2 bucket, Queues, Hyperdrive config, and the ISR cache namespace your source needs, then lets wrangler write the real ids into your root `wrangler.jsonc`. It is **idempotent** -- it reads existing ids first, so re-running creates nothing that already exists.
|
|
252
|
+
|
|
253
|
+
#### Scope and limitations
|
|
254
|
+
|
|
255
|
+
- **Supported apps (v1): full Void apps on the Cloudflare Workers target only** -- worker-bearing apps that run Void's routing (`routes/` and/or `pages/`), with D1 (SQLite) and/or KV, R2, Queues, and Hyperdrive, plus auth on D1/SQLite and ISR. Everything else fails closed **before** any account operation, with guidance:
|
|
256
|
+
- **Framework SSR is not supported here (yet).** The whole framework path -- TanStack Start, React Router, vinext (and SvelteKit, Nuxt, Analog, Astro) -- is deferred in v1. Deploy them with `void deploy` (the managed platform) or the framework's own Cloudflare adapter. Framework SSR support for this backend is a follow-up.
|
|
257
|
+
- **Static-only / SPA / SSG apps are not supported here** -- including unconfigured ones, `output: "static"` in `void.json`, and `--dir` / `--spa` deploys. Deploy those with `void deploy` (the managed platform) or host the built assets on Cloudflare Pages / any static host.
|
|
258
|
+
- **WebSocket / Durable Object routes (`*.ws.ts`) are not supported here (yet).** A WebSocket route makes the build emit a key-value-backed Durable Object namespace (a `new_classes` migration), which a fresh Cloudflare account -- and every Workers Free account -- refuses to create. Deploy those with `void deploy`. SQLite-backed WebSocket support for this backend is a follow-up.
|
|
259
|
+
- **Custom `migrations_pattern` is not supported here.** v1 deploys only Void's default migration convention -- `db/migrations/*.sql` with wrangler's default pattern (leave `migrations_pattern` unset). Remove a custom `migrations_pattern` from the D1 binding, or deploy with `void deploy`. Supporting custom patterns is a follow-up.
|
|
260
|
+
- **Non-Cloudflare targets (`node` / `bun` / `deno`) are not supported here** -- this backend deploys Cloudflare Workers only.
|
|
261
|
+
- **`--skip-build` is not supported here.** Every check on this path validates the artifact the build emits -- the worker `vars` in `dist/ssr/wrangler.json` and the generated auth schema -- so skipping the build would leave them reading a stale or missing artifact instead of the worker being uploaded. Drop `--skip-build`, or use `void deploy` (the managed platform), which supports it.
|
|
262
|
+
- **PostgreSQL apps with auth enabled are not supported here.** This backend verifies + applies auth tables through remote D1 (SQLite) migrations only, so it cannot provision or verify the Better Auth schema on Postgres/Hyperdrive. Deploy with `void deploy`, or use D1 for the auth app.
|
|
263
|
+
- **PostgreSQL apps with checked-in migrations are not supported here.** This backend applies only remote D1 (SQLite) migrations. A PostgreSQL app's `db/migrations/*.sql` are applied over Hyperdrive by the managed platform (`void deploy`) at deploy time; this self-host backend never runs them, so it would deploy against an unmigrated database. Deploy with `void deploy`, or apply the migrations yourself against your Postgres and remove them from `db/migrations/`. (A pg app _without_ auth **and** with an externally-managed schema — no checked-in migrations — still deploys.)
|
|
264
|
+
- **Auth requires checked-in migrations that produce the Better Auth schema.** The managed platform creates the Better Auth tables at runtime after deploy; this self-host backend never runs that step. Deploy reads the required Better Auth schema from the one the build itself generated (`.void/better-auth-schema.ts`, so configured renames and plugin tables are already reflected), then verifies the checked-in `db/migrations/*.sql` produce it by applying them to an in-memory SQLite database. Names alone are not enough: it also checks the constraints that carry correctness -- every auth table's `id` must be uniquely constrained (a `PRIMARY KEY` or a `UNIQUE` constraint/index; SQLite refuses a foreign key whose parent key is not unique, failing at runtime with `foreign key mismatch`), every field the schema marks `unique` must be covered by a unique constraint (a column-level `UNIQUE` or a single-column unique index both count, under any index name), and every field with a `references` target must have a matching foreign key. Column types, non-unique indexes and the exact `ON DELETE` action are deliberately not asserted. If a check fails, deploy fails closed naming the offending `table.column` -- fix the schema in `db/schema.ts` (Drizzle's `.primaryKey()`, `.unique()` and `.references()` are opt-in), run `void db generate`, commit the migration, or deploy with `void deploy`.
|
|
265
|
+
- **Migrations must apply in the same order Void validated.** Deploy checks that the files wrangler will apply to remote D1 (and the order it applies them, by numeric prefix) exactly match Void's `db/migrations/*.sql` set -- no stray files (e.g. `seed.sql`), same content, same order. Zero-pad your migration prefixes (`0001_`, `0002_`, ...) so numeric and lexicographic order agree; deploy fails closed on a mismatch.
|
|
266
|
+
- **Provision is a single-operator, dev-machine action.** The lock guarding it is per local config path only; it does not coordinate across machines or CI hosts. Two people provisioning the same account at once could create duplicate resources.
|
|
267
|
+
- **Provision fails closed in CI.** In non-interactive shells, `--provision` is disabled unless your committed `wrangler.jsonc` already covers every resource (a provable no-op). The workflow is: provision **locally**, commit the updated `wrangler.jsonc`, then let CI run `void deploy --backend cloudflare` (deploy itself is CI-safe).
|
|
268
|
+
- **Your `wrangler.jsonc` is rewritten on provision.** Wrangler preserves your comments but normalizes the whole file's indentation when it writes the new ids -- expect that in the diff.
|
|
269
|
+
- **Your `.env*` values ship as plaintext.** The build bakes them into the worker's `vars`. This backend runs Vite's production env loading, so **all four** of `.env`, `.env.local`, `.env.production` and `.env.production.local` are loaded and shipped -- including the `.local` files, which are gitignored but **not** deploy-excluded here (unlike managed `void deploy`, which reads only `.env` and `.env.production`). The one exception: a value that is also present in the shell environment with the same value is stripped back out, so a var you `export` in CI does not get baked in. Move real secrets to `wrangler secret put <NAME>` so they are never written into `wrangler.json`. Deploy warns on likely-plaintext secrets -- by key name, and by value shape for credential-bearing connection URLs such as `DATABASE_URL` -- and hard-blocks on missing required secrets.
|
|
270
|
+
- **First deploy of a new worker.** A worker that has never been deployed has no remote secrets to list yet, so the secret gate prints the still-unset required key names and the `wrangler secret put <NAME>` commands that bootstrap them on the draft worker (or add a value to `.env` / `.env.production` and rerun).
|
|
271
|
+
|
|
272
|
+
The manual steps below are the by-hand equivalent -- reach for them when you want to manage resources and `wrangler.jsonc` yourself.
|
|
273
|
+
|
|
274
|
+
### Manual: build and deploy with wrangler
|
|
275
|
+
|
|
276
|
+
Prefer to manage everything by hand? Create the resources, write `wrangler.jsonc`, and run `wrangler deploy` yourself.
|
|
277
|
+
|
|
278
|
+
#### 1. Create your resources
|
|
220
279
|
|
|
221
280
|
Create whatever bindings your app uses:
|
|
222
281
|
|
|
@@ -231,7 +290,7 @@ wrangler kv namespace create KV
|
|
|
231
290
|
wrangler r2 bucket create my-app-storage
|
|
232
291
|
```
|
|
233
292
|
|
|
234
|
-
|
|
293
|
+
#### 2. Add a `wrangler.jsonc`
|
|
235
294
|
|
|
236
295
|
Create `wrangler.jsonc` in your project root with the resource IDs from step 1:
|
|
237
296
|
|
|
@@ -264,7 +323,7 @@ Create `wrangler.jsonc` in your project root with the resource IDs from step 1:
|
|
|
264
323
|
|
|
265
324
|
Only include the bindings your app actually uses. You can also add service bindings, custom routes, environment overrides, and any other standard wrangler fields -- they all flow through to the build output. You don't need `main` or `assets` -- those are set by the plugin.
|
|
266
325
|
|
|
267
|
-
|
|
326
|
+
#### 3. Run migrations
|
|
268
327
|
|
|
269
328
|
If your app uses D1, apply migrations before deploying:
|
|
270
329
|
|
|
@@ -274,7 +333,7 @@ wrangler d1 migrations apply my-app-db --remote
|
|
|
274
333
|
|
|
275
334
|
This uses the same `db/migrations/` directory that Void uses locally. `my-app-db` is the database name from your `wrangler.jsonc`; using the database name avoids accidentally applying migrations to the wrong binding.
|
|
276
335
|
|
|
277
|
-
|
|
336
|
+
#### 4. Build and deploy
|
|
278
337
|
|
|
279
338
|
```bash
|
|
280
339
|
vite build && wrangler deploy
|
|
@@ -285,3 +344,110 @@ That's it. The Cloudflare Vite plugin produces a complete build output with a me
|
|
|
285
344
|
### Local development
|
|
286
345
|
|
|
287
346
|
`pnpm dev` continues to work as before -- Miniflare creates local instances of all bindings regardless of the IDs in your `wrangler.jsonc`. Your real resource IDs are only used when you run `wrangler deploy`.
|
|
347
|
+
|
|
348
|
+
### AI (self-host)
|
|
349
|
+
|
|
350
|
+
`void/ai` works on your own Cloudflare account, along two paths:
|
|
351
|
+
|
|
352
|
+
- **Workers AI** (`ai.run`, `ai.stream`, `ai.image`) works out of the box. When your app imports `void/ai`, `vite build` infers that you need AI and adds a Workers AI binding (`env.AI`) to the generated `wrangler.json` automatically -- you do **not** add it to `wrangler.jsonc`.
|
|
353
|
+
- **Provider models** (`ai.provider("openai").fetch(...)`) route through _your own_ Cloudflare AI Gateway. Set its id in `void.json` and add the provider's API key as a Worker secret.
|
|
354
|
+
|
|
355
|
+
```jsonc
|
|
356
|
+
// void.json
|
|
357
|
+
{
|
|
358
|
+
"ai": {
|
|
359
|
+
"gateway": "my-gateway", // an AI Gateway in YOUR Cloudflare account
|
|
360
|
+
},
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# provider API key, stored as a Worker secret (never committed)
|
|
366
|
+
wrangler secret put OPENAI_API_KEY
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
// routes/chat.ts
|
|
371
|
+
import { defineHandler } from 'void';
|
|
372
|
+
import { ai } from 'void/ai';
|
|
373
|
+
|
|
374
|
+
export const POST = defineHandler(async (c) => {
|
|
375
|
+
// Workers AI -- uses env.AI directly
|
|
376
|
+
const summary = await ai.run('@cf/meta/llama-3.1-8b-instruct', {
|
|
377
|
+
prompt: 'Summarize the changelog.',
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Provider model -- routes through your "my-gateway" AI Gateway,
|
|
381
|
+
// authed with the OPENAI_API_KEY secret above
|
|
382
|
+
const res = await ai.provider('openai').fetch('chat/completions', {
|
|
383
|
+
method: 'POST',
|
|
384
|
+
body: JSON.stringify({ model: 'gpt-4o-mini', messages: [] }),
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return c.json({ summary, provider: await res.json() });
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
`ai.gateway` is required for `ai.provider().fetch()` -- without it, that call returns a `501`. No AI traffic or provider secret passes through Void's shared proxy: requests go directly to _your own_ Cloudflare AI Gateway, authenticated with provider secrets from _your_ Worker's environment. (The request and that secret are of course still sent onward to the AI Gateway and the upstream provider you call.)
|
|
392
|
+
|
|
393
|
+
Notes:
|
|
394
|
+
|
|
395
|
+
- **No cross-tenant usage metering.** On the Void platform, AI calls are metered and billed through a shared proxy. Self-hosted, there is no metering -- you get your own [Cloudflare AI Gateway analytics](https://developers.cloudflare.com/ai-gateway/) instead.
|
|
396
|
+
- **The runtime reads the `env.AI` binding by name** -- a custom-named root AI binding is not supported.
|
|
397
|
+
- **`void dev` has no local Workers AI emulation.** In development, AI still routes through Void, so `void dev` AI requires `void auth login` even when you deploy self-hosted.
|
|
398
|
+
|
|
399
|
+
### ISR (self-host)
|
|
400
|
+
|
|
401
|
+
[Revalidation (ISR)](../guide/edge/revalidation.md) works self-hosted, but the cache KV is **not** auto-injected (a KV binding needs a real namespace id). Create a namespace and bind it as `ISR_CACHE`:
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
wrangler kv namespace create ISR_CACHE
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
```jsonc
|
|
408
|
+
// wrangler.jsonc
|
|
409
|
+
{
|
|
410
|
+
"kv_namespaces": [
|
|
411
|
+
{
|
|
412
|
+
"binding": "ISR_CACHE",
|
|
413
|
+
"id": "<your-namespace-id>",
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Configure revalidation exactly as on the platform -- globally or per-path in `void.json`:
|
|
420
|
+
|
|
421
|
+
```jsonc
|
|
422
|
+
// void.json
|
|
423
|
+
{
|
|
424
|
+
"routing": {
|
|
425
|
+
"revalidate": { "/blog/*": 3600, "*": 60 },
|
|
426
|
+
},
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
...or per page with an exported `revalidate` literal in a `.server.ts` companion (Pages mode):
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
// pages/blog/[slug].server.ts
|
|
434
|
+
export const revalidate = 3600; // seconds
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
On-demand purges work through `revalidate()`:
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { revalidate } from 'void/isr';
|
|
441
|
+
|
|
442
|
+
await revalidate({ paths: ['/blog/hello'] });
|
|
443
|
+
// or purge every ISR page:
|
|
444
|
+
await revalidate({ all: true });
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Self-hosted, `revalidate()` purges this worker's own KV entries (global, authoritative) plus the current colo's edge cache.
|
|
448
|
+
|
|
449
|
+
Limits (the single-worker cache ladder can't do everything the platform's dispatch layer does):
|
|
450
|
+
|
|
451
|
+
- **No fleet-wide / cross-colo edge purge.** `revalidate()` clears KV globally and the _local_ colo's edge cache; other colos keep serving their edge copy until it expires (bounded by the response's `s-maxage`), then re-render on the next edge miss.
|
|
452
|
+
- **The pages-protocol JSON variant is not served from a cold colo's KV.** A colo that hasn't rendered the HTML yet re-renders the JSON live; the JSON edge cache is warmed only as a side effect of the HTML render path.
|
|
453
|
+
- **The warm cache is dropped on every redeploy.** Each `vite build` bakes a fresh deployment id into the cache keys, so cached HTML never outlives the hashed assets it references -- the first request after a deploy is a cold render.
|
|
@@ -12,4 +12,8 @@ Rewrites and redirects: declare static rules in `void.json` under `routing.redir
|
|
|
12
12
|
|
|
13
13
|
Logs: surface app-level errors that should show up under `void project logs --level error` via `import { logger } from "void/log"` and `logger.error(msg, fields?)` (also `.warn` / `.info`). Anything caught and only persisted to your own DB is invisible to Cloudflare Tail; route it through `logger.*` or `console.*` so the platform can see it.
|
|
14
14
|
|
|
15
|
+
Requests: `void project logs` only has rows when a worker actually ran. 5xx generated by the edge router, and every request to a static/SPA project, produce no log line at all. Use `void project requests --status 5xx --range 12h` to see those — it lists status, method, and duration for every request the edge served, whether or not a worker was invoked.
|
|
16
|
+
|
|
17
|
+
Self-host deploy: `void deploy` targets the Void platform; to deploy to the user's OWN Cloudflare account instead, use `void deploy --backend cloudflare` (add `--provision` on the first deploy to create the D1/KV/R2/Queues/Hyperdrive resources the source needs). It uses local wrangler auth, so pin an account (`account_id` in `wrangler.jsonc` or `CLOUDFLARE_ACCOUNT_ID`) and keep real secrets in `wrangler secret put` — every `.env*` file this backend loads (`.env`, `.env.local`, `.env.production`, `.env.production.local`) ships as plaintext worker vars, `.local` included. Provisioning a Hyperdrive config reads `DATABASE_URL` from the shell, not from `.env*`. v1 supports full Void apps on the Cloudflare Workers target only (D1/KV/R2/Queues/Hyperdrive; auth and ISR on D1/SQLite); framework SSR of every kind (TanStack Start, React Router, vinext, SvelteKit, Nuxt, Analog, Astro), static/SPA/SSG apps (including `output: "static"`), WebSocket/Durable Object apps (`*.ws.ts`), a custom `migrations_pattern`, node/bun/deno targets, and PostgreSQL apps with auth all fail closed with guidance. Auth apps must ship checked-in migrations that produce the Better Auth schema. `--provision` is a single-operator, dev-machine step and is disabled in CI.
|
|
18
|
+
|
|
15
19
|
Full docs are in `node_modules/void/docs/`. If you have the `void` skill available, use it for a complete API reference covering project structure, routing, pages mode, database, auth, typed fetch, KV, storage, queues, cron jobs, CLI, configuration, and deployment.
|
|
@@ -89,6 +89,14 @@ src/
|
|
|
89
89
|
- **Drizzle-first database**: Schema is defined in `db/schema.ts` (Drizzle table definitions). `void/db` exports a Drizzle D1 instance. `@schema` is an auto-configured alias for `db/schema.ts`. Migrations live in `db/migrations/`. CLI commands: `void db push` (prototype), `void db generate` (create migration files), `void db migrate` (apply locally). `drizzle-orm` and `drizzle-kit` ship with void.
|
|
90
90
|
- **Journal coherence invariant**: `db/migrations/*.sql` and `db/migrations/meta/_journal.json` must stay in sync — every `.sql` file has a matching journal entry (by `tag`) and vice versa. `assertJournalCoherence` in `src/migrations/validate.ts` is the gate, called from `deploy`, `db status`, `db migrate`, and `db reset`. The two CLI commands that mutate migration state — `void gen migration` and `void db rename-migrations` — are responsible for keeping the journal coherent themselves: the first appends a new entry when writing a file (failing loud if the journal is missing — run `drizzle-kit generate` first), the second rewrites `tag` values alongside the file renames and also renames the matching `meta/<prefix>_snapshot.json` (with byte-level rollback on failure). `gen migration` additionally copies the previous `meta/<idx:04d>_snapshot.json` forward to the new idx so drizzle-kit's drop/rollback tooling keeps working on mixed hand-written + drizzle projects; projects without prior snapshots are skipped silently. Drift will fail loudly before touching any remote DB. Motivated by [void-sdk/void#6](https://github.com/void-sdk/void/issues/6), where an orphan scaffolded `.sql` file half-applied against D1 and wedged the deploy.
|
|
91
91
|
|
|
92
|
+
### Self-Host AI + ISR
|
|
93
|
+
|
|
94
|
+
Two features need extra wiring on a self-hosted `vite build && wrangler deploy` (no Void platform proxy/dispatch in front of the worker). Both are inert on managed `void deploy`.
|
|
95
|
+
|
|
96
|
+
- **`ai.gateway` config + `__VOID_AI_GATEWAY` var** — `void.json`'s `ai.gateway` (validated in `config.ts` as `ai?: { gateway?: string }`; non-empty string, `additionalProperties: false`) names a Cloudflare AI Gateway in the user's own account. `mergeBindings` (`index.ts`): (1) injects `{ ai: { binding: 'AI' } }`, but only when `shouldBindWorkersAi()` is true — the app infers `needsAI`, `command === 'build'`, and neither the resolved root wrangler config nor the result already declares an `ai` binding (so it never clobbers a user's own `ai` binding); (2) emits `vars.__VOID_AI_GATEWAY = config.ai.gateway` whenever `config.ai?.gateway` is set. At runtime, `runtime/ai.ts` `resolveBackend()` selects the direct backend (Priority 3) when `env.AI` is present and no `__VOID_PROXY`/`__VOID_TOKEN` exists. `ai.run/stream/image` hit Workers AI directly; `ai.provider().fetch()` routes through the user's own gateway via `directAi.gateway(__VOID_AI_GATEWAY).getUrl(provider)` with the provider key from the user's own secret (`resolveProviderKey` → `env[PROVIDER_KEY_MAP[provider]]`), returning HTTP 501 if `__VOID_AI_GATEWAY` is unset. No cross-tenant metering on this path. The runtime reads `env.AI` **by name** — a custom-named root AI binding is not honored.
|
|
97
|
+
- **Internal-binding leak check permits exactly `__VOID_AI_GATEWAY`** — the integration guard (`test/integration/kitchen-sink-build.test.ts`) that asserts no `__VOID_*` var reaches `dist/ssr/wrangler.json` explicitly excludes `__VOID_AI_GATEWAY`, because it is a legitimate, non-credential runtime var (unlike the credential keys in `INTERNAL_ENV_KEYS`, which are stripped).
|
|
98
|
+
- **`ISR_CACHE` self-host requirement** — self-host ISR (`runtime/isr-cache.ts`, `runtime/isr.ts`) reads `env.ISR_CACHE` (a `KVNamespace`) + `env.__VOID_ISR_DEPLOYMENT_ID`. Unlike D1/KV/R2, `ISR_CACHE` is **not** auto-injected (a KV binding needs a real namespace id), so the user must create the namespace and declare it as `ISR_CACHE` in their root wrangler config. `routing.revalidate` (void.json) and per-page `export const revalidate` are merged by `collectRevalidate` (`isr-config.ts`) and baked as `__VOID_ISR_REVALIDATE`. On-demand `revalidate()` on self-host purges the worker's own KV (global) plus the local-colo `caches.default` edge variants only.
|
|
99
|
+
|
|
92
100
|
### Package Exports
|
|
93
101
|
|
|
94
102
|
| Subpath | What |
|
|
@@ -203,3 +211,9 @@ Agent instructions use versioned markers (`<!--injected-by-void-v0.0.1-->` / `<!
|
|
|
203
211
|
| `test/unit/plugin-node-target.test.ts` | Node target plugin — binding guard, plugin creation (5 tests) |
|
|
204
212
|
| `test/integration/node-target.test.ts` | Node app playground — dev server + production build (5 tests) |
|
|
205
213
|
| `test/integration/queue-playground.test.ts` | Queue send → consumer → KV round-trip (1 test) |
|
|
214
|
+
|
|
215
|
+
## Pitfalls / Things That Broke
|
|
216
|
+
|
|
217
|
+
- **`__VOID_ISR_DEPLOYMENT_ID` must be a compile-time literal.** `router/compile.ts` stringifies `crypto.randomUUID()` once at build time (`export const __VOID_ISR_DEPLOYMENT_ID = "..."`, exactly like `__VOID_BUILD_KEY`) so every isolate of a deployment shares one id. A bare runtime `crypto.randomUUID()` per-isolate in the emitted source would mint a fresh id per isolate, so the edge-cache key (`handleIsr`'s `deploymentId` option) and the KV key (`env.__VOID_ISR_DEPLOYMENT_ID`, also used by the `revalidate()` purge path) would disagree — breaking cache reads and on-demand purge.
|
|
218
|
+
- **Self-host ISR requires a user-declared `ISR_CACHE` KV.** There is no `id: 'local'` injection for it: a placeholder id is fine for miniflare-backed dev but breaks a real `wrangler deploy`, which rejects an unknown namespace id. The user creates the namespace and binds it as `ISR_CACHE`; `readIsrCache`/`writeIsrCache` fail open to a live render when it is absent.
|
|
219
|
+
- **Workers AI binding injection is `command === 'build'`-gated.** `shouldBindWorkersAi()` only injects `{ ai: { binding: 'AI' } }` on build, because dev/miniflare has no local Workers AI emulation — `void dev` AI still routes through the Void proxy (P2 `__VOID_TOKEN` path) and needs `void auth login`.
|
|
@@ -42,7 +42,7 @@ export default defineConfig({
|
|
|
42
42
|
- `void auth login` — platform auth
|
|
43
43
|
- `void deploy` — build and deploy
|
|
44
44
|
- `void db *` — local DB and migration commands
|
|
45
|
-
- `void project *` — link, status, logs, rollback, delete
|
|
45
|
+
- `void project *` — link, status, logs, requests, rollback, delete
|
|
46
46
|
|
|
47
47
|
Runtime helpers include:
|
|
48
48
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
> `npm install --save @types/proper-lockfile`
|
|
3
|
+
|
|
4
|
+
# Summary
|
|
5
|
+
This package contains type definitions for proper-lockfile (https://github.com/moxystudio/node-proper-lockfile).
|
|
6
|
+
|
|
7
|
+
# Details
|
|
8
|
+
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/proper-lockfile.
|
|
9
|
+
## [index.d.ts](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/proper-lockfile/index.d.ts)
|
|
10
|
+
````ts
|
|
11
|
+
import { OperationOptions } from "retry";
|
|
12
|
+
|
|
13
|
+
export interface LockOptions {
|
|
14
|
+
stale?: number | undefined; // default: 10000
|
|
15
|
+
update?: number | undefined; // default: stale/2
|
|
16
|
+
retries?: number | OperationOptions | undefined; // default: 0
|
|
17
|
+
realpath?: boolean | undefined; // default: true
|
|
18
|
+
fs?: any; // default: graceful-fs
|
|
19
|
+
onCompromised?: ((err: Error) => any) | undefined; // default: (err) => throw err
|
|
20
|
+
lockfilePath?: string | undefined; // default: `${file}.lock`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UnlockOptions {
|
|
24
|
+
realpath?: boolean | undefined; // default: true
|
|
25
|
+
fs?: any; // default: graceful-fs
|
|
26
|
+
lockfilePath?: string | undefined; // default: `${file}.lock`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CheckOptions {
|
|
30
|
+
stale?: number | undefined; // default: 10000
|
|
31
|
+
realpath?: boolean | undefined; // default: true
|
|
32
|
+
fs?: any; // default: graceful-fs
|
|
33
|
+
lockfilePath?: string | undefined; // default: `${file}.lock`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function lock(file: string, options?: LockOptions): Promise<() => Promise<void>>;
|
|
37
|
+
export function unlock(file: string, options?: UnlockOptions): Promise<void>;
|
|
38
|
+
export function check(file: string, options?: CheckOptions): Promise<boolean>;
|
|
39
|
+
|
|
40
|
+
export function lockSync(file: string, options?: LockOptions): () => void;
|
|
41
|
+
export function unlockSync(file: string, options?: UnlockOptions): void;
|
|
42
|
+
export function checkSync(file: string, options?: CheckOptions): boolean;
|
|
43
|
+
|
|
44
|
+
````
|
|
45
|
+
|
|
46
|
+
### Additional Details
|
|
47
|
+
* Last updated: Tue, 07 Nov 2023 09:09:39 GMT
|
|
48
|
+
* Dependencies: [@types/retry](https://npmjs.com/package/@types/retry)
|
|
49
|
+
|
|
50
|
+
# Credits
|
|
51
|
+
These definitions were written by [Nikita Volodin](https://github.com/qlonik), [Linus Unnebäck](https://github.com/LinusU), and [ulrichb](https://github.com/ulrichb).
|