@venizia/ignis-docs 0.0.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +7 -7
  2. package/content/best-practices/api-usage-examples.md +15 -12
  3. package/content/best-practices/architectural-patterns.md +70 -78
  4. package/content/best-practices/architecture-decisions.md +91 -60
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/content/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/content/best-practices/code-style-standards/documentation.md +13 -13
  9. package/content/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/content/best-practices/code-style-standards/index.md +1 -1
  11. package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/content/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/content/best-practices/code-style-standards/tooling.md +8 -5
  14. package/content/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/content/best-practices/common-pitfalls.md +56 -37
  16. package/content/best-practices/contribution-workflow.md +13 -14
  17. package/content/best-practices/data-modeling.md +46 -22
  18. package/content/best-practices/deployment-strategies.md +28 -27
  19. package/content/best-practices/error-handling.md +48 -24
  20. package/content/best-practices/index.md +5 -5
  21. package/content/best-practices/performance-optimization.md +40 -31
  22. package/content/best-practices/security-guidelines.md +52 -23
  23. package/content/best-practices/testing-strategies.md +65 -51
  24. package/content/best-practices/troubleshooting-tips.md +24 -24
  25. package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
  26. package/content/extensions/components/authentication/api.md +19 -19
  27. package/content/extensions/components/authentication/errors.md +7 -7
  28. package/content/extensions/components/authentication/index.md +10 -8
  29. package/content/extensions/components/authentication/usage.md +101 -6
  30. package/content/extensions/components/authorization/api.md +45 -25
  31. package/content/extensions/components/authorization/errors.md +6 -6
  32. package/content/extensions/components/authorization/index.md +11 -10
  33. package/content/extensions/components/authorization/usage.md +21 -21
  34. package/content/extensions/components/health-check.md +1 -1
  35. package/content/extensions/components/index.md +5 -5
  36. package/content/extensions/components/mail/errors.md +15 -15
  37. package/content/extensions/components/mail/index.md +1 -2
  38. package/content/extensions/components/mail/usage.md +1 -1
  39. package/content/extensions/components/request-tracker.md +1 -1
  40. package/content/extensions/components/socket-io/api.md +9 -9
  41. package/content/extensions/components/socket-io/errors.md +5 -5
  42. package/content/extensions/components/socket-io/index.md +8 -8
  43. package/content/extensions/components/socket-io/usage.md +1 -1
  44. package/content/extensions/components/static-asset/api.md +17 -4
  45. package/content/extensions/components/static-asset/errors.md +4 -4
  46. package/content/extensions/components/static-asset/index.md +26 -28
  47. package/content/extensions/components/static-asset/usage.md +13 -12
  48. package/content/extensions/components/template/index.md +2 -2
  49. package/content/extensions/components/template/setup-page.md +1 -1
  50. package/content/extensions/components/websocket/api.md +3 -3
  51. package/content/extensions/components/websocket/errors.md +5 -5
  52. package/content/extensions/components/websocket/index.md +5 -5
  53. package/content/extensions/components/websocket/usage.md +3 -3
  54. package/content/extensions/helpers/cron/index.md +2 -2
  55. package/content/extensions/helpers/crypto/index.md +1 -1
  56. package/content/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +81 -25
  58. package/content/extensions/helpers/index.md +2 -3
  59. package/content/extensions/helpers/inversion/index.md +15 -7
  60. package/content/extensions/helpers/kafka/compile-binary.md +92 -0
  61. package/content/extensions/helpers/kafka/examples.md +1 -1
  62. package/content/extensions/helpers/kafka/index.md +3 -0
  63. package/content/extensions/helpers/logger/index.md +32 -2
  64. package/content/extensions/helpers/network/index.md +6 -0
  65. package/content/extensions/helpers/queue/index.md +14 -17
  66. package/content/extensions/helpers/redis/index.md +548 -323
  67. package/content/extensions/helpers/socket-io/index.md +14 -10
  68. package/content/extensions/helpers/storage/api.md +44 -8
  69. package/content/extensions/helpers/storage/index.md +43 -7
  70. package/content/extensions/helpers/template/index.md +6 -3
  71. package/content/extensions/helpers/types/index.md +11 -8
  72. package/content/extensions/helpers/websocket/api.md +9 -9
  73. package/content/extensions/helpers/websocket/index.md +7 -7
  74. package/content/extensions/helpers/worker-thread/index.md +2 -2
  75. package/content/extensions/index.md +3 -4
  76. package/content/extensions/src-details/mcp-server.md +18 -24
  77. package/content/guides/core-concepts/application/bootstrapping.md +11 -14
  78. package/content/guides/core-concepts/application/index.md +3 -3
  79. package/content/guides/core-concepts/components.md +19 -10
  80. package/content/guides/core-concepts/dependency-injection.md +6 -3
  81. package/content/guides/core-concepts/grpc-controllers.md +6 -5
  82. package/content/guides/core-concepts/persistent/datasources.md +42 -43
  83. package/content/guides/core-concepts/persistent/index.md +16 -7
  84. package/content/guides/core-concepts/persistent/models.md +24 -20
  85. package/content/guides/core-concepts/persistent/postgres-drivers.md +201 -0
  86. package/content/guides/core-concepts/persistent/repositories.md +40 -23
  87. package/content/guides/core-concepts/persistent/search-meilisearch.md +185 -0
  88. package/content/guides/core-concepts/persistent/search-typesense.md +431 -0
  89. package/content/guides/core-concepts/persistent/transactions.md +61 -25
  90. package/content/guides/core-concepts/rest-controllers.md +12 -9
  91. package/content/guides/core-concepts/services.md +330 -60
  92. package/content/guides/get-started/5-minute-quickstart.md +15 -15
  93. package/content/guides/get-started/philosophy.md +36 -36
  94. package/content/guides/get-started/setup.md +3 -3
  95. package/content/guides/index.md +3 -3
  96. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  97. package/content/guides/migrations/scoped-rbac-migration.md +17 -17
  98. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  99. package/content/guides/reference/glossary.md +19 -12
  100. package/content/guides/reference/mcp-docs-server.md +22 -18
  101. package/content/guides/tutorials/building-a-crud-api.md +37 -44
  102. package/content/guides/tutorials/complete-installation.md +17 -17
  103. package/content/guides/tutorials/ecommerce-api.md +163 -124
  104. package/content/guides/tutorials/realtime-chat.md +181 -135
  105. package/content/guides/tutorials/testing.md +65 -523
  106. package/content/index.md +2 -180
  107. package/content/public/apple-touch-icon.png +0 -0
  108. package/content/public/og-image.png +0 -0
  109. package/content/public/site.webmanifest +11 -0
  110. package/content/references/base/application.md +4 -5
  111. package/content/references/base/bootstrapping.md +18 -5
  112. package/content/references/base/components.md +149 -120
  113. package/content/references/base/connectors.md +178 -0
  114. package/content/references/base/controllers.md +41 -30
  115. package/content/references/base/datasources.md +163 -92
  116. package/content/references/base/dependency-injection.md +34 -22
  117. package/content/references/base/filter-system/application-usage.md +17 -14
  118. package/content/references/base/filter-system/array-operators.md +7 -2
  119. package/content/references/base/filter-system/comparison-operators.md +3 -0
  120. package/content/references/base/filter-system/default-filter.md +89 -71
  121. package/content/references/base/filter-system/fields-order-pagination.md +22 -22
  122. package/content/references/base/filter-system/index.md +6 -3
  123. package/content/references/base/filter-system/json-filtering.md +20 -1
  124. package/content/references/base/filter-system/list-operators.md +1 -1
  125. package/content/references/base/filter-system/logical-operators.md +33 -1
  126. package/content/references/base/filter-system/null-operators.md +30 -1
  127. package/content/references/base/filter-system/quick-reference.md +23 -4
  128. package/content/references/base/filter-system/tips.md +5 -5
  129. package/content/references/base/filter-system/use-cases.md +12 -12
  130. package/content/references/base/grpc-controllers.md +13 -13
  131. package/content/references/base/index.md +24 -12
  132. package/content/references/base/middlewares.md +265 -327
  133. package/content/references/base/models.md +63 -49
  134. package/content/references/base/providers.md +136 -130
  135. package/content/references/base/repositories/advanced.md +59 -58
  136. package/content/references/base/repositories/index.md +115 -91
  137. package/content/references/base/repositories/mixins.md +55 -291
  138. package/content/references/base/repositories/relations.md +54 -64
  139. package/content/references/base/repositories/soft-deletable.md +31 -30
  140. package/content/references/base/services.md +296 -93
  141. package/content/references/configuration/environment-variables.md +49 -31
  142. package/content/references/configuration/index.md +6 -6
  143. package/content/references/index.md +17 -12
  144. package/content/references/quick-reference.md +65 -106
  145. package/content/references/utilities/crypto.md +65 -23
  146. package/content/references/utilities/index.md +3 -3
  147. package/content/references/utilities/jsx.md +6 -4
  148. package/content/references/utilities/module.md +68 -20
  149. package/content/references/utilities/parse.md +4 -14
  150. package/content/references/utilities/promise.md +9 -7
  151. package/content/references/utilities/schema.md +5 -3
  152. package/dist/mcp-server/common/guards.d.ts +8 -0
  153. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  154. package/dist/mcp-server/common/guards.js +14 -0
  155. package/dist/mcp-server/common/guards.js.map +1 -0
  156. package/dist/mcp-server/common/index.d.ts +1 -0
  157. package/dist/mcp-server/common/index.d.ts.map +1 -1
  158. package/dist/mcp-server/common/index.js +1 -0
  159. package/dist/mcp-server/common/index.js.map +1 -1
  160. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  162. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  163. package/dist/mcp-server/helpers/github.helper.js +1 -1
  164. package/dist/mcp-server/index.js +7 -2
  165. package/dist/mcp-server/index.js.map +1 -1
  166. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  167. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  168. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  169. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  175. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  178. package/package.json +9 -9
  179. package/content/extensions/helpers/testing/index.md +0 -510
  180. package/content/references/base/middleware.md +0 -347
@@ -105,9 +105,38 @@ Combine AND and OR for complex logic:
105
105
  ```
106
106
 
107
107
 
108
+ ## Empty Groups
109
+
110
+ An empty `and`/`or` array is not a no-op -- each empty case resolves to what the operator means with zero conditions:
111
+
112
+ ```typescript
113
+ // Empty AND is vacuously TRUE - dropped from the query entirely
114
+ { where: { and: [] } }
115
+ // SQL: (no condition added)
116
+
117
+ // Empty OR is vacuously FALSE - compiles to a condition that matches nothing
118
+ { where: { or: [] } }
119
+ // SQL: WHERE false
120
+ ```
121
+
122
+ This matters when the array is built from a caller-supplied list, e.g. `{ or: permittedOrgIds.map(id => ({ orgId: id })) }`: an empty permission list must return zero rows, not every row, so `or: []` matching nothing is the safe default.
123
+
124
+
108
125
  ## NOT Logic
109
126
 
110
- Ignis does not have a standalone `not` logical operator. Instead, use negation operators for NOT conditions:
127
+ IGNIS has a general-purpose `not` operator that negates whatever condition it wraps - a bare value negates `eq`, and a nested operator object negates that operator. It is supported on the PostgreSQL connector:
128
+
129
+ ```typescript
130
+ // NOT equal (bare value negates eq)
131
+ { where: { status: { not: 'archived' } } }
132
+ // SQL: WHERE NOT ("status" = 'archived')
133
+
134
+ // Negate a nested operator condition
135
+ { where: { views: { not: { gt: 100 } } } }
136
+ // SQL: WHERE NOT ("views" > 100)
137
+ ```
138
+
139
+ The dedicated negation operators remain available and are often clearer for a single condition:
111
140
 
112
141
  ```typescript
113
142
  // NOT equal
@@ -131,6 +160,9 @@ Ignis does not have a standalone `not` logical operator. Instead, use negation o
131
160
  { where: { score: { notBetween: [40, 60] } } }
132
161
  ```
133
162
 
163
+ > [!NOTE]
164
+ > `ne`/`neq`/`nin` follow SQL three-valued logic - a row whose field is `NULL` never matches them (`NULL <> value` is UNKNOWN, not TRUE). Use `exists`/`notExists` or an explicit `{ field: null }` branch when you need NULL rows in the result.
165
+
134
166
 
135
167
  ## Complex Example
136
168
 
@@ -71,6 +71,30 @@ The simplest way to check for NULL:
71
71
  ```
72
72
 
73
73
 
74
+ ## exists / notExists - Presence Check
75
+
76
+ `exists` and `notExists` are presence operators - they take a boolean rather than a value, and read more naturally than `is`/`isn` against `null`. They work on the PostgreSQL connector, and `exists` also works over JSON paths there.
77
+
78
+ ```typescript
79
+ // Field is present (IS NOT NULL)
80
+ { where: { deletedAt: { exists: false } } } // no deletedAt -> IS NULL
81
+ { where: { verifiedAt: { exists: true } } } // has verifiedAt -> IS NOT NULL
82
+
83
+ // notExists is the inverse
84
+ { where: { verifiedAt: { notExists: true } } } // IS NULL
85
+
86
+ // exists over a JSON path (PostgreSQL)
87
+ { where: { 'metadata.score': { exists: true } } }
88
+ ```
89
+
90
+ | Syntax | SQL | Description |
91
+ |--------|-----|-------------|
92
+ | `{ field: { exists: true } }` | `IS NOT NULL` | Field is present |
93
+ | `{ field: { exists: false } }` | `IS NULL` | Field is missing/null |
94
+ | `{ field: { notExists: true } }` | `IS NULL` | Inverse of exists |
95
+ | `{ field: { notExists: false } }` | `IS NOT NULL` | Inverse of exists |
96
+
97
+
74
98
  ## Null Check Summary
75
99
 
76
100
  | Syntax | SQL | Description |
@@ -78,11 +102,16 @@ The simplest way to check for NULL:
78
102
  | `{ field: null }` | `IS NULL` | Direct null check |
79
103
  | `{ field: { eq: null } }` | `IS NULL` | Explicit null equality |
80
104
  | `{ field: { is: null } }` | `IS NULL` | IS operator with null |
105
+ | `{ field: { exists: false } }` | `IS NULL` | Presence check (false = missing) |
81
106
  | `{ field: { ne: null } }` | `IS NOT NULL` | Not-equal null check |
82
107
  | `{ field: { neq: null } }` | `IS NOT NULL` | Alias for ne with null |
83
108
  | `{ field: { isn: null } }` | `IS NOT NULL` | IS NOT operator with null |
109
+ | `{ field: { exists: true } }` | `IS NOT NULL` | Presence check (true = present) |
110
+
111
+ All the IS NULL / IS NOT NULL syntaxes above are equivalent -- use whichever reads best in context.
84
112
 
85
- All six syntaxes for IS NULL / IS NOT NULL are equivalent -- use whichever reads best in context.
113
+ > [!NOTE]
114
+ > `ne`/`neq` follow SQL three-valued logic: a row whose field is `NULL` never matches `{ field: { neq: value } }`. This is intentional (`NULL <> value` is UNKNOWN, not TRUE). Reach for `exists`/`notExists` or an explicit `{ field: null }` branch when you want NULL rows included.
86
115
 
87
116
 
88
117
  ## Common Patterns
@@ -21,6 +21,9 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
21
21
  | `lt` | `<` | `{ price: { lt: 100 } }` | Less than |
22
22
  | `lte` | `<=` | `{ price: { lte: 100 } }` | Less than or equal |
23
23
 
24
+ > [!NOTE]
25
+ > `ne`/`neq` follow SQL three-valued logic: a row whose field is `NULL` **never** matches `{ field: { neq: value } }` (`NULL <> value` evaluates to UNKNOWN, not TRUE). To include NULL rows, add `{ or: [{ field: { neq: value } }, { field: null }] }`.
26
+
24
27
  **See:** [Comparison Operators Guide](./comparison-operators.md)
25
28
 
26
29
 
@@ -88,12 +91,27 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
88
91
  **See:** [Null Operators Guide](./null-operators.md)
89
92
 
90
93
 
94
+ ## Presence & Negation Operators
95
+
96
+ | Operator | SQL | TypeScript Example | Description |
97
+ |----------|-----|-------------------|-------------|
98
+ | `exists` | `IS NOT NULL` / `IS NULL` | `{ deletedAt: { exists: false } }` | `exists: true` -> IS NOT NULL, `exists: false` -> IS NULL |
99
+ | `notExists` | `IS NULL` / `IS NOT NULL` | `{ verifiedAt: { notExists: true } }` | Inverse of `exists` (`notExists: true` -> IS NULL) |
100
+ | `not` | `NOT (...)` | `{ status: { not: 'archived' } }` / `{ views: { not: { gt: 100 } } }` | Negates the nested condition; a bare value negates `eq` |
101
+
102
+ `exists`/`notExists`/`not` are supported on the PostgreSQL connector. `not` recurses: `{ not: { gt: 100 } }` becomes `NOT (col > 100)`, and `{ not: 5 }` becomes `NOT (col = 5)`. `exists` also works over JSON paths on PostgreSQL (`{ 'metadata.score': { exists: true } }`).
103
+
104
+ **See:** [Null Operators Guide](./null-operators.md)
105
+
106
+
91
107
  ## Logical Operators
92
108
 
93
109
  | Operator | SQL | TypeScript Example | Description |
94
110
  |----------|-----|-------------------|-------------|
95
111
  | `and` | `AND` | `{ and: [{ age: { gt: 18 } }, { status: 'active' }] }` | All conditions must be true |
96
112
  | `or` | `OR` | `{ or: [{ role: 'admin' }, { role: 'moderator' }] }` | At least one condition must be true |
113
+ | `and: []` | (dropped) | `{ and: [] }` | Vacuously true - no condition added to the query |
114
+ | `or: []` | `false` | `{ or: [] }` | Vacuously false - matches no rows |
97
115
 
98
116
  **Implicit AND:**
99
117
  ```typescript
@@ -106,7 +124,7 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
106
124
  // WHERE status = 'active' AND age >= 18 AND role = 'user'
107
125
  ```
108
126
 
109
- **NOT logic** is expressed via negation operators (`ne`, `neq`, `nin`, `nlike`, `nilike`, `notBetween`, `isn`).
127
+ **NOT logic** is expressed via the general-purpose `not` operator (`{ field: { not: <value | operatorObject> } }`) or the dedicated negation operators (`ne`, `neq`, `nin`, `nlike`, `nilike`, `notBetween`, `isn`).
110
128
 
111
129
  **See:** [Logical Operators Guide](./logical-operators.md)
112
130
 
@@ -170,6 +188,7 @@ All comparison operators work with JSON path queries:
170
188
  - `between`, `notBetween`
171
189
  - `regexp`, `iregexp`
172
190
  - `is`, `isn`
191
+ - `exists`, `notExists`, `not`
173
192
 
174
193
  Numeric operators (`gt`, `gte`, `lt`, `lte`, `between`, `notBetween`) use safe numeric casting to handle mixed JSON value types.
175
194
 
@@ -181,7 +200,7 @@ Numeric operators (`gt`, `gte`, `lt`, `lte`, `between`, `notBetween`) use safe n
181
200
  ### Select Specific Fields
182
201
 
183
202
  ```typescript
184
- const users = await userRepo.find({
203
+ const users = await userRepository.find({
185
204
  filter: {
186
205
  where: { isActive: true },
187
206
  fields: ['id', 'name', 'email'], // Only return these fields
@@ -242,11 +261,11 @@ export class User extends BaseEntity<typeof User.schema> {
242
261
  }
243
262
 
244
263
  // All queries automatically include the default filter
245
- await userRepo.find({ filter: {} });
264
+ await userRepository.find({ filter: {} });
246
265
  // WHERE isDeleted = false LIMIT 100
247
266
 
248
267
  // Skip default filter for admin operations
249
- await userRepo.find({
268
+ await userRepository.find({
250
269
  filter: {},
251
270
  options: { shouldSkipDefaultFilter: true },
252
271
  });
@@ -65,7 +65,7 @@ const chunkSize = 500;
65
65
  const results = [];
66
66
  for (let i = 0; i < allIds.length; i += chunkSize) {
67
67
  const chunk = allIds.slice(i, i + chunkSize);
68
- const chunkResults = await repo.find({
68
+ const chunkResults = await repository.find({
69
69
  filter: { where: { id: { in: chunk } } }
70
70
  });
71
71
  results.push(...chunkResults);
@@ -89,7 +89,7 @@ for (let i = 0; i < allIds.length; i += chunkSize) {
89
89
 
90
90
  ```typescript
91
91
  // Enable logging to see generated SQL
92
- const result = await repo.find({
92
+ const result = await repository.find({
93
93
  filter: complexFilter,
94
94
  options: {
95
95
  log: { use: true, level: 'debug' },
@@ -97,7 +97,7 @@ const result = await repo.find({
97
97
  });
98
98
 
99
99
  // Or use buildQuery to inspect without executing
100
- const queryOptions = repo.buildQuery({ filter: complexFilter });
100
+ const queryOptions = repository.buildQuery({ filter: complexFilter });
101
101
  console.log('Generated query options:', queryOptions);
102
102
  ```
103
103
 
@@ -124,7 +124,7 @@ console.log('Generated query options:', queryOptions);
124
124
  ## Tip 8: Combining Multiple Array Conditions
125
125
 
126
126
  ```typescript
127
- await productRepo.find({
127
+ await productRepository.find({
128
128
  filter: {
129
129
  where: {
130
130
  // Must have ALL these categories
@@ -181,7 +181,7 @@ const createPaginationFilter = (page: number, size: number = 20) => ({
181
181
  });
182
182
 
183
183
  // Usage
184
- const products = await productRepo.find({
184
+ const products = await productRepository.find({
185
185
  filter: {
186
186
  where: {
187
187
  ...createActiveFilter(),
@@ -12,7 +12,7 @@ Real-world examples of filter usage with corresponding SQL.
12
12
  ## E-commerce Product Search
13
13
 
14
14
  ```typescript
15
- const products = await productRepo.find({
15
+ const products = await productRepository.find({
16
16
  filter: {
17
17
  where: {
18
18
  category: 'electronics',
@@ -44,7 +44,7 @@ const products = await productRepo.find({
44
44
  const thirtyDaysAgo = new Date();
45
45
  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
46
46
 
47
- const recentUsers = await userRepo.find({
47
+ const recentUsers = await userRepository.find({
48
48
  filter: {
49
49
  where: {
50
50
  createdAt: { gte: thirtyDaysAgo },
@@ -71,7 +71,7 @@ const recentUsers = await userRepo.find({
71
71
  ## Task Management: Priority Tags
72
72
 
73
73
  ```typescript
74
- const priorityTasks = await taskRepo.find({
74
+ const priorityTasks = await taskRepository.find({
75
75
  filter: {
76
76
  where: {
77
77
  status: { nin: ['completed', 'cancelled'] },
@@ -100,7 +100,7 @@ const priorityTasks = await taskRepo.find({
100
100
 
101
101
  ```typescript
102
102
  // Find active records (soft delete pattern)
103
- const activeRecords = await repo.find({
103
+ const activeRecords = await repository.find({
104
104
  filter: {
105
105
  where: { deletedAt: { is: null } },
106
106
  }
@@ -112,7 +112,7 @@ const activeRecords = await repo.find({
112
112
 
113
113
  ```typescript
114
114
  // Find ONLY soft-deleted records
115
- const deletedRecords = await repo.find({
115
+ const deletedRecords = await repository.find({
116
116
  filter: {
117
117
  where: { deletedAt: { isn: null } },
118
118
  }
@@ -142,7 +142,7 @@ const getAuthorizedFilter = (user: User): TWhere<TDocumentSchema> => {
142
142
  };
143
143
  };
144
144
 
145
- const documents = await documentRepo.find({
145
+ const documents = await documentRepository.find({
146
146
  filter: {
147
147
  where: getAuthorizedFilter(currentUser),
148
148
  order: ['updatedAt DESC'],
@@ -206,7 +206,7 @@ const searchProducts = async (query: string, filters: {
206
206
  where.categories = { contains: filters.categories };
207
207
  }
208
208
 
209
- return productRepo.find({
209
+ return productRepository.find({
210
210
  filter: {
211
211
  where,
212
212
  order: ['rating DESC', 'createdAt DESC'],
@@ -281,7 +281,7 @@ const massiveFilter: TFilter<TProductSchema> = {
281
281
  ],
282
282
  };
283
283
 
284
- const products = await productRepo.find({ filter: massiveFilter });
284
+ const products = await productRepository.find({ filter: massiveFilter });
285
285
 
286
286
  // SQL:
287
287
  // SELECT "id", "name", "price", "rating", "tags", "metadata"
@@ -334,7 +334,7 @@ const products = await productRepo.find({ filter: massiveFilter });
334
334
  const startOfWeek = new Date('2024-12-29');
335
335
  const endOfWeek = new Date('2025-01-04');
336
336
 
337
- const weekEvents = await eventRepo.find({
337
+ const weekEvents = await eventRepository.find({
338
338
  filter: {
339
339
  where: {
340
340
  eventDate: { between: [startOfWeek, endOfWeek] }
@@ -355,7 +355,7 @@ const weekEvents = await eventRepo.find({
355
355
  const sevenDaysAgo = new Date();
356
356
  sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
357
357
 
358
- const recentOrders = await orderRepo.find({
358
+ const recentOrders = await orderRepository.find({
359
359
  filter: {
360
360
  where: {
361
361
  createdAt: { gte: sevenDaysAgo },
@@ -382,7 +382,7 @@ const recentOrders = await orderRepo.find({
382
382
 
383
383
  ```typescript
384
384
  const getTenantProducts = async (tenantId: string, filter: TFilter<TProductSchema>) => {
385
- return productRepo.find({
385
+ return productRepository.find({
386
386
  filter: {
387
387
  ...filter,
388
388
  where: {
@@ -415,7 +415,7 @@ await getTenantProducts('tenant-abc', {
415
415
  ## Inventory Low Stock Alert
416
416
 
417
417
  ```typescript
418
- const lowStockProducts = await productRepo.find({
418
+ const lowStockProducts = await productRepository.find({
419
419
  filter: {
420
420
  where: {
421
421
  status: 'active',
@@ -6,9 +6,9 @@ difficulty: intermediate
6
6
 
7
7
  # Deep Dive: gRPC Controllers
8
8
 
9
- Technical reference for gRPC controller classes -- the foundation for building gRPC services in Ignis, powered by [ConnectRPC](https://connectrpc.com/).
9
+ Technical reference for gRPC controller classes -- the foundation for building gRPC services in IGNIS, powered by [ConnectRPC](https://connectrpc.com/).
10
10
 
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.
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
14
  - `packages/core/src/base/controllers/grpc/abstract.ts`
@@ -25,7 +25,7 @@ Ignis gRPC controllers follow the same patterns as REST controllers (decorator-b
25
25
  |------|-------------|
26
26
  | **AbstractGrpcController** | Abstract base class with RPC registration, ConnectRPC adapter mounting, idempotent `configure()` |
27
27
  | **BaseGrpcController** | Recommended concrete base class with `bindRoute()` and `defineRoute()` implementations |
28
- | **GrpcRequestAdapter** | Internal bridge from Ignis handlers to ConnectRPC universal handlers via `AsyncLocalStorage` |
28
+ | **GrpcRequestAdapter** | Internal bridge from IGNIS handlers to ConnectRPC universal handlers via `AsyncLocalStorage` |
29
29
  | **GrpcComponent** | Auto-discovers gRPC controllers and mounts them on the application router |
30
30
  | **@controller** | Class decorator with `transport: ControllerTransports.GRPC` and `service` field |
31
31
  | **@unary** | Method decorator for unary RPCs |
@@ -57,7 +57,7 @@ bun add @connectrpc/connect-web
57
57
  ```
58
58
 
59
59
  > [!NOTE]
60
- > These are **optional** peer dependencies. They are only loaded at runtime when a gRPC controller is configured, via `createRequire` from the application's `node_modules`. If the deps are missing, `GrpcRequestAdapter.build()` throws a clear error at startup via `validateModule()`.
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.
61
61
 
62
62
  ### Protobuf Code Generation
63
63
 
@@ -335,7 +335,7 @@ type TRpcHandler<
335
335
  ```
336
336
 
337
337
  > [!NOTE]
338
- > When using decorator-based RPCs, the handler method signature is `(opts: { request: RequestType }) => Promise<ResponseType>`. The `context` parameter is injected internally by the adapter and is not passed to the decorator-based handler method directly. The full `TRpcHandler` signature (with `context`) applies when using `defineRoute()` or `bindRoute()`.
338
+ > The adapter always calls handlers with `{ request, context }` - decorator-based handlers included. If your handler does not need the Hono context, declare only `(opts: { request: RequestType }) => Promise<ResponseType>` and ignore the `context` property.
339
339
 
340
340
  ### `IGrpcControllerOptions`
341
341
 
@@ -411,11 +411,11 @@ interface IConnectAdapterResult<
411
411
 
412
412
  ## `GrpcRequestAdapter`
413
413
 
414
- Internal bridge between Ignis gRPC controllers and ConnectRPC's universal handler system. You do not interact with this class directly -- it is created automatically during `configure()`.
414
+ Internal bridge between IGNIS gRPC controllers and ConnectRPC's universal handler system. You do not interact with this class directly -- it is created automatically during `configure()`.
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, 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.
419
419
 
420
420
  ```
421
421
  Hono Request
@@ -423,7 +423,7 @@ Hono Request
423
423
  -> AsyncLocalStorage.run(honoContext, ...)
424
424
  -> Pre-built auth middlewares (authenticate -> authorize)
425
425
  -> ConnectRPC universal handler
426
- -> Ignis TRpcHandler (reads context from AsyncLocalStorage)
426
+ -> IGNIS TRpcHandler (reads context from AsyncLocalStorage)
427
427
  -> Response
428
428
  ```
429
429
 
@@ -449,7 +449,7 @@ The optional `interceptors` array is passed to ConnectRPC's `createConnectRouter
449
449
 
450
450
  ### Internal Flow
451
451
 
452
- 1. **`buildConnectHandlers()`** -- Wraps each Ignis `TRpcHandler` into ConnectRPC's `(request, context) => response` signature. The wrapper reads the Hono context from `AsyncLocalStorage`, runs pre-built auth middlewares (built by `AbstractGrpcController.buildRpcMiddlewares()`), then passes `{ request, context }` to the Ignis handler.
452
+ 1. **`buildConnectHandlers()`** -- Wraps each IGNIS `TRpcHandler` into ConnectRPC's `(request, context) => response` signature. The wrapper reads the Hono context from `AsyncLocalStorage`, runs pre-built auth middlewares (built by `AbstractGrpcController.buildRpcMiddlewares()`), then passes `{ request, context }` to the IGNIS handler.
453
453
 
454
454
  2. **`registerService()`** -- Bridges the opaque `ServiceType` from `@controller` metadata to ConnectRPC's `router.service()` call, registering all handlers for the service.
455
455
 
@@ -839,7 +839,7 @@ import { GreeterService, SayHelloRequestSchema } from './controllers/greeter/def
839
839
  const transport = createConnectTransport({ baseUrl: 'http://localhost:3000/grpc' });
840
840
  const client = createClient(GreeterService, transport);
841
841
 
842
- const response = await client.sayHello(create(SayHelloRequestSchema, { name: 'Ignis' }));
842
+ const response = await client.sayHello(create(SayHelloRequestSchema, { name: 'IGNIS' }));
843
843
  console.log(response.message);
844
844
  ```
845
845
 
@@ -928,7 +928,7 @@ Per-RPC authentication and authorization are configured via the `authenticate` a
928
928
  name: 'sayHello',
929
929
  authenticate: {
930
930
  strategies: ['jwt'],
931
- mode: 'required',
931
+ mode: 'any',
932
932
  },
933
933
  },
934
934
  })
@@ -941,7 +941,7 @@ async sayHello(opts: { request: SayHelloRequest }): Promise<SayHelloResponse> {
941
941
  | Field | Type | Default | Description |
942
942
  |-------|------|---------|-------------|
943
943
  | `strategies` | `TAuthStrategy[]` | `[]` | Authentication strategies to apply (e.g., `['jwt']`, `['basic']`) |
944
- | `mode` | `TAuthMode` | `'any'` | `'required'` \| `'optional'` \| `'any'` \| `'all'` (defaults to `AuthenticationModes.ANY`) |
944
+ | `mode` | `TAuthMode` | `'any'` | `'any'` \| `'all'` (defaults to `AuthenticationModes.ANY`) |
945
945
 
946
946
  ### Per-RPC Authorization
947
947
 
@@ -949,7 +949,7 @@ async sayHello(opts: { request: SayHelloRequest }): Promise<SayHelloResponse> {
949
949
  @unary({
950
950
  configs: {
951
951
  name: 'deleteUser',
952
- authenticate: { strategies: ['jwt'], mode: 'required' },
952
+ authenticate: { strategies: ['jwt'], mode: 'any' },
953
953
  authorize: { action: 'delete', resource: 'user' },
954
954
  },
955
955
  })
@@ -1,6 +1,9 @@
1
1
  # Base Abstractions
2
2
 
3
- Core classes that power every Ignis application - from the Application entry point to Repositories for data access.
3
+ Core classes that power every IGNIS application - from the Application entry point to Repositories for data access.
4
+
5
+ > [!IMPORTANT] Base vs. Connectors
6
+ > The persistence layer (`BaseDataSource`/`BaseEntity`/CRUD repositories) is split into an engine-neutral root (`src/base`) and per-engine connectors (`src/connectors/{postgres,typesense}`). `BaseDataSource` and `BaseEntity` below refer to the **PostgreSQL connector**'s canonical `BasePostgresDataSource`/`BasePostgresEntity` (re-exported under these compatibility names) - see [Connectors](./connectors) for the full picture, and [Search & Typesense](/guides/core-concepts/persistent/search-typesense) for the other engine.
4
7
 
5
8
  ## Quick Reference
6
9
 
@@ -12,10 +15,10 @@ Core classes that power every Ignis application - from the Application entry poi
12
15
  | `BaseService` | Business logic layer | - |
13
16
  | `BaseProvider` | Factory pattern for runtime instantiation | `BaseHelper` |
14
17
  | `BaseComponent` | Pluggable feature modules | - |
15
- | `BaseDataSource` | Database connections | - |
16
- | `BaseEntity` | Model definitions | - |
17
- | `DefaultCRUDRepository` | Full CRUD operations | `PersistableRepository` |
18
- | `ReadableRepository` | Read-only operations | `AbstractRepository` |
18
+ | `BaseDataSource` (alias of `BasePostgresDataSource`) | PostgreSQL connections | `AbstractPostgresDataSource` -> `AbstractDataSource` |
19
+ | `BaseEntity` (alias of `BasePostgresEntity`) | Drizzle model definitions | `AbstractEntity` |
20
+ | `DefaultCRUDRepository` | Full CRUD operations (PostgreSQL connector) | `PersistableRepository` -> ... -> `AbstractRepository` |
21
+ | `ReadableRepository` | Read-only operations (PostgreSQL connector) | `PostgresBaseRepository` -> `AbstractRepository` |
19
22
 
20
23
  ## Architecture
21
24
 
@@ -67,6 +70,7 @@ Core classes that power every Ignis application - from the Application entry poi
67
70
  - [Components](./components.md) - Pluggable modules, component lifecycle
68
71
 
69
72
  ### Data Layer
73
+ - [Connectors](./connectors.md) - Base-vs-connectors architecture, dual-door exports, adding an engine
70
74
  - [Models & Enrichers](./models.md) - `BaseEntity`, schema definitions, enrichers
71
75
  - [DataSources](./datasources.md) - Database connections, auto-discovery
72
76
  - [Repositories](./repositories/) - CRUD operations, filtering, relations
@@ -78,11 +82,14 @@ Core classes that power every Ignis application - from the Application entry poi
78
82
  AbstractApplication
79
83
  └── BaseApplication ──────► Your Application
80
84
 
81
- AbstractRepository
82
- ├── ReadableRepository
83
- └── PersistableRepository
84
- └── DefaultCRUDRepository ──────► Your Repository
85
-
85
+ AbstractRepository (engine-neutral, src/base)
86
+ ├── PostgresBaseRepository (connectors/postgres)
87
+ ├── ReadableRepository
88
+ └── PersistableRepository
89
+ │ └── DefaultCRUDRepository ──────► Your Repository
90
+ └── TypesenseBaseRepository (connectors/typesense)
91
+ └── ReadableSearchRepository -> ... -> DefaultSearchRepository
92
+
86
93
  AbstractRestController
87
94
  └── BaseRestController ──────► Your REST Controller
88
95
 
@@ -91,8 +98,13 @@ AbstractGrpcController
91
98
  BaseService ──────► Your Service
92
99
  BaseProvider ──────► Your Provider
93
100
  BaseComponent ──────► Your Component
94
- BaseDataSource ──────► Your DataSource
95
- BaseEntity ──────► Your Model
101
+
102
+ AbstractDataSource (engine-neutral, src/base)
103
+ ├── AbstractPostgresDataSource -> BasePostgresDataSource (alias: BaseDataSource) ──────► Your DataSource
104
+ └── AbstractSearchDataSource -> BaseSearchDataSource -> TypesenseDataSource
105
+
106
+ AbstractEntity (engine-neutral, src/base)
107
+ └── BasePostgresEntity (alias: BaseEntity) ──────► Your Model
96
108
  ```
97
109
 
98
110
  > **Related:** [Core Concepts Guide](../../guides/core-concepts/application/) | [Persistent Layer Guide](../../guides/core-concepts/persistent/)