@voltro/cli 0.2.2 → 0.4.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 +49 -0
- package/THIRD-PARTY-NOTICES.md +234 -1
- package/bin/voltro.mjs +71 -1
- package/dist/apiBuild-CvtQeBMs.js +190 -0
- package/dist/apiBuild-DQBNqNZ8.js +2 -0
- package/dist/bin.js +2 -2
- package/dist/{commands-DQy4812j.js → commands-DhyBIs1O.js} +2381 -1768
- package/dist/{dev--jHe1vcu.js → dev-CQxbrpDz.js} +1677 -1626
- package/dist/dev-DYjGqPGD.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/serveCommand-BZzUJIyo.js +1077 -0
- package/dist/serveEntry.js +2 -2
- package/package.json +22 -19
- package/templates/AGENTS.core.md +61 -0
- package/templates/AGENTS.md +61 -0
- package/templates/agent-docs/_manifest.json +4 -4
- package/templates/agent-docs/ai.md +28 -0
- package/templates/agent-docs/authentication.md +5 -2
- package/templates/agent-docs/cli.md +98 -0
- package/templates/agent-docs/data.md +193 -3
- package/templates/agent-docs/database/advancedqueries.md +29 -0
- package/templates/agent-docs/database/querying.md +2 -0
- package/templates/agent-docs/deployment.md +3 -1
- package/templates/agent-docs/internationalization.md +237 -2
- package/templates/agent-docs/observability.md +9 -0
- package/templates/agent-docs/plugins.md +2 -2
- package/templates/agent-docs/reference.md +76 -0
- package/templates/agent-docs/schema-driven-ui.md +551 -2
- package/templates/agent-docs/templates/apibackends.md +10 -8
- package/templates/agent-docs/templates/overview.md +1 -1
- package/templates/agent-docs/testing.md +177 -3
- package/templates/apps/api-ai/package.json +7 -7
- 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-storage/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/README.md +9 -4
- package/templates/apps/api-rbac/mutations/notes.create.mutation.server.ts +1 -1
- package/templates/apps/api-rbac/mutations/notes.create.mutation.ts +6 -6
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rbac/tests/notes.create.test.ts +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +8 -8
- 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 +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- 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 +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- 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/baselines/compose/docker/api.Dockerfile +10 -5
- package/templates/baselines/compose-mariadb/docker/api.Dockerfile +10 -5
- package/dist/apiBuild-OpZROja5.js +0 -2
- package/dist/apiBuild-o70rjpVJ.js +0 -184
- package/dist/dev-BKkZglQV.js +0 -2
- package/dist/serveCommand-93rRdEp0.js +0 -1077
package/dist/serveEntry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jt as e, rt as t } from "./dev-CQxbrpDz.js";
|
|
2
2
|
import { a as n } from "./startupRunner-DhlX9nqd.js";
|
|
3
|
-
import { t as r } from "./serveCommand-
|
|
3
|
+
import { t as r } from "./serveCommand-BZzUJIyo.js";
|
|
4
4
|
export { e as loadDotEnv, n as registerAppModules, t as registerDriver, r as runServe };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -52,27 +52,30 @@
|
|
|
52
52
|
"@effect/platform-node": "^0.107.0",
|
|
53
53
|
"@effect/sql": "^0.51.1",
|
|
54
54
|
"@effect/workflow": "^0.18.2",
|
|
55
|
+
"@voltro/ai": "0.4.0",
|
|
56
|
+
"@voltro/cache": "0.4.0",
|
|
57
|
+
"@voltro/data-transfer": "0.4.0",
|
|
58
|
+
"@voltro/database": "0.4.0",
|
|
59
|
+
"@voltro/env": "0.4.0",
|
|
60
|
+
"@voltro/kv": "0.4.0",
|
|
61
|
+
"@voltro/logger": "0.4.0",
|
|
62
|
+
"@voltro/plugin-auth": "0.4.0",
|
|
63
|
+
"@voltro/plugin-broadcast": "0.4.0",
|
|
64
|
+
"@voltro/plugin-mail": "0.4.0",
|
|
65
|
+
"@voltro/plugin-storage": "0.4.0",
|
|
66
|
+
"@voltro/plugin-webhooks": "0.4.0",
|
|
67
|
+
"@voltro/protocol": "0.4.0",
|
|
68
|
+
"@voltro/runtime": "0.4.0",
|
|
69
|
+
"@voltro/serverless": "0.4.0",
|
|
70
|
+
"@voltro/workflow": "0.4.0",
|
|
71
|
+
"chokidar": "^5.0.0",
|
|
72
|
+
"ioredis": "^5.11.1",
|
|
73
|
+
"ts-morph": "^28.0.0"
|
|
74
|
+
},
|
|
75
|
+
"optionalDependencies": {
|
|
55
76
|
"@tailwindcss/vite": "^4.3.2",
|
|
56
77
|
"@vitejs/plugin-react": "^6.0.3",
|
|
57
|
-
"@voltro/ai": "0.2.2",
|
|
58
|
-
"@voltro/cache": "0.2.2",
|
|
59
|
-
"@voltro/data-transfer": "0.2.2",
|
|
60
|
-
"@voltro/database": "0.2.2",
|
|
61
|
-
"@voltro/env": "0.2.2",
|
|
62
|
-
"@voltro/kv": "0.2.2",
|
|
63
|
-
"@voltro/logger": "0.2.2",
|
|
64
|
-
"@voltro/plugin-auth": "0.2.2",
|
|
65
|
-
"@voltro/plugin-broadcast": "0.2.2",
|
|
66
|
-
"@voltro/plugin-mail": "0.2.2",
|
|
67
|
-
"@voltro/plugin-storage": "0.2.2",
|
|
68
|
-
"@voltro/plugin-webhooks": "0.2.2",
|
|
69
|
-
"@voltro/protocol": "0.2.2",
|
|
70
|
-
"@voltro/runtime": "0.2.2",
|
|
71
|
-
"@voltro/serverless": "0.2.2",
|
|
72
|
-
"@voltro/workflow": "0.2.2",
|
|
73
|
-
"chokidar": "^5.0.0",
|
|
74
78
|
"esbuild": "^0.28.0",
|
|
75
|
-
"ioredis": "^5.11.1",
|
|
76
79
|
"tsx": "^4.23.0",
|
|
77
80
|
"vite": "^8.1.4"
|
|
78
81
|
},
|
package/templates/AGENTS.core.md
CHANGED
|
@@ -61,6 +61,53 @@ Ask in order; stop at the first "yes":
|
|
|
61
61
|
Deep dive: **`data.md`** (queries/mutations/actions/streams/errors),
|
|
62
62
|
**`workflows.md`**, **`scheduling.md`**.
|
|
63
63
|
|
|
64
|
+
### Pick the CLIENT primitive (decision rubric)
|
|
65
|
+
|
|
66
|
+
`useSubscription` / `useMutation` / `useAction` are the transport — they are NOT
|
|
67
|
+
the whole client surface. The framework ships a **headless primitive for every
|
|
68
|
+
common UI job**, each derived from the descriptor Schema you already wrote. Reach
|
|
69
|
+
for one BEFORE hand-rolling; ask in order:
|
|
70
|
+
|
|
71
|
+
1. **A form / any typed write UI?** → **`useFormBinding`** — fields + validation
|
|
72
|
+
derived from the mutation's input Schema, and a server
|
|
73
|
+
`ValidationError({ field })` routes itself to that field. Not per-field `useState`.
|
|
74
|
+
2. **A table / list with sort, filter, pagination?** → **`useDataTable`** (columns
|
|
75
|
+
from the query's output Schema, live rows). **`useQueryFilters`** builds filter
|
|
76
|
+
controls from the query's INPUT Schema; **`useQueryField`** is the query-bound picker.
|
|
77
|
+
3. **A file upload?** → **`useUpload`** (progress, cancel, provider-agnostic).
|
|
78
|
+
Not `FileReader` → base64 → action.
|
|
79
|
+
4. **Gating UI on permission?** → **`useCan`** / **`useCanAny`** (scopes, from
|
|
80
|
+
`@voltro/client`, fed by `<PermissionProvider scopes>`) or
|
|
81
|
+
**`useResourceCan`** / **`useResourceCans`** (per-resource ReBAC, reactive).
|
|
82
|
+
These live in the client, not behind a plugin — scopes are a framework
|
|
83
|
+
concept, so gating a button never requires installing rbac.
|
|
84
|
+
5. **A value derived from one or more subscriptions?** → **`useDerived`**
|
|
85
|
+
(dependency-tracked, referentially stable). Not a hand-maintained `useMemo` dep array.
|
|
86
|
+
6. **A very large list?** → **`useWindowedSubscription`** (subscribe to the visible
|
|
87
|
+
window only, not a million rows).
|
|
88
|
+
7. **Debounced input?** → **`useDebounced`**; **`useAsyncValidation`** for checks
|
|
89
|
+
that need the DB (uniqueness, cross-row) — live, over a query binding.
|
|
90
|
+
8. **A loading placeholder?** → **`useFormSkeleton`** / **`useTableSkeleton`** —
|
|
91
|
+
shaped like the REAL data, from the same Schema.
|
|
92
|
+
9. **Offline writes · undo · dry-run · "why is this value here?"** → **`useOutbox`**,
|
|
93
|
+
**`useUndoLog`**, **`usePreview`**, **`useProvenance`**.
|
|
94
|
+
10. **Global rpc-error handling?** → **`useOnRpcError`** + **`reportClientError`**.
|
|
95
|
+
|
|
96
|
+
| You're about to write | Reach for instead |
|
|
97
|
+
|---|---|
|
|
98
|
+
| per-field `useState` + `isSubmitting` + `resetForm()` | `useFormBinding` |
|
|
99
|
+
| shadcn `<Table>` + per-table sort/filter state | `useDataTable` |
|
|
100
|
+
| `FileReader` → base64 → action → `storage.put` | `useUpload` |
|
|
101
|
+
| `useMemo` fanning in 3+ subscriptions | `useDerived` |
|
|
102
|
+
| `setTimeout` debounce inside `useEffect` | `useDebounced` |
|
|
103
|
+
| `data === undefined ? <Skeleton/> : …` | `useFormSkeleton` / `useTableSkeleton` |
|
|
104
|
+
| a hand-kept page-scope check | `useCan` / `useResourceCan` |
|
|
105
|
+
| a hand-rolled presence roster / notification inbox / feature flag | `@voltro/plugin-presence` / `-notifications` / `-flags` |
|
|
106
|
+
|
|
107
|
+
Deep dive: **`schema-driven-ui.md`** (forms, tables, pickers, filters, skeletons),
|
|
108
|
+
**`reference.md`** (the complete hook list). If you are hand-writing more than ~20
|
|
109
|
+
lines of generic UI plumbing, stop and check those two first.
|
|
110
|
+
|
|
64
111
|
## Project layout
|
|
65
112
|
|
|
66
113
|
One OR MORE **projects** live under `apps/`. A project (`project.json`) is the
|
|
@@ -220,8 +267,22 @@ primitive → just save; the supervised dev loop respawns and regenerates.
|
|
|
220
267
|
But DO call `assertOwnTenant(input.tenantId, ctx.request.subject)` (from
|
|
221
268
|
`@voltro/plugin-multitenancy/guard`) in custom mutations that write raw rows,
|
|
222
269
|
and declare `error: TenantMismatch` — subscriptions are auto-scoped, writes are not.
|
|
270
|
+
- **Gate authorization declaratively with `guards:`.** Add
|
|
271
|
+
`guards: [{ scope: 'notes:write' }]` to `defineMutation`/`defineQuery`/`defineAction`
|
|
272
|
+
— the framework enforces it BEFORE the executor (before the txn opens), fails
|
|
273
|
+
with a typed `ScopeError` (auto-merged into the wire error union), and checks the
|
|
274
|
+
caller's EFFECTIVE scopes (raw ∪ rbac roles). Guards are browser-safe DATA (scope
|
|
275
|
+
strings + a pure `resource: (input) => id` extractor — never a server fn). Use the
|
|
276
|
+
in-handler `ctx.access.has(scope)` / `yield* ctx.access.require(scope)` (or rbac's
|
|
277
|
+
`permission()`) only for checks that need LOADED data (row ownership).
|
|
223
278
|
- **Don't store secrets in the schema or in `Subject`.** Declare env via
|
|
224
279
|
`defineEnv` (`configuration.md`); carry only ids in `Subject`.
|
|
280
|
+
- **In production, `voltro build` BEFORE `voltro serve`.** A production
|
|
281
|
+
(`NODE_ENV=production`) serve REQUIRES the precompiled serve bundle and fails
|
|
282
|
+
loud if it's missing — production never transpiles on demand. The generated
|
|
283
|
+
Dockerfiles already do `voltro build` then `voltro serve`; if you write your
|
|
284
|
+
own prod start, build first. (`voltro dev` + a non-prod local `serve` still use
|
|
285
|
+
tsx.) Depth: the deployment topic.
|
|
225
286
|
- **Don't copy prod data down unmasked.** `voltro data export` (and
|
|
226
287
|
`--target api`) reads REAL rows — PII included. Copying prod → dev/stage MUST
|
|
227
288
|
go through a masking profile (`--profile`; classify columns `.sensitive()` /
|
package/templates/AGENTS.md
CHANGED
|
@@ -61,6 +61,53 @@ Ask in order; stop at the first "yes":
|
|
|
61
61
|
Deep dive: **`data.md`** (queries/mutations/actions/streams/errors),
|
|
62
62
|
**`workflows.md`**, **`scheduling.md`**.
|
|
63
63
|
|
|
64
|
+
### Pick the CLIENT primitive (decision rubric)
|
|
65
|
+
|
|
66
|
+
`useSubscription` / `useMutation` / `useAction` are the transport — they are NOT
|
|
67
|
+
the whole client surface. The framework ships a **headless primitive for every
|
|
68
|
+
common UI job**, each derived from the descriptor Schema you already wrote. Reach
|
|
69
|
+
for one BEFORE hand-rolling; ask in order:
|
|
70
|
+
|
|
71
|
+
1. **A form / any typed write UI?** → **`useFormBinding`** — fields + validation
|
|
72
|
+
derived from the mutation's input Schema, and a server
|
|
73
|
+
`ValidationError({ field })` routes itself to that field. Not per-field `useState`.
|
|
74
|
+
2. **A table / list with sort, filter, pagination?** → **`useDataTable`** (columns
|
|
75
|
+
from the query's output Schema, live rows). **`useQueryFilters`** builds filter
|
|
76
|
+
controls from the query's INPUT Schema; **`useQueryField`** is the query-bound picker.
|
|
77
|
+
3. **A file upload?** → **`useUpload`** (progress, cancel, provider-agnostic).
|
|
78
|
+
Not `FileReader` → base64 → action.
|
|
79
|
+
4. **Gating UI on permission?** → **`useCan`** / **`useCanAny`** (scopes, from
|
|
80
|
+
`@voltro/client`, fed by `<PermissionProvider scopes>`) or
|
|
81
|
+
**`useResourceCan`** / **`useResourceCans`** (per-resource ReBAC, reactive).
|
|
82
|
+
These live in the client, not behind a plugin — scopes are a framework
|
|
83
|
+
concept, so gating a button never requires installing rbac.
|
|
84
|
+
5. **A value derived from one or more subscriptions?** → **`useDerived`**
|
|
85
|
+
(dependency-tracked, referentially stable). Not a hand-maintained `useMemo` dep array.
|
|
86
|
+
6. **A very large list?** → **`useWindowedSubscription`** (subscribe to the visible
|
|
87
|
+
window only, not a million rows).
|
|
88
|
+
7. **Debounced input?** → **`useDebounced`**; **`useAsyncValidation`** for checks
|
|
89
|
+
that need the DB (uniqueness, cross-row) — live, over a query binding.
|
|
90
|
+
8. **A loading placeholder?** → **`useFormSkeleton`** / **`useTableSkeleton`** —
|
|
91
|
+
shaped like the REAL data, from the same Schema.
|
|
92
|
+
9. **Offline writes · undo · dry-run · "why is this value here?"** → **`useOutbox`**,
|
|
93
|
+
**`useUndoLog`**, **`usePreview`**, **`useProvenance`**.
|
|
94
|
+
10. **Global rpc-error handling?** → **`useOnRpcError`** + **`reportClientError`**.
|
|
95
|
+
|
|
96
|
+
| You're about to write | Reach for instead |
|
|
97
|
+
|---|---|
|
|
98
|
+
| per-field `useState` + `isSubmitting` + `resetForm()` | `useFormBinding` |
|
|
99
|
+
| shadcn `<Table>` + per-table sort/filter state | `useDataTable` |
|
|
100
|
+
| `FileReader` → base64 → action → `storage.put` | `useUpload` |
|
|
101
|
+
| `useMemo` fanning in 3+ subscriptions | `useDerived` |
|
|
102
|
+
| `setTimeout` debounce inside `useEffect` | `useDebounced` |
|
|
103
|
+
| `data === undefined ? <Skeleton/> : …` | `useFormSkeleton` / `useTableSkeleton` |
|
|
104
|
+
| a hand-kept page-scope check | `useCan` / `useResourceCan` |
|
|
105
|
+
| a hand-rolled presence roster / notification inbox / feature flag | `@voltro/plugin-presence` / `-notifications` / `-flags` |
|
|
106
|
+
|
|
107
|
+
Deep dive: **`schema-driven-ui.md`** (forms, tables, pickers, filters, skeletons),
|
|
108
|
+
**`reference.md`** (the complete hook list). If you are hand-writing more than ~20
|
|
109
|
+
lines of generic UI plumbing, stop and check those two first.
|
|
110
|
+
|
|
64
111
|
## Project layout
|
|
65
112
|
|
|
66
113
|
One OR MORE **projects** live under `apps/`. A project (`project.json`) is the
|
|
@@ -220,8 +267,22 @@ primitive → just save; the supervised dev loop respawns and regenerates.
|
|
|
220
267
|
But DO call `assertOwnTenant(input.tenantId, ctx.request.subject)` (from
|
|
221
268
|
`@voltro/plugin-multitenancy/guard`) in custom mutations that write raw rows,
|
|
222
269
|
and declare `error: TenantMismatch` — subscriptions are auto-scoped, writes are not.
|
|
270
|
+
- **Gate authorization declaratively with `guards:`.** Add
|
|
271
|
+
`guards: [{ scope: 'notes:write' }]` to `defineMutation`/`defineQuery`/`defineAction`
|
|
272
|
+
— the framework enforces it BEFORE the executor (before the txn opens), fails
|
|
273
|
+
with a typed `ScopeError` (auto-merged into the wire error union), and checks the
|
|
274
|
+
caller's EFFECTIVE scopes (raw ∪ rbac roles). Guards are browser-safe DATA (scope
|
|
275
|
+
strings + a pure `resource: (input) => id` extractor — never a server fn). Use the
|
|
276
|
+
in-handler `ctx.access.has(scope)` / `yield* ctx.access.require(scope)` (or rbac's
|
|
277
|
+
`permission()`) only for checks that need LOADED data (row ownership).
|
|
223
278
|
- **Don't store secrets in the schema or in `Subject`.** Declare env via
|
|
224
279
|
`defineEnv` (`configuration.md`); carry only ids in `Subject`.
|
|
280
|
+
- **In production, `voltro build` BEFORE `voltro serve`.** A production
|
|
281
|
+
(`NODE_ENV=production`) serve REQUIRES the precompiled serve bundle and fails
|
|
282
|
+
loud if it's missing — production never transpiles on demand. The generated
|
|
283
|
+
Dockerfiles already do `voltro build` then `voltro serve`; if you write your
|
|
284
|
+
own prod start, build first. (`voltro dev` + a non-prod local `serve` still use
|
|
285
|
+
tsx.) Depth: the deployment topic.
|
|
225
286
|
- **Don't copy prod data down unmasked.** `voltro data export` (and
|
|
226
287
|
`--target api`) reads REAL rows — PII included. Copying prod → dev/stage MUST
|
|
227
288
|
go through a masking profile (`--profile`; classify columns `.sensitive()` /
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"group": null,
|
|
36
36
|
"description": "The voltro CLI — every command, grouped by purpose, with the flags that actually matter.",
|
|
37
37
|
"path": "agent-docs/cli.md",
|
|
38
|
-
"files":
|
|
38
|
+
"files": 9
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
"id": "configuration",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"group": null,
|
|
171
171
|
"description": "Voltro's i18n layer (@voltro/i18n) — an opinionated wrap over react-intl, auto-wired from a single app.config.ts field, with cookie + Accept-Language locale resolution.",
|
|
172
172
|
"path": "agent-docs/internationalization.md",
|
|
173
|
-
"files":
|
|
173
|
+
"files": 4
|
|
174
174
|
},
|
|
175
175
|
{
|
|
176
176
|
"id": "introduction",
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
"group": null,
|
|
243
243
|
"description": "Project the typed descriptor graph into UI — forms, tables, pickers, and reactive components, all bound to a descriptor with near-zero glue.",
|
|
244
244
|
"path": "agent-docs/schema-driven-ui.md",
|
|
245
|
-
"files":
|
|
245
|
+
"files": 23
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
"id": "security",
|
|
@@ -305,7 +305,7 @@
|
|
|
305
305
|
"group": null,
|
|
306
306
|
"description": "Voltro's test story — the @voltro/testing package, the test pyramid (unit handlers/tools → workflow runner → e2e), and the voltro test / voltro e2e CLI commands.",
|
|
307
307
|
"path": "agent-docs/testing.md",
|
|
308
|
-
"files":
|
|
308
|
+
"files": 5
|
|
309
309
|
},
|
|
310
310
|
{
|
|
311
311
|
"id": "workflows",
|
|
@@ -1019,6 +1019,34 @@ It returns a `Stream<SeqEvent>` — each element is `{ seq, event }`. The descri
|
|
|
1019
1019
|
|
|
1020
1020
|
On the client, `useResumableAgentStream('app', 'support.run')` does the rest: it unwraps each `SeqEvent` (so `.events` are the plain inner events), tracks the highest `seq`, and on a transport drop BEFORE the run's terminal event it auto-reconnects with `fromSeq` = the last seq it rendered (exponential backoff; the no-progress cap resets whenever a reconnect delivers a new event, so a long flaky stream survives any number of well-spaced drops). The server replays past the cursor, then continues — one seamless stream.
|
|
1021
1021
|
|
|
1022
|
+
The hook's own surface is small:
|
|
1023
|
+
|
|
1024
|
+
```tsx
|
|
1025
|
+
import { useResumableAgentStream } from '@voltro/client'
|
|
1026
|
+
import type { AgentEvent } from '@voltro/ai/events'
|
|
1027
|
+
|
|
1028
|
+
const run = useResumableAgentStream<AgentEvent>('app', 'support.run')
|
|
1029
|
+
|
|
1030
|
+
// `input` MUST carry the resume key — `fromSeq` is injected by the hook.
|
|
1031
|
+
run.start({ streamId, message: prompt })
|
|
1032
|
+
run.cancel()
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
| Field | Meaning |
|
|
1036
|
+
|---|---|
|
|
1037
|
+
| `events` | The unwrapped inner events so far, in order and deduped by `seq`. |
|
|
1038
|
+
| `status` | `'idle'`, `'streaming'`, `'reconnecting'`, `'done'`, or `'error'`. |
|
|
1039
|
+
| `reconnects` | How many times the transport dropped and auto-reconnected this run. |
|
|
1040
|
+
| `error` | Set when `status === 'error'`. |
|
|
1041
|
+
| `start(input?)` | Begins a run, clearing previous events. |
|
|
1042
|
+
| `cancel()` | Stops the run and any pending reconnect. |
|
|
1043
|
+
|
|
1044
|
+
A third options argument tunes the reconnect policy: `maxReconnects` (default
|
|
1045
|
+
`6`), `backoffMs` (`400`), `maxBackoffMs` (`8000`), and `isTerminal` — which
|
|
1046
|
+
defaults to treating an `AgentEvent`-shaped `{ _tag: 'done' | 'error' }` as the
|
|
1047
|
+
end of the run. Override `isTerminal` when your element type signals completion
|
|
1048
|
+
some other way, or the hook will keep trying to resume a finished stream.
|
|
1049
|
+
|
|
1022
1050
|
For multi-node deployments use `dataStoreResumableStreamStore(ctx.store)` — it persists to the framework's own database (`streamEventsTable` + `streamStateTable`, register them in your `database/index.ts`) and elects exactly ONE producer per `streamId` via an atomic claim, so only one node runs the model while every node's consumers tail the shared log. Sweep finished streams with `gcResumableStreams(store, { olderThan })`.
|
|
1023
1051
|
|
|
1024
1052
|
For the fastest path, `redisResumableStreamStore(redis, { ttlSeconds })` backs the log with a Redis LIST (`RPUSH`/`LRANGE`) plus a `SET … NX` producer claim — TTL evicts finished/abandoned streams without a sweep. `@voltro/ai` takes no Redis dependency; you inject a tiny `ResumableRedis` client (five methods: `setNx` / `rpush` / `lrange` / `set` / `exists`) adapting ioredis / node-redis. All three backends satisfy the same `ResumableStreamStore` interface, so they swap without touching the producer/consumer code.
|
|
@@ -403,7 +403,7 @@ if (!canTrigger) throw new Unauthorised({})
|
|
|
403
403
|
```ts
|
|
404
404
|
import { permission } from '@voltro/plugin-rbac'
|
|
405
405
|
|
|
406
|
-
yield* permission(ctx, 'admin:full') // Effect<void,
|
|
406
|
+
yield* permission(ctx, 'admin:full') // Effect<void, ScopeError>
|
|
407
407
|
```
|
|
408
408
|
|
|
409
409
|
See [the RBAC plugin](/docs/plugins/rbac) for the full model.
|
|
@@ -1778,9 +1778,12 @@ the headline that builds on it.)
|
|
|
1778
1778
|
import { useResourceCan } from '@voltro/client'
|
|
1779
1779
|
|
|
1780
1780
|
const canEdit = useResourceCan('app', 'todos.can', { action: 'edit', resourceType: 'todo', resourceId: id })
|
|
1781
|
-
// canEdit.allowed: boolean
|
|
1781
|
+
// canEdit.allowed: boolean (false until the first verdict); canEdit.pending: boolean.
|
|
1782
1782
|
```
|
|
1783
1783
|
|
|
1784
|
+
Full API — including the batch `useResourceCans` for per-row gating — in
|
|
1785
|
+
[usePermissions](/docs/ui/client-utilities/use-permissions).
|
|
1786
|
+
|
|
1784
1787
|
## Capability map
|
|
1785
1788
|
|
|
1786
1789
|
`rebacPolicyGraph()` returns every resource type, its actions, the relations each
|
|
@@ -25,6 +25,7 @@ The dispatcher routes `voltro <command> [args]` to the matching subcommand and p
|
|
|
25
25
|
| [Build & run](/docs/cli/build-and-start) | `build`, `start`, `serve` |
|
|
26
26
|
| Deploy | `deploy` (`plan` — auto-detect the target tier per app + function), [`serverless`](/docs/deployment/serverless-functions) (`list` / `dev` / `serve` / `build` / `deploy`), [`static`](/docs/deployment/static-sites) (`hosts` / `deploy`) |
|
|
27
27
|
| [Database](/docs/cli/migrate) | `migrate`, `db` (`plan` / `apply` / `plans` / `drift` / `squash` / `restore-snapshot` / `migrate` / `rollback` / `status` / `seed`) |
|
|
28
|
+
| [Update](/docs/cli/update) | `update` (`--to` / `--dry-run` / `--force` / `--exact`) — bump every `@voltro/*`, install, run the codemods that adapt your source to the new version |
|
|
28
29
|
| [Data transfer](/docs/cli/data) | `data` (`export` / `import` / `unpack` / `inspect` / `backup` / `restore`) — directory + single-file `.vbundle` bundles, streaming assets, masking, at-rest encryption |
|
|
29
30
|
| Ops / infra | `cache` (`status` / `flush` / `invalidate`), `add` (`redis`), `baseline` (`list` / `status` / `set`), `schedule-manifest`, [`storage`](/docs/plugins/storage) (`doctor` / `cors`) |
|
|
30
31
|
| AI / data | `embeddings backfill <table> --text <field> --vector <field>` — (re)embed rows the `vectorEmbedding()` mixin missed (pre-existing rows / a model change); `--dry-run` to preview |
|
|
@@ -149,6 +150,7 @@ The HTTP surface is reachable directly too — e.g. `curl -s localhost:4000/_vol
|
|
|
149
150
|
- [Dev](/docs/cli/dev) — what happens during `voltro dev`
|
|
150
151
|
- [Build & start](/docs/cli/build-and-start) — production paths
|
|
151
152
|
- [Migrate](/docs/cli/migrate) — schema changes end-to-end
|
|
153
|
+
- [Update](/docs/cli/update) — upgrade the framework + run codemods
|
|
152
154
|
- [Inspect & test](/docs/cli/inspect) — debugging + harness
|
|
153
155
|
|
|
154
156
|
|
|
@@ -720,6 +722,23 @@ For WebSocket connections to land on the same backend (required for in-process s
|
|
|
720
722
|
- Reverse proxy: `lb_policy ip_hash` (Caddy) / `ip_hash` (nginx).
|
|
721
723
|
- Or use `@voltro/plugin-cluster` to share subscription state across instances → any-load-balancer-works.
|
|
722
724
|
|
|
725
|
+
## `voltro doctor` — preflight a production serve
|
|
726
|
+
|
|
727
|
+
Production `voltro serve` for an **API** app boots ONLY from the precompiled serve
|
|
728
|
+
bundle and is **fatal if it's missing** — a hand-rolled Dockerfile that runs
|
|
729
|
+
`voltro serve` without a prior `voltro build` breaks at deploy. `voltro doctor`
|
|
730
|
+
(or `voltro serve --preflight`) catches that at BUILD time instead of cold-start:
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
voltro doctor . # check the serve bundle exists; print the fix if not
|
|
734
|
+
voltro serve --preflight . # same check, then exit — never boots
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
It exits **1** when the serve bundle is missing on an API app (so it fails a CI /
|
|
738
|
+
Docker step) and prints the exact remedy: add a `voltro build .` step before
|
|
739
|
+
`voltro serve .`. Drop it into your image build right after `voltro build` to
|
|
740
|
+
guarantee the artefact is present before the image ships.
|
|
741
|
+
|
|
723
742
|
## Anti-patterns
|
|
724
743
|
|
|
725
744
|
- **Running `voltro start` against a directory without `dist/`.** It exits 1 with a clear `no built dist found — run voltro build first` (checked against `.framework/dist/index.html` before any heavy work). Run `voltro build` first.
|
|
@@ -1648,3 +1667,82 @@ The manifest is read through a TTL-cached source (~10 seconds): a procedure you
|
|
|
1648
1667
|
## Protocol scope
|
|
1649
1668
|
|
|
1650
1669
|
MCP over JSON-RPC 2.0. `initialize` negotiates the protocol revision (`2025-06-18`, `2025-03-26`, `2024-11-05`) and advertises the `tools`, `resources`, and `prompts` capabilities; methods are `ping`, `tools/list`, `tools/call`, `resources/list`, `resources/read`, `prompts/list`, `prompts/get`. The stdio bin frames this as newline-delimited JSON-RPC; the HTTP bin serves it over Streamable HTTP. Both transports route to the same pure protocol core (`handleMcpRequest`, `callTool`, `listResources`/`readResource`, `listPrompts`/`getPrompt`, `routeHttp`), all exported from `@voltro/mcp`.
|
|
1670
|
+
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
---
|
|
1674
|
+
|
|
1675
|
+
<!-- source: en/cli/update.md -->
|
|
1676
|
+
## Update
|
|
1677
|
+
|
|
1678
|
+
_voltro update — bump the framework to the latest version and run the codemods that adapt your source to any changed APIs._
|
|
1679
|
+
|
|
1680
|
+
`voltro update` upgrades an app to the latest framework release. It does three things in order:
|
|
1681
|
+
|
|
1682
|
+
1. **Bump** every `@voltro/*` dependency in `package.json` to the target version.
|
|
1683
|
+
2. **Install** with your package manager (detected from the lockfile — pnpm / npm / yarn / bun).
|
|
1684
|
+
3. **Run the codemods** shipped with the target version — automatic source rewrites for any breaking API change, plus printed manual steps for anything that can't be automated.
|
|
1685
|
+
|
|
1686
|
+
```bash
|
|
1687
|
+
voltro update # bump to the latest published version, install, run codemods
|
|
1688
|
+
voltro update --to 0.4.0 # pin an explicit target version
|
|
1689
|
+
voltro update --dry-run # preview the bump + which codemods would run — writes nothing
|
|
1690
|
+
voltro update --force # allow a dirty working tree (not recommended)
|
|
1691
|
+
voltro update --exact # pin exact versions (drop the ^ / ~ range prefix)
|
|
1692
|
+
|
|
1693
|
+
# Recover the codemods after a MANUAL version bump (no bump, no install):
|
|
1694
|
+
voltro update --codemods-only --from 0.3.0 # re-run codemods 0.3.0 → installed
|
|
1695
|
+
voltro update --codemods-only --from 0.3.0 --to 0.4.0 # explicit delta
|
|
1696
|
+
```
|
|
1697
|
+
|
|
1698
|
+
## Already bumped by hand? Recover the codemods
|
|
1699
|
+
|
|
1700
|
+
If you bump `@voltro/*` versions in `package.json` yourself and install first, a
|
|
1701
|
+
plain `voltro update` sees the installed version already equals the target and
|
|
1702
|
+
reports **"already on X — nothing to do"** — skipping the codemods AND the
|
|
1703
|
+
printed manual steps for the delta you actually crossed. To re-apply them without
|
|
1704
|
+
touching `package.json` again:
|
|
1705
|
+
|
|
1706
|
+
```bash
|
|
1707
|
+
voltro update --codemods-only --from <version-you-came-from>
|
|
1708
|
+
```
|
|
1709
|
+
|
|
1710
|
+
`--codemods-only` (alias `--run-codemods`) runs the codemods + manual notes for
|
|
1711
|
+
`[from, to]` against the already-installed tree — no version bump, no install.
|
|
1712
|
+
`--to` defaults to the installed version; pass it to pin an explicit delta.
|
|
1713
|
+
`--from` also works on a normal `voltro update` to override the auto-detected
|
|
1714
|
+
source version.
|
|
1715
|
+
|
|
1716
|
+
## The clean-tree guard
|
|
1717
|
+
|
|
1718
|
+
Codemods **rewrite your source**, so you need a clean diff to review afterwards. `voltro update` refuses to run on a dirty git working tree — commit or stash first. Use `--dry-run` to preview without touching anything, or `--force` to override the guard (you accept a mixed diff).
|
|
1719
|
+
|
|
1720
|
+
## What gets bumped
|
|
1721
|
+
|
|
1722
|
+
Every `@voltro/*` entry in `dependencies` and `devDependencies`, with the range style preserved (`^0.3.0` stays caret, `~0.3.0` stays tilde) unless you pass `--exact`. Non-registry specs (`workspace:*`, `catalog:`, `link:`, …) are left untouched — they're already resolved by your monorepo or catalog.
|
|
1723
|
+
|
|
1724
|
+
## Codemods
|
|
1725
|
+
|
|
1726
|
+
Each breaking public-API change in a release ships a **codemod**. When you update across that release, `voltro update` applies it:
|
|
1727
|
+
|
|
1728
|
+
- A **transform codemod** rewrites your source automatically — renamed imports, moved modules, changed component props, restructured call signatures. The rewrite is scoped to files that actually import the affected symbol.
|
|
1729
|
+
- A **manual codemod** prints written steps during the update, only when your app is affected — for changes that can't be mechanically transformed (a behavior change, a descriptor/executor restructure). Where the affected sites can be found but the fix needs your judgment, a codemod inserts `// TODO(voltro-migration): …` markers so you can locate every spot.
|
|
1730
|
+
|
|
1731
|
+
Codemods that span multiple versions run in order (e.g. upgrading `0.2.0 → 0.4.0` runs the `0.3.0` and `0.4.0` codemods in sequence). Review the resulting diff before committing.
|
|
1732
|
+
|
|
1733
|
+
## The database is separate
|
|
1734
|
+
|
|
1735
|
+
`voltro update` does **not** touch your database. Framework-owned `_voltro_*` tables (workflow runs, schedules, …) are reconciled by the declarative differ, not by codemods: when a release changes one of those tables, your next `voltro db apply` (or `voltro dev` boot, which auto-applies) picks up the change. After an update:
|
|
1736
|
+
|
|
1737
|
+
```bash
|
|
1738
|
+
voltro update
|
|
1739
|
+
voltro db apply # reconcile any changed framework tables — NOT voltro db migrate
|
|
1740
|
+
# then run your typecheck to confirm your code compiles against the new API
|
|
1741
|
+
```
|
|
1742
|
+
|
|
1743
|
+
Use `voltro db apply` (the declarative diff), not `voltro db migrate` (the imperative file-runner) — only the former reconciles framework tables.
|
|
1744
|
+
|
|
1745
|
+
## Where to read next
|
|
1746
|
+
|
|
1747
|
+
- [Migrate](/docs/cli/migrate) — schema changes end-to-end
|
|
1748
|
+
- [Build & start](/docs/cli/build-and-start) — production paths
|