@fluojs/websockets 1.0.0-beta.2 → 1.0.0-beta.4

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.
@@ -11,6 +11,7 @@ import { WEBSOCKET_OPTIONS_INTERNAL } from '../options-token.internal.js';
11
11
  const DEFAULT_MAX_PENDING_MESSAGES_PER_SOCKET = 256;
12
12
  const DEFAULT_MAX_WEBSOCKET_CONNECTIONS = 1_000;
13
13
  const DEFAULT_MAX_WEBSOCKET_PAYLOAD_BYTES = 1_048_576;
14
+ const DEFAULT_WEBSOCKET_SHUTDOWN_TIMEOUT_MS = 5_000;
14
15
  const LIFECYCLE_LOG_CONTEXT = 'WebSocketGatewayLifecycleService';
15
16
  const WEBSOCKET_OPEN_READY_STATE = 1;
16
17
  function hasDenoWebSocketBindingHost(adapter) {
@@ -37,10 +38,20 @@ function isHttpExceptionLike(error) {
37
38
  }
38
39
  function resolveMessageByteLength(message) {
39
40
  if (typeof message === 'string') {
40
- return Buffer.byteLength(message);
41
+ return new TextEncoder().encode(message).byteLength;
41
42
  }
42
43
  return message.size;
43
44
  }
45
+ function createCompletionSignal() {
46
+ let resolve;
47
+ const promise = new Promise(res => {
48
+ resolve = res;
49
+ });
50
+ return {
51
+ promise,
52
+ resolve
53
+ };
54
+ }
44
55
 
45
56
  /**
46
57
  * Boots Deno-backed websocket gateways and manages their room lifecycle state.
@@ -50,11 +61,14 @@ class DenoWebSocketGatewayLifecycleService {
50
61
  static {
51
62
  [_DenoWebSocketGateway, _initClass] = _applyDecs(this, [Inject(RUNTIME_CONTAINER, COMPILED_MODULES, APPLICATION_LOGGER, HTTP_APPLICATION_ADAPTER, WEBSOCKET_OPTIONS_INTERNAL)], []).c;
52
63
  }
64
+ isShuttingDown = false;
65
+ pendingUpgradeOperations = new Set();
53
66
  pendingUpgradeReservations = 0;
54
67
  roomSockets = new Map();
55
68
  shutdownPromise;
56
69
  socketRegistry = new Map();
57
70
  socketRooms = new Map();
71
+ socketStates = new Map();
58
72
  constructor(runtimeContainer, compiledModules, logger, adapter, moduleOptions) {
59
73
  this.runtimeContainer = runtimeContainer;
60
74
  this.compiledModules = compiledModules;
@@ -90,53 +104,60 @@ class DenoWebSocketGatewayLifecycleService {
90
104
  createBinding(descriptors) {
91
105
  const descriptorsByPath = this.groupDescriptorsByPath(descriptors);
92
106
  return {
93
- fetch: async (request, host) => {
94
- if (!isWebSocketUpgradeRequest(request)) {
95
- return new Response(null, {
96
- status: 426
97
- });
98
- }
99
- let targetPath;
100
- try {
101
- targetPath = normalizeGatewayPath(new URL(request.url).pathname);
102
- } catch {
103
- return new Response(null, {
104
- status: 400
105
- });
106
- }
107
- const descriptors = descriptorsByPath.get(targetPath);
108
- if (!descriptors) {
109
- return new Response(null, {
110
- status: 404
111
- });
112
- }
113
- const rejection = await this.resolveUpgradeRejection(request, targetPath);
114
- if (rejection) {
115
- return new Response(rejection.body ?? null, {
116
- headers: rejection.headers,
117
- status: rejection.status
118
- });
119
- }
120
- let response;
121
- let socket;
122
- try {
123
- ({
124
- response,
125
- socket
126
- } = host.upgrade(request));
127
- } catch (error) {
128
- this.releaseUpgradeReservation();
129
- throw error;
130
- }
131
- void this.bindConnectionHandlers(socket, request, descriptors).catch(error => {
132
- this.unregisterSocket(this.findSocketId(socket));
133
- this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
134
- socket.close(1011, 'Internal server error');
135
- });
136
- return response;
137
- }
107
+ fetch: (request, host) => this.trackPendingUpgradeOperation(this.handleUpgradeRequest(request, host, descriptorsByPath))
138
108
  };
139
109
  }
110
+ async handleUpgradeRequest(request, host, descriptorsByPath) {
111
+ if (!isWebSocketUpgradeRequest(request)) {
112
+ return new Response(null, {
113
+ status: 426
114
+ });
115
+ }
116
+ let targetPath;
117
+ try {
118
+ targetPath = normalizeGatewayPath(new URL(request.url).pathname);
119
+ } catch {
120
+ return new Response(null, {
121
+ status: 400
122
+ });
123
+ }
124
+ const descriptors = descriptorsByPath.get(targetPath);
125
+ if (!descriptors) {
126
+ return new Response(null, {
127
+ status: 404
128
+ });
129
+ }
130
+ const rejection = await this.resolveUpgradeRejection(request, targetPath);
131
+ if (rejection) {
132
+ return new Response(rejection.body ?? null, {
133
+ headers: rejection.headers,
134
+ status: rejection.status
135
+ });
136
+ }
137
+ if (this.isShuttingDown) {
138
+ this.releaseUpgradeReservation();
139
+ return new Response('WebSocket server is shutting down.', {
140
+ status: 503
141
+ });
142
+ }
143
+ let response;
144
+ let socket;
145
+ try {
146
+ ({
147
+ response,
148
+ socket
149
+ } = host.upgrade(request));
150
+ } catch (error) {
151
+ this.releaseUpgradeReservation();
152
+ throw error;
153
+ }
154
+ void this.trackPendingUpgradeOperation(this.bindConnectionHandlers(socket, request, descriptors)).catch(error => {
155
+ this.unregisterSocket(this.findSocketId(socket));
156
+ this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
157
+ socket.close(1011, 'Internal server error');
158
+ });
159
+ return response;
160
+ }
140
161
  groupDescriptorsByPath(descriptors) {
141
162
  const descriptorsByPath = new Map();
142
163
  for (const descriptor of descriptors) {
@@ -153,17 +174,35 @@ class DenoWebSocketGatewayLifecycleService {
153
174
  const state = this.createConnectionHandlerState(request, descriptors);
154
175
  this.releaseUpgradeReservation();
155
176
  this.socketRegistry.set(state.socketId, socket);
177
+ this.socketStates.set(state.socketId, state);
156
178
  this.attachConnectionListeners(state, socket, request);
157
- await this.resolveConnectionGateways(state);
158
- await this.runConnectHandlers(state, socket);
159
- await this.finalizeConnectionBinding(state, socket, request);
179
+ try {
180
+ await this.resolveConnectionGateways(state);
181
+ await this.runConnectHandlers(state, socket);
182
+ await this.finalizeConnectionBinding(state, socket, request);
183
+ if (this.isShuttingDown && socket.readyState === WEBSOCKET_OPEN_READY_STATE) {
184
+ socket.close(1001, 'Server shutting down');
185
+ await state.disconnectLifecyclePromise;
186
+ }
187
+ } finally {
188
+ if (!state.handlersReady && state.bufferedDisconnect) {
189
+ this.settleDisconnectLifecycle(state);
190
+ }
191
+ this.settleConnectLifecycle(state);
192
+ }
160
193
  }
161
194
  createConnectionHandlerState(request, descriptors) {
195
+ const connectLifecycle = createCompletionSignal();
196
+ const disconnectLifecycle = createCompletionSignal();
162
197
  return {
163
198
  bufferedDisconnect: undefined,
164
199
  bufferedMessages: [],
165
200
  bufferedMessagesStartIndex: 0,
201
+ connectLifecycleSettled: false,
202
+ connectLifecyclePromise: connectLifecycle.promise,
166
203
  descriptors,
204
+ disconnectLifecycleSettled: false,
205
+ disconnectLifecyclePromise: disconnectLifecycle.promise,
167
206
  enqueuedMessageCount: 0,
168
207
  handlerQueue: Promise.resolve(),
169
208
  handlersReady: false,
@@ -171,10 +210,26 @@ class DenoWebSocketGatewayLifecycleService {
171
210
  queuedMessages: [],
172
211
  queuedMessagesStartIndex: 0,
173
212
  request,
213
+ resolveConnectLifecycle: connectLifecycle.resolve,
214
+ resolveDisconnectLifecycle: disconnectLifecycle.resolve,
174
215
  resolved: [],
175
216
  socketId: crypto.randomUUID()
176
217
  };
177
218
  }
219
+ settleConnectLifecycle(state) {
220
+ if (state.connectLifecycleSettled) {
221
+ return;
222
+ }
223
+ state.connectLifecycleSettled = true;
224
+ state.resolveConnectLifecycle();
225
+ }
226
+ settleDisconnectLifecycle(state) {
227
+ if (state.disconnectLifecycleSettled) {
228
+ return;
229
+ }
230
+ state.disconnectLifecycleSettled = true;
231
+ state.resolveDisconnectLifecycle();
232
+ }
178
233
  attachConnectionListeners(state, socket, request) {
179
234
  socket.addEventListener('message', event => {
180
235
  if (this.closeOversizedPayload(state.socketId, socket, event.data)) {
@@ -311,6 +366,8 @@ class DenoWebSocketGatewayLifecycleService {
311
366
  await dispatchGatewayDisconnect(state.resolved, socket, disconnectEvent.code, disconnectEvent.reason, state.socketId, this.logger, LIFECYCLE_LOG_CONTEXT);
312
367
  }).catch(error => {
313
368
  this.logger.error('WebSocket gateway disconnect dispatch failed.', error, LIFECYCLE_LOG_CONTEXT);
369
+ }).finally(() => {
370
+ this.settleDisconnectLifecycle(state);
314
371
  });
315
372
  }
316
373
  async resolveConnectionGateways(state) {
@@ -361,6 +418,12 @@ class DenoWebSocketGatewayLifecycleService {
361
418
  return '';
362
419
  }
363
420
  async resolveUpgradeRejection(request, path) {
421
+ if (this.isShuttingDown) {
422
+ return {
423
+ body: 'WebSocket server is shutting down.',
424
+ status: 503
425
+ };
426
+ }
364
427
  if (!this.tryReserveUpgradeSlot()) {
365
428
  return {
366
429
  body: 'WebSocket connection limit exceeded.',
@@ -437,6 +500,13 @@ class DenoWebSocketGatewayLifecycleService {
437
500
  }
438
501
  return configured;
439
502
  }
503
+ resolveShutdownTimeoutMs() {
504
+ const configured = this.moduleOptions.shutdown?.timeoutMs;
505
+ if (typeof configured !== 'number' || !Number.isFinite(configured) || configured <= 0) {
506
+ return DEFAULT_WEBSOCKET_SHUTDOWN_TIMEOUT_MS;
507
+ }
508
+ return Math.floor(configured);
509
+ }
440
510
  async shutdown() {
441
511
  if (this.shutdownPromise) {
442
512
  await this.shutdownPromise;
@@ -446,14 +516,112 @@ class DenoWebSocketGatewayLifecycleService {
446
516
  await this.shutdownPromise;
447
517
  }
448
518
  async runShutdownLifecycle() {
519
+ this.isShuttingDown = true;
449
520
  if (hasDenoWebSocketBindingHost(this.adapter)) {
450
521
  this.adapter.configureWebSocketBinding(undefined);
451
522
  }
523
+ const shutdownTimeoutMs = this.resolveShutdownTimeoutMs();
524
+ await this.awaitPendingUpgradeOperations(shutdownTimeoutMs);
525
+ await this.closeActiveSockets(shutdownTimeoutMs);
452
526
  this.pendingUpgradeReservations = 0;
453
527
  this.socketRegistry.clear();
454
528
  this.socketRooms.clear();
529
+ this.socketStates.clear();
455
530
  this.roomSockets.clear();
456
531
  }
532
+ trackPendingUpgradeOperation(operation) {
533
+ if (typeof operation === 'function') {
534
+ return (...args) => this.trackPendingUpgradeOperation(operation(...args));
535
+ }
536
+ let trackedOperation;
537
+ trackedOperation = operation.then(() => undefined, () => undefined).finally(() => {
538
+ if (trackedOperation) {
539
+ this.pendingUpgradeOperations.delete(trackedOperation);
540
+ }
541
+ });
542
+ this.pendingUpgradeOperations.add(trackedOperation);
543
+ return operation;
544
+ }
545
+ async awaitPendingUpgradeOperations(timeoutMs) {
546
+ if (this.pendingUpgradeOperations.size === 0) {
547
+ return;
548
+ }
549
+ await new Promise((resolve, reject) => {
550
+ let settled = false;
551
+ const timeout = setTimeout(() => {
552
+ if (settled) {
553
+ return;
554
+ }
555
+ settled = true;
556
+ reject(new Error(`Timed out while waiting for in-flight Deno websocket upgrades after ${String(timeoutMs)}ms.`));
557
+ }, timeoutMs);
558
+ Promise.all([...this.pendingUpgradeOperations]).then(() => {
559
+ if (settled) {
560
+ return;
561
+ }
562
+ settled = true;
563
+ clearTimeout(timeout);
564
+ resolve();
565
+ }).catch(error => {
566
+ if (settled) {
567
+ return;
568
+ }
569
+ settled = true;
570
+ clearTimeout(timeout);
571
+ reject(error);
572
+ });
573
+ }).catch(error => {
574
+ this.logger.error(`Failed to wait for in-flight Deno websocket upgrades within ${String(timeoutMs)}ms.`, error, LIFECYCLE_LOG_CONTEXT);
575
+ });
576
+ }
577
+ async closeActiveSockets(timeoutMs) {
578
+ const activeSockets = [...this.socketRegistry.entries()];
579
+ if (activeSockets.length === 0) {
580
+ return;
581
+ }
582
+ const activeStates = activeSockets.map(([socketId]) => this.socketStates.get(socketId)).filter(state => state !== undefined);
583
+ for (const [, socket] of activeSockets) {
584
+ if (socket.readyState === WEBSOCKET_OPEN_READY_STATE) {
585
+ socket.close(1001, 'Server shutting down');
586
+ }
587
+ }
588
+ await this.awaitHandlerQueueDrain(activeStates, timeoutMs);
589
+ }
590
+ async awaitHandlerQueueDrain(states, timeoutMs) {
591
+ if (states.length === 0) {
592
+ return;
593
+ }
594
+ await new Promise((resolve, reject) => {
595
+ let settled = false;
596
+ const timeout = setTimeout(() => {
597
+ if (settled) {
598
+ return;
599
+ }
600
+ settled = true;
601
+ reject(new Error(`Timed out while closing Deno websocket connections after ${String(timeoutMs)}ms.`));
602
+ }, timeoutMs);
603
+ Promise.all(states.map(async state => {
604
+ await state.connectLifecyclePromise;
605
+ await state.disconnectLifecyclePromise;
606
+ })).then(() => {
607
+ if (settled) {
608
+ return;
609
+ }
610
+ settled = true;
611
+ clearTimeout(timeout);
612
+ resolve();
613
+ }).catch(error => {
614
+ if (settled) {
615
+ return;
616
+ }
617
+ settled = true;
618
+ clearTimeout(timeout);
619
+ reject(error);
620
+ });
621
+ }).catch(error => {
622
+ this.logger.error(`Failed to close Deno websocket connections within ${String(timeoutMs)}ms.`, error, LIFECYCLE_LOG_CONTEXT);
623
+ });
624
+ }
457
625
  joinRoom(socketId, room) {
458
626
  let rooms = this.socketRooms.get(socketId);
459
627
  if (!rooms) {
@@ -514,6 +682,7 @@ class DenoWebSocketGatewayLifecycleService {
514
682
  return;
515
683
  }
516
684
  this.socketRegistry.delete(socketId);
685
+ this.socketStates.delete(socketId);
517
686
  const rooms = this.socketRooms.get(socketId);
518
687
  if (rooms) {
519
688
  for (const room of rooms) {
@@ -1,25 +1,52 @@
1
1
  import type { WebSocketModuleOptions as SharedWebSocketModuleOptions } from '../types.js';
2
+ /**
3
+ * Defines the deno web socket message type.
4
+ */
2
5
  export type DenoWebSocketMessage = Blob | string;
6
+ /**
7
+ * Describes the deno server web socket contract.
8
+ */
3
9
  export interface DenoServerWebSocket extends Pick<WebSocket, 'addEventListener' | 'close' | 'removeEventListener' | 'send'> {
4
10
  readonly readyState: number;
5
11
  }
12
+ /**
13
+ * Describes the deno web socket upgrade result contract.
14
+ */
6
15
  export interface DenoWebSocketUpgradeResult<TSocket extends DenoServerWebSocket = DenoServerWebSocket> {
7
16
  response: Response;
8
17
  socket: TSocket;
9
18
  }
19
+ /**
20
+ * Describes the deno web socket upgrade host contract.
21
+ */
10
22
  export interface DenoWebSocketUpgradeHost<TSocket extends DenoServerWebSocket = DenoServerWebSocket> {
11
23
  upgrade(request: Request): DenoWebSocketUpgradeResult<TSocket>;
12
24
  }
25
+ /**
26
+ * Describes the deno web socket binding contract.
27
+ */
13
28
  export interface DenoWebSocketBinding<TSocket extends DenoServerWebSocket = DenoServerWebSocket> {
14
29
  fetch(request: Request, host: DenoWebSocketUpgradeHost<TSocket>): Response | Promise<Response>;
15
30
  }
31
+ /**
32
+ * Describes the deno web socket binding host contract.
33
+ */
16
34
  export interface DenoWebSocketBindingHost<TSocket extends DenoServerWebSocket = DenoServerWebSocket> {
17
35
  configureWebSocketBinding(binding: DenoWebSocketBinding<TSocket> | undefined): void;
18
36
  }
37
+ /**
38
+ * Defines the typed on message handler type.
39
+ */
19
40
  export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = (payload: TEvents[K], socket: DenoServerWebSocket, request: Request) => void | Promise<void>;
41
+ /**
42
+ * Describes the web socket gateway context contract.
43
+ */
20
44
  export interface WebSocketGatewayContext {
21
45
  request: Request;
22
46
  socket: DenoServerWebSocket;
23
47
  }
48
+ /**
49
+ * Defines the web socket module options type.
50
+ */
24
51
  export type WebSocketModuleOptions = SharedWebSocketModuleOptions;
25
52
  //# sourceMappingURL=deno-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deno-types.d.ts","sourceRoot":"","sources":["../../src/deno/deno-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,IAAI,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAE1F,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,GAAG,qBAAqB,GAAG,MAAM,CAAC;IACzH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IACnG,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IACjG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IAC7F,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,CAAC,OAAO,CAAC,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChG;AAED,MAAM,WAAW,wBAAwB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IACjG,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;CACrF;AAED,MAAM,MAAM,qBAAqB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,OAAO,IAAI,CACpG,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,OAAO,KACb,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,MAAM,MAAM,sBAAsB,GAAG,4BAA4B,CAAC"}
1
+ {"version":3,"file":"deno-types.d.ts","sourceRoot":"","sources":["../../src/deno/deno-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,IAAI,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAE1F;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,MAAM,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,GAAG,qBAAqB,GAAG,MAAM,CAAC;IACzH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IACnG,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IACjG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IAC7F,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,CAAC,OAAO,CAAC,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChG;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IACjG,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC;CACrF;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,SAAS,MAAM,OAAO,IAAI,CACpG,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,OAAO,KACb,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,4BAA4B,CAAC"}
@@ -2,27 +2,108 @@ import type { Token } from '@fluojs/core';
2
2
  import type { Container } from '@fluojs/di';
3
3
  import type { ApplicationLogger, CompiledModule } from '@fluojs/runtime';
4
4
  import type { WebSocketGatewayDescriptor, WebSocketGatewayHandlerDescriptor } from '../types.js';
5
+ /**
6
+ * Describes the discovery candidate contract.
7
+ */
5
8
  export interface DiscoveryCandidate {
6
9
  moduleName: string;
7
10
  scope: 'request' | 'singleton' | 'transient';
8
11
  targetType: Function;
9
12
  token: Token;
10
13
  }
14
+ /**
15
+ * Describes the resolved gateway instance contract.
16
+ */
11
17
  export interface ResolvedGatewayInstance {
12
18
  descriptor: WebSocketGatewayDescriptor;
13
19
  instance: unknown;
14
20
  }
21
+ /**
22
+ * Defines the parsed web socket message type.
23
+ */
15
24
  export type ParsedWebSocketMessage = {
16
25
  event?: string;
17
26
  payload: unknown;
18
27
  };
28
+ /**
29
+ * Defines the shared web socket incoming message type.
30
+ */
19
31
  export type SharedWebSocketIncomingMessage = ArrayBuffer | ArrayBufferView | Uint8Array[] | string;
32
+ /**
33
+ * Is finite positive integer.
34
+ *
35
+ * @param value The value.
36
+ * @returns The is finite positive integer result.
37
+ */
20
38
  export declare function isFinitePositiveInteger(value: unknown): value is number;
39
+ /**
40
+ * Normalize gateway path.
41
+ *
42
+ * @param path The path.
43
+ * @returns The normalize gateway path result.
44
+ */
21
45
  export declare function normalizeGatewayPath(path: string): string;
46
+ /**
47
+ * Parse incoming message.
48
+ *
49
+ * @param data The data.
50
+ * @returns The parse incoming message result.
51
+ */
22
52
  export declare function parseIncomingMessage(data: SharedWebSocketIncomingMessage): ParsedWebSocketMessage;
53
+ /**
54
+ * Discover gateway descriptors.
55
+ *
56
+ * @param compiledModules The compiled modules.
57
+ * @param logger The logger.
58
+ * @param loggerContext The logger context.
59
+ * @returns The discover gateway descriptors result.
60
+ */
23
61
  export declare function discoverGatewayDescriptors(compiledModules: readonly CompiledModule[], logger: ApplicationLogger, loggerContext: string): WebSocketGatewayDescriptor[];
62
+ /**
63
+ * Resolve gateway instance.
64
+ *
65
+ * @param runtimeContainer The runtime container.
66
+ * @param descriptor The descriptor.
67
+ * @param logger The logger.
68
+ * @param loggerContext The logger context.
69
+ * @returns The resolve gateway instance result.
70
+ */
24
71
  export declare function resolveGatewayInstance(runtimeContainer: Container, descriptor: WebSocketGatewayDescriptor, logger: ApplicationLogger, loggerContext: string): Promise<unknown | undefined>;
72
+ /**
73
+ * Dispatch gateway message.
74
+ *
75
+ * @param resolved The resolved.
76
+ * @param socket The socket.
77
+ * @param request The request.
78
+ * @param data The data.
79
+ * @param logger The logger.
80
+ * @param loggerContext The logger context.
81
+ * @returns The dispatch gateway message result.
82
+ */
25
83
  export declare function dispatchGatewayMessage<TSocket, TRequest>(resolved: readonly ResolvedGatewayInstance[], socket: TSocket, request: TRequest, data: SharedWebSocketIncomingMessage, logger: ApplicationLogger, loggerContext: string): Promise<void>;
84
+ /**
85
+ * Dispatch gateway disconnect.
86
+ *
87
+ * @param resolved The resolved.
88
+ * @param socket The socket.
89
+ * @param code The code.
90
+ * @param reason The reason.
91
+ * @param socketId The socket id.
92
+ * @param logger The logger.
93
+ * @param loggerContext The logger context.
94
+ * @returns The dispatch gateway disconnect result.
95
+ */
26
96
  export declare function dispatchGatewayDisconnect<TSocket>(resolved: readonly ResolvedGatewayInstance[], socket: TSocket, code: number, reason: string, socketId: string, logger: ApplicationLogger, loggerContext: string): Promise<void>;
97
+ /**
98
+ * Run gateway handlers.
99
+ *
100
+ * @param instance The instance.
101
+ * @param descriptor The descriptor.
102
+ * @param type The type.
103
+ * @param args The args.
104
+ * @param logger The logger.
105
+ * @param loggerContext The logger context.
106
+ * @returns The run gateway handlers result.
107
+ */
27
108
  export declare function runGatewayHandlers(instance: unknown, descriptor: WebSocketGatewayDescriptor, type: WebSocketGatewayHandlerDescriptor['type'], args: unknown[], logger: ApplicationLogger, loggerContext: string): Promise<void>;
28
109
  //# sourceMappingURL=shared.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/internal/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,KAAK,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,KAAK,EACV,0BAA0B,EAC1B,iCAAiC,EAClC,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;IAC7C,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;CACd;AAQD,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,0BAA0B,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,8BAA8B,GACtC,WAAW,GACX,eAAe,GACf,UAAU,EAAE,GACZ,MAAM,CAAC;AAEX,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,8BAA8B,GAAG,sBAAsB,CA4CjG;AAED,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,0BAA0B,EAAE,CA4B9B;AAED,wBAAsB,sBAAsB,CAC1C,gBAAgB,EAAE,SAAS,EAC3B,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAW9B;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAC5D,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,8BAA8B,EACpC,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,yBAAyB,CAAC,OAAO,EACrD,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,0BAA0B,EACtC,IAAI,EAAE,iCAAiC,CAAC,MAAM,CAAC,EAC/C,IAAI,EAAE,OAAO,EAAE,EACf,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAMf"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/internal/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAuB,KAAK,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,KAAK,EAAY,SAAS,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,KAAK,EACV,0BAA0B,EAC1B,iCAAiC,EAClC,MAAM,aAAa,CAAC;AAIrB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;IAC7C,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;CACd;AAQD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,0BAA0B,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACtC,WAAW,GACX,eAAe,GACf,UAAU,EAAE,GACZ,MAAM,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,8BAA8B,GAAG,sBAAsB,CA4CjG;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,0BAA0B,EAAE,CA4B9B;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,gBAAgB,EAAE,SAAS,EAC3B,UAAU,EAAE,0BAA0B,EACtC,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAW9B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAC5D,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,8BAA8B,EACpC,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAcf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EACrD,QAAQ,EAAE,SAAS,uBAAuB,EAAE,EAC5C,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,OAAO,EACjB,UAAU,EAAE,0BAA0B,EACtC,IAAI,EAAE,iCAAiC,CAAC,MAAM,CAAC,EAC/C,IAAI,EAAE,OAAO,EAAE,EACf,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -1,9 +1,39 @@
1
1
  import { getClassDiMetadata } from '@fluojs/core/internal';
2
2
  import { getWebSocketGatewayMetadata, getWebSocketHandlerMetadataEntries } from '../metadata.js';
3
3
  const textDecoder = new TextDecoder();
4
+
5
+ /**
6
+ * Describes the discovery candidate contract.
7
+ */
8
+
9
+ /**
10
+ * Describes the resolved gateway instance contract.
11
+ */
12
+
13
+ /**
14
+ * Defines the parsed web socket message type.
15
+ */
16
+
17
+ /**
18
+ * Defines the shared web socket incoming message type.
19
+ */
20
+
21
+ /**
22
+ * Is finite positive integer.
23
+ *
24
+ * @param value The value.
25
+ * @returns The is finite positive integer result.
26
+ */
4
27
  export function isFinitePositiveInteger(value) {
5
28
  return typeof value === 'number' && Number.isFinite(value) && value > 0 && Number.isInteger(value);
6
29
  }
30
+
31
+ /**
32
+ * Normalize gateway path.
33
+ *
34
+ * @param path The path.
35
+ * @returns The normalize gateway path result.
36
+ */
7
37
  export function normalizeGatewayPath(path) {
8
38
  if (path === '/') {
9
39
  return '/';
@@ -11,6 +41,13 @@ export function normalizeGatewayPath(path) {
11
41
  const normalized = `/${path.replace(/^\/+/, '').replace(/\/+$/, '')}`;
12
42
  return normalized === '' ? '/' : normalized;
13
43
  }
44
+
45
+ /**
46
+ * Parse incoming message.
47
+ *
48
+ * @param data The data.
49
+ * @returns The parse incoming message result.
50
+ */
14
51
  export function parseIncomingMessage(data) {
15
52
  let text;
16
53
  if (typeof data === 'string') {
@@ -50,6 +87,15 @@ export function parseIncomingMessage(data) {
50
87
  payload: parsed
51
88
  };
52
89
  }
90
+
91
+ /**
92
+ * Discover gateway descriptors.
93
+ *
94
+ * @param compiledModules The compiled modules.
95
+ * @param logger The logger.
96
+ * @param loggerContext The logger context.
97
+ * @returns The discover gateway descriptors result.
98
+ */
53
99
  export function discoverGatewayDescriptors(compiledModules, logger, loggerContext) {
54
100
  const seenTargets = new Set();
55
101
  const descriptors = [];
@@ -70,6 +116,16 @@ export function discoverGatewayDescriptors(compiledModules, logger, loggerContex
70
116
  }
71
117
  return descriptors;
72
118
  }
119
+
120
+ /**
121
+ * Resolve gateway instance.
122
+ *
123
+ * @param runtimeContainer The runtime container.
124
+ * @param descriptor The descriptor.
125
+ * @param logger The logger.
126
+ * @param loggerContext The logger context.
127
+ * @returns The resolve gateway instance result.
128
+ */
73
129
  export async function resolveGatewayInstance(runtimeContainer, descriptor, logger, loggerContext) {
74
130
  try {
75
131
  return await runtimeContainer.resolve(descriptor.token);
@@ -78,6 +134,18 @@ export async function resolveGatewayInstance(runtimeContainer, descriptor, logge
78
134
  return undefined;
79
135
  }
80
136
  }
137
+
138
+ /**
139
+ * Dispatch gateway message.
140
+ *
141
+ * @param resolved The resolved.
142
+ * @param socket The socket.
143
+ * @param request The request.
144
+ * @param data The data.
145
+ * @param logger The logger.
146
+ * @param loggerContext The logger context.
147
+ * @returns The dispatch gateway message result.
148
+ */
81
149
  export async function dispatchGatewayMessage(resolved, socket, request, data, logger, loggerContext) {
82
150
  const parsed = parseIncomingMessage(data);
83
151
  for (const {
@@ -90,6 +158,19 @@ export async function dispatchGatewayMessage(resolved, socket, request, data, lo
90
158
  }
91
159
  }
92
160
  }
161
+
162
+ /**
163
+ * Dispatch gateway disconnect.
164
+ *
165
+ * @param resolved The resolved.
166
+ * @param socket The socket.
167
+ * @param code The code.
168
+ * @param reason The reason.
169
+ * @param socketId The socket id.
170
+ * @param logger The logger.
171
+ * @param loggerContext The logger context.
172
+ * @returns The dispatch gateway disconnect result.
173
+ */
93
174
  export async function dispatchGatewayDisconnect(resolved, socket, code, reason, socketId, logger, loggerContext) {
94
175
  for (const {
95
176
  descriptor,
@@ -98,6 +179,18 @@ export async function dispatchGatewayDisconnect(resolved, socket, code, reason,
98
179
  await runGatewayHandlers(instance, descriptor, 'disconnect', [socket, code, reason, socketId], logger, loggerContext);
99
180
  }
100
181
  }
182
+
183
+ /**
184
+ * Run gateway handlers.
185
+ *
186
+ * @param instance The instance.
187
+ * @param descriptor The descriptor.
188
+ * @param type The type.
189
+ * @param args The args.
190
+ * @param logger The logger.
191
+ * @param loggerContext The logger context.
192
+ * @returns The run gateway handlers result.
193
+ */
101
194
  export async function runGatewayHandlers(instance, descriptor, type, args, logger, loggerContext) {
102
195
  const handlers = descriptor.handlers.filter(handler => handler.type === type);
103
196
  for (const handler of handlers) {