@voltro/database 0.33.0 → 0.34.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.
package/dist/sql.js CHANGED
@@ -1,13 +1,13 @@
1
- import { J as e, U as t, W as n, a as r, c as i, d as a, i as o, n as s, o as c, pt as l, r as u, s as d, st as f } from "./frameworkLiveTables-DbAgeVOU.js";
2
- import { Effect as p } from "effect";
3
- import { createLogger as m } from "@voltro/logger";
4
- import { SqlClient as h } from "@effect/sql";
5
- import { createHash as g } from "node:crypto";
6
- import { promises as _ } from "node:fs";
7
- import { join as v } from "node:path";
8
- import { pathToFileURL as y } from "node:url";
1
+ import { It as e, Jt as t, Lt as n, X as r, Z as i, _ as a, a as o, bt as s, c, et as l, i as u, l as d, m as f, mt as p, n as m, o as h, p as g, qt as _, r as v, tt as y } from "./frameworkLiveTables-CeuTYhWm.js";
2
+ import { Effect as b } from "effect";
3
+ import { createLogger as x } from "@voltro/logger";
4
+ import { SqlClient as S } from "@effect/sql";
5
+ import { createHash as C } from "node:crypto";
6
+ import { promises as ee } from "node:fs";
7
+ import { join as te } from "node:path";
8
+ import { pathToFileURL as ne } from "node:url";
9
9
  //#region src/migrations/ddlError.ts
10
- var b = (e, t, n, r = 400) => {
10
+ var re = (e, t, n, r = 400) => {
11
11
  let i = n, a = i.cause ?? {}, o = (e) => {
12
12
  let t = a[e];
13
13
  return t == null || t === "" ? void 0 : String(t);
@@ -39,19 +39,19 @@ var b = (e, t, n, r = 400) => {
39
39
  t !== void 0 && s.push(`db.${e}: ${t}`);
40
40
  }
41
41
  return s.push(""), s.join("\n");
42
- }, x = (e) => e.unique === !0 && typeof e.where == "string" && e.where.length > 0, S = (e) => e.replace(/"/g, "`"), ee = (e) => typeof e == "string" ? e : e.name ?? String(e), C = (e, t) => `_ua_${t}_${e}`.slice(0, 64), w = (e, t) => {
43
- if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(x)) return e;
42
+ }, ie = (e) => e.unique === !0 && typeof e.where == "string" && e.where.length > 0, ae = (e) => e.replace(/"/g, "`"), oe = (e) => typeof e == "string" ? e : e.name ?? String(e), se = (e, t) => `_ua_${t}_${e}`.slice(0, 64), ce = (e, t) => {
43
+ if (t !== "mysql" && t !== "mariadb" || !e.appliedIndexes.some(ie)) return e;
44
44
  let n = { ...e.fields }, r = [], i = [];
45
45
  for (let t of e.appliedIndexes) {
46
- if (!x(t)) {
46
+ if (!ie(t)) {
47
47
  r.push(t);
48
48
  continue;
49
49
  }
50
- let a = S(t.where), o = [];
50
+ let a = ae(t.where), o = [];
51
51
  for (let r of t.fields) {
52
- let i = ee(r);
52
+ let i = oe(r);
53
53
  if (!(i in e.fields)) throw Error(`@voltro/migrate: uniqueActive '${t.name}' on '${e.tableName}' references unknown column '${i}'.`);
54
- let s = C(t.name, i);
54
+ let s = se(t.name, i);
55
55
  o.push(s), n[s] = {
56
56
  type: "text",
57
57
  nullable: !0,
@@ -74,101 +74,101 @@ var b = (e, t, n, r = 400) => {
74
74
  appliedIndexes: r,
75
75
  appliedUniques: [...e.appliedUniques ?? [], ...i]
76
76
  };
77
- }, T = (e, t) => `${e}::${t}`, te = (e, t) => {
77
+ }, w = (e, t) => `${e}::${t}`, le = (e, t) => {
78
78
  let n = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = (e) => {
79
79
  n.add(e);
80
80
  let o = [...t(e)].filter((t) => t.target !== e).sort((e, t) => e.column.localeCompare(t.column));
81
- for (let t of o) n.has(t.target) ? i.add(T(e, t.column)) : r.has(t.target) || a(t.target);
81
+ for (let t of o) n.has(t.target) ? i.add(w(e, t.column)) : r.has(t.target) || a(t.target);
82
82
  n.delete(e), r.add(e);
83
83
  };
84
84
  for (let t of [...e].sort()) r.has(t) || a(t);
85
85
  return i;
86
- }, ne = 6322741009312437n, re = 250, ie = () => {
86
+ }, ue = 6322741009312437n, de = 250, fe = () => {
87
87
  let e = Number(process.env.VOLTRO_MIGRATION_LOCK_TIMEOUT_MS);
88
88
  return Number.isFinite(e) && e > 0 ? e : 3e4;
89
- }, ae = (e) => p.gen(function* () {
90
- let t = ie(), n = Date.now();
89
+ }, pe = (e) => b.gen(function* () {
90
+ let t = fe(), n = Date.now();
91
91
  for (;;) {
92
92
  if ((yield* e`
93
93
  SELECT pg_try_advisory_lock(${"6322741009312437"}::bigint) AS got`)[0]?.got) return;
94
- Date.now() - n >= t && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the postgres migration advisory lock within ${Math.round(t / 1e3)}s. Another migration is in progress, or a prior boot crashed while holding it. (A crashed process's session-level advisory lock is released once its DB connection closes; if this persists, find + terminate the holder via pg_stat_activity, or raise VOLTRO_MIGRATION_LOCK_TIMEOUT_MS.)`))), yield* p.sleep(`${re} millis`);
94
+ Date.now() - n >= t && (yield* b.die(/* @__PURE__ */ Error(`could not acquire the postgres migration advisory lock within ${Math.round(t / 1e3)}s. Another migration is in progress, or a prior boot crashed while holding it. (A crashed process's session-level advisory lock is released once its DB connection closes; if this persists, find + terminate the holder via pg_stat_activity, or raise VOLTRO_MIGRATION_LOCK_TIMEOUT_MS.)`))), yield* b.sleep(`${de} millis`);
95
95
  }
96
- }), oe = (e) => p.gen(function* () {
97
- yield* e`SELECT pg_advisory_unlock(${ne.toString()}::bigint)`;
98
- }), se = `voltro_migration_${ne.toString(36)}`, ce = 30, le = (e) => p.gen(function* () {
96
+ }), me = (e) => b.gen(function* () {
97
+ yield* e`SELECT pg_advisory_unlock(${ue.toString()}::bigint)`;
98
+ }), he = `voltro_migration_${ue.toString(36)}`, ge = 30, _e = (e) => b.gen(function* () {
99
99
  (yield* e`
100
- SELECT GET_LOCK(${se}, ${ce}) AS got`)[0]?.got !== 1 && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${se}' within ${ce}s (another migration is in progress, or a prior one left it held).`)));
101
- }), ue = (e) => p.gen(function* () {
102
- yield* e`SELECT RELEASE_LOCK(${se})`;
103
- }), de = `voltro_migration_${ne.toString(36)}`, fe = 3e4, pe = (e) => p.gen(function* () {
100
+ SELECT GET_LOCK(${he}, ${ge}) AS got`)[0]?.got !== 1 && (yield* b.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${he}' within ${ge}s (another migration is in progress, or a prior one left it held).`)));
101
+ }), ve = (e) => b.gen(function* () {
102
+ yield* e`SELECT RELEASE_LOCK(${he})`;
103
+ }), ye = `voltro_migration_${ue.toString(36)}`, be = 3e4, xe = (e) => b.gen(function* () {
104
104
  let t = yield* e`
105
105
  DECLARE @res int;
106
- EXEC @res = sp_getapplock @Resource = ${de}, @LockMode = 'Exclusive',
107
- @LockOwner = 'Session', @LockTimeout = ${fe};
106
+ EXEC @res = sp_getapplock @Resource = ${ye}, @LockMode = 'Exclusive',
107
+ @LockOwner = 'Session', @LockTimeout = ${be};
108
108
  SELECT @res AS res`;
109
- (t[0]?.res ?? -999) < 0 && (yield* p.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${de}' (sp_getapplock returned ${t[0]?.res ?? "no row"}; another migration is in progress).`)));
110
- }), me = (e) => p.gen(function* () {
111
- yield* e`EXEC sp_releaseapplock @Resource = ${de}, @LockOwner = 'Session'`;
112
- }), he = p.void, E = (e) => e.onDialectOrElse({
113
- mysql: () => le(e),
114
- mssql: () => pe(e),
115
- sqlite: () => he,
116
- orElse: () => ae(e)
117
- }), D = (e) => e.onDialectOrElse({
118
- mysql: () => ue(e),
119
- mssql: () => me(e),
120
- sqlite: () => he,
121
- orElse: () => oe(e)
122
- }), ge = (e, t) => p.acquireUseRelease(E(e), () => t, () => D(e).pipe(p.orDie)), O = "framework_changes", k = "framework_changes_", _e = (e, t) => t === "framework_changes" ? `${k}${e}` : `${k}${t}_${e}`, ve = (e) => e === "framework_changes" ? "framework_notify_change" : `framework_notify_change_${e}`, ye = m({ scope: "voltro:migrate" }), A = (e) => {
123
- ye.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
124
- }, j = (e, n) => {
125
- let r = e.type;
109
+ (t[0]?.res ?? -999) < 0 && (yield* b.die(/* @__PURE__ */ Error(`could not acquire the migration lock '${ye}' (sp_getapplock returned ${t[0]?.res ?? "no row"}; another migration is in progress).`)));
110
+ }), Se = (e) => b.gen(function* () {
111
+ yield* e`EXEC sp_releaseapplock @Resource = ${ye}, @LockOwner = 'Session'`;
112
+ }), Ce = b.void, T = (e) => e.onDialectOrElse({
113
+ mysql: () => _e(e),
114
+ mssql: () => xe(e),
115
+ sqlite: () => Ce,
116
+ orElse: () => pe(e)
117
+ }), E = (e) => e.onDialectOrElse({
118
+ mysql: () => ve(e),
119
+ mssql: () => Se(e),
120
+ sqlite: () => Ce,
121
+ orElse: () => me(e)
122
+ }), we = (e, t) => b.acquireUseRelease(T(e), () => t, () => E(e).pipe(b.orDie)), D = "framework_changes", Te = "framework_changes_", Ee = (e, t) => t === "framework_changes" ? `${Te}${e}` : `${Te}${t}_${e}`, De = (e) => e === "framework_changes" ? "framework_notify_change" : `framework_notify_change_${e}`, Oe = "voltro:notify:v2", ke = x({ scope: "voltro:migrate" }), O = (e) => {
123
+ ke.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
124
+ }, k = (e, t) => {
125
+ let n = e.type;
126
126
  if (e.spatial) {
127
- if (n === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
128
- throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${n}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
127
+ if (t === "postgres") return `${e.spatial.kind}(${e.spatial.geomKind},${e.spatial.srid})`;
128
+ throw Error(`@voltro/plugin-postgis: ${e.spatial.kind}('${e.spatial.geomKind}', ${e.spatial.srid}) is postgres-only — the active dialect is '${t}'. PostGIS has no portable equivalent; use store: 'postgres' (or DB_DIALECT=postgres) for spatial columns, or model the value as plain text/json.`);
129
129
  }
130
- if (r === "raw") {
130
+ if (n === "raw") {
131
131
  if (e.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
132
132
  return e.rawDdl;
133
133
  }
134
- if (r === "array" && e.arrayElement) {
135
- if (n === "postgres") return `${j({
134
+ if (n === "array" && e.arrayElement) {
135
+ if (t === "postgres") return `${k({
136
136
  ...e,
137
137
  type: e.arrayElement,
138
138
  arrayElement: void 0
139
- }, n)}[]`;
140
- if (n === "mysql" || n === "mariadb") return "JSON";
141
- if (n === "mssql") return "NVARCHAR(MAX)";
142
- if (t(n)) return "TEXT";
143
- }
144
- if (r === "vector") {
145
- let r = e.vectorDim;
146
- if (r === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
139
+ }, t)}[]`;
140
+ if (t === "mysql" || t === "mariadb") return "JSON";
141
+ if (t === "mssql") return "NVARCHAR(MAX)";
142
+ if (r(t)) return "TEXT";
143
+ }
144
+ if (n === "vector") {
145
+ let n = e.vectorDim;
146
+ if (n === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
147
147
  let i = e.vectorPrecision === "half";
148
- if (n === "postgres") return i ? `HALFVEC(${r})` : `VECTOR(${r})`;
149
- if (n === "mysql" || n === "mariadb") return `VECTOR(${r})`;
150
- if (n === "mssql") return "VARBINARY(MAX)";
151
- if (t(n)) return "BLOB";
152
- }
153
- if (r === "interval") {
154
- if (n === "postgres") return "INTERVAL";
155
- if (n === "mysql" || n === "mariadb" || n === "mssql") return "BIGINT";
156
- if (t(n)) return "INTEGER";
157
- }
158
- if (r === "decimal") {
159
- let r = e.precision, i = e.scale ?? 0;
160
- if (r === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
161
- return t(n) ? "NUMERIC" : n === "postgres" ? `NUMERIC(${r}, ${i})` : `DECIMAL(${r}, ${i})`;
162
- }
163
- if (r === "bigint") return t(n) ? "INTEGER" : "BIGINT";
164
- if (r === "enum") {
165
- if (n === "postgres") return e.enumName ?? "TEXT";
166
- if (n === "mysql" || n === "mariadb") return `ENUM(${(e.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
167
- if (n === "mssql") return "NVARCHAR(255)";
168
- if (t(n)) return "TEXT";
169
- }
170
- if (r === "text" && e.maxLength !== void 0 && e.generatedAs === void 0) return n === "mssql" ? `NVARCHAR(${e.maxLength})` : t(n) ? "TEXT" : `VARCHAR(${e.maxLength})`;
171
- if (t(n)) switch (r) {
148
+ if (t === "postgres") return i ? `HALFVEC(${n})` : `VECTOR(${n})`;
149
+ if (t === "mysql" || t === "mariadb") return `VECTOR(${n})`;
150
+ if (t === "mssql") return "VARBINARY(MAX)";
151
+ if (r(t)) return "BLOB";
152
+ }
153
+ if (n === "interval") {
154
+ if (t === "postgres") return "INTERVAL";
155
+ if (t === "mysql" || t === "mariadb" || t === "mssql") return "BIGINT";
156
+ if (r(t)) return "INTEGER";
157
+ }
158
+ if (n === "decimal") {
159
+ let n = e.precision, i = e.scale ?? 0;
160
+ if (n === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
161
+ return r(t) ? "NUMERIC" : t === "postgres" ? `NUMERIC(${n}, ${i})` : `DECIMAL(${n}, ${i})`;
162
+ }
163
+ if (n === "bigint") return r(t) ? "INTEGER" : "BIGINT";
164
+ if (n === "enum") {
165
+ if (t === "postgres") return e.enumName ?? "TEXT";
166
+ if (t === "mysql" || t === "mariadb") return `ENUM(${(e.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
167
+ if (t === "mssql") return "NVARCHAR(255)";
168
+ if (r(t)) return "TEXT";
169
+ }
170
+ if (n === "text" && e.maxLength !== void 0 && e.generatedAs === void 0) return t === "mssql" ? `NVARCHAR(${e.maxLength})` : r(t) ? "TEXT" : `VARCHAR(${e.maxLength})`;
171
+ if (r(t)) switch (n) {
172
172
  case "id": return "TEXT";
173
173
  case "text": return "TEXT";
174
174
  case "integer": return "INTEGER";
@@ -184,7 +184,7 @@ var b = (e, t, n, r = 400) => {
184
184
  case "array": return "TEXT";
185
185
  case "interval": return "INTEGER";
186
186
  }
187
- if (n === "mysql" || n === "mariadb") switch (r) {
187
+ if (t === "mysql" || t === "mariadb") switch (n) {
188
188
  case "id": return "VARCHAR(64)";
189
189
  case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : e.generatedAs ? "TEXT" : "LONGTEXT";
190
190
  case "integer": return "INT";
@@ -200,7 +200,7 @@ var b = (e, t, n, r = 400) => {
200
200
  case "array": return "JSON";
201
201
  case "interval": return "BIGINT";
202
202
  }
203
- if (n === "mssql") switch (r) {
203
+ if (t === "mssql") switch (n) {
204
204
  case "id": return "NVARCHAR(64)";
205
205
  case "text": return e.hasDefault && e.defaultFactory === void 0 || e.oneOf && e.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
206
206
  case "integer": return "INT";
@@ -216,7 +216,7 @@ var b = (e, t, n, r = 400) => {
216
216
  case "array": return "NVARCHAR(MAX)";
217
217
  case "interval": return "BIGINT";
218
218
  }
219
- switch (r) {
219
+ switch (n) {
220
220
  case "id": return "TEXT";
221
221
  case "text": return "TEXT";
222
222
  case "integer": return "INTEGER";
@@ -232,22 +232,22 @@ var b = (e, t, n, r = 400) => {
232
232
  case "array": return "TEXT[]";
233
233
  case "interval": return "INTERVAL";
234
234
  }
235
- }, be = (e, n) => {
235
+ }, Ae = (e, t) => {
236
236
  if (!e.hasDefault || e.defaultFactory !== void 0) return null;
237
- let r = e.defaultValue;
238
- return r === "now" ? t(n) ? "DEFAULT CURRENT_TIMESTAMP" : n === "mssql" ? "DEFAULT SYSUTCDATETIME()" : n === "mysql" || n === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof r == "boolean" ? n === "postgres" ? `DEFAULT ${r}` : `DEFAULT ${+!!r}` : typeof r == "number" ? `DEFAULT ${r}` : typeof r == "string" ? `DEFAULT '${r.replace(/'/g, "''")}'` : Array.isArray(r) ? xe(r, e, n) : typeof r == "object" && r ? M(r, n) : null;
239
- }, xe = (e, t, n) => {
240
- if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return M(e, n);
241
- let r = j({
237
+ let n = e.defaultValue;
238
+ return n === "now" ? r(t) ? "DEFAULT CURRENT_TIMESTAMP" : t === "mssql" ? "DEFAULT SYSUTCDATETIME()" : t === "mysql" || t === "mariadb" ? "DEFAULT CURRENT_TIMESTAMP(6)" : "DEFAULT now()" : typeof n == "boolean" ? t === "postgres" ? `DEFAULT ${n}` : `DEFAULT ${+!!n}` : typeof n == "number" ? `DEFAULT ${n}` : typeof n == "string" ? `DEFAULT '${n.replace(/'/g, "''")}'` : Array.isArray(n) ? je(n, e, t) : typeof n == "object" && n ? A(n, t) : null;
239
+ }, je = (e, t, n) => {
240
+ if (n !== "postgres" || t.type !== "array" || t.arrayElement === void 0) return A(e, n);
241
+ let r = k({
242
242
  ...t,
243
243
  type: t.arrayElement,
244
244
  arrayElement: void 0
245
245
  }, n);
246
246
  return `DEFAULT '{${e.map((e) => typeof e == "number" || typeof e == "boolean" ? String(e) : `"${String(e).replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`).join(",").replace(/'/g, "''")}}'::${r}[]`;
247
- }, M = (e, t) => {
247
+ }, A = (e, t) => {
248
248
  let n = JSON.stringify(e).replace(/'/g, "''");
249
249
  return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CONVERT('${n}' USING utf8mb4))` : `DEFAULT '${n}'`;
250
- }, N = (e, t) => {
250
+ }, j = (e, t) => {
251
251
  switch (t) {
252
252
  case "postgres": return `"${e}" BIGSERIAL PRIMARY KEY`;
253
253
  case "mysql":
@@ -256,7 +256,7 @@ var b = (e, t, n, r = 400) => {
256
256
  case "sqlite": return `"${e}" INTEGER PRIMARY KEY AUTOINCREMENT`;
257
257
  case "turso": return `"${e}" INTEGER PRIMARY KEY`;
258
258
  }
259
- }, Se = (e, t) => {
259
+ }, Me = (e, t) => {
260
260
  if (e === "restrict" && t === "mssql") return "NO ACTION";
261
261
  switch (e) {
262
262
  case "cascade": return "CASCADE";
@@ -264,175 +264,177 @@ var b = (e, t, n, r = 400) => {
264
264
  case "setNull": return "SET NULL";
265
265
  case "noAction": return "NO ACTION";
266
266
  }
267
- }, Ce = (e, t, r) => r === null ? n(e, t) : `${n(r, t)}.${n(e, t)}`, we = 191, Te = (e, t, r) => {
268
- let i = n(e, r);
269
- if (r !== "mysql" && r !== "mariadb") return i;
267
+ }, Ne = (e, t, n) => n === null ? i(e, t) : `${i(n, t)}.${i(e, t)}`, Pe = 191, Fe = (e, t, n) => {
268
+ let r = i(e, n);
269
+ if (n !== "mysql" && n !== "mariadb") return r;
270
270
  let a = t[e];
271
- if (!a) return i;
272
- let o = j(a, r).toUpperCase();
273
- return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${i}(${we})` : i;
274
- }, Ee = (e, t) => {
271
+ if (!a) return r;
272
+ let o = k(a, n).toUpperCase();
273
+ return o.endsWith("TEXT") || o.endsWith("BLOB") ? `${r}(${Pe})` : r;
274
+ }, Ie = (e, t) => {
275
275
  if (!e.generatedAs) return null;
276
276
  let { expr: n, stored: r } = e.generatedAs;
277
- if (t === "postgres") return r || A("[voltro:migrate] generated column requested as VIRTUAL but postgres only supports STORED — promoting"), `GENERATED ALWAYS AS (${n}) STORED`;
277
+ if (t === "postgres") return r || O("[voltro:migrate] generated column requested as VIRTUAL but postgres only supports STORED — promoting"), `GENERATED ALWAYS AS (${n}) STORED`;
278
278
  if (t === "mysql" || t === "mariadb") return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
279
279
  if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
280
280
  if (t === "turso") throw Error("@voltro/sql-turso: generated columns (.generatedAs(...)) are not supported — Turso's MVCC mode (journal_mode=experimental_mvcc) rejects them. Drop the generated column, or use the 'sqlite' / 'postgres' dialect for this table.");
281
281
  return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
282
- }, De = (e, r, i, a, o = !1, s = !1) => {
283
- if (r.type === "id" && r.idScheme?.kind === "numeric") return N(e, i);
284
- let c = i === "mssql" && r.type === "text" && !r.generatedAs && r.maxLength === void 0 && a?.has(e) ? "NVARCHAR(450)" : j(r, i), l = [n(e, i), c];
285
- r.type === "id" && !s && l.push("PRIMARY KEY");
286
- let u = Ee(r, i);
282
+ }, Le = (e, t, n, a, o = !1, s = !1) => {
283
+ if (t.type === "id" && t.idScheme?.kind === "numeric") return j(e, n);
284
+ let c = n === "mssql" && t.type === "text" && !t.generatedAs && t.maxLength === void 0 && a?.has(e) ? "NVARCHAR(450)" : k(t, n), l = [i(e, n), c];
285
+ t.type === "id" && !s && l.push("PRIMARY KEY");
286
+ let u = Ie(t, n);
287
287
  if (u) l.push(u);
288
288
  else {
289
- r.nullable || l.push("NOT NULL"), r.unique && r.type !== "id" && l.push("UNIQUE");
290
- let e = be(r, i);
289
+ t.nullable || l.push("NOT NULL"), t.unique && t.type !== "id" && l.push("UNIQUE");
290
+ let e = Ae(t, n);
291
291
  e && l.push(e);
292
292
  }
293
- if (r.type === "reference" && r.references && !o) {
294
- let e = r.references();
295
- l.push(`REFERENCES ${n(e.tableName, i)} (${n("id", i)})`);
296
- let t = Se(r.onDelete ?? "restrict", i);
297
- l.push(`ON DELETE ${t}`);
298
- let a = Se(r.refOnUpdate ?? "noAction", i);
293
+ if (t.type === "reference" && t.references && !o) {
294
+ let e = t.references();
295
+ l.push(`REFERENCES ${i(e.tableName, n)} (${i("id", n)})`);
296
+ let r = Me(t.onDelete ?? "restrict", n);
297
+ l.push(`ON DELETE ${r}`);
298
+ let a = Me(t.refOnUpdate ?? "noAction", n);
299
299
  a !== "NO ACTION" && l.push(`ON UPDATE ${a}`);
300
300
  }
301
- if (r.oneOf && r.oneOf.length > 0) {
302
- let t = r.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
303
- l.push(`CHECK (${n(e, i)} IN (${t}))`);
301
+ if (t.oneOf && t.oneOf.length > 0) {
302
+ let r = t.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
303
+ l.push(`CHECK (${i(e, n)} IN (${r}))`);
304
304
  }
305
- if (r.checks && r.checks.length > 0) for (let e of r.checks) l.push(`CHECK (${e})`);
306
- if (r.type === "enum" && r.enumValues && r.enumValues.length > 0 && (i === "mssql" || t(i))) {
307
- let t = r.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
308
- l.push(`CHECK (${n(e, i)} IN (${t}))`);
305
+ if (t.checks && t.checks.length > 0) for (let e of t.checks) l.push(`CHECK (${e})`);
306
+ if (t.type === "enum" && t.enumValues && t.enumValues.length > 0 && (n === "mssql" || r(n))) {
307
+ let r = t.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
308
+ l.push(`CHECK (${i(e, n)} IN (${r}))`);
309
309
  }
310
- if (r.type === "json" || r.type === "array") {
311
- let r = n(e, i);
312
- t(i) ? l.push(`CHECK (${r} IS NULL OR json_valid(${r}))`) : i === "mssql" && l.push(`CHECK (${r} IS NULL OR ISJSON(${r}) = 1)`);
310
+ if (t.type === "json" || t.type === "array") {
311
+ let t = i(e, n);
312
+ r(n) ? l.push(`CHECK (${t} IS NULL OR json_valid(${t}))`) : n === "mssql" && l.push(`CHECK (${t} IS NULL OR ISJSON(${t}) = 1)`);
313
313
  }
314
314
  return l.join(" ");
315
- }, Oe = (e, t, r = null, i) => {
315
+ }, Re = (e, t, n = null, r) => {
316
316
  let a = /* @__PURE__ */ new Set();
317
317
  for (let t of e.appliedIndexes ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
318
318
  for (let t of e.appliedUniques ?? []) for (let e of t.fields) typeof e == "string" && a.add(e);
319
- let o = e.appliedPrimaryKey ?? [], s = o.length > 0, c = Object.entries(e.fields).map(([e, n]) => ` ${De(e, n, t, a, i?.has(e) ?? !1, s)}`).join(",\n"), l = (e.appliedUniques ?? []).map((r) => {
320
- let i = r.fields.map((n) => Te(n, e.fields, t)).join(", ");
321
- return ` CONSTRAINT ${n(r.name, t)} UNIQUE (${i})`;
322
- }), u = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${n(e.name, t)} CHECK (${e.expr})`), d = [
323
- ...s ? [` PRIMARY KEY (${o.map((n) => Te(n, e.fields, t)).join(", ")})`] : [],
319
+ let o = e.appliedPrimaryKey ?? [], s = o.length > 0, c = Object.entries(e.fields).map(([e, n]) => ` ${Le(e, n, t, a, r?.has(e) ?? !1, s)}`).join(",\n"), l = (e.appliedUniques ?? []).map((n) => {
320
+ let r = n.fields.map((n) => Fe(n, e.fields, t)).join(", ");
321
+ return ` CONSTRAINT ${i(n.name, t)} UNIQUE (${r})`;
322
+ }), u = (e.appliedChecks ?? []).map((e) => ` CONSTRAINT ${i(e.name, t)} CHECK (${e.expr})`), d = [
323
+ ...s ? [` PRIMARY KEY (${o.map((n) => Fe(n, e.fields, t)).join(", ")})`] : [],
324
324
  ...l,
325
325
  ...u
326
- ], f = d.length > 0 ? `${c},\n${d.join(",\n")}` : c, p = Ce(e.tableName, t, r);
326
+ ], f = d.length > 0 ? `${c},\n${d.join(",\n")}` : c, p = Ne(e.tableName, t, n);
327
327
  if (t === "mssql") {
328
- let t = r === null ? e.tableName : `${r}.${e.tableName}`, n = `CREATE TABLE ${p} (\n${f}\n)`;
329
- return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${n.replace(/'/g, "''")}');`;
328
+ let t = n === null ? e.tableName : `${n}.${e.tableName}`, r = `CREATE TABLE ${p} (\n${f}\n)`;
329
+ return `IF OBJECT_ID('${t.replace(/'/g, "''")}', 'U') IS NULL EXEC(N'${r.replace(/'/g, "''")}');`;
330
330
  }
331
331
  return [
332
332
  `CREATE TABLE IF NOT EXISTS ${p} (`,
333
333
  f,
334
334
  ");"
335
335
  ].join("\n");
336
- }, ke = (e, t, r) => {
337
- let i = `${e}_${t.name}_fts`, a = (e) => n(e, r), o = t.columns.map(a).join(", "), s = t.columns.map((e) => `new.${a(e)}`).join(", "), c = [];
338
- return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(i)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ai`)} AFTER INSERT ON ${a(e)} BEGIN\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_ad`)} AFTER DELETE ON ${a(e)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${i}_au`)} AFTER UPDATE ON ${a(e)} BEGIN\n DELETE FROM ${a(i)} WHERE row_id = old.${a("id")};\n INSERT INTO ${a(i)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c;
339
- }, Ae = (t, r, i = null) => {
340
- let a = [], o = /* @__PURE__ */ new Set(), s = (e) => e.join("\0"), c = (t, n, i, a) => {
341
- let o = {
336
+ }, ze = (e, t, n) => {
337
+ let r = `${e}_${t.name}_fts`, a = (e) => i(e, n), o = t.columns.map(a).join(", "), s = t.columns.map((e) => `new.${a(e)}`).join(", "), c = [];
338
+ return c.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${a(r)} USING fts5(${o}, row_id UNINDEXED, tokenize='porter');`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${r}_ai`)} AFTER INSERT ON ${a(e)} BEGIN\n INSERT INTO ${a(r)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${r}_ad`)} AFTER DELETE ON ${a(e)} BEGIN\n DELETE FROM ${a(r)} WHERE row_id = old.${a("id")};\nEND;`), c.push(`CREATE TRIGGER IF NOT EXISTS ${a(`${r}_au`)} AFTER UPDATE ON ${a(e)} BEGIN\n DELETE FROM ${a(r)} WHERE row_id = old.${a("id")};\n INSERT INTO ${a(r)} (${o}, row_id) VALUES (${s}, new.${a("id")});\nEND;`), c;
339
+ }, Be = (e, t, n = null) => {
340
+ let r = [], a = /* @__PURE__ */ new Set(), o = (e) => e.join("\0"), s = (e, n, r, i) => {
341
+ let a = {
342
342
  using: "",
343
343
  withSuffix: "",
344
344
  skip: !1
345
345
  };
346
- if (i === void 0 || i === "btree") return o;
347
- if (i === "gist") return r === "postgres" ? {
346
+ if (r === void 0 || r === "btree") return a;
347
+ if (r === "gist") return t === "postgres" ? {
348
348
  using: " USING GIST",
349
349
  withSuffix: "",
350
350
  skip: !1
351
- } : (A(`[voltro:migrate] index '${t}' on '${n}': ${r} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), o);
352
- if (i === "gin") return r === "postgres" ? {
351
+ } : (O(`[voltro:migrate] index '${e}' on '${n}': ${t} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), a);
352
+ if (r === "gin") return t === "postgres" ? {
353
353
  using: " USING GIN",
354
354
  withSuffix: "",
355
355
  skip: !1
356
- } : r === "mysql" || r === "mariadb" ? (A(`[voltro:migrate] gin index '${t}' on '${n}': ${r} has no GIN access method — skipping (a btree on a JSON/array column can't serve containment/membership queries). Add a generated column for the specific path you query, then index that with .expressionIndex(...).`), {
356
+ } : t === "mysql" || t === "mariadb" ? (O(`[voltro:migrate] gin index '${e}' on '${n}': ${t} has no GIN access method — skipping (a btree on a JSON/array column can't serve containment/membership queries). Add a generated column for the specific path you query, then index that with .expressionIndex(...).`), {
357
357
  using: "",
358
358
  withSuffix: "",
359
359
  skip: !0
360
- }) : (A(`[voltro:migrate] gin index '${t}' on '${n}': ${r} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), o);
361
- if (i === "brin") return r === "postgres" ? {
360
+ }) : (O(`[voltro:migrate] gin index '${e}' on '${n}': ${t} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), a);
361
+ if (r === "brin") return t === "postgres" ? {
362
362
  using: " USING BRIN",
363
363
  withSuffix: "",
364
364
  skip: !1
365
- } : (A(`[voltro:migrate] brin index '${t}' on '${n}': ${r} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), o);
366
- if (i === "hnsw") {
367
- if (r === "postgres") {
368
- let t = a?.hnsw?.m ?? 16, n = a?.hnsw?.efConstruction ?? 64, r = a?.hnsw?.opclass ?? e(a?.hnsw?.distance);
365
+ } : (O(`[voltro:migrate] brin index '${e}' on '${n}': ${t} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), a);
366
+ if (r === "hnsw") {
367
+ if (t === "postgres") {
368
+ let e = i?.hnsw?.m ?? 16, t = i?.hnsw?.efConstruction ?? 64, n = i?.hnsw?.opclass ?? y(i?.hnsw?.distance);
369
369
  return {
370
370
  using: " USING hnsw",
371
- withSuffix: ` WITH (m = ${t}, ef_construction = ${n})`,
372
- opclass: r,
371
+ withSuffix: ` WITH (m = ${e}, ef_construction = ${t})`,
372
+ opclass: n,
373
373
  skip: !1
374
374
  };
375
375
  }
376
- return A(`[voltro:migrate] hnsw index '${t}' on '${n}': ${r} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
376
+ return O(`[voltro:migrate] hnsw index '${e}' on '${n}': ${t} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
377
377
  using: "",
378
378
  withSuffix: "",
379
379
  skip: !0
380
380
  };
381
381
  }
382
- return o;
383
- }, l = (e, n) => {
384
- let i = e.map((e) => {
385
- if (typeof e == "string") return Te(e, t.fields, r);
386
- if ("jsonPath" in e) {
387
- let { column: t, path: n, numeric: i } = e.jsonPath;
388
- return `(${d(t, n, r, { numeric: i })})`;
382
+ return a;
383
+ }, c = (n, r) => {
384
+ let i = n.map((n) => {
385
+ if (typeof n == "string") return Fe(n, e.fields, t);
386
+ if ("jsonPath" in n) {
387
+ let { column: e, path: r, numeric: i } = n.jsonPath;
388
+ return `(${g(e, r, t, { numeric: i })})`;
389
389
  }
390
- return `(${e.expr})`;
390
+ return `(${n.expr})`;
391
391
  });
392
- return n !== void 0 && i.length === 1 ? `${i[0]} ${n}` : i.join(", ");
393
- }, u = (e, t, a, o, s, u = "", d = !1) => {
394
- let f = c(e, t, o, s);
392
+ return r !== void 0 && i.length === 1 ? `${i[0]} ${r}` : i.join(", ");
393
+ }, l = (e, r, a, o, l, u = "", d = !1) => {
394
+ let f = s(e, r, o, l);
395
395
  if (f.skip) return "";
396
- let p = l(a, f.opclass), m = `${f.withSuffix}${u}`, h = Ce(t, r, i), g = d ? "UNIQUE " : "";
397
- return r === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${n(e, r)} ON ${h}${f.using} (${p})${m};` : r === "mysql" ? `CREATE ${g}INDEX ${n(e, r)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${n(e, r)} ON ${h}${f.using} (${p})${m};`;
396
+ let p = c(a, f.opclass), m = `${f.withSuffix}${u}`, h = Ne(r, t, n), g = d ? "UNIQUE " : "";
397
+ return t === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${i(e, t)} ON ${h}${f.using} (${p})${m};` : t === "mysql" ? `CREATE ${g}INDEX ${i(e, t)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${i(e, t)} ON ${h}${f.using} (${p})${m};`;
398
398
  };
399
- for (let e of t.appliedFullText ?? []) if (r === "postgres") {
400
- let i = e.config ?? "english", o = `${e.name}_tsv`, s = e.columns.map((t) => {
401
- let a = e.weights?.[t] ?? "A";
402
- return `setweight(to_tsvector('${i}', coalesce(${n(t, r)}, '')), '${a}')`;
399
+ for (let n of e.appliedFullText ?? []) if (t === "postgres") {
400
+ let a = n.config ?? "english", o = `${n.name}_tsv`, s = n.columns.map((e) => {
401
+ let r = n.weights?.[e] ?? "A";
402
+ return `setweight(to_tsvector('${a}', coalesce(${i(e, t)}, '')), '${r}')`;
403
403
  }).join(" || ");
404
- a.push(`ALTER TABLE ${n(t.tableName, r)} ADD COLUMN IF NOT EXISTS ${n(o, r)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), a.push(`CREATE INDEX IF NOT EXISTS ${n(e.name, r)} ON ${n(t.tableName, r)} USING GIN (${n(o, r)});`);
405
- } else if (r === "mysql" || r === "mariadb") {
406
- let i = e.columns.map((e) => n(e, r)).join(", ");
407
- a.push(`CREATE FULLTEXT INDEX ${n(e.name, r)} ON ${n(t.tableName, r)} (${i});`);
408
- } else if (r === "turso") throw Error(`@voltro/sql-turso: full-text index '${e.name}' on '${t.tableName}' is not supported — Turso's MVCC mode has no FTS5 virtual tables. Drop .fullTextIndex(), or use the 'sqlite' / 'postgres' dialect for this table.`);
409
- else r === "sqlite" ? a.push(...ke(t.tableName, e, r)) : A(`[voltro:migrate] full-text index '${e.name}' on '${t.tableName}': ${r} uses a ranked LIKE search at query time (no native FTS catalog required).`);
410
- for (let e of t.appliedIndexes) {
411
- if ((r === "mariadb" || r === "mssql") && e.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
412
- A(`[voltro:migrate] json-path index '${e.name}' on '${t.tableName}': ${r} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${r}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
404
+ r.push(`ALTER TABLE ${i(e.tableName, t)} ADD COLUMN IF NOT EXISTS ${i(o, t)} tsvector GENERATED ALWAYS AS (${s}) STORED;`), r.push(`CREATE INDEX IF NOT EXISTS ${i(n.name, t)} ON ${i(e.tableName, t)} USING GIN (${i(o, t)});`);
405
+ } else if (t === "mysql" || t === "mariadb") {
406
+ let a = n.columns.map((e) => i(e, t)).join(", ");
407
+ r.push(`CREATE FULLTEXT INDEX ${i(n.name, t)} ON ${i(e.tableName, t)} (${a});`);
408
+ } else if (t === "turso") throw Error(`@voltro/sql-turso: full-text index '${n.name}' on '${e.tableName}' is not supported — Turso's MVCC mode has no FTS5 virtual tables. Drop .fullTextIndex(), or use the 'sqlite' / 'postgres' dialect for this table.`);
409
+ else t === "sqlite" ? r.push(...ze(e.tableName, n, t)) : O(`[voltro:migrate] full-text index '${n.name}' on '${e.tableName}': ${t} uses a ranked LIKE search at query time (no native FTS catalog required).`);
410
+ for (let n of e.appliedIndexes) {
411
+ if ((t === "mariadb" || t === "mssql") && n.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
412
+ O(`[voltro:migrate] json-path index '${n.name}' on '${e.tableName}': ${t} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${t}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
413
413
  continue;
414
414
  }
415
- if (e.unique && e.where !== void 0 && e.where.length > 0 && (r === "mysql" || r === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${e.name}' on '${t.tableName}' cannot be expressed on ${r} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${r}, or move this table to postgres/sqlite/mssql.`);
416
- let n = "";
417
- e.where !== void 0 && e.where.length > 0 && (r === "mysql" || r === "mariadb" ? A(`[voltro:migrate] partial index '${e.name}' on '${t.tableName}': ${r} does not support WHERE clauses on indexes — emitting a full index`) : n = ` WHERE ${e.where}`);
418
- let i = u(e.name, t.tableName, e.fields, e.kind, e.kindOptions, n, e.unique === !0);
419
- i !== "" && a.push(i), e.fields.every((e) => typeof e == "string") && o.add(s(e.fields));
420
- }
421
- for (let [e, n] of Object.entries(t.fields)) {
422
- if (n.type !== "reference" || n.fkAutoIndex === !1 || o.has(s([e]))) continue;
423
- let r = `${t.tableName}_${e}_idx`;
424
- a.push(u(r, t.tableName, [e])), o.add(s([e]));
425
- }
426
- return a.join("\n");
427
- }, je = (e, t, n = O) => {
415
+ if (n.unique && n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${n.name}' on '${e.tableName}' cannot be expressed on ${t} — it has no partial (WHERE) index support, and a full UNIQUE index would forbid re-creating a soft-deleted row. Use a generated STORED column (text().generatedAs("CASE WHEN \`deletedAt\` IS NULL THEN <key> ELSE NULL END", { stored: true })) + .unique([...]) on ${t}, or move this table to postgres/sqlite/mssql.`);
416
+ let i = "";
417
+ n.where !== void 0 && n.where.length > 0 && (t === "mysql" || t === "mariadb" ? O(`[voltro:migrate] partial index '${n.name}' on '${e.tableName}': ${t} does not support WHERE clauses on indexes — emitting a full index`) : i = ` WHERE ${n.where}`);
418
+ let s = l(n.name, e.tableName, n.fields, n.kind, n.kindOptions, i, n.unique === !0);
419
+ s !== "" && r.push(s), n.fields.every((e) => typeof e == "string") && a.add(o(n.fields));
420
+ }
421
+ for (let [t, n] of Object.entries(e.fields)) {
422
+ if (n.type !== "reference" || n.fkAutoIndex === !1 || a.has(o([t]))) continue;
423
+ let i = `${e.tableName}_${t}_idx`;
424
+ r.push(l(i, e.tableName, [t])), a.add(o([t]));
425
+ }
426
+ return r.join("\n");
427
+ }, Ve = (e, t, n = D) => {
428
428
  if (t !== "postgres") return [];
429
429
  let r = e.filter((e) => e.isReactive !== !1);
430
- return r.length === 0 ? [] : [Me(n), ...r.map((e) => Pe(e, n))];
431
- }, Me = (e) => `
432
- CREATE OR REPLACE FUNCTION ${ve(e)}() RETURNS trigger AS $$
430
+ return r.length === 0 ? [] : [He(n), ...r.map((e) => We(e, n))];
431
+ }, He = (e) => `
432
+ CREATE OR REPLACE FUNCTION ${De(e)}() RETURNS trigger AS $$
433
433
  DECLARE
434
434
  payload TEXT;
435
+ pk JSONB;
435
436
  BEGIN
437
+ -- ${Oe}
436
438
  payload := json_build_object(
437
439
  'table', TG_TABLE_NAME,
438
440
  -- The schema the write landed in. Under namespace isolation this is
@@ -445,42 +447,63 @@ BEGIN
445
447
  'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END
446
448
  )::text;
447
449
  -- pg_notify hard-caps the payload at 8000 bytes; a wide reactive row
448
- -- (big text / json column) would otherwise abort the write itself.
449
- -- Fall back to a body-less notice the CDC consumer treats absent
450
- -- old/new as "all columns changed" and re-queries, so correctness is
451
- -- preserved; only the delta-diff fast path is skipped for that write.
450
+ -- (big text / json column) would otherwise abort the write itself. So the
451
+ -- images are dropped but the PRIMARY KEY is kept, and that is the whole
452
+ -- difference between a slow path and data loss. A subscription re-queries on
453
+ -- an image-less event and is fine either way; a TAP (search index, analytics
454
+ -- mirror, cdc-out, row history) has nothing to apply and used to drop the
455
+ -- change silently and forever. With the key, the CDC consumer re-reads the
456
+ -- row before anyone sees the event (sql-postgres/src/cdcRehydrate.ts).
457
+ --
458
+ -- to_jsonb(REC) -> 'id' rather than REC.id: a reactive table with no id
459
+ -- column would make the field reference a runtime error inside the trigger,
460
+ -- which aborts the user's WRITE. The json operator answers NULL instead.
452
461
  IF octet_length(payload) >= 7999 THEN
462
+ pk := CASE WHEN TG_OP = 'DELETE' THEN to_jsonb(OLD) -> 'id' ELSE to_jsonb(NEW) -> 'id' END;
453
463
  payload := json_build_object(
454
464
  'table', TG_TABLE_NAME,
455
465
  'schema', TG_TABLE_SCHEMA,
456
466
  'op', lower(TG_OP),
457
467
  'old', NULL,
458
468
  'new', NULL,
469
+ 'id', pk,
459
470
  'oversized', true
460
471
  )::text;
472
+ -- A key can itself be oversized (a pathological text primary key). Carrying
473
+ -- it would put us back where the cap check started: pg_notify raises, and
474
+ -- the trigger takes the user's write down with it. Dropping it costs the
475
+ -- re-read and is counted as unrecovered — never the write.
476
+ IF octet_length(payload) >= 7999 THEN
477
+ payload := json_build_object(
478
+ 'table', TG_TABLE_NAME,
479
+ 'schema', TG_TABLE_SCHEMA,
480
+ 'op', lower(TG_OP),
481
+ 'old', NULL,
482
+ 'new', NULL,
483
+ 'oversized', true
484
+ )::text;
485
+ END IF;
461
486
  END IF;
462
487
  PERFORM pg_notify('${e}', payload);
463
488
  RETURN COALESCE(NEW, OLD);
464
489
  END;
465
490
  $$ LANGUAGE plpgsql;
466
- `.trim(), Ne = (e, t) => {
467
- let n = _e(e.tableName, t);
491
+ `.trim(), Ue = (e, t) => {
492
+ let n = Ee(e.tableName, t);
468
493
  return [
469
494
  `ALTER TABLE "${e.tableName}" REPLICA IDENTITY FULL`,
470
495
  `DROP TRIGGER IF EXISTS ${n} ON "${e.tableName}"`,
471
- `CREATE TRIGGER ${n}\nAFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"\nFOR EACH ROW EXECUTE FUNCTION ${ve(t)}()`
496
+ `CREATE TRIGGER ${n}\nAFTER INSERT OR UPDATE OR DELETE ON "${e.tableName}"\nFOR EACH ROW EXECUTE FUNCTION ${De(t)}()`
472
497
  ];
473
- }, Pe = (e, t) => Ne(e, t).map((e) => `${e};`).join("\n"), Fe = (e) => {
498
+ }, We = (e, t) => Ue(e, t).map((e) => `${e};`).join("\n"), Ge = (e) => {
474
499
  if (e.dialect !== "postgres") return [];
475
500
  let t = e.channel ?? "framework_changes", n = new Map(e.tables.map((e) => [e.tableName, e])), r = [], i = e.missing.filter((e) => n.has(e));
476
- if (i.length > 0) {
477
- r.push(Me(t));
478
- for (let e of i) r.push(...Ne(n.get(e), t));
479
- }
480
- for (let n of e.stale) r.push(`DROP TRIGGER IF EXISTS ${_e(n, t)} ON "${n}"`), r.push(`ALTER TABLE "${n}" REPLICA IDENTITY DEFAULT`);
501
+ (i.length > 0 || e.functionOutdated === !0) && r.push(He(t));
502
+ for (let e of i) r.push(...Ue(n.get(e), t));
503
+ for (let n of e.stale) r.push(`DROP TRIGGER IF EXISTS ${Ee(n, t)} ON "${n}"`), r.push(`ALTER TABLE "${n}" REPLICA IDENTITY DEFAULT`);
481
504
  return r;
482
- }, Ie = (e) => e.some((e) => Object.values(e.fields).some((e) => e.type === "vector")), Le = (e) => e.some((e) => Object.values(e.fields).some((e) => e.spatial !== void 0)), Re = (e) => {
483
- let t = new Map(e.map((e) => [e.tableName, e])), n = te(e.map((e) => e.tableName), (e) => {
505
+ }, Ke = (e) => e.some((e) => Object.values(e.fields).some((e) => e.type === "vector")), qe = (e) => e.some((e) => Object.values(e.fields).some((e) => e.spatial !== void 0)), Je = (e) => {
506
+ let t = new Map(e.map((e) => [e.tableName, e])), n = le(e.map((e) => e.tableName), (e) => {
484
507
  let n = t.get(e);
485
508
  return n ? Object.entries(n.fields).flatMap(([e, n]) => {
486
509
  if (n.type !== "reference" || !n.references) return [];
@@ -491,7 +514,7 @@ $$ LANGUAGE plpgsql;
491
514
  }] : [];
492
515
  }) : [];
493
516
  }), r = [];
494
- for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(T(t.tableName, e)) && r.push({
517
+ for (let t of e) for (let [e, i] of Object.entries(t.fields)) i.type !== "reference" || !i.references || n.has(w(t.tableName, e)) && r.push({
495
518
  table: t.tableName,
496
519
  column: e,
497
520
  targetTable: i.references().tableName,
@@ -502,7 +525,7 @@ $$ LANGUAGE plpgsql;
502
525
  if (!(i.has(e.tableName) || a.has(e.tableName))) {
503
526
  a.add(e.tableName);
504
527
  for (let [r, i] of Object.entries(e.fields)) {
505
- if (i.type !== "reference" || !i.references || n.has(T(e.tableName, r))) continue;
528
+ if (i.type !== "reference" || !i.references || n.has(w(e.tableName, r))) continue;
506
529
  let a = i.references();
507
530
  if (a.tableName === e.tableName) continue;
508
531
  let o = t.get(a.tableName);
@@ -516,30 +539,30 @@ $$ LANGUAGE plpgsql;
516
539
  sorted: o,
517
540
  cyclic: r
518
541
  };
519
- }, ze = (e, t, r = null) => {
520
- let i = (e) => r ? `${n(r, t)}.${n(e, t)}` : n(e, t), a = `${e.table}_${e.column}_fkey`, o = Se(e.onDelete, t), s = Se(e.onUpdate, t), c = ` ON DELETE ${o}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (r) => `ADD CONSTRAINT ${n(a, t)} FOREIGN KEY ${r ? "IF NOT EXISTS " : ""}(${n(e.column, t)}) REFERENCES ${i(e.targetTable)} (${n("id", t)})${c}`;
542
+ }, Ye = (e, t, n = null) => {
543
+ let r = (e) => n ? `${i(n, t)}.${i(e, t)}` : i(e, t), a = `${e.table}_${e.column}_fkey`, o = Me(e.onDelete, t), s = Me(e.onUpdate, t), c = ` ON DELETE ${o}${s === "NO ACTION" ? "" : ` ON UPDATE ${s}`}`, l = (n) => `ADD CONSTRAINT ${i(a, t)} FOREIGN KEY ${n ? "IF NOT EXISTS " : ""}(${i(e.column, t)}) REFERENCES ${r(e.targetTable)} (${i("id", t)})${c}`;
521
544
  switch (t) {
522
545
  case "postgres": return [
523
546
  "DO $$ BEGIN",
524
547
  " IF NOT EXISTS (SELECT 1 FROM pg_constraint",
525
- ` WHERE conname = '${a}' AND conrelid = '${r ? `${r}.` : ""}${e.table}'::regclass) THEN`,
526
- ` ALTER TABLE ${i(e.table)} ${l(!1)};`,
548
+ ` WHERE conname = '${a}' AND conrelid = '${n ? `${n}.` : ""}${e.table}'::regclass) THEN`,
549
+ ` ALTER TABLE ${r(e.table)} ${l(!1)};`,
527
550
  " END IF;",
528
551
  "END $$;"
529
552
  ].join("\n");
530
- case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${i(e.table)} ${l(!1)};`;
531
- case "mariadb": return `ALTER TABLE ${i(e.table)} ${l(!0)};`;
532
- default: return `ALTER TABLE ${i(e.table)} ${l(!1)};`;
553
+ case "mssql": return `IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = '${a}') ALTER TABLE ${r(e.table)} ${l(!1)};`;
554
+ case "mariadb": return `ALTER TABLE ${r(e.table)} ${l(!0)};`;
555
+ default: return `ALTER TABLE ${r(e.table)} ${l(!1)};`;
533
556
  }
534
- }, Be = (e, n) => {
535
- let r = /* @__PURE__ */ new Map();
536
- if (t(n)) return r;
557
+ }, Xe = (e, t) => {
558
+ let n = /* @__PURE__ */ new Map();
559
+ if (r(t)) return n;
537
560
  for (let t of e) {
538
- let e = r.get(t.table) ?? /* @__PURE__ */ new Set();
539
- e.add(t.column), r.set(t.table, e);
561
+ let e = n.get(t.table) ?? /* @__PURE__ */ new Set();
562
+ e.add(t.column), n.set(t.table, e);
540
563
  }
541
- return r;
542
- }, Ve = (e, t) => {
564
+ return n;
565
+ }, Ze = (e, t) => {
543
566
  if (t !== "postgres") return "";
544
567
  let n = /* @__PURE__ */ new Map();
545
568
  for (let t of e) for (let e of Object.values(t.fields)) {
@@ -555,12 +578,12 @@ $$ LANGUAGE plpgsql;
555
578
  r.push(`DO $$ BEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = '${e.replace(/'/g, "''")}') THEN\n CREATE TYPE "${e}" AS ENUM (${n});\n END IF;\nEND $$;`);
556
579
  }
557
580
  return r.join("\n\n");
558
- }, He = (e, t) => {
581
+ }, Qe = (e, t) => {
559
582
  if (t !== "postgres") return "";
560
583
  let n = /* @__PURE__ */ new Set();
561
584
  for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
562
585
  let r = [];
563
- for (let e of n) for (let { from: t, to: n } of l(e)) {
586
+ for (let e of n) for (let { from: t, to: n } of s(e)) {
564
587
  let i = e.replace(/'/g, "''"), a = t.replace(/'/g, "''"), o = n.replace(/'/g, "''");
565
588
  r.push(`DO \$\$
566
589
  DECLARE
@@ -569,71 +592,71 @@ BEGIN
569
592
  SELECT oid INTO type_oid FROM pg_type WHERE typname = '${i}';\n IF type_oid IS NOT NULL\n AND EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${a}')\n AND NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumtypid = type_oid AND enumlabel = '${o}') THEN\n IF current_setting('server_version_num')::int >= 140000 THEN\n EXECUTE format('ALTER TYPE %I RENAME VALUE %L TO %L', '${i}', '${a}', '${o}');\n ELSE\n UPDATE pg_enum SET enumlabel = '${o}' WHERE enumtypid = type_oid AND enumlabel = '${a}';\n END IF;\n END IF;\nEND $$;`);
570
593
  }
571
594
  return r.join("\n\n");
572
- }, Ue = (e) => {
595
+ }, $e = (e) => {
573
596
  let t = [], n = [];
574
- for (let r of e) i(r) ? n.push(r) : t.push(r);
597
+ for (let r of e) f(r) ? n.push(r) : t.push(r);
575
598
  return {
576
599
  tables: t,
577
600
  views: n
578
601
  };
579
- }, We = (e, n, r = O) => {
580
- let { tables: i, views: o } = Ue(e), { sorted: s, cyclic: c } = Re(i.map((e) => w(e, n))), l = Be(c, n), u = [];
581
- n === "postgres" && Ie(s) && u.push("CREATE EXTENSION IF NOT EXISTS vector;"), n === "postgres" && Le(s) && u.push("CREATE EXTENSION IF NOT EXISTS postgis;");
582
- let d = He(s, n);
602
+ }, et = (e, t, n = D) => {
603
+ let { tables: i, views: o } = $e(e), { sorted: s, cyclic: c } = Je(i.map((e) => ce(e, t))), l = Xe(c, t), u = [];
604
+ t === "postgres" && Ke(s) && u.push("CREATE EXTENSION IF NOT EXISTS vector;"), t === "postgres" && qe(s) && u.push("CREATE EXTENSION IF NOT EXISTS postgis;");
605
+ let d = Qe(s, t);
583
606
  d && u.push(d);
584
- let f = Ve(s, n);
607
+ let f = Ze(s, t);
585
608
  f && u.push(f);
586
- for (let e of s) u.push(Oe(e, n, null, l.get(e.tableName)));
587
- if (!t(n)) for (let e of c) u.push(ze(e, n));
609
+ for (let e of s) u.push(Re(e, t, null, l.get(e.tableName)));
610
+ if (!r(t)) for (let e of c) u.push(Ye(e, t));
588
611
  for (let e of s) {
589
- let t = Ae(e, n);
590
- t && u.push(t);
612
+ let n = Be(e, t);
613
+ n && u.push(n);
591
614
  }
592
- u.push(...je(s, n, r));
593
- for (let e of o) u.push(a(e, n));
615
+ u.push(...Ve(s, t, n));
616
+ for (let e of o) u.push(a(e, t));
594
617
  return u.join("\n\n") + "\n";
595
- }, Ge = (e, t, r) => {
596
- let i = n(e, t);
618
+ }, tt = (e, t, n) => {
619
+ let r = i(e, t);
597
620
  switch (t) {
598
- case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${i};`;
621
+ case "postgres": return `CREATE SCHEMA IF NOT EXISTS ${r};`;
599
622
  case "mysql":
600
- case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${i};`;
601
- case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${i.replace(/'/g, "''")}');`;
623
+ case "mariadb": return `CREATE DATABASE IF NOT EXISTS ${r};`;
624
+ case "mssql": return `IF SCHEMA_ID('${e.replace(/'/g, "''")}') IS NULL EXEC(N'CREATE SCHEMA ${r.replace(/'/g, "''")}');`;
602
625
  case "turso":
603
- case "sqlite": return `ATTACH DATABASE '${(r ?? `${e}.db`).replace(/'/g, "''")}' AS ${i};`;
604
- }
605
- }, Ke = (e, r, i, a) => {
606
- let o = Ge(i, r, a), s = n(i, r);
607
- if (r === "postgres") return `${o}\n\nSET search_path TO ${s};\n\n${We(e, r)}`;
608
- if (r === "mysql" || r === "mariadb") return `${o}\n\nUSE ${s};\n\n${We(e, r)}`;
609
- let { sorted: c, cyclic: l } = Re(e), u = Be(l, r), d = [o];
610
- for (let e of c) d.push(Oe(e, r, i, u.get(e.tableName)));
611
- if (!t(r)) for (let e of l) d.push(ze(e, r, i));
626
+ case "sqlite": return `ATTACH DATABASE '${(n ?? `${e}.db`).replace(/'/g, "''")}' AS ${r};`;
627
+ }
628
+ }, nt = (e, t, n, a) => {
629
+ let o = tt(n, t, a), s = i(n, t);
630
+ if (t === "postgres") return `${o}\n\nSET search_path TO ${s};\n\n${et(e, t)}`;
631
+ if (t === "mysql" || t === "mariadb") return `${o}\n\nUSE ${s};\n\n${et(e, t)}`;
632
+ let { sorted: c, cyclic: l } = Je(e), u = Xe(l, t), d = [o];
633
+ for (let e of c) d.push(Re(e, t, n, u.get(e.tableName)));
634
+ if (!r(t)) for (let e of l) d.push(Ye(e, t, n));
612
635
  for (let e of c) {
613
- let t = Ae(e, r, i);
614
- t && d.push(t);
636
+ let r = Be(e, t, n);
637
+ r && d.push(r);
615
638
  }
616
639
  return d.join("\n\n") + "\n";
617
- }, qe = (e, t, n = O) => {
618
- let { sorted: r, cyclic: i } = Re(e), a = Be(i, t), o = [], s = He(r, t);
640
+ }, rt = (e, t, n = D) => {
641
+ let { sorted: r, cyclic: i } = Je(e), a = Xe(i, t), o = [], s = Qe(r, t);
619
642
  s && o.push(s);
620
- let c = Ve(r, t);
643
+ let c = Ze(r, t);
621
644
  c && o.push(c);
622
- for (let e of r) o.push(Oe(e, t, null, a.get(e.tableName)));
623
- for (let e of i) o.push(ze(e, t));
624
- return o.push(...je(r, t, n)), o.join("\n\n") + "\n";
625
- }, Je = (e, t = "postgres", n = O) => p.gen(function* () {
626
- let r = yield* h.SqlClient;
627
- yield* ge(r, Ze(r, We(e, t, n), t)).pipe(p.orDie);
628
- }), Ye = (e, t) => {
645
+ for (let e of r) o.push(Re(e, t, null, a.get(e.tableName)));
646
+ for (let e of i) o.push(Ye(e, t));
647
+ return o.push(...Ve(r, t, n)), o.join("\n\n") + "\n";
648
+ }, it = (e, t = "postgres", n = D) => b.gen(function* () {
649
+ let r = yield* S.SqlClient;
650
+ yield* we(r, st(r, et(e, t, n), t)).pipe(b.orDie);
651
+ }), at = (e, t) => {
629
652
  let n = t.cause ?? {};
630
653
  return (n.errno === 1061 || n.code === "ER_DUP_KEYNAME") && /^\s*CREATE\s+(UNIQUE\s+|FULLTEXT\s+)?INDEX\b/i.test(e);
631
- }, Xe = (e) => p.gen(function* () {
632
- let t = yield* h.SqlClient;
633
- for (let n of Qe(e)) process.env.VOLTRO_MIGRATE_DEBUG === "1" && ye.debug(`EXEC: ${n.replace(/\s+/g, " ").slice(0, 180)}`), yield* t.unsafe(n).pipe(p.catchIf((e) => Ye(n, e), () => p.void), p.tapError((e) => p.sync(() => {
634
- ye.error(b("migrate: statement failed", n, e, 200));
635
- })), p.orDieWith((e) => Error(b("migrate: statement failed", n, e, 400).trim())));
636
- }), Ze = (e, t, n) => (n === "turso" ? Xe(t) : e.withTransaction(Xe(t))).pipe(p.orDie), Qe = (e) => {
654
+ }, ot = (e) => b.gen(function* () {
655
+ let t = yield* S.SqlClient;
656
+ for (let n of ct(e)) process.env.VOLTRO_MIGRATE_DEBUG === "1" && ke.debug(`EXEC: ${n.replace(/\s+/g, " ").slice(0, 180)}`), yield* t.unsafe(n).pipe(b.catchIf((e) => at(n, e), () => b.void), b.tapError((e) => b.sync(() => {
657
+ ke.error(re("migrate: statement failed", n, e, 200));
658
+ })), b.orDieWith((e) => Error(re("migrate: statement failed", n, e, 400).trim())));
659
+ }), st = (e, t, n) => (n === "turso" ? ot(t) : e.withTransaction(ot(t))).pipe(b.orDie), ct = (e) => {
637
660
  let t = [], n = "", r = !1;
638
661
  for (let i = 0; i < e.length; i++) {
639
662
  if (e.slice(i, i + 2) === "$$") {
@@ -648,40 +671,43 @@ BEGIN
648
671
  }
649
672
  let i = n.trim();
650
673
  return i.length > 0 && t.push(i), t;
651
- }, $e = async (e, t, n = "postgres") => {
652
- await p.runPromise(Je(e, n).pipe(p.provide(t)));
653
- }, et = (e, t, n = "postgres") => p.gen(function* () {
654
- let r = Ke(e, n, t), i = yield* h.SqlClient;
655
- yield* ge(i, Ze(i, r, n)).pipe(p.orDie);
656
- }), tt = async (e, t, n, r = "postgres") => {
657
- await p.runPromise(et(e, t, r).pipe(p.provide(n)));
658
- }, nt = async (e, t, n = "postgres") => {
659
- await p.runPromise(p.gen(function* () {
660
- let t = qe(e, n), r = yield* h.SqlClient;
661
- yield* ge(r, Ze(r, t, n)).pipe(p.orDie);
662
- }).pipe(p.provide(t)));
663
- }, rt = (e) => {
674
+ }, lt = async (e, t, n = "postgres") => {
675
+ await b.runPromise(it(e, n).pipe(b.provide(t)));
676
+ }, ut = (e, t, n = "postgres") => b.gen(function* () {
677
+ let r = nt(e, n, t), i = yield* S.SqlClient;
678
+ yield* we(i, st(i, r, n)).pipe(b.orDie);
679
+ }), dt = async (e, t, n, r = "postgres") => {
680
+ await b.runPromise(ut(e, t, r).pipe(b.provide(n))), await d({
681
+ namespace: t,
682
+ dialect: r
683
+ });
684
+ }, ft = async (e, t, n = "postgres") => {
685
+ await b.runPromise(b.gen(function* () {
686
+ let t = rt(e, n), r = yield* S.SqlClient;
687
+ yield* we(r, st(r, t, n)).pipe(b.orDie);
688
+ }).pipe(b.provide(t)));
689
+ }, pt = (e) => {
664
690
  if (e == null) return e;
665
- if (Array.isArray(e)) return e.map(rt);
691
+ if (Array.isArray(e)) return e.map(pt);
666
692
  if (typeof e != "object") return e;
667
693
  let t = {};
668
694
  for (let n of Object.keys(e).sort()) {
669
695
  let r = e[n];
670
- r !== void 0 && (t[n] = rt(r));
696
+ r !== void 0 && (t[n] = pt(r));
671
697
  }
672
698
  return t;
673
- }, it = (e) => {
699
+ }, mt = (e) => {
674
700
  if (!e.idScheme && !e.sensitive && !e.safe) return e;
675
701
  let { idScheme: t, sensitive: n, safe: r, ...i } = e;
676
702
  return i;
677
- }, at = (e) => [...e].map(it).sort((e, t) => e.name.localeCompare(t.name)), ot = (e) => [...e].sort((e, t) => e.name.localeCompare(t.name)), st = (e) => [...e].map((e) => ({
703
+ }, ht = (e) => [...e].map(mt).sort((e, t) => e.name.localeCompare(t.name)), gt = (e) => [...e].sort((e, t) => e.name.localeCompare(t.name)), _t = (e) => [...e].map((e) => ({
678
704
  name: e.name,
679
- columns: at(e.columns),
680
- indexes: ot(e.indexes)
681
- })).sort((e, t) => e.name.localeCompare(t.name)), P = (e) => {
682
- let t = { tables: st(e.tables) }, n = JSON.stringify(rt(t));
683
- return g("sha256").update(n).digest("hex");
684
- }, F = (e) => e.slice(0, 16), ct = (e, t) => {
705
+ columns: ht(e.columns),
706
+ indexes: gt(e.indexes)
707
+ })).sort((e, t) => e.name.localeCompare(t.name)), M = (e) => {
708
+ let t = { tables: _t(e.tables) }, n = JSON.stringify(pt(t));
709
+ return C("sha256").update(n).digest("hex");
710
+ }, N = (e) => e.slice(0, 16), vt = (e, t) => {
685
711
  let n = t.references ? {
686
712
  table: t.references().tableName,
687
713
  column: "id",
@@ -720,7 +746,7 @@ BEGIN
720
746
  ...t.enumValues === void 0 ? {} : { enumValues: t.enumValues },
721
747
  ...t.spatial === void 0 ? {} : { spatial: t.spatial }
722
748
  };
723
- }, lt = (e) => {
749
+ }, yt = (e) => {
724
750
  let t = [], n = /* @__PURE__ */ new Set(), r = (e) => {
725
751
  n.has(e.name) || (n.add(e.name), t.push(e));
726
752
  }, i = e.appliedPrimaryKey ?? [];
@@ -760,7 +786,7 @@ BEGIN
760
786
  let i = n;
761
787
  if (i.dropped || !i.references || i.fkAutoIndex === !1 || s.has(t)) continue;
762
788
  let a = `${e.tableName}_${t}_idx`;
763
- f(e.tableName, a, !0, [t]), r({
789
+ p(e.tableName, a, !0, [t]), r({
764
790
  name: a,
765
791
  table: e.tableName,
766
792
  columns: [t],
@@ -768,11 +794,11 @@ BEGIN
768
794
  });
769
795
  }
770
796
  return t;
771
- }, ut = (e) => {
797
+ }, bt = (e) => {
772
798
  let t = new Set((e.appliedUniques ?? []).filter((e) => e.fields.length === 1).map((e) => e.fields[0])), n = [];
773
799
  for (let [r, i] of Object.entries(e.fields)) {
774
800
  if (i.dropped) continue;
775
- let e = ct(r, i);
801
+ let e = vt(r, i);
776
802
  n.push(t.has(r) ? {
777
803
  ...e,
778
804
  unique: !0
@@ -782,44 +808,52 @@ BEGIN
782
808
  return {
783
809
  name: e.tableName,
784
810
  columns: n,
785
- indexes: lt(e),
811
+ indexes: yt(e),
786
812
  ...r.length > 0 ? { primaryKey: [...r] } : {},
787
813
  ...i === void 0 ? {} : { renamedFrom: i }
788
814
  };
789
- }, dt = (e) => {
815
+ }, xt = (e) => {
790
816
  let t = /* @__PURE__ */ new Map();
791
817
  for (let n of e) for (let e of n.indexes) {
792
818
  let r = t.get(e.name);
793
819
  if (r !== void 0 && r !== n.name) throw Error(`duplicate index name '${e.name}' across tables '${r}' and '${n.name}'. Index names must be unique per SCHEMA, not just per table — the DB creates only one, so the other never applies and 'db plan' re-emits it forever. Give each an explicit, table-prefixed name (e.g. '${n.name}_${e.name}').`);
794
820
  t.set(e.name, n.name);
795
821
  }
796
- }, I = (e, t) => {
797
- let n = e.map((e) => ut(w(e, t)));
798
- return dt(n), { tables: n };
799
- }, ft = (e, t, n = O) => p.gen(function* () {
800
- let r = t.filter((e) => !e.tableName.startsWith("_voltro_"));
801
- if (r.length === 0) return {
822
+ }, P = (e, t) => {
823
+ let n = e.map((e) => bt(ce(e, t)));
824
+ return xt(n), { tables: n };
825
+ }, St = (e, t, n = D) => b.gen(function* () {
826
+ let r = (yield* e`
827
+ SELECT p.prosrc
828
+ FROM pg_proc p
829
+ JOIN pg_namespace n ON n.oid = p.pronamespace
830
+ WHERE n.nspname = current_schema()
831
+ AND p.proname = ${De(n)}
832
+ `)[0]?.prosrc, i = r !== void 0 && !r.includes("voltro:notify:v2"), a = t.filter((e) => !e.tableName.startsWith("_voltro_"));
833
+ if (a.length === 0) return {
802
834
  missing: [],
803
- stale: []
835
+ stale: [],
836
+ functionOutdated: i
804
837
  };
805
- let i = yield* e`
838
+ let o = yield* e`
806
839
  SELECT t.tgname
807
840
  FROM pg_trigger t
808
841
  JOIN pg_class c ON c.oid = t.tgrelid
809
842
  JOIN pg_namespace n ON n.oid = c.relnamespace
810
843
  WHERE NOT t.tgisinternal
811
844
  AND n.nspname = current_schema()
812
- AND t.tgname LIKE ${`${k}%`}
813
- `, a = new Set(i.map((e) => e.tgname)), o = [], s = [];
814
- for (let e of r) {
815
- let t = a.has(_e(e.tableName, n)), r = e.isReactive !== !1;
816
- r && !t && o.push(e.tableName), !r && t && s.push(e.tableName);
845
+ AND t.tgname LIKE ${`${Te}%`}
846
+ `, s = new Set(o.map((e) => e.tgname)), c = [], l = [];
847
+ for (let e of a) {
848
+ let t = s.has(Ee(e.tableName, n)), r = e.isReactive !== !1;
849
+ r && !t && c.push(e.tableName), !r && t && l.push(e.tableName);
817
850
  }
818
851
  return {
819
- missing: o.sort(),
820
- stale: s.sort()
852
+ missing: c.sort(),
853
+ stale: l.sort(),
854
+ functionOutdated: i
821
855
  };
822
- }), pt = (e) => {
856
+ }), Ct = (e) => {
823
857
  let t = [];
824
858
  if (e.missing.length > 0) {
825
859
  let n = e.missing.slice(0, 8).join(", "), r = e.missing.length > 8 ? `, … (+${e.missing.length - 8})` : "";
@@ -829,20 +863,20 @@ BEGIN
829
863
  let n = e.stale.slice(0, 8).join(", ");
830
864
  t.push(`${e.stale.length} .nonReactive() table(s) still carry a change trigger — ${n}. They keep paying REPLICA IDENTITY FULL and a NOTIFY on every write for a subscription nobody receives. \`voltro db apply\` removes them.`);
831
865
  }
832
- return t.length > 0 ? t.join("\n") : void 0;
833
- }, mt = {
866
+ return e.functionOutdated && t.push("The change-notify function in this database is OLDER than the framework installed here. Its triggers fire, so nothing looks wrong — but a row too large for a NOTIFY payload (postgres caps it at 8000 bytes) is emitted without its primary key, and a change that carries no key cannot be re-read: it reaches subscriptions as a re-query and reaches every TAP — search index, analytics mirror, cdc-out, row history — as nothing at all. Run `voltro db apply` to replace it."), t.length > 0 ? t.join("\n") : void 0;
867
+ }, wt = {
834
868
  postgres: !0,
835
869
  mysql: !0,
836
870
  mariadb: !0,
837
871
  mssql: !0,
838
872
  sqlite: !0
839
- }, ht = {
873
+ }, Tt = {
840
874
  postgres: !0,
841
875
  mysql: !1,
842
876
  mariadb: !1,
843
877
  mssql: !0,
844
878
  sqlite: !0
845
- }, gt = (e) => {
879
+ }, Et = (e) => {
846
880
  switch (e) {
847
881
  case "add-column":
848
882
  case "drop-column":
@@ -852,47 +886,47 @@ BEGIN
852
886
  case "add-foreign-key":
853
887
  case "add-unique":
854
888
  case "add-unique-composite":
855
- case "add-check": return ht;
856
- default: return mt;
889
+ case "add-check": return Tt;
890
+ default: return wt;
857
891
  }
858
- }, _t = (e, t) => {
892
+ }, Dt = (e, t) => {
859
893
  let n = new Map(t.tables.map((e) => [e.name, e]));
860
894
  return new Map(e.map((e) => [e.tableName, {
861
895
  table: e,
862
896
  snapshot: n.get(e.tableName),
863
897
  columnDefs: e.fields
864
898
  }]));
865
- }, L = (e) => {
899
+ }, F = (e) => {
866
900
  if (typeof e != "object" || !e) return JSON.stringify(e);
867
- if (Array.isArray(e)) return `[${e.map(L).join(",")}]`;
901
+ if (Array.isArray(e)) return `[${e.map(F).join(",")}]`;
868
902
  let t = e;
869
- return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${L(t[e])}`).join(",")}}`;
870
- }, vt = (e) => {
903
+ return `{${Object.keys(t).sort().map((e) => `${JSON.stringify(e)}:${F(t[e])}`).join(",")}}`;
904
+ }, Ot = (e) => {
871
905
  if (!e.hasDefault || e.defaultValue === void 0 || e.defaultValue === null) return null;
872
906
  let t = e.defaultValue;
873
907
  if (typeof t == "boolean") return t ? "__BOOL_TRUE__" : "__BOOL_FALSE__";
874
908
  if (typeof t == "number") return String(t);
875
- if (typeof t != "string") return L(t);
909
+ if (typeof t != "string") return F(t);
876
910
  let n = t.trim(), r = n.toLowerCase().replace(/\s+/g, "");
877
911
  if (r === "now" || r === "now()" || /^current_timestamp(\(\d*\))?$/.test(r) || r === "sysutcdatetime()" || r === "sysdatetime()" || r === "getutcdate()" || r === "getdate()") return "__NOW__";
878
912
  let i = n.replace(/::[a-zA-Z0-9_ "]+$/, "").trim(), a = /^'(.*)'$/.test(i) ? i.slice(1, -1).replace(/''/g, "'") : i, o = /^cast\(\s*(?:_\w+\s*)?\\?'(.*?)\\?'\s+as\s+json\s*\)$/i.exec(a);
879
913
  if (o) try {
880
- return L(JSON.parse(o[1]));
914
+ return F(JSON.parse(o[1]));
881
915
  } catch {}
882
916
  let s = /^convert\(\s*(?:_\w+\s*)?\\?'(.*?)\\?'\s+using\s+[\w]+\s*\)$/i.exec(a);
883
917
  if (s) try {
884
- return L(JSON.parse(s[1]));
918
+ return F(JSON.parse(s[1]));
885
919
  } catch {}
886
920
  if (a.startsWith("{") || a.startsWith("[")) try {
887
- return L(JSON.parse(a));
921
+ return F(JSON.parse(a));
888
922
  } catch {}
889
923
  return /^-?\d+(\.\d+)?$/.test(a) ? String(Number(a)) : a;
890
- }, yt = (e, t) => {
891
- let n = vt(e), r = vt(t);
924
+ }, kt = (e, t) => {
925
+ let n = Ot(e), r = Ot(t);
892
926
  if (n === r) return !0;
893
927
  let i = (e) => e === "1" || e === "true" ? "__BOOL_TRUE__" : e === "0" || e === "false" ? "__BOOL_FALSE__" : e;
894
928
  return i(n) === i(r);
895
- }, bt = (e) => {
929
+ }, At = (e) => {
896
930
  if (!e) return null;
897
931
  let t = (e) => e === "restrict" ? "noAction" : e;
898
932
  return {
@@ -901,25 +935,25 @@ BEGIN
901
935
  onDelete: t(e.onDelete),
902
936
  onUpdate: t(e.onUpdate)
903
937
  };
904
- }, xt = (e, t) => {
938
+ }, jt = (e, t) => {
905
939
  let n = e && e.length > 0 ? e : void 0, r = t && t.length > 0 ? t : void 0;
906
940
  if (!n && !r) return !0;
907
941
  if (!n || !r || n.length !== r.length) return !1;
908
942
  let i = [...n].sort(), a = [...r].sort();
909
943
  return i.every((e, t) => e === a[t]);
910
- }, St = (e, t) => {
944
+ }, Mt = (e, t) => {
911
945
  try {
912
- let n = /\((\d+)\)/.exec(j(e, t));
946
+ let n = /\((\d+)\)/.exec(k(e, t));
913
947
  return n ? Number(n[1]) : void 0;
914
948
  } catch {
915
949
  return;
916
950
  }
917
- }, Ct = (e, n, r) => r === void 0 || t(r) ? !0 : e.type !== "text" || n.type !== "text" || St(e, r) === n.maxLength, wt = (e, t, n) => e.type === t.type && e.nullable === t.nullable && e.unique === t.unique && yt(e, t) && Ct(e, t, n) && JSON.stringify(bt(e.references)) === JSON.stringify(bt(t.references)) && xt(e.oneOf, t.oneOf), Tt = (e, t) => t.oneOf && t.oneOf.length > 0 ? R({
951
+ }, Nt = (e, t, n) => n === void 0 || r(n) ? !0 : e.type !== "text" || t.type !== "text" || Mt(e, n) === t.maxLength, Pt = (e, t, n) => e.type === t.type && e.nullable === t.nullable && e.unique === t.unique && kt(e, t) && Nt(e, t, n) && JSON.stringify(At(e.references)) === JSON.stringify(At(t.references)) && jt(e.oneOf, t.oneOf), Ft = (e, t) => t.oneOf && t.oneOf.length > 0 ? I({
918
952
  kind: "add-check",
919
953
  table: e,
920
954
  column: t.name,
921
955
  values: t.oneOf
922
- }, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0, Et = (e, t) => e.unique === t.unique && (e.expression || t.expression ? !0 : e.columns.length === t.columns.length && e.columns.every((e, n) => e === t.columns[n])), Dt = (e, t, n) => {
956
+ }, "safe", `enum-membership CHECK on ${e}.${t.name}`) : void 0, It = (e, t) => e.unique === t.unique && (e.expression || t.expression ? !0 : e.columns.length === t.columns.length && e.columns.every((e, n) => e === t.columns[n])), Lt = (e, t, n) => {
923
957
  let r = new Map(t.columns.map((e) => [e.name, e])), i = new Map(e.columns.map((e) => [e.name, e])), a = [], o = [], s = [];
924
958
  for (let t of e.columns) {
925
959
  let e = r.get(t.name);
@@ -927,7 +961,7 @@ BEGIN
927
961
  a.push(t);
928
962
  continue;
929
963
  }
930
- wt(t, e, n) || s.push({
964
+ Pt(t, e, n) || s.push({
931
965
  declared: t,
932
966
  live: e
933
967
  });
@@ -938,47 +972,47 @@ BEGIN
938
972
  removed: o,
939
973
  changed: s
940
974
  };
941
- }, Ot = (e, t) => {
975
+ }, Rt = (e, t) => {
942
976
  let n = new Map(t.indexes.map((e) => [e.name, e])), r = new Map(e.indexes.map((e) => [e.name, e])), i = [], a = [];
943
977
  for (let t of e.indexes) {
944
978
  let e = n.get(t.name);
945
- (!e || !Et(t, e)) && i.push(t);
979
+ (!e || !It(t, e)) && i.push(t);
946
980
  }
947
981
  for (let e of t.indexes) r.has(e.name) || a.push(e);
948
982
  return {
949
983
  added: i,
950
984
  removed: a
951
985
  };
952
- }, R = (e, t, n, r) => ({
986
+ }, I = (e, t, n, r) => ({
953
987
  op: e,
954
988
  classification: t,
955
- atomicByDialect: gt(e.kind),
989
+ atomicByDialect: Et(e.kind),
956
990
  ...n ? { reason: n } : {},
957
991
  ...r ? { blocked: r } : {}
958
- }), kt = (e, t, n) => {
992
+ }), zt = (e, t, n) => {
959
993
  let r = {
960
994
  kind: "add-column",
961
995
  table: e,
962
996
  column: t
963
997
  };
964
- return t.nullable ? R(r, "safe", "nullable column add — no backfill needed") : t.hasDefault && t.defaultValue !== void 0 ? R(r, "needs-default", "NOT NULL with literal default — DDL DEFAULT will populate existing rows") : n?.backfill ? R(r, "needs-backfill", `backfill declared (kind=${n.backfill.kind}) — applier will run 3-step add/update/set-not-null`) : R(r, "needs-backfill", "NOT NULL column on a table whose row count is unknown", { fix: `declare \`${t.name}: <type>().backfill(sql\`...\`)\` OR \`.default(value)\` so existing rows survive the migration` });
965
- }, At = (e, t, n) => {
998
+ return t.nullable ? I(r, "safe", "nullable column add — no backfill needed") : t.hasDefault && t.defaultValue !== void 0 ? I(r, "needs-default", "NOT NULL with literal default — DDL DEFAULT will populate existing rows") : n?.backfill ? I(r, "needs-backfill", `backfill declared (kind=${n.backfill.kind}) — applier will run 3-step add/update/set-not-null`) : I(r, "needs-backfill", "NOT NULL column on a table whose row count is unknown", { fix: `declare \`${t.name}: <type>().backfill(sql\`...\`)\` OR \`.default(value)\` so existing rows survive the migration` });
999
+ }, Bt = (e, t, n) => {
966
1000
  let r = {
967
1001
  kind: "drop-column",
968
1002
  table: e,
969
1003
  column: t.name
970
1004
  };
971
- return n?.dropped ? R(r, "lossy", `column dropped via \`${t.name}: dropped()\` marker — intentional`) : R(r, "lossy", "column missing from declared schema", { fix: `if intentional, add \`${t.name}: dropped()\` to the schema. If a typo, restore the field` });
972
- }, jt = (e, t, n, r, i) => R({
1005
+ return n?.dropped ? I(r, "lossy", `column dropped via \`${t.name}: dropped()\` marker — intentional`) : I(r, "lossy", "column missing from declared schema", { fix: `if intentional, add \`${t.name}: dropped()\` to the schema. If a typo, restore the field` });
1006
+ }, Vt = (e, t, n, r, i) => I({
973
1007
  kind: "create-table",
974
1008
  table: e,
975
1009
  columns: t,
976
1010
  indexes: n,
977
1011
  ...r && r.length > 0 ? { primaryKey: r } : {}
978
- }, "safe", i ?? "new table — no data to preserve"), Mt = (e) => R({
1012
+ }, "safe", i ?? "new table — no data to preserve"), Ht = (e) => I({
979
1013
  kind: "drop-table",
980
1014
  table: e
981
- }, "lossy", "table missing from declared schema", { fix: "if intentional, set VOLTRO_DESTRUCTIVE_OK=1 OR add a file-based migration. If a typo, restore the table declaration" }), Nt = 5e4, Pt = (e, t, n, r) => {
1015
+ }, "lossy", "table missing from declared schema", { fix: "if intentional, set VOLTRO_DESTRUCTIVE_OK=1 OR add a file-based migration. If a typo, restore the table declaration" }), Ut = 5e4, Wt = (e, t, n, r) => {
982
1016
  let i = (n ?? 0) >= r, a;
983
1017
  return a = t.unique ? t.columns.length === 1 ? {
984
1018
  kind: "add-unique",
@@ -993,16 +1027,16 @@ BEGIN
993
1027
  kind: "add-index",
994
1028
  table: e,
995
1029
  index: t
996
- }, i ? R(a, "online-required", `online build on ${n} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(a, "safe", t.unique ? t.columns.length === 1 ? "add unique constraint" : "add composite UNIQUE constraint" : "add B-tree index");
997
- }, Ft = (e, t) => t.unique && t.columns.length > 1 ? R({
1030
+ }, i ? I(a, "online-required", `online build on ${n} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : I(a, "safe", t.unique ? t.columns.length === 1 ? "add unique constraint" : "add composite UNIQUE constraint" : "add B-tree index");
1031
+ }, Gt = (e, t) => t.unique && t.columns.length > 1 ? I({
998
1032
  kind: "drop-unique-composite",
999
1033
  table: e,
1000
1034
  name: t.name
1001
- }, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") : R({
1035
+ }, "safe", "drop composite UNIQUE constraint (no data loss — constraint is derived metadata)") : I({
1002
1036
  kind: "drop-index",
1003
1037
  table: e,
1004
1038
  index: t.name
1005
- }, "safe", "drop index (no data loss — index is derived data)"), It = (e, t) => {
1039
+ }, "safe", "drop index (no data loss — index is derived data)"), Kt = (e, t) => {
1006
1040
  if (e === "reference") return "text";
1007
1041
  switch (t) {
1008
1042
  case "sqlite":
@@ -1010,87 +1044,87 @@ BEGIN
1010
1044
  case "mssql": return e === "json" ? "text" : e;
1011
1045
  default: return e;
1012
1046
  }
1013
- }, Lt = (e, n, r, i, a, o) => {
1047
+ }, qt = (e, t, n, i, a, o) => {
1014
1048
  let s = [];
1015
- if (n.nullable !== r.nullable) {
1016
- let t = {
1049
+ if (t.nullable !== n.nullable) {
1050
+ let n = {
1017
1051
  kind: "alter-column-nullability",
1018
1052
  table: e,
1019
- column: n.name,
1020
- toNullable: n.nullable
1053
+ column: t.name,
1054
+ toNullable: t.nullable
1021
1055
  };
1022
- s.push(n.nullable ? R(t, "safe", `loosen ${e}.${n.name} to nullable`) : R(t, "needs-backfill", `tighten ${e}.${n.name} to NOT NULL — existing rows must already be non-null`));
1056
+ s.push(t.nullable ? I(n, "safe", `loosen ${e}.${t.name} to nullable`) : I(n, "needs-backfill", `tighten ${e}.${t.name} to NOT NULL — existing rows must already be non-null`));
1023
1057
  }
1024
- if (n.unique !== r.unique) if (n.unique) {
1025
- let t = {
1058
+ if (t.unique !== n.unique) if (t.unique) {
1059
+ let n = {
1026
1060
  kind: "add-unique",
1027
1061
  table: e,
1028
- column: n.name
1062
+ column: t.name
1029
1063
  }, r = (a ?? 0) >= o;
1030
- s.push(r ? R(t, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : R(t, "safe", `add unique constraint on ${e}.${n.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
1031
- } else s.push(R({
1064
+ s.push(r ? I(n, "online-required", `online unique build on ${a} rows — CONCURRENTLY / INPLACE / WITH(ONLINE=ON) depending on dialect`) : I(n, "safe", `add unique constraint on ${e}.${t.name} — existing rows must already be distinct (else the DB rejects at migrate)`));
1065
+ } else s.push(I({
1032
1066
  kind: "drop-unique",
1033
1067
  table: e,
1034
- column: n.name
1035
- }, "safe", `drop unique constraint on ${e}.${n.name} — constraint is derived metadata, no data loss`));
1036
- if (It(n.type, i) !== It(r.type, i)) {
1037
- let t = n.narrowedFrom;
1038
- if (t && t.from === r.type) {
1068
+ column: t.name
1069
+ }, "safe", `drop unique constraint on ${e}.${t.name} — constraint is derived metadata, no data loss`));
1070
+ if (Kt(t.type, i) !== Kt(n.type, i)) {
1071
+ let r = t.narrowedFrom;
1072
+ if (r && r.from === n.type) {
1039
1073
  let i = {
1040
1074
  kind: "alter-column-type",
1041
1075
  table: e,
1042
- column: n.name,
1043
- from: r.type,
1044
- to: n.type,
1045
- ...t.using === void 0 ? {} : { using: t.using }
1076
+ column: t.name,
1077
+ from: n.type,
1078
+ to: t.type,
1079
+ ...r.using === void 0 ? {} : { using: r.using }
1046
1080
  };
1047
- s.push(R(i, "needs-backfill", `type change ${r.type} → ${n.type} on ${e}.${n.name} — narrowedFrom('${t.from}') declared` + (t.using ? ` with USING ${t.using}` : " (implicit cast)")));
1081
+ s.push(I(i, "needs-backfill", `type change ${n.type} → ${t.type} on ${e}.${t.name} — narrowedFrom('${r.from}') declared` + (r.using ? ` with USING ${r.using}` : " (implicit cast)")));
1048
1082
  } else {
1049
- let t = {
1083
+ let r = {
1050
1084
  kind: "alter-column-type",
1051
1085
  table: e,
1052
- column: n.name,
1053
- from: r.type,
1054
- to: n.type
1086
+ column: t.name,
1087
+ from: n.type,
1088
+ to: t.type
1055
1089
  };
1056
- s.push(R(t, "lossy", `type change ${r.type} → ${n.type} on ${e}.${n.name} may not be value-preserving`, { fix: `acknowledge it on the column: \`.narrowedFrom('${r.type}', { using: '${n.name}::${n.type}' })\` (drop \`using\` if the cast is implicit), or use a file-based migration` }));
1090
+ s.push(I(r, "lossy", `type change ${n.type} → ${t.type} on ${e}.${t.name} may not be value-preserving`, { fix: `acknowledge it on the column: \`.narrowedFrom('${n.type}', { using: '${t.name}::${t.type}' })\` (drop \`using\` if the cast is implicit), or use a file-based migration` }));
1057
1091
  }
1058
1092
  }
1059
- if (n.type === "text" && r.type === "text" && !t(i ?? "postgres") && n.maxLength !== r.maxLength) {
1060
- let t = n.maxLength === void 0 || r.maxLength !== void 0 && n.maxLength > r.maxLength, i = r.maxLength === void 0 ? "unbounded" : `varchar(${r.maxLength})`, a = n.maxLength === void 0 ? "unbounded" : `varchar(${n.maxLength})`;
1061
- s.push(R({
1093
+ if (t.type === "text" && n.type === "text" && !r(i ?? "postgres") && t.maxLength !== n.maxLength) {
1094
+ let r = t.maxLength === void 0 || n.maxLength !== void 0 && t.maxLength > n.maxLength, i = n.maxLength === void 0 ? "unbounded" : `varchar(${n.maxLength})`, a = t.maxLength === void 0 ? "unbounded" : `varchar(${t.maxLength})`;
1095
+ s.push(I({
1062
1096
  kind: "alter-column-type",
1063
1097
  table: e,
1064
- column: n.name,
1098
+ column: t.name,
1065
1099
  from: "text",
1066
1100
  to: "text"
1067
- }, t ? "safe" : "needs-backfill", t ? `text length ${i} → ${a} on ${e}.${n.name} — widening, no data can be lost` : `text length ${i} → ${a} on ${e}.${n.name} — NARROWING; the ALTER fails if any existing value is longer than ${String(n.maxLength)}. Check first: SELECT COUNT(*) FROM ${e} WHERE LENGTH(${n.name}) > ${String(n.maxLength)}`));
1101
+ }, r ? "safe" : "needs-backfill", r ? `text length ${i} → ${a} on ${e}.${t.name} — widening, no data can be lost` : `text length ${i} → ${a} on ${e}.${t.name} — NARROWING; the ALTER fails if any existing value is longer than ${String(t.maxLength)}. Check first: SELECT COUNT(*) FROM ${e} WHERE LENGTH(${t.name}) > ${String(t.maxLength)}`));
1068
1102
  }
1069
- if (!yt(n, r)) {
1070
- let t = {
1103
+ if (!kt(t, n)) {
1104
+ let n = {
1071
1105
  kind: "alter-column-default",
1072
1106
  table: e,
1073
- column: n.name,
1074
- hasDefault: n.hasDefault,
1075
- ...n.defaultValue === void 0 ? {} : { defaultValue: n.defaultValue }
1107
+ column: t.name,
1108
+ hasDefault: t.hasDefault,
1109
+ ...t.defaultValue === void 0 ? {} : { defaultValue: t.defaultValue }
1076
1110
  };
1077
- s.push(R(t, "safe", `default change on ${e}.${n.name} affects only future inserts`));
1111
+ s.push(I(n, "safe", `default change on ${e}.${t.name} affects only future inserts`));
1078
1112
  }
1079
- if (!xt(n.oneOf, r.oneOf)) {
1080
- r.oneOf && r.oneOf.length > 0 && s.push(R({
1113
+ if (!jt(t.oneOf, n.oneOf)) {
1114
+ n.oneOf && n.oneOf.length > 0 && s.push(I({
1081
1115
  kind: "drop-check",
1082
1116
  table: e,
1083
- column: n.name
1084
- }, "safe", `drop stale enum CHECK on ${e}.${n.name}`));
1085
- let t = Tt(e, n);
1086
- t && s.push(t);
1117
+ column: t.name
1118
+ }, "safe", `drop stale enum CHECK on ${e}.${t.name}`));
1119
+ let r = Ft(e, t);
1120
+ r && s.push(r);
1087
1121
  }
1088
- return JSON.stringify(bt(n.references)) !== JSON.stringify(bt(r.references)) && (r.references && s.push(R({
1122
+ return JSON.stringify(At(t.references)) !== JSON.stringify(At(n.references)) && (n.references && s.push(I({
1089
1123
  kind: "drop-foreign-key",
1090
1124
  table: e,
1091
- column: n.name
1092
- }, "safe", `drop FK on ${e}.${n.name} (→ ${r.references.table})`)), s.push(...Rt(e, n))), s;
1093
- }, Rt = (e, t) => {
1125
+ column: t.name
1126
+ }, "safe", `drop FK on ${e}.${t.name} (→ ${n.references.table})`)), s.push(...Jt(e, t))), s;
1127
+ }, Jt = (e, t) => {
1094
1128
  let n = [];
1095
1129
  if (!t.references) return n;
1096
1130
  let r = t.references.table, i = t.references.orphanPolicy ?? "fail", a = {
@@ -1102,15 +1136,15 @@ BEGIN
1102
1136
  ...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
1103
1137
  ...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
1104
1138
  };
1105
- return i === "null" && !t.nullable ? n.push(R(a, "lossy", `add FK ${e}.${t.name} → ${r}: orphanPolicy 'null' needs a nullable column (the NULL-out would violate NOT NULL)`, { fix: `make ${t.name} \`.nullable()\`, OR use \`orphanPolicy: 'delete'\`, OR clean the orphans and drop the policy` })) : i === "null" || i === "delete" ? n.push(R({
1139
+ return i === "null" && !t.nullable ? n.push(I(a, "lossy", `add FK ${e}.${t.name} → ${r}: orphanPolicy 'null' needs a nullable column (the NULL-out would violate NOT NULL)`, { fix: `make ${t.name} \`.nullable()\`, OR use \`orphanPolicy: 'delete'\`, OR clean the orphans and drop the policy` })) : i === "null" || i === "delete" ? n.push(I({
1106
1140
  ...a,
1107
1141
  orphanPolicy: i
1108
- }, "lossy", i === "delete" ? `add FK ${e}.${t.name} → ${r}: orphanPolicy 'delete' first DELETES rows whose ${t.name} points at a missing ${r}` : `add FK ${e}.${t.name} → ${r}: orphanPolicy 'null' first NULLs ${t.name} where it points at a missing ${r}`)) : n.push(R(a, "needs-backfill", `add FK ${e}.${t.name} → ${r} — existing rows must already reference a valid ${r} (else set orphanPolicy:'null'/'delete')`)), n;
1109
- }, zt = (e, t, n) => {
1142
+ }, "lossy", i === "delete" ? `add FK ${e}.${t.name} → ${r}: orphanPolicy 'delete' first DELETES rows whose ${t.name} points at a missing ${r}` : `add FK ${e}.${t.name} → ${r}: orphanPolicy 'null' first NULLs ${t.name} where it points at a missing ${r}`)) : n.push(I(a, "needs-backfill", `add FK ${e}.${t.name} → ${r} — existing rows must already reference a valid ${r} (else set orphanPolicy:'null'/'delete')`)), n;
1143
+ }, Yt = (e, t, n) => {
1110
1144
  let r = [], i = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), o = new Set(n.removed.map((e) => e.name)), s = new Set(n.added.map((e) => e.name));
1111
1145
  for (let [n, c] of Object.entries(t)) {
1112
1146
  let t = c.renamedFrom;
1113
- t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(R({
1147
+ t && o.has(t) && s.has(n) && (i.add(n), a.add(t), r.push(I({
1114
1148
  kind: "rename-column",
1115
1149
  table: e.tableName,
1116
1150
  from: t,
@@ -1122,26 +1156,26 @@ BEGIN
1122
1156
  consumedAddedNames: i,
1123
1157
  consumedRemovedNames: a
1124
1158
  };
1125
- }, Bt = /* @__PURE__ */ new Set([
1159
+ }, Xt = /* @__PURE__ */ new Set([
1126
1160
  "postgres",
1127
1161
  "mysql",
1128
1162
  "mariadb",
1129
1163
  "mssql"
1130
- ]), Vt = {
1164
+ ]), Zt = {
1131
1165
  ops: [],
1132
1166
  consumedAddedNames: /* @__PURE__ */ new Set(),
1133
1167
  consumedRemovedNames: /* @__PURE__ */ new Set()
1134
- }, Ht = (e) => !e.unique && !e.expression, Ut = (e, t, n, r, i) => {
1135
- if (i === void 0 || !Bt.has(i)) return Vt;
1136
- let a = t.added.filter((e) => Ht(e) && !r.has(e.name));
1137
- if (a.length === 0) return Vt;
1168
+ }, Qt = (e) => !e.unique && !e.expression, $t = (e, t, n, r, i) => {
1169
+ if (i === void 0 || !Xt.has(i)) return Zt;
1170
+ let a = t.added.filter((e) => Qt(e) && !r.has(e.name));
1171
+ if (a.length === 0) return Zt;
1138
1172
  let o = (e) => e.columns.map((e) => n.get(e) ?? e).join("\0"), s = (e) => e.columns.join("\0"), c = [], l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set();
1139
1173
  for (let n of t.removed) {
1140
- if (!Ht(n)) continue;
1174
+ if (!Qt(n)) continue;
1141
1175
  let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
1142
1176
  if (r.length !== 1) continue;
1143
1177
  let i = r[0];
1144
- i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(R({
1178
+ i.name !== n.name && (l.add(i.name), u.add(n.name), c.push(I({
1145
1179
  kind: "rename-index",
1146
1180
  table: e,
1147
1181
  from: n.name,
@@ -1153,7 +1187,7 @@ BEGIN
1153
1187
  consumedAddedNames: l,
1154
1188
  consumedRemovedNames: u
1155
1189
  };
1156
- }, Wt = (e, t, n) => {
1190
+ }, en = (e, t, n) => {
1157
1191
  let r = [], i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
1158
1192
  for (let t of e.tables) t.renamedFrom !== void 0 && (s.set(t.renamedFrom, (s.get(t.renamedFrom) ?? 0) + 1), c.add(t.renamedFrom));
1159
1193
  for (let c of e.tables) {
@@ -1164,7 +1198,7 @@ BEGIN
1164
1198
  continue;
1165
1199
  }
1166
1200
  if (t.has(c.name)) {
1167
- r.push(R({
1201
+ r.push(I({
1168
1202
  kind: "rename-table",
1169
1203
  from: e,
1170
1204
  to: c.name
@@ -1172,14 +1206,14 @@ BEGIN
1172
1206
  continue;
1173
1207
  }
1174
1208
  if ((s.get(e) ?? 0) !== 1) {
1175
- r.push(R({
1209
+ r.push(I({
1176
1210
  kind: "rename-table",
1177
1211
  from: e,
1178
1212
  to: c.name
1179
1213
  }, "needs-rename-annotation", `.renamedFrom('${e}') is claimed by ${s.get(e)} tables — nothing says which receives its rows`, { fix: `remove the \`.renamedFrom('${e}')\` marker from all but one of them.` }));
1180
1214
  continue;
1181
1215
  }
1182
- i.set(c.name, e), a.add(e), r.push(R({
1216
+ i.set(c.name, e), a.add(e), r.push(I({
1183
1217
  kind: "rename-table",
1184
1218
  from: e,
1185
1219
  to: c.name
@@ -1193,19 +1227,19 @@ BEGIN
1193
1227
  claimedOldNames: c,
1194
1228
  refusals: o
1195
1229
  };
1196
- }, Gt = (e, t, n, r) => {
1230
+ }, tn = (e, t, n, r) => {
1197
1231
  if (t === void 0) return {
1198
1232
  live: e,
1199
1233
  ops: []
1200
1234
  };
1201
- let i = r !== void 0 && Bt.has(r), a = [], o = e.indexes.map((e) => {
1235
+ let i = r !== void 0 && Xt.has(r), a = [], o = e.indexes.map((e) => {
1202
1236
  if (!e.name.startsWith(t)) return e;
1203
1237
  let r = `${n}${e.name.slice(t.length)}`;
1204
1238
  return r === e.name ? e : e.name === `${t}_pkey` ? {
1205
1239
  ...e,
1206
1240
  name: r,
1207
1241
  table: n
1208
- } : i ? (a.push(R({
1242
+ } : i ? (a.push(I({
1209
1243
  kind: "rename-index",
1210
1244
  table: n,
1211
1245
  from: e.name,
@@ -1224,7 +1258,7 @@ BEGIN
1224
1258
  },
1225
1259
  ops: a
1226
1260
  };
1227
- }, Kt = (e) => {
1261
+ }, nn = (e) => {
1228
1262
  let t = {
1229
1263
  safe: 0,
1230
1264
  needsDefault: 0,
@@ -1259,51 +1293,51 @@ BEGIN
1259
1293
  break;
1260
1294
  }
1261
1295
  return t;
1262
- }, qt = (e) => {
1263
- let t = I(e.declared, e.dialect), n = _t(e.declared, t), r = new Map(e.live.tables.map((e) => [e.name, e])), i = e.onlineAfter ?? Nt, a = [], o = Wt(t, r, n);
1296
+ }, rn = (e) => {
1297
+ let t = P(e.declared, e.dialect), n = Dt(e.declared, t), r = new Map(e.live.tables.map((e) => [e.name, e])), i = e.onlineAfter ?? Ut, a = [], o = en(t, r, n);
1264
1298
  a.push(...o.ops);
1265
1299
  for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
1266
- a.push(jt(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
1300
+ a.push(Vt(e.name, e.columns, e.indexes, e.primaryKey, o.refusals.get(e.name)));
1267
1301
  for (let t of e.columns) {
1268
- let n = Tt(e.name, t);
1302
+ let n = Ft(e.name, t);
1269
1303
  n && a.push(n);
1270
1304
  }
1271
1305
  }
1272
- let c = new Set(e.ignoreTables ?? []);
1273
- for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) || s(t.name) || u(t.name) || c.has(t.name) || a.push(Mt(t.name));
1306
+ let s = new Set(e.ignoreTables ?? []);
1307
+ for (let t of e.live.tables) n.has(t.name) || o.claimedOldNames.has(t.name) || m(t.name) || v(t.name) || s.has(t.name) || a.push(Ht(t.name));
1274
1308
  for (let s of t.tables) {
1275
1309
  let t = o.renamedInto.get(s.name), c = r.get(s.name) ?? (t === void 0 ? void 0 : r.get(t));
1276
1310
  if (!c) continue;
1277
- let l = Gt(c, t, s.name, e.dialect);
1311
+ let l = tn(c, t, s.name, e.dialect);
1278
1312
  a.push(...l.ops);
1279
- let d = l.live, f = n.get(s.name), p = Dt(s, d, e.dialect), m = Ot(s, d), h = zt(f.table, f.columnDefs, p);
1280
- a.push(...h.ops);
1281
- for (let e of p.removed) {
1282
- if (h.consumedRemovedNames.has(e.name) || u(e.name)) continue;
1283
- let t = f.columnDefs[e.name];
1284
- a.push(At(s.name, e, t));
1313
+ let u = l.live, d = n.get(s.name), f = Lt(s, u, e.dialect), p = Rt(s, u), m = Yt(d.table, d.columnDefs, f);
1314
+ a.push(...m.ops);
1315
+ for (let e of f.removed) {
1316
+ if (m.consumedRemovedNames.has(e.name) || v(e.name)) continue;
1317
+ let t = d.columnDefs[e.name];
1318
+ a.push(Bt(s.name, e, t));
1285
1319
  }
1286
- for (let e of p.added) {
1287
- if (h.consumedAddedNames.has(e.name)) continue;
1288
- let t = f.columnDefs[e.name];
1289
- a.push(kt(s.name, e, t));
1290
- let n = Tt(s.name, e);
1291
- n && a.push(n), a.push(...Rt(s.name, e));
1320
+ for (let e of f.added) {
1321
+ if (m.consumedAddedNames.has(e.name)) continue;
1322
+ let t = d.columnDefs[e.name];
1323
+ a.push(zt(s.name, e, t));
1324
+ let n = Ft(s.name, e);
1325
+ n && a.push(n), a.push(...Jt(s.name, e));
1292
1326
  }
1293
- let g = new Map(h.ops.flatMap((e) => e.op.kind === "rename-column" ? [[e.op.from, e.op.to]] : [])), _ = Ut(s.name, m, g, new Set(d.indexes.map((e) => e.name)), e.dialect);
1294
- a.push(..._.ops);
1295
- for (let e of m.added) _.consumedAddedNames.has(e.name) || a.push(Pt(s.name, e, d.rowCount, i));
1296
- for (let e of m.removed) _.consumedRemovedNames.has(e.name) || a.push(Ft(s.name, e));
1297
- for (let { declared: t, live: n } of p.changed) a.push(...Lt(s.name, t, n, e.dialect, d.rowCount, i));
1298
- }
1299
- let l = new Set(a.flatMap((e) => e.op.kind === "create-table" ? [e.op.table] : [])), d = new Map(t.tables.map((e) => [e.name, e])), f = (e) => {
1300
- let t = d.get(e);
1301
- return t ? t.columns.flatMap((e) => e.references && l.has(e.references.table) ? [{
1327
+ let h = new Map(m.ops.flatMap((e) => e.op.kind === "rename-column" ? [[e.op.from, e.op.to]] : [])), g = $t(s.name, p, h, new Set(u.indexes.map((e) => e.name)), e.dialect);
1328
+ a.push(...g.ops);
1329
+ for (let e of p.added) g.consumedAddedNames.has(e.name) || a.push(Wt(s.name, e, u.rowCount, i));
1330
+ for (let e of p.removed) g.consumedRemovedNames.has(e.name) || a.push(Gt(s.name, e));
1331
+ for (let { declared: t, live: n } of f.changed) a.push(...qt(s.name, t, n, e.dialect, u.rowCount, i));
1332
+ }
1333
+ let c = new Set(a.flatMap((e) => e.op.kind === "create-table" ? [e.op.table] : [])), l = new Map(t.tables.map((e) => [e.name, e])), u = (e) => {
1334
+ let t = l.get(e);
1335
+ return t ? t.columns.flatMap((e) => e.references && c.has(e.references.table) ? [{
1302
1336
  column: e.name,
1303
1337
  target: e.references.table
1304
1338
  }] : []) : [];
1305
- }, p = te([...l], f), m = [];
1306
- for (let e of t.tables) if (l.has(e.name)) for (let t of e.columns) !t.references || !p.has(T(e.name, t.name)) || m.push(R({
1339
+ }, d = le([...c], u), f = [];
1340
+ for (let e of t.tables) if (c.has(e.name)) for (let t of e.columns) !t.references || !d.has(w(e.name, t.name)) || f.push(I({
1307
1341
  kind: "add-foreign-key",
1308
1342
  table: e.name,
1309
1343
  column: t.name,
@@ -1312,14 +1346,14 @@ BEGIN
1312
1346
  ...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
1313
1347
  ...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
1314
1348
  }, "safe", `deferred FK ${e.name}.${t.name} → ${t.references.table} — breaks a create-time table cycle`));
1315
- if (p.size > 0) {
1349
+ if (d.size > 0) {
1316
1350
  let e = (e) => e.references ? Object.fromEntries(Object.entries(e).filter(([e]) => e !== "references")) : e;
1317
1351
  for (let t = 0; t < a.length; t += 1) {
1318
1352
  let n = a[t];
1319
1353
  if (n.op.kind !== "create-table") continue;
1320
1354
  let r = n.op;
1321
- if (!r.columns.some((e) => p.has(T(r.table, e.name)))) continue;
1322
- let i = r.columns.map((t) => p.has(T(r.table, t.name)) ? e(t) : t);
1355
+ if (!r.columns.some((e) => d.has(w(r.table, e.name)))) continue;
1356
+ let i = r.columns.map((t) => d.has(w(r.table, t.name)) ? e(t) : t);
1323
1357
  a[t] = {
1324
1358
  ...n,
1325
1359
  op: {
@@ -1328,23 +1362,23 @@ BEGIN
1328
1362
  }
1329
1363
  };
1330
1364
  }
1331
- a.push(...m);
1365
+ a.push(...f);
1332
1366
  }
1333
- let h = new Map(t.tables.map((e) => [e.name, e])), g = /* @__PURE__ */ new Map(), _ = (e, t = /* @__PURE__ */ new Set()) => {
1334
- let n = g.get(e);
1367
+ let p = new Map(t.tables.map((e) => [e.name, e])), h = /* @__PURE__ */ new Map(), g = (e, t = /* @__PURE__ */ new Set()) => {
1368
+ let n = h.get(e);
1335
1369
  if (n !== void 0) return n;
1336
1370
  if (t.has(e)) return 0;
1337
- let r = h.get(e);
1371
+ let r = p.get(e);
1338
1372
  if (!r) return 0;
1339
1373
  let i = new Set(t).add(e), a = 0;
1340
1374
  for (let t of r.columns) {
1341
1375
  let n = t.references?.table;
1342
- n && n !== e && !p.has(T(e, t.name)) && (a = Math.max(a, _(n, i) + 1));
1376
+ n && n !== e && !d.has(w(e, t.name)) && (a = Math.max(a, g(n, i) + 1));
1343
1377
  }
1344
- return g.set(e, a), a;
1345
- }, v = (e) => e === "rename-table" ? -1 : e === "create-table" ? 0 : e === "drop-table" ? 2 : 1, y = (e) => {
1378
+ return h.set(e, a), a;
1379
+ }, _ = (e) => e === "rename-table" ? -1 : e === "create-table" ? 0 : e === "drop-table" ? 2 : 1, y = (e) => {
1346
1380
  let t = "table" in e ? e.table : "";
1347
- return e.kind === "create-table" ? _(t) : e.kind === "drop-table" ? -_(t) : 0;
1381
+ return e.kind === "create-table" ? g(t) : e.kind === "drop-table" ? -g(t) : 0;
1348
1382
  }, b = {
1349
1383
  "add-column": 0,
1350
1384
  "rename-column": 1,
@@ -1364,7 +1398,7 @@ BEGIN
1364
1398
  "add-foreign-key": 5,
1365
1399
  "add-check": 5
1366
1400
  }, x = (e) => b[e] ?? 9, S = [...a].sort((e, t) => {
1367
- let n = v(e.op.kind), r = v(t.op.kind);
1401
+ let n = _(e.op.kind), r = _(t.op.kind);
1368
1402
  if (n !== r) return n - r;
1369
1403
  let i = y(e.op), a = y(t.op);
1370
1404
  if (i !== a) return i - a;
@@ -1375,11 +1409,87 @@ BEGIN
1375
1409
  });
1376
1410
  return {
1377
1411
  operations: S,
1378
- fromFingerprint: P(e.live),
1379
- toFingerprint: P(t),
1380
- summary: Kt(S)
1412
+ fromFingerprint: M(e.live),
1413
+ toFingerprint: M(t),
1414
+ summary: nn(S)
1381
1415
  };
1382
- }, Jt = (e) => {
1416
+ }, an = (e) => e.onDialectOrElse({
1417
+ pg: () => "postgres",
1418
+ mysql: () => "mysql",
1419
+ mssql: () => "mssql",
1420
+ sqlite: () => "sqlite",
1421
+ orElse: () => "sqlite"
1422
+ }), on = {
1423
+ postgres: "plan-transactional",
1424
+ mssql: "plan-transactional",
1425
+ mysql: "per-operation",
1426
+ mariadb: "per-operation",
1427
+ sqlite: "per-operation",
1428
+ turso: "per-operation"
1429
+ }, sn = (e) => on[e], L = "_voltro_migration_ops", cn = l(L, {
1430
+ id: e({ prefix: "mop" }),
1431
+ runId: _(),
1432
+ planFingerprint: _(),
1433
+ opIndex: n(),
1434
+ opKind: _(),
1435
+ opTarget: _(),
1436
+ status: _(),
1437
+ payload: _(),
1438
+ startedAt: t().nullable(),
1439
+ appliedAt: t().nullable(),
1440
+ durationMs: n().nullable(),
1441
+ createdAt: t().default("now")
1442
+ }), ln = (e) => {
1443
+ switch (e.kind) {
1444
+ case "create-table": return /* @__PURE__ */ new Set([
1445
+ `table:${e.table}`,
1446
+ ...e.columns.map((t) => `column:${e.table}.${t.name}`),
1447
+ ...e.indexes.map((t) => `index:${e.table}.${t.name}`)
1448
+ ]);
1449
+ case "drop-table": return /* @__PURE__ */ new Set([`table:${e.table}`]);
1450
+ case "rename-table": return /* @__PURE__ */ new Set([`table:${e.from}`, `table:${e.to}`]);
1451
+ case "add-column": return /* @__PURE__ */ new Set([`column:${e.table}.${e.column.name}`]);
1452
+ case "rename-column": return /* @__PURE__ */ new Set([`column:${e.table}.${e.from}`, `column:${e.table}.${e.to}`]);
1453
+ case "drop-column":
1454
+ case "alter-column-nullability":
1455
+ case "alter-column-type":
1456
+ case "alter-column-default":
1457
+ case "add-unique":
1458
+ case "drop-unique":
1459
+ case "add-foreign-key":
1460
+ case "drop-foreign-key":
1461
+ case "add-check":
1462
+ case "drop-check": return /* @__PURE__ */ new Set([`column:${e.table}.${e.column}`]);
1463
+ case "add-index": return /* @__PURE__ */ new Set([`index:${e.table}.${e.index.name}`]);
1464
+ case "drop-index": return /* @__PURE__ */ new Set([`index:${e.table}.${e.index}`]);
1465
+ case "rename-index": return /* @__PURE__ */ new Set([`index:${e.table}.${e.from}`, `index:${e.table}.${e.to}`]);
1466
+ case "add-unique-composite":
1467
+ case "drop-unique-composite": return /* @__PURE__ */ new Set([`unique:${e.table}.${e.name}`]);
1468
+ }
1469
+ }, un = (e) => [...ln(e)].join(" "), dn = (e, t) => {
1470
+ let n = ln(t);
1471
+ for (let t of ln(e)) if (n.has(t)) return !0;
1472
+ return !1;
1473
+ }, fn = (e, t) => e.operations.some((e) => dn(e.op, t)), pn = "__voltro_rebuild", mn = "__shadow", hn = "__old", gn = /* @__PURE__ */ new Set([
1474
+ "add-unique",
1475
+ "drop-unique",
1476
+ "add-unique-composite",
1477
+ "drop-unique-composite",
1478
+ "add-foreign-key",
1479
+ "drop-foreign-key",
1480
+ "add-check",
1481
+ "drop-check",
1482
+ "alter-column-nullability",
1483
+ "alter-column-type",
1484
+ "alter-column-default"
1485
+ ]), _n = /* @__PURE__ */ new Set(["alter-column-default", "alter-column-nullability"]), vn = (e, t, n) => {
1486
+ let r = n === "sqlite" || n === "turso";
1487
+ return e.kind === "alter-column-type" && t === "online-required" || r && gn.has(e.kind) ? "artefact-repair" : _n.has(e.kind) ? "idempotent" : "probe-then-replay";
1488
+ };
1489
+ Object.keys(on);
1490
+ //#endregion
1491
+ //#region src/migrations/introspect.ts
1492
+ var yn = (e) => {
1383
1493
  let t = /[`"[]?([A-Za-z_][A-Za-z0-9_]*)[`"\]]?\s*(?:in\b|=)/i.exec(e);
1384
1494
  if (!t) return null;
1385
1495
  let n = [...e.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
@@ -1387,7 +1497,7 @@ BEGIN
1387
1497
  column: t[1],
1388
1498
  values: n
1389
1499
  };
1390
- }, Yt = (e, t) => {
1500
+ }, bn = (e, t) => {
1391
1501
  if (t === "vector") return "vector";
1392
1502
  switch (e) {
1393
1503
  case "text":
@@ -1414,7 +1524,7 @@ BEGIN
1414
1524
  case "USER-DEFINED": return "text";
1415
1525
  default: return "text";
1416
1526
  }
1417
- }, z = (e) => {
1527
+ }, R = (e) => {
1418
1528
  switch (e.toUpperCase()) {
1419
1529
  case "CASCADE": return "cascade";
1420
1530
  case "RESTRICT": return "restrict";
@@ -1422,25 +1532,25 @@ BEGIN
1422
1532
  case "SET DEFAULT": return "noAction";
1423
1533
  default: return "noAction";
1424
1534
  }
1425
- }, Xt = (e, t) => {
1535
+ }, xn = (e, t) => {
1426
1536
  let n = e !== void 0 && e !== "" ? Number(e) : NaN;
1427
1537
  return Number.isInteger(n) && n > 0 ? n : t;
1428
- }, Zt = () => Xt(process.env.VOLTRO_INTROSPECT_BATCH, 20), Qt = 300, $t = () => {
1538
+ }, Sn = () => xn(process.env.VOLTRO_INTROSPECT_BATCH, 20), Cn = 300, wn = () => {
1429
1539
  let e = process.env.VOLTRO_INTROSPECT_TIMEOUT_MS;
1430
- return e !== void 0 && e.trim() === "0" ? 0 : Xt(e, 3e4);
1431
- }, en = (e, t) => {
1540
+ return e !== void 0 && e.trim() === "0" ? 0 : xn(e, 3e4);
1541
+ }, Tn = (e, t) => {
1432
1542
  let n = Number.isFinite(t) && t >= 1 ? Math.floor(t) : 1, r = [];
1433
1543
  for (let t = 0; t < e.length; t += n) r.push(e.slice(t, t + n));
1434
1544
  return r;
1435
- }, tn = (e) => {
1436
- let t = $t(), n = p.gen(function* () {
1545
+ }, En = (e) => {
1546
+ let t = wn(), n = b.gen(function* () {
1437
1547
  t > 0 && (yield* e.unsafe(`SET LOCAL statement_timeout = ${t}`));
1438
- let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i = Zt(), a = (e, t) => p.gen(function* () {
1548
+ let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i = Sn(), a = (e, t) => b.gen(function* () {
1439
1549
  let r = [];
1440
- for (let a of en(e, i)) a.length !== 0 && r.push(...yield* t(n(a)));
1550
+ for (let a of Tn(e, i)) a.length !== 0 && r.push(...yield* t(n(a)));
1441
1551
  return r;
1442
1552
  }), o = [];
1443
- for (let t = 0;; t += Qt) {
1553
+ for (let t = 0;; t += Cn) {
1444
1554
  let n = yield* e`
1445
1555
  SELECT c.relname AS table_name,
1446
1556
  COALESCE(c.reltuples::bigint, 0) AS row_count
@@ -1448,9 +1558,9 @@ BEGIN
1448
1558
  JOIN pg_namespace n ON n.oid = c.relnamespace
1449
1559
  WHERE n.nspname = current_schema() AND c.relkind = 'r'
1450
1560
  ORDER BY c.relname
1451
- LIMIT ${r(Qt)} OFFSET ${r(t)}
1561
+ LIMIT ${r(Cn)} OFFSET ${r(t)}
1452
1562
  `;
1453
- if (o.push(...n), n.length < Qt) break;
1563
+ if (o.push(...n), n.length < Cn) break;
1454
1564
  }
1455
1565
  let s = /* @__PURE__ */ new Set(), c = [];
1456
1566
  for (let e of o) s.has(e.table_name) || (s.add(e.table_name), c.push(e));
@@ -1477,7 +1587,7 @@ BEGIN
1477
1587
  JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
1478
1588
  JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = ANY (con.conkey)
1479
1589
  WHERE con.contype = 'u' AND nsp.nspname = current_schema() AND rel.relname IN ${t}
1480
- `), m = yield* a(l, (t) => e`
1590
+ `), p = yield* a(l, (t) => e`
1481
1591
  SELECT rel.relname AS table_name, att.attname AS column_name, pg_get_constraintdef(con.oid) AS def
1482
1592
  FROM pg_constraint con
1483
1593
  JOIN pg_class rel ON rel.oid = con.conrelid
@@ -1485,12 +1595,12 @@ BEGIN
1485
1595
  JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = con.conkey[1]
1486
1596
  WHERE con.contype = 'c' AND nsp.nspname = current_schema() AND array_length(con.conkey, 1) = 1
1487
1597
  AND rel.relname IN ${t}
1488
- `), h = /* @__PURE__ */ new Map();
1489
- for (let e of m) {
1598
+ `), m = /* @__PURE__ */ new Map();
1599
+ for (let e of p) {
1490
1600
  let t = [...e.def.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
1491
- t.length > 0 && h.set(`${e.table_name}.${e.column_name}`, t);
1601
+ t.length > 0 && m.set(`${e.table_name}.${e.column_name}`, t);
1492
1602
  }
1493
- let g = yield* a(l, (t) => e`
1603
+ let h = yield* a(l, (t) => e`
1494
1604
  SELECT
1495
1605
  rel.relname AS source_table,
1496
1606
  att.attname AS source_column,
@@ -1515,7 +1625,7 @@ BEGIN
1515
1625
  WHERE con.contype = 'f' AND nsp.nspname = current_schema()
1516
1626
  AND rel.relname IN ${t}
1517
1627
  ORDER BY rel.relname, con.conname, ck.ord
1518
- `), _ = yield* a(l, (t) => e`
1628
+ `), g = yield* a(l, (t) => e`
1519
1629
  WITH ix AS (
1520
1630
  SELECT
1521
1631
  c.relname AS table_name,
@@ -1536,31 +1646,31 @@ BEGIN
1536
1646
  AND c.relname IN ${t}
1537
1647
  )
1538
1648
  SELECT * FROM ix ORDER BY table_name, index_name, position
1539
- `), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), ee = /* @__PURE__ */ new Map();
1649
+ `), _ = /* @__PURE__ */ new Map(), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map();
1540
1650
  for (let e of u) {
1541
- let t = v.get(e.table_name);
1542
- t || (t = [], v.set(e.table_name, t)), t.push(e);
1651
+ let t = _.get(e.table_name);
1652
+ t || (t = [], _.set(e.table_name, t)), t.push(e);
1543
1653
  }
1544
1654
  for (let e of d) {
1545
- let t = y.get(e.table_name);
1546
- t || (t = /* @__PURE__ */ new Set(), y.set(e.table_name, t)), t.add(e.column_name);
1655
+ let t = v.get(e.table_name);
1656
+ t || (t = /* @__PURE__ */ new Set(), v.set(e.table_name, t)), t.add(e.column_name);
1547
1657
  }
1548
- let C = /* @__PURE__ */ new Map();
1658
+ let ee = /* @__PURE__ */ new Map();
1549
1659
  for (let e of f) {
1550
- let t = `${e.table_name}\u0000${e.constraint_name}`, n = C.get(t);
1660
+ let t = `${e.table_name}\u0000${e.constraint_name}`, n = ee.get(t);
1551
1661
  n || (n = {
1552
1662
  table: e.table_name,
1553
1663
  columns: []
1554
- }, C.set(t, n)), n.columns.push({
1664
+ }, ee.set(t, n)), n.columns.push({
1555
1665
  name: e.column_name,
1556
1666
  position: e.position
1557
1667
  });
1558
1668
  }
1559
- for (let [e, t] of C) {
1669
+ for (let [e, t] of ee) {
1560
1670
  let n = [...t.columns].sort((e, t) => e.position - t.position).map((e) => e.name);
1561
1671
  if (n.length === 1) {
1562
- let e = b.get(t.table);
1563
- e || (e = /* @__PURE__ */ new Set(), b.set(t.table, e)), e.add(n[0]);
1672
+ let e = y.get(t.table);
1673
+ e || (e = /* @__PURE__ */ new Set(), y.set(t.table, e)), e.add(n[0]);
1564
1674
  } else {
1565
1675
  let r = x.get(t.table);
1566
1676
  r || (r = [], x.set(t.table, r)), r.push({
@@ -1571,29 +1681,29 @@ BEGIN
1571
1681
  });
1572
1682
  }
1573
1683
  }
1574
- for (let e of g) {
1684
+ for (let e of h) {
1575
1685
  let t = S.get(e.source_table);
1576
1686
  t || (t = [], S.set(e.source_table, t)), t.push(e);
1577
1687
  }
1578
- for (let e of _) {
1579
- let t = ee.get(e.table_name);
1580
- t || (t = /* @__PURE__ */ new Map(), ee.set(e.table_name, t));
1688
+ for (let e of g) {
1689
+ let t = C.get(e.table_name);
1690
+ t || (t = /* @__PURE__ */ new Map(), C.set(e.table_name, t));
1581
1691
  let n = t.get(e.index_name);
1582
1692
  n || (n = [], t.set(e.index_name, n)), n.push(e);
1583
1693
  }
1584
- let w = [];
1694
+ let te = [];
1585
1695
  for (let e of c) {
1586
- let t = /* @__PURE__ */ new Set(), n = (v.get(e.table_name) ?? []).filter((e) => t.has(e.column_name) ? !1 : (t.add(e.column_name), !0)), r = y.get(e.table_name) ?? /* @__PURE__ */ new Set(), i = b.get(e.table_name) ?? /* @__PURE__ */ new Set(), a = S.get(e.table_name) ?? [], o = new Map(a.map((e) => [e.source_column, e])), s = new Set(n.filter((e) => e.udt_name === "tsvector").map((e) => e.column_name)), c = n.filter((e) => !s.has(e.column_name)).map((t) => {
1587
- let n = r.has(t.column_name) && r.size === 1, a = o.get(t.column_name), s = a ? "reference" : n && t.column_name === "id" ? "id" : Yt(t.data_type, t.udt_name), c = a ? {
1696
+ let t = /* @__PURE__ */ new Set(), n = (_.get(e.table_name) ?? []).filter((e) => t.has(e.column_name) ? !1 : (t.add(e.column_name), !0)), r = v.get(e.table_name) ?? /* @__PURE__ */ new Set(), i = y.get(e.table_name) ?? /* @__PURE__ */ new Set(), a = S.get(e.table_name) ?? [], o = new Map(a.map((e) => [e.source_column, e])), s = new Set(n.filter((e) => e.udt_name === "tsvector").map((e) => e.column_name)), c = n.filter((e) => !s.has(e.column_name)).map((t) => {
1697
+ let n = r.has(t.column_name) && r.size === 1, a = o.get(t.column_name), s = a ? "reference" : n && t.column_name === "id" ? "id" : bn(t.data_type, t.udt_name), c = a ? {
1588
1698
  table: a.target_table,
1589
1699
  column: a.target_column,
1590
- onDelete: z(a.delete_rule),
1591
- onUpdate: z(a.update_rule)
1592
- } : void 0, l = h.get(`${e.table_name}.${t.column_name}`);
1700
+ onDelete: R(a.delete_rule),
1701
+ onUpdate: R(a.update_rule)
1702
+ } : void 0, l = m.get(`${e.table_name}.${t.column_name}`);
1593
1703
  return {
1594
1704
  name: t.column_name,
1595
1705
  type: s,
1596
- ...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
1706
+ ...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
1597
1707
  nullable: t.is_nullable === "YES",
1598
1708
  unique: i.has(t.column_name) || n && t.column_name === "id",
1599
1709
  hasDefault: t.column_default !== null,
@@ -1601,7 +1711,7 @@ BEGIN
1601
1711
  ...c ? { references: c } : {},
1602
1712
  ...l ? { oneOf: l } : {}
1603
1713
  };
1604
- }), l = [], u = ee.get(e.table_name);
1714
+ }), l = [], u = C.get(e.table_name);
1605
1715
  if (u) for (let [t, n] of u) {
1606
1716
  let r = /* @__PURE__ */ new Set(), i = [...n].filter((e) => e.column_name !== null && e.position !== null).sort((e, t) => (e.position ?? 0) - (t.position ?? 0)).filter((e) => {
1607
1717
  let t = e.position;
@@ -1625,20 +1735,20 @@ BEGIN
1625
1735
  unique: !0
1626
1736
  });
1627
1737
  let d = Number(e.row_count ?? 0);
1628
- w.push({
1738
+ te.push({
1629
1739
  name: e.table_name,
1630
1740
  columns: c,
1631
1741
  indexes: l,
1632
1742
  ...d > 0 ? { rowCount: d } : {}
1633
1743
  });
1634
1744
  }
1635
- return { tables: w };
1745
+ return { tables: te };
1636
1746
  });
1637
- return (t > 0 ? e.withTransaction(n) : n).pipe(p.catchAll((e) => {
1747
+ return (t > 0 ? e.withTransaction(n) : n).pipe(b.catchAll((e) => {
1638
1748
  let n = `${String(e)} ${String(e.cause ?? "")}`.toLowerCase();
1639
- return n.includes("statement timeout") || n.includes("57014") ? p.die(/* @__PURE__ */ Error(`schema introspection exceeded VOLTRO_INTROSPECT_TIMEOUT_MS (${t}ms) — the schema is very large / FK-dense or the database is slow. Raise VOLTRO_INTROSPECT_TIMEOUT_MS, set it to 0 to disable the ceiling, or use DB_DIRECT_URL for a non-pooler connection.`)) : p.fail(e);
1749
+ return n.includes("statement timeout") || n.includes("57014") ? b.die(/* @__PURE__ */ Error(`schema introspection exceeded VOLTRO_INTROSPECT_TIMEOUT_MS (${t}ms) — the schema is very large / FK-dense or the database is slow. Raise VOLTRO_INTROSPECT_TIMEOUT_MS, set it to 0 to disable the ceiling, or use DB_DIRECT_URL for a non-pooler connection.`)) : b.fail(e);
1640
1750
  }));
1641
- }, nn = (e, t) => {
1751
+ }, Dn = (e, t) => {
1642
1752
  switch (e.toLowerCase()) {
1643
1753
  case "varchar":
1644
1754
  case "char":
@@ -1670,21 +1780,21 @@ BEGIN
1670
1780
  case "json": return "json";
1671
1781
  default: return "text";
1672
1782
  }
1673
- }, B = (e) => e.map((e) => {
1783
+ }, z = (e) => e.map((e) => {
1674
1784
  let t = e, n = {};
1675
1785
  for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
1676
1786
  return n;
1677
- }), rn = (e) => p.gen(function* () {
1678
- let t = B(yield* e`
1787
+ }), On = (e) => b.gen(function* () {
1788
+ let t = z(yield* e`
1679
1789
  SELECT table_name, COALESCE(table_rows, 0) AS table_rows
1680
1790
  FROM information_schema.tables
1681
1791
  WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
1682
- ORDER BY table_name`), n = B(yield* e`
1792
+ ORDER BY table_name`), n = z(yield* e`
1683
1793
  SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
1684
1794
  character_maximum_length
1685
1795
  FROM information_schema.columns
1686
1796
  WHERE table_schema = DATABASE()
1687
- ORDER BY table_name, ordinal_position`), r = B(yield* e`
1797
+ ORDER BY table_name, ordinal_position`), r = z(yield* e`
1688
1798
  SELECT k.table_name AS source_table,
1689
1799
  k.column_name AS source_column,
1690
1800
  k.referenced_table_name AS target_table,
@@ -1694,21 +1804,21 @@ BEGIN
1694
1804
  JOIN information_schema.referential_constraints r
1695
1805
  ON k.constraint_name = r.constraint_name
1696
1806
  AND k.table_schema = r.constraint_schema
1697
- WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i = B(yield* e`
1807
+ WHERE k.table_schema = DATABASE() AND k.referenced_table_name IS NOT NULL`), i = z(yield* e`
1698
1808
  SELECT table_name, index_name, non_unique, column_name, seq_in_index
1699
1809
  FROM information_schema.statistics
1700
1810
  WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
1701
- ORDER BY table_name, index_name, seq_in_index`), a = B(yield* e`
1811
+ ORDER BY table_name, index_name, seq_in_index`), a = z(yield* e`
1702
1812
  SELECT table_name, check_clause
1703
1813
  FROM information_schema.check_constraints
1704
- WHERE constraint_schema = DATABASE()`.unprepared.pipe(p.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
1814
+ WHERE constraint_schema = DATABASE()`.unprepared.pipe(b.orElseSucceed(() => []))), o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Map();
1705
1815
  for (let e of a) {
1706
1816
  let t = /json_valid\(`?([^`)]+)`?\)/i.exec(e.check_clause);
1707
1817
  if (t) {
1708
1818
  o.add(`${e.table_name}.${t[1]}`);
1709
1819
  continue;
1710
1820
  }
1711
- let n = Jt(e.check_clause);
1821
+ let n = yn(e.check_clause);
1712
1822
  n && s.set(`${e.table_name}.${n.column}`, n.values);
1713
1823
  }
1714
1824
  let c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
@@ -1731,17 +1841,17 @@ BEGIN
1731
1841
  let t = c.get(e.table_name) ?? [], n = l.get(e.table_name) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = t.filter((e) => e.column_key === "PRI").map((e) => e.column_name), a = /* @__PURE__ */ new Set(), f = u.get(e.table_name);
1732
1842
  if (f) for (let e of f.values()) e.length === 1 && e[0].non_unique === 0 && a.add(e[0].column_name);
1733
1843
  let p = t.map((t) => {
1734
- let n = i.includes(t.column_name) && i.length === 1, c = r.get(t.column_name), l = c ? "reference" : n && t.column_name === "id" ? "id" : o.has(`${e.table_name}.${t.column_name}`) ? "json" : nn(t.data_type, t.column_type), u = c ? {
1844
+ let n = i.includes(t.column_name) && i.length === 1, c = r.get(t.column_name), l = c ? "reference" : n && t.column_name === "id" ? "id" : o.has(`${e.table_name}.${t.column_name}`) ? "json" : Dn(t.data_type, t.column_type), u = c ? {
1735
1845
  table: c.target_table,
1736
1846
  column: c.target_column,
1737
- onDelete: z(c.delete_rule),
1738
- onUpdate: z(c.update_rule)
1847
+ onDelete: R(c.delete_rule),
1848
+ onUpdate: R(c.update_rule)
1739
1849
  } : void 0, d = a.has(t.column_name) || n && t.column_name === "id", f = t.column_default === "NULL" ? null : t.column_default, p = s.get(`${e.table_name}.${t.column_name}`);
1740
1850
  return {
1741
1851
  name: t.column_name,
1742
1852
  type: l,
1743
1853
  nullable: t.is_nullable === "YES",
1744
- ...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
1854
+ ...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
1745
1855
  unique: d,
1746
1856
  hasDefault: f !== null,
1747
1857
  ...f === null ? {} : { defaultValue: f },
@@ -1773,10 +1883,10 @@ BEGIN
1773
1883
  });
1774
1884
  }
1775
1885
  return { tables: d };
1776
- }), an = (e) => {
1886
+ }), kn = (e) => {
1777
1887
  let t = e.toUpperCase();
1778
1888
  return t.includes("INT") ? "integer" : t.includes("REAL") || t.includes("FLOA") || t.includes("DOUB") ? "real" : t.includes("BLOB") ? "bytes" : "text";
1779
- }, on = (e) => p.gen(function* () {
1889
+ }, An = (e) => b.gen(function* () {
1780
1890
  let t = yield* e`
1781
1891
  SELECT name, sql FROM sqlite_master
1782
1892
  WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
@@ -1793,11 +1903,11 @@ BEGIN
1793
1903
  d.set(t.name, r), t.unique === 1 && r.length === 1 && f.add(r[0]);
1794
1904
  }
1795
1905
  let p = o.map((e) => {
1796
- let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" : an(e.type), a = r ? {
1906
+ let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" : kn(e.type), a = r ? {
1797
1907
  table: r.table,
1798
1908
  column: r.to,
1799
- onDelete: z(r.on_delete),
1800
- onUpdate: z(r.on_update)
1909
+ onDelete: R(r.on_delete),
1910
+ onUpdate: R(r.on_update)
1801
1911
  } : void 0, o = t.get(e.name);
1802
1912
  return {
1803
1913
  name: e.name,
@@ -1832,7 +1942,7 @@ BEGIN
1832
1942
  });
1833
1943
  }
1834
1944
  return { tables: r };
1835
- }), sn = (e) => {
1945
+ }), jn = (e) => {
1836
1946
  switch (e.toLowerCase()) {
1837
1947
  case "int":
1838
1948
  case "smallint":
@@ -1854,7 +1964,7 @@ BEGIN
1854
1964
  case "image": return "bytes";
1855
1965
  default: return "text";
1856
1966
  }
1857
- }, V = (e) => e === !0 || e === 1, cn = (e) => p.gen(function* () {
1967
+ }, B = (e) => e === !0 || e === 1, Mn = (e) => b.gen(function* () {
1858
1968
  let t = yield* e`
1859
1969
  SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
1860
1970
  WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
@@ -1911,21 +2021,21 @@ BEGIN
1911
2021
  let t = s.get(e) ?? [], n = c.get(e) ?? [], r = new Map(n.map((e) => [e.source_column, e])), i = new Set(n.map((e) => e.source_column)), a = l.get(e), d = [], f = /* @__PURE__ */ new Set();
1912
2022
  if (a) for (let [, e] of a) {
1913
2023
  let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
1914
- if (V(t[0].is_primary)) for (let e of t) d.push(e.column_name);
1915
- else V(t[0].is_unique) && t.length === 1 && f.add(t[0].column_name);
2024
+ if (B(t[0].is_primary)) for (let e of t) d.push(e.column_name);
2025
+ else B(t[0].is_unique) && t.length === 1 && f.add(t[0].column_name);
1916
2026
  }
1917
2027
  let p = t.map((t) => {
1918
- let n = d.length === 1 && d[0] === t.column_name, i = r.get(t.column_name), a = i ? "reference" : n && t.column_name === "id" ? "id" : sn(t.data_type), s = i ? {
2028
+ let n = d.length === 1 && d[0] === t.column_name, i = r.get(t.column_name), a = i ? "reference" : n && t.column_name === "id" ? "id" : jn(t.data_type), s = i ? {
1919
2029
  table: i.target_table,
1920
2030
  column: i.target_column,
1921
- onDelete: ln(i.delete_rule),
1922
- onUpdate: ln(i.update_rule)
1923
- } : void 0, c = t.column_default === null ? null : un(t.column_default), l = o.get(`${e}.${t.column_name}`);
2031
+ onDelete: Nn(i.delete_rule),
2032
+ onUpdate: Nn(i.update_rule)
2033
+ } : void 0, c = t.column_default === null ? null : Pn(t.column_default), l = o.get(`${e}.${t.column_name}`);
1924
2034
  return {
1925
2035
  name: t.column_name,
1926
2036
  type: a,
1927
2037
  nullable: t.is_nullable === "YES",
1928
- ...H(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: H(t.data_type, t.character_maximum_length) },
2038
+ ...V(t.data_type, t.character_maximum_length) === void 0 ? {} : { maxLength: V(t.data_type, t.character_maximum_length) },
1929
2039
  unique: f.has(t.column_name) || n && t.column_name === "id",
1930
2040
  hasDefault: c !== null,
1931
2041
  ...c === null ? {} : { defaultValue: c },
@@ -1935,11 +2045,11 @@ BEGIN
1935
2045
  }), m = [];
1936
2046
  if (a) for (let [t, n] of a) {
1937
2047
  let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
1938
- V(r[0].is_primary) || r.length === 1 && V(r[0].is_unique) || r.length === 1 && !V(r[0].is_unique) && i.has(r[0].column_name) && t !== `${e}_${r[0].column_name}_idx` || m.push({
2048
+ B(r[0].is_primary) || r.length === 1 && B(r[0].is_unique) || r.length === 1 && !B(r[0].is_unique) && i.has(r[0].column_name) && t !== `${e}_${r[0].column_name}_idx` || m.push({
1939
2049
  name: t,
1940
2050
  table: e,
1941
2051
  columns: r.map((e) => e.column_name),
1942
- unique: V(r[0].is_unique)
2052
+ unique: B(r[0].is_unique)
1943
2053
  });
1944
2054
  }
1945
2055
  d.length > 0 && m.push({
@@ -1954,51 +2064,54 @@ BEGIN
1954
2064
  });
1955
2065
  }
1956
2066
  return { tables: u };
1957
- }), ln = (e) => {
2067
+ }), Nn = (e) => {
1958
2068
  switch (e.toUpperCase().replace(/_/g, " ")) {
1959
2069
  case "CASCADE": return "cascade";
1960
2070
  case "SET NULL": return "setNull";
1961
2071
  case "SET DEFAULT": return "noAction";
1962
2072
  default: return "noAction";
1963
2073
  }
1964
- }, un = (e) => {
2074
+ }, Pn = (e) => {
1965
2075
  let t = e.trim();
1966
2076
  for (; t.startsWith("(") && t.endsWith(")");) t = t.slice(1, -1).trim();
1967
2077
  return t.startsWith("N'") && t.endsWith("'") ? t = t.slice(2, -1) : t.startsWith("'") && t.endsWith("'") && (t = t.slice(1, -1)), t;
1968
- }, dn = /* @__PURE__ */ new Set([
2078
+ }, Fn = /* @__PURE__ */ new Set([
1969
2079
  "character varying",
1970
2080
  "varchar",
1971
2081
  "nvarchar",
1972
2082
  "char",
1973
2083
  "character",
1974
2084
  "nchar"
1975
- ]), H = (e, t) => {
1976
- if (!dn.has(e.toLowerCase())) return;
2085
+ ]), V = (e, t) => {
2086
+ if (!Fn.has(e.toLowerCase())) return;
1977
2087
  let n = Number(t);
1978
2088
  if (!(!Number.isFinite(n) || n <= 0)) return n;
1979
- }, U = (e) => e.onDialectOrElse({
1980
- mysql: () => rn(e),
1981
- mssql: () => cn(e),
1982
- sqlite: () => on(e),
1983
- orElse: () => tn(e)
1984
- }), fn = (e, t) => e.onDialectOrElse({
1985
- mysql: () => p.succeed(t.replace("T", " ").replace("Z", "")),
1986
- orElse: () => p.succeed(t)
1987
- }), pn = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, W = (e, t) => {
2089
+ }, In = /* @__PURE__ */ new Set([L]), Ln = (e) => e.onDialectOrElse({
2090
+ mysql: () => On(e),
2091
+ mssql: () => Mn(e),
2092
+ sqlite: () => An(e),
2093
+ orElse: () => En(e)
2094
+ }).pipe(b.map((e) => ({
2095
+ ...e,
2096
+ tables: e.tables.filter((e) => !In.has(e.name))
2097
+ }))), H = (e, t) => e.onDialectOrElse({
2098
+ mysql: () => b.succeed(t.replace("T", " ").replace("Z", "")),
2099
+ orElse: () => b.succeed(t)
2100
+ }), Rn = (e, t) => `${t}(${e.precision ?? 38}, ${e.scale ?? 0})`, zn = (e, t) => {
1988
2101
  if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
1989
2102
  let n = e.type === "vector" && e.vectorDim === void 0 ? "vector dimension" : e.type === "array" && e.arrayElement === void 0 ? "array element type" : e.type === "enum" && e.enumName === void 0 && e.enumValues === void 0 ? "enum name/values" : void 0;
1990
2103
  if (n !== void 0) throw Error(`migration applier: cannot render '${e.name}' (${e.type}) — the schema snapshot carries no ${n}. Rendering it as 'text' would emit DDL that applies successfully and changes nothing, so this fails instead. This is a framework bug: declaredSchema.ts must carry the parameter into the ColumnSnapshot.`);
1991
- return j(e, t);
1992
- }, mn = (e) => {
1993
- let t = W(e, "postgres");
1994
- return t === void 0 ? hn(e) : t;
1995
- }, hn = (e) => {
2104
+ return k(e, t);
2105
+ }, Bn = (e) => {
2106
+ let t = zn(e, "postgres");
2107
+ return t === void 0 ? Vn(e) : t;
2108
+ }, Vn = (e) => {
1996
2109
  switch (e.type) {
1997
2110
  case "id": return "text";
1998
2111
  case "text": return "text";
1999
2112
  case "integer": return "integer";
2000
2113
  case "real": return "double precision";
2001
- case "decimal": return pn(e, "NUMERIC");
2114
+ case "decimal": return Rn(e, "NUMERIC");
2002
2115
  case "bigint": return "bigint";
2003
2116
  case "boolean": return "boolean";
2004
2117
  case "timestamp": return "timestamptz";
@@ -2012,15 +2125,15 @@ BEGIN
2012
2125
  case "interval": return "interval";
2013
2126
  case "raw": return e.rawDdl ?? "text";
2014
2127
  }
2015
- }, gn = (e) => {
2016
- let t = W(e, "mariadb");
2128
+ }, Hn = (e) => {
2129
+ let t = zn(e, "mariadb");
2017
2130
  if (t !== void 0) return t;
2018
2131
  switch (e.type) {
2019
2132
  case "id": return "VARCHAR(64)";
2020
2133
  case "text": return e.hasDefault && e.defaultValue !== void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : "LONGTEXT";
2021
2134
  case "integer": return "INT";
2022
2135
  case "real": return "DOUBLE";
2023
- case "decimal": return pn(e, "DECIMAL");
2136
+ case "decimal": return Rn(e, "DECIMAL");
2024
2137
  case "bigint": return "BIGINT";
2025
2138
  case "boolean": return "TINYINT(1)";
2026
2139
  case "timestamp": return "DATETIME(6)";
@@ -2034,82 +2147,82 @@ BEGIN
2034
2147
  case "interval": return "BIGINT";
2035
2148
  case "raw": return e.rawDdl ?? "LONGTEXT";
2036
2149
  }
2037
- }, G = (e) => `"${e}"`, K = (e) => `\`${e.replace(/`/g, "``")}\``, _n = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, vn = (e) => `ALTER TABLE ${K(e.table)} DROP COLUMN IF EXISTS ${K(e.column)}, ALGORITHM=COPY;`, yn = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, bn = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(`${e.column}${Gn(t)}`)};`, xn = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${Sn(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, Sn = (e, t) => {
2150
+ }, U = (e) => `"${e}"`, W = (e) => `\`${e.replace(/`/g, "``")}\``, Un = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, Wn = (e) => `ALTER TABLE ${W(e.table)} DROP COLUMN IF EXISTS ${W(e.column)}, ALGORITHM=COPY;`, Gn = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, Kn = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(`${e.column}${_r(t)}`)};`, qn = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${Jn(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, Jn = (e, t) => {
2038
2151
  let n = t(e.defaultValue);
2039
2152
  if (n === null) throw Error(`migration applier: cannot render the declared default for '${e.table}.${e.column}' (${JSON.stringify(e.defaultValue)}). Emitting 'DEFAULT NULL' would apply cleanly and drop the default silently, so this fails instead. This is a framework bug — report the column type + default.`);
2040
2153
  return n;
2041
- }, q = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], Cn = (e, t, n) => {
2042
- let r = q(e, t, n);
2043
- return r ? ct(n, r) : void 0;
2044
- }, wn = (e, t, n) => {
2045
- let r = n ?? e.nullable, i = be(e, "mariadb");
2154
+ }, G = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], Yn = (e, t, n) => {
2155
+ let r = G(e, t, n);
2156
+ return r ? vt(n, r) : void 0;
2157
+ }, Xn = (e, t, n) => {
2158
+ let r = n ?? e.nullable, i = Ae(e, "mariadb");
2046
2159
  return [
2047
- K(t),
2048
- j(e, "mariadb"),
2160
+ W(t),
2161
+ k(e, "mariadb"),
2049
2162
  r ? null : "NOT NULL",
2050
2163
  i
2051
2164
  ].filter(Boolean).join(" ");
2052
- }, Tn = (e, t) => e == null ? null : Array.isArray(e) ? En(e, t, "mariadb") : e === "now" ? "DEFAULT CURRENT_TIMESTAMP(6)" : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? M(e, "mariadb") : null, En = (e, t, n) => xe(e, t ?? { type: "json" }, n), Dn = (e, t) => e == null ? null : Array.isArray(e) ? En(e, t, "postgres") : e === "now" ? "DEFAULT now()" : typeof e == "boolean" || typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|::|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? M(e, "postgres") : null, J = (e, t) => {
2165
+ }, Zn = (e, t) => e == null ? null : Array.isArray(e) ? Qn(e, t, "mariadb") : e === "now" ? "DEFAULT CURRENT_TIMESTAMP(6)" : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? A(e, "mariadb") : null, Qn = (e, t, n) => je(e, t ?? { type: "json" }, n), $n = (e, t) => e == null ? null : Array.isArray(e) ? Qn(e, t, "postgres") : e === "now" ? "DEFAULT now()" : typeof e == "boolean" || typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|::|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? A(e, "postgres") : null, K = (e, t) => {
2053
2166
  if (!e.generatedAs) return null;
2054
2167
  let { expr: n, stored: r } = e.generatedAs;
2055
2168
  return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
2056
- }, On = (e) => {
2057
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "postgres");
2058
- let t = J(e, "postgres");
2059
- if (t) return `${G(e.name)} ${mn(e)} ${t}`;
2060
- let n = [G(e.name), mn(e)];
2169
+ }, er = (e) => {
2170
+ if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "postgres");
2171
+ let t = K(e, "postgres");
2172
+ if (t) return `${U(e.name)} ${Bn(e)} ${t}`;
2173
+ let n = [U(e.name), Bn(e)];
2061
2174
  if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
2062
- let t = Dn(e.defaultValue, e);
2175
+ let t = $n(e.defaultValue, e);
2063
2176
  t && n.push(t);
2064
2177
  }
2065
- return e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
2178
+ return e.references && (n.push(`REFERENCES ${U(e.references.table)} (${U(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
2066
2179
  cascade: "CASCADE",
2067
2180
  restrict: "RESTRICT",
2068
2181
  setNull: "SET NULL",
2069
2182
  noAction: "NO ACTION"
2070
2183
  }[e.references.onDelete]}`)), n.join(" ");
2071
- }, kn = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", An = (e) => {
2072
- let t = e.columns.map((e) => ` ${On(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${t}${kn(e, G)}\n)`];
2073
- for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(Xn).join(", ")})`);
2184
+ }, tr = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", nr = (e) => {
2185
+ let t = e.columns.map((e) => ` ${er(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${U(e.table)} (\n${t}${tr(e, U)}\n)`];
2186
+ for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${U(t.name)} ON ${U(e.table)} (${t.columns.map(Sr).join(", ")})`);
2074
2187
  return n;
2075
- }, jn = 191, Mn = (e) => {
2076
- let t = gn(e).toUpperCase();
2188
+ }, rr = 191, ir = (e) => {
2189
+ let t = Hn(e).toUpperCase();
2077
2190
  return t.endsWith("TEXT") || t.endsWith("BLOB");
2078
- }, Nn = (e) => {
2079
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mariadb");
2080
- let t = J(e, "mariadb");
2081
- if (t) return `${K(e.name)} ${gn(e)} ${t}`;
2082
- let n = [K(e.name), gn(e)];
2083
- if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !Mn(e) && n.push("UNIQUE"), e.hasDefault) {
2084
- let t = Tn(e.defaultValue, e);
2191
+ }, ar = (e) => {
2192
+ if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "mariadb");
2193
+ let t = K(e, "mariadb");
2194
+ if (t) return `${W(e.name)} ${Hn(e)} ${t}`;
2195
+ let n = [W(e.name), Hn(e)];
2196
+ if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !ir(e) && n.push("UNIQUE"), e.hasDefault) {
2197
+ let t = Zn(e.defaultValue, e);
2085
2198
  t && n.push(t);
2086
2199
  }
2087
- return e.references && (n.push(`REFERENCES ${K(e.references.table)} (${K(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
2200
+ return e.references && (n.push(`REFERENCES ${W(e.references.table)} (${W(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${{
2088
2201
  cascade: "CASCADE",
2089
2202
  restrict: "RESTRICT",
2090
2203
  setNull: "SET NULL",
2091
2204
  noAction: "NO ACTION"
2092
2205
  }[e.references.onDelete]}`)), n.join(" ");
2093
- }, Pn = (e) => {
2206
+ }, or = (e) => {
2094
2207
  let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
2095
- let n = t.get(e);
2096
- return n && Mn(n) ? `${K(e)}(${jn})` : K(e);
2097
- }, r = e.columns.map((e) => ` ${Nn(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${K(e.table)} (\n${r}${kn(e, n)}\n)`];
2098
- for (let t of e.columns) t.unique && t.type !== "id" && Mn(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${K(`${e.table}_${t.name}_key`)} ON ${K(e.table)} (${n(t.name)})`);
2099
- for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${K(t.name)} ON ${K(e.table)} (${t.columns.map(n).join(", ")})`);
2208
+ let n = br(t.get(e));
2209
+ return n === void 0 ? W(e) : `${W(e)}(${n})`;
2210
+ }, r = e.columns.map((e) => ` ${ar(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${W(e.table)} (\n${r}${tr(e, n)}\n)`];
2211
+ for (let t of e.columns) t.unique && t.type !== "id" && ir(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${W(`${e.table}_${t.name}_key`)} ON ${W(e.table)} (${n(t.name)})`);
2212
+ for (let t of e.indexes) t.name !== `${e.table}_pkey` && i.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(t.name)} ON ${W(e.table)} (${t.columns.map(n).join(", ")})`);
2100
2213
  return i;
2101
- }, Fn = {
2214
+ }, sr = {
2102
2215
  cascade: "CASCADE",
2103
2216
  restrict: "RESTRICT",
2104
2217
  setNull: "SET NULL",
2105
2218
  noAction: "NO ACTION"
2106
- }, In = {
2219
+ }, cr = {
2107
2220
  cascade: "CASCADE",
2108
2221
  restrict: "NO ACTION",
2109
2222
  setNull: "SET NULL",
2110
2223
  noAction: "NO ACTION"
2111
- }, Ln = (e) => {
2112
- let t = W(e, "sqlite");
2224
+ }, lr = (e) => {
2225
+ let t = zn(e, "sqlite");
2113
2226
  if (t !== void 0) return t;
2114
2227
  switch (e.type) {
2115
2228
  case "integer":
@@ -2120,60 +2233,60 @@ BEGIN
2120
2233
  case "bytes": return "BLOB";
2121
2234
  default: return "TEXT";
2122
2235
  }
2123
- }, Y = (e, t) => e == null ? null : e === "now" ? `DEFAULT ${t}` : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? M(e, "sqlite") : null, Rn = (e) => {
2124
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "sqlite");
2125
- let t = J(e, "sqlite");
2126
- if (t) return `${G(e.name)} ${Ln(e)} ${t}`;
2127
- let n = [G(e.name), Ln(e)];
2236
+ }, q = (e, t) => e == null ? null : e === "now" ? `DEFAULT ${t}` : typeof e == "boolean" ? `DEFAULT ${+!!e}` : typeof e == "number" ? `DEFAULT ${e}` : typeof e == "string" ? /[()]|^'/.test(e) ? `DEFAULT ${e}` : `DEFAULT '${e.replace(/'/g, "''")}'` : typeof e == "object" ? A(e, "sqlite") : null, ur = (e) => {
2237
+ if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "sqlite");
2238
+ let t = K(e, "sqlite");
2239
+ if (t) return `${U(e.name)} ${lr(e)} ${t}`;
2240
+ let n = [U(e.name), lr(e)];
2128
2241
  if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && n.push("UNIQUE"), e.hasDefault) {
2129
- let t = Y(e.defaultValue, "CURRENT_TIMESTAMP");
2242
+ let t = q(e.defaultValue, "CURRENT_TIMESTAMP");
2130
2243
  t && n.push(t);
2131
2244
  }
2132
- if (e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${Fn[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
2245
+ if (e.references && (n.push(`REFERENCES ${U(e.references.table)} (${U(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${sr[e.references.onDelete]}`)), e.oneOf && e.oneOf.length > 0) {
2133
2246
  let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
2134
- n.push(`CHECK (${G(e.name)} IN (${t}))`);
2247
+ n.push(`CHECK (${U(e.name)} IN (${t}))`);
2135
2248
  }
2136
2249
  return n.join(" ");
2137
- }, zn = (e) => {
2138
- let t = e.columns.map((e) => ` ${Rn(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${t}${kn(e, G)}\n)`];
2139
- for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e.table)} (${t.columns.map(G).join(", ")})`);
2250
+ }, dr = (e) => {
2251
+ let t = e.columns.map((e) => ` ${ur(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${U(e.table)} (\n${t}${tr(e, U)}\n)`];
2252
+ for (let t of e.indexes) t.name !== `${e.table}_pkey` && n.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${U(t.name)} ON ${U(e.table)} (${t.columns.map(U).join(", ")})`);
2140
2253
  return n;
2141
- }, X = (e, t) => {
2254
+ }, J = (e, t) => {
2142
2255
  let n = t.find((t) => t.tableName === e);
2143
2256
  if (!n) return [];
2144
- let r = I([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => G(e.name)).join(", "), o = [
2145
- ...zn({
2257
+ let r = P([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => U(e.name)).join(", "), o = [
2258
+ ...dr({
2146
2259
  kind: "create-table",
2147
2260
  table: i,
2148
2261
  columns: r.columns,
2149
2262
  indexes: [],
2150
2263
  ...r.primaryKey ? { primaryKey: r.primaryKey } : {}
2151
2264
  }),
2152
- `INSERT INTO ${G(i)} (${a}) SELECT ${a} FROM ${G(e)}`,
2153
- `DROP TABLE ${G(e)}`,
2154
- `ALTER TABLE ${G(i)} RENAME TO ${G(e)}`
2265
+ `INSERT INTO ${U(i)} (${a}) SELECT ${a} FROM ${U(e)}`,
2266
+ `DROP TABLE ${U(e)}`,
2267
+ `ALTER TABLE ${U(i)} RENAME TO ${U(e)}`
2155
2268
  ];
2156
- for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${G(t.name)} ON ${G(e)} (${t.columns.map(G).join(", ")})`);
2269
+ for (let t of r.indexes) t.name !== `${e}_pkey` && o.push(`CREATE ${t.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${U(t.name)} ON ${U(e)} (${t.columns.map(U).join(", ")})`);
2157
2270
  return o;
2158
- }, Bn = (e, t, n, r) => {
2271
+ }, fr = (e, t, n, r) => {
2159
2272
  let i = r ?? e.nullable;
2160
- return `ALTER TABLE ${Z(n)} ALTER COLUMN ${Z(t)} ${j(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
2161
- }, Vn = (e) => {
2162
- let t = `DECLARE @dc sysname; SELECT @dc = dc.name FROM sys.default_constraints dc JOIN sys.columns col ON col.object_id = dc.parent_object_id AND col.column_id = dc.parent_column_id WHERE dc.parent_object_id = OBJECT_ID(N'${e.table.replace(/'/g, "''")}') AND col.name = N'${e.column.replace(/'/g, "''")}'; IF @dc IS NOT NULL EXEC(N'ALTER TABLE ${Z(e.table)} DROP CONSTRAINT [' + @dc + ']');`;
2273
+ return `ALTER TABLE ${Y(n)} ALTER COLUMN ${Y(t)} ${k(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
2274
+ }, pr = (e) => {
2275
+ let t = `DECLARE @dc sysname; SELECT @dc = dc.name FROM sys.default_constraints dc JOIN sys.columns col ON col.object_id = dc.parent_object_id AND col.column_id = dc.parent_column_id WHERE dc.parent_object_id = OBJECT_ID(N'${e.table.replace(/'/g, "''")}') AND col.name = N'${e.column.replace(/'/g, "''")}'; IF @dc IS NOT NULL EXEC(N'ALTER TABLE ${Y(e.table)} DROP CONSTRAINT [' + @dc + ']');`;
2163
2276
  if (!e.hasDefault) return t;
2164
- let n = Y(e.defaultValue, "SYSUTCDATETIME()");
2277
+ let n = q(e.defaultValue, "SYSUTCDATETIME()");
2165
2278
  if (!n) return t;
2166
2279
  let r = n.replace(/^DEFAULT\s+/i, "");
2167
- return `${t} ALTER TABLE ${Z(e.table)} ADD DEFAULT ${r} FOR ${Z(e.column)};`;
2168
- }, Z = (e) => `[${e.replace(/]/g, "]]")}]`, Hn = (e) => {
2169
- let t = W(e, "mssql");
2280
+ return `${t} ALTER TABLE ${Y(e.table)} ADD DEFAULT ${r} FOR ${Y(e.column)};`;
2281
+ }, Y = (e) => `[${e.replace(/]/g, "]]")}]`, mr = (e) => {
2282
+ let t = zn(e, "mssql");
2170
2283
  if (t !== void 0) return t;
2171
2284
  switch (e.type) {
2172
2285
  case "id":
2173
2286
  case "reference": return "NVARCHAR(64)";
2174
2287
  case "integer": return "INT";
2175
2288
  case "real": return "FLOAT";
2176
- case "decimal": return pn(e, "DECIMAL");
2289
+ case "decimal": return Rn(e, "DECIMAL");
2177
2290
  case "bigint": return "BIGINT";
2178
2291
  case "boolean": return "BIT";
2179
2292
  case "timestamp": return "DATETIME2(6)";
@@ -2181,55 +2294,59 @@ BEGIN
2181
2294
  case "bytes": return "VARBINARY(MAX)";
2182
2295
  default: return "NVARCHAR(MAX)";
2183
2296
  }
2184
- }, Un = (e, t) => {
2185
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mssql");
2186
- let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : Hn(e), r = J(e, "mssql");
2187
- if (r) return `${Z(e.name)} ${r}`;
2188
- let i = [Z(e.name), n];
2297
+ }, hr = (e, t) => {
2298
+ if (e.type === "id" && e.idScheme?.kind === "numeric") return j(e.name, "mssql");
2299
+ let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : mr(e), r = K(e, "mssql");
2300
+ if (r) return `${Y(e.name)} ${r}`;
2301
+ let i = [Y(e.name), n];
2189
2302
  if (e.type === "id" && i.push("PRIMARY KEY"), e.nullable || i.push("NOT NULL"), e.unique && e.type !== "id" && i.push("UNIQUE"), e.hasDefault) {
2190
- let t = Y(e.defaultValue, "SYSUTCDATETIME()");
2303
+ let t = q(e.defaultValue, "SYSUTCDATETIME()");
2191
2304
  t && i.push(t);
2192
2305
  }
2193
- return e.references && (i.push(`REFERENCES ${Z(e.references.table)} (${Z(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${In[e.references.onDelete]}`)), i.join(" ");
2194
- }, Wn = (e) => {
2195
- let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${Un(e, n)}`).join(",\n"), i = `CREATE TABLE ${Z(e.table)} (\n${r}${kn(e, Z)}\n)`, a = [`IF OBJECT_ID(N'${t(e.table)}', N'U') IS NULL EXEC(N'${t(i)}')`];
2306
+ return e.references && (i.push(`REFERENCES ${Y(e.references.table)} (${Y(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${cr[e.references.onDelete]}`)), i.join(" ");
2307
+ }, gr = (e) => {
2308
+ let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${hr(e, n)}`).join(",\n"), i = `CREATE TABLE ${Y(e.table)} (\n${r}${tr(e, Y)}\n)`, a = [`IF OBJECT_ID(N'${t(e.table)}', N'U') IS NULL EXEC(N'${t(i)}')`];
2196
2309
  for (let n of e.indexes) {
2197
2310
  if (n.name === `${e.table}_pkey`) continue;
2198
- let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${Z(n.name)} ON ${Z(e.table)} (${n.columns.map(Z).join(", ")})`;
2311
+ let r = `CREATE ${n.unique ? "UNIQUE " : ""}INDEX ${Y(n.name)} ON ${Y(e.table)} (${n.columns.map(Y).join(", ")})`;
2199
2312
  a.push(`IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'${t(n.name)}' AND object_id = OBJECT_ID(N'${t(e.table)}')) EXEC(N'${t(r)}')`);
2200
2313
  }
2201
2314
  return a;
2202
- }, Gn = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, Kn = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, qn = (e, t, n) => {
2203
- let r = `${e.table}${Gn(t)}`;
2315
+ }, _r = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, vr = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, yr = (e, t, n) => {
2316
+ let r = `${e.table}${_r(t)}`;
2204
2317
  return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
2205
- }, Jn = (e, t, n) => {
2206
- let r = q(e, t, n);
2207
- return r ? Mn({ type: r.type }) : !1;
2208
- }, Yn = (e, t, n) => Jn(e, t, n) ? `${K(n)}(${jn})` : K(n), Xn = (e) => e.startsWith("(") ? e : G(e), Zn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${G(e.index.name)} ON ${G(e.table)} (${e.index.columns.map(Xn).join(", ")});`, Qn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${K(e.index.name)} ON ${K(e.table)} (${e.index.columns.map((n) => Yn(t, e.table, n)).join(", ")});`, $n = (e) => `DROP INDEX IF EXISTS ${G(e.index)};`, er = (e) => `DROP INDEX ${K(e.index)} ON ${K(e.table)};`, tr = (e) => `DROP INDEX IF EXISTS ${Z(e.index)} ON ${Z(e.table)};`, nr = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, rr = (e) => {
2318
+ }, br = (e) => {
2319
+ if (e === void 0 || !ir(e)) return;
2320
+ let t = typeof e.maxLength == "number" && e.maxLength > 0 ? e.maxLength : void 0;
2321
+ return t === void 0 ? rr : Math.min(rr, t);
2322
+ }, xr = (e, t, n) => {
2323
+ let r = br(Yn(e, t, n));
2324
+ return r === void 0 ? W(n) : `${W(n)}(${r})`;
2325
+ }, Sr = (e) => e.startsWith("(") ? e : U(e), Cr = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${t ? "CONCURRENTLY " : ""}IF NOT EXISTS ${U(e.index.name)} ON ${U(e.table)} (${e.index.columns.map(Sr).join(", ")});`, wr = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${W(e.index.name)} ON ${W(e.table)} (${e.index.columns.map((n) => xr(t, e.table, n)).join(", ")});`, Tr = (e) => `DROP INDEX IF EXISTS ${U(e.index)};`, Er = (e) => `DROP INDEX ${W(e.index)} ON ${W(e.table)};`, Dr = (e) => `DROP INDEX IF EXISTS ${Y(e.index)} ON ${Y(e.table)};`, Or = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, kr = (e) => {
2209
2326
  let t = (e) => e.replace(/'/g, "''");
2210
2327
  return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
2211
- }, ir = (e) => `ALTER INDEX ${G(e.from)} RENAME TO ${G(e.to)};`, ar = (e) => `ALTER TABLE ${K(e.table)} RENAME INDEX ${K(e.from)} TO ${K(e.to)};`, or = (e) => {
2328
+ }, Ar = (e) => `ALTER INDEX ${U(e.from)} RENAME TO ${U(e.to)};`, jr = (e) => `ALTER TABLE ${W(e.table)} RENAME INDEX ${W(e.from)} TO ${W(e.to)};`, Mr = (e) => {
2212
2329
  let t = (e) => e.replace(/'/g, "''");
2213
2330
  return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
2214
- }, sr = (e) => {
2215
- let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${Z(e.index.name)} ON ${Z(e.table)} (${e.index.columns.map(Z).join(", ")})`;
2331
+ }, Nr = (e) => {
2332
+ let t = (e) => e.replace(/'/g, "''"), n = `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX ${Y(e.index.name)} ON ${Y(e.table)} (${e.index.columns.map(Y).join(", ")})`;
2216
2333
  return `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'${t(e.index.name)}' AND object_id = OBJECT_ID(N'${t(e.table)}')) EXEC(N'${t(n)}');`;
2217
- }, cr = (e, t, n) => {
2334
+ }, Pr = (e, t, n) => {
2218
2335
  let r = n.find((t) => t.tableName === e);
2219
2336
  if (!r) return [];
2220
2337
  let i = [];
2221
2338
  for (let n of t) {
2222
2339
  let t = r.fields[n];
2223
- t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${Z(e)} ALTER COLUMN ${Z(n)} NVARCHAR(450) ${t.nullable ? "NULL" : "NOT NULL"};`);
2340
+ t && t.type === "text" && !t.generatedAs && i.push(`ALTER TABLE ${Y(e)} ALTER COLUMN ${Y(n)} NVARCHAR(450) ${t.nullable ? "NULL" : "NOT NULL"};`);
2224
2341
  }
2225
2342
  return i;
2226
- }, lr = (e) => `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(`${e.table}_${e.column}_key`)} UNIQUE (${G(e.column)});`, ur = (e, t) => `ALTER TABLE ${K(e.table)} ADD CONSTRAINT ${K(`${e.table}_${e.column}_key`)} UNIQUE (${Yn(t, e.table, e.column)});`, dr = (e) => {
2227
- let t = e.fields.map((e) => G(e)).join(", ");
2228
- return `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(e.name)} UNIQUE (${t});`;
2229
- }, fr = (e, t) => {
2230
- let n = e.fields.map((n) => Yn(t, e.table, n)).join(", ");
2231
- return `ALTER TABLE ${K(e.table)} ADD CONSTRAINT ${K(e.name)} UNIQUE (${n});`;
2232
- }, pr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(e.name)};`, mr = (e) => `ALTER TABLE ${K(e.table)} DROP INDEX ${K(e.name)};`, hr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_key`)};`, gr = (e) => `ALTER TABLE ${K(e.table)} DROP INDEX ${K(`${e.table}_${e.column}_key`)};`, _r = (e, t) => {
2343
+ }, Fr = (e) => `ALTER TABLE ${U(e.table)} ADD CONSTRAINT ${U(`${e.table}_${e.column}_key`)} UNIQUE (${U(e.column)});`, Ir = (e, t) => `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(`${e.table}_${e.column}_key`)} UNIQUE (${xr(t, e.table, e.column)});`, Lr = (e) => {
2344
+ let t = e.fields.map((e) => U(e)).join(", ");
2345
+ return `ALTER TABLE ${U(e.table)} ADD CONSTRAINT ${U(e.name)} UNIQUE (${t});`;
2346
+ }, Rr = (e, t) => {
2347
+ let n = e.fields.map((n) => xr(t, e.table, n)).join(", ");
2348
+ return `ALTER TABLE ${W(e.table)} ADD CONSTRAINT ${W(e.name)} UNIQUE (${n});`;
2349
+ }, zr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(e.name)};`, Br = (e) => `ALTER TABLE ${W(e.table)} DROP INDEX ${W(e.name)};`, Vr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(`${e.table}_${e.column}_key`)};`, Hr = (e) => `ALTER TABLE ${W(e.table)} DROP INDEX ${W(`${e.table}_${e.column}_key`)};`, Ur = (e, t) => {
2233
2350
  let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
2234
2351
  if (e.orphanPolicy) {
2235
2352
  let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
@@ -2247,11 +2364,11 @@ BEGIN
2247
2364
  setNull: "SET NULL",
2248
2365
  noAction: "NO ACTION"
2249
2366
  }[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
2250
- }, vr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_fkey`)};`, yr = (e) => `ALTER TABLE ${K(e.table)} DROP FOREIGN KEY ${K(`${e.table}_${e.column}_fkey`)};`, br = (e, t) => {
2367
+ }, Wr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(`${e.table}_${e.column}_fkey`)};`, Gr = (e) => `ALTER TABLE ${W(e.table)} DROP FOREIGN KEY ${W(`${e.table}_${e.column}_fkey`)};`, Kr = (e, t) => {
2251
2368
  let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
2252
2369
  return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
2253
- }, xr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_check`)};`, Sr = (e) => `ALTER TABLE ${K(e.table)} DROP CHECK ${K(`${e.table}_${e.column}_check`)};`, Cr = (e, t, n, r) => {
2254
- let i = e.column, a = r === "mariadb", o = a ? K : r === "mssql" ? Z : G, s = a ? gn(i) : r === "mssql" ? Hn(i) : r === "sqlite" ? Ln(i) : mn(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? Tn(e, i) : r === "mssql" ? Y(e, "SYSUTCDATETIME()") : r === "sqlite" ? Y(e, "CURRENT_TIMESTAMP") : Dn(e, i), u = J(i, r);
2370
+ }, qr = (e) => `ALTER TABLE ${U(e.table)} DROP CONSTRAINT IF EXISTS ${U(`${e.table}_${e.column}_check`)};`, Jr = (e) => `ALTER TABLE ${W(e.table)} DROP CHECK ${W(`${e.table}_${e.column}_check`)};`, Yr = (e, t, n, r) => {
2371
+ let i = e.column, a = r === "mariadb", o = a ? W : r === "mssql" ? Y : U, s = a ? Hn(i) : r === "mssql" ? mr(i) : r === "sqlite" ? lr(i) : Bn(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? Zn(e, i) : r === "mssql" ? q(e, "SYSUTCDATETIME()") : r === "sqlite" ? q(e, "CURRENT_TIMESTAMP") : $n(e, i), u = K(i, r);
2255
2372
  if (u) {
2256
2373
  let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
2257
2374
  return {
@@ -2279,7 +2396,7 @@ BEGIN
2279
2396
  post: f ? [f] : []
2280
2397
  };
2281
2398
  return d?.sql && (p.backfillKind = "sql", p.backfillSql = d.sql), d?.js && (p.backfillKind = "js", p.backfillJs = d.js), d?.batchSize !== void 0 && (p.batchSize = d.batchSize), d?.sleepMs !== void 0 && (p.sleepMs = d.sleepMs), p;
2282
- }, wr = (e) => {
2399
+ }, Xr = (e) => {
2283
2400
  let t = new Map(e.map((e) => [e.tableName, e]));
2284
2401
  return (e, n) => {
2285
2402
  let r = t.get(e);
@@ -2299,297 +2416,416 @@ BEGIN
2299
2416
  ...o
2300
2417
  };
2301
2418
  };
2302
- }, Q = (e, t, n) => e.unsafe(t).pipe(p.tapError((e) => p.sync(() => {
2303
- process.stderr.write(b(`applier: statement failed (op=${n})`, t, e, 400));
2304
- })), p.mapError((e) => {
2305
- let r = b(`applier: statement failed (op=${n})`, t, e, 400).trim();
2419
+ }, X = (e, t, n) => e.unsafe(t).pipe(b.tapError((e) => b.sync(() => {
2420
+ process.stderr.write(re(`applier: statement failed (op=${n})`, t, e, 400));
2421
+ })), b.mapError((e) => {
2422
+ let r = re(`applier: statement failed (op=${n})`, t, e, 400).trim();
2306
2423
  return Object.assign(e, { message: r });
2307
- }), p.map(() => void 0)), $ = (e, t, n) => p.forEach(t, (t) => Q(e, t, n), { discard: !0 }), Tr = (e, t, n) => p.gen(function* () {
2308
- let r = process.hrtime.bigint(), i = wr(n.declared), a = t.op;
2309
- switch (a.kind) {
2424
+ }), b.map(() => void 0)), Z = (e, t, n) => b.forEach(t, (t) => X(e, t, n), { discard: !0 }), Q = (e, t) => e.literal(`LIMIT ${Math.max(1, Math.floor(Number.isFinite(t) ? t : 1))}`), $ = (e, t, n, r) => e.unsafe(`SELECT ${r === void 0 ? "*" : i(r, t)} FROM ${i(n, t)} WHERE 1=0`).pipe(b.as(!0), b.catchAll(() => b.succeed(!1))), Zr = (e, t) => b.gen(function* () {
2425
+ let n = P([cn], t).tables[0];
2426
+ if (!n) return yield* b.die(/* @__PURE__ */ Error("migration applier: the resume-ledger table descriptor produced no snapshot. This is a framework bug."));
2427
+ let r = {
2428
+ kind: "create-table",
2429
+ table: L,
2430
+ columns: n.columns,
2431
+ indexes: []
2432
+ }, i = yield* e.onDialectOrElse({
2433
+ mysql: () => b.succeed(or(r)),
2434
+ mssql: () => b.succeed(gr(r)),
2435
+ sqlite: () => b.succeed(dr(r)),
2436
+ orElse: () => b.succeed(nr(r))
2437
+ });
2438
+ yield* b.forEach(i, (t) => X(e, t, "resume-ledger-bootstrap"), { discard: !0 });
2439
+ }), Qr = (e) => e`
2440
+ SELECT ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
2441
+ ${e("status")}, ${e("payload")}
2442
+ FROM ${e(L)}
2443
+ `, $r = (e, t, n, r) => b.gen(function* () {
2444
+ let i = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
2445
+ yield* b.forEach(r, ({ index: r, planned: a }) => e`
2446
+ INSERT INTO ${e(L)}
2447
+ (${e("id")}, ${e("runId")}, ${e("planFingerprint")}, ${e("opIndex")},
2448
+ ${e("opKind")}, ${e("opTarget")}, ${e("status")}, ${e("payload")},
2449
+ ${e("createdAt")})
2450
+ VALUES
2451
+ (${`mop_${t}_${r}`}, ${t}, ${n}, ${r},
2452
+ ${a.op.kind}, ${un(a.op)}, ${"pending"},
2453
+ ${JSON.stringify(a)}, ${i})
2454
+ `, { discard: !0 });
2455
+ }), ei = (e, t, n, r, i) => b.gen(function* () {
2456
+ let a = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
2457
+ if (r === "applied") {
2458
+ yield* e`
2459
+ UPDATE ${e(L)}
2460
+ SET ${e("status")} = ${r}, ${e("appliedAt")} = ${a},
2461
+ ${e("durationMs")} = ${i ?? null}
2462
+ WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
2463
+ `;
2464
+ return;
2465
+ }
2466
+ yield* e`
2467
+ UPDATE ${e(L)}
2468
+ SET ${e("status")} = ${r}, ${e("startedAt")} = ${a}
2469
+ WHERE ${e("runId")} = ${t} AND ${e("opIndex")} = ${n}
2470
+ `;
2471
+ }), ti = (e, t, n, r) => b.gen(function* () {
2472
+ let i = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
2473
+ r.length > 0 && (yield* e`
2474
+ UPDATE ${e(L)}
2475
+ SET ${e("status")} = ${"applied"}, ${e("appliedAt")} = ${i}
2476
+ WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(r)}
2477
+ `), n.length > 0 && (yield* e`
2478
+ UPDATE ${e(L)}
2479
+ SET ${e("status")} = ${"pending"}
2480
+ WHERE ${e("runId")} = ${t} AND ${e("opIndex")} IN ${e.in(n)}
2481
+ `);
2482
+ }), ni = (e, t) => t === void 0 ? e`DELETE FROM ${e(L)}`.pipe(b.asVoid) : e`DELETE FROM ${e(L)} WHERE ${e("runId")} = ${t}`.pipe(b.asVoid), ri = (e, t, n) => b.gen(function* () {
2483
+ if (vn(n.op, n.classification, t) !== "artefact-repair") return "clean";
2484
+ let r = n.op, a = (e) => i(e, t);
2485
+ if (t === "sqlite" || t === "turso") {
2486
+ if (!("table" in r)) return "clean";
2487
+ let n = `${r.table}${pn}`;
2488
+ return (yield* $(e, t, n)) ? ((yield* $(e, t, r.table)) ? yield* X(e, `DROP TABLE ${a(n)}`, "resume-repair") : yield* X(e, `ALTER TABLE ${a(n)} RENAME TO ${a(r.table)}`, "resume-repair"), yield* b.logWarning(`migration resume: reconciled an interrupted sqlite table rebuild on '${r.table}' — replaying the operation`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), "outstanding") : "clean";
2489
+ }
2490
+ if (r.kind !== "alter-column-type") return "clean";
2491
+ let o = `${r.column}${mn}`, s = `${r.column}${hn}`, c = yield* $(e, t, r.table, r.column), l = yield* $(e, t, r.table, o), u = yield* $(e, t, r.table, s), d = (e) => b.logWarning(`migration resume: ${e}`).pipe(b.annotateLogs({ scope: "voltro:migrate" }));
2492
+ return !l && !u ? "clean" : c && l && !u ? (yield* X(e, `ALTER TABLE ${a(r.table)} DROP COLUMN ${a(o)}`, "resume-repair"), yield* d(`dropped a half-populated shadow column '${r.table}.${o}' — replaying the type change`), "outstanding") : !c && l && u ? (yield* X(e, `ALTER TABLE ${a(r.table)} RENAME COLUMN ${a(o)} TO ${a(r.column)}`, "resume-repair"), yield* X(e, `ALTER TABLE ${a(r.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`completed an interrupted shadow swap on '${r.table}.${r.column}'`), "applied") : c && !l && u ? (yield* X(e, `ALTER TABLE ${a(r.table)} DROP COLUMN ${a(s)}`, "resume-repair"), yield* d(`dropped the leftover pre-swap column '${r.table}.${s}'`), "applied") : yield* b.die(/* @__PURE__ */ Error(`applyPlan: an interrupted online type change on '${r.table}.${r.column}' left a shadow-swap state this resume cannot classify (column=${c}, ${o}=${l}, ${s}=${u}).\nNothing was changed. Inspect those three columns and reconcile them by hand — the data is in whichever of them is populated — then re-run the migration.`));
2493
+ }), ii = (e, t, n, r = "fresh") => b.gen(function* () {
2494
+ let i = process.hrtime.bigint(), a = Xr(n.declared), o = t.op;
2495
+ switch (o.kind) {
2310
2496
  case "create-table": {
2311
2497
  let t = yield* e.onDialectOrElse({
2312
- mysql: () => p.succeed(Pn(a)),
2313
- mssql: () => p.succeed(Wn(a)),
2314
- sqlite: () => p.succeed(zn(a)),
2315
- orElse: () => p.succeed(An(a))
2498
+ mysql: () => b.succeed(or(o)),
2499
+ mssql: () => b.succeed(gr(o)),
2500
+ sqlite: () => b.succeed(dr(o)),
2501
+ orElse: () => b.succeed(nr(o))
2316
2502
  });
2317
- for (let n of t) yield* Q(e, n, a.kind);
2503
+ for (let n of t) yield* X(e, n, o.kind);
2318
2504
  break;
2319
2505
  }
2320
2506
  case "drop-table": {
2321
- let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? qn(a, n, e) : Kn(a, e);
2507
+ let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? yr(o, n, e) : vr(o, e);
2322
2508
  yield* e.onDialectOrElse({
2323
- mysql: () => Q(e, r(K), a.kind),
2324
- orElse: () => Q(e, r(G), a.kind)
2509
+ mysql: () => X(e, r(W), o.kind),
2510
+ orElse: () => X(e, r(U), o.kind)
2325
2511
  });
2326
2512
  break;
2327
2513
  }
2328
2514
  case "drop-column": {
2329
2515
  let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
2330
2516
  yield* e.onDialectOrElse({
2331
- mysql: () => Q(e, t ? bn(a, n, K) : vn(a), a.kind),
2332
- orElse: () => Q(e, t ? bn(a, n, G) : _n(a, G), a.kind)
2517
+ mysql: () => X(e, t ? Kn(o, n, W) : Wn(o), o.kind),
2518
+ orElse: () => X(e, t ? Kn(o, n, U) : Un(o, U), o.kind)
2333
2519
  });
2334
2520
  break;
2335
2521
  }
2336
2522
  case "rename-column":
2337
2523
  yield* e.onDialectOrElse({
2338
- mysql: () => Q(e, yn(a, K), a.kind),
2339
- mssql: () => Q(e, `EXEC sp_rename N'${a.table.replace(/'/g, "''")}.${a.from.replace(/'/g, "''")}', N'${a.to.replace(/'/g, "''")}', 'COLUMN';`, a.kind),
2340
- orElse: () => Q(e, yn(a, G), a.kind)
2524
+ mysql: () => X(e, Gn(o, W), o.kind),
2525
+ mssql: () => X(e, `EXEC sp_rename N'${o.table.replace(/'/g, "''")}.${o.from.replace(/'/g, "''")}', N'${o.to.replace(/'/g, "''")}', 'COLUMN';`, o.kind),
2526
+ orElse: () => X(e, Gn(o, U), o.kind)
2341
2527
  });
2342
2528
  break;
2343
2529
  case "add-index":
2344
2530
  yield* e.onDialectOrElse({
2345
- mysql: () => Q(e, Qn(a, n.declared), a.kind),
2346
- mssql: () => $(e, [...cr(a.table, a.index.columns, n.declared), sr(a)], a.kind),
2347
- orElse: () => Q(e, Zn(a, t.classification === "online-required"), a.kind)
2531
+ mysql: () => X(e, wr(o, n.declared), o.kind),
2532
+ mssql: () => Z(e, [...Pr(o.table, o.index.columns, n.declared), Nr(o)], o.kind),
2533
+ orElse: () => X(e, Cr(o, t.classification === "online-required"), o.kind)
2348
2534
  });
2349
2535
  break;
2350
2536
  case "drop-index":
2351
2537
  yield* e.onDialectOrElse({
2352
- mysql: () => Q(e, er(a), a.kind),
2353
- mssql: () => Q(e, tr(a), a.kind),
2354
- orElse: () => Q(e, $n(a), a.kind)
2538
+ mysql: () => X(e, Er(o), o.kind),
2539
+ mssql: () => X(e, Dr(o), o.kind),
2540
+ orElse: () => X(e, Tr(o), o.kind)
2355
2541
  });
2356
2542
  break;
2357
2543
  case "rename-table":
2358
2544
  yield* e.onDialectOrElse({
2359
- mysql: () => Q(e, nr(a, K), a.kind),
2360
- mssql: () => Q(e, rr(a), a.kind),
2361
- orElse: () => Q(e, nr(a, G), a.kind)
2545
+ mysql: () => X(e, Or(o, W), o.kind),
2546
+ mssql: () => X(e, kr(o), o.kind),
2547
+ orElse: () => X(e, Or(o, U), o.kind)
2362
2548
  });
2363
2549
  break;
2364
2550
  case "rename-index":
2365
2551
  yield* e.onDialectOrElse({
2366
- mysql: () => Q(e, ar(a), a.kind),
2367
- mssql: () => Q(e, or(a), a.kind),
2368
- orElse: () => Q(e, ir(a), a.kind)
2552
+ mysql: () => X(e, jr(o), o.kind),
2553
+ mssql: () => X(e, Mr(o), o.kind),
2554
+ orElse: () => X(e, Ar(o), o.kind)
2369
2555
  });
2370
2556
  break;
2371
2557
  case "add-unique":
2372
2558
  yield* e.onDialectOrElse({
2373
- mysql: () => Q(e, ur(a, n.declared), a.kind),
2374
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2375
- mssql: () => $(e, [...cr(a.table, [a.column], n.declared), lr(a)], a.kind),
2376
- orElse: () => Q(e, lr(a), a.kind)
2559
+ mysql: () => X(e, Ir(o, n.declared), o.kind),
2560
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2561
+ mssql: () => Z(e, [...Pr(o.table, [o.column], n.declared), Fr(o)], o.kind),
2562
+ orElse: () => X(e, Fr(o), o.kind)
2377
2563
  });
2378
2564
  break;
2379
2565
  case "drop-unique":
2380
2566
  yield* e.onDialectOrElse({
2381
- mysql: () => Q(e, gr(a), a.kind),
2382
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2383
- orElse: () => Q(e, hr(a), a.kind)
2567
+ mysql: () => X(e, Hr(o), o.kind),
2568
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2569
+ orElse: () => X(e, Vr(o), o.kind)
2384
2570
  });
2385
2571
  break;
2386
2572
  case "add-unique-composite":
2387
2573
  yield* e.onDialectOrElse({
2388
- mysql: () => Q(e, fr(a, n.declared), a.kind),
2389
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2390
- mssql: () => $(e, [...cr(a.table, a.fields, n.declared), dr(a)], a.kind),
2391
- orElse: () => Q(e, dr(a), a.kind)
2574
+ mysql: () => X(e, Rr(o, n.declared), o.kind),
2575
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2576
+ mssql: () => Z(e, [...Pr(o.table, o.fields, n.declared), Lr(o)], o.kind),
2577
+ orElse: () => X(e, Lr(o), o.kind)
2392
2578
  });
2393
2579
  break;
2394
2580
  case "drop-unique-composite":
2395
2581
  yield* e.onDialectOrElse({
2396
- mysql: () => Q(e, mr(a), a.kind),
2397
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2398
- orElse: () => Q(e, pr(a), a.kind)
2582
+ mysql: () => X(e, Br(o), o.kind),
2583
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2584
+ orElse: () => X(e, zr(o), o.kind)
2399
2585
  });
2400
2586
  break;
2401
2587
  case "add-foreign-key":
2402
2588
  yield* e.onDialectOrElse({
2403
- mysql: () => $(e, _r(a, K), a.kind),
2404
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2405
- orElse: () => $(e, _r(a, G), a.kind)
2589
+ mysql: () => Z(e, Ur(o, W), o.kind),
2590
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2591
+ orElse: () => Z(e, Ur(o, U), o.kind)
2406
2592
  });
2407
2593
  break;
2408
2594
  case "add-column": {
2409
- let n = Cr(a, t, i, yield* e.onDialectOrElse({
2410
- mysql: () => p.succeed("mariadb"),
2411
- mssql: () => p.succeed("mssql"),
2412
- sqlite: () => p.succeed("sqlite"),
2413
- orElse: () => p.succeed("postgres")
2595
+ let n = Yr(o, t, a, yield* e.onDialectOrElse({
2596
+ mysql: () => b.succeed("mariadb"),
2597
+ mssql: () => b.succeed("mssql"),
2598
+ sqlite: () => b.succeed("sqlite"),
2599
+ orElse: () => b.succeed("postgres")
2414
2600
  }));
2415
- for (let t of n.pre) yield* Q(e, t, a.kind);
2416
- if (n.backfillKind === "sql" && n.backfillSql) if (!(t.classification === "online-required" || n.batchSize !== void 0)) yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql}`;
2601
+ if (!(r === "resume" && (yield* $(e, an(e), o.table, o.column.name)))) for (let t of n.pre) yield* X(e, t, o.kind);
2602
+ if (n.backfillKind === "sql" && n.backfillSql) if (!(t.classification === "online-required" || n.batchSize !== void 0)) yield* e`UPDATE ${e(o.table)} SET ${e(o.column.name)} = ${n.backfillSql}`;
2417
2603
  else {
2418
- let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i, o = 0;
2604
+ let t = n.batchSize ?? 5e3, r = n.sleepMs ?? 0, i, a = 0;
2419
2605
  for (;;) {
2420
- let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL ORDER BY ${e("id")} LIMIT ${t}` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} LIMIT ${t}`;
2606
+ let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL ORDER BY ${e("id")} ${Q(e, t)}` : yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} ${Q(e, t)}`;
2421
2607
  if (s.length === 0) break;
2422
2608
  let c = s.map((e) => e.id);
2423
- yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, o += s.length, yield* p.logInfo(`backfill ${a.table}.${a.column.name}: ${o} rows`).pipe(p.annotateLogs({
2609
+ yield* e`UPDATE ${e(o.table)} SET ${e(o.column.name)} = ${n.backfillSql} WHERE ${e("id")} IN ${e.in(c)}`, a += s.length, yield* b.logInfo(`backfill ${o.table}.${o.column.name}: ${a} rows`).pipe(b.annotateLogs({
2424
2610
  scope: "voltro:migrate",
2425
2611
  mode: "sql-batched"
2426
- })), i = s[s.length - 1].id, r > 0 && (yield* p.sleep(`${r} millis`));
2612
+ })), i = s[s.length - 1].id, r > 0 && (yield* b.sleep(`${r} millis`));
2427
2613
  }
2428
2614
  }
2429
2615
  else if (n.backfillKind === "js" && n.backfillJs) {
2430
- let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, i, o = 0;
2616
+ let t = n.batchSize ?? 1e3, r = n.sleepMs ?? 0, i, a = 0;
2431
2617
  for (;;) {
2432
- let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL ORDER BY ${e("id")} LIMIT ${t}` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(a.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} LIMIT ${t}`;
2618
+ let s = i === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL ORDER BY ${e("id")} ${Q(e, t)}` : yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(o.column.name)} IS NULL AND ${e("id")} > ${i} ORDER BY ${e("id")} ${Q(e, t)}`;
2433
2619
  if (s.length === 0) break;
2434
2620
  for (let t of s) {
2435
- let r = yield* p.tryPromise({
2621
+ let r = yield* b.tryPromise({
2436
2622
  try: () => Promise.resolve(n.backfillJs({ id: t.id })),
2437
2623
  catch: (e) => e
2438
- }).pipe(p.orDie);
2439
- yield* e`UPDATE ${e(a.table)} SET ${e(a.column.name)} = ${r} WHERE ${e("id")} = ${t.id}`;
2624
+ }).pipe(b.orDie);
2625
+ yield* e`UPDATE ${e(o.table)} SET ${e(o.column.name)} = ${r} WHERE ${e("id")} = ${t.id}`;
2440
2626
  }
2441
- o += s.length, yield* p.logInfo(`backfill ${a.table}.${a.column.name}: ${o} rows processed`).pipe(p.annotateLogs({
2627
+ a += s.length, yield* b.logInfo(`backfill ${o.table}.${o.column.name}: ${a} rows processed`).pipe(b.annotateLogs({
2442
2628
  scope: "voltro:migrate",
2443
- table: a.table,
2444
- column: a.column.name
2445
- })), i = s[s.length - 1].id, r > 0 && (yield* p.sleep(`${r} millis`));
2629
+ table: o.table,
2630
+ column: o.column.name
2631
+ })), i = s[s.length - 1].id, r > 0 && (yield* b.sleep(`${r} millis`));
2446
2632
  }
2447
2633
  }
2448
- for (let t of n.post) yield* Q(e, t, a.kind);
2634
+ for (let t of n.post) yield* X(e, t, o.kind);
2449
2635
  break;
2450
2636
  }
2451
2637
  case "alter-column-nullability":
2452
2638
  yield* e.onDialectOrElse({
2453
2639
  mysql: () => {
2454
- let t = q(n.declared, a.table, a.column);
2455
- return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${wn(t, a.column, a.toNullable)};`, a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-nullability on mariadb needs the declared column '${a.table}.${a.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
2640
+ let t = G(n.declared, o.table, o.column);
2641
+ return t ? X(e, `ALTER TABLE ${W(o.table)} MODIFY COLUMN ${Xn(t, o.column, o.toNullable)};`, o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-nullability on mariadb needs the declared column '${o.table}.${o.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
2456
2642
  },
2457
2643
  mssql: () => {
2458
- let t = q(n.declared, a.table, a.column);
2459
- return t ? Q(e, Bn(t, a.column, a.table, a.toNullable), a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-nullability on mssql needs the declared column '${a.table}.${a.column}'.`));
2644
+ let t = G(n.declared, o.table, o.column);
2645
+ return t ? X(e, fr(t, o.column, o.table, o.toNullable), o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-nullability on mssql needs the declared column '${o.table}.${o.column}'.`));
2460
2646
  },
2461
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2462
- orElse: () => Q(e, `ALTER TABLE ${G(a.table)} ALTER COLUMN ${G(a.column)} ${a.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, a.kind)
2647
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2648
+ orElse: () => X(e, `ALTER TABLE ${U(o.table)} ALTER COLUMN ${U(o.column)} ${o.toNullable ? "DROP NOT NULL" : "SET NOT NULL"};`, o.kind)
2463
2649
  });
2464
2650
  break;
2465
2651
  case "alter-column-default": {
2466
- let t = Cn(n.declared, a.table, a.column);
2652
+ let t = Yn(n.declared, o.table, o.column);
2467
2653
  yield* e.onDialectOrElse({
2468
- mysql: () => Q(e, xn(a, K, (e) => Tn(e, t)), a.kind),
2469
- mssql: () => Q(e, Vn(a), a.kind),
2470
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2471
- orElse: () => Q(e, xn(a, G, (e) => Dn(e, t)), a.kind)
2654
+ mysql: () => X(e, qn(o, W, (e) => Zn(e, t)), o.kind),
2655
+ mssql: () => X(e, pr(o), o.kind),
2656
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2657
+ orElse: () => X(e, qn(o, U, (e) => $n(e, t)), o.kind)
2472
2658
  });
2473
2659
  break;
2474
2660
  }
2475
2661
  case "alter-column-type": {
2476
- let r = mn(Cn(n.declared, a.table, a.column) ?? { type: a.to }), i = a.using ? ` USING ${a.using}` : "";
2662
+ let r = Bn(Yn(n.declared, o.table, o.column) ?? { type: o.to }), i = o.using ? ` USING ${o.using}` : "";
2477
2663
  if (t.classification !== "online-required") {
2478
2664
  yield* e.onDialectOrElse({
2479
2665
  mysql: () => {
2480
- let t = q(n.declared, a.table, a.column);
2481
- return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${wn(t, a.column)};`, a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-type on mariadb needs the declared column '${a.table}.${a.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
2666
+ let t = G(n.declared, o.table, o.column);
2667
+ return t ? X(e, `ALTER TABLE ${W(o.table)} MODIFY COLUMN ${Xn(t, o.column)};`, o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-type on mariadb needs the declared column '${o.table}.${o.column}' (to render MODIFY COLUMN); it was not in ctx.declared.`));
2482
2668
  },
2483
2669
  mssql: () => {
2484
- let t = q(n.declared, a.table, a.column);
2485
- return t ? Q(e, Bn(t, a.column, a.table), a.kind) : p.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${a.table}.${a.column}'.`));
2670
+ let t = G(n.declared, o.table, o.column);
2671
+ return t ? X(e, fr(t, o.column, o.table), o.kind) : b.die(/* @__PURE__ */ Error(`alter-column-type on mssql needs the declared column '${o.table}.${o.column}'.`));
2486
2672
  },
2487
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2488
- orElse: () => Q(e, `ALTER TABLE ${G(a.table)} ALTER COLUMN ${G(a.column)} TYPE ${r}${i};`, a.kind)
2673
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2674
+ orElse: () => X(e, `ALTER TABLE ${U(o.table)} ALTER COLUMN ${U(o.column)} TYPE ${r}${i};`, o.kind)
2489
2675
  });
2490
2676
  break;
2491
2677
  }
2492
2678
  yield* e.onDialectOrElse({
2493
- mysql: () => p.die(/* @__PURE__ */ Error(`online-required alter-column-type on ${a.table}.${a.column} is not supported on mariadb (the shadow-swap path is postgres-only). Use a smaller-table window or an external online-DDL tool.`)),
2494
- orElse: () => p.void
2679
+ mysql: () => b.die(/* @__PURE__ */ Error(`online-required alter-column-type on ${o.table}.${o.column} is not supported on mariadb (the shadow-swap path is postgres-only). Use a smaller-table window or an external online-DDL tool.`)),
2680
+ orElse: () => b.void
2495
2681
  });
2496
- let o = `${a.column}__shadow`, s = `${a.column}__old`;
2497
- yield* e.unsafe(`ALTER TABLE ${G(a.table)} ADD COLUMN ${G(o)} ${r}`);
2498
- let c = a.using ?? G(a.column), l;
2682
+ let a = `${o.column}__shadow`, s = `${o.column}__old`;
2683
+ yield* e.unsafe(`ALTER TABLE ${U(o.table)} ADD COLUMN ${U(a)} ${r}`);
2684
+ let c = o.using ?? U(o.column), l;
2499
2685
  for (;;) {
2500
- let t = l === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(o)} IS NULL ORDER BY ${e("id")} LIMIT 5000` : yield* e`SELECT ${e("id")} FROM ${e(a.table)} WHERE ${e(o)} IS NULL AND ${e("id")} > ${l} ORDER BY ${e("id")} LIMIT 5000`;
2686
+ let t = l === void 0 ? yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(a)} IS NULL ORDER BY ${e("id")} ${Q(e, 5e3)}` : yield* e`SELECT ${e("id")} FROM ${e(o.table)} WHERE ${e(a)} IS NULL AND ${e("id")} > ${l} ORDER BY ${e("id")} ${Q(e, 5e3)}`;
2501
2687
  if (t.length === 0) break;
2502
- yield* e.unsafe(`UPDATE ${G(a.table)} SET ${G(o)} = ${c} WHERE ${G(a.column)} IS NOT NULL AND ${G(o)} IS NULL AND ${G("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
2688
+ yield* e.unsafe(`UPDATE ${U(o.table)} SET ${U(a)} = ${c} WHERE ${U(o.column)} IS NOT NULL AND ${U(a)} IS NULL AND ${U("id")} IN (${t.map((e) => `'${e.id.replace(/'/g, "''")}'`).join(",")})`), l = t[t.length - 1].id;
2503
2689
  }
2504
- yield* p.logInfo(`alter-column-type shadow ${a.table}.${a.column}: shadow populated`).pipe(p.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${G(a.table)} RENAME COLUMN ${G(a.column)} TO ${G(s)}`), yield* e.unsafe(`ALTER TABLE ${G(a.table)} RENAME COLUMN ${G(o)} TO ${G(a.column)}`), yield* e.unsafe(`ALTER TABLE ${G(a.table)} DROP COLUMN ${G(s)}`);
2690
+ yield* b.logInfo(`alter-column-type shadow ${o.table}.${o.column}: shadow populated`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), yield* e.unsafe(`ALTER TABLE ${U(o.table)} RENAME COLUMN ${U(o.column)} TO ${U(s)}`), yield* e.unsafe(`ALTER TABLE ${U(o.table)} RENAME COLUMN ${U(a)} TO ${U(o.column)}`), yield* e.unsafe(`ALTER TABLE ${U(o.table)} DROP COLUMN ${U(s)}`);
2505
2691
  break;
2506
2692
  }
2507
2693
  case "drop-foreign-key":
2508
2694
  yield* e.onDialectOrElse({
2509
- mysql: () => Q(e, yr(a), a.kind),
2510
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2511
- orElse: () => Q(e, vr(a), a.kind)
2695
+ mysql: () => X(e, Gr(o), o.kind),
2696
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2697
+ orElse: () => X(e, Wr(o), o.kind)
2512
2698
  });
2513
2699
  break;
2514
2700
  case "add-check":
2515
2701
  yield* e.onDialectOrElse({
2516
- mysql: () => Q(e, br(a, K), a.kind),
2517
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2518
- orElse: () => Q(e, br(a, G), a.kind)
2702
+ mysql: () => X(e, Kr(o, W), o.kind),
2703
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2704
+ orElse: () => X(e, Kr(o, U), o.kind)
2519
2705
  });
2520
2706
  break;
2521
2707
  case "drop-check":
2522
2708
  yield* e.onDialectOrElse({
2523
- mysql: () => Q(e, Sr(a), a.kind),
2524
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2525
- orElse: () => Q(e, xr(a), a.kind)
2709
+ mysql: () => X(e, Jr(o), o.kind),
2710
+ sqlite: () => Z(e, J(o.table, n.declared), o.kind),
2711
+ orElse: () => X(e, qr(o), o.kind)
2526
2712
  });
2527
2713
  break;
2528
2714
  }
2529
2715
  return {
2530
- op: a,
2716
+ op: o,
2531
2717
  status: "applied",
2532
- durationMs: Number((process.hrtime.bigint() - r) / 1000000n)
2718
+ durationMs: Number((process.hrtime.bigint() - i) / 1000000n)
2719
+ };
2720
+ }), ai = (e, t, n, r) => b.gen(function* () {
2721
+ let i = [...new Set(r.map((e) => e.runId))];
2722
+ if (i.length > 1) return yield* b.die(/* @__PURE__ */ Error(`applyPlan: the resume ledger holds ${i.length} unfinished runs (${i.join(", ")}). Only one migration may be in flight at a time — the advisory lock enforces it — so this means a run was interrupted and a LATER one wrote over it without clearing.\nNothing was changed. Inspect \`${L}\`, reconcile the schema by hand, and delete the rows of the run you have finished.`));
2723
+ let a = i[0], o = [...r].map((e) => ({
2724
+ index: Number(e.opIndex),
2725
+ status: e.status,
2726
+ planned: JSON.parse(e.payload)
2727
+ })).sort((e, t) => e.index - t.index), s = o.find((e) => e.status === "started"), c = o.filter((e) => e.status === "started").length;
2728
+ if (c > 1) return yield* b.die(/* @__PURE__ */ Error(`applyPlan: the resume ledger records ${c} operations as in-flight for run ${a}. It is written strictly sequentially, so at most one can be — this ledger is corrupt and the schema state it describes cannot be trusted.\nNothing was changed. Reconcile \`${L}\` by hand before re-running.`));
2729
+ if (yield* b.logWarning(`migration resume: found an interrupted run (${a}) — ${o.filter((e) => e.status === "applied").length} of ${o.length} operation(s) completed${s ? `, in flight: ${s.planned.op.kind} ${un(s.planned.op)}` : ""}`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), o.every((e) => e.status === "pending")) return {
2730
+ kind: "discard",
2731
+ runId: a,
2732
+ why: "no operation had started — no DDL ran"
2533
2733
  };
2534
- }), Er = (e, t, n) => p.gen(function* () {
2734
+ let l = s ? yield* ri(e, t, s.planned) : "clean", u = r[0].planFingerprint;
2735
+ if (u !== n.toFingerprint) return {
2736
+ kind: "discard",
2737
+ runId: a,
2738
+ why: `the declared schema changed since the interruption (recorded target ${u.slice(0, 12)}…, current ${n.toFingerprint.slice(0, 12)}…) — applying the freshly-diffed plan instead`
2739
+ };
2740
+ let d = (e) => e.status === "applied" ? !1 : s && e.index === s.index && l !== "clean" ? l === "outstanding" : fn(n, e.planned.op), f = o.filter(d), p = o.filter((e) => e.status !== "applied" && !d(e));
2741
+ return f.length === 0 ? {
2742
+ kind: "discard",
2743
+ runId: a,
2744
+ why: "every recorded operation had already taken effect"
2745
+ } : (yield* ti(e, a, f.map((e) => e.index), p.map((e) => e.index)), yield* b.logWarning(`migration resume: continuing run ${a} — replaying ${f.length} operation(s), ${o.length - f.length} already applied`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), {
2746
+ kind: "resume",
2747
+ runId: a,
2748
+ ops: f.map((e) => ({
2749
+ index: e.index,
2750
+ planned: e.planned
2751
+ }))
2752
+ });
2753
+ }), oi = (e, t, n) => b.gen(function* () {
2535
2754
  let r = t.operations.filter((e) => e.blocked);
2536
- if (r.length > 0) return yield* p.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — ${r.length} blocked operations. Run \`voltro db plan\` to see the fixes.`));
2537
- yield* E(e);
2538
- let i = Date.now(), a = [];
2755
+ if (r.length > 0) return yield* b.die(/* @__PURE__ */ Error(`applyPlan: refusing to execute — ${r.length} blocked operations. Run \`voltro db plan\` to see the fixes.`));
2756
+ yield* T(e);
2757
+ let i = Date.now(), a = [], o = an(e), s = sn(o) === "plan-transactional", l = s ? t.operations.filter((e) => e.classification !== "online-required") : [], u = s ? t.operations.filter((e) => e.classification === "online-required") : t.operations, d = `plan_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`, f = u.map((e, t) => ({
2758
+ index: t,
2759
+ planned: e
2760
+ })), p = "fresh";
2539
2761
  try {
2540
- let r = yield* e.onDialectOrElse({
2541
- pg: () => p.succeed(!0),
2542
- mssql: () => p.succeed(!0),
2543
- orElse: () => p.succeed(!1)
2544
- }), i = r ? t.operations.filter((e) => e.classification !== "online-required") : [], o = r ? t.operations.filter((e) => e.classification === "online-required") : t.operations;
2545
- i.length > 0 && (yield* e.withTransaction(p.gen(function* () {
2546
- let e = yield* h.SqlClient;
2547
- for (let t of i) a.push(yield* Tr(e, t, n));
2762
+ yield* Zr(e, o);
2763
+ let r = yield* Qr(e);
2764
+ if (r.length > 0) {
2765
+ let n = yield* ai(e, o, t, r);
2766
+ n.kind === "resume" ? (d = n.runId, f = n.ops, p = "resume") : (yield* b.logWarning(`migration resume: discarding the ledger of run ${n.runId} — ${n.why}`).pipe(b.annotateLogs({ scope: "voltro:migrate" })), yield* ni(e, n.runId));
2767
+ }
2768
+ p === "fresh" && f.length > 0 && (yield* $r(e, d, t.toFingerprint, f)), l.length > 0 && (yield* e.withTransaction(b.gen(function* () {
2769
+ let e = yield* S.SqlClient;
2770
+ for (let t of l) a.push(yield* ii(e, t, n));
2548
2771
  })));
2549
- for (let t of o) a.push(yield* Tr(e, t, n));
2772
+ for (let { index: t, planned: r } of f) {
2773
+ yield* ei(e, d, t, "started");
2774
+ let i = yield* ii(e, r, n, p);
2775
+ a.push(i), yield* ei(e, d, t, "applied", i.durationMs);
2776
+ }
2550
2777
  } finally {
2551
- yield* D(e).pipe(p.orDie);
2778
+ yield* E(e).pipe(b.orDie);
2552
2779
  }
2553
- let o = yield* n.replan(e);
2554
- if (o.operations.length > 0) {
2555
- let e = o.operations.slice(0, 10).map((e) => ` - ${e.op.kind} ${"table" in e.op ? e.op.table : ""}${"column" in e.op ? `.${String(e.op.column)}` : ""}`).join("\n");
2556
- return yield* p.die(/* @__PURE__ */ Error(`applyPlan: the migration did not converge. ${t.operations.length} operation(s) were executed without error, but re-planning against the live schema still finds ${o.operations.length}:\n${e}${o.operations.length > 10 ? `\n … and ${o.operations.length - 10} more` : ""}\nNo fingerprint was recorded — recording one would make the next boot report "schema up to date" for a schema that was never applied. This is a framework bug; the schema is unchanged and safe.\nTwo causes look identical from here, so read the list before assuming either:\n - the DDL for those operations is a no-op (it succeeded and changed nothing), or\n - the planner cannot see what the DDL did, so it proposes the same work again — which is what an\n operation naming an object a PREVIOUS operation created or renamed usually means.\nReport the operation kinds above with the column types involved.`));
2780
+ let m = yield* n.replan(e);
2781
+ if (m.operations.length > 0) {
2782
+ let e = m.operations.slice(0, 10).map((e) => ` - ${e.op.kind} ${"table" in e.op ? e.op.table : ""}${"column" in e.op ? `.${String(e.op.column)}` : ""}`).join("\n");
2783
+ return yield* b.die(/* @__PURE__ */ Error(`applyPlan: the migration did not converge. ${t.operations.length} operation(s) were executed without error, but re-planning against the live schema still finds ${m.operations.length}:\n${e}${m.operations.length > 10 ? `\n … and ${m.operations.length - 10} more` : ""}\nNo fingerprint was recorded — recording one would make the next boot report "schema up to date" for a schema that was never applied. This is a framework bug; the schema is unchanged and safe.\nTwo causes look identical from here, so read the list before assuming either:\n - the DDL for those operations is a no-op (it succeeded and changed nothing), or\n - the planner cannot see what the DDL did, so it proposes the same work again — which is what an\n operation naming an object a PREVIOUS operation created or renamed usually means.\nReport the operation kinds above with the column types involved.`));
2557
2784
  }
2558
- let s = a.filter((e) => e.status === "applied").length, c = Date.now() - i, l = `plan_${Date.now().toString(36)}`, u = (/* @__PURE__ */ new Date()).toISOString(), d = yield* fn(e, u);
2559
- return yield* e`
2785
+ let h = a.filter((e) => e.status === "applied").length, g = Date.now() - i, _ = d, v = (/* @__PURE__ */ new Date()).toISOString(), y = yield* H(e, v), x = [...l, ...f.map((e) => e.planned)];
2786
+ yield* e`
2560
2787
  INSERT INTO ${e("_voltro_migration_plans")}
2561
2788
  (${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
2562
2789
  ${e("appliedBy")}, ${e("environment")}, ${e("source")},
2563
2790
  ${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
2564
2791
  VALUES
2565
- (${l}, ${t.toFingerprint}, ${o.fromFingerprint}, ${JSON.stringify(t.operations)},
2792
+ (${_}, ${t.toFingerprint}, ${m.fromFingerprint}, ${JSON.stringify(x)},
2566
2793
  ${n.appliedBy}, ${n.environment}, ${n.source},
2567
- ${c}, ${d}, ${n.notes ?? null})
2568
- `, {
2569
- id: l,
2570
- appliedAt: u,
2794
+ ${g}, ${y}, ${n.notes ?? null})
2795
+ `, yield* ni(e, d);
2796
+ let C = [...new Set(x.flatMap((e) => e.op.kind === "rename-table" ? [e.op.to] : [e.op.table]))];
2797
+ return yield* b.promise(() => c({ changedTables: C }).catch(() => {})), {
2798
+ id: _,
2799
+ appliedAt: v,
2571
2800
  fingerprint: t.toFingerprint,
2572
- operations: t.operations,
2801
+ operations: x,
2573
2802
  appliedBy: n.appliedBy,
2574
2803
  environment: n.environment,
2575
- durationMs: c,
2576
- appliedOps: s,
2804
+ durationMs: g,
2805
+ appliedOps: h,
2577
2806
  source: n.source,
2578
2807
  ...n.notes ? { notes: n.notes } : {}
2579
2808
  };
2580
- }), Dr = (e) => e.operations.filter((e) => e.blocked).map((e) => ` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}: ${e.reason ?? "<no reason>"}\n fix: ${e.blocked?.fix ?? "<no fix>"}`).join("\n"), Or = (e) => e`
2809
+ }), si = (e) => e.operations.filter((e) => e.blocked).map((e) => ` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}: ${e.reason ?? "<no reason>"}\n fix: ${e.blocked?.fix ?? "<no fix>"}`).join("\n"), ci = (e) => e`
2581
2810
  SELECT fingerprint
2582
2811
  FROM _voltro_migration_plans
2583
2812
  ORDER BY ${e("appliedAt")} DESC
2584
2813
  LIMIT 1
2585
- `.pipe(p.map((e) => e.length > 0 ? e[0].fingerprint : void 0), p.catchAll(() => p.succeed(void 0))), kr = (e) => e.onDialectOrElse({
2586
- pg: () => "postgres",
2587
- mysql: () => "mysql",
2588
- mssql: () => "mssql",
2589
- sqlite: () => "sqlite",
2590
- orElse: () => "sqlite"
2591
- }), Ar = (e, t) => p.gen(function* () {
2592
- let n = P(I(t, kr(e))), r = yield* Or(e);
2814
+ `.pipe(b.map((e) => e.length > 0 ? e[0].fingerprint : void 0), b.catchAll(() => b.succeed(void 0))), li = (e, t, n) => b.gen(function* () {
2815
+ if ((yield* ci(e)) === t.toFingerprint) return "already-current";
2816
+ let r = `plan_${Date.now().toString(36)}`, i = yield* H(e, (/* @__PURE__ */ new Date()).toISOString());
2817
+ return yield* e`
2818
+ INSERT INTO ${e("_voltro_migration_plans")}
2819
+ (${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
2820
+ ${e("appliedBy")}, ${e("environment")}, ${e("source")},
2821
+ ${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
2822
+ VALUES
2823
+ (${r}, ${t.toFingerprint}, ${t.fromFingerprint}, ${JSON.stringify([])},
2824
+ ${n.appliedBy}, ${n.environment}, ${n.source},
2825
+ ${0}, ${i}, ${"no DDL — the declared schema already matched the database"})
2826
+ `, "recorded";
2827
+ }).pipe(b.catchAll(() => b.succeed("already-current"))), ui = (e, t) => b.gen(function* () {
2828
+ let n = M(P(t, an(e))), r = yield* ci(e);
2593
2829
  return r === n ? {
2594
2830
  kind: "up-to-date",
2595
2831
  fingerprint: n
@@ -2598,23 +2834,23 @@ BEGIN
2598
2834
  expected: n,
2599
2835
  ...r === void 0 ? {} : { actual: r }
2600
2836
  };
2601
- }), jr = [
2837
+ }), di = [
2602
2838
  "pgeagertest_",
2603
2839
  "mig_e2e_",
2604
2840
  "mig_test_",
2605
2841
  "mysqleagertest_",
2606
2842
  "mssqleagertest_",
2607
2843
  "sqliteeagertest_"
2608
- ], Mr = (e) => jr.some((t) => e.startsWith(t)), Nr = (e, t) => p.gen(function* () {
2844
+ ], fi = (e) => di.some((t) => e.startsWith(t)), pi = (e, t) => b.gen(function* () {
2609
2845
  if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
2610
- let n = t.filter((e) => Mr(e.name)).map((e) => e.name);
2846
+ let n = t.filter((e) => fi(e.name)).map((e) => e.name);
2611
2847
  if (n.length === 0) return [];
2612
- for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(p.catchAll(() => p.void));
2848
+ for (let t of n) yield* e.unsafe(`DROP TABLE IF EXISTS "${t}" CASCADE`).pipe(b.catchAll(() => b.void));
2613
2849
  return n;
2614
- }), Pr = () => {
2850
+ }), mi = () => {
2615
2851
  let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
2616
2852
  return e.length > 0 ? { ignoreTables: e } : {};
2617
- }, Fr = (e) => {
2853
+ }, hi = (e) => {
2618
2854
  if (e === void 0) return;
2619
2855
  let t = e.trim();
2620
2856
  if (t === "") return;
@@ -2624,83 +2860,87 @@ BEGIN
2624
2860
  kind: "tables",
2625
2861
  tables: new Set(n)
2626
2862
  } : void 0;
2627
- }, Ir = (e) => typeof e.table == "string" ? e.table : void 0, Lr = (e, t = { kind: "all" }) => ({
2863
+ }, gi = (e) => typeof e.table == "string" ? e.table : void 0, _i = (e, t = { kind: "all" }) => ({
2628
2864
  ...e,
2629
2865
  operations: e.operations.map((e) => {
2630
2866
  if (!e.blocked || e.classification !== "lossy") return e;
2631
2867
  if (t?.kind === "tables") {
2632
- let n = Ir(e.op);
2868
+ let n = gi(e.op);
2633
2869
  if (n === void 0 || !t.tables.has(n)) return e;
2634
2870
  }
2635
2871
  let { blocked: n, ...r } = e;
2636
2872
  return r;
2637
2873
  })
2638
- }), Rr = (e, t, n) => t === "postgres" ? ft(e, n).pipe(p.flatMap((e) => {
2639
- let t = pt(e);
2640
- return t === void 0 ? p.void : p.logWarning(`auto-migrate: ${t}`).pipe(p.annotateLogs({ scope: "voltro:migrate" }));
2641
- }), p.catchAll(() => p.void)) : p.void, zr = (e, t, n) => p.gen(function* () {
2642
- let r = kr(e), i = t;
2874
+ }), vi = (e, t, n) => t === "postgres" ? St(e, n).pipe(b.flatMap((e) => {
2875
+ let t = Ct(e);
2876
+ return t === void 0 ? b.void : b.logWarning(`auto-migrate: ${t}`).pipe(b.annotateLogs({ scope: "voltro:migrate" }));
2877
+ }), b.catchAll(() => b.void)) : b.void, yi = (e, t, n) => b.gen(function* () {
2878
+ let r = an(e), i = t;
2643
2879
  if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
2644
- let t = P(I(i, r));
2645
- if ((yield* Or(e)) === t) return yield* Rr(e, r, i), {
2880
+ let t = M(P(i, r));
2881
+ if ((yield* ci(e)) === t) return yield* vi(e, r, i), {
2646
2882
  kind: "up-to-date",
2647
2883
  fingerprint: t
2648
2884
  };
2649
2885
  }
2650
- let a = yield* U(e), o = yield* Nr(e, a.tables);
2651
- o.length > 0 && (yield* p.logInfo(`auto-migrate: reaped ${o.length} test-fixture leftover(s)`).pipe(p.annotateLogs({
2886
+ let a = yield* Ln(e), o = yield* pi(e, a.tables);
2887
+ o.length > 0 && (yield* b.logInfo(`auto-migrate: reaped ${o.length} test-fixture leftover(s)`).pipe(b.annotateLogs({
2652
2888
  scope: "voltro:migrate",
2653
2889
  count: o.length
2654
- })), a = yield* U(e));
2655
- let c = new Set(t.map((e) => e.tableName)), l = (e) => e.filter((e) => !s(e.name) || c.has(e.name)), u = qt({
2890
+ })), a = yield* Ln(e));
2891
+ let s = new Set(t.map((e) => e.tableName)), c = (e) => e.filter((e) => !m(e.name) || s.has(e.name)), l = rn({
2656
2892
  declared: i,
2657
- live: { tables: l(a.tables) },
2893
+ live: { tables: c(a.tables) },
2658
2894
  dialect: r,
2659
- ...Pr()
2895
+ ...mi()
2660
2896
  });
2661
- if (u.summary.blocked > 0) {
2662
- let e = u.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = Fr(process.env.VOLTRO_DESTRUCTIVE_OK);
2663
- if (t === void 0 || !e || (u = Lr(u, t), u.operations.some((e) => e.blocked))) return {
2897
+ if (l.summary.blocked > 0) {
2898
+ let e = l.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = hi(process.env.VOLTRO_DESTRUCTIVE_OK);
2899
+ if (t === void 0 || !e || (l = _i(l, t), l.operations.some((e) => e.blocked))) return {
2664
2900
  kind: "refused-blocked",
2665
- plan: u
2901
+ plan: l
2666
2902
  };
2667
2903
  }
2668
- return u.operations.length === 0 ? {
2904
+ return l.operations.length === 0 ? (yield* li(e, l, {
2905
+ appliedBy: n.appliedBy,
2906
+ environment: n.environment === "prod" ? "dev" : n.environment,
2907
+ source: "auto-diff"
2908
+ }), {
2669
2909
  kind: "up-to-date",
2670
- fingerprint: u.toFingerprint
2671
- } : {
2910
+ fingerprint: l.toFingerprint
2911
+ }) : {
2672
2912
  kind: "applied",
2673
- applied: yield* Er(e, u, {
2913
+ applied: yield* oi(e, l, {
2674
2914
  declared: t,
2675
2915
  appliedBy: n.appliedBy,
2676
2916
  environment: n.environment === "prod" ? "dev" : n.environment,
2677
2917
  source: "auto-diff",
2678
- replan: (e) => p.gen(function* () {
2679
- let t = yield* U(e);
2680
- return qt({
2918
+ replan: (e) => b.gen(function* () {
2919
+ let t = yield* Ln(e);
2920
+ return rn({
2681
2921
  declared: i,
2682
- live: { tables: l(t.tables) },
2922
+ live: { tables: c(t.tables) },
2683
2923
  dialect: r,
2684
- ...Pr()
2924
+ ...mi()
2685
2925
  });
2686
2926
  })
2687
2927
  }),
2688
- plan: u
2928
+ plan: l
2689
2929
  };
2690
- }), Br = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? p.succeed({
2930
+ }), bi = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? b.succeed({
2691
2931
  kind: "skipped",
2692
2932
  reason: "VOLTRO_AUTO_MIGRATE=0"
2693
- }) : n.environment === "prod" ? Ar(e, t) : zr(e, t, n), Vr = (e, t) => {
2933
+ }) : n.environment === "prod" ? ui(e, t) : yi(e, t, n), xi = (e, t) => {
2694
2934
  switch (e.kind) {
2695
2935
  case "skipped": return `auto-migrate: skipped (${e.reason})`;
2696
- case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${F(e.fingerprint)})`;
2936
+ case "up-to-date": return `auto-migrate: schema up to date (${t}, fingerprint=${N(e.fingerprint)})`;
2697
2937
  case "applied": {
2698
2938
  let t = e.plan.summary;
2699
- return `auto-migrate: applied ${e.applied.appliedOps} op(s) in ${e.applied.durationMs}ms [safe=${t.safe} needs-default=${t.needsDefault} needs-backfill=${t.needsBackfill} rename=${t.needsRenameAnnotation} lossy=${t.lossy}] fingerprint=${F(e.applied.fingerprint)}`;
2939
+ return `auto-migrate: applied ${e.applied.appliedOps} op(s) in ${e.applied.durationMs}ms [safe=${t.safe} needs-default=${t.needsDefault} needs-backfill=${t.needsBackfill} rename=${t.needsRenameAnnotation} lossy=${t.lossy}] fingerprint=${N(e.applied.fingerprint)}`;
2700
2940
  }
2701
- case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${F(e.expected)}` + (e.actual ? ` live=${F(e.actual)}` : " live=<no _voltro_migration_plans row>") + ". Run `voltro db apply --plan plan.json` from the deploy pipeline before serving.";
2941
+ case "prod-mismatch": return `auto-migrate: SCHEMA FINGERPRINT MISMATCH — declared=${N(e.expected)}` + (e.actual ? ` live=${N(e.actual)}` : " live=<no _voltro_migration_plans row>") + ". Run `voltro db apply --plan plan.json` from the deploy pipeline before serving.";
2702
2942
  case "refused-blocked": {
2703
- let t = e.plan.operations.filter((e) => e.blocked), n = t.filter((e) => e.op.kind === "drop-table").length, r = t.filter((e) => e.op.kind === "drop-table").map((e) => "table" in e.op ? e.op.table : "").filter(Boolean), i = t.filter((e) => e.op.kind === "add-column").length, a = t.filter((e) => e.op.kind === "drop-column").length, o = t.filter((e) => e.classification === "needs-rename-annotation").length, s = `auto-migrate: REFUSED — ${e.plan.summary.blocked} blocked operation(s):\n` + Dr(e.plan), c = [""];
2943
+ let t = e.plan.operations.filter((e) => e.blocked), n = t.filter((e) => e.op.kind === "drop-table").length, r = t.filter((e) => e.op.kind === "drop-table").map((e) => "table" in e.op ? e.op.table : "").filter(Boolean), i = t.filter((e) => e.op.kind === "add-column").length, a = t.filter((e) => e.op.kind === "drop-column").length, o = t.filter((e) => e.classification === "needs-rename-annotation").length, s = `auto-migrate: REFUSED — ${e.plan.summary.blocked} blocked operation(s):\n` + si(e.plan), c = [""];
2704
2944
  if (n > 0 && c.push(`${n} table(s) exist in the live DB but aren't in your declared schema.`, "This is usually one of three things:", " (a) You're iterating on the schema — the table file got renamed, moved, or temporarily commented out.", " → Restore the declaration. Your data stays intact.", " (b) You want to KEEP the table but NOT manage it with Voltro (a leftover from another tool —", " a Strapi/Rails/legacy migration artifact, or an externally-owned table).", ` → Set \`VOLTRO_DB_IGNORE_TABLES=${r.join(",")}\` (comma-separated).`, " The planner excludes those tables from the diff entirely — it never drops them, and they", " stop blocking your OTHER (additive) changes. This is the fix when a leftover freezes your schema.", " (c) You actually want to drop the table.", ` → Set \`VOLTRO_DESTRUCTIVE_OK=${r.join(",")}\` for ONE run — it acknowledges`, " the data loss for THOSE tables only; every other lossy op in this plan stays blocked.", " (`VOLTRO_DESTRUCTIVE_OK=1` acknowledges all of them, which is rarely what you mean.)", " There is deliberately no `dropped()` marker for a TABLE, unlike for a column: a", " dropped column leaves a slot worth documenting in the declaration, a dropped table", " leaves nothing — the marker would be a dead entry you must remember to delete.", " Soft-drop: set `VOLTRO_SOFT_DROP=1` alongside DESTRUCTIVE_OK to RENAME instead of DROP", " (data survives as `<name>__dropped_<ts>` for ~7d; restorable via `voltro db restore-snapshot`)."), i > 0 && c.push(`${i} required column(s) need a backfill strategy before adding to a populated table.`, " → Annotate the column: `.backfill(sql`<expr>`)` or `.backfill(row => <fn>)` or `.default(<value>)`.", " Existing rows get the backfill value; the column then lands as NOT NULL."), a > 0) {
2705
2945
  let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
2706
2946
  for (let r of e.plan.operations) {
@@ -2717,7 +2957,7 @@ BEGIN
2717
2957
  return o > 0 && c.push(`${o} column(s) look like renames (one column gone + a new one of same shape appeared).`, " → On the NEW column add `.renamedFrom('<oldName>')` so the planner emits RENAME instead of DROP+ADD."), c.push("", "See `voltro db plan` for the full machine-readable diff."), s + "\n" + c.join("\n");
2718
2958
  }
2719
2959
  }
2720
- }, Hr = /* @__PURE__ */ new Set([
2960
+ }, Si = /* @__PURE__ */ new Set([
2721
2961
  "node_modules",
2722
2962
  "dist",
2723
2963
  "build",
@@ -2726,24 +2966,24 @@ BEGIN
2726
2966
  ".next",
2727
2967
  ".git",
2728
2968
  ".framework"
2729
- ]), Ur = async (e) => {
2969
+ ]), Ci = async (e) => {
2730
2970
  let t = [], n = async (e) => {
2731
- let r = await _.readdir(e, { withFileTypes: !0 }).catch(() => []);
2971
+ let r = await ee.readdir(e, { withFileTypes: !0 }).catch(() => []);
2732
2972
  for (let i of r) {
2733
- if (Hr.has(i.name)) continue;
2734
- let r = v(e, i.name);
2735
- i.isDirectory() ? await n(r) : i.isFile() && o.test(i.name) && t.push(r);
2973
+ if (Si.has(i.name)) continue;
2974
+ let r = te(e, i.name);
2975
+ i.isDirectory() ? await n(r) : i.isFile() && u.test(i.name) && t.push(r);
2736
2976
  }
2737
2977
  };
2738
2978
  return await n(e), t.sort((e, t) => {
2739
2979
  let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
2740
2980
  return n.localeCompare(r);
2741
2981
  });
2742
- }, Wr = async (e) => {
2982
+ }, wi = async (e) => {
2743
2983
  let t = [];
2744
2984
  for (let n of e) {
2745
- let e = (await import(y(n).href)).default;
2746
- if (!r(e)) {
2985
+ let e = (await import(ne(n).href)).default;
2986
+ if (!o(e)) {
2747
2987
  process.stderr.write(`[voltro:migrate] ${n} has no default-exported \`migration(...)\` — skipped\n`);
2748
2988
  continue;
2749
2989
  }
@@ -2753,11 +2993,11 @@ BEGIN
2753
2993
  });
2754
2994
  }
2755
2995
  return t;
2756
- }, Gr = (e) => e`
2996
+ }, Ti = (e) => e`
2757
2997
  SELECT ${e("id")}
2758
2998
  FROM ${e("_voltro_migration_plans")}
2759
2999
  WHERE ${e("source")} = 'file'
2760
- `.pipe(p.map((e) => e.map((e) => e.id)), p.catchAll(() => p.succeed([]))), Kr = class extends Error {
3000
+ `.pipe(b.map((e) => e.map((e) => e.id)), b.catchAll(() => b.succeed([]))), Ei = class extends Error {
2761
3001
  migrationId;
2762
3002
  file;
2763
3003
  cause;
@@ -2765,28 +3005,28 @@ BEGIN
2765
3005
  constructor(e, t, n) {
2766
3006
  super(`migration ${e}: up() SUCCEEDED but the ledger row could not be written.\n The change IS in the database. It is NOT recorded, so the next run would apply it AGAIN.\n Do not re-run ${t} until this is resolved.\n Cause: ${n instanceof Error ? n.message : String(n)}`), this.migrationId = e, this.file = t, this.cause = n, this.name = "FileMigrationLedgerError";
2767
3007
  }
2768
- }, qr = (e, t, n, r) => p.gen(function* () {
3008
+ }, Di = (e, t, n, r) => b.gen(function* () {
2769
3009
  let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
2770
3010
  sql: e,
2771
3011
  log: {
2772
- info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
3012
+ info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
2773
3013
  scope: `migrations:file:${t.migration.id}`,
2774
3014
  ...n ?? {}
2775
3015
  })),
2776
- warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
3016
+ warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
2777
3017
  scope: `migrations:file:${t.migration.id}`,
2778
3018
  ...n ?? {}
2779
3019
  }))
2780
3020
  },
2781
3021
  appliedAt: a
2782
3022
  }, s = t.migration.up(o);
2783
- p.isEffect(s) ? yield* s : yield* p.tryPromise({
3023
+ b.isEffect(s) ? yield* s : yield* b.tryPromise({
2784
3024
  try: () => s,
2785
3025
  catch: (e) => e
2786
3026
  });
2787
3027
  let c = Date.now() - i;
2788
- !p.isEffect(s) && c < 2 && (yield* p.logWarning(`migration ${t.migration.id}: up() resolved in ${c}ms — did the body \`await\` an \`@effect/sql\` Effect by accident? \`sql.unsafe(...)\` returns an Effect, not a Promise; \`await\` on it is a silent no-op. Switch the body to \`Effect.gen(function* () { yield* sql.unsafe(...) })\` form.`).pipe(p.annotateLogs({ scope: `migrations:file:${t.migration.id}` })));
2789
- let l = yield* fn(e, a);
3028
+ !b.isEffect(s) && c < 2 && (yield* b.logWarning(`migration ${t.migration.id}: up() resolved in ${c}ms — did the body \`await\` an \`@effect/sql\` Effect by accident? \`sql.unsafe(...)\` returns an Effect, not a Promise; \`await\` on it is a silent no-op. Switch the body to \`Effect.gen(function* () { yield* sql.unsafe(...) })\` form.`).pipe(b.annotateLogs({ scope: `migrations:file:${t.migration.id}` })));
3029
+ let l = yield* H(e, a);
2790
3030
  return yield* e`
2791
3031
  INSERT INTO ${e("_voltro_migration_plans")}
2792
3032
  (${e("id")}, ${e("fingerprint")}, ${e("operations")},
@@ -2799,94 +3039,94 @@ BEGIN
2799
3039
  }])},
2800
3040
  ${r}, ${n}, ${"file"},
2801
3041
  ${c}, ${l}, ${t.migration.description})
2802
- `.pipe(p.mapError((e) => new Kr(t.migration.id, t.file, e))), { durationMs: c };
2803
- }), Jr = (e, t) => p.gen(function* () {
2804
- let n = yield* p.tryPromise({
2805
- try: () => Ur(v(t, "migrations")),
3042
+ `.pipe(b.mapError((e) => new Ei(t.migration.id, t.file, e))), { durationMs: c };
3043
+ }), Oi = (e, t) => b.gen(function* () {
3044
+ let n = yield* b.tryPromise({
3045
+ try: () => Ci(te(t, "migrations")),
2806
3046
  catch: (e) => e
2807
3047
  });
2808
3048
  if (n.length === 0) return {
2809
3049
  pending: [],
2810
3050
  skipped: []
2811
3051
  };
2812
- let r = yield* p.tryPromise({
2813
- try: () => Wr(n),
3052
+ let r = yield* b.tryPromise({
3053
+ try: () => wi(n),
2814
3054
  catch: (e) => e
2815
3055
  });
2816
3056
  if (r.length === 0) return {
2817
3057
  pending: [],
2818
3058
  skipped: []
2819
3059
  };
2820
- let i = new Set(yield* Gr(e));
3060
+ let i = new Set(yield* Ti(e));
2821
3061
  return {
2822
3062
  pending: r.filter((e) => !i.has(e.migration.id)),
2823
3063
  skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
2824
3064
  };
2825
- }), Yr = (e, t) => Jr(e, t).pipe(p.map((e) => e.pending.map((e) => e.migration.id))), Xr = (e, t) => p.gen(function* () {
2826
- let { pending: n, skipped: r } = yield* Jr(e, t.projectRoot);
3065
+ }), ki = (e, t) => Oi(e, t).pipe(b.map((e) => e.pending.map((e) => e.migration.id))), Ai = (e, t) => b.gen(function* () {
3066
+ let { pending: n, skipped: r } = yield* Oi(e, t.projectRoot);
2827
3067
  if (n.length === 0) return {
2828
3068
  applied: [],
2829
3069
  skipped: r
2830
3070
  };
2831
- yield* E(e);
3071
+ yield* T(e);
2832
3072
  let i = [];
2833
3073
  try {
2834
3074
  for (let r of n) {
2835
- yield* p.logInfo("file-based migration applying").pipe(p.annotateLogs({
3075
+ yield* b.logInfo("file-based migration applying").pipe(b.annotateLogs({
2836
3076
  scope: "voltro:migrate",
2837
3077
  id: r.migration.id,
2838
3078
  file: r.file
2839
3079
  }));
2840
- let { durationMs: n } = yield* qr(e, r, t.env, t.appliedBy);
3080
+ let { durationMs: n } = yield* Di(e, r, t.env, t.appliedBy);
2841
3081
  i.push({
2842
3082
  id: r.migration.id,
2843
3083
  durationMs: n
2844
- }), yield* p.logInfo("file-based migration applied").pipe(p.annotateLogs({
3084
+ }), yield* b.logInfo("file-based migration applied").pipe(b.annotateLogs({
2845
3085
  scope: "voltro:migrate",
2846
3086
  id: r.migration.id,
2847
3087
  durationMs: n
2848
3088
  }));
2849
3089
  }
2850
3090
  } finally {
2851
- yield* D(e).pipe(p.orDie);
3091
+ yield* E(e).pipe(b.orDie);
2852
3092
  }
2853
3093
  return {
2854
3094
  applied: i,
2855
3095
  skipped: r
2856
3096
  };
2857
- }), Zr = (e, t) => e`
3097
+ }), ji = (e, t) => e`
2858
3098
  SELECT ${e("id")}
2859
3099
  FROM ${e("_voltro_migration_plans")}
2860
3100
  WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
2861
3101
  LIMIT 1
2862
- `, Qr = (e, t) => p.gen(function* () {
2863
- if (!(yield* Zr(e, t.id))[0]) return yield* p.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
2864
- let n = yield* p.tryPromise({
2865
- try: () => Ur(v(t.projectRoot, "migrations")),
3102
+ `, Mi = (e, t) => b.gen(function* () {
3103
+ if (!(yield* ji(e, t.id))[0]) return yield* b.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
3104
+ let n = yield* b.tryPromise({
3105
+ try: () => Ci(te(t.projectRoot, "migrations")),
2866
3106
  catch: (e) => e
2867
- }), r = (yield* p.tryPromise({
2868
- try: () => Wr(n),
3107
+ }), r = (yield* b.tryPromise({
3108
+ try: () => wi(n),
2869
3109
  catch: (e) => e
2870
3110
  })).find((e) => e.migration.id === t.id);
2871
- if (!r) return yield* p.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
2872
- yield* E(e);
3111
+ if (!r) return yield* b.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
3112
+ yield* T(e);
2873
3113
  let i = Date.now();
2874
3114
  try {
2875
3115
  let n = {
2876
3116
  sql: e,
2877
3117
  log: {
2878
- info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
3118
+ info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
2879
3119
  scope: `migrations:file:${t.id}`,
2880
3120
  ...n ?? {}
2881
3121
  })),
2882
- warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
3122
+ warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
2883
3123
  scope: `migrations:file:${t.id}`,
2884
3124
  ...n ?? {}
2885
3125
  }))
2886
3126
  },
2887
3127
  appliedAt: (/* @__PURE__ */ new Date()).toISOString()
2888
3128
  }, a = r.migration.down(n);
2889
- p.isEffect(a) ? yield* a : yield* p.tryPromise({
3129
+ b.isEffect(a) ? yield* a : yield* b.tryPromise({
2890
3130
  try: () => a,
2891
3131
  catch: (e) => e
2892
3132
  });
@@ -2896,19 +3136,19 @@ BEGIN
2896
3136
  durationMs: o
2897
3137
  };
2898
3138
  } finally {
2899
- yield* D(e).pipe(p.orDie);
3139
+ yield* E(e).pipe(b.orDie);
2900
3140
  }
2901
- }), $r = (e) => p.gen(function* () {
2902
- let t = yield* h.SqlClient, n = yield* t`
3141
+ }), Ni = (e) => b.gen(function* () {
3142
+ let t = yield* S.SqlClient, n = yield* t`
2903
3143
  SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
2904
3144
  WHERE source = 'auto-diff'
2905
3145
  AND squashedAt IS NULL
2906
3146
  AND appliedAt < ${e.before}
2907
3147
  ORDER BY appliedAt DESC
2908
3148
  `;
2909
- if (n.length === 0) return yield* p.fail(/* @__PURE__ */ Error(`squash: no rows applied before ${e.before}`));
3149
+ if (n.length === 0) return yield* b.fail(/* @__PURE__ */ Error(`squash: no rows applied before ${e.before}`));
2910
3150
  let r = n[0];
2911
- if (r.fingerprint !== e.fingerprint) return yield* p.fail(/* @__PURE__ */ Error(`squash: latest pre-squash fingerprint (${r.fingerprint.slice(0, 12)}…) differs from current declared-schema fingerprint (${e.fingerprint.slice(0, 12)}…). This means the working tree carries un-applied changes — squashing now would lock the drift in.\n\nFix: run \`voltro db apply\` first to align the live schema with the working tree, then re-squash.`));
3151
+ if (r.fingerprint !== e.fingerprint) return yield* b.fail(/* @__PURE__ */ Error(`squash: latest pre-squash fingerprint (${r.fingerprint.slice(0, 12)}…) differs from current declared-schema fingerprint (${e.fingerprint.slice(0, 12)}…). This means the working tree carries un-applied changes — squashing now would lock the drift in.\n\nFix: run \`voltro db apply\` first to align the live schema with the working tree, then re-squash.`));
2912
3152
  yield* t`
2913
3153
  UPDATE _voltro_migration_plans
2914
3154
  SET squashedAt = CURRENT_TIMESTAMP
@@ -2943,7 +3183,7 @@ BEGIN
2943
3183
  snapshotFingerprint: e.fingerprint,
2944
3184
  snapshotId: i
2945
3185
  };
2946
- }), ei = { safe: !0 }, ti = (e) => {
3186
+ }), Pi = { safe: !0 }, Fi = (e) => {
2947
3187
  switch (e.kind) {
2948
3188
  case "drop-column": return {
2949
3189
  safe: !1,
@@ -2970,7 +3210,7 @@ BEGIN
2970
3210
  reason: `old instances read/write "${e.table}"."${e.column}" as ${e.from}; the new ${e.to} type can reject their writes or fail to decode their reads`,
2971
3211
  remedy: "add a new column of the target type + backfill + dual-write, cut code over, then drop the old column in a LATER deploy"
2972
3212
  };
2973
- case "alter-column-nullability": return e.toNullable ? ei : {
3213
+ case "alter-column-nullability": return e.toNullable ? Pi : {
2974
3214
  safe: !1,
2975
3215
  reason: `old instances may INSERT "${e.table}" without (or with NULL in) "${e.column}", which the new NOT NULL rejects`,
2976
3216
  remedy: `make code always write "${e.column}" and deploy that first; add NOT NULL in a LATER deploy (with a default to cover the gap)`
@@ -3000,12 +3240,12 @@ BEGIN
3000
3240
  case "drop-unique":
3001
3241
  case "drop-unique-composite":
3002
3242
  case "drop-foreign-key":
3003
- case "drop-check": return ei;
3243
+ case "drop-check": return Pi;
3004
3244
  }
3005
- }, ni = (e) => {
3245
+ }, Ii = (e) => {
3006
3246
  let t = [];
3007
3247
  for (let n of e) {
3008
- let e = ti(n.op);
3248
+ let e = Fi(n.op);
3009
3249
  e.safe || t.push({
3010
3250
  op: n,
3011
3251
  reason: e.reason,
@@ -3013,7 +3253,7 @@ BEGIN
3013
3253
  });
3014
3254
  }
3015
3255
  return t;
3016
- }, ri = (e) => {
3256
+ }, Li = (e) => {
3017
3257
  if (!e.enabled || e.unsafeCount === 0) return {
3018
3258
  refuse: !1,
3019
3259
  warnForced: !1,
@@ -3029,10 +3269,127 @@ BEGIN
3029
3269
  warnForced: !1,
3030
3270
  message: `${t}. Old instances would break against the new schema mid-rollout. Split into expand → deploy → contract (see the ⚠ list above), deploy with no overlap (maintenance window / scale-to-zero), or pass --force to override.`
3031
3271
  };
3032
- }, ii = (e, ...t) => ({
3272
+ }, Ri = (e) => {
3273
+ let t = e.table;
3274
+ if (typeof t == "string") return t;
3275
+ let n = e.from;
3276
+ return typeof n == "string" ? n : void 0;
3277
+ }, zi = (e) => ({
3278
+ kind: e.op.kind,
3279
+ table: Ri(e.op),
3280
+ classification: e.classification,
3281
+ reason: e.reason,
3282
+ blockedFix: e.blocked?.fix
3283
+ }), Bi = (e) => e.operations.map(zi), Vi = async (e) => {
3284
+ let t = e.log ?? (() => {}), n = {
3285
+ branchId: e.branchId,
3286
+ mechanism: e.mechanism,
3287
+ branchedTables: e.branchedTables,
3288
+ replayedForeignKeys: e.replayedForeignKeys
3289
+ }, r = [], i = !1, a;
3290
+ try {
3291
+ await e.provision(), i = !0, t(`branch ${e.branchId} provisioned (${e.mechanism}, ${e.branchedTables} table(s), ${e.replayedForeignKeys} foreign key(s))`);
3292
+ let o = await e.fidelityPlan();
3293
+ if (o.operations.length > 0) a = {
3294
+ ...n,
3295
+ outcome: "infidelity",
3296
+ operations: r,
3297
+ lossy: r,
3298
+ blocked: r,
3299
+ applied: !1,
3300
+ converged: !1,
3301
+ residual: r,
3302
+ infidelity: Bi(o)
3303
+ };
3304
+ else {
3305
+ let i = await e.plan(), o = Bi(i), s = o.filter((e) => e.classification === "lossy"), c = _i(i), l = c.operations.filter((e) => e.blocked !== void 0).map(zi);
3306
+ if (l.length > 0) a = {
3307
+ ...n,
3308
+ outcome: "blocked",
3309
+ operations: o,
3310
+ lossy: s,
3311
+ blocked: l,
3312
+ applied: !1,
3313
+ converged: !1,
3314
+ residual: r,
3315
+ infidelity: r
3316
+ };
3317
+ else if (o.length === 0) a = {
3318
+ ...n,
3319
+ outcome: "clean",
3320
+ operations: o,
3321
+ lossy: s,
3322
+ blocked: l,
3323
+ applied: !1,
3324
+ converged: !0,
3325
+ residual: r,
3326
+ infidelity: r
3327
+ };
3328
+ else {
3329
+ await e.apply(c), t(`branch ${e.branchId}: applied ${o.length} operation(s)`);
3330
+ let i = Bi(await e.replan());
3331
+ a = {
3332
+ ...n,
3333
+ outcome: i.length > 0 ? "diverged" : s.length > 0 ? "lossy" : "clean",
3334
+ operations: o,
3335
+ lossy: s,
3336
+ blocked: l,
3337
+ applied: !0,
3338
+ converged: i.length === 0,
3339
+ residual: i,
3340
+ infidelity: r
3341
+ };
3342
+ }
3343
+ }
3344
+ } catch (e) {
3345
+ a = {
3346
+ ...n,
3347
+ outcome: "failed",
3348
+ operations: r,
3349
+ lossy: r,
3350
+ blocked: r,
3351
+ applied: !1,
3352
+ converged: !1,
3353
+ residual: r,
3354
+ infidelity: r,
3355
+ error: e instanceof Error ? e.message : String(e)
3356
+ };
3357
+ }
3358
+ let o = !1;
3359
+ if (i && e.keep !== !0) try {
3360
+ await e.teardown(), o = !0;
3361
+ } catch (n) {
3362
+ t(`branch ${e.branchId}: TEARDOWN FAILED — drop it by hand: ${n instanceof Error ? n.message : String(n)}`);
3363
+ }
3364
+ return {
3365
+ ...a,
3366
+ tornDown: o
3367
+ };
3368
+ }, Hi = (e) => {
3369
+ switch (e.outcome) {
3370
+ case "clean": return 0;
3371
+ case "lossy": return 2;
3372
+ case "blocked": return 2;
3373
+ case "diverged": return 1;
3374
+ case "infidelity": return 1;
3375
+ case "failed": return 1;
3376
+ }
3377
+ }, Ui = (e) => ` ${e.classification === "lossy" ? "✗" : "•"} ${e.kind}${e.table ? ` ${e.table}` : ""} [${e.classification}]${e.reason ? ` — ${e.reason}` : ""}${e.blockedFix ? `\n ! ${e.blockedFix}` : ""}`, Wi = (e) => {
3378
+ let t = [];
3379
+ if (t.push(`branch rehearsal · ${e.branchId} · mechanism ${e.mechanism}`), t.push(` branched ${e.branchedTables} table(s), replayed ${e.replayedForeignKeys} foreign key(s)`), e.outcome === "failed") t.push(` FAILED: ${e.error ?? "unknown error"}`);
3380
+ else if (e.outcome === "infidelity") {
3381
+ t.push(" BRANCH IS NOT A FAITHFUL COPY of the parent — the rehearsal proves nothing about your migration."), t.push(" The parent's own schema still differs from the branch by:");
3382
+ for (let n of e.infidelity) t.push(Ui(n));
3383
+ } else {
3384
+ t.push(` plan: ${e.operations.length} operation(s), ${e.lossy.length} lossy, ${e.blocked.length} refused`);
3385
+ for (let n of e.operations) t.push(Ui(n));
3386
+ if (e.lossy.length > 0 && (t.push(""), t.push(` ⚠ ${e.lossy.length} operation(s) DESTROY DATA. They were executed on the branch (it is`), t.push(" disposable) so they are rehearsed, but production refuses them until you set"), t.push(" VOLTRO_DESTRUCTIVE_OK — naming the tables, not `1`.")), e.blocked.length > 0 && (t.push(""), t.push(` ${e.blocked.length} operation(s) the planner will not auto-apply anywhere — the plan was NOT executed.`)), e.applied && (t.push(""), t.push(e.converged ? " ✓ applied on the branch, and the re-plan is EMPTY (the migration converges)." : ` ✗ applied, but the re-plan STILL proposes ${e.residual.length} operation(s) — this migration does not converge:`), !e.converged)) for (let n of e.residual) t.push(Ui(n));
3387
+ }
3388
+ return t.push(e.tornDown ? ` branch ${e.branchId} torn down.` : ` branch ${e.branchId} KEPT — drop it when you are done.`), t.join("\n");
3389
+ }, Gi = (e, ...t) => ({
3033
3390
  _tag: "RawSqlFragment",
3034
3391
  strings: [...e],
3035
3392
  values: [...t]
3036
- }), ai = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
3393
+ }), Ki = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
3037
3394
  //#endregion
3038
- export { O as DEFAULT_CDC_CHANNEL, o as FILE_MIGRATION_PATTERN, Kr as FileMigrationLedgerError, k as REACTIVE_TRIGGER_PREFIX, ne as VOLTRO_MIGRATION_LOCK_KEY, E as acquireMigrationLock, et as applyNamespacedSchema, Er as applyPlan, Je as applySchema, ri as assessRollingDeployGate, en as chunkTables, ti as classifyRollingDeploySafety, I as declaredSnapshot, xe as defaultArrayClause, be as defaultClause, M as defaultJsonClause, Vr as describeOutcome, Fr as destructiveScope, ft as detectReactiveTriggerDrift, _n as emitDropColumnDdl, vn as emitDropColumnDdlMysql, qe as emitFrameworkBootstrapSql, Ge as emitNamespaceProvisionDdl, Ke as emitNamespacedSchemaSql, We as emitSchemaSql, P as fingerprintSchema, pt as formatReactiveTriggerDrift, Pr as ignoreTablesFromEnv, U as introspectSchema, r as isFileMigration, ai as isRawSqlFragment, Yt as mapPgType, c as migration, N as numericIdSql, Jt as parseEnumCheck, Yr as pendingFileMigrationIds, qt as planMigrations, tt as provisionTenantNamespace, Fe as reactiveTriggerRepairSql, D as releaseMigrationLock, Nn as renderColumnMysql, On as renderColumnPg, Qr as rollbackFileBasedMigration, ni as rollingDeployUnsafeOps, Xr as runFileBasedMigrations, nt as runFrameworkBootstrap, $e as runMigrate, Br as runPlannedMigrations, F as shortFingerprint, ct as snapshotColumn, ii as sql, j as sqlType, $r as squashMigrationPlans, Lr as unblockLossy, ge as withMigrationLock };
3395
+ export { D as DEFAULT_CDC_CHANNEL, u as FILE_MIGRATION_PATTERN, Ei as FileMigrationLedgerError, Te as REACTIVE_TRIGGER_PREFIX, ue as VOLTRO_MIGRATION_LOCK_KEY, T as acquireMigrationLock, ut as applyNamespacedSchema, oi as applyPlan, it as applySchema, Li as assessRollingDeployGate, Hi as branchRehearsalExitCode, Tn as chunkTables, Fi as classifyRollingDeploySafety, P as declaredSnapshot, je as defaultArrayClause, Ae as defaultClause, A as defaultJsonClause, xi as describeOutcome, hi as destructiveScope, St as detectReactiveTriggerDrift, wr as emitAddIndexMysql, or as emitCreateTableMysql, Un as emitDropColumnDdl, Wn as emitDropColumnDdlMysql, rt as emitFrameworkBootstrapSql, tt as emitNamespaceProvisionDdl, nt as emitNamespacedSchemaSql, et as emitSchemaSql, M as fingerprintSchema, Wi as formatBranchRehearsal, Ct as formatReactiveTriggerDrift, mi as ignoreTablesFromEnv, Ln as introspectSchema, o as isFileMigration, Ki as isRawSqlFragment, bn as mapPgType, h as migration, br as mysqlIndexPrefixFor, j as numericIdSql, yn as parseEnumCheck, ki as pendingFileMigrationIds, rn as planMigrations, dt as provisionTenantNamespace, Ge as reactiveTriggerRepairSql, li as recordUpToDate, Vi as rehearseMigrationOnBranch, zi as rehearsedOperation, E as releaseMigrationLock, ar as renderColumnMysql, er as renderColumnPg, Mi as rollbackFileBasedMigration, Ii as rollingDeployUnsafeOps, Ai as runFileBasedMigrations, ft as runFrameworkBootstrap, lt as runMigrate, bi as runPlannedMigrations, N as shortFingerprint, vt as snapshotColumn, Gi as sql, k as sqlType, Ni as squashMigrationPlans, _i as unblockLossy, we as withMigrationLock };