@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 hasCloudflareWorkerWebSocketBindingHost(adapter) {
@@ -37,13 +38,23 @@ 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
  if (message instanceof Blob) {
43
44
  return message.size;
44
45
  }
45
46
  return message.byteLength;
46
47
  }
48
+ function createCompletionSignal() {
49
+ let resolve;
50
+ const promise = new Promise(res => {
51
+ resolve = res;
52
+ });
53
+ return {
54
+ promise,
55
+ resolve
56
+ };
57
+ }
47
58
 
48
59
  /**
49
60
  * Boots Cloudflare Workers websocket gateways and manages their room lifecycle state.
@@ -53,11 +64,14 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
53
64
  static {
54
65
  [_CloudflareWorkersWeb, _initClass] = _applyDecs(this, [Inject(RUNTIME_CONTAINER, COMPILED_MODULES, APPLICATION_LOGGER, HTTP_APPLICATION_ADAPTER, WEBSOCKET_OPTIONS_INTERNAL)], []).c;
55
66
  }
67
+ isShuttingDown = false;
68
+ pendingUpgradeOperations = new Set();
56
69
  pendingUpgradeReservations = 0;
57
70
  roomSockets = new Map();
58
71
  shutdownPromise;
59
72
  socketRegistry = new Map();
60
73
  socketRooms = new Map();
74
+ socketStates = new Map();
61
75
  constructor(runtimeContainer, compiledModules, logger, adapter, moduleOptions) {
62
76
  this.runtimeContainer = runtimeContainer;
63
77
  this.compiledModules = compiledModules;
@@ -93,54 +107,61 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
93
107
  createBinding(descriptors) {
94
108
  const descriptorsByPath = this.groupDescriptorsByPath(descriptors);
95
109
  return {
96
- fetch: async (request, host) => {
97
- if (!isWebSocketUpgradeRequest(request)) {
98
- return new Response(null, {
99
- status: 426
100
- });
101
- }
102
- let targetPath;
103
- try {
104
- targetPath = normalizeGatewayPath(new URL(request.url).pathname);
105
- } catch {
106
- return new Response(null, {
107
- status: 400
108
- });
109
- }
110
- const matchedDescriptors = descriptorsByPath.get(targetPath);
111
- if (!matchedDescriptors) {
112
- return new Response(null, {
113
- status: 404
114
- });
115
- }
116
- const rejection = await this.resolveUpgradeRejection(request, targetPath);
117
- if (rejection) {
118
- return new Response(rejection.body ?? null, {
119
- headers: rejection.headers,
120
- status: rejection.status
121
- });
122
- }
123
- let response;
124
- let serverSocket;
125
- try {
126
- ({
127
- response,
128
- serverSocket
129
- } = host.upgrade(request));
130
- } catch (error) {
131
- this.releaseUpgradeReservation();
132
- throw error;
133
- }
134
- serverSocket.accept();
135
- void this.bindConnectionHandlers(serverSocket, request, matchedDescriptors).catch(error => {
136
- this.unregisterSocket(this.findSocketId(serverSocket));
137
- this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
138
- serverSocket.close(1011, 'Internal server error');
139
- });
140
- return response;
141
- }
110
+ fetch: (request, host) => this.trackPendingUpgradeOperation(this.handleUpgradeRequest(request, host, descriptorsByPath))
142
111
  };
143
112
  }
113
+ async handleUpgradeRequest(request, host, descriptorsByPath) {
114
+ if (!isWebSocketUpgradeRequest(request)) {
115
+ return new Response(null, {
116
+ status: 426
117
+ });
118
+ }
119
+ let targetPath;
120
+ try {
121
+ targetPath = normalizeGatewayPath(new URL(request.url).pathname);
122
+ } catch {
123
+ return new Response(null, {
124
+ status: 400
125
+ });
126
+ }
127
+ const matchedDescriptors = descriptorsByPath.get(targetPath);
128
+ if (!matchedDescriptors) {
129
+ return new Response(null, {
130
+ status: 404
131
+ });
132
+ }
133
+ const rejection = await this.resolveUpgradeRejection(request, targetPath);
134
+ if (rejection) {
135
+ return new Response(rejection.body ?? null, {
136
+ headers: rejection.headers,
137
+ status: rejection.status
138
+ });
139
+ }
140
+ if (this.isShuttingDown) {
141
+ this.releaseUpgradeReservation();
142
+ return new Response('WebSocket server is shutting down.', {
143
+ status: 503
144
+ });
145
+ }
146
+ let response;
147
+ let serverSocket;
148
+ try {
149
+ ({
150
+ response,
151
+ serverSocket
152
+ } = host.upgrade(request));
153
+ } catch (error) {
154
+ this.releaseUpgradeReservation();
155
+ throw error;
156
+ }
157
+ serverSocket.accept();
158
+ void this.trackPendingUpgradeOperation(this.bindConnectionHandlers(serverSocket, request, matchedDescriptors)).catch(error => {
159
+ this.unregisterSocket(this.findSocketId(serverSocket));
160
+ this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
161
+ serverSocket.close(1011, 'Internal server error');
162
+ });
163
+ return response;
164
+ }
144
165
  groupDescriptorsByPath(descriptors) {
145
166
  const descriptorsByPath = new Map();
146
167
  for (const descriptor of descriptors) {
@@ -157,17 +178,35 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
157
178
  const state = this.createConnectionHandlerState(request, descriptors);
158
179
  this.releaseUpgradeReservation();
159
180
  this.socketRegistry.set(state.socketId, socket);
181
+ this.socketStates.set(state.socketId, state);
160
182
  this.attachConnectionListeners(state, socket, request);
161
- await this.resolveConnectionGateways(state);
162
- await this.runConnectHandlers(state, socket);
163
- await this.finalizeConnectionBinding(state, socket, request);
183
+ try {
184
+ await this.resolveConnectionGateways(state);
185
+ await this.runConnectHandlers(state, socket);
186
+ await this.finalizeConnectionBinding(state, socket, request);
187
+ if (this.isShuttingDown && socket.readyState === WEBSOCKET_OPEN_READY_STATE) {
188
+ socket.close(1001, 'Server shutting down');
189
+ await state.disconnectLifecyclePromise;
190
+ }
191
+ } finally {
192
+ if (!state.handlersReady && state.bufferedDisconnect) {
193
+ this.settleDisconnectLifecycle(state);
194
+ }
195
+ this.settleConnectLifecycle(state);
196
+ }
164
197
  }
165
198
  createConnectionHandlerState(request, descriptors) {
199
+ const connectLifecycle = createCompletionSignal();
200
+ const disconnectLifecycle = createCompletionSignal();
166
201
  return {
167
202
  bufferedDisconnect: undefined,
168
203
  bufferedMessages: [],
169
204
  bufferedMessagesStartIndex: 0,
205
+ connectLifecycleSettled: false,
206
+ connectLifecyclePromise: connectLifecycle.promise,
170
207
  descriptors,
208
+ disconnectLifecycleSettled: false,
209
+ disconnectLifecyclePromise: disconnectLifecycle.promise,
171
210
  enqueuedMessageCount: 0,
172
211
  handlerQueue: Promise.resolve(),
173
212
  handlersReady: false,
@@ -175,10 +214,26 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
175
214
  queuedMessages: [],
176
215
  queuedMessagesStartIndex: 0,
177
216
  request,
217
+ resolveConnectLifecycle: connectLifecycle.resolve,
218
+ resolveDisconnectLifecycle: disconnectLifecycle.resolve,
178
219
  resolved: [],
179
220
  socketId: crypto.randomUUID()
180
221
  };
181
222
  }
223
+ settleConnectLifecycle(state) {
224
+ if (state.connectLifecycleSettled) {
225
+ return;
226
+ }
227
+ state.connectLifecycleSettled = true;
228
+ state.resolveConnectLifecycle();
229
+ }
230
+ settleDisconnectLifecycle(state) {
231
+ if (state.disconnectLifecycleSettled) {
232
+ return;
233
+ }
234
+ state.disconnectLifecycleSettled = true;
235
+ state.resolveDisconnectLifecycle();
236
+ }
182
237
  attachConnectionListeners(state, socket, request) {
183
238
  socket.addEventListener('message', event => {
184
239
  if (this.closeOversizedPayload(state.socketId, socket, event.data)) {
@@ -316,6 +371,8 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
316
371
  await dispatchGatewayDisconnect(state.resolved, socket, disconnectEvent.code, disconnectEvent.reason, state.socketId, this.logger, LIFECYCLE_LOG_CONTEXT);
317
372
  }).catch(error => {
318
373
  this.logger.error('WebSocket gateway disconnect dispatch failed.', error, LIFECYCLE_LOG_CONTEXT);
374
+ }).finally(() => {
375
+ this.settleDisconnectLifecycle(state);
319
376
  });
320
377
  }
321
378
  async resolveConnectionGateways(state) {
@@ -366,6 +423,12 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
366
423
  return '';
367
424
  }
368
425
  async resolveUpgradeRejection(request, path) {
426
+ if (this.isShuttingDown) {
427
+ return {
428
+ body: 'WebSocket server is shutting down.',
429
+ status: 503
430
+ };
431
+ }
369
432
  if (!this.tryReserveUpgradeSlot()) {
370
433
  return {
371
434
  body: 'WebSocket connection limit exceeded.',
@@ -442,6 +505,13 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
442
505
  }
443
506
  return configured;
444
507
  }
508
+ resolveShutdownTimeoutMs() {
509
+ const configured = this.moduleOptions.shutdown?.timeoutMs;
510
+ if (typeof configured !== 'number' || !Number.isFinite(configured) || configured <= 0) {
511
+ return DEFAULT_WEBSOCKET_SHUTDOWN_TIMEOUT_MS;
512
+ }
513
+ return Math.floor(configured);
514
+ }
445
515
  async shutdown() {
446
516
  if (this.shutdownPromise) {
447
517
  await this.shutdownPromise;
@@ -451,14 +521,112 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
451
521
  await this.shutdownPromise;
452
522
  }
453
523
  async runShutdownLifecycle() {
524
+ this.isShuttingDown = true;
454
525
  if (hasCloudflareWorkerWebSocketBindingHost(this.adapter)) {
455
526
  this.adapter.configureWebSocketBinding(undefined);
456
527
  }
528
+ const shutdownTimeoutMs = this.resolveShutdownTimeoutMs();
529
+ await this.awaitPendingUpgradeOperations(shutdownTimeoutMs);
530
+ await this.closeActiveSockets(shutdownTimeoutMs);
457
531
  this.pendingUpgradeReservations = 0;
458
532
  this.socketRegistry.clear();
459
533
  this.socketRooms.clear();
534
+ this.socketStates.clear();
460
535
  this.roomSockets.clear();
461
536
  }
537
+ trackPendingUpgradeOperation(operation) {
538
+ if (typeof operation === 'function') {
539
+ return (...args) => this.trackPendingUpgradeOperation(operation(...args));
540
+ }
541
+ let trackedOperation;
542
+ trackedOperation = operation.then(() => undefined, () => undefined).finally(() => {
543
+ if (trackedOperation) {
544
+ this.pendingUpgradeOperations.delete(trackedOperation);
545
+ }
546
+ });
547
+ this.pendingUpgradeOperations.add(trackedOperation);
548
+ return operation;
549
+ }
550
+ async awaitPendingUpgradeOperations(timeoutMs) {
551
+ if (this.pendingUpgradeOperations.size === 0) {
552
+ return;
553
+ }
554
+ await new Promise((resolve, reject) => {
555
+ let settled = false;
556
+ const timeout = setTimeout(() => {
557
+ if (settled) {
558
+ return;
559
+ }
560
+ settled = true;
561
+ reject(new Error(`Timed out while waiting for in-flight Cloudflare Worker websocket upgrades after ${String(timeoutMs)}ms.`));
562
+ }, timeoutMs);
563
+ Promise.all([...this.pendingUpgradeOperations]).then(() => {
564
+ if (settled) {
565
+ return;
566
+ }
567
+ settled = true;
568
+ clearTimeout(timeout);
569
+ resolve();
570
+ }).catch(error => {
571
+ if (settled) {
572
+ return;
573
+ }
574
+ settled = true;
575
+ clearTimeout(timeout);
576
+ reject(error);
577
+ });
578
+ }).catch(error => {
579
+ this.logger.error(`Failed to wait for in-flight Cloudflare Worker websocket upgrades within ${String(timeoutMs)}ms.`, error, LIFECYCLE_LOG_CONTEXT);
580
+ });
581
+ }
582
+ async closeActiveSockets(timeoutMs) {
583
+ const activeSockets = [...this.socketRegistry.entries()];
584
+ if (activeSockets.length === 0) {
585
+ return;
586
+ }
587
+ const activeStates = activeSockets.map(([socketId]) => this.socketStates.get(socketId)).filter(state => state !== undefined);
588
+ for (const [, socket] of activeSockets) {
589
+ if (socket.readyState === WEBSOCKET_OPEN_READY_STATE) {
590
+ socket.close(1001, 'Server shutting down');
591
+ }
592
+ }
593
+ await this.awaitHandlerQueueDrain(activeStates, timeoutMs);
594
+ }
595
+ async awaitHandlerQueueDrain(states, timeoutMs) {
596
+ if (states.length === 0) {
597
+ return;
598
+ }
599
+ await new Promise((resolve, reject) => {
600
+ let settled = false;
601
+ const timeout = setTimeout(() => {
602
+ if (settled) {
603
+ return;
604
+ }
605
+ settled = true;
606
+ reject(new Error(`Timed out while closing Cloudflare Worker websocket connections after ${String(timeoutMs)}ms.`));
607
+ }, timeoutMs);
608
+ Promise.all(states.map(async state => {
609
+ await state.connectLifecyclePromise;
610
+ await state.disconnectLifecyclePromise;
611
+ })).then(() => {
612
+ if (settled) {
613
+ return;
614
+ }
615
+ settled = true;
616
+ clearTimeout(timeout);
617
+ resolve();
618
+ }).catch(error => {
619
+ if (settled) {
620
+ return;
621
+ }
622
+ settled = true;
623
+ clearTimeout(timeout);
624
+ reject(error);
625
+ });
626
+ }).catch(error => {
627
+ this.logger.error(`Failed to close Cloudflare Worker websocket connections within ${String(timeoutMs)}ms.`, error, LIFECYCLE_LOG_CONTEXT);
628
+ });
629
+ }
462
630
  joinRoom(socketId, room) {
463
631
  let rooms = this.socketRooms.get(socketId);
464
632
  if (!rooms) {
@@ -519,6 +687,7 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
519
687
  return;
520
688
  }
521
689
  this.socketRegistry.delete(socketId);
690
+ this.socketStates.delete(socketId);
522
691
  const rooms = this.socketRooms.get(socketId);
523
692
  if (rooms) {
524
693
  for (const room of rooms) {
@@ -1,30 +1,60 @@
1
1
  import type { WebSocketModuleOptions as SharedWebSocketModuleOptions } from '../types.js';
2
+ /**
3
+ * Defines the cloudflare worker web socket message type.
4
+ */
2
5
  export type CloudflareWorkerWebSocketMessage = ArrayBuffer | ArrayBufferView | Blob | string;
6
+ /**
7
+ * Describes the cloudflare worker web socket contract.
8
+ */
3
9
  export interface CloudflareWorkerWebSocket extends Pick<WebSocket, 'addEventListener' | 'close' | 'removeEventListener' | 'send'> {
4
10
  readonly readyState: number;
5
11
  accept(): void;
6
12
  }
13
+ /**
14
+ * Describes the cloudflare worker web socket pair contract.
15
+ */
7
16
  export interface CloudflareWorkerWebSocketPair {
8
17
  0: CloudflareWorkerWebSocket;
9
18
  1: CloudflareWorkerWebSocket;
10
19
  }
20
+ /**
21
+ * Describes the cloudflare worker web socket upgrade result contract.
22
+ */
11
23
  export interface CloudflareWorkerWebSocketUpgradeResult {
12
24
  response: Response;
13
25
  serverSocket: CloudflareWorkerWebSocket;
14
26
  }
27
+ /**
28
+ * Describes the cloudflare worker web socket upgrade host contract.
29
+ */
15
30
  export interface CloudflareWorkerWebSocketUpgradeHost {
16
31
  upgrade(request: Request): CloudflareWorkerWebSocketUpgradeResult;
17
32
  }
33
+ /**
34
+ * Describes the cloudflare worker web socket binding contract.
35
+ */
18
36
  export interface CloudflareWorkerWebSocketBinding {
19
37
  fetch(request: Request, host: CloudflareWorkerWebSocketUpgradeHost): Response | Promise<Response>;
20
38
  }
39
+ /**
40
+ * Describes the cloudflare worker web socket binding host contract.
41
+ */
21
42
  export interface CloudflareWorkerWebSocketBindingHost {
22
43
  configureWebSocketBinding(binding: CloudflareWorkerWebSocketBinding | undefined): void;
23
44
  }
45
+ /**
46
+ * Defines the typed on message handler type.
47
+ */
24
48
  export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = (payload: TEvents[K], socket: CloudflareWorkerWebSocket, request: Request) => void | Promise<void>;
49
+ /**
50
+ * Describes the web socket gateway context contract.
51
+ */
25
52
  export interface WebSocketGatewayContext {
26
53
  request: Request;
27
54
  socket: CloudflareWorkerWebSocket;
28
55
  }
56
+ /**
57
+ * Defines the web socket module options type.
58
+ */
29
59
  export type WebSocketModuleOptions = SharedWebSocketModuleOptions;
30
60
  //# sourceMappingURL=cloudflare-workers-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-workers-types.d.ts","sourceRoot":"","sources":["../../src/cloudflare-workers/cloudflare-workers-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,IAAI,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAE1F,MAAM,MAAM,gCAAgC,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,MAAM,CAAC;AAE7F,MAAM,WAAW,yBACf,SAAQ,IAAI,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,GAAG,qBAAqB,GAAG,MAAM,CAAC;IACtF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,6BAA6B;IAC5C,CAAC,EAAE,yBAAyB,CAAC;IAC7B,CAAC,EAAE,yBAAyB,CAAC;CAC9B;AAED,MAAM,WAAW,sCAAsC;IACrD,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,yBAAyB,CAAC;CACzC;AAED,MAAM,WAAW,oCAAoC;IACnD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,sCAAsC,CAAC;CACnE;AAED,MAAM,WAAW,gCAAgC;IAC/C,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,oCAAoC,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACnG;AAED,MAAM,WAAW,oCAAoC;IACnD,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,GAAG,SAAS,GAAG,IAAI,CAAC;CACxF;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,yBAAyB,EACjC,OAAO,EAAE,OAAO,KACb,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,yBAAyB,CAAC;CACnC;AAED,MAAM,MAAM,sBAAsB,GAAG,4BAA4B,CAAC"}
1
+ {"version":3,"file":"cloudflare-workers-types.d.ts","sourceRoot":"","sources":["../../src/cloudflare-workers/cloudflare-workers-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,IAAI,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAE1F;;GAEG;AACH,MAAM,MAAM,gCAAgC,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,MAAM,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,yBACf,SAAQ,IAAI,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,GAAG,qBAAqB,GAAG,MAAM,CAAC;IACtF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,CAAC,EAAE,yBAAyB,CAAC;IAC7B,CAAC,EAAE,yBAAyB,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,yBAAyB,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACnD,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,sCAAsC,CAAC;CACnE;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,oCAAoC,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACnG;AAED;;GAEG;AACH,MAAM,WAAW,oCAAoC;IACnD,yBAAyB,CAAC,OAAO,EAAE,gCAAgC,GAAG,SAAS,GAAG,IAAI,CAAC;CACxF;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,yBAAyB,EACjC,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,yBAAyB,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,4BAA4B,CAAC"}
@@ -12,20 +12,26 @@ export declare class DenoWebSocketGatewayLifecycleService implements OnApplicati
12
12
  private readonly logger;
13
13
  private readonly adapter;
14
14
  private readonly moduleOptions;
15
+ private isShuttingDown;
16
+ private readonly pendingUpgradeOperations;
15
17
  private pendingUpgradeReservations;
16
18
  private readonly roomSockets;
17
19
  private shutdownPromise;
18
20
  private readonly socketRegistry;
19
21
  private readonly socketRooms;
22
+ private readonly socketStates;
20
23
  constructor(runtimeContainer: Container, compiledModules: readonly CompiledModule[], logger: ApplicationLogger, adapter: HttpApplicationAdapter, moduleOptions: WebSocketModuleOptions);
21
24
  onApplicationBootstrap(): Promise<void>;
22
25
  private assertNoServerBackedGatewayOptIn;
23
26
  onApplicationShutdown(): Promise<void>;
24
27
  onModuleDestroy(): Promise<void>;
25
28
  private createBinding;
29
+ private handleUpgradeRequest;
26
30
  private groupDescriptorsByPath;
27
31
  private bindConnectionHandlers;
28
32
  private createConnectionHandlerState;
33
+ private settleConnectLifecycle;
34
+ private settleDisconnectLifecycle;
29
35
  private attachConnectionListeners;
30
36
  private getBufferedMessageCount;
31
37
  private getQueuedMessageCount;
@@ -50,8 +56,13 @@ export declare class DenoWebSocketGatewayLifecycleService implements OnApplicati
50
56
  private tryReserveUpgradeSlot;
51
57
  private releaseUpgradeReservation;
52
58
  private resolveMaxPayloadBytes;
59
+ private resolveShutdownTimeoutMs;
53
60
  private shutdown;
54
61
  private runShutdownLifecycle;
62
+ private trackPendingUpgradeOperation;
63
+ private awaitPendingUpgradeOperations;
64
+ private closeActiveSockets;
65
+ private awaitHandlerQueueDrain;
55
66
  joinRoom(socketId: string, room: string): void;
56
67
  leaveRoom(socketId: string, room: string): void;
57
68
  broadcastToRoom(room: string, event: string, data: unknown): void;
@@ -1 +1 @@
1
- {"version":3,"file":"deno-service.d.ts","sourceRoot":"","sources":["../../src/deno/deno-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEzI,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAa3D,OAAO,KAAK,EAA8B,oBAAoB,EAA6B,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAKV,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AAsGzB;;GAEG;AACH,qBACa,oCACX,YAAW,sBAAsB,EAAE,qBAAqB,EAAE,eAAe,EAAE,oBAAoB;IAS7F,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAXhC,OAAO,CAAC,0BAA0B,CAAK;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0C;IACzE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;gBAG3C,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,sBAAsB,EAC/B,aAAa,EAAE,sBAAsB;IAGlD,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB7C,OAAO,CAAC,gCAAgC;IAclC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,OAAO,CAAC,aAAa;IAoDrB,OAAO,CAAC,sBAAsB;YAmBhB,sBAAsB;IAgBpC,OAAO,CAAC,4BAA4B;IAqBpC,OAAO,CAAC,yBAAyB;IAwCjC,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,4BAA4B;IASpC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,qBAAqB;IA0C7B,OAAO,CAAC,sBAAsB;YAyDhB,iBAAiB;YAqBjB,gBAAgB;IAQ9B,OAAO,CAAC,yBAAyB;YAsBnB,yBAAyB;YAczB,kBAAkB;YAgBlB,yBAAyB;YAUzB,8BAA8B;IAqB5C,OAAO,CAAC,YAAY;YAUN,uBAAuB;IAmDrC,OAAO,CAAC,qBAAqB;IAmB7B,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,8BAA8B;IAItC,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,yBAAyB;IAMjC,OAAO,CAAC,sBAAsB;YAUhB,QAAQ;YAUR,oBAAoB;IAWlC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAmB9C,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAc/C,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IA4BjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;IAU/C,OAAO,CAAC,gBAAgB;CAoBzB"}
1
+ {"version":3,"file":"deno-service.d.ts","sourceRoot":"","sources":["../../src/deno/deno-service.ts"],"names":[],"mappings":"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"}