@venizia/ignis-docs 0.0.7-2 → 0.0.8-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/dist/mcp-server/common/paths.d.ts +4 -2
- package/dist/mcp-server/common/paths.d.ts.map +1 -1
- package/dist/mcp-server/common/paths.js +8 -6
- package/dist/mcp-server/common/paths.js.map +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
- package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
- package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
- package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
- package/package.json +1 -1
- package/wiki/best-practices/api-usage-examples.md +9 -9
- package/wiki/best-practices/architectural-patterns.md +19 -3
- package/wiki/best-practices/architecture-decisions.md +6 -6
- package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
- package/wiki/best-practices/code-style-standards/index.md +2 -2
- package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
- package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
- package/wiki/best-practices/data-modeling.md +1 -1
- package/wiki/best-practices/deployment-strategies.md +1 -1
- package/wiki/best-practices/error-handling.md +2 -2
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/{references → extensions}/components/authentication/api.md +12 -20
- package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
- package/wiki/{references → extensions}/components/authentication/index.md +5 -8
- package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
- package/wiki/{references → extensions}/components/authorization/api.md +62 -13
- package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
- package/wiki/{references → extensions}/components/authorization/index.md +93 -6
- package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
- package/wiki/{references → extensions}/components/health-check.md +5 -4
- package/wiki/{references → extensions}/components/index.md +2 -0
- package/wiki/{references → extensions}/components/mail/index.md +1 -1
- package/wiki/{references → extensions}/components/request-tracker.md +1 -1
- package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
- package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
- package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
- package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
- package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
- package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
- package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
- package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
- package/wiki/{references → extensions}/components/swagger.md +3 -3
- package/wiki/{references → extensions}/components/template/index.md +4 -4
- package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
- package/wiki/{references → extensions}/components/template/single-page.md +1 -1
- package/wiki/{references → extensions}/components/websocket/api.md +7 -6
- package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
- package/wiki/{references → extensions}/components/websocket/index.md +17 -11
- package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
- package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
- package/wiki/{references → extensions}/helpers/env/index.md +9 -5
- package/wiki/{references → extensions}/helpers/error/index.md +2 -7
- package/wiki/{references → extensions}/helpers/index.md +18 -6
- package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
- package/wiki/extensions/helpers/kafka/consumer.md +384 -0
- package/wiki/extensions/helpers/kafka/examples.md +361 -0
- package/wiki/extensions/helpers/kafka/index.md +639 -0
- package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
- package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
- package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
- package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
- package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
- package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
- package/wiki/{references → extensions}/helpers/template/index.md +1 -1
- package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
- package/wiki/{references → extensions}/helpers/types/index.md +63 -16
- package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
- package/wiki/extensions/index.md +48 -0
- package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
- package/wiki/guides/core-concepts/application/index.md +95 -35
- package/wiki/guides/core-concepts/components-guide.md +23 -19
- package/wiki/guides/core-concepts/components.md +34 -10
- package/wiki/guides/core-concepts/dependency-injection.md +99 -34
- package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
- package/wiki/guides/core-concepts/persistent/models.md +43 -1
- package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
- package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
- package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
- package/wiki/guides/core-concepts/services.md +19 -5
- package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
- package/wiki/guides/get-started/philosophy.md +1 -1
- package/wiki/guides/index.md +2 -2
- package/wiki/guides/reference/glossary.md +7 -7
- package/wiki/guides/reference/mcp-docs-server.md +1 -1
- package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
- package/wiki/guides/tutorials/complete-installation.md +17 -14
- package/wiki/guides/tutorials/ecommerce-api.md +18 -18
- package/wiki/guides/tutorials/realtime-chat.md +8 -8
- package/wiki/guides/tutorials/testing.md +2 -2
- package/wiki/index.md +4 -3
- package/wiki/references/base/application.md +341 -21
- package/wiki/references/base/bootstrapping.md +43 -13
- package/wiki/references/base/components.md +259 -8
- package/wiki/references/base/controllers.md +556 -253
- package/wiki/references/base/datasources.md +159 -79
- package/wiki/references/base/dependency-injection.md +299 -48
- package/wiki/references/base/filter-system/application-usage.md +18 -2
- package/wiki/references/base/filter-system/array-operators.md +14 -6
- package/wiki/references/base/filter-system/comparison-operators.md +9 -3
- package/wiki/references/base/filter-system/default-filter.md +28 -3
- package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
- package/wiki/references/base/filter-system/index.md +169 -11
- package/wiki/references/base/filter-system/json-filtering.md +51 -18
- package/wiki/references/base/filter-system/list-operators.md +4 -3
- package/wiki/references/base/filter-system/logical-operators.md +7 -2
- package/wiki/references/base/filter-system/null-operators.md +50 -0
- package/wiki/references/base/filter-system/quick-reference.md +82 -243
- package/wiki/references/base/filter-system/range-operators.md +7 -1
- package/wiki/references/base/filter-system/tips.md +34 -7
- package/wiki/references/base/filter-system/use-cases.md +6 -5
- package/wiki/references/base/grpc-controllers.md +984 -0
- package/wiki/references/base/index.md +32 -24
- package/wiki/references/base/middleware.md +347 -0
- package/wiki/references/base/models.md +390 -46
- package/wiki/references/base/providers.md +14 -14
- package/wiki/references/base/repositories/advanced.md +84 -69
- package/wiki/references/base/repositories/index.md +447 -12
- package/wiki/references/base/repositories/mixins.md +103 -98
- package/wiki/references/base/repositories/relations.md +129 -45
- package/wiki/references/base/repositories/soft-deletable.md +104 -23
- package/wiki/references/base/services.md +94 -14
- package/wiki/references/index.md +12 -10
- package/wiki/references/quick-reference.md +98 -65
- package/wiki/references/utilities/crypto.md +21 -4
- package/wiki/references/utilities/date.md +25 -7
- package/wiki/references/utilities/index.md +26 -24
- package/wiki/references/utilities/jsx.md +54 -54
- package/wiki/references/utilities/module.md +8 -6
- package/wiki/references/utilities/parse.md +16 -9
- package/wiki/references/utilities/performance.md +22 -7
- package/wiki/references/utilities/promise.md +19 -16
- package/wiki/references/utilities/request.md +48 -26
- package/wiki/references/utilities/schema.md +69 -6
- package/wiki/references/utilities/statuses.md +131 -140
- package/wiki/references/helpers/kafka/consumer.md +0 -473
- package/wiki/references/helpers/kafka/examples.md +0 -234
- package/wiki/references/helpers/kafka/index.md +0 -482
- /package/wiki/{references → extensions}/components/mail/api.md +0 -0
- /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
- /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
- /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
- /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
- /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
- /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
- /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
- /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
- /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
- /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
|
@@ -31,7 +31,7 @@ Fundamental building block wrapping a Drizzle ORM schema.
|
|
|
31
31
|
|---------|-------------|
|
|
32
32
|
| **Schema Encapsulation** | Holds Drizzle `pgTable` schema for consistent repository access |
|
|
33
33
|
| **Metadata** | Works with `@model` decorator to mark database entities |
|
|
34
|
-
| **Schema Generation** | Uses `drizzle-zod` to generate Zod schemas (`
|
|
34
|
+
| **Schema Generation** | Uses `drizzle-zod` to generate Zod schemas (`select`, `create`, `update`) |
|
|
35
35
|
| **Static Properties** | Supports static `schema`, `relations`, `TABLE_NAME`, and `AUTHORIZATION_SUBJECT` |
|
|
36
36
|
| **Convenience** | Includes `toObject()` and `toJSON()` methods |
|
|
37
37
|
|
|
@@ -39,6 +39,8 @@ Fundamental building block wrapping a Drizzle ORM schema.
|
|
|
39
39
|
|
|
40
40
|
The `@model` decorator marks a class as a database entity and configures its behavior.
|
|
41
41
|
|
|
42
|
+
**File:** `packages/core/src/base/metadata/persistents.ts`
|
|
43
|
+
|
|
42
44
|
#### Decorator Options
|
|
43
45
|
|
|
44
46
|
```typescript
|
|
@@ -60,13 +62,20 @@ The `@model` decorator marks a class as a database entity and configures its beh
|
|
|
60
62
|
| Option | Type | Description |
|
|
61
63
|
|--------|------|-------------|
|
|
62
64
|
| `type` | `'entity' \| 'view'` | Entity type - `'entity'` for tables, `'view'` for database views |
|
|
63
|
-
| `tableName` | `string` | Optional custom table name
|
|
65
|
+
| `tableName` | `string` | Optional custom table name. Resolution order: `tableName` > static `TABLE_NAME` > class name |
|
|
64
66
|
| `skipMigrate` | `boolean` | Skip this model during schema migrations |
|
|
65
67
|
| `settings.hiddenProperties` | `string[]` | Array of property names to exclude from all repository query results |
|
|
66
68
|
| `settings.defaultFilter` | `TFilter` | Filter automatically applied to all repository queries (see [Default Filter](/references/base/filter-system/default-filter)) |
|
|
67
|
-
| `settings.authorize` | `IModelAuthorizeSettings` | Authorization settings — declares the model's authorization principal (see [Authorization](/
|
|
69
|
+
| `settings.authorize` | `IModelAuthorizeSettings` | Authorization settings — declares the model's authorization principal (see [Authorization](/extensions/components/authorization/usage#model-based-resource-references)) |
|
|
68
70
|
| `settings.authorize.principal` | `string` | The authorization subject name for this model. Auto-populates `AUTHORIZATION_SUBJECT` static property |
|
|
69
71
|
|
|
72
|
+
#### `@model` Behavior
|
|
73
|
+
|
|
74
|
+
When the `@model` decorator is applied:
|
|
75
|
+
1. If `settings.authorize.principal` is provided and `AUTHORIZATION_SUBJECT` is not already defined on the class, it auto-populates `AUTHORIZATION_SUBJECT` with the principal value
|
|
76
|
+
2. The model is registered in the `MetadataRegistry` model registry, keyed by table name (resolved as: `metadata.tableName` > `static TABLE_NAME` > class name)
|
|
77
|
+
3. The static `relations` property is stored as a resolver (not immediately resolved) to avoid circular dependency issues between models
|
|
78
|
+
|
|
70
79
|
### Hidden Properties
|
|
71
80
|
|
|
72
81
|
Hidden properties are **excluded at the SQL level** - they are never fetched from the database when querying through repositories. This provides:
|
|
@@ -237,7 +246,7 @@ export class User extends BaseEntity<typeof userTable> {
|
|
|
237
246
|
| Property | Type | Description |
|
|
238
247
|
|----------|------|-------------|
|
|
239
248
|
| `schema` | `TTableSchemaWithId` | Drizzle table schema defined with `pgTable()` |
|
|
240
|
-
| `relations` | `TValueOrResolver<Array<TRelationConfig>>` | Relation definitions (can be a function for lazy loading) |
|
|
249
|
+
| `relations` | `TValueOrResolver<Array<TRelationConfig>>` | Relation definitions (can be a function for lazy loading to avoid circular deps) |
|
|
241
250
|
| `TABLE_NAME` | `string \| undefined` | Optional table name (defaults to class name if not set) |
|
|
242
251
|
| `AUTHORIZATION_SUBJECT` | `string \| undefined` | Authorization principal name. Auto-populated from `@model` settings `authorize.principal` |
|
|
243
252
|
|
|
@@ -257,9 +266,40 @@ interface IEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId> {
|
|
|
257
266
|
|
|
258
267
|
| Method | Description |
|
|
259
268
|
|--------|-------------|
|
|
260
|
-
| `getSchema({ type })` | Get Zod schema for validation (`
|
|
261
|
-
| `toObject()` | Convert to plain object |
|
|
262
|
-
| `toJSON()` |
|
|
269
|
+
| `getSchema({ type })` | Get Zod schema for validation (`'select'`, `'create'`, `'update'`) |
|
|
270
|
+
| `toObject()` | Convert to plain object (shallow spread of `this`) |
|
|
271
|
+
| `toJSON()` | Delegates to `toObject()` — returns a plain object (used by `JSON.stringify`) |
|
|
272
|
+
|
|
273
|
+
### `getSchema` Method
|
|
274
|
+
|
|
275
|
+
Generates a Zod validation schema from the Drizzle table schema using `drizzle-zod`.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
getSchema(opts: { type: TSchemaType }): ZodSchema
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The `type` parameter accepts lowercase string values defined in the `SchemaTypes` class:
|
|
282
|
+
|
|
283
|
+
| Type | Value | Zod Schema Generated | Description |
|
|
284
|
+
|------|-------|---------------------|-------------|
|
|
285
|
+
| `SchemaTypes.SELECT` | `'select'` | `createSelectSchema(schema)` | Schema for query results |
|
|
286
|
+
| `SchemaTypes.CREATE` | `'create'` | `createInsertSchema(schema)` | Schema for insert operations |
|
|
287
|
+
| `SchemaTypes.UPDATE` | `'update'` | `createUpdateSchema(schema)` | Schema for update operations |
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
const user = new User();
|
|
291
|
+
|
|
292
|
+
// Get Zod schema for validating insert data
|
|
293
|
+
const createSchema = user.getSchema({ type: 'create' });
|
|
294
|
+
|
|
295
|
+
// Get Zod schema for validating query results
|
|
296
|
+
const selectSchema = user.getSchema({ type: 'select' });
|
|
297
|
+
|
|
298
|
+
// Get Zod schema for validating update data
|
|
299
|
+
const updateSchema = user.getSchema({ type: 'update' });
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
The `schemaFactory` is a static lazy singleton created via `drizzle-zod`'s `createSchemaFactory()`, shared across all `BaseEntity` instances to avoid per-entity overhead.
|
|
263
303
|
|
|
264
304
|
### Class Definition
|
|
265
305
|
|
|
@@ -288,7 +328,7 @@ export class BaseEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId>
|
|
|
288
328
|
// Constructor supports both patterns
|
|
289
329
|
constructor(opts?: { name?: string; schema?: Schema }) {
|
|
290
330
|
const ctor = new.target as typeof BaseEntity;
|
|
291
|
-
//
|
|
331
|
+
// Resolution order: opts.name > static TABLE_NAME > class name
|
|
292
332
|
const name = opts?.name ?? ctor.TABLE_NAME ?? ctor.name;
|
|
293
333
|
|
|
294
334
|
super({ scope: name });
|
|
@@ -323,7 +363,82 @@ export class BaseEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId>
|
|
|
323
363
|
}
|
|
324
364
|
```
|
|
325
365
|
|
|
326
|
-
|
|
366
|
+
## Key Types
|
|
367
|
+
|
|
368
|
+
### `TTableSchemaWithId`
|
|
369
|
+
|
|
370
|
+
Ensures a Drizzle `PgTable` has an `id` column:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
type TTableSchemaWithId<TC extends TableConfig = TableConfig> = PgTable<TC> & {
|
|
374
|
+
id: TIdColumn;
|
|
375
|
+
};
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### `TTableObject`
|
|
379
|
+
|
|
380
|
+
Infers the select (output) type from a table schema:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
type TTableObject<T extends TTableSchemaWithId> = T['$inferSelect'];
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### `TTableInsert`
|
|
387
|
+
|
|
388
|
+
Infers the insert (input) type from a table schema:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
type TTableInsert<T extends TTableSchemaWithId> = T['$inferInsert'];
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### `TGetIdType`
|
|
395
|
+
|
|
396
|
+
Extracts the `id` field type from a table schema:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
type TGetIdType<T extends TTableSchemaWithId> = TTableObject<T>['id'];
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### `IdType`
|
|
403
|
+
|
|
404
|
+
Union of supported ID types:
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
type NumberIdType = number;
|
|
408
|
+
type StringIdType = string;
|
|
409
|
+
type BigIntIdType = bigint;
|
|
410
|
+
type IdType = NumberIdType | StringIdType | BigIntIdType;
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### `TRelationConfig`
|
|
414
|
+
|
|
415
|
+
Configuration for entity relationships:
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
type TRelationConfig = {
|
|
419
|
+
name: string;
|
|
420
|
+
} & (
|
|
421
|
+
| { type: 'one'; schema: TTableSchemaWithId; metadata: /* Drizzle one() params */ }
|
|
422
|
+
| { type: 'many'; schema: TTableSchemaWithId; metadata: /* Drizzle many() params */ }
|
|
423
|
+
);
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Relation types are defined in the `RelationTypes` class:
|
|
427
|
+
|
|
428
|
+
| Type | Value | Description |
|
|
429
|
+
|------|-------|-------------|
|
|
430
|
+
| `RelationTypes.ONE` | `'one'` | One-to-one or many-to-one relationship |
|
|
431
|
+
| `RelationTypes.MANY` | `'many'` | One-to-many relationship |
|
|
432
|
+
|
|
433
|
+
### `TValueOrResolver`
|
|
434
|
+
|
|
435
|
+
From `@venizia/ignis-helpers`, enables lazy resolution to avoid circular dependencies:
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
type TValueOrResolver<T> = T | TResolver<T>; // T or () => T
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Used for `relations` on `BaseEntity` — store a function that returns the relations array, resolved lazily when `DataSource.buildSchema()` is called.
|
|
327
442
|
|
|
328
443
|
## Schema Enrichers
|
|
329
444
|
|
|
@@ -331,14 +446,16 @@ Enrichers are helper functions located in `packages/core/src/base/models/enriche
|
|
|
331
446
|
|
|
332
447
|
### Available Enrichers
|
|
333
448
|
|
|
334
|
-
| Enricher Function | Purpose |
|
|
335
|
-
| :--- | :--- |
|
|
336
|
-
| **`generateIdColumnDefs`** | Adds a primary key `id` column (string UUID or
|
|
337
|
-
| **`generateTzColumnDefs`** | Adds `createdAt`, `modifiedAt`, and `deletedAt` timestamp columns with timezone support. |
|
|
338
|
-
| **`generateUserAuditColumnDefs`** | Adds `createdBy` and `modifiedBy` columns to track user audit information. |
|
|
339
|
-
| **`
|
|
340
|
-
| **`
|
|
341
|
-
| **`extraUserColumns`** | Adds common
|
|
449
|
+
| Enricher Function | Convenience Wrapper | Purpose |
|
|
450
|
+
| :--- | :--- | :--- |
|
|
451
|
+
| **`generateIdColumnDefs`** | `enrichId` | Adds a primary key `id` column (string UUID, numeric integer, or big integer). |
|
|
452
|
+
| **`generateTzColumnDefs`** | `enrichTz` | Adds `createdAt`, `modifiedAt`, and `deletedAt` timestamp columns with timezone support. |
|
|
453
|
+
| **`generateUserAuditColumnDefs`** | `enrichUserAudit` | Adds `createdBy` and `modifiedBy` columns to track user audit information. |
|
|
454
|
+
| **`generatePrincipalColumnDefs`** | `enrichPrincipal` | Adds polymorphic principal columns (`{discriminator}Id` and `{discriminator}Type`). |
|
|
455
|
+
| **`generateDataTypeColumnDefs`** | `enrichDataTypes` | Adds generic data type columns (`dataType`, `nValue`, `tValue`, `bValue`, `jValue`, `boValue`) for flexible data storage. |
|
|
456
|
+
| **`extraUserColumns`** | — | Adds common user fields (`realm`, `status`, `type`, `activatedAt`, `lastLoginAt`, `parentId`). Imported from `@venizia/ignis` (part of auth component). |
|
|
457
|
+
|
|
458
|
+
Each `generate*` function returns column definition objects for spreading into `pgTable`. The `enrich*` convenience wrappers accept an existing `TColumnDefinitions` object as the first argument and merge the generated columns into it.
|
|
342
459
|
|
|
343
460
|
### Example Usage
|
|
344
461
|
|
|
@@ -353,7 +470,10 @@ import {
|
|
|
353
470
|
export const myTable = pgTable('MyTable', {
|
|
354
471
|
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
355
472
|
...generateTzColumnDefs(),
|
|
356
|
-
...generateUserAuditColumnDefs({
|
|
473
|
+
...generateUserAuditColumnDefs({
|
|
474
|
+
created: { dataType: 'string', columnName: 'created_by' },
|
|
475
|
+
modified: { dataType: 'string', columnName: 'modified_by' },
|
|
476
|
+
}),
|
|
357
477
|
name: text('name').notNull(),
|
|
358
478
|
});
|
|
359
479
|
```
|
|
@@ -542,6 +662,24 @@ export const myTable = pgTable('MyTable', {
|
|
|
542
662
|
- **Big Number Mode:** For `dataType: 'big-number'`, the `numberMode` field is required to specify whether to use JavaScript `number` (up to 2^53-1) or `bigint` (for larger values)
|
|
543
663
|
- **Sequence Options:** Available for `number` and `big-number` types to customize identity generation behavior
|
|
544
664
|
|
|
665
|
+
#### Convenience Wrapper: `enrichId`
|
|
666
|
+
|
|
667
|
+
```typescript
|
|
668
|
+
enrichId(baseColumns: TColumnDefinitions, opts?: TIdEnricherOptions): TColumnDefinitions
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
Merges the generated ID column into an existing column definitions object:
|
|
672
|
+
|
|
673
|
+
```typescript
|
|
674
|
+
import { text } from 'drizzle-orm/pg-core';
|
|
675
|
+
import { enrichId } from '@venizia/ignis';
|
|
676
|
+
|
|
677
|
+
const columns = enrichId(
|
|
678
|
+
{ name: text('name').notNull() },
|
|
679
|
+
{ id: { dataType: 'string' } },
|
|
680
|
+
);
|
|
681
|
+
```
|
|
682
|
+
|
|
545
683
|
|
|
546
684
|
### `generateTzColumnDefs`
|
|
547
685
|
|
|
@@ -552,7 +690,9 @@ Adds timestamp columns for tracking entity creation, modification, and soft dele
|
|
|
552
690
|
#### Signature
|
|
553
691
|
|
|
554
692
|
```typescript
|
|
555
|
-
generateTzColumnDefs
|
|
693
|
+
generateTzColumnDefs<Opts extends TTzEnricherOptions | undefined>(
|
|
694
|
+
opts?: Opts,
|
|
695
|
+
): TTzEnricherResult<Opts>
|
|
556
696
|
```
|
|
557
697
|
|
|
558
698
|
#### Options (`TTzEnricherOptions`)
|
|
@@ -579,7 +719,7 @@ The `modified` and `deleted` options use a discriminated union pattern:
|
|
|
579
719
|
| Column | Type | Constraints | Default | Description |
|
|
580
720
|
|--------|------|-------------|---------|-------------|
|
|
581
721
|
| `createdAt` | `timestamp` | `NOT NULL` | `now()` | When the record was created (always included) |
|
|
582
|
-
| `modifiedAt` | `timestamp` | `NOT NULL` | `now()` | When the record was last modified (optional, enabled by default) |
|
|
722
|
+
| `modifiedAt` | `timestamp` | `NOT NULL` | `now()`, auto-updates via `$onUpdate(() => new Date())` | When the record was last modified (optional, enabled by default) |
|
|
583
723
|
| `deletedAt` | `timestamp` | nullable | `null` | When the record was soft-deleted (optional, **disabled by default**) |
|
|
584
724
|
|
|
585
725
|
#### Usage Examples
|
|
@@ -685,16 +825,27 @@ await db.update(myTable)
|
|
|
685
825
|
|
|
686
826
|
#### Type Inference
|
|
687
827
|
|
|
688
|
-
The enricher provides
|
|
828
|
+
The enricher provides **conditional TypeScript type inference** based on the options:
|
|
689
829
|
|
|
690
830
|
```typescript
|
|
691
|
-
type TTzEnricherResult<
|
|
692
|
-
createdAt: PgTimestampBuilderInitial<string
|
|
693
|
-
|
|
694
|
-
deletedAt
|
|
695
|
-
};
|
|
831
|
+
type TTzEnricherResult<Opts extends TTzEnricherOptions | undefined = undefined> = {
|
|
832
|
+
createdAt: NotNull<HasDefault<PgTimestampBuilderInitial<string>>>;
|
|
833
|
+
} & (/* modifiedAt included unless opts.modified.enable === false */)
|
|
834
|
+
& (/* deletedAt included only when opts.deleted.enable === true */);
|
|
696
835
|
```
|
|
697
836
|
|
|
837
|
+
- `createdAt` is always present
|
|
838
|
+
- `modifiedAt` is present by default; excluded only when `modified: { enable: false }`
|
|
839
|
+
- `deletedAt` is absent by default; included only when `deleted: { enable: true, ... }`
|
|
840
|
+
|
|
841
|
+
#### Convenience Wrapper: `enrichTz`
|
|
842
|
+
|
|
843
|
+
```typescript
|
|
844
|
+
enrichTz(baseSchema: TColumnDefinitions, opts?: TTzEnricherOptions): TColumnDefinitions
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
Merges timestamp columns into an existing column definitions object.
|
|
848
|
+
|
|
698
849
|
|
|
699
850
|
### `generateUserAuditColumnDefs`
|
|
700
851
|
|
|
@@ -730,6 +881,15 @@ type TUserAuditEnricherOptions = {
|
|
|
730
881
|
- `created`: `{ dataType: 'number', columnName: 'created_by', allowAnonymous: true }`
|
|
731
882
|
- `modified`: `{ dataType: 'number', columnName: 'modified_by', allowAnonymous: true }`
|
|
732
883
|
|
|
884
|
+
#### How It Works
|
|
885
|
+
|
|
886
|
+
The enricher uses Hono's `contextStorage` (via `tryGetContext()`) to automatically retrieve the current user ID from the request context at insert/update time:
|
|
887
|
+
|
|
888
|
+
- **`createdBy`**: Set via `$default()` — only populated on record creation
|
|
889
|
+
- **`modifiedBy`**: Set via both `$default()` and `$onUpdate()` — populated on creation and updated on every modification
|
|
890
|
+
|
|
891
|
+
The user ID is read from the `Authentication.AUDIT_USER_ID` key in the Hono context.
|
|
892
|
+
|
|
733
893
|
#### `allowAnonymous` Behavior
|
|
734
894
|
|
|
735
895
|
The `allowAnonymous` option controls whether the enricher requires an authenticated user context:
|
|
@@ -749,6 +909,9 @@ The `allowAnonymous` option controls whether the enricher requires an authentica
|
|
|
749
909
|
- System-generated records
|
|
750
910
|
- Tables that allow both authenticated and anonymous operations
|
|
751
911
|
|
|
912
|
+
> [!WARNING]
|
|
913
|
+
> Fire-and-forget promises may run outside the async context, losing access to `AUDIT_USER_ID`. Ensure audit-critical operations complete within the request lifecycle.
|
|
914
|
+
|
|
752
915
|
#### Generated Columns
|
|
753
916
|
|
|
754
917
|
| Column | Data Type | Column Name | Description |
|
|
@@ -761,11 +924,11 @@ The `allowAnonymous` option controls whether the enricher requires an authentica
|
|
|
761
924
|
The enricher validates the `dataType` option and throws an error for invalid values:
|
|
762
925
|
|
|
763
926
|
```typescript
|
|
764
|
-
//
|
|
927
|
+
// Valid
|
|
765
928
|
generateUserAuditColumnDefs({ created: { dataType: 'number', columnName: 'created_by' } });
|
|
766
929
|
generateUserAuditColumnDefs({ created: { dataType: 'string', columnName: 'created_by' } });
|
|
767
930
|
|
|
768
|
-
//
|
|
931
|
+
// Invalid - throws error
|
|
769
932
|
generateUserAuditColumnDefs({ created: { dataType: 'uuid', columnName: 'created_by' } });
|
|
770
933
|
// Error: [enrichUserAudit] Invalid dataType for 'createdBy' | value: uuid | valid: ['number', 'string']
|
|
771
934
|
```
|
|
@@ -841,6 +1004,136 @@ export const auditLogTable = pgTable('AuditLog', {
|
|
|
841
1004
|
// Error: [getCurrentUserId] Invalid request context to identify user | columnName: createdBy | allowAnonymous: false
|
|
842
1005
|
```
|
|
843
1006
|
|
|
1007
|
+
#### Convenience Wrapper: `enrichUserAudit`
|
|
1008
|
+
|
|
1009
|
+
```typescript
|
|
1010
|
+
enrichUserAudit<ColumnDefinitions extends TColumnDefinitions>(
|
|
1011
|
+
baseSchema: ColumnDefinitions,
|
|
1012
|
+
opts?: TUserAuditEnricherOptions,
|
|
1013
|
+
): TUserAuditEnricherResult<ColumnDefinitions>
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
Merges user audit columns into an existing column definitions object with proper type inference.
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
### `generatePrincipalColumnDefs`
|
|
1020
|
+
|
|
1021
|
+
Adds polymorphic principal columns for associating a record with different entity types. This is the polymorphic association pattern where a row can belong to different parent types (e.g., a comment can belong to a Post, User, or Product).
|
|
1022
|
+
|
|
1023
|
+
**File:** `packages/core/src/base/models/enrichers/principal.enricher.ts`
|
|
1024
|
+
|
|
1025
|
+
#### Signature
|
|
1026
|
+
|
|
1027
|
+
```typescript
|
|
1028
|
+
generatePrincipalColumnDefs<
|
|
1029
|
+
Discriminator extends string = 'principal',
|
|
1030
|
+
IdType extends 'number' | 'string' = 'number',
|
|
1031
|
+
>(
|
|
1032
|
+
opts: TPrincipalEnricherOptions<Discriminator, IdType>,
|
|
1033
|
+
): TPrincipalColumnDef<Discriminator, IdType>
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
#### Options (`TPrincipalEnricherOptions`)
|
|
1037
|
+
|
|
1038
|
+
```typescript
|
|
1039
|
+
type TPrincipalEnricherOptions<
|
|
1040
|
+
Discriminator extends string = string,
|
|
1041
|
+
IdType extends 'number' | 'string' = 'number' | 'string',
|
|
1042
|
+
> = {
|
|
1043
|
+
discriminator?: Discriminator; // Field name prefix (default: 'principal')
|
|
1044
|
+
defaultPolymorphic?: string; // Default value for the type column (default: '')
|
|
1045
|
+
polymorphicIdType: IdType; // Required - type of the principal ID column
|
|
1046
|
+
};
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
| Option | Type | Default | Description |
|
|
1050
|
+
|--------|------|---------|-------------|
|
|
1051
|
+
| `discriminator` | `string` | `'principal'` | Prefix for generated column names |
|
|
1052
|
+
| `defaultPolymorphic` | `string` | `''` | Default value for the type discriminator column |
|
|
1053
|
+
| `polymorphicIdType` | `'number' \| 'string'` | (required) | Data type of the ID column |
|
|
1054
|
+
|
|
1055
|
+
#### Generated Columns
|
|
1056
|
+
|
|
1057
|
+
Given `discriminator = 'principal'` (default):
|
|
1058
|
+
|
|
1059
|
+
| Column | DB Column Name | Type | Constraints | Description |
|
|
1060
|
+
|--------|---------------|------|-------------|-------------|
|
|
1061
|
+
| `principalId` | `principal_id` | `integer` or `text` | `NOT NULL` | The ID of the associated entity |
|
|
1062
|
+
| `principalType` | `principal_type` | `text` | `DEFAULT ''` | The type discriminator (e.g., `'User'`, `'Post'`) |
|
|
1063
|
+
|
|
1064
|
+
With a custom discriminator (e.g., `discriminator: 'owner'`):
|
|
1065
|
+
|
|
1066
|
+
| Column | DB Column Name | Type |
|
|
1067
|
+
|--------|---------------|------|
|
|
1068
|
+
| `ownerId` | `owner_id` | `integer` or `text` |
|
|
1069
|
+
| `ownerType` | `owner_type` | `text` |
|
|
1070
|
+
|
|
1071
|
+
#### Usage Examples
|
|
1072
|
+
|
|
1073
|
+
**Default (polymorphic principal with numeric ID):**
|
|
1074
|
+
|
|
1075
|
+
```typescript
|
|
1076
|
+
import { pgTable, text } from 'drizzle-orm/pg-core';
|
|
1077
|
+
import { generateIdColumnDefs, generatePrincipalColumnDefs } from '@venizia/ignis';
|
|
1078
|
+
|
|
1079
|
+
export const commentTable = pgTable('Comment', {
|
|
1080
|
+
...generateIdColumnDefs(),
|
|
1081
|
+
...generatePrincipalColumnDefs({ polymorphicIdType: 'number' }),
|
|
1082
|
+
content: text('content').notNull(),
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
// Generates:
|
|
1086
|
+
// principalId: integer('principal_id').notNull()
|
|
1087
|
+
// principalType: text('principal_type').default('')
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
**Custom discriminator name:**
|
|
1091
|
+
|
|
1092
|
+
```typescript
|
|
1093
|
+
export const attachmentTable = pgTable('Attachment', {
|
|
1094
|
+
...generateIdColumnDefs(),
|
|
1095
|
+
...generatePrincipalColumnDefs({
|
|
1096
|
+
discriminator: 'owner',
|
|
1097
|
+
polymorphicIdType: 'string',
|
|
1098
|
+
defaultPolymorphic: 'User',
|
|
1099
|
+
}),
|
|
1100
|
+
filePath: text('file_path').notNull(),
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
// Generates:
|
|
1104
|
+
// ownerId: text('owner_id').notNull()
|
|
1105
|
+
// ownerType: text('owner_type').default('User')
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
**Polymorphic association pattern:**
|
|
1109
|
+
|
|
1110
|
+
```typescript
|
|
1111
|
+
// A notification can belong to different entity types
|
|
1112
|
+
export const notificationTable = pgTable('Notification', {
|
|
1113
|
+
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
1114
|
+
...generatePrincipalColumnDefs({
|
|
1115
|
+
discriminator: 'target',
|
|
1116
|
+
polymorphicIdType: 'string',
|
|
1117
|
+
}),
|
|
1118
|
+
message: text('message').notNull(),
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
// Usage:
|
|
1122
|
+
// { targetId: 'user-123', targetType: 'User', message: 'Welcome!' }
|
|
1123
|
+
// { targetId: 'order-456', targetType: 'Order', message: 'Order shipped' }
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
#### Convenience Wrapper: `enrichPrincipal`
|
|
1127
|
+
|
|
1128
|
+
```typescript
|
|
1129
|
+
enrichPrincipal<ColumnDefinitions extends TColumnDefinitions>(
|
|
1130
|
+
baseSchema: ColumnDefinitions,
|
|
1131
|
+
opts: TPrincipalEnricherOptions,
|
|
1132
|
+
): ColumnDefinitions & TPrincipalColumnDef
|
|
1133
|
+
```
|
|
1134
|
+
|
|
1135
|
+
Merges principal columns into an existing column definitions object.
|
|
1136
|
+
|
|
844
1137
|
|
|
845
1138
|
### `generateDataTypeColumnDefs`
|
|
846
1139
|
|
|
@@ -942,11 +1235,7 @@ export class AppConfig extends BaseEntity<typeof AppConfig.schema> {
|
|
|
942
1235
|
// { key: 'is_maintenance', dataType: 'boolean', boValue: false }
|
|
943
1236
|
```
|
|
944
1237
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
A convenience function that merges data type columns into an existing schema object, rather than spreading into `pgTable`.
|
|
948
|
-
|
|
949
|
-
#### Signature
|
|
1238
|
+
#### Convenience Wrapper: `enrichDataTypes`
|
|
950
1239
|
|
|
951
1240
|
```typescript
|
|
952
1241
|
enrichDataTypes(
|
|
@@ -955,7 +1244,7 @@ enrichDataTypes(
|
|
|
955
1244
|
): TColumnDefinitions
|
|
956
1245
|
```
|
|
957
1246
|
|
|
958
|
-
|
|
1247
|
+
Merges data type columns into an existing column definitions object:
|
|
959
1248
|
|
|
960
1249
|
```typescript
|
|
961
1250
|
import { text } from 'drizzle-orm/pg-core';
|
|
@@ -972,11 +1261,56 @@ const allColumns = enrichDataTypes(baseColumns);
|
|
|
972
1261
|
export const configTable = pgTable('Config', allColumns);
|
|
973
1262
|
```
|
|
974
1263
|
|
|
975
|
-
This is equivalent to spreading `generateDataTypeColumnDefs()` directly but useful when building column definitions programmatically.
|
|
976
|
-
|
|
977
1264
|
|
|
978
1265
|
## Schema Utilities
|
|
979
1266
|
|
|
1267
|
+
### `idParamsSchema`
|
|
1268
|
+
|
|
1269
|
+
Generates a Zod schema for path parameters containing an `id` field, suitable for OpenAPI route definitions.
|
|
1270
|
+
|
|
1271
|
+
**File:** `packages/core/src/base/models/common/types.ts`
|
|
1272
|
+
|
|
1273
|
+
#### Signature
|
|
1274
|
+
|
|
1275
|
+
```typescript
|
|
1276
|
+
idParamsSchema(opts?: { idType: string }): z.ZodObject<{ id: z.ZodNumber | z.ZodString }>
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
| `idType` | Default | Zod Type | Examples |
|
|
1280
|
+
|----------|---------|----------|----------|
|
|
1281
|
+
| `'number'` | Yes | `z.number()` | `[1, 2, 3]` |
|
|
1282
|
+
| `'string'` | | `z.string()` | `['4651e634-...', 'some_unique_id']` |
|
|
1283
|
+
|
|
1284
|
+
Throws an error for invalid `idType` values.
|
|
1285
|
+
|
|
1286
|
+
### `jsonContent`
|
|
1287
|
+
|
|
1288
|
+
Creates an OpenAPI JSON content specification:
|
|
1289
|
+
|
|
1290
|
+
```typescript
|
|
1291
|
+
jsonContent<T extends z.ZodType>(opts: {
|
|
1292
|
+
schema: T;
|
|
1293
|
+
description: string;
|
|
1294
|
+
required?: boolean;
|
|
1295
|
+
}): { description, content: { 'application/json': { schema } }, required? }
|
|
1296
|
+
```
|
|
1297
|
+
|
|
1298
|
+
### `jsonResponse`
|
|
1299
|
+
|
|
1300
|
+
Creates a complete OpenAPI response specification with success and error responses:
|
|
1301
|
+
|
|
1302
|
+
```typescript
|
|
1303
|
+
jsonResponse<ContentSchema, HeaderSchema>(opts: {
|
|
1304
|
+
schema: ContentSchema;
|
|
1305
|
+
description?: string; // Default: 'Success Response'
|
|
1306
|
+
required?: boolean;
|
|
1307
|
+
headers?: HeaderSchema;
|
|
1308
|
+
}): {
|
|
1309
|
+
200: { description, content, headers? },
|
|
1310
|
+
'4xx | 5xx': { description: 'Error Response', content: ErrorSchema }
|
|
1311
|
+
}
|
|
1312
|
+
```
|
|
1313
|
+
|
|
980
1314
|
### `snakeToCamel`
|
|
981
1315
|
|
|
982
1316
|
Converts a Zod schema from snake_case to camelCase, transforming both the schema shape and runtime data.
|
|
@@ -1043,7 +1377,7 @@ console.log(result);
|
|
|
1043
1377
|
**Use case:** API endpoint that accepts snake_case but works with camelCase internally
|
|
1044
1378
|
|
|
1045
1379
|
```typescript
|
|
1046
|
-
import {
|
|
1380
|
+
import { BaseRestController, controller, snakeToCamel } from '@venizia/ignis';
|
|
1047
1381
|
import { HTTP } from '@venizia/ignis-helpers';
|
|
1048
1382
|
import { z } from '@hono/zod-openapi';
|
|
1049
1383
|
|
|
@@ -1055,7 +1389,7 @@ const createUserSchema = snakeToCamel({
|
|
|
1055
1389
|
});
|
|
1056
1390
|
|
|
1057
1391
|
@controller({ path: '/users' })
|
|
1058
|
-
export class UserController extends
|
|
1392
|
+
export class UserController extends BaseRestController {
|
|
1059
1393
|
override binding() {
|
|
1060
1394
|
this.bindRoute({
|
|
1061
1395
|
configs: {
|
|
@@ -1073,18 +1407,18 @@ export class UserController extends BaseController {
|
|
|
1073
1407
|
handler: async (ctx) => {
|
|
1074
1408
|
// Request body is automatically camelCase
|
|
1075
1409
|
const data = ctx.req.valid('json');
|
|
1076
|
-
|
|
1410
|
+
|
|
1077
1411
|
// data = {
|
|
1078
1412
|
// firstName: string,
|
|
1079
1413
|
// lastName: string,
|
|
1080
1414
|
// emailAddress: string,
|
|
1081
1415
|
// phoneNumber?: string
|
|
1082
1416
|
// }
|
|
1083
|
-
|
|
1417
|
+
|
|
1084
1418
|
// Work with camelCase data
|
|
1085
|
-
console.log(data.firstName); //
|
|
1086
|
-
console.log(data.first_name); //
|
|
1087
|
-
|
|
1419
|
+
console.log(data.firstName); // TypeScript knows this exists
|
|
1420
|
+
console.log(data.first_name); // TypeScript error
|
|
1421
|
+
|
|
1088
1422
|
return ctx.json({ success: true }, HTTP.ResultCodes.RS_2.Ok);
|
|
1089
1423
|
},
|
|
1090
1424
|
});
|
|
@@ -1097,13 +1431,13 @@ export class UserController extends BaseController {
|
|
|
1097
1431
|
The utility includes sophisticated TypeScript type transformation:
|
|
1098
1432
|
|
|
1099
1433
|
```typescript
|
|
1100
|
-
type TSnakeToCamelCase<S extends string> =
|
|
1434
|
+
type TSnakeToCamelCase<S extends string> =
|
|
1101
1435
|
S extends `${infer T}_${infer U}`
|
|
1102
1436
|
? `${T}${Capitalize<TSnakeToCamelCase<U>>}`
|
|
1103
1437
|
: S;
|
|
1104
1438
|
|
|
1105
1439
|
type TCamelCaseKeys<T extends z.ZodRawShape> = {
|
|
1106
|
-
[K in keyof T as K extends string ? TSnakeToCamelCase<K> : K]:
|
|
1440
|
+
[K in keyof T as K extends string ? TSnakeToCamelCase<K> : K]:
|
|
1107
1441
|
T[K] extends z.ZodType<infer U> ? z.ZodType<U> : T[K];
|
|
1108
1442
|
};
|
|
1109
1443
|
```
|
|
@@ -1121,7 +1455,7 @@ The schema validates twice for safety:
|
|
|
1121
1455
|
```typescript
|
|
1122
1456
|
// If validation fails at any step, you get clear error messages
|
|
1123
1457
|
const invalidData = {
|
|
1124
|
-
user_id: 'not-a-number', //
|
|
1458
|
+
user_id: 'not-a-number', // Fails first validation
|
|
1125
1459
|
first_name: 'John',
|
|
1126
1460
|
last_name: 'Doe',
|
|
1127
1461
|
};
|
|
@@ -1140,6 +1474,16 @@ try {
|
|
|
1140
1474
|
- Preserves array structures
|
|
1141
1475
|
- Works seamlessly with Zod's other features (refinements, transforms, etc.)
|
|
1142
1476
|
|
|
1477
|
+
### `getIdType`
|
|
1478
|
+
|
|
1479
|
+
Utility function to determine the data type of an entity's `id` column at runtime:
|
|
1480
|
+
|
|
1481
|
+
```typescript
|
|
1482
|
+
getIdType<T extends TTableSchemaWithId>(opts: { entity: T }): string
|
|
1483
|
+
```
|
|
1484
|
+
|
|
1485
|
+
Returns the `dataType` property of the entity's `id` column (e.g., `'number'`, `'string'`), or `'unknown'` if not determinable.
|
|
1486
|
+
|
|
1143
1487
|
## See Also
|
|
1144
1488
|
|
|
1145
1489
|
- **Related Concepts:**
|