@voltro/cli 0.14.0 → 0.16.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 (66) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/dist/{apiBuild-370ITNRk.js → apiBuild-BJtiqoOu.js} +2 -2
  3. package/dist/apiBuild-BOczx9-m.js +2 -0
  4. package/dist/bin.js +3 -3
  5. package/dist/{commands-D-teL_TM.js → commands-8A5RU3MW.js} +1785 -1564
  6. package/dist/dbCommand-DNQ6l-su.js +2 -0
  7. package/dist/{dbCommand-Dmi-vHfA.js → dbCommand-OPMtO0sX.js} +225 -211
  8. package/dist/{dev-CKyW_LAv.js → dev-DGjEvvkV.js} +1321 -1300
  9. package/dist/{dev-CPKbckaN.js → dev-HdG7JdSk.js} +1 -1
  10. package/dist/{fileConventions-COg0ZytD.js → fileConventions-Cu0tM-Bl.js} +22 -2
  11. package/dist/{frameworkTableAssembly-DCLhpHsF.js → frameworkTableAssembly-BA2bAmYN.js} +1 -1
  12. package/dist/frameworkTableAssembly-D1zJSgOU.js +2 -0
  13. package/dist/index.js +1 -1
  14. package/dist/{inspectMetrics-CyVMeofr.js → inspectMetrics-CYDSO3za.js} +1 -1
  15. package/dist/{serveCommand-CU3Sw4ZM.js → serveCommand-C3lq3mFd.js} +7 -5
  16. package/dist/serveEntry.js +2 -2
  17. package/dist/{start-D8ATvTzo.js → start-BtgatJN3.js} +1 -1
  18. package/dist/startEntry.js +2 -2
  19. package/package.json +17 -17
  20. package/templates/AGENTS.md +1 -1
  21. package/templates/agent-docs/_index.md +1 -1
  22. package/templates/agent-docs/authentication.md +29 -0
  23. package/templates/agent-docs/cli.md +80 -0
  24. package/templates/agent-docs/data.md +11 -3
  25. package/templates/agent-docs/database/migrations.md +21 -0
  26. package/templates/agent-docs/plugins.md +1 -1
  27. package/templates/agent-docs/schema-driven-ui.md +2 -1
  28. package/templates/agent-docs/whats-new.md +84 -50
  29. package/templates/apps/api-ai/package.json +7 -7
  30. package/templates/apps/api-auth/package.json +8 -8
  31. package/templates/apps/api-backend/package.json +7 -7
  32. package/templates/apps/api-backend-deactivation/package.json +7 -7
  33. package/templates/apps/api-backend-mail/package.json +8 -8
  34. package/templates/apps/api-backend-mariadb/package.json +9 -9
  35. package/templates/apps/api-backend-storage/package.json +8 -8
  36. package/templates/apps/api-data-advanced/package.json +8 -8
  37. package/templates/apps/api-durable/package.json +8 -8
  38. package/templates/apps/api-feature-flags/package.json +9 -9
  39. package/templates/apps/api-governance/package.json +8 -8
  40. package/templates/apps/api-kv/package.json +8 -8
  41. package/templates/apps/api-moderation/package.json +8 -8
  42. package/templates/apps/api-observability/package.json +8 -8
  43. package/templates/apps/api-ratelimit/package.json +8 -8
  44. package/templates/apps/api-rbac/package.json +8 -8
  45. package/templates/apps/api-rest/package.json +7 -7
  46. package/templates/apps/api-saas/package.json +11 -11
  47. package/templates/apps/api-search/package.json +8 -8
  48. package/templates/apps/api-versioning/package.json +8 -8
  49. package/templates/apps/api-webhooks/package.json +9 -9
  50. package/templates/apps/changelog/package.json +6 -6
  51. package/templates/apps/edge-functions/package.json +2 -2
  52. package/templates/apps/frontend-admin/package.json +8 -8
  53. package/templates/apps/frontend-app/package.json +8 -8
  54. package/templates/apps/frontend-blank/package.json +7 -7
  55. package/templates/apps/frontend-contact/package.json +7 -7
  56. package/templates/apps/frontend-dashboard/package.json +7 -7
  57. package/templates/apps/frontend-docs/package.json +7 -7
  58. package/templates/apps/frontend-i18n/package.json +6 -6
  59. package/templates/apps/frontend-landing/package.json +7 -7
  60. package/templates/apps/frontend-spa/package.json +7 -7
  61. package/templates/apps/frontend-ssr/package.json +7 -7
  62. package/templates/apps/frontend-ssr-api/package.json +8 -8
  63. package/templates/apps/frontend-static-blog/package.json +6 -6
  64. package/dist/apiBuild-DatWP5hh.js +0 -2
  65. package/dist/dbCommand-B3CeMU3u.js +0 -2
  66. package/dist/frameworkTableAssembly-DYzb2elg.js +0 -2
@@ -1,25 +1,25 @@
1
1
  import { i as e, n as t, r as n, s as r, t as i } from "./seedRunner-D6eu-u5U.js";
2
- import { t as a } from "./frameworkTableAssembly-DCLhpHsF.js";
3
- import { g as o } from "./fileConventions-COg0ZytD.js";
2
+ import { t as a } from "./frameworkTableAssembly-BA2bAmYN.js";
3
+ import { g as o } from "./fileConventions-Cu0tM-Bl.js";
4
4
  import { a as s, n as c, t as l } from "./cliArgs-qdZSElM3.js";
5
5
  import { basename as u, join as d, relative as f, resolve as p } from "node:path";
6
6
  import { pathToFileURL as m } from "node:url";
7
7
  import { Effect as h } from "effect";
8
- import { createLogger as ee } from "@voltro/logger";
9
- import { promises as g, writeSync as te } from "node:fs";
10
- import { InMemoryDataStore as ne } from "@voltro/runtime";
11
- import { SqlClient as _ } from "@effect/sql";
12
- import { actorsTable as v, frameworkTables as y, isMigrationDefinition as b, registerCoreTables as re } from "@voltro/database";
13
- import { createHash as ie } from "node:crypto";
14
- import { applyPlan as x, applySchema as S, declaredSnapshot as ae, fingerprintSchema as C, ignoreTablesFromEnv as w, introspectSchema as oe, planMigrations as T, rollbackFileBasedMigration as E, runFileBasedMigrations as D, shortFingerprint as O, squashMigrationPlans as se } from "@voltro/database/sql";
8
+ import { createLogger as g } from "@voltro/logger";
9
+ import { promises as _, writeSync as v } from "node:fs";
10
+ import { InMemoryDataStore as ee } from "@voltro/runtime";
11
+ import { SqlClient as y } from "@effect/sql";
12
+ import { actorsTable as te, frameworkTables as b, isMigrationDefinition as x, registerCoreTables as ne } from "@voltro/database";
13
+ import { createHash as re } from "node:crypto";
14
+ import { applyPlan as S, applySchema as C, declaredSnapshot as ie, destructiveScope as ae, fingerprintSchema as w, ignoreTablesFromEnv as T, introspectSchema as oe, planMigrations as E, rollbackFileBasedMigration as se, runFileBasedMigrations as ce, shortFingerprint as D, squashMigrationPlans as le, unblockLossy as ue } from "@voltro/database/sql";
15
15
  //#region src/cliError.ts
16
- var k = class extends Error {
16
+ var O = class extends Error {
17
17
  isCliError = !0;
18
18
  code;
19
19
  constructor(e, t = 1) {
20
20
  super(e), this.name = "CliError", this.code = t;
21
21
  }
22
- }, ce = (e) => typeof e == "object" && !!e && e.isCliError === !0, A = /\.migration\.ts$/, j = async (e, t) => {
22
+ }, de = (e) => typeof e == "object" && !!e && e.isCliError === !0, k = /\.migration\.ts$/, A = async (e, t) => {
23
23
  let n = [];
24
24
  for (let r of e) {
25
25
  let e;
@@ -29,7 +29,7 @@ var k = class extends Error {
29
29
  t.warn("migration: failed to import", { file: r }, e);
30
30
  continue;
31
31
  }
32
- let i = (e.default && b(e.default) ? e.default : void 0) ?? Object.values(e).find(b);
32
+ let i = (e.default && x(e.default) ? e.default : void 0) ?? Object.values(e).find(x);
33
33
  if (!i) {
34
34
  t.warn("migration: no defineMigration export", { file: r });
35
35
  continue;
@@ -39,7 +39,7 @@ var k = class extends Error {
39
39
  idInFile: i.id,
40
40
  idFromFile: u(r).replace(/\.migration\.ts$/, "")
41
41
  });
42
- let a = await g.readFile(r, "utf8").catch(() => ""), o = ie("sha256").update(a).digest("hex").slice(0, 16);
42
+ let a = await _.readFile(r, "utf8").catch(() => ""), o = re("sha256").update(a).digest("hex").slice(0, 16);
43
43
  n.push({
44
44
  file: r,
45
45
  definition: i,
@@ -47,14 +47,14 @@ var k = class extends Error {
47
47
  });
48
48
  }
49
49
  return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
50
- }, M = (e) => {
50
+ }, j = (e) => {
51
51
  let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t;
52
- return h.runPromise(S(y, n).pipe(h.provide(e)));
53
- }, N = (e) => h.runPromise(h.gen(function* () {
54
- let e = yield* (yield* _.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(h.orElseSucceed(() => []));
52
+ return h.runPromise(C(b, n).pipe(h.provide(e)));
53
+ }, M = (e) => h.runPromise(h.gen(function* () {
54
+ let e = yield* (yield* y.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(h.orElseSucceed(() => []));
55
55
  return new Map(e.map((e) => [e.id, e]));
56
- }).pipe(h.provide(e))), P = (e, t) => h.runPromise(h.gen(function* () {
57
- yield* (yield* _.SqlClient)`
56
+ }).pipe(h.provide(e))), N = (e, t) => h.runPromise(h.gen(function* () {
57
+ yield* (yield* y.SqlClient)`
58
58
  INSERT INTO "_voltro_migrations"
59
59
  (id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
60
60
  VALUES
@@ -69,14 +69,14 @@ var k = class extends Error {
69
69
  "errorStack" = EXCLUDED."errorStack",
70
70
  "updatedAt" = now()
71
71
  `;
72
- }).pipe(h.provide(e))), le = async (e, t, n) => {
72
+ }).pipe(h.provide(e))), fe = async (e, t, n) => {
73
73
  if (e.length === 0) return {
74
74
  applied: [],
75
75
  failed: null,
76
76
  skipped: []
77
77
  };
78
- await M(t);
79
- let r = await N(t), i = [], a = [], o = null;
78
+ await j(t);
79
+ let r = await M(t), i = [], a = [], o = null;
80
80
  for (let s of e) {
81
81
  let e = r.get(s.definition.id);
82
82
  if (e && e.status === "applied") {
@@ -97,7 +97,7 @@ var k = class extends Error {
97
97
  name: s.definition.name
98
98
  });
99
99
  let c = Date.now(), l = [];
100
- await P(t, {
100
+ await N(t, {
101
101
  id: s.definition.id,
102
102
  name: s.definition.name,
103
103
  hash: s.hash,
@@ -138,7 +138,7 @@ var k = class extends Error {
138
138
  ...a.stack ? { stack: a.stack } : {}
139
139
  }
140
140
  };
141
- o = f, i.push(f), await P(t, {
141
+ o = f, i.push(f), await N(t, {
142
142
  id: s.definition.id,
143
143
  name: s.definition.name,
144
144
  hash: s.hash,
@@ -157,7 +157,7 @@ var k = class extends Error {
157
157
  status: "applied",
158
158
  durationMs: Date.now() - c,
159
159
  steps: l
160
- }), await P(t, {
160
+ }), await N(t, {
161
161
  id: s.definition.id,
162
162
  name: s.definition.name,
163
163
  hash: s.hash,
@@ -177,9 +177,9 @@ var k = class extends Error {
177
177
  failed: o,
178
178
  skipped: a
179
179
  };
180
- }, ue = async (e, t, n, r = {}) => {
181
- await M(t);
182
- let i = await N(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
180
+ }, pe = async (e, t, n, r = {}) => {
181
+ await j(t);
182
+ let i = await M(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
183
183
  if (a.length === 0) return n.info("migration: nothing to roll back"), {
184
184
  applied: [],
185
185
  failed: null,
@@ -232,7 +232,7 @@ var k = class extends Error {
232
232
  status: "applied",
233
233
  durationMs: Date.now() - r,
234
234
  steps: i
235
- }), await P(t, {
235
+ }), await N(t, {
236
236
  id: e.definition.id,
237
237
  name: e.definition.name,
238
238
  hash: e.hash,
@@ -248,9 +248,9 @@ var k = class extends Error {
248
248
  failed: c,
249
249
  skipped: []
250
250
  };
251
- }, F = async (e, t) => {
252
- await M(t);
253
- let n = await N(t);
251
+ }, P = async (e, t) => {
252
+ await j(t);
253
+ let n = await M(t);
254
254
  return e.map((e) => {
255
255
  let t = n.get(e.definition.id);
256
256
  return t ? {
@@ -267,7 +267,7 @@ var k = class extends Error {
267
267
  hashMatches: !0
268
268
  };
269
269
  });
270
- }, I = (e) => {
270
+ }, F = (e) => {
271
271
  if (!e.failed) return "";
272
272
  let t = e.failed, n = [
273
273
  `✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
@@ -280,7 +280,7 @@ var k = class extends Error {
280
280
  ""
281
281
  ];
282
282
  return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
283
- }, L = ee({ scope: "voltro:db" }), de = /* @__PURE__ */ new Set([
283
+ }, I = g({ scope: "voltro:db" }), me = /* @__PURE__ */ new Set([
284
284
  "node_modules",
285
285
  "dist",
286
286
  "build",
@@ -288,17 +288,17 @@ var k = class extends Error {
288
288
  ".vite",
289
289
  ".next",
290
290
  ".git"
291
- ]), R = async (e, t) => {
291
+ ]), L = async (e, t) => {
292
292
  let n = [], r = async (e) => {
293
- let i = await g.readdir(e, { withFileTypes: !0 }).catch(() => []);
293
+ let i = await _.readdir(e, { withFileTypes: !0 }).catch(() => []);
294
294
  for (let a of i) {
295
- if (de.has(a.name)) continue;
295
+ if (me.has(a.name)) continue;
296
296
  let i = d(e, a.name);
297
297
  a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
298
298
  }
299
299
  };
300
300
  return await r(e), n;
301
- }, z = (e) => p(e.find((e) => !e.startsWith("-")) ?? process.cwd()), B = () => {
301
+ }, R = (e) => p(e.find((e) => !e.startsWith("-")) ?? process.cwd()), z = () => {
302
302
  let e = process.env.DB_MAX_CONNECTIONS ?? process.env.PG_MAX_CONNECTIONS, t = e !== void 0 && e !== "" ? Number(e) : NaN, n = Number.isInteger(t) && t > 0 ? { maxConnections: t } : {}, r = process.env.DB_SCHEMA, i = r && r.length > 0 ? { schema: r } : {}, a = process.env.DB_DIRECT_URL ?? process.env.DB_MIGRATE_URL ?? process.env.DB_URL ?? process.env.DB_PRIMARY_URL;
303
303
  return a ? {
304
304
  url: a,
@@ -313,63 +313,63 @@ var k = class extends Error {
313
313
  ...n,
314
314
  ...i
315
315
  };
316
- }, V = (e) => {
316
+ }, B = (e) => {
317
317
  let t = e;
318
318
  if (typeof t != "object" || !t) return !1;
319
319
  if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
320
320
  let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
321
321
  return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
322
- }, 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 = () => {
322
+ }, V = "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`.)", H = () => {
323
323
  let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
324
- if (e === "memory") throw new k("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
324
+ if (e === "memory") throw new O("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
325
325
  return e;
326
- }, W = async () => (await r(U())).makeSqlLayer(B()), fe = async (e) => {
327
- let t = z(e), n = e.includes("--dry-run"), r = await j(await R(t, A), L);
328
- if (r.length === 0) return L.info("no migrations found", { root: t }), 0;
326
+ }, U = async () => (await r(H())).makeSqlLayer(z()), he = async (e) => {
327
+ let t = R(e), n = e.includes("--dry-run"), r = await A(await L(t, k), I);
328
+ if (r.length === 0) return I.info("no migrations found", { root: t }), 0;
329
329
  if (n) {
330
330
  let e = /* @__PURE__ */ new Set();
331
331
  try {
332
- let t = await F(r, await W());
332
+ let t = await P(r, await U());
333
333
  e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
334
334
  } catch {
335
- L.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
335
+ I.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
336
336
  }
337
337
  let t = r.filter((t) => !e.has(t.definition.id));
338
- if (t.length === 0) return L.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
339
- L.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
338
+ if (t.length === 0) return I.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
339
+ I.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
340
340
  for (let e of t) console.log(` pending ${e.definition.id}`);
341
341
  return 0;
342
342
  }
343
- let i = await le(r, await W(), L);
344
- return i.failed ? (process.stderr.write(I(i) + "\n"), 1) : (L.info("migrate complete", {
343
+ let i = await fe(r, await U(), I);
344
+ return i.failed ? (process.stderr.write(F(i) + "\n"), 1) : (I.info("migrate complete", {
345
345
  applied: i.applied.length,
346
346
  skipped: i.skipped.length
347
347
  }), 0);
348
- }, pe = async (e) => {
349
- let t = z(e), n = c(e, "--to"), r = await j(await R(t, A), L);
350
- if (r.length === 0) return L.info("no migrations found", { root: t }), 0;
351
- let i = await ue(r, await W(), L, n ? { to: n } : {});
352
- return i.failed ? (process.stderr.write(I(i) + "\n"), 1) : (L.info("rollback complete", { reverted: i.applied.length }), 0);
353
- }, me = async (e) => {
354
- let t = await j(await R(z(e), A), L);
348
+ }, ge = async (e) => {
349
+ let t = R(e), n = c(e, "--to"), r = await A(await L(t, k), I);
350
+ if (r.length === 0) return I.info("no migrations found", { root: t }), 0;
351
+ let i = await pe(r, await U(), I, n ? { to: n } : {});
352
+ return i.failed ? (process.stderr.write(F(i) + "\n"), 1) : (I.info("rollback complete", { reverted: i.applied.length }), 0);
353
+ }, _e = async (e) => {
354
+ let t = await A(await L(R(e), k), I);
355
355
  if (t.length === 0) return console.log("no migrations found"), 0;
356
- let n = await F(t, await W());
356
+ let n = await P(t, await U());
357
357
  for (let e of n) {
358
358
  let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
359
359
  console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
360
360
  }
361
361
  return 0;
362
- }, he = async (e) => {
362
+ }, ve = async (e) => {
363
363
  let t = (c(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
364
- 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."), {
365
- store: new ne(),
364
+ if (t === "memory") return I.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), I.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
365
+ store: new ee(),
366
366
  dispose: async () => {},
367
367
  dialectId: "memory"
368
368
  };
369
369
  let n = await r(t);
370
- L.info(`voltro db seed: target store=${n.id} — rows will persist`);
370
+ I.info(`voltro db seed: target store=${n.id} — rows will persist`);
371
371
  let i = await n.makeStore({
372
- sqlLayer: n.makeSqlLayer(B()),
372
+ sqlLayer: n.makeSqlLayer(z()),
373
373
  changeStrategy: "inline"
374
374
  }), a = i;
375
375
  return {
@@ -379,66 +379,77 @@ var k = class extends Error {
379
379
  },
380
380
  dialectId: n.id
381
381
  };
382
- }, ge = async (r) => {
383
- let a = z(r), o = c(r, "--id"), s = c(r, "--lifecycle"), l = await t(await R(a, i), L);
384
- if (l.length === 0) return L.info("no seeds found", { root: a }), 0;
385
- let { store: u, dispose: d, dialectId: f } = await he(r);
382
+ }, ye = async (r) => {
383
+ let a = R(r), o = c(r, "--id"), s = c(r, "--lifecycle"), l = await t(await L(a, i), I);
384
+ if (l.length === 0) return I.info("no seeds found", { root: a }), 0;
385
+ let { store: u, dispose: d, dialectId: f } = await ve(r);
386
386
  try {
387
- return o ? +((await e(l, o, u, L)).status === "failed") : +((await n((s ? l.filter((e) => e.definition.lifecycle === s) : l.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
387
+ return o ? +((await e(l, o, u, I)).status === "failed") : +((await n((s ? l.filter((e) => e.definition.lifecycle === s) : l.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
388
388
  ...e,
389
389
  definition: {
390
390
  ...e.definition,
391
391
  lifecycle: "boot"
392
392
  }
393
- })), u, L, { force: !0 })).filter((e) => e.status === "failed").length > 0);
393
+ })), u, I, { force: !0 })).filter((e) => e.status === "failed").length > 0);
394
394
  } finally {
395
395
  await d().catch((e) => {
396
- L.warn("voltro db seed: store dispose failed", { error: String(e) });
396
+ I.warn("voltro db seed: store dispose failed", { error: String(e) });
397
397
  });
398
398
  }
399
- }, _e = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", G = async (e) => {
400
- let t = await R(e, o), n = [];
399
+ }, be = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", W = async (e) => {
400
+ let t = await L(e, o), n = [];
401
401
  for (let r of t) {
402
402
  let t;
403
403
  try {
404
404
  t = await import(m(r).href);
405
405
  } catch (t) {
406
- L.warn("db: failed to import schema file", {
406
+ I.warn("db: failed to import schema file", {
407
407
  file: f(e, r),
408
408
  error: String(t)
409
409
  });
410
410
  continue;
411
411
  }
412
- for (let e of Object.values(t)) _e(e) && n.push(e);
412
+ for (let e of Object.values(t)) be(e) && n.push(e);
413
413
  }
414
414
  return n;
415
- }, K = (e) => {
415
+ }, G = (e) => {
416
416
  let t = new Map(e.map((e) => [e.tableName, e]));
417
- t.has("actors") || t.set("actors", v);
417
+ t.has("actors") || t.set("actors", te);
418
418
  let n = t.get("actors"), r = t.get("tenants");
419
- return re({
419
+ return ne({
420
420
  actors: () => n,
421
421
  ...r === void 0 ? {} : { tenants: () => r }
422
422
  }), [...t.values()];
423
- }, q = (e) => {
423
+ }, K = (e) => {
424
424
  let t = /* @__PURE__ */ new Map();
425
425
  for (let n of e) {
426
426
  let e = n.tableName;
427
427
  e !== void 0 && !t.has(e) && t.set(e, n);
428
428
  }
429
429
  return [...t.values()];
430
+ }, q = (e) => {
431
+ let t = ae(process.env.VOLTRO_DESTRUCTIVE_OK);
432
+ if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
433
+ plan: e,
434
+ acknowledged: !1
435
+ };
436
+ let n = ue(e, t);
437
+ return {
438
+ plan: n,
439
+ acknowledged: !n.operations.some((e) => e.blocked)
440
+ };
430
441
  }, J = async (e, t) => h.runPromise(t.pipe(h.provide(e))), Y = async (e) => {
431
442
  try {
432
- return await J(e, _.SqlClient.pipe(h.flatMap(oe)));
443
+ return await J(e, y.SqlClient.pipe(h.flatMap(oe)));
433
444
  } catch (e) {
434
445
  let t = String(e?.message ?? e);
435
- if (V(e) || /connection error/i.test(t)) {
436
- let n = /* @__PURE__ */ Error(`${H}\n\nOriginal error: ${t}`);
446
+ if (B(e) || /connection error/i.test(t)) {
447
+ let n = /* @__PURE__ */ Error(`${V}\n\nOriginal error: ${t}`);
437
448
  throw n.cause = e, n;
438
449
  }
439
450
  throw e;
440
451
  }
441
- }, ve = {
452
+ }, xe = {
442
453
  safe: "\x1B[32m✓\x1B[0m",
443
454
  "needs-default": "\x1B[32m⊕\x1B[0m",
444
455
  "needs-backfill": "\x1B[33m⊕\x1B[0m",
@@ -446,7 +457,7 @@ var k = class extends Error {
446
457
  lossy: "\x1B[31m✗\x1B[0m",
447
458
  "online-required": "\x1B[36m◷\x1B[0m",
448
459
  "multi-step": "\x1B[35m⇉\x1B[0m"
449
- }, ye = (e) => {
460
+ }, Se = (e) => {
450
461
  let t = e.op, n;
451
462
  switch (t.kind) {
452
463
  case "create-table":
@@ -504,7 +515,7 @@ var k = class extends Error {
504
515
  n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
505
516
  break;
506
517
  }
507
- let r = ve[e.classification], i = e.reason ? ` # ${e.reason}` : "", a = e.blocked ? `\n ! ${e.blocked.fix}` : "";
518
+ let r = xe[e.classification], i = e.reason ? ` # ${e.reason}` : "", a = e.blocked ? `\n ! ${e.blocked.fix}` : "";
508
519
  return ` ${r} ${n}${i}${a}`;
509
520
  }, X = (e) => {
510
521
  let t = e.summary, n = e.operations.length;
@@ -512,7 +523,7 @@ var k = class extends Error {
512
523
  console.log(" (schema is up to date)\n");
513
524
  return;
514
525
  }
515
- for (let t of e.operations) console.log(ye(t));
526
+ for (let t of e.operations) console.log(Se(t));
516
527
  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`);
517
528
  }, Z = (e) => {
518
529
  let t = c(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
@@ -520,15 +531,15 @@ var k = class extends Error {
520
531
  let n = Number(t);
521
532
  if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
522
533
  return n;
523
- }, be = async (e) => {
524
- let t = Z(e), n = z(e), r = await G(n);
525
- if (r.length === 0) return L.warn("db plan: no schema files found", {
534
+ }, Ce = async (e) => {
535
+ let t = Z(e), n = R(e), r = await W(n);
536
+ if (r.length === 0) return I.warn("db plan: no schema files found", {
526
537
  root: n,
527
538
  hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
528
539
  }), 1;
529
- let i = await a({ root: n }), o = K(q([
540
+ let i = await a({ root: n }), o = G(K([
530
541
  ...r,
531
- ...y,
542
+ ...b,
532
543
  ...i
533
544
  ])), s = c(e, "--against");
534
545
  if (s) {
@@ -536,115 +547,118 @@ var k = class extends Error {
536
547
  r && (i.authorization = `Bearer ${r}`);
537
548
  try {
538
549
  let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, r = await fetch(e, { headers: i });
539
- if (!r.ok) return L.error(`db plan --against: remote returned ${r.status}`, { url: e }), 1;
550
+ if (!r.ok) return I.error(`db plan --against: remote returned ${r.status}`, { url: e }), 1;
540
551
  let a = (await r.json()).drift?.liveSnapshot;
541
- if (!a) return L.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
542
- let s = T({
552
+ if (!a) return I.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
553
+ let s = E({
543
554
  declared: o,
544
555
  live: a,
545
- dialect: U(),
546
- ...w(),
556
+ dialect: H(),
557
+ ...T(),
547
558
  ...t === void 0 ? {} : { onlineAfter: t }
548
559
  });
549
560
  return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), X(s), s.summary.blocked > 0 ? 2 : 0;
550
561
  } catch (e) {
551
- return L.error(`db plan --against: ${e.message}`), 1;
562
+ return I.error(`db plan --against: ${e.message}`), 1;
552
563
  }
553
564
  }
554
- let l = T({
565
+ let l = E({
555
566
  declared: o,
556
- live: await Y(await W()),
557
- dialect: U(),
558
- ...w(),
567
+ live: await Y(await U()),
568
+ dialect: H(),
569
+ ...T(),
559
570
  ...t === void 0 ? {} : { onlineAfter: t }
560
571
  });
561
- return e.includes("--json") ? (te(1, `${JSON.stringify(l, null, 2)}\n`), l.summary.blocked > 0 ? 2 : 0) : (X(l), l.summary.blocked > 0 ? 2 : 0);
562
- }, xe = async (e, t, n) => {
572
+ return e.includes("--json") ? (v(1, `${JSON.stringify(l, null, 2)}\n`), l.summary.blocked > 0 ? 2 : 0) : (X(l), l.summary.blocked > 0 ? 2 : 0);
573
+ }, we = async (e, t, n) => {
563
574
  let r;
564
575
  try {
565
- let t = await g.readFile(p(e), "utf8");
576
+ let t = await _.readFile(p(e), "utf8");
566
577
  r = JSON.parse(t);
567
578
  } catch (t) {
568
- return L.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
579
+ return I.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
569
580
  }
570
- 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;
571
- let i = await G(n);
572
- if (i.length === 0) return L.warn("db apply --plan: no schema files found", { root: n }), 1;
573
- let o = await a({ root: n }), s = K(q([
581
+ if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return I.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
582
+ let i = await W(n);
583
+ if (i.length === 0) return I.warn("db apply --plan: no schema files found", { root: n }), 1;
584
+ let o = await a({ root: n }), s = G(K([
574
585
  ...i,
575
- ...y,
586
+ ...b,
576
587
  ...o
577
- ])), l = await W();
578
- await J(l, S(y, U()));
579
- let u = T({
588
+ ])), l = await U();
589
+ await J(l, C(b, H()));
590
+ let u = E({
580
591
  declared: s,
581
592
  live: await Y(l),
582
- dialect: U(),
583
- ...w()
593
+ dialect: H(),
594
+ ...T()
584
595
  });
585
- if (u.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=${u.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
586
- if (u.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=${u.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
587
- if (u.summary.blocked > 0) return X(u), L.error(`db apply --plan: refusing — ${u.summary.blocked} blocked operation(s). See the fix hints above.`), 2;
588
- if (u.operations.length === 0) return L.info("db apply --plan: schema is up to datenothing to apply"), 0;
589
- X(u);
590
- let d = (process.env.NODE_ENV ?? "dev").toLowerCase(), f = c(t, "--note"), m = await J(l, _.SqlClient.pipe(h.flatMap((e) => x(e, u, {
596
+ if (u.fromFingerprint !== r.fromFingerprint) return I.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=${u.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
597
+ if (u.toFingerprint !== r.toFingerprint) return I.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${u.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
598
+ let d = q(u), f = d.plan;
599
+ if (f.summary.blocked > 0 && !d.acknowledged) return X(f), I.error(`db apply --plan: refusing — ${u.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (I.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan it only relaxes"), I.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
600
+ if (d.acknowledged && I.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), f.operations.length === 0) return I.info("db apply --plan: schema is up to date — nothing to apply"), 0;
601
+ X(f);
602
+ let m = (process.env.NODE_ENV ?? "dev").toLowerCase(), g = c(t, "--note"), v = await J(l, y.SqlClient.pipe(h.flatMap((e) => S(e, f, {
591
603
  declared: s,
592
604
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
593
- environment: d === "production" ? "prod" : d === "staging" ? "staging" : "dev",
605
+ environment: m === "production" ? "prod" : m === "staging" ? "staging" : "dev",
594
606
  source: "file",
595
- ...f ? { notes: f } : {},
596
- replan: () => h.promise(() => Y(l)).pipe(h.map((e) => T({
607
+ ...g ? { notes: g } : {},
608
+ replan: () => h.promise(() => Y(l)).pipe(h.map((e) => E({
597
609
  declared: s,
598
610
  live: e,
599
- dialect: U(),
600
- ...w()
611
+ dialect: H(),
612
+ ...T()
601
613
  })))
602
614
  }))));
603
- return L.info(`db apply --plan: applied ${m.appliedOps} op(s) in ${m.durationMs}ms`, {
604
- id: m.id,
605
- fingerprint: m.fingerprint.slice(0, 16)
615
+ return I.info(`db apply --plan: applied ${v.appliedOps} op(s) in ${v.durationMs}ms`, {
616
+ id: v.id,
617
+ fingerprint: v.fingerprint.slice(0, 16)
606
618
  }), 0;
607
- }, Se = async (e) => {
608
- let t = z(e), n = Z(e), r = c(e, "--plan");
609
- if (r) return xe(r, e, t);
610
- 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;
611
- let i = await G(t);
612
- if (i.length === 0) return L.warn("db apply: no schema files found", { root: t }), 1;
613
- let o = await a({ root: t }), s = K(q([
619
+ }, Te = async (e) => {
620
+ let t = R(e), n = Z(e), r = c(e, "--plan");
621
+ if (r) return we(r, e, t);
622
+ if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return I.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;
623
+ let i = await W(t);
624
+ if (i.length === 0) return I.warn("db apply: no schema files found", { root: t }), 1;
625
+ let o = await a({ root: t }), s = G(K([
614
626
  ...i,
615
- ...y,
627
+ ...b,
616
628
  ...o
617
- ])), l = await W();
618
- await J(l, S(y, U()));
619
- let u = T({
629
+ ])), l = await U();
630
+ await J(l, C(b, H()));
631
+ let u = E({
620
632
  declared: s,
621
633
  live: await Y(l),
622
- dialect: U(),
623
- ...w(),
634
+ dialect: H(),
635
+ ...T(),
624
636
  ...n === void 0 ? {} : { onlineAfter: n }
625
637
  });
626
- if (X(u), u.summary.blocked > 0) return L.error(`db apply: refusing — ${u.summary.blocked} blocked operation(s). See the fix hints above.`), 2;
627
- if (u.operations.length === 0) return L.info("db apply: schema is up to date — nothing to apply"), 0;
628
- let d = c(e, "--note"), f = await J(l, _.SqlClient.pipe(h.flatMap((e) => x(e, u, {
638
+ X(u);
639
+ let d = q(u), f = d.plan;
640
+ if (f.summary.blocked > 0 && !d.acknowledged) return I.error(`db apply: refusing — ${f.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (I.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), I.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
641
+ if (d.acknowledged && I.warn(`db apply: proceeding with ${u.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), f.operations.length === 0) return I.info("db apply: schema is up to date — nothing to apply"), 0;
642
+ let p = c(e, "--note"), m = await J(l, y.SqlClient.pipe(h.flatMap((e) => S(e, f, {
629
643
  declared: s,
630
644
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
631
645
  environment: (process.env.NODE_ENV ?? "dev").toLowerCase() === "staging" ? "staging" : "dev",
632
646
  source: "auto-diff",
633
- ...d ? { notes: d } : {},
634
- replan: () => h.promise(() => Y(l)).pipe(h.map((e) => T({
647
+ ...p ? { notes: p } : {},
648
+ replan: () => h.promise(() => Y(l)).pipe(h.map((e) => E({
635
649
  declared: s,
636
650
  live: e,
637
- dialect: U(),
638
- ...w(),
651
+ dialect: H(),
652
+ ...T(),
639
653
  ...n === void 0 ? {} : { onlineAfter: n }
640
654
  })))
641
655
  }))));
642
- return L.info(`db apply: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
643
- id: f.id,
644
- fingerprint: f.fingerprint.slice(0, 16)
656
+ return I.info(`db apply: applied ${m.appliedOps} op(s) in ${m.durationMs}ms`, {
657
+ id: m.id,
658
+ fingerprint: m.fingerprint.slice(0, 16)
645
659
  }), 0;
646
- }, Ce = async (e) => {
647
- let t = Math.max(1, Number(c(e, "--limit") ?? "20")), n = await J(await W(), _.SqlClient.pipe(h.flatMap((e) => e`
660
+ }, Ee = async (e) => {
661
+ let t = Math.max(1, Number(c(e, "--limit") ?? "20")), n = await J(await U(), y.SqlClient.pipe(h.flatMap((e) => e`
648
662
  SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")}::text AS ${e("operations")},
649
663
  ${e("appliedBy")}, ${e("environment")}, ${e("source")},
650
664
  ${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}::text AS ${e("appliedAt")}
@@ -652,20 +666,20 @@ var k = class extends Error {
652
666
  ORDER BY ${e("appliedAt")} DESC
653
667
  LIMIT ${t}
654
668
  `)));
655
- 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;
669
+ if (n.length === 0) return I.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;
656
670
  console.log(`\n${n.length} plan${n.length === 1 ? "" : "s"} (newest first):\n`);
657
671
  for (let e of n) {
658
672
  let t = 0;
659
673
  try {
660
674
  t = JSON.parse(e.operations).length;
661
675
  } catch {}
662
- 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`);
676
+ 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`);
663
677
  }
664
678
  return console.log(), 0;
665
- }, we = async (e) => {
679
+ }, De = async (e) => {
666
680
  let t = e[0];
667
681
  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;
668
- let n = await W(), r = await J(n, _.SqlClient.pipe(h.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}::text AS ${e("operations")}
682
+ let n = await U(), r = await J(n, y.SqlClient.pipe(h.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}::text AS ${e("operations")}
669
683
  FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
670
684
  if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
671
685
  let i;
@@ -684,7 +698,7 @@ var k = class extends Error {
684
698
  if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
685
699
  let s = 0;
686
700
  for (let e of a) {
687
- let t = await J(n, _.SqlClient.pipe(h.flatMap((t) => t`
701
+ let t = await J(n, y.SqlClient.pipe(h.flatMap((t) => t`
688
702
  SELECT column_name FROM information_schema.columns
689
703
  WHERE table_schema = 'public'
690
704
  AND table_name = ${e.table}
@@ -696,11 +710,11 @@ var k = class extends Error {
696
710
  continue;
697
711
  }
698
712
  let r = t[0].column_name;
699
- await J(n, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table}" RENAME COLUMN "${r}" TO "${e.column}"`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
713
+ await J(n, y.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table}" RENAME COLUMN "${r}" TO "${e.column}"`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
700
714
  }
701
715
  let c = 0;
702
716
  for (let e of o) {
703
- let t = await J(n, _.SqlClient.pipe(h.flatMap((t) => t`
717
+ let t = await J(n, y.SqlClient.pipe(h.flatMap((t) => t`
704
718
  SELECT table_name FROM information_schema.tables
705
719
  WHERE table_schema = 'public'
706
720
  AND table_name LIKE ${e.table + "__dropped_%"}
@@ -711,18 +725,18 @@ var k = class extends Error {
711
725
  continue;
712
726
  }
713
727
  let r = t[0].table_name;
714
- await J(n, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${r}" RENAME TO "${e.table}"`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
728
+ await J(n, y.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${r}" RENAME TO "${e.table}"`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
715
729
  }
716
730
  return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
717
- }, Te = async (e) => {
731
+ }, Q = async (e) => {
718
732
  let t = c(e, "--before");
719
733
  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;
720
734
  let n = t.replace(/[^0-9]/g, "");
721
735
  if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
722
- let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await W(), o = await J(a, _.SqlClient.pipe(h.flatMap((e) => e`
736
+ let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await U(), o = await J(a, y.SqlClient.pipe(h.flatMap((e) => e`
723
737
  SELECT table_name, column_name FROM information_schema.columns
724
738
  WHERE table_schema = 'public' AND column_name LIKE ${"%__dropped_%"}
725
- `))), s = await J(a, _.SqlClient.pipe(h.flatMap((e) => e`
739
+ `))), s = await J(a, y.SqlClient.pipe(h.flatMap((e) => e`
726
740
  SELECT table_name FROM information_schema.tables
727
741
  WHERE table_schema = 'public' AND table_name LIKE ${"%__dropped_%"}
728
742
  `))), l = (e) => {
@@ -731,69 +745,69 @@ var k = class extends Error {
731
745
  }, u = 0;
732
746
  for (let e of o) {
733
747
  let t = l(e.column_name);
734
- t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table_name}" DROP COLUMN "${e.column_name}"`)))), u++);
748
+ t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a, y.SqlClient.pipe(h.flatMap((t) => t.unsafe(`ALTER TABLE "${e.table_name}" DROP COLUMN "${e.column_name}"`)))), u++);
735
749
  }
736
750
  for (let e of s) {
737
751
  let t = l(e.table_name);
738
- t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await J(a, _.SqlClient.pipe(h.flatMap((t) => t.unsafe(`DROP TABLE "${e.table_name}"`)))), u++);
752
+ t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await J(a, y.SqlClient.pipe(h.flatMap((t) => t.unsafe(`DROP TABLE "${e.table_name}"`)))), u++);
739
753
  }
740
754
  return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
741
- }, Q = async (e) => {
755
+ }, Oe = async (e) => {
742
756
  let t = c(e, "--before");
743
757
  if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
744
758
  let n = t;
745
759
  if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
746
- let r = c(e, "--note"), i = z(e), o = await G(i);
747
- if (o.length === 0) return L.warn("db squash: no schema files found", { root: i }), 1;
748
- let s = await a({ root: i }), l = C(ae(K(q([
760
+ let r = c(e, "--note"), i = R(e), o = await W(i);
761
+ if (o.length === 0) return I.warn("db squash: no schema files found", { root: i }), 1;
762
+ let s = await a({ root: i }), l = w(ie(G(K([
749
763
  ...o,
750
- ...y,
764
+ ...b,
751
765
  ...s
752
- ])))), u = await W();
766
+ ])))), u = await U();
753
767
  try {
754
- let e = await J(u, se({
768
+ let e = await J(u, le({
755
769
  before: n,
756
770
  fingerprint: l,
757
771
  appliedBy: process.env.USER ?? "cli",
758
772
  environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
759
773
  ...r === void 0 ? {} : { note: r }
760
774
  }));
761
- return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${O(e.snapshotFingerprint)}\n`), 0;
775
+ return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${D(e.snapshotFingerprint)}\n`), 0;
762
776
  } catch (e) {
763
777
  return console.error(`squash refused: ${e.message}`), 1;
764
778
  }
765
- }, Ee = async (e) => {
766
- let t = await W(), n = await J(t, _.SqlClient.pipe(h.flatMap((e) => e`
779
+ }, ke = async (e) => {
780
+ let t = await U(), n = await J(t, y.SqlClient.pipe(h.flatMap((e) => e`
767
781
  SELECT ${e("id")}, ${e("fingerprint")}, ${e("appliedAt")}::text AS ${e("appliedAt")}
768
782
  FROM ${e("_voltro_migration_plans")}
769
783
  ORDER BY ${e("appliedAt")} DESC
770
784
  LIMIT 1
771
- `))), r = C({ tables: (await Y(t)).tables.filter((e) => !e.name.startsWith("_voltro_")) });
772
- if (n.length === 0) return L.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${O(r)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
785
+ `))), r = w({ tables: (await Y(t)).tables.filter((e) => !e.name.startsWith("_voltro_")) });
786
+ if (n.length === 0) return I.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${D(r)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
773
787
  let i = n[0];
774
- return i.fingerprint === r ? (L.info(`db drift: live matches last applied (${O(r)})`), 0) : (L.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` last applied: ${O(i.fingerprint)} at ${i.appliedAt} (${i.id})`), console.log(` live now: ${O(r)}`), console.log(), console.log("Probable causes:"), console.log(" - manual DDL ran out-of-band (psql session, another tool)"), console.log(" - someone applied a plan but the row never made it into _voltro_migration_plans"), console.log(), console.log("To reconcile, run `voltro db plan` to see what your code expects vs the live DB."), 4);
775
- }, De = async (e) => {
776
- let t = z(e), n = await W(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
777
- await J(n, S(y, U()));
778
- let i = await J(n, _.SqlClient.pipe(h.flatMap((e) => D(e, {
788
+ return i.fingerprint === r ? (I.info(`db drift: live matches last applied (${D(r)})`), 0) : (I.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` last applied: ${D(i.fingerprint)} at ${i.appliedAt} (${i.id})`), console.log(` live now: ${D(r)}`), console.log(), console.log("Probable causes:"), console.log(" - manual DDL ran out-of-band (psql session, another tool)"), console.log(" - someone applied a plan but the row never made it into _voltro_migration_plans"), console.log(), console.log("To reconcile, run `voltro db plan` to see what your code expects vs the live DB."), 4);
789
+ }, Ae = async (e) => {
790
+ let t = R(e), n = await U(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
791
+ await J(n, C(b, H()));
792
+ let i = await J(n, y.SqlClient.pipe(h.flatMap((e) => ce(e, {
779
793
  projectRoot: t,
780
794
  env: r,
781
795
  appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
782
796
  }))));
783
- 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)`, {
797
+ return i.applied.length === 0 ? (I.info(`db files: no pending file-based migrations (${i.skipped.length} already applied)`), 0) : (I.info(`db files: applied ${i.applied.length} migration(s)`, {
784
798
  applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
785
799
  skipped: i.skipped.length
786
800
  }), 0);
787
- }, Oe = async (e) => {
788
- let t = z(e), n = e.find((e) => !e.startsWith("--"));
789
- if (!n) return L.error("voltro db rollback-file <migration-id>"), 2;
790
- 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;
791
- let r = await J(await W(), _.SqlClient.pipe(h.flatMap((e) => E(e, {
801
+ }, je = async (e) => {
802
+ let t = R(e), n = e.find((e) => !e.startsWith("--"));
803
+ if (!n) return I.error("voltro db rollback-file <migration-id>"), 2;
804
+ if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return I.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step."), 3;
805
+ let r = await J(await U(), y.SqlClient.pipe(h.flatMap((e) => se(e, {
792
806
  projectRoot: t,
793
807
  id: n
794
808
  }))));
795
- return L.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
796
- }, ke = () => {
809
+ return I.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
810
+ }, Me = () => {
797
811
  let e = /* @__PURE__ */ new Date();
798
812
  return [
799
813
  e.getUTCFullYear(),
@@ -803,7 +817,7 @@ var k = class extends Error {
803
817
  String(e.getUTCMinutes()).padStart(2, "0"),
804
818
  String(e.getUTCSeconds()).padStart(2, "0")
805
819
  ].join("");
806
- }, Ae = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", je = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
820
+ }, Ne = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", Pe = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
807
821
  import { Effect } from 'effect'
808
822
 
809
823
  // A hand-written migration. Reach for this when the DECLARATIVE differ can't
@@ -836,24 +850,24 @@ export default defineMigration({
836
850
  ],
837
851
  })
838
852
  `, $ = {
839
- plan: be,
840
- apply: Se,
841
- plans: Ce,
842
- drift: Ee,
843
- files: De,
844
- "rollback-file": Oe,
845
- migrate: fe,
846
- rollback: pe,
847
- status: me,
848
- seed: ge,
853
+ plan: Ce,
854
+ apply: Te,
855
+ plans: Ee,
856
+ drift: ke,
857
+ files: Ae,
858
+ "rollback-file": je,
859
+ migrate: he,
860
+ rollback: ge,
861
+ status: _e,
862
+ seed: ye,
849
863
  generate: async (e) => {
850
- let { value: t, rest: n } = s(e, "--root"), { value: r, rest: i } = s(n, "--name"), a = t ? p(t) : process.cwd(), o = Ae(r ?? l(i) ?? "migration"), c = `${ke()}_${o}`, u = d(a, "migrations"), m = d(u, `${c}.migration.ts`);
851
- return await g.mkdir(u, { recursive: !0 }), await g.writeFile(m, je(c, o), "utf8"), L.info(`created ${f(a, m)}`), console.error("Fill in the apply/undo steps, then run `voltro db migrate` (rollback with `voltro db rollback`)."), console.error("For a plain schema shape change you may not need this — `voltro db apply` diffs declared vs live."), 0;
864
+ let { value: t, rest: n } = s(e, "--root"), { value: r, rest: i } = s(n, "--name"), a = t ? p(t) : process.cwd(), o = Ne(r ?? l(i) ?? "migration"), c = `${Me()}_${o}`, u = d(a, "migrations"), m = d(u, `${c}.migration.ts`);
865
+ return await _.mkdir(u, { recursive: !0 }), await _.writeFile(m, Pe(c, o), "utf8"), I.info(`created ${f(a, m)}`), console.error("Fill in the apply/undo steps, then run `voltro db migrate` (rollback with `voltro db rollback`)."), console.error("For a plain schema shape change you may not need this — `voltro db apply` diffs declared vs live."), 0;
852
866
  },
853
- squash: Q,
854
- "restore-snapshot": we,
855
- "gc-snapshots": Te
856
- }, Me = async (e) => {
867
+ squash: Oe,
868
+ "restore-snapshot": De,
869
+ "gc-snapshots": Q
870
+ }, Fe = async (e) => {
857
871
  let [t, ...n] = e;
858
872
  if (!t) {
859
873
  console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
@@ -862,9 +876,9 @@ export default defineMigration({
862
876
  }
863
877
  let r = $[t];
864
878
  if (!r) return console.error(`voltro db: unknown subcommand '${t}'`), 2;
865
- (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)");
879
+ (process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && I.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
866
880
  let i = (e) => {
867
- V(e) ? L.error(H) : console.error(e), process.exit(1);
881
+ B(e) ? I.error(V) : console.error(e), process.exit(1);
868
882
  };
869
883
  process.on("uncaughtException", i);
870
884
  try {
@@ -874,4 +888,4 @@ export default defineMigration({
874
888
  }
875
889
  };
876
890
  //#endregion
877
- export { V as a, ce as c, K as i, B as n, Me as o, G as r, k as s, H as t };
891
+ export { B as a, de as c, G as i, z as n, Fe as o, W as r, O as s, V as t };