@replit/river 0.26.1 → 0.26.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-4W5LENT2.js → chunk-3JCZNGF7.js} +2 -2
- package/dist/{chunk-XRI2BXMM.js → chunk-BB2E5L4U.js} +25 -13
- package/dist/chunk-BB2E5L4U.js.map +1 -0
- package/dist/{chunk-6UVTCZ6K.js → chunk-JI6FFDY5.js} +5 -4
- package/dist/{chunk-6UVTCZ6K.js.map → chunk-JI6FFDY5.js.map} +1 -1
- package/dist/{chunk-UQOD22AN.js → chunk-MZELCWJK.js} +2 -2
- package/dist/{chunk-UQOD22AN.js.map → chunk-MZELCWJK.js.map} +1 -1
- package/dist/{chunk-M43R4RPL.js → chunk-OCL2FUTQ.js} +128 -45
- package/dist/chunk-OCL2FUTQ.js.map +1 -0
- package/dist/{chunk-IVNX5H6C.js → chunk-X35QRIA5.js} +54 -55
- package/dist/chunk-X35QRIA5.js.map +1 -0
- package/dist/{chunk-AYIMQWS7.js → chunk-ZY2HYJ5Y.js} +2 -2
- package/dist/{client-0f636b3a.d.ts → client-1894a9c9.d.ts} +2 -4
- package/dist/{connection-07e97a79.d.ts → connection-03ffb583.d.ts} +1 -1
- package/dist/{handshake-8752f79e.d.ts → handshake-154a0bb2.d.ts} +82 -39
- package/dist/logging/index.d.cts +1 -1
- package/dist/logging/index.d.ts +1 -1
- package/dist/{message-57296605.d.ts → message-ff78a233.d.ts} +1 -1
- package/dist/router/index.cjs +1 -1
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +9 -8
- package/dist/router/index.d.ts +9 -8
- package/dist/router/index.js +2 -2
- package/dist/{server-e304daec.d.ts → server-1f5eb427.d.ts} +27 -6
- package/dist/{services-fc99aae1.d.ts → services-491d8c32.d.ts} +3 -3
- package/dist/transport/impls/ws/client.cjs +153 -56
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +5 -4
- package/dist/transport/impls/ws/client.d.ts +5 -4
- package/dist/transport/impls/ws/client.js +8 -7
- package/dist/transport/impls/ws/client.js.map +1 -1
- package/dist/transport/impls/ws/server.cjs +179 -96
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +5 -4
- package/dist/transport/impls/ws/server.d.ts +5 -4
- package/dist/transport/impls/ws/server.js +5 -5
- package/dist/transport/index.cjs +200 -105
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +5 -4
- package/dist/transport/index.d.ts +5 -4
- package/dist/transport/index.js +5 -5
- package/dist/util/testHelpers.cjs +127 -44
- package/dist/util/testHelpers.cjs.map +1 -1
- package/dist/util/testHelpers.d.cts +5 -4
- package/dist/util/testHelpers.d.ts +5 -4
- package/dist/util/testHelpers.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-IVNX5H6C.js.map +0 -1
- package/dist/chunk-M43R4RPL.js.map +0 -1
- package/dist/chunk-XRI2BXMM.js.map +0 -1
- /package/dist/{chunk-4W5LENT2.js.map → chunk-3JCZNGF7.js.map} +0 -0
- /package/dist/{chunk-AYIMQWS7.js.map → chunk-ZY2HYJ5Y.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createSessionTelemetryInfo,
|
|
4
4
|
generateId,
|
|
5
5
|
isAck
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MZELCWJK.js";
|
|
7
7
|
import {
|
|
8
8
|
NaiveJsonCodec
|
|
9
9
|
} from "./chunk-4PVU7J25.js";
|
|
@@ -15,6 +15,7 @@ var defaultTransportOptions = {
|
|
|
15
15
|
sessionDisconnectGraceMs: 5e3,
|
|
16
16
|
connectionTimeoutMs: 2e3,
|
|
17
17
|
handshakeTimeoutMs: 1e3,
|
|
18
|
+
enableTransparentSessionReconnects: true,
|
|
18
19
|
codec: NaiveJsonCodec
|
|
19
20
|
};
|
|
20
21
|
var defaultConnectionRetryOptions = {
|
|
@@ -68,6 +69,7 @@ var StateMachineState = class {
|
|
|
68
69
|
}
|
|
69
70
|
if (prop === "_handleClose") {
|
|
70
71
|
return () => {
|
|
72
|
+
target._isConsumed = true;
|
|
71
73
|
target._handleStateExit();
|
|
72
74
|
target._handleClose();
|
|
73
75
|
};
|
|
@@ -148,15 +150,18 @@ var IdentifiedSession = class extends CommonSession {
|
|
|
148
150
|
}
|
|
149
151
|
get loggingMetadata() {
|
|
150
152
|
const spanContext = this.telemetry.span.spanContext();
|
|
151
|
-
|
|
153
|
+
const metadata = {
|
|
152
154
|
clientId: this.from,
|
|
153
155
|
connectedTo: this.to,
|
|
154
|
-
sessionId: this.id
|
|
155
|
-
|
|
156
|
+
sessionId: this.id
|
|
157
|
+
};
|
|
158
|
+
if (this.telemetry.span.isRecording()) {
|
|
159
|
+
metadata.telemetry = {
|
|
156
160
|
traceId: spanContext.traceId,
|
|
157
161
|
spanId: spanContext.spanId
|
|
158
|
-
}
|
|
159
|
-
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return metadata;
|
|
160
165
|
}
|
|
161
166
|
constructMsg(partialMsg) {
|
|
162
167
|
const msg = {
|
|
@@ -185,9 +190,32 @@ var IdentifiedSession = class extends CommonSession {
|
|
|
185
190
|
this.telemetry.span.end();
|
|
186
191
|
}
|
|
187
192
|
};
|
|
193
|
+
var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
|
|
194
|
+
graceExpiryTime;
|
|
195
|
+
gracePeriodTimeout;
|
|
196
|
+
listeners;
|
|
197
|
+
constructor(props) {
|
|
198
|
+
super(props);
|
|
199
|
+
this.listeners = props.listeners;
|
|
200
|
+
this.graceExpiryTime = props.graceExpiryTime;
|
|
201
|
+
this.gracePeriodTimeout = setTimeout(() => {
|
|
202
|
+
this.listeners.onSessionGracePeriodElapsed();
|
|
203
|
+
}, this.graceExpiryTime - Date.now());
|
|
204
|
+
}
|
|
205
|
+
_handleStateExit() {
|
|
206
|
+
super._handleStateExit();
|
|
207
|
+
if (this.gracePeriodTimeout) {
|
|
208
|
+
clearTimeout(this.gracePeriodTimeout);
|
|
209
|
+
this.gracePeriodTimeout = void 0;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
_handleClose() {
|
|
213
|
+
super._handleClose();
|
|
214
|
+
}
|
|
215
|
+
};
|
|
188
216
|
|
|
189
217
|
// transport/sessionStateMachine/SessionConnecting.ts
|
|
190
|
-
var SessionConnecting = class extends
|
|
218
|
+
var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
|
|
191
219
|
state = "Connecting" /* Connecting */;
|
|
192
220
|
connPromise;
|
|
193
221
|
listeners;
|
|
@@ -215,13 +243,24 @@ var SessionConnecting = class extends IdentifiedSession {
|
|
|
215
243
|
// close a pending connection if it resolves, ignore errors if the promise
|
|
216
244
|
// ends up rejected anyways
|
|
217
245
|
bestEffortClose() {
|
|
218
|
-
void this.connPromise.then((conn) =>
|
|
246
|
+
void this.connPromise.then((conn) => {
|
|
247
|
+
conn.close();
|
|
248
|
+
this.log?.info(
|
|
249
|
+
"connection eventually resolved but session has transitioned, closed connection",
|
|
250
|
+
{
|
|
251
|
+
...this.loggingMetadata,
|
|
252
|
+
...conn.loggingMetadata
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}).catch(() => {
|
|
219
256
|
});
|
|
220
257
|
}
|
|
221
258
|
_handleStateExit() {
|
|
222
259
|
super._handleStateExit();
|
|
223
|
-
|
|
224
|
-
|
|
260
|
+
if (this.connectionTimeout) {
|
|
261
|
+
clearTimeout(this.connectionTimeout);
|
|
262
|
+
this.connectionTimeout = void 0;
|
|
263
|
+
}
|
|
225
264
|
}
|
|
226
265
|
_handleClose() {
|
|
227
266
|
this.bestEffortClose();
|
|
@@ -230,26 +269,13 @@ var SessionConnecting = class extends IdentifiedSession {
|
|
|
230
269
|
};
|
|
231
270
|
|
|
232
271
|
// transport/sessionStateMachine/SessionNoConnection.ts
|
|
233
|
-
var SessionNoConnection = class extends
|
|
272
|
+
var SessionNoConnection = class extends IdentifiedSessionWithGracePeriod {
|
|
234
273
|
state = "NoConnection" /* NoConnection */;
|
|
235
|
-
listeners;
|
|
236
|
-
gracePeriodTimeout;
|
|
237
|
-
constructor(props) {
|
|
238
|
-
super(props);
|
|
239
|
-
this.listeners = props.listeners;
|
|
240
|
-
this.gracePeriodTimeout = setTimeout(() => {
|
|
241
|
-
this.listeners.onSessionGracePeriodElapsed();
|
|
242
|
-
}, this.options.sessionDisconnectGraceMs);
|
|
243
|
-
}
|
|
244
274
|
_handleClose() {
|
|
245
275
|
super._handleClose();
|
|
246
276
|
}
|
|
247
277
|
_handleStateExit() {
|
|
248
278
|
super._handleStateExit();
|
|
249
|
-
if (this.gracePeriodTimeout) {
|
|
250
|
-
clearTimeout(this.gracePeriodTimeout);
|
|
251
|
-
this.gracePeriodTimeout = void 0;
|
|
252
|
-
}
|
|
253
279
|
}
|
|
254
280
|
};
|
|
255
281
|
|
|
@@ -270,6 +296,13 @@ var SessionWaitingForHandshake = class extends CommonSession {
|
|
|
270
296
|
this.conn.addErrorListener(this.listeners.onConnectionErrored);
|
|
271
297
|
this.conn.addCloseListener(this.listeners.onConnectionClosed);
|
|
272
298
|
}
|
|
299
|
+
get loggingMetadata() {
|
|
300
|
+
return {
|
|
301
|
+
clientId: this.from,
|
|
302
|
+
connId: this.conn.id,
|
|
303
|
+
...this.conn.loggingMetadata
|
|
304
|
+
};
|
|
305
|
+
}
|
|
273
306
|
onHandshakeData = (msg) => {
|
|
274
307
|
const parsedMsg = this.parseMsg(msg);
|
|
275
308
|
if (parsedMsg === null) {
|
|
@@ -281,12 +314,6 @@ var SessionWaitingForHandshake = class extends CommonSession {
|
|
|
281
314
|
}
|
|
282
315
|
this.listeners.onHandshake(parsedMsg);
|
|
283
316
|
};
|
|
284
|
-
get loggingMetadata() {
|
|
285
|
-
return {
|
|
286
|
-
clientId: this.from,
|
|
287
|
-
connId: this.conn.id
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
317
|
sendHandshake(msg) {
|
|
291
318
|
return this.conn.send(this.options.codec.toBuffer(msg));
|
|
292
319
|
}
|
|
@@ -303,7 +330,7 @@ var SessionWaitingForHandshake = class extends CommonSession {
|
|
|
303
330
|
};
|
|
304
331
|
|
|
305
332
|
// transport/sessionStateMachine/SessionHandshaking.ts
|
|
306
|
-
var SessionHandshaking = class extends
|
|
333
|
+
var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
|
|
307
334
|
state = "Handshaking" /* Handshaking */;
|
|
308
335
|
conn;
|
|
309
336
|
listeners;
|
|
@@ -319,6 +346,12 @@ var SessionHandshaking = class extends IdentifiedSession {
|
|
|
319
346
|
this.conn.addErrorListener(this.listeners.onConnectionErrored);
|
|
320
347
|
this.conn.addCloseListener(this.listeners.onConnectionClosed);
|
|
321
348
|
}
|
|
349
|
+
get loggingMetadata() {
|
|
350
|
+
return {
|
|
351
|
+
...super.loggingMetadata,
|
|
352
|
+
...this.conn.loggingMetadata
|
|
353
|
+
};
|
|
354
|
+
}
|
|
322
355
|
onHandshakeData = (msg) => {
|
|
323
356
|
const parsedMsg = this.parseMsg(msg);
|
|
324
357
|
if (parsedMsg === null) {
|
|
@@ -338,7 +371,10 @@ var SessionHandshaking = class extends IdentifiedSession {
|
|
|
338
371
|
this.conn.removeDataListener(this.onHandshakeData);
|
|
339
372
|
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
340
373
|
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
341
|
-
|
|
374
|
+
if (this.handshakeTimeout) {
|
|
375
|
+
clearTimeout(this.handshakeTimeout);
|
|
376
|
+
this.handshakeTimeout = void 0;
|
|
377
|
+
}
|
|
342
378
|
}
|
|
343
379
|
_handleClose() {
|
|
344
380
|
super._handleClose();
|
|
@@ -403,6 +439,12 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
403
439
|
this.heartbeatMisses++;
|
|
404
440
|
}, this.options.heartbeatIntervalMs);
|
|
405
441
|
}
|
|
442
|
+
get loggingMetadata() {
|
|
443
|
+
return {
|
|
444
|
+
...super.loggingMetadata,
|
|
445
|
+
...this.conn.loggingMetadata
|
|
446
|
+
};
|
|
447
|
+
}
|
|
406
448
|
startActiveHeartbeat() {
|
|
407
449
|
this.isActivelyHeartbeating = true;
|
|
408
450
|
}
|
|
@@ -418,8 +460,10 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
418
460
|
}
|
|
419
461
|
onMessageData = (msg) => {
|
|
420
462
|
const parsedMsg = this.parseMsg(msg);
|
|
421
|
-
if (parsedMsg === null)
|
|
463
|
+
if (parsedMsg === null) {
|
|
464
|
+
this.listeners.onInvalidMessage("could not parse message");
|
|
422
465
|
return;
|
|
466
|
+
}
|
|
423
467
|
if (parsedMsg.seq !== this.ack) {
|
|
424
468
|
if (parsedMsg.seq < this.ack) {
|
|
425
469
|
this.log?.debug(
|
|
@@ -476,7 +520,7 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
476
520
|
};
|
|
477
521
|
|
|
478
522
|
// transport/sessionStateMachine/SessionBackingOff.ts
|
|
479
|
-
var SessionBackingOff = class extends
|
|
523
|
+
var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
|
|
480
524
|
state = "BackingOff" /* BackingOff */;
|
|
481
525
|
listeners;
|
|
482
526
|
backoffTimeout;
|
|
@@ -513,6 +557,12 @@ function inheritSharedSession(session) {
|
|
|
513
557
|
log: session.log
|
|
514
558
|
};
|
|
515
559
|
}
|
|
560
|
+
function inheritSharedSessionWithGrace(session) {
|
|
561
|
+
return {
|
|
562
|
+
...inheritSharedSession(session),
|
|
563
|
+
graceExpiryTime: session.graceExpiryTime
|
|
564
|
+
};
|
|
565
|
+
}
|
|
516
566
|
var SessionStateGraph = {
|
|
517
567
|
entrypoints: {
|
|
518
568
|
NoConnection: (to, from, listeners, options, log) => {
|
|
@@ -526,6 +576,7 @@ var SessionStateGraph = {
|
|
|
526
576
|
to,
|
|
527
577
|
seq: 0,
|
|
528
578
|
ack: 0,
|
|
579
|
+
graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
|
|
529
580
|
sendBuffer,
|
|
530
581
|
telemetry,
|
|
531
582
|
options,
|
|
@@ -557,7 +608,7 @@ var SessionStateGraph = {
|
|
|
557
608
|
transition: {
|
|
558
609
|
// happy path transitions
|
|
559
610
|
NoConnectionToBackingOff: (oldSession, backoffMs, listeners) => {
|
|
560
|
-
const carriedState =
|
|
611
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
561
612
|
oldSession._handleStateExit();
|
|
562
613
|
const session = new SessionBackingOff({
|
|
563
614
|
backoffMs,
|
|
@@ -574,7 +625,7 @@ var SessionStateGraph = {
|
|
|
574
625
|
return session;
|
|
575
626
|
},
|
|
576
627
|
BackingOffToConnecting: (oldSession, connPromise, listeners) => {
|
|
577
|
-
const carriedState =
|
|
628
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
578
629
|
oldSession._handleStateExit();
|
|
579
630
|
const session = new SessionConnecting({
|
|
580
631
|
connPromise,
|
|
@@ -591,7 +642,7 @@ var SessionStateGraph = {
|
|
|
591
642
|
return session;
|
|
592
643
|
},
|
|
593
644
|
ConnectingToHandshaking: (oldSession, conn, listeners) => {
|
|
594
|
-
const carriedState =
|
|
645
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
595
646
|
oldSession._handleStateExit();
|
|
596
647
|
const session = new SessionHandshaking({
|
|
597
648
|
conn,
|
|
@@ -668,9 +719,12 @@ var SessionStateGraph = {
|
|
|
668
719
|
},
|
|
669
720
|
// disconnect paths
|
|
670
721
|
BackingOffToNoConnection: (oldSession, listeners) => {
|
|
671
|
-
const carriedState =
|
|
722
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
672
723
|
oldSession._handleStateExit();
|
|
673
|
-
const session = new SessionNoConnection({
|
|
724
|
+
const session = new SessionNoConnection({
|
|
725
|
+
listeners,
|
|
726
|
+
...carriedState
|
|
727
|
+
});
|
|
674
728
|
session.log?.info(
|
|
675
729
|
`session ${session.id} transition from BackingOff to NoConnection`,
|
|
676
730
|
{
|
|
@@ -681,10 +735,13 @@ var SessionStateGraph = {
|
|
|
681
735
|
return session;
|
|
682
736
|
},
|
|
683
737
|
ConnectingToNoConnection: (oldSession, listeners) => {
|
|
684
|
-
const carriedState =
|
|
738
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
685
739
|
oldSession.bestEffortClose();
|
|
686
740
|
oldSession._handleStateExit();
|
|
687
|
-
const session = new SessionNoConnection({
|
|
741
|
+
const session = new SessionNoConnection({
|
|
742
|
+
listeners,
|
|
743
|
+
...carriedState
|
|
744
|
+
});
|
|
688
745
|
session.log?.info(
|
|
689
746
|
`session ${session.id} transition from Connecting to NoConnection`,
|
|
690
747
|
{
|
|
@@ -695,10 +752,13 @@ var SessionStateGraph = {
|
|
|
695
752
|
return session;
|
|
696
753
|
},
|
|
697
754
|
HandshakingToNoConnection: (oldSession, listeners) => {
|
|
698
|
-
const carriedState =
|
|
755
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
699
756
|
oldSession.conn.close();
|
|
700
757
|
oldSession._handleStateExit();
|
|
701
|
-
const session = new SessionNoConnection({
|
|
758
|
+
const session = new SessionNoConnection({
|
|
759
|
+
listeners,
|
|
760
|
+
...carriedState
|
|
761
|
+
});
|
|
702
762
|
session.log?.info(
|
|
703
763
|
`session ${session.id} transition from Handshaking to NoConnection`,
|
|
704
764
|
{
|
|
@@ -710,9 +770,14 @@ var SessionStateGraph = {
|
|
|
710
770
|
},
|
|
711
771
|
ConnectedToNoConnection: (oldSession, listeners) => {
|
|
712
772
|
const carriedState = inheritSharedSession(oldSession);
|
|
773
|
+
const graceExpiryTime = Date.now() + oldSession.options.sessionDisconnectGraceMs;
|
|
713
774
|
oldSession.conn.close();
|
|
714
775
|
oldSession._handleStateExit();
|
|
715
|
-
const session = new SessionNoConnection({
|
|
776
|
+
const session = new SessionNoConnection({
|
|
777
|
+
listeners,
|
|
778
|
+
graceExpiryTime,
|
|
779
|
+
...carriedState
|
|
780
|
+
});
|
|
716
781
|
session.log?.info(
|
|
717
782
|
`session ${session.id} transition from Connected to NoConnection`,
|
|
718
783
|
{
|
|
@@ -729,24 +794,42 @@ var ClientSessionStateGraph = {
|
|
|
729
794
|
entrypoint: SessionStateGraph.entrypoints.NoConnection,
|
|
730
795
|
transition: {
|
|
731
796
|
// happy paths
|
|
797
|
+
// NoConnection -> BackingOff: attempt to connect
|
|
732
798
|
NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,
|
|
799
|
+
// BackingOff -> Connecting: backoff period elapsed, start connection
|
|
733
800
|
BackingOffToConnecting: transitions.BackingOffToConnecting,
|
|
801
|
+
// Connecting -> Handshaking: connection established, start handshake
|
|
734
802
|
ConnectingToHandshaking: transitions.ConnectingToHandshaking,
|
|
803
|
+
// Handshaking -> Connected: handshake complete, session ready
|
|
735
804
|
HandshakingToConnected: transitions.HandshakingToConnected,
|
|
736
805
|
// disconnect paths
|
|
806
|
+
// BackingOff -> NoConnection: unused
|
|
737
807
|
BackingOffToNoConnection: transitions.BackingOffToNoConnection,
|
|
808
|
+
// Connecting -> NoConnection: connection failed or connection timeout
|
|
738
809
|
ConnectingToNoConnection: transitions.ConnectingToNoConnection,
|
|
810
|
+
// Handshaking -> NoConnection: connection closed or handshake timeout
|
|
739
811
|
HandshakingToNoConnection: transitions.HandshakingToNoConnection,
|
|
812
|
+
// Connected -> NoConnection: connection closed
|
|
740
813
|
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
814
|
+
// destroy/close paths
|
|
815
|
+
// NoConnection -> x: grace period elapsed
|
|
816
|
+
// BackingOff -> x: grace period elapsed
|
|
817
|
+
// Connecting -> x: grace period elapsed
|
|
818
|
+
// Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection
|
|
819
|
+
// Connected -> x: grace period elapsed or invalid message
|
|
741
820
|
}
|
|
742
821
|
};
|
|
743
822
|
var ServerSessionStateGraph = {
|
|
744
823
|
entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,
|
|
745
824
|
transition: {
|
|
746
825
|
// happy paths
|
|
826
|
+
// WaitingForHandshake -> Connected: handshake complete, session ready
|
|
747
827
|
WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,
|
|
748
828
|
// disconnect paths
|
|
829
|
+
// Connected -> NoConnection: connection closed
|
|
749
830
|
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
831
|
+
// destroy/close paths
|
|
832
|
+
// WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed
|
|
750
833
|
}
|
|
751
834
|
};
|
|
752
835
|
|
|
@@ -759,4 +842,4 @@ export {
|
|
|
759
842
|
ClientSessionStateGraph,
|
|
760
843
|
ServerSessionStateGraph
|
|
761
844
|
};
|
|
762
|
-
//# sourceMappingURL=chunk-
|
|
845
|
+
//# sourceMappingURL=chunk-OCL2FUTQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../transport/options.ts","../transport/sessionStateMachine/common.ts","../transport/sessionStateMachine/SessionConnecting.ts","../transport/sessionStateMachine/SessionNoConnection.ts","../transport/sessionStateMachine/SessionWaitingForHandshake.ts","../transport/sessionStateMachine/SessionHandshaking.ts","../transport/sessionStateMachine/SessionConnected.ts","../transport/sessionStateMachine/SessionBackingOff.ts","../transport/sessionStateMachine/transitions.ts"],"sourcesContent":["import { NaiveJsonCodec } from '../codec/json';\nimport { ConnectionRetryOptions } from './rateLimit';\nimport { SessionOptions } from './sessionStateMachine/common';\n\nexport type TransportOptions = SessionOptions;\n\nexport type ProvidedTransportOptions = Partial<TransportOptions>;\n\nexport const defaultTransportOptions: TransportOptions = {\n heartbeatIntervalMs: 1_000,\n heartbeatsUntilDead: 2,\n sessionDisconnectGraceMs: 5_000,\n connectionTimeoutMs: 2_000,\n handshakeTimeoutMs: 1_000,\n enableTransparentSessionReconnects: true,\n codec: NaiveJsonCodec,\n};\n\nexport type ClientTransportOptions = TransportOptions & ConnectionRetryOptions;\n\nexport type ProvidedClientTransportOptions = Partial<ClientTransportOptions>;\n\nconst defaultConnectionRetryOptions: ConnectionRetryOptions = {\n baseIntervalMs: 150,\n maxJitterMs: 200,\n maxBackoffMs: 32_000,\n attemptBudgetCapacity: 5,\n budgetRestoreIntervalMs: 200,\n};\n\nexport const defaultClientTransportOptions: ClientTransportOptions = {\n ...defaultTransportOptions,\n ...defaultConnectionRetryOptions,\n};\n\nexport type ServerTransportOptions = TransportOptions;\n\nexport type ProvidedServerTransportOptions = Partial<ServerTransportOptions>;\n\nexport const defaultServerTransportOptions: ServerTransportOptions = {\n ...defaultTransportOptions,\n};\n","import { Logger, MessageMetadata } from '../../logging';\nimport { TelemetryInfo } from '../../tracing';\nimport {\n OpaqueTransportMessage,\n OpaqueTransportMessageSchema,\n PartialTransportMessage,\n TransportClientId,\n TransportMessage,\n} from '../message';\nimport { Value } from '@sinclair/typebox/value';\nimport { Codec } from '../../codec';\nimport { generateId } from '../id';\n\nexport const enum SessionState {\n NoConnection = 'NoConnection',\n BackingOff = 'BackingOff',\n Connecting = 'Connecting',\n Handshaking = 'Handshaking',\n Connected = 'Connected',\n WaitingForHandshake = 'WaitingForHandshake',\n}\n\nexport const ERR_CONSUMED = `session state has been consumed and is no longer valid`;\n\nabstract class StateMachineState {\n abstract readonly state: SessionState;\n\n /*\n * Whether this state has been consumed\n * and we've moved on to another state\n */\n _isConsumed: boolean;\n\n // called when we're transitioning to another state\n // note that this is internal and should not be called directly\n // by consumers, the proxy will call this when the state is consumed\n // and we're transitioning to another state\n abstract _handleStateExit(): void;\n\n // called when we exit the state machine entirely\n // note that this is internal and should not be called directly\n // by consumers, the proxy will call this when .close is closed\n abstract _handleClose(): void;\n\n close(): void {\n this._handleClose();\n }\n\n constructor() {\n this._isConsumed = false;\n\n // proxy helps us prevent access to properties after the state has been consumed\n // e.g. if we hold a reference to a state and try to access it after it's been consumed\n // we intercept the access and throw an error to help catch bugs\n return new Proxy(this, {\n get(target, prop) {\n // always allow access to _isConsumed, id, and state\n if (prop === '_isConsumed' || prop === 'id' || prop === 'state') {\n return Reflect.get(target, prop);\n }\n\n // modify _handleStateExit\n if (prop === '_handleStateExit') {\n return () => {\n target._isConsumed = true;\n target._handleStateExit();\n };\n }\n\n // modify _handleClose\n if (prop === '_handleClose') {\n return () => {\n // target is the non-proxied object, we need to set _isConsumed again\n target._isConsumed = true;\n target._handleStateExit();\n target._handleClose();\n };\n }\n\n if (target._isConsumed) {\n throw new Error(\n `${ERR_CONSUMED}: getting ${prop.toString()} on consumed state`,\n );\n }\n\n return Reflect.get(target, prop);\n },\n set(target, prop, value) {\n if (target._isConsumed) {\n throw new Error(\n `${ERR_CONSUMED}: setting ${prop.toString()} on consumed state`,\n );\n }\n\n return Reflect.set(target, prop, value);\n },\n });\n }\n}\n\nexport interface SessionOptions {\n /**\n * Frequency at which to send heartbeat acknowledgements\n */\n heartbeatIntervalMs: number;\n /**\n * Number of elapsed heartbeats without a response message before we consider\n * the connection dead.\n */\n heartbeatsUntilDead: number;\n /**\n * Max duration that a session can be without a connection before we consider\n * it dead. This deadline is carried between states and is used to determine\n * when to consider the session a lost cause and delete it entirely.\n * Generally, this should be strictly greater than the sum of\n * {@link connectionTimeoutMs} and {@link handshakeTimeoutMs}.\n */\n sessionDisconnectGraceMs: number;\n /**\n * Connection timeout in milliseconds\n */\n connectionTimeoutMs: number;\n /**\n * Handshake timeout in milliseconds\n */\n handshakeTimeoutMs: number;\n /**\n * Whether to enable transparent session reconnects\n */\n enableTransparentSessionReconnects: boolean;\n /**\n * The codec to use for encoding/decoding messages over the wire\n */\n codec: Codec;\n}\n\n// all session states have a from and options\nexport interface CommonSessionProps {\n from: TransportClientId;\n options: SessionOptions;\n log: Logger | undefined;\n}\n\nexport abstract class CommonSession extends StateMachineState {\n readonly from: TransportClientId;\n readonly options: SessionOptions;\n\n log?: Logger;\n abstract get loggingMetadata(): MessageMetadata;\n\n constructor({ from, options, log }: CommonSessionProps) {\n super();\n this.from = from;\n this.options = options;\n this.log = log;\n }\n\n parseMsg(msg: Uint8Array): OpaqueTransportMessage | null {\n const parsedMsg = this.options.codec.fromBuffer(msg);\n\n if (parsedMsg === null) {\n const decodedBuffer = new TextDecoder().decode(Buffer.from(msg));\n this.log?.error(\n `received malformed msg: ${decodedBuffer}`,\n this.loggingMetadata,\n );\n return null;\n }\n\n if (!Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {\n this.log?.error(`received invalid msg: ${JSON.stringify(parsedMsg)}`, {\n ...this.loggingMetadata,\n validationErrors: [\n ...Value.Errors(OpaqueTransportMessageSchema, parsedMsg),\n ],\n });\n\n return null;\n }\n\n return parsedMsg;\n }\n}\n\nexport type InheritedProperties = Pick<\n IdentifiedSession,\n 'id' | 'from' | 'to' | 'seq' | 'ack' | 'sendBuffer' | 'telemetry' | 'options'\n>;\n\nexport type SessionId = string;\n\n// all sessions where we know the other side's client id\nexport interface IdentifiedSessionProps extends CommonSessionProps {\n id: SessionId;\n to: TransportClientId;\n seq: number;\n ack: number;\n sendBuffer: Array<OpaqueTransportMessage>;\n telemetry: TelemetryInfo;\n}\n\nexport abstract class IdentifiedSession extends CommonSession {\n readonly id: SessionId;\n readonly telemetry: TelemetryInfo;\n readonly to: TransportClientId;\n\n /**\n * Index of the message we will send next (excluding handshake)\n */\n seq: number;\n\n /**\n * Number of unique messages we've received this session (excluding handshake)\n */\n ack: number;\n sendBuffer: Array<OpaqueTransportMessage>;\n\n constructor(props: IdentifiedSessionProps) {\n const { id, to, seq, ack, sendBuffer, telemetry, log } = props;\n super(props);\n this.id = id;\n this.to = to;\n this.seq = seq;\n this.ack = ack;\n this.sendBuffer = sendBuffer;\n this.telemetry = telemetry;\n this.log = log;\n }\n\n get loggingMetadata(): MessageMetadata {\n const spanContext = this.telemetry.span.spanContext();\n\n const metadata: MessageMetadata = {\n clientId: this.from,\n connectedTo: this.to,\n sessionId: this.id,\n };\n\n if (this.telemetry.span.isRecording()) {\n metadata.telemetry = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n };\n }\n\n return metadata;\n }\n\n constructMsg<Payload>(\n partialMsg: PartialTransportMessage<Payload>,\n ): TransportMessage<Payload> {\n const msg = {\n ...partialMsg,\n id: generateId(),\n to: this.to,\n from: this.from,\n seq: this.seq,\n ack: this.ack,\n };\n\n this.seq++;\n return msg;\n }\n\n nextSeq(): number {\n return this.sendBuffer.length > 0 ? this.sendBuffer[0].seq : this.seq;\n }\n\n send(msg: PartialTransportMessage): string {\n const constructedMsg = this.constructMsg(msg);\n this.sendBuffer.push(constructedMsg);\n return constructedMsg.id;\n }\n\n _handleStateExit(): void {\n // noop\n }\n\n _handleClose(): void {\n // zero out the buffer\n this.sendBuffer.length = 0;\n this.telemetry.span.end();\n }\n}\n\nexport interface IdentifiedSessionWithGracePeriodListeners {\n onSessionGracePeriodElapsed: () => void;\n}\n\nexport interface IdentifiedSessionWithGracePeriodProps\n extends IdentifiedSessionProps {\n graceExpiryTime: number;\n listeners: IdentifiedSessionWithGracePeriodListeners;\n}\n\nexport abstract class IdentifiedSessionWithGracePeriod extends IdentifiedSession {\n graceExpiryTime: number;\n protected gracePeriodTimeout?: ReturnType<typeof setTimeout>;\n\n listeners: IdentifiedSessionWithGracePeriodListeners;\n\n constructor(props: IdentifiedSessionWithGracePeriodProps) {\n super(props);\n this.listeners = props.listeners;\n\n this.graceExpiryTime = props.graceExpiryTime;\n this.gracePeriodTimeout = setTimeout(() => {\n this.listeners.onSessionGracePeriodElapsed();\n }, this.graceExpiryTime - Date.now());\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n\n if (this.gracePeriodTimeout) {\n clearTimeout(this.gracePeriodTimeout);\n this.gracePeriodTimeout = undefined;\n }\n }\n\n _handleClose(): void {\n super._handleClose();\n }\n}\n","import { Connection } from '../connection';\nimport {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport interface SessionConnectingListeners\n extends IdentifiedSessionWithGracePeriodListeners {\n onConnectionEstablished: (conn: Connection) => void;\n onConnectionFailed: (err: unknown) => void;\n\n // timeout related\n onConnectionTimeout: () => void;\n}\n\nexport interface SessionConnectingProps<ConnType extends Connection>\n extends IdentifiedSessionWithGracePeriodProps {\n connPromise: Promise<ConnType>;\n listeners: SessionConnectingListeners;\n}\n\n/*\n * A session that is connecting but we don't have access to the raw connection yet.\n * See transitions.ts for valid transitions.\n */\nexport class SessionConnecting<\n ConnType extends Connection,\n> extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.Connecting as const;\n connPromise: Promise<ConnType>;\n listeners: SessionConnectingListeners;\n\n connectionTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionConnectingProps<ConnType>) {\n super(props);\n this.connPromise = props.connPromise;\n this.listeners = props.listeners;\n\n this.connectionTimeout = setTimeout(() => {\n this.listeners.onConnectionTimeout();\n }, this.options.connectionTimeoutMs);\n\n this.connPromise.then(\n (conn) => {\n if (this._isConsumed) return;\n this.listeners.onConnectionEstablished(conn);\n },\n (err) => {\n if (this._isConsumed) return;\n this.listeners.onConnectionFailed(err);\n },\n );\n }\n\n // close a pending connection if it resolves, ignore errors if the promise\n // ends up rejected anyways\n bestEffortClose() {\n void this.connPromise\n .then((conn) => {\n conn.close();\n this.log?.info(\n 'connection eventually resolved but session has transitioned, closed connection',\n {\n ...this.loggingMetadata,\n ...conn.loggingMetadata,\n },\n );\n })\n .catch(() => {\n // ignore errors\n });\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = undefined;\n }\n }\n\n _handleClose(): void {\n // close the pending connection if it resolves\n this.bestEffortClose();\n super._handleClose();\n }\n}\n","import {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport type SessionNoConnectionListeners =\n IdentifiedSessionWithGracePeriodListeners;\n\nexport type SessionNoConnectionProps = IdentifiedSessionWithGracePeriodProps;\n\n/*\n * A session that is not connected and cannot send or receive messages.\n * See transitions.ts for valid transitions.\n */\nexport class SessionNoConnection extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.NoConnection as const;\n\n _handleClose(): void {\n super._handleClose();\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n }\n}\n","import { Static } from '@sinclair/typebox';\nimport { Connection } from '../connection';\nimport {\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n TransportMessage,\n} from '../message';\nimport { CommonSession, CommonSessionProps, SessionState } from './common';\n\nexport interface SessionWaitingForHandshakeListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onHandshake: (msg: OpaqueTransportMessage) => void;\n onInvalidHandshake: (\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n ) => void;\n\n // timeout related\n onHandshakeTimeout: () => void;\n}\n\nexport interface SessionWaitingForHandshakeProps<ConnType extends Connection>\n extends CommonSessionProps {\n conn: ConnType;\n listeners: SessionWaitingForHandshakeListeners;\n}\n\n/*\n * Server-side session that has a connection but is waiting for the client to identify itself.\n * See transitions.ts for valid transitions.\n */\nexport class SessionWaitingForHandshake<\n ConnType extends Connection,\n> extends CommonSession {\n readonly state = SessionState.WaitingForHandshake as const;\n conn: ConnType;\n listeners: SessionWaitingForHandshakeListeners;\n\n handshakeTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionWaitingForHandshakeProps<ConnType>) {\n super(props);\n this.conn = props.conn;\n this.listeners = props.listeners;\n\n this.handshakeTimeout = setTimeout(() => {\n this.listeners.onHandshakeTimeout();\n }, this.options.handshakeTimeoutMs);\n\n this.conn.addDataListener(this.onHandshakeData);\n this.conn.addErrorListener(this.listeners.onConnectionErrored);\n this.conn.addCloseListener(this.listeners.onConnectionClosed);\n }\n\n get loggingMetadata() {\n return {\n clientId: this.from,\n connId: this.conn.id,\n ...this.conn.loggingMetadata,\n };\n }\n\n onHandshakeData = (msg: Uint8Array) => {\n const parsedMsg = this.parseMsg(msg);\n if (parsedMsg === null) {\n this.listeners.onInvalidHandshake(\n 'could not parse message',\n 'MALFORMED_HANDSHAKE',\n );\n return;\n }\n\n // after this fires, the listener is responsible for transitioning the session\n // and thus removing the handshake timeout\n this.listeners.onHandshake(parsedMsg);\n };\n\n sendHandshake(msg: TransportMessage): boolean {\n return this.conn.send(this.options.codec.toBuffer(msg));\n }\n\n _handleStateExit(): void {\n this.conn.removeDataListener(this.onHandshakeData);\n this.conn.removeErrorListener(this.listeners.onConnectionErrored);\n this.conn.removeCloseListener(this.listeners.onConnectionClosed);\n clearTimeout(this.handshakeTimeout);\n this.handshakeTimeout = undefined;\n }\n\n _handleClose(): void {\n this.conn.close();\n }\n}\n","import { Static } from '@sinclair/typebox';\nimport { Connection } from '../connection';\nimport {\n OpaqueTransportMessage,\n TransportMessage,\n HandshakeErrorResponseCodes,\n} from '../message';\nimport {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport interface SessionHandshakingListeners\n extends IdentifiedSessionWithGracePeriodListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onHandshake: (msg: OpaqueTransportMessage) => void;\n onInvalidHandshake: (\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n ) => void;\n\n // timeout related\n onHandshakeTimeout: () => void;\n}\n\nexport interface SessionHandshakingProps<ConnType extends Connection>\n extends IdentifiedSessionWithGracePeriodProps {\n conn: ConnType;\n listeners: SessionHandshakingListeners;\n}\n\n/*\n * A session that is handshaking and waiting for the other side to identify itself.\n * See transitions.ts for valid transitions.\n */\nexport class SessionHandshaking<\n ConnType extends Connection,\n> extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.Handshaking as const;\n conn: ConnType;\n listeners: SessionHandshakingListeners;\n\n handshakeTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionHandshakingProps<ConnType>) {\n super(props);\n this.conn = props.conn;\n this.listeners = props.listeners;\n\n this.handshakeTimeout = setTimeout(() => {\n this.listeners.onHandshakeTimeout();\n }, this.options.handshakeTimeoutMs);\n\n this.conn.addDataListener(this.onHandshakeData);\n this.conn.addErrorListener(this.listeners.onConnectionErrored);\n this.conn.addCloseListener(this.listeners.onConnectionClosed);\n }\n\n get loggingMetadata() {\n return {\n ...super.loggingMetadata,\n ...this.conn.loggingMetadata,\n };\n }\n\n onHandshakeData = (msg: Uint8Array) => {\n const parsedMsg = this.parseMsg(msg);\n if (parsedMsg === null) {\n this.listeners.onInvalidHandshake(\n 'could not parse message',\n 'MALFORMED_HANDSHAKE',\n );\n return;\n }\n\n this.listeners.onHandshake(parsedMsg);\n };\n\n sendHandshake(msg: TransportMessage): boolean {\n return this.conn.send(this.options.codec.toBuffer(msg));\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n this.conn.removeDataListener(this.onHandshakeData);\n this.conn.removeErrorListener(this.listeners.onConnectionErrored);\n this.conn.removeCloseListener(this.listeners.onConnectionClosed);\n\n if (this.handshakeTimeout) {\n clearTimeout(this.handshakeTimeout);\n this.handshakeTimeout = undefined;\n }\n }\n\n _handleClose(): void {\n super._handleClose();\n this.conn.close();\n }\n}\n","import { Static } from '@sinclair/typebox';\nimport {\n ControlFlags,\n ControlMessageAckSchema,\n OpaqueTransportMessage,\n PartialTransportMessage,\n isAck,\n} from '../message';\nimport {\n IdentifiedSession,\n IdentifiedSessionProps,\n SessionState,\n} from './common';\nimport { Connection } from '../connection';\nimport { SpanStatusCode } from '@opentelemetry/api';\n\nexport interface SessionConnectedListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onMessage: (msg: OpaqueTransportMessage) => void;\n onInvalidMessage: (reason: string) => void;\n}\n\nexport interface SessionConnectedProps<ConnType extends Connection>\n extends IdentifiedSessionProps {\n conn: ConnType;\n listeners: SessionConnectedListeners;\n}\n\n/*\n * A session that is connected and can send and receive messages.\n * See transitions.ts for valid transitions.\n */\nexport class SessionConnected<\n ConnType extends Connection,\n> extends IdentifiedSession {\n readonly state = SessionState.Connected as const;\n conn: ConnType;\n listeners: SessionConnectedListeners;\n\n private heartbeatHandle?: ReturnType<typeof setInterval> | undefined;\n private heartbeatMisses = 0;\n isActivelyHeartbeating: boolean;\n\n updateBookkeeping(ack: number, seq: number) {\n this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);\n this.ack = seq + 1;\n this.heartbeatMisses = 0;\n }\n\n send(msg: PartialTransportMessage): string {\n const constructedMsg = this.constructMsg(msg);\n this.sendBuffer.push(constructedMsg);\n this.conn.send(this.options.codec.toBuffer(constructedMsg));\n return constructedMsg.id;\n }\n\n constructor(props: SessionConnectedProps<ConnType>) {\n super(props);\n this.conn = props.conn;\n this.listeners = props.listeners;\n\n this.conn.addDataListener(this.onMessageData);\n this.conn.addCloseListener(this.listeners.onConnectionClosed);\n this.conn.addErrorListener(this.listeners.onConnectionErrored);\n\n // send any buffered messages\n if (this.sendBuffer.length > 0) {\n this.log?.debug(\n `sending ${this.sendBuffer.length} buffered messages`,\n this.loggingMetadata,\n );\n }\n\n for (const msg of this.sendBuffer) {\n this.conn.send(this.options.codec.toBuffer(msg));\n }\n\n // dont explicity clear the buffer, we'll just filter out old messages\n // when we receive an ack\n\n // setup heartbeat\n this.isActivelyHeartbeating = false;\n this.heartbeatHandle = setInterval(() => {\n const misses = this.heartbeatMisses;\n const missDuration = misses * this.options.heartbeatIntervalMs;\n if (misses >= this.options.heartbeatsUntilDead) {\n this.log?.info(\n `closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,\n this.loggingMetadata,\n );\n this.telemetry.span.addEvent('closing connection due to inactivity');\n\n // it is OK to close this even on the client when we can't trust the client timer\n // due to browser throttling or hibernation\n // at worst, this interval will fire later than what the server expects and the server\n // will have already closed the connection\n // this just helps us in cases where we have a proxying setup where the server has closed\n // the connection but the proxy hasn't synchronized the server-side close to the client so\n // the client isn't stuck with a pseudo-dead connection forever\n this.conn.close();\n clearInterval(this.heartbeatHandle);\n this.heartbeatHandle = undefined;\n return;\n }\n\n if (this.isActivelyHeartbeating) {\n this.sendHeartbeat();\n }\n\n this.heartbeatMisses++;\n }, this.options.heartbeatIntervalMs);\n }\n\n get loggingMetadata() {\n return {\n ...super.loggingMetadata,\n ...this.conn.loggingMetadata,\n };\n }\n\n startActiveHeartbeat() {\n this.isActivelyHeartbeating = true;\n }\n\n private sendHeartbeat() {\n this.log?.debug('sending heartbeat', this.loggingMetadata);\n this.send({\n streamId: 'heartbeat',\n controlFlags: ControlFlags.AckBit,\n payload: {\n type: 'ACK',\n } satisfies Static<typeof ControlMessageAckSchema>,\n });\n }\n\n onMessageData = (msg: Uint8Array) => {\n const parsedMsg = this.parseMsg(msg);\n if (parsedMsg === null) {\n this.listeners.onInvalidMessage('could not parse message');\n return;\n }\n\n // check message ordering here\n if (parsedMsg.seq !== this.ack) {\n if (parsedMsg.seq < this.ack) {\n this.log?.debug(\n `received duplicate msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack}), discarding`,\n {\n ...this.loggingMetadata,\n transportMessage: parsedMsg,\n },\n );\n } else {\n const reason = `received out-of-order msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;\n this.log?.error(reason, {\n ...this.loggingMetadata,\n transportMessage: parsedMsg,\n tags: ['invariant-violation'],\n });\n this.telemetry.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: reason,\n });\n\n this.listeners.onInvalidMessage(reason);\n }\n\n return;\n }\n\n // message is ok to update bookkeeping with\n this.log?.debug(`received msg`, {\n ...this.loggingMetadata,\n transportMessage: parsedMsg,\n });\n\n this.updateBookkeeping(parsedMsg.ack, parsedMsg.seq);\n\n // dispatch directly if its not an explicit ack\n if (!isAck(parsedMsg.controlFlags)) {\n this.listeners.onMessage(parsedMsg);\n return;\n }\n\n // discard acks (unless we aren't heartbeating in which case just respond)\n this.log?.debug(`discarding msg (ack bit set)`, {\n ...this.loggingMetadata,\n transportMessage: parsedMsg,\n });\n\n // if we are not actively heartbeating, we are in passive\n // heartbeat mode and should send a response to the ack\n if (!this.isActivelyHeartbeating) {\n this.sendHeartbeat();\n }\n };\n\n _handleStateExit(): void {\n super._handleStateExit();\n this.conn.removeDataListener(this.onMessageData);\n this.conn.removeCloseListener(this.listeners.onConnectionClosed);\n this.conn.removeErrorListener(this.listeners.onConnectionErrored);\n clearInterval(this.heartbeatHandle);\n this.heartbeatHandle = undefined;\n }\n\n _handleClose(): void {\n super._handleClose();\n this.conn.close();\n }\n}\n","import {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport interface SessionBackingOffListeners\n extends IdentifiedSessionWithGracePeriodListeners {\n onBackoffFinished: () => void;\n}\n\nexport interface SessionBackingOffProps\n extends IdentifiedSessionWithGracePeriodProps {\n backoffMs: number;\n listeners: SessionBackingOffListeners;\n}\n\n/*\n * A session that is backing off before attempting to connect.\n * See transitions.ts for valid transitions.\n */\nexport class SessionBackingOff extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.BackingOff as const;\n listeners: SessionBackingOffListeners;\n\n backoffTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionBackingOffProps) {\n super(props);\n this.listeners = props.listeners;\n\n this.backoffTimeout = setTimeout(() => {\n this.listeners.onBackoffFinished();\n }, props.backoffMs);\n }\n\n _handleClose(): void {\n super._handleClose();\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n\n if (this.backoffTimeout) {\n clearTimeout(this.backoffTimeout);\n this.backoffTimeout = undefined;\n }\n }\n}\n","import { OpaqueTransportMessage, TransportClientId } from '..';\nimport {\n SessionConnecting,\n SessionConnectingListeners,\n} from './SessionConnecting';\nimport {\n SessionNoConnection,\n SessionNoConnectionListeners,\n} from './SessionNoConnection';\nimport {\n IdentifiedSession,\n IdentifiedSessionProps,\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodProps,\n SessionOptions,\n} from './common';\nimport { PropagationContext, createSessionTelemetryInfo } from '../../tracing';\nimport {\n SessionWaitingForHandshake,\n SessionWaitingForHandshakeListeners,\n} from './SessionWaitingForHandshake';\nimport {\n SessionHandshaking,\n SessionHandshakingListeners,\n} from './SessionHandshaking';\nimport {\n SessionConnected,\n SessionConnectedListeners,\n} from './SessionConnected';\nimport { generateId } from '../id';\nimport { Connection } from '../connection';\nimport { Logger } from '../../logging';\nimport {\n SessionBackingOff,\n SessionBackingOffListeners,\n} from './SessionBackingOff';\n\nfunction inheritSharedSession(\n session: IdentifiedSession,\n): IdentifiedSessionProps {\n return {\n id: session.id,\n from: session.from,\n to: session.to,\n seq: session.seq,\n ack: session.ack,\n sendBuffer: session.sendBuffer,\n telemetry: session.telemetry,\n options: session.options,\n log: session.log,\n };\n}\n\nfunction inheritSharedSessionWithGrace(\n session: IdentifiedSessionWithGracePeriod,\n): Omit<IdentifiedSessionWithGracePeriodProps, 'listeners'> {\n return {\n ...inheritSharedSession(session),\n graceExpiryTime: session.graceExpiryTime,\n };\n}\n\nexport const SessionStateGraph = {\n entrypoints: {\n NoConnection: (\n to: TransportClientId,\n from: TransportClientId,\n listeners: SessionNoConnectionListeners,\n options: SessionOptions,\n log?: Logger,\n ) => {\n const id = `session-${generateId()}`;\n const telemetry = createSessionTelemetryInfo(id, to, from);\n const sendBuffer: Array<OpaqueTransportMessage> = [];\n\n const session = new SessionNoConnection({\n listeners,\n id,\n from,\n to,\n seq: 0,\n ack: 0,\n graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,\n sendBuffer,\n telemetry,\n options,\n log,\n });\n\n session.log?.info(`session ${session.id} created in NoConnection state`, {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n });\n\n return session;\n },\n WaitingForHandshake: <ConnType extends Connection>(\n from: TransportClientId,\n conn: ConnType,\n listeners: SessionWaitingForHandshakeListeners,\n options: SessionOptions,\n log?: Logger,\n ): SessionWaitingForHandshake<ConnType> => {\n const session = new SessionWaitingForHandshake({\n conn,\n listeners,\n from,\n options,\n log,\n });\n\n session.log?.info(`session created in WaitingForHandshake state`, {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n });\n\n return session;\n },\n },\n // All of the transitions 'move'/'consume' the old session and return a new one.\n // After a session is transitioned, any usage of the old session will throw.\n transition: {\n // happy path transitions\n NoConnectionToBackingOff: (\n oldSession: SessionNoConnection,\n backoffMs: number,\n listeners: SessionBackingOffListeners,\n ): SessionBackingOff => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionBackingOff({\n backoffMs,\n listeners,\n ...carriedState,\n });\n\n session.log?.info(\n `session ${session.id} transition from NoConnection to BackingOff`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n return session;\n },\n BackingOffToConnecting: <ConnType extends Connection>(\n oldSession: SessionBackingOff,\n connPromise: Promise<ConnType>,\n listeners: SessionConnectingListeners,\n ): SessionConnecting<ConnType> => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionConnecting({\n connPromise,\n listeners,\n ...carriedState,\n });\n\n session.log?.info(\n `session ${session.id} transition from BackingOff to Connecting`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n return session;\n },\n ConnectingToHandshaking: <ConnType extends Connection>(\n oldSession: SessionConnecting<ConnType>,\n conn: ConnType,\n listeners: SessionHandshakingListeners,\n ): SessionHandshaking<ConnType> => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionHandshaking({\n conn,\n listeners,\n ...carriedState,\n });\n\n session.log?.info(\n `session ${session.id} transition from Connecting to Handshaking`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n HandshakingToConnected: <ConnType extends Connection>(\n oldSession: SessionHandshaking<ConnType>,\n listeners: SessionConnectedListeners,\n ): SessionConnected<ConnType> => {\n const carriedState = inheritSharedSession(oldSession);\n const conn = oldSession.conn;\n oldSession._handleStateExit();\n\n const session = new SessionConnected({\n conn,\n listeners,\n ...carriedState,\n });\n\n session.log?.info(\n `session ${session.id} transition from Handshaking to Connected`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n WaitingForHandshakeToConnected: <ConnType extends Connection>(\n pendingSession: SessionWaitingForHandshake<ConnType>,\n oldSession: SessionNoConnection | undefined,\n sessionId: string,\n to: TransportClientId,\n propagationCtx: PropagationContext | undefined,\n listeners: SessionConnectedListeners,\n ): SessionConnected<ConnType> => {\n const conn = pendingSession.conn;\n const { from, options } = pendingSession;\n const carriedState: IdentifiedSessionProps = oldSession\n ? // old session exists, inherit state\n inheritSharedSession(oldSession)\n : // old session does not exist, create new state\n {\n id: sessionId,\n from,\n to,\n seq: 0,\n ack: 0,\n sendBuffer: [],\n telemetry: createSessionTelemetryInfo(\n sessionId,\n to,\n from,\n propagationCtx,\n ),\n options,\n log: pendingSession.log,\n };\n\n pendingSession._handleStateExit();\n oldSession?._handleStateExit();\n\n const session = new SessionConnected({\n conn,\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from WaitingForHandshake to Connected`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n // disconnect paths\n BackingOffToNoConnection: (\n oldSession: SessionBackingOff,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from BackingOff to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n ConnectingToNoConnection: <ConnType extends Connection>(\n oldSession: SessionConnecting<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession.bestEffortClose();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from Connecting to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n HandshakingToNoConnection: <ConnType extends Connection>(\n oldSession: SessionHandshaking<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession.conn.close();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from Handshaking to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n ConnectedToNoConnection: <ConnType extends Connection>(\n oldSession: SessionConnected<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSession(oldSession);\n const graceExpiryTime =\n Date.now() + oldSession.options.sessionDisconnectGraceMs;\n oldSession.conn.close();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n graceExpiryTime,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from Connected to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n },\n} as const;\n\nconst transitions = SessionStateGraph.transition;\n\nexport const ClientSessionStateGraph = {\n entrypoint: SessionStateGraph.entrypoints.NoConnection,\n transition: {\n // happy paths\n // NoConnection -> BackingOff: attempt to connect\n NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,\n // BackingOff -> Connecting: backoff period elapsed, start connection\n BackingOffToConnecting: transitions.BackingOffToConnecting,\n // Connecting -> Handshaking: connection established, start handshake\n ConnectingToHandshaking: transitions.ConnectingToHandshaking,\n // Handshaking -> Connected: handshake complete, session ready\n HandshakingToConnected: transitions.HandshakingToConnected,\n\n // disconnect paths\n // BackingOff -> NoConnection: unused\n BackingOffToNoConnection: transitions.BackingOffToNoConnection,\n // Connecting -> NoConnection: connection failed or connection timeout\n ConnectingToNoConnection: transitions.ConnectingToNoConnection,\n // Handshaking -> NoConnection: connection closed or handshake timeout\n HandshakingToNoConnection: transitions.HandshakingToNoConnection,\n // Connected -> NoConnection: connection closed\n ConnectedToNoConnection: transitions.ConnectedToNoConnection,\n\n // destroy/close paths\n // NoConnection -> x: grace period elapsed\n // BackingOff -> x: grace period elapsed\n // Connecting -> x: grace period elapsed\n // Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection\n // Connected -> x: grace period elapsed or invalid message\n },\n};\n\nexport type ClientSession<ConnType extends Connection> =\n | SessionNoConnection\n | SessionBackingOff\n | SessionConnecting<ConnType>\n | SessionHandshaking<ConnType>\n | SessionConnected<ConnType>;\n\nexport const ServerSessionStateGraph = {\n entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,\n transition: {\n // happy paths\n // WaitingForHandshake -> Connected: handshake complete, session ready\n WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,\n\n // disconnect paths\n // Connected -> NoConnection: connection closed\n ConnectedToNoConnection: transitions.ConnectedToNoConnection,\n\n // destroy/close paths\n // WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed\n },\n};\n\nexport type ServerSession<ConnType extends Connection> =\n // SessionWaitingForHandshake<ConnType> is stored separately in the server transport\n SessionConnected<ConnType> | SessionNoConnection;\n\nexport type Session<ConnType extends Connection> =\n | ClientSession<ConnType>\n | ServerSession<ConnType>;\n"],"mappings":";;;;;;;;;;;AAQO,IAAM,0BAA4C;AAAA,EACvD,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,oCAAoC;AAAA,EACpC,OAAO;AACT;AAMA,IAAM,gCAAwD;AAAA,EAC5D,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,cAAc;AAAA,EACd,uBAAuB;AAAA,EACvB,yBAAyB;AAC3B;AAEO,IAAM,gCAAwD;AAAA,EACnE,GAAG;AAAA,EACH,GAAG;AACL;AAMO,IAAM,gCAAwD;AAAA,EACnE,GAAG;AACL;;;AChCA,SAAS,aAAa;AAIf,IAAW,eAAX,kBAAWA,kBAAX;AACL,EAAAA,cAAA,kBAAe;AACf,EAAAA,cAAA,gBAAa;AACb,EAAAA,cAAA,gBAAa;AACb,EAAAA,cAAA,iBAAc;AACd,EAAAA,cAAA,eAAY;AACZ,EAAAA,cAAA,yBAAsB;AANN,SAAAA;AAAA,GAAA;AASX,IAAM,eAAe;AAE5B,IAAe,oBAAf,MAAiC;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B;AAAA,EAaA,QAAc;AACZ,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,cAAc;AACZ,SAAK,cAAc;AAKnB,WAAO,IAAI,MAAM,MAAM;AAAA,MACrB,IAAI,QAAQ,MAAM;AAEhB,YAAI,SAAS,iBAAiB,SAAS,QAAQ,SAAS,SAAS;AAC/D,iBAAO,QAAQ,IAAI,QAAQ,IAAI;AAAA,QACjC;AAGA,YAAI,SAAS,oBAAoB;AAC/B,iBAAO,MAAM;AACX,mBAAO,cAAc;AACrB,mBAAO,iBAAiB;AAAA,UAC1B;AAAA,QACF;AAGA,YAAI,SAAS,gBAAgB;AAC3B,iBAAO,MAAM;AAEX,mBAAO,cAAc;AACrB,mBAAO,iBAAiB;AACxB,mBAAO,aAAa;AAAA,UACtB;AAAA,QACF;AAEA,YAAI,OAAO,aAAa;AACtB,gBAAM,IAAI;AAAA,YACR,GAAG,YAAY,aAAa,KAAK,SAAS,CAAC;AAAA,UAC7C;AAAA,QACF;AAEA,eAAO,QAAQ,IAAI,QAAQ,IAAI;AAAA,MACjC;AAAA,MACA,IAAI,QAAQ,MAAM,OAAO;AACvB,YAAI,OAAO,aAAa;AACtB,gBAAM,IAAI;AAAA,YACR,GAAG,YAAY,aAAa,KAAK,SAAS,CAAC;AAAA,UAC7C;AAAA,QACF;AAEA,eAAO,QAAQ,IAAI,QAAQ,MAAM,KAAK;AAAA,MACxC;AAAA,IACF,CAAC;AAAA,EACH;AACF;AA6CO,IAAe,gBAAf,cAAqC,kBAAkB;AAAA,EACnD;AAAA,EACA;AAAA,EAET;AAAA,EAGA,YAAY,EAAE,MAAM,SAAS,IAAI,GAAuB;AACtD,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,SAAS,KAAgD;AACvD,UAAM,YAAY,KAAK,QAAQ,MAAM,WAAW,GAAG;AAEnD,QAAI,cAAc,MAAM;AACtB,YAAM,gBAAgB,IAAI,YAAY,EAAE,OAAO,OAAO,KAAK,GAAG,CAAC;AAC/D,WAAK,KAAK;AAAA,QACR,2BAA2B,aAAa;AAAA,QACxC,KAAK;AAAA,MACP;AACA,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,MAAM,MAAM,8BAA8B,SAAS,GAAG;AACzD,WAAK,KAAK,MAAM,yBAAyB,KAAK,UAAU,SAAS,CAAC,IAAI;AAAA,QACpE,GAAG,KAAK;AAAA,QACR,kBAAkB;AAAA,UAChB,GAAG,MAAM,OAAO,8BAA8B,SAAS;AAAA,QACzD;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;AAmBO,IAAe,oBAAf,cAAyC,cAAc;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAKT;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA,EACA;AAAA,EAEA,YAAY,OAA+B;AACzC,UAAM,EAAE,IAAI,IAAI,KAAK,KAAK,YAAY,WAAW,IAAI,IAAI;AACzD,UAAM,KAAK;AACX,SAAK,KAAK;AACV,SAAK,KAAK;AACV,SAAK,MAAM;AACX,SAAK,MAAM;AACX,SAAK,aAAa;AAClB,SAAK,YAAY;AACjB,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,IAAI,kBAAmC;AACrC,UAAM,cAAc,KAAK,UAAU,KAAK,YAAY;AAEpD,UAAM,WAA4B;AAAA,MAChC,UAAU,KAAK;AAAA,MACf,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK;AAAA,IAClB;AAEA,QAAI,KAAK,UAAU,KAAK,YAAY,GAAG;AACrC,eAAS,YAAY;AAAA,QACnB,SAAS,YAAY;AAAA,QACrB,QAAQ,YAAY;AAAA,MACtB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aACE,YAC2B;AAC3B,UAAM,MAAM;AAAA,MACV,GAAG;AAAA,MACH,IAAI,WAAW;AAAA,MACf,IAAI,KAAK;AAAA,MACT,MAAM,KAAK;AAAA,MACX,KAAK,KAAK;AAAA,MACV,KAAK,KAAK;AAAA,IACZ;AAEA,SAAK;AACL,WAAO;AAAA,EACT;AAAA,EAEA,UAAkB;AAChB,WAAO,KAAK,WAAW,SAAS,IAAI,KAAK,WAAW,CAAC,EAAE,MAAM,KAAK;AAAA,EACpE;AAAA,EAEA,KAAK,KAAsC;AACzC,UAAM,iBAAiB,KAAK,aAAa,GAAG;AAC5C,SAAK,WAAW,KAAK,cAAc;AACnC,WAAO,eAAe;AAAA,EACxB;AAAA,EAEA,mBAAyB;AAAA,EAEzB;AAAA,EAEA,eAAqB;AAEnB,SAAK,WAAW,SAAS;AACzB,SAAK,UAAU,KAAK,IAAI;AAAA,EAC1B;AACF;AAYO,IAAe,mCAAf,cAAwD,kBAAkB;AAAA,EAC/E;AAAA,EACU;AAAA,EAEV;AAAA,EAEA,YAAY,OAA8C;AACxD,UAAM,KAAK;AACX,SAAK,YAAY,MAAM;AAEvB,SAAK,kBAAkB,MAAM;AAC7B,SAAK,qBAAqB,WAAW,MAAM;AACzC,WAAK,UAAU,4BAA4B;AAAA,IAC7C,GAAG,KAAK,kBAAkB,KAAK,IAAI,CAAC;AAAA,EACtC;AAAA,EAEA,mBAAyB;AACvB,UAAM,iBAAiB;AAEvB,QAAI,KAAK,oBAAoB;AAC3B,mBAAa,KAAK,kBAAkB;AACpC,WAAK,qBAAqB;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,eAAqB;AACnB,UAAM,aAAa;AAAA,EACrB;AACF;;;ACxSO,IAAM,oBAAN,cAEG,iCAAiC;AAAA,EAChC;AAAA,EACT;AAAA,EACA;AAAA,EAEA;AAAA,EAEA,YAAY,OAAyC;AACnD,UAAM,KAAK;AACX,SAAK,cAAc,MAAM;AACzB,SAAK,YAAY,MAAM;AAEvB,SAAK,oBAAoB,WAAW,MAAM;AACxC,WAAK,UAAU,oBAAoB;AAAA,IACrC,GAAG,KAAK,QAAQ,mBAAmB;AAEnC,SAAK,YAAY;AAAA,MACf,CAAC,SAAS;AACR,YAAI,KAAK;AAAa;AACtB,aAAK,UAAU,wBAAwB,IAAI;AAAA,MAC7C;AAAA,MACA,CAAC,QAAQ;AACP,YAAI,KAAK;AAAa;AACtB,aAAK,UAAU,mBAAmB,GAAG;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAIA,kBAAkB;AAChB,SAAK,KAAK,YACP,KAAK,CAAC,SAAS;AACd,WAAK,MAAM;AACX,WAAK,KAAK;AAAA,QACR;AAAA,QACA;AAAA,UACE,GAAG,KAAK;AAAA,UACR,GAAG,KAAK;AAAA,QACV;AAAA,MACF;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AAAA,EACL;AAAA,EAEA,mBAAyB;AACvB,UAAM,iBAAiB;AAEvB,QAAI,KAAK,mBAAmB;AAC1B,mBAAa,KAAK,iBAAiB;AACnC,WAAK,oBAAoB;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,eAAqB;AAEnB,SAAK,gBAAgB;AACrB,UAAM,aAAa;AAAA,EACrB;AACF;;;AC1EO,IAAM,sBAAN,cAAkC,iCAAiC;AAAA,EAC/D;AAAA,EAET,eAAqB;AACnB,UAAM,aAAa;AAAA,EACrB;AAAA,EAEA,mBAAyB;AACvB,UAAM,iBAAiB;AAAA,EACzB;AACF;;;ACMO,IAAM,6BAAN,cAEG,cAAc;AAAA,EACb;AAAA,EACT;AAAA,EACA;AAAA,EAEA;AAAA,EAEA,YAAY,OAAkD;AAC5D,UAAM,KAAK;AACX,SAAK,OAAO,MAAM;AAClB,SAAK,YAAY,MAAM;AAEvB,SAAK,mBAAmB,WAAW,MAAM;AACvC,WAAK,UAAU,mBAAmB;AAAA,IACpC,GAAG,KAAK,QAAQ,kBAAkB;AAElC,SAAK,KAAK,gBAAgB,KAAK,eAAe;AAC9C,SAAK,KAAK,iBAAiB,KAAK,UAAU,mBAAmB;AAC7D,SAAK,KAAK,iBAAiB,KAAK,UAAU,kBAAkB;AAAA,EAC9D;AAAA,EAEA,IAAI,kBAAkB;AACpB,WAAO;AAAA,MACL,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK,KAAK;AAAA,MAClB,GAAG,KAAK,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEA,kBAAkB,CAAC,QAAoB;AACrC,UAAM,YAAY,KAAK,SAAS,GAAG;AACnC,QAAI,cAAc,MAAM;AACtB,WAAK,UAAU;AAAA,QACb;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAIA,SAAK,UAAU,YAAY,SAAS;AAAA,EACtC;AAAA,EAEA,cAAc,KAAgC;AAC5C,WAAO,KAAK,KAAK,KAAK,KAAK,QAAQ,MAAM,SAAS,GAAG,CAAC;AAAA,EACxD;AAAA,EAEA,mBAAyB;AACvB,SAAK,KAAK,mBAAmB,KAAK,eAAe;AACjD,SAAK,KAAK,oBAAoB,KAAK,UAAU,mBAAmB;AAChE,SAAK,KAAK,oBAAoB,KAAK,UAAU,kBAAkB;AAC/D,iBAAa,KAAK,gBAAgB;AAClC,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,eAAqB;AACnB,SAAK,KAAK,MAAM;AAAA,EAClB;AACF;;;ACvDO,IAAM,qBAAN,cAEG,iCAAiC;AAAA,EAChC;AAAA,EACT;AAAA,EACA;AAAA,EAEA;AAAA,EAEA,YAAY,OAA0C;AACpD,UAAM,KAAK;AACX,SAAK,OAAO,MAAM;AAClB,SAAK,YAAY,MAAM;AAEvB,SAAK,mBAAmB,WAAW,MAAM;AACvC,WAAK,UAAU,mBAAmB;AAAA,IACpC,GAAG,KAAK,QAAQ,kBAAkB;AAElC,SAAK,KAAK,gBAAgB,KAAK,eAAe;AAC9C,SAAK,KAAK,iBAAiB,KAAK,UAAU,mBAAmB;AAC7D,SAAK,KAAK,iBAAiB,KAAK,UAAU,kBAAkB;AAAA,EAC9D;AAAA,EAEA,IAAI,kBAAkB;AACpB,WAAO;AAAA,MACL,GAAG,MAAM;AAAA,MACT,GAAG,KAAK,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEA,kBAAkB,CAAC,QAAoB;AACrC,UAAM,YAAY,KAAK,SAAS,GAAG;AACnC,QAAI,cAAc,MAAM;AACtB,WAAK,UAAU;AAAA,QACb;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF;AAEA,SAAK,UAAU,YAAY,SAAS;AAAA,EACtC;AAAA,EAEA,cAAc,KAAgC;AAC5C,WAAO,KAAK,KAAK,KAAK,KAAK,QAAQ,MAAM,SAAS,GAAG,CAAC;AAAA,EACxD;AAAA,EAEA,mBAAyB;AACvB,UAAM,iBAAiB;AACvB,SAAK,KAAK,mBAAmB,KAAK,eAAe;AACjD,SAAK,KAAK,oBAAoB,KAAK,UAAU,mBAAmB;AAChE,SAAK,KAAK,oBAAoB,KAAK,UAAU,kBAAkB;AAE/D,QAAI,KAAK,kBAAkB;AACzB,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEA,eAAqB;AACnB,UAAM,aAAa;AACnB,SAAK,KAAK,MAAM;AAAA,EAClB;AACF;;;ACvFA,SAAS,sBAAsB;AAmBxB,IAAM,mBAAN,cAEG,kBAAkB;AAAA,EACjB;AAAA,EACT;AAAA,EACA;AAAA,EAEQ;AAAA,EACA,kBAAkB;AAAA,EAC1B;AAAA,EAEA,kBAAkB,KAAa,KAAa;AAC1C,SAAK,aAAa,KAAK,WAAW,OAAO,CAAC,YAAY,QAAQ,OAAO,GAAG;AACxE,SAAK,MAAM,MAAM;AACjB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,KAAK,KAAsC;AACzC,UAAM,iBAAiB,KAAK,aAAa,GAAG;AAC5C,SAAK,WAAW,KAAK,cAAc;AACnC,SAAK,KAAK,KAAK,KAAK,QAAQ,MAAM,SAAS,cAAc,CAAC;AAC1D,WAAO,eAAe;AAAA,EACxB;AAAA,EAEA,YAAY,OAAwC;AAClD,UAAM,KAAK;AACX,SAAK,OAAO,MAAM;AAClB,SAAK,YAAY,MAAM;AAEvB,SAAK,KAAK,gBAAgB,KAAK,aAAa;AAC5C,SAAK,KAAK,iBAAiB,KAAK,UAAU,kBAAkB;AAC5D,SAAK,KAAK,iBAAiB,KAAK,UAAU,mBAAmB;AAG7D,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,WAAK,KAAK;AAAA,QACR,WAAW,KAAK,WAAW,MAAM;AAAA,QACjC,KAAK;AAAA,MACP;AAAA,IACF;AAEA,eAAW,OAAO,KAAK,YAAY;AACjC,WAAK,KAAK,KAAK,KAAK,QAAQ,MAAM,SAAS,GAAG,CAAC;AAAA,IACjD;AAMA,SAAK,yBAAyB;AAC9B,SAAK,kBAAkB,YAAY,MAAM;AACvC,YAAM,SAAS,KAAK;AACpB,YAAM,eAAe,SAAS,KAAK,QAAQ;AAC3C,UAAI,UAAU,KAAK,QAAQ,qBAAqB;AAC9C,aAAK,KAAK;AAAA,UACR,yBAAyB,KAAK,EAAE,8BAA8B,MAAM,wBAAwB,YAAY;AAAA,UACxG,KAAK;AAAA,QACP;AACA,aAAK,UAAU,KAAK,SAAS,sCAAsC;AASnE,aAAK,KAAK,MAAM;AAChB,sBAAc,KAAK,eAAe;AAClC,aAAK,kBAAkB;AACvB;AAAA,MACF;AAEA,UAAI,KAAK,wBAAwB;AAC/B,aAAK,cAAc;AAAA,MACrB;AAEA,WAAK;AAAA,IACP,GAAG,KAAK,QAAQ,mBAAmB;AAAA,EACrC;AAAA,EAEA,IAAI,kBAAkB;AACpB,WAAO;AAAA,MACL,GAAG,MAAM;AAAA,MACT,GAAG,KAAK,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,SAAK,yBAAyB;AAAA,EAChC;AAAA,EAEQ,gBAAgB;AACtB,SAAK,KAAK,MAAM,qBAAqB,KAAK,eAAe;AACzD,SAAK,KAAK;AAAA,MACR,UAAU;AAAA,MACV;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,gBAAgB,CAAC,QAAoB;AACnC,UAAM,YAAY,KAAK,SAAS,GAAG;AACnC,QAAI,cAAc,MAAM;AACtB,WAAK,UAAU,iBAAiB,yBAAyB;AACzD;AAAA,IACF;AAGA,QAAI,UAAU,QAAQ,KAAK,KAAK;AAC9B,UAAI,UAAU,MAAM,KAAK,KAAK;AAC5B,aAAK,KAAK;AAAA,UACR,oCAAoC,UAAU,GAAG,iBAAiB,KAAK,GAAG;AAAA,UAC1E;AAAA,YACE,GAAG,KAAK;AAAA,YACR,kBAAkB;AAAA,UACpB;AAAA,QACF;AAAA,MACF,OAAO;AACL,cAAM,SAAS,uCAAuC,UAAU,GAAG,iBAAiB,KAAK,GAAG;AAC5F,aAAK,KAAK,MAAM,QAAQ;AAAA,UACtB,GAAG,KAAK;AAAA,UACR,kBAAkB;AAAA,UAClB,MAAM,CAAC,qBAAqB;AAAA,QAC9B,CAAC;AACD,aAAK,UAAU,KAAK,UAAU;AAAA,UAC5B,MAAM,eAAe;AAAA,UACrB,SAAS;AAAA,QACX,CAAC;AAED,aAAK,UAAU,iBAAiB,MAAM;AAAA,MACxC;AAEA;AAAA,IACF;AAGA,SAAK,KAAK,MAAM,gBAAgB;AAAA,MAC9B,GAAG,KAAK;AAAA,MACR,kBAAkB;AAAA,IACpB,CAAC;AAED,SAAK,kBAAkB,UAAU,KAAK,UAAU,GAAG;AAGnD,QAAI,CAAC,MAAM,UAAU,YAAY,GAAG;AAClC,WAAK,UAAU,UAAU,SAAS;AAClC;AAAA,IACF;AAGA,SAAK,KAAK,MAAM,gCAAgC;AAAA,MAC9C,GAAG,KAAK;AAAA,MACR,kBAAkB;AAAA,IACpB,CAAC;AAID,QAAI,CAAC,KAAK,wBAAwB;AAChC,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,mBAAyB;AACvB,UAAM,iBAAiB;AACvB,SAAK,KAAK,mBAAmB,KAAK,aAAa;AAC/C,SAAK,KAAK,oBAAoB,KAAK,UAAU,kBAAkB;AAC/D,SAAK,KAAK,oBAAoB,KAAK,UAAU,mBAAmB;AAChE,kBAAc,KAAK,eAAe;AAClC,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEA,eAAqB;AACnB,UAAM,aAAa;AACnB,SAAK,KAAK,MAAM;AAAA,EAClB;AACF;;;AC7LO,IAAM,oBAAN,cAAgC,iCAAiC;AAAA,EAC7D;AAAA,EACT;AAAA,EAEA;AAAA,EAEA,YAAY,OAA+B;AACzC,UAAM,KAAK;AACX,SAAK,YAAY,MAAM;AAEvB,SAAK,iBAAiB,WAAW,MAAM;AACrC,WAAK,UAAU,kBAAkB;AAAA,IACnC,GAAG,MAAM,SAAS;AAAA,EACpB;AAAA,EAEA,eAAqB;AACnB,UAAM,aAAa;AAAA,EACrB;AAAA,EAEA,mBAAyB;AACvB,UAAM,iBAAiB;AAEvB,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AACF;;;ACZA,SAAS,qBACP,SACwB;AACxB,SAAO;AAAA,IACL,IAAI,QAAQ;AAAA,IACZ,MAAM,QAAQ;AAAA,IACd,IAAI,QAAQ;AAAA,IACZ,KAAK,QAAQ;AAAA,IACb,KAAK,QAAQ;AAAA,IACb,YAAY,QAAQ;AAAA,IACpB,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,IACjB,KAAK,QAAQ;AAAA,EACf;AACF;AAEA,SAAS,8BACP,SAC0D;AAC1D,SAAO;AAAA,IACL,GAAG,qBAAqB,OAAO;AAAA,IAC/B,iBAAiB,QAAQ;AAAA,EAC3B;AACF;AAEO,IAAM,oBAAoB;AAAA,EAC/B,aAAa;AAAA,IACX,cAAc,CACZ,IACA,MACA,WACA,SACA,QACG;AACH,YAAM,KAAK,WAAW,WAAW,CAAC;AAClC,YAAM,YAAY,2BAA2B,IAAI,IAAI,IAAI;AACzD,YAAM,aAA4C,CAAC;AAEnD,YAAM,UAAU,IAAI,oBAAoB;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,QACL,iBAAiB,KAAK,IAAI,IAAI,QAAQ;AAAA,QACtC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,cAAQ,KAAK,KAAK,WAAW,QAAQ,EAAE,kCAAkC;AAAA,QACvE,GAAG,QAAQ;AAAA,QACX,MAAM,CAAC,kBAAkB;AAAA,MAC3B,CAAC;AAED,aAAO;AAAA,IACT;AAAA,IACA,qBAAqB,CACnB,MACA,MACA,WACA,SACA,QACyC;AACzC,YAAM,UAAU,IAAI,2BAA2B;AAAA,QAC7C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,cAAQ,KAAK,KAAK,gDAAgD;AAAA,QAChE,GAAG,QAAQ;AAAA,QACX,MAAM,CAAC,kBAAkB;AAAA,MAC3B,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,YAAY;AAAA;AAAA,IAEV,0BAA0B,CACxB,YACA,WACA,cACsB;AACtB,YAAM,eAAe,8BAA8B,UAAU;AAC7D,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,kBAAkB;AAAA,QACpC;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AAED,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IACA,wBAAwB,CACtB,YACA,aACA,cACgC;AAChC,YAAM,eAAe,8BAA8B,UAAU;AAC7D,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,kBAAkB;AAAA,QACpC;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AAED,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IACA,yBAAyB,CACvB,YACA,MACA,cACiC;AACjC,YAAM,eAAe,8BAA8B,UAAU;AAC7D,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,mBAAmB;AAAA,QACrC;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AAED,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,wBAAwB,CACtB,YACA,cAC+B;AAC/B,YAAM,eAAe,qBAAqB,UAAU;AACpD,YAAM,OAAO,WAAW;AACxB,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,iBAAiB;AAAA,QACnC;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AAED,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,gCAAgC,CAC9B,gBACA,YACA,WACA,IACA,gBACA,cAC+B;AAC/B,YAAM,OAAO,eAAe;AAC5B,YAAM,EAAE,MAAM,QAAQ,IAAI;AAC1B,YAAM,eAAuC;AAAA;AAAA,QAEzC,qBAAqB,UAAU;AAAA;AAAA;AAAA,QAE/B;AAAA,UACE,IAAI;AAAA,UACJ;AAAA,UACA;AAAA,UACA,KAAK;AAAA,UACL,KAAK;AAAA,UACL,YAAY,CAAC;AAAA,UACb,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA;AAAA,UACA,KAAK,eAAe;AAAA,QACtB;AAAA;AAEJ,qBAAe,iBAAiB;AAChC,kBAAY,iBAAiB;AAE7B,YAAM,UAAU,IAAI,iBAAiB;AAAA,QACnC;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA;AAAA,IAEA,0BAA0B,CACxB,YACA,cACwB;AACxB,YAAM,eAAe,8BAA8B,UAAU;AAC7D,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,oBAAoB;AAAA,QACtC;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,0BAA0B,CACxB,YACA,cACwB;AACxB,YAAM,eAAe,8BAA8B,UAAU;AAC7D,iBAAW,gBAAgB;AAC3B,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,oBAAoB;AAAA,QACtC;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,2BAA2B,CACzB,YACA,cACwB;AACxB,YAAM,eAAe,8BAA8B,UAAU;AAC7D,iBAAW,KAAK,MAAM;AACtB,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,oBAAoB;AAAA,QACtC;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IACA,yBAAyB,CACvB,YACA,cACwB;AACxB,YAAM,eAAe,qBAAqB,UAAU;AACpD,YAAM,kBACJ,KAAK,IAAI,IAAI,WAAW,QAAQ;AAClC,iBAAW,KAAK,MAAM;AACtB,iBAAW,iBAAiB;AAE5B,YAAM,UAAU,IAAI,oBAAoB;AAAA,QACtC;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL,CAAC;AACD,cAAQ,KAAK;AAAA,QACX,WAAW,QAAQ,EAAE;AAAA,QACrB;AAAA,UACE,GAAG,QAAQ;AAAA,UACX,MAAM,CAAC,kBAAkB;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAM,cAAc,kBAAkB;AAE/B,IAAM,0BAA0B;AAAA,EACrC,YAAY,kBAAkB,YAAY;AAAA,EAC1C,YAAY;AAAA;AAAA;AAAA,IAGV,0BAA0B,YAAY;AAAA;AAAA,IAEtC,wBAAwB,YAAY;AAAA;AAAA,IAEpC,yBAAyB,YAAY;AAAA;AAAA,IAErC,wBAAwB,YAAY;AAAA;AAAA;AAAA,IAIpC,0BAA0B,YAAY;AAAA;AAAA,IAEtC,0BAA0B,YAAY;AAAA;AAAA,IAEtC,2BAA2B,YAAY;AAAA;AAAA,IAEvC,yBAAyB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvC;AACF;AASO,IAAM,0BAA0B;AAAA,EACrC,YAAY,kBAAkB,YAAY;AAAA,EAC1C,YAAY;AAAA;AAAA;AAAA,IAGV,gCAAgC,YAAY;AAAA;AAAA;AAAA,IAI5C,yBAAyB,YAAY;AAAA;AAAA;AAAA,EAIvC;AACF;","names":["SessionState"]}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProtocolError,
|
|
3
3
|
Transport
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JI6FFDY5.js";
|
|
5
5
|
import {
|
|
6
6
|
ServerSessionStateGraph,
|
|
7
7
|
defaultServerTransportOptions
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-OCL2FUTQ.js";
|
|
9
9
|
import {
|
|
10
10
|
ControlMessageHandshakeRequestSchema,
|
|
11
11
|
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
12
12
|
PROTOCOL_VERSION,
|
|
13
13
|
coerceErrorString,
|
|
14
14
|
handshakeResponseMessage
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-MZELCWJK.js";
|
|
16
16
|
|
|
17
17
|
// transport/server.ts
|
|
18
18
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
@@ -203,19 +203,58 @@ var ServerTransport = class extends Transport {
|
|
|
203
203
|
return;
|
|
204
204
|
}
|
|
205
205
|
let oldSession = this.sessions.get(msg.from);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
let parsedMetadata = {};
|
|
207
|
+
if (this.handshakeExtensions) {
|
|
208
|
+
if (!Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
|
|
209
|
+
this.rejectHandshakeRequest(
|
|
210
|
+
session,
|
|
211
|
+
msg.from,
|
|
212
|
+
"received malformed handshake metadata",
|
|
213
|
+
"MALFORMED_HANDSHAKE_META",
|
|
214
|
+
{
|
|
215
|
+
...session.loggingMetadata,
|
|
216
|
+
connectedTo: msg.from,
|
|
217
|
+
validationErrors: [
|
|
218
|
+
...Value.Errors(
|
|
219
|
+
this.handshakeExtensions.schema,
|
|
220
|
+
msg.payload.metadata
|
|
221
|
+
)
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const previousParsedMetadata = oldSession ? this.sessionHandshakeMetadata.get(oldSession.to) : void 0;
|
|
228
|
+
const parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(
|
|
229
|
+
msg.payload.metadata,
|
|
230
|
+
previousParsedMetadata
|
|
231
|
+
);
|
|
232
|
+
if (session._isConsumed) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (Value.Check(
|
|
236
|
+
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
237
|
+
parsedMetadataOrFailureCode
|
|
238
|
+
)) {
|
|
239
|
+
this.rejectHandshakeRequest(
|
|
240
|
+
session,
|
|
241
|
+
msg.from,
|
|
242
|
+
"rejected by handshake handler",
|
|
243
|
+
parsedMetadataOrFailureCode,
|
|
244
|
+
{
|
|
245
|
+
...session.loggingMetadata,
|
|
246
|
+
connectedTo: msg.from,
|
|
247
|
+
clientId: this.clientId
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
parsedMetadata = parsedMetadataOrFailureCode;
|
|
214
253
|
}
|
|
215
254
|
let connectCase = "new session";
|
|
216
255
|
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
217
256
|
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
|
|
218
|
-
if (oldSession && oldSession.id === msg.payload.sessionId) {
|
|
257
|
+
if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
|
|
219
258
|
connectCase = "transparent reconnection";
|
|
220
259
|
const ourNextSeq = oldSession.nextSeq();
|
|
221
260
|
const ourAck = oldSession.ack;
|
|
@@ -274,10 +313,11 @@ var ServerTransport = class extends Transport {
|
|
|
274
313
|
}
|
|
275
314
|
if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {
|
|
276
315
|
connectCase = "unknown session";
|
|
316
|
+
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}`;
|
|
277
317
|
this.rejectHandshakeRequest(
|
|
278
318
|
session,
|
|
279
319
|
msg.from,
|
|
280
|
-
|
|
320
|
+
rejectionMessage,
|
|
281
321
|
"SESSION_STATE_MISMATCH",
|
|
282
322
|
{
|
|
283
323
|
...session.loggingMetadata,
|
|
@@ -341,50 +381,9 @@ var ServerTransport = class extends Transport {
|
|
|
341
381
|
this.pendingSessions.delete(session);
|
|
342
382
|
connectedSession.startActiveHeartbeat();
|
|
343
383
|
}
|
|
344
|
-
async validateHandshakeMetadata(handshakingSession, existingSession, rawMetadata, from) {
|
|
345
|
-
if (!this.handshakeExtensions) {
|
|
346
|
-
return {};
|
|
347
|
-
}
|
|
348
|
-
if (!Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
|
|
349
|
-
this.rejectHandshakeRequest(
|
|
350
|
-
handshakingSession,
|
|
351
|
-
from,
|
|
352
|
-
"received malformed handshake metadata",
|
|
353
|
-
"MALFORMED_HANDSHAKE_META",
|
|
354
|
-
{
|
|
355
|
-
...handshakingSession.loggingMetadata,
|
|
356
|
-
connectedTo: from,
|
|
357
|
-
validationErrors: [
|
|
358
|
-
...Value.Errors(this.handshakeExtensions.schema, rawMetadata)
|
|
359
|
-
]
|
|
360
|
-
}
|
|
361
|
-
);
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
const previousParsedMetadata = existingSession ? this.sessionHandshakeMetadata.get(existingSession.to) : void 0;
|
|
365
|
-
const parsedMetadata = await this.handshakeExtensions.validate(
|
|
366
|
-
rawMetadata,
|
|
367
|
-
previousParsedMetadata
|
|
368
|
-
);
|
|
369
|
-
if (Value.Check(HandshakeErrorCustomHandlerFatalResponseCodes, parsedMetadata)) {
|
|
370
|
-
this.rejectHandshakeRequest(
|
|
371
|
-
handshakingSession,
|
|
372
|
-
from,
|
|
373
|
-
"rejected by handshake handler",
|
|
374
|
-
parsedMetadata,
|
|
375
|
-
{
|
|
376
|
-
...handshakingSession.loggingMetadata,
|
|
377
|
-
connectedTo: from,
|
|
378
|
-
clientId: this.clientId
|
|
379
|
-
}
|
|
380
|
-
);
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
return parsedMetadata;
|
|
384
|
-
}
|
|
385
384
|
};
|
|
386
385
|
|
|
387
386
|
export {
|
|
388
387
|
ServerTransport
|
|
389
388
|
};
|
|
390
|
-
//# sourceMappingURL=chunk-
|
|
389
|
+
//# sourceMappingURL=chunk-X35QRIA5.js.map
|