@voltro/cli 0.35.0 → 0.37.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 +158 -0
- package/dist/apiBuild-D0f6hYbH.js +2 -0
- package/dist/{apiBuild-DYD_ONLD.js → apiBuild-DGsShRuF.js} +1 -1
- package/dist/{baselineCommand-DIttzO8A.js → baselineCommand-C6NMt-oa.js} +1 -4
- package/dist/baselineCommand-Cfw2Afwm.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/{build-CD8K4XOr.js → build-CiFA-E19.js} +7 -7
- package/dist/{checkCommand-DKpDLlqu.js → checkCommand-CIPYyNyc.js} +1 -1
- package/dist/{checkCommand-Ct9xkTrS.js → checkCommand-CLPTzYqM.js} +3 -3
- package/dist/{cloudCmd-Cvv5HGaZ.js → cloudCmd-NQSwe_Qk.js} +1 -1
- package/dist/{codegen-CYM3Zqrf.js → codegen-GYYcdpCg.js} +2 -2
- package/dist/codegen-uYrrDzQv.js +2 -0
- package/dist/{codegenCommand-C4YoQIc2.js → codegenCommand-BxS3sCeJ.js} +2 -2
- package/dist/{codemodRunner-BnFq3Fgu.js → codemodRunner-cyqu5oxr.js} +122 -46
- package/dist/{commands-BE8E7zF3.js → commands-BwG9A9Vv.js} +73 -72
- package/dist/{dashboardCommand-D7SgZGaN.js → dashboardCommand-C7U146xQ.js} +1 -1
- package/dist/{dataCommand-BhYwDgg-.js → dataCommand-BOwd8_aJ.js} +2 -2
- package/dist/{dbCommand-O8HA63s2.js → dbCommand-C4UW8cVN.js} +353 -351
- package/dist/dbCommand-CC-qNzpz.js +2 -0
- package/dist/{dev-D2BikO7a.js → dev-BV6XNYKf.js} +1 -1
- package/dist/{dev-C7sFZq3m.js → dev-bwDgSObH.js} +2000 -2000
- package/dist/{doctorCommand-DnimF5IM.js → doctorCommand-BXyrtN2l.js} +9 -9
- package/dist/doctorCommand-C4L61G_L.js +2 -0
- package/dist/{dormancyCommand-QewYug_s.js → dormancyCommand-BDdRRLsa.js} +1 -1
- package/dist/{embeddingsCommand-BfiLS_QI.js → embeddingsCommand-CjWGcAE6.js} +1 -1
- package/dist/{envCommand-CCGPRQY1.js → envCommand-B_TeG297.js} +1 -1
- package/dist/{evolveCommand-CHsLCtDf.js → evolveCommand-D6N-WfA4.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{infoCommand-DwOgK1t6.js → infoCommand-BHKTprrx.js} +1 -1
- package/dist/inspectGateHint-BF6608UT.js +4 -0
- package/dist/{metaCommands-7MJfZ5cf.js → metaCommands-CIoQNlaQ.js} +1 -1
- package/dist/{migrate-BV7I-ZHZ.js → migrate-RwgUWXfc.js} +1 -1
- package/dist/{privacyCommand-C-Df56U_.js → privacyCommand-XejDMvmu.js} +4 -3
- package/dist/{probeCommand-CZfaaUOZ.js → probeCommand-BPEpwT32.js} +40 -39
- package/dist/{projectScaffold-DmzEKHib.js → projectScaffold-CxgtJvlb.js} +1 -1
- package/dist/{projectScaffold-LMMtaavR.js → projectScaffold-DDgWbNLe.js} +2 -2
- package/dist/{runtimeTrace-CH3eUiMw.js → runtimeTrace-WYV7qYvz.js} +1 -1
- package/dist/{sdkgen-BLkvGRfX.js → sdkgen-TM5sSdf7.js} +1 -1
- package/dist/{serveCommand-iwlUBNS1.js → serveCommand-CyZsXL1l.js} +487 -487
- package/dist/serveCommand-qumm7jdF.js +2 -0
- package/dist/serveEntry.js +1 -1
- package/dist/subcommandNames-CKG5Dz3a.js +27 -0
- package/dist/{templates-De8IR5-c.js → templates-BR-fb4SP.js} +1 -4
- package/dist/{test-CI6iDsYc.js → test-DIQ0jlkQ.js} +1 -1
- package/dist/{updateCommand-x0pI_x-B.js → updateCommand-9GHHsmDf.js} +1 -1
- package/dist/updateCommand-BN0tkl9-.js +2 -0
- package/dist/{webhooksCommand-DlAgS2Iw.js → webhooksCommand-CHWSPk5z.js} +1 -1
- package/package.json +29 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +38 -0
- package/templates/agent-docs/data.md +30 -0
- package/templates/agent-docs/database/misc.md +33 -0
- package/templates/agent-docs/plugins/audit.md +46 -0
- package/templates/agent-docs/whats-new.md +44 -121
- 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-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +10 -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-saas/package.json +11 -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-versioning/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-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-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 +3 -3
- package/dist/apiBuild-B8aoJvuw.js +0 -2
- package/dist/baselineCommand-C2ClWZN3.js +0 -2
- package/dist/codegen-ChBi_hVa.js +0 -2
- package/dist/dbCommand-DS4b97Is.js +0 -2
- package/dist/doctorCommand-CM4Ch9C7.js +0 -2
- package/dist/serveCommand-CbDHU6l-.js +0 -2
- package/dist/updateCommand-BlMZhWgO.js +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# What's new in 0.
|
|
1
|
+
# What's new in 0.37.0
|
|
2
2
|
|
|
3
3
|
Read this FIRST when a task touches an area you have not worked in recently.
|
|
4
4
|
It is the cheapest way to notice that the framework grew the thing you were
|
|
@@ -9,163 +9,86 @@ BREAKING entries name a codemod; run `voltro update` to apply it.
|
|
|
9
9
|
|
|
10
10
|
### ⚠ BREAKING
|
|
11
11
|
|
|
12
|
-
- **@voltro/
|
|
13
|
-
- **@voltro/cli** — The declared framework table set no longer reads a runtime flag. `CDC`, `VOLTRO_UNDO` and `VOLTRO_TRACING_PERSIST` each moved it before this release; `app.config.ts` gained `schema: { traces?, undo? }` to declare the two that still need a decision.
|
|
12
|
+
- **@voltro/protocol, @voltro/runtime, @voltro/cli** — A field a procedure's input schema does not declare now REJECTS the call. It used to be discarded and the call ran with what was left.
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
The measurement, from a consumer's root layout:
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
The three got two different answers, deliberately. `CDC` left the derivation entirely: `_voltro_cdc_offsets` follows the DIALECT now, so a mariadb or mssql app declares it whether or not that process drives CDC. The cost is one empty offsets table and it is the same trade `impliesScheduleTables` already makes in writing. `VOLTRO_UNDO` and `VOLTRO_TRACING_PERSIST` could not simply be dropped — both can legitimately turn a table on in production, and a declared set that ignored them would leave capture writing to a table nobody created — so they keep their runtime meaning and lose their declaring power. Turning capture OFF still needs no declaration and never will; turning it ON without one is refused at boot, on both boot paths, with the config field named.
|
|
22
|
-
|
|
23
|
-
The `prod-mismatch` refusal also stopped being two hashes and a command. It prints which of the three decided tables THIS process declared and from which input, because the ledger stores no table set to diff against and the command it used to recommend was the one the operator had just run successfully. That half matters beyond the framework's own tables: a plugin's `extendSchema.tables` is app code and can read anything, so the rule above cannot be enforced for it.
|
|
24
|
-
|
|
25
|
-
**`voltro update` carries you across this** — codemod `0.35.0/05_declared-schema-drops-runtime-flags`.
|
|
26
|
-
- **@voltro/protocol, @voltro/runtime, @voltro/cli, @voltro/devtools-ui** — A declared event must decide who may listen — the boot gate now covers `defineEvent`, closing SEC-1's sibling. `defineEvent`'s `guards:` was optional and `bindEvent` skipped an empty list, so under `security.defaultDeny` an event with NO access declaration was silently subscribable by anyone who could open the socket, while the identical shape was already refused for every procedure.
|
|
27
|
-
|
|
28
|
-
`defineEvent` now accepts `openAccess: '<reason>'` — mutually exclusive with `guards:`, reason string required — exactly as the four procedure definers do. The erased `{ open }` decision rides the same `guards` array every enforcement path reads; `bindEvent` treats it as "no check" (an open event pays what an unguarded one pays: nothing), and `eventToRpc` no longer unions `ScopeError` into the wire contract for an event that cannot produce a denial.
|
|
29
|
-
|
|
30
|
-
**Breaking for `security.defaultDeny` apps (the default):** an app with a `*.event.ts` declaring neither `guards:` nor `openAccess:` now refuses to boot under `voltro dev` and `voltro serve`, naming every undecided event — the same message, from the same gate, procedures get. `voltro doctor` lists the same set. Migration: give each event a decision (`guards: [{ scope: '…' }]` or `openAccess: '<why anyone may listen>'`); an app that wants the old default-allow declares `security: { defaultDeny: false }` once, in `app.config.ts`. Plugin-declared events are not judged — the gate reads the app's own discovered files only.
|
|
16
|
+
```ts
|
|
17
|
+
query?.('userSettings.list', { employeeId })
|
|
18
|
+
```
|
|
31
19
|
|
|
32
|
-
|
|
20
|
+
That procedure declares `userId` / `userIdIn`. Effect's default `onExcessProperty: 'ignore'` decoded the payload to `{}` — not reasoned, measured:
|
|
33
21
|
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
```ts
|
|
23
|
+
decodeUnknownSync(Struct({ userId: optional(String) }))({ employeeId: 'e' }) // → {}
|
|
24
|
+
```
|
|
36
25
|
|
|
37
|
-
|
|
26
|
+
An empty input to a LIST query is not a narrower filter, it is the ABSENCE of one. Their admin, signed in as `2d0add2c…`, was served the settings row of `4410c2f8…` — another user's language and theme in the first paint, with nothing in any log to say so.
|
|
38
27
|
|
|
39
|
-
|
|
28
|
+
**Why refuse rather than warn.** The decoder cannot tell a projection field from a FILTER field, and that asymmetry is the whole risk: dropping an unknown `include` costs a caller some data, dropping an unknown `tenantId` hands them somebody else's. Nothing at decode time distinguishes the two, so the safe direction is the only one available — the same fail-closed reasoning as the row filter's refusal, one layer up. A warning would have to be read by someone, in a log, after the wrong rows were already served.
|
|
40
29
|
|
|
41
|
-
The
|
|
30
|
+
The typed loader query that shipped in 0.36.0 closes the same hole for callers we compile. This closes it for the ones we do not: a plain `fetch`, a curl, a still-cached bundle after a field rename, and every untyped caller.
|
|
42
31
|
|
|
43
|
-
|
|
32
|
+
Three things measured rather than assumed, because none follows from the annotation's name: it propagates into NESTED structs, through every member of a UNION, and leaves a non-struct payload (`Schema.Void`, a scalar) alone.
|
|
44
33
|
|
|
45
|
-
**`
|
|
34
|
+
**`Schema.Struct({})` needed a filter, and only a real process showed it.** The fixture's `notes.list` declares an empty input; `POST /rpc` with `{ employeeId }` came back `200` with a snapshot, which for twenty minutes read as the whole change having failed. An empty `TypeLiteral` has no property signatures, so Effect has no expected key set for a key to be excess OF — self-consistent, and the wrong answer here, because `input: Schema.Struct({})` is the STRONGEST declaration a procedure can make and it was the one shape that accepted everything. It gets an explicit predicate now; `Schema.Record` keeps its open key set, because there the openness is declared.
|
|
46
35
|
|
|
47
|
-
|
|
36
|
+
**Verified against a running `voltro serve`, not only in units.** A declared input succeeds and inserts its row; an undeclared field is refused naming the key and the accepted set. The refusal arrives on the channel a payload decode failure ALREADY used — a missing required field produces the same `Die` with a `ParseError` message — so this adds no new error shape for a client to handle, it moves one case onto the channel the sibling case was always on.
|
|
48
37
|
|
|
49
|
-
|
|
38
|
+
`strictInput` lives in one module and every `Rpc.make` payload in `@voltro/protocol` goes through it — query, mutation, action, stream, event, plus the workflow start on both the server lifter and the browser-loaded rpc group. `strictInput.test.ts` asserts that SET by scanning the source, not the five lifters somebody remembered: a rule applied at the sites you can list is the shape that let `bootStoreCodec` be fixed twice and break a third time.
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
- **@voltro/protocol, @voltro/runtime, @voltro/cli, @voltro/ai, @voltro/plugin-billing, @voltro/plugin-flags, @voltro/plugin-governance, @voltro/plugin-notifications, @voltro/plugin-presence, @voltro/plugin-storage** — Every first-party plugin rpc route now declares an access decision (`guards:` or `openAccess: '<reason>'`), and `security.defaultDeny` is enforced in the DISPATCH spine as defense in depth behind the boot gate: a descriptor that reaches the wire with no decision (a third-party plugin route, an embedder's hand-bound descriptor) is refused per-request with a typed `ScopeError` before the transaction / external I/O. Twelve previously-open routes now require a scope: `billing.startCheckout` / `portalUrl` / `previewChange` / `changePlan` / `changeSeats` / `invoices` → `billing:manage`; `billing.reportUsage` → `billing:report`; `governance.export` / `erase` → `admin:full` (already enforced in-handler, now declared); `storage.mintUploadUrl` / `ingestUrl` → `storage:manage`; `storage.listRefs` → `storage:browse`. Migration: grant the scope to the role/subjects that legitimately hold each capability (rbac role, `resolveScopes`, api-key scopes) — the codemod lists every route and the open-by-design surfaces that did NOT change. `PluginRpcRoute` gains `guards`/`openAccess` fields, carried through the route lift into the enforced descriptor; the synthesized agent/undo/connections built-ins declare `openAccess` so they keep serving under default-deny.
|
|
40
|
+
**`voltro update` carries you across this** — codemod `0.37.0/01_procedure-input-rejects-undeclared-fields`, a written note. A transform would have to guess which declared field a stray one meant, which is the same guess that produced the defect.
|
|
53
41
|
|
|
54
42
|
### Added
|
|
55
43
|
|
|
56
|
-
- **@voltro/
|
|
57
|
-
- **@voltro/data-transfer, @voltro/sql-postgres, @voltro/cli** — The logical importer bulk-loads postgres targets via `COPY … FROM STDIN` (PERF-13). `voltro data import` engages it automatically wherever plain-INSERT semantics provably hold — `--mode replace`, or the default `upsert` into a table that is empty at import time (the fresh-target shape of every cross-dialect migration) — and never under `--atomic`. A refused COPY batch is atomic (nothing landed), so the importer replays exactly that batch through the per-row path with held-row / deferred-FK semantics intact. MEASURED on a 7-column table (text/int/bool/jsonb/timestamptz), 50 000 rows, local postgres: row-by-row 12.8 s (~3.9 k rows/s) vs COPY 0.59 s (~84.6 k rows/s) — **21.7×**. New seams: `ImportOptions.copyLoader` / `copyBatchSize` (default 5000) in `@voltro/data-transfer`, and `makePgCopySession` / `encodeCopyRow` in `@voltro/sql-postgres` (a submittable CopyIn query over the existing `pg` driver — no new dependency). Other dialects keep the per-row writes.
|
|
58
|
-
- **@voltro/cli** — `voltro probe access` asks a RUNNING app whether its declared access is actually enforced — the question none of the existing checks ask.
|
|
59
|
-
|
|
60
|
-
`voltro check`, the boot access gate and `security.defaultDeny` all verify that a decision was DECLARED. None of them verifies that the declaration REFUSES anyone. That distinction is not hypothetical here: the dispatch spine and the boot gate were separate for several releases, a procedure filtered out of the rpc group while still bound in the handler map served silently on one path and crashed the other, and `check` itself counted a decided-open route as guarded. Every one was the declaration and the behaviour disagreeing, found by reading rather than by asking.
|
|
61
|
-
|
|
62
|
-
It calls every guarded procedure with NO credentials and reports three verdicts: `refused` (enforcement works), `admitted` (the finding), and `inconclusive` — the call failed for a reason that is not an access refusal, usually payload validation running before the guard. `inconclusive` is never counted as a pass; `--strict` fails on it, which is what CI wants.
|
|
63
|
-
|
|
64
|
-
It probes ANONYMOUSLY on purpose. That is strictly weaker than scope-by-scope differentiation and strictly safer: the alternative puts credential minting into a command that can be pointed at production. Procedures declared `openAccess:` are skipped — probing them would report every deliberately-public route as a finding and bury the real ones, which is the same signal-to-noise failure `kind: 'open'` was added to the wire to fix.
|
|
65
|
-
|
|
66
|
-
`fetchJson` gained an explicit `anonymous` option for this one caller; it is an opt-out, never a default, and both directions are pinned by a test — a bearer attached here would make every result meaningless while still printing green.
|
|
67
|
-
|
|
68
|
-
**Validated against a live app, and it took two corrections to get there.** The first version sent a readable request id, which the transport converts with `BigInt(id)` — so every probe came back as a Defect before any guard ran, and every app looked broken. The second read a top-level `_tag` off an object while `POST /rpc` answers an ARRAY of envelopes, so a correctly-refused call scored as `admitted`. Both versions had a green unit suite, because the fixtures asserted the shape the code assumed. The fixtures are now copied from a real transcript.
|
|
69
|
-
- **@voltro/cli, @voltro/runtime** — `app.config.ts` gained `reactive: { deliveryConcurrency, rawReadTrackingLimit }` — the delivery-loop tunables were env-only, which left a number the framework picks on the project's behalf undeclarable in the one file that carries every other tunable.
|
|
44
|
+
- **@voltro/plugin-audit** — `redactInput` / `redactOutcome` gained `'shape'`, and `redactSubject` gained `'metadata-shape'`: the payload's STRUCTURE survives, no value from it.
|
|
70
45
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
The primitives have existed for two rounds (`setResidencyConfig`, `residentPlacement`, `bindResidentStore`), exported and tested, with **zero callers**. A user could reach them, but nothing in the framework did: there was no way to declare residency and no request ever consulted it. That gap was pinned by a test walking every workspace source, which went red on this change and asked for the module header to be corrected — it now names its consumers instead of asserting it has none, so a SECOND unreviewed caller still fails.
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
tenancy: {
|
|
78
|
-
isolation: 'namespace',
|
|
79
|
-
residency: {
|
|
80
|
-
servableRegions: ['eu-west'],
|
|
81
|
-
regionUrlEnv: { 'eu-west': 'DB_URL_EU', 'us-east': 'DB_URL_US' },
|
|
82
|
-
homes: [{ tenantId: 'acme', region: 'eu-west' }],
|
|
83
|
-
},
|
|
84
|
-
}
|
|
46
|
+
```json
|
|
47
|
+
{ "__redacted": { "jiraToken": "string(113)", "attempts": "number" } }
|
|
85
48
|
```
|
|
86
49
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
**Every failure is a refusal, never a fallback**, because a residency system that degrades to a default store violates residency at exactly the moment something is misconfigured. Unresolvable tenant, unmapped home, or a home region this deployment does not serve are all typed refusals; the last one names the region so a gateway can route it.
|
|
90
|
-
|
|
91
|
-
Four declarations are refused at BOOT rather than warned about: residency without `isolation: 'namespace'` (the region keeps regions apart, the namespace keeps tenants apart — one without the other is not isolation), a servable region with no env-var name, one whose env var is unset, and a tenant mapped to two regions.
|
|
92
|
-
|
|
93
|
-
Two boundaries worth knowing:
|
|
94
|
-
|
|
95
|
-
- `ctx.storeForTenant(id)` resolves residency for THAT tenant, not the caller's, so a handler acting on another tenant reaches that tenant's region or is refused. Background work (schedules, workflows) runs with no tenant and must use it — `ctx.store` there is the primary store. - A transaction is never re-routed. A caller-supplied store is used as given; it already went through residency to exist, and moving writes off the connection holding the lock is a worse failure than the one residency prevents.
|
|
96
|
-
|
|
97
|
-
Homes resolve once at boot (an array, or a function reading your own table), so adding a tenant home needs a restart — chosen over a cache with a staleness window on a decision whose whole value is that it is never wrong.
|
|
98
|
-
- **@voltro/runtime, @voltro/cli** — `voltro schedule backfill <name> --from <iso> --to <iso> [--yes] [--limit N]` and `POST /_voltro/inspect/schedules/:name/backfill` (WF-14) — fire every cron occurrence of a schedule over an explicit range, sequentially, each recorded against its own cron-derived `scheduledAt` with `trigger: 'manual'`. Fills the gap boot backfill (walks from the last recorded run only) and cluster-cron catch-up (capped at one day) leave open. Bounded and confirmable: above 25 occurrences it refuses without `--yes` (printing the count), above the per-request cap (default 1 000, `--limit` up to a hard ceiling of 10 000) it refuses outright, firing nothing — never a silent prefix. Wired on both boot paths through one shared hook.
|
|
99
|
-
- **@voltro/devtools-ui, @voltro/plugin-search** — The Search dashboard panel now RENDERS the drift surface REL-1 shipped server-side and no dashboard showed (the additive-JSON silent-drift shape the 4-layer rule exists for): per-index `dropped` / `pendingDrift` / `drifted` / last-drift badges, the repair queue itself (`GET /drift` — oldest first, with attempt counts and the engine's last error), and a **Resync now** action (`POST /resync`) gated on the new `canResyncSearch` capability (its own flag — a resync re-reads only the drifted rows; a reindex re-reads the whole table). Landed across all four layers in one change set: shared `SearchPage` + wire types + capability + EN/DE strings here; HTTP fetchers + page wiring in voltro-devtools; tenant-scoped `apps.inspectSearchDrift` / `apps.inspectSearchResync` proxies + hooks + page wiring in voltro-cloud (the indexes proxy schema carries the new fields as OPTIONAL, so a customer app from before the drift ledger still decodes). `search.query` also now carries an explicit access decision (`openAccess`, with the tenant-scoping rationale in source) instead of the undecided SEC-1 shape.
|
|
100
|
-
- **@voltro/cli** — Workflow wakes over the change stream (WF-8): on a fleet where remote changes reach the change spine (Postgres LISTEN/NOTIFY CDC — the common broker-less multi-replica deployment), a remote replica's `signal-sent` event, start context, or run transition now triggers an immediate, coalesced `pollStorage` on every replica, so cross-replica signal/step latency stops being bounded by the 10 s storage poll. Honest subset by design: the cluster engine has no per-run wake seam, so the change event wakes the poll early rather than replacing it — the poll tick stays the safety net. Local-origin changes never wake (a replica waking on its own recorder rows would be a poll storm). Wired by the same `makeWorkflowWake` builder on both boot paths.
|
|
101
|
-
- **@voltro/runtime, @voltro/workflow, @voltro/cli** — `ctx.workflows.start(name, payload, { at: Date })` — delayed one-off starts (WF-13). The start is parked as a durable `_voltro_workflow_pending` row (`mode: 'delayed'`) and fired by the coordinated drainer when `at` arrives, so it survives restarts and fires on whichever replica drains. At `at` it becomes an ordinary ARRIVAL: declared flow control (debounce, singleton, rateLimit, …) judges it as of that moment — `at` never bypasses a control. The handle reports `status: 'queued'` with `deferral: { mode: 'delayed', dueAt }`. An `at` in the past starts immediately; `{ at, wait: true }` is refused. `at` is an absolute instant by design (no `delay` spelling): a delay is ambiguous about its epoch and every queue system answers it differently, while an instant composes with the schedule/backfill surfaces.
|
|
102
|
-
- **@voltro/workflow, @voltro/cli** — `workflows: { recording: 'coarse' }` in `app.config.ts` (env override `VOLTRO_WORKFLOW_RECORDING`) — turns off the two fire-and-forget per-step writes to `_voltro_workflow_run_steps` (WF-10) for hot high-step workflows. Run rows, run events (signals/timers/cancels/stall reports) and the cluster engine's durable journal are unaffected — replay and redrive work exactly as before; the cost is an empty step timeline for runs recorded under coarse. Measured before it was built (`packages/cli/scripts/admission-throughput.mjs`): the recorder costs exactly 2 store writes per step, off the step's critical path — which is why the knob is a skip, not a batcher.
|
|
103
|
-
- **@voltro/workflow, @voltro/cli** — `workflow({ schedule })` — the workflow-side cron declaration (WF-12), with Temporal Schedules' overlap vocabulary about the RUN: `onOverlap: 'skip' | 'buffer' | 'cancelOther'`. Pure sugar over the shipped scheduler: at boot it lowers into a real schedule named `workflow:<name>` (same coordinated claims, run rows, Schedules panel, `voltro schedule` verbs). The synthesised firing awaits the workflow run to completion, which is what makes skip/buffer bind on the run's duration; `cancelOther` cancels only the still-running run this schedule itself started. The firing watchdog (`schedule.maxRuntime`) defaults to 24 h here. Cron and timezone are validated at definition time.
|
|
50
|
+
Requested by the consumer who had asked for the redaction one round earlier, and both requests were right. They spent a day on a bug their own audit trail could have ended in seconds — a value arrived as 113 characters where 44 were due, and the row that would have said so read `{"__redacted":"all"}`. `'all'` remains the default on every field; this is opt-in.
|
|
104
51
|
|
|
105
|
-
|
|
52
|
+
The rules, and the two that are decisions rather than details:
|
|
106
53
|
|
|
107
|
-
-
|
|
108
|
-
- **@voltro/database, @voltro/cli, @voltro/workflow** — The migration advisory lock is now scoped to the configured schema (`DB_SCHEMA`) instead of one framework-wide constant. A postgres advisory lock is database-scoped and MySQL `GET_LOCK` is server-wide, so two apps sharing one database in different schemas used to serialize each other's migrations and defer each other's boot-time trigger repair — with a log line blaming "another instance". Now: postgres derives a stable 64-bit key from the schema name (FNV-1a 64 of `voltro_migration_lock:<schema>`, sign bit cleared; collisions across schemas are possible and only reintroduce serialization, never a race); mysql/mariadb/mssql suffix the lock NAME with the schema (hashed past MySQL's 64-char `GET_LOCK` cap). Every taker moved together in this change — the declarative applier, the file-based runner, the boot auto-migrate, the CLI's reactive-trigger boot repair, and the workflow cluster first-boot gate (its own distinct key, same derivation). On mysql/mariadb, where `GET_LOCK` is server-wide and `DB_SCHEMA` is not a connection pin, setting `DB_SCHEMA` to your database name is how two apps on one server un-share the lock.
|
|
54
|
+
- A string reports its LENGTH. Never a prefix, never a hash — `enc:v1:` is a prefix and so is the first byte of a private key, so there is no prefix length that is safe for every credential format. - A number, boolean or date reports its TYPE only. A number can BE the secret. - **A key can be the value.** An object keyed by user data puts a datum where a schema name belongs, so a key is reproduced only when it looks like a declared field — a short plain identifier. The first version truncated long keys and documented the weakness instead; this module's own test caught 62 characters of a secret surviving on the first run. A leak with a footnote is still a leak. - **A string's length is a real disclosure, and a small one.** Stated in the docs rather than buried: for a fixed-format credential it carries nothing, for a human-chosen password it is a weak hint. `'all'` stays the default for anyone that matters to.
|
|
109
55
|
|
|
110
|
-
|
|
111
|
-
- **@voltro/plugin-search** — `POST /reindex` now STREAMS the source table (keyset-paginated `streamTable`) and upserts one bounded page at a time instead of loading the whole table into memory — the old shape was an OOM on exactly the tables big enough to need a reindex (PERF-12). The page size is a new tunable, `searchPlugin({ sync: { reindexBatchSize } })` (default 1000), and the `/indexes` panel reports it as part of the policy in force. `backfillIndex` keeps its plain-array signature for small explicit seeds. Additive surface only: a new optional `SearchSyncOptions` knob + a new `SYNC_DEFAULTS` key — no existing call site changes meaning.
|
|
112
|
-
- **@voltro/plugin-search** — Sync-stat counters no longer pay a read+CAS against the OLTP primary on EVERY indexed-table write (PERF-14). Counts buffer in memory and flush per window — `searchPlugin({ sync: { statsFlushIntervalMs } })` (default 5000 ms; `0` restores the per-event durable write) with an early flush at `statsFlushMaxBuffered` (default 1000) pending counts. Mirrors the runtime's api-key usage buffer, SHUTDOWN included: plugin deactivate drains the tail on both boot paths, so a graceful deploy loses nothing; a hard crash loses at most the current window of counters (never a change — the drift ledger stays the durable record). `GET /indexes` drains the buffer before reading, so the panel stays truthful mid-window. `StatsStore` gained a delta-applying `add` (the flush target); both shipped impls carry it and nothing consumes user-provided `StatsStore` implementations.
|
|
113
|
-
- **@voltro/database, @voltro/plugin-webhooks, @voltro/testing, @voltro/voltro, @voltro/workflow** — Golden churn from this round's signature WIDENINGS, classified per package:
|
|
114
|
-
|
|
115
|
-
- **@voltro/database** — every migration entry point (`applySchema`, `runMigrate`, `runFrameworkBootstrap`, `applyNamespacedSchema`, `provisionTenantNamespace`, the lock functions) gained a trailing OPTIONAL parameter (`SchemaApplyOptions` / `MigrationLockScope`) for the schema-scoped lock and the dialect retry predicate. Every existing call compiles unchanged; omitting the parameter is exactly the old behavior. - **@voltro/workflow / @voltro/testing / @voltro/voltro** — the same widenings re-exported through the aggregates, plus `PresenceWrite`-adjacent type surface already classified in this release's presence entry. - **@voltro/plugin-webhooks** — `deliverWebhookWorkflow`'s payload type inference had COLLAPSED to `AnyStructSchema | Struct<Fields>`, which made `execute`'s requirements `any` for every consumer: there was no type contract in force to break, only one that silently did not exist. It now infers the real payload struct. The export's only callers are the framework's own boot paths (it exists for cluster-runner registration); an app that passed a wrong-shaped payload under `any` now gets the compile error it should always have had — which is the fix, not collateral.
|
|
56
|
+
The `'shape'` outcome describes the payload it REPLACES — the value on success, the error on failure — rather than the event. Describing the event would report `{ kind, value, durationMs }` and hide the field, which is the failure the option exists to end. An error's `_tag` still survives, as it does under `'all'`.
|
|
116
57
|
|
|
117
58
|
### Fixed
|
|
118
59
|
|
|
119
|
-
- **@voltro/cli
|
|
120
|
-
|
|
121
|
-
A consumer with 565 undecided procedures set `security: { defaultDeny: false }` across their app, and their reasoning was correct at every step from what they were shown. Their authority is per-team — a viewer in one team, an admin in another — so a subject-global `guards: [{ scope }]` would state a check they do not perform, and the boot refusal warns against exactly that ("reaching for a scope every caller already holds satisfies the gate, reads as protection, and enforces nothing"). `openAccess:` would be untrue. Both offered forms were rightly rejected, so they turned the gate off and kept enforcing in handlers.
|
|
122
|
-
|
|
123
|
-
The form that fits them — `guards: [{ action, resourceType, resource }]`, backed by `defineResourcePolicy` and a tuple source registered over their own tables — has shipped for several releases, is wired on both boot paths, fails closed without a resolver, and is documented under Authentication → Authorization. They looked: they read `GuardSpec.resource`, whose doc comment described the resolver as "a future ReBAC / `accessPolicy()` resolver". That sentence was written before the ReBAC path shipped and never updated, and it is the only thing a reader of that type has. A doc comment that says "future" about something built is not a small inaccuracy — it argued a careful team out of a security gate.
|
|
124
|
-
|
|
125
|
-
An enumeration inside a refusal is read as exhaustive, and the more careful the reader, the more thoroughly they act on it. `accessDecisionForms.test.ts` pins all three forms in all three places, including the `defaultDeny: false` branch — an app that has already given up is precisely the audience that needs to learn there was a third option.
|
|
126
|
-
- **@voltro/protocol, @voltro/database, @voltro/runtime, @voltro/plugin-broadcast, @voltro/sql-postgres, @voltro/sql-mysql, @voltro/sql-sqlite, @voltro/sql-mssql** — A reactivity-channel publish now says where it came from — `origin: 'inline'`, because it happened in THIS process — instead of borrowing the `'injected'` stamp its transport seam applies by default. Two defects came out of that one mislabel, both silent:
|
|
127
|
-
|
|
128
|
-
- **A channel published synchronously from inside a change listener never left the replica.** `plugin-broadcast` suppresses re-publishes while it is injecting, and the bracket was coarse: it dropped EVERY emission made in that window, not just the event it had injected. So `onChange` → `publishReactivity` woke the local node and no peer ever heard it — no error, no log. The one plugin doing cross-replica fan-out (presence) escaped only because it re-publishes from its own transport callback. The guard now suppresses by provenance, so a local publish made inside the bracket travels like any other. - **A replica could not tell its own channel publish from a peer's.** Both arrived `'injected'`, so a listener fanning a channel onward had nothing to key on and needed a boolean per channel to avoid an echo. `origin` answers it now.
|
|
129
|
-
|
|
130
|
-
Two supporting changes, each with its own failure mode:
|
|
131
|
-
|
|
132
|
-
- `origin` no longer survives the wire. It describes how an event reached THIS process, so the receiving replica strips what the sender serialised and stamps its own. Without this the guard fails OPEN — measured, an arrival still claiming `'inline'` amplified one publish into 163 events and killed the test worker. - The transport-origin stamp has one definition (`externalChangeEvent`, `@voltro/database`) instead of five hand copies across the memory store and the four dialect stores. `injectOriginParity.test.ts` fails if any store grows its own again — a store that hand-stamps would override a caller's stated origin, and the visible result is a channel that stops crossing replicas on that dialect only.
|
|
60
|
+
- **@voltro/cli** — `voltro db --help` listed fifteen of eighteen subcommands. `adopt`, `scan-credentials` and `encrypt-column` shipped and never joined the hand-written string.
|
|
133
61
|
|
|
134
|
-
|
|
135
|
-
- **@voltro/cli, @voltro/devtools-ui** — `voltro check` no longer mistakes a deliberate `openAccess:` mutation for an unguarded one — and no longer mistakes it for a guarded one either. The manifest serialises the decision as a `kind: 'open'` guard entry carrying the reason string; `toInput` now translates it into `openAccess` on the graph procedure with `hasGuards: false` (nothing IS checked), and the `rbac/unguarded-mutation` rule skips a procedure whose author already "confirmed it is intentionally public" — the rule's own fix text. Previously the open entry was counted as a guard, so the finding disappeared for the wrong reason: the open mutation read as protected.
|
|
62
|
+
A consumer wrote both halves of that gap into a requirements document, as separate items, neither of them about help text:
|
|
136
63
|
|
|
137
|
-
|
|
138
|
-
- **@voltro/cli** — `ctx.query` in a loader rejects an `error` event instead of returning it as the query's rows.
|
|
64
|
+
- **`voltro db encrypt-column` "does not exist"** — filed as a feature request, quoting the fifteen names they saw as evidence. It shipped in 0.33.0, and enabling `.encrypted()` on a populated column by hand is exactly the migration they were about to write themselves. - **`scan-credentials` "no longer exists"** — filed as CLOSED, a credential scanner struck off their list as removed. It had not moved.
|
|
139
65
|
|
|
140
|
-
A
|
|
66
|
+
A quoted enumeration is read as exhaustive, and the more careful the reader the more thoroughly they act on the missing entry. Same lesson a boot refusal in `procedureAccessGate` had already taught us, in a place nobody thought of as a message.
|
|
141
67
|
|
|
142
|
-
The
|
|
68
|
+
The usage line is now GENERATED from the dispatch table's key type (`Record<DbSubcommand, Handler>` in `dbCommand.ts`, names in `subcommandNames.ts`), so a handler with no name or a name with no handler fails to compile. `voltro privacy` is keyed the same way. The prose summary beside it cannot be generated — it carries per-command annotations — so a test asserts it mentions every name, because it carried the identical three omissions and it is what `voltro --help` prints first.
|
|
143
69
|
|
|
144
|
-
|
|
70
|
+
`subcommandHelpParity.test.ts` also NAMES the six commands whose subcommand menus have no dispatch table behind them (`webhooks`, `evolve`, `new`, `data`, `storage`, `add`). They dispatch through a switch and are unchecked; a silently-unchecked command reads exactly like a checked one.
|
|
71
|
+
- **@voltro/cli** — A 401 or 403 from the inspect surface now names `VOLTRO_INSPECT_TOKEN` and says which side is missing.
|
|
145
72
|
|
|
146
|
-
|
|
147
|
-
- **@voltro/database, @voltro/cli** — The schema apply (`applySchema` / boot auto-migrate / framework bootstrap) now consults the dialect's own transient-failure predicate instead of dying on the first `SQLITE_BUSY`. The gap was located in `@voltro/sql-turso`: `busy_timeout` cannot retry the deferred-upgrade lock class, the store path already honoured the dialect's `retryFilter`, and the migration applier never consulted it — so a DDL statement that met the schema lock failed on the first attempt while every equivalent DML statement would have been retried. The CLI threads each loaded dialect's `retryFilter` through the new `SchemaApplyOptions`; retries are bounded (`VOLTRO_MIGRATION_DDL_RETRIES`, default 4, exponential backoff with jitter) and only ever re-run statements that are safe to re-run: per-statement on the per-operation dialects (sqlite/turso/mysql/mariadb, `IF NOT EXISTS`-shaped or covered by the duplicate-index tolerance), whole-transaction on postgres/mssql, whose retryable classes (deadlock victim, serialization failure) roll the transaction back cleanly. No `retryFilter` threaded means no retry — exactly the previous behavior. The declarative plan applier (`applyPlan`) is deliberately unchanged: its operations are not uniformly idempotent, and partial failure there is owned by the resume ledger.
|
|
148
|
-
- **@voltro/web, @voltro/client** — The web first load no longer ships `msgpackr` — 190.5 → 180.6 KB gz (−9.9 KB, 5.1% of the whole first load) for every app, measured on the zero-procedure fixture and re-pinned into `bundle-budget.json`.
|
|
73
|
+
`voltro db plan --against <url>` printed `remote returned 403` and stopped. A consumer read that as a DATABASE permission problem — the natural reading of a 403 from a command whose entire subject is a database — and went looking at grants. The cause is one unset environment variable, which the command reads four lines above the message.
|
|
149
74
|
|
|
150
|
-
|
|
75
|
+
`voltro probe access` had half of it: it named the variable on 401 and not on 403, while classifying both as `refused`. So the two commands somebody needs during an access migration were the two that would not say what was wrong, and one of them said something misleading instead.
|
|
151
76
|
|
|
152
|
-
|
|
153
|
-
- **@voltro/cli
|
|
77
|
+
`inspectGateHint` is shared by both call sites and distinguishes the two statuses, because they call for different actions: a 401 means no credential was sent (set the variable), a 403 means the one sent was not accepted (the two values differ). Both halves of the sentence name the server AND the calling shell — naming one side produces a second failed attempt.
|
|
78
|
+
- **@voltro/cli** — `VOLTRO_TEMPLATES_DIR` is authoritative when set. It used to be a HINT: if the path it named held no `apps/` (or no `baselines/`), both resolvers fell through to the sibling-checkout walk-up and quietly used a different tree — or none.
|
|
154
79
|
|
|
155
|
-
|
|
80
|
+
A pointer that silently isn't followed is worse than a wrong one. A CI job aimed at the wrong path scaffolded from whatever it happened to find, and a job whose checkout had failed reported an empty template catalogue with nothing connecting that emptiness to the variable it was given. `scripts/lib/docsSite.mjs` states the same rule for `VOLTRO_DOCS_DIR`, and arrived at it the same way: you said where it is; it is not there.
|
|
156
81
|
|
|
157
|
-
|
|
82
|
+
Behaviourally this only changes the misconfigured case — a correct `VOLTRO_TEMPLATES_DIR` resolved to the same place before and after. What changes is that a wrong one now shows up as "not found, here is the path I was told" at the first thing that reads it, instead of as a different tree three steps later.
|
|
158
83
|
|
|
159
|
-
|
|
160
|
-
- **@voltro/runtime, @voltro/cli, @voltro/testing** — `setRowFilter`'s registration moved from a module-local variable to a `globalThis` cell, and a scoped store that receives no filter while one is registered now throws instead of serving unfiltered rows.
|
|
84
|
+
The unbundled resolution order is otherwise untouched: sibling `voltro-templates` → `.voltro-templates` → the bundled `templates/` a published CLI ships.
|
|
161
85
|
|
|
162
|
-
|
|
86
|
+
### Internal (no consumer-facing effect)
|
|
163
87
|
|
|
164
|
-
|
|
88
|
+
- **@voltro/plugin-ai-flows** — Two comments in the flow engine cited task records from a plans tracker that has since been deleted. Comment-only; no behavior, no API, nothing a consumer can observe.
|
|
165
89
|
|
|
166
|
-
|
|
90
|
+
Worth writing down because of HOW it surfaced. The tracker was retired in the META repo, and the gate that went red was in THIS one — `check-stale-task-comments.mjs` resolves a comment's `task #NN` against `../plans`, so deleting a plan document in one repo can only be half a change, and the other half is in a repo the deleting commit never touched.
|
|
167
91
|
|
|
168
|
-
`@voltro/
|
|
92
|
+
Neither comment was WRONG, which is the part that makes the rule earn its keep. The first claims `@voltro/ai` has first-class media generation — true: `generateImage`, `generateSpeech`, `generateVideo` all ship in `packages/ai/src/media.ts`. It now names those three instead of a record number, which is checkable without the deleted document. The second only quoted the retired id inside its own account of a defect (a `"not yet wired (task #35)"` message that outlived the shipped HITL park and misled an audit into filing it as unbuilt); the quote lost the number and kept the whole lesson.
|
|
169
93
|
|
|
170
|
-
The
|
|
171
|
-
- **@voltro/cli** — A bare `voltro serve` under docker compose now drains on SIGTERM — in-flight requests complete against a fully-alive app, the listener refuses new work, live WebSockets are ended cleanly, and the process exits on its own, well inside `VOLTRO_SHUTDOWN_GRACE_MS`. No preStop hook or endpoint removal required. Two real defects closed (both measured against a live server): a single connected WebSocket wedged `nodeServer.close()` — node's `closeAllConnections()`/`closeIdleConnections()` cannot end an upgraded socket while `close()` still waits on it — so EVERY shutdown with a connected web client ran to the 10s deadline cut and the steps queued behind the close (the store's connection-pool close included) silently never ran; and the shutdown hook deactivated plugins and drained the analytics mirror BEFORE the request drain, so a request finishing during shutdown hit dead services and its writes were never mirrored. The drain is bounded: in-flight requests get 60% of the shutdown grace (floor 500ms), stragglers are then destroyed, and idle keep-alive sockets are swept continuously so a finished response never delays exit. The stale `serveApi` comment claiming `NodeRuntime.runMain` owns SIGTERM (and pointing at a k8s preStop hook as the fix) is rewritten to describe the drain that actually runs. Verify against a real serve with `node scripts/serve-drain-check.mjs`.
|
|
94
|
+
The check's own failure text is the reasoning: a plan is retired for exactly two reasons — the work shipped, or it was dropped without shipping — and a comment still citing it asserts the second while usually meaning the first.
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/ai": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
15
|
+
"@voltro/ai": "0.37.0",
|
|
16
|
+
"@voltro/cli": "0.37.0",
|
|
17
|
+
"@voltro/database": "0.37.0",
|
|
18
|
+
"@voltro/env": "0.37.0",
|
|
19
|
+
"@voltro/protocol": "0.37.0",
|
|
20
|
+
"@voltro/runtime": "0.37.0",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.37.0",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"@vitest/coverage-v8": "^4.1.10",
|
|
27
27
|
"vitest": "^4.1.10"
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-auth": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
22
|
-
"@voltro/sql-postgres": "0.
|
|
16
|
+
"@voltro/cli": "0.37.0",
|
|
17
|
+
"@voltro/database": "0.37.0",
|
|
18
|
+
"@voltro/env": "0.37.0",
|
|
19
|
+
"@voltro/plugin-auth": "0.37.0",
|
|
20
|
+
"@voltro/protocol": "0.37.0",
|
|
21
|
+
"@voltro/runtime": "0.37.0",
|
|
22
|
+
"@voltro/sql-postgres": "0.37.0",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.37.0",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"@vitest/coverage-v8": "^4.1.10",
|
|
29
29
|
"vitest": "^4.1.10"
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@effect/platform": "^0.97.0",
|
|
18
18
|
"@effect/rpc": "^0.76.0",
|
|
19
|
-
"@voltro/cli": "0.
|
|
20
|
-
"@voltro/database": "0.
|
|
21
|
-
"@voltro/env": "0.
|
|
22
|
-
"@voltro/plugin-multitenancy": "0.
|
|
23
|
-
"@voltro/protocol": "0.
|
|
24
|
-
"@voltro/runtime": "0.
|
|
19
|
+
"@voltro/cli": "0.37.0",
|
|
20
|
+
"@voltro/database": "0.37.0",
|
|
21
|
+
"@voltro/env": "0.37.0",
|
|
22
|
+
"@voltro/plugin-multitenancy": "0.37.0",
|
|
23
|
+
"@voltro/protocol": "0.37.0",
|
|
24
|
+
"@voltro/runtime": "0.37.0",
|
|
25
25
|
"effect": "^3.22.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@voltro/testing": "0.
|
|
28
|
+
"@voltro/testing": "0.37.0",
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"@vitest/coverage-v8": "^4.1.10",
|
|
31
31
|
"vitest": "^4.1.10"
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-deactivation": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
16
|
+
"@voltro/cli": "0.37.0",
|
|
17
|
+
"@voltro/database": "0.37.0",
|
|
18
|
+
"@voltro/env": "0.37.0",
|
|
19
|
+
"@voltro/plugin-deactivation": "0.37.0",
|
|
20
|
+
"@voltro/protocol": "0.37.0",
|
|
21
|
+
"@voltro/runtime": "0.37.0",
|
|
22
22
|
"effect": "^3.22.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.
|
|
25
|
+
"@voltro/testing": "0.37.0",
|
|
26
26
|
"typescript": "^6.0.3",
|
|
27
27
|
"@vitest/coverage-v8": "^4.1.10",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@react-email/components": "^1.0.12",
|
|
15
15
|
"@react-email/render": "^1.4.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-mail": "0.
|
|
20
|
-
"@voltro/plugin-multitenancy": "0.
|
|
21
|
-
"@voltro/protocol": "0.
|
|
22
|
-
"@voltro/runtime": "0.
|
|
16
|
+
"@voltro/cli": "0.37.0",
|
|
17
|
+
"@voltro/database": "0.37.0",
|
|
18
|
+
"@voltro/env": "0.37.0",
|
|
19
|
+
"@voltro/plugin-mail": "0.37.0",
|
|
20
|
+
"@voltro/plugin-multitenancy": "0.37.0",
|
|
21
|
+
"@voltro/protocol": "0.37.0",
|
|
22
|
+
"@voltro/runtime": "0.37.0",
|
|
23
23
|
"effect": "^3.22.0",
|
|
24
24
|
"react": "^19.0.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/testing": "0.
|
|
27
|
+
"@voltro/testing": "0.37.0",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"@vitest/coverage-v8": "^4.1.10",
|
|
30
30
|
"vitest": "^4.1.10"
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.
|
|
20
|
-
"@voltro/plugin-storage": "0.
|
|
21
|
-
"@voltro/protocol": "0.
|
|
22
|
-
"@voltro/runtime": "0.
|
|
23
|
-
"@voltro/sql-mysql": "0.
|
|
16
|
+
"@voltro/cli": "0.37.0",
|
|
17
|
+
"@voltro/database": "0.37.0",
|
|
18
|
+
"@voltro/env": "0.37.0",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.37.0",
|
|
20
|
+
"@voltro/plugin-storage": "0.37.0",
|
|
21
|
+
"@voltro/protocol": "0.37.0",
|
|
22
|
+
"@voltro/runtime": "0.37.0",
|
|
23
|
+
"@voltro/sql-mysql": "0.37.0",
|
|
24
24
|
"effect": "^3.22.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/testing": "0.
|
|
27
|
+
"@voltro/testing": "0.37.0",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"@vitest/coverage-v8": "^4.1.10",
|
|
30
30
|
"vitest": "^4.1.10"
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
22
|
-
"@voltro/sql-sqlite": "0.
|
|
16
|
+
"@voltro/cli": "0.37.0",
|
|
17
|
+
"@voltro/database": "0.37.0",
|
|
18
|
+
"@voltro/env": "0.37.0",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.37.0",
|
|
20
|
+
"@voltro/protocol": "0.37.0",
|
|
21
|
+
"@voltro/runtime": "0.37.0",
|
|
22
|
+
"@voltro/sql-sqlite": "0.37.0",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.37.0",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"@vitest/coverage-v8": "^4.1.10",
|
|
29
29
|
"vitest": "^4.1.10"
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"test": "voltro test"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@voltro/cli": "0.
|
|
15
|
-
"@voltro/database": "0.
|
|
16
|
-
"@voltro/env": "0.
|
|
17
|
-
"@voltro/plugin-multitenancy": "0.
|
|
18
|
-
"@voltro/plugin-storage": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
14
|
+
"@voltro/cli": "0.37.0",
|
|
15
|
+
"@voltro/database": "0.37.0",
|
|
16
|
+
"@voltro/env": "0.37.0",
|
|
17
|
+
"@voltro/plugin-multitenancy": "0.37.0",
|
|
18
|
+
"@voltro/plugin-storage": "0.37.0",
|
|
19
|
+
"@voltro/protocol": "0.37.0",
|
|
20
|
+
"@voltro/runtime": "0.37.0",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.37.0",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"@vitest/coverage-v8": "^4.1.10",
|
|
27
27
|
"vitest": "^4.1.10"
|