@venizia/ignis-docs 0.0.5 → 0.0.6-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 (98) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +0 -8
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +3 -18
  78. package/wiki/references/utilities/jsx.md +1 -8
  79. package/wiki/references/utilities/statuses.md +0 -7
  80. package/wiki/references/components/authentication.md +0 -476
  81. package/wiki/references/components/mail.md +0 -687
  82. package/wiki/references/components/socket-io.md +0 -562
  83. package/wiki/references/components/static-asset.md +0 -1277
  84. package/wiki/references/helpers/cron.md +0 -108
  85. package/wiki/references/helpers/crypto.md +0 -132
  86. package/wiki/references/helpers/env.md +0 -83
  87. package/wiki/references/helpers/error.md +0 -97
  88. package/wiki/references/helpers/inversion.md +0 -176
  89. package/wiki/references/helpers/logger.md +0 -296
  90. package/wiki/references/helpers/network.md +0 -396
  91. package/wiki/references/helpers/queue.md +0 -150
  92. package/wiki/references/helpers/redis.md +0 -142
  93. package/wiki/references/helpers/socket-io.md +0 -932
  94. package/wiki/references/helpers/storage.md +0 -665
  95. package/wiki/references/helpers/testing.md +0 -133
  96. package/wiki/references/helpers/types.md +0 -167
  97. package/wiki/references/helpers/uid.md +0 -167
  98. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -0,0 +1,513 @@
1
+ # Socket.IO
2
+
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.
4
+
5
+ ## Quick Reference
6
+
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 |
11
+
12
+ #### Import Paths
13
+
14
+ ```typescript
15
+ import {
16
+ SocketIOServerHelper,
17
+ SocketIOClientHelper,
18
+ SocketIOConstants,
19
+ SocketIOClientStates,
20
+ } from '@venizia/ignis-helpers';
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';
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';
47
+ import { DefaultRedisHelper } from '@venizia/ignis-helpers';
48
+ import { createServer } from 'node:http';
49
+
50
+ const httpServer = createServer();
51
+
52
+ const redisHelper = new DefaultRedisHelper({
53
+ name: 'socket-redis',
54
+ host: 'localhost',
55
+ port: 6379,
56
+ });
57
+
58
+ const socketServer = new SocketIOServerHelper({
59
+ identifier: 'my-socket-server',
60
+ runtime: 'node',
61
+ server: httpServer,
62
+ redisConnection: redisHelper,
63
+ serverOptions: {
64
+ cors: { origin: '*' },
65
+ path: '/socket.io',
66
+ },
67
+ authenticateFn: async (handshake) => {
68
+ const token = handshake.auth?.token;
69
+ return !!token; // Return true to accept, false to reject
70
+ },
71
+ validateRoomFn: async ({ socket, rooms }) => {
72
+ // Return only the rooms the client is allowed to join
73
+ return rooms.filter(r => r.startsWith('public-'));
74
+ },
75
+ clientConnectedFn: async ({ socket }) => {
76
+ console.log('Client authenticated:', socket.id);
77
+ },
78
+ defaultRooms: ['io-default', 'io-notification'],
79
+ authenticateTimeout: 10000,
80
+ pingInterval: 30000,
81
+ });
82
+ ```
83
+
84
+ #### `TSocketIOServerOptions`
85
+
86
+ A discriminated union based on the `runtime` field:
87
+
88
+ | Option | Type | Default | Description |
89
+ |--------|------|---------|-------------|
90
+ | `identifier` | `string` | -- | Unique identifier for this server instance (used as logger scope) |
91
+ | `runtime` | `'node' \| 'bun'` | -- | Runtime environment. Determines which server field is required |
92
+ | `server` | `HTTPServer` | -- | Node.js HTTP server instance. **Required when `runtime` is `'node'`** |
93
+ | `engine` | `any` | -- | `@socket.io/bun-engine` Server instance. **Required when `runtime` is `'bun'`** |
94
+ | `serverOptions` | `Partial<ServerOptions>` | `{}` | Socket.IO `ServerOptions` (cors, path, transports, etc.) |
95
+ | `redisConnection` | `DefaultRedisHelper` | -- | **Required.** Redis helper used to create pub, sub, and emitter clients |
96
+ | `authenticateFn` | `TSocketIOAuthenticateFn` | -- | **Required.** Called with the client's handshake data. Return `true` to accept, `false` to reject |
97
+ | `validateRoomFn` | `TSocketIOValidateRoomFn` | `undefined` | Called when a client requests to join rooms. Return the allowed subset |
98
+ | `clientConnectedFn` | `TSocketIOClientConnectedFn` | `undefined` | Called after a client is fully authenticated and has joined default rooms |
99
+ | `defaultRooms` | `string[]` | `['io-default', 'io-notification']` | Rooms that every authenticated client joins automatically |
100
+ | `authenticateTimeout` | `number` | `10000` (10 s) | Milliseconds before an unauthenticated client is disconnected |
101
+ | `pingInterval` | `number` | `30000` (30 s) | Interval in milliseconds between heartbeat pings to authenticated clients |
102
+
103
+ > [!WARNING]
104
+ > 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`.
105
+
106
+ ### Client
107
+
108
+ `SocketIOClientHelper` connects to a Socket.IO server. Configuration is done entirely via the constructor -- `configure()` is called automatically.
109
+
110
+ ```typescript
111
+ import { SocketIOClientHelper } from '@venizia/ignis-helpers';
112
+
113
+ const socketClient = new SocketIOClientHelper({
114
+ identifier: 'my-client',
115
+ host: 'http://localhost:3000',
116
+ options: {
117
+ path: '/socket.io',
118
+ extraHeaders: {
119
+ Authorization: 'Bearer my-jwt-token',
120
+ },
121
+ },
122
+ onConnected: () => {
123
+ console.log('Connected to server');
124
+ socketClient.authenticate();
125
+ },
126
+ onDisconnected: (reason) => {
127
+ console.log('Disconnected:', reason);
128
+ },
129
+ onError: (error) => {
130
+ console.error('Connection error:', error);
131
+ },
132
+ onAuthenticated: () => {
133
+ console.log('Successfully authenticated');
134
+ },
135
+ onUnauthenticated: (message) => {
136
+ console.warn('Authentication failed:', message);
137
+ },
138
+ });
139
+ ```
140
+
141
+ #### `ISocketIOClientOptions`
142
+
143
+ | Option | Type | Default | Description |
144
+ |--------|------|---------|-------------|
145
+ | `identifier` | `string` | -- | Unique identifier for this client (used as logger scope) |
146
+ | `host` | `string` | -- | Server URL to connect to (e.g., `'http://localhost:3000'`) |
147
+ | `options` | `IOptions` | -- | Socket.IO client options (extends `SocketOptions` with `path` and `extraHeaders`) |
148
+ | `onConnected` | `() => ValueOrPromise<void>` | `undefined` | Called when the transport connection is established |
149
+ | `onDisconnected` | `(reason: string) => ValueOrPromise<void>` | `undefined` | Called when disconnected. The client state resets to `'unauthorized'` |
150
+ | `onError` | `(error: Error) => ValueOrPromise<void>` | `undefined` | Called on connection errors |
151
+ | `onAuthenticated` | `() => ValueOrPromise<void>` | `undefined` | Called when the server sends an `authenticated` event |
152
+ | `onUnauthenticated` | `(message: string) => ValueOrPromise<void>` | `undefined` | Called when the server rejects authentication |
153
+
154
+ #### `IOptions`
155
+
156
+ Extends `SocketOptions` from `socket.io-client`:
157
+
158
+ | Option | Type | Description |
159
+ |--------|------|-------------|
160
+ | `path` | `string` | Socket.IO server path (e.g., `'/socket.io'`) |
161
+ | `extraHeaders` | `Record<string \| symbol \| number, any>` | Additional headers sent with the connection request |
162
+
163
+ ## Usage
164
+
165
+ ### Server Setup
166
+
167
+ After constructing the server helper, call `configure()` to initialize the Socket.IO server, set up the Redis adapter, and start listening for connections.
168
+
169
+ ```typescript
170
+ const socketServer = new SocketIOServerHelper({
171
+ identifier: 'my-server',
172
+ runtime: 'node',
173
+ server: httpServer,
174
+ redisConnection: redisHelper,
175
+ serverOptions: { cors: { origin: '*' } },
176
+ authenticateFn: async (handshake) => {
177
+ return verifyToken(handshake.auth?.token);
178
+ },
179
+ });
180
+
181
+ await socketServer.configure();
182
+ // Server is now ready and listening for connections
183
+
184
+ httpServer.listen(3000, () => {
185
+ console.log('HTTP + Socket.IO server running on port 3000');
186
+ });
187
+ ```
188
+
189
+ #### Bun Runtime
190
+
191
+ For Bun, pass the `@socket.io/bun-engine` instance instead of an HTTP server:
192
+
193
+ ```typescript
194
+ import { SocketIOServerHelper } from '@venizia/ignis-helpers';
195
+
196
+ const socketServer = new SocketIOServerHelper({
197
+ identifier: 'my-bun-server',
198
+ runtime: 'bun',
199
+ engine: bunEngineInstance,
200
+ redisConnection: redisHelper,
201
+ serverOptions: {},
202
+ authenticateFn: async (handshake) => {
203
+ return verifyToken(handshake.auth?.token);
204
+ },
205
+ });
206
+
207
+ await socketServer.configure();
208
+ ```
209
+
210
+ ### Client Connection
211
+
212
+ The client connects automatically on construction. Call `authenticate()` after the connection is established to trigger the server-side authentication flow.
213
+
214
+ ```typescript
215
+ const client = new SocketIOClientHelper({
216
+ identifier: 'app-client',
217
+ host: 'http://localhost:3000',
218
+ options: {
219
+ path: '/socket.io',
220
+ extraHeaders: { Authorization: 'Bearer my-token' },
221
+ },
222
+ onConnected: () => {
223
+ // Connection established -- initiate authentication
224
+ client.authenticate();
225
+ },
226
+ onAuthenticated: () => {
227
+ // Now safe to subscribe and emit
228
+ client.joinRooms({ rooms: ['chat-room-1'] });
229
+ },
230
+ });
231
+ ```
232
+
233
+ ### Emitting Events
234
+
235
+ #### From the Server
236
+
237
+ Use `send()` to emit events through the Redis emitter. Messages can target a specific socket ID, a room, or broadcast to all clients.
238
+
239
+ ```typescript
240
+ // Send to a specific client
241
+ socketServer.send({
242
+ destination: 'client-socket-id',
243
+ payload: {
244
+ topic: 'notification',
245
+ data: { message: 'Hello!' },
246
+ },
247
+ });
248
+
249
+ // Broadcast to all connected clients (no destination)
250
+ socketServer.send({
251
+ payload: {
252
+ topic: 'announcement',
253
+ data: { message: 'Server update in 5 minutes' },
254
+ },
255
+ });
256
+
257
+ // Send with logging and callback
258
+ socketServer.send({
259
+ destination: 'some-room',
260
+ payload: {
261
+ topic: 'room-event',
262
+ data: { action: 'update' },
263
+ },
264
+ doLog: true,
265
+ cb: () => {
266
+ console.log('Message queued');
267
+ },
268
+ });
269
+ ```
270
+
271
+ #### From the Client
272
+
273
+ Use `emit()` to send events to the server.
274
+
275
+ ```typescript
276
+ client.emit({
277
+ topic: 'chat-message',
278
+ data: { text: 'Hello, world!' },
279
+ });
280
+
281
+ // With logging enabled
282
+ client.emit({
283
+ topic: 'user-action',
284
+ data: { action: 'click', target: 'button-1' },
285
+ doLog: true,
286
+ });
287
+
288
+ // With callback
289
+ client.emit({
290
+ topic: 'upload-complete',
291
+ data: { fileId: '123' },
292
+ cb: () => {
293
+ console.log('Emit completed');
294
+ },
295
+ });
296
+ ```
297
+
298
+ ### Listening for Events
299
+
300
+ #### Server-Side Event Binding
301
+
302
+ Use `on()` to register event handlers on the IO server instance.
303
+
304
+ ```typescript
305
+ socketServer.on({
306
+ topic: 'custom-event',
307
+ handler: (data) => {
308
+ console.log('Received:', data);
309
+ },
310
+ });
311
+ ```
312
+
313
+ #### Client-Side Event Subscription
314
+
315
+ Use `subscribe()` for single events and `subscribeMany()` for batch registration.
316
+
317
+ ```typescript
318
+ // Single event
319
+ client.subscribe({
320
+ event: 'notification',
321
+ handler: (data) => {
322
+ console.log('Notification:', data);
323
+ },
324
+ });
325
+
326
+ // Prevent duplicate handlers (default behavior)
327
+ client.subscribe({
328
+ event: 'notification',
329
+ handler: (data) => { /* ... */ },
330
+ ignoreDuplicate: true, // Default: true -- skips if handler already exists
331
+ });
332
+
333
+ // Allow multiple handlers for same event
334
+ client.subscribe({
335
+ event: 'chat-message',
336
+ handler: handler1,
337
+ ignoreDuplicate: false,
338
+ });
339
+
340
+ // Batch subscribe
341
+ client.subscribeMany({
342
+ events: {
343
+ 'user-joined': (data) => console.log('Joined:', data),
344
+ 'user-left': (data) => console.log('Left:', data),
345
+ 'typing': (data) => console.log('Typing:', data),
346
+ },
347
+ });
348
+
349
+ // Unsubscribe from a specific event (removes all handlers)
350
+ client.unsubscribe({ event: 'notification' });
351
+
352
+ // Unsubscribe from multiple events
353
+ client.unsubscribeMany({ events: ['user-joined', 'user-left'] });
354
+ ```
355
+
356
+ ### Rooms
357
+
358
+ #### Client-Side Room Operations
359
+
360
+ ```typescript
361
+ // Join rooms (validated by server's validateRoomFn)
362
+ client.joinRooms({ rooms: ['chat-room-1', 'notifications'] });
363
+
364
+ // Leave rooms
365
+ client.leaveRooms({ rooms: ['chat-room-1'] });
366
+ ```
367
+
368
+ #### Server-Side Room Behavior
369
+
370
+ 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.
371
+
372
+ ```typescript
373
+ const socketServer = new SocketIOServerHelper({
374
+ // ...
375
+ defaultRooms: ['general', 'announcements'],
376
+ validateRoomFn: async ({ socket, rooms }) => {
377
+ // Only allow rooms the user has permission for
378
+ const userPermissions = await getUserPermissions(socket.id);
379
+ return rooms.filter(room => userPermissions.includes(room));
380
+ },
381
+ });
382
+ ```
383
+
384
+ ### Authentication Flow
385
+
386
+ The server enforces a post-connection authentication protocol:
387
+
388
+ ```
389
+ Client connects
390
+ |
391
+ v
392
+ Server creates client entry (state: UNAUTHORIZED)
393
+ |-- Starts authenticateTimeout timer (default: 10s)
394
+ |-- Registers disconnect handler
395
+ |
396
+ Client emits 'authenticate' event
397
+ |
398
+ v
399
+ Server calls authenticateFn(handshake)
400
+ |
401
+ +-- Returns true:
402
+ | |-- State -> AUTHENTICATED
403
+ | |-- Clear auth timeout
404
+ | |-- Join default rooms
405
+ | |-- Start ping interval
406
+ | |-- Emit 'authenticated' to client
407
+ | +-- Invoke clientConnectedFn
408
+ |
409
+ +-- Returns false:
410
+ | |-- State -> UNAUTHORIZED
411
+ | |-- Emit 'unauthenticated' to client
412
+ | +-- Disconnect
413
+ |
414
+ +-- Timeout (no auth within authenticateTimeout):
415
+ +-- Disconnect
416
+ ```
417
+
418
+ ### Redis Adapter
419
+
420
+ 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:
421
+
422
+ - **redisPub** -- Publishes adapter messages
423
+ - **redisSub** -- Subscribes to adapter messages
424
+ - **redisEmitter** -- Powers `send()` for cross-instance message delivery
425
+
426
+ All three connections are initialized and awaited during `configure()`. If the parent client uses `lazyConnect`, the duplicated clients will connect automatically.
427
+
428
+ ```typescript
429
+ // Messages sent via send() use the Redis emitter,
430
+ // so they reach clients on ANY server instance
431
+ socketServer.send({
432
+ destination: 'some-room',
433
+ payload: {
434
+ topic: 'update',
435
+ data: { value: 42 },
436
+ },
437
+ });
438
+ ```
439
+
440
+ ### Graceful Shutdown
441
+
442
+ #### Server
443
+
444
+ ```typescript
445
+ await socketServer.shutdown();
446
+ // 1. Disconnects all clients (clears intervals and timeouts)
447
+ // 2. Closes the IO server
448
+ // 3. Quits all three Redis connections (pub, sub, emitter)
449
+ ```
450
+
451
+ #### Client
452
+
453
+ ```typescript
454
+ client.shutdown();
455
+ // 1. Removes all event listeners
456
+ // 2. Disconnects if connected
457
+ // 3. Resets state to 'unauthorized'
458
+ ```
459
+
460
+ ## Troubleshooting
461
+
462
+ ### `[SocketIOServerHelper] Invalid HTTP server for Node.js runtime!`
463
+
464
+ **Cause:** The `server` option is missing or falsy when `runtime` is `'node'`.
465
+
466
+ **Fix:** Pass a valid `http.Server` instance.
467
+
468
+ ### `[SocketIOServerHelper] Invalid @socket.io/bun-engine instance for Bun runtime!`
469
+
470
+ **Cause:** The `engine` option is missing or falsy when `runtime` is `'bun'`.
471
+
472
+ **Fix:** Pass a valid `@socket.io/bun-engine` Server instance.
473
+
474
+ ### `[SocketIOServerHelper] Unsupported runtime!`
475
+
476
+ **Cause:** The `runtime` value is neither `'node'` nor `'bun'`.
477
+
478
+ **Fix:** Use `RuntimeModules.NODE` or `RuntimeModules.BUN`.
479
+
480
+ ### `Invalid redis connection to config socket.io adapter!`
481
+
482
+ **Cause:** The `redisConnection` option is missing, `null`, or `undefined`.
483
+
484
+ **Fix:** Pass a valid `DefaultRedisHelper` instance.
485
+
486
+ ### `[on] Invalid topic to start binding handler`
487
+
488
+ **Cause:** An empty or falsy `topic` was passed to `on()`.
489
+
490
+ **Fix:** Provide a non-empty string topic.
491
+
492
+ ### `[on] IOServer is not initialized yet!`
493
+
494
+ **Cause:** `on()` was called before `configure()` completed.
495
+
496
+ **Fix:** Await `configure()` before registering event handlers.
497
+
498
+ ### `Invalid socket client state to emit`
499
+
500
+ **Cause (client):** `emit()` was called when the client is not connected.
501
+
502
+ **Fix:** Check that the client is connected before emitting, or emit inside the `onConnected` callback.
503
+
504
+ ### `Topic is required to emit`
505
+
506
+ **Cause (client):** `emit()` was called with an empty or falsy `topic`.
507
+
508
+ **Fix:** Provide a non-empty string topic.
509
+
510
+ ## See Also
511
+
512
+ - [API Reference](./api) -- Full method signatures, types, and constants
513
+ - [WebSocket Helper](../websocket/) -- Bun-native WebSocket alternative