@replit/river 0.200.0-rc.3 → 0.200.0-rc.4

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 (64) hide show
  1. package/dist/{chunk-WSCAA7VY.js → chunk-2BF4VMUZ.js} +2 -2
  2. package/dist/{chunk-ZQVKFLAB.js → chunk-BZQQXMVF.js} +6 -4
  3. package/dist/chunk-BZQQXMVF.js.map +1 -0
  4. package/dist/{chunk-UFMDEG44.js → chunk-F7Z2QQRL.js} +2 -2
  5. package/dist/{chunk-QXLXDJD5.js → chunk-IMFMNIEO.js} +11 -9
  6. package/dist/chunk-IMFMNIEO.js.map +1 -0
  7. package/dist/{chunk-GWYJZFCW.js → chunk-RDGHFHXN.js} +10 -5
  8. package/dist/chunk-RDGHFHXN.js.map +1 -0
  9. package/dist/{chunk-O4O2E6DJ.js → chunk-SI4YHBTI.js} +3 -3
  10. package/dist/{chunk-UIYGPURD.js → chunk-SIRRYRLQ.js} +46 -10
  11. package/dist/chunk-SIRRYRLQ.js.map +1 -0
  12. package/dist/{chunk-3CCOX55A.js → chunk-V57VWV5S.js} +7 -5
  13. package/dist/chunk-V57VWV5S.js.map +1 -0
  14. package/dist/{client-a84783be.d.ts → client-f56a6da3.d.ts} +1 -1
  15. package/dist/{connection-320fb130.d.ts → connection-11991b13.d.ts} +1 -1
  16. package/dist/{connection-d0b488e6.d.ts → connection-6031a354.d.ts} +1 -1
  17. package/dist/{context-c9668e95.d.ts → context-73df8978.d.ts} +2 -1
  18. package/dist/router/index.cjs +45 -9
  19. package/dist/router/index.cjs.map +1 -1
  20. package/dist/router/index.d.cts +7 -7
  21. package/dist/router/index.d.ts +7 -7
  22. package/dist/router/index.js +2 -2
  23. package/dist/{server-dbad597e.d.ts → server-9f31d98f.d.ts} +1 -1
  24. package/dist/{services-690e5553.d.ts → services-c67758fc.d.ts} +2 -2
  25. package/dist/transport/impls/uds/client.cjs +12 -6
  26. package/dist/transport/impls/uds/client.cjs.map +1 -1
  27. package/dist/transport/impls/uds/client.d.cts +3 -3
  28. package/dist/transport/impls/uds/client.d.ts +3 -3
  29. package/dist/transport/impls/uds/client.js +5 -5
  30. package/dist/transport/impls/uds/server.cjs +16 -9
  31. package/dist/transport/impls/uds/server.cjs.map +1 -1
  32. package/dist/transport/impls/uds/server.d.cts +3 -3
  33. package/dist/transport/impls/uds/server.d.ts +3 -3
  34. package/dist/transport/impls/uds/server.js +5 -5
  35. package/dist/transport/impls/ws/client.cjs +12 -6
  36. package/dist/transport/impls/ws/client.cjs.map +1 -1
  37. package/dist/transport/impls/ws/client.d.cts +3 -3
  38. package/dist/transport/impls/ws/client.d.ts +3 -3
  39. package/dist/transport/impls/ws/client.js +5 -5
  40. package/dist/transport/impls/ws/server.cjs +16 -9
  41. package/dist/transport/impls/ws/server.cjs.map +1 -1
  42. package/dist/transport/impls/ws/server.d.cts +3 -3
  43. package/dist/transport/impls/ws/server.d.ts +3 -3
  44. package/dist/transport/impls/ws/server.js +5 -5
  45. package/dist/transport/index.cjs +18 -10
  46. package/dist/transport/index.cjs.map +1 -1
  47. package/dist/transport/index.d.cts +3 -3
  48. package/dist/transport/index.d.ts +3 -3
  49. package/dist/transport/index.js +5 -5
  50. package/dist/util/testHelpers.cjs +12 -5
  51. package/dist/util/testHelpers.cjs.map +1 -1
  52. package/dist/util/testHelpers.d.cts +3 -3
  53. package/dist/util/testHelpers.d.ts +3 -3
  54. package/dist/util/testHelpers.js +7 -5
  55. package/dist/util/testHelpers.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/chunk-3CCOX55A.js.map +0 -1
  58. package/dist/chunk-GWYJZFCW.js.map +0 -1
  59. package/dist/chunk-QXLXDJD5.js.map +0 -1
  60. package/dist/chunk-UIYGPURD.js.map +0 -1
  61. package/dist/chunk-ZQVKFLAB.js.map +0 -1
  62. /package/dist/{chunk-WSCAA7VY.js.map → chunk-2BF4VMUZ.js.map} +0 -0
  63. /package/dist/{chunk-UFMDEG44.js.map → chunk-F7Z2QQRL.js.map} +0 -0
  64. /package/dist/{chunk-O4O2E6DJ.js.map → chunk-SI4YHBTI.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Connection
3
- } from "./chunk-O4O2E6DJ.js";
3
+ } from "./chunk-SI4YHBTI.js";
4
4
 
5
5
  // transport/impls/ws/connection.ts
6
6
  var WebSocketConnection = class extends Connection {
@@ -47,4 +47,4 @@ var WebSocketConnection = class extends Connection {
47
47
  export {
48
48
  WebSocketConnection
49
49
  };
50
- //# sourceMappingURL=chunk-WSCAA7VY.js.map
50
+ //# sourceMappingURL=chunk-2BF4VMUZ.js.map
@@ -1,19 +1,20 @@
1
1
  import {
2
2
  ProtocolError,
3
3
  Transport
4
- } from "./chunk-O4O2E6DJ.js";
4
+ } from "./chunk-SI4YHBTI.js";
5
5
  import {
6
6
  SessionStateGraph,
7
7
  defaultClientTransportOptions
8
- } from "./chunk-GWYJZFCW.js";
8
+ } from "./chunk-RDGHFHXN.js";
9
9
  import {
10
10
  ControlMessageHandshakeResponseSchema,
11
11
  HandshakeErrorRetriableResponseCodes,
12
12
  coerceErrorString,
13
+ currentProtocolVersion,
13
14
  getPropagationContext,
14
15
  handshakeRequestMessage,
15
16
  tracing_default
16
- } from "./chunk-3CCOX55A.js";
17
+ } from "./chunk-V57VWV5S.js";
17
18
 
18
19
  // transport/client.ts
19
20
  import { SpanStatusCode } from "@opentelemetry/api";
@@ -150,6 +151,7 @@ var ClientTransport = class extends Transport {
150
151
  }
151
152
  },
152
153
  this.options,
154
+ currentProtocolVersion,
153
155
  this.log
154
156
  );
155
157
  this.updateSession(session);
@@ -396,4 +398,4 @@ var ClientTransport = class extends Transport {
396
398
  export {
397
399
  ClientTransport
398
400
  };
399
- //# sourceMappingURL=chunk-ZQVKFLAB.js.map
401
+ //# sourceMappingURL=chunk-BZQQXMVF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../transport/client.ts","../transport/rateLimit.ts"],"sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { ClientHandshakeOptions } from '../router/handshake';\nimport {\n ControlMessageHandshakeResponseSchema,\n HandshakeErrorRetriableResponseCodes,\n OpaqueTransportMessage,\n PartialTransportMessage,\n TransportClientId,\n currentProtocolVersion,\n handshakeRequestMessage,\n} from './message';\nimport {\n ClientTransportOptions,\n ProvidedClientTransportOptions,\n defaultClientTransportOptions,\n} from './options';\nimport { LeakyBucketRateLimit } from './rateLimit';\nimport { Transport } from './transport';\nimport { coerceErrorString } from '../util/stringify';\nimport { ProtocolError } from './events';\nimport { Value } from '@sinclair/typebox/value';\nimport tracer, { getPropagationContext } from '../tracing';\nimport { Connection } from './connection';\nimport { MessageMetadata } from '../logging';\nimport { SessionConnecting } from './sessionStateMachine/SessionConnecting';\nimport { SessionHandshaking } from './sessionStateMachine/SessionHandshaking';\nimport { SessionConnected } from './sessionStateMachine/SessionConnected';\nimport { SessionStateGraph } from './sessionStateMachine/transitions';\nimport { SessionState } from './sessionStateMachine/common';\nimport { SessionNoConnection } from './sessionStateMachine/SessionNoConnection';\n\nexport abstract class ClientTransport<\n ConnType extends Connection,\n> extends Transport<ConnType> {\n /**\n * The options for this transport.\n */\n protected options: ClientTransportOptions;\n\n retryBudget: LeakyBucketRateLimit;\n\n /**\n * A flag indicating whether the transport should automatically reconnect\n * when a connection is dropped.\n * Realistically, this should always be true for clients unless you are writing\n * tests or a special case where you don't want to reconnect.\n */\n reconnectOnConnectionDrop = true;\n\n /**\n * Optional handshake options for this client.\n */\n handshakeExtensions?: ClientHandshakeOptions;\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedClientTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.options = {\n ...defaultClientTransportOptions,\n ...providedOptions,\n };\n this.retryBudget = new LeakyBucketRateLimit(this.options);\n }\n\n extendHandshake(options: ClientHandshakeOptions) {\n this.handshakeExtensions = options;\n }\n\n /**\n * Abstract method that creates a new {@link Connection} object.\n * This should call {@link handleConnection} when the connection is created.\n * The downstream client implementation needs to implement this.\n *\n * @param to The client ID of the node to connect to.\n * @returns The new connection object.\n */\n protected abstract createNewOutgoingConnection(\n to: TransportClientId,\n ): Promise<ConnType>;\n\n private tryReconnecting(to: string) {\n if (this.reconnectOnConnectionDrop && this.getStatus() === 'open') {\n this.connect(to);\n }\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 let session = this.sessions.get(to);\n if (!session) {\n session = this.createUnconnectedSession(to);\n }\n\n return session.send(msg);\n }\n\n private createUnconnectedSession(to: string): SessionNoConnection {\n const session = SessionStateGraph.entrypoints.NoConnection(\n to,\n this.clientId,\n {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(session);\n },\n },\n this.options,\n currentProtocolVersion,\n this.log,\n );\n\n this.updateSession(session);\n return session;\n }\n\n // listeners\n protected onConnectingFailed(session: SessionConnecting<ConnType>) {\n const noConnectionSession = super.onConnectingFailed(session);\n this.tryReconnecting(noConnectionSession.to);\n return noConnectionSession;\n }\n\n protected onConnClosed(\n session: SessionHandshaking<ConnType> | SessionConnected<ConnType>,\n ) {\n const noConnectionSession = super.onConnClosed(session);\n this.tryReconnecting(noConnectionSession.to);\n return noConnectionSession;\n }\n\n protected onConnectionEstablished(\n session: SessionConnecting<ConnType>,\n conn: ConnType,\n ): SessionHandshaking<ConnType> {\n // transition to handshaking\n const handshakingSession =\n SessionStateGraph.transition.ConnectingToHandshaking(session, conn, {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n this.log?.error(\n `connection to ${handshakingSession.to} errored during handshake: ${errStr}`,\n handshakingSession.loggingMetadata,\n );\n },\n onConnectionClosed: () => {\n this.log?.warn(\n `connection to ${handshakingSession.to} closed during handshake`,\n handshakingSession.loggingMetadata,\n );\n this.onConnClosed(handshakingSession);\n },\n onHandshake: (msg) => {\n this.onHandshakeResponse(handshakingSession, msg);\n },\n onInvalidHandshake: (reason) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n handshakingSession.loggingMetadata,\n );\n this.deleteSession(session);\n this.protocolError(ProtocolError.HandshakeFailed, reason);\n },\n onHandshakeTimeout: () => {\n this.log?.error(\n `connection to ${handshakingSession.to} timed out during handshake`,\n handshakingSession.loggingMetadata,\n );\n this.onConnClosed(handshakingSession);\n },\n });\n\n this.updateSession(handshakingSession);\n void this.sendHandshake(handshakingSession);\n return handshakingSession;\n }\n\n private rejectHandshakeResponse(\n session: SessionHandshaking<ConnType>,\n reason: string,\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 this.deleteSession(session);\n }\n\n protected onHandshakeResponse(\n session: SessionHandshaking<ConnType>,\n msg: OpaqueTransportMessage,\n ) {\n // invariant: msg is a handshake response\n if (!Value.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {\n const reason = `received invalid handshake response`;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n validationErrors: [\n ...Value.Errors(ControlMessageHandshakeResponseSchema, msg.payload),\n ],\n });\n return;\n }\n\n // invariant: handshake response should be ok\n if (!msg.payload.status.ok) {\n // TODO: remove conditional check after we know code is always present\n const retriable = msg.payload.status.code\n ? Value.Check(\n HandshakeErrorRetriableResponseCodes,\n msg.payload.status.code,\n )\n : false;\n\n const reason = `handshake failed: ${msg.payload.status.reason}`;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n\n if (retriable) {\n this.tryReconnecting(session.to);\n } else {\n this.deleteSession(session);\n this.protocolError(ProtocolError.HandshakeFailed, reason);\n }\n\n return;\n }\n\n // invariant: session id should match between client + server\n if (msg.payload.status.sessionId !== session.id) {\n const reason = `session id mismatch: expected ${session.id}, got ${msg.payload.status.sessionId}`;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n return;\n }\n\n // transition to connected!\n this.log?.info(`handshake from ${msg.from} ok`, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n\n const connectedSession =\n SessionStateGraph.transition.HandshakingToConnected(session, {\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.deleteSession(connectedSession);\n this.protocolError(ProtocolError.MessageOrderingViolated, reason);\n },\n });\n\n this.updateSession(connectedSession);\n this.retryBudget.startRestoringBudget(connectedSession.to);\n }\n\n /**\n * Manually attempts to connect to a client.\n * @param to The client ID of the node to connect to.\n */\n connect(to: TransportClientId) {\n // create a new session if one does not exist\n let session = this.sessions.get(to);\n session ??= this.createUnconnectedSession(to);\n\n if (session.state !== SessionState.NoConnection) {\n // already trying to connect\n this.log?.debug(\n `session to ${to} has state ${session.state}, skipping connect attempt`,\n session.loggingMetadata,\n );\n return;\n }\n\n if (this.getStatus() !== 'open') {\n this.log?.info(\n `transport state is no longer open, cancelling attempt to connect to ${to}`,\n session.loggingMetadata,\n );\n return;\n }\n\n // check budget\n if (!this.retryBudget.hasBudget(to)) {\n const budgetConsumed = this.retryBudget.getBudgetConsumed(to);\n const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;\n this.log?.error(errMsg, session.loggingMetadata);\n this.protocolError(ProtocolError.RetriesExceeded, errMsg);\n return;\n }\n\n let sleep = Promise.resolve();\n const backoffMs = this.retryBudget.getBackoffMs(to);\n if (backoffMs > 0) {\n sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));\n }\n\n this.log?.info(\n `attempting connection to ${to} (${backoffMs}ms backoff)`,\n session.loggingMetadata,\n );\n\n this.retryBudget.consumeBudget(to);\n const reconnectPromise = tracer.startActiveSpan('connect', async (span) => {\n try {\n span.addEvent('backoff', { backoffMs });\n await sleep;\n if (this.getStatus() !== 'open') {\n throw new Error('transport state is no longer open');\n }\n\n span.addEvent('connecting');\n return await this.createNewOutgoingConnection(to);\n } catch (err) {\n // rethrow the error so that the promise is rejected\n // as it was before we wrapped it in a span\n const errStr = coerceErrorString(err);\n span.recordException(errStr);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw err;\n } finally {\n span.end();\n }\n });\n\n const connectingSession =\n SessionStateGraph.transition.NoConnectionToConnecting(\n session,\n reconnectPromise,\n {\n onConnectionEstablished: (conn) => {\n this.log?.debug(\n `connection to ${connectingSession.to} established`,\n connectingSession.loggingMetadata,\n );\n\n // cast here because conn can't be narrowed to ConnType\n // in the callback due to variance rules\n this.onConnectionEstablished(connectingSession, conn as ConnType);\n },\n onConnectionFailed: (error: unknown) => {\n const errStr = coerceErrorString(error);\n this.log?.error(\n `error connecting to ${connectingSession.to}: ${errStr}`,\n connectingSession.loggingMetadata,\n );\n this.onConnectingFailed(connectingSession);\n },\n onConnectionTimeout: () => {\n this.log?.error(\n `connection to ${connectingSession.to} timed out`,\n connectingSession.loggingMetadata,\n );\n this.onConnectingFailed(connectingSession);\n },\n },\n );\n\n this.updateSession(connectingSession);\n }\n\n private async sendHandshake(session: SessionHandshaking<ConnType>) {\n let metadata: unknown = undefined;\n\n if (this.handshakeExtensions) {\n metadata = await this.handshakeExtensions.construct();\n }\n\n const requestMsg = handshakeRequestMessage({\n from: this.clientId,\n to: session.to,\n sessionId: session.id,\n expectedSessionState: {\n nextExpectedSeq: session.ack,\n nextSentSeq: session.nextSeq(),\n },\n metadata,\n tracing: getPropagationContext(session.telemetry.ctx),\n });\n\n this.log?.debug(`sending handshake request to ${session.to}`, {\n ...session.loggingMetadata,\n transportMessage: requestMsg,\n });\n\n session.sendHandshake(requestMsg);\n }\n\n close() {\n this.retryBudget.close();\n super.close();\n }\n}\n","import { TransportClientId } from './message';\n\n/**\n * Options to control the backoff and retry behavior of the client transport's connection behaviour.\n *\n * River implements exponential backoff with jitter to prevent flooding the server\n * when there's an issue with connection establishment.\n *\n * The backoff is calculated via the following:\n * backOff = min(jitter + {@link baseIntervalMs} * 2 ^ budget_consumed, {@link maxBackoffMs})\n *\n * We use a leaky bucket rate limit with a budget of {@link attemptBudgetCapacity} reconnection attempts.\n * Budget only starts to restore after a successful handshake at a rate of one budget per {@link budgetRestoreIntervalMs}.\n */\nexport interface ConnectionRetryOptions {\n /**\n * The base interval to wait before retrying a connection.\n */\n baseIntervalMs: number;\n\n /**\n * The maximum random jitter to add to the total backoff time.\n */\n maxJitterMs: number;\n\n /**\n * The maximum amount of time to wait before retrying a connection.\n * This does not include the jitter.\n */\n maxBackoffMs: number;\n\n /**\n * The max number of times to attempt a connection before a successful handshake.\n * This persists across connections but starts restoring budget after a successful handshake.\n * The restoration interval depends on {@link budgetRestoreIntervalMs}\n */\n attemptBudgetCapacity: number;\n\n /**\n * After a successful connection attempt, how long to wait before we restore a single budget.\n */\n budgetRestoreIntervalMs: number;\n}\n\nexport class LeakyBucketRateLimit {\n private budgetConsumed: Map<TransportClientId, number>;\n private intervalHandles: Map<\n TransportClientId,\n ReturnType<typeof setInterval>\n >;\n private readonly options: ConnectionRetryOptions;\n\n constructor(options: ConnectionRetryOptions) {\n this.options = options;\n this.budgetConsumed = new Map();\n this.intervalHandles = new Map();\n }\n\n getBackoffMs(user: TransportClientId) {\n if (!this.budgetConsumed.has(user)) return 0;\n\n const exponent = Math.max(0, this.getBudgetConsumed(user) - 1);\n const jitter = Math.floor(Math.random() * this.options.maxJitterMs);\n const backoffMs = Math.min(\n this.options.baseIntervalMs * 2 ** exponent,\n this.options.maxBackoffMs,\n );\n\n return backoffMs + jitter;\n }\n\n get totalBudgetRestoreTime() {\n return (\n this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity\n );\n }\n\n consumeBudget(user: TransportClientId) {\n // If we're consuming again, let's ensure that we're not leaking\n this.stopLeak(user);\n this.budgetConsumed.set(user, this.getBudgetConsumed(user) + 1);\n }\n\n getBudgetConsumed(user: TransportClientId) {\n return this.budgetConsumed.get(user) ?? 0;\n }\n\n hasBudget(user: TransportClientId) {\n return this.getBudgetConsumed(user) < this.options.attemptBudgetCapacity;\n }\n\n startRestoringBudget(user: TransportClientId) {\n if (this.intervalHandles.has(user)) {\n return;\n }\n\n const restoreBudgetForUser = () => {\n const currentBudget = this.budgetConsumed.get(user);\n if (!currentBudget) {\n this.stopLeak(user);\n return;\n }\n\n const newBudget = currentBudget - 1;\n if (newBudget === 0) {\n this.budgetConsumed.delete(user);\n return;\n }\n\n this.budgetConsumed.set(user, newBudget);\n };\n\n const intervalHandle = setInterval(\n restoreBudgetForUser,\n this.options.budgetRestoreIntervalMs,\n );\n\n this.intervalHandles.set(user, intervalHandle);\n }\n\n private stopLeak(user: TransportClientId) {\n if (!this.intervalHandles.has(user)) {\n return;\n }\n\n clearInterval(this.intervalHandles.get(user));\n this.intervalHandles.delete(user);\n }\n\n close() {\n for (const user of this.intervalHandles.keys()) {\n this.stopLeak(user);\n }\n }\n}\n"],"mappings":"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","names":["msg"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Connection
3
- } from "./chunk-O4O2E6DJ.js";
3
+ } from "./chunk-SI4YHBTI.js";
4
4
 
5
5
  // transport/transforms/messageFraming.ts
6
6
  import { Transform } from "node:stream";
@@ -109,4 +109,4 @@ var UdsConnection = class extends Connection {
109
109
  export {
110
110
  UdsConnection
111
111
  };
112
- //# sourceMappingURL=chunk-UFMDEG44.js.map
112
+ //# sourceMappingURL=chunk-F7Z2QQRL.js.map
@@ -1,17 +1,18 @@
1
1
  import {
2
2
  ProtocolError,
3
3
  Transport
4
- } from "./chunk-O4O2E6DJ.js";
4
+ } from "./chunk-SI4YHBTI.js";
5
5
  import {
6
6
  SessionStateGraph,
7
7
  defaultServerTransportOptions
8
- } from "./chunk-GWYJZFCW.js";
8
+ } from "./chunk-RDGHFHXN.js";
9
9
  import {
10
10
  ControlMessageHandshakeRequestSchema,
11
- PROTOCOL_VERSION,
11
+ acceptedProtocolVersions,
12
12
  coerceErrorString,
13
+ currentProtocolVersion,
13
14
  handshakeResponseMessage
14
- } from "./chunk-3CCOX55A.js";
15
+ } from "./chunk-V57VWV5S.js";
15
16
 
16
17
  // transport/server.ts
17
18
  import { SpanStatusCode } from "@opentelemetry/api";
@@ -38,7 +39,7 @@ var ServerTransport = class extends Transport {
38
39
  };
39
40
  this.log?.info(`initiated server transport`, {
40
41
  clientId: this.clientId,
41
- protocolVersion: PROTOCOL_VERSION
42
+ protocolVersion: currentProtocolVersion
42
43
  });
43
44
  }
44
45
  extendHandshake(options) {
@@ -177,11 +178,11 @@ var ServerTransport = class extends Transport {
177
178
  return;
178
179
  }
179
180
  const gotVersion = msg.payload.protocolVersion;
180
- if (gotVersion !== PROTOCOL_VERSION) {
181
+ if (!acceptedProtocolVersions.includes(gotVersion)) {
181
182
  this.rejectHandshakeRequest(
182
183
  session,
183
184
  msg.from,
184
- `expected protocol version ${PROTOCOL_VERSION}, got ${gotVersion}`,
185
+ `expected protocol version oneof [${acceptedProtocolVersions.toString()}], got ${gotVersion}`,
185
186
  "PROTOCOL_VERSION_MISMATCH",
186
187
  {
187
188
  ...session.loggingMetadata,
@@ -326,7 +327,8 @@ var ServerTransport = class extends Transport {
326
327
  this.protocolError(ProtocolError.MessageOrderingViolated, reason);
327
328
  this.deleteSession(connectedSession);
328
329
  }
329
- }
330
+ },
331
+ gotVersion
330
332
  );
331
333
  this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
332
334
  this.updateSession(connectedSession);
@@ -379,4 +381,4 @@ var ServerTransport = class extends Transport {
379
381
  export {
380
382
  ServerTransport
381
383
  };
382
- //# sourceMappingURL=chunk-QXLXDJD5.js.map
384
+ //# sourceMappingURL=chunk-IMFMNIEO.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 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 { Session, SessionState } from './sessionStateMachine/common';\nimport { SessionStateGraph } 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 pendingSessions = new Set<SessionWaitingForHandshake<ConnType>>();\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedServerTransportOptions,\n ) {\n super(clientId, providedOptions);\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: Session<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 = SessionStateGraph.entrypoints.WaitingForHandshake(\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) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n pendingSession.loggingMetadata,\n );\n this.deletePendingSession(pendingSession);\n this.protocolError(ProtocolError.HandshakeFailed, reason);\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(ProtocolError.HandshakeFailed, reason);\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 const parsedMetadata = await this.validateHandshakeMetadata(\n session,\n oldSession,\n msg.payload.metadata,\n msg.from,\n );\n\n if (parsedMetadata === false) {\n return;\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 if (oldSession && oldSession.id === msg.payload.sessionId) {\n connectCase = 'transparent reconnection';\n\n // invariant: ordering must be correct\n const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n // TODO: remove nullish coalescing when we're sure this is always set\n const clientNextSentSeq =\n msg.payload.expectedSessionState.nextSentSeq ?? 0;\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.Connected) {\n const noConnectionSession =\n SessionStateGraph.transition.ConnectedToNoConnection(oldSession, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n\n oldSession = noConnectionSession;\n } else if (oldSession.state === SessionState.Handshaking) {\n const noConnectionSession =\n SessionStateGraph.transition.HandshakingToNoConnection(oldSession, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n });\n\n oldSession = noConnectionSession;\n } else if (oldSession.state === SessionState.Connecting) {\n const noConnectionSession =\n SessionStateGraph.transition.ConnectingToNoConnection(oldSession, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\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.deleteSession(oldSession);\n oldSession = undefined;\n } else {\n connectCase = 'unknown session';\n\n const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n // TODO: remove nullish coalescing when we're sure this is always set\n const clientNextSentSeq =\n msg.payload.expectedSessionState.nextSentSeq ?? 0;\n\n if (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 this.rejectHandshakeRequest(\n session,\n msg.from,\n `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n return;\n }\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 const responseMsg = handshakeResponseMessage({\n from: this.clientId,\n to: msg.from,\n status: {\n ok: true,\n sessionId,\n },\n });\n session.sendHandshake(responseMsg);\n\n // transition\n const connectedSession =\n SessionStateGraph.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(ProtocolError.MessageOrderingViolated, reason);\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 private async validateHandshakeMetadata(\n handshakingSession: SessionWaitingForHandshake<ConnType>,\n existingSession: Session<ConnType> | undefined,\n rawMetadata: Static<\n typeof ControlMessageHandshakeRequestSchema\n >['metadata'],\n from: TransportClientId,\n ): Promise<ParsedMetadata | false> {\n let parsedMetadata: ParsedMetadata = {};\n if (this.handshakeExtensions) {\n // check that the metadata that was sent is the correct shape\n if (!Value.Check(this.handshakeExtensions.schema, rawMetadata)) {\n this.rejectHandshakeRequest(\n handshakingSession,\n from,\n 'received malformed handshake metadata',\n 'MALFORMED_HANDSHAKE_META',\n {\n ...handshakingSession.loggingMetadata,\n connectedTo: from,\n validationErrors: [\n ...Value.Errors(this.handshakeExtensions.schema, rawMetadata),\n ],\n },\n );\n\n return false;\n }\n\n const previousParsedMetadata = existingSession\n ? this.sessionHandshakeMetadata.get(existingSession.to)\n : undefined;\n\n parsedMetadata = await this.handshakeExtensions.validate(\n rawMetadata,\n previousParsedMetadata,\n );\n\n // handler rejected the connection\n if (parsedMetadata === false) {\n this.rejectHandshakeRequest(\n handshakingSession,\n from,\n 'rejected by handshake handler',\n 'REJECTED_BY_CUSTOM_HANDLER',\n {\n ...handshakingSession.loggingMetadata,\n connectedTo: from,\n clientId: this.clientId,\n },\n );\n\n return false;\n }\n }\n\n return parsedMetadata;\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-3CCOX55A.js";
6
+ } from "./chunk-V57VWV5S.js";
7
7
  import {
8
8
  NaiveJsonCodec
9
9
  } from "./chunk-4PVU7J25.js";
@@ -125,6 +125,7 @@ var IdentifiedSession = class extends CommonSession {
125
125
  id;
126
126
  telemetry;
127
127
  to;
128
+ protocolVersion;
128
129
  /**
129
130
  * Index of the message we will send next (excluding handshake)
130
131
  */
@@ -134,7 +135,7 @@ var IdentifiedSession = class extends CommonSession {
134
135
  */
135
136
  ack;
136
137
  sendBuffer;
137
- constructor(id, from, to, seq, ack, sendBuffer, telemetry, options, log) {
138
+ constructor(id, from, to, seq, ack, sendBuffer, telemetry, options, protocolVersion, log) {
138
139
  super(from, options, log);
139
140
  this.id = id;
140
141
  this.to = to;
@@ -143,6 +144,7 @@ var IdentifiedSession = class extends CommonSession {
143
144
  this.sendBuffer = sendBuffer;
144
145
  this.telemetry = telemetry;
145
146
  this.log = log;
147
+ this.protocolVersion = protocolVersion;
146
148
  }
147
149
  get loggingMetadata() {
148
150
  const spanContext = this.telemetry.span.spanContext();
@@ -475,12 +477,13 @@ function inheritSharedSession(session) {
475
477
  session.sendBuffer,
476
478
  session.telemetry,
477
479
  session.options,
480
+ session.protocolVersion,
478
481
  session.log
479
482
  ];
480
483
  }
481
484
  var SessionStateGraph = {
482
485
  entrypoints: {
483
- NoConnection(to, from, listeners, options, log) {
486
+ NoConnection(to, from, listeners, options, protocolVersion, log) {
484
487
  const id = `session-${generateId()}`;
485
488
  const telemetry = createSessionTelemetryInfo(id, to, from);
486
489
  const sendBuffer = [];
@@ -494,6 +497,7 @@ var SessionStateGraph = {
494
497
  sendBuffer,
495
498
  telemetry,
496
499
  options,
500
+ protocolVersion,
497
501
  log
498
502
  );
499
503
  session.log?.info(`session ${session.id} created in NoConnection state`, {
@@ -565,7 +569,7 @@ var SessionStateGraph = {
565
569
  );
566
570
  return session;
567
571
  },
568
- WaitingForHandshakeToConnected(pendingSession, oldSession, sessionId, to, propagationCtx, listeners) {
572
+ WaitingForHandshakeToConnected(pendingSession, oldSession, sessionId, to, propagationCtx, listeners, protocolVersion) {
569
573
  const conn = pendingSession.conn;
570
574
  const { from, options } = pendingSession;
571
575
  const carriedState = oldSession ? (
@@ -582,6 +586,7 @@ var SessionStateGraph = {
582
586
  [],
583
587
  createSessionTelemetryInfo(sessionId, to, from, propagationCtx),
584
588
  options,
589
+ protocolVersion,
585
590
  pendingSession.log
586
591
  ]
587
592
  );
@@ -650,4 +655,4 @@ export {
650
655
  SessionState,
651
656
  SessionStateGraph
652
657
  };
653
- //# sourceMappingURL=chunk-GWYJZFCW.js.map
658
+ //# sourceMappingURL=chunk-RDGHFHXN.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/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 codec: NaiveJsonCodec,\n};\n\nexport type ClientTransportOptions = TransportOptions & ConnectionRetryOptions;\n\nexport type ProvidedClientTransportOptions = Partial<ClientTransportOptions>;\n\nconst defaultConnectionRetryOptions: ConnectionRetryOptions = {\n baseIntervalMs: 250,\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 { SessionNoConnection } from './SessionNoConnection';\nimport { SessionConnecting } from './SessionConnecting';\nimport { SessionHandshaking } from './SessionHandshaking';\nimport { SessionConnected } from './SessionConnected';\nimport { Codec } from '../../codec';\nimport { Connection } from '../connection';\nimport { generateId } from '../id';\n\nexport const enum SessionState {\n NoConnection = 'NoConnection',\n Connecting = 'Connecting',\n Handshaking = 'Handshaking',\n Connected = 'Connected',\n WaitingForHandshake = 'WaitingForHandshake',\n}\n\nexport type Session<ConnType extends Connection> =\n | SessionNoConnection\n | SessionConnecting<ConnType>\n | SessionHandshaking<ConnType>\n | SessionConnected<ConnType>;\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._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 * Duration to wait between connection disconnect and actual session disconnect\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 * 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 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(\n from: TransportClientId,\n options: SessionOptions,\n log: Logger | undefined,\n ) {\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 abstract class IdentifiedSession extends CommonSession {\n readonly id: SessionId;\n readonly telemetry: TelemetryInfo;\n readonly to: TransportClientId;\n readonly protocolVersion: string;\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(\n id: SessionId,\n from: TransportClientId,\n to: TransportClientId,\n seq: number,\n ack: number,\n sendBuffer: Array<OpaqueTransportMessage>,\n telemetry: TelemetryInfo,\n options: SessionOptions,\n protocolVersion: string,\n log: Logger | undefined,\n ) {\n super(from, options, log);\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 this.protocolVersion = protocolVersion;\n }\n\n get loggingMetadata(): MessageMetadata {\n const spanContext = this.telemetry.span.spanContext();\n\n return {\n clientId: this.from,\n connectedTo: this.to,\n sessionId: this.id,\n telemetry: {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n },\n };\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","import { Connection } from '../connection';\nimport { IdentifiedSession, SessionState } from './common';\n\nexport interface SessionConnectingListeners {\n onConnectionEstablished: (conn: Connection) => void;\n onConnectionFailed: (err: unknown) => void;\n\n // timeout related\n onConnectionTimeout: () => void;\n}\n\n/*\n * A session that is connecting but we don't have access to the raw connection\n * yet.\n *\n * Valid transitions:\n * - Connecting -> NoConnection (timeout)\n * - Connecting -> Handshaking (on connection established)\n */\nexport class SessionConnecting<\n ConnType extends Connection,\n> extends IdentifiedSession {\n readonly state = SessionState.Connecting as const;\n connPromise: Promise<ConnType>;\n listeners: SessionConnectingListeners;\n\n connectionTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(\n connPromise: Promise<ConnType>,\n listeners: SessionConnectingListeners,\n ...args: ConstructorParameters<typeof IdentifiedSession>\n ) {\n super(...args);\n this.connPromise = connPromise;\n this.listeners = listeners;\n\n this.connectionTimeout = setTimeout(() => {\n listeners.onConnectionTimeout();\n }, this.options.connectionTimeoutMs);\n\n connPromise.then(\n (conn) => {\n if (this._isConsumed) return;\n listeners.onConnectionEstablished(conn);\n },\n (err) => {\n if (this._isConsumed) return;\n 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) => conn.close())\n .catch(() => {\n // ignore errors\n });\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = undefined;\n }\n\n _handleClose(): void {\n // close the pending connection if it resolves\n this.bestEffortClose();\n super._handleClose();\n }\n}\n","import { IdentifiedSession, SessionState } from './common';\n\nexport interface SessionNoConnectionListeners {\n // timeout related\n onSessionGracePeriodElapsed: () => void;\n}\n\n/*\n * A session that is not connected and cannot send or receive messages.\n *\n * Valid transitions:\n * - NoConnection -> Connecting (on connect)\n */\nexport class SessionNoConnection extends IdentifiedSession {\n readonly state = SessionState.NoConnection as const;\n listeners: SessionNoConnectionListeners;\n\n gracePeriodTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(\n listeners: SessionNoConnectionListeners,\n ...args: ConstructorParameters<typeof IdentifiedSession>\n ) {\n super(...args);\n this.listeners = listeners;\n\n this.gracePeriodTimeout = setTimeout(() => {\n this.listeners.onSessionGracePeriodElapsed();\n }, this.options.sessionDisconnectGraceMs);\n }\n\n _handleClose(): void {\n super._handleClose();\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","import { MessageMetadata } from '../../logging';\nimport { Connection } from '../connection';\nimport { TransportMessage } from '../message';\nimport { SessionHandshakingListeners } from './SessionHandshaking';\nimport { CommonSession, SessionState } from './common';\n\n/*\n * Server-side session that has a connection but is waiting for the client to identify itself.\n *\n * Valid transitions:\n * - WaitingForHandshake -> NoConnection (on close)\n * - WaitingForHandshake -> Connected (on handshake)\n */\nexport class SessionWaitingForHandshake<\n ConnType extends Connection,\n> extends CommonSession {\n readonly state = SessionState.WaitingForHandshake as const;\n conn: ConnType;\n listeners: SessionHandshakingListeners;\n\n handshakeTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(\n conn: ConnType,\n listeners: SessionHandshakingListeners,\n ...args: ConstructorParameters<typeof CommonSession>\n ) {\n super(...args);\n this.conn = conn;\n this.listeners = listeners;\n\n this.handshakeTimeout = setTimeout(() => {\n listeners.onHandshakeTimeout();\n }, this.options.handshakeTimeoutMs);\n\n this.conn.addDataListener(this.onHandshakeData);\n this.conn.addErrorListener(listeners.onConnectionErrored);\n this.conn.addCloseListener(listeners.onConnectionClosed);\n }\n\n onHandshakeData = (msg: Uint8Array) => {\n const parsedMsg = this.parseMsg(msg);\n if (parsedMsg === null) {\n this.listeners.onInvalidHandshake('could not parse message');\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 get loggingMetadata(): MessageMetadata {\n return {\n clientId: this.from,\n connId: this.conn.id,\n };\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 { Connection } from '../connection';\nimport { OpaqueTransportMessage, TransportMessage } from '../message';\nimport { IdentifiedSession, SessionState } from './common';\n\nexport interface SessionHandshakingListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onHandshake: (msg: OpaqueTransportMessage) => void;\n onInvalidHandshake: (reason: string) => void;\n\n // timeout related\n onHandshakeTimeout: () => void;\n}\n\n/*\n * A session that is handshaking and waiting for the other side to identify itself.\n *\n * Valid transitions:\n * - Handshaking -> NoConnection (on close)\n * - Handshaking -> Connected (on handshake)\n */\nexport class SessionHandshaking<\n ConnType extends Connection,\n> extends IdentifiedSession {\n readonly state = SessionState.Handshaking as const;\n conn: ConnType;\n listeners: SessionHandshakingListeners;\n\n handshakeTimeout: ReturnType<typeof setTimeout>;\n\n constructor(\n conn: ConnType,\n listeners: SessionHandshakingListeners,\n ...args: ConstructorParameters<typeof IdentifiedSession>\n ) {\n super(...args);\n this.conn = conn;\n this.listeners = listeners;\n\n this.handshakeTimeout = setTimeout(() => {\n listeners.onHandshakeTimeout();\n }, this.options.handshakeTimeoutMs);\n\n this.conn.addDataListener(this.onHandshakeData);\n this.conn.addErrorListener(listeners.onConnectionErrored);\n this.conn.addCloseListener(listeners.onConnectionClosed);\n }\n\n onHandshakeData = (msg: Uint8Array) => {\n const parsedMsg = this.parseMsg(msg);\n if (parsedMsg === null) {\n this.listeners.onInvalidHandshake('could not parse message');\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 clearTimeout(this.handshakeTimeout);\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 { IdentifiedSession, SessionState } 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\n/*\n * A session that is connected and can send and receive messages.\n *\n * Valid transitions:\n * - Connected -> NoConnection (on close)\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 heartbeatHandle?: ReturnType<typeof setInterval> | undefined;\n heartbeatMisses = 0;\n\n get isActivelyHeartbeating() {\n return this.heartbeatHandle !== undefined;\n }\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(\n conn: ConnType,\n listeners: SessionConnectedListeners,\n ...args: ConstructorParameters<typeof IdentifiedSession>\n ) {\n super(...args);\n this.conn = conn;\n this.listeners = listeners;\n\n this.conn.addDataListener(this.onMessageData);\n this.conn.addCloseListener(listeners.onConnectionClosed);\n this.conn.addErrorListener(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 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\n startActiveHeartbeat() {\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 this.conn.close();\n clearInterval(this.heartbeatHandle);\n this.heartbeatHandle = undefined;\n return;\n }\n\n this.sendHeartbeat();\n this.heartbeatMisses++;\n }, this.options.heartbeatIntervalMs);\n }\n\n private sendHeartbeat() {\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) return;\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 { OpaqueTransportMessage, TransportClientId } from '..';\nimport {\n SessionConnecting,\n SessionConnectingListeners,\n} from './SessionConnecting';\nimport {\n SessionNoConnection,\n SessionNoConnectionListeners,\n} from './SessionNoConnection';\nimport { IdentifiedSession, SessionOptions } from './common';\nimport { PropagationContext, createSessionTelemetryInfo } from '../../tracing';\nimport { SessionWaitingForHandshake } 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';\n\nfunction inheritSharedSession(\n session: IdentifiedSession,\n): ConstructorParameters<typeof IdentifiedSession> {\n return [\n session.id,\n session.from,\n session.to,\n session.seq,\n session.ack,\n session.sendBuffer,\n session.telemetry,\n session.options,\n session.protocolVersion,\n session.log,\n ];\n}\n\n/*\n * Session state machine:\n * 1. SessionNoConnection is the client entrypoint as\n * we know who the other side is already, we just need to connect\n * 5. SessionWaitingForHandshake is the server entrypoint\n * as we have a connection but don't know who the other side is yet\n *\n * 1. SessionNoConnection ◄──┐\n * │ reconnect / connect attempt │\n * ▼ │\n * 2. SessionConnecting │\n * │ connect success ──────────────┤ connect failure\n * ▼ │\n * 3. SessionHandshaking │\n * │ handshake success ┌──────┤ connection drop\n * 5. WaitingForHandshake │ handshake failure ─────┤ │\n * │ handshake success ▼ │ │ connection drop\n * ├───────────────────────► 4. SessionConnected │ │ heartbeat misses\n * │ │ invalid message ───────┼──────┘\n * │ ▼ │\n * └───────────────────────► x. Destroy Session ◄─────┘\n * handshake failure\n */\nexport const SessionStateGraph = {\n entrypoints: {\n NoConnection(\n to: TransportClientId,\n from: TransportClientId,\n listeners: SessionNoConnectionListeners,\n options: SessionOptions,\n protocolVersion: string,\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 0,\n 0,\n sendBuffer,\n telemetry,\n options,\n protocolVersion,\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: SessionHandshakingListeners,\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 NoConnectionToConnecting<ConnType extends Connection>(\n oldSession: SessionNoConnection,\n connPromise: Promise<ConnType>,\n listeners: SessionConnectingListeners,\n ): SessionConnecting<ConnType> {\n const carriedState = inheritSharedSession(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionConnecting(\n connPromise,\n listeners,\n ...carriedState,\n );\n session.log?.info(\n `session ${session.id} transition from NoConnection 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 = inheritSharedSession(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionHandshaking(conn, listeners, ...carriedState);\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(conn, listeners, ...carriedState);\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 protocolVersion: string,\n ): SessionConnected<ConnType> {\n const conn = pendingSession.conn;\n const { from, options } = pendingSession;\n const carriedState: ConstructorParameters<typeof IdentifiedSession> =\n oldSession\n ? // old session exists, inherit state\n inheritSharedSession(oldSession)\n : // old session does not exist, create new state\n [\n sessionId,\n from,\n to,\n 0,\n 0,\n [],\n createSessionTelemetryInfo(sessionId, to, from, propagationCtx),\n options,\n protocolVersion,\n pendingSession.log,\n ];\n\n pendingSession._handleStateExit();\n oldSession?._handleStateExit();\n\n const session = new SessionConnected(conn, listeners, ...carriedState);\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 ConnectingToNoConnection<ConnType extends Connection>(\n oldSession: SessionConnecting<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection {\n const carriedState = inheritSharedSession(oldSession);\n oldSession.bestEffortClose();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection(listeners, ...carriedState);\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 = inheritSharedSession(oldSession);\n oldSession.conn.close();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection(listeners, ...carriedState);\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 oldSession.conn.close();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection(listeners, ...carriedState);\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"],"mappings":"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","names":["SessionState"]}
@@ -5,10 +5,10 @@ import {
5
5
  import {
6
6
  SessionStateGraph,
7
7
  defaultTransportOptions
8
- } from "./chunk-GWYJZFCW.js";
8
+ } from "./chunk-RDGHFHXN.js";
9
9
  import {
10
10
  generateId
11
- } from "./chunk-3CCOX55A.js";
11
+ } from "./chunk-V57VWV5S.js";
12
12
 
13
13
  // transport/events.ts
14
14
  var ProtocolError = {
@@ -274,4 +274,4 @@ export {
274
274
  Transport,
275
275
  Connection
276
276
  };
277
- //# sourceMappingURL=chunk-O4O2E6DJ.js.map
277
+ //# sourceMappingURL=chunk-SI4YHBTI.js.map
@@ -13,7 +13,7 @@ import {
13
13
  isStreamCloseRequest,
14
14
  isStreamOpen,
15
15
  requestCloseStreamMessage
16
- } from "./chunk-3CCOX55A.js";
16
+ } from "./chunk-V57VWV5S.js";
17
17
 
18
18
  // router/services.ts
19
19
  import { Type } from "@sinclair/typebox";
@@ -982,7 +982,8 @@ var RiverServer = class {
982
982
  initPayload,
983
983
  from,
984
984
  sessionId,
985
- tracingCtx
985
+ tracingCtx,
986
+ protocolVersion
986
987
  }) {
987
988
  this.openStreams.add(streamId);
988
989
  let cleanClose = true;
@@ -1045,7 +1046,7 @@ var RiverServer = class {
1045
1046
  if (isStreamCloseRequest(msg.controlFlags)) {
1046
1047
  outputWriter.triggerCloseRequest();
1047
1048
  }
1048
- if (isStreamAbort(msg.controlFlags)) {
1049
+ if (isStreamAbortBackwardsCompat(msg.controlFlags, protocolVersion)) {
1049
1050
  let abortResult;
1050
1051
  if (Value2.Check(InputErrResultSchema, msg.payload)) {
1051
1052
  abortResult = msg.payload;
@@ -1112,7 +1113,7 @@ var RiverServer = class {
1112
1113
  })
1113
1114
  );
1114
1115
  }
1115
- if (isStreamClose(msg.controlFlags)) {
1116
+ if (isStreamCloseBackwardsCompat(msg.controlFlags, protocolVersion)) {
1116
1117
  inputReader.triggerClose();
1117
1118
  }
1118
1119
  };
@@ -1131,26 +1132,38 @@ var RiverServer = class {
1131
1132
  });
1132
1133
  onFinishedCallbacks.length = 0;
1133
1134
  };
1135
+ const procClosesWithResponse = procedure.type === "rpc" || procedure.type === "upload";
1134
1136
  const inputReader = new ReadStreamImpl(() => {
1135
1137
  this.transport.send(from, requestCloseStreamMessage(streamId));
1136
1138
  });
1137
1139
  inputReader.onClose(() => {
1140
+ if (protocolVersion === "v1.1") {
1141
+ if (!procClosesWithResponse && !outputWriter.isClosed()) {
1142
+ outputWriter.close();
1143
+ }
1144
+ }
1138
1145
  if (outputWriter.isClosed()) {
1139
1146
  cleanup();
1140
1147
  }
1141
1148
  });
1142
- const procClosesWithResponse = procedure.type === "rpc" || procedure.type === "upload";
1143
1149
  const outputWriter = new WriteStreamImpl((response) => {
1144
1150
  this.transport.send(from, {
1145
1151
  streamId,
1146
- controlFlags: procClosesWithResponse ? 8 /* StreamClosedBit */ : 0,
1152
+ controlFlags: procClosesWithResponse ? getStreamCloseBackwardsCompat(protocolVersion) : 0,
1147
1153
  payload: response
1148
1154
  });
1149
1155
  });
1150
1156
  outputWriter.onClose(() => {
1151
1157
  if (!procClosesWithResponse && cleanClose) {
1158
+ const message = closeStreamMessage(streamId);
1159
+ message.controlFlags = getStreamCloseBackwardsCompat(protocolVersion);
1152
1160
  this.transport.send(from, closeStreamMessage(streamId));
1153
1161
  }
1162
+ if (protocolVersion === "v1.1") {
1163
+ if (!inputReader.isClosed()) {
1164
+ inputReader.triggerClose();
1165
+ }
1166
+ }
1154
1167
  if (inputReader.isClosed()) {
1155
1168
  cleanup();
1156
1169
  }
@@ -1166,7 +1179,7 @@ var RiverServer = class {
1166
1179
  })
1167
1180
  );
1168
1181
  };
1169
- if (isStreamClose(controlFlags)) {
1182
+ if (isStreamCloseBackwardsCompat(controlFlags, protocolVersion)) {
1170
1183
  inputReader.triggerClose();
1171
1184
  } else if (procedure.type === "rpc" || procedure.type === "subscription") {
1172
1185
  this.log?.warn(`${procedure.type} sent an init without a stream close`, {
@@ -1477,7 +1490,8 @@ var RiverServer = class {
1477
1490
  tracingCtx: initMessage.tracing,
1478
1491
  initPayload: initMessage.payload,
1479
1492
  from: initMessage.from,
1480
- sessionId: session.id
1493
+ sessionId: session.id,
1494
+ protocolVersion: session.protocolVersion
1481
1495
  };
1482
1496
  }
1483
1497
  abortStream(to, streamId, payload) {
@@ -1487,7 +1501,11 @@ var RiverServer = class {
1487
1501
  this.serverAbortedStreams.set(to, abortedForSession);
1488
1502
  }
1489
1503
  abortedForSession.add(streamId);
1490
- this.transport.send(to, abortMessage(streamId, payload));
1504
+ this.transport.send(
1505
+ to,
1506
+ // TODO remove once clients migrate to v2
1507
+ this.transport.sessions.get(to)?.protocolVersion === "v1.1" ? closeStreamMessage(streamId) : abortMessage(streamId, payload)
1508
+ );
1491
1509
  }
1492
1510
  };
1493
1511
  var LRUSet = class {
@@ -1512,6 +1530,24 @@ var LRUSet = class {
1512
1530
  return this.items.has(item);
1513
1531
  }
1514
1532
  };
1533
+ function isStreamAbortBackwardsCompat(controlFlags, protocolVersion) {
1534
+ if (protocolVersion === "v1.1") {
1535
+ return false;
1536
+ }
1537
+ return isStreamAbort(controlFlags);
1538
+ }
1539
+ function isStreamCloseBackwardsCompat(controlFlags, protocolVersion) {
1540
+ if (protocolVersion === "v1.1") {
1541
+ return isStreamAbort(controlFlags);
1542
+ }
1543
+ return isStreamClose(controlFlags);
1544
+ }
1545
+ function getStreamCloseBackwardsCompat(protocolVersion) {
1546
+ if (protocolVersion === "v1.1") {
1547
+ return 4 /* StreamAbortBit */;
1548
+ }
1549
+ return 8 /* StreamClosedBit */;
1550
+ }
1515
1551
  function createServer(transport, services, providedServerOptions) {
1516
1552
  return new RiverServer(
1517
1553
  transport,
@@ -1548,4 +1584,4 @@ export {
1548
1584
  createClientHandshakeOptions,
1549
1585
  createServerHandshakeOptions
1550
1586
  };
1551
- //# sourceMappingURL=chunk-UIYGPURD.js.map
1587
+ //# sourceMappingURL=chunk-SIRRYRLQ.js.map