bun-query-builder 0.1.26 → 0.1.28
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 +159 -88
- package/dist/browser.d.ts +5 -2
- package/dist/client.d.ts +44 -7
- package/dist/orm.d.ts +44 -31
- package/dist/schema.d.ts +65 -3
- package/dist/src/index.js +153 -82
- package/dist/type-inference.d.ts +34 -64
- package/package.json +1 -1
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
|
@@ -118,13 +118,22 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
|
|
|
118
118
|
whereJsonContainsKey?: (path: string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
119
119
|
whereJsonDoesntContainKey?: (path: string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
120
120
|
whereJsonLength?: (path: string, opOrLen: WhereOperator | number, len?: number) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
121
|
-
with?: (...relations:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
with?: (...relations: WithRelationArg<DB, TTable>[]) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
122
|
+
whereHas?: (relation: TableRelationName<DB, TTable>, callback?: (qb: any) => any) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
123
|
+
whereDoesntHave?: (relation: TableRelationName<DB, TTable>, callback?: (qb: any) => any) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
124
|
+
has?: (relation: TableRelationName<DB, TTable>) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
125
|
+
doesntHave?: (relation: TableRelationName<DB, TTable>) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
126
|
+
withCount?: (...relations: TableRelationName<DB, TTable>[]) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
127
|
+
withSum?: (relation: TableRelationName<DB, TTable>, column: string) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
128
|
+
withAvg?: (relation: TableRelationName<DB, TTable>, column: string) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
129
|
+
withMax?: (relation: TableRelationName<DB, TTable>, column: string) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
130
|
+
withMin?: (relation: TableRelationName<DB, TTable>, column: string) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
131
|
+
withPivot?: (relation: TableRelationName<DB, TTable>, ...columns: string[]) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
132
|
+
wherePivot?: (relation: TableRelationName<DB, TTable>, column: string, opOrValue: any, value?: any) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
133
|
+
wherePivotIn?: (relation: TableRelationName<DB, TTable>, column: string, values: any[]) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
134
|
+
wherePivotNotIn?: (relation: TableRelationName<DB, TTable>, column: string, values: any[]) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
135
|
+
wherePivotNull?: (relation: TableRelationName<DB, TTable>, column: string) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
136
|
+
wherePivotNotNull?: (relation: TableRelationName<DB, TTable>, column: string) => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
128
137
|
applyPivotColumns?: () => SelectQueryBuilder<DB, TTable, TSelected, any>
|
|
129
138
|
lockForUpdate: () => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
130
139
|
sharedLock: () => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
@@ -399,6 +408,34 @@ export type SelectedRow<DB extends DatabaseSchema<any>, _TTable extends keyof DB
|
|
|
399
408
|
declare type JoinColumn<DB extends DatabaseSchema<any>, TTables extends string> = TTables extends any
|
|
400
409
|
? `${TTables}.${keyof DB[TTables]['columns'] & string}`
|
|
401
410
|
: never;
|
|
411
|
+
/**
|
|
412
|
+
* # `TableRelationName<DB, TTable>`
|
|
413
|
+
*
|
|
414
|
+
* The relation names declared for a table, read from the type-level
|
|
415
|
+
* `relations` map that `DatabaseSchema` carries. Falls back to `string`
|
|
416
|
+
* for hand-written schema types that don't declare relation metadata
|
|
417
|
+
* (inferred `R` is `unknown` when the property is absent), so existing
|
|
418
|
+
* untyped schemas keep compiling. A table that declares ZERO relations
|
|
419
|
+
* yields `never` — every relation name is rejected.
|
|
420
|
+
*/
|
|
421
|
+
export type TableRelationName<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = DB[TTable] extends { relations?: infer R }
|
|
422
|
+
? unknown extends R ? string : keyof NonNullable<R> & string
|
|
423
|
+
: string;
|
|
424
|
+
/**
|
|
425
|
+
* # `WithRelationArg<DB, TTable>`
|
|
426
|
+
*
|
|
427
|
+
* Argument accepted by `.with()`: a declared relation name, a dotted nested
|
|
428
|
+
* path rooted at a declared relation (`'posts.comments'`), or a record
|
|
429
|
+
* mapping relation names to constraint callbacks. A table with zero declared
|
|
430
|
+
* relations accepts nothing (the bare `Partial<Record<never, ...>>` would be
|
|
431
|
+
* `{}`, which strings are assignable to — hence the explicit never guard).
|
|
432
|
+
*/
|
|
433
|
+
export type WithRelationArg<DB extends DatabaseSchema<any>, TTable extends keyof DB & string> = [TableRelationName<DB, TTable>] extends [never]
|
|
434
|
+
? never
|
|
435
|
+
:
|
|
436
|
+
| TableRelationName<DB, TTable>
|
|
437
|
+
| `${TableRelationName<DB, TTable>}.${string}`
|
|
438
|
+
| Partial<Record<TableRelationName<DB, TTable>, (qb: any) => any>>;
|
|
402
439
|
// Convert snake_case to PascalCase at the type level (e.g. created_at -> CreatedAt)
|
|
403
440
|
declare type SnakeToPascal<S extends string> = S extends `${infer H}_${infer T}`
|
|
404
441
|
? `${Capitalize<H>}${SnakeToPascal<T>}`
|
package/dist/orm.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Database, type SQLQueryBindings } from 'bun:sqlite';
|
|
2
2
|
import type { Faker } from '@stacksjs/ts-faker';
|
|
3
|
+
import type { RelationCardinality } from './type-inference';
|
|
3
4
|
import type { SupportedDialect } from './types';
|
|
4
5
|
export type {
|
|
5
6
|
ModelInstance,
|
|
@@ -195,12 +196,17 @@ declare type AttributeKeys<TDef extends ModelDefinition> = keyof TDef['attribute
|
|
|
195
196
|
declare type InferAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
|
|
196
197
|
TAttr extends { factory: (faker: Faker) => infer R } ? R :
|
|
197
198
|
unknown;
|
|
198
|
-
// 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.
|
|
199
201
|
declare type InferModelAttributes<TDef extends ModelDefinition> = {
|
|
200
|
-
[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]>
|
|
201
205
|
}
|
|
202
|
-
// System fields added by traits
|
|
203
|
-
|
|
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 } &
|
|
204
210
|
(TDef['traits'] extends { useUuid: true } ? { uuid: string } : {}) &
|
|
205
211
|
(TDef['traits'] extends { useTimestamps: true } ? { created_at: string; updated_at: string | null } : {}) &
|
|
206
212
|
(TDef['traits'] extends { timestampable: true | object } ? { created_at: string; updated_at: string | null } : {}) &
|
|
@@ -212,7 +218,7 @@ declare type SystemFields<TDef extends ModelDefinition> = { id: number } &
|
|
|
212
218
|
declare type ModelAttributes<TDef extends ModelDefinition> = InferModelAttributes<TDef> & SystemFields<TDef>;
|
|
213
219
|
// All valid column names
|
|
214
220
|
declare type ColumnName<TDef extends ModelDefinition> = | AttributeKeys<TDef>
|
|
215
|
-
| 'id'
|
|
221
|
+
| (TDef extends { primaryKey: infer PK extends string } ? PK : 'id')
|
|
216
222
|
| (TDef['traits'] extends { useUuid: true } ? 'uuid' : never)
|
|
217
223
|
| (TDef['traits'] extends { useTimestamps: true } ? 'created_at' | 'updated_at' : never)
|
|
218
224
|
| (TDef['traits'] extends { timestampable: true | object } ? 'created_at' | 'updated_at' : never)
|
|
@@ -232,37 +238,44 @@ declare type FillableKeys<TDef extends ModelDefinition> = {
|
|
|
232
238
|
declare type NumericColumns<TDef extends ModelDefinition> = {
|
|
233
239
|
[K in AttributeKeys<TDef>]: TDef['attributes'][K] extends { type: 'number' } ? K : never
|
|
234
240
|
}[AttributeKeys<TDef>];
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
?
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
declare type
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
? K extends string ? K : never : never);
|
|
256
|
-
declare type InferHasManyThroughNames<TDef> = (TDef extends { hasManyThrough: readonly (infer R)[] }
|
|
257
|
-
? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never)
|
|
258
|
-
| (TDef extends { hasManyThrough: Readonly<Record<infer K, unknown>> }
|
|
259
|
-
? 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;
|
|
260
261
|
export type InferRelationNames<TDef> = | InferBelongsToNames<TDef>
|
|
261
262
|
| InferHasManyNames<TDef>
|
|
262
263
|
| InferHasOneNames<TDef>
|
|
263
264
|
| InferBelongsToManyNames<TDef>
|
|
264
265
|
| InferHasOneThroughNames<TDef>
|
|
265
266
|
| InferHasManyThroughNames<TDef>;
|
|
267
|
+
/**
|
|
268
|
+
* Cardinality-aware value of a loaded relation as returned by
|
|
269
|
+
* `ModelInstance.getRelation()`. Relations declared as to-many (hasMany,
|
|
270
|
+
* belongsToMany, hasManyThrough) yield arrays; to-one relations (hasOne,
|
|
271
|
+
* belongsTo, hasOneThrough) yield a single instance or null. `undefined`
|
|
272
|
+
* means the relation was not eager-loaded.
|
|
273
|
+
*/
|
|
274
|
+
declare type LoadedRelationValue<TDef, R extends string> = 'one' extends RelationCardinality<TDef, R>
|
|
275
|
+
? ModelInstance<any, any> | null | undefined
|
|
276
|
+
: 'many' extends RelationCardinality<TDef, R>
|
|
277
|
+
? ModelInstance<any, any>[] | undefined
|
|
278
|
+
: ModelInstance<any, any>[] | ModelInstance<any, any> | null | undefined;
|
|
266
279
|
declare type WhereOperator = '=' | '!=' | '<' | '>' | '<=' | '>=' | 'like' | 'not like' | 'in' | 'not in';
|
|
267
280
|
// --- Dialect-aware execution layer -------------------------------------------
|
|
268
281
|
//
|
|
@@ -306,7 +319,7 @@ declare class ModelInstance<TDef extends ModelDefinition, TSelected extends Colu
|
|
|
306
319
|
set<K extends ColumnName<TDef>>(key: K, value: K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown): void;
|
|
307
320
|
only<K extends TSelected>(keys: ReadonlyArray<K>): Partial<ModelAttributes<TDef>>;
|
|
308
321
|
except<K extends TSelected>(keys: ReadonlyArray<K>): Partial<ModelAttributes<TDef>>;
|
|
309
|
-
getRelation(name:
|
|
322
|
+
getRelation<R extends InferRelationNames<TDef> & string>(name: R): LoadedRelationValue<TDef, R>;
|
|
310
323
|
setRelation(name: string, data: ModelInstance<any, any>[] | ModelInstance<any, any> | null): void;
|
|
311
324
|
getLoadedRelations(): Record<string, ModelInstance<any, any>[] | ModelInstance<any, any> | null>;
|
|
312
325
|
get attributes(): Pick<ModelAttributes<TDef>, TSelected & keyof ModelAttributes<TDef>>;
|
package/dist/schema.d.ts
CHANGED
|
@@ -258,6 +258,61 @@ export type InferTableName<M extends ModelDefinition> = M extends {
|
|
|
258
258
|
: M extends { name: infer N extends string }
|
|
259
259
|
? `${Lowercase<N>}s`
|
|
260
260
|
: string;
|
|
261
|
+
/**
|
|
262
|
+
* Resolve a models-record entry to the raw definition. `defineModel()` (and
|
|
263
|
+
* `createModel`/`createBrowserModel`) wrap definitions in an object exposing
|
|
264
|
+
* `getDefinition()` / `definition`; `buildDatabaseSchema` unwraps these at
|
|
265
|
+
* runtime, so the type level must unwrap them too or the schema degrades to
|
|
266
|
+
* an untyped index signature.
|
|
267
|
+
*/
|
|
268
|
+
declare type UnwrapModelDefinition<M> = M extends { getDefinition: () => infer D } ? D :
|
|
269
|
+
M extends { definition: infer D } ? D :
|
|
270
|
+
M;
|
|
271
|
+
/**
|
|
272
|
+
* Unwrap a relation entry to the related model's name. Entries may be a plain
|
|
273
|
+
* model-name string, a `{ model: 'X' }` config (belongsToMany Option A/B), or
|
|
274
|
+
* a `{ through, target }` through-relation descriptor.
|
|
275
|
+
*/
|
|
276
|
+
declare type RelationEntryModelName<E> = E extends string ? E :
|
|
277
|
+
E extends { model: infer M extends string } ? M :
|
|
278
|
+
E extends { target: infer T extends string } ? T :
|
|
279
|
+
never;
|
|
280
|
+
/**
|
|
281
|
+
* Normalize one relation declaration (array or record form) into a
|
|
282
|
+
* `relationName -> relatedModelName` record, mirroring `buildSchemaMeta`:
|
|
283
|
+
* array entries use the (unwrapped) model name as the relation name; record
|
|
284
|
+
* entries use the key.
|
|
285
|
+
*/
|
|
286
|
+
declare type RelationRecordOf<V> = [V] extends [never]
|
|
287
|
+
? {} // absent relation kind — must yield {} (not never) so intersections survive
|
|
288
|
+
: V extends readonly (infer E)[]
|
|
289
|
+
? { [K in RelationEntryModelName<E> & string]: K }
|
|
290
|
+
: V extends Readonly<Record<string, unknown>>
|
|
291
|
+
? { [K in keyof V & string]: RelationEntryModelName<V[K]> }
|
|
292
|
+
: {}
|
|
293
|
+
/** All relations of a model as a `relationName -> relatedModelName` record. */
|
|
294
|
+
declare type ModelRelationsRecord<M> = RelationRecordOf<M extends { hasOne: infer V } ? V : never>
|
|
295
|
+
& RelationRecordOf<M extends { hasMany: infer V } ? V : never>
|
|
296
|
+
& RelationRecordOf<M extends { belongsTo: infer V } ? V : never>
|
|
297
|
+
& RelationRecordOf<M extends { belongsToMany: infer V } ? V : never>
|
|
298
|
+
& RelationRecordOf<M extends { hasOneThrough: infer V } ? V : never>
|
|
299
|
+
& RelationRecordOf<M extends { hasManyThrough: infer V } ? V : never>
|
|
300
|
+
& RelationRecordOf<M extends { morphOne: infer V } ? V : never>
|
|
301
|
+
& RelationRecordOf<M extends { morphMany: infer V } ? V : never>
|
|
302
|
+
& RelationRecordOf<M extends { morphToMany: infer V } ? V : never>
|
|
303
|
+
& RelationRecordOf<M extends { morphedByMany: infer V } ? V : never>;
|
|
304
|
+
/** Resolve a related model name to its table name within the models record. */
|
|
305
|
+
declare type RelatedTableName<MRecord extends ModelRecord, ModelName> = ModelName extends keyof MRecord ? InferTableName<UnwrapModelDefinition<MRecord[ModelName]>> : string;
|
|
306
|
+
/**
|
|
307
|
+
* # `InferTableRelations<M, MRecord>`
|
|
308
|
+
*
|
|
309
|
+
* `relationName -> relatedTableName` record for one model, resolved against
|
|
310
|
+
* the full models record. Powers the type-level narrowing of `.with()`,
|
|
311
|
+
* `.whereHas()`, `.withCount()`, etc. on the query builder.
|
|
312
|
+
*/
|
|
313
|
+
export type InferTableRelations<M, MRecord extends ModelRecord> = {
|
|
314
|
+
[K in keyof ModelRelationsRecord<UnwrapModelDefinition<M>> & string]: RelatedTableName<MRecord, ModelRelationsRecord<UnwrapModelDefinition<M>>[K]>
|
|
315
|
+
}
|
|
261
316
|
/**
|
|
262
317
|
* # `DatabaseSchema<Models>`
|
|
263
318
|
*
|
|
@@ -265,6 +320,11 @@ export type InferTableName<M extends ModelDefinition> = M extends {
|
|
|
265
320
|
* table columns and primary key. This is the primary input for the query
|
|
266
321
|
* builder's type-safety.
|
|
267
322
|
*
|
|
323
|
+
* The `relations` field is type-level only (phantom): `buildDatabaseSchema`
|
|
324
|
+
* never materializes it at runtime. It maps relation names to related table
|
|
325
|
+
* names so builder methods like `.with()` can narrow their accepted relation
|
|
326
|
+
* names per table.
|
|
327
|
+
*
|
|
268
328
|
* @example
|
|
269
329
|
* ```ts
|
|
270
330
|
* const models = defineModels({ User, Post })
|
|
@@ -272,8 +332,10 @@ export type InferTableName<M extends ModelDefinition> = M extends {
|
|
|
272
332
|
* ```
|
|
273
333
|
*/
|
|
274
334
|
export type DatabaseSchema<MRecord extends ModelRecord> = {
|
|
275
|
-
[MName in keyof MRecord & string as InferTableName<MRecord[MName]
|
|
276
|
-
columns: InferAttributes<MRecord[MName]
|
|
277
|
-
primaryKey: InferPrimaryKey<MRecord[MName]
|
|
335
|
+
[MName in keyof MRecord & string as InferTableName<UnwrapModelDefinition<MRecord[MName]>>]: {
|
|
336
|
+
columns: InferAttributes<UnwrapModelDefinition<MRecord[MName]>>
|
|
337
|
+
primaryKey: InferPrimaryKey<UnwrapModelDefinition<MRecord[MName]>>
|
|
338
|
+
/** Phantom, type-level only: relation name -> related table name. */
|
|
339
|
+
relations?: InferTableRelations<MRecord[MName], MRecord>
|
|
278
340
|
};
|
|
279
341
|
}
|