@vercube/ws 0.0.22 → 0.0.23

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 (3) hide show
  1. package/README.md +10 -5
  2. package/dist/index.mjs +216 -216
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -3,10 +3,10 @@
3
3
  <br>
4
4
  <br>
5
5
 
6
- # Vercube
7
-
8
- Next generation HTTP framework
9
-
6
+ # Vercube
7
+
8
+ Next generation HTTP framework
9
+
10
10
  <a href="https://www.npmjs.com/package/@vercube/auth">
11
11
  <img src="https://img.shields.io/npm/v/%40vercube%2Fauth?style=for-the-badge&logo=npm&color=%23767eff" alt="npm"/>
12
12
  </a>
@@ -16,6 +16,9 @@
16
16
  <a href="https://github.com/vercube/vercube/blob/main/LICENSE" target="_blank">
17
17
  <img src="https://img.shields.io/npm/l/%40vercube%2Fauth?style=for-the-badge&color=%23767eff" alt="License"/>
18
18
  </a>
19
+ <a href="https://codecov.io/gh/vercube/vercube" target="_blank">
20
+ <img src="https://img.shields.io/codecov/c/github/vercube/vercube?style=for-the-badge&color=%23767eff" alt="Coverage"/>
21
+ </a>
19
22
  <br/>
20
23
  <br/>
21
24
  </div>
@@ -23,7 +26,9 @@
23
26
  An ultra-efficient JavaScript server framework that runs anywhere - Node.js, Bun, or Deno - with unmatched flexibility and complete configurability for developers who refuse to sacrifice speed or control.
24
27
 
25
28
  ## <a name="module">Websocket module</a>
29
+
26
30
  The Websocket module enables you to set up websocket connections on your application. It offers a collection of decorators that enable you to listen to incoming messages, emit/broadcast messages and more. It works based on namespaces.
27
31
 
28
32
  ## <a name="documentation">📖 Documentation</a>
29
- Comprehensive documentation is available at [vercube.dev](https://vercube.dev). There you'll find detailed module descriptions, project information, guides, and everything else you need to know about Vercube.
33
+
34
+ Comprehensive documentation is available at [vercube.dev](https://vercube.dev). There you'll find detailed module descriptions, project information, guides, and everything else you need to know about Vercube.
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import "node:module";
2
2
  import { BadRequestError, BasePlugin, HttpServer, ValidationProvider, initializeMetadata, initializeMetadataMethod } from "@vercube/core";
3
3
  import { BaseDecorator, Identity, Inject, InjectOptional, createDecorator } from "@vercube/di";
4
- import { plugin } from "crossws/server";
5
- import { defineHooks } from "crossws";
6
4
  import { Logger } from "@vercube/logger";
5
+ import { defineHooks } from "crossws";
6
+ import { plugin } from "crossws/server";
7
7
 
8
8
  //#region rolldown:runtime
9
9
  var __create = Object.create;
@@ -64,12 +64,8 @@ let WebsocketTypes;
64
64
  })(WebsocketTypes || (WebsocketTypes = {}));
65
65
 
66
66
  //#endregion
67
- //#region src/Symbols/WebsocketSymbols.ts
68
- const $WebsocketService = Identity("WebsocketService");
69
-
70
- //#endregion
71
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/decorate.js
72
- var require_decorate = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/decorate.js": ((exports, module) => {
67
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.3/node_modules/@oxc-project/runtime/src/helpers/decorate.js
68
+ var require_decorate = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.3/node_modules/@oxc-project/runtime/src/helpers/decorate.js": ((exports, module) => {
73
69
  function __decorate(decorators, target, key, desc) {
74
70
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
75
71
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -80,12 +76,202 @@ var require_decorate = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@o
80
76
  }) });
81
77
 
82
78
  //#endregion
83
- //#region src/Decorators/Message.ts
79
+ //#region src/Services/WebsocketService.ts
84
80
  var import_decorate$5 = /* @__PURE__ */ __toESM(require_decorate(), 1);
85
81
  /**
82
+ * WebsocketService class responsible for dealing with Websocket connections.
83
+ *
84
+ * This class is responsible for:
85
+ * - Registering namespaces and accepting websocket connections for them
86
+ * - Registering event handlers and handling them
87
+ */
88
+ var WebsocketService = class {
89
+ /**
90
+ * Http Server for injecting the server plugin
91
+ */
92
+ gHttpServer;
93
+ /**
94
+ * Validation provider for running the schema validation
95
+ */
96
+ gValidationProvider;
97
+ gLogger;
98
+ /**
99
+ * Internal namespace registry
100
+ */
101
+ fNamespaces = {};
102
+ /**
103
+ * Internal handlers registry
104
+ */
105
+ fHandlers = {
106
+ [WebsocketTypes.HandlerAction.CONNECTION]: {},
107
+ [WebsocketTypes.HandlerAction.MESSAGE]: {}
108
+ };
109
+ /**
110
+ * Register a new namespace.
111
+ *
112
+ * @param {string} path - The namespace path to register.
113
+ * @returns {void}
114
+ */
115
+ registerNamespace(path) {
116
+ if (!this?.fNamespaces?.[path.toLowerCase()]) this.fNamespaces[path.toLowerCase()] = [];
117
+ }
118
+ /**
119
+ * Register a new handler for a namespace or event.
120
+ *
121
+ * @param {WebsocketTypes.HandlerAction} action - The handler action type.
122
+ * @param {string} namespace - The namespace to register the handler for.
123
+ * @param {WebsocketTypes.HandlerAttributes} handler - The handler attributes.
124
+ * @returns {void}
125
+ */
126
+ registerHandler(action, namespace, handler) {
127
+ const normalizedNamespace = namespace.toLowerCase();
128
+ if (action === WebsocketTypes.HandlerAction.CONNECTION) this.fHandlers[action][normalizedNamespace] = handler;
129
+ if (action === WebsocketTypes.HandlerAction.MESSAGE) {
130
+ const event = handler.event;
131
+ if (!event) {
132
+ this.gLogger?.warn("WebsocketService::registerHandler", `Cannot register message handler without an event name for namespace "${normalizedNamespace}".`);
133
+ return;
134
+ }
135
+ if (!this.fHandlers[action][normalizedNamespace]) this.fHandlers[action][normalizedNamespace] = {};
136
+ this.fHandlers[action][normalizedNamespace][event] = handler;
137
+ }
138
+ this.registerNamespace(normalizedNamespace);
139
+ }
140
+ /**
141
+ * Broadcast a message to all peers in the same namespace (including the sender).
142
+ *
143
+ * @param {Peer} peer - The sender peer (used to determine the namespace).
144
+ * @param {unknown} message - The message to broadcast.
145
+ * @returns {void}
146
+ */
147
+ broadcast(peer, message) {
148
+ const namespace = peer.namespace?.toLowerCase();
149
+ if (!namespace) return;
150
+ const peers = this.fNamespaces[namespace];
151
+ if (!peers || peers.length === 0) return;
152
+ for (const p of peers) p.send(message);
153
+ }
154
+ /**
155
+ * Emit a message to a single peer.
156
+ *
157
+ * @param {Peer} peer - The peer to send the message to.
158
+ * @param {unknown} message - The message to send.
159
+ * @returns {void}
160
+ */
161
+ emit(peer, message) {
162
+ peer.send(message);
163
+ }
164
+ /**
165
+ * Broadcast a message to all peers in the same namespace except the sender.
166
+ *
167
+ * @param {Peer} peer - The sender peer (used to determine the namespace).
168
+ * @param {unknown} message - The message to broadcast.
169
+ * @returns {void}
170
+ */
171
+ broadcastOthers(peer, message) {
172
+ const namespace = peer.namespace?.toLowerCase();
173
+ if (!namespace) return;
174
+ const peers = this.fNamespaces[namespace];
175
+ if (!peers || peers.length === 0) return;
176
+ for (const p of peers) if (p.id !== peer.id) p.send(message);
177
+ }
178
+ /**
179
+ * Initialize the websocket service and attach the server plugin.
180
+ *
181
+ * @returns {void}
182
+ */
183
+ initialize() {
184
+ const hooks = defineHooks({
185
+ upgrade: async (request) => {
186
+ const url = new URL(request.url);
187
+ const namespace = url.pathname;
188
+ const parameters = Object.fromEntries(url.searchParams.entries());
189
+ const isNamespaceRegistered = !!this.fNamespaces?.[namespace?.toLowerCase()];
190
+ if (!isNamespaceRegistered) {
191
+ this.gLogger?.warn("WebsocketService::initialize", `Namespace "${namespace}" is not registered. Connection rejected.`);
192
+ return new Response("Namespace not registered", { status: 403 });
193
+ }
194
+ const handler = this.fHandlers[WebsocketTypes.HandlerAction.CONNECTION]?.[namespace];
195
+ if (handler) try {
196
+ const result = await handler.callback(parameters, request);
197
+ if (result === false) return new Response("Unauthorized", { status: 403 });
198
+ } catch (error) {
199
+ if (error instanceof Error) return new Response(error.message, { status: 403 });
200
+ return new Response("Unknown error", { status: 403 });
201
+ }
202
+ return {
203
+ namespace,
204
+ headers: {}
205
+ };
206
+ },
207
+ open: async (peer) => {
208
+ const namespace = peer.namespace?.toLowerCase();
209
+ if (namespace && this.fNamespaces[namespace]) this.fNamespaces[namespace].push(peer);
210
+ },
211
+ message: async (peer, message) => {
212
+ await this.handleMessage(peer, message);
213
+ },
214
+ close: async (peer) => {
215
+ const namespace = peer.namespace?.toLowerCase();
216
+ if (namespace && this.fNamespaces[namespace]) {
217
+ const peers = this.fNamespaces[namespace];
218
+ this.fNamespaces[namespace] = peers.filter((p) => p.id !== peer.id);
219
+ }
220
+ },
221
+ error: async (peer, error) => {
222
+ this.gLogger?.error("WebsocketService::initialize", `Error: ${error.message}`, { peer });
223
+ }
224
+ });
225
+ const serverPlugin = plugin(hooks);
226
+ this.gHttpServer.addPlugin(serverPlugin);
227
+ }
228
+ /**
229
+ * Handle an incoming websocket message for a peer.
230
+ *
231
+ * @param {Peer} peer - The peer receiving the message.
232
+ * @param {Message} rawMessage - The raw websocket message.
233
+ * @returns {Promise<void>}
234
+ */
235
+ async handleMessage(peer, rawMessage) {
236
+ try {
237
+ const msg = JSON.parse(rawMessage.text());
238
+ const namespace = peer.namespace?.toLowerCase();
239
+ const event = msg.event;
240
+ const data = msg.data;
241
+ const handler = this.fHandlers[WebsocketTypes.HandlerAction.MESSAGE]?.[namespace]?.[event];
242
+ if (!handler) {
243
+ this.gLogger?.warn("WebsocketService::handleMessage", `No message handler for event "${event}" in namespace "${namespace}"`);
244
+ return;
245
+ }
246
+ if (handler.schema) {
247
+ if (!this.gValidationProvider) {
248
+ this.gLogger?.warn("WebsocketService::handleMessage", "ValidationProvider is not registered");
249
+ return;
250
+ }
251
+ const result = await this.gValidationProvider.validate(handler.schema, data);
252
+ if (result?.issues?.length) throw new BadRequestError("Websocket message validation error", result.issues);
253
+ }
254
+ await handler.callback(data, peer);
255
+ } catch (error) {
256
+ this.gLogger?.error("WebsocketService::handleMessage", `Failed to process message: ${error}`);
257
+ }
258
+ }
259
+ };
260
+ (0, import_decorate$5.default)([Inject(HttpServer)], WebsocketService.prototype, "gHttpServer", void 0);
261
+ (0, import_decorate$5.default)([InjectOptional(ValidationProvider)], WebsocketService.prototype, "gValidationProvider", void 0);
262
+ (0, import_decorate$5.default)([InjectOptional(Logger)], WebsocketService.prototype, "gLogger", void 0);
263
+
264
+ //#endregion
265
+ //#region src/Symbols/WebsocketSymbols.ts
266
+ const $WebsocketService = Identity("WebsocketService");
267
+
268
+ //#endregion
269
+ //#region src/Decorators/Message.ts
270
+ var import_decorate$4 = /* @__PURE__ */ __toESM(require_decorate(), 1);
271
+ /**
86
272
  * A decorator class for listening to websocket messages under
87
273
  * a specific event.
88
- *
274
+ *
89
275
  * This class extends the BaseDecorator and is used to listen
90
276
  * to websocket messages under a specific event.
91
277
  *
@@ -117,7 +303,7 @@ var MessageDecorator = class extends BaseDecorator {
117
303
  });
118
304
  }
119
305
  };
120
- (0, import_decorate$5.default)([InjectOptional($WebsocketService)], MessageDecorator.prototype, "gWebsocketService", void 0);
306
+ (0, import_decorate$4.default)([InjectOptional($WebsocketService)], MessageDecorator.prototype, "gWebsocketService", void 0);
121
307
  /**
122
308
  * A decorator function for listening to websocket messages under a specific event.
123
309
  *
@@ -133,7 +319,7 @@ function Message(params) {
133
319
 
134
320
  //#endregion
135
321
  //#region src/Decorators/Emit.ts
136
- var import_decorate$4 = /* @__PURE__ */ __toESM(require_decorate(), 1);
322
+ var import_decorate$3 = /* @__PURE__ */ __toESM(require_decorate(), 1);
137
323
  /**
138
324
  * A decorator class for emitting websocket messages to the peer.
139
325
  *
@@ -162,7 +348,7 @@ var EmitDecorator = class extends BaseDecorator {
162
348
  };
163
349
  }
164
350
  };
165
- (0, import_decorate$4.default)([InjectOptional($WebsocketService)], EmitDecorator.prototype, "gWebsocketService", void 0);
351
+ (0, import_decorate$3.default)([InjectOptional($WebsocketService)], EmitDecorator.prototype, "gWebsocketService", void 0);
166
352
  /**
167
353
  * A decorator function for emitting websocket messages to the peer.
168
354
  *
@@ -178,11 +364,11 @@ function Emit(event) {
178
364
 
179
365
  //#endregion
180
366
  //#region src/Decorators/Broadcast.ts
181
- var import_decorate$3 = /* @__PURE__ */ __toESM(require_decorate(), 1);
367
+ var import_decorate$2 = /* @__PURE__ */ __toESM(require_decorate(), 1);
182
368
  /**
183
- * A decorator class for broadcasting websocket messages to everyone
369
+ * A decorator class for broadcasting websocket messages to everyone
184
370
  * on the namespace (including the peer).
185
- *
371
+ *
186
372
  * This class extends the BaseDecorator and is used to emit the result of
187
373
  * your function as a websocket message to everyone on the namespace.
188
374
  *
@@ -208,7 +394,7 @@ var BroadcastDecorator = class extends BaseDecorator {
208
394
  };
209
395
  }
210
396
  };
211
- (0, import_decorate$3.default)([InjectOptional($WebsocketService)], BroadcastDecorator.prototype, "gWebsocketService", void 0);
397
+ (0, import_decorate$2.default)([InjectOptional($WebsocketService)], BroadcastDecorator.prototype, "gWebsocketService", void 0);
212
398
  /**
213
399
  * A decorator function for broadcasting websocket messages to everyone on the namespace (including the peer).
214
400
  *
@@ -224,11 +410,11 @@ function Broadcast(event) {
224
410
 
225
411
  //#endregion
226
412
  //#region src/Decorators/BroadcastOthers.ts
227
- var import_decorate$2 = /* @__PURE__ */ __toESM(require_decorate(), 1);
413
+ var import_decorate$1 = /* @__PURE__ */ __toESM(require_decorate(), 1);
228
414
  /**
229
- * A decorator class for broadcasting websocket messages to everyone
415
+ * A decorator class for broadcasting websocket messages to everyone
230
416
  * on the namespace (except the peer).
231
- *
417
+ *
232
418
  * This class extends the BaseDecorator and is used to emit the result of
233
419
  * your function as a websocket message to everyone on the namespace
234
420
  * (except the peer).
@@ -255,7 +441,7 @@ var BroadcastOthersDecorator = class extends BaseDecorator {
255
441
  };
256
442
  }
257
443
  };
258
- (0, import_decorate$2.default)([InjectOptional($WebsocketService)], BroadcastOthersDecorator.prototype, "gWebsocketService", void 0);
444
+ (0, import_decorate$1.default)([InjectOptional($WebsocketService)], BroadcastOthersDecorator.prototype, "gWebsocketService", void 0);
259
445
  /**
260
446
  * A decorator function for broadcasting websocket messages to everyone on the namespace (except the peer).
261
447
  *
@@ -271,18 +457,18 @@ function BroadcastOthers(event) {
271
457
 
272
458
  //#endregion
273
459
  //#region src/Decorators/OnConnectionAttempt.ts
274
- var import_decorate$1 = /* @__PURE__ */ __toESM(require_decorate(), 1);
460
+ var import_decorate = /* @__PURE__ */ __toESM(require_decorate(), 1);
275
461
  /**
276
462
  * A decorator class for handling websocket connection attempts.
277
463
  *
278
464
  * This class extends the BaseDecorator and is used to handle a
279
465
  * websocket connection attempt to a namespace.
280
- *
466
+ *
281
467
  * If your function throws, or returns false, the connection
282
468
  * will not be accepted.
283
- *
469
+ *
284
470
  * The decorated function will receive the following parameters:
285
- *
471
+ *
286
472
  * @param {Record<string, unknown>} params - The connection query parameters.
287
473
  * @param {Request} request - The original HTTP request.
288
474
  *
@@ -306,7 +492,7 @@ var OnConnectionAttemptDecorator = class extends BaseDecorator {
306
492
  this.gWebsocketService.registerHandler(WebsocketTypes.HandlerAction.CONNECTION, namespace, { callback: originalMethod });
307
493
  }
308
494
  };
309
- (0, import_decorate$1.default)([InjectOptional($WebsocketService)], OnConnectionAttemptDecorator.prototype, "gWebsocketService", void 0);
495
+ (0, import_decorate.default)([InjectOptional($WebsocketService)], OnConnectionAttemptDecorator.prototype, "gWebsocketService", void 0);
310
496
  /**
311
497
  * A decorator function for handling websocket connection attempts to a namespace.
312
498
  *
@@ -324,212 +510,26 @@ function OnConnectionAttempt() {
324
510
  return createDecorator(OnConnectionAttemptDecorator, void 0);
325
511
  }
326
512
 
327
- //#endregion
328
- //#region src/Services/WebsocketService.ts
329
- var import_decorate = /* @__PURE__ */ __toESM(require_decorate(), 1);
330
- /**
331
- * WebsocketService class responsible for dealing with Websocket connections.
332
- *
333
- * This class is responsible for:
334
- * - Registering namespaces and accepting websocket connections for them
335
- * - Registering event handlers and handling them
336
- */
337
- var WebsocketService = class {
338
- /**
339
- * Http Server for injecting the server plugin
340
- */
341
- gHttpServer;
342
- /**
343
- * Validation provider for running the schema validation
344
- */
345
- gValidationProvider;
346
- gLogger;
347
- /**
348
- * Internal namespace registry
349
- */
350
- fNamespaces = {};
351
- /**
352
- * Internal handlers registry
353
- */
354
- fHandlers = {
355
- [WebsocketTypes.HandlerAction.CONNECTION]: {},
356
- [WebsocketTypes.HandlerAction.MESSAGE]: {}
357
- };
358
- /**
359
- * Register a new namespace.
360
- *
361
- * @param {string} path - The namespace path to register.
362
- * @returns {void}
363
- */
364
- registerNamespace(path) {
365
- if (!this?.fNamespaces?.[path.toLowerCase()]) this.fNamespaces[path.toLowerCase()] = [];
366
- }
367
- /**
368
- * Register a new handler for a namespace or event.
369
- *
370
- * @param {WebsocketTypes.HandlerAction} action - The handler action type.
371
- * @param {string} namespace - The namespace to register the handler for.
372
- * @param {WebsocketTypes.HandlerAttributes} handler - The handler attributes.
373
- * @returns {void}
374
- */
375
- registerHandler(action, namespace, handler) {
376
- const normalizedNamespace = namespace.toLowerCase();
377
- if (action === WebsocketTypes.HandlerAction.CONNECTION) this.fHandlers[action][normalizedNamespace] = handler;
378
- if (action === WebsocketTypes.HandlerAction.MESSAGE) {
379
- const event = handler.event;
380
- if (!event) {
381
- this.gLogger?.warn("WebsocketService::registerHandler", `Cannot register message handler without an event name for namespace "${normalizedNamespace}".`);
382
- return;
383
- }
384
- if (!this.fHandlers[action][normalizedNamespace]) this.fHandlers[action][normalizedNamespace] = {};
385
- this.fHandlers[action][normalizedNamespace][event] = handler;
386
- }
387
- this.registerNamespace(normalizedNamespace);
388
- }
389
- /**
390
- * Broadcast a message to all peers in the same namespace (including the sender).
391
- *
392
- * @param {Peer} peer - The sender peer (used to determine the namespace).
393
- * @param {unknown} message - The message to broadcast.
394
- * @returns {void}
395
- */
396
- broadcast(peer, message) {
397
- const namespace = peer.namespace?.toLowerCase();
398
- if (!namespace) return;
399
- const peers = this.fNamespaces[namespace];
400
- if (!peers || peers.length === 0) return;
401
- for (const p of peers) p.send(message);
402
- }
403
- /**
404
- * Emit a message to a single peer.
405
- *
406
- * @param {Peer} peer - The peer to send the message to.
407
- * @param {unknown} message - The message to send.
408
- * @returns {void}
409
- */
410
- emit(peer, message) {
411
- peer.send(message);
412
- }
413
- /**
414
- * Broadcast a message to all peers in the same namespace except the sender.
415
- *
416
- * @param {Peer} peer - The sender peer (used to determine the namespace).
417
- * @param {unknown} message - The message to broadcast.
418
- * @returns {void}
419
- */
420
- broadcastOthers(peer, message) {
421
- const namespace = peer.namespace?.toLowerCase();
422
- if (!namespace) return;
423
- const peers = this.fNamespaces[namespace];
424
- if (!peers || peers.length === 0) return;
425
- for (const p of peers) if (p.id !== peer.id) p.send(message);
426
- }
427
- /**
428
- * Initialize the websocket service and attach the server plugin.
429
- *
430
- * @returns {void}
431
- */
432
- initialize() {
433
- const hooks = defineHooks({
434
- upgrade: async (request) => {
435
- const url = new URL(request.url);
436
- const namespace = url.pathname;
437
- const parameters = Object.fromEntries(url.searchParams.entries());
438
- const isNamespaceRegistered = !!this.fNamespaces?.[namespace?.toLowerCase()];
439
- if (!isNamespaceRegistered) {
440
- this.gLogger?.warn("WebsocketService::initialize", `Namespace "${namespace}" is not registered. Connection rejected.`);
441
- return new Response("Namespace not registered", { status: 403 });
442
- }
443
- const handler = this.fHandlers[WebsocketTypes.HandlerAction.CONNECTION]?.[namespace];
444
- if (handler) try {
445
- const result = await handler.callback(parameters, request);
446
- if (result === false) return new Response("Unauthorized", { status: 403 });
447
- } catch (error) {
448
- if (error instanceof Error) return new Response(error.message, { status: 403 });
449
- return new Response("Unknown error", { status: 403 });
450
- }
451
- return {
452
- namespace,
453
- headers: {}
454
- };
455
- },
456
- open: async (peer) => {
457
- const namespace = peer.namespace?.toLowerCase();
458
- if (namespace && this.fNamespaces[namespace]) this.fNamespaces[namespace].push(peer);
459
- },
460
- message: async (peer, message) => {
461
- await this.handleMessage(peer, message);
462
- },
463
- close: async (peer) => {
464
- const namespace = peer.namespace?.toLowerCase();
465
- if (namespace && this.fNamespaces[namespace]) {
466
- const peers = this.fNamespaces[namespace];
467
- this.fNamespaces[namespace] = peers.filter((p) => p.id !== peer.id);
468
- }
469
- },
470
- error: async (peer, error) => {
471
- this.gLogger?.error("WebsocketService::initialize", `Error: ${error.message}`, { peer });
472
- }
473
- });
474
- const serverPlugin = plugin(hooks);
475
- this.gHttpServer.addPlugin(serverPlugin);
476
- }
477
- /**
478
- * Handle an incoming websocket message for a peer.
479
- *
480
- * @param {Peer} peer - The peer receiving the message.
481
- * @param {Message} rawMessage - The raw websocket message.
482
- * @returns {Promise<void>}
483
- */
484
- async handleMessage(peer, rawMessage) {
485
- try {
486
- const msg = JSON.parse(rawMessage.text());
487
- const namespace = peer.namespace?.toLowerCase();
488
- const event = msg.event;
489
- const data = msg.data;
490
- const handler = this.fHandlers[WebsocketTypes.HandlerAction.MESSAGE]?.[namespace]?.[event];
491
- if (!handler) {
492
- this.gLogger?.warn("WebsocketService::handleMessage", `No message handler for event "${event}" in namespace "${namespace}"`);
493
- return;
494
- }
495
- if (handler.schema) {
496
- if (!this.gValidationProvider) {
497
- this.gLogger?.warn("WebsocketService::handleMessage", "ValidationProvider is not registered");
498
- return;
499
- }
500
- const result = await this.gValidationProvider.validate(handler.schema, data);
501
- if (result?.issues?.length) throw new BadRequestError("Websocket message validation error", result.issues);
502
- }
503
- await handler.callback(data, peer);
504
- } catch (error) {
505
- this.gLogger?.error("WebsocketService::handleMessage", `Failed to process message: ${error}`);
506
- }
507
- }
508
- };
509
- (0, import_decorate.default)([Inject(HttpServer)], WebsocketService.prototype, "gHttpServer", void 0);
510
- (0, import_decorate.default)([InjectOptional(ValidationProvider)], WebsocketService.prototype, "gValidationProvider", void 0);
511
- (0, import_decorate.default)([InjectOptional(Logger)], WebsocketService.prototype, "gLogger", void 0);
512
-
513
513
  //#endregion
514
514
  //#region src/Plugins/WebsocketPlugin.ts
515
515
  /**
516
516
  * Websocket Plugin for Vercube framework
517
- *
517
+ *
518
518
  * Enables websocket connections and use of decorators related
519
519
  * to the Websocket package.
520
- *
520
+ *
521
521
  * @example
522
522
  * ```ts
523
523
  * import { createApp } from '@vercube/core';
524
524
  * import { WebsocketPlugin } from '@vercube/ws';
525
- *
525
+ *
526
526
  * const app = createApp({
527
527
  * setup: async (app) => {
528
528
  * app.addPlugin(WebsocketPlugin);
529
529
  * }
530
530
  * });
531
531
  * ```
532
- *
532
+ *
533
533
  * @see {@link https://vercube.dev} for full documentation
534
534
  */
535
535
  var WebsocketPlugin = class extends BasePlugin {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercube/ws",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Websocket module for Vercube framework",
5
5
  "repository": "@vercube/ws",
6
6
  "license": "MIT",
@@ -18,14 +18,14 @@
18
18
  "README.md"
19
19
  ],
20
20
  "devDependencies": {
21
- "zod": "4.0.17"
21
+ "zod": "4.1.3"
22
22
  },
23
23
  "dependencies": {
24
24
  "crossws": "0.4.1",
25
- "srvx": "0.8.6",
26
- "@vercube/core": "0.0.22",
27
- "@vercube/di": "0.0.22",
28
- "@vercube/logger": "0.0.22"
25
+ "srvx": "0.8.7",
26
+ "@vercube/logger": "0.0.23",
27
+ "@vercube/core": "0.0.23",
28
+ "@vercube/di": "0.0.23"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public"