@replit/river 0.200.0-rc.7 → 0.200.0-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/{chunk-BZBEE2VR.js → chunk-3ROTSXAO.js} +14 -4
  2. package/dist/chunk-3ROTSXAO.js.map +1 -0
  3. package/dist/{chunk-VLW5OKZG.js → chunk-B5VE44UX.js} +2 -2
  4. package/dist/{chunk-CXPZSAU4.js → chunk-FAIV2RO2.js} +62 -36
  5. package/dist/chunk-FAIV2RO2.js.map +1 -0
  6. package/dist/{chunk-U4W75CMT.js → chunk-J54ZWTQM.js} +7 -6
  7. package/dist/chunk-J54ZWTQM.js.map +1 -0
  8. package/dist/{chunk-F2E7ILHW.js → chunk-XV5WUEIR.js} +70 -59
  9. package/dist/chunk-XV5WUEIR.js.map +1 -0
  10. package/dist/{chunk-3XKJOFZA.js → chunk-Y3JHOIJ7.js} +141 -50
  11. package/dist/chunk-Y3JHOIJ7.js.map +1 -0
  12. package/dist/{chunk-XIJVDPYY.js → chunk-Y4DP7WHM.js} +2 -2
  13. package/dist/{chunk-XIJVDPYY.js.map → chunk-Y4DP7WHM.js.map} +1 -1
  14. package/dist/{client-829bf1f9.d.ts → client-22a47343.d.ts} +3 -5
  15. package/dist/{connection-5e67d641.d.ts → connection-260e45a8.d.ts} +1 -1
  16. package/dist/{context-9eabf54f.d.ts → context-b4aff18f.d.ts} +90 -43
  17. package/dist/logging/index.d.cts +1 -1
  18. package/dist/logging/index.d.ts +1 -1
  19. package/dist/{message-fd349b27.d.ts → message-7d135e38.d.ts} +3 -1
  20. package/dist/router/index.cjs +12 -3
  21. package/dist/router/index.cjs.map +1 -1
  22. package/dist/router/index.d.cts +9 -8
  23. package/dist/router/index.d.ts +9 -8
  24. package/dist/router/index.js +2 -2
  25. package/dist/{server-d82a2d9b.d.ts → server-dd6a9853.d.ts} +27 -6
  26. package/dist/{services-6a446f04.d.ts → services-bd2c50c0.d.ts} +3 -3
  27. package/dist/transport/impls/ws/client.cjs +216 -88
  28. package/dist/transport/impls/ws/client.cjs.map +1 -1
  29. package/dist/transport/impls/ws/client.d.cts +5 -4
  30. package/dist/transport/impls/ws/client.d.ts +5 -4
  31. package/dist/transport/impls/ws/client.js +8 -7
  32. package/dist/transport/impls/ws/client.js.map +1 -1
  33. package/dist/transport/impls/ws/server.cjs +220 -109
  34. package/dist/transport/impls/ws/server.cjs.map +1 -1
  35. package/dist/transport/impls/ws/server.d.cts +5 -4
  36. package/dist/transport/impls/ws/server.d.ts +5 -4
  37. package/dist/transport/impls/ws/server.js +5 -5
  38. package/dist/transport/index.cjs +278 -141
  39. package/dist/transport/index.cjs.map +1 -1
  40. package/dist/transport/index.d.cts +5 -4
  41. package/dist/transport/index.d.ts +5 -4
  42. package/dist/transport/index.js +5 -5
  43. package/dist/util/testHelpers.cjs +151 -51
  44. package/dist/util/testHelpers.cjs.map +1 -1
  45. package/dist/util/testHelpers.d.cts +5 -4
  46. package/dist/util/testHelpers.d.ts +5 -4
  47. package/dist/util/testHelpers.js +3 -3
  48. package/package.json +1 -1
  49. package/dist/chunk-3XKJOFZA.js.map +0 -1
  50. package/dist/chunk-BZBEE2VR.js.map +0 -1
  51. package/dist/chunk-CXPZSAU4.js.map +0 -1
  52. package/dist/chunk-F2E7ILHW.js.map +0 -1
  53. package/dist/chunk-U4W75CMT.js.map +0 -1
  54. /package/dist/{chunk-VLW5OKZG.js.map → chunk-B5VE44UX.js.map} +0 -0
@@ -1,18 +1,19 @@
1
1
  import {
2
2
  ProtocolError,
3
3
  Transport
4
- } from "./chunk-U4W75CMT.js";
4
+ } from "./chunk-J54ZWTQM.js";
5
5
  import {
6
6
  ServerSessionStateGraph,
7
7
  defaultServerTransportOptions
8
- } from "./chunk-3XKJOFZA.js";
8
+ } from "./chunk-Y3JHOIJ7.js";
9
9
  import {
10
10
  ControlMessageHandshakeRequestSchema,
11
+ HandshakeErrorCustomHandlerFatalResponseCodes,
11
12
  acceptedProtocolVersions,
12
13
  coerceErrorString,
13
14
  currentProtocolVersion,
14
15
  handshakeResponseMessage
15
- } from "./chunk-BZBEE2VR.js";
16
+ } from "./chunk-3ROTSXAO.js";
16
17
 
17
18
  // transport/server.ts
18
19
  import { SpanStatusCode } from "@opentelemetry/api";
@@ -127,13 +128,17 @@ var ServerTransport = class extends Transport {
127
128
  receivedHandshake = true;
128
129
  void this.onHandshakeRequest(pendingSession, msg);
129
130
  },
130
- onInvalidHandshake: (reason) => {
131
+ onInvalidHandshake: (reason, code) => {
131
132
  this.log?.error(
132
133
  `invalid handshake: ${reason}`,
133
134
  pendingSession.loggingMetadata
134
135
  );
135
136
  this.deletePendingSession(pendingSession);
136
- this.protocolError(ProtocolError.HandshakeFailed, reason);
137
+ this.protocolError({
138
+ type: ProtocolError.HandshakeFailed,
139
+ code,
140
+ message: reason
141
+ });
137
142
  }
138
143
  },
139
144
  this.options,
@@ -158,7 +163,11 @@ var ServerTransport = class extends Transport {
158
163
  }
159
164
  })
160
165
  );
161
- this.protocolError(ProtocolError.HandshakeFailed, reason);
166
+ this.protocolError({
167
+ type: ProtocolError.HandshakeFailed,
168
+ code,
169
+ message: reason
170
+ });
162
171
  this.deletePendingSession(session);
163
172
  }
164
173
  async onHandshakeRequest(session, msg) {
@@ -195,19 +204,58 @@ var ServerTransport = class extends Transport {
195
204
  return;
196
205
  }
197
206
  let oldSession = this.sessions.get(msg.from);
198
- const parsedMetadata = await this.validateHandshakeMetadata(
199
- session,
200
- oldSession,
201
- msg.payload.metadata,
202
- msg.from
203
- );
204
- if (parsedMetadata === false) {
205
- return;
207
+ let parsedMetadata = {};
208
+ if (this.handshakeExtensions) {
209
+ if (!Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
210
+ this.rejectHandshakeRequest(
211
+ session,
212
+ msg.from,
213
+ "received malformed handshake metadata",
214
+ "MALFORMED_HANDSHAKE_META",
215
+ {
216
+ ...session.loggingMetadata,
217
+ connectedTo: msg.from,
218
+ validationErrors: [
219
+ ...Value.Errors(
220
+ this.handshakeExtensions.schema,
221
+ msg.payload.metadata
222
+ )
223
+ ]
224
+ }
225
+ );
226
+ return;
227
+ }
228
+ const previousParsedMetadata = oldSession ? this.sessionHandshakeMetadata.get(oldSession.to) : void 0;
229
+ const parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(
230
+ msg.payload.metadata,
231
+ previousParsedMetadata
232
+ );
233
+ if (session._isConsumed) {
234
+ return;
235
+ }
236
+ if (Value.Check(
237
+ HandshakeErrorCustomHandlerFatalResponseCodes,
238
+ parsedMetadataOrFailureCode
239
+ )) {
240
+ this.rejectHandshakeRequest(
241
+ session,
242
+ msg.from,
243
+ "rejected by handshake handler",
244
+ parsedMetadataOrFailureCode,
245
+ {
246
+ ...session.loggingMetadata,
247
+ connectedTo: msg.from,
248
+ clientId: this.clientId
249
+ }
250
+ );
251
+ return;
252
+ }
253
+ parsedMetadata = parsedMetadataOrFailureCode;
206
254
  }
207
255
  let connectCase = "new session";
208
256
  const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
209
257
  const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
210
- if (oldSession && oldSession.id === msg.payload.sessionId) {
258
+ if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
211
259
  connectCase = "transparent reconnection";
212
260
  const ourNextSeq = oldSession.nextSeq();
213
261
  const ourAck = oldSession.ack;
@@ -266,10 +314,11 @@ var ServerTransport = class extends Transport {
266
314
  }
267
315
  if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {
268
316
  connectCase = "unknown session";
317
+ 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}`;
269
318
  this.rejectHandshakeRequest(
270
319
  session,
271
320
  msg.from,
272
- `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`,
321
+ rejectionMessage,
273
322
  "SESSION_STATE_MISMATCH",
274
323
  {
275
324
  ...session.loggingMetadata,
@@ -320,7 +369,10 @@ var ServerTransport = class extends Transport {
320
369
  },
321
370
  onMessage: (msg2) => this.handleMsg(msg2),
322
371
  onInvalidMessage: (reason) => {
323
- this.protocolError(ProtocolError.MessageOrderingViolated, reason);
372
+ this.protocolError({
373
+ type: ProtocolError.MessageOrderingViolated,
374
+ message: reason
375
+ });
324
376
  this.deleteSession(connectedSession);
325
377
  }
326
378
  },
@@ -331,50 +383,9 @@ var ServerTransport = class extends Transport {
331
383
  this.pendingSessions.delete(session);
332
384
  connectedSession.startActiveHeartbeat();
333
385
  }
334
- async validateHandshakeMetadata(handshakingSession, existingSession, rawMetadata, from) {
335
- let parsedMetadata = {};
336
- if (this.handshakeExtensions) {
337
- if (!Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
338
- this.rejectHandshakeRequest(
339
- handshakingSession,
340
- from,
341
- "received malformed handshake metadata",
342
- "MALFORMED_HANDSHAKE_META",
343
- {
344
- ...handshakingSession.loggingMetadata,
345
- connectedTo: from,
346
- validationErrors: [
347
- ...Value.Errors(this.handshakeExtensions.schema, rawMetadata)
348
- ]
349
- }
350
- );
351
- return false;
352
- }
353
- const previousParsedMetadata = existingSession ? this.sessionHandshakeMetadata.get(existingSession.to) : void 0;
354
- parsedMetadata = await this.handshakeExtensions.validate(
355
- rawMetadata,
356
- previousParsedMetadata
357
- );
358
- if (parsedMetadata === false) {
359
- this.rejectHandshakeRequest(
360
- handshakingSession,
361
- from,
362
- "rejected by handshake handler",
363
- "REJECTED_BY_CUSTOM_HANDLER",
364
- {
365
- ...handshakingSession.loggingMetadata,
366
- connectedTo: from,
367
- clientId: this.clientId
368
- }
369
- );
370
- return false;
371
- }
372
- }
373
- return parsedMetadata;
374
- }
375
386
  };
376
387
 
377
388
  export {
378
389
  ServerTransport
379
390
  };
380
- //# sourceMappingURL=chunk-F2E7ILHW.js.map
391
+ //# sourceMappingURL=chunk-XV5WUEIR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/server.ts"],"sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { ParsedMetadata } from '../router/context';\nimport { ServerHandshakeOptions } from '../router/handshake';\nimport {\n ControlMessageHandshakeRequestSchema,\n HandshakeErrorCustomHandlerFatalResponseCodes,\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n acceptedProtocolVersions,\n PartialTransportMessage,\n TransportClientId,\n handshakeResponseMessage,\n currentProtocolVersion,\n} from './message';\nimport {\n ProvidedServerTransportOptions,\n ServerTransportOptions,\n defaultServerTransportOptions,\n} from './options';\nimport { Transport } from './transport';\nimport { coerceErrorString } from '../util/stringify';\nimport { Static } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { ProtocolError } from './events';\nimport { Connection } from './connection';\nimport { MessageMetadata } from '../logging';\nimport { SessionWaitingForHandshake } from './sessionStateMachine/SessionWaitingForHandshake';\nimport { SessionState } from './sessionStateMachine/common';\nimport {\n ServerSession,\n ServerSessionStateGraph,\n} from './sessionStateMachine/transitions';\n\nexport abstract class ServerTransport<\n ConnType extends Connection,\n> extends Transport<ConnType> {\n /**\n * The options for this transport.\n */\n protected options: ServerTransportOptions;\n\n /**\n * Optional handshake options for the server.\n */\n handshakeExtensions?: ServerHandshakeOptions;\n\n /**\n * A map of session handshake data for each session.\n */\n sessionHandshakeMetadata = new Map<TransportClientId, ParsedMetadata>();\n\n sessions = new Map<TransportClientId, ServerSession<ConnType>>();\n pendingSessions = new Set<SessionWaitingForHandshake<ConnType>>();\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedServerTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.sessions = new Map();\n this.options = {\n ...defaultServerTransportOptions,\n ...providedOptions,\n };\n this.log?.info(`initiated server transport`, {\n clientId: this.clientId,\n protocolVersion: currentProtocolVersion,\n });\n }\n\n extendHandshake(options: ServerHandshakeOptions) {\n this.handshakeExtensions = options;\n }\n\n send(to: string, msg: PartialTransportMessage): string {\n if (this.getStatus() === 'closed') {\n const err = 'transport is closed, cant send';\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n const session = this.sessions.get(to);\n if (!session) {\n const err = `session to ${to} does not exist`;\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n return session.send(msg);\n }\n\n protected deletePendingSession(\n pendingSession: SessionWaitingForHandshake<ConnType>,\n ) {\n pendingSession.close();\n // we don't dispatch a session disconnect event\n // for a non-identified session, just delete directly\n\n this.pendingSessions.delete(pendingSession);\n }\n\n protected deleteSession(session: ServerSession<ConnType>): void {\n this.sessionHandshakeMetadata.delete(session.to);\n super.deleteSession(session);\n }\n\n protected handleConnection(conn: ConnType) {\n if (this.getStatus() !== 'open') return;\n\n this.log?.info(`new incoming connection`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n });\n\n let receivedHandshake = false;\n const pendingSession = ServerSessionStateGraph.entrypoint(\n this.clientId,\n conn,\n {\n onConnectionClosed: () => {\n this.log?.warn(\n `connection from unknown closed before handshake finished`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onConnectionErrored: (err) => {\n const errorString = coerceErrorString(err);\n this.log?.warn(\n `connection from unknown errored before handshake finished: ${errorString}`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onHandshakeTimeout: () => {\n this.log?.warn(\n `connection from unknown timed out before handshake finished`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onHandshake: (msg) => {\n if (receivedHandshake) {\n this.log?.error(\n `received multiple handshake messages from pending session`,\n {\n ...pendingSession.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n this.deletePendingSession(pendingSession);\n return;\n }\n\n // let this resolve async, we just need to make sure its only\n // called once so we don't race while transitioning to connected\n // onHandshakeRequest is async as custom validation may be async\n receivedHandshake = true;\n void this.onHandshakeRequest(pendingSession, msg);\n },\n onInvalidHandshake: (reason, code) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n pendingSession.loggingMetadata,\n );\n this.deletePendingSession(pendingSession);\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\n },\n },\n this.options,\n this.log,\n );\n\n this.pendingSessions.add(pendingSession);\n }\n\n private rejectHandshakeRequest(\n session: SessionWaitingForHandshake<ConnType>,\n to: TransportClientId,\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n metadata: MessageMetadata,\n ) {\n session.conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: reason,\n });\n\n this.log?.warn(reason, metadata);\n\n session.sendHandshake(\n handshakeResponseMessage({\n from: this.clientId,\n to,\n status: {\n ok: false,\n code,\n reason,\n },\n }),\n );\n\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\n this.deletePendingSession(session);\n }\n\n protected async onHandshakeRequest(\n session: SessionWaitingForHandshake<ConnType>,\n msg: OpaqueTransportMessage,\n ) {\n // invariant: msg is a handshake request\n if (!Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'received invalid handshake request',\n 'MALFORMED_HANDSHAKE',\n {\n ...session.loggingMetadata,\n transportMessage: msg,\n connectedTo: msg.from,\n validationErrors: [\n ...Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload),\n ],\n },\n );\n\n return;\n }\n\n // invariant: handshake request passes all the validation\n const gotVersion = msg.payload.protocolVersion;\n if (!acceptedProtocolVersions.includes(gotVersion)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `expected protocol version oneof [${acceptedProtocolVersions.toString()}], got ${gotVersion}`,\n 'PROTOCOL_VERSION_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n let oldSession = this.sessions.get(msg.from);\n\n // invariant: must pass custom validation if defined\n let parsedMetadata: ParsedMetadata = {};\n if (this.handshakeExtensions) {\n if (!Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'received malformed handshake metadata',\n 'MALFORMED_HANDSHAKE_META',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n validationErrors: [\n ...Value.Errors(\n this.handshakeExtensions.schema,\n msg.payload.metadata,\n ),\n ],\n },\n );\n\n return;\n }\n\n const previousParsedMetadata = oldSession\n ? this.sessionHandshakeMetadata.get(oldSession.to)\n : undefined;\n\n const parsedMetadataOrFailureCode =\n await this.handshakeExtensions.validate(\n msg.payload.metadata,\n previousParsedMetadata,\n );\n\n // double-check to make sure we haven't transitioned the session yet\n if (session._isConsumed) {\n // bail out, don't need to do anything\n return;\n }\n\n // handler rejected the connection\n if (\n Value.Check(\n HandshakeErrorCustomHandlerFatalResponseCodes,\n parsedMetadataOrFailureCode,\n )\n ) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'rejected by handshake handler',\n parsedMetadataOrFailureCode,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n clientId: this.clientId,\n },\n );\n\n return;\n }\n\n // success!\n parsedMetadata = parsedMetadataOrFailureCode;\n }\n\n // 4 connect cases\n // 1. new session\n // we dont have a session and the client is requesting a new one\n // we can create the session as normal\n // 2. client is reconnecting to an existing session but we don't have it\n // reject this handshake, there's nothing we can do to salvage it\n // 3. transparent reconnect (old session exists and is the same as the client wants)\n // assign to old session\n // 4. hard reconnect (oldSession exists but but the client wants a new one)\n // we close the old session and create a new one\n let connectCase:\n | 'new session'\n | 'unknown session'\n | 'transparent reconnection'\n | 'hard reconnection' = 'new session';\n const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;\n\n if (\n this.options.enableTransparentSessionReconnects &&\n oldSession &&\n oldSession.id === msg.payload.sessionId\n ) {\n connectCase = 'transparent reconnection';\n\n // invariant: ordering must be correct\n const ourNextSeq = oldSession.nextSeq();\n const ourAck = oldSession.ack;\n\n // two incorrect cases where we cannot permit a reconnect:\n // - if the client is about to send a message in the future w.r.t to the server\n // - client.seq > server.ack => nextSentSeq > oldSession.ack\n if (clientNextSentSeq > ourAck) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n // - if the server is about to send a message in the future w.r.t to the client\n // - server.seq > client.ack => oldSession.nextSeq() > nextExpectedSeq\n if (ourNextSeq > clientNextExpectedSeq) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n // transparent reconnect seems ok, proceed by transitioning old session\n // to not connected\n if (oldSession.state !== SessionState.NoConnection) {\n const noConnectionSession =\n ServerSessionStateGraph.transition.ConnectedToNoConnection(\n oldSession,\n {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n },\n );\n\n oldSession = noConnectionSession;\n }\n\n this.updateSession(oldSession);\n } else if (oldSession) {\n connectCase = 'hard reconnection';\n\n // just nuke the old session entirely and proceed as if this was new\n this.log?.info(\n `client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n sessionId: msg.payload.sessionId,\n },\n );\n this.deleteSession(oldSession);\n oldSession = undefined;\n }\n\n if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {\n // we don't have a session, but the client is trying to reconnect\n // to an old session. we can't do anything about this, so we reject\n connectCase = 'unknown session';\n\n const rejectionMessage = this.options.enableTransparentSessionReconnects\n ? `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`\n : `client is attempting a transparent reconnect to a session but the server does not support it: ${msg.payload.sessionId}`;\n\n this.rejectHandshakeRequest(\n session,\n msg.from,\n rejectionMessage,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n return;\n }\n\n // from this point on, we're committed to connecting\n const sessionId = msg.payload.sessionId;\n this.log?.info(\n `handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n },\n );\n\n const responseMsg = handshakeResponseMessage({\n from: this.clientId,\n to: msg.from,\n status: {\n ok: true,\n sessionId,\n },\n });\n\n session.sendHandshake(responseMsg);\n\n // transition\n const connectedSession =\n ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(\n session,\n // by this point oldSession is either no connection or we dont have an old session\n oldSession,\n sessionId,\n msg.from,\n msg.tracing,\n {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n this.log?.warn(\n `connection to ${connectedSession.to} errored: ${errStr}`,\n connectedSession.loggingMetadata,\n );\n },\n onConnectionClosed: () => {\n this.log?.info(\n `connection to ${connectedSession.to} closed`,\n connectedSession.loggingMetadata,\n );\n this.onConnClosed(connectedSession);\n },\n onMessage: (msg) => this.handleMsg(msg),\n onInvalidMessage: (reason) => {\n this.protocolError({\n type: ProtocolError.MessageOrderingViolated,\n message: reason,\n });\n this.deleteSession(connectedSession);\n },\n },\n gotVersion,\n );\n\n this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);\n this.updateSession(connectedSession);\n this.pendingSessions.delete(session);\n connectedSession.startActiveHeartbeat();\n }\n}\n"],"mappings":"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","names":["msg"]}
@@ -3,7 +3,7 @@ import {
3
3
  createSessionTelemetryInfo,
4
4
  generateId,
5
5
  isAck
6
- } from "./chunk-BZBEE2VR.js";
6
+ } from "./chunk-3ROTSXAO.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
  };
@@ -150,15 +152,18 @@ var IdentifiedSession = class extends CommonSession {
150
152
  }
151
153
  get loggingMetadata() {
152
154
  const spanContext = this.telemetry.span.spanContext();
153
- return {
155
+ const metadata = {
154
156
  clientId: this.from,
155
157
  connectedTo: this.to,
156
- sessionId: this.id,
157
- telemetry: {
158
+ sessionId: this.id
159
+ };
160
+ if (this.telemetry.span.isRecording()) {
161
+ metadata.telemetry = {
158
162
  traceId: spanContext.traceId,
159
163
  spanId: spanContext.spanId
160
- }
161
- };
164
+ };
165
+ }
166
+ return metadata;
162
167
  }
163
168
  constructMsg(partialMsg) {
164
169
  const msg = {
@@ -187,9 +192,32 @@ var IdentifiedSession = class extends CommonSession {
187
192
  this.telemetry.span.end();
188
193
  }
189
194
  };
195
+ var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
196
+ graceExpiryTime;
197
+ gracePeriodTimeout;
198
+ listeners;
199
+ constructor(props) {
200
+ super(props);
201
+ this.listeners = props.listeners;
202
+ this.graceExpiryTime = props.graceExpiryTime;
203
+ this.gracePeriodTimeout = setTimeout(() => {
204
+ this.listeners.onSessionGracePeriodElapsed();
205
+ }, this.graceExpiryTime - Date.now());
206
+ }
207
+ _handleStateExit() {
208
+ super._handleStateExit();
209
+ if (this.gracePeriodTimeout) {
210
+ clearTimeout(this.gracePeriodTimeout);
211
+ this.gracePeriodTimeout = void 0;
212
+ }
213
+ }
214
+ _handleClose() {
215
+ super._handleClose();
216
+ }
217
+ };
190
218
 
191
219
  // transport/sessionStateMachine/SessionConnecting.ts
192
- var SessionConnecting = class extends IdentifiedSession {
220
+ var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
193
221
  state = "Connecting" /* Connecting */;
194
222
  connPromise;
195
223
  listeners;
@@ -198,9 +226,6 @@ var SessionConnecting = class extends IdentifiedSession {
198
226
  super(props);
199
227
  this.connPromise = props.connPromise;
200
228
  this.listeners = props.listeners;
201
- this.connectionTimeout = setTimeout(() => {
202
- this.listeners.onConnectionTimeout();
203
- }, this.options.connectionTimeoutMs);
204
229
  this.connPromise.then(
205
230
  (conn) => {
206
231
  if (this._isConsumed)
@@ -213,17 +238,33 @@ var SessionConnecting = class extends IdentifiedSession {
213
238
  this.listeners.onConnectionFailed(err);
214
239
  }
215
240
  );
241
+ this.connectionTimeout = setTimeout(() => {
242
+ this.listeners.onConnectionTimeout();
243
+ }, this.options.connectionTimeoutMs);
216
244
  }
217
245
  // close a pending connection if it resolves, ignore errors if the promise
218
246
  // ends up rejected anyways
219
247
  bestEffortClose() {
220
- void this.connPromise.then((conn) => conn.close()).catch(() => {
248
+ const logger = this.log;
249
+ const metadata = this.loggingMetadata;
250
+ this.connPromise.then((conn) => {
251
+ conn.close();
252
+ logger?.info(
253
+ "connection eventually resolved but session has transitioned, closed connection",
254
+ {
255
+ ...metadata,
256
+ ...conn.loggingMetadata
257
+ }
258
+ );
259
+ }).catch(() => {
221
260
  });
222
261
  }
223
262
  _handleStateExit() {
224
263
  super._handleStateExit();
225
- clearTimeout(this.connectionTimeout);
226
- this.connectionTimeout = void 0;
264
+ if (this.connectionTimeout) {
265
+ clearTimeout(this.connectionTimeout);
266
+ this.connectionTimeout = void 0;
267
+ }
227
268
  }
228
269
  _handleClose() {
229
270
  this.bestEffortClose();
@@ -232,26 +273,13 @@ var SessionConnecting = class extends IdentifiedSession {
232
273
  };
233
274
 
234
275
  // transport/sessionStateMachine/SessionNoConnection.ts
235
- var SessionNoConnection = class extends IdentifiedSession {
276
+ var SessionNoConnection = class extends IdentifiedSessionWithGracePeriod {
236
277
  state = "NoConnection" /* NoConnection */;
237
- listeners;
238
- gracePeriodTimeout;
239
- constructor(props) {
240
- super(props);
241
- this.listeners = props.listeners;
242
- this.gracePeriodTimeout = setTimeout(() => {
243
- this.listeners.onSessionGracePeriodElapsed();
244
- }, this.options.sessionDisconnectGraceMs);
245
- }
246
278
  _handleClose() {
247
279
  super._handleClose();
248
280
  }
249
281
  _handleStateExit() {
250
282
  super._handleStateExit();
251
- if (this.gracePeriodTimeout) {
252
- clearTimeout(this.gracePeriodTimeout);
253
- this.gracePeriodTimeout = void 0;
254
- }
255
283
  }
256
284
  };
257
285
 
@@ -272,20 +300,24 @@ var SessionWaitingForHandshake = class extends CommonSession {
272
300
  this.conn.addErrorListener(this.listeners.onConnectionErrored);
273
301
  this.conn.addCloseListener(this.listeners.onConnectionClosed);
274
302
  }
303
+ get loggingMetadata() {
304
+ return {
305
+ clientId: this.from,
306
+ connId: this.conn.id,
307
+ ...this.conn.loggingMetadata
308
+ };
309
+ }
275
310
  onHandshakeData = (msg) => {
276
311
  const parsedMsg = this.parseMsg(msg);
277
312
  if (parsedMsg === null) {
278
- this.listeners.onInvalidHandshake("could not parse message");
313
+ this.listeners.onInvalidHandshake(
314
+ "could not parse message",
315
+ "MALFORMED_HANDSHAKE"
316
+ );
279
317
  return;
280
318
  }
281
319
  this.listeners.onHandshake(parsedMsg);
282
320
  };
283
- get loggingMetadata() {
284
- return {
285
- clientId: this.from,
286
- connId: this.conn.id
287
- };
288
- }
289
321
  sendHandshake(msg) {
290
322
  return this.conn.send(this.options.codec.toBuffer(msg));
291
323
  }
@@ -302,7 +334,7 @@ var SessionWaitingForHandshake = class extends CommonSession {
302
334
  };
303
335
 
304
336
  // transport/sessionStateMachine/SessionHandshaking.ts
305
- var SessionHandshaking = class extends IdentifiedSession {
337
+ var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
306
338
  state = "Handshaking" /* Handshaking */;
307
339
  conn;
308
340
  listeners;
@@ -318,10 +350,19 @@ var SessionHandshaking = class extends IdentifiedSession {
318
350
  this.conn.addErrorListener(this.listeners.onConnectionErrored);
319
351
  this.conn.addCloseListener(this.listeners.onConnectionClosed);
320
352
  }
353
+ get loggingMetadata() {
354
+ return {
355
+ ...super.loggingMetadata,
356
+ ...this.conn.loggingMetadata
357
+ };
358
+ }
321
359
  onHandshakeData = (msg) => {
322
360
  const parsedMsg = this.parseMsg(msg);
323
361
  if (parsedMsg === null) {
324
- this.listeners.onInvalidHandshake("could not parse message");
362
+ this.listeners.onInvalidHandshake(
363
+ "could not parse message",
364
+ "MALFORMED_HANDSHAKE"
365
+ );
325
366
  return;
326
367
  }
327
368
  this.listeners.onHandshake(parsedMsg);
@@ -334,7 +375,10 @@ var SessionHandshaking = class extends IdentifiedSession {
334
375
  this.conn.removeDataListener(this.onHandshakeData);
335
376
  this.conn.removeErrorListener(this.listeners.onConnectionErrored);
336
377
  this.conn.removeCloseListener(this.listeners.onConnectionClosed);
337
- clearTimeout(this.handshakeTimeout);
378
+ if (this.handshakeTimeout) {
379
+ clearTimeout(this.handshakeTimeout);
380
+ this.handshakeTimeout = void 0;
381
+ }
338
382
  }
339
383
  _handleClose() {
340
384
  super._handleClose();
@@ -399,6 +443,12 @@ var SessionConnected = class extends IdentifiedSession {
399
443
  this.heartbeatMisses++;
400
444
  }, this.options.heartbeatIntervalMs);
401
445
  }
446
+ get loggingMetadata() {
447
+ return {
448
+ ...super.loggingMetadata,
449
+ ...this.conn.loggingMetadata
450
+ };
451
+ }
402
452
  startActiveHeartbeat() {
403
453
  this.isActivelyHeartbeating = true;
404
454
  }
@@ -414,8 +464,10 @@ var SessionConnected = class extends IdentifiedSession {
414
464
  }
415
465
  onMessageData = (msg) => {
416
466
  const parsedMsg = this.parseMsg(msg);
417
- if (parsedMsg === null)
467
+ if (parsedMsg === null) {
468
+ this.listeners.onInvalidMessage("could not parse message");
418
469
  return;
470
+ }
419
471
  if (parsedMsg.seq !== this.ack) {
420
472
  if (parsedMsg.seq < this.ack) {
421
473
  this.log?.debug(
@@ -472,7 +524,7 @@ var SessionConnected = class extends IdentifiedSession {
472
524
  };
473
525
 
474
526
  // transport/sessionStateMachine/SessionBackingOff.ts
475
- var SessionBackingOff = class extends IdentifiedSession {
527
+ var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
476
528
  state = "BackingOff" /* BackingOff */;
477
529
  listeners;
478
530
  backoffTimeout;
@@ -510,6 +562,12 @@ function inheritSharedSession(session) {
510
562
  protocolVersion: session.protocolVersion
511
563
  };
512
564
  }
565
+ function inheritSharedSessionWithGrace(session) {
566
+ return {
567
+ ...inheritSharedSession(session),
568
+ graceExpiryTime: session.graceExpiryTime
569
+ };
570
+ }
513
571
  var SessionStateGraph = {
514
572
  entrypoints: {
515
573
  NoConnection: (to, from, listeners, options, protocolVersion, log) => {
@@ -523,6 +581,7 @@ var SessionStateGraph = {
523
581
  to,
524
582
  seq: 0,
525
583
  ack: 0,
584
+ graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
526
585
  sendBuffer,
527
586
  telemetry,
528
587
  options,
@@ -555,7 +614,7 @@ var SessionStateGraph = {
555
614
  transition: {
556
615
  // happy path transitions
557
616
  NoConnectionToBackingOff: (oldSession, backoffMs, listeners) => {
558
- const carriedState = inheritSharedSession(oldSession);
617
+ const carriedState = inheritSharedSessionWithGrace(oldSession);
559
618
  oldSession._handleStateExit();
560
619
  const session = new SessionBackingOff({
561
620
  backoffMs,
@@ -572,7 +631,7 @@ var SessionStateGraph = {
572
631
  return session;
573
632
  },
574
633
  BackingOffToConnecting: (oldSession, connPromise, listeners) => {
575
- const carriedState = inheritSharedSession(oldSession);
634
+ const carriedState = inheritSharedSessionWithGrace(oldSession);
576
635
  oldSession._handleStateExit();
577
636
  const session = new SessionConnecting({
578
637
  connPromise,
@@ -589,7 +648,7 @@ var SessionStateGraph = {
589
648
  return session;
590
649
  },
591
650
  ConnectingToHandshaking: (oldSession, conn, listeners) => {
592
- const carriedState = inheritSharedSession(oldSession);
651
+ const carriedState = inheritSharedSessionWithGrace(oldSession);
593
652
  oldSession._handleStateExit();
594
653
  const session = new SessionHandshaking({
595
654
  conn,
@@ -667,9 +726,12 @@ var SessionStateGraph = {
667
726
  },
668
727
  // disconnect paths
669
728
  BackingOffToNoConnection: (oldSession, listeners) => {
670
- const carriedState = inheritSharedSession(oldSession);
729
+ const carriedState = inheritSharedSessionWithGrace(oldSession);
671
730
  oldSession._handleStateExit();
672
- const session = new SessionNoConnection({ listeners, ...carriedState });
731
+ const session = new SessionNoConnection({
732
+ listeners,
733
+ ...carriedState
734
+ });
673
735
  session.log?.info(
674
736
  `session ${session.id} transition from BackingOff to NoConnection`,
675
737
  {
@@ -680,10 +742,13 @@ var SessionStateGraph = {
680
742
  return session;
681
743
  },
682
744
  ConnectingToNoConnection: (oldSession, listeners) => {
683
- const carriedState = inheritSharedSession(oldSession);
745
+ const carriedState = inheritSharedSessionWithGrace(oldSession);
684
746
  oldSession.bestEffortClose();
685
747
  oldSession._handleStateExit();
686
- const session = new SessionNoConnection({ listeners, ...carriedState });
748
+ const session = new SessionNoConnection({
749
+ listeners,
750
+ ...carriedState
751
+ });
687
752
  session.log?.info(
688
753
  `session ${session.id} transition from Connecting to NoConnection`,
689
754
  {
@@ -694,10 +759,13 @@ var SessionStateGraph = {
694
759
  return session;
695
760
  },
696
761
  HandshakingToNoConnection: (oldSession, listeners) => {
697
- const carriedState = inheritSharedSession(oldSession);
762
+ const carriedState = inheritSharedSessionWithGrace(oldSession);
698
763
  oldSession.conn.close();
699
764
  oldSession._handleStateExit();
700
- const session = new SessionNoConnection({ listeners, ...carriedState });
765
+ const session = new SessionNoConnection({
766
+ listeners,
767
+ ...carriedState
768
+ });
701
769
  session.log?.info(
702
770
  `session ${session.id} transition from Handshaking to NoConnection`,
703
771
  {
@@ -709,9 +777,14 @@ var SessionStateGraph = {
709
777
  },
710
778
  ConnectedToNoConnection: (oldSession, listeners) => {
711
779
  const carriedState = inheritSharedSession(oldSession);
780
+ const graceExpiryTime = Date.now() + oldSession.options.sessionDisconnectGraceMs;
712
781
  oldSession.conn.close();
713
782
  oldSession._handleStateExit();
714
- const session = new SessionNoConnection({ listeners, ...carriedState });
783
+ const session = new SessionNoConnection({
784
+ listeners,
785
+ graceExpiryTime,
786
+ ...carriedState
787
+ });
715
788
  session.log?.info(
716
789
  `session ${session.id} transition from Connected to NoConnection`,
717
790
  {
@@ -728,24 +801,42 @@ var ClientSessionStateGraph = {
728
801
  entrypoint: SessionStateGraph.entrypoints.NoConnection,
729
802
  transition: {
730
803
  // happy paths
804
+ // NoConnection -> BackingOff: attempt to connect
731
805
  NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,
806
+ // BackingOff -> Connecting: backoff period elapsed, start connection
732
807
  BackingOffToConnecting: transitions.BackingOffToConnecting,
808
+ // Connecting -> Handshaking: connection established, start handshake
733
809
  ConnectingToHandshaking: transitions.ConnectingToHandshaking,
810
+ // Handshaking -> Connected: handshake complete, session ready
734
811
  HandshakingToConnected: transitions.HandshakingToConnected,
735
812
  // disconnect paths
813
+ // BackingOff -> NoConnection: unused
736
814
  BackingOffToNoConnection: transitions.BackingOffToNoConnection,
815
+ // Connecting -> NoConnection: connection failed or connection timeout
737
816
  ConnectingToNoConnection: transitions.ConnectingToNoConnection,
817
+ // Handshaking -> NoConnection: connection closed or handshake timeout
738
818
  HandshakingToNoConnection: transitions.HandshakingToNoConnection,
819
+ // Connected -> NoConnection: connection closed
739
820
  ConnectedToNoConnection: transitions.ConnectedToNoConnection
821
+ // destroy/close paths
822
+ // NoConnection -> x: grace period elapsed
823
+ // BackingOff -> x: grace period elapsed
824
+ // Connecting -> x: grace period elapsed
825
+ // Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection
826
+ // Connected -> x: grace period elapsed or invalid message
740
827
  }
741
828
  };
742
829
  var ServerSessionStateGraph = {
743
830
  entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,
744
831
  transition: {
745
832
  // happy paths
833
+ // WaitingForHandshake -> Connected: handshake complete, session ready
746
834
  WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,
747
835
  // disconnect paths
836
+ // Connected -> NoConnection: connection closed
748
837
  ConnectedToNoConnection: transitions.ConnectedToNoConnection
838
+ // destroy/close paths
839
+ // WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed
749
840
  }
750
841
  };
751
842
 
@@ -758,4 +849,4 @@ export {
758
849
  ClientSessionStateGraph,
759
850
  ServerSessionStateGraph
760
851
  };
761
- //# sourceMappingURL=chunk-3XKJOFZA.js.map
852
+ //# sourceMappingURL=chunk-Y3JHOIJ7.js.map