bun-query-builder 0.1.63 → 0.2.2
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/actions/introspect-db.d.ts +9 -0
- package/dist/actions/migrate-rollback.d.ts +10 -2
- package/dist/actions/migrate-status.d.ts +1 -1
- package/dist/actions/migrate.d.ts +4 -0
- package/dist/actions/seed.d.ts +8 -1
- package/dist/bin/cli.js +624 -331
- package/dist/client.d.ts +2 -2
- package/dist/config.d.ts +13 -0
- package/dist/db.d.ts +12 -0
- package/dist/drivers/index-name.d.ts +1 -0
- package/dist/inflect.d.ts +17 -0
- package/dist/migrations.d.ts +26 -0
- package/dist/src/index.js +611 -315
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -11641,9 +11641,18 @@ var init_dist = __esm(() => {
|
|
|
11641
11641
|
});
|
|
11642
11642
|
|
|
11643
11643
|
// src/config.ts
|
|
11644
|
+
import process19 from "process";
|
|
11644
11645
|
function isMysqlLike(dialect = config5.dialect) {
|
|
11645
11646
|
return dialect === "mysql" || dialect === "singlestore";
|
|
11646
11647
|
}
|
|
11648
|
+
function resolveDialect(explicit) {
|
|
11649
|
+
if (explicit)
|
|
11650
|
+
return explicit;
|
|
11651
|
+
const fromEnv = process19.env.DB_DIALECT || process19.env.DB_CONNECTION;
|
|
11652
|
+
if (fromEnv)
|
|
11653
|
+
return fromEnv;
|
|
11654
|
+
return config5.dialect || "postgres";
|
|
11655
|
+
}
|
|
11647
11656
|
function getPlaceholder(index) {
|
|
11648
11657
|
if (config5.dialect === "postgres") {
|
|
11649
11658
|
return `$${index}`;
|
|
@@ -11802,7 +11811,7 @@ var {SQL } = globalThis.Bun;
|
|
|
11802
11811
|
import { Database } from "bun:sqlite";
|
|
11803
11812
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11804
11813
|
import { dirname as dirname5 } from "path";
|
|
11805
|
-
import
|
|
11814
|
+
import process21 from "process";
|
|
11806
11815
|
|
|
11807
11816
|
class SQLiteWrapper {
|
|
11808
11817
|
db;
|
|
@@ -12080,9 +12089,13 @@ function sqliteFileFromUrl(url) {
|
|
|
12080
12089
|
const path = stripped.startsWith("///") ? stripped.slice(2) : stripped.startsWith("//") ? stripped.slice(2) : stripped;
|
|
12081
12090
|
return path || ":memory:";
|
|
12082
12091
|
}
|
|
12092
|
+
function resolveDatabaseName(dbConfig = config5.database) {
|
|
12093
|
+
const raw = process21.env.DB_DATABASE || dbConfig?.database || "";
|
|
12094
|
+
return String(raw).replace(/^['"]|['"]$/g, "");
|
|
12095
|
+
}
|
|
12083
12096
|
function createConnectionString(dialect, dbConfig) {
|
|
12084
|
-
if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") &&
|
|
12085
|
-
const e =
|
|
12097
|
+
if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") && process21.env.DB_CONNECTION === dialect) {
|
|
12098
|
+
const e = process21.env;
|
|
12086
12099
|
const envDb = (e.DB_DATABASE || dbConfig.database || "").replace(/^['"]|['"]$/g, "");
|
|
12087
12100
|
const envUser = e.DB_USERNAME || dbConfig.username;
|
|
12088
12101
|
const envPass = e.DB_PASSWORD ?? dbConfig.password ?? "";
|
|
@@ -12096,7 +12109,7 @@ function createConnectionString(dialect, dbConfig) {
|
|
|
12096
12109
|
return dialect === "sqlite" ? sqliteFileFromUrl(dbConfig.url) : dbConfig.url;
|
|
12097
12110
|
}
|
|
12098
12111
|
const { database, username, password, host = "localhost", port, ssl } = dbConfig;
|
|
12099
|
-
const sslQ = ssl ||
|
|
12112
|
+
const sslQ = ssl || process21.env.DB_SSL === "true" || process21.env.DB_SSL === "1" ? "?ssl=true" : "";
|
|
12100
12113
|
switch (dialect) {
|
|
12101
12114
|
case "postgres":
|
|
12102
12115
|
return `postgres://${username}:${password}@${host}${port ? `:${port}` : ""}/${database}${sslQ}`;
|
|
@@ -12370,6 +12383,17 @@ var init_benchmark = __esm(() => {
|
|
|
12370
12383
|
});
|
|
12371
12384
|
|
|
12372
12385
|
// src/inflect.ts
|
|
12386
|
+
function toTableName(modelName) {
|
|
12387
|
+
const snake = String(modelName).replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
12388
|
+
if (snake.endsWith("y") && !/[aeou]y$/.test(snake))
|
|
12389
|
+
return `${snake.slice(0, -1)}ies`;
|
|
12390
|
+
if (snake.endsWith("s") || snake.endsWith("x") || snake.endsWith("ch") || snake.endsWith("sh"))
|
|
12391
|
+
return `${snake}es`;
|
|
12392
|
+
return `${snake}s`;
|
|
12393
|
+
}
|
|
12394
|
+
function tableNameFor(model) {
|
|
12395
|
+
return typeof model?.table === "string" && model.table.length > 0 ? model.table : toTableName(String(model?.name ?? ""));
|
|
12396
|
+
}
|
|
12373
12397
|
function stripTrailingS(name) {
|
|
12374
12398
|
return name.endsWith("s") ? name.slice(0, -1) : name;
|
|
12375
12399
|
}
|
|
@@ -12484,6 +12508,24 @@ var init_pivot = __esm(() => {
|
|
|
12484
12508
|
function isRawExpression(expr) {
|
|
12485
12509
|
return typeof expr === "object" && expr !== null && "raw" in expr && typeof expr.raw === "string";
|
|
12486
12510
|
}
|
|
12511
|
+
function isBoundSqlExpression(expr) {
|
|
12512
|
+
return typeof expr === "object" && expr !== null && "sql" in expr && typeof expr.sql === "string" && (!("parameters" in expr) || Array.isArray(expr.parameters));
|
|
12513
|
+
}
|
|
12514
|
+
function renderBoundSqlExpression(expression, startIndex) {
|
|
12515
|
+
const parameters = expression.parameters ?? [];
|
|
12516
|
+
if (config5.dialect !== "postgres" || parameters.length === 0)
|
|
12517
|
+
return { text: expression.sql, parameters };
|
|
12518
|
+
let parameterIndex = 0;
|
|
12519
|
+
const text = expression.sql.replace(/\?/g, (placeholder) => {
|
|
12520
|
+
if (parameterIndex >= parameters.length)
|
|
12521
|
+
return placeholder;
|
|
12522
|
+
return getPlaceholder(startIndex + parameterIndex++);
|
|
12523
|
+
});
|
|
12524
|
+
if (parameterIndex !== parameters.length) {
|
|
12525
|
+
throw new TypeError(`[query-builder] SQL expression declares ${parameters.length} parameters but contains only ${parameterIndex} placeholders`);
|
|
12526
|
+
}
|
|
12527
|
+
return { text, parameters };
|
|
12528
|
+
}
|
|
12487
12529
|
function raw(strings, ...values) {
|
|
12488
12530
|
if (typeof strings === "string")
|
|
12489
12531
|
return { raw: strings };
|
|
@@ -12957,6 +12999,31 @@ function createQueryBuilder(state) {
|
|
|
12957
12999
|
throw err;
|
|
12958
13000
|
});
|
|
12959
13001
|
}
|
|
13002
|
+
function mutationCount(result) {
|
|
13003
|
+
if (typeof result === "number")
|
|
13004
|
+
return Number.isFinite(result) ? result : 0;
|
|
13005
|
+
if (typeof result === "bigint")
|
|
13006
|
+
return Number(result);
|
|
13007
|
+
if (!result || typeof result !== "object")
|
|
13008
|
+
return 0;
|
|
13009
|
+
const record = result;
|
|
13010
|
+
for (const key of [
|
|
13011
|
+
"changes",
|
|
13012
|
+
"affectedRows",
|
|
13013
|
+
"count",
|
|
13014
|
+
"rowCount",
|
|
13015
|
+
"numAffectedRows",
|
|
13016
|
+
"numUpdatedRows",
|
|
13017
|
+
"numDeletedRows",
|
|
13018
|
+
"numInsertedOrUpdatedRows"
|
|
13019
|
+
]) {
|
|
13020
|
+
if (record[key] !== undefined && record[key] !== null)
|
|
13021
|
+
return mutationCount(record[key]);
|
|
13022
|
+
}
|
|
13023
|
+
if (Array.isArray(result))
|
|
13024
|
+
return result.reduce((total, item) => total + mutationCount(item), 0);
|
|
13025
|
+
return 0;
|
|
13026
|
+
}
|
|
12960
13027
|
function makeExecutableQuery(q, text) {
|
|
12961
13028
|
const sqlText = text ?? computeSqlText(q);
|
|
12962
13029
|
return {
|
|
@@ -14052,7 +14119,16 @@ function createQueryBuilder(state) {
|
|
|
14052
14119
|
built = null;
|
|
14053
14120
|
return this;
|
|
14054
14121
|
}
|
|
14055
|
-
|
|
14122
|
+
if (typeof expr === "function") {
|
|
14123
|
+
throw new TypeError("where(callback) is not supported. This builder has no expression callback, and " + "ignoring one silently would leave the query matching every row the filter was " + "meant to exclude. Use whereNested(subquery), whereAny(...), or the orWhere* " + "helpers to build a grouped condition.");
|
|
14124
|
+
}
|
|
14125
|
+
if (expr === undefined || expr === null) {
|
|
14126
|
+
throw new TypeError("where() was called with no condition.");
|
|
14127
|
+
}
|
|
14128
|
+
if (typeof expr === "string") {
|
|
14129
|
+
throw new TypeError(`where(${JSON.stringify(expr)}) is missing an operator and a value. Pass ` + "where(column, operator, value), or whereRaw() for a raw fragment.");
|
|
14130
|
+
}
|
|
14131
|
+
throw new TypeError(`where() does not understand a condition of type ${typeof expr}.`);
|
|
14056
14132
|
},
|
|
14057
14133
|
whereNull(column) {
|
|
14058
14134
|
validateIdentifier(String(column), "whereNull(column)");
|
|
@@ -15657,8 +15733,17 @@ function createQueryBuilder(state) {
|
|
|
15657
15733
|
const setClauses = Array.from({ length: len });
|
|
15658
15734
|
for (let i = 0;i < len; i++) {
|
|
15659
15735
|
const key = keys[i];
|
|
15660
|
-
|
|
15661
|
-
|
|
15736
|
+
const value = values[key];
|
|
15737
|
+
if (isRawExpression(value)) {
|
|
15738
|
+
setClauses[i] = `${quoteId(key)} = ${value.raw}`;
|
|
15739
|
+
} else if (isBoundSqlExpression(value)) {
|
|
15740
|
+
const expression = renderBoundSqlExpression(value, params.length + 1);
|
|
15741
|
+
setClauses[i] = `${quoteId(key)} = ${expression.text}`;
|
|
15742
|
+
params.push(...expression.parameters);
|
|
15743
|
+
} else {
|
|
15744
|
+
setClauses[i] = `${quoteId(key)} = ${getPlaceholder(params.length + 1)}`;
|
|
15745
|
+
params.push(value);
|
|
15746
|
+
}
|
|
15662
15747
|
}
|
|
15663
15748
|
sqlText = `${sqlText} SET ${setClauses.join(", ")}`;
|
|
15664
15749
|
built = _sql.unsafe(sqlText, params);
|
|
@@ -15666,17 +15751,29 @@ function createQueryBuilder(state) {
|
|
|
15666
15751
|
},
|
|
15667
15752
|
where(expr, op, value) {
|
|
15668
15753
|
const getWhereKeyword = () => SQL_PATTERNS.WHERE.test(sqlText) ? "AND" : "WHERE";
|
|
15669
|
-
if (typeof expr === "string"
|
|
15754
|
+
if (op !== undefined && (typeof expr === "string" || isRawExpression(expr) || isBoundSqlExpression(expr))) {
|
|
15755
|
+
const safeOperator = assertSafeWhereOperator(op, "updateTable.where");
|
|
15756
|
+
let left;
|
|
15757
|
+
if (typeof expr === "string") {
|
|
15758
|
+
left = quoteId(expr);
|
|
15759
|
+
} else if (isRawExpression(expr)) {
|
|
15760
|
+
left = expr.raw;
|
|
15761
|
+
} else {
|
|
15762
|
+
const expression = renderBoundSqlExpression(expr, params.length + 1);
|
|
15763
|
+
left = expression.text;
|
|
15764
|
+
params.push(...expression.parameters);
|
|
15765
|
+
}
|
|
15670
15766
|
const paramIndex = params.length + 1;
|
|
15671
|
-
sqlText = `${sqlText} ${getWhereKeyword()} ${
|
|
15767
|
+
sqlText = `${sqlText} ${getWhereKeyword()} ${left} ${safeOperator} ${getPlaceholder(paramIndex)}`;
|
|
15672
15768
|
params.push(value);
|
|
15673
15769
|
built = _sql.unsafe(sqlText, params);
|
|
15674
15770
|
return this;
|
|
15675
15771
|
}
|
|
15676
15772
|
if (Array.isArray(expr)) {
|
|
15677
15773
|
const [col, op2, val] = expr;
|
|
15774
|
+
const safeOperator = assertSafeWhereOperator(op2, "updateTable.where");
|
|
15678
15775
|
const paramIndex = params.length + 1;
|
|
15679
|
-
sqlText = `${sqlText} ${getWhereKeyword()} ${quoteId(String(col))} ${
|
|
15776
|
+
sqlText = `${sqlText} ${getWhereKeyword()} ${quoteId(String(col))} ${safeOperator} ${getPlaceholder(paramIndex)}`;
|
|
15680
15777
|
params.push(val);
|
|
15681
15778
|
built = _sql.unsafe(sqlText, params);
|
|
15682
15779
|
return this;
|
|
@@ -15735,15 +15832,16 @@ function createQueryBuilder(state) {
|
|
|
15735
15832
|
}
|
|
15736
15833
|
return makeExecutableQuery(built, sqlText);
|
|
15737
15834
|
},
|
|
15738
|
-
execute() {
|
|
15739
|
-
|
|
15835
|
+
async execute() {
|
|
15836
|
+
const result = await runWithHooks(built, "update");
|
|
15837
|
+
return mutationCount(result);
|
|
15740
15838
|
},
|
|
15741
15839
|
async executeTakeFirst() {
|
|
15742
|
-
const result = await runWithHooks(built, "update");
|
|
15840
|
+
const result = mutationCount(await runWithHooks(built, "update"));
|
|
15743
15841
|
return { numUpdatedRows: result };
|
|
15744
15842
|
},
|
|
15745
15843
|
async executeTakeFirstOrThrow() {
|
|
15746
|
-
const result = await runWithHooks(built, "update");
|
|
15844
|
+
const result = mutationCount(await runWithHooks(built, "update"));
|
|
15747
15845
|
if (result === 0)
|
|
15748
15846
|
throw new Error("No rows updated");
|
|
15749
15847
|
return { numUpdatedRows: result };
|
|
@@ -15864,18 +15962,18 @@ function createQueryBuilder(state) {
|
|
|
15864
15962
|
} catch (err) {
|
|
15865
15963
|
throw err;
|
|
15866
15964
|
}
|
|
15867
|
-
const result = await runWithHooks(ensureDelBuilt(), "delete");
|
|
15965
|
+
const result = mutationCount(await runWithHooks(ensureDelBuilt(), "delete"));
|
|
15868
15966
|
try {
|
|
15869
15967
|
await config5.hooks?.afterDelete?.({ table: String(table), where: whereCondition, result });
|
|
15870
15968
|
} catch {}
|
|
15871
15969
|
return result;
|
|
15872
15970
|
},
|
|
15873
15971
|
async executeTakeFirst() {
|
|
15874
|
-
const result = await runWithHooks(ensureDelBuilt(), "delete");
|
|
15972
|
+
const result = mutationCount(await runWithHooks(ensureDelBuilt(), "delete"));
|
|
15875
15973
|
return { numDeletedRows: result };
|
|
15876
15974
|
},
|
|
15877
15975
|
async executeTakeFirstOrThrow() {
|
|
15878
|
-
const result = await runWithHooks(ensureDelBuilt(), "delete");
|
|
15976
|
+
const result = mutationCount(await runWithHooks(ensureDelBuilt(), "delete"));
|
|
15879
15977
|
if (result === 0)
|
|
15880
15978
|
throw new Error("No rows deleted");
|
|
15881
15979
|
return { numDeletedRows: result };
|
|
@@ -16590,7 +16688,7 @@ var init_cache = __esm(() => {
|
|
|
16590
16688
|
});
|
|
16591
16689
|
|
|
16592
16690
|
// src/actions/console.ts
|
|
16593
|
-
import
|
|
16691
|
+
import process22 from "process";
|
|
16594
16692
|
import { createInterface } from "readline";
|
|
16595
16693
|
async function startConsole() {
|
|
16596
16694
|
console.log("-- Query Builder Interactive Console");
|
|
@@ -16599,8 +16697,8 @@ async function startConsole() {
|
|
|
16599
16697
|
console.log();
|
|
16600
16698
|
const qb = createQueryBuilder();
|
|
16601
16699
|
const rl = createInterface({
|
|
16602
|
-
input:
|
|
16603
|
-
output:
|
|
16700
|
+
input: process22.stdin,
|
|
16701
|
+
output: process22.stdout,
|
|
16604
16702
|
prompt: "qb> "
|
|
16605
16703
|
});
|
|
16606
16704
|
let multilineBuffer = "";
|
|
@@ -16651,7 +16749,7 @@ Tips:
|
|
|
16651
16749
|
if (cmd === ".exit" || cmd === ".quit") {
|
|
16652
16750
|
console.log("Goodbye!");
|
|
16653
16751
|
rl.close();
|
|
16654
|
-
|
|
16752
|
+
process22.exit(0);
|
|
16655
16753
|
} else if (cmd === ".help") {
|
|
16656
16754
|
console.log(helpText);
|
|
16657
16755
|
} else if (cmd === ".clear") {
|
|
@@ -16722,7 +16820,7 @@ Tips:
|
|
|
16722
16820
|
rl.on("close", () => {
|
|
16723
16821
|
console.log(`
|
|
16724
16822
|
Goodbye!`);
|
|
16725
|
-
|
|
16823
|
+
process22.exit(0);
|
|
16726
16824
|
});
|
|
16727
16825
|
rl.prompt();
|
|
16728
16826
|
}
|
|
@@ -17100,9 +17198,8 @@ var init_db_info = __esm(() => {
|
|
|
17100
17198
|
});
|
|
17101
17199
|
|
|
17102
17200
|
// src/actions/db-optimize.ts
|
|
17103
|
-
import process22 from "process";
|
|
17104
17201
|
async function dbOptimize(options = {}) {
|
|
17105
|
-
const dialect = options.dialect
|
|
17202
|
+
const dialect = resolveDialect(options.dialect);
|
|
17106
17203
|
const aggressive = options.aggressive || false;
|
|
17107
17204
|
if (options.verbose) {
|
|
17108
17205
|
console.log(`Optimizing ${dialect} database${aggressive ? " (aggressive mode)" : ""}...`);
|
|
@@ -17142,7 +17239,7 @@ async function dbOptimize(options = {}) {
|
|
|
17142
17239
|
await bunSql`ANALYZE TABLE ${bunSql(table)}`;
|
|
17143
17240
|
}
|
|
17144
17241
|
} else {
|
|
17145
|
-
const dbName =
|
|
17242
|
+
const dbName = resolveDatabaseName();
|
|
17146
17243
|
const tables = await bunSql`
|
|
17147
17244
|
SELECT table_name
|
|
17148
17245
|
FROM information_schema.tables
|
|
@@ -17195,9 +17292,8 @@ var init_db_optimize = __esm(() => {
|
|
|
17195
17292
|
});
|
|
17196
17293
|
|
|
17197
17294
|
// src/actions/db-wipe.ts
|
|
17198
|
-
import process23 from "process";
|
|
17199
17295
|
async function dbWipe(options = {}) {
|
|
17200
|
-
const dialect = options.dialect
|
|
17296
|
+
const dialect = resolveDialect(options.dialect);
|
|
17201
17297
|
if (options.verbose) {
|
|
17202
17298
|
console.log(`Wiping all tables from ${dialect} database...`);
|
|
17203
17299
|
}
|
|
@@ -17211,7 +17307,7 @@ async function dbWipe(options = {}) {
|
|
|
17211
17307
|
`;
|
|
17212
17308
|
tables = result.map((row) => row.tablename);
|
|
17213
17309
|
} else if (isMysqlLike(dialect)) {
|
|
17214
|
-
const dbName =
|
|
17310
|
+
const dbName = resolveDatabaseName();
|
|
17215
17311
|
const result = await bunSql`
|
|
17216
17312
|
SELECT table_name
|
|
17217
17313
|
FROM information_schema.tables
|
|
@@ -17648,6 +17744,8 @@ function sqlTypeToNormalized(rawType, dialect, ctx) {
|
|
|
17648
17744
|
return "decimal";
|
|
17649
17745
|
if (base === "date")
|
|
17650
17746
|
return "date";
|
|
17747
|
+
if (base === "timestamptz" || /\bwith time zone\b/.test(t))
|
|
17748
|
+
return "timestamptz";
|
|
17651
17749
|
if (/^(?:timestamp|datetime)/.test(base))
|
|
17652
17750
|
return "datetime";
|
|
17653
17751
|
if (/^time/.test(base))
|
|
@@ -17658,10 +17756,6 @@ function sqlTypeToNormalized(rawType, dialect, ctx) {
|
|
|
17658
17756
|
return "enum";
|
|
17659
17757
|
return "string";
|
|
17660
17758
|
}
|
|
17661
|
-
function stripIndexPrefix(physicalName, table) {
|
|
17662
|
-
const prefix = `${table}_`;
|
|
17663
|
-
return physicalName.startsWith(prefix) ? physicalName.slice(prefix.length) : physicalName;
|
|
17664
|
-
}
|
|
17665
17759
|
function asAction(raw2) {
|
|
17666
17760
|
const v = String(raw2 ?? "").toLowerCase().trim();
|
|
17667
17761
|
if (v === "cascade" || v === "restrict" || v === "set null")
|
|
@@ -17697,7 +17791,7 @@ async function buildSqliteTable(qb, table) {
|
|
|
17697
17791
|
const isUnique = Number(idx.unique) === 1;
|
|
17698
17792
|
if (isUnique && columns2.length === 1)
|
|
17699
17793
|
uniqueColumns.add(columns2[0]);
|
|
17700
|
-
indexes.push({ name:
|
|
17794
|
+
indexes.push({ name: physName, columns: columns2, type: isUnique ? "unique" : "index" });
|
|
17701
17795
|
}
|
|
17702
17796
|
const columns = colRows.map((r) => {
|
|
17703
17797
|
const name = String(r.name);
|
|
@@ -17734,7 +17828,31 @@ function normalizeRawDefault(raw2) {
|
|
|
17734
17828
|
return raw2;
|
|
17735
17829
|
return String(raw2);
|
|
17736
17830
|
}
|
|
17737
|
-
async function
|
|
17831
|
+
async function pgEnumLabels(qb) {
|
|
17832
|
+
const rows = await qb.unsafe(`SELECT t.typname AS type_name, e.enumlabel AS label
|
|
17833
|
+
FROM pg_type t
|
|
17834
|
+
JOIN pg_enum e ON e.enumtypid = t.oid
|
|
17835
|
+
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
17836
|
+
WHERE n.nspname = 'public'
|
|
17837
|
+
ORDER BY t.typname, e.enumsortorder`);
|
|
17838
|
+
const labels = new Map;
|
|
17839
|
+
for (const row of rows) {
|
|
17840
|
+
const name = String(row.type_name);
|
|
17841
|
+
const existing = labels.get(name);
|
|
17842
|
+
if (existing)
|
|
17843
|
+
existing.push(String(row.label));
|
|
17844
|
+
else
|
|
17845
|
+
labels.set(name, [String(row.label)]);
|
|
17846
|
+
}
|
|
17847
|
+
return labels;
|
|
17848
|
+
}
|
|
17849
|
+
function pgColumnType(dataType, udtName, enums) {
|
|
17850
|
+
const enumValues = enums.get(udtName);
|
|
17851
|
+
const type = sqlTypeToNormalized(String(dataType || udtName || ""), "postgres", { enumValues });
|
|
17852
|
+
return enumValues ? { type, enumValues, enumTypeName: udtName } : { type };
|
|
17853
|
+
}
|
|
17854
|
+
async function buildPgTable(qb, table, enumLabels) {
|
|
17855
|
+
const enums = enumLabels ?? await pgEnumLabels(qb);
|
|
17738
17856
|
const colRows = await qb.unsafe(`SELECT c.column_name, c.data_type, c.udt_name, c.is_nullable, c.column_default, c.character_maximum_length,
|
|
17739
17857
|
(SELECT COUNT(*) FROM information_schema.table_constraints tc
|
|
17740
17858
|
JOIN information_schema.key_column_usage k ON k.constraint_name = tc.constraint_name
|
|
@@ -17782,7 +17900,7 @@ async function buildPgTable(qb, table) {
|
|
|
17782
17900
|
const hasDefault = rawDefault != null && !isSerial;
|
|
17783
17901
|
return {
|
|
17784
17902
|
name,
|
|
17785
|
-
|
|
17903
|
+
...pgColumnType(String(r.data_type ?? ""), r.udt_name == null ? "" : String(r.udt_name), enums),
|
|
17786
17904
|
isPrimaryKey,
|
|
17787
17905
|
isUnique: uniqueColumns.has(name),
|
|
17788
17906
|
isNullable: isPrimaryKey ? false : String(r.is_nullable).toUpperCase() === "YES",
|
|
@@ -17868,7 +17986,7 @@ function groupPhysicalIndexes(table, rows) {
|
|
|
17868
17986
|
if (entry.isUnique && entry.columns.length === 1)
|
|
17869
17987
|
uniqueColumns.add(entry.columns[0]);
|
|
17870
17988
|
indexes.push({
|
|
17871
|
-
name:
|
|
17989
|
+
name: physName,
|
|
17872
17990
|
columns: entry.columns,
|
|
17873
17991
|
type: entry.isUnique ? "unique" : "index",
|
|
17874
17992
|
where: entry.where
|
|
@@ -17881,13 +17999,14 @@ async function buildPlanFromDatabase(dialect, opts = {}) {
|
|
|
17881
17999
|
const qb = createQueryBuilder();
|
|
17882
18000
|
const allTables = opts.tables?.length ? opts.tables : await listTables(qb, d);
|
|
17883
18001
|
const tables = allTables.filter((t) => t !== "migrations");
|
|
18002
|
+
const enumLabels = d === "postgres" ? await pgEnumLabels(qb) : undefined;
|
|
17884
18003
|
const out = [];
|
|
17885
18004
|
for (const table of tables) {
|
|
17886
18005
|
let plan;
|
|
17887
18006
|
if (d === "sqlite")
|
|
17888
18007
|
plan = await buildSqliteTable(qb, table);
|
|
17889
18008
|
else if (d === "postgres")
|
|
17890
|
-
plan = await buildPgTable(qb, table);
|
|
18009
|
+
plan = await buildPgTable(qb, table, enumLabels);
|
|
17891
18010
|
else
|
|
17892
18011
|
plan = await buildMysqlTable(qb, table);
|
|
17893
18012
|
if (plan.columns.length > 0)
|
|
@@ -17903,7 +18022,7 @@ var init_introspect_db = __esm(() => {
|
|
|
17903
18022
|
// src/actions/make-model.ts
|
|
17904
18023
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
17905
18024
|
import { dirname as dirname7, join as join7 } from "path";
|
|
17906
|
-
import
|
|
18025
|
+
import process23 from "process";
|
|
17907
18026
|
function findWorkspaceRoot(startPath) {
|
|
17908
18027
|
let currentPath = startPath;
|
|
17909
18028
|
while (currentPath !== dirname7(currentPath)) {
|
|
@@ -17912,10 +18031,10 @@ function findWorkspaceRoot(startPath) {
|
|
|
17912
18031
|
}
|
|
17913
18032
|
currentPath = dirname7(currentPath);
|
|
17914
18033
|
}
|
|
17915
|
-
return
|
|
18034
|
+
return process23.cwd();
|
|
17916
18035
|
}
|
|
17917
18036
|
async function makeModel(name, options = {}) {
|
|
17918
|
-
const workspaceRoot = findWorkspaceRoot(
|
|
18037
|
+
const workspaceRoot = findWorkspaceRoot(process23.cwd());
|
|
17919
18038
|
const modelsDir = options.dir || join7(workspaceRoot, "app/Models");
|
|
17920
18039
|
if (!existsSync14(modelsDir)) {
|
|
17921
18040
|
mkdirSync7(modelsDir, { recursive: true });
|
|
@@ -17958,6 +18077,12 @@ export default model
|
|
|
17958
18077
|
}
|
|
17959
18078
|
var init_make_model = () => {};
|
|
17960
18079
|
|
|
18080
|
+
// src/drivers/index-name.ts
|
|
18081
|
+
function qualifiedIndexName(tableName, indexName) {
|
|
18082
|
+
const prefix = `${tableName}_`;
|
|
18083
|
+
return indexName.startsWith(prefix) ? indexName : `${prefix}${indexName}`;
|
|
18084
|
+
}
|
|
18085
|
+
|
|
17961
18086
|
// src/drivers/mysql.ts
|
|
17962
18087
|
class MySQLDriver {
|
|
17963
18088
|
quoteIdentifier(id) {
|
|
@@ -18044,7 +18169,7 @@ class MySQLDriver {
|
|
|
18044
18169
|
throw new Error(`[migrations] Partial indexes (CompositeIndex.where) are not supported on MySQL. Index '${index.name}' on table '${tableName}' uses WHERE clause: ${index.where}`);
|
|
18045
18170
|
}
|
|
18046
18171
|
const kind = index.type === "unique" ? "UNIQUE " : "";
|
|
18047
|
-
const idxName =
|
|
18172
|
+
const idxName = qualifiedIndexName(tableName, index.name);
|
|
18048
18173
|
const columns = index.columns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
18049
18174
|
return `CREATE ${kind}INDEX ${this.quoteIdentifier(idxName)} ON ${this.quoteIdentifier(tableName)} (${columns});`;
|
|
18050
18175
|
}
|
|
@@ -18097,7 +18222,7 @@ class MySQLDriver {
|
|
|
18097
18222
|
return `ALTER TABLE ${this.quoteIdentifier(tableName)} DROP COLUMN ${this.quoteIdentifier(columnName)};`;
|
|
18098
18223
|
}
|
|
18099
18224
|
dropIndex(tableName, indexName) {
|
|
18100
|
-
const fullIndexName =
|
|
18225
|
+
const fullIndexName = qualifiedIndexName(tableName, indexName);
|
|
18101
18226
|
return `DROP INDEX ${this.quoteIdentifier(fullIndexName)} ON ${this.quoteIdentifier(tableName)};`;
|
|
18102
18227
|
}
|
|
18103
18228
|
dropEnumType(_enumTypeName) {
|
|
@@ -18111,7 +18236,7 @@ class MySQLDriver {
|
|
|
18111
18236
|
)`;
|
|
18112
18237
|
}
|
|
18113
18238
|
getExecutedMigrationsQuery() {
|
|
18114
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18239
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18115
18240
|
}
|
|
18116
18241
|
recordMigrationQuery() {
|
|
18117
18242
|
return "INSERT INTO migrations (migration) VALUES (?)";
|
|
@@ -18144,6 +18269,7 @@ class MySQLDriver {
|
|
|
18144
18269
|
return parts.join(" ");
|
|
18145
18270
|
}
|
|
18146
18271
|
}
|
|
18272
|
+
var init_mysql = () => {};
|
|
18147
18273
|
|
|
18148
18274
|
// src/drivers/postgres.ts
|
|
18149
18275
|
class PostgresDriver {
|
|
@@ -18178,7 +18304,7 @@ class PostgresDriver {
|
|
|
18178
18304
|
return "jsonb";
|
|
18179
18305
|
case "enum":
|
|
18180
18306
|
if (column.enumValues && column.enumValues.length > 0) {
|
|
18181
|
-
return this.quoteIdentifier(column.enumTypeName
|
|
18307
|
+
return column.enumTypeName ? this.quoteIdentifier(column.enumTypeName) : "text";
|
|
18182
18308
|
}
|
|
18183
18309
|
return "text";
|
|
18184
18310
|
default:
|
|
@@ -18232,7 +18358,7 @@ class PostgresDriver {
|
|
|
18232
18358
|
}
|
|
18233
18359
|
createIndex(tableName, index) {
|
|
18234
18360
|
const kind = index.type === "unique" ? "UNIQUE " : "";
|
|
18235
|
-
const idxName =
|
|
18361
|
+
const idxName = qualifiedIndexName(tableName, index.name);
|
|
18236
18362
|
const columns = index.columns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
18237
18363
|
const where = index.where ? ` WHERE ${index.where}` : "";
|
|
18238
18364
|
return `CREATE ${kind}INDEX IF NOT EXISTS ${this.quoteIdentifier(idxName)} ON ${this.quoteIdentifier(tableName)} (${columns})${where};`;
|
|
@@ -18260,7 +18386,17 @@ class PostgresDriver {
|
|
|
18260
18386
|
}
|
|
18261
18387
|
modifyColumn(tableName, column) {
|
|
18262
18388
|
const typeSql = this.getColumnType(column);
|
|
18263
|
-
|
|
18389
|
+
const table = this.quoteIdentifier(tableName);
|
|
18390
|
+
const name = this.quoteIdentifier(column.name);
|
|
18391
|
+
const statements = [
|
|
18392
|
+
`ALTER TABLE ${table} ALTER COLUMN ${name} TYPE ${typeSql} USING ${name}::${typeSql};`
|
|
18393
|
+
];
|
|
18394
|
+
if (!column.isPrimaryKey)
|
|
18395
|
+
statements.push(`ALTER TABLE ${table} ALTER COLUMN ${name} ${column.isNullable ? "DROP NOT NULL" : "SET NOT NULL"};`);
|
|
18396
|
+
const defaultValue = this.getDefaultValue(column);
|
|
18397
|
+
statements.push(defaultValue ? `ALTER TABLE ${table} ALTER COLUMN ${name} SET ${defaultValue};` : `ALTER TABLE ${table} ALTER COLUMN ${name} DROP DEFAULT;`);
|
|
18398
|
+
return statements.join(`
|
|
18399
|
+
`);
|
|
18264
18400
|
}
|
|
18265
18401
|
renameColumn(tableName, from, to) {
|
|
18266
18402
|
return `ALTER TABLE ${this.quoteIdentifier(tableName)} RENAME COLUMN ${this.quoteIdentifier(from)} TO ${this.quoteIdentifier(to)};`;
|
|
@@ -18278,7 +18414,7 @@ class PostgresDriver {
|
|
|
18278
18414
|
return `ALTER TABLE ${this.quoteIdentifier(tableName)} DROP COLUMN ${this.quoteIdentifier(columnName)};`;
|
|
18279
18415
|
}
|
|
18280
18416
|
dropIndex(tableName, indexName) {
|
|
18281
|
-
const fullIndexName =
|
|
18417
|
+
const fullIndexName = qualifiedIndexName(tableName, indexName);
|
|
18282
18418
|
return `DROP INDEX IF EXISTS ${this.quoteIdentifier(fullIndexName)};`;
|
|
18283
18419
|
}
|
|
18284
18420
|
dropEnumType(enumTypeName) {
|
|
@@ -18292,7 +18428,7 @@ class PostgresDriver {
|
|
|
18292
18428
|
)`;
|
|
18293
18429
|
}
|
|
18294
18430
|
getExecutedMigrationsQuery() {
|
|
18295
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18431
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18296
18432
|
}
|
|
18297
18433
|
recordMigrationQuery() {
|
|
18298
18434
|
return "INSERT INTO migrations (migration) VALUES ($1)";
|
|
@@ -18321,10 +18457,12 @@ class PostgresDriver {
|
|
|
18321
18457
|
return parts.join(" ");
|
|
18322
18458
|
}
|
|
18323
18459
|
}
|
|
18460
|
+
var init_postgres = () => {};
|
|
18324
18461
|
|
|
18325
18462
|
// src/drivers/singlestore.ts
|
|
18326
18463
|
var SingleStoreDriver;
|
|
18327
18464
|
var init_singlestore = __esm(() => {
|
|
18465
|
+
init_mysql();
|
|
18328
18466
|
SingleStoreDriver = class SingleStoreDriver extends MySQLDriver {
|
|
18329
18467
|
createTable(table) {
|
|
18330
18468
|
const columns = table.columns.map((c) => this.renderColumn(c)).join(`,
|
|
@@ -18458,7 +18596,7 @@ class SQLiteDriver {
|
|
|
18458
18596
|
}
|
|
18459
18597
|
createIndex(tableName, index) {
|
|
18460
18598
|
const kind = index.type === "unique" ? "UNIQUE " : "";
|
|
18461
|
-
const idxName =
|
|
18599
|
+
const idxName = qualifiedIndexName(tableName, index.name);
|
|
18462
18600
|
const columns = index.columns.map((c) => this.quoteIdentifier(c)).join(", ");
|
|
18463
18601
|
const where = index.where ? ` WHERE ${index.where}` : "";
|
|
18464
18602
|
return `CREATE ${kind}INDEX IF NOT EXISTS ${this.quoteIdentifier(idxName)} ON ${this.quoteIdentifier(tableName)} (${columns})${where};`;
|
|
@@ -18523,7 +18661,7 @@ class SQLiteDriver {
|
|
|
18523
18661
|
return `ALTER TABLE ${this.quoteIdentifier(tableName)} DROP COLUMN ${this.quoteIdentifier(columnName)};`;
|
|
18524
18662
|
}
|
|
18525
18663
|
dropIndex(tableName, indexName) {
|
|
18526
|
-
const fullIndexName =
|
|
18664
|
+
const fullIndexName = qualifiedIndexName(tableName, indexName);
|
|
18527
18665
|
return `DROP INDEX IF EXISTS ${this.quoteIdentifier(fullIndexName)};`;
|
|
18528
18666
|
}
|
|
18529
18667
|
dropEnumType(_enumTypeName) {
|
|
@@ -18537,7 +18675,7 @@ class SQLiteDriver {
|
|
|
18537
18675
|
)`;
|
|
18538
18676
|
}
|
|
18539
18677
|
getExecutedMigrationsQuery() {
|
|
18540
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18678
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18541
18679
|
}
|
|
18542
18680
|
recordMigrationQuery() {
|
|
18543
18681
|
return "INSERT INTO migrations (migration) VALUES (?)";
|
|
@@ -18569,6 +18707,7 @@ class SQLiteDriver {
|
|
|
18569
18707
|
return parts.join(" ");
|
|
18570
18708
|
}
|
|
18571
18709
|
}
|
|
18710
|
+
var init_sqlite = () => {};
|
|
18572
18711
|
|
|
18573
18712
|
// src/drivers/index.ts
|
|
18574
18713
|
function getDialectDriver(dialect) {
|
|
@@ -18586,10 +18725,34 @@ function getDialectDriver(dialect) {
|
|
|
18586
18725
|
}
|
|
18587
18726
|
}
|
|
18588
18727
|
var init_drivers = __esm(() => {
|
|
18728
|
+
init_mysql();
|
|
18729
|
+
init_postgres();
|
|
18589
18730
|
init_singlestore();
|
|
18731
|
+
init_sqlite();
|
|
18732
|
+
init_mysql();
|
|
18733
|
+
init_postgres();
|
|
18590
18734
|
init_singlestore();
|
|
18735
|
+
init_sqlite();
|
|
18591
18736
|
});
|
|
18592
18737
|
|
|
18738
|
+
// src/workspace.ts
|
|
18739
|
+
import { existsSync as existsSync16 } from "fs";
|
|
18740
|
+
import { dirname as dirname8, join as join9 } from "path";
|
|
18741
|
+
import process24 from "process";
|
|
18742
|
+
function findWorkspaceRoot2(startPath = process24.cwd()) {
|
|
18743
|
+
let currentPath = startPath;
|
|
18744
|
+
while (currentPath !== dirname8(currentPath)) {
|
|
18745
|
+
if (existsSync16(join9(currentPath, "package.json")))
|
|
18746
|
+
return currentPath;
|
|
18747
|
+
currentPath = dirname8(currentPath);
|
|
18748
|
+
}
|
|
18749
|
+
return process24.cwd();
|
|
18750
|
+
}
|
|
18751
|
+
function getSqlDirectory(workspaceRoot) {
|
|
18752
|
+
return join9(workspaceRoot ?? findWorkspaceRoot2(), "database", "migrations");
|
|
18753
|
+
}
|
|
18754
|
+
var init_workspace = () => {};
|
|
18755
|
+
|
|
18593
18756
|
// src/actions/migrate.ts
|
|
18594
18757
|
var exports_migrate = {};
|
|
18595
18758
|
__export(exports_migrate, {
|
|
@@ -18601,23 +18764,48 @@ __export(exports_migrate, {
|
|
|
18601
18764
|
copyModelsToGenerated: () => copyModelsToGenerated,
|
|
18602
18765
|
clearGeneratedDirectory: () => clearGeneratedDirectory
|
|
18603
18766
|
});
|
|
18604
|
-
import { existsSync as
|
|
18767
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync3, rmSync, unlinkSync, writeFileSync as writeFileSync11 } from "fs";
|
|
18605
18768
|
import { tmpdir } from "os";
|
|
18606
|
-
import { join as
|
|
18769
|
+
import { join as join10 } from "path";
|
|
18607
18770
|
import process25 from "process";
|
|
18771
|
+
async function settleAppliedFiles(qb, dialect, files) {
|
|
18772
|
+
if (files.length === 0)
|
|
18773
|
+
return;
|
|
18774
|
+
const sqlDir = ensureSqlDirectory(getWorkspaceRoot());
|
|
18775
|
+
try {
|
|
18776
|
+
await createMigrationsTable(qb, dialect);
|
|
18777
|
+
for (const file2 of files) {
|
|
18778
|
+
await recordMigration(qb, file2, dialect);
|
|
18779
|
+
info(`-- \u2713 Recorded applied migration: ${file2}`);
|
|
18780
|
+
}
|
|
18781
|
+
} catch (err) {
|
|
18782
|
+
info(`-- Could not record applied migrations (the schema change itself succeeded): ${err instanceof Error ? err.message : String(err)}`);
|
|
18783
|
+
}
|
|
18784
|
+
}
|
|
18785
|
+
function isGeneratedMigration(sqlDir, file2) {
|
|
18786
|
+
try {
|
|
18787
|
+
return isGeneratedMigrationSql(readFileSync3(join10(sqlDir, file2), "utf8"));
|
|
18788
|
+
} catch {
|
|
18789
|
+
return false;
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
function isAlreadyAppliedError(err) {
|
|
18793
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18794
|
+
return /duplicate column name/i.test(message) || /duplicate key name/i.test(message) || /\balready exists\b/i.test(message);
|
|
18795
|
+
}
|
|
18608
18796
|
function info(message) {
|
|
18609
18797
|
if (config5.verbose)
|
|
18610
18798
|
console.log(message);
|
|
18611
18799
|
}
|
|
18612
18800
|
function getSnapshotDir(workspaceRoot) {
|
|
18613
|
-
return
|
|
18801
|
+
return join10(workspaceRoot, config5.snapshotDir || ".qb");
|
|
18614
18802
|
}
|
|
18615
18803
|
function getSnapshotPath(workspaceRoot, dialect) {
|
|
18616
|
-
return
|
|
18804
|
+
return join10(getSnapshotDir(workspaceRoot), `model-snapshot.${dialect}.json`);
|
|
18617
18805
|
}
|
|
18618
18806
|
function loadPlanSnapshot(workspaceRoot, dialect) {
|
|
18619
18807
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18620
|
-
if (!
|
|
18808
|
+
if (!existsSync17(snapshotPath)) {
|
|
18621
18809
|
return;
|
|
18622
18810
|
}
|
|
18623
18811
|
try {
|
|
@@ -18640,7 +18828,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
18640
18828
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18641
18829
|
const snapshotDir = getSnapshotDir(workspaceRoot);
|
|
18642
18830
|
const hash = hashMigrationPlan(plan);
|
|
18643
|
-
if (
|
|
18831
|
+
if (existsSync17(snapshotPath)) {
|
|
18644
18832
|
try {
|
|
18645
18833
|
const current = JSON.parse(readFileSync3(snapshotPath, "utf8"));
|
|
18646
18834
|
if (current?.hash === hash && current?.dialect === dialect && current?.plan) {
|
|
@@ -18649,7 +18837,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
18649
18837
|
}
|
|
18650
18838
|
} catch {}
|
|
18651
18839
|
}
|
|
18652
|
-
if (!
|
|
18840
|
+
if (!existsSync17(snapshotDir)) {
|
|
18653
18841
|
mkdirSync8(snapshotDir, { recursive: true });
|
|
18654
18842
|
info(`-- Created snapshot directory: ${snapshotDir}`);
|
|
18655
18843
|
}
|
|
@@ -18667,11 +18855,11 @@ function saveMigrationSnapshot(plan, options = {}) {
|
|
|
18667
18855
|
savePlanSnapshot(options.workspaceRoot ?? getWorkspaceRoot(), dialect, plan);
|
|
18668
18856
|
}
|
|
18669
18857
|
function getWorkspaceRoot() {
|
|
18670
|
-
return
|
|
18858
|
+
return findWorkspaceRoot2();
|
|
18671
18859
|
}
|
|
18672
18860
|
function ensureSqlDirectory(workspaceRoot) {
|
|
18673
18861
|
const sqlDir = getSqlDirectory(workspaceRoot);
|
|
18674
|
-
if (!
|
|
18862
|
+
if (!existsSync17(sqlDir)) {
|
|
18675
18863
|
mkdirSync8(sqlDir, { recursive: true });
|
|
18676
18864
|
info(`-- Created SQL directory: ${sqlDir}`);
|
|
18677
18865
|
}
|
|
@@ -18680,21 +18868,22 @@ function ensureSqlDirectory(workspaceRoot) {
|
|
|
18680
18868
|
async function generateMigration(dir, opts = {}) {
|
|
18681
18869
|
await getConfig();
|
|
18682
18870
|
if (!dir) {
|
|
18683
|
-
dir =
|
|
18871
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18684
18872
|
}
|
|
18685
18873
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
18686
18874
|
const workspaceRoot = getWorkspaceRoot();
|
|
18687
18875
|
const models = await loadModels({ modelsDir: dir });
|
|
18688
18876
|
const plan = buildMigrationPlan2(models, { dialect });
|
|
18689
18877
|
let previous;
|
|
18878
|
+
let reconciledFromDatabase = false;
|
|
18690
18879
|
if (!opts.full) {
|
|
18691
18880
|
previous = loadPlanSnapshot(workspaceRoot, dialect);
|
|
18692
18881
|
if (previous) {
|
|
18693
18882
|
info("-- Comparing with stored model snapshot");
|
|
18694
18883
|
} else {
|
|
18695
|
-
const defaultStatePath =
|
|
18884
|
+
const defaultStatePath = join10(dir, `.qb-migrations.${dialect}.json`);
|
|
18696
18885
|
const statePath = String(opts.state || defaultStatePath);
|
|
18697
|
-
if (
|
|
18886
|
+
if (existsSync17(statePath)) {
|
|
18698
18887
|
try {
|
|
18699
18888
|
const raw2 = readFileSync3(statePath, "utf8");
|
|
18700
18889
|
const parsed = JSON.parse(raw2);
|
|
@@ -18711,6 +18900,7 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18711
18900
|
const scoped = livePlan.tables.filter((t) => modelTables.has(t.table));
|
|
18712
18901
|
if (scoped.length > 0) {
|
|
18713
18902
|
previous = { dialect: livePlan.dialect, tables: scoped };
|
|
18903
|
+
reconciledFromDatabase = true;
|
|
18714
18904
|
info(`-- No snapshot; reconciling against live database (${scoped.length} matching table${scoped.length === 1 ? "" : "s"})`);
|
|
18715
18905
|
}
|
|
18716
18906
|
} catch (err) {
|
|
@@ -18724,25 +18914,34 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18724
18914
|
} else {
|
|
18725
18915
|
info("-- Full migration requested, ignoring any previous state");
|
|
18726
18916
|
}
|
|
18727
|
-
const diff = opts.full ? { statements: generateSql(plan, { dryRun: opts.dryRun }), operations: plan.tables.map((t) => ({ kind: "create_table", table: t.table, destructive: false, sql: "" })) } : generateDiffOperations(previous, plan, {
|
|
18917
|
+
const diff = opts.full ? { statements: generateSql(plan, { dryRun: opts.dryRun }), operations: plan.tables.map((t) => ({ kind: "create_table", table: t.table, destructive: false, sql: "" })) } : generateDiffOperations(previous, plan, {
|
|
18918
|
+
applyRenames: opts.applyRenames,
|
|
18919
|
+
dryRun: opts.dryRun,
|
|
18920
|
+
preserveUnknownColumns: reconciledFromDatabase
|
|
18921
|
+
});
|
|
18728
18922
|
const sqlStatements = diff.statements;
|
|
18729
18923
|
const operations = diff.operations;
|
|
18730
18924
|
const sql = sqlStatements.join(`
|
|
18731
18925
|
`);
|
|
18732
18926
|
const hasChanges = sqlStatements.some((stmt) => /\b(?:CREATE|ALTER|DROP)\b/i.test(stmt));
|
|
18733
18927
|
if (opts.apply) {
|
|
18734
|
-
const dirPath = mkdtempSync(
|
|
18735
|
-
const filePath =
|
|
18928
|
+
const dirPath = mkdtempSync(join10(tmpdir(), "qb-migrate-"));
|
|
18929
|
+
const filePath = join10(dirPath, "migration.sql");
|
|
18736
18930
|
try {
|
|
18737
18931
|
if (hasChanges) {
|
|
18738
18932
|
writeFileSync11(filePath, sql);
|
|
18933
|
+
const qb = createQueryBuilder();
|
|
18934
|
+
await qb.file(filePath);
|
|
18739
18935
|
info("-- Migration applied");
|
|
18936
|
+
await settleAppliedFiles(qb, dialect, lastCreatedMigrationFiles());
|
|
18740
18937
|
} else {
|
|
18741
18938
|
info("-- No changes; nothing to apply");
|
|
18742
18939
|
}
|
|
18743
18940
|
} catch (err) {
|
|
18744
18941
|
console.error("-- Migration failed:", err);
|
|
18745
18942
|
throw err;
|
|
18943
|
+
} finally {
|
|
18944
|
+
rmSync(dirPath, { recursive: true, force: true });
|
|
18746
18945
|
}
|
|
18747
18946
|
}
|
|
18748
18947
|
if (!opts.dryRun)
|
|
@@ -18751,9 +18950,6 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18751
18950
|
}
|
|
18752
18951
|
async function executeMigration(dir) {
|
|
18753
18952
|
await getConfig();
|
|
18754
|
-
if (!dir) {
|
|
18755
|
-
dir = join9(process25.cwd(), "app/Models");
|
|
18756
|
-
}
|
|
18757
18953
|
const workspaceRoot = getWorkspaceRoot();
|
|
18758
18954
|
const sqlDir = ensureSqlDirectory(workspaceRoot);
|
|
18759
18955
|
const dialect = config5.dialect || "postgres";
|
|
@@ -18768,42 +18964,25 @@ async function executeMigration(dir) {
|
|
|
18768
18964
|
const qb = createQueryBuilder();
|
|
18769
18965
|
await createMigrationsTable(qb, dialect);
|
|
18770
18966
|
const executedMigrations = await getExecutedMigrations(qb, dialect);
|
|
18771
|
-
const
|
|
18772
|
-
|
|
18773
|
-
for (const file2 of scriptFiles) {
|
|
18774
|
-
if (file2.includes("alter-") && file2.includes("-table")) {
|
|
18775
|
-
transientMigrations.push(file2);
|
|
18776
|
-
} else if (!executedMigrations.includes(file2)) {
|
|
18777
|
-
permanentMigrations.push(file2);
|
|
18778
|
-
}
|
|
18779
|
-
}
|
|
18780
|
-
const totalPending = permanentMigrations.length + transientMigrations.length;
|
|
18781
|
-
if (totalPending === 0) {
|
|
18967
|
+
const pending = scriptFiles.filter((file2) => !executedMigrations.includes(file2));
|
|
18968
|
+
if (pending.length === 0) {
|
|
18782
18969
|
info("-- No pending migrations to execute");
|
|
18783
18970
|
return true;
|
|
18784
18971
|
}
|
|
18785
|
-
info(`-- Executing ${
|
|
18786
|
-
for (const file2 of
|
|
18787
|
-
const filePath =
|
|
18972
|
+
info(`-- Executing ${pending.length} migration${pending.length === 1 ? "" : "s"}`);
|
|
18973
|
+
for (const file2 of pending) {
|
|
18974
|
+
const filePath = join10(sqlDir, file2);
|
|
18788
18975
|
info(`-- Executing: ${file2}`);
|
|
18789
18976
|
try {
|
|
18790
18977
|
await qb.file(filePath);
|
|
18791
18978
|
await recordMigration(qb, file2, dialect);
|
|
18792
18979
|
info(`-- \u2713 Migration ${file2} executed and recorded`);
|
|
18793
18980
|
} catch (err) {
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
|
|
18799
|
-
const filePath = join9(sqlDir, file2);
|
|
18800
|
-
info(`-- Executing: ${file2} (transient)`);
|
|
18801
|
-
try {
|
|
18802
|
-
await qb.file(filePath);
|
|
18803
|
-
info(`-- \u2713 Migration ${file2} executed (not recorded)`);
|
|
18804
|
-
unlinkSync(filePath);
|
|
18805
|
-
info(`-- \uD83D\uDDD1\uFE0F Deleted transient migration: ${file2}`);
|
|
18806
|
-
} catch (err) {
|
|
18981
|
+
if (isGeneratedMigration(sqlDir, file2) && isAlreadyAppliedError(err)) {
|
|
18982
|
+
await recordMigration(qb, file2, dialect);
|
|
18983
|
+
info(`-- \u2713 Migration ${file2} was already applied; recorded it (${err instanceof Error ? err.message : String(err)})`);
|
|
18984
|
+
continue;
|
|
18985
|
+
}
|
|
18807
18986
|
console.error(`-- \u2717 Migration ${file2} failed:`, err);
|
|
18808
18987
|
throw err;
|
|
18809
18988
|
}
|
|
@@ -18818,7 +18997,7 @@ async function executeMigration(dir) {
|
|
|
18818
18997
|
async function resetDatabase(dir, opts = {}) {
|
|
18819
18998
|
await getConfig();
|
|
18820
18999
|
if (!dir) {
|
|
18821
|
-
dir =
|
|
19000
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18822
19001
|
}
|
|
18823
19002
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
18824
19003
|
const driver = getDialectDriver(dialect);
|
|
@@ -18915,42 +19094,50 @@ async function resetDatabase(dir, opts = {}) {
|
|
|
18915
19094
|
}
|
|
18916
19095
|
async function deleteMigrationFiles(dir, workspaceRoot, opts = {}) {
|
|
18917
19096
|
if (!dir) {
|
|
18918
|
-
dir =
|
|
19097
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18919
19098
|
}
|
|
18920
19099
|
if (!workspaceRoot) {
|
|
18921
19100
|
workspaceRoot = getWorkspaceRoot();
|
|
18922
19101
|
}
|
|
18923
19102
|
const dialect = String(opts.dialect || config5.dialect || "postgres");
|
|
18924
19103
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18925
|
-
if (
|
|
19104
|
+
if (existsSync17(snapshotPath)) {
|
|
18926
19105
|
unlinkSync(snapshotPath);
|
|
18927
19106
|
info(`-- Removed model snapshot file: ${snapshotPath}`);
|
|
18928
19107
|
}
|
|
18929
|
-
const defaultStatePath =
|
|
19108
|
+
const defaultStatePath = join10(dir, `.qb-migrations.${dialect}.json`);
|
|
18930
19109
|
const statePath = String(opts.state || defaultStatePath);
|
|
18931
|
-
if (
|
|
19110
|
+
if (existsSync17(statePath)) {
|
|
18932
19111
|
unlinkSync(statePath);
|
|
18933
19112
|
info(`-- Removed legacy migration state file: ${statePath}`);
|
|
18934
19113
|
}
|
|
18935
|
-
|
|
18936
|
-
|
|
18937
|
-
|
|
18938
|
-
|
|
18939
|
-
|
|
18940
|
-
|
|
18941
|
-
|
|
18942
|
-
|
|
19114
|
+
removeGeneratedMigrationFiles(getSqlDirectory(workspaceRoot));
|
|
19115
|
+
}
|
|
19116
|
+
function removeGeneratedMigrationFiles(sqlDir) {
|
|
19117
|
+
if (!existsSync17(sqlDir))
|
|
19118
|
+
return;
|
|
19119
|
+
let removed = 0;
|
|
19120
|
+
const kept = [];
|
|
19121
|
+
for (const file2 of readdirSync6(sqlDir).filter((f) => f.endsWith(".sql"))) {
|
|
19122
|
+
if (!isGeneratedMigration(sqlDir, file2)) {
|
|
19123
|
+
kept.push(file2);
|
|
19124
|
+
continue;
|
|
18943
19125
|
}
|
|
18944
|
-
|
|
19126
|
+
unlinkSync(join10(sqlDir, file2));
|
|
19127
|
+
info(`-- Removed generated migration file: ${file2}`);
|
|
19128
|
+
removed += 1;
|
|
18945
19129
|
}
|
|
19130
|
+
info(`-- Cleaned up ${removed} generated migration file${removed === 1 ? "" : "s"} from migrations directory`);
|
|
19131
|
+
if (kept.length > 0)
|
|
19132
|
+
info(`-- Kept ${kept.length} hand-written migration${kept.length === 1 ? "" : "s"}: ${kept.slice(0, 5).join(", ")}${kept.length > 5 ? `, +${kept.length - 5} more` : ""}`);
|
|
18946
19133
|
}
|
|
18947
19134
|
async function copyModelsToGenerated(_dir, _workspaceRoot) {}
|
|
18948
19135
|
async function clearGeneratedDirectory(workspaceRoot) {
|
|
18949
19136
|
if (!workspaceRoot) {
|
|
18950
19137
|
workspaceRoot = getWorkspaceRoot();
|
|
18951
19138
|
}
|
|
18952
|
-
const generatedDir =
|
|
18953
|
-
if (
|
|
19139
|
+
const generatedDir = join10(workspaceRoot, "generated");
|
|
19140
|
+
if (existsSync17(generatedDir)) {
|
|
18954
19141
|
try {
|
|
18955
19142
|
rmSync(generatedDir, { recursive: true, force: true });
|
|
18956
19143
|
info("-- Cleared generated directory");
|
|
@@ -18959,12 +19146,6 @@ async function clearGeneratedDirectory(workspaceRoot) {
|
|
|
18959
19146
|
}
|
|
18960
19147
|
}
|
|
18961
19148
|
}
|
|
18962
|
-
function getSqlDirectory(workspaceRoot) {
|
|
18963
|
-
if (!workspaceRoot) {
|
|
18964
|
-
workspaceRoot = getWorkspaceRoot();
|
|
18965
|
-
}
|
|
18966
|
-
return join9(workspaceRoot, "database", "migrations");
|
|
18967
|
-
}
|
|
18968
19149
|
async function createMigrationsTable(qb, dialect) {
|
|
18969
19150
|
const driver = getDialectDriver(dialect);
|
|
18970
19151
|
const createTableSql = driver.createMigrationsTable();
|
|
@@ -19003,6 +19184,7 @@ var init_migrate = __esm(() => {
|
|
|
19003
19184
|
init_drivers();
|
|
19004
19185
|
init_src2();
|
|
19005
19186
|
init_introspect_db();
|
|
19187
|
+
init_workspace();
|
|
19006
19188
|
});
|
|
19007
19189
|
|
|
19008
19190
|
// src/actions/migrate-generate.ts
|
|
@@ -19014,28 +19196,49 @@ var init_migrate_generate = __esm(() => {
|
|
|
19014
19196
|
});
|
|
19015
19197
|
|
|
19016
19198
|
// src/actions/migrate-rollback.ts
|
|
19017
|
-
import { existsSync as
|
|
19018
|
-
import {
|
|
19019
|
-
import process26 from "process";
|
|
19199
|
+
import { existsSync as existsSync18, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
19200
|
+
import { join as join11 } from "path";
|
|
19020
19201
|
function splitSqlStatements2(sql) {
|
|
19021
19202
|
const out = [];
|
|
19022
19203
|
let buf = "";
|
|
19023
|
-
let
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19031
|
-
|
|
19032
|
-
|
|
19204
|
+
let quote = null;
|
|
19205
|
+
for (let i = 0;i < sql.length; i++) {
|
|
19206
|
+
const ch = sql[i];
|
|
19207
|
+
if (quote) {
|
|
19208
|
+
buf += ch;
|
|
19209
|
+
if (ch === quote) {
|
|
19210
|
+
if (sql[i + 1] === quote) {
|
|
19211
|
+
buf += sql[i + 1];
|
|
19212
|
+
i++;
|
|
19213
|
+
} else {
|
|
19214
|
+
quote = null;
|
|
19215
|
+
}
|
|
19216
|
+
}
|
|
19217
|
+
continue;
|
|
19218
|
+
}
|
|
19219
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
19220
|
+
quote = ch;
|
|
19221
|
+
buf += ch;
|
|
19222
|
+
continue;
|
|
19223
|
+
}
|
|
19224
|
+
if (ch === "-" && sql[i + 1] === "-") {
|
|
19225
|
+
const newline = sql.indexOf(`
|
|
19226
|
+
`, i);
|
|
19227
|
+
i = newline === -1 ? sql.length : newline;
|
|
19228
|
+
continue;
|
|
19229
|
+
}
|
|
19230
|
+
if (ch === "/" && sql[i + 1] === "*") {
|
|
19231
|
+
const close = sql.indexOf("*/", i + 2);
|
|
19232
|
+
i = close === -1 ? sql.length : close + 1;
|
|
19233
|
+
continue;
|
|
19234
|
+
}
|
|
19235
|
+
if (ch === ";") {
|
|
19033
19236
|
if (buf.trim())
|
|
19034
19237
|
out.push(buf.trim());
|
|
19035
19238
|
buf = "";
|
|
19036
|
-
|
|
19037
|
-
buf += ch;
|
|
19239
|
+
continue;
|
|
19038
19240
|
}
|
|
19241
|
+
buf += ch;
|
|
19039
19242
|
}
|
|
19040
19243
|
if (buf.trim())
|
|
19041
19244
|
out.push(buf.trim());
|
|
@@ -19049,8 +19252,20 @@ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
|
19049
19252
|
let m;
|
|
19050
19253
|
if (m = /^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
|
|
19051
19254
|
down.push(`DROP TABLE IF EXISTS ${q(m[1])}`);
|
|
19052
|
-
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+
|
|
19053
|
-
|
|
19255
|
+
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+CONSTRAINT\s+["`']?(\w+)["`']?([\s\S]*)$/i.exec(stmt)) {
|
|
19256
|
+
if (dialect === "sqlite") {
|
|
19257
|
+
skipped.push(stmt);
|
|
19258
|
+
} else if (isMysqlLike(dialect) && /\bFOREIGN\s+KEY\b/i.test(m[3] ?? "")) {
|
|
19259
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP FOREIGN KEY ${q(m[2])}`);
|
|
19260
|
+
} else {
|
|
19261
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP CONSTRAINT ${q(m[2])}`);
|
|
19262
|
+
}
|
|
19263
|
+
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+(?:COLUMN\s+["`']?(\w+)["`']?|["`'](\w+)["`']|(\w+))/i.exec(stmt)) {
|
|
19264
|
+
const column = m[2] ?? m[3] ?? m[4];
|
|
19265
|
+
if (m[4] && /^(?:PRIMARY|UNIQUE|INDEX|KEY|FOREIGN|CHECK|FULLTEXT|SPATIAL)$/i.test(m[4]))
|
|
19266
|
+
skipped.push(stmt);
|
|
19267
|
+
else
|
|
19268
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP COLUMN ${q(column)}`);
|
|
19054
19269
|
} else if (m = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?(?:\s+ON\s+["`']?(\w+)["`']?)?/i.exec(stmt)) {
|
|
19055
19270
|
down.push(isMysqlLike(dialect) && m[2] ? `DROP INDEX ${q(m[1])} ON ${q(m[2])}` : `DROP INDEX IF EXISTS ${q(m[1])}`);
|
|
19056
19271
|
} else {
|
|
@@ -19059,22 +19274,6 @@ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
|
19059
19274
|
}
|
|
19060
19275
|
return { down: down.reverse(), skipped };
|
|
19061
19276
|
}
|
|
19062
|
-
function findWorkspaceRoot2(startPath) {
|
|
19063
|
-
let currentPath = startPath;
|
|
19064
|
-
while (currentPath !== dirname8(currentPath)) {
|
|
19065
|
-
if (existsSync17(join10(currentPath, "package.json"))) {
|
|
19066
|
-
return currentPath;
|
|
19067
|
-
}
|
|
19068
|
-
currentPath = dirname8(currentPath);
|
|
19069
|
-
}
|
|
19070
|
-
return process26.cwd();
|
|
19071
|
-
}
|
|
19072
|
-
function getSqlDirectory2(workspaceRoot) {
|
|
19073
|
-
if (!workspaceRoot) {
|
|
19074
|
-
workspaceRoot = findWorkspaceRoot2(process26.cwd());
|
|
19075
|
-
}
|
|
19076
|
-
return join10(workspaceRoot, "database", "migrations");
|
|
19077
|
-
}
|
|
19078
19277
|
async function migrateRollback(options = {}) {
|
|
19079
19278
|
const steps = options.steps || 1;
|
|
19080
19279
|
const reverseSchema = options.reverseSchema !== false;
|
|
@@ -19107,12 +19306,12 @@ async function migrateRollback(options = {}) {
|
|
|
19107
19306
|
console.log(` - ${migration.migration}`);
|
|
19108
19307
|
}
|
|
19109
19308
|
console.log();
|
|
19110
|
-
const sqlDir =
|
|
19309
|
+
const sqlDir = getSqlDirectory();
|
|
19111
19310
|
let reversedAny = false;
|
|
19112
19311
|
for (const migration of migrationsToRollback) {
|
|
19113
19312
|
try {
|
|
19114
|
-
const filePath =
|
|
19115
|
-
if (reverseSchema &&
|
|
19313
|
+
const filePath = join11(sqlDir, migration.migration);
|
|
19314
|
+
if (reverseSchema && existsSync18(filePath)) {
|
|
19116
19315
|
const forwardSql = readFileSync4(filePath, "utf8");
|
|
19117
19316
|
const { down, skipped } = deriveDownStatements(forwardSql);
|
|
19118
19317
|
for (const stmt of down) {
|
|
@@ -19123,10 +19322,10 @@ async function migrateRollback(options = {}) {
|
|
|
19123
19322
|
if (skipped.length > 0)
|
|
19124
19323
|
console.log(`-- \u26A0\uFE0F ${skipped.length} statement(s) in ${migration.migration} could not be auto-reversed (data/complex DDL) \u2014 reverse manually.`);
|
|
19125
19324
|
}
|
|
19126
|
-
const deleteSql = `DELETE FROM migrations WHERE migration = $1`;
|
|
19325
|
+
const deleteSql = `DELETE FROM migrations WHERE migration = ${getPlaceholder(1)}`;
|
|
19127
19326
|
await qb.unsafe(deleteSql, [migration.migration]);
|
|
19128
19327
|
console.log(`-- \u2713 Removed migration record: ${migration.migration}`);
|
|
19129
|
-
if (
|
|
19328
|
+
if (options.deleteFiles && existsSync18(filePath)) {
|
|
19130
19329
|
unlinkSync2(filePath);
|
|
19131
19330
|
console.log(`-- \uD83D\uDDD1\uFE0F Deleted migration file: ${migration.migration}`);
|
|
19132
19331
|
}
|
|
@@ -19149,37 +19348,20 @@ async function migrateRollback(options = {}) {
|
|
|
19149
19348
|
var init_migrate_rollback = __esm(() => {
|
|
19150
19349
|
init_config();
|
|
19151
19350
|
init_src2();
|
|
19351
|
+
init_workspace();
|
|
19152
19352
|
});
|
|
19153
19353
|
|
|
19154
19354
|
// src/actions/migrate-status.ts
|
|
19155
|
-
import { existsSync as
|
|
19156
|
-
import { dirname as dirname9, join as join11 } from "path";
|
|
19157
|
-
import process27 from "process";
|
|
19158
|
-
function findWorkspaceRoot3(startPath) {
|
|
19159
|
-
let currentPath = startPath;
|
|
19160
|
-
while (currentPath !== dirname9(currentPath)) {
|
|
19161
|
-
if (existsSync18(join11(currentPath, "package.json"))) {
|
|
19162
|
-
return currentPath;
|
|
19163
|
-
}
|
|
19164
|
-
currentPath = dirname9(currentPath);
|
|
19165
|
-
}
|
|
19166
|
-
return process27.cwd();
|
|
19167
|
-
}
|
|
19168
|
-
function getSqlDirectory3(workspaceRoot) {
|
|
19169
|
-
if (!workspaceRoot) {
|
|
19170
|
-
workspaceRoot = findWorkspaceRoot3(process27.cwd());
|
|
19171
|
-
}
|
|
19172
|
-
return join11(workspaceRoot, "database", "migrations");
|
|
19173
|
-
}
|
|
19355
|
+
import { existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
|
|
19174
19356
|
async function migrateStatus() {
|
|
19175
19357
|
const dialect = config5.dialect || "postgres";
|
|
19176
19358
|
const driver = getDialectDriver(dialect);
|
|
19177
|
-
const sqlDir =
|
|
19359
|
+
const sqlDir = getSqlDirectory();
|
|
19178
19360
|
console.log("-- Migration Status");
|
|
19179
19361
|
console.log(`-- Dialect: ${dialect}`);
|
|
19180
19362
|
console.log(`-- SQL directory: ${sqlDir}`);
|
|
19181
19363
|
console.log();
|
|
19182
|
-
if (!
|
|
19364
|
+
if (!existsSync19(sqlDir)) {
|
|
19183
19365
|
console.log("-- No SQL directory found. No migrations have been created yet.");
|
|
19184
19366
|
return [];
|
|
19185
19367
|
}
|
|
@@ -19200,27 +19382,16 @@ async function migrateStatus() {
|
|
|
19200
19382
|
console.log();
|
|
19201
19383
|
}
|
|
19202
19384
|
const executedMap = new Map(executedMigrations.map((m) => [m.migration, m.executed_at]));
|
|
19203
|
-
const statuses = migrationFiles.map((file2) => {
|
|
19204
|
-
|
|
19205
|
-
|
|
19206
|
-
|
|
19207
|
-
|
|
19208
|
-
};
|
|
19209
|
-
}
|
|
19210
|
-
const executedAt = executedMap.get(file2);
|
|
19211
|
-
return {
|
|
19212
|
-
file: file2,
|
|
19213
|
-
status: executedAt ? "executed" : "pending",
|
|
19214
|
-
executedAt
|
|
19215
|
-
};
|
|
19216
|
-
});
|
|
19385
|
+
const statuses = migrationFiles.map((file2) => ({
|
|
19386
|
+
file: file2,
|
|
19387
|
+
status: executedMap.has(file2) ? "executed" : "pending",
|
|
19388
|
+
executedAt: executedMap.get(file2)
|
|
19389
|
+
}));
|
|
19217
19390
|
const executed = statuses.filter((s) => s.status === "executed");
|
|
19218
19391
|
const pending = statuses.filter((s) => s.status === "pending");
|
|
19219
|
-
const transient = statuses.filter((s) => s.status === "transient");
|
|
19220
19392
|
console.log(`-- Total migrations: ${migrationFiles.length}`);
|
|
19221
19393
|
console.log(`-- Executed: ${executed.length}`);
|
|
19222
19394
|
console.log(`-- Pending: ${pending.length}`);
|
|
19223
|
-
console.log(`-- Transient: ${transient.length}`);
|
|
19224
19395
|
console.log();
|
|
19225
19396
|
if (executed.length > 0) {
|
|
19226
19397
|
console.log("\u2713 Executed Migrations:");
|
|
@@ -19236,13 +19407,6 @@ async function migrateStatus() {
|
|
|
19236
19407
|
}
|
|
19237
19408
|
console.log();
|
|
19238
19409
|
}
|
|
19239
|
-
if (transient.length > 0) {
|
|
19240
|
-
console.log("\u26A1 Transient Migrations (ALTER TABLE - not tracked):");
|
|
19241
|
-
for (const migration of transient) {
|
|
19242
|
-
console.log(` - ${migration.file}`);
|
|
19243
|
-
}
|
|
19244
|
-
console.log();
|
|
19245
|
-
}
|
|
19246
19410
|
return statuses;
|
|
19247
19411
|
} catch (err) {
|
|
19248
19412
|
console.error("-- Failed to get migration status:", err);
|
|
@@ -19256,14 +19420,15 @@ var init_migrate_status = __esm(() => {
|
|
|
19256
19420
|
init_config();
|
|
19257
19421
|
init_drivers();
|
|
19258
19422
|
init_src2();
|
|
19423
|
+
init_workspace();
|
|
19259
19424
|
});
|
|
19260
19425
|
|
|
19261
19426
|
// src/actions/model-show.ts
|
|
19262
19427
|
import { readdirSync as readdirSync9 } from "fs";
|
|
19263
19428
|
import { extname as extname2, join as join12 } from "path";
|
|
19264
|
-
import
|
|
19429
|
+
import process26 from "process";
|
|
19265
19430
|
async function modelShow(modelName, options = {}) {
|
|
19266
|
-
const dir = options.dir || join12(
|
|
19431
|
+
const dir = options.dir || join12(process26.cwd(), "app/Models");
|
|
19267
19432
|
try {
|
|
19268
19433
|
const files = readdirSync9(dir);
|
|
19269
19434
|
const modelFile = files.find((f) => {
|
|
@@ -19477,9 +19642,9 @@ var init_query_explain_all = __esm(() => {
|
|
|
19477
19642
|
// src/actions/relation-diagram.ts
|
|
19478
19643
|
import { writeFileSync as writeFileSync12 } from "fs";
|
|
19479
19644
|
import { join as join14 } from "path";
|
|
19480
|
-
import
|
|
19645
|
+
import process27 from "process";
|
|
19481
19646
|
async function relationDiagram(options = {}) {
|
|
19482
|
-
const dir = options.dir || join14(
|
|
19647
|
+
const dir = options.dir || join14(process27.cwd(), "app/Models");
|
|
19483
19648
|
const format = options.format || "mermaid";
|
|
19484
19649
|
try {
|
|
19485
19650
|
const models = await loadModels({ modelsDir: dir });
|
|
@@ -19599,25 +19764,25 @@ var init_relation_diagram = __esm(() => {
|
|
|
19599
19764
|
});
|
|
19600
19765
|
|
|
19601
19766
|
// src/actions/seed.ts
|
|
19602
|
-
import { existsSync as
|
|
19767
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
19603
19768
|
import { dirname as dirname10, join as join15 } from "path";
|
|
19604
|
-
import
|
|
19605
|
-
function
|
|
19769
|
+
import process28 from "process";
|
|
19770
|
+
function findWorkspaceRoot3(startPath) {
|
|
19606
19771
|
let currentPath = startPath;
|
|
19607
19772
|
while (currentPath !== dirname10(currentPath)) {
|
|
19608
|
-
if (
|
|
19773
|
+
if (existsSync20(join15(currentPath, "package.json"))) {
|
|
19609
19774
|
return currentPath;
|
|
19610
19775
|
}
|
|
19611
19776
|
currentPath = dirname10(currentPath);
|
|
19612
19777
|
}
|
|
19613
|
-
return
|
|
19778
|
+
return process28.cwd();
|
|
19614
19779
|
}
|
|
19615
19780
|
async function loadSeeders(seedersDir) {
|
|
19616
|
-
if (!
|
|
19781
|
+
if (!existsSync20(seedersDir)) {
|
|
19617
19782
|
console.log(`-- Seeders directory not found: ${seedersDir}`);
|
|
19618
19783
|
return [];
|
|
19619
19784
|
}
|
|
19620
|
-
const files = readdirSync11(seedersDir);
|
|
19785
|
+
const files = readdirSync11(seedersDir).sort((a, b) => a.localeCompare(b));
|
|
19621
19786
|
const seederFiles = files.filter((file2) => (file2.endsWith(".ts") || file2.endsWith(".js")) && file2 !== "index.ts" && file2 !== "index.js");
|
|
19622
19787
|
const seeders = [];
|
|
19623
19788
|
for (const file2 of seederFiles) {
|
|
@@ -19630,6 +19795,7 @@ async function loadSeeders(seedersDir) {
|
|
|
19630
19795
|
if (instance && typeof instance.run === "function") {
|
|
19631
19796
|
seeders.push({
|
|
19632
19797
|
name: file2.replace(/\.(ts|js)$/, ""),
|
|
19798
|
+
className: String(SeederClass.name || ""),
|
|
19633
19799
|
instance
|
|
19634
19800
|
});
|
|
19635
19801
|
}
|
|
@@ -19642,7 +19808,7 @@ async function loadSeeders(seedersDir) {
|
|
|
19642
19808
|
return seeders;
|
|
19643
19809
|
}
|
|
19644
19810
|
async function runSeeders(config6 = {}) {
|
|
19645
|
-
const workspaceRoot =
|
|
19811
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19646
19812
|
const seedersDir = config6.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
19647
19813
|
const verbose = config6.verbose ?? true;
|
|
19648
19814
|
if (verbose) {
|
|
@@ -19677,17 +19843,18 @@ async function runSeeders(config6 = {}) {
|
|
|
19677
19843
|
}
|
|
19678
19844
|
}
|
|
19679
19845
|
async function runSeeder(className, options = {}) {
|
|
19680
|
-
const workspaceRoot =
|
|
19846
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19681
19847
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
19682
19848
|
const verbose = options.verbose ?? true;
|
|
19683
19849
|
if (verbose) {
|
|
19684
19850
|
console.log(`-- Running seeder: ${className}`);
|
|
19685
19851
|
}
|
|
19686
19852
|
const seeders = await loadSeeders(seedersDir);
|
|
19687
|
-
const seeder = seeders.find((s) => s.name === className);
|
|
19853
|
+
const seeder = seeders.find((s) => s.className === className) ?? seeders.find((s) => s.name === className);
|
|
19688
19854
|
if (!seeder) {
|
|
19689
|
-
|
|
19690
|
-
|
|
19855
|
+
const known = seeders.map((s) => s.className || s.name).join(", ") || "none";
|
|
19856
|
+
console.error(`-- Seeder not found: ${className}. Available: ${known}`);
|
|
19857
|
+
throw new Error(`Seeder not found: ${className}. Available: ${known}`);
|
|
19691
19858
|
}
|
|
19692
19859
|
const qb = createQueryBuilder();
|
|
19693
19860
|
try {
|
|
@@ -19701,9 +19868,9 @@ async function runSeeder(className, options = {}) {
|
|
|
19701
19868
|
}
|
|
19702
19869
|
}
|
|
19703
19870
|
async function makeSeeder(name) {
|
|
19704
|
-
const workspaceRoot =
|
|
19871
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19705
19872
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
19706
|
-
if (!
|
|
19873
|
+
if (!existsSync20(seedersDir)) {
|
|
19707
19874
|
mkdirSync9(seedersDir, { recursive: true });
|
|
19708
19875
|
console.log(`-- Created seeders directory: ${seedersDir}`);
|
|
19709
19876
|
}
|
|
@@ -19711,7 +19878,7 @@ async function makeSeeder(name) {
|
|
|
19711
19878
|
const className = `${baseName}Seeder`;
|
|
19712
19879
|
const fileName = `${className}.ts`;
|
|
19713
19880
|
const filePath = join15(seedersDir, fileName);
|
|
19714
|
-
if (
|
|
19881
|
+
if (existsSync20(filePath)) {
|
|
19715
19882
|
console.error(`-- Seeder already exists: ${filePath}`);
|
|
19716
19883
|
throw new Error(`Seeder already exists: ${filePath}`);
|
|
19717
19884
|
}
|
|
@@ -19765,7 +19932,7 @@ export default class ${className} extends Seeder {
|
|
|
19765
19932
|
console.log(`-- \u2713 Created seeder: ${filePath}`);
|
|
19766
19933
|
}
|
|
19767
19934
|
async function freshDatabase(options = {}) {
|
|
19768
|
-
const workspaceRoot =
|
|
19935
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19769
19936
|
const modelsDir = options.modelsDir || join15(workspaceRoot, "app/Models");
|
|
19770
19937
|
const seedersDir = options.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
19771
19938
|
const verbose = options.verbose ?? true;
|
|
@@ -19825,22 +19992,22 @@ var init_unsafe = __esm(() => {
|
|
|
19825
19992
|
});
|
|
19826
19993
|
|
|
19827
19994
|
// src/actions/validate.ts
|
|
19828
|
-
import { existsSync as
|
|
19995
|
+
import { existsSync as existsSync21 } from "fs";
|
|
19829
19996
|
import { dirname as dirname11, join as join16 } from "path";
|
|
19830
|
-
import
|
|
19831
|
-
function
|
|
19997
|
+
import process29 from "process";
|
|
19998
|
+
function findWorkspaceRoot4(startPath) {
|
|
19832
19999
|
let currentPath = startPath;
|
|
19833
20000
|
while (currentPath !== dirname11(currentPath)) {
|
|
19834
|
-
if (
|
|
20001
|
+
if (existsSync21(join16(currentPath, "package.json"))) {
|
|
19835
20002
|
return currentPath;
|
|
19836
20003
|
}
|
|
19837
20004
|
currentPath = dirname11(currentPath);
|
|
19838
20005
|
}
|
|
19839
|
-
return
|
|
20006
|
+
return process29.cwd();
|
|
19840
20007
|
}
|
|
19841
20008
|
async function validateSchema(dir) {
|
|
19842
20009
|
if (!dir) {
|
|
19843
|
-
dir = join16(
|
|
20010
|
+
dir = join16(findWorkspaceRoot4(process29.cwd()), "app/Models");
|
|
19844
20011
|
}
|
|
19845
20012
|
const dialect = config5.dialect || "postgres";
|
|
19846
20013
|
console.log("-- Validating Schema");
|
|
@@ -25091,18 +25258,11 @@ function toSnakeCase(str) {
|
|
|
25091
25258
|
snakeCaseCache.set(str, cached);
|
|
25092
25259
|
return cached;
|
|
25093
25260
|
}
|
|
25094
|
-
function
|
|
25261
|
+
function toTableName2(modelName) {
|
|
25095
25262
|
let cached = tableNameCache.get(modelName);
|
|
25096
25263
|
if (cached !== undefined)
|
|
25097
25264
|
return cached;
|
|
25098
|
-
|
|
25099
|
-
if (snake.endsWith("y") && !snake.endsWith("ay") && !snake.endsWith("ey") && !snake.endsWith("oy") && !snake.endsWith("uy")) {
|
|
25100
|
-
cached = snake.slice(0, -1) + "ies";
|
|
25101
|
-
} else if (snake.endsWith("s") || snake.endsWith("x") || snake.endsWith("ch") || snake.endsWith("sh")) {
|
|
25102
|
-
cached = `${snake}es`;
|
|
25103
|
-
} else {
|
|
25104
|
-
cached = `${snake}s`;
|
|
25105
|
-
}
|
|
25265
|
+
cached = toTableName(modelName);
|
|
25106
25266
|
tableNameCache.set(modelName, cached);
|
|
25107
25267
|
return cached;
|
|
25108
25268
|
}
|
|
@@ -25187,14 +25347,14 @@ function resolveRelation(definition, relationName) {
|
|
|
25187
25347
|
const hasManyModel = findModelName(definition.hasMany);
|
|
25188
25348
|
if (hasManyModel) {
|
|
25189
25349
|
const relatedModel = getModelFromRegistry(hasManyModel);
|
|
25190
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25350
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(hasManyModel);
|
|
25191
25351
|
const foreignKey = toSnakeCase(parentName) + "_id";
|
|
25192
25352
|
return { type: "hasMany", relatedModelName: hasManyModel, relatedTable, foreignKey, localKey: parentPk };
|
|
25193
25353
|
}
|
|
25194
25354
|
const hasOneModel = findModelName(definition.hasOne);
|
|
25195
25355
|
if (hasOneModel) {
|
|
25196
25356
|
const relatedModel = getModelFromRegistry(hasOneModel);
|
|
25197
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25357
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(hasOneModel);
|
|
25198
25358
|
const foreignKey = toSnakeCase(parentName) + "_id";
|
|
25199
25359
|
return { type: "hasOne", relatedModelName: hasOneModel, relatedTable, foreignKey, localKey: parentPk };
|
|
25200
25360
|
}
|
|
@@ -25202,7 +25362,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25202
25362
|
if (belongsToEntry) {
|
|
25203
25363
|
const belongsToModel = belongsToEntry.model;
|
|
25204
25364
|
const relatedModel = getModelFromRegistry(belongsToModel);
|
|
25205
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25365
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(belongsToModel);
|
|
25206
25366
|
const relatedPk = relatedModel?.getDefinition?.()?.primaryKey || "id";
|
|
25207
25367
|
const foreignKey = belongsToEntry.foreignKey || `${toSnakeCase(belongsToModel)}_id`;
|
|
25208
25368
|
return { type: "belongsTo", relatedModelName: belongsToModel, relatedTable, foreignKey, localKey: relatedPk };
|
|
@@ -25212,7 +25372,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25212
25372
|
const isConfig = typeof found.entry === "object";
|
|
25213
25373
|
const relatedModelName = isConfig ? found.entry.model : found.entry;
|
|
25214
25374
|
const relatedModel = getModelFromRegistry(relatedModelName);
|
|
25215
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25375
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(relatedModelName);
|
|
25216
25376
|
const config6 = isConfig ? found.entry : null;
|
|
25217
25377
|
let pivotTable;
|
|
25218
25378
|
let pivotModelName;
|
|
@@ -25224,7 +25384,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25224
25384
|
if (!throughModel) {
|
|
25225
25385
|
throw new Error(`[orm] belongsToMany relation '${relationName}' on '${parentName}' references unknown through model '${config6.through}'. Make sure '${config6.through}' is registered.`);
|
|
25226
25386
|
}
|
|
25227
|
-
pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table ||
|
|
25387
|
+
pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table || toTableName2(config6.through);
|
|
25228
25388
|
} else {
|
|
25229
25389
|
const a2 = singularizeWord(parentTable);
|
|
25230
25390
|
const b2 = singularizeWord(relatedTable);
|
|
@@ -25275,10 +25435,10 @@ function resolveRelation(definition, relationName) {
|
|
|
25275
25435
|
return null;
|
|
25276
25436
|
const throughModel = getModelFromRegistry(throughModelName);
|
|
25277
25437
|
const throughDef = throughModel?.getDefinition?.() || throughModel?.definition;
|
|
25278
|
-
const throughTable = throughModel?.getTable?.() || throughDef?.table ||
|
|
25438
|
+
const throughTable = throughModel?.getTable?.() || throughDef?.table || toTableName2(throughModelName);
|
|
25279
25439
|
const throughPk = throughDef?.primaryKey || "id";
|
|
25280
25440
|
const targetModel = getModelFromRegistry(targetModelName);
|
|
25281
|
-
const targetTable = targetModel?.getTable?.() ||
|
|
25441
|
+
const targetTable = targetModel?.getTable?.() || toTableName2(targetModelName);
|
|
25282
25442
|
return {
|
|
25283
25443
|
type,
|
|
25284
25444
|
relatedModelName: targetModelName,
|
|
@@ -27875,7 +28035,7 @@ function buildDatabaseSchema(models) {
|
|
|
27875
28035
|
for (const modelName of Object.keys(models)) {
|
|
27876
28036
|
const rawModel = models[modelName];
|
|
27877
28037
|
const m2 = rawModel.definition ?? rawModel.getDefinition?.() ?? rawModel;
|
|
27878
|
-
const table =
|
|
28038
|
+
const table = tableNameFor(m2);
|
|
27879
28039
|
const attrs = m2.attributes ?? {};
|
|
27880
28040
|
const columns = {};
|
|
27881
28041
|
for (const key of Object.keys(attrs))
|
|
@@ -27887,30 +28047,53 @@ function buildDatabaseSchema(models) {
|
|
|
27887
28047
|
}
|
|
27888
28048
|
return schema;
|
|
27889
28049
|
}
|
|
28050
|
+
var init_factory = () => {};
|
|
27890
28051
|
|
|
27891
28052
|
// src/loader.ts
|
|
27892
|
-
import { readdirSync as readdirSync12
|
|
27893
|
-
import { basename, extname as extname4 } from "path";
|
|
27894
|
-
import
|
|
27895
|
-
|
|
27896
|
-
const
|
|
27897
|
-
|
|
27898
|
-
|
|
27899
|
-
const
|
|
28053
|
+
import { readdirSync as readdirSync12 } from "fs";
|
|
28054
|
+
import { basename, extname as extname4, isAbsolute as isAbsolute3, join as join17 } from "path";
|
|
28055
|
+
import process30 from "process";
|
|
28056
|
+
function isModelFile(name) {
|
|
28057
|
+
const ext = extname4(name);
|
|
28058
|
+
if (!MODEL_EXTENSIONS.has(ext))
|
|
28059
|
+
return false;
|
|
28060
|
+
const base = basename(name, ext);
|
|
28061
|
+
if (base.startsWith("_") || base === "index")
|
|
28062
|
+
return false;
|
|
28063
|
+
return !base.endsWith(".d");
|
|
28064
|
+
}
|
|
28065
|
+
function isModelDefinition(def) {
|
|
28066
|
+
if (!def || typeof def !== "object")
|
|
28067
|
+
return false;
|
|
28068
|
+
const d2 = def;
|
|
28069
|
+
return typeof d2.name === "string" || typeof d2.table === "string";
|
|
28070
|
+
}
|
|
28071
|
+
function collectModelFiles(dir) {
|
|
28072
|
+
const files = [];
|
|
28073
|
+
const entries = readdirSync12(dir, { withFileTypes: true }).sort((a2, b2) => a2.name.localeCompare(b2.name));
|
|
27900
28074
|
for (const entry of entries) {
|
|
27901
|
-
const full =
|
|
27902
|
-
|
|
27903
|
-
|
|
27904
|
-
continue;
|
|
27905
|
-
const ext = extname4(full);
|
|
27906
|
-
if (![".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"].includes(ext))
|
|
28075
|
+
const full = join17(dir, entry.name);
|
|
28076
|
+
if (entry.isDirectory()) {
|
|
28077
|
+
files.push(...collectModelFiles(full));
|
|
27907
28078
|
continue;
|
|
28079
|
+
}
|
|
28080
|
+
if (isModelFile(entry.name))
|
|
28081
|
+
files.push(full);
|
|
28082
|
+
}
|
|
28083
|
+
return files;
|
|
28084
|
+
}
|
|
28085
|
+
async function loadModels(options) {
|
|
28086
|
+
const cwd = options.cwd ?? process30.cwd();
|
|
28087
|
+
const dir = isAbsolute3(options.modelsDir) ? options.modelsDir : join17(cwd, options.modelsDir);
|
|
28088
|
+
const result = {};
|
|
28089
|
+
for (const full of collectModelFiles(dir)) {
|
|
27908
28090
|
const cacheBuster = `?t=${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
27909
28091
|
const mod = await import(`${full}${cacheBuster}`);
|
|
27910
28092
|
const rawDef = mod.default ?? mod;
|
|
27911
28093
|
const def = rawDef.definition ?? rawDef.getDefinition?.() ?? rawDef;
|
|
27912
|
-
|
|
27913
|
-
|
|
28094
|
+
if (!isModelDefinition(def))
|
|
28095
|
+
continue;
|
|
28096
|
+
const name = def.name ?? basename(full, extname4(full));
|
|
27914
28097
|
result[name] = {
|
|
27915
28098
|
...def,
|
|
27916
28099
|
name
|
|
@@ -27918,7 +28101,10 @@ async function loadModels(options) {
|
|
|
27918
28101
|
}
|
|
27919
28102
|
return result;
|
|
27920
28103
|
}
|
|
27921
|
-
var
|
|
28104
|
+
var MODEL_EXTENSIONS;
|
|
28105
|
+
var init_loader = __esm(() => {
|
|
28106
|
+
MODEL_EXTENSIONS = new Set([".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]);
|
|
28107
|
+
});
|
|
27922
28108
|
|
|
27923
28109
|
// src/meta.ts
|
|
27924
28110
|
function buildSchemaMeta(models) {
|
|
@@ -27930,7 +28116,7 @@ function buildSchemaMeta(models) {
|
|
|
27930
28116
|
for (const name of Object.keys(models)) {
|
|
27931
28117
|
const rawModel = models[name];
|
|
27932
28118
|
const m2 = rawModel.definition ?? rawModel.getDefinition?.() ?? rawModel;
|
|
27933
|
-
const table =
|
|
28119
|
+
const table = tableNameFor(m2);
|
|
27934
28120
|
modelToTable[name] = table;
|
|
27935
28121
|
tableToModel[table] = name;
|
|
27936
28122
|
primaryKeys[table] = m2.primaryKey ?? "id";
|
|
@@ -28004,9 +28190,9 @@ function buildSchemaMeta(models) {
|
|
|
28004
28190
|
var init_meta = () => {};
|
|
28005
28191
|
|
|
28006
28192
|
// src/migrations.ts
|
|
28007
|
-
import { existsSync as
|
|
28008
|
-
import { dirname as dirname12, join as
|
|
28009
|
-
import
|
|
28193
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync10, readdirSync as readdirSync13, readFileSync as readFileSync6, writeFileSync as writeFileSync14 } from "fs";
|
|
28194
|
+
import { dirname as dirname12, join as join18 } from "path";
|
|
28195
|
+
import process31 from "process";
|
|
28010
28196
|
function info2(message) {
|
|
28011
28197
|
if (config5.verbose)
|
|
28012
28198
|
console.log(message);
|
|
@@ -28017,36 +28203,80 @@ function snakeCase(str) {
|
|
|
28017
28203
|
function singularizeTable(table) {
|
|
28018
28204
|
return singularizerFor(config5.relations?.singularizeStrategy)(table);
|
|
28019
28205
|
}
|
|
28020
|
-
function
|
|
28206
|
+
function lastCreatedMigrationFiles() {
|
|
28207
|
+
return [...createdMigrationFiles];
|
|
28208
|
+
}
|
|
28209
|
+
function findWorkspaceRoot5(startPath) {
|
|
28021
28210
|
let currentPath = startPath;
|
|
28022
28211
|
while (currentPath !== dirname12(currentPath)) {
|
|
28023
|
-
if (
|
|
28212
|
+
if (existsSync23(join18(currentPath, "package.json"))) {
|
|
28024
28213
|
return currentPath;
|
|
28025
28214
|
}
|
|
28026
28215
|
currentPath = dirname12(currentPath);
|
|
28027
28216
|
}
|
|
28028
|
-
return
|
|
28217
|
+
return process31.cwd();
|
|
28029
28218
|
}
|
|
28030
28219
|
function ensureSqlDirectory2() {
|
|
28031
|
-
const workspaceRoot =
|
|
28032
|
-
const sqlDir =
|
|
28033
|
-
if (!
|
|
28220
|
+
const workspaceRoot = findWorkspaceRoot5(process31.cwd());
|
|
28221
|
+
const sqlDir = join18(workspaceRoot, "database", "migrations");
|
|
28222
|
+
if (!existsSync23(sqlDir)) {
|
|
28034
28223
|
mkdirSync10(sqlDir, { recursive: true });
|
|
28035
28224
|
info2(`-- Created SQL directory: ${sqlDir}`);
|
|
28036
28225
|
}
|
|
28037
28226
|
return sqlDir;
|
|
28038
28227
|
}
|
|
28228
|
+
function isGeneratedMigrationSql(sql2) {
|
|
28229
|
+
return sql2.trimStart().startsWith(GENERATED_MARKER);
|
|
28230
|
+
}
|
|
28231
|
+
function migrationBody(sql2) {
|
|
28232
|
+
return sql2.split(`
|
|
28233
|
+
`).filter((line) => !line.trimStart().startsWith("--")).join(`
|
|
28234
|
+
`).replace(/\s+/g, " ").trim().replace(/;+$/, "");
|
|
28235
|
+
}
|
|
28236
|
+
function existingMigrationBodies(sqlDir) {
|
|
28237
|
+
const bodies = new Set;
|
|
28238
|
+
let files;
|
|
28239
|
+
try {
|
|
28240
|
+
files = readdirSync13(sqlDir).filter((f2) => f2.endsWith(".sql"));
|
|
28241
|
+
} catch {
|
|
28242
|
+
return bodies;
|
|
28243
|
+
}
|
|
28244
|
+
for (const file2 of files) {
|
|
28245
|
+
try {
|
|
28246
|
+
bodies.add(migrationBody(readFileSync6(join18(sqlDir, file2), "utf8")));
|
|
28247
|
+
} catch {}
|
|
28248
|
+
}
|
|
28249
|
+
return bodies;
|
|
28250
|
+
}
|
|
28039
28251
|
function createMigrationFile(statement, fileName) {
|
|
28040
28252
|
if (!statement)
|
|
28041
28253
|
return false;
|
|
28042
28254
|
const sqlDir = ensureSqlDirectory2();
|
|
28255
|
+
if (existingCorpusBodies === null)
|
|
28256
|
+
existingCorpusBodies = existingMigrationBodies(sqlDir);
|
|
28257
|
+
const body = migrationBody(statement);
|
|
28258
|
+
if (body.length > 0 && existingCorpusBodies.has(body)) {
|
|
28259
|
+
info2(`-- Migration already in the corpus, not writing it again: ${fileName}`);
|
|
28260
|
+
return false;
|
|
28261
|
+
}
|
|
28043
28262
|
migrationCounter++;
|
|
28044
|
-
|
|
28045
|
-
|
|
28046
|
-
|
|
28047
|
-
|
|
28263
|
+
let sequence = useDeterministicNames ? String(migrationCounter).padStart(10, "0") : String(Math.floor(Date.now() / 1000) + migrationCounter);
|
|
28264
|
+
let fullFileName = `${sequence}-${fileName}.sql`;
|
|
28265
|
+
if (!useDeterministicNames) {
|
|
28266
|
+
let next = Number(sequence);
|
|
28267
|
+
while (existsSync23(join18(sqlDir, fullFileName))) {
|
|
28268
|
+
next += 1;
|
|
28269
|
+
sequence = String(next);
|
|
28270
|
+
fullFileName = `${sequence}-${fileName}.sql`;
|
|
28271
|
+
}
|
|
28272
|
+
}
|
|
28273
|
+
const filePath = join18(sqlDir, fullFileName);
|
|
28274
|
+
writeFileSync14(filePath, `${GENERATED_MARKER}
|
|
28275
|
+
${statement}`);
|
|
28048
28276
|
info2(`-- Migration file created: ${fullFileName}`);
|
|
28049
28277
|
migrationsCreatedCount++;
|
|
28278
|
+
createdMigrationFiles.push(fullFileName);
|
|
28279
|
+
existingCorpusBodies?.add(body);
|
|
28050
28280
|
return true;
|
|
28051
28281
|
}
|
|
28052
28282
|
function guessTypeFromName(columnName) {
|
|
@@ -28221,7 +28451,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28221
28451
|
const tables = [];
|
|
28222
28452
|
for (const modelName of Object.keys(models)) {
|
|
28223
28453
|
const model = models[modelName];
|
|
28224
|
-
const table =
|
|
28454
|
+
const table = tableNameFor(model);
|
|
28225
28455
|
const primaryKey = model.primaryKey ?? "id";
|
|
28226
28456
|
const _autoIncrement = model.autoIncrement !== false;
|
|
28227
28457
|
const attrs = model.attributes ?? {};
|
|
@@ -28237,6 +28467,23 @@ function buildMigrationPlan2(models, options) {
|
|
|
28237
28467
|
hasDefault: false
|
|
28238
28468
|
});
|
|
28239
28469
|
}
|
|
28470
|
+
const declaredFkTargets = new Map;
|
|
28471
|
+
const declaresBelongsTo = normalizeRelationList(model.belongsTo).length > 0;
|
|
28472
|
+
const columnNames = new Set(Object.keys(attrs).map((name) => snakeCase(name)));
|
|
28473
|
+
const polymorphicColumns = new Set;
|
|
28474
|
+
for (const column of columnNames) {
|
|
28475
|
+
if (!column.endsWith("_type"))
|
|
28476
|
+
continue;
|
|
28477
|
+
const idColumn = `${column.slice(0, -"_type".length)}_id`;
|
|
28478
|
+
if (columnNames.has(idColumn))
|
|
28479
|
+
polymorphicColumns.add(idColumn);
|
|
28480
|
+
}
|
|
28481
|
+
for (const rel of normalizeRelationList(model.belongsTo)) {
|
|
28482
|
+
const column = rel.foreignKey ?? `${snakeCase(rel.model)}_id`;
|
|
28483
|
+
const table2 = meta.modelToTable[rel.model];
|
|
28484
|
+
if (table2)
|
|
28485
|
+
declaredFkTargets.set(column, table2);
|
|
28486
|
+
}
|
|
28240
28487
|
for (const attrName of Object.keys(attrs)) {
|
|
28241
28488
|
const attr = attrs[attrName];
|
|
28242
28489
|
const columnName = snakeCase(attrName);
|
|
@@ -28284,7 +28531,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28284
28531
|
enumValues,
|
|
28285
28532
|
maxLength: typeof maxLen === "number" && maxLen > 0 && maxLen <= 255 ? maxLen : undefined
|
|
28286
28533
|
};
|
|
28287
|
-
if (columnName.endsWith("_id") && attr.foreignKey !== false) {
|
|
28534
|
+
if (columnName.endsWith("_id") && attr.foreignKey !== false && !polymorphicColumns.has(columnName)) {
|
|
28288
28535
|
if (typeof attr.foreignKey === "object" && attr.foreignKey !== null) {
|
|
28289
28536
|
const fkConfig = attr.foreignKey;
|
|
28290
28537
|
col.references = {
|
|
@@ -28296,9 +28543,10 @@ function buildMigrationPlan2(models, options) {
|
|
|
28296
28543
|
if (fkConfig.nullable !== undefined)
|
|
28297
28544
|
col.isNullable = fkConfig.nullable;
|
|
28298
28545
|
} else {
|
|
28546
|
+
const declared = declaredFkTargets.get(columnName);
|
|
28299
28547
|
const base = columnName.replace(/_id$/, "");
|
|
28300
28548
|
const maybeModel = base.replace(/(^|_)([a-z])/g, (_2, __, c2) => c2.toUpperCase());
|
|
28301
|
-
const refTable = meta.modelToTable[maybeModel];
|
|
28549
|
+
const refTable = declared ?? (declaresBelongsTo ? undefined : meta.modelToTable[maybeModel]);
|
|
28302
28550
|
if (refTable) {
|
|
28303
28551
|
const refPk = meta.primaryKeys[refTable] ?? "id";
|
|
28304
28552
|
col.references = { table: refTable, column: refPk };
|
|
@@ -28313,9 +28561,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28313
28561
|
if (columns.some((c2) => c2.name === fkColumnName))
|
|
28314
28562
|
continue;
|
|
28315
28563
|
const refTable = meta.modelToTable[rel.model];
|
|
28316
|
-
|
|
28317
|
-
continue;
|
|
28318
|
-
const refPk = meta.primaryKeys[refTable] ?? "id";
|
|
28564
|
+
const refPk = refTable ? meta.primaryKeys[refTable] ?? "id" : undefined;
|
|
28319
28565
|
columns.push({
|
|
28320
28566
|
name: fkColumnName,
|
|
28321
28567
|
type: "bigint",
|
|
@@ -28323,7 +28569,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28323
28569
|
isUnique: false,
|
|
28324
28570
|
isNullable: true,
|
|
28325
28571
|
hasDefault: false,
|
|
28326
|
-
references: { table: refTable, column: refPk, onDelete: rel.onDelete }
|
|
28572
|
+
references: refTable && refPk && !polymorphicColumns.has(fkColumnName) ? { table: refTable, column: refPk, onDelete: rel.onDelete } : undefined
|
|
28327
28573
|
});
|
|
28328
28574
|
}
|
|
28329
28575
|
const traits = model.traits;
|
|
@@ -28404,7 +28650,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28404
28650
|
for (const modelName of Object.keys(models)) {
|
|
28405
28651
|
const model = models[modelName];
|
|
28406
28652
|
const traits = model?.traits ?? {};
|
|
28407
|
-
const modelTable =
|
|
28653
|
+
const modelTable = tableNameFor(model);
|
|
28408
28654
|
const singular = singularizeTable(modelTable);
|
|
28409
28655
|
if (traits.likeable) {
|
|
28410
28656
|
const opts = typeof traits.likeable === "object" ? traits.likeable : {};
|
|
@@ -28424,7 +28670,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28424
28670
|
}
|
|
28425
28671
|
if (traits.taggable) {
|
|
28426
28672
|
ensureTable({
|
|
28427
|
-
table: "
|
|
28673
|
+
table: "taggables",
|
|
28428
28674
|
columns: [
|
|
28429
28675
|
{ name: "id", type: "bigint", isPrimaryKey: true, isUnique: false, isNullable: false, hasDefault: false },
|
|
28430
28676
|
{ name: "name", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
@@ -28432,37 +28678,61 @@ function buildMigrationPlan2(models, options) {
|
|
|
28432
28678
|
{ name: "description", type: "text", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false },
|
|
28433
28679
|
{ name: "order", type: "integer", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: 0 },
|
|
28434
28680
|
{ name: "is_active", type: "boolean", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: true },
|
|
28681
|
+
{ name: "taggable_id", type: "bigint", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false },
|
|
28682
|
+
{ name: "taggable_type", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28683
|
+
{ name: "created_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: "CURRENT_TIMESTAMP" },
|
|
28684
|
+
{ name: "updated_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false }
|
|
28685
|
+
],
|
|
28686
|
+
indexes: [
|
|
28687
|
+
{ name: "taggables_target_index", columns: ["taggable_id", "taggable_type"], type: "index" },
|
|
28688
|
+
{ name: "taggables_type_slug_unique", columns: ["taggable_type", "slug"], type: "unique" }
|
|
28689
|
+
]
|
|
28690
|
+
});
|
|
28691
|
+
ensureTable({
|
|
28692
|
+
table: "taggable_models",
|
|
28693
|
+
columns: [
|
|
28694
|
+
{ name: "id", type: "bigint", isPrimaryKey: true, isUnique: false, isNullable: false, hasDefault: false },
|
|
28695
|
+
{ name: "tag_id", type: "bigint", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false, references: { table: "taggables", column: "id" } },
|
|
28435
28696
|
{ name: "taggable_id", type: "bigint", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28436
28697
|
{ name: "taggable_type", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28437
28698
|
{ name: "created_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: "CURRENT_TIMESTAMP" },
|
|
28438
28699
|
{ name: "updated_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false }
|
|
28439
28700
|
],
|
|
28440
|
-
indexes: [
|
|
28701
|
+
indexes: [
|
|
28702
|
+
{ name: "taggable_models_owner_unique", columns: ["tag_id", "taggable_id", "taggable_type"], type: "unique" },
|
|
28703
|
+
{ name: "taggable_models_target_index", columns: ["taggable_id", "taggable_type"], type: "index" }
|
|
28704
|
+
]
|
|
28441
28705
|
});
|
|
28442
28706
|
}
|
|
28443
28707
|
if (traits.categorizable) {
|
|
28444
28708
|
ensureTable({
|
|
28445
|
-
table: "
|
|
28709
|
+
table: "categorizables",
|
|
28446
28710
|
columns: [
|
|
28447
28711
|
{ name: "id", type: "bigint", isPrimaryKey: true, isUnique: false, isNullable: false, hasDefault: false },
|
|
28448
28712
|
{ name: "name", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28449
28713
|
{ name: "slug", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28450
28714
|
{ name: "description", type: "text", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false },
|
|
28715
|
+
{ name: "is_active", type: "boolean", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: true },
|
|
28716
|
+
{ name: "categorizable_type", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28451
28717
|
{ name: "created_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: "CURRENT_TIMESTAMP" },
|
|
28452
28718
|
{ name: "updated_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false }
|
|
28453
28719
|
],
|
|
28454
|
-
indexes: [{ name: "
|
|
28720
|
+
indexes: [{ name: "categorizables_type_slug_unique", columns: ["categorizable_type", "slug"], type: "unique" }]
|
|
28455
28721
|
});
|
|
28456
28722
|
ensureTable({
|
|
28457
28723
|
table: "categorizable_models",
|
|
28458
28724
|
columns: [
|
|
28459
28725
|
{ name: "id", type: "bigint", isPrimaryKey: true, isUnique: false, isNullable: false, hasDefault: false },
|
|
28460
|
-
{ name: "category_id", type: "bigint", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false, references: { table: "
|
|
28726
|
+
{ name: "category_id", type: "bigint", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false, references: { table: "categorizables", column: "id" } },
|
|
28461
28727
|
{ name: "categorizable_id", type: "bigint", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28462
28728
|
{ name: "categorizable_type", type: "string", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: false },
|
|
28463
|
-
{ name: "created_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: "CURRENT_TIMESTAMP" }
|
|
28729
|
+
{ name: "created_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: false, hasDefault: true, defaultValue: "CURRENT_TIMESTAMP" },
|
|
28730
|
+
{ name: "updated_at", type: "datetime", isPrimaryKey: false, isUnique: false, isNullable: true, hasDefault: false }
|
|
28464
28731
|
],
|
|
28465
|
-
indexes: [
|
|
28732
|
+
indexes: [
|
|
28733
|
+
{ name: "categorizable_models_owner_unique", columns: ["category_id", "categorizable_id", "categorizable_type"], type: "unique" },
|
|
28734
|
+
{ name: "categorizable_models_target_index", columns: ["categorizable_id", "categorizable_type"], type: "index" }
|
|
28735
|
+
]
|
|
28466
28736
|
});
|
|
28467
28737
|
}
|
|
28468
28738
|
}
|
|
@@ -28471,7 +28741,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28471
28741
|
const btm = model?.belongsToMany;
|
|
28472
28742
|
if (!btm || typeof btm !== "object" || Array.isArray(btm))
|
|
28473
28743
|
continue;
|
|
28474
|
-
const parentTable =
|
|
28744
|
+
const parentTable = tableNameFor(model);
|
|
28475
28745
|
const parentSingular = singularizeTable(parentTable);
|
|
28476
28746
|
for (const [relKey, value] of Object.entries(btm)) {
|
|
28477
28747
|
const cfg = value;
|
|
@@ -28532,6 +28802,8 @@ function generateSql(plan, opts = {}) {
|
|
|
28532
28802
|
migrationCounter = 0;
|
|
28533
28803
|
migrationsCreatedCount = 0;
|
|
28534
28804
|
migrationsUpdatedCount = 0;
|
|
28805
|
+
createdMigrationFiles = [];
|
|
28806
|
+
existingCorpusBodies = null;
|
|
28535
28807
|
useDeterministicNames = true;
|
|
28536
28808
|
const emit = opts.dryRun ? () => false : createMigrationFile;
|
|
28537
28809
|
const statements = [];
|
|
@@ -28793,12 +29065,13 @@ function referencesAreDifferent(r1, r2) {
|
|
|
28793
29065
|
const act = (a2) => (a2 ?? "no action").toLowerCase();
|
|
28794
29066
|
return r1.table !== r2.table || r1.column !== r2.column || act(r1.onDelete) !== act(r2.onDelete) || act(r1.onUpdate) !== act(r2.onUpdate);
|
|
28795
29067
|
}
|
|
28796
|
-
function
|
|
29068
|
+
function indexKey(index, table) {
|
|
29069
|
+
return `${index.type}:${qualifiedIndexName(table, index.name)}:${index.columns.join(",")}`;
|
|
29070
|
+
}
|
|
29071
|
+
function mapIndexesByKey(indexes, table) {
|
|
28797
29072
|
const map = {};
|
|
28798
|
-
for (const i2 of indexes)
|
|
28799
|
-
|
|
28800
|
-
map[key] = i2;
|
|
28801
|
-
}
|
|
29073
|
+
for (const i2 of indexes)
|
|
29074
|
+
map[indexKey(i2, table)] = i2;
|
|
28802
29075
|
return map;
|
|
28803
29076
|
}
|
|
28804
29077
|
function generateDiffOperations(previous, next, opts = {}) {
|
|
@@ -28813,10 +29086,13 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
28813
29086
|
return { statements: statements2, operations: operations2 };
|
|
28814
29087
|
}
|
|
28815
29088
|
const applyRenames = opts.applyRenames !== false;
|
|
29089
|
+
const preserveUnknownColumns = opts.preserveUnknownColumns === true;
|
|
28816
29090
|
const emit = opts.dryRun ? () => false : createMigrationFile;
|
|
28817
29091
|
migrationCounter = 0;
|
|
28818
29092
|
migrationsCreatedCount = 0;
|
|
28819
29093
|
migrationsUpdatedCount = 0;
|
|
29094
|
+
createdMigrationFiles = [];
|
|
29095
|
+
existingCorpusBodies = null;
|
|
28820
29096
|
useDeterministicNames = false;
|
|
28821
29097
|
const chunks = [];
|
|
28822
29098
|
const operations = [];
|
|
@@ -28895,42 +29171,52 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
28895
29171
|
const prevCols = mapColumnsByName(prev.columns);
|
|
28896
29172
|
const currCols = mapColumnsByName(curr.columns);
|
|
28897
29173
|
for (const colName of Object.keys(currCols)) {
|
|
28898
|
-
|
|
28899
|
-
|
|
28900
|
-
|
|
28901
|
-
|
|
28902
|
-
|
|
28903
|
-
|
|
28904
|
-
|
|
28905
|
-
|
|
28906
|
-
|
|
28907
|
-
|
|
28908
|
-
|
|
28909
|
-
|
|
28910
|
-
|
|
28911
|
-
}
|
|
29174
|
+
const c2 = currCols[colName];
|
|
29175
|
+
if (c2.type !== "enum" || !c2.enumValues || c2.enumValues.length === 0)
|
|
29176
|
+
continue;
|
|
29177
|
+
const enumTypeName = `${curr.table}_${c2.name}_type`;
|
|
29178
|
+
c2.enumTypeName = enumTypeName;
|
|
29179
|
+
if (prevCols[colName])
|
|
29180
|
+
continue;
|
|
29181
|
+
if (!enumTypes.has(enumTypeName)) {
|
|
29182
|
+
const createEnumStatement = driver.createEnumType(enumTypeName, c2.enumValues);
|
|
29183
|
+
if (createEnumStatement) {
|
|
29184
|
+
chunks.push(createEnumStatement);
|
|
29185
|
+
operations.push({ kind: "create_enum", table: curr.table, column: c2.name, destructive: false, sql: createEnumStatement });
|
|
29186
|
+
emit(createEnumStatement, `create-${enumTypeName}-enum`);
|
|
28912
29187
|
}
|
|
29188
|
+
enumTypes.add(enumTypeName);
|
|
28913
29189
|
}
|
|
28914
29190
|
}
|
|
28915
29191
|
}
|
|
28916
29192
|
for (const tableName of Object.keys(nextTables)) {
|
|
28917
29193
|
const prev = prevTables[tableName];
|
|
28918
|
-
|
|
29194
|
+
let curr = nextTables[tableName];
|
|
28919
29195
|
if (!prev)
|
|
28920
29196
|
continue;
|
|
28921
29197
|
const prevCols = mapColumnsByName(prev.columns);
|
|
28922
|
-
const
|
|
28923
|
-
|
|
28924
|
-
|
|
29198
|
+
const prevIdx = mapIndexesByKey(prev.indexes, prev.table);
|
|
29199
|
+
let currCols = mapColumnsByName(curr.columns);
|
|
29200
|
+
let currIdx = mapIndexesByKey(curr.indexes, curr.table);
|
|
28925
29201
|
let renames = [];
|
|
28926
29202
|
let removedCols = Object.keys(prevCols).filter((n2) => !currCols[n2]);
|
|
28927
29203
|
let addedCols = Object.keys(currCols).filter((n2) => !prevCols[n2]);
|
|
28928
|
-
if (applyRenames) {
|
|
29204
|
+
if (applyRenames && !preserveUnknownColumns) {
|
|
28929
29205
|
const detected = detectColumnRenames(prevCols, currCols, dialect);
|
|
28930
29206
|
renames = detected.renames;
|
|
28931
29207
|
removedCols = detected.removed;
|
|
28932
29208
|
addedCols = detected.added;
|
|
28933
29209
|
}
|
|
29210
|
+
if (preserveUnknownColumns && removedCols.length > 0) {
|
|
29211
|
+
const preserved = removedCols.map((name) => prevCols[name]);
|
|
29212
|
+
const preservedNames = new Set(removedCols);
|
|
29213
|
+
const keptIndexes = prev.indexes.filter((idx) => idx.columns.some((c2) => preservedNames.has(c2)) && !currIdx[indexKey(idx)]);
|
|
29214
|
+
info2(`-- Keeping ${preserved.length} column(s) on "${curr.table}" no model declares: ${removedCols.join(", ")} \u2014 reconciled from the database, where an undeclared column is out of scope rather than removed`);
|
|
29215
|
+
curr = { ...curr, columns: [...curr.columns, ...preserved], indexes: [...curr.indexes, ...keptIndexes] };
|
|
29216
|
+
currCols = mapColumnsByName(curr.columns);
|
|
29217
|
+
currIdx = mapIndexesByKey(curr.indexes, curr.table);
|
|
29218
|
+
removedCols = [];
|
|
29219
|
+
}
|
|
28934
29220
|
const modifiedCols = Object.keys(currCols).filter((n2) => prevCols[n2] && columnsAreDifferent(prevCols[n2], currCols[n2], dialect));
|
|
28935
29221
|
const fkChangedCols = Object.keys(currCols).filter((n2) => prevCols[n2] && referencesAreDifferent(prevCols[n2].references, currCols[n2].references));
|
|
28936
29222
|
if (dialect === "sqlite") {
|
|
@@ -29078,11 +29364,13 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
29078
29364
|
function generateDiffSql(previous, next, opts = {}) {
|
|
29079
29365
|
return generateDiffOperations(previous, next, opts).statements;
|
|
29080
29366
|
}
|
|
29081
|
-
var migrationCounter = 0, migrationsCreatedCount = 0, migrationsUpdatedCount = 0, useDeterministicNames = true;
|
|
29367
|
+
var migrationCounter = 0, migrationsCreatedCount = 0, migrationsUpdatedCount = 0, useDeterministicNames = true, createdMigrationFiles, existingCorpusBodies = null, GENERATED_MARKER = "-- qb:generated";
|
|
29082
29368
|
var init_migrations = __esm(() => {
|
|
29083
29369
|
init_config();
|
|
29084
29370
|
init_drivers();
|
|
29371
|
+
init_sqlite();
|
|
29085
29372
|
init_meta();
|
|
29373
|
+
createdMigrationFiles = [];
|
|
29086
29374
|
});
|
|
29087
29375
|
|
|
29088
29376
|
// src/schema.ts
|
|
@@ -29116,6 +29404,7 @@ var init_src2 = __esm(() => {
|
|
|
29116
29404
|
init_dynamodb_single_table();
|
|
29117
29405
|
init_dynamodb_tooling_adapter();
|
|
29118
29406
|
init_dynamodb();
|
|
29407
|
+
init_factory();
|
|
29119
29408
|
init_loader();
|
|
29120
29409
|
init_meta();
|
|
29121
29410
|
init_migrations();
|
|
@@ -29125,11 +29414,11 @@ var init_src2 = __esm(() => {
|
|
|
29125
29414
|
});
|
|
29126
29415
|
|
|
29127
29416
|
// ../../node_modules/@stacksjs/clapp/dist/index.js
|
|
29128
|
-
import { existsSync as
|
|
29417
|
+
import { existsSync as existsSync25 } from "fs";
|
|
29129
29418
|
import fs from "fs/promises";
|
|
29130
29419
|
import os from "os";
|
|
29131
29420
|
import path from "path";
|
|
29132
|
-
import
|
|
29421
|
+
import process33 from "process";
|
|
29133
29422
|
import { EventEmitter } from "events";
|
|
29134
29423
|
import process52 from "process";
|
|
29135
29424
|
import process210 from "process";
|
|
@@ -29153,10 +29442,10 @@ class Telemetry {
|
|
|
29153
29442
|
this.configPath = path.join(configDir, "telemetry.json");
|
|
29154
29443
|
}
|
|
29155
29444
|
async isEnabled() {
|
|
29156
|
-
if (
|
|
29445
|
+
if (process33.env.DO_NOT_TRACK === "1" || process33.env.DO_NOT_TRACK === "true") {
|
|
29157
29446
|
return false;
|
|
29158
29447
|
}
|
|
29159
|
-
if (
|
|
29448
|
+
if (process33.env.NO_TELEMETRY === "1" || process33.env.NO_TELEMETRY === "true") {
|
|
29160
29449
|
return false;
|
|
29161
29450
|
}
|
|
29162
29451
|
const config6 = await this.loadConfig();
|
|
@@ -29185,7 +29474,7 @@ class Telemetry {
|
|
|
29185
29474
|
...data,
|
|
29186
29475
|
timestamp: Date.now(),
|
|
29187
29476
|
platform: os.platform(),
|
|
29188
|
-
nodeVersion:
|
|
29477
|
+
nodeVersion: process33.version
|
|
29189
29478
|
};
|
|
29190
29479
|
this.events.push(telemetryEvent);
|
|
29191
29480
|
if (this.events.length >= 10) {
|
|
@@ -29241,7 +29530,7 @@ class Telemetry {
|
|
|
29241
29530
|
const config6 = await this.loadConfig();
|
|
29242
29531
|
return {
|
|
29243
29532
|
enabled: config6.enabled,
|
|
29244
|
-
doNotTrack:
|
|
29533
|
+
doNotTrack: process33.env.DO_NOT_TRACK === "1" || process33.env.DO_NOT_TRACK === "true",
|
|
29245
29534
|
eventsQueued: this.events.length,
|
|
29246
29535
|
lastSent: config6.lastSent
|
|
29247
29536
|
};
|
|
@@ -29251,7 +29540,7 @@ class Telemetry {
|
|
|
29251
29540
|
return this.config;
|
|
29252
29541
|
}
|
|
29253
29542
|
try {
|
|
29254
|
-
if (
|
|
29543
|
+
if (existsSync25(this.configPath)) {
|
|
29255
29544
|
const data = await fs.readFile(this.configPath, "utf-8");
|
|
29256
29545
|
this.config = JSON.parse(data);
|
|
29257
29546
|
return this.config;
|
|
@@ -31163,7 +31452,7 @@ function getPrefix() {
|
|
|
31163
31452
|
}
|
|
31164
31453
|
var prefix = getPrefix();
|
|
31165
31454
|
// package.json
|
|
31166
|
-
var version2 = "0.
|
|
31455
|
+
var version2 = "0.2.2";
|
|
31167
31456
|
|
|
31168
31457
|
// bin/cli.ts
|
|
31169
31458
|
init_actions();
|
|
@@ -31218,7 +31507,7 @@ cli.command("unsafe <sql>", "Execute an unsafe SQL string (one statement with pa
|
|
|
31218
31507
|
cli.command("file <path>", "Execute a SQL file (supports $1, $2 params)").option("--params <json>", "JSON array of parameters").example("query-builder file ./migrations/seed.sql").example('query-builder file ./reports/top.sql --params "[30]"').action(async (path2, opts) => {
|
|
31219
31508
|
await file(path2, opts);
|
|
31220
31509
|
});
|
|
31221
|
-
cli.command("migrate <dir>", "Generate SQL migrations from models").option("--dialect <d>", "Dialect (postgres|mysql|sqlite)
|
|
31510
|
+
cli.command("migrate <dir>", "Generate SQL migrations from models").option("--dialect <d>", "Dialect (postgres|mysql|sqlite) \u2014 defaults to the configured dialect").option("--state <path>", "Path to migration state file (defaults to <dir>/.qb-migrations.<dialect>.json)").option("--apply", "Execute the generated SQL against the database").option("--full", "Force full migration SQL instead of incremental diff").example("query-builder migrate ./app/Models --dialect postgres").action(async (dir, opts) => {
|
|
31222
31511
|
try {
|
|
31223
31512
|
await generateMigration(dir, {
|
|
31224
31513
|
dialect: opts.dialect,
|
|
@@ -31262,7 +31551,7 @@ cli.command("relation:diagram", "Generate relationship diagram from models").opt
|
|
|
31262
31551
|
proc.default.exitCode = 1;
|
|
31263
31552
|
}
|
|
31264
31553
|
});
|
|
31265
|
-
cli.command("migrate:fresh <dir>", "Reset database and run all migrations").option("--dialect <d>", "Dialect (postgres|mysql|sqlite)
|
|
31554
|
+
cli.command("migrate:fresh <dir>", "Reset database and run all migrations").option("--dialect <d>", "Dialect (postgres|mysql|sqlite) \u2014 defaults to the configured dialect").option("--state <path>", "Path to migration state file (defaults to <dir>/.qb-migrations.<dialect>.json)").option("--full", "Force full migration SQL instead of incremental diff").example("query-builder migrate:fresh ./app/Models --dialect postgres").action(async (dir, opts) => {
|
|
31266
31555
|
try {
|
|
31267
31556
|
console.log("-- Resetting database...");
|
|
31268
31557
|
await resetDatabase(dir, {
|
|
@@ -31283,7 +31572,7 @@ cli.command("migrate:fresh <dir>", "Reset database and run all migrations").opti
|
|
|
31283
31572
|
proc.default.exitCode = 1;
|
|
31284
31573
|
}
|
|
31285
31574
|
});
|
|
31286
|
-
cli.command("reset <dir>", "Drop all tables and reset database").option("--dialect <d>", "Dialect (postgres|mysql|sqlite)
|
|
31575
|
+
cli.command("reset <dir>", "Drop all tables and reset database").option("--dialect <d>", "Dialect (postgres|mysql|sqlite) \u2014 defaults to the configured dialect").option("--state <path>", "Path to migration state file (defaults to <dir>/.qb-migrations.<dialect>.json)").example("query-builder reset ./app/Models --dialect postgres").action(async (dir, opts) => {
|
|
31287
31576
|
try {
|
|
31288
31577
|
await resetDatabase(dir, {
|
|
31289
31578
|
dialect: opts.dialect,
|
|
@@ -31387,16 +31676,20 @@ cli.command("migrate:list", "List all migrations (alias for migrate:status)").ex
|
|
|
31387
31676
|
proc.default.exitCode = 1;
|
|
31388
31677
|
}
|
|
31389
31678
|
});
|
|
31390
|
-
cli.command("migrate:rollback", "Rollback migrations").option("--steps <n>", "Number of migrations to rollback", { default: 1 }).example("query-builder migrate:rollback").example("query-builder migrate:rollback --steps 2").action(async (opts) => {
|
|
31679
|
+
cli.command("migrate:rollback", "Rollback migrations").option("--steps <n>", "Number of migrations to rollback", { default: 1 }).option("--no-reverse-schema", "Only remove the migration records; leave the schema alone").option("--delete-files", "Also delete the rolled-back migration files from disk").example("query-builder migrate:rollback").example("query-builder migrate:rollback --steps 2").example("query-builder migrate:rollback --no-reverse-schema").action(async (opts) => {
|
|
31391
31680
|
try {
|
|
31392
|
-
await migrateRollback({
|
|
31681
|
+
await migrateRollback({
|
|
31682
|
+
steps: Number(opts.steps),
|
|
31683
|
+
reverseSchema: opts.reverseSchema,
|
|
31684
|
+
deleteFiles: Boolean(opts.deleteFiles)
|
|
31685
|
+
});
|
|
31393
31686
|
} catch (err) {
|
|
31394
31687
|
console.error("-- Rollback failed:", err);
|
|
31395
31688
|
const proc = await import("process");
|
|
31396
31689
|
proc.default.exitCode = 1;
|
|
31397
31690
|
}
|
|
31398
31691
|
});
|
|
31399
|
-
cli.command("migrate:generate [dir]", "Generate migration from model changes").option("--dialect <d>", "Dialect (postgres|mysql|sqlite)
|
|
31692
|
+
cli.command("migrate:generate [dir]", "Generate migration from model changes").option("--dialect <d>", "Dialect (postgres|mysql|sqlite) \u2014 defaults to the configured dialect").option("--state <path>", "Path to migration state file").option("--apply", "Execute the generated SQL").option("--full", "Force full migration SQL").example("query-builder migrate:generate").example("query-builder migrate:generate ./app/Models --dialect postgres").action(async (dir, opts) => {
|
|
31400
31693
|
try {
|
|
31401
31694
|
await migrateGenerate(dir, {
|
|
31402
31695
|
dialect: opts?.dialect,
|
|
@@ -31428,7 +31721,7 @@ cli.command("db:stats", "Show database statistics (alias for db:info)").example(
|
|
|
31428
31721
|
proc.default.exitCode = 1;
|
|
31429
31722
|
}
|
|
31430
31723
|
});
|
|
31431
|
-
cli.command("db:wipe", "Drop all tables from the database").option("--dialect <d>", "Dialect (postgres|mysql|sqlite)
|
|
31724
|
+
cli.command("db:wipe", "Drop all tables from the database").option("--dialect <d>", "Dialect (postgres|mysql|sqlite) \u2014 defaults to the configured dialect").option("--force", "Skip confirmation prompt").option("--verbose", "Enable verbose output").example("query-builder db:wipe").example("query-builder db:wipe --force").action(async (opts) => {
|
|
31432
31725
|
try {
|
|
31433
31726
|
await dbWipe({
|
|
31434
31727
|
dialect: opts.dialect,
|
|
@@ -31441,7 +31734,7 @@ cli.command("db:wipe", "Drop all tables from the database").option("--dialect <d
|
|
|
31441
31734
|
proc.default.exitCode = 1;
|
|
31442
31735
|
}
|
|
31443
31736
|
});
|
|
31444
|
-
cli.command("db:optimize", "Optimize database tables (VACUUM, ANALYZE, OPTIMIZE)").option("--dialect <d>", "Dialect (postgres|mysql|sqlite)
|
|
31737
|
+
cli.command("db:optimize", "Optimize database tables (VACUUM, ANALYZE, OPTIMIZE)").option("--dialect <d>", "Dialect (postgres|mysql|sqlite) \u2014 defaults to the configured dialect").option("--aggressive", "Use aggressive optimization (VACUUM FULL for postgres)").option("--tables <tables>", "Comma-separated list of tables to optimize").option("--verbose", "Enable verbose output").example("query-builder db:optimize").example("query-builder db:optimize --aggressive").example("query-builder db:optimize --tables users,posts").action(async (opts) => {
|
|
31445
31738
|
try {
|
|
31446
31739
|
await dbOptimize({
|
|
31447
31740
|
dialect: opts.dialect,
|