bun-query-builder 0.2.28 → 0.2.30
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 +564 -371
- package/dist/browser.d.ts +2 -2
- package/dist/client.d.ts +7 -0
- package/dist/db.d.ts +29 -4
- package/dist/faker-compat.d.ts +83 -0
- package/dist/index.d.ts +1 -0
- package/dist/orm.d.ts +3 -3
- package/dist/schema.d.ts +2 -2
- package/dist/sql-fragments.d.ts +69 -0
- package/dist/src/index.js +564 -370
- package/dist/type-inference.d.ts +3 -3
- 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>
|
package/dist/db.d.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import { Database } from 'bun:sqlite';
|
|
2
2
|
import { SQL } from 'bun';
|
|
3
3
|
import type { DatabaseConfig, PoolConfig, SupportedDialect } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* SQLite wrapper that provides a SQL-like tagged template literal interface
|
|
6
|
+
* using bun:sqlite's Database class for better compiled binary support.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Give `$1`-style placeholders their Postgres meaning on SQLite: `$n` is the
|
|
10
|
+
* n-th value, wherever it appears and however often.
|
|
11
|
+
*
|
|
12
|
+
* bun:sqlite treats `$1` as a NAME, not an index. Handed an array it binds the
|
|
13
|
+
* values to those names in ORDER OF FIRST APPEARANCE and ignores the numbers
|
|
14
|
+
* entirely, so a query whose placeholders are not already in ascending order
|
|
15
|
+
* binds the wrong values and says nothing:
|
|
16
|
+
*
|
|
17
|
+
* sql.unsafe('SELECT $2 AS a, $1 AS b', ['ONE', 'TWO'])
|
|
18
|
+
* -> { a: 'ONE', b: 'TWO' } // both backwards, no error
|
|
19
|
+
*
|
|
20
|
+
* Every caller writing `$n` means the Postgres semantics, because the same
|
|
21
|
+
* query text is expected to run on both drivers. The failure is silent, and it
|
|
22
|
+
* lands hardest in exactly the queries that repeat a parameter - authorisation
|
|
23
|
+
* checks of the shape "owner_id = $2 OR EXISTS (... user_id = $2)" - where the
|
|
24
|
+
* result is a permission answer computed from the wrong values.
|
|
25
|
+
*
|
|
26
|
+
* Rewritten to ordinal `?` here, once, at the only place both `query` and `run`
|
|
27
|
+
* pass through. Placeholders inside string literals, quoted identifiers and
|
|
28
|
+
* comments are left alone: `'$1'` is data, not a parameter. SQL with no
|
|
29
|
+
* numbered placeholders is returned untouched, so `?` queries and Bun's own
|
|
30
|
+
* tagged-template output are unaffected.
|
|
31
|
+
*/
|
|
32
|
+
export declare function bindNumberedPlaceholders(sql: string, params: any[]): { sql: string, params: any[] };
|
|
4
33
|
export declare function splitSqlStatements(sql: string): string[];
|
|
5
34
|
/**
|
|
6
35
|
* Creates a database connection string based on the configured dialect and database settings.
|
|
@@ -98,10 +127,6 @@ export declare interface DriverConnection {
|
|
|
98
127
|
}
|
|
99
128
|
/** An `unsafe(...)` result: a query object that is ALSO directly awaitable. */
|
|
100
129
|
export type AwaitableDriverQuery = DriverQuery & PromiseLike<any>;
|
|
101
|
-
/**
|
|
102
|
-
* SQLite wrapper that provides a SQL-like tagged template literal interface
|
|
103
|
-
* using bun:sqlite's Database class for better compiled binary support.
|
|
104
|
-
*/
|
|
105
130
|
declare class SQLiteWrapper {
|
|
106
131
|
constructor(filename: string);
|
|
107
132
|
query(sql: string, params?: any[]): any[];
|
|
@@ -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
|
|
@@ -237,7 +237,7 @@ declare type InferType<T> = T extends keyof PrimitiveTypeMap ? PrimitiveTypeMap[
|
|
|
237
237
|
declare type AttributeKeys<TDef extends ModelDefinition> = keyof TDef['attributes'] & string;
|
|
238
238
|
// Infer single attribute type
|
|
239
239
|
declare type InferAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
|
|
240
|
-
TAttr extends { factory: (faker:
|
|
240
|
+
TAttr extends { factory: (faker: FactoryFaker) => infer R }
|
|
241
241
|
? [Exclude<R, null | undefined>] extends [never]
|
|
242
242
|
? TAttr extends { validation: { rule: infer V } } ? InferType<V> | Extract<R, null | undefined> : R
|
|
243
243
|
: R
|
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
|
+
}
|