bun-query-builder 0.1.62 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +432 -293
- package/dist/config.d.ts +30 -3
- package/dist/db.d.ts +18 -0
- package/dist/inflect.d.ts +17 -0
- package/dist/migrations.d.ts +26 -0
- package/dist/src/index.js +419 -277
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -11640,9 +11640,18 @@ var init_dist = __esm(() => {
|
|
|
11640
11640
|
});
|
|
11641
11641
|
|
|
11642
11642
|
// src/config.ts
|
|
11643
|
+
import process19 from "process";
|
|
11643
11644
|
function isMysqlLike(dialect = config5.dialect) {
|
|
11644
11645
|
return dialect === "mysql" || dialect === "singlestore";
|
|
11645
11646
|
}
|
|
11647
|
+
function resolveDialect(explicit) {
|
|
11648
|
+
if (explicit)
|
|
11649
|
+
return explicit;
|
|
11650
|
+
const fromEnv = process19.env.DB_DIALECT || process19.env.DB_CONNECTION;
|
|
11651
|
+
if (fromEnv)
|
|
11652
|
+
return fromEnv;
|
|
11653
|
+
return config5.dialect || "postgres";
|
|
11654
|
+
}
|
|
11646
11655
|
function getPlaceholder(index) {
|
|
11647
11656
|
if (config5.dialect === "postgres") {
|
|
11648
11657
|
return `$${index}`;
|
|
@@ -11661,26 +11670,23 @@ function getPlaceholders(count, startIndex = 1) {
|
|
|
11661
11670
|
return count === 1 ? "?" : `?${", ?".repeat(count - 1)}`;
|
|
11662
11671
|
}
|
|
11663
11672
|
async function getConfig() {
|
|
11664
|
-
if (!
|
|
11665
|
-
|
|
11673
|
+
if (!configState.fileLoaded) {
|
|
11674
|
+
configState.fileLoaded = true;
|
|
11675
|
+
const loaded = await loadConfig6({
|
|
11666
11676
|
name: "query-builder",
|
|
11667
11677
|
alias: "qb",
|
|
11668
11678
|
defaultConfig: defaultConfig4
|
|
11669
11679
|
});
|
|
11670
|
-
|
|
11680
|
+
const applicable = {};
|
|
11681
|
+
for (const [key, value] of Object.entries(loaded ?? {})) {
|
|
11682
|
+
if (!configState.explicit.has(key))
|
|
11683
|
+
applicable[key] = value;
|
|
11684
|
+
}
|
|
11685
|
+
applyConfig(applicable);
|
|
11671
11686
|
}
|
|
11672
11687
|
return config5;
|
|
11673
11688
|
}
|
|
11674
|
-
function
|
|
11675
|
-
if (userConfig.dialect && _lastConfiguredDialect && userConfig.dialect !== _lastConfiguredDialect) {
|
|
11676
|
-
const key = `${_lastConfiguredDialect}->${userConfig.dialect}`;
|
|
11677
|
-
if (!_warnedDialectConflicts.has(key)) {
|
|
11678
|
-
_warnedDialectConflicts.add(key);
|
|
11679
|
-
console.warn(`[query-builder] setConfig({ dialect: '${userConfig.dialect}' }) overrides a previous ` + `setConfig({ dialect: '${_lastConfiguredDialect}' }). ` + `Config is process-wide; in-flight queries from the previous configuration may break. ` + `Run conflicting connections in separate processes \u2014 see stacksjs/stacks#1862 #18.`);
|
|
11680
|
-
}
|
|
11681
|
-
}
|
|
11682
|
-
if (userConfig.dialect)
|
|
11683
|
-
_lastConfiguredDialect = userConfig.dialect;
|
|
11689
|
+
function applyConfig(userConfig) {
|
|
11684
11690
|
Object.assign(config5, userConfig);
|
|
11685
11691
|
if (userConfig.database) {
|
|
11686
11692
|
config5.database = { ...config5.database, ...userConfig.database };
|
|
@@ -11694,11 +11700,22 @@ function setConfig(userConfig) {
|
|
|
11694
11700
|
if (userConfig.softDeletes) {
|
|
11695
11701
|
config5.softDeletes = { ...config5.softDeletes, ...userConfig.softDeletes };
|
|
11696
11702
|
}
|
|
11697
|
-
|
|
11698
|
-
|
|
11703
|
+
}
|
|
11704
|
+
function setConfig(userConfig) {
|
|
11705
|
+
if (userConfig.dialect && _lastConfiguredDialect && userConfig.dialect !== _lastConfiguredDialect) {
|
|
11706
|
+
const key = `${_lastConfiguredDialect}->${userConfig.dialect}`;
|
|
11707
|
+
if (!_warnedDialectConflicts.has(key)) {
|
|
11708
|
+
_warnedDialectConflicts.add(key);
|
|
11709
|
+
console.warn(`[query-builder] setConfig({ dialect: '${userConfig.dialect}' }) overrides a previous ` + `setConfig({ dialect: '${_lastConfiguredDialect}' }). ` + `Config is process-wide; in-flight queries from the previous configuration may break. ` + `Run conflicting connections in separate processes \u2014 see stacksjs/stacks#1862 #18.`);
|
|
11710
|
+
}
|
|
11699
11711
|
}
|
|
11712
|
+
if (userConfig.dialect)
|
|
11713
|
+
_lastConfiguredDialect = userConfig.dialect;
|
|
11714
|
+
for (const key of Object.keys(userConfig))
|
|
11715
|
+
configState.explicit.add(key);
|
|
11716
|
+
applyConfig(userConfig);
|
|
11700
11717
|
}
|
|
11701
|
-
var defaultConfig4, CONFIG_SINGLETON_KEY, config5,
|
|
11718
|
+
var defaultConfig4, CONFIG_SINGLETON_KEY, config5, CONFIG_STATE_KEY, configState, _lastConfiguredDialect = null, _warnedDialectConflicts;
|
|
11702
11719
|
var init_config = __esm(() => {
|
|
11703
11720
|
init_dist();
|
|
11704
11721
|
defaultConfig4 = {
|
|
@@ -11761,6 +11778,8 @@ var init_config = __esm(() => {
|
|
|
11761
11778
|
};
|
|
11762
11779
|
CONFIG_SINGLETON_KEY = Symbol.for("bun-query-builder.config");
|
|
11763
11780
|
config5 = globalThis[CONFIG_SINGLETON_KEY] ??= { ...defaultConfig4 };
|
|
11781
|
+
CONFIG_STATE_KEY = Symbol.for("bun-query-builder.config-state");
|
|
11782
|
+
configState = globalThis[CONFIG_STATE_KEY] ??= { explicit: new Set, fileLoaded: false };
|
|
11764
11783
|
_warnedDialectConflicts = new Set;
|
|
11765
11784
|
});
|
|
11766
11785
|
|
|
@@ -11791,7 +11810,7 @@ var {SQL } = globalThis.Bun;
|
|
|
11791
11810
|
import { Database } from "bun:sqlite";
|
|
11792
11811
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11793
11812
|
import { dirname as dirname5 } from "path";
|
|
11794
|
-
import
|
|
11813
|
+
import process21 from "process";
|
|
11795
11814
|
|
|
11796
11815
|
class SQLiteWrapper {
|
|
11797
11816
|
db;
|
|
@@ -12069,9 +12088,13 @@ function sqliteFileFromUrl(url) {
|
|
|
12069
12088
|
const path = stripped.startsWith("///") ? stripped.slice(2) : stripped.startsWith("//") ? stripped.slice(2) : stripped;
|
|
12070
12089
|
return path || ":memory:";
|
|
12071
12090
|
}
|
|
12091
|
+
function resolveDatabaseName(dbConfig = config5.database) {
|
|
12092
|
+
const raw = process21.env.DB_DATABASE || dbConfig?.database || "";
|
|
12093
|
+
return String(raw).replace(/^['"]|['"]$/g, "");
|
|
12094
|
+
}
|
|
12072
12095
|
function createConnectionString(dialect, dbConfig) {
|
|
12073
|
-
if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") &&
|
|
12074
|
-
const e =
|
|
12096
|
+
if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") && process21.env.DB_CONNECTION === dialect) {
|
|
12097
|
+
const e = process21.env;
|
|
12075
12098
|
const envDb = (e.DB_DATABASE || dbConfig.database || "").replace(/^['"]|['"]$/g, "");
|
|
12076
12099
|
const envUser = e.DB_USERNAME || dbConfig.username;
|
|
12077
12100
|
const envPass = e.DB_PASSWORD ?? dbConfig.password ?? "";
|
|
@@ -12085,7 +12108,7 @@ function createConnectionString(dialect, dbConfig) {
|
|
|
12085
12108
|
return dialect === "sqlite" ? sqliteFileFromUrl(dbConfig.url) : dbConfig.url;
|
|
12086
12109
|
}
|
|
12087
12110
|
const { database, username, password, host = "localhost", port, ssl } = dbConfig;
|
|
12088
|
-
const sslQ = ssl ||
|
|
12111
|
+
const sslQ = ssl || process21.env.DB_SSL === "true" || process21.env.DB_SSL === "1" ? "?ssl=true" : "";
|
|
12089
12112
|
switch (dialect) {
|
|
12090
12113
|
case "postgres":
|
|
12091
12114
|
return `postgres://${username}:${password}@${host}${port ? `:${port}` : ""}/${database}${sslQ}`;
|
|
@@ -12359,6 +12382,17 @@ var init_benchmark = __esm(() => {
|
|
|
12359
12382
|
});
|
|
12360
12383
|
|
|
12361
12384
|
// src/inflect.ts
|
|
12385
|
+
function toTableName(modelName) {
|
|
12386
|
+
const snake = String(modelName).replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
12387
|
+
if (snake.endsWith("y") && !/[aeou]y$/.test(snake))
|
|
12388
|
+
return `${snake.slice(0, -1)}ies`;
|
|
12389
|
+
if (snake.endsWith("s") || snake.endsWith("x") || snake.endsWith("ch") || snake.endsWith("sh"))
|
|
12390
|
+
return `${snake}es`;
|
|
12391
|
+
return `${snake}s`;
|
|
12392
|
+
}
|
|
12393
|
+
function tableNameFor(model) {
|
|
12394
|
+
return typeof model?.table === "string" && model.table.length > 0 ? model.table : toTableName(String(model?.name ?? ""));
|
|
12395
|
+
}
|
|
12362
12396
|
function stripTrailingS(name) {
|
|
12363
12397
|
return name.endsWith("s") ? name.slice(0, -1) : name;
|
|
12364
12398
|
}
|
|
@@ -16579,7 +16613,7 @@ var init_cache = __esm(() => {
|
|
|
16579
16613
|
});
|
|
16580
16614
|
|
|
16581
16615
|
// src/actions/console.ts
|
|
16582
|
-
import
|
|
16616
|
+
import process22 from "process";
|
|
16583
16617
|
import { createInterface } from "readline";
|
|
16584
16618
|
async function startConsole() {
|
|
16585
16619
|
console.log("-- Query Builder Interactive Console");
|
|
@@ -16588,8 +16622,8 @@ async function startConsole() {
|
|
|
16588
16622
|
console.log();
|
|
16589
16623
|
const qb = createQueryBuilder();
|
|
16590
16624
|
const rl = createInterface({
|
|
16591
|
-
input:
|
|
16592
|
-
output:
|
|
16625
|
+
input: process22.stdin,
|
|
16626
|
+
output: process22.stdout,
|
|
16593
16627
|
prompt: "qb> "
|
|
16594
16628
|
});
|
|
16595
16629
|
let multilineBuffer = "";
|
|
@@ -16640,7 +16674,7 @@ Tips:
|
|
|
16640
16674
|
if (cmd === ".exit" || cmd === ".quit") {
|
|
16641
16675
|
console.log("Goodbye!");
|
|
16642
16676
|
rl.close();
|
|
16643
|
-
|
|
16677
|
+
process22.exit(0);
|
|
16644
16678
|
} else if (cmd === ".help") {
|
|
16645
16679
|
console.log(helpText);
|
|
16646
16680
|
} else if (cmd === ".clear") {
|
|
@@ -16711,7 +16745,7 @@ Tips:
|
|
|
16711
16745
|
rl.on("close", () => {
|
|
16712
16746
|
console.log(`
|
|
16713
16747
|
Goodbye!`);
|
|
16714
|
-
|
|
16748
|
+
process22.exit(0);
|
|
16715
16749
|
});
|
|
16716
16750
|
rl.prompt();
|
|
16717
16751
|
}
|
|
@@ -17089,9 +17123,8 @@ var init_db_info = __esm(() => {
|
|
|
17089
17123
|
});
|
|
17090
17124
|
|
|
17091
17125
|
// src/actions/db-optimize.ts
|
|
17092
|
-
import process22 from "process";
|
|
17093
17126
|
async function dbOptimize(options = {}) {
|
|
17094
|
-
const dialect = options.dialect
|
|
17127
|
+
const dialect = resolveDialect(options.dialect);
|
|
17095
17128
|
const aggressive = options.aggressive || false;
|
|
17096
17129
|
if (options.verbose) {
|
|
17097
17130
|
console.log(`Optimizing ${dialect} database${aggressive ? " (aggressive mode)" : ""}...`);
|
|
@@ -17131,7 +17164,7 @@ async function dbOptimize(options = {}) {
|
|
|
17131
17164
|
await bunSql`ANALYZE TABLE ${bunSql(table)}`;
|
|
17132
17165
|
}
|
|
17133
17166
|
} else {
|
|
17134
|
-
const dbName =
|
|
17167
|
+
const dbName = resolveDatabaseName();
|
|
17135
17168
|
const tables = await bunSql`
|
|
17136
17169
|
SELECT table_name
|
|
17137
17170
|
FROM information_schema.tables
|
|
@@ -17184,9 +17217,8 @@ var init_db_optimize = __esm(() => {
|
|
|
17184
17217
|
});
|
|
17185
17218
|
|
|
17186
17219
|
// src/actions/db-wipe.ts
|
|
17187
|
-
import process23 from "process";
|
|
17188
17220
|
async function dbWipe(options = {}) {
|
|
17189
|
-
const dialect = options.dialect
|
|
17221
|
+
const dialect = resolveDialect(options.dialect);
|
|
17190
17222
|
if (options.verbose) {
|
|
17191
17223
|
console.log(`Wiping all tables from ${dialect} database...`);
|
|
17192
17224
|
}
|
|
@@ -17200,7 +17232,7 @@ async function dbWipe(options = {}) {
|
|
|
17200
17232
|
`;
|
|
17201
17233
|
tables = result.map((row) => row.tablename);
|
|
17202
17234
|
} else if (isMysqlLike(dialect)) {
|
|
17203
|
-
const dbName =
|
|
17235
|
+
const dbName = resolveDatabaseName();
|
|
17204
17236
|
const result = await bunSql`
|
|
17205
17237
|
SELECT table_name
|
|
17206
17238
|
FROM information_schema.tables
|
|
@@ -17637,6 +17669,8 @@ function sqlTypeToNormalized(rawType, dialect, ctx) {
|
|
|
17637
17669
|
return "decimal";
|
|
17638
17670
|
if (base === "date")
|
|
17639
17671
|
return "date";
|
|
17672
|
+
if (base === "timestamptz" || /\bwith time zone\b/.test(t))
|
|
17673
|
+
return "timestamptz";
|
|
17640
17674
|
if (/^(?:timestamp|datetime)/.test(base))
|
|
17641
17675
|
return "datetime";
|
|
17642
17676
|
if (/^time/.test(base))
|
|
@@ -17892,7 +17926,7 @@ var init_introspect_db = __esm(() => {
|
|
|
17892
17926
|
// src/actions/make-model.ts
|
|
17893
17927
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
17894
17928
|
import { dirname as dirname7, join as join7 } from "path";
|
|
17895
|
-
import
|
|
17929
|
+
import process23 from "process";
|
|
17896
17930
|
function findWorkspaceRoot(startPath) {
|
|
17897
17931
|
let currentPath = startPath;
|
|
17898
17932
|
while (currentPath !== dirname7(currentPath)) {
|
|
@@ -17901,10 +17935,10 @@ function findWorkspaceRoot(startPath) {
|
|
|
17901
17935
|
}
|
|
17902
17936
|
currentPath = dirname7(currentPath);
|
|
17903
17937
|
}
|
|
17904
|
-
return
|
|
17938
|
+
return process23.cwd();
|
|
17905
17939
|
}
|
|
17906
17940
|
async function makeModel(name, options = {}) {
|
|
17907
|
-
const workspaceRoot = findWorkspaceRoot(
|
|
17941
|
+
const workspaceRoot = findWorkspaceRoot(process23.cwd());
|
|
17908
17942
|
const modelsDir = options.dir || join7(workspaceRoot, "app/Models");
|
|
17909
17943
|
if (!existsSync14(modelsDir)) {
|
|
17910
17944
|
mkdirSync7(modelsDir, { recursive: true });
|
|
@@ -18100,7 +18134,7 @@ class MySQLDriver {
|
|
|
18100
18134
|
)`;
|
|
18101
18135
|
}
|
|
18102
18136
|
getExecutedMigrationsQuery() {
|
|
18103
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18137
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18104
18138
|
}
|
|
18105
18139
|
recordMigrationQuery() {
|
|
18106
18140
|
return "INSERT INTO migrations (migration) VALUES (?)";
|
|
@@ -18281,7 +18315,7 @@ class PostgresDriver {
|
|
|
18281
18315
|
)`;
|
|
18282
18316
|
}
|
|
18283
18317
|
getExecutedMigrationsQuery() {
|
|
18284
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18318
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18285
18319
|
}
|
|
18286
18320
|
recordMigrationQuery() {
|
|
18287
18321
|
return "INSERT INTO migrations (migration) VALUES ($1)";
|
|
@@ -18526,7 +18560,7 @@ class SQLiteDriver {
|
|
|
18526
18560
|
)`;
|
|
18527
18561
|
}
|
|
18528
18562
|
getExecutedMigrationsQuery() {
|
|
18529
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18563
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18530
18564
|
}
|
|
18531
18565
|
recordMigrationQuery() {
|
|
18532
18566
|
return "INSERT INTO migrations (migration) VALUES (?)";
|
|
@@ -18579,6 +18613,24 @@ var init_drivers = __esm(() => {
|
|
|
18579
18613
|
init_singlestore();
|
|
18580
18614
|
});
|
|
18581
18615
|
|
|
18616
|
+
// src/workspace.ts
|
|
18617
|
+
import { existsSync as existsSync16 } from "fs";
|
|
18618
|
+
import { dirname as dirname8, join as join9 } from "path";
|
|
18619
|
+
import process24 from "process";
|
|
18620
|
+
function findWorkspaceRoot2(startPath = process24.cwd()) {
|
|
18621
|
+
let currentPath = startPath;
|
|
18622
|
+
while (currentPath !== dirname8(currentPath)) {
|
|
18623
|
+
if (existsSync16(join9(currentPath, "package.json")))
|
|
18624
|
+
return currentPath;
|
|
18625
|
+
currentPath = dirname8(currentPath);
|
|
18626
|
+
}
|
|
18627
|
+
return process24.cwd();
|
|
18628
|
+
}
|
|
18629
|
+
function getSqlDirectory(workspaceRoot) {
|
|
18630
|
+
return join9(workspaceRoot ?? findWorkspaceRoot2(), "database", "migrations");
|
|
18631
|
+
}
|
|
18632
|
+
var init_workspace = () => {};
|
|
18633
|
+
|
|
18582
18634
|
// src/actions/migrate.ts
|
|
18583
18635
|
var exports_migrate = {};
|
|
18584
18636
|
__export(exports_migrate, {
|
|
@@ -18590,23 +18642,48 @@ __export(exports_migrate, {
|
|
|
18590
18642
|
copyModelsToGenerated: () => copyModelsToGenerated,
|
|
18591
18643
|
clearGeneratedDirectory: () => clearGeneratedDirectory
|
|
18592
18644
|
});
|
|
18593
|
-
import { existsSync as
|
|
18645
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync3, rmSync, unlinkSync, writeFileSync as writeFileSync11 } from "fs";
|
|
18594
18646
|
import { tmpdir } from "os";
|
|
18595
|
-
import { join as
|
|
18647
|
+
import { join as join10 } from "path";
|
|
18596
18648
|
import process25 from "process";
|
|
18649
|
+
async function settleAppliedFiles(qb, dialect, files) {
|
|
18650
|
+
if (files.length === 0)
|
|
18651
|
+
return;
|
|
18652
|
+
const sqlDir = ensureSqlDirectory(getWorkspaceRoot());
|
|
18653
|
+
try {
|
|
18654
|
+
await createMigrationsTable(qb, dialect);
|
|
18655
|
+
for (const file2 of files) {
|
|
18656
|
+
await recordMigration(qb, file2, dialect);
|
|
18657
|
+
info(`-- \u2713 Recorded applied migration: ${file2}`);
|
|
18658
|
+
}
|
|
18659
|
+
} catch (err) {
|
|
18660
|
+
info(`-- Could not record applied migrations (the schema change itself succeeded): ${err instanceof Error ? err.message : String(err)}`);
|
|
18661
|
+
}
|
|
18662
|
+
}
|
|
18663
|
+
function isGeneratedMigration(sqlDir, file2) {
|
|
18664
|
+
try {
|
|
18665
|
+
return isGeneratedMigrationSql(readFileSync3(join10(sqlDir, file2), "utf8"));
|
|
18666
|
+
} catch {
|
|
18667
|
+
return false;
|
|
18668
|
+
}
|
|
18669
|
+
}
|
|
18670
|
+
function isAlreadyAppliedError(err) {
|
|
18671
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18672
|
+
return /duplicate column name/i.test(message) || /duplicate key name/i.test(message) || /\balready exists\b/i.test(message);
|
|
18673
|
+
}
|
|
18597
18674
|
function info(message) {
|
|
18598
18675
|
if (config5.verbose)
|
|
18599
18676
|
console.log(message);
|
|
18600
18677
|
}
|
|
18601
18678
|
function getSnapshotDir(workspaceRoot) {
|
|
18602
|
-
return
|
|
18679
|
+
return join10(workspaceRoot, config5.snapshotDir || ".qb");
|
|
18603
18680
|
}
|
|
18604
18681
|
function getSnapshotPath(workspaceRoot, dialect) {
|
|
18605
|
-
return
|
|
18682
|
+
return join10(getSnapshotDir(workspaceRoot), `model-snapshot.${dialect}.json`);
|
|
18606
18683
|
}
|
|
18607
18684
|
function loadPlanSnapshot(workspaceRoot, dialect) {
|
|
18608
18685
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18609
|
-
if (!
|
|
18686
|
+
if (!existsSync17(snapshotPath)) {
|
|
18610
18687
|
return;
|
|
18611
18688
|
}
|
|
18612
18689
|
try {
|
|
@@ -18629,7 +18706,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
18629
18706
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18630
18707
|
const snapshotDir = getSnapshotDir(workspaceRoot);
|
|
18631
18708
|
const hash = hashMigrationPlan(plan);
|
|
18632
|
-
if (
|
|
18709
|
+
if (existsSync17(snapshotPath)) {
|
|
18633
18710
|
try {
|
|
18634
18711
|
const current = JSON.parse(readFileSync3(snapshotPath, "utf8"));
|
|
18635
18712
|
if (current?.hash === hash && current?.dialect === dialect && current?.plan) {
|
|
@@ -18638,7 +18715,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
18638
18715
|
}
|
|
18639
18716
|
} catch {}
|
|
18640
18717
|
}
|
|
18641
|
-
if (!
|
|
18718
|
+
if (!existsSync17(snapshotDir)) {
|
|
18642
18719
|
mkdirSync8(snapshotDir, { recursive: true });
|
|
18643
18720
|
info(`-- Created snapshot directory: ${snapshotDir}`);
|
|
18644
18721
|
}
|
|
@@ -18656,11 +18733,11 @@ function saveMigrationSnapshot(plan, options = {}) {
|
|
|
18656
18733
|
savePlanSnapshot(options.workspaceRoot ?? getWorkspaceRoot(), dialect, plan);
|
|
18657
18734
|
}
|
|
18658
18735
|
function getWorkspaceRoot() {
|
|
18659
|
-
return
|
|
18736
|
+
return findWorkspaceRoot2();
|
|
18660
18737
|
}
|
|
18661
18738
|
function ensureSqlDirectory(workspaceRoot) {
|
|
18662
18739
|
const sqlDir = getSqlDirectory(workspaceRoot);
|
|
18663
|
-
if (!
|
|
18740
|
+
if (!existsSync17(sqlDir)) {
|
|
18664
18741
|
mkdirSync8(sqlDir, { recursive: true });
|
|
18665
18742
|
info(`-- Created SQL directory: ${sqlDir}`);
|
|
18666
18743
|
}
|
|
@@ -18669,21 +18746,22 @@ function ensureSqlDirectory(workspaceRoot) {
|
|
|
18669
18746
|
async function generateMigration(dir, opts = {}) {
|
|
18670
18747
|
await getConfig();
|
|
18671
18748
|
if (!dir) {
|
|
18672
|
-
dir =
|
|
18749
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18673
18750
|
}
|
|
18674
18751
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
18675
18752
|
const workspaceRoot = getWorkspaceRoot();
|
|
18676
18753
|
const models = await loadModels({ modelsDir: dir });
|
|
18677
18754
|
const plan = buildMigrationPlan2(models, { dialect });
|
|
18678
18755
|
let previous;
|
|
18756
|
+
let reconciledFromDatabase = false;
|
|
18679
18757
|
if (!opts.full) {
|
|
18680
18758
|
previous = loadPlanSnapshot(workspaceRoot, dialect);
|
|
18681
18759
|
if (previous) {
|
|
18682
18760
|
info("-- Comparing with stored model snapshot");
|
|
18683
18761
|
} else {
|
|
18684
|
-
const defaultStatePath =
|
|
18762
|
+
const defaultStatePath = join10(dir, `.qb-migrations.${dialect}.json`);
|
|
18685
18763
|
const statePath = String(opts.state || defaultStatePath);
|
|
18686
|
-
if (
|
|
18764
|
+
if (existsSync17(statePath)) {
|
|
18687
18765
|
try {
|
|
18688
18766
|
const raw2 = readFileSync3(statePath, "utf8");
|
|
18689
18767
|
const parsed = JSON.parse(raw2);
|
|
@@ -18700,6 +18778,7 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18700
18778
|
const scoped = livePlan.tables.filter((t) => modelTables.has(t.table));
|
|
18701
18779
|
if (scoped.length > 0) {
|
|
18702
18780
|
previous = { dialect: livePlan.dialect, tables: scoped };
|
|
18781
|
+
reconciledFromDatabase = true;
|
|
18703
18782
|
info(`-- No snapshot; reconciling against live database (${scoped.length} matching table${scoped.length === 1 ? "" : "s"})`);
|
|
18704
18783
|
}
|
|
18705
18784
|
} catch (err) {
|
|
@@ -18713,25 +18792,34 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18713
18792
|
} else {
|
|
18714
18793
|
info("-- Full migration requested, ignoring any previous state");
|
|
18715
18794
|
}
|
|
18716
|
-
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, {
|
|
18795
|
+
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, {
|
|
18796
|
+
applyRenames: opts.applyRenames,
|
|
18797
|
+
dryRun: opts.dryRun,
|
|
18798
|
+
preserveUnknownColumns: reconciledFromDatabase
|
|
18799
|
+
});
|
|
18717
18800
|
const sqlStatements = diff.statements;
|
|
18718
18801
|
const operations = diff.operations;
|
|
18719
18802
|
const sql = sqlStatements.join(`
|
|
18720
18803
|
`);
|
|
18721
18804
|
const hasChanges = sqlStatements.some((stmt) => /\b(?:CREATE|ALTER|DROP)\b/i.test(stmt));
|
|
18722
18805
|
if (opts.apply) {
|
|
18723
|
-
const dirPath = mkdtempSync(
|
|
18724
|
-
const filePath =
|
|
18806
|
+
const dirPath = mkdtempSync(join10(tmpdir(), "qb-migrate-"));
|
|
18807
|
+
const filePath = join10(dirPath, "migration.sql");
|
|
18725
18808
|
try {
|
|
18726
18809
|
if (hasChanges) {
|
|
18727
18810
|
writeFileSync11(filePath, sql);
|
|
18811
|
+
const qb = createQueryBuilder();
|
|
18812
|
+
await qb.file(filePath);
|
|
18728
18813
|
info("-- Migration applied");
|
|
18814
|
+
await settleAppliedFiles(qb, dialect, lastCreatedMigrationFiles());
|
|
18729
18815
|
} else {
|
|
18730
18816
|
info("-- No changes; nothing to apply");
|
|
18731
18817
|
}
|
|
18732
18818
|
} catch (err) {
|
|
18733
18819
|
console.error("-- Migration failed:", err);
|
|
18734
18820
|
throw err;
|
|
18821
|
+
} finally {
|
|
18822
|
+
rmSync(dirPath, { recursive: true, force: true });
|
|
18735
18823
|
}
|
|
18736
18824
|
}
|
|
18737
18825
|
if (!opts.dryRun)
|
|
@@ -18740,9 +18828,6 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18740
18828
|
}
|
|
18741
18829
|
async function executeMigration(dir) {
|
|
18742
18830
|
await getConfig();
|
|
18743
|
-
if (!dir) {
|
|
18744
|
-
dir = join9(process25.cwd(), "app/Models");
|
|
18745
|
-
}
|
|
18746
18831
|
const workspaceRoot = getWorkspaceRoot();
|
|
18747
18832
|
const sqlDir = ensureSqlDirectory(workspaceRoot);
|
|
18748
18833
|
const dialect = config5.dialect || "postgres";
|
|
@@ -18757,42 +18842,25 @@ async function executeMigration(dir) {
|
|
|
18757
18842
|
const qb = createQueryBuilder();
|
|
18758
18843
|
await createMigrationsTable(qb, dialect);
|
|
18759
18844
|
const executedMigrations = await getExecutedMigrations(qb, dialect);
|
|
18760
|
-
const
|
|
18761
|
-
|
|
18762
|
-
for (const file2 of scriptFiles) {
|
|
18763
|
-
if (file2.includes("alter-") && file2.includes("-table")) {
|
|
18764
|
-
transientMigrations.push(file2);
|
|
18765
|
-
} else if (!executedMigrations.includes(file2)) {
|
|
18766
|
-
permanentMigrations.push(file2);
|
|
18767
|
-
}
|
|
18768
|
-
}
|
|
18769
|
-
const totalPending = permanentMigrations.length + transientMigrations.length;
|
|
18770
|
-
if (totalPending === 0) {
|
|
18845
|
+
const pending = scriptFiles.filter((file2) => !executedMigrations.includes(file2));
|
|
18846
|
+
if (pending.length === 0) {
|
|
18771
18847
|
info("-- No pending migrations to execute");
|
|
18772
18848
|
return true;
|
|
18773
18849
|
}
|
|
18774
|
-
info(`-- Executing ${
|
|
18775
|
-
for (const file2 of
|
|
18776
|
-
const filePath =
|
|
18850
|
+
info(`-- Executing ${pending.length} migration${pending.length === 1 ? "" : "s"}`);
|
|
18851
|
+
for (const file2 of pending) {
|
|
18852
|
+
const filePath = join10(sqlDir, file2);
|
|
18777
18853
|
info(`-- Executing: ${file2}`);
|
|
18778
18854
|
try {
|
|
18779
18855
|
await qb.file(filePath);
|
|
18780
18856
|
await recordMigration(qb, file2, dialect);
|
|
18781
18857
|
info(`-- \u2713 Migration ${file2} executed and recorded`);
|
|
18782
18858
|
} catch (err) {
|
|
18783
|
-
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
|
|
18788
|
-
const filePath = join9(sqlDir, file2);
|
|
18789
|
-
info(`-- Executing: ${file2} (transient)`);
|
|
18790
|
-
try {
|
|
18791
|
-
await qb.file(filePath);
|
|
18792
|
-
info(`-- \u2713 Migration ${file2} executed (not recorded)`);
|
|
18793
|
-
unlinkSync(filePath);
|
|
18794
|
-
info(`-- \uD83D\uDDD1\uFE0F Deleted transient migration: ${file2}`);
|
|
18795
|
-
} catch (err) {
|
|
18859
|
+
if (isGeneratedMigration(sqlDir, file2) && isAlreadyAppliedError(err)) {
|
|
18860
|
+
await recordMigration(qb, file2, dialect);
|
|
18861
|
+
info(`-- \u2713 Migration ${file2} was already applied; recorded it (${err instanceof Error ? err.message : String(err)})`);
|
|
18862
|
+
continue;
|
|
18863
|
+
}
|
|
18796
18864
|
console.error(`-- \u2717 Migration ${file2} failed:`, err);
|
|
18797
18865
|
throw err;
|
|
18798
18866
|
}
|
|
@@ -18807,7 +18875,7 @@ async function executeMigration(dir) {
|
|
|
18807
18875
|
async function resetDatabase(dir, opts = {}) {
|
|
18808
18876
|
await getConfig();
|
|
18809
18877
|
if (!dir) {
|
|
18810
|
-
dir =
|
|
18878
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18811
18879
|
}
|
|
18812
18880
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
18813
18881
|
const driver = getDialectDriver(dialect);
|
|
@@ -18904,42 +18972,50 @@ async function resetDatabase(dir, opts = {}) {
|
|
|
18904
18972
|
}
|
|
18905
18973
|
async function deleteMigrationFiles(dir, workspaceRoot, opts = {}) {
|
|
18906
18974
|
if (!dir) {
|
|
18907
|
-
dir =
|
|
18975
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18908
18976
|
}
|
|
18909
18977
|
if (!workspaceRoot) {
|
|
18910
18978
|
workspaceRoot = getWorkspaceRoot();
|
|
18911
18979
|
}
|
|
18912
18980
|
const dialect = String(opts.dialect || config5.dialect || "postgres");
|
|
18913
18981
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18914
|
-
if (
|
|
18982
|
+
if (existsSync17(snapshotPath)) {
|
|
18915
18983
|
unlinkSync(snapshotPath);
|
|
18916
18984
|
info(`-- Removed model snapshot file: ${snapshotPath}`);
|
|
18917
18985
|
}
|
|
18918
|
-
const defaultStatePath =
|
|
18986
|
+
const defaultStatePath = join10(dir, `.qb-migrations.${dialect}.json`);
|
|
18919
18987
|
const statePath = String(opts.state || defaultStatePath);
|
|
18920
|
-
if (
|
|
18988
|
+
if (existsSync17(statePath)) {
|
|
18921
18989
|
unlinkSync(statePath);
|
|
18922
18990
|
info(`-- Removed legacy migration state file: ${statePath}`);
|
|
18923
18991
|
}
|
|
18924
|
-
|
|
18925
|
-
|
|
18926
|
-
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
|
|
18931
|
-
|
|
18992
|
+
removeGeneratedMigrationFiles(getSqlDirectory(workspaceRoot));
|
|
18993
|
+
}
|
|
18994
|
+
function removeGeneratedMigrationFiles(sqlDir) {
|
|
18995
|
+
if (!existsSync17(sqlDir))
|
|
18996
|
+
return;
|
|
18997
|
+
let removed = 0;
|
|
18998
|
+
const kept = [];
|
|
18999
|
+
for (const file2 of readdirSync6(sqlDir).filter((f) => f.endsWith(".sql"))) {
|
|
19000
|
+
if (!isGeneratedMigration(sqlDir, file2)) {
|
|
19001
|
+
kept.push(file2);
|
|
19002
|
+
continue;
|
|
18932
19003
|
}
|
|
18933
|
-
|
|
19004
|
+
unlinkSync(join10(sqlDir, file2));
|
|
19005
|
+
info(`-- Removed generated migration file: ${file2}`);
|
|
19006
|
+
removed += 1;
|
|
18934
19007
|
}
|
|
19008
|
+
info(`-- Cleaned up ${removed} generated migration file${removed === 1 ? "" : "s"} from migrations directory`);
|
|
19009
|
+
if (kept.length > 0)
|
|
19010
|
+
info(`-- Kept ${kept.length} hand-written migration${kept.length === 1 ? "" : "s"}: ${kept.slice(0, 5).join(", ")}${kept.length > 5 ? `, +${kept.length - 5} more` : ""}`);
|
|
18935
19011
|
}
|
|
18936
19012
|
async function copyModelsToGenerated(_dir, _workspaceRoot) {}
|
|
18937
19013
|
async function clearGeneratedDirectory(workspaceRoot) {
|
|
18938
19014
|
if (!workspaceRoot) {
|
|
18939
19015
|
workspaceRoot = getWorkspaceRoot();
|
|
18940
19016
|
}
|
|
18941
|
-
const generatedDir =
|
|
18942
|
-
if (
|
|
19017
|
+
const generatedDir = join10(workspaceRoot, "generated");
|
|
19018
|
+
if (existsSync17(generatedDir)) {
|
|
18943
19019
|
try {
|
|
18944
19020
|
rmSync(generatedDir, { recursive: true, force: true });
|
|
18945
19021
|
info("-- Cleared generated directory");
|
|
@@ -18948,12 +19024,6 @@ async function clearGeneratedDirectory(workspaceRoot) {
|
|
|
18948
19024
|
}
|
|
18949
19025
|
}
|
|
18950
19026
|
}
|
|
18951
|
-
function getSqlDirectory(workspaceRoot) {
|
|
18952
|
-
if (!workspaceRoot) {
|
|
18953
|
-
workspaceRoot = getWorkspaceRoot();
|
|
18954
|
-
}
|
|
18955
|
-
return join9(workspaceRoot, "database", "migrations");
|
|
18956
|
-
}
|
|
18957
19027
|
async function createMigrationsTable(qb, dialect) {
|
|
18958
19028
|
const driver = getDialectDriver(dialect);
|
|
18959
19029
|
const createTableSql = driver.createMigrationsTable();
|
|
@@ -18992,6 +19062,7 @@ var init_migrate = __esm(() => {
|
|
|
18992
19062
|
init_drivers();
|
|
18993
19063
|
init_src2();
|
|
18994
19064
|
init_introspect_db();
|
|
19065
|
+
init_workspace();
|
|
18995
19066
|
});
|
|
18996
19067
|
|
|
18997
19068
|
// src/actions/migrate-generate.ts
|
|
@@ -19003,28 +19074,49 @@ var init_migrate_generate = __esm(() => {
|
|
|
19003
19074
|
});
|
|
19004
19075
|
|
|
19005
19076
|
// src/actions/migrate-rollback.ts
|
|
19006
|
-
import { existsSync as
|
|
19007
|
-
import {
|
|
19008
|
-
import process26 from "process";
|
|
19077
|
+
import { existsSync as existsSync18, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
19078
|
+
import { join as join11 } from "path";
|
|
19009
19079
|
function splitSqlStatements2(sql) {
|
|
19010
19080
|
const out = [];
|
|
19011
19081
|
let buf = "";
|
|
19012
|
-
let
|
|
19013
|
-
|
|
19014
|
-
|
|
19015
|
-
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19082
|
+
let quote = null;
|
|
19083
|
+
for (let i = 0;i < sql.length; i++) {
|
|
19084
|
+
const ch = sql[i];
|
|
19085
|
+
if (quote) {
|
|
19086
|
+
buf += ch;
|
|
19087
|
+
if (ch === quote) {
|
|
19088
|
+
if (sql[i + 1] === quote) {
|
|
19089
|
+
buf += sql[i + 1];
|
|
19090
|
+
i++;
|
|
19091
|
+
} else {
|
|
19092
|
+
quote = null;
|
|
19093
|
+
}
|
|
19094
|
+
}
|
|
19095
|
+
continue;
|
|
19096
|
+
}
|
|
19097
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
19098
|
+
quote = ch;
|
|
19099
|
+
buf += ch;
|
|
19100
|
+
continue;
|
|
19101
|
+
}
|
|
19102
|
+
if (ch === "-" && sql[i + 1] === "-") {
|
|
19103
|
+
const newline = sql.indexOf(`
|
|
19104
|
+
`, i);
|
|
19105
|
+
i = newline === -1 ? sql.length : newline;
|
|
19106
|
+
continue;
|
|
19107
|
+
}
|
|
19108
|
+
if (ch === "/" && sql[i + 1] === "*") {
|
|
19109
|
+
const close = sql.indexOf("*/", i + 2);
|
|
19110
|
+
i = close === -1 ? sql.length : close + 1;
|
|
19111
|
+
continue;
|
|
19112
|
+
}
|
|
19113
|
+
if (ch === ";") {
|
|
19022
19114
|
if (buf.trim())
|
|
19023
19115
|
out.push(buf.trim());
|
|
19024
19116
|
buf = "";
|
|
19025
|
-
|
|
19026
|
-
buf += ch;
|
|
19117
|
+
continue;
|
|
19027
19118
|
}
|
|
19119
|
+
buf += ch;
|
|
19028
19120
|
}
|
|
19029
19121
|
if (buf.trim())
|
|
19030
19122
|
out.push(buf.trim());
|
|
@@ -19038,8 +19130,20 @@ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
|
19038
19130
|
let m;
|
|
19039
19131
|
if (m = /^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
|
|
19040
19132
|
down.push(`DROP TABLE IF EXISTS ${q(m[1])}`);
|
|
19041
|
-
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+
|
|
19042
|
-
|
|
19133
|
+
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+CONSTRAINT\s+["`']?(\w+)["`']?([\s\S]*)$/i.exec(stmt)) {
|
|
19134
|
+
if (dialect === "sqlite") {
|
|
19135
|
+
skipped.push(stmt);
|
|
19136
|
+
} else if (isMysqlLike(dialect) && /\bFOREIGN\s+KEY\b/i.test(m[3] ?? "")) {
|
|
19137
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP FOREIGN KEY ${q(m[2])}`);
|
|
19138
|
+
} else {
|
|
19139
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP CONSTRAINT ${q(m[2])}`);
|
|
19140
|
+
}
|
|
19141
|
+
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+(?:COLUMN\s+["`']?(\w+)["`']?|["`'](\w+)["`']|(\w+))/i.exec(stmt)) {
|
|
19142
|
+
const column = m[2] ?? m[3] ?? m[4];
|
|
19143
|
+
if (m[4] && /^(?:PRIMARY|UNIQUE|INDEX|KEY|FOREIGN|CHECK|FULLTEXT|SPATIAL)$/i.test(m[4]))
|
|
19144
|
+
skipped.push(stmt);
|
|
19145
|
+
else
|
|
19146
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP COLUMN ${q(column)}`);
|
|
19043
19147
|
} else if (m = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?(?:\s+ON\s+["`']?(\w+)["`']?)?/i.exec(stmt)) {
|
|
19044
19148
|
down.push(isMysqlLike(dialect) && m[2] ? `DROP INDEX ${q(m[1])} ON ${q(m[2])}` : `DROP INDEX IF EXISTS ${q(m[1])}`);
|
|
19045
19149
|
} else {
|
|
@@ -19048,22 +19152,6 @@ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
|
19048
19152
|
}
|
|
19049
19153
|
return { down: down.reverse(), skipped };
|
|
19050
19154
|
}
|
|
19051
|
-
function findWorkspaceRoot2(startPath) {
|
|
19052
|
-
let currentPath = startPath;
|
|
19053
|
-
while (currentPath !== dirname8(currentPath)) {
|
|
19054
|
-
if (existsSync17(join10(currentPath, "package.json"))) {
|
|
19055
|
-
return currentPath;
|
|
19056
|
-
}
|
|
19057
|
-
currentPath = dirname8(currentPath);
|
|
19058
|
-
}
|
|
19059
|
-
return process26.cwd();
|
|
19060
|
-
}
|
|
19061
|
-
function getSqlDirectory2(workspaceRoot) {
|
|
19062
|
-
if (!workspaceRoot) {
|
|
19063
|
-
workspaceRoot = findWorkspaceRoot2(process26.cwd());
|
|
19064
|
-
}
|
|
19065
|
-
return join10(workspaceRoot, "database", "migrations");
|
|
19066
|
-
}
|
|
19067
19155
|
async function migrateRollback(options = {}) {
|
|
19068
19156
|
const steps = options.steps || 1;
|
|
19069
19157
|
const reverseSchema = options.reverseSchema !== false;
|
|
@@ -19096,12 +19184,12 @@ async function migrateRollback(options = {}) {
|
|
|
19096
19184
|
console.log(` - ${migration.migration}`);
|
|
19097
19185
|
}
|
|
19098
19186
|
console.log();
|
|
19099
|
-
const sqlDir =
|
|
19187
|
+
const sqlDir = getSqlDirectory();
|
|
19100
19188
|
let reversedAny = false;
|
|
19101
19189
|
for (const migration of migrationsToRollback) {
|
|
19102
19190
|
try {
|
|
19103
|
-
const filePath =
|
|
19104
|
-
if (reverseSchema &&
|
|
19191
|
+
const filePath = join11(sqlDir, migration.migration);
|
|
19192
|
+
if (reverseSchema && existsSync18(filePath)) {
|
|
19105
19193
|
const forwardSql = readFileSync4(filePath, "utf8");
|
|
19106
19194
|
const { down, skipped } = deriveDownStatements(forwardSql);
|
|
19107
19195
|
for (const stmt of down) {
|
|
@@ -19112,10 +19200,10 @@ async function migrateRollback(options = {}) {
|
|
|
19112
19200
|
if (skipped.length > 0)
|
|
19113
19201
|
console.log(`-- \u26A0\uFE0F ${skipped.length} statement(s) in ${migration.migration} could not be auto-reversed (data/complex DDL) \u2014 reverse manually.`);
|
|
19114
19202
|
}
|
|
19115
|
-
const deleteSql = `DELETE FROM migrations WHERE migration = $1`;
|
|
19203
|
+
const deleteSql = `DELETE FROM migrations WHERE migration = ${getPlaceholder(1)}`;
|
|
19116
19204
|
await qb.unsafe(deleteSql, [migration.migration]);
|
|
19117
19205
|
console.log(`-- \u2713 Removed migration record: ${migration.migration}`);
|
|
19118
|
-
if (
|
|
19206
|
+
if (options.deleteFiles && existsSync18(filePath)) {
|
|
19119
19207
|
unlinkSync2(filePath);
|
|
19120
19208
|
console.log(`-- \uD83D\uDDD1\uFE0F Deleted migration file: ${migration.migration}`);
|
|
19121
19209
|
}
|
|
@@ -19138,37 +19226,20 @@ async function migrateRollback(options = {}) {
|
|
|
19138
19226
|
var init_migrate_rollback = __esm(() => {
|
|
19139
19227
|
init_config();
|
|
19140
19228
|
init_src2();
|
|
19229
|
+
init_workspace();
|
|
19141
19230
|
});
|
|
19142
19231
|
|
|
19143
19232
|
// src/actions/migrate-status.ts
|
|
19144
|
-
import { existsSync as
|
|
19145
|
-
import { dirname as dirname9, join as join11 } from "path";
|
|
19146
|
-
import process27 from "process";
|
|
19147
|
-
function findWorkspaceRoot3(startPath) {
|
|
19148
|
-
let currentPath = startPath;
|
|
19149
|
-
while (currentPath !== dirname9(currentPath)) {
|
|
19150
|
-
if (existsSync18(join11(currentPath, "package.json"))) {
|
|
19151
|
-
return currentPath;
|
|
19152
|
-
}
|
|
19153
|
-
currentPath = dirname9(currentPath);
|
|
19154
|
-
}
|
|
19155
|
-
return process27.cwd();
|
|
19156
|
-
}
|
|
19157
|
-
function getSqlDirectory3(workspaceRoot) {
|
|
19158
|
-
if (!workspaceRoot) {
|
|
19159
|
-
workspaceRoot = findWorkspaceRoot3(process27.cwd());
|
|
19160
|
-
}
|
|
19161
|
-
return join11(workspaceRoot, "database", "migrations");
|
|
19162
|
-
}
|
|
19233
|
+
import { existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
|
|
19163
19234
|
async function migrateStatus() {
|
|
19164
19235
|
const dialect = config5.dialect || "postgres";
|
|
19165
19236
|
const driver = getDialectDriver(dialect);
|
|
19166
|
-
const sqlDir =
|
|
19237
|
+
const sqlDir = getSqlDirectory();
|
|
19167
19238
|
console.log("-- Migration Status");
|
|
19168
19239
|
console.log(`-- Dialect: ${dialect}`);
|
|
19169
19240
|
console.log(`-- SQL directory: ${sqlDir}`);
|
|
19170
19241
|
console.log();
|
|
19171
|
-
if (!
|
|
19242
|
+
if (!existsSync19(sqlDir)) {
|
|
19172
19243
|
console.log("-- No SQL directory found. No migrations have been created yet.");
|
|
19173
19244
|
return [];
|
|
19174
19245
|
}
|
|
@@ -19189,27 +19260,16 @@ async function migrateStatus() {
|
|
|
19189
19260
|
console.log();
|
|
19190
19261
|
}
|
|
19191
19262
|
const executedMap = new Map(executedMigrations.map((m) => [m.migration, m.executed_at]));
|
|
19192
|
-
const statuses = migrationFiles.map((file2) => {
|
|
19193
|
-
|
|
19194
|
-
|
|
19195
|
-
|
|
19196
|
-
|
|
19197
|
-
};
|
|
19198
|
-
}
|
|
19199
|
-
const executedAt = executedMap.get(file2);
|
|
19200
|
-
return {
|
|
19201
|
-
file: file2,
|
|
19202
|
-
status: executedAt ? "executed" : "pending",
|
|
19203
|
-
executedAt
|
|
19204
|
-
};
|
|
19205
|
-
});
|
|
19263
|
+
const statuses = migrationFiles.map((file2) => ({
|
|
19264
|
+
file: file2,
|
|
19265
|
+
status: executedMap.has(file2) ? "executed" : "pending",
|
|
19266
|
+
executedAt: executedMap.get(file2)
|
|
19267
|
+
}));
|
|
19206
19268
|
const executed = statuses.filter((s) => s.status === "executed");
|
|
19207
19269
|
const pending = statuses.filter((s) => s.status === "pending");
|
|
19208
|
-
const transient = statuses.filter((s) => s.status === "transient");
|
|
19209
19270
|
console.log(`-- Total migrations: ${migrationFiles.length}`);
|
|
19210
19271
|
console.log(`-- Executed: ${executed.length}`);
|
|
19211
19272
|
console.log(`-- Pending: ${pending.length}`);
|
|
19212
|
-
console.log(`-- Transient: ${transient.length}`);
|
|
19213
19273
|
console.log();
|
|
19214
19274
|
if (executed.length > 0) {
|
|
19215
19275
|
console.log("\u2713 Executed Migrations:");
|
|
@@ -19225,13 +19285,6 @@ async function migrateStatus() {
|
|
|
19225
19285
|
}
|
|
19226
19286
|
console.log();
|
|
19227
19287
|
}
|
|
19228
|
-
if (transient.length > 0) {
|
|
19229
|
-
console.log("\u26A1 Transient Migrations (ALTER TABLE - not tracked):");
|
|
19230
|
-
for (const migration of transient) {
|
|
19231
|
-
console.log(` - ${migration.file}`);
|
|
19232
|
-
}
|
|
19233
|
-
console.log();
|
|
19234
|
-
}
|
|
19235
19288
|
return statuses;
|
|
19236
19289
|
} catch (err) {
|
|
19237
19290
|
console.error("-- Failed to get migration status:", err);
|
|
@@ -19245,14 +19298,15 @@ var init_migrate_status = __esm(() => {
|
|
|
19245
19298
|
init_config();
|
|
19246
19299
|
init_drivers();
|
|
19247
19300
|
init_src2();
|
|
19301
|
+
init_workspace();
|
|
19248
19302
|
});
|
|
19249
19303
|
|
|
19250
19304
|
// src/actions/model-show.ts
|
|
19251
19305
|
import { readdirSync as readdirSync9 } from "fs";
|
|
19252
19306
|
import { extname as extname2, join as join12 } from "path";
|
|
19253
|
-
import
|
|
19307
|
+
import process26 from "process";
|
|
19254
19308
|
async function modelShow(modelName, options = {}) {
|
|
19255
|
-
const dir = options.dir || join12(
|
|
19309
|
+
const dir = options.dir || join12(process26.cwd(), "app/Models");
|
|
19256
19310
|
try {
|
|
19257
19311
|
const files = readdirSync9(dir);
|
|
19258
19312
|
const modelFile = files.find((f) => {
|
|
@@ -19466,9 +19520,9 @@ var init_query_explain_all = __esm(() => {
|
|
|
19466
19520
|
// src/actions/relation-diagram.ts
|
|
19467
19521
|
import { writeFileSync as writeFileSync12 } from "fs";
|
|
19468
19522
|
import { join as join14 } from "path";
|
|
19469
|
-
import
|
|
19523
|
+
import process27 from "process";
|
|
19470
19524
|
async function relationDiagram(options = {}) {
|
|
19471
|
-
const dir = options.dir || join14(
|
|
19525
|
+
const dir = options.dir || join14(process27.cwd(), "app/Models");
|
|
19472
19526
|
const format = options.format || "mermaid";
|
|
19473
19527
|
try {
|
|
19474
19528
|
const models = await loadModels({ modelsDir: dir });
|
|
@@ -19588,25 +19642,25 @@ var init_relation_diagram = __esm(() => {
|
|
|
19588
19642
|
});
|
|
19589
19643
|
|
|
19590
19644
|
// src/actions/seed.ts
|
|
19591
|
-
import { existsSync as
|
|
19645
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
19592
19646
|
import { dirname as dirname10, join as join15 } from "path";
|
|
19593
|
-
import
|
|
19594
|
-
function
|
|
19647
|
+
import process28 from "process";
|
|
19648
|
+
function findWorkspaceRoot3(startPath) {
|
|
19595
19649
|
let currentPath = startPath;
|
|
19596
19650
|
while (currentPath !== dirname10(currentPath)) {
|
|
19597
|
-
if (
|
|
19651
|
+
if (existsSync20(join15(currentPath, "package.json"))) {
|
|
19598
19652
|
return currentPath;
|
|
19599
19653
|
}
|
|
19600
19654
|
currentPath = dirname10(currentPath);
|
|
19601
19655
|
}
|
|
19602
|
-
return
|
|
19656
|
+
return process28.cwd();
|
|
19603
19657
|
}
|
|
19604
19658
|
async function loadSeeders(seedersDir) {
|
|
19605
|
-
if (!
|
|
19659
|
+
if (!existsSync20(seedersDir)) {
|
|
19606
19660
|
console.log(`-- Seeders directory not found: ${seedersDir}`);
|
|
19607
19661
|
return [];
|
|
19608
19662
|
}
|
|
19609
|
-
const files = readdirSync11(seedersDir);
|
|
19663
|
+
const files = readdirSync11(seedersDir).sort((a, b) => a.localeCompare(b));
|
|
19610
19664
|
const seederFiles = files.filter((file2) => (file2.endsWith(".ts") || file2.endsWith(".js")) && file2 !== "index.ts" && file2 !== "index.js");
|
|
19611
19665
|
const seeders = [];
|
|
19612
19666
|
for (const file2 of seederFiles) {
|
|
@@ -19619,6 +19673,7 @@ async function loadSeeders(seedersDir) {
|
|
|
19619
19673
|
if (instance && typeof instance.run === "function") {
|
|
19620
19674
|
seeders.push({
|
|
19621
19675
|
name: file2.replace(/\.(ts|js)$/, ""),
|
|
19676
|
+
className: String(SeederClass.name || ""),
|
|
19622
19677
|
instance
|
|
19623
19678
|
});
|
|
19624
19679
|
}
|
|
@@ -19631,7 +19686,7 @@ async function loadSeeders(seedersDir) {
|
|
|
19631
19686
|
return seeders;
|
|
19632
19687
|
}
|
|
19633
19688
|
async function runSeeders(config6 = {}) {
|
|
19634
|
-
const workspaceRoot =
|
|
19689
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19635
19690
|
const seedersDir = config6.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
19636
19691
|
const verbose = config6.verbose ?? true;
|
|
19637
19692
|
if (verbose) {
|
|
@@ -19666,17 +19721,18 @@ async function runSeeders(config6 = {}) {
|
|
|
19666
19721
|
}
|
|
19667
19722
|
}
|
|
19668
19723
|
async function runSeeder(className, options = {}) {
|
|
19669
|
-
const workspaceRoot =
|
|
19724
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19670
19725
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
19671
19726
|
const verbose = options.verbose ?? true;
|
|
19672
19727
|
if (verbose) {
|
|
19673
19728
|
console.log(`-- Running seeder: ${className}`);
|
|
19674
19729
|
}
|
|
19675
19730
|
const seeders = await loadSeeders(seedersDir);
|
|
19676
|
-
const seeder = seeders.find((s) => s.name === className);
|
|
19731
|
+
const seeder = seeders.find((s) => s.className === className) ?? seeders.find((s) => s.name === className);
|
|
19677
19732
|
if (!seeder) {
|
|
19678
|
-
|
|
19679
|
-
|
|
19733
|
+
const known = seeders.map((s) => s.className || s.name).join(", ") || "none";
|
|
19734
|
+
console.error(`-- Seeder not found: ${className}. Available: ${known}`);
|
|
19735
|
+
throw new Error(`Seeder not found: ${className}. Available: ${known}`);
|
|
19680
19736
|
}
|
|
19681
19737
|
const qb = createQueryBuilder();
|
|
19682
19738
|
try {
|
|
@@ -19690,9 +19746,9 @@ async function runSeeder(className, options = {}) {
|
|
|
19690
19746
|
}
|
|
19691
19747
|
}
|
|
19692
19748
|
async function makeSeeder(name) {
|
|
19693
|
-
const workspaceRoot =
|
|
19749
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19694
19750
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
19695
|
-
if (!
|
|
19751
|
+
if (!existsSync20(seedersDir)) {
|
|
19696
19752
|
mkdirSync9(seedersDir, { recursive: true });
|
|
19697
19753
|
console.log(`-- Created seeders directory: ${seedersDir}`);
|
|
19698
19754
|
}
|
|
@@ -19700,7 +19756,7 @@ async function makeSeeder(name) {
|
|
|
19700
19756
|
const className = `${baseName}Seeder`;
|
|
19701
19757
|
const fileName = `${className}.ts`;
|
|
19702
19758
|
const filePath = join15(seedersDir, fileName);
|
|
19703
|
-
if (
|
|
19759
|
+
if (existsSync20(filePath)) {
|
|
19704
19760
|
console.error(`-- Seeder already exists: ${filePath}`);
|
|
19705
19761
|
throw new Error(`Seeder already exists: ${filePath}`);
|
|
19706
19762
|
}
|
|
@@ -19754,7 +19810,7 @@ export default class ${className} extends Seeder {
|
|
|
19754
19810
|
console.log(`-- \u2713 Created seeder: ${filePath}`);
|
|
19755
19811
|
}
|
|
19756
19812
|
async function freshDatabase(options = {}) {
|
|
19757
|
-
const workspaceRoot =
|
|
19813
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19758
19814
|
const modelsDir = options.modelsDir || join15(workspaceRoot, "app/Models");
|
|
19759
19815
|
const seedersDir = options.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
19760
19816
|
const verbose = options.verbose ?? true;
|
|
@@ -19814,22 +19870,22 @@ var init_unsafe = __esm(() => {
|
|
|
19814
19870
|
});
|
|
19815
19871
|
|
|
19816
19872
|
// src/actions/validate.ts
|
|
19817
|
-
import { existsSync as
|
|
19873
|
+
import { existsSync as existsSync21 } from "fs";
|
|
19818
19874
|
import { dirname as dirname11, join as join16 } from "path";
|
|
19819
|
-
import
|
|
19820
|
-
function
|
|
19875
|
+
import process29 from "process";
|
|
19876
|
+
function findWorkspaceRoot4(startPath) {
|
|
19821
19877
|
let currentPath = startPath;
|
|
19822
19878
|
while (currentPath !== dirname11(currentPath)) {
|
|
19823
|
-
if (
|
|
19879
|
+
if (existsSync21(join16(currentPath, "package.json"))) {
|
|
19824
19880
|
return currentPath;
|
|
19825
19881
|
}
|
|
19826
19882
|
currentPath = dirname11(currentPath);
|
|
19827
19883
|
}
|
|
19828
|
-
return
|
|
19884
|
+
return process29.cwd();
|
|
19829
19885
|
}
|
|
19830
19886
|
async function validateSchema(dir) {
|
|
19831
19887
|
if (!dir) {
|
|
19832
|
-
dir = join16(
|
|
19888
|
+
dir = join16(findWorkspaceRoot4(process29.cwd()), "app/Models");
|
|
19833
19889
|
}
|
|
19834
19890
|
const dialect = config5.dialect || "postgres";
|
|
19835
19891
|
console.log("-- Validating Schema");
|
|
@@ -25080,18 +25136,11 @@ function toSnakeCase(str) {
|
|
|
25080
25136
|
snakeCaseCache.set(str, cached);
|
|
25081
25137
|
return cached;
|
|
25082
25138
|
}
|
|
25083
|
-
function
|
|
25139
|
+
function toTableName2(modelName) {
|
|
25084
25140
|
let cached = tableNameCache.get(modelName);
|
|
25085
25141
|
if (cached !== undefined)
|
|
25086
25142
|
return cached;
|
|
25087
|
-
|
|
25088
|
-
if (snake.endsWith("y") && !snake.endsWith("ay") && !snake.endsWith("ey") && !snake.endsWith("oy") && !snake.endsWith("uy")) {
|
|
25089
|
-
cached = snake.slice(0, -1) + "ies";
|
|
25090
|
-
} else if (snake.endsWith("s") || snake.endsWith("x") || snake.endsWith("ch") || snake.endsWith("sh")) {
|
|
25091
|
-
cached = `${snake}es`;
|
|
25092
|
-
} else {
|
|
25093
|
-
cached = `${snake}s`;
|
|
25094
|
-
}
|
|
25143
|
+
cached = toTableName(modelName);
|
|
25095
25144
|
tableNameCache.set(modelName, cached);
|
|
25096
25145
|
return cached;
|
|
25097
25146
|
}
|
|
@@ -25176,14 +25225,14 @@ function resolveRelation(definition, relationName) {
|
|
|
25176
25225
|
const hasManyModel = findModelName(definition.hasMany);
|
|
25177
25226
|
if (hasManyModel) {
|
|
25178
25227
|
const relatedModel = getModelFromRegistry(hasManyModel);
|
|
25179
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25228
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(hasManyModel);
|
|
25180
25229
|
const foreignKey = toSnakeCase(parentName) + "_id";
|
|
25181
25230
|
return { type: "hasMany", relatedModelName: hasManyModel, relatedTable, foreignKey, localKey: parentPk };
|
|
25182
25231
|
}
|
|
25183
25232
|
const hasOneModel = findModelName(definition.hasOne);
|
|
25184
25233
|
if (hasOneModel) {
|
|
25185
25234
|
const relatedModel = getModelFromRegistry(hasOneModel);
|
|
25186
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25235
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(hasOneModel);
|
|
25187
25236
|
const foreignKey = toSnakeCase(parentName) + "_id";
|
|
25188
25237
|
return { type: "hasOne", relatedModelName: hasOneModel, relatedTable, foreignKey, localKey: parentPk };
|
|
25189
25238
|
}
|
|
@@ -25191,7 +25240,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25191
25240
|
if (belongsToEntry) {
|
|
25192
25241
|
const belongsToModel = belongsToEntry.model;
|
|
25193
25242
|
const relatedModel = getModelFromRegistry(belongsToModel);
|
|
25194
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25243
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(belongsToModel);
|
|
25195
25244
|
const relatedPk = relatedModel?.getDefinition?.()?.primaryKey || "id";
|
|
25196
25245
|
const foreignKey = belongsToEntry.foreignKey || `${toSnakeCase(belongsToModel)}_id`;
|
|
25197
25246
|
return { type: "belongsTo", relatedModelName: belongsToModel, relatedTable, foreignKey, localKey: relatedPk };
|
|
@@ -25201,7 +25250,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25201
25250
|
const isConfig = typeof found.entry === "object";
|
|
25202
25251
|
const relatedModelName = isConfig ? found.entry.model : found.entry;
|
|
25203
25252
|
const relatedModel = getModelFromRegistry(relatedModelName);
|
|
25204
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25253
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(relatedModelName);
|
|
25205
25254
|
const config6 = isConfig ? found.entry : null;
|
|
25206
25255
|
let pivotTable;
|
|
25207
25256
|
let pivotModelName;
|
|
@@ -25213,7 +25262,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25213
25262
|
if (!throughModel) {
|
|
25214
25263
|
throw new Error(`[orm] belongsToMany relation '${relationName}' on '${parentName}' references unknown through model '${config6.through}'. Make sure '${config6.through}' is registered.`);
|
|
25215
25264
|
}
|
|
25216
|
-
pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table ||
|
|
25265
|
+
pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table || toTableName2(config6.through);
|
|
25217
25266
|
} else {
|
|
25218
25267
|
const a2 = singularizeWord(parentTable);
|
|
25219
25268
|
const b2 = singularizeWord(relatedTable);
|
|
@@ -25264,10 +25313,10 @@ function resolveRelation(definition, relationName) {
|
|
|
25264
25313
|
return null;
|
|
25265
25314
|
const throughModel = getModelFromRegistry(throughModelName);
|
|
25266
25315
|
const throughDef = throughModel?.getDefinition?.() || throughModel?.definition;
|
|
25267
|
-
const throughTable = throughModel?.getTable?.() || throughDef?.table ||
|
|
25316
|
+
const throughTable = throughModel?.getTable?.() || throughDef?.table || toTableName2(throughModelName);
|
|
25268
25317
|
const throughPk = throughDef?.primaryKey || "id";
|
|
25269
25318
|
const targetModel = getModelFromRegistry(targetModelName);
|
|
25270
|
-
const targetTable = targetModel?.getTable?.() ||
|
|
25319
|
+
const targetTable = targetModel?.getTable?.() || toTableName2(targetModelName);
|
|
25271
25320
|
return {
|
|
25272
25321
|
type,
|
|
25273
25322
|
relatedModelName: targetModelName,
|
|
@@ -27377,7 +27426,7 @@ function resolveKeyPattern(keyPatterns, data) {
|
|
|
27377
27426
|
}
|
|
27378
27427
|
return resolved;
|
|
27379
27428
|
}
|
|
27380
|
-
async function parseStacksModels(
|
|
27429
|
+
async function parseStacksModels(_config) {
|
|
27381
27430
|
return { models: new Map };
|
|
27382
27431
|
}
|
|
27383
27432
|
function toDynamoDBItem(item) {
|
|
@@ -27864,7 +27913,7 @@ function buildDatabaseSchema(models) {
|
|
|
27864
27913
|
for (const modelName of Object.keys(models)) {
|
|
27865
27914
|
const rawModel = models[modelName];
|
|
27866
27915
|
const m2 = rawModel.definition ?? rawModel.getDefinition?.() ?? rawModel;
|
|
27867
|
-
const table =
|
|
27916
|
+
const table = tableNameFor(m2);
|
|
27868
27917
|
const attrs = m2.attributes ?? {};
|
|
27869
27918
|
const columns = {};
|
|
27870
27919
|
for (const key of Object.keys(attrs))
|
|
@@ -27876,30 +27925,53 @@ function buildDatabaseSchema(models) {
|
|
|
27876
27925
|
}
|
|
27877
27926
|
return schema;
|
|
27878
27927
|
}
|
|
27928
|
+
var init_factory = () => {};
|
|
27879
27929
|
|
|
27880
27930
|
// src/loader.ts
|
|
27881
|
-
import { readdirSync as readdirSync12
|
|
27882
|
-
import { basename, extname as extname4 } from "path";
|
|
27883
|
-
import
|
|
27884
|
-
|
|
27885
|
-
const
|
|
27886
|
-
|
|
27887
|
-
|
|
27888
|
-
const
|
|
27931
|
+
import { readdirSync as readdirSync12 } from "fs";
|
|
27932
|
+
import { basename, extname as extname4, isAbsolute as isAbsolute3, join as join17 } from "path";
|
|
27933
|
+
import process30 from "process";
|
|
27934
|
+
function isModelFile(name) {
|
|
27935
|
+
const ext = extname4(name);
|
|
27936
|
+
if (!MODEL_EXTENSIONS.has(ext))
|
|
27937
|
+
return false;
|
|
27938
|
+
const base = basename(name, ext);
|
|
27939
|
+
if (base.startsWith("_") || base === "index")
|
|
27940
|
+
return false;
|
|
27941
|
+
return !base.endsWith(".d");
|
|
27942
|
+
}
|
|
27943
|
+
function isModelDefinition(def) {
|
|
27944
|
+
if (!def || typeof def !== "object")
|
|
27945
|
+
return false;
|
|
27946
|
+
const d2 = def;
|
|
27947
|
+
return typeof d2.name === "string" || typeof d2.table === "string";
|
|
27948
|
+
}
|
|
27949
|
+
function collectModelFiles(dir) {
|
|
27950
|
+
const files = [];
|
|
27951
|
+
const entries = readdirSync12(dir, { withFileTypes: true }).sort((a2, b2) => a2.name.localeCompare(b2.name));
|
|
27889
27952
|
for (const entry of entries) {
|
|
27890
|
-
const full =
|
|
27891
|
-
|
|
27892
|
-
|
|
27893
|
-
continue;
|
|
27894
|
-
const ext = extname4(full);
|
|
27895
|
-
if (![".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"].includes(ext))
|
|
27953
|
+
const full = join17(dir, entry.name);
|
|
27954
|
+
if (entry.isDirectory()) {
|
|
27955
|
+
files.push(...collectModelFiles(full));
|
|
27896
27956
|
continue;
|
|
27957
|
+
}
|
|
27958
|
+
if (isModelFile(entry.name))
|
|
27959
|
+
files.push(full);
|
|
27960
|
+
}
|
|
27961
|
+
return files;
|
|
27962
|
+
}
|
|
27963
|
+
async function loadModels(options) {
|
|
27964
|
+
const cwd = options.cwd ?? process30.cwd();
|
|
27965
|
+
const dir = isAbsolute3(options.modelsDir) ? options.modelsDir : join17(cwd, options.modelsDir);
|
|
27966
|
+
const result = {};
|
|
27967
|
+
for (const full of collectModelFiles(dir)) {
|
|
27897
27968
|
const cacheBuster = `?t=${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
27898
27969
|
const mod = await import(`${full}${cacheBuster}`);
|
|
27899
27970
|
const rawDef = mod.default ?? mod;
|
|
27900
27971
|
const def = rawDef.definition ?? rawDef.getDefinition?.() ?? rawDef;
|
|
27901
|
-
|
|
27902
|
-
|
|
27972
|
+
if (!isModelDefinition(def))
|
|
27973
|
+
continue;
|
|
27974
|
+
const name = def.name ?? basename(full, extname4(full));
|
|
27903
27975
|
result[name] = {
|
|
27904
27976
|
...def,
|
|
27905
27977
|
name
|
|
@@ -27907,7 +27979,10 @@ async function loadModels(options) {
|
|
|
27907
27979
|
}
|
|
27908
27980
|
return result;
|
|
27909
27981
|
}
|
|
27910
|
-
var
|
|
27982
|
+
var MODEL_EXTENSIONS;
|
|
27983
|
+
var init_loader = __esm(() => {
|
|
27984
|
+
MODEL_EXTENSIONS = new Set([".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]);
|
|
27985
|
+
});
|
|
27911
27986
|
|
|
27912
27987
|
// src/meta.ts
|
|
27913
27988
|
function buildSchemaMeta(models) {
|
|
@@ -27919,7 +27994,7 @@ function buildSchemaMeta(models) {
|
|
|
27919
27994
|
for (const name of Object.keys(models)) {
|
|
27920
27995
|
const rawModel = models[name];
|
|
27921
27996
|
const m2 = rawModel.definition ?? rawModel.getDefinition?.() ?? rawModel;
|
|
27922
|
-
const table =
|
|
27997
|
+
const table = tableNameFor(m2);
|
|
27923
27998
|
modelToTable[name] = table;
|
|
27924
27999
|
tableToModel[table] = name;
|
|
27925
28000
|
primaryKeys[table] = m2.primaryKey ?? "id";
|
|
@@ -27993,9 +28068,9 @@ function buildSchemaMeta(models) {
|
|
|
27993
28068
|
var init_meta = () => {};
|
|
27994
28069
|
|
|
27995
28070
|
// src/migrations.ts
|
|
27996
|
-
import { existsSync as
|
|
27997
|
-
import { dirname as dirname12, join as
|
|
27998
|
-
import
|
|
28071
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync10, readdirSync as readdirSync13, readFileSync as readFileSync6, writeFileSync as writeFileSync14 } from "fs";
|
|
28072
|
+
import { dirname as dirname12, join as join18 } from "path";
|
|
28073
|
+
import process31 from "process";
|
|
27999
28074
|
function info2(message) {
|
|
28000
28075
|
if (config5.verbose)
|
|
28001
28076
|
console.log(message);
|
|
@@ -28006,36 +28081,80 @@ function snakeCase(str) {
|
|
|
28006
28081
|
function singularizeTable(table) {
|
|
28007
28082
|
return singularizerFor(config5.relations?.singularizeStrategy)(table);
|
|
28008
28083
|
}
|
|
28009
|
-
function
|
|
28084
|
+
function lastCreatedMigrationFiles() {
|
|
28085
|
+
return [...createdMigrationFiles];
|
|
28086
|
+
}
|
|
28087
|
+
function findWorkspaceRoot5(startPath) {
|
|
28010
28088
|
let currentPath = startPath;
|
|
28011
28089
|
while (currentPath !== dirname12(currentPath)) {
|
|
28012
|
-
if (
|
|
28090
|
+
if (existsSync23(join18(currentPath, "package.json"))) {
|
|
28013
28091
|
return currentPath;
|
|
28014
28092
|
}
|
|
28015
28093
|
currentPath = dirname12(currentPath);
|
|
28016
28094
|
}
|
|
28017
|
-
return
|
|
28095
|
+
return process31.cwd();
|
|
28018
28096
|
}
|
|
28019
28097
|
function ensureSqlDirectory2() {
|
|
28020
|
-
const workspaceRoot =
|
|
28021
|
-
const sqlDir =
|
|
28022
|
-
if (!
|
|
28098
|
+
const workspaceRoot = findWorkspaceRoot5(process31.cwd());
|
|
28099
|
+
const sqlDir = join18(workspaceRoot, "database", "migrations");
|
|
28100
|
+
if (!existsSync23(sqlDir)) {
|
|
28023
28101
|
mkdirSync10(sqlDir, { recursive: true });
|
|
28024
28102
|
info2(`-- Created SQL directory: ${sqlDir}`);
|
|
28025
28103
|
}
|
|
28026
28104
|
return sqlDir;
|
|
28027
28105
|
}
|
|
28106
|
+
function isGeneratedMigrationSql(sql2) {
|
|
28107
|
+
return sql2.trimStart().startsWith(GENERATED_MARKER);
|
|
28108
|
+
}
|
|
28109
|
+
function migrationBody(sql2) {
|
|
28110
|
+
return sql2.split(`
|
|
28111
|
+
`).filter((line) => !line.trimStart().startsWith("--")).join(`
|
|
28112
|
+
`).replace(/\s+/g, " ").trim().replace(/;+$/, "");
|
|
28113
|
+
}
|
|
28114
|
+
function existingMigrationBodies(sqlDir) {
|
|
28115
|
+
const bodies = new Set;
|
|
28116
|
+
let files;
|
|
28117
|
+
try {
|
|
28118
|
+
files = readdirSync13(sqlDir).filter((f2) => f2.endsWith(".sql"));
|
|
28119
|
+
} catch {
|
|
28120
|
+
return bodies;
|
|
28121
|
+
}
|
|
28122
|
+
for (const file2 of files) {
|
|
28123
|
+
try {
|
|
28124
|
+
bodies.add(migrationBody(readFileSync6(join18(sqlDir, file2), "utf8")));
|
|
28125
|
+
} catch {}
|
|
28126
|
+
}
|
|
28127
|
+
return bodies;
|
|
28128
|
+
}
|
|
28028
28129
|
function createMigrationFile(statement, fileName) {
|
|
28029
28130
|
if (!statement)
|
|
28030
28131
|
return false;
|
|
28031
28132
|
const sqlDir = ensureSqlDirectory2();
|
|
28133
|
+
if (existingCorpusBodies === null)
|
|
28134
|
+
existingCorpusBodies = existingMigrationBodies(sqlDir);
|
|
28135
|
+
const body = migrationBody(statement);
|
|
28136
|
+
if (body.length > 0 && existingCorpusBodies.has(body)) {
|
|
28137
|
+
info2(`-- Migration already in the corpus, not writing it again: ${fileName}`);
|
|
28138
|
+
return false;
|
|
28139
|
+
}
|
|
28032
28140
|
migrationCounter++;
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28141
|
+
let sequence = useDeterministicNames ? String(migrationCounter).padStart(10, "0") : String(Math.floor(Date.now() / 1000) + migrationCounter);
|
|
28142
|
+
let fullFileName = `${sequence}-${fileName}.sql`;
|
|
28143
|
+
if (!useDeterministicNames) {
|
|
28144
|
+
let next = Number(sequence);
|
|
28145
|
+
while (existsSync23(join18(sqlDir, fullFileName))) {
|
|
28146
|
+
next += 1;
|
|
28147
|
+
sequence = String(next);
|
|
28148
|
+
fullFileName = `${sequence}-${fileName}.sql`;
|
|
28149
|
+
}
|
|
28150
|
+
}
|
|
28151
|
+
const filePath = join18(sqlDir, fullFileName);
|
|
28152
|
+
writeFileSync14(filePath, `${GENERATED_MARKER}
|
|
28153
|
+
${statement}`);
|
|
28037
28154
|
info2(`-- Migration file created: ${fullFileName}`);
|
|
28038
28155
|
migrationsCreatedCount++;
|
|
28156
|
+
createdMigrationFiles.push(fullFileName);
|
|
28157
|
+
existingCorpusBodies?.add(body);
|
|
28039
28158
|
return true;
|
|
28040
28159
|
}
|
|
28041
28160
|
function guessTypeFromName(columnName) {
|
|
@@ -28210,7 +28329,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28210
28329
|
const tables = [];
|
|
28211
28330
|
for (const modelName of Object.keys(models)) {
|
|
28212
28331
|
const model = models[modelName];
|
|
28213
|
-
const table =
|
|
28332
|
+
const table = tableNameFor(model);
|
|
28214
28333
|
const primaryKey = model.primaryKey ?? "id";
|
|
28215
28334
|
const _autoIncrement = model.autoIncrement !== false;
|
|
28216
28335
|
const attrs = model.attributes ?? {};
|
|
@@ -28302,9 +28421,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28302
28421
|
if (columns.some((c2) => c2.name === fkColumnName))
|
|
28303
28422
|
continue;
|
|
28304
28423
|
const refTable = meta.modelToTable[rel.model];
|
|
28305
|
-
|
|
28306
|
-
continue;
|
|
28307
|
-
const refPk = meta.primaryKeys[refTable] ?? "id";
|
|
28424
|
+
const refPk = refTable ? meta.primaryKeys[refTable] ?? "id" : undefined;
|
|
28308
28425
|
columns.push({
|
|
28309
28426
|
name: fkColumnName,
|
|
28310
28427
|
type: "bigint",
|
|
@@ -28312,7 +28429,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28312
28429
|
isUnique: false,
|
|
28313
28430
|
isNullable: true,
|
|
28314
28431
|
hasDefault: false,
|
|
28315
|
-
references: { table: refTable, column: refPk, onDelete: rel.onDelete }
|
|
28432
|
+
references: refTable && refPk ? { table: refTable, column: refPk, onDelete: rel.onDelete } : undefined
|
|
28316
28433
|
});
|
|
28317
28434
|
}
|
|
28318
28435
|
const traits = model.traits;
|
|
@@ -28393,7 +28510,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28393
28510
|
for (const modelName of Object.keys(models)) {
|
|
28394
28511
|
const model = models[modelName];
|
|
28395
28512
|
const traits = model?.traits ?? {};
|
|
28396
|
-
const modelTable =
|
|
28513
|
+
const modelTable = tableNameFor(model);
|
|
28397
28514
|
const singular = singularizeTable(modelTable);
|
|
28398
28515
|
if (traits.likeable) {
|
|
28399
28516
|
const opts = typeof traits.likeable === "object" ? traits.likeable : {};
|
|
@@ -28460,7 +28577,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28460
28577
|
const btm = model?.belongsToMany;
|
|
28461
28578
|
if (!btm || typeof btm !== "object" || Array.isArray(btm))
|
|
28462
28579
|
continue;
|
|
28463
|
-
const parentTable =
|
|
28580
|
+
const parentTable = tableNameFor(model);
|
|
28464
28581
|
const parentSingular = singularizeTable(parentTable);
|
|
28465
28582
|
for (const [relKey, value] of Object.entries(btm)) {
|
|
28466
28583
|
const cfg = value;
|
|
@@ -28521,6 +28638,8 @@ function generateSql(plan, opts = {}) {
|
|
|
28521
28638
|
migrationCounter = 0;
|
|
28522
28639
|
migrationsCreatedCount = 0;
|
|
28523
28640
|
migrationsUpdatedCount = 0;
|
|
28641
|
+
createdMigrationFiles = [];
|
|
28642
|
+
existingCorpusBodies = null;
|
|
28524
28643
|
useDeterministicNames = true;
|
|
28525
28644
|
const emit = opts.dryRun ? () => false : createMigrationFile;
|
|
28526
28645
|
const statements = [];
|
|
@@ -28782,12 +28901,13 @@ function referencesAreDifferent(r1, r2) {
|
|
|
28782
28901
|
const act = (a2) => (a2 ?? "no action").toLowerCase();
|
|
28783
28902
|
return r1.table !== r2.table || r1.column !== r2.column || act(r1.onDelete) !== act(r2.onDelete) || act(r1.onUpdate) !== act(r2.onUpdate);
|
|
28784
28903
|
}
|
|
28904
|
+
function indexKey(index) {
|
|
28905
|
+
return `${index.type}:${index.name}:${index.columns.join(",")}`;
|
|
28906
|
+
}
|
|
28785
28907
|
function mapIndexesByKey(indexes) {
|
|
28786
28908
|
const map = {};
|
|
28787
|
-
for (const i2 of indexes)
|
|
28788
|
-
|
|
28789
|
-
map[key] = i2;
|
|
28790
|
-
}
|
|
28909
|
+
for (const i2 of indexes)
|
|
28910
|
+
map[indexKey(i2)] = i2;
|
|
28791
28911
|
return map;
|
|
28792
28912
|
}
|
|
28793
28913
|
function generateDiffOperations(previous, next, opts = {}) {
|
|
@@ -28802,10 +28922,13 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
28802
28922
|
return { statements: statements2, operations: operations2 };
|
|
28803
28923
|
}
|
|
28804
28924
|
const applyRenames = opts.applyRenames !== false;
|
|
28925
|
+
const preserveUnknownColumns = opts.preserveUnknownColumns === true;
|
|
28805
28926
|
const emit = opts.dryRun ? () => false : createMigrationFile;
|
|
28806
28927
|
migrationCounter = 0;
|
|
28807
28928
|
migrationsCreatedCount = 0;
|
|
28808
28929
|
migrationsUpdatedCount = 0;
|
|
28930
|
+
createdMigrationFiles = [];
|
|
28931
|
+
existingCorpusBodies = null;
|
|
28809
28932
|
useDeterministicNames = false;
|
|
28810
28933
|
const chunks = [];
|
|
28811
28934
|
const operations = [];
|
|
@@ -28904,22 +29027,32 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
28904
29027
|
}
|
|
28905
29028
|
for (const tableName of Object.keys(nextTables)) {
|
|
28906
29029
|
const prev = prevTables[tableName];
|
|
28907
|
-
|
|
29030
|
+
let curr = nextTables[tableName];
|
|
28908
29031
|
if (!prev)
|
|
28909
29032
|
continue;
|
|
28910
29033
|
const prevCols = mapColumnsByName(prev.columns);
|
|
28911
|
-
const currCols = mapColumnsByName(curr.columns);
|
|
28912
29034
|
const prevIdx = mapIndexesByKey(prev.indexes);
|
|
28913
|
-
|
|
29035
|
+
let currCols = mapColumnsByName(curr.columns);
|
|
29036
|
+
let currIdx = mapIndexesByKey(curr.indexes);
|
|
28914
29037
|
let renames = [];
|
|
28915
29038
|
let removedCols = Object.keys(prevCols).filter((n2) => !currCols[n2]);
|
|
28916
29039
|
let addedCols = Object.keys(currCols).filter((n2) => !prevCols[n2]);
|
|
28917
|
-
if (applyRenames) {
|
|
29040
|
+
if (applyRenames && !preserveUnknownColumns) {
|
|
28918
29041
|
const detected = detectColumnRenames(prevCols, currCols, dialect);
|
|
28919
29042
|
renames = detected.renames;
|
|
28920
29043
|
removedCols = detected.removed;
|
|
28921
29044
|
addedCols = detected.added;
|
|
28922
29045
|
}
|
|
29046
|
+
if (preserveUnknownColumns && removedCols.length > 0) {
|
|
29047
|
+
const preserved = removedCols.map((name) => prevCols[name]);
|
|
29048
|
+
const preservedNames = new Set(removedCols);
|
|
29049
|
+
const keptIndexes = prev.indexes.filter((idx) => idx.columns.some((c2) => preservedNames.has(c2)) && !currIdx[indexKey(idx)]);
|
|
29050
|
+
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`);
|
|
29051
|
+
curr = { ...curr, columns: [...curr.columns, ...preserved], indexes: [...curr.indexes, ...keptIndexes] };
|
|
29052
|
+
currCols = mapColumnsByName(curr.columns);
|
|
29053
|
+
currIdx = mapIndexesByKey(curr.indexes);
|
|
29054
|
+
removedCols = [];
|
|
29055
|
+
}
|
|
28923
29056
|
const modifiedCols = Object.keys(currCols).filter((n2) => prevCols[n2] && columnsAreDifferent(prevCols[n2], currCols[n2], dialect));
|
|
28924
29057
|
const fkChangedCols = Object.keys(currCols).filter((n2) => prevCols[n2] && referencesAreDifferent(prevCols[n2].references, currCols[n2].references));
|
|
28925
29058
|
if (dialect === "sqlite") {
|
|
@@ -29067,11 +29200,12 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
29067
29200
|
function generateDiffSql(previous, next, opts = {}) {
|
|
29068
29201
|
return generateDiffOperations(previous, next, opts).statements;
|
|
29069
29202
|
}
|
|
29070
|
-
var migrationCounter = 0, migrationsCreatedCount = 0, migrationsUpdatedCount = 0, useDeterministicNames = true;
|
|
29203
|
+
var migrationCounter = 0, migrationsCreatedCount = 0, migrationsUpdatedCount = 0, useDeterministicNames = true, createdMigrationFiles, existingCorpusBodies = null, GENERATED_MARKER = "-- qb:generated";
|
|
29071
29204
|
var init_migrations = __esm(() => {
|
|
29072
29205
|
init_config();
|
|
29073
29206
|
init_drivers();
|
|
29074
29207
|
init_meta();
|
|
29208
|
+
createdMigrationFiles = [];
|
|
29075
29209
|
});
|
|
29076
29210
|
|
|
29077
29211
|
// src/schema.ts
|
|
@@ -29105,6 +29239,7 @@ var init_src2 = __esm(() => {
|
|
|
29105
29239
|
init_dynamodb_single_table();
|
|
29106
29240
|
init_dynamodb_tooling_adapter();
|
|
29107
29241
|
init_dynamodb();
|
|
29242
|
+
init_factory();
|
|
29108
29243
|
init_loader();
|
|
29109
29244
|
init_meta();
|
|
29110
29245
|
init_migrations();
|
|
@@ -29120,9 +29255,11 @@ export {
|
|
|
29120
29255
|
validateSchema,
|
|
29121
29256
|
unsafe,
|
|
29122
29257
|
unmarshallItem,
|
|
29258
|
+
toTableName,
|
|
29123
29259
|
toModelInstance,
|
|
29124
29260
|
toDynamoDBItem,
|
|
29125
29261
|
tinker,
|
|
29262
|
+
tableNameFor,
|
|
29126
29263
|
tableInfo,
|
|
29127
29264
|
stripTrailingS,
|
|
29128
29265
|
startConsole,
|
|
@@ -29141,6 +29278,7 @@ export {
|
|
|
29141
29278
|
resolveSqlitePragmas,
|
|
29142
29279
|
resolvePivot,
|
|
29143
29280
|
resolveKeyPattern,
|
|
29281
|
+
resolveDialect,
|
|
29144
29282
|
resetDatabase,
|
|
29145
29283
|
resetConnection,
|
|
29146
29284
|
relationDiagram,
|
|
@@ -29153,6 +29291,7 @@ export {
|
|
|
29153
29291
|
dbOptimize as optimizeDatabase,
|
|
29154
29292
|
modelShow,
|
|
29155
29293
|
modelNameForTable,
|
|
29294
|
+
migrationBody,
|
|
29156
29295
|
migrateStatus,
|
|
29157
29296
|
migrateRollback,
|
|
29158
29297
|
migrateModels,
|
|
@@ -29162,8 +29301,10 @@ export {
|
|
|
29162
29301
|
makeSeeder,
|
|
29163
29302
|
makeModel,
|
|
29164
29303
|
loadModels,
|
|
29304
|
+
lastCreatedMigrationFiles,
|
|
29165
29305
|
iterateAllPivots,
|
|
29166
29306
|
isMysqlLike,
|
|
29307
|
+
isGeneratedMigrationSql,
|
|
29167
29308
|
isDefinitionEqual,
|
|
29168
29309
|
isBrowser,
|
|
29169
29310
|
introspectDatabase,
|
|
@@ -29258,6 +29399,7 @@ export {
|
|
|
29258
29399
|
MySQLDriver,
|
|
29259
29400
|
Model,
|
|
29260
29401
|
MIGRATIONS_TABLE,
|
|
29402
|
+
GENERATED_MARKER,
|
|
29261
29403
|
EntityQueryBuilder,
|
|
29262
29404
|
DynamoDBToolingAdapter,
|
|
29263
29405
|
DynamoDBQueryBuilder,
|