@replit/river 0.6.4 → 0.7.1

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.
Files changed (54) hide show
  1. package/README.md +4 -4
  2. package/dist/__tests__/e2e.test.js +7 -8
  3. package/dist/__tests__/typescript-stress.test.d.ts +4 -2
  4. package/dist/__tests__/typescript-stress.test.d.ts.map +1 -1
  5. package/dist/__tests__/typescript-stress.test.js +3 -1
  6. package/dist/router/client.d.ts +3 -2
  7. package/dist/router/client.d.ts.map +1 -1
  8. package/dist/router/client.js +5 -5
  9. package/dist/router/server.d.ts +2 -2
  10. package/dist/router/server.d.ts.map +1 -1
  11. package/dist/router/server.js +10 -10
  12. package/dist/testUtils.d.ts +6 -5
  13. package/dist/testUtils.d.ts.map +1 -1
  14. package/dist/testUtils.js +7 -16
  15. package/dist/transport/impls/{stdio.d.ts → stdio/stdio.d.ts} +17 -20
  16. package/dist/transport/impls/stdio/stdio.d.ts.map +1 -0
  17. package/dist/transport/impls/stdio/stdio.js +80 -0
  18. package/dist/transport/impls/stdio/stdio.test.d.ts.map +1 -0
  19. package/dist/transport/impls/{stdio.test.js → stdio/stdio.test.js} +5 -5
  20. package/dist/transport/impls/ws/client.d.ts +45 -0
  21. package/dist/transport/impls/ws/client.d.ts.map +1 -0
  22. package/dist/transport/impls/ws/client.js +102 -0
  23. package/dist/transport/impls/ws/connection.d.ts +11 -0
  24. package/dist/transport/impls/ws/connection.d.ts.map +1 -0
  25. package/dist/transport/impls/ws/connection.js +22 -0
  26. package/dist/transport/impls/ws/server.d.ts +19 -0
  27. package/dist/transport/impls/ws/server.d.ts.map +1 -0
  28. package/dist/transport/impls/ws/server.js +53 -0
  29. package/dist/transport/impls/ws/ws.test.d.ts.map +1 -0
  30. package/dist/transport/impls/{ws.test.js → ws/ws.test.js} +38 -5
  31. package/dist/transport/index.d.ts +3 -3
  32. package/dist/transport/index.d.ts.map +1 -1
  33. package/dist/transport/index.js +6 -3
  34. package/dist/transport/message.d.ts +4 -1
  35. package/dist/transport/message.d.ts.map +1 -1
  36. package/dist/transport/message.js +3 -0
  37. package/dist/transport/transport.d.ts +132 -0
  38. package/dist/transport/transport.d.ts.map +1 -0
  39. package/dist/transport/transport.js +241 -0
  40. package/package.json +4 -3
  41. package/dist/__tests__/integration.test.d.ts +0 -50
  42. package/dist/__tests__/integration.test.js +0 -193
  43. package/dist/transport/impls/stdio.d.ts.map +0 -1
  44. package/dist/transport/impls/stdio.js +0 -56
  45. package/dist/transport/impls/stdio.test.d.ts.map +0 -1
  46. package/dist/transport/impls/ws.d.ts +0 -71
  47. package/dist/transport/impls/ws.d.ts.map +0 -1
  48. package/dist/transport/impls/ws.js +0 -156
  49. package/dist/transport/impls/ws.test.d.ts.map +0 -1
  50. package/dist/transport/types.d.ts +0 -50
  51. package/dist/transport/types.d.ts.map +0 -1
  52. package/dist/transport/types.js +0 -95
  53. /package/dist/transport/impls/{stdio.test.d.ts → stdio/stdio.test.d.ts} +0 -0
  54. /package/dist/transport/impls/{ws.test.d.ts → ws/ws.test.d.ts} +0 -0
@@ -1,156 +0,0 @@
1
- import { Transport } from '../types';
2
- import { NaiveJsonCodec } from '../../codec/json';
3
- import { log } from '../../logging';
4
- const defaultOptions = {
5
- retryIntervalMs: 250,
6
- codec: NaiveJsonCodec,
7
- binaryType: 'arraybuffer',
8
- };
9
- /**
10
- * A transport implementation that uses a WebSocket connection with automatic reconnection.
11
- * @class
12
- * @extends Transport
13
- */
14
- export class WebSocketTransport extends Transport {
15
- /**
16
- * A function that returns a Promise that resolves to a WebSocket instance.
17
- */
18
- wsGetter;
19
- ws;
20
- options;
21
- /**
22
- * A flag indicating whether the transport has been destroyed.
23
- * A destroyed transport will not attempt to reconnect and cannot be used again.
24
- */
25
- state;
26
- /**
27
- * An ongoing reconnect attempt if it exists. When the attempt finishes, it contains a
28
- * {@link WebSocketResult} object when a connection is established or an error occurs.
29
- */
30
- reconnectPromise;
31
- /**
32
- * An array of message IDs that are waiting to be sent over the WebSocket connection.
33
- * This builds up if the WebSocket is down for a period of time.
34
- */
35
- sendQueue;
36
- /**
37
- * Creates a new WebSocketTransport instance.
38
- * @param wsGetter A function that returns a Promise that resolves to a WebSocket instance.
39
- * @param clientId The ID of the client using the transport.
40
- * @param providedOptions An optional object containing configuration options for the transport.
41
- */
42
- constructor(wsGetter, clientId, providedOptions) {
43
- const options = { ...defaultOptions, ...providedOptions };
44
- super(options.codec, clientId);
45
- this.state = 'open';
46
- this.wsGetter = wsGetter;
47
- this.options = options;
48
- this.sendQueue = [];
49
- this.tryConnect();
50
- }
51
- /**
52
- * Begins a new attempt to establish a WebSocket connection.
53
- */
54
- async tryConnect() {
55
- if (this.state !== 'open') {
56
- return;
57
- }
58
- // wait until it's ready or we get an error
59
- this.reconnectPromise ??= new Promise(async (resolve) => {
60
- log?.info(`${this.clientId} -- establishing a new websocket`);
61
- const ws = await this.wsGetter();
62
- if (ws.readyState === ws.OPEN) {
63
- return resolve({ ws });
64
- }
65
- if (ws.readyState === ws.CLOSING || ws.readyState === ws.CLOSED) {
66
- return resolve({ err: 'ws is closing or closed' });
67
- }
68
- ws.addEventListener('open', function onOpen() {
69
- ws.removeEventListener('open', onOpen);
70
- resolve({ ws });
71
- });
72
- ws.addEventListener('error', function onError(err) {
73
- ws.removeEventListener('error', onError);
74
- resolve({ err: err.message });
75
- });
76
- ws.addEventListener('close', function onClose(evt) {
77
- ws.removeEventListener('close', onClose);
78
- resolve({ err: evt.reason });
79
- });
80
- });
81
- const res = await this.reconnectPromise;
82
- // only send if we resolved a valid websocket
83
- if ('ws' in res && res.ws.readyState === res.ws.OPEN) {
84
- log?.info(`${this.clientId} -- websocket ok`);
85
- this.ws = res.ws;
86
- this.ws.binaryType = 'arraybuffer';
87
- this.ws.onmessage = (msg) => this.onMessage(msg.data);
88
- this.ws.onclose = () => {
89
- this.reconnectPromise = undefined;
90
- this.tryConnect().catch();
91
- };
92
- // send outstanding
93
- for (const id of this.sendQueue) {
94
- const msg = this.sendBuffer.get(id);
95
- if (!msg) {
96
- const err = 'tried to resend a message we received an ack for';
97
- log?.error(err);
98
- throw new Error(err);
99
- }
100
- log?.info(`${this.clientId} -- sending ${JSON.stringify(msg)}`);
101
- this.ws.send(this.codec.toBuffer(msg));
102
- }
103
- this.sendQueue = [];
104
- return;
105
- }
106
- // otherwise try and reconnect again
107
- log?.warn(`${this.clientId} -- websocket failed, trying again in ${this.options.retryIntervalMs}ms`);
108
- this.reconnectPromise = undefined;
109
- setTimeout(() => this.tryConnect(), this.options.retryIntervalMs);
110
- }
111
- /**
112
- * Sends a message over the WebSocket connection. If the WebSocket connection is
113
- * not healthy, it will queue until the connection is successful.
114
- * @param msg The message to send.
115
- * @returns The ID of the sent message.
116
- */
117
- send(msg) {
118
- const id = msg.id;
119
- if (this.state === 'destroyed') {
120
- const err = 'ws is destroyed, cant send';
121
- log?.error(err + `: ${JSON.stringify(msg)}`);
122
- throw new Error(err);
123
- }
124
- else if (this.state === 'closed') {
125
- log?.info(`ws is closed, discarding msg: ${JSON.stringify(msg)}`);
126
- return msg.id;
127
- }
128
- this.sendBuffer.set(id, msg);
129
- if (this.ws && this.ws.readyState === this.ws.OPEN) {
130
- log?.info(`${this.clientId} -- sending ${JSON.stringify(msg)}`);
131
- this.ws.send(this.codec.toBuffer(msg));
132
- }
133
- else {
134
- log?.info(`${this.clientId} -- transport not ready, queuing ${JSON.stringify(msg)}`);
135
- this.sendQueue.push(id);
136
- this.tryConnect().catch();
137
- }
138
- return id;
139
- }
140
- /**
141
- * Closes the WebSocket transport. Any messages sent while the transport is closed will be silently discarded.
142
- */
143
- async close() {
144
- log?.info('closed ws transport');
145
- this.state = 'closed';
146
- return this.ws?.close();
147
- }
148
- /**
149
- * Destroys the WebSocket transport. Any messages sent while the transport is closed will throw an error.
150
- */
151
- async destroy() {
152
- log?.info('destroyed ws transport');
153
- this.state = 'destroyed';
154
- return this.ws?.close();
155
- }
156
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"ws.test.d.ts","sourceRoot":"","sources":["../../../transport/impls/ws.test.ts"],"names":[],"mappings":""}
@@ -1,50 +0,0 @@
1
- import { Codec } from '../codec/types';
2
- import { MessageId, OpaqueTransportMessage, TransportClientId } from './message';
3
- /**
4
- * Abstract base for a transport layer for communication between nodes in a River network.
5
- * Any River transport methods need to implement this interface.
6
- * @abstract
7
- */
8
- export declare abstract class Transport {
9
- /**
10
- * The {@link Codec} used to encode and decode messages.
11
- */
12
- codec: Codec;
13
- /**
14
- * The client ID of this transport.
15
- */
16
- clientId: TransportClientId;
17
- /**
18
- * The set of message handlers registered with this transport.
19
- */
20
- handlers: Set<(msg: OpaqueTransportMessage) => void>;
21
- /**
22
- * The buffer of messages that have been sent but not yet acknowledged.
23
- */
24
- sendBuffer: Map<MessageId, OpaqueTransportMessage>;
25
- /**
26
- * Creates a new Transport instance.
27
- * @param codec The codec used to encode and decode messages.
28
- * @param clientId The client ID of this transport.
29
- */
30
- constructor(codec: Codec, clientId: TransportClientId);
31
- /**
32
- * Called when a message is received by this transport.
33
- * You generally shouldn't need to override this in downstream transport implementations.
34
- * @param msg The received message.
35
- */
36
- onMessage(msg: Uint8Array): void;
37
- /**
38
- * Adds a message listener to this transport.
39
- * @param handler The message handler to add.
40
- */
41
- addMessageListener(handler: (msg: OpaqueTransportMessage) => void): void;
42
- /**
43
- * Removes a message listener from this transport.
44
- * @param handler The message handler to remove.
45
- */
46
- removeMessageListener(handler: (msg: OpaqueTransportMessage) => void): void;
47
- abstract send(msg: OpaqueTransportMessage): MessageId;
48
- abstract close(): Promise<void>;
49
- }
50
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../transport/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC,OAAO,EAEL,SAAS,EACT,sBAAsB,EAGtB,iBAAiB,EAGlB,MAAM,WAAW,CAAC;AAGnB;;;;GAIG;AACH,8BAAsB,SAAS;IAC7B;;OAEG;IACH,KAAK,EAAE,KAAK,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,iBAAiB,CAAC;IAE5B;;OAEG;IACH,QAAQ,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,sBAAsB,KAAK,IAAI,CAAC,CAAC;IAGrD;;OAEG;IACH,UAAU,EAAE,GAAG,CAAC,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAEnD;;;;OAIG;gBACS,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB;IAOrD;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,UAAU;IAoDzB;;;OAGG;IACH,kBAAkB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,IAAI,GAAG,IAAI;IAIxE;;;OAGG;IACH,qBAAqB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,sBAAsB,KAAK,IAAI,GAAG,IAAI;IAI3E,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,sBAAsB,GAAG,SAAS;IACrD,QAAQ,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAChC"}
@@ -1,95 +0,0 @@
1
- import { Value } from '@sinclair/typebox/value';
2
- import { OpaqueTransportMessageSchema, TransportAckSchema, isAck, reply, } from './message';
3
- import { log } from '../logging';
4
- /**
5
- * Abstract base for a transport layer for communication between nodes in a River network.
6
- * Any River transport methods need to implement this interface.
7
- * @abstract
8
- */
9
- export class Transport {
10
- /**
11
- * The {@link Codec} used to encode and decode messages.
12
- */
13
- codec;
14
- /**
15
- * The client ID of this transport.
16
- */
17
- clientId;
18
- /**
19
- * The set of message handlers registered with this transport.
20
- */
21
- handlers;
22
- // TODO; we can do much better here on retry (maybe resending the sendBuffer on fixed interval)
23
- /**
24
- * The buffer of messages that have been sent but not yet acknowledged.
25
- */
26
- sendBuffer;
27
- /**
28
- * Creates a new Transport instance.
29
- * @param codec The codec used to encode and decode messages.
30
- * @param clientId The client ID of this transport.
31
- */
32
- constructor(codec, clientId) {
33
- this.handlers = new Set();
34
- this.sendBuffer = new Map();
35
- this.codec = codec;
36
- this.clientId = clientId;
37
- }
38
- /**
39
- * Called when a message is received by this transport.
40
- * You generally shouldn't need to override this in downstream transport implementations.
41
- * @param msg The received message.
42
- */
43
- onMessage(msg) {
44
- const parsedMsg = this.codec.fromBuffer(msg);
45
- if (parsedMsg === null) {
46
- log?.warn(`${this.clientId} -- received malformed msg: ${new TextDecoder().decode(msg)}`);
47
- return;
48
- }
49
- let stringifiedMessage;
50
- if (log) {
51
- stringifiedMessage = JSON.stringify(parsedMsg);
52
- }
53
- if (Value.Check(TransportAckSchema, parsedMsg) &&
54
- isAck(parsedMsg.controlFlags)) {
55
- // process ack
56
- log?.info(`${this.clientId} -- received ack: ${stringifiedMessage}`);
57
- if (this.sendBuffer.has(parsedMsg.payload.ack)) {
58
- this.sendBuffer.delete(parsedMsg.payload.ack);
59
- }
60
- }
61
- else if (Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
62
- // regular river message
63
- log?.info(`${this.clientId} -- received msg: ${stringifiedMessage}`);
64
- // ignore if not for us
65
- if (parsedMsg.to !== this.clientId && parsedMsg.to !== 'broadcast') {
66
- return;
67
- }
68
- // handle actual message
69
- for (const handler of this.handlers) {
70
- handler(parsedMsg);
71
- }
72
- const ackMsg = reply(parsedMsg, { ack: parsedMsg.id });
73
- ackMsg.controlFlags = 1 /* ControlFlags.AckBit */;
74
- ackMsg.from = this.clientId;
75
- this.send(ackMsg);
76
- }
77
- else {
78
- log?.warn(`${this.clientId} -- received invalid transport msg: ${stringifiedMessage}`);
79
- }
80
- }
81
- /**
82
- * Adds a message listener to this transport.
83
- * @param handler The message handler to add.
84
- */
85
- addMessageListener(handler) {
86
- this.handlers.add(handler);
87
- }
88
- /**
89
- * Removes a message listener from this transport.
90
- * @param handler The message handler to remove.
91
- */
92
- removeMessageListener(handler) {
93
- this.handlers.delete(handler);
94
- }
95
- }