@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.
Files changed (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,459 +1,127 @@
1
- # WebSocket -- Setup & Configuration
1
+ ---
2
+ title: WebSocket Component
3
+ description: Wires WebSocketServerHelper into the running Bun server, with Redis Pub/Sub scaling and post-connection authentication
4
+ difficulty: intermediate
5
+ ---
2
6
 
3
- > Bun-native real-time, bidirectional communication using pure WebSocket -- with Redis Pub/Sub for horizontal scaling, application-level heartbeat, and post-connection authentication.
7
+ # WebSocket Component
4
8
 
5
- > [!IMPORTANT]
6
- > **Bun only.** The WebSocket component will throw an error if the runtime is Node.js. For Node.js support, use the [Socket.IO Component](../socket-io/) instead.
7
-
8
- ## Quick Reference
9
-
10
- | Item | Value |
11
- |------|-------|
12
- | **Package** | `@venizia/ignis` (core component) + `@venizia/ignis-helpers` (helper classes) |
13
- | **Component** | `WebSocketComponent` |
14
- | **Server Helper** | [`WebSocketServerHelper`](/extensions/helpers/websocket/) |
15
- | **Emitter Helper** | `WebSocketEmitter` (standalone Redis publisher) |
16
- | **Runtimes** | Bun only (throws on Node.js) |
17
- | **Scaling** | Redis Pub/Sub (ioredis -- single or Cluster) |
18
-
19
- #### Import Paths
9
+ `WebSocketComponent` registers a Bun-native WebSocket server on your application by creating and binding a [`WebSocketServerHelper`](/extensions/helpers/websocket/) once the HTTP server is listening.
20
10
 
21
11
  > [!IMPORTANT]
22
- > `WebSocketComponent` and `WebSocketBindingKeys` are **not** exported from the `@venizia/ignis` barrel. You must import from the `@venizia/ignis/websocket` subpath.
23
-
24
- ```typescript
25
- // From core -- subpath import (NOT from '@venizia/ignis')
26
- import {
27
- WebSocketComponent,
28
- WebSocketBindingKeys,
29
- } from '@venizia/ignis/websocket';
30
-
31
- // From helpers -- types, helpers, constants (exported from main entry)
32
- import {
33
- WebSocketServerHelper,
34
- WebSocketEmitter,
35
- WebSocketDefaults,
36
- WebSocketEvents,
37
- WebSocketChannels,
38
- WebSocketClientStates,
39
- WebSocketMessageTypes,
40
- } from '@venizia/ignis-helpers';
41
-
42
- import type {
43
- IWebSocketServerOptions,
44
- IWebSocketEmitterOptions,
45
- IWebSocketClient,
46
- IWebSocketMessage,
47
- IRedisSocketMessage,
48
- IBunWebSocketConfig,
49
- TWebSocketAuthenticateFn,
50
- TWebSocketValidateRoomFn,
51
- TWebSocketClientConnectedFn,
52
- TWebSocketClientDisconnectedFn,
53
- TWebSocketMessageHandler,
54
- TWebSocketOutboundTransformer,
55
- TWebSocketHandshakeFn,
56
- } from '@venizia/ignis-helpers';
57
- ```
58
-
59
- > [!NOTE]
60
- > `IServerOptions` (the core component's subset type) is **not** exported from `@venizia/ignis` or `@venizia/ignis/websocket`. Only `WebSocketBindingKeys` and `WebSocketComponent` are exported from the core subpath. All helper types, constants, and classes are imported from `@venizia/ignis-helpers`.
12
+ > **Bun only.** `binding()` throws if the runtime is Node.js. For Node.js support, use the [Socket.IO Component](../socket-io/) instead.
61
13
 
62
- ### Use Cases
14
+ ## In one example
63
15
 
64
- - Live notifications and alerts
65
- - Real-time chat and messaging
66
- - Collaborative editing (docs, whiteboards)
67
- - Live data streams (dashboards, monitoring)
68
- - Multiplayer game state synchronization
69
- - IoT device communication
70
- - Background job progress updates (via `WebSocketEmitter`)
71
- - Cross-service event broadcasting (via `WebSocketEmitter`)
72
-
73
- ## Setup
74
-
75
- ### Step 1: Install Dependencies
76
-
77
- ```bash
78
- # Core dependency (already included via @venizia/ignis)
79
- # ioredis is required for Redis Pub/Sub
80
- bun add ioredis
81
- ```
82
-
83
- ### Step 2: Bind Required Services
84
-
85
- In your application's `preConfigure()` method, bind the required services and register the component:
86
-
87
- #### Full Setup Example
88
16
  ```typescript
89
17
  import { BaseApplication } from '@venizia/ignis';
90
- import {
91
- WebSocketComponent,
92
- WebSocketBindingKeys,
93
- } from '@venizia/ignis/websocket';
94
- import {
95
- RedisSingleHelper,
96
- } from '@venizia/ignis-helpers';
97
- import type {
98
- TWebSocketAuthenticateFn,
99
- TWebSocketValidateRoomFn,
100
- TWebSocketClientConnectedFn,
101
- TWebSocketClientDisconnectedFn,
102
- TWebSocketMessageHandler,
103
- TWebSocketOutboundTransformer,
104
- TWebSocketHandshakeFn,
105
- IBunWebSocketConfig,
106
- ValueOrPromise,
107
- } from '@venizia/ignis-helpers';
18
+ import { WebSocketComponent, WebSocketBindingKeys } from '@venizia/ignis/websocket';
19
+ import { RedisSingleHelper, TWebSocketAuthenticateFn, ValueOrPromise } from '@venizia/ignis-helpers';
108
20
 
109
21
  export class Application extends BaseApplication {
110
- private redisHelper: RedisSingleHelper;
111
-
112
22
  preConfigure(): ValueOrPromise<void> {
113
- this.setupWebSocket();
114
- // ... other setup
115
- }
116
-
117
- setupWebSocket() {
118
- // 1. Redis connection (required for cross-instance messaging)
119
- this.redisHelper = new RedisSingleHelper({
120
- name: 'websocket-redis',
121
- host: process.env.REDIS_HOST ?? 'localhost',
122
- port: +(process.env.REDIS_PORT ?? 6379),
123
- password: process.env.REDIS_PASSWORD,
124
- autoConnect: false,
125
- });
126
-
127
- this.bind<RedisSingleHelper>({
128
- key: WebSocketBindingKeys.REDIS_CONNECTION,
129
- }).toValue(this.redisHelper);
130
-
131
- // 2. Authentication handler (required)
132
- const authenticateFn: TWebSocketAuthenticateFn = async (data) => {
133
- const token = data.token as string;
134
- if (!token) return null;
135
-
136
- const user = await verifyJWT(token);
137
- if (!user) return null;
138
-
139
- return { userId: user.id, metadata: { role: user.role } };
140
- };
141
-
142
- this.bind<TWebSocketAuthenticateFn>({
143
- key: WebSocketBindingKeys.AUTHENTICATE_HANDLER,
144
- }).toValue(authenticateFn);
145
-
146
- // 3. Room validation handler (optional -- joins rejected without this)
147
- const validateRoomFn: TWebSocketValidateRoomFn = ({ clientId, userId, rooms }) => {
148
- return rooms.filter(room => room.startsWith('public-'));
149
- };
150
-
151
- this.bind<TWebSocketValidateRoomFn>({
152
- key: WebSocketBindingKeys.VALIDATE_ROOM_HANDLER,
153
- }).toValue(validateRoomFn);
154
-
155
- // 4. Client connected handler (optional)
156
- const clientConnectedFn: TWebSocketClientConnectedFn = ({ clientId, userId }) => {
157
- console.log('Client connected:', clientId, userId);
158
- };
159
-
160
- this.bind<TWebSocketClientConnectedFn>({
161
- key: WebSocketBindingKeys.CLIENT_CONNECTED_HANDLER,
162
- }).toValue(clientConnectedFn);
163
-
164
- // 5. Client disconnected handler (optional)
165
- const clientDisconnectedFn: TWebSocketClientDisconnectedFn = ({ clientId, userId }) => {
166
- console.log('Client disconnected:', clientId, userId);
167
- };
168
-
169
- this.bind<TWebSocketClientDisconnectedFn>({
170
- key: WebSocketBindingKeys.CLIENT_DISCONNECTED_HANDLER,
171
- }).toValue(clientDisconnectedFn);
172
-
173
- // 6. Message handler (optional -- for custom events)
174
- const messageHandler: TWebSocketMessageHandler = ({ clientId, userId, message }) => {
175
- console.log('Custom event:', message.event, message.data);
176
- };
177
-
178
- this.bind<TWebSocketMessageHandler>({
179
- key: WebSocketBindingKeys.MESSAGE_HANDLER,
180
- }).toValue(messageHandler);
181
-
182
- // 7. Outbound transformer (optional -- for per-client encryption)
183
- const outboundTransformer: TWebSocketOutboundTransformer = async ({ client, event, data }) => {
184
- if (!client.encrypted) return null;
185
- // Encrypt using client's derived AES key (from ECDH handshake)
186
- const encrypted = await encryptForClient(client.id, JSON.stringify({ event, data }));
187
- return { event: 'encrypted', data: encrypted };
188
- };
189
-
190
- this.bind<TWebSocketOutboundTransformer>({
191
- key: WebSocketBindingKeys.OUTBOUND_TRANSFORMER,
192
- }).toValue(outboundTransformer);
193
-
194
- // 8. Handshake handler (optional -- required when requireEncryption is true)
195
- const handshakeFn: TWebSocketHandshakeFn = async ({ clientId, data }) => {
196
- const clientPubKey = data.publicKey as string;
197
- if (!clientPubKey) return null; // Reject -- no public key provided
198
- const salt = crypto.getRandomValues(new Uint8Array(32));
199
- const saltB64 = Buffer.from(salt).toString('base64');
200
- const aesKey = await deriveSharedSecret(clientPubKey, salt);
201
- storeClientKey(clientId, aesKey);
202
- return { serverPublicKey: serverPublicKeyB64, salt: saltB64 };
23
+ // 1. Redis connection (required, used for cross-instance Pub/Sub)
24
+ this.bind({ key: WebSocketBindingKeys.REDIS_CONNECTION }).toValue(
25
+ new RedisSingleHelper({ name: 'websocket-redis', host: 'localhost', port: 6379, autoConnect: false }),
26
+ );
27
+
28
+ // 2. Authenticate handler (required, decides accept/reject per client)
29
+ const authenticateFn: TWebSocketAuthenticateFn = async data => {
30
+ const user = await verifyJWT(data.token as string);
31
+ return user ? { userId: user.id } : null;
203
32
  };
33
+ this.bind({ key: WebSocketBindingKeys.AUTHENTICATE_HANDLER }).toValue(authenticateFn);
204
34
 
205
- this.bind<TWebSocketHandshakeFn>({
206
- key: WebSocketBindingKeys.HANDSHAKE_HANDLER,
207
- }).toValue(handshakeFn);
208
-
209
- // 9. Server options (optional -- customize defaults)
210
- this.bind({
211
- key: WebSocketBindingKeys.SERVER_OPTIONS,
212
- }).toValue({
213
- identifier: 'my-app-websocket',
214
- requireEncryption: true,
215
- });
216
-
217
- // 10. Register the component
35
+ // 3. Register (binding() validates the two bindings above and defers the rest)
218
36
  this.component(WebSocketComponent);
219
37
  }
220
38
  }
221
39
  ```
222
40
 
223
- ## Configuration
41
+ `WebSocketComponent` and `WebSocketBindingKeys` come from the `@venizia/ignis/websocket` subpath. They are **not** exported from the `@venizia/ignis` root barrel. Helper types (`TWebSocketAuthenticateFn`, `WebSocketServerHelper`, `WebSocketEmitter`, `WebSocketDefaults`, ...) come from `@venizia/ignis-helpers`.
224
42
 
225
- The core component's `IServerOptions` interface controls the WebSocket server setup. Default values come from `DEFAULT_SERVER_OPTIONS` and `WebSocketDefaults`:
43
+ ## How it works
226
44
 
227
- ```typescript
228
- {
229
- identifier: 'WEBSOCKET_SERVER',
230
- path: '/ws', // WebSocketDefaults.PATH
231
- defaultRooms: [ // Joined automatically after auth
232
- 'ws-default', // WebSocketDefaults.ROOM
233
- 'ws-notification', // WebSocketDefaults.NOTIFICATION_ROOM
234
- ],
235
- heartbeatInterval: 30000, // 30 seconds (WebSocketDefaults.HEARTBEAT_INTERVAL)
236
- heartbeatTimeout: 90000, // 90 seconds (WebSocketDefaults.HEARTBEAT_TIMEOUT)
237
- requireEncryption: false,
238
- serverOptions: { // Bun native WebSocket config (IBunWebSocketConfig)
239
- sendPings: true, // WebSocketDefaults.SEND_PINGS
240
- idleTimeout: 60, // WebSocketDefaults.IDLE_TIMEOUT (seconds)
241
- maxPayloadLength: 131072, // WebSocketDefaults.MAX_PAYLOAD_LENGTH (128 KB)
242
- },
243
- }
244
- ```
45
+ - **Two-phase startup solves a timing problem.** `binding()` runs during `initialize()`, before any Bun server exists. It validates bindings and registers a post-start hook. The actual `WebSocketServerHelper` is only constructed once `executePostStartHooks()` runs, after `Bun.serve()` has produced a live server instance.
46
+ - **Two bindings gate startup.** `REDIS_CONNECTION` and `AUTHENTICATE_HANDLER` are both required. `REDIS_CONNECTION` must be an `AbstractRedisHelper` instance. `binding()` throws synchronously if either is missing or the wrong type - before the post-start hook is even registered.
47
+ - **Runtime is checked first, fast.** `RuntimeModules.detect()` runs at the top of `binding()`. On Node.js it throws immediately, so a misconfigured app fails at startup, not on first connection.
48
+ - **The instance appears only after start.** The post-start hook binds the configured helper to `WebSocketBindingKeys.WEBSOCKET_INSTANCE`. It does not exist during DI construction. Inject it lazily from a service or controller, never via `@inject` in a constructor.
49
+ - **A custom `fetch` handler splits traffic.** After the post-start hook runs, `server.reload()` swaps in a new handler. It routes WebSocket upgrade requests - `GET <path>` with an `Upgrade: websocket` header - to Bun's native handler.
50
+ - Everything else goes to the existing Hono server, unchanged.
245
51
 
246
- > [!NOTE]
247
- > The `DEFAULT_SERVER_OPTIONS` in the core component only sets `identifier` and `path`. The remaining defaults (`defaultRooms`, `heartbeatInterval`, `heartbeatTimeout`, `serverOptions`) come from `WebSocketDefaults` applied by the helper constructor.
52
+ ## Common tasks
248
53
 
249
- To customize options, bind a partial options object before registering the component:
54
+ ### Customize the server path, rooms, and heartbeat
250
55
 
251
- #### Custom Server Options Example
252
- ```typescript
253
- import { WebSocketBindingKeys } from '@venizia/ignis/websocket';
56
+ Bind a partial options object to `SERVER_OPTIONS` before registering the component. Unset fields fall back to `WebSocketDefaults`.
254
57
 
255
- this.bind({
256
- key: WebSocketBindingKeys.SERVER_OPTIONS,
257
- }).toValue({
258
- identifier: 'my-app-websocket',
58
+ ```typescript
59
+ this.bind({ key: WebSocketBindingKeys.SERVER_OPTIONS }).toValue({
259
60
  path: '/realtime',
260
- defaultRooms: ['general', 'announcements'], // Override default rooms
261
- heartbeatInterval: 20000, // More frequent heartbeats
262
- heartbeatTimeout: 60000, // Shorter timeout
263
- requireEncryption: true, // Require ECDH handshake
264
- serverOptions: {
265
- maxPayloadLength: 2097152, // 2 MB max payload
266
- backpressureLimit: 2097152, // 2 MB backpressure limit
267
- },
61
+ defaultRooms: ['general', 'announcements'],
62
+ heartbeatInterval: 20_000,
63
+ heartbeatTimeout: 60_000,
268
64
  });
269
65
  ```
270
66
 
271
- > [!NOTE]
272
- > `authTimeout` and `encryptedBatchLimit` are properties of the helper's `IWebSocketServerOptions`, not the core component's `IServerOptions`. The component uses the helper defaults for those (`5000` ms and `10` respectively). If you need to customize them, you must set them on the helper directly (not via binding keys).
273
-
274
- ### `WebSocketDefaults` Constants
275
-
276
- All tunable defaults are defined in the `WebSocketDefaults` class. The helper falls back to these when no explicit value is provided.
277
-
278
- | Constant | Value | Description |
279
- |----------|-------|-------------|
280
- | `PATH` | `'/ws'` | Default WebSocket endpoint path |
281
- | `ROOM` | `'ws-default'` | Default room name |
282
- | `NOTIFICATION_ROOM` | `'ws-notification'` | Default notification room name |
283
- | `BROADCAST_TOPIC` | `'ws:internal:broadcast'` | Internal Bun pub/sub broadcast topic |
284
- | `MAX_PAYLOAD_LENGTH` | `131072` (128 KB) | Maximum message payload size |
285
- | `IDLE_TIMEOUT` | `60` | Bun idle timeout in seconds |
286
- | `BACKPRESSURE_LIMIT` | `1048576` (1 MB) | Bun backpressure limit |
287
- | `SEND_PINGS` | `true` | Enable WebSocket pings |
288
- | `PUBLISH_TO_SELF` | `false` | Whether server receives its own publishes |
289
- | `AUTH_TIMEOUT` | `5000` (5 s) | Time to authenticate before disconnect |
290
- | `HEARTBEAT_INTERVAL` | `30000` (30 s) | Interval between heartbeat sweeps |
291
- | `HEARTBEAT_TIMEOUT` | `90000` (90 s) | Disconnect after 3 missed heartbeats |
292
- | `ENCRYPTED_BATCH_LIMIT` | `10` | Max concurrent encryption operations |
293
-
294
- > [!TIP]
295
- > `MAX_PAYLOAD_LENGTH`, `IDLE_TIMEOUT`, `BACKPRESSURE_LIMIT`, `SEND_PINGS`, and `PUBLISH_TO_SELF` are Bun-native WebSocket settings passed via `serverOptions` inside `IServerOptions`. The rest are application-level settings on `IWebSocketServerOptions` (the helper constructor options).
296
-
297
- #### Full `IBunWebSocketConfig` Interface
298
- ```typescript
299
- /** Bun WebSocket native configuration options */
300
- interface IBunWebSocketConfig {
301
- perMessageDeflate?: boolean;
302
- maxPayloadLength?: number; // Default: 128 KB (131072)
303
- idleTimeout?: number; // Default: 60 s
304
- backpressureLimit?: number; // Default: 1 MB (1048576)
305
- closeOnBackpressureLimit?: boolean;
306
- sendPings?: boolean; // Default: true
307
- publishToSelf?: boolean; // Default: false
308
- }
309
- ```
67
+ ### Add optional lifecycle callbacks
310
68
 
311
- These options are passed directly to Bun's native WebSocket handler. Set them via `serverOptions` inside the options bound to `WebSocketBindingKeys.SERVER_OPTIONS`.
69
+ `VALIDATE_ROOM_HANDLER`, `CLIENT_CONNECTED_HANDLER`, `CLIENT_DISCONNECTED_HANDLER`, and `MESSAGE_HANDLER` are all optional bindings.
312
70
 
313
- #### Full `IServerOptions` Interface (Core Component)
314
71
  ```typescript
315
- interface IServerOptions {
316
- identifier: string; // Default: 'WEBSOCKET_SERVER'
317
- path?: string; // Default: '/ws' (from WebSocketDefaults.PATH)
318
- defaultRooms?: string[]; // Default: ['ws-default', 'ws-notification']
319
- serverOptions?: IBunWebSocketConfig; // Bun native WebSocket config
320
- heartbeatInterval?: number; // Default: 30000 (30 s)
321
- heartbeatTimeout?: number; // Default: 90000 (90 s)
322
- requireEncryption?: boolean; // Default: false
323
- }
72
+ this.bind({ key: WebSocketBindingKeys.VALIDATE_ROOM_HANDLER }).toValue(
73
+ ({ rooms }: { rooms: string[] }) => rooms.filter(room => room.startsWith('public-')),
74
+ );
324
75
  ```
325
76
 
326
- > [!NOTE]
327
- > `IServerOptions` is the **core component's** options type. It is a subset of the helper's `IWebSocketServerOptions`, which additionally includes `server`, `redisConnection`, callback functions, `authTimeout`, and `encryptedBatchLimit`. The component fills in those extra fields from the DI container before constructing the helper.
328
-
329
- ## Binding Keys
330
-
331
- | Binding Key | Constant | Type | Required | Default |
332
- |------------|----------|------|----------|---------|
333
- | `@app/websocket/server-options` | `WebSocketBindingKeys.SERVER_OPTIONS` | `Partial<IServerOptions>` | No | See [Configuration](#configuration) |
334
- | `@app/websocket/redis-connection` | `WebSocketBindingKeys.REDIS_CONNECTION` | `IRedisHelper` | **Yes** | `null` |
335
- | `@app/websocket/authenticate-handler` | `WebSocketBindingKeys.AUTHENTICATE_HANDLER` | `TWebSocketAuthenticateFn` | **Yes** | `null` |
336
- | `@app/websocket/validate-room-handler` | `WebSocketBindingKeys.VALIDATE_ROOM_HANDLER` | `TWebSocketValidateRoomFn` | No | `null` |
337
- | `@app/websocket/client-connected-handler` | `WebSocketBindingKeys.CLIENT_CONNECTED_HANDLER` | `TWebSocketClientConnectedFn` | No | `null` |
338
- | `@app/websocket/client-disconnected-handler` | `WebSocketBindingKeys.CLIENT_DISCONNECTED_HANDLER` | `TWebSocketClientDisconnectedFn` | No | `null` |
339
- | `@app/websocket/message-handler` | `WebSocketBindingKeys.MESSAGE_HANDLER` | `TWebSocketMessageHandler` | No | `null` |
340
- | `@app/websocket/outbound-transformer` | `WebSocketBindingKeys.OUTBOUND_TRANSFORMER` | `TWebSocketOutboundTransformer` | No | `null` |
341
- | `@app/websocket/handshake-handler` | `WebSocketBindingKeys.HANDSHAKE_HANDLER` | `TWebSocketHandshakeFn` | No* | `null` |
342
- | `@app/websocket/instance` | `WebSocketBindingKeys.WEBSOCKET_INSTANCE` | `WebSocketServerHelper` | -- | *Set by component* |
343
-
344
- > [!NOTE]
345
- > `HANDSHAKE_HANDLER` is required when `IServerOptions.requireEncryption` is `true`. It performs ECDH key exchange during authentication.
346
-
347
- > [!NOTE]
348
- > `WEBSOCKET_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 WebSocket.
349
-
350
- ### Callback Type Signatures
351
-
352
- | Binding Key | Callback Type | Required | Description |
353
- |-------------|--------------|----------|-------------|
354
- | `AUTHENTICATE_HANDLER` | `TWebSocketAuthenticateFn` | **Yes** | Returns <code v-pre>{ userId, metadata }</code> or `null`/`false` to reject |
355
- | `VALIDATE_ROOM_HANDLER` | `TWebSocketValidateRoomFn` | No | Filters requested rooms, returns allowed rooms |
356
- | `CLIENT_CONNECTED_HANDLER` | `TWebSocketClientConnectedFn` | No | Called after successful authentication |
357
- | `CLIENT_DISCONNECTED_HANDLER` | `TWebSocketClientDisconnectedFn` | No | Called on disconnect (after cleanup) |
358
- | `MESSAGE_HANDLER` | `TWebSocketMessageHandler` | No | Handles non-system messages from authenticated clients |
359
- | `OUTBOUND_TRANSFORMER` | `TWebSocketOutboundTransformer` | No | Transforms outbound messages (e.g., per-client encryption) |
360
- | `HANDSHAKE_HANDLER` | `TWebSocketHandshakeFn` | When `requireEncryption: true` | Returns <code v-pre>{ serverPublicKey, salt }</code> or `null`/`false` to reject |
361
-
362
- #### `TWebSocketAuthenticateFn`
363
- ```typescript
364
- type TWebSocketAuthenticateFn<
365
- AuthDataType extends Record<string, unknown> = Record<string, unknown>,
366
- MetadataType extends Record<string, unknown> = Record<string, unknown>,
367
- > = (
368
- opts: AuthDataType,
369
- ) => ValueOrPromise<{ userId?: string; metadata?: MetadataType } | null | false>;
370
- ```
371
-
372
- Receives the `data` field from the client's `authenticate` event. Return <code v-pre>{ userId, metadata }</code> on success, or `null`/`false` to reject (closes with code `4003`).
373
-
374
- #### `TWebSocketValidateRoomFn`
375
- ```typescript
376
- type TWebSocketValidateRoomFn = (opts: {
377
- clientId: string;
378
- userId?: string;
379
- rooms: string[];
380
- }) => ValueOrPromise<string[]>;
381
- ```
382
-
383
- Called when a client sends a `join` event. Receives the sanitized room list (internal `ws:` prefix rooms are already filtered out). Return the subset of rooms the client is allowed to join.
384
-
385
77
  > [!WARNING]
386
- > If no `validateRoomFn` is bound, **all join requests are rejected**. You must bind this handler if you want clients to join custom rooms.
78
+ > Without `VALIDATE_ROOM_HANDLER` bound, **every** client `join` request is rejected.
387
79
 
388
- #### `TWebSocketClientConnectedFn`
389
- ```typescript
390
- type TWebSocketClientConnectedFn<
391
- MetadataType extends Record<string, unknown> = Record<string, unknown>,
392
- > = (opts: {
393
- clientId: string;
394
- userId?: string;
395
- metadata?: MetadataType;
396
- }) => ValueOrPromise<void>;
397
- ```
80
+ ### Inject the running instance in a service
398
81
 
399
- Called after a client has been fully authenticated, joined default rooms, and received the `connected` event. Errors thrown here are caught and logged -- they do not disconnect the client.
82
+ `WEBSOCKET_INSTANCE` is bound after the server starts, so resolve it lazily rather than through the constructor.
400
83
 
401
- #### `TWebSocketClientDisconnectedFn`
402
84
  ```typescript
403
- type TWebSocketClientDisconnectedFn = (opts: {
404
- clientId: string;
405
- userId?: string;
406
- }) => ValueOrPromise<void>;
85
+ private get ws(): WebSocketServerHelper {
86
+ return this.application.get<WebSocketServerHelper>({
87
+ key: WebSocketBindingKeys.WEBSOCKET_INSTANCE,
88
+ });
89
+ }
407
90
  ```
408
91
 
409
- Called after internal cleanup (auth timer cleared, removed from user/room indexes, removed from clients map). Errors thrown here are caught and logged.
92
+ ### Send from a process with no WebSocket server
410
93
 
411
- #### `TWebSocketMessageHandler`
412
- ```typescript
413
- type TWebSocketMessageHandler = (opts: {
414
- clientId: string;
415
- userId?: string;
416
- message: IWebSocketMessage;
417
- }) => ValueOrPromise<void>;
418
- ```
94
+ Background workers, cron jobs, and other microservices use the standalone `WebSocketEmitter` - it uses the same Redis connection with no local server required.
419
95
 
420
- Called for any message from an authenticated client whose `event` is not a system event (`authenticate`, `connected`, `disconnect`, `join`, `leave`, `error`, `heartbeat`, `encrypted`). If no handler is bound, non-system messages are silently dropped.
421
-
422
- #### `TWebSocketOutboundTransformer`
423
96
  ```typescript
424
- type TWebSocketOutboundTransformer<
425
- DataType = unknown,
426
- MetadataType extends Record<string, unknown> = Record<string, unknown>,
427
- > = (opts: {
428
- client: IWebSocketClient<MetadataType>;
429
- event: string;
430
- data: DataType;
431
- }) => ValueOrPromise<TNullable<{ event: string; data: DataType }>>;
97
+ import { WebSocketEmitter } from '@venizia/ignis-helpers';
98
+
99
+ const emitter = new WebSocketEmitter({ redisConnection: redisHelper });
100
+ await emitter.configure();
101
+ await emitter.toRoom({ room: 'dashboard-viewers', event: 'data:update', data: { cpu: 42 } });
432
102
  ```
433
103
 
434
- Intercepts every outbound message **to encrypted clients only** before `socket.send()`. Return `null` to send the original <code v-pre>{ event, data }</code> unchanged, or return a transformed <code v-pre>{ event, data }</code> (e.g., <code v-pre>{ event: 'encrypted', data: ciphertext }</code>).
104
+ ### Require encrypted sessions
435
105
 
436
- > [!NOTE]
437
- > The transformer is only called for clients where `client.encrypted === true`. Non-encrypted clients bypass this entirely (zero overhead).
106
+ Set `requireEncryption: true` and bind a `HANDSHAKE_HANDLER`. It becomes required the moment encryption is turned on.
438
107
 
439
- #### `TWebSocketHandshakeFn`
440
108
  ```typescript
441
- type TWebSocketHandshakeFn<
442
- AuthDataType extends Record<string, unknown> = Record<string, unknown>,
443
- > = (opts: {
444
- clientId: string;
445
- userId?: string;
446
- data: AuthDataType;
447
- }) => ValueOrPromise<{ serverPublicKey: string; salt: string } | null | false>;
109
+ this.bind({ key: WebSocketBindingKeys.SERVER_OPTIONS }).toValue({ requireEncryption: true });
110
+ this.bind({ key: WebSocketBindingKeys.HANDSHAKE_HANDLER }).toValue(handshakeFn);
448
111
  ```
449
112
 
450
- Called during authentication when `requireEncryption` is `true`. Receives the same `data` payload as `authenticateFn`. Return <code v-pre>{ serverPublicKey, salt }</code> on success -- these are included in the `connected` event sent to the client. Return `null`/`false` to reject (closes with code `4004`).
113
+ ## See also
114
+
115
+ - [Usage & Examples](./usage) - injecting the helper, `WebSocketEmitter`, wire protocol, client tracking, delivery strategy
116
+ - [Full Reference](./api) - lifecycle diagram, binding keys, configuration options, `WebSocketEmitter` API, internals
117
+ - [Error Reference](./errors) - error conditions and troubleshooting
118
+ - [WebSocketServerHelper](/extensions/helpers/websocket/) - the helper this component wires in
119
+ - [Socket.IO Component](../socket-io/) - Node.js-compatible alternative
120
+ - [Bun WebSocket Documentation](https://bun.sh/docs/api/websockets) - official Bun WebSocket API reference
451
121
 
452
- ## See Also
122
+ **Files:**
453
123
 
454
- - [Usage & Examples](./usage) - Server-side usage, emitter, wire protocol, client tracking, and delivery strategy
455
- - [API Reference](./api) - Architecture, WebSocketEmitter API, and internals
456
- - [Error Reference](./errors) - Error conditions table and troubleshooting
457
- - [WebSocketServerHelper](/extensions/helpers/websocket/) - Helper API documentation
458
- - [Socket.IO Component](../socket-io/) - Node.js-compatible alternative with Socket.IO
459
- - [Bun WebSocket Documentation](https://bun.sh/docs/api/websockets) - Official Bun WebSocket API reference
124
+ - [`packages/core-server/src/components/websocket/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/websocket/component.ts) - `WebSocketComponent`
125
+ - [`packages/core-server/src/components/websocket/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/websocket/common/keys.ts) - `WebSocketBindingKeys`
126
+ - [`packages/core-server/src/components/websocket/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/websocket/common/types.ts) - `IServerOptions`, `DEFAULT_SERVER_OPTIONS`
127
+ - [`packages/core-server/src/components/websocket/handlers/bun.handler.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/websocket/handlers/bun.handler.ts) - `createBunFetchHandler`