@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.
Files changed (123) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architectural-patterns.md +0 -2
  3. package/wiki/best-practices/architecture-decisions.md +0 -8
  4. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  5. package/wiki/best-practices/code-style-standards/index.md +0 -1
  6. package/wiki/best-practices/code-style-standards/tooling.md +0 -3
  7. package/wiki/best-practices/contribution-workflow.md +12 -12
  8. package/wiki/best-practices/index.md +4 -14
  9. package/wiki/best-practices/performance-optimization.md +3 -3
  10. package/wiki/best-practices/security-guidelines.md +2 -2
  11. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  12. package/wiki/guides/core-concepts/application/bootstrapping.md +6 -7
  13. package/wiki/guides/core-concepts/components-guide.md +1 -1
  14. package/wiki/guides/core-concepts/components.md +2 -2
  15. package/wiki/guides/core-concepts/dependency-injection.md +4 -5
  16. package/wiki/guides/core-concepts/persistent/datasources.md +4 -5
  17. package/wiki/guides/core-concepts/services.md +1 -1
  18. package/wiki/guides/get-started/5-minute-quickstart.md +4 -5
  19. package/wiki/guides/get-started/philosophy.md +12 -24
  20. package/wiki/guides/index.md +2 -9
  21. package/wiki/guides/reference/mcp-docs-server.md +13 -13
  22. package/wiki/guides/tutorials/building-a-crud-api.md +10 -10
  23. package/wiki/guides/tutorials/complete-installation.md +11 -12
  24. package/wiki/guides/tutorials/ecommerce-api.md +3 -3
  25. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  26. package/wiki/guides/tutorials/testing.md +4 -5
  27. package/wiki/index.md +8 -14
  28. package/wiki/references/base/bootstrapping.md +0 -3
  29. package/wiki/references/base/components.md +2 -2
  30. package/wiki/references/base/controllers.md +0 -1
  31. package/wiki/references/base/datasources.md +1 -1
  32. package/wiki/references/base/dependency-injection.md +2 -2
  33. package/wiki/references/base/filter-system/default-filter.md +2 -3
  34. package/wiki/references/base/filter-system/index.md +1 -1
  35. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  36. package/wiki/references/base/middlewares.md +0 -8
  37. package/wiki/references/base/providers.md +0 -9
  38. package/wiki/references/base/repositories/advanced.md +1 -1
  39. package/wiki/references/base/repositories/mixins.md +2 -3
  40. package/wiki/references/base/services.md +0 -1
  41. package/wiki/references/components/authentication/api.md +444 -0
  42. package/wiki/references/components/authentication/errors.md +177 -0
  43. package/wiki/references/components/authentication/index.md +571 -0
  44. package/wiki/references/components/authentication/usage.md +781 -0
  45. package/wiki/references/components/health-check.md +292 -103
  46. package/wiki/references/components/index.md +14 -12
  47. package/wiki/references/components/mail/api.md +505 -0
  48. package/wiki/references/components/mail/errors.md +176 -0
  49. package/wiki/references/components/mail/index.md +535 -0
  50. package/wiki/references/components/mail/usage.md +404 -0
  51. package/wiki/references/components/request-tracker.md +229 -25
  52. package/wiki/references/components/socket-io/api.md +1051 -0
  53. package/wiki/references/components/socket-io/errors.md +119 -0
  54. package/wiki/references/components/socket-io/index.md +410 -0
  55. package/wiki/references/components/socket-io/usage.md +322 -0
  56. package/wiki/references/components/static-asset/api.md +261 -0
  57. package/wiki/references/components/static-asset/errors.md +89 -0
  58. package/wiki/references/components/static-asset/index.md +617 -0
  59. package/wiki/references/components/static-asset/usage.md +364 -0
  60. package/wiki/references/components/swagger.md +390 -110
  61. package/wiki/references/components/template/api-page.md +125 -0
  62. package/wiki/references/components/template/errors-page.md +100 -0
  63. package/wiki/references/components/template/index.md +104 -0
  64. package/wiki/references/components/template/setup-page.md +134 -0
  65. package/wiki/references/components/template/single-page.md +132 -0
  66. package/wiki/references/components/template/usage-page.md +127 -0
  67. package/wiki/references/components/websocket/api.md +508 -0
  68. package/wiki/references/components/websocket/errors.md +123 -0
  69. package/wiki/references/components/websocket/index.md +453 -0
  70. package/wiki/references/components/websocket/usage.md +475 -0
  71. package/wiki/references/helpers/cron/index.md +224 -0
  72. package/wiki/references/helpers/crypto/index.md +537 -0
  73. package/wiki/references/helpers/env/index.md +214 -0
  74. package/wiki/references/helpers/error/index.md +232 -0
  75. package/wiki/references/helpers/index.md +16 -15
  76. package/wiki/references/helpers/inversion/index.md +608 -0
  77. package/wiki/references/helpers/logger/index.md +600 -0
  78. package/wiki/references/helpers/network/api.md +986 -0
  79. package/wiki/references/helpers/network/index.md +620 -0
  80. package/wiki/references/helpers/queue/index.md +589 -0
  81. package/wiki/references/helpers/redis/index.md +495 -0
  82. package/wiki/references/helpers/socket-io/api.md +497 -0
  83. package/wiki/references/helpers/socket-io/index.md +513 -0
  84. package/wiki/references/helpers/storage/api.md +705 -0
  85. package/wiki/references/helpers/storage/index.md +583 -0
  86. package/wiki/references/helpers/template/index.md +66 -0
  87. package/wiki/references/helpers/template/single-page.md +126 -0
  88. package/wiki/references/helpers/testing/index.md +510 -0
  89. package/wiki/references/helpers/types/index.md +512 -0
  90. package/wiki/references/helpers/uid/index.md +272 -0
  91. package/wiki/references/helpers/websocket/api.md +736 -0
  92. package/wiki/references/helpers/websocket/index.md +574 -0
  93. package/wiki/references/helpers/worker-thread/index.md +470 -0
  94. package/wiki/references/index.md +2 -9
  95. package/wiki/references/quick-reference.md +3 -18
  96. package/wiki/references/utilities/jsx.md +1 -8
  97. package/wiki/references/utilities/statuses.md +0 -7
  98. package/wiki/references/components/authentication.md +0 -476
  99. package/wiki/references/components/mail.md +0 -687
  100. package/wiki/references/components/socket-io.md +0 -562
  101. package/wiki/references/components/static-asset.md +0 -1277
  102. package/wiki/references/helpers/cron.md +0 -108
  103. package/wiki/references/helpers/crypto.md +0 -132
  104. package/wiki/references/helpers/env.md +0 -83
  105. package/wiki/references/helpers/error.md +0 -97
  106. package/wiki/references/helpers/inversion.md +0 -176
  107. package/wiki/references/helpers/logger.md +0 -296
  108. package/wiki/references/helpers/network.md +0 -396
  109. package/wiki/references/helpers/queue.md +0 -150
  110. package/wiki/references/helpers/redis.md +0 -142
  111. package/wiki/references/helpers/socket-io.md +0 -932
  112. package/wiki/references/helpers/storage.md +0 -665
  113. package/wiki/references/helpers/testing.md +0 -133
  114. package/wiki/references/helpers/types.md +0 -167
  115. package/wiki/references/helpers/uid.md +0 -167
  116. package/wiki/references/helpers/worker-thread.md +0 -178
  117. package/wiki/references/src-details/boot.md +0 -379
  118. package/wiki/references/src-details/core.md +0 -263
  119. package/wiki/references/src-details/dev-configs.md +0 -298
  120. package/wiki/references/src-details/docs.md +0 -71
  121. package/wiki/references/src-details/helpers.md +0 -211
  122. package/wiki/references/src-details/index.md +0 -86
  123. package/wiki/references/src-details/inversion.md +0 -340
@@ -1,562 +0,0 @@
1
- # Socket.IO Component
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
- | **Helper** | [`SocketIOServerHelper`](/references/helpers/socket-io) |
12
- | **Runtimes** | Node.js (`@hono/node-server`) and Bun (native) |
13
- | **Scaling** | `@socket.io/redis-adapter` + `@socket.io/redis-emitter` |
14
-
15
- ### Binding Keys
16
-
17
- | Binding Key | Constant | Type | Required | Default |
18
- |------------|----------|------|----------|---------|
19
- | `@app/socket-io/server-options` | `SocketIOBindingKeys.SERVER_OPTIONS` | `Partial<IServerOptions>` | No | See [Default Options](#default-server-options) |
20
- | `@app/socket-io/redis-connection` | `SocketIOBindingKeys.REDIS_CONNECTION` | `RedisHelper` / `DefaultRedisHelper` | **Yes** | `null` |
21
- | `@app/socket-io/authenticate-handler` | `SocketIOBindingKeys.AUTHENTICATE_HANDLER` | `TSocketIOAuthenticateFn` | **Yes** | `null` |
22
- | `@app/socket-io/validate-room-handler` | `SocketIOBindingKeys.VALIDATE_ROOM_HANDLER` | `TSocketIOValidateRoomFn` | No | `null` |
23
- | `@app/socket-io/client-connected-handler` | `SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER` | `TSocketIOClientConnectedFn` | No | `null` |
24
- | `@app/socket-io/instance` | `SocketIOBindingKeys.SOCKET_IO_INSTANCE` | `SocketIOServerHelper` | — | *Set by component* |
25
-
26
- > [!NOTE]
27
- > `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.
28
-
29
- ### Use Cases
30
-
31
- - Live notifications and alerts
32
- - Real-time chat and messaging
33
- - Collaborative editing (docs, whiteboards)
34
- - Live data streams (dashboards, monitoring)
35
- - Multiplayer game state synchronization
36
-
37
- ---
38
-
39
- ## Architecture Overview
40
-
41
- ```
42
- SocketIOComponent
43
- ┌──────────────────────────────────────────┐
44
- │ │
45
- │ binding() │
46
- │ ├── resolveBindings() │
47
- │ │ ├── SERVER_OPTIONS │
48
- │ │ ├── REDIS_CONNECTION │
49
- │ │ ├── AUTHENTICATE_HANDLER │
50
- │ │ ├── VALIDATE_ROOM_HANDLER │
51
- │ │ └── CLIENT_CONNECTED_HANDLER │
52
- │ │ │
53
- │ └── RuntimeModules.detect() │
54
- │ ├── BUN → registerBunHook() │
55
- │ └── NODE → registerNodeHook() │
56
- │ │
57
- │ (Post-start hooks execute after server) │
58
- │ ├── Creates SocketIOServerHelper │
59
- │ ├── await socketIOHelper.configure() │
60
- │ ├── Binds to SOCKET_IO_INSTANCE │
61
- │ └── Wires into server (runtime-specific)│
62
- └──────────────────────────────────────────┘
63
- ```
64
-
65
- ### Lifecycle Integration
66
-
67
- The component uses the **post-start hook** system to solve a fundamental timing problem: Socket.IO needs a running server instance, but components are initialized *before* the server starts.
68
-
69
- ```
70
- Application Lifecycle
71
- ═════════════════════
72
-
73
- ┌─────────────────┐
74
- │ preConfigure() │ ← Register SocketIOComponent here
75
- └────────┬────────┘
76
-
77
- ┌────────▼────────┐
78
- │ initialize() │ ← Component.binding() runs here
79
- │ │ Resolves bindings, registers post-start hook
80
- └────────┬────────┘
81
-
82
- ┌────────▼────────┐
83
- │ setupMiddlewares │
84
- └────────┬────────┘
85
-
86
- ┌────────▼──────────────┐
87
- │ startBunModule() OR │ ← Server starts, instance created
88
- │ startNodeModule() │
89
- └────────┬──────────────┘
90
-
91
- ┌────────▼──────────────────┐
92
- │ executePostStartHooks() │ ← SocketIOServerHelper created HERE
93
- │ └── socket-io-initialize│ Server instance is now available
94
- └───────────────────────────┘
95
- ```
96
-
97
- ### Runtime-Specific Behavior
98
-
99
- | Aspect | Node.js | Bun |
100
- |--------|---------|-----|
101
- | **Server Type** | `node:http.Server` | `Bun.Server` |
102
- | **IO Server Init** | `new IOServer(httpServer, opts)` | `new IOServer()` + `io.bind(engine)` |
103
- | **Engine** | Built-in (`socket.io`) | `@socket.io/bun-engine` (optional peer dep) |
104
- | **Request Routing** | Socket.IO attaches to HTTP server automatically | `server.reload({ fetch, websocket })` wires engine into Bun's request loop |
105
- | **WebSocket Upgrade** | Handled by `node:http.Server` upgrade event | Handled by Bun's `websocket` handler |
106
- | **Dynamic Import** | None needed | `await import('@socket.io/bun-engine')` at runtime |
107
-
108
- ---
109
-
110
- ## Setup Guide
111
-
112
- ### Step 1: Install Dependencies
113
-
114
- ```bash
115
- # Core dependency (already included via @venizia/ignis)
116
- # ioredis is required for the Redis adapter
117
-
118
- # For Bun runtime only — optional peer dependency
119
- bun add @socket.io/bun-engine
120
- ```
121
-
122
- ### Step 2: Bind Required Services
123
-
124
- In your application's `preConfigure()` method, bind the required services and register the component:
125
-
126
- ```typescript
127
- import {
128
- BaseApplication,
129
- SocketIOComponent,
130
- SocketIOBindingKeys,
131
- RedisHelper,
132
- TSocketIOAuthenticateFn,
133
- TSocketIOValidateRoomFn,
134
- TSocketIOClientConnectedFn,
135
- ValueOrPromise,
136
- } from '@venizia/ignis';
137
-
138
- export class Application extends BaseApplication {
139
- private redisHelper: RedisHelper;
140
-
141
- preConfigure(): ValueOrPromise<void> {
142
- this.setupSocketIO();
143
- // ... other setup
144
- }
145
-
146
- setupSocketIO() {
147
- // 1. Redis connection (required for adapter + emitter)
148
- this.redisHelper = new RedisHelper({
149
- name: 'socket-io-redis',
150
- host: process.env.REDIS_HOST ?? 'localhost',
151
- port: +(process.env.REDIS_PORT ?? 6379),
152
- password: process.env.REDIS_PASSWORD,
153
- autoConnect: false,
154
- });
155
-
156
- this.bind<RedisHelper>({
157
- key: SocketIOBindingKeys.REDIS_CONNECTION,
158
- }).toValue(this.redisHelper);
159
-
160
- // 2. Authentication handler (required)
161
- const authenticateFn: TSocketIOAuthenticateFn = handshake => {
162
- const token = handshake.headers.authorization;
163
- // Implement your auth logic — JWT verification, session check, etc.
164
- return !!token;
165
- };
166
-
167
- this.bind<TSocketIOAuthenticateFn>({
168
- key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,
169
- }).toValue(authenticateFn);
170
-
171
- // 3. Room validation handler (optional — joins rejected without this)
172
- const validateRoomFn: TSocketIOValidateRoomFn = ({ socket, rooms }) => {
173
- // Return the rooms that the client is allowed to join
174
- const allowedRooms = rooms.filter(room => room.startsWith('public-'));
175
- return allowedRooms;
176
- };
177
-
178
- this.bind<TSocketIOValidateRoomFn>({
179
- key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,
180
- }).toValue(validateRoomFn);
181
-
182
- // 4. Client connected handler (optional)
183
- const clientConnectedFn: TSocketIOClientConnectedFn = ({ socket }) => {
184
- console.log('Client connected:', socket.id);
185
- // Register custom event handlers on the socket
186
- };
187
-
188
- this.bind<TSocketIOClientConnectedFn>({
189
- key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,
190
- }).toValue(clientConnectedFn);
191
-
192
- // 5. Register the component — that's it!
193
- this.component(SocketIOComponent);
194
- }
195
- }
196
- ```
197
-
198
- ### Step 3: Use in Services/Controllers
199
-
200
- Inject `SocketIOServerHelper` to interact with Socket.IO:
201
-
202
- ```typescript
203
- import {
204
- BaseService,
205
- inject,
206
- SocketIOBindingKeys,
207
- SocketIOServerHelper,
208
- CoreBindings,
209
- BaseApplication,
210
- } from '@venizia/ignis';
211
-
212
- export class NotificationService extends BaseService {
213
- // Lazy getter pattern — helper is bound AFTER server starts
214
- private _io: SocketIOServerHelper | null = null;
215
-
216
- constructor(
217
- @inject({ key: CoreBindings.APPLICATION_INSTANCE })
218
- private application: BaseApplication,
219
- ) {
220
- super({ scope: NotificationService.name });
221
- }
222
-
223
- private get io(): SocketIOServerHelper {
224
- if (!this._io) {
225
- this._io = this.application.get<SocketIOServerHelper>({
226
- key: SocketIOBindingKeys.SOCKET_IO_INSTANCE,
227
- isOptional: true,
228
- }) ?? null;
229
- }
230
-
231
- if (!this._io) {
232
- throw new Error('SocketIO not initialized');
233
- }
234
-
235
- return this._io;
236
- }
237
-
238
- // Send to a specific client
239
- notifyUser(opts: { userId: string; message: string }) {
240
- this.io.send({
241
- destination: opts.userId,
242
- payload: {
243
- topic: 'notification',
244
- data: { message: opts.message, time: new Date().toISOString() },
245
- },
246
- });
247
- }
248
-
249
- // Send to a room
250
- notifyRoom(opts: { room: string; message: string }) {
251
- this.io.send({
252
- destination: opts.room,
253
- payload: {
254
- topic: 'room:update',
255
- data: { message: opts.message },
256
- },
257
- });
258
- }
259
-
260
- // Broadcast to all clients
261
- broadcastAnnouncement(opts: { message: string }) {
262
- this.io.send({
263
- payload: {
264
- topic: 'system:announcement',
265
- data: { message: opts.message },
266
- },
267
- });
268
- }
269
- }
270
- ```
271
-
272
- > [!IMPORTANT]
273
- > **Lazy getter pattern**: Since `SocketIOServerHelper` is bound via a post-start hook, it's not available during DI construction. Use a lazy getter that resolves from the application container on first access.
274
-
275
- ---
276
-
277
- ## Default Server Options
278
-
279
- The component applies these defaults if `SocketIOBindingKeys.SERVER_OPTIONS` is not bound or partially overridden:
280
-
281
- ```typescript
282
- const DEFAULT_SERVER_OPTIONS = {
283
- identifier: 'SOCKET_IO_SERVER',
284
- path: '/io',
285
- cors: {
286
- origin: '*',
287
- methods: ['GET', 'POST'],
288
- preflightContinue: false,
289
- optionsSuccessStatus: 204,
290
- credentials: true,
291
- },
292
- perMessageDeflate: {
293
- threshold: 4096,
294
- zlibDeflateOptions: { chunkSize: 10 * 1024 },
295
- zlibInflateOptions: { windowBits: 12, memLevel: 8 },
296
- clientNoContextTakeover: true,
297
- serverNoContextTakeover: true,
298
- serverMaxWindowBits: 10,
299
- concurrencyLimit: 20,
300
- },
301
- };
302
- ```
303
-
304
- | Option | Default | Description |
305
- |--------|---------|-------------|
306
- | `identifier` | `'SOCKET_IO_SERVER'` | Unique identifier for the helper instance |
307
- | `path` | `'/io'` | URL path for Socket.IO handshake/polling |
308
- | `cors.origin` | `'*'` | Allowed origins (restrict in production!) |
309
- | `cors.credentials` | `true` | Allow cookies/auth headers |
310
- | `perMessageDeflate` | Enabled | WebSocket compression settings |
311
-
312
- ### Custom Server Options
313
-
314
- Override defaults by binding custom options:
315
-
316
- ```typescript
317
- this.bind<Partial<IServerOptions>>({
318
- key: SocketIOBindingKeys.SERVER_OPTIONS,
319
- }).toValue({
320
- identifier: 'my-app-socket',
321
- path: '/socket.io',
322
- cors: {
323
- origin: ['https://myapp.com', 'https://admin.myapp.com'],
324
- methods: ['GET', 'POST'],
325
- credentials: true,
326
- },
327
- pingTimeout: 60000,
328
- pingInterval: 25000,
329
- maxHttpBufferSize: 1e6, // 1MB
330
- });
331
- ```
332
-
333
- ---
334
-
335
- ## Component Internals
336
-
337
- ### `resolveBindings()`
338
-
339
- Reads all binding keys from the DI container and validates required ones:
340
-
341
- | Binding | Validation | Error on Failure |
342
- |---------|-----------|------------------|
343
- | `SERVER_OPTIONS` | Optional, merged with defaults | — |
344
- | `REDIS_CONNECTION` | Must be `instanceof DefaultRedisHelper` | `"Invalid instance of redisConnection"` |
345
- | `AUTHENTICATE_HANDLER` | Must be a function (non-null) | `"Invalid authenticateFn"` |
346
- | `VALIDATE_ROOM_HANDLER` | Optional, checked via `isBound()` | — |
347
- | `CLIENT_CONNECTED_HANDLER` | Optional, checked via `isBound()` | — |
348
-
349
- ### `registerBunHook()`
350
-
351
- Registers a post-start hook that:
352
-
353
- 1. Dynamically imports `@socket.io/bun-engine`
354
- 2. Creates a `BunEngine` instance with CORS config bridging
355
- 3. Creates `SocketIOServerHelper` with `runtime: RuntimeModules.BUN`
356
- 4. Awaits `socketIOHelper.configure()` which waits for all Redis connections to be ready before initializing the adapter and emitter
357
- 5. Binds the helper to `SOCKET_IO_INSTANCE`
358
- 6. Calls `serverInstance.reload()` to wire the engine's `fetch` and `websocket` handlers into the running Bun server
359
-
360
- **CORS type bridging**: Socket.IO and `@socket.io/bun-engine` have slightly different CORS type definitions. The component extracts individual fields explicitly to avoid type mismatches without using `as any`.
361
-
362
- ### `registerNodeHook()`
363
-
364
- Registers a post-start hook that:
365
-
366
- 1. Gets the HTTP server instance via `getServerInstance()`
367
- 2. Creates `SocketIOServerHelper` with `runtime: RuntimeModules.NODE`
368
- 3. Awaits `socketIOHelper.configure()` which waits for all Redis connections to be ready before initializing the adapter and emitter
369
- 4. Binds the helper to `SOCKET_IO_INSTANCE`
370
-
371
- Node mode is simpler because Socket.IO natively attaches to `node:http.Server`.
372
-
373
- ---
374
-
375
- ## Post-Start Hook System
376
-
377
- The component relies on `AbstractApplication`'s post-start hook system:
378
-
379
- ### API
380
-
381
- ```typescript
382
- // Register a hook (during binding phase)
383
- application.registerPostStartHook({
384
- identifier: string, // Unique name for logging
385
- hook: () => ValueOrPromise<void>, // Async function to execute
386
- });
387
-
388
- // Get the server instance (available after start)
389
- application.getServerInstance<T>(): T | undefined;
390
- ```
391
-
392
- ### How Hooks Execute
393
-
394
- ```
395
- executePostStartHooks()
396
- ├── Hook 1: "socket-io-initialize"
397
- │ ├── performance.now() → start
398
- │ ├── await hook()
399
- │ └── log: "Executed hook | identifier: socket-io-initialize | took: 12.5 (ms)"
400
- ├── Hook 2: "another-hook"
401
- │ └── ...
402
- └── (hooks run sequentially in registration order)
403
- ```
404
-
405
- - Hooks run **sequentially** (not parallel) to guarantee ordering
406
- - Each hook is timed with `performance.now()` for diagnostics
407
- - If a hook throws, it propagates to `start()` and the server fails to start
408
-
409
- ---
410
-
411
- ## Graceful Shutdown
412
-
413
- Always shut down the Socket.IO server before stopping the application:
414
-
415
- ```typescript
416
- override async stop(): Promise<void> {
417
- // 1. Shut down Socket.IO (disconnects all clients, closes IO server, quits Redis)
418
- const socketIOHelper = this.get<SocketIOServerHelper>({
419
- key: SocketIOBindingKeys.SOCKET_IO_INSTANCE,
420
- isOptional: true,
421
- });
422
-
423
- if (socketIOHelper) {
424
- await socketIOHelper.shutdown();
425
- }
426
-
427
- // 2. Disconnect Redis helper
428
- if (this.redisHelper) {
429
- await this.redisHelper.disconnect();
430
- }
431
-
432
- // 3. Stop the HTTP/Bun server
433
- await super.stop();
434
- }
435
- ```
436
-
437
- ### Shutdown Sequence
438
-
439
- ```
440
- socketIOHelper.shutdown()
441
- ├── Disconnect all tracked clients
442
- │ ├── clearInterval(ping)
443
- │ ├── clearTimeout(authenticateTimeout)
444
- │ └── socket.disconnect()
445
- ├── clients.clear()
446
- ├── io.close() — closes the Socket.IO server
447
- └── Redis cleanup
448
- ├── redisPub.quit()
449
- ├── redisSub.quit()
450
- └── redisEmitter.quit()
451
- ```
452
-
453
- ---
454
-
455
- ## Complete Example
456
-
457
- A full working example is available at `examples/socket-io-test/`. It demonstrates:
458
-
459
- | Feature | Implementation |
460
- |---------|---------------|
461
- | Application setup | `src/application.ts` — bindings, component registration, graceful shutdown |
462
- | REST endpoints | `src/controllers/socket-test.controller.ts` — 9 endpoints for Socket.IO management |
463
- | Event handling | `src/services/socket-event.service.ts` — chat, echo, room management |
464
- | Automated test client | `client.ts` — 15+ test cases covering all features |
465
-
466
- ### REST API Endpoints
467
-
468
- | Method | Path | Description |
469
- |--------|------|-------------|
470
- | `GET` | `/socket/info` | Server status + connected client count |
471
- | `GET` | `/socket/clients` | List all connected client IDs |
472
- | `GET` | `/socket/health` | Health check (is SocketIO ready?) |
473
- | `POST` | `/socket/broadcast` | Broadcast `{ topic, data }` to all clients |
474
- | `POST` | `/socket/room/{roomId}/send` | Send `{ topic, data }` to a room |
475
- | `POST` | `/socket/client/{clientId}/send` | Send `{ topic, data }` to a specific client |
476
- | `POST` | `/socket/client/{clientId}/join` | Join client to `{ rooms: string[] }` |
477
- | `POST` | `/socket/client/{clientId}/leave` | Remove client from `{ rooms: string[] }` |
478
- | `GET` | `/socket/client/{clientId}/rooms` | List rooms a client belongs to |
479
-
480
- ### Running the Example
481
-
482
- ```bash
483
- # Start the server
484
- cd examples/socket-io-test
485
- bun run server:dev
486
-
487
- # In another terminal — run automated tests
488
- bun client.ts
489
- ```
490
-
491
- ---
492
-
493
- ## Troubleshooting
494
-
495
- ### "SocketIO not initialized"
496
-
497
- **Cause**: You're trying to use `SocketIOServerHelper` before the server has started (e.g., during DI construction).
498
-
499
- **Fix**: Use the lazy getter pattern shown in [Step 3](#step-3-use-in-servicescontrollers). Never `@inject` `SOCKET_IO_INSTANCE` directly in a constructor — it doesn't exist yet at construction time.
500
-
501
- ### "Invalid instance of redisConnection"
502
-
503
- **Cause**: The value bound to `REDIS_CONNECTION` is not an instance of `DefaultRedisHelper` (or its subclass `RedisHelper`).
504
-
505
- **Fix**: Use `RedisHelper` (recommended) or `DefaultRedisHelper`:
506
-
507
- ```typescript
508
- // Correct
509
- this.bind({ key: SocketIOBindingKeys.REDIS_CONNECTION })
510
- .toValue(new RedisHelper({ name: 'socket-io', host, port, password }));
511
-
512
- // Wrong — raw ioredis client
513
- this.bind({ key: SocketIOBindingKeys.REDIS_CONNECTION })
514
- .toValue(new Redis(6379)); // This is NOT a DefaultRedisHelper!
515
- ```
516
-
517
- ### "Cannot find module '@socket.io/bun-engine'"
518
-
519
- **Cause**: Running on Bun runtime without the optional peer dependency installed.
520
-
521
- **Fix**: `bun add @socket.io/bun-engine`
522
-
523
- ### Socket.IO connects but events aren't received
524
-
525
- **Cause**: Clients must emit `authenticate` after connecting. Unauthenticated clients are disconnected after the timeout (default: 10 seconds).
526
-
527
- **Fix**: Ensure your client emits the authenticate event:
528
-
529
- ```typescript
530
- socket.on('connect', () => {
531
- socket.emit('authenticate');
532
- });
533
-
534
- socket.on('authenticated', (data) => {
535
- // Now ready to send/receive events
536
- });
537
- ```
538
-
539
- ---
540
-
541
- ## See Also
542
-
543
- - **Related Concepts:**
544
- - [Components Overview](/guides/core-concepts/components) — Component system basics
545
- - [Application](/guides/core-concepts/application/) — Registering components
546
-
547
- - **Other Components:**
548
- - [Components Index](./index) — All built-in components
549
-
550
- - **References:**
551
- - [Socket.IO Helper](/references/helpers/socket-io) — Full `SocketIOServerHelper` + `SocketIOClientHelper` API reference
552
-
553
- - **External Resources:**
554
- - [Socket.IO Documentation](https://socket.io/docs/) — Official docs
555
- - [Socket.IO Redis Adapter](https://socket.io/docs/v4/redis-adapter/) — Horizontal scaling guide
556
- - [@socket.io/bun-engine](https://github.com/socketio/bun-engine) — Bun runtime support
557
-
558
- - **Tutorials:**
559
- - [Real-Time Chat](/guides/tutorials/realtime-chat) — Building a chat app with Socket.IO
560
-
561
- - **Changelog:**
562
- - [2026-02-06: Socket.IO Integration Fix](/changelogs/2026-02-06-socket-io-integration-fix) — Lifecycle timing fix + Bun runtime support