bun-query-builder 0.2.27 → 0.2.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 +461 -355
- package/dist/browser.d.ts +2 -2
- package/dist/client.d.ts +7 -0
- package/dist/faker-compat.d.ts +83 -0
- package/dist/index.d.ts +1 -0
- package/dist/orm.d.ts +26 -14
- package/dist/schema.d.ts +2 -2
- package/dist/sql-fragments.d.ts +69 -0
- package/dist/src/index.js +461 -354
- package/dist/type-inference.d.ts +8 -6
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BrowserConfig } from './types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FactoryFaker } from './faker-compat';
|
|
3
3
|
// Export types for external use
|
|
4
4
|
export type { BrowserModelInstance, BrowserModelQueryBuilder };
|
|
5
5
|
/**
|
|
@@ -115,7 +115,7 @@ export declare interface BrowserTypedAttribute<T = unknown> {
|
|
|
115
115
|
rule: unknown
|
|
116
116
|
message?: Record<string, string>
|
|
117
117
|
}
|
|
118
|
-
factory?: (faker:
|
|
118
|
+
factory?: (faker: FactoryFaker) => InferType<T>
|
|
119
119
|
}
|
|
120
120
|
// Base model definition for browser
|
|
121
121
|
export declare interface BrowserModelDefinition {
|
package/dist/client.d.ts
CHANGED
|
@@ -86,6 +86,13 @@ export declare interface BaseSelectQueryBuilder<DB extends DatabaseSchema<any>,
|
|
|
86
86
|
whereNotILike?: (column: keyof DB[TTable]['columns'] & string, pattern: string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
87
87
|
orWhereNotLike: (column: keyof DB[TTable]['columns'] & string, pattern: string, caseSensitive?: boolean) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
88
88
|
orWhereNotILike?: (column: keyof DB[TTable]['columns'] & string, pattern: string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
89
|
+
whereGroup: (callback: (builder: SelectQueryBuilder<DB, TTable, TSelected, TJoined>) => unknown) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
90
|
+
orWhereGroup: (callback: (builder: SelectQueryBuilder<DB, TTable, TSelected, TJoined>) => unknown) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
91
|
+
orWhereNull: (column: keyof DB[TTable]['columns'] & string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
92
|
+
orWhereNotNull: (column: keyof DB[TTable]['columns'] & string) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
93
|
+
orWhereBetween: (column: keyof DB[TTable]['columns'] & string, start: any, end: any) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
94
|
+
orWhereExists: (subquery: { toSQL: () => any }) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
95
|
+
orWhereRaw: (fragment: SqlFragment) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
89
96
|
whereAny: <K extends keyof DB[TTable]['columns'] & string>(columns: K[], op: WhereOperator, value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
90
97
|
whereAll: <K extends keyof DB[TTable]['columns'] & string>(columns: K[], op: WhereOperator, value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
91
98
|
whereNone: <K extends keyof DB[TTable]['columns'] & string>(columns: K[], op: WhereOperator, value: DB[TTable]['columns'][K]) => SelectQueryBuilder<DB, TTable, TSelected, TJoined>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap a ts-faker instance in the surface factories are written against.
|
|
3
|
+
*
|
|
4
|
+
* Returned as `FactoryFaker` rather than as the underlying type, because the
|
|
5
|
+
* point of the wrapper is that it is a different shape - a caller typed against
|
|
6
|
+
* ts-faker would be told `helpers.arrayElement` does not exist, which is the
|
|
7
|
+
* error this module exists to remove.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createFakerCompatLayer(underlying: Record<string, any>): FactoryFaker;
|
|
10
|
+
/**
|
|
11
|
+
* The faker a factory is actually handed.
|
|
12
|
+
*
|
|
13
|
+
* Model factories in this ecosystem are written in the faker-js dialect - it is
|
|
14
|
+
* what every example, every scaffolded model and a decade of muscle memory
|
|
15
|
+
* produce:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* factory: faker => faker.helpers.arrayElement(['draft', 'published']),
|
|
19
|
+
* factory: faker => faker.string.alphanumeric(12),
|
|
20
|
+
* factory: faker => faker.datatype.number({ min: 1, max: 10 }),
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* `@stacksjs/ts-faker` is a different, smaller surface: no `helpers.arrayElement`,
|
|
24
|
+
* no `datatype`, `address` rather than `location`, `catchphrase` rather than
|
|
25
|
+
* `catchPhrase`, and options objects where faker-js takes a bare number. Handing
|
|
26
|
+
* a factory the raw instance means `arrayElement is not a function` at seed
|
|
27
|
+
* time - and it is a *runtime* failure in the one code path nobody runs until
|
|
28
|
+
* they are setting up a new environment.
|
|
29
|
+
*
|
|
30
|
+
* So this module adapts one to the other, and exports the adapted shape as a
|
|
31
|
+
* type. Both halves matter: without the proxy the calls throw, and without the
|
|
32
|
+
* type every one of those factories is a type error in a codebase that
|
|
33
|
+
* typechecks.
|
|
34
|
+
*
|
|
35
|
+
* **Everything here is a translation, never an invention.** Where ts-faker has
|
|
36
|
+
* no equivalent at all the method is built from what it does have (a slug from
|
|
37
|
+
* `lorem.slug`, a registration from `vehicle.registration`), because a factory
|
|
38
|
+
* that returns a plausible wrong-shaped string is better than a seeder that
|
|
39
|
+
* stops.
|
|
40
|
+
*/
|
|
41
|
+
/** What faker-js calls a string generator's options, and what ts-faker wants. */
|
|
42
|
+
export declare interface CompatStringOptions {
|
|
43
|
+
length?: number
|
|
44
|
+
casing?: 'upper' | 'lower' | 'mixed'
|
|
45
|
+
}
|
|
46
|
+
/** What faker-js passes to a number generator. */
|
|
47
|
+
export declare interface CompatNumberOptions {
|
|
48
|
+
min?: number
|
|
49
|
+
max?: number
|
|
50
|
+
fractionDigits?: number
|
|
51
|
+
multipleOf?: number
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The faker-js-shaped surface a factory receives.
|
|
55
|
+
*
|
|
56
|
+
* Deliberately loose on the modules this file does not translate: they are
|
|
57
|
+
* forwarded to ts-faker untouched, and describing them here would be a second
|
|
58
|
+
* copy of ts-faker's own types that goes stale on its next release. What is
|
|
59
|
+
* spelled out is exactly what the compat layer adds or changes.
|
|
60
|
+
*/
|
|
61
|
+
export declare interface FactoryFaker {
|
|
62
|
+
[module: string]: any
|
|
63
|
+
location: Record<string, (...args: any[]) => any>
|
|
64
|
+
datatype: {
|
|
65
|
+
boolean: (options?: number | { probability?: number }) => boolean
|
|
66
|
+
number: (options?: number | CompatNumberOptions) => number
|
|
67
|
+
float: (options?: CompatNumberOptions) => number
|
|
68
|
+
uuid: () => string
|
|
69
|
+
string: (length?: number) => string
|
|
70
|
+
hexadecimal: (options?: number | CompatStringOptions) => string
|
|
71
|
+
array: (length?: number) => unknown[]
|
|
72
|
+
json: () => string
|
|
73
|
+
}
|
|
74
|
+
helpers: {
|
|
75
|
+
arrayElement: <T>(items: readonly T[]) => T
|
|
76
|
+
arrayElements: <T>(items: readonly T[], count?: number) => T[]
|
|
77
|
+
shuffle: <T>(items: readonly T[]) => T[]
|
|
78
|
+
maybe: <T>(produce: () => T, options?: { probability?: number }) => T | undefined
|
|
79
|
+
slugify: (text: string) => string
|
|
80
|
+
replaceSymbols: (pattern: string) => string
|
|
81
|
+
[method: string]: any
|
|
82
|
+
}
|
|
83
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export * from './dynamodb-single-table';
|
|
|
35
35
|
export * from './dynamodb-tooling-adapter';
|
|
36
36
|
export * from './dynamodb/index';
|
|
37
37
|
export * from './factory';
|
|
38
|
+
export * from './faker-compat';
|
|
38
39
|
export * from './inflect';
|
|
39
40
|
export * from './loader';
|
|
40
41
|
export * from './meta';
|
package/dist/orm.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Database, type SQLQueryBindings } from 'bun:sqlite';
|
|
2
|
-
import type {
|
|
2
|
+
import type { FactoryFaker } from './faker-compat';
|
|
3
3
|
import type { RelationCardinality } from './type-inference';
|
|
4
4
|
import type { SupportedDialect } from './types';
|
|
5
5
|
export type {
|
|
@@ -56,7 +56,7 @@ export declare interface TypedAttribute<T = unknown> {
|
|
|
56
56
|
rule: unknown
|
|
57
57
|
message?: Record<string, string>
|
|
58
58
|
}
|
|
59
|
-
factory?: (faker:
|
|
59
|
+
factory?: (faker: FactoryFaker) => InferType<T>
|
|
60
60
|
}
|
|
61
61
|
/** Structural type for model instances passed to lifecycle hooks. */
|
|
62
62
|
// eslint-disable-next-line ts/no-empty-object-type
|
|
@@ -228,14 +228,16 @@ declare type PrimitiveTypeMap = {
|
|
|
228
228
|
declare type InferType<T> = T extends keyof PrimitiveTypeMap ? PrimitiveTypeMap[T] :
|
|
229
229
|
T extends readonly (infer U)[] ? U :
|
|
230
230
|
T extends (infer U)[] ? U :
|
|
231
|
-
T extends {
|
|
232
|
-
|
|
233
|
-
|
|
231
|
+
T extends { getShape: () => infer TShape extends Readonly<Record<string, unknown>> }
|
|
232
|
+
? { -readonly [TKey in keyof TShape]: InferType<TShape[TKey]> }
|
|
233
|
+
: T extends { test: (value: infer U) => unknown } ? U :
|
|
234
|
+
T extends { validate: (value: infer U) => unknown } ? U :
|
|
235
|
+
unknown;
|
|
234
236
|
// Extract attribute keys from definition
|
|
235
237
|
declare type AttributeKeys<TDef extends ModelDefinition> = keyof TDef['attributes'] & string;
|
|
236
238
|
// Infer single attribute type
|
|
237
239
|
declare type InferAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
|
|
238
|
-
TAttr extends { factory: (faker:
|
|
240
|
+
TAttr extends { factory: (faker: FactoryFaker) => infer R }
|
|
239
241
|
? [Exclude<R, null | undefined>] extends [never]
|
|
240
242
|
? TAttr extends { validation: { rule: infer V } } ? InferType<V> | Extract<R, null | undefined> : R
|
|
241
243
|
: R
|
|
@@ -370,25 +372,35 @@ declare type NumericColumns<TDef extends ModelDefinition> = {
|
|
|
370
372
|
* and would otherwise leak its own keys ('length', indices, ...) into the
|
|
371
373
|
* relation-name union.
|
|
372
374
|
*/
|
|
373
|
-
declare type RelationKeyOf<V> = V extends
|
|
374
|
-
?
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
:
|
|
375
|
+
declare type RelationKeyOf<V> = V extends string
|
|
376
|
+
? Lowercase<V>
|
|
377
|
+
: V extends readonly (infer E)[]
|
|
378
|
+
? E extends string ? Lowercase<E>
|
|
379
|
+
: E extends { model: infer M extends string } ? Lowercase<M>
|
|
380
|
+
: never
|
|
381
|
+
: V extends Readonly<Record<infer K, unknown>>
|
|
382
|
+
? K & string
|
|
383
|
+
: never;
|
|
380
384
|
declare type InferBelongsToNames<TDef> = TDef extends { belongsTo: infer V } ? RelationKeyOf<V> : never;
|
|
381
385
|
declare type InferHasManyNames<TDef> = TDef extends { hasMany: infer V } ? RelationKeyOf<V> : never;
|
|
382
386
|
declare type InferHasOneNames<TDef> = TDef extends { hasOne: infer V } ? RelationKeyOf<V> : never;
|
|
383
387
|
declare type InferBelongsToManyNames<TDef> = TDef extends { belongsToMany: infer V } ? RelationKeyOf<V> : never;
|
|
384
388
|
declare type InferHasOneThroughNames<TDef> = TDef extends { hasOneThrough: infer V } ? RelationKeyOf<V> : never;
|
|
385
389
|
declare type InferHasManyThroughNames<TDef> = TDef extends { hasManyThrough: infer V } ? RelationKeyOf<V> : never;
|
|
390
|
+
declare type InferMorphOneNames<TDef> = TDef extends { morphOne: infer V } ? RelationKeyOf<V> : never;
|
|
391
|
+
declare type InferMorphManyNames<TDef> = TDef extends { morphMany: infer V } ? RelationKeyOf<V> : never;
|
|
392
|
+
declare type InferMorphToManyNames<TDef> = TDef extends { morphToMany: infer V } ? RelationKeyOf<V> : never;
|
|
393
|
+
declare type InferMorphedByManyNames<TDef> = TDef extends { morphedByMany: infer V } ? RelationKeyOf<V> : never;
|
|
386
394
|
export type InferRelationNames<TDef> = | InferBelongsToNames<TDef>
|
|
387
395
|
| InferHasManyNames<TDef>
|
|
388
396
|
| InferHasOneNames<TDef>
|
|
389
397
|
| InferBelongsToManyNames<TDef>
|
|
390
398
|
| InferHasOneThroughNames<TDef>
|
|
391
|
-
| InferHasManyThroughNames<TDef
|
|
399
|
+
| InferHasManyThroughNames<TDef>
|
|
400
|
+
| InferMorphOneNames<TDef>
|
|
401
|
+
| InferMorphManyNames<TDef>
|
|
402
|
+
| InferMorphToManyNames<TDef>
|
|
403
|
+
| InferMorphedByManyNames<TDef>;
|
|
392
404
|
/**
|
|
393
405
|
* Cardinality-aware value of a loaded relation as returned by
|
|
394
406
|
* `ModelInstance.getRelation()`. Relations declared as to-many (hasMany,
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FactoryFaker } from './faker-compat';
|
|
2
2
|
/**
|
|
3
3
|
* # `defineModel(model)`
|
|
4
4
|
*
|
|
@@ -62,7 +62,7 @@ export declare interface Attribute {
|
|
|
62
62
|
fillable?: boolean
|
|
63
63
|
guarded?: boolean
|
|
64
64
|
foreignKey?: boolean | ForeignKeyConfig
|
|
65
|
-
factory?: (faker:
|
|
65
|
+
factory?: (faker: FactoryFaker) => unknown
|
|
66
66
|
validation: {
|
|
67
67
|
rule: ValidationType
|
|
68
68
|
message?: ValidatorMessage
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a WHERE body (without the `WHERE` keyword) from an ordered term list.
|
|
3
|
+
*
|
|
4
|
+
* Each MAXIMAL RUN of `OR` terms is bracketed together with the term that opens
|
|
5
|
+
* it, so a chain groups the way it reads:
|
|
6
|
+
*
|
|
7
|
+
* .where(a).where(b).orWhere(c) -> a AND (b OR c)
|
|
8
|
+
* .where(a).orWhere(b).where(c) -> (a OR b) AND c
|
|
9
|
+
*
|
|
10
|
+
* In effect `OR` binds TIGHTER than `AND` in chain order — the inverse of raw
|
|
11
|
+
* SQL. That is a deliberate choice for a fluent builder, and it is already what
|
|
12
|
+
* `whereAny`/`whereAll`/`whereNone` and the ORM's `whereGroup` do; `orWhere`
|
|
13
|
+
* was the odd one out. Previously the terms were concatenated as flat text and
|
|
14
|
+
* SQL's precedence re-associated `a AND b OR c` to `(a AND b) OR c`, so the
|
|
15
|
+
* first filter stopped applying to any row the `OR` matched — silently, and in
|
|
16
|
+
* the direction that returns MORE rows.
|
|
17
|
+
*
|
|
18
|
+
* The brackets are omitted when the body has a single top-level piece, so a
|
|
19
|
+
* plain two-term `.where(a).orWhere(b)` and any all-`AND` chain still render
|
|
20
|
+
* byte-for-byte as before. The escape hatch for the one shape this rule can no
|
|
21
|
+
* longer express directly — `(a AND b) OR c` — is `whereGroup(cb)`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderWhereTerms(terms: readonly WhereTerm[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Render `col IN (…)` / `col NOT IN (…)`, including the empty case.
|
|
26
|
+
*
|
|
27
|
+
* An empty `IN` is FALSE (nothing is a member of the empty set) and an empty
|
|
28
|
+
* `NOT IN` is TRUE (everything is a non-member). Those constants are not
|
|
29
|
+
* interchangeable and the mirror is easy to get backwards: writing FALSE for
|
|
30
|
+
* `NOT IN` would silently drop every row from any query whose exclusion list
|
|
31
|
+
* happened to come back empty — the same shape of silent, widening-or-narrowing
|
|
32
|
+
* failure as the `IN ()` bug itself.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderInPredicate(column: string, values: any[], negated: boolean, placeholders: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Constant predicates, for the cases where a collection is empty.
|
|
37
|
+
*
|
|
38
|
+
* `1 = 0` and `1 = 1` take no parameters and parse identically on SQLite,
|
|
39
|
+
* Postgres and MySQL, which is the point: the alternatives are engine-specific.
|
|
40
|
+
* `IN ()` is the example that motivated these — SQLite parses it and matches
|
|
41
|
+
* nothing, while Postgres and MySQL reject it as a syntax error, so the same
|
|
42
|
+
* call site behaved differently depending on where it ran.
|
|
43
|
+
*/
|
|
44
|
+
export declare const FALSE_PREDICATE: '1 = 0';
|
|
45
|
+
export declare const TRUE_PREDICATE: '1 = 1';
|
|
46
|
+
/**
|
|
47
|
+
* SQL fragment renderers shared by the select builder (`client.ts`) and the ORM
|
|
48
|
+
* query builder (`orm.ts`).
|
|
49
|
+
*
|
|
50
|
+
* They live here rather than in either builder because the two had independent
|
|
51
|
+
* copies of the same defect: both joined their WHERE terms as flat text, so
|
|
52
|
+
* `orWhere` produced an ungrouped `OR` and SQL's native precedence silently
|
|
53
|
+
* re-associated the chain. Two implementations of one rule is how they drifted;
|
|
54
|
+
* one implementation is how they stop.
|
|
55
|
+
*
|
|
56
|
+
* See stacksjs/bun-query-builder#1083.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* A single WHERE predicate plus the connector the CALLER asked for.
|
|
60
|
+
*
|
|
61
|
+
* The connector belongs to the term rather than sitting between terms because
|
|
62
|
+
* the first term's connector is meaningless — nothing precedes it — and
|
|
63
|
+
* carrying it anyway is what lets a leading `.orWhere(x)` render as `WHERE x`
|
|
64
|
+
* instead of the `FROM t OR x` syntax error it used to emit.
|
|
65
|
+
*/
|
|
66
|
+
export declare interface WhereTerm {
|
|
67
|
+
conn: 'AND' | 'OR'
|
|
68
|
+
sql: string
|
|
69
|
+
}
|