@venizia/ignis-docs 0.2.0 → 0.2.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,51 +1,23 @@
1
+ ---
2
+ title: Socket.IO
3
+ description: Runtime-agnostic Socket.IO server and client helpers with a built-in authentication handshake and Redis-backed horizontal scaling
4
+ difficulty: intermediate
5
+ ---
6
+
1
7
  # Socket.IO
2
8
 
3
- Runtime-agnostic Socket.IO server and client helpers with built-in authentication flow, room management, Redis adapter for horizontal scaling, and automatic heartbeat keep-alive.
9
+ `SocketIOServerHelper` and `SocketIOClientHelper` wrap `socket.io` with a mandatory post-connection authentication handshake and room management. A Redis adapter makes sure events reach a client no matter which server instance it's connected to.
4
10
 
5
- ## Quick Reference
11
+ These are the raw classes: construct and `configure()` them yourself. Need the server helper inside an IGNIS app instead? [`SocketIOComponent`](/extensions/components/socket-io/) wires it into DI for you, on both Node.js and Bun.
6
12
 
7
- | Class | Extends | Role |
8
- |-------|---------|------|
9
- | `SocketIOServerHelper` | `BaseHelper` | Manages a Socket.IO server with authentication, rooms, ping intervals, and Redis-backed messaging |
10
- | `SocketIOClientHelper` | `BaseHelper` | Manages a Socket.IO client connection with authentication, event subscriptions, and room operations |
13
+ ## In one example
11
14
 
12
- #### Import Paths
15
+ The smallest working server: construct with a Redis connection and an `authenticateFn`, then `configure()`.
13
16
 
14
17
  ```typescript
15
- import {
16
- SocketIOServerHelper,
17
- SocketIOClientHelper,
18
- SocketIOConstants,
19
- SocketIOClientStates,
20
- } from '@venizia/ignis-helpers/socket-io';
21
-
22
- import type {
23
- TSocketIOServerOptions,
24
- ISocketIOServerBaseOptions,
25
- ISocketIOServerNodeOptions,
26
- ISocketIOServerBunOptions,
27
- ISocketIOClientOptions,
28
- IOptions,
29
- ISocketIOClient,
30
- IHandshake,
31
- TSocketIOAuthenticateFn,
32
- TSocketIOValidateRoomFn,
33
- TSocketIOClientConnectedFn,
34
- TSocketIOEventHandler,
35
- TSocketIOClientState,
36
- } from '@venizia/ignis-helpers/socket-io';
37
- ```
38
-
39
- ## Creating an Instance
40
-
41
- ### Server
42
-
43
- `SocketIOServerHelper` requires a Redis connection for the pub/sub adapter, an HTTP server (Node.js) or Bun engine instance, and an authentication function.
44
-
45
- ```typescript
46
- import { SocketIOServerHelper } from '@venizia/ignis-helpers/socket-io';
47
- import { RedisSingleHelper } from '@venizia/ignis-helpers';
48
18
  import { createServer } from 'node:http';
19
+ import { RedisSingleHelper } from '@venizia/ignis-helpers';
20
+ import { SocketIOServerHelper } from '@venizia/ignis-helpers/socket-io';
49
21
 
50
22
  const httpServer = createServer();
51
23
 
@@ -61,457 +33,135 @@ const socketServer = new SocketIOServerHelper({
61
33
  runtime: 'node',
62
34
  server: httpServer,
63
35
  redisConnection: redisHelper,
64
- serverOptions: {
65
- cors: { origin: '*' },
66
- path: '/socket.io',
67
- },
68
- authenticateFn: async (handshake) => {
69
- const token = handshake.auth?.token;
70
- return !!token; // Return true to accept, false to reject
71
- },
72
- validateRoomFn: async ({ socket, rooms }) => {
73
- // Return only the rooms the client is allowed to join
74
- return rooms.filter(r => r.startsWith('public-'));
75
- },
76
- clientConnectedFn: async ({ socket }) => {
77
- console.log('Client authenticated:', socket.id);
78
- },
79
- defaultRooms: ['io-default', 'io-notification'],
80
- authenticateTimeout: 10000,
81
- pingInterval: 30000,
82
- });
83
- ```
84
-
85
- #### `TSocketIOServerOptions`
86
-
87
- A discriminated union based on the `runtime` field:
88
-
89
- | Option | Type | Default | Description |
90
- |--------|------|---------|-------------|
91
- | `identifier` | `string` | -- | Unique identifier for this server instance (used as logger scope) |
92
- | `runtime` | `'node' \| 'bun'` | -- | Runtime environment. Determines which server field is required |
93
- | `server` | `HTTPServer` | -- | Node.js HTTP server instance. **Required when `runtime` is `'node'`** |
94
- | `engine` | `any` | -- | `@socket.io/bun-engine` Server instance. **Required when `runtime` is `'bun'`** |
95
- | `serverOptions` | `Partial<ServerOptions>` | -- | **Required by the type.** Socket.IO `ServerOptions` (cors, path, transports, etc.). Pass `{}` when no options are needed; nullish values fall back to `{}` at runtime |
96
- | `redisConnection` | `IRedisHelper` | -- | **Required.** Redis helper used to create pub, sub, and emitter clients |
97
- | `authenticateFn` | `TSocketIOAuthenticateFn` | -- | **Required.** Called with the client's handshake data. Return `true` to accept, `false` to reject |
98
- | `validateRoomFn` | `TSocketIOValidateRoomFn` | `undefined` | Called when a client requests to join rooms. Return the allowed subset |
99
- | `clientConnectedFn` | `TSocketIOClientConnectedFn` | `undefined` | Called after a client is fully authenticated and has joined default rooms |
100
- | `defaultRooms` | `string[]` | `['io-default', 'io-notification']` | Rooms that every authenticated client joins automatically |
101
- | `authenticateTimeout` | `number` | `10000` (10 s) | Milliseconds before an unauthenticated client is disconnected |
102
- | `pingInterval` | `number` | `30000` (30 s) | Interval in milliseconds between heartbeat pings to authenticated clients |
103
-
104
- > [!WARNING]
105
- > If no `validateRoomFn` is provided, **all room join requests are rejected** with a warning log. You must provide this callback if you want clients to join custom rooms beyond the `defaultRooms`.
106
-
107
- ### Client
108
-
109
- `SocketIOClientHelper` connects to a Socket.IO server. Configuration is done entirely via the constructor -- `configure()` is called automatically.
110
-
111
- ```typescript
112
- import { SocketIOClientHelper } from '@venizia/ignis-helpers/socket-io';
113
-
114
- const socketClient = new SocketIOClientHelper({
115
- identifier: 'my-client',
116
- host: 'http://localhost:3000',
117
- options: {
118
- path: '/socket.io',
119
- extraHeaders: {
120
- Authorization: 'Bearer my-jwt-token',
121
- },
122
- },
123
- onConnected: () => {
124
- console.log('Connected to server');
125
- socketClient.authenticate();
126
- },
127
- onDisconnected: (reason) => {
128
- console.log('Disconnected:', reason);
129
- },
130
- onError: (error) => {
131
- console.error('Connection error:', error);
132
- },
133
- onAuthenticated: () => {
134
- console.log('Successfully authenticated');
135
- },
136
- onUnauthenticated: (message) => {
137
- console.warn('Authentication failed:', message);
138
- },
139
- });
140
- ```
141
-
142
- #### `ISocketIOClientOptions`
143
-
144
- | Option | Type | Default | Description |
145
- |--------|------|---------|-------------|
146
- | `identifier` | `string` | -- | Unique identifier for this client (used as logger scope) |
147
- | `host` | `string` | -- | Server URL to connect to (e.g., `'http://localhost:3000'`) |
148
- | `options` | `IOptions` | -- | Socket.IO client options (extends `SocketOptions` with `path` and `extraHeaders`) |
149
- | `onConnected` | `() => ValueOrPromise<void>` | `undefined` | Called when the transport connection is established |
150
- | `onDisconnected` | `(reason: string) => ValueOrPromise<void>` | `undefined` | Called when disconnected. The client state resets to `'unauthorized'` |
151
- | `onError` | `(error: Error) => ValueOrPromise<void>` | `undefined` | Called on connection errors |
152
- | `onAuthenticated` | `() => ValueOrPromise<void>` | `undefined` | Called when the server sends an `authenticated` event |
153
- | `onUnauthenticated` | `(message: string) => ValueOrPromise<void>` | `undefined` | Called when the server rejects authentication |
154
-
155
- #### `IOptions`
156
-
157
- Extends `SocketOptions` from `socket.io-client`:
158
-
159
- | Option | Type | Description |
160
- |--------|------|-------------|
161
- | `path` | `string` | Socket.IO server path (e.g., `'/socket.io'`) |
162
- | `extraHeaders` | `Record<string \| symbol \| number, any>` | Additional headers sent with the connection request |
163
-
164
- ## Usage
165
-
166
- ### Server Setup
167
-
168
- After constructing the server helper, call `configure()` to initialize the Socket.IO server, set up the Redis adapter, and start listening for connections.
169
-
170
- ```typescript
171
- const socketServer = new SocketIOServerHelper({
172
- identifier: 'my-server',
173
- runtime: 'node',
174
- server: httpServer,
175
- redisConnection: redisHelper,
176
36
  serverOptions: { cors: { origin: '*' } },
177
- authenticateFn: async (handshake) => {
178
- return verifyToken(handshake.auth?.token);
179
- },
37
+ authenticateFn: async handshake => !!handshake.auth?.token,
180
38
  });
181
39
 
182
40
  await socketServer.configure();
183
- // Server is now ready and listening for connections
184
-
185
- httpServer.listen(3000, () => {
186
- console.log('HTTP + Socket.IO server running on port 3000');
187
- });
41
+ httpServer.listen(3000);
188
42
  ```
189
43
 
190
- #### Bun Runtime
191
-
192
- For Bun, pass the `@socket.io/bun-engine` instance instead of an HTTP server:
44
+ A client connects at the transport level, then must explicitly authenticate before it can join rooms or exchange events:
193
45
 
194
46
  ```typescript
195
- import { SocketIOServerHelper } from '@venizia/ignis-helpers/socket-io';
196
-
197
- const socketServer = new SocketIOServerHelper({
198
- identifier: 'my-bun-server',
199
- runtime: 'bun',
200
- engine: bunEngineInstance,
201
- redisConnection: redisHelper,
202
- serverOptions: {},
203
- authenticateFn: async (handshake) => {
204
- return verifyToken(handshake.auth?.token);
205
- },
206
- });
207
-
208
- await socketServer.configure();
209
- ```
210
-
211
- ### Client Connection
212
-
213
- The client connects automatically on construction. Call `authenticate()` after the connection is established to trigger the server-side authentication flow.
47
+ import { SocketIOClientHelper } from '@venizia/ignis-helpers/socket-io';
214
48
 
215
- ```typescript
216
49
  const client = new SocketIOClientHelper({
217
50
  identifier: 'app-client',
218
51
  host: 'http://localhost:3000',
219
52
  options: {
220
53
  path: '/socket.io',
221
- extraHeaders: { Authorization: 'Bearer my-token' },
222
- },
223
- onConnected: () => {
224
- // Connection established -- initiate authentication
225
- client.authenticate();
226
- },
227
- onAuthenticated: () => {
228
- // Now safe to subscribe and emit
229
- client.joinRooms({ rooms: ['chat-room-1'] });
54
+ extraHeaders: { Authorization: 'Bearer my-jwt-token' },
230
55
  },
56
+ onConnected: () => client.authenticate(),
57
+ onAuthenticated: () => client.emit({ topic: 'ready', data: {} }),
231
58
  });
232
59
  ```
233
60
 
234
- ### Emitting Events
61
+ ## How it works
235
62
 
236
- #### From the Server
63
+ - **Two independent helpers, both extending `BaseHelper`:**
237
64
 
238
- Use `send()` to emit events through the Redis emitter. Messages can target a specific socket ID, a room, or broadcast to all clients.
65
+ | Helper | Wraps |
66
+ |--------|-------|
67
+ | `SocketIOServerHelper` | a `socket.io` `Server` |
68
+ | `SocketIOClientHelper` | a `socket.io-client` `Socket` |
239
69
 
240
- ```typescript
241
- // Send to a specific client
242
- socketServer.send({
243
- destination: 'client-socket-id',
244
- payload: {
245
- topic: 'notification',
246
- data: { message: 'Hello!' },
247
- },
248
- });
70
+ - **The client isn't locked to this server.** It can talk to any `socket.io` server, not only this one.
71
+ - **Runtime-agnostic server.** Pass a Node.js `http.Server` for `runtime: 'node'`, or an `@socket.io/bun-engine` instance for `runtime: 'bun'`.
72
+ - **Redis is mandatory server-side.** `configure()` duplicates the parent `redisConnection` into three dedicated clients:
249
73
 
250
- // Broadcast to all connected clients (no destination)
251
- socketServer.send({
252
- payload: {
253
- topic: 'announcement',
254
- data: { message: 'Server update in 5 minutes' },
255
- },
256
- });
74
+ | Client | Powers |
75
+ |--------|--------|
76
+ | `redisPub` + `redisSub` | `@socket.io/redis-adapter` - cross-instance room broadcast |
77
+ | `redisEmitter` | `@socket.io/redis-emitter` - used by `send()` |
257
78
 
258
- // Send with logging and callback
259
- socketServer.send({
260
- destination: 'some-room',
261
- payload: {
262
- topic: 'room-event',
263
- data: { action: 'update' },
264
- },
265
- doLog: true,
266
- cb: () => {
267
- console.log('Message queued');
268
- },
269
- });
270
- ```
79
+ - **Boot fails fast on a broken Redis connection.** `configure()` waits for all three clients to reach `ready`. It rejects after 30 seconds if any never do, so a broken Redis connection fails boot instead of hanging it.
80
+ - **Authentication is a step separate from connecting.** A client connects at the transport level in state `UNAUTHORIZED`, then must emit `'authenticate'`. The server then calls `authenticateFn(handshake)`.
81
+ - **Only `true` authenticates.** It moves the client to `AUTHENTICATED` and joins it to `defaultRooms`.
82
+ - **Unauthenticated clients time out.** A client that never authenticates within `authenticateTimeout` is disconnected. That includes a client whose `authenticateFn` is still pending when the timeout fires.
83
+ - **Heartbeat has no pong check.** Once authenticated, the server pings on `pingInterval` as a keep-alive. A silently dead connection is only caught when the underlying transport itself notices.
84
+ - **Custom rooms need `validateRoomFn`.** Room joins beyond `defaultRooms` are rejected unless you supply `validateRoomFn`.
271
85
 
272
- #### From the Client
86
+ **Defaults**
273
87
 
274
- Use `emit()` to send events to the server.
88
+ | Option | Default | Purpose |
89
+ |--------|---------|---------|
90
+ | `authenticateTimeout` | 10s | Disconnects a client that never authenticates |
91
+ | `pingInterval` | 30s | Server heartbeat interval once authenticated |
275
92
 
276
- ```typescript
277
- client.emit({
278
- topic: 'chat-message',
279
- data: { text: 'Hello, world!' },
280
- });
93
+ ## Common tasks
281
94
 
282
- // With logging enabled
283
- client.emit({
284
- topic: 'user-action',
285
- data: { action: 'click', target: 'button-1' },
286
- doLog: true,
287
- });
95
+ ### Send a message from the server
288
96
 
289
- // With callback
290
- client.emit({
291
- topic: 'upload-complete',
292
- data: { fileId: '123' },
293
- cb: () => {
294
- console.log('Emit completed');
295
- },
296
- });
297
- ```
298
-
299
- ### Listening for Events
300
-
301
- #### Server-Side Event Binding
302
-
303
- Use `on()` to register event handlers on the IO server instance.
97
+ `send()` goes through the Redis emitter, so it reaches the target on any server instance. Omit `destination` to broadcast to everyone.
304
98
 
305
99
  ```typescript
306
- socketServer.on({
307
- topic: 'custom-event',
308
- handler: (data) => {
309
- console.log('Received:', data);
310
- },
100
+ socketServer.send({
101
+ destination: 'some-room', // socket ID, room name, or omitted to broadcast
102
+ payload: { topic: 'notification', data: { message: 'Hello!' } },
103
+ callback: () => console.log('queued'),
311
104
  });
312
105
  ```
313
106
 
314
- #### Client-Side Event Subscription
107
+ ### Listen for a custom event
315
108
 
316
- Use `subscribe()` for single events and `subscribeMany()` for batch registration.
109
+ Register on the server with `on()`. Subscribe on the client with `subscribe()`.
317
110
 
318
111
  ```typescript
319
- // Single event
320
- client.subscribe({
321
- event: 'notification',
322
- handler: (data) => {
323
- console.log('Notification:', data);
324
- },
112
+ socketServer.on({
113
+ topic: 'custom-event',
114
+ handler: (data: { userId: string }) => console.log('received:', data),
325
115
  });
326
116
 
327
- // Prevent duplicate handlers (default behavior)
328
117
  client.subscribe({
329
118
  event: 'notification',
330
- handler: (data) => { /* ... */ },
331
- ignoreDuplicate: true, // Default: true -- skips if handler already exists
332
- });
333
-
334
- // Allow multiple handlers for same event
335
- client.subscribe({
336
- event: 'chat-message',
337
- handler: handler1,
338
- ignoreDuplicate: false,
339
- });
340
-
341
- // Batch subscribe
342
- client.subscribeMany({
343
- events: {
344
- 'user-joined': (data) => console.log('Joined:', data),
345
- 'user-left': (data) => console.log('Left:', data),
346
- 'typing': (data) => console.log('Typing:', data),
347
- },
119
+ handler: data => console.log('notification:', data),
348
120
  });
349
-
350
- // Unsubscribe from a specific event (removes all handlers)
351
- client.unsubscribe({ event: 'notification' });
352
-
353
- // Unsubscribe from multiple events
354
- client.unsubscribeMany({ events: ['user-joined', 'user-left'] });
355
- ```
356
-
357
- ### Rooms
358
-
359
- #### Client-Side Room Operations
360
-
361
- ```typescript
362
- // Join rooms (validated by server's validateRoomFn)
363
- client.joinRooms({ rooms: ['chat-room-1', 'notifications'] });
364
-
365
- // Leave rooms
366
- client.leaveRooms({ rooms: ['chat-room-1'] });
367
121
  ```
368
122
 
369
- #### Server-Side Room Behavior
123
+ ### Manage rooms
370
124
 
371
- Authenticated clients automatically join the `defaultRooms` (by default, `'io-default'` and `'io-notification'`). Custom room join requests are validated through `validateRoomFn` before the client is allowed to join.
125
+ Clients request rooms with `joinRooms()` / `leaveRooms()`. The server filters join requests through `validateRoomFn`.
372
126
 
373
127
  ```typescript
374
128
  const socketServer = new SocketIOServerHelper({
375
129
  // ...
376
130
  defaultRooms: ['general', 'announcements'],
377
- validateRoomFn: async ({ socket, rooms }) => {
378
- // Only allow rooms the user has permission for
379
- const userPermissions = await getUserPermissions(socket.id);
380
- return rooms.filter(room => userPermissions.includes(room));
381
- },
131
+ validateRoomFn: async ({ socket, rooms }) => rooms.filter(room => room.startsWith('public-')),
382
132
  });
383
- ```
384
133
 
385
- ### Authentication Flow
386
-
387
- The server enforces a post-connection authentication protocol:
388
-
389
- ```
390
- Client connects
391
- |
392
- v
393
- Server creates client entry (state: UNAUTHORIZED)
394
- |-- Starts authenticateTimeout timer (default: 10s)
395
- |-- Registers disconnect handler
396
- |
397
- Client emits 'authenticate' event
398
- |
399
- v
400
- Server calls authenticateFn(handshake)
401
- |
402
- +-- Returns true:
403
- | |-- State -> AUTHENTICATED
404
- | |-- Clear auth timeout
405
- | |-- Join default rooms
406
- | |-- Start ping interval
407
- | |-- Emit 'authenticated' to client
408
- | +-- Invoke clientConnectedFn
409
- |
410
- +-- Returns false:
411
- | |-- State -> UNAUTHORIZED
412
- | |-- Emit 'unauthenticated' to client
413
- | +-- Disconnect
414
- |
415
- +-- Timeout (no auth within authenticateTimeout):
416
- +-- Disconnect
134
+ client.joinRooms({ rooms: ['public-chat'] });
135
+ client.leaveRooms({ rooms: ['public-chat'] });
417
136
  ```
418
137
 
419
- ### Redis Adapter
420
-
421
- The server uses `@socket.io/redis-adapter` and `@socket.io/redis-emitter` for horizontal scaling. Three Redis connections are created by duplicating the provided `redisConnection` client:
422
-
423
- - **redisPub** -- Publishes adapter messages
424
- - **redisSub** -- Subscribes to adapter messages
425
- - **redisEmitter** -- Powers `send()` for cross-instance message delivery
426
-
427
- All three connections are initialized and awaited during `configure()`. If the parent client uses `lazyConnect`, the duplicated clients will connect automatically.
428
-
429
- > [!NOTE]
430
- > `configure()` fails fast: if any of the three Redis clients never reaches the `ready` state, it rejects after **30 seconds** rather than hanging boot indefinitely.
138
+ ### Emit from the client
431
139
 
432
140
  ```typescript
433
- // Messages sent via send() use the Redis emitter,
434
- // so they reach clients on ANY server instance
435
- socketServer.send({
436
- destination: 'some-room',
437
- payload: {
438
- topic: 'update',
439
- data: { value: 42 },
440
- },
141
+ client.emit({
142
+ topic: 'chat-message',
143
+ data: { text: 'Hello, world!' },
144
+ callback: () => console.log('emit completed'),
441
145
  });
442
146
  ```
443
147
 
444
- ### Graceful Shutdown
445
-
446
- #### Server
447
-
448
- ```typescript
449
- await socketServer.shutdown();
450
- // 1. Disconnects all clients (clears intervals and timeouts)
451
- // 2. Closes the IO server
452
- // 3. Quits all three Redis connections (pub, sub, emitter)
453
- ```
454
-
455
- #### Client
148
+ ### Shut down cleanly
456
149
 
457
150
  ```typescript
458
- client.shutdown();
459
- // 1. Removes all event listeners
460
- // 2. Disconnects if connected
461
- // 3. Resets state to 'unauthorized'
151
+ await socketServer.shutdown(); // disconnects clients, closes IO server, quits all 3 Redis clients
152
+ client.shutdown(); // removes listeners, disconnects, resets state
462
153
  ```
463
154
 
464
- ## Troubleshooting
465
-
466
- ### `[SocketIOServerHelper] Invalid HTTP server for Node.js runtime!`
467
-
468
- **Cause:** The `server` option is missing or falsy when `runtime` is `'node'`.
469
-
470
- **Fix:** Pass a valid `http.Server` instance.
471
-
472
- ### `[SocketIOServerHelper] Invalid @socket.io/bun-engine instance for Bun runtime!`
473
-
474
- **Cause:** The `engine` option is missing or falsy when `runtime` is `'bun'`.
475
-
476
- **Fix:** Pass a valid `@socket.io/bun-engine` Server instance.
477
-
478
- ### `[SocketIOServerHelper] Unsupported runtime!`
479
-
480
- **Cause:** The `runtime` value is neither `'node'` nor `'bun'`.
481
-
482
- **Fix:** Use `RuntimeModules.NODE` or `RuntimeModules.BUN`.
483
-
484
- ### `Invalid redis connection to config socket.io adapter!`
485
-
486
- **Cause:** The `redisConnection` option is missing, `null`, or `undefined`.
487
-
488
- **Fix:** Pass a valid `IRedisHelper` instance (e.g., `RedisSingleHelper` or `RedisClusterHelper`).
489
-
490
- ### `[on] Invalid topic to start binding handler`
491
-
492
- **Cause:** An empty or falsy `topic` was passed to `on()`.
493
-
494
- **Fix:** Provide a non-empty string topic.
495
-
496
- ### `[on] IOServer is not initialized yet!`
497
-
498
- **Cause:** `on()` was called before `configure()` completed.
499
-
500
- **Fix:** Await `configure()` before registering event handlers.
501
-
502
- ### `Invalid socket client state to emit`
503
-
504
- **Cause (client):** `emit()` was called when the client is not connected.
505
-
506
- **Fix:** Check that the client is connected before emitting, or emit inside the `onConnected` callback.
507
-
508
- ### `Topic is required to emit`
509
-
510
- **Cause (client):** `emit()` was called with an empty or falsy `topic`.
155
+ ## See also
511
156
 
512
- **Fix:** Provide a non-empty string topic.
157
+ - [Full reference](./api) - every method signature, type, constant, and error case
158
+ - [Socket.IO Component](/extensions/components/socket-io/) - DI-managed lifecycle wrapper around this helper
159
+ - [WebSocket Helper](../websocket/) - Bun-native alternative with no `socket.io` dependency
160
+ - [Redis Helper](../redis/) - `RedisSingleHelper` / `RedisClusterHelper` used as `redisConnection`
513
161
 
514
- ## See Also
162
+ **Files:**
515
163
 
516
- - [API Reference](./api) -- Full method signatures, types, and constants
517
- - [WebSocket Helper](../websocket/) -- Bun-native WebSocket alternative
164
+ - [`packages/helpers/src/modules/socket/socket-io/server/helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/socket/socket-io/server/helper.ts) - `SocketIOServerHelper`
165
+ - [`packages/helpers/src/modules/socket/socket-io/client/helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/socket/socket-io/client/helper.ts) - `SocketIOClientHelper`
166
+ - [`packages/helpers/src/modules/socket/socket-io/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/socket/socket-io/common/types.ts) - option and callback types
167
+ - [`packages/helpers/src/modules/socket/socket-io/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/socket/socket-io/common/constants.ts) - `SocketIOConstants`, `SocketIOClientStates`