bun-query-builder 0.2.41 → 0.2.44
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/bin/cli.js +257 -96
- package/dist/client.d.ts +19 -1
- package/dist/column-types.d.ts +26 -0
- package/dist/drivers/sqlite.d.ts +11 -6
- package/dist/model.d.ts +4 -0
- package/dist/orm.d.ts +2 -0
- package/dist/src/browser.js +1 -1
- package/dist/src/index.js +258 -95
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -476,7 +476,7 @@ class BrowserModelQueryBuilder {
|
|
|
476
476
|
for (const where of this._wheres) {
|
|
477
477
|
if (where.operator === "=") {
|
|
478
478
|
params.append(where.column, String(where.value));
|
|
479
|
-
} else if (
|
|
479
|
+
} else if (where.operator === "in" && Array.isArray(where.value)) {
|
|
480
480
|
params.append(`${where.column}[]`, where.value.join(","));
|
|
481
481
|
} else if (where.operator === "is" && where.value === null) {
|
|
482
482
|
params.append(`filter[${where.column}][is]`, "null");
|
|
@@ -12856,7 +12856,10 @@ function warnOnceBareSqlFragment(context) {
|
|
|
12856
12856
|
if (warnedSqlFragmentContexts.has(context))
|
|
12857
12857
|
return;
|
|
12858
12858
|
warnedSqlFragmentContexts.add(context);
|
|
12859
|
-
console.warn(`[query-builder] ${context}: bare string passed to a *Raw method. ` + `Prefer \`
|
|
12859
|
+
console.warn(`[query-builder] ${context}: bare string passed to a *Raw method. ` + `Prefer the \`raw\`...\`\` tagged template, which escapes interpolated ` + `values for the configured dialect \u2014 concatenating request input into SQL ` + `is an injection vector. This will become a hard error in a future release.`);
|
|
12860
|
+
}
|
|
12861
|
+
function escapeStringLiteral(value, dialect = config5.dialect) {
|
|
12862
|
+
return isMysqlLike(dialect) ? value.replace(/\\/g, "\\\\").replace(/'/g, "''") : value.replace(/'/g, "''");
|
|
12860
12863
|
}
|
|
12861
12864
|
function formatSubqueryValue(val) {
|
|
12862
12865
|
if (val === null)
|
|
@@ -12866,7 +12869,7 @@ function formatSubqueryValue(val) {
|
|
|
12866
12869
|
if (typeof val === "boolean")
|
|
12867
12870
|
return val ? "1" : "0";
|
|
12868
12871
|
if (typeof val === "string")
|
|
12869
|
-
return `'${val
|
|
12872
|
+
return `'${escapeStringLiteral(val)}'`;
|
|
12870
12873
|
if (val instanceof Date)
|
|
12871
12874
|
return `'${val.toISOString()}'`;
|
|
12872
12875
|
throw new TypeError(`[query-builder] subquery condition: refusing to interpolate value of type ${typeof val}`);
|
|
@@ -14487,11 +14490,13 @@ function createQueryBuilder(state) {
|
|
|
14487
14490
|
const dialect = config5.dialect;
|
|
14488
14491
|
const idx = whereParams.length + 1;
|
|
14489
14492
|
if (dialect === "postgres") {
|
|
14493
|
+
const needsJsonbLift = typeof json === "number" || typeof json === "boolean";
|
|
14494
|
+
const operand = needsJsonbLift ? `to_jsonb(${getPlaceholder(idx)})` : getPlaceholder(idx);
|
|
14490
14495
|
if (config5.sql?.jsonContainsMode === "function")
|
|
14491
|
-
pushWhere("AND", `jsonb_contains(${column}, ${
|
|
14496
|
+
pushWhere("AND", `jsonb_contains(${column}, ${operand})`);
|
|
14492
14497
|
else
|
|
14493
|
-
pushWhere("AND", `${column} @> ${
|
|
14494
|
-
whereParams.push(
|
|
14498
|
+
pushWhere("AND", `${column} @> ${operand}`);
|
|
14499
|
+
whereParams.push(json);
|
|
14495
14500
|
} else if (isMysqlLike(dialect)) {
|
|
14496
14501
|
pushWhere("AND", `JSON_CONTAINS(${column}, ${getPlaceholder(idx)})`);
|
|
14497
14502
|
whereParams.push(JSON.stringify(json));
|
|
@@ -15788,6 +15793,31 @@ function createQueryBuilder(state) {
|
|
|
15788
15793
|
let built;
|
|
15789
15794
|
let sqlText = "";
|
|
15790
15795
|
const params = [];
|
|
15796
|
+
let noRows = false;
|
|
15797
|
+
const emptyInsertResult = () => isPostgres ? [] : { changes: 0, lastInsertRowid: 0 };
|
|
15798
|
+
const emptyReturningBuilder = () => {
|
|
15799
|
+
const noRow = async () => {
|
|
15800
|
+
return;
|
|
15801
|
+
};
|
|
15802
|
+
const noRowOrFail = async () => {
|
|
15803
|
+
throw new Error("Insert with RETURNING returned no rows");
|
|
15804
|
+
};
|
|
15805
|
+
return {
|
|
15806
|
+
where: () => emptyReturningBuilder(),
|
|
15807
|
+
andWhere: () => emptyReturningBuilder(),
|
|
15808
|
+
orWhere: () => emptyReturningBuilder(),
|
|
15809
|
+
orderBy: () => emptyReturningBuilder(),
|
|
15810
|
+
limit: () => emptyReturningBuilder(),
|
|
15811
|
+
offset: () => emptyReturningBuilder(),
|
|
15812
|
+
toSQL: () => makeExecutableQuery(null, ""),
|
|
15813
|
+
execute: async () => [],
|
|
15814
|
+
get: async () => [],
|
|
15815
|
+
first: noRow,
|
|
15816
|
+
executeTakeFirst: noRow,
|
|
15817
|
+
firstOrFail: noRowOrFail,
|
|
15818
|
+
executeTakeFirstOrThrow: noRowOrFail
|
|
15819
|
+
};
|
|
15820
|
+
};
|
|
15791
15821
|
const isPostgres = config5.dialect === "postgres";
|
|
15792
15822
|
const quoteId = isPostgres ? (id) => `"${String(id).replace(/"/g, '""')}"` : isMysqlLike(config5.dialect) ? (id) => `\`${String(id).replace(/`/g, "``")}\`` : (id) => `"${String(id).replace(/"/g, '""')}"`;
|
|
15793
15823
|
const getPlaceholder2 = isPostgres ? (index) => `$${index + 1}` : (_index) => "?";
|
|
@@ -15796,9 +15826,13 @@ function createQueryBuilder(state) {
|
|
|
15796
15826
|
const rows = Array.isArray(data) ? data : [data];
|
|
15797
15827
|
const rowCount = rows.length;
|
|
15798
15828
|
if (rowCount === 0) {
|
|
15799
|
-
|
|
15829
|
+
noRows = true;
|
|
15830
|
+
built = null;
|
|
15831
|
+
sqlText = "";
|
|
15832
|
+
params.length = 0;
|
|
15800
15833
|
return this;
|
|
15801
15834
|
}
|
|
15835
|
+
noRows = false;
|
|
15802
15836
|
const firstRow = rows[0];
|
|
15803
15837
|
const keys = Object.keys(firstRow);
|
|
15804
15838
|
const colCount = keys.length;
|
|
@@ -15852,6 +15886,8 @@ function createQueryBuilder(state) {
|
|
|
15852
15886
|
return this;
|
|
15853
15887
|
},
|
|
15854
15888
|
returning(...cols) {
|
|
15889
|
+
if (noRows)
|
|
15890
|
+
return emptyReturningBuilder();
|
|
15855
15891
|
const returningSql = `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
15856
15892
|
const q = _sql.unsafe(returningSql, params);
|
|
15857
15893
|
const runFirst = async () => {
|
|
@@ -15885,11 +15921,15 @@ function createQueryBuilder(state) {
|
|
|
15885
15921
|
};
|
|
15886
15922
|
},
|
|
15887
15923
|
toSQL() {
|
|
15924
|
+
if (noRows)
|
|
15925
|
+
return makeExecutableQuery(null, "");
|
|
15888
15926
|
if (!built)
|
|
15889
15927
|
built = _sql.unsafe(sqlText, params);
|
|
15890
15928
|
return makeExecutableQuery(built, sqlText);
|
|
15891
15929
|
},
|
|
15892
15930
|
execute() {
|
|
15931
|
+
if (noRows)
|
|
15932
|
+
return Promise.resolve(emptyInsertResult());
|
|
15893
15933
|
const hooks = activeHooks();
|
|
15894
15934
|
const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hooks.beforeCreate || hooks.afterCreate || hasSlowQueryHook(hooks));
|
|
15895
15935
|
if (!hasHooks) {
|
|
@@ -15904,12 +15944,16 @@ function createQueryBuilder(state) {
|
|
|
15904
15944
|
return runWithHooks(built, "insert");
|
|
15905
15945
|
},
|
|
15906
15946
|
async executeTakeFirst() {
|
|
15947
|
+
if (noRows)
|
|
15948
|
+
return emptyInsertResult();
|
|
15907
15949
|
if (!built)
|
|
15908
15950
|
built = _sql.unsafe(sqlText, params);
|
|
15909
15951
|
const result = await runWithHooks(built, "insert");
|
|
15910
15952
|
return result;
|
|
15911
15953
|
},
|
|
15912
15954
|
async executeTakeFirstOrThrow() {
|
|
15955
|
+
if (noRows)
|
|
15956
|
+
throw new Error("Insert failed");
|
|
15913
15957
|
if (!built)
|
|
15914
15958
|
built = _sql.unsafe(sqlText, params);
|
|
15915
15959
|
const result = await runWithHooks(built, "insert");
|
|
@@ -15918,6 +15962,8 @@ function createQueryBuilder(state) {
|
|
|
15918
15962
|
return result;
|
|
15919
15963
|
},
|
|
15920
15964
|
returningAll() {
|
|
15965
|
+
if (noRows)
|
|
15966
|
+
return emptyReturningBuilder();
|
|
15921
15967
|
const returningSql = `${sqlText} RETURNING *`;
|
|
15922
15968
|
const q = _sql.unsafe(returningSql, params);
|
|
15923
15969
|
const runFirst = async () => {
|
|
@@ -16024,8 +16070,22 @@ function createQueryBuilder(state) {
|
|
|
16024
16070
|
built = _sql.unsafe(sqlText, params);
|
|
16025
16071
|
return this;
|
|
16026
16072
|
}
|
|
16027
|
-
if (
|
|
16073
|
+
if (isRawExpression(expr)) {
|
|
16074
|
+
sqlText = `${sqlText} ${getWhereKeyword()} ${expr.raw}`;
|
|
16075
|
+
built = _sql.unsafe(sqlText, params);
|
|
16076
|
+
return this;
|
|
16077
|
+
}
|
|
16078
|
+
if (isBoundSqlExpression(expr)) {
|
|
16079
|
+
const rendered = renderBoundSqlExpression(expr, params.length + 1);
|
|
16080
|
+
sqlText = `${sqlText} ${getWhereKeyword()} ${rendered.text}`;
|
|
16081
|
+
params.push(...rendered.parameters);
|
|
16082
|
+
built = _sql.unsafe(sqlText, params);
|
|
16083
|
+
return this;
|
|
16084
|
+
}
|
|
16085
|
+
if (expr && typeof expr === "object") {
|
|
16028
16086
|
const keys = Object.keys(expr);
|
|
16087
|
+
if (keys.length === 0)
|
|
16088
|
+
throw new TypeError("[query-builder] updateTable.where({}): an empty object is not a filter. Pass a condition, or drop the where() call if updating every row is intended.");
|
|
16029
16089
|
const len = keys.length;
|
|
16030
16090
|
const baseIdx = params.length;
|
|
16031
16091
|
const conditions = Array.from({ length: len });
|
|
@@ -16035,8 +16095,9 @@ function createQueryBuilder(state) {
|
|
|
16035
16095
|
}
|
|
16036
16096
|
sqlText = `${sqlText} ${getWhereKeyword()} ${conditions.join(" AND ")}`;
|
|
16037
16097
|
built = _sql.unsafe(sqlText, params);
|
|
16098
|
+
return this;
|
|
16038
16099
|
}
|
|
16039
|
-
|
|
16100
|
+
throw new TypeError(`[query-builder] updateTable.where(): expected a condition, got ${expr === null ? "null" : typeof expr}. Refusing to run an UPDATE with no WHERE \u2014 drop the where() call if updating every row is intended.`);
|
|
16040
16101
|
},
|
|
16041
16102
|
whereNull(column) {
|
|
16042
16103
|
const keyword = SQL_PATTERNS.WHERE.test(sqlText) ? "AND" : "WHERE";
|
|
@@ -16051,22 +16112,40 @@ function createQueryBuilder(state) {
|
|
|
16051
16112
|
return this;
|
|
16052
16113
|
},
|
|
16053
16114
|
returning(...cols) {
|
|
16054
|
-
const
|
|
16055
|
-
const
|
|
16115
|
+
const parent = this;
|
|
16116
|
+
const retText = () => `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
16117
|
+
const build = () => params.length > 0 ? _sql.unsafe(retText(), params) : _sql.unsafe(retText());
|
|
16056
16118
|
const runFirst = async () => {
|
|
16057
|
-
const rows = await runWithHooks(
|
|
16119
|
+
const rows = await runWithHooks(build(), "update");
|
|
16058
16120
|
return Array.isArray(rows) ? rows[0] : rows;
|
|
16059
16121
|
};
|
|
16122
|
+
const unsupported = (name) => () => {
|
|
16123
|
+
throw new TypeError(`[query-builder] updateTable.returning(...).${name}() is not supported \u2014 an UPDATE cannot be ordered or limited through this builder. Apply the filter with where() instead.`);
|
|
16124
|
+
};
|
|
16060
16125
|
const obj = {
|
|
16061
|
-
where: () =>
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16126
|
+
where: (...args) => {
|
|
16127
|
+
parent.where(...args);
|
|
16128
|
+
return obj;
|
|
16129
|
+
},
|
|
16130
|
+
andWhere: (...args) => {
|
|
16131
|
+
parent.where(...args);
|
|
16132
|
+
return obj;
|
|
16133
|
+
},
|
|
16134
|
+
whereNull: (...args) => {
|
|
16135
|
+
parent.whereNull(...args);
|
|
16136
|
+
return obj;
|
|
16137
|
+
},
|
|
16138
|
+
whereNotNull: (...args) => {
|
|
16139
|
+
parent.whereNotNull(...args);
|
|
16140
|
+
return obj;
|
|
16141
|
+
},
|
|
16142
|
+
orWhere: unsupported("orWhere"),
|
|
16143
|
+
orderBy: unsupported("orderBy"),
|
|
16144
|
+
limit: unsupported("limit"),
|
|
16145
|
+
offset: unsupported("offset"),
|
|
16146
|
+
toSQL: () => makeExecutableQuery(build(), retText()),
|
|
16147
|
+
execute: () => runWithHooks(build(), "update"),
|
|
16148
|
+
get: () => runWithHooks(build(), "update"),
|
|
16070
16149
|
first: runFirst,
|
|
16071
16150
|
executeTakeFirst: runFirst,
|
|
16072
16151
|
async firstOrFail() {
|
|
@@ -16105,17 +16184,17 @@ function createQueryBuilder(state) {
|
|
|
16105
16184
|
return { numUpdatedRows: result };
|
|
16106
16185
|
},
|
|
16107
16186
|
returningAll() {
|
|
16108
|
-
const retAllText = `${sqlText} RETURNING *`;
|
|
16109
|
-
const
|
|
16187
|
+
const retAllText = () => `${sqlText} RETURNING *`;
|
|
16188
|
+
const build = () => params.length > 0 ? _sql.unsafe(retAllText(), params) : _sql.unsafe(retAllText());
|
|
16110
16189
|
return {
|
|
16111
|
-
toSQL: () => makeExecutableQuery(
|
|
16112
|
-
execute: () => runWithHooks(
|
|
16190
|
+
toSQL: () => makeExecutableQuery(build(), retAllText()),
|
|
16191
|
+
execute: () => runWithHooks(build(), "update"),
|
|
16113
16192
|
async executeTakeFirst() {
|
|
16114
|
-
const result = await runWithHooks(
|
|
16193
|
+
const result = await runWithHooks(build(), "update");
|
|
16115
16194
|
return Array.isArray(result) ? result[0] : result;
|
|
16116
16195
|
},
|
|
16117
16196
|
async executeTakeFirstOrThrow() {
|
|
16118
|
-
const result = await runWithHooks(
|
|
16197
|
+
const result = await runWithHooks(build(), "update");
|
|
16119
16198
|
const first = Array.isArray(result) ? result[0] : result;
|
|
16120
16199
|
if (!first)
|
|
16121
16200
|
throw new Error("Update with RETURNING failed");
|
|
@@ -16180,8 +16259,22 @@ function createQueryBuilder(state) {
|
|
|
16180
16259
|
built = null;
|
|
16181
16260
|
return this;
|
|
16182
16261
|
}
|
|
16183
|
-
if (
|
|
16262
|
+
if (isRawExpression(expr)) {
|
|
16263
|
+
sqlText += ` ${getWhereKeyword()} ${expr.raw}`;
|
|
16264
|
+
built = null;
|
|
16265
|
+
return this;
|
|
16266
|
+
}
|
|
16267
|
+
if (isBoundSqlExpression(expr)) {
|
|
16268
|
+
const rendered = renderBoundSqlExpression(expr, delParams.length + 1);
|
|
16269
|
+
sqlText += ` ${getWhereKeyword()} ${rendered.text}`;
|
|
16270
|
+
delParams.push(...rendered.parameters);
|
|
16271
|
+
built = null;
|
|
16272
|
+
return this;
|
|
16273
|
+
}
|
|
16274
|
+
if (expr && typeof expr === "object") {
|
|
16184
16275
|
const keys = Object.keys(expr);
|
|
16276
|
+
if (keys.length === 0)
|
|
16277
|
+
throw new TypeError("[query-builder] deleteFrom.where({}): an empty object is not a filter. Pass a condition, or drop the where() call if deleting every row is intended.");
|
|
16185
16278
|
const conditions = [];
|
|
16186
16279
|
for (const key of keys) {
|
|
16187
16280
|
const paramIndex = delParams.length + 1;
|
|
@@ -16192,8 +16285,7 @@ function createQueryBuilder(state) {
|
|
|
16192
16285
|
built = null;
|
|
16193
16286
|
return this;
|
|
16194
16287
|
}
|
|
16195
|
-
|
|
16196
|
-
return this;
|
|
16288
|
+
throw new TypeError(`[query-builder] deleteFrom.where(): expected a condition, got ${expr === null ? "null" : typeof expr}. Refusing to run a DELETE with no WHERE \u2014 drop the where() call if deleting every row is intended.`);
|
|
16197
16289
|
},
|
|
16198
16290
|
whereNull(column) {
|
|
16199
16291
|
sqlText += ` ${getWhereKeyword()} ${quoteId(String(column))} IS NULL`;
|
|
@@ -16206,22 +16298,48 @@ function createQueryBuilder(state) {
|
|
|
16206
16298
|
return this;
|
|
16207
16299
|
},
|
|
16208
16300
|
returning(...cols) {
|
|
16209
|
-
const
|
|
16210
|
-
const
|
|
16301
|
+
const parent = this;
|
|
16302
|
+
const retText = () => `${sqlText} RETURNING ${cols.join(", ")}`;
|
|
16303
|
+
const build = () => delParams.length > 0 ? _sql.unsafe(retText(), delParams) : _sql.unsafe(retText());
|
|
16304
|
+
const runDelete = async () => {
|
|
16305
|
+
await activeHooks()?.beforeDelete?.({ table: String(table), where: whereCondition });
|
|
16306
|
+
const rows = await runWithHooks(build(), "delete");
|
|
16307
|
+
try {
|
|
16308
|
+
await activeHooks()?.afterDelete?.({ table: String(table), where: whereCondition, result: rows });
|
|
16309
|
+
} catch {}
|
|
16310
|
+
return rows;
|
|
16311
|
+
};
|
|
16211
16312
|
const runFirst = async () => {
|
|
16212
|
-
const rows = await
|
|
16313
|
+
const rows = await runDelete();
|
|
16213
16314
|
return Array.isArray(rows) ? rows[0] : rows;
|
|
16214
16315
|
};
|
|
16316
|
+
const unsupported = (name) => () => {
|
|
16317
|
+
throw new TypeError(`[query-builder] deleteFrom.returning(...).${name}() is not supported \u2014 a DELETE cannot be ordered or limited through this builder. Narrow it with where() instead.`);
|
|
16318
|
+
};
|
|
16215
16319
|
const obj = {
|
|
16216
|
-
where: () =>
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16320
|
+
where: (...args) => {
|
|
16321
|
+
parent.where(...args);
|
|
16322
|
+
return obj;
|
|
16323
|
+
},
|
|
16324
|
+
andWhere: (...args) => {
|
|
16325
|
+
parent.where(...args);
|
|
16326
|
+
return obj;
|
|
16327
|
+
},
|
|
16328
|
+
whereNull: (...args) => {
|
|
16329
|
+
parent.whereNull(...args);
|
|
16330
|
+
return obj;
|
|
16331
|
+
},
|
|
16332
|
+
whereNotNull: (...args) => {
|
|
16333
|
+
parent.whereNotNull(...args);
|
|
16334
|
+
return obj;
|
|
16335
|
+
},
|
|
16336
|
+
orWhere: unsupported("orWhere"),
|
|
16337
|
+
orderBy: unsupported("orderBy"),
|
|
16338
|
+
limit: unsupported("limit"),
|
|
16339
|
+
offset: unsupported("offset"),
|
|
16340
|
+
toSQL: () => makeExecutableQuery(build(), retText()),
|
|
16341
|
+
execute: () => runDelete(),
|
|
16342
|
+
get: () => runDelete(),
|
|
16225
16343
|
first: runFirst,
|
|
16226
16344
|
executeTakeFirst: runFirst,
|
|
16227
16345
|
async firstOrFail() {
|
|
@@ -16265,17 +16383,17 @@ function createQueryBuilder(state) {
|
|
|
16265
16383
|
return { numDeletedRows: result };
|
|
16266
16384
|
},
|
|
16267
16385
|
returningAll() {
|
|
16268
|
-
const retAllText = `${sqlText} RETURNING *`;
|
|
16269
|
-
const
|
|
16386
|
+
const retAllText = () => `${sqlText} RETURNING *`;
|
|
16387
|
+
const build = () => delParams.length > 0 ? _sql.unsafe(retAllText(), delParams) : _sql.unsafe(retAllText());
|
|
16270
16388
|
return {
|
|
16271
|
-
toSQL: () => makeExecutableQuery(
|
|
16272
|
-
execute: () => runWithHooks(
|
|
16389
|
+
toSQL: () => makeExecutableQuery(build(), retAllText()),
|
|
16390
|
+
execute: () => runWithHooks(build(), "delete"),
|
|
16273
16391
|
async executeTakeFirst() {
|
|
16274
|
-
const result = await runWithHooks(
|
|
16392
|
+
const result = await runWithHooks(build(), "delete");
|
|
16275
16393
|
return Array.isArray(result) ? result[0] : result;
|
|
16276
16394
|
},
|
|
16277
16395
|
async executeTakeFirstOrThrow() {
|
|
16278
|
-
const result = await runWithHooks(
|
|
16396
|
+
const result = await runWithHooks(build(), "delete");
|
|
16279
16397
|
const first = Array.isArray(result) ? result[0] : result;
|
|
16280
16398
|
if (!first)
|
|
16281
16399
|
throw new Error("Delete with RETURNING failed");
|
|
@@ -18939,11 +19057,66 @@ var init_vitess = __esm(() => {
|
|
|
18939
19057
|
};
|
|
18940
19058
|
});
|
|
18941
19059
|
|
|
18942
|
-
// src/
|
|
19060
|
+
// src/column-types.ts
|
|
18943
19061
|
function isNumericPlanType(type) {
|
|
18944
19062
|
return type === "integer" || type === "bigint" || type === "float" || type === "double" || type === "decimal";
|
|
18945
19063
|
}
|
|
19064
|
+
function normalizeAttributeType(raw2) {
|
|
19065
|
+
if (typeof raw2 !== "string")
|
|
19066
|
+
return;
|
|
19067
|
+
switch (raw2.toLowerCase()) {
|
|
19068
|
+
case "string":
|
|
19069
|
+
return "string";
|
|
19070
|
+
case "text":
|
|
19071
|
+
return "text";
|
|
19072
|
+
case "integer":
|
|
19073
|
+
case "int":
|
|
19074
|
+
return "integer";
|
|
19075
|
+
case "number":
|
|
19076
|
+
return "integer";
|
|
19077
|
+
case "bigint":
|
|
19078
|
+
return "bigint";
|
|
19079
|
+
case "float":
|
|
19080
|
+
return "float";
|
|
19081
|
+
case "double":
|
|
19082
|
+
return "double";
|
|
19083
|
+
case "decimal":
|
|
19084
|
+
return "decimal";
|
|
19085
|
+
case "boolean":
|
|
19086
|
+
case "bool":
|
|
19087
|
+
return "boolean";
|
|
19088
|
+
case "date":
|
|
19089
|
+
return "date";
|
|
19090
|
+
case "datetime":
|
|
19091
|
+
case "timestamp":
|
|
19092
|
+
return "datetime";
|
|
19093
|
+
case "timestamptz":
|
|
19094
|
+
case "timestamp_tz":
|
|
19095
|
+
return "timestamptz";
|
|
19096
|
+
case "json":
|
|
19097
|
+
return "json";
|
|
19098
|
+
case "enum":
|
|
19099
|
+
return "enum";
|
|
19100
|
+
default:
|
|
19101
|
+
return;
|
|
19102
|
+
}
|
|
19103
|
+
}
|
|
19104
|
+
function sqliteAffinityFor(type) {
|
|
19105
|
+
switch (type) {
|
|
19106
|
+
case "boolean":
|
|
19107
|
+
case "integer":
|
|
19108
|
+
case "bigint":
|
|
19109
|
+
return "INTEGER";
|
|
19110
|
+
case "float":
|
|
19111
|
+
case "double":
|
|
19112
|
+
case "decimal":
|
|
19113
|
+
return "REAL";
|
|
19114
|
+
default:
|
|
19115
|
+
return "TEXT";
|
|
19116
|
+
}
|
|
19117
|
+
}
|
|
18946
19118
|
|
|
19119
|
+
// src/drivers/sqlite.ts
|
|
18947
19120
|
class SQLiteDriver {
|
|
18948
19121
|
quoteIdentifier(id) {
|
|
18949
19122
|
return `"${id.replace(/"/g, '""')}"`;
|
|
@@ -18952,40 +19125,14 @@ class SQLiteDriver {
|
|
|
18952
19125
|
if (column.name.endsWith("_id") && isNumericPlanType(column.type)) {
|
|
18953
19126
|
return "INTEGER";
|
|
18954
19127
|
}
|
|
18955
|
-
|
|
18956
|
-
|
|
18957
|
-
|
|
18958
|
-
|
|
18959
|
-
|
|
18960
|
-
|
|
18961
|
-
return "INTEGER";
|
|
18962
|
-
case "integer":
|
|
18963
|
-
return "INTEGER";
|
|
18964
|
-
case "bigint":
|
|
18965
|
-
return "INTEGER";
|
|
18966
|
-
case "float":
|
|
18967
|
-
return "REAL";
|
|
18968
|
-
case "double":
|
|
18969
|
-
return "REAL";
|
|
18970
|
-
case "decimal":
|
|
18971
|
-
return "REAL";
|
|
18972
|
-
case "date":
|
|
18973
|
-
return "TEXT";
|
|
18974
|
-
case "datetime":
|
|
18975
|
-
return "TEXT";
|
|
18976
|
-
case "timestamptz":
|
|
18977
|
-
return "TEXT";
|
|
18978
|
-
case "json":
|
|
18979
|
-
return "TEXT";
|
|
18980
|
-
case "enum":
|
|
18981
|
-
if (column.enumValues && column.enumValues.length > 0) {
|
|
18982
|
-
const enumValues = column.enumValues.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
18983
|
-
return `TEXT CHECK (${this.quoteIdentifier(column.name)} IN (${enumValues}))`;
|
|
18984
|
-
}
|
|
18985
|
-
return "TEXT";
|
|
18986
|
-
default:
|
|
18987
|
-
return "TEXT";
|
|
19128
|
+
if (column.type === "enum") {
|
|
19129
|
+
if (column.enumValues && column.enumValues.length > 0) {
|
|
19130
|
+
const enumValues = column.enumValues.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
19131
|
+
return `TEXT CHECK (${this.quoteIdentifier(column.name)} IN (${enumValues}))`;
|
|
19132
|
+
}
|
|
19133
|
+
return "TEXT";
|
|
18988
19134
|
}
|
|
19135
|
+
return sqliteAffinityFor(column.type);
|
|
18989
19136
|
}
|
|
18990
19137
|
getPrimaryKeyType(column) {
|
|
18991
19138
|
return this.getColumnType(column);
|
|
@@ -25381,6 +25528,14 @@ function assertValidOrderByColumn(name, context) {
|
|
|
25381
25528
|
if (parts.length > 2 || parts.some((p2) => p2.length === 0 || p2.length > 64 || !SAFE_SQL_IDENTIFIER.test(p2)))
|
|
25382
25529
|
throw new TypeError(`[bun-query-builder] ${context}: invalid ORDER BY column '${name}' \u2014 expected 'column' or 'table.column' of [A-Za-z0-9_] \u2014 refusing to interpolate into SQL`);
|
|
25383
25530
|
}
|
|
25531
|
+
function registerLocalModel(name, model) {
|
|
25532
|
+
localModels.set(name, model);
|
|
25533
|
+
relationCache.clear();
|
|
25534
|
+
}
|
|
25535
|
+
function clearLocalModels() {
|
|
25536
|
+
localModels.clear();
|
|
25537
|
+
relationCache.clear();
|
|
25538
|
+
}
|
|
25384
25539
|
function getModelFromRegistry(name) {
|
|
25385
25540
|
if (!_getModel) {
|
|
25386
25541
|
try {
|
|
@@ -25391,7 +25546,7 @@ function getModelFromRegistry(name) {
|
|
|
25391
25546
|
};
|
|
25392
25547
|
}
|
|
25393
25548
|
}
|
|
25394
|
-
return _getModel(name);
|
|
25549
|
+
return _getModel(name) ?? localModels.get(name);
|
|
25395
25550
|
}
|
|
25396
25551
|
function toPostgresPlaceholders(sql2) {
|
|
25397
25552
|
const hit = pgPlaceholderCache.get(sql2);
|
|
@@ -25589,6 +25744,7 @@ function findAttributeDef(attrs, key) {
|
|
|
25589
25744
|
class ModelInstance {
|
|
25590
25745
|
_attributes;
|
|
25591
25746
|
_original;
|
|
25747
|
+
_forced = new Set;
|
|
25592
25748
|
_definition;
|
|
25593
25749
|
_hasSaved = false;
|
|
25594
25750
|
_relations = {};
|
|
@@ -25722,7 +25878,10 @@ class ModelInstance {
|
|
|
25722
25878
|
forceFill(data) {
|
|
25723
25879
|
if (this._original === null)
|
|
25724
25880
|
this._original = { ...this._attributes };
|
|
25725
|
-
|
|
25881
|
+
const normalized = normalizeAttributeKeys(data);
|
|
25882
|
+
Object.assign(this._attributes, normalized);
|
|
25883
|
+
for (const col of Object.keys(normalized))
|
|
25884
|
+
this._forced.add(col);
|
|
25726
25885
|
return this;
|
|
25727
25886
|
}
|
|
25728
25887
|
async save() {
|
|
@@ -25754,7 +25913,7 @@ class ModelInstance {
|
|
|
25754
25913
|
const attrs = this._definition.attributes;
|
|
25755
25914
|
const data = {};
|
|
25756
25915
|
for (const [key, attr] of Object.entries(attrs)) {
|
|
25757
|
-
if (attr.guarded)
|
|
25916
|
+
if (attr.guarded && !this._forced.has(toSnakeCase(key)))
|
|
25758
25917
|
continue;
|
|
25759
25918
|
const col = toSnakeCase(key);
|
|
25760
25919
|
if (this._attributes[col] !== undefined) {
|
|
@@ -27152,7 +27311,10 @@ function createModelInternal(definition) {
|
|
|
27152
27311
|
});
|
|
27153
27312
|
}
|
|
27154
27313
|
function createModel(definition) {
|
|
27155
|
-
|
|
27314
|
+
const model = createModelInternal(definition);
|
|
27315
|
+
if (definition?.name)
|
|
27316
|
+
registerLocalModel(definition.name, model);
|
|
27317
|
+
return model;
|
|
27156
27318
|
}
|
|
27157
27319
|
async function createTableFromModel(definition) {
|
|
27158
27320
|
const exec = getExecutor();
|
|
@@ -27169,12 +27331,9 @@ async function createTableFromModel(definition) {
|
|
|
27169
27331
|
if (emitted.has(name))
|
|
27170
27332
|
continue;
|
|
27171
27333
|
emitted.add(name);
|
|
27172
|
-
|
|
27173
|
-
|
|
27174
|
-
|
|
27175
|
-
else if (attr.type === "boolean")
|
|
27176
|
-
colType = "INTEGER";
|
|
27177
|
-
if (name.endsWith("_id"))
|
|
27334
|
+
const declaredType = normalizeAttributeType(attr.type);
|
|
27335
|
+
let colType = sqliteAffinityFor(declaredType);
|
|
27336
|
+
if (name.endsWith("_id") && (declaredType === undefined || isNumericPlanType(declaredType)))
|
|
27178
27337
|
colType = "INTEGER";
|
|
27179
27338
|
let colDef = `${name} ${colType}`;
|
|
27180
27339
|
if (attr.unique)
|
|
@@ -27240,12 +27399,13 @@ async function seedModel(definition, count, faker) {
|
|
|
27240
27399
|
await exec.run(`INSERT INTO ${definition.table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`, Object.values(data));
|
|
27241
27400
|
}
|
|
27242
27401
|
}
|
|
27243
|
-
var SAFE_SQL_IDENTIFIER, _getModel = null, pgPlaceholderCache, PG_PLACEHOLDER_CACHE_MAX = 500, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, SOFT_DELETE_COLUMN = "deleted_at", BTM_RELATED_ALIAS = "__btm_rel__", btmKeysCache, snakeCaseCache, tableNameCache, relationCache, RELATION_CACHE_MAX = 1000, RESOLVABLE_RELATION_KINDS, DECLARABLE_RELATION_KINDS;
|
|
27402
|
+
var SAFE_SQL_IDENTIFIER, _getModel = null, localModels, pgPlaceholderCache, PG_PLACEHOLDER_CACHE_MAX = 500, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, SOFT_DELETE_COLUMN = "deleted_at", BTM_RELATED_ALIAS = "__btm_rel__", btmKeysCache, snakeCaseCache, tableNameCache, relationCache, RELATION_CACHE_MAX = 1000, RESOLVABLE_RELATION_KINDS, DECLARABLE_RELATION_KINDS;
|
|
27244
27403
|
var init_orm = __esm(() => {
|
|
27245
27404
|
init_config();
|
|
27246
27405
|
init_db();
|
|
27247
27406
|
init_sqlite_pragmas();
|
|
27248
27407
|
SAFE_SQL_IDENTIFIER = /^[A-Z_][A-Z0-9_]*$/i;
|
|
27408
|
+
localModels = new Map;
|
|
27249
27409
|
pgPlaceholderCache = new Map;
|
|
27250
27410
|
btmKeysCache = new WeakMap;
|
|
27251
27411
|
snakeCaseCache = new Map;
|
|
@@ -27290,6 +27450,7 @@ function hasModel(name) {
|
|
|
27290
27450
|
}
|
|
27291
27451
|
function clearModelRegistry() {
|
|
27292
27452
|
modelRegistry.clear();
|
|
27453
|
+
clearLocalModels();
|
|
27293
27454
|
}
|
|
27294
27455
|
function registerModel(name, model) {
|
|
27295
27456
|
modelRegistry.set(name, model);
|
|
@@ -29065,7 +29226,7 @@ function detectTypeFromValidationRule(rule) {
|
|
|
29065
29226
|
}
|
|
29066
29227
|
return;
|
|
29067
29228
|
}
|
|
29068
|
-
function
|
|
29229
|
+
function normalizeAttributeType2(value) {
|
|
29069
29230
|
if (typeof value !== "string")
|
|
29070
29231
|
return;
|
|
29071
29232
|
const type = value.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
@@ -29155,7 +29316,7 @@ function buildMigrationPlan2(models, options) {
|
|
|
29155
29316
|
let inferred;
|
|
29156
29317
|
let enumValues;
|
|
29157
29318
|
const isPk = attrName === primaryKey;
|
|
29158
|
-
inferred =
|
|
29319
|
+
inferred = normalizeAttributeType2(attr.type) ?? detectTypeFromValidationRule(attr.validation?.rule);
|
|
29159
29320
|
if (inferred === "string") {
|
|
29160
29321
|
const maxLength = extractMaxFromRules(attr.validation?.rule);
|
|
29161
29322
|
if (maxLength && maxLength > MAX_BOUNDED_VARCHAR_LENGTH)
|
|
@@ -32354,7 +32515,7 @@ function getPrefix() {
|
|
|
32354
32515
|
}
|
|
32355
32516
|
var prefix = getPrefix();
|
|
32356
32517
|
// package.json
|
|
32357
|
-
var version2 = "0.2.
|
|
32518
|
+
var version2 = "0.2.44";
|
|
32358
32519
|
|
|
32359
32520
|
// bin/cli.ts
|
|
32360
32521
|
init_actions();
|
package/dist/client.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { config } from './config';
|
|
|
2
2
|
import { resetConnection } from './db';
|
|
3
3
|
import type { DatabaseSchema } from './schema';
|
|
4
4
|
import type { DriverConnection } from './db';
|
|
5
|
-
import type { QueryHooks } from './types';
|
|
5
|
+
import type { QueryHooks, SupportedDialect } from './types';
|
|
6
6
|
import type { SchemaMeta } from './meta';
|
|
7
7
|
/**
|
|
8
8
|
* # `raw`
|
|
@@ -31,6 +31,24 @@ import type { SchemaMeta } from './meta';
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
export declare function raw(strings: TemplateStringsArray | string, ...values: unknown[]): RawExpression;
|
|
34
|
+
/**
|
|
35
|
+
* Format a value for safe interpolation into a relationship-subquery
|
|
36
|
+
* fragment. Numbers / booleans / null pass through; everything else is
|
|
37
|
+
* rejected.
|
|
38
|
+
*
|
|
39
|
+
* **Strings are escaped for the configured dialect, not just for ANSI.**
|
|
40
|
+
* Doubling the single quote is correct on Postgres and SQLite and is not
|
|
41
|
+
* sufficient on the MySQL family, where a backslash is itself an escape
|
|
42
|
+
* character unless NO_BACKSLASH_ESCAPES is set. Doubling alone lets
|
|
43
|
+
* `x\'; DROP TABLE t; --` through: it becomes `'x\''; DROP TABLE t; --'`,
|
|
44
|
+
* MySQL reads `\'` as a literal quote inside the string, the next quote closes
|
|
45
|
+
* it, and the remainder executes as its own statement.
|
|
46
|
+
*
|
|
47
|
+
* So on a MySQL-like dialect the backslash is doubled first. Order matters:
|
|
48
|
+
* escaping quotes first and backslashes second would re-escape the backslash
|
|
49
|
+
* this function had just introduced.
|
|
50
|
+
*/
|
|
51
|
+
export declare function escapeStringLiteral(value: string, dialect?: SupportedDialect): string;
|
|
34
52
|
// eslint-disable-next-line pickier/no-unused-vars
|
|
35
53
|
export declare function createQueryBuilder<DB extends DatabaseSchema<any>>(state?: Partial<InternalState>): QueryBuilder<DB>;
|
|
36
54
|
/**
|