@replit/river 0.216.0 → 0.217.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.
- package/README.md +2 -2
- package/dist/{adapter-BXCk-dmy.d.ts → adapter-Dl5Mewp3.d.ts} +1 -1
- package/dist/{chunk-ZLMQQI43.js → chunk-JFFRB3SS.js} +57 -27
- package/dist/chunk-JFFRB3SS.js.map +1 -0
- package/dist/{chunk-75ZMPCKC.js → chunk-VK3VJZGG.js} +19 -18
- package/dist/chunk-VK3VJZGG.js.map +1 -0
- package/dist/chunk-WMPWPIA4.js +72 -0
- package/dist/chunk-WMPWPIA4.js.map +1 -0
- package/dist/{client-BNc5Pj_4.d.ts → client-YP9bECp8.d.ts} +2 -2
- package/dist/codec/index.d.ts +3 -3
- package/dist/codec/index.js +2 -2
- package/dist/{connection-ou9w2dSY.d.ts → connection-D_HE_YQB.d.ts} +3 -3
- package/dist/customSchemas/index.d.ts +34 -0
- package/dist/customSchemas/index.js +9 -0
- package/dist/customSchemas/index.js.map +1 -0
- package/dist/{index-ZWkoesQD.d.ts → index-DgUMnNOi.d.ts} +1 -1
- package/dist/logging/index.d.ts +3 -3
- package/dist/{message-CpXWqmJw.d.ts → message-DxS8db8A.d.ts} +30 -31
- package/dist/protobuf/index.d.ts +11 -15
- package/dist/protobuf/index.js +7 -5
- package/dist/protobuf/index.js.map +1 -1
- package/dist/router/index.d.ts +11 -11
- package/dist/router/index.js +1 -1
- package/dist/{server-BPu7Td80.d.ts → server-BfM3_JLq.d.ts} +5 -5
- package/dist/{services-cwGAC2rB.d.cts → services-CL6k3HMH.d.ts} +19 -10
- package/dist/testUtil/index.d.ts +8 -8
- package/dist/testUtil/index.js +2 -2
- package/dist/testUtil/index.js.map +1 -1
- package/dist/transport/impls/ws/client.d.ts +7 -7
- package/dist/transport/impls/ws/client.js +2 -2
- package/dist/transport/impls/ws/server.d.ts +7 -7
- package/dist/transport/impls/ws/server.js +2 -2
- package/dist/transport/impls/ws/server.js.map +1 -1
- package/dist/transport/index.d.ts +8 -8
- package/dist/transport/index.js +2 -2
- package/dist/{transport-B1MUtXL7.d.ts → transport-CUpXnch7.d.ts} +4 -4
- package/package.json +14 -39
- package/dist/adapter-D5X11kmP.d.cts +0 -29
- package/dist/chunk-75ZMPCKC.js.map +0 -1
- package/dist/chunk-ZLMQQI43.js.map +0 -1
- package/dist/client-BZUvFL6B.d.cts +0 -54
- package/dist/codec/index.cjs +0 -268
- package/dist/codec/index.cjs.map +0 -1
- package/dist/codec/index.d.cts +0 -19
- package/dist/connection-xxgJHs2o.d.cts +0 -40
- package/dist/index-BAGGleT3.d.cts +0 -37
- package/dist/logging/index.cjs +0 -55
- package/dist/logging/index.cjs.map +0 -1
- package/dist/logging/index.d.cts +0 -4
- package/dist/message-CpXWqmJw.d.cts +0 -119
- package/dist/protobuf/codec.cjs +0 -107
- package/dist/protobuf/codec.cjs.map +0 -1
- package/dist/protobuf/codec.d.cts +0 -13
- package/dist/protobuf/index.cjs +0 -1877
- package/dist/protobuf/index.cjs.map +0 -1
- package/dist/protobuf/index.d.cts +0 -488
- package/dist/router/index.cjs +0 -2035
- package/dist/router/index.cjs.map +0 -1
- package/dist/router/index.d.cts +0 -80
- package/dist/server-JdnoVO11.d.cts +0 -72
- package/dist/services-BrTFTO5Q.d.ts +0 -1125
- package/dist/testUtil/index.cjs +0 -3051
- package/dist/testUtil/index.cjs.map +0 -1
- package/dist/testUtil/index.d.cts +0 -122
- package/dist/transport/impls/ws/client.cjs +0 -2308
- package/dist/transport/impls/ws/client.cjs.map +0 -1
- package/dist/transport/impls/ws/client.d.cts +0 -33
- package/dist/transport/impls/ws/server.cjs +0 -2179
- package/dist/transport/impls/ws/server.cjs.map +0 -1
- package/dist/transport/impls/ws/server.d.cts +0 -21
- package/dist/transport/index.cjs +0 -2727
- package/dist/transport/index.cjs.map +0 -1
- package/dist/transport/index.d.cts +0 -11
- package/dist/transport-BnU3Zb0Q.d.cts +0 -590
- package/dist/types-BGGvYIJM.d.cts +0 -20
- package/dist/wslike-Dng9H1C7.d.cts +0 -40
package/dist/testUtil/index.cjs
DELETED
|
@@ -1,3051 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// testUtil/index.ts
|
|
31
|
-
var testUtil_exports = {};
|
|
32
|
-
__export(testUtil_exports, {
|
|
33
|
-
InMemoryConnection: () => InMemoryConnection,
|
|
34
|
-
closeAllConnections: () => closeAllConnections,
|
|
35
|
-
createDummyTransportMessage: () => createDummyTransportMessage,
|
|
36
|
-
createLocalWebSocketClient: () => createLocalWebSocketClient,
|
|
37
|
-
createMockTransportNetwork: () => createMockTransportNetwork,
|
|
38
|
-
createPartialContext: () => createPartialContext,
|
|
39
|
-
createWebSocketServer: () => createWebSocketServer,
|
|
40
|
-
dummySession: () => dummySession,
|
|
41
|
-
getClientSendFn: () => getClientSendFn,
|
|
42
|
-
getReadableIterator: () => getReadableIterator,
|
|
43
|
-
getServerSendFn: () => getServerSendFn,
|
|
44
|
-
getTransportConnections: () => getTransportConnections,
|
|
45
|
-
isReadableDone: () => isReadableDone,
|
|
46
|
-
numberOfConnections: () => numberOfConnections,
|
|
47
|
-
onWsServerReady: () => onWsServerReady,
|
|
48
|
-
payloadToTransportMessage: () => payloadToTransportMessage,
|
|
49
|
-
readNextResult: () => readNextResult,
|
|
50
|
-
testingClientSessionOptions: () => testingClientSessionOptions,
|
|
51
|
-
testingSessionOptions: () => testingSessionOptions,
|
|
52
|
-
waitForMessage: () => waitForMessage
|
|
53
|
-
});
|
|
54
|
-
module.exports = __toCommonJS(testUtil_exports);
|
|
55
|
-
var import_ws = __toESM(require("ws"), 1);
|
|
56
|
-
|
|
57
|
-
// transport/message.ts
|
|
58
|
-
var import_typebox = require("@sinclair/typebox");
|
|
59
|
-
|
|
60
|
-
// transport/id.ts
|
|
61
|
-
var import_nanoid = require("nanoid");
|
|
62
|
-
var alphabet = (0, import_nanoid.customAlphabet)(
|
|
63
|
-
"1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"
|
|
64
|
-
);
|
|
65
|
-
var generateId = () => alphabet(12);
|
|
66
|
-
|
|
67
|
-
// transport/message.ts
|
|
68
|
-
var TransportMessageSchema = (t) => import_typebox.Type.Object({
|
|
69
|
-
id: import_typebox.Type.String(),
|
|
70
|
-
from: import_typebox.Type.String(),
|
|
71
|
-
to: import_typebox.Type.String(),
|
|
72
|
-
seq: import_typebox.Type.Integer(),
|
|
73
|
-
ack: import_typebox.Type.Integer(),
|
|
74
|
-
serviceName: import_typebox.Type.Optional(import_typebox.Type.String()),
|
|
75
|
-
procedureName: import_typebox.Type.Optional(import_typebox.Type.String()),
|
|
76
|
-
streamId: import_typebox.Type.String(),
|
|
77
|
-
controlFlags: import_typebox.Type.Integer(),
|
|
78
|
-
tracing: import_typebox.Type.Optional(
|
|
79
|
-
import_typebox.Type.Object({
|
|
80
|
-
traceparent: import_typebox.Type.String(),
|
|
81
|
-
tracestate: import_typebox.Type.String()
|
|
82
|
-
})
|
|
83
|
-
),
|
|
84
|
-
payload: t
|
|
85
|
-
});
|
|
86
|
-
var ControlMessageAckSchema = import_typebox.Type.Object({
|
|
87
|
-
type: import_typebox.Type.Literal("ACK")
|
|
88
|
-
});
|
|
89
|
-
var ControlMessageCloseSchema = import_typebox.Type.Object({
|
|
90
|
-
type: import_typebox.Type.Literal("CLOSE")
|
|
91
|
-
});
|
|
92
|
-
var currentProtocolVersion = "v2.0";
|
|
93
|
-
var acceptedProtocolVersions = ["v1.1", currentProtocolVersion];
|
|
94
|
-
function isAcceptedProtocolVersion(version2) {
|
|
95
|
-
return acceptedProtocolVersions.includes(version2);
|
|
96
|
-
}
|
|
97
|
-
var ControlMessageHandshakeRequestSchema = import_typebox.Type.Object({
|
|
98
|
-
type: import_typebox.Type.Literal("HANDSHAKE_REQ"),
|
|
99
|
-
protocolVersion: import_typebox.Type.String(),
|
|
100
|
-
sessionId: import_typebox.Type.String(),
|
|
101
|
-
/**
|
|
102
|
-
* Specifies what the server's expected session state (from the pov of the client). This can be
|
|
103
|
-
* used by the server to know whether this is a new or a reestablished connection, and whether it
|
|
104
|
-
* is compatible with what it already has.
|
|
105
|
-
*/
|
|
106
|
-
expectedSessionState: import_typebox.Type.Object({
|
|
107
|
-
// what the client expects the server to send next
|
|
108
|
-
nextExpectedSeq: import_typebox.Type.Integer(),
|
|
109
|
-
nextSentSeq: import_typebox.Type.Integer()
|
|
110
|
-
}),
|
|
111
|
-
metadata: import_typebox.Type.Optional(import_typebox.Type.Unknown())
|
|
112
|
-
});
|
|
113
|
-
var HandshakeErrorRetriableResponseCodes = import_typebox.Type.Union([
|
|
114
|
-
import_typebox.Type.Literal("SESSION_STATE_MISMATCH")
|
|
115
|
-
]);
|
|
116
|
-
var HandshakeErrorCustomHandlerFatalResponseCodes = import_typebox.Type.Union([
|
|
117
|
-
// The custom validation handler rejected the handler because the client is unsupported.
|
|
118
|
-
import_typebox.Type.Literal("REJECTED_UNSUPPORTED_CLIENT"),
|
|
119
|
-
// The custom validation handler rejected the handshake.
|
|
120
|
-
import_typebox.Type.Literal("REJECTED_BY_CUSTOM_HANDLER")
|
|
121
|
-
]);
|
|
122
|
-
var HandshakeErrorFatalResponseCodes = import_typebox.Type.Union([
|
|
123
|
-
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
124
|
-
// The ciient sent a handshake that doesn't comply with the extended handshake metadata.
|
|
125
|
-
import_typebox.Type.Literal("MALFORMED_HANDSHAKE_META"),
|
|
126
|
-
// The ciient sent a handshake that doesn't comply with ControlMessageHandshakeRequestSchema.
|
|
127
|
-
import_typebox.Type.Literal("MALFORMED_HANDSHAKE"),
|
|
128
|
-
// The client's protocol version does not match the server's.
|
|
129
|
-
import_typebox.Type.Literal("PROTOCOL_VERSION_MISMATCH")
|
|
130
|
-
]);
|
|
131
|
-
var HandshakeErrorResponseCodes = import_typebox.Type.Union([
|
|
132
|
-
HandshakeErrorRetriableResponseCodes,
|
|
133
|
-
HandshakeErrorFatalResponseCodes
|
|
134
|
-
]);
|
|
135
|
-
var ControlMessageHandshakeResponseSchema = import_typebox.Type.Object({
|
|
136
|
-
type: import_typebox.Type.Literal("HANDSHAKE_RESP"),
|
|
137
|
-
status: import_typebox.Type.Union([
|
|
138
|
-
import_typebox.Type.Object({
|
|
139
|
-
ok: import_typebox.Type.Literal(true),
|
|
140
|
-
sessionId: import_typebox.Type.String()
|
|
141
|
-
}),
|
|
142
|
-
import_typebox.Type.Object({
|
|
143
|
-
ok: import_typebox.Type.Literal(false),
|
|
144
|
-
reason: import_typebox.Type.String(),
|
|
145
|
-
code: HandshakeErrorResponseCodes
|
|
146
|
-
})
|
|
147
|
-
])
|
|
148
|
-
});
|
|
149
|
-
var ControlMessagePayloadSchema = import_typebox.Type.Union([
|
|
150
|
-
ControlMessageCloseSchema,
|
|
151
|
-
ControlMessageAckSchema,
|
|
152
|
-
ControlMessageHandshakeRequestSchema,
|
|
153
|
-
ControlMessageHandshakeResponseSchema
|
|
154
|
-
]);
|
|
155
|
-
var OpaqueTransportMessageSchema = TransportMessageSchema(
|
|
156
|
-
import_typebox.Type.Unknown()
|
|
157
|
-
);
|
|
158
|
-
function handshakeRequestMessage({
|
|
159
|
-
from,
|
|
160
|
-
to,
|
|
161
|
-
sessionId,
|
|
162
|
-
expectedSessionState,
|
|
163
|
-
metadata,
|
|
164
|
-
tracing
|
|
165
|
-
}) {
|
|
166
|
-
return {
|
|
167
|
-
id: generateId(),
|
|
168
|
-
from,
|
|
169
|
-
to,
|
|
170
|
-
seq: 0,
|
|
171
|
-
ack: 0,
|
|
172
|
-
streamId: generateId(),
|
|
173
|
-
controlFlags: 0,
|
|
174
|
-
tracing,
|
|
175
|
-
payload: {
|
|
176
|
-
type: "HANDSHAKE_REQ",
|
|
177
|
-
protocolVersion: currentProtocolVersion,
|
|
178
|
-
sessionId,
|
|
179
|
-
expectedSessionState,
|
|
180
|
-
metadata
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
function handshakeResponseMessage({
|
|
185
|
-
from,
|
|
186
|
-
to,
|
|
187
|
-
status
|
|
188
|
-
}) {
|
|
189
|
-
return {
|
|
190
|
-
id: generateId(),
|
|
191
|
-
from,
|
|
192
|
-
to,
|
|
193
|
-
seq: 0,
|
|
194
|
-
ack: 0,
|
|
195
|
-
streamId: generateId(),
|
|
196
|
-
controlFlags: 0,
|
|
197
|
-
payload: {
|
|
198
|
-
type: "HANDSHAKE_RESP",
|
|
199
|
-
status
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
function isAck(controlFlag) {
|
|
204
|
-
return (controlFlag & 1 /* AckBit */) === 1 /* AckBit */;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// codec/json.ts
|
|
208
|
-
var encoder = new TextEncoder();
|
|
209
|
-
var decoder = new TextDecoder();
|
|
210
|
-
function uint8ArrayToBase64(uint8Array) {
|
|
211
|
-
let binary = "";
|
|
212
|
-
uint8Array.forEach((byte) => {
|
|
213
|
-
binary += String.fromCharCode(byte);
|
|
214
|
-
});
|
|
215
|
-
return btoa(binary);
|
|
216
|
-
}
|
|
217
|
-
function base64ToUint8Array(base64) {
|
|
218
|
-
const binaryString = atob(base64);
|
|
219
|
-
const uint8Array = new Uint8Array(binaryString.length);
|
|
220
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
221
|
-
uint8Array[i] = binaryString.charCodeAt(i);
|
|
222
|
-
}
|
|
223
|
-
return uint8Array;
|
|
224
|
-
}
|
|
225
|
-
var NaiveJsonCodec = {
|
|
226
|
-
toBuffer: (obj) => {
|
|
227
|
-
return encoder.encode(
|
|
228
|
-
JSON.stringify(obj, function replacer(key) {
|
|
229
|
-
const val = this[key];
|
|
230
|
-
if (val instanceof Uint8Array) {
|
|
231
|
-
return { $t: uint8ArrayToBase64(val) };
|
|
232
|
-
} else if (typeof val === "bigint") {
|
|
233
|
-
return { $b: val.toString() };
|
|
234
|
-
} else {
|
|
235
|
-
return val;
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
);
|
|
239
|
-
},
|
|
240
|
-
fromBuffer: (buff) => {
|
|
241
|
-
const parsed = JSON.parse(
|
|
242
|
-
decoder.decode(buff),
|
|
243
|
-
function reviver(_key, val) {
|
|
244
|
-
if (val?.$t !== void 0) {
|
|
245
|
-
return base64ToUint8Array(val.$t);
|
|
246
|
-
} else if (val?.$b !== void 0) {
|
|
247
|
-
return BigInt(val.$b);
|
|
248
|
-
} else {
|
|
249
|
-
return val;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
);
|
|
253
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
254
|
-
throw new Error("unpacked msg is not an object");
|
|
255
|
-
}
|
|
256
|
-
return parsed;
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// transport/options.ts
|
|
261
|
-
var defaultTransportOptions = {
|
|
262
|
-
heartbeatIntervalMs: 1e3,
|
|
263
|
-
heartbeatsUntilDead: 2,
|
|
264
|
-
sessionDisconnectGraceMs: 5e3,
|
|
265
|
-
connectionTimeoutMs: 2e3,
|
|
266
|
-
handshakeTimeoutMs: 1e3,
|
|
267
|
-
enableTransparentSessionReconnects: true,
|
|
268
|
-
codec: NaiveJsonCodec
|
|
269
|
-
};
|
|
270
|
-
var defaultConnectionRetryOptions = {
|
|
271
|
-
baseIntervalMs: 150,
|
|
272
|
-
maxJitterMs: 200,
|
|
273
|
-
maxBackoffMs: 32e3,
|
|
274
|
-
attemptBudgetCapacity: 5,
|
|
275
|
-
budgetRestoreIntervalMs: 200,
|
|
276
|
-
isFatalConnectionError: () => false
|
|
277
|
-
};
|
|
278
|
-
var defaultClientTransportOptions = {
|
|
279
|
-
...defaultTransportOptions,
|
|
280
|
-
...defaultConnectionRetryOptions
|
|
281
|
-
};
|
|
282
|
-
var defaultServerTransportOptions = {
|
|
283
|
-
...defaultTransportOptions
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
// transport/sessionStateMachine/common.ts
|
|
287
|
-
var ERR_CONSUMED = `session state has been consumed and is no longer valid`;
|
|
288
|
-
var StateMachineState = class {
|
|
289
|
-
/*
|
|
290
|
-
* Whether this state has been consumed
|
|
291
|
-
* and we've moved on to another state
|
|
292
|
-
*/
|
|
293
|
-
_isConsumed;
|
|
294
|
-
/**
|
|
295
|
-
* Cleanup this state machine state and mark it as consumed.
|
|
296
|
-
* After calling close, it is an error to access any properties on the state.
|
|
297
|
-
* You should never need to call this as a consumer.
|
|
298
|
-
*
|
|
299
|
-
* If you're looking to close the session from the client,
|
|
300
|
-
* use `.hardDisconnect` on the client transport.
|
|
301
|
-
*/
|
|
302
|
-
close() {
|
|
303
|
-
this._handleClose();
|
|
304
|
-
}
|
|
305
|
-
constructor() {
|
|
306
|
-
this._isConsumed = false;
|
|
307
|
-
return new Proxy(this, {
|
|
308
|
-
get(target, prop) {
|
|
309
|
-
if (prop === "_isConsumed" || prop === "id" || prop === "state") {
|
|
310
|
-
return Reflect.get(target, prop);
|
|
311
|
-
}
|
|
312
|
-
if (prop === "_handleStateExit") {
|
|
313
|
-
return () => {
|
|
314
|
-
target._isConsumed = true;
|
|
315
|
-
target._handleStateExit();
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
if (prop === "_handleClose") {
|
|
319
|
-
return () => {
|
|
320
|
-
target._isConsumed = true;
|
|
321
|
-
target._handleStateExit();
|
|
322
|
-
target._handleClose();
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
if (target._isConsumed) {
|
|
326
|
-
throw new Error(
|
|
327
|
-
`${ERR_CONSUMED}: getting ${prop.toString()} on consumed state`
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
return Reflect.get(target, prop);
|
|
331
|
-
},
|
|
332
|
-
set(target, prop, value) {
|
|
333
|
-
if (target._isConsumed) {
|
|
334
|
-
throw new Error(
|
|
335
|
-
`${ERR_CONSUMED}: setting ${prop.toString()} on consumed state`
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
return Reflect.set(target, prop, value);
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
var CommonSession = class extends StateMachineState {
|
|
344
|
-
from;
|
|
345
|
-
options;
|
|
346
|
-
codec;
|
|
347
|
-
tracer;
|
|
348
|
-
log;
|
|
349
|
-
constructor({ from, options, log, tracer, codec }) {
|
|
350
|
-
super();
|
|
351
|
-
this.from = from;
|
|
352
|
-
this.options = options;
|
|
353
|
-
this.log = log;
|
|
354
|
-
this.tracer = tracer;
|
|
355
|
-
this.codec = codec;
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
var IdentifiedSession = class extends CommonSession {
|
|
359
|
-
id;
|
|
360
|
-
telemetry;
|
|
361
|
-
to;
|
|
362
|
-
protocolVersion;
|
|
363
|
-
listeners;
|
|
364
|
-
/**
|
|
365
|
-
* Index of the message we will send next (excluding handshake)
|
|
366
|
-
*/
|
|
367
|
-
seq;
|
|
368
|
-
/**
|
|
369
|
-
* Last seq we sent over the wire this session (excluding handshake) and retransmissions
|
|
370
|
-
*/
|
|
371
|
-
seqSent;
|
|
372
|
-
/**
|
|
373
|
-
* Number of unique messages we've received this session (excluding handshake)
|
|
374
|
-
*/
|
|
375
|
-
ack;
|
|
376
|
-
sendBuffer;
|
|
377
|
-
constructor(props) {
|
|
378
|
-
const {
|
|
379
|
-
id,
|
|
380
|
-
to,
|
|
381
|
-
seq,
|
|
382
|
-
ack,
|
|
383
|
-
sendBuffer,
|
|
384
|
-
telemetry,
|
|
385
|
-
log,
|
|
386
|
-
protocolVersion,
|
|
387
|
-
seqSent: messagesSent,
|
|
388
|
-
listeners
|
|
389
|
-
} = props;
|
|
390
|
-
super(props);
|
|
391
|
-
this.id = id;
|
|
392
|
-
this.to = to;
|
|
393
|
-
this.seq = seq;
|
|
394
|
-
this.ack = ack;
|
|
395
|
-
this.sendBuffer = sendBuffer;
|
|
396
|
-
this.telemetry = telemetry;
|
|
397
|
-
this.log = log;
|
|
398
|
-
this.protocolVersion = protocolVersion;
|
|
399
|
-
this.seqSent = messagesSent;
|
|
400
|
-
this.listeners = listeners;
|
|
401
|
-
}
|
|
402
|
-
get loggingMetadata() {
|
|
403
|
-
const metadata = {
|
|
404
|
-
clientId: this.from,
|
|
405
|
-
connectedTo: this.to,
|
|
406
|
-
sessionId: this.id
|
|
407
|
-
};
|
|
408
|
-
if (this.telemetry.span.isRecording()) {
|
|
409
|
-
const spanContext = this.telemetry.span.spanContext();
|
|
410
|
-
metadata.telemetry = {
|
|
411
|
-
traceId: spanContext.traceId,
|
|
412
|
-
spanId: spanContext.spanId
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
return metadata;
|
|
416
|
-
}
|
|
417
|
-
encodeMsg(partialMsg) {
|
|
418
|
-
const msg = {
|
|
419
|
-
...partialMsg,
|
|
420
|
-
id: generateId(),
|
|
421
|
-
to: this.to,
|
|
422
|
-
from: this.from,
|
|
423
|
-
seq: this.seq,
|
|
424
|
-
ack: this.ack
|
|
425
|
-
};
|
|
426
|
-
const encoded = this.codec.toBuffer(msg);
|
|
427
|
-
if (!encoded.ok) {
|
|
428
|
-
this.listeners.onMessageSendFailure(
|
|
429
|
-
{ ...partialMsg, seq: this.seq },
|
|
430
|
-
encoded.reason
|
|
431
|
-
);
|
|
432
|
-
return encoded;
|
|
433
|
-
}
|
|
434
|
-
this.seq++;
|
|
435
|
-
return {
|
|
436
|
-
ok: true,
|
|
437
|
-
value: {
|
|
438
|
-
id: msg.id,
|
|
439
|
-
seq: msg.seq,
|
|
440
|
-
msg: partialMsg,
|
|
441
|
-
data: encoded.value
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
nextSeq() {
|
|
446
|
-
return this.sendBuffer.length > 0 ? this.sendBuffer[0].seq : this.seq;
|
|
447
|
-
}
|
|
448
|
-
send(msg) {
|
|
449
|
-
const encodeResult = this.encodeMsg(msg);
|
|
450
|
-
if (!encodeResult.ok) {
|
|
451
|
-
return encodeResult;
|
|
452
|
-
}
|
|
453
|
-
this.sendBuffer.push(encodeResult.value);
|
|
454
|
-
return {
|
|
455
|
-
ok: true,
|
|
456
|
-
value: encodeResult.value.id
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
_handleStateExit() {
|
|
460
|
-
}
|
|
461
|
-
_handleClose() {
|
|
462
|
-
this.sendBuffer.length = 0;
|
|
463
|
-
this.telemetry.span.end();
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
|
|
467
|
-
graceExpiryTime;
|
|
468
|
-
gracePeriodTimeout;
|
|
469
|
-
listeners;
|
|
470
|
-
constructor(props) {
|
|
471
|
-
super(props);
|
|
472
|
-
this.listeners = props.listeners;
|
|
473
|
-
this.graceExpiryTime = props.graceExpiryTime;
|
|
474
|
-
this.gracePeriodTimeout = setTimeout(() => {
|
|
475
|
-
this.listeners.onSessionGracePeriodElapsed();
|
|
476
|
-
}, this.graceExpiryTime - Date.now());
|
|
477
|
-
}
|
|
478
|
-
_handleStateExit() {
|
|
479
|
-
super._handleStateExit();
|
|
480
|
-
if (this.gracePeriodTimeout) {
|
|
481
|
-
clearTimeout(this.gracePeriodTimeout);
|
|
482
|
-
this.gracePeriodTimeout = void 0;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
_handleClose() {
|
|
486
|
-
super._handleClose();
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
|
|
490
|
-
// transport/sessionStateMachine/SessionConnecting.ts
|
|
491
|
-
var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
|
|
492
|
-
state = "Connecting" /* Connecting */;
|
|
493
|
-
connPromise;
|
|
494
|
-
listeners;
|
|
495
|
-
connectionTimeout;
|
|
496
|
-
constructor(props) {
|
|
497
|
-
super(props);
|
|
498
|
-
this.connPromise = props.connPromise;
|
|
499
|
-
this.listeners = props.listeners;
|
|
500
|
-
this.connPromise.then(
|
|
501
|
-
(conn) => {
|
|
502
|
-
if (this._isConsumed) return;
|
|
503
|
-
this.listeners.onConnectionEstablished(conn);
|
|
504
|
-
},
|
|
505
|
-
(err) => {
|
|
506
|
-
if (this._isConsumed) return;
|
|
507
|
-
this.listeners.onConnectionFailed(err);
|
|
508
|
-
}
|
|
509
|
-
);
|
|
510
|
-
this.connectionTimeout = setTimeout(() => {
|
|
511
|
-
this.listeners.onConnectionTimeout();
|
|
512
|
-
}, this.options.connectionTimeoutMs);
|
|
513
|
-
}
|
|
514
|
-
// close a pending connection if it resolves, ignore errors if the promise
|
|
515
|
-
// ends up rejected anyways
|
|
516
|
-
bestEffortClose() {
|
|
517
|
-
const logger = this.log;
|
|
518
|
-
const metadata = this.loggingMetadata;
|
|
519
|
-
this.connPromise.then((conn) => {
|
|
520
|
-
conn.close();
|
|
521
|
-
logger?.info(
|
|
522
|
-
"connection eventually resolved but session has transitioned, closed connection",
|
|
523
|
-
{
|
|
524
|
-
...metadata,
|
|
525
|
-
...conn.loggingMetadata
|
|
526
|
-
}
|
|
527
|
-
);
|
|
528
|
-
}).catch(() => {
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
_handleStateExit() {
|
|
532
|
-
super._handleStateExit();
|
|
533
|
-
if (this.connectionTimeout) {
|
|
534
|
-
clearTimeout(this.connectionTimeout);
|
|
535
|
-
this.connectionTimeout = void 0;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
_handleClose() {
|
|
539
|
-
super._handleClose();
|
|
540
|
-
this.bestEffortClose();
|
|
541
|
-
}
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
// transport/sessionStateMachine/SessionNoConnection.ts
|
|
545
|
-
var SessionNoConnection = class extends IdentifiedSessionWithGracePeriod {
|
|
546
|
-
state = "NoConnection" /* NoConnection */;
|
|
547
|
-
_handleClose() {
|
|
548
|
-
super._handleClose();
|
|
549
|
-
}
|
|
550
|
-
_handleStateExit() {
|
|
551
|
-
super._handleStateExit();
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
// tracing/index.ts
|
|
556
|
-
var import_api = require("@opentelemetry/api");
|
|
557
|
-
|
|
558
|
-
// transport/stringifyError.ts
|
|
559
|
-
function coerceErrorString(err) {
|
|
560
|
-
if (err instanceof Error) {
|
|
561
|
-
return err.message || "unknown reason";
|
|
562
|
-
}
|
|
563
|
-
return `[coerced to error] ${String(err)}`;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// package.json
|
|
567
|
-
var version = "0.216.0";
|
|
568
|
-
|
|
569
|
-
// tracing/index.ts
|
|
570
|
-
function getPropagationContext(ctx) {
|
|
571
|
-
const tracing = {
|
|
572
|
-
traceparent: "",
|
|
573
|
-
tracestate: ""
|
|
574
|
-
};
|
|
575
|
-
import_api.propagation.inject(ctx, tracing);
|
|
576
|
-
return tracing;
|
|
577
|
-
}
|
|
578
|
-
function createSessionTelemetryInfo(tracer, sessionId, to, from, propagationCtx) {
|
|
579
|
-
const parentCtx = propagationCtx ? import_api.propagation.extract(import_api.context.active(), propagationCtx) : import_api.context.active();
|
|
580
|
-
const span = tracer.startSpan(
|
|
581
|
-
`river.session`,
|
|
582
|
-
{
|
|
583
|
-
attributes: {
|
|
584
|
-
component: "river",
|
|
585
|
-
"river.session.id": sessionId,
|
|
586
|
-
"river.session.to": to,
|
|
587
|
-
"river.session.from": from
|
|
588
|
-
}
|
|
589
|
-
},
|
|
590
|
-
parentCtx
|
|
591
|
-
);
|
|
592
|
-
const ctx = import_api.trace.setSpan(parentCtx, span);
|
|
593
|
-
return { span, ctx };
|
|
594
|
-
}
|
|
595
|
-
function createConnectionTelemetryInfo(tracer, connection, info) {
|
|
596
|
-
const span = tracer.startSpan(
|
|
597
|
-
`river.connection`,
|
|
598
|
-
{
|
|
599
|
-
attributes: {
|
|
600
|
-
component: "river",
|
|
601
|
-
"river.connection.id": connection.id
|
|
602
|
-
},
|
|
603
|
-
links: [{ context: info.span.spanContext() }]
|
|
604
|
-
},
|
|
605
|
-
info.ctx
|
|
606
|
-
);
|
|
607
|
-
const ctx = import_api.trace.setSpan(info.ctx, span);
|
|
608
|
-
return { span, ctx };
|
|
609
|
-
}
|
|
610
|
-
function getTracer() {
|
|
611
|
-
return import_api.trace.getTracer("river", version);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// transport/sessionStateMachine/SessionWaitingForHandshake.ts
|
|
615
|
-
var SessionWaitingForHandshake = class extends CommonSession {
|
|
616
|
-
state = "WaitingForHandshake" /* WaitingForHandshake */;
|
|
617
|
-
conn;
|
|
618
|
-
listeners;
|
|
619
|
-
handshakeTimeout;
|
|
620
|
-
constructor(props) {
|
|
621
|
-
super(props);
|
|
622
|
-
this.conn = props.conn;
|
|
623
|
-
this.listeners = props.listeners;
|
|
624
|
-
this.handshakeTimeout = setTimeout(() => {
|
|
625
|
-
this.listeners.onHandshakeTimeout();
|
|
626
|
-
}, this.options.handshakeTimeoutMs);
|
|
627
|
-
this.conn.setDataListener(this.onHandshakeData);
|
|
628
|
-
this.conn.setErrorListener(this.listeners.onConnectionErrored);
|
|
629
|
-
this.conn.setCloseListener(this.listeners.onConnectionClosed);
|
|
630
|
-
}
|
|
631
|
-
get loggingMetadata() {
|
|
632
|
-
return {
|
|
633
|
-
clientId: this.from,
|
|
634
|
-
connId: this.conn.id,
|
|
635
|
-
...this.conn.loggingMetadata
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
onHandshakeData = (msg) => {
|
|
639
|
-
const parsedMsgRes = this.codec.fromBuffer(msg);
|
|
640
|
-
if (!parsedMsgRes.ok) {
|
|
641
|
-
this.listeners.onInvalidHandshake(
|
|
642
|
-
`could not parse handshake message: ${parsedMsgRes.reason}`,
|
|
643
|
-
"MALFORMED_HANDSHAKE"
|
|
644
|
-
);
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
this.listeners.onHandshake(parsedMsgRes.value);
|
|
648
|
-
};
|
|
649
|
-
sendHandshake(msg) {
|
|
650
|
-
const buff = this.codec.toBuffer(msg);
|
|
651
|
-
if (!buff.ok) {
|
|
652
|
-
return buff;
|
|
653
|
-
}
|
|
654
|
-
const sent = this.conn.send(buff.value);
|
|
655
|
-
if (!sent) {
|
|
656
|
-
return {
|
|
657
|
-
ok: false,
|
|
658
|
-
reason: "failed to send handshake"
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
return {
|
|
662
|
-
ok: true,
|
|
663
|
-
value: msg.id
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
_handleStateExit() {
|
|
667
|
-
this.conn.removeDataListener();
|
|
668
|
-
this.conn.removeErrorListener();
|
|
669
|
-
this.conn.removeCloseListener();
|
|
670
|
-
clearTimeout(this.handshakeTimeout);
|
|
671
|
-
this.handshakeTimeout = void 0;
|
|
672
|
-
}
|
|
673
|
-
_handleClose() {
|
|
674
|
-
this.conn.close();
|
|
675
|
-
}
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
// transport/sessionStateMachine/SessionHandshaking.ts
|
|
679
|
-
var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
|
|
680
|
-
state = "Handshaking" /* Handshaking */;
|
|
681
|
-
conn;
|
|
682
|
-
listeners;
|
|
683
|
-
handshakeTimeout;
|
|
684
|
-
constructor(props) {
|
|
685
|
-
super(props);
|
|
686
|
-
this.conn = props.conn;
|
|
687
|
-
this.listeners = props.listeners;
|
|
688
|
-
this.handshakeTimeout = setTimeout(() => {
|
|
689
|
-
this.listeners.onHandshakeTimeout();
|
|
690
|
-
}, this.options.handshakeTimeoutMs);
|
|
691
|
-
this.conn.setDataListener(this.onHandshakeData);
|
|
692
|
-
this.conn.setErrorListener(this.listeners.onConnectionErrored);
|
|
693
|
-
this.conn.setCloseListener(this.listeners.onConnectionClosed);
|
|
694
|
-
}
|
|
695
|
-
get loggingMetadata() {
|
|
696
|
-
return {
|
|
697
|
-
...super.loggingMetadata,
|
|
698
|
-
...this.conn.loggingMetadata
|
|
699
|
-
};
|
|
700
|
-
}
|
|
701
|
-
onHandshakeData = (msg) => {
|
|
702
|
-
const parsedMsgRes = this.codec.fromBuffer(msg);
|
|
703
|
-
if (!parsedMsgRes.ok) {
|
|
704
|
-
this.listeners.onInvalidHandshake(
|
|
705
|
-
`could not parse handshake message: ${parsedMsgRes.reason}`,
|
|
706
|
-
"MALFORMED_HANDSHAKE"
|
|
707
|
-
);
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
this.listeners.onHandshake(parsedMsgRes.value);
|
|
711
|
-
};
|
|
712
|
-
sendHandshake(msg) {
|
|
713
|
-
const buff = this.codec.toBuffer(msg);
|
|
714
|
-
if (!buff.ok) {
|
|
715
|
-
return buff;
|
|
716
|
-
}
|
|
717
|
-
const sent = this.conn.send(buff.value);
|
|
718
|
-
if (!sent) {
|
|
719
|
-
return {
|
|
720
|
-
ok: false,
|
|
721
|
-
reason: "failed to send handshake"
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
return {
|
|
725
|
-
ok: true,
|
|
726
|
-
value: msg.id
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
_handleStateExit() {
|
|
730
|
-
super._handleStateExit();
|
|
731
|
-
this.conn.removeDataListener();
|
|
732
|
-
this.conn.removeErrorListener();
|
|
733
|
-
this.conn.removeCloseListener();
|
|
734
|
-
if (this.handshakeTimeout) {
|
|
735
|
-
clearTimeout(this.handshakeTimeout);
|
|
736
|
-
this.handshakeTimeout = void 0;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
_handleClose() {
|
|
740
|
-
super._handleClose();
|
|
741
|
-
this.conn.close();
|
|
742
|
-
}
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
// transport/sessionStateMachine/SessionConnected.ts
|
|
746
|
-
var import_api2 = require("@opentelemetry/api");
|
|
747
|
-
var SessionConnected = class extends IdentifiedSession {
|
|
748
|
-
state = "Connected" /* Connected */;
|
|
749
|
-
conn;
|
|
750
|
-
listeners;
|
|
751
|
-
heartbeatHandle;
|
|
752
|
-
heartbeatMissTimeout;
|
|
753
|
-
isActivelyHeartbeating = false;
|
|
754
|
-
updateBookkeeping(ack, seq) {
|
|
755
|
-
this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
|
|
756
|
-
this.ack = seq + 1;
|
|
757
|
-
if (this.heartbeatMissTimeout) {
|
|
758
|
-
clearTimeout(this.heartbeatMissTimeout);
|
|
759
|
-
}
|
|
760
|
-
this.startMissingHeartbeatTimeout();
|
|
761
|
-
}
|
|
762
|
-
assertSendOrdering(encodedMsg) {
|
|
763
|
-
if (encodedMsg.seq > this.seqSent + 1) {
|
|
764
|
-
const msg = `invariant violation: would have sent out of order msg (seq: ${encodedMsg.seq}, expected: ${this.seqSent} + 1)`;
|
|
765
|
-
this.log?.error(msg, {
|
|
766
|
-
...this.loggingMetadata,
|
|
767
|
-
tags: ["invariant-violation"]
|
|
768
|
-
});
|
|
769
|
-
throw new Error(msg);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
send(msg) {
|
|
773
|
-
const encodeResult = this.encodeMsg(msg);
|
|
774
|
-
if (!encodeResult.ok) {
|
|
775
|
-
return encodeResult;
|
|
776
|
-
}
|
|
777
|
-
const encodedMsg = encodeResult.value;
|
|
778
|
-
this.assertSendOrdering(encodedMsg);
|
|
779
|
-
this.sendBuffer.push(encodedMsg);
|
|
780
|
-
const sent = this.conn.send(encodedMsg.data);
|
|
781
|
-
if (!sent) {
|
|
782
|
-
const reason = "failed to send message";
|
|
783
|
-
this.listeners.onMessageSendFailure(
|
|
784
|
-
{ ...encodedMsg.msg, seq: encodedMsg.seq },
|
|
785
|
-
reason
|
|
786
|
-
);
|
|
787
|
-
return { ok: false, reason };
|
|
788
|
-
}
|
|
789
|
-
this.seqSent = encodedMsg.seq;
|
|
790
|
-
return { ok: true, value: encodedMsg.id };
|
|
791
|
-
}
|
|
792
|
-
constructor(props) {
|
|
793
|
-
super(props);
|
|
794
|
-
this.conn = props.conn;
|
|
795
|
-
this.listeners = props.listeners;
|
|
796
|
-
this.conn.setDataListener(this.onMessageData);
|
|
797
|
-
this.conn.setCloseListener(this.listeners.onConnectionClosed);
|
|
798
|
-
this.conn.setErrorListener(this.listeners.onConnectionErrored);
|
|
799
|
-
}
|
|
800
|
-
sendBufferedMessages() {
|
|
801
|
-
if (this.sendBuffer.length > 0) {
|
|
802
|
-
this.log?.info(
|
|
803
|
-
`sending ${this.sendBuffer.length} buffered messages, starting at seq ${this.nextSeq()}`,
|
|
804
|
-
this.loggingMetadata
|
|
805
|
-
);
|
|
806
|
-
for (const msg of this.sendBuffer) {
|
|
807
|
-
this.assertSendOrdering(msg);
|
|
808
|
-
const sent = this.conn.send(msg.data);
|
|
809
|
-
if (!sent) {
|
|
810
|
-
const reason = "failed to send buffered message";
|
|
811
|
-
this.listeners.onMessageSendFailure(
|
|
812
|
-
{ ...msg.msg, seq: msg.seq },
|
|
813
|
-
reason
|
|
814
|
-
);
|
|
815
|
-
return { ok: false, reason };
|
|
816
|
-
}
|
|
817
|
-
this.seqSent = msg.seq;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
return { ok: true, value: void 0 };
|
|
821
|
-
}
|
|
822
|
-
get loggingMetadata() {
|
|
823
|
-
return {
|
|
824
|
-
...super.loggingMetadata,
|
|
825
|
-
...this.conn.loggingMetadata
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
startMissingHeartbeatTimeout() {
|
|
829
|
-
const maxMisses = this.options.heartbeatsUntilDead;
|
|
830
|
-
const missDuration = maxMisses * this.options.heartbeatIntervalMs;
|
|
831
|
-
this.heartbeatMissTimeout = setTimeout(() => {
|
|
832
|
-
this.log?.info(
|
|
833
|
-
`closing connection to ${this.to} due to inactivity (missed ${maxMisses} heartbeats which is ${missDuration}ms)`,
|
|
834
|
-
this.loggingMetadata
|
|
835
|
-
);
|
|
836
|
-
this.telemetry.span.addEvent(
|
|
837
|
-
"closing connection due to missing heartbeat"
|
|
838
|
-
);
|
|
839
|
-
this.conn.close();
|
|
840
|
-
}, missDuration);
|
|
841
|
-
}
|
|
842
|
-
startActiveHeartbeat() {
|
|
843
|
-
this.isActivelyHeartbeating = true;
|
|
844
|
-
this.heartbeatHandle = setInterval(() => {
|
|
845
|
-
this.sendHeartbeat();
|
|
846
|
-
}, this.options.heartbeatIntervalMs);
|
|
847
|
-
}
|
|
848
|
-
sendHeartbeat() {
|
|
849
|
-
this.log?.debug("sending heartbeat", this.loggingMetadata);
|
|
850
|
-
const heartbeat = {
|
|
851
|
-
streamId: "heartbeat",
|
|
852
|
-
controlFlags: 1 /* AckBit */,
|
|
853
|
-
payload: {
|
|
854
|
-
type: "ACK"
|
|
855
|
-
}
|
|
856
|
-
};
|
|
857
|
-
this.send(heartbeat);
|
|
858
|
-
}
|
|
859
|
-
onMessageData = (msg) => {
|
|
860
|
-
const parsedMsgRes = this.codec.fromBuffer(msg);
|
|
861
|
-
if (!parsedMsgRes.ok) {
|
|
862
|
-
this.listeners.onInvalidMessage(
|
|
863
|
-
`could not parse message: ${parsedMsgRes.reason}`
|
|
864
|
-
);
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
const parsedMsg = parsedMsgRes.value;
|
|
868
|
-
if (parsedMsg.seq !== this.ack) {
|
|
869
|
-
if (parsedMsg.seq < this.ack) {
|
|
870
|
-
this.log?.debug(
|
|
871
|
-
`received duplicate msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack}), discarding`,
|
|
872
|
-
{
|
|
873
|
-
...this.loggingMetadata,
|
|
874
|
-
transportMessage: parsedMsg
|
|
875
|
-
}
|
|
876
|
-
);
|
|
877
|
-
} else {
|
|
878
|
-
const reason = `received out-of-order msg, closing connection (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;
|
|
879
|
-
this.log?.error(reason, {
|
|
880
|
-
...this.loggingMetadata,
|
|
881
|
-
transportMessage: parsedMsg,
|
|
882
|
-
tags: ["invariant-violation"]
|
|
883
|
-
});
|
|
884
|
-
this.telemetry.span.setStatus({
|
|
885
|
-
code: import_api2.SpanStatusCode.ERROR,
|
|
886
|
-
message: reason
|
|
887
|
-
});
|
|
888
|
-
this.conn.close();
|
|
889
|
-
}
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
this.log?.debug(`received msg`, {
|
|
893
|
-
...this.loggingMetadata,
|
|
894
|
-
transportMessage: parsedMsg
|
|
895
|
-
});
|
|
896
|
-
this.updateBookkeeping(parsedMsg.ack, parsedMsg.seq);
|
|
897
|
-
if (!isAck(parsedMsg.controlFlags)) {
|
|
898
|
-
this.listeners.onMessage(parsedMsg);
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
this.log?.debug(`discarding msg (ack bit set)`, {
|
|
902
|
-
...this.loggingMetadata,
|
|
903
|
-
transportMessage: parsedMsg
|
|
904
|
-
});
|
|
905
|
-
if (!this.isActivelyHeartbeating) {
|
|
906
|
-
this.sendHeartbeat();
|
|
907
|
-
}
|
|
908
|
-
};
|
|
909
|
-
_handleStateExit() {
|
|
910
|
-
super._handleStateExit();
|
|
911
|
-
this.conn.removeDataListener();
|
|
912
|
-
this.conn.removeCloseListener();
|
|
913
|
-
this.conn.removeErrorListener();
|
|
914
|
-
if (this.heartbeatHandle) {
|
|
915
|
-
clearInterval(this.heartbeatHandle);
|
|
916
|
-
this.heartbeatHandle = void 0;
|
|
917
|
-
}
|
|
918
|
-
if (this.heartbeatMissTimeout) {
|
|
919
|
-
clearTimeout(this.heartbeatMissTimeout);
|
|
920
|
-
this.heartbeatMissTimeout = void 0;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
_handleClose() {
|
|
924
|
-
super._handleClose();
|
|
925
|
-
this.conn.close();
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
|
|
929
|
-
// transport/sessionStateMachine/SessionBackingOff.ts
|
|
930
|
-
var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
|
|
931
|
-
state = "BackingOff" /* BackingOff */;
|
|
932
|
-
listeners;
|
|
933
|
-
backoffTimeout;
|
|
934
|
-
constructor(props) {
|
|
935
|
-
super(props);
|
|
936
|
-
this.listeners = props.listeners;
|
|
937
|
-
this.backoffTimeout = setTimeout(() => {
|
|
938
|
-
this.listeners.onBackoffFinished();
|
|
939
|
-
}, props.backoffMs);
|
|
940
|
-
}
|
|
941
|
-
_handleClose() {
|
|
942
|
-
super._handleClose();
|
|
943
|
-
}
|
|
944
|
-
_handleStateExit() {
|
|
945
|
-
super._handleStateExit();
|
|
946
|
-
if (this.backoffTimeout) {
|
|
947
|
-
clearTimeout(this.backoffTimeout);
|
|
948
|
-
this.backoffTimeout = void 0;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
};
|
|
952
|
-
|
|
953
|
-
// codec/adapter.ts
|
|
954
|
-
var import_value3 = require("@sinclair/typebox/value");
|
|
955
|
-
|
|
956
|
-
// logging/log.ts
|
|
957
|
-
var import_api3 = require("@opentelemetry/api");
|
|
958
|
-
var LoggingLevels = {
|
|
959
|
-
debug: -1,
|
|
960
|
-
info: 0,
|
|
961
|
-
warn: 1,
|
|
962
|
-
error: 2
|
|
963
|
-
};
|
|
964
|
-
var cleanedLogFn = (log) => {
|
|
965
|
-
return (msg, metadata) => {
|
|
966
|
-
if (metadata && !metadata.telemetry) {
|
|
967
|
-
const span = import_api3.trace.getSpan(import_api3.context.active());
|
|
968
|
-
if (span) {
|
|
969
|
-
metadata.telemetry = {
|
|
970
|
-
traceId: span.spanContext().traceId,
|
|
971
|
-
spanId: span.spanContext().spanId
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
if (!metadata?.transportMessage) {
|
|
976
|
-
log(msg, metadata);
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
const { payload, ...rest } = metadata.transportMessage;
|
|
980
|
-
metadata.transportMessage = rest;
|
|
981
|
-
log(msg, metadata);
|
|
982
|
-
};
|
|
983
|
-
};
|
|
984
|
-
var BaseLogger = class {
|
|
985
|
-
minLevel;
|
|
986
|
-
output;
|
|
987
|
-
constructor(output, minLevel = "info") {
|
|
988
|
-
this.minLevel = minLevel;
|
|
989
|
-
this.output = output;
|
|
990
|
-
}
|
|
991
|
-
debug(msg, metadata) {
|
|
992
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {
|
|
993
|
-
this.output(msg, metadata ?? {}, "debug");
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
info(msg, metadata) {
|
|
997
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {
|
|
998
|
-
this.output(msg, metadata ?? {}, "info");
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
warn(msg, metadata) {
|
|
1002
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {
|
|
1003
|
-
this.output(msg, metadata ?? {}, "warn");
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
error(msg, metadata) {
|
|
1007
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {
|
|
1008
|
-
this.output(msg, metadata ?? {}, "error");
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
};
|
|
1012
|
-
var createLogProxy = (log) => ({
|
|
1013
|
-
debug: cleanedLogFn(log.debug.bind(log)),
|
|
1014
|
-
info: cleanedLogFn(log.info.bind(log)),
|
|
1015
|
-
warn: cleanedLogFn(log.warn.bind(log)),
|
|
1016
|
-
error: cleanedLogFn(log.error.bind(log))
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
// transport/events.ts
|
|
1020
|
-
var ProtocolError = {
|
|
1021
|
-
RetriesExceeded: "conn_retry_exceeded",
|
|
1022
|
-
HandshakeFailed: "handshake_failed",
|
|
1023
|
-
MessageOrderingViolated: "message_ordering_violated",
|
|
1024
|
-
InvalidMessage: "invalid_message",
|
|
1025
|
-
MessageSendFailure: "message_send_failure"
|
|
1026
|
-
};
|
|
1027
|
-
var EventDispatcher = class {
|
|
1028
|
-
eventListeners = {};
|
|
1029
|
-
removeAllListeners() {
|
|
1030
|
-
this.eventListeners = {};
|
|
1031
|
-
}
|
|
1032
|
-
numberOfListeners(eventType) {
|
|
1033
|
-
return this.eventListeners[eventType]?.size ?? 0;
|
|
1034
|
-
}
|
|
1035
|
-
addEventListener(eventType, handler) {
|
|
1036
|
-
if (!this.eventListeners[eventType]) {
|
|
1037
|
-
this.eventListeners[eventType] = /* @__PURE__ */ new Set();
|
|
1038
|
-
}
|
|
1039
|
-
this.eventListeners[eventType]?.add(handler);
|
|
1040
|
-
}
|
|
1041
|
-
removeEventListener(eventType, handler) {
|
|
1042
|
-
const handlers = this.eventListeners[eventType];
|
|
1043
|
-
if (handlers) {
|
|
1044
|
-
this.eventListeners[eventType]?.delete(handler);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
dispatchEvent(eventType, event) {
|
|
1048
|
-
const handlers = this.eventListeners[eventType];
|
|
1049
|
-
if (handlers) {
|
|
1050
|
-
const copy = [...handlers];
|
|
1051
|
-
for (const handler of copy) {
|
|
1052
|
-
handler(event);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
};
|
|
1057
|
-
|
|
1058
|
-
// transport/transport.ts
|
|
1059
|
-
var Transport = class {
|
|
1060
|
-
/**
|
|
1061
|
-
* The status of the transport.
|
|
1062
|
-
*/
|
|
1063
|
-
status;
|
|
1064
|
-
/**
|
|
1065
|
-
* The client ID of this transport.
|
|
1066
|
-
*/
|
|
1067
|
-
clientId;
|
|
1068
|
-
/**
|
|
1069
|
-
* The event dispatcher for handling events of type EventTypes.
|
|
1070
|
-
*/
|
|
1071
|
-
eventDispatcher;
|
|
1072
|
-
/**
|
|
1073
|
-
* The options for this transport.
|
|
1074
|
-
*/
|
|
1075
|
-
options;
|
|
1076
|
-
log;
|
|
1077
|
-
tracer;
|
|
1078
|
-
sessions;
|
|
1079
|
-
/**
|
|
1080
|
-
* Creates a new Transport instance.
|
|
1081
|
-
* @param codec The codec used to encode and decode messages.
|
|
1082
|
-
* @param clientId The client ID of this transport.
|
|
1083
|
-
*/
|
|
1084
|
-
constructor(clientId, providedOptions) {
|
|
1085
|
-
this.options = { ...defaultTransportOptions, ...providedOptions };
|
|
1086
|
-
this.eventDispatcher = new EventDispatcher();
|
|
1087
|
-
this.clientId = clientId;
|
|
1088
|
-
this.status = "open";
|
|
1089
|
-
this.sessions = /* @__PURE__ */ new Map();
|
|
1090
|
-
this.tracer = getTracer();
|
|
1091
|
-
}
|
|
1092
|
-
bindLogger(fn, level) {
|
|
1093
|
-
if (typeof fn === "function") {
|
|
1094
|
-
this.log = createLogProxy(new BaseLogger(fn, level));
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
this.log = createLogProxy(fn);
|
|
1098
|
-
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Called when a message is received by this transport.
|
|
1101
|
-
* You generally shouldn't need to override this in downstream transport implementations.
|
|
1102
|
-
* @param message The received message.
|
|
1103
|
-
*/
|
|
1104
|
-
handleMsg(message) {
|
|
1105
|
-
if (this.getStatus() !== "open") return;
|
|
1106
|
-
this.eventDispatcher.dispatchEvent("message", message);
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Adds a listener to this transport.
|
|
1110
|
-
* @param the type of event to listen for
|
|
1111
|
-
* @param handler The message handler to add.
|
|
1112
|
-
*/
|
|
1113
|
-
addEventListener(type, handler) {
|
|
1114
|
-
this.eventDispatcher.addEventListener(type, handler);
|
|
1115
|
-
}
|
|
1116
|
-
/**
|
|
1117
|
-
* Removes a listener from this transport.
|
|
1118
|
-
* @param the type of event to un-listen on
|
|
1119
|
-
* @param handler The message handler to remove.
|
|
1120
|
-
*/
|
|
1121
|
-
removeEventListener(type, handler) {
|
|
1122
|
-
this.eventDispatcher.removeEventListener(type, handler);
|
|
1123
|
-
}
|
|
1124
|
-
protocolError(message) {
|
|
1125
|
-
this.eventDispatcher.dispatchEvent("protocolError", message);
|
|
1126
|
-
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Default close implementation for transports. You should override this in the downstream
|
|
1129
|
-
* implementation if you need to do any additional cleanup and call super.close() at the end.
|
|
1130
|
-
* Closes the transport. Any messages sent while the transport is closed will be silently discarded.
|
|
1131
|
-
*/
|
|
1132
|
-
close() {
|
|
1133
|
-
this.status = "closed";
|
|
1134
|
-
const sessions = Array.from(this.sessions.values());
|
|
1135
|
-
for (const session of sessions) {
|
|
1136
|
-
this.deleteSession(session);
|
|
1137
|
-
}
|
|
1138
|
-
this.eventDispatcher.dispatchEvent("transportStatus", {
|
|
1139
|
-
status: this.status
|
|
1140
|
-
});
|
|
1141
|
-
this.eventDispatcher.removeAllListeners();
|
|
1142
|
-
this.log?.info(`manually closed transport`, { clientId: this.clientId });
|
|
1143
|
-
}
|
|
1144
|
-
getStatus() {
|
|
1145
|
-
return this.status;
|
|
1146
|
-
}
|
|
1147
|
-
// state transitions
|
|
1148
|
-
createSession(session) {
|
|
1149
|
-
const activeSession = this.sessions.get(session.to);
|
|
1150
|
-
if (activeSession) {
|
|
1151
|
-
const msg = `attempt to create session for ${session.to} but active session (${activeSession.id}) already exists`;
|
|
1152
|
-
this.log?.error(msg, {
|
|
1153
|
-
...session.loggingMetadata,
|
|
1154
|
-
tags: ["invariant-violation"]
|
|
1155
|
-
});
|
|
1156
|
-
throw new Error(msg);
|
|
1157
|
-
}
|
|
1158
|
-
this.sessions.set(session.to, session);
|
|
1159
|
-
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
1160
|
-
status: "created",
|
|
1161
|
-
session
|
|
1162
|
-
});
|
|
1163
|
-
this.eventDispatcher.dispatchEvent("sessionTransition", {
|
|
1164
|
-
state: session.state,
|
|
1165
|
-
id: session.id
|
|
1166
|
-
});
|
|
1167
|
-
}
|
|
1168
|
-
updateSession(session) {
|
|
1169
|
-
const activeSession = this.sessions.get(session.to);
|
|
1170
|
-
if (!activeSession) {
|
|
1171
|
-
const msg = `attempt to transition session for ${session.to} but no active session exists`;
|
|
1172
|
-
this.log?.error(msg, {
|
|
1173
|
-
...session.loggingMetadata,
|
|
1174
|
-
tags: ["invariant-violation"]
|
|
1175
|
-
});
|
|
1176
|
-
throw new Error(msg);
|
|
1177
|
-
}
|
|
1178
|
-
if (activeSession.id !== session.id) {
|
|
1179
|
-
const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;
|
|
1180
|
-
this.log?.error(msg, {
|
|
1181
|
-
...session.loggingMetadata,
|
|
1182
|
-
tags: ["invariant-violation"]
|
|
1183
|
-
});
|
|
1184
|
-
throw new Error(msg);
|
|
1185
|
-
}
|
|
1186
|
-
this.sessions.set(session.to, session);
|
|
1187
|
-
this.eventDispatcher.dispatchEvent("sessionTransition", {
|
|
1188
|
-
state: session.state,
|
|
1189
|
-
id: session.id
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
deleteSession(session, options) {
|
|
1193
|
-
if (session._isConsumed) return;
|
|
1194
|
-
const loggingMetadata = session.loggingMetadata;
|
|
1195
|
-
if (loggingMetadata.tags && options?.unhealthy) {
|
|
1196
|
-
loggingMetadata.tags.push("unhealthy-session");
|
|
1197
|
-
}
|
|
1198
|
-
session.log?.info(`closing session ${session.id}`, loggingMetadata);
|
|
1199
|
-
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
1200
|
-
status: "closing",
|
|
1201
|
-
session
|
|
1202
|
-
});
|
|
1203
|
-
const to = session.to;
|
|
1204
|
-
session.close();
|
|
1205
|
-
this.sessions.delete(to);
|
|
1206
|
-
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
1207
|
-
status: "closed",
|
|
1208
|
-
session: { id: session.id, to }
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
// common listeners
|
|
1212
|
-
onSessionGracePeriodElapsed(session) {
|
|
1213
|
-
this.log?.info(
|
|
1214
|
-
`session to ${session.to} grace period elapsed, closing`,
|
|
1215
|
-
session.loggingMetadata
|
|
1216
|
-
);
|
|
1217
|
-
this.deleteSession(session);
|
|
1218
|
-
}
|
|
1219
|
-
onConnectingFailed(session) {
|
|
1220
|
-
const noConnectionSession = SessionStateGraph.transition.ConnectingToNoConnection(session, {
|
|
1221
|
-
onSessionGracePeriodElapsed: () => {
|
|
1222
|
-
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1223
|
-
},
|
|
1224
|
-
onMessageSendFailure: (msg, reason) => {
|
|
1225
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1226
|
-
...noConnectionSession.loggingMetadata,
|
|
1227
|
-
transportMessage: msg
|
|
1228
|
-
});
|
|
1229
|
-
this.protocolError({
|
|
1230
|
-
type: ProtocolError.MessageSendFailure,
|
|
1231
|
-
message: reason
|
|
1232
|
-
});
|
|
1233
|
-
this.deleteSession(noConnectionSession, { unhealthy: true });
|
|
1234
|
-
}
|
|
1235
|
-
});
|
|
1236
|
-
this.updateSession(noConnectionSession);
|
|
1237
|
-
return noConnectionSession;
|
|
1238
|
-
}
|
|
1239
|
-
onConnClosed(session) {
|
|
1240
|
-
let noConnectionSession;
|
|
1241
|
-
const listeners = {
|
|
1242
|
-
onSessionGracePeriodElapsed: () => {
|
|
1243
|
-
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1244
|
-
},
|
|
1245
|
-
onMessageSendFailure: (msg, reason) => {
|
|
1246
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1247
|
-
...noConnectionSession.loggingMetadata,
|
|
1248
|
-
transportMessage: msg
|
|
1249
|
-
});
|
|
1250
|
-
this.protocolError({
|
|
1251
|
-
type: ProtocolError.MessageSendFailure,
|
|
1252
|
-
message: reason
|
|
1253
|
-
});
|
|
1254
|
-
this.deleteSession(noConnectionSession, { unhealthy: true });
|
|
1255
|
-
}
|
|
1256
|
-
};
|
|
1257
|
-
if (session.state === "Handshaking" /* Handshaking */) {
|
|
1258
|
-
noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(
|
|
1259
|
-
session,
|
|
1260
|
-
listeners
|
|
1261
|
-
);
|
|
1262
|
-
} else {
|
|
1263
|
-
noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(
|
|
1264
|
-
session,
|
|
1265
|
-
listeners
|
|
1266
|
-
);
|
|
1267
|
-
}
|
|
1268
|
-
this.updateSession(noConnectionSession);
|
|
1269
|
-
return noConnectionSession;
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Gets a send closure scoped to a specific session. Sending using the returned
|
|
1273
|
-
* closure after the session has transitioned to a different state will be a noop.
|
|
1274
|
-
*
|
|
1275
|
-
* Session objects themselves can become stale as they transition between
|
|
1276
|
-
* states. As stale sessions cannot be used again (and will throw), holding
|
|
1277
|
-
* onto a session object is not recommended.
|
|
1278
|
-
*/
|
|
1279
|
-
getSessionBoundSendFn(to, sessionId) {
|
|
1280
|
-
if (this.getStatus() !== "open") {
|
|
1281
|
-
throw new Error("cannot get a bound send function on a closed transport");
|
|
1282
|
-
}
|
|
1283
|
-
return (msg) => {
|
|
1284
|
-
const session = this.sessions.get(to);
|
|
1285
|
-
if (!session) {
|
|
1286
|
-
throw new Error(
|
|
1287
|
-
`session scope for ${sessionId} has ended (close), can't send`
|
|
1288
|
-
);
|
|
1289
|
-
}
|
|
1290
|
-
const sameSession = session.id === sessionId;
|
|
1291
|
-
if (!sameSession || session._isConsumed) {
|
|
1292
|
-
throw new Error(
|
|
1293
|
-
`session scope for ${sessionId} has ended (transition), can't send`
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
const res = session.send(msg);
|
|
1297
|
-
if (!res.ok) {
|
|
1298
|
-
throw new Error(res.reason);
|
|
1299
|
-
}
|
|
1300
|
-
return res.value;
|
|
1301
|
-
};
|
|
1302
|
-
}
|
|
1303
|
-
};
|
|
1304
|
-
|
|
1305
|
-
// transport/client.ts
|
|
1306
|
-
var import_api4 = require("@opentelemetry/api");
|
|
1307
|
-
|
|
1308
|
-
// transport/rateLimit.ts
|
|
1309
|
-
var LeakyBucketRateLimit = class {
|
|
1310
|
-
budgetConsumed;
|
|
1311
|
-
intervalHandle;
|
|
1312
|
-
options;
|
|
1313
|
-
constructor(options) {
|
|
1314
|
-
this.options = options;
|
|
1315
|
-
this.budgetConsumed = 0;
|
|
1316
|
-
}
|
|
1317
|
-
getBackoffMs() {
|
|
1318
|
-
if (this.getBudgetConsumed() === 0) {
|
|
1319
|
-
return 0;
|
|
1320
|
-
}
|
|
1321
|
-
const exponent = Math.max(0, this.getBudgetConsumed() - 1);
|
|
1322
|
-
const jitter = Math.floor(Math.random() * this.options.maxJitterMs);
|
|
1323
|
-
const backoffMs = Math.min(
|
|
1324
|
-
this.options.baseIntervalMs * 2 ** exponent,
|
|
1325
|
-
this.options.maxBackoffMs
|
|
1326
|
-
);
|
|
1327
|
-
return backoffMs + jitter;
|
|
1328
|
-
}
|
|
1329
|
-
get totalBudgetRestoreTime() {
|
|
1330
|
-
return this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity;
|
|
1331
|
-
}
|
|
1332
|
-
consumeBudget() {
|
|
1333
|
-
this.stopLeak();
|
|
1334
|
-
this.budgetConsumed = this.getBudgetConsumed() + 1;
|
|
1335
|
-
}
|
|
1336
|
-
getBudgetConsumed() {
|
|
1337
|
-
return this.budgetConsumed;
|
|
1338
|
-
}
|
|
1339
|
-
hasBudget() {
|
|
1340
|
-
return this.getBudgetConsumed() < this.options.attemptBudgetCapacity;
|
|
1341
|
-
}
|
|
1342
|
-
startRestoringBudget() {
|
|
1343
|
-
if (this.intervalHandle) {
|
|
1344
|
-
return;
|
|
1345
|
-
}
|
|
1346
|
-
const restoreBudgetForUser = () => {
|
|
1347
|
-
const currentBudget = this.budgetConsumed;
|
|
1348
|
-
if (!currentBudget) {
|
|
1349
|
-
this.stopLeak();
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
const newBudget = currentBudget - 1;
|
|
1353
|
-
if (newBudget === 0) {
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
this.budgetConsumed = newBudget;
|
|
1357
|
-
};
|
|
1358
|
-
this.intervalHandle = setInterval(
|
|
1359
|
-
restoreBudgetForUser,
|
|
1360
|
-
this.options.budgetRestoreIntervalMs
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
1363
|
-
stopLeak() {
|
|
1364
|
-
if (!this.intervalHandle) {
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
clearInterval(this.intervalHandle);
|
|
1368
|
-
this.intervalHandle = void 0;
|
|
1369
|
-
}
|
|
1370
|
-
resetBudget() {
|
|
1371
|
-
this.stopLeak();
|
|
1372
|
-
this.budgetConsumed = 0;
|
|
1373
|
-
}
|
|
1374
|
-
close() {
|
|
1375
|
-
this.stopLeak();
|
|
1376
|
-
}
|
|
1377
|
-
};
|
|
1378
|
-
|
|
1379
|
-
// transport/client.ts
|
|
1380
|
-
var import_value = require("@sinclair/typebox/value");
|
|
1381
|
-
var ClientTransport = class extends Transport {
|
|
1382
|
-
/**
|
|
1383
|
-
* The options for this transport.
|
|
1384
|
-
*/
|
|
1385
|
-
options;
|
|
1386
|
-
retryBudget;
|
|
1387
|
-
/**
|
|
1388
|
-
* A flag indicating whether the transport should automatically reconnect
|
|
1389
|
-
* when a connection is dropped.
|
|
1390
|
-
* Realistically, this should always be true for clients unless you are writing
|
|
1391
|
-
* tests or a special case where you don't want to reconnect.
|
|
1392
|
-
*/
|
|
1393
|
-
reconnectOnConnectionDrop = true;
|
|
1394
|
-
/**
|
|
1395
|
-
* Optional handshake options for this client.
|
|
1396
|
-
*/
|
|
1397
|
-
handshakeExtensions;
|
|
1398
|
-
sessions;
|
|
1399
|
-
constructor(clientId, providedOptions) {
|
|
1400
|
-
super(clientId, providedOptions);
|
|
1401
|
-
this.sessions = /* @__PURE__ */ new Map();
|
|
1402
|
-
this.options = {
|
|
1403
|
-
...defaultClientTransportOptions,
|
|
1404
|
-
...providedOptions
|
|
1405
|
-
};
|
|
1406
|
-
this.retryBudget = new LeakyBucketRateLimit(this.options);
|
|
1407
|
-
}
|
|
1408
|
-
extendHandshake(options) {
|
|
1409
|
-
this.handshakeExtensions = options;
|
|
1410
|
-
}
|
|
1411
|
-
tryReconnecting(to) {
|
|
1412
|
-
const oldSession = this.sessions.get(to);
|
|
1413
|
-
if (!this.options.enableTransparentSessionReconnects && oldSession) {
|
|
1414
|
-
this.deleteSession(oldSession);
|
|
1415
|
-
}
|
|
1416
|
-
if (this.reconnectOnConnectionDrop && this.getStatus() === "open") {
|
|
1417
|
-
this.connect(to);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
/*
|
|
1421
|
-
* Creates a raw unconnected session object.
|
|
1422
|
-
* This is mostly a River internal, you shouldn't need to use this directly.
|
|
1423
|
-
*/
|
|
1424
|
-
createUnconnectedSession(to) {
|
|
1425
|
-
const session = ClientSessionStateGraph.entrypoint(
|
|
1426
|
-
to,
|
|
1427
|
-
this.clientId,
|
|
1428
|
-
{
|
|
1429
|
-
onSessionGracePeriodElapsed: () => {
|
|
1430
|
-
this.onSessionGracePeriodElapsed(session);
|
|
1431
|
-
},
|
|
1432
|
-
onMessageSendFailure: (msg, reason) => {
|
|
1433
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1434
|
-
...session.loggingMetadata,
|
|
1435
|
-
transportMessage: msg
|
|
1436
|
-
});
|
|
1437
|
-
this.protocolError({
|
|
1438
|
-
type: ProtocolError.MessageSendFailure,
|
|
1439
|
-
message: reason
|
|
1440
|
-
});
|
|
1441
|
-
this.deleteSession(session, { unhealthy: true });
|
|
1442
|
-
}
|
|
1443
|
-
},
|
|
1444
|
-
this.options,
|
|
1445
|
-
currentProtocolVersion,
|
|
1446
|
-
this.tracer,
|
|
1447
|
-
this.log
|
|
1448
|
-
);
|
|
1449
|
-
this.createSession(session);
|
|
1450
|
-
return session;
|
|
1451
|
-
}
|
|
1452
|
-
// listeners
|
|
1453
|
-
onConnectingFailed(session) {
|
|
1454
|
-
const noConnectionSession = super.onConnectingFailed(session);
|
|
1455
|
-
this.tryReconnecting(noConnectionSession.to);
|
|
1456
|
-
return noConnectionSession;
|
|
1457
|
-
}
|
|
1458
|
-
onConnClosed(session) {
|
|
1459
|
-
const noConnectionSession = super.onConnClosed(session);
|
|
1460
|
-
this.tryReconnecting(noConnectionSession.to);
|
|
1461
|
-
return noConnectionSession;
|
|
1462
|
-
}
|
|
1463
|
-
onConnectionEstablished(session, conn) {
|
|
1464
|
-
const handshakingSession = ClientSessionStateGraph.transition.ConnectingToHandshaking(
|
|
1465
|
-
session,
|
|
1466
|
-
conn,
|
|
1467
|
-
{
|
|
1468
|
-
onConnectionErrored: (err) => {
|
|
1469
|
-
const errStr = coerceErrorString(err);
|
|
1470
|
-
this.log?.error(
|
|
1471
|
-
`connection to ${handshakingSession.to} errored during handshake: ${errStr}`,
|
|
1472
|
-
handshakingSession.loggingMetadata
|
|
1473
|
-
);
|
|
1474
|
-
},
|
|
1475
|
-
onConnectionClosed: () => {
|
|
1476
|
-
this.log?.warn(
|
|
1477
|
-
`connection to ${handshakingSession.to} closed during handshake`,
|
|
1478
|
-
handshakingSession.loggingMetadata
|
|
1479
|
-
);
|
|
1480
|
-
this.onConnClosed(handshakingSession);
|
|
1481
|
-
},
|
|
1482
|
-
onHandshake: (msg) => {
|
|
1483
|
-
this.onHandshakeResponse(handshakingSession, msg);
|
|
1484
|
-
},
|
|
1485
|
-
onInvalidHandshake: (reason, code) => {
|
|
1486
|
-
this.log?.error(
|
|
1487
|
-
`invalid handshake: ${reason}`,
|
|
1488
|
-
handshakingSession.loggingMetadata
|
|
1489
|
-
);
|
|
1490
|
-
this.deleteSession(session, { unhealthy: true });
|
|
1491
|
-
this.protocolError({
|
|
1492
|
-
type: ProtocolError.HandshakeFailed,
|
|
1493
|
-
code,
|
|
1494
|
-
message: reason
|
|
1495
|
-
});
|
|
1496
|
-
},
|
|
1497
|
-
onHandshakeTimeout: () => {
|
|
1498
|
-
this.log?.error(
|
|
1499
|
-
`connection to ${handshakingSession.to} timed out during handshake`,
|
|
1500
|
-
handshakingSession.loggingMetadata
|
|
1501
|
-
);
|
|
1502
|
-
this.onConnClosed(handshakingSession);
|
|
1503
|
-
},
|
|
1504
|
-
onSessionGracePeriodElapsed: () => {
|
|
1505
|
-
this.onSessionGracePeriodElapsed(handshakingSession);
|
|
1506
|
-
},
|
|
1507
|
-
onMessageSendFailure: (msg, reason) => {
|
|
1508
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1509
|
-
...handshakingSession.loggingMetadata,
|
|
1510
|
-
transportMessage: msg
|
|
1511
|
-
});
|
|
1512
|
-
this.protocolError({
|
|
1513
|
-
type: ProtocolError.MessageSendFailure,
|
|
1514
|
-
message: reason
|
|
1515
|
-
});
|
|
1516
|
-
this.deleteSession(handshakingSession, { unhealthy: true });
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
);
|
|
1520
|
-
this.updateSession(handshakingSession);
|
|
1521
|
-
void this.sendHandshake(handshakingSession);
|
|
1522
|
-
return handshakingSession;
|
|
1523
|
-
}
|
|
1524
|
-
rejectHandshakeResponse(session, reason, metadata) {
|
|
1525
|
-
session.conn.telemetry?.span.setStatus({
|
|
1526
|
-
code: import_api4.SpanStatusCode.ERROR,
|
|
1527
|
-
message: reason
|
|
1528
|
-
});
|
|
1529
|
-
this.log?.warn(reason, metadata);
|
|
1530
|
-
this.deleteSession(session, { unhealthy: true });
|
|
1531
|
-
}
|
|
1532
|
-
onHandshakeResponse(session, msg) {
|
|
1533
|
-
if (!import_value.Value.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {
|
|
1534
|
-
const reason = `received invalid handshake response`;
|
|
1535
|
-
this.rejectHandshakeResponse(session, reason, {
|
|
1536
|
-
...session.loggingMetadata,
|
|
1537
|
-
transportMessage: msg,
|
|
1538
|
-
validationErrors: [
|
|
1539
|
-
...import_value.Value.Errors(ControlMessageHandshakeResponseSchema, msg.payload)
|
|
1540
|
-
]
|
|
1541
|
-
});
|
|
1542
|
-
return;
|
|
1543
|
-
}
|
|
1544
|
-
if (!msg.payload.status.ok) {
|
|
1545
|
-
const retriable = import_value.Value.Check(
|
|
1546
|
-
HandshakeErrorRetriableResponseCodes,
|
|
1547
|
-
msg.payload.status.code
|
|
1548
|
-
);
|
|
1549
|
-
const reason = `handshake failed: ${msg.payload.status.reason}`;
|
|
1550
|
-
const to = session.to;
|
|
1551
|
-
this.rejectHandshakeResponse(session, reason, {
|
|
1552
|
-
...session.loggingMetadata,
|
|
1553
|
-
transportMessage: msg
|
|
1554
|
-
});
|
|
1555
|
-
if (retriable) {
|
|
1556
|
-
this.tryReconnecting(to);
|
|
1557
|
-
} else {
|
|
1558
|
-
this.protocolError({
|
|
1559
|
-
type: ProtocolError.HandshakeFailed,
|
|
1560
|
-
code: msg.payload.status.code,
|
|
1561
|
-
message: reason
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
return;
|
|
1565
|
-
}
|
|
1566
|
-
if (msg.payload.status.sessionId !== session.id) {
|
|
1567
|
-
const reason = `session id mismatch: expected ${session.id}, got ${msg.payload.status.sessionId}`;
|
|
1568
|
-
this.rejectHandshakeResponse(session, reason, {
|
|
1569
|
-
...session.loggingMetadata,
|
|
1570
|
-
transportMessage: msg
|
|
1571
|
-
});
|
|
1572
|
-
return;
|
|
1573
|
-
}
|
|
1574
|
-
this.log?.info(`handshake from ${msg.from} ok`, {
|
|
1575
|
-
...session.loggingMetadata,
|
|
1576
|
-
transportMessage: msg
|
|
1577
|
-
});
|
|
1578
|
-
const connectedSession = ClientSessionStateGraph.transition.HandshakingToConnected(session, {
|
|
1579
|
-
onConnectionErrored: (err) => {
|
|
1580
|
-
const errStr = coerceErrorString(err);
|
|
1581
|
-
if (err instanceof Error && this.options.isFatalConnectionError(err)) {
|
|
1582
|
-
this.log?.warn(
|
|
1583
|
-
`connection to ${connectedSession.to} fatally errored: ${errStr}`,
|
|
1584
|
-
connectedSession.loggingMetadata
|
|
1585
|
-
);
|
|
1586
|
-
this.reconnectOnConnectionDrop = false;
|
|
1587
|
-
} else {
|
|
1588
|
-
this.log?.warn(
|
|
1589
|
-
`connection to ${connectedSession.to} errored: ${errStr}`,
|
|
1590
|
-
connectedSession.loggingMetadata
|
|
1591
|
-
);
|
|
1592
|
-
}
|
|
1593
|
-
},
|
|
1594
|
-
onConnectionClosed: () => {
|
|
1595
|
-
this.log?.info(
|
|
1596
|
-
`connection to ${connectedSession.to} closed`,
|
|
1597
|
-
connectedSession.loggingMetadata
|
|
1598
|
-
);
|
|
1599
|
-
this.onConnClosed(connectedSession);
|
|
1600
|
-
},
|
|
1601
|
-
onMessage: (msg2) => {
|
|
1602
|
-
this.handleMsg(msg2);
|
|
1603
|
-
},
|
|
1604
|
-
onInvalidMessage: (reason) => {
|
|
1605
|
-
this.log?.error(`invalid message: ${reason}`, {
|
|
1606
|
-
...connectedSession.loggingMetadata,
|
|
1607
|
-
transportMessage: msg
|
|
1608
|
-
});
|
|
1609
|
-
this.protocolError({
|
|
1610
|
-
type: ProtocolError.InvalidMessage,
|
|
1611
|
-
message: reason
|
|
1612
|
-
});
|
|
1613
|
-
this.deleteSession(connectedSession, { unhealthy: true });
|
|
1614
|
-
},
|
|
1615
|
-
onMessageSendFailure: (msg2, reason) => {
|
|
1616
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1617
|
-
...connectedSession.loggingMetadata,
|
|
1618
|
-
transportMessage: msg2
|
|
1619
|
-
});
|
|
1620
|
-
this.protocolError({
|
|
1621
|
-
type: ProtocolError.MessageSendFailure,
|
|
1622
|
-
message: reason
|
|
1623
|
-
});
|
|
1624
|
-
this.deleteSession(connectedSession, { unhealthy: true });
|
|
1625
|
-
}
|
|
1626
|
-
});
|
|
1627
|
-
const res = connectedSession.sendBufferedMessages();
|
|
1628
|
-
if (!res.ok) {
|
|
1629
|
-
return;
|
|
1630
|
-
}
|
|
1631
|
-
this.updateSession(connectedSession);
|
|
1632
|
-
this.retryBudget.startRestoringBudget();
|
|
1633
|
-
}
|
|
1634
|
-
/**
|
|
1635
|
-
* Manually attempts to connect to a client.
|
|
1636
|
-
* @param to The client ID of the node to connect to.
|
|
1637
|
-
*/
|
|
1638
|
-
connect(to) {
|
|
1639
|
-
if (this.getStatus() !== "open") {
|
|
1640
|
-
this.log?.info(
|
|
1641
|
-
`transport state is no longer open, cancelling attempt to connect to ${to}`
|
|
1642
|
-
);
|
|
1643
|
-
return;
|
|
1644
|
-
}
|
|
1645
|
-
const session = this.sessions.get(to) ?? this.createUnconnectedSession(to);
|
|
1646
|
-
if (session.state !== "NoConnection" /* NoConnection */) {
|
|
1647
|
-
this.log?.debug(
|
|
1648
|
-
`session to ${to} has state ${session.state}, skipping connect attempt`,
|
|
1649
|
-
session.loggingMetadata
|
|
1650
|
-
);
|
|
1651
|
-
return;
|
|
1652
|
-
}
|
|
1653
|
-
if (!this.retryBudget.hasBudget()) {
|
|
1654
|
-
const budgetConsumed = this.retryBudget.getBudgetConsumed();
|
|
1655
|
-
const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
|
|
1656
|
-
this.log?.error(errMsg, session.loggingMetadata);
|
|
1657
|
-
this.protocolError({
|
|
1658
|
-
type: ProtocolError.RetriesExceeded,
|
|
1659
|
-
message: errMsg
|
|
1660
|
-
});
|
|
1661
|
-
return;
|
|
1662
|
-
}
|
|
1663
|
-
const backoffMs = this.retryBudget.getBackoffMs();
|
|
1664
|
-
this.log?.info(
|
|
1665
|
-
`attempting connection to ${to} (${backoffMs}ms backoff)`,
|
|
1666
|
-
session.loggingMetadata
|
|
1667
|
-
);
|
|
1668
|
-
this.retryBudget.consumeBudget();
|
|
1669
|
-
const backingOffSession = ClientSessionStateGraph.transition.NoConnectionToBackingOff(
|
|
1670
|
-
session,
|
|
1671
|
-
backoffMs,
|
|
1672
|
-
{
|
|
1673
|
-
onBackoffFinished: () => {
|
|
1674
|
-
this.onBackoffFinished(backingOffSession);
|
|
1675
|
-
},
|
|
1676
|
-
onSessionGracePeriodElapsed: () => {
|
|
1677
|
-
this.onSessionGracePeriodElapsed(backingOffSession);
|
|
1678
|
-
},
|
|
1679
|
-
onMessageSendFailure: (msg, reason) => {
|
|
1680
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1681
|
-
...backingOffSession.loggingMetadata,
|
|
1682
|
-
transportMessage: msg
|
|
1683
|
-
});
|
|
1684
|
-
this.protocolError({
|
|
1685
|
-
type: ProtocolError.MessageSendFailure,
|
|
1686
|
-
message: reason
|
|
1687
|
-
});
|
|
1688
|
-
this.deleteSession(backingOffSession, { unhealthy: true });
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
);
|
|
1692
|
-
this.updateSession(backingOffSession);
|
|
1693
|
-
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Manually kills all sessions to the server (including all pending state).
|
|
1696
|
-
* This is useful for when you want to close all connections to a server
|
|
1697
|
-
* and don't want to wait for the grace period to elapse.
|
|
1698
|
-
*/
|
|
1699
|
-
hardDisconnect() {
|
|
1700
|
-
const sessions = Array.from(this.sessions.values());
|
|
1701
|
-
for (const session of sessions) {
|
|
1702
|
-
this.deleteSession(session);
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
onBackoffFinished(session) {
|
|
1706
|
-
const connPromise = session.tracer.startActiveSpan(
|
|
1707
|
-
"connect",
|
|
1708
|
-
async (span) => {
|
|
1709
|
-
try {
|
|
1710
|
-
return await this.createNewOutgoingConnection(session.to);
|
|
1711
|
-
} catch (err) {
|
|
1712
|
-
const errStr = coerceErrorString(err);
|
|
1713
|
-
span.recordException(errStr);
|
|
1714
|
-
span.setStatus({ code: import_api4.SpanStatusCode.ERROR });
|
|
1715
|
-
throw err;
|
|
1716
|
-
} finally {
|
|
1717
|
-
span.end();
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
);
|
|
1721
|
-
const connectingSession = ClientSessionStateGraph.transition.BackingOffToConnecting(
|
|
1722
|
-
session,
|
|
1723
|
-
connPromise,
|
|
1724
|
-
{
|
|
1725
|
-
onConnectionEstablished: (conn) => {
|
|
1726
|
-
this.log?.debug(
|
|
1727
|
-
`connection to ${connectingSession.to} established`,
|
|
1728
|
-
{
|
|
1729
|
-
...conn.loggingMetadata,
|
|
1730
|
-
...connectingSession.loggingMetadata
|
|
1731
|
-
}
|
|
1732
|
-
);
|
|
1733
|
-
this.onConnectionEstablished(connectingSession, conn);
|
|
1734
|
-
},
|
|
1735
|
-
onConnectionFailed: (error) => {
|
|
1736
|
-
const errStr = coerceErrorString(error);
|
|
1737
|
-
this.log?.error(
|
|
1738
|
-
`error connecting to ${connectingSession.to}: ${errStr}`,
|
|
1739
|
-
connectingSession.loggingMetadata
|
|
1740
|
-
);
|
|
1741
|
-
this.onConnectingFailed(connectingSession);
|
|
1742
|
-
},
|
|
1743
|
-
onConnectionTimeout: () => {
|
|
1744
|
-
this.log?.error(
|
|
1745
|
-
`connection to ${connectingSession.to} timed out`,
|
|
1746
|
-
connectingSession.loggingMetadata
|
|
1747
|
-
);
|
|
1748
|
-
this.onConnectingFailed(connectingSession);
|
|
1749
|
-
},
|
|
1750
|
-
onSessionGracePeriodElapsed: () => {
|
|
1751
|
-
this.onSessionGracePeriodElapsed(connectingSession);
|
|
1752
|
-
},
|
|
1753
|
-
onMessageSendFailure: (msg, reason) => {
|
|
1754
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
1755
|
-
...connectingSession.loggingMetadata,
|
|
1756
|
-
transportMessage: msg
|
|
1757
|
-
});
|
|
1758
|
-
this.protocolError({
|
|
1759
|
-
type: ProtocolError.MessageSendFailure,
|
|
1760
|
-
message: reason
|
|
1761
|
-
});
|
|
1762
|
-
this.deleteSession(connectingSession, { unhealthy: true });
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
);
|
|
1766
|
-
this.updateSession(connectingSession);
|
|
1767
|
-
}
|
|
1768
|
-
async sendHandshake(session) {
|
|
1769
|
-
let metadata = void 0;
|
|
1770
|
-
if (this.handshakeExtensions) {
|
|
1771
|
-
try {
|
|
1772
|
-
metadata = await this.handshakeExtensions.construct();
|
|
1773
|
-
} catch (err) {
|
|
1774
|
-
const errStr = coerceErrorString(err);
|
|
1775
|
-
this.log?.error(
|
|
1776
|
-
`failed to construct handshake metadata for session to ${session.to}: ${errStr}`,
|
|
1777
|
-
session.loggingMetadata
|
|
1778
|
-
);
|
|
1779
|
-
this.protocolError({
|
|
1780
|
-
type: ProtocolError.HandshakeFailed,
|
|
1781
|
-
message: `failed to construct handshake metadata: ${errStr}`
|
|
1782
|
-
});
|
|
1783
|
-
this.deleteSession(session, { unhealthy: true });
|
|
1784
|
-
return;
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
if (session._isConsumed) {
|
|
1788
|
-
return;
|
|
1789
|
-
}
|
|
1790
|
-
const requestMsg = handshakeRequestMessage({
|
|
1791
|
-
from: this.clientId,
|
|
1792
|
-
to: session.to,
|
|
1793
|
-
sessionId: session.id,
|
|
1794
|
-
expectedSessionState: {
|
|
1795
|
-
nextExpectedSeq: session.ack,
|
|
1796
|
-
nextSentSeq: session.nextSeq()
|
|
1797
|
-
},
|
|
1798
|
-
metadata,
|
|
1799
|
-
tracing: getPropagationContext(session.telemetry.ctx)
|
|
1800
|
-
});
|
|
1801
|
-
this.log?.debug(`sending handshake request to ${session.to}`, {
|
|
1802
|
-
...session.loggingMetadata,
|
|
1803
|
-
transportMessage: requestMsg
|
|
1804
|
-
});
|
|
1805
|
-
const res = session.sendHandshake(requestMsg);
|
|
1806
|
-
if (!res.ok) {
|
|
1807
|
-
this.log?.error(`failed to send handshake request: ${res.reason}`, {
|
|
1808
|
-
...session.loggingMetadata,
|
|
1809
|
-
transportMessage: requestMsg
|
|
1810
|
-
});
|
|
1811
|
-
this.protocolError({
|
|
1812
|
-
type: ProtocolError.MessageSendFailure,
|
|
1813
|
-
message: res.reason
|
|
1814
|
-
});
|
|
1815
|
-
this.deleteSession(session, { unhealthy: true });
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
close() {
|
|
1819
|
-
this.retryBudget.close();
|
|
1820
|
-
super.close();
|
|
1821
|
-
}
|
|
1822
|
-
};
|
|
1823
|
-
|
|
1824
|
-
// transport/server.ts
|
|
1825
|
-
var import_api5 = require("@opentelemetry/api");
|
|
1826
|
-
var import_value2 = require("@sinclair/typebox/value");
|
|
1827
|
-
var ServerTransport = class extends Transport {
|
|
1828
|
-
/**
|
|
1829
|
-
* The options for this transport.
|
|
1830
|
-
*/
|
|
1831
|
-
options;
|
|
1832
|
-
/**
|
|
1833
|
-
* Optional handshake options for the server.
|
|
1834
|
-
*/
|
|
1835
|
-
handshakeExtensions;
|
|
1836
|
-
/**
|
|
1837
|
-
* A map of session handshake data for each session.
|
|
1838
|
-
*/
|
|
1839
|
-
sessionHandshakeMetadata = /* @__PURE__ */ new Map();
|
|
1840
|
-
sessions = /* @__PURE__ */ new Map();
|
|
1841
|
-
pendingSessions = /* @__PURE__ */ new Set();
|
|
1842
|
-
constructor(clientId, providedOptions) {
|
|
1843
|
-
super(clientId, providedOptions);
|
|
1844
|
-
this.sessions = /* @__PURE__ */ new Map();
|
|
1845
|
-
this.options = {
|
|
1846
|
-
...defaultServerTransportOptions,
|
|
1847
|
-
...providedOptions
|
|
1848
|
-
};
|
|
1849
|
-
this.log?.info(`initiated server transport`, {
|
|
1850
|
-
clientId: this.clientId,
|
|
1851
|
-
protocolVersion: currentProtocolVersion
|
|
1852
|
-
});
|
|
1853
|
-
}
|
|
1854
|
-
extendHandshake(options) {
|
|
1855
|
-
this.handshakeExtensions = options;
|
|
1856
|
-
}
|
|
1857
|
-
deletePendingSession(pendingSession) {
|
|
1858
|
-
pendingSession.close();
|
|
1859
|
-
this.pendingSessions.delete(pendingSession);
|
|
1860
|
-
}
|
|
1861
|
-
deleteSession(session, options) {
|
|
1862
|
-
this.sessionHandshakeMetadata.delete(session.to);
|
|
1863
|
-
super.deleteSession(session, options);
|
|
1864
|
-
}
|
|
1865
|
-
handleConnection(conn) {
|
|
1866
|
-
if (this.getStatus() !== "open") return;
|
|
1867
|
-
this.log?.info(`new incoming connection`, {
|
|
1868
|
-
...conn.loggingMetadata,
|
|
1869
|
-
clientId: this.clientId
|
|
1870
|
-
});
|
|
1871
|
-
let receivedHandshake = false;
|
|
1872
|
-
const pendingSession = ServerSessionStateGraph.entrypoint(
|
|
1873
|
-
this.clientId,
|
|
1874
|
-
conn,
|
|
1875
|
-
{
|
|
1876
|
-
onConnectionClosed: () => {
|
|
1877
|
-
this.log?.warn(
|
|
1878
|
-
`connection from unknown closed before handshake finished`,
|
|
1879
|
-
pendingSession.loggingMetadata
|
|
1880
|
-
);
|
|
1881
|
-
this.deletePendingSession(pendingSession);
|
|
1882
|
-
},
|
|
1883
|
-
onConnectionErrored: (err) => {
|
|
1884
|
-
const errorString = coerceErrorString(err);
|
|
1885
|
-
this.log?.warn(
|
|
1886
|
-
`connection from unknown errored before handshake finished: ${errorString}`,
|
|
1887
|
-
pendingSession.loggingMetadata
|
|
1888
|
-
);
|
|
1889
|
-
this.deletePendingSession(pendingSession);
|
|
1890
|
-
},
|
|
1891
|
-
onHandshakeTimeout: () => {
|
|
1892
|
-
this.log?.warn(
|
|
1893
|
-
`connection from unknown timed out before handshake finished`,
|
|
1894
|
-
pendingSession.loggingMetadata
|
|
1895
|
-
);
|
|
1896
|
-
this.deletePendingSession(pendingSession);
|
|
1897
|
-
},
|
|
1898
|
-
onHandshake: (msg) => {
|
|
1899
|
-
if (receivedHandshake) {
|
|
1900
|
-
this.log?.error(
|
|
1901
|
-
`received multiple handshake messages from pending session`,
|
|
1902
|
-
{
|
|
1903
|
-
...pendingSession.loggingMetadata,
|
|
1904
|
-
connectedTo: msg.from,
|
|
1905
|
-
transportMessage: msg
|
|
1906
|
-
}
|
|
1907
|
-
);
|
|
1908
|
-
this.deletePendingSession(pendingSession);
|
|
1909
|
-
return;
|
|
1910
|
-
}
|
|
1911
|
-
receivedHandshake = true;
|
|
1912
|
-
void this.onHandshakeRequest(pendingSession, msg);
|
|
1913
|
-
},
|
|
1914
|
-
onInvalidHandshake: (reason, code) => {
|
|
1915
|
-
this.log?.error(
|
|
1916
|
-
`invalid handshake: ${reason}`,
|
|
1917
|
-
pendingSession.loggingMetadata
|
|
1918
|
-
);
|
|
1919
|
-
this.deletePendingSession(pendingSession);
|
|
1920
|
-
this.protocolError({
|
|
1921
|
-
type: ProtocolError.HandshakeFailed,
|
|
1922
|
-
code,
|
|
1923
|
-
message: reason
|
|
1924
|
-
});
|
|
1925
|
-
}
|
|
1926
|
-
},
|
|
1927
|
-
this.options,
|
|
1928
|
-
this.tracer,
|
|
1929
|
-
this.log
|
|
1930
|
-
);
|
|
1931
|
-
this.pendingSessions.add(pendingSession);
|
|
1932
|
-
}
|
|
1933
|
-
rejectHandshakeRequest(session, to, reason, code, metadata) {
|
|
1934
|
-
session.conn.telemetry?.span.setStatus({
|
|
1935
|
-
code: import_api5.SpanStatusCode.ERROR,
|
|
1936
|
-
message: reason
|
|
1937
|
-
});
|
|
1938
|
-
this.log?.warn(reason, metadata);
|
|
1939
|
-
const responseMsg = handshakeResponseMessage({
|
|
1940
|
-
from: this.clientId,
|
|
1941
|
-
to,
|
|
1942
|
-
status: {
|
|
1943
|
-
ok: false,
|
|
1944
|
-
code,
|
|
1945
|
-
reason
|
|
1946
|
-
}
|
|
1947
|
-
});
|
|
1948
|
-
const res = session.sendHandshake(responseMsg);
|
|
1949
|
-
if (!res.ok) {
|
|
1950
|
-
this.log?.error(`failed to send handshake response: ${res.reason}`, {
|
|
1951
|
-
...session.loggingMetadata,
|
|
1952
|
-
transportMessage: responseMsg
|
|
1953
|
-
});
|
|
1954
|
-
this.protocolError({
|
|
1955
|
-
type: ProtocolError.MessageSendFailure,
|
|
1956
|
-
message: res.reason
|
|
1957
|
-
});
|
|
1958
|
-
this.deletePendingSession(session);
|
|
1959
|
-
return;
|
|
1960
|
-
}
|
|
1961
|
-
this.protocolError({
|
|
1962
|
-
type: ProtocolError.HandshakeFailed,
|
|
1963
|
-
code,
|
|
1964
|
-
message: reason
|
|
1965
|
-
});
|
|
1966
|
-
this.deletePendingSession(session);
|
|
1967
|
-
}
|
|
1968
|
-
async onHandshakeRequest(session, msg) {
|
|
1969
|
-
if (!import_value2.Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {
|
|
1970
|
-
this.rejectHandshakeRequest(
|
|
1971
|
-
session,
|
|
1972
|
-
msg.from,
|
|
1973
|
-
"received invalid handshake request",
|
|
1974
|
-
"MALFORMED_HANDSHAKE",
|
|
1975
|
-
{
|
|
1976
|
-
...session.loggingMetadata,
|
|
1977
|
-
transportMessage: msg,
|
|
1978
|
-
connectedTo: msg.from,
|
|
1979
|
-
validationErrors: [
|
|
1980
|
-
...import_value2.Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload)
|
|
1981
|
-
]
|
|
1982
|
-
}
|
|
1983
|
-
);
|
|
1984
|
-
return;
|
|
1985
|
-
}
|
|
1986
|
-
const gotVersion = msg.payload.protocolVersion;
|
|
1987
|
-
if (!isAcceptedProtocolVersion(gotVersion)) {
|
|
1988
|
-
this.rejectHandshakeRequest(
|
|
1989
|
-
session,
|
|
1990
|
-
msg.from,
|
|
1991
|
-
`expected protocol version oneof [${acceptedProtocolVersions.toString()}], got ${gotVersion}`,
|
|
1992
|
-
"PROTOCOL_VERSION_MISMATCH",
|
|
1993
|
-
{
|
|
1994
|
-
...session.loggingMetadata,
|
|
1995
|
-
connectedTo: msg.from,
|
|
1996
|
-
transportMessage: msg
|
|
1997
|
-
}
|
|
1998
|
-
);
|
|
1999
|
-
return;
|
|
2000
|
-
}
|
|
2001
|
-
let parsedMetadata = {};
|
|
2002
|
-
if (this.handshakeExtensions) {
|
|
2003
|
-
if (!import_value2.Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
|
|
2004
|
-
this.rejectHandshakeRequest(
|
|
2005
|
-
session,
|
|
2006
|
-
msg.from,
|
|
2007
|
-
"received malformed handshake metadata",
|
|
2008
|
-
"MALFORMED_HANDSHAKE_META",
|
|
2009
|
-
{
|
|
2010
|
-
...session.loggingMetadata,
|
|
2011
|
-
connectedTo: msg.from,
|
|
2012
|
-
validationErrors: [
|
|
2013
|
-
...import_value2.Value.Errors(
|
|
2014
|
-
this.handshakeExtensions.schema,
|
|
2015
|
-
msg.payload.metadata
|
|
2016
|
-
)
|
|
2017
|
-
]
|
|
2018
|
-
}
|
|
2019
|
-
);
|
|
2020
|
-
return;
|
|
2021
|
-
}
|
|
2022
|
-
const previousParsedMetadata = this.sessionHandshakeMetadata.get(
|
|
2023
|
-
msg.from
|
|
2024
|
-
);
|
|
2025
|
-
let parsedMetadataOrFailureCode;
|
|
2026
|
-
try {
|
|
2027
|
-
parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(
|
|
2028
|
-
msg.payload.metadata,
|
|
2029
|
-
previousParsedMetadata
|
|
2030
|
-
);
|
|
2031
|
-
} catch (err) {
|
|
2032
|
-
this.rejectHandshakeRequest(
|
|
2033
|
-
session,
|
|
2034
|
-
msg.from,
|
|
2035
|
-
`handshake validation threw: ${coerceErrorString(err)}`,
|
|
2036
|
-
"REJECTED_BY_CUSTOM_HANDLER",
|
|
2037
|
-
{
|
|
2038
|
-
...session.loggingMetadata,
|
|
2039
|
-
connectedTo: msg.from,
|
|
2040
|
-
clientId: this.clientId
|
|
2041
|
-
}
|
|
2042
|
-
);
|
|
2043
|
-
return;
|
|
2044
|
-
}
|
|
2045
|
-
if (session._isConsumed) {
|
|
2046
|
-
return;
|
|
2047
|
-
}
|
|
2048
|
-
if (import_value2.Value.Check(
|
|
2049
|
-
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
2050
|
-
parsedMetadataOrFailureCode
|
|
2051
|
-
)) {
|
|
2052
|
-
this.rejectHandshakeRequest(
|
|
2053
|
-
session,
|
|
2054
|
-
msg.from,
|
|
2055
|
-
"rejected by handshake handler",
|
|
2056
|
-
parsedMetadataOrFailureCode,
|
|
2057
|
-
{
|
|
2058
|
-
...session.loggingMetadata,
|
|
2059
|
-
connectedTo: msg.from,
|
|
2060
|
-
clientId: this.clientId
|
|
2061
|
-
}
|
|
2062
|
-
);
|
|
2063
|
-
return;
|
|
2064
|
-
}
|
|
2065
|
-
parsedMetadata = parsedMetadataOrFailureCode;
|
|
2066
|
-
}
|
|
2067
|
-
let connectCase = "new session";
|
|
2068
|
-
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
2069
|
-
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
|
|
2070
|
-
let oldSession = this.sessions.get(msg.from);
|
|
2071
|
-
if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
|
|
2072
|
-
connectCase = "transparent reconnection";
|
|
2073
|
-
const ourNextSeq = oldSession.nextSeq();
|
|
2074
|
-
const ourAck = oldSession.ack;
|
|
2075
|
-
if (clientNextSentSeq > ourAck) {
|
|
2076
|
-
this.rejectHandshakeRequest(
|
|
2077
|
-
session,
|
|
2078
|
-
msg.from,
|
|
2079
|
-
`client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
|
|
2080
|
-
"SESSION_STATE_MISMATCH",
|
|
2081
|
-
{
|
|
2082
|
-
...session.loggingMetadata,
|
|
2083
|
-
connectedTo: msg.from,
|
|
2084
|
-
transportMessage: msg
|
|
2085
|
-
}
|
|
2086
|
-
);
|
|
2087
|
-
return;
|
|
2088
|
-
}
|
|
2089
|
-
if (ourNextSeq > clientNextExpectedSeq) {
|
|
2090
|
-
this.rejectHandshakeRequest(
|
|
2091
|
-
session,
|
|
2092
|
-
msg.from,
|
|
2093
|
-
`server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
|
|
2094
|
-
"SESSION_STATE_MISMATCH",
|
|
2095
|
-
{
|
|
2096
|
-
...session.loggingMetadata,
|
|
2097
|
-
connectedTo: msg.from,
|
|
2098
|
-
transportMessage: msg
|
|
2099
|
-
}
|
|
2100
|
-
);
|
|
2101
|
-
return;
|
|
2102
|
-
}
|
|
2103
|
-
if (oldSession.state !== "NoConnection" /* NoConnection */) {
|
|
2104
|
-
const noConnectionSession = ServerSessionStateGraph.transition.ConnectedToNoConnection(
|
|
2105
|
-
oldSession,
|
|
2106
|
-
{
|
|
2107
|
-
onSessionGracePeriodElapsed: () => {
|
|
2108
|
-
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
2109
|
-
},
|
|
2110
|
-
onMessageSendFailure: (msg2, reason) => {
|
|
2111
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
2112
|
-
...noConnectionSession.loggingMetadata,
|
|
2113
|
-
transportMessage: msg2
|
|
2114
|
-
});
|
|
2115
|
-
this.protocolError({
|
|
2116
|
-
type: ProtocolError.MessageSendFailure,
|
|
2117
|
-
message: reason
|
|
2118
|
-
});
|
|
2119
|
-
this.deleteSession(noConnectionSession, { unhealthy: true });
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
);
|
|
2123
|
-
oldSession = noConnectionSession;
|
|
2124
|
-
this.updateSession(oldSession);
|
|
2125
|
-
}
|
|
2126
|
-
} else if (oldSession) {
|
|
2127
|
-
connectCase = "hard reconnection";
|
|
2128
|
-
this.log?.info(
|
|
2129
|
-
`client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,
|
|
2130
|
-
{
|
|
2131
|
-
...session.loggingMetadata,
|
|
2132
|
-
connectedTo: msg.from,
|
|
2133
|
-
sessionId: msg.payload.sessionId
|
|
2134
|
-
}
|
|
2135
|
-
);
|
|
2136
|
-
this.deleteSession(oldSession);
|
|
2137
|
-
oldSession = void 0;
|
|
2138
|
-
}
|
|
2139
|
-
if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {
|
|
2140
|
-
connectCase = "unknown session";
|
|
2141
|
-
const rejectionMessage = this.options.enableTransparentSessionReconnects ? `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}` : `client is attempting a transparent reconnect to a session but the server does not support it: ${msg.payload.sessionId}`;
|
|
2142
|
-
this.rejectHandshakeRequest(
|
|
2143
|
-
session,
|
|
2144
|
-
msg.from,
|
|
2145
|
-
rejectionMessage,
|
|
2146
|
-
"SESSION_STATE_MISMATCH",
|
|
2147
|
-
{
|
|
2148
|
-
...session.loggingMetadata,
|
|
2149
|
-
connectedTo: msg.from,
|
|
2150
|
-
transportMessage: msg
|
|
2151
|
-
}
|
|
2152
|
-
);
|
|
2153
|
-
return;
|
|
2154
|
-
}
|
|
2155
|
-
const sessionId = msg.payload.sessionId;
|
|
2156
|
-
this.log?.info(
|
|
2157
|
-
`handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,
|
|
2158
|
-
{
|
|
2159
|
-
...session.loggingMetadata,
|
|
2160
|
-
connectedTo: msg.from
|
|
2161
|
-
}
|
|
2162
|
-
);
|
|
2163
|
-
const responseMsg = handshakeResponseMessage({
|
|
2164
|
-
from: this.clientId,
|
|
2165
|
-
to: msg.from,
|
|
2166
|
-
status: {
|
|
2167
|
-
ok: true,
|
|
2168
|
-
sessionId
|
|
2169
|
-
}
|
|
2170
|
-
});
|
|
2171
|
-
const res = session.sendHandshake(responseMsg);
|
|
2172
|
-
if (!res.ok) {
|
|
2173
|
-
this.log?.error(`failed to send handshake response: ${res.reason}`, {
|
|
2174
|
-
...session.loggingMetadata,
|
|
2175
|
-
transportMessage: responseMsg
|
|
2176
|
-
});
|
|
2177
|
-
this.protocolError({
|
|
2178
|
-
type: ProtocolError.MessageSendFailure,
|
|
2179
|
-
message: res.reason
|
|
2180
|
-
});
|
|
2181
|
-
this.deletePendingSession(session);
|
|
2182
|
-
return;
|
|
2183
|
-
}
|
|
2184
|
-
this.pendingSessions.delete(session);
|
|
2185
|
-
const connectedSession = ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(
|
|
2186
|
-
session,
|
|
2187
|
-
// by this point oldSession is either no connection or we dont have an old session
|
|
2188
|
-
oldSession,
|
|
2189
|
-
sessionId,
|
|
2190
|
-
msg.from,
|
|
2191
|
-
msg.tracing,
|
|
2192
|
-
{
|
|
2193
|
-
onConnectionErrored: (err) => {
|
|
2194
|
-
const errStr = coerceErrorString(err);
|
|
2195
|
-
this.log?.warn(
|
|
2196
|
-
`connection to ${connectedSession.to} errored: ${errStr}`,
|
|
2197
|
-
connectedSession.loggingMetadata
|
|
2198
|
-
);
|
|
2199
|
-
},
|
|
2200
|
-
onConnectionClosed: () => {
|
|
2201
|
-
this.log?.info(
|
|
2202
|
-
`connection to ${connectedSession.to} closed`,
|
|
2203
|
-
connectedSession.loggingMetadata
|
|
2204
|
-
);
|
|
2205
|
-
this.onConnClosed(connectedSession);
|
|
2206
|
-
},
|
|
2207
|
-
onMessage: (msg2) => {
|
|
2208
|
-
this.handleMsg(msg2);
|
|
2209
|
-
},
|
|
2210
|
-
onInvalidMessage: (reason) => {
|
|
2211
|
-
this.log?.error(`invalid message: ${reason}`, {
|
|
2212
|
-
...connectedSession.loggingMetadata,
|
|
2213
|
-
transportMessage: msg
|
|
2214
|
-
});
|
|
2215
|
-
this.protocolError({
|
|
2216
|
-
type: ProtocolError.InvalidMessage,
|
|
2217
|
-
message: reason
|
|
2218
|
-
});
|
|
2219
|
-
this.deleteSession(connectedSession, { unhealthy: true });
|
|
2220
|
-
},
|
|
2221
|
-
onMessageSendFailure: (msg2, reason) => {
|
|
2222
|
-
this.log?.error(`failed to send message: ${reason}`, {
|
|
2223
|
-
...connectedSession.loggingMetadata,
|
|
2224
|
-
transportMessage: msg2
|
|
2225
|
-
});
|
|
2226
|
-
this.protocolError({
|
|
2227
|
-
type: ProtocolError.MessageSendFailure,
|
|
2228
|
-
message: reason
|
|
2229
|
-
});
|
|
2230
|
-
this.deleteSession(connectedSession, { unhealthy: true });
|
|
2231
|
-
}
|
|
2232
|
-
},
|
|
2233
|
-
gotVersion
|
|
2234
|
-
);
|
|
2235
|
-
const bufferSendRes = connectedSession.sendBufferedMessages();
|
|
2236
|
-
if (!bufferSendRes.ok) {
|
|
2237
|
-
return;
|
|
2238
|
-
}
|
|
2239
|
-
this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
|
|
2240
|
-
if (oldSession) {
|
|
2241
|
-
this.updateSession(connectedSession);
|
|
2242
|
-
} else {
|
|
2243
|
-
this.createSession(connectedSession);
|
|
2244
|
-
}
|
|
2245
|
-
connectedSession.startActiveHeartbeat();
|
|
2246
|
-
}
|
|
2247
|
-
};
|
|
2248
|
-
|
|
2249
|
-
// transport/connection.ts
|
|
2250
|
-
var Connection = class {
|
|
2251
|
-
id;
|
|
2252
|
-
telemetry;
|
|
2253
|
-
constructor() {
|
|
2254
|
-
this.id = `conn-${generateId()}`;
|
|
2255
|
-
}
|
|
2256
|
-
get loggingMetadata() {
|
|
2257
|
-
const metadata = { connId: this.id };
|
|
2258
|
-
if (this.telemetry?.span.isRecording()) {
|
|
2259
|
-
const spanContext = this.telemetry.span.spanContext();
|
|
2260
|
-
metadata.telemetry = {
|
|
2261
|
-
traceId: spanContext.traceId,
|
|
2262
|
-
spanId: spanContext.spanId
|
|
2263
|
-
};
|
|
2264
|
-
}
|
|
2265
|
-
return metadata;
|
|
2266
|
-
}
|
|
2267
|
-
dataListener;
|
|
2268
|
-
closeListener;
|
|
2269
|
-
errorListener;
|
|
2270
|
-
onData(msg) {
|
|
2271
|
-
this.dataListener?.(msg);
|
|
2272
|
-
}
|
|
2273
|
-
onError(err) {
|
|
2274
|
-
this.errorListener?.(err);
|
|
2275
|
-
}
|
|
2276
|
-
onClose() {
|
|
2277
|
-
this.closeListener?.();
|
|
2278
|
-
this.telemetry?.span.end();
|
|
2279
|
-
}
|
|
2280
|
-
/**
|
|
2281
|
-
* Set the callback for when a message is received.
|
|
2282
|
-
* @param cb The message handler callback.
|
|
2283
|
-
*/
|
|
2284
|
-
setDataListener(cb) {
|
|
2285
|
-
this.dataListener = cb;
|
|
2286
|
-
}
|
|
2287
|
-
removeDataListener() {
|
|
2288
|
-
this.dataListener = void 0;
|
|
2289
|
-
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Set the callback for when the connection is closed.
|
|
2292
|
-
* This should also be called if an error happens and after notifying the error listener.
|
|
2293
|
-
* @param cb The callback to call when the connection is closed.
|
|
2294
|
-
*/
|
|
2295
|
-
setCloseListener(cb) {
|
|
2296
|
-
this.closeListener = cb;
|
|
2297
|
-
}
|
|
2298
|
-
removeCloseListener() {
|
|
2299
|
-
this.closeListener = void 0;
|
|
2300
|
-
}
|
|
2301
|
-
/**
|
|
2302
|
-
* Set the callback for when an error is received.
|
|
2303
|
-
* This should only be used for logging errors, all cleanup
|
|
2304
|
-
* should be delegated to setCloseListener.
|
|
2305
|
-
*
|
|
2306
|
-
* The implementer should take care such that the implemented
|
|
2307
|
-
* connection will call both the close and error callbacks
|
|
2308
|
-
* on an error.
|
|
2309
|
-
*
|
|
2310
|
-
* @param cb The callback to call when an error is received.
|
|
2311
|
-
*/
|
|
2312
|
-
setErrorListener(cb) {
|
|
2313
|
-
this.errorListener = cb;
|
|
2314
|
-
}
|
|
2315
|
-
removeErrorListener() {
|
|
2316
|
-
this.errorListener = void 0;
|
|
2317
|
-
}
|
|
2318
|
-
};
|
|
2319
|
-
|
|
2320
|
-
// codec/adapter.ts
|
|
2321
|
-
var CodecMessageAdapter = class {
|
|
2322
|
-
constructor(codec) {
|
|
2323
|
-
this.codec = codec;
|
|
2324
|
-
}
|
|
2325
|
-
toBuffer(msg) {
|
|
2326
|
-
try {
|
|
2327
|
-
return {
|
|
2328
|
-
ok: true,
|
|
2329
|
-
value: this.codec.toBuffer(msg)
|
|
2330
|
-
};
|
|
2331
|
-
} catch (e) {
|
|
2332
|
-
return {
|
|
2333
|
-
ok: false,
|
|
2334
|
-
reason: coerceErrorString(e)
|
|
2335
|
-
};
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
fromBuffer(buf) {
|
|
2339
|
-
try {
|
|
2340
|
-
const parsedMsg = this.codec.fromBuffer(buf);
|
|
2341
|
-
if (!import_value3.Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
|
|
2342
|
-
return {
|
|
2343
|
-
ok: false,
|
|
2344
|
-
reason: "transport message schema mismatch"
|
|
2345
|
-
};
|
|
2346
|
-
}
|
|
2347
|
-
return {
|
|
2348
|
-
ok: true,
|
|
2349
|
-
value: parsedMsg
|
|
2350
|
-
};
|
|
2351
|
-
} catch (e) {
|
|
2352
|
-
return {
|
|
2353
|
-
ok: false,
|
|
2354
|
-
reason: coerceErrorString(e)
|
|
2355
|
-
};
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
};
|
|
2359
|
-
|
|
2360
|
-
// transport/sessionStateMachine/transitions.ts
|
|
2361
|
-
function inheritSharedSession(session) {
|
|
2362
|
-
return {
|
|
2363
|
-
id: session.id,
|
|
2364
|
-
from: session.from,
|
|
2365
|
-
to: session.to,
|
|
2366
|
-
seq: session.seq,
|
|
2367
|
-
ack: session.ack,
|
|
2368
|
-
seqSent: session.seqSent,
|
|
2369
|
-
sendBuffer: session.sendBuffer,
|
|
2370
|
-
telemetry: session.telemetry,
|
|
2371
|
-
options: session.options,
|
|
2372
|
-
log: session.log,
|
|
2373
|
-
tracer: session.tracer,
|
|
2374
|
-
protocolVersion: session.protocolVersion,
|
|
2375
|
-
codec: session.codec
|
|
2376
|
-
};
|
|
2377
|
-
}
|
|
2378
|
-
function inheritSharedSessionWithGrace(session) {
|
|
2379
|
-
return {
|
|
2380
|
-
...inheritSharedSession(session),
|
|
2381
|
-
graceExpiryTime: session.graceExpiryTime
|
|
2382
|
-
};
|
|
2383
|
-
}
|
|
2384
|
-
var SessionStateGraph = {
|
|
2385
|
-
entrypoints: {
|
|
2386
|
-
NoConnection: (to, from, listeners, options, protocolVersion, tracer, log) => {
|
|
2387
|
-
const id = `session-${generateId()}`;
|
|
2388
|
-
const telemetry = createSessionTelemetryInfo(tracer, id, to, from);
|
|
2389
|
-
const sendBuffer = [];
|
|
2390
|
-
const session = new SessionNoConnection({
|
|
2391
|
-
listeners,
|
|
2392
|
-
id,
|
|
2393
|
-
from,
|
|
2394
|
-
to,
|
|
2395
|
-
seq: 0,
|
|
2396
|
-
ack: 0,
|
|
2397
|
-
seqSent: 0,
|
|
2398
|
-
graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
|
|
2399
|
-
sendBuffer,
|
|
2400
|
-
telemetry,
|
|
2401
|
-
options,
|
|
2402
|
-
protocolVersion,
|
|
2403
|
-
tracer,
|
|
2404
|
-
log,
|
|
2405
|
-
codec: new CodecMessageAdapter(options.codec)
|
|
2406
|
-
});
|
|
2407
|
-
session.log?.info(`session ${session.id} created in NoConnection state`, {
|
|
2408
|
-
...session.loggingMetadata,
|
|
2409
|
-
tags: ["state-transition"]
|
|
2410
|
-
});
|
|
2411
|
-
return session;
|
|
2412
|
-
},
|
|
2413
|
-
WaitingForHandshake: (from, conn, listeners, options, tracer, log) => {
|
|
2414
|
-
const session = new SessionWaitingForHandshake({
|
|
2415
|
-
conn,
|
|
2416
|
-
listeners,
|
|
2417
|
-
from,
|
|
2418
|
-
options,
|
|
2419
|
-
tracer,
|
|
2420
|
-
log,
|
|
2421
|
-
codec: new CodecMessageAdapter(options.codec)
|
|
2422
|
-
});
|
|
2423
|
-
session.log?.info(`session created in WaitingForHandshake state`, {
|
|
2424
|
-
...session.loggingMetadata,
|
|
2425
|
-
tags: ["state-transition"]
|
|
2426
|
-
});
|
|
2427
|
-
return session;
|
|
2428
|
-
}
|
|
2429
|
-
},
|
|
2430
|
-
// All of the transitions 'move'/'consume' the old session and return a new one.
|
|
2431
|
-
// After a session is transitioned, any usage of the old session will throw.
|
|
2432
|
-
transition: {
|
|
2433
|
-
// happy path transitions
|
|
2434
|
-
NoConnectionToBackingOff: (oldSession, backoffMs, listeners) => {
|
|
2435
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2436
|
-
oldSession._handleStateExit();
|
|
2437
|
-
const session = new SessionBackingOff({
|
|
2438
|
-
backoffMs,
|
|
2439
|
-
listeners,
|
|
2440
|
-
...carriedState
|
|
2441
|
-
});
|
|
2442
|
-
session.log?.info(
|
|
2443
|
-
`session ${session.id} transition from NoConnection to BackingOff`,
|
|
2444
|
-
{
|
|
2445
|
-
...session.loggingMetadata,
|
|
2446
|
-
tags: ["state-transition"]
|
|
2447
|
-
}
|
|
2448
|
-
);
|
|
2449
|
-
return session;
|
|
2450
|
-
},
|
|
2451
|
-
BackingOffToConnecting: (oldSession, connPromise, listeners) => {
|
|
2452
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2453
|
-
oldSession._handleStateExit();
|
|
2454
|
-
const session = new SessionConnecting({
|
|
2455
|
-
connPromise,
|
|
2456
|
-
listeners,
|
|
2457
|
-
...carriedState
|
|
2458
|
-
});
|
|
2459
|
-
session.log?.info(
|
|
2460
|
-
`session ${session.id} transition from BackingOff to Connecting`,
|
|
2461
|
-
{
|
|
2462
|
-
...session.loggingMetadata,
|
|
2463
|
-
tags: ["state-transition"]
|
|
2464
|
-
}
|
|
2465
|
-
);
|
|
2466
|
-
return session;
|
|
2467
|
-
},
|
|
2468
|
-
ConnectingToHandshaking: (oldSession, conn, listeners) => {
|
|
2469
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2470
|
-
oldSession._handleStateExit();
|
|
2471
|
-
const session = new SessionHandshaking({
|
|
2472
|
-
conn,
|
|
2473
|
-
listeners,
|
|
2474
|
-
...carriedState
|
|
2475
|
-
});
|
|
2476
|
-
conn.telemetry = createConnectionTelemetryInfo(
|
|
2477
|
-
session.tracer,
|
|
2478
|
-
conn,
|
|
2479
|
-
session.telemetry
|
|
2480
|
-
);
|
|
2481
|
-
session.log?.info(
|
|
2482
|
-
`session ${session.id} transition from Connecting to Handshaking`,
|
|
2483
|
-
{
|
|
2484
|
-
...session.loggingMetadata,
|
|
2485
|
-
tags: ["state-transition"]
|
|
2486
|
-
}
|
|
2487
|
-
);
|
|
2488
|
-
return session;
|
|
2489
|
-
},
|
|
2490
|
-
HandshakingToConnected: (oldSession, listeners) => {
|
|
2491
|
-
const carriedState = inheritSharedSession(oldSession);
|
|
2492
|
-
const conn = oldSession.conn;
|
|
2493
|
-
oldSession._handleStateExit();
|
|
2494
|
-
const session = new SessionConnected({
|
|
2495
|
-
conn,
|
|
2496
|
-
listeners,
|
|
2497
|
-
...carriedState
|
|
2498
|
-
});
|
|
2499
|
-
session.startMissingHeartbeatTimeout();
|
|
2500
|
-
session.log?.info(
|
|
2501
|
-
`session ${session.id} transition from Handshaking to Connected`,
|
|
2502
|
-
{
|
|
2503
|
-
...session.loggingMetadata,
|
|
2504
|
-
tags: ["state-transition"]
|
|
2505
|
-
}
|
|
2506
|
-
);
|
|
2507
|
-
return session;
|
|
2508
|
-
},
|
|
2509
|
-
WaitingForHandshakeToConnected: (pendingSession, oldSession, sessionId, to, propagationCtx, listeners, protocolVersion) => {
|
|
2510
|
-
const conn = pendingSession.conn;
|
|
2511
|
-
const { from, options } = pendingSession;
|
|
2512
|
-
const carriedState = oldSession ? (
|
|
2513
|
-
// old session exists, inherit state
|
|
2514
|
-
inheritSharedSession(oldSession)
|
|
2515
|
-
) : (
|
|
2516
|
-
// old session does not exist, create new state
|
|
2517
|
-
{
|
|
2518
|
-
id: sessionId,
|
|
2519
|
-
from,
|
|
2520
|
-
to,
|
|
2521
|
-
seq: 0,
|
|
2522
|
-
ack: 0,
|
|
2523
|
-
seqSent: 0,
|
|
2524
|
-
sendBuffer: [],
|
|
2525
|
-
telemetry: createSessionTelemetryInfo(
|
|
2526
|
-
pendingSession.tracer,
|
|
2527
|
-
sessionId,
|
|
2528
|
-
to,
|
|
2529
|
-
from,
|
|
2530
|
-
propagationCtx
|
|
2531
|
-
),
|
|
2532
|
-
options,
|
|
2533
|
-
tracer: pendingSession.tracer,
|
|
2534
|
-
log: pendingSession.log,
|
|
2535
|
-
protocolVersion,
|
|
2536
|
-
codec: new CodecMessageAdapter(options.codec)
|
|
2537
|
-
}
|
|
2538
|
-
);
|
|
2539
|
-
pendingSession._handleStateExit();
|
|
2540
|
-
oldSession?._handleStateExit();
|
|
2541
|
-
const session = new SessionConnected({
|
|
2542
|
-
conn,
|
|
2543
|
-
listeners,
|
|
2544
|
-
...carriedState
|
|
2545
|
-
});
|
|
2546
|
-
session.startMissingHeartbeatTimeout();
|
|
2547
|
-
conn.telemetry = createConnectionTelemetryInfo(
|
|
2548
|
-
session.tracer,
|
|
2549
|
-
conn,
|
|
2550
|
-
session.telemetry
|
|
2551
|
-
);
|
|
2552
|
-
session.log?.info(
|
|
2553
|
-
`session ${session.id} transition from WaitingForHandshake to Connected`,
|
|
2554
|
-
{
|
|
2555
|
-
...session.loggingMetadata,
|
|
2556
|
-
tags: ["state-transition"]
|
|
2557
|
-
}
|
|
2558
|
-
);
|
|
2559
|
-
return session;
|
|
2560
|
-
},
|
|
2561
|
-
// disconnect paths
|
|
2562
|
-
BackingOffToNoConnection: (oldSession, listeners) => {
|
|
2563
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2564
|
-
oldSession._handleStateExit();
|
|
2565
|
-
const session = new SessionNoConnection({
|
|
2566
|
-
listeners,
|
|
2567
|
-
...carriedState
|
|
2568
|
-
});
|
|
2569
|
-
session.log?.info(
|
|
2570
|
-
`session ${session.id} transition from BackingOff to NoConnection`,
|
|
2571
|
-
{
|
|
2572
|
-
...session.loggingMetadata,
|
|
2573
|
-
tags: ["state-transition"]
|
|
2574
|
-
}
|
|
2575
|
-
);
|
|
2576
|
-
return session;
|
|
2577
|
-
},
|
|
2578
|
-
ConnectingToNoConnection: (oldSession, listeners) => {
|
|
2579
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2580
|
-
oldSession.bestEffortClose();
|
|
2581
|
-
oldSession._handleStateExit();
|
|
2582
|
-
const session = new SessionNoConnection({
|
|
2583
|
-
listeners,
|
|
2584
|
-
...carriedState
|
|
2585
|
-
});
|
|
2586
|
-
session.log?.info(
|
|
2587
|
-
`session ${session.id} transition from Connecting to NoConnection`,
|
|
2588
|
-
{
|
|
2589
|
-
...session.loggingMetadata,
|
|
2590
|
-
tags: ["state-transition"]
|
|
2591
|
-
}
|
|
2592
|
-
);
|
|
2593
|
-
return session;
|
|
2594
|
-
},
|
|
2595
|
-
HandshakingToNoConnection: (oldSession, listeners) => {
|
|
2596
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2597
|
-
oldSession.conn.close();
|
|
2598
|
-
oldSession._handleStateExit();
|
|
2599
|
-
const session = new SessionNoConnection({
|
|
2600
|
-
listeners,
|
|
2601
|
-
...carriedState
|
|
2602
|
-
});
|
|
2603
|
-
session.log?.info(
|
|
2604
|
-
`session ${session.id} transition from Handshaking to NoConnection`,
|
|
2605
|
-
{
|
|
2606
|
-
...session.loggingMetadata,
|
|
2607
|
-
tags: ["state-transition"]
|
|
2608
|
-
}
|
|
2609
|
-
);
|
|
2610
|
-
return session;
|
|
2611
|
-
},
|
|
2612
|
-
ConnectedToNoConnection: (oldSession, listeners) => {
|
|
2613
|
-
const carriedState = inheritSharedSession(oldSession);
|
|
2614
|
-
const graceExpiryTime = Date.now() + oldSession.options.sessionDisconnectGraceMs;
|
|
2615
|
-
oldSession.conn.close();
|
|
2616
|
-
oldSession._handleStateExit();
|
|
2617
|
-
const session = new SessionNoConnection({
|
|
2618
|
-
listeners,
|
|
2619
|
-
graceExpiryTime,
|
|
2620
|
-
...carriedState
|
|
2621
|
-
});
|
|
2622
|
-
session.log?.info(
|
|
2623
|
-
`session ${session.id} transition from Connected to NoConnection`,
|
|
2624
|
-
{
|
|
2625
|
-
...session.loggingMetadata,
|
|
2626
|
-
tags: ["state-transition"]
|
|
2627
|
-
}
|
|
2628
|
-
);
|
|
2629
|
-
return session;
|
|
2630
|
-
}
|
|
2631
|
-
}
|
|
2632
|
-
};
|
|
2633
|
-
var transitions = SessionStateGraph.transition;
|
|
2634
|
-
var ClientSessionStateGraph = {
|
|
2635
|
-
entrypoint: SessionStateGraph.entrypoints.NoConnection,
|
|
2636
|
-
transition: {
|
|
2637
|
-
// happy paths
|
|
2638
|
-
// NoConnection -> BackingOff: attempt to connect
|
|
2639
|
-
NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,
|
|
2640
|
-
// BackingOff -> Connecting: backoff period elapsed, start connection
|
|
2641
|
-
BackingOffToConnecting: transitions.BackingOffToConnecting,
|
|
2642
|
-
// Connecting -> Handshaking: connection established, start handshake
|
|
2643
|
-
ConnectingToHandshaking: transitions.ConnectingToHandshaking,
|
|
2644
|
-
// Handshaking -> Connected: handshake complete, session ready
|
|
2645
|
-
HandshakingToConnected: transitions.HandshakingToConnected,
|
|
2646
|
-
// disconnect paths
|
|
2647
|
-
// BackingOff -> NoConnection: unused
|
|
2648
|
-
BackingOffToNoConnection: transitions.BackingOffToNoConnection,
|
|
2649
|
-
// Connecting -> NoConnection: connection failed or connection timeout
|
|
2650
|
-
ConnectingToNoConnection: transitions.ConnectingToNoConnection,
|
|
2651
|
-
// Handshaking -> NoConnection: connection closed or handshake timeout
|
|
2652
|
-
HandshakingToNoConnection: transitions.HandshakingToNoConnection,
|
|
2653
|
-
// Connected -> NoConnection: connection closed
|
|
2654
|
-
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
2655
|
-
// destroy/close paths
|
|
2656
|
-
// NoConnection -> x: grace period elapsed
|
|
2657
|
-
// BackingOff -> x: grace period elapsed
|
|
2658
|
-
// Connecting -> x: grace period elapsed
|
|
2659
|
-
// Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection
|
|
2660
|
-
// Connected -> x: grace period elapsed or invalid message
|
|
2661
|
-
}
|
|
2662
|
-
};
|
|
2663
|
-
var ServerSessionStateGraph = {
|
|
2664
|
-
entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,
|
|
2665
|
-
transition: {
|
|
2666
|
-
// happy paths
|
|
2667
|
-
// WaitingForHandshake -> Connected: handshake complete, session ready
|
|
2668
|
-
WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,
|
|
2669
|
-
// disconnect paths
|
|
2670
|
-
// Connected -> NoConnection: connection closed
|
|
2671
|
-
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
2672
|
-
// destroy/close paths
|
|
2673
|
-
// WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed
|
|
2674
|
-
}
|
|
2675
|
-
};
|
|
2676
|
-
|
|
2677
|
-
// testUtil/observable/observable.ts
|
|
2678
|
-
var Observable = class {
|
|
2679
|
-
value;
|
|
2680
|
-
listeners;
|
|
2681
|
-
constructor(initialValue) {
|
|
2682
|
-
this.value = initialValue;
|
|
2683
|
-
this.listeners = /* @__PURE__ */ new Set();
|
|
2684
|
-
}
|
|
2685
|
-
/**
|
|
2686
|
-
* Gets the current value of the observable.
|
|
2687
|
-
*/
|
|
2688
|
-
get() {
|
|
2689
|
-
return this.value;
|
|
2690
|
-
}
|
|
2691
|
-
/**
|
|
2692
|
-
* Sets the current value of the observable. All listeners will get an update with this value.
|
|
2693
|
-
* @param newValue - The new value to set.
|
|
2694
|
-
*/
|
|
2695
|
-
set(tx) {
|
|
2696
|
-
const newValue = tx(this.value);
|
|
2697
|
-
this.value = newValue;
|
|
2698
|
-
this.listeners.forEach((listener) => listener(newValue));
|
|
2699
|
-
}
|
|
2700
|
-
/**
|
|
2701
|
-
* Subscribes to changes in the observable value.
|
|
2702
|
-
* @param listener - A callback function that will be called when the value changes.
|
|
2703
|
-
* @returns A function that can be called to unsubscribe from further notifications.
|
|
2704
|
-
*/
|
|
2705
|
-
observe(listener) {
|
|
2706
|
-
this.listeners.add(listener);
|
|
2707
|
-
listener(this.get());
|
|
2708
|
-
return () => this.listeners.delete(listener);
|
|
2709
|
-
}
|
|
2710
|
-
/**
|
|
2711
|
-
* Returns the number of listeners currently observing the observable
|
|
2712
|
-
*/
|
|
2713
|
-
get listenerCount() {
|
|
2714
|
-
return this.listeners.size;
|
|
2715
|
-
}
|
|
2716
|
-
};
|
|
2717
|
-
|
|
2718
|
-
// testUtil/duplex/duplexPair.ts
|
|
2719
|
-
var import_node_stream = require("stream");
|
|
2720
|
-
var import_assert = __toESM(require("assert"), 1);
|
|
2721
|
-
var kCallback = Symbol("Callback");
|
|
2722
|
-
var kInitOtherSide = Symbol("InitOtherSide");
|
|
2723
|
-
var DuplexSide = class extends import_node_stream.Duplex {
|
|
2724
|
-
otherSide;
|
|
2725
|
-
[kCallback];
|
|
2726
|
-
constructor() {
|
|
2727
|
-
super();
|
|
2728
|
-
this[kCallback] = null;
|
|
2729
|
-
this.otherSide = null;
|
|
2730
|
-
}
|
|
2731
|
-
[kInitOtherSide](otherSide) {
|
|
2732
|
-
if (this.otherSide === null) {
|
|
2733
|
-
this.otherSide = otherSide;
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
_read() {
|
|
2737
|
-
const callback = this[kCallback];
|
|
2738
|
-
if (callback) {
|
|
2739
|
-
this[kCallback] = null;
|
|
2740
|
-
callback();
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
_write(chunk, _encoding, callback) {
|
|
2744
|
-
(0, import_assert.default)(this.otherSide !== null);
|
|
2745
|
-
(0, import_assert.default)(this.otherSide[kCallback] === null);
|
|
2746
|
-
if (chunk.length === 0) {
|
|
2747
|
-
process.nextTick(callback);
|
|
2748
|
-
} else {
|
|
2749
|
-
this.otherSide.push(chunk);
|
|
2750
|
-
this.otherSide[kCallback] = callback;
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
_final(callback) {
|
|
2754
|
-
this.otherSide?.on("end", callback);
|
|
2755
|
-
this.otherSide?.push(null);
|
|
2756
|
-
}
|
|
2757
|
-
};
|
|
2758
|
-
function duplexPair() {
|
|
2759
|
-
const side0 = new DuplexSide();
|
|
2760
|
-
const side1 = new DuplexSide();
|
|
2761
|
-
side0[kInitOtherSide](side1);
|
|
2762
|
-
side1[kInitOtherSide](side0);
|
|
2763
|
-
side0.on("close", () => {
|
|
2764
|
-
setImmediate(() => {
|
|
2765
|
-
side1.destroy();
|
|
2766
|
-
});
|
|
2767
|
-
});
|
|
2768
|
-
side1.on("close", () => {
|
|
2769
|
-
setImmediate(() => {
|
|
2770
|
-
side0.destroy();
|
|
2771
|
-
});
|
|
2772
|
-
});
|
|
2773
|
-
return [side0, side1];
|
|
2774
|
-
}
|
|
2775
|
-
|
|
2776
|
-
// testUtil/fixtures/mockTransport.ts
|
|
2777
|
-
var import_nanoid2 = require("nanoid");
|
|
2778
|
-
var InMemoryConnection = class extends Connection {
|
|
2779
|
-
conn;
|
|
2780
|
-
constructor(pipe) {
|
|
2781
|
-
super();
|
|
2782
|
-
this.conn = pipe;
|
|
2783
|
-
this.conn.allowHalfOpen = false;
|
|
2784
|
-
this.conn.on("data", (data) => {
|
|
2785
|
-
this.dataListener?.(data);
|
|
2786
|
-
});
|
|
2787
|
-
this.conn.on("close", () => {
|
|
2788
|
-
this.closeListener?.();
|
|
2789
|
-
});
|
|
2790
|
-
this.conn.on("error", (err) => {
|
|
2791
|
-
this.errorListener?.(err);
|
|
2792
|
-
});
|
|
2793
|
-
}
|
|
2794
|
-
send(payload) {
|
|
2795
|
-
setImmediate(() => {
|
|
2796
|
-
this.conn.write(payload);
|
|
2797
|
-
});
|
|
2798
|
-
return true;
|
|
2799
|
-
}
|
|
2800
|
-
close() {
|
|
2801
|
-
setImmediate(() => {
|
|
2802
|
-
this.conn.end();
|
|
2803
|
-
this.conn.emit("close");
|
|
2804
|
-
});
|
|
2805
|
-
}
|
|
2806
|
-
};
|
|
2807
|
-
function createMockTransportNetwork(opts) {
|
|
2808
|
-
const connections = new Observable({});
|
|
2809
|
-
const transports = [];
|
|
2810
|
-
class MockClientTransport extends ClientTransport {
|
|
2811
|
-
async createNewOutgoingConnection(to) {
|
|
2812
|
-
const [clientToServer, serverToClient] = duplexPair();
|
|
2813
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
2814
|
-
const connId = (0, import_nanoid2.nanoid)();
|
|
2815
|
-
connections.set((prev) => ({
|
|
2816
|
-
...prev,
|
|
2817
|
-
[connId]: {
|
|
2818
|
-
id: connId,
|
|
2819
|
-
clientToServer,
|
|
2820
|
-
serverToClient,
|
|
2821
|
-
clientId: this.clientId,
|
|
2822
|
-
serverId: to,
|
|
2823
|
-
handled: false
|
|
2824
|
-
}
|
|
2825
|
-
}));
|
|
2826
|
-
return new InMemoryConnection(clientToServer);
|
|
2827
|
-
}
|
|
2828
|
-
}
|
|
2829
|
-
class MockServerTransport extends ServerTransport {
|
|
2830
|
-
subscribeCleanup;
|
|
2831
|
-
constructor(clientId, options) {
|
|
2832
|
-
super(clientId, options);
|
|
2833
|
-
this.subscribeCleanup = connections.observe((conns) => {
|
|
2834
|
-
for (const conn of Object.values(conns)) {
|
|
2835
|
-
if (conn.handled || conn.serverId !== this.clientId) {
|
|
2836
|
-
continue;
|
|
2837
|
-
}
|
|
2838
|
-
conn.handled = true;
|
|
2839
|
-
const connection = new InMemoryConnection(conn.serverToClient);
|
|
2840
|
-
this.handleConnection(connection);
|
|
2841
|
-
}
|
|
2842
|
-
});
|
|
2843
|
-
}
|
|
2844
|
-
close() {
|
|
2845
|
-
this.subscribeCleanup();
|
|
2846
|
-
super.close();
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
return {
|
|
2850
|
-
getClientTransport: (id, handshakeOptions) => {
|
|
2851
|
-
const clientTransport = new MockClientTransport(id, opts?.client);
|
|
2852
|
-
if (handshakeOptions) {
|
|
2853
|
-
clientTransport.extendHandshake(handshakeOptions);
|
|
2854
|
-
}
|
|
2855
|
-
transports.push(clientTransport);
|
|
2856
|
-
return clientTransport;
|
|
2857
|
-
},
|
|
2858
|
-
getServerTransport: (id = "SERVER", handshakeOptions) => {
|
|
2859
|
-
const serverTransport = new MockServerTransport(id, opts?.server);
|
|
2860
|
-
if (handshakeOptions) {
|
|
2861
|
-
serverTransport.extendHandshake(handshakeOptions);
|
|
2862
|
-
}
|
|
2863
|
-
transports.push(serverTransport);
|
|
2864
|
-
return serverTransport;
|
|
2865
|
-
},
|
|
2866
|
-
simulatePhantomDisconnect() {
|
|
2867
|
-
for (const conn of Object.values(connections.get())) {
|
|
2868
|
-
conn.serverToClient.pause();
|
|
2869
|
-
conn.clientToServer.pause();
|
|
2870
|
-
}
|
|
2871
|
-
},
|
|
2872
|
-
async restartServer() {
|
|
2873
|
-
for (const transport of transports) {
|
|
2874
|
-
if (transport.clientId !== "SERVER") continue;
|
|
2875
|
-
transport.close();
|
|
2876
|
-
}
|
|
2877
|
-
for (const conn of Object.values(connections.get())) {
|
|
2878
|
-
conn.serverToClient.destroy();
|
|
2879
|
-
conn.clientToServer.destroy();
|
|
2880
|
-
}
|
|
2881
|
-
},
|
|
2882
|
-
cleanup() {
|
|
2883
|
-
for (const conn of Object.values(connections.get())) {
|
|
2884
|
-
conn.serverToClient.destroy();
|
|
2885
|
-
conn.clientToServer.destroy();
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
};
|
|
2889
|
-
}
|
|
2890
|
-
|
|
2891
|
-
// testUtil/index.ts
|
|
2892
|
-
function createLocalWebSocketClient(port) {
|
|
2893
|
-
const sock = new import_ws.default(`ws://localhost:${port}`);
|
|
2894
|
-
sock.binaryType = "arraybuffer";
|
|
2895
|
-
return sock;
|
|
2896
|
-
}
|
|
2897
|
-
function createWebSocketServer(server) {
|
|
2898
|
-
return new import_ws.WebSocketServer({ server });
|
|
2899
|
-
}
|
|
2900
|
-
function onWsServerReady(server) {
|
|
2901
|
-
return new Promise((resolve, reject) => {
|
|
2902
|
-
server.listen(() => {
|
|
2903
|
-
const addr = server.address();
|
|
2904
|
-
if (typeof addr === "object" && addr) {
|
|
2905
|
-
resolve(addr.port);
|
|
2906
|
-
} else {
|
|
2907
|
-
reject(new Error("couldn't find a port to allocate"));
|
|
2908
|
-
}
|
|
2909
|
-
});
|
|
2910
|
-
});
|
|
2911
|
-
}
|
|
2912
|
-
var readableIterators = /* @__PURE__ */ new WeakMap();
|
|
2913
|
-
function getReadableIterator(readable) {
|
|
2914
|
-
let iter = readableIterators.get(readable);
|
|
2915
|
-
if (!iter) {
|
|
2916
|
-
iter = readable[Symbol.asyncIterator]();
|
|
2917
|
-
readableIterators.set(readable, iter);
|
|
2918
|
-
}
|
|
2919
|
-
return iter;
|
|
2920
|
-
}
|
|
2921
|
-
async function readNextResult(readable) {
|
|
2922
|
-
const res = await getReadableIterator(readable).next();
|
|
2923
|
-
if (res.done) {
|
|
2924
|
-
throw new Error("readNext from a done Readable");
|
|
2925
|
-
}
|
|
2926
|
-
return res.value;
|
|
2927
|
-
}
|
|
2928
|
-
async function isReadableDone(readable) {
|
|
2929
|
-
const res = await getReadableIterator(readable).next();
|
|
2930
|
-
return res.done;
|
|
2931
|
-
}
|
|
2932
|
-
function payloadToTransportMessage(payload) {
|
|
2933
|
-
return {
|
|
2934
|
-
streamId: "stream",
|
|
2935
|
-
controlFlags: 0,
|
|
2936
|
-
payload
|
|
2937
|
-
};
|
|
2938
|
-
}
|
|
2939
|
-
function createDummyTransportMessage() {
|
|
2940
|
-
return payloadToTransportMessage({
|
|
2941
|
-
msg: "cool",
|
|
2942
|
-
test: Math.random()
|
|
2943
|
-
});
|
|
2944
|
-
}
|
|
2945
|
-
async function waitForMessage(t, filter, rejectMismatch) {
|
|
2946
|
-
return new Promise((resolve, reject) => {
|
|
2947
|
-
function cleanup() {
|
|
2948
|
-
t.removeEventListener("message", onMessage);
|
|
2949
|
-
}
|
|
2950
|
-
function onMessage(msg) {
|
|
2951
|
-
if (!filter || filter(msg)) {
|
|
2952
|
-
cleanup();
|
|
2953
|
-
resolve(msg.payload);
|
|
2954
|
-
} else if (rejectMismatch) {
|
|
2955
|
-
cleanup();
|
|
2956
|
-
reject(new Error("message didnt match the filter"));
|
|
2957
|
-
}
|
|
2958
|
-
}
|
|
2959
|
-
t.addEventListener("message", onMessage);
|
|
2960
|
-
});
|
|
2961
|
-
}
|
|
2962
|
-
var testingSessionOptions = defaultTransportOptions;
|
|
2963
|
-
var testingClientSessionOptions = defaultClientTransportOptions;
|
|
2964
|
-
function dummySession() {
|
|
2965
|
-
return SessionStateGraph.entrypoints.NoConnection(
|
|
2966
|
-
"client",
|
|
2967
|
-
"server",
|
|
2968
|
-
{
|
|
2969
|
-
onSessionGracePeriodElapsed: () => {
|
|
2970
|
-
},
|
|
2971
|
-
onMessageSendFailure: () => {
|
|
2972
|
-
}
|
|
2973
|
-
},
|
|
2974
|
-
testingSessionOptions,
|
|
2975
|
-
currentProtocolVersion,
|
|
2976
|
-
getTracer()
|
|
2977
|
-
);
|
|
2978
|
-
}
|
|
2979
|
-
function getClientSendFn(clientTransport, serverTransport) {
|
|
2980
|
-
const session = clientTransport.sessions.get(serverTransport.clientId) ?? clientTransport.createUnconnectedSession(serverTransport.clientId);
|
|
2981
|
-
return clientTransport.getSessionBoundSendFn(
|
|
2982
|
-
serverTransport.clientId,
|
|
2983
|
-
session.id
|
|
2984
|
-
);
|
|
2985
|
-
}
|
|
2986
|
-
function getServerSendFn(serverTransport, clientTransport) {
|
|
2987
|
-
const session = serverTransport.sessions.get(clientTransport.clientId);
|
|
2988
|
-
if (!session) {
|
|
2989
|
-
throw new Error("session not found");
|
|
2990
|
-
}
|
|
2991
|
-
return serverTransport.getSessionBoundSendFn(
|
|
2992
|
-
clientTransport.clientId,
|
|
2993
|
-
session.id
|
|
2994
|
-
);
|
|
2995
|
-
}
|
|
2996
|
-
function getTransportConnections(transport) {
|
|
2997
|
-
const connections = [];
|
|
2998
|
-
for (const session of transport.sessions.values()) {
|
|
2999
|
-
if (session.state === "Connected" /* Connected */) {
|
|
3000
|
-
connections.push(session.conn);
|
|
3001
|
-
}
|
|
3002
|
-
}
|
|
3003
|
-
return connections;
|
|
3004
|
-
}
|
|
3005
|
-
function numberOfConnections(transport) {
|
|
3006
|
-
return getTransportConnections(transport).length;
|
|
3007
|
-
}
|
|
3008
|
-
function closeAllConnections(transport) {
|
|
3009
|
-
for (const conn of getTransportConnections(transport)) {
|
|
3010
|
-
conn.close();
|
|
3011
|
-
}
|
|
3012
|
-
}
|
|
3013
|
-
function createPartialContext(partial) {
|
|
3014
|
-
return new Proxy(partial, {
|
|
3015
|
-
get(target, prop, receiver) {
|
|
3016
|
-
if (prop in target) {
|
|
3017
|
-
return Reflect.get(target, prop, receiver);
|
|
3018
|
-
}
|
|
3019
|
-
if (typeof prop === "string" && prop !== "then") {
|
|
3020
|
-
throw new Error(
|
|
3021
|
-
`${prop} is not mocked in the test context. Provide it via createPartialContext if your test needs it.`
|
|
3022
|
-
);
|
|
3023
|
-
}
|
|
3024
|
-
return void 0;
|
|
3025
|
-
}
|
|
3026
|
-
});
|
|
3027
|
-
}
|
|
3028
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
3029
|
-
0 && (module.exports = {
|
|
3030
|
-
InMemoryConnection,
|
|
3031
|
-
closeAllConnections,
|
|
3032
|
-
createDummyTransportMessage,
|
|
3033
|
-
createLocalWebSocketClient,
|
|
3034
|
-
createMockTransportNetwork,
|
|
3035
|
-
createPartialContext,
|
|
3036
|
-
createWebSocketServer,
|
|
3037
|
-
dummySession,
|
|
3038
|
-
getClientSendFn,
|
|
3039
|
-
getReadableIterator,
|
|
3040
|
-
getServerSendFn,
|
|
3041
|
-
getTransportConnections,
|
|
3042
|
-
isReadableDone,
|
|
3043
|
-
numberOfConnections,
|
|
3044
|
-
onWsServerReady,
|
|
3045
|
-
payloadToTransportMessage,
|
|
3046
|
-
readNextResult,
|
|
3047
|
-
testingClientSessionOptions,
|
|
3048
|
-
testingSessionOptions,
|
|
3049
|
-
waitForMessage
|
|
3050
|
-
});
|
|
3051
|
-
//# sourceMappingURL=index.cjs.map
|