@voltro/cli 0.29.0 → 0.30.1

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 (169) hide show
  1. package/CHANGELOG.md +448 -0
  2. package/THIRD-PARTY-NOTICES.md +2 -2
  3. package/dist/apiBuild-Ca6Sq5jy.js +2 -0
  4. package/dist/{apiBuild-BESZbTjA.js → apiBuild-DmkzRyv7.js} +2 -2
  5. package/dist/bin.js +3 -3
  6. package/dist/{checkCommand-BPAtyWs8.js → checkCommand-C4konUy9.js} +435 -304
  7. package/dist/checkCommand-DHD0aBeR.js +2 -0
  8. package/dist/{commands-82BDEktg.js → commands-BkJft9K2.js} +2413 -1814
  9. package/dist/{dbCommand-bCBbk6tz.js → dbCommand-qn__kLEt.js} +344 -265
  10. package/dist/dbCommand-wfGi3FgG.js +2 -0
  11. package/dist/{dev-Cg4BKLmi.js → dev-C_NPcLDp.js} +2716 -1782
  12. package/dist/{dev-DeEJj5T4.js → dev-DXhhZ7-f.js} +1 -1
  13. package/dist/frameworkTableAssembly-Bn_c4Mnl.js +2 -0
  14. package/dist/{frameworkTableAssembly-4Db40V23.js → frameworkTableAssembly-Cpgn2e88.js} +155 -141
  15. package/dist/index.d.ts +7 -2
  16. package/dist/index.js +1 -1
  17. package/dist/{inspect-BMvQpwqy.js → inspect-BUUjt773.js} +150 -14
  18. package/dist/inspect-mmBuRXmy.js +2 -0
  19. package/dist/{inspectMetrics-DHYygE4s.js → inspectMetrics-CkB2J0Zn.js} +566 -512
  20. package/dist/{manifestBuild-C4R1EdHn.js → manifestBuild-BLrVuSlM.js} +1 -1
  21. package/dist/manifestBuild-Dj8Jjoto.js +2 -0
  22. package/dist/{sdkgen-CW5NbYF0.js → sdkgen-wsWLrq4l.js} +1 -1
  23. package/dist/serveCommand-HtI4X5kj.js +1577 -0
  24. package/dist/serveEntry.js +2 -2
  25. package/dist/{start-CsCIaW4-.js → start-egkbpugc.js} +177 -175
  26. package/dist/startEntry.js +2 -2
  27. package/package.json +17 -17
  28. package/templates/AGENTS.core.md +50 -0
  29. package/templates/AGENTS.md +53 -2
  30. package/templates/agent-docs/_index.md +3 -2
  31. package/templates/agent-docs/_manifest.json +16 -4
  32. package/templates/agent-docs/ai.md +56 -0
  33. package/templates/agent-docs/cli.md +46 -0
  34. package/templates/agent-docs/internationalization.md +8 -7
  35. package/templates/agent-docs/local-first-mobile.md +18 -6
  36. package/templates/agent-docs/routing.md +15 -1
  37. package/templates/agent-docs/security.md +17 -0
  38. package/templates/agent-docs/templates/apibackends.md +209 -0
  39. package/templates/agent-docs/templates/appshells.md +144 -1
  40. package/templates/agent-docs/templates/mobile.md +60 -0
  41. package/templates/agent-docs/templates/overview.md +17 -7
  42. package/templates/agent-docs/whats-new.md +36 -243
  43. package/templates/agent-docs/workflows.md +367 -0
  44. package/templates/apps/api-ai/package.json +7 -7
  45. package/templates/apps/api-auth/package.json +8 -8
  46. package/templates/apps/api-backend/package.json +7 -7
  47. package/templates/apps/api-backend-deactivation/package.json +7 -7
  48. package/templates/apps/api-backend-mail/package.json +8 -8
  49. package/templates/apps/api-backend-mariadb/package.json +9 -9
  50. package/templates/apps/api-backend-sqlite/package.json +8 -8
  51. package/templates/apps/api-backend-storage/package.json +8 -8
  52. package/templates/apps/api-cms/package.json +10 -10
  53. package/templates/apps/api-collab/README.md +70 -0
  54. package/templates/apps/api-collab/app.config.ts +28 -0
  55. package/templates/apps/api-collab/database/schema.ts +63 -0
  56. package/templates/apps/api-collab/mutations/documents.create.mutation.server.ts +19 -0
  57. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +36 -0
  58. package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +20 -0
  59. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +30 -0
  60. package/templates/apps/api-collab/package.json +30 -0
  61. package/templates/apps/api-collab/queries/documents.query.server.ts +16 -0
  62. package/templates/apps/api-collab/queries/documents.query.ts +23 -0
  63. package/templates/apps/api-collab/template.json +6 -0
  64. package/templates/apps/api-collab/tests/documents.setBody.test.ts +89 -0
  65. package/templates/apps/api-collab/tsconfig.json +5 -0
  66. package/templates/apps/api-data-advanced/package.json +8 -8
  67. package/templates/apps/api-durable/package.json +8 -8
  68. package/templates/apps/api-feature-flags/package.json +9 -9
  69. package/templates/apps/api-governance/package.json +8 -8
  70. package/templates/apps/api-kv/package.json +8 -8
  71. package/templates/apps/api-moderation/package.json +8 -8
  72. package/templates/apps/api-observability/package.json +8 -8
  73. package/templates/apps/api-ratelimit/package.json +8 -8
  74. package/templates/apps/api-rbac/package.json +8 -8
  75. package/templates/apps/api-rest/package.json +7 -7
  76. package/templates/apps/api-saas/package.json +11 -11
  77. package/templates/apps/api-saas-starter/package.json +10 -10
  78. package/templates/apps/api-search/package.json +8 -8
  79. package/templates/apps/api-status/package.json +8 -8
  80. package/templates/apps/api-versioning/package.json +8 -8
  81. package/templates/apps/api-webhooks/package.json +9 -9
  82. package/templates/apps/changelog/package.json +6 -6
  83. package/templates/apps/edge-functions/package.json +2 -2
  84. package/templates/apps/frontend-admin/app.config.ts +1 -1
  85. package/templates/apps/frontend-admin/package.json +8 -8
  86. package/templates/apps/frontend-admin/src/locales/index.ts +1 -1
  87. package/templates/apps/frontend-app/app.config.ts +1 -1
  88. package/templates/apps/frontend-app/package.json +8 -8
  89. package/templates/apps/frontend-app/src/locales/index.ts +1 -1
  90. package/templates/apps/frontend-app/src/pages/layout.tsx +1 -1
  91. package/templates/apps/frontend-app/src/pages/page.tsx +1 -1
  92. package/templates/apps/frontend-auth/package.json +8 -8
  93. package/templates/apps/frontend-auth/src/locales/index.ts +1 -1
  94. package/templates/apps/frontend-blank/app.config.ts +1 -1
  95. package/templates/apps/frontend-blank/package.json +7 -7
  96. package/templates/apps/frontend-blank/src/locales/index.ts +1 -1
  97. package/templates/apps/frontend-blank/src/pages/layout.tsx +1 -1
  98. package/templates/apps/frontend-blank/src/pages/page.tsx +1 -1
  99. package/templates/apps/frontend-cms/package.json +9 -9
  100. package/templates/apps/frontend-cms/src/locales/index.ts +1 -1
  101. package/templates/apps/frontend-collab/README.md +80 -0
  102. package/templates/apps/frontend-collab/app.config.ts +38 -0
  103. package/templates/apps/frontend-collab/package.json +33 -0
  104. package/templates/apps/frontend-collab/src/locales/de.ts +29 -0
  105. package/templates/apps/frontend-collab/src/locales/en.ts +31 -0
  106. package/templates/apps/frontend-collab/src/locales/index.ts +15 -0
  107. package/templates/apps/frontend-collab/src/pages/layout.tsx +39 -0
  108. package/templates/apps/frontend-collab/src/pages/page.test.tsx +150 -0
  109. package/templates/apps/frontend-collab/src/pages/page.tsx +186 -0
  110. package/templates/apps/frontend-collab/template.json +6 -0
  111. package/templates/apps/frontend-collab/tsconfig.json +11 -0
  112. package/templates/apps/frontend-contact/package.json +7 -7
  113. package/templates/apps/frontend-dashboard/app.config.ts +1 -1
  114. package/templates/apps/frontend-dashboard/package.json +7 -7
  115. package/templates/apps/frontend-dashboard/src/locales/index.ts +1 -1
  116. package/templates/apps/frontend-docs/package.json +7 -7
  117. package/templates/apps/frontend-i18n/package.json +6 -6
  118. package/templates/apps/frontend-landing/package.json +7 -7
  119. package/templates/apps/frontend-portal/package.json +8 -8
  120. package/templates/apps/frontend-portal/src/locales/index.ts +1 -1
  121. package/templates/apps/frontend-saas/README.md +1 -1
  122. package/templates/apps/frontend-saas/app.config.ts +1 -1
  123. package/templates/apps/frontend-saas/package.json +8 -8
  124. package/templates/apps/frontend-saas/src/locales/index.ts +1 -1
  125. package/templates/apps/frontend-spa/app.config.ts +1 -1
  126. package/templates/apps/frontend-spa/package.json +7 -7
  127. package/templates/apps/frontend-spa/src/locales/index.ts +1 -1
  128. package/templates/apps/frontend-spa/src/pages/layout.tsx +1 -1
  129. package/templates/apps/frontend-spa/src/pages/page.tsx +1 -1
  130. package/templates/apps/frontend-ssr/app.config.ts +1 -1
  131. package/templates/apps/frontend-ssr/package.json +7 -7
  132. package/templates/apps/frontend-ssr/src/locales/index.ts +1 -1
  133. package/templates/apps/frontend-ssr/src/pages/layout.tsx +1 -1
  134. package/templates/apps/frontend-ssr/src/pages/page.tsx +1 -1
  135. package/templates/apps/frontend-ssr-api/app.config.ts +1 -1
  136. package/templates/apps/frontend-ssr-api/package.json +8 -8
  137. package/templates/apps/frontend-ssr-api/src/locales/index.ts +1 -1
  138. package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +1 -1
  139. package/templates/apps/frontend-static-blog/package.json +6 -6
  140. package/templates/apps/frontend-status/package.json +8 -8
  141. package/templates/apps/frontend-status/src/locales/index.ts +1 -1
  142. package/templates/apps/mobile-app/README.md +58 -0
  143. package/templates/apps/mobile-app/app.config.ts +33 -0
  144. package/templates/apps/mobile-app/babel.config.js +5 -0
  145. package/templates/apps/mobile-app/metro.config.js +20 -0
  146. package/templates/apps/mobile-app/package.json +33 -0
  147. package/templates/apps/mobile-app/src/app/_layout.tsx +61 -0
  148. package/templates/apps/mobile-app/src/app/index.tsx +62 -0
  149. package/templates/apps/mobile-app/src/app/orders/[id].tsx +19 -0
  150. package/templates/apps/mobile-app/src/app/settings.tsx +51 -0
  151. package/templates/apps/mobile-app/src/client.ts +74 -0
  152. package/templates/apps/mobile-app/src/lib/deeplinks.ts +39 -0
  153. package/templates/apps/mobile-app/src/lib/notifications.ts +31 -0
  154. package/templates/apps/mobile-app/src/lib/sync.ts +29 -0
  155. package/templates/apps/mobile-app/src/persistence.ts +38 -0
  156. package/templates/apps/mobile-app/template.json +6 -0
  157. package/templates/apps/mobile-app/tests/deeplinks.test.ts +32 -0
  158. package/templates/apps/mobile-app/tests/notifications.test.ts +26 -0
  159. package/templates/apps/mobile-app/tests/sync.test.ts +34 -0
  160. package/templates/apps/mobile-app/tsconfig.json +10 -0
  161. package/templates/apps/mobile-app/voltro.mobile.ts +11 -0
  162. package/templates/patches/@effect__cluster@0.60.0.patch +6 -6
  163. package/dist/apiBuild-C8d74T6g.js +0 -2
  164. package/dist/checkCommand-DBvZvCh1.js +0 -2
  165. package/dist/dbCommand-CaIVTp-a.js +0 -2
  166. package/dist/frameworkTableAssembly-BTNirAAk.js +0 -2
  167. package/dist/inspect-DHeaMZak.js +0 -2
  168. package/dist/manifestBuild-Ck_90gZy.js +0 -2
  169. package/dist/serveCommand-BXl8mhZD.js +0 -1489
@@ -1,6 +1,6 @@
1
1
  import { i as e, n as t, r as n, s as r, t as i } from "./seedRunner-Bqxgp7HZ.js";
2
2
  import { n as a } from "./appModuleLoader-C9r9mxZt.js";
3
- import { t as o } from "./frameworkTableAssembly-4Db40V23.js";
3
+ import { t as o } from "./frameworkTableAssembly-Cpgn2e88.js";
4
4
  import { b as s } from "./fileConventions-Cof68_BL.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";
@@ -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 { DEFAULT_CDC_CHANNEL as oe, applyPlan as S, applySchema as C, assessRollingDeployGate as se, declaredSnapshot as ce, destructiveScope as le, detectReactiveTriggerDrift as ue, fingerprintSchema as w, ignoreTablesFromEnv as T, introspectSchema as de, pendingFileMigrationIds as fe, planMigrations as E, reactiveTriggerRepairSql as pe, rollbackFileBasedMigration as me, rollingDeployUnsafeOps as he, runFileBasedMigrations as ge, shortFingerprint as D, squashMigrationPlans as _e, unblockLossy as ve } from "@voltro/database/sql";
13
+ import { actorsTable as te, frameworkTables as x, isMigrationDefinition as ne, quoteIdent as S, 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 ue, pendingFileMigrationIds as de, planMigrations as D, reactiveTriggerRepairSql as fe, rollbackFileBasedMigration as pe, rollingDeployUnsafeOps as me, runFileBasedMigrations as he, shortFingerprint as O, squashMigrationPlans as ge, unblockLossy as _e } from "@voltro/database/sql";
16
16
  //#region src/cliError.ts
17
- var ye = class extends Error {
17
+ var k = 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
- }, be = (e) => typeof e == "object" && !!e && e.isCliError === !0, xe = (e) => typeof e != "string", Se = (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 ye = class extends Error {
37
37
  });
38
38
  continue;
39
39
  }
40
- if (xe(n)) {
40
+ if (ye(n)) {
41
41
  c.push({
42
42
  target: t,
43
43
  source: n.expr,
@@ -80,23 +80,23 @@ var ye = class extends Error {
80
80
  remintIds: f,
81
81
  fatal: i.some((e) => e.fatal)
82
82
  };
83
- }, Ce = (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
- ], we = (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 Ce(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
- }, Te = (e) => e === "mysql" || e === "mariadb" ? (e) => `\`${e}\`` : e === "mssql" ? (e) => `[${e}]` : (e) => `"${e}"`, O = (e) => `${e}__adopt_snapshot`, Ee = (e, t, n, r, i) => {
99
- let a = Te(n), o = O(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}"`, we = (e) => `${e}__adopt_snapshot`, Te = (e, t, n, r, i) => {
99
+ let a = Ce(n), o = we(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 ye = class extends Error {
106
106
  drop: `DROP TABLE ${a(e.from)}`,
107
107
  dropSnapshot: `DROP TABLE ${a(o)}`
108
108
  };
109
- }, k = (e) => {
109
+ }, A = (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
- }, De = 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 = Ee(e, t, n, r, i), c = O(e.from), l = k(await a(s.verifySource));
116
+ let s = Te(e, t, n, r, i), c = we(e.from), l = A(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 = k(await a(s.verifyTarget));
120
+ let u = A(await a(s.verifyTarget));
121
121
  return u === l ? o.keepSource === !0 ? {
122
122
  copied: u,
123
123
  sourceRows: l,
@@ -137,7 +137,68 @@ var ye = class extends Error {
137
137
  snapshotTable: c,
138
138
  sourceDropped: !1
139
139
  };
140
- }, A = /\.migration\.ts$/, j = async (e, t) => {
140
+ }, De = [
141
+ "token",
142
+ "secret",
143
+ "password",
144
+ "apikey",
145
+ "api_key",
146
+ "credential",
147
+ "privatekey",
148
+ "private_key"
149
+ ], Oe = ["_voltro_audit_log", "_voltro_row_history"], ke = (e, t) => {
150
+ switch (t) {
151
+ case "postgres": return `LOWER(CAST(${e} AS text))`;
152
+ case "mysql":
153
+ case "mariadb": return `LOWER(CAST(${e} AS CHAR))`;
154
+ case "mssql": return `LOWER(CAST(${e} AS NVARCHAR(MAX)))`;
155
+ case "sqlite":
156
+ case "turso": return `LOWER(CAST(${e} AS TEXT))`;
157
+ }
158
+ }, Ae = (e, t, n = De) => n.map((n) => `${ke(e, t)} LIKE '%${n}%'`).join("\n OR "), je = async (e, t) => {
159
+ let n = await g.runPromise(e.unsafe(t));
160
+ return Number(Object.values(n[0] ?? { n: 0 })[0] ?? 0);
161
+ }, Me = async (e, t, n, r) => {
162
+ let i = [];
163
+ for (let a of n) {
164
+ let n = r(a.table), o;
165
+ try {
166
+ o = await je(e, `SELECT COUNT(*) AS n FROM ${n}`);
167
+ } catch {
168
+ i.push({
169
+ table: a.table,
170
+ column: a.column,
171
+ scanned: void 0,
172
+ hits: void 0
173
+ });
174
+ continue;
175
+ }
176
+ let s = await je(e, `SELECT COUNT(*) AS n FROM ${n}\n WHERE ${Ae(r(a.column), t)}`);
177
+ i.push({
178
+ table: a.table,
179
+ column: a.column,
180
+ scanned: o,
181
+ hits: s
182
+ });
183
+ }
184
+ return i;
185
+ }, Ne = (e, t) => {
186
+ let n = [`credential scan · dialect ${t}`, ""];
187
+ for (let t of e) {
188
+ if (t.scanned === void 0) {
189
+ n.push(` · ${t.table} — table not present (nothing scanned)`);
190
+ continue;
191
+ }
192
+ if (t.scanned === 0) {
193
+ n.push(` · ${t.table} — table is EMPTY (0 rows scanned; this is not a clean bill of health)`);
194
+ continue;
195
+ }
196
+ let e = (t.hits ?? 0) > 0 ? "✗" : "✓";
197
+ n.push(` ${e} ${t.table}.${t.column} — ${t.hits} of ${t.scanned} row(s) match a credential-shaped key`);
198
+ }
199
+ let r = e.reduce((e, t) => e + (t.hits ?? 0), 0);
200
+ return n.push(""), r > 0 ? n.push(` ${r} row(s) carry something credential-shaped.`, " Purge them AND rotate the credentials — assume anything written to a log", " table has been read. Then stop putting the credential on the Subject:", " `connectionCredentials(...)` keeps it in the framework vault.") : e.every((e) => e.scanned === void 0 || e.scanned === 0) ? n.push(" NOTHING WAS SCANNED. This run proves nothing about your data.") : n.push(" No credential-shaped keys found in the rows that exist."), n.push("", " Run this on EVERY environment. A dev database is not a sample of production."), n.join("\n");
201
+ }, Pe = (e) => e.some((e) => (e.hits ?? 0) > 0) ? 1 : e.every((e) => e.scanned === void 0 || e.scanned === 0) ? 2 : 0, j = /\.migration\.ts$/, M = async (e, t) => {
141
202
  let n = [];
142
203
  for (let r of e) {
143
204
  let e;
@@ -157,7 +218,7 @@ var ye = class extends Error {
157
218
  idInFile: i.id,
158
219
  idFromFile: d(r).replace(/\.migration\.ts$/, "")
159
220
  });
160
- let a = await v.readFile(r, "utf8").catch(() => ""), o = ae("sha256").update(a).digest("hex").slice(0, 16);
221
+ let a = await v.readFile(r, "utf8").catch(() => ""), o = ie("sha256").update(a).digest("hex").slice(0, 16);
161
222
  n.push({
162
223
  file: r,
163
224
  definition: i,
@@ -165,13 +226,13 @@ var ye = class extends Error {
165
226
  });
166
227
  }
167
228
  return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
168
- }, M = (e) => {
229
+ }, N = (e) => {
169
230
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t;
170
- return g.runPromise(C(x, n).pipe(g.provide(e)));
171
- }, N = (e) => g.runPromise(g.gen(function* () {
231
+ return g.runPromise(w(x, n).pipe(g.provide(e)));
232
+ }, P = (e) => g.runPromise(g.gen(function* () {
172
233
  let e = yield* (yield* b.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(g.orElseSucceed(() => []));
173
234
  return new Map(e.map((e) => [e.id, e]));
174
- }).pipe(g.provide(e))), P = (e, t) => g.runPromise(g.gen(function* () {
235
+ }).pipe(g.provide(e))), F = (e, t) => g.runPromise(g.gen(function* () {
175
236
  yield* (yield* b.SqlClient)`
176
237
  INSERT INTO "_voltro_migrations"
177
238
  (id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
@@ -187,14 +248,14 @@ var ye = class extends Error {
187
248
  "errorStack" = EXCLUDED."errorStack",
188
249
  "updatedAt" = now()
189
250
  `;
190
- }).pipe(g.provide(e))), Oe = async (e, t, n) => {
251
+ }).pipe(g.provide(e))), Fe = async (e, t, n) => {
191
252
  if (e.length === 0) return {
192
253
  applied: [],
193
254
  failed: null,
194
255
  skipped: []
195
256
  };
196
- await M(t);
197
- let r = await N(t), i = [], a = [], o = null;
257
+ await N(t);
258
+ let r = await P(t), i = [], a = [], o = null;
198
259
  for (let s of e) {
199
260
  let e = r.get(s.definition.id);
200
261
  if (e && e.status === "applied") {
@@ -215,7 +276,7 @@ var ye = class extends Error {
215
276
  name: s.definition.name
216
277
  });
217
278
  let c = Date.now(), l = [];
218
- await P(t, {
279
+ await F(t, {
219
280
  id: s.definition.id,
220
281
  name: s.definition.name,
221
282
  hash: s.hash,
@@ -256,7 +317,7 @@ var ye = class extends Error {
256
317
  ...a.stack ? { stack: a.stack } : {}
257
318
  }
258
319
  };
259
- o = f, i.push(f), await P(t, {
320
+ o = f, i.push(f), await F(t, {
260
321
  id: s.definition.id,
261
322
  name: s.definition.name,
262
323
  hash: s.hash,
@@ -275,7 +336,7 @@ var ye = class extends Error {
275
336
  status: "applied",
276
337
  durationMs: Date.now() - c,
277
338
  steps: l
278
- }), await P(t, {
339
+ }), await F(t, {
279
340
  id: s.definition.id,
280
341
  name: s.definition.name,
281
342
  hash: s.hash,
@@ -295,9 +356,9 @@ var ye = class extends Error {
295
356
  failed: o,
296
357
  skipped: a
297
358
  };
298
- }, ke = 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");
359
+ }, Ie = async (e, t, n, r = {}) => {
360
+ await N(t);
361
+ let i = await P(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
301
362
  if (a.length === 0) return n.info("migration: nothing to roll back"), {
302
363
  applied: [],
303
364
  failed: null,
@@ -350,7 +411,7 @@ var ye = class extends Error {
350
411
  status: "applied",
351
412
  durationMs: Date.now() - r,
352
413
  steps: i
353
- }), await P(t, {
414
+ }), await F(t, {
354
415
  id: e.definition.id,
355
416
  name: e.definition.name,
356
417
  hash: e.hash,
@@ -366,9 +427,9 @@ var ye = class extends Error {
366
427
  failed: c,
367
428
  skipped: []
368
429
  };
369
- }, F = async (e, t) => {
370
- await M(t);
371
- let n = await N(t);
430
+ }, I = async (e, t) => {
431
+ await N(t);
432
+ let n = await P(t);
372
433
  return e.map((e) => {
373
434
  let t = n.get(e.definition.id);
374
435
  return t ? {
@@ -385,7 +446,7 @@ var ye = class extends Error {
385
446
  hashMatches: !0
386
447
  };
387
448
  });
388
- }, I = (e) => {
449
+ }, L = (e) => {
389
450
  if (!e.failed) return "";
390
451
  let t = e.failed, n = [
391
452
  `✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
@@ -398,7 +459,7 @@ var ye = class extends Error {
398
459
  ""
399
460
  ];
400
461
  return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
401
- }, L = _({ scope: "voltro:db" }), Ae = /* @__PURE__ */ new Set([
462
+ }, R = _({ scope: "voltro:db" }), Le = /* @__PURE__ */ new Set([
402
463
  "node_modules",
403
464
  "dist",
404
465
  "build",
@@ -406,17 +467,17 @@ var ye = class extends Error {
406
467
  ".vite",
407
468
  ".next",
408
469
  ".git"
409
- ]), R = async (e, t) => {
470
+ ]), z = async (e, t) => {
410
471
  let n = [], r = async (e) => {
411
472
  let i = await v.readdir(e, { withFileTypes: !0 }).catch(() => []);
412
473
  for (let a of i) {
413
- if (Ae.has(a.name)) continue;
474
+ if (Le.has(a.name)) continue;
414
475
  let i = f(e, a.name);
415
476
  a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
416
477
  }
417
478
  };
418
479
  return await r(e), n;
419
- }, z = (e) => m(e.find((e) => !e.startsWith("-")) ?? process.cwd()), B = () => {
480
+ }, B = (e) => m(e.find((e) => !e.startsWith("-")) ?? process.cwd()), V = () => {
420
481
  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
482
  return a ? {
422
483
  url: a,
@@ -431,63 +492,63 @@ var ye = class extends Error {
431
492
  ...n,
432
493
  ...i
433
494
  };
434
- }, V = (e) => {
495
+ }, H = (e) => {
435
496
  let t = e;
436
497
  if (typeof t != "object" || !t) return !1;
437
498
  if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
438
499
  let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
439
500
  return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
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 = () => {
501
+ }, 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 = () => {
441
502
  let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
442
- if (e === "memory") throw new ye("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
503
+ if (e === "memory") throw new k("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
443
504
  return e;
444
- }, W = (e) => re(e, U()), G = (e) => e === "mysql" || e === "mariadb" ? "table_schema = DATABASE()" : e === "mssql" ? "table_schema = SCHEMA_NAME()" : "table_schema = 'public'", K = async () => (await r(U())).makeSqlLayer(B()), je = 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;
505
+ }, G = (e) => S(e, W()), K = (e) => e === "mysql" || e === "mariadb" ? "table_schema = DATABASE()" : e === "mssql" ? "table_schema = SCHEMA_NAME()" : "table_schema = 'public'", q = async () => (await r(W())).makeSqlLayer(V()), Re = async (e) => {
506
+ let t = B(e), n = e.includes("--dry-run"), r = await M(await z(t, j), R);
507
+ if (r.length === 0) return R.info("no migrations found", { root: t }), 0;
447
508
  if (n) {
448
509
  let e = /* @__PURE__ */ new Set();
449
510
  try {
450
- let t = await F(r, await K());
511
+ let t = await I(r, await q());
451
512
  e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
452
513
  } catch {
453
- L.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
514
+ R.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
454
515
  }
455
516
  let t = r.filter((t) => !e.has(t.definition.id));
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)`);
517
+ if (t.length === 0) return R.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
518
+ R.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
458
519
  for (let e of t) console.log(` pending ${e.definition.id}`);
459
520
  return 0;
460
521
  }
461
- let i = await Oe(r, await K(), L);
462
- return i.failed ? (process.stderr.write(I(i) + "\n"), 1) : (L.info("migrate complete", {
522
+ let i = await Fe(r, await q(), R);
523
+ return i.failed ? (process.stderr.write(L(i) + "\n"), 1) : (R.info("migrate complete", {
463
524
  applied: i.applied.length,
464
525
  skipped: i.skipped.length
465
526
  }), 0);
466
- }, Me = 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 ke(r, await K(), 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
- }, Ne = async (e) => {
472
- let t = await j(await R(z(e), A), L);
527
+ }, ze = async (e) => {
528
+ let t = B(e), n = l(e, "--to"), r = await M(await z(t, j), R);
529
+ if (r.length === 0) return R.info("no migrations found", { root: t }), 0;
530
+ let i = await Ie(r, await q(), R, n ? { to: n } : {});
531
+ return i.failed ? (process.stderr.write(L(i) + "\n"), 1) : (R.info("rollback complete", { reverted: i.applied.length }), 0);
532
+ }, Be = async (e) => {
533
+ let t = await M(await z(B(e), j), R);
473
534
  if (t.length === 0) return console.log("no migrations found"), 0;
474
- let n = await F(t, await K());
535
+ let n = await I(t, await q());
475
536
  for (let e of n) {
476
537
  let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
477
538
  console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
478
539
  }
479
540
  return 0;
480
- }, Pe = async (e) => {
541
+ }, Ve = async (e) => {
481
542
  let t = (l(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
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."), {
543
+ if (t === "memory") return R.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), R.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
483
544
  store: new ee(),
484
545
  dispose: async () => {},
485
546
  dialectId: "memory"
486
547
  };
487
548
  let n = await r(t);
488
- L.info(`voltro db seed: target store=${n.id} — rows will persist`);
549
+ R.info(`voltro db seed: target store=${n.id} — rows will persist`);
489
550
  let i = await n.makeStore({
490
- sqlLayer: n.makeSqlLayer(B()),
551
+ sqlLayer: n.makeSqlLayer(V()),
491
552
  changeStrategy: "inline"
492
553
  }), a = i;
493
554
  return {
@@ -497,77 +558,77 @@ var ye = class extends Error {
497
558
  },
498
559
  dialectId: n.id
499
560
  };
500
- }, Fe = 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 Pe(r);
561
+ }, He = async (r) => {
562
+ let a = B(r), o = l(r, "--id"), s = l(r, "--lifecycle"), c = await t(await z(a, i), R);
563
+ if (c.length === 0) return R.info("no seeds found", { root: a }), 0;
564
+ let { store: u, dispose: d, dialectId: f } = await Ve(r);
504
565
  try {
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) => ({
566
+ 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) => ({
506
567
  ...e,
507
568
  definition: {
508
569
  ...e.definition,
509
570
  lifecycle: "boot"
510
571
  }
511
- })), u, L, { force: !0 })).filter((e) => e.status === "failed").length > 0);
572
+ })), u, R, { force: !0 })).filter((e) => e.status === "failed").length > 0);
512
573
  } finally {
513
574
  await d().catch((e) => {
514
- L.warn("voltro db seed: store dispose failed", { error: String(e) });
575
+ R.warn("voltro db seed: store dispose failed", { error: String(e) });
515
576
  });
516
577
  }
517
- }, Ie = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", Le = async (e) => {
518
- let t = await R(e, s), n = [];
578
+ }, Ue = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", We = async (e) => {
579
+ let t = await z(e, s), n = [];
519
580
  for (let r of t) {
520
581
  let t;
521
582
  try {
522
583
  t = await import(h(r).href);
523
584
  } catch (t) {
524
- L.warn("db: failed to import schema file", {
585
+ R.warn("db: failed to import schema file", {
525
586
  file: p(e, r),
526
587
  error: String(t)
527
588
  });
528
589
  continue;
529
590
  }
530
- for (let e of Object.values(t)) Ie(e) && n.push(e);
591
+ for (let e of Object.values(t)) Ue(e) && n.push(e);
531
592
  }
532
593
  return n;
533
- }, Re = (e) => {
594
+ }, Ge = (e) => {
534
595
  let t = new Map(e.map((e) => [e.tableName, e]));
535
596
  t.has("actors") || t.set("actors", te);
536
597
  let n = t.get("actors"), r = t.get("tenants");
537
- return ie({
598
+ return re({
538
599
  actors: () => n,
539
600
  ...r === void 0 ? {} : { tenants: () => r }
540
601
  }), [...t.values()];
541
- }, ze = (e) => {
602
+ }, Ke = (e) => {
542
603
  let t = /* @__PURE__ */ new Map();
543
604
  for (let n of e) {
544
605
  let e = n.tableName;
545
606
  e !== void 0 && !t.has(e) && t.set(e, n);
546
607
  }
547
608
  return [...t.values()];
548
- }, Be = (e) => {
549
- let t = le(process.env.VOLTRO_DESTRUCTIVE_OK);
609
+ }, qe = (e) => {
610
+ let t = ce(process.env.VOLTRO_DESTRUCTIVE_OK);
550
611
  if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
551
612
  plan: e,
552
613
  acknowledged: !1
553
614
  };
554
- let n = ve(e, t);
615
+ let n = _e(e, t);
555
616
  return {
556
617
  plan: n,
557
618
  acknowledged: !n.operations.some((e) => e.blocked)
558
619
  };
559
- }, q = async (e, t) => g.runPromise(t.pipe(g.provide(e))), J = async (e) => {
620
+ }, J = async (e, t) => g.runPromise(t.pipe(g.provide(e))), Y = async (e) => {
560
621
  try {
561
- return await q(e, b.SqlClient.pipe(g.flatMap(de)));
622
+ return await J(e, b.SqlClient.pipe(g.flatMap(ue)));
562
623
  } catch (e) {
563
624
  let t = String(e?.message ?? e);
564
- if (V(e) || /connection error/i.test(t)) {
565
- let n = /* @__PURE__ */ Error(`${H}\n\nOriginal error: ${t}`);
625
+ if (H(e) || /connection error/i.test(t)) {
626
+ let n = /* @__PURE__ */ Error(`${U}\n\nOriginal error: ${t}`);
566
627
  throw n.cause = e, n;
567
628
  }
568
629
  throw e;
569
630
  }
570
- }, Ve = {
631
+ }, Je = {
571
632
  safe: "\x1B[32m✓\x1B[0m",
572
633
  "needs-default": "\x1B[32m⊕\x1B[0m",
573
634
  "needs-backfill": "\x1B[33m⊕\x1B[0m",
@@ -575,7 +636,7 @@ var ye = class extends Error {
575
636
  lossy: "\x1B[31m✗\x1B[0m",
576
637
  "online-required": "\x1B[36m◷\x1B[0m",
577
638
  "multi-step": "\x1B[35m⇉\x1B[0m"
578
- }, He = (e) => {
639
+ }, Ye = (e) => {
579
640
  let t = e.op, n;
580
641
  switch (t.kind) {
581
642
  case "create-table":
@@ -639,9 +700,9 @@ var ye = class extends Error {
639
700
  n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
640
701
  break;
641
702
  }
642
- let r = Ve[e.classification], i = e.reason ? ` # ${e.reason}` : "", a = e.blocked ? `\n ! ${e.blocked.fix}` : "";
703
+ let r = Je[e.classification], i = e.reason ? ` # ${e.reason}` : "", a = e.blocked ? `\n ! ${e.blocked.fix}` : "";
643
704
  return ` ${r} ${n}${i}${a}`;
644
- }, Ue = async (e, t) => {
705
+ }, Xe = async (e, t) => {
645
706
  let n = e.operations.filter((e) => e.classification === "needs-default" && e.op.kind === "add-column");
646
707
  if (n.length === 0) return;
647
708
  let r = [];
@@ -650,7 +711,7 @@ var ye = class extends Error {
650
711
  if (i === void 0 || a === void 0) continue;
651
712
  let o = 0;
652
713
  try {
653
- let e = await q(t, b.SqlClient.pipe(g.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${re(i, U())}`))));
714
+ let e = await J(t, b.SqlClient.pipe(g.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${S(i, W())}`))));
654
715
  o = Number(Object.values(e[0] ?? { n: 0 })[0] ?? 0);
655
716
  } catch {
656
717
  continue;
@@ -662,15 +723,15 @@ var ye = class extends Error {
662
723
  for (let e of r) console.log(e);
663
724
  console.log(" (the shape is correct either way — this is the VALUE a state diff cannot know)");
664
725
  }
665
- }, Y = async (e, t) => {
666
- if (U() !== "postgres") return;
667
- let n = oe, r;
726
+ }, X = async (e, t) => {
727
+ if (W() !== "postgres") return;
728
+ let n = ae, r;
668
729
  try {
669
- r = await q(t, b.SqlClient.pipe(g.flatMap((t) => ue(t, e, n))));
730
+ r = await J(t, b.SqlClient.pipe(g.flatMap((t) => le(t, e, n))));
670
731
  } catch {
671
732
  return;
672
733
  }
673
- let i = pe({
734
+ let i = fe({
674
735
  tables: e,
675
736
  dialect: "postgres",
676
737
  missing: r.missing,
@@ -678,53 +739,53 @@ var ye = class extends Error {
678
739
  channel: n
679
740
  });
680
741
  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)` : ""));
742
+ R.info(`db apply: installing change triggers on ${r.missing.length} table(s)` + (r.stale.length > 0 ? `, removing ${r.stale.length} stale one(s)` : ""));
682
743
  let a = 0;
683
- for (let e of i) await q(t, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), a += 1;
684
- L.info(`db apply: change triggers converged (${a} statement(s))`, {
744
+ for (let e of i) await J(t, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), a += 1;
745
+ R.info(`db apply: change triggers converged (${a} statement(s))`, {
685
746
  installed: r.missing.length,
686
747
  removed: r.stale.length
687
748
  });
688
- }, X = (e) => {
749
+ }, Z = (e) => {
689
750
  let t = e.summary, n = e.operations.length;
690
751
  if (console.log(`\nschema diff: ${n} operation${n === 1 ? "" : "s"}, ${t.blocked} blocked\n`), n === 0) {
691
752
  console.log(" (schema is up to date)\n");
692
753
  return;
693
754
  }
694
- for (let t of e.operations) console.log(He(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`), We(e);
696
- }, We = (e) => {
697
- let t = he(e.operations);
755
+ for (let t of e.operations) console.log(Ye(t));
756
+ 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`), Ze(e);
757
+ }, Ze = (e) => {
758
+ let t = me(e.operations);
698
759
  if (t.length !== 0) {
699
760
  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
761
  for (let e of t) console.log(` • ${e.op.op.kind}: ${e.reason}`), console.log(` → ${e.remedy}`);
701
762
  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
763
  }
703
- }, Ge = (e, t) => {
704
- let n = se({
764
+ }, Qe = (e, t) => {
765
+ let n = oe({
705
766
  enabled: process.env.VOLTRO_ROLLING_DEPLOY === "1",
706
767
  forced: t.includes("--force"),
707
- unsafeCount: he(e.operations).length
768
+ unsafeCount: me(e.operations).length
708
769
  });
709
- return n.refuse ? (L.error(`db apply: ${n.message}`), !0) : (n.warnForced && L.warn(`db apply: ${n.message}`), !1);
710
- }, Ke = (e) => {
770
+ return n.refuse ? (R.error(`db apply: ${n.message}`), !0) : (n.warnForced && R.warn(`db apply: ${n.message}`), !1);
771
+ }, $e = (e) => {
711
772
  let t = l(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
712
773
  if (t === void 0) return;
713
774
  let n = Number(t);
714
775
  if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
715
776
  return n;
716
- }, Z = async (e) => {
717
- let t = await Le(e);
777
+ }, Q = async (e) => {
778
+ let t = await We(e);
718
779
  if (t.length === 0) return;
719
780
  let n = await o({ root: e });
720
- return Re(ze([
781
+ return Ge(Ke([
721
782
  ...t,
722
783
  ...x,
723
784
  ...n
724
785
  ]));
725
- }, qe = async (e) => {
726
- let t = Ke(e), n = z(e), r = await Z(n);
727
- if (r === void 0) return L.warn("db plan: no schema files found", {
786
+ }, et = async (e) => {
787
+ let t = $e(e), n = B(e), r = await Q(n);
788
+ if (r === void 0) return R.warn("db plan: no schema files found", {
728
789
  root: n,
729
790
  hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
730
791
  }), 1;
@@ -734,115 +795,115 @@ var ye = class extends Error {
734
795
  a && (o.authorization = `Bearer ${a}`);
735
796
  try {
736
797
  let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers: o });
737
- if (!i.ok) return L.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
798
+ if (!i.ok) return R.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
738
799
  let a = (await i.json()).drift?.liveSnapshot;
739
- if (!a) return L.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
740
- let s = E({
800
+ if (!a) return R.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
801
+ let s = D({
741
802
  declared: r,
742
803
  live: a,
743
- dialect: U(),
744
- ...T(),
804
+ dialect: W(),
805
+ ...E(),
745
806
  ...t === void 0 ? {} : { onlineAfter: t }
746
807
  });
747
- return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), X(s), s.summary.blocked > 0 ? 2 : 0;
808
+ return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), Z(s), s.summary.blocked > 0 ? 2 : 0;
748
809
  } catch (e) {
749
- return L.error(`db plan --against: ${e.message}`), 1;
810
+ return R.error(`db plan --against: ${e.message}`), 1;
750
811
  }
751
812
  }
752
- let a = E({
813
+ let a = D({
753
814
  declared: r,
754
- live: await J(await K()),
755
- dialect: U(),
756
- ...T(),
815
+ live: await Y(await q()),
816
+ dialect: W(),
817
+ ...E(),
757
818
  ...t === void 0 ? {} : { onlineAfter: t }
758
819
  });
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
- }, Je = async (e, t, n) => {
820
+ 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);
821
+ }, tt = async (e, t, n) => {
761
822
  let r;
762
823
  try {
763
824
  let t = await v.readFile(m(e), "utf8");
764
825
  r = JSON.parse(t);
765
826
  } catch (t) {
766
- return L.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
827
+ return R.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
767
828
  }
768
- 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;
769
- let i = await Z(n);
770
- if (i === void 0) return L.warn("db apply --plan: no schema files found", { root: n }), 1;
771
- let a = await K();
772
- await q(a, C(x, U()));
773
- let o = await q(a, b.SqlClient.pipe(g.flatMap((e) => fe(e, n)))).catch(() => []);
774
- 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;
775
- let s = E({
829
+ 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;
830
+ let i = await Q(n);
831
+ if (i === void 0) return R.warn("db apply --plan: no schema files found", { root: n }), 1;
832
+ let a = await q();
833
+ await J(a, w(x, W()));
834
+ let o = await J(a, b.SqlClient.pipe(g.flatMap((e) => de(e, n)))).catch(() => []);
835
+ 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;
836
+ let s = D({
776
837
  declared: i,
777
- live: await J(a),
778
- dialect: U(),
779
- ...T()
838
+ live: await Y(a),
839
+ dialect: W(),
840
+ ...E()
780
841
  });
781
- 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;
782
- 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;
783
- let c = Be(s), u = c.plan;
784
- if (u.summary.blocked > 0 && !c.acknowledged) return X(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;
785
- if (c.acknowledged && L.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), Ge(u, t)) return 2;
786
- if (u.operations.length === 0) return L.info("db apply --plan: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), 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) => S(e, u, {
842
+ 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;
843
+ 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;
844
+ let c = qe(s), u = c.plan;
845
+ 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;
846
+ if (c.acknowledged && R.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), Qe(u, t)) return 2;
847
+ if (u.operations.length === 0) return R.info("db apply --plan: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), await X(i, a), 0;
848
+ Z(u);
849
+ let d = (process.env.NODE_ENV ?? "dev").toLowerCase(), f = l(t, "--note"), p = await J(a, b.SqlClient.pipe(g.flatMap((e) => C(e, u, {
789
850
  declared: i,
790
851
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
791
852
  environment: d === "production" ? "prod" : d === "staging" ? "staging" : "dev",
792
853
  source: "file",
793
854
  ...f ? { notes: f } : {},
794
- replan: () => g.promise(() => J(a)).pipe(g.map((e) => E({
855
+ replan: () => g.promise(() => Y(a)).pipe(g.map((e) => D({
795
856
  declared: i,
796
857
  live: e,
797
- dialect: U(),
798
- ...T()
858
+ dialect: W(),
859
+ ...E()
799
860
  })))
800
861
  }))));
801
- return L.info(`db apply --plan: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
862
+ return R.info(`db apply --plan: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
802
863
  id: p.id,
803
864
  fingerprint: p.fingerprint.slice(0, 16)
804
- }), await Y(i, a), 0;
805
- }, Ye = async (e) => {
806
- let t = z(e), n = Ke(e), r = l(e, "--plan");
807
- if (r) return Je(r, e, t);
808
- 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;
809
- let i = await Z(t);
810
- if (i === void 0) return L.warn("db apply: no schema files found", { root: t }), 1;
811
- let a = await K(), o = await it(t, a);
812
- 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;
813
- await q(a, C(x, U()));
814
- let s = E({
865
+ }), await X(i, a), 0;
866
+ }, nt = async (e) => {
867
+ let t = B(e), n = $e(e), r = l(e, "--plan");
868
+ if (r) return tt(r, e, t);
869
+ 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;
870
+ let i = await Q(t);
871
+ if (i === void 0) return R.warn("db apply: no schema files found", { root: t }), 1;
872
+ let a = await q(), o = await dt(t, a);
873
+ 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;
874
+ await J(a, w(x, W()));
875
+ let s = D({
815
876
  declared: i,
816
- live: await J(a),
817
- dialect: U(),
818
- ...T(),
877
+ live: await Y(a),
878
+ dialect: W(),
879
+ ...E(),
819
880
  ...n === void 0 ? {} : { onlineAfter: n }
820
881
  });
821
- X(s), await Ue(s, a);
822
- let c = Be(s), u = c.plan;
823
- 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;
824
- if (c.acknowledged && L.warn(`db apply: proceeding with ${s.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), Ge(u, e)) return 2;
825
- if (u.operations.length === 0) return L.info("db apply: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), await et(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 Y(i, a), 0;
826
- let d = l(e, "--note"), f = await q(a, b.SqlClient.pipe(g.flatMap((e) => S(e, u, {
882
+ Z(s), await Xe(s, a);
883
+ let c = qe(s), u = c.plan;
884
+ 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;
885
+ if (c.acknowledged && R.warn(`db apply: proceeding with ${s.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), Qe(u, e)) return 2;
886
+ if (u.operations.length === 0) return R.info("db apply: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), await st(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`.")), await X(i, a), 0;
887
+ let d = l(e, "--note"), f = await J(a, b.SqlClient.pipe(g.flatMap((e) => C(e, u, {
827
888
  declared: i,
828
889
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
829
890
  environment: (process.env.NODE_ENV ?? "dev").toLowerCase() === "staging" ? "staging" : "dev",
830
891
  source: "auto-diff",
831
892
  ...d ? { notes: d } : {},
832
- replan: () => g.promise(() => J(a)).pipe(g.map((e) => E({
893
+ replan: () => g.promise(() => Y(a)).pipe(g.map((e) => D({
833
894
  declared: i,
834
895
  live: e,
835
- dialect: U(),
836
- ...T(),
896
+ dialect: W(),
897
+ ...E(),
837
898
  ...n === void 0 ? {} : { onlineAfter: n }
838
899
  })))
839
900
  }))));
840
- return L.info(`db apply: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
901
+ return R.info(`db apply: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
841
902
  id: f.id,
842
903
  fingerprint: f.fingerprint.slice(0, 16)
843
- }), await Y(i, a), 0;
844
- }, Xe = 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`
904
+ }), await X(i, a), 0;
905
+ }, rt = async (e) => {
906
+ let t = Math.max(1, Number(l(e, "--limit") ?? "20")), n = await J(await q(), b.SqlClient.pipe(g.flatMap((e) => e`
846
907
  SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")},
847
908
  ${e("appliedBy")}, ${e("environment")}, ${e("source")},
848
909
  ${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}
@@ -850,25 +911,25 @@ var ye = class extends Error {
850
911
  ORDER BY ${e("appliedAt")} DESC
851
912
  LIMIT ${t}
852
913
  `)));
853
- 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;
914
+ 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;
854
915
  console.log(`\n${n.length} plan${n.length === 1 ? "" : "s"} (newest first):\n`);
855
916
  for (let e of n) {
856
917
  let t = 0;
857
918
  try {
858
- t = JSON.parse(tt(e.operations)).length;
919
+ t = JSON.parse(ct(e.operations)).length;
859
920
  } catch {}
860
- console.log(` ${e.id} fp=${D(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`);
921
+ console.log(` ${e.id} fp=${O(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`);
861
922
  }
862
923
  return console.log(), 0;
863
- }, Ze = async (e) => {
924
+ }, it = async (e) => {
864
925
  let t = e[0];
865
926
  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;
866
- let n = await K(), r = await q(n, b.SqlClient.pipe(g.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
927
+ let n = await q(), r = await J(n, b.SqlClient.pipe(g.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
867
928
  FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
868
929
  if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
869
930
  let i;
870
931
  try {
871
- i = JSON.parse(tt(r[0].operations));
932
+ i = JSON.parse(ct(r[0].operations));
872
933
  } catch {
873
934
  return console.error(`restore-snapshot: plan '${t}' has corrupted operations JSON`), 1;
874
935
  }
@@ -882,9 +943,9 @@ var ye = class extends Error {
882
943
  if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
883
944
  let s = 0;
884
945
  for (let e of a) {
885
- let t = await q(n, b.SqlClient.pipe(g.flatMap((t) => t`
946
+ let t = await J(n, b.SqlClient.pipe(g.flatMap((t) => t`
886
947
  SELECT column_name FROM information_schema.columns
887
- WHERE ${t.unsafe(G(U()))}
948
+ WHERE ${t.unsafe(K(W()))}
888
949
  AND table_name = ${e.table}
889
950
  AND column_name LIKE ${e.column + "__dropped_%"}
890
951
  ORDER BY column_name DESC LIMIT 1
@@ -894,13 +955,13 @@ var ye = class extends Error {
894
955
  continue;
895
956
  }
896
957
  let r = t[0].column_name;
897
- await q(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${W(e.table)} RENAME COLUMN ${W(r)} TO ${W(e.column)}`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
958
+ await J(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table)} RENAME COLUMN ${G(r)} TO ${G(e.column)}`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
898
959
  }
899
960
  let c = 0;
900
961
  for (let e of o) {
901
- let t = await q(n, b.SqlClient.pipe(g.flatMap((t) => t`
962
+ let t = await J(n, b.SqlClient.pipe(g.flatMap((t) => t`
902
963
  SELECT table_name FROM information_schema.tables
903
- WHERE ${t.unsafe(G(U()))}
964
+ WHERE ${t.unsafe(K(W()))}
904
965
  AND table_name LIKE ${e.table + "__dropped_%"}
905
966
  ORDER BY table_name DESC LIMIT 1
906
967
  `)));
@@ -909,57 +970,57 @@ var ye = class extends Error {
909
970
  continue;
910
971
  }
911
972
  let r = t[0].table_name;
912
- await q(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${W(r)} RENAME TO ${W(e.table)}`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
973
+ await J(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${G(r)} RENAME TO ${G(e.table)}`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
913
974
  }
914
975
  return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
915
- }, Qe = async (e) => {
976
+ }, at = async (e) => {
916
977
  let t = l(e, "--before");
917
978
  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;
918
979
  let n = t.replace(/[^0-9]/g, "");
919
980
  if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
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`
981
+ let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await q(), o = await J(a, b.SqlClient.pipe(g.flatMap((e) => e`
921
982
  SELECT table_name, column_name FROM information_schema.columns
922
- WHERE ${e.unsafe(G(U()))} AND column_name LIKE ${"%__dropped_%"}
923
- `))), s = await q(a, b.SqlClient.pipe(g.flatMap((e) => e`
983
+ WHERE ${e.unsafe(K(W()))} AND column_name LIKE ${"%__dropped_%"}
984
+ `))), s = await J(a, b.SqlClient.pipe(g.flatMap((e) => e`
924
985
  SELECT table_name FROM information_schema.tables
925
- WHERE ${e.unsafe(G(U()))} AND table_name LIKE ${"%__dropped_%"}
986
+ WHERE ${e.unsafe(K(W()))} AND table_name LIKE ${"%__dropped_%"}
926
987
  `))), c = (e) => {
927
988
  let t = /__dropped_(\d{14})$/.exec(e);
928
989
  return t ? t[1] : null;
929
990
  }, u = 0;
930
991
  for (let e of o) {
931
992
  let t = c(e.column_name);
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 ${W(e.table_name)} DROP COLUMN ${W(e.column_name)}`)))), u++);
993
+ 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 ${G(e.table_name)} DROP COLUMN ${G(e.column_name)}`)))), u++);
933
994
  }
934
995
  for (let e of s) {
935
996
  let t = c(e.table_name);
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 ${W(e.table_name)}`)))), u++);
997
+ 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 ${G(e.table_name)}`)))), u++);
937
998
  }
938
999
  return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
939
- }, $e = async (e) => {
1000
+ }, ot = async (e) => {
940
1001
  let t = l(e, "--before");
941
1002
  if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
942
1003
  let n = t;
943
1004
  if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
944
- let r = l(e, "--note"), i = z(e), a = await Z(i);
945
- if (a === void 0) return L.warn("db squash: no schema files found", { root: i }), 1;
946
- let o = w(ce(a)), s = await K();
1005
+ let r = l(e, "--note"), i = B(e), a = await Q(i);
1006
+ if (a === void 0) return R.warn("db squash: no schema files found", { root: i }), 1;
1007
+ let o = T(se(a)), s = await q();
947
1008
  try {
948
- let e = await q(s, _e({
1009
+ let e = await J(s, ge({
949
1010
  before: n,
950
1011
  fingerprint: o,
951
1012
  appliedBy: process.env.USER ?? "cli",
952
1013
  environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
953
1014
  ...r === void 0 ? {} : { note: r }
954
1015
  }));
955
- return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${D(e.snapshotFingerprint)}\n`), 0;
1016
+ return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${O(e.snapshotFingerprint)}\n`), 0;
956
1017
  } catch (e) {
957
1018
  return console.error(`squash refused: ${e.message}`), 1;
958
1019
  }
959
- }, et = async (e) => {
1020
+ }, st = async (e) => {
960
1021
  try {
961
- let t = w(await J(e));
962
- await q(e, b.SqlClient.pipe(g.flatMap((e) => e`
1022
+ let t = T(await Y(e));
1023
+ await J(e, b.SqlClient.pipe(g.flatMap((e) => e`
963
1024
  UPDATE ${e("_voltro_migration_plans")}
964
1025
  SET ${e("liveFingerprint")} = ${t}
965
1026
  WHERE ${e("id")} = (
@@ -971,7 +1032,7 @@ var ye = class extends Error {
971
1032
  AND ${e("liveFingerprint")} IS NULL
972
1033
  `)));
973
1034
  } catch {}
974
- }, tt = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), Q = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), nt = (e) => e === void 0 ? {
1035
+ }, ct = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), $ = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), lt = (e) => e === void 0 ? {
975
1036
  accept: !1,
976
1037
  reason: "no-declared-schema"
977
1038
  } : e > 0 ? {
@@ -980,38 +1041,38 @@ var ye = class extends Error {
980
1041
  } : {
981
1042
  accept: !0,
982
1043
  reason: "converged"
983
- }, rt = async (e) => {
984
- let t = e.includes("--accept"), n = await K(), r = await q(n, b.SqlClient.pipe(g.flatMap((e) => e`
1044
+ }, ut = async (e) => {
1045
+ let t = e.includes("--accept"), n = await q(), r = await J(n, b.SqlClient.pipe(g.flatMap((e) => e`
985
1046
  SELECT * FROM ${e("_voltro_migration_plans")}
986
1047
  ORDER BY ${e("appliedAt")} DESC
987
1048
  LIMIT 1
988
- `))), i = await J(n), a = w(i);
989
- 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;
1049
+ `))), i = await Y(n), a = T(i);
1050
+ if (r.length === 0) return R.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${O(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
990
1051
  let o = r[0];
991
- 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 ${Q(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;
992
- if (o.liveFingerprint === a) return L.info(`db drift: live matches last applied (${D(a)})`), 0;
1052
+ 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: ${O(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;
1053
+ if (o.liveFingerprint === a) return R.info(`db drift: live matches last applied (${O(a)})`), 0;
993
1054
  let s = await (async () => {
994
1055
  try {
995
- let t = await Z(z(e));
996
- return t === void 0 ? void 0 : E({
1056
+ let t = await Q(B(e));
1057
+ return t === void 0 ? void 0 : D({
997
1058
  declared: t,
998
1059
  live: i,
999
- dialect: U(),
1000
- ...T()
1060
+ dialect: W(),
1061
+ ...E()
1001
1062
  }).operations;
1002
1063
  } catch {
1003
1064
  return;
1004
1065
  }
1005
1066
  })();
1006
1067
  if (t) {
1007
- let e = nt(s?.length);
1008
- if (e.reason === "no-declared-schema") return L.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
1068
+ let e = lt(s?.length);
1069
+ if (e.reason === "no-declared-schema") return R.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
1009
1070
  if (!e.accept && s !== void 0) {
1010
- L.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
1071
+ R.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
1011
1072
  for (let e of s.slice(0, 10)) console.log(` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}`);
1012
1073
  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;
1013
1074
  }
1014
- return await q(n, b.SqlClient.pipe(g.flatMap((e) => e`
1075
+ return await J(n, b.SqlClient.pipe(g.flatMap((e) => e`
1015
1076
  UPDATE ${e("_voltro_migration_plans")}
1016
1077
  SET ${e("liveFingerprint")} = ${a}
1017
1078
  WHERE ${e("id")} = (
@@ -1020,43 +1081,43 @@ var ye = class extends Error {
1020
1081
  ORDER BY ${e("appliedAt")} DESC LIMIT 1
1021
1082
  ) AS ${e("newest")}
1022
1083
  )
1023
- `))), 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;
1084
+ `))), R.info(`db drift: accepted the live schema as the baseline (${O(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;
1024
1085
  }
1025
- return L.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${D(o.liveFingerprint)} at ${Q(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);
1026
- }, it = async (e, t) => {
1086
+ return R.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${O(o.liveFingerprint)} at ${$(o.appliedAt)} (${o.id})`), console.log(` live now: ${O(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);
1087
+ }, dt = async (e, t) => {
1027
1088
  let n = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
1028
1089
  try {
1029
- let r = await q(t, b.SqlClient.pipe(g.flatMap((t) => ge(t, {
1090
+ let r = await J(t, b.SqlClient.pipe(g.flatMap((t) => he(t, {
1030
1091
  projectRoot: e,
1031
1092
  env: n,
1032
1093
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
1033
1094
  }))));
1034
- 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 };
1095
+ 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 };
1035
1096
  } catch (e) {
1036
1097
  return { failed: e };
1037
1098
  }
1038
- }, at = async (e) => {
1039
- let t = z(e), n = await K(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
1040
- await q(n, C(x, U()));
1041
- let i = await q(n, b.SqlClient.pipe(g.flatMap((e) => ge(e, {
1099
+ }, ft = async (e) => {
1100
+ let t = B(e), n = await q(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
1101
+ await J(n, w(x, W()));
1102
+ let i = await J(n, b.SqlClient.pipe(g.flatMap((e) => he(e, {
1042
1103
  projectRoot: t,
1043
1104
  env: r,
1044
1105
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
1045
1106
  }))));
1046
- 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)`, {
1107
+ 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)`, {
1047
1108
  applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
1048
1109
  skipped: i.skipped.length
1049
1110
  }), 0);
1050
- }, ot = async (e) => {
1051
- let t = z(e), n = e.find((e) => !e.startsWith("--"));
1052
- if (!n) return L.error("voltro db rollback-file <migration-id>"), 2;
1053
- 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;
1054
- let r = await q(await K(), b.SqlClient.pipe(g.flatMap((e) => me(e, {
1111
+ }, pt = async (e) => {
1112
+ let t = B(e), n = e.find((e) => !e.startsWith("--"));
1113
+ if (!n) return R.error("voltro db rollback-file <migration-id>"), 2;
1114
+ 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;
1115
+ let r = await J(await q(), b.SqlClient.pipe(g.flatMap((e) => pe(e, {
1055
1116
  projectRoot: t,
1056
1117
  id: n
1057
1118
  }))));
1058
- return L.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
1059
- }, st = () => {
1119
+ return R.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
1120
+ }, mt = () => {
1060
1121
  let e = /* @__PURE__ */ new Date();
1061
1122
  return [
1062
1123
  e.getUTCFullYear(),
@@ -1066,7 +1127,7 @@ var ye = class extends Error {
1066
1127
  String(e.getUTCMinutes()).padStart(2, "0"),
1067
1128
  String(e.getUTCSeconds()).padStart(2, "0")
1068
1129
  ].join("");
1069
- }, ct = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", lt = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
1130
+ }, ht = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", gt = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
1070
1131
  import { Effect } from 'effect'
1071
1132
 
1072
1133
  // A hand-written migration. Reach for this when the DECLARATIVE differ can't
@@ -1098,24 +1159,24 @@ export default defineMigration({
1098
1159
  }),
1099
1160
  ],
1100
1161
  })
1101
- `, $ = {
1102
- plan: qe,
1103
- apply: Ye,
1104
- plans: Xe,
1105
- drift: rt,
1106
- files: at,
1107
- "rollback-file": ot,
1108
- migrate: je,
1109
- rollback: Me,
1110
- status: Ne,
1111
- seed: Fe,
1162
+ `, _t = {
1163
+ plan: et,
1164
+ apply: nt,
1165
+ plans: rt,
1166
+ drift: ut,
1167
+ files: ft,
1168
+ "rollback-file": pt,
1169
+ migrate: Re,
1170
+ rollback: ze,
1171
+ status: Be,
1172
+ seed: He,
1112
1173
  generate: async (e) => {
1113
- let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o = ct(r ?? u(i) ?? "migration"), s = `${st()}_${o}`, l = f(a, "migrations"), d = f(l, `${s}.migration.ts`);
1114
- return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d, lt(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;
1174
+ let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o = ht(r ?? u(i) ?? "migration"), s = `${mt()}_${o}`, l = f(a, "migrations"), d = f(l, `${s}.migration.ts`);
1175
+ return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d, gt(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;
1115
1176
  },
1116
- squash: $e,
1117
- "restore-snapshot": Ze,
1118
- "gc-snapshots": Qe,
1177
+ squash: ot,
1178
+ "restore-snapshot": it,
1179
+ "gc-snapshots": at,
1119
1180
  adopt: async (e) => {
1120
1181
  let t = (t) => {
1121
1182
  let n = e.indexOf(`--${t}`);
@@ -1129,32 +1190,50 @@ export default defineMigration({
1129
1190
  into: r,
1130
1191
  map: o.map,
1131
1192
  ...o.leaveUnset ? { leaveUnset: o.leaveUnset } : {}
1132
- }, c = U(), l = await K(), u = await q(l, b.SqlClient.pipe(g.flatMap(de))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
1193
+ }, c = W(), l = await q(), u = await J(l, b.SqlClient.pipe(g.flatMap(ue))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
1133
1194
  name: e.name,
1134
1195
  nullable: e.nullable,
1135
1196
  hasDefault: e.defaultValue !== void 0 && e.defaultValue !== null
1136
1197
  })), f = d(n), p = d(r);
1137
1198
  if (f === void 0) return console.error(`voltro db adopt: '${n}' does not exist in the database`), 1;
1138
1199
  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;
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 = Se(s, f, p, await _(r));
1140
- for (let e of we(s, v, await _(n))) console.log(e);
1200
+ 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 = be(s, f, p, await _(r));
1201
+ for (let e of Se(s, v, await _(n))) console.log(e);
1141
1202
  if (v.fatal) return 1;
1142
1203
  if (!e.includes("--apply")) return console.log(""), console.log(" (dry run — nothing written. Re-run with --apply.)"), 0;
1143
- let y = await De(s, v, c, "id", void 0, h, { keepSource: e.includes("--keep-source") });
1204
+ let y = await Ee(s, v, c, "id", void 0, h, { keepSource: e.includes("--keep-source") });
1144
1205
  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);
1206
+ },
1207
+ "scan-credentials": async (e) => {
1208
+ let t = W(), n = [];
1209
+ for (let t = 0; t < e.length; t += 1) {
1210
+ if (e[t] !== "--table") continue;
1211
+ let r = e[t + 1];
1212
+ if (r === void 0) throw new k("voltro db scan-credentials: --table needs a value");
1213
+ let [i, a = "subject"] = r.split(":");
1214
+ n.push({
1215
+ table: i,
1216
+ column: a
1217
+ });
1218
+ }
1219
+ let r = [...Oe.map((e) => ({
1220
+ table: e,
1221
+ column: "subject"
1222
+ })), ...n], i = await J(await q(), b.SqlClient.pipe(g.flatMap((e) => g.promise(() => Me(e, t, r, G)))));
1223
+ return console.log(Ne(i, t)), Pe(i);
1145
1224
  }
1146
- }, ut = async (e) => {
1225
+ }, vt = async (e) => {
1147
1226
  let [t, ...n] = e;
1148
1227
  if (!t) {
1149
1228
  console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
1150
- for (let e of Object.keys($)) console.error(` ${e}`);
1229
+ for (let e of Object.keys(_t)) console.error(` ${e}`);
1151
1230
  return 1;
1152
1231
  }
1153
- let r = $[t];
1232
+ let r = _t[t];
1154
1233
  if (!r) return console.error(`voltro db: unknown subcommand '${t}'`), 2;
1155
- (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)");
1234
+ (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)");
1156
1235
  let i = (e) => {
1157
- V(e) ? L.error(H) : console.error(e), process.exit(1);
1236
+ H(e) ? R.error(U) : console.error(e), process.exit(1);
1158
1237
  };
1159
1238
  process.on("uncaughtException", i);
1160
1239
  try {
@@ -1164,4 +1243,4 @@ export default defineMigration({
1164
1243
  }
1165
1244
  };
1166
1245
  //#endregion
1167
- export { Re as a, ye as c, nt as i, be as l, B as n, V as o, Le as r, ut as s, H as t };
1246
+ export { Ge as a, k as c, lt as i, ve as l, V as n, H as o, We as r, vt as s, U as t };