bun-query-builder 0.1.27 → 0.1.29
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 +26 -10
- package/dist/browser.d.ts +5 -2
- package/dist/client.d.ts +39 -19
- package/dist/orm.d.ts +34 -34
- package/dist/src/index.js +25 -9
- package/dist/type-inference.d.ts +34 -104
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -14728,17 +14728,15 @@ function createQueryBuilder(state) {
|
|
|
14728
14728
|
addWhereText("WHERE", `${String(col)} IS ${onlyTrashed ? "NOT " : ""}NULL`);
|
|
14729
14729
|
}
|
|
14730
14730
|
}
|
|
14731
|
+
const cacheKey = useCache ? `${String(finalQuery)}\x00${JSON.stringify(whereParams)}` : "";
|
|
14731
14732
|
if (useCache) {
|
|
14732
|
-
const cacheKey = String(finalQuery);
|
|
14733
14733
|
const cached = queryCache.get(cacheKey);
|
|
14734
14734
|
if (cached)
|
|
14735
14735
|
return cached;
|
|
14736
14736
|
}
|
|
14737
14737
|
const result = await runWithHooks(finalQuery, "select", { signal: abortSignal, timeoutMs });
|
|
14738
|
-
if (useCache)
|
|
14739
|
-
const cacheKey = String(finalQuery);
|
|
14738
|
+
if (useCache)
|
|
14740
14739
|
queryCache.set(cacheKey, result, cacheTtl);
|
|
14741
|
-
}
|
|
14742
14740
|
return hydratePivotRows(result);
|
|
14743
14741
|
},
|
|
14744
14742
|
async executeTakeFirst() {
|
|
@@ -23803,6 +23801,13 @@ function assertValidIdentifier(name, context) {
|
|
|
23803
23801
|
if (!SAFE_SQL_IDENTIFIER.test(name))
|
|
23804
23802
|
throw new TypeError(`[bun-query-builder] ${context}: identifier '${name}' contains characters outside [A-Za-z0-9_] \u2014 refusing to interpolate into SQL`);
|
|
23805
23803
|
}
|
|
23804
|
+
function assertValidOrderByColumn(name, context) {
|
|
23805
|
+
if (typeof name !== "string" || name.length === 0)
|
|
23806
|
+
throw new TypeError(`[bun-query-builder] ${context}: identifier must be a non-empty string, got ${typeof name}`);
|
|
23807
|
+
const parts = name.split(".");
|
|
23808
|
+
if (parts.length > 2 || parts.some((p2) => p2.length === 0 || p2.length > 64 || !SAFE_SQL_IDENTIFIER.test(p2)))
|
|
23809
|
+
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`);
|
|
23810
|
+
}
|
|
23806
23811
|
function getModelFromRegistry(name) {
|
|
23807
23812
|
if (!_getModel) {
|
|
23808
23813
|
try {
|
|
@@ -24091,12 +24096,16 @@ class ModelInstance {
|
|
|
24091
24096
|
for (const [key, value] of Object.entries(data)) {
|
|
24092
24097
|
const attr = attrs[key];
|
|
24093
24098
|
if (attr?.fillable && !attr?.guarded) {
|
|
24099
|
+
if (this._original === null)
|
|
24100
|
+
this._original = { ...this._attributes };
|
|
24094
24101
|
this._attributes[key] = value;
|
|
24095
24102
|
}
|
|
24096
24103
|
}
|
|
24097
24104
|
return this;
|
|
24098
24105
|
}
|
|
24099
24106
|
forceFill(data) {
|
|
24107
|
+
if (this._original === null)
|
|
24108
|
+
this._original = { ...this._attributes };
|
|
24100
24109
|
Object.assign(this._attributes, data);
|
|
24101
24110
|
return this;
|
|
24102
24111
|
}
|
|
@@ -24492,7 +24501,7 @@ class BelongsToManyRelationBuilder {
|
|
|
24492
24501
|
return this;
|
|
24493
24502
|
}
|
|
24494
24503
|
orderBy(column, direction = "asc") {
|
|
24495
|
-
|
|
24504
|
+
assertValidOrderByColumn(column, "orderBy(column)");
|
|
24496
24505
|
if (direction !== "asc" && direction !== "desc")
|
|
24497
24506
|
throw new TypeError(`[bun-query-builder] orderBy(direction): expected 'asc' or 'desc', got '${direction}'`);
|
|
24498
24507
|
this._orderBy.push(`${column} ${direction.toUpperCase()}`);
|
|
@@ -24833,7 +24842,7 @@ class ModelQueryBuilder {
|
|
|
24833
24842
|
return this;
|
|
24834
24843
|
}
|
|
24835
24844
|
orderBy(column, direction = "asc") {
|
|
24836
|
-
|
|
24845
|
+
assertValidOrderByColumn(column, "orderBy(column)");
|
|
24837
24846
|
if (direction !== "asc" && direction !== "desc")
|
|
24838
24847
|
throw new TypeError(`[bun-query-builder] orderBy(direction): expected 'asc' or 'desc', got '${direction}'`);
|
|
24839
24848
|
this._orderBy.push({ column, direction });
|
|
@@ -25194,7 +25203,14 @@ class ModelQueryBuilder {
|
|
|
25194
25203
|
sql2 += ` WHERE ${whereBody}`;
|
|
25195
25204
|
}
|
|
25196
25205
|
const row = await exec.get(sql2, params);
|
|
25197
|
-
|
|
25206
|
+
const v2 = row?.v;
|
|
25207
|
+
if (v2 == null)
|
|
25208
|
+
return null;
|
|
25209
|
+
if (typeof v2 === "string") {
|
|
25210
|
+
const n2 = Number(v2);
|
|
25211
|
+
return v2.trim() !== "" && !Number.isNaN(n2) ? n2 : v2;
|
|
25212
|
+
}
|
|
25213
|
+
return v2;
|
|
25198
25214
|
}
|
|
25199
25215
|
max(column) {
|
|
25200
25216
|
return this.aggregate("MAX", column);
|
|
@@ -25203,10 +25219,10 @@ class ModelQueryBuilder {
|
|
|
25203
25219
|
return this.aggregate("MIN", column);
|
|
25204
25220
|
}
|
|
25205
25221
|
async avg(column) {
|
|
25206
|
-
return await this.aggregate("AVG", column) || 0;
|
|
25222
|
+
return Number(await this.aggregate("AVG", column) ?? 0) || 0;
|
|
25207
25223
|
}
|
|
25208
25224
|
async sum(column) {
|
|
25209
|
-
return await this.aggregate("SUM", column) || 0;
|
|
25225
|
+
return Number(await this.aggregate("SUM", column) ?? 0) || 0;
|
|
25210
25226
|
}
|
|
25211
25227
|
async delete() {
|
|
25212
25228
|
const exec = getExecutor();
|
|
@@ -29920,7 +29936,7 @@ function getPrefix() {
|
|
|
29920
29936
|
}
|
|
29921
29937
|
var prefix = getPrefix();
|
|
29922
29938
|
// package.json
|
|
29923
|
-
var version2 = "0.1.
|
|
29939
|
+
var version2 = "0.1.29";
|
|
29924
29940
|
|
|
29925
29941
|
// bin/cli.ts
|
|
29926
29942
|
init_actions();
|
package/dist/browser.d.ts
CHANGED
|
@@ -201,9 +201,12 @@ declare type BrowserAttributeKeys<TDef extends BrowserModelDefinition> = keyof T
|
|
|
201
201
|
declare type InferBrowserAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
|
|
202
202
|
TAttr extends { factory: (faker: unknown) => infer R } ? R :
|
|
203
203
|
unknown;
|
|
204
|
-
// Build the full attributes type from definition
|
|
204
|
+
// Build the full attributes type from definition. Columns declared
|
|
205
|
+
// `nullable: true` admit null — mirrors InferAttributes in type-inference.ts.
|
|
205
206
|
declare type InferBrowserModelAttributes<TDef extends BrowserModelDefinition> = {
|
|
206
|
-
[K in BrowserAttributeKeys<TDef>]:
|
|
207
|
+
[K in BrowserAttributeKeys<TDef>]: TDef['attributes'][K] extends { nullable: true }
|
|
208
|
+
? InferBrowserAttributeType<TDef['attributes'][K]> | null
|
|
209
|
+
: InferBrowserAttributeType<TDef['attributes'][K]>
|
|
207
210
|
}
|
|
208
211
|
// System fields added by traits
|
|
209
212
|
declare type BrowserSystemFields<TDef extends BrowserModelDefinition> = { id: number } &
|
package/dist/client.d.ts
CHANGED
|
@@ -104,8 +104,11 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
|
|
|
104
104
|
having: (expr: WhereExpression<any>) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
105
105
|
havingRaw: (fragment: SqlFragment) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
106
106
|
addSelect: (...columns: ((keyof DB[TTable]['columns'] & string) | string | SqlFragment)[]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
107
|
-
select?:
|
|
108
|
-
|
|
107
|
+
select?: {
|
|
108
|
+
<K extends keyof DB[TTable]['columns'] & string>(columns: K[]): SelectQueryBuilder<DB, TTable, Pick<DB[TTable]['columns'], K>, TJoined>
|
|
109
|
+
(columns: string | SqlFragment | (string | SqlFragment)[]): SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
110
|
+
}
|
|
111
|
+
selectAll?: () => SelectQueryBuilder<DB, TTable, DB[TTable]['columns'], TJoined>
|
|
109
112
|
orderByRaw: (fragment: SqlFragment) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
110
113
|
union: (other: { toSQL: () => any }) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
111
114
|
unionAll: (other: { toSQL: () => any }) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
@@ -146,7 +149,7 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
|
|
|
146
149
|
}
|
|
147
150
|
exists: () => Promise<boolean>
|
|
148
151
|
doesntExist: () => Promise<boolean>
|
|
149
|
-
cursorPaginate: (perPage: number, cursor?: string | number, column?: string, direction?: 'asc' | 'desc') => Promise<{ data:
|
|
152
|
+
cursorPaginate: (perPage: number, cursor?: string | number, column?: string, direction?: 'asc' | 'desc') => Promise<{ data: SelectedRow<DB, TTable, TSelected>[], meta: { perPage: number, nextCursor: string | number | null } }>
|
|
150
153
|
chunk: (size: number, handler: (rows: any[]) => Promise<void> | void) => Promise<void>
|
|
151
154
|
chunkById: (size: number, column?: string, handler?: (rows: any[]) => Promise<void> | void) => Promise<void>
|
|
152
155
|
eachById: (size: number, column?: string, handler?: (row: any) => Promise<void> | void) => Promise<void>
|
|
@@ -175,8 +178,8 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
|
|
|
175
178
|
count: () => Promise<number>
|
|
176
179
|
avg: (column: keyof DB[TTable]['columns'] & string) => Promise<number>
|
|
177
180
|
sum: (column: keyof DB[TTable]['columns'] & string) => Promise<number>
|
|
178
|
-
max:
|
|
179
|
-
min:
|
|
181
|
+
max: <K extends keyof DB[TTable]['columns'] & string>(column: K) => Promise<DB[TTable]['columns'][K] | null>
|
|
182
|
+
min: <K extends keyof DB[TTable]['columns'] & string>(column: K) => Promise<DB[TTable]['columns'][K] | null>
|
|
180
183
|
readonly rows: TSelected[]
|
|
181
184
|
readonly row: TSelected
|
|
182
185
|
values: () => Promise<any[][]>
|
|
@@ -224,13 +227,19 @@ export declare interface TableQueryBuilder<DB extends DatabaseSchema<any>, TTabl
|
|
|
224
227
|
insert: (data: Partial<DB[TTable]['columns']> | Partial<DB[TTable]['columns']>[]) => InsertQueryBuilder<DB, TTable>
|
|
225
228
|
update: (values: Partial<DB[TTable]['columns']>) => UpdateQueryBuilder<DB, TTable>
|
|
226
229
|
delete: () => DeleteQueryBuilder<DB, TTable>
|
|
227
|
-
select:
|
|
230
|
+
select: <K extends keyof DB[TTable]['columns'] & string>(...columns: K[]) => SelectQueryBuilder<DB, TTable, Pick<DB[TTable]['columns'], K>>
|
|
228
231
|
}
|
|
229
232
|
export declare interface QueryBuilder<DB extends DatabaseSchema<any>> {
|
|
230
|
-
select:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
select: {
|
|
234
|
+
<TTable extends keyof DB & string, K extends keyof DB[TTable]['columns'] & string>(
|
|
235
|
+
table: TTable,
|
|
236
|
+
...columns: K[]
|
|
237
|
+
): SelectQueryBuilder<DB, TTable, Pick<DB[TTable]['columns'], K>>
|
|
238
|
+
<TTable extends keyof DB & string>(
|
|
239
|
+
table: TTable,
|
|
240
|
+
...columns: ((keyof DB[TTable]['columns'] & string) | `${string} as ${string}`)[]
|
|
241
|
+
): SelectQueryBuilder<DB, TTable, any>
|
|
242
|
+
}
|
|
234
243
|
selectFrom: <TTable extends keyof DB & string>(table: TTable) => TypedSelectQueryBuilder<DB, TTable, DB[TTable]['columns'], TTable, `SELECT * FROM ${TTable}`>
|
|
235
244
|
insertInto: <TTable extends keyof DB & string>(table: TTable) => TypedInsertQueryBuilder<DB, TTable>
|
|
236
245
|
updateTable: <TTable extends keyof DB & string>(table: TTable) => UpdateQueryBuilder<DB, TTable>
|
|
@@ -413,22 +422,29 @@ declare type JoinColumn<DB extends DatabaseSchema<any>, TTables extends string>
|
|
|
413
422
|
*
|
|
414
423
|
* The relation names declared for a table, read from the type-level
|
|
415
424
|
* `relations` map that `DatabaseSchema` carries. Falls back to `string`
|
|
416
|
-
* for hand-written schema types that don't declare relation metadata
|
|
417
|
-
*
|
|
425
|
+
* for hand-written schema types that don't declare relation metadata
|
|
426
|
+
* (inferred `R` is `unknown` when the property is absent), so existing
|
|
427
|
+
* untyped schemas keep compiling. A table that declares ZERO relations
|
|
428
|
+
* yields `never` — every relation name is rejected.
|
|
418
429
|
*/
|
|
419
430
|
export type TableRelationName<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = DB[TTable] extends { relations?: infer R }
|
|
420
|
-
?
|
|
431
|
+
? unknown extends R ? string : keyof NonNullable<R> & string
|
|
421
432
|
: string;
|
|
422
433
|
/**
|
|
423
434
|
* # `WithRelationArg<DB, TTable>`
|
|
424
435
|
*
|
|
425
436
|
* Argument accepted by `.with()`: a declared relation name, a dotted nested
|
|
426
437
|
* path rooted at a declared relation (`'posts.comments'`), or a record
|
|
427
|
-
* mapping relation names to constraint callbacks.
|
|
438
|
+
* mapping relation names to constraint callbacks. A table with zero declared
|
|
439
|
+
* relations accepts nothing (the bare `Partial<Record<never, ...>>` would be
|
|
440
|
+
* `{}`, which strings are assignable to — hence the explicit never guard).
|
|
428
441
|
*/
|
|
429
|
-
export type WithRelationArg<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> =
|
|
430
|
-
|
|
431
|
-
|
|
442
|
+
export type WithRelationArg<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = [TableRelationName<DB, TTable>] extends [never]
|
|
443
|
+
? never
|
|
444
|
+
:
|
|
445
|
+
| TableRelationName<DB, TTable>
|
|
446
|
+
| `${TableRelationName<DB, TTable>}.${string}`
|
|
447
|
+
| Partial<Record<TableRelationName<DB, TTable>, (qb: any) => any>>;
|
|
432
448
|
// Convert snake_case to PascalCase at the type level (e.g. created_at -> CreatedAt)
|
|
433
449
|
declare type SnakeToPascal<S extends string> = S extends `${infer H}_${infer T}`
|
|
434
450
|
? `${Capitalize<H>}${SnakeToPascal<T>}`
|
|
@@ -449,11 +465,15 @@ declare type _TypedDynamicWhereMethods<DB extends DatabaseSchema<any>, TTable ex
|
|
|
449
465
|
value: DB[TTable]['columns'][K],
|
|
450
466
|
) => TypedSelectQueryBuilder<DB, TTable, TSelected, TJoined, `${TSql} AND ${K} = ?`>
|
|
451
467
|
}
|
|
468
|
+
// NOTE: TypedSelectQueryBuilder must NOT also intersect DynamicWhereMethods —
|
|
469
|
+
// _TypedDynamicWhereMethods declares the same `where<Column>` keys, and the
|
|
470
|
+
// untyped variant (returning a plain SelectQueryBuilder) would win overload
|
|
471
|
+
// resolution, silently downgrading `toSQL()` from the composed literal SQL
|
|
472
|
+
// type back to `string` after any dynamic-where call.
|
|
452
473
|
export type TypedSelectQueryBuilder<DB extends DatabaseSchema<any>, TTable extends keyof DB & string, TSelected, TJoined extends string = TTable, TSql extends string = `SELECT * FROM ${TTable}`,> = Omit<
|
|
453
474
|
BaseSelectQueryBuilder<DB, TTable, TSelected, TJoined>,
|
|
454
475
|
'toSQL' | 'where' | 'andWhere' | 'orWhere' | 'orderBy' | 'limit'
|
|
455
|
-
> &
|
|
456
|
-
& _TypedDynamicWhereMethods<DB, TTable, TSelected, TJoined, TSql>
|
|
476
|
+
> & _TypedDynamicWhereMethods<DB, TTable, TSelected, TJoined, TSql>
|
|
457
477
|
& {
|
|
458
478
|
toSQL: () => TSql
|
|
459
479
|
where: (<K extends keyof DB[TTable]['columns'] & string>(
|
package/dist/orm.d.ts
CHANGED
|
@@ -196,12 +196,17 @@ declare type AttributeKeys<TDef extends ModelDefinition> = keyof TDef['attribute
|
|
|
196
196
|
declare type InferAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
|
|
197
197
|
TAttr extends { factory: (faker: Faker) => infer R } ? R :
|
|
198
198
|
unknown;
|
|
199
|
-
// Build the full attributes type from definition
|
|
199
|
+
// Build the full attributes type from definition. Columns declared
|
|
200
|
+
// `nullable: true` admit null — mirrors InferAttributes in type-inference.ts.
|
|
200
201
|
declare type InferModelAttributes<TDef extends ModelDefinition> = {
|
|
201
|
-
[K in AttributeKeys<TDef>]:
|
|
202
|
+
[K in AttributeKeys<TDef>]: TDef['attributes'][K] extends { nullable: true }
|
|
203
|
+
? InferAttributeType<TDef['attributes'][K]> | null
|
|
204
|
+
: InferAttributeType<TDef['attributes'][K]>
|
|
202
205
|
}
|
|
203
|
-
// System fields added by traits
|
|
204
|
-
|
|
206
|
+
// System fields added by traits. The primary-key column honors the model's
|
|
207
|
+
// declared `primaryKey` (default 'id') — a custom-pk model exposes THAT
|
|
208
|
+
// column, not a phantom 'id'. Mirrors InferAttributes in type-inference.ts.
|
|
209
|
+
declare type SystemFields<TDef extends ModelDefinition> = { [K in TDef extends { primaryKey: infer PK extends string } ? PK : 'id']: number } &
|
|
205
210
|
(TDef['traits'] extends { useUuid: true } ? { uuid: string } : {}) &
|
|
206
211
|
(TDef['traits'] extends { useTimestamps: true } ? { created_at: string; updated_at: string | null } : {}) &
|
|
207
212
|
(TDef['traits'] extends { timestampable: true | object } ? { created_at: string; updated_at: string | null } : {}) &
|
|
@@ -213,7 +218,7 @@ declare type SystemFields<TDef extends ModelDefinition> = { id: number } &
|
|
|
213
218
|
declare type ModelAttributes<TDef extends ModelDefinition> = InferModelAttributes<TDef> & SystemFields<TDef>;
|
|
214
219
|
// All valid column names
|
|
215
220
|
declare type ColumnName<TDef extends ModelDefinition> = | AttributeKeys<TDef>
|
|
216
|
-
| 'id'
|
|
221
|
+
| (TDef extends { primaryKey: infer PK extends string } ? PK : 'id')
|
|
217
222
|
| (TDef['traits'] extends { useUuid: true } ? 'uuid' : never)
|
|
218
223
|
| (TDef['traits'] extends { useTimestamps: true } ? 'created_at' | 'updated_at' : never)
|
|
219
224
|
| (TDef['traits'] extends { timestampable: true | object } ? 'created_at' | 'updated_at' : never)
|
|
@@ -233,31 +238,26 @@ declare type FillableKeys<TDef extends ModelDefinition> = {
|
|
|
233
238
|
declare type NumericColumns<TDef extends ModelDefinition> = {
|
|
234
239
|
[K in AttributeKeys<TDef>]: TDef['attributes'][K] extends { type: 'number' } ? K : never
|
|
235
240
|
}[AttributeKeys<TDef>];
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
?
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
declare type
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
? K extends string ? K : never : never);
|
|
257
|
-
declare type InferHasManyThroughNames<TDef> = (TDef extends { hasManyThrough: readonly (infer R)[] }
|
|
258
|
-
? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never)
|
|
259
|
-
| (TDef extends { hasManyThrough: Readonly<Record<infer K, unknown>> }
|
|
260
|
-
? K extends string ? K : never : never);
|
|
241
|
+
/**
|
|
242
|
+
* Relation names of one relation declaration. Array form lowercases the
|
|
243
|
+
* (unwrapped) model name; record form uses the keys. The array case MUST be
|
|
244
|
+
* checked first: a tuple also structurally matches `Readonly<Record<...>>`
|
|
245
|
+
* and would otherwise leak its own keys ('length', indices, ...) into the
|
|
246
|
+
* relation-name union.
|
|
247
|
+
*/
|
|
248
|
+
declare type RelationKeyOf<V> = V extends readonly (infer E)[]
|
|
249
|
+
? E extends string ? Lowercase<E>
|
|
250
|
+
: E extends { model: infer M extends string } ? Lowercase<M>
|
|
251
|
+
: never
|
|
252
|
+
: V extends Readonly<Record<infer K, unknown>>
|
|
253
|
+
? K & string
|
|
254
|
+
: never;
|
|
255
|
+
declare type InferBelongsToNames<TDef> = TDef extends { belongsTo: infer V } ? RelationKeyOf<V> : never;
|
|
256
|
+
declare type InferHasManyNames<TDef> = TDef extends { hasMany: infer V } ? RelationKeyOf<V> : never;
|
|
257
|
+
declare type InferHasOneNames<TDef> = TDef extends { hasOne: infer V } ? RelationKeyOf<V> : never;
|
|
258
|
+
declare type InferBelongsToManyNames<TDef> = TDef extends { belongsToMany: infer V } ? RelationKeyOf<V> : never;
|
|
259
|
+
declare type InferHasOneThroughNames<TDef> = TDef extends { hasOneThrough: infer V } ? RelationKeyOf<V> : never;
|
|
260
|
+
declare type InferHasManyThroughNames<TDef> = TDef extends { hasManyThrough: infer V } ? RelationKeyOf<V> : never;
|
|
261
261
|
export type InferRelationNames<TDef> = | InferBelongsToNames<TDef>
|
|
262
262
|
| InferHasManyNames<TDef>
|
|
263
263
|
| InferHasOneNames<TDef>
|
|
@@ -317,8 +317,8 @@ declare class ModelInstance<TDef extends ModelDefinition, TSelected extends Colu
|
|
|
317
317
|
getAttribute<K extends TSelected>(key: K): K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown;
|
|
318
318
|
getAttributes(): Pick<ModelAttributes<TDef>, TSelected & keyof ModelAttributes<TDef>>;
|
|
319
319
|
set<K extends ColumnName<TDef>>(key: K, value: K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown): void;
|
|
320
|
-
only<K extends TSelected>(keys: ReadonlyArray<K>):
|
|
321
|
-
except<K extends TSelected>(keys: ReadonlyArray<K>):
|
|
320
|
+
only<K extends TSelected>(keys: ReadonlyArray<K>): Pick<ModelAttributes<TDef>, K & keyof ModelAttributes<TDef>>;
|
|
321
|
+
except<K extends TSelected>(keys: ReadonlyArray<K>): Omit<Pick<ModelAttributes<TDef>, TSelected & keyof ModelAttributes<TDef>>, K>;
|
|
322
322
|
getRelation<R extends InferRelationNames<TDef> & string>(name: R): LoadedRelationValue<TDef, R>;
|
|
323
323
|
setRelation(name: string, data: ModelInstance<any, any>[] | ModelInstance<any, any> | null): void;
|
|
324
324
|
getLoadedRelations(): Record<string, ModelInstance<any, any>[] | ModelInstance<any, any> | null>;
|
|
@@ -440,8 +440,8 @@ declare class ModelQueryBuilder<TDef extends ModelDefinition, TSelected extends
|
|
|
440
440
|
to: number | null
|
|
441
441
|
}>;
|
|
442
442
|
pluck<K extends ColumnName<TDef>>(column: K): Promise<(K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown)[]>;
|
|
443
|
-
max<K extends ColumnName<TDef>>(column: K): Promise<number | null>;
|
|
444
|
-
min<K extends ColumnName<TDef>>(column: K): Promise<number | null>;
|
|
443
|
+
max<K extends ColumnName<TDef>>(column: K): Promise<(K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : number) | null>;
|
|
444
|
+
min<K extends ColumnName<TDef>>(column: K): Promise<(K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : number) | null>;
|
|
445
445
|
avg<K extends NumericColumns<TDef>>(column: K): Promise<number>;
|
|
446
446
|
sum<K extends NumericColumns<TDef>>(column: K): Promise<number>;
|
|
447
447
|
delete(): Promise<number>;
|
package/dist/src/index.js
CHANGED
|
@@ -14728,17 +14728,15 @@ function createQueryBuilder(state) {
|
|
|
14728
14728
|
addWhereText("WHERE", `${String(col)} IS ${onlyTrashed ? "NOT " : ""}NULL`);
|
|
14729
14729
|
}
|
|
14730
14730
|
}
|
|
14731
|
+
const cacheKey = useCache ? `${String(finalQuery)}\x00${JSON.stringify(whereParams)}` : "";
|
|
14731
14732
|
if (useCache) {
|
|
14732
|
-
const cacheKey = String(finalQuery);
|
|
14733
14733
|
const cached = queryCache.get(cacheKey);
|
|
14734
14734
|
if (cached)
|
|
14735
14735
|
return cached;
|
|
14736
14736
|
}
|
|
14737
14737
|
const result = await runWithHooks(finalQuery, "select", { signal: abortSignal, timeoutMs });
|
|
14738
|
-
if (useCache)
|
|
14739
|
-
const cacheKey = String(finalQuery);
|
|
14738
|
+
if (useCache)
|
|
14740
14739
|
queryCache.set(cacheKey, result, cacheTtl);
|
|
14741
|
-
}
|
|
14742
14740
|
return hydratePivotRows(result);
|
|
14743
14741
|
},
|
|
14744
14742
|
async executeTakeFirst() {
|
|
@@ -23803,6 +23801,13 @@ function assertValidIdentifier(name, context) {
|
|
|
23803
23801
|
if (!SAFE_SQL_IDENTIFIER.test(name))
|
|
23804
23802
|
throw new TypeError(`[bun-query-builder] ${context}: identifier '${name}' contains characters outside [A-Za-z0-9_] \u2014 refusing to interpolate into SQL`);
|
|
23805
23803
|
}
|
|
23804
|
+
function assertValidOrderByColumn(name, context) {
|
|
23805
|
+
if (typeof name !== "string" || name.length === 0)
|
|
23806
|
+
throw new TypeError(`[bun-query-builder] ${context}: identifier must be a non-empty string, got ${typeof name}`);
|
|
23807
|
+
const parts = name.split(".");
|
|
23808
|
+
if (parts.length > 2 || parts.some((p2) => p2.length === 0 || p2.length > 64 || !SAFE_SQL_IDENTIFIER.test(p2)))
|
|
23809
|
+
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`);
|
|
23810
|
+
}
|
|
23806
23811
|
function getModelFromRegistry(name) {
|
|
23807
23812
|
if (!_getModel) {
|
|
23808
23813
|
try {
|
|
@@ -24091,12 +24096,16 @@ class ModelInstance {
|
|
|
24091
24096
|
for (const [key, value] of Object.entries(data)) {
|
|
24092
24097
|
const attr = attrs[key];
|
|
24093
24098
|
if (attr?.fillable && !attr?.guarded) {
|
|
24099
|
+
if (this._original === null)
|
|
24100
|
+
this._original = { ...this._attributes };
|
|
24094
24101
|
this._attributes[key] = value;
|
|
24095
24102
|
}
|
|
24096
24103
|
}
|
|
24097
24104
|
return this;
|
|
24098
24105
|
}
|
|
24099
24106
|
forceFill(data) {
|
|
24107
|
+
if (this._original === null)
|
|
24108
|
+
this._original = { ...this._attributes };
|
|
24100
24109
|
Object.assign(this._attributes, data);
|
|
24101
24110
|
return this;
|
|
24102
24111
|
}
|
|
@@ -24492,7 +24501,7 @@ class BelongsToManyRelationBuilder {
|
|
|
24492
24501
|
return this;
|
|
24493
24502
|
}
|
|
24494
24503
|
orderBy(column, direction = "asc") {
|
|
24495
|
-
|
|
24504
|
+
assertValidOrderByColumn(column, "orderBy(column)");
|
|
24496
24505
|
if (direction !== "asc" && direction !== "desc")
|
|
24497
24506
|
throw new TypeError(`[bun-query-builder] orderBy(direction): expected 'asc' or 'desc', got '${direction}'`);
|
|
24498
24507
|
this._orderBy.push(`${column} ${direction.toUpperCase()}`);
|
|
@@ -24833,7 +24842,7 @@ class ModelQueryBuilder {
|
|
|
24833
24842
|
return this;
|
|
24834
24843
|
}
|
|
24835
24844
|
orderBy(column, direction = "asc") {
|
|
24836
|
-
|
|
24845
|
+
assertValidOrderByColumn(column, "orderBy(column)");
|
|
24837
24846
|
if (direction !== "asc" && direction !== "desc")
|
|
24838
24847
|
throw new TypeError(`[bun-query-builder] orderBy(direction): expected 'asc' or 'desc', got '${direction}'`);
|
|
24839
24848
|
this._orderBy.push({ column, direction });
|
|
@@ -25194,7 +25203,14 @@ class ModelQueryBuilder {
|
|
|
25194
25203
|
sql2 += ` WHERE ${whereBody}`;
|
|
25195
25204
|
}
|
|
25196
25205
|
const row = await exec.get(sql2, params);
|
|
25197
|
-
|
|
25206
|
+
const v2 = row?.v;
|
|
25207
|
+
if (v2 == null)
|
|
25208
|
+
return null;
|
|
25209
|
+
if (typeof v2 === "string") {
|
|
25210
|
+
const n2 = Number(v2);
|
|
25211
|
+
return v2.trim() !== "" && !Number.isNaN(n2) ? n2 : v2;
|
|
25212
|
+
}
|
|
25213
|
+
return v2;
|
|
25198
25214
|
}
|
|
25199
25215
|
max(column) {
|
|
25200
25216
|
return this.aggregate("MAX", column);
|
|
@@ -25203,10 +25219,10 @@ class ModelQueryBuilder {
|
|
|
25203
25219
|
return this.aggregate("MIN", column);
|
|
25204
25220
|
}
|
|
25205
25221
|
async avg(column) {
|
|
25206
|
-
return await this.aggregate("AVG", column) || 0;
|
|
25222
|
+
return Number(await this.aggregate("AVG", column) ?? 0) || 0;
|
|
25207
25223
|
}
|
|
25208
25224
|
async sum(column) {
|
|
25209
|
-
return await this.aggregate("SUM", column) || 0;
|
|
25225
|
+
return Number(await this.aggregate("SUM", column) ?? 0) || 0;
|
|
25210
25226
|
}
|
|
25211
25227
|
async delete() {
|
|
25212
25228
|
const exec = getExecutor();
|
package/dist/type-inference.d.ts
CHANGED
|
@@ -302,112 +302,42 @@ export type InferPivotColumns<TModel, R extends string> = ResolveDefinition<TMod
|
|
|
302
302
|
: Record<string, unknown>
|
|
303
303
|
: Record<string, unknown>
|
|
304
304
|
: Record<string, unknown>;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
declare type
|
|
313
|
-
?
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
declare type
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
declare type
|
|
325
|
-
? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never)
|
|
326
|
-
| (TDef extends { hasOneThrough: Readonly<Record<infer K, unknown>> }
|
|
327
|
-
? K extends string ? K : never : never);
|
|
328
|
-
declare type InferHasManyThroughNames<TDef> = (TDef extends { hasManyThrough: readonly (infer R)[] }
|
|
329
|
-
? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never)
|
|
330
|
-
| (TDef extends { hasManyThrough: Readonly<Record<infer K, unknown>> }
|
|
331
|
-
? K extends string ? K : never : never);
|
|
305
|
+
/**
|
|
306
|
+
* Relation names of one relation declaration. Array form lowercases the
|
|
307
|
+
* (unwrapped) model name; record form uses the keys. The array case MUST be
|
|
308
|
+
* checked first: a tuple also structurally matches `Readonly<Record<...>>`
|
|
309
|
+
* and would otherwise leak its own keys ('length', indices, ...) into the
|
|
310
|
+
* relation-name union.
|
|
311
|
+
*/
|
|
312
|
+
declare type RelationKeyOf<V> = V extends readonly (infer E)[]
|
|
313
|
+
? E extends string ? Lowercase<E>
|
|
314
|
+
: E extends { model: infer M extends string } ? Lowercase<M>
|
|
315
|
+
: never
|
|
316
|
+
: V extends Readonly<Record<infer K, unknown>>
|
|
317
|
+
? K & string
|
|
318
|
+
: never;
|
|
319
|
+
declare type InferBelongsToNames<TDef> = TDef extends { belongsTo: infer V } ? RelationKeyOf<V> : never;
|
|
320
|
+
declare type InferHasManyNames<TDef> = TDef extends { hasMany: infer V } ? RelationKeyOf<V> : never;
|
|
321
|
+
declare type InferHasOneNames<TDef> = TDef extends { hasOne: infer V } ? RelationKeyOf<V> : never;
|
|
322
|
+
declare type InferBelongsToManyNames<TDef> = TDef extends { belongsToMany: infer V } ? RelationKeyOf<V> : never;
|
|
323
|
+
declare type InferHasOneThroughNames<TDef> = TDef extends { hasOneThrough: infer V } ? RelationKeyOf<V> : never;
|
|
324
|
+
declare type InferHasManyThroughNames<TDef> = TDef extends { hasManyThrough: infer V } ? RelationKeyOf<V> : never;
|
|
332
325
|
/**
|
|
333
326
|
* Determine the cardinality of a relation on a model.
|
|
334
|
-
* hasMany
|
|
327
|
+
* hasMany / belongsToMany / hasManyThrough / morphMany / morphToMany /
|
|
328
|
+
* morphedByMany → 'many'; hasOne / belongsTo / hasOneThrough / morphOne →
|
|
329
|
+
* 'one'. Both array and record declaration forms are supported via
|
|
330
|
+
* `RelationKeyOf` (array-first, so tuple keys never leak in).
|
|
335
331
|
*/
|
|
336
332
|
export type RelationCardinality<TModel, R extends string> = ResolveDefinition<TModel> extends infer TDef
|
|
337
|
-
?
|
|
338
|
-
(TDef extends {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
| (TDef extends {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
| (TDef extends {
|
|
347
|
-
? Lowercase<M & string> extends R ? 'one' : never
|
|
348
|
-
: never)
|
|
349
|
-
// hasOne object syntax
|
|
350
|
-
| (TDef extends { hasOne: Readonly<Record<infer K, unknown>> }
|
|
351
|
-
? K extends string ? K extends R ? 'one' : never : never
|
|
352
|
-
: never)
|
|
353
|
-
// belongsTo array syntax
|
|
354
|
-
| (TDef extends { belongsTo: readonly (infer M)[] }
|
|
355
|
-
? Lowercase<M & string> extends R ? 'one' : never
|
|
356
|
-
: never)
|
|
357
|
-
// belongsTo object syntax
|
|
358
|
-
| (TDef extends { belongsTo: Readonly<Record<infer K, unknown>> }
|
|
359
|
-
? K extends string ? K extends R ? 'one' : never : never
|
|
360
|
-
: never)
|
|
361
|
-
// belongsToMany array syntax
|
|
362
|
-
| (TDef extends { belongsToMany: readonly (infer M)[] }
|
|
363
|
-
? M extends string
|
|
364
|
-
? Lowercase<M> extends R ? 'many' : never
|
|
365
|
-
: M extends { model: infer N extends string }
|
|
366
|
-
? Lowercase<N> extends R ? 'many' : never
|
|
367
|
-
: never
|
|
368
|
-
: never)
|
|
369
|
-
// belongsToMany object syntax
|
|
370
|
-
| (TDef extends { belongsToMany: Readonly<Record<infer K, unknown>> }
|
|
371
|
-
? K extends string ? K extends R ? 'many' : never : never
|
|
372
|
-
: never)
|
|
373
|
-
// hasOneThrough array syntax ({ model } entries)
|
|
374
|
-
| (TDef extends { hasOneThrough: readonly (infer M)[] }
|
|
375
|
-
? M extends string
|
|
376
|
-
? Lowercase<M> extends R ? 'one' : never
|
|
377
|
-
: M extends { model: infer N extends string }
|
|
378
|
-
? Lowercase<N> extends R ? 'one' : never
|
|
379
|
-
: never
|
|
380
|
-
: never)
|
|
381
|
-
// hasOneThrough object syntax
|
|
382
|
-
| (TDef extends { hasOneThrough: Readonly<Record<infer K, unknown>> }
|
|
383
|
-
? K extends string ? K extends R ? 'one' : never : never
|
|
384
|
-
: never)
|
|
385
|
-
// hasManyThrough array syntax ({ model } entries)
|
|
386
|
-
| (TDef extends { hasManyThrough: readonly (infer M)[] }
|
|
387
|
-
? M extends string
|
|
388
|
-
? Lowercase<M> extends R ? 'many' : never
|
|
389
|
-
: M extends { model: infer N extends string }
|
|
390
|
-
? Lowercase<N> extends R ? 'many' : never
|
|
391
|
-
: never
|
|
392
|
-
: never)
|
|
393
|
-
// hasManyThrough object syntax
|
|
394
|
-
| (TDef extends { hasManyThrough: Readonly<Record<infer K, unknown>> }
|
|
395
|
-
? K extends string ? K extends R ? 'many' : never : never
|
|
396
|
-
: never)
|
|
397
|
-
// morphOne object syntax
|
|
398
|
-
| (TDef extends { morphOne: Readonly<Record<infer K, unknown>> }
|
|
399
|
-
? K extends string ? K extends R ? 'one' : never : never
|
|
400
|
-
: never)
|
|
401
|
-
// morphMany object syntax
|
|
402
|
-
| (TDef extends { morphMany: Readonly<Record<infer K, unknown>> }
|
|
403
|
-
? K extends string ? K extends R ? 'many' : never : never
|
|
404
|
-
: never)
|
|
405
|
-
// morphToMany object syntax
|
|
406
|
-
| (TDef extends { morphToMany: Readonly<Record<infer K, unknown>> }
|
|
407
|
-
? K extends string ? K extends R ? 'many' : never : never
|
|
408
|
-
: never)
|
|
409
|
-
// morphedByMany object syntax
|
|
410
|
-
| (TDef extends { morphedByMany: Readonly<Record<infer K, unknown>> }
|
|
411
|
-
? K extends string ? K extends R ? 'many' : never : never
|
|
412
|
-
: never)
|
|
333
|
+
? (TDef extends { hasMany: infer V } ? (R extends RelationKeyOf<V> ? 'many' : never) : never)
|
|
334
|
+
| (TDef extends { hasOne: infer V } ? (R extends RelationKeyOf<V> ? 'one' : never) : never)
|
|
335
|
+
| (TDef extends { belongsTo: infer V } ? (R extends RelationKeyOf<V> ? 'one' : never) : never)
|
|
336
|
+
| (TDef extends { belongsToMany: infer V } ? (R extends RelationKeyOf<V> ? 'many' : never) : never)
|
|
337
|
+
| (TDef extends { hasOneThrough: infer V } ? (R extends RelationKeyOf<V> ? 'one' : never) : never)
|
|
338
|
+
| (TDef extends { hasManyThrough: infer V } ? (R extends RelationKeyOf<V> ? 'many' : never) : never)
|
|
339
|
+
| (TDef extends { morphOne: infer V } ? (R extends RelationKeyOf<V> ? 'one' : never) : never)
|
|
340
|
+
| (TDef extends { morphMany: infer V } ? (R extends RelationKeyOf<V> ? 'many' : never) : never)
|
|
341
|
+
| (TDef extends { morphToMany: infer V } ? (R extends RelationKeyOf<V> ? 'many' : never) : never)
|
|
342
|
+
| (TDef extends { morphedByMany: infer V } ? (R extends RelationKeyOf<V> ? 'many' : never) : never)
|
|
413
343
|
: never;
|
package/package.json
CHANGED