bun-query-builder 0.1.20 → 0.1.23
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/data.d.ts +8 -0
- package/dist/bin/cli.js +198 -82
- package/dist/browser.d.ts +17 -6
- package/dist/drivers/mysql.d.ts +13 -1
- package/dist/drivers/postgres.d.ts +13 -1
- package/dist/drivers/sqlite.d.ts +13 -1
- package/dist/index.d.ts +5 -5
- package/dist/model.d.ts +12 -0
- package/dist/src/index.js +216 -87
- package/package.json +3 -3
- package/LICENSE.md +0 -21
package/dist/src/index.js
CHANGED
|
@@ -11663,13 +11663,22 @@ async function getConfig() {
|
|
|
11663
11663
|
return _config;
|
|
11664
11664
|
}
|
|
11665
11665
|
function setConfig(userConfig) {
|
|
11666
|
-
|
|
11667
|
-
|
|
11668
|
-
|
|
11669
|
-
|
|
11670
|
-
if (userConfig.
|
|
11671
|
-
|
|
11672
|
-
|
|
11666
|
+
Object.assign(config5, userConfig);
|
|
11667
|
+
if (userConfig.database) {
|
|
11668
|
+
config5.database = { ...config5.database, ...userConfig.database };
|
|
11669
|
+
}
|
|
11670
|
+
if (userConfig.timestamps) {
|
|
11671
|
+
config5.timestamps = { ...config5.timestamps, ...userConfig.timestamps };
|
|
11672
|
+
}
|
|
11673
|
+
if (userConfig.pagination) {
|
|
11674
|
+
config5.pagination = { ...config5.pagination, ...userConfig.pagination };
|
|
11675
|
+
}
|
|
11676
|
+
if (userConfig.softDeletes) {
|
|
11677
|
+
config5.softDeletes = { ...config5.softDeletes, ...userConfig.softDeletes };
|
|
11678
|
+
}
|
|
11679
|
+
if (_config) {
|
|
11680
|
+
Object.assign(_config, config5);
|
|
11681
|
+
}
|
|
11673
11682
|
}
|
|
11674
11683
|
var defaultConfig4, config5, _config = null;
|
|
11675
11684
|
var init_config = __esm(() => {
|
|
@@ -12368,6 +12377,7 @@ function createQueryBuilder(state) {
|
|
|
12368
12377
|
function applyCondition(expr) {
|
|
12369
12378
|
if (Array.isArray(expr)) {
|
|
12370
12379
|
const [col, op, val] = expr;
|
|
12380
|
+
validateIdentifier(col, "where(column)");
|
|
12371
12381
|
const colName = String(col);
|
|
12372
12382
|
switch (op) {
|
|
12373
12383
|
case "in":
|
|
@@ -12385,9 +12395,12 @@ function createQueryBuilder(state) {
|
|
|
12385
12395
|
case "like":
|
|
12386
12396
|
return _sql.unsafe(`${colName} LIKE ${getPlaceholder(1)}`, [val]);
|
|
12387
12397
|
case "is":
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12398
|
+
case "is not": {
|
|
12399
|
+
if (val !== null && val !== undefined) {
|
|
12400
|
+
throw new TypeError(`[query-builder] where(..., '${op}', ?): operator '${op}' only accepts NULL/undefined as value, got ${typeof val} (${String(val)})`);
|
|
12401
|
+
}
|
|
12402
|
+
return _sql.unsafe(`${colName} IS ${op === "is not" ? "NOT " : ""}NULL`);
|
|
12403
|
+
}
|
|
12391
12404
|
case "!=":
|
|
12392
12405
|
return _sql.unsafe(`${colName} <> ${getPlaceholder(1)}`, [val]);
|
|
12393
12406
|
case "<":
|
|
@@ -12395,8 +12408,9 @@ function createQueryBuilder(state) {
|
|
|
12395
12408
|
case "<=":
|
|
12396
12409
|
case ">=":
|
|
12397
12410
|
case "=":
|
|
12398
|
-
default:
|
|
12399
12411
|
return _sql.unsafe(`${colName} ${op} ${getPlaceholder(1)}`, [val]);
|
|
12412
|
+
default:
|
|
12413
|
+
throw new TypeError(`[query-builder] where(..., '${String(op)}', ?): unsupported operator. Allowed: =, !=, <>, <, <=, >, >=, like, in, not in, is, is not`);
|
|
12400
12414
|
}
|
|
12401
12415
|
}
|
|
12402
12416
|
if ("raw" in expr) {
|
|
@@ -12409,6 +12423,7 @@ function createQueryBuilder(state) {
|
|
|
12409
12423
|
const allParams = [];
|
|
12410
12424
|
let paramIndex = 1;
|
|
12411
12425
|
for (const key of keys) {
|
|
12426
|
+
validateIdentifier(key, "where(object key)");
|
|
12412
12427
|
const value = expr[key];
|
|
12413
12428
|
if (Array.isArray(value)) {
|
|
12414
12429
|
const placeholders = getPlaceholders(value.length, paramIndex);
|
|
@@ -12597,17 +12612,17 @@ function createQueryBuilder(state) {
|
|
|
12597
12612
|
return;
|
|
12598
12613
|
const parentTable = String(table);
|
|
12599
12614
|
const parentPk = meta.primaryKeys[parentTable] ?? "id";
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12615
|
+
validateIdentifier2(resolved.pivotTable, "wherePivot auto-join (pivot table)");
|
|
12616
|
+
validateIdentifier2(resolved.fkParent, "wherePivot auto-join (parent FK)");
|
|
12617
|
+
validateIdentifier2(parentTable, "wherePivot auto-join (parent table)");
|
|
12618
|
+
validateIdentifier2(parentPk, "wherePivot auto-join (parent PK)");
|
|
12604
12619
|
built = sql`${ensureBuilt()} LEFT JOIN ${sql(resolved.pivotTable)} ON ${sql(`${resolved.pivotTable}.${resolved.fkParent}`)} = ${sql(`${parentTable}.${parentPk}`)}`;
|
|
12605
12620
|
text = `${text} LEFT JOIN ${resolved.pivotTable} ON ${resolved.pivotTable}.${resolved.fkParent} = ${parentTable}.${parentPk}`;
|
|
12606
12621
|
joinedTables.add(resolved.pivotTable);
|
|
12607
12622
|
};
|
|
12608
12623
|
const whereConditions = [];
|
|
12609
12624
|
const whereParams = [];
|
|
12610
|
-
const
|
|
12625
|
+
const validateIdentifier2 = (name, context) => {
|
|
12611
12626
|
if (!SQL_PATTERNS.IDENTIFIER.test(name)) {
|
|
12612
12627
|
const contextMsg = context ? ` in ${context}` : "";
|
|
12613
12628
|
throw new Error(`[query-builder] Invalid identifier${contextMsg}: '${name}'. Identifiers must start with a letter or underscore and contain only alphanumeric characters, underscores, and dots.`);
|
|
@@ -12631,18 +12646,56 @@ function createQueryBuilder(state) {
|
|
|
12631
12646
|
}
|
|
12632
12647
|
}
|
|
12633
12648
|
};
|
|
12649
|
+
const SAFE_WHERE_OPERATORS = new Set([
|
|
12650
|
+
"=",
|
|
12651
|
+
"!=",
|
|
12652
|
+
"<>",
|
|
12653
|
+
"<",
|
|
12654
|
+
"<=",
|
|
12655
|
+
">",
|
|
12656
|
+
">=",
|
|
12657
|
+
"like",
|
|
12658
|
+
"not like",
|
|
12659
|
+
"ilike",
|
|
12660
|
+
"not ilike",
|
|
12661
|
+
"in",
|
|
12662
|
+
"not in",
|
|
12663
|
+
"is",
|
|
12664
|
+
"is not",
|
|
12665
|
+
"between",
|
|
12666
|
+
"not between"
|
|
12667
|
+
]);
|
|
12668
|
+
function assertSafeWhereOperator(op, context) {
|
|
12669
|
+
if (typeof op !== "string")
|
|
12670
|
+
throw new TypeError(`[query-builder] ${context}: operator must be a string, got ${typeof op}`);
|
|
12671
|
+
const lower = op.toLowerCase();
|
|
12672
|
+
if (!SAFE_WHERE_OPERATORS.has(lower))
|
|
12673
|
+
throw new TypeError(`[query-builder] ${context}: refusing to use '${op}' as a SQL operator \u2014 not in the allowed set (${[...SAFE_WHERE_OPERATORS].join(", ")})`);
|
|
12674
|
+
return op;
|
|
12675
|
+
}
|
|
12676
|
+
function formatSubqueryValue(val) {
|
|
12677
|
+
if (val === null)
|
|
12678
|
+
return "NULL";
|
|
12679
|
+
if (typeof val === "number" && Number.isFinite(val))
|
|
12680
|
+
return String(val);
|
|
12681
|
+
if (typeof val === "boolean")
|
|
12682
|
+
return val ? "1" : "0";
|
|
12683
|
+
if (typeof val === "string")
|
|
12684
|
+
return `'${val.replace(/'/g, "''")}'`;
|
|
12685
|
+
throw new TypeError(`[query-builder] subquery condition: refusing to interpolate value of type ${typeof val}`);
|
|
12686
|
+
}
|
|
12634
12687
|
const buildHasSubquery = (parentTable, targetTable, pk, callback) => {
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12688
|
+
validateIdentifier2(parentTable, "relationship subquery (parent table)");
|
|
12689
|
+
validateIdentifier2(targetTable, "relationship subquery (target table)");
|
|
12690
|
+
validateIdentifier2(pk, "relationship subquery (primary key)");
|
|
12638
12691
|
const fk = `${parentTable.endsWith("s") ? parentTable.slice(0, -1) : parentTable}_id`;
|
|
12639
|
-
|
|
12692
|
+
validateIdentifier2(fk, "relationship subquery (foreign key)");
|
|
12640
12693
|
let subquerySQL = `SELECT 1 FROM ${targetTable} WHERE ${targetTable}.${fk} = ${parentTable}.${pk}`;
|
|
12641
12694
|
if (callback) {
|
|
12642
12695
|
const subQb = {
|
|
12643
12696
|
where: (col, op, val) => {
|
|
12644
|
-
|
|
12645
|
-
return `${targetTable}.${col} ${op
|
|
12697
|
+
validateIdentifier2(col, "relationship subquery condition");
|
|
12698
|
+
return `${targetTable}.${col} ${assertSafeWhereOperator(op, "whereHas callback")} ${formatSubqueryValue(val)}`;
|
|
12646
12699
|
}
|
|
12647
12700
|
};
|
|
12648
12701
|
const condition = callback(subQb);
|
|
@@ -12653,16 +12706,16 @@ function createQueryBuilder(state) {
|
|
|
12653
12706
|
return subquerySQL;
|
|
12654
12707
|
};
|
|
12655
12708
|
const buildBelongsToSubquery = (parentTable, targetTable, pk, callback) => {
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12709
|
+
validateIdentifier2(parentTable, "relationship subquery (parent table)");
|
|
12710
|
+
validateIdentifier2(targetTable, "relationship subquery (target table)");
|
|
12711
|
+
validateIdentifier2(pk, "relationship subquery (primary key)");
|
|
12659
12712
|
const fk = `${targetTable.endsWith("s") ? targetTable.slice(0, -1) : targetTable}_id`;
|
|
12660
|
-
|
|
12713
|
+
validateIdentifier2(fk, "relationship subquery (foreign key)");
|
|
12661
12714
|
let subquerySQL = `SELECT 1 FROM ${targetTable} WHERE ${targetTable}.${pk} = ${parentTable}.${fk}`;
|
|
12662
12715
|
if (callback) {
|
|
12663
12716
|
const subQb = {
|
|
12664
12717
|
where: (col, op, val) => {
|
|
12665
|
-
|
|
12718
|
+
validateIdentifier2(col, "relationship subquery condition");
|
|
12666
12719
|
return `${targetTable}.${col} ${op} ${typeof val === "string" ? `'${val}'` : val}`;
|
|
12667
12720
|
}
|
|
12668
12721
|
};
|
|
@@ -12674,24 +12727,24 @@ function createQueryBuilder(state) {
|
|
|
12674
12727
|
return subquerySQL;
|
|
12675
12728
|
};
|
|
12676
12729
|
const buildBelongsToManySubquery = (parentTable, targetTable, pk, targetPk, callback, relationKey) => {
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
|
|
12680
|
-
|
|
12730
|
+
validateIdentifier2(parentTable, "relationship subquery (parent table)");
|
|
12731
|
+
validateIdentifier2(targetTable, "relationship subquery (target table)");
|
|
12732
|
+
validateIdentifier2(pk, "relationship subquery (primary key)");
|
|
12733
|
+
validateIdentifier2(targetPk, "relationship subquery (target primary key)");
|
|
12681
12734
|
const resolved = relationKey && meta ? resolvePivot(meta, parentTable, relationKey, { singularize, models: meta.models }) : null;
|
|
12682
12735
|
const a = singularize(parentTable);
|
|
12683
12736
|
const b = singularize(targetTable);
|
|
12684
12737
|
const pivot = resolved?.pivotTable ?? [a, b].sort().join("_");
|
|
12685
12738
|
const fkA = resolved?.fkParent ?? `${a}_id`;
|
|
12686
12739
|
const fkB = resolved?.fkRelated ?? `${b}_id`;
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12740
|
+
validateIdentifier2(pivot, "relationship subquery (pivot table)");
|
|
12741
|
+
validateIdentifier2(fkA, "relationship subquery (foreign key A)");
|
|
12742
|
+
validateIdentifier2(fkB, "relationship subquery (foreign key B)");
|
|
12690
12743
|
let subquerySQL = `SELECT 1 FROM ${pivot} JOIN ${targetTable} ON ${targetTable}.${targetPk} = ${pivot}.${fkB} WHERE ${pivot}.${fkA} = ${parentTable}.${pk}`;
|
|
12691
12744
|
if (callback) {
|
|
12692
12745
|
const subQb = {
|
|
12693
12746
|
where: (col, op, val) => {
|
|
12694
|
-
|
|
12747
|
+
validateIdentifier2(col, "relationship subquery condition");
|
|
12695
12748
|
return `${targetTable}.${col} ${op} ${typeof val === "string" ? `'${val}'` : val}`;
|
|
12696
12749
|
}
|
|
12697
12750
|
};
|
|
@@ -12703,24 +12756,24 @@ function createQueryBuilder(state) {
|
|
|
12703
12756
|
return subquerySQL;
|
|
12704
12757
|
};
|
|
12705
12758
|
const buildHasCountSubquery = (parentTable, targetTable, pk) => {
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12759
|
+
validateIdentifier2(parentTable, "withCount (parent table)");
|
|
12760
|
+
validateIdentifier2(targetTable, "withCount (target table)");
|
|
12761
|
+
validateIdentifier2(pk, "withCount (primary key)");
|
|
12709
12762
|
const fk = `${parentTable.endsWith("s") ? parentTable.slice(0, -1) : parentTable}_id`;
|
|
12710
|
-
|
|
12763
|
+
validateIdentifier2(fk, "withCount (foreign key)");
|
|
12711
12764
|
return `(SELECT COUNT(*) FROM ${targetTable} WHERE ${targetTable}.${fk} = ${parentTable}.${pk})`;
|
|
12712
12765
|
};
|
|
12713
12766
|
const buildBelongsToManyCountSubquery = (parentTable, targetTable, pk, relationKey) => {
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12767
|
+
validateIdentifier2(parentTable, "withCount (parent table)");
|
|
12768
|
+
validateIdentifier2(targetTable, "withCount (target table)");
|
|
12769
|
+
validateIdentifier2(pk, "withCount (primary key)");
|
|
12717
12770
|
const resolved = relationKey && meta ? resolvePivot(meta, parentTable, relationKey, { singularize, models: meta.models }) : null;
|
|
12718
12771
|
const a = singularize(parentTable);
|
|
12719
12772
|
const b = singularize(targetTable);
|
|
12720
12773
|
const pivot = resolved?.pivotTable ?? [a, b].sort().join("_");
|
|
12721
12774
|
const fkA = resolved?.fkParent ?? `${a}_id`;
|
|
12722
|
-
|
|
12723
|
-
|
|
12775
|
+
validateIdentifier2(pivot, "withCount (pivot table)");
|
|
12776
|
+
validateIdentifier2(fkA, "withCount (foreign key)");
|
|
12724
12777
|
return `(SELECT COUNT(*) FROM ${pivot} WHERE ${pivot}.${fkA} = ${parentTable}.${pk})`;
|
|
12725
12778
|
};
|
|
12726
12779
|
const applyPivotColumnsToQuery = () => {
|
|
@@ -12732,7 +12785,7 @@ function createQueryBuilder(state) {
|
|
|
12732
12785
|
if (!resolved)
|
|
12733
12786
|
continue;
|
|
12734
12787
|
for (const col of columns2) {
|
|
12735
|
-
|
|
12788
|
+
validateIdentifier2(col, "withPivot");
|
|
12736
12789
|
}
|
|
12737
12790
|
const pivotColumnsStr = columns2.map((col) => `${resolved.pivotTable}.${col} AS pivot_${col}`);
|
|
12738
12791
|
allPivotColumns.push(...pivotColumnsStr);
|
|
@@ -13282,8 +13335,8 @@ function createQueryBuilder(state) {
|
|
|
13282
13335
|
if (!resolved) {
|
|
13283
13336
|
throw new Error(`[query-builder] Relationship '${relation}' is not a belongsToMany relationship on table '${String(table)}'`);
|
|
13284
13337
|
}
|
|
13285
|
-
|
|
13286
|
-
|
|
13338
|
+
validateIdentifier2(resolved.pivotTable, "wherePivot (pivot table)");
|
|
13339
|
+
validateIdentifier2(column, "wherePivot (column)");
|
|
13287
13340
|
ensurePivotJoined(resolved);
|
|
13288
13341
|
const op = value === undefined ? "=" : String(opOrValue);
|
|
13289
13342
|
const val = value === undefined ? opOrValue : value;
|
|
@@ -13305,8 +13358,8 @@ function createQueryBuilder(state) {
|
|
|
13305
13358
|
if (!resolved) {
|
|
13306
13359
|
throw new Error(`[query-builder] Relationship '${relation}' is not a belongsToMany relationship on table '${String(table)}'`);
|
|
13307
13360
|
}
|
|
13308
|
-
|
|
13309
|
-
|
|
13361
|
+
validateIdentifier2(resolved.pivotTable, "wherePivotIn (pivot table)");
|
|
13362
|
+
validateIdentifier2(column, "wherePivotIn (column)");
|
|
13310
13363
|
ensurePivotJoined(resolved);
|
|
13311
13364
|
const placeholders = getPlaceholders(values.length, whereParams.length + 1);
|
|
13312
13365
|
const clause = `${resolved.pivotTable}.${column} IN (${placeholders})`;
|
|
@@ -13326,8 +13379,8 @@ function createQueryBuilder(state) {
|
|
|
13326
13379
|
if (!resolved) {
|
|
13327
13380
|
throw new Error(`[query-builder] Relationship '${relation}' is not a belongsToMany relationship on table '${String(table)}'`);
|
|
13328
13381
|
}
|
|
13329
|
-
|
|
13330
|
-
|
|
13382
|
+
validateIdentifier2(resolved.pivotTable, "wherePivotNotIn (pivot table)");
|
|
13383
|
+
validateIdentifier2(column, "wherePivotNotIn (column)");
|
|
13331
13384
|
ensurePivotJoined(resolved);
|
|
13332
13385
|
const placeholders = getPlaceholders(values.length, whereParams.length + 1);
|
|
13333
13386
|
const clause = `${resolved.pivotTable}.${column} NOT IN (${placeholders})`;
|
|
@@ -13347,8 +13400,8 @@ function createQueryBuilder(state) {
|
|
|
13347
13400
|
if (!resolved) {
|
|
13348
13401
|
throw new Error(`[query-builder] Relationship '${relation}' is not a belongsToMany relationship on table '${String(table)}'`);
|
|
13349
13402
|
}
|
|
13350
|
-
|
|
13351
|
-
|
|
13403
|
+
validateIdentifier2(resolved.pivotTable, "wherePivotNull (pivot table)");
|
|
13404
|
+
validateIdentifier2(column, "wherePivotNull (column)");
|
|
13352
13405
|
ensurePivotJoined(resolved);
|
|
13353
13406
|
const clause = `${resolved.pivotTable}.${column} IS NULL`;
|
|
13354
13407
|
whereConditions.push(clause);
|
|
@@ -13366,8 +13419,8 @@ function createQueryBuilder(state) {
|
|
|
13366
13419
|
if (!resolved) {
|
|
13367
13420
|
throw new Error(`[query-builder] Relationship '${relation}' is not a belongsToMany relationship on table '${String(table)}'`);
|
|
13368
13421
|
}
|
|
13369
|
-
|
|
13370
|
-
|
|
13422
|
+
validateIdentifier2(resolved.pivotTable, "wherePivotNotNull (pivot table)");
|
|
13423
|
+
validateIdentifier2(column, "wherePivotNotNull (column)");
|
|
13371
13424
|
ensurePivotJoined(resolved);
|
|
13372
13425
|
const clause = `${resolved.pivotTable}.${column} IS NOT NULL`;
|
|
13373
13426
|
whereConditions.push(clause);
|
|
@@ -13600,10 +13653,14 @@ function createQueryBuilder(state) {
|
|
|
13600
13653
|
return this;
|
|
13601
13654
|
},
|
|
13602
13655
|
whereDate(column, op, date) {
|
|
13656
|
+
validateIdentifier2(column, "whereDate(column)");
|
|
13657
|
+
const dateString = date instanceof Date ? date.toISOString() : typeof date === "string" ? date : (() => {
|
|
13658
|
+
throw new TypeError(`[query-builder] whereDate(date): expected string or Date, got ${typeof date}`);
|
|
13659
|
+
})();
|
|
13603
13660
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
13604
13661
|
const idx = whereParams.length + 1;
|
|
13605
13662
|
text += ` ${keyword} ${column} ${op} ${getPlaceholder(idx)}`;
|
|
13606
|
-
whereParams.push(
|
|
13663
|
+
whereParams.push(dateString);
|
|
13607
13664
|
built = null;
|
|
13608
13665
|
return this;
|
|
13609
13666
|
},
|
|
@@ -13614,12 +13671,16 @@ function createQueryBuilder(state) {
|
|
|
13614
13671
|
return this;
|
|
13615
13672
|
},
|
|
13616
13673
|
whereColumn(left, op, right) {
|
|
13674
|
+
validateIdentifier2(left, "whereColumn(left)");
|
|
13675
|
+
validateIdentifier2(right, "whereColumn(right)");
|
|
13617
13676
|
const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
|
|
13618
13677
|
text += ` ${keyword} ${left} ${op} ${right}`;
|
|
13619
13678
|
built = null;
|
|
13620
13679
|
return this;
|
|
13621
13680
|
},
|
|
13622
13681
|
orWhereColumn(left, op, right) {
|
|
13682
|
+
validateIdentifier2(left, "orWhereColumn(left)");
|
|
13683
|
+
validateIdentifier2(right, "orWhereColumn(right)");
|
|
13623
13684
|
text += ` OR ${left} ${op} ${right}`;
|
|
13624
13685
|
built = null;
|
|
13625
13686
|
return this;
|
|
@@ -13839,11 +13900,15 @@ function createQueryBuilder(state) {
|
|
|
13839
13900
|
return this;
|
|
13840
13901
|
},
|
|
13841
13902
|
limit(n) {
|
|
13903
|
+
if (!Number.isFinite(n) || n < 0 || !Number.isInteger(n))
|
|
13904
|
+
throw new TypeError(`[bun-query-builder] limit(n): expected non-negative integer, got ${n}`);
|
|
13842
13905
|
text = SQL_PATTERNS.LIMIT.test(text) ? text.replace(SQL_PATTERNS.LIMIT, ` LIMIT ${n}`) : `${text} LIMIT ${n}`;
|
|
13843
13906
|
built = null;
|
|
13844
13907
|
return this;
|
|
13845
13908
|
},
|
|
13846
13909
|
offset(n) {
|
|
13910
|
+
if (!Number.isFinite(n) || n < 0 || !Number.isInteger(n))
|
|
13911
|
+
throw new TypeError(`[bun-query-builder] offset(n): expected non-negative integer, got ${n}`);
|
|
13847
13912
|
text = SQL_PATTERNS.OFFSET.test(text) ? text.replace(SQL_PATTERNS.OFFSET, ` OFFSET ${n}`) : `${text} OFFSET ${n}`;
|
|
13848
13913
|
built = null;
|
|
13849
13914
|
return this;
|
|
@@ -14116,19 +14181,25 @@ function createQueryBuilder(state) {
|
|
|
14116
14181
|
includeTrashed = true;
|
|
14117
14182
|
onlyTrashed = true;
|
|
14118
14183
|
const softDeleteColumn = config5.softDeletes?.column || "deleted_at";
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14184
|
+
const splice = (raw, predicate2) => {
|
|
14185
|
+
const upper = raw.toUpperCase();
|
|
14186
|
+
let depth = 0;
|
|
14187
|
+
for (let i = 0;i < raw.length; i++) {
|
|
14188
|
+
const c = raw[i];
|
|
14189
|
+
if (c === "(")
|
|
14190
|
+
depth++;
|
|
14191
|
+
else if (c === ")")
|
|
14192
|
+
depth--;
|
|
14193
|
+
else if (depth === 0 && upper.substring(i, i + 5) === "WHERE" && (i === 0 || /\s/.test(raw[i - 1] ?? "")) && /\s/.test(raw[i + 5] ?? "")) {
|
|
14194
|
+
return `${raw.substring(0, i)}WHERE ${predicate2} AND ${raw.substring(i + 6)}`;
|
|
14195
|
+
}
|
|
14196
|
+
}
|
|
14197
|
+
return `${raw} WHERE ${predicate2}`;
|
|
14198
|
+
};
|
|
14199
|
+
const predicate = `${table}.${softDeleteColumn} IS NOT NULL`;
|
|
14200
|
+
text = splice(text, predicate);
|
|
14124
14201
|
const currentSql = String(ensureBuilt());
|
|
14125
|
-
|
|
14126
|
-
const newSql = currentSql.replace(/WHERE/, `WHERE ${table}.${softDeleteColumn} IS NOT NULL AND`);
|
|
14127
|
-
built = sql([newSql]);
|
|
14128
|
-
} else {
|
|
14129
|
-
const newSql = `${currentSql} WHERE ${table}.${softDeleteColumn} IS NOT NULL`;
|
|
14130
|
-
built = sql([newSql]);
|
|
14131
|
-
}
|
|
14202
|
+
built = sql([splice(currentSql, predicate)]);
|
|
14132
14203
|
return this;
|
|
14133
14204
|
},
|
|
14134
14205
|
scope(name, value) {
|
|
@@ -14302,7 +14373,15 @@ function createQueryBuilder(state) {
|
|
|
14302
14373
|
},
|
|
14303
14374
|
async count() {
|
|
14304
14375
|
const fromIdx = text.indexOf(" FROM ");
|
|
14305
|
-
const
|
|
14376
|
+
const hasGroupBy = / GROUP BY /i.test(text);
|
|
14377
|
+
let countText;
|
|
14378
|
+
if (hasGroupBy) {
|
|
14379
|
+
countText = `SELECT COUNT(*) as c FROM (${text}) AS _bqb_count_sub`;
|
|
14380
|
+
} else if (fromIdx !== -1) {
|
|
14381
|
+
countText = `SELECT COUNT(*) as c${text.substring(fromIdx)}`;
|
|
14382
|
+
} else {
|
|
14383
|
+
countText = `SELECT COUNT(*) as c FROM ${table}`;
|
|
14384
|
+
}
|
|
14306
14385
|
const cHooks = config5.hooks;
|
|
14307
14386
|
const cHasHooks = cHooks && (cHooks.onQueryStart || cHooks.onQueryEnd || cHooks.onQueryError || cHooks.startSpan);
|
|
14308
14387
|
if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !cHasHooks) {
|
|
@@ -14726,7 +14805,7 @@ function createQueryBuilder(state) {
|
|
|
14726
14805
|
let sqlText = "";
|
|
14727
14806
|
const params = [];
|
|
14728
14807
|
const isPostgres = config5.dialect === "postgres";
|
|
14729
|
-
const quoteId = isPostgres ? (id) => `"${id}"` : config5.dialect === "mysql" ? (id) => `\`${id}\`` : (id) => id
|
|
14808
|
+
const quoteId = isPostgres ? (id) => `"${String(id).replace(/"/g, '""')}"` : config5.dialect === "mysql" ? (id) => `\`${String(id).replace(/`/g, "``")}\`` : (id) => `"${String(id).replace(/"/g, '""')}"`;
|
|
14730
14809
|
const getPlaceholder2 = isPostgres ? (index) => `$${index + 1}` : (_index) => "?";
|
|
14731
14810
|
return {
|
|
14732
14811
|
values(data) {
|
|
@@ -14859,11 +14938,11 @@ function createQueryBuilder(state) {
|
|
|
14859
14938
|
updateTable(table) {
|
|
14860
14939
|
let built;
|
|
14861
14940
|
const params = [];
|
|
14862
|
-
const quoteId = (
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
return `"${
|
|
14941
|
+
const quoteId = (identifier) => {
|
|
14942
|
+
const s = String(identifier);
|
|
14943
|
+
if (config5.dialect === "mysql")
|
|
14944
|
+
return `\`${s.replace(/`/g, "``")}\``;
|
|
14945
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
14867
14946
|
};
|
|
14868
14947
|
let sqlText = `UPDATE ${quoteId(String(table))}`;
|
|
14869
14948
|
return {
|
|
@@ -14967,11 +15046,11 @@ function createQueryBuilder(state) {
|
|
|
14967
15046
|
};
|
|
14968
15047
|
},
|
|
14969
15048
|
deleteFrom(table) {
|
|
14970
|
-
const quoteId = (
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
14974
|
-
return `"${
|
|
15049
|
+
const quoteId = (identifier) => {
|
|
15050
|
+
const s = String(identifier);
|
|
15051
|
+
if (config5.dialect === "mysql")
|
|
15052
|
+
return `\`${s.replace(/`/g, "``")}\``;
|
|
15053
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
14975
15054
|
};
|
|
14976
15055
|
const quotedTable = quoteId(String(table));
|
|
14977
15056
|
let sqlText = `DELETE FROM ${quotedTable}`;
|
|
@@ -15255,6 +15334,10 @@ function createQueryBuilder(state) {
|
|
|
15255
15334
|
};
|
|
15256
15335
|
},
|
|
15257
15336
|
async insertOrIgnore(table, values) {
|
|
15337
|
+
if (config5.dialect === "mysql") {
|
|
15338
|
+
const built2 = bunSql`INSERT IGNORE INTO ${bunSql(String(table))} ${bunSql(values)}`;
|
|
15339
|
+
return built2.execute();
|
|
15340
|
+
}
|
|
15258
15341
|
const built = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(values)} ON CONFLICT DO NOTHING`;
|
|
15259
15342
|
return built.execute();
|
|
15260
15343
|
},
|
|
@@ -15290,7 +15373,15 @@ function createQueryBuilder(state) {
|
|
|
15290
15373
|
async upsert(table, rows, conflictColumns, mergeColumns) {
|
|
15291
15374
|
const targetCols = conflictColumns.map((c) => String(c));
|
|
15292
15375
|
const setCols = (mergeColumns ?? []).map((c) => String(c));
|
|
15293
|
-
|
|
15376
|
+
if (config5.dialect === "mysql") {
|
|
15377
|
+
const updateList2 = setCols.map((c) => `\`${c.replace(/`/g, "``")}\` = VALUES(\`${c.replace(/`/g, "``")}\`)`).join(", ");
|
|
15378
|
+
const built2 = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(rows)} ON DUPLICATE KEY UPDATE ${bunSql.unsafe(updateList2)}`;
|
|
15379
|
+
return built2.execute();
|
|
15380
|
+
}
|
|
15381
|
+
const isPostgres = config5.dialect === "postgres";
|
|
15382
|
+
const quoteCol = (column) => isPostgres ? `"${column.replace(/"/g, '""')}"` : `"${column.replace(/"/g, '""')}"`;
|
|
15383
|
+
const updateList = setCols.map((column) => `${quoteCol(column)} = EXCLUDED.${quoteCol(column)}`).join(", ");
|
|
15384
|
+
const built = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(rows)} ON CONFLICT (${bunSql(targetCols)}) DO UPDATE SET ${bunSql.unsafe(updateList)}`;
|
|
15294
15385
|
return built.execute();
|
|
15295
15386
|
},
|
|
15296
15387
|
async save(table, values) {
|
|
@@ -24055,6 +24146,14 @@ var init_src = __esm(async () => {
|
|
|
24055
24146
|
|
|
24056
24147
|
// src/orm.ts
|
|
24057
24148
|
import { Database as Database2 } from "bun:sqlite";
|
|
24149
|
+
function assertValidIdentifier(name, context) {
|
|
24150
|
+
if (typeof name !== "string" || name.length === 0)
|
|
24151
|
+
throw new TypeError(`[bun-query-builder] ${context}: identifier must be a non-empty string, got ${typeof name}`);
|
|
24152
|
+
if (name.length > 64)
|
|
24153
|
+
throw new TypeError(`[bun-query-builder] ${context}: identifier '${name}' exceeds 64 chars`);
|
|
24154
|
+
if (!SAFE_SQL_IDENTIFIER.test(name))
|
|
24155
|
+
throw new TypeError(`[bun-query-builder] ${context}: identifier '${name}' contains characters outside [A-Za-z0-9_] \u2014 refusing to interpolate into SQL`);
|
|
24156
|
+
}
|
|
24058
24157
|
function getModelFromRegistry(name) {
|
|
24059
24158
|
if (!_getModel) {
|
|
24060
24159
|
try {
|
|
@@ -24605,14 +24704,21 @@ class BelongsToManyRelationBuilder {
|
|
|
24605
24704
|
return this;
|
|
24606
24705
|
}
|
|
24607
24706
|
orderBy(column, direction = "asc") {
|
|
24707
|
+
assertValidIdentifier(column, "orderBy(column)");
|
|
24708
|
+
if (direction !== "asc" && direction !== "desc")
|
|
24709
|
+
throw new TypeError(`[bun-query-builder] orderBy(direction): expected 'asc' or 'desc', got '${direction}'`);
|
|
24608
24710
|
this._orderBy.push(`${column} ${direction.toUpperCase()}`);
|
|
24609
24711
|
return this;
|
|
24610
24712
|
}
|
|
24611
24713
|
limit(n2) {
|
|
24714
|
+
if (!Number.isFinite(n2) || n2 < 0 || !Number.isInteger(n2))
|
|
24715
|
+
throw new TypeError(`[bun-query-builder] limit(n): expected non-negative integer, got ${n2}`);
|
|
24612
24716
|
this._limit = n2;
|
|
24613
24717
|
return this;
|
|
24614
24718
|
}
|
|
24615
24719
|
offset(n2) {
|
|
24720
|
+
if (!Number.isFinite(n2) || n2 < 0 || !Number.isInteger(n2))
|
|
24721
|
+
throw new TypeError(`[bun-query-builder] offset(n): expected non-negative integer, got ${n2}`);
|
|
24616
24722
|
this._offset = n2;
|
|
24617
24723
|
return this;
|
|
24618
24724
|
}
|
|
@@ -24839,18 +24945,22 @@ class ModelQueryBuilder {
|
|
|
24839
24945
|
return this;
|
|
24840
24946
|
}
|
|
24841
24947
|
whereNull(column) {
|
|
24948
|
+
assertValidIdentifier(column, "whereNull(column)");
|
|
24842
24949
|
this._wheres.push({ column, operator: "=", value: null, boolean: "and" });
|
|
24843
24950
|
return this;
|
|
24844
24951
|
}
|
|
24845
24952
|
orWhereNull(column) {
|
|
24953
|
+
assertValidIdentifier(column, "orWhereNull(column)");
|
|
24846
24954
|
this._wheres.push({ column, operator: "=", value: null, boolean: "or" });
|
|
24847
24955
|
return this;
|
|
24848
24956
|
}
|
|
24849
24957
|
whereNotNull(column) {
|
|
24958
|
+
assertValidIdentifier(column, "whereNotNull(column)");
|
|
24850
24959
|
this._wheres.push({ column, operator: "!=", value: null, boolean: "and" });
|
|
24851
24960
|
return this;
|
|
24852
24961
|
}
|
|
24853
24962
|
orWhereNotNull(column) {
|
|
24963
|
+
assertValidIdentifier(column, "orWhereNotNull(column)");
|
|
24854
24964
|
this._wheres.push({ column, operator: "!=", value: null, boolean: "or" });
|
|
24855
24965
|
return this;
|
|
24856
24966
|
}
|
|
@@ -24897,13 +25007,19 @@ class ModelQueryBuilder {
|
|
|
24897
25007
|
return this;
|
|
24898
25008
|
}
|
|
24899
25009
|
whereBetween(column, range) {
|
|
25010
|
+
assertValidIdentifier(column, "whereBetween(column)");
|
|
24900
25011
|
this._wheres.push({ column, operator: ">=", value: range[0], boolean: "and" });
|
|
24901
25012
|
this._wheres.push({ column, operator: "<=", value: range[1], boolean: "and" });
|
|
24902
25013
|
return this;
|
|
24903
25014
|
}
|
|
24904
25015
|
whereNotBetween(column, range) {
|
|
24905
|
-
|
|
24906
|
-
|
|
25016
|
+
assertValidIdentifier(column, "whereNotBetween(column)");
|
|
25017
|
+
const col = column;
|
|
25018
|
+
this._wheres.push({
|
|
25019
|
+
raw: `(${col} < ? OR ${col} > ?)`,
|
|
25020
|
+
rawParams: [range[0], range[1]],
|
|
25021
|
+
boolean: "and"
|
|
25022
|
+
});
|
|
24907
25023
|
return this;
|
|
24908
25024
|
}
|
|
24909
25025
|
when(condition, callback) {
|
|
@@ -24913,6 +25029,9 @@ class ModelQueryBuilder {
|
|
|
24913
25029
|
return this;
|
|
24914
25030
|
}
|
|
24915
25031
|
orderBy(column, direction = "asc") {
|
|
25032
|
+
assertValidIdentifier(column, "orderBy(column)");
|
|
25033
|
+
if (direction !== "asc" && direction !== "desc")
|
|
25034
|
+
throw new TypeError(`[bun-query-builder] orderBy(direction): expected 'asc' or 'desc', got '${direction}'`);
|
|
24916
25035
|
this._orderBy.push({ column, direction });
|
|
24917
25036
|
return this;
|
|
24918
25037
|
}
|
|
@@ -25157,6 +25276,7 @@ class ModelQueryBuilder {
|
|
|
25157
25276
|
return results[0];
|
|
25158
25277
|
}
|
|
25159
25278
|
increment(column, amount = 1) {
|
|
25279
|
+
assertValidIdentifier(column, "increment(column)");
|
|
25160
25280
|
const db = getDatabase();
|
|
25161
25281
|
const params = [amount];
|
|
25162
25282
|
let sql2 = `UPDATE ${this._definition.table} SET ${column} = ${column} + ?`;
|
|
@@ -25213,6 +25333,7 @@ class ModelQueryBuilder {
|
|
|
25213
25333
|
};
|
|
25214
25334
|
}
|
|
25215
25335
|
pluck(column) {
|
|
25336
|
+
assertValidIdentifier(column, "pluck(column)");
|
|
25216
25337
|
const db = getDatabase();
|
|
25217
25338
|
const params = [];
|
|
25218
25339
|
let sql2 = `SELECT ${column} FROM ${this._definition.table}`;
|
|
@@ -25230,6 +25351,7 @@ class ModelQueryBuilder {
|
|
|
25230
25351
|
return rows.map((r2) => r2[column]);
|
|
25231
25352
|
}
|
|
25232
25353
|
aggregate(fn, column) {
|
|
25354
|
+
assertValidIdentifier(column, `${fn}(column)`);
|
|
25233
25355
|
const db = getDatabase();
|
|
25234
25356
|
const params = [];
|
|
25235
25357
|
let sql2 = `SELECT ${fn}(${column}) as v FROM ${this._definition.table}`;
|
|
@@ -25506,8 +25628,9 @@ async function seedModel(definition, count, faker) {
|
|
|
25506
25628
|
db.run(`INSERT INTO ${definition.table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`, Object.values(data));
|
|
25507
25629
|
}
|
|
25508
25630
|
}
|
|
25509
|
-
var _getModel = null, globalDb = null, snakeCaseCache, tableNameCache, relationCache, preparedStatementCache;
|
|
25631
|
+
var SAFE_SQL_IDENTIFIER, _getModel = null, globalDb = null, snakeCaseCache, tableNameCache, relationCache, preparedStatementCache;
|
|
25510
25632
|
var init_orm = __esm(() => {
|
|
25633
|
+
SAFE_SQL_IDENTIFIER = /^[A-Z_][A-Z0-9_]*$/i;
|
|
25511
25634
|
snakeCaseCache = new Map;
|
|
25512
25635
|
tableNameCache = new Map;
|
|
25513
25636
|
relationCache = new Map;
|
|
@@ -25517,6 +25640,7 @@ var init_orm = __esm(() => {
|
|
|
25517
25640
|
// src/model.ts
|
|
25518
25641
|
var exports_model = {};
|
|
25519
25642
|
__export(exports_model, {
|
|
25643
|
+
registerModel: () => registerModel,
|
|
25520
25644
|
registerBrowserModels: () => registerBrowserModels,
|
|
25521
25645
|
hasModel: () => hasModel,
|
|
25522
25646
|
getModelRegistry: () => getModelRegistry,
|
|
@@ -25540,6 +25664,10 @@ function hasModel(name) {
|
|
|
25540
25664
|
function clearModelRegistry() {
|
|
25541
25665
|
modelRegistry.clear();
|
|
25542
25666
|
}
|
|
25667
|
+
function registerModel(name, model) {
|
|
25668
|
+
modelRegistry.set(name, model);
|
|
25669
|
+
return model;
|
|
25670
|
+
}
|
|
25543
25671
|
function isClientSide() {
|
|
25544
25672
|
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
25545
25673
|
}
|
|
@@ -25556,7 +25684,7 @@ function defineModel(definition) {
|
|
|
25556
25684
|
getName: () => definition.name
|
|
25557
25685
|
});
|
|
25558
25686
|
}
|
|
25559
|
-
|
|
25687
|
+
registerModel(definition.name, model);
|
|
25560
25688
|
return model;
|
|
25561
25689
|
}
|
|
25562
25690
|
function registerBrowserModels(models) {
|
|
@@ -27853,7 +27981,7 @@ function defineSeeder(seederClass) {
|
|
|
27853
27981
|
return seederClass;
|
|
27854
27982
|
}
|
|
27855
27983
|
// src/index.ts
|
|
27856
|
-
var init_src2 = __esm(() => {
|
|
27984
|
+
var init_src2 = __esm(async () => {
|
|
27857
27985
|
init_model2();
|
|
27858
27986
|
init_model2();
|
|
27859
27987
|
init_actions();
|
|
@@ -27870,7 +27998,7 @@ var init_src2 = __esm(() => {
|
|
|
27870
27998
|
init_migrations();
|
|
27871
27999
|
init_orm();
|
|
27872
28000
|
});
|
|
27873
|
-
init_src2();
|
|
28001
|
+
await init_src2();
|
|
27874
28002
|
|
|
27875
28003
|
export {
|
|
27876
28004
|
dbWipe as wipeDatabase,
|
|
@@ -27897,6 +28025,7 @@ export {
|
|
|
27897
28025
|
resetDatabase,
|
|
27898
28026
|
resetConnection,
|
|
27899
28027
|
relationDiagram,
|
|
28028
|
+
registerModel,
|
|
27900
28029
|
registerBrowserModels,
|
|
27901
28030
|
queryExplainAll,
|
|
27902
28031
|
ping,
|