@venizia/ignis-docs 0.0.5 → 0.0.6-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/package.json +1 -1
- package/wiki/best-practices/architectural-patterns.md +0 -2
- package/wiki/best-practices/architecture-decisions.md +0 -8
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/code-style-standards/index.md +0 -1
- package/wiki/best-practices/code-style-standards/tooling.md +0 -3
- package/wiki/best-practices/contribution-workflow.md +12 -12
- package/wiki/best-practices/index.md +4 -14
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/guides/core-concepts/application/bootstrapping.md +6 -7
- package/wiki/guides/core-concepts/components-guide.md +1 -1
- package/wiki/guides/core-concepts/components.md +2 -2
- package/wiki/guides/core-concepts/dependency-injection.md +4 -5
- package/wiki/guides/core-concepts/persistent/datasources.md +4 -5
- package/wiki/guides/core-concepts/services.md +1 -1
- package/wiki/guides/get-started/5-minute-quickstart.md +4 -5
- package/wiki/guides/get-started/philosophy.md +12 -24
- package/wiki/guides/index.md +2 -9
- package/wiki/guides/reference/mcp-docs-server.md +13 -13
- package/wiki/guides/tutorials/building-a-crud-api.md +10 -10
- package/wiki/guides/tutorials/complete-installation.md +11 -12
- package/wiki/guides/tutorials/ecommerce-api.md +3 -3
- package/wiki/guides/tutorials/realtime-chat.md +6 -6
- package/wiki/guides/tutorials/testing.md +4 -5
- package/wiki/index.md +8 -14
- package/wiki/references/base/bootstrapping.md +0 -3
- package/wiki/references/base/components.md +2 -2
- package/wiki/references/base/controllers.md +0 -1
- package/wiki/references/base/datasources.md +1 -1
- package/wiki/references/base/dependency-injection.md +2 -2
- package/wiki/references/base/filter-system/default-filter.md +2 -3
- package/wiki/references/base/filter-system/index.md +1 -1
- package/wiki/references/base/filter-system/quick-reference.md +0 -14
- package/wiki/references/base/middlewares.md +0 -8
- package/wiki/references/base/providers.md +0 -9
- package/wiki/references/base/repositories/advanced.md +1 -1
- package/wiki/references/base/repositories/mixins.md +2 -3
- package/wiki/references/base/services.md +0 -1
- package/wiki/references/components/authentication/api.md +444 -0
- package/wiki/references/components/authentication/errors.md +177 -0
- package/wiki/references/components/authentication/index.md +571 -0
- package/wiki/references/components/authentication/usage.md +781 -0
- package/wiki/references/components/health-check.md +292 -103
- package/wiki/references/components/index.md +14 -12
- package/wiki/references/components/mail/api.md +505 -0
- package/wiki/references/components/mail/errors.md +176 -0
- package/wiki/references/components/mail/index.md +535 -0
- package/wiki/references/components/mail/usage.md +404 -0
- package/wiki/references/components/request-tracker.md +229 -25
- package/wiki/references/components/socket-io/api.md +1051 -0
- package/wiki/references/components/socket-io/errors.md +119 -0
- package/wiki/references/components/socket-io/index.md +410 -0
- package/wiki/references/components/socket-io/usage.md +322 -0
- package/wiki/references/components/static-asset/api.md +261 -0
- package/wiki/references/components/static-asset/errors.md +89 -0
- package/wiki/references/components/static-asset/index.md +617 -0
- package/wiki/references/components/static-asset/usage.md +364 -0
- package/wiki/references/components/swagger.md +390 -110
- package/wiki/references/components/template/api-page.md +125 -0
- package/wiki/references/components/template/errors-page.md +100 -0
- package/wiki/references/components/template/index.md +104 -0
- package/wiki/references/components/template/setup-page.md +134 -0
- package/wiki/references/components/template/single-page.md +132 -0
- package/wiki/references/components/template/usage-page.md +127 -0
- package/wiki/references/components/websocket/api.md +508 -0
- package/wiki/references/components/websocket/errors.md +123 -0
- package/wiki/references/components/websocket/index.md +453 -0
- package/wiki/references/components/websocket/usage.md +475 -0
- package/wiki/references/helpers/cron/index.md +224 -0
- package/wiki/references/helpers/crypto/index.md +537 -0
- package/wiki/references/helpers/env/index.md +214 -0
- package/wiki/references/helpers/error/index.md +232 -0
- package/wiki/references/helpers/index.md +16 -15
- package/wiki/references/helpers/inversion/index.md +608 -0
- package/wiki/references/helpers/logger/index.md +600 -0
- package/wiki/references/helpers/network/api.md +986 -0
- package/wiki/references/helpers/network/index.md +620 -0
- package/wiki/references/helpers/queue/index.md +589 -0
- package/wiki/references/helpers/redis/index.md +495 -0
- package/wiki/references/helpers/socket-io/api.md +497 -0
- package/wiki/references/helpers/socket-io/index.md +513 -0
- package/wiki/references/helpers/storage/api.md +705 -0
- package/wiki/references/helpers/storage/index.md +583 -0
- package/wiki/references/helpers/template/index.md +66 -0
- package/wiki/references/helpers/template/single-page.md +126 -0
- package/wiki/references/helpers/testing/index.md +510 -0
- package/wiki/references/helpers/types/index.md +512 -0
- package/wiki/references/helpers/uid/index.md +272 -0
- package/wiki/references/helpers/websocket/api.md +736 -0
- package/wiki/references/helpers/websocket/index.md +574 -0
- package/wiki/references/helpers/worker-thread/index.md +470 -0
- package/wiki/references/index.md +2 -9
- package/wiki/references/quick-reference.md +3 -18
- package/wiki/references/utilities/jsx.md +1 -8
- package/wiki/references/utilities/statuses.md +0 -7
- package/wiki/references/components/authentication.md +0 -476
- package/wiki/references/components/mail.md +0 -687
- package/wiki/references/components/socket-io.md +0 -562
- package/wiki/references/components/static-asset.md +0 -1277
- package/wiki/references/helpers/cron.md +0 -108
- package/wiki/references/helpers/crypto.md +0 -132
- package/wiki/references/helpers/env.md +0 -83
- package/wiki/references/helpers/error.md +0 -97
- package/wiki/references/helpers/inversion.md +0 -176
- package/wiki/references/helpers/logger.md +0 -296
- package/wiki/references/helpers/network.md +0 -396
- package/wiki/references/helpers/queue.md +0 -150
- package/wiki/references/helpers/redis.md +0 -142
- package/wiki/references/helpers/socket-io.md +0 -932
- package/wiki/references/helpers/storage.md +0 -665
- package/wiki/references/helpers/testing.md +0 -133
- package/wiki/references/helpers/types.md +0 -167
- package/wiki/references/helpers/uid.md +0 -167
- package/wiki/references/helpers/worker-thread.md +0 -178
- package/wiki/references/src-details/boot.md +0 -379
- package/wiki/references/src-details/core.md +0 -263
- package/wiki/references/src-details/dev-configs.md +0 -298
- package/wiki/references/src-details/docs.md +0 -71
- package/wiki/references/src-details/helpers.md +0 -211
- package/wiki/references/src-details/index.md +0 -86
- package/wiki/references/src-details/inversion.md +0 -340
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Socket.IO -- Error Reference
|
|
2
|
+
|
|
3
|
+
> Error conditions, failure messages, and troubleshooting for the Socket.IO component, server helper, and client helper.
|
|
4
|
+
|
|
5
|
+
## Error Conditions
|
|
6
|
+
|
|
7
|
+
### Component Errors
|
|
8
|
+
|
|
9
|
+
| Method | Condition | Error Message |
|
|
10
|
+
|--------|-----------|---------------|
|
|
11
|
+
| `binding()` | `application` is falsy | `"[binding] Invalid application to bind SocketIOComponent"` |
|
|
12
|
+
| `binding()` | Unsupported runtime | `"[SocketIOComponent] Unsupported runtime: <runtime>"` |
|
|
13
|
+
| `resolveBindings()` | `REDIS_CONNECTION` not instanceof `DefaultRedisHelper` | `"Invalid instance of redisConnection | Please init connection with RedisHelper for single redis connection or RedisClusterHelper for redis cluster mode!"` |
|
|
14
|
+
| `resolveBindings()` | `AUTHENTICATE_HANDLER` is falsy | `"[DANGER][SocketIOComponent] Invalid authenticateFn to setup io socket server!"` |
|
|
15
|
+
| `registerNodeHook()` | HTTP server not available | `"[SocketIOComponent] HTTP server not available for Node.js runtime!"` |
|
|
16
|
+
|
|
17
|
+
### Server Helper Errors
|
|
18
|
+
|
|
19
|
+
| Method | Condition | Error Message |
|
|
20
|
+
|--------|-----------|---------------|
|
|
21
|
+
| `setRuntime()` | Node.js runtime, `server` missing | `"[SocketIOServerHelper] Invalid HTTP server for Node.js runtime!"` |
|
|
22
|
+
| `setRuntime()` | Bun runtime, `engine` missing | `"[SocketIOServerHelper] Invalid @socket.io/bun-engine instance for Bun runtime!"` |
|
|
23
|
+
| `setRuntime()` | Unknown runtime | `"[SocketIOServerHelper] Unsupported runtime!"` |
|
|
24
|
+
| `initRedisClients()` | `redisConnection` is falsy | `"Invalid redis connection to config socket.io adapter!"` |
|
|
25
|
+
| `initIOServer()` | Node.js runtime, `server` missing at configure time | `"[DANGER] Invalid HTTP server instance to init Socket.io server!"` |
|
|
26
|
+
| `initIOServer()` | Bun runtime, `engine` missing at configure time | `"[DANGER] Invalid @socket.io/bun-engine instance to init Socket.io server!"` |
|
|
27
|
+
| `initIOServer()` | Unknown runtime at configure time | `"[configure] Unsupported runtime: <runtime>"` |
|
|
28
|
+
| `getEngine()` | Runtime is not Bun | `"[getEngine] Engine is only available for Bun runtime!"` |
|
|
29
|
+
| `on()` | `topic` is empty | `"[on] Invalid topic to start binding handler"` |
|
|
30
|
+
| `on()` | `handler` is falsy | `"[on] Invalid event handler | topic: <topic>"` |
|
|
31
|
+
| `on()` | IO server not initialized | `"[on] IOServer is not initialized yet!"` |
|
|
32
|
+
|
|
33
|
+
### Client Helper Errors
|
|
34
|
+
|
|
35
|
+
| Method | Condition | Error Message |
|
|
36
|
+
|--------|-----------|---------------|
|
|
37
|
+
| `emit()` | Socket not connected | `"Invalid socket client state to emit"` (statusCode: 400) |
|
|
38
|
+
| `emit()` | `topic` is falsy | `"Topic is required to emit"` (statusCode: 400) |
|
|
39
|
+
|
|
40
|
+
### Server Authentication Errors (sent to client)
|
|
41
|
+
|
|
42
|
+
| Condition | Event | Message |
|
|
43
|
+
|-----------|-------|---------|
|
|
44
|
+
| `authenticateFn` returned `false` | `unauthenticated` | `"Invalid token to authenticate! Please login again!"` |
|
|
45
|
+
| `authenticateFn` threw an error | `unauthenticated` | `"Failed to authenticate connection! Please login again!"` |
|
|
46
|
+
|
|
47
|
+
## Troubleshooting
|
|
48
|
+
|
|
49
|
+
### "SocketIO not initialized"
|
|
50
|
+
|
|
51
|
+
**Cause**: You're trying to use `SocketIOServerHelper` before the server has started (e.g., during DI construction).
|
|
52
|
+
|
|
53
|
+
**Fix**: Use the lazy getter pattern shown in the [Usage & Examples](./usage) page. Never `@inject` `SOCKET_IO_INSTANCE` directly in a constructor -- it doesn't exist yet at construction time.
|
|
54
|
+
|
|
55
|
+
### "Invalid instance of redisConnection"
|
|
56
|
+
|
|
57
|
+
**Cause**: The value bound to `REDIS_CONNECTION` is not an instance of `DefaultRedisHelper` (or its subclasses `RedisHelper` / `RedisClusterHelper`).
|
|
58
|
+
|
|
59
|
+
**Fix**: Use `RedisHelper` (single instance) or `RedisClusterHelper` (cluster mode):
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Correct -- single instance
|
|
63
|
+
this.bind({ key: SocketIOBindingKeys.REDIS_CONNECTION })
|
|
64
|
+
.toValue(new RedisHelper({ name: 'socket-io', host, port, password }));
|
|
65
|
+
|
|
66
|
+
// Correct -- cluster mode
|
|
67
|
+
this.bind({ key: SocketIOBindingKeys.REDIS_CONNECTION })
|
|
68
|
+
.toValue(new RedisClusterHelper({ name: 'socket-io', nodes, password }));
|
|
69
|
+
|
|
70
|
+
// Wrong -- raw ioredis client
|
|
71
|
+
this.bind({ key: SocketIOBindingKeys.REDIS_CONNECTION })
|
|
72
|
+
.toValue(new Redis(6379)); // This is NOT a DefaultRedisHelper!
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### "Cannot find module '@socket.io/bun-engine'"
|
|
76
|
+
|
|
77
|
+
**Cause**: Running on Bun runtime without the optional peer dependency installed.
|
|
78
|
+
|
|
79
|
+
**Fix**: `bun add @socket.io/bun-engine`
|
|
80
|
+
|
|
81
|
+
### Socket.IO connects but events aren't received
|
|
82
|
+
|
|
83
|
+
**Cause**: Clients must emit `authenticate` after connecting. Unauthenticated clients are disconnected after the timeout (default: 10 seconds).
|
|
84
|
+
|
|
85
|
+
**Fix**: Ensure your client emits the authenticate event:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
socket.on('connect', () => {
|
|
89
|
+
socket.emit('authenticate');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
socket.on('authenticated', (data) => {
|
|
93
|
+
// Now ready to send/receive events
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### "Invalid socket client state to emit"
|
|
98
|
+
|
|
99
|
+
**Cause**: Calling `emit()` on `SocketIOClientHelper` when the socket is not connected.
|
|
100
|
+
|
|
101
|
+
**Fix**: Ensure the socket is connected before emitting. Check `client.getSocketClient().connected` or wait for the `onConnected` callback.
|
|
102
|
+
|
|
103
|
+
### Client disconnects immediately after connecting
|
|
104
|
+
|
|
105
|
+
**Cause**: The authentication timeout expired (default: 10 seconds). The client connected but did not emit `authenticate` in time.
|
|
106
|
+
|
|
107
|
+
**Fix**: Emit `authenticate` immediately on connect, or increase the `authenticateTimeout` in the server helper options.
|
|
108
|
+
|
|
109
|
+
### Room join requests are silently rejected
|
|
110
|
+
|
|
111
|
+
**Cause**: No `validateRoomFn` is bound. Without a validation function, all room join requests are rejected by design (security-by-default).
|
|
112
|
+
|
|
113
|
+
**Fix**: Bind a `VALIDATE_ROOM_HANDLER` that returns the list of allowed rooms.
|
|
114
|
+
|
|
115
|
+
## See Also
|
|
116
|
+
|
|
117
|
+
- [Setup & Configuration](./) -- Quick reference, installation, bindings, constants
|
|
118
|
+
- [Usage & Examples](./usage) -- Server-side usage, client helper, advanced patterns
|
|
119
|
+
- [API Reference](./api) -- Architecture, method signatures, internals, types
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# Socket.IO -- Setup & Configuration
|
|
2
|
+
|
|
3
|
+
> Real-time, bidirectional, event-based communication using Socket.IO -- with automatic runtime detection for both Node.js and Bun.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Item | Value |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| **Package** | `@venizia/ignis` (core) |
|
|
10
|
+
| **Class** | `SocketIOComponent` |
|
|
11
|
+
| **Server Helper** | [`SocketIOServerHelper`](/references/helpers/socket-io/) |
|
|
12
|
+
| **Client Helper** | [`SocketIOClientHelper`](/references/helpers/socket-io/) |
|
|
13
|
+
| **Runtimes** | Node.js (`@hono/node-server`) and Bun (native) |
|
|
14
|
+
| **Scaling** | `@socket.io/redis-adapter` + `@socket.io/redis-emitter` |
|
|
15
|
+
|
|
16
|
+
#### Import Paths
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
SocketIOComponent,
|
|
20
|
+
SocketIOBindingKeys,
|
|
21
|
+
SocketIOServerHelper,
|
|
22
|
+
RedisHelper,
|
|
23
|
+
} from '@venizia/ignis';
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
SocketIOClientHelper,
|
|
27
|
+
SocketIOConstants,
|
|
28
|
+
SocketIOClientStates,
|
|
29
|
+
} from '@venizia/ignis-helpers/socket-io';
|
|
30
|
+
|
|
31
|
+
import type {
|
|
32
|
+
IServerOptions,
|
|
33
|
+
TSocketIOAuthenticateFn,
|
|
34
|
+
TSocketIOValidateRoomFn,
|
|
35
|
+
TSocketIOClientConnectedFn,
|
|
36
|
+
ISocketIOClientOptions,
|
|
37
|
+
IOptions,
|
|
38
|
+
TSocketIOEventHandler,
|
|
39
|
+
TSocketIOClientState,
|
|
40
|
+
} from '@venizia/ignis';
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Use Cases
|
|
44
|
+
|
|
45
|
+
- Live notifications and alerts
|
|
46
|
+
- Real-time chat and messaging
|
|
47
|
+
- Collaborative editing (docs, whiteboards)
|
|
48
|
+
- Live data streams (dashboards, monitoring)
|
|
49
|
+
- Multiplayer game state synchronization
|
|
50
|
+
- Service-to-service real-time communication (via `SocketIOClientHelper`)
|
|
51
|
+
|
|
52
|
+
## Server Helper Setup
|
|
53
|
+
|
|
54
|
+
### Step 1: Install Dependencies
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Core dependency (already included via @venizia/ignis)
|
|
58
|
+
# ioredis is required for the Redis adapter
|
|
59
|
+
|
|
60
|
+
# For Bun runtime only -- optional peer dependency
|
|
61
|
+
bun add @socket.io/bun-engine
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 2: Bind Required Services
|
|
65
|
+
|
|
66
|
+
In your application's `preConfigure()` method, bind the required services and register the component:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import {
|
|
70
|
+
BaseApplication,
|
|
71
|
+
SocketIOComponent,
|
|
72
|
+
SocketIOBindingKeys,
|
|
73
|
+
RedisHelper,
|
|
74
|
+
TSocketIOAuthenticateFn,
|
|
75
|
+
TSocketIOValidateRoomFn,
|
|
76
|
+
TSocketIOClientConnectedFn,
|
|
77
|
+
ValueOrPromise,
|
|
78
|
+
} from '@venizia/ignis';
|
|
79
|
+
|
|
80
|
+
export class Application extends BaseApplication {
|
|
81
|
+
private redisHelper: RedisHelper;
|
|
82
|
+
|
|
83
|
+
preConfigure(): ValueOrPromise<void> {
|
|
84
|
+
this.setupSocketIO();
|
|
85
|
+
// ... other setup
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setupSocketIO() {
|
|
89
|
+
// 1. Redis connection (required for adapter + emitter)
|
|
90
|
+
this.redisHelper = new RedisHelper({
|
|
91
|
+
name: 'socket-io-redis',
|
|
92
|
+
host: process.env.REDIS_HOST ?? 'localhost',
|
|
93
|
+
port: +(process.env.REDIS_PORT ?? 6379),
|
|
94
|
+
password: process.env.REDIS_PASSWORD,
|
|
95
|
+
autoConnect: false,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
this.bind<RedisHelper>({
|
|
99
|
+
key: SocketIOBindingKeys.REDIS_CONNECTION,
|
|
100
|
+
}).toValue(this.redisHelper);
|
|
101
|
+
|
|
102
|
+
// 2. Authentication handler (required)
|
|
103
|
+
const authenticateFn: TSocketIOAuthenticateFn = handshake => {
|
|
104
|
+
const token = handshake.headers.authorization;
|
|
105
|
+
// Implement your auth logic -- JWT verification, session check, etc.
|
|
106
|
+
return !!token;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
this.bind<TSocketIOAuthenticateFn>({
|
|
110
|
+
key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,
|
|
111
|
+
}).toValue(authenticateFn);
|
|
112
|
+
|
|
113
|
+
// 3. Room validation handler (optional -- joins rejected without this)
|
|
114
|
+
const validateRoomFn: TSocketIOValidateRoomFn = ({ socket, rooms }) => {
|
|
115
|
+
// Return the rooms that the client is allowed to join
|
|
116
|
+
const allowedRooms = rooms.filter(room => room.startsWith('public-'));
|
|
117
|
+
return allowedRooms;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this.bind<TSocketIOValidateRoomFn>({
|
|
121
|
+
key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,
|
|
122
|
+
}).toValue(validateRoomFn);
|
|
123
|
+
|
|
124
|
+
// 4. Client connected handler (optional)
|
|
125
|
+
const clientConnectedFn: TSocketIOClientConnectedFn = ({ socket }) => {
|
|
126
|
+
console.log('Client connected:', socket.id);
|
|
127
|
+
// Register custom event handlers on the socket
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
this.bind<TSocketIOClientConnectedFn>({
|
|
131
|
+
key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,
|
|
132
|
+
}).toValue(clientConnectedFn);
|
|
133
|
+
|
|
134
|
+
// 5. Register the component -- that's it!
|
|
135
|
+
this.component(SocketIOComponent);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `autoConnect: false` Rationale
|
|
141
|
+
|
|
142
|
+
The `RedisHelper` is created with `autoConnect: false` because the server helper internally calls `client.duplicate()` to create 3 independent Redis connections (pub, sub, emitter). The duplicated clients inherit the `lazyConnect` setting from the parent. During `configure()`, the helper detects clients in `wait` status and explicitly calls `client.connect()` on each, then awaits all 3 to reach `ready` status before proceeding. This avoids race conditions where the parent connects before the duplicates are created.
|
|
143
|
+
|
|
144
|
+
#### Redis Connection Alternatives
|
|
145
|
+
|
|
146
|
+
You can use either `RedisHelper` (single Redis instance) or `RedisClusterHelper` (Redis Cluster mode). Both extend `DefaultRedisHelper`, which is the type the component validates against:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { RedisClusterHelper } from '@venizia/ignis';
|
|
150
|
+
|
|
151
|
+
// For Redis Cluster deployments
|
|
152
|
+
const redisHelper = new RedisClusterHelper({
|
|
153
|
+
name: 'socket-io-redis-cluster',
|
|
154
|
+
nodes: [
|
|
155
|
+
{ host: 'redis-node-1', port: 6379 },
|
|
156
|
+
{ host: 'redis-node-2', port: 6380 },
|
|
157
|
+
{ host: 'redis-node-3', port: 6381 },
|
|
158
|
+
],
|
|
159
|
+
password: process.env.REDIS_PASSWORD,
|
|
160
|
+
autoConnect: false,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
this.bind<RedisClusterHelper>({
|
|
164
|
+
key: SocketIOBindingKeys.REDIS_CONNECTION,
|
|
165
|
+
}).toValue(redisHelper);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The internal `TRedisClient` type is `Redis | Cluster`, so both ioredis connection types are supported transparently.
|
|
169
|
+
|
|
170
|
+
## Configuration
|
|
171
|
+
|
|
172
|
+
### Default Server Options
|
|
173
|
+
|
|
174
|
+
The component applies these defaults if `SocketIOBindingKeys.SERVER_OPTIONS` is not bound or partially overridden:
|
|
175
|
+
|
|
176
|
+
| Option | Default | Description |
|
|
177
|
+
|--------|---------|-------------|
|
|
178
|
+
| `identifier` | `'SOCKET_IO_SERVER'` | Unique identifier for the helper instance |
|
|
179
|
+
| `path` | `'/io'` | URL path for Socket.IO handshake/polling |
|
|
180
|
+
| `cors.origin` | `'*'` | Allowed origins (restrict in production!) |
|
|
181
|
+
| `cors.methods` | `['GET', 'POST']` | Allowed HTTP methods for CORS preflight |
|
|
182
|
+
| `cors.preflightContinue` | `false` | Pass preflight to next handler |
|
|
183
|
+
| `cors.optionsSuccessStatus` | `204` | Status code for successful OPTIONS requests |
|
|
184
|
+
| `cors.credentials` | `true` | Allow cookies/auth headers |
|
|
185
|
+
| `perMessageDeflate.threshold` | `4096` | Minimum message size to compress (bytes) |
|
|
186
|
+
| `perMessageDeflate.concurrencyLimit` | `20` | Max concurrent compression operations |
|
|
187
|
+
| `perMessageDeflate.clientNoContextTakeover` | `true` | Client releases compression context after each message |
|
|
188
|
+
| `perMessageDeflate.serverNoContextTakeover` | `true` | Server releases compression context after each message |
|
|
189
|
+
| `perMessageDeflate.serverMaxWindowBits` | `10` | Server-side maximum window size (2^10 = 1KB) |
|
|
190
|
+
|
|
191
|
+
> [!WARNING]
|
|
192
|
+
> The default `cors.origin: '*'` is suitable for development only. In production, restrict this to your specific domains.
|
|
193
|
+
|
|
194
|
+
#### Full `DEFAULT_SERVER_OPTIONS`
|
|
195
|
+
```typescript
|
|
196
|
+
const DEFAULT_SERVER_OPTIONS: Partial<IServerOptions> = {
|
|
197
|
+
identifier: 'SOCKET_IO_SERVER',
|
|
198
|
+
path: '/io',
|
|
199
|
+
cors: {
|
|
200
|
+
origin: '*',
|
|
201
|
+
methods: ['GET', 'POST'],
|
|
202
|
+
preflightContinue: false,
|
|
203
|
+
optionsSuccessStatus: 204,
|
|
204
|
+
credentials: true,
|
|
205
|
+
},
|
|
206
|
+
perMessageDeflate: {
|
|
207
|
+
threshold: 4096,
|
|
208
|
+
zlibDeflateOptions: { chunkSize: 10 * 1024 },
|
|
209
|
+
zlibInflateOptions: { windowBits: 12, memLevel: 8 },
|
|
210
|
+
clientNoContextTakeover: true,
|
|
211
|
+
serverNoContextTakeover: true,
|
|
212
|
+
serverMaxWindowBits: 10,
|
|
213
|
+
concurrencyLimit: 20,
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Custom Configuration
|
|
219
|
+
|
|
220
|
+
Bind custom server options before registering the component:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { SocketIOBindingKeys, IServerOptions } from '@venizia/ignis';
|
|
224
|
+
|
|
225
|
+
const customOptions: Partial<IServerOptions> = {
|
|
226
|
+
identifier: 'my-app-socket',
|
|
227
|
+
path: '/socket.io',
|
|
228
|
+
cors: {
|
|
229
|
+
origin: ['https://myapp.com', 'https://admin.myapp.com'],
|
|
230
|
+
methods: ['GET', 'POST'],
|
|
231
|
+
credentials: true,
|
|
232
|
+
},
|
|
233
|
+
pingTimeout: 60000,
|
|
234
|
+
pingInterval: 25000,
|
|
235
|
+
maxHttpBufferSize: 1e6, // 1MB
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
this.bind<Partial<IServerOptions>>({
|
|
239
|
+
key: SocketIOBindingKeys.SERVER_OPTIONS,
|
|
240
|
+
}).toValue(customOptions);
|
|
241
|
+
|
|
242
|
+
this.component(SocketIOComponent);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Binding Keys
|
|
246
|
+
|
|
247
|
+
All binding keys are available in `SocketIOBindingKeys`:
|
|
248
|
+
|
|
249
|
+
| Binding Key | Constant | Type | Required | Default |
|
|
250
|
+
|------------|----------|------|----------|---------|
|
|
251
|
+
| `@app/socket-io/server-options` | `SERVER_OPTIONS` | `Partial<IServerOptions>` | No | See defaults above |
|
|
252
|
+
| `@app/socket-io/redis-connection` | `REDIS_CONNECTION` | `RedisHelper` / `RedisClusterHelper` / `DefaultRedisHelper` | **Yes** | `null` |
|
|
253
|
+
| `@app/socket-io/authenticate-handler` | `AUTHENTICATE_HANDLER` | `TSocketIOAuthenticateFn` | **Yes** | `null` |
|
|
254
|
+
| `@app/socket-io/validate-room-handler` | `VALIDATE_ROOM_HANDLER` | `TSocketIOValidateRoomFn` | No | `null` |
|
|
255
|
+
| `@app/socket-io/client-connected-handler` | `CLIENT_CONNECTED_HANDLER` | `TSocketIOClientConnectedFn` | No | `null` |
|
|
256
|
+
| `@app/socket-io/instance` | `SOCKET_IO_INSTANCE` | `SocketIOServerHelper` | -- | *Set by component* |
|
|
257
|
+
|
|
258
|
+
> [!NOTE]
|
|
259
|
+
> `SOCKET_IO_INSTANCE` is **not** set by you -- the component creates and binds it automatically after the server starts. Inject it in services/controllers to interact with Socket.IO.
|
|
260
|
+
|
|
261
|
+
## Constants
|
|
262
|
+
|
|
263
|
+
Constants are exported from `@venizia/ignis-helpers/socket-io` and used internally by both the component and the helper.
|
|
264
|
+
|
|
265
|
+
### System Events
|
|
266
|
+
|
|
267
|
+
| Constant | Value | Description |
|
|
268
|
+
|----------|-------|-------------|
|
|
269
|
+
| `SocketIOConstants.EVENT_PING` | `'ping'` | Keep-alive ping emitted at `pingInterval` (default: 30s) |
|
|
270
|
+
| `SocketIOConstants.EVENT_CONNECT` | `'connection'` | New client connected (server-side event) |
|
|
271
|
+
| `SocketIOConstants.EVENT_DISCONNECT` | `'disconnect'` | Client disconnected |
|
|
272
|
+
| `SocketIOConstants.EVENT_JOIN` | `'join'` | Client requests to join room(s) |
|
|
273
|
+
| `SocketIOConstants.EVENT_LEAVE` | `'leave'` | Client requests to leave room(s) |
|
|
274
|
+
| `SocketIOConstants.EVENT_AUTHENTICATE` | `'authenticate'` | Client sends auth credentials |
|
|
275
|
+
| `SocketIOConstants.EVENT_AUTHENTICATED` | `'authenticated'` | Auth success response sent to client |
|
|
276
|
+
| `SocketIOConstants.EVENT_UNAUTHENTICATE` | `'unauthenticated'` | Auth failure response sent to client |
|
|
277
|
+
|
|
278
|
+
### Default Rooms
|
|
279
|
+
|
|
280
|
+
All authenticated clients are automatically joined to these rooms:
|
|
281
|
+
|
|
282
|
+
| Constant | Value | Description |
|
|
283
|
+
|----------|-------|-------------|
|
|
284
|
+
| `SocketIOConstants.ROOM_DEFAULT` | `'io-default'` | Default room all authenticated clients join |
|
|
285
|
+
| `SocketIOConstants.ROOM_NOTIFICATION` | `'io-notification'` | Notification broadcast room |
|
|
286
|
+
|
|
287
|
+
> [!TIP]
|
|
288
|
+
> You can override default rooms via the `defaultRooms` option on `SocketIOServerHelper`. The component uses the defaults above when not overridden.
|
|
289
|
+
|
|
290
|
+
### Internal Constants (Server Helper)
|
|
291
|
+
|
|
292
|
+
These constants are defined at module scope in the server helper and are not exported, but they govern default behavior:
|
|
293
|
+
|
|
294
|
+
| Constant | Value | Description |
|
|
295
|
+
|----------|-------|-------------|
|
|
296
|
+
| `CLIENT_AUTHENTICATE_TIMEOUT` | `10_000` (10s) | Time allowed for a client to authenticate before forced disconnect |
|
|
297
|
+
| `CLIENT_PING_INTERVAL` | `30_000` (30s) | Interval between server-to-client ping emissions |
|
|
298
|
+
|
|
299
|
+
Both can be overridden via the `authenticateTimeout` and `pingInterval` constructor options on `SocketIOServerHelper`.
|
|
300
|
+
|
|
301
|
+
### Client States
|
|
302
|
+
|
|
303
|
+
Each connected client tracks an authentication state that governs what actions are permitted:
|
|
304
|
+
|
|
305
|
+
| State | Constant | Description |
|
|
306
|
+
|-------|----------|-------------|
|
|
307
|
+
| `unauthorized` | `SocketIOClientStates.UNAUTHORIZED` | Initial state -- client must emit `authenticate` within the timeout (default: 10s) |
|
|
308
|
+
| `authenticating` | `SocketIOClientStates.AUTHENTICATING` | Auth in progress -- `authenticateFn` is executing |
|
|
309
|
+
| `authenticated` | `SocketIOClientStates.AUTHENTICATED` | Auth successful -- client can send/receive events and join rooms |
|
|
310
|
+
|
|
311
|
+
#### State Machine Diagram
|
|
312
|
+
```
|
|
313
|
+
+------------------+
|
|
314
|
+
connect ---------->| unauthorized |
|
|
315
|
+
+--------+---------+
|
|
316
|
+
| emit('authenticate')
|
|
317
|
+
+--------v---------+
|
|
318
|
+
| authenticating |
|
|
319
|
+
+---+----------+---+
|
|
320
|
+
success | | failure
|
|
321
|
+
+---------v--+ +-------v-----------+
|
|
322
|
+
|authenticated| | unauthorized |--> disconnect
|
|
323
|
+
+-------------+ +------------------+
|
|
324
|
+
^
|
|
325
|
+
timeout (10s)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
#### `SocketIOClientStates` Source
|
|
329
|
+
```typescript
|
|
330
|
+
export class SocketIOClientStates {
|
|
331
|
+
static readonly UNAUTHORIZED = 'unauthorized';
|
|
332
|
+
static readonly AUTHENTICATING = 'authenticating';
|
|
333
|
+
static readonly AUTHENTICATED = 'authenticated';
|
|
334
|
+
|
|
335
|
+
static readonly SCHEME_SET = new Set([
|
|
336
|
+
this.UNAUTHORIZED,
|
|
337
|
+
this.AUTHENTICATING,
|
|
338
|
+
this.AUTHENTICATED,
|
|
339
|
+
]);
|
|
340
|
+
|
|
341
|
+
static isValid(input: string): input is TConstValue<typeof SocketIOClientStates> {
|
|
342
|
+
return this.SCHEME_SET.has(input);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Resolved Bindings
|
|
348
|
+
|
|
349
|
+
The component resolves all binding keys into a single `IResolvedBindings` object during the `binding()` phase:
|
|
350
|
+
|
|
351
|
+
#### `IResolvedBindings` Interface
|
|
352
|
+
```typescript
|
|
353
|
+
interface IResolvedBindings {
|
|
354
|
+
redisConnection: DefaultRedisHelper;
|
|
355
|
+
authenticateFn: TSocketIOAuthenticateFn;
|
|
356
|
+
validateRoomFn?: TSocketIOValidateRoomFn;
|
|
357
|
+
clientConnectedFn?: TSocketIOClientConnectedFn;
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### Callback Type Signatures
|
|
362
|
+
```typescript
|
|
363
|
+
// Called with the socket handshake -- return true to authenticate, false to reject
|
|
364
|
+
type TSocketIOAuthenticateFn = (args: IHandshake) => ValueOrPromise<boolean>;
|
|
365
|
+
|
|
366
|
+
// Called when client emits 'join' -- return the subset of rooms the client is allowed to join
|
|
367
|
+
type TSocketIOValidateRoomFn = (opts: {
|
|
368
|
+
socket: IOSocket;
|
|
369
|
+
rooms: string[];
|
|
370
|
+
}) => ValueOrPromise<string[]>;
|
|
371
|
+
|
|
372
|
+
// Called after successful authentication -- register custom event handlers here
|
|
373
|
+
type TSocketIOClientConnectedFn = (opts: { socket: IOSocket }) => ValueOrPromise<void>;
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
#### `IHandshake` Interface
|
|
377
|
+
```typescript
|
|
378
|
+
interface IHandshake {
|
|
379
|
+
headers: IncomingHttpHeaders;
|
|
380
|
+
time: string;
|
|
381
|
+
address: string;
|
|
382
|
+
xdomain: boolean;
|
|
383
|
+
secure: boolean;
|
|
384
|
+
issued: number;
|
|
385
|
+
url: string;
|
|
386
|
+
query: ParsedUrlQuery;
|
|
387
|
+
auth: { [key: string]: any };
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## See Also
|
|
392
|
+
|
|
393
|
+
- [Usage & Examples](./usage) -- Server-side usage, client helper, advanced patterns
|
|
394
|
+
- [API Reference](./api) -- Architecture, method signatures, internals, types
|
|
395
|
+
- [Error Reference](./errors) -- Error conditions and troubleshooting
|
|
396
|
+
- **Guides:**
|
|
397
|
+
- [Components Overview](/guides/core-concepts/components) -- Component system basics
|
|
398
|
+
- [Application](/guides/core-concepts/application/) -- Registering components
|
|
399
|
+
- **Components:**
|
|
400
|
+
- [Components Index](../index) -- All built-in components
|
|
401
|
+
- **Helpers:**
|
|
402
|
+
- [Socket.IO Helper](/references/helpers/socket-io/) -- Full `SocketIOServerHelper` + `SocketIOClientHelper` API reference
|
|
403
|
+
- **External Resources:**
|
|
404
|
+
- [Socket.IO Documentation](https://socket.io/docs/) -- Official docs
|
|
405
|
+
- [Socket.IO Redis Adapter](https://socket.io/docs/v4/redis-adapter/) -- Horizontal scaling guide
|
|
406
|
+
- [@socket.io/bun-engine](https://github.com/socketio/bun-engine) -- Bun runtime support
|
|
407
|
+
- **Tutorials:**
|
|
408
|
+
- [Real-Time Chat](/guides/tutorials/realtime-chat) -- Building a chat app with Socket.IO
|
|
409
|
+
- **Changelog:**
|
|
410
|
+
- [2026-02-06: Socket.IO Integration Fix](/changelogs/2026-02-06-socket-io-integration-fix) -- Lifecycle timing fix + Bun runtime support
|