@venizia/ignis-docs 0.2.1-0 → 0.2.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/content/best-practices/architectural-patterns.md +3 -3
  2. package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
  3. package/content/best-practices/contribution-workflow.md +2 -2
  4. package/content/best-practices/error-handling.md +94 -89
  5. package/content/best-practices/security-guidelines.md +5 -5
  6. package/content/extensions/components/api-reference.md +22 -21
  7. package/content/extensions/components/authentication/api.md +64 -28
  8. package/content/extensions/components/authentication/errors.md +19 -5
  9. package/content/extensions/components/authentication/index.md +19 -18
  10. package/content/extensions/components/authentication/usage.md +55 -30
  11. package/content/extensions/components/authorization/api.md +351 -84
  12. package/content/extensions/components/authorization/errors.md +51 -17
  13. package/content/extensions/components/authorization/getting-started.md +227 -0
  14. package/content/extensions/components/authorization/index.md +24 -16
  15. package/content/extensions/components/authorization/usage.md +45 -21
  16. package/content/extensions/components/health-check.md +15 -9
  17. package/content/extensions/components/index.md +24 -90
  18. package/content/extensions/components/mail/api.md +105 -54
  19. package/content/extensions/components/mail/errors.md +12 -10
  20. package/content/extensions/components/mail/index.md +32 -13
  21. package/content/extensions/components/mail/usage.md +26 -18
  22. package/content/extensions/components/request-tracker.md +18 -14
  23. package/content/extensions/components/socket-io/api.md +377 -882
  24. package/content/extensions/components/socket-io/errors.md +49 -51
  25. package/content/extensions/components/socket-io/index.md +72 -88
  26. package/content/extensions/components/socket-io/usage.md +107 -117
  27. package/content/extensions/components/static-asset/api.md +83 -31
  28. package/content/extensions/components/static-asset/errors.md +18 -7
  29. package/content/extensions/components/static-asset/index.md +18 -11
  30. package/content/extensions/components/static-asset/usage.md +11 -6
  31. package/content/extensions/components/template/index.md +3 -3
  32. package/content/extensions/components/websocket/api.md +58 -27
  33. package/content/extensions/components/websocket/errors.md +3 -3
  34. package/content/extensions/components/websocket/index.md +8 -7
  35. package/content/extensions/components/websocket/usage.md +21 -8
  36. package/content/extensions/helpers/cron/index.md +8 -7
  37. package/content/extensions/helpers/crypto/index.md +16 -8
  38. package/content/extensions/helpers/crypto/reference.md +96 -24
  39. package/content/extensions/helpers/env/index.md +14 -10
  40. package/content/extensions/helpers/error/index.md +99 -23
  41. package/content/extensions/helpers/index.md +61 -47
  42. package/content/extensions/helpers/inversion/index.md +23 -6
  43. package/content/extensions/helpers/inversion/reference.md +30 -22
  44. package/content/extensions/helpers/kafka/admin.md +3 -2
  45. package/content/extensions/helpers/kafka/compile-binary.md +69 -44
  46. package/content/extensions/helpers/kafka/consumer.md +24 -21
  47. package/content/extensions/helpers/kafka/examples.md +22 -234
  48. package/content/extensions/helpers/kafka/index.md +30 -62
  49. package/content/extensions/helpers/kafka/producer.md +31 -26
  50. package/content/extensions/helpers/kafka/schema-registry.md +19 -14
  51. package/content/extensions/helpers/logger/hf-logger.md +49 -22
  52. package/content/extensions/helpers/logger/index.md +37 -12
  53. package/content/extensions/helpers/logger/pino.md +31 -11
  54. package/content/extensions/helpers/logger/reference.md +243 -52
  55. package/content/extensions/helpers/network/api.md +65 -30
  56. package/content/extensions/helpers/network/index.md +32 -11
  57. package/content/extensions/helpers/queue/index.md +17 -6
  58. package/content/extensions/helpers/queue/reference.md +52 -25
  59. package/content/extensions/helpers/redis/index.md +29 -11
  60. package/content/extensions/helpers/redis/reference.md +85 -28
  61. package/content/extensions/helpers/secrets/index.md +82 -12
  62. package/content/extensions/helpers/socket-io/api.md +40 -21
  63. package/content/extensions/helpers/socket-io/index.md +26 -10
  64. package/content/extensions/helpers/storage/api.md +42 -24
  65. package/content/extensions/helpers/storage/index.md +10 -7
  66. package/content/extensions/helpers/types/index.md +20 -7
  67. package/content/extensions/helpers/types/reference.md +53 -14
  68. package/content/extensions/helpers/uid/index.md +166 -10
  69. package/content/extensions/helpers/websocket/api.md +52 -13
  70. package/content/extensions/helpers/websocket/index.md +7 -4
  71. package/content/extensions/helpers/worker-thread/index.md +9 -5
  72. package/content/extensions/helpers/worker-thread/reference.md +20 -20
  73. package/content/extensions/index.md +38 -39
  74. package/content/extensions/src-details/mcp-server.md +96 -548
  75. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  76. package/content/guides/core-concepts/persistent/index.md +5 -1
  77. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  78. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  79. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  80. package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
  81. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  82. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  83. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  84. package/content/guides/get-started/philosophy.md +135 -670
  85. package/content/guides/get-started/setup.md +53 -74
  86. package/content/guides/migrations/redis-helpers-migration.md +4 -3
  87. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  88. package/content/guides/migrations/unified-connectors-migration.md +7 -6
  89. package/content/guides/tutorials/realtime-chat.md +1 -1
  90. package/content/references/base/application.md +63 -21
  91. package/content/references/base/components.md +3 -3
  92. package/content/references/base/connectors.md +14 -14
  93. package/content/references/base/controllers.md +12 -12
  94. package/content/references/base/datasources-reference.md +32 -31
  95. package/content/references/base/datasources.md +6 -6
  96. package/content/references/base/dependency-injection.md +12 -11
  97. package/content/references/base/filter-system/application-usage.md +54 -30
  98. package/content/references/base/filter-system/array-operators.md +24 -46
  99. package/content/references/base/filter-system/comparison-operators.md +47 -67
  100. package/content/references/base/filter-system/default-filter.md +59 -53
  101. package/content/references/base/filter-system/fields-order-pagination.md +92 -146
  102. package/content/references/base/filter-system/index.md +25 -13
  103. package/content/references/base/filter-system/json-filtering.md +45 -184
  104. package/content/references/base/filter-system/list-operators.md +23 -53
  105. package/content/references/base/filter-system/logical-operators.md +63 -121
  106. package/content/references/base/filter-system/null-operators.md +34 -104
  107. package/content/references/base/filter-system/pattern-matching.md +40 -55
  108. package/content/references/base/filter-system/quick-reference.md +86 -198
  109. package/content/references/base/filter-system/range-operators.md +18 -46
  110. package/content/references/base/filter-system/tips.md +6 -6
  111. package/content/references/base/filter-system/use-cases.md +33 -15
  112. package/content/references/base/grpc-controllers.md +53 -17
  113. package/content/references/base/index.md +5 -3
  114. package/content/references/base/middlewares.md +11 -10
  115. package/content/references/base/models-reference.md +17 -17
  116. package/content/references/base/models.md +4 -3
  117. package/content/references/base/providers.md +8 -8
  118. package/content/references/base/repositories/advanced.md +224 -326
  119. package/content/references/base/repositories/index.md +19 -6
  120. package/content/references/base/repositories/mixins.md +5 -5
  121. package/content/references/base/repositories/relations.md +160 -293
  122. package/content/references/base/repositories/soft-deletable.md +16 -6
  123. package/content/references/base/secrets.md +17 -13
  124. package/content/references/base/services.md +6 -4
  125. package/content/references/configuration/environment-variables.md +31 -23
  126. package/content/references/configuration/index.md +6 -4
  127. package/content/references/index.md +1 -1
  128. package/content/references/utilities/duration.md +85 -0
  129. package/content/references/utilities/index.md +5 -1
  130. package/content/references/utilities/jsx-reference.md +11 -11
  131. package/content/references/utilities/jsx.md +2 -2
  132. package/content/references/utilities/module.md +78 -25
  133. package/content/references/utilities/request.md +2 -1
  134. package/content/references/utilities/retry.md +139 -0
  135. package/content/references/utilities/schema.md +2 -2
  136. package/content/references/utilities/statuses-reference.md +4 -4
  137. package/content/references/utilities/statuses.md +5 -5
  138. package/dist/mcp-server/index.js +0 -0
  139. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  140. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  141. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  142. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  143. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  144. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  145. package/package.json +17 -16
@@ -6,11 +6,11 @@ difficulty: intermediate
6
6
 
7
7
  # Use Case Gallery
8
8
 
9
- Runnable `filter` objects paired with the SQL `FilterBuilder` produces for them - copy the shape that's closest to what you need. For the operators themselves, start at the [Filter System Overview](./).
9
+ Runnable `filter` objects, paired with the SQL `FilterBuilder` produces for them. Copy the shape closest to what you need. For the operators themselves, start at the [Filter System Overview](./).
10
10
 
11
11
  ## Soft delete
12
12
 
13
- The smallest real use case - `is`/`isn` against a nullable timestamp:
13
+ Goal: return only non-deleted rows, or only deleted ones.
14
14
 
15
15
  ```typescript
16
16
  // Active (non-deleted) records
@@ -26,11 +26,13 @@ const deletedRecords = await repository.find({
26
26
  // SQL: SELECT * FROM "Record" WHERE "deleted_at" IS NOT NULL
27
27
  ```
28
28
 
29
- If every query on a model should exclude deleted rows, encode this once as `settings.defaultFilter` instead of repeating it at every call site - see [Default Filter](./default-filter).
29
+ Notice: `is`/`isn` against a nullable timestamp is the whole pattern - no separate `deleted: boolean` column needed.
30
+
31
+ If every query on a model should exclude deleted rows, encode this once as `settings.defaultFilter` instead of repeating it at every call site. See [Default Filter](./default-filter).
30
32
 
31
33
  ## E-commerce product search
32
34
 
33
- Range, list, and pattern operators combined with field selection and sorting:
35
+ Goal: a price range, a minimum quantity, and a status, sorted and paged for a listing page.
34
36
 
35
37
  ```typescript
36
38
  const products = await productRepository.find({
@@ -58,9 +60,11 @@ const products = await productRepository.find({
58
60
  // LIMIT 24
59
61
  ```
60
62
 
63
+ Notice: four `where` keys AND-compose automatically - no explicit `and` needed for a flat condition list.
64
+
61
65
  ## Admin dashboard: recent users
62
66
 
63
- `gte` for a rolling window, `nin` to exclude states, `isn` for presence:
67
+ Goal: users created in the last 30 days, excluding banned or suspended accounts, with a verified email.
64
68
 
65
69
  ```typescript
66
70
  const thirtyDaysAgo = new Date();
@@ -89,9 +93,11 @@ const recentUsers = await userRepository.find({
89
93
  // LIMIT 50
90
94
  ```
91
95
 
96
+ Notice: `nin` on `status` silently drops any row where `status` is `NULL`. See [Tips & Edge Cases](./tips) before relying on this for a nullable column.
97
+
92
98
  ## Multi-tenant isolation at the call site
93
99
 
94
- `settings.defaultFilter` (see [Default Filter](./default-filter)) is the model-level way to enforce a tenant scope. A helper that injects `tenantId` at every call site is the call-site alternative - useful when tenant isolation is a caller concern rather than a per-model constant:
100
+ `settings.defaultFilter` (see [Default Filter](./default-filter)) is the model-level way to enforce a tenant scope. The alternative below is a helper that injects `tenantId` at every call site instead. Use it when tenant isolation is a caller concern, not a per-model constant.
95
101
 
96
102
  ```typescript
97
103
  const getTenantProducts = (tenantId: string, filter: TFilter<TProductSchema>) =>
@@ -115,11 +121,11 @@ await getTenantProducts('tenant-abc', {
115
121
  // LIMIT 20
116
122
  ```
117
123
 
118
- Unlike `defaultFilter`'s narrowing merge, this is a plain object spread - `tenantId`/`deletedAt` simply overwrite same-named keys from `filter.where` because they're spread last.
124
+ Notice: this is a plain object spread, not `mergeFilter`'s narrowing merge - `tenantId`/`deletedAt` overwrite same-named keys from `filter.where` because they're spread last.
119
125
 
120
126
  ## Task management: priority tags
121
127
 
122
- `nin` plus an array `overlaps` operator, with a relation include:
128
+ Goal: open tasks assigned to the current user that carry an urgent or high-priority tag, with the parent project loaded.
123
129
 
124
130
  ```typescript
125
131
  const priorityTasks = await taskRepository.find({
@@ -146,9 +152,11 @@ const priorityTasks = await taskRepository.find({
146
152
  // SELECT * FROM "Project" WHERE "id" IN (...)
147
153
  ```
148
154
 
155
+ Notice: `include` runs as a separate query, not a SQL `JOIN` - see [Relations & Includes](../repositories/relations).
156
+
149
157
  ## Date range queries
150
158
 
151
- `between` for a closed window, `gte` for a rolling one:
159
+ Goal: a closed window (a specific week) versus a rolling window (the last 7 days).
152
160
 
153
161
  ```typescript
154
162
  const startOfWeek = new Date('2024-12-29');
@@ -184,9 +192,11 @@ const recentOrders = await orderRepository.find({
184
192
  // ORDER BY "total" DESC LIMIT 100
185
193
  ```
186
194
 
195
+ Notice: `between` needs exactly two elements - `FilterBuilder` throws on any other array length.
196
+
187
197
  ## Inventory low-stock alert
188
198
 
189
- Nested `or`/`and` groups plus a JSON path presence check:
199
+ Goal: active products at or under a reorder threshold, flagged critical below 5 units or fast-moving stock at 10 or fewer.
190
200
 
191
201
  ```typescript
192
202
  const lowStockProducts = await productRepository.find({
@@ -218,9 +228,11 @@ const lowStockProducts = await productRepository.find({
218
228
  // ORDER BY "quantity" ASC
219
229
  ```
220
230
 
231
+ Notice: `or` nests an `and` group one level deep - `FilterBuilder` recurses through logical groups, so nesting depth is not limited to one.
232
+
221
233
  ## Complex authorization filter
222
234
 
223
- A `where` builder branching on role, composed with the caller's own scope - the `or` group only appears for non-admins:
235
+ Goal: an admin sees everything; everyone else sees only what they own, what is public, or what is shared with them.
224
236
 
225
237
  ```typescript
226
238
  const getAuthorizedFilter = (user: User): TWhere<TDocumentSchema> => {
@@ -255,9 +267,11 @@ const documents = await documentRepository.find({
255
267
  // ORDER BY "updated_at" DESC LIMIT 100
256
268
  ```
257
269
 
270
+ Notice: a plain TypeScript function builds the `where`, branching on role. A filter is a normal object, not a DSL with its own control flow.
271
+
258
272
  ## Full-text search with metadata
259
273
 
260
- Conditional `where` assembly - each filter argument adds a key only if the caller supplied it:
274
+ Goal: assemble a `where` clause from optional caller input, adding a key only when the caller supplied it.
261
275
 
262
276
  ```typescript
263
277
  const searchProducts = async (
@@ -295,9 +309,11 @@ const searchProducts = async (
295
309
  // ORDER BY "rating" DESC, "created_at" DESC LIMIT 50
296
310
  ```
297
311
 
312
+ Notice: `ilike` reaches into a JSON path (`'metadata.keywords'`) the same way it reaches a top-level column.
313
+
298
314
  ## Everything at once
299
315
 
300
- Every operator family, a JSON path, a three-way `or`, and a scoped relation include in one filter - the ceiling of what a single `TFilter` can express:
316
+ Every operator family, a JSON path, a three-way `or`, and a scoped relation include - all in one filter. This is the ceiling of what a single `TFilter` can express.
301
317
 
302
318
  ```typescript
303
319
  const massiveFilter: TFilter<TProductSchema> = {
@@ -363,6 +379,8 @@ const products = await productRepository.find({ filter: massiveFilter });
363
379
  // SELECT * FROM "Review" WHERE "product_id" IN (...) AND "rating" >= 4 ORDER BY "created_at" DESC LIMIT 5
364
380
  ```
365
381
 
382
+ Notice: `'metadata.priority': { gte: 3 }` gets the numeric `CASE` cast because the operand is a number. `'metadata.isNewArrival': true` does not, because it compares as text. See [Tips & Edge Cases](./tips) for the full casting rule.
383
+
366
384
  ## See also
367
385
 
368
386
  - [Filter System Overview](./) - the `filter` shape and every `where` operator family
@@ -372,5 +390,5 @@ const products = await productRepository.find({ filter: massiveFilter });
372
390
 
373
391
  **Files:**
374
392
 
375
- - [`packages/core/src/connectors/postgres/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
376
- - [`packages/core/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
393
+ - [`packages/core-server/src/connectors/relational/repositories/dialect/filter.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/relational/repositories/dialect/filter.ts) - `FilterBuilder`, translates `TFilter` to Drizzle/SQL
394
+ - [`packages/core-server/src/connectors/postgres/repositories/dialect/query.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/dialect/query.ts) - `PostgresQueryOperators.FNS`, per-operator SQL builders
@@ -11,13 +11,13 @@ Technical reference for gRPC controller classes -- the foundation for building g
11
11
  IGNIS gRPC controllers follow the same patterns as REST controllers (decorator-based routing, `binding()` method, DI integration) while bridging to ConnectRPC's universal handler system. REST and gRPC controllers coexist in the same application, sharing the same DI container, middleware pipeline, and lifecycle.
12
12
 
13
13
  **Files:**
14
- - `packages/core/src/base/controllers/grpc/abstract.ts`
15
- - `packages/core/src/base/controllers/grpc/base.ts`
16
- - `packages/core/src/base/controllers/grpc/adapter.ts`
17
- - `packages/core/src/base/controllers/grpc/common/types.ts`
18
- - `packages/core/src/base/metadata/routes/rpc.ts`
19
- - `packages/core/src/components/controller/grpc/grpc.component.ts`
20
- - `packages/core/src/components/controller/grpc/common/types.ts`
14
+ - `packages/core-server/src/base/controllers/grpc/abstract.ts`
15
+ - `packages/core-server/src/base/controllers/grpc/base.ts`
16
+ - `packages/core-server/src/base/controllers/grpc/adapter.ts`
17
+ - `packages/core-server/src/base/controllers/grpc/common/types.ts`
18
+ - `packages/core-server/src/base/metadata/routes/rpc.ts`
19
+ - `packages/core-server/src/components/controller/grpc/grpc.component.ts`
20
+ - `packages/core-server/src/components/controller/grpc/common/types.ts`
21
21
 
22
22
  ## Quick Reference
23
23
 
@@ -35,7 +35,7 @@ IGNIS gRPC controllers follow the same patterns as REST controllers (decorator-b
35
35
  | **@rpc** | Generic method decorator (requires explicit `method` in configs) |
36
36
 
37
37
  > [!WARNING]
38
- > **Current version supports unary RPCs only.** The `@serverStream`, `@clientStream`, and `@bidiStream` decorators still exist and set metadata correctly, but `BaseGrpcController.registerRoute()` will throw a clear error at boot time if a non-unary RPC is registered. This is because the Connect protocol over HTTP/1.1 cannot support streaming. The decorators are preserved for forward compatibility.
38
+ > **Current version supports unary RPCs only.** The `@serverStream`, `@clientStream`, and `@bidiStream` decorators still exist and set metadata correctly. But `BaseGrpcController.registerRoute()` throws a clear error at boot time if a non-unary RPC is registered - the Connect protocol over HTTP/1.1 cannot support streaming. The decorators are preserved for forward compatibility.
39
39
 
40
40
  ## Prerequisites
41
41
 
@@ -57,7 +57,7 @@ bun add @connectrpc/connect-web
57
57
  ```
58
58
 
59
59
  > [!NOTE]
60
- > `@connectrpc/connect` is an **optional** peer dependency of `@venizia/ignis` - it is only loaded at runtime when a gRPC controller is configured, via `createRequire` from the application's `node_modules`. If it is missing, `GrpcRequestAdapter.build()` throws a clear error at startup via `validateModule()`. `@bufbuild/protobuf` is required by your generated protobuf code (e.g. `create()`), not by the framework itself.
60
+ > `@connectrpc/connect` is an **optional** peer dependency of `@venizia/ignis`. It is only loaded at runtime when a gRPC controller is configured, via `createRequire` from the application's `node_modules`. If it is missing, `GrpcRequestAdapter.build()` throws a clear error at startup via `ModuleUtility.assertInstalled()`. A compiled binary has no `node_modules` to resolve against and must pass the peer through `IGrpcComponentConfig.module` - see [Peer Dependency Loading](#peer-dependency-loading). `@bufbuild/protobuf` is required by your generated protobuf code (e.g. `create()`), not by the framework itself.
61
61
 
62
62
  ### Protobuf Code Generation
63
63
 
@@ -229,7 +229,7 @@ The `configure()` method on `AbstractGrpcController` is idempotent (guarded by `
229
229
 
230
230
  ## RPC Decorators
231
231
 
232
- All RPC decorators live in `packages/core/src/base/metadata/routes/rpc.ts`. They register metadata in the `MetadataRegistry`, which is read during `configure()`.
232
+ All RPC decorators live in `packages/core-server/src/base/metadata/routes/rpc.ts`. They register metadata in the `MetadataRegistry`, which is read during `configure()`.
233
233
 
234
234
  ### `@rpc` -- Generic
235
235
 
@@ -415,7 +415,7 @@ Internal bridge between IGNIS gRPC controllers and ConnectRPC's universal handle
415
415
 
416
416
  ### Architecture
417
417
 
418
- The adapter solves a key challenge: ConnectRPC handlers have their own `(request, context) => response` signature, but IGNIS controllers need access to the Hono `Context` for middleware, auth, and request-scoped state. The adapter uses `AsyncLocalStorage` to provide request-scoped context isolation, ensuring concurrent requests never share state.
418
+ The adapter solves a key challenge: ConnectRPC handlers have their own `(request, context) => response` signature. IGNIS controllers, though, need access to the Hono `Context` for middleware, auth, and request-scoped state. The adapter uses `AsyncLocalStorage` to provide request-scoped context isolation, ensuring concurrent requests never share state.
419
419
 
420
420
  ```
421
421
  Hono Request
@@ -429,7 +429,7 @@ Hono Request
429
429
 
430
430
  ### Static `build()` Method
431
431
 
432
- The only public API. Validates peer deps via `validateModule()`, creates the adapter, and returns the middleware + registered paths:
432
+ The only public API. Validates peer deps via `ModuleUtility.assertInstalled()`, creates the adapter, and returns the middleware + registered paths:
433
433
 
434
434
  ```typescript
435
435
  static async build(opts: {
@@ -462,12 +462,27 @@ The optional `interceptors` array is passed to ConnectRPC's `createConnectRouter
462
462
 
463
463
  ### Peer Dependency Loading
464
464
 
465
- The adapter loads ConnectRPC modules at runtime using `createRequire` from the application's `node_modules`:
465
+ The adapter needs two entry points:
466
466
 
467
467
  - `@connectrpc/connect` -- for `createConnectRouter`
468
468
  - `@connectrpc/connect/protocol` -- for `universalServerRequestFromFetch` and `universalServerResponseToFetch`
469
469
 
470
- This approach supports single-file builds where the peer deps may not be resolvable via standard `import`.
470
+ By default it loads both at runtime using `createRequire` from the application's `node_modules`. That keeps the specifier invisible to `Bun.build`, so a consumer who never uses gRPC is not forced to install the peer.
471
+
472
+ **A compiled application must pass `module`.** A `bun build --compile` binary ships without `node_modules`, so `createRequire` has nothing to resolve against. Hand the peer over through the component options instead - the static import is what embeds it in the binary:
473
+
474
+ ```typescript
475
+ import * as connect from '@connectrpc/connect';
476
+ import * as protocol from '@connectrpc/connect/protocol';
477
+
478
+ this.bind({ key: GrpcBindingKeys.GRPC_COMPONENT_OPTIONS }).toValue({
479
+ module: { connect, protocol },
480
+ });
481
+ ```
482
+
483
+ `GrpcComponent` assigns the module to each controller before `configure()`, and the adapter skips both `assertInstalled` and `createRequire` when it is present.
484
+
485
+ `ModuleUtility.register` does not work here. The adapter resolves the specifier itself, so the registry never reaches it.
471
486
 
472
487
  ### Error Handling
473
488
 
@@ -477,7 +492,7 @@ On handler errors, the adapter returns a JSON response with:
477
492
  - `grpc-message` header: URL-encoded error message
478
493
  - Body: JSON `{ message, code }`
479
494
 
480
- The adapter uses a duck-type check on `error.code` to preserve gRPC status codes from ConnectRPC errors without importing `ConnectError` directly, avoiding tight coupling to the peer dependency.
495
+ The adapter uses a duck-type check on `error.code` to preserve gRPC status codes from ConnectRPC errors, without importing `ConnectError` directly. That avoids tight coupling to the peer dependency.
481
496
 
482
497
  ## `GrpcComponent`
483
498
 
@@ -488,9 +503,30 @@ Auto-discovers and configures gRPC controllers during the application lifecycle.
488
503
  ```typescript
489
504
  interface IGrpcComponentConfig {
490
505
  interceptors?: unknown[];
506
+ module?: IConnectRpcModule;
491
507
  }
492
508
  ```
493
509
 
510
+ | Option | Type | Default | Meaning |
511
+ |---|---|---|---|
512
+ | `interceptors` | `unknown[]` | none | ConnectRPC interceptors, passed to `createConnectRouter` for every gRPC controller. An empty list passes nothing |
513
+ | `module` | `IConnectRpcModule` | none | The ConnectRPC peer, as `{ connect, protocol }`. Required for a compiled binary - see [Peer Dependency Loading](#peer-dependency-loading) |
514
+
515
+ Both options are component-wide. `GrpcComponent` assigns them to every gRPC controller it discovers, before calling `configure()`:
516
+
517
+ ```typescript
518
+ import type { Interceptor } from '@connectrpc/connect';
519
+
520
+ const logging: Interceptor = next => async request => {
521
+ const response = await next(request);
522
+ return response;
523
+ };
524
+
525
+ this.bind({ key: GrpcBindingKeys.GRPC_COMPONENT_OPTIONS }).toValue({
526
+ interceptors: [logging],
527
+ });
528
+ ```
529
+
494
530
  The component registers a default (empty) config binding under the key `'@app/grpc/options'` (`GrpcBindingKeys.GRPC_COMPONENT_OPTIONS`).
495
531
 
496
532
  ### Behavior
@@ -623,7 +659,7 @@ export class Application extends BaseApplication {
623
659
  ```
624
660
 
625
661
  > [!WARNING]
626
- > If `transports` does not include `ControllerTransports.GRPC`, gRPC controllers are still registered in the DI container but the `GrpcComponent` is never mounted -- their `configure()` is never called and no routes are served.
662
+ > If `transports` does not include `ControllerTransports.GRPC`, gRPC controllers are still registered in the DI container. But the `GrpcComponent` is never mounted -- their `configure()` is never called and no routes are served.
627
663
 
628
664
  ### Dual Transport
629
665
 
@@ -639,7 +675,7 @@ preConfigure() {
639
675
  }
640
676
  ```
641
677
 
642
- REST controllers are handled by the `RestComponent` (active when transports includes `ControllerTransports.REST`, which is the default); gRPC controllers are handled by the `GrpcComponent` (active when transport is enabled). They share the same DI container and lifecycle.
678
+ REST controllers are handled by the `RestComponent`, active when `transports` includes `ControllerTransports.REST` (the default). gRPC controllers are handled by the `GrpcComponent`, active when that transport is enabled. They share the same DI container and lifecycle.
643
679
 
644
680
  ## Complete Example
645
681
 
@@ -9,7 +9,7 @@ Core classes that power every IGNIS application - from the Application entry poi
9
9
 
10
10
  | Class | Purpose | Extends |
11
11
  |-------|---------|---------|
12
- | `BaseApplication` | Application entry point, DI container | `AbstractApplication` |
12
+ | `BaseApplication` | Application entry point, DI container | `ServerApplication` -> `RestApplication` -> `AbstractApplication` |
13
13
  | `BaseRestController` | REST/HTTP route handlers | `AbstractRestController` |
14
14
  | `BaseGrpcController` | gRPC route handlers (ConnectRPC) | `AbstractGrpcController` |
15
15
  | `BaseService` | Business logic layer | - |
@@ -79,8 +79,10 @@ Core classes that power every IGNIS application - from the Application entry poi
79
79
  ## Class Hierarchy
80
80
 
81
81
  ```
82
- AbstractApplication
83
- └── BaseApplication ──────► Your Application
82
+ AbstractApplication (browser-pure, @venizia/ignis-kernel)
83
+ └── RestApplication (browser-pure, owns the router)
84
+ └── ServerApplication (opens the socket)
85
+ └── BaseApplication ──────► Your Application
84
86
 
85
87
  AbstractRepository (engine-neutral, src/base)
86
88
  ├── PostgresBaseRepository (connectors/postgres)
@@ -10,10 +10,10 @@ lastUpdated: 2026-06-14
10
10
  IGNIS provides built-in middleware functions and a provider-based middleware class for handling common HTTP concerns: error handling, request logging, 404 responses, and favicon serving. These are registered automatically by `BaseApplication` during startup - you do not import or wire them manually.
11
11
 
12
12
  **Files:**
13
- - `packages/core/src/base/middlewares/app-error/app-error.middleware.ts`
14
- - `packages/core/src/base/middlewares/not-found/not-found.middleware.ts`
15
- - `packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts`
16
- - `packages/core/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
13
+ - `packages/core-server/src/base/middlewares/app-error/app-error.middleware.ts`
14
+ - `packages/core-server/src/base/middlewares/not-found/not-found.middleware.ts`
15
+ - `packages/core-server/src/base/middlewares/request-spy/request-spy.middleware.ts`
16
+ - `packages/core-server/src/base/middlewares/emoji-favicon/emoji-favicon.middleware.ts`
17
17
 
18
18
  ## Prerequisites
19
19
 
@@ -86,7 +86,7 @@ class AppErrorMiddleware extends BaseHelper implements IProvider<ErrorHandler> {
86
86
 
87
87
  When `error.name === 'ZodError'`, returns HTTP `422 Unprocessable Entity`.
88
88
 
89
- `message` and `normalized.code` come from the first failing issue - `message` is that issue's message, `normalized.code` is its `params.code` if the schema set one, otherwise its raw Zod code. The full per-field list stays under `details.cause`. `normalized.args` is always `{}` - a Zod issue carries no interpolation values.
89
+ `message` and `normalized.code` come from the first failing issue. `message` is that issue's message; `normalized.code` is its `params.code` if the schema set one, otherwise its raw Zod code. The full per-field list stays under `details.cause`. `normalized.args` is always `{}` - a Zod issue carries no interpolation values.
90
90
 
91
91
  ```json
92
92
  {
@@ -126,11 +126,11 @@ z.string().refine(isEmail, {
126
126
  ```
127
127
 
128
128
  > [!NOTE]
129
- > When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived code exists - `normalized.code` still resolves to `MessageCode.DEFAULT` (`"core.system_error"`) via `MessageCode.resolve(undefined)`. No error response from this middleware is ever missing `normalized.code`.
129
+ > When `error.message` cannot be parsed as the expected Zod issue array (a malformed or unrecognized `ZodError`), no issue-derived code exists. `normalized.code` still resolves to `MessageCode.DEFAULT` (`"core.system_error"`) via `MessageCode.resolve(undefined)`. No error response from this middleware is ever missing `normalized.code`.
130
130
 
131
131
  #### 2. PostgreSQL Constraint Violations
132
132
 
133
- Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class and returned as HTTP `400 Bad Request`. A known code uses its specific message; any other in-class code uses `"Invalid database request"` as a fallback.
133
+ Database errors in SQLSTATE class `22` (data exception), `23` (integrity constraint), and `44` (WITH CHECK OPTION violation) are detected by class. They return HTTP `400 Bad Request`. A known code uses its specific message; any other in-class code uses `"Invalid database request"` as a fallback.
134
134
 
135
135
  | Class | Codes with a specific message |
136
136
  |-------|-------------------------------|
@@ -139,7 +139,7 @@ Database errors in SQLSTATE class `22` (data exception), `23` (integrity constra
139
139
  | `44` View check | `44000` WITH CHECK OPTION violation |
140
140
 
141
141
  :::tip Transient conflicts return 409, not 400/500
142
- Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable and returns **409 Conflict** with `normalized.code: "database.conflict"` and a safe "please retry" message - the client can safely retry the same request. Programming/infra classes (`42` syntax, `53` resources, `0A`, `25`, `28`) remain 500.
142
+ Class `40` (`40001` serialization failure, `40P01` deadlock) is transient/retryable. It returns **409 Conflict** with `normalized.code: "database.conflict"` and a safe "please retry" message - the client can safely retry the same request. Programming/infra classes (`42` syntax, `53` resources, `0A`, `25`, `28`) remain 500.
143
143
  :::
144
144
 
145
145
  :::warning Production sanitizes database internals
@@ -249,7 +249,8 @@ function notFoundHandler(opts: {
249
249
  }
250
250
  ```
251
251
 
252
- The handler logs the 404 at error level with the request ID, path, and full URL.
252
+ The handler logs the 404 at warn level with the request ID, path, and full URL. An unrouted path is a
253
+ client mistake, not a server fault - alerting tuned to error level should not fire on it.
253
254
 
254
255
 
255
256
  ## RequestSpyMiddleware
@@ -496,7 +497,7 @@ export class MyMiddleware extends BaseHelper implements IProvider<MiddlewareHand
496
497
  }
497
498
  ```
498
499
 
499
- Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`), then use the resolved handler inside `setupMiddlewares()` - `get()` returns the produced `MiddlewareHandler` because the container calls `value()` for provider bindings:
500
+ Register it with `.toProvider()` (the same pattern `RequestTrackerComponent` uses for `RequestSpyMiddleware`). Then use the resolved handler inside `setupMiddlewares()`: `get()` returns the produced `MiddlewareHandler`, because the container calls `value()` for provider bindings.
500
501
 
501
502
  ```typescript
502
503
  export class MyApplication extends BaseApplication {
@@ -10,10 +10,10 @@ Exhaustive reference for the `@model` decorator, the entity class hierarchy, and
10
10
 
11
11
  **Files:**
12
12
 
13
- - [`packages/core/src/base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/base.ts) - neutral `AbstractEntity`
14
- - [`packages/core/src/connectors/postgres/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/base.ts) - PostgreSQL entity (`BaseRelationalEntity`, aliases `BaseEntity`/`BasePostgresEntity`)
15
- - [`packages/core/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/metadata/persistents.ts) - `@model` decorator
16
- - [`packages/core/src/connectors/postgres/models/enrichers`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/enrichers) - schema enrichers
13
+ - [`packages/core-server/src/base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/models/base.ts) - neutral `AbstractEntity`
14
+ - [`packages/core-server/src/connectors/postgres/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/base.ts) - PostgreSQL entity (`BaseRelationalEntity`, aliases `BaseEntity`/`BasePostgresEntity`)
15
+ - [`packages/core-server/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/metadata/persistents.ts) - `@model` decorator
16
+ - [`packages/core-server/src/connectors/postgres/models/enrichers`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/enrichers) - schema enrichers
17
17
 
18
18
  ## `AbstractEntity` (neutral) vs. `BaseEntity` (connector)
19
19
 
@@ -23,7 +23,7 @@ IGNIS separates the engine-neutral entity root from the connector-specific imple
23
23
  - **Minimal surface.** It carries only a `name`, an abstract `getSchema()`, a `getIdType(): TIdSchemaType` method (default `'string'`), and `toObject()`/`toJSON()`.
24
24
  - **Everything else is connector-owned.** The Drizzle-backed entity, `drizzle-zod` schema generation, and all schema enrichers belong to the PostgreSQL connector, not the neutral base. See [Connectors](/references/base/connectors) for the full base-vs-connector architecture.
25
25
 
26
- `Source ->` [`packages/core/src/base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/base.ts)
26
+ `Source ->` [`packages/core-server/src/base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/models/base.ts)
27
27
 
28
28
  ```typescript
29
29
  export abstract class AbstractEntity<Schema = unknown> extends BaseHelper {
@@ -57,7 +57,7 @@ export abstract class AbstractEntity<Schema = unknown> extends BaseHelper {
57
57
 
58
58
  Marks a class as a database entity and configures its behavior.
59
59
 
60
- `Source ->` [`packages/core/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/metadata/persistents.ts)
60
+ `Source ->` [`packages/core-server/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/metadata/persistents.ts)
61
61
 
62
62
  ```typescript
63
63
  @model({
@@ -102,7 +102,7 @@ When the `@model` decorator is applied:
102
102
 
103
103
  PostgreSQL connector entity class, wrapping a Drizzle ORM schema. Extends the neutral `AbstractEntity`.
104
104
 
105
- `Source ->` [`packages/core/src/connectors/postgres/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/base.ts)
105
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/base.ts)
106
106
 
107
107
  ### Purpose
108
108
 
@@ -418,7 +418,7 @@ export const myTable = pgTable('MyTable', {
418
418
 
419
419
  Adds a primary key `id` column with full TypeScript type inference.
420
420
 
421
- `Source ->` [`packages/core/src/connectors/postgres/models/enrichers/id.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/enrichers/id.enricher.ts)
421
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/enrichers/id.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/enrichers/id.enricher.ts)
422
422
 
423
423
  ```typescript
424
424
  generateIdColumnDefs<Opts extends TIdEnricherOptions | undefined>(opts?: Opts): TIdColumnDef<Opts>
@@ -466,7 +466,7 @@ enrichId(baseColumns: TColumnDefinitions, opts?: TIdEnricherOptions): TColumnDef
466
466
 
467
467
  Adds timestamp columns for creation, modification, and soft deletion.
468
468
 
469
- `Source ->` [`packages/core/src/connectors/postgres/models/enrichers/tz.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/enrichers/tz.enricher.ts)
469
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/enrichers/tz.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/enrichers/tz.enricher.ts)
470
470
 
471
471
  ```typescript
472
472
  generateTzColumnDefs<Opts extends TTzEnricherOptions | undefined>(opts?: Opts): TTzEnricherResult<Opts>
@@ -540,7 +540,7 @@ await db.update(myTable).set({ deletedAt: null }).where(eq(myTable.id, id));
540
540
 
541
541
  Adds `createdBy` and `modifiedBy` columns tracking which user created or modified a record.
542
542
 
543
- `Source ->` [`packages/core/src/connectors/postgres/models/enrichers/user-audit.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/enrichers/user-audit.enricher.ts)
543
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/enrichers/user-audit.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/enrichers/user-audit.enricher.ts)
544
544
 
545
545
  ```typescript
546
546
  generateUserAuditColumnDefs(opts?: TUserAuditEnricherOptions): {
@@ -571,7 +571,7 @@ Defaults:
571
571
 
572
572
  **How it works**
573
573
 
574
- - **Retrieval.** The enricher uses Hono's `contextStorage` (via `tryGetContext()`) to retrieve the current user ID at insert/update time, reading it from the `Authentication.AUDIT_USER_ID` key.
574
+ - **Retrieval.** The enricher reads the request context from `RequestContextRegistry` at insert/update time, then takes the user ID off the `Authentication.AUDIT_USER_ID` key. An application installs the resolver over that registry when it registers its default middlewares, backed by Hono's `contextStorage`. A host that installs none - a browser Worker - has no request context at all, which is the `allowAnonymous` case below.
575
575
  - **`createdBy`.** Set via `$default()` - creation only.
576
576
  - **`modifiedBy`.** Set via both `$default()` and `$onUpdate()` - creation and every modification.
577
577
 
@@ -610,7 +610,7 @@ enrichUserAudit<ColumnDefinitions extends TColumnDefinitions>(
610
610
 
611
611
  Adds polymorphic principal columns for associating a record with different entity types (a comment can belong to a Post, User, or Product).
612
612
 
613
- `Source ->` [`packages/core/src/connectors/postgres/models/enrichers/principal.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/enrichers/principal.enricher.ts)
613
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/enrichers/principal.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/enrichers/principal.enricher.ts)
614
614
 
615
615
  ```typescript
616
616
  generatePrincipalColumnDefs<
@@ -666,7 +666,7 @@ enrichPrincipal<ColumnDefinitions extends TColumnDefinitions>(
666
666
 
667
667
  Adds polymorphic data storage columns for entities that store values of different types in a single table (key-value stores, settings tables).
668
668
 
669
- `Source ->` [`packages/core/src/connectors/postgres/models/enrichers/data-type.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/enrichers/data-type.enricher.ts)
669
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/enrichers/data-type.enricher.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/enrichers/data-type.enricher.ts)
670
670
 
671
671
  ```typescript
672
672
  generateDataTypeColumnDefs(opts?: TDataTypeEnricherOptions): {
@@ -731,7 +731,7 @@ enrichDataTypes(baseSchema: TColumnDefinitions, opts?: TDataTypeEnricherOptions)
731
731
 
732
732
  ## Key types
733
733
 
734
- `Source ->` [`packages/core/src/connectors/postgres/models/common`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/common)
734
+ `Source ->` [`packages/core-server/src/connectors/postgres/models/common`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/common)
735
735
 
736
736
  ### `TTableSchemaWithId`
737
737
 
@@ -756,7 +756,7 @@ type TTableInsert<T extends TTableSchemaWithId> = T['$inferInsert'];
756
756
 
757
757
  Configuration for entity relationships.
758
758
 
759
- `Source ->` [`packages/core/src/connectors/postgres/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/repositories/common/types.ts)
759
+ `Source ->` [`packages/core-server/src/connectors/postgres/repositories/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/repositories/common/types.ts)
760
760
 
761
761
  ```typescript
762
762
  type TRelationConfig = {
@@ -781,7 +781,7 @@ Used for `relations` on `BaseEntity` - store a function that returns the relatio
781
781
 
782
782
  ## Schema utilities
783
783
 
784
- `Source ->` [`packages/core/src/base/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/common/types.ts)
784
+ `Source ->` [`packages/core-server/src/base/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/models/common/types.ts)
785
785
 
786
786
  ### `idParamsSchema`
787
787
 
@@ -862,7 +862,7 @@ There are two distinct `getIdType`s in the framework - do not confuse them.
862
862
 
863
863
  | | Neutral instance method | PostgreSQL utility function |
864
864
  |---|---|---|
865
- | Location | `AbstractEntity.getIdType()` ([`base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/base.ts)) | `getIdType()` ([`connectors/postgres/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/common/types.ts)) |
865
+ | Location | `AbstractEntity.getIdType()` ([`base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/models/base.ts)) | `getIdType()` ([`connectors/postgres/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/common/types.ts)) |
866
866
  | Signature | `getIdType(): TIdSchemaType` | `getIdType<T extends TTableSchemaWithId>(opts: { entity: T }): string` |
867
867
  | Purpose | Neutral capability every engine's entity implements - returns `'string'` \| `'number'` at the entity level. Used by `idParamsSchema` to build the right Zod schema for path parameters. | PostgreSQL-specific - inspects a Drizzle table schema's `id` column and returns its `dataType`, or `'unknown'` if not determinable |
868
868
 
@@ -32,7 +32,8 @@ A `@repository` binds this model to a datasource, and the datasource auto-discov
32
32
  - **Registration.** The `@model` decorator registers the class in the framework's metadata registry, keyed by table name (resolved as `tableName` > static `TABLE_NAME` > class name).
33
33
  - **Validation at decoration time.** It validates `settings.defaultLimit` and, when you declare an authorization principal, copies it onto the static `AUTHORIZATION_SUBJECT` property.
34
34
  - **Plain Drizzle schema.** The static `schema` is a plain Drizzle `pgTable`. Enrichers such as `generateIdColumnDefs` return column definitions you spread into that table, so common columns (id, timestamps, audit, principal) stay standardized across models.
35
- - **Zod on demand.** `BaseEntity` generates Zod schemas from the Drizzle schema via `getSchema({ type })` - `'select'`, `'create'`, and `'update'` variants for validating query results, inserts, and updates. The generator is a shared lazy singleton, so there is no per-entity cost.
35
+ - **Zod on demand.** `BaseEntity` generates Zod schemas from the Drizzle schema via `getSchema({ type })` - `'select'`, `'create'`, and `'update'` variants for validating query results, inserts, and updates.
36
+ - The generator is a shared lazy singleton, so there is no per-entity cost.
36
37
 
37
38
  **Two layers**
38
39
 
@@ -149,5 +150,5 @@ See [Authorization](/extensions/components/authorization/usage#model-based-resou
149
150
 
150
151
  **Files:**
151
152
 
152
- - [`packages/core/src/base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/base.ts) - neutral `AbstractEntity`
153
- - [`packages/core/src/connectors/postgres/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/models/base.ts) - PostgreSQL `BaseEntity`
153
+ - [`packages/core-server/src/base/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/models/base.ts) - neutral `AbstractEntity`
154
+ - [`packages/core-server/src/connectors/postgres/models/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/connectors/postgres/models/base.ts) - PostgreSQL `BaseEntity`
@@ -10,7 +10,7 @@ lastUpdated: 2026-03-15
10
10
  Providers implement the Factory pattern in IGNIS, allowing you to create and configure instances dynamically at runtime based on configuration or context. Unlike services that contain business logic, providers are factories that produce values, instances, or functions.
11
11
 
12
12
  **Files:**
13
- - `packages/core/src/base/providers/base.ts`
13
+ - `packages/core-server/src/base/providers/base.ts`
14
14
 
15
15
  ## Prerequisites
16
16
 
@@ -229,7 +229,7 @@ export class LoggerProvider extends BaseProvider<ILogger> {
229
229
  }
230
230
  ```
231
231
 
232
- Register the provider with `.toProvider()` - consumers then `get()` the **produced value**, not the provider instance (the container instantiates the provider and calls `value(container)` for you):
232
+ Register the provider with `.toProvider()`. Consumers then `get()` the **produced value**, not the provider instance - the container instantiates the provider and calls `value(container)` for you:
233
233
 
234
234
  ```typescript
235
235
  // In your application (e.g. preConfigure)
@@ -327,7 +327,7 @@ graph TD
327
327
 
328
328
  1. **Registered via `.toProvider()`**: Providers are bound explicitly (`bind({ key }).toProvider(MyProvider)`), not auto-scanned
329
329
  2. **`value()` Called by the Container**: `container.get({ key })` instantiates the provider and calls `value(container)` - consumers receive the produced value, never the provider instance
330
- 3. **Singleton Scope Caches the Produced Value**: With `.setScope(BindingScopes.SINGLETON)`, the container caches the result of `value()` and returns it on subsequent `get()` calls; with the default transient scope, `value()` runs on every `get()`
330
+ 3. **Singleton Scope Caches the Produced Value**: With `.setScope(BindingScopes.SINGLETON)`, the container caches the result of `value()` and returns it on subsequent `get()` calls. With the default transient scope, `value()` runs on every `get()`
331
331
  4. **Factory vs Instance**: Providers can return:
332
332
  - Direct instances (created each time `value()` is called)
333
333
  - Factory functions (deferred creation)
@@ -350,7 +350,7 @@ app.bind({ key: 'providers.Database' }).toProvider(DatabaseProvider);
350
350
 
351
351
  ### Example 1: Mail Transport Provider
352
352
 
353
- From `packages/core/src/components/mail/providers/mail-transporter.provider.ts`:
353
+ From `packages/core-server/src/components/mail/providers/mail-transporter.provider.ts`:
354
354
 
355
355
  ```typescript
356
356
  export type TGetMailTransportFn = (options: TMailOptions) => IMailTransport;
@@ -419,7 +419,7 @@ this.application.bind({ key: MailKeys.MAIL_TRANSPORT_INSTANCE }).toValue(mailTra
419
419
 
420
420
  ### Example 2: Queue Executor Provider
421
421
 
422
- From `packages/core/src/components/mail/providers/mail-queue-executor.provider.ts`:
422
+ From `packages/core-server/src/components/mail/providers/mail-queue-executor.provider.ts`:
423
423
 
424
424
  ```typescript
425
425
  export type TGetMailQueueExecutorFn = (config: IMailQueueExecutorConfig) => IMailQueueExecutor;
@@ -472,7 +472,7 @@ export class MailQueueExecutorProvider extends BaseProvider<TGetMailQueueExecuto
472
472
  Providers can also produce middleware. `RequestSpyMiddleware` is a real-world example that implements `IProvider<MiddlewareHandler>` directly (extending `BaseHelper`, not `BaseProvider`):
473
473
 
474
474
  ```typescript
475
- // From packages/core/src/base/middlewares/request-spy/request-spy.middleware.ts
475
+ // From packages/core-server/src/base/middlewares/request-spy/request-spy.middleware.ts
476
476
  export class RequestSpyMiddleware extends BaseHelper implements IProvider<MiddlewareHandler> {
477
477
  static readonly REQUEST_ID_KEY = 'requestId';
478
478
 
@@ -509,7 +509,7 @@ export class RequestSpyMiddleware extends BaseHelper implements IProvider<Middle
509
509
 
510
510
  See [Middlewares](./middlewares.md) for the full implementation (IP resolution, body-parsing rules, and production log redaction).
511
511
 
512
- Note that `RequestSpyMiddleware.value()` does not accept a `container` parameter -- the `IProvider<T>` interface defines `value(container: Container): T`, but implementations may ignore the parameter when they don't need container access. In practice, `RequestSpyMiddleware` is registered via `RequestTrackerComponent`, which binds it as a provider in the DI container and resolves it automatically.
512
+ Note that `RequestSpyMiddleware.value()` does not accept a `container` parameter. The `IProvider<T>` interface defines `value(container: Container): T`, but implementations may ignore the parameter when they don't need container access. In practice, `RequestSpyMiddleware` is registered via `RequestTrackerComponent`, which binds it as a provider in the DI container and resolves it automatically.
513
513
 
514
514
 
515
515
  ## Common Patterns
@@ -545,7 +545,7 @@ export class S3StorageProvider extends BaseProvider<S3Storage> {
545
545
 
546
546
  ### Pattern 2: Lazy Singleton
547
547
 
548
- Create instance only once, lazily (or simply bind with `.setScope(BindingScopes.SINGLETON)` and let the container cache the produced value):
548
+ Create instance only once, lazily (or bind with `.setScope(BindingScopes.SINGLETON)` and let the container cache the produced value):
549
549
 
550
550
  ```typescript
551
551
  export class DatabaseConnectionProvider extends BaseProvider<DatabaseConnection> {