@voltro/cli 0.36.0 → 0.38.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 +182 -0
- package/dist/{apiBuild-DiE9zcj6.js → apiBuild-CvacV4zA.js} +2 -2
- package/dist/apiBuild-ttSDDOGp.js +2 -0
- package/dist/{baselineCommand-DIttzO8A.js → baselineCommand-C6NMt-oa.js} +1 -4
- package/dist/baselineCommand-Cfw2Afwm.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/bootRefusal-pgvDxlrU.js +7 -0
- package/dist/{build-DBe6c7qC.js → build-_LoTC1s0.js} +31 -31
- package/dist/{checkCommand-BhG0gmUD.js → checkCommand-Pjk5sBl2.js} +3 -3
- package/dist/{checkCommand-BTA6P3Yr.js → checkCommand-wibtQx-N.js} +1 -1
- package/dist/{cloudCmd-Cvv5HGaZ.js → cloudCmd-NQSwe_Qk.js} +1 -1
- package/dist/{codegen-CYM3Zqrf.js → codegen-GYYcdpCg.js} +2 -2
- package/dist/codegen-uYrrDzQv.js +2 -0
- package/dist/{codegenCommand-DH-8ZXzR.js → codegenCommand-C60m_LCp.js} +2 -2
- package/dist/{codemodRunner-DjQGrdmg.js → codemodRunner-CXgQ-ecJ.js} +339 -167
- package/dist/{commands-Bp7utV-7.js → commands-DaZVi9wi.js} +74 -73
- package/dist/{dashboardCommand-D5Jwjmr1.js → dashboardCommand-Dxy_kmoW.js} +3 -3
- package/dist/{dataCommand-DnCm0B2C.js → dataCommand-D6-Ha2lf.js} +2 -2
- package/dist/{dbCommand-O8HA63s2.js → dbCommand-C4UW8cVN.js} +353 -351
- package/dist/dbCommand-CC-qNzpz.js +2 -0
- package/dist/{dev-BSFTgCQ5.js → dev-CAOoXOkc.js} +1 -1
- package/dist/{dev-Dc4cpmRl.js → dev-E7Eo5GHc.js} +262 -261
- package/dist/doctorCommand-Cjl8Yii8.js +2 -0
- package/dist/{doctorCommand-CNFtt3nR.js → doctorCommand-gmDyALFL.js} +10 -10
- package/dist/{dormancyCommand-6F6NsJKf.js → dormancyCommand-CE6wG8lj.js} +1 -1
- package/dist/{embeddingsCommand-DRRdneJD.js → embeddingsCommand-C3uAuf6N.js} +1 -1
- package/dist/{envCommand-TZzcyqZ-.js → envCommand-DzuWh8ya.js} +19 -19
- package/dist/{evolveCommand-iU35TPjT.js → evolveCommand-DPLqcYb2.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{infoCommand-unqisJGp.js → infoCommand-CmczGj_l.js} +1 -1
- package/dist/inspectGateHint-BF6608UT.js +4 -0
- package/dist/{metaCommands-7MJfZ5cf.js → metaCommands-CIoQNlaQ.js} +1 -1
- package/dist/{migrate-BV7I-ZHZ.js → migrate-RwgUWXfc.js} +1 -1
- package/dist/{privacyCommand-C-Df56U_.js → privacyCommand-XejDMvmu.js} +4 -3
- package/dist/{probeCommand-CZfaaUOZ.js → probeCommand-BPEpwT32.js} +40 -39
- package/dist/{projectScaffold-DmzEKHib.js → projectScaffold-CxgtJvlb.js} +1 -1
- package/dist/{projectScaffold-LMMtaavR.js → projectScaffold-DDgWbNLe.js} +2 -2
- package/dist/{runtimeTrace-D5LJ_N55.js → runtimeTrace-6jfznSzr.js} +1 -1
- package/dist/{sdkgen-82xVuvbt.js → sdkgen-CEJZZQUA.js} +1 -1
- package/dist/serveCommand-DNRF2nDI.js +2 -0
- package/dist/{serveCommand-VVGcIMIw.js → serveCommand-DnY4VS3D.js} +541 -540
- package/dist/serveEntry.js +1 -1
- package/dist/{start-BgN62boB.js → start-CidwCcjl.js} +1 -1
- package/dist/{start-T4VesWiM.js → start-RCh6qKFe.js} +24 -24
- package/dist/startEntry.js +1 -1
- package/dist/startup.d.ts +40 -1
- package/dist/startup.js +2 -2
- package/dist/startupRunner-CEqQq7ax.js +108 -0
- package/dist/subcommandNames-CKG5Dz3a.js +27 -0
- package/dist/{templates-De8IR5-c.js → templates-BR-fb4SP.js} +1 -4
- package/dist/{test-CI6iDsYc.js → test-DIQ0jlkQ.js} +1 -1
- package/dist/updateCommand-B0J_T9Vs.js +2 -0
- package/dist/{updateCommand-CwnUK_mf.js → updateCommand-CmksX7m_.js} +1 -1
- package/dist/{webDev-Dybxew86.js → webDev-BKdD7c_a.js} +689 -691
- package/dist/webDev-CcJfdSjl2.js +2 -0
- package/dist/{webhooksCommand-di1CJVzu.js → webhooksCommand-BHeaF1ZJ.js} +1 -1
- package/package.json +35 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +16 -0
- package/templates/agent-docs/data.md +30 -0
- package/templates/agent-docs/plugins/audit.md +50 -0
- package/templates/agent-docs/plugins/auth.md +4 -6
- package/templates/agent-docs/reference.md +3 -2
- package/templates/agent-docs/whats-new.md +59 -35
- 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-cms/tests/content.write.test.ts +16 -2
- package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +11 -5
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-collab/tests/documents.setBody.test.ts +8 -2
- 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/mutations/documents.update.mutation.server.ts +7 -5
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +3 -3
- package/templates/patches/@effect__cluster@0.60.0.patch +51 -3
- package/dist/apiBuild-ZEZnCXn2.js +0 -2
- package/dist/baselineCommand-C2ClWZN3.js +0 -2
- package/dist/codegen-ChBi_hVa.js +0 -2
- package/dist/dbCommand-DS4b97Is.js +0 -2
- package/dist/doctorCommand-BYffQc0s.js +0 -2
- package/dist/serveCommand-CEBVP36A.js +0 -2
- package/dist/startupRunner-DPGFchOa.js +0 -71
- package/dist/updateCommand-CSBXVsYq.js +0 -2
- package/dist/webDev-BcykISYQ2.js +0 -2
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as n } from "./
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { n as
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
1
|
+
import { t as e } from "./subcommandNames-CKG5Dz3a.js";
|
|
2
|
+
import { n as t, r as n } from "./nodeEnvironment-cGFAj1J8.js";
|
|
3
|
+
import { t as r } from "./cliError-BmdYnghb.js";
|
|
4
|
+
import { n as i } from "./appModuleLoader-C9r9mxZt.js";
|
|
5
|
+
import { a, i as o, n as s, t as c } from "./cliArgs-D4p8n7EE.js";
|
|
6
|
+
import { r as l } from "./dialectDriver-CgXnDfec.js";
|
|
7
|
+
import { a as u } from "./connectionConfig-UFlIEiys.js";
|
|
8
|
+
import { i as d, n as f, r as p, s as m, t as h } from "./seedRunner-ZmLSqNe2.js";
|
|
9
|
+
import { n as g, s as _, y as v } from "./frameworkTableAssembly-oMPBKqlE.js";
|
|
10
|
+
import { b as y, x as b } from "./fileConventions-Cof68_BL.js";
|
|
11
|
+
import { n as ee, t as x } from "./inspectGateHint-BF6608UT.js";
|
|
12
|
+
import { basename as te, join as S, relative as C, resolve as w } from "node:path";
|
|
13
|
+
import { promises as T, writeSync as ne } from "node:fs";
|
|
14
|
+
import { pathToFileURL as E } from "node:url";
|
|
15
|
+
import { createLogger as D } from "@voltro/logger";
|
|
16
|
+
import { Effect as O } from "effect";
|
|
17
|
+
import { SqlClient as k } from "@effect/sql";
|
|
18
|
+
import { DEFAULT_CDC_CHANNEL as A, applyPlan as re, applySchema as ie, assessRollingDeployGate as ae, branchRehearsalExitCode as oe, declaredSnapshot as se, destructiveScope as ce, fingerprintSchema as le, formatBranchRehearsal as ue, ignoreTablesFromEnv as j, introspectSchema as de, pendingFileMigrationIds as fe, planMigrations as M, recordUpToDate as pe, rehearseMigrationOnBranch as me, rollbackFileBasedMigration as he, rollingDeployUnsafeOps as ge, runFileBasedMigrations as _e, shortFingerprint as N, squashMigrationPlans as ve, unblockLossy as ye } from "@voltro/database/sql";
|
|
19
|
+
import { actorsTable as be, branchNamespaceName as xe, frameworkTables as Se, isEncrypted as P, isMigrationDefinition as Ce, makeNamespaceBranchExecutor as we, provisionBranch as Te, quoteIdent as Ee, registerCoreTables as De, resolveBranchMechanism as Oe, teardownBranch as ke } from "@voltro/database";
|
|
20
|
+
import { InMemoryDataStore as Ae, makeFieldCipher as je } from "@voltro/runtime";
|
|
21
|
+
import { createHash as Me } from "node:crypto";
|
|
20
22
|
//#region src/adoptPlan.ts
|
|
21
|
-
var
|
|
23
|
+
var Ne = (e) => typeof e != "string", Pe = (e, t, n, r) => {
|
|
22
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 ?? []);
|
|
23
25
|
r > 0 && i.push({
|
|
24
26
|
kind: "target-not-empty",
|
|
@@ -35,7 +37,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
35
37
|
});
|
|
36
38
|
continue;
|
|
37
39
|
}
|
|
38
|
-
if (
|
|
40
|
+
if (Ne(n)) {
|
|
39
41
|
c.push({
|
|
40
42
|
target: t,
|
|
41
43
|
source: n.expr,
|
|
@@ -78,23 +80,23 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
78
80
|
remintIds: f,
|
|
79
81
|
fatal: i.some((e) => e.fatal)
|
|
80
82
|
};
|
|
81
|
-
},
|
|
83
|
+
}, Fe = (e, t) => [
|
|
82
84
|
`back up '${e.from}' to a restorable dump`,
|
|
83
85
|
`copy rows: INSERT INTO ${e.into} (${t.assignments.map((e) => e.target).join(", ")}) SELECT … FROM ${e.from}`,
|
|
84
86
|
...t.remintIds ? ["record old id → new id in a translation table", `rewrite every reference to '${e.from}', JSON columns included`] : [],
|
|
85
87
|
"verify: row counts match and a sample round-trips",
|
|
86
88
|
`drop '${e.from}' — LAST, and only after the verify`
|
|
87
|
-
],
|
|
89
|
+
], Ie = (e, t, n) => {
|
|
88
90
|
let r = [`adopt · ${e.from} (${n} row(s)) → ${e.into}`, ""];
|
|
89
91
|
for (let e of t.assignments) r.push(` ${e.target.padEnd(24)} ← ${e.raw ? `${e.source} (raw)` : e.source}`);
|
|
90
92
|
t.problems.length > 0 && r.push("");
|
|
91
93
|
for (let e of t.problems) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.detail}`);
|
|
92
94
|
if (r.push(""), t.fatal) return r.push(" REFUSED — fix the problems above. Nothing was written."), r;
|
|
93
95
|
r.push(" would run, in this order:");
|
|
94
|
-
for (let [n, i] of
|
|
96
|
+
for (let [n, i] of Fe(e, t).entries()) r.push(` ${n + 1}. ${i}`);
|
|
95
97
|
return r;
|
|
96
|
-
},
|
|
97
|
-
let a =
|
|
98
|
+
}, Le = (e) => e === "mysql" || e === "mariadb" ? (e) => `\`${e}\`` : e === "mssql" ? (e) => `[${e}]` : (e) => `"${e}"`, Re = (e) => `${e}__adopt_snapshot`, ze = (e, t, n, r, i) => {
|
|
99
|
+
let a = Le(n), o = Re(e.from), s = t.assignments.map((e) => e.target), c = t.assignments.map((e) => e.raw ? e.source : a(e.source));
|
|
98
100
|
return i !== void 0 && (s.unshift(r), c.unshift(i)), {
|
|
99
101
|
snapshot: n === "mssql" ? `SELECT * INTO ${a(o)} FROM ${a(e.from)}` : `CREATE TABLE ${a(o)} AS SELECT * FROM ${a(e.from)}`,
|
|
100
102
|
copy: `INSERT INTO ${a(e.into)} (${s.map(a).join(", ")}) SELECT ${c.join(", ")} FROM ${a(e.from)}`,
|
|
@@ -104,18 +106,18 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
104
106
|
drop: `DROP TABLE ${a(e.from)}`,
|
|
105
107
|
dropSnapshot: `DROP TABLE ${a(o)}`
|
|
106
108
|
};
|
|
107
|
-
},
|
|
109
|
+
}, Be = (e) => {
|
|
108
110
|
let t = e[0];
|
|
109
111
|
if (t === void 0) return 0;
|
|
110
112
|
let n = t.n ?? t.N ?? Object.values(t)[0];
|
|
111
113
|
return Number(n ?? 0);
|
|
112
|
-
},
|
|
114
|
+
}, Ve = async (e, t, n, r, i, a, o = {}) => {
|
|
113
115
|
if (t.fatal) throw Error("adopt: the plan was refused — nothing was run");
|
|
114
|
-
let s =
|
|
116
|
+
let s = ze(e, t, n, r, i), c = Re(e.from), l = Be(await a(s.verifySource));
|
|
115
117
|
await a(s.snapshot);
|
|
116
118
|
for (let e of s.translations) await a(e);
|
|
117
119
|
await a(s.copy);
|
|
118
|
-
let u =
|
|
120
|
+
let u = Be(await a(s.verifyTarget));
|
|
119
121
|
return u === l ? o.keepSource === !0 ? {
|
|
120
122
|
copied: u,
|
|
121
123
|
sourceRows: l,
|
|
@@ -135,7 +137,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
135
137
|
snapshotTable: c,
|
|
136
138
|
sourceDropped: !1
|
|
137
139
|
};
|
|
138
|
-
},
|
|
140
|
+
}, F = [
|
|
139
141
|
"token",
|
|
140
142
|
"secret",
|
|
141
143
|
"password",
|
|
@@ -144,7 +146,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
144
146
|
"credential",
|
|
145
147
|
"privatekey",
|
|
146
148
|
"private_key"
|
|
147
|
-
],
|
|
149
|
+
], He = [
|
|
148
150
|
{
|
|
149
151
|
table: "_voltro_audit_log",
|
|
150
152
|
column: "subject"
|
|
@@ -189,7 +191,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
189
191
|
table: "_voltro_row_history",
|
|
190
192
|
column: "subjectId"
|
|
191
193
|
}
|
|
192
|
-
],
|
|
194
|
+
], Ue = (e, t) => {
|
|
193
195
|
switch (t) {
|
|
194
196
|
case "postgres": return `LOWER(CAST(${e} AS text))`;
|
|
195
197
|
case "mysql":
|
|
@@ -198,17 +200,17 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
198
200
|
case "sqlite":
|
|
199
201
|
case "turso": return `LOWER(CAST(${e} AS TEXT))`;
|
|
200
202
|
}
|
|
201
|
-
},
|
|
203
|
+
}, We = (e, t, n = F) => n.map((n) => `${Ue(e, t)} LIKE '%${n}%'`).join("\n OR "), Ge = ["_omitted", "__redacted"], Ke = (e, t) => {
|
|
202
204
|
let n = e.toLowerCase();
|
|
203
205
|
return t.some((e) => n.includes(e));
|
|
204
|
-
},
|
|
206
|
+
}, qe = (e, t = []) => {
|
|
205
207
|
if (Array.isArray(e)) {
|
|
206
|
-
for (let n of e)
|
|
208
|
+
for (let n of e) qe(n, t);
|
|
207
209
|
return t;
|
|
208
210
|
}
|
|
209
|
-
if (typeof e == "object" && e) for (let [n, r] of Object.entries(e)) t.push(n),
|
|
211
|
+
if (typeof e == "object" && e) for (let [n, r] of Object.entries(e)) t.push(n), qe(r, t);
|
|
210
212
|
return t;
|
|
211
|
-
},
|
|
213
|
+
}, Je = (e) => Array.isArray(e) ? e.map(Je) : typeof e == "object" && e ? Object.fromEntries(Object.entries(e).map(([e, t]) => [e, Ge.includes(e) ? null : Je(t)])) : e, Ye = (e, t = F) => {
|
|
212
214
|
let n;
|
|
213
215
|
try {
|
|
214
216
|
n = JSON.parse(e);
|
|
@@ -220,33 +222,33 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
220
222
|
};
|
|
221
223
|
}
|
|
222
224
|
return {
|
|
223
|
-
asKey:
|
|
224
|
-
redactionMarkerOnly: !
|
|
225
|
+
asKey: qe(n).some((e) => Ke(e, t)),
|
|
226
|
+
redactionMarkerOnly: !Ke(JSON.stringify(Je(n)) ?? "", t),
|
|
225
227
|
undecidable: !1
|
|
226
228
|
};
|
|
227
|
-
},
|
|
229
|
+
}, Xe = (e) => ({
|
|
228
230
|
examined: e.length,
|
|
229
231
|
asKey: e.filter((e) => e.asKey).length,
|
|
230
232
|
redactionMarkerOnly: e.filter((e) => e.redactionMarkerOnly && !e.asKey).length,
|
|
231
233
|
undecidable: e.filter((e) => e.undecidable).length
|
|
232
|
-
}),
|
|
234
|
+
}), I = (e) => {
|
|
233
235
|
let t = e.classification;
|
|
234
236
|
return t === void 0 || e.hits === void 0 || e.hits === 0 ? !1 : t.examined === e.hits && t.undecidable === 0 && t.asKey === 0 && t.redactionMarkerOnly === e.hits;
|
|
235
|
-
},
|
|
236
|
-
let i = `WHERE ${
|
|
237
|
+
}, Ze = (e, t, n, r) => {
|
|
238
|
+
let i = `WHERE ${We(t, n)}`, a = Ue(t, n).replace(/^LOWER\((.*)\)$/s, "$1");
|
|
237
239
|
return n === "mssql" ? `SELECT TOP (${r}) ${a} AS v FROM ${e}\n ${i}` : `SELECT ${a} AS v FROM ${e}\n ${i}\n LIMIT ${r}`;
|
|
238
|
-
},
|
|
240
|
+
}, Qe = async (e, t, n, r, i) => (await O.runPromise(e.unsafe(Ze(t, n, r, i)))).map((e) => {
|
|
239
241
|
let t = Object.values(e)[0];
|
|
240
242
|
return typeof t == "string" ? t : JSON.stringify(t ?? null);
|
|
241
|
-
}),
|
|
242
|
-
let n = await
|
|
243
|
+
}), L = async (e, t) => {
|
|
244
|
+
let n = await O.runPromise(e.unsafe(t));
|
|
243
245
|
return Number(Object.values(n[0] ?? { n: 0 })[0] ?? 0);
|
|
244
|
-
},
|
|
246
|
+
}, $e = async (e, t, n, r) => {
|
|
245
247
|
let i = [];
|
|
246
248
|
for (let a of n) {
|
|
247
249
|
let n = r(a.table), o;
|
|
248
250
|
try {
|
|
249
|
-
o = await
|
|
251
|
+
o = await L(e, `SELECT COUNT(*) AS n FROM ${n}`);
|
|
250
252
|
} catch {
|
|
251
253
|
i.push({
|
|
252
254
|
table: a.table,
|
|
@@ -258,9 +260,9 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
258
260
|
continue;
|
|
259
261
|
}
|
|
260
262
|
try {
|
|
261
|
-
let s = await
|
|
262
|
-
if (s > 0) for (let i of
|
|
263
|
-
let o = await
|
|
263
|
+
let s = await L(e, `SELECT COUNT(*) AS n FROM ${n}\n WHERE ${We(r(a.column), t)}`), c = [];
|
|
264
|
+
if (s > 0) for (let i of F) try {
|
|
265
|
+
let o = await L(e, `SELECT COUNT(*) AS n FROM ${n}\n WHERE ${We(r(a.column), t, [i])}`);
|
|
264
266
|
o > 0 && c.push({
|
|
265
267
|
needle: i,
|
|
266
268
|
rows: o
|
|
@@ -268,7 +270,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
268
270
|
} catch {}
|
|
269
271
|
let l;
|
|
270
272
|
if (s > 0) try {
|
|
271
|
-
l =
|
|
273
|
+
l = Xe((await Qe(e, n, r(a.column), t, 500)).map((e) => Ye(e)));
|
|
272
274
|
} catch {}
|
|
273
275
|
i.push({
|
|
274
276
|
table: a.table,
|
|
@@ -289,8 +291,8 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
289
291
|
}
|
|
290
292
|
}
|
|
291
293
|
return i;
|
|
292
|
-
},
|
|
293
|
-
let n =
|
|
294
|
+
}, et = (e, t) => {
|
|
295
|
+
let n = F.length, r = [`credential scan · dialect ${t}`, ""], i = /* @__PURE__ */ new Set();
|
|
294
296
|
for (let t of e) {
|
|
295
297
|
if (t.unavailable === "column") {
|
|
296
298
|
r.push(` · ${t.table}.${t.column} — column is not present on this deployment (${t.scanned} row(s) exist but this column was not scanned)`);
|
|
@@ -304,34 +306,34 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
304
306
|
r.push(` · ${t.table} — table is EMPTY (0 rows scanned; this is not a clean bill of health)`);
|
|
305
307
|
continue;
|
|
306
308
|
}
|
|
307
|
-
let e =
|
|
309
|
+
let e = I(t), n = (t.hits ?? 0) === 0 ? "✓" : e ? "~" : "✗";
|
|
308
310
|
r.push(` ${n} ${t.table}.${t.column} — ${t.hits} of ${t.scanned} row(s) contain a credential-shaped NAME (anywhere in the value)`), (t.matchedNeedles?.length ?? 0) > 0 && r.push(` matched (rows per needle, may overlap): ${t.matchedNeedles.map((e) => `${e.needle} (${e.rows})`).join(", ")}`);
|
|
309
311
|
let a = t.classification;
|
|
310
|
-
a !== void 0 && (r.push(` examined ${a.examined} of ${t.hits} matched row(s)${a.examined < (t.hits ?? 0) ? " (capped at 500)" : ""}: ${a.asKey} as a JSON KEY, ${a.redactionMarkerOnly} only inside a redaction marker${a.undecidable > 0 ? `, ${a.undecidable} not parseable as JSON` : ""}`), e && r.push(` EXPLAINED — every match is \`${
|
|
312
|
+
a !== void 0 && (r.push(` examined ${a.examined} of ${t.hits} matched row(s)${a.examined < (t.hits ?? 0) ? " (capped at 500)" : ""}: ${a.asKey} as a JSON KEY, ${a.redactionMarkerOnly} only inside a redaction marker${a.undecidable > 0 ? `, ${a.undecidable} not parseable as JSON` : ""}`), e && r.push(` EXPLAINED — every match is \`${Ge.join("` / `")}\`, the framework's own`, " record that a `.sensitive()` column was left OUT. Nothing to purge, nothing to rotate."));
|
|
311
313
|
}
|
|
312
|
-
let a = e.reduce((e, t) => e + (
|
|
314
|
+
let a = e.reduce((e, t) => e + (I(t) ? 0 : t.hits ?? 0), 0), o = e.reduce((e, t) => e + (I(t) ? t.hits ?? 0 : 0), 0);
|
|
313
315
|
return r.push(""), o > 0 && r.push(` ${o} row(s) matched only the framework's own redaction markers and are NOT findings.`), a > 0 ? r.push(` ${a} row(s) carry something credential-shaped.`, " Purge them AND rotate the credentials — assume anything written to a log", " table has been read. Then stop putting the credential on the Subject:", " `connectionCredentials(...)` keeps it in the framework vault.") : e.every((e) => e.scanned === void 0 || e.scanned === 0 || e.unavailable !== void 0) ? r.push(" NOTHING WAS SCANNED. This run proves nothing about your data.") : r.push(" No credential-shaped keys found in the rows that exist."), r.push("", ` These are NAME heuristics (${n} of them), not proof. A credential in a`, " column whose key is not credential-shaped — `keyValue`, `pat`, `authz` —", " is invisible here. A clean run means \"no credential-shaped NAME found\",", " which is not the same as \"no credential\".", "", " Run this on EVERY environment. A dev database is not a sample of production."), r.join("\n");
|
|
314
|
-
},
|
|
316
|
+
}, tt = (e) => e.some((e) => (e.hits ?? 0) > 0 && !I(e)) ? 1 : e.every((e) => e.unavailable !== void 0 || e.scanned === void 0 || e.scanned === 0) ? 2 : 0, R = /\.migration\.ts$/, nt = async (e, t) => {
|
|
315
317
|
let n = [];
|
|
316
318
|
for (let r of e) {
|
|
317
319
|
let e;
|
|
318
320
|
try {
|
|
319
|
-
e = await import(
|
|
321
|
+
e = await import(E(r).href);
|
|
320
322
|
} catch (e) {
|
|
321
323
|
t.warn("migration: failed to import", { file: r }, e);
|
|
322
324
|
continue;
|
|
323
325
|
}
|
|
324
|
-
let i = (e.default &&
|
|
326
|
+
let i = (e.default && Ce(e.default) ? e.default : void 0) ?? Object.values(e).find(Ce);
|
|
325
327
|
if (!i) {
|
|
326
328
|
t.warn("migration: no defineMigration export", { file: r });
|
|
327
329
|
continue;
|
|
328
330
|
}
|
|
329
|
-
i.id !==
|
|
331
|
+
i.id !== te(r).replace(/\.migration\.ts$/, "") && t.warn("migration: id does not match filename — id wins for ordering, but rename the file for clarity", {
|
|
330
332
|
file: r,
|
|
331
333
|
idInFile: i.id,
|
|
332
|
-
idFromFile:
|
|
334
|
+
idFromFile: te(r).replace(/\.migration\.ts$/, "")
|
|
333
335
|
});
|
|
334
|
-
let a = await
|
|
336
|
+
let a = await T.readFile(r, "utf8").catch(() => ""), o = Me("sha256").update(a).digest("hex").slice(0, 16);
|
|
335
337
|
n.push({
|
|
336
338
|
file: r,
|
|
337
339
|
definition: i,
|
|
@@ -339,14 +341,14 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
339
341
|
});
|
|
340
342
|
}
|
|
341
343
|
return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
|
|
342
|
-
},
|
|
343
|
-
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t, r = n, i = t === "memory" ? void 0 : await
|
|
344
|
-
return
|
|
345
|
-
},
|
|
346
|
-
let e = yield* (yield*
|
|
344
|
+
}, rt = async (e) => {
|
|
345
|
+
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t, r = n, i = t === "memory" ? void 0 : await l(n).then((e) => e.retryFilter, () => void 0);
|
|
346
|
+
return O.runPromise(ie(Se, r, void 0, i === void 0 ? {} : { retryFilter: i }).pipe(O.provide(e)));
|
|
347
|
+
}, it = (e) => O.runPromise(O.gen(function* () {
|
|
348
|
+
let e = yield* (yield* k.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(O.orElseSucceed(() => []));
|
|
347
349
|
return new Map(e.map((e) => [e.id, e]));
|
|
348
|
-
}).pipe(
|
|
349
|
-
yield* (yield*
|
|
350
|
+
}).pipe(O.provide(e))), z = (e, t) => O.runPromise(O.gen(function* () {
|
|
351
|
+
yield* (yield* k.SqlClient)`
|
|
350
352
|
INSERT INTO "_voltro_migrations"
|
|
351
353
|
(id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
|
|
352
354
|
VALUES
|
|
@@ -361,14 +363,14 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
361
363
|
"errorStack" = EXCLUDED."errorStack",
|
|
362
364
|
"updatedAt" = now()
|
|
363
365
|
`;
|
|
364
|
-
}).pipe(
|
|
366
|
+
}).pipe(O.provide(e))), at = async (e, t, n) => {
|
|
365
367
|
if (e.length === 0) return {
|
|
366
368
|
applied: [],
|
|
367
369
|
failed: null,
|
|
368
370
|
skipped: []
|
|
369
371
|
};
|
|
370
|
-
await
|
|
371
|
-
let r = await
|
|
372
|
+
await rt(t);
|
|
373
|
+
let r = await it(t), i = [], a = [], o = null;
|
|
372
374
|
for (let s of e) {
|
|
373
375
|
let e = r.get(s.definition.id);
|
|
374
376
|
if (e && e.status === "applied") {
|
|
@@ -403,7 +405,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
403
405
|
for (let e of s.definition.steps) {
|
|
404
406
|
let n = Date.now();
|
|
405
407
|
try {
|
|
406
|
-
await
|
|
408
|
+
await O.runPromise(e.apply.pipe(O.provide(t))), l.push({
|
|
407
409
|
stepName: e.name,
|
|
408
410
|
status: "applied",
|
|
409
411
|
durationMs: Date.now() - n
|
|
@@ -469,9 +471,9 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
469
471
|
failed: o,
|
|
470
472
|
skipped: a
|
|
471
473
|
};
|
|
472
|
-
},
|
|
473
|
-
await
|
|
474
|
-
let i = await
|
|
474
|
+
}, ot = async (e, t, n, r = {}) => {
|
|
475
|
+
await rt(t);
|
|
476
|
+
let i = await it(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
|
|
475
477
|
if (a.length === 0) return n.info("migration: nothing to roll back"), {
|
|
476
478
|
applied: [],
|
|
477
479
|
failed: null,
|
|
@@ -489,7 +491,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
489
491
|
for (let n = e.definition.steps.length - 1; n >= 0; n--) {
|
|
490
492
|
let o = e.definition.steps[n], s = Date.now();
|
|
491
493
|
try {
|
|
492
|
-
await
|
|
494
|
+
await O.runPromise(o.undo.pipe(O.provide(t))), i.push({
|
|
493
495
|
stepName: o.name,
|
|
494
496
|
status: "applied",
|
|
495
497
|
durationMs: Date.now() - s
|
|
@@ -540,9 +542,9 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
540
542
|
failed: c,
|
|
541
543
|
skipped: []
|
|
542
544
|
};
|
|
543
|
-
},
|
|
544
|
-
await
|
|
545
|
-
let n = await
|
|
545
|
+
}, st = async (e, t) => {
|
|
546
|
+
await rt(t);
|
|
547
|
+
let n = await it(t);
|
|
546
548
|
return e.map((e) => {
|
|
547
549
|
let t = n.get(e.definition.id);
|
|
548
550
|
return t ? {
|
|
@@ -559,7 +561,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
559
561
|
hashMatches: !0
|
|
560
562
|
};
|
|
561
563
|
});
|
|
562
|
-
},
|
|
564
|
+
}, ct = (e) => {
|
|
563
565
|
if (!e.failed) return "";
|
|
564
566
|
let t = e.failed, n = [
|
|
565
567
|
`✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
|
|
@@ -572,22 +574,22 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
572
574
|
""
|
|
573
575
|
];
|
|
574
576
|
return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
|
|
575
|
-
},
|
|
577
|
+
}, lt = (e) => {
|
|
576
578
|
let t = e.split(".");
|
|
577
579
|
if (t.length !== 2 || !t[0] || !t[1]) throw Error(`voltro db encrypt-column: expected <table>.<column>, got "${e}"`);
|
|
578
580
|
return {
|
|
579
581
|
table: t[0],
|
|
580
582
|
column: t[1]
|
|
581
583
|
};
|
|
582
|
-
},
|
|
583
|
-
let n =
|
|
584
|
+
}, ut = (e) => 49 + 4 * Math.ceil(e / 3), dt = (e, t) => {
|
|
585
|
+
let n = ut(e);
|
|
584
586
|
return {
|
|
585
587
|
longestPlaintextBytes: e,
|
|
586
588
|
longestCiphertext: n,
|
|
587
589
|
declaredMaxLength: t,
|
|
588
590
|
fits: t === void 0 || n <= t
|
|
589
591
|
};
|
|
590
|
-
},
|
|
592
|
+
}, ft = (e, t) => `${e.table}.${e.column} is too narrow: its longest value is ${t.longestPlaintextBytes} byte(s), which encrypts to ${t.longestCiphertext} characters, and the column is declared as ${t.declaredMaxLength}. Widen it first (\`.maxLength(${t.longestCiphertext})\` or more, then \`voltro db apply\`) and run this again. Nothing was written.`, pt = (e, t) => {
|
|
591
593
|
let n = t ? "would encrypt" : "encrypted";
|
|
592
594
|
return [
|
|
593
595
|
` ${e.table}.${e.column}`,
|
|
@@ -596,10 +598,10 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
596
598
|
` null / empty ${e.empty}`,
|
|
597
599
|
` ${n.padEnd(18)} ${e.encrypted}`
|
|
598
600
|
].join("\n");
|
|
599
|
-
},
|
|
601
|
+
}, mt = (e, t, n) => {
|
|
600
602
|
if (e == null) return;
|
|
601
603
|
let r = typeof e == "string" ? e : String(e);
|
|
602
|
-
if (r === "" ||
|
|
604
|
+
if (r === "" || P(r)) return;
|
|
603
605
|
let i = t.encrypt(r), a;
|
|
604
606
|
try {
|
|
605
607
|
a = t.decrypt(i);
|
|
@@ -608,13 +610,13 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
608
610
|
}
|
|
609
611
|
if (a !== r) throw Error(`${n.table}.${n.column}: round-trip mismatch — the cipher did not return what it was given. Nothing was written.`);
|
|
610
612
|
return i;
|
|
611
|
-
},
|
|
613
|
+
}, ht = (e) => Buffer.byteLength(e, "utf8"), gt = (e, t) => {
|
|
612
614
|
if (e !== void 0) try {
|
|
613
615
|
return t.decrypt(e), !0;
|
|
614
616
|
} catch {
|
|
615
617
|
return !1;
|
|
616
618
|
}
|
|
617
|
-
},
|
|
619
|
+
}, _t = (e, t) => `${e.table}.${e.column} already holds values encrypted with a DIFFERENT key than ${t} — the active key cannot decrypt them. Continuing would encrypt the remaining rows under a second key, and the column would then be readable with neither key alone. Nothing was written. Point ${t} at the key the existing rows were written with.`, vt = (e) => [
|
|
618
620
|
"",
|
|
619
621
|
" NEXT — this converted the rows that exist. New writes are still plaintext",
|
|
620
622
|
" until the column carries the modifier:",
|
|
@@ -624,7 +626,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
624
626
|
" Order does not matter: a read returns a non-ciphertext value unchanged, so",
|
|
625
627
|
" the column may hold a mix while you deploy. Run this again afterwards to",
|
|
626
628
|
" catch anything written in between — it skips what is already encrypted."
|
|
627
|
-
].join("\n"), B =
|
|
629
|
+
].join("\n"), B = D({ scope: "voltro:db" }), yt = /* @__PURE__ */ new Set([
|
|
628
630
|
"node_modules",
|
|
629
631
|
"dist",
|
|
630
632
|
"build",
|
|
@@ -634,15 +636,15 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
634
636
|
".git"
|
|
635
637
|
]), V = async (e, t) => {
|
|
636
638
|
let n = [], r = async (e) => {
|
|
637
|
-
let i = await
|
|
639
|
+
let i = await T.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
638
640
|
for (let a of i) {
|
|
639
|
-
if (
|
|
640
|
-
let i =
|
|
641
|
+
if (yt.has(a.name)) continue;
|
|
642
|
+
let i = S(e, a.name);
|
|
641
643
|
a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
|
|
642
644
|
}
|
|
643
645
|
};
|
|
644
646
|
return await r(e), n;
|
|
645
|
-
},
|
|
647
|
+
}, bt = [
|
|
646
648
|
"--against",
|
|
647
649
|
"--batch",
|
|
648
650
|
"--before",
|
|
@@ -661,23 +663,23 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
661
663
|
"--store",
|
|
662
664
|
"--to",
|
|
663
665
|
"--token"
|
|
664
|
-
], H = (e) =>
|
|
666
|
+
], H = (e) => w(c(e, bt) ?? process.cwd()), U = () => u("migration"), xt = (e) => {
|
|
665
667
|
let t = e;
|
|
666
668
|
if (typeof t != "object" || !t) return !1;
|
|
667
669
|
if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
|
|
668
670
|
let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
|
|
669
671
|
return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
|
|
670
|
-
},
|
|
672
|
+
}, St = "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`.)", W = () => {
|
|
671
673
|
let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
672
|
-
if (e === "memory") throw new
|
|
674
|
+
if (e === "memory") throw new r("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
|
|
673
675
|
return e;
|
|
674
|
-
}, G = (e) =>
|
|
675
|
-
let t = H(e), n = e.includes("--dry-run"), r = await
|
|
676
|
+
}, G = (e) => Ee(e, W()), K = (e) => e === "mysql" || e === "mariadb" ? "table_schema = DATABASE()" : e === "mssql" ? "table_schema = SCHEMA_NAME()" : "table_schema = 'public'", q = async () => (await l(W())).makeSqlLayer(U()), Ct = async (e) => {
|
|
677
|
+
let t = H(e), n = e.includes("--dry-run"), r = await nt(await V(t, R), B);
|
|
676
678
|
if (r.length === 0) return B.info("no migrations found", { root: t }), 0;
|
|
677
679
|
if (n) {
|
|
678
680
|
let e = /* @__PURE__ */ new Set();
|
|
679
681
|
try {
|
|
680
|
-
let t = await
|
|
682
|
+
let t = await st(r, await q());
|
|
681
683
|
e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
|
|
682
684
|
} catch {
|
|
683
685
|
B.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
|
|
@@ -688,33 +690,33 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
688
690
|
for (let e of t) console.log(` pending ${e.definition.id}`);
|
|
689
691
|
return 0;
|
|
690
692
|
}
|
|
691
|
-
let i = await
|
|
692
|
-
return i.failed ? (process.stderr.write(
|
|
693
|
+
let i = await at(r, await q(), B);
|
|
694
|
+
return i.failed ? (process.stderr.write(ct(i) + "\n"), 1) : (B.info("migrate complete", {
|
|
693
695
|
applied: i.applied.length,
|
|
694
696
|
skipped: i.skipped.length
|
|
695
697
|
}), 0);
|
|
696
|
-
},
|
|
697
|
-
let t = H(e), n =
|
|
698
|
+
}, wt = async (e) => {
|
|
699
|
+
let t = H(e), n = s(e, "--to"), r = await nt(await V(t, R), B);
|
|
698
700
|
if (r.length === 0) return B.info("no migrations found", { root: t }), 0;
|
|
699
|
-
let i = await
|
|
700
|
-
return i.failed ? (process.stderr.write(
|
|
701
|
-
},
|
|
702
|
-
let t = await
|
|
701
|
+
let i = await ot(r, await q(), B, n ? { to: n } : {});
|
|
702
|
+
return i.failed ? (process.stderr.write(ct(i) + "\n"), 1) : (B.info("rollback complete", { reverted: i.applied.length }), 0);
|
|
703
|
+
}, Tt = async (e) => {
|
|
704
|
+
let t = await nt(await V(H(e), R), B);
|
|
703
705
|
if (t.length === 0) return console.log("no migrations found"), 0;
|
|
704
|
-
let n = await
|
|
706
|
+
let n = await st(t, await q());
|
|
705
707
|
for (let e of n) {
|
|
706
708
|
let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
|
|
707
709
|
console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
|
|
708
710
|
}
|
|
709
711
|
return 0;
|
|
710
|
-
},
|
|
711
|
-
let t = (
|
|
712
|
+
}, Et = async (e) => {
|
|
713
|
+
let t = (s(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
712
714
|
if (t === "memory") return B.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), B.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
|
|
713
|
-
store: new
|
|
715
|
+
store: new Ae(),
|
|
714
716
|
dispose: async () => {},
|
|
715
717
|
dialectId: "memory"
|
|
716
718
|
};
|
|
717
|
-
let n = await
|
|
719
|
+
let n = await l(t);
|
|
718
720
|
B.info(`voltro db seed: target store=${n.id} — rows will persist`);
|
|
719
721
|
let r = await n.makeStore({
|
|
720
722
|
sqlLayer: n.makeSqlLayer(U()),
|
|
@@ -727,12 +729,12 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
727
729
|
},
|
|
728
730
|
dialectId: n.id
|
|
729
731
|
};
|
|
730
|
-
},
|
|
731
|
-
let t = H(e), n =
|
|
732
|
+
}, Dt = async (e) => {
|
|
733
|
+
let t = H(e), n = s(e, "--id"), r = s(e, "--lifecycle"), i = await f(await V(t, b), B);
|
|
732
734
|
if (i.length === 0) return B.info("no seeds found", { root: t }), 0;
|
|
733
|
-
let { store: a, dispose:
|
|
735
|
+
let { store: a, dispose: o, dialectId: c } = await Et(e);
|
|
734
736
|
try {
|
|
735
|
-
return n ? +((await
|
|
737
|
+
return n ? +((await d(i, n, a, B)).status === "failed") : +((await p((r ? i.filter((e) => e.definition.lifecycle === r) : i.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
|
|
736
738
|
...e,
|
|
737
739
|
definition: {
|
|
738
740
|
...e.definition,
|
|
@@ -740,64 +742,64 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
740
742
|
}
|
|
741
743
|
})), a, B, { force: !0 })).filter((e) => e.status === "failed").length > 0);
|
|
742
744
|
} finally {
|
|
743
|
-
await
|
|
745
|
+
await o().catch((e) => {
|
|
744
746
|
B.warn("voltro db seed: store dispose failed", { error: String(e) });
|
|
745
747
|
});
|
|
746
748
|
}
|
|
747
|
-
},
|
|
748
|
-
let t = await V(e,
|
|
749
|
+
}, Ot = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", kt = async (e) => {
|
|
750
|
+
let t = await V(e, y), n = [];
|
|
749
751
|
for (let r of t) {
|
|
750
752
|
let t;
|
|
751
753
|
try {
|
|
752
|
-
t = await import(
|
|
754
|
+
t = await import(E(r).href);
|
|
753
755
|
} catch (t) {
|
|
754
756
|
B.warn("db: failed to import schema file", {
|
|
755
|
-
file:
|
|
757
|
+
file: C(e, r),
|
|
756
758
|
error: String(t)
|
|
757
759
|
});
|
|
758
760
|
continue;
|
|
759
761
|
}
|
|
760
|
-
for (let e of Object.values(t))
|
|
762
|
+
for (let e of Object.values(t)) Ot(e) && n.push(e);
|
|
761
763
|
}
|
|
762
764
|
return n;
|
|
763
|
-
},
|
|
765
|
+
}, At = (e) => {
|
|
764
766
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
765
|
-
t.has("actors") || t.set("actors",
|
|
767
|
+
t.has("actors") || t.set("actors", be);
|
|
766
768
|
let n = t.get("actors"), r = t.get("tenants");
|
|
767
|
-
return
|
|
769
|
+
return De({
|
|
768
770
|
actors: () => n,
|
|
769
771
|
...r === void 0 ? {} : { tenants: () => r }
|
|
770
772
|
}), [...t.values()];
|
|
771
|
-
},
|
|
773
|
+
}, jt = (e) => {
|
|
772
774
|
let t = /* @__PURE__ */ new Map();
|
|
773
775
|
for (let n of e) {
|
|
774
776
|
let e = n.tableName;
|
|
775
777
|
e !== void 0 && !t.has(e) && t.set(e, n);
|
|
776
778
|
}
|
|
777
779
|
return [...t.values()];
|
|
778
|
-
},
|
|
779
|
-
let t =
|
|
780
|
+
}, Mt = (e) => {
|
|
781
|
+
let t = ce(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
780
782
|
if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
|
|
781
783
|
plan: e,
|
|
782
784
|
acknowledged: !1
|
|
783
785
|
};
|
|
784
|
-
let n =
|
|
786
|
+
let n = ye(e, t);
|
|
785
787
|
return {
|
|
786
788
|
plan: n,
|
|
787
789
|
acknowledged: !n.operations.some((e) => e.blocked)
|
|
788
790
|
};
|
|
789
|
-
}, J = async (e, t) =>
|
|
791
|
+
}, J = async (e, t) => O.runPromise(t.pipe(O.provide(e))), Y = async (e) => {
|
|
790
792
|
try {
|
|
791
|
-
return await J(e,
|
|
793
|
+
return await J(e, k.SqlClient.pipe(O.flatMap(de)));
|
|
792
794
|
} catch (e) {
|
|
793
795
|
let t = String(e?.message ?? e);
|
|
794
|
-
if (
|
|
795
|
-
let n = /* @__PURE__ */ Error(`${
|
|
796
|
+
if (xt(e) || /connection error/i.test(t)) {
|
|
797
|
+
let n = /* @__PURE__ */ Error(`${St}\n\nOriginal error: ${t}`);
|
|
796
798
|
throw n.cause = e, n;
|
|
797
799
|
}
|
|
798
800
|
throw e;
|
|
799
801
|
}
|
|
800
|
-
},
|
|
802
|
+
}, Nt = {
|
|
801
803
|
safe: "\x1B[32m✓\x1B[0m",
|
|
802
804
|
"needs-default": "\x1B[32m⊕\x1B[0m",
|
|
803
805
|
"needs-backfill": "\x1B[33m⊕\x1B[0m",
|
|
@@ -805,7 +807,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
805
807
|
lossy: "\x1B[31m✗\x1B[0m",
|
|
806
808
|
"online-required": "\x1B[36m◷\x1B[0m",
|
|
807
809
|
"multi-step": "\x1B[35m⇉\x1B[0m"
|
|
808
|
-
},
|
|
810
|
+
}, Pt = (e) => {
|
|
809
811
|
let t = e.op, n;
|
|
810
812
|
switch (t.kind) {
|
|
811
813
|
case "create-table":
|
|
@@ -869,9 +871,9 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
869
871
|
n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
|
|
870
872
|
break;
|
|
871
873
|
}
|
|
872
|
-
let r =
|
|
874
|
+
let r = Nt[e.classification], i = e.reason ? ` [2m# ${e.reason}[0m` : "", a = e.blocked ? `\n [31m! ${e.blocked.fix}[0m` : "";
|
|
873
875
|
return ` ${r} ${n}${i}${a}`;
|
|
874
|
-
},
|
|
876
|
+
}, Ft = async (e, t) => {
|
|
875
877
|
let n = e.operations.filter((e) => e.classification === "needs-default" && e.op.kind === "add-column");
|
|
876
878
|
if (n.length === 0) return;
|
|
877
879
|
let r = [];
|
|
@@ -880,7 +882,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
880
882
|
if (i === void 0 || a === void 0) continue;
|
|
881
883
|
let o = 0;
|
|
882
884
|
try {
|
|
883
|
-
let e = await J(t,
|
|
885
|
+
let e = await J(t, k.SqlClient.pipe(O.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${Ee(i, W())}`))));
|
|
884
886
|
o = Number(Object.values(e[0] ?? { n: 0 })[0] ?? 0);
|
|
885
887
|
} catch {
|
|
886
888
|
continue;
|
|
@@ -892,11 +894,11 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
892
894
|
for (let e of r) console.log(e);
|
|
893
895
|
console.log(" (the shape is correct either way — this is the VALUE a state diff cannot know)");
|
|
894
896
|
}
|
|
895
|
-
},
|
|
897
|
+
}, It = async (e, t) => {
|
|
896
898
|
if (W() !== "postgres") return;
|
|
897
|
-
let n =
|
|
899
|
+
let n = A, r;
|
|
898
900
|
try {
|
|
899
|
-
r = await J(t,
|
|
901
|
+
r = await J(t, k.SqlClient.pipe(O.flatMap((t) => m(t, e, n))));
|
|
900
902
|
} catch {
|
|
901
903
|
return;
|
|
902
904
|
}
|
|
@@ -904,7 +906,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
904
906
|
if (a.length === 0) return;
|
|
905
907
|
B.info(i.missing.length + i.stale.length > 0 ? `db apply: installing change triggers on ${i.missing.length} table(s)` + (i.stale.length > 0 ? `, removing ${i.stale.length} stale one(s)` : "") + (i.functionOutdated ? ", replacing an outdated notify function" : "") : "db apply: replacing an outdated change-notify function (no table drift)");
|
|
906
908
|
let o = 0;
|
|
907
|
-
for (let e of a) await J(t,
|
|
909
|
+
for (let e of a) await J(t, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(e)))), o += 1;
|
|
908
910
|
B.info(`db apply: change triggers converged (${o} statement(s))`, {
|
|
909
911
|
installed: i.missing.length,
|
|
910
912
|
removed: i.stale.length,
|
|
@@ -916,81 +918,81 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
916
918
|
console.log(" (schema is up to date)\n");
|
|
917
919
|
return;
|
|
918
920
|
}
|
|
919
|
-
for (let t of e.operations) console.log(
|
|
920
|
-
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`),
|
|
921
|
-
},
|
|
922
|
-
let t =
|
|
921
|
+
for (let t of e.operations) console.log(Pt(t));
|
|
922
|
+
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`), Lt(e);
|
|
923
|
+
}, Lt = (e) => {
|
|
924
|
+
let t = ge(e.operations);
|
|
923
925
|
if (t.length !== 0) {
|
|
924
926
|
console.log(` ⚠ ${t.length} operation(s) UNSAFE under a rolling deploy`), console.log(" (old + new instances overlap → old code breaks against the new schema):");
|
|
925
927
|
for (let e of t) console.log(` • ${e.op.op.kind}: ${e.reason}`), console.log(` → ${e.remedy}`);
|
|
926
928
|
console.log(" Deploy with a maintenance window / scale-to-zero, or split into"), console.log(" expand → deploy → contract. See docs: deployment/production-hardening.\n");
|
|
927
929
|
}
|
|
928
|
-
},
|
|
929
|
-
let n =
|
|
930
|
+
}, Rt = (e, t) => {
|
|
931
|
+
let n = ae({
|
|
930
932
|
enabled: process.env.VOLTRO_ROLLING_DEPLOY === "1",
|
|
931
933
|
forced: t.includes("--force"),
|
|
932
|
-
unsafeCount:
|
|
934
|
+
unsafeCount: ge(e.operations).length
|
|
933
935
|
});
|
|
934
936
|
return n.refuse ? (B.error(`db apply: ${n.message}`), !0) : (n.warnForced && B.warn(`db apply: ${n.message}`), !1);
|
|
935
|
-
},
|
|
936
|
-
let t =
|
|
937
|
+
}, zt = (e) => {
|
|
938
|
+
let t = s(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
|
|
937
939
|
if (t === void 0) return;
|
|
938
940
|
let n = Number(t);
|
|
939
941
|
if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
|
|
940
942
|
return n;
|
|
941
943
|
}, Z = async (e) => {
|
|
942
|
-
let t = await
|
|
944
|
+
let t = await kt(e);
|
|
943
945
|
if (t.length === 0) return;
|
|
944
|
-
let n = await
|
|
945
|
-
return
|
|
946
|
+
let n = await g({ root: e });
|
|
947
|
+
return At(jt([
|
|
946
948
|
...t,
|
|
947
|
-
...
|
|
949
|
+
...Se,
|
|
948
950
|
...n
|
|
949
951
|
]));
|
|
950
952
|
}, Q = async (e, t, n) => {
|
|
951
953
|
let r;
|
|
952
954
|
try {
|
|
953
|
-
await J(n,
|
|
955
|
+
await J(n, v(await _(e), (e, t) => {
|
|
954
956
|
B.info(`db apply: ${e}`, t ?? {});
|
|
955
957
|
}));
|
|
956
958
|
} catch (e) {
|
|
957
959
|
r = e;
|
|
958
960
|
}
|
|
959
|
-
return await
|
|
960
|
-
},
|
|
961
|
-
let n = { close: async () => {} }, r = await V(e,
|
|
961
|
+
return await It(t, n), r === void 0 ? 0 : (B.error("db apply: a plugin migration FAILED — the schema was applied, the plugin steps were not", { error: r instanceof Error ? r.message : String(r) }), 1);
|
|
962
|
+
}, Bt = async (e, t) => {
|
|
963
|
+
let n = { close: async () => {} }, r = await V(e, b);
|
|
962
964
|
if (r.length === 0) return n;
|
|
963
|
-
let i = await
|
|
965
|
+
let i = await f(r, B), a = i.filter((e) => e.definition.lifecycle === "onSchemaChange");
|
|
964
966
|
if (a.length === 0) return n;
|
|
965
|
-
let { store: o, dispose: s } = await
|
|
967
|
+
let { store: o, dispose: s } = await Et(t), c = h(i, o, B.child("seeds"));
|
|
966
968
|
return B.info(`db apply: ${a.length} onSchemaChange seed(s) armed for this apply`, { ids: a.map((e) => e.definition.id) }), { close: async () => {
|
|
967
969
|
c(), await s().catch((e) => {
|
|
968
970
|
B.warn("db apply: seed store dispose failed", { error: String(e) });
|
|
969
971
|
});
|
|
970
972
|
} };
|
|
971
|
-
},
|
|
972
|
-
let t =
|
|
973
|
+
}, Vt = async (e) => {
|
|
974
|
+
let t = zt(e), n = H(e), r = await Z(n);
|
|
973
975
|
if (r === void 0) return B.warn("db plan: no schema files found", {
|
|
974
976
|
root: n,
|
|
975
977
|
hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
|
|
976
978
|
}), 1;
|
|
977
|
-
let i =
|
|
979
|
+
let i = s(e, "--against");
|
|
978
980
|
if (i) {
|
|
979
|
-
let n = i, a =
|
|
980
|
-
a && (
|
|
981
|
+
let n = i, a = s(e, "--token") ?? process.env.VOLTRO_INSPECT_TOKEN, o = { accept: "application/json" };
|
|
982
|
+
a && (o.authorization = `Bearer ${a}`);
|
|
981
983
|
try {
|
|
982
|
-
let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers:
|
|
983
|
-
if (!i.ok) return B.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
|
|
984
|
+
let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers: o });
|
|
985
|
+
if (!i.ok) return B.error(`db plan --against: remote returned ${i.status}`, { url: e }), ee(i.status) && console.error(` ${x(i.status)}`), 1;
|
|
984
986
|
let a = (await i.json()).drift?.liveSnapshot;
|
|
985
987
|
if (!a) return B.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
|
|
986
|
-
let
|
|
988
|
+
let s = M({
|
|
987
989
|
declared: r,
|
|
988
990
|
live: a,
|
|
989
991
|
dialect: W(),
|
|
990
992
|
...j(),
|
|
991
993
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
992
994
|
});
|
|
993
|
-
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), X(
|
|
995
|
+
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), X(s), s.summary.blocked > 0 ? 2 : 0;
|
|
994
996
|
} catch (e) {
|
|
995
997
|
return B.error(`db plan --against: ${e.message}`), 1;
|
|
996
998
|
}
|
|
@@ -1002,11 +1004,11 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1002
1004
|
...j(),
|
|
1003
1005
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
1004
1006
|
});
|
|
1005
|
-
return e.includes("--json") ? (
|
|
1006
|
-
}, $ =
|
|
1007
|
+
return e.includes("--json") ? (ne(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (X(a), a.summary.blocked > 0 ? 2 : 0);
|
|
1008
|
+
}, $ = n, Ht = async (e, t, n) => {
|
|
1007
1009
|
let r;
|
|
1008
1010
|
try {
|
|
1009
|
-
let t = await
|
|
1011
|
+
let t = await T.readFile(w(e), "utf8");
|
|
1010
1012
|
r = JSON.parse(t);
|
|
1011
1013
|
} catch (t) {
|
|
1012
1014
|
return B.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
|
|
@@ -1014,8 +1016,8 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1014
1016
|
if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return B.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
|
|
1015
1017
|
let i = await Z(n);
|
|
1016
1018
|
if (i === void 0) return B.warn("db apply --plan: no schema files found", { root: n }), 1;
|
|
1017
|
-
let a = await q(),
|
|
1018
|
-
if (
|
|
1019
|
+
let a = await q(), o = await J(a, k.SqlClient.pipe(O.flatMap((e) => fe(e, n)))).catch(() => []);
|
|
1020
|
+
if (o.length > 0) return B.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;
|
|
1019
1021
|
let c = M({
|
|
1020
1022
|
declared: i,
|
|
1021
1023
|
live: await Y(a),
|
|
@@ -1024,11 +1026,11 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1024
1026
|
});
|
|
1025
1027
|
if (c.fromFingerprint !== r.fromFingerprint) return B.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=${c.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
|
|
1026
1028
|
if (c.toFingerprint !== r.toFingerprint) return B.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${c.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
|
|
1027
|
-
let l =
|
|
1029
|
+
let l = Mt(c), u = l.plan;
|
|
1028
1030
|
if (u.summary.blocked > 0 && !l.acknowledged) return X(u), B.error(`db apply --plan: refusing — ${c.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (B.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), B.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
1029
|
-
if (l.acknowledged && B.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"),
|
|
1031
|
+
if (l.acknowledged && B.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), Rt(u, t)) return 2;
|
|
1030
1032
|
if (u.operations.length === 0) {
|
|
1031
|
-
let e = await J(a,
|
|
1033
|
+
let e = await J(a, k.SqlClient.pipe(O.flatMap((e) => pe(e, u, {
|
|
1032
1034
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
|
|
1033
1035
|
environment: $(),
|
|
1034
1036
|
source: "file"
|
|
@@ -1036,13 +1038,13 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1036
1038
|
return B.info(e === "recorded" ? `db apply --plan: schema is up to date — no DDL to apply, recorded fingerprint=${u.toFingerprint.slice(0, 16)} (change triggers are reconciled separately below)` : "db apply --plan: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), Q(n, i, a);
|
|
1037
1039
|
}
|
|
1038
1040
|
X(u);
|
|
1039
|
-
let d =
|
|
1041
|
+
let d = s(t, "--note"), f = await J(a, k.SqlClient.pipe(O.flatMap((e) => re(e, u, {
|
|
1040
1042
|
declared: i,
|
|
1041
1043
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
|
|
1042
1044
|
environment: $(),
|
|
1043
1045
|
source: "file",
|
|
1044
1046
|
...d ? { notes: d } : {},
|
|
1045
|
-
replan: () =>
|
|
1047
|
+
replan: () => O.promise(() => Y(a)).pipe(O.map((e) => M({
|
|
1046
1048
|
declared: i,
|
|
1047
1049
|
live: e,
|
|
1048
1050
|
dialect: W(),
|
|
@@ -1053,48 +1055,48 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1053
1055
|
id: f.id,
|
|
1054
1056
|
fingerprint: f.fingerprint.slice(0, 16)
|
|
1055
1057
|
}), Q(n, i, a);
|
|
1056
|
-
},
|
|
1057
|
-
let t = H(e), n = await
|
|
1058
|
+
}, Ut = async (e) => {
|
|
1059
|
+
let t = H(e), n = await Bt(t, e);
|
|
1058
1060
|
try {
|
|
1059
|
-
return await
|
|
1061
|
+
return await Wt(e, t);
|
|
1060
1062
|
} finally {
|
|
1061
1063
|
await n.close();
|
|
1062
1064
|
}
|
|
1063
|
-
},
|
|
1064
|
-
let r =
|
|
1065
|
-
if (i) return
|
|
1066
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error(`voltro db apply: refusing — auto-apply on prod is not allowed.${
|
|
1065
|
+
}, Wt = async (e, n) => {
|
|
1066
|
+
let r = zt(e), i = s(e, "--plan");
|
|
1067
|
+
if (i) return Ht(i, e, n);
|
|
1068
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error(`voltro db apply: refusing — auto-apply on prod is not allowed.${t()}`), 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;
|
|
1067
1069
|
let a = await Z(n);
|
|
1068
1070
|
if (a === void 0) return B.warn("db apply: no schema files found", { root: n }), 1;
|
|
1069
|
-
let
|
|
1071
|
+
let o = await q(), c = await sn(n, o);
|
|
1070
1072
|
if ("failed" in c) return B.error("db apply: a file-based migration FAILED — the schema diff was NOT run"), console.error(c.failed), console.error("Fix the migration and re-run. `voltro db files` runs them alone."), 1;
|
|
1071
1073
|
let l = M({
|
|
1072
1074
|
declared: a,
|
|
1073
|
-
live: await Y(
|
|
1075
|
+
live: await Y(o),
|
|
1074
1076
|
dialect: W(),
|
|
1075
1077
|
...j(),
|
|
1076
1078
|
...r === void 0 ? {} : { onlineAfter: r }
|
|
1077
1079
|
});
|
|
1078
|
-
X(l), await
|
|
1079
|
-
let u =
|
|
1080
|
+
X(l), await Ft(l, o);
|
|
1081
|
+
let u = Mt(l), d = u.plan;
|
|
1080
1082
|
if (d.summary.blocked > 0 && !u.acknowledged) return B.error(`db apply: refusing — ${d.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (B.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), B.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
1081
|
-
if (u.acknowledged && B.warn(`db apply: proceeding with ${l.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`),
|
|
1083
|
+
if (u.acknowledged && B.warn(`db apply: proceeding with ${l.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), Rt(d, e)) return 2;
|
|
1082
1084
|
if (d.operations.length === 0) {
|
|
1083
1085
|
B.info("db apply: schema is up to date — no DDL to apply (change triggers are reconciled separately below)");
|
|
1084
|
-
let
|
|
1086
|
+
let t = await J(o, k.SqlClient.pipe(O.flatMap((e) => pe(e, d, {
|
|
1085
1087
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:apply",
|
|
1086
1088
|
environment: $(),
|
|
1087
1089
|
source: "auto-diff"
|
|
1088
1090
|
}))));
|
|
1089
|
-
return
|
|
1091
|
+
return t === "recorded" && B.info(`db apply: recorded fingerprint=${d.toFingerprint.slice(0, 16)} (0 operations)`), await tn(o), s(e, "--note") !== void 0 && t !== "recorded" && (B.warn("db apply: --note was ignored — this schema is already the latest recorded one, so there is no new history row to attach it to."), console.error("To record that the CURRENT live schema is the accepted baseline, use `voltro db drift --accept`.")), Q(n, a, o);
|
|
1090
1092
|
}
|
|
1091
|
-
let f =
|
|
1093
|
+
let f = s(e, "--note"), p = await J(o, k.SqlClient.pipe(O.flatMap((e) => re(e, d, {
|
|
1092
1094
|
declared: a,
|
|
1093
1095
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
|
|
1094
1096
|
environment: $(),
|
|
1095
1097
|
source: "auto-diff",
|
|
1096
1098
|
...f ? { notes: f } : {},
|
|
1097
|
-
replan: () =>
|
|
1099
|
+
replan: () => O.promise(() => Y(o)).pipe(O.map((e) => M({
|
|
1098
1100
|
declared: a,
|
|
1099
1101
|
live: e,
|
|
1100
1102
|
dialect: W(),
|
|
@@ -1105,8 +1107,8 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1105
1107
|
return B.info(`db apply: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
|
|
1106
1108
|
id: p.id,
|
|
1107
1109
|
fingerprint: p.fingerprint.slice(0, 16)
|
|
1108
|
-
}), Q(n, a,
|
|
1109
|
-
},
|
|
1110
|
+
}), Q(n, a, o);
|
|
1111
|
+
}, Gt = (e) => {
|
|
1110
1112
|
let t = [];
|
|
1111
1113
|
for (let n of e.tables) for (let e of n.columns) {
|
|
1112
1114
|
let r = e.references;
|
|
@@ -1119,7 +1121,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1119
1121
|
});
|
|
1120
1122
|
}
|
|
1121
1123
|
return t;
|
|
1122
|
-
},
|
|
1124
|
+
}, Kt = (e) => {
|
|
1123
1125
|
let t = [];
|
|
1124
1126
|
for (let n of e.tables) for (let e of n.indexes) e.expression !== !0 && e.name !== `${n.name}_pkey` && e.columns.length !== 0 && t.push({
|
|
1125
1127
|
table: n.name,
|
|
@@ -1128,7 +1130,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1128
1130
|
name: e.name
|
|
1129
1131
|
});
|
|
1130
1132
|
return t;
|
|
1131
|
-
},
|
|
1133
|
+
}, qt = (e, t) => {
|
|
1132
1134
|
let n = (e) => JSON.stringify(e.op), r = new Set(e.operations.map(n)), i = new Set(t.operations.map(n)), a = [...t.operations.filter((e) => !r.has(n(e))), ...e.operations.filter((e) => !i.has(n(e)))];
|
|
1133
1135
|
return {
|
|
1134
1136
|
...t,
|
|
@@ -1138,88 +1140,88 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1138
1140
|
blocked: 0
|
|
1139
1141
|
}
|
|
1140
1142
|
};
|
|
1141
|
-
},
|
|
1143
|
+
}, Jt = (e) => {
|
|
1142
1144
|
if (e === void 0) return "empty";
|
|
1143
1145
|
if (e === "empty" || e === "copy") return e;
|
|
1144
|
-
throw e === "fresh" ? new
|
|
1145
|
-
},
|
|
1146
|
+
throw e === "fresh" ? new r("voltro db branch: --seed fresh is not implemented — the branch lifecycle has no executor step that runs boot seeds, so the branch would come up EMPTY while this reported it as seeded. Use --seed copy (the parent's rows) or --seed empty (schema only).") : new r(`voltro db branch: --seed must be one of empty|copy, got "${e}"`);
|
|
1147
|
+
}, Yt = (e) => {
|
|
1146
1148
|
if (e !== void 0) {
|
|
1147
1149
|
if (e === "namespace" || e === "neon-cow") return e;
|
|
1148
|
-
throw new
|
|
1150
|
+
throw new r(`voltro db branch: --prefer must be one of namespace|neon-cow, got "${e}"`);
|
|
1149
1151
|
}
|
|
1150
|
-
},
|
|
1151
|
-
let t = H(e),
|
|
1152
|
+
}, Xt = async (e) => {
|
|
1153
|
+
let t = H(e), n = Jt(s(e, "--seed")), i = Yt(s(e, "--prefer")), a = e.includes("--keep"), o = e.includes("--json"), c = zt(e), u = W();
|
|
1152
1154
|
if (u !== "postgres") return B.error(`voltro db branch: not supported on '${u}'.`), console.error(" The branch PLAN is dialect-agnostic; the shipped namespace EXECUTOR is Postgres-only"), console.error(" (CREATE SCHEMA / CREATE TABLE … LIKE … INCLUDING ALL / \"-quoted identifiers)."), console.error(" MySQL, MariaDB, SQLite and SQL Server need their own BranchExecutor — see"), console.error(" makeNamespaceBranchExecutor in @voltro/database."), 1;
|
|
1153
1155
|
let d = await Z(t);
|
|
1154
1156
|
if (d === void 0) return B.warn("db branch: no schema files found", {
|
|
1155
1157
|
root: t,
|
|
1156
1158
|
hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
|
|
1157
1159
|
}), 1;
|
|
1158
|
-
let f = U(), p =
|
|
1159
|
-
seed:
|
|
1160
|
+
let f = U(), p = Oe({
|
|
1161
|
+
seed: n,
|
|
1160
1162
|
...f.url === void 0 ? {} : { dbUrl: f.url },
|
|
1161
1163
|
...i === void 0 ? {} : { prefer: i }
|
|
1162
1164
|
});
|
|
1163
|
-
if (p === "neon-cow") return B.error("voltro db branch: this connection resolves to the Neon copy-on-write mechanism, which the CLI cannot execute."), console.error(" A Neon CoW branch is a single call to Neon's branch REST API and needs a Neon API token;"), console.error(" the framework ships the PLAN, and the cloud control plane executes it."), console.error(" Re-run with `--prefer namespace` to rehearse on a schema-per-branch copy instead"), console.error(` (portable, no vendor API — it copies ${
|
|
1164
|
-
let m = f.schema ?? "public", h =
|
|
1165
|
-
if (!Number.isInteger(_) || _ < 0) throw new
|
|
1166
|
-
let v =
|
|
1165
|
+
if (p === "neon-cow") return B.error("voltro db branch: this connection resolves to the Neon copy-on-write mechanism, which the CLI cannot execute."), console.error(" A Neon CoW branch is a single call to Neon's branch REST API and needs a Neon API token;"), console.error(" the framework ships the PLAN, and the cloud control plane executes it."), console.error(" Re-run with `--prefer namespace` to rehearse on a schema-per-branch copy instead"), console.error(` (portable, no vendor API — it copies ${n === "copy" ? "every row" : "the schema only"}).`), 1;
|
|
1166
|
+
let m = f.schema ?? "public", h = s(e, "--name") ?? te(t), g = s(e, "--pr"), _ = g === void 0 ? 0 : Number(g);
|
|
1167
|
+
if (!Number.isInteger(_) || _ < 0) throw new r(`voltro db branch: --pr must be a non-negative integer, got "${String(g)}"`);
|
|
1168
|
+
let v = xe(h, _), y = await l(u), b = y.makeSqlLayer(f), ee = y.makeSqlLayer({
|
|
1167
1169
|
...f,
|
|
1168
1170
|
schema: v
|
|
1169
|
-
}),
|
|
1171
|
+
}), x = await Y(b), S = M({
|
|
1170
1172
|
declared: d,
|
|
1171
|
-
live:
|
|
1173
|
+
live: x,
|
|
1172
1174
|
dialect: u,
|
|
1173
1175
|
...j(),
|
|
1174
|
-
...
|
|
1175
|
-
}),
|
|
1176
|
+
...c === void 0 ? {} : { onlineAfter: c }
|
|
1177
|
+
}), C = x.tables.map((e) => e.name), w = Gt(x), T = Kt(x), E = we({ run: (e) => J(b, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(e)), O.asVoid)) }), D = async () => M({
|
|
1176
1178
|
declared: d,
|
|
1177
|
-
live: await Y(
|
|
1179
|
+
live: await Y(ee),
|
|
1178
1180
|
dialect: u,
|
|
1179
1181
|
...j(),
|
|
1180
|
-
...
|
|
1181
|
-
}), A = await
|
|
1182
|
+
...c === void 0 ? {} : { onlineAfter: c }
|
|
1183
|
+
}), A = await me({
|
|
1182
1184
|
branchId: v,
|
|
1183
1185
|
mechanism: p,
|
|
1184
|
-
branchedTables:
|
|
1185
|
-
replayedForeignKeys:
|
|
1186
|
+
branchedTables: C.length,
|
|
1187
|
+
replayedForeignKeys: w.length,
|
|
1186
1188
|
keep: a,
|
|
1187
1189
|
log: (e) => {
|
|
1188
|
-
|
|
1190
|
+
o || B.info(`db branch: ${e}`);
|
|
1189
1191
|
},
|
|
1190
1192
|
provision: async () => {
|
|
1191
|
-
await
|
|
1193
|
+
await Te({
|
|
1192
1194
|
appSlug: h,
|
|
1193
1195
|
prNumber: _,
|
|
1194
|
-
seed:
|
|
1195
|
-
tableNames:
|
|
1196
|
+
seed: n,
|
|
1197
|
+
tableNames: C,
|
|
1196
1198
|
parentNamespace: m,
|
|
1197
|
-
foreignKeys:
|
|
1198
|
-
indexNames:
|
|
1199
|
+
foreignKeys: w,
|
|
1200
|
+
indexNames: T,
|
|
1199
1201
|
prefer: "namespace",
|
|
1200
1202
|
...f.url === void 0 ? {} : { dbUrl: f.url }
|
|
1201
|
-
},
|
|
1203
|
+
}, E);
|
|
1202
1204
|
},
|
|
1203
|
-
fidelityPlan: async () =>
|
|
1204
|
-
plan:
|
|
1205
|
+
fidelityPlan: async () => qt(S, await D()),
|
|
1206
|
+
plan: D,
|
|
1205
1207
|
apply: async (e) => {
|
|
1206
|
-
await J(
|
|
1208
|
+
await J(ee, k.SqlClient.pipe(O.flatMap((t) => re(t, e, {
|
|
1207
1209
|
declared: d,
|
|
1208
1210
|
appliedBy: "cli:db-branch",
|
|
1209
1211
|
environment: $(),
|
|
1210
1212
|
source: "auto-diff",
|
|
1211
1213
|
notes: `migration rehearsal on branch ${v}`,
|
|
1212
|
-
replan: () =>
|
|
1214
|
+
replan: () => O.promise(D)
|
|
1213
1215
|
}))));
|
|
1214
1216
|
},
|
|
1215
|
-
replan: async () => await
|
|
1217
|
+
replan: async () => await D(),
|
|
1216
1218
|
teardown: async () => {
|
|
1217
|
-
await
|
|
1219
|
+
await ke(v, "namespace", E);
|
|
1218
1220
|
}
|
|
1219
1221
|
});
|
|
1220
|
-
return
|
|
1221
|
-
},
|
|
1222
|
-
let t = Math.max(1, Number(
|
|
1222
|
+
return o ? ne(1, `${JSON.stringify(A, null, 2)}\n`) : console.log(`\n${ue(A)}\n`), oe(A);
|
|
1223
|
+
}, Zt = async (e) => {
|
|
1224
|
+
let t = Math.max(1, Number(s(e, "--limit") ?? "20")), n = await J(await q(), k.SqlClient.pipe(O.flatMap((e) => e`
|
|
1223
1225
|
SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
1224
1226
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
1225
1227
|
${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}
|
|
@@ -1232,20 +1234,20 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1232
1234
|
for (let e of n) {
|
|
1233
1235
|
let t = 0;
|
|
1234
1236
|
try {
|
|
1235
|
-
t = JSON.parse(
|
|
1237
|
+
t = JSON.parse(nn(e.operations)).length;
|
|
1236
1238
|
} catch {}
|
|
1237
|
-
console.log(` ${e.id} fp=${N(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${
|
|
1239
|
+
console.log(` ${e.id} fp=${N(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${rn(e.appliedAt)} by=${e.appliedBy}`), e.notes && console.log(` [2mnote: ${e.notes}[0m`);
|
|
1238
1240
|
}
|
|
1239
1241
|
return console.log(), 0;
|
|
1240
|
-
},
|
|
1242
|
+
}, Qt = async (e) => {
|
|
1241
1243
|
let t = e[0];
|
|
1242
1244
|
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;
|
|
1243
|
-
let n = await q(), r = await J(n,
|
|
1245
|
+
let n = await q(), r = await J(n, k.SqlClient.pipe(O.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
|
|
1244
1246
|
FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
|
|
1245
1247
|
if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
|
|
1246
1248
|
let i;
|
|
1247
1249
|
try {
|
|
1248
|
-
i = JSON.parse(
|
|
1250
|
+
i = JSON.parse(nn(r[0].operations));
|
|
1249
1251
|
} catch {
|
|
1250
1252
|
return console.error(`restore-snapshot: plan '${t}' has corrupted operations JSON`), 1;
|
|
1251
1253
|
}
|
|
@@ -1259,7 +1261,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1259
1261
|
if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
|
|
1260
1262
|
let s = 0;
|
|
1261
1263
|
for (let e of a) {
|
|
1262
|
-
let t = await J(n,
|
|
1264
|
+
let t = await J(n, k.SqlClient.pipe(O.flatMap((t) => t`
|
|
1263
1265
|
SELECT column_name FROM information_schema.columns
|
|
1264
1266
|
WHERE ${t.unsafe(K(W()))}
|
|
1265
1267
|
AND table_name = ${e.table}
|
|
@@ -1271,11 +1273,11 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1271
1273
|
continue;
|
|
1272
1274
|
}
|
|
1273
1275
|
let r = t[0].column_name;
|
|
1274
|
-
await J(n,
|
|
1276
|
+
await J(n, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table)} RENAME COLUMN ${G(r)} TO ${G(e.column)}`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
|
|
1275
1277
|
}
|
|
1276
1278
|
let c = 0;
|
|
1277
1279
|
for (let e of o) {
|
|
1278
|
-
let t = await J(n,
|
|
1280
|
+
let t = await J(n, k.SqlClient.pipe(O.flatMap((t) => t`
|
|
1279
1281
|
SELECT table_name FROM information_schema.tables
|
|
1280
1282
|
WHERE ${t.unsafe(K(W()))}
|
|
1281
1283
|
AND table_name LIKE ${e.table + "__dropped_%"}
|
|
@@ -1286,45 +1288,45 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1286
1288
|
continue;
|
|
1287
1289
|
}
|
|
1288
1290
|
let r = t[0].table_name;
|
|
1289
|
-
await J(n,
|
|
1291
|
+
await J(n, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(`ALTER TABLE ${G(r)} RENAME TO ${G(e.table)}`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
|
|
1290
1292
|
}
|
|
1291
1293
|
return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
|
|
1292
|
-
},
|
|
1293
|
-
let t =
|
|
1294
|
+
}, $t = async (e) => {
|
|
1295
|
+
let t = s(e, "--before");
|
|
1294
1296
|
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;
|
|
1295
1297
|
let n = t.replace(/[^0-9]/g, "");
|
|
1296
1298
|
if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
|
|
1297
|
-
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await q(),
|
|
1299
|
+
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await q(), o = await J(a, k.SqlClient.pipe(O.flatMap((e) => e`
|
|
1298
1300
|
SELECT table_name, column_name FROM information_schema.columns
|
|
1299
1301
|
WHERE ${e.unsafe(K(W()))} AND column_name LIKE ${"%__dropped_%"}
|
|
1300
|
-
`))), c = await J(a,
|
|
1302
|
+
`))), c = await J(a, k.SqlClient.pipe(O.flatMap((e) => e`
|
|
1301
1303
|
SELECT table_name FROM information_schema.tables
|
|
1302
1304
|
WHERE ${e.unsafe(K(W()))} AND table_name LIKE ${"%__dropped_%"}
|
|
1303
1305
|
`))), l = (e) => {
|
|
1304
1306
|
let t = /__dropped_(\d{14})$/.exec(e);
|
|
1305
1307
|
return t ? t[1] : null;
|
|
1306
1308
|
}, u = 0;
|
|
1307
|
-
for (let e of
|
|
1309
|
+
for (let e of o) {
|
|
1308
1310
|
let t = l(e.column_name);
|
|
1309
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a,
|
|
1311
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table_name)} DROP COLUMN ${G(e.column_name)}`)))), u++);
|
|
1310
1312
|
}
|
|
1311
1313
|
for (let e of c) {
|
|
1312
1314
|
let t = l(e.table_name);
|
|
1313
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await J(a,
|
|
1315
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await J(a, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(`DROP TABLE ${G(e.table_name)}`)))), u++);
|
|
1314
1316
|
}
|
|
1315
1317
|
return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
|
|
1316
|
-
},
|
|
1317
|
-
let t =
|
|
1318
|
+
}, en = async (e) => {
|
|
1319
|
+
let t = s(e, "--before");
|
|
1318
1320
|
if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
|
|
1319
1321
|
let n = t;
|
|
1320
1322
|
if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
|
|
1321
|
-
let r =
|
|
1323
|
+
let r = s(e, "--note"), i = H(e), a = await Z(i);
|
|
1322
1324
|
if (a === void 0) return B.warn("db squash: no schema files found", { root: i }), 1;
|
|
1323
|
-
let
|
|
1325
|
+
let o = le(se(a)), c = await q();
|
|
1324
1326
|
try {
|
|
1325
|
-
let e = await J(c,
|
|
1327
|
+
let e = await J(c, ve({
|
|
1326
1328
|
before: n,
|
|
1327
|
-
fingerprint:
|
|
1329
|
+
fingerprint: o,
|
|
1328
1330
|
appliedBy: process.env.USER ?? "cli",
|
|
1329
1331
|
environment: $(),
|
|
1330
1332
|
...r === void 0 ? {} : { note: r }
|
|
@@ -1333,10 +1335,10 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1333
1335
|
} catch (e) {
|
|
1334
1336
|
return console.error(`squash refused: ${e.message}`), 1;
|
|
1335
1337
|
}
|
|
1336
|
-
},
|
|
1338
|
+
}, tn = async (e) => {
|
|
1337
1339
|
try {
|
|
1338
|
-
let t =
|
|
1339
|
-
await J(e,
|
|
1340
|
+
let t = le(await Y(e));
|
|
1341
|
+
await J(e, k.SqlClient.pipe(O.flatMap((e) => e`
|
|
1340
1342
|
UPDATE ${e("_voltro_migration_plans")}
|
|
1341
1343
|
SET ${e("liveFingerprint")} = ${t}
|
|
1342
1344
|
WHERE ${e("id")} = (
|
|
@@ -1348,7 +1350,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1348
1350
|
AND ${e("liveFingerprint")} IS NULL
|
|
1349
1351
|
`)));
|
|
1350
1352
|
} catch {}
|
|
1351
|
-
},
|
|
1353
|
+
}, nn = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), rn = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), an = (e) => e === void 0 ? {
|
|
1352
1354
|
accept: !1,
|
|
1353
1355
|
reason: "no-declared-schema"
|
|
1354
1356
|
} : e > 0 ? {
|
|
@@ -1357,15 +1359,15 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1357
1359
|
} : {
|
|
1358
1360
|
accept: !0,
|
|
1359
1361
|
reason: "converged"
|
|
1360
|
-
},
|
|
1361
|
-
let t = e.includes("--accept"), n = await q(), r = await J(n,
|
|
1362
|
+
}, on = async (e) => {
|
|
1363
|
+
let t = e.includes("--accept"), n = await q(), r = await J(n, k.SqlClient.pipe(O.flatMap((e) => e`
|
|
1362
1364
|
SELECT * FROM ${e("_voltro_migration_plans")}
|
|
1363
1365
|
ORDER BY ${e("appliedAt")} DESC
|
|
1364
1366
|
LIMIT 1
|
|
1365
|
-
`))), i = await Y(n), a =
|
|
1367
|
+
`))), i = await Y(n), a = le(i);
|
|
1366
1368
|
if (r.length === 0) return B.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${N(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
|
|
1367
1369
|
let o = r[0];
|
|
1368
|
-
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return B.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${
|
|
1370
|
+
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return B.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${rn(o.appliedAt)}`), console.log(` live now: ${N(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;
|
|
1369
1371
|
if (o.liveFingerprint === a) return B.info(`db drift: live matches last applied (${N(a)})`), 0;
|
|
1370
1372
|
let s = await (async () => {
|
|
1371
1373
|
try {
|
|
@@ -1381,14 +1383,14 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1381
1383
|
}
|
|
1382
1384
|
})();
|
|
1383
1385
|
if (t) {
|
|
1384
|
-
let e =
|
|
1386
|
+
let e = an(s?.length);
|
|
1385
1387
|
if (e.reason === "no-declared-schema") return B.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
|
|
1386
1388
|
if (!e.accept && s !== void 0) {
|
|
1387
1389
|
B.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
|
|
1388
1390
|
for (let e of s.slice(0, 10)) console.log(` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}`);
|
|
1389
1391
|
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;
|
|
1390
1392
|
}
|
|
1391
|
-
return await J(n,
|
|
1393
|
+
return await J(n, k.SqlClient.pipe(O.flatMap((e) => e`
|
|
1392
1394
|
UPDATE ${e("_voltro_migration_plans")}
|
|
1393
1395
|
SET ${e("liveFingerprint")} = ${a}
|
|
1394
1396
|
WHERE ${e("id")} = (
|
|
@@ -1399,11 +1401,11 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1399
1401
|
)
|
|
1400
1402
|
`))), B.info(`db drift: accepted the live schema as the baseline (${N(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;
|
|
1401
1403
|
}
|
|
1402
|
-
return B.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${N(o.liveFingerprint)} at ${
|
|
1403
|
-
},
|
|
1404
|
+
return B.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${N(o.liveFingerprint)} at ${rn(o.appliedAt)} (${o.id})`), console.log(` live now: ${N(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);
|
|
1405
|
+
}, sn = async (e, t) => {
|
|
1404
1406
|
let n = $();
|
|
1405
1407
|
try {
|
|
1406
|
-
let r = await J(t,
|
|
1408
|
+
let r = await J(t, k.SqlClient.pipe(O.flatMap((t) => _e(t, {
|
|
1407
1409
|
projectRoot: e,
|
|
1408
1410
|
env: n,
|
|
1409
1411
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
@@ -1412,10 +1414,10 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1412
1414
|
} catch (e) {
|
|
1413
1415
|
return { failed: e };
|
|
1414
1416
|
}
|
|
1415
|
-
},
|
|
1417
|
+
}, cn = async (e) => {
|
|
1416
1418
|
let t = H(e), n = await q(), r = $();
|
|
1417
|
-
await J(n,
|
|
1418
|
-
let i = await J(n,
|
|
1419
|
+
await J(n, ie(Se, W(), void 0, { retryFilter: (await l(W())).retryFilter }));
|
|
1420
|
+
let i = await J(n, k.SqlClient.pipe(O.flatMap((e) => _e(e, {
|
|
1419
1421
|
projectRoot: t,
|
|
1420
1422
|
env: r,
|
|
1421
1423
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
@@ -1424,16 +1426,16 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1424
1426
|
applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
|
|
1425
1427
|
skipped: i.skipped.length
|
|
1426
1428
|
}), 0);
|
|
1427
|
-
},
|
|
1428
|
-
let n = H(
|
|
1429
|
+
}, ln = async (e) => {
|
|
1430
|
+
let n = H(e), r = c(e, bt);
|
|
1429
1431
|
if (!r) return B.error("voltro db rollback-file <migration-id>"), 2;
|
|
1430
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step." +
|
|
1431
|
-
let i = await J(await q(),
|
|
1432
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step." + t()), 3;
|
|
1433
|
+
let i = await J(await q(), k.SqlClient.pipe(O.flatMap((e) => he(e, {
|
|
1432
1434
|
projectRoot: n,
|
|
1433
1435
|
id: r
|
|
1434
1436
|
}))));
|
|
1435
1437
|
return B.info(`db rollback-file: rolled back ${i.id} in ${i.durationMs}ms`), 0;
|
|
1436
|
-
},
|
|
1438
|
+
}, un = () => {
|
|
1437
1439
|
let e = /* @__PURE__ */ new Date();
|
|
1438
1440
|
return [
|
|
1439
1441
|
e.getUTCFullYear(),
|
|
@@ -1443,7 +1445,7 @@ var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
|
1443
1445
|
String(e.getUTCMinutes()).padStart(2, "0"),
|
|
1444
1446
|
String(e.getUTCSeconds()).padStart(2, "0")
|
|
1445
1447
|
].join("");
|
|
1446
|
-
},
|
|
1448
|
+
}, dn = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", fn = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
|
|
1447
1449
|
import { Effect } from 'effect'
|
|
1448
1450
|
|
|
1449
1451
|
// A hand-written migration. Reach for this when the DECLARATIVE differ can't
|
|
@@ -1475,100 +1477,100 @@ export default defineMigration({
|
|
|
1475
1477
|
}),
|
|
1476
1478
|
],
|
|
1477
1479
|
})
|
|
1478
|
-
`,
|
|
1479
|
-
plan:
|
|
1480
|
-
apply:
|
|
1481
|
-
branch:
|
|
1482
|
-
plans:
|
|
1483
|
-
drift:
|
|
1484
|
-
files:
|
|
1485
|
-
"rollback-file":
|
|
1486
|
-
migrate:
|
|
1487
|
-
rollback:
|
|
1488
|
-
status:
|
|
1489
|
-
seed:
|
|
1480
|
+
`, pn = {
|
|
1481
|
+
plan: Vt,
|
|
1482
|
+
apply: Ut,
|
|
1483
|
+
branch: Xt,
|
|
1484
|
+
plans: Zt,
|
|
1485
|
+
drift: on,
|
|
1486
|
+
files: cn,
|
|
1487
|
+
"rollback-file": ln,
|
|
1488
|
+
migrate: Ct,
|
|
1489
|
+
rollback: wt,
|
|
1490
|
+
status: Tt,
|
|
1491
|
+
seed: Dt,
|
|
1490
1492
|
generate: async (e) => {
|
|
1491
|
-
let { value: t, rest: n } =
|
|
1492
|
-
return await
|
|
1493
|
+
let { value: t, rest: n } = a(e, "--root"), { value: r, rest: i } = a(n, "--name"), o = t ? w(t) : process.cwd(), s = dn(r ?? c(i, []) ?? "migration"), l = `${un()}_${s}`, u = S(o, "migrations"), d = S(u, `${l}.migration.ts`);
|
|
1494
|
+
return await T.mkdir(u, { recursive: !0 }), await T.writeFile(d, fn(l, s), "utf8"), B.info(`created ${C(o, 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;
|
|
1493
1495
|
},
|
|
1494
|
-
squash:
|
|
1495
|
-
"restore-snapshot":
|
|
1496
|
-
"gc-snapshots":
|
|
1496
|
+
squash: en,
|
|
1497
|
+
"restore-snapshot": Qt,
|
|
1498
|
+
"gc-snapshots": $t,
|
|
1497
1499
|
adopt: async (e) => {
|
|
1498
1500
|
let t = (t) => {
|
|
1499
1501
|
let n = e.indexOf(`--${t}`);
|
|
1500
1502
|
return n === -1 ? void 0 : e[n + 1];
|
|
1501
|
-
}, n = t("from"),
|
|
1502
|
-
if (n === void 0 ||
|
|
1503
|
-
let o = (await
|
|
1503
|
+
}, n = t("from"), r = t("into"), a = t("map");
|
|
1504
|
+
if (n === void 0 || r === void 0 || a === void 0) return console.error("voltro db adopt --from <table> --into <table> --map <file.ts> [--apply] [--keep-source]"), console.error(" Dry run unless --apply. The map file default-exports { map, leaveUnset? }."), 1;
|
|
1505
|
+
let o = (await i(w(process.cwd(), a))).default;
|
|
1504
1506
|
if (o?.map === void 0) return console.error(`voltro db adopt: ${a} must default-export { map: { <targetColumn>: <source> } }`), 1;
|
|
1505
1507
|
let s = {
|
|
1506
1508
|
from: n,
|
|
1507
|
-
into:
|
|
1509
|
+
into: r,
|
|
1508
1510
|
map: o.map,
|
|
1509
1511
|
...o.leaveUnset ? { leaveUnset: o.leaveUnset } : {}
|
|
1510
|
-
}, c = W(), l = await q(), u = await J(l,
|
|
1512
|
+
}, c = W(), l = await q(), u = await J(l, k.SqlClient.pipe(O.flatMap(de))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
|
|
1511
1513
|
name: e.name,
|
|
1512
1514
|
nullable: e.nullable,
|
|
1513
1515
|
hasDefault: e.defaultValue !== void 0 && e.defaultValue !== null
|
|
1514
|
-
})), f = d(n), p = d(
|
|
1516
|
+
})), f = d(n), p = d(r);
|
|
1515
1517
|
if (f === void 0) return console.error(`voltro db adopt: '${n}' does not exist in the database`), 1;
|
|
1516
|
-
if (p === void 0) return console.error(`voltro db adopt: '${
|
|
1517
|
-
let m = async (e) => await J(l,
|
|
1518
|
-
for (let e of
|
|
1518
|
+
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;
|
|
1519
|
+
let m = async (e) => await J(l, k.SqlClient.pipe(O.flatMap((t) => t.unsafe(e)))), h = async (e) => Number(Object.values((await m(`SELECT COUNT(*) AS n FROM "${e}"`))[0] ?? { n: 0 })[0] ?? 0), g = Pe(s, f, p, await h(r));
|
|
1520
|
+
for (let e of Ie(s, g, await h(n))) console.log(e);
|
|
1519
1521
|
if (g.fatal) return 1;
|
|
1520
1522
|
if (!e.includes("--apply")) return console.log(""), console.log(" (dry run — nothing written. Re-run with --apply.)"), 0;
|
|
1521
|
-
let _ = await
|
|
1523
|
+
let _ = await Ve(s, g, c, "id", void 0, m, { keepSource: e.includes("--keep-source") });
|
|
1522
1524
|
return console.log(""), console.log(` copied ${_.copied}/${_.sourceRows} row(s)`), console.log(` snapshot: ${_.snapshotTable} — restore from here, then drop it yourself`), _.verified ? (console.log(_.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);
|
|
1523
1525
|
},
|
|
1524
1526
|
"scan-credentials": async (e) => {
|
|
1525
|
-
let t = W(),
|
|
1527
|
+
let t = W(), n = [];
|
|
1526
1528
|
for (let t = 0; t < e.length; t += 1) {
|
|
1527
1529
|
if (e[t] !== "--table") continue;
|
|
1528
1530
|
let i = e[t + 1];
|
|
1529
|
-
if (i === void 0) throw new
|
|
1531
|
+
if (i === void 0) throw new r("voltro db scan-credentials: --table needs a value");
|
|
1530
1532
|
let [a, o = "subject"] = i.split(":");
|
|
1531
|
-
|
|
1533
|
+
n.push({
|
|
1532
1534
|
table: a,
|
|
1533
1535
|
column: o
|
|
1534
1536
|
});
|
|
1535
1537
|
}
|
|
1536
|
-
let i = [...
|
|
1537
|
-
return console.log(
|
|
1538
|
+
let i = [...He, ...n], a = await J(await q(), k.SqlClient.pipe(O.flatMap((e) => O.promise(() => $e(e, t, i, G)))));
|
|
1539
|
+
return console.log(et(a, t)), tt(a);
|
|
1538
1540
|
},
|
|
1539
1541
|
"encrypt-column": async (e) => {
|
|
1540
|
-
let t = W(),
|
|
1542
|
+
let t = W(), n = e.includes("--dry-run"), i = e.includes("--yes"), a = (t) => {
|
|
1541
1543
|
let n = e.indexOf(t);
|
|
1542
1544
|
return n >= 0 ? e[n + 1] : void 0;
|
|
1543
|
-
}, s = Math.max(1, Number(
|
|
1544
|
-
if (!l) throw new
|
|
1545
|
-
let u =
|
|
1546
|
-
if (d.length === 0) throw new
|
|
1545
|
+
}, s = Math.max(1, Number(a("--batch") ?? 500)), c = a("--key-env") ?? "FIELD_ENCRYPTION_KEY", l = process.env[c];
|
|
1546
|
+
if (!l) throw new r(`voltro db encrypt-column: ${c} is not set. It must be the SAME key your app runs with (\`governancePlugin({ fieldEncryption: { key } })\`) — encrypting with a different one produces rows the app cannot read. Use --key-env to name a different variable.`);
|
|
1547
|
+
let u = je(l), d = o(e, bt).filter((e) => e.includes(".")).map(lt);
|
|
1548
|
+
if (d.length === 0) throw new r("voltro db encrypt-column: name at least one <table>.<column>");
|
|
1547
1549
|
let f = await q(), p = [];
|
|
1548
1550
|
for (let e of d) {
|
|
1549
|
-
let a = G(e.table), o = G(e.column), l = await J(f,
|
|
1550
|
-
let i = await
|
|
1551
|
-
if (i.length === 0) throw new
|
|
1551
|
+
let a = G(e.table), o = G(e.column), l = await J(f, k.SqlClient.pipe(O.flatMap((n) => O.promise(async () => {
|
|
1552
|
+
let i = await O.runPromise(n.unsafe(`SELECT character_maximum_length AS n FROM information_schema.columns\n WHERE ${K(t)} AND table_name = '${e.table}' AND column_name = '${e.column}'`));
|
|
1553
|
+
if (i.length === 0) throw new r(`voltro db encrypt-column: ${e.table}.${e.column} does not exist on this database`);
|
|
1552
1554
|
let a = Object.values(i[0])[0];
|
|
1553
1555
|
return a == null ? void 0 : Number(a);
|
|
1554
|
-
})))), d = await J(f,
|
|
1555
|
-
for (let e of d) typeof e.v != "string" ||
|
|
1556
|
-
let h =
|
|
1557
|
-
if (!h.fits) return console.error(` ✗ ${
|
|
1558
|
-
if (
|
|
1559
|
-
let g = d.filter((t) =>
|
|
1560
|
-
if (!
|
|
1556
|
+
})))), d = await J(f, k.SqlClient.pipe(O.flatMap((e) => O.promise(async () => await O.runPromise(e.unsafe(`SELECT ${G("id")} AS id, ${o} AS v FROM ${a}`)))))), m = 0;
|
|
1557
|
+
for (let e of d) typeof e.v != "string" || P(e.v) || (m = Math.max(m, ht(e.v)));
|
|
1558
|
+
let h = dt(m, l);
|
|
1559
|
+
if (!h.fits) return console.error(` ✗ ${ft(e, h)}`), 1;
|
|
1560
|
+
if (gt(d.map((e) => e.v).find((e) => typeof e == "string" && P(e)), u) === !1) return console.error(` ✗ ${_t(e, c)}`), 1;
|
|
1561
|
+
let g = d.filter((t) => mt(t.v, u, e) !== void 0).length;
|
|
1562
|
+
if (!n && !i) return console.error(` ✗ ${e.table}.${e.column}: ${g} row(s) would be rewritten in place. Re-run with --yes, or --dry-run to see the counts without writing.`), 1;
|
|
1561
1563
|
let _ = 0, v = 0, y = 0, b = [];
|
|
1562
1564
|
for (let t of d) {
|
|
1563
1565
|
if (t.v === null || t.v === void 0 || t.v === "") {
|
|
1564
1566
|
v += 1;
|
|
1565
1567
|
continue;
|
|
1566
1568
|
}
|
|
1567
|
-
if (typeof t.v == "string" &&
|
|
1569
|
+
if (typeof t.v == "string" && P(t.v)) {
|
|
1568
1570
|
_ += 1;
|
|
1569
1571
|
continue;
|
|
1570
1572
|
}
|
|
1571
|
-
let n =
|
|
1573
|
+
let n = mt(t.v, u, e);
|
|
1572
1574
|
if (n === void 0) {
|
|
1573
1575
|
v += 1;
|
|
1574
1576
|
continue;
|
|
@@ -1578,10 +1580,10 @@ export default defineMigration({
|
|
|
1578
1580
|
value: n
|
|
1579
1581
|
}), y += 1;
|
|
1580
1582
|
}
|
|
1581
|
-
if (!
|
|
1583
|
+
if (!n && b.length > 0) for (let t = 0; t < b.length; t += s) {
|
|
1582
1584
|
let n = b.slice(t, t + s);
|
|
1583
|
-
await J(f,
|
|
1584
|
-
for (let r of n) await
|
|
1585
|
+
await J(f, k.SqlClient.pipe(O.flatMap((t) => O.promise(async () => {
|
|
1586
|
+
for (let r of n) await O.runPromise(t`UPDATE ${t(e.table)} SET ${t(e.column)} = ${r.value} WHERE ${t("id")} = ${r.id}`);
|
|
1585
1587
|
})))), B.info(`encrypt-column ${e.table}.${e.column}: ${Math.min(t + n.length, b.length)}/${b.length}`);
|
|
1586
1588
|
}
|
|
1587
1589
|
p.push({
|
|
@@ -1593,29 +1595,29 @@ export default defineMigration({
|
|
|
1593
1595
|
encrypted: y
|
|
1594
1596
|
});
|
|
1595
1597
|
}
|
|
1596
|
-
console.log(`field encryption · dialect ${t}${
|
|
1597
|
-
for (let e of p) console.log(
|
|
1598
|
-
return
|
|
1598
|
+
console.log(`field encryption · dialect ${t}${n ? " · DRY RUN, nothing written" : ""}`), console.log("");
|
|
1599
|
+
for (let e of p) console.log(pt(e, n));
|
|
1600
|
+
return n || console.log(vt(d)), 0;
|
|
1599
1601
|
}
|
|
1600
|
-
},
|
|
1601
|
-
let [
|
|
1602
|
-
if (!
|
|
1602
|
+
}, mn = (e) => Object.prototype.hasOwnProperty.call(pn, e), hn = async (t) => {
|
|
1603
|
+
let [n, ...r] = t;
|
|
1604
|
+
if (!n) {
|
|
1603
1605
|
console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
|
|
1604
|
-
for (let e of Object.keys(
|
|
1606
|
+
for (let e of Object.keys(pn)) console.error(` ${e}`);
|
|
1605
1607
|
return 1;
|
|
1606
1608
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
+
if (!mn(n)) return console.error(`voltro db: unknown subcommand '${n}'`), console.error(` known: ${e.join(", ")}`), 2;
|
|
1610
|
+
let i = pn[n];
|
|
1609
1611
|
(process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && B.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
|
|
1610
|
-
let
|
|
1611
|
-
|
|
1612
|
+
let a = (e) => {
|
|
1613
|
+
xt(e) ? B.error(St) : console.error(e), process.exit(1);
|
|
1612
1614
|
};
|
|
1613
|
-
process.on("uncaughtException",
|
|
1615
|
+
process.on("uncaughtException", a);
|
|
1614
1616
|
try {
|
|
1615
|
-
return await r
|
|
1617
|
+
return await i(r);
|
|
1616
1618
|
} finally {
|
|
1617
|
-
process.off("uncaughtException",
|
|
1619
|
+
process.off("uncaughtException", a);
|
|
1618
1620
|
}
|
|
1619
1621
|
};
|
|
1620
1622
|
//#endregion
|
|
1621
|
-
export {
|
|
1623
|
+
export { At as a, an as i, U as n, xt as o, kt as r, hn as s, St as t };
|