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