@voltro/cli 0.23.0 → 0.25.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 +537 -0
- package/THIRD-PARTY-NOTICES.md +2 -2
- package/bin/voltro.mjs +39 -4
- package/dist/{apiBuild-JQtIhZPy.js → apiBuild-BqhCSytw.js} +46 -24
- package/dist/apiBuild-N1R4V792.js +2 -0
- package/dist/bin.js +3 -3
- package/dist/{commands-Bsu9Buln.js → commands-7EmYJ9Xg.js} +2796 -2478
- package/dist/dbCommand-CIrdFLp9.js +2 -0
- package/dist/{dbCommand-GI7-BVZt.js → dbCommand-FUU12FvD.js} +285 -246
- package/dist/{dev-DRxF_qOB.js → dev-BvHT7WZa.js} +1 -1
- package/dist/{dev-jM07kq_D.js → dev-MacSQ1Ll.js} +2042 -1631
- package/dist/fileConventions-3bffWssN.js +30 -0
- package/dist/frameworkTableAssembly-BsnCKzQ6.js +2 -0
- package/dist/{frameworkTableAssembly-sg41s14F.js → frameworkTableAssembly-Cw5zJz6n.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/inspect-C9gjHwBk.js +2 -0
- package/dist/{inspect-_ldwsAwH.js → inspect-DuLUrZp9.js} +14 -1
- package/dist/{inspectMetrics-dcC7gnxu.js → inspectMetrics-EQwH7BI4.js} +15 -15
- package/dist/manifestBuild-BVwS1Z_6.js +2 -0
- package/dist/{manifestBuild-Bs1Uw22_.js → manifestBuild-Dneq4_Jx.js} +1 -1
- package/dist/serveCommand-5ZFiNO1R.js +1241 -0
- package/dist/serveEntry.js +2 -2
- package/dist/{start-C_4VUDT8.js → start-C-ZWSDpg.js} +2 -2
- package/dist/startEntry.js +2 -2
- package/package.json +17 -17
- package/templates/AGENTS.core.md +12 -5
- package/templates/AGENTS.md +14 -7
- package/templates/agent-docs/_index.md +2 -2
- package/templates/agent-docs/_manifest.json +3 -3
- package/templates/agent-docs/authentication.md +21 -0
- package/templates/agent-docs/cli.md +32 -0
- package/templates/agent-docs/data.md +491 -167
- package/templates/agent-docs/database/migrations.md +132 -63
- package/templates/agent-docs/database/schema.md +80 -0
- package/templates/agent-docs/database/seedsdialects.md +12 -0
- package/templates/agent-docs/deployment.md +75 -1
- package/templates/agent-docs/plugins.md +1 -1
- package/templates/agent-docs/reference.md +2 -2
- package/templates/agent-docs/security.md +18 -0
- package/templates/agent-docs/templates/apibackends.md +5 -5
- package/templates/agent-docs/templates/overview.md +1 -1
- package/templates/agent-docs/whats-new.md +263 -169
- package/templates/agent-docs/workflows.md +74 -20
- package/templates/apps/api-ai/package.json +8 -7
- package/templates/apps/api-auth/package.json +9 -8
- package/templates/apps/api-backend/package.json +8 -7
- package/templates/apps/api-backend-deactivation/package.json +8 -7
- package/templates/apps/api-backend-mail/package.json +9 -8
- package/templates/apps/api-backend-mariadb/package.json +10 -9
- package/templates/apps/api-backend-storage/package.json +9 -8
- package/templates/apps/api-data-advanced/package.json +9 -8
- package/templates/apps/api-durable/README.md +2 -1
- package/templates/apps/api-durable/events/orders.event.ts +18 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.server.ts +17 -15
- package/templates/apps/api-durable/package.json +9 -8
- package/templates/apps/api-durable/triggers/order.placed.trigger.tsx +6 -4
- package/templates/apps/api-feature-flags/package.json +10 -9
- package/templates/apps/api-governance/package.json +9 -8
- package/templates/apps/api-kv/package.json +9 -8
- package/templates/apps/api-moderation/package.json +9 -8
- package/templates/apps/api-observability/package.json +9 -8
- package/templates/apps/api-ratelimit/package.json +9 -8
- package/templates/apps/api-rbac/package.json +9 -8
- package/templates/apps/api-rest/package.json +8 -7
- package/templates/apps/api-saas/package.json +12 -11
- package/templates/apps/api-search/package.json +9 -8
- package/templates/apps/api-versioning/package.json +9 -8
- package/templates/apps/api-webhooks/README.md +2 -2
- package/templates/apps/api-webhooks/events/orders.event.ts +35 -0
- package/templates/apps/api-webhooks/package.json +10 -9
- package/templates/apps/api-webhooks/template.json +1 -1
- package/templates/apps/changelog/package.json +7 -6
- package/templates/apps/edge-functions/package.json +3 -2
- package/templates/apps/frontend-admin/package.json +9 -8
- package/templates/apps/frontend-app/package.json +9 -8
- package/templates/apps/frontend-blank/package.json +8 -7
- package/templates/apps/frontend-contact/package.json +8 -7
- package/templates/apps/frontend-contact/src/globals.d.ts +6 -0
- package/templates/apps/frontend-dashboard/package.json +8 -7
- package/templates/apps/frontend-docs/package.json +8 -7
- package/templates/apps/frontend-docs/src/globals.d.ts +6 -0
- package/templates/apps/frontend-i18n/package.json +7 -6
- package/templates/apps/frontend-landing/package.json +8 -7
- package/templates/apps/frontend-landing/src/globals.d.ts +6 -0
- package/templates/apps/frontend-spa/package.json +8 -7
- package/templates/apps/frontend-spa/src/globals.d.ts +6 -0
- package/templates/apps/frontend-ssr/package.json +8 -7
- package/templates/apps/frontend-ssr/src/globals.d.ts +6 -0
- package/templates/apps/frontend-ssr-api/package.json +9 -8
- package/templates/apps/frontend-static-blog/package.json +7 -6
- package/templates/apps/frontend-static-blog/src/globals.d.ts +6 -0
- package/dist/apiBuild-CD-4JLLA.js +0 -2
- package/dist/dbCommand-CSguuUQn.js +0 -2
- package/dist/fileConventions-CBNPZVOV.js +0 -29
- package/dist/frameworkTableAssembly-DRJrAVr1.js +0 -2
- package/dist/inspect-BA67TF6v.js +0 -2
- package/dist/manifestBuild-i-fRHg_H.js +0 -2
- package/dist/serveCommand-Cl8sicD5.js +0 -1162
- package/templates/apps/api-webhooks/events/order.completed.webhook.tsx +0 -22
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as e, n as t, r as n, s as r, t as i } from "./seedRunner-D6eu-u5U.js";
|
|
2
2
|
import { n as a } from "./appModuleLoader-C9r9mxZt.js";
|
|
3
|
-
import { t as o } from "./frameworkTableAssembly-
|
|
4
|
-
import {
|
|
3
|
+
import { t as o } from "./frameworkTableAssembly-Cw5zJz6n.js";
|
|
4
|
+
import { _ as s } from "./fileConventions-3bffWssN.js";
|
|
5
5
|
import { a as c, n as l, t as u } from "./cliArgs-qdZSElM3.js";
|
|
6
6
|
import { basename as d, join as f, relative as p, resolve as m } from "node:path";
|
|
7
7
|
import { pathToFileURL as h } from "node:url";
|
|
@@ -10,17 +10,17 @@ import { createLogger as _ } from "@voltro/logger";
|
|
|
10
10
|
import { promises as v, writeSync as y } from "node:fs";
|
|
11
11
|
import { InMemoryDataStore as ee } from "@voltro/runtime";
|
|
12
12
|
import { SqlClient as b } from "@effect/sql";
|
|
13
|
-
import { actorsTable as te, frameworkTables as x, isMigrationDefinition as
|
|
14
|
-
import { createHash as
|
|
15
|
-
import { applyPlan as
|
|
13
|
+
import { actorsTable as te, frameworkTables as x, isMigrationDefinition as S, quoteIdent as ne, registerCoreTables as re } from "@voltro/database";
|
|
14
|
+
import { createHash as ie } from "node:crypto";
|
|
15
|
+
import { DEFAULT_CDC_CHANNEL as ae, applyPlan as C, applySchema as w, assessRollingDeployGate as oe, declaredSnapshot as se, destructiveScope as ce, detectReactiveTriggerDrift as le, fingerprintSchema as T, ignoreTablesFromEnv as E, introspectSchema as D, pendingFileMigrationIds as ue, planMigrations as O, reactiveTriggerRepairSql as de, rollbackFileBasedMigration as fe, rollingDeployUnsafeOps as pe, runFileBasedMigrations as me, shortFingerprint as k, squashMigrationPlans as he, unblockLossy as ge } from "@voltro/database/sql";
|
|
16
16
|
//#region src/cliError.ts
|
|
17
|
-
var
|
|
17
|
+
var _e = class extends Error {
|
|
18
18
|
isCliError = !0;
|
|
19
19
|
code;
|
|
20
20
|
constructor(e, t = 1) {
|
|
21
21
|
super(e), this.name = "CliError", this.code = t;
|
|
22
22
|
}
|
|
23
|
-
},
|
|
23
|
+
}, ve = (e) => typeof e == "object" && !!e && e.isCliError === !0, ye = (e) => typeof e != "string", be = (e, t, n, r) => {
|
|
24
24
|
let i = [], a = new Map(t.map((e) => [e.name, e])), o = new Map(n.map((e) => [e.name, e])), s = new Set(e.leaveUnset ?? []);
|
|
25
25
|
r > 0 && i.push({
|
|
26
26
|
kind: "target-not-empty",
|
|
@@ -37,7 +37,7 @@ var O = class extends Error {
|
|
|
37
37
|
});
|
|
38
38
|
continue;
|
|
39
39
|
}
|
|
40
|
-
if (
|
|
40
|
+
if (ye(n)) {
|
|
41
41
|
c.push({
|
|
42
42
|
target: t,
|
|
43
43
|
source: n.expr,
|
|
@@ -80,23 +80,23 @@ var O = class extends Error {
|
|
|
80
80
|
remintIds: f,
|
|
81
81
|
fatal: i.some((e) => e.fatal)
|
|
82
82
|
};
|
|
83
|
-
},
|
|
83
|
+
}, xe = (e, t) => [
|
|
84
84
|
`back up '${e.from}' to a restorable dump`,
|
|
85
85
|
`copy rows: INSERT INTO ${e.into} (${t.assignments.map((e) => e.target).join(", ")}) SELECT … FROM ${e.from}`,
|
|
86
86
|
...t.remintIds ? ["record old id → new id in a translation table", `rewrite every reference to '${e.from}', JSON columns included`] : [],
|
|
87
87
|
"verify: row counts match and a sample round-trips",
|
|
88
88
|
`drop '${e.from}' — LAST, and only after the verify`
|
|
89
|
-
],
|
|
89
|
+
], Se = (e, t, n) => {
|
|
90
90
|
let r = [`adopt · ${e.from} (${n} row(s)) → ${e.into}`, ""];
|
|
91
91
|
for (let e of t.assignments) r.push(` ${e.target.padEnd(24)} ← ${e.raw ? `${e.source} (raw)` : e.source}`);
|
|
92
92
|
t.problems.length > 0 && r.push("");
|
|
93
93
|
for (let e of t.problems) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.detail}`);
|
|
94
94
|
if (r.push(""), t.fatal) return r.push(" REFUSED — fix the problems above. Nothing was written."), r;
|
|
95
95
|
r.push(" would run, in this order:");
|
|
96
|
-
for (let [n, i] of
|
|
96
|
+
for (let [n, i] of xe(e, t).entries()) r.push(` ${n + 1}. ${i}`);
|
|
97
97
|
return r;
|
|
98
|
-
},
|
|
99
|
-
let a =
|
|
98
|
+
}, Ce = (e) => e === "mysql" || e === "mariadb" ? (e) => `\`${e}\`` : e === "mssql" ? (e) => `[${e}]` : (e) => `"${e}"`, A = (e) => `${e}__adopt_snapshot`, we = (e, t, n, r, i) => {
|
|
99
|
+
let a = Ce(n), o = A(e.from), s = t.assignments.map((e) => e.target), c = t.assignments.map((e) => e.raw ? e.source : a(e.source));
|
|
100
100
|
return i !== void 0 && (s.unshift(r), c.unshift(i)), {
|
|
101
101
|
snapshot: n === "mssql" ? `SELECT * INTO ${a(o)} FROM ${a(e.from)}` : `CREATE TABLE ${a(o)} AS SELECT * FROM ${a(e.from)}`,
|
|
102
102
|
copy: `INSERT INTO ${a(e.into)} (${s.map(a).join(", ")}) SELECT ${c.join(", ")} FROM ${a(e.from)}`,
|
|
@@ -106,18 +106,18 @@ var O = class extends Error {
|
|
|
106
106
|
drop: `DROP TABLE ${a(e.from)}`,
|
|
107
107
|
dropSnapshot: `DROP TABLE ${a(o)}`
|
|
108
108
|
};
|
|
109
|
-
},
|
|
109
|
+
}, j = (e) => {
|
|
110
110
|
let t = e[0];
|
|
111
111
|
if (t === void 0) return 0;
|
|
112
112
|
let n = t.n ?? t.N ?? Object.values(t)[0];
|
|
113
113
|
return Number(n ?? 0);
|
|
114
|
-
},
|
|
114
|
+
}, Te = async (e, t, n, r, i, a, o = {}) => {
|
|
115
115
|
if (t.fatal) throw Error("adopt: the plan was refused — nothing was run");
|
|
116
|
-
let s =
|
|
116
|
+
let s = we(e, t, n, r, i), c = A(e.from), l = j(await a(s.verifySource));
|
|
117
117
|
await a(s.snapshot);
|
|
118
118
|
for (let e of s.translations) await a(e);
|
|
119
119
|
await a(s.copy);
|
|
120
|
-
let u =
|
|
120
|
+
let u = j(await a(s.verifyTarget));
|
|
121
121
|
return u === l ? o.keepSource === !0 ? {
|
|
122
122
|
copied: u,
|
|
123
123
|
sourceRows: l,
|
|
@@ -137,7 +137,7 @@ var O = class extends Error {
|
|
|
137
137
|
snapshotTable: c,
|
|
138
138
|
sourceDropped: !1
|
|
139
139
|
};
|
|
140
|
-
},
|
|
140
|
+
}, M = /\.migration\.ts$/, N = async (e, t) => {
|
|
141
141
|
let n = [];
|
|
142
142
|
for (let r of e) {
|
|
143
143
|
let e;
|
|
@@ -147,7 +147,7 @@ var O = class extends Error {
|
|
|
147
147
|
t.warn("migration: failed to import", { file: r }, e);
|
|
148
148
|
continue;
|
|
149
149
|
}
|
|
150
|
-
let i = (e.default &&
|
|
150
|
+
let i = (e.default && S(e.default) ? e.default : void 0) ?? Object.values(e).find(S);
|
|
151
151
|
if (!i) {
|
|
152
152
|
t.warn("migration: no defineMigration export", { file: r });
|
|
153
153
|
continue;
|
|
@@ -157,7 +157,7 @@ var O = class extends Error {
|
|
|
157
157
|
idInFile: i.id,
|
|
158
158
|
idFromFile: d(r).replace(/\.migration\.ts$/, "")
|
|
159
159
|
});
|
|
160
|
-
let a = await v.readFile(r, "utf8").catch(() => ""), o =
|
|
160
|
+
let a = await v.readFile(r, "utf8").catch(() => ""), o = ie("sha256").update(a).digest("hex").slice(0, 16);
|
|
161
161
|
n.push({
|
|
162
162
|
file: r,
|
|
163
163
|
definition: i,
|
|
@@ -165,13 +165,13 @@ var O = class extends Error {
|
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
167
|
return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
|
|
168
|
-
},
|
|
168
|
+
}, P = (e) => {
|
|
169
169
|
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t;
|
|
170
|
-
return g.runPromise(
|
|
171
|
-
},
|
|
170
|
+
return g.runPromise(w(x, n).pipe(g.provide(e)));
|
|
171
|
+
}, F = (e) => g.runPromise(g.gen(function* () {
|
|
172
172
|
let e = yield* (yield* b.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(g.orElseSucceed(() => []));
|
|
173
173
|
return new Map(e.map((e) => [e.id, e]));
|
|
174
|
-
}).pipe(g.provide(e))),
|
|
174
|
+
}).pipe(g.provide(e))), I = (e, t) => g.runPromise(g.gen(function* () {
|
|
175
175
|
yield* (yield* b.SqlClient)`
|
|
176
176
|
INSERT INTO "_voltro_migrations"
|
|
177
177
|
(id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
|
|
@@ -187,14 +187,14 @@ var O = class extends Error {
|
|
|
187
187
|
"errorStack" = EXCLUDED."errorStack",
|
|
188
188
|
"updatedAt" = now()
|
|
189
189
|
`;
|
|
190
|
-
}).pipe(g.provide(e))),
|
|
190
|
+
}).pipe(g.provide(e))), Ee = async (e, t, n) => {
|
|
191
191
|
if (e.length === 0) return {
|
|
192
192
|
applied: [],
|
|
193
193
|
failed: null,
|
|
194
194
|
skipped: []
|
|
195
195
|
};
|
|
196
|
-
await
|
|
197
|
-
let r = await
|
|
196
|
+
await P(t);
|
|
197
|
+
let r = await F(t), i = [], a = [], o = null;
|
|
198
198
|
for (let s of e) {
|
|
199
199
|
let e = r.get(s.definition.id);
|
|
200
200
|
if (e && e.status === "applied") {
|
|
@@ -215,7 +215,7 @@ var O = class extends Error {
|
|
|
215
215
|
name: s.definition.name
|
|
216
216
|
});
|
|
217
217
|
let c = Date.now(), l = [];
|
|
218
|
-
await
|
|
218
|
+
await I(t, {
|
|
219
219
|
id: s.definition.id,
|
|
220
220
|
name: s.definition.name,
|
|
221
221
|
hash: s.hash,
|
|
@@ -256,7 +256,7 @@ var O = class extends Error {
|
|
|
256
256
|
...a.stack ? { stack: a.stack } : {}
|
|
257
257
|
}
|
|
258
258
|
};
|
|
259
|
-
o = f, i.push(f), await
|
|
259
|
+
o = f, i.push(f), await I(t, {
|
|
260
260
|
id: s.definition.id,
|
|
261
261
|
name: s.definition.name,
|
|
262
262
|
hash: s.hash,
|
|
@@ -275,7 +275,7 @@ var O = class extends Error {
|
|
|
275
275
|
status: "applied",
|
|
276
276
|
durationMs: Date.now() - c,
|
|
277
277
|
steps: l
|
|
278
|
-
}), await
|
|
278
|
+
}), await I(t, {
|
|
279
279
|
id: s.definition.id,
|
|
280
280
|
name: s.definition.name,
|
|
281
281
|
hash: s.hash,
|
|
@@ -295,9 +295,9 @@ var O = class extends Error {
|
|
|
295
295
|
failed: o,
|
|
296
296
|
skipped: a
|
|
297
297
|
};
|
|
298
|
-
},
|
|
299
|
-
await
|
|
300
|
-
let i = await
|
|
298
|
+
}, De = async (e, t, n, r = {}) => {
|
|
299
|
+
await P(t);
|
|
300
|
+
let i = await F(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
|
|
301
301
|
if (a.length === 0) return n.info("migration: nothing to roll back"), {
|
|
302
302
|
applied: [],
|
|
303
303
|
failed: null,
|
|
@@ -350,7 +350,7 @@ var O = class extends Error {
|
|
|
350
350
|
status: "applied",
|
|
351
351
|
durationMs: Date.now() - r,
|
|
352
352
|
steps: i
|
|
353
|
-
}), await
|
|
353
|
+
}), await I(t, {
|
|
354
354
|
id: e.definition.id,
|
|
355
355
|
name: e.definition.name,
|
|
356
356
|
hash: e.hash,
|
|
@@ -366,9 +366,9 @@ var O = class extends Error {
|
|
|
366
366
|
failed: c,
|
|
367
367
|
skipped: []
|
|
368
368
|
};
|
|
369
|
-
},
|
|
370
|
-
await
|
|
371
|
-
let n = await
|
|
369
|
+
}, L = async (e, t) => {
|
|
370
|
+
await P(t);
|
|
371
|
+
let n = await F(t);
|
|
372
372
|
return e.map((e) => {
|
|
373
373
|
let t = n.get(e.definition.id);
|
|
374
374
|
return t ? {
|
|
@@ -385,7 +385,7 @@ var O = class extends Error {
|
|
|
385
385
|
hashMatches: !0
|
|
386
386
|
};
|
|
387
387
|
});
|
|
388
|
-
},
|
|
388
|
+
}, R = (e) => {
|
|
389
389
|
if (!e.failed) return "";
|
|
390
390
|
let t = e.failed, n = [
|
|
391
391
|
`✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
|
|
@@ -398,7 +398,7 @@ var O = class extends Error {
|
|
|
398
398
|
""
|
|
399
399
|
];
|
|
400
400
|
return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
|
|
401
|
-
},
|
|
401
|
+
}, z = _({ scope: "voltro:db" }), Oe = /* @__PURE__ */ new Set([
|
|
402
402
|
"node_modules",
|
|
403
403
|
"dist",
|
|
404
404
|
"build",
|
|
@@ -406,17 +406,17 @@ var O = class extends Error {
|
|
|
406
406
|
".vite",
|
|
407
407
|
".next",
|
|
408
408
|
".git"
|
|
409
|
-
]),
|
|
409
|
+
]), B = async (e, t) => {
|
|
410
410
|
let n = [], r = async (e) => {
|
|
411
411
|
let i = await v.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
412
412
|
for (let a of i) {
|
|
413
|
-
if (
|
|
413
|
+
if (Oe.has(a.name)) continue;
|
|
414
414
|
let i = f(e, a.name);
|
|
415
415
|
a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
|
|
416
416
|
}
|
|
417
417
|
};
|
|
418
418
|
return await r(e), n;
|
|
419
|
-
},
|
|
419
|
+
}, V = (e) => m(e.find((e) => !e.startsWith("-")) ?? process.cwd()), H = () => {
|
|
420
420
|
let e = process.env.DB_MAX_CONNECTIONS ?? process.env.PG_MAX_CONNECTIONS, t = e !== void 0 && e !== "" ? Number(e) : NaN, n = Number.isInteger(t) && t > 0 ? { maxConnections: t } : {}, r = process.env.DB_SCHEMA, i = r && r.length > 0 ? { schema: r } : {}, a = process.env.DB_DIRECT_URL ?? process.env.DB_MIGRATE_URL ?? process.env.DB_URL ?? process.env.DB_PRIMARY_URL;
|
|
421
421
|
return a ? {
|
|
422
422
|
url: a,
|
|
@@ -431,63 +431,63 @@ var O = class extends Error {
|
|
|
431
431
|
...n,
|
|
432
432
|
...i
|
|
433
433
|
};
|
|
434
|
-
},
|
|
434
|
+
}, U = (e) => {
|
|
435
435
|
let t = e;
|
|
436
436
|
if (typeof t != "object" || !t) return !1;
|
|
437
437
|
if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
|
|
438
438
|
let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
|
|
439
439
|
return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
|
|
440
|
-
},
|
|
440
|
+
}, W = "Live-DB introspection got a corrupt / misaligned response from the database. This happens when `voltro db plan` / `apply` / `drift` / `migrate` runs through a connection POOLER (Supabase Supavisor / PgBouncer) on a LARGE schema — the pooler mis-frames a big catalog response. The introspection already batches by table to keep responses small; if a batch still trips it:\n 1. Lower the batch: set `VOLTRO_INTROSPECT_BATCH` to a smaller number of tables (default 20 — try 5 or 1). This keeps `db plan` on the normal pooler, no direct connection needed.\n 2. Or use a DIRECT (non-pooler) connection: set `DB_DIRECT_URL` (or `DB_MIGRATE_URL`) to the direct database URL — the runtime keeps `DB_URL`, so only the migration path is affected. (Supabase: direct = `db.<project-ref>.supabase.co` [IPv6-only on newer projects]; pooler = `aws-0-<region>.pooler.supabase.com`.)", G = () => {
|
|
441
441
|
let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
442
|
-
if (e === "memory") throw new
|
|
442
|
+
if (e === "memory") throw new _e("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
|
|
443
443
|
return e;
|
|
444
|
-
},
|
|
445
|
-
let t =
|
|
446
|
-
if (r.length === 0) return
|
|
444
|
+
}, K = async () => (await r(G())).makeSqlLayer(H()), ke = async (e) => {
|
|
445
|
+
let t = V(e), n = e.includes("--dry-run"), r = await N(await B(t, M), z);
|
|
446
|
+
if (r.length === 0) return z.info("no migrations found", { root: t }), 0;
|
|
447
447
|
if (n) {
|
|
448
448
|
let e = /* @__PURE__ */ new Set();
|
|
449
449
|
try {
|
|
450
|
-
let t = await
|
|
450
|
+
let t = await L(r, await K());
|
|
451
451
|
e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
|
|
452
452
|
} catch {
|
|
453
|
-
|
|
453
|
+
z.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
|
|
454
454
|
}
|
|
455
455
|
let t = r.filter((t) => !e.has(t.definition.id));
|
|
456
|
-
if (t.length === 0) return
|
|
457
|
-
|
|
456
|
+
if (t.length === 0) return z.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
|
|
457
|
+
z.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
|
|
458
458
|
for (let e of t) console.log(` pending ${e.definition.id}`);
|
|
459
459
|
return 0;
|
|
460
460
|
}
|
|
461
|
-
let i = await
|
|
462
|
-
return i.failed ? (process.stderr.write(
|
|
461
|
+
let i = await Ee(r, await K(), z);
|
|
462
|
+
return i.failed ? (process.stderr.write(R(i) + "\n"), 1) : (z.info("migrate complete", {
|
|
463
463
|
applied: i.applied.length,
|
|
464
464
|
skipped: i.skipped.length
|
|
465
465
|
}), 0);
|
|
466
|
-
},
|
|
467
|
-
let t =
|
|
468
|
-
if (r.length === 0) return
|
|
469
|
-
let i = await
|
|
470
|
-
return i.failed ? (process.stderr.write(
|
|
471
|
-
},
|
|
472
|
-
let t = await
|
|
466
|
+
}, Ae = async (e) => {
|
|
467
|
+
let t = V(e), n = l(e, "--to"), r = await N(await B(t, M), z);
|
|
468
|
+
if (r.length === 0) return z.info("no migrations found", { root: t }), 0;
|
|
469
|
+
let i = await De(r, await K(), z, n ? { to: n } : {});
|
|
470
|
+
return i.failed ? (process.stderr.write(R(i) + "\n"), 1) : (z.info("rollback complete", { reverted: i.applied.length }), 0);
|
|
471
|
+
}, je = async (e) => {
|
|
472
|
+
let t = await N(await B(V(e), M), z);
|
|
473
473
|
if (t.length === 0) return console.log("no migrations found"), 0;
|
|
474
|
-
let n = await
|
|
474
|
+
let n = await L(t, await K());
|
|
475
475
|
for (let e of n) {
|
|
476
476
|
let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
|
|
477
477
|
console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
|
|
478
478
|
}
|
|
479
479
|
return 0;
|
|
480
|
-
},
|
|
480
|
+
}, Me = async (e) => {
|
|
481
481
|
let t = (l(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
482
|
-
if (t === "memory") return
|
|
482
|
+
if (t === "memory") return z.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), z.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
|
|
483
483
|
store: new ee(),
|
|
484
484
|
dispose: async () => {},
|
|
485
485
|
dialectId: "memory"
|
|
486
486
|
};
|
|
487
487
|
let n = await r(t);
|
|
488
|
-
|
|
488
|
+
z.info(`voltro db seed: target store=${n.id} — rows will persist`);
|
|
489
489
|
let i = await n.makeStore({
|
|
490
|
-
sqlLayer: n.makeSqlLayer(
|
|
490
|
+
sqlLayer: n.makeSqlLayer(H()),
|
|
491
491
|
changeStrategy: "inline"
|
|
492
492
|
}), a = i;
|
|
493
493
|
return {
|
|
@@ -497,77 +497,77 @@ var O = class extends Error {
|
|
|
497
497
|
},
|
|
498
498
|
dialectId: n.id
|
|
499
499
|
};
|
|
500
|
-
},
|
|
501
|
-
let a =
|
|
502
|
-
if (c.length === 0) return
|
|
503
|
-
let { store: u, dispose: d, dialectId: f } = await
|
|
500
|
+
}, Ne = async (r) => {
|
|
501
|
+
let a = V(r), o = l(r, "--id"), s = l(r, "--lifecycle"), c = await t(await B(a, i), z);
|
|
502
|
+
if (c.length === 0) return z.info("no seeds found", { root: a }), 0;
|
|
503
|
+
let { store: u, dispose: d, dialectId: f } = await Me(r);
|
|
504
504
|
try {
|
|
505
|
-
return o ? +((await e(c, o, u,
|
|
505
|
+
return o ? +((await e(c, o, u, z)).status === "failed") : +((await n((s ? c.filter((e) => e.definition.lifecycle === s) : c.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
|
|
506
506
|
...e,
|
|
507
507
|
definition: {
|
|
508
508
|
...e.definition,
|
|
509
509
|
lifecycle: "boot"
|
|
510
510
|
}
|
|
511
|
-
})), u,
|
|
511
|
+
})), u, z, { force: !0 })).filter((e) => e.status === "failed").length > 0);
|
|
512
512
|
} finally {
|
|
513
513
|
await d().catch((e) => {
|
|
514
|
-
|
|
514
|
+
z.warn("voltro db seed: store dispose failed", { error: String(e) });
|
|
515
515
|
});
|
|
516
516
|
}
|
|
517
|
-
},
|
|
518
|
-
let t = await
|
|
517
|
+
}, Pe = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", Fe = async (e) => {
|
|
518
|
+
let t = await B(e, s), n = [];
|
|
519
519
|
for (let r of t) {
|
|
520
520
|
let t;
|
|
521
521
|
try {
|
|
522
522
|
t = await import(h(r).href);
|
|
523
523
|
} catch (t) {
|
|
524
|
-
|
|
524
|
+
z.warn("db: failed to import schema file", {
|
|
525
525
|
file: p(e, r),
|
|
526
526
|
error: String(t)
|
|
527
527
|
});
|
|
528
528
|
continue;
|
|
529
529
|
}
|
|
530
|
-
for (let e of Object.values(t))
|
|
530
|
+
for (let e of Object.values(t)) Pe(e) && n.push(e);
|
|
531
531
|
}
|
|
532
532
|
return n;
|
|
533
|
-
},
|
|
533
|
+
}, Ie = (e) => {
|
|
534
534
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
535
535
|
t.has("actors") || t.set("actors", te);
|
|
536
536
|
let n = t.get("actors"), r = t.get("tenants");
|
|
537
|
-
return
|
|
537
|
+
return re({
|
|
538
538
|
actors: () => n,
|
|
539
539
|
...r === void 0 ? {} : { tenants: () => r }
|
|
540
540
|
}), [...t.values()];
|
|
541
|
-
},
|
|
541
|
+
}, Le = (e) => {
|
|
542
542
|
let t = /* @__PURE__ */ new Map();
|
|
543
543
|
for (let n of e) {
|
|
544
544
|
let e = n.tableName;
|
|
545
545
|
e !== void 0 && !t.has(e) && t.set(e, n);
|
|
546
546
|
}
|
|
547
547
|
return [...t.values()];
|
|
548
|
-
},
|
|
548
|
+
}, Re = (e) => {
|
|
549
549
|
let t = ce(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
550
550
|
if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
|
|
551
551
|
plan: e,
|
|
552
552
|
acknowledged: !1
|
|
553
553
|
};
|
|
554
|
-
let n =
|
|
554
|
+
let n = ge(e, t);
|
|
555
555
|
return {
|
|
556
556
|
plan: n,
|
|
557
557
|
acknowledged: !n.operations.some((e) => e.blocked)
|
|
558
558
|
};
|
|
559
|
-
},
|
|
559
|
+
}, q = async (e, t) => g.runPromise(t.pipe(g.provide(e))), J = async (e) => {
|
|
560
560
|
try {
|
|
561
|
-
return await
|
|
561
|
+
return await q(e, b.SqlClient.pipe(g.flatMap(D)));
|
|
562
562
|
} catch (e) {
|
|
563
563
|
let t = String(e?.message ?? e);
|
|
564
|
-
if (
|
|
565
|
-
let n = /* @__PURE__ */ Error(`${
|
|
564
|
+
if (U(e) || /connection error/i.test(t)) {
|
|
565
|
+
let n = /* @__PURE__ */ Error(`${W}\n\nOriginal error: ${t}`);
|
|
566
566
|
throw n.cause = e, n;
|
|
567
567
|
}
|
|
568
568
|
throw e;
|
|
569
569
|
}
|
|
570
|
-
},
|
|
570
|
+
}, ze = {
|
|
571
571
|
safe: "\x1B[32m✓\x1B[0m",
|
|
572
572
|
"needs-default": "\x1B[32m⊕\x1B[0m",
|
|
573
573
|
"needs-backfill": "\x1B[33m⊕\x1B[0m",
|
|
@@ -575,7 +575,7 @@ var O = class extends Error {
|
|
|
575
575
|
lossy: "\x1B[31m✗\x1B[0m",
|
|
576
576
|
"online-required": "\x1B[36m◷\x1B[0m",
|
|
577
577
|
"multi-step": "\x1B[35m⇉\x1B[0m"
|
|
578
|
-
},
|
|
578
|
+
}, Be = (e) => {
|
|
579
579
|
let t = e.op, n;
|
|
580
580
|
switch (t.kind) {
|
|
581
581
|
case "create-table":
|
|
@@ -639,9 +639,9 @@ var O = class extends Error {
|
|
|
639
639
|
n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
|
|
640
640
|
break;
|
|
641
641
|
}
|
|
642
|
-
let r =
|
|
642
|
+
let r = ze[e.classification], i = e.reason ? ` [2m# ${e.reason}[0m` : "", a = e.blocked ? `\n [31m! ${e.blocked.fix}[0m` : "";
|
|
643
643
|
return ` ${r} ${n}${i}${a}`;
|
|
644
|
-
},
|
|
644
|
+
}, Ve = async (e, t) => {
|
|
645
645
|
let n = e.operations.filter((e) => e.classification === "needs-default" && e.op.kind === "add-column");
|
|
646
646
|
if (n.length === 0) return;
|
|
647
647
|
let r = [];
|
|
@@ -650,7 +650,7 @@ var O = class extends Error {
|
|
|
650
650
|
if (i === void 0 || a === void 0) continue;
|
|
651
651
|
let o = 0;
|
|
652
652
|
try {
|
|
653
|
-
let e = await
|
|
653
|
+
let e = await q(t, b.SqlClient.pipe(g.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${ne(i, G())}`))));
|
|
654
654
|
o = Number(Object.values(e[0] ?? { n: 0 })[0] ?? 0);
|
|
655
655
|
} catch {
|
|
656
656
|
continue;
|
|
@@ -662,32 +662,69 @@ var O = class extends Error {
|
|
|
662
662
|
for (let e of r) console.log(e);
|
|
663
663
|
console.log(" (the shape is correct either way — this is the VALUE a state diff cannot know)");
|
|
664
664
|
}
|
|
665
|
-
},
|
|
665
|
+
}, Y = async (e, t) => {
|
|
666
|
+
if (G() !== "postgres") return;
|
|
667
|
+
let n = ae, r;
|
|
668
|
+
try {
|
|
669
|
+
r = await q(t, b.SqlClient.pipe(g.flatMap((t) => le(t, e, n))));
|
|
670
|
+
} catch {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
let i = de({
|
|
674
|
+
tables: e,
|
|
675
|
+
dialect: "postgres",
|
|
676
|
+
missing: r.missing,
|
|
677
|
+
stale: r.stale,
|
|
678
|
+
channel: n
|
|
679
|
+
});
|
|
680
|
+
if (i.length === 0) return;
|
|
681
|
+
z.info(`db apply: installing change triggers on ${r.missing.length} table(s)` + (r.stale.length > 0 ? `, removing ${r.stale.length} stale one(s)` : ""));
|
|
682
|
+
let a = 0;
|
|
683
|
+
for (let e of i) await q(t, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), a += 1;
|
|
684
|
+
z.info(`db apply: change triggers converged (${a} statement(s))`, {
|
|
685
|
+
installed: r.missing.length,
|
|
686
|
+
removed: r.stale.length
|
|
687
|
+
});
|
|
688
|
+
}, X = (e) => {
|
|
666
689
|
let t = e.summary, n = e.operations.length;
|
|
667
690
|
if (console.log(`\nschema diff: ${n} operation${n === 1 ? "" : "s"}, ${t.blocked} blocked\n`), n === 0) {
|
|
668
691
|
console.log(" (schema is up to date)\n");
|
|
669
692
|
return;
|
|
670
693
|
}
|
|
671
|
-
for (let t of e.operations) console.log(
|
|
672
|
-
console.log(), console.log(` safe: ${t.safe} needs-default: ${t.needsDefault} needs-backfill: ${t.needsBackfill}`), console.log(` rename: ${t.needsRenameAnnotation} lossy: ${t.lossy} blocked: ${t.blocked}`), console.log(` fingerprint: ${e.fromFingerprint.slice(0, 16)} → ${e.toFingerprint.slice(0, 16)}\n`);
|
|
673
|
-
},
|
|
694
|
+
for (let t of e.operations) console.log(Be(t));
|
|
695
|
+
console.log(), console.log(` safe: ${t.safe} needs-default: ${t.needsDefault} needs-backfill: ${t.needsBackfill}`), console.log(` rename: ${t.needsRenameAnnotation} lossy: ${t.lossy} blocked: ${t.blocked}`), console.log(` fingerprint: ${e.fromFingerprint.slice(0, 16)} → ${e.toFingerprint.slice(0, 16)}\n`), He(e);
|
|
696
|
+
}, He = (e) => {
|
|
697
|
+
let t = pe(e.operations);
|
|
698
|
+
if (t.length !== 0) {
|
|
699
|
+
console.log(` ⚠ ${t.length} operation(s) UNSAFE under a rolling deploy`), console.log(" (old + new instances overlap → old code breaks against the new schema):");
|
|
700
|
+
for (let e of t) console.log(` • ${e.op.op.kind}: ${e.reason}`), console.log(` → ${e.remedy}`);
|
|
701
|
+
console.log(" Deploy with a maintenance window / scale-to-zero, or split into"), console.log(" expand → deploy → contract. See docs: deployment/production-hardening.\n");
|
|
702
|
+
}
|
|
703
|
+
}, Ue = (e, t) => {
|
|
704
|
+
let n = oe({
|
|
705
|
+
enabled: process.env.VOLTRO_ROLLING_DEPLOY === "1",
|
|
706
|
+
forced: t.includes("--force"),
|
|
707
|
+
unsafeCount: pe(e.operations).length
|
|
708
|
+
});
|
|
709
|
+
return n.refuse ? (z.error(`db apply: ${n.message}`), !0) : (n.warnForced && z.warn(`db apply: ${n.message}`), !1);
|
|
710
|
+
}, We = (e) => {
|
|
674
711
|
let t = l(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
|
|
675
712
|
if (t === void 0) return;
|
|
676
713
|
let n = Number(t);
|
|
677
714
|
if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
|
|
678
715
|
return n;
|
|
679
|
-
},
|
|
680
|
-
let t = await
|
|
716
|
+
}, Z = async (e) => {
|
|
717
|
+
let t = await Fe(e);
|
|
681
718
|
if (t.length === 0) return;
|
|
682
719
|
let n = await o({ root: e });
|
|
683
|
-
return
|
|
720
|
+
return Ie(Le([
|
|
684
721
|
...t,
|
|
685
722
|
...x,
|
|
686
723
|
...n
|
|
687
724
|
]));
|
|
688
|
-
},
|
|
689
|
-
let t =
|
|
690
|
-
if (r === void 0) return
|
|
725
|
+
}, Ge = async (e) => {
|
|
726
|
+
let t = We(e), n = V(e), r = await Z(n);
|
|
727
|
+
if (r === void 0) return z.warn("db plan: no schema files found", {
|
|
691
728
|
root: n,
|
|
692
729
|
hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
|
|
693
730
|
}), 1;
|
|
@@ -697,113 +734,115 @@ var O = class extends Error {
|
|
|
697
734
|
a && (o.authorization = `Bearer ${a}`);
|
|
698
735
|
try {
|
|
699
736
|
let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers: o });
|
|
700
|
-
if (!i.ok) return
|
|
737
|
+
if (!i.ok) return z.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
|
|
701
738
|
let a = (await i.json()).drift?.liveSnapshot;
|
|
702
|
-
if (!a) return
|
|
703
|
-
let s =
|
|
739
|
+
if (!a) return z.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
|
|
740
|
+
let s = O({
|
|
704
741
|
declared: r,
|
|
705
742
|
live: a,
|
|
706
|
-
dialect:
|
|
707
|
-
...
|
|
743
|
+
dialect: G(),
|
|
744
|
+
...E(),
|
|
708
745
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
709
746
|
});
|
|
710
|
-
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`),
|
|
747
|
+
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), X(s), s.summary.blocked > 0 ? 2 : 0;
|
|
711
748
|
} catch (e) {
|
|
712
|
-
return
|
|
749
|
+
return z.error(`db plan --against: ${e.message}`), 1;
|
|
713
750
|
}
|
|
714
751
|
}
|
|
715
|
-
let a =
|
|
752
|
+
let a = O({
|
|
716
753
|
declared: r,
|
|
717
|
-
live: await
|
|
718
|
-
dialect:
|
|
719
|
-
...
|
|
754
|
+
live: await J(await K()),
|
|
755
|
+
dialect: G(),
|
|
756
|
+
...E(),
|
|
720
757
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
721
758
|
});
|
|
722
|
-
return e.includes("--json") ? (y(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (
|
|
723
|
-
},
|
|
759
|
+
return e.includes("--json") ? (y(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (X(a), a.summary.blocked > 0 ? 2 : 0);
|
|
760
|
+
}, Ke = async (e, t, n) => {
|
|
724
761
|
let r;
|
|
725
762
|
try {
|
|
726
763
|
let t = await v.readFile(m(e), "utf8");
|
|
727
764
|
r = JSON.parse(t);
|
|
728
765
|
} catch (t) {
|
|
729
|
-
return
|
|
766
|
+
return z.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
|
|
730
767
|
}
|
|
731
|
-
if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return
|
|
732
|
-
let i = await
|
|
733
|
-
if (i === void 0) return
|
|
734
|
-
let a = await
|
|
735
|
-
await
|
|
736
|
-
let o = await
|
|
737
|
-
if (o.length > 0) return
|
|
738
|
-
let s =
|
|
768
|
+
if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return z.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
|
|
769
|
+
let i = await Z(n);
|
|
770
|
+
if (i === void 0) return z.warn("db apply --plan: no schema files found", { root: n }), 1;
|
|
771
|
+
let a = await K();
|
|
772
|
+
await q(a, w(x, G()));
|
|
773
|
+
let o = await q(a, b.SqlClient.pipe(g.flatMap((e) => ue(e, n)))).catch(() => []);
|
|
774
|
+
if (o.length > 0) return z.error(`db apply --plan: refusing — ${o.length} pending file-based migration(s).`), console.error(` ${o.join("\n ")}`), console.error(""), console.error(" These perform the changes a state diff cannot infer, so they change the shape"), console.error(" this plan was computed against. Apply them and regenerate the plan:"), console.error(""), console.error(" voltro db files ."), console.error(" voltro db plan --json > plan.json"), console.error(" voltro db apply --plan plan.json"), 1;
|
|
775
|
+
let s = O({
|
|
739
776
|
declared: i,
|
|
740
|
-
live: await
|
|
741
|
-
dialect:
|
|
742
|
-
...
|
|
777
|
+
live: await J(a),
|
|
778
|
+
dialect: G(),
|
|
779
|
+
...E()
|
|
743
780
|
});
|
|
744
|
-
if (s.fromFingerprint !== r.fromFingerprint) return
|
|
745
|
-
if (s.toFingerprint !== r.toFingerprint) return
|
|
746
|
-
let c =
|
|
747
|
-
if (u.summary.blocked > 0 && !c.acknowledged) return
|
|
748
|
-
if (c.acknowledged &&
|
|
749
|
-
|
|
750
|
-
|
|
781
|
+
if (s.fromFingerprint !== r.fromFingerprint) return z.error("db apply --plan: refusing — the live schema has drifted since this plan was generated."), console.error(` plan.from=${r.fromFingerprint.slice(0, 16)} live=${s.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
|
|
782
|
+
if (s.toFingerprint !== r.toFingerprint) return z.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${s.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
|
|
783
|
+
let c = Re(s), u = c.plan;
|
|
784
|
+
if (u.summary.blocked > 0 && !c.acknowledged) return X(u), z.error(`db apply --plan: refusing — ${s.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (z.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), z.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
785
|
+
if (c.acknowledged && z.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), Ue(u, t)) return 2;
|
|
786
|
+
if (u.operations.length === 0) return z.info("db apply --plan: schema is up to date — nothing to apply"), await Y(i, a), 0;
|
|
787
|
+
X(u);
|
|
788
|
+
let d = (process.env.NODE_ENV ?? "dev").toLowerCase(), f = l(t, "--note"), p = await q(a, b.SqlClient.pipe(g.flatMap((e) => C(e, u, {
|
|
751
789
|
declared: i,
|
|
752
790
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
|
|
753
791
|
environment: d === "production" ? "prod" : d === "staging" ? "staging" : "dev",
|
|
754
792
|
source: "file",
|
|
755
793
|
...f ? { notes: f } : {},
|
|
756
|
-
replan: () => g.promise(() =>
|
|
794
|
+
replan: () => g.promise(() => J(a)).pipe(g.map((e) => O({
|
|
757
795
|
declared: i,
|
|
758
796
|
live: e,
|
|
759
|
-
dialect:
|
|
760
|
-
...
|
|
797
|
+
dialect: G(),
|
|
798
|
+
...E()
|
|
761
799
|
})))
|
|
762
800
|
}))));
|
|
763
|
-
return
|
|
801
|
+
return z.info(`db apply --plan: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
|
|
764
802
|
id: p.id,
|
|
765
803
|
fingerprint: p.fingerprint.slice(0, 16)
|
|
766
|
-
}), 0;
|
|
767
|
-
},
|
|
768
|
-
let t =
|
|
769
|
-
if (r) return
|
|
770
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return
|
|
771
|
-
let i = await
|
|
772
|
-
if (i === void 0) return
|
|
773
|
-
let a = await
|
|
774
|
-
if ("failed" in o) return
|
|
775
|
-
await
|
|
776
|
-
let s =
|
|
804
|
+
}), await Y(i, a), 0;
|
|
805
|
+
}, qe = async (e) => {
|
|
806
|
+
let t = V(e), n = We(e), r = l(e, "--plan");
|
|
807
|
+
if (r) return Ke(r, e, t);
|
|
808
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return z.error("voltro db apply: refusing — auto-apply on prod is not allowed."), console.error("In production, schema changes go through `voltro db plan --json > plan.json`"), console.error("then `voltro db apply --plan plan.json` as an explicit step in your deploy pipeline."), 3;
|
|
809
|
+
let i = await Z(t);
|
|
810
|
+
if (i === void 0) return z.warn("db apply: no schema files found", { root: t }), 1;
|
|
811
|
+
let a = await K(), o = await nt(t, a);
|
|
812
|
+
if ("failed" in o) return z.error("db apply: a file-based migration FAILED — the schema diff was NOT run"), console.error(o.failed), console.error("Fix the migration and re-run. `voltro db files` runs them alone."), 1;
|
|
813
|
+
await q(a, w(x, G()));
|
|
814
|
+
let s = O({
|
|
777
815
|
declared: i,
|
|
778
|
-
live: await
|
|
779
|
-
dialect:
|
|
780
|
-
...
|
|
816
|
+
live: await J(a),
|
|
817
|
+
dialect: G(),
|
|
818
|
+
...E(),
|
|
781
819
|
...n === void 0 ? {} : { onlineAfter: n }
|
|
782
820
|
});
|
|
783
|
-
|
|
784
|
-
let c =
|
|
785
|
-
if (u.summary.blocked > 0 && !c.acknowledged) return
|
|
786
|
-
if (c.acknowledged &&
|
|
787
|
-
|
|
821
|
+
X(s), await Ve(s, a);
|
|
822
|
+
let c = Re(s), u = c.plan;
|
|
823
|
+
if (u.summary.blocked > 0 && !c.acknowledged) return z.error(`db apply: refusing — ${u.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (z.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), z.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
824
|
+
if (c.acknowledged && z.warn(`db apply: proceeding with ${s.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), Ue(u, e)) return 2;
|
|
825
|
+
if (u.operations.length === 0) return z.info("db apply: schema is up to date — nothing to apply"), await Qe(a), l(e, "--note") !== void 0 && (z.warn("db apply: --note was ignored — an empty plan writes no history row to attach it to."), console.error("To record that the CURRENT live schema is the accepted baseline, use `voltro db drift --accept`.")), await Y(i, a), 0;
|
|
826
|
+
let d = l(e, "--note"), f = await q(a, b.SqlClient.pipe(g.flatMap((e) => C(e, u, {
|
|
788
827
|
declared: i,
|
|
789
828
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
|
|
790
829
|
environment: (process.env.NODE_ENV ?? "dev").toLowerCase() === "staging" ? "staging" : "dev",
|
|
791
830
|
source: "auto-diff",
|
|
792
831
|
...d ? { notes: d } : {},
|
|
793
|
-
replan: () => g.promise(() =>
|
|
832
|
+
replan: () => g.promise(() => J(a)).pipe(g.map((e) => O({
|
|
794
833
|
declared: i,
|
|
795
834
|
live: e,
|
|
796
|
-
dialect:
|
|
797
|
-
...
|
|
835
|
+
dialect: G(),
|
|
836
|
+
...E(),
|
|
798
837
|
...n === void 0 ? {} : { onlineAfter: n }
|
|
799
838
|
})))
|
|
800
839
|
}))));
|
|
801
|
-
return
|
|
840
|
+
return z.info(`db apply: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
|
|
802
841
|
id: f.id,
|
|
803
842
|
fingerprint: f.fingerprint.slice(0, 16)
|
|
804
|
-
}), 0;
|
|
805
|
-
},
|
|
806
|
-
let t = Math.max(1, Number(l(e, "--limit") ?? "20")), n = await
|
|
843
|
+
}), await Y(i, a), 0;
|
|
844
|
+
}, Je = async (e) => {
|
|
845
|
+
let t = Math.max(1, Number(l(e, "--limit") ?? "20")), n = await q(await K(), b.SqlClient.pipe(g.flatMap((e) => e`
|
|
807
846
|
SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
808
847
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
809
848
|
${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}
|
|
@@ -811,25 +850,25 @@ var O = class extends Error {
|
|
|
811
850
|
ORDER BY ${e("appliedAt")} DESC
|
|
812
851
|
LIMIT ${t}
|
|
813
852
|
`)));
|
|
814
|
-
if (n.length === 0) return
|
|
853
|
+
if (n.length === 0) return z.info("db plans: no planner-based migrations applied yet"), console.log("Run `voltro db apply` after a schema change to start a plan history."), 0;
|
|
815
854
|
console.log(`\n${n.length} plan${n.length === 1 ? "" : "s"} (newest first):\n`);
|
|
816
855
|
for (let e of n) {
|
|
817
856
|
let t = 0;
|
|
818
857
|
try {
|
|
819
|
-
t = JSON.parse(
|
|
858
|
+
t = JSON.parse($e(e.operations)).length;
|
|
820
859
|
} catch {}
|
|
821
|
-
console.log(` ${e.id} fp=${
|
|
860
|
+
console.log(` ${e.id} fp=${k(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${Q(e.appliedAt)} by=${e.appliedBy}`), e.notes && console.log(` [2mnote: ${e.notes}[0m`);
|
|
822
861
|
}
|
|
823
862
|
return console.log(), 0;
|
|
824
|
-
},
|
|
863
|
+
}, Ye = async (e) => {
|
|
825
864
|
let t = e[0];
|
|
826
865
|
if (!t) return console.error("voltro db restore-snapshot <plan-id>"), console.error(" Renames soft-dropped columns/tables from a plan back to their original names."), console.error(" Requires the plan to have been applied with VOLTRO_SOFT_DROP=1."), 2;
|
|
827
|
-
let n = await
|
|
866
|
+
let n = await K(), r = await q(n, b.SqlClient.pipe(g.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
|
|
828
867
|
FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
|
|
829
868
|
if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
|
|
830
869
|
let i;
|
|
831
870
|
try {
|
|
832
|
-
i = JSON.parse(
|
|
871
|
+
i = JSON.parse($e(r[0].operations));
|
|
833
872
|
} catch {
|
|
834
873
|
return console.error(`restore-snapshot: plan '${t}' has corrupted operations JSON`), 1;
|
|
835
874
|
}
|
|
@@ -843,7 +882,7 @@ var O = class extends Error {
|
|
|
843
882
|
if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
|
|
844
883
|
let s = 0;
|
|
845
884
|
for (let e of a) {
|
|
846
|
-
let t = await
|
|
885
|
+
let t = await q(n, b.SqlClient.pipe(g.flatMap((t) => t`
|
|
847
886
|
SELECT column_name FROM information_schema.columns
|
|
848
887
|
WHERE table_schema = 'public'
|
|
849
888
|
AND table_name = ${e.table}
|
|
@@ -855,11 +894,11 @@ var O = class extends Error {
|
|
|
855
894
|
continue;
|
|
856
895
|
}
|
|
857
896
|
let r = t[0].column_name;
|
|
858
|
-
await
|
|
897
|
+
await q(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table}" RENAME COLUMN "${r}" TO "${e.column}"`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
|
|
859
898
|
}
|
|
860
899
|
let c = 0;
|
|
861
900
|
for (let e of o) {
|
|
862
|
-
let t = await
|
|
901
|
+
let t = await q(n, b.SqlClient.pipe(g.flatMap((t) => t`
|
|
863
902
|
SELECT table_name FROM information_schema.tables
|
|
864
903
|
WHERE table_schema = 'public'
|
|
865
904
|
AND table_name LIKE ${e.table + "__dropped_%"}
|
|
@@ -870,18 +909,18 @@ var O = class extends Error {
|
|
|
870
909
|
continue;
|
|
871
910
|
}
|
|
872
911
|
let r = t[0].table_name;
|
|
873
|
-
await
|
|
912
|
+
await q(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE "${r}" RENAME TO "${e.table}"`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
|
|
874
913
|
}
|
|
875
914
|
return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
|
|
876
|
-
},
|
|
915
|
+
}, Xe = async (e) => {
|
|
877
916
|
let t = l(e, "--before");
|
|
878
917
|
if (!t) return console.error("voltro db gc-snapshots --before <date> [--dry-run]"), console.error(" Permanently drops soft-dropped columns/tables (<name>__dropped_<ts>) older than <date>."), console.error(" <date> is ISO — e.g. 2026-01-01 or 2026-01-01T00:00:00Z. NOT reversible."), 2;
|
|
879
918
|
let n = t.replace(/[^0-9]/g, "");
|
|
880
919
|
if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
|
|
881
|
-
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await
|
|
920
|
+
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await K(), o = await q(a, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
882
921
|
SELECT table_name, column_name FROM information_schema.columns
|
|
883
922
|
WHERE table_schema = 'public' AND column_name LIKE ${"%__dropped_%"}
|
|
884
|
-
`))), s = await
|
|
923
|
+
`))), s = await q(a, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
885
924
|
SELECT table_name FROM information_schema.tables
|
|
886
925
|
WHERE table_schema = 'public' AND table_name LIKE ${"%__dropped_%"}
|
|
887
926
|
`))), c = (e) => {
|
|
@@ -890,37 +929,37 @@ var O = class extends Error {
|
|
|
890
929
|
}, u = 0;
|
|
891
930
|
for (let e of o) {
|
|
892
931
|
let t = c(e.column_name);
|
|
893
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await
|
|
932
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await q(a, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table_name}" DROP COLUMN "${e.column_name}"`)))), u++);
|
|
894
933
|
}
|
|
895
934
|
for (let e of s) {
|
|
896
935
|
let t = c(e.table_name);
|
|
897
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await
|
|
936
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await q(a, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`DROP TABLE "${e.table_name}"`)))), u++);
|
|
898
937
|
}
|
|
899
938
|
return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
|
|
900
|
-
},
|
|
939
|
+
}, Ze = async (e) => {
|
|
901
940
|
let t = l(e, "--before");
|
|
902
941
|
if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
|
|
903
942
|
let n = t;
|
|
904
943
|
if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
|
|
905
|
-
let r = l(e, "--note"), i =
|
|
906
|
-
if (a === void 0) return
|
|
907
|
-
let o =
|
|
944
|
+
let r = l(e, "--note"), i = V(e), a = await Z(i);
|
|
945
|
+
if (a === void 0) return z.warn("db squash: no schema files found", { root: i }), 1;
|
|
946
|
+
let o = T(se(a)), s = await K();
|
|
908
947
|
try {
|
|
909
|
-
let e = await
|
|
948
|
+
let e = await q(s, he({
|
|
910
949
|
before: n,
|
|
911
950
|
fingerprint: o,
|
|
912
951
|
appliedBy: process.env.USER ?? "cli",
|
|
913
952
|
environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
|
|
914
953
|
...r === void 0 ? {} : { note: r }
|
|
915
954
|
}));
|
|
916
|
-
return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${
|
|
955
|
+
return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${k(e.snapshotFingerprint)}\n`), 0;
|
|
917
956
|
} catch (e) {
|
|
918
957
|
return console.error(`squash refused: ${e.message}`), 1;
|
|
919
958
|
}
|
|
920
|
-
},
|
|
959
|
+
}, Qe = async (e) => {
|
|
921
960
|
try {
|
|
922
|
-
let t =
|
|
923
|
-
await
|
|
961
|
+
let t = T(await J(e));
|
|
962
|
+
await q(e, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
924
963
|
UPDATE ${e("_voltro_migration_plans")}
|
|
925
964
|
SET ${e("liveFingerprint")} = ${t}
|
|
926
965
|
WHERE ${e("id")} = (
|
|
@@ -932,7 +971,7 @@ var O = class extends Error {
|
|
|
932
971
|
AND ${e("liveFingerprint")} IS NULL
|
|
933
972
|
`)));
|
|
934
973
|
} catch {}
|
|
935
|
-
},
|
|
974
|
+
}, $e = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), Q = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), et = (e) => e === void 0 ? {
|
|
936
975
|
accept: !1,
|
|
937
976
|
reason: "no-declared-schema"
|
|
938
977
|
} : e > 0 ? {
|
|
@@ -941,38 +980,38 @@ var O = class extends Error {
|
|
|
941
980
|
} : {
|
|
942
981
|
accept: !0,
|
|
943
982
|
reason: "converged"
|
|
944
|
-
},
|
|
945
|
-
let t = e.includes("--accept"), n = await
|
|
983
|
+
}, tt = async (e) => {
|
|
984
|
+
let t = e.includes("--accept"), n = await K(), r = await q(n, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
946
985
|
SELECT * FROM ${e("_voltro_migration_plans")}
|
|
947
986
|
ORDER BY ${e("appliedAt")} DESC
|
|
948
987
|
LIMIT 1
|
|
949
|
-
`))), i = await
|
|
950
|
-
if (r.length === 0) return
|
|
988
|
+
`))), i = await J(n), a = T(i);
|
|
989
|
+
if (r.length === 0) return z.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${k(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
|
|
951
990
|
let o = r[0];
|
|
952
|
-
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return
|
|
953
|
-
if (o.liveFingerprint === a) return
|
|
991
|
+
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return z.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${Q(o.appliedAt)}`), console.log(` live now: ${k(a)}`), console.log(), console.log("That row was written before the framework recorded a live-schema baseline,"), console.log("and the value it does carry is the DECLARED snapshot's hash — not comparable"), console.log("with a live one. Run `voltro db apply` once to establish the baseline (a no-op"), console.log("apply backfills it too); use `voltro db plan` meanwhile, which compares declared"), console.log("against live directly."), console.log(), console.log("Exit code 3 — NOT 0. \"Did not compare\" is not \"clean\", and a gate on the exit"), console.log("code has to be able to tell them apart."), 3;
|
|
992
|
+
if (o.liveFingerprint === a) return z.info(`db drift: live matches last applied (${k(a)})`), 0;
|
|
954
993
|
let s = await (async () => {
|
|
955
994
|
try {
|
|
956
|
-
let t = await
|
|
957
|
-
return t === void 0 ? void 0 :
|
|
995
|
+
let t = await Z(V(e));
|
|
996
|
+
return t === void 0 ? void 0 : O({
|
|
958
997
|
declared: t,
|
|
959
998
|
live: i,
|
|
960
|
-
dialect:
|
|
961
|
-
...
|
|
999
|
+
dialect: G(),
|
|
1000
|
+
...E()
|
|
962
1001
|
}).operations;
|
|
963
1002
|
} catch {
|
|
964
1003
|
return;
|
|
965
1004
|
}
|
|
966
1005
|
})();
|
|
967
1006
|
if (t) {
|
|
968
|
-
let e =
|
|
969
|
-
if (e.reason === "no-declared-schema") return
|
|
1007
|
+
let e = et(s?.length);
|
|
1008
|
+
if (e.reason === "no-declared-schema") return z.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
|
|
970
1009
|
if (!e.accept && s !== void 0) {
|
|
971
|
-
|
|
1010
|
+
z.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
|
|
972
1011
|
for (let e of s.slice(0, 10)) console.log(` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}`);
|
|
973
1012
|
return s.length > 10 && console.log(` … and ${s.length - 10} more`), console.log(), console.log("`--accept` records the CURRENT live schema as the applied baseline. It is for a"), console.log("change that already landed out of band and left the schema correct — not for one"), console.log("that has not landed. Run `voltro db apply` instead; it writes a real baseline."), 4;
|
|
974
1013
|
}
|
|
975
|
-
return await
|
|
1014
|
+
return await q(n, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
976
1015
|
UPDATE ${e("_voltro_migration_plans")}
|
|
977
1016
|
SET ${e("liveFingerprint")} = ${a}
|
|
978
1017
|
WHERE ${e("id")} = (
|
|
@@ -981,43 +1020,43 @@ var O = class extends Error {
|
|
|
981
1020
|
ORDER BY ${e("appliedAt")} DESC LIMIT 1
|
|
982
1021
|
) AS ${e("newest")}
|
|
983
1022
|
)
|
|
984
|
-
`))),
|
|
1023
|
+
`))), z.info(`db drift: accepted the live schema as the baseline (${k(a)})`), console.log(), console.log("`voltro db plan` reports 0 operations against this schema, so the live state already"), console.log("matches your declaration — only the ledger did not know. It does now."), console.log(), console.log("No history entry was invented: the latest row's baseline was updated in place,"), console.log("because no migration ran."), 0;
|
|
985
1024
|
}
|
|
986
|
-
return
|
|
987
|
-
},
|
|
1025
|
+
return z.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${k(o.liveFingerprint)} at ${Q(o.appliedAt)} (${o.id})`), console.log(` live now: ${k(a)}`), console.log(), console.log("Something changed the live schema after the last apply. This command can see"), console.log("THAT it changed, not what or who — the fingerprints are hashes, not a diff."), console.log(), s === void 0 ? (console.log("No schema files found here, so the declared side could not be compared."), console.log("Run `voltro db plan` from your project root for the actual difference."), 4) : s.length === 0 ? (console.log("Your DECLARED schema is already satisfied — `voltro db plan` reports 0 operations"), console.log("against this database. So the live schema is not wrong, only unrecorded: something"), console.log("applied a change without going through the planner (a hand-run ALTER, a DBA"), console.log("window, a restored dump), or it touched a table your code does not declare."), console.log(), console.log("If that was deliberate and the schema is right, record it:"), console.log(), console.log(" voltro db drift --accept"), console.log(), console.log("It updates the latest ledger row's baseline to the live schema and invents no"), console.log("history entry. Drift then measures from here."), 4) : (console.log(`\`voltro db plan\` reports ${s.length} operation(s) still pending against your`), console.log("declared schema, so this is not merely an unrecorded change — run it for the diff,"), console.log("then `voltro db apply`, which writes a fresh baseline of its own."), 4);
|
|
1026
|
+
}, nt = async (e, t) => {
|
|
988
1027
|
let n = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
|
|
989
1028
|
try {
|
|
990
|
-
let r = await
|
|
1029
|
+
let r = await q(t, b.SqlClient.pipe(g.flatMap((t) => me(t, {
|
|
991
1030
|
projectRoot: e,
|
|
992
1031
|
env: n,
|
|
993
1032
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
994
1033
|
}))));
|
|
995
|
-
return r.applied.length > 0 &&
|
|
1034
|
+
return r.applied.length > 0 && z.info(`db apply: applied ${r.applied.length} pending file-based migration(s) first`, { applied: r.applied.map((e) => `${e.id} (${e.durationMs}ms)`) }), { applied: r.applied.length };
|
|
996
1035
|
} catch (e) {
|
|
997
1036
|
return { failed: e };
|
|
998
1037
|
}
|
|
999
|
-
},
|
|
1000
|
-
let t =
|
|
1001
|
-
await
|
|
1002
|
-
let i = await
|
|
1038
|
+
}, rt = async (e) => {
|
|
1039
|
+
let t = V(e), n = await K(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
|
|
1040
|
+
await q(n, w(x, G()));
|
|
1041
|
+
let i = await q(n, b.SqlClient.pipe(g.flatMap((e) => me(e, {
|
|
1003
1042
|
projectRoot: t,
|
|
1004
1043
|
env: r,
|
|
1005
1044
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
1006
1045
|
}))));
|
|
1007
|
-
return i.applied.length === 0 ? (
|
|
1046
|
+
return i.applied.length === 0 ? (z.info(`db files: no pending file-based migrations (${i.skipped.length} already applied)`), 0) : (z.info(`db files: applied ${i.applied.length} migration(s)`, {
|
|
1008
1047
|
applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
|
|
1009
1048
|
skipped: i.skipped.length
|
|
1010
1049
|
}), 0);
|
|
1011
|
-
},
|
|
1012
|
-
let t =
|
|
1013
|
-
if (!n) return
|
|
1014
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return
|
|
1015
|
-
let r = await
|
|
1050
|
+
}, it = async (e) => {
|
|
1051
|
+
let t = V(e), n = e.find((e) => !e.startsWith("--"));
|
|
1052
|
+
if (!n) return z.error("voltro db rollback-file <migration-id>"), 2;
|
|
1053
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return z.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step."), 3;
|
|
1054
|
+
let r = await q(await K(), b.SqlClient.pipe(g.flatMap((e) => fe(e, {
|
|
1016
1055
|
projectRoot: t,
|
|
1017
1056
|
id: n
|
|
1018
1057
|
}))));
|
|
1019
|
-
return
|
|
1020
|
-
},
|
|
1058
|
+
return z.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
|
|
1059
|
+
}, at = () => {
|
|
1021
1060
|
let e = /* @__PURE__ */ new Date();
|
|
1022
1061
|
return [
|
|
1023
1062
|
e.getUTCFullYear(),
|
|
@@ -1027,7 +1066,7 @@ var O = class extends Error {
|
|
|
1027
1066
|
String(e.getUTCMinutes()).padStart(2, "0"),
|
|
1028
1067
|
String(e.getUTCSeconds()).padStart(2, "0")
|
|
1029
1068
|
].join("");
|
|
1030
|
-
},
|
|
1069
|
+
}, ot = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", st = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
|
|
1031
1070
|
import { Effect } from 'effect'
|
|
1032
1071
|
|
|
1033
1072
|
// A hand-written migration. Reach for this when the DECLARATIVE differ can't
|
|
@@ -1059,24 +1098,24 @@ export default defineMigration({
|
|
|
1059
1098
|
}),
|
|
1060
1099
|
],
|
|
1061
1100
|
})
|
|
1062
|
-
`, $
|
|
1063
|
-
plan:
|
|
1064
|
-
apply:
|
|
1065
|
-
plans:
|
|
1066
|
-
drift:
|
|
1067
|
-
files:
|
|
1068
|
-
"rollback-file":
|
|
1069
|
-
migrate:
|
|
1070
|
-
rollback:
|
|
1071
|
-
status:
|
|
1072
|
-
seed:
|
|
1101
|
+
`, $ = {
|
|
1102
|
+
plan: Ge,
|
|
1103
|
+
apply: qe,
|
|
1104
|
+
plans: Je,
|
|
1105
|
+
drift: tt,
|
|
1106
|
+
files: rt,
|
|
1107
|
+
"rollback-file": it,
|
|
1108
|
+
migrate: ke,
|
|
1109
|
+
rollback: Ae,
|
|
1110
|
+
status: je,
|
|
1111
|
+
seed: Ne,
|
|
1073
1112
|
generate: async (e) => {
|
|
1074
|
-
let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o =
|
|
1075
|
-
return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d,
|
|
1113
|
+
let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o = ot(r ?? u(i) ?? "migration"), s = `${at()}_${o}`, l = f(a, "migrations"), d = f(l, `${s}.migration.ts`);
|
|
1114
|
+
return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d, st(s, o), "utf8"), z.info(`created ${p(a, d)}`), console.error("Fill in the apply/undo steps, then run `voltro db migrate` (rollback with `voltro db rollback`)."), console.error("For a plain schema shape change you may not need this — `voltro db apply` diffs declared vs live."), 0;
|
|
1076
1115
|
},
|
|
1077
|
-
squash:
|
|
1078
|
-
"restore-snapshot":
|
|
1079
|
-
"gc-snapshots":
|
|
1116
|
+
squash: Ze,
|
|
1117
|
+
"restore-snapshot": Ye,
|
|
1118
|
+
"gc-snapshots": Xe,
|
|
1080
1119
|
adopt: async (e) => {
|
|
1081
1120
|
let t = (t) => {
|
|
1082
1121
|
let n = e.indexOf(`--${t}`);
|
|
@@ -1090,32 +1129,32 @@ export default defineMigration({
|
|
|
1090
1129
|
into: r,
|
|
1091
1130
|
map: o.map,
|
|
1092
1131
|
...o.leaveUnset ? { leaveUnset: o.leaveUnset } : {}
|
|
1093
|
-
}, c =
|
|
1132
|
+
}, c = G(), l = await K(), u = await q(l, b.SqlClient.pipe(g.flatMap(D))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
|
|
1094
1133
|
name: e.name,
|
|
1095
1134
|
nullable: e.nullable,
|
|
1096
1135
|
hasDefault: e.defaultValue !== void 0 && e.defaultValue !== null
|
|
1097
1136
|
})), f = d(n), p = d(r);
|
|
1098
1137
|
if (f === void 0) return console.error(`voltro db adopt: '${n}' does not exist in the database`), 1;
|
|
1099
1138
|
if (p === void 0) return console.error(`voltro db adopt: '${r}' does not exist in the database — boot once so the plugin's table is created`), 1;
|
|
1100
|
-
let h = async (e) => await
|
|
1101
|
-
for (let e of
|
|
1139
|
+
let h = async (e) => await q(l, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), _ = async (e) => Number(Object.values((await h(`SELECT COUNT(*) AS n FROM "${e}"`))[0] ?? { n: 0 })[0] ?? 0), v = be(s, f, p, await _(r));
|
|
1140
|
+
for (let e of Se(s, v, await _(n))) console.log(e);
|
|
1102
1141
|
if (v.fatal) return 1;
|
|
1103
1142
|
if (!e.includes("--apply")) return console.log(""), console.log(" (dry run — nothing written. Re-run with --apply.)"), 0;
|
|
1104
|
-
let y = await
|
|
1143
|
+
let y = await Te(s, v, c, "id", void 0, h, { keepSource: e.includes("--keep-source") });
|
|
1105
1144
|
return console.log(""), console.log(` copied ${y.copied}/${y.sourceRows} row(s)`), console.log(` snapshot: ${y.snapshotTable} — restore from here, then drop it yourself`), y.verified ? (console.log(y.sourceDropped ? ` dropped ${n}` : ` ${n} left in place (--keep-source)`), 0) : (console.error(" ✗ counts DIFFER — the source was NOT dropped. Compare the two tables before doing anything else."), 1);
|
|
1106
1145
|
}
|
|
1107
|
-
},
|
|
1146
|
+
}, ct = async (e) => {
|
|
1108
1147
|
let [t, ...n] = e;
|
|
1109
1148
|
if (!t) {
|
|
1110
1149
|
console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
|
|
1111
|
-
for (let e of Object.keys($
|
|
1150
|
+
for (let e of Object.keys($)) console.error(` ${e}`);
|
|
1112
1151
|
return 1;
|
|
1113
1152
|
}
|
|
1114
|
-
let r = $
|
|
1153
|
+
let r = $[t];
|
|
1115
1154
|
if (!r) return console.error(`voltro db: unknown subcommand '${t}'`), 2;
|
|
1116
|
-
(process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) &&
|
|
1155
|
+
(process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && z.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
|
|
1117
1156
|
let i = (e) => {
|
|
1118
|
-
|
|
1157
|
+
U(e) ? z.error(W) : console.error(e), process.exit(1);
|
|
1119
1158
|
};
|
|
1120
1159
|
process.on("uncaughtException", i);
|
|
1121
1160
|
try {
|
|
@@ -1125,4 +1164,4 @@ export default defineMigration({
|
|
|
1125
1164
|
}
|
|
1126
1165
|
};
|
|
1127
1166
|
//#endregion
|
|
1128
|
-
export {
|
|
1167
|
+
export { Ie as a, _e as c, et as i, ve as l, H as n, U as o, Fe as r, ct as s, W as t };
|