@voltro/cli 0.49.0 → 0.50.1
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 +114 -0
- package/dist/apiBuild-BZ-plaQN.js +2 -0
- package/dist/{apiBuild-QmYHaCF4.js → apiBuild-D3s7lx57.js} +2 -2
- package/dist/appGraph-CM9wvUmD.js +2 -0
- package/dist/appGraph-Cv2W_wO5.js +111 -0
- package/dist/bin.js +1 -1
- package/dist/{build-Bi4O_g7T.js → build-CFmqF39c.js} +7 -7
- package/dist/{checkCommand-udbQpphy.js → checkCommand-DomvYDi1.js} +1 -1
- package/dist/{checkCommand-D8vjQXQt.js → checkCommand-DsKIjL6k.js} +54 -45
- package/dist/{codegenCommand-DApSH7hK.js → codegenCommand-C0S27nbB.js} +2 -2
- package/dist/{codemodRunner-De1_316I.js → codemodRunner-DwY8Rjbl.js} +696 -481
- package/dist/{commands-a5PxCaIg.js → commands-Brp4DI8u.js} +22 -22
- package/dist/{dashboardCommand-DaoUUqJS.js → dashboardCommand-bbLfc9FW.js} +3 -3
- package/dist/{dataCommand-CJv_OMoS.js → dataCommand-k9mXTXin.js} +511 -333
- package/dist/{dev-C31Nvlbv.js → dev-C0cbqysu.js} +4 -4
- package/dist/{dev-DFVuzOj7.js → dev-Dz9GqA6P.js} +1 -1
- package/dist/doctorCommand-BlciWUJb.js +2 -0
- package/dist/{doctorCommand-B3uraeAe.js → doctorCommand-DyViaaCP.js} +38 -22
- package/dist/{doctorSourceRelations-Dd48LPvO.js → doctorSourceRelations-c5mlN5HI.js} +66 -34
- package/dist/{dormancyCommand-QUXQ9yoz.js → dormancyCommand-DMZPA_HW.js} +1 -1
- package/dist/{embeddingsCommand-Zw1-rYSF.js → embeddingsCommand-CMxRPkg6.js} +1 -1
- package/dist/{envCommand-DtaJ4xEf.js → envCommand-CDLr-yD4.js} +2 -2
- package/dist/{evolveCommand-JDD3hE1i.js → evolveCommand-Ct4NesL3.js} +2 -2
- package/dist/{generateCommand-CcyvH2ve.js → generateCommand-BDdOxhnv.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-COsUC1DB.js → infoCommand-orsKJKUy.js} +1 -1
- package/dist/{inspect-B7U7Cl_Z.js → inspect-CtL_xTbu.js} +1 -0
- package/dist/inspect-CuGDYES0.js +2 -0
- package/dist/{interruptedReplace-C1ZI84xi.js → interruptedReplace-C3O3M1MM.js} +2 -1
- package/dist/interruptedReplace-CvmiAM9K.js +2 -0
- package/dist/manifestBuild-CPjhvM62.js +2 -0
- package/dist/{manifestBuild-BK42hu0k.js → manifestBuild-CuU1VrSm.js} +2 -2
- package/dist/{runtimeTrace-Di6fcndp.js → runtimeTrace-Bo8jWsNe.js} +1 -1
- package/dist/{sdkgen-CIi5gM17.js → sdkgen-OZhiT2vz.js} +1 -1
- package/dist/serveCommand-Bbgg7Paq.js +2 -0
- package/dist/{serveCommand-N3udT49p.js → serveCommand-YbD5VgfV.js} +8 -8
- package/dist/serveEntry.js +1 -1
- package/dist/{start-DvlYDyAr.js → start-7b_bL8Uc.js} +1 -1
- package/dist/{start-7HcSIKEp.js → start-B1nH9ahf.js} +2 -2
- package/dist/startEntry.js +1 -1
- package/dist/{updateCommand-2BfbeMvp.js → updateCommand-BgH6vTtq.js} +2 -1
- package/dist/updateCommand-BweY17z-.js +2 -0
- package/dist/{webDev-B4IEMB0n.js → webDev-BbdkzMbd.js} +1 -1
- package/dist/webDev-BcRFTsMA.js +2 -0
- package/dist/{webhooksCommand-CwX9IzvJ.js → webhooksCommand-DjE6u0r4.js} +1 -1
- package/package.json +41 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +113 -3
- package/templates/agent-docs/data.md +36 -0
- package/templates/agent-docs/whats-new.md +30 -41
- 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 +4 -4
- package/dist/apiBuild-dZbHdqMg.js +0 -2
- package/dist/appGraph-KGDPTuTy.js +0 -110
- package/dist/appGraph-zuMGKVYX.js +0 -2
- package/dist/doctorCommand-CPL6EhrW.js +0 -2
- package/dist/inspect-DUze25t0.js +0 -2
- package/dist/interruptedReplace-DNJ7MKph.js +0 -2
- package/dist/manifestBuild-j0n109tt.js +0 -2
- package/dist/serveCommand-CzSDngKm.js +0 -2
- package/dist/updateCommand-CtYm7aKH.js +0 -2
- package/dist/webDev-B79ji8M5.js +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.50.1",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -676,6 +676,30 @@
|
|
|
676
676
|
"title": "store.query() now returns the row type — casts on row fields may stop compiling",
|
|
677
677
|
"kind": "manual"
|
|
678
678
|
},
|
|
679
|
+
{
|
|
680
|
+
"version": "0.50.0",
|
|
681
|
+
"id": "0.50.0/01_transfer-runs-endpoint",
|
|
682
|
+
"title": "`GET /_voltro/admin/imports` → `GET /_voltro/admin/transfers`",
|
|
683
|
+
"kind": "manual"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
"version": "0.50.0",
|
|
687
|
+
"id": "0.50.0/02_reissue-data-imports-rename",
|
|
688
|
+
"title": "RE-CHECK: `voltro data imports` → `voltro data transfers` (0.49.0's note could not see your scripts)",
|
|
689
|
+
"kind": "manual"
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
"version": "0.50.0",
|
|
693
|
+
"id": "0.50.0/03_interrupted-replace-tables-nullable",
|
|
694
|
+
"title": "`InterruptedReplace.tables` is `number | null`",
|
|
695
|
+
"kind": "manual"
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
"version": "0.51.0",
|
|
699
|
+
"id": "0.51.0/01_computed-source-keeps-its-names",
|
|
700
|
+
"title": "A COMPUTED `source:` can keep its typed names — do not widen the whole registry",
|
|
701
|
+
"kind": "manual"
|
|
702
|
+
},
|
|
679
703
|
{
|
|
680
704
|
"version": "0.6.0",
|
|
681
705
|
"id": "0.6.0/01_no-dev-session-secret",
|
|
@@ -751,22 +775,22 @@
|
|
|
751
775
|
"@effect/platform-node": "^0.108.0",
|
|
752
776
|
"@effect/sql": "^0.52.0",
|
|
753
777
|
"@effect/workflow": "^0.19.0",
|
|
754
|
-
"@voltro/ai": "0.
|
|
755
|
-
"@voltro/cache": "0.
|
|
756
|
-
"@voltro/data-transfer": "0.
|
|
757
|
-
"@voltro/database": "0.
|
|
758
|
-
"@voltro/env": "0.
|
|
759
|
-
"@voltro/kv": "0.
|
|
760
|
-
"@voltro/logger": "0.
|
|
761
|
-
"@voltro/plugin-auth": "0.
|
|
762
|
-
"@voltro/plugin-broadcast": "0.
|
|
763
|
-
"@voltro/plugin-mail": "0.
|
|
764
|
-
"@voltro/plugin-storage": "0.
|
|
765
|
-
"@voltro/plugin-webhooks": "0.
|
|
766
|
-
"@voltro/protocol": "0.
|
|
767
|
-
"@voltro/runtime": "0.
|
|
768
|
-
"@voltro/serverless": "0.
|
|
769
|
-
"@voltro/workflow": "0.
|
|
778
|
+
"@voltro/ai": "0.50.1",
|
|
779
|
+
"@voltro/cache": "0.50.1",
|
|
780
|
+
"@voltro/data-transfer": "0.50.1",
|
|
781
|
+
"@voltro/database": "0.50.1",
|
|
782
|
+
"@voltro/env": "0.50.1",
|
|
783
|
+
"@voltro/kv": "0.50.1",
|
|
784
|
+
"@voltro/logger": "0.50.1",
|
|
785
|
+
"@voltro/plugin-auth": "0.50.1",
|
|
786
|
+
"@voltro/plugin-broadcast": "0.50.1",
|
|
787
|
+
"@voltro/plugin-mail": "0.50.1",
|
|
788
|
+
"@voltro/plugin-storage": "0.50.1",
|
|
789
|
+
"@voltro/plugin-webhooks": "0.50.1",
|
|
790
|
+
"@voltro/protocol": "0.50.1",
|
|
791
|
+
"@voltro/runtime": "0.50.1",
|
|
792
|
+
"@voltro/serverless": "0.50.1",
|
|
793
|
+
"@voltro/workflow": "0.50.1",
|
|
770
794
|
"chokidar": "^5.0.0",
|
|
771
795
|
"ioredis": "^5.11.1",
|
|
772
796
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.md
CHANGED
|
@@ -707,7 +707,7 @@ each plugin's own README.
|
|
|
707
707
|
|
|
708
708
|
| Topic | Open | Summary |
|
|
709
709
|
|---|---|---|
|
|
710
|
-
| **What's new in 0.
|
|
710
|
+
| **What's new in 0.50.1** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
711
711
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
712
712
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
713
713
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -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.50.1** | `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. |
|
|
@@ -290,6 +290,19 @@ carries its own guards, or it is a UI-affordance scope that `useCan` reads to
|
|
|
290
290
|
hide a button and no server check backs on purpose. All three are fine. Not
|
|
291
291
|
knowing which is not.
|
|
292
292
|
|
|
293
|
+
`rbac/unguarded-mutation` flags a mutation that declares neither a guard nor an
|
|
294
|
+
`openAccess:` reason. It skips **`internal: true`** procedures: those are in no
|
|
295
|
+
rpc group and on no route, so "any caller who can reach the rpc surface" names a
|
|
296
|
+
surface that does not exist — and neither remedy applies either, since a guard
|
|
297
|
+
would protect nothing and `openAccess` is refused outright on an internal
|
|
298
|
+
descriptor. This is the same `isWireReachable` predicate the boot access gate and
|
|
299
|
+
all three rpc-group assemblies use, so `check` and `voltro doctor` cannot answer
|
|
300
|
+
the question differently.
|
|
301
|
+
|
|
302
|
+
A manifest that does not carry the field at all — an api older than it — is read
|
|
303
|
+
as *reachable*, not as internal. The rule stays loud rather than going quiet on
|
|
304
|
+
the apps least able to notice.
|
|
305
|
+
|
|
293
306
|
#### Declared vs OBSERVED — reconciled against reality
|
|
294
307
|
|
|
295
308
|
A query's `source` and a mutation's `targets` are not documentation: the
|
|
@@ -323,6 +336,30 @@ Three things about that output are deliberate:
|
|
|
323
336
|
about the runs that happened, not a proof about the ones that didn't, and
|
|
324
337
|
`check`'s exit code gates CI. They are always warnings.
|
|
325
338
|
|
|
339
|
+
A fourth thing is not visible in that output and matters more than any of the
|
|
340
|
+
three: **the recorder only knows what ran.** A boot is not a run. An idle dev
|
|
341
|
+
instance that started, served nothing and stopped produces
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
observed: 0/2755 procedures exercised (0%)
|
|
345
|
+
2753 unexercised — no recording, so nothing is claimed about them
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
which is the honest answer and also a useless one — the section says nothing
|
|
349
|
+
about 99.9% of the surface. If you want this as a CI gate, the recording pass has
|
|
350
|
+
to be a run that actually *calls* the procedures. `voltro e2e` is one: it spawns
|
|
351
|
+
`voltro dev` for the api, which turns recording on, and then drives the specs. A
|
|
352
|
+
harness of your own that boots the app in its own process needs
|
|
353
|
+
`VOLTRO_OBSERVE_GRAPH=1` set before anything is imported. Either way, booting the
|
|
354
|
+
app and then running `check` measures nothing, and the report will not pretend
|
|
355
|
+
otherwise.
|
|
356
|
+
|
|
357
|
+
It is still worth wiring up at low coverage, because the findings are per
|
|
358
|
+
procedure and do not need company. One deployment had exactly one procedure
|
|
359
|
+
observed, and that one produced a real defect: a mutation upserting
|
|
360
|
+
`push_subscriptions` with no declared target while two queries read that table as
|
|
361
|
+
their `source`, so a registered device never appeared in any running subscription.
|
|
362
|
+
|
|
326
363
|
No file → the section is skipped silently. This is not derived by parsing your
|
|
327
364
|
handlers: a static pass over code that reaches the store through shared helpers,
|
|
328
365
|
behind conditionals, has a long tail of both false positives and false negatives
|
|
@@ -2996,7 +3033,7 @@ $ voltro data transfers --target api --api-url https://app.example
|
|
|
2996
3033
|
2026-08-23T08:00:00.000Z · export all — 228866 row(s) across 104 table(s) · via api · from backups/nightly.vbundle
|
|
2997
3034
|
```
|
|
2998
3035
|
|
|
2999
|
-
Same data over `GET /_voltro/admin/
|
|
3036
|
+
Same data over `GET /_voltro/admin/transfers?limit=20`, behind the same
|
|
3000
3037
|
data-transfer secret (the row names bundles and schema fingerprints). It works
|
|
3001
3038
|
from a different machine than the one that started the import, and through
|
|
3002
3039
|
anything that forwards a GET.
|
|
@@ -3330,12 +3367,85 @@ yield* runImport({ store: target, bundleDir: './backup', targetSnapshot }).pipe(
|
|
|
3330
3367
|
## Native backup / restore
|
|
3331
3368
|
|
|
3332
3369
|
```bash
|
|
3333
|
-
voltro data backup ./backups/2026-07-01
|
|
3334
|
-
voltro data
|
|
3370
|
+
voltro data backup ./backups/2026-07-01 # pg_dump --format=custom / mariadb-dump --single-transaction / …
|
|
3371
|
+
voltro data backup ./backups/2026-07-01 --assets # rows AND the stored blobs
|
|
3372
|
+
voltro data restore ./backups/2026-07-01 --assets # pg_restore / mariadb / … + the blobs
|
|
3335
3373
|
```
|
|
3336
3374
|
|
|
3337
3375
|
These shell out to the vendor tools resolved from your `DB_DIALECT` + connection env. They produce a dialect-native artifact (`db.dump`, `db.sql`, `db.sqlite`, `db.bacpac`) that is lossless and point-in-time consistent for **same-dialect** restore — the right tool for disaster recovery. Secrets are passed via the tools' environment variables (`PGPASSWORD`, `MYSQL_PWD`), never on the command line, where the tool supports it. The named tool must be installed and on `PATH`.
|
|
3338
3376
|
|
|
3377
|
+
On **mariadb** the MariaDB-named binaries (`mariadb-dump`, `mariadb`) are preferred and Oracle's (`mysqldump`, `mysql`) are the fallback — with the reason carried into the failure, because the error that fallback produces (`Unknown table 'COLUMN_STATISTICS' in information_schema`, 1109) names a table nobody asked for. Two things worth knowing before you go and install the MariaDB client package:
|
|
3378
|
+
|
|
3379
|
+
- Do **not** reach for `--column-statistics=0`. That flag does not exist on `mariadb-dump`, so it patches the wrong client and breaks the right one.
|
|
3380
|
+
- A MariaDB **12.x** client requires TLS by default. Running it *by hand* against a server without TLS fails with `TLS/SSL error: SSL is required` (2026) and needs `--skip-ssl`. These commands are not affected — it is the first manual call after installing that trips.
|
|
3381
|
+
|
|
3382
|
+
### `--assets` — the blobs are not in the dump
|
|
3383
|
+
|
|
3384
|
+
A vendor dump contains rows. Your blobs are in object storage, and no `pg_dump` has ever seen them. So a rows-only backup restores a database whose rows reference objects that are not there — and the reference and the object are checked at different times, which is why that state is discovered by a user, months later, rather than by the restore.
|
|
3385
|
+
|
|
3386
|
+
The backend it reads is the one your app **configured** — `storagePlugin({ provider: s3(…) })` if you installed it, the `STORAGE_*` env otherwise. (The commands used to resolve the env default unconditionally, so an app that configured its provider in code had its export, import and backup reading a different backend than the rest of it.)
|
|
3387
|
+
|
|
3388
|
+
`--assets` captures them alongside the dump, through the same content-addressed pipeline `voltro data export --assets` uses: each blob is streamed (never buffered whole), stored under `assets/<sha256>` so identical content is stored once, and listed in `assets/index.ndjson`. `restore --assets` streams them back and **re-hashes on the way**, so a corrupted artifact can never silently overwrite good bytes.
|
|
3389
|
+
|
|
3390
|
+
```
|
|
3391
|
+
backups/2026-07-01/
|
|
3392
|
+
├─ db.dump # the vendor artifact (rows)
|
|
3393
|
+
├─ voltro-backup-stamp.json # provenance, incl. what --assets captured
|
|
3394
|
+
├─ assets/index.ndjson # key → sha256 → size → contentType
|
|
3395
|
+
└─ assets/<sha256> # the blob bodies, deduped by content
|
|
3396
|
+
```
|
|
3397
|
+
|
|
3398
|
+
Three refusals, each for a belief that is otherwise acted on silently:
|
|
3399
|
+
|
|
3400
|
+
- **`backup --assets` with no storage provider configured → refused.** There is nothing to capture, and a flag that is accepted and ignored lets you build a rollback story on an artifact that does not contain what you asked for. "Configured" means one of the two things a person actually did: installed `storagePlugin(...)`, or set `STORAGE_PROVIDER`. An in-memory provider nobody asked for is not a decision — and until recently it was what this check saw, which is why the refusal never fired and `--assets` wrote artifacts stamped as carrying blobs that held none.
|
|
3401
|
+
- **`restore --assets` on a rows-only backup → refused.** You believe the blobs are in there. Restoring the rows anyway produces exactly the dangling state this exists to prevent.
|
|
3402
|
+
- **restore *without* `--assets` on a backup that HAS them → warned, not refused.** Restoring rows without blobs is legitimate (a schema drill, a lower environment), and refusing it would push people at `--force`.
|
|
3403
|
+
|
|
3404
|
+
**A reference the provider cannot resolve is reported, not fatal.** A row in `_voltro_storage_refs` can point at an object that was deleted, or that never arrived because an earlier import ran without `--assets`. That is a fact about your data, and no backup can put back bytes that are not there — so the capture records the key, steps over it, and the run says how many:
|
|
3405
|
+
|
|
3406
|
+
```
|
|
3407
|
+
warn 177 of 178 blob reference(s) point at objects the storage provider does not have;
|
|
3408
|
+
they are NOT in this backup and no restore can bring them back.
|
|
3409
|
+
```
|
|
3410
|
+
|
|
3411
|
+
Aborting on the first one made `--assets` unusable for exactly the deployment that needed it: 178 references, one resolvable, and the run stopped at the second — leaving an `assets/` directory with a single blob, **no `voltro-backup-stamp.json`** (the writer never got that far), and nothing anywhere saying 177 objects had been skipped. The stamp is now written on every path, including the one where the asset phase fails, because it describes the **dump** and the dump is already on disk and correct. Without it, `restore` greeted an artifact this tool had written minutes earlier with *"an older/handmade backup. Cannot verify dialect or schema version."*
|
|
3412
|
+
|
|
3413
|
+
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
|
+
|
|
3415
|
+
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
|
+
|
|
3417
|
+
### A restore that is interrupted refuses the next boot
|
|
3418
|
+
|
|
3419
|
+
`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.
|
|
3420
|
+
|
|
3421
|
+
This is the counterpart to `--allow-live`, and it guards from the other side: `--allow-live` asks you not to restore over a running instance, and this says *this database is mid-restore, do not serve it*. A half-restored database looks exactly like a normal one from the inside — every query answers, nothing errors.
|
|
3422
|
+
|
|
3423
|
+
**The restore artifact can erase the marker, and this table said otherwise.** It read "postgres, mysql, mariadb: drops only the objects the dump names — the marker survives". The reasoning is right and the premise was wrong: a native dump names the *whole* database, `_voltro_replace_in_progress` included, and a mysql-family restore writes `DROP TABLE IF EXISTS` in front of each table. The table sorts early, so the guard was removed near the *start* of the window it covers. Measured downstream: one row before the restore, zero after, twice.
|
|
3424
|
+
|
|
3425
|
+
Two changes, covering different dumps:
|
|
3426
|
+
|
|
3427
|
+
- **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
|
+
- **`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
|
+
|
|
3430
|
+
| dialect | shape | effect |
|
|
3431
|
+
|---|---|---|
|
|
3432
|
+
| 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 |
|
|
3433
|
+
| sqlite, turso | whole-file replacement — made **atomic** (temp file + rename) | there is no half-restored state to catch; a killed restore leaves the live file untouched |
|
|
3434
|
+
| mssql | `sqlpackage /Action:Import` replaces the database | a *failed* import is the one case not covered here — verify with `--drill` |
|
|
3435
|
+
|
|
3436
|
+
Clear a marker deliberately with `voltro data clear-replace-marker --yes` once you have decided the current state is correct.
|
|
3437
|
+
|
|
3438
|
+
### Both directions are in the history
|
|
3439
|
+
|
|
3440
|
+
`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:
|
|
3441
|
+
|
|
3442
|
+
```
|
|
3443
|
+
2026-08-23T02:00:00.000Z · backup native postgres + assets — finished (412 blob(s)) · via cli · from ./backups/2026-08-23/db.dump
|
|
3444
|
+
2026-08-22T09:14:02.000Z · FAILED restore native mariadb — mariadb-dump: exited with code 2 · via cli · from ./backups/2026-08-21/db.sql
|
|
3445
|
+
```
|
|
3446
|
+
|
|
3447
|
+
A native run reports **blobs**, not rows: the vendor tool reports no row count we can trust, and printing `0 row(s)` over a `pg_dump` that worked would be a measurement, and a wrong one. A target with no `_voltro_data_transfers` table still gets its backup — the closing line says it was not recorded, rather than implying it was.
|
|
3448
|
+
|
|
3339
3449
|
### The provenance stamp — a restore that refuses the wrong DB
|
|
3340
3450
|
|
|
3341
3451
|
A native dump is opaque: it doesn't say which dialect made it, which schema shape it carries, or when. `backup` writes a sidecar `voltro-backup-stamp.json` next to the artifact recording exactly that — `dialect`, the live schema `fingerprint`, the `@voltro/cli` version, and the timestamp.
|
|
@@ -1700,6 +1700,32 @@ That matters because the failure it replaces is silent. A `source:` is matched b
|
|
|
1700
1700
|
|
|
1701
1701
|
The file is generated, so **commit it** like the rpc group and let `voltro dev` rewrite it. Before the first run — and in a project that never generates it — `source:` is plain `string` again, which is exactly the previous behaviour; there is no configuration and nothing to opt into.
|
|
1702
1702
|
|
|
1703
|
+
The error carries a suggestion when the name is close to a real one — `Did you mean '"error_logs"'?` — so a rename usually resolves without leaving the editor.
|
|
1704
|
+
|
|
1705
|
+
#### A COMPUTED `source:` can keep its names
|
|
1706
|
+
|
|
1707
|
+
A generic reader — the table arrives in `input`, the caller picks it out of a registry — is computed, and yet every name it can produce is known. The obvious derivation is not assignable:
|
|
1708
|
+
|
|
1709
|
+
```ts
|
|
1710
|
+
source: Object.keys(JUNCTION_REGISTRY), // string[] — ✗
|
|
1711
|
+
```
|
|
1712
|
+
|
|
1713
|
+
The tempting exit is a cast, or annotating `ReactivitySourceValue` (the wide `string | ReactivityChannel` shape, exported from `@voltro/protocol`). Both compile at once and take that **entire** set of tables out of the check permanently. Keep the literal's keys instead:
|
|
1714
|
+
|
|
1715
|
+
```ts
|
|
1716
|
+
const REGISTRY = { … } as const satisfies Readonly<Record<string, JunctionMeta>>
|
|
1717
|
+
|
|
1718
|
+
// consumers that index with a plain string still get the wide type
|
|
1719
|
+
export const JUNCTION_REGISTRY: Readonly<Record<string, JunctionMeta>> = REGISTRY
|
|
1720
|
+
|
|
1721
|
+
// … and the source list keeps its literal names
|
|
1722
|
+
export const JUNCTION_TABLE_NAMES = Object.keys(REGISTRY) as ReadonlyArray<keyof typeof REGISTRY>
|
|
1723
|
+
```
|
|
1724
|
+
|
|
1725
|
+
The part that is easy to get wrong: an **annotation widens the keys back**, even when the literal carries `as const`. A registry written as `export const R: Readonly<Record<string, Meta>> = { … } as const` has `keyof typeof R === string`, and nothing about it looks wrong — the annotation is checked against the literal and then replaces its type. `satisfies` checks without replacing. That is the whole reason for the two-line split above.
|
|
1726
|
+
|
|
1727
|
+
Reach for `ReactivitySourceValue` when there genuinely is no key set to keep — a name read from a config file, or assembled at runtime. Not when recovering one takes two lines.
|
|
1728
|
+
|
|
1703
1729
|
Two things it deliberately does not narrow. A **plugin's** route `source:` stays `string`: a plugin ships against many apps and cannot know any of their tables. And nothing that READS a descriptor's source at runtime narrows either — a reader that refused an unknown name would reject the stale name it exists to report.
|
|
1704
1730
|
|
|
1705
1731
|
**It does not check the other direction — except for relations.** A `source:` that omits a table the query genuinely reads is silent: the name is right, the table exists, and nothing has an opinion. That is the failure that costs a user report — they type, the row lands, and the panel does not move.
|
|
@@ -1714,6 +1740,16 @@ Two things it deliberately does not narrow. A **plugin's** route `source:` stays
|
|
|
1714
1740
|
|
|
1715
1741
|
An eager-loaded relation is composition by definition — its rows are IN the result — and its table comes from the relation registry, so the rule has no heuristic and no exception list. A **many-to-many** wants the junction table too, and says so separately: adding or removing a link writes only the junction row, so declaring the target alone leaves the list stale on exactly the operation a user performs to change it.
|
|
1716
1742
|
|
|
1743
|
+
Nested relations resolve in the **same** pass: a relation loaded under `.with({ subTasks: { with: { watchers: true } } })` is looked up on `task_sub_tasks`, not on the query's base, so both levels are reported at once rather than one per run.
|
|
1744
|
+
|
|
1745
|
+
The one thing it still cannot see is a base table it cannot find — neither declared in `source:` nor written as `database.<name>` / `.select('<name>')`. There is then no table to resolve the relations against, and the query would otherwise report clean while every relation on it is unchecked. So doctor says so:
|
|
1746
|
+
|
|
1747
|
+
```
|
|
1748
|
+
⚠ 1 query could not be fully checked — the base table is neither declared nor readable from the executor:
|
|
1749
|
+
reports.byKind: `blocked` resolved against no table
|
|
1750
|
+
declare the base table in `source:` and re-run — relations under it are unchecked until then.
|
|
1751
|
+
```
|
|
1752
|
+
|
|
1717
1753
|
What doctor deliberately does NOT do is derive every table an executor reads. That needs a judgement — does this read **compose** the result or merely **restrict** it? — and only composing reads belong in `source:`; a restricting one re-running on every unrelated write puts a hundred lists back on the wire. A scan has to infer that from syntax, and a rule that guesses on a correct codebase teaches you to ignore it.
|
|
1718
1754
|
|
|
1719
1755
|
So: when a live view does not update, check the writing table is in the reading query's `source:` before anything else.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# What's new in 0.
|
|
1
|
+
# What's new in 0.50.1
|
|
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
|
|
@@ -7,65 +7,54 @@ workaround for something that shipped two versions ago.
|
|
|
7
7
|
|
|
8
8
|
BREAKING entries name a codemod; run `voltro update` to apply it.
|
|
9
9
|
|
|
10
|
-
###
|
|
11
|
-
|
|
12
|
-
- **@voltro/cli, @voltro/data-transfer, @voltro/database, @voltro/sql-sqlite, @voltro/voltro** — A data transfer is a series of short requests now — no single one may outlive a caller's budget.
|
|
13
|
-
|
|
14
|
-
**The rule:** *a request that carries bytes never runs a transfer; a request that starts a transfer never carries bytes.* It was broken in the worst available place. The upload was already chunked and resumable — many short requests, each abandonable — and then the FINAL chunk fell through and ran the whole import. So the longest request of the flow arrived AFTER the entire upload had succeeded, and a caller under a policy that caps a single request (a job runner that kills a client at ten minutes; a 30 s ingress ceiling) lost the most expensive thing they had already paid for. A single-request upload had the same shape without the excuse, and the export had no protocol at all: one request that read the whole database and streamed it back.
|
|
15
|
-
|
|
16
|
-
**Import.** `POST /_voltro/admin/import` accumulates and answers `202`. `POST /_voltro/admin/import/start` begins the run and answers `202 { runId }` as soon as the run's first row exists. `start` is idempotent per upload — it is a short request and therefore a retryable one, and without a claim a retry would begin a second destructive run from the same bytes. A claim whose run has ENDED is taken over rather than honoured forever, so a process that dies holding one cannot poison a bundle.
|
|
17
|
-
|
|
18
|
-
**Export.** `POST /_voltro/admin/export` answers `202 { runId }` and produces in the background; the bytes come back from `GET /_voltro/admin/export/download?runId=&offset=&length=` in ranges, resumable, with the total in a header. Object storage (`--bundle-key`) remains for a bundle you want to KEEP — it is no longer the only way to get one out, because requiring it would leave an instance without storage unable to export at all.
|
|
19
|
-
|
|
20
|
-
**The client.** `--max-request-seconds` (or `VOLTRO_MAX_REQUEST_SECONDS`) declares the budget — declared rather than probed, because the thing that kills a request is a policy on the caller's side and only they know it. `--detach` returns once the run has started and says the outcome is NOT known. Attached, the CLI polls the record and prints per-table progress; Ctrl-C then loses the watching and never the run.
|
|
21
|
-
|
|
22
|
-
**The trap, stated because it is the one way to get this wrong:** a failure used to arrive in the response (409 on drift, 409 on a refused mode, 500 otherwise). After the split the response is a `202`, so **a client deriving its exit code from the status line reports a failed import as a success.** The exit code comes from the polled record, in one shared function, and the drift check moved into `start` where it can still be a refusal that leaves no history.
|
|
10
|
+
### Added
|
|
23
11
|
|
|
24
|
-
|
|
12
|
+
- **@voltro/cli** — A `0.51.0` upgrade note for the case 0.45.0's typed-`source:` note left open: a source that is COMPUTED but whose names are all known — a generic reader that takes its table from `input` or a registry. Both remedies the original offered are wrong there. There is no misspelled name to fix, and widening to `ReactivitySourceValue` (or casting) compiles instantly while taking that entire set of tables permanently out of the check the narrowing exists to provide.
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
- **@voltro/database, @voltro/voltro** — The run-history exports say `transfer`, not `import`.
|
|
14
|
+
The note carries the recipe that keeps the names — `as const satisfies`, with the key set recovered separately — and the trap under it: an ANNOTATION widens the keys back even when the literal carries `as const`, because it is checked against the literal and then replaces its type. Filed under an unreached version because codemod selection is `from < version <= to`: 0.45.0's note can never fire again for anyone already past it. Documented on the subscriptions page in both languages.
|
|
28
15
|
|
|
29
|
-
|
|
16
|
+
### Changed
|
|
30
17
|
|
|
31
|
-
|
|
18
|
+
- **@voltro/data-transfer** — The `mariadb-dump`-not-on-PATH hint now also warns that a MariaDB 12.x client requires TLS by default, so running it BY HAND against a server without TLS fails with `TLS/SSL error: SSL is required` (2026) and needs `--skip-ssl`. Our own invocation is unaffected. It is said here because this hint is what sends people to install that client package, and the next thing many of them do is run the tool by hand — where the failure reads like a broken install of the thing we just told them to install.
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
### Fixed
|
|
34
21
|
|
|
35
|
-
|
|
22
|
+
- **@voltro/cli, @voltro/mcp** — `voltro check`'s `rbac/unguarded-mutation` no longer fires on `internal: true` procedures. Those are in no rpc group and on no route, so the finding's own sentence — "any caller who can reach the rpc surface can invoke it" — named a surface that does not exist, and neither remedy it offered was available: a guard protects nothing there, and `openAccess` is refused outright on an internal descriptor. An app with 990 internal mutations got 990 warnings and a `FAILED` exit from the same tree on which `voltro doctor` reported zero procedures without an access decision, which made `check` unusable as the CI gate it exists to be.
|
|
36
23
|
|
|
37
|
-
|
|
24
|
+
The capability manifest now carries `internal: true` per procedure (present only when true), and the rule reads it through `isWireReachable` — the same predicate the boot access gate and all three rpc-group assemblies use, rather than a fourth spelling of the same flag. A manifest that does not carry the field is read as REACHABLE, so the rule stays loud on an api older than the field rather than going quiet on it.
|
|
25
|
+
- **@voltro/data-transfer, @voltro/cli** — `--assets` aborted the whole capture at the first blob reference the storage provider could not resolve. A dangling reference is a fact about the source data — a row pointing at an object that was deleted, or that never arrived because an earlier import ran without the flag — and no backup can put back bytes that are not there. Aborting made the flag unusable for exactly the deployment that needed it: 178 references, one resolvable, and the run stopped at the second.
|
|
38
26
|
|
|
39
|
-
|
|
27
|
+
The phase now records the key, steps over it, and the run reports how many were skipped. Only a genuine not-found (`status === 404`) is treated this way; a 403 from a rotated credential or a 5xx from a backend outage still fails the capture, because calling those "the object is gone" turns a recoverable outage into a backup that quietly contains nothing.
|
|
40
28
|
|
|
41
|
-
|
|
29
|
+
The same abort also cost the artefact its provenance: `voltro-backup-stamp.json` was written after the asset phase, so a backup taken WITH `--assets` had no stamp at all, and `restore` then greeted an artefact this tool had written minutes earlier as "an older/handmade backup. Cannot verify dialect or schema version." The stamp is now written on every path — it describes the dump, and the dump is on disk and correct by the time the assets run.
|
|
30
|
+
- **@voltro/cli** — `voltro doctor`'s eager-loaded-relation rule resolves nested relations against their PARENT's target table instead of against the query's roots, so every level is reported in one pass. It flattened the `.with({ … })` tree before resolving, which meant a level-2 relation stayed invisible until level 1's table had been added to `source:` — each fix revealed the next level, and a clean run after the first fix meant nothing. One app ran `doctor → fix → doctor` twice before the output stopped producing new findings.
|
|
42
31
|
|
|
43
|
-
|
|
32
|
+
The one case that is still genuinely under-reported is now stated instead of being silent: when the executor's base table is neither declared nor textually readable, nothing resolves, and doctor prints which relation names it could not check rather than reporting clean.
|
|
33
|
+
- **@voltro/data-transfer** — A native restore whose client exited before consuming its input took the whole CLI down with an unhandled `EPIPE`. `createReadStream(dump).pipe(child.stdin)` had no `error` listener, so Node threw on a write to a pipe with no reader:
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### Fixed
|
|
35
|
+
Error: write EPIPE Emitted 'error' event on Socket instance at: at Socket.onerror (node:internal/streams/readable:1045:14)
|
|
48
36
|
|
|
49
|
-
|
|
37
|
+
What that cost is the point. The crash landed before `close`, so the step never resumed: the client's stderr — the one place the reason was written down — was discarded, the transfer row was never closed, the in-progress marker was never re-asserted, and the operator got a raw Node stack trace over a database that was now half replaced. A deployment met it on a 64 MB restore and could not diagnose it at all, because the only diagnosis had been thrown away.
|
|
50
38
|
|
|
51
|
-
|
|
39
|
+
Every stream in the step now carries a handler. `EPIPE` on the child's stdin is deliberately swallowed — a child closes the pipe by dying, and its exit code and stderr are the actual failure — while any other stream error is carried and reported. The failure line now always includes the tool's stderr (and says so explicitly when the tool printed none), plus how many bytes of the artefact had been fed before it stopped. A source error also ends the child's stdin, which turns a restore that used to hang forever on a vanished dump into one that fails, and a clean exit over a broken input stream is reported as a failure rather than as a truncated success.
|
|
40
|
+
- **@voltro/cli** — `voltro check`'s observed-vs-declared findings advised an op that does not exist. The recorder reports what the store did, and `store.upsert` is one call; a `target:` is `InsertTarget | UpdateTarget | DeleteTarget` and has no `upsert` member. So `observed/undeclared-write` said to add `{ table: '…', op: 'upsert' }` — a `TS2322`, from a line whose whole purpose is to be pasted. It now advises both halves, and the observed → declarable mapping is typed `Record<ObservedWriteOp, ReadonlyArray<TargetSpec['op']>>` so a new recorder op cannot ship without a declarable answer.
|
|
52
41
|
|
|
53
|
-
|
|
42
|
+
The remedy no longer produces a finding of its own either: declared targets are now collected per table as a SET. `new Map(targets.map(t => [t.table, t.op]))` kept only the last op, so the correct two-entry declaration read as "declares update" and the insert half came back as a `wrong-op`.
|
|
43
|
+
- **@voltro/data-transfer, @voltro/cli** — The in-progress restore marker did not survive a native restore, on the exact dialects that were documented as safe. The claim — postgres and the mysql family drop only the objects the dump names, so the marker row survives — had a correct argument and a wrong premise: a native dump names the WHOLE database, `_voltro_replace_in_progress` included, and a mysql-family restore writes `DROP TABLE IF EXISTS` in front of each table. The table sorts early, so the guard was removed near the START of the window it exists to cover. Measured downstream as one row before and zero after, twice, once by hand and once through the command — and then a restore died and left a database with a schema, some of its tables, no users and no blobs, with nothing to stop the next boot.
|
|
54
44
|
|
|
55
|
-
|
|
45
|
+
Two changes, covering different dumps. `voltro data backup` now excludes the marker table (`--exclude-table` / `--ignore-table`), so an artefact we produce cannot carry the thing that erases the guard on the way back in. And `restore` re-asserts the marker after the tool exits, on the failing path as well as the succeeding one, which covers dumps taken before this change and dumps made by hand: rewritten is a warning, and unwritable is an error, because the guard is then off for that run.
|
|
46
|
+
- **@voltro/plugin-storage, @voltro/cli** — `--assets` could not be refused when no storage was configured, and resolved the wrong backend when it was.
|
|
56
47
|
|
|
57
|
-
|
|
48
|
+
`resolveStorageProvider({})` is total: its final `default:` arm returns an in-process `memoryProvider`. So the three `if (!provider)` refusals in `voltro data backup` / `export` / `restore` — whose entire job is to refuse a flag the app cannot honour — were checking a condition that cannot hold. What they let through is worse than an unchecked flag: with no storage configured, `backup --assets` captured from a fresh memory provider, found nothing in it, and wrote an artefact stamped `assets: { count: 0 }` — a rollback story that says the blobs are in there.
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
- **@voltro/cli** — `voltro codegen` declared twenty framework tables fewer than a boot.
|
|
50
|
+
It also ignored the provider the app REGISTERED. An app on `storagePlugin({ provider: s3(…) })` had its export/import/backup read and write the env-derived default instead, which surfaces later as "no object at key" — the exact drift `appStorageProvider()` exists to prevent, in call sites that never adopted it.
|
|
61
51
|
|
|
62
|
-
|
|
52
|
+
`configuredStorageProvider()` is the honest predicate: the app's registered provider, else the env-named one, else `undefined`. A memory provider nobody asked for is not a decision. The CLI now loads the app config BEFORE resolving — `storagePlugin(...)` registers at construction, so asking first answered with the default no matter what the app had configured.
|
|
63
53
|
|
|
64
|
-
|
|
54
|
+
### Internal (no consumer-facing effect)
|
|
65
55
|
|
|
66
|
-
|
|
67
|
-
- **@voltro/cli** — `voltro data --help` advertised four subcommands out of nine.
|
|
56
|
+
- **@voltro/cli** — `voltro data backup` / `restore` / `clear-replace-marker` are now driven end to end, one case per declared option and one per refusal, as real CLI subprocesses against a real database.
|
|
68
57
|
|
|
69
|
-
|
|
58
|
+
Every defect this command has shipped lived in the SEQUENCE rather than in a function — an unhandled `EPIPE` that discarded the client's stderr before `close` fired, a marker erased by the restore's own artefact, an asset abort that skipped the provenance stamp, a refusal that could not fire — so none of them was reachable by testing a part. `nativeBackupRestore.e2e.test.ts` covers the whole surface against sqlite (a file copy: no vendor binary, runs anywhere) and `nativeBackupRestoreDialects.integration.test.ts` runs the vendor-tool half against postgres, mariadb and mysql, skipping BY NAME when the server or the client binary is absent — including when the client is present but too old for the server, which a "is it on PATH" check reports as ready.
|
|
70
59
|
|
|
71
|
-
|
|
60
|
+
The suite checks itself against `DATA_FLAGS`, the command's own declared option list, so an option cannot be added without a case that drives it.
|
|
@@ -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.50.1",
|
|
16
|
+
"@voltro/cli": "0.50.1",
|
|
17
|
+
"@voltro/database": "0.50.1",
|
|
18
|
+
"@voltro/env": "0.50.1",
|
|
19
|
+
"@voltro/protocol": "0.50.1",
|
|
20
|
+
"@voltro/runtime": "0.50.1",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.50.1",
|
|
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.50.1",
|
|
17
|
+
"@voltro/database": "0.50.1",
|
|
18
|
+
"@voltro/env": "0.50.1",
|
|
19
|
+
"@voltro/plugin-auth": "0.50.1",
|
|
20
|
+
"@voltro/protocol": "0.50.1",
|
|
21
|
+
"@voltro/runtime": "0.50.1",
|
|
22
|
+
"@voltro/sql-postgres": "0.50.1",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.50.1",
|
|
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.50.1",
|
|
20
|
+
"@voltro/database": "0.50.1",
|
|
21
|
+
"@voltro/env": "0.50.1",
|
|
22
|
+
"@voltro/plugin-multitenancy": "0.50.1",
|
|
23
|
+
"@voltro/protocol": "0.50.1",
|
|
24
|
+
"@voltro/runtime": "0.50.1",
|
|
25
25
|
"effect": "^3.22.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@voltro/testing": "0.
|
|
28
|
+
"@voltro/testing": "0.50.1",
|
|
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.50.1",
|
|
17
|
+
"@voltro/database": "0.50.1",
|
|
18
|
+
"@voltro/env": "0.50.1",
|
|
19
|
+
"@voltro/plugin-deactivation": "0.50.1",
|
|
20
|
+
"@voltro/protocol": "0.50.1",
|
|
21
|
+
"@voltro/runtime": "0.50.1",
|
|
22
22
|
"effect": "^3.22.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.
|
|
25
|
+
"@voltro/testing": "0.50.1",
|
|
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.50.1",
|
|
17
|
+
"@voltro/database": "0.50.1",
|
|
18
|
+
"@voltro/env": "0.50.1",
|
|
19
|
+
"@voltro/plugin-mail": "0.50.1",
|
|
20
|
+
"@voltro/plugin-multitenancy": "0.50.1",
|
|
21
|
+
"@voltro/protocol": "0.50.1",
|
|
22
|
+
"@voltro/runtime": "0.50.1",
|
|
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.50.1",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"@vitest/coverage-v8": "^4.1.10",
|
|
30
30
|
"vitest": "^4.1.10"
|