@voltro/cli 0.50.1 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +231 -0
- package/dist/{agentsMd-D6yD7IQv.js → agentsMd-0l980yhL.js} +4 -1
- package/dist/agentsMd-SDDSkyl4.js +2 -0
- package/dist/apiBuild-BYBpL7Pz.js +2 -0
- package/dist/{apiBuild-D3s7lx57.js → apiBuild-CSFI8QGq.js} +3 -3
- package/dist/appGraph-CFmFKHb2.js +2 -0
- package/dist/appGraph-yRuKjOba.js +111 -0
- package/dist/bin.js +1 -1
- package/dist/build-CPgcMQug.js +793 -0
- package/dist/checkCommand-2SbqzukH.js +2 -0
- package/dist/{checkCommand-DsKIjL6k.js → checkCommand-COmqc2cB.js} +107 -60
- package/dist/{cloudCmd-F4YJeqM3.js → cloudCmd-QUXh-b5w.js} +1 -1
- package/dist/codegen-CctkDO-1.js +2 -0
- package/dist/{codegen-CrMXs4hb.js → codegen-VF479Cnb.js} +1 -1
- package/dist/{codegenCommand-C0S27nbB.js → codegenCommand-DCdG2JN-.js} +12 -12
- package/dist/{codemodRunner-DwY8Rjbl.js → codemodRunner-r7J9lIa7.js} +771 -636
- package/dist/{commands-Brp4DI8u.js → commands-Cc_nV8WI.js} +37 -36
- package/dist/{dashboardCommand-bbLfc9FW.js → dashboardCommand-C-gKvwqh.js} +3 -3
- package/dist/{dataCommand-k9mXTXin.js → dataCommand-BgpBHnlB.js} +299 -253
- package/dist/dbCommand-DNb6yeOG.js +2 -0
- package/dist/{dbCommand-Bf7Cy0mn.js → dbCommand-sHedr-NJ.js} +3 -3
- package/dist/dev--A3nsxA3.js +3 -0
- package/dist/{dev-C0cbqysu.js → dev-CRHoCEiy.js} +2258 -2219
- package/dist/doctorCommand-CqoWA2p5.js +2 -0
- package/dist/{doctorCommand-DyViaaCP.js → doctorCommand-DtfJ3FA6.js} +314 -234
- package/dist/{dormancyCommand-DMZPA_HW.js → dormancyCommand-Drn7o0No.js} +1 -1
- package/dist/{embeddingsCommand-CMxRPkg6.js → embeddingsCommand-Z-jO1fWN.js} +1 -1
- package/dist/{envCommand-CDLr-yD4.js → envCommand-D4gCrrTZ.js} +15 -15
- package/dist/{evolveCommand-Ct4NesL3.js → evolveCommand-CMROeKeA.js} +2 -2
- package/dist/fileConventions-DOqD3lPS.js +34 -0
- package/dist/{fileTaxonomy-CJfgOllU.js → fileTaxonomy-DvDUV9wq.js} +1 -1
- package/dist/frameworkTableAssembly-C6ETawPR.js +2 -0
- package/dist/{frameworkTableAssembly-BwJVEKLr.js → frameworkTableAssembly-w-XnLa3q.js} +1 -1
- package/dist/{generateCommand-BDdOxhnv.js → generateCommand-DTNPGjxo.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-orsKJKUy.js → infoCommand-DXM868o_.js} +1 -1
- package/dist/inspectMetrics-CGF94puw.js +143 -0
- package/dist/{metaCommands-CfRLra0s.js → metaCommands-C6RFmF1r.js} +2 -2
- package/dist/{migrate-D3MK9BpK.js → migrate-D0F-eTlK.js} +2 -2
- package/dist/{pageConvention-cEiRxdab.js → pageConvention-CzUiSbtU.js} +1 -1
- package/dist/{privacyCommand-XejDMvmu.js → privacyCommand-DGdopOI6.js} +1 -1
- package/dist/{projectScaffold-mIX_DpSe.js → projectScaffold-B4dmTlwT.js} +1 -1
- package/dist/{projectScaffold-BIl97_E6.js → projectScaffold-EzlErR4E.js} +1 -1
- package/dist/{renderModeScan-D7J1B7Kw.js → renderModeScan-CcH2X1_D.js} +25 -10
- package/dist/{renderProfile-1OWWAAtx.js → renderProfile-Ck32Fzxr.js} +2 -2
- package/dist/{runtimeTrace-Bo8jWsNe.js → runtimeTrace-BPQyCmC5.js} +1 -1
- package/dist/{sdkgen-OZhiT2vz.js → sdkgen-Se88ifTd.js} +1 -1
- package/dist/serveCommand-DLc-BznW.js +2 -0
- package/dist/{serveCommand-YbD5VgfV.js → serveCommand-DkP3OT0W.js} +885 -868
- package/dist/serveEntry.js +1 -1
- package/dist/start-DfL3fOiN.js +3 -0
- package/dist/start-jw89Xbqy.js +1339 -0
- package/dist/startEntry.js +1 -1
- package/dist/{staticCommand-Dr2M6tpU.js → staticCommand-BwNEDlSU.js} +1 -1
- package/dist/{test-rXFq4S76.js → test-f3amja6a.js} +1 -1
- package/dist/updateCommand-5gFVfK5q.js +2 -0
- package/dist/{updateCommand-BgH6vTtq.js → updateCommand-BMk2e4ky.js} +1 -1
- package/dist/{webDev-BbdkzMbd.js → webDev-BgWL9gKV.js} +1233 -853
- package/dist/webDev-CZbTsDcH.js +2 -0
- package/dist/{webhooksCommand-DjE6u0r4.js → webhooksCommand-CoIO3jbj.js} +1 -1
- package/package.json +48 -17
- package/templates/AGENTS.core.md +26 -5
- package/templates/AGENTS.md +30 -9
- package/templates/agent-docs/_index.md +4 -4
- package/templates/agent-docs/_manifest.json +11 -11
- package/templates/agent-docs/cli.md +125 -14
- package/templates/agent-docs/data.md +210 -7
- package/templates/agent-docs/database/schema.md +1 -1
- package/templates/agent-docs/database/seedsdialects.md +1 -1
- package/templates/agent-docs/deployment.md +22 -3
- package/templates/agent-docs/internationalization.md +131 -13
- package/templates/agent-docs/introduction.md +46 -0
- package/templates/agent-docs/local-first-mobile.md +34 -7
- package/templates/agent-docs/plugins/ai-flows.md +1 -1
- package/templates/agent-docs/plugins/audit.md +5 -5
- package/templates/agent-docs/plugins/cdc-out.md +2 -2
- package/templates/agent-docs/plugins/{versioning.md → row-history.md} +22 -22
- package/templates/agent-docs/plugins/storage.md +2 -2
- package/templates/agent-docs/plugins.md +29 -7
- package/templates/agent-docs/reference.md +39 -2
- package/templates/agent-docs/routing.md +341 -47
- package/templates/agent-docs/schema-driven-ui.md +78 -2
- package/templates/agent-docs/security.md +125 -8
- package/templates/agent-docs/templates/apibackends.md +14 -14
- package/templates/agent-docs/templates/overview.md +1 -1
- package/templates/agent-docs/whats-new.md +100 -29
- package/templates/apps/api-ai/package.json +6 -7
- package/templates/apps/api-ai/tests/summarize.test.ts +1 -1
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +9 -10
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/{api-versioning → api-row-history}/README.md +3 -3
- package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/app.config.ts +3 -3
- package/templates/apps/{api-versioning → api-row-history}/database/schema.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.server.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.ts +1 -1
- package/templates/apps/{api-versioning → api-row-history}/package.json +8 -8
- package/templates/apps/api-row-history/template.json +6 -0
- package/templates/apps/{api-versioning → api-row-history}/tests/documents.create.test.ts +1 -1
- package/templates/apps/api-saas/app.config.ts +1 -0
- package/templates/apps/api-saas/package.json +10 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- package/templates/apps/frontend-collab/src/pages/page.test.tsx +10 -9
- package/templates/apps/frontend-collab/src/pages/page.tsx +42 -65
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +6 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +6 -7
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/agentsMd-Bu_XQgVf.js +0 -2
- package/dist/apiBuild-BZ-plaQN.js +0 -2
- package/dist/appGraph-CM9wvUmD.js +0 -2
- package/dist/appGraph-Cv2W_wO5.js +0 -111
- package/dist/build-CFmqF39c.js +0 -721
- package/dist/checkCommand-DomvYDi1.js +0 -2
- package/dist/codegen-DiMn2KkZ.js +0 -2
- package/dist/dbCommand-Bnl7MSGC.js +0 -2
- package/dist/dev-Dz9GqA6P.js +0 -3
- package/dist/doctorCommand-BlciWUJb.js +0 -2
- package/dist/fileConventions-Cof68_BL.js +0 -33
- package/dist/frameworkTableAssembly-CVDB2hCq.js +0 -2
- package/dist/inspectMetrics-CfdKLh6t.js +0 -72
- package/dist/serveCommand-Bbgg7Paq.js +0 -2
- package/dist/start-7b_bL8Uc.js +0 -3
- package/dist/start-B1nH9ahf.js +0 -1150
- package/dist/updateCommand-BweY17z-.js +0 -2
- package/dist/webDev-BcRFTsMA.js +0 -2
- package/templates/apps/api-versioning/template.json +0 -6
- /package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.server.ts +0 -0
- /package/templates/apps/{api-versioning → api-row-history}/tsconfig.json +0 -0
|
@@ -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
|
|
225
|
+
let { walk: t, loadDiscovered: n } = await import("./dev--A3nsxA3.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.
|
|
3
|
+
"version": "0.52.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",
|
|
@@ -700,6 +700,36 @@
|
|
|
700
700
|
"title": "A COMPUTED `source:` can keep its typed names — do not widen the whole registry",
|
|
701
701
|
"kind": "manual"
|
|
702
702
|
},
|
|
703
|
+
{
|
|
704
|
+
"version": "0.51.0",
|
|
705
|
+
"id": "0.51.0/02_restore-refuses-without-marker",
|
|
706
|
+
"title": "`voltro data restore` refuses when it cannot write the in-progress marker",
|
|
707
|
+
"kind": "manual"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
"version": "0.51.0",
|
|
711
|
+
"id": "0.51.0/03_restore-assets-returns-counts",
|
|
712
|
+
"title": "`restoreAssetsFromCas` returns `{ count, objects }`, not a number",
|
|
713
|
+
"kind": "manual"
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
"version": "0.51.0",
|
|
717
|
+
"id": "0.51.0/04_i18n-timezone-prop",
|
|
718
|
+
"title": "`<I18nProvider>`: `timeZone` is its own prop, not an `intlConfig` member",
|
|
719
|
+
"kind": "transform"
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
"version": "0.52.0",
|
|
723
|
+
"id": "0.52.0/01_plugin-versioning-to-row-history",
|
|
724
|
+
"title": "@voltro/plugin-versioning → @voltro/plugin-row-history (the name now says what it does)",
|
|
725
|
+
"kind": "transform"
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
"version": "0.52.0",
|
|
729
|
+
"id": "0.52.0/02_http-interceptor-fail-closed",
|
|
730
|
+
"title": "A throwing `onHttpRequest` interceptor now refuses the request (500) instead of being ignored",
|
|
731
|
+
"kind": "manual"
|
|
732
|
+
},
|
|
703
733
|
{
|
|
704
734
|
"version": "0.6.0",
|
|
705
735
|
"id": "0.6.0/01_no-dev-session-secret",
|
|
@@ -775,22 +805,23 @@
|
|
|
775
805
|
"@effect/platform-node": "^0.108.0",
|
|
776
806
|
"@effect/sql": "^0.52.0",
|
|
777
807
|
"@effect/workflow": "^0.19.0",
|
|
778
|
-
"@voltro/ai": "0.
|
|
779
|
-
"@voltro/cache": "0.
|
|
780
|
-
"@voltro/
|
|
781
|
-
"@voltro/
|
|
782
|
-
"@voltro/
|
|
783
|
-
"@voltro/
|
|
784
|
-
"@voltro/
|
|
785
|
-
"@voltro/
|
|
786
|
-
"@voltro/plugin-
|
|
787
|
-
"@voltro/plugin-
|
|
788
|
-
"@voltro/plugin-
|
|
789
|
-
"@voltro/plugin-
|
|
790
|
-
"@voltro/
|
|
791
|
-
"@voltro/
|
|
792
|
-
"@voltro/
|
|
793
|
-
"@voltro/
|
|
808
|
+
"@voltro/ai": "0.52.0",
|
|
809
|
+
"@voltro/cache": "0.52.0",
|
|
810
|
+
"@voltro/client": "0.52.0",
|
|
811
|
+
"@voltro/data-transfer": "0.52.0",
|
|
812
|
+
"@voltro/database": "0.52.0",
|
|
813
|
+
"@voltro/env": "0.52.0",
|
|
814
|
+
"@voltro/kv": "0.52.0",
|
|
815
|
+
"@voltro/logger": "0.52.0",
|
|
816
|
+
"@voltro/plugin-auth": "0.52.0",
|
|
817
|
+
"@voltro/plugin-broadcast": "0.52.0",
|
|
818
|
+
"@voltro/plugin-mail": "0.52.0",
|
|
819
|
+
"@voltro/plugin-storage": "0.52.0",
|
|
820
|
+
"@voltro/plugin-webhooks": "0.52.0",
|
|
821
|
+
"@voltro/protocol": "0.52.0",
|
|
822
|
+
"@voltro/runtime": "0.52.0",
|
|
823
|
+
"@voltro/serverless": "0.52.0",
|
|
824
|
+
"@voltro/workflow": "0.52.0",
|
|
794
825
|
"chokidar": "^5.0.0",
|
|
795
826
|
"ioredis": "^5.11.1",
|
|
796
827
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.core.md
CHANGED
|
@@ -404,11 +404,21 @@ Do NOT write `x.page.tsx`, `index.page.tsx` or `[id].page.tsx` — those were th
|
|
|
404
404
|
**Every suffix above is a CONTRACT, not a label.** Something else depends on each
|
|
405
405
|
promise, and `voltro doctor` enforces all of them:
|
|
406
406
|
|
|
407
|
-
- `*.component.ui.tsx` may read (`useT`, `useCan`, `usePermissions
|
|
408
|
-
never import a write hook
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
407
|
+
- `*.component.ui.tsx` may read (`useT`, `useTFn`, `useCan`, `usePermissions`,
|
|
408
|
+
the `@voltro/i18n` formatters) but must never import a write hook
|
|
409
|
+
(`useMutation`, `useAction`, `useUpload`, …). That is what lets a caller render
|
|
410
|
+
it ten thousand times in a list, reuse it across features, and prerender it
|
|
411
|
+
without reading its source. Lift the write into the owning `*.component.tsx`
|
|
412
|
+
and pass a handler down.
|
|
413
|
+
|
|
414
|
+
**Reading is a recommendation, not a grudging allowance — call the hook, do
|
|
415
|
+
not take a prop.** Threading `t` / `formatDate` / `useCan` through props is
|
|
416
|
+
prop-drilling: it grows the prop surface with every formatter, has to be
|
|
417
|
+
restated in every intermediate signature and every test, and does NOT make the
|
|
418
|
+
component more reusable — it makes every call site worse. The one argument for
|
|
419
|
+
the prop, "it renders without a provider", is already paid for the moment any
|
|
420
|
+
test mounts one. Pick one way per component head: a `t` prop beside a
|
|
421
|
+
formatter hook is the worst of both.
|
|
412
422
|
- `*.internal.ts` is the promise that refactoring inside that directory breaks
|
|
413
423
|
nobody. An import from another subtree revokes it.
|
|
414
424
|
- `*.types.ts` having no runtime export is what makes importing it free and makes
|
|
@@ -686,6 +696,17 @@ primitive → just save; the supervised dev loop respawns and regenerates.
|
|
|
686
696
|
- **Don't roll your own session token / ICU/i18n / job runner** — use
|
|
687
697
|
`signSession`/`verifySession`, `@voltro/i18n`, `@effect/workflow`.
|
|
688
698
|
- **Don't add `bullmq`/`temporal`/`react-query`/SWR** — see Mental model.
|
|
699
|
+
- **Don't build a GraphQL layer over the stores.** It would bypass the
|
|
700
|
+
reactivity path (source-based invalidation, per-delivery guards) — a second,
|
|
701
|
+
dead read path. External consumers → REST + OpenAPI (`defineRestRoute` +
|
|
702
|
+
`@voltro/plugin-openapi`); internal clients → RPC + live subscriptions.
|
|
703
|
+
- **Don't write `'use server'` / `'use client'` — there is no RSC.** Islands +
|
|
704
|
+
loaders + streaming SSR are the model; those directives mark a boundary this
|
|
705
|
+
framework does not have.
|
|
706
|
+
- **Don't split the app into microservices with service-to-service RPC.** One
|
|
707
|
+
monolith process, scaled by running more replicas (`@effect/cluster` shards
|
|
708
|
+
durable workflows across instances), is the architecture. An external system
|
|
709
|
+
boundary is REST + OpenAPI; a reliable outbound effect is `ctx.outbox`.
|
|
689
710
|
|
|
690
711
|
## Plugins
|
|
691
712
|
|
package/templates/AGENTS.md
CHANGED
|
@@ -404,11 +404,21 @@ Do NOT write `x.page.tsx`, `index.page.tsx` or `[id].page.tsx` — those were th
|
|
|
404
404
|
**Every suffix above is a CONTRACT, not a label.** Something else depends on each
|
|
405
405
|
promise, and `voltro doctor` enforces all of them:
|
|
406
406
|
|
|
407
|
-
- `*.component.ui.tsx` may read (`useT`, `useCan`, `usePermissions
|
|
408
|
-
never import a write hook
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
407
|
+
- `*.component.ui.tsx` may read (`useT`, `useTFn`, `useCan`, `usePermissions`,
|
|
408
|
+
the `@voltro/i18n` formatters) but must never import a write hook
|
|
409
|
+
(`useMutation`, `useAction`, `useUpload`, …). That is what lets a caller render
|
|
410
|
+
it ten thousand times in a list, reuse it across features, and prerender it
|
|
411
|
+
without reading its source. Lift the write into the owning `*.component.tsx`
|
|
412
|
+
and pass a handler down.
|
|
413
|
+
|
|
414
|
+
**Reading is a recommendation, not a grudging allowance — call the hook, do
|
|
415
|
+
not take a prop.** Threading `t` / `formatDate` / `useCan` through props is
|
|
416
|
+
prop-drilling: it grows the prop surface with every formatter, has to be
|
|
417
|
+
restated in every intermediate signature and every test, and does NOT make the
|
|
418
|
+
component more reusable — it makes every call site worse. The one argument for
|
|
419
|
+
the prop, "it renders without a provider", is already paid for the moment any
|
|
420
|
+
test mounts one. Pick one way per component head: a `t` prop beside a
|
|
421
|
+
formatter hook is the worst of both.
|
|
412
422
|
- `*.internal.ts` is the promise that refactoring inside that directory breaks
|
|
413
423
|
nobody. An import from another subtree revokes it.
|
|
414
424
|
- `*.types.ts` having no runtime export is what makes importing it free and makes
|
|
@@ -686,6 +696,17 @@ primitive → just save; the supervised dev loop respawns and regenerates.
|
|
|
686
696
|
- **Don't roll your own session token / ICU/i18n / job runner** — use
|
|
687
697
|
`signSession`/`verifySession`, `@voltro/i18n`, `@effect/workflow`.
|
|
688
698
|
- **Don't add `bullmq`/`temporal`/`react-query`/SWR** — see Mental model.
|
|
699
|
+
- **Don't build a GraphQL layer over the stores.** It would bypass the
|
|
700
|
+
reactivity path (source-based invalidation, per-delivery guards) — a second,
|
|
701
|
+
dead read path. External consumers → REST + OpenAPI (`defineRestRoute` +
|
|
702
|
+
`@voltro/plugin-openapi`); internal clients → RPC + live subscriptions.
|
|
703
|
+
- **Don't write `'use server'` / `'use client'` — there is no RSC.** Islands +
|
|
704
|
+
loaders + streaming SSR are the model; those directives mark a boundary this
|
|
705
|
+
framework does not have.
|
|
706
|
+
- **Don't split the app into microservices with service-to-service RPC.** One
|
|
707
|
+
monolith process, scaled by running more replicas (`@effect/cluster` shards
|
|
708
|
+
durable workflows across instances), is the architecture. An external system
|
|
709
|
+
boundary is REST + OpenAPI; a reliable outbound effect is `ctx.outbox`.
|
|
689
710
|
|
|
690
711
|
## Plugins
|
|
691
712
|
|
|
@@ -707,7 +728,7 @@ each plugin's own README.
|
|
|
707
728
|
|
|
708
729
|
| Topic | Open | Summary |
|
|
709
730
|
|---|---|---|
|
|
710
|
-
| **What's new in 0.
|
|
731
|
+
| **What's new in 0.52.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
732
|
| 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
733
|
| 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
734
|
| 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. |
|
|
@@ -765,7 +786,7 @@ each plugin's own README.
|
|
|
765
786
|
| auth-workos | `node_modules/@voltro/cli/templates/agent-docs/plugins/auth-workos.md` (or `node_modules/@voltro/plugin-auth-workos/README.md`) | WorkOS AuthStrategy — verifies WorkOS AuthKit / SSO JWTs via JWKS (no API key) and maps org_id → tenantId. |
|
|
766
787
|
| billing | `node_modules/@voltro/cli/templates/agent-docs/plugins/billing.md` (or `node_modules/@voltro/plugin-billing/README.md`) | Subscriptions, plans, entitlements, and usage metering over a pluggable provider (Stripe + mock). Money is integer minor units. |
|
|
767
788
|
| broadcast | `node_modules/@voltro/cli/templates/agent-docs/plugins/broadcast.md` (or `node_modules/@voltro/plugin-broadcast/README.md`) | Cross-replica reactivity over a pub/sub bus (Redis / NATS) for non-postgres dialects — closes the single-instance gap so a write on one pod surfaces on another. |
|
|
768
|
-
| cdc-out | `node_modules/@voltro/cli/templates/agent-docs/plugins/cdc-out.md` (or `node_modules/@voltro/plugin-cdc-out/README.md`) | Declaratively mirror table changes outward to external sinks (webhook
|
|
789
|
+
| cdc-out | `node_modules/@voltro/cli/templates/agent-docs/plugins/cdc-out.md` (or `node_modules/@voltro/plugin-cdc-out/README.md`) | Declaratively mirror table changes outward to external sinks (webhook, plus a CdcSink interface for custom sinks) through a durable outbox — ordered per pipe, at-least-once from enqueue, retried with backoff, dead-lettered. |
|
|
769
790
|
| clickhouse | `node_modules/@voltro/cli/templates/agent-docs/plugins/clickhouse.md` (or `node_modules/@voltro/plugin-clickhouse/README.md`) | Production-grade OLAP AnalyticsSink over ClickHouse — self-hosted or ClickHouse Cloud — for billions of events with millisecond aggregates. |
|
|
770
791
|
| datadog | `node_modules/@voltro/cli/templates/agent-docs/plugins/datadog.md` (or `node_modules/@voltro/plugin-datadog/README.md`) | Agentless Datadog metrics exporter — pushes the unified Metrics-API to Datadog's /api/v2/series HTTP intake. |
|
|
771
792
|
| deactivation | `node_modules/@voltro/cli/templates/agent-docs/plugins/deactivation.md` (or `node_modules/@voltro/plugin-deactivation/README.md`) | A schema mixin that marks a subject as deactivated (can't log in) while keeping its data fully visible — the deliberate opposite of soft-delete. |
|
|
@@ -785,14 +806,14 @@ each plugin's own README.
|
|
|
785
806
|
| prometheus | `node_modules/@voltro/cli/templates/agent-docs/plugins/prometheus.md` (or `node_modules/@voltro/plugin-prometheus/README.md`) | Prometheus exporter — scrapes the unified Metrics-API at GET /metrics in text exposition format. |
|
|
786
807
|
| ratelimit | `node_modules/@voltro/cli/templates/agent-docs/plugins/ratelimit.md` (or `node_modules/@voltro/plugin-ratelimit/README.md`) | Per-endpoint, per-subject and per-tenant request limits via the rpc interceptors. Sliding-window / fixed-window / token-bucket, memory / postgres / redis stores. |
|
|
787
808
|
| rbac | `node_modules/@voltro/cli/templates/agent-docs/plugins/rbac.md` (or `node_modules/@voltro/plugin-rbac/README.md`) | Roles + permissions + the permission() handler guard. Roles compile to scopes. |
|
|
809
|
+
| row-history | `node_modules/@voltro/cli/templates/agent-docs/plugins/row-history.md` (or `node_modules/@voltro/plugin-row-history/README.md`) | Full row history + time-travel. audit() records who/when; row-history records what-changed-to-what — a value snapshot of every row on every write, with as-of queries. |
|
|
788
810
|
| scim | `node_modules/@voltro/cli/templates/agent-docs/plugins/scim.md` (or `node_modules/@voltro/plugin-scim/README.md`) | SCIM 2.0 provisioning — Users + Groups REST endpoints so an enterprise IdP (Okta, Entra, OneLogin) can create / update / deactivate users in your app. |
|
|
789
811
|
| search | `node_modules/@voltro/cli/templates/agent-docs/plugins/search.md` (or `node_modules/@voltro/plugin-search/README.md`) | Keep an external search index (Typesense / Meilisearch / Algolia) in sync with your tables via the ChangeEvent tap, query it tenant-scoped through a typed action + hook. |
|
|
790
812
|
| sentry | `node_modules/@voltro/cli/templates/agent-docs/plugins/sentry.md` (or `node_modules/@voltro/plugin-sentry/README.md`) | Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and opt-in performance traces. |
|
|
791
813
|
| soft-delete | `node_modules/@voltro/cli/templates/agent-docs/plugins/soft-delete.md` (or `node_modules/@voltro/plugin-soft-delete/README.md`) | The softDelete() schema mixin — deletedAt / deletedBy columns, delete() redirected to an UPDATE, default reads filtered, hardDelete() bypass. |
|
|
792
814
|
| sso-saml | `node_modules/@voltro/cli/templates/agent-docs/plugins/sso-saml.md` (or `node_modules/@voltro/plugin-sso-saml/README.md`) | Enterprise SAML 2.0 SSO — SP-initiated login, ACS assertion consumer, SP metadata. Signature verification via @node-saml/node-saml; framework session minting built in. |
|
|
793
|
-
| storage | `node_modules/@voltro/cli/templates/agent-docs/plugins/storage.md` (or `node_modules/@voltro/plugin-storage/README.md`) | File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / R2
|
|
815
|
+
| storage | `node_modules/@voltro/cli/templates/agent-docs/plugins/storage.md` (or `node_modules/@voltro/plugin-storage/README.md`) | File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / MinIO (R2 and GCS via their S3 interop) / Azure / database / filesystem / memory providers, presigned URLs, a dashboard browser. |
|
|
794
816
|
| tinybird | `node_modules/@voltro/cli/templates/agent-docs/plugins/tinybird.md` (or `node_modules/@voltro/plugin-tinybird/README.md`) | Hosted-ClickHouse AnalyticsSink over Tinybird's Events API + Pipes — pay-as-you-go OLAP without operating a cluster. |
|
|
795
|
-
| versioning | `node_modules/@voltro/cli/templates/agent-docs/plugins/versioning.md` (or `node_modules/@voltro/plugin-versioning/README.md`) | Full row history + time-travel. audit() records who/when; versioning records what-changed-to-what — a value snapshot of every row on every write, with as-of queries. |
|
|
796
817
|
| webhooks | `node_modules/@voltro/cli/templates/agent-docs/plugins/webhooks.md` (or `node_modules/@voltro/plugin-webhooks/README.md`) | First-class outgoing + incoming webhooks — declared events with a webhook: block, defineIncomingWebhook in *.webhook.tsx files, runtime subscriptions, durable delivery via @effect/workflow, HMAC signing, provider presets, and idempotency. |
|
|
797
818
|
|
|
798
819
|
|
|
@@ -9,7 +9,7 @@ each plugin's own README.
|
|
|
9
9
|
|
|
10
10
|
| Topic | Open | Summary |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| **What's new in 0.
|
|
12
|
+
| **What's new in 0.52.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. |
|
|
@@ -67,7 +67,7 @@ each plugin's own README.
|
|
|
67
67
|
| auth-workos | `node_modules/@voltro/cli/templates/agent-docs/plugins/auth-workos.md` (or `node_modules/@voltro/plugin-auth-workos/README.md`) | WorkOS AuthStrategy — verifies WorkOS AuthKit / SSO JWTs via JWKS (no API key) and maps org_id → tenantId. |
|
|
68
68
|
| billing | `node_modules/@voltro/cli/templates/agent-docs/plugins/billing.md` (or `node_modules/@voltro/plugin-billing/README.md`) | Subscriptions, plans, entitlements, and usage metering over a pluggable provider (Stripe + mock). Money is integer minor units. |
|
|
69
69
|
| broadcast | `node_modules/@voltro/cli/templates/agent-docs/plugins/broadcast.md` (or `node_modules/@voltro/plugin-broadcast/README.md`) | Cross-replica reactivity over a pub/sub bus (Redis / NATS) for non-postgres dialects — closes the single-instance gap so a write on one pod surfaces on another. |
|
|
70
|
-
| cdc-out | `node_modules/@voltro/cli/templates/agent-docs/plugins/cdc-out.md` (or `node_modules/@voltro/plugin-cdc-out/README.md`) | Declaratively mirror table changes outward to external sinks (webhook
|
|
70
|
+
| cdc-out | `node_modules/@voltro/cli/templates/agent-docs/plugins/cdc-out.md` (or `node_modules/@voltro/plugin-cdc-out/README.md`) | Declaratively mirror table changes outward to external sinks (webhook, plus a CdcSink interface for custom sinks) through a durable outbox — ordered per pipe, at-least-once from enqueue, retried with backoff, dead-lettered. |
|
|
71
71
|
| clickhouse | `node_modules/@voltro/cli/templates/agent-docs/plugins/clickhouse.md` (or `node_modules/@voltro/plugin-clickhouse/README.md`) | Production-grade OLAP AnalyticsSink over ClickHouse — self-hosted or ClickHouse Cloud — for billions of events with millisecond aggregates. |
|
|
72
72
|
| datadog | `node_modules/@voltro/cli/templates/agent-docs/plugins/datadog.md` (or `node_modules/@voltro/plugin-datadog/README.md`) | Agentless Datadog metrics exporter — pushes the unified Metrics-API to Datadog's /api/v2/series HTTP intake. |
|
|
73
73
|
| deactivation | `node_modules/@voltro/cli/templates/agent-docs/plugins/deactivation.md` (or `node_modules/@voltro/plugin-deactivation/README.md`) | A schema mixin that marks a subject as deactivated (can't log in) while keeping its data fully visible — the deliberate opposite of soft-delete. |
|
|
@@ -87,12 +87,12 @@ each plugin's own README.
|
|
|
87
87
|
| prometheus | `node_modules/@voltro/cli/templates/agent-docs/plugins/prometheus.md` (or `node_modules/@voltro/plugin-prometheus/README.md`) | Prometheus exporter — scrapes the unified Metrics-API at GET /metrics in text exposition format. |
|
|
88
88
|
| ratelimit | `node_modules/@voltro/cli/templates/agent-docs/plugins/ratelimit.md` (or `node_modules/@voltro/plugin-ratelimit/README.md`) | Per-endpoint, per-subject and per-tenant request limits via the rpc interceptors. Sliding-window / fixed-window / token-bucket, memory / postgres / redis stores. |
|
|
89
89
|
| rbac | `node_modules/@voltro/cli/templates/agent-docs/plugins/rbac.md` (or `node_modules/@voltro/plugin-rbac/README.md`) | Roles + permissions + the permission() handler guard. Roles compile to scopes. |
|
|
90
|
+
| row-history | `node_modules/@voltro/cli/templates/agent-docs/plugins/row-history.md` (or `node_modules/@voltro/plugin-row-history/README.md`) | Full row history + time-travel. audit() records who/when; row-history records what-changed-to-what — a value snapshot of every row on every write, with as-of queries. |
|
|
90
91
|
| scim | `node_modules/@voltro/cli/templates/agent-docs/plugins/scim.md` (or `node_modules/@voltro/plugin-scim/README.md`) | SCIM 2.0 provisioning — Users + Groups REST endpoints so an enterprise IdP (Okta, Entra, OneLogin) can create / update / deactivate users in your app. |
|
|
91
92
|
| search | `node_modules/@voltro/cli/templates/agent-docs/plugins/search.md` (or `node_modules/@voltro/plugin-search/README.md`) | Keep an external search index (Typesense / Meilisearch / Algolia) in sync with your tables via the ChangeEvent tap, query it tenant-scoped through a typed action + hook. |
|
|
92
93
|
| sentry | `node_modules/@voltro/cli/templates/agent-docs/plugins/sentry.md` (or `node_modules/@voltro/plugin-sentry/README.md`) | Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and opt-in performance traces. |
|
|
93
94
|
| soft-delete | `node_modules/@voltro/cli/templates/agent-docs/plugins/soft-delete.md` (or `node_modules/@voltro/plugin-soft-delete/README.md`) | The softDelete() schema mixin — deletedAt / deletedBy columns, delete() redirected to an UPDATE, default reads filtered, hardDelete() bypass. |
|
|
94
95
|
| sso-saml | `node_modules/@voltro/cli/templates/agent-docs/plugins/sso-saml.md` (or `node_modules/@voltro/plugin-sso-saml/README.md`) | Enterprise SAML 2.0 SSO — SP-initiated login, ACS assertion consumer, SP metadata. Signature verification via @node-saml/node-saml; framework session minting built in. |
|
|
95
|
-
| storage | `node_modules/@voltro/cli/templates/agent-docs/plugins/storage.md` (or `node_modules/@voltro/plugin-storage/README.md`) | File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / R2
|
|
96
|
+
| storage | `node_modules/@voltro/cli/templates/agent-docs/plugins/storage.md` (or `node_modules/@voltro/plugin-storage/README.md`) | File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / MinIO (R2 and GCS via their S3 interop) / Azure / database / filesystem / memory providers, presigned URLs, a dashboard browser. |
|
|
96
97
|
| tinybird | `node_modules/@voltro/cli/templates/agent-docs/plugins/tinybird.md` (or `node_modules/@voltro/plugin-tinybird/README.md`) | Hosted-ClickHouse AnalyticsSink over Tinybird's Events API + Pipes — pay-as-you-go OLAP without operating a cluster. |
|
|
97
|
-
| versioning | `node_modules/@voltro/cli/templates/agent-docs/plugins/versioning.md` (or `node_modules/@voltro/plugin-versioning/README.md`) | Full row history + time-travel. audit() records who/when; versioning records what-changed-to-what — a value snapshot of every row on every write, with as-of queries. |
|
|
98
98
|
| webhooks | `node_modules/@voltro/cli/templates/agent-docs/plugins/webhooks.md` (or `node_modules/@voltro/plugin-webhooks/README.md`) | First-class outgoing + incoming webhooks — declared events with a webhook: block, defineIncomingWebhook in *.webhook.tsx files, runtime subscriptions, durable delivery via @effect/workflow, HMAC signing, provider presets, and idempotency. |
|
|
@@ -460,7 +460,7 @@
|
|
|
460
460
|
{
|
|
461
461
|
"slug": "cdc-out",
|
|
462
462
|
"title": "CDC-out (reverse-ETL)",
|
|
463
|
-
"description": "Declaratively mirror table changes outward to external sinks (webhook
|
|
463
|
+
"description": "Declaratively mirror table changes outward to external sinks (webhook, plus a CdcSink interface for custom sinks) through a durable outbox — ordered per pipe, at-least-once from enqueue, retried with backoff, dead-lettered.",
|
|
464
464
|
"pkg": "@voltro/plugin-cdc-out",
|
|
465
465
|
"doc": "plugins/cdc-out.md",
|
|
466
466
|
"module": "agent-docs/plugins/cdc-out.md"
|
|
@@ -617,6 +617,14 @@
|
|
|
617
617
|
"doc": "plugins/rbac.md",
|
|
618
618
|
"module": "agent-docs/plugins/rbac.md"
|
|
619
619
|
},
|
|
620
|
+
{
|
|
621
|
+
"slug": "row-history",
|
|
622
|
+
"title": "Row history",
|
|
623
|
+
"description": "Full row history + time-travel. audit() records who/when; row-history records what-changed-to-what — a value snapshot of every row on every write, with as-of queries.",
|
|
624
|
+
"pkg": "@voltro/plugin-row-history",
|
|
625
|
+
"doc": "plugins/row-history.md",
|
|
626
|
+
"module": "agent-docs/plugins/row-history.md"
|
|
627
|
+
},
|
|
620
628
|
{
|
|
621
629
|
"slug": "scim",
|
|
622
630
|
"title": "SCIM",
|
|
@@ -660,7 +668,7 @@
|
|
|
660
668
|
{
|
|
661
669
|
"slug": "storage",
|
|
662
670
|
"title": "Storage",
|
|
663
|
-
"description": "File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / R2
|
|
671
|
+
"description": "File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / MinIO (R2 and GCS via their S3 interop) / Azure / database / filesystem / memory providers, presigned URLs, a dashboard browser.",
|
|
664
672
|
"pkg": "@voltro/plugin-storage",
|
|
665
673
|
"doc": "plugins/storage.md",
|
|
666
674
|
"module": "agent-docs/plugins/storage.md"
|
|
@@ -673,14 +681,6 @@
|
|
|
673
681
|
"doc": "plugins/tinybird.md",
|
|
674
682
|
"module": "agent-docs/plugins/tinybird.md"
|
|
675
683
|
},
|
|
676
|
-
{
|
|
677
|
-
"slug": "versioning",
|
|
678
|
-
"title": "Row versioning",
|
|
679
|
-
"description": "Full row history + time-travel. audit() records who/when; versioning records what-changed-to-what — a value snapshot of every row on every write, with as-of queries.",
|
|
680
|
-
"pkg": "@voltro/plugin-versioning",
|
|
681
|
-
"doc": "plugins/versioning.md",
|
|
682
|
-
"module": "agent-docs/plugins/versioning.md"
|
|
683
|
-
},
|
|
684
684
|
{
|
|
685
685
|
"slug": "webhooks",
|
|
686
686
|
"title": "Webhooks",
|
|
@@ -710,9 +710,9 @@
|
|
|
710
710
|
"api-ratelimit": "agent-docs/templates/apibackends.md",
|
|
711
711
|
"api-rbac": "agent-docs/templates/apibackends.md",
|
|
712
712
|
"api-rest": "agent-docs/templates/apibackends.md",
|
|
713
|
+
"api-row-history": "agent-docs/templates/apibackends.md",
|
|
713
714
|
"api-saas": "agent-docs/templates/apibackends.md",
|
|
714
715
|
"api-search": "agent-docs/templates/apibackends.md",
|
|
715
|
-
"api-versioning": "agent-docs/templates/apibackends.md",
|
|
716
716
|
"api-webhooks": "agent-docs/templates/apibackends.md",
|
|
717
717
|
"app": "agent-docs/templates/appshells.md",
|
|
718
718
|
"blank": "agent-docs/templates/appshells.md",
|
|
@@ -142,6 +142,12 @@ config value always winning:
|
|
|
142
142
|
| `VOLTRO_CSP_HTML` | CSP for HTML responses (under `strict` it defaults to the same policy as `VOLTRO_CSP`). `off` drops just this one. |
|
|
143
143
|
| `VOLTRO_HSTS` | `Strict-Transport-Security` value. `off` drops just this one. |
|
|
144
144
|
| `VOLTRO_MAX_RPC_BODY_BYTES` | Cap on the buffered `POST /rpc` JSON body (default 8 MiB) — an oversized body is refused `413` and never buffered past the cap. File uploads ride plugin routes with their own limits. |
|
|
145
|
+
| `VOLTRO_MAX_BODY_BYTES` | Cap on every OTHER body read — plugin HTTP routes, REST routes, incoming webhooks (default 8 MiB, matching the rpc cap). The config-file spelling is `http.maxBodyBytes` in `app.config.ts`; per-route overrides (`defineRestRoute({ maxBodyBytes })`, a webhook handler's `maxBodyBytes`) win over both. Oversize is `413` for `Content-Length` and chunked alike. |
|
|
146
|
+
|
|
147
|
+
Response compression for the buffered non-rpc surfaces (and `voltro start`'s
|
|
148
|
+
HTML) is configured in the same `http:` block — `http.compression.{enabled,minBytes}`
|
|
149
|
+
(default on, 1 KiB threshold; `POST /rpc` is never compressed). Details + the
|
|
150
|
+
BREACH reasoning: [Security → compression](/docs/security/overview#response-compression--and-where-breach-sits).
|
|
145
151
|
|
|
146
152
|
Generate any of the secret vars above with `voltro secret generate <purpose>` (see [`secret`](#command-quick-reference)) — it picks the right length and format. A lower environment's secrets must always differ from production's.
|
|
147
153
|
|
|
@@ -317,36 +323,48 @@ fails to update.
|
|
|
317
323
|
|
|
318
324
|
```
|
|
319
325
|
observed: 12/34 procedures exercised (35%)
|
|
326
|
+
1 declared/observed mismatch among the 12 that ran
|
|
320
327
|
mutation(orders.place) (api/orders/place.mutation.ts)
|
|
321
328
|
writes 'inventory' (update) but declares no target for it
|
|
322
329
|
subscriptions on that table are not invalidated by this mutation
|
|
323
330
|
fix: add { table: 'inventory', op: 'update' } to this procedure's targets
|
|
324
|
-
|
|
331
|
+
20 never ran — no observation exists, so nothing is claimed about them
|
|
332
|
+
2 ran with no table access recorded — indistinguishable from touching nothing, so nothing is claimed about them either
|
|
325
333
|
```
|
|
326
334
|
|
|
327
|
-
|
|
335
|
+
Four things about that output are deliberate:
|
|
328
336
|
|
|
329
337
|
- **Coverage comes first.** Three findings at 8% coverage and three at 95% are
|
|
330
338
|
different claims. Hiding the denominator is how a check starts overstating
|
|
331
339
|
what it knows.
|
|
332
|
-
-
|
|
333
|
-
|
|
334
|
-
|
|
340
|
+
- **Every result line carries the count it is a result about.** `1 mismatch
|
|
341
|
+
among the 12 that ran` — never a bare verdict. A sentence that still reads as
|
|
342
|
+
a conclusion once it is cut out of this block will eventually be cut out of
|
|
343
|
+
it, and quoted as a clean bill of health for a surface nobody measured.
|
|
344
|
+
- **The counts partition the declared set — they add up to the total, always.**
|
|
345
|
+
There are two ways to say nothing about a procedure, and both get a line:
|
|
346
|
+
it never ran, or it ran and no table access was recorded for it. A procedure
|
|
347
|
+
that appeared in no line at all would be indistinguishable from a defect in
|
|
348
|
+
`check` itself, so the two kinds of blindness are named separately and never
|
|
349
|
+
folded into the findings.
|
|
335
350
|
- **Observed diagnostics never fail the build.** An observation is evidence
|
|
336
351
|
about the runs that happened, not a proof about the ones that didn't, and
|
|
337
352
|
`check`'s exit code gates CI. They are always warnings.
|
|
338
353
|
|
|
339
|
-
A
|
|
340
|
-
|
|
354
|
+
A fifth thing is not visible in that output and matters more than any of the
|
|
355
|
+
four: **the recorder only knows what ran.** A boot is not a run. An idle dev
|
|
341
356
|
instance that started, served nothing and stopped produces
|
|
342
357
|
|
|
343
358
|
```
|
|
344
|
-
observed: 0/
|
|
345
|
-
|
|
359
|
+
observed: 0/34 procedures exercised (0%)
|
|
360
|
+
nothing was compared — a declaration is only checked against a procedure that RAN
|
|
361
|
+
34 never ran — no observation exists, so nothing is claimed about them
|
|
346
362
|
```
|
|
347
363
|
|
|
348
|
-
|
|
349
|
-
|
|
364
|
+
Note what that is NOT: it is not "no mismatches found". At zero coverage there
|
|
365
|
+
is no result to report, so the section reports the absence of the comparison
|
|
366
|
+
instead. The honest answer is also a useless one — it says nothing about any of
|
|
367
|
+
the surface. If you want this as a CI gate, the recording pass has
|
|
350
368
|
to be a run that actually *calls* the procedures. `voltro e2e` is one: it spawns
|
|
351
369
|
`voltro dev` for the api, which turns recording on, and then drives the specs. A
|
|
352
370
|
harness of your own that boots the app in its own process needs
|
|
@@ -365,6 +383,29 @@ handlers: a static pass over code that reaches the store through shared helpers,
|
|
|
365
383
|
behind conditionals, has a long tail of both false positives and false negatives
|
|
366
384
|
— and a check that is *sometimes* wrong is one people stop reading.
|
|
367
385
|
|
|
386
|
+
#### Declared vs LIVE — against a running server
|
|
387
|
+
|
|
388
|
+
When `check` runs against a live api (a local `voltro dev`, or a deployed one
|
|
389
|
+
via `--url`), it also diffs the tags your source declares — plugin routes
|
|
390
|
+
included — against the server's actual rpc registry:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
declared vs live: 2 of 214 source-declared tags are NOT registered on the server
|
|
394
|
+
⚠ presence.heartbeat — a generated client calls this and gets "Unknown request tag"
|
|
395
|
+
⚠ presence.list — a generated client calls this and gets "Unknown request tag"
|
|
396
|
+
fix: if the server runs older code, redeploy; if it is current, plugin route registration was dropped — check the boot line "plugin routes registered"
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Only the source→live direction is a finding — a tag the server carries that your
|
|
400
|
+
source does not declare is normal runtime synthesis (agents, undo, approvals).
|
|
401
|
+
This exists because exactly that gap has shipped once: every
|
|
402
|
+
plugin-contributed procedure dead under `voltro serve`, with nothing anywhere
|
|
403
|
+
saying so — the only evidence was a `Defect` frame in the browser console of
|
|
404
|
+
whoever happened to look. (That registration defect is fixed; the diff is the
|
|
405
|
+
runtime backstop for the next one, e.g. a stale deploy.) The server now also
|
|
406
|
+
logs every `Defect` frame it sends (`rpc defect sent to client`), so an unknown
|
|
407
|
+
tag is an operator-visible event rather than a client-only one.
|
|
408
|
+
|
|
368
409
|
The HTTP surface is reachable directly too — e.g. `curl -s localhost:4000/_voltro/inspect/rpc | jq` (there is no `/_voltro/inspect/queries` endpoint; it's `rpc` for procedures, `routes` for the web page tree, `subscriptions` for active subscribers).
|
|
369
410
|
|
|
370
411
|
## Where to read next
|
|
@@ -1364,7 +1405,7 @@ Point the Kubernetes liveness probe at `/internal/liveness` and the readiness pr
|
|
|
1364
1405
|
For WebSocket connections to land on the same backend (required for in-process subscription state):
|
|
1365
1406
|
|
|
1366
1407
|
- Reverse proxy: `lb_policy ip_hash` (Caddy) / `ip_hash` (nginx).
|
|
1367
|
-
-
|
|
1408
|
+
- Cross-instance subscription invalidation is built in on Postgres (LISTEN/NOTIFY) and MySQL/MariaDB (binlog CDC); on any other dialect add [`@voltro/plugin-broadcast`](/docs/plugins/broadcast) (Redis/NATS). With that in place a reconnect may land on any replica and still sees every change — sticky sessions then only keep one live connection pinned, they are not a correctness requirement.
|
|
1368
1409
|
|
|
1369
1410
|
## `voltro build api --target <swift|kotlin>` — native SDK generation
|
|
1370
1411
|
|
|
@@ -1796,6 +1837,47 @@ matching rule lives inside the CLI, so you cannot re-derive the list with your
|
|
|
1796
1837
|
own grep. `--json` prints the complete scan, nothing elided, with no preflight
|
|
1797
1838
|
output mixed in:
|
|
1798
1839
|
|
|
1840
|
+
### Unimported `@voltro/*` dependencies
|
|
1841
|
+
|
|
1842
|
+
A declared framework dependency nobody imports still gets installed, walked on
|
|
1843
|
+
every `voltro update`, and read as evidence the package is in use — its
|
|
1844
|
+
breaking-change notes included. The usual origin is a migration: the app moves
|
|
1845
|
+
off a framework package to a third-party one, and the `package.json` entry
|
|
1846
|
+
stays. `voltro doctor` checks every `@voltro/*` in `dependencies` and
|
|
1847
|
+
`devDependencies` for an import site:
|
|
1848
|
+
|
|
1849
|
+
```
|
|
1850
|
+
unimported deps · 8 @voltro/* package(s) declared, 214 file(s) scanned
|
|
1851
|
+
⚠ @voltro/i18n — declared in dependencies, imported nowhere
|
|
1852
|
+
a dependency nobody imports still gets installed, updated, and read as
|
|
1853
|
+
evidence the package is in use — its breaking-change notes included.
|
|
1854
|
+
Remove it, or if it IS imported through an assembled specifier the scan
|
|
1855
|
+
cannot see, keep it and ignore this line — the rule is advisory.
|
|
1856
|
+
· (2 loaded by the framework itself: @voltro/cli, @voltro/sql-postgres)
|
|
1857
|
+
```
|
|
1858
|
+
|
|
1859
|
+
Scoped to `@voltro/*` deliberately: for third-party packages the same question
|
|
1860
|
+
has a long tail of legitimate no-import shapes, and a rule that is sometimes
|
|
1861
|
+
wrong is one people stop reading. Three states are distinguished, and each is
|
|
1862
|
+
printed:
|
|
1863
|
+
|
|
1864
|
+
- **Exempt, by name** — packages the framework loads on your declaration
|
|
1865
|
+
(`@voltro/cli` is the binary; `@voltro/devtools` is mounted by `voltro dev`;
|
|
1866
|
+
the `@voltro/sql-*` dialect drivers are loaded from your config). An
|
|
1867
|
+
exemption you cannot see is a finding you cannot question.
|
|
1868
|
+
- **Not measurable yet** — `@voltro/client` / `@voltro/web` are normally
|
|
1869
|
+
imported by *generated* code. On a tree where codegen has never run, their
|
|
1870
|
+
absence is a missing measurement, not a dead dependency; the section says so
|
|
1871
|
+
and tells you to run `voltro dev` once.
|
|
1872
|
+
- **Unimported** — advisory, never fatal. A mention in a comment or an error
|
|
1873
|
+
string does not count as an import (a commented-out import is exactly the
|
|
1874
|
+
residue this looks for), and an import assembled at runtime from string
|
|
1875
|
+
pieces is invisible to the scan — the finding text says both.
|
|
1876
|
+
|
|
1877
|
+
The full report is in `voltro doctor --json` under `unimportedDeps`
|
|
1878
|
+
(`null` when there is no `package.json` to read — "could not check" and
|
|
1879
|
+
"checked, clean" never print the same).
|
|
1880
|
+
|
|
1799
1881
|
### Duplicate package instances
|
|
1800
1882
|
|
|
1801
1883
|
`voltro doctor` also reports any identity-sensitive package resolved at more than
|
|
@@ -2850,7 +2932,7 @@ Not every run can take it, and a run that cannot **says why** rather than quietl
|
|
|
2850
2932
|
taking the slower path:
|
|
2851
2933
|
|
|
2852
2934
|
- a store the framework cannot send DDL to (the in-memory store).
|
|
2853
|
-
- a **write recorder** on any table in the set — `
|
|
2935
|
+
- a **write recorder** on any table in the set — `rowHistoryPlugin({ timing:
|
|
2854
2936
|
'in-transaction' })` and friends. A recorder is keyed by table name, so a
|
|
2855
2937
|
staged write would find none and the recorder would silently not run. Its
|
|
2856
2938
|
promise is "if the change committed, the entry is there", so the run keeps the
|
|
@@ -2906,7 +2988,7 @@ ITSELF is not a cycle: one statement carries the whole table.
|
|
|
2906
2988
|
|
|
2907
2989
|
### A `replace` does not write per-row history
|
|
2908
2990
|
|
|
2909
|
-
Write recorders — `
|
|
2991
|
+
Write recorders — `rowHistoryPlugin({ timing: 'in-transaction' })` and anything
|
|
2910
2992
|
else registered through the same seam — are **suspended for a `replace`**. A
|
|
2911
2993
|
replace sets a state; it does not change rows, so a per-row history entry would
|
|
2912
2994
|
describe something that did not happen. On a large bundle that is not a detail:
|
|
@@ -3412,8 +3494,22 @@ Aborting on the first one made `--assets` unusable for exactly the deployment th
|
|
|
3412
3494
|
|
|
3413
3495
|
Only a genuine *not found* is treated this way. A 403 from a rotated credential or a 5xx from a backend outage still fails the capture — calling those "the object is gone" would turn a recoverable outage into a backup that quietly contains nothing.
|
|
3414
3496
|
|
|
3497
|
+
**Three numbers, because they answer three questions.** `_voltro_storage_refs` holds one row per *reference*, several of which legitimately name one *key*, and the content-addressed store keeps one body per distinct *object*:
|
|
3498
|
+
|
|
3499
|
+
```
|
|
3500
|
+
57 reference(s) → 16 key(s) → 16 object(s), 65476 byte(s) under assets/
|
|
3501
|
+
```
|
|
3502
|
+
|
|
3503
|
+
The stamp carries all three (`references`, `count`, `objects`, with `totalBytes` and `objectBytes` beside them). It used to carry only the reference count under the name `count`, so a stamp read `57` over a directory holding 16 files — and anyone answering *"are all the blobs there?"* after a restore compared the two and found a 3.5× gap that was not one. A key named by several references is also fetched once now, rather than downloaded and hashed once per row.
|
|
3504
|
+
|
|
3415
3505
|
Resume is per blob key, so re-running a `--assets` capture that was interrupted transfers only what is missing. The **dump itself has no resume** — a vendor artifact is one opaque file with no offset to restart from. If you need a resumable, chunkable, observable transfer, that is the logical path (`export` / `import`), and it is why the logical path exists.
|
|
3416
3506
|
|
|
3507
|
+
### The stamp's skew warning compares the backup against the TARGET
|
|
3508
|
+
|
|
3509
|
+
`restore` reads `voltro-backup-stamp.json` before touching anything and warns when the backup's schema fingerprint differs from the target's. That warning used to say the difference was against "what this code declares", and it was not — the value it compares against is the target database's *live* schema, read by introspection at restore time. Bringing a target to the backup's shape makes the warning disappear while the declared fingerprint is a third value entirely, which is how the mislabel was caught. The comparison was always the useful one; only the sentence was wrong, and it sent readers looking for a code change where a database differed.
|
|
3510
|
+
|
|
3511
|
+
The same distinction shows up in `voltro db plan`, which prints `live … · declared …` rather than `from → to` for the same reason: **a hash of a live database never equals the hash of the declaration it came from.** Introspection cannot recover everything a declaration carries — generated expressions, `maxLength`, sensitivity markers — so the two are not comparable and are not meant to match. The plan's operation list is what says whether they agree; `0 operations` under two different fingerprints means they do.
|
|
3512
|
+
|
|
3417
3513
|
### A restore that is interrupted refuses the next boot
|
|
3418
3514
|
|
|
3419
3515
|
`restore` writes one row into `_voltro_replace_in_progress` **before** the first destructive statement and removes it **after** the last write — the blobs included. Its presence at boot is a refusal naming the artifact that was going in.
|
|
@@ -3427,6 +3523,17 @@ Two changes, covering different dumps:
|
|
|
3427
3523
|
- **A backup taken by `voltro data backup` excludes the marker table** (`--exclude-table` / `--ignore-table`). It can no longer carry the thing that erases the guard on the way back in.
|
|
3428
3524
|
- **`restore` writes the marker back after the tool exits**, on the failing path as well as the succeeding one. That covers dumps taken before this version and dumps made by hand. If it was removed and rewritten you get a warning saying so; if it could not be rewritten you get an error, because the guard is then off for that run and nothing will stop the next boot.
|
|
3429
3525
|
|
|
3526
|
+
**A restore that cannot write the marker at all is refused.** Two things can prevent it — the bookkeeping store will not open (wrong credentials, an unreachable database, a missing env var, no `app.config.ts` from here), or the table is not there yet — and both mean the same thing to you: this restore would run with no guard. The refusal names which one it was:
|
|
3527
|
+
|
|
3528
|
+
```
|
|
3529
|
+
✗ refusing to restore: the in-progress marker cannot be written.
|
|
3530
|
+
reason: bookkeeping is unavailable: connect ECONNREFUSED 127.0.0.1:5432
|
|
3531
|
+
```
|
|
3532
|
+
|
|
3533
|
+
This used to be a silent hole rather than a refusal, and worse than silent. The failure to open the store was caught and discarded, and the discarded value guarded *every* branch below it — including the refusal that would have reported the guard missing. So a restore ran on and, over a database with zero marker rows, printed *"the next boot will REFUSE, by design"*. The next boot did not refuse, and `voltro data clear-replace-marker` had nothing to clear. A restore is the operation you run against a target that is already unwell, so the precaution was falling away exactly when it was needed.
|
|
3534
|
+
|
|
3535
|
+
**`--no-marker`** restores without the guard, deliberately. It warns every time and names the reason the marker was unavailable. It exists because the accidental way did: if going unguarded is ever right, it should be something you typed.
|
|
3536
|
+
|
|
3430
3537
|
| dialect | shape | effect |
|
|
3431
3538
|
|---|---|---|
|
|
3432
3539
|
| postgres, mysql, mariadb | the dump names the whole database, so the restore drops the marker table too | our backups exclude it; for any other dump the marker is written back after the tool exits |
|
|
@@ -3435,6 +3542,10 @@ Two changes, covering different dumps:
|
|
|
3435
3542
|
|
|
3436
3543
|
Clear a marker deliberately with `voltro data clear-replace-marker --yes` once you have decided the current state is correct.
|
|
3437
3544
|
|
|
3545
|
+
**`_voltro_data_transfers` is excluded for the same reason, one table over.** The restore opens its own run row there *before* the tool starts; a dump that carried the table dropped it mid-flight, and the update recording the outcome then wrote into a table that no longer held the row. The visible result was that a failed native restore did not appear in `voltro data transfers` at all — only the `backup` row the dump had brought over from the *source* database. The command that answers "did the restore finish" could not see the run asking.
|
|
3546
|
+
|
|
3547
|
+
Exactly those two tables are excluded, and the line is deliberate: a native restore into the same deployment *should* bring the migration ledger, the stored plans, the CDC offsets and the schedule claims — they describe the data being restored. These two describe the *restore*, and a record of an operation must not be overwritten by the operation it records.
|
|
3548
|
+
|
|
3438
3549
|
### Both directions are in the history
|
|
3439
3550
|
|
|
3440
3551
|
`backup` and `restore` write a row to the same `_voltro_data_transfers` record `import` and `export` use, so `voltro data transfers` answers "did last night's backup finish" from the instance that ran it:
|