bun-query-builder 0.1.25 → 0.1.26
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/index.d.ts +1 -0
- package/dist/actions/introspect-db.d.ts +32 -0
- package/dist/actions/migrate-rollback.d.ts +14 -0
- package/dist/bin/cli.js +819 -235
- package/dist/client.d.ts +3 -3
- package/dist/config.d.ts +1 -15
- package/dist/db.d.ts +54 -0
- package/dist/orm.d.ts +16 -0
- package/dist/relation-utils.d.ts +27 -0
- package/dist/src/index.js +810 -229
- package/dist/types.d.ts +21 -0
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -11694,7 +11694,7 @@ function setConfig(userConfig) {
|
|
|
11694
11694
|
Object.assign(_config, config5);
|
|
11695
11695
|
}
|
|
11696
11696
|
}
|
|
11697
|
-
var defaultConfig4, config5, _config = null, _lastConfiguredDialect = null, _warnedDialectConflicts;
|
|
11697
|
+
var defaultConfig4, CONFIG_SINGLETON_KEY, config5, _config = null, _lastConfiguredDialect = null, _warnedDialectConflicts;
|
|
11698
11698
|
var init_config = __esm(() => {
|
|
11699
11699
|
init_dist();
|
|
11700
11700
|
defaultConfig4 = {
|
|
@@ -11754,14 +11754,14 @@ var init_config = __esm(() => {
|
|
|
11754
11754
|
defaultFilter: true
|
|
11755
11755
|
}
|
|
11756
11756
|
};
|
|
11757
|
-
|
|
11757
|
+
CONFIG_SINGLETON_KEY = Symbol.for("bun-query-builder.config");
|
|
11758
|
+
config5 = globalThis[CONFIG_SINGLETON_KEY] ??= { ...defaultConfig4 };
|
|
11758
11759
|
_warnedDialectConflicts = new Set;
|
|
11759
11760
|
});
|
|
11760
11761
|
|
|
11761
11762
|
// src/db.ts
|
|
11762
11763
|
var {SQL } = globalThis.Bun;
|
|
11763
11764
|
import { Database } from "bun:sqlite";
|
|
11764
|
-
import process19 from "process";
|
|
11765
11765
|
|
|
11766
11766
|
class SQLiteWrapper {
|
|
11767
11767
|
db;
|
|
@@ -11993,6 +11993,21 @@ function createConnectionString(dialect, dbConfig) {
|
|
|
11993
11993
|
throw new Error(`Unsupported dialect: ${dialect}`);
|
|
11994
11994
|
}
|
|
11995
11995
|
}
|
|
11996
|
+
function resolvePoolOptions(pool) {
|
|
11997
|
+
if (!pool)
|
|
11998
|
+
return {};
|
|
11999
|
+
const out = {};
|
|
12000
|
+
const toSeconds = (ms) => Math.max(0, Math.round(ms / 1000));
|
|
12001
|
+
if (typeof pool.max === "number" && Number.isFinite(pool.max))
|
|
12002
|
+
out.max = pool.max;
|
|
12003
|
+
if (typeof pool.idleTimeoutMs === "number" && Number.isFinite(pool.idleTimeoutMs))
|
|
12004
|
+
out.idleTimeout = toSeconds(pool.idleTimeoutMs);
|
|
12005
|
+
if (typeof pool.acquireTimeoutMs === "number" && Number.isFinite(pool.acquireTimeoutMs))
|
|
12006
|
+
out.connectionTimeout = toSeconds(pool.acquireTimeoutMs);
|
|
12007
|
+
if (typeof pool.maxLifetimeMs === "number" && Number.isFinite(pool.maxLifetimeMs))
|
|
12008
|
+
out.maxLifetime = toSeconds(pool.maxLifetimeMs);
|
|
12009
|
+
return out;
|
|
12010
|
+
}
|
|
11996
12011
|
function getBunSql() {
|
|
11997
12012
|
const dialect = config5.dialect;
|
|
11998
12013
|
const connectionString = createConnectionString(dialect, config5.database);
|
|
@@ -12000,14 +12015,8 @@ function getBunSql() {
|
|
|
12000
12015
|
if (dialect === "sqlite") {
|
|
12001
12016
|
return createSQLiteSQL(connectionString);
|
|
12002
12017
|
}
|
|
12003
|
-
const
|
|
12004
|
-
|
|
12005
|
-
sql.catch((error) => {
|
|
12006
|
-
if (config5.verbose && !error.message.includes("database") && !error.message.includes("does not exist")) {
|
|
12007
|
-
console.warn(`[query-builder] Database connection error: ${error.message}`);
|
|
12008
|
-
}
|
|
12009
|
-
});
|
|
12010
|
-
}
|
|
12018
|
+
const poolOptions = resolvePoolOptions(config5.database.pool);
|
|
12019
|
+
const sql = Object.keys(poolOptions).length > 0 ? new SQL(connectionString, poolOptions) : new SQL(connectionString);
|
|
12011
12020
|
return sql;
|
|
12012
12021
|
} catch (error) {
|
|
12013
12022
|
console.error(`[query-builder] Failed to create database connection for dialect '${dialect}': ${error.message}`);
|
|
@@ -12017,19 +12026,31 @@ function getBunSql() {
|
|
|
12017
12026
|
throw error;
|
|
12018
12027
|
}
|
|
12019
12028
|
}
|
|
12029
|
+
function connectionSignature() {
|
|
12030
|
+
const d = config5.database;
|
|
12031
|
+
return JSON.stringify({
|
|
12032
|
+
dialect: config5.dialect,
|
|
12033
|
+
database: d.database,
|
|
12034
|
+
username: d.username,
|
|
12035
|
+
password: d.password,
|
|
12036
|
+
host: d.host,
|
|
12037
|
+
port: d.port,
|
|
12038
|
+
url: d.url,
|
|
12039
|
+
pool: resolvePoolOptions(d.pool)
|
|
12040
|
+
});
|
|
12041
|
+
}
|
|
12020
12042
|
function getOrCreateBunSql(forceNew = false) {
|
|
12021
|
-
const
|
|
12043
|
+
const signature = connectionSignature();
|
|
12044
|
+
const configChanged = _bunSqlInstance !== null && _currentSignature !== signature;
|
|
12022
12045
|
if (forceNew || configChanged || !_bunSqlInstance) {
|
|
12023
12046
|
_bunSqlInstance = getBunSql();
|
|
12024
|
-
|
|
12025
|
-
_currentDatabase = config5.database.database;
|
|
12047
|
+
_currentSignature = signature;
|
|
12026
12048
|
}
|
|
12027
12049
|
return _bunSqlInstance;
|
|
12028
12050
|
}
|
|
12029
12051
|
function resetConnection() {
|
|
12030
12052
|
_bunSqlInstance = null;
|
|
12031
|
-
|
|
12032
|
-
_currentDatabase = null;
|
|
12053
|
+
_currentSignature = null;
|
|
12033
12054
|
}
|
|
12034
12055
|
async function withFreshConnection(fn) {
|
|
12035
12056
|
try {
|
|
@@ -12044,7 +12065,7 @@ async function withFreshConnection(fn) {
|
|
|
12044
12065
|
}
|
|
12045
12066
|
}
|
|
12046
12067
|
function createLazyBunSql() {
|
|
12047
|
-
return new Proxy({}, {
|
|
12068
|
+
return new Proxy(function lazyBunSql() {}, {
|
|
12048
12069
|
get(_target, prop) {
|
|
12049
12070
|
const sql = getOrCreateBunSql();
|
|
12050
12071
|
const value = sql[prop];
|
|
@@ -12059,20 +12080,10 @@ function createLazyBunSql() {
|
|
|
12059
12080
|
}
|
|
12060
12081
|
});
|
|
12061
12082
|
}
|
|
12062
|
-
var _bunSqlInstance = null,
|
|
12083
|
+
var _bunSqlInstance = null, _currentSignature = null, bunSql;
|
|
12063
12084
|
var init_db = __esm(() => {
|
|
12064
12085
|
init_config();
|
|
12065
12086
|
bunSql = createLazyBunSql();
|
|
12066
|
-
if (typeof process19 !== "undefined" && process19.on) {
|
|
12067
|
-
const existingHandler = process19.listeners("unhandledRejection").find((h) => h.name === "sqlConnectionErrorHandler");
|
|
12068
|
-
if (!existingHandler) {
|
|
12069
|
-
let sqlConnectionErrorHandler = function(reason) {
|
|
12070
|
-
if (reason && (reason.message?.includes("database") || reason.message?.includes("does not exist") || reason.code === "ERR_POSTGRES_SERVER_ERROR" || reason.code === "3D000")) {}
|
|
12071
|
-
};
|
|
12072
|
-
Object.defineProperty(sqlConnectionErrorHandler, "name", { value: "sqlConnectionErrorHandler" });
|
|
12073
|
-
process19.on("unhandledRejection", sqlConnectionErrorHandler);
|
|
12074
|
-
}
|
|
12075
|
-
}
|
|
12076
12087
|
});
|
|
12077
12088
|
|
|
12078
12089
|
// src/actions/benchmark.ts
|
|
@@ -12322,6 +12333,47 @@ function* iterateAllPivots(meta, options = {}) {
|
|
|
12322
12333
|
function isRawExpression(expr) {
|
|
12323
12334
|
return typeof expr === "object" && expr !== null && "raw" in expr && typeof expr.raw === "string";
|
|
12324
12335
|
}
|
|
12336
|
+
function quoteInsertIdent(id) {
|
|
12337
|
+
return config5.dialect === "mysql" ? `\`${id.replace(/`/g, "``")}\`` : `"${id.replace(/"/g, '""')}"`;
|
|
12338
|
+
}
|
|
12339
|
+
function buildInsertClause(rows, startIndex = 1) {
|
|
12340
|
+
const cols = Object.keys(rows[0] ?? {});
|
|
12341
|
+
const params = [];
|
|
12342
|
+
let idx = startIndex;
|
|
12343
|
+
const tuples = rows.map((row) => {
|
|
12344
|
+
const phs = cols.map((c) => {
|
|
12345
|
+
params.push(row[c]);
|
|
12346
|
+
return getPlaceholder(idx++);
|
|
12347
|
+
});
|
|
12348
|
+
return `(${phs.join(", ")})`;
|
|
12349
|
+
});
|
|
12350
|
+
return {
|
|
12351
|
+
colsSql: cols.map(quoteInsertIdent).join(", "),
|
|
12352
|
+
valuesSql: tuples.join(", "),
|
|
12353
|
+
params,
|
|
12354
|
+
nextIndex: idx
|
|
12355
|
+
};
|
|
12356
|
+
}
|
|
12357
|
+
function hasSlowQueryHook(h) {
|
|
12358
|
+
return Boolean(h && (h.onSlowQuery || h.slowQueryThresholdMs != null && h.slowQueryThresholdMs >= 0));
|
|
12359
|
+
}
|
|
12360
|
+
function renderSelectColumn(col) {
|
|
12361
|
+
if (typeof col === "string")
|
|
12362
|
+
return col;
|
|
12363
|
+
if (isRawExpression(col))
|
|
12364
|
+
return col.raw;
|
|
12365
|
+
if (col && typeof col === "object") {
|
|
12366
|
+
const anyCol = col;
|
|
12367
|
+
if (typeof anyCol.raw === "function")
|
|
12368
|
+
return String(anyCol.raw());
|
|
12369
|
+
if (typeof anyCol.sql === "string")
|
|
12370
|
+
return anyCol.sql;
|
|
12371
|
+
const str = String(col);
|
|
12372
|
+
if (str !== "[object Object]")
|
|
12373
|
+
return str;
|
|
12374
|
+
}
|
|
12375
|
+
throw new TypeError(`[query-builder] select(): unsupported column ${String(col)} \u2014 pass a column name, a string[], or a SQL fragment (e.g. sql\`count(*) as c\`)`);
|
|
12376
|
+
}
|
|
12325
12377
|
function validateIdentifier(name, context) {
|
|
12326
12378
|
if (!SQL_PATTERNS.IDENTIFIER.test(name)) {
|
|
12327
12379
|
const contextMsg = context ? ` in ${context}` : "";
|
|
@@ -12536,20 +12588,35 @@ function createQueryBuilder(state) {
|
|
|
12536
12588
|
config5.debug.captureText = prev;
|
|
12537
12589
|
return s;
|
|
12538
12590
|
}
|
|
12591
|
+
function computeParams(q) {
|
|
12592
|
+
if (!q || typeof q !== "object")
|
|
12593
|
+
return;
|
|
12594
|
+
if (Array.isArray(q.values))
|
|
12595
|
+
return q.values;
|
|
12596
|
+
if (Array.isArray(q.parameters))
|
|
12597
|
+
return q.parameters;
|
|
12598
|
+
if (Array.isArray(q.params))
|
|
12599
|
+
return q.params;
|
|
12600
|
+
return;
|
|
12601
|
+
}
|
|
12539
12602
|
function runWithHooks(q, kind, opts) {
|
|
12540
12603
|
const hooks = config5.hooks;
|
|
12541
|
-
const
|
|
12604
|
+
const slowMs = hooks?.slowQueryThresholdMs;
|
|
12605
|
+
const slowEnabled = slowMs != null && slowMs >= 0;
|
|
12606
|
+
const hasSlowQuery = Boolean(hooks?.onSlowQuery || slowEnabled);
|
|
12607
|
+
const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hasSlowQuery);
|
|
12542
12608
|
const hasTimeoutOrSignal = opts?.timeoutMs && opts.timeoutMs > 0 || opts?.signal;
|
|
12543
12609
|
if (!hasHooks && !hasTimeoutOrSignal) {
|
|
12544
12610
|
return q.execute();
|
|
12545
12611
|
}
|
|
12546
12612
|
const text = computeSqlText(q);
|
|
12613
|
+
const params = computeParams(q);
|
|
12547
12614
|
const startAt = Date.now();
|
|
12548
12615
|
let span;
|
|
12549
12616
|
try {
|
|
12550
|
-
hooks?.onQueryStart?.({ sql: text, kind });
|
|
12617
|
+
hooks?.onQueryStart?.({ sql: text, params, kind });
|
|
12551
12618
|
if (hooks?.startSpan)
|
|
12552
|
-
span = hooks.startSpan({ sql: text, kind });
|
|
12619
|
+
span = hooks.startSpan({ sql: text, params, kind });
|
|
12553
12620
|
} catch {}
|
|
12554
12621
|
let finished = false;
|
|
12555
12622
|
const finish = (err, rowCount) => {
|
|
@@ -12559,9 +12626,15 @@ function createQueryBuilder(state) {
|
|
|
12559
12626
|
const durationMs = Date.now() - startAt;
|
|
12560
12627
|
try {
|
|
12561
12628
|
if (err) {
|
|
12562
|
-
hooks?.onQueryError?.({ sql: text, error: err, durationMs, kind });
|
|
12629
|
+
hooks?.onQueryError?.({ sql: text, params, error: err, durationMs, kind });
|
|
12563
12630
|
} else {
|
|
12564
|
-
hooks?.onQueryEnd?.({ sql: text, durationMs, rowCount, kind });
|
|
12631
|
+
hooks?.onQueryEnd?.({ sql: text, params, durationMs, rowCount, kind });
|
|
12632
|
+
if (slowEnabled && durationMs >= slowMs) {
|
|
12633
|
+
if (hooks?.onSlowQuery)
|
|
12634
|
+
hooks.onSlowQuery({ sql: text, params, durationMs, kind });
|
|
12635
|
+
else
|
|
12636
|
+
console.warn(`[query-builder] slow query (${durationMs}ms >= ${slowMs}ms): ${text}`);
|
|
12637
|
+
}
|
|
12565
12638
|
}
|
|
12566
12639
|
} catch {}
|
|
12567
12640
|
try {
|
|
@@ -12641,6 +12714,36 @@ function createQueryBuilder(state) {
|
|
|
12641
12714
|
const p = hasWhere ? prefix : "WHERE";
|
|
12642
12715
|
text = `${text} ${p} ${clause}`;
|
|
12643
12716
|
};
|
|
12717
|
+
const appendSetOp = (op, other) => {
|
|
12718
|
+
const st = other.__rawState?.();
|
|
12719
|
+
if (st) {
|
|
12720
|
+
const offset = whereParams.length;
|
|
12721
|
+
const otherSql = config5.dialect === "postgres" ? st.sql.replace(/\$(\d+)/g, (_m, n) => `$${Number(n) + offset}`) : st.sql;
|
|
12722
|
+
text += ` ${op} ${otherSql}`;
|
|
12723
|
+
whereParams.push(...st.params);
|
|
12724
|
+
} else {
|
|
12725
|
+
text += ` ${op} ${String(other.toSQL())}`;
|
|
12726
|
+
}
|
|
12727
|
+
built = null;
|
|
12728
|
+
};
|
|
12729
|
+
const insertJoin = (joinClause) => {
|
|
12730
|
+
const re = /\(|\)|\b(?:WHERE|GROUP BY|HAVING|ORDER BY|LIMIT|OFFSET|UNION)\b/gi;
|
|
12731
|
+
let depth = 0;
|
|
12732
|
+
let cut = -1;
|
|
12733
|
+
let mm;
|
|
12734
|
+
while (mm = re.exec(text)) {
|
|
12735
|
+
if (mm[0] === "(") {
|
|
12736
|
+
depth++;
|
|
12737
|
+
} else if (mm[0] === ")") {
|
|
12738
|
+
depth = Math.max(0, depth - 1);
|
|
12739
|
+
} else if (depth === 0) {
|
|
12740
|
+
cut = mm.index;
|
|
12741
|
+
break;
|
|
12742
|
+
}
|
|
12743
|
+
}
|
|
12744
|
+
text = cut >= 0 ? `${text.slice(0, cut)}${joinClause} ${text.slice(cut)}` : `${text} ${joinClause}`;
|
|
12745
|
+
built = null;
|
|
12746
|
+
};
|
|
12644
12747
|
const joinedTables = new Set;
|
|
12645
12748
|
let timeoutMs;
|
|
12646
12749
|
let abortSignal;
|
|
@@ -12725,6 +12828,18 @@ function createQueryBuilder(state) {
|
|
|
12725
12828
|
}
|
|
12726
12829
|
}
|
|
12727
12830
|
};
|
|
12831
|
+
const buildOverClause = (partitionBy, orderBy) => {
|
|
12832
|
+
const cols = Array.isArray(partitionBy) ? partitionBy : partitionBy ? [partitionBy] : [];
|
|
12833
|
+
const parts = [];
|
|
12834
|
+
if (cols.length)
|
|
12835
|
+
parts.push(`PARTITION BY ${cols.join(", ")}`);
|
|
12836
|
+
if (orderBy && orderBy.length)
|
|
12837
|
+
parts.push(`ORDER BY ${orderBy.map(([c, d]) => `${c} ${d === "desc" ? "DESC" : "ASC"}`).join(", ")}`);
|
|
12838
|
+
return parts.length ? `OVER (${parts.join(" ")})` : "OVER ()";
|
|
12839
|
+
};
|
|
12840
|
+
const addWindowFunction = (fnExpr, alias, partitionBy, orderBy) => {
|
|
12841
|
+
addToSelectClause(`${fnExpr} ${buildOverClause(partitionBy, orderBy)} AS ${alias}`);
|
|
12842
|
+
};
|
|
12728
12843
|
function assertSafeWhereOperator(op, context) {
|
|
12729
12844
|
if (typeof op !== "string")
|
|
12730
12845
|
throw new TypeError(`[query-builder] ${context}: operator must be a string, got ${typeof op}`);
|
|
@@ -12864,6 +12979,46 @@ function createQueryBuilder(state) {
|
|
|
12864
12979
|
validateIdentifier(fkA, "withCount (foreign key)");
|
|
12865
12980
|
return `(SELECT COUNT(*) FROM ${pivot} WHERE ${pivot}.${fkA} = ${parentTable}.${pk})`;
|
|
12866
12981
|
};
|
|
12982
|
+
const applyRelationAggregate = (fn, relation, column) => {
|
|
12983
|
+
if (!meta)
|
|
12984
|
+
return;
|
|
12985
|
+
validateIdentifier(column, `with${fn[0]}${fn.slice(1).toLowerCase()} (column)`);
|
|
12986
|
+
const parentTable = String(table);
|
|
12987
|
+
const rels = meta.relations?.[parentTable];
|
|
12988
|
+
if (!rels)
|
|
12989
|
+
return;
|
|
12990
|
+
const found = Object.entries(rels).find(([_t, relMap2]) => relMap2 && typeof relMap2 === "object" && (relation in relMap2));
|
|
12991
|
+
if (!found)
|
|
12992
|
+
return;
|
|
12993
|
+
const [type, relMap] = found;
|
|
12994
|
+
const entry = relMap[relation];
|
|
12995
|
+
const targetModel = typeof entry === "string" ? entry : entry?.model || entry?.target || entry;
|
|
12996
|
+
const targetTable = meta.modelToTable[targetModel] || targetModel;
|
|
12997
|
+
const pk = meta.primaryKeys[parentTable] ?? "id";
|
|
12998
|
+
validateIdentifier(targetTable, `with${fn} (target table)`);
|
|
12999
|
+
const aggExpr = `${fn}(${targetTable}.${column})`;
|
|
13000
|
+
let sub;
|
|
13001
|
+
if (type === "hasMany" || type === "hasOne") {
|
|
13002
|
+
const fk = `${parentTable.endsWith("s") ? parentTable.slice(0, -1) : parentTable}_id`;
|
|
13003
|
+
validateIdentifier(fk, `with${fn} (foreign key)`);
|
|
13004
|
+
sub = `(SELECT ${aggExpr} FROM ${targetTable} WHERE ${targetTable}.${fk} = ${parentTable}.${pk})`;
|
|
13005
|
+
} else if (type === "belongsToMany") {
|
|
13006
|
+
const resolved = meta ? resolvePivot(meta, parentTable, relation, { singularize, models: meta.models }) : null;
|
|
13007
|
+
const a = singularize(parentTable);
|
|
13008
|
+
const b = singularize(targetTable);
|
|
13009
|
+
const pivot = resolved?.pivotTable ?? [a, b].sort().join("_");
|
|
13010
|
+
const fkA = resolved?.fkParent ?? `${a}_id`;
|
|
13011
|
+
const fkB = resolved?.fkRelated ?? `${b}_id`;
|
|
13012
|
+
const targetPk = meta.primaryKeys[targetTable] ?? "id";
|
|
13013
|
+
validateIdentifier(pivot, `with${fn} (pivot table)`);
|
|
13014
|
+
validateIdentifier(fkA, `with${fn} (foreign key)`);
|
|
13015
|
+
validateIdentifier(fkB, `with${fn} (related key)`);
|
|
13016
|
+
sub = `(SELECT ${aggExpr} FROM ${pivot} JOIN ${targetTable} ON ${targetTable}.${targetPk} = ${pivot}.${fkB} WHERE ${pivot}.${fkA} = ${parentTable}.${pk})`;
|
|
13017
|
+
} else {
|
|
13018
|
+
return;
|
|
13019
|
+
}
|
|
13020
|
+
addToSelectClause(`${sub} AS ${relation}_${fn.toLowerCase()}_${column}`);
|
|
13021
|
+
};
|
|
12867
13022
|
const applyPivotColumnsToQuery = () => {
|
|
12868
13023
|
if (pivotColumns.size === 0)
|
|
12869
13024
|
return;
|
|
@@ -12958,6 +13113,52 @@ function createQueryBuilder(state) {
|
|
|
12958
13113
|
built = null;
|
|
12959
13114
|
return this;
|
|
12960
13115
|
},
|
|
13116
|
+
over(expression, alias, opts = {}) {
|
|
13117
|
+
addWindowFunction(expression, alias, opts.partitionBy, opts.orderBy);
|
|
13118
|
+
return this;
|
|
13119
|
+
},
|
|
13120
|
+
lag(column, opts = {}) {
|
|
13121
|
+
const args = [column, String(opts.offset ?? 1)];
|
|
13122
|
+
if (opts.defaultValue !== undefined)
|
|
13123
|
+
args.push(String(opts.defaultValue));
|
|
13124
|
+
addWindowFunction(`LAG(${args.join(", ")})`, opts.alias ?? `${column}_lag`, opts.partitionBy, opts.orderBy);
|
|
13125
|
+
return this;
|
|
13126
|
+
},
|
|
13127
|
+
lead(column, opts = {}) {
|
|
13128
|
+
const args = [column, String(opts.offset ?? 1)];
|
|
13129
|
+
if (opts.defaultValue !== undefined)
|
|
13130
|
+
args.push(String(opts.defaultValue));
|
|
13131
|
+
addWindowFunction(`LEAD(${args.join(", ")})`, opts.alias ?? `${column}_lead`, opts.partitionBy, opts.orderBy);
|
|
13132
|
+
return this;
|
|
13133
|
+
},
|
|
13134
|
+
sumOver(column, opts = {}) {
|
|
13135
|
+
addWindowFunction(`SUM(${column})`, opts.alias ?? `${column}_sum`, opts.partitionBy, opts.orderBy);
|
|
13136
|
+
return this;
|
|
13137
|
+
},
|
|
13138
|
+
avgOver(column, opts = {}) {
|
|
13139
|
+
addWindowFunction(`AVG(${column})`, opts.alias ?? `${column}_avg`, opts.partitionBy, opts.orderBy);
|
|
13140
|
+
return this;
|
|
13141
|
+
},
|
|
13142
|
+
countOver(column = "*", opts = {}) {
|
|
13143
|
+
addWindowFunction(`COUNT(${column})`, opts.alias ?? "count_over", opts.partitionBy, opts.orderBy);
|
|
13144
|
+
return this;
|
|
13145
|
+
},
|
|
13146
|
+
minOver(column, opts = {}) {
|
|
13147
|
+
addWindowFunction(`MIN(${column})`, opts.alias ?? `${column}_min`, opts.partitionBy, opts.orderBy);
|
|
13148
|
+
return this;
|
|
13149
|
+
},
|
|
13150
|
+
maxOver(column, opts = {}) {
|
|
13151
|
+
addWindowFunction(`MAX(${column})`, opts.alias ?? `${column}_max`, opts.partitionBy, opts.orderBy);
|
|
13152
|
+
return this;
|
|
13153
|
+
},
|
|
13154
|
+
firstValue(column, opts = {}) {
|
|
13155
|
+
addWindowFunction(`FIRST_VALUE(${column})`, opts.alias ?? `${column}_first`, opts.partitionBy, opts.orderBy);
|
|
13156
|
+
return this;
|
|
13157
|
+
},
|
|
13158
|
+
lastValue(column, opts = {}) {
|
|
13159
|
+
addWindowFunction(`LAST_VALUE(${column})`, opts.alias ?? `${column}_last`, opts.partitionBy, opts.orderBy);
|
|
13160
|
+
return this;
|
|
13161
|
+
},
|
|
12961
13162
|
selectAll() {
|
|
12962
13163
|
return this;
|
|
12963
13164
|
},
|
|
@@ -12967,22 +13168,24 @@ function createQueryBuilder(state) {
|
|
|
12967
13168
|
const cols = Array.isArray(columns2) ? columns2 : [columns2];
|
|
12968
13169
|
if (cols.length === 0)
|
|
12969
13170
|
return this;
|
|
13171
|
+
const rendered = cols.map(renderSelectColumn);
|
|
12970
13172
|
const fromIndex = text.indexOf(" FROM ");
|
|
12971
13173
|
if (fromIndex !== -1) {
|
|
12972
|
-
text = `SELECT ${
|
|
13174
|
+
text = `SELECT ${rendered.join(", ")}${text.substring(fromIndex)}`;
|
|
12973
13175
|
} else {
|
|
12974
|
-
text = `SELECT ${
|
|
13176
|
+
text = `SELECT ${rendered.join(", ")} FROM ${table}`;
|
|
12975
13177
|
}
|
|
12976
13178
|
return this;
|
|
12977
13179
|
},
|
|
12978
13180
|
addSelect(...columns2) {
|
|
12979
13181
|
if (!columns2.length)
|
|
12980
13182
|
return this;
|
|
13183
|
+
const rendered = columns2.map(renderSelectColumn);
|
|
12981
13184
|
const fromIdx = text.indexOf(" FROM ");
|
|
12982
13185
|
if (fromIdx !== -1) {
|
|
12983
|
-
text = `${text.substring(0, fromIdx)}, ${
|
|
13186
|
+
text = `${text.substring(0, fromIdx)}, ${rendered.join(", ")}${text.substring(fromIdx)}`;
|
|
12984
13187
|
} else {
|
|
12985
|
-
text += `, ${
|
|
13188
|
+
text += `, ${rendered.join(", ")}`;
|
|
12986
13189
|
}
|
|
12987
13190
|
built = null;
|
|
12988
13191
|
return this;
|
|
@@ -13399,6 +13602,22 @@ function createQueryBuilder(state) {
|
|
|
13399
13602
|
}
|
|
13400
13603
|
return this;
|
|
13401
13604
|
},
|
|
13605
|
+
withSum(relation, column) {
|
|
13606
|
+
applyRelationAggregate("SUM", relation, column);
|
|
13607
|
+
return this;
|
|
13608
|
+
},
|
|
13609
|
+
withAvg(relation, column) {
|
|
13610
|
+
applyRelationAggregate("AVG", relation, column);
|
|
13611
|
+
return this;
|
|
13612
|
+
},
|
|
13613
|
+
withMax(relation, column) {
|
|
13614
|
+
applyRelationAggregate("MAX", relation, column);
|
|
13615
|
+
return this;
|
|
13616
|
+
},
|
|
13617
|
+
withMin(relation, column) {
|
|
13618
|
+
applyRelationAggregate("MIN", relation, column);
|
|
13619
|
+
return this;
|
|
13620
|
+
},
|
|
13402
13621
|
applyPivotColumns() {
|
|
13403
13622
|
applyPivotColumnsToQuery();
|
|
13404
13623
|
return this;
|
|
@@ -13610,8 +13829,9 @@ function createQueryBuilder(state) {
|
|
|
13610
13829
|
},
|
|
13611
13830
|
whereBetween(column, start, end) {
|
|
13612
13831
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
13832
|
+
const i = whereParams.length + 1;
|
|
13833
|
+
text = `${text} ${keyword} ${String(column)} BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`;
|
|
13613
13834
|
whereParams.push(start, end);
|
|
13614
|
-
text = `${text} ${keyword} ${String(column)} BETWEEN ? AND ?`;
|
|
13615
13835
|
built = null;
|
|
13616
13836
|
return this;
|
|
13617
13837
|
},
|
|
@@ -13623,9 +13843,30 @@ function createQueryBuilder(state) {
|
|
|
13623
13843
|
},
|
|
13624
13844
|
whereJsonContains(column, json) {
|
|
13625
13845
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
13846
|
+
const dialect = config5.dialect;
|
|
13626
13847
|
const idx = whereParams.length + 1;
|
|
13627
|
-
|
|
13628
|
-
|
|
13848
|
+
if (dialect === "postgres") {
|
|
13849
|
+
if (config5.sql?.jsonContainsMode === "function")
|
|
13850
|
+
text += ` ${keyword} jsonb_contains(${column}, ${getPlaceholder(idx)})`;
|
|
13851
|
+
else
|
|
13852
|
+
text += ` ${keyword} ${column} @> ${getPlaceholder(idx)}`;
|
|
13853
|
+
whereParams.push(JSON.stringify(json));
|
|
13854
|
+
} else if (dialect === "mysql") {
|
|
13855
|
+
text += ` ${keyword} JSON_CONTAINS(${column}, ${getPlaceholder(idx)})`;
|
|
13856
|
+
whereParams.push(JSON.stringify(json));
|
|
13857
|
+
} else {
|
|
13858
|
+
if (Array.isArray(json)) {
|
|
13859
|
+
const conds = json.map((_, i) => `EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx + i)})`);
|
|
13860
|
+
text += ` ${keyword} (${conds.join(" AND ")})`;
|
|
13861
|
+
for (const v of json)
|
|
13862
|
+
whereParams.push(v);
|
|
13863
|
+
} else if (json !== null && typeof json === "object") {
|
|
13864
|
+
throw new Error("[query-builder] whereJsonContains: object containment is not supported on SQLite \u2014 pass a scalar or array, or use whereJsonPath.");
|
|
13865
|
+
} else {
|
|
13866
|
+
text += ` ${keyword} EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx)})`;
|
|
13867
|
+
whereParams.push(json);
|
|
13868
|
+
}
|
|
13869
|
+
}
|
|
13629
13870
|
built = null;
|
|
13630
13871
|
return this;
|
|
13631
13872
|
},
|
|
@@ -13652,69 +13893,85 @@ function createQueryBuilder(state) {
|
|
|
13652
13893
|
whereLike(column, pattern, caseSensitive = false) {
|
|
13653
13894
|
const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13654
13895
|
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
13655
|
-
|
|
13896
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13897
|
+
addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13898
|
+
whereParams.push(pattern);
|
|
13656
13899
|
return this;
|
|
13657
13900
|
},
|
|
13658
13901
|
whereILike(column, pattern) {
|
|
13902
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13659
13903
|
if (config5.dialect === "postgres") {
|
|
13660
13904
|
built = sql`${ensureBuilt()} WHERE ${sql(String(column))} ILIKE ${pattern}`;
|
|
13661
|
-
addWhereText("WHERE", `${String(column)} ILIKE
|
|
13905
|
+
addWhereText("WHERE", `${String(column)} ILIKE ${ph}`);
|
|
13662
13906
|
} else {
|
|
13663
13907
|
const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13664
13908
|
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
13665
|
-
addWhereText("WHERE", `LOWER(${String(column)}) LIKE LOWER(
|
|
13909
|
+
addWhereText("WHERE", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
|
|
13666
13910
|
}
|
|
13911
|
+
whereParams.push(pattern);
|
|
13667
13912
|
return this;
|
|
13668
13913
|
},
|
|
13669
13914
|
orWhereLike(column, pattern, caseSensitive = false) {
|
|
13670
13915
|
const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13671
13916
|
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
13672
|
-
|
|
13917
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13918
|
+
addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13919
|
+
whereParams.push(pattern);
|
|
13673
13920
|
return this;
|
|
13674
13921
|
},
|
|
13675
13922
|
orWhereILike(column, pattern) {
|
|
13923
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13676
13924
|
if (config5.dialect === "postgres") {
|
|
13677
13925
|
built = sql`${ensureBuilt()} OR ${sql(String(column))} ILIKE ${pattern}`;
|
|
13678
|
-
addWhereText("OR", `${String(column)} ILIKE
|
|
13926
|
+
addWhereText("OR", `${String(column)} ILIKE ${ph}`);
|
|
13679
13927
|
} else {
|
|
13680
13928
|
const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
|
|
13681
13929
|
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
13682
|
-
addWhereText("OR", `LOWER(${String(column)}) LIKE LOWER(
|
|
13930
|
+
addWhereText("OR", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
|
|
13683
13931
|
}
|
|
13932
|
+
whereParams.push(pattern);
|
|
13684
13933
|
return this;
|
|
13685
13934
|
},
|
|
13686
13935
|
whereNotLike(column, pattern, caseSensitive = false) {
|
|
13687
13936
|
const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
13688
13937
|
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
13689
|
-
|
|
13938
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13939
|
+
addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13940
|
+
whereParams.push(pattern);
|
|
13690
13941
|
return this;
|
|
13691
13942
|
},
|
|
13692
13943
|
whereNotILike(column, pattern) {
|
|
13944
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13693
13945
|
if (config5.dialect === "postgres") {
|
|
13694
13946
|
built = sql`${ensureBuilt()} WHERE ${sql(String(column))} NOT ILIKE ${pattern}`;
|
|
13695
|
-
addWhereText("WHERE", `${String(column)} NOT ILIKE
|
|
13947
|
+
addWhereText("WHERE", `${String(column)} NOT ILIKE ${ph}`);
|
|
13696
13948
|
} else {
|
|
13697
13949
|
const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
13698
13950
|
built = sql`${ensureBuilt()} WHERE ${expr}`;
|
|
13699
|
-
addWhereText("WHERE", `LOWER(${String(column)}) NOT LIKE LOWER(
|
|
13951
|
+
addWhereText("WHERE", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
|
|
13700
13952
|
}
|
|
13953
|
+
whereParams.push(pattern);
|
|
13701
13954
|
return this;
|
|
13702
13955
|
},
|
|
13703
13956
|
orWhereNotLike(column, pattern, caseSensitive = false) {
|
|
13704
13957
|
const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
13705
13958
|
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
13706
|
-
|
|
13959
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13960
|
+
addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
|
|
13961
|
+
whereParams.push(pattern);
|
|
13707
13962
|
return this;
|
|
13708
13963
|
},
|
|
13709
13964
|
orWhereNotILike(column, pattern) {
|
|
13965
|
+
const ph = getPlaceholder(whereParams.length + 1);
|
|
13710
13966
|
if (config5.dialect === "postgres") {
|
|
13711
13967
|
built = sql`${ensureBuilt()} OR ${sql(String(column))} NOT ILIKE ${pattern}`;
|
|
13712
|
-
addWhereText("OR", `${String(column)} NOT ILIKE
|
|
13968
|
+
addWhereText("OR", `${String(column)} NOT ILIKE ${ph}`);
|
|
13713
13969
|
} else {
|
|
13714
13970
|
const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
|
|
13715
13971
|
built = sql`${ensureBuilt()} OR ${expr}`;
|
|
13716
|
-
addWhereText("OR", `LOWER(${String(column)}) NOT LIKE LOWER(
|
|
13972
|
+
addWhereText("OR", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
|
|
13717
13973
|
}
|
|
13974
|
+
whereParams.push(pattern);
|
|
13718
13975
|
return this;
|
|
13719
13976
|
},
|
|
13720
13977
|
whereAny(cols, op, value) {
|
|
@@ -13755,7 +14012,8 @@ function createQueryBuilder(state) {
|
|
|
13755
14012
|
},
|
|
13756
14013
|
whereNotBetween(column, start, end) {
|
|
13757
14014
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
13758
|
-
|
|
14015
|
+
const i = whereParams.length + 1;
|
|
14016
|
+
text += ` ${keyword} ${column} NOT BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`;
|
|
13759
14017
|
whereParams.push(start, end);
|
|
13760
14018
|
built = null;
|
|
13761
14019
|
return this;
|
|
@@ -14023,7 +14281,7 @@ function createQueryBuilder(state) {
|
|
|
14023
14281
|
return this;
|
|
14024
14282
|
},
|
|
14025
14283
|
join(table2, onLeft, operator, onRight) {
|
|
14026
|
-
|
|
14284
|
+
insertJoin(`JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
|
|
14027
14285
|
joinedTables.add(table2);
|
|
14028
14286
|
return this;
|
|
14029
14287
|
},
|
|
@@ -14032,44 +14290,37 @@ function createQueryBuilder(state) {
|
|
|
14032
14290
|
validateQualifiedIdentifier(onLeft, "joinSub(onLeft)");
|
|
14033
14291
|
validateQualifiedIdentifier(onRight, "joinSub(onRight)");
|
|
14034
14292
|
assertSafeWhereOperator(operator, "joinSub(operator)");
|
|
14035
|
-
|
|
14036
|
-
built = null;
|
|
14293
|
+
insertJoin(`JOIN (${String(sub.toSQL())}) AS ${alias} ON ${onLeft} ${operator} ${onRight}`);
|
|
14037
14294
|
joinedTables.add(alias);
|
|
14038
14295
|
return this;
|
|
14039
14296
|
},
|
|
14040
14297
|
innerJoin(table2, onLeft, operator, onRight) {
|
|
14041
|
-
|
|
14042
|
-
built = null;
|
|
14298
|
+
insertJoin(`INNER JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
|
|
14043
14299
|
joinedTables.add(table2);
|
|
14044
14300
|
return this;
|
|
14045
14301
|
},
|
|
14046
14302
|
leftJoin(table2, onLeft, operator, onRight) {
|
|
14047
|
-
|
|
14048
|
-
built = null;
|
|
14303
|
+
insertJoin(`LEFT JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
|
|
14049
14304
|
joinedTables.add(table2);
|
|
14050
14305
|
return this;
|
|
14051
14306
|
},
|
|
14052
14307
|
leftJoinSub(sub, alias, onLeft, operator, onRight) {
|
|
14053
|
-
|
|
14054
|
-
built = null;
|
|
14308
|
+
insertJoin(`LEFT JOIN (${String(sub.toSQL())}) AS ${alias} ON ${onLeft} ${operator} ${onRight}`);
|
|
14055
14309
|
joinedTables.add(alias);
|
|
14056
14310
|
return this;
|
|
14057
14311
|
},
|
|
14058
14312
|
rightJoin(table2, onLeft, operator, onRight) {
|
|
14059
|
-
|
|
14060
|
-
built = null;
|
|
14313
|
+
insertJoin(`RIGHT JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
|
|
14061
14314
|
joinedTables.add(table2);
|
|
14062
14315
|
return this;
|
|
14063
14316
|
},
|
|
14064
14317
|
crossJoin(table2) {
|
|
14065
|
-
|
|
14066
|
-
built = null;
|
|
14318
|
+
insertJoin(`CROSS JOIN ${table2}`);
|
|
14067
14319
|
joinedTables.add(table2);
|
|
14068
14320
|
return this;
|
|
14069
14321
|
},
|
|
14070
14322
|
crossJoinSub(sub, alias) {
|
|
14071
|
-
|
|
14072
|
-
built = null;
|
|
14323
|
+
insertJoin(`CROSS JOIN (${String(sub.toSQL())}) AS ${alias}`);
|
|
14073
14324
|
joinedTables.add(alias);
|
|
14074
14325
|
return this;
|
|
14075
14326
|
},
|
|
@@ -14122,9 +14373,10 @@ function createQueryBuilder(state) {
|
|
|
14122
14373
|
return this;
|
|
14123
14374
|
},
|
|
14124
14375
|
having(expr) {
|
|
14376
|
+
const kw = /\bHAVING\b/i.test(text) ? "AND" : "HAVING";
|
|
14125
14377
|
if (Array.isArray(expr)) {
|
|
14126
14378
|
const paramIdx = whereParams.length + 1;
|
|
14127
|
-
text = `${text}
|
|
14379
|
+
text = `${text} ${kw} ${expr[0]} ${expr[1]} ${getPlaceholder(paramIdx)}`;
|
|
14128
14380
|
whereParams.push(expr[2]);
|
|
14129
14381
|
built = null;
|
|
14130
14382
|
} else if (expr && typeof expr === "object" && !("raw" in expr)) {
|
|
@@ -14138,18 +14390,19 @@ function createQueryBuilder(state) {
|
|
|
14138
14390
|
conditions[i] = `${key} = ${getPlaceholder(baseIdx + i + 1)}`;
|
|
14139
14391
|
whereParams.push(expr[key]);
|
|
14140
14392
|
}
|
|
14141
|
-
text = `${text}
|
|
14393
|
+
text = `${text} ${kw} ${conditions.join(" AND ")}`;
|
|
14142
14394
|
built = null;
|
|
14143
14395
|
}
|
|
14144
14396
|
} else if (expr && typeof expr.raw !== "undefined") {
|
|
14145
|
-
text += `
|
|
14397
|
+
text += ` ${kw} ${expr.raw}`;
|
|
14146
14398
|
built = null;
|
|
14147
14399
|
}
|
|
14148
14400
|
return this;
|
|
14149
14401
|
},
|
|
14150
14402
|
havingRaw(fragment) {
|
|
14151
14403
|
assertSqlFragment(fragment, "havingRaw(fragment)");
|
|
14152
|
-
text
|
|
14404
|
+
const kw = /\bHAVING\b/i.test(text) ? "AND" : "HAVING";
|
|
14405
|
+
text += ` ${kw} ${String(fragment)}`;
|
|
14153
14406
|
built = null;
|
|
14154
14407
|
return this;
|
|
14155
14408
|
},
|
|
@@ -14160,13 +14413,27 @@ function createQueryBuilder(state) {
|
|
|
14160
14413
|
return this;
|
|
14161
14414
|
},
|
|
14162
14415
|
union(other) {
|
|
14163
|
-
|
|
14164
|
-
built = null;
|
|
14416
|
+
appendSetOp("UNION", other);
|
|
14165
14417
|
return this;
|
|
14166
14418
|
},
|
|
14167
14419
|
unionAll(other) {
|
|
14168
|
-
|
|
14169
|
-
|
|
14420
|
+
appendSetOp("UNION ALL", other);
|
|
14421
|
+
return this;
|
|
14422
|
+
},
|
|
14423
|
+
intersect(other) {
|
|
14424
|
+
appendSetOp("INTERSECT", other);
|
|
14425
|
+
return this;
|
|
14426
|
+
},
|
|
14427
|
+
intersectAll(other) {
|
|
14428
|
+
appendSetOp("INTERSECT ALL", other);
|
|
14429
|
+
return this;
|
|
14430
|
+
},
|
|
14431
|
+
except(other) {
|
|
14432
|
+
appendSetOp("EXCEPT", other);
|
|
14433
|
+
return this;
|
|
14434
|
+
},
|
|
14435
|
+
exceptAll(other) {
|
|
14436
|
+
appendSetOp("EXCEPT ALL", other);
|
|
14170
14437
|
return this;
|
|
14171
14438
|
},
|
|
14172
14439
|
forPage(page, perPage) {
|
|
@@ -14210,11 +14477,22 @@ function createQueryBuilder(state) {
|
|
|
14210
14477
|
const e = await this.exists();
|
|
14211
14478
|
return !e;
|
|
14212
14479
|
},
|
|
14213
|
-
async paginate(perPage, page = 1) {
|
|
14480
|
+
async paginate(perPage, page = 1, opts = {}) {
|
|
14214
14481
|
if (!Number.isFinite(perPage) || perPage <= 0 || !Number.isInteger(perPage))
|
|
14215
14482
|
throw new TypeError(`[query-builder] paginate(perPage): expected positive integer, got ${perPage}`);
|
|
14216
14483
|
if (!Number.isFinite(page) || page < 1 || !Number.isInteger(page))
|
|
14217
14484
|
throw new TypeError(`[query-builder] paginate(page): expected integer >= 1, got ${page}`);
|
|
14485
|
+
if (opts.tx) {
|
|
14486
|
+
const baseSql = reorderSelectClauses(text);
|
|
14487
|
+
const baseParams = [...whereParams];
|
|
14488
|
+
const cRows2 = await opts.tx.unsafe(`SELECT COUNT(*) as c FROM (${baseSql}) as sub`, baseParams);
|
|
14489
|
+
const total2 = Number(cRows2?.[0]?.c ?? 0);
|
|
14490
|
+
const lastPage2 = Math.max(1, Math.ceil(total2 / perPage));
|
|
14491
|
+
const p2 = Math.max(1, Math.min(page, lastPage2));
|
|
14492
|
+
const offset2 = (p2 - 1) * perPage;
|
|
14493
|
+
const data2 = await opts.tx.unsafe(`${baseSql} LIMIT ${perPage} OFFSET ${offset2}`, baseParams);
|
|
14494
|
+
return { data: data2, meta: { perPage, page: p2, total: total2, lastPage: lastPage2 } };
|
|
14495
|
+
}
|
|
14218
14496
|
const countQ = sql`SELECT COUNT(*) as c FROM (${ensureBuilt()}) as sub`;
|
|
14219
14497
|
const cRows = await runWithHooks(countQ, "select", { signal: abortSignal, timeoutMs });
|
|
14220
14498
|
const [cRow] = cRows;
|
|
@@ -14371,7 +14649,7 @@ function createQueryBuilder(state) {
|
|
|
14371
14649
|
},
|
|
14372
14650
|
async get() {
|
|
14373
14651
|
const hooks = config5.hooks;
|
|
14374
|
-
const hasQueryHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan);
|
|
14652
|
+
const hasQueryHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hasSlowQueryHook(hooks));
|
|
14375
14653
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !hasQueryHooks) {
|
|
14376
14654
|
const prepareFn = _sql._prepareStatement;
|
|
14377
14655
|
if (prepareFn) {
|
|
@@ -14426,7 +14704,7 @@ function createQueryBuilder(state) {
|
|
|
14426
14704
|
},
|
|
14427
14705
|
async first() {
|
|
14428
14706
|
const fHooks = config5.hooks;
|
|
14429
|
-
const fHasQueryHooks = fHooks && (fHooks.onQueryStart || fHooks.onQueryEnd || fHooks.onQueryError || fHooks.startSpan);
|
|
14707
|
+
const fHasQueryHooks = fHooks && (fHooks.onQueryStart || fHooks.onQueryEnd || fHooks.onQueryError || fHooks.startSpan || hasSlowQueryHook(fHooks));
|
|
14430
14708
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !fHasQueryHooks) {
|
|
14431
14709
|
const prepareFn = _sql._prepareStatement;
|
|
14432
14710
|
if (prepareFn) {
|
|
@@ -14508,7 +14786,7 @@ function createQueryBuilder(state) {
|
|
|
14508
14786
|
countText = `SELECT COUNT(*) as c FROM ${table}`;
|
|
14509
14787
|
}
|
|
14510
14788
|
const cHooks = config5.hooks;
|
|
14511
|
-
const cHasHooks = cHooks && (cHooks.onQueryStart || cHooks.onQueryEnd || cHooks.onQueryError || cHooks.startSpan);
|
|
14789
|
+
const cHasHooks = cHooks && (cHooks.onQueryStart || cHooks.onQueryEnd || cHooks.onQueryError || cHooks.startSpan || hasSlowQueryHook(cHooks));
|
|
14512
14790
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !cHasHooks) {
|
|
14513
14791
|
const prepareFn = _sql._prepareStatement;
|
|
14514
14792
|
if (prepareFn) {
|
|
@@ -14526,7 +14804,7 @@ function createQueryBuilder(state) {
|
|
|
14526
14804
|
const fromIdx = text.indexOf(" FROM ");
|
|
14527
14805
|
const avgText = fromIdx !== -1 ? `SELECT AVG(${column}) as a${text.substring(fromIdx)}` : `SELECT AVG(${column}) as a FROM ${table}`;
|
|
14528
14806
|
const aHooks = config5.hooks;
|
|
14529
|
-
const aHasHooks = aHooks && (aHooks.onQueryStart || aHooks.onQueryEnd || aHooks.onQueryError || aHooks.startSpan);
|
|
14807
|
+
const aHasHooks = aHooks && (aHooks.onQueryStart || aHooks.onQueryEnd || aHooks.onQueryError || aHooks.startSpan || hasSlowQueryHook(aHooks));
|
|
14530
14808
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !aHasHooks) {
|
|
14531
14809
|
const prepareFn = _sql._prepareStatement;
|
|
14532
14810
|
if (prepareFn) {
|
|
@@ -14596,6 +14874,9 @@ function createQueryBuilder(state) {
|
|
|
14596
14874
|
toParams() {
|
|
14597
14875
|
return ensureBuilt().values?.() ?? [];
|
|
14598
14876
|
},
|
|
14877
|
+
__rawState() {
|
|
14878
|
+
return { sql: reorderSelectClauses(text), params: [...whereParams] };
|
|
14879
|
+
},
|
|
14599
14880
|
raw() {
|
|
14600
14881
|
return ensureBuilt().raw();
|
|
14601
14882
|
},
|
|
@@ -14954,15 +15235,15 @@ function createQueryBuilder(state) {
|
|
|
14954
15235
|
params.length = totalParams;
|
|
14955
15236
|
if (rowCount === 1) {
|
|
14956
15237
|
if (!isPostgres) {
|
|
14957
|
-
let cols = keys[0];
|
|
15238
|
+
let cols = quoteId(keys[0]);
|
|
14958
15239
|
let placeholders = "?";
|
|
14959
15240
|
params[0] = firstRow[keys[0]];
|
|
14960
15241
|
for (let c = 1;c < colCount; c++) {
|
|
14961
|
-
cols += `,${keys[c]}`;
|
|
15242
|
+
cols += `,${quoteId(keys[c])}`;
|
|
14962
15243
|
placeholders += ",?";
|
|
14963
15244
|
params[c] = firstRow[keys[c]];
|
|
14964
15245
|
}
|
|
14965
|
-
sqlText = `INSERT INTO ${table}(${cols})VALUES(${placeholders})`;
|
|
15246
|
+
sqlText = `INSERT INTO ${quoteId(table)}(${cols})VALUES(${placeholders})`;
|
|
14966
15247
|
} else {
|
|
14967
15248
|
const columnList = keys.map((k) => quoteId(k)).join(",");
|
|
14968
15249
|
sqlText = `INSERT INTO ${quoteId(table)}(${columnList})VALUES(`;
|
|
@@ -15020,7 +15301,7 @@ function createQueryBuilder(state) {
|
|
|
15020
15301
|
},
|
|
15021
15302
|
execute() {
|
|
15022
15303
|
const hooks = config5.hooks;
|
|
15023
|
-
const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hooks.beforeCreate || hooks.afterCreate);
|
|
15304
|
+
const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hooks.beforeCreate || hooks.afterCreate || hasSlowQueryHook(hooks));
|
|
15024
15305
|
if (!hasHooks) {
|
|
15025
15306
|
const prepareFn = _sql._prepareStatement;
|
|
15026
15307
|
if (prepareFn) {
|
|
@@ -15494,55 +15775,72 @@ function createQueryBuilder(state) {
|
|
|
15494
15775
|
};
|
|
15495
15776
|
},
|
|
15496
15777
|
async insertOrIgnore(table, values) {
|
|
15497
|
-
|
|
15498
|
-
|
|
15499
|
-
return
|
|
15500
|
-
}
|
|
15501
|
-
const
|
|
15502
|
-
|
|
15778
|
+
const rows = Array.isArray(values) ? values : [values];
|
|
15779
|
+
if (!rows.length)
|
|
15780
|
+
return;
|
|
15781
|
+
const { colsSql, valuesSql, params } = buildInsertClause(rows);
|
|
15782
|
+
const tbl = quoteInsertIdent(String(table));
|
|
15783
|
+
const sqlText = config5.dialect === "mysql" ? `INSERT IGNORE INTO ${tbl} (${colsSql}) VALUES ${valuesSql}` : `INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql} ON CONFLICT DO NOTHING`;
|
|
15784
|
+
return bunSql.unsafe(sqlText, params).execute();
|
|
15503
15785
|
},
|
|
15504
15786
|
async insertGetId(table, values, idColumn = "id") {
|
|
15787
|
+
const { colsSql, valuesSql, params } = buildInsertClause([values]);
|
|
15788
|
+
const tbl = quoteInsertIdent(String(table));
|
|
15505
15789
|
if (config5.dialect === "mysql") {
|
|
15506
|
-
|
|
15507
|
-
const
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
const
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
const
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
}
|
|
15790
|
+
await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
15791
|
+
const [row2] = await bunSql.unsafe(`SELECT LAST_INSERT_ID() as id`).execute();
|
|
15792
|
+
return row2?.id;
|
|
15793
|
+
}
|
|
15794
|
+
if (config5.dialect === "sqlite") {
|
|
15795
|
+
const res = await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
15796
|
+
if (res?.lastInsertRowid != null)
|
|
15797
|
+
return res.lastInsertRowid;
|
|
15798
|
+
const [row2] = await bunSql.unsafe(`SELECT last_insert_rowid() as id`).execute();
|
|
15799
|
+
return row2?.id;
|
|
15800
|
+
}
|
|
15801
|
+
const [row] = await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql} RETURNING ${quoteInsertIdent(String(idColumn))} as id`, params).execute();
|
|
15802
|
+
return row?.id;
|
|
15518
15803
|
},
|
|
15519
15804
|
async updateOrInsert(table, match, values) {
|
|
15520
|
-
const
|
|
15521
|
-
const
|
|
15522
|
-
|
|
15805
|
+
const tbl = quoteInsertIdent(String(table));
|
|
15806
|
+
const matchKeys = Object.keys(match);
|
|
15807
|
+
let idx = 1;
|
|
15808
|
+
const whereSql = matchKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(idx++)}`).join(" AND ");
|
|
15809
|
+
const whereParams = matchKeys.map((k) => match[k]);
|
|
15810
|
+
const existsRows = await bunSql.unsafe(`SELECT 1 FROM ${tbl} WHERE ${whereSql} LIMIT 1`, whereParams).execute();
|
|
15523
15811
|
if (existsRows.length) {
|
|
15524
|
-
const
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
const
|
|
15529
|
-
await
|
|
15812
|
+
const setKeys = Object.keys(values);
|
|
15813
|
+
let i = 1;
|
|
15814
|
+
const setSql = setKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(i++)}`).join(", ");
|
|
15815
|
+
const whereSql2 = matchKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(i++)}`).join(" AND ");
|
|
15816
|
+
const params2 = [...setKeys.map((k) => values[k]), ...matchKeys.map((k) => match[k])];
|
|
15817
|
+
await bunSql.unsafe(`UPDATE ${tbl} SET ${setSql} WHERE ${whereSql2}`, params2).execute();
|
|
15530
15818
|
return true;
|
|
15531
15819
|
}
|
|
15820
|
+
const { colsSql, valuesSql, params } = buildInsertClause([{ ...match, ...values }]);
|
|
15821
|
+
await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
15822
|
+
return true;
|
|
15532
15823
|
},
|
|
15533
15824
|
async upsert(table, rows, conflictColumns, mergeColumns) {
|
|
15534
15825
|
const targetCols = conflictColumns.map((c) => String(c));
|
|
15535
15826
|
const setCols = (mergeColumns ?? []).map((c) => String(c));
|
|
15827
|
+
const list = rows;
|
|
15828
|
+
if (!list.length)
|
|
15829
|
+
return;
|
|
15830
|
+
const { colsSql, valuesSql, params } = buildInsertClause(list);
|
|
15831
|
+
const tbl = quoteInsertIdent(String(table));
|
|
15832
|
+
const insert = `INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`;
|
|
15536
15833
|
if (config5.dialect === "mysql") {
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
const
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15834
|
+
if (setCols.length === 0)
|
|
15835
|
+
return bunSql.unsafe(`INSERT IGNORE INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
|
|
15836
|
+
const updateList2 = setCols.map((c) => `${quoteInsertIdent(c)} = VALUES(${quoteInsertIdent(c)})`).join(", ");
|
|
15837
|
+
return bunSql.unsafe(`${insert} ON DUPLICATE KEY UPDATE ${updateList2}`, params).execute();
|
|
15838
|
+
}
|
|
15839
|
+
const targets = targetCols.map(quoteInsertIdent).join(", ");
|
|
15840
|
+
if (setCols.length === 0)
|
|
15841
|
+
return bunSql.unsafe(`${insert} ON CONFLICT (${targets}) DO NOTHING`, params).execute();
|
|
15842
|
+
const updateList = setCols.map((c) => `${quoteInsertIdent(c)} = EXCLUDED.${quoteInsertIdent(c)}`).join(", ");
|
|
15843
|
+
return bunSql.unsafe(`${insert} ON CONFLICT (${targets}) DO UPDATE SET ${updateList}`, params).execute();
|
|
15546
15844
|
},
|
|
15547
15845
|
async save(table, values) {
|
|
15548
15846
|
const pk = meta?.primaryKeys[String(table)] ?? "id";
|
|
@@ -15645,7 +15943,8 @@ function createQueryBuilder(state) {
|
|
|
15645
15943
|
const colCount = keys.length;
|
|
15646
15944
|
const rowCount = rows.length;
|
|
15647
15945
|
const params = Array.from({ length: rowCount * colCount });
|
|
15648
|
-
|
|
15946
|
+
const quoteId = config5.dialect === "mysql" ? (id) => `\`${String(id).replace(/`/g, "``")}\`` : (id) => `"${String(id).replace(/"/g, '""')}"`;
|
|
15947
|
+
let sql = `INSERT INTO ${quoteId(String(table))}(${keys.map(quoteId).join(",")})VALUES`;
|
|
15649
15948
|
let pidx = 0;
|
|
15650
15949
|
for (let r = 0;r < rowCount; r++) {
|
|
15651
15950
|
if (r > 0)
|
|
@@ -15952,7 +16251,7 @@ var init_cache = __esm(() => {
|
|
|
15952
16251
|
});
|
|
15953
16252
|
|
|
15954
16253
|
// src/actions/console.ts
|
|
15955
|
-
import
|
|
16254
|
+
import process19 from "process";
|
|
15956
16255
|
import { createInterface } from "readline";
|
|
15957
16256
|
async function startConsole() {
|
|
15958
16257
|
console.log("-- Query Builder Interactive Console");
|
|
@@ -15961,8 +16260,8 @@ async function startConsole() {
|
|
|
15961
16260
|
console.log();
|
|
15962
16261
|
const qb = createQueryBuilder();
|
|
15963
16262
|
const rl = createInterface({
|
|
15964
|
-
input:
|
|
15965
|
-
output:
|
|
16263
|
+
input: process19.stdin,
|
|
16264
|
+
output: process19.stdout,
|
|
15966
16265
|
prompt: "qb> "
|
|
15967
16266
|
});
|
|
15968
16267
|
let multilineBuffer = "";
|
|
@@ -16013,7 +16312,7 @@ Tips:
|
|
|
16013
16312
|
if (cmd === ".exit" || cmd === ".quit") {
|
|
16014
16313
|
console.log("Goodbye!");
|
|
16015
16314
|
rl.close();
|
|
16016
|
-
|
|
16315
|
+
process19.exit(0);
|
|
16017
16316
|
} else if (cmd === ".help") {
|
|
16018
16317
|
console.log(helpText);
|
|
16019
16318
|
} else if (cmd === ".clear") {
|
|
@@ -16084,7 +16383,7 @@ Tips:
|
|
|
16084
16383
|
rl.on("close", () => {
|
|
16085
16384
|
console.log(`
|
|
16086
16385
|
Goodbye!`);
|
|
16087
|
-
|
|
16386
|
+
process19.exit(0);
|
|
16088
16387
|
});
|
|
16089
16388
|
rl.prompt();
|
|
16090
16389
|
}
|
|
@@ -16462,9 +16761,9 @@ var init_db_info = __esm(() => {
|
|
|
16462
16761
|
});
|
|
16463
16762
|
|
|
16464
16763
|
// src/actions/db-optimize.ts
|
|
16465
|
-
import
|
|
16764
|
+
import process21 from "process";
|
|
16466
16765
|
async function dbOptimize(options = {}) {
|
|
16467
|
-
const dialect = options.dialect ||
|
|
16766
|
+
const dialect = options.dialect || process21.env.DB_DIALECT || "postgres";
|
|
16468
16767
|
const aggressive = options.aggressive || false;
|
|
16469
16768
|
if (options.verbose) {
|
|
16470
16769
|
console.log(`Optimizing ${dialect} database${aggressive ? " (aggressive mode)" : ""}...`);
|
|
@@ -16504,7 +16803,7 @@ async function dbOptimize(options = {}) {
|
|
|
16504
16803
|
await bunSql`ANALYZE TABLE ${bunSql(table)}`;
|
|
16505
16804
|
}
|
|
16506
16805
|
} else {
|
|
16507
|
-
const dbName =
|
|
16806
|
+
const dbName = process21.env.DB_NAME || "test";
|
|
16508
16807
|
const tables = await bunSql`
|
|
16509
16808
|
SELECT table_name
|
|
16510
16809
|
FROM information_schema.tables
|
|
@@ -16556,9 +16855,9 @@ var init_db_optimize = __esm(() => {
|
|
|
16556
16855
|
});
|
|
16557
16856
|
|
|
16558
16857
|
// src/actions/db-wipe.ts
|
|
16559
|
-
import
|
|
16858
|
+
import process22 from "process";
|
|
16560
16859
|
async function dbWipe(options = {}) {
|
|
16561
|
-
const dialect = options.dialect ||
|
|
16860
|
+
const dialect = options.dialect || process22.env.DB_DIALECT || "postgres";
|
|
16562
16861
|
if (options.verbose) {
|
|
16563
16862
|
console.log(`Wiping all tables from ${dialect} database...`);
|
|
16564
16863
|
}
|
|
@@ -16572,7 +16871,7 @@ async function dbWipe(options = {}) {
|
|
|
16572
16871
|
`;
|
|
16573
16872
|
tables = result.map((row) => row.tablename);
|
|
16574
16873
|
} else if (dialect === "mysql") {
|
|
16575
|
-
const dbName =
|
|
16874
|
+
const dbName = process22.env.DB_NAME || "test";
|
|
16576
16875
|
const result = await bunSql`
|
|
16577
16876
|
SELECT table_name
|
|
16578
16877
|
FROM information_schema.tables
|
|
@@ -16865,10 +17164,130 @@ var init_introspect = __esm(() => {
|
|
|
16865
17164
|
init_src2();
|
|
16866
17165
|
});
|
|
16867
17166
|
|
|
17167
|
+
// src/actions/introspect-db.ts
|
|
17168
|
+
function sqlTypeToAttr(sqlType) {
|
|
17169
|
+
const t = sqlType.toLowerCase();
|
|
17170
|
+
if (/^bool|boolean|tinyint\(1\)/.test(t))
|
|
17171
|
+
return "boolean";
|
|
17172
|
+
if (/int|serial|numeric|decimal|double|real|float|money/.test(t))
|
|
17173
|
+
return "number";
|
|
17174
|
+
if (/timestamp|datetime|date|time/.test(t))
|
|
17175
|
+
return "datetime";
|
|
17176
|
+
if (/json/.test(t))
|
|
17177
|
+
return "json";
|
|
17178
|
+
return "string";
|
|
17179
|
+
}
|
|
17180
|
+
function singularize(name) {
|
|
17181
|
+
if (/ies$/i.test(name))
|
|
17182
|
+
return name.replace(/ies$/i, "y");
|
|
17183
|
+
if (/ses$/i.test(name))
|
|
17184
|
+
return name.replace(/es$/i, "");
|
|
17185
|
+
if (/s$/i.test(name) && !/ss$/i.test(name))
|
|
17186
|
+
return name.replace(/s$/i, "");
|
|
17187
|
+
return name;
|
|
17188
|
+
}
|
|
17189
|
+
function pascalCase(name) {
|
|
17190
|
+
return name.replace(/[-_\s]+/g, " ").split(" ").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
17191
|
+
}
|
|
17192
|
+
function modelNameForTable(table) {
|
|
17193
|
+
return pascalCase(singularize(table));
|
|
17194
|
+
}
|
|
17195
|
+
async function listTables(qb, dialect) {
|
|
17196
|
+
if (dialect === "postgres") {
|
|
17197
|
+
const rows2 = await qb.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name`);
|
|
17198
|
+
return rows2.map((r) => r.table_name);
|
|
17199
|
+
}
|
|
17200
|
+
if (dialect === "mysql") {
|
|
17201
|
+
const rows2 = await qb.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE' ORDER BY table_name`);
|
|
17202
|
+
return rows2.map((r) => r.table_name ?? r.TABLE_NAME);
|
|
17203
|
+
}
|
|
17204
|
+
const rows = await qb.unsafe(`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
|
|
17205
|
+
return rows.map((r) => r.name);
|
|
17206
|
+
}
|
|
17207
|
+
async function readColumns(qb, dialect, table) {
|
|
17208
|
+
if (dialect === "sqlite") {
|
|
17209
|
+
const rows2 = await qb.unsafe(`PRAGMA table_info(${table})`);
|
|
17210
|
+
return rows2.map((r) => ({
|
|
17211
|
+
name: r.name,
|
|
17212
|
+
sqlType: String(r.type || ""),
|
|
17213
|
+
nullable: Number(r.notnull) === 0,
|
|
17214
|
+
isPrimaryKey: Number(r.pk) > 0
|
|
17215
|
+
}));
|
|
17216
|
+
}
|
|
17217
|
+
if (dialect === "postgres") {
|
|
17218
|
+
const rows2 = await qb.unsafe(`SELECT c.column_name, c.data_type, c.is_nullable,
|
|
17219
|
+
(SELECT COUNT(*) FROM information_schema.table_constraints tc
|
|
17220
|
+
JOIN information_schema.key_column_usage k ON k.constraint_name = tc.constraint_name
|
|
17221
|
+
WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_name = c.table_name AND k.column_name = c.column_name) AS is_pk
|
|
17222
|
+
FROM information_schema.columns c
|
|
17223
|
+
WHERE c.table_name = $1 AND c.table_schema = 'public'
|
|
17224
|
+
ORDER BY c.ordinal_position`, [table]);
|
|
17225
|
+
return rows2.map((r) => ({
|
|
17226
|
+
name: r.column_name,
|
|
17227
|
+
sqlType: String(r.data_type || ""),
|
|
17228
|
+
nullable: String(r.is_nullable).toUpperCase() === "YES",
|
|
17229
|
+
isPrimaryKey: Number(r.is_pk) > 0
|
|
17230
|
+
}));
|
|
17231
|
+
}
|
|
17232
|
+
const rows = await qb.unsafe(`SELECT column_name, data_type, is_nullable, column_key
|
|
17233
|
+
FROM information_schema.columns
|
|
17234
|
+
WHERE table_name = ? AND table_schema = DATABASE()
|
|
17235
|
+
ORDER BY ordinal_position`, [table]);
|
|
17236
|
+
return rows.map((r) => ({
|
|
17237
|
+
name: r.column_name ?? r.COLUMN_NAME,
|
|
17238
|
+
sqlType: String(r.data_type ?? r.DATA_TYPE ?? ""),
|
|
17239
|
+
nullable: String(r.is_nullable ?? r.IS_NULLABLE).toUpperCase() === "YES",
|
|
17240
|
+
isPrimaryKey: String(r.column_key ?? r.COLUMN_KEY).toUpperCase() === "PRI"
|
|
17241
|
+
}));
|
|
17242
|
+
}
|
|
17243
|
+
function generateModelSource(table, columns) {
|
|
17244
|
+
const modelName = modelNameForTable(table);
|
|
17245
|
+
const pk = columns.find((c) => c.isPrimaryKey)?.name ?? "id";
|
|
17246
|
+
const attrLines = columns.map((c) => {
|
|
17247
|
+
const parts = [`type: '${sqlTypeToAttr(c.sqlType)}'`];
|
|
17248
|
+
if (!c.nullable && !c.isPrimaryKey)
|
|
17249
|
+
parts.push("required: true");
|
|
17250
|
+
return ` ${c.name}: { ${parts.join(", ")} },`;
|
|
17251
|
+
}).join(`
|
|
17252
|
+
`);
|
|
17253
|
+
return `export const ${modelName} = defineModel({
|
|
17254
|
+
name: '${modelName}',
|
|
17255
|
+
table: '${table}',
|
|
17256
|
+
primaryKey: '${pk}',
|
|
17257
|
+
attributes: {
|
|
17258
|
+
${attrLines}
|
|
17259
|
+
},
|
|
17260
|
+
})
|
|
17261
|
+
`;
|
|
17262
|
+
}
|
|
17263
|
+
async function introspectDatabase(opts = {}) {
|
|
17264
|
+
const dialect = config5.dialect || "postgres";
|
|
17265
|
+
const qb = createQueryBuilder();
|
|
17266
|
+
const tables = opts.tables?.length ? opts.tables : await listTables(qb, dialect);
|
|
17267
|
+
const out = [];
|
|
17268
|
+
for (const table of tables) {
|
|
17269
|
+
const columns = await readColumns(qb, dialect, table);
|
|
17270
|
+
if (!columns.length)
|
|
17271
|
+
continue;
|
|
17272
|
+
out.push({
|
|
17273
|
+
table,
|
|
17274
|
+
modelName: modelNameForTable(table),
|
|
17275
|
+
primaryKey: columns.find((c) => c.isPrimaryKey)?.name ?? "id",
|
|
17276
|
+
columns,
|
|
17277
|
+
source: generateModelSource(table, columns)
|
|
17278
|
+
});
|
|
17279
|
+
}
|
|
17280
|
+
return out;
|
|
17281
|
+
}
|
|
17282
|
+
var init_introspect_db = __esm(() => {
|
|
17283
|
+
init_config();
|
|
17284
|
+
init_src2();
|
|
17285
|
+
});
|
|
17286
|
+
|
|
16868
17287
|
// src/actions/make-model.ts
|
|
16869
17288
|
import { existsSync as existsSync14, mkdirSync as mkdirSync5, writeFileSync as writeFileSync10 } from "fs";
|
|
16870
17289
|
import { dirname as dirname5, join as join7 } from "path";
|
|
16871
|
-
import
|
|
17290
|
+
import process23 from "process";
|
|
16872
17291
|
function findWorkspaceRoot(startPath) {
|
|
16873
17292
|
let currentPath = startPath;
|
|
16874
17293
|
while (currentPath !== dirname5(currentPath)) {
|
|
@@ -16877,10 +17296,10 @@ function findWorkspaceRoot(startPath) {
|
|
|
16877
17296
|
}
|
|
16878
17297
|
currentPath = dirname5(currentPath);
|
|
16879
17298
|
}
|
|
16880
|
-
return
|
|
17299
|
+
return process23.cwd();
|
|
16881
17300
|
}
|
|
16882
17301
|
async function makeModel(name, options = {}) {
|
|
16883
|
-
const workspaceRoot = findWorkspaceRoot(
|
|
17302
|
+
const workspaceRoot = findWorkspaceRoot(process23.cwd());
|
|
16884
17303
|
const modelsDir = options.dir || join7(workspaceRoot, "app/Models");
|
|
16885
17304
|
if (!existsSync14(modelsDir)) {
|
|
16886
17305
|
mkdirSync5(modelsDir, { recursive: true });
|
|
@@ -17437,7 +17856,7 @@ __export(exports_migrate, {
|
|
|
17437
17856
|
import { existsSync as existsSync16, mkdirSync as mkdirSync7, mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync3, rmSync, unlinkSync, writeFileSync as writeFileSync11 } from "fs";
|
|
17438
17857
|
import { tmpdir } from "os";
|
|
17439
17858
|
import { join as join9 } from "path";
|
|
17440
|
-
import
|
|
17859
|
+
import process24 from "process";
|
|
17441
17860
|
function info(message) {
|
|
17442
17861
|
if (config5.verbose)
|
|
17443
17862
|
console.log(message);
|
|
@@ -17484,7 +17903,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
|
|
|
17484
17903
|
info(`-- Model snapshot saved to ${snapshotPath}`);
|
|
17485
17904
|
}
|
|
17486
17905
|
function getWorkspaceRoot() {
|
|
17487
|
-
return
|
|
17906
|
+
return process24.cwd();
|
|
17488
17907
|
}
|
|
17489
17908
|
function ensureSqlDirectory(workspaceRoot) {
|
|
17490
17909
|
const sqlDir = getSqlDirectory(workspaceRoot);
|
|
@@ -17496,7 +17915,7 @@ function ensureSqlDirectory(workspaceRoot) {
|
|
|
17496
17915
|
}
|
|
17497
17916
|
async function generateMigration(dir, opts = {}) {
|
|
17498
17917
|
if (!dir) {
|
|
17499
|
-
dir = join9(
|
|
17918
|
+
dir = join9(process24.cwd(), "app/Models");
|
|
17500
17919
|
}
|
|
17501
17920
|
const dialect = opts.dialect || config5.dialect || "postgres";
|
|
17502
17921
|
const workspaceRoot = getWorkspaceRoot();
|
|
@@ -17551,7 +17970,7 @@ async function generateMigration(dir, opts = {}) {
|
|
|
17551
17970
|
}
|
|
17552
17971
|
async function executeMigration(dir) {
|
|
17553
17972
|
if (!dir) {
|
|
17554
|
-
dir = join9(
|
|
17973
|
+
dir = join9(process24.cwd(), "app/Models");
|
|
17555
17974
|
}
|
|
17556
17975
|
const workspaceRoot = getWorkspaceRoot();
|
|
17557
17976
|
const sqlDir = ensureSqlDirectory(workspaceRoot);
|
|
@@ -17616,7 +18035,7 @@ async function executeMigration(dir) {
|
|
|
17616
18035
|
}
|
|
17617
18036
|
async function resetDatabase(dir, opts = {}) {
|
|
17618
18037
|
if (!dir) {
|
|
17619
|
-
dir = join9(
|
|
18038
|
+
dir = join9(process24.cwd(), "app/Models");
|
|
17620
18039
|
}
|
|
17621
18040
|
const dialect = opts.dialect || "postgres";
|
|
17622
18041
|
const driver = getDialectDriver(dialect);
|
|
@@ -17709,7 +18128,7 @@ async function resetDatabase(dir, opts = {}) {
|
|
|
17709
18128
|
}
|
|
17710
18129
|
async function deleteMigrationFiles(dir, workspaceRoot, opts = {}) {
|
|
17711
18130
|
if (!dir) {
|
|
17712
|
-
dir = join9(
|
|
18131
|
+
dir = join9(process24.cwd(), "app/Models");
|
|
17713
18132
|
}
|
|
17714
18133
|
if (!workspaceRoot) {
|
|
17715
18134
|
workspaceRoot = getWorkspaceRoot();
|
|
@@ -17807,9 +18226,51 @@ var init_migrate_generate = __esm(() => {
|
|
|
17807
18226
|
});
|
|
17808
18227
|
|
|
17809
18228
|
// src/actions/migrate-rollback.ts
|
|
17810
|
-
import { existsSync as existsSync17, unlinkSync as unlinkSync2 } from "fs";
|
|
18229
|
+
import { existsSync as existsSync17, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
17811
18230
|
import { dirname as dirname7, join as join10 } from "path";
|
|
17812
|
-
import
|
|
18231
|
+
import process25 from "process";
|
|
18232
|
+
function splitSqlStatements2(sql) {
|
|
18233
|
+
const out = [];
|
|
18234
|
+
let buf = "";
|
|
18235
|
+
let inString = false;
|
|
18236
|
+
const lines = sql.split(`
|
|
18237
|
+
`).filter((l) => !/^\s*--/.test(l));
|
|
18238
|
+
const text = lines.join(`
|
|
18239
|
+
`);
|
|
18240
|
+
for (let i = 0;i < text.length; i++) {
|
|
18241
|
+
const ch = text[i];
|
|
18242
|
+
if (ch === "'")
|
|
18243
|
+
inString = !inString;
|
|
18244
|
+
if (ch === ";" && !inString) {
|
|
18245
|
+
if (buf.trim())
|
|
18246
|
+
out.push(buf.trim());
|
|
18247
|
+
buf = "";
|
|
18248
|
+
} else {
|
|
18249
|
+
buf += ch;
|
|
18250
|
+
}
|
|
18251
|
+
}
|
|
18252
|
+
if (buf.trim())
|
|
18253
|
+
out.push(buf.trim());
|
|
18254
|
+
return out;
|
|
18255
|
+
}
|
|
18256
|
+
function deriveDownStatements(forwardSql, dialect = config5.dialect) {
|
|
18257
|
+
const q = (id) => dialect === "mysql" ? `\`${id}\`` : `"${id}"`;
|
|
18258
|
+
const down = [];
|
|
18259
|
+
const skipped = [];
|
|
18260
|
+
for (const stmt of splitSqlStatements2(forwardSql)) {
|
|
18261
|
+
let m;
|
|
18262
|
+
if (m = /^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
|
|
18263
|
+
down.push(`DROP TABLE IF EXISTS ${q(m[1])}`);
|
|
18264
|
+
} else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+(?:COLUMN\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
|
|
18265
|
+
down.push(`ALTER TABLE ${q(m[1])} DROP COLUMN ${q(m[2])}`);
|
|
18266
|
+
} else if (m = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?(?:\s+ON\s+["`']?(\w+)["`']?)?/i.exec(stmt)) {
|
|
18267
|
+
down.push(dialect === "mysql" && m[2] ? `DROP INDEX ${q(m[1])} ON ${q(m[2])}` : `DROP INDEX IF EXISTS ${q(m[1])}`);
|
|
18268
|
+
} else {
|
|
18269
|
+
skipped.push(stmt);
|
|
18270
|
+
}
|
|
18271
|
+
}
|
|
18272
|
+
return { down: down.reverse(), skipped };
|
|
18273
|
+
}
|
|
17813
18274
|
function findWorkspaceRoot2(startPath) {
|
|
17814
18275
|
let currentPath = startPath;
|
|
17815
18276
|
while (currentPath !== dirname7(currentPath)) {
|
|
@@ -17818,16 +18279,17 @@ function findWorkspaceRoot2(startPath) {
|
|
|
17818
18279
|
}
|
|
17819
18280
|
currentPath = dirname7(currentPath);
|
|
17820
18281
|
}
|
|
17821
|
-
return
|
|
18282
|
+
return process25.cwd();
|
|
17822
18283
|
}
|
|
17823
18284
|
function getSqlDirectory2(workspaceRoot) {
|
|
17824
18285
|
if (!workspaceRoot) {
|
|
17825
|
-
workspaceRoot = findWorkspaceRoot2(
|
|
18286
|
+
workspaceRoot = findWorkspaceRoot2(process25.cwd());
|
|
17826
18287
|
}
|
|
17827
18288
|
return join10(workspaceRoot, "database", "migrations");
|
|
17828
18289
|
}
|
|
17829
18290
|
async function migrateRollback(options = {}) {
|
|
17830
18291
|
const steps = options.steps || 1;
|
|
18292
|
+
const reverseSchema = options.reverseSchema !== false;
|
|
17831
18293
|
console.log("-- Rolling back migrations");
|
|
17832
18294
|
console.log(`-- Steps: ${steps}`);
|
|
17833
18295
|
console.log();
|
|
@@ -17857,13 +18319,25 @@ async function migrateRollback(options = {}) {
|
|
|
17857
18319
|
console.log(` - ${migration.migration}`);
|
|
17858
18320
|
}
|
|
17859
18321
|
console.log();
|
|
18322
|
+
const sqlDir = getSqlDirectory2();
|
|
18323
|
+
let reversedAny = false;
|
|
17860
18324
|
for (const migration of migrationsToRollback) {
|
|
17861
18325
|
try {
|
|
18326
|
+
const filePath = join10(sqlDir, migration.migration);
|
|
18327
|
+
if (reverseSchema && existsSync17(filePath)) {
|
|
18328
|
+
const forwardSql = readFileSync4(filePath, "utf8");
|
|
18329
|
+
const { down, skipped } = deriveDownStatements(forwardSql);
|
|
18330
|
+
for (const stmt of down) {
|
|
18331
|
+
await qb.unsafe(stmt);
|
|
18332
|
+
console.log(`-- \u21A9 ${stmt}`);
|
|
18333
|
+
reversedAny = true;
|
|
18334
|
+
}
|
|
18335
|
+
if (skipped.length > 0)
|
|
18336
|
+
console.log(`-- \u26A0\uFE0F ${skipped.length} statement(s) in ${migration.migration} could not be auto-reversed (data/complex DDL) \u2014 reverse manually.`);
|
|
18337
|
+
}
|
|
17862
18338
|
const deleteSql = `DELETE FROM migrations WHERE migration = $1`;
|
|
17863
18339
|
await qb.unsafe(deleteSql, [migration.migration]);
|
|
17864
18340
|
console.log(`-- \u2713 Removed migration record: ${migration.migration}`);
|
|
17865
|
-
const sqlDir = getSqlDirectory2();
|
|
17866
|
-
const filePath = join10(sqlDir, migration.migration);
|
|
17867
18341
|
if (existsSync17(filePath)) {
|
|
17868
18342
|
unlinkSync2(filePath);
|
|
17869
18343
|
console.log(`-- \uD83D\uDDD1\uFE0F Deleted migration file: ${migration.migration}`);
|
|
@@ -17874,24 +18348,25 @@ async function migrateRollback(options = {}) {
|
|
|
17874
18348
|
}
|
|
17875
18349
|
}
|
|
17876
18350
|
console.log();
|
|
17877
|
-
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
|
|
17881
|
-
|
|
18351
|
+
if (reverseSchema) {
|
|
18352
|
+
console.log(reversedAny ? "-- \u2713 Reverse DDL executed for the rolled-back migration(s)." : "-- \u26A0\uFE0F No reversible DDL found; only migration records were removed.");
|
|
18353
|
+
} else {
|
|
18354
|
+
console.log("-- \u26A0\uFE0F reverseSchema disabled: only migration records were removed.");
|
|
18355
|
+
}
|
|
17882
18356
|
} catch (err) {
|
|
17883
18357
|
console.error("-- Rollback failed:", err);
|
|
17884
18358
|
throw err;
|
|
17885
18359
|
}
|
|
17886
18360
|
}
|
|
17887
18361
|
var init_migrate_rollback = __esm(() => {
|
|
18362
|
+
init_config();
|
|
17888
18363
|
init_src2();
|
|
17889
18364
|
});
|
|
17890
18365
|
|
|
17891
18366
|
// src/actions/migrate-status.ts
|
|
17892
18367
|
import { existsSync as existsSync18, readdirSync as readdirSync8 } from "fs";
|
|
17893
18368
|
import { dirname as dirname8, join as join11 } from "path";
|
|
17894
|
-
import
|
|
18369
|
+
import process26 from "process";
|
|
17895
18370
|
function findWorkspaceRoot3(startPath) {
|
|
17896
18371
|
let currentPath = startPath;
|
|
17897
18372
|
while (currentPath !== dirname8(currentPath)) {
|
|
@@ -17900,11 +18375,11 @@ function findWorkspaceRoot3(startPath) {
|
|
|
17900
18375
|
}
|
|
17901
18376
|
currentPath = dirname8(currentPath);
|
|
17902
18377
|
}
|
|
17903
|
-
return
|
|
18378
|
+
return process26.cwd();
|
|
17904
18379
|
}
|
|
17905
18380
|
function getSqlDirectory3(workspaceRoot) {
|
|
17906
18381
|
if (!workspaceRoot) {
|
|
17907
|
-
workspaceRoot = findWorkspaceRoot3(
|
|
18382
|
+
workspaceRoot = findWorkspaceRoot3(process26.cwd());
|
|
17908
18383
|
}
|
|
17909
18384
|
return join11(workspaceRoot, "database", "migrations");
|
|
17910
18385
|
}
|
|
@@ -17998,9 +18473,9 @@ var init_migrate_status = __esm(() => {
|
|
|
17998
18473
|
// src/actions/model-show.ts
|
|
17999
18474
|
import { readdirSync as readdirSync9 } from "fs";
|
|
18000
18475
|
import { extname as extname2, join as join12 } from "path";
|
|
18001
|
-
import
|
|
18476
|
+
import process27 from "process";
|
|
18002
18477
|
async function modelShow(modelName, options = {}) {
|
|
18003
|
-
const dir = options.dir || join12(
|
|
18478
|
+
const dir = options.dir || join12(process27.cwd(), "app/Models");
|
|
18004
18479
|
try {
|
|
18005
18480
|
const files = readdirSync9(dir);
|
|
18006
18481
|
const modelFile = files.find((f) => {
|
|
@@ -18119,7 +18594,7 @@ var init_ping = __esm(() => {
|
|
|
18119
18594
|
});
|
|
18120
18595
|
|
|
18121
18596
|
// src/actions/query-explain-all.ts
|
|
18122
|
-
import { readdirSync as readdirSync10, readFileSync as
|
|
18597
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
18123
18598
|
import { extname as extname3, join as join13 } from "path";
|
|
18124
18599
|
async function queryExplainAll(path, options = {}) {
|
|
18125
18600
|
const results = [];
|
|
@@ -18145,7 +18620,7 @@ async function queryExplainAll(path, options = {}) {
|
|
|
18145
18620
|
}
|
|
18146
18621
|
for (const file2 of files) {
|
|
18147
18622
|
try {
|
|
18148
|
-
const query =
|
|
18623
|
+
const query = readFileSync5(file2, "utf8").trim();
|
|
18149
18624
|
if (!query) {
|
|
18150
18625
|
if (options.verbose) {
|
|
18151
18626
|
console.log(`\u2298 ${file2}: empty file`);
|
|
@@ -18182,7 +18657,7 @@ async function queryExplainAll(path, options = {}) {
|
|
|
18182
18657
|
} catch (error) {
|
|
18183
18658
|
results.push({
|
|
18184
18659
|
file: file2,
|
|
18185
|
-
query:
|
|
18660
|
+
query: readFileSync5(file2, "utf8").trim(),
|
|
18186
18661
|
plan: [],
|
|
18187
18662
|
error: error.message
|
|
18188
18663
|
});
|
|
@@ -18214,9 +18689,9 @@ var init_query_explain_all = __esm(() => {
|
|
|
18214
18689
|
// src/actions/relation-diagram.ts
|
|
18215
18690
|
import { writeFileSync as writeFileSync12 } from "fs";
|
|
18216
18691
|
import { join as join14 } from "path";
|
|
18217
|
-
import
|
|
18692
|
+
import process28 from "process";
|
|
18218
18693
|
async function relationDiagram(options = {}) {
|
|
18219
|
-
const dir = options.dir || join14(
|
|
18694
|
+
const dir = options.dir || join14(process28.cwd(), "app/Models");
|
|
18220
18695
|
const format = options.format || "mermaid";
|
|
18221
18696
|
try {
|
|
18222
18697
|
const models = await loadModels({ modelsDir: dir });
|
|
@@ -18338,7 +18813,7 @@ var init_relation_diagram = __esm(() => {
|
|
|
18338
18813
|
// src/actions/seed.ts
|
|
18339
18814
|
import { existsSync as existsSync19, mkdirSync as mkdirSync8, readdirSync as readdirSync11, writeFileSync as writeFileSync13 } from "fs";
|
|
18340
18815
|
import { dirname as dirname9, join as join15 } from "path";
|
|
18341
|
-
import
|
|
18816
|
+
import process29 from "process";
|
|
18342
18817
|
function findWorkspaceRoot4(startPath) {
|
|
18343
18818
|
let currentPath = startPath;
|
|
18344
18819
|
while (currentPath !== dirname9(currentPath)) {
|
|
@@ -18347,7 +18822,7 @@ function findWorkspaceRoot4(startPath) {
|
|
|
18347
18822
|
}
|
|
18348
18823
|
currentPath = dirname9(currentPath);
|
|
18349
18824
|
}
|
|
18350
|
-
return
|
|
18825
|
+
return process29.cwd();
|
|
18351
18826
|
}
|
|
18352
18827
|
async function loadSeeders(seedersDir) {
|
|
18353
18828
|
if (!existsSync19(seedersDir)) {
|
|
@@ -18379,7 +18854,7 @@ async function loadSeeders(seedersDir) {
|
|
|
18379
18854
|
return seeders;
|
|
18380
18855
|
}
|
|
18381
18856
|
async function runSeeders(config6 = {}) {
|
|
18382
|
-
const workspaceRoot = findWorkspaceRoot4(
|
|
18857
|
+
const workspaceRoot = findWorkspaceRoot4(process29.cwd());
|
|
18383
18858
|
const seedersDir = config6.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
18384
18859
|
const verbose = config6.verbose ?? true;
|
|
18385
18860
|
if (verbose) {
|
|
@@ -18414,7 +18889,7 @@ async function runSeeders(config6 = {}) {
|
|
|
18414
18889
|
}
|
|
18415
18890
|
}
|
|
18416
18891
|
async function runSeeder(className, options = {}) {
|
|
18417
|
-
const workspaceRoot = findWorkspaceRoot4(
|
|
18892
|
+
const workspaceRoot = findWorkspaceRoot4(process29.cwd());
|
|
18418
18893
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
18419
18894
|
const verbose = options.verbose ?? true;
|
|
18420
18895
|
if (verbose) {
|
|
@@ -18438,7 +18913,7 @@ async function runSeeder(className, options = {}) {
|
|
|
18438
18913
|
}
|
|
18439
18914
|
}
|
|
18440
18915
|
async function makeSeeder(name) {
|
|
18441
|
-
const workspaceRoot = findWorkspaceRoot4(
|
|
18916
|
+
const workspaceRoot = findWorkspaceRoot4(process29.cwd());
|
|
18442
18917
|
const seedersDir = join15(workspaceRoot, "database/seeders");
|
|
18443
18918
|
if (!existsSync19(seedersDir)) {
|
|
18444
18919
|
mkdirSync8(seedersDir, { recursive: true });
|
|
@@ -18502,7 +18977,7 @@ export default class ${className} extends Seeder {
|
|
|
18502
18977
|
console.log(`-- \u2713 Created seeder: ${filePath}`);
|
|
18503
18978
|
}
|
|
18504
18979
|
async function freshDatabase(options = {}) {
|
|
18505
|
-
const workspaceRoot = findWorkspaceRoot4(
|
|
18980
|
+
const workspaceRoot = findWorkspaceRoot4(process29.cwd());
|
|
18506
18981
|
const modelsDir = options.modelsDir || join15(workspaceRoot, "app/Models");
|
|
18507
18982
|
const seedersDir = options.seedersDir || join15(workspaceRoot, "database/seeders");
|
|
18508
18983
|
const verbose = options.verbose ?? true;
|
|
@@ -18564,7 +19039,7 @@ var init_unsafe = __esm(() => {
|
|
|
18564
19039
|
// src/actions/validate.ts
|
|
18565
19040
|
import { existsSync as existsSync20 } from "fs";
|
|
18566
19041
|
import { dirname as dirname10, join as join16 } from "path";
|
|
18567
|
-
import
|
|
19042
|
+
import process30 from "process";
|
|
18568
19043
|
function findWorkspaceRoot5(startPath) {
|
|
18569
19044
|
let currentPath = startPath;
|
|
18570
19045
|
while (currentPath !== dirname10(currentPath)) {
|
|
@@ -18573,11 +19048,11 @@ function findWorkspaceRoot5(startPath) {
|
|
|
18573
19048
|
}
|
|
18574
19049
|
currentPath = dirname10(currentPath);
|
|
18575
19050
|
}
|
|
18576
|
-
return
|
|
19051
|
+
return process30.cwd();
|
|
18577
19052
|
}
|
|
18578
19053
|
async function validateSchema(dir) {
|
|
18579
19054
|
if (!dir) {
|
|
18580
|
-
dir = join16(findWorkspaceRoot5(
|
|
19055
|
+
dir = join16(findWorkspaceRoot5(process30.cwd()), "app/Models");
|
|
18581
19056
|
}
|
|
18582
19057
|
const dialect = config5.dialect || "postgres";
|
|
18583
19058
|
console.log("-- Validating Schema");
|
|
@@ -18796,6 +19271,7 @@ var init_actions = __esm(() => {
|
|
|
18796
19271
|
init_file();
|
|
18797
19272
|
init_inspect();
|
|
18798
19273
|
init_introspect();
|
|
19274
|
+
init_introspect_db();
|
|
18799
19275
|
init_make_model();
|
|
18800
19276
|
init_migrate();
|
|
18801
19277
|
init_migrate_generate();
|
|
@@ -23415,6 +23891,14 @@ function getDatabase() {
|
|
|
23415
23891
|
return exec.sqliteDb;
|
|
23416
23892
|
throw new Error(`[bun-query-builder] getDatabase() is only available for the sqlite dialect; ` + `the configured dialect is '${exec.dialect}'. Use the async model API instead.`);
|
|
23417
23893
|
}
|
|
23894
|
+
function softDeletesEnabled(definition) {
|
|
23895
|
+
const t2 = definition.traits;
|
|
23896
|
+
return Boolean(t2?.useSoftDeletes || t2?.softDeletable);
|
|
23897
|
+
}
|
|
23898
|
+
function timestampsEnabled(definition) {
|
|
23899
|
+
const t2 = definition.traits;
|
|
23900
|
+
return Boolean(t2?.useTimestamps || t2?.timestampable);
|
|
23901
|
+
}
|
|
23418
23902
|
function collectBelongsToManyKeys(definition) {
|
|
23419
23903
|
const keys = new Set;
|
|
23420
23904
|
const rel = definition.belongsToMany;
|
|
@@ -23584,7 +24068,7 @@ class ModelInstance {
|
|
|
23584
24068
|
if (changeKeys.length > 0) {
|
|
23585
24069
|
const sets = changeKeys.map((k2) => `${k2} = ?`).join(", ");
|
|
23586
24070
|
const values = [...Object.values(changes), this._attributes[pk]];
|
|
23587
|
-
if (this._definition
|
|
24071
|
+
if (timestampsEnabled(this._definition)) {
|
|
23588
24072
|
const now = formatNow();
|
|
23589
24073
|
await exec.run(`UPDATE ${this._definition.table} SET ${sets}, updated_at = ? WHERE ${pk} = ?`, [...Object.values(changes), now, this._attributes[pk]]);
|
|
23590
24074
|
} else {
|
|
@@ -23596,11 +24080,13 @@ class ModelInstance {
|
|
|
23596
24080
|
const attrs = this._definition.attributes;
|
|
23597
24081
|
const data = {};
|
|
23598
24082
|
for (const [key, attr] of Object.entries(attrs)) {
|
|
23599
|
-
if (attr.
|
|
24083
|
+
if (attr.guarded)
|
|
24084
|
+
continue;
|
|
24085
|
+
if (this._attributes[key] !== undefined) {
|
|
23600
24086
|
data[key] = this._attributes[key];
|
|
23601
24087
|
}
|
|
23602
24088
|
}
|
|
23603
|
-
if (this._definition
|
|
24089
|
+
if (timestampsEnabled(this._definition)) {
|
|
23604
24090
|
const now = formatNow();
|
|
23605
24091
|
data.created_at = now;
|
|
23606
24092
|
data.updated_at = now;
|
|
@@ -23646,14 +24132,31 @@ class ModelInstance {
|
|
|
23646
24132
|
if (!pkValue)
|
|
23647
24133
|
throw new Error("Cannot delete a model without a primary key");
|
|
23648
24134
|
await hooks?.beforeDelete?.(this);
|
|
23649
|
-
if (this._definition
|
|
23650
|
-
|
|
24135
|
+
if (softDeletesEnabled(this._definition)) {
|
|
24136
|
+
const now = formatNow();
|
|
24137
|
+
await exec.run(`UPDATE ${this._definition.table} SET deleted_at = ? WHERE ${pk} = ?`, [now, pkValue]);
|
|
24138
|
+
this._attributes[SOFT_DELETE_COLUMN] = now;
|
|
23651
24139
|
} else {
|
|
23652
24140
|
await exec.run(`DELETE FROM ${this._definition.table} WHERE ${pk} = ?`, [pkValue]);
|
|
23653
24141
|
}
|
|
23654
24142
|
await hooks?.afterDelete?.(this);
|
|
23655
24143
|
return true;
|
|
23656
24144
|
}
|
|
24145
|
+
async restore() {
|
|
24146
|
+
if (!softDeletesEnabled(this._definition))
|
|
24147
|
+
throw new Error(`[orm] restore() requires soft deletes on '${this._definition.name}'`);
|
|
24148
|
+
const pk = this._definition.primaryKey || "id";
|
|
24149
|
+
const pkValue = this._attributes[pk];
|
|
24150
|
+
if (!pkValue)
|
|
24151
|
+
throw new Error("Cannot restore a model without a primary key");
|
|
24152
|
+
await getExecutor().run(`UPDATE ${this._definition.table} SET ${SOFT_DELETE_COLUMN} = ? WHERE ${pk} = ?`, [null, pkValue]);
|
|
24153
|
+
this._attributes[SOFT_DELETE_COLUMN] = null;
|
|
24154
|
+
this._original = null;
|
|
24155
|
+
return this;
|
|
24156
|
+
}
|
|
24157
|
+
trashed() {
|
|
24158
|
+
return this._attributes[SOFT_DELETE_COLUMN] != null;
|
|
24159
|
+
}
|
|
23657
24160
|
async refresh() {
|
|
23658
24161
|
const exec = getExecutor();
|
|
23659
24162
|
const pk = this._definition.primaryKey || "id";
|
|
@@ -23958,9 +24461,23 @@ class BelongsToManyRelationBuilder {
|
|
|
23958
24461
|
this._offset = n2;
|
|
23959
24462
|
return this;
|
|
23960
24463
|
}
|
|
24464
|
+
relatedSelectColumns() {
|
|
24465
|
+
const cols = new Set([this.relatedPk, ...Object.keys(this._relatedDef.attributes ?? {})]);
|
|
24466
|
+
const t2 = this._relatedDef.traits;
|
|
24467
|
+
if (t2?.useTimestamps || t2?.timestampable) {
|
|
24468
|
+
cols.add("created_at");
|
|
24469
|
+
cols.add("updated_at");
|
|
24470
|
+
}
|
|
24471
|
+
if (t2?.useSoftDeletes || t2?.softDeletable)
|
|
24472
|
+
cols.add("deleted_at");
|
|
24473
|
+
if (t2?.useUuid)
|
|
24474
|
+
cols.add("uuid");
|
|
24475
|
+
return [...cols];
|
|
24476
|
+
}
|
|
23961
24477
|
buildSelect() {
|
|
23962
24478
|
const params = [];
|
|
23963
|
-
|
|
24479
|
+
const relatedSelect = this.relatedSelectColumns().map((c2) => `${this.relatedTable}.${c2} AS ${BTM_RELATED_ALIAS}${c2}`).join(", ");
|
|
24480
|
+
let sql2 = `SELECT ${relatedSelect}, ${this.pivotTable}.* FROM ${this.relatedTable}`;
|
|
23964
24481
|
sql2 += ` INNER JOIN ${this.pivotTable} ON ${this.pivotTable}.${this.fkRelated} = ${this.relatedTable}.${this.relatedPk}`;
|
|
23965
24482
|
sql2 += ` WHERE ${this.pivotTable}.${this.fkParent} = ?`;
|
|
23966
24483
|
params.push(this.parentId);
|
|
@@ -23981,25 +24498,17 @@ class BelongsToManyRelationBuilder {
|
|
|
23981
24498
|
return { sql: sql2, params };
|
|
23982
24499
|
}
|
|
23983
24500
|
hydrateRows(rows) {
|
|
23984
|
-
const relatedAttrs = new Set(Object.keys(this._relatedDef.attributes ?? {}));
|
|
23985
|
-
relatedAttrs.add(this.relatedPk);
|
|
23986
|
-
relatedAttrs.add("created_at");
|
|
23987
|
-
relatedAttrs.add("updated_at");
|
|
23988
|
-
relatedAttrs.add("deleted_at");
|
|
23989
24501
|
const fkParent = this.fkParent;
|
|
23990
24502
|
const fkRelated = this.fkRelated;
|
|
23991
24503
|
return rows.map((raw) => {
|
|
23992
24504
|
const relatedRow = {};
|
|
23993
24505
|
const pivotExtras = {};
|
|
23994
24506
|
for (const [k2, v2] of Object.entries(raw)) {
|
|
23995
|
-
if (k2
|
|
23996
|
-
|
|
23997
|
-
if (
|
|
23998
|
-
relatedRow[k2] = v2;
|
|
23999
|
-
else
|
|
24507
|
+
if (k2.startsWith(BTM_RELATED_ALIAS))
|
|
24508
|
+
relatedRow[k2.slice(BTM_RELATED_ALIAS.length)] = v2;
|
|
24509
|
+
else if (k2 !== fkParent && k2 !== fkRelated)
|
|
24000
24510
|
pivotExtras[k2] = v2;
|
|
24001
24511
|
}
|
|
24002
|
-
relatedRow[this.relatedPk] = raw[this.relatedPk];
|
|
24003
24512
|
const inst = new ModelInstance(this._relatedDef, relatedRow);
|
|
24004
24513
|
inst.pivot = pivotExtras;
|
|
24005
24514
|
return inst;
|
|
@@ -24146,9 +24655,18 @@ class ModelQueryBuilder {
|
|
|
24146
24655
|
_offset;
|
|
24147
24656
|
_select = ["*"];
|
|
24148
24657
|
_withRelations = [];
|
|
24658
|
+
_trashed = "exclude";
|
|
24149
24659
|
constructor(definition) {
|
|
24150
24660
|
this._definition = definition;
|
|
24151
24661
|
}
|
|
24662
|
+
withTrashed() {
|
|
24663
|
+
this._trashed = "include";
|
|
24664
|
+
return this;
|
|
24665
|
+
}
|
|
24666
|
+
onlyTrashed() {
|
|
24667
|
+
this._trashed = "only";
|
|
24668
|
+
return this;
|
|
24669
|
+
}
|
|
24152
24670
|
where(column, operatorOrValue, value) {
|
|
24153
24671
|
if (value === undefined) {
|
|
24154
24672
|
this._wheres.push({ column, operator: "=", value: operatorOrValue, boolean: "and" });
|
|
@@ -24326,11 +24844,24 @@ class ModelQueryBuilder {
|
|
|
24326
24844
|
}
|
|
24327
24845
|
return clauses.join(" ");
|
|
24328
24846
|
}
|
|
24847
|
+
softDeleteClause() {
|
|
24848
|
+
if (this._trashed === "include" || !softDeletesEnabled(this._definition))
|
|
24849
|
+
return "";
|
|
24850
|
+
return this._trashed === "only" ? `${SOFT_DELETE_COLUMN} IS NOT NULL` : `${SOFT_DELETE_COLUMN} IS NULL`;
|
|
24851
|
+
}
|
|
24852
|
+
composeWhere(params) {
|
|
24853
|
+
const userClause = this._wheres.length > 0 ? this.buildWhereClauses(params) : "";
|
|
24854
|
+
const sd = this.softDeleteClause();
|
|
24855
|
+
if (userClause && sd)
|
|
24856
|
+
return `(${userClause}) AND ${sd}`;
|
|
24857
|
+
return userClause || sd;
|
|
24858
|
+
}
|
|
24329
24859
|
buildQuery() {
|
|
24330
24860
|
const params = [];
|
|
24331
24861
|
let sql2 = `SELECT ${this._select.join(", ")} FROM ${this._definition.table}`;
|
|
24332
|
-
|
|
24333
|
-
|
|
24862
|
+
const whereBody = this.composeWhere(params);
|
|
24863
|
+
if (whereBody) {
|
|
24864
|
+
sql2 += ` WHERE ${whereBody}`;
|
|
24334
24865
|
}
|
|
24335
24866
|
if (this._orderBy.length > 0) {
|
|
24336
24867
|
sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${o2.column} ${o2.direction.toUpperCase()}`).join(", ")}`;
|
|
@@ -24492,8 +25023,9 @@ class ModelQueryBuilder {
|
|
|
24492
25023
|
const exec = getExecutor();
|
|
24493
25024
|
const params = [];
|
|
24494
25025
|
let sql2 = `SELECT COUNT(*) as count FROM ${this._definition.table}`;
|
|
24495
|
-
|
|
24496
|
-
|
|
25026
|
+
const whereBody = this.composeWhere(params);
|
|
25027
|
+
if (whereBody) {
|
|
25028
|
+
sql2 += ` WHERE ${whereBody}`;
|
|
24497
25029
|
}
|
|
24498
25030
|
const row = await exec.get(sql2, params);
|
|
24499
25031
|
return Number(row?.count ?? 0);
|
|
@@ -24518,7 +25050,7 @@ class ModelQueryBuilder {
|
|
|
24518
25050
|
const exec = getExecutor();
|
|
24519
25051
|
const params = [amount];
|
|
24520
25052
|
let sql2 = `UPDATE ${this._definition.table} SET ${column} = ${column} + ?`;
|
|
24521
|
-
if (this._definition
|
|
25053
|
+
if (timestampsEnabled(this._definition)) {
|
|
24522
25054
|
sql2 += `, updated_at = ?`;
|
|
24523
25055
|
params.push(formatNow());
|
|
24524
25056
|
}
|
|
@@ -24539,6 +25071,7 @@ class ModelQueryBuilder {
|
|
|
24539
25071
|
builder._orderBy = [...this._orderBy];
|
|
24540
25072
|
builder._select = [...this._select];
|
|
24541
25073
|
builder._withRelations = [...this._withRelations];
|
|
25074
|
+
builder._trashed = this._trashed;
|
|
24542
25075
|
builder._limit = size;
|
|
24543
25076
|
builder._offset = page * size;
|
|
24544
25077
|
const results = await builder.get();
|
|
@@ -24575,8 +25108,9 @@ class ModelQueryBuilder {
|
|
|
24575
25108
|
const exec = getExecutor();
|
|
24576
25109
|
const params = [];
|
|
24577
25110
|
let sql2 = `SELECT ${column} FROM ${this._definition.table}`;
|
|
24578
|
-
|
|
24579
|
-
|
|
25111
|
+
const whereBody = this.composeWhere(params);
|
|
25112
|
+
if (whereBody) {
|
|
25113
|
+
sql2 += ` WHERE ${whereBody}`;
|
|
24580
25114
|
}
|
|
24581
25115
|
if (this._orderBy.length > 0) {
|
|
24582
25116
|
sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${o2.column} ${o2.direction.toUpperCase()}`).join(", ")}`;
|
|
@@ -24593,8 +25127,9 @@ class ModelQueryBuilder {
|
|
|
24593
25127
|
const exec = getExecutor();
|
|
24594
25128
|
const params = [];
|
|
24595
25129
|
let sql2 = `SELECT ${fn}(${column}) as v FROM ${this._definition.table}`;
|
|
24596
|
-
|
|
24597
|
-
|
|
25130
|
+
const whereBody = this.composeWhere(params);
|
|
25131
|
+
if (whereBody) {
|
|
25132
|
+
sql2 += ` WHERE ${whereBody}`;
|
|
24598
25133
|
}
|
|
24599
25134
|
const row = await exec.get(sql2, params);
|
|
24600
25135
|
return row?.v == null ? null : Number(row.v);
|
|
@@ -24625,10 +25160,10 @@ class ModelQueryBuilder {
|
|
|
24625
25160
|
const entries = Object.entries(data);
|
|
24626
25161
|
const sets = entries.map(([k2]) => `${k2} = ?`).join(", ");
|
|
24627
25162
|
const params = entries.map(([, v2]) => v2);
|
|
24628
|
-
if (this._definition
|
|
25163
|
+
if (timestampsEnabled(this._definition)) {
|
|
24629
25164
|
params.push(formatNow());
|
|
24630
25165
|
}
|
|
24631
|
-
let sql2 = `UPDATE ${this._definition.table} SET ${sets}${this._definition
|
|
25166
|
+
let sql2 = `UPDATE ${this._definition.table} SET ${sets}${timestampsEnabled(this._definition) ? ", updated_at = ?" : ""}`;
|
|
24632
25167
|
if (this._wheres.length > 0) {
|
|
24633
25168
|
sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
|
|
24634
25169
|
}
|
|
@@ -24674,10 +25209,13 @@ function createModel(definition) {
|
|
|
24674
25209
|
limit: (count) => new ModelQueryBuilder(definition).limit(count),
|
|
24675
25210
|
take: (count) => new ModelQueryBuilder(definition).take(count),
|
|
24676
25211
|
skip: (count) => new ModelQueryBuilder(definition).skip(count),
|
|
25212
|
+
withTrashed: () => new ModelQueryBuilder(definition).withTrashed(),
|
|
25213
|
+
onlyTrashed: () => new ModelQueryBuilder(definition).onlyTrashed(),
|
|
24677
25214
|
async find(id) {
|
|
24678
25215
|
const exec = getExecutor();
|
|
24679
25216
|
const pk = definition.primaryKey || "id";
|
|
24680
|
-
const
|
|
25217
|
+
const sd = softDeletesEnabled(definition) ? ` AND ${SOFT_DELETE_COLUMN} IS NULL` : "";
|
|
25218
|
+
const row = await exec.get(`SELECT * FROM ${definition.table} WHERE ${pk} = ?${sd}`, [id]);
|
|
24681
25219
|
return row ? new ModelInstance(definition, row) : undefined;
|
|
24682
25220
|
},
|
|
24683
25221
|
async findOrFail(id) {
|
|
@@ -24689,7 +25227,8 @@ function createModel(definition) {
|
|
|
24689
25227
|
async findMany(ids) {
|
|
24690
25228
|
const exec = getExecutor();
|
|
24691
25229
|
const pk = definition.primaryKey || "id";
|
|
24692
|
-
const
|
|
25230
|
+
const sd = softDeletesEnabled(definition) ? ` AND ${SOFT_DELETE_COLUMN} IS NULL` : "";
|
|
25231
|
+
const rows = await exec.all(`SELECT * FROM ${definition.table} WHERE ${pk} IN (${ids.map(() => "?").join(", ")})${sd}`, ids);
|
|
24693
25232
|
return rows.map((row) => new ModelInstance(definition, row));
|
|
24694
25233
|
},
|
|
24695
25234
|
all: () => new ModelQueryBuilder(definition).get(),
|
|
@@ -24804,10 +25343,10 @@ async function createTableFromModel(definition) {
|
|
|
24804
25343
|
}
|
|
24805
25344
|
columns.push(colDef);
|
|
24806
25345
|
}
|
|
24807
|
-
if (definition
|
|
25346
|
+
if (timestampsEnabled(definition)) {
|
|
24808
25347
|
columns.push("created_at TEXT", "updated_at TEXT");
|
|
24809
25348
|
}
|
|
24810
|
-
if (definition
|
|
25349
|
+
if (softDeletesEnabled(definition)) {
|
|
24811
25350
|
columns.push("deleted_at TEXT");
|
|
24812
25351
|
}
|
|
24813
25352
|
await exec.run(`CREATE TABLE IF NOT EXISTS ${definition.table} (${columns.join(", ")})`, []);
|
|
@@ -24852,7 +25391,7 @@ async function seedModel(definition, count, faker) {
|
|
|
24852
25391
|
if (attr.factory)
|
|
24853
25392
|
data[name] = attr.factory(faker);
|
|
24854
25393
|
}
|
|
24855
|
-
if (definition
|
|
25394
|
+
if (timestampsEnabled(definition)) {
|
|
24856
25395
|
const now = formatNow();
|
|
24857
25396
|
data.created_at = now;
|
|
24858
25397
|
data.updated_at = now;
|
|
@@ -24863,7 +25402,7 @@ async function seedModel(definition, count, faker) {
|
|
|
24863
25402
|
await exec.run(`INSERT INTO ${definition.table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`, Object.values(data));
|
|
24864
25403
|
}
|
|
24865
25404
|
}
|
|
24866
|
-
var SAFE_SQL_IDENTIFIER, _getModel = null, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, snakeCaseCache, tableNameCache, relationCache;
|
|
25405
|
+
var SAFE_SQL_IDENTIFIER, _getModel = null, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, SOFT_DELETE_COLUMN = "deleted_at", BTM_RELATED_ALIAS = "__btm_rel__", snakeCaseCache, tableNameCache, relationCache;
|
|
24867
25406
|
var init_orm = __esm(() => {
|
|
24868
25407
|
init_config();
|
|
24869
25408
|
init_db();
|
|
@@ -26319,9 +26858,9 @@ function buildDatabaseSchema(models) {
|
|
|
26319
26858
|
// src/loader.ts
|
|
26320
26859
|
import { readdirSync as readdirSync12, statSync as statSync5 } from "fs";
|
|
26321
26860
|
import { basename, extname as extname4 } from "path";
|
|
26322
|
-
import
|
|
26861
|
+
import process31 from "process";
|
|
26323
26862
|
async function loadModels(options) {
|
|
26324
|
-
const cwd = options.cwd ??
|
|
26863
|
+
const cwd = options.cwd ?? process31.cwd();
|
|
26325
26864
|
const dir = options.modelsDir.startsWith("/") ? options.modelsDir : `${cwd}/${options.modelsDir}`;
|
|
26326
26865
|
const result = {};
|
|
26327
26866
|
const entries = readdirSync12(dir);
|
|
@@ -26348,6 +26887,23 @@ async function loadModels(options) {
|
|
|
26348
26887
|
}
|
|
26349
26888
|
var init_loader = () => {};
|
|
26350
26889
|
|
|
26890
|
+
// src/relation-utils.ts
|
|
26891
|
+
function normalizeRelationEntry(entry) {
|
|
26892
|
+
if (typeof entry === "string")
|
|
26893
|
+
return { model: entry };
|
|
26894
|
+
if (entry && typeof entry === "object" && typeof entry.model === "string") {
|
|
26895
|
+
const e2 = entry;
|
|
26896
|
+
return { model: e2.model, foreignKey: e2.foreignKey, onDelete: e2.onDelete };
|
|
26897
|
+
}
|
|
26898
|
+
return null;
|
|
26899
|
+
}
|
|
26900
|
+
function normalizeRelationList(rel) {
|
|
26901
|
+
if (!rel)
|
|
26902
|
+
return [];
|
|
26903
|
+
const entries = Array.isArray(rel) ? rel : typeof rel === "object" ? Object.values(rel) : [];
|
|
26904
|
+
return entries.map(normalizeRelationEntry).filter((x2) => x2 !== null);
|
|
26905
|
+
}
|
|
26906
|
+
|
|
26351
26907
|
// src/meta.ts
|
|
26352
26908
|
function buildSchemaMeta(models) {
|
|
26353
26909
|
const modelToTable = {};
|
|
@@ -26365,13 +26921,24 @@ function buildSchemaMeta(models) {
|
|
|
26365
26921
|
const toRecord = (v2) => {
|
|
26366
26922
|
if (!v2)
|
|
26367
26923
|
return {};
|
|
26924
|
+
const rec = {};
|
|
26368
26925
|
if (Array.isArray(v2)) {
|
|
26369
|
-
const
|
|
26370
|
-
|
|
26371
|
-
|
|
26926
|
+
for (const item of v2) {
|
|
26927
|
+
const n2 = normalizeRelationEntry(item);
|
|
26928
|
+
if (n2)
|
|
26929
|
+
rec[n2.model] = n2.model;
|
|
26930
|
+
}
|
|
26372
26931
|
return rec;
|
|
26373
26932
|
}
|
|
26374
|
-
|
|
26933
|
+
if (typeof v2 === "object") {
|
|
26934
|
+
for (const [key, val] of Object.entries(v2)) {
|
|
26935
|
+
const n2 = normalizeRelationEntry(val);
|
|
26936
|
+
if (n2)
|
|
26937
|
+
rec[key] = n2.model;
|
|
26938
|
+
}
|
|
26939
|
+
return rec;
|
|
26940
|
+
}
|
|
26941
|
+
return {};
|
|
26375
26942
|
};
|
|
26376
26943
|
const toBelongsToManyRecord = (v2) => {
|
|
26377
26944
|
if (!v2)
|
|
@@ -26418,11 +26985,12 @@ function buildSchemaMeta(models) {
|
|
|
26418
26985
|
}
|
|
26419
26986
|
return { modelToTable, tableToModel, primaryKeys, relations, scopes: scopesByTable, models };
|
|
26420
26987
|
}
|
|
26988
|
+
var init_meta = () => {};
|
|
26421
26989
|
|
|
26422
26990
|
// src/migrations.ts
|
|
26423
26991
|
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync14 } from "fs";
|
|
26424
26992
|
import { dirname as dirname11, join as join17 } from "path";
|
|
26425
|
-
import
|
|
26993
|
+
import process33 from "process";
|
|
26426
26994
|
function info2(message) {
|
|
26427
26995
|
if (config5.verbose)
|
|
26428
26996
|
console.log(message);
|
|
@@ -26438,10 +27006,10 @@ function findWorkspaceRoot6(startPath) {
|
|
|
26438
27006
|
}
|
|
26439
27007
|
currentPath = dirname11(currentPath);
|
|
26440
27008
|
}
|
|
26441
|
-
return
|
|
27009
|
+
return process33.cwd();
|
|
26442
27010
|
}
|
|
26443
27011
|
function ensureSqlDirectory2() {
|
|
26444
|
-
const workspaceRoot = findWorkspaceRoot6(
|
|
27012
|
+
const workspaceRoot = findWorkspaceRoot6(process33.cwd());
|
|
26445
27013
|
const sqlDir = join17(workspaceRoot, "database", "migrations");
|
|
26446
27014
|
if (!existsSync21(sqlDir)) {
|
|
26447
27015
|
mkdirSync9(sqlDir, { recursive: true });
|
|
@@ -26580,15 +27148,6 @@ function detectTypeFromValidationRule(rule) {
|
|
|
26580
27148
|
}
|
|
26581
27149
|
return;
|
|
26582
27150
|
}
|
|
26583
|
-
function normalizeBelongsTo(belongsTo) {
|
|
26584
|
-
if (!belongsTo)
|
|
26585
|
-
return [];
|
|
26586
|
-
if (Array.isArray(belongsTo))
|
|
26587
|
-
return belongsTo;
|
|
26588
|
-
if (typeof belongsTo === "object")
|
|
26589
|
-
return Object.values(belongsTo);
|
|
26590
|
-
return [];
|
|
26591
|
-
}
|
|
26592
27151
|
function buildMigrationPlan2(models, options) {
|
|
26593
27152
|
const meta = buildSchemaMeta(models);
|
|
26594
27153
|
const tables = [];
|
|
@@ -26677,12 +27236,12 @@ function buildMigrationPlan2(models, options) {
|
|
|
26677
27236
|
}
|
|
26678
27237
|
columns.push(col);
|
|
26679
27238
|
}
|
|
26680
|
-
const
|
|
26681
|
-
for (const
|
|
26682
|
-
const fkColumnName = `${snakeCase(
|
|
27239
|
+
const belongsToRelations = normalizeRelationList(model.belongsTo);
|
|
27240
|
+
for (const rel of belongsToRelations) {
|
|
27241
|
+
const fkColumnName = rel.foreignKey ?? `${snakeCase(rel.model)}_id`;
|
|
26683
27242
|
if (columns.some((c2) => c2.name === fkColumnName))
|
|
26684
27243
|
continue;
|
|
26685
|
-
const refTable = meta.modelToTable[
|
|
27244
|
+
const refTable = meta.modelToTable[rel.model];
|
|
26686
27245
|
if (!refTable)
|
|
26687
27246
|
continue;
|
|
26688
27247
|
const refPk = meta.primaryKeys[refTable] ?? "id";
|
|
@@ -26693,7 +27252,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
26693
27252
|
isUnique: false,
|
|
26694
27253
|
isNullable: true,
|
|
26695
27254
|
hasDefault: false,
|
|
26696
|
-
references: { table: refTable, column: refPk }
|
|
27255
|
+
references: { table: refTable, column: refPk, onDelete: rel.onDelete }
|
|
26697
27256
|
});
|
|
26698
27257
|
}
|
|
26699
27258
|
const traits = model.traits;
|
|
@@ -27010,6 +27569,13 @@ function columnsAreDifferent(col1, col2) {
|
|
|
27010
27569
|
}
|
|
27011
27570
|
return false;
|
|
27012
27571
|
}
|
|
27572
|
+
function referencesAreDifferent(r1, r2) {
|
|
27573
|
+
if (Boolean(r1) !== Boolean(r2))
|
|
27574
|
+
return true;
|
|
27575
|
+
if (!r1 || !r2)
|
|
27576
|
+
return false;
|
|
27577
|
+
return r1.table !== r2.table || r1.column !== r2.column || r1.onDelete !== r2.onDelete || r1.onUpdate !== r2.onUpdate;
|
|
27578
|
+
}
|
|
27013
27579
|
function mapIndexesByKey(indexes) {
|
|
27014
27580
|
const map = {};
|
|
27015
27581
|
for (const i2 of indexes) {
|
|
@@ -27153,6 +27719,13 @@ function generateDiffSql(previous, next) {
|
|
|
27153
27719
|
info2(`-- Detected column type change: ${curr.table}.${colName} (${prevCol.type} -> ${currCol.type})`);
|
|
27154
27720
|
hasChanges = true;
|
|
27155
27721
|
}
|
|
27722
|
+
if (referencesAreDifferent(prevCol.references, currCol.references) && currCol.references) {
|
|
27723
|
+
const addFkStatement = driver.addForeignKey(curr.table, currCol.name, currCol.references.table, currCol.references.column, currCol.references.onDelete, currCol.references.onUpdate);
|
|
27724
|
+
tableChanges.push(addFkStatement);
|
|
27725
|
+
chunks.push(addFkStatement);
|
|
27726
|
+
info2(`-- Detected foreign-key change: ${curr.table}.${colName} -> ${currCol.references.table}(${currCol.references.column})${currCol.references.onDelete ? ` ON DELETE ${currCol.references.onDelete}` : ""}`);
|
|
27727
|
+
hasChanges = true;
|
|
27728
|
+
}
|
|
27156
27729
|
}
|
|
27157
27730
|
}
|
|
27158
27731
|
for (const colName of Object.keys(currCols)) {
|
|
@@ -27206,6 +27779,7 @@ var migrationCounter = 0, migrationsCreatedCount = 0, migrationsUpdatedCount = 0
|
|
|
27206
27779
|
var init_migrations = __esm(() => {
|
|
27207
27780
|
init_config();
|
|
27208
27781
|
init_drivers();
|
|
27782
|
+
init_meta();
|
|
27209
27783
|
});
|
|
27210
27784
|
|
|
27211
27785
|
// src/schema.ts
|
|
@@ -27240,6 +27814,7 @@ var init_src2 = __esm(() => {
|
|
|
27240
27814
|
init_dynamodb_tooling_adapter();
|
|
27241
27815
|
init_dynamodb();
|
|
27242
27816
|
init_loader();
|
|
27817
|
+
init_meta();
|
|
27243
27818
|
init_migrations();
|
|
27244
27819
|
init_orm();
|
|
27245
27820
|
});
|
|
@@ -27249,7 +27824,7 @@ import { existsSync as existsSync23 } from "fs";
|
|
|
27249
27824
|
import fs from "fs/promises";
|
|
27250
27825
|
import os from "os";
|
|
27251
27826
|
import path from "path";
|
|
27252
|
-
import
|
|
27827
|
+
import process35 from "process";
|
|
27253
27828
|
import { EventEmitter } from "events";
|
|
27254
27829
|
import process52 from "process";
|
|
27255
27830
|
import process210 from "process";
|
|
@@ -27273,10 +27848,10 @@ class Telemetry {
|
|
|
27273
27848
|
this.configPath = path.join(configDir, "telemetry.json");
|
|
27274
27849
|
}
|
|
27275
27850
|
async isEnabled() {
|
|
27276
|
-
if (
|
|
27851
|
+
if (process35.env.DO_NOT_TRACK === "1" || process35.env.DO_NOT_TRACK === "true") {
|
|
27277
27852
|
return false;
|
|
27278
27853
|
}
|
|
27279
|
-
if (
|
|
27854
|
+
if (process35.env.NO_TELEMETRY === "1" || process35.env.NO_TELEMETRY === "true") {
|
|
27280
27855
|
return false;
|
|
27281
27856
|
}
|
|
27282
27857
|
const config6 = await this.loadConfig();
|
|
@@ -27305,7 +27880,7 @@ class Telemetry {
|
|
|
27305
27880
|
...data,
|
|
27306
27881
|
timestamp: Date.now(),
|
|
27307
27882
|
platform: os.platform(),
|
|
27308
|
-
nodeVersion:
|
|
27883
|
+
nodeVersion: process35.version
|
|
27309
27884
|
};
|
|
27310
27885
|
this.events.push(telemetryEvent);
|
|
27311
27886
|
if (this.events.length >= 10) {
|
|
@@ -27361,7 +27936,7 @@ class Telemetry {
|
|
|
27361
27936
|
const config6 = await this.loadConfig();
|
|
27362
27937
|
return {
|
|
27363
27938
|
enabled: config6.enabled,
|
|
27364
|
-
doNotTrack:
|
|
27939
|
+
doNotTrack: process35.env.DO_NOT_TRACK === "1" || process35.env.DO_NOT_TRACK === "true",
|
|
27365
27940
|
eventsQueued: this.events.length,
|
|
27366
27941
|
lastSent: config6.lastSent
|
|
27367
27942
|
};
|
|
@@ -29283,7 +29858,7 @@ function getPrefix() {
|
|
|
29283
29858
|
}
|
|
29284
29859
|
var prefix = getPrefix();
|
|
29285
29860
|
// package.json
|
|
29286
|
-
var version2 = "0.1.
|
|
29861
|
+
var version2 = "0.1.26";
|
|
29287
29862
|
|
|
29288
29863
|
// bin/cli.ts
|
|
29289
29864
|
init_actions();
|
|
@@ -29309,6 +29884,15 @@ var cli = new CLI("query-builder");
|
|
|
29309
29884
|
cli.command("introspect <dir>", "Load models and print inferred schema").option("--verbose", "Enable verbose logging").example("query-builder introspect ./app/Models --verbose").action(async (dir, _options) => {
|
|
29310
29885
|
await introspect(dir, _options);
|
|
29311
29886
|
});
|
|
29887
|
+
cli.command("introspect:db", "Reverse-introspect the live database into defineModel() source (#1047)").option("--table <table>", "Limit to a single table (repeatable)").example("query-builder introspect:db > app/Models/generated.ts").action(async (_options) => {
|
|
29888
|
+
const t2 = _options?.table;
|
|
29889
|
+
const tables = t2 ? Array.isArray(t2) ? t2 : [t2] : undefined;
|
|
29890
|
+
const models = await introspectDatabase({ tables });
|
|
29891
|
+
console.log(`import { defineModel } from 'bun-query-builder'
|
|
29892
|
+
|
|
29893
|
+
${models.map((m2) => m2.source).join(`
|
|
29894
|
+
`)}`);
|
|
29895
|
+
});
|
|
29312
29896
|
cli.command("sql <dir> <table>", "Build a sample query for a table").option("--limit <n>", "Limit rows", { default: 10 }).example("query-builder sql ./app/Models users --limit 5").action(async (dir, table, opts) => {
|
|
29313
29897
|
await sql(dir, table, opts);
|
|
29314
29898
|
});
|