@voltro/database 0.32.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,106 +1044,107 @@ 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
- if (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})`)), n.references)) {
1093
- let t = n.references.table, r = n.references.orphanPolicy ?? "fail", i = {
1094
- kind: "add-foreign-key",
1095
- table: e,
1096
- column: n.name,
1097
- targetTable: t,
1098
- targetColumn: n.references.column,
1099
- ...n.references.onDelete ? { onDelete: n.references.onDelete } : {},
1100
- ...n.references.onUpdate ? { onUpdate: n.references.onUpdate } : {}
1101
- };
1102
- r === "null" && !n.nullable ? s.push(R(i, "lossy", `add FK ${e}.${n.name} ${t}: orphanPolicy 'null' needs a nullable column (the NULL-out would violate NOT NULL)`, { fix: `make ${n.name} \`.nullable()\`, OR use \`orphanPolicy: 'delete'\`, OR clean the orphans and drop the policy` })) : r === "null" || r === "delete" ? s.push(R({
1103
- ...i,
1104
- orphanPolicy: r
1105
- }, "lossy", r === "delete" ? `add FK ${e}.${n.name} → ${t}: orphanPolicy 'delete' first DELETES rows whose ${n.name} points at a missing ${t}` : `add FK ${e}.${n.name} ${t}: orphanPolicy 'null' first NULLs ${n.name} where it points at a missing ${t}`)) : s.push(R(i, "needs-backfill", `add FK ${e}.${n.name} ${t} — existing rows must already reference a valid ${t} (else set orphanPolicy:'null'/'delete')`));
1106
- }
1107
- return s;
1108
- }, Rt = (e, t, n) => {
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) => {
1128
+ let n = [];
1129
+ if (!t.references) return n;
1130
+ let r = t.references.table, i = t.references.orphanPolicy ?? "fail", a = {
1131
+ kind: "add-foreign-key",
1132
+ table: e,
1133
+ column: t.name,
1134
+ targetTable: r,
1135
+ targetColumn: t.references.column,
1136
+ ...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
1137
+ ...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
1138
+ };
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({
1140
+ ...a,
1141
+ orphanPolicy: i
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) => {
1109
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));
1110
1145
  for (let [n, c] of Object.entries(t)) {
1111
1146
  let t = c.renamedFrom;
1112
- 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({
1113
1148
  kind: "rename-column",
1114
1149
  table: e.tableName,
1115
1150
  from: t,
@@ -1121,26 +1156,26 @@ BEGIN
1121
1156
  consumedAddedNames: i,
1122
1157
  consumedRemovedNames: a
1123
1158
  };
1124
- }, zt = /* @__PURE__ */ new Set([
1159
+ }, Xt = /* @__PURE__ */ new Set([
1125
1160
  "postgres",
1126
1161
  "mysql",
1127
1162
  "mariadb",
1128
1163
  "mssql"
1129
- ]), Bt = {
1164
+ ]), Zt = {
1130
1165
  ops: [],
1131
1166
  consumedAddedNames: /* @__PURE__ */ new Set(),
1132
1167
  consumedRemovedNames: /* @__PURE__ */ new Set()
1133
- }, Vt = (e) => !e.unique && !e.expression, Ht = (e, t, n, r, i) => {
1134
- if (i === void 0 || !zt.has(i)) return Bt;
1135
- let a = t.added.filter((e) => Vt(e) && !r.has(e.name));
1136
- if (a.length === 0) return Bt;
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;
1137
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();
1138
1173
  for (let n of t.removed) {
1139
- if (!Vt(n)) continue;
1174
+ if (!Qt(n)) continue;
1140
1175
  let t = o(n), r = a.filter((e) => !l.has(e.name) && s(e) === t);
1141
1176
  if (r.length !== 1) continue;
1142
1177
  let i = r[0];
1143
- 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({
1144
1179
  kind: "rename-index",
1145
1180
  table: e,
1146
1181
  from: n.name,
@@ -1152,7 +1187,7 @@ BEGIN
1152
1187
  consumedAddedNames: l,
1153
1188
  consumedRemovedNames: u
1154
1189
  };
1155
- }, Ut = (e, t, n) => {
1190
+ }, en = (e, t, n) => {
1156
1191
  let r = [], i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Set();
1157
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));
1158
1193
  for (let c of e.tables) {
@@ -1163,7 +1198,7 @@ BEGIN
1163
1198
  continue;
1164
1199
  }
1165
1200
  if (t.has(c.name)) {
1166
- r.push(R({
1201
+ r.push(I({
1167
1202
  kind: "rename-table",
1168
1203
  from: e,
1169
1204
  to: c.name
@@ -1171,14 +1206,14 @@ BEGIN
1171
1206
  continue;
1172
1207
  }
1173
1208
  if ((s.get(e) ?? 0) !== 1) {
1174
- r.push(R({
1209
+ r.push(I({
1175
1210
  kind: "rename-table",
1176
1211
  from: e,
1177
1212
  to: c.name
1178
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.` }));
1179
1214
  continue;
1180
1215
  }
1181
- i.set(c.name, e), a.add(e), r.push(R({
1216
+ i.set(c.name, e), a.add(e), r.push(I({
1182
1217
  kind: "rename-table",
1183
1218
  from: e,
1184
1219
  to: c.name
@@ -1192,19 +1227,19 @@ BEGIN
1192
1227
  claimedOldNames: c,
1193
1228
  refusals: o
1194
1229
  };
1195
- }, Wt = (e, t, n, r) => {
1230
+ }, tn = (e, t, n, r) => {
1196
1231
  if (t === void 0) return {
1197
1232
  live: e,
1198
1233
  ops: []
1199
1234
  };
1200
- let i = r !== void 0 && zt.has(r), a = [], o = e.indexes.map((e) => {
1235
+ let i = r !== void 0 && Xt.has(r), a = [], o = e.indexes.map((e) => {
1201
1236
  if (!e.name.startsWith(t)) return e;
1202
1237
  let r = `${n}${e.name.slice(t.length)}`;
1203
1238
  return r === e.name ? e : e.name === `${t}_pkey` ? {
1204
1239
  ...e,
1205
1240
  name: r,
1206
1241
  table: n
1207
- } : i ? (a.push(R({
1242
+ } : i ? (a.push(I({
1208
1243
  kind: "rename-index",
1209
1244
  table: n,
1210
1245
  from: e.name,
@@ -1223,7 +1258,7 @@ BEGIN
1223
1258
  },
1224
1259
  ops: a
1225
1260
  };
1226
- }, Gt = (e) => {
1261
+ }, nn = (e) => {
1227
1262
  let t = {
1228
1263
  safe: 0,
1229
1264
  needsDefault: 0,
@@ -1258,51 +1293,51 @@ BEGIN
1258
1293
  break;
1259
1294
  }
1260
1295
  return t;
1261
- }, Kt = (e) => {
1262
- 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 = Ut(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);
1263
1298
  a.push(...o.ops);
1264
1299
  for (let e of t.tables) if (!o.renamedInto.has(e.name) && !r.has(e.name)) {
1265
- 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)));
1266
1301
  for (let t of e.columns) {
1267
- let n = Tt(e.name, t);
1302
+ let n = Ft(e.name, t);
1268
1303
  n && a.push(n);
1269
1304
  }
1270
1305
  }
1271
- let c = new Set(e.ignoreTables ?? []);
1272
- 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));
1273
1308
  for (let s of t.tables) {
1274
1309
  let t = o.renamedInto.get(s.name), c = r.get(s.name) ?? (t === void 0 ? void 0 : r.get(t));
1275
1310
  if (!c) continue;
1276
- let l = Wt(c, t, s.name, e.dialect);
1311
+ let l = tn(c, t, s.name, e.dialect);
1277
1312
  a.push(...l.ops);
1278
- let d = l.live, f = n.get(s.name), p = Dt(s, d, e.dialect), m = Ot(s, d), h = Rt(f.table, f.columnDefs, p);
1279
- a.push(...h.ops);
1280
- for (let e of p.removed) {
1281
- if (h.consumedRemovedNames.has(e.name) || u(e.name)) continue;
1282
- let t = f.columnDefs[e.name];
1283
- 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));
1284
1319
  }
1285
- for (let e of p.added) {
1286
- if (h.consumedAddedNames.has(e.name)) continue;
1287
- let t = f.columnDefs[e.name];
1288
- a.push(kt(s.name, e, t));
1289
- let n = Tt(s.name, e);
1290
- n && a.push(n);
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));
1291
1326
  }
1292
- let g = new Map(h.ops.flatMap((e) => e.op.kind === "rename-column" ? [[e.op.from, e.op.to]] : [])), _ = Ht(s.name, m, g, new Set(d.indexes.map((e) => e.name)), e.dialect);
1293
- a.push(..._.ops);
1294
- for (let e of m.added) _.consumedAddedNames.has(e.name) || a.push(Pt(s.name, e, d.rowCount, i));
1295
- for (let e of m.removed) _.consumedRemovedNames.has(e.name) || a.push(Ft(s.name, e));
1296
- for (let { declared: t, live: n } of p.changed) a.push(...Lt(s.name, t, n, e.dialect, d.rowCount, i));
1297
- }
1298
- 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) => {
1299
- let t = d.get(e);
1300
- 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) ? [{
1301
1336
  column: e.name,
1302
1337
  target: e.references.table
1303
1338
  }] : []) : [];
1304
- }, p = te([...l], f), m = [];
1305
- 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({
1306
1341
  kind: "add-foreign-key",
1307
1342
  table: e.name,
1308
1343
  column: t.name,
@@ -1311,14 +1346,14 @@ BEGIN
1311
1346
  ...t.references.onDelete ? { onDelete: t.references.onDelete } : {},
1312
1347
  ...t.references.onUpdate ? { onUpdate: t.references.onUpdate } : {}
1313
1348
  }, "safe", `deferred FK ${e.name}.${t.name} → ${t.references.table} — breaks a create-time table cycle`));
1314
- if (p.size > 0) {
1349
+ if (d.size > 0) {
1315
1350
  let e = (e) => e.references ? Object.fromEntries(Object.entries(e).filter(([e]) => e !== "references")) : e;
1316
1351
  for (let t = 0; t < a.length; t += 1) {
1317
1352
  let n = a[t];
1318
1353
  if (n.op.kind !== "create-table") continue;
1319
1354
  let r = n.op;
1320
- if (!r.columns.some((e) => p.has(T(r.table, e.name)))) continue;
1321
- 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);
1322
1357
  a[t] = {
1323
1358
  ...n,
1324
1359
  op: {
@@ -1327,23 +1362,23 @@ BEGIN
1327
1362
  }
1328
1363
  };
1329
1364
  }
1330
- a.push(...m);
1365
+ a.push(...f);
1331
1366
  }
1332
- let h = new Map(t.tables.map((e) => [e.name, e])), g = /* @__PURE__ */ new Map(), _ = (e, t = /* @__PURE__ */ new Set()) => {
1333
- 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);
1334
1369
  if (n !== void 0) return n;
1335
1370
  if (t.has(e)) return 0;
1336
- let r = h.get(e);
1371
+ let r = p.get(e);
1337
1372
  if (!r) return 0;
1338
1373
  let i = new Set(t).add(e), a = 0;
1339
1374
  for (let t of r.columns) {
1340
1375
  let n = t.references?.table;
1341
- 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));
1342
1377
  }
1343
- return g.set(e, a), a;
1344
- }, 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) => {
1345
1380
  let t = "table" in e ? e.table : "";
1346
- 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;
1347
1382
  }, b = {
1348
1383
  "add-column": 0,
1349
1384
  "rename-column": 1,
@@ -1363,7 +1398,7 @@ BEGIN
1363
1398
  "add-foreign-key": 5,
1364
1399
  "add-check": 5
1365
1400
  }, x = (e) => b[e] ?? 9, S = [...a].sort((e, t) => {
1366
- let n = v(e.op.kind), r = v(t.op.kind);
1401
+ let n = _(e.op.kind), r = _(t.op.kind);
1367
1402
  if (n !== r) return n - r;
1368
1403
  let i = y(e.op), a = y(t.op);
1369
1404
  if (i !== a) return i - a;
@@ -1374,11 +1409,87 @@ BEGIN
1374
1409
  });
1375
1410
  return {
1376
1411
  operations: S,
1377
- fromFingerprint: P(e.live),
1378
- toFingerprint: P(t),
1379
- summary: Gt(S)
1412
+ fromFingerprint: M(e.live),
1413
+ toFingerprint: M(t),
1414
+ summary: nn(S)
1380
1415
  };
1381
- }, qt = (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) => {
1382
1493
  let t = /[`"[]?([A-Za-z_][A-Za-z0-9_]*)[`"\]]?\s*(?:in\b|=)/i.exec(e);
1383
1494
  if (!t) return null;
1384
1495
  let n = [...e.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
@@ -1386,7 +1497,7 @@ BEGIN
1386
1497
  column: t[1],
1387
1498
  values: n
1388
1499
  };
1389
- }, Jt = (e, t) => {
1500
+ }, bn = (e, t) => {
1390
1501
  if (t === "vector") return "vector";
1391
1502
  switch (e) {
1392
1503
  case "text":
@@ -1413,7 +1524,7 @@ BEGIN
1413
1524
  case "USER-DEFINED": return "text";
1414
1525
  default: return "text";
1415
1526
  }
1416
- }, z = (e) => {
1527
+ }, R = (e) => {
1417
1528
  switch (e.toUpperCase()) {
1418
1529
  case "CASCADE": return "cascade";
1419
1530
  case "RESTRICT": return "restrict";
@@ -1421,25 +1532,25 @@ BEGIN
1421
1532
  case "SET DEFAULT": return "noAction";
1422
1533
  default: return "noAction";
1423
1534
  }
1424
- }, Yt = (e, t) => {
1535
+ }, xn = (e, t) => {
1425
1536
  let n = e !== void 0 && e !== "" ? Number(e) : NaN;
1426
1537
  return Number.isInteger(n) && n > 0 ? n : t;
1427
- }, Xt = () => Yt(process.env.VOLTRO_INTROSPECT_BATCH, 20), Zt = 300, Qt = () => {
1538
+ }, Sn = () => xn(process.env.VOLTRO_INTROSPECT_BATCH, 20), Cn = 300, wn = () => {
1428
1539
  let e = process.env.VOLTRO_INTROSPECT_TIMEOUT_MS;
1429
- return e !== void 0 && e.trim() === "0" ? 0 : Yt(e, 3e4);
1430
- }, $t = (e, t) => {
1540
+ return e !== void 0 && e.trim() === "0" ? 0 : xn(e, 3e4);
1541
+ }, Tn = (e, t) => {
1431
1542
  let n = Number.isFinite(t) && t >= 1 ? Math.floor(t) : 1, r = [];
1432
1543
  for (let t = 0; t < e.length; t += n) r.push(e.slice(t, t + n));
1433
1544
  return r;
1434
- }, en = (e) => {
1435
- let t = Qt(), n = p.gen(function* () {
1545
+ }, En = (e) => {
1546
+ let t = wn(), n = b.gen(function* () {
1436
1547
  t > 0 && (yield* e.unsafe(`SET LOCAL statement_timeout = ${t}`));
1437
- let n = (t) => e.unsafe("(" + t.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ") + ")"), r = (t) => e.unsafe(String(Math.max(0, Math.trunc(t)))), i = Xt(), 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* () {
1438
1549
  let r = [];
1439
- for (let a of $t(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)));
1440
1551
  return r;
1441
1552
  }), o = [];
1442
- for (let t = 0;; t += Zt) {
1553
+ for (let t = 0;; t += Cn) {
1443
1554
  let n = yield* e`
1444
1555
  SELECT c.relname AS table_name,
1445
1556
  COALESCE(c.reltuples::bigint, 0) AS row_count
@@ -1447,9 +1558,9 @@ BEGIN
1447
1558
  JOIN pg_namespace n ON n.oid = c.relnamespace
1448
1559
  WHERE n.nspname = current_schema() AND c.relkind = 'r'
1449
1560
  ORDER BY c.relname
1450
- LIMIT ${r(Zt)} OFFSET ${r(t)}
1561
+ LIMIT ${r(Cn)} OFFSET ${r(t)}
1451
1562
  `;
1452
- if (o.push(...n), n.length < Zt) break;
1563
+ if (o.push(...n), n.length < Cn) break;
1453
1564
  }
1454
1565
  let s = /* @__PURE__ */ new Set(), c = [];
1455
1566
  for (let e of o) s.has(e.table_name) || (s.add(e.table_name), c.push(e));
@@ -1476,7 +1587,7 @@ BEGIN
1476
1587
  JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
1477
1588
  JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = ANY (con.conkey)
1478
1589
  WHERE con.contype = 'u' AND nsp.nspname = current_schema() AND rel.relname IN ${t}
1479
- `), m = yield* a(l, (t) => e`
1590
+ `), p = yield* a(l, (t) => e`
1480
1591
  SELECT rel.relname AS table_name, att.attname AS column_name, pg_get_constraintdef(con.oid) AS def
1481
1592
  FROM pg_constraint con
1482
1593
  JOIN pg_class rel ON rel.oid = con.conrelid
@@ -1484,12 +1595,12 @@ BEGIN
1484
1595
  JOIN pg_attribute att ON att.attrelid = rel.oid AND att.attnum = con.conkey[1]
1485
1596
  WHERE con.contype = 'c' AND nsp.nspname = current_schema() AND array_length(con.conkey, 1) = 1
1486
1597
  AND rel.relname IN ${t}
1487
- `), h = /* @__PURE__ */ new Map();
1488
- for (let e of m) {
1598
+ `), m = /* @__PURE__ */ new Map();
1599
+ for (let e of p) {
1489
1600
  let t = [...e.def.matchAll(/'((?:[^']|'')*)'/g)].map((e) => e[1].replace(/''/g, "'"));
1490
- 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);
1491
1602
  }
1492
- let g = yield* a(l, (t) => e`
1603
+ let h = yield* a(l, (t) => e`
1493
1604
  SELECT
1494
1605
  rel.relname AS source_table,
1495
1606
  att.attname AS source_column,
@@ -1514,7 +1625,7 @@ BEGIN
1514
1625
  WHERE con.contype = 'f' AND nsp.nspname = current_schema()
1515
1626
  AND rel.relname IN ${t}
1516
1627
  ORDER BY rel.relname, con.conname, ck.ord
1517
- `), _ = yield* a(l, (t) => e`
1628
+ `), g = yield* a(l, (t) => e`
1518
1629
  WITH ix AS (
1519
1630
  SELECT
1520
1631
  c.relname AS table_name,
@@ -1535,31 +1646,31 @@ BEGIN
1535
1646
  AND c.relname IN ${t}
1536
1647
  )
1537
1648
  SELECT * FROM ix ORDER BY table_name, index_name, position
1538
- `), 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();
1539
1650
  for (let e of u) {
1540
- let t = v.get(e.table_name);
1541
- 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);
1542
1653
  }
1543
1654
  for (let e of d) {
1544
- let t = y.get(e.table_name);
1545
- 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);
1546
1657
  }
1547
- let C = /* @__PURE__ */ new Map();
1658
+ let ee = /* @__PURE__ */ new Map();
1548
1659
  for (let e of f) {
1549
- 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);
1550
1661
  n || (n = {
1551
1662
  table: e.table_name,
1552
1663
  columns: []
1553
- }, C.set(t, n)), n.columns.push({
1664
+ }, ee.set(t, n)), n.columns.push({
1554
1665
  name: e.column_name,
1555
1666
  position: e.position
1556
1667
  });
1557
1668
  }
1558
- for (let [e, t] of C) {
1669
+ for (let [e, t] of ee) {
1559
1670
  let n = [...t.columns].sort((e, t) => e.position - t.position).map((e) => e.name);
1560
1671
  if (n.length === 1) {
1561
- let e = b.get(t.table);
1562
- 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]);
1563
1674
  } else {
1564
1675
  let r = x.get(t.table);
1565
1676
  r || (r = [], x.set(t.table, r)), r.push({
@@ -1570,29 +1681,29 @@ BEGIN
1570
1681
  });
1571
1682
  }
1572
1683
  }
1573
- for (let e of g) {
1684
+ for (let e of h) {
1574
1685
  let t = S.get(e.source_table);
1575
1686
  t || (t = [], S.set(e.source_table, t)), t.push(e);
1576
1687
  }
1577
- for (let e of _) {
1578
- let t = ee.get(e.table_name);
1579
- 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));
1580
1691
  let n = t.get(e.index_name);
1581
1692
  n || (n = [], t.set(e.index_name, n)), n.push(e);
1582
1693
  }
1583
- let w = [];
1694
+ let te = [];
1584
1695
  for (let e of c) {
1585
- 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) => {
1586
- 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" : Jt(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 ? {
1587
1698
  table: a.target_table,
1588
1699
  column: a.target_column,
1589
- onDelete: z(a.delete_rule),
1590
- onUpdate: z(a.update_rule)
1591
- } : 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}`);
1592
1703
  return {
1593
1704
  name: t.column_name,
1594
1705
  type: s,
1595
- ...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) },
1596
1707
  nullable: t.is_nullable === "YES",
1597
1708
  unique: i.has(t.column_name) || n && t.column_name === "id",
1598
1709
  hasDefault: t.column_default !== null,
@@ -1600,7 +1711,7 @@ BEGIN
1600
1711
  ...c ? { references: c } : {},
1601
1712
  ...l ? { oneOf: l } : {}
1602
1713
  };
1603
- }), l = [], u = ee.get(e.table_name);
1714
+ }), l = [], u = C.get(e.table_name);
1604
1715
  if (u) for (let [t, n] of u) {
1605
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) => {
1606
1717
  let t = e.position;
@@ -1624,20 +1735,20 @@ BEGIN
1624
1735
  unique: !0
1625
1736
  });
1626
1737
  let d = Number(e.row_count ?? 0);
1627
- w.push({
1738
+ te.push({
1628
1739
  name: e.table_name,
1629
1740
  columns: c,
1630
1741
  indexes: l,
1631
1742
  ...d > 0 ? { rowCount: d } : {}
1632
1743
  });
1633
1744
  }
1634
- return { tables: w };
1745
+ return { tables: te };
1635
1746
  });
1636
- return (t > 0 ? e.withTransaction(n) : n).pipe(p.catchAll((e) => {
1747
+ return (t > 0 ? e.withTransaction(n) : n).pipe(b.catchAll((e) => {
1637
1748
  let n = `${String(e)} ${String(e.cause ?? "")}`.toLowerCase();
1638
- 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);
1639
1750
  }));
1640
- }, tn = (e, t) => {
1751
+ }, Dn = (e, t) => {
1641
1752
  switch (e.toLowerCase()) {
1642
1753
  case "varchar":
1643
1754
  case "char":
@@ -1669,21 +1780,21 @@ BEGIN
1669
1780
  case "json": return "json";
1670
1781
  default: return "text";
1671
1782
  }
1672
- }, B = (e) => e.map((e) => {
1783
+ }, z = (e) => e.map((e) => {
1673
1784
  let t = e, n = {};
1674
1785
  for (let e of Object.keys(t)) n[e.toLowerCase()] = t[e];
1675
1786
  return n;
1676
- }), nn = (e) => p.gen(function* () {
1677
- let t = B(yield* e`
1787
+ }), On = (e) => b.gen(function* () {
1788
+ let t = z(yield* e`
1678
1789
  SELECT table_name, COALESCE(table_rows, 0) AS table_rows
1679
1790
  FROM information_schema.tables
1680
1791
  WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
1681
- ORDER BY table_name`), n = B(yield* e`
1792
+ ORDER BY table_name`), n = z(yield* e`
1682
1793
  SELECT table_name, column_name, data_type, column_type, is_nullable, column_default, column_key,
1683
1794
  character_maximum_length
1684
1795
  FROM information_schema.columns
1685
1796
  WHERE table_schema = DATABASE()
1686
- ORDER BY table_name, ordinal_position`), r = B(yield* e`
1797
+ ORDER BY table_name, ordinal_position`), r = z(yield* e`
1687
1798
  SELECT k.table_name AS source_table,
1688
1799
  k.column_name AS source_column,
1689
1800
  k.referenced_table_name AS target_table,
@@ -1693,21 +1804,21 @@ BEGIN
1693
1804
  JOIN information_schema.referential_constraints r
1694
1805
  ON k.constraint_name = r.constraint_name
1695
1806
  AND k.table_schema = r.constraint_schema
1696
- 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`
1697
1808
  SELECT table_name, index_name, non_unique, column_name, seq_in_index
1698
1809
  FROM information_schema.statistics
1699
1810
  WHERE table_schema = DATABASE() AND index_name <> 'PRIMARY'
1700
- 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`
1701
1812
  SELECT table_name, check_clause
1702
1813
  FROM information_schema.check_constraints
1703
- 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();
1704
1815
  for (let e of a) {
1705
1816
  let t = /json_valid\(`?([^`)]+)`?\)/i.exec(e.check_clause);
1706
1817
  if (t) {
1707
1818
  o.add(`${e.table_name}.${t[1]}`);
1708
1819
  continue;
1709
1820
  }
1710
- let n = qt(e.check_clause);
1821
+ let n = yn(e.check_clause);
1711
1822
  n && s.set(`${e.table_name}.${n.column}`, n.values);
1712
1823
  }
1713
1824
  let c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
@@ -1730,17 +1841,17 @@ BEGIN
1730
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);
1731
1842
  if (f) for (let e of f.values()) e.length === 1 && e[0].non_unique === 0 && a.add(e[0].column_name);
1732
1843
  let p = t.map((t) => {
1733
- 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" : tn(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 ? {
1734
1845
  table: c.target_table,
1735
1846
  column: c.target_column,
1736
- onDelete: z(c.delete_rule),
1737
- onUpdate: z(c.update_rule)
1847
+ onDelete: R(c.delete_rule),
1848
+ onUpdate: R(c.update_rule)
1738
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}`);
1739
1850
  return {
1740
1851
  name: t.column_name,
1741
1852
  type: l,
1742
1853
  nullable: t.is_nullable === "YES",
1743
- ...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) },
1744
1855
  unique: d,
1745
1856
  hasDefault: f !== null,
1746
1857
  ...f === null ? {} : { defaultValue: f },
@@ -1772,10 +1883,10 @@ BEGIN
1772
1883
  });
1773
1884
  }
1774
1885
  return { tables: d };
1775
- }), rn = (e) => {
1886
+ }), kn = (e) => {
1776
1887
  let t = e.toUpperCase();
1777
1888
  return t.includes("INT") ? "integer" : t.includes("REAL") || t.includes("FLOA") || t.includes("DOUB") ? "real" : t.includes("BLOB") ? "bytes" : "text";
1778
- }, an = (e) => p.gen(function* () {
1889
+ }, An = (e) => b.gen(function* () {
1779
1890
  let t = yield* e`
1780
1891
  SELECT name, sql FROM sqlite_master
1781
1892
  WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
@@ -1792,11 +1903,11 @@ BEGIN
1792
1903
  d.set(t.name, r), t.unique === 1 && r.length === 1 && f.add(r[0]);
1793
1904
  }
1794
1905
  let p = o.map((e) => {
1795
- let n = u.length === 1 && u[0] === e.name, r = l.get(e.name), i = r ? "reference" : n && e.name === "id" ? "id" : rn(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 ? {
1796
1907
  table: r.table,
1797
1908
  column: r.to,
1798
- onDelete: z(r.on_delete),
1799
- onUpdate: z(r.on_update)
1909
+ onDelete: R(r.on_delete),
1910
+ onUpdate: R(r.on_update)
1800
1911
  } : void 0, o = t.get(e.name);
1801
1912
  return {
1802
1913
  name: e.name,
@@ -1831,7 +1942,7 @@ BEGIN
1831
1942
  });
1832
1943
  }
1833
1944
  return { tables: r };
1834
- }), on = (e) => {
1945
+ }), jn = (e) => {
1835
1946
  switch (e.toLowerCase()) {
1836
1947
  case "int":
1837
1948
  case "smallint":
@@ -1853,7 +1964,7 @@ BEGIN
1853
1964
  case "image": return "bytes";
1854
1965
  default: return "text";
1855
1966
  }
1856
- }, V = (e) => e === !0 || e === 1, sn = (e) => p.gen(function* () {
1967
+ }, B = (e) => e === !0 || e === 1, Mn = (e) => b.gen(function* () {
1857
1968
  let t = yield* e`
1858
1969
  SELECT TABLE_NAME AS table_name FROM INFORMATION_SCHEMA.TABLES
1859
1970
  WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME`, n = yield* e`
@@ -1910,21 +2021,21 @@ BEGIN
1910
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();
1911
2022
  if (a) for (let [, e] of a) {
1912
2023
  let t = [...e].sort((e, t) => e.key_ordinal - t.key_ordinal);
1913
- if (V(t[0].is_primary)) for (let e of t) d.push(e.column_name);
1914
- 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);
1915
2026
  }
1916
2027
  let p = t.map((t) => {
1917
- 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" : on(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 ? {
1918
2029
  table: i.target_table,
1919
2030
  column: i.target_column,
1920
- onDelete: cn(i.delete_rule),
1921
- onUpdate: cn(i.update_rule)
1922
- } : void 0, c = t.column_default === null ? null : ln(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}`);
1923
2034
  return {
1924
2035
  name: t.column_name,
1925
2036
  type: a,
1926
2037
  nullable: t.is_nullable === "YES",
1927
- ...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) },
1928
2039
  unique: f.has(t.column_name) || n && t.column_name === "id",
1929
2040
  hasDefault: c !== null,
1930
2041
  ...c === null ? {} : { defaultValue: c },
@@ -1934,11 +2045,11 @@ BEGIN
1934
2045
  }), m = [];
1935
2046
  if (a) for (let [t, n] of a) {
1936
2047
  let r = [...n].sort((e, t) => e.key_ordinal - t.key_ordinal);
1937
- 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({
1938
2049
  name: t,
1939
2050
  table: e,
1940
2051
  columns: r.map((e) => e.column_name),
1941
- unique: V(r[0].is_unique)
2052
+ unique: B(r[0].is_unique)
1942
2053
  });
1943
2054
  }
1944
2055
  d.length > 0 && m.push({
@@ -1953,51 +2064,54 @@ BEGIN
1953
2064
  });
1954
2065
  }
1955
2066
  return { tables: u };
1956
- }), cn = (e) => {
2067
+ }), Nn = (e) => {
1957
2068
  switch (e.toUpperCase().replace(/_/g, " ")) {
1958
2069
  case "CASCADE": return "cascade";
1959
2070
  case "SET NULL": return "setNull";
1960
2071
  case "SET DEFAULT": return "noAction";
1961
2072
  default: return "noAction";
1962
2073
  }
1963
- }, ln = (e) => {
2074
+ }, Pn = (e) => {
1964
2075
  let t = e.trim();
1965
2076
  for (; t.startsWith("(") && t.endsWith(")");) t = t.slice(1, -1).trim();
1966
2077
  return t.startsWith("N'") && t.endsWith("'") ? t = t.slice(2, -1) : t.startsWith("'") && t.endsWith("'") && (t = t.slice(1, -1)), t;
1967
- }, un = /* @__PURE__ */ new Set([
2078
+ }, Fn = /* @__PURE__ */ new Set([
1968
2079
  "character varying",
1969
2080
  "varchar",
1970
2081
  "nvarchar",
1971
2082
  "char",
1972
2083
  "character",
1973
2084
  "nchar"
1974
- ]), H = (e, t) => {
1975
- if (!un.has(e.toLowerCase())) return;
2085
+ ]), V = (e, t) => {
2086
+ if (!Fn.has(e.toLowerCase())) return;
1976
2087
  let n = Number(t);
1977
2088
  if (!(!Number.isFinite(n) || n <= 0)) return n;
1978
- }, U = (e) => e.onDialectOrElse({
1979
- mysql: () => nn(e),
1980
- mssql: () => sn(e),
1981
- sqlite: () => an(e),
1982
- orElse: () => en(e)
1983
- }), dn = (e, t) => e.onDialectOrElse({
1984
- mysql: () => p.succeed(t.replace("T", " ").replace("Z", "")),
1985
- orElse: () => p.succeed(t)
1986
- }), fn = (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) => {
1987
2101
  if (!(e.type === "text" && e.maxLength !== void 0) && !e.spatial && e.type !== "vector" && e.type !== "array" && e.type !== "enum") return;
1988
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;
1989
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.`);
1990
- return j(e, t);
1991
- }, pn = (e) => {
1992
- let t = W(e, "postgres");
1993
- return t === void 0 ? mn(e) : t;
1994
- }, mn = (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) => {
1995
2109
  switch (e.type) {
1996
2110
  case "id": return "text";
1997
2111
  case "text": return "text";
1998
2112
  case "integer": return "integer";
1999
2113
  case "real": return "double precision";
2000
- case "decimal": return fn(e, "NUMERIC");
2114
+ case "decimal": return Rn(e, "NUMERIC");
2001
2115
  case "bigint": return "bigint";
2002
2116
  case "boolean": return "boolean";
2003
2117
  case "timestamp": return "timestamptz";
@@ -2011,15 +2125,15 @@ BEGIN
2011
2125
  case "interval": return "interval";
2012
2126
  case "raw": return e.rawDdl ?? "text";
2013
2127
  }
2014
- }, hn = (e) => {
2015
- let t = W(e, "mariadb");
2128
+ }, Hn = (e) => {
2129
+ let t = zn(e, "mariadb");
2016
2130
  if (t !== void 0) return t;
2017
2131
  switch (e.type) {
2018
2132
  case "id": return "VARCHAR(64)";
2019
2133
  case "text": return e.hasDefault && e.defaultValue !== void 0 || e.oneOf && e.oneOf.length > 0 ? "VARCHAR(255)" : "LONGTEXT";
2020
2134
  case "integer": return "INT";
2021
2135
  case "real": return "DOUBLE";
2022
- case "decimal": return fn(e, "DECIMAL");
2136
+ case "decimal": return Rn(e, "DECIMAL");
2023
2137
  case "bigint": return "BIGINT";
2024
2138
  case "boolean": return "TINYINT(1)";
2025
2139
  case "timestamp": return "DATETIME(6)";
@@ -2033,82 +2147,82 @@ BEGIN
2033
2147
  case "interval": return "BIGINT";
2034
2148
  case "raw": return e.rawDdl ?? "LONGTEXT";
2035
2149
  }
2036
- }, G = (e) => `"${e}"`, K = (e) => `\`${e.replace(/`/g, "``")}\``, gn = (e, t) => `ALTER TABLE ${t(e.table)} DROP COLUMN IF EXISTS ${t(e.column)};`, _n = (e) => `ALTER TABLE ${K(e.table)} DROP COLUMN IF EXISTS ${K(e.column)}, ALGORITHM=COPY;`, vn = (e, t) => `ALTER TABLE ${t(e.table)} RENAME COLUMN ${t(e.from)} TO ${t(e.to)};`, yn = (e, t, n) => `ALTER TABLE ${n(e.table)} RENAME COLUMN ${n(e.column)} TO ${n(`${e.column}${Wn(t)}`)};`, bn = (e, t, n) => e.hasDefault && e.defaultValue !== void 0 ? `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} SET ${xn(e, n)};` : `ALTER TABLE ${t(e.table)} ALTER COLUMN ${t(e.column)} DROP DEFAULT;`, xn = (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) => {
2037
2151
  let n = t(e.defaultValue);
2038
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.`);
2039
2153
  return n;
2040
- }, q = (e, t, n) => e.find((e) => e.tableName === t)?.fields[n], Sn = (e, t, n) => {
2041
- let r = q(e, t, n);
2042
- return r ? ct(n, r) : void 0;
2043
- }, Cn = (e, t, n) => {
2044
- 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");
2045
2159
  return [
2046
- K(t),
2047
- j(e, "mariadb"),
2160
+ W(t),
2161
+ k(e, "mariadb"),
2048
2162
  r ? null : "NOT NULL",
2049
2163
  i
2050
2164
  ].filter(Boolean).join(" ");
2051
- }, wn = (e, t) => e == null ? null : Array.isArray(e) ? Tn(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, Tn = (e, t, n) => xe(e, t ?? { type: "json" }, n), En = (e, t) => e == null ? null : Array.isArray(e) ? Tn(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) => {
2052
2166
  if (!e.generatedAs) return null;
2053
2167
  let { expr: n, stored: r } = e.generatedAs;
2054
2168
  return t === "postgres" ? `GENERATED ALWAYS AS (${n}) STORED` : t === "mssql" ? `AS (${n})${r ? " PERSISTED" : ""}` : `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
2055
- }, Dn = (e) => {
2056
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "postgres");
2057
- let t = J(e, "postgres");
2058
- if (t) return `${G(e.name)} ${pn(e)} ${t}`;
2059
- let n = [G(e.name), pn(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)];
2060
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) {
2061
- let t = En(e.defaultValue, e);
2175
+ let t = $n(e.defaultValue, e);
2062
2176
  t && n.push(t);
2063
2177
  }
2064
- 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 ${{
2065
2179
  cascade: "CASCADE",
2066
2180
  restrict: "RESTRICT",
2067
2181
  setNull: "SET NULL",
2068
2182
  noAction: "NO ACTION"
2069
2183
  }[e.references.onDelete]}`)), n.join(" ");
2070
- }, On = (e, t) => e.primaryKey && e.primaryKey.length > 0 ? `,\n PRIMARY KEY (${e.primaryKey.map(t).join(", ")})` : "", kn = (e) => {
2071
- let t = e.columns.map((e) => ` ${Dn(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${t}${On(e, G)}\n)`];
2072
- 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(Yn).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(", ")})`);
2073
2187
  return n;
2074
- }, An = 191, jn = (e) => {
2075
- let t = hn(e).toUpperCase();
2188
+ }, rr = 191, ir = (e) => {
2189
+ let t = Hn(e).toUpperCase();
2076
2190
  return t.endsWith("TEXT") || t.endsWith("BLOB");
2077
- }, Mn = (e) => {
2078
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mariadb");
2079
- let t = J(e, "mariadb");
2080
- if (t) return `${K(e.name)} ${hn(e)} ${t}`;
2081
- let n = [K(e.name), hn(e)];
2082
- if (e.type === "id" && n.push("PRIMARY KEY"), e.nullable || n.push("NOT NULL"), e.unique && e.type !== "id" && !jn(e) && n.push("UNIQUE"), e.hasDefault) {
2083
- let t = wn(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);
2084
2198
  t && n.push(t);
2085
2199
  }
2086
- 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 ${{
2087
2201
  cascade: "CASCADE",
2088
2202
  restrict: "RESTRICT",
2089
2203
  setNull: "SET NULL",
2090
2204
  noAction: "NO ACTION"
2091
2205
  }[e.references.onDelete]}`)), n.join(" ");
2092
- }, Nn = (e) => {
2206
+ }, or = (e) => {
2093
2207
  let t = new Map(e.columns.map((e) => [e.name, e])), n = (e) => {
2094
- let n = t.get(e);
2095
- return n && jn(n) ? `${K(e)}(${An})` : K(e);
2096
- }, r = e.columns.map((e) => ` ${Mn(e)}`).join(",\n"), i = [`CREATE TABLE IF NOT EXISTS ${K(e.table)} (\n${r}${On(e, n)}\n)`];
2097
- for (let t of e.columns) t.unique && t.type !== "id" && jn(t) && i.push(`CREATE UNIQUE INDEX IF NOT EXISTS ${K(`${e.table}_${t.name}_key`)} ON ${K(e.table)} (${n(t.name)})`);
2098
- 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(", ")})`);
2099
2213
  return i;
2100
- }, Pn = {
2214
+ }, sr = {
2101
2215
  cascade: "CASCADE",
2102
2216
  restrict: "RESTRICT",
2103
2217
  setNull: "SET NULL",
2104
2218
  noAction: "NO ACTION"
2105
- }, Fn = {
2219
+ }, cr = {
2106
2220
  cascade: "CASCADE",
2107
2221
  restrict: "NO ACTION",
2108
2222
  setNull: "SET NULL",
2109
2223
  noAction: "NO ACTION"
2110
- }, In = (e) => {
2111
- let t = W(e, "sqlite");
2224
+ }, lr = (e) => {
2225
+ let t = zn(e, "sqlite");
2112
2226
  if (t !== void 0) return t;
2113
2227
  switch (e.type) {
2114
2228
  case "integer":
@@ -2119,60 +2233,60 @@ BEGIN
2119
2233
  case "bytes": return "BLOB";
2120
2234
  default: return "TEXT";
2121
2235
  }
2122
- }, 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, Ln = (e) => {
2123
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "sqlite");
2124
- let t = J(e, "sqlite");
2125
- if (t) return `${G(e.name)} ${In(e)} ${t}`;
2126
- let n = [G(e.name), In(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)];
2127
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) {
2128
- let t = Y(e.defaultValue, "CURRENT_TIMESTAMP");
2242
+ let t = q(e.defaultValue, "CURRENT_TIMESTAMP");
2129
2243
  t && n.push(t);
2130
2244
  }
2131
- if (e.references && (n.push(`REFERENCES ${G(e.references.table)} (${G(e.references.column)})`), e.references.onDelete && n.push(`ON DELETE ${Pn[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) {
2132
2246
  let t = e.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
2133
- n.push(`CHECK (${G(e.name)} IN (${t}))`);
2247
+ n.push(`CHECK (${U(e.name)} IN (${t}))`);
2134
2248
  }
2135
2249
  return n.join(" ");
2136
- }, Rn = (e) => {
2137
- let t = e.columns.map((e) => ` ${Ln(e)}`).join(",\n"), n = [`CREATE TABLE IF NOT EXISTS ${G(e.table)} (\n${t}${On(e, G)}\n)`];
2138
- 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(", ")})`);
2139
2253
  return n;
2140
- }, X = (e, t) => {
2254
+ }, J = (e, t) => {
2141
2255
  let n = t.find((t) => t.tableName === e);
2142
2256
  if (!n) return [];
2143
- let r = I([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => G(e.name)).join(", "), o = [
2144
- ...Rn({
2257
+ let r = P([n]).tables[0], i = `${e}__voltro_rebuild`, a = r.columns.map((e) => U(e.name)).join(", "), o = [
2258
+ ...dr({
2145
2259
  kind: "create-table",
2146
2260
  table: i,
2147
2261
  columns: r.columns,
2148
2262
  indexes: [],
2149
2263
  ...r.primaryKey ? { primaryKey: r.primaryKey } : {}
2150
2264
  }),
2151
- `INSERT INTO ${G(i)} (${a}) SELECT ${a} FROM ${G(e)}`,
2152
- `DROP TABLE ${G(e)}`,
2153
- `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)}`
2154
2268
  ];
2155
- 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(", ")})`);
2156
2270
  return o;
2157
- }, zn = (e, t, n, r) => {
2271
+ }, fr = (e, t, n, r) => {
2158
2272
  let i = r ?? e.nullable;
2159
- return `ALTER TABLE ${Z(n)} ALTER COLUMN ${Z(t)} ${j(e, "mssql")} ${i ? "NULL" : "NOT NULL"};`;
2160
- }, Bn = (e) => {
2161
- 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 + ']');`;
2162
2276
  if (!e.hasDefault) return t;
2163
- let n = Y(e.defaultValue, "SYSUTCDATETIME()");
2277
+ let n = q(e.defaultValue, "SYSUTCDATETIME()");
2164
2278
  if (!n) return t;
2165
2279
  let r = n.replace(/^DEFAULT\s+/i, "");
2166
- return `${t} ALTER TABLE ${Z(e.table)} ADD DEFAULT ${r} FOR ${Z(e.column)};`;
2167
- }, Z = (e) => `[${e.replace(/]/g, "]]")}]`, Vn = (e) => {
2168
- 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");
2169
2283
  if (t !== void 0) return t;
2170
2284
  switch (e.type) {
2171
2285
  case "id":
2172
2286
  case "reference": return "NVARCHAR(64)";
2173
2287
  case "integer": return "INT";
2174
2288
  case "real": return "FLOAT";
2175
- case "decimal": return fn(e, "DECIMAL");
2289
+ case "decimal": return Rn(e, "DECIMAL");
2176
2290
  case "bigint": return "BIGINT";
2177
2291
  case "boolean": return "BIT";
2178
2292
  case "timestamp": return "DATETIME2(6)";
@@ -2180,55 +2294,59 @@ BEGIN
2180
2294
  case "bytes": return "VARBINARY(MAX)";
2181
2295
  default: return "NVARCHAR(MAX)";
2182
2296
  }
2183
- }, Hn = (e, t) => {
2184
- if (e.type === "id" && e.idScheme?.kind === "numeric") return N(e.name, "mssql");
2185
- let n = e.type === "text" && t?.has(e.name) ? "NVARCHAR(450)" : Vn(e), r = J(e, "mssql");
2186
- if (r) return `${Z(e.name)} ${r}`;
2187
- 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];
2188
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) {
2189
- let t = Y(e.defaultValue, "SYSUTCDATETIME()");
2303
+ let t = q(e.defaultValue, "SYSUTCDATETIME()");
2190
2304
  t && i.push(t);
2191
2305
  }
2192
- return e.references && (i.push(`REFERENCES ${Z(e.references.table)} (${Z(e.references.column)})`), e.references.onDelete && i.push(`ON DELETE ${Fn[e.references.onDelete]}`)), i.join(" ");
2193
- }, Un = (e) => {
2194
- let t = (e) => e.replace(/'/g, "''"), n = new Set(e.indexes.flatMap((e) => e.columns)), r = e.columns.map((e) => ` ${Hn(e, n)}`).join(",\n"), i = `CREATE TABLE ${Z(e.table)} (\n${r}${On(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)}')`];
2195
2309
  for (let n of e.indexes) {
2196
2310
  if (n.name === `${e.table}_pkey`) continue;
2197
- 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(", ")})`;
2198
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)}')`);
2199
2313
  }
2200
2314
  return a;
2201
- }, Wn = (e) => `__dropped_${e.replace(/[-:T.Z]/g, "").slice(0, 14)}`, Gn = (e, t) => `DROP TABLE IF EXISTS ${t(e.table)};`, Kn = (e, t, n) => {
2202
- let r = `${e.table}${Wn(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)}`;
2203
2317
  return `ALTER TABLE ${n(e.table)} RENAME TO ${n(r)};`;
2204
- }, qn = (e, t, n) => {
2205
- let r = q(e, t, n);
2206
- return r ? jn({ type: r.type }) : !1;
2207
- }, Jn = (e, t, n) => qn(e, t, n) ? `${K(n)}(${An})` : K(n), Yn = (e) => e.startsWith("(") ? e : G(e), Xn = (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(Yn).join(", ")});`, Zn = (e, t) => `CREATE ${e.index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${K(e.index.name)} ON ${K(e.table)} (${e.index.columns.map((n) => Jn(t, e.table, n)).join(", ")});`, Qn = (e) => `DROP INDEX IF EXISTS ${G(e.index)};`, $n = (e) => `DROP INDEX ${K(e.index)} ON ${K(e.table)};`, er = (e) => `DROP INDEX IF EXISTS ${Z(e.index)} ON ${Z(e.table)};`, tr = (e, t) => `ALTER TABLE ${t(e.from)} RENAME TO ${t(e.to)};`, nr = (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) => {
2208
2326
  let t = (e) => e.replace(/'/g, "''");
2209
2327
  return `EXEC sp_rename N'${t(e.from)}', N'${t(e.to)}';`;
2210
- }, rr = (e) => `ALTER INDEX ${G(e.from)} RENAME TO ${G(e.to)};`, ir = (e) => `ALTER TABLE ${K(e.table)} RENAME INDEX ${K(e.from)} TO ${K(e.to)};`, ar = (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) => {
2211
2329
  let t = (e) => e.replace(/'/g, "''");
2212
2330
  return `EXEC sp_rename N'${t(e.table)}.${t(e.from)}', N'${t(e.to)}', N'INDEX';`;
2213
- }, or = (e) => {
2214
- 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(", ")})`;
2215
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)}');`;
2216
- }, sr = (e, t, n) => {
2334
+ }, Pr = (e, t, n) => {
2217
2335
  let r = n.find((t) => t.tableName === e);
2218
2336
  if (!r) return [];
2219
2337
  let i = [];
2220
2338
  for (let n of t) {
2221
2339
  let t = r.fields[n];
2222
- 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"};`);
2223
2341
  }
2224
2342
  return i;
2225
- }, cr = (e) => `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(`${e.table}_${e.column}_key`)} UNIQUE (${G(e.column)});`, lr = (e, t) => `ALTER TABLE ${K(e.table)} ADD CONSTRAINT ${K(`${e.table}_${e.column}_key`)} UNIQUE (${Jn(t, e.table, e.column)});`, ur = (e) => {
2226
- let t = e.fields.map((e) => G(e)).join(", ");
2227
- return `ALTER TABLE ${G(e.table)} ADD CONSTRAINT ${G(e.name)} UNIQUE (${t});`;
2228
- }, dr = (e, t) => {
2229
- let n = e.fields.map((n) => Jn(t, e.table, n)).join(", ");
2230
- return `ALTER TABLE ${K(e.table)} ADD CONSTRAINT ${K(e.name)} UNIQUE (${n});`;
2231
- }, fr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(e.name)};`, pr = (e) => `ALTER TABLE ${K(e.table)} DROP INDEX ${K(e.name)};`, mr = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_key`)};`, hr = (e) => `ALTER TABLE ${K(e.table)} DROP INDEX ${K(`${e.table}_${e.column}_key`)};`, gr = (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) => {
2232
2350
  let n = [], r = t(e.table), i = t(e.column), a = t(e.targetTable), o = t(e.targetColumn);
2233
2351
  if (e.orphanPolicy) {
2234
2352
  let t = `${i} IS NOT NULL AND ${i} NOT IN (SELECT ${o} FROM ${a})`;
@@ -2246,11 +2364,11 @@ BEGIN
2246
2364
  setNull: "SET NULL",
2247
2365
  noAction: "NO ACTION"
2248
2366
  }[e.onDelete]}`), n.push(s.join(" ") + ";"), n;
2249
- }, _r = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_fkey`)};`, vr = (e) => `ALTER TABLE ${K(e.table)} DROP FOREIGN KEY ${K(`${e.table}_${e.column}_fkey`)};`, yr = (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) => {
2250
2368
  let n = e.values.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
2251
2369
  return `ALTER TABLE ${t(e.table)} ADD CONSTRAINT ${t(`${e.table}_${e.column}_check`)} CHECK (${t(e.column)} IN (${n}));`;
2252
- }, br = (e) => `ALTER TABLE ${G(e.table)} DROP CONSTRAINT IF EXISTS ${G(`${e.table}_${e.column}_check`)};`, xr = (e) => `ALTER TABLE ${K(e.table)} DROP CHECK ${K(`${e.table}_${e.column}_check`)};`, Sr = (e, t, n, r) => {
2253
- let i = e.column, a = r === "mariadb", o = a ? K : r === "mssql" ? Z : G, s = a ? hn(i) : r === "mssql" ? Vn(i) : r === "sqlite" ? In(i) : pn(i), c = r === "mssql" ? "ADD" : "ADD COLUMN", l = (e) => a ? wn(e, i) : r === "mssql" ? Y(e, "SYSUTCDATETIME()") : r === "sqlite" ? Y(e, "CURRENT_TIMESTAMP") : En(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);
2254
2372
  if (u) {
2255
2373
  let t = r === "mssql" ? `${o(i.name)} ${u}` : `${o(i.name)} ${s} ${u}`;
2256
2374
  return {
@@ -2278,7 +2396,7 @@ BEGIN
2278
2396
  post: f ? [f] : []
2279
2397
  };
2280
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;
2281
- }, Cr = (e) => {
2399
+ }, Xr = (e) => {
2282
2400
  let t = new Map(e.map((e) => [e.tableName, e]));
2283
2401
  return (e, n) => {
2284
2402
  let r = t.get(e);
@@ -2298,297 +2416,416 @@ BEGIN
2298
2416
  ...o
2299
2417
  };
2300
2418
  };
2301
- }, Q = (e, t, n) => e.unsafe(t).pipe(p.tapError((e) => p.sync(() => {
2302
- process.stderr.write(b(`applier: statement failed (op=${n})`, t, e, 400));
2303
- })), p.mapError((e) => {
2304
- 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();
2305
2423
  return Object.assign(e, { message: r });
2306
- }), p.map(() => void 0)), $ = (e, t, n) => p.forEach(t, (t) => Q(e, t, n), { discard: !0 }), wr = (e, t, n) => p.gen(function* () {
2307
- let r = process.hrtime.bigint(), i = Cr(n.declared), a = t.op;
2308
- 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) {
2309
2496
  case "create-table": {
2310
2497
  let t = yield* e.onDialectOrElse({
2311
- mysql: () => p.succeed(Nn(a)),
2312
- mssql: () => p.succeed(Un(a)),
2313
- sqlite: () => p.succeed(Rn(a)),
2314
- orElse: () => p.succeed(kn(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))
2315
2502
  });
2316
- for (let n of t) yield* Q(e, n, a.kind);
2503
+ for (let n of t) yield* X(e, n, o.kind);
2317
2504
  break;
2318
2505
  }
2319
2506
  case "drop-table": {
2320
- let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString(), r = (e) => t ? Kn(a, n, e) : Gn(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);
2321
2508
  yield* e.onDialectOrElse({
2322
- mysql: () => Q(e, r(K), a.kind),
2323
- orElse: () => Q(e, r(G), a.kind)
2509
+ mysql: () => X(e, r(W), o.kind),
2510
+ orElse: () => X(e, r(U), o.kind)
2324
2511
  });
2325
2512
  break;
2326
2513
  }
2327
2514
  case "drop-column": {
2328
2515
  let t = process.env.VOLTRO_SOFT_DROP === "1", n = (/* @__PURE__ */ new Date()).toISOString();
2329
2516
  yield* e.onDialectOrElse({
2330
- mysql: () => Q(e, t ? yn(a, n, K) : _n(a), a.kind),
2331
- orElse: () => Q(e, t ? yn(a, n, G) : gn(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)
2332
2519
  });
2333
2520
  break;
2334
2521
  }
2335
2522
  case "rename-column":
2336
2523
  yield* e.onDialectOrElse({
2337
- mysql: () => Q(e, vn(a, K), a.kind),
2338
- mssql: () => Q(e, `EXEC sp_rename N'${a.table.replace(/'/g, "''")}.${a.from.replace(/'/g, "''")}', N'${a.to.replace(/'/g, "''")}', 'COLUMN';`, a.kind),
2339
- orElse: () => Q(e, vn(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)
2340
2527
  });
2341
2528
  break;
2342
2529
  case "add-index":
2343
2530
  yield* e.onDialectOrElse({
2344
- mysql: () => Q(e, Zn(a, n.declared), a.kind),
2345
- mssql: () => $(e, [...sr(a.table, a.index.columns, n.declared), or(a)], a.kind),
2346
- orElse: () => Q(e, Xn(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)
2347
2534
  });
2348
2535
  break;
2349
2536
  case "drop-index":
2350
2537
  yield* e.onDialectOrElse({
2351
- mysql: () => Q(e, $n(a), a.kind),
2352
- mssql: () => Q(e, er(a), a.kind),
2353
- orElse: () => Q(e, Qn(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)
2354
2541
  });
2355
2542
  break;
2356
2543
  case "rename-table":
2357
2544
  yield* e.onDialectOrElse({
2358
- mysql: () => Q(e, tr(a, K), a.kind),
2359
- mssql: () => Q(e, nr(a), a.kind),
2360
- orElse: () => Q(e, tr(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)
2361
2548
  });
2362
2549
  break;
2363
2550
  case "rename-index":
2364
2551
  yield* e.onDialectOrElse({
2365
- mysql: () => Q(e, ir(a), a.kind),
2366
- mssql: () => Q(e, ar(a), a.kind),
2367
- orElse: () => Q(e, rr(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)
2368
2555
  });
2369
2556
  break;
2370
2557
  case "add-unique":
2371
2558
  yield* e.onDialectOrElse({
2372
- mysql: () => Q(e, lr(a, n.declared), a.kind),
2373
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2374
- mssql: () => $(e, [...sr(a.table, [a.column], n.declared), cr(a)], a.kind),
2375
- orElse: () => Q(e, cr(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)
2376
2563
  });
2377
2564
  break;
2378
2565
  case "drop-unique":
2379
2566
  yield* e.onDialectOrElse({
2380
- mysql: () => Q(e, hr(a), a.kind),
2381
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2382
- orElse: () => Q(e, mr(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)
2383
2570
  });
2384
2571
  break;
2385
2572
  case "add-unique-composite":
2386
2573
  yield* e.onDialectOrElse({
2387
- mysql: () => Q(e, dr(a, n.declared), a.kind),
2388
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2389
- mssql: () => $(e, [...sr(a.table, a.fields, n.declared), ur(a)], a.kind),
2390
- orElse: () => Q(e, ur(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)
2391
2578
  });
2392
2579
  break;
2393
2580
  case "drop-unique-composite":
2394
2581
  yield* e.onDialectOrElse({
2395
- mysql: () => Q(e, pr(a), a.kind),
2396
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2397
- orElse: () => Q(e, fr(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)
2398
2585
  });
2399
2586
  break;
2400
2587
  case "add-foreign-key":
2401
2588
  yield* e.onDialectOrElse({
2402
- mysql: () => $(e, gr(a, K), a.kind),
2403
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2404
- orElse: () => $(e, gr(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)
2405
2592
  });
2406
2593
  break;
2407
2594
  case "add-column": {
2408
- let n = Sr(a, t, i, yield* e.onDialectOrElse({
2409
- mysql: () => p.succeed("mariadb"),
2410
- mssql: () => p.succeed("mssql"),
2411
- sqlite: () => p.succeed("sqlite"),
2412
- 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")
2413
2600
  }));
2414
- for (let t of n.pre) yield* Q(e, t, a.kind);
2415
- 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}`;
2416
2603
  else {
2417
- 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;
2418
2605
  for (;;) {
2419
- 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)}`;
2420
2607
  if (s.length === 0) break;
2421
2608
  let c = s.map((e) => e.id);
2422
- 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({
2423
2610
  scope: "voltro:migrate",
2424
2611
  mode: "sql-batched"
2425
- })), 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`));
2426
2613
  }
2427
2614
  }
2428
2615
  else if (n.backfillKind === "js" && n.backfillJs) {
2429
- 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;
2430
2617
  for (;;) {
2431
- 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)}`;
2432
2619
  if (s.length === 0) break;
2433
2620
  for (let t of s) {
2434
- let r = yield* p.tryPromise({
2621
+ let r = yield* b.tryPromise({
2435
2622
  try: () => Promise.resolve(n.backfillJs({ id: t.id })),
2436
2623
  catch: (e) => e
2437
- }).pipe(p.orDie);
2438
- 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}`;
2439
2626
  }
2440
- 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({
2441
2628
  scope: "voltro:migrate",
2442
- table: a.table,
2443
- column: a.column.name
2444
- })), 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`));
2445
2632
  }
2446
2633
  }
2447
- 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);
2448
2635
  break;
2449
2636
  }
2450
2637
  case "alter-column-nullability":
2451
2638
  yield* e.onDialectOrElse({
2452
2639
  mysql: () => {
2453
- let t = q(n.declared, a.table, a.column);
2454
- return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${Cn(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.`));
2455
2642
  },
2456
2643
  mssql: () => {
2457
- let t = q(n.declared, a.table, a.column);
2458
- return t ? Q(e, zn(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}'.`));
2459
2646
  },
2460
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2461
- 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)
2462
2649
  });
2463
2650
  break;
2464
2651
  case "alter-column-default": {
2465
- let t = Sn(n.declared, a.table, a.column);
2652
+ let t = Yn(n.declared, o.table, o.column);
2466
2653
  yield* e.onDialectOrElse({
2467
- mysql: () => Q(e, bn(a, K, (e) => wn(e, t)), a.kind),
2468
- mssql: () => Q(e, Bn(a), a.kind),
2469
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2470
- orElse: () => Q(e, bn(a, G, (e) => En(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)
2471
2658
  });
2472
2659
  break;
2473
2660
  }
2474
2661
  case "alter-column-type": {
2475
- let r = pn(Sn(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}` : "";
2476
2663
  if (t.classification !== "online-required") {
2477
2664
  yield* e.onDialectOrElse({
2478
2665
  mysql: () => {
2479
- let t = q(n.declared, a.table, a.column);
2480
- return t ? Q(e, `ALTER TABLE ${K(a.table)} MODIFY COLUMN ${Cn(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.`));
2481
2668
  },
2482
2669
  mssql: () => {
2483
- let t = q(n.declared, a.table, a.column);
2484
- return t ? Q(e, zn(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}'.`));
2485
2672
  },
2486
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2487
- 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)
2488
2675
  });
2489
2676
  break;
2490
2677
  }
2491
2678
  yield* e.onDialectOrElse({
2492
- 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.`)),
2493
- 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
2494
2681
  });
2495
- let o = `${a.column}__shadow`, s = `${a.column}__old`;
2496
- yield* e.unsafe(`ALTER TABLE ${G(a.table)} ADD COLUMN ${G(o)} ${r}`);
2497
- 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;
2498
2685
  for (;;) {
2499
- 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)}`;
2500
2687
  if (t.length === 0) break;
2501
- 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;
2502
2689
  }
2503
- 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)}`);
2504
2691
  break;
2505
2692
  }
2506
2693
  case "drop-foreign-key":
2507
2694
  yield* e.onDialectOrElse({
2508
- mysql: () => Q(e, vr(a), a.kind),
2509
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2510
- orElse: () => Q(e, _r(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)
2511
2698
  });
2512
2699
  break;
2513
2700
  case "add-check":
2514
2701
  yield* e.onDialectOrElse({
2515
- mysql: () => Q(e, yr(a, K), a.kind),
2516
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2517
- orElse: () => Q(e, yr(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)
2518
2705
  });
2519
2706
  break;
2520
2707
  case "drop-check":
2521
2708
  yield* e.onDialectOrElse({
2522
- mysql: () => Q(e, xr(a), a.kind),
2523
- sqlite: () => $(e, X(a.table, n.declared), a.kind),
2524
- orElse: () => Q(e, br(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)
2525
2712
  });
2526
2713
  break;
2527
2714
  }
2528
2715
  return {
2529
- op: a,
2716
+ op: o,
2530
2717
  status: "applied",
2531
- durationMs: Number((process.hrtime.bigint() - r) / 1000000n)
2718
+ durationMs: Number((process.hrtime.bigint() - i) / 1000000n)
2532
2719
  };
2533
- }), Tr = (e, t, n) => p.gen(function* () {
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"
2733
+ };
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* () {
2534
2754
  let r = t.operations.filter((e) => e.blocked);
2535
- 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.`));
2536
- yield* E(e);
2537
- 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";
2538
2761
  try {
2539
- let r = yield* e.onDialectOrElse({
2540
- pg: () => p.succeed(!0),
2541
- mssql: () => p.succeed(!0),
2542
- orElse: () => p.succeed(!1)
2543
- }), i = r ? t.operations.filter((e) => e.classification !== "online-required") : [], o = r ? t.operations.filter((e) => e.classification === "online-required") : t.operations;
2544
- i.length > 0 && (yield* e.withTransaction(p.gen(function* () {
2545
- let e = yield* h.SqlClient;
2546
- for (let t of i) a.push(yield* wr(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));
2547
2771
  })));
2548
- for (let t of o) a.push(yield* wr(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
+ }
2549
2777
  } finally {
2550
- yield* D(e).pipe(p.orDie);
2778
+ yield* E(e).pipe(b.orDie);
2551
2779
  }
2552
- let o = yield* n.replan(e);
2553
- if (o.operations.length > 0) {
2554
- 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");
2555
- 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.`));
2556
2784
  }
2557
- 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* dn(e, u);
2558
- 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`
2559
2787
  INSERT INTO ${e("_voltro_migration_plans")}
2560
2788
  (${e("id")}, ${e("fingerprint")}, ${e("liveFingerprint")}, ${e("operations")},
2561
2789
  ${e("appliedBy")}, ${e("environment")}, ${e("source")},
2562
2790
  ${e("durationMs")}, ${e("appliedAt")}, ${e("notes")})
2563
2791
  VALUES
2564
- (${l}, ${t.toFingerprint}, ${o.fromFingerprint}, ${JSON.stringify(t.operations)},
2792
+ (${_}, ${t.toFingerprint}, ${m.fromFingerprint}, ${JSON.stringify(x)},
2565
2793
  ${n.appliedBy}, ${n.environment}, ${n.source},
2566
- ${c}, ${d}, ${n.notes ?? null})
2567
- `, {
2568
- id: l,
2569
- 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,
2570
2800
  fingerprint: t.toFingerprint,
2571
- operations: t.operations,
2801
+ operations: x,
2572
2802
  appliedBy: n.appliedBy,
2573
2803
  environment: n.environment,
2574
- durationMs: c,
2575
- appliedOps: s,
2804
+ durationMs: g,
2805
+ appliedOps: h,
2576
2806
  source: n.source,
2577
2807
  ...n.notes ? { notes: n.notes } : {}
2578
2808
  };
2579
- }), Er = (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"), Dr = (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`
2580
2810
  SELECT fingerprint
2581
2811
  FROM _voltro_migration_plans
2582
2812
  ORDER BY ${e("appliedAt")} DESC
2583
2813
  LIMIT 1
2584
- `.pipe(p.map((e) => e.length > 0 ? e[0].fingerprint : void 0), p.catchAll(() => p.succeed(void 0))), Or = (e) => e.onDialectOrElse({
2585
- pg: () => "postgres",
2586
- mysql: () => "mysql",
2587
- mssql: () => "mssql",
2588
- sqlite: () => "sqlite",
2589
- orElse: () => "sqlite"
2590
- }), kr = (e, t) => p.gen(function* () {
2591
- let n = P(I(t, Or(e))), r = yield* Dr(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);
2592
2829
  return r === n ? {
2593
2830
  kind: "up-to-date",
2594
2831
  fingerprint: n
@@ -2597,23 +2834,23 @@ BEGIN
2597
2834
  expected: n,
2598
2835
  ...r === void 0 ? {} : { actual: r }
2599
2836
  };
2600
- }), Ar = [
2837
+ }), di = [
2601
2838
  "pgeagertest_",
2602
2839
  "mig_e2e_",
2603
2840
  "mig_test_",
2604
2841
  "mysqleagertest_",
2605
2842
  "mssqleagertest_",
2606
2843
  "sqliteeagertest_"
2607
- ], jr = (e) => Ar.some((t) => e.startsWith(t)), Mr = (e, t) => p.gen(function* () {
2844
+ ], fi = (e) => di.some((t) => e.startsWith(t)), pi = (e, t) => b.gen(function* () {
2608
2845
  if (process.env.VOLTRO_TEST_FIXTURE_GC === "off") return [];
2609
- let n = t.filter((e) => jr(e.name)).map((e) => e.name);
2846
+ let n = t.filter((e) => fi(e.name)).map((e) => e.name);
2610
2847
  if (n.length === 0) return [];
2611
- 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));
2612
2849
  return n;
2613
- }), Nr = () => {
2850
+ }), mi = () => {
2614
2851
  let e = (process.env.VOLTRO_DB_IGNORE_TABLES ?? "").split(",").map((e) => e.trim()).filter((e) => e.length > 0);
2615
2852
  return e.length > 0 ? { ignoreTables: e } : {};
2616
- }, Pr = (e) => {
2853
+ }, hi = (e) => {
2617
2854
  if (e === void 0) return;
2618
2855
  let t = e.trim();
2619
2856
  if (t === "") return;
@@ -2623,83 +2860,87 @@ BEGIN
2623
2860
  kind: "tables",
2624
2861
  tables: new Set(n)
2625
2862
  } : void 0;
2626
- }, Fr = (e) => typeof e.table == "string" ? e.table : void 0, Ir = (e, t = { kind: "all" }) => ({
2863
+ }, gi = (e) => typeof e.table == "string" ? e.table : void 0, _i = (e, t = { kind: "all" }) => ({
2627
2864
  ...e,
2628
2865
  operations: e.operations.map((e) => {
2629
2866
  if (!e.blocked || e.classification !== "lossy") return e;
2630
2867
  if (t?.kind === "tables") {
2631
- let n = Fr(e.op);
2868
+ let n = gi(e.op);
2632
2869
  if (n === void 0 || !t.tables.has(n)) return e;
2633
2870
  }
2634
2871
  let { blocked: n, ...r } = e;
2635
2872
  return r;
2636
2873
  })
2637
- }), Lr = (e, t, n) => t === "postgres" ? ft(e, n).pipe(p.flatMap((e) => {
2638
- let t = pt(e);
2639
- return t === void 0 ? p.void : p.logWarning(`auto-migrate: ${t}`).pipe(p.annotateLogs({ scope: "voltro:migrate" }));
2640
- }), p.catchAll(() => p.void)) : p.void, Rr = (e, t, n) => p.gen(function* () {
2641
- let r = Or(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;
2642
2879
  if (process.env.VOLTRO_MIGRATE_FORCE !== "1") {
2643
- let t = P(I(i, r));
2644
- if ((yield* Dr(e)) === t) return yield* Lr(e, r, i), {
2880
+ let t = M(P(i, r));
2881
+ if ((yield* ci(e)) === t) return yield* vi(e, r, i), {
2645
2882
  kind: "up-to-date",
2646
2883
  fingerprint: t
2647
2884
  };
2648
2885
  }
2649
- let a = yield* U(e), o = yield* Mr(e, a.tables);
2650
- 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({
2651
2888
  scope: "voltro:migrate",
2652
2889
  count: o.length
2653
- })), a = yield* U(e));
2654
- let c = new Set(t.map((e) => e.tableName)), l = (e) => e.filter((e) => !s(e.name) || c.has(e.name)), u = Kt({
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({
2655
2892
  declared: i,
2656
- live: { tables: l(a.tables) },
2893
+ live: { tables: c(a.tables) },
2657
2894
  dialect: r,
2658
- ...Nr()
2895
+ ...mi()
2659
2896
  });
2660
- if (u.summary.blocked > 0) {
2661
- let e = u.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy"), t = Pr(process.env.VOLTRO_DESTRUCTIVE_OK);
2662
- if (t === void 0 || !e || (u = Ir(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 {
2663
2900
  kind: "refused-blocked",
2664
- plan: u
2901
+ plan: l
2665
2902
  };
2666
2903
  }
2667
- 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
+ }), {
2668
2909
  kind: "up-to-date",
2669
- fingerprint: u.toFingerprint
2670
- } : {
2910
+ fingerprint: l.toFingerprint
2911
+ }) : {
2671
2912
  kind: "applied",
2672
- applied: yield* Tr(e, u, {
2913
+ applied: yield* oi(e, l, {
2673
2914
  declared: t,
2674
2915
  appliedBy: n.appliedBy,
2675
2916
  environment: n.environment === "prod" ? "dev" : n.environment,
2676
2917
  source: "auto-diff",
2677
- replan: (e) => p.gen(function* () {
2678
- let t = yield* U(e);
2679
- return Kt({
2918
+ replan: (e) => b.gen(function* () {
2919
+ let t = yield* Ln(e);
2920
+ return rn({
2680
2921
  declared: i,
2681
- live: { tables: l(t.tables) },
2922
+ live: { tables: c(t.tables) },
2682
2923
  dialect: r,
2683
- ...Nr()
2924
+ ...mi()
2684
2925
  });
2685
2926
  })
2686
2927
  }),
2687
- plan: u
2928
+ plan: l
2688
2929
  };
2689
- }), zr = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? p.succeed({
2930
+ }), bi = (e, t, n) => process.env.VOLTRO_AUTO_MIGRATE === "0" ? b.succeed({
2690
2931
  kind: "skipped",
2691
2932
  reason: "VOLTRO_AUTO_MIGRATE=0"
2692
- }) : n.environment === "prod" ? kr(e, t) : Rr(e, t, n), Br = (e, t) => {
2933
+ }) : n.environment === "prod" ? ui(e, t) : yi(e, t, n), xi = (e, t) => {
2693
2934
  switch (e.kind) {
2694
2935
  case "skipped": return `auto-migrate: skipped (${e.reason})`;
2695
- 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)})`;
2696
2937
  case "applied": {
2697
2938
  let t = e.plan.summary;
2698
- 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)}`;
2699
2940
  }
2700
- 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.";
2701
2942
  case "refused-blocked": {
2702
- 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` + Er(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 = [""];
2703
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) {
2704
2945
  let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
2705
2946
  for (let r of e.plan.operations) {
@@ -2716,7 +2957,7 @@ BEGIN
2716
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");
2717
2958
  }
2718
2959
  }
2719
- }, Vr = /* @__PURE__ */ new Set([
2960
+ }, Si = /* @__PURE__ */ new Set([
2720
2961
  "node_modules",
2721
2962
  "dist",
2722
2963
  "build",
@@ -2725,24 +2966,24 @@ BEGIN
2725
2966
  ".next",
2726
2967
  ".git",
2727
2968
  ".framework"
2728
- ]), Hr = async (e) => {
2969
+ ]), Ci = async (e) => {
2729
2970
  let t = [], n = async (e) => {
2730
- let r = await _.readdir(e, { withFileTypes: !0 }).catch(() => []);
2971
+ let r = await ee.readdir(e, { withFileTypes: !0 }).catch(() => []);
2731
2972
  for (let i of r) {
2732
- if (Vr.has(i.name)) continue;
2733
- let r = v(e, i.name);
2734
- 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);
2735
2976
  }
2736
2977
  };
2737
2978
  return await n(e), t.sort((e, t) => {
2738
2979
  let n = e.split("/").pop() ?? e, r = t.split("/").pop() ?? t;
2739
2980
  return n.localeCompare(r);
2740
2981
  });
2741
- }, Ur = async (e) => {
2982
+ }, wi = async (e) => {
2742
2983
  let t = [];
2743
2984
  for (let n of e) {
2744
- let e = (await import(y(n).href)).default;
2745
- if (!r(e)) {
2985
+ let e = (await import(ne(n).href)).default;
2986
+ if (!o(e)) {
2746
2987
  process.stderr.write(`[voltro:migrate] ${n} has no default-exported \`migration(...)\` — skipped\n`);
2747
2988
  continue;
2748
2989
  }
@@ -2752,11 +2993,11 @@ BEGIN
2752
2993
  });
2753
2994
  }
2754
2995
  return t;
2755
- }, Wr = (e) => e`
2996
+ }, Ti = (e) => e`
2756
2997
  SELECT ${e("id")}
2757
2998
  FROM ${e("_voltro_migration_plans")}
2758
2999
  WHERE ${e("source")} = 'file'
2759
- `.pipe(p.map((e) => e.map((e) => e.id)), p.catchAll(() => p.succeed([]))), Gr = class extends Error {
3000
+ `.pipe(b.map((e) => e.map((e) => e.id)), b.catchAll(() => b.succeed([]))), Ei = class extends Error {
2760
3001
  migrationId;
2761
3002
  file;
2762
3003
  cause;
@@ -2764,28 +3005,28 @@ BEGIN
2764
3005
  constructor(e, t, n) {
2765
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";
2766
3007
  }
2767
- }, Kr = (e, t, n, r) => p.gen(function* () {
3008
+ }, Di = (e, t, n, r) => b.gen(function* () {
2768
3009
  let i = Date.now(), a = (/* @__PURE__ */ new Date()).toISOString(), o = {
2769
3010
  sql: e,
2770
3011
  log: {
2771
- info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
3012
+ info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
2772
3013
  scope: `migrations:file:${t.migration.id}`,
2773
3014
  ...n ?? {}
2774
3015
  })),
2775
- warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
3016
+ warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
2776
3017
  scope: `migrations:file:${t.migration.id}`,
2777
3018
  ...n ?? {}
2778
3019
  }))
2779
3020
  },
2780
3021
  appliedAt: a
2781
3022
  }, s = t.migration.up(o);
2782
- p.isEffect(s) ? yield* s : yield* p.tryPromise({
3023
+ b.isEffect(s) ? yield* s : yield* b.tryPromise({
2783
3024
  try: () => s,
2784
3025
  catch: (e) => e
2785
3026
  });
2786
3027
  let c = Date.now() - i;
2787
- !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}` })));
2788
- let l = yield* dn(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);
2789
3030
  return yield* e`
2790
3031
  INSERT INTO ${e("_voltro_migration_plans")}
2791
3032
  (${e("id")}, ${e("fingerprint")}, ${e("operations")},
@@ -2798,94 +3039,94 @@ BEGIN
2798
3039
  }])},
2799
3040
  ${r}, ${n}, ${"file"},
2800
3041
  ${c}, ${l}, ${t.migration.description})
2801
- `.pipe(p.mapError((e) => new Gr(t.migration.id, t.file, e))), { durationMs: c };
2802
- }), qr = (e, t) => p.gen(function* () {
2803
- let n = yield* p.tryPromise({
2804
- try: () => Hr(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")),
2805
3046
  catch: (e) => e
2806
3047
  });
2807
3048
  if (n.length === 0) return {
2808
3049
  pending: [],
2809
3050
  skipped: []
2810
3051
  };
2811
- let r = yield* p.tryPromise({
2812
- try: () => Ur(n),
3052
+ let r = yield* b.tryPromise({
3053
+ try: () => wi(n),
2813
3054
  catch: (e) => e
2814
3055
  });
2815
3056
  if (r.length === 0) return {
2816
3057
  pending: [],
2817
3058
  skipped: []
2818
3059
  };
2819
- let i = new Set(yield* Wr(e));
3060
+ let i = new Set(yield* Ti(e));
2820
3061
  return {
2821
3062
  pending: r.filter((e) => !i.has(e.migration.id)),
2822
3063
  skipped: r.filter((e) => i.has(e.migration.id)).map((e) => e.migration.id)
2823
3064
  };
2824
- }), Jr = (e, t) => qr(e, t).pipe(p.map((e) => e.pending.map((e) => e.migration.id))), Yr = (e, t) => p.gen(function* () {
2825
- let { pending: n, skipped: r } = yield* qr(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);
2826
3067
  if (n.length === 0) return {
2827
3068
  applied: [],
2828
3069
  skipped: r
2829
3070
  };
2830
- yield* E(e);
3071
+ yield* T(e);
2831
3072
  let i = [];
2832
3073
  try {
2833
3074
  for (let r of n) {
2834
- yield* p.logInfo("file-based migration applying").pipe(p.annotateLogs({
3075
+ yield* b.logInfo("file-based migration applying").pipe(b.annotateLogs({
2835
3076
  scope: "voltro:migrate",
2836
3077
  id: r.migration.id,
2837
3078
  file: r.file
2838
3079
  }));
2839
- let { durationMs: n } = yield* Kr(e, r, t.env, t.appliedBy);
3080
+ let { durationMs: n } = yield* Di(e, r, t.env, t.appliedBy);
2840
3081
  i.push({
2841
3082
  id: r.migration.id,
2842
3083
  durationMs: n
2843
- }), yield* p.logInfo("file-based migration applied").pipe(p.annotateLogs({
3084
+ }), yield* b.logInfo("file-based migration applied").pipe(b.annotateLogs({
2844
3085
  scope: "voltro:migrate",
2845
3086
  id: r.migration.id,
2846
3087
  durationMs: n
2847
3088
  }));
2848
3089
  }
2849
3090
  } finally {
2850
- yield* D(e).pipe(p.orDie);
3091
+ yield* E(e).pipe(b.orDie);
2851
3092
  }
2852
3093
  return {
2853
3094
  applied: i,
2854
3095
  skipped: r
2855
3096
  };
2856
- }), Xr = (e, t) => e`
3097
+ }), ji = (e, t) => e`
2857
3098
  SELECT ${e("id")}
2858
3099
  FROM ${e("_voltro_migration_plans")}
2859
3100
  WHERE ${e("id")} = ${t} AND ${e("source")} = 'file'
2860
3101
  LIMIT 1
2861
- `, Zr = (e, t) => p.gen(function* () {
2862
- if (!(yield* Xr(e, t.id))[0]) return yield* p.fail(/* @__PURE__ */ Error(`rollback: ${t.id} not found in _voltro_migration_plans (file source)`));
2863
- let n = yield* p.tryPromise({
2864
- try: () => Hr(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")),
2865
3106
  catch: (e) => e
2866
- }), r = (yield* p.tryPromise({
2867
- try: () => Ur(n),
3107
+ }), r = (yield* b.tryPromise({
3108
+ try: () => wi(n),
2868
3109
  catch: (e) => e
2869
3110
  })).find((e) => e.migration.id === t.id);
2870
- if (!r) return yield* p.fail(/* @__PURE__ */ Error(`rollback: file for migration ${t.id} not found on disk under ${t.projectRoot}/migrations/`));
2871
- 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);
2872
3113
  let i = Date.now();
2873
3114
  try {
2874
3115
  let n = {
2875
3116
  sql: e,
2876
3117
  log: {
2877
- info: (e, n) => p.logInfo(e).pipe(p.annotateLogs({
3118
+ info: (e, n) => b.logInfo(e).pipe(b.annotateLogs({
2878
3119
  scope: `migrations:file:${t.id}`,
2879
3120
  ...n ?? {}
2880
3121
  })),
2881
- warn: (e, n) => p.logWarning(e).pipe(p.annotateLogs({
3122
+ warn: (e, n) => b.logWarning(e).pipe(b.annotateLogs({
2882
3123
  scope: `migrations:file:${t.id}`,
2883
3124
  ...n ?? {}
2884
3125
  }))
2885
3126
  },
2886
3127
  appliedAt: (/* @__PURE__ */ new Date()).toISOString()
2887
3128
  }, a = r.migration.down(n);
2888
- p.isEffect(a) ? yield* a : yield* p.tryPromise({
3129
+ b.isEffect(a) ? yield* a : yield* b.tryPromise({
2889
3130
  try: () => a,
2890
3131
  catch: (e) => e
2891
3132
  });
@@ -2895,19 +3136,19 @@ BEGIN
2895
3136
  durationMs: o
2896
3137
  };
2897
3138
  } finally {
2898
- yield* D(e).pipe(p.orDie);
3139
+ yield* E(e).pipe(b.orDie);
2899
3140
  }
2900
- }), Qr = (e) => p.gen(function* () {
2901
- let t = yield* h.SqlClient, n = yield* t`
3141
+ }), Ni = (e) => b.gen(function* () {
3142
+ let t = yield* S.SqlClient, n = yield* t`
2902
3143
  SELECT id, fingerprint, appliedAt FROM _voltro_migration_plans
2903
3144
  WHERE source = 'auto-diff'
2904
3145
  AND squashedAt IS NULL
2905
3146
  AND appliedAt < ${e.before}
2906
3147
  ORDER BY appliedAt DESC
2907
3148
  `;
2908
- 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}`));
2909
3150
  let r = n[0];
2910
- 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.`));
2911
3152
  yield* t`
2912
3153
  UPDATE _voltro_migration_plans
2913
3154
  SET squashedAt = CURRENT_TIMESTAMP
@@ -2942,7 +3183,7 @@ BEGIN
2942
3183
  snapshotFingerprint: e.fingerprint,
2943
3184
  snapshotId: i
2944
3185
  };
2945
- }), $r = { safe: !0 }, ei = (e) => {
3186
+ }), Pi = { safe: !0 }, Fi = (e) => {
2946
3187
  switch (e.kind) {
2947
3188
  case "drop-column": return {
2948
3189
  safe: !1,
@@ -2969,7 +3210,7 @@ BEGIN
2969
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`,
2970
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"
2971
3212
  };
2972
- case "alter-column-nullability": return e.toNullable ? $r : {
3213
+ case "alter-column-nullability": return e.toNullable ? Pi : {
2973
3214
  safe: !1,
2974
3215
  reason: `old instances may INSERT "${e.table}" without (or with NULL in) "${e.column}", which the new NOT NULL rejects`,
2975
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)`
@@ -2999,12 +3240,12 @@ BEGIN
2999
3240
  case "drop-unique":
3000
3241
  case "drop-unique-composite":
3001
3242
  case "drop-foreign-key":
3002
- case "drop-check": return $r;
3243
+ case "drop-check": return Pi;
3003
3244
  }
3004
- }, ti = (e) => {
3245
+ }, Ii = (e) => {
3005
3246
  let t = [];
3006
3247
  for (let n of e) {
3007
- let e = ei(n.op);
3248
+ let e = Fi(n.op);
3008
3249
  e.safe || t.push({
3009
3250
  op: n,
3010
3251
  reason: e.reason,
@@ -3012,7 +3253,7 @@ BEGIN
3012
3253
  });
3013
3254
  }
3014
3255
  return t;
3015
- }, ni = (e) => {
3256
+ }, Li = (e) => {
3016
3257
  if (!e.enabled || e.unsafeCount === 0) return {
3017
3258
  refuse: !1,
3018
3259
  warnForced: !1,
@@ -3028,10 +3269,127 @@ BEGIN
3028
3269
  warnForced: !1,
3029
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.`
3030
3271
  };
3031
- }, ri = (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) => ({
3032
3390
  _tag: "RawSqlFragment",
3033
3391
  strings: [...e],
3034
3392
  values: [...t]
3035
- }), ii = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
3393
+ }), Ki = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
3036
3394
  //#endregion
3037
- export { O as DEFAULT_CDC_CHANNEL, o as FILE_MIGRATION_PATTERN, Gr as FileMigrationLedgerError, k as REACTIVE_TRIGGER_PREFIX, ne as VOLTRO_MIGRATION_LOCK_KEY, E as acquireMigrationLock, et as applyNamespacedSchema, Tr as applyPlan, Je as applySchema, ni as assessRollingDeployGate, $t as chunkTables, ei as classifyRollingDeploySafety, I as declaredSnapshot, xe as defaultArrayClause, be as defaultClause, M as defaultJsonClause, Br as describeOutcome, Pr as destructiveScope, ft as detectReactiveTriggerDrift, gn as emitDropColumnDdl, _n as emitDropColumnDdlMysql, qe as emitFrameworkBootstrapSql, Ge as emitNamespaceProvisionDdl, Ke as emitNamespacedSchemaSql, We as emitSchemaSql, P as fingerprintSchema, pt as formatReactiveTriggerDrift, Nr as ignoreTablesFromEnv, U as introspectSchema, r as isFileMigration, ii as isRawSqlFragment, Jt as mapPgType, c as migration, N as numericIdSql, qt as parseEnumCheck, Jr as pendingFileMigrationIds, Kt as planMigrations, tt as provisionTenantNamespace, Fe as reactiveTriggerRepairSql, D as releaseMigrationLock, Mn as renderColumnMysql, Dn as renderColumnPg, Zr as rollbackFileBasedMigration, ti as rollingDeployUnsafeOps, Yr as runFileBasedMigrations, nt as runFrameworkBootstrap, $e as runMigrate, zr as runPlannedMigrations, F as shortFingerprint, ct as snapshotColumn, ri as sql, j as sqlType, Qr as squashMigrationPlans, Ir 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 };