@stacksjs/database 0.72.22 → 0.72.23
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/utils.d.ts +183 -111
- package/dist/utils.js +1 -1
- package/package.json +10 -10
package/dist/utils.d.ts
CHANGED
|
@@ -4,9 +4,7 @@ export declare function acquireDbConfigLock(): Promise<() => void>;
|
|
|
4
4
|
// Function to initialize the config when it's available
|
|
5
5
|
export declare function initializeDbConfig(config: any): void;
|
|
6
6
|
export declare function createDatabaseQueryHooks(dispatch: (event: DatabaseQueryLogEvent) => void | Promise<void>): QueryHooks;
|
|
7
|
-
declare function ensureConfigLoaded(): Promise<void>;
|
|
8
7
|
export declare function ensureDatabaseConfigLoaded(): Promise<void>;
|
|
9
|
-
declare function getDb(): ReturnType<typeof createQueryBuilder>;
|
|
10
8
|
/**
|
|
11
9
|
* Discard every cached database client after the underlying query-builder
|
|
12
10
|
* connection is reset.
|
|
@@ -19,24 +17,46 @@ declare function getDb(): ReturnType<typeof createQueryBuilder>;
|
|
|
19
17
|
*/
|
|
20
18
|
export declare function resetDatabaseConnection(): void;
|
|
21
19
|
/**
|
|
22
|
-
*
|
|
20
|
+
* Fluent chain returned by entry-point methods like `selectFrom`/`updateTable`.
|
|
23
21
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* bun-query-builder marks legacy chain methods (e.g. `selectAll`, `whereILike`,
|
|
23
|
+
* `selectAllRelations`) as optional in its declarations even though they're
|
|
24
|
+
* always present at runtime. Re-typing them here avoids forcing every call
|
|
25
|
+
* site to use `?.()` or `!` on the chain.
|
|
26
|
+
*
|
|
27
|
+
* **The chain carries its row type.** `TRow` comes from the augmented
|
|
28
|
+
* `DatabaseSchema` that `buddy generate:db-types` writes, so
|
|
29
|
+
* `db.selectFrom('users').executeTakeFirst()` answers a `users` row rather than
|
|
30
|
+
* `any` - and `select(['id', 'handle'])` narrows it to those two columns.
|
|
31
|
+
*
|
|
32
|
+
* Every terminal used to be `Promise<any>`, and the cost was not theoretical:
|
|
33
|
+
* an application on top of this ends up annotating every result `any` to say
|
|
34
|
+
* what it already knows, which is a thousand places the compiler has been told
|
|
35
|
+
* to stop looking. A row it cannot type is `Record<string, unknown>` instead -
|
|
36
|
+
* still a value the caller has to narrow, but one narrowing it is checked.
|
|
37
|
+
*
|
|
38
|
+
* A join or an aliased select list widens the row to
|
|
39
|
+
* `Record<string, unknown>`: the shape then depends on the aliases rather than
|
|
40
|
+
* on any one table, and claiming otherwise would be worse than not knowing.
|
|
28
41
|
*/
|
|
29
|
-
declare function getReadDb(): ReturnType<typeof createQueryBuilder>;
|
|
30
42
|
/**
|
|
31
|
-
*
|
|
43
|
+
* State the shape of rows this package knows and the query cannot.
|
|
44
|
+
*
|
|
45
|
+
* A raw query answers `Record<string, unknown>` when the table is not in the
|
|
46
|
+
* generated `DatabaseSchema` - which is always true *inside* the framework,
|
|
47
|
+
* because an application's schema does not exist at framework build time. The
|
|
48
|
+
* package that ships the model does know the shape, and this is where that
|
|
49
|
+
* knowledge is written down: once, at the boundary, named and greppable.
|
|
32
50
|
*
|
|
33
|
-
*
|
|
34
|
-
* `
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
51
|
+
* It is an assertion, and deliberately an obvious one. What it replaces is a
|
|
52
|
+
* `Promise<any>` that spread the same claim silently through every caller.
|
|
53
|
+
*
|
|
54
|
+
* Application code should not need it: `buddy generate:db-types` gives `db` the
|
|
55
|
+
* real column types, and a row that arrives typed does not want asserting.
|
|
38
56
|
*/
|
|
39
|
-
declare function
|
|
57
|
+
export declare function asRows<TRow>(rows: ReadonlyArray<Record<string, unknown>>): TRow[];
|
|
58
|
+
/** The single-row form of {@link asRows}. */
|
|
59
|
+
export declare function asRow<TRow>(row: Record<string, unknown> | undefined): TRow | undefined;
|
|
40
60
|
/**
|
|
41
61
|
* Update bun-query-builder configuration
|
|
42
62
|
*/
|
|
@@ -64,15 +84,11 @@ export declare const RAW_QUERY_SOFT_DELETE_CONFIG: {
|
|
|
64
84
|
column: 'deleted_at';
|
|
65
85
|
defaultFilter: true
|
|
66
86
|
};
|
|
67
|
-
/** Statements that mutate, for the read-your-writes tracking in `./replicas`. */
|
|
68
|
-
declare const WRITE_ENTRY_POINTS: Set<any>;
|
|
69
|
-
/** Reads that are candidates for replica routing. */
|
|
70
|
-
declare const READ_ENTRY_POINTS: Set<any>;
|
|
71
87
|
/**
|
|
72
88
|
* Lazy proxy for the query builder - connection is only made when first used.
|
|
73
89
|
* This is the main entry point for database operations.
|
|
74
90
|
*/
|
|
75
|
-
export declare const db:
|
|
91
|
+
export declare const db: Db;
|
|
76
92
|
/**
|
|
77
93
|
* Replica-routed handle exposed as `db.read`.
|
|
78
94
|
*
|
|
@@ -80,7 +96,7 @@ export declare const db: Proxy;
|
|
|
80
96
|
* available behind it (`db.read.selectFrom(...).where(...)`) without
|
|
81
97
|
* re-declaring every chain entry point.
|
|
82
98
|
*/
|
|
83
|
-
export declare const readDb:
|
|
99
|
+
export declare const readDb: Omit<Db, 'read'>;
|
|
84
100
|
export declare interface DatabaseQueryLogEvent {
|
|
85
101
|
query: {
|
|
86
102
|
sql: string
|
|
@@ -89,91 +105,79 @@ export declare interface DatabaseQueryLogEvent {
|
|
|
89
105
|
queryDurationMillis: number
|
|
90
106
|
error?: unknown
|
|
91
107
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
unionAll: (...args: any[]) => FluentChain
|
|
153
|
-
values: (...args: any[]) => FluentChain
|
|
154
|
-
set: (...args: any[]) => FluentChain
|
|
155
|
-
returning: (...args: any[]) => FluentChain
|
|
156
|
-
returningAll: () => FluentChain
|
|
157
|
-
onConflict: (...args: any[]) => FluentChain
|
|
158
|
-
onDuplicateKeyUpdate: (...args: any[]) => FluentChain
|
|
159
|
-
onConflictDoNothing: (...args: any[]) => FluentChain
|
|
160
|
-
onDuplicateKeyIgnore: () => FluentChain
|
|
161
|
-
forUpdate: () => FluentChain
|
|
162
|
-
forShare: () => FluentChain
|
|
108
|
+
export declare interface BaseFluentChain<TRow = Record<string, unknown>, TKind extends ChainKind = 'select'> {
|
|
109
|
+
where(callback: (eb: import('./types').StacksExpressionBuilder) => unknown): FluentChain<TRow, TKind>
|
|
110
|
+
where(...args: unknown[]): FluentChain<TRow, TKind>
|
|
111
|
+
whereNull: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
112
|
+
whereNotNull: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
113
|
+
whereIn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
114
|
+
whereNotIn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
115
|
+
whereLike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
116
|
+
whereNotLike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
117
|
+
whereILike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
118
|
+
whereNotILike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
119
|
+
whereBetween: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
120
|
+
whereNotBetween: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
121
|
+
whereRaw: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
122
|
+
whereColumn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
123
|
+
orWhere: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
124
|
+
orWhereNull: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
125
|
+
orWhereNotNull: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
126
|
+
orWhereIn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
127
|
+
orWhereNotIn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
128
|
+
orWhereLike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
129
|
+
orWhereNotLike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
130
|
+
orWhereILike: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
131
|
+
orWhereColumn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
132
|
+
andWhere: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
133
|
+
whereAny: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
134
|
+
whereAll: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
135
|
+
whereNone: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
136
|
+
having: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
137
|
+
groupBy: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
138
|
+
orderBy: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
139
|
+
limit: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
140
|
+
offset: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
141
|
+
select<K extends KnownKeys<TRow> & string>(columns: readonly K[]): FluentChain<Pick<TRow, K>, TKind>
|
|
142
|
+
select(selection: ((eb: import('./types').StacksExpressionBuilder) => unknown) | ReadonlyArray<string | ((eb: import('./types').StacksExpressionBuilder) => unknown) | unknown>): FluentChain<Record<string, unknown>, TKind>
|
|
143
|
+
select(...args: unknown[]): FluentChain<Record<string, unknown>, TKind>
|
|
144
|
+
selectAll: () => FluentChain<TRow, TKind>
|
|
145
|
+
selectAllRelations: () => FluentChain<Record<string, unknown>, TKind>
|
|
146
|
+
selectRaw: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
147
|
+
distinct: () => FluentChain<TRow, TKind>
|
|
148
|
+
distinctOn: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
149
|
+
innerJoin: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
150
|
+
leftJoin: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
151
|
+
rightJoin: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
152
|
+
fullJoin: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
153
|
+
crossJoin: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
154
|
+
with: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
155
|
+
union: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
156
|
+
unionAll: (...args: unknown[]) => FluentChain<Record<string, unknown>, TKind>
|
|
157
|
+
values: (values: Partial<TRow> | ReadonlyArray<Partial<TRow>> | object | readonly object[]) => FluentChain<TRow, TKind>
|
|
158
|
+
set: (values: Partial<TRow> | object) => FluentChain<TRow, TKind>
|
|
159
|
+
returning<K extends KnownKeys<TRow> & string>(columns: readonly K[]): FluentChain<Pick<TRow, K>, 'returning'>
|
|
160
|
+
returning(...args: unknown[]): FluentChain<Record<string, unknown>, 'returning'>
|
|
161
|
+
returningAll: () => FluentChain<TRow, 'returning'>
|
|
162
|
+
onConflict: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
163
|
+
onDuplicateKeyUpdate: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
164
|
+
onConflictDoNothing: (...args: unknown[]) => FluentChain<TRow, TKind>
|
|
165
|
+
onDuplicateKeyIgnore: () => FluentChain<TRow, TKind>
|
|
166
|
+
forUpdate: () => FluentChain<TRow, TKind>
|
|
167
|
+
forShare: () => FluentChain<TRow, TKind>
|
|
163
168
|
toSQL: () => string
|
|
164
|
-
execute: () => Promise<
|
|
165
|
-
executeTakeFirst: () => Promise<
|
|
166
|
-
executeTakeFirstOrThrow: () => Promise<
|
|
167
|
-
pluck: (...args:
|
|
168
|
-
count: (...args:
|
|
169
|
-
sum: (...args:
|
|
170
|
-
avg: (...args:
|
|
171
|
-
min: (...args:
|
|
172
|
-
max: (...args:
|
|
169
|
+
execute: () => Promise<ResultOf<TRow, TKind>>
|
|
170
|
+
executeTakeFirst: () => Promise<FirstOf<TRow, TKind>>
|
|
171
|
+
executeTakeFirstOrThrow: () => Promise<NonNullable<FirstOf<TRow, TKind>>>
|
|
172
|
+
pluck: (...args: unknown[]) => Promise<unknown[]>
|
|
173
|
+
count: (...args: unknown[]) => Promise<number>
|
|
174
|
+
sum: (...args: unknown[]) => Promise<number>
|
|
175
|
+
avg: (...args: unknown[]) => Promise<number>
|
|
176
|
+
min: (...args: unknown[]) => Promise<unknown>
|
|
177
|
+
max: (...args: unknown[]) => Promise<unknown>
|
|
173
178
|
exists: () => Promise<boolean>
|
|
174
179
|
doesntExist: () => Promise<boolean>
|
|
175
|
-
$call: (callback: (query: FluentChain) => FluentChain) => FluentChain
|
|
176
|
-
[key: string]: any
|
|
180
|
+
$call: (callback: (query: FluentChain<TRow, TKind>) => FluentChain<TRow, TKind>) => FluentChain<TRow, TKind>
|
|
177
181
|
}
|
|
178
182
|
/*.ts` and
|
|
179
183
|
* emits `database/types.d.ts` containing:
|
|
@@ -197,20 +201,75 @@ export declare interface FluentChain {
|
|
|
197
201
|
export declare interface DatabaseSchema {}
|
|
198
202
|
declare interface Db extends Pick<Required<RawQueryBuilder>, GenericPassthroughKeys> {
|
|
199
203
|
fn: import('./types').ExpressionFunctions
|
|
200
|
-
selectFrom: (table:
|
|
201
|
-
insertInto: (table:
|
|
202
|
-
updateTable: (table:
|
|
203
|
-
deleteFrom: (table:
|
|
204
|
-
table: (table:
|
|
205
|
-
selectFromSub: (sub:
|
|
206
|
-
select: (table:
|
|
207
|
-
unsafe: (query: string, params?:
|
|
204
|
+
selectFrom: <T extends TableName>(table: T) => FluentChain<RowOf<T>, 'select'>
|
|
205
|
+
insertInto: <T extends TableName>(table: T) => FluentChain<RowOf<T>, 'insert'>
|
|
206
|
+
updateTable: <T extends TableName>(table: T) => FluentChain<RowOf<T>, 'update'>
|
|
207
|
+
deleteFrom: <T extends TableName>(table: T) => FluentChain<RowOf<T>, 'delete'>
|
|
208
|
+
table: <T extends TableName>(table: T) => FluentChain<RowOf<T>, 'select'>
|
|
209
|
+
selectFromSub: (sub: unknown, alias: string) => FluentChain<Record<string, unknown>>
|
|
210
|
+
select: <T extends TableName>(table: T, ...columns: string[]) => FluentChain<Record<string, unknown>>
|
|
211
|
+
unsafe: (query: string, params?: unknown[]) => UnsafeReturn
|
|
208
212
|
read: Omit<Db, 'read'>
|
|
209
213
|
}
|
|
210
214
|
// The bun-query-builder types `unsafe()` as returning `Promise<any>`, but at
|
|
211
215
|
// runtime it returns a Bun SQL Statement that has `.execute()`. This interface
|
|
212
216
|
// corrects the return type so callers can chain `.execute()` without type errors.
|
|
213
217
|
declare type UnsafeReturn = Promise<any> & { execute: () => Promise<any> }
|
|
218
|
+
/**
|
|
219
|
+
* The keys a row type actually declares, or `never` for a loose record.
|
|
220
|
+
*
|
|
221
|
+
* `keyof Record<string, unknown>` is `string`, so a narrowing overload written
|
|
222
|
+
* against it will happily accept *anything* as a column - including
|
|
223
|
+
* `'menu_items.id as id'`, which then becomes a property name in the result
|
|
224
|
+
* type. That is worse than not narrowing: the row type looks specific and every
|
|
225
|
+
* key in it is fiction.
|
|
226
|
+
*/
|
|
227
|
+
export type KnownKeys<T> = string extends keyof T ? never : keyof T;
|
|
228
|
+
/**
|
|
229
|
+
* Which verb started a chain, so its terminals can answer the right thing.
|
|
230
|
+
*
|
|
231
|
+
* `returning` is its own kind rather than a flag on the others: a mutation with
|
|
232
|
+
* `RETURNING` answers rows, and that is the difference between reading
|
|
233
|
+
* `rows[0].id` and reading a count.
|
|
234
|
+
*/
|
|
235
|
+
export type ChainKind = 'select' | 'insert' | 'update' | 'delete' | 'returning';
|
|
236
|
+
/** What `execute()` resolves to for each verb. */
|
|
237
|
+
export type ResultOf<TRow, TKind extends ChainKind> = TKind extends 'select' | 'returning'
|
|
238
|
+
? TRow[]
|
|
239
|
+
: number;
|
|
240
|
+
/** What `executeTakeFirst()` resolves to for each verb. */
|
|
241
|
+
export type FirstOf<TRow, TKind extends ChainKind> = TKind extends 'select' | 'returning' | 'insert'
|
|
242
|
+
? TRow | undefined
|
|
243
|
+
: TKind extends 'update'
|
|
244
|
+
? { numUpdatedRows?: number }
|
|
245
|
+
: { numDeletedRows?: number }
|
|
246
|
+
/** `created_at` -> `CreatedAt`, for the dynamic helper names below. */
|
|
247
|
+
declare type SnakeToPascal<S extends string> = S extends `${infer Head}_${infer Tail}`
|
|
248
|
+
? `${Capitalize<Head>}${SnakeToPascal<Tail>}`
|
|
249
|
+
: Capitalize<S>;
|
|
250
|
+
/**
|
|
251
|
+
* The dynamic `where<Column>` helpers bun-query-builder generates.
|
|
252
|
+
*
|
|
253
|
+
* Derived from the row type rather than allowed by an index signature. The
|
|
254
|
+
* index signature that used to be here (`[key: string]: any`) made every
|
|
255
|
+
* misspelling legal and every result `any`: `whereHndle('a')` compiled, and so
|
|
256
|
+
* did reading a property that does not exist.
|
|
257
|
+
*/
|
|
258
|
+
export type DynamicWhereMethods<TRow, TKind extends ChainKind = 'select'> = {
|
|
259
|
+
[K in keyof TRow & string as `where${SnakeToPascal<K>}`]: (value: TRow[K]) => FluentChain<TRow, TKind>
|
|
260
|
+
} & {
|
|
261
|
+
[K in keyof TRow & string as `orWhere${SnakeToPascal<K>}`]: (value: TRow[K]) => FluentChain<TRow, TKind>
|
|
262
|
+
} & {
|
|
263
|
+
[K in keyof TRow & string as `andWhere${SnakeToPascal<K>}`]: (value: TRow[K]) => FluentChain<TRow, TKind>
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* A chain over one table's rows: the methods, plus the generated helpers.
|
|
267
|
+
*
|
|
268
|
+
* A type alias rather than an interface because the helper half is a mapped
|
|
269
|
+
* type over `TRow`, and an interface can only extend members TypeScript knows
|
|
270
|
+
* statically.
|
|
271
|
+
*/
|
|
272
|
+
export type FluentChain<TRow = Record<string, unknown>, TKind extends ChainKind = 'select'> = BaseFluentChain<TRow, TKind> & DynamicWhereMethods<TRow, TKind>;
|
|
214
273
|
/**
|
|
215
274
|
* Top-level surface of the lazy `db` proxy. Methods that return a chainable
|
|
216
275
|
* builder are typed as `FluentChain` to flatten the optional-method noise
|
|
@@ -262,6 +321,19 @@ declare type GenericPassthroughKeys = | 'transaction'
|
|
|
262
321
|
*/
|
|
263
322
|
// eslint-disable-next-line ts/no-empty-object-type
|
|
264
323
|
export type TableName = (keyof DatabaseSchema & string) | (string & {});
|
|
324
|
+
/**
|
|
325
|
+
* The row type of a registered table, or an unknown-valued record.
|
|
326
|
+
*
|
|
327
|
+
* A table the generated `DatabaseSchema` knows answers its own columns. One it
|
|
328
|
+
* does not - an app that has never run `buddy generate:db-types`, or a table
|
|
329
|
+
* that lives outside a model - answers `Record<string, unknown>`: still a value
|
|
330
|
+
* the caller narrows, but narrowing it is checked rather than waved through.
|
|
331
|
+
*/
|
|
332
|
+
export type RowOf<T extends TableName> = T extends keyof DatabaseSchema
|
|
333
|
+
? DatabaseSchema[T] extends { columns: infer C }
|
|
334
|
+
? C
|
|
335
|
+
: DatabaseSchema[T]
|
|
336
|
+
: Record<string, unknown>;
|
|
265
337
|
// SQLite bootstrap pragmas (stacksjs/stacks#1951) now live in
|
|
266
338
|
// @stacksjs/query-builder — the one chokepoint every framework
|
|
267
339
|
// query-builder instance is created through — so EVERY fresh sqlite
|
package/dist/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AsyncLocalStorage}from"node:async_hooks";import{createQueryBuilder,registerPersistentQueryHooks,resetConnection as resetQueryBuilderConnection,setConfig}from"@stacksjs/query-builder";import{SQL}from"bun";import{env as envVars}from"@stacksjs/env";import{getConnectionDefaults}from"./defaults";import{isMysqlWire,isVitessSharded,toQueryBuilderDialect}from"./dialect";import{relativeMigrationDirectory,resolveMigrationDirectory}from"./migration-path";import{contextInTransaction,markContextWrote,resolveReplicaConnection,selectReplica,shouldRouteToReplica,withTransactionContext}from"./replicas";import{aggregateFunctions}from"./types";const sqliteDefaults=getConnectionDefaults("sqlite",envVars),mysqlDefaults=getConnectionDefaults("mysql",envVars),postgresDefaults=getConnectionDefaults("postgres",envVars);let appEnv=envVars.APP_ENV||"local",dbDriver=envVars.DB_CONNECTION||"sqlite",dbConfig={connections:{sqlite:{database:sqliteDefaults.database,prefix:""},mysql:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},singlestore:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},vitess:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:15306,prefix:"",sharded:isVitessSharded()},postgres:{name:postgresDefaults.database,host:postgresDefaults.host,username:postgresDefaults.username,password:postgresDefaults.password,port:postgresDefaults.port,prefix:""}}},dbConfigLockTail=Promise.resolve();export function acquireDbConfigLock(){let release=()=>{};const held=new Promise((resolve)=>{release=resolve}),acquired=dbConfigLockTail.then(()=>release);dbConfigLockTail=dbConfigLockTail.then(()=>held);return acquired}export function initializeDbConfig(config){if(config?.app?.env)appEnv=config.app.env;if(config?.database?.default)dbDriver=config.database.default;if(config?.database)dbConfig=config.database;updateQueryBuilderConfig();_dbInstance=null;_replicaInstances=new Map}function getEnv(){return appEnv}function getDriver(){return dbDriver}function getDatabaseConfig(){return dbConfig}function getDialect(){return toQueryBuilderDialect(getDriver())}function getDbConfig(){const driver=getDriver(),database=getDatabaseConfig(),env=getEnv();if(driver==="sqlite"){const defaultName=env!=="testing"?"database/stacks.sqlite":"database/stacks_testing.sqlite";return{database:database.connections?.sqlite?.database??defaultName}}if(driver==="mysql")return{database:database.connections?.mysql?.name||"stacks",host:database.connections?.mysql?.host??"127.0.0.1",username:database.connections?.mysql?.username??"root",password:database.connections?.mysql?.password??"",port:database.connections?.mysql?.port??3306};if(driver==="singlestore")return{database:database.connections?.singlestore?.name||"stacks",host:database.connections?.singlestore?.host??"127.0.0.1",username:database.connections?.singlestore?.username??"root",password:database.connections?.singlestore?.password??"",port:database.connections?.singlestore?.port??3306};if(driver==="vitess")return{database:database.connections?.vitess?.name||"stacks",host:database.connections?.vitess?.host??"127.0.0.1",username:database.connections?.vitess?.username??"root",password:database.connections?.vitess?.password??"",port:database.connections?.vitess?.port??15306};if(driver==="postgres"){const dbName=database.connections?.postgres?.name??"stacks";return{database:env==="testing"?`${dbName}_testing`:dbName,host:database.connections?.postgres?.host??"127.0.0.1",username:database.connections?.postgres?.username??"",password:database.connections?.postgres?.password??"",port:database.connections?.postgres?.port??5432}}return{database:":memory:"}}export const QB_SNAPSHOT_DIR="storage/framework/database",RAW_QUERY_SOFT_DELETE_CONFIG={enabled:!1,column:"deleted_at",defaultFilter:!0};export function createDatabaseQueryHooks(dispatch){function forward(event){try{Promise.resolve(dispatch(event)).catch(()=>{})}catch{}}return{onQueryEnd:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs}),onQueryError:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs,error:event.error})}}function forwardDatabaseQuery(event){import("./query-logger").then(({logQuery})=>logQuery(event)).catch(()=>{})}registerPersistentQueryHooks(createDatabaseQueryHooks(forwardDatabaseQuery));function getPoolConfig(){const driver=getDriver();if(driver==="sqlite")return;return getDatabaseConfig().connections?.[driver]?.pool}function getReplicas(){const driver=getDriver();if(driver==="sqlite")return[];return getDatabaseConfig().connections?.[driver]?.replicas??[]}function getReadPolicy(){return getDatabaseConfig().reads??{}}function toBunPoolOptions(pool){if(!pool)return{};const options={};if(pool.max!==void 0)options.max=pool.max;if(pool.idleTimeoutMs!==void 0)options.idleTimeout=Math.round(pool.idleTimeoutMs/1000);if(pool.acquireTimeoutMs!==void 0)options.connectionTimeout=Math.round(pool.acquireTimeoutMs/1000);if(pool.maxLifetimeMs!==void 0)options.maxLifetime=Math.round(pool.maxLifetimeMs/1000);return options}function updateQueryBuilderConfig(){const dialect=getDialect(),dbConfigForQb=getDbConfig(),pool=getPoolConfig();setConfig({dialect,vitess:{sharded:isVitessSharded(dbConfig.connections.vitess?.sharded)},database:pool?{...dbConfigForQb,pool}:dbConfigForQb,verbose:getEnv()!=="production",snapshotDir:QB_SNAPSHOT_DIR,migrationDir:relativeMigrationDirectory(resolveMigrationDirectory(toQueryBuilderDialect(dialect),{snapshotDir:QB_SNAPSHOT_DIR})),timestamps:{createdAt:"created_at",updatedAt:"updated_at",defaultOrderColumn:"created_at"},softDeletes:RAW_QUERY_SOFT_DELETE_CONFIG})}updateQueryBuilderConfig();let _dbInstance=null,_configInitPromise=null;function ensureConfigLoaded(){if(!_configInitPromise)_configInitPromise=(async()=>{try{const{config,overridesReady}=await import("@stacksjs/config");await overridesReady;if(config){initializeDbConfig(config);_dbInstance=null}}catch{}})();return _configInitPromise}export async function ensureDatabaseConfigLoaded(){await ensureConfigLoaded()}export{applySqlitePragmas,SQLITE_BOOTSTRAP_PRAGMAS}from"@stacksjs/query-builder";const sqliteTxOwner=new AsyncLocalStorage;let sqliteTxTail=Promise.resolve();function serializeSqliteTransaction(run){if(sqliteTxOwner.getStore())return run();const result=sqliteTxTail.then(()=>sqliteTxOwner.run(!0,run));sqliteTxTail=result.then(()=>{return},()=>{return});return result}function applySqliteTransactionSerialization(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>serializeSqliteTransaction(()=>original(...args))}function applyTransactionRoutingContext(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>withTransactionContext(()=>original(...args))}function getDb(){if(!_dbInstance){updateQueryBuilderConfig();_dbInstance=createQueryBuilder();if(getDialect()==="sqlite")applySqliteTransactionSerialization(_dbInstance);applyTransactionRoutingContext(_dbInstance)}return _dbInstance}let _replicaInstances=new Map;export function resetDatabaseConnection(){resetQueryBuilderConnection();_dbInstance=null;_replicaInstances=new Map}function getReplicaDb(replica){const primary=getDbConfig(),resolved=resolveReplicaConnection(replica,primary),key=`${resolved.host}:${resolved.port??""}`,cached=_replicaInstances.get(key);if(cached)return cached;const scheme=isMysqlWire(getDriver())?"mysql":"postgres",auth=resolved.username?`${encodeURIComponent(resolved.username)}:${encodeURIComponent(resolved.password??"")}@`:"",url=`${scheme}://${auth}${resolved.host}:${resolved.port}/${resolved.database}`,sql=new SQL({url,...toBunPoolOptions(getPoolConfig())}),instance=createQueryBuilder({sql});_replicaInstances.set(key,instance);return instance}function getReadDb(){const replicas=getReplicas(),policy=getReadPolicy();if(!shouldRouteToReplica({policy,replicas}))return getDb();const replica=selectReplica(replicas,policy.strategy);return replica?getReplicaDb(replica):getDb()}function getExplicitReadDb(){const replicas=getReplicas();if(!replicas.length||contextInTransaction())return getDb();const replica=selectReplica(replicas,getReadPolicy().strategy);return replica?getReplicaDb(replica):getDb()}const WRITE_ENTRY_POINTS=new Set(["insertInto","updateTable","deleteFrom","create","createMany","insertOrIgnore","insertGetId","updateOrInsert","upsert"]),READ_ENTRY_POINTS=new Set(["selectFrom","selectFromSub","select"]);ensureConfigLoaded();export const db=new Proxy({},{get(_target,prop){if(prop==="fn")return aggregateFunctions;if(prop==="read")return readDb;if(typeof prop==="string"&&WRITE_ENTRY_POINTS.has(prop))markContextWrote();const instance=typeof prop==="string"&&READ_ENTRY_POINTS.has(prop)?getReadDb():getDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);return value}}),readDb=new Proxy({},{get(_target,prop){if(prop==="fn")return aggregateFunctions;const instance=getExplicitReadDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);return value}});export{setConfig};
|
|
1
|
+
import{AsyncLocalStorage}from"node:async_hooks";import{createQueryBuilder,registerPersistentQueryHooks,resetConnection as resetQueryBuilderConnection,setConfig}from"@stacksjs/query-builder";import{SQL}from"bun";import{env as envVars}from"@stacksjs/env";import{getConnectionDefaults}from"./defaults";import{isMysqlWire,isVitessSharded,toQueryBuilderDialect}from"./dialect";import{relativeMigrationDirectory,resolveMigrationDirectory}from"./migration-path";import{contextInTransaction,markContextWrote,resolveReplicaConnection,selectReplica,shouldRouteToReplica,withTransactionContext}from"./replicas";import{aggregateFunctions}from"./types";const sqliteDefaults=getConnectionDefaults("sqlite",envVars),mysqlDefaults=getConnectionDefaults("mysql",envVars),postgresDefaults=getConnectionDefaults("postgres",envVars);let appEnv=envVars.APP_ENV||"local",dbDriver=envVars.DB_CONNECTION||"sqlite",dbConfig={connections:{sqlite:{database:sqliteDefaults.database,prefix:""},mysql:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},singlestore:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:mysqlDefaults.port,prefix:""},vitess:{name:mysqlDefaults.database,host:mysqlDefaults.host,username:mysqlDefaults.username,password:mysqlDefaults.password,port:15306,prefix:"",sharded:isVitessSharded()},postgres:{name:postgresDefaults.database,host:postgresDefaults.host,username:postgresDefaults.username,password:postgresDefaults.password,port:postgresDefaults.port,prefix:""}}},dbConfigLockTail=Promise.resolve();export function acquireDbConfigLock(){let release=()=>{};const held=new Promise((resolve)=>{release=resolve}),acquired=dbConfigLockTail.then(()=>release);dbConfigLockTail=dbConfigLockTail.then(()=>held);return acquired}export function initializeDbConfig(config){if(config?.app?.env)appEnv=config.app.env;if(config?.database?.default)dbDriver=config.database.default;if(config?.database)dbConfig=config.database;updateQueryBuilderConfig();_dbInstance=null;_replicaInstances=new Map}function getEnv(){return appEnv}function getDriver(){return dbDriver}function getDatabaseConfig(){return dbConfig}function getDialect(){return toQueryBuilderDialect(getDriver())}function getDbConfig(){const driver=getDriver(),database=getDatabaseConfig(),env=getEnv();if(driver==="sqlite"){const defaultName=env!=="testing"?"database/stacks.sqlite":"database/stacks_testing.sqlite";return{database:database.connections?.sqlite?.database??defaultName}}if(driver==="mysql")return{database:database.connections?.mysql?.name||"stacks",host:database.connections?.mysql?.host??"127.0.0.1",username:database.connections?.mysql?.username??"root",password:database.connections?.mysql?.password??"",port:database.connections?.mysql?.port??3306};if(driver==="singlestore")return{database:database.connections?.singlestore?.name||"stacks",host:database.connections?.singlestore?.host??"127.0.0.1",username:database.connections?.singlestore?.username??"root",password:database.connections?.singlestore?.password??"",port:database.connections?.singlestore?.port??3306};if(driver==="vitess")return{database:database.connections?.vitess?.name||"stacks",host:database.connections?.vitess?.host??"127.0.0.1",username:database.connections?.vitess?.username??"root",password:database.connections?.vitess?.password??"",port:database.connections?.vitess?.port??15306};if(driver==="postgres"){const dbName=database.connections?.postgres?.name??"stacks";return{database:env==="testing"?`${dbName}_testing`:dbName,host:database.connections?.postgres?.host??"127.0.0.1",username:database.connections?.postgres?.username??"",password:database.connections?.postgres?.password??"",port:database.connections?.postgres?.port??5432}}return{database:":memory:"}}export const QB_SNAPSHOT_DIR="storage/framework/database",RAW_QUERY_SOFT_DELETE_CONFIG={enabled:!1,column:"deleted_at",defaultFilter:!0};export function createDatabaseQueryHooks(dispatch){function forward(event){try{Promise.resolve(dispatch(event)).catch(()=>{})}catch{}}return{onQueryEnd:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs}),onQueryError:(event)=>forward({query:{sql:event.sql,parameters:event.params},queryDurationMillis:event.durationMs,error:event.error})}}function forwardDatabaseQuery(event){import("./query-logger").then(({logQuery})=>logQuery(event)).catch(()=>{})}registerPersistentQueryHooks(createDatabaseQueryHooks(forwardDatabaseQuery));function getPoolConfig(){const driver=getDriver();if(driver==="sqlite")return;return getDatabaseConfig().connections?.[driver]?.pool}function getReplicas(){const driver=getDriver();if(driver==="sqlite")return[];return getDatabaseConfig().connections?.[driver]?.replicas??[]}function getReadPolicy(){return getDatabaseConfig().reads??{}}function toBunPoolOptions(pool){if(!pool)return{};const options={};if(pool.max!==void 0)options.max=pool.max;if(pool.idleTimeoutMs!==void 0)options.idleTimeout=Math.round(pool.idleTimeoutMs/1000);if(pool.acquireTimeoutMs!==void 0)options.connectionTimeout=Math.round(pool.acquireTimeoutMs/1000);if(pool.maxLifetimeMs!==void 0)options.maxLifetime=Math.round(pool.maxLifetimeMs/1000);return options}function updateQueryBuilderConfig(){const dialect=getDialect(),dbConfigForQb=getDbConfig(),pool=getPoolConfig();setConfig({dialect,vitess:{sharded:isVitessSharded(dbConfig.connections.vitess?.sharded)},database:pool?{...dbConfigForQb,pool}:dbConfigForQb,verbose:getEnv()!=="production",snapshotDir:QB_SNAPSHOT_DIR,migrationDir:relativeMigrationDirectory(resolveMigrationDirectory(toQueryBuilderDialect(dialect),{snapshotDir:QB_SNAPSHOT_DIR})),timestamps:{createdAt:"created_at",updatedAt:"updated_at",defaultOrderColumn:"created_at"},softDeletes:RAW_QUERY_SOFT_DELETE_CONFIG})}updateQueryBuilderConfig();let _dbInstance=null,_configInitPromise=null;function ensureConfigLoaded(){if(!_configInitPromise)_configInitPromise=(async()=>{try{const{config,overridesReady}=await import("@stacksjs/config");await overridesReady;if(config){initializeDbConfig(config);_dbInstance=null}}catch{}})();return _configInitPromise}export async function ensureDatabaseConfigLoaded(){await ensureConfigLoaded()}export{applySqlitePragmas,SQLITE_BOOTSTRAP_PRAGMAS}from"@stacksjs/query-builder";const sqliteTxOwner=new AsyncLocalStorage;let sqliteTxTail=Promise.resolve();function serializeSqliteTransaction(run){if(sqliteTxOwner.getStore())return run();const result=sqliteTxTail.then(()=>sqliteTxOwner.run(!0,run));sqliteTxTail=result.then(()=>{return},()=>{return});return result}function applySqliteTransactionSerialization(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>serializeSqliteTransaction(()=>original(...args))}function applyTransactionRoutingContext(instance){const original=instance.transaction.bind(instance);instance.transaction=(...args)=>withTransactionContext(()=>original(...args))}function getDb(){if(!_dbInstance){updateQueryBuilderConfig();_dbInstance=createQueryBuilder();if(getDialect()==="sqlite")applySqliteTransactionSerialization(_dbInstance);applyTransactionRoutingContext(_dbInstance)}return _dbInstance}let _replicaInstances=new Map;export function resetDatabaseConnection(){resetQueryBuilderConnection();_dbInstance=null;_replicaInstances=new Map}function getReplicaDb(replica){const primary=getDbConfig(),resolved=resolveReplicaConnection(replica,primary),key=`${resolved.host}:${resolved.port??""}`,cached=_replicaInstances.get(key);if(cached)return cached;const scheme=isMysqlWire(getDriver())?"mysql":"postgres",auth=resolved.username?`${encodeURIComponent(resolved.username)}:${encodeURIComponent(resolved.password??"")}@`:"",url=`${scheme}://${auth}${resolved.host}:${resolved.port}/${resolved.database}`,sql=new SQL({url,...toBunPoolOptions(getPoolConfig())}),instance=createQueryBuilder({sql});_replicaInstances.set(key,instance);return instance}function getReadDb(){const replicas=getReplicas(),policy=getReadPolicy();if(!shouldRouteToReplica({policy,replicas}))return getDb();const replica=selectReplica(replicas,policy.strategy);return replica?getReplicaDb(replica):getDb()}function getExplicitReadDb(){const replicas=getReplicas();if(!replicas.length||contextInTransaction())return getDb();const replica=selectReplica(replicas,getReadPolicy().strategy);return replica?getReplicaDb(replica):getDb()}const WRITE_ENTRY_POINTS=new Set(["insertInto","updateTable","deleteFrom","create","createMany","insertOrIgnore","insertGetId","updateOrInsert","upsert"]),READ_ENTRY_POINTS=new Set(["selectFrom","selectFromSub","select"]);ensureConfigLoaded();export function asRows(rows){return rows}export function asRow(row){return row}export const db=new Proxy({},{get(_target,prop){if(prop==="fn")return aggregateFunctions;if(prop==="read")return readDb;if(typeof prop==="string"&&WRITE_ENTRY_POINTS.has(prop))markContextWrote();const instance=typeof prop==="string"&&READ_ENTRY_POINTS.has(prop)?getReadDb():getDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);return value}}),readDb=new Proxy({},{get(_target,prop){if(prop==="fn")return aggregateFunctions;const instance=getExplicitReadDb(),value=instance[prop];if(typeof value==="function")return value.bind(instance);return value}});export{setConfig};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/database",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.72.
|
|
5
|
+
"version": "0.72.23",
|
|
6
6
|
"description": "The Stacks database integration.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -65,15 +65,15 @@
|
|
|
65
65
|
"dynamodb-tooling": "^0.3.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@stacksjs/cli": "0.72.
|
|
69
|
-
"@stacksjs/config": "0.72.
|
|
70
|
-
"@stacksjs/logging": "0.72.
|
|
71
|
-
"@stacksjs/router": "0.72.
|
|
68
|
+
"@stacksjs/cli": "0.72.23",
|
|
69
|
+
"@stacksjs/config": "0.72.23",
|
|
70
|
+
"@stacksjs/logging": "0.72.23",
|
|
71
|
+
"@stacksjs/router": "0.72.23",
|
|
72
72
|
"better-dx": "^0.2.23",
|
|
73
|
-
"@stacksjs/path": "0.72.
|
|
74
|
-
"@stacksjs/query-builder": "0.72.
|
|
75
|
-
"@stacksjs/storage": "0.72.
|
|
76
|
-
"@stacksjs/strings": "0.72.
|
|
77
|
-
"@stacksjs/utils": "0.72.
|
|
73
|
+
"@stacksjs/path": "0.72.23",
|
|
74
|
+
"@stacksjs/query-builder": "0.72.23",
|
|
75
|
+
"@stacksjs/storage": "0.72.23",
|
|
76
|
+
"@stacksjs/strings": "0.72.23",
|
|
77
|
+
"@stacksjs/utils": "0.72.23"
|
|
78
78
|
}
|
|
79
79
|
}
|