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