@replit/river 0.216.1 → 0.217.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +2 -2
  2. package/dist/{adapter-BXCk-dmy.d.ts → adapter-Dl5Mewp3.d.ts} +1 -1
  3. package/dist/{chunk-C4EPHIKQ.js → chunk-FWVKRK36.js} +48 -26
  4. package/dist/chunk-FWVKRK36.js.map +1 -0
  5. package/dist/chunk-WMPWPIA4.js +72 -0
  6. package/dist/chunk-WMPWPIA4.js.map +1 -0
  7. package/dist/{chunk-62BM2WOT.js → chunk-YTV5S3DJ.js} +27 -19
  8. package/dist/chunk-YTV5S3DJ.js.map +1 -0
  9. package/dist/{client-BNc5Pj_4.d.ts → client-Tf2PXWoq.d.ts} +2 -2
  10. package/dist/codec/index.d.ts +3 -3
  11. package/dist/codec/index.js +2 -2
  12. package/dist/{connection-ou9w2dSY.d.ts → connection-D9yKScvK.d.ts} +3 -3
  13. package/dist/customSchemas/index.d.ts +34 -0
  14. package/dist/customSchemas/index.js +9 -0
  15. package/dist/customSchemas/index.js.map +1 -0
  16. package/dist/{index-ZWkoesQD.d.ts → index-DgUMnNOi.d.ts} +1 -1
  17. package/dist/logging/index.d.ts +3 -3
  18. package/dist/{message-CpXWqmJw.d.ts → message-DxS8db8A.d.ts} +30 -31
  19. package/dist/protobuf/index.d.ts +12 -16
  20. package/dist/protobuf/index.js +9 -7
  21. package/dist/protobuf/index.js.map +1 -1
  22. package/dist/router/index.d.ts +11 -11
  23. package/dist/router/index.js +1 -1
  24. package/dist/{server-BPu7Td80.d.ts → server-DhHG-q75.d.ts} +5 -5
  25. package/dist/{services-DpT2yNtt.d.ts → services-BT8Bq8Rs.d.ts} +8 -9
  26. package/dist/testUtil/index.d.ts +8 -8
  27. package/dist/testUtil/index.js +2 -2
  28. package/dist/testUtil/index.js.map +1 -1
  29. package/dist/transport/impls/ws/client.d.ts +7 -7
  30. package/dist/transport/impls/ws/client.js +2 -2
  31. package/dist/transport/impls/ws/server.d.ts +7 -7
  32. package/dist/transport/impls/ws/server.js +2 -2
  33. package/dist/transport/impls/ws/server.js.map +1 -1
  34. package/dist/transport/index.d.ts +8 -8
  35. package/dist/transport/index.js +2 -2
  36. package/dist/{transport-B1MUtXL7.d.ts → transport-D6czLXyc.d.ts} +13 -12
  37. package/package.json +14 -39
  38. package/dist/adapter-D5X11kmP.d.cts +0 -29
  39. package/dist/chunk-62BM2WOT.js.map +0 -1
  40. package/dist/chunk-C4EPHIKQ.js.map +0 -1
  41. package/dist/client-BZUvFL6B.d.cts +0 -54
  42. package/dist/codec/index.cjs +0 -268
  43. package/dist/codec/index.cjs.map +0 -1
  44. package/dist/codec/index.d.cts +0 -19
  45. package/dist/connection-xxgJHs2o.d.cts +0 -40
  46. package/dist/index-BAGGleT3.d.cts +0 -37
  47. package/dist/logging/index.cjs +0 -55
  48. package/dist/logging/index.cjs.map +0 -1
  49. package/dist/logging/index.d.cts +0 -4
  50. package/dist/message-CpXWqmJw.d.cts +0 -119
  51. package/dist/protobuf/codec.cjs +0 -107
  52. package/dist/protobuf/codec.cjs.map +0 -1
  53. package/dist/protobuf/codec.d.cts +0 -13
  54. package/dist/protobuf/index.cjs +0 -1877
  55. package/dist/protobuf/index.cjs.map +0 -1
  56. package/dist/protobuf/index.d.cts +0 -488
  57. package/dist/router/index.cjs +0 -2043
  58. package/dist/router/index.cjs.map +0 -1
  59. package/dist/router/index.d.cts +0 -80
  60. package/dist/server-JdnoVO11.d.cts +0 -72
  61. package/dist/services-CjigASqe.d.cts +0 -1135
  62. package/dist/testUtil/index.cjs +0 -3051
  63. package/dist/testUtil/index.cjs.map +0 -1
  64. package/dist/testUtil/index.d.cts +0 -122
  65. package/dist/transport/impls/ws/client.cjs +0 -2308
  66. package/dist/transport/impls/ws/client.cjs.map +0 -1
  67. package/dist/transport/impls/ws/client.d.cts +0 -33
  68. package/dist/transport/impls/ws/server.cjs +0 -2179
  69. package/dist/transport/impls/ws/server.cjs.map +0 -1
  70. package/dist/transport/impls/ws/server.d.cts +0 -21
  71. package/dist/transport/index.cjs +0 -2727
  72. package/dist/transport/index.cjs.map +0 -1
  73. package/dist/transport/index.d.cts +0 -11
  74. package/dist/transport-BnU3Zb0Q.d.cts +0 -590
  75. package/dist/types-BGGvYIJM.d.cts +0 -20
  76. package/dist/wslike-Dng9H1C7.d.cts +0 -40
@@ -1,2179 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // transport/impls/ws/server.ts
21
- var server_exports = {};
22
- __export(server_exports, {
23
- WebSocketServerTransport: () => WebSocketServerTransport
24
- });
25
- module.exports = __toCommonJS(server_exports);
26
-
27
- // transport/id.ts
28
- var import_nanoid = require("nanoid");
29
- var alphabet = (0, import_nanoid.customAlphabet)(
30
- "1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"
31
- );
32
- var generateId = () => alphabet(12);
33
-
34
- // transport/connection.ts
35
- var Connection = class {
36
- id;
37
- telemetry;
38
- constructor() {
39
- this.id = `conn-${generateId()}`;
40
- }
41
- get loggingMetadata() {
42
- const metadata = { connId: this.id };
43
- if (this.telemetry?.span.isRecording()) {
44
- const spanContext = this.telemetry.span.spanContext();
45
- metadata.telemetry = {
46
- traceId: spanContext.traceId,
47
- spanId: spanContext.spanId
48
- };
49
- }
50
- return metadata;
51
- }
52
- dataListener;
53
- closeListener;
54
- errorListener;
55
- onData(msg) {
56
- this.dataListener?.(msg);
57
- }
58
- onError(err) {
59
- this.errorListener?.(err);
60
- }
61
- onClose() {
62
- this.closeListener?.();
63
- this.telemetry?.span.end();
64
- }
65
- /**
66
- * Set the callback for when a message is received.
67
- * @param cb The message handler callback.
68
- */
69
- setDataListener(cb) {
70
- this.dataListener = cb;
71
- }
72
- removeDataListener() {
73
- this.dataListener = void 0;
74
- }
75
- /**
76
- * Set the callback for when the connection is closed.
77
- * This should also be called if an error happens and after notifying the error listener.
78
- * @param cb The callback to call when the connection is closed.
79
- */
80
- setCloseListener(cb) {
81
- this.closeListener = cb;
82
- }
83
- removeCloseListener() {
84
- this.closeListener = void 0;
85
- }
86
- /**
87
- * Set the callback for when an error is received.
88
- * This should only be used for logging errors, all cleanup
89
- * should be delegated to setCloseListener.
90
- *
91
- * The implementer should take care such that the implemented
92
- * connection will call both the close and error callbacks
93
- * on an error.
94
- *
95
- * @param cb The callback to call when an error is received.
96
- */
97
- setErrorListener(cb) {
98
- this.errorListener = cb;
99
- }
100
- removeErrorListener() {
101
- this.errorListener = void 0;
102
- }
103
- };
104
-
105
- // transport/impls/ws/connection.ts
106
- var WS_HEALTHY_CLOSE_CODE = 1e3;
107
- var WebSocketCloseError = class extends Error {
108
- code;
109
- reason;
110
- constructor(code, reason) {
111
- super(`websocket closed with code and reason: ${code} - ${reason}`);
112
- this.code = code;
113
- this.reason = reason;
114
- }
115
- };
116
- var WebSocketConnection = class extends Connection {
117
- ws;
118
- extras;
119
- get loggingMetadata() {
120
- const metadata = super.loggingMetadata;
121
- if (this.extras) {
122
- metadata.extras = this.extras;
123
- }
124
- return metadata;
125
- }
126
- constructor(ws, extras) {
127
- super();
128
- this.ws = ws;
129
- this.extras = extras;
130
- this.ws.binaryType = "arraybuffer";
131
- let didError = false;
132
- this.ws.onerror = () => {
133
- didError = true;
134
- };
135
- this.ws.onclose = ({ code, reason }) => {
136
- if (didError) {
137
- const err = new WebSocketCloseError(code, reason);
138
- this.onError(err);
139
- }
140
- this.onClose();
141
- };
142
- this.ws.onmessage = (msg) => {
143
- this.onData(msg.data);
144
- };
145
- }
146
- send(payload) {
147
- try {
148
- this.ws.send(payload);
149
- return true;
150
- } catch {
151
- return false;
152
- }
153
- }
154
- close() {
155
- this.ws.close(WS_HEALTHY_CLOSE_CODE);
156
- }
157
- };
158
-
159
- // transport/server.ts
160
- var import_api4 = require("@opentelemetry/api");
161
-
162
- // transport/message.ts
163
- var import_typebox = require("@sinclair/typebox");
164
- var TransportMessageSchema = (t) => import_typebox.Type.Object({
165
- id: import_typebox.Type.String(),
166
- from: import_typebox.Type.String(),
167
- to: import_typebox.Type.String(),
168
- seq: import_typebox.Type.Integer(),
169
- ack: import_typebox.Type.Integer(),
170
- serviceName: import_typebox.Type.Optional(import_typebox.Type.String()),
171
- procedureName: import_typebox.Type.Optional(import_typebox.Type.String()),
172
- streamId: import_typebox.Type.String(),
173
- controlFlags: import_typebox.Type.Integer(),
174
- tracing: import_typebox.Type.Optional(
175
- import_typebox.Type.Object({
176
- traceparent: import_typebox.Type.String(),
177
- tracestate: import_typebox.Type.String()
178
- })
179
- ),
180
- payload: t
181
- });
182
- var ControlMessageAckSchema = import_typebox.Type.Object({
183
- type: import_typebox.Type.Literal("ACK")
184
- });
185
- var ControlMessageCloseSchema = import_typebox.Type.Object({
186
- type: import_typebox.Type.Literal("CLOSE")
187
- });
188
- var currentProtocolVersion = "v2.0";
189
- var acceptedProtocolVersions = ["v1.1", currentProtocolVersion];
190
- function isAcceptedProtocolVersion(version2) {
191
- return acceptedProtocolVersions.includes(version2);
192
- }
193
- var ControlMessageHandshakeRequestSchema = import_typebox.Type.Object({
194
- type: import_typebox.Type.Literal("HANDSHAKE_REQ"),
195
- protocolVersion: import_typebox.Type.String(),
196
- sessionId: import_typebox.Type.String(),
197
- /**
198
- * Specifies what the server's expected session state (from the pov of the client). This can be
199
- * used by the server to know whether this is a new or a reestablished connection, and whether it
200
- * is compatible with what it already has.
201
- */
202
- expectedSessionState: import_typebox.Type.Object({
203
- // what the client expects the server to send next
204
- nextExpectedSeq: import_typebox.Type.Integer(),
205
- nextSentSeq: import_typebox.Type.Integer()
206
- }),
207
- metadata: import_typebox.Type.Optional(import_typebox.Type.Unknown())
208
- });
209
- var HandshakeErrorRetriableResponseCodes = import_typebox.Type.Union([
210
- import_typebox.Type.Literal("SESSION_STATE_MISMATCH")
211
- ]);
212
- var HandshakeErrorCustomHandlerFatalResponseCodes = import_typebox.Type.Union([
213
- // The custom validation handler rejected the handler because the client is unsupported.
214
- import_typebox.Type.Literal("REJECTED_UNSUPPORTED_CLIENT"),
215
- // The custom validation handler rejected the handshake.
216
- import_typebox.Type.Literal("REJECTED_BY_CUSTOM_HANDLER")
217
- ]);
218
- var HandshakeErrorFatalResponseCodes = import_typebox.Type.Union([
219
- HandshakeErrorCustomHandlerFatalResponseCodes,
220
- // The ciient sent a handshake that doesn't comply with the extended handshake metadata.
221
- import_typebox.Type.Literal("MALFORMED_HANDSHAKE_META"),
222
- // The ciient sent a handshake that doesn't comply with ControlMessageHandshakeRequestSchema.
223
- import_typebox.Type.Literal("MALFORMED_HANDSHAKE"),
224
- // The client's protocol version does not match the server's.
225
- import_typebox.Type.Literal("PROTOCOL_VERSION_MISMATCH")
226
- ]);
227
- var HandshakeErrorResponseCodes = import_typebox.Type.Union([
228
- HandshakeErrorRetriableResponseCodes,
229
- HandshakeErrorFatalResponseCodes
230
- ]);
231
- var ControlMessageHandshakeResponseSchema = import_typebox.Type.Object({
232
- type: import_typebox.Type.Literal("HANDSHAKE_RESP"),
233
- status: import_typebox.Type.Union([
234
- import_typebox.Type.Object({
235
- ok: import_typebox.Type.Literal(true),
236
- sessionId: import_typebox.Type.String()
237
- }),
238
- import_typebox.Type.Object({
239
- ok: import_typebox.Type.Literal(false),
240
- reason: import_typebox.Type.String(),
241
- code: HandshakeErrorResponseCodes
242
- })
243
- ])
244
- });
245
- var ControlMessagePayloadSchema = import_typebox.Type.Union([
246
- ControlMessageCloseSchema,
247
- ControlMessageAckSchema,
248
- ControlMessageHandshakeRequestSchema,
249
- ControlMessageHandshakeResponseSchema
250
- ]);
251
- var OpaqueTransportMessageSchema = TransportMessageSchema(
252
- import_typebox.Type.Unknown()
253
- );
254
- function handshakeResponseMessage({
255
- from,
256
- to,
257
- status
258
- }) {
259
- return {
260
- id: generateId(),
261
- from,
262
- to,
263
- seq: 0,
264
- ack: 0,
265
- streamId: generateId(),
266
- controlFlags: 0,
267
- payload: {
268
- type: "HANDSHAKE_RESP",
269
- status
270
- }
271
- };
272
- }
273
- function isAck(controlFlag) {
274
- return (controlFlag & 1 /* AckBit */) === 1 /* AckBit */;
275
- }
276
-
277
- // codec/json.ts
278
- var encoder = new TextEncoder();
279
- var decoder = new TextDecoder();
280
- function uint8ArrayToBase64(uint8Array) {
281
- let binary = "";
282
- uint8Array.forEach((byte) => {
283
- binary += String.fromCharCode(byte);
284
- });
285
- return btoa(binary);
286
- }
287
- function base64ToUint8Array(base64) {
288
- const binaryString = atob(base64);
289
- const uint8Array = new Uint8Array(binaryString.length);
290
- for (let i = 0; i < binaryString.length; i++) {
291
- uint8Array[i] = binaryString.charCodeAt(i);
292
- }
293
- return uint8Array;
294
- }
295
- var NaiveJsonCodec = {
296
- toBuffer: (obj) => {
297
- return encoder.encode(
298
- JSON.stringify(obj, function replacer(key) {
299
- const val = this[key];
300
- if (val instanceof Uint8Array) {
301
- return { $t: uint8ArrayToBase64(val) };
302
- } else if (typeof val === "bigint") {
303
- return { $b: val.toString() };
304
- } else {
305
- return val;
306
- }
307
- })
308
- );
309
- },
310
- fromBuffer: (buff) => {
311
- const parsed = JSON.parse(
312
- decoder.decode(buff),
313
- function reviver(_key, val) {
314
- if (val?.$t !== void 0) {
315
- return base64ToUint8Array(val.$t);
316
- } else if (val?.$b !== void 0) {
317
- return BigInt(val.$b);
318
- } else {
319
- return val;
320
- }
321
- }
322
- );
323
- if (typeof parsed !== "object" || parsed === null) {
324
- throw new Error("unpacked msg is not an object");
325
- }
326
- return parsed;
327
- }
328
- };
329
-
330
- // transport/options.ts
331
- var defaultTransportOptions = {
332
- heartbeatIntervalMs: 1e3,
333
- heartbeatsUntilDead: 2,
334
- sessionDisconnectGraceMs: 5e3,
335
- connectionTimeoutMs: 2e3,
336
- handshakeTimeoutMs: 1e3,
337
- enableTransparentSessionReconnects: true,
338
- codec: NaiveJsonCodec
339
- };
340
- var defaultConnectionRetryOptions = {
341
- baseIntervalMs: 150,
342
- maxJitterMs: 200,
343
- maxBackoffMs: 32e3,
344
- attemptBudgetCapacity: 5,
345
- budgetRestoreIntervalMs: 200,
346
- isFatalConnectionError: () => false
347
- };
348
- var defaultClientTransportOptions = {
349
- ...defaultTransportOptions,
350
- ...defaultConnectionRetryOptions
351
- };
352
- var defaultServerTransportOptions = {
353
- ...defaultTransportOptions
354
- };
355
-
356
- // logging/log.ts
357
- var import_api = require("@opentelemetry/api");
358
- var LoggingLevels = {
359
- debug: -1,
360
- info: 0,
361
- warn: 1,
362
- error: 2
363
- };
364
- var cleanedLogFn = (log) => {
365
- return (msg, metadata) => {
366
- if (metadata && !metadata.telemetry) {
367
- const span = import_api.trace.getSpan(import_api.context.active());
368
- if (span) {
369
- metadata.telemetry = {
370
- traceId: span.spanContext().traceId,
371
- spanId: span.spanContext().spanId
372
- };
373
- }
374
- }
375
- if (!metadata?.transportMessage) {
376
- log(msg, metadata);
377
- return;
378
- }
379
- const { payload, ...rest } = metadata.transportMessage;
380
- metadata.transportMessage = rest;
381
- log(msg, metadata);
382
- };
383
- };
384
- var BaseLogger = class {
385
- minLevel;
386
- output;
387
- constructor(output, minLevel = "info") {
388
- this.minLevel = minLevel;
389
- this.output = output;
390
- }
391
- debug(msg, metadata) {
392
- if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {
393
- this.output(msg, metadata ?? {}, "debug");
394
- }
395
- }
396
- info(msg, metadata) {
397
- if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {
398
- this.output(msg, metadata ?? {}, "info");
399
- }
400
- }
401
- warn(msg, metadata) {
402
- if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {
403
- this.output(msg, metadata ?? {}, "warn");
404
- }
405
- }
406
- error(msg, metadata) {
407
- if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {
408
- this.output(msg, metadata ?? {}, "error");
409
- }
410
- }
411
- };
412
- var createLogProxy = (log) => ({
413
- debug: cleanedLogFn(log.debug.bind(log)),
414
- info: cleanedLogFn(log.info.bind(log)),
415
- warn: cleanedLogFn(log.warn.bind(log)),
416
- error: cleanedLogFn(log.error.bind(log))
417
- });
418
-
419
- // transport/events.ts
420
- var ProtocolError = {
421
- RetriesExceeded: "conn_retry_exceeded",
422
- HandshakeFailed: "handshake_failed",
423
- MessageOrderingViolated: "message_ordering_violated",
424
- InvalidMessage: "invalid_message",
425
- MessageSendFailure: "message_send_failure"
426
- };
427
- var EventDispatcher = class {
428
- eventListeners = {};
429
- removeAllListeners() {
430
- this.eventListeners = {};
431
- }
432
- numberOfListeners(eventType) {
433
- return this.eventListeners[eventType]?.size ?? 0;
434
- }
435
- addEventListener(eventType, handler) {
436
- if (!this.eventListeners[eventType]) {
437
- this.eventListeners[eventType] = /* @__PURE__ */ new Set();
438
- }
439
- this.eventListeners[eventType]?.add(handler);
440
- }
441
- removeEventListener(eventType, handler) {
442
- const handlers = this.eventListeners[eventType];
443
- if (handlers) {
444
- this.eventListeners[eventType]?.delete(handler);
445
- }
446
- }
447
- dispatchEvent(eventType, event) {
448
- const handlers = this.eventListeners[eventType];
449
- if (handlers) {
450
- const copy = [...handlers];
451
- for (const handler of copy) {
452
- handler(event);
453
- }
454
- }
455
- }
456
- };
457
-
458
- // transport/sessionStateMachine/common.ts
459
- var ERR_CONSUMED = `session state has been consumed and is no longer valid`;
460
- var StateMachineState = class {
461
- /*
462
- * Whether this state has been consumed
463
- * and we've moved on to another state
464
- */
465
- _isConsumed;
466
- /**
467
- * Cleanup this state machine state and mark it as consumed.
468
- * After calling close, it is an error to access any properties on the state.
469
- * You should never need to call this as a consumer.
470
- *
471
- * If you're looking to close the session from the client,
472
- * use `.hardDisconnect` on the client transport.
473
- */
474
- close() {
475
- this._handleClose();
476
- }
477
- constructor() {
478
- this._isConsumed = false;
479
- return new Proxy(this, {
480
- get(target, prop) {
481
- if (prop === "_isConsumed" || prop === "id" || prop === "state") {
482
- return Reflect.get(target, prop);
483
- }
484
- if (prop === "_handleStateExit") {
485
- return () => {
486
- target._isConsumed = true;
487
- target._handleStateExit();
488
- };
489
- }
490
- if (prop === "_handleClose") {
491
- return () => {
492
- target._isConsumed = true;
493
- target._handleStateExit();
494
- target._handleClose();
495
- };
496
- }
497
- if (target._isConsumed) {
498
- throw new Error(
499
- `${ERR_CONSUMED}: getting ${prop.toString()} on consumed state`
500
- );
501
- }
502
- return Reflect.get(target, prop);
503
- },
504
- set(target, prop, value) {
505
- if (target._isConsumed) {
506
- throw new Error(
507
- `${ERR_CONSUMED}: setting ${prop.toString()} on consumed state`
508
- );
509
- }
510
- return Reflect.set(target, prop, value);
511
- }
512
- });
513
- }
514
- };
515
- var CommonSession = class extends StateMachineState {
516
- from;
517
- options;
518
- codec;
519
- tracer;
520
- log;
521
- constructor({ from, options, log, tracer, codec }) {
522
- super();
523
- this.from = from;
524
- this.options = options;
525
- this.log = log;
526
- this.tracer = tracer;
527
- this.codec = codec;
528
- }
529
- };
530
- var IdentifiedSession = class extends CommonSession {
531
- id;
532
- telemetry;
533
- to;
534
- protocolVersion;
535
- listeners;
536
- /**
537
- * Index of the message we will send next (excluding handshake)
538
- */
539
- seq;
540
- /**
541
- * Last seq we sent over the wire this session (excluding handshake) and retransmissions
542
- */
543
- seqSent;
544
- /**
545
- * Number of unique messages we've received this session (excluding handshake)
546
- */
547
- ack;
548
- sendBuffer;
549
- constructor(props) {
550
- const {
551
- id,
552
- to,
553
- seq,
554
- ack,
555
- sendBuffer,
556
- telemetry,
557
- log,
558
- protocolVersion,
559
- seqSent: messagesSent,
560
- listeners
561
- } = props;
562
- super(props);
563
- this.id = id;
564
- this.to = to;
565
- this.seq = seq;
566
- this.ack = ack;
567
- this.sendBuffer = sendBuffer;
568
- this.telemetry = telemetry;
569
- this.log = log;
570
- this.protocolVersion = protocolVersion;
571
- this.seqSent = messagesSent;
572
- this.listeners = listeners;
573
- }
574
- get loggingMetadata() {
575
- const metadata = {
576
- clientId: this.from,
577
- connectedTo: this.to,
578
- sessionId: this.id
579
- };
580
- if (this.telemetry.span.isRecording()) {
581
- const spanContext = this.telemetry.span.spanContext();
582
- metadata.telemetry = {
583
- traceId: spanContext.traceId,
584
- spanId: spanContext.spanId
585
- };
586
- }
587
- return metadata;
588
- }
589
- encodeMsg(partialMsg) {
590
- const msg = {
591
- ...partialMsg,
592
- id: generateId(),
593
- to: this.to,
594
- from: this.from,
595
- seq: this.seq,
596
- ack: this.ack
597
- };
598
- const encoded = this.codec.toBuffer(msg);
599
- if (!encoded.ok) {
600
- this.listeners.onMessageSendFailure(
601
- { ...partialMsg, seq: this.seq },
602
- encoded.reason
603
- );
604
- return encoded;
605
- }
606
- this.seq++;
607
- return {
608
- ok: true,
609
- value: {
610
- id: msg.id,
611
- seq: msg.seq,
612
- msg: partialMsg,
613
- data: encoded.value
614
- }
615
- };
616
- }
617
- nextSeq() {
618
- return this.sendBuffer.length > 0 ? this.sendBuffer[0].seq : this.seq;
619
- }
620
- send(msg) {
621
- const encodeResult = this.encodeMsg(msg);
622
- if (!encodeResult.ok) {
623
- return encodeResult;
624
- }
625
- this.sendBuffer.push(encodeResult.value);
626
- return {
627
- ok: true,
628
- value: encodeResult.value.id
629
- };
630
- }
631
- _handleStateExit() {
632
- }
633
- _handleClose() {
634
- this.sendBuffer.length = 0;
635
- this.telemetry.span.end();
636
- }
637
- };
638
- var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
639
- graceExpiryTime;
640
- gracePeriodTimeout;
641
- listeners;
642
- constructor(props) {
643
- super(props);
644
- this.listeners = props.listeners;
645
- this.graceExpiryTime = props.graceExpiryTime;
646
- this.gracePeriodTimeout = setTimeout(() => {
647
- this.listeners.onSessionGracePeriodElapsed();
648
- }, this.graceExpiryTime - Date.now());
649
- }
650
- _handleStateExit() {
651
- super._handleStateExit();
652
- if (this.gracePeriodTimeout) {
653
- clearTimeout(this.gracePeriodTimeout);
654
- this.gracePeriodTimeout = void 0;
655
- }
656
- }
657
- _handleClose() {
658
- super._handleClose();
659
- }
660
- };
661
-
662
- // transport/sessionStateMachine/SessionConnecting.ts
663
- var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
664
- state = "Connecting" /* Connecting */;
665
- connPromise;
666
- listeners;
667
- connectionTimeout;
668
- constructor(props) {
669
- super(props);
670
- this.connPromise = props.connPromise;
671
- this.listeners = props.listeners;
672
- this.connPromise.then(
673
- (conn) => {
674
- if (this._isConsumed) return;
675
- this.listeners.onConnectionEstablished(conn);
676
- },
677
- (err) => {
678
- if (this._isConsumed) return;
679
- this.listeners.onConnectionFailed(err);
680
- }
681
- );
682
- this.connectionTimeout = setTimeout(() => {
683
- this.listeners.onConnectionTimeout();
684
- }, this.options.connectionTimeoutMs);
685
- }
686
- // close a pending connection if it resolves, ignore errors if the promise
687
- // ends up rejected anyways
688
- bestEffortClose() {
689
- const logger = this.log;
690
- const metadata = this.loggingMetadata;
691
- this.connPromise.then((conn) => {
692
- conn.close();
693
- logger?.info(
694
- "connection eventually resolved but session has transitioned, closed connection",
695
- {
696
- ...metadata,
697
- ...conn.loggingMetadata
698
- }
699
- );
700
- }).catch(() => {
701
- });
702
- }
703
- _handleStateExit() {
704
- super._handleStateExit();
705
- if (this.connectionTimeout) {
706
- clearTimeout(this.connectionTimeout);
707
- this.connectionTimeout = void 0;
708
- }
709
- }
710
- _handleClose() {
711
- super._handleClose();
712
- this.bestEffortClose();
713
- }
714
- };
715
-
716
- // transport/sessionStateMachine/SessionNoConnection.ts
717
- var SessionNoConnection = class extends IdentifiedSessionWithGracePeriod {
718
- state = "NoConnection" /* NoConnection */;
719
- _handleClose() {
720
- super._handleClose();
721
- }
722
- _handleStateExit() {
723
- super._handleStateExit();
724
- }
725
- };
726
-
727
- // tracing/index.ts
728
- var import_api2 = require("@opentelemetry/api");
729
-
730
- // transport/stringifyError.ts
731
- function coerceErrorString(err) {
732
- if (err instanceof Error) {
733
- return err.message || "unknown reason";
734
- }
735
- return `[coerced to error] ${String(err)}`;
736
- }
737
-
738
- // package.json
739
- var version = "0.216.1";
740
-
741
- // tracing/index.ts
742
- function createSessionTelemetryInfo(tracer, sessionId, to, from, propagationCtx) {
743
- const parentCtx = propagationCtx ? import_api2.propagation.extract(import_api2.context.active(), propagationCtx) : import_api2.context.active();
744
- const span = tracer.startSpan(
745
- `river.session`,
746
- {
747
- attributes: {
748
- component: "river",
749
- "river.session.id": sessionId,
750
- "river.session.to": to,
751
- "river.session.from": from
752
- }
753
- },
754
- parentCtx
755
- );
756
- const ctx = import_api2.trace.setSpan(parentCtx, span);
757
- return { span, ctx };
758
- }
759
- function createConnectionTelemetryInfo(tracer, connection, info) {
760
- const span = tracer.startSpan(
761
- `river.connection`,
762
- {
763
- attributes: {
764
- component: "river",
765
- "river.connection.id": connection.id
766
- },
767
- links: [{ context: info.span.spanContext() }]
768
- },
769
- info.ctx
770
- );
771
- const ctx = import_api2.trace.setSpan(info.ctx, span);
772
- return { span, ctx };
773
- }
774
- function getTracer() {
775
- return import_api2.trace.getTracer("river", version);
776
- }
777
-
778
- // transport/sessionStateMachine/SessionWaitingForHandshake.ts
779
- var SessionWaitingForHandshake = class extends CommonSession {
780
- state = "WaitingForHandshake" /* WaitingForHandshake */;
781
- conn;
782
- listeners;
783
- handshakeTimeout;
784
- constructor(props) {
785
- super(props);
786
- this.conn = props.conn;
787
- this.listeners = props.listeners;
788
- this.handshakeTimeout = setTimeout(() => {
789
- this.listeners.onHandshakeTimeout();
790
- }, this.options.handshakeTimeoutMs);
791
- this.conn.setDataListener(this.onHandshakeData);
792
- this.conn.setErrorListener(this.listeners.onConnectionErrored);
793
- this.conn.setCloseListener(this.listeners.onConnectionClosed);
794
- }
795
- get loggingMetadata() {
796
- return {
797
- clientId: this.from,
798
- connId: this.conn.id,
799
- ...this.conn.loggingMetadata
800
- };
801
- }
802
- onHandshakeData = (msg) => {
803
- const parsedMsgRes = this.codec.fromBuffer(msg);
804
- if (!parsedMsgRes.ok) {
805
- this.listeners.onInvalidHandshake(
806
- `could not parse handshake message: ${parsedMsgRes.reason}`,
807
- "MALFORMED_HANDSHAKE"
808
- );
809
- return;
810
- }
811
- this.listeners.onHandshake(parsedMsgRes.value);
812
- };
813
- sendHandshake(msg) {
814
- const buff = this.codec.toBuffer(msg);
815
- if (!buff.ok) {
816
- return buff;
817
- }
818
- const sent = this.conn.send(buff.value);
819
- if (!sent) {
820
- return {
821
- ok: false,
822
- reason: "failed to send handshake"
823
- };
824
- }
825
- return {
826
- ok: true,
827
- value: msg.id
828
- };
829
- }
830
- _handleStateExit() {
831
- this.conn.removeDataListener();
832
- this.conn.removeErrorListener();
833
- this.conn.removeCloseListener();
834
- clearTimeout(this.handshakeTimeout);
835
- this.handshakeTimeout = void 0;
836
- }
837
- _handleClose() {
838
- this.conn.close();
839
- }
840
- };
841
-
842
- // transport/sessionStateMachine/SessionHandshaking.ts
843
- var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
844
- state = "Handshaking" /* Handshaking */;
845
- conn;
846
- listeners;
847
- handshakeTimeout;
848
- constructor(props) {
849
- super(props);
850
- this.conn = props.conn;
851
- this.listeners = props.listeners;
852
- this.handshakeTimeout = setTimeout(() => {
853
- this.listeners.onHandshakeTimeout();
854
- }, this.options.handshakeTimeoutMs);
855
- this.conn.setDataListener(this.onHandshakeData);
856
- this.conn.setErrorListener(this.listeners.onConnectionErrored);
857
- this.conn.setCloseListener(this.listeners.onConnectionClosed);
858
- }
859
- get loggingMetadata() {
860
- return {
861
- ...super.loggingMetadata,
862
- ...this.conn.loggingMetadata
863
- };
864
- }
865
- onHandshakeData = (msg) => {
866
- const parsedMsgRes = this.codec.fromBuffer(msg);
867
- if (!parsedMsgRes.ok) {
868
- this.listeners.onInvalidHandshake(
869
- `could not parse handshake message: ${parsedMsgRes.reason}`,
870
- "MALFORMED_HANDSHAKE"
871
- );
872
- return;
873
- }
874
- this.listeners.onHandshake(parsedMsgRes.value);
875
- };
876
- sendHandshake(msg) {
877
- const buff = this.codec.toBuffer(msg);
878
- if (!buff.ok) {
879
- return buff;
880
- }
881
- const sent = this.conn.send(buff.value);
882
- if (!sent) {
883
- return {
884
- ok: false,
885
- reason: "failed to send handshake"
886
- };
887
- }
888
- return {
889
- ok: true,
890
- value: msg.id
891
- };
892
- }
893
- _handleStateExit() {
894
- super._handleStateExit();
895
- this.conn.removeDataListener();
896
- this.conn.removeErrorListener();
897
- this.conn.removeCloseListener();
898
- if (this.handshakeTimeout) {
899
- clearTimeout(this.handshakeTimeout);
900
- this.handshakeTimeout = void 0;
901
- }
902
- }
903
- _handleClose() {
904
- super._handleClose();
905
- this.conn.close();
906
- }
907
- };
908
-
909
- // transport/sessionStateMachine/SessionConnected.ts
910
- var import_api3 = require("@opentelemetry/api");
911
- var SessionConnected = class extends IdentifiedSession {
912
- state = "Connected" /* Connected */;
913
- conn;
914
- listeners;
915
- heartbeatHandle;
916
- heartbeatMissTimeout;
917
- isActivelyHeartbeating = false;
918
- updateBookkeeping(ack, seq) {
919
- this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
920
- this.ack = seq + 1;
921
- if (this.heartbeatMissTimeout) {
922
- clearTimeout(this.heartbeatMissTimeout);
923
- }
924
- this.startMissingHeartbeatTimeout();
925
- }
926
- assertSendOrdering(encodedMsg) {
927
- if (encodedMsg.seq > this.seqSent + 1) {
928
- const msg = `invariant violation: would have sent out of order msg (seq: ${encodedMsg.seq}, expected: ${this.seqSent} + 1)`;
929
- this.log?.error(msg, {
930
- ...this.loggingMetadata,
931
- tags: ["invariant-violation"]
932
- });
933
- throw new Error(msg);
934
- }
935
- }
936
- send(msg) {
937
- const encodeResult = this.encodeMsg(msg);
938
- if (!encodeResult.ok) {
939
- return encodeResult;
940
- }
941
- const encodedMsg = encodeResult.value;
942
- this.assertSendOrdering(encodedMsg);
943
- this.sendBuffer.push(encodedMsg);
944
- const sent = this.conn.send(encodedMsg.data);
945
- if (!sent) {
946
- const reason = "failed to send message";
947
- this.listeners.onMessageSendFailure(
948
- { ...encodedMsg.msg, seq: encodedMsg.seq },
949
- reason
950
- );
951
- return { ok: false, reason };
952
- }
953
- this.seqSent = encodedMsg.seq;
954
- return { ok: true, value: encodedMsg.id };
955
- }
956
- constructor(props) {
957
- super(props);
958
- this.conn = props.conn;
959
- this.listeners = props.listeners;
960
- this.conn.setDataListener(this.onMessageData);
961
- this.conn.setCloseListener(this.listeners.onConnectionClosed);
962
- this.conn.setErrorListener(this.listeners.onConnectionErrored);
963
- }
964
- sendBufferedMessages() {
965
- if (this.sendBuffer.length > 0) {
966
- this.log?.info(
967
- `sending ${this.sendBuffer.length} buffered messages, starting at seq ${this.nextSeq()}`,
968
- this.loggingMetadata
969
- );
970
- for (const msg of this.sendBuffer) {
971
- this.assertSendOrdering(msg);
972
- const sent = this.conn.send(msg.data);
973
- if (!sent) {
974
- const reason = "failed to send buffered message";
975
- this.listeners.onMessageSendFailure(
976
- { ...msg.msg, seq: msg.seq },
977
- reason
978
- );
979
- return { ok: false, reason };
980
- }
981
- this.seqSent = msg.seq;
982
- }
983
- }
984
- return { ok: true, value: void 0 };
985
- }
986
- get loggingMetadata() {
987
- return {
988
- ...super.loggingMetadata,
989
- ...this.conn.loggingMetadata
990
- };
991
- }
992
- startMissingHeartbeatTimeout() {
993
- const maxMisses = this.options.heartbeatsUntilDead;
994
- const missDuration = maxMisses * this.options.heartbeatIntervalMs;
995
- this.heartbeatMissTimeout = setTimeout(() => {
996
- this.log?.info(
997
- `closing connection to ${this.to} due to inactivity (missed ${maxMisses} heartbeats which is ${missDuration}ms)`,
998
- this.loggingMetadata
999
- );
1000
- this.telemetry.span.addEvent(
1001
- "closing connection due to missing heartbeat"
1002
- );
1003
- this.conn.close();
1004
- }, missDuration);
1005
- }
1006
- startActiveHeartbeat() {
1007
- this.isActivelyHeartbeating = true;
1008
- this.heartbeatHandle = setInterval(() => {
1009
- this.sendHeartbeat();
1010
- }, this.options.heartbeatIntervalMs);
1011
- }
1012
- sendHeartbeat() {
1013
- this.log?.debug("sending heartbeat", this.loggingMetadata);
1014
- const heartbeat = {
1015
- streamId: "heartbeat",
1016
- controlFlags: 1 /* AckBit */,
1017
- payload: {
1018
- type: "ACK"
1019
- }
1020
- };
1021
- this.send(heartbeat);
1022
- }
1023
- onMessageData = (msg) => {
1024
- const parsedMsgRes = this.codec.fromBuffer(msg);
1025
- if (!parsedMsgRes.ok) {
1026
- this.listeners.onInvalidMessage(
1027
- `could not parse message: ${parsedMsgRes.reason}`
1028
- );
1029
- return;
1030
- }
1031
- const parsedMsg = parsedMsgRes.value;
1032
- if (parsedMsg.seq !== this.ack) {
1033
- if (parsedMsg.seq < this.ack) {
1034
- this.log?.debug(
1035
- `received duplicate msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack}), discarding`,
1036
- {
1037
- ...this.loggingMetadata,
1038
- transportMessage: parsedMsg
1039
- }
1040
- );
1041
- } else {
1042
- const reason = `received out-of-order msg, closing connection (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;
1043
- this.log?.error(reason, {
1044
- ...this.loggingMetadata,
1045
- transportMessage: parsedMsg,
1046
- tags: ["invariant-violation"]
1047
- });
1048
- this.telemetry.span.setStatus({
1049
- code: import_api3.SpanStatusCode.ERROR,
1050
- message: reason
1051
- });
1052
- this.conn.close();
1053
- }
1054
- return;
1055
- }
1056
- this.log?.debug(`received msg`, {
1057
- ...this.loggingMetadata,
1058
- transportMessage: parsedMsg
1059
- });
1060
- this.updateBookkeeping(parsedMsg.ack, parsedMsg.seq);
1061
- if (!isAck(parsedMsg.controlFlags)) {
1062
- this.listeners.onMessage(parsedMsg);
1063
- return;
1064
- }
1065
- this.log?.debug(`discarding msg (ack bit set)`, {
1066
- ...this.loggingMetadata,
1067
- transportMessage: parsedMsg
1068
- });
1069
- if (!this.isActivelyHeartbeating) {
1070
- this.sendHeartbeat();
1071
- }
1072
- };
1073
- _handleStateExit() {
1074
- super._handleStateExit();
1075
- this.conn.removeDataListener();
1076
- this.conn.removeCloseListener();
1077
- this.conn.removeErrorListener();
1078
- if (this.heartbeatHandle) {
1079
- clearInterval(this.heartbeatHandle);
1080
- this.heartbeatHandle = void 0;
1081
- }
1082
- if (this.heartbeatMissTimeout) {
1083
- clearTimeout(this.heartbeatMissTimeout);
1084
- this.heartbeatMissTimeout = void 0;
1085
- }
1086
- }
1087
- _handleClose() {
1088
- super._handleClose();
1089
- this.conn.close();
1090
- }
1091
- };
1092
-
1093
- // transport/sessionStateMachine/SessionBackingOff.ts
1094
- var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
1095
- state = "BackingOff" /* BackingOff */;
1096
- listeners;
1097
- backoffTimeout;
1098
- constructor(props) {
1099
- super(props);
1100
- this.listeners = props.listeners;
1101
- this.backoffTimeout = setTimeout(() => {
1102
- this.listeners.onBackoffFinished();
1103
- }, props.backoffMs);
1104
- }
1105
- _handleClose() {
1106
- super._handleClose();
1107
- }
1108
- _handleStateExit() {
1109
- super._handleStateExit();
1110
- if (this.backoffTimeout) {
1111
- clearTimeout(this.backoffTimeout);
1112
- this.backoffTimeout = void 0;
1113
- }
1114
- }
1115
- };
1116
-
1117
- // codec/adapter.ts
1118
- var import_value = require("@sinclair/typebox/value");
1119
- var CodecMessageAdapter = class {
1120
- constructor(codec) {
1121
- this.codec = codec;
1122
- }
1123
- toBuffer(msg) {
1124
- try {
1125
- return {
1126
- ok: true,
1127
- value: this.codec.toBuffer(msg)
1128
- };
1129
- } catch (e) {
1130
- return {
1131
- ok: false,
1132
- reason: coerceErrorString(e)
1133
- };
1134
- }
1135
- }
1136
- fromBuffer(buf) {
1137
- try {
1138
- const parsedMsg = this.codec.fromBuffer(buf);
1139
- if (!import_value.Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
1140
- return {
1141
- ok: false,
1142
- reason: "transport message schema mismatch"
1143
- };
1144
- }
1145
- return {
1146
- ok: true,
1147
- value: parsedMsg
1148
- };
1149
- } catch (e) {
1150
- return {
1151
- ok: false,
1152
- reason: coerceErrorString(e)
1153
- };
1154
- }
1155
- }
1156
- };
1157
-
1158
- // transport/sessionStateMachine/transitions.ts
1159
- function inheritSharedSession(session) {
1160
- return {
1161
- id: session.id,
1162
- from: session.from,
1163
- to: session.to,
1164
- seq: session.seq,
1165
- ack: session.ack,
1166
- seqSent: session.seqSent,
1167
- sendBuffer: session.sendBuffer,
1168
- telemetry: session.telemetry,
1169
- options: session.options,
1170
- log: session.log,
1171
- tracer: session.tracer,
1172
- protocolVersion: session.protocolVersion,
1173
- codec: session.codec
1174
- };
1175
- }
1176
- function inheritSharedSessionWithGrace(session) {
1177
- return {
1178
- ...inheritSharedSession(session),
1179
- graceExpiryTime: session.graceExpiryTime
1180
- };
1181
- }
1182
- var SessionStateGraph = {
1183
- entrypoints: {
1184
- NoConnection: (to, from, listeners, options, protocolVersion, tracer, log) => {
1185
- const id = `session-${generateId()}`;
1186
- const telemetry = createSessionTelemetryInfo(tracer, id, to, from);
1187
- const sendBuffer = [];
1188
- const session = new SessionNoConnection({
1189
- listeners,
1190
- id,
1191
- from,
1192
- to,
1193
- seq: 0,
1194
- ack: 0,
1195
- seqSent: 0,
1196
- graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
1197
- sendBuffer,
1198
- telemetry,
1199
- options,
1200
- protocolVersion,
1201
- tracer,
1202
- log,
1203
- codec: new CodecMessageAdapter(options.codec)
1204
- });
1205
- session.log?.info(`session ${session.id} created in NoConnection state`, {
1206
- ...session.loggingMetadata,
1207
- tags: ["state-transition"]
1208
- });
1209
- return session;
1210
- },
1211
- WaitingForHandshake: (from, conn, listeners, options, tracer, log) => {
1212
- const session = new SessionWaitingForHandshake({
1213
- conn,
1214
- listeners,
1215
- from,
1216
- options,
1217
- tracer,
1218
- log,
1219
- codec: new CodecMessageAdapter(options.codec)
1220
- });
1221
- session.log?.info(`session created in WaitingForHandshake state`, {
1222
- ...session.loggingMetadata,
1223
- tags: ["state-transition"]
1224
- });
1225
- return session;
1226
- }
1227
- },
1228
- // All of the transitions 'move'/'consume' the old session and return a new one.
1229
- // After a session is transitioned, any usage of the old session will throw.
1230
- transition: {
1231
- // happy path transitions
1232
- NoConnectionToBackingOff: (oldSession, backoffMs, listeners) => {
1233
- const carriedState = inheritSharedSessionWithGrace(oldSession);
1234
- oldSession._handleStateExit();
1235
- const session = new SessionBackingOff({
1236
- backoffMs,
1237
- listeners,
1238
- ...carriedState
1239
- });
1240
- session.log?.info(
1241
- `session ${session.id} transition from NoConnection to BackingOff`,
1242
- {
1243
- ...session.loggingMetadata,
1244
- tags: ["state-transition"]
1245
- }
1246
- );
1247
- return session;
1248
- },
1249
- BackingOffToConnecting: (oldSession, connPromise, listeners) => {
1250
- const carriedState = inheritSharedSessionWithGrace(oldSession);
1251
- oldSession._handleStateExit();
1252
- const session = new SessionConnecting({
1253
- connPromise,
1254
- listeners,
1255
- ...carriedState
1256
- });
1257
- session.log?.info(
1258
- `session ${session.id} transition from BackingOff to Connecting`,
1259
- {
1260
- ...session.loggingMetadata,
1261
- tags: ["state-transition"]
1262
- }
1263
- );
1264
- return session;
1265
- },
1266
- ConnectingToHandshaking: (oldSession, conn, listeners) => {
1267
- const carriedState = inheritSharedSessionWithGrace(oldSession);
1268
- oldSession._handleStateExit();
1269
- const session = new SessionHandshaking({
1270
- conn,
1271
- listeners,
1272
- ...carriedState
1273
- });
1274
- conn.telemetry = createConnectionTelemetryInfo(
1275
- session.tracer,
1276
- conn,
1277
- session.telemetry
1278
- );
1279
- session.log?.info(
1280
- `session ${session.id} transition from Connecting to Handshaking`,
1281
- {
1282
- ...session.loggingMetadata,
1283
- tags: ["state-transition"]
1284
- }
1285
- );
1286
- return session;
1287
- },
1288
- HandshakingToConnected: (oldSession, listeners) => {
1289
- const carriedState = inheritSharedSession(oldSession);
1290
- const conn = oldSession.conn;
1291
- oldSession._handleStateExit();
1292
- const session = new SessionConnected({
1293
- conn,
1294
- listeners,
1295
- ...carriedState
1296
- });
1297
- session.startMissingHeartbeatTimeout();
1298
- session.log?.info(
1299
- `session ${session.id} transition from Handshaking to Connected`,
1300
- {
1301
- ...session.loggingMetadata,
1302
- tags: ["state-transition"]
1303
- }
1304
- );
1305
- return session;
1306
- },
1307
- WaitingForHandshakeToConnected: (pendingSession, oldSession, sessionId, to, propagationCtx, listeners, protocolVersion) => {
1308
- const conn = pendingSession.conn;
1309
- const { from, options } = pendingSession;
1310
- const carriedState = oldSession ? (
1311
- // old session exists, inherit state
1312
- inheritSharedSession(oldSession)
1313
- ) : (
1314
- // old session does not exist, create new state
1315
- {
1316
- id: sessionId,
1317
- from,
1318
- to,
1319
- seq: 0,
1320
- ack: 0,
1321
- seqSent: 0,
1322
- sendBuffer: [],
1323
- telemetry: createSessionTelemetryInfo(
1324
- pendingSession.tracer,
1325
- sessionId,
1326
- to,
1327
- from,
1328
- propagationCtx
1329
- ),
1330
- options,
1331
- tracer: pendingSession.tracer,
1332
- log: pendingSession.log,
1333
- protocolVersion,
1334
- codec: new CodecMessageAdapter(options.codec)
1335
- }
1336
- );
1337
- pendingSession._handleStateExit();
1338
- oldSession?._handleStateExit();
1339
- const session = new SessionConnected({
1340
- conn,
1341
- listeners,
1342
- ...carriedState
1343
- });
1344
- session.startMissingHeartbeatTimeout();
1345
- conn.telemetry = createConnectionTelemetryInfo(
1346
- session.tracer,
1347
- conn,
1348
- session.telemetry
1349
- );
1350
- session.log?.info(
1351
- `session ${session.id} transition from WaitingForHandshake to Connected`,
1352
- {
1353
- ...session.loggingMetadata,
1354
- tags: ["state-transition"]
1355
- }
1356
- );
1357
- return session;
1358
- },
1359
- // disconnect paths
1360
- BackingOffToNoConnection: (oldSession, listeners) => {
1361
- const carriedState = inheritSharedSessionWithGrace(oldSession);
1362
- oldSession._handleStateExit();
1363
- const session = new SessionNoConnection({
1364
- listeners,
1365
- ...carriedState
1366
- });
1367
- session.log?.info(
1368
- `session ${session.id} transition from BackingOff to NoConnection`,
1369
- {
1370
- ...session.loggingMetadata,
1371
- tags: ["state-transition"]
1372
- }
1373
- );
1374
- return session;
1375
- },
1376
- ConnectingToNoConnection: (oldSession, listeners) => {
1377
- const carriedState = inheritSharedSessionWithGrace(oldSession);
1378
- oldSession.bestEffortClose();
1379
- oldSession._handleStateExit();
1380
- const session = new SessionNoConnection({
1381
- listeners,
1382
- ...carriedState
1383
- });
1384
- session.log?.info(
1385
- `session ${session.id} transition from Connecting to NoConnection`,
1386
- {
1387
- ...session.loggingMetadata,
1388
- tags: ["state-transition"]
1389
- }
1390
- );
1391
- return session;
1392
- },
1393
- HandshakingToNoConnection: (oldSession, listeners) => {
1394
- const carriedState = inheritSharedSessionWithGrace(oldSession);
1395
- oldSession.conn.close();
1396
- oldSession._handleStateExit();
1397
- const session = new SessionNoConnection({
1398
- listeners,
1399
- ...carriedState
1400
- });
1401
- session.log?.info(
1402
- `session ${session.id} transition from Handshaking to NoConnection`,
1403
- {
1404
- ...session.loggingMetadata,
1405
- tags: ["state-transition"]
1406
- }
1407
- );
1408
- return session;
1409
- },
1410
- ConnectedToNoConnection: (oldSession, listeners) => {
1411
- const carriedState = inheritSharedSession(oldSession);
1412
- const graceExpiryTime = Date.now() + oldSession.options.sessionDisconnectGraceMs;
1413
- oldSession.conn.close();
1414
- oldSession._handleStateExit();
1415
- const session = new SessionNoConnection({
1416
- listeners,
1417
- graceExpiryTime,
1418
- ...carriedState
1419
- });
1420
- session.log?.info(
1421
- `session ${session.id} transition from Connected to NoConnection`,
1422
- {
1423
- ...session.loggingMetadata,
1424
- tags: ["state-transition"]
1425
- }
1426
- );
1427
- return session;
1428
- }
1429
- }
1430
- };
1431
- var transitions = SessionStateGraph.transition;
1432
- var ClientSessionStateGraph = {
1433
- entrypoint: SessionStateGraph.entrypoints.NoConnection,
1434
- transition: {
1435
- // happy paths
1436
- // NoConnection -> BackingOff: attempt to connect
1437
- NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,
1438
- // BackingOff -> Connecting: backoff period elapsed, start connection
1439
- BackingOffToConnecting: transitions.BackingOffToConnecting,
1440
- // Connecting -> Handshaking: connection established, start handshake
1441
- ConnectingToHandshaking: transitions.ConnectingToHandshaking,
1442
- // Handshaking -> Connected: handshake complete, session ready
1443
- HandshakingToConnected: transitions.HandshakingToConnected,
1444
- // disconnect paths
1445
- // BackingOff -> NoConnection: unused
1446
- BackingOffToNoConnection: transitions.BackingOffToNoConnection,
1447
- // Connecting -> NoConnection: connection failed or connection timeout
1448
- ConnectingToNoConnection: transitions.ConnectingToNoConnection,
1449
- // Handshaking -> NoConnection: connection closed or handshake timeout
1450
- HandshakingToNoConnection: transitions.HandshakingToNoConnection,
1451
- // Connected -> NoConnection: connection closed
1452
- ConnectedToNoConnection: transitions.ConnectedToNoConnection
1453
- // destroy/close paths
1454
- // NoConnection -> x: grace period elapsed
1455
- // BackingOff -> x: grace period elapsed
1456
- // Connecting -> x: grace period elapsed
1457
- // Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection
1458
- // Connected -> x: grace period elapsed or invalid message
1459
- }
1460
- };
1461
- var ServerSessionStateGraph = {
1462
- entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,
1463
- transition: {
1464
- // happy paths
1465
- // WaitingForHandshake -> Connected: handshake complete, session ready
1466
- WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,
1467
- // disconnect paths
1468
- // Connected -> NoConnection: connection closed
1469
- ConnectedToNoConnection: transitions.ConnectedToNoConnection
1470
- // destroy/close paths
1471
- // WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed
1472
- }
1473
- };
1474
-
1475
- // transport/transport.ts
1476
- var Transport = class {
1477
- /**
1478
- * The status of the transport.
1479
- */
1480
- status;
1481
- /**
1482
- * The client ID of this transport.
1483
- */
1484
- clientId;
1485
- /**
1486
- * The event dispatcher for handling events of type EventTypes.
1487
- */
1488
- eventDispatcher;
1489
- /**
1490
- * The options for this transport.
1491
- */
1492
- options;
1493
- log;
1494
- tracer;
1495
- sessions;
1496
- /**
1497
- * Creates a new Transport instance.
1498
- * @param codec The codec used to encode and decode messages.
1499
- * @param clientId The client ID of this transport.
1500
- */
1501
- constructor(clientId, providedOptions) {
1502
- this.options = { ...defaultTransportOptions, ...providedOptions };
1503
- this.eventDispatcher = new EventDispatcher();
1504
- this.clientId = clientId;
1505
- this.status = "open";
1506
- this.sessions = /* @__PURE__ */ new Map();
1507
- this.tracer = getTracer();
1508
- }
1509
- bindLogger(fn, level) {
1510
- if (typeof fn === "function") {
1511
- this.log = createLogProxy(new BaseLogger(fn, level));
1512
- return;
1513
- }
1514
- this.log = createLogProxy(fn);
1515
- }
1516
- /**
1517
- * Called when a message is received by this transport.
1518
- * You generally shouldn't need to override this in downstream transport implementations.
1519
- * @param message The received message.
1520
- */
1521
- handleMsg(message) {
1522
- if (this.getStatus() !== "open") return;
1523
- this.eventDispatcher.dispatchEvent("message", message);
1524
- }
1525
- /**
1526
- * Adds a listener to this transport.
1527
- * @param the type of event to listen for
1528
- * @param handler The message handler to add.
1529
- */
1530
- addEventListener(type, handler) {
1531
- this.eventDispatcher.addEventListener(type, handler);
1532
- }
1533
- /**
1534
- * Removes a listener from this transport.
1535
- * @param the type of event to un-listen on
1536
- * @param handler The message handler to remove.
1537
- */
1538
- removeEventListener(type, handler) {
1539
- this.eventDispatcher.removeEventListener(type, handler);
1540
- }
1541
- protocolError(message) {
1542
- this.eventDispatcher.dispatchEvent("protocolError", message);
1543
- }
1544
- /**
1545
- * Default close implementation for transports. You should override this in the downstream
1546
- * implementation if you need to do any additional cleanup and call super.close() at the end.
1547
- * Closes the transport. Any messages sent while the transport is closed will be silently discarded.
1548
- */
1549
- close() {
1550
- this.status = "closed";
1551
- const sessions = Array.from(this.sessions.values());
1552
- for (const session of sessions) {
1553
- this.deleteSession(session);
1554
- }
1555
- this.eventDispatcher.dispatchEvent("transportStatus", {
1556
- status: this.status
1557
- });
1558
- this.eventDispatcher.removeAllListeners();
1559
- this.log?.info(`manually closed transport`, { clientId: this.clientId });
1560
- }
1561
- getStatus() {
1562
- return this.status;
1563
- }
1564
- // state transitions
1565
- createSession(session) {
1566
- const activeSession = this.sessions.get(session.to);
1567
- if (activeSession) {
1568
- const msg = `attempt to create session for ${session.to} but active session (${activeSession.id}) already exists`;
1569
- this.log?.error(msg, {
1570
- ...session.loggingMetadata,
1571
- tags: ["invariant-violation"]
1572
- });
1573
- throw new Error(msg);
1574
- }
1575
- this.sessions.set(session.to, session);
1576
- this.eventDispatcher.dispatchEvent("sessionStatus", {
1577
- status: "created",
1578
- session
1579
- });
1580
- this.eventDispatcher.dispatchEvent("sessionTransition", {
1581
- state: session.state,
1582
- id: session.id
1583
- });
1584
- }
1585
- updateSession(session) {
1586
- const activeSession = this.sessions.get(session.to);
1587
- if (!activeSession) {
1588
- const msg = `attempt to transition session for ${session.to} but no active session exists`;
1589
- this.log?.error(msg, {
1590
- ...session.loggingMetadata,
1591
- tags: ["invariant-violation"]
1592
- });
1593
- throw new Error(msg);
1594
- }
1595
- if (activeSession.id !== session.id) {
1596
- const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;
1597
- this.log?.error(msg, {
1598
- ...session.loggingMetadata,
1599
- tags: ["invariant-violation"]
1600
- });
1601
- throw new Error(msg);
1602
- }
1603
- this.sessions.set(session.to, session);
1604
- this.eventDispatcher.dispatchEvent("sessionTransition", {
1605
- state: session.state,
1606
- id: session.id
1607
- });
1608
- }
1609
- deleteSession(session, options) {
1610
- if (session._isConsumed) return;
1611
- const loggingMetadata = session.loggingMetadata;
1612
- if (loggingMetadata.tags && options?.unhealthy) {
1613
- loggingMetadata.tags.push("unhealthy-session");
1614
- }
1615
- session.log?.info(`closing session ${session.id}`, loggingMetadata);
1616
- this.eventDispatcher.dispatchEvent("sessionStatus", {
1617
- status: "closing",
1618
- session
1619
- });
1620
- const to = session.to;
1621
- session.close();
1622
- this.sessions.delete(to);
1623
- this.eventDispatcher.dispatchEvent("sessionStatus", {
1624
- status: "closed",
1625
- session: { id: session.id, to }
1626
- });
1627
- }
1628
- // common listeners
1629
- onSessionGracePeriodElapsed(session) {
1630
- this.log?.info(
1631
- `session to ${session.to} grace period elapsed, closing`,
1632
- session.loggingMetadata
1633
- );
1634
- this.deleteSession(session);
1635
- }
1636
- onConnectingFailed(session) {
1637
- const noConnectionSession = SessionStateGraph.transition.ConnectingToNoConnection(session, {
1638
- onSessionGracePeriodElapsed: () => {
1639
- this.onSessionGracePeriodElapsed(noConnectionSession);
1640
- },
1641
- onMessageSendFailure: (msg, reason) => {
1642
- this.log?.error(`failed to send message: ${reason}`, {
1643
- ...noConnectionSession.loggingMetadata,
1644
- transportMessage: msg
1645
- });
1646
- this.protocolError({
1647
- type: ProtocolError.MessageSendFailure,
1648
- message: reason
1649
- });
1650
- this.deleteSession(noConnectionSession, { unhealthy: true });
1651
- }
1652
- });
1653
- this.updateSession(noConnectionSession);
1654
- return noConnectionSession;
1655
- }
1656
- onConnClosed(session) {
1657
- let noConnectionSession;
1658
- const listeners = {
1659
- onSessionGracePeriodElapsed: () => {
1660
- this.onSessionGracePeriodElapsed(noConnectionSession);
1661
- },
1662
- onMessageSendFailure: (msg, reason) => {
1663
- this.log?.error(`failed to send message: ${reason}`, {
1664
- ...noConnectionSession.loggingMetadata,
1665
- transportMessage: msg
1666
- });
1667
- this.protocolError({
1668
- type: ProtocolError.MessageSendFailure,
1669
- message: reason
1670
- });
1671
- this.deleteSession(noConnectionSession, { unhealthy: true });
1672
- }
1673
- };
1674
- if (session.state === "Handshaking" /* Handshaking */) {
1675
- noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(
1676
- session,
1677
- listeners
1678
- );
1679
- } else {
1680
- noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(
1681
- session,
1682
- listeners
1683
- );
1684
- }
1685
- this.updateSession(noConnectionSession);
1686
- return noConnectionSession;
1687
- }
1688
- /**
1689
- * Gets a send closure scoped to a specific session. Sending using the returned
1690
- * closure after the session has transitioned to a different state will be a noop.
1691
- *
1692
- * Session objects themselves can become stale as they transition between
1693
- * states. As stale sessions cannot be used again (and will throw), holding
1694
- * onto a session object is not recommended.
1695
- */
1696
- getSessionBoundSendFn(to, sessionId) {
1697
- if (this.getStatus() !== "open") {
1698
- throw new Error("cannot get a bound send function on a closed transport");
1699
- }
1700
- return (msg) => {
1701
- const session = this.sessions.get(to);
1702
- if (!session) {
1703
- throw new Error(
1704
- `session scope for ${sessionId} has ended (close), can't send`
1705
- );
1706
- }
1707
- const sameSession = session.id === sessionId;
1708
- if (!sameSession || session._isConsumed) {
1709
- throw new Error(
1710
- `session scope for ${sessionId} has ended (transition), can't send`
1711
- );
1712
- }
1713
- const res = session.send(msg);
1714
- if (!res.ok) {
1715
- throw new Error(res.reason);
1716
- }
1717
- return res.value;
1718
- };
1719
- }
1720
- };
1721
-
1722
- // transport/server.ts
1723
- var import_value2 = require("@sinclair/typebox/value");
1724
- var ServerTransport = class extends Transport {
1725
- /**
1726
- * The options for this transport.
1727
- */
1728
- options;
1729
- /**
1730
- * Optional handshake options for the server.
1731
- */
1732
- handshakeExtensions;
1733
- /**
1734
- * A map of session handshake data for each session.
1735
- */
1736
- sessionHandshakeMetadata = /* @__PURE__ */ new Map();
1737
- sessions = /* @__PURE__ */ new Map();
1738
- pendingSessions = /* @__PURE__ */ new Set();
1739
- constructor(clientId, providedOptions) {
1740
- super(clientId, providedOptions);
1741
- this.sessions = /* @__PURE__ */ new Map();
1742
- this.options = {
1743
- ...defaultServerTransportOptions,
1744
- ...providedOptions
1745
- };
1746
- this.log?.info(`initiated server transport`, {
1747
- clientId: this.clientId,
1748
- protocolVersion: currentProtocolVersion
1749
- });
1750
- }
1751
- extendHandshake(options) {
1752
- this.handshakeExtensions = options;
1753
- }
1754
- deletePendingSession(pendingSession) {
1755
- pendingSession.close();
1756
- this.pendingSessions.delete(pendingSession);
1757
- }
1758
- deleteSession(session, options) {
1759
- this.sessionHandshakeMetadata.delete(session.to);
1760
- super.deleteSession(session, options);
1761
- }
1762
- handleConnection(conn) {
1763
- if (this.getStatus() !== "open") return;
1764
- this.log?.info(`new incoming connection`, {
1765
- ...conn.loggingMetadata,
1766
- clientId: this.clientId
1767
- });
1768
- let receivedHandshake = false;
1769
- const pendingSession = ServerSessionStateGraph.entrypoint(
1770
- this.clientId,
1771
- conn,
1772
- {
1773
- onConnectionClosed: () => {
1774
- this.log?.warn(
1775
- `connection from unknown closed before handshake finished`,
1776
- pendingSession.loggingMetadata
1777
- );
1778
- this.deletePendingSession(pendingSession);
1779
- },
1780
- onConnectionErrored: (err) => {
1781
- const errorString = coerceErrorString(err);
1782
- this.log?.warn(
1783
- `connection from unknown errored before handshake finished: ${errorString}`,
1784
- pendingSession.loggingMetadata
1785
- );
1786
- this.deletePendingSession(pendingSession);
1787
- },
1788
- onHandshakeTimeout: () => {
1789
- this.log?.warn(
1790
- `connection from unknown timed out before handshake finished`,
1791
- pendingSession.loggingMetadata
1792
- );
1793
- this.deletePendingSession(pendingSession);
1794
- },
1795
- onHandshake: (msg) => {
1796
- if (receivedHandshake) {
1797
- this.log?.error(
1798
- `received multiple handshake messages from pending session`,
1799
- {
1800
- ...pendingSession.loggingMetadata,
1801
- connectedTo: msg.from,
1802
- transportMessage: msg
1803
- }
1804
- );
1805
- this.deletePendingSession(pendingSession);
1806
- return;
1807
- }
1808
- receivedHandshake = true;
1809
- void this.onHandshakeRequest(pendingSession, msg);
1810
- },
1811
- onInvalidHandshake: (reason, code) => {
1812
- this.log?.error(
1813
- `invalid handshake: ${reason}`,
1814
- pendingSession.loggingMetadata
1815
- );
1816
- this.deletePendingSession(pendingSession);
1817
- this.protocolError({
1818
- type: ProtocolError.HandshakeFailed,
1819
- code,
1820
- message: reason
1821
- });
1822
- }
1823
- },
1824
- this.options,
1825
- this.tracer,
1826
- this.log
1827
- );
1828
- this.pendingSessions.add(pendingSession);
1829
- }
1830
- rejectHandshakeRequest(session, to, reason, code, metadata) {
1831
- session.conn.telemetry?.span.setStatus({
1832
- code: import_api4.SpanStatusCode.ERROR,
1833
- message: reason
1834
- });
1835
- this.log?.warn(reason, metadata);
1836
- const responseMsg = handshakeResponseMessage({
1837
- from: this.clientId,
1838
- to,
1839
- status: {
1840
- ok: false,
1841
- code,
1842
- reason
1843
- }
1844
- });
1845
- const res = session.sendHandshake(responseMsg);
1846
- if (!res.ok) {
1847
- this.log?.error(`failed to send handshake response: ${res.reason}`, {
1848
- ...session.loggingMetadata,
1849
- transportMessage: responseMsg
1850
- });
1851
- this.protocolError({
1852
- type: ProtocolError.MessageSendFailure,
1853
- message: res.reason
1854
- });
1855
- this.deletePendingSession(session);
1856
- return;
1857
- }
1858
- this.protocolError({
1859
- type: ProtocolError.HandshakeFailed,
1860
- code,
1861
- message: reason
1862
- });
1863
- this.deletePendingSession(session);
1864
- }
1865
- async onHandshakeRequest(session, msg) {
1866
- if (!import_value2.Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {
1867
- this.rejectHandshakeRequest(
1868
- session,
1869
- msg.from,
1870
- "received invalid handshake request",
1871
- "MALFORMED_HANDSHAKE",
1872
- {
1873
- ...session.loggingMetadata,
1874
- transportMessage: msg,
1875
- connectedTo: msg.from,
1876
- validationErrors: [
1877
- ...import_value2.Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload)
1878
- ]
1879
- }
1880
- );
1881
- return;
1882
- }
1883
- const gotVersion = msg.payload.protocolVersion;
1884
- if (!isAcceptedProtocolVersion(gotVersion)) {
1885
- this.rejectHandshakeRequest(
1886
- session,
1887
- msg.from,
1888
- `expected protocol version oneof [${acceptedProtocolVersions.toString()}], got ${gotVersion}`,
1889
- "PROTOCOL_VERSION_MISMATCH",
1890
- {
1891
- ...session.loggingMetadata,
1892
- connectedTo: msg.from,
1893
- transportMessage: msg
1894
- }
1895
- );
1896
- return;
1897
- }
1898
- let parsedMetadata = {};
1899
- if (this.handshakeExtensions) {
1900
- if (!import_value2.Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
1901
- this.rejectHandshakeRequest(
1902
- session,
1903
- msg.from,
1904
- "received malformed handshake metadata",
1905
- "MALFORMED_HANDSHAKE_META",
1906
- {
1907
- ...session.loggingMetadata,
1908
- connectedTo: msg.from,
1909
- validationErrors: [
1910
- ...import_value2.Value.Errors(
1911
- this.handshakeExtensions.schema,
1912
- msg.payload.metadata
1913
- )
1914
- ]
1915
- }
1916
- );
1917
- return;
1918
- }
1919
- const previousParsedMetadata = this.sessionHandshakeMetadata.get(
1920
- msg.from
1921
- );
1922
- let parsedMetadataOrFailureCode;
1923
- try {
1924
- parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(
1925
- msg.payload.metadata,
1926
- previousParsedMetadata
1927
- );
1928
- } catch (err) {
1929
- this.rejectHandshakeRequest(
1930
- session,
1931
- msg.from,
1932
- `handshake validation threw: ${coerceErrorString(err)}`,
1933
- "REJECTED_BY_CUSTOM_HANDLER",
1934
- {
1935
- ...session.loggingMetadata,
1936
- connectedTo: msg.from,
1937
- clientId: this.clientId
1938
- }
1939
- );
1940
- return;
1941
- }
1942
- if (session._isConsumed) {
1943
- return;
1944
- }
1945
- if (import_value2.Value.Check(
1946
- HandshakeErrorCustomHandlerFatalResponseCodes,
1947
- parsedMetadataOrFailureCode
1948
- )) {
1949
- this.rejectHandshakeRequest(
1950
- session,
1951
- msg.from,
1952
- "rejected by handshake handler",
1953
- parsedMetadataOrFailureCode,
1954
- {
1955
- ...session.loggingMetadata,
1956
- connectedTo: msg.from,
1957
- clientId: this.clientId
1958
- }
1959
- );
1960
- return;
1961
- }
1962
- parsedMetadata = parsedMetadataOrFailureCode;
1963
- }
1964
- let connectCase = "new session";
1965
- const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
1966
- const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
1967
- let oldSession = this.sessions.get(msg.from);
1968
- if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
1969
- connectCase = "transparent reconnection";
1970
- const ourNextSeq = oldSession.nextSeq();
1971
- const ourAck = oldSession.ack;
1972
- if (clientNextSentSeq > ourAck) {
1973
- this.rejectHandshakeRequest(
1974
- session,
1975
- msg.from,
1976
- `client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
1977
- "SESSION_STATE_MISMATCH",
1978
- {
1979
- ...session.loggingMetadata,
1980
- connectedTo: msg.from,
1981
- transportMessage: msg
1982
- }
1983
- );
1984
- return;
1985
- }
1986
- if (ourNextSeq > clientNextExpectedSeq) {
1987
- this.rejectHandshakeRequest(
1988
- session,
1989
- msg.from,
1990
- `server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
1991
- "SESSION_STATE_MISMATCH",
1992
- {
1993
- ...session.loggingMetadata,
1994
- connectedTo: msg.from,
1995
- transportMessage: msg
1996
- }
1997
- );
1998
- return;
1999
- }
2000
- if (oldSession.state !== "NoConnection" /* NoConnection */) {
2001
- const noConnectionSession = ServerSessionStateGraph.transition.ConnectedToNoConnection(
2002
- oldSession,
2003
- {
2004
- onSessionGracePeriodElapsed: () => {
2005
- this.onSessionGracePeriodElapsed(noConnectionSession);
2006
- },
2007
- onMessageSendFailure: (msg2, reason) => {
2008
- this.log?.error(`failed to send message: ${reason}`, {
2009
- ...noConnectionSession.loggingMetadata,
2010
- transportMessage: msg2
2011
- });
2012
- this.protocolError({
2013
- type: ProtocolError.MessageSendFailure,
2014
- message: reason
2015
- });
2016
- this.deleteSession(noConnectionSession, { unhealthy: true });
2017
- }
2018
- }
2019
- );
2020
- oldSession = noConnectionSession;
2021
- this.updateSession(oldSession);
2022
- }
2023
- } else if (oldSession) {
2024
- connectCase = "hard reconnection";
2025
- this.log?.info(
2026
- `client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,
2027
- {
2028
- ...session.loggingMetadata,
2029
- connectedTo: msg.from,
2030
- sessionId: msg.payload.sessionId
2031
- }
2032
- );
2033
- this.deleteSession(oldSession);
2034
- oldSession = void 0;
2035
- }
2036
- if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {
2037
- connectCase = "unknown session";
2038
- 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}`;
2039
- this.rejectHandshakeRequest(
2040
- session,
2041
- msg.from,
2042
- rejectionMessage,
2043
- "SESSION_STATE_MISMATCH",
2044
- {
2045
- ...session.loggingMetadata,
2046
- connectedTo: msg.from,
2047
- transportMessage: msg
2048
- }
2049
- );
2050
- return;
2051
- }
2052
- const sessionId = msg.payload.sessionId;
2053
- this.log?.info(
2054
- `handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,
2055
- {
2056
- ...session.loggingMetadata,
2057
- connectedTo: msg.from
2058
- }
2059
- );
2060
- const responseMsg = handshakeResponseMessage({
2061
- from: this.clientId,
2062
- to: msg.from,
2063
- status: {
2064
- ok: true,
2065
- sessionId
2066
- }
2067
- });
2068
- const res = session.sendHandshake(responseMsg);
2069
- if (!res.ok) {
2070
- this.log?.error(`failed to send handshake response: ${res.reason}`, {
2071
- ...session.loggingMetadata,
2072
- transportMessage: responseMsg
2073
- });
2074
- this.protocolError({
2075
- type: ProtocolError.MessageSendFailure,
2076
- message: res.reason
2077
- });
2078
- this.deletePendingSession(session);
2079
- return;
2080
- }
2081
- this.pendingSessions.delete(session);
2082
- const connectedSession = ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(
2083
- session,
2084
- // by this point oldSession is either no connection or we dont have an old session
2085
- oldSession,
2086
- sessionId,
2087
- msg.from,
2088
- msg.tracing,
2089
- {
2090
- onConnectionErrored: (err) => {
2091
- const errStr = coerceErrorString(err);
2092
- this.log?.warn(
2093
- `connection to ${connectedSession.to} errored: ${errStr}`,
2094
- connectedSession.loggingMetadata
2095
- );
2096
- },
2097
- onConnectionClosed: () => {
2098
- this.log?.info(
2099
- `connection to ${connectedSession.to} closed`,
2100
- connectedSession.loggingMetadata
2101
- );
2102
- this.onConnClosed(connectedSession);
2103
- },
2104
- onMessage: (msg2) => {
2105
- this.handleMsg(msg2);
2106
- },
2107
- onInvalidMessage: (reason) => {
2108
- this.log?.error(`invalid message: ${reason}`, {
2109
- ...connectedSession.loggingMetadata,
2110
- transportMessage: msg
2111
- });
2112
- this.protocolError({
2113
- type: ProtocolError.InvalidMessage,
2114
- message: reason
2115
- });
2116
- this.deleteSession(connectedSession, { unhealthy: true });
2117
- },
2118
- onMessageSendFailure: (msg2, reason) => {
2119
- this.log?.error(`failed to send message: ${reason}`, {
2120
- ...connectedSession.loggingMetadata,
2121
- transportMessage: msg2
2122
- });
2123
- this.protocolError({
2124
- type: ProtocolError.MessageSendFailure,
2125
- message: reason
2126
- });
2127
- this.deleteSession(connectedSession, { unhealthy: true });
2128
- }
2129
- },
2130
- gotVersion
2131
- );
2132
- const bufferSendRes = connectedSession.sendBufferedMessages();
2133
- if (!bufferSendRes.ok) {
2134
- return;
2135
- }
2136
- this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
2137
- if (oldSession) {
2138
- this.updateSession(connectedSession);
2139
- } else {
2140
- this.createSession(connectedSession);
2141
- }
2142
- connectedSession.startActiveHeartbeat();
2143
- }
2144
- };
2145
-
2146
- // transport/impls/ws/server.ts
2147
- function cleanHeaders(headers) {
2148
- const cleanedHeaders = {};
2149
- for (const [key, value] of Object.entries(headers)) {
2150
- if (!key.startsWith("sec-") && value) {
2151
- const cleanedValue = Array.isArray(value) ? value[0] : value;
2152
- cleanedHeaders[key] = cleanedValue;
2153
- }
2154
- }
2155
- return cleanedHeaders;
2156
- }
2157
- var WebSocketServerTransport = class extends ServerTransport {
2158
- wss;
2159
- constructor(wss, clientId, providedOptions) {
2160
- super(clientId, providedOptions);
2161
- this.wss = wss;
2162
- this.wss.on("connection", this.connectionHandler);
2163
- }
2164
- connectionHandler = (ws, req) => {
2165
- const conn = new WebSocketConnection(ws, {
2166
- headers: cleanHeaders(req.headersDistinct)
2167
- });
2168
- this.handleConnection(conn);
2169
- };
2170
- close() {
2171
- super.close();
2172
- this.wss.off("connection", this.connectionHandler);
2173
- }
2174
- };
2175
- // Annotate the CommonJS export names for ESM import in node:
2176
- 0 && (module.exports = {
2177
- WebSocketServerTransport
2178
- });
2179
- //# sourceMappingURL=server.cjs.map