@venizia/ignis-docs 0.0.8-3 → 0.1.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 (213) hide show
  1. package/README.md +7 -7
  2. package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
  3. package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
  4. package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
  5. package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
  9. package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
  11. package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
  14. package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
  16. package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
  17. package/{wiki → content}/best-practices/data-modeling.md +44 -20
  18. package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
  19. package/{wiki → content}/best-practices/error-handling.md +48 -24
  20. package/{wiki → content}/best-practices/index.md +5 -5
  21. package/{wiki → content}/best-practices/performance-optimization.md +36 -28
  22. package/{wiki → content}/best-practices/security-guidelines.md +52 -23
  23. package/{wiki → content}/best-practices/testing-strategies.md +65 -51
  24. package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
  25. package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
  26. package/{wiki → content}/extensions/components/authentication/api.md +19 -19
  27. package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
  28. package/{wiki → content}/extensions/components/authentication/index.md +10 -8
  29. package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
  30. package/{wiki → content}/extensions/components/authorization/api.md +45 -25
  31. package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
  32. package/{wiki → content}/extensions/components/authorization/index.md +11 -10
  33. package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
  34. package/{wiki → content}/extensions/components/health-check.md +1 -1
  35. package/{wiki → content}/extensions/components/index.md +5 -5
  36. package/{wiki → content}/extensions/components/mail/errors.md +15 -15
  37. package/{wiki → content}/extensions/components/mail/index.md +1 -2
  38. package/{wiki → content}/extensions/components/mail/usage.md +1 -1
  39. package/{wiki → content}/extensions/components/request-tracker.md +1 -1
  40. package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
  41. package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
  42. package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
  43. package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
  44. package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
  45. package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
  46. package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
  47. package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
  48. package/{wiki → content}/extensions/components/template/index.md +2 -2
  49. package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
  50. package/{wiki → content}/extensions/components/websocket/api.md +3 -3
  51. package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
  52. package/{wiki → content}/extensions/components/websocket/index.md +5 -5
  53. package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
  54. package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
  55. package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
  56. package/{wiki → content}/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +283 -0
  58. package/{wiki → content}/extensions/helpers/index.md +2 -3
  59. package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
  60. package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
  61. package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
  62. package/{wiki → content}/extensions/helpers/network/index.md +6 -0
  63. package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
  64. package/content/extensions/helpers/redis/index.md +713 -0
  65. package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
  66. package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
  67. package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
  68. package/{wiki → content}/extensions/helpers/template/index.md +6 -3
  69. package/{wiki → content}/extensions/helpers/types/index.md +11 -8
  70. package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
  71. package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
  72. package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
  73. package/{wiki → content}/extensions/index.md +3 -4
  74. package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
  75. package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
  76. package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
  77. package/{wiki → content}/guides/core-concepts/components.md +19 -10
  78. package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
  79. package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
  80. package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
  81. package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
  82. package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
  83. package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
  84. package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
  85. package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
  86. package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
  87. package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
  88. package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
  89. package/content/guides/core-concepts/services.md +389 -0
  90. package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
  91. package/{wiki → content}/guides/get-started/philosophy.md +36 -36
  92. package/{wiki → content}/guides/get-started/setup.md +3 -3
  93. package/{wiki → content}/guides/index.md +3 -3
  94. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  95. package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
  96. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  97. package/{wiki → content}/guides/reference/glossary.md +19 -12
  98. package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
  99. package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
  100. package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
  101. package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
  102. package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
  103. package/content/guides/tutorials/testing.md +264 -0
  104. package/content/index.md +5 -0
  105. package/content/public/apple-touch-icon.png +0 -0
  106. package/content/public/og-image.png +0 -0
  107. package/content/public/site.webmanifest +11 -0
  108. package/{wiki → content}/references/base/application.md +4 -5
  109. package/{wiki → content}/references/base/bootstrapping.md +18 -5
  110. package/{wiki → content}/references/base/components.md +149 -120
  111. package/content/references/base/connectors.md +178 -0
  112. package/{wiki → content}/references/base/controllers.md +41 -30
  113. package/content/references/base/datasources.md +527 -0
  114. package/{wiki → content}/references/base/dependency-injection.md +34 -22
  115. package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
  116. package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
  117. package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
  118. package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
  119. package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
  120. package/{wiki → content}/references/base/filter-system/index.md +6 -3
  121. package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
  122. package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
  123. package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
  124. package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
  125. package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
  126. package/{wiki → content}/references/base/filter-system/tips.md +5 -5
  127. package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
  128. package/{wiki → content}/references/base/grpc-controllers.md +13 -13
  129. package/{wiki → content}/references/base/index.md +24 -12
  130. package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
  131. package/{wiki → content}/references/base/models.md +63 -49
  132. package/{wiki → content}/references/base/providers.md +136 -130
  133. package/{wiki → content}/references/base/repositories/advanced.md +59 -58
  134. package/{wiki → content}/references/base/repositories/index.md +115 -91
  135. package/content/references/base/repositories/mixins.md +99 -0
  136. package/{wiki → content}/references/base/repositories/relations.md +54 -64
  137. package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
  138. package/content/references/base/services.md +404 -0
  139. package/{wiki → content}/references/configuration/environment-variables.md +46 -30
  140. package/{wiki → content}/references/configuration/index.md +6 -6
  141. package/{wiki → content}/references/index.md +17 -12
  142. package/{wiki → content}/references/quick-reference.md +65 -106
  143. package/content/references/utilities/crypto.md +98 -0
  144. package/{wiki → content}/references/utilities/index.md +3 -3
  145. package/{wiki → content}/references/utilities/jsx.md +6 -4
  146. package/content/references/utilities/module.md +90 -0
  147. package/{wiki → content}/references/utilities/parse.md +4 -14
  148. package/{wiki → content}/references/utilities/promise.md +9 -7
  149. package/{wiki → content}/references/utilities/schema.md +5 -3
  150. package/dist/mcp-server/common/guards.d.ts +8 -0
  151. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  152. package/dist/mcp-server/common/guards.js +14 -0
  153. package/dist/mcp-server/common/guards.js.map +1 -0
  154. package/dist/mcp-server/common/index.d.ts +1 -0
  155. package/dist/mcp-server/common/index.d.ts.map +1 -1
  156. package/dist/mcp-server/common/index.js +1 -0
  157. package/dist/mcp-server/common/index.js.map +1 -1
  158. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  159. package/dist/mcp-server/common/paths.js +2 -2
  160. package/dist/mcp-server/common/paths.js.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  162. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  163. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  164. package/dist/mcp-server/helpers/github.helper.js +1 -1
  165. package/dist/mcp-server/index.js +7 -2
  166. package/dist/mcp-server/index.js.map +1 -1
  167. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  168. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  169. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  170. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  175. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  178. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  179. package/package.json +12 -12
  180. package/wiki/extensions/helpers/error/index.md +0 -227
  181. package/wiki/extensions/helpers/redis/index.md +0 -488
  182. package/wiki/extensions/helpers/testing/index.md +0 -510
  183. package/wiki/guides/core-concepts/services.md +0 -119
  184. package/wiki/guides/tutorials/testing.md +0 -722
  185. package/wiki/index.md +0 -183
  186. package/wiki/references/base/datasources.md +0 -454
  187. package/wiki/references/base/middlewares.md +0 -590
  188. package/wiki/references/base/repositories/mixins.md +0 -335
  189. package/wiki/references/base/services.md +0 -201
  190. package/wiki/references/utilities/crypto.md +0 -56
  191. package/wiki/references/utilities/module.md +0 -42
  192. /package/{wiki → content}/extensions/components/mail/api.md +0 -0
  193. /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
  194. /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
  195. /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
  196. /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
  197. /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
  198. /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
  199. /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
  200. /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
  201. /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
  202. /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
  203. /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
  204. /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
  205. /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
  206. /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
  207. /package/{wiki → content}/public/logo.svg +0 -0
  208. /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
  209. /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
  210. /package/{wiki → content}/references/utilities/date.md +0 -0
  211. /package/{wiki → content}/references/utilities/performance.md +0 -0
  212. /package/{wiki → content}/references/utilities/request.md +0 -0
  213. /package/{wiki → content}/references/utilities/statuses.md +0 -0
@@ -30,8 +30,6 @@ await app.start();
30
30
  - `initialize()` - Bootstrap the application
31
31
  - `start()` - Start HTTP server
32
32
  - `stop()` - Stop server gracefully
33
- - `get<T>(key)` - Resolve from DI container
34
- - `mountControllers()` - Register controllers
35
33
 
36
34
  ### BaseRestController
37
35
 
@@ -85,7 +83,7 @@ class GreeterController extends BaseGrpcController {
85
83
  ```typescript
86
84
  import { BaseService, injectable } from '@venizia/ignis';
87
85
 
88
- @injectable()
86
+ @injectable({})
89
87
  class UserService extends BaseService {
90
88
  constructor() {
91
89
  super({ scope: UserService.name });
@@ -93,7 +91,7 @@ class UserService extends BaseService {
93
91
 
94
92
  async getUser(id: string) {
95
93
  this.logger.info('Getting user', id);
96
- return this.userRepo.findById(id);
94
+ return this.userRepository.findById({ id });
97
95
  }
98
96
  }
99
97
  ```
@@ -104,27 +102,25 @@ class UserService extends BaseService {
104
102
  ### DefaultCRUDRepository
105
103
 
106
104
  ```typescript
107
- import { DefaultCRUDRepository } from '@venizia/ignis';
105
+ import { DefaultCRUDRepository, repository } from '@venizia/ignis';
108
106
  import { User } from '../models';
107
+ import { PostgresDataSource } from '../datasources';
109
108
 
110
- class UserRepository extends DefaultCRUDRepository<User> {
111
- constructor() {
112
- super(User);
113
- }
109
+ @repository({ model: User, dataSource: PostgresDataSource })
110
+ class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
111
+ // No constructor needed - dataSource auto-injected from @repository decorator
114
112
  }
115
113
  ```
116
114
 
117
115
  **Key Methods:**
118
- - `create(data)` - Create single entity
119
- - `createMany(data[])` - Create multiple entities
120
- - `find(filter?)` - Find many with filter
121
- - `findById(id)` - Find by ID
122
- - `findOne(filter)` - Find single entity
123
- - `count(filter?)` - Count entities
124
- - `update(id, data)` - Update by ID
125
- - `updateMany(filter, data)` - Update multiple
126
- - `delete(id)` - Delete by ID (soft/hard based on config)
127
- - `deleteMany(filter)` - Delete multiple
116
+ - `find({ filter })` - Find many with filter, returns `T[]`
117
+ - `findById({ id })` - Find by ID
118
+ - `findOne({ filter })` - Find single entity
119
+ - `count({ where? })` - Count entities
120
+ - `create({ data })` - Create single entity, returns `{ count, data }`
121
+ - `createAll({ data: [] })` - Create multiple entities, returns `{ count, data[] }`
122
+ - `updateById({ id, data })` - Update by ID
123
+ - `deleteById({ id })` - Delete by ID
128
124
 
129
125
  ### BaseEntity
130
126
 
@@ -132,10 +128,10 @@ class UserRepository extends DefaultCRUDRepository<User> {
132
128
  import { BaseEntity, model } from '@venizia/ignis';
133
129
  import { integer, text, pgTable } from 'drizzle-orm/pg-core';
134
130
 
135
- @model()
131
+ @model({ type: 'entity' })
136
132
  class User extends BaseEntity {
137
- static readonly tableName = 'users';
138
- static readonly schema = pgTable(User.tableName, {
133
+ static readonly TABLE_NAME = 'users';
134
+ static readonly schema = pgTable(User.TABLE_NAME, {
139
135
  id: integer('id').primaryKey(),
140
136
  name: text('name').notNull(),
141
137
  email: text('email').notNull().unique(),
@@ -144,7 +140,7 @@ class User extends BaseEntity {
144
140
  ```
145
141
 
146
142
  **Key Properties:**
147
- - `static tableName` - Database table name
143
+ - `static TABLE_NAME` - Database table name
148
144
  - `static schema` - Drizzle schema definition
149
145
  - `static AUTHORIZATION_SUBJECT` - Authorization principal (auto-set from `@model` settings `authorize.principal`)
150
146
 
@@ -226,7 +222,7 @@ class UserController extends BaseRestController {
226
222
  | Operator | SQL | Example |
227
223
  |----------|-----|---------|
228
224
  | `in` | `IN` | `{ status: { in: ['active', 'pending'] } }` |
229
- | `notIn` | `NOT IN` | `{ status: { notIn: ['deleted'] } }` |
225
+ | `nin` | `NOT IN` | `{ status: { nin: ['deleted'] } }` |
230
226
 
231
227
  ### Pattern Matching
232
228
 
@@ -234,17 +230,8 @@ class UserController extends BaseRestController {
234
230
  |----------|-----|---------|
235
231
  | `like` | `LIKE` | `{ name: { like: '%john%' } }` |
236
232
  | `ilike` | `ILIKE` | `{ email: { ilike: '%@gmail.com' } }` |
237
- | `notLike` | `NOT LIKE` | `{ name: { notLike: '%test%' } }` |
238
- | `notILike` | `NOT ILIKE` | `{ email: { notILike: '%spam%' } }` |
239
- | `startsWith` | `LIKE 'value%'` | `{ name: { startsWith: 'John' } }` |
240
- | `endsWith` | `LIKE '%value'` | `{ email: { endsWith: '@example.com' } }` |
241
-
242
- ### Null Operators
243
-
244
- | Operator | SQL | Example |
245
- |----------|-----|---------|
246
- | `isNull` | `IS NULL` | `{ deletedAt: { isNull: true } }` |
247
- | `isNotNull` | `IS NOT NULL` | `{ email: { isNotNull: true } }` |
233
+ | `nlike` | `NOT LIKE` | `{ name: { nlike: '%test%' } }` |
234
+ | `nilike` | `NOT ILIKE` | `{ email: { nilike: '%spam%' } }` |
248
235
 
249
236
  ### Logical Operators
250
237
 
@@ -262,61 +249,59 @@ class UserController extends BaseRestController {
262
249
  | `containedBy` | `<@` | `{ tags: { containedBy: ['ts', 'js', 'go'] } }` |
263
250
  | `overlaps` | `&&` | `{ tags: { overlaps: ['react', 'vue'] } }` |
264
251
 
265
- ### JSON Operators (PostgreSQL)
266
-
267
- | Operator | Description | Example |
268
- |----------|-------------|---------|
269
- | `jsonPath` | Query JSON field | `{ metadata: { jsonPath: '$.user.name', eq: 'John' } }` |
270
-
271
-
272
252
  ## Common Filters
273
253
 
274
254
  ### Basic Find
275
255
 
276
256
  ```typescript
277
- const users = await userRepo.find({
278
- where: { isActive: true },
279
- orderBy: { createdAt: 'desc' },
280
- limit: 10,
281
- offset: 0,
257
+ const users = await userRepository.find({
258
+ filter: {
259
+ where: { isActive: true },
260
+ order: ['createdAt DESC'],
261
+ limit: 10,
262
+ offset: 0,
263
+ },
282
264
  });
283
265
  ```
284
266
 
285
267
  ### With Multiple Conditions
286
268
 
287
269
  ```typescript
288
- const users = await userRepo.find({
289
- where: {
290
- and: [
291
- { age: { gte: 18 } },
292
- { status: { in: ['active', 'pending'] } },
293
- { email: { endsWith: '@company.com' } }
294
- ]
295
- }
270
+ const users = await userRepository.find({
271
+ filter: {
272
+ where: {
273
+ and: [
274
+ { age: { gte: 18 } },
275
+ { status: { in: ['active', 'pending'] } },
276
+ { email: { ilike: '%@company.com' } },
277
+ ],
278
+ },
279
+ },
296
280
  });
297
281
  ```
298
282
 
299
283
  ### With Relations
300
284
 
301
285
  ```typescript
302
- const posts = await postRepo.find({
303
- where: { published: true },
304
- include: {
305
- author: true,
306
- comments: {
307
- where: { approved: true },
308
- limit: 5
309
- }
310
- }
286
+ const posts = await postRepository.find({
287
+ filter: {
288
+ where: { published: true },
289
+ include: [
290
+ { relation: 'author' },
291
+ { relation: 'comments', scope: { where: { approved: true }, limit: 5 } },
292
+ ],
293
+ },
311
294
  });
312
295
  ```
313
296
 
314
297
  ### Selecting Fields
315
298
 
316
299
  ```typescript
317
- const users = await userRepo.find({
318
- where: { isActive: true },
319
- fields: ['id', 'name', 'email'], // Only these fields
300
+ const users = await userRepository.find({
301
+ filter: {
302
+ where: { isActive: true },
303
+ fields: ['id', 'name', 'email'],
304
+ },
320
305
  });
321
306
  ```
322
307
 
@@ -328,7 +313,7 @@ const users = await userRepo.find({
328
313
  ```typescript
329
314
  import { injectable } from '@venizia/ignis';
330
315
 
331
- @injectable()
316
+ @injectable({})
332
317
  class MyService extends BaseService {
333
318
  // ...
334
319
  }
@@ -348,13 +333,6 @@ class UserController extends BaseRestController {
348
333
  }
349
334
  ```
350
335
 
351
- ### Manual Resolution
352
-
353
- ```typescript
354
- const userService = app.get<UserService>('services.UserService');
355
- ```
356
-
357
-
358
336
  ## Common Imports
359
337
 
360
338
  ### Core Framework
@@ -408,21 +386,20 @@ import {
408
386
  ApplicationLogger,
409
387
 
410
388
  // Caching
411
- RedisHelper,
389
+ RedisSingleHelper,
412
390
 
413
391
  // Queues
414
392
  QueueHelper,
415
393
 
416
394
  // Crypto
417
395
  hash,
418
- compare,
419
396
 
420
397
  // HTTP
421
398
  HTTP,
422
399
  } from '@venizia/ignis-helpers';
423
400
  import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
424
401
  import { CronHelper } from '@venizia/ignis-helpers/cron';
425
- import { MinIOHelper } from '@venizia/ignis-helpers/minio';
402
+ import { MinioHelper } from '@venizia/ignis-helpers/minio';
426
403
  ```
427
404
 
428
405
  ### Dependency Injection
@@ -487,14 +464,17 @@ getDashboard(c: Context) {
487
464
  import { Statuses } from '@venizia/ignis';
488
465
 
489
466
  // Create with status
490
- const order = await orderRepo.create({
491
- items: [...],
492
- status: Statuses.PENDING,
467
+ const { data: order } = await orderRepository.create({
468
+ data: {
469
+ items: [...],
470
+ status: Statuses.PENDING,
471
+ },
493
472
  });
494
473
 
495
474
  // Update status
496
- await orderRepo.update(orderId, {
497
- status: Statuses.COMPLETED,
475
+ await orderRepository.updateById({
476
+ id: orderId,
477
+ data: { status: Statuses.COMPLETED },
498
478
  });
499
479
 
500
480
  // Check status
@@ -556,27 +536,6 @@ app.notFound(notFoundHandler({ logger: app.logger }));
556
536
  ```
557
537
 
558
538
 
559
- ## Environment Variables
560
-
561
- ### Loading Environment
562
-
563
- ```typescript
564
- import { EnvHelper } from '@venizia/ignis-helpers';
565
-
566
- // Load from .env file
567
- EnvHelper.load();
568
-
569
- // Get variable
570
- const dbUrl = EnvHelper.get('DATABASE_URL');
571
-
572
- // Get with default
573
- const port = EnvHelper.get('PORT', '3000');
574
-
575
- // Get required (throws if missing)
576
- const apiKey = EnvHelper.getRequired('API_KEY');
577
- ```
578
-
579
-
580
539
  ## Common Patterns
581
540
 
582
541
  ### Controller → Service → Repository
@@ -605,7 +564,7 @@ class UserController extends BaseRestController {
605
564
  class UserService extends BaseService {
606
565
  constructor(
607
566
  @inject({ key: 'repositories.UserRepository' })
608
- private userRepo: UserRepository,
567
+ private userRepository: UserRepository,
609
568
  ) {
610
569
  super({ scope: UserService.name });
611
570
  }
@@ -614,7 +573,7 @@ class UserService extends BaseService {
614
573
  // Business logic
615
574
  const hashedPassword = await hash({ value: data.password });
616
575
 
617
- return this.userRepo.create({
576
+ return this.userRepository.create({
618
577
  data: {
619
578
  ...data,
620
579
  password: hashedPassword,
@@ -0,0 +1,98 @@
1
+ # Crypto Utility
2
+
3
+ The Crypto utility provides a single stateless `hash` function built on Node's built-in `node:crypto` module. It covers the two most common lightweight hashing needs - MD5 digests and HMAC-SHA256 signatures - without any external dependencies.
4
+
5
+ ::: tip Full AES / RSA / ECDH encryption
6
+ This page covers only the utility-level `hash` function. For full symmetric/asymmetric encryption (AES-256-CBC, AES-256-GCM, RSA, ECDH key exchange) see the [Crypto helper](/extensions/helpers/crypto/).
7
+ :::
8
+
9
+ ## `hash`
10
+
11
+ Creates a hash or HMAC digest of a string and returns it as a text-encoded string.
12
+
13
+ ### Signature
14
+
15
+ ```typescript
16
+ hash(
17
+ text: string,
18
+ options: {
19
+ algorithm: 'SHA256' | 'MD5';
20
+ secret?: string;
21
+ outputType: BinaryToTextEncoding; // 'hex' | 'base64' | 'base64url' | 'latin1'
22
+ },
23
+ ): string
24
+ ```
25
+
26
+ **Parameters**
27
+
28
+ | Parameter | Type | Description |
29
+ |-----------|------|-------------|
30
+ | `text` | `string` | The input string to hash. |
31
+ | `options.algorithm` | `'SHA256' \| 'MD5'` | Hashing algorithm. |
32
+ | `options.secret` | `string` (optional) | Secret key for HMAC. Only used when `algorithm` is `'SHA256'`. |
33
+ | `options.outputType` | `BinaryToTextEncoding` | Encoding of the output string - typically `'hex'` or `'base64'`. |
34
+
35
+ ### Behavior
36
+
37
+ | Algorithm | `secret` provided | Result |
38
+ |-----------|-------------------|--------|
39
+ | `'MD5'` | ignored | MD5 digest of `text` |
40
+ | `'SHA256'` | yes | HMAC-SHA256 of `text` signed with `secret` |
41
+ | `'SHA256'` | no / `undefined` | `text` returned unchanged (no-op) |
42
+ | any other | - | `text` returned unchanged (no-op) |
43
+
44
+ The SHA256 pass-through is intentional: it lets callers skip hashing conditionally (for example, when a secret is not yet configured) without adding an extra `if` at the call site. If you need an unconditional SHA256 hash without a secret, use MD5 or the full [Crypto helper](/extensions/helpers/crypto/).
45
+
46
+ ### Examples
47
+
48
+ **MD5 digest (hex)**
49
+
50
+ ```typescript
51
+ import { hash } from '@venizia/ignis-helpers';
52
+
53
+ const digest = hash('user@example.com', { algorithm: 'MD5', outputType: 'hex' });
54
+ // => 'b58996c504c5638798eb6b511e6f49af'
55
+ ```
56
+
57
+ **MD5 digest (base64) - useful for HTTP ETags**
58
+
59
+ ```typescript
60
+ const etag = hash(JSON.stringify(payload), { algorithm: 'MD5', outputType: 'base64' });
61
+ // => 'tYlsUExWOHeY62a1EW9Jr...'
62
+ ```
63
+
64
+ **HMAC-SHA256 - signing a webhook payload**
65
+
66
+ ```typescript
67
+ import { hash } from '@venizia/ignis-helpers';
68
+
69
+ const signature = hash(rawBody, {
70
+ algorithm: 'SHA256',
71
+ secret: process.env.WEBHOOK_SECRET,
72
+ outputType: 'hex',
73
+ });
74
+
75
+ // Compare against the value in the X-Hub-Signature-256 header
76
+ const expected = `sha256=${signature}`;
77
+ ```
78
+
79
+ **HMAC-SHA256 in base64 - API request signing**
80
+
81
+ ```typescript
82
+ const hmac = hash(`${timestamp}.${body}`, {
83
+ algorithm: 'SHA256',
84
+ secret: apiKey,
85
+ outputType: 'base64',
86
+ });
87
+ ```
88
+
89
+ ## When to use
90
+
91
+ Use `hash` for lightweight, one-off hashing that does not require key management or IV handling:
92
+
93
+ - Building cache keys or ETags from response bodies (MD5)
94
+ - Verifying webhook signatures (HMAC-SHA256)
95
+ - Signing API requests with a shared secret (HMAC-SHA256)
96
+ - Anonymising personally identifiable data before storing in logs (MD5)
97
+
98
+ For encryption, decryption, or asymmetric operations (AES, RSA, ECDH) use the [Crypto helper](/extensions/helpers/crypto/) instead.
@@ -1,6 +1,6 @@
1
1
  # Utilities
2
2
 
3
- Pure, standalone functions providing common, reusable logic for the Ignis framework. All utilities are stateless and easy to use.
3
+ Pure, standalone functions providing common, reusable logic for the IGNIS framework. All utilities are stateless and easy to use.
4
4
 
5
5
  ## Quick Reference
6
6
 
@@ -10,7 +10,7 @@ Pure, standalone functions providing common, reusable logic for the Ignis framew
10
10
  | **Date** | `ignis-helpers` | Date/time manipulation | `dayjs`, `sleep()`, `isWeekday()`, `getDateTz()`, `hrTime()` |
11
11
  | **JSX** | `ignis` | HTML/JSX responses | `htmlContent()`, `htmlResponse()` |
12
12
  | **Module** | `ignis-helpers` | Module validation | `validateModule()` |
13
- | **Parse** | `ignis-helpers` | Data type conversion | `int()`, `float()`, `toBoolean()`, `toCamel()`, `getNumberValue()` |
13
+ | **Parse** | `ignis-helpers` | Data type conversion | `int()`, `float()`, `toBoolean()`, `toCamel()` |
14
14
  | **Performance** | `ignis-helpers` | Execution timing | `executeWithPerformanceMeasure()`, `getPerformanceCheckpoint()` |
15
15
  | **Promise** | `ignis-helpers` | Promise helpers | `executePromiseWithLimit()`, `isPromiseLike()`, `getDeepProperty()` |
16
16
  | **Request** | `ignis-helpers` | HTTP utilities | `parseMultipartBody()`, `sanitizeFilename()`, `createContentDispositionHeader()` |
@@ -47,7 +47,7 @@ Utilities are imported from `@venizia/ignis` (schema, JSX, and status helpers) o
47
47
 
48
48
  ```typescript
49
49
  import { jsonContent, jsonResponse, htmlResponse, requiredString, Statuses } from '@venizia/ignis';
50
- import { hash, dayjs, sleep, int, float, toBoolean, getNumberValue } from '@venizia/ignis-helpers';
50
+ import { hash, dayjs, sleep, int, float, toBoolean } from '@venizia/ignis-helpers';
51
51
 
52
52
  // Crypto
53
53
  const md5Hash = hash('some text', { algorithm: 'MD5', outputType: 'hex' });
@@ -186,7 +186,8 @@ export class PageController extends BaseRestController {
186
186
  ### HTML Email Preview
187
187
 
188
188
  ```typescript
189
- import { BaseRestController, get, htmlResponse, TRouteContext, z } from '@venizia/ignis';
189
+ import { BaseRestController, get, htmlResponse, TRouteContext } from '@venizia/ignis';
190
+ import { z } from '@hono/zod-openapi';
190
191
  import { HTTP } from '@venizia/ignis-helpers';
191
192
 
192
193
  const EmailRoutes = {
@@ -225,7 +226,8 @@ export class EmailController extends BaseRestController {
225
226
  ### Documentation Page
226
227
 
227
228
  ```typescript
228
- import { BaseRestController, get, htmlResponse, TRouteContext, z } from '@venizia/ignis';
229
+ import { BaseRestController, get, htmlResponse, TRouteContext } from '@venizia/ignis';
230
+ import { z } from '@hono/zod-openapi';
229
231
  import { HTTP } from '@venizia/ignis-helpers';
230
232
 
231
233
  const DocsRoutes = {
@@ -355,7 +357,7 @@ async getUserProfile(c: TRouteContext) {
355
357
  return c.html(<UserProfile user={user} />);
356
358
  }
357
359
 
358
- // Bad: Don't use htmlResponse for API endpoints use jsonResponse instead
360
+ // Bad: Don't use htmlResponse for API endpoints - use jsonResponse instead
359
361
  ```
360
362
 
361
363
  ### 2. Combine with Authentication
@@ -564,7 +566,7 @@ async getUsers() {
564
566
  - **Related References:**
565
567
  - [Schema Utility](./schema.md) - JSON content and response helpers
566
568
  - [Controllers](../base/controllers.md) - Defining routes and handlers
567
- - [OpenAPI Component](/extensions/components/swagger) - API documentation
569
+ - [OpenAPI Component](/extensions/components/api-reference) - API documentation
568
570
 
569
571
  - **External Resources:**
570
572
  - [Hono JSX Documentation](https://hono.dev/guides/jsx)
@@ -0,0 +1,90 @@
1
+ # Module Utility
2
+
3
+ The Module utility provides a pre-flight check for optional peer dependencies. IGNIS helpers and components often depend on packages that are not bundled with the framework (for example, `@connectrpc/connect` for gRPC, `@hono/swagger-ui` for the Swagger component). Calling `validateModule` at the start of a lazy-loaded code path ensures a clear, actionable error is thrown before any import is attempted, rather than a cryptic "Cannot find module" crash.
4
+
5
+ Resolution is rooted at `process.cwd()/node_modules` via Node's `createRequire`, so peer dependencies installed in the consuming application are found correctly even though this utility lives inside `packages/helpers/dist/`.
6
+
7
+ ## `validateModule`
8
+
9
+ Resolves each module name in sequence using `require.resolve`. If any module cannot be found it logs the failure and throws an `ApplicationError` with an install instruction. Returns a `Promise<void>` - it is async to support consistent `await` usage at call sites, though resolution itself is synchronous.
10
+
11
+ ### Signature
12
+
13
+ ```typescript
14
+ validateModule(opts: {
15
+ scope?: string;
16
+ modules: Array<string>;
17
+ }): Promise<void>
18
+ ```
19
+
20
+ **Options**
21
+
22
+ | Option | Type | Default | Description |
23
+ |--------|------|---------|-------------|
24
+ | `modules` | `Array<string>` | `[]` | Module names to check. Evaluated in order - the first missing module stops the loop and throws. |
25
+ | `scope` | `string` | `''` | Human-readable label for the calling feature (e.g. the component or helper class name). Included in the error message to tell the developer which feature needs the package. |
26
+
27
+ ### Error message format
28
+
29
+ When a module is missing the thrown error reads:
30
+
31
+ ```
32
+ [validateModule] <module> is required for <scope>. Please install '<module>'
33
+ ```
34
+
35
+ If `scope` is omitted:
36
+
37
+ ```
38
+ [validateModule] <module> is required. Please install '<module>'
39
+ ```
40
+
41
+ ### Example - gRPC controller (optional dep guard)
42
+
43
+ The `GrpcRequestAdapter` uses `validateModule` to gate the ConnectRPC import. The check runs once before the adapter is wired up, so the error surfaces at startup rather than on the first request.
44
+
45
+ ```typescript
46
+ import { validateModule } from '@venizia/ignis-helpers';
47
+
48
+ const GRPC_MODULES = ['@connectrpc/connect'];
49
+
50
+ export class MyGrpcController extends BaseGrpcController {
51
+ async configure() {
52
+ // Fails fast with a clear install instruction if the peer dep is absent
53
+ await validateModule({ scope: MyGrpcController.name, modules: GRPC_MODULES });
54
+
55
+ const { ConnectRouter } = await import('@connectrpc/connect');
56
+ // ... register routes
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### Example - custom helper with multiple optional deps
62
+
63
+ When a feature requires several packages, list them all. The first missing one stops the check.
64
+
65
+ ```typescript
66
+ import { validateModule } from '@venizia/ignis-helpers';
67
+ import { BaseHelper } from '@venizia/ignis-helpers';
68
+
69
+ export class KafkaQueueHelper extends BaseHelper {
70
+ async configure() {
71
+ await validateModule({
72
+ scope: KafkaQueueHelper.name,
73
+ modules: ['kafkajs', 'kafkajs-snappy'],
74
+ });
75
+
76
+ const { Kafka } = await import('kafkajs');
77
+ // ... initialise Kafka client
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## When to use
83
+
84
+ Use `validateModule` whenever your code does a dynamic `import()` of a package that is listed as an optional peer dependency in `package.json`. The recommended pattern is:
85
+
86
+ 1. Declare the dep as `peerDependenciesMeta` with `optional: true` in `package.json`.
87
+ 2. Call `validateModule` at the top of the method that needs the package - before any `import()`.
88
+ 3. Pass the feature or class name as `scope` so the error message pinpoints which feature triggered the check.
89
+
90
+ Avoid calling `validateModule` on every request. Place it in an initialisation hook (`configure`, `binding`, `boot`) that runs once at startup.
@@ -12,16 +12,13 @@ The Parse utility provides a collection of functions for data type checking, con
12
12
  - **`int(input)`**: Parses a value to an integer. Handles strings with commas and defaults to `0` if the input is invalid.
13
13
  - **`float(input, digit = 2)`**: Parses a value to a float, rounding to a specified number of digits. Handles strings with commas and defaults to `0` if the input is invalid.
14
14
  - **`toBoolean(input)`**: Converts a value to a boolean. Returns `false` for empty string, `'false'`, `'0'`, `false`, `0`, `null`, and `undefined`. Returns `true` for everything else.
15
- - **`toStringDecimal(input, digit = 2, options?)`**: Formats a number to a string with a specified number of decimal places. By default uses locale-specific formatting (`useLocaleFormat: true`). When `useLocaleFormat` is `false`, uses `toFixed()` instead.
16
15
 
17
16
  ```typescript
18
- import { int, float, toBoolean, toStringDecimal } from '@venizia/ignis-helpers';
17
+ import { int, float, toBoolean } from '@venizia/ignis-helpers';
19
18
 
20
19
  const myInt = int('1,000'); // => 1000
21
20
  const myFloat = float('1,234.567', 2); // => 1234.57
22
21
  const myBool = toBoolean('true'); // => true
23
- const formatted = toStringDecimal(1234.5, 2); // => '1,234.50'
24
- const fixed = toStringDecimal(1234.5, 2, { useLocaleFormat: false }); // => '1234.50'
25
22
  ```
26
23
 
27
24
  ## String and Object Transformation
@@ -41,17 +38,12 @@ const camelObject = keysToCamel({ 'first-name': 'John', 'last_name': 'Doe' });
41
38
 
42
39
  ## Number Parsing
43
40
 
44
- - **`getNumberValue(input, opts?)`**: Parses a string to a number with locale support (US or EU format).
45
41
 
46
42
  ```typescript
47
- import { getNumberValue } from '@venizia/ignis-helpers';
48
43
 
49
- // US format (default) comma is thousands separator
50
- getNumberValue('1,234.56', { method: 'float' }); // => 1234.56
51
- getNumberValue('1,234', { method: 'int' }); // => 1234
44
+ // US format (default) - comma is thousands separator
52
45
 
53
- // EU format dot is thousands separator, comma is decimal
54
- getNumberValue('1.234,56', { method: 'float', locale: 'eu' }); // => 1234.56
46
+ // EU format - dot is thousands separator, comma is decimal
55
47
  ```
56
48
 
57
49
  | Option | Type | Default | Description |
@@ -61,11 +53,10 @@ getNumberValue('1.234,56', { method: 'float', locale: 'eu' }); // => 1234.56
61
53
 
62
54
  ## Array Transformation
63
55
 
64
- - **`parseArrayToRecordWithKey(opts)`**: Transforms an array of objects into a record (plain object), using a specified property of the objects as keys. Takes an options object with `arr` and `keyMap` properties. Throws an error if `keyMap` is not found in an element. Last element wins on duplicate keys.
65
56
  - **`parseArrayToMapWithKey(arr, keyMap)`**: Transforms an array of objects into a `Map`, using a specified property of the objects as keys. Takes positional arguments (not an options object). Throws an error if `keyMap` is not found in an element. Last element wins on duplicate keys.
66
57
 
67
58
  ```typescript
68
- import { parseArrayToRecordWithKey, parseArrayToMapWithKey } from '@venizia/ignis-helpers';
59
+ import { parseArrayToMapWithKey } from '@venizia/ignis-helpers';
69
60
 
70
61
  const users = [
71
62
  { id: 1, name: 'Alice' },
@@ -73,7 +64,6 @@ const users = [
73
64
  ];
74
65
 
75
66
  // Record (options object pattern)
76
- const usersRecord = parseArrayToRecordWithKey({ arr: users, keyMap: 'id' });
77
67
  // => { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }
78
68
 
79
69
  // Map (positional arguments)
@@ -38,17 +38,19 @@ const results = await executePromiseWithLimit({
38
38
  console.log('All tasks finished:', results);
39
39
  ```
40
40
 
41
- ## `transformValueOrPromise`
41
+ ## `toError`
42
42
 
43
- This async function applies a transformation function to a value that might be a direct value or a Promise. It always returns a Promise.
43
+ Normalizes an unknown thrown value into an `Error` instance. Useful in `catch` blocks where the caught value is typed `unknown` and isn't guaranteed to already be an `Error`.
44
44
 
45
45
  ```typescript
46
- import { transformValueOrPromise } from '@venizia/ignis-helpers';
46
+ import { toError } from '@venizia/ignis-helpers';
47
47
 
48
- const double = (n: number) => n * 2;
49
-
50
- const result1 = await transformValueOrPromise(5, double); // => 10
51
- const result2 = await transformValueOrPromise(Promise.resolve(5), double); // => 10
48
+ try {
49
+ await riskyOperation();
50
+ } catch (caught) {
51
+ const error = toError(caught);
52
+ console.error(error.message);
53
+ }
52
54
  ```
53
55
 
54
56
  ## `isPromiseLike`