@voltro/cli 0.44.1 → 0.46.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 +280 -1
- package/dist/{apiBuild-DvWreIQP.js → apiBuild-BKdd0I1Z.js} +2 -2
- package/dist/apiBuild-P2gewrfQ.js +2 -0
- package/dist/bin.d.ts +24 -0
- package/dist/bin.js +46 -11
- package/dist/{build-Ckc-FA7d.js → build-co6yadYz.js} +8 -8
- package/dist/{checkCommand-BNfEs4KG.js → checkCommand-B-eiW-G_.js} +12 -12
- package/dist/{checkCommand-AlbawIal.js → checkCommand-CXAXTjZL.js} +1 -1
- package/dist/codegen-DcE2li1i.js +2 -0
- package/dist/{codegen-CbpGCWLG.js → codegen-sbN1Y8PB.js} +37 -37
- package/dist/codegenCommand-CB6_3sX0.js +144 -0
- package/dist/{codemodRunner-BKTnaGOz.js → codemodRunner-DSZjgyCL.js} +49 -0
- package/dist/{commands-CYkHTox2.js → commands-C-hxgAGr.js} +24 -24
- package/dist/{dashboardCommand-DHDmlo9A.js → dashboardCommand-CEVO0GWF.js} +3 -3
- package/dist/{dataCommand-WFn8CM11.js → dataCommand-k0ezbQWa.js} +355 -250
- package/dist/{dbCommand-B237qI-j.js → dbCommand-BwkyNxn-.js} +110 -110
- package/dist/dbCommand-C2XK63Dd.js +2 -0
- package/dist/{dev-D-JZ6CGQ.js → dev-8hTEnN6s.js} +2154 -2076
- package/dist/{dev-BZO5aLdr.js → dev-DyoYst4j.js} +1 -1
- package/dist/devActivity.d.ts +24 -0
- package/dist/{doctorCommand-BlUyESpD.js → doctorCommand-B6D7HH1-.js} +249 -228
- package/dist/doctorCommand-Btl2Jz1a.js +2 -0
- package/dist/doctorSourceRelations-CPjSwYuZ.js +83 -0
- package/dist/{dormancyCommand-lQxdrU8C.js → dormancyCommand-CrPAwOgo.js} +1 -1
- package/dist/{embeddingsCommand-ByBTju6X.js → embeddingsCommand-DsSxsn_b.js} +1 -1
- package/dist/{envCommand-CpCbYAgR.js → envCommand-B_NPVGUf.js} +2 -2
- package/dist/{evalCommand-6RUfPen4.js → evalCommand-Db01Kbng.js} +24 -24
- package/dist/{evolveCommand-CBV3-TtK.js → evolveCommand-DUuaCIyn.js} +2 -2
- package/dist/{frameworkTableAssembly-oMPBKqlE.js → frameworkTableAssembly-BPlPfU07.js} +82 -78
- package/dist/frameworkTableAssembly-Bob3vj9R.js +2 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +1 -1
- package/dist/{infoCommand-DJoA9cKD.js → infoCommand-CuNl9cbh.js} +1 -1
- package/dist/inspectState.d.ts +24 -0
- package/dist/interruptedReplace-C1ZI84xi.js +27 -0
- package/dist/interruptedReplace-DNJ7MKph.js +2 -0
- package/dist/{migrate-DN47p2PG.js → migrate-CM1qwalu.js} +27 -27
- package/dist/{runtimeTrace-DJ9tPhrc.js → runtimeTrace-BRNM06vK.js} +1 -1
- package/dist/{sdkgen-7dr8-Onw.js → sdkgen-CRCw_XcR.js} +1 -1
- package/dist/{seedRunner-IdHEprqf.js → seedRunner-Dgsiwk_e.js} +59 -59
- package/dist/serveCommand-BNHMVXyp.js +2 -0
- package/dist/serveCommand-CHOr74WM.js +1928 -0
- package/dist/serveEntry.d.ts +24 -0
- package/dist/serveEntry.js +1 -1
- package/dist/{start-Ds3xyn8R.js → start-7HcSIKEp.js} +1 -1
- package/dist/{start-BAlGcjF5.js → start-DvlYDyAr.js} +1 -1
- package/dist/startEntry.d.ts +24 -0
- package/dist/startEntry.js +1 -1
- package/dist/startup.d.ts +24 -0
- package/dist/{test-CLYXrZ2F.js → test-CHb7UwvE.js} +1 -1
- package/dist/{updateCommand-BvRidHp6.js → updateCommand-BlyXavoG.js} +1 -1
- package/dist/updateCommand-CT5AvVg7.js +2 -0
- package/dist/{webDev-101csg6Q.js → webDev-B4IEMB0n.js} +6 -6
- package/dist/webDev-B79ji8M5.js +2 -0
- package/dist/{webhooksCommand-BfO_JF2b.js → webhooksCommand-BRrG_sGf.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 +179 -3
- package/templates/agent-docs/data.md +103 -7
- package/templates/agent-docs/plugins/versioning.md +2 -0
- package/templates/agent-docs/whats-new.md +166 -6
- 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-XaW40W8x.js +0 -2
- package/dist/codegen-i8QGcHsi.js +0 -2
- package/dist/codegenCommand-iClXoRCW.js +0 -129
- package/dist/dbCommand-BvQeKQOb.js +0 -2
- package/dist/doctorCommand-BeLOwq7S.js +0 -2
- package/dist/frameworkTableAssembly-DgylYh9_.js +0 -2
- package/dist/serveCommand-BlnAlNQg.js +0 -1891
- package/dist/serveCommand-Dg_E1ev4.js +0 -2
- package/dist/updateCommand-DcKVmLVN.js +0 -2
- package/dist/webDev-BLKBO34K.js +0 -2
package/dist/serveEntry.d.ts
CHANGED
|
@@ -20,3 +20,27 @@ export declare const registerDriver: (spec: string, mod: Record<string, unknown>
|
|
|
20
20
|
export declare const runServe: (args: ReadonlyArray<string>) => Promise<number>;
|
|
21
21
|
|
|
22
22
|
export { }
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
declare module '@voltro/protocol' {
|
|
26
|
+
interface VoltroTableNames {
|
|
27
|
+
'tasks': true;
|
|
28
|
+
'task_sub_tasks': true;
|
|
29
|
+
'_voltro_workflow_runs': true;
|
|
30
|
+
'_voltro_workflow_run_steps': true;
|
|
31
|
+
'_voltro_workflow_run_events': true;
|
|
32
|
+
'_voltro_workflow_events': true;
|
|
33
|
+
'_voltro_workflow_event_deliveries': true;
|
|
34
|
+
'_voltro_approvals': true;
|
|
35
|
+
'_voltro_connections': true;
|
|
36
|
+
'_voltro_undo_log': true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
declare module '@voltro/protocol' {
|
|
42
|
+
interface VoltroTableNames {
|
|
43
|
+
'invoices': true;
|
|
44
|
+
'invoice_lines': true;
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/serveEntry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./loadEnv-D9nEOClM.js";
|
|
2
2
|
import { r as t } from "./appModuleLoader-C9r9mxZt.js";
|
|
3
3
|
import { i as n } from "./dialectDriver-czCHYpeH.js";
|
|
4
|
-
import { t as r } from "./serveCommand-
|
|
4
|
+
import { t as r } from "./serveCommand-CHOr74WM.js";
|
|
5
5
|
export { e as loadDotEnv, t as registerAppModules, n as registerDriver, r as runServe };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as e, t } from "./nodeEnvironment-cGFAj1J8.js";
|
|
2
2
|
import { r as n } from "./appPort-B_HpJ_ck.js";
|
|
3
|
-
import { A as r, C as i, D as a, E as o, F as s, H as c, I as l, J as ee, L as u, M as d, N as f, O as te, P as p, R as m, S as h, T as ne, V as g, W as re, X as _, Y as v, Z as y, a as b, b as x, d as S, f as C, ht as w, i as ie, j as T, k as E, n as D, nt as O, q as k, s as ae, tt as A, vt as j, x as oe, y as M, z as se } from "./webDev-
|
|
3
|
+
import { A as r, C as i, D as a, E as o, F as s, H as c, I as l, J as ee, L as u, M as d, N as f, O as te, P as p, R as m, S as h, T as ne, V as g, W as re, X as _, Y as v, Z as y, a as b, b as x, d as S, f as C, ht as w, i as ie, j as T, k as E, n as D, nt as O, q as k, s as ae, tt as A, vt as j, x as oe, y as M, z as se } from "./webDev-B4IEMB0n.js";
|
|
4
4
|
import { o as N, t as P } from "./connectionConfig-Bk9IC7D0.js";
|
|
5
5
|
import { A as F, D as I, a as ce, h as le, j as ue, k as de } from "./inspect-B7U7Cl_Z.js";
|
|
6
6
|
import { a as fe, c as L, d as pe, f as R, l as me, p as he, u as ge } from "./renderModeScan-D7J1B7Kw.js";
|
package/dist/startEntry.d.ts
CHANGED
|
@@ -3,3 +3,27 @@ export declare const loadDotEnv: (argv: ReadonlyArray<string>) => void;
|
|
|
3
3
|
export declare const runStartCommand: (args: ReadonlyArray<string>) => Promise<number>;
|
|
4
4
|
|
|
5
5
|
export { }
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
declare module '@voltro/protocol' {
|
|
9
|
+
interface VoltroTableNames {
|
|
10
|
+
'tasks': true;
|
|
11
|
+
'task_sub_tasks': true;
|
|
12
|
+
'_voltro_workflow_runs': true;
|
|
13
|
+
'_voltro_workflow_run_steps': true;
|
|
14
|
+
'_voltro_workflow_run_events': true;
|
|
15
|
+
'_voltro_workflow_events': true;
|
|
16
|
+
'_voltro_workflow_event_deliveries': true;
|
|
17
|
+
'_voltro_approvals': true;
|
|
18
|
+
'_voltro_connections': true;
|
|
19
|
+
'_voltro_undo_log': true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
declare module '@voltro/protocol' {
|
|
25
|
+
interface VoltroTableNames {
|
|
26
|
+
'invoices': true;
|
|
27
|
+
'invoice_lines': true;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/startEntry.js
CHANGED
package/dist/startup.d.ts
CHANGED
|
@@ -63,3 +63,27 @@ export declare interface StartupHandle {
|
|
|
63
63
|
export declare const startupTimeoutMsFromEnv: (envValue: string | undefined) => number;
|
|
64
64
|
|
|
65
65
|
export { }
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
declare module '@voltro/protocol' {
|
|
69
|
+
interface VoltroTableNames {
|
|
70
|
+
'tasks': true;
|
|
71
|
+
'task_sub_tasks': true;
|
|
72
|
+
'_voltro_workflow_runs': true;
|
|
73
|
+
'_voltro_workflow_run_steps': true;
|
|
74
|
+
'_voltro_workflow_run_events': true;
|
|
75
|
+
'_voltro_workflow_events': true;
|
|
76
|
+
'_voltro_workflow_event_deliveries': true;
|
|
77
|
+
'_voltro_approvals': true;
|
|
78
|
+
'_voltro_connections': true;
|
|
79
|
+
'_voltro_undo_log': true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
declare module '@voltro/protocol' {
|
|
85
|
+
interface VoltroTableNames {
|
|
86
|
+
'invoices': true;
|
|
87
|
+
'invoice_lines': true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -79,7 +79,7 @@ var d = l({ scope: "voltro:test" }), f = (e) => {
|
|
|
79
79
|
}, C = async (e) => {
|
|
80
80
|
let t = m(e);
|
|
81
81
|
if (!o(t.root)) return console.error(`voltro test: no such directory: ${t.root}`), 1;
|
|
82
|
-
let { checkRpcGroupFreshness: n } = await import("./codegen-
|
|
82
|
+
let { checkRpcGroupFreshness: n } = await import("./codegen-DcE2li1i.js"), r = await n(t.root);
|
|
83
83
|
if (r.kind === "stale") return console.error(`voltro test: ${r.file} is stale — ${r.reason}.`), console.error(" The tests would run against the previously generated procedure group, pass, and prove nothing about the descriptors you just edited.\n Run `voltro codegen` (or boot `voltro dev` once) and try again."), 1;
|
|
84
84
|
d.info("running vitest", {
|
|
85
85
|
root: t.root,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e, r as t, t as n } from "./codemodRunner-
|
|
1
|
+
import { n as e, r as t, t as n } from "./codemodRunner-DSZjgyCL.js";
|
|
2
2
|
import { basename as r, dirname as i, join as a, relative as o, resolve as s } from "node:path";
|
|
3
3
|
import { existsSync as c, readFileSync as l, readdirSync as u, statSync as d, writeFileSync as f } from "node:fs";
|
|
4
4
|
import { totalmem as p } from "node:os";
|
|
@@ -14,8 +14,8 @@ import { existsSync as j, promises as M, readFileSync as N, readdirSync as P, st
|
|
|
14
14
|
import { fileURLToPath as te, pathToFileURL as L } from "node:url";
|
|
15
15
|
import { addSink as R, createLogger as z, printReadyBlock as ne } from "@voltro/logger";
|
|
16
16
|
import { createRequire as B } from "node:module";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { currentTraceContext as V, resolveSecret as re } from "@voltro/runtime";
|
|
18
|
+
import { homedir as H } from "node:os";
|
|
19
19
|
import { FRAMEWORK_ENV_CATALOG as U, appEnvEntries as ie, checkEnvNaming as W, formatEnvErrors as ae, installEnvSnapshot as G, isEnvContract as oe, pluginEnvEntries as se, renderDotEnvExample as ce, resolveEnv as le } from "@voltro/env";
|
|
20
20
|
import { __installPublicEnv as ue } from "@voltro/env/public";
|
|
21
21
|
import { mkdir as de, writeFile as fe } from "node:fs/promises";
|
|
@@ -68,7 +68,7 @@ __installPublicEnv(publicEnvValues)
|
|
|
68
68
|
}, Ce = async (e) => {
|
|
69
69
|
let t = oe(e.rawEnv) ? e.rawEnv : void 0, n = await le({
|
|
70
70
|
contract: t,
|
|
71
|
-
resolveSecret:
|
|
71
|
+
resolveSecret: re
|
|
72
72
|
}), r = [...n.errors, ...W(t, e.appType)];
|
|
73
73
|
if (r.length > 0) throw Error(ae(r));
|
|
74
74
|
G(n.values), e.appType === "web" && ue(n.publicValues);
|
|
@@ -780,7 +780,7 @@ __installPublicEnv(publicEnvValues)
|
|
|
780
780
|
if (zt(t[i], a), !r) {
|
|
781
781
|
r = !0;
|
|
782
782
|
try {
|
|
783
|
-
let t = a.length > 0 ? It(a[0]) : "", r = t.length > 500 ? t.slice(0, 500) + "…" : t, o =
|
|
783
|
+
let t = a.length > 0 ? It(a[0]) : "", r = t.length > 500 ? t.slice(0, 500) + "…" : t, o = V();
|
|
784
784
|
e.emit("log", {
|
|
785
785
|
level: i,
|
|
786
786
|
message: r,
|
|
@@ -818,7 +818,7 @@ __installPublicEnv(publicEnvValues)
|
|
|
818
818
|
argsCount: 1,
|
|
819
819
|
...t.scope === void 0 ? {} : { scope: t.scope }
|
|
820
820
|
});
|
|
821
|
-
let a =
|
|
821
|
+
let a = V(), o = a !== void 0 && t.fields.traceId === void 0 ? {
|
|
822
822
|
...t.fields,
|
|
823
823
|
traceId: a.traceId
|
|
824
824
|
} : t.fields;
|
|
@@ -1190,7 +1190,7 @@ import {
|
|
|
1190
1190
|
a || (a = !0, i.destroy(), r(e));
|
|
1191
1191
|
};
|
|
1192
1192
|
i.once("connect", () => o(!0)), i.once("error", () => o(!1)), setTimeout(() => o(!1), n);
|
|
1193
|
-
}), Dn = () => !!(process.env.VOLTRO_DASHBOARD === "off" || process.argv.includes("--disable-dashboard")), On = () => process.env[Tn] === "1", kn = () => process.stdout.isTTY === !0, An = O(
|
|
1193
|
+
}), Dn = () => !!(process.env.VOLTRO_DASHBOARD === "off" || process.argv.includes("--disable-dashboard")), On = () => process.env[Tn] === "1", kn = () => process.stdout.isTTY === !0, An = O(H(), ".voltro", "dashboard-launch.lock"), jn = 1e4, Mn = async () => {
|
|
1194
1194
|
try {
|
|
1195
1195
|
await M.mkdir(D(An), { recursive: !0 });
|
|
1196
1196
|
let e = await M.open(An, "wx");
|
|
@@ -222,7 +222,7 @@ createVerifier({ secret: [process.env.WEBHOOK_SECRET, process.env.WEBHOOK_SECRET
|
|
|
222
222
|
...t === void 0 ? {} : { payload: t }
|
|
223
223
|
};
|
|
224
224
|
}, S = u({ scope: "voltro:webhooks" }), C = ["--out", "--name"], w = async (e) => {
|
|
225
|
-
let { walk: t, loadDiscovered: n } = await import("./dev-
|
|
225
|
+
let { walk: t, loadDiscovered: n } = await import("./dev-DyoYst4j.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-il9ti-HE.js");
|
|
226
226
|
return r((await n(await t(e))).events.map((e) => ({
|
|
227
227
|
file: e.file,
|
|
228
228
|
descriptor: e.descriptor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -640,6 +640,18 @@
|
|
|
640
640
|
"title": "`runImport` returns an ImportOutcome — read `.manifest` for the bundle",
|
|
641
641
|
"kind": "manual"
|
|
642
642
|
},
|
|
643
|
+
{
|
|
644
|
+
"version": "0.45.0",
|
|
645
|
+
"id": "0.45.0/01_typed-source-table-name",
|
|
646
|
+
"title": "`source:` is typechecked against the app’s tables, from the next `voltro dev`",
|
|
647
|
+
"kind": "manual"
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"version": "0.46.0",
|
|
651
|
+
"id": "0.46.0/01_typed-aggregate-source",
|
|
652
|
+
"title": "an aggregate’s `incremental.source` is typechecked against the app’s tables",
|
|
653
|
+
"kind": "manual"
|
|
654
|
+
},
|
|
643
655
|
{
|
|
644
656
|
"version": "0.5.0",
|
|
645
657
|
"id": "0.5.0/01_one-terminal-exactly-one",
|
|
@@ -727,22 +739,22 @@
|
|
|
727
739
|
"@effect/platform-node": "^0.108.0",
|
|
728
740
|
"@effect/sql": "^0.52.0",
|
|
729
741
|
"@effect/workflow": "^0.19.0",
|
|
730
|
-
"@voltro/ai": "0.
|
|
731
|
-
"@voltro/cache": "0.
|
|
732
|
-
"@voltro/data-transfer": "0.
|
|
733
|
-
"@voltro/database": "0.
|
|
734
|
-
"@voltro/env": "0.
|
|
735
|
-
"@voltro/kv": "0.
|
|
736
|
-
"@voltro/logger": "0.
|
|
737
|
-
"@voltro/plugin-auth": "0.
|
|
738
|
-
"@voltro/plugin-broadcast": "0.
|
|
739
|
-
"@voltro/plugin-mail": "0.
|
|
740
|
-
"@voltro/plugin-storage": "0.
|
|
741
|
-
"@voltro/plugin-webhooks": "0.
|
|
742
|
-
"@voltro/protocol": "0.
|
|
743
|
-
"@voltro/runtime": "0.
|
|
744
|
-
"@voltro/serverless": "0.
|
|
745
|
-
"@voltro/workflow": "0.
|
|
742
|
+
"@voltro/ai": "0.46.0",
|
|
743
|
+
"@voltro/cache": "0.46.0",
|
|
744
|
+
"@voltro/data-transfer": "0.46.0",
|
|
745
|
+
"@voltro/database": "0.46.0",
|
|
746
|
+
"@voltro/env": "0.46.0",
|
|
747
|
+
"@voltro/kv": "0.46.0",
|
|
748
|
+
"@voltro/logger": "0.46.0",
|
|
749
|
+
"@voltro/plugin-auth": "0.46.0",
|
|
750
|
+
"@voltro/plugin-broadcast": "0.46.0",
|
|
751
|
+
"@voltro/plugin-mail": "0.46.0",
|
|
752
|
+
"@voltro/plugin-storage": "0.46.0",
|
|
753
|
+
"@voltro/plugin-webhooks": "0.46.0",
|
|
754
|
+
"@voltro/protocol": "0.46.0",
|
|
755
|
+
"@voltro/runtime": "0.46.0",
|
|
756
|
+
"@voltro/serverless": "0.46.0",
|
|
757
|
+
"@voltro/workflow": "0.46.0",
|
|
746
758
|
"chokidar": "^5.0.0",
|
|
747
759
|
"ioredis": "^5.11.1",
|
|
748
760
|
"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.46.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
711
711
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
712
712
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
713
713
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -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.46.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
13
13
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
14
14
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
15
15
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -1377,6 +1377,22 @@ Docker step) and prints the exact remedy: add a `voltro build .` step before
|
|
|
1377
1377
|
`voltro serve .`. Drop it into your image build right after `voltro build` to
|
|
1378
1378
|
guarantee the artefact is present before the image ships.
|
|
1379
1379
|
|
|
1380
|
+
### Relations a query loads but does not declare
|
|
1381
|
+
|
|
1382
|
+
An eager-loaded relation is part of the RESULT, so its table has to be in `source:` or the view stops updating when it changes:
|
|
1383
|
+
|
|
1384
|
+
```
|
|
1385
|
+
✗ 1 query loads a relation it does not declare:
|
|
1386
|
+
tasks.getById: eager-loads `subTasks` from 'tasks' but does not declare
|
|
1387
|
+
'task_sub_tasks' in `source:` — the view will not update when 'task_sub_tasks' changes.
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
This is the failure that looks like a broken feature and is not: the write lands, a reload shows it, and every test of the write path is green. The name in `source:` is spelled right and the table exists, so neither the typed `source:` nor the boot audit has anything to say.
|
|
1391
|
+
|
|
1392
|
+
The rule has no exception list, deliberately. It resolves `.with({ … })` keys through the relation registry, so the missing table is a fact rather than an inference — and a **many-to-many** is reported twice when needed, because the junction table is where a link add/remove actually writes.
|
|
1393
|
+
|
|
1394
|
+
It reads what it can read literally: a computed `.with()` key yields nothing rather than a guess.
|
|
1395
|
+
|
|
1380
1396
|
### The access-decision gate
|
|
1381
1397
|
|
|
1382
1398
|
Before the authz scan below — which is a heuristic over executor SOURCE — doctor
|
|
@@ -2594,11 +2610,25 @@ Rows are written as newline-delimited JSON — streamable, resumable by line, an
|
|
|
2594
2610
|
|
|
2595
2611
|
| Flag | Selects |
|
|
2596
2612
|
|---|---|
|
|
2597
|
-
| *(none)* | Every table (the default). |
|
|
2613
|
+
| *(none)* | Every table the app has DATA in (the default) — see the note below on the handful that describe a deployment rather than filling it. |
|
|
2598
2614
|
| `--tenant <id>` | Every `tenant()`-scoped table filtered to that tenant, **plus** the FK closure in BOTH directions: (1) the transitive **FK-parent** closure of those rows — closure-pulled shared tables (a global `users` / reference table) are **row-subset to the ids the tenant's rows actually reference**, never exported in full; and (2) the **child** closure — rows that *reference* the tenant's rows (the comments on the tenant's projects) come along too, each scoped to the ids that actually point into the tenant set. The child walk is anchored on the `tenant()` tables, so a row that references **only a shared parent** (a global `users` another tenant also references) is **not** pulled — that would be a cross-tenant leak. A `--tenant` bundle therefore carries the tenant's parents AND children and **no other tenant's rows** — that's what makes it safe as a GDPR / per-tenant takeout. |
|
|
2599
2615
|
| `--tables a,b` | An explicit set (you own referential integrity; the importer's deferred-FK resolution covers load-order dangles, see below). |
|
|
2600
2616
|
| `--exclude a,b` | Everything EXCEPT these, expanded against the live table list. Direct target only. Cannot be combined with `--tables` or `--tenant`; a name that does not exist is refused, because an exclusion that excludes nothing leaves the run looking like it worked. |
|
|
2601
2617
|
|
|
2618
|
+
#### What `all` deliberately leaves out
|
|
2619
|
+
|
|
2620
|
+
A few framework tables hold rows **about a deployment** rather than an app's data — its migration ledger, its file-migration and seed records, its CDC offsets, its schedule claims and wakeups, its workflow watermarks / pending starts / admissions / pauses. They are dropped from `all`, skipped on import, and never emptied by a `replace`, and the run says which ones and why.
|
|
2621
|
+
|
|
2622
|
+
The reason is worth one paragraph, because it cost a real environment ninety minutes. A `scope: all` bundle carried `_voltro_migration_plans`, `replace` wrote it, and the target's next boot refused:
|
|
2623
|
+
|
|
2624
|
+
```
|
|
2625
|
+
auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=6e2c61081a9ed80c live=28af9a54414f22f1
|
|
2626
|
+
```
|
|
2627
|
+
|
|
2628
|
+
The refusal was right. That fingerprint is computed over the **declared table set**, which legitimately differs per environment — `NODE_ENV=production` declares `_voltro_traces` and `_voltro_undo_log`, a `development` run does not. So the imported row was not out of date, it was **foreign**: it stated a schema decision made somewhere else. Two of the others would have made the target *act* — a pending workflow start runs a workflow somebody queued elsewhere, a pause silently stops one here.
|
|
2629
|
+
|
|
2630
|
+
`all` is the only scope filtered. **Name one of these in `--tables` and you get it** — an explicit name is an expectation, and this command refuses to drop those silently.
|
|
2631
|
+
|
|
2602
2632
|
**Every table needs a single-column primary key.** The export is keyset-paginated, so it orders by one column and resumes from the last value on the next page. That column comes from the real primary key — a declared `id()` where there is one, otherwise the table's actual PK, whatever it is called.
|
|
2603
2633
|
|
|
2604
2634
|
A table with a **composite** primary key, or none at all, is **refused by name** rather than exported. Ordering by one column of a composite key splits equal values across page boundaries, which drops or duplicates rows into a bundle that reports success — and a short backup is discovered at the restore. Leave such a table out with `--exclude`.
|
|
@@ -2737,6 +2767,128 @@ returns **`409` schema drift** with the fingerprints + diff; `--force` sends
|
|
|
2737
2767
|
|
|
2738
2768
|
The pre-flight only runs when the importer has a target schema (the CLI introspects it; the API endpoint uses the instance's declared schema). Importing into a fresh/empty database with no comparable schema simply skips the check.
|
|
2739
2769
|
|
|
2770
|
+
### `replace` writes down what it is about to destroy
|
|
2771
|
+
|
|
2772
|
+
Before the first delete, a `replace` exports the target's **current** rows for exactly the tables it is going to empty, as an ordinary bundle beside yours:
|
|
2773
|
+
|
|
2774
|
+
```
|
|
2775
|
+
rollback capture: 240172 row(s) across 75 table(s) → ./out.rollback-2026-08-21T09-10-11-000Z
|
|
2776
|
+
If this run does not finish, restore with: voltro data import ./out.rollback-… --mode replace
|
|
2777
|
+
```
|
|
2778
|
+
|
|
2779
|
+
It is on disk **before** anything is destroyed, so it does not depend on a transaction, or on the process being alive to roll one back. That distinction is the whole reason it exists: a deployment lost 240 172 rows to a `replace` whose api pod disappeared nine minutes in, and recovered from an export they had taken twenty minutes earlier out of habit. This is that habit, made into the tool's behaviour.
|
|
2780
|
+
|
|
2781
|
+
**It is fail-closed.** A capture that cannot be taken stops the import before it starts, and the target is untouched. A safety net you believe in and do not have is worse than none — the belief is what stops you taking your own export.
|
|
2782
|
+
|
|
2783
|
+
`--no-rollback` turns it off, and `--rollback-dir <path>` puts it somewhere else. It is only taken for `replace`: `upsert` and `append` do not destroy, so there is no moment where the old state has silently become unreachable.
|
|
2784
|
+
|
|
2785
|
+
**Over `--target api` it goes to the instance's object storage**, because there the process that would roll a transaction back IS the instance — a capture in the pod's filesystem would go away with the failure it exists for. Name a key:
|
|
2786
|
+
|
|
2787
|
+
```bash
|
|
2788
|
+
voltro data import ./out --target api --api-url <url> --token $SECRET --mode replace --rollback-key backups/before.vbundle
|
|
2789
|
+
```
|
|
2790
|
+
|
|
2791
|
+
The instance writes the capture there **before the first delete**, and refuses the run (409) if it has no storage configured — asking for a capture and being served without one is the answer that removes your own precaution while looking like agreement. A `replace` that names no key still runs, and says what it did not keep.
|
|
2792
|
+
|
|
2793
|
+
### `replace` loads somewhere else first
|
|
2794
|
+
|
|
2795
|
+
The target keeps its rows until the load stands. `replace` creates a staging
|
|
2796
|
+
table per table, loads the bundle into those, and then swaps the CONTENT across
|
|
2797
|
+
in one short transaction of server-side SQL. It says so when it does:
|
|
2798
|
+
|
|
2799
|
+
```
|
|
2800
|
+
staging 114 table(s) before the swap — the target keeps its rows until the load stands.
|
|
2801
|
+
The destructive step is one server-side transaction at the end, not the whole load.
|
|
2802
|
+
```
|
|
2803
|
+
|
|
2804
|
+
The difference is what a dead process costs. Loading straight into the target
|
|
2805
|
+
holds the destructive transaction open for the whole load — minutes, for a large
|
|
2806
|
+
bundle — and the target only survives because the database rolls that
|
|
2807
|
+
transaction back. With staging, a process that dies during the load leaves the
|
|
2808
|
+
target exactly as it was, because nothing has been deleted yet.
|
|
2809
|
+
|
|
2810
|
+
Not every run can take it, and a run that cannot **says why** rather than quietly
|
|
2811
|
+
taking the slower path:
|
|
2812
|
+
|
|
2813
|
+
- a store the framework cannot send DDL to (the in-memory store).
|
|
2814
|
+
- a **write recorder** on any table in the set — `versioningPlugin({ timing:
|
|
2815
|
+
'in-transaction' })` and friends. A recorder is keyed by table name, so a
|
|
2816
|
+
staged write would find none and the recorder would silently not run. Its
|
|
2817
|
+
promise is "if the change committed, the entry is there", so the run keeps the
|
|
2818
|
+
path that can keep it.
|
|
2819
|
+
|
|
2820
|
+
**`--no-atomic` stages too, and that is where it changes the most.** The flag
|
|
2821
|
+
exists for resumability on a large bundle, and it used to be the mode with the
|
|
2822
|
+
worst failure: the target emptied and partially refilled, in neither state.
|
|
2823
|
+
Staged, the ledger keeps its exact meaning — a recorded table is one fully
|
|
2824
|
+
loaded, it just lands in staging — while the target stays untouched until the
|
|
2825
|
+
swap. Resumable and all-or-nothing at once, which the two could not be before.
|
|
2826
|
+
|
|
2827
|
+
A resumed run continues from what it already staged rather than reloading it,
|
|
2828
|
+
and a run whose swap has not happened keeps its staged rows and says so:
|
|
2829
|
+
|
|
2830
|
+
```
|
|
2831
|
+
the staged rows are KEPT so a re-run can continue from them rather than reload.
|
|
2832
|
+
If you are not going to re-run this bundle, drop them: voltro data clear-staging --yes
|
|
2833
|
+
```
|
|
2834
|
+
|
|
2835
|
+
**A failed swap names every offending row, not the first.** Staging carries no
|
|
2836
|
+
foreign keys — a staged row whose parent has not been staged yet must not be
|
|
2837
|
+
refused — so a dangling reference surfaces at the swap, where the database
|
|
2838
|
+
reports one constraint. The importer then asks staging the same question and
|
|
2839
|
+
lists every row that fails it:
|
|
2840
|
+
|
|
2841
|
+
```
|
|
2842
|
+
the swap could not run: 2 row(s) in the bundle reference a row the bundle does not carry.
|
|
2843
|
+
tasks.t_41: ownerId = "u_9" — no such row in users
|
|
2844
|
+
tasks.t_88: ownerId = "u_12" — no such row in users
|
|
2845
|
+
The target is UNCHANGED — the swap runs in one transaction and none of it committed.
|
|
2846
|
+
```
|
|
2847
|
+
|
|
2848
|
+
Staging tables from a run that died mid-load are collected by the next
|
|
2849
|
+
`replace` over the same tables. One over a DIFFERENT set leaves them, and
|
|
2850
|
+
nothing else removes them:
|
|
2851
|
+
|
|
2852
|
+
```bash
|
|
2853
|
+
voltro data clear-staging --yes
|
|
2854
|
+
```
|
|
2855
|
+
|
|
2856
|
+
Deliberately a command and not a boot sweep: a booting process cannot tell a
|
|
2857
|
+
leftover from a staging table another replica is loading into right now, and
|
|
2858
|
+
deleting the second would destroy an import in flight.
|
|
2859
|
+
|
|
2860
|
+
A **cycle** in the bundle's foreign keys is detected before the load, not after
|
|
2861
|
+
it. The swap inserts parents first, so two tables referencing each other cannot
|
|
2862
|
+
both be satisfied by a bulk copy on postgres, sqlite or SQL Server — `SET
|
|
2863
|
+
CONSTRAINTS ALL DEFERRED` does not help, because postgres only defers a
|
|
2864
|
+
constraint declared `DEFERRABLE`. Such a run says so and takes the row-by-row
|
|
2865
|
+
path, whose deferred-FK pass exists for exactly that shape. A table referencing
|
|
2866
|
+
ITSELF is not a cycle: one statement carries the whole table.
|
|
2867
|
+
|
|
2868
|
+
### An interrupted `replace` cannot be silent
|
|
2869
|
+
|
|
2870
|
+
The capture only helps if somebody knows to reach for it. A half-replaced database is indistinguishable from an empty one **from the inside** — every table exists, every constraint holds, every query returns nothing without erroring — so a run that emptied a target and disappeared can be served over for hours before anyone asks the right question.
|
|
2871
|
+
|
|
2872
|
+
So a `replace` writes one row before the first delete and removes it after the last insert. Finding it at boot is a **refusal**, not a warning:
|
|
2873
|
+
|
|
2874
|
+
```
|
|
2875
|
+
refusing to start: a destructive import did not finish.
|
|
2876
|
+
- a `replace` over api began emptying 114 table(s) 12 minute(s) ago and never reported finishing.
|
|
2877
|
+
The target's previous rows were captured first:
|
|
2878
|
+
./out.rollback-2026-08-21T09-10-11-000Z
|
|
2879
|
+
Restore them with: voltro data import ./out.rollback-… --mode replace
|
|
2880
|
+
```
|
|
2881
|
+
|
|
2882
|
+
The row lives in the same transaction as the emptying, so it is present **exactly when the emptying is**: a run that rolls back cleanly takes the marker with it, and a boot over a database nothing happened to is not refused. A completed `replace` clears its own marker and any older one — so the recovery import both restores the data and silences the alarm, in one command.
|
|
2883
|
+
|
|
2884
|
+
Nothing expires. A half-replaced database does not become whole with time, so clearing it is a decision:
|
|
2885
|
+
|
|
2886
|
+
```bash
|
|
2887
|
+
voltro data clear-replace-marker --yes
|
|
2888
|
+
```
|
|
2889
|
+
|
|
2890
|
+
The devtools import panel takes the same two precautions as the command line — a capture before the first delete, and the marker — because a button is easier to press than a command is to type.
|
|
2891
|
+
|
|
2740
2892
|
### Deferred-FK resolution
|
|
2741
2893
|
|
|
2742
2894
|
A row whose write fails on a foreign-key constraint — a forward reference from a `live`-consistency export, a genuine FK **cycle** between tables (which the exporter orders by breaking the closing edge), or an intra-table self-reference to a later row — does **not** fail the import. It is held and resolved after every table has streamed:
|
|
@@ -2757,7 +2909,21 @@ The error carries three things, and they answer different questions:
|
|
|
2757
2909
|
| `byTable` | **complete** per-table counts (`refused`, `primary`), worst first. |
|
|
2758
2910
|
| `rows` | up to 20 refused rows — primary ones first — each with `table`, `id`, `reason`. |
|
|
2759
2911
|
|
|
2760
|
-
`rows` is **capped and `byTable` is not**, and that distinction is worth one sentence: tallying the tables in the printed list answers "how big is the cap", not "which tables failed". The CLI prints the `byTable` line above the list and says so when the list is truncated
|
|
2912
|
+
`rows` is **capped and `byTable` is not**, and that distinction is worth one sentence: tallying the tables in the printed list answers "how big is the cap", not "which tables failed". The CLI prints the `byTable` line above the list and says so when the list is truncated:
|
|
2913
|
+
|
|
2914
|
+
```
|
|
2915
|
+
import refused 35 row(s), of which 35 are the actual failures — the rest could not land because a row they reference did not.
|
|
2916
|
+
by table: vacations 26, weeklyUpdates 9
|
|
2917
|
+
vacations v1 not-null constraint userId: the column requires a value [ER_BAD_NULL_ERROR/1048]
|
|
2918
|
+
…
|
|
2919
|
+
(the list above is capped at 20 of 35 — the counts by table are complete)
|
|
2920
|
+
```
|
|
2921
|
+
|
|
2922
|
+
A refused import ends with a **non-zero exit status and that report** — not with a stack trace. A refusal is a condition with a named cause, not a framework defect, so it is not dressed as one. The report is the same on both transports: over `--target api` the refusal crosses as a 500 whose message embeds it, and the CLI renders that rather than printing the body raw.
|
|
2923
|
+
|
|
2924
|
+
**`RowsRefusedError` keeps its tag in every mode, `--atomic` included.** That is worth stating because it is the mode `replace` uses by default: the whole table phase runs in one transaction, and rolling that transaction back needs a rejection — but the rejection carries the typed error, not a rendering of it. So `Effect.catchTag('RowsRefusedError', …)` works on the default path, which is the one most likely to raise one.
|
|
2925
|
+
|
|
2926
|
+
One trap if you consume `runImport` yourself: what `Effect.runPromise` **rejects** with is a `FiberFailure`, and `err._tag` on one of those reads `undefined` however good the error inside is. Use `Effect.catchTag` on the effect, or `asImportError(err)` (exported from `@voltro/data-transfer`) on the rejection — reading the tag off the caught value takes the "not my error" branch every time.
|
|
2761
2927
|
|
|
2762
2928
|
A row's `reason` is stated as precisely as the driver allows, in three tiers:
|
|
2763
2929
|
|
|
@@ -2773,7 +2939,7 @@ Unlike the rpc wire, this string DOES include the driver's own words. The audien
|
|
|
2773
2939
|
|---|---|---|---|
|
|
2774
2940
|
| `upsert` (default) | INSERT-or-UPDATE by primary key | sync / idempotent re-import | overwrites per row |
|
|
2775
2941
|
| `append` | INSERT only | additive data (event log, new seed) | `--on-conflict skip` (default) or `fail` |
|
|
2776
|
-
| `replace` |
|
|
2942
|
+
| `replace` | capture the target, empty it, then INSERT | full refresh — target ends up **exactly** the bundle | — |
|
|
2777
2943
|
|
|
2778
2944
|
`replace` **refuses a partial bundle** (a subset / tenant / table scope): emptying would delete rows the bundle never carried. Re-export with full scope, or use `upsert`. (`append --on-conflict fail` throws on a duplicate primary key only where the store enforces the constraint — every SQL dialect does; the in-memory dev store overwrites.)
|
|
2779
2945
|
|
|
@@ -2898,6 +3064,16 @@ voltro data import ./out --target api --api-url https://api.example --token $SEC
|
|
|
2898
3064
|
|
|
2899
3065
|
Note the asymmetry when a deadline *is* hit: re-running an `upsert` after the first run has finished is safe (it is idempotent); re-running a **`replace`** while the first is still mid-flight would empty the target under it. Check the instance's log before deciding — the message says so too.
|
|
2900
3066
|
|
|
3067
|
+
An instance that is **not answering at all** is a different message, and it names the instance rather than a database:
|
|
3068
|
+
|
|
3069
|
+
```
|
|
3070
|
+
voltro: api import: the instance at api.example.com is not answering (ETIMEDOUT).
|
|
3071
|
+
That address came from --api-url (or VOLTRO_API_URL) — it is the running instance, not the database.
|
|
3072
|
+
Nothing was sent, so nothing was imported. …
|
|
3073
|
+
```
|
|
3074
|
+
|
|
3075
|
+
Worth one line because the alternative was measured: a connect failure carries an address, a port and an errno, which is the same shape a database driver's carries — so without this the api host was reported as an unreachable *database*, attributed to `DB_URL`, a variable not in play on that run. Every word after the address was wrong, and the address being right is what made it convincing.
|
|
3076
|
+
|
|
2901
3077
|
**Import** (`POST /_voltro/admin/import`) applies a bundle **in the instance's own process** through its store — full app pipeline (validation, mixins, **field encryption**, hooks) AND automatic reactivity. Two transports, neither needs a server-readable path:
|
|
2902
3078
|
- **upload** (default) — the CLI **packs the bundle and uploads the bytes**; the server unpacks to its own temp dir, imports in-process, cleans up. Buffered → small/moderate bundles.
|
|
2903
3079
|
- **storage-pull** (`--bundle-key <key>`) — the CLI sends only `{ "bundleKey": "<key>" }`; the instance **streams that archive from its configured object storage** (S3 / Azure / MinIO / filesystem, resolved from the storage env — `STORAGE_PROVIDER`, `S3_*`, `AZURE_*`, …), no body buffering → arbitrarily large bundles.
|