@voltro/database 0.11.1 → 0.11.3
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/CHANGELOG.md +95 -0
- package/dist/{fileBased-Cc-F1oFv.js → fileBased-BrYB2u6E.js} +88 -79
- package/dist/index.d.ts +9 -3
- package/dist/index.js +508 -508
- package/dist/sql.js +100 -100
- package/package.json +2 -2
package/dist/sql.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { R as e, U as t, a as n, c as r, ct as i, i as a, n as o, nt as s, r as c, t as l, z as u } from "./fileBased-BrYB2u6E.js";
|
|
2
2
|
import { Effect as d } from "effect";
|
|
3
3
|
import { createLogger as f } from "@voltro/logger";
|
|
4
4
|
import { SqlClient as p } from "@effect/sql";
|
|
@@ -121,54 +121,54 @@ var _ = (e, t, n, r = 400) => {
|
|
|
121
121
|
orElse: () => oe(e)
|
|
122
122
|
}), ge = (e, t) => d.acquireUseRelease(w(e), () => t, () => T(e).pipe(d.orDie)), _e = f({ scope: "voltro:migrate" }), E = (e) => {
|
|
123
123
|
_e.warn(e.replace(/^\[voltro:migrate\]\s*/, ""));
|
|
124
|
-
}, D = (
|
|
125
|
-
let r =
|
|
126
|
-
if (
|
|
127
|
-
if (n === "postgres") return `${
|
|
128
|
-
throw Error(`@voltro/plugin-postgis: ${
|
|
124
|
+
}, D = (t, n) => {
|
|
125
|
+
let r = t.type;
|
|
126
|
+
if (t.spatial) {
|
|
127
|
+
if (n === "postgres") return `${t.spatial.kind}(${t.spatial.geomKind},${t.spatial.srid})`;
|
|
128
|
+
throw Error(`@voltro/plugin-postgis: ${t.spatial.kind}('${t.spatial.geomKind}', ${t.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.`);
|
|
129
129
|
}
|
|
130
130
|
if (r === "raw") {
|
|
131
|
-
if (
|
|
132
|
-
return
|
|
131
|
+
if (t.rawDdl === void 0) throw Error("raw() column is missing its DDL fragment — this is a framework bug.");
|
|
132
|
+
return t.rawDdl;
|
|
133
133
|
}
|
|
134
|
-
if (r === "array" &&
|
|
134
|
+
if (r === "array" && t.arrayElement) {
|
|
135
135
|
if (n === "postgres") return `${D({
|
|
136
|
-
...
|
|
137
|
-
type:
|
|
136
|
+
...t,
|
|
137
|
+
type: t.arrayElement,
|
|
138
138
|
arrayElement: void 0
|
|
139
139
|
}, n)}[]`;
|
|
140
140
|
if (n === "mysql" || n === "mariadb") return "JSON";
|
|
141
141
|
if (n === "mssql") return "NVARCHAR(MAX)";
|
|
142
|
-
if (
|
|
142
|
+
if (e(n)) return "TEXT";
|
|
143
143
|
}
|
|
144
144
|
if (r === "vector") {
|
|
145
|
-
let r =
|
|
145
|
+
let r = t.vectorDim;
|
|
146
146
|
if (r === void 0) throw Error("vector column is missing its dimension — this is a framework bug.");
|
|
147
|
-
let i =
|
|
147
|
+
let i = t.vectorPrecision === "half";
|
|
148
148
|
if (n === "postgres") return i ? `HALFVEC(${r})` : `VECTOR(${r})`;
|
|
149
149
|
if (n === "mysql" || n === "mariadb") return `VECTOR(${r})`;
|
|
150
150
|
if (n === "mssql") return "VARBINARY(MAX)";
|
|
151
|
-
if (
|
|
151
|
+
if (e(n)) return "BLOB";
|
|
152
152
|
}
|
|
153
153
|
if (r === "interval") {
|
|
154
154
|
if (n === "postgres") return "INTERVAL";
|
|
155
155
|
if (n === "mysql" || n === "mariadb" || n === "mssql") return "BIGINT";
|
|
156
|
-
if (
|
|
156
|
+
if (e(n)) return "INTEGER";
|
|
157
157
|
}
|
|
158
158
|
if (r === "decimal") {
|
|
159
|
-
let r =
|
|
159
|
+
let r = t.precision, i = t.scale ?? 0;
|
|
160
160
|
if (r === void 0) throw Error("decimal column is missing its precision — this is a framework bug.");
|
|
161
|
-
return
|
|
161
|
+
return e(n) ? "NUMERIC" : n === "postgres" ? `NUMERIC(${r}, ${i})` : `DECIMAL(${r}, ${i})`;
|
|
162
162
|
}
|
|
163
|
-
if (r === "bigint") return
|
|
163
|
+
if (r === "bigint") return e(n) ? "INTEGER" : "BIGINT";
|
|
164
164
|
if (r === "enum") {
|
|
165
|
-
if (n === "postgres") return
|
|
166
|
-
if (n === "mysql" || n === "mariadb") return `ENUM(${(
|
|
165
|
+
if (n === "postgres") return t.enumName ?? "TEXT";
|
|
166
|
+
if (n === "mysql" || n === "mariadb") return `ENUM(${(t.enumValues ?? []).map((e) => `'${e.replace(/'/g, "''")}'`).join(", ")})`;
|
|
167
167
|
if (n === "mssql") return "NVARCHAR(255)";
|
|
168
|
-
if (
|
|
168
|
+
if (e(n)) return "TEXT";
|
|
169
169
|
}
|
|
170
|
-
if (r === "text" &&
|
|
171
|
-
if (
|
|
170
|
+
if (r === "text" && t.maxLength !== void 0 && t.generatedAs === void 0) return n === "mssql" ? `NVARCHAR(${t.maxLength})` : e(n) ? "TEXT" : `VARCHAR(${t.maxLength})`;
|
|
171
|
+
if (e(n)) switch (r) {
|
|
172
172
|
case "id": return "TEXT";
|
|
173
173
|
case "text": return "TEXT";
|
|
174
174
|
case "integer": return "INTEGER";
|
|
@@ -186,7 +186,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
186
186
|
}
|
|
187
187
|
if (n === "mysql" || n === "mariadb") switch (r) {
|
|
188
188
|
case "id": return "VARCHAR(64)";
|
|
189
|
-
case "text": return
|
|
189
|
+
case "text": return t.hasDefault && t.defaultFactory === void 0 || t.oneOf && t.oneOf.length > 0 ? "VARCHAR(255)" : t.generatedAs ? "TEXT" : "LONGTEXT";
|
|
190
190
|
case "integer": return "INT";
|
|
191
191
|
case "real": return "DOUBLE";
|
|
192
192
|
case "boolean": return "TINYINT(1)";
|
|
@@ -202,7 +202,7 @@ var _ = (e, t, n, r = 400) => {
|
|
|
202
202
|
}
|
|
203
203
|
if (n === "mssql") switch (r) {
|
|
204
204
|
case "id": return "NVARCHAR(64)";
|
|
205
|
-
case "text": return
|
|
205
|
+
case "text": return t.hasDefault && t.defaultFactory === void 0 || t.oneOf && t.oneOf.length > 0 ? "NVARCHAR(450)" : "NVARCHAR(MAX)";
|
|
206
206
|
case "integer": return "INT";
|
|
207
207
|
case "real": return "FLOAT";
|
|
208
208
|
case "boolean": return "BIT";
|
|
@@ -228,14 +228,14 @@ var _ = (e, t, n, r = 400) => {
|
|
|
228
228
|
case "bytes": return "BYTEA";
|
|
229
229
|
case "reference": return "TEXT";
|
|
230
230
|
case "vector": return "VECTOR";
|
|
231
|
-
case "enum": return
|
|
231
|
+
case "enum": return t.enumName ?? "TEXT";
|
|
232
232
|
case "array": return "TEXT[]";
|
|
233
233
|
case "interval": return "INTERVAL";
|
|
234
234
|
}
|
|
235
|
-
}, ve = (
|
|
236
|
-
if (!
|
|
237
|
-
let r =
|
|
238
|
-
return r === "now" ?
|
|
235
|
+
}, ve = (t, n) => {
|
|
236
|
+
if (!t.hasDefault || t.defaultFactory !== void 0) return null;
|
|
237
|
+
let r = t.defaultValue;
|
|
238
|
+
return r === "now" ? e(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, "''")}'` : typeof r == "object" && r && !Array.isArray(r) ? O(r, n) : null;
|
|
239
239
|
}, O = (e, t) => {
|
|
240
240
|
let n = JSON.stringify(e).replace(/'/g, "''");
|
|
241
241
|
return t === "postgres" ? `DEFAULT '${n}'::jsonb` : t === "mysql" || t === "mariadb" ? `DEFAULT (CAST('${n}' AS JSON))` : `DEFAULT '${n}'`;
|
|
@@ -271,9 +271,9 @@ var _ = (e, t, n, r = 400) => {
|
|
|
271
271
|
if (t === "mssql") return `AS (${n})${r ? " PERSISTED" : ""}`;
|
|
272
272
|
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.");
|
|
273
273
|
return `GENERATED ALWAYS AS (${n}) ${r ? "STORED" : "VIRTUAL"}`;
|
|
274
|
-
}, Ce = (
|
|
275
|
-
if (n.type === "id" && n.idScheme?.kind === "numeric") return k(
|
|
276
|
-
let s = r === "mssql" && n.type === "text" && !n.generatedAs && n.maxLength === void 0 && i?.has(
|
|
274
|
+
}, Ce = (t, n, r, i, a = !1, o = !1) => {
|
|
275
|
+
if (n.type === "id" && n.idScheme?.kind === "numeric") return k(t, r);
|
|
276
|
+
let s = r === "mssql" && n.type === "text" && !n.generatedAs && n.maxLength === void 0 && i?.has(t) ? "NVARCHAR(450)" : D(n, r), c = [u(t, r), s];
|
|
277
277
|
n.type === "id" && !o && c.push("PRIMARY KEY");
|
|
278
278
|
let l = Se(n, r);
|
|
279
279
|
if (l) c.push(l);
|
|
@@ -291,17 +291,17 @@ var _ = (e, t, n, r = 400) => {
|
|
|
291
291
|
i !== "NO ACTION" && c.push(`ON UPDATE ${i}`);
|
|
292
292
|
}
|
|
293
293
|
if (n.oneOf && n.oneOf.length > 0) {
|
|
294
|
-
let
|
|
295
|
-
c.push(`CHECK (${u(
|
|
294
|
+
let e = n.oneOf.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
295
|
+
c.push(`CHECK (${u(t, r)} IN (${e}))`);
|
|
296
296
|
}
|
|
297
297
|
if (n.checks && n.checks.length > 0) for (let e of n.checks) c.push(`CHECK (${e})`);
|
|
298
|
-
if (n.type === "enum" && n.enumValues && n.enumValues.length > 0 && (r === "mssql" ||
|
|
299
|
-
let
|
|
300
|
-
c.push(`CHECK (${u(
|
|
298
|
+
if (n.type === "enum" && n.enumValues && n.enumValues.length > 0 && (r === "mssql" || e(r))) {
|
|
299
|
+
let e = n.enumValues.map((e) => `'${e.replace(/'/g, "''")}'`).join(", ");
|
|
300
|
+
c.push(`CHECK (${u(t, r)} IN (${e}))`);
|
|
301
301
|
}
|
|
302
302
|
if (n.type === "json" || n.type === "array") {
|
|
303
|
-
let n = u(
|
|
304
|
-
|
|
303
|
+
let n = u(t, r);
|
|
304
|
+
e(r) ? c.push(`CHECK (${n} IS NULL OR json_valid(${n}))`) : r === "mssql" && c.push(`CHECK (${n} IS NULL OR ISJSON(${n}) = 1)`);
|
|
305
305
|
}
|
|
306
306
|
return c.join(" ");
|
|
307
307
|
}, we = (e, t, n = null, r) => {
|
|
@@ -328,8 +328,8 @@ var _ = (e, t, n, r = 400) => {
|
|
|
328
328
|
}, Te = (e, t, n) => {
|
|
329
329
|
let r = `${e}_${t.name}_fts`, i = (e) => u(e, n), a = t.columns.map(i).join(", "), o = t.columns.map((e) => `new.${i(e)}`).join(", "), s = [];
|
|
330
330
|
return s.push(`CREATE VIRTUAL TABLE IF NOT EXISTS ${i(r)} USING fts5(${a}, row_id UNINDEXED, tokenize='porter');`), s.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_ai`)} AFTER INSERT ON ${i(e)} BEGIN\n INSERT INTO ${i(r)} (${a}, row_id) VALUES (${o}, new.${i("id")});\nEND;`), s.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_ad`)} AFTER DELETE ON ${i(e)} BEGIN\n DELETE FROM ${i(r)} WHERE row_id = old.${i("id")};\nEND;`), s.push(`CREATE TRIGGER IF NOT EXISTS ${i(`${r}_au`)} AFTER UPDATE ON ${i(e)} BEGIN\n DELETE FROM ${i(r)} WHERE row_id = old.${i("id")};\n INSERT INTO ${i(r)} (${a}, row_id) VALUES (${o}, new.${i("id")});\nEND;`), s;
|
|
331
|
-
}, Ee = (
|
|
332
|
-
let
|
|
331
|
+
}, Ee = (e, n, r = null) => {
|
|
332
|
+
let i = [], o = /* @__PURE__ */ new Set(), s = (e) => e.join("\0"), c = (e, r, i, a) => {
|
|
333
333
|
let o = {
|
|
334
334
|
using: "",
|
|
335
335
|
withSuffix: "",
|
|
@@ -340,82 +340,82 @@ var _ = (e, t, n, r = 400) => {
|
|
|
340
340
|
using: " USING GIST",
|
|
341
341
|
withSuffix: "",
|
|
342
342
|
skip: !1
|
|
343
|
-
} : (E(`[voltro:migrate] index '${
|
|
343
|
+
} : (E(`[voltro:migrate] index '${e}' on '${r}': ${n} doesn't support GiST — falling back to btree. Spatial queries on this column will fall back to sequential scan.`), o);
|
|
344
344
|
if (i === "gin") return n === "postgres" ? {
|
|
345
345
|
using: " USING GIN",
|
|
346
346
|
withSuffix: "",
|
|
347
347
|
skip: !1
|
|
348
|
-
} : n === "mysql" || n === "mariadb" ? (E(`[voltro:migrate] gin index '${
|
|
348
|
+
} : n === "mysql" || n === "mariadb" ? (E(`[voltro:migrate] gin index '${e}' on '${r}': ${n} 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(...).`), {
|
|
349
349
|
using: "",
|
|
350
350
|
withSuffix: "",
|
|
351
351
|
skip: !0
|
|
352
|
-
}) : (E(`[voltro:migrate] gin index '${
|
|
352
|
+
}) : (E(`[voltro:migrate] gin index '${e}' on '${r}': ${n} has no GIN access method — falling back to btree. Containment/membership queries won't use this index efficiently.`), o);
|
|
353
353
|
if (i === "brin") return n === "postgres" ? {
|
|
354
354
|
using: " USING BRIN",
|
|
355
355
|
withSuffix: "",
|
|
356
356
|
skip: !1
|
|
357
|
-
} : (E(`[voltro:migrate] brin index '${
|
|
357
|
+
} : (E(`[voltro:migrate] brin index '${e}' on '${r}': ${n} has no BRIN access method — falling back to btree (larger on disk, but the range scan still works).`), o);
|
|
358
358
|
if (i === "hnsw") {
|
|
359
359
|
if (n === "postgres") {
|
|
360
|
-
let
|
|
360
|
+
let e = a?.hnsw?.m ?? 16, n = a?.hnsw?.efConstruction ?? 64, r = a?.hnsw?.opclass ?? t(a?.hnsw?.distance);
|
|
361
361
|
return {
|
|
362
362
|
using: " USING hnsw",
|
|
363
|
-
withSuffix: ` WITH (m = ${
|
|
363
|
+
withSuffix: ` WITH (m = ${e}, ef_construction = ${n})`,
|
|
364
364
|
opclass: r,
|
|
365
365
|
skip: !1
|
|
366
366
|
};
|
|
367
367
|
}
|
|
368
|
-
return E(`[voltro:migrate] hnsw index '${
|
|
368
|
+
return E(`[voltro:migrate] hnsw index '${e}' on '${r}': ${n} has no pgvector / HNSW access method — skipping. Nearest-neighbour queries on this column fall back to a sequential scan.`), {
|
|
369
369
|
using: "",
|
|
370
370
|
withSuffix: "",
|
|
371
371
|
skip: !0
|
|
372
372
|
};
|
|
373
373
|
}
|
|
374
374
|
return o;
|
|
375
|
-
}, l = (
|
|
376
|
-
let
|
|
377
|
-
if (typeof
|
|
378
|
-
if ("jsonPath" in
|
|
379
|
-
let { column:
|
|
380
|
-
return `(${
|
|
375
|
+
}, l = (t, r) => {
|
|
376
|
+
let i = t.map((t) => {
|
|
377
|
+
if (typeof t == "string") return xe(t, e.fields, n);
|
|
378
|
+
if ("jsonPath" in t) {
|
|
379
|
+
let { column: e, path: r, numeric: i } = t.jsonPath;
|
|
380
|
+
return `(${a(e, r, n, { numeric: i })})`;
|
|
381
381
|
}
|
|
382
|
-
return `(${
|
|
382
|
+
return `(${t.expr})`;
|
|
383
383
|
});
|
|
384
|
-
return r !== void 0 &&
|
|
384
|
+
return r !== void 0 && i.length === 1 ? `${i[0]} ${r}` : i.join(", ");
|
|
385
385
|
}, d = (e, t, i, a, o, s = "", d = !1) => {
|
|
386
386
|
let f = c(e, t, a, o);
|
|
387
387
|
if (f.skip) return "";
|
|
388
388
|
let p = l(i, f.opclass), m = `${f.withSuffix}${s}`, h = ye(t, n, r), g = d ? "UNIQUE " : "";
|
|
389
389
|
return n === "mssql" ? `IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = '${e}') CREATE ${g}INDEX ${u(e, n)} ON ${h}${f.using} (${p})${m};` : n === "mysql" ? `CREATE ${g}INDEX ${u(e, n)} ON ${h}${f.using} (${p})${m};` : `CREATE ${g}INDEX IF NOT EXISTS ${u(e, n)} ON ${h}${f.using} (${p})${m};`;
|
|
390
390
|
};
|
|
391
|
-
for (let
|
|
392
|
-
let r =
|
|
393
|
-
let i =
|
|
394
|
-
return `setweight(to_tsvector('${r}', coalesce(${u(
|
|
391
|
+
for (let t of e.appliedFullText ?? []) if (n === "postgres") {
|
|
392
|
+
let r = t.config ?? "english", a = `${t.name}_tsv`, o = t.columns.map((e) => {
|
|
393
|
+
let i = t.weights?.[e] ?? "A";
|
|
394
|
+
return `setweight(to_tsvector('${r}', coalesce(${u(e, n)}, '')), '${i}')`;
|
|
395
395
|
}).join(" || ");
|
|
396
|
-
|
|
396
|
+
i.push(`ALTER TABLE ${u(e.tableName, n)} ADD COLUMN IF NOT EXISTS ${u(a, n)} tsvector GENERATED ALWAYS AS (${o}) STORED;`), i.push(`CREATE INDEX IF NOT EXISTS ${u(t.name, n)} ON ${u(e.tableName, n)} USING GIN (${u(a, n)});`);
|
|
397
397
|
} else if (n === "mysql" || n === "mariadb") {
|
|
398
|
-
let r =
|
|
399
|
-
|
|
400
|
-
} else if (n === "turso") throw Error(`@voltro/sql-turso: full-text index '${
|
|
401
|
-
else n === "sqlite" ?
|
|
402
|
-
for (let
|
|
403
|
-
if ((n === "mariadb" || n === "mssql") &&
|
|
404
|
-
E(`[voltro:migrate] json-path index '${
|
|
398
|
+
let r = t.columns.map((e) => u(e, n)).join(", ");
|
|
399
|
+
i.push(`CREATE FULLTEXT INDEX ${u(t.name, n)} ON ${u(e.tableName, n)} (${r});`);
|
|
400
|
+
} else if (n === "turso") throw Error(`@voltro/sql-turso: full-text index '${t.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.`);
|
|
401
|
+
else n === "sqlite" ? i.push(...Te(e.tableName, t, n)) : E(`[voltro:migrate] full-text index '${t.name}' on '${e.tableName}': ${n} uses a ranked LIKE search at query time (no native FTS catalog required).`);
|
|
402
|
+
for (let t of e.appliedIndexes) {
|
|
403
|
+
if ((n === "mariadb" || n === "mssql") && t.fields.some((e) => typeof e != "string" && "jsonPath" in e)) {
|
|
404
|
+
E(`[voltro:migrate] json-path index '${t.name}' on '${e.tableName}': ${n} cannot index a JSON expression directly — skipping. For an indexed JSON path on ${n}, add a generated column for the path (text().generatedAs(...) / .stored()) and .index([...]) it, then filter on that column instead of jsonField(...).`);
|
|
405
405
|
continue;
|
|
406
406
|
}
|
|
407
|
-
if (
|
|
407
|
+
if (t.unique && t.where !== void 0 && t.where.length > 0 && (n === "mysql" || n === "mariadb")) throw Error(`@voltro/migrate: uniqueActive index '${t.name}' on '${e.tableName}' cannot be expressed on ${n} — 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 ${n}, or move this table to postgres/sqlite/mssql.`);
|
|
408
408
|
let r = "";
|
|
409
|
-
|
|
410
|
-
let
|
|
411
|
-
|
|
409
|
+
t.where !== void 0 && t.where.length > 0 && (n === "mysql" || n === "mariadb" ? E(`[voltro:migrate] partial index '${t.name}' on '${e.tableName}': ${n} does not support WHERE clauses on indexes — emitting a full index`) : r = ` WHERE ${t.where}`);
|
|
410
|
+
let a = d(t.name, e.tableName, t.fields, t.kind, t.kindOptions, r, t.unique === !0);
|
|
411
|
+
a !== "" && i.push(a), t.fields.every((e) => typeof e == "string") && o.add(s(t.fields));
|
|
412
412
|
}
|
|
413
|
-
for (let [
|
|
414
|
-
if (n.type !== "reference" || n.fkAutoIndex === !1 || o.has(s([
|
|
415
|
-
let r = `${
|
|
416
|
-
|
|
413
|
+
for (let [t, n] of Object.entries(e.fields)) {
|
|
414
|
+
if (n.type !== "reference" || n.fkAutoIndex === !1 || o.has(s([t]))) continue;
|
|
415
|
+
let r = `${e.tableName}_${t}_idx`;
|
|
416
|
+
i.push(d(r, e.tableName, [t])), o.add(s([t]));
|
|
417
417
|
}
|
|
418
|
-
return
|
|
418
|
+
return i.join("\n");
|
|
419
419
|
}, De = "CREATE OR REPLACE FUNCTION framework_notify_change() RETURNS trigger AS $$\nDECLARE\n payload TEXT;\nBEGIN\n payload := json_build_object(\n 'table', TG_TABLE_NAME,\n -- The schema the write landed in. Under namespace isolation this is\n -- the tenant schema (tenant_<id>); the CDC consumer uses it so a\n -- write in tenant A namespace does not spuriously wake tenant B\n -- subscriptions on the same-named table. public/absent = shared.\n 'schema', TG_TABLE_SCHEMA,\n 'op', lower(TG_OP),\n 'old', CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE row_to_json(OLD) END,\n 'new', CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE row_to_json(NEW) END\n )::text;\n -- pg_notify hard-caps the payload at 8000 bytes; a wide reactive row\n -- (big text / json column) would otherwise abort the write itself.\n -- Fall back to a body-less notice — the CDC consumer treats absent\n -- old/new as \"all columns changed\" and re-queries, so correctness is\n -- preserved; only the delta-diff fast path is skipped for that write.\n IF octet_length(payload) >= 7999 THEN\n payload := json_build_object(\n 'table', TG_TABLE_NAME,\n 'schema', TG_TABLE_SCHEMA,\n 'op', lower(TG_OP),\n 'old', NULL,\n 'new', NULL,\n 'oversized', true\n )::text;\n END IF;\n PERFORM pg_notify('framework_changes', payload);\n RETURN COALESCE(NEW, OLD);\nEND;\n$$ LANGUAGE plpgsql;", Oe = (e) => `
|
|
420
420
|
ALTER TABLE "${e.tableName}" REPLICA IDENTITY FULL;
|
|
421
421
|
DROP TRIGGER IF EXISTS framework_changes_${e.tableName} ON "${e.tableName}";
|
|
@@ -474,12 +474,12 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
474
474
|
case "mariadb": return `ALTER TABLE ${r(e.table)} ${c(!0)};`;
|
|
475
475
|
default: return `ALTER TABLE ${r(e.table)} ${c(!1)};`;
|
|
476
476
|
}
|
|
477
|
-
}, Ne = (
|
|
477
|
+
}, Ne = (t, n) => {
|
|
478
478
|
let r = /* @__PURE__ */ new Map();
|
|
479
|
-
if (
|
|
480
|
-
for (let
|
|
481
|
-
let
|
|
482
|
-
|
|
479
|
+
if (e(n)) return r;
|
|
480
|
+
for (let e of t) {
|
|
481
|
+
let t = r.get(e.table) ?? /* @__PURE__ */ new Set();
|
|
482
|
+
t.add(e.column), r.set(e.table, t);
|
|
483
483
|
}
|
|
484
484
|
return r;
|
|
485
485
|
}, Pe = (e, t) => {
|
|
@@ -503,7 +503,7 @@ FOR EACH ROW EXECUTE FUNCTION framework_notify_change();
|
|
|
503
503
|
let n = /* @__PURE__ */ new Set();
|
|
504
504
|
for (let t of e) for (let e of Object.values(t.fields)) e.type === "enum" && e.enumName && n.add(e.enumName);
|
|
505
505
|
let r = [];
|
|
506
|
-
for (let e of n) for (let { from: t, to: n } of
|
|
506
|
+
for (let e of n) for (let { from: t, to: n } of i(e)) {
|
|
507
507
|
let i = e.replace(/'/g, "''"), a = t.replace(/'/g, "''"), o = n.replace(/'/g, "''");
|
|
508
508
|
r.push(`DO \$\$
|
|
509
509
|
DECLARE
|
|
@@ -519,15 +519,15 @@ BEGIN
|
|
|
519
519
|
tables: t,
|
|
520
520
|
views: r
|
|
521
521
|
};
|
|
522
|
-
}, j = (
|
|
523
|
-
let { tables: i, views: a } = Ie(
|
|
522
|
+
}, j = (t, n) => {
|
|
523
|
+
let { tables: i, views: a } = Ie(t), { sorted: o, cyclic: s } = je(i.map((e) => b(e, n))), c = Ne(s, n), l = [];
|
|
524
524
|
n === "postgres" && ke(o) && l.push("CREATE EXTENSION IF NOT EXISTS vector;"), n === "postgres" && Ae(o) && l.push("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
525
525
|
let u = Fe(o, n);
|
|
526
526
|
u && l.push(u);
|
|
527
527
|
let d = Pe(o, n);
|
|
528
528
|
d && l.push(d);
|
|
529
529
|
for (let e of o) l.push(we(e, n, null, c.get(e.tableName)));
|
|
530
|
-
if (!
|
|
530
|
+
if (!e(n)) for (let e of s) l.push(Me(e, n));
|
|
531
531
|
for (let e of o) {
|
|
532
532
|
let t = Ee(e, n);
|
|
533
533
|
t && l.push(t);
|
|
@@ -549,13 +549,13 @@ BEGIN
|
|
|
549
549
|
case "turso":
|
|
550
550
|
case "sqlite": return `ATTACH DATABASE '${(n ?? `${e}.db`).replace(/'/g, "''")}' AS ${r};`;
|
|
551
551
|
}
|
|
552
|
-
}, Re = (
|
|
552
|
+
}, Re = (t, n, r, i) => {
|
|
553
553
|
let a = Le(r, n, i), o = u(r, n);
|
|
554
|
-
if (n === "postgres") return `${a}\n\nSET search_path TO ${o};\n\n${j(
|
|
555
|
-
if (n === "mysql" || n === "mariadb") return `${a}\n\nUSE ${o};\n\n${j(
|
|
556
|
-
let { sorted: s, cyclic: c } = je(
|
|
554
|
+
if (n === "postgres") return `${a}\n\nSET search_path TO ${o};\n\n${j(t, n)}`;
|
|
555
|
+
if (n === "mysql" || n === "mariadb") return `${a}\n\nUSE ${o};\n\n${j(t, n)}`;
|
|
556
|
+
let { sorted: s, cyclic: c } = je(t), l = Ne(c, n), d = [a];
|
|
557
557
|
for (let e of s) d.push(we(e, n, r, l.get(e.tableName)));
|
|
558
|
-
if (!
|
|
558
|
+
if (!e(n)) for (let e of c) d.push(Me(e, n, r));
|
|
559
559
|
for (let e of s) {
|
|
560
560
|
let t = Ee(e, n, r);
|
|
561
561
|
t && d.push(t);
|
|
@@ -713,13 +713,13 @@ BEGIN
|
|
|
713
713
|
columns: t.fields,
|
|
714
714
|
unique: !0
|
|
715
715
|
});
|
|
716
|
-
let
|
|
717
|
-
for (let e of a) e.fields.every((e) => typeof e == "string") &&
|
|
716
|
+
let c = /* @__PURE__ */ new Set();
|
|
717
|
+
for (let e of a) e.fields.every((e) => typeof e == "string") && c.add(e.fields.join("\0"));
|
|
718
718
|
for (let [t, n] of Object.entries(e.fields)) {
|
|
719
719
|
let i = n;
|
|
720
|
-
if (i.dropped || !i.references || i.fkAutoIndex === !1 ||
|
|
720
|
+
if (i.dropped || !i.references || i.fkAutoIndex === !1 || c.has(t)) continue;
|
|
721
721
|
let a = `${e.tableName}_${t}_idx`;
|
|
722
|
-
|
|
722
|
+
s(e.tableName, a, !0, [t]), r({
|
|
723
723
|
name: a,
|
|
724
724
|
table: e.tableName,
|
|
725
725
|
columns: [t],
|
|
@@ -2418,7 +2418,7 @@ BEGIN
|
|
|
2418
2418
|
for (let i of r) {
|
|
2419
2419
|
if (cr.has(i.name)) continue;
|
|
2420
2420
|
let r = g(e, i.name);
|
|
2421
|
-
i.isDirectory() ? await n(r) : i.isFile() &&
|
|
2421
|
+
i.isDirectory() ? await n(r) : i.isFile() && l.test(i.name) && t.push(r);
|
|
2422
2422
|
}
|
|
2423
2423
|
};
|
|
2424
2424
|
return await n(e), t.sort((e, t) => {
|
|
@@ -2429,7 +2429,7 @@ BEGIN
|
|
|
2429
2429
|
let t = [];
|
|
2430
2430
|
for (let n of e) {
|
|
2431
2431
|
let e = (await import(ee(n).href)).default;
|
|
2432
|
-
if (!
|
|
2432
|
+
if (!o(e)) {
|
|
2433
2433
|
process.stderr.write(`[voltro:migrate] ${n} has no default-exported \`migration(...)\` — skipped\n`);
|
|
2434
2434
|
continue;
|
|
2435
2435
|
}
|
|
@@ -2619,4 +2619,4 @@ BEGIN
|
|
|
2619
2619
|
values: [...t]
|
|
2620
2620
|
}), vr = (e) => typeof e == "object" && !!e && e._tag === "RawSqlFragment";
|
|
2621
2621
|
//#endregion
|
|
2622
|
-
export {
|
|
2622
|
+
export { l as FILE_MIGRATION_PATTERN, C as VOLTRO_MIGRATION_LOCK_KEY, ut as _internalCmp, w as acquireMigrationLock, Ke as applyNamespacedSchema, Yn as applyPlan, Be as applySchema, Lt as chunkTables, P as declaredSnapshot, ve as defaultClause, O as defaultJsonClause, sr as describeOutcome, Yt as emitDropColumnDdl, Xt as emitDropColumnDdlMysql, ze as emitFrameworkBootstrapSql, Le as emitNamespaceProvisionDdl, Re as emitNamespacedSchemaSql, j as emitSchemaSql, M as fingerprintSchema, rr as ignoreTablesFromEnv, qt as introspectSchema, o as isFileMigration, vr as isRawSqlFragment, Mt as mapPgType, c as migration, k as numericIdSql, jt as parseEnumCheck, At as planMigrations, qe as provisionTenantNamespace, T as releaseMigrationLock, sn as renderColumnMysql, rn as renderColumnPg, hr as rollbackFileBasedMigration, pr as runFileBasedMigrations, Je as runFrameworkBootstrap, Ge as runMigrate, or as runPlannedMigrations, N as shortFingerprint, _r as sql, D as sqlType, gr as squashMigrationPlans, ir as unblockLossy, ge as withMigrationLock };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/database",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "Browser-safe schema DSL, query builder, and cross-dialect migration planner for Voltro — one schema, every SQL backend.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@effect/sql": "^0.51.1",
|
|
46
|
-
"@voltro/logger": "0.11.
|
|
46
|
+
"@voltro/logger": "0.11.3",
|
|
47
47
|
"typeid-js": "^1.2.0",
|
|
48
48
|
"ulidx": "^2.4.1"
|
|
49
49
|
},
|