@voltro/cli 0.22.1 → 0.23.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +316 -0
  2. package/dist/apiBuild-CD-4JLLA.js +2 -0
  3. package/dist/{apiBuild-DiWxVz-M.js → apiBuild-JQtIhZPy.js} +2 -2
  4. package/dist/bin.js +3 -3
  5. package/dist/{commands-BxRaIOBG.js → commands-Bsu9Buln.js} +1767 -1501
  6. package/dist/dbCommand-CSguuUQn.js +2 -0
  7. package/dist/{dbCommand-DVASmZj2.js → dbCommand-GI7-BVZt.js} +440 -259
  8. package/dist/{dev-Dd3EZzj5.js → dev-DRxF_qOB.js} +1 -1
  9. package/dist/{dev-BRiPgbKw.js → dev-jM07kq_D.js} +1479 -1479
  10. package/dist/index.js +1 -1
  11. package/dist/{inspectMetrics-9ZSuDeqD.js → inspectMetrics-dcC7gnxu.js} +566 -552
  12. package/dist/{serveCommand-DXJOARkO.js → serveCommand-Cl8sicD5.js} +280 -279
  13. package/dist/serveEntry.js +2 -2
  14. package/dist/{start-CsIjcOi-.js → start-C_4VUDT8.js} +327 -325
  15. package/dist/startEntry.js +2 -2
  16. package/package.json +17 -17
  17. package/templates/AGENTS.md +1 -1
  18. package/templates/agent-docs/_index.md +1 -1
  19. package/templates/agent-docs/_manifest.json +1 -1
  20. package/templates/agent-docs/caching.md +21 -5
  21. package/templates/agent-docs/cli.md +44 -0
  22. package/templates/agent-docs/data.md +23 -5
  23. package/templates/agent-docs/database/migrations.md +94 -0
  24. package/templates/agent-docs/plugins.md +42 -0
  25. package/templates/agent-docs/routing.md +23 -1
  26. package/templates/agent-docs/templates/apibackends.md +2 -2
  27. package/templates/agent-docs/whats-new.md +281 -32
  28. package/templates/apps/api-ai/package.json +7 -7
  29. package/templates/apps/api-auth/package.json +8 -8
  30. package/templates/apps/api-backend/package.json +7 -7
  31. package/templates/apps/api-backend-deactivation/package.json +7 -7
  32. package/templates/apps/api-backend-mail/package.json +8 -8
  33. package/templates/apps/api-backend-mariadb/package.json +9 -9
  34. package/templates/apps/api-backend-storage/package.json +8 -8
  35. package/templates/apps/api-data-advanced/package.json +8 -8
  36. package/templates/apps/api-durable/package.json +8 -8
  37. package/templates/apps/api-feature-flags/package.json +9 -9
  38. package/templates/apps/api-governance/package.json +8 -8
  39. package/templates/apps/api-kv/package.json +8 -8
  40. package/templates/apps/api-moderation/package.json +8 -8
  41. package/templates/apps/api-observability/package.json +8 -8
  42. package/templates/apps/api-ratelimit/package.json +8 -8
  43. package/templates/apps/api-rbac/package.json +8 -8
  44. package/templates/apps/api-rest/package.json +7 -7
  45. package/templates/apps/api-saas/package.json +11 -11
  46. package/templates/apps/api-search/package.json +8 -8
  47. package/templates/apps/api-versioning/app.config.ts +6 -4
  48. package/templates/apps/api-versioning/package.json +8 -8
  49. package/templates/apps/api-webhooks/package.json +9 -9
  50. package/templates/apps/changelog/package.json +6 -6
  51. package/templates/apps/edge-functions/package.json +2 -2
  52. package/templates/apps/frontend-admin/package.json +8 -8
  53. package/templates/apps/frontend-app/package.json +8 -8
  54. package/templates/apps/frontend-blank/package.json +7 -7
  55. package/templates/apps/frontend-contact/package.json +7 -7
  56. package/templates/apps/frontend-dashboard/package.json +7 -7
  57. package/templates/apps/frontend-docs/package.json +7 -7
  58. package/templates/apps/frontend-i18n/package.json +6 -6
  59. package/templates/apps/frontend-landing/package.json +7 -7
  60. package/templates/apps/frontend-spa/package.json +7 -7
  61. package/templates/apps/frontend-ssr/package.json +7 -7
  62. package/templates/apps/frontend-ssr-api/package.json +8 -8
  63. package/templates/apps/frontend-static-blog/package.json +6 -6
  64. package/dist/apiBuild-COyPDf3R.js +0 -2
  65. package/dist/dbCommand-CO3eSAZR.js +0 -2
@@ -1,45 +1,163 @@
1
1
  import { i as e, n as t, r as n, s as r, t as i } from "./seedRunner-D6eu-u5U.js";
2
- import { t as a } from "./frameworkTableAssembly-sg41s14F.js";
3
- import { g as o } from "./fileConventions-CBNPZVOV.js";
4
- import { a as s, n as c, t as l } from "./cliArgs-qdZSElM3.js";
5
- import { basename as u, join as d, relative as f, resolve as p } from "node:path";
6
- import { pathToFileURL as m } from "node:url";
7
- import { Effect as h } from "effect";
8
- import { createLogger as ee } from "@voltro/logger";
9
- import { promises as g, writeSync as te } from "node:fs";
10
- import { InMemoryDataStore as ne } from "@voltro/runtime";
11
- import { SqlClient as _ } from "@effect/sql";
12
- import { actorsTable as re, frameworkTables as v, isMigrationDefinition as y, registerCoreTables as ie } from "@voltro/database";
2
+ import { n as a } from "./appModuleLoader-C9r9mxZt.js";
3
+ import { t as o } from "./frameworkTableAssembly-sg41s14F.js";
4
+ import { g as s } from "./fileConventions-CBNPZVOV.js";
5
+ import { a as c, n as l, t as u } from "./cliArgs-qdZSElM3.js";
6
+ import { basename as d, join as f, relative as p, resolve as m } from "node:path";
7
+ import { pathToFileURL as h } from "node:url";
8
+ import { Effect as g } from "effect";
9
+ import { createLogger as _ } from "@voltro/logger";
10
+ import { promises as v, writeSync as y } from "node:fs";
11
+ import { InMemoryDataStore as ee } from "@voltro/runtime";
12
+ import { SqlClient as b } from "@effect/sql";
13
+ import { actorsTable as te, frameworkTables as x, isMigrationDefinition as ne, quoteIdent as re, registerCoreTables as ie } from "@voltro/database";
13
14
  import { createHash as ae } from "node:crypto";
14
- import { applyPlan as b, applySchema as x, declaredSnapshot as oe, destructiveScope as se, fingerprintSchema as S, ignoreTablesFromEnv as C, introspectSchema as ce, planMigrations as w, rollbackFileBasedMigration as le, runFileBasedMigrations as ue, shortFingerprint as T, squashMigrationPlans as de, unblockLossy as fe } from "@voltro/database/sql";
15
+ import { applyPlan as oe, applySchema as S, declaredSnapshot as se, destructiveScope as ce, fingerprintSchema as C, ignoreTablesFromEnv as w, introspectSchema as T, pendingFileMigrationIds as le, planMigrations as E, rollbackFileBasedMigration as ue, runFileBasedMigrations as de, shortFingerprint as D, squashMigrationPlans as fe, unblockLossy as pe } from "@voltro/database/sql";
15
16
  //#region src/cliError.ts
16
- var E = class extends Error {
17
+ var O = class extends Error {
17
18
  isCliError = !0;
18
19
  code;
19
20
  constructor(e, t = 1) {
20
21
  super(e), this.name = "CliError", this.code = t;
21
22
  }
22
- }, pe = (e) => typeof e == "object" && !!e && e.isCliError === !0, D = /\.migration\.ts$/, O = async (e, t) => {
23
+ }, me = (e) => typeof e == "object" && !!e && e.isCliError === !0, he = (e) => typeof e != "string", ge = (e, t, n, r) => {
24
+ let i = [], a = new Map(t.map((e) => [e.name, e])), o = new Map(n.map((e) => [e.name, e])), s = new Set(e.leaveUnset ?? []);
25
+ r > 0 && i.push({
26
+ kind: "target-not-empty",
27
+ detail: `'${e.into}' already holds ${r} row(s) — adopt MOVES rows into a table, it does not merge into one`,
28
+ fatal: !0
29
+ });
30
+ let c = [], l = /* @__PURE__ */ new Set();
31
+ for (let [t, n] of Object.entries(e.map)) {
32
+ if (!o.has(t)) {
33
+ i.push({
34
+ kind: "unknown-target-column",
35
+ detail: `'${e.into}' has no column '${t}'`,
36
+ fatal: !0
37
+ });
38
+ continue;
39
+ }
40
+ if (he(n)) {
41
+ c.push({
42
+ target: t,
43
+ source: n.expr,
44
+ raw: !0
45
+ });
46
+ continue;
47
+ }
48
+ if (!a.has(n)) {
49
+ i.push({
50
+ kind: "unknown-source-column",
51
+ detail: `'${e.from}' has no column '${n}' (mapped to '${t}')`,
52
+ fatal: !0
53
+ });
54
+ continue;
55
+ }
56
+ l.add(n), c.push({
57
+ target: t,
58
+ source: n,
59
+ raw: !1
60
+ });
61
+ }
62
+ for (let t of n) t.name in e.map || s.has(t.name) || t.nullable || t.hasDefault || i.push({
63
+ kind: "required-target-unmapped",
64
+ detail: `'${e.into}.${t.name}' is NOT NULL with no default and nothing maps to it`,
65
+ fatal: !0
66
+ });
67
+ for (let n of t) l.has(n.name) || i.push({
68
+ kind: "source-column-unmapped",
69
+ detail: `'${e.from}.${n.name}' is not carried across — its values are lost when the table is dropped`,
70
+ fatal: !1
71
+ });
72
+ let u = t.find((e) => e.idPrefix !== void 0), d = n.find((e) => e.idPrefix !== void 0), f = u !== void 0 && d !== void 0 && u.idPrefix !== d.idPrefix;
73
+ return f && i.push({
74
+ kind: "id-prefix-differs",
75
+ detail: `id prefix '${u.idPrefix}' → '${d.idPrefix}': every row gets a NEW id, so every reference to '${e.from}' elsewhere must be rewritten from the translation table — including ids inside JSON columns`,
76
+ fatal: !1
77
+ }), {
78
+ problems: i,
79
+ assignments: c.sort((e, t) => e.target.localeCompare(t.target)),
80
+ remintIds: f,
81
+ fatal: i.some((e) => e.fatal)
82
+ };
83
+ }, _e = (e, t) => [
84
+ `back up '${e.from}' to a restorable dump`,
85
+ `copy rows: INSERT INTO ${e.into} (${t.assignments.map((e) => e.target).join(", ")}) SELECT … FROM ${e.from}`,
86
+ ...t.remintIds ? ["record old id → new id in a translation table", `rewrite every reference to '${e.from}', JSON columns included`] : [],
87
+ "verify: row counts match and a sample round-trips",
88
+ `drop '${e.from}' — LAST, and only after the verify`
89
+ ], ve = (e, t, n) => {
90
+ let r = [`adopt · ${e.from} (${n} row(s)) → ${e.into}`, ""];
91
+ for (let e of t.assignments) r.push(` ${e.target.padEnd(24)} ← ${e.raw ? `${e.source} (raw)` : e.source}`);
92
+ t.problems.length > 0 && r.push("");
93
+ for (let e of t.problems) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.detail}`);
94
+ if (r.push(""), t.fatal) return r.push(" REFUSED — fix the problems above. Nothing was written."), r;
95
+ r.push(" would run, in this order:");
96
+ for (let [n, i] of _e(e, t).entries()) r.push(` ${n + 1}. ${i}`);
97
+ return r;
98
+ }, ye = (e) => e === "mysql" || e === "mariadb" ? (e) => `\`${e}\`` : e === "mssql" ? (e) => `[${e}]` : (e) => `"${e}"`, k = (e) => `${e}__adopt_snapshot`, be = (e, t, n, r, i) => {
99
+ let a = ye(n), o = k(e.from), s = t.assignments.map((e) => e.target), c = t.assignments.map((e) => e.raw ? e.source : a(e.source));
100
+ return i !== void 0 && (s.unshift(r), c.unshift(i)), {
101
+ snapshot: n === "mssql" ? `SELECT * INTO ${a(o)} FROM ${a(e.from)}` : `CREATE TABLE ${a(o)} AS SELECT * FROM ${a(e.from)}`,
102
+ copy: `INSERT INTO ${a(e.into)} (${s.map(a).join(", ")}) SELECT ${c.join(", ")} FROM ${a(e.from)}`,
103
+ translations: i === void 0 ? [] : [`CREATE TABLE ${a(`${o}__ids`)} (old_id TEXT, new_id TEXT)`],
104
+ verifySource: `SELECT COUNT(*) AS n FROM ${a(e.from)}`,
105
+ verifyTarget: `SELECT COUNT(*) AS n FROM ${a(e.into)}`,
106
+ drop: `DROP TABLE ${a(e.from)}`,
107
+ dropSnapshot: `DROP TABLE ${a(o)}`
108
+ };
109
+ }, A = (e) => {
110
+ let t = e[0];
111
+ if (t === void 0) return 0;
112
+ let n = t.n ?? t.N ?? Object.values(t)[0];
113
+ return Number(n ?? 0);
114
+ }, xe = async (e, t, n, r, i, a, o = {}) => {
115
+ if (t.fatal) throw Error("adopt: the plan was refused — nothing was run");
116
+ let s = be(e, t, n, r, i), c = k(e.from), l = A(await a(s.verifySource));
117
+ await a(s.snapshot);
118
+ for (let e of s.translations) await a(e);
119
+ await a(s.copy);
120
+ let u = A(await a(s.verifyTarget));
121
+ return u === l ? o.keepSource === !0 ? {
122
+ copied: u,
123
+ sourceRows: l,
124
+ verified: !0,
125
+ snapshotTable: c,
126
+ sourceDropped: !1
127
+ } : (await a(s.drop), {
128
+ copied: u,
129
+ sourceRows: l,
130
+ verified: !0,
131
+ snapshotTable: c,
132
+ sourceDropped: !0
133
+ }) : {
134
+ copied: u,
135
+ sourceRows: l,
136
+ verified: !1,
137
+ snapshotTable: c,
138
+ sourceDropped: !1
139
+ };
140
+ }, j = /\.migration\.ts$/, M = async (e, t) => {
23
141
  let n = [];
24
142
  for (let r of e) {
25
143
  let e;
26
144
  try {
27
- e = await import(m(r).href);
145
+ e = await import(h(r).href);
28
146
  } catch (e) {
29
147
  t.warn("migration: failed to import", { file: r }, e);
30
148
  continue;
31
149
  }
32
- let i = (e.default && y(e.default) ? e.default : void 0) ?? Object.values(e).find(y);
150
+ let i = (e.default && ne(e.default) ? e.default : void 0) ?? Object.values(e).find(ne);
33
151
  if (!i) {
34
152
  t.warn("migration: no defineMigration export", { file: r });
35
153
  continue;
36
154
  }
37
- i.id !== u(r).replace(/\.migration\.ts$/, "") && t.warn("migration: id does not match filename — id wins for ordering, but rename the file for clarity", {
155
+ i.id !== d(r).replace(/\.migration\.ts$/, "") && t.warn("migration: id does not match filename — id wins for ordering, but rename the file for clarity", {
38
156
  file: r,
39
157
  idInFile: i.id,
40
- idFromFile: u(r).replace(/\.migration\.ts$/, "")
158
+ idFromFile: d(r).replace(/\.migration\.ts$/, "")
41
159
  });
42
- let a = await g.readFile(r, "utf8").catch(() => ""), o = ae("sha256").update(a).digest("hex").slice(0, 16);
160
+ let a = await v.readFile(r, "utf8").catch(() => ""), o = ae("sha256").update(a).digest("hex").slice(0, 16);
43
161
  n.push({
44
162
  file: r,
45
163
  definition: i,
@@ -47,14 +165,14 @@ var E = class extends Error {
47
165
  });
48
166
  }
49
167
  return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
50
- }, k = (e) => {
168
+ }, N = (e) => {
51
169
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t;
52
- return h.runPromise(x(v, n).pipe(h.provide(e)));
53
- }, A = (e) => h.runPromise(h.gen(function* () {
54
- let e = yield* (yield* _.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(h.orElseSucceed(() => []));
170
+ return g.runPromise(S(x, n).pipe(g.provide(e)));
171
+ }, P = (e) => g.runPromise(g.gen(function* () {
172
+ let e = yield* (yield* b.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(g.orElseSucceed(() => []));
55
173
  return new Map(e.map((e) => [e.id, e]));
56
- }).pipe(h.provide(e))), j = (e, t) => h.runPromise(h.gen(function* () {
57
- yield* (yield* _.SqlClient)`
174
+ }).pipe(g.provide(e))), F = (e, t) => g.runPromise(g.gen(function* () {
175
+ yield* (yield* b.SqlClient)`
58
176
  INSERT INTO "_voltro_migrations"
59
177
  (id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
60
178
  VALUES
@@ -69,14 +187,14 @@ var E = class extends Error {
69
187
  "errorStack" = EXCLUDED."errorStack",
70
188
  "updatedAt" = now()
71
189
  `;
72
- }).pipe(h.provide(e))), me = async (e, t, n) => {
190
+ }).pipe(g.provide(e))), Se = async (e, t, n) => {
73
191
  if (e.length === 0) return {
74
192
  applied: [],
75
193
  failed: null,
76
194
  skipped: []
77
195
  };
78
- await k(t);
79
- let r = await A(t), i = [], a = [], o = null;
196
+ await N(t);
197
+ let r = await P(t), i = [], a = [], o = null;
80
198
  for (let s of e) {
81
199
  let e = r.get(s.definition.id);
82
200
  if (e && e.status === "applied") {
@@ -97,7 +215,7 @@ var E = class extends Error {
97
215
  name: s.definition.name
98
216
  });
99
217
  let c = Date.now(), l = [];
100
- await j(t, {
218
+ await F(t, {
101
219
  id: s.definition.id,
102
220
  name: s.definition.name,
103
221
  hash: s.hash,
@@ -111,7 +229,7 @@ var E = class extends Error {
111
229
  for (let e of s.definition.steps) {
112
230
  let n = Date.now();
113
231
  try {
114
- await h.runPromise(e.apply.pipe(h.provide(t))), l.push({
232
+ await g.runPromise(e.apply.pipe(g.provide(t))), l.push({
115
233
  stepName: e.name,
116
234
  status: "applied",
117
235
  durationMs: Date.now() - n
@@ -138,7 +256,7 @@ var E = class extends Error {
138
256
  ...a.stack ? { stack: a.stack } : {}
139
257
  }
140
258
  };
141
- o = f, i.push(f), await j(t, {
259
+ o = f, i.push(f), await F(t, {
142
260
  id: s.definition.id,
143
261
  name: s.definition.name,
144
262
  hash: s.hash,
@@ -157,7 +275,7 @@ var E = class extends Error {
157
275
  status: "applied",
158
276
  durationMs: Date.now() - c,
159
277
  steps: l
160
- }), await j(t, {
278
+ }), await F(t, {
161
279
  id: s.definition.id,
162
280
  name: s.definition.name,
163
281
  hash: s.hash,
@@ -177,9 +295,9 @@ var E = class extends Error {
177
295
  failed: o,
178
296
  skipped: a
179
297
  };
180
- }, he = async (e, t, n, r = {}) => {
181
- await k(t);
182
- let i = await A(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
298
+ }, Ce = async (e, t, n, r = {}) => {
299
+ await N(t);
300
+ let i = await P(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
183
301
  if (a.length === 0) return n.info("migration: nothing to roll back"), {
184
302
  applied: [],
185
303
  failed: null,
@@ -197,7 +315,7 @@ var E = class extends Error {
197
315
  for (let n = e.definition.steps.length - 1; n >= 0; n--) {
198
316
  let o = e.definition.steps[n], s = Date.now();
199
317
  try {
200
- await h.runPromise(o.undo.pipe(h.provide(t))), i.push({
318
+ await g.runPromise(o.undo.pipe(g.provide(t))), i.push({
201
319
  stepName: o.name,
202
320
  status: "applied",
203
321
  durationMs: Date.now() - s
@@ -232,7 +350,7 @@ var E = class extends Error {
232
350
  status: "applied",
233
351
  durationMs: Date.now() - r,
234
352
  steps: i
235
- }), await j(t, {
353
+ }), await F(t, {
236
354
  id: e.definition.id,
237
355
  name: e.definition.name,
238
356
  hash: e.hash,
@@ -248,9 +366,9 @@ var E = class extends Error {
248
366
  failed: c,
249
367
  skipped: []
250
368
  };
251
- }, M = async (e, t) => {
252
- await k(t);
253
- let n = await A(t);
369
+ }, I = async (e, t) => {
370
+ await N(t);
371
+ let n = await P(t);
254
372
  return e.map((e) => {
255
373
  let t = n.get(e.definition.id);
256
374
  return t ? {
@@ -267,7 +385,7 @@ var E = class extends Error {
267
385
  hashMatches: !0
268
386
  };
269
387
  });
270
- }, N = (e) => {
388
+ }, L = (e) => {
271
389
  if (!e.failed) return "";
272
390
  let t = e.failed, n = [
273
391
  `✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
@@ -280,7 +398,7 @@ var E = class extends Error {
280
398
  ""
281
399
  ];
282
400
  return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
283
- }, P = ee({ scope: "voltro:db" }), ge = /* @__PURE__ */ new Set([
401
+ }, R = _({ scope: "voltro:db" }), we = /* @__PURE__ */ new Set([
284
402
  "node_modules",
285
403
  "dist",
286
404
  "build",
@@ -288,17 +406,17 @@ var E = class extends Error {
288
406
  ".vite",
289
407
  ".next",
290
408
  ".git"
291
- ]), F = async (e, t) => {
409
+ ]), z = async (e, t) => {
292
410
  let n = [], r = async (e) => {
293
- let i = await g.readdir(e, { withFileTypes: !0 }).catch(() => []);
411
+ let i = await v.readdir(e, { withFileTypes: !0 }).catch(() => []);
294
412
  for (let a of i) {
295
- if (ge.has(a.name)) continue;
296
- let i = d(e, a.name);
413
+ if (we.has(a.name)) continue;
414
+ let i = f(e, a.name);
297
415
  a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
298
416
  }
299
417
  };
300
418
  return await r(e), n;
301
- }, I = (e) => p(e.find((e) => !e.startsWith("-")) ?? process.cwd()), L = () => {
419
+ }, B = (e) => m(e.find((e) => !e.startsWith("-")) ?? process.cwd()), V = () => {
302
420
  let e = process.env.DB_MAX_CONNECTIONS ?? process.env.PG_MAX_CONNECTIONS, t = e !== void 0 && e !== "" ? Number(e) : NaN, n = Number.isInteger(t) && t > 0 ? { maxConnections: t } : {}, r = process.env.DB_SCHEMA, i = r && r.length > 0 ? { schema: r } : {}, a = process.env.DB_DIRECT_URL ?? process.env.DB_MIGRATE_URL ?? process.env.DB_URL ?? process.env.DB_PRIMARY_URL;
303
421
  return a ? {
304
422
  url: a,
@@ -313,63 +431,63 @@ var E = class extends Error {
313
431
  ...n,
314
432
  ...i
315
433
  };
316
- }, R = (e) => {
434
+ }, H = (e) => {
317
435
  let t = e;
318
436
  if (typeof t != "object" || !t) return !1;
319
437
  if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
320
438
  let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
321
439
  return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
322
- }, z = "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`.)", B = () => {
440
+ }, U = "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 = () => {
323
441
  let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
324
- if (e === "memory") throw new E("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
442
+ if (e === "memory") throw new O("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
325
443
  return e;
326
- }, V = async () => (await r(B())).makeSqlLayer(L()), _e = async (e) => {
327
- let t = I(e), n = e.includes("--dry-run"), r = await O(await F(t, D), P);
328
- if (r.length === 0) return P.info("no migrations found", { root: t }), 0;
444
+ }, G = async () => (await r(W())).makeSqlLayer(V()), Te = async (e) => {
445
+ let t = B(e), n = e.includes("--dry-run"), r = await M(await z(t, j), R);
446
+ if (r.length === 0) return R.info("no migrations found", { root: t }), 0;
329
447
  if (n) {
330
448
  let e = /* @__PURE__ */ new Set();
331
449
  try {
332
- let t = await M(r, await V());
450
+ let t = await I(r, await G());
333
451
  e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
334
452
  } catch {
335
- P.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
453
+ R.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
336
454
  }
337
455
  let t = r.filter((t) => !e.has(t.definition.id));
338
- if (t.length === 0) return P.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
339
- P.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
456
+ if (t.length === 0) return R.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
457
+ R.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
340
458
  for (let e of t) console.log(` pending ${e.definition.id}`);
341
459
  return 0;
342
460
  }
343
- let i = await me(r, await V(), P);
344
- return i.failed ? (process.stderr.write(N(i) + "\n"), 1) : (P.info("migrate complete", {
461
+ let i = await Se(r, await G(), R);
462
+ return i.failed ? (process.stderr.write(L(i) + "\n"), 1) : (R.info("migrate complete", {
345
463
  applied: i.applied.length,
346
464
  skipped: i.skipped.length
347
465
  }), 0);
348
- }, ve = async (e) => {
349
- let t = I(e), n = c(e, "--to"), r = await O(await F(t, D), P);
350
- if (r.length === 0) return P.info("no migrations found", { root: t }), 0;
351
- let i = await he(r, await V(), P, n ? { to: n } : {});
352
- return i.failed ? (process.stderr.write(N(i) + "\n"), 1) : (P.info("rollback complete", { reverted: i.applied.length }), 0);
353
- }, ye = async (e) => {
354
- let t = await O(await F(I(e), D), P);
466
+ }, Ee = async (e) => {
467
+ let t = B(e), n = l(e, "--to"), r = await M(await z(t, j), R);
468
+ if (r.length === 0) return R.info("no migrations found", { root: t }), 0;
469
+ let i = await Ce(r, await G(), R, n ? { to: n } : {});
470
+ return i.failed ? (process.stderr.write(L(i) + "\n"), 1) : (R.info("rollback complete", { reverted: i.applied.length }), 0);
471
+ }, De = async (e) => {
472
+ let t = await M(await z(B(e), j), R);
355
473
  if (t.length === 0) return console.log("no migrations found"), 0;
356
- let n = await M(t, await V());
474
+ let n = await I(t, await G());
357
475
  for (let e of n) {
358
476
  let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
359
477
  console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
360
478
  }
361
479
  return 0;
362
- }, be = async (e) => {
363
- let t = (c(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
364
- if (t === "memory") return P.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), P.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
365
- store: new ne(),
480
+ }, Oe = async (e) => {
481
+ let t = (l(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
482
+ if (t === "memory") return R.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), R.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
483
+ store: new ee(),
366
484
  dispose: async () => {},
367
485
  dialectId: "memory"
368
486
  };
369
487
  let n = await r(t);
370
- P.info(`voltro db seed: target store=${n.id} — rows will persist`);
488
+ R.info(`voltro db seed: target store=${n.id} — rows will persist`);
371
489
  let i = await n.makeStore({
372
- sqlLayer: n.makeSqlLayer(L()),
490
+ sqlLayer: n.makeSqlLayer(V()),
373
491
  changeStrategy: "inline"
374
492
  }), a = i;
375
493
  return {
@@ -379,77 +497,77 @@ var E = class extends Error {
379
497
  },
380
498
  dialectId: n.id
381
499
  };
382
- }, xe = async (r) => {
383
- let a = I(r), o = c(r, "--id"), s = c(r, "--lifecycle"), l = await t(await F(a, i), P);
384
- if (l.length === 0) return P.info("no seeds found", { root: a }), 0;
385
- let { store: u, dispose: d, dialectId: f } = await be(r);
500
+ }, ke = async (r) => {
501
+ let a = B(r), o = l(r, "--id"), s = l(r, "--lifecycle"), c = await t(await z(a, i), R);
502
+ if (c.length === 0) return R.info("no seeds found", { root: a }), 0;
503
+ let { store: u, dispose: d, dialectId: f } = await Oe(r);
386
504
  try {
387
- return o ? +((await e(l, o, u, P)).status === "failed") : +((await n((s ? l.filter((e) => e.definition.lifecycle === s) : l.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
505
+ return o ? +((await e(c, o, u, R)).status === "failed") : +((await n((s ? c.filter((e) => e.definition.lifecycle === s) : c.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
388
506
  ...e,
389
507
  definition: {
390
508
  ...e.definition,
391
509
  lifecycle: "boot"
392
510
  }
393
- })), u, P, { force: !0 })).filter((e) => e.status === "failed").length > 0);
511
+ })), u, R, { force: !0 })).filter((e) => e.status === "failed").length > 0);
394
512
  } finally {
395
513
  await d().catch((e) => {
396
- P.warn("voltro db seed: store dispose failed", { error: String(e) });
514
+ R.warn("voltro db seed: store dispose failed", { error: String(e) });
397
515
  });
398
516
  }
399
- }, Se = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", H = async (e) => {
400
- let t = await F(e, o), n = [];
517
+ }, Ae = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", K = async (e) => {
518
+ let t = await z(e, s), n = [];
401
519
  for (let r of t) {
402
520
  let t;
403
521
  try {
404
- t = await import(m(r).href);
522
+ t = await import(h(r).href);
405
523
  } catch (t) {
406
- P.warn("db: failed to import schema file", {
407
- file: f(e, r),
524
+ R.warn("db: failed to import schema file", {
525
+ file: p(e, r),
408
526
  error: String(t)
409
527
  });
410
528
  continue;
411
529
  }
412
- for (let e of Object.values(t)) Se(e) && n.push(e);
530
+ for (let e of Object.values(t)) Ae(e) && n.push(e);
413
531
  }
414
532
  return n;
415
- }, U = (e) => {
533
+ }, q = (e) => {
416
534
  let t = new Map(e.map((e) => [e.tableName, e]));
417
- t.has("actors") || t.set("actors", re);
535
+ t.has("actors") || t.set("actors", te);
418
536
  let n = t.get("actors"), r = t.get("tenants");
419
537
  return ie({
420
538
  actors: () => n,
421
539
  ...r === void 0 ? {} : { tenants: () => r }
422
540
  }), [...t.values()];
423
- }, Ce = (e) => {
541
+ }, je = (e) => {
424
542
  let t = /* @__PURE__ */ new Map();
425
543
  for (let n of e) {
426
544
  let e = n.tableName;
427
545
  e !== void 0 && !t.has(e) && t.set(e, n);
428
546
  }
429
547
  return [...t.values()];
430
- }, W = (e) => {
431
- let t = se(process.env.VOLTRO_DESTRUCTIVE_OK);
548
+ }, J = (e) => {
549
+ let t = ce(process.env.VOLTRO_DESTRUCTIVE_OK);
432
550
  if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
433
551
  plan: e,
434
552
  acknowledged: !1
435
553
  };
436
- let n = fe(e, t);
554
+ let n = pe(e, t);
437
555
  return {
438
556
  plan: n,
439
557
  acknowledged: !n.operations.some((e) => e.blocked)
440
558
  };
441
- }, G = async (e, t) => h.runPromise(t.pipe(h.provide(e))), K = async (e) => {
559
+ }, Y = async (e, t) => g.runPromise(t.pipe(g.provide(e))), X = async (e) => {
442
560
  try {
443
- return await G(e, _.SqlClient.pipe(h.flatMap(ce)));
561
+ return await Y(e, b.SqlClient.pipe(g.flatMap(T)));
444
562
  } catch (e) {
445
563
  let t = String(e?.message ?? e);
446
- if (R(e) || /connection error/i.test(t)) {
447
- let n = /* @__PURE__ */ Error(`${z}\n\nOriginal error: ${t}`);
564
+ if (H(e) || /connection error/i.test(t)) {
565
+ let n = /* @__PURE__ */ Error(`${U}\n\nOriginal error: ${t}`);
448
566
  throw n.cause = e, n;
449
567
  }
450
568
  throw e;
451
569
  }
452
- }, we = {
570
+ }, Me = {
453
571
  safe: "\x1B[32m✓\x1B[0m",
454
572
  "needs-default": "\x1B[32m⊕\x1B[0m",
455
573
  "needs-backfill": "\x1B[33m⊕\x1B[0m",
@@ -457,7 +575,7 @@ var E = class extends Error {
457
575
  lossy: "\x1B[31m✗\x1B[0m",
458
576
  "online-required": "\x1B[36m◷\x1B[0m",
459
577
  "multi-step": "\x1B[35m⇉\x1B[0m"
460
- }, Te = (e) => {
578
+ }, Ne = (e) => {
461
579
  let t = e.op, n;
462
580
  switch (t.kind) {
463
581
  case "create-table":
@@ -521,147 +639,171 @@ var E = class extends Error {
521
639
  n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
522
640
  break;
523
641
  }
524
- let r = we[e.classification], i = e.reason ? ` # ${e.reason}` : "", a = e.blocked ? `\n ! ${e.blocked.fix}` : "";
642
+ let r = Me[e.classification], i = e.reason ? ` # ${e.reason}` : "", a = e.blocked ? `\n ! ${e.blocked.fix}` : "";
525
643
  return ` ${r} ${n}${i}${a}`;
526
- }, q = (e) => {
644
+ }, Pe = async (e, t) => {
645
+ let n = e.operations.filter((e) => e.classification === "needs-default" && e.op.kind === "add-column");
646
+ if (n.length === 0) return;
647
+ let r = [];
648
+ for (let e of n) {
649
+ let n = e.op, i = n.table, a = n.column?.name;
650
+ if (i === void 0 || a === void 0) continue;
651
+ let o = 0;
652
+ try {
653
+ let e = await Y(t, b.SqlClient.pipe(g.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${re(i, W())}`))));
654
+ o = Number(Object.values(e[0] ?? { n: 0 })[0] ?? 0);
655
+ } catch {
656
+ continue;
657
+ }
658
+ o !== 0 && r.push(` ${o.toLocaleString("en-US")} existing row(s) in ${i} will get the default for ${a}`);
659
+ }
660
+ if (r.length !== 0) {
661
+ console.log("\n existing rows that a default will fill:");
662
+ for (let e of r) console.log(e);
663
+ console.log(" (the shape is correct either way — this is the VALUE a state diff cannot know)");
664
+ }
665
+ }, Z = (e) => {
527
666
  let t = e.summary, n = e.operations.length;
528
667
  if (console.log(`\nschema diff: ${n} operation${n === 1 ? "" : "s"}, ${t.blocked} blocked\n`), n === 0) {
529
668
  console.log(" (schema is up to date)\n");
530
669
  return;
531
670
  }
532
- for (let t of e.operations) console.log(Te(t));
671
+ for (let t of e.operations) console.log(Ne(t));
533
672
  console.log(), console.log(` safe: ${t.safe} needs-default: ${t.needsDefault} needs-backfill: ${t.needsBackfill}`), console.log(` rename: ${t.needsRenameAnnotation} lossy: ${t.lossy} blocked: ${t.blocked}`), console.log(` fingerprint: ${e.fromFingerprint.slice(0, 16)} → ${e.toFingerprint.slice(0, 16)}\n`);
534
- }, J = (e) => {
535
- let t = c(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
673
+ }, Fe = (e) => {
674
+ let t = l(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
536
675
  if (t === void 0) return;
537
676
  let n = Number(t);
538
677
  if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
539
678
  return n;
540
- }, Y = async (e) => {
541
- let t = await H(e);
679
+ }, Q = async (e) => {
680
+ let t = await K(e);
542
681
  if (t.length === 0) return;
543
- let n = await a({ root: e });
544
- return U(Ce([
682
+ let n = await o({ root: e });
683
+ return q(je([
545
684
  ...t,
546
- ...v,
685
+ ...x,
547
686
  ...n
548
687
  ]));
549
- }, Ee = async (e) => {
550
- let t = J(e), n = I(e), r = await Y(n);
551
- if (r === void 0) return P.warn("db plan: no schema files found", {
688
+ }, Ie = async (e) => {
689
+ let t = Fe(e), n = B(e), r = await Q(n);
690
+ if (r === void 0) return R.warn("db plan: no schema files found", {
552
691
  root: n,
553
692
  hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
554
693
  }), 1;
555
- let i = c(e, "--against");
694
+ let i = l(e, "--against");
556
695
  if (i) {
557
- let n = i, a = c(e, "--token") ?? process.env.VOLTRO_INSPECT_TOKEN, o = { accept: "application/json" };
696
+ let n = i, a = l(e, "--token") ?? process.env.VOLTRO_INSPECT_TOKEN, o = { accept: "application/json" };
558
697
  a && (o.authorization = `Bearer ${a}`);
559
698
  try {
560
699
  let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers: o });
561
- if (!i.ok) return P.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
700
+ if (!i.ok) return R.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
562
701
  let a = (await i.json()).drift?.liveSnapshot;
563
- if (!a) return P.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
564
- let s = w({
702
+ if (!a) return R.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
703
+ let s = E({
565
704
  declared: r,
566
705
  live: a,
567
- dialect: B(),
568
- ...C(),
706
+ dialect: W(),
707
+ ...w(),
569
708
  ...t === void 0 ? {} : { onlineAfter: t }
570
709
  });
571
- return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), q(s), s.summary.blocked > 0 ? 2 : 0;
710
+ return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), Z(s), s.summary.blocked > 0 ? 2 : 0;
572
711
  } catch (e) {
573
- return P.error(`db plan --against: ${e.message}`), 1;
712
+ return R.error(`db plan --against: ${e.message}`), 1;
574
713
  }
575
714
  }
576
- let a = w({
715
+ let a = E({
577
716
  declared: r,
578
- live: await K(await V()),
579
- dialect: B(),
580
- ...C(),
717
+ live: await X(await G()),
718
+ dialect: W(),
719
+ ...w(),
581
720
  ...t === void 0 ? {} : { onlineAfter: t }
582
721
  });
583
- return e.includes("--json") ? (te(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (q(a), a.summary.blocked > 0 ? 2 : 0);
584
- }, De = async (e, t, n) => {
722
+ return e.includes("--json") ? (y(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (Z(a), a.summary.blocked > 0 ? 2 : 0);
723
+ }, Le = async (e, t, n) => {
585
724
  let r;
586
725
  try {
587
- let t = await g.readFile(p(e), "utf8");
726
+ let t = await v.readFile(m(e), "utf8");
588
727
  r = JSON.parse(t);
589
728
  } catch (t) {
590
- return P.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
729
+ return R.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
591
730
  }
592
- if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return P.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
593
- let i = await Y(n);
594
- if (i === void 0) return P.warn("db apply --plan: no schema files found", { root: n }), 1;
595
- let a = await V();
596
- await G(a, x(v, B()));
597
- let o = w({
731
+ if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return R.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
732
+ let i = await Q(n);
733
+ if (i === void 0) return R.warn("db apply --plan: no schema files found", { root: n }), 1;
734
+ let a = await G();
735
+ await Y(a, S(x, W()));
736
+ let o = await Y(a, b.SqlClient.pipe(g.flatMap((e) => le(e, n)))).catch(() => []);
737
+ if (o.length > 0) return R.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;
738
+ let s = E({
598
739
  declared: i,
599
- live: await K(a),
600
- dialect: B(),
601
- ...C()
740
+ live: await X(a),
741
+ dialect: W(),
742
+ ...w()
602
743
  });
603
- if (o.fromFingerprint !== r.fromFingerprint) return P.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=${o.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
604
- if (o.toFingerprint !== r.toFingerprint) return P.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${o.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
605
- let s = W(o), l = s.plan;
606
- if (l.summary.blocked > 0 && !s.acknowledged) return q(l), P.error(`db apply --plan: refusing — ${o.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (P.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), P.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
607
- if (s.acknowledged && P.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), l.operations.length === 0) return P.info("db apply --plan: schema is up to date — nothing to apply"), 0;
608
- q(l);
609
- let u = (process.env.NODE_ENV ?? "dev").toLowerCase(), d = c(t, "--note"), f = await G(a, _.SqlClient.pipe(h.flatMap((e) => b(e, l, {
744
+ if (s.fromFingerprint !== r.fromFingerprint) return R.error("db apply --plan: refusing — the live schema has drifted since this plan was generated."), console.error(` plan.from=${r.fromFingerprint.slice(0, 16)} live=${s.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
745
+ if (s.toFingerprint !== r.toFingerprint) return R.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${s.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
746
+ let c = J(s), u = c.plan;
747
+ if (u.summary.blocked > 0 && !c.acknowledged) return Z(u), R.error(`db apply --plan: refusing — ${s.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (R.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), R.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
748
+ if (c.acknowledged && R.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), u.operations.length === 0) return R.info("db apply --plan: schema is up to date — nothing to apply"), 0;
749
+ Z(u);
750
+ let d = (process.env.NODE_ENV ?? "dev").toLowerCase(), f = l(t, "--note"), p = await Y(a, b.SqlClient.pipe(g.flatMap((e) => oe(e, u, {
610
751
  declared: i,
611
752
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
612
- environment: u === "production" ? "prod" : u === "staging" ? "staging" : "dev",
753
+ environment: d === "production" ? "prod" : d === "staging" ? "staging" : "dev",
613
754
  source: "file",
614
- ...d ? { notes: d } : {},
615
- replan: () => h.promise(() => K(a)).pipe(h.map((e) => w({
755
+ ...f ? { notes: f } : {},
756
+ replan: () => g.promise(() => X(a)).pipe(g.map((e) => E({
616
757
  declared: i,
617
758
  live: e,
618
- dialect: B(),
619
- ...C()
759
+ dialect: W(),
760
+ ...w()
620
761
  })))
621
762
  }))));
622
- return P.info(`db apply --plan: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
623
- id: f.id,
624
- fingerprint: f.fingerprint.slice(0, 16)
763
+ return R.info(`db apply --plan: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
764
+ id: p.id,
765
+ fingerprint: p.fingerprint.slice(0, 16)
625
766
  }), 0;
626
- }, Oe = async (e) => {
627
- let t = I(e), n = J(e), r = c(e, "--plan");
628
- if (r) return De(r, e, t);
629
- if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return P.error("voltro db apply: refusing — auto-apply on prod is not allowed."), console.error("In production, schema changes go through `voltro db plan --json > plan.json`"), console.error("then `voltro db apply --plan plan.json` as an explicit step in your deploy pipeline."), 3;
630
- let i = await Y(t);
631
- if (i === void 0) return P.warn("db apply: no schema files found", { root: t }), 1;
632
- let a = await V();
633
- await G(a, x(v, B()));
634
- let o = w({
767
+ }, Re = async (e) => {
768
+ let t = B(e), n = Fe(e), r = l(e, "--plan");
769
+ if (r) return Le(r, e, t);
770
+ if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return R.error("voltro db apply: refusing — auto-apply on prod is not allowed."), console.error("In production, schema changes go through `voltro db plan --json > plan.json`"), console.error("then `voltro db apply --plan plan.json` as an explicit step in your deploy pipeline."), 3;
771
+ let i = await Q(t);
772
+ if (i === void 0) return R.warn("db apply: no schema files found", { root: t }), 1;
773
+ let a = await G(), o = await qe(t, a);
774
+ if ("failed" in o) return R.error("db apply: a file-based migration FAILED — the schema diff was NOT run"), console.error(o.failed), console.error("Fix the migration and re-run. `voltro db files` runs them alone."), 1;
775
+ await Y(a, S(x, W()));
776
+ let s = E({
635
777
  declared: i,
636
- live: await K(a),
637
- dialect: B(),
638
- ...C(),
778
+ live: await X(a),
779
+ dialect: W(),
780
+ ...w(),
639
781
  ...n === void 0 ? {} : { onlineAfter: n }
640
782
  });
641
- q(o);
642
- let s = W(o), l = s.plan;
643
- if (l.summary.blocked > 0 && !s.acknowledged) return P.error(`db apply: refusing — ${l.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (P.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), P.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
644
- if (s.acknowledged && P.warn(`db apply: proceeding with ${o.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), l.operations.length === 0) return P.info("db apply: schema is up to date — nothing to apply"), await Ne(a), c(e, "--note") !== void 0 && (P.warn("db apply: --note was ignored — an empty plan writes no history row to attach it to."), console.error("To record that the CURRENT live schema is the accepted baseline, use `voltro db drift --accept`.")), 0;
645
- let u = c(e, "--note"), d = await G(a, _.SqlClient.pipe(h.flatMap((e) => b(e, l, {
783
+ Z(s), await Pe(s, a);
784
+ let c = J(s), u = c.plan;
785
+ if (u.summary.blocked > 0 && !c.acknowledged) return R.error(`db apply: refusing — ${u.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (R.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), R.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
786
+ if (c.acknowledged && R.warn(`db apply: proceeding with ${s.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), u.operations.length === 0) return R.info("db apply: schema is up to date — nothing to apply"), await Ue(a), l(e, "--note") !== void 0 && (R.warn("db apply: --note was ignored — an empty plan writes no history row to attach it to."), console.error("To record that the CURRENT live schema is the accepted baseline, use `voltro db drift --accept`.")), 0;
787
+ let d = l(e, "--note"), f = await Y(a, b.SqlClient.pipe(g.flatMap((e) => oe(e, u, {
646
788
  declared: i,
647
789
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
648
790
  environment: (process.env.NODE_ENV ?? "dev").toLowerCase() === "staging" ? "staging" : "dev",
649
791
  source: "auto-diff",
650
- ...u ? { notes: u } : {},
651
- replan: () => h.promise(() => K(a)).pipe(h.map((e) => w({
792
+ ...d ? { notes: d } : {},
793
+ replan: () => g.promise(() => X(a)).pipe(g.map((e) => E({
652
794
  declared: i,
653
795
  live: e,
654
- dialect: B(),
655
- ...C(),
796
+ dialect: W(),
797
+ ...w(),
656
798
  ...n === void 0 ? {} : { onlineAfter: n }
657
799
  })))
658
800
  }))));
659
- return P.info(`db apply: applied ${d.appliedOps} op(s) in ${d.durationMs}ms`, {
660
- id: d.id,
661
- fingerprint: d.fingerprint.slice(0, 16)
801
+ return R.info(`db apply: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
802
+ id: f.id,
803
+ fingerprint: f.fingerprint.slice(0, 16)
662
804
  }), 0;
663
- }, ke = async (e) => {
664
- let t = Math.max(1, Number(c(e, "--limit") ?? "20")), n = await G(await V(), _.SqlClient.pipe(h.flatMap((e) => e`
805
+ }, ze = async (e) => {
806
+ let t = Math.max(1, Number(l(e, "--limit") ?? "20")), n = await Y(await G(), b.SqlClient.pipe(g.flatMap((e) => e`
665
807
  SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")},
666
808
  ${e("appliedBy")}, ${e("environment")}, ${e("source")},
667
809
  ${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}
@@ -669,25 +811,25 @@ var E = class extends Error {
669
811
  ORDER BY ${e("appliedAt")} DESC
670
812
  LIMIT ${t}
671
813
  `)));
672
- if (n.length === 0) return P.info("db plans: no planner-based migrations applied yet"), console.log("Run `voltro db apply` after a schema change to start a plan history."), 0;
814
+ if (n.length === 0) return R.info("db plans: no planner-based migrations applied yet"), console.log("Run `voltro db apply` after a schema change to start a plan history."), 0;
673
815
  console.log(`\n${n.length} plan${n.length === 1 ? "" : "s"} (newest first):\n`);
674
816
  for (let e of n) {
675
817
  let t = 0;
676
818
  try {
677
- t = JSON.parse(X(e.operations)).length;
819
+ t = JSON.parse(We(e.operations)).length;
678
820
  } catch {}
679
- console.log(` ${e.id} fp=${T(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${Z(e.appliedAt)} by=${e.appliedBy}`), e.notes && console.log(` [2mnote: ${e.notes}[0m`);
821
+ console.log(` ${e.id} fp=${D(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${$(e.appliedAt)} by=${e.appliedBy}`), e.notes && console.log(` [2mnote: ${e.notes}[0m`);
680
822
  }
681
823
  return console.log(), 0;
682
- }, Ae = async (e) => {
824
+ }, Be = async (e) => {
683
825
  let t = e[0];
684
826
  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;
685
- let n = await V(), r = await G(n, _.SqlClient.pipe(h.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
827
+ let n = await G(), r = await Y(n, b.SqlClient.pipe(g.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
686
828
  FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
687
829
  if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
688
830
  let i;
689
831
  try {
690
- i = JSON.parse(X(r[0].operations));
832
+ i = JSON.parse(We(r[0].operations));
691
833
  } catch {
692
834
  return console.error(`restore-snapshot: plan '${t}' has corrupted operations JSON`), 1;
693
835
  }
@@ -701,7 +843,7 @@ var E = class extends Error {
701
843
  if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
702
844
  let s = 0;
703
845
  for (let e of a) {
704
- let t = await G(n, _.SqlClient.pipe(h.flatMap((t) => t`
846
+ let t = await Y(n, b.SqlClient.pipe(g.flatMap((t) => t`
705
847
  SELECT column_name FROM information_schema.columns
706
848
  WHERE table_schema = 'public'
707
849
  AND table_name = ${e.table}
@@ -713,11 +855,11 @@ var E = class extends Error {
713
855
  continue;
714
856
  }
715
857
  let r = t[0].column_name;
716
- await G(n, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table}" RENAME COLUMN "${r}" TO "${e.column}"`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
858
+ await Y(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table}" RENAME COLUMN "${r}" TO "${e.column}"`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
717
859
  }
718
860
  let c = 0;
719
861
  for (let e of o) {
720
- let t = await G(n, _.SqlClient.pipe(h.flatMap((t) => t`
862
+ let t = await Y(n, b.SqlClient.pipe(g.flatMap((t) => t`
721
863
  SELECT table_name FROM information_schema.tables
722
864
  WHERE table_schema = 'public'
723
865
  AND table_name LIKE ${e.table + "__dropped_%"}
@@ -728,57 +870,57 @@ var E = class extends Error {
728
870
  continue;
729
871
  }
730
872
  let r = t[0].table_name;
731
- await G(n, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${r}" RENAME TO "${e.table}"`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
873
+ await Y(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE "${r}" RENAME TO "${e.table}"`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
732
874
  }
733
875
  return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
734
- }, je = async (e) => {
735
- let t = c(e, "--before");
876
+ }, Ve = async (e) => {
877
+ let t = l(e, "--before");
736
878
  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;
737
879
  let n = t.replace(/[^0-9]/g, "");
738
880
  if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
739
- let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await V(), o = await G(a, _.SqlClient.pipe(h.flatMap((e) => e`
881
+ let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await G(), o = await Y(a, b.SqlClient.pipe(g.flatMap((e) => e`
740
882
  SELECT table_name, column_name FROM information_schema.columns
741
883
  WHERE table_schema = 'public' AND column_name LIKE ${"%__dropped_%"}
742
- `))), s = await G(a, _.SqlClient.pipe(h.flatMap((e) => e`
884
+ `))), s = await Y(a, b.SqlClient.pipe(g.flatMap((e) => e`
743
885
  SELECT table_name FROM information_schema.tables
744
886
  WHERE table_schema = 'public' AND table_name LIKE ${"%__dropped_%"}
745
- `))), l = (e) => {
887
+ `))), c = (e) => {
746
888
  let t = /__dropped_(\d{14})$/.exec(e);
747
889
  return t ? t[1] : null;
748
890
  }, u = 0;
749
891
  for (let e of o) {
750
- let t = l(e.column_name);
751
- t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await G(a, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table_name}" DROP COLUMN "${e.column_name}"`)))), u++);
892
+ let t = c(e.column_name);
893
+ t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await Y(a, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table_name}" DROP COLUMN "${e.column_name}"`)))), u++);
752
894
  }
753
895
  for (let e of s) {
754
- let t = l(e.table_name);
755
- t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await G(a, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`DROP TABLE "${e.table_name}"`)))), u++);
896
+ let t = c(e.table_name);
897
+ t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await Y(a, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`DROP TABLE "${e.table_name}"`)))), u++);
756
898
  }
757
899
  return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
758
- }, Me = async (e) => {
759
- let t = c(e, "--before");
900
+ }, He = async (e) => {
901
+ let t = l(e, "--before");
760
902
  if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
761
903
  let n = t;
762
904
  if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
763
- let r = c(e, "--note"), i = I(e), a = await Y(i);
764
- if (a === void 0) return P.warn("db squash: no schema files found", { root: i }), 1;
765
- let o = S(oe(a)), s = await V();
905
+ let r = l(e, "--note"), i = B(e), a = await Q(i);
906
+ if (a === void 0) return R.warn("db squash: no schema files found", { root: i }), 1;
907
+ let o = C(se(a)), s = await G();
766
908
  try {
767
- let e = await G(s, de({
909
+ let e = await Y(s, fe({
768
910
  before: n,
769
911
  fingerprint: o,
770
912
  appliedBy: process.env.USER ?? "cli",
771
913
  environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
772
914
  ...r === void 0 ? {} : { note: r }
773
915
  }));
774
- return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${T(e.snapshotFingerprint)}\n`), 0;
916
+ return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${D(e.snapshotFingerprint)}\n`), 0;
775
917
  } catch (e) {
776
918
  return console.error(`squash refused: ${e.message}`), 1;
777
919
  }
778
- }, Ne = async (e) => {
920
+ }, Ue = async (e) => {
779
921
  try {
780
- let t = S(await K(e));
781
- await G(e, _.SqlClient.pipe(h.flatMap((e) => e`
922
+ let t = C(await X(e));
923
+ await Y(e, b.SqlClient.pipe(g.flatMap((e) => e`
782
924
  UPDATE ${e("_voltro_migration_plans")}
783
925
  SET ${e("liveFingerprint")} = ${t}
784
926
  WHERE ${e("id")} = (
@@ -790,7 +932,7 @@ var E = class extends Error {
790
932
  AND ${e("liveFingerprint")} IS NULL
791
933
  `)));
792
934
  } catch {}
793
- }, X = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), Z = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), Q = (e) => e === void 0 ? {
935
+ }, We = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), $ = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), Ge = (e) => e === void 0 ? {
794
936
  accept: !1,
795
937
  reason: "no-declared-schema"
796
938
  } : e > 0 ? {
@@ -799,38 +941,38 @@ var E = class extends Error {
799
941
  } : {
800
942
  accept: !0,
801
943
  reason: "converged"
802
- }, Pe = async (e) => {
803
- let t = e.includes("--accept"), n = await V(), r = await G(n, _.SqlClient.pipe(h.flatMap((e) => e`
944
+ }, Ke = async (e) => {
945
+ let t = e.includes("--accept"), n = await G(), r = await Y(n, b.SqlClient.pipe(g.flatMap((e) => e`
804
946
  SELECT * FROM ${e("_voltro_migration_plans")}
805
947
  ORDER BY ${e("appliedAt")} DESC
806
948
  LIMIT 1
807
- `))), i = await K(n), a = S(i);
808
- if (r.length === 0) return P.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${T(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
949
+ `))), i = await X(n), a = C(i);
950
+ if (r.length === 0) return R.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${D(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
809
951
  let o = r[0];
810
- if (o.liveFingerprint === null || o.liveFingerprint === void 0) return P.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${Z(o.appliedAt)}`), console.log(` live now: ${T(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;
811
- if (o.liveFingerprint === a) return P.info(`db drift: live matches last applied (${T(a)})`), 0;
952
+ if (o.liveFingerprint === null || o.liveFingerprint === void 0) return R.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${$(o.appliedAt)}`), console.log(` live now: ${D(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;
953
+ if (o.liveFingerprint === a) return R.info(`db drift: live matches last applied (${D(a)})`), 0;
812
954
  let s = await (async () => {
813
955
  try {
814
- let t = await Y(I(e));
815
- return t === void 0 ? void 0 : w({
956
+ let t = await Q(B(e));
957
+ return t === void 0 ? void 0 : E({
816
958
  declared: t,
817
959
  live: i,
818
- dialect: B(),
819
- ...C()
960
+ dialect: W(),
961
+ ...w()
820
962
  }).operations;
821
963
  } catch {
822
964
  return;
823
965
  }
824
966
  })();
825
967
  if (t) {
826
- let e = Q(s?.length);
827
- if (e.reason === "no-declared-schema") return P.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
968
+ let e = Ge(s?.length);
969
+ if (e.reason === "no-declared-schema") return R.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
828
970
  if (!e.accept && s !== void 0) {
829
- P.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
971
+ R.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
830
972
  for (let e of s.slice(0, 10)) console.log(` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}`);
831
973
  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;
832
974
  }
833
- return await G(n, _.SqlClient.pipe(h.flatMap((e) => e`
975
+ return await Y(n, b.SqlClient.pipe(g.flatMap((e) => e`
834
976
  UPDATE ${e("_voltro_migration_plans")}
835
977
  SET ${e("liveFingerprint")} = ${a}
836
978
  WHERE ${e("id")} = (
@@ -839,31 +981,43 @@ var E = class extends Error {
839
981
  ORDER BY ${e("appliedAt")} DESC LIMIT 1
840
982
  ) AS ${e("newest")}
841
983
  )
842
- `))), P.info(`db drift: accepted the live schema as the baseline (${T(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;
984
+ `))), R.info(`db drift: accepted the live schema as the baseline (${D(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;
843
985
  }
844
- return P.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${T(o.liveFingerprint)} at ${Z(o.appliedAt)} (${o.id})`), console.log(` live now: ${T(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);
845
- }, Fe = async (e) => {
846
- let t = I(e), n = await V(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
847
- await G(n, x(v, B()));
848
- let i = await G(n, _.SqlClient.pipe(h.flatMap((e) => ue(e, {
986
+ return R.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${D(o.liveFingerprint)} at ${$(o.appliedAt)} (${o.id})`), console.log(` live now: ${D(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);
987
+ }, qe = async (e, t) => {
988
+ let n = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
989
+ try {
990
+ let r = await Y(t, b.SqlClient.pipe(g.flatMap((t) => de(t, {
991
+ projectRoot: e,
992
+ env: n,
993
+ appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
994
+ }))));
995
+ return r.applied.length > 0 && R.info(`db apply: applied ${r.applied.length} pending file-based migration(s) first`, { applied: r.applied.map((e) => `${e.id} (${e.durationMs}ms)`) }), { applied: r.applied.length };
996
+ } catch (e) {
997
+ return { failed: e };
998
+ }
999
+ }, Je = async (e) => {
1000
+ let t = B(e), n = await G(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
1001
+ await Y(n, S(x, W()));
1002
+ let i = await Y(n, b.SqlClient.pipe(g.flatMap((e) => de(e, {
849
1003
  projectRoot: t,
850
1004
  env: r,
851
1005
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
852
1006
  }))));
853
- return i.applied.length === 0 ? (P.info(`db files: no pending file-based migrations (${i.skipped.length} already applied)`), 0) : (P.info(`db files: applied ${i.applied.length} migration(s)`, {
1007
+ return i.applied.length === 0 ? (R.info(`db files: no pending file-based migrations (${i.skipped.length} already applied)`), 0) : (R.info(`db files: applied ${i.applied.length} migration(s)`, {
854
1008
  applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
855
1009
  skipped: i.skipped.length
856
1010
  }), 0);
857
- }, Ie = async (e) => {
858
- let t = I(e), n = e.find((e) => !e.startsWith("--"));
859
- if (!n) return P.error("voltro db rollback-file <migration-id>"), 2;
860
- if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return P.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step."), 3;
861
- let r = await G(await V(), _.SqlClient.pipe(h.flatMap((e) => le(e, {
1011
+ }, Ye = async (e) => {
1012
+ let t = B(e), n = e.find((e) => !e.startsWith("--"));
1013
+ if (!n) return R.error("voltro db rollback-file <migration-id>"), 2;
1014
+ if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return R.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step."), 3;
1015
+ let r = await Y(await G(), b.SqlClient.pipe(g.flatMap((e) => ue(e, {
862
1016
  projectRoot: t,
863
1017
  id: n
864
1018
  }))));
865
- return P.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
866
- }, Le = () => {
1019
+ return R.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
1020
+ }, Xe = () => {
867
1021
  let e = /* @__PURE__ */ new Date();
868
1022
  return [
869
1023
  e.getUTCFullYear(),
@@ -873,7 +1027,7 @@ var E = class extends Error {
873
1027
  String(e.getUTCMinutes()).padStart(2, "0"),
874
1028
  String(e.getUTCSeconds()).padStart(2, "0")
875
1029
  ].join("");
876
- }, Re = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", ze = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
1030
+ }, Ze = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", Qe = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
877
1031
  import { Effect } from 'effect'
878
1032
 
879
1033
  // A hand-written migration. Reach for this when the DECLARATIVE differ can't
@@ -905,36 +1059,63 @@ export default defineMigration({
905
1059
  }),
906
1060
  ],
907
1061
  })
908
- `, $ = {
909
- plan: Ee,
910
- apply: Oe,
911
- plans: ke,
912
- drift: Pe,
913
- files: Fe,
914
- "rollback-file": Ie,
915
- migrate: _e,
916
- rollback: ve,
917
- status: ye,
918
- seed: xe,
1062
+ `, $e = {
1063
+ plan: Ie,
1064
+ apply: Re,
1065
+ plans: ze,
1066
+ drift: Ke,
1067
+ files: Je,
1068
+ "rollback-file": Ye,
1069
+ migrate: Te,
1070
+ rollback: Ee,
1071
+ status: De,
1072
+ seed: ke,
919
1073
  generate: async (e) => {
920
- let { value: t, rest: n } = s(e, "--root"), { value: r, rest: i } = s(n, "--name"), a = t ? p(t) : process.cwd(), o = Re(r ?? l(i) ?? "migration"), c = `${Le()}_${o}`, u = d(a, "migrations"), m = d(u, `${c}.migration.ts`);
921
- return await g.mkdir(u, { recursive: !0 }), await g.writeFile(m, ze(c, o), "utf8"), P.info(`created ${f(a, m)}`), 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;
1074
+ let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o = Ze(r ?? u(i) ?? "migration"), s = `${Xe()}_${o}`, l = f(a, "migrations"), d = f(l, `${s}.migration.ts`);
1075
+ return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d, Qe(s, o), "utf8"), R.info(`created ${p(a, d)}`), console.error("Fill in the apply/undo steps, then run `voltro db migrate` (rollback with `voltro db rollback`)."), console.error("For a plain schema shape change you may not need this — `voltro db apply` diffs declared vs live."), 0;
922
1076
  },
923
- squash: Me,
924
- "restore-snapshot": Ae,
925
- "gc-snapshots": je
926
- }, Be = async (e) => {
1077
+ squash: He,
1078
+ "restore-snapshot": Be,
1079
+ "gc-snapshots": Ve,
1080
+ adopt: async (e) => {
1081
+ let t = (t) => {
1082
+ let n = e.indexOf(`--${t}`);
1083
+ return n === -1 ? void 0 : e[n + 1];
1084
+ }, n = t("from"), r = t("into"), i = t("map");
1085
+ if (n === void 0 || r === void 0 || i === 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;
1086
+ let o = (await a(m(process.cwd(), i))).default;
1087
+ if (o?.map === void 0) return console.error(`voltro db adopt: ${i} must default-export { map: { <targetColumn>: <source> } }`), 1;
1088
+ let s = {
1089
+ from: n,
1090
+ into: r,
1091
+ map: o.map,
1092
+ ...o.leaveUnset ? { leaveUnset: o.leaveUnset } : {}
1093
+ }, c = W(), l = await G(), u = await Y(l, b.SqlClient.pipe(g.flatMap(T))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
1094
+ name: e.name,
1095
+ nullable: e.nullable,
1096
+ hasDefault: e.defaultValue !== void 0 && e.defaultValue !== null
1097
+ })), f = d(n), p = d(r);
1098
+ if (f === void 0) return console.error(`voltro db adopt: '${n}' does not exist in the database`), 1;
1099
+ if (p === void 0) return console.error(`voltro db adopt: '${r}' does not exist in the database — boot once so the plugin's table is created`), 1;
1100
+ let h = async (e) => await Y(l, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), _ = async (e) => Number(Object.values((await h(`SELECT COUNT(*) AS n FROM "${e}"`))[0] ?? { n: 0 })[0] ?? 0), v = ge(s, f, p, await _(r));
1101
+ for (let e of ve(s, v, await _(n))) console.log(e);
1102
+ if (v.fatal) return 1;
1103
+ if (!e.includes("--apply")) return console.log(""), console.log(" (dry run — nothing written. Re-run with --apply.)"), 0;
1104
+ let y = await xe(s, v, c, "id", void 0, h, { keepSource: e.includes("--keep-source") });
1105
+ return console.log(""), console.log(` copied ${y.copied}/${y.sourceRows} row(s)`), console.log(` snapshot: ${y.snapshotTable} — restore from here, then drop it yourself`), y.verified ? (console.log(y.sourceDropped ? ` dropped ${n}` : ` ${n} left in place (--keep-source)`), 0) : (console.error(" ✗ counts DIFFER — the source was NOT dropped. Compare the two tables before doing anything else."), 1);
1106
+ }
1107
+ }, et = async (e) => {
927
1108
  let [t, ...n] = e;
928
1109
  if (!t) {
929
1110
  console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
930
- for (let e of Object.keys($)) console.error(` ${e}`);
1111
+ for (let e of Object.keys($e)) console.error(` ${e}`);
931
1112
  return 1;
932
1113
  }
933
- let r = $[t];
1114
+ let r = $e[t];
934
1115
  if (!r) return console.error(`voltro db: unknown subcommand '${t}'`), 2;
935
- (process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && P.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
1116
+ (process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && R.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
936
1117
  let i = (e) => {
937
- R(e) ? P.error(z) : console.error(e), process.exit(1);
1118
+ H(e) ? R.error(U) : console.error(e), process.exit(1);
938
1119
  };
939
1120
  process.on("uncaughtException", i);
940
1121
  try {
@@ -944,4 +1125,4 @@ export default defineMigration({
944
1125
  }
945
1126
  };
946
1127
  //#endregion
947
- export { U as a, E as c, Q as i, pe as l, L as n, R as o, H as r, Be as s, z as t };
1128
+ export { q as a, O as c, Ge as i, me as l, V as n, H as o, K as r, et as s, U as t };