sqlite-zod-orm 3.0.0 → 3.2.0
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/README.md +181 -223
- package/dist/index.js +5010 -0
- package/package.json +13 -13
- package/src/build.ts +8 -10
- package/src/database.ts +491 -0
- package/src/index.ts +24 -0
- package/src/proxy-query.ts +55 -51
- package/src/query-builder.ts +145 -6
- package/src/schema.ts +122 -0
- package/src/types.ts +195 -0
- package/src/satidb.ts +0 -1153
package/src/types.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* types.ts — All type definitions for sqlite-zod-orm
|
|
3
|
+
*
|
|
4
|
+
* NavEntity<S, R, Table> computes navigation methods from the relations config
|
|
5
|
+
* at the type level — full autocomplete on schema fields AND relationship methods.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import type { QueryBuilder } from './query-builder';
|
|
9
|
+
|
|
10
|
+
export type ZodType = z.ZodTypeAny;
|
|
11
|
+
export type SchemaMap = Record<string, z.ZodType<any>>;
|
|
12
|
+
|
|
13
|
+
/** Relations config: `{ childTable: { fkColumn: 'parentTable' } }` */
|
|
14
|
+
export type RelationsConfig = Record<string, Record<string, string>>;
|
|
15
|
+
|
|
16
|
+
/** Internal cast: all schemas are z.object() at runtime */
|
|
17
|
+
export const asZodObject = (s: z.ZodType<any>) => s as unknown as z.ZodObject<any>;
|
|
18
|
+
|
|
19
|
+
/** Index definition: single column or composite columns */
|
|
20
|
+
export type IndexDef = string | string[];
|
|
21
|
+
|
|
22
|
+
/** Options for the Database constructor */
|
|
23
|
+
export type DatabaseOptions<R extends RelationsConfig = RelationsConfig> = {
|
|
24
|
+
changeTracking?: boolean;
|
|
25
|
+
indexes?: Record<string, IndexDef[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Declare relationships between tables.
|
|
28
|
+
*
|
|
29
|
+
* Format: `{ childTable: { fkColumn: 'parentTable' } }`
|
|
30
|
+
*
|
|
31
|
+
* `books: { author_id: 'authors' }` → FOREIGN KEY, lazy nav, fluent join.
|
|
32
|
+
*/
|
|
33
|
+
relations?: R;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type Relationship = {
|
|
37
|
+
type: 'belongs-to' | 'one-to-many';
|
|
38
|
+
from: string;
|
|
39
|
+
to: string;
|
|
40
|
+
relationshipField: string;
|
|
41
|
+
foreignKey: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Type Helpers
|
|
46
|
+
// =============================================================================
|
|
47
|
+
|
|
48
|
+
export type InferSchema<S extends z.ZodType<any>> = z.infer<S>;
|
|
49
|
+
export type InputSchema<S extends z.ZodType<any>> = z.input<S>;
|
|
50
|
+
export type EntityData<S extends z.ZodType<any>> = Omit<InputSchema<S>, 'id'>;
|
|
51
|
+
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Navigation Type Inference (simple approach)
|
|
54
|
+
// =============================================================================
|
|
55
|
+
|
|
56
|
+
/** Strip `_id` suffix: `'author_id'` → `'author'` */
|
|
57
|
+
type StripIdSuffix<S extends string> = S extends `${infer Base}_id` ? Base : S;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Base entity type with schema fields, id, update(), delete().
|
|
61
|
+
* No nav methods — those are added by NavMethods.
|
|
62
|
+
*/
|
|
63
|
+
type BaseEntity<S extends z.ZodType<any>> = z.infer<S> & {
|
|
64
|
+
id: number;
|
|
65
|
+
update: (data: Partial<Omit<z.input<S>, 'id'>>) => BaseEntity<S> | null;
|
|
66
|
+
delete: () => void;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Belongs-to nav methods for a table.
|
|
71
|
+
* `books: { author_id: 'authors' }` → book gets `{ author: () => BaseEntity<AuthorSchema> | null }`
|
|
72
|
+
*/
|
|
73
|
+
type BelongsToNav<
|
|
74
|
+
S extends SchemaMap,
|
|
75
|
+
R extends RelationsConfig,
|
|
76
|
+
Table extends string,
|
|
77
|
+
> = Table extends keyof R
|
|
78
|
+
? { readonly [FK in Extract<keyof R[Table], string> as StripIdSuffix<FK>]:
|
|
79
|
+
R[Table][FK] extends keyof S
|
|
80
|
+
? () => BaseEntity<S[R[Table][FK]]> | null
|
|
81
|
+
: never }
|
|
82
|
+
: {};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Has-many nav methods for a table.
|
|
86
|
+
* If `books: { author_id: 'authors' }`, then `authors` gets `{ books: () => BaseEntity<BookSchema>[] }`
|
|
87
|
+
*
|
|
88
|
+
* We use key remapping in a mapped type: for each child table in R,
|
|
89
|
+
* check if ParentTable appears in its FK target values.
|
|
90
|
+
*/
|
|
91
|
+
type HasManyNav<
|
|
92
|
+
S extends SchemaMap,
|
|
93
|
+
R extends RelationsConfig,
|
|
94
|
+
ParentTable extends string,
|
|
95
|
+
> = {
|
|
96
|
+
readonly [ChildTable in Extract<keyof R & keyof S, string> as
|
|
97
|
+
_FKTargetsContain<R[ChildTable], ParentTable> extends true ? ChildTable : never
|
|
98
|
+
]: () => BaseEntity<S[ChildTable]>[];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/** Check if any value in record Rec equals Target */
|
|
102
|
+
type _FKTargetsContain<Rec, Target extends string> =
|
|
103
|
+
Target extends Rec[keyof Rec] ? true : false;
|
|
104
|
+
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// NavEntity — Full entity type with nav methods (one level deep)
|
|
107
|
+
// =============================================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A fully typed entity returned from the ORM.
|
|
111
|
+
*
|
|
112
|
+
* Nav methods return BaseEntity (one level deep typing).
|
|
113
|
+
* Calling `book.author()` gives you a typed entity, and calling
|
|
114
|
+
* `.books()` on that result works at runtime, just without
|
|
115
|
+
* the second level of nav method types.
|
|
116
|
+
*/
|
|
117
|
+
export type NavEntity<
|
|
118
|
+
S extends SchemaMap,
|
|
119
|
+
R extends RelationsConfig,
|
|
120
|
+
Table extends string,
|
|
121
|
+
> = BaseEntity<S[Table & keyof S]>
|
|
122
|
+
& BelongsToNav<S, R, Table>
|
|
123
|
+
& HasManyNav<S, R, Table>;
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Entity Accessors
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
/** Fluent update builder */
|
|
130
|
+
export type UpdateBuilder<T> = {
|
|
131
|
+
where: (conditions: Record<string, any>) => UpdateBuilder<T>;
|
|
132
|
+
exec: () => number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** Nav-aware entity accessor for a specific table */
|
|
136
|
+
export type NavEntityAccessor<
|
|
137
|
+
S extends SchemaMap,
|
|
138
|
+
R extends RelationsConfig,
|
|
139
|
+
Table extends string,
|
|
140
|
+
> = {
|
|
141
|
+
insert: (data: Omit<z.input<S[Table & keyof S]>, 'id'>) => NavEntity<S, R, Table>;
|
|
142
|
+
update: ((id: number, data: Partial<Omit<z.input<S[Table & keyof S]>, 'id'>>) => NavEntity<S, R, Table> | null)
|
|
143
|
+
& ((data: Partial<Omit<z.input<S[Table & keyof S]>, 'id'>>) => UpdateBuilder<NavEntity<S, R, Table>>);
|
|
144
|
+
upsert: (conditions?: Partial<z.infer<S[Table & keyof S]>>, data?: Partial<z.infer<S[Table & keyof S]>>) => NavEntity<S, R, Table>;
|
|
145
|
+
delete: (id: number) => void;
|
|
146
|
+
subscribe: (event: 'insert' | 'update' | 'delete', callback: (data: NavEntity<S, R, Table>) => void) => void;
|
|
147
|
+
unsubscribe: (event: 'insert' | 'update' | 'delete', callback: (data: NavEntity<S, R, Table>) => void) => void;
|
|
148
|
+
select: (...cols: (keyof z.infer<S[Table & keyof S]> & string)[]) => QueryBuilder<NavEntity<S, R, Table>>;
|
|
149
|
+
_tableName: string;
|
|
150
|
+
readonly _schema?: S[Table & keyof S];
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** Map each table in the schema to its nav-aware accessor */
|
|
154
|
+
export type TypedNavAccessors<S extends SchemaMap, R extends RelationsConfig> = {
|
|
155
|
+
[K in Extract<keyof S, string>]: NavEntityAccessor<S, R, K>;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// Legacy types (used internally by _Database class)
|
|
160
|
+
// =============================================================================
|
|
161
|
+
|
|
162
|
+
export type AugmentedEntity<S extends z.ZodType<any>> = InferSchema<S> & {
|
|
163
|
+
id: number;
|
|
164
|
+
update: (data: Partial<EntityData<S>>) => AugmentedEntity<S> | null;
|
|
165
|
+
delete: () => void;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export type EntityAccessor<S extends z.ZodType<any>> = {
|
|
169
|
+
insert: (data: EntityData<S>) => AugmentedEntity<S>;
|
|
170
|
+
update: ((id: number, data: Partial<EntityData<S>>) => AugmentedEntity<S> | null) & ((data: Partial<EntityData<S>>) => UpdateBuilder<AugmentedEntity<S>>);
|
|
171
|
+
upsert: (conditions?: Partial<InferSchema<S>>, data?: Partial<InferSchema<S>>) => AugmentedEntity<S>;
|
|
172
|
+
delete: (id: number) => void;
|
|
173
|
+
subscribe: (event: 'insert' | 'update' | 'delete', callback: (data: AugmentedEntity<S>) => void) => void;
|
|
174
|
+
unsubscribe: (event: 'insert' | 'update' | 'delete', callback: (data: AugmentedEntity<S>) => void) => void;
|
|
175
|
+
select: (...cols: (keyof InferSchema<S> & string)[]) => QueryBuilder<AugmentedEntity<S>>;
|
|
176
|
+
_tableName: string;
|
|
177
|
+
readonly _schema?: S;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export type TypedAccessors<T extends SchemaMap> = {
|
|
181
|
+
[K in keyof T]: EntityAccessor<T[K]>;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// =============================================================================
|
|
185
|
+
// Proxy query column types
|
|
186
|
+
// =============================================================================
|
|
187
|
+
|
|
188
|
+
import type { ColumnNode } from './proxy-query';
|
|
189
|
+
|
|
190
|
+
export type ColumnRef = ColumnNode & string;
|
|
191
|
+
|
|
192
|
+
export type ProxyColumns<T> = Required<{ [K in keyof T]: ColumnRef }> & {
|
|
193
|
+
id: ColumnRef;
|
|
194
|
+
[k: string]: ColumnRef;
|
|
195
|
+
};
|