void 0.23.0 → 0.25.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.
Files changed (169) hide show
  1. package/AGENTS_TEMPLATE.md +1 -1
  2. package/README.md +19 -106
  3. package/dist/{account-cmd-C84Ee8cO.mjs → account-cmd-DcR1ywt8.mjs} +5 -5
  4. package/dist/{auth-CSkdO2Bb.mjs → auth-7wuZ5ob8.mjs} +22 -49
  5. package/dist/auth-CdgZ9uag.mjs +2 -0
  6. package/dist/{auth-link-CioEg6uY.mjs → auth-link-Z6E5DYvM.mjs} +5 -6
  7. package/dist/{auth-router-BsR981d4.mjs → auth-router-BdM4avI8.mjs} +7 -7
  8. package/dist/{build-cmd-LzvNJORH.mjs → build-cmd-CEE7D-hM.mjs} +7 -8
  9. package/dist/{cache-C4MvnrMH.mjs → cache-TcZnBLtH.mjs} +6 -7
  10. package/dist/{cancel-deploy-abUxpP2n.mjs → cancel-deploy-CYoZmcmR.mjs} +4 -6
  11. package/dist/{cf-access-P0iZnu0q.mjs → cf-access-AHKBzmuu.mjs} +4 -48
  12. package/dist/{cf-access-DRsQRe6k.mjs → cf-access-TWNzXltp.mjs} +3 -11
  13. package/dist/cf-build-output-ChY6-XWP.mjs +2 -0
  14. package/dist/cli/cli.mjs +49 -46
  15. package/dist/cli/cloudflare-operation-process.mjs +16 -7
  16. package/dist/cli/env-schema-probe.mjs +3 -3
  17. package/dist/client-B4Me-9HE.mjs +2 -0
  18. package/dist/{client-RV8NVeB8.mjs → client-RsPo8wsP.mjs} +129 -33
  19. package/dist/{cloudflare-cmd-BcrVyTmJ.mjs → cloudflare-cmd-bhwCxAN6.mjs} +2 -2
  20. package/dist/{cloudflare-connect-B8uPZ4nx.mjs → cloudflare-connect-CuhRgk3k.mjs} +2 -2
  21. package/dist/{cloudflare-operations-AiWashgg.mjs → cloudflare-operations-BhbK6yTq.mjs} +1 -1
  22. package/dist/{cloudflare-operations-fxHb-byx.mjs → cloudflare-operations-CjaV4lZi.mjs} +2 -2
  23. package/dist/{connect-Bd4kJd9U.mjs → connect-DCvUmPXY.mjs} +13 -10
  24. package/dist/{create-project-Boczwj5r.mjs → create-project-C734I-Mu.mjs} +1 -1
  25. package/dist/{create-project-bMf6ffLZ.mjs → create-project-eAPgYq35.mjs} +13 -13
  26. package/dist/{db-8uG64XLl.mjs → db-C07GEdKF.mjs} +27 -30
  27. package/dist/{delete-NvbiyeJf.mjs → delete-pKMVsrpJ.mjs} +5 -6
  28. package/dist/deploy-BDu1vhSH.mjs +2 -0
  29. package/dist/{deploy-DzAIwqNU.mjs → deploy-DfsAe-T0.mjs} +9567 -9215
  30. package/dist/{dev-inbox-P0u4tM8Y.mjs → dev-inbox-D64ZKUod.mjs} +5 -6
  31. package/dist/{domain-y5Tvydvo.mjs → domain-D3uA9ize.mjs} +8 -8
  32. package/dist/{email-B3umsW75.mjs → email-z3o2cwjH.mjs} +8 -8
  33. package/dist/{env-BS6qYHDb.mjs → env-3Q8-_2IH.mjs} +7 -9
  34. package/dist/{env-types-C2_bIk_b.mjs → env-types-D5kds_OZ.mjs} +1 -1
  35. package/dist/{env-validation-DbTg7-ar.mjs → env-validation-cZBvGWaP.mjs} +1 -1
  36. package/dist/{env-validation-BPn7vk-V.mjs → env-validation-vBSQ8gw5.mjs} +3 -3
  37. package/dist/gen-ById9r2j.mjs +2 -0
  38. package/dist/{gen-B580--vC.mjs → gen-NjwoG9FK.mjs} +2 -4
  39. package/dist/{github-cmd-v45BdfKX.mjs → github-cmd-DquUq-b1.mjs} +15 -24
  40. package/dist/{headers-DWi2IXWx.mjs → headers-2rKda9Y6.mjs} +1 -1
  41. package/dist/help-DPGBwomV.mjs +2 -0
  42. package/dist/{help-daGKjXGk.mjs → help-DRZXC4fj.mjs} +8 -4
  43. package/dist/{inbound-aVHEUhKo.mjs → inbound-fRkqcXaC.mjs} +1 -1
  44. package/dist/index.d.mts +18 -14
  45. package/dist/index.mjs +2176 -2110
  46. package/dist/{info-B97bTX9N.mjs → info-BmA3_IFZ.mjs} +1 -3
  47. package/dist/{init-Bvy7zrBo.mjs → init-DT7yuuyl.mjs} +28 -27
  48. package/dist/{link-CDqqCjFl.mjs → link-CXOlqh2F.mjs} +6 -7
  49. package/dist/{list-CZj0dzKY.mjs → list-eEWVJEPn.mjs} +8 -9
  50. package/dist/{local-d1-2CMnpuW_.mjs → local-d1-Df4XpwhW.mjs} +1 -1
  51. package/dist/log-vqY3pqwI.mjs +29 -0
  52. package/dist/login-C98-sw79.mjs +2 -0
  53. package/dist/{login-UZKFM_u7.mjs → login-pWQHYVu2.mjs} +12 -10
  54. package/dist/{logs-CUZ6t9t3.mjs → logs-DnxVX7TR.mjs} +11 -13
  55. package/dist/migrate-CEu2X5DB.mjs +2 -0
  56. package/dist/{migrate-DHul7PRV.mjs → migrate-CV1on-yJ.mjs} +2 -4
  57. package/dist/{operator-args-CLgKGlwU.mjs → operator-args-CAVcmwUt.mjs} +12 -5
  58. package/dist/{operator-auth-8KnOVXVd.mjs → operator-auth-CkmTE3rt.mjs} +2 -2
  59. package/dist/{operator-client-DUwumHpQ.mjs → operator-client-LmwjxT6q.mjs} +3 -3
  60. package/dist/{operator-cmd-DBA6dl0m.mjs → operator-cmd-d-IEKgNk.mjs} +6 -6
  61. package/dist/{output-Dm_A4Tbv.mjs → output-BBsUwdyT.mjs} +22 -14
  62. package/dist/pages/client.d.mts +3 -0
  63. package/dist/pages/client.mjs +120 -29
  64. package/dist/pages/index.mjs +1 -1
  65. package/dist/{platform-args-BJdRtlLq.mjs → platform-args-Bzf8JPc2.mjs} +11 -3
  66. package/dist/platform-args-Cq6EjQro.mjs +2 -0
  67. package/dist/{platform-auth-config-B56E9YP1.mjs → platform-auth-config-0V0lA3pF.mjs} +7 -7
  68. package/dist/{platform-auth-protection-BDWO_aER.mjs → platform-auth-protection-DFeJ-JkK.mjs} +9 -9
  69. package/dist/{platform-auth-recovery-1gg4CSRe.mjs → platform-auth-recovery-ICy4muq6.mjs} +7 -7
  70. package/dist/{platform-cmd-Bt-1w6pY.mjs → platform-cmd-BPySFOew.mjs} +1 -1
  71. package/dist/{platform-cmd-DMcQStSc.mjs → platform-cmd-D7vSWMvV.mjs} +38 -28
  72. package/dist/platform-discovery-DDDUvnqp.mjs +27 -0
  73. package/dist/{platform-domain-BNkcz0OB.mjs → platform-domain-CUdY064e.mjs} +5 -5
  74. package/dist/{platform-lifecycle-BE6C_jxh.mjs → platform-lifecycle-BaUdTRPU.mjs} +1 -1
  75. package/dist/{platform-lifecycle-9OyBALhH.mjs → platform-lifecycle-Dm09JNkn.mjs} +4404 -3297
  76. package/dist/{platform-management-CjwLVQwN.mjs → platform-management-BSEc_ZeG.mjs} +48 -11
  77. package/dist/platform-management-Bp6aoH2C.mjs +2 -0
  78. package/dist/{platform-plans-config-BNGKGr4P.mjs → platform-plans-config-B0pOVQyW.mjs} +3 -3
  79. package/dist/{platform-recovery-D6KSpuFm.mjs → platform-recovery-WOZ95Uze.mjs} +4 -4
  80. package/dist/{platform-registry-QBa4DK32.mjs → platform-registry-KfNEF84O.mjs} +3 -3
  81. package/dist/{prepare-B5Mkic5u.mjs → prepare-39EvOhI6.mjs} +2 -4
  82. package/dist/prepare-CnjcTV2s.mjs +2 -0
  83. package/dist/{prepare-DOsL0CC9.mjs → prepare-DnaKBi--.mjs} +1 -1
  84. package/dist/{project-C0Fqu05O.mjs → project-BkbfDs0V.mjs} +15 -82
  85. package/dist/project-IOjBU8R6.mjs +2 -0
  86. package/dist/{project-cmd-CNzrqvBv.mjs → project-cmd-Bs5A392E.mjs} +21 -22
  87. package/dist/{project-team-DQOWPfQT.mjs → project-team-ohAWWaew.mjs} +8 -9
  88. package/dist/{project-token-CTDYU0Cc.mjs → project-token-CP01Jna6.mjs} +6 -9
  89. package/dist/{project-zero-trust-BNAkW-_0.mjs → project-zero-trust-BzkVgx35.mjs} +5 -6
  90. package/dist/prompts-6OSVA7DJ.mjs +2087 -0
  91. package/dist/prompts-ZAuJlyCj.mjs +2 -0
  92. package/dist/{provision-C4ORE7G7.mjs → provision-BEv0k-qz.mjs} +1 -1
  93. package/dist/{provision-CJFgTZY8.mjs → provision-Btj1jppf.mjs} +5 -7
  94. package/dist/{requests-MhxYnau8.mjs → requests-B3PBuGlX.mjs} +5 -6
  95. package/dist/{rollback-CJ6iSDoU.mjs → rollback-DDub9GsO.mjs} +9 -10
  96. package/dist/runtime/ai.mjs +3 -5
  97. package/dist/runtime/email/testing.mjs +2 -2
  98. package/dist/runtime/email.mjs +36 -18
  99. package/dist/runtime/remote/index.mjs +2 -2
  100. package/dist/runtime/routing.mjs +1 -1
  101. package/dist/{secret-Dli5fP0B.mjs → secret-3c72lY1U.mjs} +15 -16
  102. package/dist/{skills-D1II1Juz.mjs → skills-BPQUGClu.mjs} +1 -1
  103. package/dist/{subcommand-prompt-CY1C4fvl.mjs → subcommand-prompt-Dhom39bd.mjs} +40 -32
  104. package/dist/sveltekit.d.mts +1 -1
  105. package/dist/sveltekit.mjs +4 -9
  106. package/dist/{wrangler-7K-bW_DL.mjs → wrangler-DhUerFkW.mjs} +10 -15
  107. package/package.json +22 -23
  108. package/sandbox.Dockerfile +1 -1
  109. package/skills/void/SKILL.md +6 -1
  110. package/skills/void/docs/guide/ai.md +24 -86
  111. package/skills/void/docs/guide/auth.md +0 -2
  112. package/skills/void/docs/guide/database/d1.md +7 -7
  113. package/skills/void/docs/guide/deployment.md +58 -102
  114. package/skills/void/docs/guide/edge/headers.md +1 -1
  115. package/skills/void/docs/guide/email/domains.md +5 -1
  116. package/skills/void/docs/guide/email/sending.md +1 -1
  117. package/skills/void/docs/guide/env-vars.md +1 -5
  118. package/skills/void/docs/guide/index.md +19 -18
  119. package/skills/void/docs/guide/kv.md +0 -2
  120. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +49 -47
  121. package/skills/void/docs/guide/pages-routing/overview.md +2 -2
  122. package/skills/void/docs/guide/platform/administration/email.md +0 -2
  123. package/skills/void/docs/guide/platform/administration/operations.md +11 -22
  124. package/skills/void/docs/guide/platform/administration/plans.md +1 -5
  125. package/skills/void/docs/guide/platform/administration/zero-trust.md +24 -9
  126. package/skills/void/docs/guide/platform/development/schema-ci.md +7 -1
  127. package/skills/void/docs/guide/platform/installation/ci.md +1 -1
  128. package/skills/void/docs/guide/platform/installation/credentials.md +10 -3
  129. package/skills/void/docs/guide/platform/installation/domains.md +8 -3
  130. package/skills/void/docs/guide/platform/installation/first-deployment.md +2 -2
  131. package/skills/void/docs/guide/platform/installation/maintenance.md +37 -5
  132. package/skills/void/docs/guide/platform/installation/prerequisites.md +87 -14
  133. package/skills/void/docs/guide/platform/installation/setup.md +80 -10
  134. package/skills/void/docs/guide/quickstart.md +37 -80
  135. package/skills/void/docs/guide/remote-dev.md +1 -1
  136. package/skills/void/docs/guide/sandboxes.md +36 -46
  137. package/skills/void/docs/guide/self-hosted-platform.md +20 -0
  138. package/skills/void/docs/guide/websockets.md +0 -2
  139. package/skills/void/docs/integrations/frameworks/overview.md +2 -2
  140. package/skills/void/docs/integrations/frameworks/sveltekit.md +40 -25
  141. package/skills/void/docs/integrations/nodejs-bun-deno.md +7 -2
  142. package/skills/void/docs/reference/api/resources.md +27 -0
  143. package/skills/void/docs/reference/api.md +1 -1
  144. package/skills/void/docs/reference/cli/auth.md +4 -3
  145. package/skills/void/docs/reference/cli/database.md +1 -1
  146. package/skills/void/docs/reference/cli/deploy.md +21 -1
  147. package/skills/void/docs/reference/cli/platform-installation.md +42 -27
  148. package/skills/void/docs/reference/cli/platform-operations.md +4 -8
  149. package/skills/void/docs/reference/cli/platform-zero-trust.md +14 -0
  150. package/skills/void/docs/reference/cli/platform.md +5 -0
  151. package/skills/void/docs/reference/config.md +1 -1
  152. package/skills/void/docs/reference/resource-inference.md +11 -8
  153. package/dist/auth-CCjt0hcq.mjs +0 -2
  154. package/dist/cf-build-output-_0HNWysu.mjs +0 -2
  155. package/dist/client-Cu7jWiF1.mjs +0 -2
  156. package/dist/deploy-bjXdFCtn.mjs +0 -2
  157. package/dist/dist-BuDuKZJv.mjs +0 -2
  158. package/dist/dist-CTBk70IR.mjs +0 -1310
  159. package/dist/gen-BVaUUumi.mjs +0 -2
  160. package/dist/help-DofyZuY7.mjs +0 -2
  161. package/dist/log-ChfPKsVd.mjs +0 -26
  162. package/dist/login-DYt_An22.mjs +0 -2
  163. package/dist/migrate-8_2u55MD.mjs +0 -2
  164. package/dist/platform-args-D3RXyR6h.mjs +0 -2
  165. package/dist/platform-management-CnyTdcWX.mjs +0 -2
  166. package/dist/prepare-cgvDMtSb.mjs +0 -2
  167. package/dist/project-BuUwfj6f.mjs +0 -2
  168. package/skills/void/docs/guide/env-migration.md +0 -109
  169. package/dist/{cf-build-output-BPqOT964.mjs → cf-build-output-U8o1Gl6X.mjs} +1 -1
@@ -4,7 +4,7 @@ import { m as readConfig } from "./config-Br_JZD6u.mjs";
4
4
  import { a as hasSandboxSqliteMigration, c as resolveSandboxConfig, i as assertSandboxNamespaceConfig, l as sandboxContainerConfig, r as SANDBOX_MIGRATION_TAG } from "./config-C_XRIPx2.mjs";
5
5
  import { i as persistCloudflareConfig, r as materializeCloudflareConfig } from "./cloudflare-config-Bktvwtpf.mjs";
6
6
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
7
- import { appendSqliteClassMigration } from "@void/deploy-core";
7
+ import { appendSqliteClassMigration, isRecord } from "@void/deploy-core";
8
8
  import { createHash } from "node:crypto";
9
9
  import { SyntaxKind, applyEdits, createScanner, findNodeAtLocation, modify, parse, parseTree } from "jsonc-parser";
10
10
  //#region src/cli/wrangler.ts
@@ -686,7 +686,10 @@ function writeProvisionedWranglerConfig(root, patch, options) {
686
686
  const matches = [];
687
687
  for (let i = 0; i < currentList.length; i++) {
688
688
  const entry = currentList[i];
689
- if (isRecord(entry) && entry[identityKey] === binding) matches.push(i);
689
+ if (isRecord(entry) && entry[identityKey] === binding) matches.push({
690
+ index: i,
691
+ entry
692
+ });
690
693
  }
691
694
  if (matches.length > 1) throw new Error(`deploy: cannot update "${configPath}" — binding ${JSON.stringify(binding)} is declared ${matches.length} times in "${field}". Void cannot choose which entry to update, and wrangler rejects duplicate binding names. Remove the duplicate and retry.`);
692
695
  const owners = collectWorkerBindingNames(configWithoutField(currentConfig, field));
@@ -694,8 +697,7 @@ function writeProvisionedWranglerConfig(root, patch, options) {
694
697
  const owner = owners.get(binding);
695
698
  if (owner !== void 0) throw new Error(`deploy: cannot update "${configPath}" — cannot adopt ${JSON.stringify(binding)} into "${field}" because that binding name is already used by a ${owner} binding. Wrangler requires every binding name to be unique across the whole worker, so this would make the config undeployable. Rename the conflicting binding and retry.`);
696
699
  if (matches.length === 1) {
697
- const index = matches[0];
698
- const existing = isRecord(currentList[index]) ? currentList[index] : {};
700
+ const { index, entry: existing } = matches[0];
699
701
  for (const [key, value] of Object.entries(properties)) if (!jsonEqual(existing[key], value)) set([
700
702
  field,
701
703
  index,
@@ -752,20 +754,16 @@ function writeProvisionedWranglerConfig(root, patch, options) {
752
754
  const upsertQueueEntries = (section, entries) => {
753
755
  if (!entries || entries.length === 0) return;
754
756
  const currentQueues = assertObjectContainer(parseJsonc(text).queues, "queues");
755
- if (section === "producers") assertValidQueueProducers(currentQueues?.producers);
756
- const currentList = Array.isArray(currentQueues?.[section]) ? [...currentQueues[section]] : [];
757
+ const currentList = section === "producers" ? assertValidQueueProducers(currentQueues?.producers) : Array.isArray(currentQueues?.consumers) ? [...currentQueues.consumers] : [];
757
758
  for (const entry of entries) {
758
759
  let index;
759
760
  if (section === "producers") {
760
761
  const matches = [];
761
- for (let i = 0; i < currentList.length; i++) {
762
- const existing = currentList[i];
763
- if (isRecord(existing) && existing.binding === entry.binding) matches.push(i);
764
- }
762
+ for (let i = 0; i < currentList.length; i++) if (currentList[i].binding === entry.binding) matches.push(i);
765
763
  if (matches.length > 1) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(entry.binding)} is declared ${matches.length} times across producers. A producer binding must map to exactly one queue, so wrangler rejects this config. Remove the duplicate producers and retry.`);
766
764
  if (matches.length === 1) {
767
765
  const existing = currentList[matches[0]];
768
- if (isRecord(existing) && existing.queue !== entry.queue) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(entry.binding)} is already bound to queue ${JSON.stringify(existing.queue)}, but queue ${JSON.stringify(entry.queue)} also requests it. A producer binding must map to exactly one queue. Rename or remove the conflicting producer and retry.`);
766
+ if (existing.queue !== entry.queue) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(entry.binding)} is already bound to queue ${JSON.stringify(existing.queue)}, but queue ${JSON.stringify(entry.queue)} also requests it. A producer binding must map to exactly one queue. Rename or remove the conflicting producer and retry.`);
769
767
  }
770
768
  index = matches.length === 1 ? matches[0] : -1;
771
769
  workerBindingOwners ??= collectWorkerBindingNames(parseJsonc(text));
@@ -774,7 +772,7 @@ function writeProvisionedWranglerConfig(root, patch, options) {
774
772
  if (owner !== void 0) throw new Error(`deploy: cannot update "${configPath}" — queue producer binding ${JSON.stringify(bindingName)} is already used by a ${owner} binding. Wrangler requires every binding name to be unique across the whole worker, so this producer would make the config undeployable. Rename the conflicting binding (or the queue) and retry.`);
775
773
  } else index = currentList.findIndex((existing) => isRecord(existing) && existing.queue === entry.queue);
776
774
  if (index >= 0) {
777
- const existing = isRecord(currentList[index]) ? currentList[index] : {};
775
+ const existing = currentList[index];
778
776
  for (const [key, value] of Object.entries(entry)) {
779
777
  if (value === void 0) continue;
780
778
  if (!jsonEqual(existing[key], value)) set([
@@ -1243,9 +1241,6 @@ function offsetToLineColumn(text, offset) {
1243
1241
  column
1244
1242
  };
1245
1243
  }
1246
- function isRecord(value) {
1247
- return typeof value === "object" && value !== null && !Array.isArray(value);
1248
- }
1249
1244
  function validateDurableObjectMigrations(value, configPath) {
1250
1245
  if (value === void 0) return [];
1251
1246
  if (!Array.isArray(value)) throw new Error(`deploy: cannot update "${configPath}" — "migrations" must be an array of Durable Object migration steps.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -331,24 +331,24 @@
331
331
  }
332
332
  },
333
333
  "dependencies": {
334
- "@cloudflare/build-output-utils": "0.8.4",
335
- "@cloudflare/codemods": "0.3.0",
336
- "@cloudflare/config": "0.22.0",
334
+ "@cloudflare/build-output-utils": "0.8.5",
335
+ "@cloudflare/codemods": "0.4.0",
336
+ "@cloudflare/config": "0.23.0",
337
337
  "@cloudflare/sandbox": "1.0.0",
338
- "@cloudflare/vite-plugin": "1.62.2",
339
- "@cloudflare/workers-auth": "0.9.5",
338
+ "@cloudflare/vite-plugin": "1.62.5",
339
+ "@cloudflare/workers-auth": "0.12.0",
340
340
  "@cloudflare/workers-types": "^4.20260702.1",
341
341
  "@napi-rs/keyring": "2.1.0",
342
- "@void/deploy-cloudflare": "0.23.0",
343
- "@void/deploy-core": "0.23.0",
344
- "@void/edge": "0.23.0",
345
- "@void/isr": "0.23.0",
346
- "@void/platform": "0.23.0",
342
+ "@void/deploy-cloudflare": "0.25.0",
343
+ "@void/deploy-core": "0.25.0",
344
+ "@void/edge": "0.25.0",
345
+ "@void/isr": "0.25.0",
346
+ "@void/platform": "0.25.0",
347
347
  "acorn": "^8.18.0",
348
- "better-auth": "^1.7.6",
348
+ "better-auth": "^1.7.7",
349
349
  "better-sqlite3": "^13.0.3",
350
350
  "blake3-jit": "^1.1.0",
351
- "cf": "1.0.0-beta.9",
351
+ "cf": "1.0.0-beta.12",
352
352
  "drizzle-arktype": "^0.1.3",
353
353
  "drizzle-kit": "^0.31.11",
354
354
  "drizzle-orm": "^0.45.3",
@@ -356,28 +356,27 @@
356
356
  "drizzle-zod": "^0.8.3",
357
357
  "email-validator": "^2.0.4",
358
358
  "es-module-lexer": "^3.0.2",
359
- "hono": "^4.13.11",
360
- "ignore": "^7.0.10",
359
+ "hono": "^4.13.13",
360
+ "ignore": "^7.0.12",
361
361
  "jiti": "^2.7.0",
362
362
  "jsonc-parser": "3.3.1",
363
363
  "mime": "4.1.0",
364
- "miniflare": "5.20260926.1-alpha",
364
+ "miniflare": "5.20261001.0-alpha",
365
365
  "minimatch": "^10.2.6",
366
366
  "mysql2": "3.24.5",
367
- "pg": "8.23.0",
368
- "postal-mime": "^4.0.0",
367
+ "pg": "8.23.1",
368
+ "postal-mime": "^4.0.4",
369
369
  "proper-lockfile": "^4.1.2",
370
370
  "undici": "8.11.2"
371
371
  },
372
372
  "devDependencies": {
373
- "@clack/core": "^1.5.1",
374
- "@clack/prompts": "^1.8.1",
375
373
  "@electric-sql/pglite": "0.5.8",
376
374
  "@types/better-sqlite3": "^9.6.0",
377
- "@types/node": "^26.6.3",
375
+ "@types/node": "^26.6.4",
378
376
  "@types/pg": "^8.23.1",
379
377
  "@types/proper-lockfile": "^4.1.4",
380
- "arktype": "^2.2.5",
378
+ "@voidzero-dev/vite-plus-prompts": "0.0.0",
379
+ "arktype": "^2.2.7",
381
380
  "ci-info": "^4.4.0",
382
381
  "estree-walker": "^3.0.3",
383
382
  "fast-wrap-ansi": "^0.2.2",
@@ -394,7 +393,7 @@
394
393
  "zod": "^4.6.5"
395
394
  },
396
395
  "peerDependencies": {
397
- "@void/md": "0.23.0",
396
+ "@void/md": "0.25.0",
398
397
  "arktype": ">=2.0.0",
399
398
  "valibot": ">=1.0.0-beta.7",
400
399
  "vite": "^8.0.0",
@@ -1,4 +1,4 @@
1
- FROM node:24.21.0-trixie-slim
1
+ FROM node:26.10.0-trixie-slim
2
2
  COPY --from=docker.io/cloudflare/sandbox:1.0.0 /usr/local/bin/sandbox-shim /usr/local/bin/sandbox-shim
3
3
  WORKDIR /workspace
4
4
  CMD ["sleep", "infinity"]
@@ -16,7 +16,12 @@ workflows. Mention Wrangler only when the user needs to recognize a literal
16
16
  configuration file or environment variable.
17
17
 
18
18
  Use `void` and `@void/*` in imports, examples, and package manifests. Follow the
19
- project's existing framework and deployment destination.
19
+ project's existing framework and deployment destination. Deploying to a Void
20
+ platform requires an explicit `void connect <url>` first; never infer a platform
21
+ from old examples, tokens, or project IDs.
22
+ For a one-time deployment elsewhere, connect to that platform, then set
23
+ `VOID_API_URL` for the deploy command and pass `--project <slug>`. This preserves
24
+ the app's existing saved destination, including when the target project is new.
20
25
 
21
26
  ## Task Routing
22
27
 
@@ -4,19 +4,24 @@ outline: deep
4
4
 
5
5
  # AI
6
6
 
7
- Void provides a typed AI client for Workers AI and Cloudflare's [AI Gateway](https://developers.cloudflare.com/ai-gateway/). Import `ai` from `void/ai` and run inference directly from your route handlers. Direct deployments use your Cloudflare account; a team platform routes requests through its installation's proxy and usage controls.
7
+ Run Workers AI models and provider APIs from server code with `ai` from `void/ai`. Provider requests use Cloudflare's [AI Gateway](https://developers.cloudflare.com/ai-gateway/).
8
8
 
9
- ```ts
10
- import { ai } from 'void/ai';
11
- ```
9
+ ## Local Development
12
10
 
13
- ## Handling usage limits
11
+ For a direct Cloudflare project, connect your account and start development:
14
12
 
15
- Every AI operation is lazy and must be executed with `.match({ ok, limited })`. Both handlers are required, including for provider-native requests, model listing and document conversion. Return a useful fallback from `limited`, or use `limit.response({ message: 'AI is temporarily unavailable.' })` to send a structured HTTP 429. Limits expose `resource`, `reason`, and an optional `retryAt`. Other failures still reject.
13
+ ```sh
14
+ void connect --platform cloudflare
15
+ npm run dev
16
+ ```
16
17
 
17
- Do not await the operation itself; await its `.match()` call. TypeScript checks your handlers and the resulting success/fallback union.
18
+ When the app imports `void/ai`, Void enables a remote Workers AI binding during
19
+ development and preview. Requests use your Cloudflare account and its allowance;
20
+ Workers AI has no local simulator. Use your Cloudflare login or
21
+ `CLOUDFLARE_API_TOKEN` in an automated environment.
18
22
 
19
- Streaming responses end with a `void:error` SSE event if generation is interrupted after the response starts. A recognized quota interruption calls your `limited` handler and emits `void:limit`, preserving the message from `limit.response()`. `fetchStream()` throws on either terminal event so your UI can exit its generating state. This also applies to streaming provider responses and `ai.run()` with `stream: true`.
23
+ For a team platform, use `void connect <platform-url>` and link your project.
24
+ Development requests use that platform's account and usage limits.
20
25
 
21
26
  ## Basic Usage
22
27
 
@@ -29,16 +34,16 @@ import { ai } from 'void/ai';
29
34
  export const POST = defineHandler(async (c) => {
30
35
  const { prompt } = await c.req.json();
31
36
 
32
- const result = await ai
37
+ return ai
33
38
  .run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
34
39
  messages: [{ role: 'user', content: prompt }],
35
40
  })
36
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
37
-
38
- return c.json(result);
41
+ .match({ ok: (result) => c.json(result), limited: (limit) => limit.response() });
39
42
  });
40
43
  ```
41
44
 
45
+ Every operation runs through `.match({ ok, limited })`. Both handlers are required. Return a fallback from `limited`, or use `limit.response()` to send a structured HTTP 429. Limits include `resource`, `reason`, and an optional `retryAt`; other failures throw. Await `.match()`, rather than the operation itself.
46
+
42
47
  You can use any model available through Cloudflare's AI binding, including Workers AI models such as `@cf/meta/llama-3.3-70b-instruct-fp8-fast` and Cloudflare Gateway models such as `google/gemini-2.5-flash` or `openai/gpt-4.1-mini`. The input object must match the selected Cloudflare model's schema. Models that return binary data, such as generated images, are returned as a `Blob` from `ai.run()`.
43
48
 
44
49
  ## Streaming
@@ -60,6 +65,8 @@ export const POST = defineHandler(async (c) => {
60
65
  });
61
66
  ```
62
67
 
68
+ Streaming responses end with a `void:error` SSE event if generation is interrupted after the response starts. A recognized quota interruption calls your `limited` handler and emits `void:limit`, preserving the message from `limit.response()`. `fetchStream()` throws on either terminal event so your UI can exit its generating state. This also applies to streaming provider responses and `ai.run()` with `stream: true`.
69
+
63
70
  ## Listing Models
64
71
 
65
72
  Use `ai.models()` to list available models:
@@ -85,23 +92,6 @@ const result = await ai
85
92
  .match({ ok: (result) => result, limited: (limit) => limit.response() });
86
93
  ```
87
94
 
88
- ## Local Development
89
-
90
- For a direct Cloudflare project, connect your account and start development:
91
-
92
- ```sh
93
- void connect --platform cloudflare
94
- npm run dev
95
- ```
96
-
97
- When the app imports `void/ai`, Void enables a remote Workers AI binding during
98
- development and preview. Requests use your Cloudflare account and its allowance;
99
- Workers AI has no local simulator. Use your Cloudflare login or
100
- `CLOUDFLARE_API_TOKEN` in an automated environment.
101
-
102
- For a team platform, use `void connect <platform-url>` and link your project.
103
- Development requests use that platform's account and usage limits.
104
-
105
95
  ## Usage Limits
106
96
 
107
97
  Workers AI usage is measured in [neurons](https://developers.cloudflare.com/workers-ai/platform/pricing/).
@@ -127,33 +117,7 @@ const result = await ai
127
117
  .match({ ok: (result) => result, limited: (limit) => limit.response() });
128
118
  ```
129
119
 
130
- OpenAI-compatible models use OpenAI-style `messages`:
131
-
132
- ```ts
133
- const result = await ai
134
- .run('openai/gpt-4.1-mini', {
135
- messages: [{ role: 'user', content: 'Summarize this deploy.' }],
136
- })
137
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
138
- ```
139
-
140
- Pass Cloudflare AI Gateway options as the third argument:
141
-
142
- ```ts
143
- const result = await ai
144
- .run(
145
- 'openai/gpt-4.1-mini',
146
- {
147
- messages: [{ role: 'user', content: 'Summarize this deploy.' }],
148
- },
149
- {
150
- gateway: {
151
- skipCache: true,
152
- },
153
- },
154
- )
155
- .match({ ok: (result) => result, limited: (limit) => limit.response() });
156
- ```
120
+ Pass AI Gateway options as the third argument: `ai.run(model, inputs, { gateway: { skipCache: true } })`.
157
121
 
158
122
  For provider-native requests on a direct deployment, configure [`ai.gateway`](../integrations/cloudflare.md#ai-self-host).
159
123
 
@@ -173,7 +137,7 @@ import { ai } from 'void/ai';
173
137
  export const POST = defineHandler(async (c) => {
174
138
  const { prompt } = await c.req.json();
175
139
 
176
- const response = await ai
140
+ return ai
177
141
  .provider('openai')
178
142
  .fetch('/chat/completions', {
179
143
  body: {
@@ -183,9 +147,6 @@ export const POST = defineHandler(async (c) => {
183
147
  },
184
148
  })
185
149
  .match({ ok: (result) => result, limited: (limit) => limit.response() });
186
-
187
- const result = await response.json();
188
- return c.json(result);
189
150
  });
190
151
  ```
191
152
 
@@ -205,8 +166,6 @@ const response = await ai
205
166
  },
206
167
  })
207
168
  .match({ ok: (result) => result, limited: (limit) => limit.response() });
208
-
209
- const result = await response.json();
210
169
  ```
211
170
 
212
171
  ### Custom Providers
@@ -277,30 +236,9 @@ export const POST = defineHandler(async (c) => {
277
236
 
278
237
  ### Provider Key Convention
279
238
 
280
- Provider-native requests require an API key set as a project secret. The env var name is automatically derived from the provider name:
281
-
282
- | Provider prefix | Env var |
283
- | ------------------ | --------------------- |
284
- | `openai` | `OPENAI_API_KEY` |
285
- | `anthropic` | `ANTHROPIC_API_KEY` |
286
- | `google-ai-studio` | `GOOGLE_API_KEY` |
287
- | `deepseek` | `DEEPSEEK_API_KEY` |
288
- | `groq` | `GROQ_API_KEY` |
289
- | `mistral` | `MISTRAL_API_KEY` |
290
- | `grok` | `GROK_API_KEY` |
291
- | `openrouter` | `OPENROUTER_API_KEY` |
292
- | `perplexity` | `PERPLEXITY_API_KEY` |
293
- | `cohere` | `COHERE_API_KEY` |
294
- | `cerebras` | `CEREBRAS_API_KEY` |
295
- | `huggingface` | `HUGGINGFACE_API_KEY` |
296
- | `replicate` | `REPLICATE_API_KEY` |
297
- | `baseten` | `BASETEN_API_KEY` |
298
- | `cartesia` | `CARTESIA_API_KEY` |
299
- | `deepgram` | `DEEPGRAM_API_KEY` |
300
- | `elevenlabs` | `ELEVENLABS_API_KEY` |
301
- | `fal` | `FAL_API_KEY` |
302
- | `ideogram` | `IDEOGRAM_API_KEY` |
303
- | `parallel` | `PARALLEL_API_KEY` |
239
+ Provider-native requests use a project secret, such as `OPENAI_API_KEY` for OpenAI or `GOOGLE_API_KEY` for Google AI Studio.
240
+
241
+ See the [provider key reference](../reference/api/resources.md#provider-keys) for supported providers and their secret names.
304
242
 
305
243
  OpenAI-style providers use `Authorization: Bearer <key>`. Google AI Studio uses `x-goog-api-key`. Use `apiKeyHeader` and `apiKeyPrefix` for custom providers.
306
244
 
@@ -155,5 +155,3 @@ void db generate
155
155
  ```
156
156
 
157
157
  Review and commit the SQL before deploying. Void includes your auth configuration and plugin tables; you don't need to duplicate them in `db/schema.ts` or run the Better Auth CLI.
158
-
159
- MySQL stores OAuth access, refresh, and ID tokens as unbounded text. Existing direct-deploy MySQL apps should run `void db generate` once after upgrading to widen earlier `varchar(255)` token columns. Void platform deployments apply the same safe widening automatically.
@@ -6,12 +6,6 @@ outline: deep
6
6
 
7
7
  [Cloudflare D1](https://developers.cloudflare.com/d1/) is Void's default SQLite database. Start with a [schema](../database.md#schema-definition); Void manages the database locally and when you deploy.
8
8
 
9
- ## Read replication
10
-
11
- Void enables [D1 read replication](https://developers.cloudflare.com/d1/best-practices/read-replication/) when it provisions a database. To enable it on an existing database, run `void deploy --platform cloudflare --provision`. Queries through `db` from `void/db` use a D1 session for each Void request, so reads can run near the request and later queries in that request see at least the same data. Framework handlers without Void's request scope start each database operation on the primary. `createDb` starts a replica-capable session for the binding you pass it.
12
-
13
- Replicas can lag between requests. Authentication session checks start on the primary so a revoked session is not accepted because of replica lag. If another read must start with the latest primary data, use `env.DB.withSession('first-primary')` from `cloudflare:workers`. Other query libraries and raw D1 calls need their own `withSession()` to use replicas; a bare D1 binding reads from the primary.
14
-
15
9
  ## Local Database
16
10
 
17
11
  `void dev` and local `void db` commands share the same database. If `cloudflare.d1_databases` in `void.config.ts` declares the D1 binding, Void uses its ID. A `preview_database_id` takes precedence during local development.
@@ -44,6 +38,12 @@ await db.batch([
44
38
  ]);
45
39
  ```
46
40
 
41
+ ## Read replication
42
+
43
+ Void enables [D1 read replication](https://developers.cloudflare.com/d1/best-practices/read-replication/) when it provisions a database. To enable it on an existing database, run `void deploy --platform cloudflare --provision`. Queries through `db` from `void/db` use a D1 session for each Void request, so reads can run near the request and later queries in that request see at least the same data. Framework handlers without Void's request scope start each database operation on the primary. `createDb` starts a replica-capable session for the binding you pass it.
44
+
45
+ Replicas can lag between requests. Authentication session checks start on the primary so a revoked session is not accepted because of replica lag. If another read must start with the latest primary data, use `env.DB.withSession('first-primary')` from `cloudflare:workers`. Other query libraries and raw D1 calls need their own `withSession()` to use replicas; a bare D1 binding reads from the primary.
46
+
47
47
  ## Alternative Query Layers
48
48
 
49
49
  Use `env.DB` from `cloudflare:workers` with another query library or the raw D1 API:
@@ -69,5 +69,5 @@ const { results } = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bin
69
69
  Void still handles binding inference and deployment regardless of which query layer you use.
70
70
 
71
71
  ::: warning ⚠️ Meta framework limitation
72
- Nuxt and SvelteKit do not support `cloudflare:workers` imports during local development. Use `void/db` or your framework's binding access API.
72
+ Nuxt and SvelteKit 2 do not support `cloudflare:workers` imports during local development. Use `void/db` or your framework's binding access API. SvelteKit 3 supports these imports.
73
73
  :::