@simplysm/orm-common 13.0.97 → 13.0.98
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 +267 -0
- package/docs/core.md +225 -0
- package/docs/expression.md +296 -0
- package/docs/query-builder.md +196 -0
- package/docs/queryable.md +578 -0
- package/docs/schema-builders.md +415 -0
- package/docs/types.md +445 -0
- package/docs/utilities.md +122 -0
- package/package.json +2 -2
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# Schema Builders
|
|
2
|
+
|
|
3
|
+
Fluent API builders for defining tables, views, procedures, columns, indexes, and relations.
|
|
4
|
+
|
|
5
|
+
Source: `src/schema/table-builder.ts`, `src/schema/view-builder.ts`, `src/schema/procedure-builder.ts`, `src/schema/factory/column-builder.ts`, `src/schema/factory/index-builder.ts`, `src/schema/factory/relation-builder.ts`, `src/models/system-migration.ts`
|
|
6
|
+
|
|
7
|
+
## Table / TableBuilder
|
|
8
|
+
|
|
9
|
+
### Table (factory function)
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
function Table(name: string): TableBuilder<{}, {}>;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### TableBuilder
|
|
16
|
+
|
|
17
|
+
Immutable fluent builder. Each method returns a new instance.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
class TableBuilder<
|
|
21
|
+
TColumns extends ColumnBuilderRecord,
|
|
22
|
+
TRelations extends RelationBuilderRecord,
|
|
23
|
+
> {
|
|
24
|
+
readonly meta: {
|
|
25
|
+
name: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
database?: string;
|
|
28
|
+
schema?: string;
|
|
29
|
+
columns?: TColumns;
|
|
30
|
+
primaryKey?: (keyof TColumns & string)[];
|
|
31
|
+
relations?: TRelations;
|
|
32
|
+
indexes?: IndexBuilder<(keyof TColumns & string)[]>[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Type inference helpers
|
|
36
|
+
readonly $inferSelect: InferColumns<TColumns> & InferDeepRelations<TRelations>;
|
|
37
|
+
readonly $inferColumns: InferColumns<TColumns>;
|
|
38
|
+
readonly $inferInsert: InferInsertColumns<TColumns>;
|
|
39
|
+
readonly $inferUpdate: InferUpdateColumns<TColumns>;
|
|
40
|
+
|
|
41
|
+
description(desc: string): TableBuilder<TColumns, TRelations>;
|
|
42
|
+
database(db: string): TableBuilder<TColumns, TRelations>;
|
|
43
|
+
schema(schema: string): TableBuilder<TColumns, TRelations>;
|
|
44
|
+
columns<T extends ColumnBuilderRecord>(
|
|
45
|
+
fn: (c: ReturnType<typeof createColumnFactory>) => T,
|
|
46
|
+
): TableBuilder<T, TRelations>;
|
|
47
|
+
primaryKey(...columns: (keyof TColumns & string)[]): TableBuilder<TColumns, TRelations>;
|
|
48
|
+
indexes(
|
|
49
|
+
fn: (i: ReturnType<typeof createIndexFactory<keyof TColumns & string>>) => IndexBuilder<string[]>[],
|
|
50
|
+
): TableBuilder<TColumns, TRelations>;
|
|
51
|
+
relations<T extends RelationBuilderRecord>(
|
|
52
|
+
fn: (r: RelationFactory) => T,
|
|
53
|
+
): TableBuilder<TColumns, T>;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Example:**
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const User = Table("User")
|
|
61
|
+
.database("mydb")
|
|
62
|
+
.columns((c) => ({
|
|
63
|
+
id: c.bigint().autoIncrement(),
|
|
64
|
+
name: c.varchar(100),
|
|
65
|
+
email: c.varchar(200).nullable(),
|
|
66
|
+
status: c.varchar(20).default("active"),
|
|
67
|
+
createdAt: c.datetime(),
|
|
68
|
+
}))
|
|
69
|
+
.primaryKey("id")
|
|
70
|
+
.indexes((i) => [i.index("email").unique()])
|
|
71
|
+
.relations((r) => ({
|
|
72
|
+
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
73
|
+
}));
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## View / ViewBuilder
|
|
79
|
+
|
|
80
|
+
### View (factory function)
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
function View(name: string): ViewBuilder<any, any, {}>;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### ViewBuilder
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
class ViewBuilder<
|
|
90
|
+
TDbContext extends DbContextBase,
|
|
91
|
+
TData extends DataRecord,
|
|
92
|
+
TRelations extends RelationBuilderRecord,
|
|
93
|
+
> {
|
|
94
|
+
readonly meta: {
|
|
95
|
+
name: string;
|
|
96
|
+
description?: string;
|
|
97
|
+
database?: string;
|
|
98
|
+
schema?: string;
|
|
99
|
+
viewFn?: (db: TDbContext) => Queryable<TData, any>;
|
|
100
|
+
relations?: TRelations;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
readonly $inferSelect: TData;
|
|
104
|
+
|
|
105
|
+
description(desc: string): ViewBuilder<TDbContext, TData, TRelations>;
|
|
106
|
+
database(db: string): ViewBuilder<TDbContext, TData, TRelations>;
|
|
107
|
+
schema(schema: string): ViewBuilder<TDbContext, TData, TRelations>;
|
|
108
|
+
query<TViewData extends DataRecord, TDb extends DbContextBase>(
|
|
109
|
+
viewFn: (db: TDb) => Queryable<TViewData, any>,
|
|
110
|
+
): ViewBuilder<TDb, TViewData, TRelations>;
|
|
111
|
+
relations<T extends RelationBuilderRecord>(
|
|
112
|
+
fn: (r: RelationFactory) => T,
|
|
113
|
+
): ViewBuilder<TDbContext, TData & InferDeepRelations<T>, TRelations>;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Example:**
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const ActiveUsers = View("ActiveUsers")
|
|
121
|
+
.database("mydb")
|
|
122
|
+
.query((db: MyDb) =>
|
|
123
|
+
db.user()
|
|
124
|
+
.where((u) => [expr.eq(u.status, "active")])
|
|
125
|
+
.select((u) => ({ id: u.id, name: u.name, email: u.email }))
|
|
126
|
+
);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Procedure / ProcedureBuilder
|
|
132
|
+
|
|
133
|
+
### Procedure (factory function)
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
function Procedure(name: string): ProcedureBuilder<never, never>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### ProcedureBuilder
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
class ProcedureBuilder<
|
|
143
|
+
TParams extends ColumnBuilderRecord,
|
|
144
|
+
TReturns extends ColumnBuilderRecord,
|
|
145
|
+
> {
|
|
146
|
+
readonly meta: {
|
|
147
|
+
name: string;
|
|
148
|
+
description?: string;
|
|
149
|
+
database?: string;
|
|
150
|
+
schema?: string;
|
|
151
|
+
params?: TParams;
|
|
152
|
+
returns?: TReturns;
|
|
153
|
+
query?: string;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
description(desc: string): ProcedureBuilder<TParams, TReturns>;
|
|
157
|
+
database(db: string): ProcedureBuilder<TParams, TReturns>;
|
|
158
|
+
schema(schema: string): ProcedureBuilder<TParams, TReturns>;
|
|
159
|
+
params<T extends ColumnBuilderRecord>(
|
|
160
|
+
fn: (c: ReturnType<typeof createColumnFactory>) => T,
|
|
161
|
+
): ProcedureBuilder<T, TReturns>;
|
|
162
|
+
returns<T extends ColumnBuilderRecord>(
|
|
163
|
+
fn: (c: ReturnType<typeof createColumnFactory>) => T,
|
|
164
|
+
): ProcedureBuilder<TParams, T>;
|
|
165
|
+
body(sql: string): ProcedureBuilder<TParams, TReturns>;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Example:**
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const GetUserById = Procedure("GetUserById")
|
|
173
|
+
.database("mydb")
|
|
174
|
+
.params((c) => ({ userId: c.bigint() }))
|
|
175
|
+
.returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
|
|
176
|
+
.body("SELECT id, name FROM User WHERE id = userId");
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## ColumnBuilder
|
|
182
|
+
|
|
183
|
+
Column definition builder used within `TableBuilder.columns()`.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
class ColumnBuilder<TValue extends ColumnPrimitive, TMeta extends ColumnMeta> {
|
|
187
|
+
readonly meta: TMeta;
|
|
188
|
+
|
|
189
|
+
autoIncrement(): ColumnBuilder<TValue, TMeta & { autoIncrement: true }>;
|
|
190
|
+
nullable(): ColumnBuilder<TValue | undefined, TMeta & { nullable: true }>;
|
|
191
|
+
default(value: TValue): ColumnBuilder<TValue, TMeta & { default: typeof value }>;
|
|
192
|
+
description(desc: string): ColumnBuilder<TValue, TMeta & { description: string }>;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## createColumnFactory
|
|
197
|
+
|
|
198
|
+
Returns an object with methods for all column types.
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
function createColumnFactory(): {
|
|
202
|
+
int(): ColumnBuilder<number, ...>;
|
|
203
|
+
bigint(): ColumnBuilder<number, ...>;
|
|
204
|
+
float(): ColumnBuilder<number, ...>;
|
|
205
|
+
double(): ColumnBuilder<number, ...>;
|
|
206
|
+
decimal(precision: number, scale?: number): ColumnBuilder<number, ...>;
|
|
207
|
+
varchar(length: number): ColumnBuilder<string, ...>;
|
|
208
|
+
char(length: number): ColumnBuilder<string, ...>;
|
|
209
|
+
text(): ColumnBuilder<string, ...>;
|
|
210
|
+
binary(): ColumnBuilder<Bytes, ...>;
|
|
211
|
+
boolean(): ColumnBuilder<boolean, ...>;
|
|
212
|
+
datetime(): ColumnBuilder<DateTime, ...>;
|
|
213
|
+
date(): ColumnBuilder<DateOnly, ...>;
|
|
214
|
+
time(): ColumnBuilder<Time, ...>;
|
|
215
|
+
uuid(): ColumnBuilder<Uuid, ...>;
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Example:**
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
Table("Product").columns((c) => ({
|
|
223
|
+
id: c.bigint().autoIncrement(),
|
|
224
|
+
name: c.varchar(100),
|
|
225
|
+
price: c.decimal(10, 2),
|
|
226
|
+
description: c.text().nullable(),
|
|
227
|
+
isActive: c.boolean().default(true),
|
|
228
|
+
createdAt: c.datetime(),
|
|
229
|
+
}))
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## IndexBuilder
|
|
235
|
+
|
|
236
|
+
Index definition builder used within `TableBuilder.indexes()`.
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
class IndexBuilder<TKeys extends string[]> {
|
|
240
|
+
readonly meta: {
|
|
241
|
+
columns: TKeys;
|
|
242
|
+
name?: string;
|
|
243
|
+
unique?: boolean;
|
|
244
|
+
orderBy?: { [K in keyof TKeys]: "ASC" | "DESC" };
|
|
245
|
+
description?: string;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
name(name: string): IndexBuilder<TKeys>;
|
|
249
|
+
unique(): IndexBuilder<TKeys>;
|
|
250
|
+
orderBy(...orderBy: { [K in keyof TKeys]: "ASC" | "DESC" }): IndexBuilder<TKeys>;
|
|
251
|
+
description(description: string): IndexBuilder<TKeys>;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## createIndexFactory
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
function createIndexFactory<TColumnKey extends string>(): {
|
|
259
|
+
index<TKeys extends TColumnKey[]>(...columns: [...TKeys]): IndexBuilder<TKeys>;
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Example:**
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
Table("User")
|
|
267
|
+
.columns((c) => ({ id: c.bigint(), email: c.varchar(200), name: c.varchar(100) }))
|
|
268
|
+
.indexes((i) => [
|
|
269
|
+
i.index("email").unique(),
|
|
270
|
+
i.index("name", "email").orderBy("ASC", "DESC"),
|
|
271
|
+
i.index("email").name("IX_User_Email"),
|
|
272
|
+
]);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Relation Builders
|
|
278
|
+
|
|
279
|
+
### ForeignKeyBuilder
|
|
280
|
+
|
|
281
|
+
N:1 FK relation that creates a DB constraint.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
class ForeignKeyBuilder<TOwner extends TableBuilder<any, any>, TTargetFn extends () => TableBuilder<any, any>> {
|
|
285
|
+
readonly meta: {
|
|
286
|
+
ownerFn: () => TOwner;
|
|
287
|
+
columns: string[];
|
|
288
|
+
targetFn: TTargetFn;
|
|
289
|
+
description?: string;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
description(desc: string): ForeignKeyBuilder<TOwner, TTargetFn>;
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### ForeignKeyTargetBuilder
|
|
297
|
+
|
|
298
|
+
1:N FK reverse-reference. Loaded as array by default; call `.single()` for 1:1.
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
class ForeignKeyTargetBuilder<TTargetTableFn extends () => TableBuilder<any, any>, TIsSingle extends boolean> {
|
|
302
|
+
readonly meta: {
|
|
303
|
+
targetTableFn: TTargetTableFn;
|
|
304
|
+
relationName: string;
|
|
305
|
+
description?: string;
|
|
306
|
+
isSingle?: TIsSingle;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
description(desc: string): ForeignKeyTargetBuilder<TTargetTableFn, TIsSingle>;
|
|
310
|
+
single(): ForeignKeyTargetBuilder<TTargetTableFn, true>;
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### RelationKeyBuilder
|
|
315
|
+
|
|
316
|
+
N:1 logical relation (no DB FK constraint). Can be used in Views.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
class RelationKeyBuilder<
|
|
320
|
+
TOwner extends TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
321
|
+
TTargetFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
322
|
+
> {
|
|
323
|
+
readonly meta: {
|
|
324
|
+
ownerFn: () => TOwner;
|
|
325
|
+
columns: string[];
|
|
326
|
+
targetFn: TTargetFn;
|
|
327
|
+
description?: string;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
description(desc: string): RelationKeyBuilder<TOwner, TTargetFn>;
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### RelationKeyTargetBuilder
|
|
335
|
+
|
|
336
|
+
1:N logical reverse-reference (no DB FK constraint). Can be used in Views.
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
class RelationKeyTargetBuilder<
|
|
340
|
+
TTargetTableFn extends () => TableBuilder<any, any> | ViewBuilder<any, any, any>,
|
|
341
|
+
TIsSingle extends boolean,
|
|
342
|
+
> {
|
|
343
|
+
readonly meta: {
|
|
344
|
+
targetTableFn: TTargetTableFn;
|
|
345
|
+
relationName: string;
|
|
346
|
+
description?: string;
|
|
347
|
+
isSingle?: TIsSingle;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
description(desc: string): RelationKeyTargetBuilder<TTargetTableFn, TIsSingle>;
|
|
351
|
+
single(): RelationKeyTargetBuilder<TTargetTableFn, true>;
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### createRelationFactory
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
function createRelationFactory<TOwner, TColumnKey extends string>(
|
|
359
|
+
ownerFn: () => TOwner,
|
|
360
|
+
): TOwner extends TableBuilder<any, any>
|
|
361
|
+
? RelationFkFactory & RelationRkFactory // Tables get both FK and RK
|
|
362
|
+
: RelationRkFactory; // Views get only RK
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Factory methods:
|
|
366
|
+
- `foreignKey(columns, targetFn)` -- N:1 FK (table only)
|
|
367
|
+
- `foreignKeyTarget(targetTableFn, relationName)` -- 1:N FK reverse-reference (table only)
|
|
368
|
+
- `relationKey(columns, targetFn)` -- N:1 logical relation
|
|
369
|
+
- `relationKeyTarget(targetTableFn, relationName)` -- 1:N logical reverse-reference
|
|
370
|
+
|
|
371
|
+
**Example:**
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
const Post = Table("Post")
|
|
375
|
+
.columns((c) => ({ id: c.bigint().autoIncrement(), authorId: c.bigint() }))
|
|
376
|
+
.primaryKey("id")
|
|
377
|
+
.relations((r) => ({
|
|
378
|
+
author: r.foreignKey(["authorId"], () => User),
|
|
379
|
+
}));
|
|
380
|
+
|
|
381
|
+
const User = Table("User")
|
|
382
|
+
.columns((c) => ({ id: c.bigint().autoIncrement(), name: c.varchar(100) }))
|
|
383
|
+
.primaryKey("id")
|
|
384
|
+
.relations((r) => ({
|
|
385
|
+
posts: r.foreignKeyTarget(() => Post, "author"),
|
|
386
|
+
profile: r.foreignKeyTarget(() => Profile, "user").single(),
|
|
387
|
+
}));
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## _Migration
|
|
393
|
+
|
|
394
|
+
Built-in system migration table. Automatically included by `defineDbContext`.
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
const _Migration = Table("_migration")
|
|
398
|
+
.columns((c) => ({ code: c.varchar(255) }))
|
|
399
|
+
.description("System Migration Table")
|
|
400
|
+
.primaryKey("code");
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Type Inference Utilities
|
|
406
|
+
|
|
407
|
+
| Type | Description |
|
|
408
|
+
|------|-------------|
|
|
409
|
+
| `ColumnBuilderRecord` | `Record<string, ColumnBuilder<ColumnPrimitive, ColumnMeta>>` |
|
|
410
|
+
| `InferColumns<T>` | Map column builders to value types (`{ id: number; name: string; ... }`) |
|
|
411
|
+
| `InferInsertColumns<T>` | Required columns + Partial optional columns (autoIncrement/nullable/default are optional) |
|
|
412
|
+
| `InferUpdateColumns<T>` | All columns Partial |
|
|
413
|
+
| `InferColumnExprs<T>` | Map column builders to `ExprInput` types |
|
|
414
|
+
| `RelationBuilderRecord` | Record of FK/FKT/RK/RKT builders |
|
|
415
|
+
| `InferDeepRelations<T>` | Deep relation type inference (all relations optional) |
|