@replit/river 0.217.1 → 0.218.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/{chunk-JFFRB3SS.js → chunk-2HK3WK7W.js} +55 -8
- package/dist/chunk-2HK3WK7W.js.map +1 -0
- package/dist/{chunk-VK3VJZGG.js → chunk-PWNG6VBS.js} +313 -10
- package/dist/chunk-PWNG6VBS.js.map +1 -0
- package/dist/{client-YP9bECp8.d.ts → client-B35XYDNc.d.ts} +22 -1
- package/dist/codec/index.js +2 -2
- package/dist/{connection-D_HE_YQB.d.ts → connection-bbb8EEFF.d.ts} +1 -1
- package/dist/protobuf/index.d.ts +8 -8
- package/dist/protobuf/index.js +20 -8
- package/dist/protobuf/index.js.map +1 -1
- package/dist/router/index.d.ts +7 -7
- package/dist/router/index.js +1 -1
- package/dist/{server-BfM3_JLq.d.ts → server-B-s_HVtb.d.ts} +35 -1
- package/dist/{services-CL6k3HMH.d.ts → services-CVMD2iBf.d.ts} +2 -2
- package/dist/testUtil/index.d.ts +4 -4
- package/dist/testUtil/index.js +2 -2
- package/dist/transport/impls/ws/client.d.ts +3 -3
- package/dist/transport/impls/ws/client.js +2 -2
- package/dist/transport/impls/ws/server.d.ts +3 -3
- package/dist/transport/impls/ws/server.js +2 -2
- package/dist/transport/index.d.ts +4 -4
- package/dist/transport/index.js +2 -2
- package/dist/{transport-CUpXnch7.d.ts → transport-WUuMJkX5.d.ts} +60 -10
- package/package.json +1 -1
- package/dist/chunk-JFFRB3SS.js.map +0 -1
- package/dist/chunk-VK3VJZGG.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../transport/events.ts","../codec/json.ts","../transport/options.ts","../transport/sessionStateMachine/common.ts","../transport/sessionStateMachine/SessionConnecting.ts","../transport/sessionStateMachine/SessionNoConnection.ts","../transport/sessionStateMachine/SessionWaitingForHandshake.ts","../transport/sessionStateMachine/SessionHandshaking.ts","../transport/sessionStateMachine/SessionConnected.ts","../transport/sessionStateMachine/SessionBackingOff.ts","../codec/binary.ts","../codec/adapter.ts","../transport/sessionStateMachine/transitions.ts","../transport/transport.ts","../transport/client.ts","../transport/rateLimit.ts","../transport/server.ts","../transport/connection.ts","../transport/impls/ws/connection.ts"],"sourcesContent":["import type { Static } from 'typebox';\nimport { Connection } from './connection';\nimport { OpaqueTransportMessage, HandshakeErrorResponseCodes } from './message';\nimport { Session, SessionState } from './sessionStateMachine';\nimport { SessionId } from './sessionStateMachine/common';\nimport { TransportStatus } from './transport';\n\nexport const ProtocolError = {\n RetriesExceeded: 'conn_retry_exceeded',\n HandshakeFailed: 'handshake_failed',\n MessageOrderingViolated: 'message_ordering_violated',\n InvalidMessage: 'invalid_message',\n MessageSendFailure: 'message_send_failure',\n} as const;\n\nexport type ProtocolErrorType =\n (typeof ProtocolError)[keyof typeof ProtocolError];\n\nexport interface EventMap {\n message: OpaqueTransportMessage;\n sessionStatus:\n | {\n status: 'created' | 'closing';\n session: Session<Connection>;\n }\n | {\n status: 'closed';\n session: Pick<Session<Connection>, 'id' | 'to'>;\n };\n sessionTransition:\n | { state: SessionState.Connected; id: SessionId }\n | { state: SessionState.Handshaking; id: SessionId }\n | { state: SessionState.Connecting; id: SessionId }\n | { state: SessionState.BackingOff; id: SessionId }\n | { state: SessionState.NoConnection; id: SessionId };\n protocolError:\n | {\n type: (typeof ProtocolError)['HandshakeFailed'];\n code: Static<typeof HandshakeErrorResponseCodes>;\n message: string;\n }\n | {\n type: Omit<\n ProtocolErrorType,\n (typeof ProtocolError)['HandshakeFailed']\n >;\n message: string;\n };\n transportStatus: {\n status: TransportStatus;\n };\n}\n\nexport type EventTypes = keyof EventMap;\nexport type EventHandler<K extends EventTypes> = (\n event: EventMap[K],\n) => unknown;\n\nexport class EventDispatcher<T extends EventTypes> {\n private eventListeners: { [K in T]?: Set<EventHandler<K>> } = {};\n\n removeAllListeners() {\n this.eventListeners = {};\n }\n\n numberOfListeners<K extends T>(eventType: K) {\n return this.eventListeners[eventType]?.size ?? 0;\n }\n\n addEventListener<K extends T>(eventType: K, handler: EventHandler<K>) {\n if (!this.eventListeners[eventType]) {\n this.eventListeners[eventType] = new Set();\n }\n\n this.eventListeners[eventType]?.add(handler);\n }\n\n removeEventListener<K extends T>(eventType: K, handler: EventHandler<K>) {\n const handlers = this.eventListeners[eventType];\n if (handlers) {\n this.eventListeners[eventType]?.delete(handler);\n }\n }\n\n dispatchEvent<K extends T>(eventType: K, event: EventMap[K]) {\n const handlers = this.eventListeners[eventType];\n if (handlers) {\n // copying ensures that adding more listeners in a handler doesn't\n // affect the current dispatch.\n const copy = [...handlers];\n for (const handler of copy) {\n handler(event);\n }\n }\n }\n}\n","import { Codec } from './types';\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n// Convert Uint8Array to base64\nfunction uint8ArrayToBase64(uint8Array: Uint8Array) {\n let binary = '';\n uint8Array.forEach((byte) => {\n binary += String.fromCharCode(byte);\n });\n\n return btoa(binary);\n}\n\n// Convert base64 to Uint8Array\nfunction base64ToUint8Array(base64: string) {\n const binaryString = atob(base64);\n const uint8Array = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n uint8Array[i] = binaryString.charCodeAt(i);\n }\n\n return uint8Array;\n}\n\ninterface Base64EncodedValue {\n $t: string;\n}\n\ninterface BigIntEncodedValue {\n $b: string;\n}\n\n/**\n * Naive JSON codec implementation using JSON.stringify and JSON.parse.\n * @type {Codec}\n */\nexport const NaiveJsonCodec: Codec = {\n toBuffer: (obj: object) => {\n return encoder.encode(\n JSON.stringify(obj, function replacer<\n T extends object,\n >(this: T, key: keyof T) {\n const val = this[key];\n if (val instanceof Uint8Array) {\n return { $t: uint8ArrayToBase64(val) } satisfies Base64EncodedValue;\n } else if (typeof val === 'bigint') {\n return { $b: val.toString() } satisfies BigIntEncodedValue;\n } else {\n return val;\n }\n }),\n );\n },\n fromBuffer: (buff: Uint8Array) => {\n const parsed = JSON.parse(\n decoder.decode(buff),\n function reviver(_key, val: unknown) {\n if ((val as Base64EncodedValue | undefined)?.$t !== undefined) {\n return base64ToUint8Array((val as Base64EncodedValue).$t);\n } else if ((val as BigIntEncodedValue | undefined)?.$b !== undefined) {\n return BigInt((val as BigIntEncodedValue).$b);\n } else {\n return val;\n }\n },\n ) as unknown;\n if (typeof parsed !== 'object' || parsed === null) {\n throw new Error('unpacked msg is not an object');\n }\n\n return parsed;\n },\n};\n","import { NaiveJsonCodec } from '../codec/json';\nimport { ConnectionRetryOptions } from './rateLimit';\nimport { SessionOptions } from './sessionStateMachine/common';\n\nexport type TransportOptions = SessionOptions;\n\nexport type ProvidedTransportOptions = Partial<TransportOptions>;\n\nexport const defaultTransportOptions: TransportOptions = {\n heartbeatIntervalMs: 1_000,\n heartbeatsUntilDead: 2,\n sessionDisconnectGraceMs: 5_000,\n connectionTimeoutMs: 2_000,\n handshakeTimeoutMs: 1_000,\n enableTransparentSessionReconnects: true,\n codec: NaiveJsonCodec,\n};\n\nexport type ClientTransportOptions = TransportOptions & ConnectionRetryOptions;\n\nexport type ProvidedClientTransportOptions = Partial<ClientTransportOptions>;\n\nconst defaultConnectionRetryOptions: ConnectionRetryOptions = {\n baseIntervalMs: 150,\n maxJitterMs: 200,\n maxBackoffMs: 32_000,\n attemptBudgetCapacity: 5,\n budgetRestoreIntervalMs: 200,\n isFatalConnectionError: () => false,\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 EncodedTransportMessage,\n PartialTransportMessage,\n ProtocolVersion,\n TransportClientId,\n} from '../message';\nimport { Codec, CodecMessageAdapter } from '../../codec';\nimport { generateId } from '../id';\nimport { Tracer } from '@opentelemetry/api';\nimport { EncodeResult, SendResult } from '../results';\n\nexport const enum SessionState {\n NoConnection = 'NoConnection',\n BackingOff = 'BackingOff',\n Connecting = 'Connecting',\n Handshaking = 'Handshaking',\n Connected = 'Connected',\n WaitingForHandshake = 'WaitingForHandshake',\n}\n\nexport const ERR_CONSUMED = `session state has been consumed and is no longer valid`;\n\nabstract class StateMachineState {\n abstract readonly state: SessionState;\n\n /*\n * Whether this state has been consumed\n * and we've moved on to another state\n */\n _isConsumed: boolean;\n\n // called when we're transitioning to another state\n // note that this is internal and should not be called directly\n // by consumers, the proxy will call this when the state is consumed\n // and we're transitioning to another state\n abstract _handleStateExit(): void;\n\n // called when we exit the state machine entirely\n // note that this is internal and should not be called directly\n // by consumers, the proxy will call this when .close is closed\n abstract _handleClose(): void;\n\n /**\n * Cleanup this state machine state and mark it as consumed.\n * After calling close, it is an error to access any properties on the state.\n * You should never need to call this as a consumer.\n *\n * If you're looking to close the session from the client,\n * use `.hardDisconnect` on the client transport.\n */\n close(): void {\n this._handleClose();\n }\n\n constructor() {\n this._isConsumed = false;\n\n // proxy helps us prevent access to properties after the state has been consumed\n // e.g. if we hold a reference to a state and try to access it after it's been consumed\n // we intercept the access and throw an error to help catch bugs\n return new Proxy(this, {\n get(target, prop) {\n // always allow access to _isConsumed, id, and state\n if (prop === '_isConsumed' || prop === 'id' || prop === 'state') {\n return Reflect.get(target, prop);\n }\n\n // modify _handleStateExit\n if (prop === '_handleStateExit') {\n return () => {\n target._isConsumed = true;\n target._handleStateExit();\n };\n }\n\n // modify _handleClose\n if (prop === '_handleClose') {\n return () => {\n // target is the non-proxied object, we need to set _isConsumed again\n target._isConsumed = true;\n target._handleStateExit();\n target._handleClose();\n };\n }\n\n if (target._isConsumed) {\n throw new Error(\n `${ERR_CONSUMED}: getting ${prop.toString()} on consumed state`,\n );\n }\n\n return Reflect.get(target, prop);\n },\n set(target, prop, value) {\n if (target._isConsumed) {\n throw new Error(\n `${ERR_CONSUMED}: setting ${prop.toString()} on consumed state`,\n );\n }\n\n return Reflect.set(target, prop, value);\n },\n });\n }\n}\n\nexport interface SessionOptions {\n /**\n * Frequency at which to send heartbeat acknowledgements\n */\n heartbeatIntervalMs: number;\n /**\n * Number of elapsed heartbeats without a response message before we consider\n * the connection dead.\n */\n heartbeatsUntilDead: number;\n /**\n * Max duration that a session can be without a connection before we consider\n * it dead. This deadline is carried between states and is used to determine\n * when to consider the session a lost cause and delete it entirely.\n * Generally, this should be strictly greater than the sum of\n * {@link connectionTimeoutMs} and {@link handshakeTimeoutMs}.\n */\n sessionDisconnectGraceMs: number;\n /**\n * Connection timeout in milliseconds\n */\n connectionTimeoutMs: number;\n /**\n * Handshake timeout in milliseconds\n */\n handshakeTimeoutMs: number;\n /**\n * Whether to enable transparent session reconnects\n */\n enableTransparentSessionReconnects: boolean;\n /**\n * The codec to use for encoding/decoding messages over the wire\n */\n codec: Codec;\n}\n\n// all session states have a from and options\nexport interface CommonSessionProps {\n from: TransportClientId;\n options: SessionOptions;\n codec: CodecMessageAdapter;\n tracer: Tracer;\n log: Logger | undefined;\n}\n\nexport abstract class CommonSession extends StateMachineState {\n readonly from: TransportClientId;\n readonly options: SessionOptions;\n\n readonly codec: CodecMessageAdapter;\n tracer: Tracer;\n log?: Logger;\n abstract get loggingMetadata(): MessageMetadata;\n\n constructor({ from, options, log, tracer, codec }: CommonSessionProps) {\n super();\n this.from = from;\n this.options = options;\n this.log = log;\n this.tracer = tracer;\n this.codec = codec;\n }\n}\n\nexport type InheritedProperties = Pick<\n IdentifiedSession,\n | 'id'\n | 'from'\n | 'to'\n | 'seq'\n | 'ack'\n | 'seqSent'\n | 'sendBuffer'\n | 'telemetry'\n | 'options'\n>;\n\nexport type SessionId = string;\n\nexport interface IdentifiedSessionListeners {\n onMessageSendFailure: (\n msg: PartialTransportMessage & { seq: number },\n reason: string,\n ) => void;\n}\n\n// all sessions where we know the other side's client id\nexport interface IdentifiedSessionProps extends CommonSessionProps {\n id: SessionId;\n to: TransportClientId;\n seq: number;\n ack: number;\n seqSent: number;\n sendBuffer: Array<EncodedTransportMessage>;\n telemetry: TelemetryInfo;\n protocolVersion: ProtocolVersion;\n listeners: IdentifiedSessionListeners;\n}\n\nexport abstract class IdentifiedSession extends CommonSession {\n readonly id: SessionId;\n readonly telemetry: TelemetryInfo;\n readonly to: TransportClientId;\n readonly protocolVersion: ProtocolVersion;\n listeners: IdentifiedSessionListeners;\n\n /**\n * Index of the message we will send next (excluding handshake)\n */\n seq: number;\n\n /**\n * Last seq we sent over the wire this session (excluding handshake) and retransmissions\n */\n seqSent: number;\n\n /**\n * Number of unique messages we've received this session (excluding handshake)\n */\n ack: number;\n sendBuffer: Array<EncodedTransportMessage>;\n\n constructor(props: IdentifiedSessionProps) {\n const {\n id,\n to,\n seq,\n ack,\n sendBuffer,\n telemetry,\n log,\n protocolVersion,\n seqSent: messagesSent,\n listeners,\n } = props;\n super(props);\n this.id = id;\n this.to = to;\n this.seq = seq;\n this.ack = ack;\n this.sendBuffer = sendBuffer;\n this.telemetry = telemetry;\n this.log = log;\n this.protocolVersion = protocolVersion;\n this.seqSent = messagesSent;\n this.listeners = listeners;\n }\n\n get loggingMetadata(): MessageMetadata {\n const metadata: MessageMetadata = {\n clientId: this.from,\n connectedTo: this.to,\n sessionId: this.id,\n };\n\n if (this.telemetry.span.isRecording()) {\n const spanContext = this.telemetry.span.spanContext();\n metadata.telemetry = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n };\n }\n\n return metadata;\n }\n\n encodeMsg(partialMsg: PartialTransportMessage): EncodeResult {\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 const encoded = this.codec.toBuffer(msg);\n if (!encoded.ok) {\n // safety: onMessageSendFailure tears down the session via protocol error,\n // which emits sessionStatus 'closing' and cleans up all procedure listeners.\n this.listeners.onMessageSendFailure(\n { ...partialMsg, seq: this.seq },\n encoded.reason,\n );\n\n return encoded;\n }\n\n this.seq++;\n\n return {\n ok: true,\n value: {\n id: msg.id,\n seq: msg.seq,\n msg: partialMsg,\n data: encoded.value,\n },\n };\n }\n\n nextSeq(): number {\n return this.sendBuffer.length > 0 ? this.sendBuffer[0].seq : this.seq;\n }\n\n send(msg: PartialTransportMessage): SendResult {\n const encodeResult = this.encodeMsg(msg);\n if (!encodeResult.ok) {\n return encodeResult;\n }\n\n this.sendBuffer.push(encodeResult.value);\n\n return {\n ok: true,\n value: encodeResult.value.id,\n };\n }\n\n _handleStateExit(): void {\n // noop\n }\n\n _handleClose(): void {\n // zero out the buffer\n this.sendBuffer.length = 0;\n this.telemetry.span.end();\n }\n}\n\nexport interface IdentifiedSessionWithGracePeriodListeners\n extends IdentifiedSessionListeners {\n onSessionGracePeriodElapsed: () => void;\n}\n\nexport interface IdentifiedSessionWithGracePeriodProps\n extends IdentifiedSessionProps {\n graceExpiryTime: number;\n listeners: IdentifiedSessionWithGracePeriodListeners;\n}\n\nexport abstract class IdentifiedSessionWithGracePeriod extends IdentifiedSession {\n graceExpiryTime: number;\n protected gracePeriodTimeout?: ReturnType<typeof setTimeout>;\n\n listeners: IdentifiedSessionWithGracePeriodListeners;\n\n constructor(props: IdentifiedSessionWithGracePeriodProps) {\n super(props);\n this.listeners = props.listeners;\n\n this.graceExpiryTime = props.graceExpiryTime;\n this.gracePeriodTimeout = setTimeout(() => {\n this.listeners.onSessionGracePeriodElapsed();\n }, this.graceExpiryTime - Date.now());\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n\n if (this.gracePeriodTimeout) {\n clearTimeout(this.gracePeriodTimeout);\n this.gracePeriodTimeout = undefined;\n }\n }\n\n _handleClose(): void {\n super._handleClose();\n }\n}\n","import { Connection } from '../connection';\nimport {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport interface SessionConnectingListeners\n extends IdentifiedSessionWithGracePeriodListeners {\n onConnectionEstablished: (conn: Connection) => void;\n onConnectionFailed: (err: unknown) => void;\n\n // timeout related\n onConnectionTimeout: () => void;\n}\n\nexport interface SessionConnectingProps<ConnType extends Connection>\n extends IdentifiedSessionWithGracePeriodProps {\n connPromise: Promise<ConnType>;\n listeners: SessionConnectingListeners;\n}\n\n/*\n * A session that is connecting but we don't have access to the raw connection yet.\n * See transitions.ts for valid transitions.\n */\nexport class SessionConnecting<\n ConnType extends Connection,\n> extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.Connecting as const;\n connPromise: Promise<ConnType>;\n listeners: SessionConnectingListeners;\n\n connectionTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionConnectingProps<ConnType>) {\n super(props);\n this.connPromise = props.connPromise;\n this.listeners = props.listeners;\n\n this.connPromise.then(\n (conn) => {\n if (this._isConsumed) return;\n this.listeners.onConnectionEstablished(conn);\n },\n (err) => {\n if (this._isConsumed) return;\n this.listeners.onConnectionFailed(err);\n },\n );\n\n this.connectionTimeout = setTimeout(() => {\n this.listeners.onConnectionTimeout();\n }, this.options.connectionTimeoutMs);\n }\n\n // close a pending connection if it resolves, ignore errors if the promise\n // ends up rejected anyways\n bestEffortClose() {\n // these can technically be stale if the connPromise resolves after the\n // state has transitioned, but that's fine, this is best effort anyways\n // we pull these out so even if the state has transitioned, we can still log\n // without erroring out\n const logger = this.log;\n const metadata = this.loggingMetadata;\n\n this.connPromise\n .then((conn) => {\n conn.close();\n logger?.info(\n 'connection eventually resolved but session has transitioned, closed connection',\n {\n ...metadata,\n ...conn.loggingMetadata,\n },\n );\n })\n .catch(() => {\n // ignore errors\n });\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n if (this.connectionTimeout) {\n clearTimeout(this.connectionTimeout);\n this.connectionTimeout = undefined;\n }\n }\n\n _handleClose(): void {\n super._handleClose();\n\n // close the pending connection if it resolves\n this.bestEffortClose();\n }\n}\n","import {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport type SessionNoConnectionListeners =\n IdentifiedSessionWithGracePeriodListeners;\n\nexport type SessionNoConnectionProps = IdentifiedSessionWithGracePeriodProps;\n\n/*\n * A session that is not connected and cannot send or receive messages.\n * See transitions.ts for valid transitions.\n */\nexport class SessionNoConnection extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.NoConnection as const;\n\n _handleClose(): void {\n super._handleClose();\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n }\n}\n","import type { Static } from 'typebox';\nimport { Connection } from '../connection';\nimport {\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n TransportMessage,\n} from '../message';\nimport { CommonSession, CommonSessionProps, SessionState } from './common';\nimport { SendResult } from '../results';\n\nexport interface SessionWaitingForHandshakeListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onHandshake: (msg: OpaqueTransportMessage) => void;\n onInvalidHandshake: (\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n ) => void;\n\n // timeout related\n onHandshakeTimeout: () => void;\n}\n\nexport interface SessionWaitingForHandshakeProps<ConnType extends Connection>\n extends CommonSessionProps {\n conn: ConnType;\n listeners: SessionWaitingForHandshakeListeners;\n}\n\n/*\n * Server-side session that has a connection but is waiting for the client to identify itself.\n * See transitions.ts for valid transitions.\n */\nexport class SessionWaitingForHandshake<\n ConnType extends Connection,\n> extends CommonSession {\n readonly state = SessionState.WaitingForHandshake as const;\n conn: ConnType;\n listeners: SessionWaitingForHandshakeListeners;\n\n handshakeTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionWaitingForHandshakeProps<ConnType>) {\n super(props);\n this.conn = props.conn;\n this.listeners = props.listeners;\n\n this.handshakeTimeout = setTimeout(() => {\n this.listeners.onHandshakeTimeout();\n }, this.options.handshakeTimeoutMs);\n\n this.conn.setDataListener(this.onHandshakeData);\n this.conn.setErrorListener(this.listeners.onConnectionErrored);\n this.conn.setCloseListener(this.listeners.onConnectionClosed);\n }\n\n get loggingMetadata() {\n return {\n clientId: this.from,\n connId: this.conn.id,\n ...this.conn.loggingMetadata,\n };\n }\n\n onHandshakeData = (msg: Uint8Array) => {\n const parsedMsgRes = this.codec.fromBuffer(msg);\n if (!parsedMsgRes.ok) {\n this.listeners.onInvalidHandshake(\n `could not parse handshake message: ${parsedMsgRes.reason}`,\n 'MALFORMED_HANDSHAKE',\n );\n\n return;\n }\n\n // after this fires, the listener is responsible for transitioning the session\n // and thus removing the handshake timeout\n this.listeners.onHandshake(parsedMsgRes.value);\n };\n\n sendHandshake(msg: TransportMessage): SendResult {\n const buff = this.codec.toBuffer(msg);\n if (!buff.ok) {\n return buff;\n }\n\n const sent = this.conn.send(buff.value);\n if (!sent) {\n return {\n ok: false,\n reason: 'failed to send handshake',\n };\n }\n\n return {\n ok: true,\n value: msg.id,\n };\n }\n\n _handleStateExit(): void {\n this.conn.removeDataListener();\n this.conn.removeErrorListener();\n this.conn.removeCloseListener();\n clearTimeout(this.handshakeTimeout);\n this.handshakeTimeout = undefined;\n }\n\n _handleClose(): void {\n this.conn.close();\n }\n}\n","import type { Static } from 'typebox';\nimport { Connection } from '../connection';\nimport {\n OpaqueTransportMessage,\n TransportMessage,\n HandshakeErrorResponseCodes,\n} from '../message';\nimport {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\nimport { SendResult } from '../results';\n\nexport interface SessionHandshakingListeners\n extends IdentifiedSessionWithGracePeriodListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onHandshake: (msg: OpaqueTransportMessage) => void;\n onInvalidHandshake: (\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n ) => void;\n\n // timeout related\n onHandshakeTimeout: () => void;\n}\n\nexport interface SessionHandshakingProps<ConnType extends Connection>\n extends IdentifiedSessionWithGracePeriodProps {\n conn: ConnType;\n listeners: SessionHandshakingListeners;\n}\n\n/*\n * A session that is handshaking and waiting for the other side to identify itself.\n * See transitions.ts for valid transitions.\n */\nexport class SessionHandshaking<\n ConnType extends Connection,\n> extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.Handshaking as const;\n conn: ConnType;\n listeners: SessionHandshakingListeners;\n\n handshakeTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionHandshakingProps<ConnType>) {\n super(props);\n this.conn = props.conn;\n this.listeners = props.listeners;\n\n this.handshakeTimeout = setTimeout(() => {\n this.listeners.onHandshakeTimeout();\n }, this.options.handshakeTimeoutMs);\n\n this.conn.setDataListener(this.onHandshakeData);\n this.conn.setErrorListener(this.listeners.onConnectionErrored);\n this.conn.setCloseListener(this.listeners.onConnectionClosed);\n }\n\n get loggingMetadata() {\n return {\n ...super.loggingMetadata,\n ...this.conn.loggingMetadata,\n };\n }\n\n onHandshakeData = (msg: Uint8Array) => {\n const parsedMsgRes = this.codec.fromBuffer(msg);\n if (!parsedMsgRes.ok) {\n this.listeners.onInvalidHandshake(\n `could not parse handshake message: ${parsedMsgRes.reason}`,\n 'MALFORMED_HANDSHAKE',\n );\n\n return;\n }\n\n this.listeners.onHandshake(parsedMsgRes.value);\n };\n\n sendHandshake(msg: TransportMessage): SendResult {\n const buff = this.codec.toBuffer(msg);\n if (!buff.ok) {\n return buff;\n }\n\n const sent = this.conn.send(buff.value);\n if (!sent) {\n return {\n ok: false,\n reason: 'failed to send handshake',\n };\n }\n\n return {\n ok: true,\n value: msg.id,\n };\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n this.conn.removeDataListener();\n this.conn.removeErrorListener();\n this.conn.removeCloseListener();\n\n if (this.handshakeTimeout) {\n clearTimeout(this.handshakeTimeout);\n this.handshakeTimeout = undefined;\n }\n }\n\n _handleClose(): void {\n super._handleClose();\n this.conn.close();\n }\n}\n","import type { Static } from 'typebox';\nimport {\n ControlFlags,\n ControlMessageAckSchema,\n EncodedTransportMessage,\n OpaqueTransportMessage,\n PartialTransportMessage,\n RehandshakeStreamId,\n rehandshakeRequestMessage,\n isAck,\n} from '../message';\nimport {\n IdentifiedSession,\n IdentifiedSessionListeners,\n IdentifiedSessionProps,\n SessionState,\n} from './common';\nimport { Connection } from '../connection';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { SendBufferResult, SendResult } from '../results';\n\nexport interface SessionConnectedListeners extends IdentifiedSessionListeners {\n onConnectionErrored: (err: unknown) => void;\n onConnectionClosed: () => void;\n onMessage: (msg: OpaqueTransportMessage) => void;\n /**\n * A frame arrived on the reserved re-handshake stream. The transport consumes\n * it to drive the follow-up handshake rather than surfacing it to the router.\n */\n onRehandshake: (msg: OpaqueTransportMessage) => void;\n /**\n * A scheduled re-handshake went unanswered within its deadline. Only the server\n * arms this (via {@link SessionConnected.scheduleRehandshake}); it tears the\n * session down rather than keep serving a credential past its expiry.\n */\n onRehandshakeTimeout?: () => void;\n onInvalidMessage: (reason: string) => void;\n}\n\nexport interface SessionConnectedProps<ConnType extends Connection>\n extends IdentifiedSessionProps {\n conn: ConnType;\n listeners: SessionConnectedListeners;\n}\n\n/*\n * A session that is connected and can send and receive messages.\n * See transitions.ts for valid transitions.\n */\nexport class SessionConnected<\n ConnType extends Connection,\n> extends IdentifiedSession {\n readonly state = SessionState.Connected as const;\n conn: ConnType;\n listeners: SessionConnectedListeners;\n\n private heartbeatHandle?: ReturnType<typeof setInterval> | undefined;\n private heartbeatMissTimeout?: ReturnType<typeof setTimeout> | undefined;\n private isActivelyHeartbeating = false;\n private rehandshakeTimer?: ReturnType<typeof setTimeout> | undefined;\n private credentialExpiry?: number | undefined;\n\n updateBookkeeping(ack: number, seq: number) {\n this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);\n this.ack = seq + 1;\n\n if (this.heartbeatMissTimeout) {\n clearTimeout(this.heartbeatMissTimeout);\n }\n\n this.startMissingHeartbeatTimeout();\n }\n\n private assertSendOrdering(encodedMsg: EncodedTransportMessage) {\n if (encodedMsg.seq > this.seqSent + 1) {\n const msg = `invariant violation: would have sent out of order msg (seq: ${encodedMsg.seq}, expected: ${this.seqSent} + 1)`;\n this.log?.error(msg, {\n ...this.loggingMetadata,\n tags: ['invariant-violation'],\n });\n\n throw new Error(msg);\n }\n }\n\n send(msg: PartialTransportMessage): SendResult {\n const encodeResult = this.encodeMsg(msg);\n if (!encodeResult.ok) {\n return encodeResult;\n }\n\n const encodedMsg = encodeResult.value;\n this.assertSendOrdering(encodedMsg);\n this.sendBuffer.push(encodedMsg);\n\n const sent = this.conn.send(encodedMsg.data);\n if (!sent) {\n const reason = 'failed to send message';\n this.listeners.onMessageSendFailure(\n { ...encodedMsg.msg, seq: encodedMsg.seq },\n reason,\n );\n\n return { ok: false, reason };\n }\n\n this.seqSent = encodedMsg.seq;\n\n return { ok: true, value: encodedMsg.id };\n }\n\n constructor(props: SessionConnectedProps<ConnType>) {\n super(props);\n this.conn = props.conn;\n this.listeners = props.listeners;\n\n this.conn.setDataListener(this.onMessageData);\n this.conn.setCloseListener(this.listeners.onConnectionClosed);\n this.conn.setErrorListener(this.listeners.onConnectionErrored);\n }\n\n sendBufferedMessages(): SendBufferResult {\n // send any buffered messages\n // dont explicity clear the buffer, we'll just filter out old messages\n // when we receive an ack\n if (this.sendBuffer.length > 0) {\n this.log?.info(\n `sending ${\n this.sendBuffer.length\n } buffered messages, starting at seq ${this.nextSeq()}`,\n this.loggingMetadata,\n );\n\n for (const msg of this.sendBuffer) {\n this.assertSendOrdering(msg);\n\n const sent = this.conn.send(msg.data);\n if (!sent) {\n const reason = 'failed to send buffered message';\n this.listeners.onMessageSendFailure(\n { ...msg.msg, seq: msg.seq },\n reason,\n );\n\n return { ok: false, reason };\n }\n\n this.seqSent = msg.seq;\n }\n }\n\n return { ok: true, value: undefined };\n }\n\n get loggingMetadata() {\n return {\n ...super.loggingMetadata,\n ...this.conn.loggingMetadata,\n };\n }\n\n startMissingHeartbeatTimeout() {\n const maxMisses = this.options.heartbeatsUntilDead;\n const missDuration = maxMisses * this.options.heartbeatIntervalMs;\n this.heartbeatMissTimeout = setTimeout(() => {\n this.log?.info(\n `closing connection to ${this.to} due to inactivity (missed ${maxMisses} heartbeats which is ${missDuration}ms)`,\n this.loggingMetadata,\n );\n this.telemetry.span.addEvent(\n 'closing connection due to missing heartbeat',\n );\n\n this.conn.close();\n }, missDuration);\n }\n\n startActiveHeartbeat() {\n this.isActivelyHeartbeating = true;\n this.heartbeatHandle = setInterval(() => {\n this.sendHeartbeat();\n }, this.options.heartbeatIntervalMs);\n }\n\n private sendHeartbeat(): void {\n this.log?.debug('sending heartbeat', this.loggingMetadata);\n const heartbeat = {\n streamId: 'heartbeat',\n controlFlags: ControlFlags.AckBit,\n payload: {\n type: 'ACK',\n } satisfies Static<typeof ControlMessageAckSchema>,\n } satisfies PartialTransportMessage;\n\n this.send(heartbeat);\n }\n\n /**\n * Schedules the next proactive re-handshake from the credential's expiry. The\n * server calls this after each (re)validation, mirroring {@link startActiveHeartbeat}:\n * once armed the session drives the exchange itself — one handshake window before\n * expiry it sends a re-handshake request and waits for the response, firing\n * {@link SessionConnectedListeners.onRehandshakeTimeout} if none arrives in time.\n * Passing `undefined` (a credential that never expires) cancels any schedule.\n */\n scheduleRehandshake(expiry: number | undefined) {\n this.clearRehandshakeTimer();\n this.credentialExpiry = expiry;\n if (expiry === undefined) {\n return;\n }\n\n // re-handshake one window before expiry so the exchange resolves (a refresh\n // lands, or the deadline below tears the session down) by the time it expires\n const delayMs = expiry - this.options.handshakeTimeoutMs - Date.now();\n this.rehandshakeTimer = setTimeout(\n () => {\n this.rehandshakeTimer = undefined;\n this.sendRehandshakeRequest();\n },\n Math.max(0, delayMs),\n );\n }\n\n /**\n * Sends a re-handshake request immediately and arms the response deadline,\n * bypassing the expiry schedule. Returns false if the request couldn't be sent.\n */\n requestRehandshakeNow(): boolean {\n this.clearRehandshakeTimer();\n\n return this.sendRehandshakeRequest();\n }\n\n private sendRehandshakeRequest(): boolean {\n const res = this.send(rehandshakeRequestMessage());\n if (!res.ok) {\n // the send failure already tore the session down via onMessageSendFailure\n return false;\n }\n\n // clamp the deadline to the credential's remaining life, so one validated with\n // little time left is still torn down by expiry rather than a full window later\n const deadlineMs =\n this.credentialExpiry !== undefined\n ? Math.min(\n this.options.handshakeTimeoutMs,\n this.credentialExpiry - Date.now(),\n )\n : this.options.handshakeTimeoutMs;\n this.rehandshakeTimer = setTimeout(\n () => {\n this.rehandshakeTimer = undefined;\n this.listeners.onRehandshakeTimeout?.();\n },\n Math.max(0, deadlineMs),\n );\n\n return true;\n }\n\n clearRehandshakeTimer() {\n if (this.rehandshakeTimer) {\n clearTimeout(this.rehandshakeTimer);\n this.rehandshakeTimer = undefined;\n }\n }\n\n onMessageData = (msg: Uint8Array) => {\n const parsedMsgRes = this.codec.fromBuffer(msg);\n if (!parsedMsgRes.ok) {\n this.listeners.onInvalidMessage(\n `could not parse message: ${parsedMsgRes.reason}`,\n );\n\n return;\n }\n\n const parsedMsg = parsedMsgRes.value;\n\n // messages must originate from this session's peer\n if (parsedMsg.from !== this.to) {\n this.listeners.onInvalidMessage(\n `received message with 'from' (${parsedMsg.from}) that does not match the session peer (${this.to})`,\n );\n\n return;\n }\n\n // check message ordering here\n if (parsedMsg.seq !== this.ack) {\n if (parsedMsg.seq < this.ack) {\n this.log?.debug(\n `received duplicate msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack}), discarding`,\n {\n ...this.loggingMetadata,\n transportMessage: parsedMsg,\n },\n );\n } else {\n const reason = `received out-of-order msg, closing connection (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\n this.telemetry.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: reason,\n });\n\n // try to recover by closing the connection and re-handshaking\n // with the session intact\n this.conn.close();\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 // re-handshake frames ride a reserved stream and are consumed by the\n // transport, never surfaced to the router (same as the acks handled below)\n if (parsedMsg.streamId === RehandshakeStreamId) {\n this.listeners.onRehandshake(parsedMsg);\n\n return;\n }\n\n this.listeners.onMessage(parsedMsg);\n\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();\n this.conn.removeCloseListener();\n this.conn.removeErrorListener();\n\n if (this.heartbeatHandle) {\n clearInterval(this.heartbeatHandle);\n this.heartbeatHandle = undefined;\n }\n\n if (this.heartbeatMissTimeout) {\n clearTimeout(this.heartbeatMissTimeout);\n this.heartbeatMissTimeout = undefined;\n }\n\n this.clearRehandshakeTimer();\n }\n\n _handleClose(): void {\n super._handleClose();\n this.conn.close();\n }\n}\n","import {\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodListeners,\n IdentifiedSessionWithGracePeriodProps,\n SessionState,\n} from './common';\n\nexport interface SessionBackingOffListeners\n extends IdentifiedSessionWithGracePeriodListeners {\n onBackoffFinished: () => void;\n}\n\nexport interface SessionBackingOffProps\n extends IdentifiedSessionWithGracePeriodProps {\n backoffMs: number;\n listeners: SessionBackingOffListeners;\n}\n\n/*\n * A session that is backing off before attempting to connect.\n * See transitions.ts for valid transitions.\n */\nexport class SessionBackingOff extends IdentifiedSessionWithGracePeriod {\n readonly state = SessionState.BackingOff as const;\n listeners: SessionBackingOffListeners;\n\n backoffTimeout?: ReturnType<typeof setTimeout>;\n\n constructor(props: SessionBackingOffProps) {\n super(props);\n this.listeners = props.listeners;\n\n this.backoffTimeout = setTimeout(() => {\n this.listeners.onBackoffFinished();\n }, props.backoffMs);\n }\n\n _handleClose(): void {\n super._handleClose();\n }\n\n _handleStateExit(): void {\n super._handleStateExit();\n\n if (this.backoffTimeout) {\n clearTimeout(this.backoffTimeout);\n this.backoffTimeout = undefined;\n }\n }\n}\n","import { DecodeError, ExtensionCodec, decode, encode } from '@msgpack/msgpack';\nimport { Codec } from './types';\n\nconst BIGINT_EXT_TYPE = 0;\nconst extensionCodec = new ExtensionCodec();\nextensionCodec.register({\n type: BIGINT_EXT_TYPE,\n encode(input: unknown): Uint8Array | null {\n if (typeof input === 'bigint') {\n if (\n input <= Number.MAX_SAFE_INTEGER &&\n input >= Number.MIN_SAFE_INTEGER\n ) {\n return encode(Number(input));\n } else {\n return encode(String(input));\n }\n } else {\n return null;\n }\n },\n decode(data: Uint8Array): bigint {\n const val = decode(data);\n if (!(typeof val === 'string' || typeof val === 'number')) {\n throw new DecodeError(`unexpected BigInt source: ${typeof val}`);\n }\n\n return BigInt(val);\n },\n});\n\n/**\n * Binary codec, uses [msgpack](https://www.npmjs.com/package/@msgpack/msgpack) under the hood\n * @type {Codec}\n */\nexport const BinaryCodec: Codec = {\n toBuffer(obj) {\n return encode(obj, {\n ignoreUndefined: true,\n initialBufferSize: 512,\n extensionCodec,\n });\n },\n fromBuffer: (buff: Uint8Array) => {\n const res = decode(buff, { extensionCodec });\n if (typeof res !== 'object' || res === null) {\n throw new Error('unpacked msg is not an object');\n }\n\n return res;\n },\n};\n","import { Value } from 'typebox/value';\nimport {\n OpaqueTransportMessage,\n OpaqueTransportMessageSchema,\n} from '../transport';\nimport { Codec } from './types';\nimport { DeserializeResult, SerializeResult } from '../transport/results';\nimport { coerceErrorString } from '../transport/stringifyError';\n\n/**\n * Adapts a {@link Codec} to the {@link OpaqueTransportMessage} format,\n * accounting for fallibility of toBuffer and fromBuffer and wrapping\n * it with a Result type.\n */\nexport class CodecMessageAdapter {\n constructor(private readonly codec: Codec) {}\n\n toBuffer(msg: OpaqueTransportMessage): SerializeResult {\n try {\n return {\n ok: true,\n value: this.codec.toBuffer(msg),\n };\n } catch (e) {\n return {\n ok: false,\n reason: coerceErrorString(e),\n };\n }\n }\n\n fromBuffer(buf: Uint8Array): DeserializeResult {\n try {\n const parsedMsg = this.codec.fromBuffer(buf);\n if (!Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {\n return {\n ok: false,\n reason: 'transport message schema mismatch',\n };\n }\n\n return {\n ok: true,\n value: parsedMsg,\n };\n } catch (e) {\n return {\n ok: false,\n reason: coerceErrorString(e),\n };\n }\n }\n}\n","import { TransportClientId } from '..';\nimport {\n SessionConnecting,\n SessionConnectingListeners,\n} from './SessionConnecting';\nimport {\n SessionNoConnection,\n SessionNoConnectionListeners,\n} from './SessionNoConnection';\nimport {\n IdentifiedSession,\n IdentifiedSessionProps,\n IdentifiedSessionWithGracePeriod,\n IdentifiedSessionWithGracePeriodProps,\n SessionOptions,\n} from './common';\nimport {\n PropagationContext,\n createConnectionTelemetryInfo,\n createSessionTelemetryInfo,\n} from '../../tracing';\nimport {\n SessionWaitingForHandshake,\n SessionWaitingForHandshakeListeners,\n} from './SessionWaitingForHandshake';\nimport {\n SessionHandshaking,\n SessionHandshakingListeners,\n} from './SessionHandshaking';\nimport {\n SessionConnected,\n SessionConnectedListeners,\n} from './SessionConnected';\nimport { generateId } from '../id';\nimport { Connection } from '../connection';\nimport { Logger } from '../../logging';\nimport {\n SessionBackingOff,\n SessionBackingOffListeners,\n} from './SessionBackingOff';\nimport { EncodedTransportMessage, ProtocolVersion } from '../message';\nimport { Tracer } from '@opentelemetry/api';\nimport { CodecMessageAdapter } from '../../codec';\n\nfunction inheritSharedSession(\n session: IdentifiedSession,\n): Omit<IdentifiedSessionProps, 'listeners'> {\n return {\n id: session.id,\n from: session.from,\n to: session.to,\n seq: session.seq,\n ack: session.ack,\n seqSent: session.seqSent,\n sendBuffer: session.sendBuffer,\n telemetry: session.telemetry,\n options: session.options,\n log: session.log,\n tracer: session.tracer,\n protocolVersion: session.protocolVersion,\n codec: session.codec,\n };\n}\n\nfunction inheritSharedSessionWithGrace(\n session: IdentifiedSessionWithGracePeriod,\n): Omit<IdentifiedSessionWithGracePeriodProps, 'listeners'> {\n return {\n ...inheritSharedSession(session),\n graceExpiryTime: session.graceExpiryTime,\n };\n}\n\nexport const SessionStateGraph = {\n entrypoints: {\n NoConnection: (\n to: TransportClientId,\n from: TransportClientId,\n listeners: SessionNoConnectionListeners,\n options: SessionOptions,\n protocolVersion: ProtocolVersion,\n tracer: Tracer,\n log?: Logger,\n ) => {\n const id = `session-${generateId()}`;\n const telemetry = createSessionTelemetryInfo(tracer, id, to, from);\n const sendBuffer: Array<EncodedTransportMessage> = [];\n\n const session = new SessionNoConnection({\n listeners,\n id,\n from,\n to,\n seq: 0,\n ack: 0,\n seqSent: 0,\n graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,\n sendBuffer,\n telemetry,\n options,\n protocolVersion,\n tracer,\n log,\n codec: new CodecMessageAdapter(options.codec),\n });\n\n session.log?.info(`session ${session.id} created in NoConnection state`, {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n });\n\n return session;\n },\n WaitingForHandshake: <ConnType extends Connection>(\n from: TransportClientId,\n conn: ConnType,\n listeners: SessionWaitingForHandshakeListeners,\n options: SessionOptions,\n tracer: Tracer,\n log?: Logger,\n ): SessionWaitingForHandshake<ConnType> => {\n const session = new SessionWaitingForHandshake({\n conn,\n listeners,\n from,\n options,\n tracer,\n log,\n codec: new CodecMessageAdapter(options.codec),\n });\n\n session.log?.info(`session created in WaitingForHandshake state`, {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n });\n\n return session;\n },\n },\n // All of the transitions 'move'/'consume' the old session and return a new one.\n // After a session is transitioned, any usage of the old session will throw.\n transition: {\n // happy path transitions\n NoConnectionToBackingOff: (\n oldSession: SessionNoConnection,\n backoffMs: number,\n listeners: SessionBackingOffListeners,\n ): SessionBackingOff => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionBackingOff({\n backoffMs,\n listeners,\n ...carriedState,\n });\n\n session.log?.info(\n `session ${session.id} transition from NoConnection to BackingOff`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n BackingOffToConnecting: <ConnType extends Connection>(\n oldSession: SessionBackingOff,\n connPromise: Promise<ConnType>,\n listeners: SessionConnectingListeners,\n ): SessionConnecting<ConnType> => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionConnecting({\n connPromise,\n listeners,\n ...carriedState,\n });\n\n session.log?.info(\n `session ${session.id} transition from BackingOff to Connecting`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n ConnectingToHandshaking: <ConnType extends Connection>(\n oldSession: SessionConnecting<ConnType>,\n conn: ConnType,\n listeners: SessionHandshakingListeners,\n ): SessionHandshaking<ConnType> => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionHandshaking({\n conn,\n listeners,\n ...carriedState,\n });\n\n conn.telemetry = createConnectionTelemetryInfo(\n session.tracer,\n conn,\n session.telemetry,\n );\n session.log?.info(\n `session ${session.id} transition from Connecting to Handshaking`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n HandshakingToConnected: <ConnType extends Connection>(\n oldSession: SessionHandshaking<ConnType>,\n listeners: SessionConnectedListeners,\n ): SessionConnected<ConnType> => {\n const carriedState = inheritSharedSession(oldSession);\n const conn = oldSession.conn;\n oldSession._handleStateExit();\n\n const session = new SessionConnected({\n conn,\n listeners,\n ...carriedState,\n });\n\n session.startMissingHeartbeatTimeout();\n\n session.log?.info(\n `session ${session.id} transition from Handshaking to Connected`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n WaitingForHandshakeToConnected: <ConnType extends Connection>(\n pendingSession: SessionWaitingForHandshake<ConnType>,\n oldSession: SessionNoConnection | undefined,\n sessionId: string,\n to: TransportClientId,\n propagationCtx: PropagationContext | undefined,\n listeners: SessionConnectedListeners,\n protocolVersion: ProtocolVersion,\n ): SessionConnected<ConnType> => {\n const conn = pendingSession.conn;\n const { from, options } = pendingSession;\n const carriedState: Omit<IdentifiedSessionProps, 'listeners'> = oldSession\n ? // old session exists, inherit state\n inheritSharedSession(oldSession)\n : // old session does not exist, create new state\n ({\n id: sessionId,\n from,\n to,\n seq: 0,\n ack: 0,\n seqSent: 0,\n sendBuffer: [],\n telemetry: createSessionTelemetryInfo(\n pendingSession.tracer,\n sessionId,\n to,\n from,\n propagationCtx,\n ),\n options,\n tracer: pendingSession.tracer,\n log: pendingSession.log,\n protocolVersion,\n codec: new CodecMessageAdapter(options.codec),\n } satisfies Omit<IdentifiedSessionProps, 'listeners'>);\n\n pendingSession._handleStateExit();\n oldSession?._handleStateExit();\n\n const session = new SessionConnected({\n conn,\n listeners,\n ...carriedState,\n });\n\n session.startMissingHeartbeatTimeout();\n\n conn.telemetry = createConnectionTelemetryInfo(\n session.tracer,\n conn,\n session.telemetry,\n );\n session.log?.info(\n `session ${session.id} transition from WaitingForHandshake to Connected`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n // disconnect paths\n BackingOffToNoConnection: (\n oldSession: SessionBackingOff,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from BackingOff to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n ConnectingToNoConnection: <ConnType extends Connection>(\n oldSession: SessionConnecting<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession.bestEffortClose();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from Connecting to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n HandshakingToNoConnection: <ConnType extends Connection>(\n oldSession: SessionHandshaking<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSessionWithGrace(oldSession);\n oldSession.conn.close();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from Handshaking to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n ConnectedToNoConnection: <ConnType extends Connection>(\n oldSession: SessionConnected<ConnType>,\n listeners: SessionNoConnectionListeners,\n ): SessionNoConnection => {\n const carriedState = inheritSharedSession(oldSession);\n const graceExpiryTime =\n Date.now() + oldSession.options.sessionDisconnectGraceMs;\n oldSession.conn.close();\n oldSession._handleStateExit();\n\n const session = new SessionNoConnection({\n listeners,\n graceExpiryTime,\n ...carriedState,\n });\n session.log?.info(\n `session ${session.id} transition from Connected to NoConnection`,\n {\n ...session.loggingMetadata,\n tags: ['state-transition'],\n },\n );\n\n return session;\n },\n },\n} as const;\n\nconst transitions = SessionStateGraph.transition;\n\nexport const ClientSessionStateGraph = {\n entrypoint: SessionStateGraph.entrypoints.NoConnection,\n transition: {\n // happy paths\n // NoConnection -> BackingOff: attempt to connect\n NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,\n // BackingOff -> Connecting: backoff period elapsed, start connection\n BackingOffToConnecting: transitions.BackingOffToConnecting,\n // Connecting -> Handshaking: connection established, start handshake\n ConnectingToHandshaking: transitions.ConnectingToHandshaking,\n // Handshaking -> Connected: handshake complete, session ready\n HandshakingToConnected: transitions.HandshakingToConnected,\n\n // disconnect paths\n // BackingOff -> NoConnection: unused\n BackingOffToNoConnection: transitions.BackingOffToNoConnection,\n // Connecting -> NoConnection: connection failed or connection timeout\n ConnectingToNoConnection: transitions.ConnectingToNoConnection,\n // Handshaking -> NoConnection: connection closed or handshake timeout\n HandshakingToNoConnection: transitions.HandshakingToNoConnection,\n // Connected -> NoConnection: connection closed\n ConnectedToNoConnection: transitions.ConnectedToNoConnection,\n\n // destroy/close paths\n // NoConnection -> x: grace period elapsed\n // BackingOff -> x: grace period elapsed\n // Connecting -> x: grace period elapsed\n // Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection\n // Connected -> x: grace period elapsed or invalid message\n },\n};\n\nexport type ClientSession<ConnType extends Connection> =\n | SessionNoConnection\n | SessionBackingOff\n | SessionConnecting<ConnType>\n | SessionHandshaking<ConnType>\n | SessionConnected<ConnType>;\n\nexport const ServerSessionStateGraph = {\n entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,\n transition: {\n // happy paths\n // WaitingForHandshake -> Connected: handshake complete, session ready\n WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,\n\n // disconnect paths\n // Connected -> NoConnection: connection closed\n ConnectedToNoConnection: transitions.ConnectedToNoConnection,\n\n // destroy/close paths\n // WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed\n },\n};\n\nexport type ServerSession<ConnType extends Connection> =\n // SessionWaitingForHandshake<ConnType> is stored separately in the server transport\n SessionConnected<ConnType> | SessionNoConnection;\n\nexport type Session<ConnType extends Connection> =\n | ClientSession<ConnType>\n | ServerSession<ConnType>;\n","import {\n OpaqueTransportMessage,\n PartialTransportMessage,\n TransportClientId,\n} from './message';\nimport {\n BaseLogger,\n LogFn,\n Logger,\n LoggingLevel,\n createLogProxy,\n} from '../logging/log';\nimport {\n EventDispatcher,\n EventHandler,\n EventMap,\n EventTypes,\n ProtocolError,\n} from './events';\nimport {\n ProvidedTransportOptions,\n TransportOptions,\n defaultTransportOptions,\n} from './options';\nimport {\n SessionConnected,\n SessionConnecting,\n SessionHandshaking,\n SessionNoConnection,\n SessionNoConnectionListeners,\n SessionState,\n} from './sessionStateMachine';\nimport { Connection } from './connection';\nimport { Session, SessionStateGraph } from './sessionStateMachine/transitions';\nimport { SessionId } from './sessionStateMachine/common';\nimport { Tracer } from '@opentelemetry/api';\nimport { getTracer } from '../tracing';\n\n/**\n * Represents the possible states of a transport.\n * @property {'open'} open - The transport is open and operational (note that this doesn't mean it is actively connected)\n * @property {'closed'} closed - The transport is permanently closed and cannot be reopened.\n */\nexport type TransportStatus = 'open' | 'closed';\n\nexport interface DeleteSessionOptions {\n unhealthy: boolean;\n}\n\nexport type SessionBoundSendFn = (msg: PartialTransportMessage) => string;\n\n/**\n * Transports manage the lifecycle (creation/deletion) of sessions\n *\n * ```plaintext\n * ▲\n * incoming │\n * messages │\n * ▼\n * ┌─────────────┐ 1:N ┌───────────┐ 1:1* ┌────────────┐\n * │ Transport │ ◄─────► │ Session │ ◄─────► │ Connection │\n * └─────────────┘ └───────────┘ └────────────┘\n * ▲ * (may or may not be initialized yet)\n * │\n * ▼\n * ┌───────────┐\n * │ Message │\n * │ Listeners │\n * └───────────┘\n * ```\n * @abstract\n */\nexport abstract class Transport<ConnType extends Connection> {\n /**\n * The status of the transport.\n */\n private status: TransportStatus;\n\n /**\n * The client ID of this transport.\n */\n clientId: TransportClientId;\n\n /**\n * The event dispatcher for handling events of type EventTypes.\n */\n eventDispatcher: EventDispatcher<EventTypes>;\n\n /**\n * The options for this transport.\n */\n protected options: TransportOptions;\n log?: Logger;\n tracer: Tracer;\n\n sessions: Map<TransportClientId, Session<ConnType>>;\n\n /**\n * Creates a new Transport instance.\n * @param codec The codec used to encode and decode messages.\n * @param clientId The client ID of this transport.\n */\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedTransportOptions,\n ) {\n this.options = { ...defaultTransportOptions, ...providedOptions };\n this.eventDispatcher = new EventDispatcher();\n this.clientId = clientId;\n this.status = 'open';\n this.sessions = new Map();\n this.tracer = getTracer();\n }\n\n bindLogger(fn: LogFn | Logger, level?: LoggingLevel) {\n // construct logger from fn\n if (typeof fn === 'function') {\n this.log = createLogProxy(new BaseLogger(fn, level));\n\n return;\n }\n\n // object case, just assign\n this.log = createLogProxy(fn);\n }\n\n /**\n * Called when a message is received by this transport.\n * You generally shouldn't need to override this in downstream transport implementations.\n * @param message The received message.\n */\n protected handleMsg(message: OpaqueTransportMessage) {\n if (this.getStatus() !== 'open') return;\n\n this.eventDispatcher.dispatchEvent('message', message);\n }\n\n /**\n * Adds a listener to this transport.\n * @param the type of event to listen for\n * @param handler The message handler to add.\n */\n addEventListener<K extends EventTypes, T extends EventHandler<K>>(\n type: K,\n handler: T,\n ): void {\n this.eventDispatcher.addEventListener(type, handler);\n }\n\n /**\n * Removes a listener from this transport.\n * @param the type of event to un-listen on\n * @param handler The message handler to remove.\n */\n removeEventListener<K extends EventTypes, T extends EventHandler<K>>(\n type: K,\n handler: T,\n ): void {\n this.eventDispatcher.removeEventListener(type, handler);\n }\n\n protected protocolError(message: EventMap['protocolError']) {\n this.eventDispatcher.dispatchEvent('protocolError', message);\n }\n\n /**\n * Default close implementation for transports. You should override this in the downstream\n * implementation if you need to do any additional cleanup and call super.close() at the end.\n * Closes the transport. Any messages sent while the transport is closed will be silently discarded.\n */\n close() {\n this.status = 'closed';\n\n const sessions = Array.from(this.sessions.values());\n for (const session of sessions) {\n this.deleteSession(session);\n }\n\n this.eventDispatcher.dispatchEvent('transportStatus', {\n status: this.status,\n });\n\n this.eventDispatcher.removeAllListeners();\n this.log?.info(`manually closed transport`, { clientId: this.clientId });\n }\n\n getStatus(): TransportStatus {\n return this.status;\n }\n\n // state transitions\n protected createSession<S extends Session<ConnType>>(session: S): void {\n const activeSession = this.sessions.get(session.to);\n if (activeSession) {\n const msg = `attempt to create session for ${session.to} but active session (${activeSession.id}) already exists`;\n this.log?.error(msg, {\n ...session.loggingMetadata,\n tags: ['invariant-violation'],\n });\n throw new Error(msg);\n }\n\n this.sessions.set(session.to, session);\n this.eventDispatcher.dispatchEvent('sessionStatus', {\n status: 'created',\n session: session,\n });\n\n this.eventDispatcher.dispatchEvent('sessionTransition', {\n state: session.state,\n id: session.id,\n } as EventMap['sessionTransition']);\n }\n\n protected updateSession<S extends Session<ConnType>>(session: S): void {\n const activeSession = this.sessions.get(session.to);\n if (!activeSession) {\n const msg = `attempt to transition session for ${session.to} but no active session exists`;\n this.log?.error(msg, {\n ...session.loggingMetadata,\n tags: ['invariant-violation'],\n });\n throw new Error(msg);\n }\n\n if (activeSession.id !== session.id) {\n const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;\n this.log?.error(msg, {\n ...session.loggingMetadata,\n tags: ['invariant-violation'],\n });\n throw new Error(msg);\n }\n\n this.sessions.set(session.to, session);\n this.eventDispatcher.dispatchEvent('sessionTransition', {\n state: session.state,\n id: session.id,\n } as EventMap['sessionTransition']);\n }\n\n protected deleteSession(\n session: Session<ConnType>,\n options?: DeleteSessionOptions,\n ) {\n // ensure idempotency esp re: dispatching events\n if (session._isConsumed) return;\n\n const loggingMetadata = session.loggingMetadata;\n if (loggingMetadata.tags && options?.unhealthy) {\n loggingMetadata.tags.push('unhealthy-session');\n }\n\n session.log?.info(`closing session ${session.id}`, loggingMetadata);\n this.eventDispatcher.dispatchEvent('sessionStatus', {\n status: 'closing',\n session: session,\n });\n\n const to = session.to;\n session.close();\n this.sessions.delete(to);\n this.eventDispatcher.dispatchEvent('sessionStatus', {\n status: 'closed',\n session: { id: session.id, to: to },\n });\n }\n\n // common listeners\n protected onSessionGracePeriodElapsed(session: Session<ConnType>) {\n this.log?.info(\n `session to ${session.to} grace period elapsed, closing`,\n session.loggingMetadata,\n );\n\n this.deleteSession(session);\n }\n\n protected onConnectingFailed(\n session: SessionConnecting<ConnType>,\n ): SessionNoConnection {\n // transition to no connection\n const noConnectionSession =\n SessionStateGraph.transition.ConnectingToNoConnection(session, {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...noConnectionSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(noConnectionSession, { unhealthy: true });\n },\n });\n\n this.updateSession(noConnectionSession);\n\n return noConnectionSession;\n }\n\n protected onConnClosed(\n session: SessionHandshaking<ConnType> | SessionConnected<ConnType>,\n ): SessionNoConnection {\n // transition to no connection\n let noConnectionSession: SessionNoConnection;\n const listeners: SessionNoConnectionListeners = {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...noConnectionSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(noConnectionSession, { unhealthy: true });\n },\n };\n\n if (session.state === SessionState.Handshaking) {\n noConnectionSession =\n SessionStateGraph.transition.HandshakingToNoConnection(\n session,\n listeners,\n );\n } else {\n noConnectionSession =\n SessionStateGraph.transition.ConnectedToNoConnection(\n session,\n listeners,\n );\n }\n\n this.updateSession(noConnectionSession);\n\n return noConnectionSession;\n }\n\n /**\n * Gets a send closure scoped to a specific session. Sending using the returned\n * closure after the session has transitioned to a different state will be a noop.\n *\n * Session objects themselves can become stale as they transition between\n * states. As stale sessions cannot be used again (and will throw), holding\n * onto a session object is not recommended.\n */\n getSessionBoundSendFn(\n to: TransportClientId,\n sessionId: SessionId,\n ): SessionBoundSendFn {\n if (this.getStatus() !== 'open') {\n throw new Error('cannot get a bound send function on a closed transport');\n }\n\n return (msg: PartialTransportMessage) => {\n const session = this.sessions.get(to);\n if (!session) {\n throw new Error(\n `session scope for ${sessionId} has ended (close), can't send`,\n );\n }\n\n const sameSession = session.id === sessionId;\n if (!sameSession || session._isConsumed) {\n throw new Error(\n `session scope for ${sessionId} has ended (transition), can't send`,\n );\n }\n\n const res = session.send(msg);\n if (!res.ok) {\n throw new Error(res.reason);\n }\n\n return res.value;\n };\n }\n}\n","import { SpanStatusCode } from '@opentelemetry/api';\nimport { ClientHandshakeOptions } from '../router/handshake';\nimport { validationErrorToRiverErrors } from '../router/errors';\nimport {\n ControlMessageHandshakeResponseSchema,\n ControlMessageRehandshakeRequestSchema,\n HandshakeErrorRetriableResponseCodes,\n OpaqueTransportMessage,\n TransportClientId,\n currentProtocolVersion,\n handshakeRequestMessage,\n rehandshakeResponseMessage,\n} from './message';\nimport {\n ClientTransportOptions,\n ProvidedClientTransportOptions,\n defaultClientTransportOptions,\n} from './options';\nimport { LeakyBucketRateLimit } from './rateLimit';\nimport { DeleteSessionOptions, Transport } from './transport';\nimport { coerceErrorString } from './stringifyError';\nimport { ProtocolError } from './events';\nimport { Value } from 'typebox/value';\nimport { 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 {\n ClientSession,\n ClientSessionStateGraph,\n Session,\n} from './sessionStateMachine/transitions';\nimport { SessionState } from './sessionStateMachine/common';\nimport { SessionNoConnection } from './sessionStateMachine/SessionNoConnection';\nimport { SessionBackingOff } from './sessionStateMachine/SessionBackingOff';\n\n/**\n * Outcome of constructing client handshake metadata, kept as a value (rather than a\n * rejecting promise) so it can be prefetched when a connection attempt starts and\n * awaited later even if that attempt is abandoned before the handshake is sent.\n */\ntype ConstructedHandshakeMetadata =\n | {\n ok: true;\n metadata: Awaited<ReturnType<ClientHandshakeOptions['construct']>>;\n }\n | { ok: false; reason: string };\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 /**\n * Handshake-metadata constructions prefetched when a connection attempt begins\n * (only when {@link ClientHandshakeOptions.eager} is set), so the\n * token fetch overlaps the dial instead of following it. Keyed by the peer being\n * connected to; consumed when the handshake is sent and cleared when an attempt\n * is abandoned.\n */\n private pendingHandshakeMetadata = new Map<\n TransportClientId,\n Promise<ConstructedHandshakeMetadata>\n >();\n\n sessions: Map<TransportClientId, ClientSession<ConnType>>;\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedClientTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.sessions = new Map();\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 protected handleRehandshakeMessage(message: OpaqueTransportMessage): void {\n if (!Value.Check(ControlMessageRehandshakeRequestSchema, message.payload)) {\n this.log?.warn(\n `ignoring malformed re-handshake request from ${message.from}`,\n { clientId: this.clientId, connectedTo: message.from },\n );\n\n return;\n }\n\n void this.sendRehandshake(message.from);\n }\n\n /**\n * Re-constructs handshake metadata via the configured handshake extension and\n * sends it back to the server so it can replace the metadata for this session.\n * Triggered by a server {@link ControlMessageRehandshakeRequestSchema}.\n */\n private async sendRehandshake(to: TransportClientId) {\n if (!this.handshakeExtensions) {\n this.log?.warn(\n `got re-handshake request from ${to} but no handshake extensions are configured, ignoring`,\n { clientId: this.clientId, connectedTo: to },\n );\n\n return;\n }\n\n const session = this.sessions.get(to);\n if (!session || session.state !== SessionState.Connected) {\n return;\n }\n\n const loggingMetadata = session.loggingMetadata;\n const sessionId = session.id;\n\n let metadata: unknown;\n try {\n metadata = await this.handshakeExtensions.construct();\n } catch (err) {\n this.log?.error(\n `failed to construct re-handshake metadata for ${to}: ${coerceErrorString(\n err,\n )}`,\n loggingMetadata,\n );\n\n return;\n }\n\n // bind the send to the session that asked to re-handshake: a hard reconnect\n // during construct() makes the send throw rather than delivering stale\n // metadata to the freshly handshaked session that replaced it\n try {\n const send = this.getSessionBoundSendFn(to, sessionId);\n send(rehandshakeResponseMessage(metadata));\n } catch (err) {\n const reason = coerceErrorString(err);\n this.log?.error(\n `failed to send re-handshake metadata to ${to}: ${reason}`,\n loggingMetadata,\n );\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n }\n }\n\n /**\n * Abstract method that creates a new {@link Connection} object.\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: TransportClientId) {\n const oldSession = this.sessions.get(to);\n if (!this.options.enableTransparentSessionReconnects && oldSession) {\n this.deleteSession(oldSession);\n }\n\n if (this.reconnectOnConnectionDrop && this.getStatus() === 'open') {\n this.connect(to);\n }\n }\n\n /*\n * Creates a raw unconnected session object.\n * This is mostly a River internal, you shouldn't need to use this directly.\n */\n createUnconnectedSession(to: string): SessionNoConnection {\n const session = ClientSessionStateGraph.entrypoint(\n to,\n this.clientId,\n {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(session);\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(session, { unhealthy: true });\n },\n },\n this.options,\n currentProtocolVersion,\n this.tracer,\n this.log,\n );\n\n this.createSession(session);\n\n return session;\n }\n\n protected deleteSession(\n session: Session<ConnType>,\n options?: DeleteSessionOptions,\n ): void {\n // drop any handshake metadata prefetched for an attempt that's being torn down\n this.pendingHandshakeMetadata.delete(session.to);\n super.deleteSession(session, options);\n }\n\n // listeners\n protected onConnectingFailed(session: SessionConnecting<ConnType>) {\n const noConnectionSession = super.onConnectingFailed(session);\n this.tryReconnecting(noConnectionSession.to);\n\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\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 ClientSessionStateGraph.transition.ConnectingToHandshaking(\n session,\n conn,\n {\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, code) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n handshakingSession.loggingMetadata,\n );\n this.deleteSession(session, { unhealthy: true });\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\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 onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(handshakingSession);\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...handshakingSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(handshakingSession, { unhealthy: true });\n },\n },\n );\n\n this.updateSession(handshakingSession);\n void this.sendHandshake(handshakingSession);\n\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, { unhealthy: true });\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: Value.Errors(\n ControlMessageHandshakeResponseSchema,\n msg.payload,\n ).flatMap(validationErrorToRiverErrors),\n });\n\n return;\n }\n\n // invariant: handshake response should be ok\n if (!msg.payload.status.ok) {\n const retriable = Value.Check(\n HandshakeErrorRetriableResponseCodes,\n msg.payload.status.code,\n );\n\n const reason = `handshake failed: ${msg.payload.status.reason}`;\n const to = session.to;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n\n if (retriable) {\n this.tryReconnecting(to);\n } else {\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code: msg.payload.status.code,\n message: reason,\n });\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\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 ClientSessionStateGraph.transition.HandshakingToConnected(session, {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n\n if (\n err instanceof Error &&\n this.options.isFatalConnectionError(err)\n ) {\n this.log?.warn(\n `connection to ${connectedSession.to} fatally errored: ${errStr}`,\n connectedSession.loggingMetadata,\n );\n\n this.reconnectOnConnectionDrop = false;\n } else {\n this.log?.warn(\n `connection to ${connectedSession.to} errored: ${errStr}`,\n connectedSession.loggingMetadata,\n );\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) => {\n this.handleMsg(msg);\n },\n onRehandshake: (msg) => {\n this.handleRehandshakeMessage(msg);\n },\n onInvalidMessage: (reason) => {\n this.log?.error(`invalid message: ${reason}`, {\n ...connectedSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.InvalidMessage,\n message: reason,\n });\n this.deleteSession(connectedSession, { unhealthy: true });\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...connectedSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(connectedSession, { unhealthy: true });\n },\n });\n\n const res = connectedSession.sendBufferedMessages();\n if (!res.ok) {\n return;\n }\n\n this.updateSession(connectedSession);\n this.retryBudget.startRestoringBudget();\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 if (this.getStatus() !== 'open') {\n this.log?.info(\n `transport state is no longer open, cancelling attempt to connect to ${to}`,\n );\n\n return;\n }\n\n const session = this.sessions.get(to) ?? this.createUnconnectedSession(to);\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\n return;\n }\n\n // check budget\n if (!this.retryBudget.hasBudget()) {\n const budgetConsumed = this.retryBudget.getBudgetConsumed();\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({\n type: ProtocolError.RetriesExceeded,\n message: errMsg,\n });\n\n return;\n }\n\n const backoffMs = this.retryBudget.getBackoffMs();\n\n this.log?.info(\n `attempting connection to ${to} (${backoffMs}ms backoff)`,\n session.loggingMetadata,\n );\n\n this.retryBudget.consumeBudget();\n const backingOffSession =\n ClientSessionStateGraph.transition.NoConnectionToBackingOff(\n session,\n backoffMs,\n {\n onBackoffFinished: () => {\n this.onBackoffFinished(backingOffSession);\n },\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(backingOffSession);\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...backingOffSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(backingOffSession, { unhealthy: true });\n },\n },\n );\n\n this.updateSession(backingOffSession);\n }\n\n /**\n * Manually kills all sessions to the server (including all pending state).\n * This is useful for when you want to close all connections to a server\n * and don't want to wait for the grace period to elapse.\n */\n hardDisconnect() {\n // create a copy of the sessions to avoid modifying the map while iterating\n const sessions = Array.from(this.sessions.values());\n for (const session of sessions) {\n this.deleteSession(session);\n }\n }\n\n protected onBackoffFinished(session: SessionBackingOff) {\n const connPromise = session.tracer.startActiveSpan(\n 'connect',\n async (span) => {\n try {\n return await this.createNewOutgoingConnection(session.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\n // when opted in, kick off the handshake-metadata construction now so a slow\n // token fetch overlaps establishing the connection instead of running after it\n if (this.handshakeExtensions?.eager) {\n this.pendingHandshakeMetadata.set(\n session.to,\n this.constructHandshakeMetadata(),\n );\n }\n\n // transition to connecting\n const connectingSession =\n ClientSessionStateGraph.transition.BackingOffToConnecting(\n session,\n connPromise,\n {\n onConnectionEstablished: (conn) => {\n this.log?.debug(\n `connection to ${connectingSession.to} established`,\n {\n ...conn.loggingMetadata,\n ...connectingSession.loggingMetadata,\n },\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 onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(connectingSession);\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...connectingSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(connectingSession, { unhealthy: true });\n },\n },\n );\n\n this.updateSession(connectingSession);\n }\n\n /**\n * Constructs handshake metadata via the configured handshake extension, capturing\n * a failure as a value so a prefetched result can be awaited without rejecting.\n */\n private async constructHandshakeMetadata(): Promise<ConstructedHandshakeMetadata> {\n if (!this.handshakeExtensions) {\n return { ok: true, metadata: undefined };\n }\n\n try {\n return { ok: true, metadata: await this.handshakeExtensions.construct() };\n } catch (err) {\n return { ok: false, reason: coerceErrorString(err) };\n }\n }\n\n private async sendHandshake(session: SessionHandshaking<ConnType>) {\n let metadata: unknown = undefined;\n\n if (this.handshakeExtensions) {\n // consume the metadata prefetched when the connection attempt began, falling\n // back to constructing now if none is in flight (e.g. a direct transition)\n const pending = this.pendingHandshakeMetadata.get(session.to);\n this.pendingHandshakeMetadata.delete(session.to);\n const result = await (pending ?? this.constructHandshakeMetadata());\n\n if (!result.ok) {\n this.log?.error(\n `failed to construct handshake metadata for session to ${session.to}: ${result.reason}`,\n session.loggingMetadata,\n );\n\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n message: `failed to construct handshake metadata: ${result.reason}`,\n });\n this.deleteSession(session, { unhealthy: true });\n\n return;\n }\n\n metadata = result.metadata;\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 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 const res = session.sendHandshake(requestMsg);\n if (!res.ok) {\n this.log?.error(`failed to send handshake request: ${res.reason}`, {\n ...session.loggingMetadata,\n transportMessage: requestMsg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: res.reason,\n });\n this.deleteSession(session, { unhealthy: true });\n }\n }\n\n close() {\n this.retryBudget.close();\n super.close();\n }\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 /**\n * A function to determine whether an error is fatal and should not be retried.\n * If this function returns true, the client transport will not attempt to reconnect.\n */\n isFatalConnectionError: (err: Error) => boolean;\n}\n\nexport class LeakyBucketRateLimit {\n private budgetConsumed: number;\n private intervalHandle?: ReturnType<typeof setInterval>;\n private readonly options: ConnectionRetryOptions;\n\n constructor(options: ConnectionRetryOptions) {\n this.options = options;\n this.budgetConsumed = 0;\n }\n\n getBackoffMs() {\n if (this.getBudgetConsumed() === 0) {\n return 0;\n }\n\n const exponent = Math.max(0, this.getBudgetConsumed() - 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() {\n // If we're consuming again, let's ensure that we're not leaking\n this.stopLeak();\n this.budgetConsumed = this.getBudgetConsumed() + 1;\n }\n\n getBudgetConsumed() {\n return this.budgetConsumed;\n }\n\n hasBudget() {\n return this.getBudgetConsumed() < this.options.attemptBudgetCapacity;\n }\n\n startRestoringBudget() {\n if (this.intervalHandle) {\n return;\n }\n\n const restoreBudgetForUser = () => {\n const currentBudget = this.budgetConsumed;\n if (!currentBudget) {\n this.stopLeak();\n\n return;\n }\n\n const newBudget = currentBudget - 1;\n if (newBudget === 0) {\n return;\n }\n\n this.budgetConsumed = newBudget;\n };\n\n this.intervalHandle = setInterval(\n restoreBudgetForUser,\n this.options.budgetRestoreIntervalMs,\n );\n }\n\n private stopLeak() {\n if (!this.intervalHandle) {\n return;\n }\n\n clearInterval(this.intervalHandle);\n this.intervalHandle = undefined;\n }\n\n resetBudget() {\n this.stopLeak();\n this.budgetConsumed = 0;\n }\n\n close() {\n this.stopLeak();\n }\n}\n","import { SpanStatusCode } from '@opentelemetry/api';\nimport { ServerHandshakeOptions } from '../router/handshake';\nimport { validationErrorToRiverErrors } from '../router/errors';\nimport {\n ControlMessageHandshakeRequestSchema,\n ControlMessageRehandshakeResponseSchema,\n HandshakeErrorCustomHandlerFatalResponseCodes,\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n acceptedProtocolVersions,\n TransportClientId,\n handshakeResponseMessage,\n currentProtocolVersion,\n isAcceptedProtocolVersion,\n} from './message';\nimport {\n ProvidedServerTransportOptions,\n ServerTransportOptions,\n defaultServerTransportOptions,\n} from './options';\nimport { DeleteSessionOptions, Transport } from './transport';\nimport { coerceErrorString } from './stringifyError';\nimport type { Static, TSchema } from 'typebox';\nimport { Value } from '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 MetadataSchema extends TSchema = TSchema,\n ParsedMetadata extends object = object,\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<MetadataSchema, ParsedMetadata>;\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(\n options: ServerHandshakeOptions<MetadataSchema, ParsedMetadata>,\n ) {\n this.handshakeExtensions = options;\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(\n session: ServerSession<ConnType>,\n options?: DeleteSessionOptions,\n ): void {\n this.sessionHandshakeMetadata.delete(session.to);\n super.deleteSession(session, options);\n }\n\n /**\n * Asks the connected client to re-handshake — re-construct and resend its\n * handshake metadata (e.g. a refreshed token). Returns false if there is no\n * live connection to send the request over.\n *\n * On a successful re-handshake the stored metadata is replaced and observed by\n * subsequent procedure calls. If the client does not respond with metadata that\n * re-validates before the response deadline — the shorter of\n * {@link SessionOptions.handshakeTimeoutMs} and the credential's remaining\n * lifetime — the session is torn down.\n */\n requestRehandshake(to: TransportClientId): boolean {\n const session = this.sessions.get(to);\n if (!session || session.state !== SessionState.Connected) {\n return false;\n }\n\n return session.requestRehandshakeNow();\n }\n\n /**\n * Stores freshly validated handshake metadata for a client and hands the\n * credential's expiry to the session, which schedules and runs the next\n * re-handshake itself. Called on every successful (re)handshake, so the schedule\n * perpetuates itself and survives transparent reconnects.\n */\n private storeSessionMetadata(\n session: ServerSession<ConnType>,\n parsed: ParsedMetadata,\n ) {\n this.sessionHandshakeMetadata.set(session.to, parsed);\n\n if (session.state === SessionState.Connected) {\n session.scheduleRehandshake(\n this.handshakeExtensions?.expiry?.(parsed)?.getTime(),\n );\n }\n }\n\n protected handleRehandshakeMessage(message: OpaqueTransportMessage): void {\n if (\n !Value.Check(ControlMessageRehandshakeResponseSchema, message.payload)\n ) {\n // a frame on the reserved re-handshake stream that isn't a valid response\n // is a protocol violation by the authenticated peer; fail the refresh\n const session = this.sessions.get(message.from);\n if (session) {\n this.teardownForFailedRehandshake(\n session,\n 'received malformed re-handshake control message',\n );\n }\n\n return;\n }\n\n void this.onRehandshakeResponse(message.from, message.payload.metadata);\n }\n\n /**\n * Re-validates handshake metadata sent by the client during a re-handshake and\n * replaces the stored metadata on success. Any failure (malformed metadata,\n * rejection, or a thrown validator) tears the session down.\n */\n private async onRehandshakeResponse(\n from: TransportClientId,\n metadata: unknown,\n ) {\n const handshakeExtensions = this.handshakeExtensions;\n if (!handshakeExtensions) {\n return;\n }\n\n const session = this.sessions.get(from);\n if (!session) {\n return;\n }\n\n if (!Value.Check(handshakeExtensions.schema, metadata)) {\n this.teardownForFailedRehandshake(\n session,\n 'received malformed handshake metadata during re-handshake',\n );\n\n return;\n }\n\n const previousParsedMetadata = this.sessionHandshakeMetadata.get(from);\n\n let parsedMetadataOrFailureCode;\n try {\n parsedMetadataOrFailureCode = await handshakeExtensions.validate(\n metadata,\n previousParsedMetadata,\n from,\n );\n } catch (err) {\n // teardownForFailedRehandshake no-ops if this session was already replaced\n // (e.g. a transparent reconnect) while we awaited validation\n this.teardownForFailedRehandshake(\n session,\n `handshake validation threw during re-handshake: ${coerceErrorString(\n err,\n )}`,\n );\n\n return;\n }\n\n if (\n Value.Check(\n HandshakeErrorCustomHandlerFatalResponseCodes,\n parsedMetadataOrFailureCode,\n )\n ) {\n this.teardownForFailedRehandshake(\n session,\n 're-handshake metadata rejected by handshake handler',\n );\n\n return;\n }\n\n // a reconnect/teardown during validation may have replaced this exact session\n // (a transparent reconnect keeps the same id); only store and reschedule if\n // it's still the one we validated against, so we don't clobber fresher metadata\n if (this.sessions.get(from) !== session) {\n return;\n }\n\n this.storeSessionMetadata(\n session,\n parsedMetadataOrFailureCode as ParsedMetadata,\n );\n\n this.log?.info(`re-handshake from ${from} ok`, {\n ...session.loggingMetadata,\n connectedTo: from,\n });\n }\n\n /**\n * Tears down a session whose re-handshake failed (rejected, malformed, timed\n * out, or a thrown validator). No-ops if {@link session} is no longer the live\n * session for its peer — a transparent reconnect keeps the same id, so callers\n * reaching here after an async gap can't accidentally close the session that\n * replaced it.\n */\n private teardownForFailedRehandshake(\n session: ServerSession<ConnType>,\n reason: string,\n ) {\n if (this.sessions.get(session.to) !== session) {\n return;\n }\n\n const to = session.to;\n this.log?.warn(`tearing down session to ${to}: ${reason}`, {\n ...session.loggingMetadata,\n connectedTo: to,\n });\n\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code: 'REJECTED_BY_CUSTOM_HANDLER',\n message: reason,\n });\n this.deleteSession(session, { unhealthy: true });\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\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.tracer,\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 const responseMsg = handshakeResponseMessage({\n from: this.clientId,\n to,\n status: {\n ok: false,\n code,\n reason,\n },\n });\n\n const res = session.sendHandshake(responseMsg);\n if (!res.ok) {\n this.log?.error(`failed to send handshake response: ${res.reason}`, {\n ...session.loggingMetadata,\n transportMessage: responseMsg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: res.reason,\n });\n this.deletePendingSession(session);\n\n return;\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: Value.Errors(\n ControlMessageHandshakeRequestSchema,\n msg.payload,\n ).flatMap(validationErrorToRiverErrors),\n },\n );\n\n return;\n }\n\n // invariant: handshake request passes all the validation\n const gotVersion = msg.payload.protocolVersion;\n if (!isAcceptedProtocolVersion(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 // invariant: must pass custom validation if defined\n let parsedMetadata: ParsedMetadata = {} as 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: Value.Errors(\n this.handshakeExtensions.schema,\n msg.payload.metadata,\n ).flatMap(validationErrorToRiverErrors),\n },\n );\n\n return;\n }\n\n const previousParsedMetadata = this.sessionHandshakeMetadata.get(\n msg.from,\n );\n\n let parsedMetadataOrFailureCode;\n try {\n parsedMetadataOrFailureCode = await this.handshakeExtensions.validate(\n msg.payload.metadata,\n previousParsedMetadata,\n msg.from,\n );\n } catch (err) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `handshake validation threw: ${coerceErrorString(err)}`,\n 'REJECTED_BY_CUSTOM_HANDLER',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n clientId: this.clientId,\n },\n );\n\n return;\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 as ParsedMetadata;\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;\n\n let oldSession = this.sessions.get(msg.from);\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 onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...noConnectionSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(noConnectionSession, { unhealthy: true });\n },\n },\n );\n\n oldSession = noConnectionSession;\n this.updateSession(oldSession);\n }\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\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 const res = session.sendHandshake(responseMsg);\n if (!res.ok) {\n this.log?.error(`failed to send handshake response: ${res.reason}`, {\n ...session.loggingMetadata,\n transportMessage: responseMsg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: res.reason,\n });\n this.deletePendingSession(session);\n\n return;\n }\n\n // transition\n this.pendingSessions.delete(session);\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) => {\n this.handleMsg(msg);\n },\n onRehandshake: (msg) => {\n this.handleRehandshakeMessage(msg);\n },\n onRehandshakeTimeout: () => {\n this.teardownForFailedRehandshake(\n connectedSession,\n 're-handshake timed out',\n );\n },\n onInvalidMessage: (reason) => {\n this.log?.error(`invalid message: ${reason}`, {\n ...connectedSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.InvalidMessage,\n message: reason,\n });\n this.deleteSession(connectedSession, { unhealthy: true });\n },\n onMessageSendFailure: (msg, reason) => {\n this.log?.error(`failed to send message: ${reason}`, {\n ...connectedSession.loggingMetadata,\n transportMessage: msg,\n });\n\n this.protocolError({\n type: ProtocolError.MessageSendFailure,\n message: reason,\n });\n this.deleteSession(connectedSession, { unhealthy: true });\n },\n },\n gotVersion,\n );\n\n const bufferSendRes = connectedSession.sendBufferedMessages();\n if (!bufferSendRes.ok) {\n return;\n }\n\n this.storeSessionMetadata(connectedSession, parsedMetadata);\n if (oldSession) {\n this.updateSession(connectedSession);\n } else {\n this.createSession(connectedSession);\n }\n\n connectedSession.startActiveHeartbeat();\n }\n}\n","import { TelemetryInfo } from '../tracing';\nimport { MessageMetadata } from '../logging';\nimport { generateId } from './id';\n\n/**\n * A connection is the actual raw underlying transport connection.\n * It's responsible for dispatching to/from the actual connection itself\n * This should be instantiated as soon as the client/server has a connection\n * It's tied to the lifecycle of the underlying transport connection (i.e. if the WS drops, this connection should be deleted)\n */\nexport abstract class Connection {\n id: string;\n telemetry?: TelemetryInfo;\n\n constructor() {\n this.id = `conn-${generateId()}`; // for debugging, no collision safety needed\n }\n\n get loggingMetadata(): MessageMetadata {\n const metadata: MessageMetadata = { connId: this.id };\n\n if (this.telemetry?.span.isRecording()) {\n const spanContext = this.telemetry.span.spanContext();\n metadata.telemetry = {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n };\n }\n\n return metadata;\n }\n\n dataListener?: (msg: Uint8Array) => void;\n closeListener?: () => void;\n errorListener?: (err: Error) => void;\n\n onData(msg: Uint8Array) {\n this.dataListener?.(msg);\n }\n\n onError(err: Error) {\n this.errorListener?.(err);\n }\n\n onClose() {\n this.closeListener?.();\n this.telemetry?.span.end();\n }\n\n /**\n * Set the callback for when a message is received.\n * @param cb The message handler callback.\n */\n setDataListener(cb: (msg: Uint8Array) => void) {\n this.dataListener = cb;\n }\n\n removeDataListener() {\n this.dataListener = undefined;\n }\n\n /**\n * Set the callback for when the connection is closed.\n * This should also be called if an error happens and after notifying the error listener.\n * @param cb The callback to call when the connection is closed.\n */\n setCloseListener(cb: () => void): void {\n this.closeListener = cb;\n }\n\n removeCloseListener(): void {\n this.closeListener = undefined;\n }\n\n /**\n * Set the callback for when an error is received.\n * This should only be used for logging errors, all cleanup\n * should be delegated to setCloseListener.\n *\n * The implementer should take care such that the implemented\n * connection will call both the close and error callbacks\n * on an error.\n *\n * @param cb The callback to call when an error is received.\n */\n setErrorListener(cb: (err: Error) => void): void {\n this.errorListener = cb;\n }\n\n removeErrorListener(): void {\n this.errorListener = undefined;\n }\n\n /**\n * Sends a message over the connection.\n * @param msg The message to send.\n * @returns true if the message was sent, false otherwise.\n */\n abstract send(msg: Uint8Array): boolean;\n\n /**\n * Closes the connection.\n */\n abstract close(): void;\n}\n","import { Connection } from '../../connection';\nimport { WsLike } from './wslike';\n\ninterface ConnectionInfoExtras extends Record<string, unknown> {\n headers: Record<string, string>;\n}\n\nconst WS_HEALTHY_CLOSE_CODE = 1000;\n\nexport class WebSocketCloseError extends Error {\n code: number;\n reason: string;\n\n constructor(code: number, reason: string) {\n super(`websocket closed with code and reason: ${code} - ${reason}`);\n this.code = code;\n this.reason = reason;\n }\n}\n\nexport class WebSocketConnection extends Connection {\n ws: WsLike;\n extras?: ConnectionInfoExtras;\n\n get loggingMetadata() {\n const metadata = super.loggingMetadata;\n if (this.extras) {\n metadata.extras = this.extras;\n }\n\n return metadata;\n }\n\n constructor(ws: WsLike, extras?: ConnectionInfoExtras) {\n super();\n this.ws = ws;\n this.extras = extras;\n this.ws.binaryType = 'arraybuffer';\n\n // Websockets are kinda shitty, they emit error events with no\n // information other than it errored, so we have to do some extra\n // work to figure out what happened.\n let didError = false;\n this.ws.onerror = () => {\n didError = true;\n };\n\n this.ws.onclose = ({ code, reason }) => {\n if (didError) {\n const err = new WebSocketCloseError(code, reason);\n this.onError(err);\n }\n\n this.onClose();\n };\n\n this.ws.onmessage = (msg) => {\n this.onData(msg.data as Uint8Array);\n };\n }\n\n send(payload: Uint8Array) {\n try {\n this.ws.send(payload);\n\n return true;\n } catch {\n return false;\n }\n }\n\n close() {\n // we close with 1000 normal even if its not really healthy at the river level\n // if we don't specify this, it defaults to 1005 which\n // some proxies/loggers detect as an error\n this.ws.close(WS_HEALTHY_CLOSE_CODE);\n }\n}\n"],"mappings":"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","names":["SessionState","SpanStatusCode","Value","Value","SpanStatusCode","msg","SpanStatusCode","Value","Value","SpanStatusCode","msg"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Connection, T as Transport, m as ClientTransportOptions, L as LeakyBucketRateLimit, n as ClientHandshakeOptions, o as ClientSession, P as ProvidedClientTransportOptions, i as SessionNoConnection, g as SessionConnecting, h as SessionHandshaking, f as SessionConnected, p as SessionBackingOff } from './transport-
|
|
1
|
+
import { C as Connection, T as Transport, m as ClientTransportOptions, L as LeakyBucketRateLimit, n as ClientHandshakeOptions, o as ClientSession, P as ProvidedClientTransportOptions, i as SessionNoConnection, S as Session, D as DeleteSessionOptions, g as SessionConnecting, h as SessionHandshaking, f as SessionConnected, p as SessionBackingOff } from './transport-WUuMJkX5.js';
|
|
2
2
|
import { T as TransportClientId, O as OpaqueTransportMessage } from './message-DxS8db8A.js';
|
|
3
3
|
|
|
4
4
|
declare abstract class ClientTransport<ConnType extends Connection> extends Transport<ConnType> {
|
|
@@ -18,9 +18,24 @@ declare abstract class ClientTransport<ConnType extends Connection> extends Tran
|
|
|
18
18
|
* Optional handshake options for this client.
|
|
19
19
|
*/
|
|
20
20
|
handshakeExtensions?: ClientHandshakeOptions;
|
|
21
|
+
/**
|
|
22
|
+
* Handshake-metadata constructions prefetched when a connection attempt begins
|
|
23
|
+
* (only when {@link ClientHandshakeOptions.eager} is set), so the
|
|
24
|
+
* token fetch overlaps the dial instead of following it. Keyed by the peer being
|
|
25
|
+
* connected to; consumed when the handshake is sent and cleared when an attempt
|
|
26
|
+
* is abandoned.
|
|
27
|
+
*/
|
|
28
|
+
private pendingHandshakeMetadata;
|
|
21
29
|
sessions: Map<TransportClientId, ClientSession<ConnType>>;
|
|
22
30
|
constructor(clientId: TransportClientId, providedOptions?: ProvidedClientTransportOptions);
|
|
23
31
|
extendHandshake(options: ClientHandshakeOptions): void;
|
|
32
|
+
protected handleRehandshakeMessage(message: OpaqueTransportMessage): void;
|
|
33
|
+
/**
|
|
34
|
+
* Re-constructs handshake metadata via the configured handshake extension and
|
|
35
|
+
* sends it back to the server so it can replace the metadata for this session.
|
|
36
|
+
* Triggered by a server {@link ControlMessageRehandshakeRequestSchema}.
|
|
37
|
+
*/
|
|
38
|
+
private sendRehandshake;
|
|
24
39
|
/**
|
|
25
40
|
* Abstract method that creates a new {@link Connection} object.
|
|
26
41
|
*
|
|
@@ -30,6 +45,7 @@ declare abstract class ClientTransport<ConnType extends Connection> extends Tran
|
|
|
30
45
|
protected abstract createNewOutgoingConnection(to: TransportClientId): Promise<ConnType>;
|
|
31
46
|
private tryReconnecting;
|
|
32
47
|
createUnconnectedSession(to: string): SessionNoConnection;
|
|
48
|
+
protected deleteSession(session: Session<ConnType>, options?: DeleteSessionOptions): void;
|
|
33
49
|
protected onConnectingFailed(session: SessionConnecting<ConnType>): SessionNoConnection;
|
|
34
50
|
protected onConnClosed(session: SessionHandshaking<ConnType> | SessionConnected<ConnType>): SessionNoConnection;
|
|
35
51
|
protected onConnectionEstablished(session: SessionConnecting<ConnType>, conn: ConnType): SessionHandshaking<ConnType>;
|
|
@@ -47,6 +63,11 @@ declare abstract class ClientTransport<ConnType extends Connection> extends Tran
|
|
|
47
63
|
*/
|
|
48
64
|
hardDisconnect(): void;
|
|
49
65
|
protected onBackoffFinished(session: SessionBackingOff): void;
|
|
66
|
+
/**
|
|
67
|
+
* Constructs handshake metadata via the configured handshake extension, capturing
|
|
68
|
+
* a failure as a value so a prefetched result can be awaited without rejecting.
|
|
69
|
+
*/
|
|
70
|
+
private constructHandshakeMetadata;
|
|
50
71
|
private sendHandshake;
|
|
51
72
|
close(): void;
|
|
52
73
|
}
|
package/dist/codec/index.js
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
BinaryCodec,
|
|
3
3
|
CodecMessageAdapter,
|
|
4
4
|
NaiveJsonCodec
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-PWNG6VBS.js";
|
|
6
6
|
import "../chunk-CC7RN7GI.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-2HK3WK7W.js";
|
|
8
8
|
export {
|
|
9
9
|
BinaryCodec,
|
|
10
10
|
CodecMessageAdapter,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { T as Tags } from './index-DgUMnNOi.js';
|
|
2
2
|
import { P as ProtocolVersion, O as OpaqueTransportMessage } from './message-DxS8db8A.js';
|
|
3
|
-
import { C as Connection } from './transport-
|
|
3
|
+
import { C as Connection } from './transport-WUuMJkX5.js';
|
|
4
4
|
import { W as WsLike } from './wslike-Dng9H1C7.js';
|
|
5
5
|
|
|
6
6
|
interface ConnectionInfoExtras extends Record<string, unknown> {
|
package/dist/protobuf/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { ProtoCodec } from './codec.js';
|
|
2
2
|
import { DescService, DescMethod, DescMethodUnary, MessageShape, MessageInitShape, DescMethodServerStreaming, DescMethodClientStreaming, DescMethodBiDiStreaming, DescMessage } from '@bufbuild/protobuf';
|
|
3
|
-
import { C as ClientTransport } from '../client-
|
|
4
|
-
import { z as SessionId, C as Connection, n as ClientHandshakeOptions, q as ServerHandshakeOptions } from '../transport-
|
|
3
|
+
import { C as ClientTransport } from '../client-B35XYDNc.js';
|
|
4
|
+
import { z as SessionId, C as Connection, n as ClientHandshakeOptions, q as ServerHandshakeOptions } from '../transport-WUuMJkX5.js';
|
|
5
5
|
import { T as TransportClientId, f as HandshakeErrorCustomHandlerFatalResponseCodes, O as OpaqueTransportMessage } from '../message-DxS8db8A.js';
|
|
6
|
-
import { a2 as ErrorPayload, C as CANCEL_CODE, g as INVALID_REQUEST_CODE, U as UNCAUGHT_ERROR_CODE, T as UNEXPECTED_DISCONNECT_CODE, f as ErrResult, x as Result, X as Writable, s as Readable } from '../services-
|
|
7
|
-
export { E as Err, O as Ok, h as OkResult, t as ReadableBrokenError, u as ReadableResult, y as ResultUnwrapErr, z as ResultUnwrapOk } from '../services-
|
|
6
|
+
import { a2 as ErrorPayload, C as CANCEL_CODE, g as INVALID_REQUEST_CODE, U as UNCAUGHT_ERROR_CODE, T as UNEXPECTED_DISCONNECT_CODE, f as ErrResult, x as Result, X as Writable, s as Readable } from '../services-CVMD2iBf.js';
|
|
7
|
+
export { E as Err, O as Ok, h as OkResult, t as ReadableBrokenError, u as ReadableResult, y as ResultUnwrapErr, z as ResultUnwrapOk } from '../services-CVMD2iBf.js';
|
|
8
8
|
import { Span } from '@opentelemetry/api';
|
|
9
9
|
import { TUint8Array } from '../customSchemas/index.js';
|
|
10
10
|
import { Static, TSchema } from 'typebox';
|
|
11
|
-
import { S as ServerTransport } from '../server-
|
|
11
|
+
import { S as ServerTransport } from '../server-B-s_HVtb.js';
|
|
12
12
|
import '../types-BGGvYIJM.js';
|
|
13
13
|
import '../index-DgUMnNOi.js';
|
|
14
14
|
import '../adapter-Dl5Mewp3.js';
|
|
@@ -238,15 +238,15 @@ declare function createClient<Service extends DescService>(service: Service, tra
|
|
|
238
238
|
declare const HandshakeBytesSchema: TUint8Array;
|
|
239
239
|
type ProtobufHandshakeFailureCode = Static<typeof HandshakeErrorCustomHandlerFatalResponseCodes>;
|
|
240
240
|
type ConstructHandshake<Schema extends DescMessage> = () => MessageInitShape<Schema> | Promise<MessageInitShape<Schema>>;
|
|
241
|
-
type ValidateHandshake<Schema extends DescMessage, ParsedMetadata> = (metadata: MessageShape<Schema>, previousParsedMetadata?: ParsedMetadata) => ParsedMetadata | ProtobufHandshakeFailureCode | Promise<ParsedMetadata | ProtobufHandshakeFailureCode>;
|
|
241
|
+
type ValidateHandshake<Schema extends DescMessage, ParsedMetadata> = (metadata: MessageShape<Schema>, previousParsedMetadata?: ParsedMetadata, from?: TransportClientId) => ParsedMetadata | ProtobufHandshakeFailureCode | Promise<ParsedMetadata | ProtobufHandshakeFailureCode>;
|
|
242
242
|
/**
|
|
243
243
|
* Create client-side handshake options backed by a protobuf message type.
|
|
244
244
|
*/
|
|
245
|
-
declare function createClientHandshakeOptions<Schema extends DescMessage>(schema: Schema, construct: ConstructHandshake<Schema
|
|
245
|
+
declare function createClientHandshakeOptions<Schema extends DescMessage>(schema: Schema, construct: ConstructHandshake<Schema>, eager?: boolean): ClientHandshakeOptions<typeof HandshakeBytesSchema>;
|
|
246
246
|
/**
|
|
247
247
|
* Create server-side handshake options backed by a protobuf message type.
|
|
248
248
|
*/
|
|
249
|
-
declare function createServerHandshakeOptions<Schema extends DescMessage, ParsedMetadata extends object = object>(schema: Schema, validate: ValidateHandshake<Schema, ParsedMetadata
|
|
249
|
+
declare function createServerHandshakeOptions<Schema extends DescMessage, ParsedMetadata extends object = object>(schema: Schema, validate: ValidateHandshake<Schema, ParsedMetadata>, expiry?: (parsedMetadata: ParsedMetadata) => Date | undefined): ServerHandshakeOptions<typeof HandshakeBytesSchema, ParsedMetadata>;
|
|
250
250
|
|
|
251
251
|
/**
|
|
252
252
|
* An object that may implement async or sync disposal.
|
package/dist/protobuf/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
isStreamClose,
|
|
30
30
|
isStreamOpen,
|
|
31
31
|
recordRiverError
|
|
32
|
-
} from "../chunk-
|
|
32
|
+
} from "../chunk-2HK3WK7W.js";
|
|
33
33
|
|
|
34
34
|
// protobuf/client.ts
|
|
35
35
|
import { Value } from "typebox/value";
|
|
@@ -467,27 +467,29 @@ async function getSingleMessage(resReadable, log) {
|
|
|
467
467
|
|
|
468
468
|
// protobuf/handshake.ts
|
|
469
469
|
var HandshakeBytesSchema = Uint8ArrayType();
|
|
470
|
-
function createClientHandshakeOptions2(schema, construct) {
|
|
470
|
+
function createClientHandshakeOptions2(schema, construct, eager) {
|
|
471
471
|
return createClientHandshakeOptions(
|
|
472
472
|
HandshakeBytesSchema,
|
|
473
473
|
async () => {
|
|
474
474
|
const metadata = await construct();
|
|
475
475
|
return encodeMessageBytes(schema, metadata);
|
|
476
|
-
}
|
|
476
|
+
},
|
|
477
|
+
eager
|
|
477
478
|
);
|
|
478
479
|
}
|
|
479
|
-
function createServerHandshakeOptions2(schema, validate) {
|
|
480
|
+
function createServerHandshakeOptions2(schema, validate, expiry) {
|
|
480
481
|
return createServerHandshakeOptions(
|
|
481
482
|
HandshakeBytesSchema,
|
|
482
|
-
async (metadata, previousParsedMetadata) => {
|
|
483
|
+
async (metadata, previousParsedMetadata, from) => {
|
|
483
484
|
let decoded;
|
|
484
485
|
try {
|
|
485
486
|
decoded = decodeMessageBytes(schema, metadata);
|
|
486
487
|
} catch {
|
|
487
488
|
return "REJECTED_BY_CUSTOM_HANDLER";
|
|
488
489
|
}
|
|
489
|
-
return await validate(decoded, previousParsedMetadata);
|
|
490
|
-
}
|
|
490
|
+
return await validate(decoded, previousParsedMetadata, from);
|
|
491
|
+
},
|
|
492
|
+
expiry
|
|
491
493
|
);
|
|
492
494
|
}
|
|
493
495
|
|
|
@@ -969,12 +971,22 @@ var ProtobufServer = class {
|
|
|
969
971
|
});
|
|
970
972
|
}
|
|
971
973
|
};
|
|
974
|
+
const transport = this.transport;
|
|
975
|
+
const currentMetadata = () => {
|
|
976
|
+
const session = transport.sessions.get(from);
|
|
977
|
+
if (session?.id === sessionId) {
|
|
978
|
+
return transport.sessionHandshakeMetadata.get(from) ?? sessionMetadata;
|
|
979
|
+
}
|
|
980
|
+
return sessionMetadata;
|
|
981
|
+
};
|
|
972
982
|
const handlerContext = {
|
|
973
983
|
...serviceContext,
|
|
974
984
|
state: serviceState,
|
|
975
985
|
from,
|
|
976
986
|
sessionId,
|
|
977
|
-
metadata
|
|
987
|
+
get metadata() {
|
|
988
|
+
return currentMetadata();
|
|
989
|
+
},
|
|
978
990
|
span,
|
|
979
991
|
service,
|
|
980
992
|
method,
|