@replit/river 0.8.0 → 0.9.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.md +2 -0
- package/dist/__tests__/bandwidth.bench.js +1 -1
- package/dist/__tests__/e2e.test.js +119 -5
- package/dist/__tests__/fixtures/cleanup.d.ts +12 -0
- package/dist/__tests__/fixtures/cleanup.d.ts.map +1 -0
- package/dist/__tests__/fixtures/cleanup.js +39 -0
- package/dist/__tests__/fixtures/services.d.ts +72 -0
- package/dist/__tests__/fixtures/services.d.ts.map +1 -1
- package/dist/__tests__/fixtures/services.js +55 -0
- package/dist/__tests__/handler.test.js +35 -2
- package/dist/__tests__/invariants.test.d.ts +2 -0
- package/dist/__tests__/invariants.test.d.ts.map +1 -0
- package/dist/__tests__/invariants.test.js +136 -0
- package/dist/__tests__/serialize.test.js +40 -0
- package/dist/__tests__/typescript-stress.test.d.ts +392 -196
- package/dist/__tests__/typescript-stress.test.d.ts.map +1 -1
- package/dist/__tests__/typescript-stress.test.js +13 -3
- package/dist/router/builder.d.ts +49 -11
- package/dist/router/builder.d.ts.map +1 -1
- package/dist/router/builder.js +8 -2
- package/dist/router/client.d.ts +18 -2
- package/dist/router/client.d.ts.map +1 -1
- package/dist/router/client.js +52 -11
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/server.d.ts +15 -1
- package/dist/router/server.d.ts.map +1 -1
- package/dist/router/server.js +106 -44
- package/dist/transport/events.d.ts +19 -0
- package/dist/transport/events.d.ts.map +1 -0
- package/dist/transport/events.js +26 -0
- package/dist/transport/impls/stdio/stdio.d.ts +0 -4
- package/dist/transport/impls/stdio/stdio.d.ts.map +1 -1
- package/dist/transport/impls/stdio/stdio.js +0 -5
- package/dist/transport/impls/stdio/stdio.test.js +5 -0
- package/dist/transport/impls/ws/client.d.ts.map +1 -1
- package/dist/transport/impls/ws/client.js +2 -2
- package/dist/transport/impls/ws/connection.d.ts.map +1 -1
- package/dist/transport/impls/ws/connection.js +2 -1
- package/dist/transport/impls/ws/server.d.ts +0 -2
- package/dist/transport/impls/ws/server.d.ts.map +1 -1
- package/dist/transport/impls/ws/server.js +4 -9
- package/dist/transport/impls/ws/ws.test.js +30 -10
- package/dist/transport/index.d.ts +3 -3
- package/dist/transport/index.d.ts.map +1 -1
- package/dist/transport/index.js +3 -3
- package/dist/transport/message.d.ts +10 -0
- package/dist/transport/message.d.ts.map +1 -1
- package/dist/transport/message.js +15 -0
- package/dist/transport/transport.d.ts +48 -19
- package/dist/transport/transport.d.ts.map +1 -1
- package/dist/transport/transport.js +60 -27
- package/dist/util/testHelpers.d.ts +58 -7
- package/dist/util/testHelpers.d.ts.map +1 -1
- package/dist/util/testHelpers.js +133 -3
- package/package.json +12 -13
- /package/dist/__tests__/{largePayload.json → fixtures/largePayload.json} +0 -0
package/dist/router/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pushable } from 'it-pushable';
|
|
2
|
-
import { ControlMessagePayloadSchema, isStreamClose, isStreamOpen, reply, } from '../transport/message';
|
|
2
|
+
import { ControlMessagePayloadSchema, isStreamClose, isStreamOpen, reply, closeStream, } from '../transport/message';
|
|
3
3
|
import { log } from '../logging';
|
|
4
4
|
import { Value } from '@sinclair/typebox/value';
|
|
5
5
|
import { Err, UNCAUGHT_ERROR, } from './result';
|
|
@@ -15,6 +15,16 @@ export async function createServer(transport, services, extendedContext) {
|
|
|
15
15
|
const contextMap = new Map();
|
|
16
16
|
// map of streamId to ProcStream
|
|
17
17
|
const streamMap = new Map();
|
|
18
|
+
async function cleanupStream(id) {
|
|
19
|
+
const stream = streamMap.get(id);
|
|
20
|
+
if (stream) {
|
|
21
|
+
stream.incoming.end();
|
|
22
|
+
await stream.promises.inputHandler;
|
|
23
|
+
stream.outgoing.end();
|
|
24
|
+
await stream.promises.outputHandler;
|
|
25
|
+
streamMap.delete(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
18
28
|
function getContext(service) {
|
|
19
29
|
const context = contextMap.get(service);
|
|
20
30
|
if (!context) {
|
|
@@ -28,50 +38,70 @@ export async function createServer(transport, services, extendedContext) {
|
|
|
28
38
|
for (const service of Object.values(services)) {
|
|
29
39
|
contextMap.set(service, { ...extendedContext, state: service.state });
|
|
30
40
|
}
|
|
31
|
-
const handler = async (
|
|
32
|
-
if (
|
|
41
|
+
const handler = async (message) => {
|
|
42
|
+
if (message.to !== transport.clientId) {
|
|
33
43
|
log?.info(`${transport.clientId} -- got msg with destination that isn't the server, ignoring`);
|
|
34
44
|
return;
|
|
35
45
|
}
|
|
36
|
-
if (!(
|
|
37
|
-
log?.warn(`${transport.clientId} -- couldn't find service ${
|
|
46
|
+
if (!(message.serviceName in services)) {
|
|
47
|
+
log?.warn(`${transport.clientId} -- couldn't find service ${message.serviceName}`);
|
|
38
48
|
return;
|
|
39
49
|
}
|
|
40
|
-
const service = services[
|
|
50
|
+
const service = services[message.serviceName];
|
|
41
51
|
const serviceContext = getContext(service);
|
|
42
|
-
if (!(
|
|
43
|
-
log?.warn(`${transport.clientId} -- couldn't find a matching procedure for ${
|
|
52
|
+
if (!(message.procedureName in service.procedures)) {
|
|
53
|
+
log?.warn(`${transport.clientId} -- couldn't find a matching procedure for ${message.serviceName}.${message.procedureName}`);
|
|
44
54
|
return;
|
|
45
55
|
}
|
|
46
|
-
const procedure = service.procedures[
|
|
47
|
-
const streamIdx = `${
|
|
48
|
-
if (isStreamOpen(
|
|
56
|
+
const procedure = service.procedures[message.procedureName];
|
|
57
|
+
const streamIdx = `${message.serviceName}.${message.procedureName}:${message.streamId}`;
|
|
58
|
+
if (isStreamOpen(message.controlFlags) && !streamMap.has(streamIdx)) {
|
|
49
59
|
const incoming = pushable({ objectMode: true });
|
|
50
60
|
const outgoing = pushable({ objectMode: true });
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
const outputHandler =
|
|
62
|
+
// sending outgoing messages back to client
|
|
63
|
+
(async () => {
|
|
64
|
+
for await (const response of outgoing) {
|
|
65
|
+
transport.send(response);
|
|
66
|
+
}
|
|
67
|
+
// we ended, send a close bit back to the client
|
|
68
|
+
// only subscriptions and streams have streams the
|
|
69
|
+
// handler can close
|
|
70
|
+
if (procedure.type === 'subscription' ||
|
|
71
|
+
procedure.type === 'stream') {
|
|
72
|
+
transport.send(closeStream(transport.clientId, message.from, message.serviceName, message.procedureName, message.streamId));
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
59
75
|
function errorHandler(err) {
|
|
60
76
|
const errorMsg = err instanceof Error ? err.message : `[coerced to error] ${err}`;
|
|
61
|
-
log?.error(`${transport.clientId} -- procedure ${
|
|
62
|
-
outgoing.push(reply(
|
|
77
|
+
log?.error(`${transport.clientId} -- procedure ${message.serviceName}.${message.procedureName}:${message.streamId} threw an error: ${errorMsg}`);
|
|
78
|
+
outgoing.push(reply(message, Err({
|
|
63
79
|
code: UNCAUGHT_ERROR,
|
|
64
80
|
message: errorMsg,
|
|
65
81
|
})));
|
|
66
82
|
}
|
|
67
83
|
// pump incoming message stream -> handler -> outgoing message stream
|
|
84
|
+
let inputHandler;
|
|
68
85
|
if (procedure.type === 'stream') {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
if ('init' in procedure) {
|
|
87
|
+
inputHandler = (async () => {
|
|
88
|
+
const initMessage = await incoming.next();
|
|
89
|
+
if (initMessage.done) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
return procedure
|
|
93
|
+
.handler(serviceContext, initMessage.value, incoming, outgoing)
|
|
94
|
+
.catch(errorHandler);
|
|
95
|
+
})();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
inputHandler = procedure
|
|
99
|
+
.handler(serviceContext, incoming, outgoing)
|
|
100
|
+
.catch(errorHandler);
|
|
101
|
+
}
|
|
72
102
|
}
|
|
73
103
|
else if (procedure.type === 'rpc') {
|
|
74
|
-
|
|
104
|
+
inputHandler = (async () => {
|
|
75
105
|
const inputMessage = await incoming.next();
|
|
76
106
|
if (inputMessage.done) {
|
|
77
107
|
return;
|
|
@@ -83,10 +113,10 @@ export async function createServer(transport, services, extendedContext) {
|
|
|
83
113
|
catch (err) {
|
|
84
114
|
errorHandler(err);
|
|
85
115
|
}
|
|
86
|
-
})()
|
|
116
|
+
})();
|
|
87
117
|
}
|
|
88
118
|
else if (procedure.type === 'subscription') {
|
|
89
|
-
|
|
119
|
+
inputHandler = (async () => {
|
|
90
120
|
const inputMessage = await incoming.next();
|
|
91
121
|
if (inputMessage.done) {
|
|
92
122
|
return;
|
|
@@ -97,40 +127,72 @@ export async function createServer(transport, services, extendedContext) {
|
|
|
97
127
|
catch (err) {
|
|
98
128
|
errorHandler(err);
|
|
99
129
|
}
|
|
100
|
-
})()
|
|
130
|
+
})();
|
|
131
|
+
}
|
|
132
|
+
else if (procedure.type === 'upload') {
|
|
133
|
+
if ('init' in procedure) {
|
|
134
|
+
inputHandler = (async () => {
|
|
135
|
+
const initMessage = await incoming.next();
|
|
136
|
+
if (initMessage.done) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const outputMessage = await procedure.handler(serviceContext, initMessage.value, incoming);
|
|
141
|
+
outgoing.push(outputMessage);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
errorHandler(err);
|
|
145
|
+
}
|
|
146
|
+
})();
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
inputHandler = (async () => {
|
|
150
|
+
try {
|
|
151
|
+
const outputMessage = await procedure.handler(serviceContext, incoming);
|
|
152
|
+
outgoing.push(outputMessage);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
errorHandler(err);
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// procedure is inferred to be never here as this is not a valid procedure type
|
|
162
|
+
// we cast just to log
|
|
163
|
+
log?.warn(`${transport.clientId} -- got request for invalid procedure type ${procedure.type} at ${message.serviceName}.${message.procedureName}`);
|
|
164
|
+
return;
|
|
101
165
|
}
|
|
102
166
|
streamMap.set(streamIdx, {
|
|
103
167
|
incoming,
|
|
104
168
|
outgoing,
|
|
105
|
-
|
|
169
|
+
promises: { inputHandler, outputHandler },
|
|
106
170
|
});
|
|
107
171
|
}
|
|
108
172
|
const procStream = streamMap.get(streamIdx);
|
|
109
173
|
if (!procStream) {
|
|
110
|
-
log?.warn(`${transport.clientId} -- couldn't find a matching procedure stream for ${
|
|
174
|
+
log?.warn(`${transport.clientId} -- couldn't find a matching procedure stream for ${message.serviceName}.${message.procedureName}:${message.streamId}`);
|
|
111
175
|
return;
|
|
112
176
|
}
|
|
113
|
-
if (Value.Check(procedure.input,
|
|
114
|
-
|
|
177
|
+
if (Value.Check(procedure.input, message.payload) ||
|
|
178
|
+
('init' in procedure && Value.Check(procedure.init, message.payload))) {
|
|
179
|
+
procStream.incoming.push(message);
|
|
115
180
|
}
|
|
116
|
-
else if (!Value.Check(ControlMessagePayloadSchema,
|
|
117
|
-
log?.error(`${transport.clientId} -- procedure ${
|
|
181
|
+
else if (!Value.Check(ControlMessagePayloadSchema, message.payload)) {
|
|
182
|
+
log?.error(`${transport.clientId} -- procedure ${message.serviceName}.${message.procedureName} received invalid payload: ${JSON.stringify(message.payload)}`);
|
|
118
183
|
}
|
|
119
|
-
if (isStreamClose(
|
|
120
|
-
|
|
121
|
-
await Promise.all(procStream.openPromises);
|
|
122
|
-
procStream.outgoing.end();
|
|
184
|
+
if (isStreamClose(message.controlFlags)) {
|
|
185
|
+
await cleanupStream(streamIdx);
|
|
123
186
|
}
|
|
124
187
|
};
|
|
125
|
-
transport.
|
|
188
|
+
transport.addEventListener('message', handler);
|
|
126
189
|
return {
|
|
127
190
|
services,
|
|
191
|
+
streams: streamMap,
|
|
128
192
|
async close() {
|
|
129
|
-
transport.
|
|
130
|
-
for (const
|
|
131
|
-
|
|
132
|
-
await Promise.all(stream.openPromises);
|
|
133
|
-
stream.outgoing.end();
|
|
193
|
+
transport.removeEventListener('message', handler);
|
|
194
|
+
for (const streamIdx of streamMap.keys()) {
|
|
195
|
+
await cleanupStream(streamIdx);
|
|
134
196
|
}
|
|
135
197
|
},
|
|
136
198
|
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OpaqueTransportMessage } from './message';
|
|
2
|
+
import { Connection } from './transport';
|
|
3
|
+
export interface EventMap {
|
|
4
|
+
message: OpaqueTransportMessage;
|
|
5
|
+
connectionStatus: {
|
|
6
|
+
status: 'connect' | 'disconnect';
|
|
7
|
+
conn: Connection;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export type EventTypes = keyof EventMap;
|
|
11
|
+
export type EventHandler<K extends EventTypes> = (event: EventMap[K]) => void;
|
|
12
|
+
export declare class EventDispatcher<T extends EventTypes> {
|
|
13
|
+
private eventListeners;
|
|
14
|
+
numberOfListeners<K extends T>(eventType: K): number;
|
|
15
|
+
addEventListener<K extends T>(eventType: K, handler: EventHandler<K>): void;
|
|
16
|
+
removeEventListener<K extends T>(eventType: K, handler: EventHandler<K>): void;
|
|
17
|
+
dispatchEvent<K extends T>(eventType: K, event: EventMap[K]): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../transport/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,sBAAsB,CAAC;IAChC,gBAAgB,EAAE;QAChB,MAAM,EAAE,SAAS,GAAG,YAAY,CAAC;QACjC,IAAI,EAAE,UAAU,CAAC;KAClB,CAAC;CACH;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC;AACxC,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAE9E,qBAAa,eAAe,CAAC,CAAC,SAAS,UAAU;IAC/C,OAAO,CAAC,cAAc,CAA2C;IAEjE,iBAAiB,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC;IAI3C,gBAAgB,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;IAQpE,mBAAmB,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;IAOvE,aAAa,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;CAQ5D"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class EventDispatcher {
|
|
2
|
+
eventListeners = {};
|
|
3
|
+
numberOfListeners(eventType) {
|
|
4
|
+
return this.eventListeners[eventType]?.size ?? 0;
|
|
5
|
+
}
|
|
6
|
+
addEventListener(eventType, handler) {
|
|
7
|
+
if (!this.eventListeners[eventType]) {
|
|
8
|
+
this.eventListeners[eventType] = new Set();
|
|
9
|
+
}
|
|
10
|
+
this.eventListeners[eventType]?.add(handler);
|
|
11
|
+
}
|
|
12
|
+
removeEventListener(eventType, handler) {
|
|
13
|
+
const handlers = this.eventListeners[eventType];
|
|
14
|
+
if (handlers) {
|
|
15
|
+
this.eventListeners[eventType]?.delete(handler);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
dispatchEvent(eventType, event) {
|
|
19
|
+
const handlers = this.eventListeners[eventType];
|
|
20
|
+
if (handlers) {
|
|
21
|
+
for (const handler of handlers) {
|
|
22
|
+
handler(event);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -3,9 +3,6 @@ import { Codec } from '../../../codec';
|
|
|
3
3
|
import { TransportClientId } from '../../message';
|
|
4
4
|
import { Connection, Transport } from '../../transport';
|
|
5
5
|
export declare class StdioConnection extends Connection {
|
|
6
|
-
/**
|
|
7
|
-
* The writable stream to use as output.
|
|
8
|
-
*/
|
|
9
6
|
output: NodeJS.WritableStream;
|
|
10
7
|
constructor(transport: Transport<StdioConnection>, connectedTo: TransportClientId, output: NodeJS.WritableStream);
|
|
11
8
|
send(payload: Uint8Array): boolean;
|
|
@@ -16,7 +13,6 @@ interface Options {
|
|
|
16
13
|
}
|
|
17
14
|
/**
|
|
18
15
|
* A transport implementation that uses standard input and output streams.
|
|
19
|
-
* Can only be used 1:1, not N:1
|
|
20
16
|
* @extends Transport
|
|
21
17
|
*/
|
|
22
18
|
export declare class StdioTransport extends Transport<StdioConnection> {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../../transport/impls/stdio/stdio.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAKxD,qBAAa,eAAgB,SAAQ,UAAU;IAC7C
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../../transport/impls/stdio/stdio.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAKxD,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;gBAG5B,SAAS,EAAE,SAAS,CAAC,eAAe,CAAC,EACrC,WAAW,EAAE,iBAAiB,EAC9B,MAAM,EAAE,MAAM,CAAC,cAAc;IAM/B,IAAI,CAAC,OAAO,EAAE,UAAU;IAOlB,KAAK;CAGZ;AAED,UAAU,OAAO;IACf,KAAK,EAAE,KAAK,CAAC;CACd;AAMD;;;GAGG;AACH,qBAAa,cAAe,SAAQ,SAAS,CAAC,eAAe,CAAC;IAC5D,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC,cAAc,CAAiB;IAC7C,MAAM,EAAE,MAAM,CAAC,cAAc,CAAkB;IAE/C;;;;;OAKG;gBAED,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,GAAE,MAAM,CAAC,cAA8B,EAC5C,MAAM,GAAE,MAAM,CAAC,cAA+B,EAC9C,eAAe,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAUpC,8BAA8B,IAAI,IAAI;IAwBhC,mBAAmB,CAAC,EAAE,EAAE,iBAAiB;CAShD"}
|
|
@@ -4,9 +4,6 @@ import { Connection, Transport } from '../../transport';
|
|
|
4
4
|
import readline from 'readline';
|
|
5
5
|
const newlineBuff = new TextEncoder().encode('\n');
|
|
6
6
|
export class StdioConnection extends Connection {
|
|
7
|
-
/**
|
|
8
|
-
* The writable stream to use as output.
|
|
9
|
-
*/
|
|
10
7
|
output;
|
|
11
8
|
constructor(transport, connectedTo, output) {
|
|
12
9
|
super(transport, connectedTo);
|
|
@@ -19,7 +16,6 @@ export class StdioConnection extends Connection {
|
|
|
19
16
|
return this.output.write(out);
|
|
20
17
|
}
|
|
21
18
|
async close() {
|
|
22
|
-
this.transport.onDisconnect(this);
|
|
23
19
|
this.output.end();
|
|
24
20
|
}
|
|
25
21
|
}
|
|
@@ -28,7 +24,6 @@ const defaultOptions = {
|
|
|
28
24
|
};
|
|
29
25
|
/**
|
|
30
26
|
* A transport implementation that uses standard input and output streams.
|
|
31
|
-
* Can only be used 1:1, not N:1
|
|
32
27
|
* @extends Transport
|
|
33
28
|
*/
|
|
34
29
|
export class StdioTransport extends Transport {
|
|
@@ -3,6 +3,7 @@ import stream from 'node:stream';
|
|
|
3
3
|
import { StdioTransport } from './stdio';
|
|
4
4
|
import { waitForMessage } from '../..';
|
|
5
5
|
import { payloadToTransportMessage } from '../../../util/testHelpers';
|
|
6
|
+
import { ensureTransportIsClean } from '../../../__tests__/fixtures/cleanup';
|
|
6
7
|
describe('sending and receiving across node streams works', () => {
|
|
7
8
|
test('basic send/receive', async () => {
|
|
8
9
|
const clientToServer = new stream.PassThrough();
|
|
@@ -16,5 +17,9 @@ describe('sending and receiving across node streams works', () => {
|
|
|
16
17
|
const p = waitForMessage(serverTransport);
|
|
17
18
|
clientTransport.send(payloadToTransportMessage(msg, 'stream', clientTransport.clientId, serverTransport.clientId));
|
|
18
19
|
await expect(p).resolves.toStrictEqual(msg);
|
|
20
|
+
await clientTransport.close();
|
|
21
|
+
await serverTransport.close();
|
|
22
|
+
await ensureTransportIsClean(clientTransport);
|
|
23
|
+
await ensureTransportIsClean(serverTransport);
|
|
19
24
|
});
|
|
20
25
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../transport/impls/ws/client.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,UAAU,OAAO;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;CACd;AAQD,KAAK,eAAe,GAAG;IAAE,EAAE,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,SAAS,CAAC,mBAAmB,CAAC;IAC1E;;OAEG;IACH,QAAQ,EAAE,CAAC,EAAE,EAAE,iBAAiB,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,iBAAiB,CAAC;IAE5B,iBAAiB,EAAE,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IAEpE;;;;;OAKG;gBAED,QAAQ,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,EAClC,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAWpC,8BAA8B,IAAI,IAAI;IAIhC,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,SAAI;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../transport/impls/ws/client.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,UAAU,OAAO;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,KAAK,CAAC;CACd;AAQD,KAAK,eAAe,GAAG;IAAE,EAAE,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,SAAS,CAAC,mBAAmB,CAAC;IAC1E;;OAEG;IACH,QAAQ,EAAE,CAAC,EAAE,EAAE,iBAAiB,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,iBAAiB,CAAC;IAE5B,iBAAiB,EAAE,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC;IAEpE;;;;;OAKG;gBAED,QAAQ,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,EAClC,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IAWpC,8BAA8B,IAAI,IAAI;IAIhC,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,SAAI;IAuEjD;;OAEG;IACG,IAAI;CAGX"}
|
|
@@ -83,13 +83,13 @@ export class WebSocketClientTransport extends Transport {
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
// otherwise try and reconnect again
|
|
86
|
-
log?.warn(`${this.clientId} -- websocket failed, trying again in ${this.options.retryIntervalMs}ms`);
|
|
87
86
|
this.reconnectPromises.delete(to);
|
|
88
87
|
if (attempt >= this.options.retryAttemptsMax) {
|
|
89
|
-
|
|
88
|
+
throw new Error(`${this.clientId} -- websocket to ${to} failed after ${attempt} attempts, giving up`);
|
|
90
89
|
}
|
|
91
90
|
else {
|
|
92
91
|
// linear backoff
|
|
92
|
+
log?.warn(`${this.clientId} -- websocket to ${to} failed, trying again in ${this.options.retryIntervalMs * attempt}ms`);
|
|
93
93
|
setTimeout(() => this.createNewConnection(to, attempt + 1), this.options.retryIntervalMs * attempt);
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../../transport/impls/ws/connection.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,SAAS,MAAM,eAAe,CAAC;AAEtC,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,EAAE,EAAE,SAAS,CAAC;gBAGZ,SAAS,EAAE,SAAS,CAAC,mBAAmB,CAAC,EACzC,WAAW,EAAE,iBAAiB,EAC9B,EAAE,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../../transport/impls/ws/connection.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,SAAS,MAAM,eAAe,CAAC;AAEtC,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,EAAE,EAAE,SAAS,CAAC;gBAGZ,SAAS,EAAE,SAAS,CAAC,mBAAmB,CAAC,EACzC,WAAW,EAAE,iBAAiB,EAC9B,EAAE,EAAE,SAAS;IAUf,IAAI,CAAC,OAAO,EAAE,UAAU;IASlB,KAAK;CAGZ"}
|
|
@@ -5,7 +5,8 @@ export class WebSocketConnection extends Connection {
|
|
|
5
5
|
super(transport, connectedTo);
|
|
6
6
|
this.ws = ws;
|
|
7
7
|
ws.binaryType = 'arraybuffer';
|
|
8
|
-
|
|
8
|
+
// take over the onmessage for this websocket
|
|
9
|
+
this.ws.onmessage = (msg) => transport.onMessage(msg.data);
|
|
9
10
|
}
|
|
10
11
|
send(payload) {
|
|
11
12
|
if (this.ws.readyState === this.ws.OPEN) {
|
|
@@ -12,8 +12,6 @@ export declare class WebSocketServerTransport extends Transport<WebSocketConnect
|
|
|
12
12
|
constructor(wss: Server, clientId: TransportClientId, providedOptions?: Partial<Options>);
|
|
13
13
|
setupConnectionStatusListeners(): void;
|
|
14
14
|
createNewConnection(to: string): Promise<void>;
|
|
15
|
-
destroy(): Promise<void>;
|
|
16
|
-
close(): Promise<void>;
|
|
17
15
|
}
|
|
18
16
|
export {};
|
|
19
17
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../transport/impls/ws/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,UAAU,OAAO;IACf,KAAK,EAAE,KAAK,CAAC;CACd;AAMD,qBAAa,wBAAyB,SAAQ,SAAS,CAAC,mBAAmB,CAAC;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,iBAAiB,CAAC;gBAG1B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IASpC,8BAA8B,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../transport/impls/ws/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,UAAU,OAAO;IACf,KAAK,EAAE,KAAK,CAAC;CACd;AAMD,qBAAa,wBAAyB,SAAQ,SAAS,CAAC,mBAAmB,CAAC;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,iBAAiB,CAAC;gBAG1B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,iBAAiB,EAC3B,eAAe,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC;IASpC,8BAA8B,IAAI,IAAI;IAiChC,mBAAmB,CAAC,EAAE,EAAE,MAAM;CAKrC"}
|
|
@@ -19,8 +19,11 @@ export class WebSocketServerTransport extends Transport {
|
|
|
19
19
|
this.wss.on('connection', (ws) => {
|
|
20
20
|
let conn = undefined;
|
|
21
21
|
ws.onmessage = (msg) => {
|
|
22
|
+
// when we establish WebSocketConnection, ws.onmessage
|
|
23
|
+
// gets overriden so this only runs on the first valid message
|
|
24
|
+
// the websocket receives
|
|
22
25
|
const parsedMsg = this.parseMsg(msg.data);
|
|
23
|
-
if (parsedMsg) {
|
|
26
|
+
if (parsedMsg && !conn) {
|
|
24
27
|
conn = new WebSocketConnection(this, parsedMsg.from, ws);
|
|
25
28
|
this.onConnect(conn);
|
|
26
29
|
this.handleMsg(parsedMsg);
|
|
@@ -42,12 +45,4 @@ export class WebSocketServerTransport extends Transport {
|
|
|
42
45
|
log?.warn(err);
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
45
|
-
async destroy() {
|
|
46
|
-
super.destroy();
|
|
47
|
-
this.wss.close();
|
|
48
|
-
}
|
|
49
|
-
async close() {
|
|
50
|
-
super.close();
|
|
51
|
-
this.wss.close();
|
|
52
|
-
}
|
|
53
48
|
}
|
|
@@ -4,21 +4,24 @@ import { createWebSocketServer, createWsTransports, createDummyTransportMessage,
|
|
|
4
4
|
import { msg, waitForMessage } from '../..';
|
|
5
5
|
import { WebSocketServerTransport } from './server';
|
|
6
6
|
import { WebSocketClientTransport } from './client';
|
|
7
|
+
import { testFinishesCleanly } from '../../../__tests__/fixtures/cleanup';
|
|
7
8
|
describe('sending and receiving across websockets works', async () => {
|
|
8
9
|
const server = http.createServer();
|
|
9
10
|
const port = await onServerReady(server);
|
|
10
11
|
const wss = await createWebSocketServer(server);
|
|
11
12
|
afterAll(() => {
|
|
12
|
-
wss.
|
|
13
|
-
socket.close();
|
|
14
|
-
});
|
|
13
|
+
wss.close();
|
|
15
14
|
server.close();
|
|
16
15
|
});
|
|
17
16
|
test('basic send/receive', async () => {
|
|
18
17
|
const [clientTransport, serverTransport] = createWsTransports(port, wss);
|
|
19
18
|
const msg = createDummyTransportMessage();
|
|
20
19
|
clientTransport.send(msg);
|
|
21
|
-
|
|
20
|
+
await expect(waitForMessage(serverTransport, (recv) => recv.id === msg.id)).resolves.toStrictEqual(msg.payload);
|
|
21
|
+
await testFinishesCleanly({
|
|
22
|
+
clientTransports: [clientTransport],
|
|
23
|
+
serverTransport,
|
|
24
|
+
});
|
|
22
25
|
});
|
|
23
26
|
test('sending respects to/from fields', async () => {
|
|
24
27
|
const makeDummyMessage = (from, to, message) => {
|
|
@@ -50,6 +53,10 @@ describe('sending and receiving across websockets works', async () => {
|
|
|
50
53
|
serverTransport.send(msg1);
|
|
51
54
|
serverTransport.send(msg2);
|
|
52
55
|
await expect(promises).resolves.toStrictEqual([msg1.payload, msg2.payload]);
|
|
56
|
+
await testFinishesCleanly({
|
|
57
|
+
clientTransports: [client1, client2],
|
|
58
|
+
serverTransport,
|
|
59
|
+
});
|
|
53
60
|
});
|
|
54
61
|
});
|
|
55
62
|
describe('retry logic', async () => {
|
|
@@ -57,9 +64,7 @@ describe('retry logic', async () => {
|
|
|
57
64
|
const port = await onServerReady(server);
|
|
58
65
|
const wss = await createWebSocketServer(server);
|
|
59
66
|
afterAll(() => {
|
|
60
|
-
wss.
|
|
61
|
-
socket.close();
|
|
62
|
-
});
|
|
67
|
+
wss.close();
|
|
63
68
|
server.close();
|
|
64
69
|
});
|
|
65
70
|
// TODO: right now, we only test client-side disconnects, we probably
|
|
@@ -73,7 +78,12 @@ describe('retry logic', async () => {
|
|
|
73
78
|
await expect(waitForMessage(serverTransport, (recv) => recv.id === msg1.id)).resolves.toStrictEqual(msg1.payload);
|
|
74
79
|
clientTransport.connections.forEach((conn) => conn.ws.close());
|
|
75
80
|
clientTransport.send(msg2);
|
|
76
|
-
|
|
81
|
+
// by this point the client should have reconnected
|
|
82
|
+
await expect(waitForMessage(serverTransport, (recv) => recv.id === msg2.id)).resolves.toStrictEqual(msg2.payload);
|
|
83
|
+
await testFinishesCleanly({
|
|
84
|
+
clientTransports: [clientTransport],
|
|
85
|
+
serverTransport,
|
|
86
|
+
});
|
|
77
87
|
});
|
|
78
88
|
test('ws transport is recreated after unclean disconnect', async () => {
|
|
79
89
|
const [clientTransport, serverTransport] = createWsTransports(port, wss);
|
|
@@ -83,7 +93,13 @@ describe('retry logic', async () => {
|
|
|
83
93
|
await expect(waitForMessage(serverTransport, (recv) => recv.id === msg1.id)).resolves.toStrictEqual(msg1.payload);
|
|
84
94
|
clientTransport.connections.forEach((conn) => conn.ws.terminate());
|
|
85
95
|
clientTransport.send(msg2);
|
|
86
|
-
|
|
96
|
+
// by this point the client should have reconnected
|
|
97
|
+
await expect(waitForMessage(serverTransport, (recv) => recv.id === msg2.id)).resolves.toStrictEqual(msg2.payload);
|
|
98
|
+
// this is not expected to be clean because we destroyed the transport
|
|
99
|
+
await testFinishesCleanly({
|
|
100
|
+
clientTransports: [clientTransport],
|
|
101
|
+
serverTransport,
|
|
102
|
+
});
|
|
87
103
|
});
|
|
88
104
|
test('ws transport is not recreated after destroy', async () => {
|
|
89
105
|
const [clientTransport, serverTransport] = createWsTransports(port, wss);
|
|
@@ -92,6 +108,10 @@ describe('retry logic', async () => {
|
|
|
92
108
|
clientTransport.send(msg1);
|
|
93
109
|
await expect(waitForMessage(serverTransport, (recv) => recv.id === msg1.id)).resolves.toStrictEqual(msg1.payload);
|
|
94
110
|
clientTransport.destroy();
|
|
95
|
-
|
|
111
|
+
expect(() => clientTransport.send(msg2)).toThrow(new Error('transport is destroyed, cant send'));
|
|
112
|
+
// this is not expected to be clean because we destroyed the transport
|
|
113
|
+
expect(clientTransport.state).toEqual('destroyed');
|
|
114
|
+
await clientTransport.close();
|
|
115
|
+
await serverTransport.close();
|
|
96
116
|
});
|
|
97
117
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { OpaqueTransportMessage } from './message';
|
|
2
|
-
import {
|
|
3
|
-
export { Transport } from './transport';
|
|
2
|
+
import { Transport, Connection } from './transport';
|
|
3
|
+
export { Transport, Connection } from './transport';
|
|
4
4
|
export { TransportMessageSchema, OpaqueTransportMessageSchema, msg, reply, } from './message';
|
|
5
|
-
export type { TransportMessage, MessageId, OpaqueTransportMessage, TransportClientId, } from './message';
|
|
5
|
+
export type { TransportMessage, MessageId, OpaqueTransportMessage, TransportClientId, isStreamOpen, isStreamClose, } from './message';
|
|
6
6
|
/**
|
|
7
7
|
* Waits for a message from the transport.
|
|
8
8
|
* @param {Transport} t - The transport to listen to.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,sBAAsB,EACtB,4BAA4B,EAC5B,GAAG,EACH,KAAK,GACN,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,gBAAgB,EAChB,SAAS,EACT,sBAAsB,EACtB,iBAAiB,EACjB,YAAY,EACZ,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,EACxB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,OAAO,EACjD,cAAc,CAAC,EAAE,OAAO,oBAczB"}
|
package/dist/transport/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// re-export
|
|
2
|
-
export { Transport } from './transport';
|
|
2
|
+
export { Transport, Connection } from './transport';
|
|
3
3
|
export { TransportMessageSchema, OpaqueTransportMessageSchema, msg, reply, } from './message';
|
|
4
4
|
/**
|
|
5
5
|
* Waits for a message from the transport.
|
|
@@ -12,12 +12,12 @@ export async function waitForMessage(t, filter, rejectMismatch) {
|
|
|
12
12
|
function onMessage(msg) {
|
|
13
13
|
if (!filter || filter?.(msg)) {
|
|
14
14
|
resolve(msg.payload);
|
|
15
|
-
t.
|
|
15
|
+
t.removeEventListener('message', onMessage);
|
|
16
16
|
}
|
|
17
17
|
else if (rejectMismatch) {
|
|
18
18
|
reject(new Error('message didnt match the filter'));
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
t.
|
|
21
|
+
t.addEventListener('message', onMessage);
|
|
22
22
|
});
|
|
23
23
|
}
|
|
@@ -103,6 +103,16 @@ export declare function msg<Payload extends object>(from: string, to: string, se
|
|
|
103
103
|
* @returns A new transport message with appropriate to, from, and payload fields
|
|
104
104
|
*/
|
|
105
105
|
export declare function reply<Payload extends object>(msg: OpaqueTransportMessage, response: Payload): TransportMessage<Payload>;
|
|
106
|
+
/**
|
|
107
|
+
* Create a request to close a stream
|
|
108
|
+
* @param from The ID of the client initiating the close.
|
|
109
|
+
* @param to The ID of the client being closed.
|
|
110
|
+
* @param respondTo The transport message to respond to.
|
|
111
|
+
* @returns The close message
|
|
112
|
+
*/
|
|
113
|
+
export declare function closeStream(from: TransportClientId, to: TransportClientId, service: string, proc: string, stream: string): TransportMessage<{
|
|
114
|
+
type: "CLOSE";
|
|
115
|
+
}>;
|
|
106
116
|
/**
|
|
107
117
|
* Checks if the given control flag (usually found in msg.controlFlag) is an ack message.
|
|
108
118
|
* @param controlFlag - The control flag to check.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../transport/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../transport/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,OAAO,EAAU,MAAM,mBAAmB,CAAC;AAG1D;;;;;;GAMG;AACH,0BAAkB,YAAY;IAC5B,MAAM,IAAS;IACf,aAAa,IAAS;IACtB,eAAe,IAAS;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;EAU/B,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;EAI9B,CAAC;AAEF,eAAO,MAAM,2BAA2B;;EAEtC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;EAExC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,CAC1B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACzE;IACF,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEvC;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,OAAO,SAAS,MAAM,EACxC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,GACf,gBAAgB,CAAC,OAAO,CAAC,CAW3B;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,OAAO,SAAS,MAAM,EAC1C,GAAG,EAAE,sBAAsB,EAC3B,QAAQ,EAAE,OAAO,GAChB,gBAAgB,CAAC,OAAO,CAAC,CAS3B;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,iBAAiB,EACrB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM;;GAOf;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAK1D"}
|
|
@@ -65,12 +65,27 @@ export function msg(from, to, service, proc, stream, payload) {
|
|
|
65
65
|
export function reply(msg, response) {
|
|
66
66
|
return {
|
|
67
67
|
...msg,
|
|
68
|
+
controlFlags: 0,
|
|
68
69
|
id: nanoid(),
|
|
69
70
|
to: msg.from,
|
|
70
71
|
from: msg.to,
|
|
71
72
|
payload: response,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a request to close a stream
|
|
77
|
+
* @param from The ID of the client initiating the close.
|
|
78
|
+
* @param to The ID of the client being closed.
|
|
79
|
+
* @param respondTo The transport message to respond to.
|
|
80
|
+
* @returns The close message
|
|
81
|
+
*/
|
|
82
|
+
export function closeStream(from, to, service, proc, stream) {
|
|
83
|
+
const closeMessage = msg(from, to, service, proc, stream, {
|
|
84
|
+
type: 'CLOSE',
|
|
85
|
+
});
|
|
86
|
+
closeMessage.controlFlags |= 4 /* ControlFlags.StreamClosedBit */;
|
|
87
|
+
return closeMessage;
|
|
88
|
+
}
|
|
74
89
|
/**
|
|
75
90
|
* Checks if the given control flag (usually found in msg.controlFlag) is an ack message.
|
|
76
91
|
* @param controlFlag - The control flag to check.
|