@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
@@ -1,6 +1,6 @@
1
1
  # Building a Real-Time Chat Application
2
2
 
3
- This tutorial shows you how to build a real-time chat application with rooms, direct messages, typing indicators, and presence using Socket.IO powered by the `SocketIOComponent` and `SocketIOServerHelper`.
3
+ This tutorial shows you how to build a real-time chat application with rooms, direct messages, typing indicators, and presence using Socket.IO - powered by the `SocketIOComponent` and `SocketIOServerHelper`.
4
4
 
5
5
  **What You'll Build:**
6
6
 
@@ -29,7 +29,7 @@ bun add hono @hono/zod-openapi @venizia/ignis @venizia/ignis-helpers
29
29
  bun add drizzle-orm drizzle-zod pg
30
30
  bun add -d typescript @types/bun @venizia/dev-configs drizzle-kit @types/pg
31
31
 
32
- # For Bun runtime Socket.IO engine
32
+ # For Bun runtime - Socket.IO engine
33
33
  bun add @socket.io/bun-engine
34
34
  ```
35
35
 
@@ -46,10 +46,10 @@ Models in IGNIS combine Drizzle ORM schemas with Entity classes.
46
46
  // src/models/user.model.ts
47
47
  import {
48
48
  BaseEntity,
49
- createRelations,
50
49
  generateIdColumnDefs,
51
50
  generateTzColumnDefs,
52
51
  model,
52
+ TRelationConfig,
53
53
  TTableObject,
54
54
  } from '@venizia/ignis';
55
55
  import { pgTable, varchar, text, timestamp, boolean } from 'drizzle-orm/pg-core';
@@ -64,18 +64,13 @@ export const userTable = pgTable('User', {
64
64
  lastSeenAt: timestamp('last_seen_at'),
65
65
  });
66
66
 
67
- export const userRelations = createRelations({
68
- source: userTable,
69
- relations: [],
70
- });
71
-
72
67
  export type TUserSchema = typeof userTable;
73
68
  export type TUser = TTableObject<TUserSchema>;
74
69
 
75
70
  @model({ type: 'entity' })
76
71
  export class User extends BaseEntity<typeof User.schema> {
77
72
  static override schema = userTable;
78
- static override relations = () => userRelations.definitions;
73
+ static override relations = (): TRelationConfig[] => [];
79
74
  static override TABLE_NAME = 'User';
80
75
  }
81
76
  ```
@@ -86,10 +81,11 @@ export class User extends BaseEntity<typeof User.schema> {
86
81
  // src/models/room.model.ts
87
82
  import {
88
83
  BaseEntity,
89
- createRelations,
90
84
  generateIdColumnDefs,
91
85
  generateTzColumnDefs,
92
86
  model,
87
+ RelationTypes,
88
+ TRelationConfig,
93
89
  TTableObject,
94
90
  } from '@venizia/ignis';
95
91
  import { pgTable, varchar, text, timestamp, boolean } from 'drizzle-orm/pg-core';
@@ -113,22 +109,6 @@ export const roomMemberTable = pgTable('RoomMember', {
113
109
  lastReadAt: timestamp('last_read_at'),
114
110
  });
115
111
 
116
- export const roomRelations = createRelations({
117
- source: roomTable,
118
- relations: [
119
- { type: 'one', name: 'creator', target: () => userTable, fields: ['createdBy'], references: ['id'] },
120
- { type: 'many', name: 'members', target: () => roomMemberTable, fields: ['id'], references: ['roomId'] },
121
- ],
122
- });
123
-
124
- export const roomMemberRelations = createRelations({
125
- source: roomMemberTable,
126
- relations: [
127
- { type: 'one', name: 'room', target: () => roomTable, fields: ['roomId'], references: ['id'] },
128
- { type: 'one', name: 'user', target: () => userTable, fields: ['userId'], references: ['id'] },
129
- ],
130
- });
131
-
132
112
  export type TRoomSchema = typeof roomTable;
133
113
  export type TRoom = TTableObject<TRoomSchema>;
134
114
  export type TRoomMemberSchema = typeof roomMemberTable;
@@ -137,14 +117,44 @@ export type TRoomMember = TTableObject<TRoomMemberSchema>;
137
117
  @model({ type: 'entity' })
138
118
  export class Room extends BaseEntity<typeof Room.schema> {
139
119
  static override schema = roomTable;
140
- static override relations = () => roomRelations.definitions;
120
+
121
+ static override relations = (): TRelationConfig[] => [
122
+ {
123
+ name: 'creator',
124
+ type: RelationTypes.ONE,
125
+ schema: userTable,
126
+ metadata: { fields: [roomTable.createdBy], references: [userTable.id] },
127
+ },
128
+ {
129
+ name: 'members',
130
+ type: RelationTypes.MANY,
131
+ schema: roomMemberTable,
132
+ metadata: { fields: [roomTable.id], references: [roomMemberTable.roomId] },
133
+ },
134
+ ];
135
+
141
136
  static override TABLE_NAME = 'Room';
142
137
  }
143
138
 
144
139
  @model({ type: 'entity' })
145
140
  export class RoomMember extends BaseEntity<typeof RoomMember.schema> {
146
141
  static override schema = roomMemberTable;
147
- static override relations = () => roomMemberRelations.definitions;
142
+
143
+ static override relations = (): TRelationConfig[] => [
144
+ {
145
+ name: 'room',
146
+ type: RelationTypes.ONE,
147
+ schema: roomTable,
148
+ metadata: { fields: [roomMemberTable.roomId], references: [roomTable.id] },
149
+ },
150
+ {
151
+ name: 'user',
152
+ type: RelationTypes.ONE,
153
+ schema: userTable,
154
+ metadata: { fields: [roomMemberTable.userId], references: [userTable.id] },
155
+ },
156
+ ];
157
+
148
158
  static override TABLE_NAME = 'RoomMember';
149
159
  }
150
160
  ```
@@ -155,10 +165,11 @@ export class RoomMember extends BaseEntity<typeof RoomMember.schema> {
155
165
  // src/models/message.model.ts
156
166
  import {
157
167
  BaseEntity,
158
- createRelations,
159
168
  generateIdColumnDefs,
160
169
  generateTzColumnDefs,
161
170
  model,
171
+ RelationTypes,
172
+ TRelationConfig,
162
173
  TTableObject,
163
174
  } from '@venizia/ignis';
164
175
  import { pgTable, text, timestamp, varchar } from 'drizzle-orm/pg-core';
@@ -189,22 +200,6 @@ export const directMessageTable = pgTable('DirectMessage', {
189
200
  deletedAt: timestamp('deleted_at'),
190
201
  });
191
202
 
192
- export const messageRelations = createRelations({
193
- source: messageTable,
194
- relations: [
195
- { type: 'one', name: 'room', target: () => roomTable, fields: ['roomId'], references: ['id'] },
196
- { type: 'one', name: 'sender', target: () => userTable, fields: ['senderId'], references: ['id'] },
197
- ],
198
- });
199
-
200
- export const directMessageRelations = createRelations({
201
- source: directMessageTable,
202
- relations: [
203
- { type: 'one', name: 'sender', target: () => userTable, fields: ['senderId'], references: ['id'] },
204
- { type: 'one', name: 'receiver', target: () => userTable, fields: ['receiverId'], references: ['id'] },
205
- ],
206
- });
207
-
208
203
  export type TMessageSchema = typeof messageTable;
209
204
  export type TMessage = TTableObject<TMessageSchema>;
210
205
  export type TDirectMessageSchema = typeof directMessageTable;
@@ -213,14 +208,44 @@ export type TDirectMessage = TTableObject<TDirectMessageSchema>;
213
208
  @model({ type: 'entity' })
214
209
  export class Message extends BaseEntity<typeof Message.schema> {
215
210
  static override schema = messageTable;
216
- static override relations = () => messageRelations.definitions;
211
+
212
+ static override relations = (): TRelationConfig[] => [
213
+ {
214
+ name: 'room',
215
+ type: RelationTypes.ONE,
216
+ schema: roomTable,
217
+ metadata: { fields: [messageTable.roomId], references: [roomTable.id] },
218
+ },
219
+ {
220
+ name: 'sender',
221
+ type: RelationTypes.ONE,
222
+ schema: userTable,
223
+ metadata: { fields: [messageTable.senderId], references: [userTable.id] },
224
+ },
225
+ ];
226
+
217
227
  static override TABLE_NAME = 'Message';
218
228
  }
219
229
 
220
230
  @model({ type: 'entity' })
221
231
  export class DirectMessage extends BaseEntity<typeof DirectMessage.schema> {
222
232
  static override schema = directMessageTable;
223
- static override relations = () => directMessageRelations.definitions;
233
+
234
+ static override relations = (): TRelationConfig[] => [
235
+ {
236
+ name: 'sender',
237
+ type: RelationTypes.ONE,
238
+ schema: userTable,
239
+ metadata: { fields: [directMessageTable.senderId], references: [userTable.id] },
240
+ },
241
+ {
242
+ name: 'receiver',
243
+ type: RelationTypes.ONE,
244
+ schema: userTable,
245
+ metadata: { fields: [directMessageTable.receiverId], references: [userTable.id] },
246
+ },
247
+ ];
248
+
224
249
  static override TABLE_NAME = 'DirectMessage';
225
250
  }
226
251
  ```
@@ -243,7 +268,7 @@ import {
243
268
  datasource,
244
269
  ValueOrPromise,
245
270
  } from '@venizia/ignis';
246
- import { drizzle } from 'drizzle-orm/node-postgres';
271
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
247
272
  import { Pool } from 'pg';
248
273
 
249
274
  interface IDSConfigs {
@@ -254,7 +279,7 @@ interface IDSConfigs {
254
279
  password: string;
255
280
  }
256
281
 
257
- @datasource({ driver: 'node-postgres' })
282
+ @datasource({ driver: NodePostgresDriver })
258
283
  export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
259
284
  constructor() {
260
285
  super({
@@ -270,16 +295,18 @@ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
270
295
  }
271
296
 
272
297
  override configure(): ValueOrPromise<void> {
273
- const schema = this.getSchema();
298
+ const schema = Object.keys(this.getSchema());
274
299
 
275
300
  this.logger.debug(
276
301
  '[configure] Auto-discovered schema | Schema + Relations (%s): %o',
277
- Object.keys(schema).length,
278
- Object.keys(schema),
302
+ schema.length,
303
+ schema,
279
304
  );
280
305
 
281
- const client = new Pool(this.settings);
282
- this.connector = drizzle({ client, schema });
306
+ // The client must land on this.client - a local would leave beginTransaction() with nothing
307
+ // to resolve a driver from, and it would throw `No driver and no client`. NodePostgresDriver
308
+ // named in @datasource above is what wires the driver and Drizzle connector from it.
309
+ this.client = new Pool(this.settings);
283
310
  }
284
311
  }
285
312
  ```
@@ -325,25 +352,27 @@ export class RoomRepository extends DefaultCRUDRepository<typeof Room.schema> {
325
352
 
326
353
  async findByUser(opts: { userId: string }) {
327
354
  const memberships = await this._memberRepo.find({
328
- where: { userId: opts.userId },
329
- include: { room: true },
355
+ filter: { where: { userId: opts.userId }, include: [{ relation: 'room' }] },
330
356
  });
331
357
  return memberships.map(m => m.room);
332
358
  }
333
359
 
334
360
  async isMember(opts: { roomId: string; userId: string }): Promise<boolean> {
335
361
  const member = await this._memberRepo.findOne({
336
- where: { roomId: opts.roomId, userId: opts.userId },
362
+ filter: { where: { roomId: opts.roomId, userId: opts.userId } },
337
363
  });
338
364
  return !!member;
339
365
  }
340
366
 
341
367
  async addMember(opts: { roomId: string; userId: string; role?: string }) {
342
- return this._memberRepo.create({
343
- roomId: opts.roomId,
344
- userId: opts.userId,
345
- role: opts.role ?? 'member',
368
+ const { data } = await this._memberRepo.create({
369
+ data: {
370
+ roomId: opts.roomId,
371
+ userId: opts.userId,
372
+ role: opts.role ?? 'member',
373
+ },
346
374
  });
375
+ return data;
347
376
  }
348
377
 
349
378
  async removeMember(opts: { roomId: string; userId: string }) {
@@ -354,14 +383,13 @@ export class RoomRepository extends DefaultCRUDRepository<typeof Room.schema> {
354
383
 
355
384
  async getMember(opts: { roomId: string; userId: string }) {
356
385
  return this._memberRepo.findOne({
357
- where: { roomId: opts.roomId, userId: opts.userId },
386
+ filter: { where: { roomId: opts.roomId, userId: opts.userId } },
358
387
  });
359
388
  }
360
389
 
361
390
  async getMembers(opts: { roomId: string }) {
362
391
  return this._memberRepo.find({
363
- where: { roomId: opts.roomId },
364
- include: { user: true },
392
+ filter: { where: { roomId: opts.roomId }, include: [{ relation: 'user' }] },
365
393
  });
366
394
  }
367
395
  }
@@ -393,7 +421,8 @@ export class MessageRepository extends DefaultCRUDRepository<typeof Message.sche
393
421
  }
394
422
 
395
423
  async createDirectMessage(opts: { senderId: string; receiverId: string; content: string }) {
396
- return this._dmRepo.create(opts);
424
+ const { data } = await this._dmRepo.create({ data: opts });
425
+ return data;
397
426
  }
398
427
 
399
428
  async findDirectMessages(opts: {
@@ -403,26 +432,26 @@ export class MessageRepository extends DefaultCRUDRepository<typeof Message.sche
403
432
  before?: string;
404
433
  }) {
405
434
  return this._dmRepo.find({
406
- where: {
407
- or: [
408
- { senderId: opts.userId1, receiverId: opts.userId2 },
409
- { senderId: opts.userId2, receiverId: opts.userId1 },
410
- ],
435
+ filter: {
436
+ where: {
437
+ or: [
438
+ { senderId: opts.userId1, receiverId: opts.userId2 },
439
+ { senderId: opts.userId2, receiverId: opts.userId1 },
440
+ ],
441
+ },
442
+ order: ['createdAt DESC'],
443
+ limit: opts.limit ?? 50,
411
444
  },
412
- orderBy: { createdAt: 'desc' },
413
- limit: opts.limit ?? 50,
414
445
  });
415
446
  }
416
447
 
417
448
  async findConversations(opts: { userId: string }) {
418
449
  // Get unique conversation partners
419
450
  const sent = await this._dmRepo.find({
420
- where: { senderId: opts.userId },
421
- include: { receiver: true },
451
+ filter: { where: { senderId: opts.userId }, include: [{ relation: 'receiver' }] },
422
452
  });
423
453
  const received = await this._dmRepo.find({
424
- where: { receiverId: opts.userId },
425
- include: { sender: true },
454
+ filter: { where: { receiverId: opts.userId }, include: [{ relation: 'sender' }] },
426
455
  });
427
456
 
428
457
  // Combine and deduplicate
@@ -450,7 +479,8 @@ import {
450
479
  BindingKeys,
451
480
  BindingNamespaces,
452
481
  } from '@venizia/ignis';
453
- import { ISocketIOClient, getError, SocketIOServerHelper } from '@venizia/ignis-helpers';
482
+ import { getError } from '@venizia/ignis-helpers';
483
+ import { ISocketIOClient, SocketIOServerHelper } from '@venizia/ignis-helpers/socket-io';
454
484
  import { Socket } from 'socket.io';
455
485
  import { MessageRepository } from '../repositories/message.repository';
456
486
  import { RoomRepository } from '../repositories/room.repository';
@@ -492,7 +522,7 @@ export class ChatService extends BaseService {
492
522
  }
493
523
 
494
524
  // ---------------------------------------------------------------------------
495
- // SocketIOServerHelper lazy getter (bound after server starts via post-start hook)
525
+ // SocketIOServerHelper - lazy getter (bound after server starts via post-start hook)
496
526
  // ---------------------------------------------------------------------------
497
527
  private get socketIOHelper(): SocketIOServerHelper {
498
528
  if (!this._socketIOHelper) {
@@ -511,7 +541,7 @@ export class ChatService extends BaseService {
511
541
  }
512
542
 
513
543
  // ---------------------------------------------------------------------------
514
- // Socket event handlers called from clientConnectedFn after authentication
544
+ // Socket event handlers - called from clientConnectedFn after authentication
515
545
  // ---------------------------------------------------------------------------
516
546
  registerClientHandlers(opts: { socket: Socket }) {
517
547
  const logger = this.logger.for(this.registerClientHandlers.name);
@@ -699,7 +729,7 @@ export class ChatService extends BaseService {
699
729
  // Room operations
700
730
  // ---------------------------------------------------------------------------
701
731
  async createRoom(opts: { name: string; description?: string; isPrivate?: boolean; createdBy: string }) {
702
- const room = await this._roomRepo.create(opts);
732
+ const { data: room } = await this._roomRepo.create({ data: opts });
703
733
 
704
734
  // Add creator as admin
705
735
  await this._roomRepo.addMember({ roomId: room.id, userId: opts.createdBy, role: 'admin' });
@@ -708,7 +738,7 @@ export class ChatService extends BaseService {
708
738
  }
709
739
 
710
740
  async joinRoom(opts: { roomId: string; userId: string }) {
711
- const room = await this._roomRepo.findById(opts.roomId);
741
+ const room = await this._roomRepo.findById({ id: opts.roomId });
712
742
  if (!room) {
713
743
  throw getError({ statusCode: 404, message: 'Room not found' });
714
744
  }
@@ -742,14 +772,16 @@ export class ChatService extends BaseService {
742
772
  throw getError({ statusCode: 403, message: 'Not a member of this room' });
743
773
  }
744
774
 
745
- const message = await this._messageRepo.create({
746
- roomId: opts.roomId,
747
- senderId: opts.senderId,
748
- content: opts.content,
749
- type: opts.type ?? 'text',
775
+ const { data: message } = await this._messageRepo.create({
776
+ data: {
777
+ roomId: opts.roomId,
778
+ senderId: opts.senderId,
779
+ content: opts.content,
780
+ type: opts.type ?? 'text',
781
+ },
750
782
  });
751
783
 
752
- const sender = await this._userRepo.findById(opts.senderId);
784
+ const sender = await this._userRepo.findById({ id: opts.senderId });
753
785
 
754
786
  return {
755
787
  ...message,
@@ -763,7 +795,7 @@ export class ChatService extends BaseService {
763
795
  }
764
796
 
765
797
  async editMessage(opts: { messageId: string; userId: string; content: string }) {
766
- const message = await this._messageRepo.findById(opts.messageId);
798
+ const message = await this._messageRepo.findById({ id: opts.messageId });
767
799
 
768
800
  if (!message) {
769
801
  throw getError({ statusCode: 404, message: 'Message not found' });
@@ -773,14 +805,17 @@ export class ChatService extends BaseService {
773
805
  throw getError({ statusCode: 403, message: 'Cannot edit others messages' });
774
806
  }
775
807
 
776
- return this._messageRepo.updateById(opts.messageId, {
777
- content: opts.content,
778
- editedAt: new Date(),
808
+ return this._messageRepo.updateById({
809
+ id: opts.messageId,
810
+ data: {
811
+ content: opts.content,
812
+ editedAt: new Date(),
813
+ },
779
814
  });
780
815
  }
781
816
 
782
817
  async deleteMessage(opts: { messageId: string; userId: string }) {
783
- const message = await this._messageRepo.findById(opts.messageId);
818
+ const message = await this._messageRepo.findById({ id: opts.messageId });
784
819
 
785
820
  if (!message) {
786
821
  throw getError({ statusCode: 404, message: 'Message not found' });
@@ -793,8 +828,11 @@ export class ChatService extends BaseService {
793
828
  }
794
829
  }
795
830
 
796
- return this._messageRepo.updateById(opts.messageId, {
797
- deletedAt: new Date(),
831
+ return this._messageRepo.updateById({
832
+ id: opts.messageId,
833
+ data: {
834
+ deletedAt: new Date(),
835
+ },
798
836
  });
799
837
  }
800
838
 
@@ -805,16 +843,18 @@ export class ChatService extends BaseService {
805
843
  };
806
844
 
807
845
  if (opts.before) {
808
- const beforeMessage = await this._messageRepo.findById(opts.before);
846
+ const beforeMessage = await this._messageRepo.findById({ id: opts.before });
809
847
  if (beforeMessage) {
810
848
  where.createdAt = { lt: beforeMessage.createdAt };
811
849
  }
812
850
  }
813
851
 
814
852
  return this._messageRepo.find({
815
- where,
816
- orderBy: { createdAt: 'desc' },
817
- limit: opts.limit ?? 50,
853
+ filter: {
854
+ where,
855
+ order: ['createdAt DESC'],
856
+ limit: opts.limit ?? 50,
857
+ },
818
858
  });
819
859
  }
820
860
 
@@ -842,16 +882,22 @@ export class ChatService extends BaseService {
842
882
  // Presence operations
843
883
  // ---------------------------------------------------------------------------
844
884
  async setOnline(opts: { userId: string }) {
845
- await this._userRepo.updateById(opts.userId, {
846
- isOnline: true,
847
- lastSeenAt: new Date(),
885
+ await this._userRepo.updateById({
886
+ id: opts.userId,
887
+ data: {
888
+ isOnline: true,
889
+ lastSeenAt: new Date(),
890
+ },
848
891
  });
849
892
  }
850
893
 
851
894
  async setOffline(opts: { userId: string }) {
852
- await this._userRepo.updateById(opts.userId, {
853
- isOnline: false,
854
- lastSeenAt: new Date(),
895
+ await this._userRepo.updateById({
896
+ id: opts.userId,
897
+ data: {
898
+ isOnline: false,
899
+ lastSeenAt: new Date(),
900
+ },
855
901
  });
856
902
  }
857
903
 
@@ -948,7 +994,7 @@ import {
948
994
  import {
949
995
  applicationEnvironment,
950
996
  type ISocketIOServerBaseOptions,
951
- RedisHelper,
997
+ RedisSingleHelper,
952
998
  SocketIOServerHelper,
953
999
  } from '@venizia/ignis-helpers';
954
1000
  import { ChatService } from './services/chat.service';
@@ -958,7 +1004,7 @@ import { RoomRepository, RoomMemberRepository } from './repositories/room.reposi
958
1004
  import { MessageRepository, DirectMessageRepository } from './repositories/message.repository';
959
1005
 
960
1006
  export class ChatApp extends BaseApplication {
961
- private redisHelper: RedisHelper;
1007
+ private redisHelper: RedisSingleHelper;
962
1008
 
963
1009
  getAppInfo(): IApplicationInfo {
964
1010
  return { name: 'chat-api', version: '1.0.0' };
@@ -990,9 +1036,9 @@ export class ChatApp extends BaseApplication {
990
1036
 
991
1037
  // ---------------------------------------------------------------------------
992
1038
  private setupSocketIO() {
993
- // 1. Redis connection SocketIOServerHelper creates 3 duplicate connections
1039
+ // 1. Redis connection - SocketIOServerHelper creates 3 duplicate connections
994
1040
  // for adapter (pub/sub) and emitter automatically
995
- this.redisHelper = new RedisHelper({
1041
+ this.redisHelper = new RedisSingleHelper({
996
1042
  name: 'chat-redis',
997
1043
  host: process.env.APP_ENV_REDIS_HOST ?? 'localhost',
998
1044
  port: +(process.env.APP_ENV_REDIS_PORT ?? 6379),
@@ -1000,11 +1046,11 @@ export class ChatApp extends BaseApplication {
1000
1046
  autoConnect: false,
1001
1047
  });
1002
1048
 
1003
- this.bind<RedisHelper>({
1049
+ this.bind<RedisSingleHelper>({
1004
1050
  key: SocketIOBindingKeys.REDIS_CONNECTION,
1005
1051
  }).toValue(this.redisHelper);
1006
1052
 
1007
- // 2. Authentication handler called when a client emits 'authenticate'
1053
+ // 2. Authentication handler - called when a client emits 'authenticate'
1008
1054
  // Receives the Socket.IO handshake (headers, query, auth object)
1009
1055
  const authenticateFn: ISocketIOServerBaseOptions['authenticateFn'] = handshake => {
1010
1056
  const token =
@@ -1024,7 +1070,7 @@ export class ChatApp extends BaseApplication {
1024
1070
  key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,
1025
1071
  }).toValue(authenticateFn);
1026
1072
 
1027
- // 3. Client connected handler called AFTER successful authentication
1073
+ // 3. Client connected handler - called AFTER successful authentication
1028
1074
  // This is where you register custom event handlers on each socket
1029
1075
  const clientConnectedFn: ISocketIOServerBaseOptions['clientConnectedFn'] = ({ socket }) => {
1030
1076
  const chatService = this.get<ChatService>({
@@ -1041,7 +1087,7 @@ export class ChatApp extends BaseApplication {
1041
1087
  key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,
1042
1088
  }).toValue(clientConnectedFn);
1043
1089
 
1044
- // 4. (Optional) Custom server options override defaults
1090
+ // 4. (Optional) Custom server options - override defaults
1045
1091
  this.bind({
1046
1092
  key: SocketIOBindingKeys.SERVER_OPTIONS,
1047
1093
  }).toValue({
@@ -1054,7 +1100,7 @@ export class ChatApp extends BaseApplication {
1054
1100
  },
1055
1101
  });
1056
1102
 
1057
- // 5. Register the component that's it!
1103
+ // 5. Register the component - that's it!
1058
1104
  // SocketIOComponent handles:
1059
1105
  // - Runtime detection (Node.js / Bun)
1060
1106
  // - Post-start hook to create SocketIOServerHelper after server starts
@@ -1096,7 +1142,7 @@ Application Lifecycle
1096
1142
  preConfigure()
1097
1143
  ├── Register repositories, services, controllers
1098
1144
  └── setupSocketIO()
1099
- ├── Bind RedisHelper → REDIS_CONNECTION
1145
+ ├── Bind RedisSingleHelper → REDIS_CONNECTION
1100
1146
  ├── Bind authenticateFn → AUTHENTICATE_HANDLER
1101
1147
  ├── Bind clientConnectedFn → CLIENT_CONNECTED_HANDLER
1102
1148
  ├── Bind server options → SERVER_OPTIONS
@@ -1104,12 +1150,12 @@ preConfigure()
1104
1150
 
1105
1151
  initialize()
1106
1152
  └── SocketIOComponent.binding()
1107
- ├── resolveBindings() reads all bound values
1108
- ├── RuntimeModules.detect() auto-detect Node.js or Bun
1153
+ ├── resolveBindings() - reads all bound values
1154
+ ├── RuntimeModules.detect() - auto-detect Node.js or Bun
1109
1155
  └── registerPostStartHook('socket-io-initialize')
1110
1156
 
1111
1157
  start()
1112
- ├── startBunModule() / startNodeModule() server starts
1158
+ ├── startBunModule() / startNodeModule() - server starts
1113
1159
  └── executePostStartHooks()
1114
1160
  └── 'socket-io-initialize'
1115
1161
  ├── Create SocketIOServerHelper (with Redis adapter + emitter)
@@ -1215,7 +1261,7 @@ export class ChatController extends BaseRestController {
1215
1261
  },
1216
1262
  });
1217
1263
 
1218
- // GET /chat/rooms/:roomId/messages
1264
+ // GET /chat/rooms/{roomId}/messages
1219
1265
  this.bindRoute({ configs: ChatRoutes.GET_MESSAGES }).to({
1220
1266
  handler: async (c: TRouteContext) => {
1221
1267
  const roomId = c.req.param('roomId');
@@ -1241,7 +1287,7 @@ export class ChatController extends BaseRestController {
1241
1287
  },
1242
1288
  });
1243
1289
 
1244
- // GET /chat/dm/:userId
1290
+ // GET /chat/dm/{userId}
1245
1291
  this.bindRoute({ configs: ChatRoutes.GET_DIRECT_MESSAGES }).to({
1246
1292
  handler: async (c: TRouteContext) => {
1247
1293
  const currentUserId = c.get('userId');
@@ -1265,7 +1311,7 @@ export class ChatController extends BaseRestController {
1265
1311
 
1266
1312
  ## 8. Client Usage
1267
1313
 
1268
- Clients must follow the `SocketIOServerHelper` authentication flow: **connect** → **emit `authenticate`** → **receive `authenticated`** then they're ready to send and receive events.
1314
+ Clients must follow the `SocketIOServerHelper` authentication flow: **connect** → **emit `authenticate`** → **receive `authenticated`** - then they're ready to send and receive events.
1269
1315
 
1270
1316
  ### JavaScript Client Example
1271
1317
 
@@ -1293,16 +1339,16 @@ class ChatClient {
1293
1339
  }
1294
1340
 
1295
1341
  // ---------------------------------------------------------------------------
1296
- // Connection lifecycle follows SocketIOServerHelper's auth flow
1342
+ // Connection lifecycle - follows SocketIOServerHelper's auth flow
1297
1343
  // ---------------------------------------------------------------------------
1298
1344
  private setupLifecycle() {
1299
- // Step 1: Connected now send authenticate event
1345
+ // Step 1: Connected - now send authenticate event
1300
1346
  this._socket.on('connect', () => {
1301
1347
  console.log('Connected | id:', this._socket.id);
1302
1348
  this._socket.emit('authenticate');
1303
1349
  });
1304
1350
 
1305
- // Step 2: Authenticated ready to use
1351
+ // Step 2: Authenticated - ready to use
1306
1352
  this._socket.on('authenticated', (data: { id: string; time: string }) => {
1307
1353
  console.log('Authenticated | id:', data.id);
1308
1354
  this._authenticated = true;
@@ -1317,7 +1363,7 @@ class ChatClient {
1317
1363
 
1318
1364
  // Keep-alive ping from server (every 30s)
1319
1365
  this._socket.on('ping', () => {
1320
- // Server is checking we're alive no action needed
1366
+ // Server is checking we're alive - no action needed
1321
1367
  });
1322
1368
 
1323
1369
  this._socket.on('disconnect', (reason: string) => {
@@ -1327,7 +1373,7 @@ class ChatClient {
1327
1373
  }
1328
1374
 
1329
1375
  // ---------------------------------------------------------------------------
1330
- // Custom event handlers registered after authentication
1376
+ // Custom event handlers - registered after authentication
1331
1377
  // ---------------------------------------------------------------------------
1332
1378
  private setupEventHandlers() {
1333
1379
  this._socket.on('message:new', (message) => {
@@ -1405,7 +1451,7 @@ const chat = new ChatClient({ token: 'your-jwt-token' });
1405
1451
  For server-to-server or microservice communication, use the built-in `SocketIOClientHelper`:
1406
1452
 
1407
1453
  ```typescript
1408
- import { SocketIOClientHelper } from '@venizia/ignis-helpers';
1454
+ import { SocketIOClientHelper } from '@venizia/ignis-helpers/socket-io';
1409
1455
 
1410
1456
  const client = new SocketIOClientHelper({
1411
1457
  identifier: 'chat-service-client',
@@ -1446,7 +1492,7 @@ Redis scaling is **built-in** and **automatic** when using `SocketIOComponent`.
1446
1492
 
1447
1493
  ### How It Works
1448
1494
 
1449
- When you bind a `RedisHelper` to `SocketIOBindingKeys.REDIS_CONNECTION`, the `SocketIOServerHelper` automatically:
1495
+ When you bind a `RedisSingleHelper` (or any `AbstractRedisHelper` subclass) to `SocketIOBindingKeys.REDIS_CONNECTION`, the `SocketIOServerHelper` automatically:
1450
1496
 
1451
1497
  1. Creates 3 duplicate Redis connections from your helper
1452
1498
  2. Sets up `@socket.io/redis-adapter` for cross-instance pub/sub
@@ -1464,7 +1510,7 @@ Process A Redis Process B
1464
1510
 
1465
1511
  ### Multi-Instance Deployment
1466
1512
 
1467
- Run multiple instances behind a load balancer Redis keeps them in sync:
1513
+ Run multiple instances behind a load balancer - Redis keeps them in sync:
1468
1514
 
1469
1515
  ```bash
1470
1516
  # Instance 1
@@ -1481,10 +1527,10 @@ All calls to `socketIOHelper.send()` go through the Redis emitter, so messages r
1481
1527
 
1482
1528
  ### Redis Configuration
1483
1529
 
1484
- The only thing you need is a `RedisHelper` bound to the correct key (already done in the application setup):
1530
+ The only thing you need is a `RedisSingleHelper` bound to the correct key (already done in the application setup):
1485
1531
 
1486
1532
  ```typescript
1487
- this.redisHelper = new RedisHelper({
1533
+ this.redisHelper = new RedisSingleHelper({
1488
1534
  name: 'chat-redis',
1489
1535
  host: process.env.APP_ENV_REDIS_HOST ?? 'localhost',
1490
1536
  port: +(process.env.APP_ENV_REDIS_PORT ?? 6379),
@@ -1492,7 +1538,7 @@ this.redisHelper = new RedisHelper({
1492
1538
  autoConnect: false,
1493
1539
  });
1494
1540
 
1495
- this.bind<RedisHelper>({
1541
+ this.bind<RedisSingleHelper>({
1496
1542
  key: SocketIOBindingKeys.REDIS_CONNECTION,
1497
1543
  }).toValue(this.redisHelper);
1498
1544
  ```
@@ -1507,7 +1553,7 @@ this.bind<RedisHelper>({
1507
1553
  | Presence | `socketIOHelper.send()` broadcast on connect/disconnect |
1508
1554
  | History | REST API with cursor-based pagination |
1509
1555
  | Authentication | `SocketIOServerHelper` built-in flow (connect → authenticate → authenticated) |
1510
- | Scaling | Redis adapter/emitter automatic via `RedisHelper` binding |
1556
+ | Scaling | Redis adapter/emitter - automatic via `RedisSingleHelper` binding |
1511
1557
  | Runtime | Auto-detected (Node.js or Bun) by `SocketIOComponent` |
1512
1558
 
1513
1559
  ## Next Steps
@@ -1520,6 +1566,6 @@ this.bind<RedisHelper>({
1520
1566
 
1521
1567
  ## See Also
1522
1568
 
1523
- - [Socket.IO Component](/extensions/components/socket-io/) Component reference
1524
- - [Socket.IO Helper](/extensions/helpers/socket-io/) Server + Client helper API
1525
- - [Socket.IO Test Example](https://github.com/VENIZIA-AI/ignis/tree/main/examples/socket-io-test) Working example with automated test client
1569
+ - [Socket.IO Component](/extensions/components/socket-io/) - Component reference
1570
+ - [Socket.IO Helper](/extensions/helpers/socket-io/) - Server + Client helper API
1571
+ - [Socket.IO Test Example](https://github.com/VENIZIA-AI/ignis/tree/main/examples/socket-io-test) - Working example with automated test client