@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.
Files changed (161) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  6. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  8. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  9. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  11. package/package.json +1 -1
  12. package/wiki/best-practices/api-usage-examples.md +9 -9
  13. package/wiki/best-practices/architectural-patterns.md +19 -3
  14. package/wiki/best-practices/architecture-decisions.md +6 -6
  15. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  16. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  17. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  18. package/wiki/best-practices/code-style-standards/index.md +2 -2
  19. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  20. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  21. package/wiki/best-practices/data-modeling.md +1 -1
  22. package/wiki/best-practices/deployment-strategies.md +1 -1
  23. package/wiki/best-practices/error-handling.md +2 -2
  24. package/wiki/best-practices/performance-optimization.md +3 -3
  25. package/wiki/best-practices/security-guidelines.md +2 -2
  26. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  27. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  28. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  29. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  30. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  31. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  32. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  33. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  34. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  35. package/wiki/{references → extensions}/components/health-check.md +5 -4
  36. package/wiki/{references → extensions}/components/index.md +2 -0
  37. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  38. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  39. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  40. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  41. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  42. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  43. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  44. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  45. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  46. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  47. package/wiki/{references → extensions}/components/swagger.md +3 -3
  48. package/wiki/{references → extensions}/components/template/index.md +4 -4
  49. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  50. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  51. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  52. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  53. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  54. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  55. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  56. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  57. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  58. package/wiki/{references → extensions}/helpers/index.md +18 -6
  59. package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
  60. package/wiki/extensions/helpers/kafka/consumer.md +384 -0
  61. package/wiki/extensions/helpers/kafka/examples.md +361 -0
  62. package/wiki/extensions/helpers/kafka/index.md +639 -0
  63. package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
  64. package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
  65. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  66. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  67. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  68. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  69. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  70. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  71. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  72. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  73. package/wiki/extensions/index.md +48 -0
  74. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  75. package/wiki/guides/core-concepts/application/index.md +95 -35
  76. package/wiki/guides/core-concepts/components-guide.md +23 -19
  77. package/wiki/guides/core-concepts/components.md +34 -10
  78. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  79. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  80. package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
  81. package/wiki/guides/core-concepts/persistent/models.md +43 -1
  82. package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
  83. package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
  84. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
  85. package/wiki/guides/core-concepts/services.md +19 -5
  86. package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
  87. package/wiki/guides/get-started/philosophy.md +1 -1
  88. package/wiki/guides/index.md +2 -2
  89. package/wiki/guides/reference/glossary.md +7 -7
  90. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  91. package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
  92. package/wiki/guides/tutorials/complete-installation.md +17 -14
  93. package/wiki/guides/tutorials/ecommerce-api.md +18 -18
  94. package/wiki/guides/tutorials/realtime-chat.md +8 -8
  95. package/wiki/guides/tutorials/testing.md +2 -2
  96. package/wiki/index.md +4 -3
  97. package/wiki/references/base/application.md +341 -21
  98. package/wiki/references/base/bootstrapping.md +43 -13
  99. package/wiki/references/base/components.md +259 -8
  100. package/wiki/references/base/controllers.md +556 -253
  101. package/wiki/references/base/datasources.md +159 -79
  102. package/wiki/references/base/dependency-injection.md +299 -48
  103. package/wiki/references/base/filter-system/application-usage.md +18 -2
  104. package/wiki/references/base/filter-system/array-operators.md +14 -6
  105. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  106. package/wiki/references/base/filter-system/default-filter.md +28 -3
  107. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  108. package/wiki/references/base/filter-system/index.md +169 -11
  109. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  110. package/wiki/references/base/filter-system/list-operators.md +4 -3
  111. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  112. package/wiki/references/base/filter-system/null-operators.md +50 -0
  113. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  114. package/wiki/references/base/filter-system/range-operators.md +7 -1
  115. package/wiki/references/base/filter-system/tips.md +34 -7
  116. package/wiki/references/base/filter-system/use-cases.md +6 -5
  117. package/wiki/references/base/grpc-controllers.md +984 -0
  118. package/wiki/references/base/index.md +32 -24
  119. package/wiki/references/base/middleware.md +347 -0
  120. package/wiki/references/base/models.md +390 -46
  121. package/wiki/references/base/providers.md +14 -14
  122. package/wiki/references/base/repositories/advanced.md +84 -69
  123. package/wiki/references/base/repositories/index.md +447 -12
  124. package/wiki/references/base/repositories/mixins.md +103 -98
  125. package/wiki/references/base/repositories/relations.md +129 -45
  126. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  127. package/wiki/references/base/services.md +94 -14
  128. package/wiki/references/index.md +12 -10
  129. package/wiki/references/quick-reference.md +98 -65
  130. package/wiki/references/utilities/crypto.md +21 -4
  131. package/wiki/references/utilities/date.md +25 -7
  132. package/wiki/references/utilities/index.md +26 -24
  133. package/wiki/references/utilities/jsx.md +54 -54
  134. package/wiki/references/utilities/module.md +8 -6
  135. package/wiki/references/utilities/parse.md +16 -9
  136. package/wiki/references/utilities/performance.md +22 -7
  137. package/wiki/references/utilities/promise.md +19 -16
  138. package/wiki/references/utilities/request.md +48 -26
  139. package/wiki/references/utilities/schema.md +69 -6
  140. package/wiki/references/utilities/statuses.md +131 -140
  141. package/wiki/references/helpers/kafka/consumer.md +0 -473
  142. package/wiki/references/helpers/kafka/examples.md +0 -234
  143. package/wiki/references/helpers/kafka/index.md +0 -482
  144. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  145. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  146. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  147. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  148. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  149. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  150. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  151. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  152. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  153. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  154. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  155. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  156. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  157. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  158. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  159. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  160. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  161. /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 (`SELECT`, `CREATE`, `UPDATE`) |
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 (defaults to class 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](/references/components/authorization/usage#model-based-resource-references)) |
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 (`SELECT`, `CREATE`, `UPDATE`) |
261
- | `toObject()` | Convert to plain object |
262
- | `toJSON()` | Convert to JSON string |
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
- // Use explicit TABLE_NAME if defined, otherwise fall back to class name
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
- **Performance Note:** The `schemaFactory` is implemented as a static lazy singleton, meaning it's created once and shared across all `BaseEntity` instances. This avoids the overhead of creating a new `drizzle-zod` schema factory for every entity instantiation.
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 numeric serial). |
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
- | **`generateDataTypeColumnDefs`** | Adds generic data type columns (`dataType`, `nValue`, `tValue`, `bValue`, `jValue`, `boValue`) for flexible data storage. |
340
- | **`generatePrincipalColumnDefs`** | Adds polymorphic fields for associating with different principal types. |
341
- | **`extraUserColumns`** | Adds common fields for a user model, such as `realm`, `status`, `type`, `activatedAt`, `lastLoginAt`, and `parentId`. Import from `@venizia/ignis`. |
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({ created: { dataType: 'string' }, modified: { dataType: 'string' } }),
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(opts?: TTzEnricherOptions): TTzEnricherResult
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 proper TypeScript type inference:
828
+ The enricher provides **conditional TypeScript type inference** based on the options:
689
829
 
690
830
  ```typescript
691
- type TTzEnricherResult<ColumnDefinitions extends TColumnDefinitions = TColumnDefinitions> = {
692
- createdAt: PgTimestampBuilderInitial<string> & NotNull & HasDefault;
693
- modifiedAt?: PgTimestampBuilderInitial<string> & NotNull & HasDefault;
694
- deletedAt?: PgTimestampBuilderInitial<string>;
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
- // Valid
927
+ // Valid
765
928
  generateUserAuditColumnDefs({ created: { dataType: 'number', columnName: 'created_by' } });
766
929
  generateUserAuditColumnDefs({ created: { dataType: 'string', columnName: 'created_by' } });
767
930
 
768
- // Invalid - throws error
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
- ### `enrichDataTypes`
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
- #### Usage
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 { BaseController, controller, snakeToCamel } from '@venizia/ignis';
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 BaseController {
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); // TypeScript knows this exists
1086
- console.log(data.first_name); // TypeScript error
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', // Fails first validation
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:**