@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.
@@ -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 };