@nmtjs/gateway 0.15.0-beta.1 → 0.15.0-beta.2
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/dist/api.js +3 -0
- package/dist/connections.js +29 -0
- package/dist/enums.js +16 -0
- package/dist/gateway.js +456 -0
- package/dist/index.js +9 -0
- package/dist/injectables.js +27 -0
- package/dist/rpcs.js +78 -0
- package/dist/streams.js +277 -0
- package/dist/transport.js +3 -0
- package/dist/types.js +1 -0
- package/package.json +10 -10
package/dist/api.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MAX_UINT32, throwError } from '@nmtjs/common';
|
|
2
|
+
export class ConnectionManager {
|
|
3
|
+
connections = new Map();
|
|
4
|
+
streamIds = new Map();
|
|
5
|
+
add(connection) {
|
|
6
|
+
this.connections.set(connection.id, connection);
|
|
7
|
+
this.streamIds.set(connection.id, 0);
|
|
8
|
+
}
|
|
9
|
+
get(id) {
|
|
10
|
+
return this.connections.get(id) ?? throwError('Connection not found');
|
|
11
|
+
}
|
|
12
|
+
has(id) {
|
|
13
|
+
return this.connections.has(id);
|
|
14
|
+
}
|
|
15
|
+
remove(id) {
|
|
16
|
+
this.connections.delete(id);
|
|
17
|
+
this.streamIds.delete(id);
|
|
18
|
+
}
|
|
19
|
+
getAll() {
|
|
20
|
+
return this.connections.values();
|
|
21
|
+
}
|
|
22
|
+
getStreamId(connectionId) {
|
|
23
|
+
let streamId = this.streamIds.get(connectionId);
|
|
24
|
+
if (streamId >= MAX_UINT32)
|
|
25
|
+
streamId = 0;
|
|
26
|
+
this.streamIds.set(connectionId, streamId + 1);
|
|
27
|
+
return streamId;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/enums.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export var GatewayHook;
|
|
2
|
+
(function (GatewayHook) {
|
|
3
|
+
GatewayHook["Connect"] = "Connect";
|
|
4
|
+
GatewayHook["Disconnect"] = "Disconnect";
|
|
5
|
+
})(GatewayHook || (GatewayHook = {}));
|
|
6
|
+
export var ProxyableTransportType;
|
|
7
|
+
(function (ProxyableTransportType) {
|
|
8
|
+
ProxyableTransportType["WebSocket"] = "WebSocket";
|
|
9
|
+
ProxyableTransportType["HTTP"] = "HTTP";
|
|
10
|
+
})(ProxyableTransportType || (ProxyableTransportType = {}));
|
|
11
|
+
export var StreamTimeout;
|
|
12
|
+
(function (StreamTimeout) {
|
|
13
|
+
StreamTimeout["Pull"] = "Pull";
|
|
14
|
+
StreamTimeout["Consume"] = "Consume";
|
|
15
|
+
StreamTimeout["Finish"] = "Finish";
|
|
16
|
+
})(StreamTimeout || (StreamTimeout = {}));
|
package/dist/gateway.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isTypedArray } from 'node:util/types';
|
|
3
|
+
import { anyAbortSignal, isAbortError } from '@nmtjs/common';
|
|
4
|
+
import { createFactoryInjectable, provide, Scope } from '@nmtjs/core';
|
|
5
|
+
import { ClientMessageType, isBlobInterface, kBlobKey, ProtocolBlob, ServerMessageType, } from '@nmtjs/protocol';
|
|
6
|
+
import { getFormat, ProtocolError, versions } from '@nmtjs/protocol/server';
|
|
7
|
+
import { ConnectionManager } from "./connections.js";
|
|
8
|
+
import { StreamTimeout } from "./enums.js";
|
|
9
|
+
import * as injectables from "./injectables.js";
|
|
10
|
+
import { RpcManager } from "./rpcs.js";
|
|
11
|
+
import { BlobStreamsManager } from "./streams.js";
|
|
12
|
+
export class Gateway {
|
|
13
|
+
logger;
|
|
14
|
+
connections;
|
|
15
|
+
rpcs;
|
|
16
|
+
blobStreams;
|
|
17
|
+
options;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = {
|
|
20
|
+
rpcStreamConsumeTimeout: 5000,
|
|
21
|
+
streamTimeouts: {
|
|
22
|
+
//@ts-expect-error
|
|
23
|
+
[StreamTimeout.Pull]: options.streamTimeouts?.[StreamTimeout.Pull] ?? 5000,
|
|
24
|
+
//@ts-expect-error
|
|
25
|
+
[StreamTimeout.Consume]: options.streamTimeouts?.[StreamTimeout.Consume] ?? 5000,
|
|
26
|
+
//@ts-expect-error
|
|
27
|
+
[StreamTimeout.Finish]: options.streamTimeouts?.[StreamTimeout.Finish] ?? 10000,
|
|
28
|
+
},
|
|
29
|
+
...options,
|
|
30
|
+
identity: options.identity ??
|
|
31
|
+
createFactoryInjectable({
|
|
32
|
+
dependencies: { connectionId: injectables.connectionId },
|
|
33
|
+
factory: ({ connectionId }) => connectionId,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
this.logger = options.logger.child({}, gatewayLoggerOptions);
|
|
37
|
+
this.connections = new ConnectionManager();
|
|
38
|
+
this.rpcs = new RpcManager();
|
|
39
|
+
this.blobStreams = new BlobStreamsManager({
|
|
40
|
+
timeouts: this.options.streamTimeouts,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async start() {
|
|
44
|
+
const hosts = [];
|
|
45
|
+
for (const key in this.options.transports) {
|
|
46
|
+
const { transport, proxyable } = this.options.transports[key];
|
|
47
|
+
const url = await transport.start({
|
|
48
|
+
formats: this.options.formats,
|
|
49
|
+
onConnect: this.onConnect(key),
|
|
50
|
+
onDisconnect: this.onDisconnect(key),
|
|
51
|
+
onMessage: this.onMessage(key),
|
|
52
|
+
onRpc: this.onRpc(key),
|
|
53
|
+
});
|
|
54
|
+
this.logger.info(`Transport [${key}] started on [${url}]`);
|
|
55
|
+
if (proxyable)
|
|
56
|
+
hosts.push({ url, type: proxyable });
|
|
57
|
+
}
|
|
58
|
+
return hosts;
|
|
59
|
+
}
|
|
60
|
+
async stop() {
|
|
61
|
+
// Close all connections
|
|
62
|
+
for (const connection of this.connections.getAll()) {
|
|
63
|
+
await this.closeConnection(connection.id);
|
|
64
|
+
}
|
|
65
|
+
for (const key in this.options.transports) {
|
|
66
|
+
const { transport } = this.options.transports[key];
|
|
67
|
+
await transport.stop({ formats: this.options.formats });
|
|
68
|
+
this.logger.debug(`Transport [${key}] stopped`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
send(transport, connectionId, data) {
|
|
72
|
+
if (transport in this.options.transports) {
|
|
73
|
+
const transportInstance = this.options.transports[transport].transport;
|
|
74
|
+
if (transportInstance.send) {
|
|
75
|
+
return transportInstance.send(connectionId, data);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async reload() {
|
|
80
|
+
for (const connections of this.connections.connections.values()) {
|
|
81
|
+
await connections.container.dispose();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
createRpcContext(connection, messageContext, logger, gatewayRpc, signal) {
|
|
85
|
+
const { callId, payload, procedure, metadata } = gatewayRpc;
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
this.rpcs.set(connection.id, callId, controller);
|
|
88
|
+
signal = signal
|
|
89
|
+
? anyAbortSignal(signal, controller.signal)
|
|
90
|
+
: controller.signal;
|
|
91
|
+
const container = connection.container.fork(Scope.Call);
|
|
92
|
+
const dispose = async () => {
|
|
93
|
+
const streamAbortReason = 'Stream is not consumed by a user';
|
|
94
|
+
// Abort streams related to this call
|
|
95
|
+
this.blobStreams.abortClientCallStreams(connection.id, callId, streamAbortReason);
|
|
96
|
+
this.rpcs.delete(connection.id, callId);
|
|
97
|
+
this.rpcs.releasePull(connection.id, callId);
|
|
98
|
+
await container.dispose();
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
...messageContext,
|
|
102
|
+
callId,
|
|
103
|
+
payload,
|
|
104
|
+
procedure,
|
|
105
|
+
metadata,
|
|
106
|
+
container,
|
|
107
|
+
signal,
|
|
108
|
+
logger: logger.child({ callId, procedure }),
|
|
109
|
+
[Symbol.asyncDispose]: dispose,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
createMessageContext(connection, transportKey) {
|
|
113
|
+
const transport = this.options.transports[transportKey].transport;
|
|
114
|
+
const { id: connectionId, protocol, decoder, encoder } = connection;
|
|
115
|
+
const streamId = this.connections.getStreamId.bind(this.connections, connectionId);
|
|
116
|
+
return {
|
|
117
|
+
connectionId,
|
|
118
|
+
protocol,
|
|
119
|
+
encoder,
|
|
120
|
+
decoder,
|
|
121
|
+
transport,
|
|
122
|
+
streamId,
|
|
123
|
+
addClientStream: ({ streamId, callId, metadata }) => {
|
|
124
|
+
const stream = this.blobStreams.createClientStream(connectionId, callId, streamId, metadata, {
|
|
125
|
+
read: (size) => {
|
|
126
|
+
transport.send(connectionId, protocol.encodeMessage(this.createMessageContext(connection, transportKey), ServerMessageType.ClientStreamPull, { streamId, size: size || 65535 }));
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
stream.once('error', () => {
|
|
130
|
+
this.send(transportKey, connectionId, protocol.encodeMessage(this.createMessageContext(connection, transportKey), ServerMessageType.ClientStreamAbort, { streamId }));
|
|
131
|
+
});
|
|
132
|
+
const consume = () => {
|
|
133
|
+
this.blobStreams.consumeClientStream(connectionId, callId, streamId);
|
|
134
|
+
return stream;
|
|
135
|
+
};
|
|
136
|
+
const consumer = Object.defineProperties(consume, {
|
|
137
|
+
[kBlobKey]: {
|
|
138
|
+
enumerable: false,
|
|
139
|
+
configurable: false,
|
|
140
|
+
writable: false,
|
|
141
|
+
value: true,
|
|
142
|
+
},
|
|
143
|
+
metadata: {
|
|
144
|
+
value: metadata,
|
|
145
|
+
enumerable: true,
|
|
146
|
+
configurable: false,
|
|
147
|
+
writable: false,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
return consumer;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
onConnect(transport) {
|
|
155
|
+
const logger = this.logger.child({ transport });
|
|
156
|
+
return async (options, ...injections) => {
|
|
157
|
+
logger.debug('Initiating new connection');
|
|
158
|
+
const protocol = versions[options.protocolVersion];
|
|
159
|
+
if (!protocol)
|
|
160
|
+
throw new Error('Unsupported protocol version');
|
|
161
|
+
const id = randomUUID();
|
|
162
|
+
const container = this.options.container.fork(Scope.Connection);
|
|
163
|
+
try {
|
|
164
|
+
await container.provide([
|
|
165
|
+
provide(injectables.connectionData, options.data),
|
|
166
|
+
provide(injectables.connectionId, id),
|
|
167
|
+
]);
|
|
168
|
+
await container.provide(injections);
|
|
169
|
+
const identity = await container.resolve(this.options.identity);
|
|
170
|
+
const { accept, contentType, type } = options;
|
|
171
|
+
const { decoder, encoder } = getFormat(this.options.formats, {
|
|
172
|
+
accept,
|
|
173
|
+
contentType,
|
|
174
|
+
});
|
|
175
|
+
const connection = {
|
|
176
|
+
id,
|
|
177
|
+
type,
|
|
178
|
+
identity,
|
|
179
|
+
transport,
|
|
180
|
+
protocol,
|
|
181
|
+
container,
|
|
182
|
+
encoder,
|
|
183
|
+
decoder,
|
|
184
|
+
abortController: new AbortController(),
|
|
185
|
+
};
|
|
186
|
+
this.connections.add(connection);
|
|
187
|
+
await container.provide(injectables.connectionAbortSignal, connection.abortController.signal);
|
|
188
|
+
logger.debug({
|
|
189
|
+
id,
|
|
190
|
+
protocol: options.protocolVersion,
|
|
191
|
+
type,
|
|
192
|
+
accept,
|
|
193
|
+
contentType,
|
|
194
|
+
identity,
|
|
195
|
+
transportData: options.data,
|
|
196
|
+
}, 'Connection established');
|
|
197
|
+
return Object.assign(connection, {
|
|
198
|
+
[Symbol.asyncDispose]: async () => {
|
|
199
|
+
await this.onDisconnect(transport)(connection.id);
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
logger.debug({ error }, 'Error establishing connection');
|
|
205
|
+
container.dispose();
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
onDisconnect(transport) {
|
|
211
|
+
const logger = this.logger.child({ transport });
|
|
212
|
+
return async (connectionId) => {
|
|
213
|
+
logger.debug({ connectionId }, 'Disconnecting connection');
|
|
214
|
+
await this.closeConnection(connectionId);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
onMessage(transport) {
|
|
218
|
+
const _logger = this.logger.child({ transport });
|
|
219
|
+
return async ({ connectionId, data }, ...injections) => {
|
|
220
|
+
const logger = _logger.child({ connectionId });
|
|
221
|
+
try {
|
|
222
|
+
const connection = this.connections.get(connectionId);
|
|
223
|
+
const messageContext = this.createMessageContext(connection, transport);
|
|
224
|
+
const message = connection.protocol.decodeMessage(messageContext, Buffer.from(data));
|
|
225
|
+
logger.trace(message, 'Received message');
|
|
226
|
+
switch (message.type) {
|
|
227
|
+
case ClientMessageType.Rpc: {
|
|
228
|
+
const rpcContext = this.createRpcContext(connection, messageContext, logger, message.rpc);
|
|
229
|
+
try {
|
|
230
|
+
await rpcContext.container.provide([
|
|
231
|
+
...injections,
|
|
232
|
+
provide(injectables.createBlob, this.createBlobFunction(rpcContext)),
|
|
233
|
+
]);
|
|
234
|
+
await this.handleRpcMessage(connection, rpcContext);
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
await rpcContext[Symbol.asyncDispose]();
|
|
238
|
+
}
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
case ClientMessageType.RpcPull: {
|
|
242
|
+
this.rpcs.releasePull(connectionId, message.callId);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case ClientMessageType.RpcAbort: {
|
|
246
|
+
this.rpcs.abort(connectionId, message.callId);
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
case ClientMessageType.ClientStreamAbort: {
|
|
250
|
+
this.blobStreams.abortClientStream(connectionId, message.streamId, message.reason);
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
case ClientMessageType.ClientStreamPush: {
|
|
254
|
+
this.blobStreams.pushToClientStream(connectionId, message.streamId, message.chunk);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case ClientMessageType.ClientStreamEnd: {
|
|
258
|
+
this.blobStreams.endClientStream(connectionId, message.streamId);
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
case ClientMessageType.ServerStreamAbort: {
|
|
262
|
+
this.blobStreams.abortServerStream(connectionId, message.streamId, message.reason);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case ClientMessageType.ServerStreamPull: {
|
|
266
|
+
this.blobStreams.pullServerStream(connectionId, message.streamId);
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
default:
|
|
270
|
+
throw new Error('Unknown message type');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
logger.trace({ error }, 'Error handling message');
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
onRpc(transport) {
|
|
280
|
+
const _logger = this.logger.child({ transport });
|
|
281
|
+
return async (connection, rpc, signal, ...injections) => {
|
|
282
|
+
const logger = _logger.child({ connectionId: connection.id });
|
|
283
|
+
const messageContext = this.createMessageContext(connection, connection.transport);
|
|
284
|
+
const rpcContext = this.createRpcContext(connection, messageContext, logger, rpc, signal);
|
|
285
|
+
try {
|
|
286
|
+
await rpcContext.container.provide([
|
|
287
|
+
...injections,
|
|
288
|
+
provide(injectables.rpcAbortSignal, signal),
|
|
289
|
+
provide(injectables.createBlob, this.createBlobFunction(rpcContext)),
|
|
290
|
+
]);
|
|
291
|
+
const result = await this.options.api.call({
|
|
292
|
+
connection,
|
|
293
|
+
payload: rpc.payload,
|
|
294
|
+
procedure: rpc.procedure,
|
|
295
|
+
metadata: rpc.metadata,
|
|
296
|
+
container: rpcContext.container,
|
|
297
|
+
signal: rpcContext.signal,
|
|
298
|
+
});
|
|
299
|
+
if (typeof result === 'function') {
|
|
300
|
+
return result(async () => {
|
|
301
|
+
await rpcContext[Symbol.asyncDispose]();
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
await rpcContext[Symbol.asyncDispose]();
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
await rpcContext[Symbol.asyncDispose]();
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
async handleRpcMessage(connection, context) {
|
|
316
|
+
const { container, connectionId, transport, protocol, signal, callId, procedure, payload, encoder, } = context;
|
|
317
|
+
try {
|
|
318
|
+
await container.provide(injectables.rpcAbortSignal, signal);
|
|
319
|
+
const response = await this.options.api.call({
|
|
320
|
+
connection: connection,
|
|
321
|
+
container,
|
|
322
|
+
payload,
|
|
323
|
+
procedure,
|
|
324
|
+
signal,
|
|
325
|
+
});
|
|
326
|
+
if (typeof response === 'function') {
|
|
327
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.RpcStreamResponse, {
|
|
328
|
+
callId,
|
|
329
|
+
}));
|
|
330
|
+
try {
|
|
331
|
+
const consumeTimeoutSignal = this.options.rpcStreamConsumeTimeout
|
|
332
|
+
? AbortSignal.timeout(this.options.rpcStreamConsumeTimeout)
|
|
333
|
+
: undefined;
|
|
334
|
+
const streamSignal = consumeTimeoutSignal
|
|
335
|
+
? anyAbortSignal(signal, consumeTimeoutSignal)
|
|
336
|
+
: signal;
|
|
337
|
+
await this.rpcs.awaitPull(connectionId, callId, streamSignal);
|
|
338
|
+
for await (const chunk of response()) {
|
|
339
|
+
signal.throwIfAborted();
|
|
340
|
+
const chunkEncoded = encoder.encode(chunk);
|
|
341
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.RpcStreamChunk, { callId, chunk: chunkEncoded }));
|
|
342
|
+
await this.rpcs.awaitPull(connectionId, callId);
|
|
343
|
+
}
|
|
344
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.RpcStreamEnd, {
|
|
345
|
+
callId,
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
if (!isAbortError(error)) {
|
|
350
|
+
this.logger.error(error);
|
|
351
|
+
}
|
|
352
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.RpcStreamAbort, {
|
|
353
|
+
callId,
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
const streams = this.blobStreams.getServerStreamsMetadata(connectionId, callId);
|
|
359
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.RpcResponse, {
|
|
360
|
+
callId,
|
|
361
|
+
result: response,
|
|
362
|
+
streams,
|
|
363
|
+
error: null,
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.RpcResponse, {
|
|
369
|
+
callId,
|
|
370
|
+
result: null,
|
|
371
|
+
streams: {},
|
|
372
|
+
error,
|
|
373
|
+
}));
|
|
374
|
+
const level = error instanceof ProtocolError ? 'trace' : 'error';
|
|
375
|
+
this.logger[level](error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async closeConnection(connectionId) {
|
|
379
|
+
if (this.connections.has(connectionId)) {
|
|
380
|
+
const connection = this.connections.get(connectionId);
|
|
381
|
+
connection.abortController.abort();
|
|
382
|
+
connection.container.dispose();
|
|
383
|
+
}
|
|
384
|
+
this.rpcs.close(connectionId);
|
|
385
|
+
this.blobStreams.cleanupConnection(connectionId);
|
|
386
|
+
this.connections.remove(connectionId);
|
|
387
|
+
}
|
|
388
|
+
createBlobFunction(context) {
|
|
389
|
+
const { streamId: getStreamId, transport, protocol, connectionId, callId, encoder, } = context;
|
|
390
|
+
return (source, metadata) => {
|
|
391
|
+
const streamId = getStreamId();
|
|
392
|
+
const blob = ProtocolBlob.from(source, metadata, () => {
|
|
393
|
+
return encoder.encodeBlob(streamId);
|
|
394
|
+
});
|
|
395
|
+
const stream = this.blobStreams.createServerStream(connectionId, callId, streamId, blob);
|
|
396
|
+
stream.on('data', (chunk) => {
|
|
397
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.ServerStreamPush, {
|
|
398
|
+
streamId: streamId,
|
|
399
|
+
chunk: Buffer.from(chunk),
|
|
400
|
+
}));
|
|
401
|
+
});
|
|
402
|
+
stream.on('error', (error) => {
|
|
403
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.ServerStreamAbort, {
|
|
404
|
+
streamId: streamId,
|
|
405
|
+
reason: error.message,
|
|
406
|
+
}));
|
|
407
|
+
});
|
|
408
|
+
stream.once('finish', () => {
|
|
409
|
+
transport.send(connectionId, protocol.encodeMessage(context, ServerMessageType.ServerStreamEnd, {
|
|
410
|
+
streamId: streamId,
|
|
411
|
+
}));
|
|
412
|
+
});
|
|
413
|
+
stream.once('close', () => {
|
|
414
|
+
this.blobStreams.removeServerStream(connectionId, streamId);
|
|
415
|
+
});
|
|
416
|
+
return blob;
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const gatewayLoggerOptions = {
|
|
421
|
+
serializers: {
|
|
422
|
+
chunk: (chunk) => isTypedArray(chunk) ? `<Buffer length=${chunk.byteLength}>` : chunk,
|
|
423
|
+
payload: (payload) => {
|
|
424
|
+
function traverseObject(obj) {
|
|
425
|
+
if (Array.isArray(obj)) {
|
|
426
|
+
return obj.map(traverseObject);
|
|
427
|
+
}
|
|
428
|
+
else if (isTypedArray(obj)) {
|
|
429
|
+
return `<${obj.constructor.name} length=${obj.byteLength}>`;
|
|
430
|
+
}
|
|
431
|
+
else if (typeof obj === 'object' && obj !== null) {
|
|
432
|
+
const result = {};
|
|
433
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
434
|
+
result[key] = traverseObject(value);
|
|
435
|
+
}
|
|
436
|
+
return result;
|
|
437
|
+
}
|
|
438
|
+
else if (isBlobInterface(obj)) {
|
|
439
|
+
return `<ClientBlobStream metadata=${JSON.stringify(obj.metadata)}>`;
|
|
440
|
+
}
|
|
441
|
+
return obj;
|
|
442
|
+
}
|
|
443
|
+
return traverseObject(payload);
|
|
444
|
+
},
|
|
445
|
+
headers: (value) => {
|
|
446
|
+
if (value instanceof Headers) {
|
|
447
|
+
const obj = {};
|
|
448
|
+
value.forEach((v, k) => {
|
|
449
|
+
obj[k] = v;
|
|
450
|
+
});
|
|
451
|
+
return obj;
|
|
452
|
+
}
|
|
453
|
+
return value;
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./api.js";
|
|
2
|
+
export * from "./connections.js";
|
|
3
|
+
export * from "./enums.js";
|
|
4
|
+
export * from "./gateway.js";
|
|
5
|
+
export * from "./injectables.js";
|
|
6
|
+
export * from "./rpcs.js";
|
|
7
|
+
export * from "./streams.js";
|
|
8
|
+
export * from "./transport.js";
|
|
9
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { anyAbortSignal } from '@nmtjs/common';
|
|
2
|
+
import { createFactoryInjectable, createLazyInjectable, createOptionalInjectable, Scope, } from '@nmtjs/core';
|
|
3
|
+
export const connection = createLazyInjectable(Scope.Connection, 'Gateway connection');
|
|
4
|
+
export const connectionId = createLazyInjectable(Scope.Connection, 'Gateway connection id');
|
|
5
|
+
export const connectionData = createLazyInjectable(Scope.Connection, "Gateway connection's data");
|
|
6
|
+
export const connectionAbortSignal = createLazyInjectable(Scope.Connection, 'Connection abort signal');
|
|
7
|
+
export const rpcClientAbortSignal = createLazyInjectable(Scope.Call, 'RPC client abort signal');
|
|
8
|
+
export const rpcStreamAbortSignal = createLazyInjectable(Scope.Call, 'RPC stream abort signal');
|
|
9
|
+
export const rpcAbortSignal = createFactoryInjectable({
|
|
10
|
+
dependencies: {
|
|
11
|
+
rpcClientAbortSignal,
|
|
12
|
+
connectionAbortSignal,
|
|
13
|
+
rpcStreamAbortSignal: createOptionalInjectable(rpcStreamAbortSignal),
|
|
14
|
+
},
|
|
15
|
+
factory: (ctx) => anyAbortSignal(ctx.rpcClientAbortSignal, ctx.connectionAbortSignal, ctx.rpcStreamAbortSignal),
|
|
16
|
+
}, 'Any RPC abort signal');
|
|
17
|
+
export const createBlob = createLazyInjectable(Scope.Call, 'Create RPC blob');
|
|
18
|
+
export const GatewayInjectables = {
|
|
19
|
+
connection,
|
|
20
|
+
connectionId,
|
|
21
|
+
connectionData,
|
|
22
|
+
connectionAbortSignal,
|
|
23
|
+
rpcClientAbortSignal,
|
|
24
|
+
rpcStreamAbortSignal,
|
|
25
|
+
rpcAbortSignal,
|
|
26
|
+
createBlob,
|
|
27
|
+
};
|
package/dist/rpcs.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { createFuture } from '@nmtjs/common';
|
|
2
|
+
export class RpcManager {
|
|
3
|
+
// connectionId:callId -> AbortController
|
|
4
|
+
rpcs = new Map();
|
|
5
|
+
// connectionId:callId -> Future<void>
|
|
6
|
+
streams = new Map();
|
|
7
|
+
set(connectionId, callId, controller) {
|
|
8
|
+
const key = this.getKey(connectionId, callId);
|
|
9
|
+
this.rpcs.set(key, controller);
|
|
10
|
+
}
|
|
11
|
+
get(connectionId, callId) {
|
|
12
|
+
const key = this.getKey(connectionId, callId);
|
|
13
|
+
return this.rpcs.get(key);
|
|
14
|
+
}
|
|
15
|
+
delete(connectionId, callId) {
|
|
16
|
+
const key = this.getKey(connectionId, callId);
|
|
17
|
+
this.rpcs.delete(key);
|
|
18
|
+
}
|
|
19
|
+
abort(connectionId, callId) {
|
|
20
|
+
const key = this.getKey(connectionId, callId);
|
|
21
|
+
const controller = this.rpcs.get(key);
|
|
22
|
+
if (controller) {
|
|
23
|
+
controller.abort();
|
|
24
|
+
this.rpcs.delete(key);
|
|
25
|
+
this.releasePull(connectionId, callId);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
awaitPull(connectionId, callId, signal) {
|
|
29
|
+
const key = this.getKey(connectionId, callId);
|
|
30
|
+
const rpc = this.rpcs.get(key);
|
|
31
|
+
if (!rpc)
|
|
32
|
+
throw new Error(`RPC not found`);
|
|
33
|
+
const future = this.streams.get(key);
|
|
34
|
+
if (future) {
|
|
35
|
+
return future.promise;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const newFuture = createFuture();
|
|
39
|
+
if (signal)
|
|
40
|
+
signal.addEventListener('abort', () => newFuture.resolve(), {
|
|
41
|
+
once: true,
|
|
42
|
+
});
|
|
43
|
+
this.streams.set(key, newFuture);
|
|
44
|
+
return newFuture.promise;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
releasePull(connectionId, callId) {
|
|
48
|
+
const key = this.getKey(connectionId, callId);
|
|
49
|
+
const future = this.streams.get(key);
|
|
50
|
+
if (future) {
|
|
51
|
+
future.resolve();
|
|
52
|
+
this.streams.delete(key);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
close(connectionId) {
|
|
56
|
+
// Iterate all RPCs and abort those belonging to this connection
|
|
57
|
+
// Optimization: Maintain a Set<callId> per connectionId
|
|
58
|
+
for (const [key, controller] of this.rpcs) {
|
|
59
|
+
if (key.startsWith(`${connectionId}:`)) {
|
|
60
|
+
controller.abort();
|
|
61
|
+
this.rpcs.delete(key);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Also release any pending pulls for this connection
|
|
65
|
+
for (const key of this.streams.keys()) {
|
|
66
|
+
if (key.startsWith(`${connectionId}:`)) {
|
|
67
|
+
const future = this.streams.get(key);
|
|
68
|
+
if (future) {
|
|
69
|
+
future.resolve();
|
|
70
|
+
this.streams.delete(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
getKey(connectionId, callId) {
|
|
76
|
+
return `${connectionId}:${callId}`;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/streams.js
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { noopFn } from '@nmtjs/common';
|
|
2
|
+
import { ProtocolClientStream, ProtocolServerStream, } from '@nmtjs/protocol/server';
|
|
3
|
+
import { StreamTimeout } from "./enums.js";
|
|
4
|
+
/**
|
|
5
|
+
* @todo Clarify Pull/Consume timeout semantics - currently ambiguous whether
|
|
6
|
+
* Pull timeout means "client not pulling" or "server not producing" for server streams
|
|
7
|
+
*/
|
|
8
|
+
export class BlobStreamsManager {
|
|
9
|
+
clientStreams = new Map();
|
|
10
|
+
serverStreams = new Map();
|
|
11
|
+
// Index for quick lookup by callId (connectionId:callId -> Set<streamId>)
|
|
12
|
+
connectionClientStreams = new Map();
|
|
13
|
+
connectionServerStreams = new Map();
|
|
14
|
+
clientCallStreams = new Map();
|
|
15
|
+
serverCallStreams = new Map();
|
|
16
|
+
timeoutDurations;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.timeoutDurations = {
|
|
19
|
+
[StreamTimeout.Pull]: config.timeouts[StreamTimeout.Pull],
|
|
20
|
+
[StreamTimeout.Consume]: config.timeouts[StreamTimeout.Consume],
|
|
21
|
+
[StreamTimeout.Finish]: config.timeouts[StreamTimeout.Finish],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// --- Client Streams (Upload) ---
|
|
25
|
+
createClientStream(connectionId, callId, streamId, metadata, options) {
|
|
26
|
+
const stream = new ProtocolClientStream(streamId, metadata, options);
|
|
27
|
+
stream.on('error', noopFn);
|
|
28
|
+
const key = this.getKey(connectionId, streamId);
|
|
29
|
+
const state = {
|
|
30
|
+
connectionId,
|
|
31
|
+
callId,
|
|
32
|
+
stream,
|
|
33
|
+
timeouts: {
|
|
34
|
+
[StreamTimeout.Pull]: undefined,
|
|
35
|
+
[StreamTimeout.Consume]: undefined,
|
|
36
|
+
[StreamTimeout.Finish]: undefined,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
this.clientStreams.set(key, state);
|
|
40
|
+
this.trackClientCall(connectionId, callId, streamId);
|
|
41
|
+
this.trackConnectionClientStream(connectionId, streamId);
|
|
42
|
+
this.startTimeout(state, StreamTimeout.Consume);
|
|
43
|
+
return stream;
|
|
44
|
+
}
|
|
45
|
+
pushToClientStream(connectionId, streamId, chunk) {
|
|
46
|
+
const key = this.getKey(connectionId, streamId);
|
|
47
|
+
const state = this.clientStreams.get(key);
|
|
48
|
+
if (state) {
|
|
49
|
+
state.stream.write(chunk);
|
|
50
|
+
this.clearTimeout(state, StreamTimeout.Consume);
|
|
51
|
+
this.startTimeout(state, StreamTimeout.Pull);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
endClientStream(connectionId, streamId) {
|
|
55
|
+
const key = this.getKey(connectionId, streamId);
|
|
56
|
+
const state = this.clientStreams.get(key);
|
|
57
|
+
if (state) {
|
|
58
|
+
state.stream.end(null);
|
|
59
|
+
this.removeClientStream(connectionId, streamId);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
abortClientStream(connectionId, streamId, error = 'Aborted') {
|
|
63
|
+
const key = this.getKey(connectionId, streamId);
|
|
64
|
+
const state = this.clientStreams.get(key);
|
|
65
|
+
if (state) {
|
|
66
|
+
state.stream.destroy(new Error(error));
|
|
67
|
+
this.removeClientStream(connectionId, streamId);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
consumeClientStream(connectionId, callId, streamId) {
|
|
71
|
+
this.untrackClientCall(connectionId, callId, streamId);
|
|
72
|
+
}
|
|
73
|
+
removeClientStream(connectionId, streamId) {
|
|
74
|
+
const key = this.getKey(connectionId, streamId);
|
|
75
|
+
const state = this.clientStreams.get(key);
|
|
76
|
+
if (state) {
|
|
77
|
+
this.clientStreams.delete(key);
|
|
78
|
+
this.clearTimeout(state, StreamTimeout.Finish);
|
|
79
|
+
this.clearTimeout(state, StreamTimeout.Pull);
|
|
80
|
+
this.clearTimeout(state, StreamTimeout.Consume);
|
|
81
|
+
this.untrackClientCall(connectionId, state.callId, streamId);
|
|
82
|
+
this.untrackConnectionClientStream(connectionId, streamId);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// --- Server Streams (Download) ---
|
|
86
|
+
getServerStreamsMetadata(connectionId, callId) {
|
|
87
|
+
const key = this.getCallKey(connectionId, callId);
|
|
88
|
+
const streamIds = this.serverCallStreams.get(key);
|
|
89
|
+
const streams = {};
|
|
90
|
+
if (streamIds) {
|
|
91
|
+
for (const streamId of streamIds) {
|
|
92
|
+
const streamKey = this.getKey(connectionId, streamId);
|
|
93
|
+
const state = this.serverStreams.get(streamKey);
|
|
94
|
+
if (state) {
|
|
95
|
+
streams[streamId] = state.stream.metadata;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return streams;
|
|
100
|
+
}
|
|
101
|
+
createServerStream(connectionId, callId, streamId, blob) {
|
|
102
|
+
const stream = new ProtocolServerStream(streamId, blob);
|
|
103
|
+
const key = this.getKey(connectionId, streamId);
|
|
104
|
+
const state = {
|
|
105
|
+
connectionId,
|
|
106
|
+
callId,
|
|
107
|
+
stream,
|
|
108
|
+
timeouts: {
|
|
109
|
+
[StreamTimeout.Pull]: undefined,
|
|
110
|
+
[StreamTimeout.Consume]: undefined,
|
|
111
|
+
[StreamTimeout.Finish]: undefined,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
// Prevent unhandled 'error' events, in case the user did not subscribe to them
|
|
115
|
+
stream.on('error', noopFn);
|
|
116
|
+
this.serverStreams.set(key, state);
|
|
117
|
+
this.trackServerCall(connectionId, callId, streamId);
|
|
118
|
+
this.trackConnectionServerStream(connectionId, streamId);
|
|
119
|
+
this.startTimeout(state, StreamTimeout.Finish);
|
|
120
|
+
this.startTimeout(state, StreamTimeout.Consume);
|
|
121
|
+
return stream;
|
|
122
|
+
}
|
|
123
|
+
pullServerStream(connectionId, streamId) {
|
|
124
|
+
const key = this.getKey(connectionId, streamId);
|
|
125
|
+
const state = this.serverStreams.get(key);
|
|
126
|
+
if (state) {
|
|
127
|
+
state.stream.resume();
|
|
128
|
+
this.clearTimeout(state, StreamTimeout.Consume);
|
|
129
|
+
this.startTimeout(state, StreamTimeout.Pull);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
abortServerStream(connectionId, streamId, error = 'Aborted') {
|
|
133
|
+
const key = this.getKey(connectionId, streamId);
|
|
134
|
+
const state = this.serverStreams.get(key);
|
|
135
|
+
if (state) {
|
|
136
|
+
state.stream.destroy(new Error(error));
|
|
137
|
+
this.removeServerStream(connectionId, streamId);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
removeServerStream(connectionId, streamId) {
|
|
141
|
+
const key = this.getKey(connectionId, streamId);
|
|
142
|
+
const state = this.serverStreams.get(key);
|
|
143
|
+
if (state) {
|
|
144
|
+
this.serverStreams.delete(key);
|
|
145
|
+
this.clearTimeout(state, StreamTimeout.Pull);
|
|
146
|
+
this.clearTimeout(state, StreamTimeout.Consume);
|
|
147
|
+
this.clearTimeout(state, StreamTimeout.Finish);
|
|
148
|
+
this.untrackServerCall(connectionId, state.callId, streamId);
|
|
149
|
+
this.untrackConnectionServerStream(connectionId, streamId);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// --- Timeouts ---
|
|
153
|
+
startTimeout(state, type) {
|
|
154
|
+
this.clearTimeout(state, type);
|
|
155
|
+
const duration = this.timeoutDurations[type];
|
|
156
|
+
const timeout = setTimeout(() => {
|
|
157
|
+
if (state.stream instanceof ProtocolClientStream) {
|
|
158
|
+
this.abortClientStream(state.connectionId, state.stream.id, `${type} timeout`);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
this.abortServerStream(state.connectionId, state.stream.id, `${type} timeout`);
|
|
162
|
+
}
|
|
163
|
+
state.timeouts[type] = undefined;
|
|
164
|
+
}, duration);
|
|
165
|
+
state.timeouts[type] = timeout;
|
|
166
|
+
}
|
|
167
|
+
clearTimeout(state, type) {
|
|
168
|
+
const timeout = state.timeouts[type];
|
|
169
|
+
if (timeout) {
|
|
170
|
+
clearTimeout(timeout);
|
|
171
|
+
state.timeouts[type] = undefined;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// --- Helpers ---
|
|
175
|
+
getKey(connectionId, streamId) {
|
|
176
|
+
return `${connectionId}:${streamId}`;
|
|
177
|
+
}
|
|
178
|
+
getCallKey(connectionId, callId) {
|
|
179
|
+
return `${connectionId}:${callId}`;
|
|
180
|
+
}
|
|
181
|
+
trackClientCall(connectionId, callId, streamId) {
|
|
182
|
+
const key = this.getCallKey(connectionId, callId);
|
|
183
|
+
let set = this.clientCallStreams.get(key);
|
|
184
|
+
if (!set) {
|
|
185
|
+
set = new Set();
|
|
186
|
+
this.clientCallStreams.set(key, set);
|
|
187
|
+
}
|
|
188
|
+
set.add(streamId);
|
|
189
|
+
}
|
|
190
|
+
untrackClientCall(connectionId, callId, streamId) {
|
|
191
|
+
const key = this.getCallKey(connectionId, callId);
|
|
192
|
+
const set = this.clientCallStreams.get(key);
|
|
193
|
+
if (set) {
|
|
194
|
+
set.delete(streamId);
|
|
195
|
+
if (set.size === 0) {
|
|
196
|
+
this.clientCallStreams.delete(key);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
trackServerCall(connectionId, callId, streamId) {
|
|
201
|
+
const key = this.getCallKey(connectionId, callId);
|
|
202
|
+
let set = this.serverCallStreams.get(key);
|
|
203
|
+
if (!set) {
|
|
204
|
+
set = new Set();
|
|
205
|
+
this.serverCallStreams.set(key, set);
|
|
206
|
+
}
|
|
207
|
+
set.add(streamId);
|
|
208
|
+
}
|
|
209
|
+
untrackServerCall(connectionId, callId, streamId) {
|
|
210
|
+
const key = this.getCallKey(connectionId, callId);
|
|
211
|
+
const set = this.serverCallStreams.get(key);
|
|
212
|
+
if (set) {
|
|
213
|
+
set.delete(streamId);
|
|
214
|
+
if (set.size === 0) {
|
|
215
|
+
this.serverCallStreams.delete(key);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
trackConnectionClientStream(connectionId, streamId) {
|
|
220
|
+
let set = this.connectionClientStreams.get(connectionId);
|
|
221
|
+
if (!set) {
|
|
222
|
+
set = new Set();
|
|
223
|
+
this.connectionClientStreams.set(connectionId, set);
|
|
224
|
+
}
|
|
225
|
+
set.add(streamId);
|
|
226
|
+
}
|
|
227
|
+
untrackConnectionClientStream(connectionId, streamId) {
|
|
228
|
+
const set = this.connectionClientStreams.get(connectionId);
|
|
229
|
+
if (set) {
|
|
230
|
+
set.delete(streamId);
|
|
231
|
+
if (set.size === 0) {
|
|
232
|
+
this.connectionClientStreams.delete(connectionId);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
trackConnectionServerStream(connectionId, streamId) {
|
|
237
|
+
let set = this.connectionServerStreams.get(connectionId);
|
|
238
|
+
if (!set) {
|
|
239
|
+
set = new Set();
|
|
240
|
+
this.connectionServerStreams.set(connectionId, set);
|
|
241
|
+
}
|
|
242
|
+
set.add(streamId);
|
|
243
|
+
}
|
|
244
|
+
untrackConnectionServerStream(connectionId, streamId) {
|
|
245
|
+
const set = this.connectionServerStreams.get(connectionId);
|
|
246
|
+
if (set) {
|
|
247
|
+
set.delete(streamId);
|
|
248
|
+
if (set.size === 0) {
|
|
249
|
+
this.connectionServerStreams.delete(connectionId);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// --- Cleanup ---
|
|
254
|
+
abortClientCallStreams(connectionId, callId, reason = 'Call aborted') {
|
|
255
|
+
const key = this.getCallKey(connectionId, callId);
|
|
256
|
+
const clientStreamIds = this.clientCallStreams.get(key);
|
|
257
|
+
if (clientStreamIds) {
|
|
258
|
+
for (const streamId of [...clientStreamIds]) {
|
|
259
|
+
this.abortClientStream(connectionId, streamId, reason);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
cleanupConnection(connectionId) {
|
|
264
|
+
const clientStreamIds = this.connectionClientStreams.get(connectionId);
|
|
265
|
+
if (clientStreamIds) {
|
|
266
|
+
for (const streamId of [...clientStreamIds]) {
|
|
267
|
+
this.abortClientStream(connectionId, streamId, 'Connection closed');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const serverStreamIds = this.connectionServerStreams.get(connectionId);
|
|
271
|
+
if (serverStreamIds) {
|
|
272
|
+
for (const streamId of [...serverStreamIds]) {
|
|
273
|
+
this.abortServerStream(connectionId, streamId, 'Connection closed');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -6,26 +6,26 @@
|
|
|
6
6
|
},
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"hookable": "6.0.0-rc.1",
|
|
9
|
-
"@nmtjs/
|
|
10
|
-
"@nmtjs/
|
|
11
|
-
"@nmtjs/
|
|
12
|
-
"@nmtjs/protocol": "0.15.0-beta.
|
|
9
|
+
"@nmtjs/core": "0.15.0-beta.2",
|
|
10
|
+
"@nmtjs/type": "0.15.0-beta.2",
|
|
11
|
+
"@nmtjs/common": "0.15.0-beta.2",
|
|
12
|
+
"@nmtjs/protocol": "0.15.0-beta.2"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"@nmtjs/common": "0.15.0-beta.
|
|
16
|
-
"@nmtjs/
|
|
17
|
-
"@nmtjs/
|
|
18
|
-
"@nmtjs/
|
|
15
|
+
"@nmtjs/common": "0.15.0-beta.2",
|
|
16
|
+
"@nmtjs/type": "0.15.0-beta.2",
|
|
17
|
+
"@nmtjs/protocol": "0.15.0-beta.2",
|
|
18
|
+
"@nmtjs/core": "0.15.0-beta.2"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@nmtjs/_tests": "0.15.0-beta.
|
|
21
|
+
"@nmtjs/_tests": "0.15.0-beta.2"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
25
|
"LICENSE.md",
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
|
-
"version": "0.15.0-beta.
|
|
28
|
+
"version": "0.15.0-beta.2",
|
|
29
29
|
"scripts": {
|
|
30
30
|
"clean-build": "rm -rf ./dist",
|
|
31
31
|
"build": "tsc",
|