@fluojs/websockets 1.0.8 → 2.0.0
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.
- package/README.ko.md +19 -6
- package/README.md +19 -6
- package/dist/bun/bun-service.d.ts +3 -1
- package/dist/bun/bun-service.d.ts.map +1 -1
- package/dist/bun/bun-service.js +75 -26
- package/dist/bun/bun-types.d.ts +5 -3
- package/dist/bun/bun-types.d.ts.map +1 -1
- package/dist/bun.d.ts +2 -1
- package/dist/bun.d.ts.map +1 -1
- package/dist/bun.js +2 -1
- package/dist/cloudflare-workers/cloudflare-workers-service.d.ts +4 -0
- package/dist/cloudflare-workers/cloudflare-workers-service.d.ts.map +1 -1
- package/dist/cloudflare-workers/cloudflare-workers-service.js +89 -20
- package/dist/cloudflare-workers/cloudflare-workers-types.d.ts +2 -2
- package/dist/cloudflare-workers/cloudflare-workers-types.d.ts.map +1 -1
- package/dist/cloudflare-workers.d.ts +2 -1
- package/dist/cloudflare-workers.d.ts.map +1 -1
- package/dist/cloudflare-workers.js +2 -1
- package/dist/decorators.d.ts +2 -2
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +4 -4
- package/dist/deno/deno-service.d.ts +4 -0
- package/dist/deno/deno-service.d.ts.map +1 -1
- package/dist/deno/deno-service.js +85 -16
- package/dist/deno/deno-types.d.ts +2 -2
- package/dist/deno/deno-types.d.ts.map +1 -1
- package/dist/deno.d.ts +2 -1
- package/dist/deno.d.ts.map +1 -1
- package/dist/deno.js +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/shared.d.ts +6 -3
- package/dist/internal/shared.d.ts.map +1 -1
- package/dist/internal/shared.js +26 -6
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +6 -0
- package/dist/node/node-service.d.ts +3 -2
- package/dist/node/node-service.d.ts.map +1 -1
- package/dist/node/node-service.js +21 -5
- package/dist/node/node-types.d.ts +2 -2
- package/dist/node/node-types.d.ts.map +1 -1
- package/dist/node.d.ts +1 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +2 -1
- package/dist/types.d.ts +26 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +21 -13
|
@@ -14,9 +14,6 @@ const DEFAULT_MAX_WEBSOCKET_PAYLOAD_BYTES = 1_048_576;
|
|
|
14
14
|
const DEFAULT_WEBSOCKET_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
15
15
|
const LIFECYCLE_LOG_CONTEXT = 'WebSocketGatewayLifecycleService';
|
|
16
16
|
const WEBSOCKET_OPEN_READY_STATE = 1;
|
|
17
|
-
function hasCloudflareWorkerWebSocketBindingHost(adapter) {
|
|
18
|
-
return 'configureWebSocketBinding' in adapter && typeof adapter.configureWebSocketBinding === 'function';
|
|
19
|
-
}
|
|
20
17
|
function isWebSocketUpgradeRequest(request) {
|
|
21
18
|
return request.headers.get('upgrade')?.toLowerCase() === 'websocket';
|
|
22
19
|
}
|
|
@@ -72,15 +69,15 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
72
69
|
return;
|
|
73
70
|
}
|
|
74
71
|
assertNoFetchStyleServerBackedGatewayOptIn(descriptors, 'cloudflare-workers');
|
|
75
|
-
resolveSupportedFetchStyleRealtimeCapability(this.adapter, {
|
|
72
|
+
const capability = resolveSupportedFetchStyleRealtimeCapability(this.adapter, {
|
|
76
73
|
packageSubpath: 'cloudflare-workers',
|
|
77
74
|
platformPackage: '@fluojs/platform-cloudflare-workers',
|
|
78
75
|
runtimeName: 'Cloudflare Workers'
|
|
79
76
|
});
|
|
80
|
-
if (!
|
|
81
|
-
throw new Error('Cloudflare Workers WebSocket gateway bootstrap requires the selected adapter to expose
|
|
77
|
+
if (!capability.bindingInstallation) {
|
|
78
|
+
throw new Error('Cloudflare Workers WebSocket gateway bootstrap requires the selected adapter to expose websocket binding installation through its realtime capability. Use @fluojs/platform-cloudflare-workers with @fluojs/websockets/cloudflare-workers.');
|
|
82
79
|
}
|
|
83
|
-
|
|
80
|
+
capability.bindingInstallation.install(this.createBinding(descriptors));
|
|
84
81
|
}
|
|
85
82
|
async onApplicationShutdown() {
|
|
86
83
|
await this.shutdown();
|
|
@@ -141,7 +138,7 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
141
138
|
}
|
|
142
139
|
serverSocket.accept();
|
|
143
140
|
void this.trackPendingUpgradeOperation(this.bindConnectionHandlers(serverSocket, request, matchedDescriptors)).catch(error => {
|
|
144
|
-
this.
|
|
141
|
+
this.unregisterTrackedSocketWithDeferredStateCleanup(this.findSocketId(serverSocket));
|
|
145
142
|
this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
|
|
146
143
|
serverSocket.close(1011, 'Internal server error');
|
|
147
144
|
});
|
|
@@ -187,11 +184,13 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
187
184
|
bufferedDisconnect: undefined,
|
|
188
185
|
bufferedMessages: [],
|
|
189
186
|
bufferedMessagesStartIndex: 0,
|
|
187
|
+
cleanupScheduled: false,
|
|
190
188
|
connectLifecycleSettled: false,
|
|
191
189
|
connectLifecyclePromise: connectLifecycle.promise,
|
|
192
190
|
descriptors,
|
|
193
191
|
disconnectLifecycleSettled: false,
|
|
194
192
|
disconnectLifecyclePromise: disconnectLifecycle.promise,
|
|
193
|
+
disconnectDispatchScheduled: false,
|
|
195
194
|
enqueuedMessageCount: 0,
|
|
196
195
|
handlerQueue: Promise.resolve(),
|
|
197
196
|
handlersReady: false,
|
|
@@ -202,7 +201,8 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
202
201
|
resolveConnectLifecycle: connectLifecycle.resolve,
|
|
203
202
|
resolveDisconnectLifecycle: disconnectLifecycle.resolve,
|
|
204
203
|
resolved: [],
|
|
205
|
-
socketId: crypto.randomUUID()
|
|
204
|
+
socketId: crypto.randomUUID(),
|
|
205
|
+
terminalFailureHandled: false
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
208
|
settleConnectLifecycle(state) {
|
|
@@ -231,11 +231,11 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
231
231
|
this.enqueueMessageDispatch(state, socket, request, event.data);
|
|
232
232
|
});
|
|
233
233
|
socket.addEventListener('error', event => {
|
|
234
|
-
this.
|
|
234
|
+
this.closeSocketAfterTerminalFailure(state, socket, 'Socket error');
|
|
235
235
|
this.logger.error('WebSocket gateway socket emitted an error.', event, LIFECYCLE_LOG_CONTEXT);
|
|
236
236
|
});
|
|
237
237
|
socket.addEventListener('close', event => {
|
|
238
|
-
this.
|
|
238
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
239
239
|
const closeEvent = event;
|
|
240
240
|
const disconnectEvent = {
|
|
241
241
|
code: closeEvent.code,
|
|
@@ -300,13 +300,16 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
300
300
|
this.logger.warn(`WebSocket connection ${state.socketId} dropped the oldest pending message due to buffer limit (${String(limit)}).`, LIFECYCLE_LOG_CONTEXT);
|
|
301
301
|
}
|
|
302
302
|
enqueueMessageDispatch(state, socket, request, message) {
|
|
303
|
+
if (state.terminalFailureHandled) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
303
306
|
const limit = isFinitePositiveInteger(this.moduleOptions.buffer?.maxPendingMessagesPerSocket) ? this.moduleOptions.buffer.maxPendingMessagesPerSocket : DEFAULT_MAX_PENDING_MESSAGES_PER_SOCKET;
|
|
304
307
|
const policy = this.moduleOptions.buffer?.overflowPolicy ?? 'drop-oldest';
|
|
305
308
|
if (this.getQueuedMessageCount(state) >= limit) {
|
|
306
309
|
if (policy === 'close') {
|
|
307
310
|
socket.close(1013, 'Ready-state message queue limit exceeded');
|
|
308
311
|
this.clearQueuedMessages(state);
|
|
309
|
-
this.
|
|
312
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
310
313
|
this.logger.warn(`WebSocket connection ${state.socketId} exceeded ready-state message queue limit (${String(limit)}). Connection closed.`, LIFECYCLE_LOG_CONTEXT);
|
|
311
314
|
return;
|
|
312
315
|
}
|
|
@@ -341,7 +344,19 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
341
344
|
continue;
|
|
342
345
|
}
|
|
343
346
|
const normalizedMessage = await this.normalizeMessage(nextMessage);
|
|
344
|
-
await dispatchGatewayMessage(state.resolved, socket, request, normalizedMessage, this.
|
|
347
|
+
const outcome = await dispatchGatewayMessage(state.resolved, socket, request, normalizedMessage, state.socketId, this.moduleOptions.replies?.mode, message => {
|
|
348
|
+
try {
|
|
349
|
+
socket.send(message);
|
|
350
|
+
return true;
|
|
351
|
+
} catch {
|
|
352
|
+
this.closeSocketAfterTerminalFailure(state, socket, 'Send failed');
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}, this.logger, LIFECYCLE_LOG_CONTEXT);
|
|
356
|
+
if (outcome === 'terminal') {
|
|
357
|
+
this.clearQueuedMessages(state);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
345
360
|
}
|
|
346
361
|
this.clearQueuedMessages(state);
|
|
347
362
|
}
|
|
@@ -352,6 +367,10 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
352
367
|
return message;
|
|
353
368
|
}
|
|
354
369
|
enqueueDisconnectDispatch(state, socket, disconnectEvent) {
|
|
370
|
+
if (state.disconnectDispatchScheduled) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
state.disconnectDispatchScheduled = true;
|
|
355
374
|
state.handlerQueue = state.handlerQueue.then(async () => {
|
|
356
375
|
await dispatchGatewayDisconnect(state.resolved, socket, disconnectEvent.code, disconnectEvent.reason, state.socketId, this.logger, LIFECYCLE_LOG_CONTEXT);
|
|
357
376
|
}).catch(error => {
|
|
@@ -557,10 +576,7 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
557
576
|
}
|
|
558
577
|
async closeActiveSockets(timeoutMs) {
|
|
559
578
|
const activeSockets = [...this.socketRegistry.entries()];
|
|
560
|
-
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const activeStates = activeSockets.map(([socketId]) => this.socketStates.get(socketId)).filter(state => state !== undefined);
|
|
579
|
+
const activeStates = [...this.socketStates.values()];
|
|
564
580
|
for (const [, socket] of activeSockets) {
|
|
565
581
|
if (socket.readyState === WEBSOCKET_OPEN_READY_STATE) {
|
|
566
582
|
socket.close(1001, 'Server shutting down');
|
|
@@ -568,6 +584,15 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
568
584
|
}
|
|
569
585
|
await this.awaitHandlerQueueDrain(activeStates, timeoutMs);
|
|
570
586
|
}
|
|
587
|
+
scheduleSocketStateCleanup(state) {
|
|
588
|
+
if (state.cleanupScheduled) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
state.cleanupScheduled = true;
|
|
592
|
+
void Promise.all([state.connectLifecyclePromise, state.disconnectLifecyclePromise]).finally(() => {
|
|
593
|
+
this.socketStates.delete(state.socketId);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
571
596
|
async awaitHandlerQueueDrain(states, timeoutMs) {
|
|
572
597
|
if (states.length === 0) {
|
|
573
598
|
return;
|
|
@@ -604,6 +629,10 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
604
629
|
});
|
|
605
630
|
}
|
|
606
631
|
joinRoom(socketId, room) {
|
|
632
|
+
const socket = this.socketRegistry.get(socketId);
|
|
633
|
+
if (!socket || socket.readyState !== WEBSOCKET_OPEN_READY_STATE) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
607
636
|
let rooms = this.socketRooms.get(socketId);
|
|
608
637
|
if (!rooms) {
|
|
609
638
|
rooms = new Set();
|
|
@@ -646,7 +675,13 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
646
675
|
try {
|
|
647
676
|
socket.send(message);
|
|
648
677
|
} catch (error) {
|
|
649
|
-
this.
|
|
678
|
+
const state = this.socketStates.get(socketId);
|
|
679
|
+
if (state) {
|
|
680
|
+
this.closeSocketAfterTerminalFailure(state, socket, 'Send failed');
|
|
681
|
+
} else {
|
|
682
|
+
socket.close(1011, 'Send failed');
|
|
683
|
+
this.unregisterSocket(socketId);
|
|
684
|
+
}
|
|
650
685
|
this.logger.warn(`WebSocket connection ${socketId} failed to send a room broadcast and was removed. ${error instanceof Error ? error.message : 'Unknown error.'}`, LIFECYCLE_LOG_CONTEXT);
|
|
651
686
|
}
|
|
652
687
|
}
|
|
@@ -658,12 +693,46 @@ class CloudflareWorkersWebSocketGatewayLifecycleService {
|
|
|
658
693
|
}
|
|
659
694
|
return new Set(rooms);
|
|
660
695
|
}
|
|
661
|
-
|
|
696
|
+
closeSocketAfterTerminalFailure(state, socket, reason) {
|
|
697
|
+
if (state.terminalFailureHandled) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
state.terminalFailureHandled = true;
|
|
701
|
+
this.clearQueuedMessages(state);
|
|
702
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
703
|
+
socket.close(1011, reason);
|
|
704
|
+
const disconnectEvent = {
|
|
705
|
+
code: 1011,
|
|
706
|
+
reason
|
|
707
|
+
};
|
|
708
|
+
if (!state.handlersReady) {
|
|
709
|
+
state.bufferedDisconnect ??= disconnectEvent;
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
this.enqueueDisconnectDispatch(state, socket, disconnectEvent);
|
|
713
|
+
}
|
|
714
|
+
unregisterSocketWithDeferredStateCleanup(state) {
|
|
715
|
+
this.unregisterSocket(state.socketId, {
|
|
716
|
+
deleteState: false
|
|
717
|
+
});
|
|
718
|
+
this.scheduleSocketStateCleanup(state);
|
|
719
|
+
}
|
|
720
|
+
unregisterTrackedSocketWithDeferredStateCleanup(socketId) {
|
|
721
|
+
const state = this.socketStates.get(socketId);
|
|
722
|
+
if (!state) {
|
|
723
|
+
this.unregisterSocket(socketId);
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
727
|
+
}
|
|
728
|
+
unregisterSocket(socketId, options = {}) {
|
|
662
729
|
if (!socketId) {
|
|
663
730
|
return;
|
|
664
731
|
}
|
|
665
732
|
this.socketRegistry.delete(socketId);
|
|
666
|
-
|
|
733
|
+
if (options.deleteState !== false) {
|
|
734
|
+
this.socketStates.delete(socketId);
|
|
735
|
+
}
|
|
667
736
|
const rooms = this.socketRooms.get(socketId);
|
|
668
737
|
if (rooms) {
|
|
669
738
|
for (const room of rooms) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WebSocketModuleOptions as SharedWebSocketModuleOptions,
|
|
1
|
+
import type { WebSocketModuleOptions as SharedWebSocketModuleOptions, WebSocketUpgradeGuard as SharedWebSocketUpgradeGuard, WebSocketUpgradeContext, WebSocketUpgradeRejection } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Defines the cloudflare worker web socket message type.
|
|
4
4
|
*/
|
|
@@ -45,7 +45,7 @@ export interface CloudflareWorkerWebSocketBindingHost {
|
|
|
45
45
|
/**
|
|
46
46
|
* Defines the typed on message handler type.
|
|
47
47
|
*/
|
|
48
|
-
export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = (
|
|
48
|
+
export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = import('../types.js').TypedOnMessageHandler<TEvents, K, CloudflareWorkerWebSocket, Request>;
|
|
49
49
|
/**
|
|
50
50
|
* Describes the web socket gateway context contract.
|
|
51
51
|
*/
|
|
@@ -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,EACV,sBAAsB,IAAI,4BAA4B,EACtD,
|
|
1
|
+
{"version":3,"file":"cloudflare-workers-types.d.ts","sourceRoot":"","sources":["../../src/cloudflare-workers/cloudflare-workers-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,IAAI,4BAA4B,EACtD,qBAAqB,IAAI,2BAA2B,EACpD,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB;;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,OAAO,aAAa,EAAE,qBAAqB,CAC/I,OAAO,EACP,CAAC,EACD,yBAAyB,EACzB,OAAO,CACR,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,yBAAyB,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAC;AAEzE,YAAY,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
+
export * from './cloudflare-workers/cloudflare-workers.js';
|
|
1
2
|
export * from './decorators.js';
|
|
2
3
|
export * from './metadata.js';
|
|
3
|
-
export
|
|
4
|
+
export type { WebSocketEventEnvelope, WebSocketReplyMode } from './types.js';
|
|
4
5
|
//# sourceMappingURL=cloudflare-workers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-workers.d.ts","sourceRoot":"","sources":["../src/cloudflare-workers.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,
|
|
1
|
+
{"version":3,"file":"cloudflare-workers.d.ts","sourceRoot":"","sources":["../src/cloudflare-workers.ts"],"names":[],"mappings":"AAAA,cAAc,4CAA4C,CAAC;AAC3D,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/decorators.d.ts
CHANGED
|
@@ -33,8 +33,8 @@ export declare function WebSocketGateway(options?: WebSocketGatewayOptions): Cla
|
|
|
33
33
|
*
|
|
34
34
|
* class ChatGateway {
|
|
35
35
|
* @OnMessage('ping')
|
|
36
|
-
* handlePing(payload: unknown) {
|
|
37
|
-
*
|
|
36
|
+
* handlePing(payload: unknown, socket: { send(data: string): void }) {
|
|
37
|
+
* socket.send(JSON.stringify({ event: 'pong', data: payload }));
|
|
38
38
|
* }
|
|
39
39
|
* }
|
|
40
40
|
* ```
|
package/dist/decorators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAmC,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG9G,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AACnD,KAAK,mBAAmB,GAAG,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAmC,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG9G,KAAK,wBAAwB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAC1F,KAAK,yBAAyB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;AACjG,KAAK,kBAAkB,GAAG,wBAAwB,CAAC;AACnD,KAAK,mBAAmB,GAAG,yBAAyB,CAAC;AAgErD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,uBAA4B,GACpC,kBAAkB,CAMpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CACvB,OAAO,SAAS,iBAAiB,GAAG,iBAAiB,EACrD,CAAC,SAAS,MAAM,OAAO,GAAG,MAAM,OAAO,EACvC,KAAK,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,mBAAmB,CAKzC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,mBAAmB,CAI/C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,mBAAmB,CAIlD"}
|
package/dist/decorators.js
CHANGED
|
@@ -22,8 +22,8 @@ function defineStandardGatewayMetadata(metadata, options) {
|
|
|
22
22
|
}
|
|
23
23
|
function defineStandardHandlerMetadata(metadata, propertyKey, handlerMetadata) {
|
|
24
24
|
const bag = getStandardMetadataBag(metadata);
|
|
25
|
-
const
|
|
26
|
-
const map =
|
|
25
|
+
const inherited = bag[webSocketHandlerMetadataSymbol];
|
|
26
|
+
const map = Object.hasOwn(bag, webSocketHandlerMetadataSymbol) ? inherited ?? new Map() : new Map(inherited);
|
|
27
27
|
map.set(propertyKey, {
|
|
28
28
|
event: handlerMetadata.event,
|
|
29
29
|
type: handlerMetadata.type
|
|
@@ -79,8 +79,8 @@ export function WebSocketGateway(options = {}) {
|
|
|
79
79
|
*
|
|
80
80
|
* class ChatGateway {
|
|
81
81
|
* @OnMessage('ping')
|
|
82
|
-
* handlePing(payload: unknown) {
|
|
83
|
-
*
|
|
82
|
+
* handlePing(payload: unknown, socket: { send(data: string): void }) {
|
|
83
|
+
* socket.send(JSON.stringify({ event: 'pong', data: payload }));
|
|
84
84
|
* }
|
|
85
85
|
* }
|
|
86
86
|
* ```
|
|
@@ -61,11 +61,15 @@ export declare class DenoWebSocketGatewayLifecycleService implements OnApplicati
|
|
|
61
61
|
private trackPendingUpgradeOperation;
|
|
62
62
|
private awaitPendingUpgradeOperations;
|
|
63
63
|
private closeActiveSockets;
|
|
64
|
+
private scheduleSocketStateCleanup;
|
|
64
65
|
private awaitHandlerQueueDrain;
|
|
65
66
|
joinRoom(socketId: string, room: string): void;
|
|
66
67
|
leaveRoom(socketId: string, room: string): void;
|
|
67
68
|
broadcastToRoom(room: string, event: string, data: unknown): void;
|
|
68
69
|
getRooms(socketId: string): ReadonlySet<string>;
|
|
70
|
+
private closeSocketAfterTerminalFailure;
|
|
71
|
+
private unregisterSocketWithDeferredStateCleanup;
|
|
72
|
+
private unregisterTrackedSocketWithDeferredStateCleanup;
|
|
69
73
|
private unregisterSocket;
|
|
70
74
|
}
|
|
71
75
|
//# sourceMappingURL=deno-service.d.ts.map
|
|
@@ -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,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAgBzI,OAAO,KAAK,EAA8B,oBAAoB,EAA6B,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAKV,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"deno-service.d.ts","sourceRoot":"","sources":["../../src/deno/deno-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -146,7 +146,7 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
146
146
|
throw error;
|
|
147
147
|
}
|
|
148
148
|
void this.trackPendingUpgradeOperation(this.bindConnectionHandlers(socket, request, descriptors)).catch(error => {
|
|
149
|
-
this.
|
|
149
|
+
this.unregisterTrackedSocketWithDeferredStateCleanup(this.findSocketId(socket));
|
|
150
150
|
this.logger.error('WebSocket gateway open lifecycle failed.', error, LIFECYCLE_LOG_CONTEXT);
|
|
151
151
|
socket.close(1011, 'Internal server error');
|
|
152
152
|
});
|
|
@@ -192,11 +192,13 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
192
192
|
bufferedDisconnect: undefined,
|
|
193
193
|
bufferedMessages: [],
|
|
194
194
|
bufferedMessagesStartIndex: 0,
|
|
195
|
+
cleanupScheduled: false,
|
|
195
196
|
connectLifecycleSettled: false,
|
|
196
197
|
connectLifecyclePromise: connectLifecycle.promise,
|
|
197
198
|
descriptors,
|
|
198
199
|
disconnectLifecycleSettled: false,
|
|
199
200
|
disconnectLifecyclePromise: disconnectLifecycle.promise,
|
|
201
|
+
disconnectDispatchScheduled: false,
|
|
200
202
|
enqueuedMessageCount: 0,
|
|
201
203
|
handlerQueue: Promise.resolve(),
|
|
202
204
|
handlersReady: false,
|
|
@@ -207,7 +209,8 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
207
209
|
resolveConnectLifecycle: connectLifecycle.resolve,
|
|
208
210
|
resolveDisconnectLifecycle: disconnectLifecycle.resolve,
|
|
209
211
|
resolved: [],
|
|
210
|
-
socketId: crypto.randomUUID()
|
|
212
|
+
socketId: crypto.randomUUID(),
|
|
213
|
+
terminalFailureHandled: false
|
|
211
214
|
};
|
|
212
215
|
}
|
|
213
216
|
settleConnectLifecycle(state) {
|
|
@@ -236,11 +239,11 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
236
239
|
this.enqueueMessageDispatch(state, socket, request, event.data);
|
|
237
240
|
});
|
|
238
241
|
socket.addEventListener('error', event => {
|
|
239
|
-
this.
|
|
242
|
+
this.closeSocketAfterTerminalFailure(state, socket, 'Socket error');
|
|
240
243
|
this.logger.error('WebSocket gateway socket emitted an error.', event, LIFECYCLE_LOG_CONTEXT);
|
|
241
244
|
});
|
|
242
245
|
socket.addEventListener('close', event => {
|
|
243
|
-
this.
|
|
246
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
244
247
|
const disconnectEvent = {
|
|
245
248
|
code: event.code,
|
|
246
249
|
reason: event.reason
|
|
@@ -304,13 +307,16 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
304
307
|
this.logger.warn(`WebSocket connection ${state.socketId} dropped the oldest pending message due to buffer limit (${String(limit)}).`, LIFECYCLE_LOG_CONTEXT);
|
|
305
308
|
}
|
|
306
309
|
enqueueMessageDispatch(state, socket, request, message) {
|
|
310
|
+
if (state.terminalFailureHandled) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
307
313
|
const limit = isFinitePositiveInteger(this.moduleOptions.buffer?.maxPendingMessagesPerSocket) ? this.moduleOptions.buffer.maxPendingMessagesPerSocket : DEFAULT_MAX_PENDING_MESSAGES_PER_SOCKET;
|
|
308
314
|
const policy = this.moduleOptions.buffer?.overflowPolicy ?? 'drop-oldest';
|
|
309
315
|
if (this.getQueuedMessageCount(state) >= limit) {
|
|
310
316
|
if (policy === 'close') {
|
|
311
317
|
socket.close(1013, 'Ready-state message queue limit exceeded');
|
|
312
318
|
this.clearQueuedMessages(state);
|
|
313
|
-
this.
|
|
319
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
314
320
|
this.logger.warn(`WebSocket connection ${state.socketId} exceeded ready-state message queue limit (${String(limit)}). Connection closed.`, LIFECYCLE_LOG_CONTEXT);
|
|
315
321
|
return;
|
|
316
322
|
}
|
|
@@ -345,7 +351,19 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
345
351
|
continue;
|
|
346
352
|
}
|
|
347
353
|
const normalizedMessage = await this.normalizeMessage(nextMessage);
|
|
348
|
-
await dispatchGatewayMessage(state.resolved, socket, request, normalizedMessage, this.
|
|
354
|
+
const outcome = await dispatchGatewayMessage(state.resolved, socket, request, normalizedMessage, state.socketId, this.moduleOptions.replies?.mode, message => {
|
|
355
|
+
try {
|
|
356
|
+
socket.send(message);
|
|
357
|
+
return true;
|
|
358
|
+
} catch {
|
|
359
|
+
this.closeSocketAfterTerminalFailure(state, socket, 'Send failed');
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}, this.logger, LIFECYCLE_LOG_CONTEXT);
|
|
363
|
+
if (outcome === 'terminal') {
|
|
364
|
+
this.clearQueuedMessages(state);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
349
367
|
}
|
|
350
368
|
this.clearQueuedMessages(state);
|
|
351
369
|
}
|
|
@@ -356,6 +374,10 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
356
374
|
return message;
|
|
357
375
|
}
|
|
358
376
|
enqueueDisconnectDispatch(state, socket, disconnectEvent) {
|
|
377
|
+
if (state.disconnectDispatchScheduled) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
state.disconnectDispatchScheduled = true;
|
|
359
381
|
state.handlerQueue = state.handlerQueue.then(async () => {
|
|
360
382
|
await dispatchGatewayDisconnect(state.resolved, socket, disconnectEvent.code, disconnectEvent.reason, state.socketId, this.logger, LIFECYCLE_LOG_CONTEXT);
|
|
361
383
|
}).catch(error => {
|
|
@@ -511,9 +533,6 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
511
533
|
}
|
|
512
534
|
async runShutdownLifecycle() {
|
|
513
535
|
this.isShuttingDown = true;
|
|
514
|
-
if (hasDenoWebSocketBindingHost(this.adapter)) {
|
|
515
|
-
this.adapter.configureWebSocketBinding(undefined);
|
|
516
|
-
}
|
|
517
536
|
const shutdownTimeoutMs = this.resolveShutdownTimeoutMs();
|
|
518
537
|
await this.awaitPendingUpgradeOperations(shutdownTimeoutMs);
|
|
519
538
|
await this.closeActiveSockets(shutdownTimeoutMs);
|
|
@@ -570,10 +589,7 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
570
589
|
}
|
|
571
590
|
async closeActiveSockets(timeoutMs) {
|
|
572
591
|
const activeSockets = [...this.socketRegistry.entries()];
|
|
573
|
-
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
const activeStates = activeSockets.map(([socketId]) => this.socketStates.get(socketId)).filter(state => state !== undefined);
|
|
592
|
+
const activeStates = [...this.socketStates.values()];
|
|
577
593
|
for (const [, socket] of activeSockets) {
|
|
578
594
|
if (socket.readyState === WEBSOCKET_OPEN_READY_STATE) {
|
|
579
595
|
socket.close(1001, 'Server shutting down');
|
|
@@ -581,6 +597,15 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
581
597
|
}
|
|
582
598
|
await this.awaitHandlerQueueDrain(activeStates, timeoutMs);
|
|
583
599
|
}
|
|
600
|
+
scheduleSocketStateCleanup(state) {
|
|
601
|
+
if (state.cleanupScheduled) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
state.cleanupScheduled = true;
|
|
605
|
+
void Promise.all([state.connectLifecyclePromise, state.disconnectLifecyclePromise]).finally(() => {
|
|
606
|
+
this.socketStates.delete(state.socketId);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
584
609
|
async awaitHandlerQueueDrain(states, timeoutMs) {
|
|
585
610
|
if (states.length === 0) {
|
|
586
611
|
return;
|
|
@@ -617,6 +642,10 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
617
642
|
});
|
|
618
643
|
}
|
|
619
644
|
joinRoom(socketId, room) {
|
|
645
|
+
const socket = this.socketRegistry.get(socketId);
|
|
646
|
+
if (!socket || socket.readyState !== WEBSOCKET_OPEN_READY_STATE) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
620
649
|
let rooms = this.socketRooms.get(socketId);
|
|
621
650
|
if (!rooms) {
|
|
622
651
|
rooms = new Set();
|
|
@@ -659,7 +688,13 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
659
688
|
try {
|
|
660
689
|
socket.send(message);
|
|
661
690
|
} catch (error) {
|
|
662
|
-
this.
|
|
691
|
+
const state = this.socketStates.get(socketId);
|
|
692
|
+
if (state) {
|
|
693
|
+
this.closeSocketAfterTerminalFailure(state, socket, 'Send failed');
|
|
694
|
+
} else {
|
|
695
|
+
socket.close(1011, 'Send failed');
|
|
696
|
+
this.unregisterSocket(socketId);
|
|
697
|
+
}
|
|
663
698
|
this.logger.warn(`WebSocket connection ${socketId} failed to send a room broadcast and was removed. ${error instanceof Error ? error.message : 'Unknown error.'}`, LIFECYCLE_LOG_CONTEXT);
|
|
664
699
|
}
|
|
665
700
|
}
|
|
@@ -671,12 +706,46 @@ class DenoWebSocketGatewayLifecycleService {
|
|
|
671
706
|
}
|
|
672
707
|
return new Set(rooms);
|
|
673
708
|
}
|
|
674
|
-
|
|
709
|
+
closeSocketAfterTerminalFailure(state, socket, reason) {
|
|
710
|
+
if (state.terminalFailureHandled) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
state.terminalFailureHandled = true;
|
|
714
|
+
this.clearQueuedMessages(state);
|
|
715
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
716
|
+
socket.close(1011, reason);
|
|
717
|
+
const disconnectEvent = {
|
|
718
|
+
code: 1011,
|
|
719
|
+
reason
|
|
720
|
+
};
|
|
721
|
+
if (!state.handlersReady) {
|
|
722
|
+
state.bufferedDisconnect ??= disconnectEvent;
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
this.enqueueDisconnectDispatch(state, socket, disconnectEvent);
|
|
726
|
+
}
|
|
727
|
+
unregisterSocketWithDeferredStateCleanup(state) {
|
|
728
|
+
this.unregisterSocket(state.socketId, {
|
|
729
|
+
deleteState: false
|
|
730
|
+
});
|
|
731
|
+
this.scheduleSocketStateCleanup(state);
|
|
732
|
+
}
|
|
733
|
+
unregisterTrackedSocketWithDeferredStateCleanup(socketId) {
|
|
734
|
+
const state = this.socketStates.get(socketId);
|
|
735
|
+
if (!state) {
|
|
736
|
+
this.unregisterSocket(socketId);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
this.unregisterSocketWithDeferredStateCleanup(state);
|
|
740
|
+
}
|
|
741
|
+
unregisterSocket(socketId, options = {}) {
|
|
675
742
|
if (!socketId) {
|
|
676
743
|
return;
|
|
677
744
|
}
|
|
678
745
|
this.socketRegistry.delete(socketId);
|
|
679
|
-
|
|
746
|
+
if (options.deleteState !== false) {
|
|
747
|
+
this.socketStates.delete(socketId);
|
|
748
|
+
}
|
|
680
749
|
const rooms = this.socketRooms.get(socketId);
|
|
681
750
|
if (rooms) {
|
|
682
751
|
for (const room of rooms) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WebSocketModuleOptions as SharedWebSocketModuleOptions,
|
|
1
|
+
import type { WebSocketModuleOptions as SharedWebSocketModuleOptions, WebSocketUpgradeGuard as SharedWebSocketUpgradeGuard, WebSocketUpgradeContext, WebSocketUpgradeRejection } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Defines the Deno websocket inbound message contract.
|
|
4
4
|
*
|
|
@@ -42,7 +42,7 @@ export interface DenoWebSocketBindingHost<TSocket extends DenoServerWebSocket =
|
|
|
42
42
|
/**
|
|
43
43
|
* Defines the typed on message handler type.
|
|
44
44
|
*/
|
|
45
|
-
export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = (
|
|
45
|
+
export type TypedOnMessageHandler<TEvents extends Record<string, unknown>, K extends keyof TEvents> = import('../types.js').TypedOnMessageHandler<TEvents, K, DenoServerWebSocket, Request>;
|
|
46
46
|
/**
|
|
47
47
|
* Describes the web socket gateway context contract.
|
|
48
48
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno-types.d.ts","sourceRoot":"","sources":["../../src/deno/deno-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,IAAI,4BAA4B,EACtD,
|
|
1
|
+
{"version":3,"file":"deno-types.d.ts","sourceRoot":"","sources":["../../src/deno/deno-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,IAAI,4BAA4B,EACtD,qBAAqB,IAAI,2BAA2B,EACpD,uBAAuB,EACvB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,MAAM,CAAC;AAEjF;;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,OAAO,aAAa,EAAE,qBAAqB,CAC/I,OAAO,EACP,CAAC,EACD,mBAAmB,EACnB,OAAO,CACR,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAC;AAEzE,YAAY,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC"}
|
package/dist/deno.d.ts
CHANGED
package/dist/deno.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../src/deno.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,
|
|
1
|
+
{"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../src/deno.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/deno.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './decorators.js';
|
|
2
2
|
export * from './metadata.js';
|
|
3
3
|
export * from './module.js';
|
|
4
|
-
export * from './service.js';
|
|
5
|
-
export type { WebSocketEventMap, WebSocketGatewayDescriptor, WebSocketGatewayHandlerDescriptor, WebSocketGatewayHandlerMetadata, WebSocketGatewayHandlerType, WebSocketGatewayMetadata, WebSocketGatewayOptions, WebSocketGatewayServerBackedOptions, WebSocketRoomService, WebSocketUpgradeContext, WebSocketUpgradeRejection, } from './types.js';
|
|
6
4
|
export type { TypedOnMessageHandler, WebSocketGatewayContext, WebSocketModuleOptions, WebSocketUpgradeGuard, } from './node/node-types.js';
|
|
5
|
+
export * from './service.js';
|
|
6
|
+
export type { WebSocketEventEnvelope, WebSocketEventMap, WebSocketGatewayDescriptor, WebSocketGatewayHandlerDescriptor, WebSocketGatewayHandlerMetadata, WebSocketGatewayHandlerType, WebSocketGatewayMetadata, WebSocketGatewayOptions, WebSocketGatewayServerBackedOptions, WebSocketReplyMode, WebSocketRoomService, WebSocketUpgradeContext, WebSocketUpgradeRejection, } from './types.js';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,YAAY,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,iCAAiC,EACjC,+BAA+B,EAC/B,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,mCAAmC,EACnC,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,YAAY,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,YAAY,EACV,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,YAAY,EACV,sBAAsB,EACtB,iBAAiB,EACjB,0BAA0B,EAC1B,iCAAiC,EACjC,+BAA+B,EAC/B,2BAA2B,EAC3B,wBAAwB,EACxB,uBAAuB,EACvB,mCAAmC,EACnC,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,YAAY,CAAC"}
|
|
@@ -2,7 +2,7 @@ import type { Token } from '@fluojs/core';
|
|
|
2
2
|
import type { Container } from '@fluojs/di';
|
|
3
3
|
import type { FetchStyleHttpAdapterRealtimeCapability, HttpApplicationAdapter } from '@fluojs/http';
|
|
4
4
|
import type { ApplicationLogger, CompiledModule } from '@fluojs/runtime';
|
|
5
|
-
import type { WebSocketGatewayDescriptor, WebSocketGatewayHandlerDescriptor } from '../types.js';
|
|
5
|
+
import type { WebSocketGatewayDescriptor, WebSocketGatewayHandlerDescriptor, WebSocketReplyMode } from '../types.js';
|
|
6
6
|
/**
|
|
7
7
|
* Describes the discovery candidate contract.
|
|
8
8
|
*/
|
|
@@ -98,11 +98,14 @@ export declare function resolveGatewayInstance(runtimeContainer: Container, desc
|
|
|
98
98
|
* @param socket The socket.
|
|
99
99
|
* @param request The request.
|
|
100
100
|
* @param data The data.
|
|
101
|
+
* @param socketId Stable connection identity passed to message handlers.
|
|
102
|
+
* @param replyMode Optional policy for serializing valid handler return envelopes.
|
|
103
|
+
* @param sendReply Runtime-owned reply sender that reports terminal send failures.
|
|
101
104
|
* @param logger The logger.
|
|
102
105
|
* @param loggerContext The logger context.
|
|
103
|
-
* @returns
|
|
106
|
+
* @returns `terminal` when reply delivery failed; otherwise `completed`.
|
|
104
107
|
*/
|
|
105
|
-
export declare function dispatchGatewayMessage<TSocket, TRequest>(resolved: readonly ResolvedGatewayInstance[], socket: TSocket, request: TRequest, data: SharedWebSocketIncomingMessage, logger: ApplicationLogger, loggerContext: string): Promise<
|
|
108
|
+
export declare function dispatchGatewayMessage<TSocket, TRequest>(resolved: readonly ResolvedGatewayInstance[], socket: TSocket, request: TRequest, data: SharedWebSocketIncomingMessage, socketId: string, replyMode: WebSocketReplyMode | undefined, sendReply: (message: string) => boolean, logger: ApplicationLogger, loggerContext: string): Promise<'completed' | 'terminal'>;
|
|
106
109
|
/**
|
|
107
110
|
* Dispatch gateway disconnect.
|
|
108
111
|
*
|