@venizia/ignis-docs 0.2.0 → 0.2.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) 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 +22 -11
  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 +26 -2
  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 +6 -2
  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 +182 -93
  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 +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -1,64 +1,90 @@
1
- # Socket.IO -- API Reference
1
+ ---
2
+ title: Socket.IO - Full Reference
3
+ description: Complete reference for SocketIOServerHelper and SocketIOClientHelper - every option, method signature, type, constant, and error case
4
+ difficulty: intermediate
5
+ ---
2
6
 
3
- > Full method signatures, type definitions, and constants for `SocketIOServerHelper` and `SocketIOClientHelper`.
7
+ # Socket.IO - Full Reference
8
+
9
+ Exhaustive reference for `SocketIOServerHelper` and `SocketIOClientHelper`. For a readable introduction and the common tasks, start with the [Socket.IO overview](/extensions/helpers/socket-io/).
10
+
11
+ **Files:**
12
+
13
+ - [`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`
14
+ - [`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`
15
+ - [`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
16
+ - [`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`
17
+ - [`packages/helpers/src/modules/socket/socket-io/index.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/socket/socket-io/index.ts) - barrel export (`@venizia/ignis-helpers/socket-io`)
4
18
 
5
19
  ## Architecture
6
20
 
7
21
  ```
8
22
  SocketIOServerHelper (extends BaseHelper)
9
23
  |
10
- |-- configure()
11
- | |-- waitForRedisReady() x3 (pub, sub, emitter)
12
- | |-- initIOServer() (Node.js HTTPServer or Bun engine)
24
+ |-- constructor(opts)
25
+ | |-- setRuntime(opts) # validates `server` (Node) or `engine` (Bun)
26
+ | +-- initRedisClients(opts.redisConnection)
27
+ | |-- redisPub = duplicateClient()
28
+ | |-- redisSub = duplicateClient()
29
+ | +-- redisEmitter = duplicateClient()
30
+ |
31
+ |-- configure() [async]
32
+ | |-- ensureRedisClientsConnecting([redisPub, redisSub, redisEmitter])
33
+ | |-- await Promise.all(waitForRedisReady x3) # 30s timeout per client
34
+ | |-- initIOServer() # new IOServer(server, serverOptions) | new IOServer() + io.bind(engine)
13
35
  | |-- io.adapter(createAdapter(redisPub, redisSub))
14
- | |-- new Emitter(redisEmitter)
15
- | +-- io.on('connection', onClientConnect)
36
+ | |-- emitter = new Emitter(redisEmitter)
37
+ | +-- io.on('connection', socket => onClientConnect({ socket }))
16
38
  |
17
39
  |-- onClientConnect({ socket })
18
- | |-- Create ISocketIOClient (state: UNAUTHORIZED)
40
+ | |-- Create ISocketIOClient entry (state: UNAUTHORIZED)
19
41
  | |-- Start authenticateTimeout timer
20
42
  | |-- Register 'disconnect' handler
21
- | +-- Register 'authenticate' handler
22
- | |-- authenticateFn(handshake)
23
- | +-- onClientAuthenticated({ socket })
24
- | |-- State -> AUTHENTICATED
25
- | |-- Join defaultRooms
26
- | |-- Register room handlers (join/leave)
27
- | |-- Start ping interval
28
- | |-- Emit 'authenticated' to client
29
- | +-- Invoke clientConnectedFn
43
+ | +-- registerAuthHandler({ socket, handshake, clientId })
44
+ | +-- socket.on('authenticate', ...)
45
+ | |-- state -> AUTHENTICATING
46
+ | |-- authenticateFn(handshake)
47
+ | |-- true -> onClientAuthenticated({ socket })
48
+ | +-- false or throw -> emit 'unauthenticated', disconnect
30
49
  |
31
- |-- send({ destination?, payload, doLog?, cb? })
32
- | +-- emitter.compress(true).to(destination).emit(topic, data)
50
+ |-- onClientAuthenticated({ socket })
51
+ | |-- state -> AUTHENTICATED, clear authenticateTimeout
52
+ | |-- ping({ socket, doIgnoreAuth: true }) # immediate first ping
53
+ | |-- Join all defaultRooms
54
+ | |-- registerRoomHandlers({ socket, clientId }) # 'join' / 'leave'
55
+ | |-- interval = setInterval(ping, pingInterval)
56
+ | |-- send 'authenticated' to socket.id
57
+ | +-- invokeHook(clientConnectedFn)
33
58
  |
34
- +-- shutdown()
35
- |-- Disconnect all clients
36
- |-- io.close()
37
- +-- Quit Redis clients (pub, sub, emitter)
59
+ |-- send({ destination?, payload, doLog?, callback? })
60
+ | +-- emitter.compress(true).to(destination)?.emit(topic, data)
61
+ |
62
+ +-- shutdown() [async]
63
+ |-- Disconnect all tracked clients (clears their timers)
64
+ |-- close() # io.close()
65
+ +-- Promise.all([redisPub.quit(), redisSub.quit(), redisEmitter.quit()])
38
66
 
39
67
 
40
68
  SocketIOClientHelper (extends BaseHelper)
41
69
  |
42
70
  |-- constructor -> configure()
43
- | |-- io(host, options)
44
- | |-- Register 'connect' handler
45
- | |-- Register 'disconnect' handler
46
- | |-- Register 'connect_error' handler
47
- | |-- Register 'authenticated' handler
48
- | |-- Register 'unauthenticated' handler
49
- | +-- Register 'ping' handler
71
+ | |-- client = io(host, options)
72
+ | |-- Register 'connect' -> onConnected
73
+ | |-- Register 'disconnect' -> state UNAUTHORIZED, onDisconnected
74
+ | |-- Register 'connect_error' -> onError
75
+ | |-- Register 'authenticated' -> state AUTHENTICATED, onAuthenticated
76
+ | |-- Register 'unauthenticated' -> state UNAUTHORIZED, onUnauthenticated
77
+ | +-- Register 'ping' -> debug log
50
78
  |
51
- |-- authenticate()
52
- | +-- client.emit('authenticate')
53
- |
54
- |-- subscribe({ event, handler, ignoreDuplicate? })
55
- |-- emit({ topic, data, doLog?, cb? })
56
- |-- joinRooms({ rooms }) -> client.emit('join', { rooms })
79
+ |-- authenticate() -> state AUTHENTICATING, client.emit('authenticate')
80
+ |-- subscribe({ event, handler, ignoreDuplicate? }) # wraps handler, tracked in wrappedHandlers
81
+ |-- emit({ topic, data, doLog?, callback? })
82
+ |-- joinRooms({ rooms }) -> client.emit('join', { rooms })
57
83
  |-- leaveRooms({ rooms }) -> client.emit('leave', { rooms })
58
84
  |
59
85
  +-- shutdown()
60
86
  |-- client.removeAllListeners()
61
- |-- client.disconnect()
87
+ |-- disconnect() if connected
62
88
  +-- state -> UNAUTHORIZED
63
89
  ```
64
90
 
@@ -66,25 +92,33 @@ SocketIOClientHelper (extends BaseHelper)
66
92
 
67
93
  ### `SocketIOServerHelper`
68
94
 
69
- Extends `BaseHelper`. Manages a Socket.IO server with Redis adapter, authentication, room management, and heartbeat pings.
95
+ Extends `BaseHelper`. Manages a `socket.io` server with a Redis adapter, authentication, room management, and heartbeat pings.
70
96
 
71
97
  #### `constructor(opts: TSocketIOServerOptions)`
72
98
 
73
- Creates the server helper. Validates the runtime-specific server/engine and initializes three Redis client connections by duplicating the provided `redisConnection`.
99
+ Validates the runtime-specific `server` (Node) or `engine` (Bun) field and duplicates the provided `redisConnection` into three independent clients (`redisPub`, `redisSub`, `redisEmitter`). Does **not** start the IO server - call `configure()` to complete initialization.
74
100
 
75
- Does **not** start the IO server -- call `configure()` to complete initialization.
101
+ **Throws:**
102
+
103
+ - `'[SocketIOServerHelper] Invalid HTTP server for Node.js runtime!'` - `runtime: 'node'` and `server` is falsy
104
+ - `'[SocketIOServerHelper] Invalid @socket.io/bun-engine instance for Bun runtime!'` - `runtime: 'bun'` and `engine` is falsy
105
+ - `'[SocketIOServerHelper] Unsupported runtime!'` - `runtime` is neither `'node'` nor `'bun'`
106
+ - `'Invalid redis connection to config socket.io adapter!'` - `redisConnection` is falsy
76
107
 
77
108
  #### `configure(): Promise<void>`
78
109
 
79
- Initializes the Socket.IO server and sets up Redis infrastructure:
110
+ 1. Registers `error` listeners on all three duplicated Redis clients (logged, not thrown)
111
+ 2. Kicks off `connect()` on any client still in `'wait'` status (duplicated clients inherit `lazyConnect` from the parent and do not dial on their own)
112
+ 3. `await Promise.all([waitForRedisReady(redisPub), waitForRedisReady(redisSub), waitForRedisReady(redisEmitter)])`
113
+ 4. Creates the `IOServer` - `new IOServer(server, serverOptions)` for Node.js, or `new IOServer()` followed by `io.bind(engine)` for Bun
114
+ 5. Attaches the Redis adapter via `@socket.io/redis-adapter`
115
+ 6. Creates the Redis emitter via `@socket.io/redis-emitter`
116
+ 7. Registers the `'connection'` handler
80
117
 
81
- 1. Ensures all three Redis clients (pub, sub, emitter) are connected and ready
82
- 2. Creates the `IOServer` based on runtime (`new IOServer(httpServer, serverOptions)` for Node.js, or `new IOServer()` with `io.bind(engine)` for Bun)
83
- 3. Attaches the Redis adapter via `@socket.io/redis-adapter`
84
- 4. Creates a Redis emitter via `@socket.io/redis-emitter`
85
- 5. Registers the `'connection'` event handler
118
+ Must be called before `on()`, `send()`, or any server operation.
86
119
 
87
- Must be called before `on()`, `send()`, or any server operations.
120
+ > [!NOTE]
121
+ > `waitForRedisReady` rejects after **30 seconds** if a client never reaches `ready` (or immediately on that client's `error` event), so a broken Redis connection fails `configure()` instead of hanging boot indefinitely.
88
122
 
89
123
  #### `getIOServer(): IOServer`
90
124
 
@@ -92,144 +126,151 @@ Returns the underlying `socket.io` `Server` instance for direct access.
92
126
 
93
127
  ```typescript
94
128
  const io = socketServer.getIOServer();
95
- io.of('/admin').on('connection', (socket) => { /* ... */ });
129
+ io.of('/admin').on('connection', socket => {
130
+ /* ... */
131
+ });
96
132
  ```
97
133
 
98
134
  #### `getEngine(): any`
99
135
 
100
- Returns the Bun engine instance. Throws if the runtime is not `'bun'`.
136
+ Returns the Bun engine instance.
101
137
 
102
- ```typescript
103
- // Error: '[getEngine] Engine is only available for Bun runtime!'
104
- ```
138
+ **Throws:** `'[getEngine] Engine is only available for Bun runtime!'` - `runtime` is not `'bun'`
105
139
 
106
140
  #### `getClients(opts?: { id?: string }): ISocketIOClient | Map<string, ISocketIOClient> | undefined`
107
141
 
108
- Returns client information.
142
+ Without `id`, returns the full `Map<string, ISocketIOClient>`. With `{ id }`, returns that client's entry or `undefined`.
109
143
 
110
144
  ```typescript
111
- // Get all clients
112
145
  const allClients = socketServer.getClients() as Map<string, ISocketIOClient>;
113
-
114
- // Get a specific client by socket ID
115
146
  const client = socketServer.getClients({ id: 'socket-id' }) as ISocketIOClient | undefined;
116
147
  ```
117
148
 
118
- #### `on<HandlerArgsType, HandlerReturnType>(opts: { topic: string; handler: (...args: HandlerArgsType) => ValueOrPromise<HandlerReturnType> }): void`
149
+ #### `on<HandlerArgsType extends unknown[] = unknown[], HandlerReturnType = void>(opts: { topic: string; handler: (...args: HandlerArgsType) => ValueOrPromise<HandlerReturnType> }): void`
119
150
 
120
- Registers an event handler on the IO server instance.
151
+ Registers an event handler directly on the IO server instance (`io.on(topic, handler)`).
121
152
 
122
153
  ```typescript
123
154
  socketServer.on({
124
155
  topic: 'custom-event',
125
- handler: (data: { userId: string }) => {
126
- console.log('Received:', data);
127
- },
156
+ handler: (data: { userId: string }) => console.log('received:', data),
128
157
  });
129
158
  ```
130
159
 
131
160
  **Throws:**
132
- - `'[on] Invalid topic to start binding handler'` -- if `topic` is empty/falsy
133
- - `'[on] Invalid event handler | topic: {topic}'` -- if `handler` is missing
134
- - `'[on] IOServer is not initialized yet!'` -- if called before `configure()`
161
+
162
+ - `'[on] Invalid topic to start binding handler'` - `topic` is empty/falsy
163
+ - `'[on] Invalid event handler | topic: {topic}'` - `handler` is missing
164
+ - `'[on] IOServer is not initialized yet!'` - called before `configure()` completes
135
165
 
136
166
  #### `onClientConnect(opts: { socket: IOSocket }): void`
137
167
 
138
- Handles a new socket connection. Called automatically by the `'connection'` event. Can also be called manually.
168
+ Handles a new socket connection. Invoked automatically by the `'connection'` event; can also be called manually.
139
169
 
140
170
  1. Creates an `ISocketIOClient` entry with state `UNAUTHORIZED`
141
171
  2. Starts the `authenticateTimeout` timer
142
172
  3. Registers `'disconnect'` and `'authenticate'` handlers on the socket
143
173
 
144
- Returns early (no-op) if `socket` is falsy or the client ID already exists.
174
+ No-op if `socket` is falsy or the client ID already exists in the registry.
145
175
 
146
176
  #### `onClientAuthenticated(opts: { socket: IOSocket }): void`
147
177
 
148
- Called after successful authentication. Can also be called manually to programmatically authenticate a client.
178
+ Runs after successful authentication. Can also be called manually to authenticate a client programmatically.
179
+
180
+ 1. Sets state to `AUTHENTICATED`
181
+ 2. Clears and unsets the `authenticateTimeout` timer
182
+ 3. Sends an immediate ping (`doIgnoreAuth: true`)
183
+ 4. Joins all `defaultRooms`
184
+ 5. Registers room handlers (`'join'`, `'leave'`)
185
+ 6. Starts the periodic ping `interval`
186
+ 7. Emits `'authenticated'` to the client with `{ id, time }`
187
+ 8. Invokes `clientConnectedFn` - errors are caught and logged, never surfaced to the caller
149
188
 
150
- 1. Sets client state to `AUTHENTICATED`
151
- 2. Sends an initial ping
152
- 3. Joins all `defaultRooms`
153
- 4. Registers room handlers (`join`, `leave`)
154
- 5. Starts the periodic ping interval
155
- 6. Emits `'authenticated'` event to the client with `{ id, time }`
156
- 7. Invokes `clientConnectedFn` callback (errors caught and logged)
189
+ If the client entry no longer exists (disconnected mid-flow), logs and calls `disconnect({ socket })` instead.
157
190
 
158
191
  #### `ping(opts: { socket: IOSocket; doIgnoreAuth: boolean }): void`
159
192
 
160
- Sends a `'ping'` event to the client with `{ time: ISO string }`.
193
+ Emits `'ping'` to the client with `{ time: ISO string }`.
161
194
 
162
- - If `doIgnoreAuth` is `false` and the client is not in `AUTHENTICATED` state, the client is disconnected
163
- - If the socket or client is not found, returns silently
195
+ - If `doIgnoreAuth` is `false` and the client is not `AUTHENTICATED`, the client is disconnected instead of pinged
196
+ - No-op if the socket or the tracked client is not found
164
197
 
165
198
  #### `disconnect(opts: { socket: IOSocket }): void`
166
199
 
167
- Disconnects a client and cleans up internal state:
168
-
169
200
  1. Clears the ping `interval` timer
170
201
  2. Clears the `authenticateTimeout` timer
171
- 3. Removes the client from the `clients` map
202
+ 3. Removes the client from the registry
172
203
  4. Calls `socket.disconnect()`
173
204
 
174
- #### `send(opts: { destination?: string; payload: { topic: string; data: any }; doLog?: boolean; cb?: () => void }): void`
205
+ No-op if `socket` is falsy.
206
+
207
+ #### `send(opts: { destination?: string; payload: { topic: string; data: any }; doLog?: boolean; callback?: () => void }): void`
175
208
 
176
- Emits a message via the Redis emitter with compression enabled.
209
+ Emits through the Redis emitter with compression enabled (`emitter.compress(true)`), reaching clients on **any** server instance.
177
210
 
178
211
  | Parameter | Type | Description |
179
212
  |-----------|------|-------------|
180
- | `destination` | `string \| undefined` | Socket ID or room name. If omitted, broadcasts to all |
213
+ | `destination` | `string \| undefined` | Socket ID or room name. Omit to broadcast to all clients |
181
214
  | `payload.topic` | `string` | Event name |
182
215
  | `payload.data` | `any` | Event payload |
183
- | `doLog` | `boolean` | If `true`, logs the message details. Default: `false` |
184
- | `cb` | `() => void` | Callback invoked asynchronously via `setImmediate` after emission |
216
+ | `doLog` | `boolean` | Logs the message details. Default: `false` |
217
+ | `callback` | `() => void` | Invoked via `setImmediate` after the emit call returns |
185
218
 
186
- Returns early (no-op) if `payload`, `topic`, or `data` is falsy.
219
+ No-op if `payload`, `payload.topic`, or `payload.data` is falsy. Logs an error and returns if called before `configure()` (the Redis emitter does not exist yet).
187
220
 
188
- #### `shutdown(): Promise<void>`
221
+ ```typescript
222
+ socketServer.send({
223
+ destination: 'some-room',
224
+ payload: { topic: 'update', data: { value: 42 } },
225
+ callback: () => console.log('queued'),
226
+ });
227
+ ```
189
228
 
190
- Gracefully shuts down the server:
229
+ #### `shutdown(): Promise<void>`
191
230
 
192
- 1. Disconnects all tracked clients (clears their intervals and timeouts)
231
+ 1. Disconnects every tracked client, clearing its `interval` and `authenticateTimeout`
193
232
  2. Clears the `clients` map
194
- 3. Closes the IO server
195
- 4. Quits all three Redis connections (pub, sub, emitter)
233
+ 3. Closes the IO server (no-op if `configure()` never ran)
234
+ 4. `await Promise.all([redisPub.quit(), redisSub.quit(), redisEmitter.quit()])` - runs even if closing the IO server rejects, so the three Redis sockets never leak
196
235
 
197
236
  ## Client API
198
237
 
199
238
  ### `SocketIOClientHelper`
200
239
 
201
- Extends `BaseHelper`. Manages a Socket.IO client connection with authentication, event subscriptions, and room operations.
240
+ Extends `BaseHelper`. Manages a `socket.io-client` connection with authentication, event subscriptions, and room operations.
202
241
 
203
242
  #### `constructor(opts: ISocketIOClientOptions)`
204
243
 
205
- Creates and immediately configures the client. The constructor calls `configure()` internally, which establishes the connection and registers lifecycle event handlers.
244
+ Stores the callbacks and calls `configure()` internally - the client starts connecting immediately.
206
245
 
207
246
  #### `configure(): void`
208
247
 
209
- Initializes the `socket.io-client` connection and registers internal event handlers. Called automatically by the constructor. If called again when a client already exists, returns early (no-op).
248
+ Creates the `socket.io-client` connection (`io(host, options)`) and registers the internal lifecycle handlers below. Called automatically by the constructor; a second call is a no-op if a client instance already exists.
210
249
 
211
- Registered handlers:
212
- - `'connect'` -- invokes `onConnected` callback
213
- - `'disconnect'` -- resets state to `UNAUTHORIZED`, invokes `onDisconnected` callback
214
- - `'connect_error'` -- invokes `onError` callback
215
- - `'authenticated'` -- sets state to `AUTHENTICATED`, invokes `onAuthenticated` callback
216
- - `'unauthenticated'` -- resets state to `UNAUTHORIZED`, invokes `onUnauthenticated` callback
217
- - `'ping'` -- logs debug message
250
+ | Event | Behavior |
251
+ |-------|----------|
252
+ | `'connect'` | Invokes `onConnected` |
253
+ | `'disconnect'` | State -> `UNAUTHORIZED`, invokes `onDisconnected(reason)` |
254
+ | `'connect_error'` | Invokes `onError(error)` |
255
+ | `'authenticated'` | State -> `AUTHENTICATED`, invokes `onAuthenticated` |
256
+ | `'unauthenticated'` | State -> `UNAUTHORIZED`, invokes `onUnauthenticated(message)` |
257
+ | `'ping'` | Logs a debug message only |
258
+
259
+ Every callback invocation is wrapped so a rejected promise is caught and logged, never thrown.
218
260
 
219
261
  #### `getState(): TSocketIOClientState`
220
262
 
221
- Returns the current authentication state: `'unauthorized'`, `'authenticating'`, or `'authenticated'`.
263
+ Returns `'unauthorized'`, `'authenticating'`, or `'authenticated'`.
222
264
 
223
265
  ```typescript
224
- const state = client.getState();
225
- if (state === 'authenticated') {
266
+ if (client.getState() === 'authenticated') {
226
267
  client.emit({ topic: 'message', data: { text: 'hello' } });
227
268
  }
228
269
  ```
229
270
 
230
271
  #### `getSocketClient(): Socket`
231
272
 
232
- Returns the underlying `socket.io-client` `Socket` instance for direct access.
273
+ Returns the underlying `socket.io-client` `Socket` instance.
233
274
 
234
275
  ```typescript
235
276
  const rawSocket = client.getSocketClient();
@@ -238,143 +279,175 @@ rawSocket.io.opts.reconnection = false;
238
279
 
239
280
  #### `authenticate(): void`
240
281
 
241
- Initiates the authentication handshake by emitting the `'authenticate'` event to the server.
282
+ Emits `'authenticate'` to the server to start the handshake.
242
283
 
243
- - Does nothing if the client is not connected
244
- - Does nothing if the current state is not `'unauthorized'`
284
+ - No-op if the client is not connected
285
+ - No-op if the current state is not `'unauthorized'`
245
286
  - Sets state to `AUTHENTICATING` before emitting
246
287
 
247
288
  ```typescript
248
- // Typically called inside the onConnected callback
249
289
  const client = new SocketIOClientHelper({
250
290
  // ...
251
- onConnected: () => {
252
- client.authenticate();
253
- },
291
+ onConnected: () => client.authenticate(),
254
292
  });
255
293
  ```
256
294
 
257
295
  #### `subscribe<T>(opts: { event: string; handler: TSocketIOEventHandler<T>; ignoreDuplicate?: boolean }): void`
258
296
 
259
- Registers an event handler on the client socket. The handler is automatically wrapped with error handling (catches both sync throws and async rejections).
297
+ Registers a handler on the client socket. The handler is wrapped with error handling that catches both sync throws and async rejections, and the wrapper is tracked in an internal `wrappedHandlers` map so `unsubscribe()` can find and remove it.
260
298
 
261
299
  | Parameter | Type | Default | Description |
262
300
  |-----------|------|---------|-------------|
263
- | `event` | `string` | -- | Event name to listen for |
264
- | `handler` | `TSocketIOEventHandler<T>` | -- | Callback receiving the event data |
265
- | `ignoreDuplicate` | `boolean` | `true` | If `true` and a handler already exists for this event, skips registration |
301
+ | `event` | `string` | - | Event name to listen for |
302
+ | `handler` | `TSocketIOEventHandler<T>` | - | Callback receiving the event data |
303
+ | `ignoreDuplicate` | `boolean` | `true` | Skip registration if a handler already exists for this event |
266
304
 
267
305
  #### `subscribeMany(opts: { events: Record<string, TSocketIOEventHandler>; ignoreDuplicate?: boolean }): void`
268
306
 
269
- Batch-registers multiple event handlers. Calls `subscribe()` for each entry.
307
+ Calls `subscribe()` for each entry in `events`.
270
308
 
271
309
  ```typescript
272
310
  client.subscribeMany({
273
311
  events: {
274
- 'event-a': (data) => { /* ... */ },
275
- 'event-b': (data) => { /* ... */ },
312
+ 'user-joined': data => console.log('joined:', data),
313
+ 'user-left': data => console.log('left:', data),
276
314
  },
277
- ignoreDuplicate: false,
278
315
  });
279
316
  ```
280
317
 
281
318
  #### `unsubscribe(opts: { event: string; handler?: TSocketIOEventHandler }): void`
282
319
 
283
- Removes event handlers from the client socket.
284
-
285
- - If `handler` is provided, removes only that specific handler
286
- - If `handler` is omitted, removes **all** handlers for the event
287
- - If no listeners exist for the event, returns early (no-op)
320
+ - With `handler`, removes only that specific handler (looked up in `wrappedHandlers`; no-op if it was not registered through this helper)
321
+ - Without `handler`, removes **all** handlers for the event
322
+ - No-op if the event has no listeners
288
323
 
289
324
  #### `unsubscribeMany(opts: { events: string[] }): void`
290
325
 
291
- Batch-unsubscribes from multiple events. Removes all handlers for each event.
292
-
293
- ```typescript
294
- client.unsubscribeMany({ events: ['event-a', 'event-b'] });
295
- ```
326
+ Calls `unsubscribe()` for each event, removing all handlers for each.
296
327
 
297
328
  #### `connect(): void`
298
329
 
299
- Manually connects the client socket. Useful after a manual `disconnect()`.
300
-
301
- Returns early if the client instance does not exist.
330
+ Manually reconnects the client socket. No-op if the client instance does not exist (i.e. `configure()` never ran).
302
331
 
303
332
  #### `disconnect(): void`
304
333
 
305
- Manually disconnects the client socket without cleaning up listeners or resetting state.
306
-
307
- Returns early if the client instance does not exist.
334
+ Manually disconnects the client socket without removing listeners or resetting state. No-op if the client instance does not exist.
308
335
 
309
336
  > [!TIP]
310
- > Use `shutdown()` instead of `disconnect()` for a full cleanup that also removes listeners and resets the authentication state.
337
+ > Use `shutdown()` for a full cleanup that also removes listeners and resets the authentication state.
311
338
 
312
- #### `emit<T>(opts: { topic: string; data: T; doLog?: boolean; cb?: () => void }): void`
339
+ #### `emit<T>(opts: { topic: string; data: T; doLog?: boolean; callback?: () => void }): void`
313
340
 
314
341
  Emits an event to the server.
315
342
 
316
343
  | Parameter | Type | Default | Description |
317
344
  |-----------|------|---------|-------------|
318
- | `topic` | `string` | -- | Event name |
319
- | `data` | `T` | -- | Event payload |
320
- | `doLog` | `boolean` | `false` | If `true`, logs the emission details |
321
- | `cb` | `() => void` | `undefined` | Callback invoked asynchronously via `setImmediate` |
345
+ | `topic` | `string` | - | Event name |
346
+ | `data` | `T` | - | Event payload |
347
+ | `doLog` | `boolean` | `false` | Logs the emission details |
348
+ | `callback` | `() => void` | `undefined` | Invoked via `setImmediate` after the emit call |
322
349
 
323
350
  **Throws:**
324
- - `'Invalid socket client state to emit'` (status 400) -- if the client is not connected
325
- - `'Topic is required to emit'` (status 400) -- if `topic` is empty/falsy
326
351
 
327
- #### `joinRooms(opts: { rooms: string[] }): void`
352
+ - `'Invalid socket client state to emit'` (status 400) - the client is not connected
353
+ - `'Topic is required to emit'` (status 400) - `topic` is empty/falsy
328
354
 
329
- Requests to join rooms by emitting a `'join'` event to the server with `{ rooms }`. The server validates the request through its `validateRoomFn`.
355
+ #### `joinRooms(opts: { rooms: string[] }): void`
330
356
 
331
- Logs a warning and returns early if the client is not connected.
357
+ Emits `'join'` with `{ rooms }`; the server validates the request through its `validateRoomFn`. Logs a warning and no-ops if the client is not connected.
332
358
 
333
359
  #### `leaveRooms(opts: { rooms: string[] }): void`
334
360
 
335
- Requests to leave rooms by emitting a `'leave'` event to the server with `{ rooms }`.
361
+ Emits `'leave'` with `{ rooms }`. Logs a warning and no-ops if the client is not connected.
336
362
 
337
- Logs a warning and returns early if the client is not connected.
363
+ > [!NOTE]
364
+ > Unlike `joinRooms()`, leave requests are **not** validated server-side - the client can request to leave any room name, including ones it never joined (`socket.leave()` on a room the socket isn't in is a no-op).
338
365
 
339
366
  #### `shutdown(): void`
340
367
 
341
- Fully shuts down the client:
342
-
343
368
  1. Removes all event listeners (`removeAllListeners()`)
344
369
  2. Disconnects if currently connected
345
370
  3. Resets state to `UNAUTHORIZED`
346
371
 
372
+ ## Authentication Protocol
373
+
374
+ ```
375
+ Client connects (transport only)
376
+ |
377
+ v
378
+ Server creates client entry (state: UNAUTHORIZED)
379
+ |-- Starts authenticateTimeout timer (default: 10s)
380
+ |-- Registers 'disconnect' handler
381
+ |
382
+ Client emits 'authenticate'
383
+ |
384
+ v
385
+ State -> AUTHENTICATING
386
+ Server calls authenticateFn(handshake)
387
+ |
388
+ +-- Resolves true:
389
+ | |-- State -> AUTHENTICATED, authenticateTimeout cleared
390
+ | |-- Immediate ping, join defaultRooms, start ping interval
391
+ | |-- Emit 'authenticated' { id, time } to client
392
+ | +-- Invoke clientConnectedFn (errors caught and logged)
393
+ |
394
+ +-- Resolves false:
395
+ | |-- State -> UNAUTHORIZED
396
+ | |-- Emit 'unauthenticated' { message: 'Invalid token to authenticate! Please login again!', time }
397
+ | +-- Disconnect (via the emit callback)
398
+ |
399
+ +-- Throws / rejects:
400
+ | |-- State -> UNAUTHORIZED
401
+ | |-- Emit 'unauthenticated' { message: 'Failed to authenticate connection! Please login again!', time }
402
+ | +-- Disconnect (via the emit callback), logged with doLog: true
403
+ |
404
+ +-- authenticateTimeout elapses first:
405
+ +-- Disconnect - including a client whose authenticateFn is still pending;
406
+ its eventual resolution finds the client already removed and is a no-op
407
+ ```
408
+
409
+ A client that re-emits `'authenticate'` while already `AUTHENTICATING` or `AUTHENTICATED` is ignored (logged as a warning, `authenticateFn` is not called again).
410
+
411
+ ## Redis Adapter
412
+
413
+ 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`:
414
+
415
+ | Client | Purpose |
416
+ |--------|---------|
417
+ | `redisPub` | Publishes adapter messages (room membership, cross-instance socket.io internals) |
418
+ | `redisSub` | Subscribes to adapter messages |
419
+ | `redisEmitter` | Powers `send()` for cross-instance message delivery |
420
+
421
+ - **Initialized during `configure()`.** All three clients are created and awaited before the IO server starts.
422
+ - **Lazy connect handled explicitly.** If the parent `redisConnection` uses `lazyConnect` (`autoConnect: false`), the duplicated clients are kicked into `connect()` explicitly, since duplicated clients inherit `lazyConnect` but never dial on their own.
423
+
347
424
  ## Types Reference
348
425
 
349
426
  ### `IHandshake`
350
427
 
351
- Represents the client handshake data available during authentication.
352
-
353
428
  ```typescript
354
429
  interface IHandshake {
355
- headers: IncomingHttpHeaders; // HTTP headers from the initial request
356
- time: string; // Connection time as ISO string
357
- address: string; // Client IP address
358
- xdomain: boolean; // Whether the connection is cross-domain
359
- secure: boolean; // Whether the connection uses TLS
360
- issued: number; // Timestamp when the handshake was issued
361
- url: string; // Request URL
362
- query: ParsedUrlQuery; // Parsed query string parameters
363
- auth: { [key: string]: any }; // Authentication payload sent by the client
430
+ headers: IncomingHttpHeaders;
431
+ time: string;
432
+ address: string;
433
+ xdomain: boolean;
434
+ secure: boolean;
435
+ issued: number;
436
+ url: string;
437
+ query: ParsedUrlQuery;
438
+ auth: { [key: string]: any };
364
439
  }
365
440
  ```
366
441
 
367
442
  ### `ISocketIOClient`
368
443
 
369
- Internal representation of a connected client tracked by the server.
370
-
371
444
  ```typescript
372
445
  interface ISocketIOClient {
373
- id: string; // Socket ID
374
- socket: IOSocket; // The socket.io Socket instance
375
- state: TSocketIOClientState; // 'unauthorized' | 'authenticating' | 'authenticated'
376
- interval?: NodeJS.Timeout; // Ping interval timer (set after authentication)
377
- authenticateTimeout: NodeJS.Timeout; // Auth timeout timer (cleared after authentication)
446
+ id: string;
447
+ socket: IOSocket;
448
+ state: TSocketIOClientState;
449
+ interval?: NodeJS.Timeout; // Ping interval timer, set after authentication
450
+ authenticateTimeout?: NodeJS.Timeout; // Cleared (set to undefined) after authentication
378
451
  }
379
452
  ```
380
453
 
@@ -386,44 +459,31 @@ type TSocketIOClientState = 'unauthorized' | 'authenticating' | 'authenticated';
386
459
 
387
460
  ### `TSocketIOAuthenticateFn`
388
461
 
389
- Server-side authentication callback. Receives the handshake data and returns a boolean indicating whether to accept or reject the connection.
390
-
391
462
  ```typescript
392
463
  type TSocketIOAuthenticateFn = (args: IHandshake) => ValueOrPromise<boolean>;
393
464
  ```
394
465
 
395
466
  ### `TSocketIOValidateRoomFn`
396
467
 
397
- Server-side room validation callback. Receives the socket and requested rooms, returns the subset of rooms the client is allowed to join.
398
-
399
468
  ```typescript
400
- type TSocketIOValidateRoomFn = (opts: {
401
- socket: IOSocket;
402
- rooms: string[];
403
- }) => ValueOrPromise<string[]>;
469
+ type TSocketIOValidateRoomFn = (opts: { socket: IOSocket; rooms: string[] }) => ValueOrPromise<string[]>;
404
470
  ```
405
471
 
406
472
  ### `TSocketIOClientConnectedFn`
407
473
 
408
- Server-side callback invoked after a client is fully authenticated and has joined default rooms.
409
-
410
474
  ```typescript
411
- type TSocketIOClientConnectedFn = (opts: {
412
- socket: IOSocket;
413
- }) => ValueOrPromise<void>;
475
+ type TSocketIOClientConnectedFn = (opts: { socket: IOSocket }) => ValueOrPromise<void>;
414
476
  ```
415
477
 
416
478
  ### `TSocketIOEventHandler<T>`
417
479
 
418
- Client-side event handler type.
419
-
420
480
  ```typescript
421
481
  type TSocketIOEventHandler<T = unknown> = (data: T) => ValueOrPromise<void>;
422
482
  ```
423
483
 
424
484
  ### `IOptions`
425
485
 
426
- Client connection options. Extends `SocketOptions` from `socket.io-client`.
486
+ Client connection options. Extends `SocketOptions` from `socket.io-client`; `path` and `extraHeaders` are required by the type.
427
487
 
428
488
  ```typescript
429
489
  interface IOptions extends SocketOptions {
@@ -432,24 +492,53 @@ interface IOptions extends SocketOptions {
432
492
  }
433
493
  ```
434
494
 
495
+ ### `ISocketIOClientOptions`
496
+
497
+ | Option | Type | Default | Description |
498
+ |--------|------|---------|-------------|
499
+ | `identifier` | `string` | - | Unique identifier for this client (used as logger scope) |
500
+ | `host` | `string` | - | Server URL to connect to (e.g. `'http://localhost:3000'`) |
501
+ | `options` | `IOptions` | - | Socket.IO client options |
502
+ | `onConnected` | `() => ValueOrPromise<void>` | `undefined` | Called when the transport connection is established |
503
+ | `onDisconnected` | `(reason: string) => ValueOrPromise<void>` | `undefined` | Called on disconnect; state resets to `'unauthorized'` first |
504
+ | `onError` | `(error: Error) => ValueOrPromise<void>` | `undefined` | Called on `'connect_error'` |
505
+ | `onAuthenticated` | `() => ValueOrPromise<void>` | `undefined` | Called when the server confirms authentication |
506
+ | `onUnauthenticated` | `(message: string) => ValueOrPromise<void>` | `undefined` | Called when the server rejects authentication |
507
+
435
508
  ### `TSocketIOServerOptions`
436
509
 
437
- Discriminated union for server constructor options:
510
+ Discriminated union on `runtime`.
438
511
 
439
512
  ```typescript
440
513
  type TSocketIOServerOptions = ISocketIOServerNodeOptions | ISocketIOServerBunOptions;
441
514
 
515
+ interface ISocketIOServerBaseOptions {
516
+ identifier: string;
517
+ serverOptions: Partial<ServerOptions>; // socket.io ServerOptions - required by the type, pass {} if unused
518
+ redisConnection: IRedisHelper;
519
+ defaultRooms?: string[]; // Default: ['io-default', 'io-notification']
520
+ authenticateTimeout?: number; // Default: 10000 (10s)
521
+ pingInterval?: number; // Default: 30000 (30s)
522
+
523
+ authenticateFn: TSocketIOAuthenticateFn;
524
+ validateRoomFn?: TSocketIOValidateRoomFn;
525
+ clientConnectedFn?: TSocketIOClientConnectedFn;
526
+ }
527
+
442
528
  interface ISocketIOServerNodeOptions extends ISocketIOServerBaseOptions {
443
529
  runtime: 'node';
444
- server: HTTPServer;
530
+ server: HTTPServer; // node:http Server
445
531
  }
446
532
 
447
533
  interface ISocketIOServerBunOptions extends ISocketIOServerBaseOptions {
448
534
  runtime: 'bun';
449
- engine: any;
535
+ engine: any; // @socket.io/bun-engine Server instance - typed `any` since it's an optional peer dep
450
536
  }
451
537
  ```
452
538
 
539
+ > [!WARNING]
540
+ > If no `validateRoomFn` is provided, **all** custom room join requests are rejected with a warning log. Clients still get `defaultRooms` automatically.
541
+
453
542
  ## Constants
454
543
 
455
544
  ### `SocketIOConstants`
@@ -458,12 +547,12 @@ interface ISocketIOServerBunOptions extends ISocketIOServerBaseOptions {
458
547
  |----------|-------|-------------|
459
548
  | `EVENT_PING` | `'ping'` | Heartbeat event emitted by the server at `pingInterval` |
460
549
  | `EVENT_CONNECT` | `'connection'` | Server-side connection event |
461
- | `EVENT_DISCONNECT` | `'disconnect'` | Disconnect event (both server and client) |
550
+ | `EVENT_DISCONNECT` | `'disconnect'` | Disconnect event (server and client) |
462
551
  | `EVENT_JOIN` | `'join'` | Room join request event |
463
552
  | `EVENT_LEAVE` | `'leave'` | Room leave request event |
464
- | `EVENT_AUTHENTICATE` | `'authenticate'` | Client-to-server authentication request |
465
- | `EVENT_AUTHENTICATED` | `'authenticated'` | Server-to-client authentication success |
466
- | `EVENT_UNAUTHENTICATE` | `'unauthenticated'` | Server-to-client authentication failure |
553
+ | `EVENT_AUTHENTICATE` | `'authenticate'` | Client -> server authentication request |
554
+ | `EVENT_AUTHENTICATED` | `'authenticated'` | Server -> client authentication success |
555
+ | `EVENT_UNAUTHENTICATE` | `'unauthenticated'` | Server -> client authentication failure |
467
556
  | `ROOM_DEFAULT` | `'io-default'` | Default room name |
468
557
  | `ROOM_NOTIFICATION` | `'io-notification'` | Default notification room name |
469
558
 
@@ -475,23 +564,36 @@ interface ISocketIOServerBunOptions extends ISocketIOServerBaseOptions {
475
564
  | `AUTHENTICATING` | `'authenticating'` | Authentication in progress |
476
565
  | `AUTHENTICATED` | `'authenticated'` | Successfully authenticated |
477
566
 
478
- #### `SocketIOClientStates.isValid(input: string): boolean`
479
-
480
- Static method that checks whether a string is a valid client state value.
567
+ `SocketIOClientStates.isValid(input: string): input is TConstValue<typeof SocketIOClientStates>` - checks membership against the backing `SCHEME_SET`.
481
568
 
482
569
  ```typescript
483
570
  SocketIOClientStates.isValid('authenticated'); // true
484
571
  SocketIOClientStates.isValid('invalid'); // false
485
572
  ```
486
573
 
487
- ### Internal Defaults
574
+ ### Internal defaults
488
575
 
489
576
  | Constant | Value | Description |
490
577
  |----------|-------|-------------|
491
- | `CLIENT_AUTHENTICATE_TIMEOUT` | `10000` (10 s) | Default timeout before disconnecting unauthenticated clients |
492
- | `CLIENT_PING_INTERVAL` | `30000` (30 s) | Default interval between heartbeat pings |
493
-
494
- ## See Also
495
-
496
- - [Setup & Usage](./) -- Getting started, constructor options, and examples
497
- - [WebSocket Helper](../websocket/) -- Bun-native WebSocket alternative
578
+ | `CLIENT_AUTHENTICATE_TIMEOUT` | `10_000` (10s) | Default `authenticateTimeout` |
579
+ | `CLIENT_PING_INTERVAL` | `30_000` (30s) | Default `pingInterval` |
580
+
581
+ ## Common Errors
582
+
583
+ | Error | Cause | Fix |
584
+ |-------|-------|-----|
585
+ | `[SocketIOServerHelper] Invalid HTTP server for Node.js runtime!` | `server` missing/falsy with `runtime: 'node'` | Pass a valid `http.Server` instance |
586
+ | `[SocketIOServerHelper] Invalid @socket.io/bun-engine instance for Bun runtime!` | `engine` missing/falsy with `runtime: 'bun'` | Pass a valid `@socket.io/bun-engine` `Server` instance |
587
+ | `[SocketIOServerHelper] Unsupported runtime!` | `runtime` is neither `'node'` nor `'bun'` | Use `RuntimeModules.NODE`, `RuntimeModules.BUN`, or `RuntimeModules.detect()` |
588
+ | `Invalid redis connection to config socket.io adapter!` | `redisConnection` missing, `null`, or `undefined` | Pass a valid `IRedisHelper` (e.g. `RedisSingleHelper`, `RedisClusterHelper`) |
589
+ | `[on] Invalid topic to start binding handler` | Empty/falsy `topic` passed to `on()` | Provide a non-empty string topic |
590
+ | `[on] IOServer is not initialized yet!` | `on()` called before `configure()` completed | `await configure()` before registering handlers |
591
+ | `Invalid socket client state to emit` (client) | `emit()` called while not connected | Check `getState()` / connection status, or emit inside `onConnected` |
592
+ | `Topic is required to emit` (client) | `emit()` called with an empty/falsy `topic` | Provide a non-empty string topic |
593
+
594
+ ## See also
595
+
596
+ - [Socket.IO overview](/extensions/helpers/socket-io/) - getting started, constructor options, and common tasks
597
+ - [Socket.IO Component](/extensions/components/socket-io/) - DI-managed lifecycle wrapper around this helper
598
+ - [WebSocket Helper](../websocket/) - Bun-native alternative with no `socket.io` dependency
599
+ - [Redis Helper](/extensions/helpers/redis/reference) - `duplicateClient()` and the topology classes used as `redisConnection`