bun-query-builder 0.1.63 → 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 +405 -276
- package/dist/config.d.ts +13 -0
- package/dist/db.d.ts +12 -0
- package/dist/inflect.d.ts +17 -0
- package/dist/migrations.d.ts +26 -0
- package/dist/src/index.js +392 -260
- 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}`;
|
|
@@ -11801,7 +11810,7 @@ var {SQL } = globalThis.Bun;
|
|
|
11801
11810
|
import { Database } from "bun:sqlite";
|
|
11802
11811
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11803
11812
|
import { dirname as dirname5 } from "path";
|
|
11804
|
-
import
|
|
11813
|
+
import process21 from "process";
|
|
11805
11814
|
|
|
11806
11815
|
class SQLiteWrapper {
|
|
11807
11816
|
db;
|
|
@@ -12079,9 +12088,13 @@ function sqliteFileFromUrl(url) {
|
|
|
12079
12088
|
const path = stripped.startsWith("///") ? stripped.slice(2) : stripped.startsWith("//") ? stripped.slice(2) : stripped;
|
|
12080
12089
|
return path || ":memory:";
|
|
12081
12090
|
}
|
|
12091
|
+
function resolveDatabaseName(dbConfig = config5.database) {
|
|
12092
|
+
const raw = process21.env.DB_DATABASE || dbConfig?.database || "";
|
|
12093
|
+
return String(raw).replace(/^['"]|['"]$/g, "");
|
|
12094
|
+
}
|
|
12082
12095
|
function createConnectionString(dialect, dbConfig) {
|
|
12083
|
-
if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") &&
|
|
12084
|
-
const e =
|
|
12096
|
+
if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") && process21.env.DB_CONNECTION === dialect) {
|
|
12097
|
+
const e = process21.env;
|
|
12085
12098
|
const envDb = (e.DB_DATABASE || dbConfig.database || "").replace(/^['"]|['"]$/g, "");
|
|
12086
12099
|
const envUser = e.DB_USERNAME || dbConfig.username;
|
|
12087
12100
|
const envPass = e.DB_PASSWORD ?? dbConfig.password ?? "";
|
|
@@ -12095,7 +12108,7 @@ function createConnectionString(dialect, dbConfig) {
|
|
|
12095
12108
|
return dialect === "sqlite" ? sqliteFileFromUrl(dbConfig.url) : dbConfig.url;
|
|
12096
12109
|
}
|
|
12097
12110
|
const { database, username, password, host = "localhost", port, ssl } = dbConfig;
|
|
12098
|
-
const sslQ = ssl ||
|
|
12111
|
+
const sslQ = ssl || process21.env.DB_SSL === "true" || process21.env.DB_SSL === "1" ? "?ssl=true" : "";
|
|
12099
12112
|
switch (dialect) {
|
|
12100
12113
|
case "postgres":
|
|
12101
12114
|
return `postgres://${username}:${password}@${host}${port ? `:${port}` : ""}/${database}${sslQ}`;
|
|
@@ -12369,6 +12382,17 @@ var init_benchmark = __esm(() => {
|
|
|
12369
12382
|
});
|
|
12370
12383
|
|
|
12371
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
|
+
}
|
|
12372
12396
|
function stripTrailingS(name) {
|
|
12373
12397
|
return name.endsWith("s") ? name.slice(0, -1) : name;
|
|
12374
12398
|
}
|
|
@@ -16589,7 +16613,7 @@ var init_cache = __esm(() => {
|
|
|
16589
16613
|
});
|
|
16590
16614
|
|
|
16591
16615
|
// src/actions/console.ts
|
|
16592
|
-
import
|
|
16616
|
+
import process22 from "process";
|
|
16593
16617
|
import { createInterface } from "readline";
|
|
16594
16618
|
async function startConsole() {
|
|
16595
16619
|
console.log("-- Query Builder Interactive Console");
|
|
@@ -16598,8 +16622,8 @@ async function startConsole() {
|
|
|
16598
16622
|
console.log();
|
|
16599
16623
|
const qb = createQueryBuilder();
|
|
16600
16624
|
const rl = createInterface({
|
|
16601
|
-
input:
|
|
16602
|
-
output:
|
|
16625
|
+
input: process22.stdin,
|
|
16626
|
+
output: process22.stdout,
|
|
16603
16627
|
prompt: "qb> "
|
|
16604
16628
|
});
|
|
16605
16629
|
let multilineBuffer = "";
|
|
@@ -16650,7 +16674,7 @@ Tips:
|
|
|
16650
16674
|
if (cmd === ".exit" || cmd === ".quit") {
|
|
16651
16675
|
console.log("Goodbye!");
|
|
16652
16676
|
rl.close();
|
|
16653
|
-
|
|
16677
|
+
process22.exit(0);
|
|
16654
16678
|
} else if (cmd === ".help") {
|
|
16655
16679
|
console.log(helpText);
|
|
16656
16680
|
} else if (cmd === ".clear") {
|
|
@@ -16721,7 +16745,7 @@ Tips:
|
|
|
16721
16745
|
rl.on("close", () => {
|
|
16722
16746
|
console.log(`
|
|
16723
16747
|
Goodbye!`);
|
|
16724
|
-
|
|
16748
|
+
process22.exit(0);
|
|
16725
16749
|
});
|
|
16726
16750
|
rl.prompt();
|
|
16727
16751
|
}
|
|
@@ -17099,9 +17123,8 @@ var init_db_info = __esm(() => {
|
|
|
17099
17123
|
});
|
|
17100
17124
|
|
|
17101
17125
|
// src/actions/db-optimize.ts
|
|
17102
|
-
import process22 from "process";
|
|
17103
17126
|
async function dbOptimize(options = {}) {
|
|
17104
|
-
const dialect = options.dialect
|
|
17127
|
+
const dialect = resolveDialect(options.dialect);
|
|
17105
17128
|
const aggressive = options.aggressive || false;
|
|
17106
17129
|
if (options.verbose) {
|
|
17107
17130
|
console.log(`Optimizing ${dialect} database${aggressive ? " (aggressive mode)" : ""}...`);
|
|
@@ -17141,7 +17164,7 @@ async function dbOptimize(options = {}) {
|
|
|
17141
17164
|
await bunSql`ANALYZE TABLE ${bunSql(table)}`;
|
|
17142
17165
|
}
|
|
17143
17166
|
} else {
|
|
17144
|
-
const dbName =
|
|
17167
|
+
const dbName = resolveDatabaseName();
|
|
17145
17168
|
const tables = await bunSql`
|
|
17146
17169
|
SELECT table_name
|
|
17147
17170
|
FROM information_schema.tables
|
|
@@ -17194,9 +17217,8 @@ var init_db_optimize = __esm(() => {
|
|
|
17194
17217
|
});
|
|
17195
17218
|
|
|
17196
17219
|
// src/actions/db-wipe.ts
|
|
17197
|
-
import process23 from "process";
|
|
17198
17220
|
async function dbWipe(options = {}) {
|
|
17199
|
-
const dialect = options.dialect
|
|
17221
|
+
const dialect = resolveDialect(options.dialect);
|
|
17200
17222
|
if (options.verbose) {
|
|
17201
17223
|
console.log(`Wiping all tables from ${dialect} database...`);
|
|
17202
17224
|
}
|
|
@@ -17210,7 +17232,7 @@ async function dbWipe(options = {}) {
|
|
|
17210
17232
|
`;
|
|
17211
17233
|
tables = result.map((row) => row.tablename);
|
|
17212
17234
|
} else if (isMysqlLike(dialect)) {
|
|
17213
|
-
const dbName =
|
|
17235
|
+
const dbName = resolveDatabaseName();
|
|
17214
17236
|
const result = await bunSql`
|
|
17215
17237
|
SELECT table_name
|
|
17216
17238
|
FROM information_schema.tables
|
|
@@ -17647,6 +17669,8 @@ function sqlTypeToNormalized(rawType, dialect, ctx) {
|
|
|
17647
17669
|
return "decimal";
|
|
17648
17670
|
if (base === "date")
|
|
17649
17671
|
return "date";
|
|
17672
|
+
if (base === "timestamptz" || /\bwith time zone\b/.test(t))
|
|
17673
|
+
return "timestamptz";
|
|
17650
17674
|
if (/^(?:timestamp|datetime)/.test(base))
|
|
17651
17675
|
return "datetime";
|
|
17652
17676
|
if (/^time/.test(base))
|
|
@@ -17902,7 +17926,7 @@ var init_introspect_db = __esm(() => {
|
|
|
17902
17926
|
// src/actions/make-model.ts
|
|
17903
17927
|
import { existsSync as existsSync14, mkdirSync as mkdirSync7, writeFileSync as writeFileSync10 } from "fs";
|
|
17904
17928
|
import { dirname as dirname7, join as join7 } from "path";
|
|
17905
|
-
import
|
|
17929
|
+
import process23 from "process";
|
|
17906
17930
|
function findWorkspaceRoot(startPath) {
|
|
17907
17931
|
let currentPath = startPath;
|
|
17908
17932
|
while (currentPath !== dirname7(currentPath)) {
|
|
@@ -17911,10 +17935,10 @@ function findWorkspaceRoot(startPath) {
|
|
|
17911
17935
|
}
|
|
17912
17936
|
currentPath = dirname7(currentPath);
|
|
17913
17937
|
}
|
|
17914
|
-
return
|
|
17938
|
+
return process23.cwd();
|
|
17915
17939
|
}
|
|
17916
17940
|
async function makeModel(name, options = {}) {
|
|
17917
|
-
const workspaceRoot = findWorkspaceRoot(
|
|
17941
|
+
const workspaceRoot = findWorkspaceRoot(process23.cwd());
|
|
17918
17942
|
const modelsDir = options.dir || join7(workspaceRoot, "app/Models");
|
|
17919
17943
|
if (!existsSync14(modelsDir)) {
|
|
17920
17944
|
mkdirSync7(modelsDir, { recursive: true });
|
|
@@ -18110,7 +18134,7 @@ class MySQLDriver {
|
|
|
18110
18134
|
)`;
|
|
18111
18135
|
}
|
|
18112
18136
|
getExecutedMigrationsQuery() {
|
|
18113
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18137
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18114
18138
|
}
|
|
18115
18139
|
recordMigrationQuery() {
|
|
18116
18140
|
return "INSERT INTO migrations (migration) VALUES (?)";
|
|
@@ -18291,7 +18315,7 @@ class PostgresDriver {
|
|
|
18291
18315
|
)`;
|
|
18292
18316
|
}
|
|
18293
18317
|
getExecutedMigrationsQuery() {
|
|
18294
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18318
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18295
18319
|
}
|
|
18296
18320
|
recordMigrationQuery() {
|
|
18297
18321
|
return "INSERT INTO migrations (migration) VALUES ($1)";
|
|
@@ -18536,7 +18560,7 @@ class SQLiteDriver {
|
|
|
18536
18560
|
)`;
|
|
18537
18561
|
}
|
|
18538
18562
|
getExecutedMigrationsQuery() {
|
|
18539
|
-
return "SELECT migration FROM migrations ORDER BY executed_at";
|
|
18563
|
+
return "SELECT migration, executed_at FROM migrations ORDER BY executed_at";
|
|
18540
18564
|
}
|
|
18541
18565
|
recordMigrationQuery() {
|
|
18542
18566
|
return "INSERT INTO migrations (migration) VALUES (?)";
|
|
@@ -18589,6 +18613,24 @@ var init_drivers = __esm(() => {
|
|
|
18589
18613
|
init_singlestore();
|
|
18590
18614
|
});
|
|
18591
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
|
+
|
|
18592
18634
|
// src/actions/migrate.ts
|
|
18593
18635
|
var exports_migrate = {};
|
|
18594
18636
|
__export(exports_migrate, {
|
|
@@ -18600,23 +18642,48 @@ __export(exports_migrate, {
|
|
|
18600
18642
|
copyModelsToGenerated: () => copyModelsToGenerated,
|
|
18601
18643
|
clearGeneratedDirectory: () => clearGeneratedDirectory
|
|
18602
18644
|
});
|
|
18603
|
-
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";
|
|
18604
18646
|
import { tmpdir } from "os";
|
|
18605
|
-
import { join as
|
|
18647
|
+
import { join as join10 } from "path";
|
|
18606
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
|
+
}
|
|
18607
18674
|
function info(message) {
|
|
18608
18675
|
if (config5.verbose)
|
|
18609
18676
|
console.log(message);
|
|
18610
18677
|
}
|
|
18611
18678
|
function getSnapshotDir(workspaceRoot) {
|
|
18612
|
-
return
|
|
18679
|
+
return join10(workspaceRoot, config5.snapshotDir || ".qb");
|
|
18613
18680
|
}
|
|
18614
18681
|
function getSnapshotPath(workspaceRoot, dialect) {
|
|
18615
|
-
return
|
|
18682
|
+
return join10(getSnapshotDir(workspaceRoot), `model-snapshot.${dialect}.json`);
|
|
18616
18683
|
}
|
|
18617
18684
|
function loadPlanSnapshot(workspaceRoot, dialect) {
|
|
18618
18685
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18619
|
-
if (!
|
|
18686
|
+
if (!existsSync17(snapshotPath)) {
|
|
18620
18687
|
return;
|
|
18621
18688
|
}
|
|
18622
18689
|
try {
|
|
@@ -18639,7 +18706,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
18639
18706
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18640
18707
|
const snapshotDir = getSnapshotDir(workspaceRoot);
|
|
18641
18708
|
const hash = hashMigrationPlan(plan);
|
|
18642
|
-
if (
|
|
18709
|
+
if (existsSync17(snapshotPath)) {
|
|
18643
18710
|
try {
|
|
18644
18711
|
const current = JSON.parse(readFileSync3(snapshotPath, "utf8"));
|
|
18645
18712
|
if (current?.hash === hash && current?.dialect === dialect && current?.plan) {
|
|
@@ -18648,7 +18715,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
18648
18715
|
}
|
|
18649
18716
|
} catch {}
|
|
18650
18717
|
}
|
|
18651
|
-
if (!
|
|
18718
|
+
if (!existsSync17(snapshotDir)) {
|
|
18652
18719
|
mkdirSync8(snapshotDir, { recursive: true });
|
|
18653
18720
|
info(`-- Created snapshot directory: ${snapshotDir}`);
|
|
18654
18721
|
}
|
|
@@ -18666,11 +18733,11 @@ function saveMigrationSnapshot(plan, options = {}) {
|
|
|
18666
18733
|
savePlanSnapshot(options.workspaceRoot ?? getWorkspaceRoot(), dialect, plan);
|
|
18667
18734
|
}
|
|
18668
18735
|
function getWorkspaceRoot() {
|
|
18669
|
-
return
|
|
18736
|
+
return findWorkspaceRoot2();
|
|
18670
18737
|
}
|
|
18671
18738
|
function ensureSqlDirectory(workspaceRoot) {
|
|
18672
18739
|
const sqlDir = getSqlDirectory(workspaceRoot);
|
|
18673
|
-
if (!
|
|
18740
|
+
if (!existsSync17(sqlDir)) {
|
|
18674
18741
|
mkdirSync8(sqlDir, { recursive: true });
|
|
18675
18742
|
info(`-- Created SQL directory: ${sqlDir}`);
|
|
18676
18743
|
}
|
|
@@ -18679,21 +18746,22 @@ function ensureSqlDirectory(workspaceRoot) {
|
|
|
18679
18746
|
async function generateMigration(dir, opts = {}) {
|
|
18680
18747
|
await getConfig();
|
|
18681
18748
|
if (!dir) {
|
|
18682
|
-
dir =
|
|
18749
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18683
18750
|
}
|
|
18684
18751
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
18685
18752
|
const workspaceRoot = getWorkspaceRoot();
|
|
18686
18753
|
const models = await loadModels({ modelsDir: dir });
|
|
18687
18754
|
const plan = buildMigrationPlan2(models, { dialect });
|
|
18688
18755
|
let previous;
|
|
18756
|
+
let reconciledFromDatabase = false;
|
|
18689
18757
|
if (!opts.full) {
|
|
18690
18758
|
previous = loadPlanSnapshot(workspaceRoot, dialect);
|
|
18691
18759
|
if (previous) {
|
|
18692
18760
|
info("-- Comparing with stored model snapshot");
|
|
18693
18761
|
} else {
|
|
18694
|
-
const defaultStatePath =
|
|
18762
|
+
const defaultStatePath = join10(dir, `.qb-migrations.${dialect}.json`);
|
|
18695
18763
|
const statePath = String(opts.state || defaultStatePath);
|
|
18696
|
-
if (
|
|
18764
|
+
if (existsSync17(statePath)) {
|
|
18697
18765
|
try {
|
|
18698
18766
|
const raw2 = readFileSync3(statePath, "utf8");
|
|
18699
18767
|
const parsed = JSON.parse(raw2);
|
|
@@ -18710,6 +18778,7 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18710
18778
|
const scoped = livePlan.tables.filter((t) => modelTables.has(t.table));
|
|
18711
18779
|
if (scoped.length > 0) {
|
|
18712
18780
|
previous = { dialect: livePlan.dialect, tables: scoped };
|
|
18781
|
+
reconciledFromDatabase = true;
|
|
18713
18782
|
info(`-- No snapshot; reconciling against live database (${scoped.length} matching table${scoped.length === 1 ? "" : "s"})`);
|
|
18714
18783
|
}
|
|
18715
18784
|
} catch (err) {
|
|
@@ -18723,25 +18792,34 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18723
18792
|
} else {
|
|
18724
18793
|
info("-- Full migration requested, ignoring any previous state");
|
|
18725
18794
|
}
|
|
18726
|
-
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
|
+
});
|
|
18727
18800
|
const sqlStatements = diff.statements;
|
|
18728
18801
|
const operations = diff.operations;
|
|
18729
18802
|
const sql = sqlStatements.join(`
|
|
18730
18803
|
`);
|
|
18731
18804
|
const hasChanges = sqlStatements.some((stmt) => /\b(?:CREATE|ALTER|DROP)\b/i.test(stmt));
|
|
18732
18805
|
if (opts.apply) {
|
|
18733
|
-
const dirPath = mkdtempSync(
|
|
18734
|
-
const filePath =
|
|
18806
|
+
const dirPath = mkdtempSync(join10(tmpdir(), "qb-migrate-"));
|
|
18807
|
+
const filePath = join10(dirPath, "migration.sql");
|
|
18735
18808
|
try {
|
|
18736
18809
|
if (hasChanges) {
|
|
18737
18810
|
writeFileSync11(filePath, sql);
|
|
18811
|
+
const qb = createQueryBuilder();
|
|
18812
|
+
await qb.file(filePath);
|
|
18738
18813
|
info("-- Migration applied");
|
|
18814
|
+
await settleAppliedFiles(qb, dialect, lastCreatedMigrationFiles());
|
|
18739
18815
|
} else {
|
|
18740
18816
|
info("-- No changes; nothing to apply");
|
|
18741
18817
|
}
|
|
18742
18818
|
} catch (err) {
|
|
18743
18819
|
console.error("-- Migration failed:", err);
|
|
18744
18820
|
throw err;
|
|
18821
|
+
} finally {
|
|
18822
|
+
rmSync(dirPath, { recursive: true, force: true });
|
|
18745
18823
|
}
|
|
18746
18824
|
}
|
|
18747
18825
|
if (!opts.dryRun)
|
|
@@ -18750,9 +18828,6 @@ async function generateMigration(dir, opts = {}) {
|
|
|
18750
18828
|
}
|
|
18751
18829
|
async function executeMigration(dir) {
|
|
18752
18830
|
await getConfig();
|
|
18753
|
-
if (!dir) {
|
|
18754
|
-
dir = join9(process25.cwd(), "app/Models");
|
|
18755
|
-
}
|
|
18756
18831
|
const workspaceRoot = getWorkspaceRoot();
|
|
18757
18832
|
const sqlDir = ensureSqlDirectory(workspaceRoot);
|
|
18758
18833
|
const dialect = config5.dialect || "postgres";
|
|
@@ -18767,42 +18842,25 @@ async function executeMigration(dir) {
|
|
|
18767
18842
|
const qb = createQueryBuilder();
|
|
18768
18843
|
await createMigrationsTable(qb, dialect);
|
|
18769
18844
|
const executedMigrations = await getExecutedMigrations(qb, dialect);
|
|
18770
|
-
const
|
|
18771
|
-
|
|
18772
|
-
for (const file2 of scriptFiles) {
|
|
18773
|
-
if (file2.includes("alter-") && file2.includes("-table")) {
|
|
18774
|
-
transientMigrations.push(file2);
|
|
18775
|
-
} else if (!executedMigrations.includes(file2)) {
|
|
18776
|
-
permanentMigrations.push(file2);
|
|
18777
|
-
}
|
|
18778
|
-
}
|
|
18779
|
-
const totalPending = permanentMigrations.length + transientMigrations.length;
|
|
18780
|
-
if (totalPending === 0) {
|
|
18845
|
+
const pending = scriptFiles.filter((file2) => !executedMigrations.includes(file2));
|
|
18846
|
+
if (pending.length === 0) {
|
|
18781
18847
|
info("-- No pending migrations to execute");
|
|
18782
18848
|
return true;
|
|
18783
18849
|
}
|
|
18784
|
-
info(`-- Executing ${
|
|
18785
|
-
for (const file2 of
|
|
18786
|
-
const filePath =
|
|
18850
|
+
info(`-- Executing ${pending.length} migration${pending.length === 1 ? "" : "s"}`);
|
|
18851
|
+
for (const file2 of pending) {
|
|
18852
|
+
const filePath = join10(sqlDir, file2);
|
|
18787
18853
|
info(`-- Executing: ${file2}`);
|
|
18788
18854
|
try {
|
|
18789
18855
|
await qb.file(filePath);
|
|
18790
18856
|
await recordMigration(qb, file2, dialect);
|
|
18791
18857
|
info(`-- \u2713 Migration ${file2} executed and recorded`);
|
|
18792
18858
|
} catch (err) {
|
|
18793
|
-
|
|
18794
|
-
|
|
18795
|
-
|
|
18796
|
-
|
|
18797
|
-
|
|
18798
|
-
const filePath = join9(sqlDir, file2);
|
|
18799
|
-
info(`-- Executing: ${file2} (transient)`);
|
|
18800
|
-
try {
|
|
18801
|
-
await qb.file(filePath);
|
|
18802
|
-
info(`-- \u2713 Migration ${file2} executed (not recorded)`);
|
|
18803
|
-
unlinkSync(filePath);
|
|
18804
|
-
info(`-- \uD83D\uDDD1\uFE0F Deleted transient migration: ${file2}`);
|
|
18805
|
-
} 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
|
+
}
|
|
18806
18864
|
console.error(`-- \u2717 Migration ${file2} failed:`, err);
|
|
18807
18865
|
throw err;
|
|
18808
18866
|
}
|
|
@@ -18817,7 +18875,7 @@ async function executeMigration(dir) {
|
|
|
18817
18875
|
async function resetDatabase(dir, opts = {}) {
|
|
18818
18876
|
await getConfig();
|
|
18819
18877
|
if (!dir) {
|
|
18820
|
-
dir =
|
|
18878
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18821
18879
|
}
|
|
18822
18880
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
18823
18881
|
const driver = getDialectDriver(dialect);
|
|
@@ -18914,42 +18972,50 @@ async function resetDatabase(dir, opts = {}) {
|
|
|
18914
18972
|
}
|
|
18915
18973
|
async function deleteMigrationFiles(dir, workspaceRoot, opts = {}) {
|
|
18916
18974
|
if (!dir) {
|
|
18917
|
-
dir =
|
|
18975
|
+
dir = join10(process25.cwd(), "app/Models");
|
|
18918
18976
|
}
|
|
18919
18977
|
if (!workspaceRoot) {
|
|
18920
18978
|
workspaceRoot = getWorkspaceRoot();
|
|
18921
18979
|
}
|
|
18922
18980
|
const dialect = String(opts.dialect || config5.dialect || "postgres");
|
|
18923
18981
|
const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
|
|
18924
|
-
if (
|
|
18982
|
+
if (existsSync17(snapshotPath)) {
|
|
18925
18983
|
unlinkSync(snapshotPath);
|
|
18926
18984
|
info(`-- Removed model snapshot file: ${snapshotPath}`);
|
|
18927
18985
|
}
|
|
18928
|
-
const defaultStatePath =
|
|
18986
|
+
const defaultStatePath = join10(dir, `.qb-migrations.${dialect}.json`);
|
|
18929
18987
|
const statePath = String(opts.state || defaultStatePath);
|
|
18930
|
-
if (
|
|
18988
|
+
if (existsSync17(statePath)) {
|
|
18931
18989
|
unlinkSync(statePath);
|
|
18932
18990
|
info(`-- Removed legacy migration state file: ${statePath}`);
|
|
18933
18991
|
}
|
|
18934
|
-
|
|
18935
|
-
|
|
18936
|
-
|
|
18937
|
-
|
|
18938
|
-
|
|
18939
|
-
|
|
18940
|
-
|
|
18941
|
-
|
|
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;
|
|
18942
19003
|
}
|
|
18943
|
-
|
|
19004
|
+
unlinkSync(join10(sqlDir, file2));
|
|
19005
|
+
info(`-- Removed generated migration file: ${file2}`);
|
|
19006
|
+
removed += 1;
|
|
18944
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` : ""}`);
|
|
18945
19011
|
}
|
|
18946
19012
|
async function copyModelsToGenerated(_dir, _workspaceRoot) {}
|
|
18947
19013
|
async function clearGeneratedDirectory(workspaceRoot) {
|
|
18948
19014
|
if (!workspaceRoot) {
|
|
18949
19015
|
workspaceRoot = getWorkspaceRoot();
|
|
18950
19016
|
}
|
|
18951
|
-
const generatedDir =
|
|
18952
|
-
if (
|
|
19017
|
+
const generatedDir = join10(workspaceRoot, "generated");
|
|
19018
|
+
if (existsSync17(generatedDir)) {
|
|
18953
19019
|
try {
|
|
18954
19020
|
rmSync(generatedDir, { recursive: true, force: true });
|
|
18955
19021
|
info("-- Cleared generated directory");
|
|
@@ -18958,12 +19024,6 @@ async function clearGeneratedDirectory(workspaceRoot) {
|
|
|
18958
19024
|
}
|
|
18959
19025
|
}
|
|
18960
19026
|
}
|
|
18961
|
-
function getSqlDirectory(workspaceRoot) {
|
|
18962
|
-
if (!workspaceRoot) {
|
|
18963
|
-
workspaceRoot = getWorkspaceRoot();
|
|
18964
|
-
}
|
|
18965
|
-
return join9(workspaceRoot, "database", "migrations");
|
|
18966
|
-
}
|
|
18967
19027
|
async function createMigrationsTable(qb, dialect) {
|
|
18968
19028
|
const driver = getDialectDriver(dialect);
|
|
18969
19029
|
const createTableSql = driver.createMigrationsTable();
|
|
@@ -19002,6 +19062,7 @@ var init_migrate = __esm(() => {
|
|
|
19002
19062
|
init_drivers();
|
|
19003
19063
|
init_src2();
|
|
19004
19064
|
init_introspect_db();
|
|
19065
|
+
init_workspace();
|
|
19005
19066
|
});
|
|
19006
19067
|
|
|
19007
19068
|
// src/actions/migrate-generate.ts
|
|
@@ -19013,28 +19074,49 @@ var init_migrate_generate = __esm(() => {
|
|
|
19013
19074
|
});
|
|
19014
19075
|
|
|
19015
19076
|
// src/actions/migrate-rollback.ts
|
|
19016
|
-
import { existsSync as
|
|
19017
|
-
import {
|
|
19018
|
-
import process26 from "process";
|
|
19077
|
+
import { existsSync as existsSync18, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
19078
|
+
import { join as join11 } from "path";
|
|
19019
19079
|
function splitSqlStatements2(sql) {
|
|
19020
19080
|
const out = [];
|
|
19021
19081
|
let buf = "";
|
|
19022
|
-
let
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19031
|
-
|
|
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 === ";") {
|
|
19032
19114
|
if (buf.trim())
|
|
19033
19115
|
out.push(buf.trim());
|
|
19034
19116
|
buf = "";
|
|
19035
|
-
|
|
19036
|
-
buf += ch;
|
|
19117
|
+
continue;
|
|
19037
19118
|
}
|
|
19119
|
+
buf += ch;
|
|
19038
19120
|
}
|
|
19039
19121
|
if (buf.trim())
|
|
19040
19122
|
out.push(buf.trim());
|
|
@@ -19048,8 +19130,20 @@ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
|
19048
19130
|
let m;
|
|
19049
19131
|
if (m = /^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
|
|
19050
19132
|
down.push(`DROP TABLE IF EXISTS ${q(m[1])}`);
|
|
19051
|
-
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+
|
|
19052
|
-
|
|
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)}`);
|
|
19053
19147
|
} else if (m = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?(?:\s+ON\s+["`']?(\w+)["`']?)?/i.exec(stmt)) {
|
|
19054
19148
|
down.push(isMysqlLike(dialect) && m[2] ? `DROP INDEX ${q(m[1])} ON ${q(m[2])}` : `DROP INDEX IF EXISTS ${q(m[1])}`);
|
|
19055
19149
|
} else {
|
|
@@ -19058,22 +19152,6 @@ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
|
19058
19152
|
}
|
|
19059
19153
|
return { down: down.reverse(), skipped };
|
|
19060
19154
|
}
|
|
19061
|
-
function findWorkspaceRoot2(startPath) {
|
|
19062
|
-
let currentPath = startPath;
|
|
19063
|
-
while (currentPath !== dirname8(currentPath)) {
|
|
19064
|
-
if (existsSync17(join10(currentPath, "package.json"))) {
|
|
19065
|
-
return currentPath;
|
|
19066
|
-
}
|
|
19067
|
-
currentPath = dirname8(currentPath);
|
|
19068
|
-
}
|
|
19069
|
-
return process26.cwd();
|
|
19070
|
-
}
|
|
19071
|
-
function getSqlDirectory2(workspaceRoot) {
|
|
19072
|
-
if (!workspaceRoot) {
|
|
19073
|
-
workspaceRoot = findWorkspaceRoot2(process26.cwd());
|
|
19074
|
-
}
|
|
19075
|
-
return join10(workspaceRoot, "database", "migrations");
|
|
19076
|
-
}
|
|
19077
19155
|
async function migrateRollback(options = {}) {
|
|
19078
19156
|
const steps = options.steps || 1;
|
|
19079
19157
|
const reverseSchema = options.reverseSchema !== false;
|
|
@@ -19106,12 +19184,12 @@ async function migrateRollback(options = {}) {
|
|
|
19106
19184
|
console.log(` - ${migration.migration}`);
|
|
19107
19185
|
}
|
|
19108
19186
|
console.log();
|
|
19109
|
-
const sqlDir =
|
|
19187
|
+
const sqlDir = getSqlDirectory();
|
|
19110
19188
|
let reversedAny = false;
|
|
19111
19189
|
for (const migration of migrationsToRollback) {
|
|
19112
19190
|
try {
|
|
19113
|
-
const filePath =
|
|
19114
|
-
if (reverseSchema &&
|
|
19191
|
+
const filePath = join11(sqlDir, migration.migration);
|
|
19192
|
+
if (reverseSchema && existsSync18(filePath)) {
|
|
19115
19193
|
const forwardSql = readFileSync4(filePath, "utf8");
|
|
19116
19194
|
const { down, skipped } = deriveDownStatements(forwardSql);
|
|
19117
19195
|
for (const stmt of down) {
|
|
@@ -19122,10 +19200,10 @@ async function migrateRollback(options = {}) {
|
|
|
19122
19200
|
if (skipped.length > 0)
|
|
19123
19201
|
console.log(`-- \u26A0\uFE0F ${skipped.length} statement(s) in ${migration.migration} could not be auto-reversed (data/complex DDL) \u2014 reverse manually.`);
|
|
19124
19202
|
}
|
|
19125
|
-
const deleteSql = `DELETE FROM migrations WHERE migration = $1`;
|
|
19203
|
+
const deleteSql = `DELETE FROM migrations WHERE migration = ${getPlaceholder(1)}`;
|
|
19126
19204
|
await qb.unsafe(deleteSql, [migration.migration]);
|
|
19127
19205
|
console.log(`-- \u2713 Removed migration record: ${migration.migration}`);
|
|
19128
|
-
if (
|
|
19206
|
+
if (options.deleteFiles && existsSync18(filePath)) {
|
|
19129
19207
|
unlinkSync2(filePath);
|
|
19130
19208
|
console.log(`-- \uD83D\uDDD1\uFE0F Deleted migration file: ${migration.migration}`);
|
|
19131
19209
|
}
|
|
@@ -19148,37 +19226,20 @@ async function migrateRollback(options = {}) {
|
|
|
19148
19226
|
var init_migrate_rollback = __esm(() => {
|
|
19149
19227
|
init_config();
|
|
19150
19228
|
init_src2();
|
|
19229
|
+
init_workspace();
|
|
19151
19230
|
});
|
|
19152
19231
|
|
|
19153
19232
|
// src/actions/migrate-status.ts
|
|
19154
|
-
import { existsSync as
|
|
19155
|
-
import { dirname as dirname9, join as join11 } from "path";
|
|
19156
|
-
import process27 from "process";
|
|
19157
|
-
function findWorkspaceRoot3(startPath) {
|
|
19158
|
-
let currentPath = startPath;
|
|
19159
|
-
while (currentPath !== dirname9(currentPath)) {
|
|
19160
|
-
if (existsSync18(join11(currentPath, "package.json"))) {
|
|
19161
|
-
return currentPath;
|
|
19162
|
-
}
|
|
19163
|
-
currentPath = dirname9(currentPath);
|
|
19164
|
-
}
|
|
19165
|
-
return process27.cwd();
|
|
19166
|
-
}
|
|
19167
|
-
function getSqlDirectory3(workspaceRoot) {
|
|
19168
|
-
if (!workspaceRoot) {
|
|
19169
|
-
workspaceRoot = findWorkspaceRoot3(process27.cwd());
|
|
19170
|
-
}
|
|
19171
|
-
return join11(workspaceRoot, "database", "migrations");
|
|
19172
|
-
}
|
|
19233
|
+
import { existsSync as existsSync19, readdirSync as readdirSync8 } from "fs";
|
|
19173
19234
|
async function migrateStatus() {
|
|
19174
19235
|
const dialect = config5.dialect || "postgres";
|
|
19175
19236
|
const driver = getDialectDriver(dialect);
|
|
19176
|
-
const sqlDir =
|
|
19237
|
+
const sqlDir = getSqlDirectory();
|
|
19177
19238
|
console.log("-- Migration Status");
|
|
19178
19239
|
console.log(`-- Dialect: ${dialect}`);
|
|
19179
19240
|
console.log(`-- SQL directory: ${sqlDir}`);
|
|
19180
19241
|
console.log();
|
|
19181
|
-
if (!
|
|
19242
|
+
if (!existsSync19(sqlDir)) {
|
|
19182
19243
|
console.log("-- No SQL directory found. No migrations have been created yet.");
|
|
19183
19244
|
return [];
|
|
19184
19245
|
}
|
|
@@ -19199,27 +19260,16 @@ async function migrateStatus() {
|
|
|
19199
19260
|
console.log();
|
|
19200
19261
|
}
|
|
19201
19262
|
const executedMap = new Map(executedMigrations.map((m) => [m.migration, m.executed_at]));
|
|
19202
|
-
const statuses = migrationFiles.map((file2) => {
|
|
19203
|
-
|
|
19204
|
-
|
|
19205
|
-
|
|
19206
|
-
|
|
19207
|
-
};
|
|
19208
|
-
}
|
|
19209
|
-
const executedAt = executedMap.get(file2);
|
|
19210
|
-
return {
|
|
19211
|
-
file: file2,
|
|
19212
|
-
status: executedAt ? "executed" : "pending",
|
|
19213
|
-
executedAt
|
|
19214
|
-
};
|
|
19215
|
-
});
|
|
19263
|
+
const statuses = migrationFiles.map((file2) => ({
|
|
19264
|
+
file: file2,
|
|
19265
|
+
status: executedMap.has(file2) ? "executed" : "pending",
|
|
19266
|
+
executedAt: executedMap.get(file2)
|
|
19267
|
+
}));
|
|
19216
19268
|
const executed = statuses.filter((s) => s.status === "executed");
|
|
19217
19269
|
const pending = statuses.filter((s) => s.status === "pending");
|
|
19218
|
-
const transient = statuses.filter((s) => s.status === "transient");
|
|
19219
19270
|
console.log(`-- Total migrations: ${migrationFiles.length}`);
|
|
19220
19271
|
console.log(`-- Executed: ${executed.length}`);
|
|
19221
19272
|
console.log(`-- Pending: ${pending.length}`);
|
|
19222
|
-
console.log(`-- Transient: ${transient.length}`);
|
|
19223
19273
|
console.log();
|
|
19224
19274
|
if (executed.length > 0) {
|
|
19225
19275
|
console.log("\u2713 Executed Migrations:");
|
|
@@ -19235,13 +19285,6 @@ async function migrateStatus() {
|
|
|
19235
19285
|
}
|
|
19236
19286
|
console.log();
|
|
19237
19287
|
}
|
|
19238
|
-
if (transient.length > 0) {
|
|
19239
|
-
console.log("\u26A1 Transient Migrations (ALTER TABLE - not tracked):");
|
|
19240
|
-
for (const migration of transient) {
|
|
19241
|
-
console.log(` - ${migration.file}`);
|
|
19242
|
-
}
|
|
19243
|
-
console.log();
|
|
19244
|
-
}
|
|
19245
19288
|
return statuses;
|
|
19246
19289
|
} catch (err) {
|
|
19247
19290
|
console.error("-- Failed to get migration status:", err);
|
|
@@ -19255,14 +19298,15 @@ var init_migrate_status = __esm(() => {
|
|
|
19255
19298
|
init_config();
|
|
19256
19299
|
init_drivers();
|
|
19257
19300
|
init_src2();
|
|
19301
|
+
init_workspace();
|
|
19258
19302
|
});
|
|
19259
19303
|
|
|
19260
19304
|
// src/actions/model-show.ts
|
|
19261
19305
|
import { readdirSync as readdirSync9 } from "fs";
|
|
19262
19306
|
import { extname as extname2, join as join12 } from "path";
|
|
19263
|
-
import
|
|
19307
|
+
import process26 from "process";
|
|
19264
19308
|
async function modelShow(modelName, options = {}) {
|
|
19265
|
-
const dir = options.dir || join12(
|
|
19309
|
+
const dir = options.dir || join12(process26.cwd(), "app/Models");
|
|
19266
19310
|
try {
|
|
19267
19311
|
const files = readdirSync9(dir);
|
|
19268
19312
|
const modelFile = files.find((f) => {
|
|
@@ -19476,9 +19520,9 @@ var init_query_explain_all = __esm(() => {
|
|
|
19476
19520
|
// src/actions/relation-diagram.ts
|
|
19477
19521
|
import { writeFileSync as writeFileSync12 } from "fs";
|
|
19478
19522
|
import { join as join14 } from "path";
|
|
19479
|
-
import
|
|
19523
|
+
import process27 from "process";
|
|
19480
19524
|
async function relationDiagram(options = {}) {
|
|
19481
|
-
const dir = options.dir || join14(
|
|
19525
|
+
const dir = options.dir || join14(process27.cwd(), "app/Models");
|
|
19482
19526
|
const format = options.format || "mermaid";
|
|
19483
19527
|
try {
|
|
19484
19528
|
const models = await loadModels({ modelsDir: dir });
|
|
@@ -19598,25 +19642,25 @@ var init_relation_diagram = __esm(() => {
|
|
|
19598
19642
|
});
|
|
19599
19643
|
|
|
19600
19644
|
// src/actions/seed.ts
|
|
19601
|
-
import { existsSync as
|
|
19645
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readdirSync as readdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
19602
19646
|
import { dirname as dirname10, join as join15 } from "path";
|
|
19603
|
-
import
|
|
19604
|
-
function
|
|
19647
|
+
import process28 from "process";
|
|
19648
|
+
function findWorkspaceRoot3(startPath) {
|
|
19605
19649
|
let currentPath = startPath;
|
|
19606
19650
|
while (currentPath !== dirname10(currentPath)) {
|
|
19607
|
-
if (
|
|
19651
|
+
if (existsSync20(join15(currentPath, "package.json"))) {
|
|
19608
19652
|
return currentPath;
|
|
19609
19653
|
}
|
|
19610
19654
|
currentPath = dirname10(currentPath);
|
|
19611
19655
|
}
|
|
19612
|
-
return
|
|
19656
|
+
return process28.cwd();
|
|
19613
19657
|
}
|
|
19614
19658
|
async function loadSeeders(seedersDir) {
|
|
19615
|
-
if (!
|
|
19659
|
+
if (!existsSync20(seedersDir)) {
|
|
19616
19660
|
console.log(`-- Seeders directory not found: ${seedersDir}`);
|
|
19617
19661
|
return [];
|
|
19618
19662
|
}
|
|
19619
|
-
const files = readdirSync11(seedersDir);
|
|
19663
|
+
const files = readdirSync11(seedersDir).sort((a, b) => a.localeCompare(b));
|
|
19620
19664
|
const seederFiles = files.filter((file2) => (file2.endsWith(".ts") || file2.endsWith(".js")) && file2 !== "index.ts" && file2 !== "index.js");
|
|
19621
19665
|
const seeders = [];
|
|
19622
19666
|
for (const file2 of seederFiles) {
|
|
@@ -19629,6 +19673,7 @@ async function loadSeeders(seedersDir) {
|
|
|
19629
19673
|
if (instance && typeof instance.run === "function") {
|
|
19630
19674
|
seeders.push({
|
|
19631
19675
|
name: file2.replace(/\.(ts|js)$/, ""),
|
|
19676
|
+
className: String(SeederClass.name || ""),
|
|
19632
19677
|
instance
|
|
19633
19678
|
});
|
|
19634
19679
|
}
|
|
@@ -19641,7 +19686,7 @@ async function loadSeeders(seedersDir) {
|
|
|
19641
19686
|
return seeders;
|
|
19642
19687
|
}
|
|
19643
19688
|
async function runSeeders(config6 = {}) {
|
|
19644
|
-
const workspaceRoot =
|
|
19689
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19645
19690
|
const seedersDir = config6.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
19646
19691
|
const verbose = config6.verbose ?? true;
|
|
19647
19692
|
if (verbose) {
|
|
@@ -19676,17 +19721,18 @@ async function runSeeders(config6 = {}) {
|
|
|
19676
19721
|
}
|
|
19677
19722
|
}
|
|
19678
19723
|
async function runSeeder(className, options = {}) {
|
|
19679
|
-
const workspaceRoot =
|
|
19724
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19680
19725
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
19681
19726
|
const verbose = options.verbose ?? true;
|
|
19682
19727
|
if (verbose) {
|
|
19683
19728
|
console.log(`-- Running seeder: ${className}`);
|
|
19684
19729
|
}
|
|
19685
19730
|
const seeders = await loadSeeders(seedersDir);
|
|
19686
|
-
const seeder = seeders.find((s) => s.name === className);
|
|
19731
|
+
const seeder = seeders.find((s) => s.className === className) ?? seeders.find((s) => s.name === className);
|
|
19687
19732
|
if (!seeder) {
|
|
19688
|
-
|
|
19689
|
-
|
|
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}`);
|
|
19690
19736
|
}
|
|
19691
19737
|
const qb = createQueryBuilder();
|
|
19692
19738
|
try {
|
|
@@ -19700,9 +19746,9 @@ async function runSeeder(className, options = {}) {
|
|
|
19700
19746
|
}
|
|
19701
19747
|
}
|
|
19702
19748
|
async function makeSeeder(name) {
|
|
19703
|
-
const workspaceRoot =
|
|
19749
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19704
19750
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
19705
|
-
if (!
|
|
19751
|
+
if (!existsSync20(seedersDir)) {
|
|
19706
19752
|
mkdirSync9(seedersDir, { recursive: true });
|
|
19707
19753
|
console.log(`-- Created seeders directory: ${seedersDir}`);
|
|
19708
19754
|
}
|
|
@@ -19710,7 +19756,7 @@ async function makeSeeder(name) {
|
|
|
19710
19756
|
const className = `${baseName}Seeder`;
|
|
19711
19757
|
const fileName = `${className}.ts`;
|
|
19712
19758
|
const filePath = join15(seedersDir, fileName);
|
|
19713
|
-
if (
|
|
19759
|
+
if (existsSync20(filePath)) {
|
|
19714
19760
|
console.error(`-- Seeder already exists: ${filePath}`);
|
|
19715
19761
|
throw new Error(`Seeder already exists: ${filePath}`);
|
|
19716
19762
|
}
|
|
@@ -19764,7 +19810,7 @@ export default class ${className} extends Seeder {
|
|
|
19764
19810
|
console.log(`-- \u2713 Created seeder: ${filePath}`);
|
|
19765
19811
|
}
|
|
19766
19812
|
async function freshDatabase(options = {}) {
|
|
19767
|
-
const workspaceRoot =
|
|
19813
|
+
const workspaceRoot = findWorkspaceRoot3(process28.cwd());
|
|
19768
19814
|
const modelsDir = options.modelsDir || join15(workspaceRoot, "app/Models");
|
|
19769
19815
|
const seedersDir = options.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
19770
19816
|
const verbose = options.verbose ?? true;
|
|
@@ -19824,22 +19870,22 @@ var init_unsafe = __esm(() => {
|
|
|
19824
19870
|
});
|
|
19825
19871
|
|
|
19826
19872
|
// src/actions/validate.ts
|
|
19827
|
-
import { existsSync as
|
|
19873
|
+
import { existsSync as existsSync21 } from "fs";
|
|
19828
19874
|
import { dirname as dirname11, join as join16 } from "path";
|
|
19829
|
-
import
|
|
19830
|
-
function
|
|
19875
|
+
import process29 from "process";
|
|
19876
|
+
function findWorkspaceRoot4(startPath) {
|
|
19831
19877
|
let currentPath = startPath;
|
|
19832
19878
|
while (currentPath !== dirname11(currentPath)) {
|
|
19833
|
-
if (
|
|
19879
|
+
if (existsSync21(join16(currentPath, "package.json"))) {
|
|
19834
19880
|
return currentPath;
|
|
19835
19881
|
}
|
|
19836
19882
|
currentPath = dirname11(currentPath);
|
|
19837
19883
|
}
|
|
19838
|
-
return
|
|
19884
|
+
return process29.cwd();
|
|
19839
19885
|
}
|
|
19840
19886
|
async function validateSchema(dir) {
|
|
19841
19887
|
if (!dir) {
|
|
19842
|
-
dir = join16(
|
|
19888
|
+
dir = join16(findWorkspaceRoot4(process29.cwd()), "app/Models");
|
|
19843
19889
|
}
|
|
19844
19890
|
const dialect = config5.dialect || "postgres";
|
|
19845
19891
|
console.log("-- Validating Schema");
|
|
@@ -25090,18 +25136,11 @@ function toSnakeCase(str) {
|
|
|
25090
25136
|
snakeCaseCache.set(str, cached);
|
|
25091
25137
|
return cached;
|
|
25092
25138
|
}
|
|
25093
|
-
function
|
|
25139
|
+
function toTableName2(modelName) {
|
|
25094
25140
|
let cached = tableNameCache.get(modelName);
|
|
25095
25141
|
if (cached !== undefined)
|
|
25096
25142
|
return cached;
|
|
25097
|
-
|
|
25098
|
-
if (snake.endsWith("y") && !snake.endsWith("ay") && !snake.endsWith("ey") && !snake.endsWith("oy") && !snake.endsWith("uy")) {
|
|
25099
|
-
cached = snake.slice(0, -1) + "ies";
|
|
25100
|
-
} else if (snake.endsWith("s") || snake.endsWith("x") || snake.endsWith("ch") || snake.endsWith("sh")) {
|
|
25101
|
-
cached = `${snake}es`;
|
|
25102
|
-
} else {
|
|
25103
|
-
cached = `${snake}s`;
|
|
25104
|
-
}
|
|
25143
|
+
cached = toTableName(modelName);
|
|
25105
25144
|
tableNameCache.set(modelName, cached);
|
|
25106
25145
|
return cached;
|
|
25107
25146
|
}
|
|
@@ -25186,14 +25225,14 @@ function resolveRelation(definition, relationName) {
|
|
|
25186
25225
|
const hasManyModel = findModelName(definition.hasMany);
|
|
25187
25226
|
if (hasManyModel) {
|
|
25188
25227
|
const relatedModel = getModelFromRegistry(hasManyModel);
|
|
25189
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25228
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(hasManyModel);
|
|
25190
25229
|
const foreignKey = toSnakeCase(parentName) + "_id";
|
|
25191
25230
|
return { type: "hasMany", relatedModelName: hasManyModel, relatedTable, foreignKey, localKey: parentPk };
|
|
25192
25231
|
}
|
|
25193
25232
|
const hasOneModel = findModelName(definition.hasOne);
|
|
25194
25233
|
if (hasOneModel) {
|
|
25195
25234
|
const relatedModel = getModelFromRegistry(hasOneModel);
|
|
25196
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25235
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(hasOneModel);
|
|
25197
25236
|
const foreignKey = toSnakeCase(parentName) + "_id";
|
|
25198
25237
|
return { type: "hasOne", relatedModelName: hasOneModel, relatedTable, foreignKey, localKey: parentPk };
|
|
25199
25238
|
}
|
|
@@ -25201,7 +25240,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25201
25240
|
if (belongsToEntry) {
|
|
25202
25241
|
const belongsToModel = belongsToEntry.model;
|
|
25203
25242
|
const relatedModel = getModelFromRegistry(belongsToModel);
|
|
25204
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25243
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(belongsToModel);
|
|
25205
25244
|
const relatedPk = relatedModel?.getDefinition?.()?.primaryKey || "id";
|
|
25206
25245
|
const foreignKey = belongsToEntry.foreignKey || `${toSnakeCase(belongsToModel)}_id`;
|
|
25207
25246
|
return { type: "belongsTo", relatedModelName: belongsToModel, relatedTable, foreignKey, localKey: relatedPk };
|
|
@@ -25211,7 +25250,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25211
25250
|
const isConfig = typeof found.entry === "object";
|
|
25212
25251
|
const relatedModelName = isConfig ? found.entry.model : found.entry;
|
|
25213
25252
|
const relatedModel = getModelFromRegistry(relatedModelName);
|
|
25214
|
-
const relatedTable = relatedModel?.getTable?.() ||
|
|
25253
|
+
const relatedTable = relatedModel?.getTable?.() || toTableName2(relatedModelName);
|
|
25215
25254
|
const config6 = isConfig ? found.entry : null;
|
|
25216
25255
|
let pivotTable;
|
|
25217
25256
|
let pivotModelName;
|
|
@@ -25223,7 +25262,7 @@ function resolveRelation(definition, relationName) {
|
|
|
25223
25262
|
if (!throughModel) {
|
|
25224
25263
|
throw new Error(`[orm] belongsToMany relation '${relationName}' on '${parentName}' references unknown through model '${config6.through}'. Make sure '${config6.through}' is registered.`);
|
|
25225
25264
|
}
|
|
25226
|
-
pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table ||
|
|
25265
|
+
pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table || toTableName2(config6.through);
|
|
25227
25266
|
} else {
|
|
25228
25267
|
const a2 = singularizeWord(parentTable);
|
|
25229
25268
|
const b2 = singularizeWord(relatedTable);
|
|
@@ -25274,10 +25313,10 @@ function resolveRelation(definition, relationName) {
|
|
|
25274
25313
|
return null;
|
|
25275
25314
|
const throughModel = getModelFromRegistry(throughModelName);
|
|
25276
25315
|
const throughDef = throughModel?.getDefinition?.() || throughModel?.definition;
|
|
25277
|
-
const throughTable = throughModel?.getTable?.() || throughDef?.table ||
|
|
25316
|
+
const throughTable = throughModel?.getTable?.() || throughDef?.table || toTableName2(throughModelName);
|
|
25278
25317
|
const throughPk = throughDef?.primaryKey || "id";
|
|
25279
25318
|
const targetModel = getModelFromRegistry(targetModelName);
|
|
25280
|
-
const targetTable = targetModel?.getTable?.() ||
|
|
25319
|
+
const targetTable = targetModel?.getTable?.() || toTableName2(targetModelName);
|
|
25281
25320
|
return {
|
|
25282
25321
|
type,
|
|
25283
25322
|
relatedModelName: targetModelName,
|
|
@@ -27874,7 +27913,7 @@ function buildDatabaseSchema(models) {
|
|
|
27874
27913
|
for (const modelName of Object.keys(models)) {
|
|
27875
27914
|
const rawModel = models[modelName];
|
|
27876
27915
|
const m2 = rawModel.definition ?? rawModel.getDefinition?.() ?? rawModel;
|
|
27877
|
-
const table =
|
|
27916
|
+
const table = tableNameFor(m2);
|
|
27878
27917
|
const attrs = m2.attributes ?? {};
|
|
27879
27918
|
const columns = {};
|
|
27880
27919
|
for (const key of Object.keys(attrs))
|
|
@@ -27886,30 +27925,53 @@ function buildDatabaseSchema(models) {
|
|
|
27886
27925
|
}
|
|
27887
27926
|
return schema;
|
|
27888
27927
|
}
|
|
27928
|
+
var init_factory = () => {};
|
|
27889
27929
|
|
|
27890
27930
|
// src/loader.ts
|
|
27891
|
-
import { readdirSync as readdirSync12
|
|
27892
|
-
import { basename, extname as extname4 } from "path";
|
|
27893
|
-
import
|
|
27894
|
-
|
|
27895
|
-
const
|
|
27896
|
-
|
|
27897
|
-
|
|
27898
|
-
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));
|
|
27899
27952
|
for (const entry of entries) {
|
|
27900
|
-
const full =
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
continue;
|
|
27904
|
-
const ext = extname4(full);
|
|
27905
|
-
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));
|
|
27906
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)) {
|
|
27907
27968
|
const cacheBuster = `?t=${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
27908
27969
|
const mod = await import(`${full}${cacheBuster}`);
|
|
27909
27970
|
const rawDef = mod.default ?? mod;
|
|
27910
27971
|
const def = rawDef.definition ?? rawDef.getDefinition?.() ?? rawDef;
|
|
27911
|
-
|
|
27912
|
-
|
|
27972
|
+
if (!isModelDefinition(def))
|
|
27973
|
+
continue;
|
|
27974
|
+
const name = def.name ?? basename(full, extname4(full));
|
|
27913
27975
|
result[name] = {
|
|
27914
27976
|
...def,
|
|
27915
27977
|
name
|
|
@@ -27917,7 +27979,10 @@ async function loadModels(options) {
|
|
|
27917
27979
|
}
|
|
27918
27980
|
return result;
|
|
27919
27981
|
}
|
|
27920
|
-
var
|
|
27982
|
+
var MODEL_EXTENSIONS;
|
|
27983
|
+
var init_loader = __esm(() => {
|
|
27984
|
+
MODEL_EXTENSIONS = new Set([".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]);
|
|
27985
|
+
});
|
|
27921
27986
|
|
|
27922
27987
|
// src/meta.ts
|
|
27923
27988
|
function buildSchemaMeta(models) {
|
|
@@ -27929,7 +27994,7 @@ function buildSchemaMeta(models) {
|
|
|
27929
27994
|
for (const name of Object.keys(models)) {
|
|
27930
27995
|
const rawModel = models[name];
|
|
27931
27996
|
const m2 = rawModel.definition ?? rawModel.getDefinition?.() ?? rawModel;
|
|
27932
|
-
const table =
|
|
27997
|
+
const table = tableNameFor(m2);
|
|
27933
27998
|
modelToTable[name] = table;
|
|
27934
27999
|
tableToModel[table] = name;
|
|
27935
28000
|
primaryKeys[table] = m2.primaryKey ?? "id";
|
|
@@ -28003,9 +28068,9 @@ function buildSchemaMeta(models) {
|
|
|
28003
28068
|
var init_meta = () => {};
|
|
28004
28069
|
|
|
28005
28070
|
// src/migrations.ts
|
|
28006
|
-
import { existsSync as
|
|
28007
|
-
import { dirname as dirname12, join as
|
|
28008
|
-
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";
|
|
28009
28074
|
function info2(message) {
|
|
28010
28075
|
if (config5.verbose)
|
|
28011
28076
|
console.log(message);
|
|
@@ -28016,36 +28081,80 @@ function snakeCase(str) {
|
|
|
28016
28081
|
function singularizeTable(table) {
|
|
28017
28082
|
return singularizerFor(config5.relations?.singularizeStrategy)(table);
|
|
28018
28083
|
}
|
|
28019
|
-
function
|
|
28084
|
+
function lastCreatedMigrationFiles() {
|
|
28085
|
+
return [...createdMigrationFiles];
|
|
28086
|
+
}
|
|
28087
|
+
function findWorkspaceRoot5(startPath) {
|
|
28020
28088
|
let currentPath = startPath;
|
|
28021
28089
|
while (currentPath !== dirname12(currentPath)) {
|
|
28022
|
-
if (
|
|
28090
|
+
if (existsSync23(join18(currentPath, "package.json"))) {
|
|
28023
28091
|
return currentPath;
|
|
28024
28092
|
}
|
|
28025
28093
|
currentPath = dirname12(currentPath);
|
|
28026
28094
|
}
|
|
28027
|
-
return
|
|
28095
|
+
return process31.cwd();
|
|
28028
28096
|
}
|
|
28029
28097
|
function ensureSqlDirectory2() {
|
|
28030
|
-
const workspaceRoot =
|
|
28031
|
-
const sqlDir =
|
|
28032
|
-
if (!
|
|
28098
|
+
const workspaceRoot = findWorkspaceRoot5(process31.cwd());
|
|
28099
|
+
const sqlDir = join18(workspaceRoot, "database", "migrations");
|
|
28100
|
+
if (!existsSync23(sqlDir)) {
|
|
28033
28101
|
mkdirSync10(sqlDir, { recursive: true });
|
|
28034
28102
|
info2(`-- Created SQL directory: ${sqlDir}`);
|
|
28035
28103
|
}
|
|
28036
28104
|
return sqlDir;
|
|
28037
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
|
+
}
|
|
28038
28129
|
function createMigrationFile(statement, fileName) {
|
|
28039
28130
|
if (!statement)
|
|
28040
28131
|
return false;
|
|
28041
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
|
+
}
|
|
28042
28140
|
migrationCounter++;
|
|
28043
|
-
|
|
28044
|
-
|
|
28045
|
-
|
|
28046
|
-
|
|
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}`);
|
|
28047
28154
|
info2(`-- Migration file created: ${fullFileName}`);
|
|
28048
28155
|
migrationsCreatedCount++;
|
|
28156
|
+
createdMigrationFiles.push(fullFileName);
|
|
28157
|
+
existingCorpusBodies?.add(body);
|
|
28049
28158
|
return true;
|
|
28050
28159
|
}
|
|
28051
28160
|
function guessTypeFromName(columnName) {
|
|
@@ -28220,7 +28329,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28220
28329
|
const tables = [];
|
|
28221
28330
|
for (const modelName of Object.keys(models)) {
|
|
28222
28331
|
const model = models[modelName];
|
|
28223
|
-
const table =
|
|
28332
|
+
const table = tableNameFor(model);
|
|
28224
28333
|
const primaryKey = model.primaryKey ?? "id";
|
|
28225
28334
|
const _autoIncrement = model.autoIncrement !== false;
|
|
28226
28335
|
const attrs = model.attributes ?? {};
|
|
@@ -28312,9 +28421,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28312
28421
|
if (columns.some((c2) => c2.name === fkColumnName))
|
|
28313
28422
|
continue;
|
|
28314
28423
|
const refTable = meta.modelToTable[rel.model];
|
|
28315
|
-
|
|
28316
|
-
continue;
|
|
28317
|
-
const refPk = meta.primaryKeys[refTable] ?? "id";
|
|
28424
|
+
const refPk = refTable ? meta.primaryKeys[refTable] ?? "id" : undefined;
|
|
28318
28425
|
columns.push({
|
|
28319
28426
|
name: fkColumnName,
|
|
28320
28427
|
type: "bigint",
|
|
@@ -28322,7 +28429,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28322
28429
|
isUnique: false,
|
|
28323
28430
|
isNullable: true,
|
|
28324
28431
|
hasDefault: false,
|
|
28325
|
-
references: { table: refTable, column: refPk, onDelete: rel.onDelete }
|
|
28432
|
+
references: refTable && refPk ? { table: refTable, column: refPk, onDelete: rel.onDelete } : undefined
|
|
28326
28433
|
});
|
|
28327
28434
|
}
|
|
28328
28435
|
const traits = model.traits;
|
|
@@ -28403,7 +28510,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28403
28510
|
for (const modelName of Object.keys(models)) {
|
|
28404
28511
|
const model = models[modelName];
|
|
28405
28512
|
const traits = model?.traits ?? {};
|
|
28406
|
-
const modelTable =
|
|
28513
|
+
const modelTable = tableNameFor(model);
|
|
28407
28514
|
const singular = singularizeTable(modelTable);
|
|
28408
28515
|
if (traits.likeable) {
|
|
28409
28516
|
const opts = typeof traits.likeable === "object" ? traits.likeable : {};
|
|
@@ -28470,7 +28577,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
28470
28577
|
const btm = model?.belongsToMany;
|
|
28471
28578
|
if (!btm || typeof btm !== "object" || Array.isArray(btm))
|
|
28472
28579
|
continue;
|
|
28473
|
-
const parentTable =
|
|
28580
|
+
const parentTable = tableNameFor(model);
|
|
28474
28581
|
const parentSingular = singularizeTable(parentTable);
|
|
28475
28582
|
for (const [relKey, value] of Object.entries(btm)) {
|
|
28476
28583
|
const cfg = value;
|
|
@@ -28531,6 +28638,8 @@ function generateSql(plan, opts = {}) {
|
|
|
28531
28638
|
migrationCounter = 0;
|
|
28532
28639
|
migrationsCreatedCount = 0;
|
|
28533
28640
|
migrationsUpdatedCount = 0;
|
|
28641
|
+
createdMigrationFiles = [];
|
|
28642
|
+
existingCorpusBodies = null;
|
|
28534
28643
|
useDeterministicNames = true;
|
|
28535
28644
|
const emit = opts.dryRun ? () => false : createMigrationFile;
|
|
28536
28645
|
const statements = [];
|
|
@@ -28792,12 +28901,13 @@ function referencesAreDifferent(r1, r2) {
|
|
|
28792
28901
|
const act = (a2) => (a2 ?? "no action").toLowerCase();
|
|
28793
28902
|
return r1.table !== r2.table || r1.column !== r2.column || act(r1.onDelete) !== act(r2.onDelete) || act(r1.onUpdate) !== act(r2.onUpdate);
|
|
28794
28903
|
}
|
|
28904
|
+
function indexKey(index) {
|
|
28905
|
+
return `${index.type}:${index.name}:${index.columns.join(",")}`;
|
|
28906
|
+
}
|
|
28795
28907
|
function mapIndexesByKey(indexes) {
|
|
28796
28908
|
const map = {};
|
|
28797
|
-
for (const i2 of indexes)
|
|
28798
|
-
|
|
28799
|
-
map[key] = i2;
|
|
28800
|
-
}
|
|
28909
|
+
for (const i2 of indexes)
|
|
28910
|
+
map[indexKey(i2)] = i2;
|
|
28801
28911
|
return map;
|
|
28802
28912
|
}
|
|
28803
28913
|
function generateDiffOperations(previous, next, opts = {}) {
|
|
@@ -28812,10 +28922,13 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
28812
28922
|
return { statements: statements2, operations: operations2 };
|
|
28813
28923
|
}
|
|
28814
28924
|
const applyRenames = opts.applyRenames !== false;
|
|
28925
|
+
const preserveUnknownColumns = opts.preserveUnknownColumns === true;
|
|
28815
28926
|
const emit = opts.dryRun ? () => false : createMigrationFile;
|
|
28816
28927
|
migrationCounter = 0;
|
|
28817
28928
|
migrationsCreatedCount = 0;
|
|
28818
28929
|
migrationsUpdatedCount = 0;
|
|
28930
|
+
createdMigrationFiles = [];
|
|
28931
|
+
existingCorpusBodies = null;
|
|
28819
28932
|
useDeterministicNames = false;
|
|
28820
28933
|
const chunks = [];
|
|
28821
28934
|
const operations = [];
|
|
@@ -28914,22 +29027,32 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
28914
29027
|
}
|
|
28915
29028
|
for (const tableName of Object.keys(nextTables)) {
|
|
28916
29029
|
const prev = prevTables[tableName];
|
|
28917
|
-
|
|
29030
|
+
let curr = nextTables[tableName];
|
|
28918
29031
|
if (!prev)
|
|
28919
29032
|
continue;
|
|
28920
29033
|
const prevCols = mapColumnsByName(prev.columns);
|
|
28921
|
-
const currCols = mapColumnsByName(curr.columns);
|
|
28922
29034
|
const prevIdx = mapIndexesByKey(prev.indexes);
|
|
28923
|
-
|
|
29035
|
+
let currCols = mapColumnsByName(curr.columns);
|
|
29036
|
+
let currIdx = mapIndexesByKey(curr.indexes);
|
|
28924
29037
|
let renames = [];
|
|
28925
29038
|
let removedCols = Object.keys(prevCols).filter((n2) => !currCols[n2]);
|
|
28926
29039
|
let addedCols = Object.keys(currCols).filter((n2) => !prevCols[n2]);
|
|
28927
|
-
if (applyRenames) {
|
|
29040
|
+
if (applyRenames && !preserveUnknownColumns) {
|
|
28928
29041
|
const detected = detectColumnRenames(prevCols, currCols, dialect);
|
|
28929
29042
|
renames = detected.renames;
|
|
28930
29043
|
removedCols = detected.removed;
|
|
28931
29044
|
addedCols = detected.added;
|
|
28932
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
|
+
}
|
|
28933
29056
|
const modifiedCols = Object.keys(currCols).filter((n2) => prevCols[n2] && columnsAreDifferent(prevCols[n2], currCols[n2], dialect));
|
|
28934
29057
|
const fkChangedCols = Object.keys(currCols).filter((n2) => prevCols[n2] && referencesAreDifferent(prevCols[n2].references, currCols[n2].references));
|
|
28935
29058
|
if (dialect === "sqlite") {
|
|
@@ -29077,11 +29200,12 @@ function generateDiffOperations(previous, next, opts = {}) {
|
|
|
29077
29200
|
function generateDiffSql(previous, next, opts = {}) {
|
|
29078
29201
|
return generateDiffOperations(previous, next, opts).statements;
|
|
29079
29202
|
}
|
|
29080
|
-
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";
|
|
29081
29204
|
var init_migrations = __esm(() => {
|
|
29082
29205
|
init_config();
|
|
29083
29206
|
init_drivers();
|
|
29084
29207
|
init_meta();
|
|
29208
|
+
createdMigrationFiles = [];
|
|
29085
29209
|
});
|
|
29086
29210
|
|
|
29087
29211
|
// src/schema.ts
|
|
@@ -29115,6 +29239,7 @@ var init_src2 = __esm(() => {
|
|
|
29115
29239
|
init_dynamodb_single_table();
|
|
29116
29240
|
init_dynamodb_tooling_adapter();
|
|
29117
29241
|
init_dynamodb();
|
|
29242
|
+
init_factory();
|
|
29118
29243
|
init_loader();
|
|
29119
29244
|
init_meta();
|
|
29120
29245
|
init_migrations();
|
|
@@ -29130,9 +29255,11 @@ export {
|
|
|
29130
29255
|
validateSchema,
|
|
29131
29256
|
unsafe,
|
|
29132
29257
|
unmarshallItem,
|
|
29258
|
+
toTableName,
|
|
29133
29259
|
toModelInstance,
|
|
29134
29260
|
toDynamoDBItem,
|
|
29135
29261
|
tinker,
|
|
29262
|
+
tableNameFor,
|
|
29136
29263
|
tableInfo,
|
|
29137
29264
|
stripTrailingS,
|
|
29138
29265
|
startConsole,
|
|
@@ -29151,6 +29278,7 @@ export {
|
|
|
29151
29278
|
resolveSqlitePragmas,
|
|
29152
29279
|
resolvePivot,
|
|
29153
29280
|
resolveKeyPattern,
|
|
29281
|
+
resolveDialect,
|
|
29154
29282
|
resetDatabase,
|
|
29155
29283
|
resetConnection,
|
|
29156
29284
|
relationDiagram,
|
|
@@ -29163,6 +29291,7 @@ export {
|
|
|
29163
29291
|
dbOptimize as optimizeDatabase,
|
|
29164
29292
|
modelShow,
|
|
29165
29293
|
modelNameForTable,
|
|
29294
|
+
migrationBody,
|
|
29166
29295
|
migrateStatus,
|
|
29167
29296
|
migrateRollback,
|
|
29168
29297
|
migrateModels,
|
|
@@ -29172,8 +29301,10 @@ export {
|
|
|
29172
29301
|
makeSeeder,
|
|
29173
29302
|
makeModel,
|
|
29174
29303
|
loadModels,
|
|
29304
|
+
lastCreatedMigrationFiles,
|
|
29175
29305
|
iterateAllPivots,
|
|
29176
29306
|
isMysqlLike,
|
|
29307
|
+
isGeneratedMigrationSql,
|
|
29177
29308
|
isDefinitionEqual,
|
|
29178
29309
|
isBrowser,
|
|
29179
29310
|
introspectDatabase,
|
|
@@ -29268,6 +29399,7 @@ export {
|
|
|
29268
29399
|
MySQLDriver,
|
|
29269
29400
|
Model,
|
|
29270
29401
|
MIGRATIONS_TABLE,
|
|
29402
|
+
GENERATED_MARKER,
|
|
29271
29403
|
EntityQueryBuilder,
|
|
29272
29404
|
DynamoDBToolingAdapter,
|
|
29273
29405
|
DynamoDBQueryBuilder,
|