@xwxfox/foxdb 0.0.1
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/LICENSE.md +21 -0
- package/README.md +274 -0
- package/dist/index.d.mts +1463 -0
- package/dist/index.mjs +48 -0
- package/package.json +67 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,1463 @@
|
|
|
1
|
+
import { TObject as TObject2, TArray, TSchema, TProperties, Static } from "typebox";
|
|
2
|
+
import { TObject, TString, TNumber, TInteger, TBoolean, TLiteral } from "typebox";
|
|
3
|
+
/** @category Schema */
|
|
4
|
+
type TScalarSchema = TString | TNumber | TInteger | TBoolean | TLiteral<string> | TLiteral<number> | TLiteral<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* typed reference to a scalar column
|
|
7
|
+
* @category Schema
|
|
8
|
+
*/
|
|
9
|
+
interface ColumnRef<
|
|
10
|
+
N extends string = string,
|
|
11
|
+
S extends TScalarSchema = TScalarSchema
|
|
12
|
+
> {
|
|
13
|
+
readonly _tag: "ColumnRef";
|
|
14
|
+
readonly name: N;
|
|
15
|
+
readonly schema: S;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* map of scalar columns to their typed refs
|
|
19
|
+
* @category Schema
|
|
20
|
+
*/
|
|
21
|
+
type ColumnRefs<T extends TObject> = { readonly [K in keyof T["properties"] as T["properties"][K] extends TScalarSchema ? K : never] : T["properties"][K] extends TScalarSchema ? ColumnRef<K & string, T["properties"][K]> : never };
|
|
22
|
+
/**
|
|
23
|
+
* foxdb/src/typed-relation.ts
|
|
24
|
+
* Runtime + compile-time representation of a single relation.
|
|
25
|
+
* @category Relations
|
|
26
|
+
*/
|
|
27
|
+
interface TypedRelation<
|
|
28
|
+
O extends string = string,
|
|
29
|
+
F extends string = string,
|
|
30
|
+
T extends string = string,
|
|
31
|
+
TF extends string = string,
|
|
32
|
+
K extends "scalar" | "subTable" = "scalar" | "subTable",
|
|
33
|
+
A extends string | undefined = string | undefined
|
|
34
|
+
> {
|
|
35
|
+
ownerTable: O;
|
|
36
|
+
ownerField: F;
|
|
37
|
+
targetTable: T;
|
|
38
|
+
targetField: TF;
|
|
39
|
+
kind: K;
|
|
40
|
+
as: A;
|
|
41
|
+
}
|
|
42
|
+
import { Database, SQLQueryBindings } from "bun:sqlite";
|
|
43
|
+
/**
|
|
44
|
+
* prepared sqlite statement
|
|
45
|
+
* @category Database
|
|
46
|
+
*/
|
|
47
|
+
interface BunStatement {
|
|
48
|
+
run(...params: SQLQueryBindings[]): {
|
|
49
|
+
changes: number;
|
|
50
|
+
lastInsertRowid: number | bigint;
|
|
51
|
+
};
|
|
52
|
+
all(...params: SQLQueryBindings[]): unknown[];
|
|
53
|
+
get(...params: SQLQueryBindings[]): unknown;
|
|
54
|
+
finalize(): void;
|
|
55
|
+
}
|
|
56
|
+
/** @category Database */
|
|
57
|
+
interface DatabaseOptions {
|
|
58
|
+
/** Path to the SQLite file. Defaults to ":memory:" */
|
|
59
|
+
path?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Cache size in pages (each page is typically 4 KB).
|
|
62
|
+
* Negative value = kilobytes. Defaults to -64000 (~64 MB).
|
|
63
|
+
*/
|
|
64
|
+
cacheSize?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Busy timeout in milliseconds while waiting for a write lock.
|
|
67
|
+
* Defaults to 5000.
|
|
68
|
+
*/
|
|
69
|
+
busyTimeout?: number;
|
|
70
|
+
/**
|
|
71
|
+
* PRAGMA synchronous level.
|
|
72
|
+
* "OFF" | "NORMAL" | "FULL" | "EXTRA". Defaults to "NORMAL".
|
|
73
|
+
* NORMAL is safe with WAL and gives the best perf/safety trade-off.
|
|
74
|
+
*/
|
|
75
|
+
synchronous?: "OFF" | "NORMAL" | "FULL" | "EXTRA";
|
|
76
|
+
/** PRAGMA mmap_size in bytes. 0 disables. Defaults to 256 MB. */
|
|
77
|
+
mmapSize?: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* sqlite database with statement caching and pragma tuning
|
|
81
|
+
* @category Database
|
|
82
|
+
*/
|
|
83
|
+
declare class BunDatabase {
|
|
84
|
+
/** underlying bun:sqlite database */
|
|
85
|
+
readonly db: Database;
|
|
86
|
+
constructor(opts?: DatabaseOptions);
|
|
87
|
+
/** execute ddl (create table, index, etc) */
|
|
88
|
+
exec(sql: string): void;
|
|
89
|
+
/** run a block inside a transaction */
|
|
90
|
+
transaction<T>(fn: () => T): T;
|
|
91
|
+
/** close the database and finalize cached statements */
|
|
92
|
+
close(): void;
|
|
93
|
+
private static readonly _MAX_CACHE_SIZE;
|
|
94
|
+
private readonly _stmtCache;
|
|
95
|
+
/** get or compile a prepared statement */
|
|
96
|
+
prepare(sql: string): BunStatement;
|
|
97
|
+
/** clear the statement cache */
|
|
98
|
+
clearCache(): void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @internal
|
|
102
|
+
* @category Advanced
|
|
103
|
+
*/
|
|
104
|
+
type ScalarProperties<P extends TProperties> = { [K in keyof P as P[K] extends TArray<infer _> ? never : K] : P[K] };
|
|
105
|
+
/**
|
|
106
|
+
* @internal
|
|
107
|
+
* @category Advanced
|
|
108
|
+
*/
|
|
109
|
+
type ArrayOfObjectProperties<P extends TProperties> = { [K in keyof P as P[K] extends TArray<TObject2> ? K : never] : P[K] extends TArray<infer Item> ? Item : never };
|
|
110
|
+
/**
|
|
111
|
+
* column names that are scalar (not arrays or nested objects)
|
|
112
|
+
* @category Query Types
|
|
113
|
+
*/
|
|
114
|
+
type ScalarKeys<T extends TObject2> = keyof ScalarProperties<T["properties"]> & string;
|
|
115
|
+
/**
|
|
116
|
+
* @internal
|
|
117
|
+
* @category Query Types
|
|
118
|
+
*/
|
|
119
|
+
type SubTableKeys<T extends TObject2> = keyof ArrayOfObjectProperties<T["properties"]> & string;
|
|
120
|
+
/**
|
|
121
|
+
* @internal
|
|
122
|
+
* @category Advanced
|
|
123
|
+
*/
|
|
124
|
+
type SubTableScalarPath<T extends TObject2> = { [K in SubTableKeys<T>] : T["properties"][K] extends TArray<infer Item> ? Item extends TObject2 ? `${K}.${ScalarKeys<Item>}` : never : never }[SubTableKeys<T>];
|
|
125
|
+
/**
|
|
126
|
+
* @internal
|
|
127
|
+
* @category Advanced
|
|
128
|
+
*/
|
|
129
|
+
type ScalarPath<T extends TObject2> = ScalarKeys<T> | SubTableScalarPath<T>;
|
|
130
|
+
/**
|
|
131
|
+
* the typescript type that matches a typebox schema
|
|
132
|
+
* @category Query Types
|
|
133
|
+
*/
|
|
134
|
+
type Infer<T extends TSchema> = Static<T>;
|
|
135
|
+
/** @category Query Types */
|
|
136
|
+
type ScalarFilter<V> = V extends string ? {
|
|
137
|
+
eq: V;
|
|
138
|
+
} | {
|
|
139
|
+
ne: V;
|
|
140
|
+
} | {
|
|
141
|
+
like: string;
|
|
142
|
+
} | {
|
|
143
|
+
in: V[];
|
|
144
|
+
} | {
|
|
145
|
+
notIn: V[];
|
|
146
|
+
} | {
|
|
147
|
+
isNull: true;
|
|
148
|
+
} | {
|
|
149
|
+
isNotNull: true;
|
|
150
|
+
} : V extends number | bigint ? {
|
|
151
|
+
eq: V;
|
|
152
|
+
} | {
|
|
153
|
+
ne: V;
|
|
154
|
+
} | {
|
|
155
|
+
gt: V;
|
|
156
|
+
} | {
|
|
157
|
+
gte: V;
|
|
158
|
+
} | {
|
|
159
|
+
lt: V;
|
|
160
|
+
} | {
|
|
161
|
+
lte: V;
|
|
162
|
+
} | {
|
|
163
|
+
between: [V, V];
|
|
164
|
+
} | {
|
|
165
|
+
in: V[];
|
|
166
|
+
} | {
|
|
167
|
+
isNull: true;
|
|
168
|
+
} | {
|
|
169
|
+
isNotNull: true;
|
|
170
|
+
} : V extends boolean ? {
|
|
171
|
+
eq: V;
|
|
172
|
+
} | {
|
|
173
|
+
isNull: true;
|
|
174
|
+
} | {
|
|
175
|
+
isNotNull: true;
|
|
176
|
+
} : {
|
|
177
|
+
isNull: true;
|
|
178
|
+
} | {
|
|
179
|
+
isNotNull: true;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Where filters for queries - only scalar columns are filterable.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* // String filters
|
|
187
|
+
* orm.users.findMany({ where: { name: { like: "%alice%" } } });
|
|
188
|
+
* orm.users.findMany({ where: { email: { in: ["a@x.com", "b@x.com"] } } });
|
|
189
|
+
*
|
|
190
|
+
* // Number filters
|
|
191
|
+
* orm.users.findMany({ where: { age: { gte: 18, lte: 65 } } });
|
|
192
|
+
* orm.products.findMany({ where: { price: { between: [10, 100] } } });
|
|
193
|
+
*
|
|
194
|
+
* // Boolean / null filters
|
|
195
|
+
* orm.users.findMany({ where: { active: { eq: true } } });
|
|
196
|
+
* orm.users.findMany({ where: { deletedAt: { isNull: true } } });
|
|
197
|
+
* ```
|
|
198
|
+
* @category Query Types
|
|
199
|
+
* @category Query Types
|
|
200
|
+
*/
|
|
201
|
+
type WhereClause<T extends TObject2> = { [K in ScalarKeys<T>]? : ScalarFilter<Static<T["properties"][K]>> };
|
|
202
|
+
/**
|
|
203
|
+
* sort direction for queries
|
|
204
|
+
* @category Query Types
|
|
205
|
+
*/
|
|
206
|
+
type OrderByClause<T extends TObject2> = {
|
|
207
|
+
column: ScalarKeys<T>;
|
|
208
|
+
direction?: "ASC" | "DESC";
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* @internal
|
|
212
|
+
* @category Advanced
|
|
213
|
+
*/
|
|
214
|
+
interface PaginationOptions {
|
|
215
|
+
limit?: number;
|
|
216
|
+
offset?: number;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Options for `findMany`, `findPage`, and `findOne`.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* orm.users.findMany({
|
|
224
|
+
* where: { age: { gte: 18 } },
|
|
225
|
+
* orderBy: { column: "name", direction: "ASC" },
|
|
226
|
+
* limit: 10,
|
|
227
|
+
* offset: 0,
|
|
228
|
+
* });
|
|
229
|
+
*
|
|
230
|
+
* // Multiple orderBy clauses
|
|
231
|
+
* orm.users.findMany({
|
|
232
|
+
* orderBy: [
|
|
233
|
+
* { column: "status", direction: "DESC" },
|
|
234
|
+
* { column: "createdAt", direction: "ASC" },
|
|
235
|
+
* ],
|
|
236
|
+
* });
|
|
237
|
+
*
|
|
238
|
+
* // Include sub-tables
|
|
239
|
+
* orm.orders.findMany({ include: ["lineItems"] });
|
|
240
|
+
* ```
|
|
241
|
+
* @category Query Types
|
|
242
|
+
* @category Query Types
|
|
243
|
+
*/
|
|
244
|
+
interface FindOptions<T extends TObject2> extends PaginationOptions {
|
|
245
|
+
where?: WhereClause<T>;
|
|
246
|
+
orderBy?: OrderByClause<T> | OrderByClause<T>[];
|
|
247
|
+
include?: SubTableKeys<T>[];
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* full record to insert
|
|
251
|
+
* @category Query Types
|
|
252
|
+
*/
|
|
253
|
+
type InsertData<T extends TObject2> = Infer<T>;
|
|
254
|
+
/**
|
|
255
|
+
* update payload - must include the primary key
|
|
256
|
+
* @category Query Types
|
|
257
|
+
*/
|
|
258
|
+
type UpdateData<
|
|
259
|
+
T extends TObject2,
|
|
260
|
+
PK extends ScalarKeys<T>
|
|
261
|
+
> = Pick<Infer<T>, PK> & Partial<Omit<Infer<T>, PK>>;
|
|
262
|
+
/**
|
|
263
|
+
* index on one or more columns
|
|
264
|
+
* @category Schema
|
|
265
|
+
*/
|
|
266
|
+
interface IndexDefinition {
|
|
267
|
+
name?: string;
|
|
268
|
+
columns: ColumnRef<string, TScalarSchema>[];
|
|
269
|
+
unique?: boolean;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* timestamp configuration for a table
|
|
273
|
+
* @category Schema
|
|
274
|
+
*/
|
|
275
|
+
type TimestampConfig = true | false | {
|
|
276
|
+
createdAt?: string;
|
|
277
|
+
updatedAt?: string;
|
|
278
|
+
} | undefined;
|
|
279
|
+
/**
|
|
280
|
+
* @internal
|
|
281
|
+
* @category Schema
|
|
282
|
+
*/
|
|
283
|
+
type TimestampShape<T extends TimestampConfig> = true extends T ? {
|
|
284
|
+
createdAt: number;
|
|
285
|
+
updatedAt: number;
|
|
286
|
+
} : [T] extends [{
|
|
287
|
+
createdAt?: infer C;
|
|
288
|
+
updatedAt?: infer U;
|
|
289
|
+
}] ? (C extends string ? { [K in C] : number } : {}) & (U extends string ? { [K in U] : number } : {}) : {};
|
|
290
|
+
/**
|
|
291
|
+
* A database row with optional timestamps and materialized relations.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* // Without relations
|
|
296
|
+
* type User = Entity<{ id: string; name: string }>;
|
|
297
|
+
* // → { id: string; name: string }
|
|
298
|
+
*
|
|
299
|
+
* // With timestamps
|
|
300
|
+
* type UserWithTS = Entity<{ id: string }, never, { createdAt: number }>;
|
|
301
|
+
* // → { id: string; createdAt: number }
|
|
302
|
+
*
|
|
303
|
+
* // With relations
|
|
304
|
+
* type UserWithRels = Entity<{ id: string }, { posts: Post[] }>;
|
|
305
|
+
* // → { id: string; materialize(): { posts: Post[] } }
|
|
306
|
+
* ```
|
|
307
|
+
* @category Query Types
|
|
308
|
+
* @category Query Types
|
|
309
|
+
*/
|
|
310
|
+
type Entity<
|
|
311
|
+
T,
|
|
312
|
+
Mat = never,
|
|
313
|
+
TS = {}
|
|
314
|
+
> = [Mat] extends [never] ? T & TS : T & TS & {
|
|
315
|
+
materialize(): Mat;
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* table descriptor passed to createORM
|
|
319
|
+
* @category Schema
|
|
320
|
+
*/
|
|
321
|
+
interface TableConfig<
|
|
322
|
+
T extends TObject2 = TObject2,
|
|
323
|
+
PK extends string = string,
|
|
324
|
+
TS extends TimestampConfig = undefined
|
|
325
|
+
> {
|
|
326
|
+
schema: T;
|
|
327
|
+
primaryKey: ColumnRef<PK>;
|
|
328
|
+
indexes?: IndexDefinition[];
|
|
329
|
+
subTables?: Partial<Record<string, {
|
|
330
|
+
indexes?: IndexDefinition[];
|
|
331
|
+
}>>;
|
|
332
|
+
timestamps?: TS;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* read-only metadata about the current database schema
|
|
336
|
+
* @category Setup
|
|
337
|
+
*/
|
|
338
|
+
interface MetaAccessors {
|
|
339
|
+
schemaHash: string | null;
|
|
340
|
+
schemaJSON: string | null;
|
|
341
|
+
tables: string[] | null;
|
|
342
|
+
relations: unknown[] | null;
|
|
343
|
+
version: string | null;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* @internal
|
|
347
|
+
* @category Relations
|
|
348
|
+
*/
|
|
349
|
+
interface RelationEntry<
|
|
350
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
351
|
+
Owner extends keyof Tables,
|
|
352
|
+
Target extends keyof Tables
|
|
353
|
+
> {
|
|
354
|
+
ownerField: ScalarPath<Tables[Owner]["schema"]>;
|
|
355
|
+
targetTableName: Target;
|
|
356
|
+
targetField: ScalarKeys<Tables[Target]["schema"]>;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* @internal
|
|
360
|
+
* @category Relations
|
|
361
|
+
*/
|
|
362
|
+
type RelationsConfig<Tables extends Record<string, TableConfig<any, any, any>>> = { [K in keyof Tables & string]? : Array<{ [Target in keyof Tables & string] : RelationEntry<Tables, K, Target> }[keyof Tables & string]> };
|
|
363
|
+
/**
|
|
364
|
+
* @internal
|
|
365
|
+
* @category Relations
|
|
366
|
+
*/
|
|
367
|
+
type ScalarMergeNames<
|
|
368
|
+
Rels extends readonly TypedRelation[],
|
|
369
|
+
Owner extends string
|
|
370
|
+
> = Extract<Rels[number], {
|
|
371
|
+
ownerTable: Owner;
|
|
372
|
+
kind: "scalar";
|
|
373
|
+
}> extends TypedRelation<any, any, any, any, any, infer A> ? A extends string ? A : never : never;
|
|
374
|
+
/**
|
|
375
|
+
* @internal
|
|
376
|
+
* @category Relations
|
|
377
|
+
*/
|
|
378
|
+
type ScalarMergeType<
|
|
379
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
380
|
+
Rels extends readonly TypedRelation[],
|
|
381
|
+
Owner extends string,
|
|
382
|
+
Name extends string
|
|
383
|
+
> = Extract<Rels[number], {
|
|
384
|
+
ownerTable: Owner;
|
|
385
|
+
kind: "scalar";
|
|
386
|
+
as: Name;
|
|
387
|
+
}> extends TypedRelation<any, any, infer TT, any, any, any> ? TT extends keyof Tables ? Infer<Tables[TT]["schema"]> | null : never : never;
|
|
388
|
+
/**
|
|
389
|
+
* @internal
|
|
390
|
+
* @category Relations
|
|
391
|
+
*/
|
|
392
|
+
type ScalarMerge<
|
|
393
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
394
|
+
Rels extends readonly TypedRelation[],
|
|
395
|
+
Owner extends string
|
|
396
|
+
> = { [K in ScalarMergeNames<Rels, Owner>] : ScalarMergeType<Tables, Rels, Owner, K> };
|
|
397
|
+
/**
|
|
398
|
+
* @internal
|
|
399
|
+
* @category Relations
|
|
400
|
+
*/
|
|
401
|
+
type SubMergeNames<
|
|
402
|
+
Rels extends readonly TypedRelation[],
|
|
403
|
+
Owner extends string,
|
|
404
|
+
Sub extends string
|
|
405
|
+
> = Extract<Rels[number], {
|
|
406
|
+
ownerTable: Owner;
|
|
407
|
+
kind: "subTable";
|
|
408
|
+
ownerField: `${Sub}.${string}`;
|
|
409
|
+
}> extends TypedRelation<any, any, any, any, any, infer A> ? A extends string ? A : never : never;
|
|
410
|
+
/**
|
|
411
|
+
* @internal
|
|
412
|
+
* @category Relations
|
|
413
|
+
*/
|
|
414
|
+
type SubMergeType<
|
|
415
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
416
|
+
Rels extends readonly TypedRelation[],
|
|
417
|
+
Owner extends string,
|
|
418
|
+
Sub extends string,
|
|
419
|
+
Name extends string
|
|
420
|
+
> = Extract<Rels[number], {
|
|
421
|
+
ownerTable: Owner;
|
|
422
|
+
kind: "subTable";
|
|
423
|
+
ownerField: `${Sub}.${string}`;
|
|
424
|
+
as: Name;
|
|
425
|
+
}> extends TypedRelation<any, any, infer TT, any, any, any> ? TT extends keyof Tables ? Infer<Tables[TT]["schema"]> | null : never : never;
|
|
426
|
+
/**
|
|
427
|
+
* @internal
|
|
428
|
+
* @category Relations
|
|
429
|
+
*/
|
|
430
|
+
type SubMerge<
|
|
431
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
432
|
+
Rels extends readonly TypedRelation[],
|
|
433
|
+
Owner extends string,
|
|
434
|
+
Sub extends string
|
|
435
|
+
> = { [K in SubMergeNames<Rels, Owner, Sub>] : SubMergeType<Tables, Rels, Owner, Sub, K> };
|
|
436
|
+
/**
|
|
437
|
+
* Entity with resolved relations. Sub-table arrays get their related entities
|
|
438
|
+
* merged in, and scalar relations appear as direct properties.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```ts
|
|
442
|
+
* // Given: OrderSchema with lineItems: Array<{ sku: string; qty: number }>
|
|
443
|
+
* // And a relation: lineItems.sku → products.sku (as "product")
|
|
444
|
+
*
|
|
445
|
+
* type M = Materialized<OrderSchema, Tables, Rels, "orders">;
|
|
446
|
+
* // M.lineItems becomes Array<{ sku: string; qty: number; product: Product | null }>
|
|
447
|
+
* // M also gets scalar relation properties like `.related` accessors
|
|
448
|
+
* ```
|
|
449
|
+
* @category Relations
|
|
450
|
+
* @category Relations
|
|
451
|
+
*/
|
|
452
|
+
type Materialized<
|
|
453
|
+
T extends TObject2,
|
|
454
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
455
|
+
Rels extends readonly TypedRelation[],
|
|
456
|
+
Owner extends string
|
|
457
|
+
> = { [K in keyof Infer<T>] : K extends string ? Infer<T>[K] extends Array<infer Item> ? Array<Item & SubMerge<Tables, Rels, Owner, K>> : Infer<T>[K] : Infer<T>[K] } & ScalarMerge<Tables, Rels, Owner>;
|
|
458
|
+
/**
|
|
459
|
+
* paginated query result
|
|
460
|
+
* @category Query Types
|
|
461
|
+
*/
|
|
462
|
+
interface PageResult<T> {
|
|
463
|
+
data: T[];
|
|
464
|
+
total: number;
|
|
465
|
+
limit: number;
|
|
466
|
+
offset: number;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* insert or update on conflict
|
|
470
|
+
* @category Query Types
|
|
471
|
+
*/
|
|
472
|
+
interface UpsertOptions<
|
|
473
|
+
T extends TObject2,
|
|
474
|
+
PK extends ScalarKeys<T>
|
|
475
|
+
> {
|
|
476
|
+
data: InsertData<T>;
|
|
477
|
+
conflictTarget: PK | PK[];
|
|
478
|
+
/** columns to update on conflict - defaults to all non-pk columns */
|
|
479
|
+
update?: Array<ScalarKeys<T>>;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* a single migration step
|
|
483
|
+
* @category Migration
|
|
484
|
+
*/
|
|
485
|
+
interface Migration {
|
|
486
|
+
name: string;
|
|
487
|
+
date: string;
|
|
488
|
+
up: (db: BunDatabase) => void;
|
|
489
|
+
down?: (db: BunDatabase) => void;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* options for migrate()
|
|
493
|
+
* @category Migration
|
|
494
|
+
*/
|
|
495
|
+
interface MigrateOptions {
|
|
496
|
+
path: string;
|
|
497
|
+
migrationsDir: string;
|
|
498
|
+
direction?: "up";
|
|
499
|
+
target?: string;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* @internal
|
|
503
|
+
* @category Migration
|
|
504
|
+
*/
|
|
505
|
+
type SchemaChange = {
|
|
506
|
+
kind: "add-table";
|
|
507
|
+
table: string;
|
|
508
|
+
} | {
|
|
509
|
+
kind: "add-column";
|
|
510
|
+
table: string;
|
|
511
|
+
column: {
|
|
512
|
+
name: string;
|
|
513
|
+
sqlType: string;
|
|
514
|
+
nullable: boolean;
|
|
515
|
+
optional: boolean;
|
|
516
|
+
};
|
|
517
|
+
hasDefault: boolean;
|
|
518
|
+
} | {
|
|
519
|
+
kind: "add-index";
|
|
520
|
+
table: string;
|
|
521
|
+
index: {
|
|
522
|
+
name: string;
|
|
523
|
+
unique: number;
|
|
524
|
+
columns: string[];
|
|
525
|
+
};
|
|
526
|
+
} | {
|
|
527
|
+
kind: "add-subtable";
|
|
528
|
+
table: string;
|
|
529
|
+
subTable: {
|
|
530
|
+
fieldName: string;
|
|
531
|
+
tableName: string;
|
|
532
|
+
columns: {
|
|
533
|
+
name: string;
|
|
534
|
+
sqlType: string;
|
|
535
|
+
nullable: boolean;
|
|
536
|
+
optional: boolean;
|
|
537
|
+
}[];
|
|
538
|
+
};
|
|
539
|
+
} | {
|
|
540
|
+
kind: "drop-column";
|
|
541
|
+
table: string;
|
|
542
|
+
column: string;
|
|
543
|
+
} | {
|
|
544
|
+
kind: "rename-column";
|
|
545
|
+
table: string;
|
|
546
|
+
from: string;
|
|
547
|
+
to: string;
|
|
548
|
+
} | {
|
|
549
|
+
kind: "change-type";
|
|
550
|
+
table: string;
|
|
551
|
+
column: string;
|
|
552
|
+
from: string;
|
|
553
|
+
to: string;
|
|
554
|
+
} | {
|
|
555
|
+
kind: "change-nullable";
|
|
556
|
+
table: string;
|
|
557
|
+
column: string;
|
|
558
|
+
to: boolean;
|
|
559
|
+
} | {
|
|
560
|
+
kind: "drop-table";
|
|
561
|
+
table: string;
|
|
562
|
+
} | {
|
|
563
|
+
kind: "change-pk";
|
|
564
|
+
table: string;
|
|
565
|
+
} | {
|
|
566
|
+
kind: "drop-index";
|
|
567
|
+
table: string;
|
|
568
|
+
index: {
|
|
569
|
+
name: string;
|
|
570
|
+
unique: number;
|
|
571
|
+
columns: string[];
|
|
572
|
+
};
|
|
573
|
+
};
|
|
574
|
+
/**
|
|
575
|
+
* @internal
|
|
576
|
+
* @category Migration
|
|
577
|
+
*/
|
|
578
|
+
interface SchemaDiff {
|
|
579
|
+
safe: SchemaChange[];
|
|
580
|
+
unsafe: SchemaChange[];
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* how to handle schema drift on startup
|
|
584
|
+
* @category Migration
|
|
585
|
+
*/
|
|
586
|
+
type SyncPolicy = "ignore" | "warn" | "error" | "auto" | ((diff: SchemaDiff, db: BunDatabase) => boolean | void);
|
|
587
|
+
/**
|
|
588
|
+
* specific operations that can be listened to per table
|
|
589
|
+
* @category Events
|
|
590
|
+
*/
|
|
591
|
+
type TableOperation = "insert" | "insertMany" | "update" | "delete" | "deleteWhere" | "upsert" | "findById" | "findMany" | "findOne" | "findPage" | "count" | "flush";
|
|
592
|
+
/**
|
|
593
|
+
* broad categories for event listening
|
|
594
|
+
* @category Events
|
|
595
|
+
*/
|
|
596
|
+
type BroadOperation = "read" | "write" | "delete";
|
|
597
|
+
/**
|
|
598
|
+
* @internal
|
|
599
|
+
* @category Events
|
|
600
|
+
*/
|
|
601
|
+
type TableEventOperation = TableOperation | BroadOperation;
|
|
602
|
+
/**
|
|
603
|
+
* payload delivered to event listeners
|
|
604
|
+
* @category Events
|
|
605
|
+
*/
|
|
606
|
+
interface TableEventPayload<
|
|
607
|
+
T = unknown,
|
|
608
|
+
Op extends TableEventOperation = TableEventOperation
|
|
609
|
+
> {
|
|
610
|
+
table: string;
|
|
611
|
+
operation: Op;
|
|
612
|
+
data?: T | T[] | Partial<T> | Record<string, unknown>;
|
|
613
|
+
result?: T | T[] | PageResult<T> | number | null;
|
|
614
|
+
id?: unknown;
|
|
615
|
+
where?: unknown;
|
|
616
|
+
options?: unknown;
|
|
617
|
+
timestamp: number;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* how to handle errors
|
|
621
|
+
* @category Errors
|
|
622
|
+
*/
|
|
623
|
+
type ErrorPolicy = "throw" | "emit" | "emit-swallow" | "crash";
|
|
624
|
+
/**
|
|
625
|
+
* when to delete db files on shutdown
|
|
626
|
+
* @category Setup
|
|
627
|
+
*/
|
|
628
|
+
type UnlinkPolicy = true | "onlyGraceful" | "any" | false | undefined;
|
|
629
|
+
import { TObject as TObject4 } from "typebox";
|
|
630
|
+
/** @category Events */
|
|
631
|
+
type Listener = (payload: unknown) => void;
|
|
632
|
+
/**
|
|
633
|
+
* lifecycle events emitted by the orm
|
|
634
|
+
* @category Events
|
|
635
|
+
*/
|
|
636
|
+
interface LifecycleEventMap {
|
|
637
|
+
start: {
|
|
638
|
+
phase: "start";
|
|
639
|
+
timestamp: number;
|
|
640
|
+
};
|
|
641
|
+
ready: {
|
|
642
|
+
phase: "ready";
|
|
643
|
+
timestamp: number;
|
|
644
|
+
};
|
|
645
|
+
shutdown: {
|
|
646
|
+
phase: "shutdown";
|
|
647
|
+
timestamp: number;
|
|
648
|
+
};
|
|
649
|
+
exit: {
|
|
650
|
+
phase: "exit";
|
|
651
|
+
timestamp: number;
|
|
652
|
+
};
|
|
653
|
+
fail: {
|
|
654
|
+
phase: "fail";
|
|
655
|
+
error: Error;
|
|
656
|
+
timestamp: number;
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* typed event listener api
|
|
661
|
+
* @category Events
|
|
662
|
+
*/
|
|
663
|
+
interface ORMEvents<Tables extends Record<string, TableConfig<any, any, any>>> {
|
|
664
|
+
/** listen to lifecycle events (start, ready, shutdown, exit, fail) */
|
|
665
|
+
on<K extends (keyof LifecycleEventMap) & string>(event: K, listener: (payload: LifecycleEventMap[K]) => void): () => void;
|
|
666
|
+
/** listen to table events (insert, update, findMany, etc) */
|
|
667
|
+
on<
|
|
668
|
+
Table extends keyof Tables & string,
|
|
669
|
+
Op extends TableOperation | BroadOperation
|
|
670
|
+
>(table: Table, operation: Op, listener: (payload: TableEventPayload<Tables[Table] extends TableConfig<infer S> ? Infer<S> : never, Op extends TableOperation ? Op : BroadOperation>) => void): () => void;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* @internal
|
|
674
|
+
* @category Events
|
|
675
|
+
*/
|
|
676
|
+
declare class EventBus {
|
|
677
|
+
private readonly listeners;
|
|
678
|
+
private readonly tableActive;
|
|
679
|
+
has(event: string): boolean;
|
|
680
|
+
hasAny(tableName: string): boolean;
|
|
681
|
+
on(event: string, fn: Listener): () => void;
|
|
682
|
+
emit(event: string, payload: unknown): void;
|
|
683
|
+
emitIf(event: string, payload: unknown): void;
|
|
684
|
+
}
|
|
685
|
+
import { TObject as TObject3 } from "typebox";
|
|
686
|
+
/** @category Advanced */
|
|
687
|
+
type SqliteType = "TEXT" | "INTEGER" | "REAL" | "BLOB";
|
|
688
|
+
/** @category Advanced */
|
|
689
|
+
interface ColumnMeta {
|
|
690
|
+
name: string;
|
|
691
|
+
sqlType: SqliteType;
|
|
692
|
+
nullable: boolean;
|
|
693
|
+
/** True if this is actually an `Optional` wrapper */
|
|
694
|
+
optional: boolean;
|
|
695
|
+
}
|
|
696
|
+
/** @category Advanced */
|
|
697
|
+
interface SubTableMeta {
|
|
698
|
+
/** e.g. "lineItems" */
|
|
699
|
+
fieldName: string;
|
|
700
|
+
/** e.g. "sales__lineItems" */
|
|
701
|
+
tableName: string;
|
|
702
|
+
/** Schema of a single item in the array */
|
|
703
|
+
itemSchema: TObject3;
|
|
704
|
+
/** Columns of the sub-table row (owner PK is prepended automatically) */
|
|
705
|
+
columns: ColumnMeta[];
|
|
706
|
+
}
|
|
707
|
+
/** @category Advanced */
|
|
708
|
+
interface TableMeta {
|
|
709
|
+
tableName: string;
|
|
710
|
+
columns: ColumnMeta[];
|
|
711
|
+
subTables: SubTableMeta[];
|
|
712
|
+
}
|
|
713
|
+
/** @category Advanced */
|
|
714
|
+
declare function introspectTable(tableName: string, schema: TObject3): TableMeta;
|
|
715
|
+
/** @category Advanced */
|
|
716
|
+
declare function buildCreateTableSQL(meta: TableMeta, primaryKey: string): string[];
|
|
717
|
+
/**
|
|
718
|
+
* Typed repository for a single table. Every entry in your `tables` config
|
|
719
|
+
* becomes one of these on the ORM object, fully typed to its schema.
|
|
720
|
+
*
|
|
721
|
+
* @category Repositories
|
|
722
|
+
*
|
|
723
|
+
* @example
|
|
724
|
+
* ```ts
|
|
725
|
+
* const orm = createORM({
|
|
726
|
+
* tables: {
|
|
727
|
+
* users: table(UserSchema, (s) => ({ primaryKey: s.id })),
|
|
728
|
+
* },
|
|
729
|
+
* });
|
|
730
|
+
*
|
|
731
|
+
* // All methods are fully typed - wrong property names are caught at compile time
|
|
732
|
+
* orm.users.insert({ id: "u1", name: "alice", email: "a@x.com" });
|
|
733
|
+
* const user = orm.users.findById("u1");
|
|
734
|
+
* orm.users.update({ id: "u1", name: "alice smith" });
|
|
735
|
+
* orm.users.deleteById("u1");
|
|
736
|
+
* ```
|
|
737
|
+
*/
|
|
738
|
+
declare class Repository<
|
|
739
|
+
T extends TObject4,
|
|
740
|
+
PK extends ScalarKeys<T>,
|
|
741
|
+
Mat = never,
|
|
742
|
+
TS = {}
|
|
743
|
+
> {
|
|
744
|
+
readonly tableName: string;
|
|
745
|
+
/** table metadata - columns, sub-tables, indexes */
|
|
746
|
+
readonly meta: TableMeta;
|
|
747
|
+
private readonly validator;
|
|
748
|
+
private readonly db;
|
|
749
|
+
private readonly descriptor;
|
|
750
|
+
private _entityProto;
|
|
751
|
+
private readonly _timestampNames;
|
|
752
|
+
private _materialize?;
|
|
753
|
+
private _materializeMany?;
|
|
754
|
+
private _events?;
|
|
755
|
+
/** @internal */
|
|
756
|
+
setEventBus(bus: EventBus): void;
|
|
757
|
+
constructor(tableName: string, config: TableConfig<T, PK>, db: BunDatabase);
|
|
758
|
+
/** Inject materializers after ORM two-pass init */
|
|
759
|
+
setMaterializer(single: (record: Record<string, unknown>) => Record<string, unknown>, many: (records: Record<string, unknown>[]) => Record<string, unknown>[]): void;
|
|
760
|
+
/** Wrap raw data in an entity object */
|
|
761
|
+
private _wrap;
|
|
762
|
+
/** Narrow a parsed schema value to a plain record for dynamic property access */
|
|
763
|
+
private _record;
|
|
764
|
+
private _emit;
|
|
765
|
+
private _migrate;
|
|
766
|
+
/**
|
|
767
|
+
* Validate and coerce data against the schema. Throws on invalid input.
|
|
768
|
+
*
|
|
769
|
+
* @group Validation
|
|
770
|
+
*
|
|
771
|
+
* @example
|
|
772
|
+
* ```ts
|
|
773
|
+
* const user = orm.users.parse({ id: "u1", name: "alice" });
|
|
774
|
+
* // user is typed as Infer<typeof UserSchema>
|
|
775
|
+
* ```
|
|
776
|
+
*/
|
|
777
|
+
parse(data: unknown): Infer<T>;
|
|
778
|
+
/**
|
|
779
|
+
* Type-guard - returns true if data matches the schema.
|
|
780
|
+
*
|
|
781
|
+
* @group Validation
|
|
782
|
+
*
|
|
783
|
+
* @example
|
|
784
|
+
* ```ts
|
|
785
|
+
* if (orm.users.check(someData)) {
|
|
786
|
+
* // someData is now typed as Infer<typeof UserSchema>
|
|
787
|
+
* }
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
790
|
+
check(data: unknown): data is Infer<T>;
|
|
791
|
+
/**
|
|
792
|
+
* Insert a single record. Returns the inserted entity.
|
|
793
|
+
*
|
|
794
|
+
* @group Writing
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* const user = orm.users.insert({
|
|
799
|
+
* id: "u1",
|
|
800
|
+
* name: "alice",
|
|
801
|
+
* email: "alice@example.com",
|
|
802
|
+
* });
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
insert(data: InsertData<T>): Entity<Infer<T>, Mat, TS>;
|
|
806
|
+
/**
|
|
807
|
+
* Insert many records in a single transaction.
|
|
808
|
+
*
|
|
809
|
+
* @group Writing
|
|
810
|
+
*
|
|
811
|
+
* @example
|
|
812
|
+
* ```ts
|
|
813
|
+
* orm.users.insertMany([
|
|
814
|
+
* { id: "u1", name: "alice", email: "a@x.com" },
|
|
815
|
+
* { id: "u2", name: "bob", email: "b@x.com" },
|
|
816
|
+
* ]);
|
|
817
|
+
* ```
|
|
818
|
+
*/
|
|
819
|
+
insertMany(records: InsertData<T>[]): Entity<Infer<T>, Mat, TS>[];
|
|
820
|
+
private _insertParsed;
|
|
821
|
+
/**
|
|
822
|
+
* Insert or update on conflict. If the record exists (by conflict target),
|
|
823
|
+
* it updates the specified columns instead.
|
|
824
|
+
*
|
|
825
|
+
* @group Writing
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* ```ts
|
|
829
|
+
* orm.users.upsert({
|
|
830
|
+
* data: { id: "u1", name: "alice", email: "new@x.com" },
|
|
831
|
+
* conflictTarget: "id",
|
|
832
|
+
* });
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
upsert(opts: UpsertOptions<T, PK>): Entity<Infer<T>, Mat, TS>;
|
|
836
|
+
/**
|
|
837
|
+
* Find a record by its primary key.
|
|
838
|
+
*
|
|
839
|
+
* @group Reading
|
|
840
|
+
*
|
|
841
|
+
* @example
|
|
842
|
+
* ```ts
|
|
843
|
+
* const user = orm.users.findById("u1");
|
|
844
|
+
* if (user) console.log(user.name);
|
|
845
|
+
* ```
|
|
846
|
+
*/
|
|
847
|
+
findById(id: Infer<T>[PK]): Entity<Infer<T>, Mat, TS> | null;
|
|
848
|
+
/** Internal findById without event emission - used by update() */
|
|
849
|
+
private _findByIdRaw;
|
|
850
|
+
/**
|
|
851
|
+
* Find many records matching the given filters.
|
|
852
|
+
*
|
|
853
|
+
* @group Reading
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* const adults = orm.users.findMany({
|
|
858
|
+
* where: { age: { gte: 18 } },
|
|
859
|
+
* orderBy: { column: "name", direction: "ASC" },
|
|
860
|
+
* limit: 10,
|
|
861
|
+
* });
|
|
862
|
+
*
|
|
863
|
+
* // Include sub-tables
|
|
864
|
+
* const orders = orm.orders.findMany({ include: ["lineItems"] });
|
|
865
|
+
* ```
|
|
866
|
+
*/
|
|
867
|
+
findMany(opts?: FindOptions<T>): Entity<Infer<T>, Mat, TS>[];
|
|
868
|
+
/**
|
|
869
|
+
* Find many with total count - useful for pagination.
|
|
870
|
+
*
|
|
871
|
+
* @group Reading
|
|
872
|
+
*
|
|
873
|
+
* @example
|
|
874
|
+
* ```ts
|
|
875
|
+
* const page = orm.users.findPage({
|
|
876
|
+
* where: { status: { eq: "active" } },
|
|
877
|
+
* limit: 10,
|
|
878
|
+
* offset: 0,
|
|
879
|
+
* });
|
|
880
|
+
* // page.data - the records
|
|
881
|
+
* // page.total - total matching records
|
|
882
|
+
* // page.limit, page.offset - what you passed in
|
|
883
|
+
* ```
|
|
884
|
+
*/
|
|
885
|
+
findPage(opts?: FindOptions<T>): PageResult<Entity<Infer<T>, Mat, TS>>;
|
|
886
|
+
/**
|
|
887
|
+
* Find a single record matching the given filters. Equivalent to `findMany`
|
|
888
|
+
* with `limit: 1`, but returns the entity directly (or `null`).
|
|
889
|
+
*
|
|
890
|
+
* @group Reading
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* ```ts
|
|
894
|
+
* const admin = orm.users.findOne({
|
|
895
|
+
* where: { role: { eq: "admin" } },
|
|
896
|
+
* });
|
|
897
|
+
* ```
|
|
898
|
+
*/
|
|
899
|
+
findOne(opts?: FindOptions<T>): Entity<Infer<T>, Mat, TS> | null;
|
|
900
|
+
/**
|
|
901
|
+
* Find many with resolved relations (n+1 safe). If you have cross-table
|
|
902
|
+
* relations configured, this eagerly loads them in a single batch query.
|
|
903
|
+
*
|
|
904
|
+
* @group Reading
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```ts
|
|
908
|
+
* const orders = orm.orders.findManyMaterialized();
|
|
909
|
+
* for (const item of orders[0].lineItems) {
|
|
910
|
+
* console.log(item.product.name); // eagerly resolved
|
|
911
|
+
* }
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
findManyMaterialized(opts?: FindOptions<T>): Entity<Infer<T>, Mat, TS>[];
|
|
915
|
+
/**
|
|
916
|
+
* Count records matching the given filters.
|
|
917
|
+
*
|
|
918
|
+
* @group Reading
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```ts
|
|
922
|
+
* const total = orm.users.count();
|
|
923
|
+
* const adults = orm.users.count({ age: { gte: 18 } });
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
count(where?: WhereClause<T>): number;
|
|
927
|
+
/**
|
|
928
|
+
* Update a record - must include the primary key. Returns the updated
|
|
929
|
+
* entity, or `null` if no record matched.
|
|
930
|
+
*
|
|
931
|
+
* @group Writing
|
|
932
|
+
*
|
|
933
|
+
* @example
|
|
934
|
+
* ```ts
|
|
935
|
+
* orm.users.update({ id: "u1", name: "alice smith" });
|
|
936
|
+
* ```
|
|
937
|
+
*/
|
|
938
|
+
update(data: UpdateData<T, PK>): Entity<Infer<T>, Mat, TS> | null;
|
|
939
|
+
/**
|
|
940
|
+
* Delete a record by its primary key. Returns `true` if a record was deleted.
|
|
941
|
+
*
|
|
942
|
+
* @group Writing
|
|
943
|
+
*
|
|
944
|
+
* @example
|
|
945
|
+
* ```ts
|
|
946
|
+
* const deleted = orm.users.deleteById("u1");
|
|
947
|
+
* ```
|
|
948
|
+
*/
|
|
949
|
+
deleteById(id: Infer<T>[PK]): boolean;
|
|
950
|
+
/**
|
|
951
|
+
* Delete records matching the given filters. Returns the number of rows deleted.
|
|
952
|
+
*
|
|
953
|
+
* @group Writing
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* ```ts
|
|
957
|
+
* const removed = orm.users.deleteWhere({ status: { eq: "banned" } });
|
|
958
|
+
* ```
|
|
959
|
+
*/
|
|
960
|
+
deleteWhere(where: WhereClause<T>): number;
|
|
961
|
+
/**
|
|
962
|
+
* Truncate the table and all sub-tables. Deletes all rows but keeps the schema.
|
|
963
|
+
*
|
|
964
|
+
* @group Lifecycle
|
|
965
|
+
*
|
|
966
|
+
* @example
|
|
967
|
+
* ```ts
|
|
968
|
+
* orm.users.flush(); // users table is now empty
|
|
969
|
+
* ```
|
|
970
|
+
*/
|
|
971
|
+
flush(): void;
|
|
972
|
+
/**
|
|
973
|
+
* Drop the table and all sub-tables. **This destroys the schema and all data.**
|
|
974
|
+
*
|
|
975
|
+
* @group Lifecycle
|
|
976
|
+
*
|
|
977
|
+
* @example
|
|
978
|
+
* ```ts
|
|
979
|
+
* orm.users.drop(); // table no longer exists
|
|
980
|
+
* ```
|
|
981
|
+
*/
|
|
982
|
+
drop(): void;
|
|
983
|
+
private _hydrateOne;
|
|
984
|
+
/**
|
|
985
|
+
* Run raw SQL - escape hatch for queries the ORM doesn't support directly.
|
|
986
|
+
*
|
|
987
|
+
* @group Raw SQL
|
|
988
|
+
*
|
|
989
|
+
* @example
|
|
990
|
+
* ```ts
|
|
991
|
+
* const rows = orm.users.raw<{ name: string; count: number }>(
|
|
992
|
+
* 'SELECT name, COUNT(*) as count FROM users GROUP BY name'
|
|
993
|
+
* );
|
|
994
|
+
* ```
|
|
995
|
+
*/
|
|
996
|
+
raw<R = unknown>(sql: string, ...params: unknown[]): R[];
|
|
997
|
+
}
|
|
998
|
+
declare class RelationBuilder<Tables extends Record<string, TableConfig<any, any, any>>> {
|
|
999
|
+
private readonly tables;
|
|
1000
|
+
private readonly relations;
|
|
1001
|
+
constructor(tables: Tables);
|
|
1002
|
+
from<Owner extends keyof Tables & string>(ownerTable: Owner): {
|
|
1003
|
+
scalar: <const Field extends ScalarKeys<Tables[Owner]["schema"]>>(field: Field) => {
|
|
1004
|
+
to: <
|
|
1005
|
+
Target extends keyof Tables & string,
|
|
1006
|
+
TField extends ScalarKeys<Tables[Target]["schema"]>,
|
|
1007
|
+
const As extends string | undefined = undefined
|
|
1008
|
+
>(targetTable: Target, targetField: TField, opts?: {
|
|
1009
|
+
as?: As;
|
|
1010
|
+
}) => TypedRelation<Owner, Field, Target, TField, "scalar", As>;
|
|
1011
|
+
};
|
|
1012
|
+
subTable: <
|
|
1013
|
+
const Sub extends string,
|
|
1014
|
+
const Col extends string
|
|
1015
|
+
>(subField: Sub, subColumn: Col) => {
|
|
1016
|
+
to: <
|
|
1017
|
+
Target extends keyof Tables & string,
|
|
1018
|
+
TField extends ScalarKeys<Tables[Target]["schema"]>,
|
|
1019
|
+
const As extends string | undefined = undefined
|
|
1020
|
+
>(targetTable: Target, targetField: TField, opts?: {
|
|
1021
|
+
as?: As;
|
|
1022
|
+
}) => TypedRelation<Owner, `${Sub}.${Col}`, Target, TField, "subTable", As>;
|
|
1023
|
+
};
|
|
1024
|
+
};
|
|
1025
|
+
build(): TypedRelation[];
|
|
1026
|
+
}
|
|
1027
|
+
/** @category Relations */
|
|
1028
|
+
declare function createRelationBuilder<T extends Record<string, TableConfig<any, any, any>>>(tables: T): RelationBuilder<T>;
|
|
1029
|
+
import { Static as Static2 } from "typebox";
|
|
1030
|
+
declare const SchemaHashSchema: unknown;
|
|
1031
|
+
/** @category Database */
|
|
1032
|
+
declare const SchemaJSONSchema: unknown;
|
|
1033
|
+
declare const TablesSchema: unknown;
|
|
1034
|
+
declare const RelationsSchema: unknown;
|
|
1035
|
+
declare const VersionSchema: unknown;
|
|
1036
|
+
type SchemaHash = Static2<typeof SchemaHashSchema>;
|
|
1037
|
+
/** @category Database */
|
|
1038
|
+
type SchemaJSON = Static2<typeof SchemaJSONSchema>;
|
|
1039
|
+
type TablesList = Static2<typeof TablesSchema>;
|
|
1040
|
+
type RelationsList = Static2<typeof RelationsSchema>;
|
|
1041
|
+
type Version = Static2<typeof VersionSchema>;
|
|
1042
|
+
/** @category Database */
|
|
1043
|
+
declare class MetaStore {
|
|
1044
|
+
private readonly db;
|
|
1045
|
+
private static readonly KEY_SCHEMA_HASH;
|
|
1046
|
+
private static readonly KEY_SCHEMA_JSON;
|
|
1047
|
+
private static readonly KEY_TABLES;
|
|
1048
|
+
private static readonly KEY_RELATIONS;
|
|
1049
|
+
private static readonly KEY_VERSION;
|
|
1050
|
+
constructor(db: BunDatabase);
|
|
1051
|
+
private getRow;
|
|
1052
|
+
private setRow;
|
|
1053
|
+
private getValidatedJSON;
|
|
1054
|
+
private setValidatedJSON;
|
|
1055
|
+
getString(key: string): string | null;
|
|
1056
|
+
setString(key: string, value: string): void;
|
|
1057
|
+
getJSON<T>(key: string): T | null;
|
|
1058
|
+
setJSON(key: string, value: unknown): void;
|
|
1059
|
+
getCompressed(key: string): Uint8Array | null;
|
|
1060
|
+
setCompressed(key: string, data: Uint8Array): void;
|
|
1061
|
+
delete(key: string): void;
|
|
1062
|
+
getSchemaHash(): SchemaHash | null;
|
|
1063
|
+
setSchemaHash(value: SchemaHash): void;
|
|
1064
|
+
getSchemaJSON(): SchemaJSON | null;
|
|
1065
|
+
setSchemaJSON(value: SchemaJSON): void;
|
|
1066
|
+
getTables(): TablesList | null;
|
|
1067
|
+
setTables(value: TablesList): void;
|
|
1068
|
+
getRelations(): RelationsList | null;
|
|
1069
|
+
setRelations(value: RelationsList): void;
|
|
1070
|
+
getVersion(): Version | null;
|
|
1071
|
+
setVersion(value: Version): void;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* context passed to lifecycle hooks
|
|
1075
|
+
* @category Lifecycle
|
|
1076
|
+
*/
|
|
1077
|
+
interface ORMContext<
|
|
1078
|
+
T extends Record<string, TableConfig<any, any, any>> = Record<string, TableConfig<any, any, any>>,
|
|
1079
|
+
Rels extends readonly TypedRelation[] = readonly TypedRelation[]
|
|
1080
|
+
> {
|
|
1081
|
+
orm: foxdb<T, Rels>;
|
|
1082
|
+
db: BunDatabase;
|
|
1083
|
+
meta: MetaStore;
|
|
1084
|
+
tables: string[];
|
|
1085
|
+
repos: Map<string, unknown>;
|
|
1086
|
+
logger: {
|
|
1087
|
+
log: (...args: unknown[]) => void;
|
|
1088
|
+
error: (...args: unknown[]) => void;
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* lifecycle hook signature
|
|
1093
|
+
* @category Lifecycle
|
|
1094
|
+
*/
|
|
1095
|
+
type LifecycleHook<
|
|
1096
|
+
T extends Record<string, TableConfig<any, any, any>> = Record<string, TableConfig<any, any, any>>,
|
|
1097
|
+
Rels extends readonly TypedRelation[] = readonly TypedRelation[]
|
|
1098
|
+
> = (ctx: ORMContext<T, Rels>) => void | Promise<void>;
|
|
1099
|
+
/**
|
|
1100
|
+
* manages startup and shutdown hook execution
|
|
1101
|
+
* @category Lifecycle
|
|
1102
|
+
*/
|
|
1103
|
+
declare class LifecycleManager<
|
|
1104
|
+
T extends Record<string, TableConfig<any, any, any>> = Record<string, TableConfig<any, any, any>>,
|
|
1105
|
+
Rels extends readonly TypedRelation[] = readonly TypedRelation[]
|
|
1106
|
+
> {
|
|
1107
|
+
private startHooks;
|
|
1108
|
+
private readyHooks;
|
|
1109
|
+
private shutdownHooks;
|
|
1110
|
+
private exitHooks;
|
|
1111
|
+
private _ready;
|
|
1112
|
+
private _shutdown;
|
|
1113
|
+
/** true after runReady completes */
|
|
1114
|
+
get isReady();
|
|
1115
|
+
/** true after runShutdown completes */
|
|
1116
|
+
get isShutdown();
|
|
1117
|
+
/** register a startup hook */
|
|
1118
|
+
onStart(hook: LifecycleHook<T, Rels>);
|
|
1119
|
+
/** register a ready hook */
|
|
1120
|
+
onReady(hook: LifecycleHook<T, Rels>);
|
|
1121
|
+
/** register a shutdown hook */
|
|
1122
|
+
onShutdown(hook: LifecycleHook<T, Rels>);
|
|
1123
|
+
/** register an exit hook */
|
|
1124
|
+
onExit(hook: LifecycleHook<T, Rels>);
|
|
1125
|
+
/** @internal */
|
|
1126
|
+
runStart(ctx: ORMContext<T, Rels>): void;
|
|
1127
|
+
/** @internal */
|
|
1128
|
+
runReady(ctx: ORMContext<T, Rels>): void;
|
|
1129
|
+
/** @internal */
|
|
1130
|
+
runShutdown(ctx: ORMContext<T, Rels>): void;
|
|
1131
|
+
/** @internal */
|
|
1132
|
+
runExit(ctx: ORMContext<T, Rels>): void;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Base options for opening a SQLite database.
|
|
1136
|
+
* @category Setup
|
|
1137
|
+
*/
|
|
1138
|
+
interface CreateORMBaseOptions {
|
|
1139
|
+
/** file path - defaults to ":memory:" */
|
|
1140
|
+
path?: string;
|
|
1141
|
+
cacheSize?: number;
|
|
1142
|
+
busyTimeout?: number;
|
|
1143
|
+
synchronous?: "OFF" | "NORMAL" | "FULL" | "EXTRA";
|
|
1144
|
+
mmapSize?: number;
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Options passed to `createORM`.
|
|
1148
|
+
*
|
|
1149
|
+
* @category Setup
|
|
1150
|
+
*
|
|
1151
|
+
* @example
|
|
1152
|
+
* ```ts
|
|
1153
|
+
* const orm = createORM({
|
|
1154
|
+
* path: "app.db",
|
|
1155
|
+
* tables: {
|
|
1156
|
+
* users: table(UserSchema, (s) => ({
|
|
1157
|
+
* primaryKey: s.id,
|
|
1158
|
+
* indexes: [{ columns: [s.email] }],
|
|
1159
|
+
* timestamps: true,
|
|
1160
|
+
* })),
|
|
1161
|
+
* },
|
|
1162
|
+
* relations: (r) => [
|
|
1163
|
+
* r.from("users").scalar("profileId").to("profiles", "id"),
|
|
1164
|
+
* ],
|
|
1165
|
+
* seed: (o) => {
|
|
1166
|
+
* o.users.insert({ id: "1", name: "alice" });
|
|
1167
|
+
* },
|
|
1168
|
+
* onReady: () => console.log("db ready"),
|
|
1169
|
+
* autoMigrate: true,
|
|
1170
|
+
* migrations: { dir: "./migrations" },
|
|
1171
|
+
* });
|
|
1172
|
+
* ```
|
|
1173
|
+
*/
|
|
1174
|
+
interface CreateORMOptions<
|
|
1175
|
+
T extends Record<string, TableConfig<any, any, any>> = Record<string, TableConfig<any, any, any>>,
|
|
1176
|
+
Rels extends readonly TypedRelation[] = readonly TypedRelation[]
|
|
1177
|
+
> extends CreateORMBaseOptions {
|
|
1178
|
+
/** table schemas */
|
|
1179
|
+
tables: T;
|
|
1180
|
+
/** cross-table relations */
|
|
1181
|
+
relations?: RelationsConfig<any> | ((builder: RelationBuilder<T>) => Rels);
|
|
1182
|
+
/** how to handle schema drift */
|
|
1183
|
+
sync?: SyncPolicy;
|
|
1184
|
+
/** migration directory */
|
|
1185
|
+
migrations?: {
|
|
1186
|
+
dir: string;
|
|
1187
|
+
};
|
|
1188
|
+
/** runs before schema validation */
|
|
1189
|
+
onStart?: LifecycleHook<T, Rels>;
|
|
1190
|
+
/** runs after the db is fully ready */
|
|
1191
|
+
onReady?: LifecycleHook<T, Rels>;
|
|
1192
|
+
/** runs before closing the db */
|
|
1193
|
+
onShutdown?: LifecycleHook<T, Rels>;
|
|
1194
|
+
/** runs after the db is closed */
|
|
1195
|
+
onExit?: LifecycleHook<T, Rels>;
|
|
1196
|
+
/** run pending migrations on startup */
|
|
1197
|
+
autoMigrate?: boolean;
|
|
1198
|
+
/** seed data after sync */
|
|
1199
|
+
seed?: (orm: foxdb<T, Rels>) => void;
|
|
1200
|
+
/** wipe and recreate the db on every launch */
|
|
1201
|
+
rebuildOnLaunch?: boolean;
|
|
1202
|
+
/** flush these tables before seed */
|
|
1203
|
+
flushOnStart?: Array<keyof T & string>;
|
|
1204
|
+
/** flush these tables before close */
|
|
1205
|
+
flushOnExit?: Array<keyof T & string>;
|
|
1206
|
+
/** drop these tables before seed */
|
|
1207
|
+
dropOnStart?: Array<keyof T & string>;
|
|
1208
|
+
/** drop these tables before close */
|
|
1209
|
+
dropOnExit?: Array<keyof T & string>;
|
|
1210
|
+
/** wipe metadata on startup */
|
|
1211
|
+
flushMetaOnStart?: boolean;
|
|
1212
|
+
/** wipe metadata on exit */
|
|
1213
|
+
flushMetaOnExit?: boolean;
|
|
1214
|
+
/** how to handle errors */
|
|
1215
|
+
errorPolicy?: ErrorPolicy;
|
|
1216
|
+
/** delete db files on exit */
|
|
1217
|
+
unlinkDbFilesOnExit?: UnlinkPolicy;
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* The ORM object returned by `createORM`.
|
|
1221
|
+
*
|
|
1222
|
+
* Every key in your `tables` config becomes a fully typed **Repository**
|
|
1223
|
+
* with CRUD methods. The type definition below is a mapped type that
|
|
1224
|
+
* transforms your schema config into the actual runtime API — TypeDoc
|
|
1225
|
+
* cannot expand it, so see the example for how it works in practice.
|
|
1226
|
+
*
|
|
1227
|
+
* @see {@link Repository} for the full method reference
|
|
1228
|
+
*
|
|
1229
|
+
* @category Setup
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* ```ts
|
|
1233
|
+
* const orm = createORM({
|
|
1234
|
+
* tables: {
|
|
1235
|
+
* users: table(UserSchema, (s) => ({ primaryKey: s.id })),
|
|
1236
|
+
* },
|
|
1237
|
+
* });
|
|
1238
|
+
*
|
|
1239
|
+
* // ─── Every table key is a Repository ───
|
|
1240
|
+
*
|
|
1241
|
+
* // Insert
|
|
1242
|
+
* orm.users.insert({ id: "u1", name: "alice" });
|
|
1243
|
+
* orm.users.insertMany([{ id: "u2", name: "bob" }]);
|
|
1244
|
+
*
|
|
1245
|
+
* // Query
|
|
1246
|
+
* const user = orm.users.findById("u1");
|
|
1247
|
+
* const all = orm.users.findMany();
|
|
1248
|
+
* const page = orm.users.findPage({ limit: 10, offset: 0 });
|
|
1249
|
+
* const one = orm.users.findOne({ where: { name: { eq: "alice" } } });
|
|
1250
|
+
* const count = orm.users.count({ age: { gte: 18 } });
|
|
1251
|
+
*
|
|
1252
|
+
* // Update & delete
|
|
1253
|
+
* orm.users.update({ id: "u1", name: "alice smith" });
|
|
1254
|
+
* orm.users.upsert({ data: { id: "u1", name: "alice" }, conflictTarget: "id" });
|
|
1255
|
+
* orm.users.deleteById("u1");
|
|
1256
|
+
* orm.users.deleteWhere({ status: { eq: "banned" } });
|
|
1257
|
+
*
|
|
1258
|
+
* // ─── Framework methods (prefixed with _) ───
|
|
1259
|
+
*
|
|
1260
|
+
* orm._transaction(() => {
|
|
1261
|
+
* orm.users.insert({ id: "t1", name: "txn" });
|
|
1262
|
+
* orm.users.deleteById("old");
|
|
1263
|
+
* });
|
|
1264
|
+
*
|
|
1265
|
+
* orm._close();
|
|
1266
|
+
* ```
|
|
1267
|
+
*/
|
|
1268
|
+
type foxdb<
|
|
1269
|
+
Tables extends Record<string, TableConfig<any, any, any>>,
|
|
1270
|
+
Rels extends readonly TypedRelation[] = readonly TypedRelation[]
|
|
1271
|
+
> = { [K in keyof Tables] : Tables[K] extends TableConfig<infer T, infer PK, infer TS> ? Repository<T, PK extends ScalarKeys<T> ? PK : never, Materialized<T, Tables, Rels, K & string>, TimestampShape<TS>> : never } & {
|
|
1272
|
+
/** run a transaction */
|
|
1273
|
+
_transaction<R>(fn: () => R): R;
|
|
1274
|
+
/** close the database */
|
|
1275
|
+
_close(): void;
|
|
1276
|
+
/** read-only metadata */
|
|
1277
|
+
_meta: MetaAccessors;
|
|
1278
|
+
/** resolve relations for a single record */
|
|
1279
|
+
_materialize<Owner extends keyof Tables & string>(ownerTable: Owner, record: Record<string, unknown>): Record<string, unknown>;
|
|
1280
|
+
/** resolve relations for many records (n+1 safe) */
|
|
1281
|
+
_materializeMany<Owner extends keyof Tables & string>(ownerTable: Owner, records: Record<string, unknown>[]): Array<Record<string, unknown>>;
|
|
1282
|
+
/** truncate all tables */
|
|
1283
|
+
_flush(opts?: {
|
|
1284
|
+
includeMeta?: boolean;
|
|
1285
|
+
}): void;
|
|
1286
|
+
/** run pending migrations */
|
|
1287
|
+
_migrate(): Promise<void>;
|
|
1288
|
+
/** listen to table or lifecycle events */
|
|
1289
|
+
_events: ORMEvents<Tables>;
|
|
1290
|
+
};
|
|
1291
|
+
/** create a typed orm instance backed by sqlite */
|
|
1292
|
+
declare function createORM<
|
|
1293
|
+
const T extends Record<string, TableConfig<any, any, any>>,
|
|
1294
|
+
const Rels extends readonly TypedRelation[] = readonly TypedRelation[]
|
|
1295
|
+
>(opts: CreateORMOptions<T, Rels>): foxdb<T, Rels>;
|
|
1296
|
+
import { TObject as TObject5 } from "typebox";
|
|
1297
|
+
/** @category Schema */
|
|
1298
|
+
interface SubTableConfig {
|
|
1299
|
+
indexes?: IndexDefinition[];
|
|
1300
|
+
}
|
|
1301
|
+
/** @category Schema */
|
|
1302
|
+
interface TableDescriptor<
|
|
1303
|
+
T extends TObject5,
|
|
1304
|
+
PK extends string,
|
|
1305
|
+
TS extends TimestampConfig = undefined
|
|
1306
|
+
> extends TableConfig<T, PK, TS> {}
|
|
1307
|
+
/** @category Schema */
|
|
1308
|
+
interface TableConfigShape<
|
|
1309
|
+
PK extends string,
|
|
1310
|
+
TS extends TimestampConfig
|
|
1311
|
+
> {
|
|
1312
|
+
primaryKey: ColumnRef<PK>;
|
|
1313
|
+
indexes?: IndexDefinition[];
|
|
1314
|
+
subTables?: Partial<Record<string, SubTableConfig>>;
|
|
1315
|
+
timestamps?: TS;
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Describe a table schema + config for `createORM`.
|
|
1319
|
+
*
|
|
1320
|
+
* @example
|
|
1321
|
+
* ```ts
|
|
1322
|
+
* const UserSchema = Object({ id: String(), name: String(), email: String() });
|
|
1323
|
+
*
|
|
1324
|
+
* const users = table(UserSchema, (s) => ({
|
|
1325
|
+
* primaryKey: s.id,
|
|
1326
|
+
* indexes: [{ columns: [s.email], unique: true }],
|
|
1327
|
+
* timestamps: true, // adds createdAt / updatedAt
|
|
1328
|
+
* }));
|
|
1329
|
+
* ```
|
|
1330
|
+
* @category Schema
|
|
1331
|
+
* @category Schema
|
|
1332
|
+
*/
|
|
1333
|
+
declare function table<
|
|
1334
|
+
T extends TObject5,
|
|
1335
|
+
PK extends string,
|
|
1336
|
+
const TS extends boolean
|
|
1337
|
+
>(schema: T, configure: (columns: ColumnRefs<T>) => TableConfigShape<PK, TS> & {
|
|
1338
|
+
timestamps: TS;
|
|
1339
|
+
}): TableDescriptor<T, PK, TS>;
|
|
1340
|
+
/** @category Schema */
|
|
1341
|
+
declare function table<
|
|
1342
|
+
T extends TObject5,
|
|
1343
|
+
PK extends string,
|
|
1344
|
+
const TS extends {
|
|
1345
|
+
createdAt?: string;
|
|
1346
|
+
updatedAt?: string;
|
|
1347
|
+
}
|
|
1348
|
+
>(schema: T, configure: (columns: ColumnRefs<T>) => TableConfigShape<PK, TS> & {
|
|
1349
|
+
timestamps: TS;
|
|
1350
|
+
}): TableDescriptor<T, PK, TS>;
|
|
1351
|
+
/** @category Schema */
|
|
1352
|
+
declare function table<
|
|
1353
|
+
T extends TObject5,
|
|
1354
|
+
PK extends string
|
|
1355
|
+
>(schema: T, configure: (columns: ColumnRefs<T>) => TableConfigShape<PK, undefined>): TableDescriptor<T, PK, undefined>;
|
|
1356
|
+
declare function migrate(opts: MigrateOptions): Promise<void>;
|
|
1357
|
+
/** @category Migration */
|
|
1358
|
+
declare function createMigration(name: string, migrationsDir: string): string;
|
|
1359
|
+
/**
|
|
1360
|
+
* src/errors.ts
|
|
1361
|
+
* Central error type with invisible tracing and structured context.
|
|
1362
|
+
*/
|
|
1363
|
+
/**
|
|
1364
|
+
* a single entry in the error trace
|
|
1365
|
+
* @category Errors
|
|
1366
|
+
*/
|
|
1367
|
+
interface TraceEntry {
|
|
1368
|
+
label: string;
|
|
1369
|
+
time: number;
|
|
1370
|
+
details?: Record<string, unknown>;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* context captured when an error occurs
|
|
1374
|
+
* @category Errors
|
|
1375
|
+
*/
|
|
1376
|
+
interface ORMErrorContext {
|
|
1377
|
+
table?: string;
|
|
1378
|
+
operation?: string;
|
|
1379
|
+
sql?: string;
|
|
1380
|
+
params?: unknown[];
|
|
1381
|
+
schema?: unknown;
|
|
1382
|
+
[key: string]: unknown;
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* structured error with trace and context
|
|
1386
|
+
* @category Errors
|
|
1387
|
+
*/
|
|
1388
|
+
declare class ORMError extends Error {
|
|
1389
|
+
readonly code: string;
|
|
1390
|
+
readonly trace: TraceEntry[];
|
|
1391
|
+
readonly context: ORMErrorContext;
|
|
1392
|
+
constructor(message: string, opts: {
|
|
1393
|
+
code: string;
|
|
1394
|
+
trace: TraceEntry[];
|
|
1395
|
+
context?: ORMErrorContext;
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* @internal
|
|
1400
|
+
* @category Errors
|
|
1401
|
+
*/
|
|
1402
|
+
declare function withTrace<T>(label: string, details: Record<string, unknown> | undefined, fn: () => T): T;
|
|
1403
|
+
/**
|
|
1404
|
+
* throw an ORMError with trace and context
|
|
1405
|
+
* @category Errors
|
|
1406
|
+
*/
|
|
1407
|
+
declare function raise(code: string, message: string, context?: ORMErrorContext): never;
|
|
1408
|
+
/**
|
|
1409
|
+
* how to handle runtime errors
|
|
1410
|
+
* @category Errors
|
|
1411
|
+
*/
|
|
1412
|
+
type ErrorPolicy2 = "throw" | "emit" | "emit-swallow" | "crash";
|
|
1413
|
+
/** @category Schema */
|
|
1414
|
+
interface TimestampConfig2 {
|
|
1415
|
+
createdAt?: string;
|
|
1416
|
+
updatedAt?: string;
|
|
1417
|
+
}
|
|
1418
|
+
/** @category Schema */
|
|
1419
|
+
declare const DEFAULT_TIMESTAMP_NAMES: Required<Pick<TimestampConfig2, "createdAt" | "updatedAt">>;
|
|
1420
|
+
/** @category Schema */
|
|
1421
|
+
declare function resolveTimestampNames(config: boolean | TimestampConfig2 | undefined, meta: TableMeta): {
|
|
1422
|
+
createdAt: string | null;
|
|
1423
|
+
updatedAt: string | null;
|
|
1424
|
+
};
|
|
1425
|
+
/** @category Advanced */
|
|
1426
|
+
interface InspectorColumn {
|
|
1427
|
+
name: string;
|
|
1428
|
+
type: string;
|
|
1429
|
+
notnull: number;
|
|
1430
|
+
dflt_value: string | null;
|
|
1431
|
+
pk: number;
|
|
1432
|
+
}
|
|
1433
|
+
/** @category Advanced */
|
|
1434
|
+
interface InspectorIndex {
|
|
1435
|
+
name: string;
|
|
1436
|
+
unique: number;
|
|
1437
|
+
columns: string[];
|
|
1438
|
+
}
|
|
1439
|
+
/** @category Advanced */
|
|
1440
|
+
interface InspectorTable {
|
|
1441
|
+
name: string;
|
|
1442
|
+
columns: InspectorColumn[];
|
|
1443
|
+
indexes: InspectorIndex[];
|
|
1444
|
+
}
|
|
1445
|
+
/** @category Advanced */
|
|
1446
|
+
declare function inspectSchema(db: BunDatabase, tableName: string): InspectorTable;
|
|
1447
|
+
/** @category Advanced */
|
|
1448
|
+
declare function inspectAllTables(db: BunDatabase): InspectorTable[];
|
|
1449
|
+
interface DesiredTable {
|
|
1450
|
+
name: string;
|
|
1451
|
+
columns: ColumnMeta[];
|
|
1452
|
+
indexes: Array<{
|
|
1453
|
+
name?: string;
|
|
1454
|
+
columns: string[];
|
|
1455
|
+
unique?: boolean;
|
|
1456
|
+
}>;
|
|
1457
|
+
primaryKey: string;
|
|
1458
|
+
}
|
|
1459
|
+
/** @category Migration */
|
|
1460
|
+
declare function computeDiff(desired: DesiredTable[], actual: InspectorTable[]): SchemaDiff;
|
|
1461
|
+
/** @category Migration */
|
|
1462
|
+
declare function applySync(diff: SchemaDiff, db: BunDatabase, policy: SyncPolicy, desired: DesiredTable[]): void;
|
|
1463
|
+
export { withTrace, table, resolveTimestampNames, raise, migrate, introspectTable, inspectSchema, inspectAllTables, foxdb, createRelationBuilder, createORM, createMigration, computeDiff, buildCreateTableSQL, applySync, WhereClause, UpsertOptions, UpdateData, UnlinkPolicy, TypedRelation, TraceEntry, TimestampShape, TimestampConfig2 as TimestampNamesConfig, TableOperation, TableMeta, TableEventPayload, TableEventOperation, TableDescriptor, TableConfigShape, TableConfig, TScalarSchema, SyncPolicy, SubTableScalarPath, SubTableMeta, SubTableKeys, SubTableConfig, SubMergeType, SubMergeNames, SubMerge, SqliteType, SchemaJSONSchema, SchemaJSON, SchemaDiff, SchemaChange, ScalarProperties, ScalarPath, ScalarMergeType, ScalarMergeNames, ScalarMerge, ScalarKeys, ScalarFilter, Repository, RelationsConfig, RelationEntry, RelationBuilder, PageResult, OrderByClause, ORMEvents, ORMErrorContext, ORMError, ORMContext, Migration, MigrateOptions, MetaStore, Materialized, Listener, LifecycleManager, LifecycleHook, LifecycleEventMap, InspectorTable, InspectorIndex, InspectorColumn, InsertData, Infer, IndexDefinition, FindOptions, EventBus, ErrorPolicy2 as ErrorPolicy, Entity, DesiredTable, DatabaseOptions, DEFAULT_TIMESTAMP_NAMES, CreateORMOptions, CreateORMBaseOptions, ColumnRefs, ColumnRef, ColumnMeta, BunStatement, BunDatabase, BroadOperation, ArrayOfObjectProperties };
|