@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,12 +1,141 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Socket.IO Component - Usage & Examples
|
|
3
|
+
description: Full setup steps, server-side usage, the client helper, and advanced patterns
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
# Usage & Examples
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
Task-oriented patterns for the Socket.IO component: full setup, sending messages from a service, using the standalone client helper, and reading the example app.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
## Full setup
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
### 1. Install dependencies
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Core dependency (already included via @venizia/ignis)
|
|
17
|
+
# ioredis is required for the Redis adapter
|
|
18
|
+
|
|
19
|
+
# Bun runtime only - optional peer dependency
|
|
20
|
+
bun add @socket.io/bun-engine
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. Bind required and optional services
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { BaseApplication } from '@venizia/ignis';
|
|
27
|
+
import { SocketIOComponent, SocketIOBindingKeys } from '@venizia/ignis/socket-io';
|
|
28
|
+
import { RedisSingleHelper, ValueOrPromise } from '@venizia/ignis-helpers';
|
|
29
|
+
import type {
|
|
30
|
+
TSocketIOAuthenticateFn,
|
|
31
|
+
TSocketIOValidateRoomFn,
|
|
32
|
+
TSocketIOClientConnectedFn,
|
|
33
|
+
} from '@venizia/ignis-helpers/socket-io';
|
|
34
|
+
|
|
35
|
+
export class Application extends BaseApplication {
|
|
36
|
+
private redisHelper: RedisSingleHelper;
|
|
37
|
+
|
|
38
|
+
preConfigure(): ValueOrPromise<void> {
|
|
39
|
+
this.setupSocketIO();
|
|
40
|
+
// ... other setup
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setupSocketIO() {
|
|
44
|
+
// 1. Redis connection (required for adapter + emitter)
|
|
45
|
+
this.redisHelper = new RedisSingleHelper({
|
|
46
|
+
name: 'socket-io-redis',
|
|
47
|
+
host: process.env.REDIS_HOST ?? 'localhost',
|
|
48
|
+
port: +(process.env.REDIS_PORT ?? 6379),
|
|
49
|
+
password: process.env.REDIS_PASSWORD,
|
|
50
|
+
autoConnect: false,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.bind<RedisSingleHelper>({
|
|
54
|
+
key: SocketIOBindingKeys.REDIS_CONNECTION,
|
|
55
|
+
}).toValue(this.redisHelper);
|
|
56
|
+
|
|
57
|
+
// 2. Authentication handler (required)
|
|
58
|
+
const authenticateFn: TSocketIOAuthenticateFn = handshake => {
|
|
59
|
+
const token = handshake.headers.authorization;
|
|
60
|
+
// Implement your auth logic: JWT verification, session check, etc.
|
|
61
|
+
return !!token;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
this.bind<TSocketIOAuthenticateFn>({
|
|
65
|
+
key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,
|
|
66
|
+
}).toValue(authenticateFn);
|
|
67
|
+
|
|
68
|
+
// 3. Room validation handler (optional - joins rejected without this)
|
|
69
|
+
const validateRoomFn: TSocketIOValidateRoomFn = ({ socket, rooms }) => {
|
|
70
|
+
// Return the rooms that the client is allowed to join
|
|
71
|
+
return rooms.filter(room => room.startsWith('public-'));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
this.bind<TSocketIOValidateRoomFn>({
|
|
75
|
+
key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,
|
|
76
|
+
}).toValue(validateRoomFn);
|
|
77
|
+
|
|
78
|
+
// 4. Client connected handler (optional)
|
|
79
|
+
const clientConnectedFn: TSocketIOClientConnectedFn = ({ socket }) => {
|
|
80
|
+
console.log('Client connected:', socket.id);
|
|
81
|
+
// Register custom event handlers on the socket
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
this.bind<TSocketIOClientConnectedFn>({
|
|
85
|
+
key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,
|
|
86
|
+
}).toValue(clientConnectedFn);
|
|
87
|
+
|
|
88
|
+
// 5. Register the component - that's it!
|
|
89
|
+
this.component(SocketIOComponent);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 3. Why `autoConnect: false`
|
|
95
|
+
|
|
96
|
+
The helper owns the connection timing, not you. `RedisSingleHelper` is created with `autoConnect: false` because the server helper calls `duplicateClient()` three times:
|
|
97
|
+
|
|
98
|
+
| Duplicate | Role |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `redisPub` | Redis adapter - publishes room broadcasts |
|
|
101
|
+
| `redisSub` | Redis adapter - subscribes to room broadcasts |
|
|
102
|
+
| `redisEmitter` | Redis emitter - direct cross-instance send |
|
|
103
|
+
|
|
104
|
+
- **Duplicates inherit `lazyConnect`, not connection state.** During `configure()`, the helper checks each client's status. Any client still `wait`ing gets `connect()` called on it explicitly. The helper then waits for all three to reach `ready` before proceeding.
|
|
105
|
+
- **This avoids a race.** If the parent connects before the duplicates exist, the duplicates can end up in a state inconsistent with the parent's connection lifecycle.
|
|
106
|
+
|
|
107
|
+
### Redis connection alternatives
|
|
108
|
+
|
|
109
|
+
`RedisSingleHelper` (single instance), `RedisClusterHelper` (cluster mode), and `RedisSentinelHelper` (Sentinel HA) all extend `AbstractRedisHelper` and satisfy the `IRedisHelper` interface the component validates against.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { RedisClusterHelper } from '@venizia/ignis-helpers';
|
|
113
|
+
|
|
114
|
+
// For Redis Cluster deployments
|
|
115
|
+
const redisHelper = new RedisClusterHelper({
|
|
116
|
+
name: 'socket-io-redis-cluster',
|
|
117
|
+
nodes: [
|
|
118
|
+
{ host: 'redis-node-1', port: 6379 },
|
|
119
|
+
{ host: 'redis-node-2', port: 6380 },
|
|
120
|
+
{ host: 'redis-node-3', port: 6381 },
|
|
121
|
+
],
|
|
122
|
+
password: process.env.REDIS_PASSWORD,
|
|
123
|
+
autoConnect: false,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
this.bind<RedisClusterHelper>({
|
|
127
|
+
key: SocketIOBindingKeys.REDIS_CONNECTION,
|
|
128
|
+
}).toValue(redisHelper);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The internal `TRedisClient` type is `Redis | Cluster`, so both ioredis connection types work transparently.
|
|
132
|
+
|
|
133
|
+
> [!NOTE]
|
|
134
|
+
> Full defaults, the complete binding key table, and every system event/room constant are in the [Full Reference](./api#configuration-reference).
|
|
135
|
+
|
|
136
|
+
## Inject the helper in a service or controller
|
|
137
|
+
|
|
138
|
+
`SocketIOServerHelper` is bound to `SOCKET_IO_INSTANCE` inside a post-start hook. That hook runs after the server starts - well after the DI container already built every service and controller. Use a lazy getter that resolves the helper on first access. Never `@inject` it in a constructor.
|
|
10
139
|
|
|
11
140
|
```typescript
|
|
12
141
|
import {
|
|
@@ -19,7 +148,6 @@ import { SocketIOBindingKeys } from '@venizia/ignis/socket-io';
|
|
|
19
148
|
import { SocketIOServerHelper } from '@venizia/ignis-helpers/socket-io';
|
|
20
149
|
|
|
21
150
|
export class NotificationService extends BaseService {
|
|
22
|
-
// Lazy getter pattern -- helper is bound AFTER server starts
|
|
23
151
|
private _io: SocketIOServerHelper | null = null;
|
|
24
152
|
|
|
25
153
|
constructor(
|
|
@@ -59,47 +187,37 @@ export class NotificationService extends BaseService {
|
|
|
59
187
|
notifyRoom(opts: { room: string; message: string }) {
|
|
60
188
|
this.io.send({
|
|
61
189
|
destination: opts.room,
|
|
62
|
-
payload: {
|
|
63
|
-
topic: 'room:update',
|
|
64
|
-
data: { message: opts.message },
|
|
65
|
-
},
|
|
190
|
+
payload: { topic: 'room:update', data: { message: opts.message } },
|
|
66
191
|
});
|
|
67
192
|
}
|
|
68
193
|
|
|
69
194
|
// Broadcast to all clients
|
|
70
195
|
broadcastAnnouncement(opts: { message: string }) {
|
|
71
196
|
this.io.send({
|
|
72
|
-
payload: {
|
|
73
|
-
topic: 'system:announcement',
|
|
74
|
-
data: { message: opts.message },
|
|
75
|
-
},
|
|
197
|
+
payload: { topic: 'system:announcement', data: { message: opts.message } },
|
|
76
198
|
});
|
|
77
199
|
}
|
|
78
200
|
}
|
|
79
201
|
```
|
|
80
202
|
|
|
81
|
-
|
|
82
|
-
|
|
203
|
+
- **Never `@inject` `SOCKET_IO_INSTANCE` in a constructor.** It is not bound yet at that point.
|
|
204
|
+
- **`send()` reads via the Redis emitter.** It works even if the destination client is connected to a different server instance - see [`send()` in the Full Reference](./api#messaging-via-send).
|
|
83
205
|
|
|
84
|
-
##
|
|
206
|
+
## Use the client helper
|
|
85
207
|
|
|
86
|
-
`SocketIOClientHelper`
|
|
208
|
+
`SocketIOClientHelper` wraps `socket.io-client` with authentication flow, lifecycle callbacks, and error-safe event subscription. Use it when your process needs to connect *to* a Socket.IO server, not run one - service-to-service communication, testing, or relay services.
|
|
87
209
|
|
|
88
|
-
### Client
|
|
210
|
+
### Client setup
|
|
89
211
|
|
|
90
212
|
```typescript
|
|
91
|
-
import {
|
|
92
|
-
SocketIOClientHelper,
|
|
93
|
-
} from '@venizia/ignis-helpers/socket-io';
|
|
213
|
+
import { SocketIOClientHelper } from '@venizia/ignis-helpers/socket-io';
|
|
94
214
|
|
|
95
215
|
const client = new SocketIOClientHelper({
|
|
96
216
|
identifier: 'notification-relay',
|
|
97
217
|
host: 'http://localhost:3000',
|
|
98
218
|
options: {
|
|
99
219
|
path: '/io',
|
|
100
|
-
extraHeaders: {
|
|
101
|
-
authorization: 'Bearer <token>',
|
|
102
|
-
},
|
|
220
|
+
extraHeaders: { authorization: 'Bearer <token>' },
|
|
103
221
|
},
|
|
104
222
|
|
|
105
223
|
// Lifecycle callbacks (all optional)
|
|
@@ -107,56 +225,52 @@ const client = new SocketIOClientHelper({
|
|
|
107
225
|
console.log('Connected to server');
|
|
108
226
|
client.authenticate();
|
|
109
227
|
},
|
|
110
|
-
onDisconnected:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
console.error('Connection error:', error);
|
|
115
|
-
},
|
|
116
|
-
onAuthenticated: () => {
|
|
117
|
-
console.log('Authentication successful');
|
|
118
|
-
},
|
|
119
|
-
onUnauthenticated: (message) => {
|
|
120
|
-
console.warn('Authentication failed:', message);
|
|
121
|
-
},
|
|
228
|
+
onDisconnected: reason => console.log('Disconnected:', reason),
|
|
229
|
+
onError: error => console.error('Connection error:', error),
|
|
230
|
+
onAuthenticated: () => console.log('Authentication successful'),
|
|
231
|
+
onUnauthenticated: message => console.warn('Authentication failed:', message),
|
|
122
232
|
});
|
|
123
233
|
```
|
|
124
234
|
|
|
125
|
-
|
|
235
|
+
- **The constructor calls `configure()` immediately.** It creates the `socket.io-client` `Socket` instance via `io(host, options)` and registers the internal event handlers. See the [full handler table](./api#client-configure-event-handlers) in the Full Reference.
|
|
236
|
+
- **The socket connects on its own unless you disable it.** Set `autoConnect: false` in `options` and call `client.connect()` yourself when you're ready.
|
|
237
|
+
|
|
238
|
+
#### `connect` vs `connection` event
|
|
126
239
|
|
|
127
|
-
|
|
240
|
+
Client and server fire different event names for the same moment:
|
|
128
241
|
|
|
129
|
-
|
|
242
|
+
| Side | Fires |
|
|
243
|
+
|---|---|
|
|
244
|
+
| Client (`socket.io-client`) | `connect` - no suffix |
|
|
245
|
+
| Server (`socket.io`) | `connection` - with the suffix |
|
|
130
246
|
|
|
131
|
-
|
|
247
|
+
This is a Socket.IO convention, not an IGNIS one. The client helper listens on `'connect'`. The server helper listens on `SocketIOConstants.EVENT_CONNECT`, which equals `'connection'`.
|
|
132
248
|
|
|
133
|
-
### Authentication
|
|
249
|
+
### Authentication flow
|
|
134
250
|
|
|
135
|
-
After connecting, the client must emit `authenticate` to start the
|
|
251
|
+
After connecting, the client must emit `authenticate` to start the handshake. The server validates credentials from the socket handshake (headers, query params, `auth` object) and responds with either `authenticated` or `unauthenticated`.
|
|
136
252
|
|
|
137
253
|
```typescript
|
|
138
|
-
// Manual authentication after connection
|
|
139
254
|
client.authenticate();
|
|
140
255
|
```
|
|
141
256
|
|
|
142
|
-
|
|
143
|
-
1. The socket must be connected (`client.connected === true`)
|
|
144
|
-
2. The current state must be `unauthorized` -- calling `authenticate()` while `authenticating` or already `authenticated` is a no-op with a warning log
|
|
257
|
+
`authenticate()` is a no-op with a warning log unless both conditions hold:
|
|
145
258
|
|
|
146
|
-
|
|
259
|
+
1. The socket is connected (`client.connected === true`).
|
|
260
|
+
2. The current state is `unauthorized` - calling `authenticate()` while `authenticating` or already `authenticated` does nothing.
|
|
147
261
|
|
|
148
|
-
|
|
262
|
+
#### Authentication failure messages
|
|
149
263
|
|
|
150
|
-
|
|
151
|
-
|-------------|---------|-------|
|
|
152
|
-
| `authenticateFn` returned `false` | `"Invalid token to authenticate! Please login again!"` | Credentials were checked but deemed invalid |
|
|
153
|
-
| `authenticateFn` threw an error | `"Failed to authenticate connection! Please login again!"` | An unexpected error occurred during validation |
|
|
264
|
+
The server sends a different message depending on how `authenticateFn` failed. Both paths reset the client to `unauthorized`, emit `unauthenticated` with the message, and disconnect the socket after delivery (via `setImmediate`).
|
|
154
265
|
|
|
155
|
-
|
|
266
|
+
| Failure mode | Message |
|
|
267
|
+
|---|---|
|
|
268
|
+
| `authenticateFn` returned `false` | `"Invalid token to authenticate! Please login again!"` |
|
|
269
|
+
| `authenticateFn` threw an error | `"Failed to authenticate connection! Please login again!"` |
|
|
156
270
|
|
|
157
|
-
### Event
|
|
271
|
+
### Event subscription
|
|
158
272
|
|
|
159
|
-
|
|
273
|
+
Handlers are wrapped in a dual try-catch. It catches both synchronous throws and asynchronous rejections, so a broken handler never crashes the client.
|
|
160
274
|
|
|
161
275
|
```typescript
|
|
162
276
|
// Subscribe to a single event
|
|
@@ -167,26 +281,24 @@ client.subscribe({
|
|
|
167
281
|
},
|
|
168
282
|
});
|
|
169
283
|
|
|
170
|
-
//
|
|
284
|
+
// ignoreDuplicate: false stacks a second handler for the same event
|
|
171
285
|
client.subscribe({
|
|
172
286
|
event: 'chat:message',
|
|
173
|
-
handler:
|
|
174
|
-
ignoreDuplicate: false,
|
|
287
|
+
handler: data => { /* second handler */ },
|
|
288
|
+
ignoreDuplicate: false,
|
|
175
289
|
});
|
|
176
290
|
|
|
177
291
|
// Subscribe to multiple events at once
|
|
178
292
|
client.subscribeMany({
|
|
179
293
|
events: {
|
|
180
|
-
'user:joined':
|
|
181
|
-
'user:left':
|
|
182
|
-
'room:updated':
|
|
294
|
+
'user:joined': data => console.log('User joined:', data),
|
|
295
|
+
'user:left': data => console.log('User left:', data),
|
|
296
|
+
'room:updated': data => console.log('Room updated:', data),
|
|
183
297
|
},
|
|
184
298
|
});
|
|
185
299
|
```
|
|
186
300
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
By default (`ignoreDuplicate: true`), `subscribe()` checks `socket.hasListeners(event)` before registering. If listeners already exist for the event, the call is a no-op and logs an info message. Set `ignoreDuplicate: false` to allow multiple handlers for the same event.
|
|
301
|
+
The default (`ignoreDuplicate: true`) checks `socket.hasListeners(event)` first. If a listener already exists, `subscribe()` is a no-op that logs an info message. Set `ignoreDuplicate: false` to stack handlers instead.
|
|
190
302
|
|
|
191
303
|
### Unsubscribing
|
|
192
304
|
|
|
@@ -201,22 +313,22 @@ client.unsubscribe({ event: 'chat:message', handler: myHandler });
|
|
|
201
313
|
client.unsubscribeMany({ events: ['chat:message', 'user:joined', 'room:updated'] });
|
|
202
314
|
```
|
|
203
315
|
|
|
204
|
-
### Emitting
|
|
316
|
+
### Emitting events
|
|
205
317
|
|
|
206
318
|
```typescript
|
|
207
319
|
client.emit({
|
|
208
320
|
topic: 'chat:send',
|
|
209
321
|
data: { text: 'Hello world' },
|
|
210
|
-
doLog: true,
|
|
211
|
-
|
|
322
|
+
doLog: true, // optional: log the emission
|
|
323
|
+
callback: () => { // optional: invoked via setImmediate after emit
|
|
212
324
|
console.log('Message sent');
|
|
213
325
|
},
|
|
214
326
|
});
|
|
215
327
|
```
|
|
216
328
|
|
|
217
|
-
|
|
329
|
+
`emit()` throws if the socket is not connected or if `topic` is missing. The server helper's `send()` silently drops a message with a missing field - `emit()` never does that. It always throws instead.
|
|
218
330
|
|
|
219
|
-
### Room
|
|
331
|
+
### Room management
|
|
220
332
|
|
|
221
333
|
```typescript
|
|
222
334
|
// Request to join rooms (server validates via validateRoomFn)
|
|
@@ -226,9 +338,9 @@ client.joinRooms({ rooms: ['chat-room-1', 'notifications'] });
|
|
|
226
338
|
client.leaveRooms({ rooms: ['chat-room-1'] });
|
|
227
339
|
```
|
|
228
340
|
|
|
229
|
-
Both methods emit Socket.IO
|
|
341
|
+
Both methods emit a Socket.IO event to the server: `join` or `leave`. The actual join or leave happens server-side. If the socket isn't connected, the call is a no-op with a warning log.
|
|
230
342
|
|
|
231
|
-
### Connection
|
|
343
|
+
### Connection management
|
|
232
344
|
|
|
233
345
|
```typescript
|
|
234
346
|
// Manually connect (useful when autoConnect: false in options)
|
|
@@ -247,34 +359,30 @@ const rawSocket = client.getSocketClient();
|
|
|
247
359
|
### Shutdown
|
|
248
360
|
|
|
249
361
|
```typescript
|
|
250
|
-
// Clean shutdown: removes all listeners, disconnects, resets state
|
|
251
362
|
client.shutdown();
|
|
252
363
|
```
|
|
253
364
|
|
|
254
|
-
|
|
255
|
-
1. Calls `removeAllListeners()` on the underlying socket to prevent memory leaks
|
|
256
|
-
2. Disconnects if still connected
|
|
257
|
-
3. Resets state to `unauthorized`
|
|
365
|
+
`shutdown()` does three things, in order:
|
|
258
366
|
|
|
259
|
-
|
|
367
|
+
1. Calls `removeAllListeners()` on the underlying socket to prevent memory leaks.
|
|
368
|
+
2. Disconnects if still connected.
|
|
369
|
+
3. Resets state to `unauthorized`.
|
|
260
370
|
|
|
261
|
-
|
|
371
|
+
## Run the complete example
|
|
262
372
|
|
|
263
|
-
A full working example
|
|
373
|
+
A full working example lives at `examples/socket-io-test/`.
|
|
264
374
|
|
|
265
375
|
| Feature | Implementation |
|
|
266
|
-
|
|
267
|
-
| Application setup | `src/application.ts`
|
|
268
|
-
| REST endpoints | `src/controllers/socket-test.controller.ts`
|
|
269
|
-
| Event handling | `src/services/socket-event.service.ts`
|
|
270
|
-
| Automated test client | `client.ts`
|
|
271
|
-
|
|
272
|
-
#### REST API Endpoints
|
|
376
|
+
|---|---|
|
|
377
|
+
| Application setup | `src/application.ts` - bindings, component registration, graceful shutdown |
|
|
378
|
+
| REST endpoints | `src/controllers/socket-test.controller.ts` - 9 endpoints for Socket.IO management |
|
|
379
|
+
| Event handling | `src/services/socket-event.service.ts` - chat, echo, room management |
|
|
380
|
+
| Automated test client | `client.ts` - 15+ test cases covering all features |
|
|
273
381
|
|
|
274
|
-
|
|
382
|
+
### REST API endpoints
|
|
275
383
|
|
|
276
384
|
| Method | Path | Description |
|
|
277
|
-
|
|
385
|
+
|---|---|---|
|
|
278
386
|
| `GET` | `/socket/info` | Server status + connected client count |
|
|
279
387
|
| `GET` | `/socket/clients` | List all connected client IDs |
|
|
280
388
|
| `GET` | `/socket/health` | Health check (is SocketIO ready?) |
|
|
@@ -285,38 +393,37 @@ The example provides a REST API for managing Socket.IO:
|
|
|
285
393
|
| `POST` | `/socket/client/{clientId}/leave` | Remove client from <code v-pre>{{ rooms: string[] }}</code> |
|
|
286
394
|
| `GET` | `/socket/client/{clientId}/rooms` | List rooms a client belongs to |
|
|
287
395
|
|
|
288
|
-
|
|
396
|
+
### Running the example
|
|
289
397
|
|
|
290
398
|
```bash
|
|
291
399
|
# Start the server
|
|
292
400
|
cd examples/socket-io-test
|
|
293
401
|
bun run server:dev
|
|
294
402
|
|
|
295
|
-
# In another terminal
|
|
403
|
+
# In another terminal - run automated tests
|
|
296
404
|
bun client.ts
|
|
297
405
|
```
|
|
298
406
|
|
|
299
|
-
The automated client
|
|
407
|
+
The automated client exercises:
|
|
300
408
|
|
|
301
|
-
- Authentication
|
|
409
|
+
- Authentication with valid and invalid tokens
|
|
302
410
|
- Ping/pong keepalive
|
|
303
411
|
- Room join/leave with validation
|
|
304
412
|
- Client-to-client messaging
|
|
305
|
-
- Room broadcasting
|
|
306
|
-
-
|
|
307
|
-
- REST API for Socket.IO management
|
|
413
|
+
- Room and global broadcasting
|
|
414
|
+
- The REST API
|
|
308
415
|
- Graceful disconnection
|
|
309
416
|
|
|
310
|
-
|
|
417
|
+
Read the example for these production-ready patterns:
|
|
311
418
|
|
|
312
|
-
- Binding multiple handlers in
|
|
313
|
-
-
|
|
419
|
+
- Binding multiple handlers in one `setupSocketIO()` method
|
|
420
|
+
- The lazy getter pattern for `SocketIOServerHelper`
|
|
314
421
|
- Custom event registration via `CLIENT_CONNECTED_HANDLER`
|
|
315
|
-
- Room validation
|
|
316
|
-
-
|
|
422
|
+
- Room validation that blocks unauthorized rooms
|
|
423
|
+
- A graceful shutdown sequence in `application.stop()`
|
|
317
424
|
|
|
318
|
-
## See
|
|
425
|
+
## See also
|
|
319
426
|
|
|
320
|
-
- [
|
|
321
|
-
- [
|
|
322
|
-
- [Error Reference](./errors)
|
|
427
|
+
- [Overview](./) - quick start, imports, common configuration tasks
|
|
428
|
+
- [Full Reference](./api) - architecture, configuration reference, method signatures, internals, types
|
|
429
|
+
- [Error Reference](./errors) - error conditions and troubleshooting
|