@voltro/cli 0.23.0 → 0.25.0

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