@voltro/cli 0.37.0 → 0.39.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 (183) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/THIRD-PARTY-NOTICES.md +80 -45
  3. package/dist/{addCommand-BNeoeSxe.js → addCommand-C05L9tZh.js} +2 -2
  4. package/dist/addCommand-C3MdJG-a.js +2 -0
  5. package/dist/{agentsMd-mhQMF1bx.js → agentsMd-7zI2h5l9.js} +3 -3
  6. package/dist/agentsMd-BFCXh2gl.js +2 -0
  7. package/dist/apiBuild-2GvK8CUB.js +2 -0
  8. package/dist/{apiBuild-DGsShRuF.js → apiBuild-DGalUk9v.js} +3 -3
  9. package/dist/baselineCommand-CXv680Dc.js +2 -0
  10. package/dist/{baselineCommand-C6NMt-oa.js → baselineCommand-Ck2Xm8M7.js} +18 -18
  11. package/dist/bin.js +1 -1
  12. package/dist/bootRefusal-pgvDxlrU.js +7 -0
  13. package/dist/{build-CiFA-E19.js → build-CfF6t0UM.js} +88 -81
  14. package/dist/{capabilitiesCommand-nq_pz5xd.js → capabilitiesCommand-D5gucwYa.js} +8 -8
  15. package/dist/{checkCommand-CIPYyNyc.js → checkCommand-BV56Pcc1.js} +1 -1
  16. package/dist/{checkCommand-CLPTzYqM.js → checkCommand-CBtynBKW.js} +34 -34
  17. package/dist/{cloudCmd-NQSwe_Qk.js → cloudCmd-C42gaO8s.js} +16 -16
  18. package/dist/{clusterCmd-D5wsCmA_.js → clusterCmd-DrVFCzSj.js} +8 -4
  19. package/dist/{codegen-GYYcdpCg.js → codegen-CbpGCWLG.js} +1 -1
  20. package/dist/codegen-i8QGcHsi.js +2 -0
  21. package/dist/codegenCommand-W7SDdiAQ.js +129 -0
  22. package/dist/{codemodRunner-cyqu5oxr.js → codemodRunner-D-jTyvWo.js} +679 -495
  23. package/dist/{commands-BwG9A9Vv.js → commands-BD9eBRY3.js} +48 -48
  24. package/dist/{connectionConfig-UFlIEiys.js → connectionConfig-Bk9IC7D0.js} +1 -1
  25. package/dist/{dashboardCommand-C7U146xQ.js → dashboardCommand-7qGylm0F.js} +3 -3
  26. package/dist/{dataCommand-BOwd8_aJ.js → dataCommand-C_F2DYxe.js} +14 -11
  27. package/dist/{dbCommand-C4UW8cVN.js → dbCommand-DSwGv9wS.js} +50 -49
  28. package/dist/dbCommand-DcqEyxju.js +2 -0
  29. package/dist/dev-alhkKoEX.js +3 -0
  30. package/dist/{dev-bwDgSObH.js → dev-gqpnzVhI.js} +2357 -2280
  31. package/dist/{dialectDriver-CgXnDfec.js → dialectDriver-czCHYpeH.js} +2 -1
  32. package/dist/{doctorCommand-BXyrtN2l.js → doctorCommand-Bvs-BQrM.js} +242 -238
  33. package/dist/doctorCommand-CVXfRrng.js +2 -0
  34. package/dist/{dormancyCommand-BDdRRLsa.js → dormancyCommand-B-PPHc9Q.js} +1 -1
  35. package/dist/{e2eCmd-BRabZww-.js → e2eCmd-uFHig1hV.js} +8 -2
  36. package/dist/{embeddingsCommand-CjWGcAE6.js → embeddingsCommand-OKY6XjUf.js} +6 -6
  37. package/dist/{envCommand-B_TeG297.js → envCommand-B1-Zm85H.js} +2 -2
  38. package/dist/{evolveCommand-D6N-WfA4.js → evolveCommand-C4-NlFbd.js} +6 -8
  39. package/dist/{generateCommand-DbgcUpGw.js → generateCommand-CcyvH2ve.js} +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/{infoCommand-BHKTprrx.js → infoCommand-CTo8Jnhq.js} +7 -7
  42. package/dist/inspect-B7U7Cl_Z.js +1192 -0
  43. package/dist/inspect-DUze25t0.js +2 -0
  44. package/dist/{inspectCmd-EHFZ9yYu.js → inspectCmd-niF97fAq.js} +5 -5
  45. package/dist/inspectGateHint-BjnFubmH.js +7 -0
  46. package/dist/{logFileSink-C_D2wRN1.js → logFileSink-B4uP8pvf.js} +1 -1
  47. package/dist/{logsCmd-D36xK7Zu.js → logsCmd-B6oNsfaZ.js} +10 -7
  48. package/dist/{manifestBuild-hpPLaGxV.js → manifestBuild-BK42hu0k.js} +1 -1
  49. package/dist/manifestBuild-j0n109tt.js +2 -0
  50. package/dist/{metaCommands-CIoQNlaQ.js → metaCommands-DUYR--Ts.js} +1 -1
  51. package/dist/{migrate-RwgUWXfc.js → migrate-BnPw2zC8.js} +7 -4
  52. package/dist/{packageCommand-Cug_3Ogl.js → packageCommand-9SVyvsSo.js} +3 -2
  53. package/dist/{probeCommand-BPEpwT32.js → probeCommand-C9gazU0H.js} +35 -24
  54. package/dist/{projectScaffold-CxgtJvlb.js → projectScaffold-BEhhHjPr.js} +1 -1
  55. package/dist/{projectScaffold-DDgWbNLe.js → projectScaffold-BvhLOrLq.js} +17 -15
  56. package/dist/{runtimeTrace-WYV7qYvz.js → runtimeTrace-DzZOCd94.js} +1 -1
  57. package/dist/{scheduleManifestCmd-D2x0CTTY.js → scheduleManifestCmd-kmWzrO_w.js} +6 -9
  58. package/dist/{sdkgen-TM5sSdf7.js → sdkgen-C4roLErM.js} +4 -4
  59. package/dist/{seedRunner-ZmLSqNe2.js → seedRunner-IdHEprqf.js} +1 -4
  60. package/dist/serveCommand-B6TATyCj.js +1770 -0
  61. package/dist/serveCommand-CNR0gI9V.js +2 -0
  62. package/dist/serveEntry.js +2 -2
  63. package/dist/{serverlessCommand-CfJZy6dS.js → serverlessCommand-DbJ7Plg9.js} +3 -3
  64. package/dist/{start-BgN62boB.js → start-BNuTWdRd.js} +1 -1
  65. package/dist/{start-T4VesWiM.js → start-ft_KzFTd.js} +240 -237
  66. package/dist/startEntry.js +1 -1
  67. package/dist/startup.d.ts +40 -1
  68. package/dist/startup.js +2 -2
  69. package/dist/startupRunner-CEqQq7ax.js +108 -0
  70. package/dist/{test-DIQ0jlkQ.js → test-CLYXrZ2F.js} +1 -1
  71. package/dist/{tracesCmd-DStmCJPi.js → tracesCmd-DgtgOUdi.js} +7 -4
  72. package/dist/{typecheckCommand-BlsWiCNq.js → typecheckCommand-BuCtT92o.js} +7 -7
  73. package/dist/updateCommand-6FMU2klq.js +2 -0
  74. package/dist/{updateCommand-9GHHsmDf.js → updateCommand-CHBmCB17.js} +5 -3
  75. package/dist/{webDev-Dybxew86.js → webDev-CTpSY-e_.js} +892 -856
  76. package/dist/webDev-id3I5PvG.js +2 -0
  77. package/dist/{webhookDiscovery-D7VaeMlz.js → webhookDiscovery-CphsQe59.js} +4 -6
  78. package/dist/{webhookDiscovery-CrGAfhIG.js → webhookDiscovery-il9ti-HE.js} +1 -1
  79. package/dist/{webhooksCommand-CHWSPk5z.js → webhooksCommand-BVvOtZ1B.js} +1 -1
  80. package/package.json +39 -21
  81. package/templates/AGENTS.md +2 -2
  82. package/templates/agent-docs/_index.md +2 -2
  83. package/templates/agent-docs/_manifest.json +2 -2
  84. package/templates/agent-docs/authentication.md +30 -0
  85. package/templates/agent-docs/cli.md +17 -13
  86. package/templates/agent-docs/data.md +44 -1
  87. package/templates/agent-docs/database/migrations.md +2 -2
  88. package/templates/agent-docs/database/seedsdialects.md +2 -2
  89. package/templates/agent-docs/deployment.md +21 -7
  90. package/templates/agent-docs/internationalization.md +10 -3
  91. package/templates/agent-docs/local-first-mobile.md +141 -20
  92. package/templates/agent-docs/plugins/audit.md +4 -0
  93. package/templates/agent-docs/plugins/auth.md +4 -6
  94. package/templates/agent-docs/plugins/mail.md +1 -1
  95. package/templates/agent-docs/plugins/ratelimit.md +1 -1
  96. package/templates/agent-docs/plugins.md +3 -1
  97. package/templates/agent-docs/reference.md +3 -2
  98. package/templates/agent-docs/releases.md +130 -1
  99. package/templates/agent-docs/routing.md +34 -2
  100. package/templates/agent-docs/templates/apibackends.md +1 -1
  101. package/templates/agent-docs/templates/mobile.md +1 -1
  102. package/templates/agent-docs/whats-new.md +178 -44
  103. package/templates/apps/api-ai/package.json +7 -7
  104. package/templates/apps/api-auth/package.json +8 -8
  105. package/templates/apps/api-backend/package.json +7 -7
  106. package/templates/apps/api-backend-deactivation/package.json +7 -7
  107. package/templates/apps/api-backend-mail/package.json +8 -8
  108. package/templates/apps/api-backend-mariadb/package.json +9 -9
  109. package/templates/apps/api-backend-sqlite/package.json +8 -8
  110. package/templates/apps/api-backend-storage/package.json +8 -8
  111. package/templates/apps/api-cms/package.json +10 -10
  112. package/templates/apps/api-cms/tests/content.write.test.ts +16 -2
  113. package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +11 -5
  114. package/templates/apps/api-collab/package.json +8 -8
  115. package/templates/apps/api-collab/tests/documents.setBody.test.ts +8 -2
  116. package/templates/apps/api-data-advanced/package.json +8 -8
  117. package/templates/apps/api-durable/package.json +8 -8
  118. package/templates/apps/api-feature-flags/package.json +9 -9
  119. package/templates/apps/api-governance/package.json +8 -8
  120. package/templates/apps/api-kv/package.json +8 -8
  121. package/templates/apps/api-moderation/package.json +8 -8
  122. package/templates/apps/api-observability/package.json +8 -8
  123. package/templates/apps/api-ratelimit/package.json +8 -8
  124. package/templates/apps/api-rbac/authz.ts +16 -1
  125. package/templates/apps/api-rbac/package.json +8 -8
  126. package/templates/apps/api-rest/package.json +7 -7
  127. package/templates/apps/api-saas/package.json +11 -11
  128. package/templates/apps/api-saas-starter/package.json +10 -10
  129. package/templates/apps/api-search/package.json +8 -8
  130. package/templates/apps/api-status/package.json +8 -8
  131. package/templates/apps/api-versioning/mutations/documents.update.mutation.server.ts +7 -5
  132. package/templates/apps/api-versioning/package.json +8 -8
  133. package/templates/apps/api-webhooks/package.json +9 -9
  134. package/templates/apps/changelog/package.json +6 -6
  135. package/templates/apps/edge-functions/package.json +2 -2
  136. package/templates/apps/frontend-admin/package.json +8 -8
  137. package/templates/apps/frontend-app/package.json +9 -9
  138. package/templates/apps/frontend-auth/package.json +8 -8
  139. package/templates/apps/frontend-blank/package.json +7 -7
  140. package/templates/apps/frontend-cms/package.json +9 -9
  141. package/templates/apps/frontend-collab/package.json +10 -10
  142. package/templates/apps/frontend-contact/package.json +7 -7
  143. package/templates/apps/frontend-dashboard/package.json +7 -7
  144. package/templates/apps/frontend-docs/package.json +7 -7
  145. package/templates/apps/frontend-i18n/package.json +6 -6
  146. package/templates/apps/frontend-landing/package.json +7 -7
  147. package/templates/apps/frontend-portal/package.json +8 -8
  148. package/templates/apps/frontend-saas/package.json +8 -8
  149. package/templates/apps/frontend-spa/package.json +7 -7
  150. package/templates/apps/frontend-ssr/package.json +7 -7
  151. package/templates/apps/frontend-ssr-api/package.json +8 -8
  152. package/templates/apps/frontend-static-blog/package.json +6 -6
  153. package/templates/apps/frontend-status/package.json +8 -8
  154. package/templates/apps/mobile-app/README.md +41 -17
  155. package/templates/apps/mobile-app/app.config.ts +1 -1
  156. package/templates/apps/mobile-app/package.json +15 -11
  157. package/templates/apps/mobile-app/src/app/_layout.tsx +44 -13
  158. package/templates/apps/mobile-app/src/app/index.tsx +1 -1
  159. package/templates/apps/mobile-app/src/app/settings.tsx +4 -1
  160. package/templates/apps/mobile-app/src/client.ts +74 -57
  161. package/templates/apps/mobile-app/src/lib/api.ts +4 -4
  162. package/templates/apps/mobile-app/src/lib/deeplinks.ts +6 -6
  163. package/templates/apps/mobile-app/src/persistence.ts +24 -3
  164. package/templates/apps/mobile-app/tsconfig.json +17 -3
  165. package/templates/patches/@effect__cluster@0.60.0.patch +51 -3
  166. package/dist/addCommand-aXSQveak.js +0 -2
  167. package/dist/agentsMd-BTchIZku.js +0 -2
  168. package/dist/apiBuild-D0f6hYbH.js +0 -2
  169. package/dist/baselineCommand-Cfw2Afwm.js +0 -2
  170. package/dist/codegen-uYrrDzQv.js +0 -2
  171. package/dist/codegenCommand-BxS3sCeJ.js +0 -30
  172. package/dist/dbCommand-CC-qNzpz.js +0 -2
  173. package/dist/dev-BV6XNYKf.js +0 -3
  174. package/dist/doctorCommand-C4L61G_L.js +0 -2
  175. package/dist/inspect-CjTYzAs_.js +0 -1190
  176. package/dist/inspect-P4pxoMaV.js +0 -2
  177. package/dist/inspectGateHint-BF6608UT.js +0 -4
  178. package/dist/manifestBuild-COkJoyAr.js +0 -2
  179. package/dist/serveCommand-CyZsXL1l.js +0 -1766
  180. package/dist/serveCommand-qumm7jdF.js +0 -2
  181. package/dist/startupRunner-DPGFchOa.js +0 -71
  182. package/dist/updateCommand-BN0tkl9-.js +0 -2
  183. package/dist/webDev-BcykISYQ2.js +0 -2
@@ -0,0 +1,2 @@
1
+ import { _ as e } from "./webDev-CTpSY-e_.js";
2
+ export { e as tryRunWebServe };
@@ -23,12 +23,10 @@ var r = /\.webhook\.tsx?$/, i = async (t, r) => {
23
23
  descriptor: s
24
24
  });
25
25
  break;
26
- case "webhookProvider":
27
- o.push({
28
- file: i,
29
- descriptor: s
30
- });
31
- break;
26
+ case "webhookProvider": o.push({
27
+ file: i,
28
+ descriptor: s
29
+ });
32
30
  }
33
31
  }
34
32
  return {
@@ -1,2 +1,2 @@
1
- import { i as e, r as t, t as n } from "./webhookDiscovery-D7VaeMlz.js";
1
+ import { i as e, r as t, t as n } from "./webhookDiscovery-CphsQe59.js";
2
2
  export { n as WEBHOOK_PATTERN, t as loadWebhookFiles, e as outgoingFromEvents };
@@ -222,7 +222,7 @@ createVerifier({ secret: [process.env.WEBHOOK_SECRET, process.env.WEBHOOK_SECRET
222
222
  ...t === void 0 ? {} : { payload: t }
223
223
  };
224
224
  }, S = u({ scope: "voltro:webhooks" }), C = ["--out", "--name"], w = async (e) => {
225
- let { walk: t, loadDiscovered: n } = await import("./dev-BV6XNYKf.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-CrGAfhIG.js");
225
+ let { walk: t, loadDiscovered: n } = await import("./dev-alhkKoEX.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-il9ti-HE.js");
226
226
  return r((await n(await t(e))).events.map((e) => ({
227
227
  file: e.file,
228
228
  descriptor: e.descriptor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/cli",
3
- "version": "0.37.0",
3
+ "version": "0.39.0",
4
4
  "description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -580,6 +580,24 @@
580
580
  "title": "a procedure input rejects a field it does not declare (it used to drop it)",
581
581
  "kind": "manual"
582
582
  },
583
+ {
584
+ "version": "0.38.0",
585
+ "id": "0.38.0/01_impersonation-key-in-protocol",
586
+ "title": "`IMPERSONATION_METADATA_KEY` is imported from @voltro/protocol",
587
+ "kind": "transform"
588
+ },
589
+ {
590
+ "version": "0.38.0",
591
+ "id": "0.38.0/02_startup-failure-refuses-boot",
592
+ "title": "a `*.startup.ts` that fails refuses the boot (it used to warn and continue)",
593
+ "kind": "manual"
594
+ },
595
+ {
596
+ "version": "0.39.0",
597
+ "id": "0.39.0/01_empty-input-schema-rejects-every-field",
598
+ "title": "a procedure declaring `Schema.Struct({})` used to accept every field and now accepts none",
599
+ "kind": "manual"
600
+ },
583
601
  {
584
602
  "version": "0.4.0",
585
603
  "id": "0.4.0/01_rbac-forbidden-to-scopeerror",
@@ -679,22 +697,22 @@
679
697
  "@effect/platform-node": "^0.108.0",
680
698
  "@effect/sql": "^0.52.0",
681
699
  "@effect/workflow": "^0.19.0",
682
- "@voltro/ai": "0.37.0",
683
- "@voltro/cache": "0.37.0",
684
- "@voltro/data-transfer": "0.37.0",
685
- "@voltro/database": "0.37.0",
686
- "@voltro/env": "0.37.0",
687
- "@voltro/kv": "0.37.0",
688
- "@voltro/logger": "0.37.0",
689
- "@voltro/plugin-auth": "0.37.0",
690
- "@voltro/plugin-broadcast": "0.37.0",
691
- "@voltro/plugin-mail": "0.37.0",
692
- "@voltro/plugin-storage": "0.37.0",
693
- "@voltro/plugin-webhooks": "0.37.0",
694
- "@voltro/protocol": "0.37.0",
695
- "@voltro/runtime": "0.37.0",
696
- "@voltro/serverless": "0.37.0",
697
- "@voltro/workflow": "0.37.0",
700
+ "@voltro/ai": "0.39.0",
701
+ "@voltro/cache": "0.39.0",
702
+ "@voltro/data-transfer": "0.39.0",
703
+ "@voltro/database": "0.39.0",
704
+ "@voltro/env": "0.39.0",
705
+ "@voltro/kv": "0.39.0",
706
+ "@voltro/logger": "0.39.0",
707
+ "@voltro/plugin-auth": "0.39.0",
708
+ "@voltro/plugin-broadcast": "0.39.0",
709
+ "@voltro/plugin-mail": "0.39.0",
710
+ "@voltro/plugin-storage": "0.39.0",
711
+ "@voltro/plugin-webhooks": "0.39.0",
712
+ "@voltro/protocol": "0.39.0",
713
+ "@voltro/runtime": "0.39.0",
714
+ "@voltro/serverless": "0.39.0",
715
+ "@voltro/workflow": "0.39.0",
698
716
  "chokidar": "^5.0.0",
699
717
  "ioredis": "^5.11.1",
700
718
  "tinyglobby": "^0.2.17",
@@ -703,10 +721,10 @@
703
721
  "optionalDependencies": {
704
722
  "@tailwindcss/vite": "^4.3.3",
705
723
  "@vercel/nft": "^1.10.2",
706
- "@vitejs/plugin-react": "^6.0.4",
707
- "esbuild": "^0.28.0",
708
- "tsx": "^4.23.1",
709
- "vite": "^8.1.5"
724
+ "@vitejs/plugin-react": "^6.0.5",
725
+ "esbuild": "^0.28.2",
726
+ "tsx": "^4.23.12",
727
+ "vite": "^8.2.1"
710
728
  },
711
729
  "peerDependencies": {
712
730
  "@effect/platform": "^0.97.0",
@@ -707,7 +707,7 @@ each plugin's own README.
707
707
 
708
708
  | Topic | Open | Summary |
709
709
  |---|---|---|
710
- | **What's new in 0.37.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
710
+ | **What's new in 0.39.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
711
711
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
712
712
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
713
713
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -733,7 +733,7 @@ each plugin's own README.
733
733
  | Observability | `node_modules/@voltro/cli/templates/agent-docs/observability.md` | OpenTelemetry tracing in Voltro — the auto-emitted spans for every primitive, span attributes and nesting, the three enabling modes (console / OTLP / buffer), and adding your own spans with Effect.withSpan. |
734
734
  | Plugins | `node_modules/@voltro/cli/templates/agent-docs/plugins.md` | How Voltro plugins compose into the runtime, what they can intercept, the catalogue, and writing your own. |
735
735
  | Reference | `node_modules/@voltro/cli/templates/agent-docs/reference.md` | The client-side hook surface, grouped by purpose. |
736
- | Releases | `node_modules/@voltro/cli/templates/agent-docs/releases.md` | The largest release in the framework's history what breaks your boot, what changes at runtime, and why none of it is rewritten for you. |
736
+ | Releases | `node_modules/@voltro/cli/templates/agent-docs/releases.md` | 0.35 through 0.38 in one passthe boot-breaking access declarations, the stricter input handling, and the runtime behaviour that moved underneath you. |
737
737
  | Routing | `node_modules/@voltro/cli/templates/agent-docs/routing.md` | Voltro's file-based router — pages, layouts, render modes, loaders, navigation, islands. The web side of the framework. |
738
738
  | Scheduling | `node_modules/@voltro/cli/templates/agent-docs/scheduling.md` | Deployment-agnostic scheduled jobs in Voltro — one *.cron.tsx definition that runs unchanged on a single box, a multi-instance fleet, or an external scheduler. |
739
739
  | Schema-driven UI | `node_modules/@voltro/cli/templates/agent-docs/schema-driven-ui.md` | Project the typed descriptor graph into UI — forms, tables, pickers, and reactive components, all bound to a descriptor with near-zero glue. |
@@ -9,7 +9,7 @@ each plugin's own README.
9
9
 
10
10
  | Topic | Open | Summary |
11
11
  |---|---|---|
12
- | **What's new in 0.37.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
12
+ | **What's new in 0.39.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
13
13
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
14
14
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
15
15
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -35,7 +35,7 @@ each plugin's own README.
35
35
  | Observability | `node_modules/@voltro/cli/templates/agent-docs/observability.md` | OpenTelemetry tracing in Voltro — the auto-emitted spans for every primitive, span attributes and nesting, the three enabling modes (console / OTLP / buffer), and adding your own spans with Effect.withSpan. |
36
36
  | Plugins | `node_modules/@voltro/cli/templates/agent-docs/plugins.md` | How Voltro plugins compose into the runtime, what they can intercept, the catalogue, and writing your own. |
37
37
  | Reference | `node_modules/@voltro/cli/templates/agent-docs/reference.md` | The client-side hook surface, grouped by purpose. |
38
- | Releases | `node_modules/@voltro/cli/templates/agent-docs/releases.md` | The largest release in the framework's history what breaks your boot, what changes at runtime, and why none of it is rewritten for you. |
38
+ | Releases | `node_modules/@voltro/cli/templates/agent-docs/releases.md` | 0.35 through 0.38 in one passthe boot-breaking access declarations, the stricter input handling, and the runtime behaviour that moved underneath you. |
39
39
  | Routing | `node_modules/@voltro/cli/templates/agent-docs/routing.md` | Voltro's file-based router — pages, layouts, render modes, loaders, navigation, islands. The web side of the framework. |
40
40
  | Scheduling | `node_modules/@voltro/cli/templates/agent-docs/scheduling.md` | Deployment-agnostic scheduled jobs in Voltro — one *.cron.tsx definition that runs unchanged on a single box, a multi-instance fleet, or an external scheduler. |
41
41
  | Schema-driven UI | `node_modules/@voltro/cli/templates/agent-docs/schema-driven-ui.md` | Project the typed descriptor graph into UI — forms, tables, pickers, and reactive components, all bound to a descriptor with near-zero glue. |
@@ -231,9 +231,9 @@
231
231
  "title": "Releases",
232
232
  "section": "Releases",
233
233
  "group": null,
234
- "description": "The largest release in the framework's history what breaks your boot, what changes at runtime, and why none of it is rewritten for you.",
234
+ "description": "0.35 through 0.38 in one passthe boot-breaking access declarations, the stricter input handling, and the runtime behaviour that moved underneath you.",
235
235
  "path": "agent-docs/releases.md",
236
- "files": 1
236
+ "files": 2
237
237
  },
238
238
  {
239
239
  "id": "routing",
@@ -2154,6 +2154,36 @@ that throws — each is a denial, not a pass. An authorization question nobody c
2154
2154
  answer is a refusal; treating it as a pass is how a policy layer ends up
2155
2155
  enforcing nothing while looking like it does.
2156
2156
 
2157
+ **A guard naming an unregistered `resourceType` refuses the BOOT.** Denying is
2158
+ correct per call and useless as a deployment outcome: the app comes up green and
2159
+ every guarded procedure is down, with a log line per refused call as the only
2160
+ sign. A consumer put a number on it — 39 procedures, team settings through role
2161
+ administration. So the two facts are compared once, after the startups have run,
2162
+ and a missing registration names the type and the procedures that demanded it.
2163
+ The commonest cause is a typo: the `resourceType` in a guard and the one in
2164
+ `defineResourcePolicy` are two strings, and nothing but that check compares them.
2165
+
2166
+ ### The source sees the whole subject
2167
+
2168
+ `req.subject` is the caller, not just `req.subjectId`. That matters whenever a
2169
+ CREDENTIAL is narrower than the person holding it — an API key above all:
2170
+
2171
+ ```ts
2172
+ setTupleSource(async (req) => {
2173
+ // A key minted for one team must not act on another. `req.subjectId` is the
2174
+ // OWNING USER, so a source reading only memberships passes an owner who
2175
+ // belongs to both teams.
2176
+ const boundTeam = req.subject.metadata?.teamId
2177
+ if (boundTeam !== undefined && boundTeam !== req.resourceId) return []
2178
+ return loadResourceTuples(store, req.subjectId, req.resourceType, req.resourceId)
2179
+ })
2180
+ ```
2181
+
2182
+ Narrow it yourself: `Subject` is a union whose system and anonymous members carry
2183
+ no `metadata`. Without this a declared guard could not express the binding, so an
2184
+ app had to keep a hand-written check in the executor beside it — and a guard that
2185
+ must always run paired with a hand-written check is not a declaration.
2186
+
2157
2187
  ### Guards are re-checked on every subscription delivery
2158
2188
 
2159
2189
  A subscription is a long-lived grant. Its guards — scope and relationship alike
@@ -33,7 +33,7 @@ The dispatcher routes `voltro <command> [args]` to the matching subcommand and p
33
33
  | [Inspect & debug](/docs/cli/inspect) | `inspect`, `logs`, `traces`, `workflows`, `cluster`, `check` |
34
34
  | [Health & surface](/docs/cli/build-and-start) | [`doctor`](/docs/cli/build-and-start) — serve preflight + the hand-roll detector (names the shipped primitive at the spot you're rebuilding it); [`capabilities`](/docs/cli/build-and-start) (`--json`) — the export surface read from your installed `@voltro/*`, so it can be verified instead of recalled; `info` (`--json`) — CLI / node / package-manager / dialect + every installed `@voltro/*` version, flagging lockstep skew (exits 1 on skew) |
35
35
  | Harness | `test`, `e2e` |
36
- | Cloud | `cloud` (`login` / `whoami` / `projects` / `env` / `import`) |
36
+ | Cloud | `cloud` (`login` / `whoami` / `projects` / `env` / `import`); `login` is a top-level alias of `cloud login` |
37
37
  | Secrets | `secret` (`generate [purpose]` — the right var+format per secret; `generate` alone → a generic secret; `list`) |
38
38
  | Meta | `agents-md`, `telemetry` (reports that Voltro collects none — no phone-home, nothing to opt out of), `version`, `help` |
39
39
 
@@ -44,7 +44,7 @@ to the reachable runtime set — the standalone Dockerfiles run it for you) and
44
44
  version's codemods). Both are dispatchable; neither is something you invoke
45
45
  directly.
46
46
 
47
- Each command takes an optional path argument (the app directory) — defaults to `.` when run from inside an app. `voltro init` initialises the current directory as a workspace root — `pnpm-workspace.yaml`, a root `package.json` with `dev`/`build`/`test`/`typecheck`, a `.gitignore` and `git init`, idempotently and without scaffolding any apps (`voltro create-project <name>` does that, and bootstraps the same root when there isn't one). `voltro secret generate [purpose]` prints a cryptographically strong secret — with a purpose (`data-transfer`, `session`, `bundle-key`, `field-encryption`, `storage`, `inspect`) it emits the correct env var + length/format as a paste-ready `NAME=value` (`voltro secret list` shows them all); with no purpose, a generic base64url secret. `voltro telemetry` reports that Voltro collects none. `voltro deploy` shows the deploy paths — self-host via a [baseline](/docs/deployment/baselines) + CI, managed via the control-plane client `voltro cloud` (managed cloud deploy is coming soon — see [Voltro Cloud](/docs/deployment/voltro-cloud)), individual [serverless functions](/docs/deployment/serverless-functions) (`voltro serverless` → self-hosted Node, or Cloudflare / Scaleway), or a [static site](/docs/deployment/static-sites) (`voltro static` → Cloudflare Pages / S3 / Netlify).
47
+ Each command takes an optional path argument (the app directory) — defaults to `.` when run from inside an app. `voltro init` initialises the current directory as a workspace root — `pnpm-workspace.yaml`, a root `package.json` with `dev`/`build`/`test`/`typecheck`, a `.gitignore` and `git init`, idempotently and without scaffolding any apps (`voltro create-project <name>` does that, and bootstraps the same root when there isn't one). `voltro secret generate [purpose]` prints a cryptographically strong secret — with a purpose (`data-transfer`, `session`, `bundle-key`, `field-encryption`, `storage`, `inspect`) it emits the correct env var + length/format as a paste-ready `NAME=value` (`voltro secret list` shows them all); with no purpose, a generic base64url secret. `voltro telemetry` reports that Voltro collects none. `voltro deploy` shows the deploy paths — self-host via a [baseline](/docs/deployment/self-hosting) + CI, managed via the control-plane client `voltro cloud` (managed cloud deploy is coming soon — see [Voltro Cloud](/docs/deployment/voltro-cloud)), individual [serverless functions](/docs/deployment/serverless-functions) (`voltro serverless` → self-hosted Node, or Cloudflare / Scaleway), or a [static site](/docs/deployment/static-sites) (`voltro static` → Cloudflare Pages / S3 / Netlify).
48
48
 
49
49
  ## Help + version
50
50
 
@@ -620,7 +620,7 @@ is added with `voltro add-app`. Now `voltro list-templates` shows it; `voltro
620
620
  create-project --web my-template` (or `--api`) uses it, and any kind is added
621
621
  with `voltro add-app <name> --template my-template`.
622
622
 
623
- For private templates (in your own repo), set `VOLTRO_TEMPLATES_PATH=/path/to/my/templates` and the CLI walks there instead of the default location.
623
+ For private templates (in your own repo), set `VOLTRO_TEMPLATES_DIR=/path/to/my/templates` and the CLI resolves templates from there instead of the default location. Point it at the templates **root** — the CLI appends `apps/`, so your templates live at `/path/to/my/templates/apps/<template-id>/`. When the variable is set it is authoritative: the CLI does not fall back to the bundled templates.
624
624
 
625
625
  ## Idempotency
626
626
 
@@ -1851,17 +1851,21 @@ voltro capabilities --json # the full machine-readable surface
1851
1851
  ```
1852
1852
 
1853
1853
  ```text
1854
- voltro capabilities — 2517 exported symbols across 36 packages
1855
- @voltro/runtime@0.4.0 — 9 primitives, 41 values, 118 types
1856
- defineAggregate, defineReaction, defineSubscriber, defineResourcePolicy, …
1857
- @voltro/client@0.4.0 — 47 hooks, 12 components, 60 types
1858
- useSubscription, useMutation, useFormBinding, useDataTable, useUpload, …
1859
-
1860
- * 8 primitive(s)/hook(s) appear nowhere in this project's agent guide:
1861
- @voltro/plugin-mail: defineEmail
1854
+ voltro capabilities — 3856 exported symbols across 36 packages
1855
+ @voltro/runtime@0.38.0 — 12 primitives, 5 hooks, 100 components, 360 values, 388 types
1856
+ defineAggregate, defineConnection, defineCostBudget, defineEventTrigger, defineExecutor,
1857
+ @voltro/database@0.38.0 — 3 primitives, 33 components, 333 values, 223 types
1858
+ defineMigration *, defineMixin, defineSeed *
1859
+
1860
+ * 9 primitive(s)/hook(s) appear nowhere in this project's agent guide:
1861
+ @voltro/database: defineMigration
1862
+ @voltro/plugin-flags: defineFlag
1862
1863
 
1863
1864
  ```
1864
1865
 
1866
+ The count is the packages **installed in that project**, not everything the
1867
+ framework publishes — a leaner app reports fewer.
1868
+
1865
1869
  Every symbol reported was read out of an installed package a moment ago, so an
1866
1870
  agent can **verify** the surface instead of recalling it. The `--json` form is
1867
1871
  stable and locale-independent — the same tree produces byte-identical output on
@@ -2195,7 +2199,7 @@ gives the split no meaning.
2195
2199
 
2196
2200
  A plugin mounting a non-GET inspect endpoint must also declare the
2197
2201
  `inspect:write` permission, and the boot audit refuses it otherwise. That governs
2198
- what a PLUGIN may mount; the write credential governs who may call it. The [Voltro Dashboard](/docs/observability/dashboard) consumes it to render the route sitemap, RPC list, subscription panel, workflow runs, and metrics. You can also hit the endpoints directly with `curl` (the `voltro inspect` subcommands above are the thin wrapper over exactly these).
2202
+ what a PLUGIN may mount; the write credential governs who may call it. The [Voltro Dashboard](/docs/observability/overview) consumes it to render the route sitemap, RPC list, subscription panel, workflow runs, and metrics. You can also hit the endpoints directly with `curl` (the `voltro inspect` subcommands above are the thin wrapper over exactly these).
2199
2203
 
2200
2204
  ```bash
2201
2205
  PORT=4000 # from the app's app.config.ts
@@ -3785,4 +3789,4 @@ Emits the `globalEnv` / `globalPassThroughEnv` entries for `turbo.json`, so Turb
3785
3789
  ## Related
3786
3790
 
3787
3791
  - [Secrets](/docs/cli/overview#common-env-vars) — generating secret values with `voltro secret`.
3788
- - [Configuration](/docs/configuration) — declaring env vars with `envVar(...)`.
3792
+ - [Configuration](/docs/configuration/environment) — declaring env vars with `envVar(...)`.
@@ -970,7 +970,24 @@ const create = useMutation('app', 'notes.create')
970
970
  await create.mutate({ title, body })
971
971
  ```
972
972
 
973
- The client stages an optimistic patch, calls the server, then removes the patch when the server result or failure arrives. Server-pushed deltas are still the source of truth.
973
+ The client stages an optimistic patch, calls the server, then retires the patch. Server-pushed deltas are still the source of truth.
974
+
975
+ **Retiring the patch has exactly two shapes, and they are not interchangeable:**
976
+
977
+ - **Rollback — only when the write FAILED.** The server did not write, so the
978
+ preview must go, immediately. Nothing else ever rolls a patch back: not a
979
+ timer, not an elapsed window, not a heuristic.
980
+ - **Hand-off — when the server's own state arrives and reflects the write.** A
981
+ delta always supersedes (it *is* the echo of committed writes); a fresh
982
+ snapshot supersedes only when it actually moved the base. Base gains the real
983
+ row and the placeholder goes in the same update, so there is no flash and no
984
+ optimistic+real duplicate.
985
+
986
+ If a confirmed write never gets its echo — reactivity is broken for that query's
987
+ source table — the client **re-issues the subscription and keeps the preview**,
988
+ and reports it on the error bus (visible in `voltro logs`). It does not fall back
989
+ to a base it knows does not reflect the write: a user watching their saved change
990
+ disappear will either redo it or plan on a state they believe was not stored.
974
991
 
975
992
  For special shapes, override the patch:
976
993
 
@@ -1809,6 +1826,32 @@ first-paint optimization, never a correctness dependency. A failed preload is
1809
1826
  likewise non-fatal — the live subscription still delivers the value on the
1810
1827
  client, the only loss is the first-paint seed.
1811
1828
 
1829
+ ### `preloadFailed` — "no data" vs "could not get data"
1830
+
1831
+ A preload that FAILED server-side and one that was never declared arrive the same
1832
+ way: as the absence of a seed. That makes an empty first paint ambiguous, and the
1833
+ ambiguity is not academic — a session cookie that has outlived the IdP's token
1834
+ lifetime makes EVERY preload on the page fail at once, so the page renders its
1835
+ empty state while the server log holds the only explanation.
1836
+
1837
+ `preloadFailed` separates the two:
1838
+
1839
+ ```tsx
1840
+ const projects = usePreloadedSubscription<Project[]>('app', 'projects.list')
1841
+
1842
+ if (projects.loading) return <Skeleton/>
1843
+ if (projects.preloadFailed) return <Spinner label="Loading…"/> // not empty — unasked
1844
+ return <ProjectTable rows={projects.data}/>
1845
+ ```
1846
+
1847
+ It is a boolean and says nothing about WHY. The server's failure text is a
1848
+ refused call's error message; it belongs in the server log, which is the one
1849
+ place a browser cannot read. It also says nothing about the LIVE subscription,
1850
+ which usually recovers on its own — the browser reconnects with a credential the
1851
+ SSR request did not have. Read it as "the first paint has no server data, and
1852
+ that was not for lack of asking", which is exactly enough to pick a spinner over
1853
+ an empty state.
1854
+
1812
1855
  ### Seeding by hand
1813
1856
 
1814
1857
  `export const preload` is sugar over an explicit seed. When a loader ALREADY has
@@ -1835,7 +1835,7 @@ The token is the same one the remote app boots with:
1835
1835
  VOLTRO_INSPECT_TOKEN=<secret> voltro start
1836
1836
  ```
1837
1837
 
1838
- See [Introspection](/docs/observability/inspect) for the full auth
1838
+ See [Introspection](/docs/cli/inspect) for the full auth
1839
1839
  configuration.
1840
1840
 
1841
1841
  ## URL shape
@@ -1861,7 +1861,7 @@ appends it. The trailing-slash variant works too.
1861
1861
 
1862
1862
  - [Migration overview](/docs/database/migrations) — the
1863
1863
  plan/apply lifecycle this slots into
1864
- - [Introspection](/docs/observability/inspect) — the
1864
+ - [Introspection](/docs/cli/inspect) — the
1865
1865
  `/_voltro/inspect/*` surface this command consumes
1866
1866
  - [Drift detection](/docs/database/migrations/drift) — fingerprint-based
1867
1867
  drift surfacing the same inspect endpoint emits
@@ -1043,7 +1043,7 @@ MariaDB's GTID format differs from MySQL's: `0-1-100` (domain-server-sequence) v
1043
1043
  - **`mariadb` schema package is wire-compatible with `mysql`**. If you migrate from MySQL → MariaDB, the framework re-emits DDL cleanly via `applySchema(..., 'mariadb')`. Production data round-trips through `mysqldump` without translation.
1044
1044
  - **`sql_mode=NO_BACKSLASH_ESCAPES`** is sometimes set on MariaDB deploys. The framework's identifier escaping handles it, but user-written `unsafe()` strings that hand-escape backslashes may produce wrong output. Leave that mode off if you can.
1045
1045
  - **Sequence-based ID columns**. MariaDB has true CREATE SEQUENCE; the framework doesn't use it (TypeID / ULID / Snowflake are client-side). If you reach for sequences for legacy reasons, they're outside the framework's auto-injection path.
1046
- - **Hand-rolled `AUTO_INCREMENT` primary keys** work the same as on mysql: an `insert` / `insertMany` with no client-side `id` recovers the DB-generated id via `LAST_INSERT_ID()` (connection-pinned; `insertMany` recovers the whole consecutive range). See the [mysql page](/docs/en/database/dialects/mysql#auto_increment-ids--last_insert_id-recovery) for the worked example — the recovery path is identical on both engines.
1046
+ - **Hand-rolled `AUTO_INCREMENT` primary keys** work the same as on mysql: an `insert` / `insertMany` with no client-side `id` recovers the DB-generated id via `LAST_INSERT_ID()` (connection-pinned; `insertMany` recovers the whole consecutive range). See the [mysql page](/docs/database/dialects/mysql#auto_increment-ids--last_insert_id-recovery) for the worked example — the recovery path is identical on both engines.
1047
1047
 
1048
1048
  ## Where it lives
1049
1049
 
@@ -1080,7 +1080,7 @@ SQL Server 2016 / 2017 work for the basic workflow path; some performance charac
1080
1080
  ```sh
1081
1081
  DB_DIALECT=mssql
1082
1082
  DB_URL=mssql://sa:<password>@localhost:11433/voltro_test
1083
- # or discrete fields: DB_DIALECT + DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_NAME
1083
+ # or discrete fields: DB_DIALECT + DB_HOST / DB_PORT / DB_USER / DB_PASSWORD / DB_DATABASE
1084
1084
 
1085
1085
  # Local Docker dev fixture defaults:
1086
1086
  # container: mcr.microsoft.com/mssql/server:2022-latest
@@ -1248,21 +1248,35 @@ those do not come out of the pool budget, they come out of the DATABASE's.
1248
1248
 
1249
1249
  Set `REPLICA_COUNT` from your deployment (Helm: `{{ .Values.replicaCount }}`) and
1250
1250
  the line does the multiplication for you; without it the line still names the
1251
- formula. `voltro dev` deliberately does not print it — one process, no replicas.
1251
+ formula.
1252
+
1253
+ **`voltro dev` prints it too, when the environment says it is not a laptop.**
1254
+ A bare `voltro dev` stays silent — one process, no replicas, nothing to
1255
+ multiply. But `voltro dev` is a supported way to RUN an app, and a deployment
1256
+ that uses it needs this line as much as any other. So it prints whenever
1257
+ `REPLICA_COUNT`, `DB_MAX_CONNECTIONS` / `PG_MAX_CONNECTIONS`, or
1258
+ `DB_REPLICA_URLS` is set — each of which means somebody has already decided
1259
+ something about the number.
1252
1260
 
1253
1261
  **Some connections are not in the pool, and the count is per process.** A
1254
- connection running `LISTEN` cannot be returned to a pool, so the driver opens a
1255
- standalone one. There are three such places and a full deployment can hold all
1256
- three:
1262
+ connection that speaks a long-lived protocol cannot be returned to a pool, so
1263
+ the driver opens a standalone one. There are four such places and a full
1264
+ deployment can hold several at once:
1257
1265
 
1258
1266
  | Process | Connection | When |
1259
1267
  |---|---|---|
1260
- | api `voltro serve` | CDC `LISTEN` consumer | `changeStrategy: 'cdc'` (the default on postgres) |
1268
+ | api `voltro serve` / `dev` | CDC `LISTEN` consumer | postgres, `changeStrategy: 'cdc'` (the default) |
1269
+ | api `voltro serve` / `dev` | binlog CDC reader | mysql / mariadb, `changeStrategy: 'cdc'` |
1261
1270
  | web `voltro start` | ISR invalidator `LISTEN` | any page declares `cacheInvalidatesOn` |
1262
1271
  | web `voltro start` | postgres ISR cache client | `SSR_CACHE=postgres` |
1263
1272
 
1264
- The third is not a `LISTEN` at all, which is why counting `LISTEN` rows in
1265
- `pg_stat_activity` undercounts. Each process prints its own number in the boot
1273
+ SQL Server is the one CDC dialect that costs nothing here: Change Tracking is
1274
+ read with ordinary queries through the pool, so its out-of-pool count is a
1275
+ verified zero rather than an omission.
1276
+
1277
+ Two of the four are not a `LISTEN` at all — the binlog reader speaks the
1278
+ replication protocol, and the ISR cache client is an ordinary client — which is
1279
+ why counting `LISTEN` rows in `pg_stat_activity` undercounts. Each process prints its own number in the boot
1266
1280
  line above — including `No connections outside the pool in this process` when
1267
1281
  there are none, so "counted, zero" is distinguishable from "not counted".
1268
1282
 
@@ -838,9 +838,16 @@ program.pipe(withTimezone('Europe/Berlin'))
838
838
  ```
839
839
 
840
840
  `currentTimezone` never fails — an absent context is the documented `UTC`
841
- default, so call sites don't handle a missing-service error. The runtime provides
842
- the tag per request (via `resolvedTimezoneLayer`); the web layer's `useTimezone()`
843
- hook is a thin projection over it.
841
+ default, so call sites don't handle a missing-service error.
842
+
843
+ > **What ships today is the SEAM, not the wiring.** `@voltro/datetime/context`
844
+ > exports `currentTimezone`, `withTimezone` and `resolvedTimezoneLayer`, and you
845
+ > provide the layer yourself. The framework does **not** yet install it per
846
+ > request, and there is **no `useTimezone()` hook** in the web layer — both are
847
+ > planned to land in `@voltro/runtime` + `@voltro/web`. Until then: resolve the
848
+ > zone yourself (`resolveTimezone`) and provide `resolvedTimezoneLayer` around
849
+ > the work that needs it, or pass an explicit `timeZone` argument. Without a
850
+ > provided layer every call reads the `UTC` default.
844
851
 
845
852
  ## Arithmetic — the DST split is in the names
846
853