@replit/river 0.214.0 → 0.215.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/{adapter-DjiEwOYi.d.cts → adapter-CjgmjtUJ.d.cts} +3 -2
  2. package/dist/{adapter-Cp7_gIVA.d.ts → adapter-Dtt4bYL-.d.ts} +3 -2
  3. package/dist/{chunk-DRYCLL6N.js → chunk-3DDZCLJM.js} +182 -54
  4. package/dist/chunk-3DDZCLJM.js.map +1 -0
  5. package/dist/{chunk-LECVFB2G.js → chunk-NUGV5QWU.js} +15 -10
  6. package/dist/chunk-NUGV5QWU.js.map +1 -0
  7. package/dist/{client-DXJRow2s.d.cts → client-B9aKi9Li.d.cts} +2 -2
  8. package/dist/{client-Dw0JBBs3.d.ts → client-BOc8blGj.d.ts} +2 -2
  9. package/dist/codec/index.cjs.map +1 -1
  10. package/dist/codec/index.d.cts +3 -3
  11. package/dist/codec/index.d.ts +3 -3
  12. package/dist/codec/index.js +2 -2
  13. package/dist/{connection-Dzkqj18h.d.cts → connection-1hFoyxuX.d.cts} +3 -3
  14. package/dist/{connection-C2lYgRh0.d.ts → connection-DnMYvolf.d.ts} +3 -3
  15. package/dist/{index-D9R6UTMl.d.cts → index-Bf9PGbS4.d.cts} +1 -1
  16. package/dist/{index-CSM8soK7.d.ts → index-DiAq34gk.d.ts} +1 -1
  17. package/dist/logging/index.d.cts +2 -2
  18. package/dist/logging/index.d.ts +2 -2
  19. package/dist/{message-Dlsh5WDF.d.cts → message-DL74OqsX.d.cts} +12 -1
  20. package/dist/{message-Dlsh5WDF.d.ts → message-DL74OqsX.d.ts} +12 -1
  21. package/dist/router/index.cjs +14 -9
  22. package/dist/router/index.cjs.map +1 -1
  23. package/dist/router/index.d.cts +11 -11
  24. package/dist/router/index.d.ts +11 -11
  25. package/dist/router/index.js +1 -1
  26. package/dist/{server-BDSYa-CO.d.cts → server-BkEzDYIv.d.cts} +4 -4
  27. package/dist/{server-DFOzjvLh.d.ts → server-_bfE7LYc.d.ts} +4 -4
  28. package/dist/{services-D47_GPCH.d.ts → services-KdKBWdJr.d.ts} +4 -4
  29. package/dist/{services-DqYQvm_L.d.cts → services-zaansuuR.d.cts} +4 -4
  30. package/dist/testUtil/index.cjs +183 -53
  31. package/dist/testUtil/index.cjs.map +1 -1
  32. package/dist/testUtil/index.d.cts +7 -7
  33. package/dist/testUtil/index.d.ts +7 -7
  34. package/dist/testUtil/index.js +4 -2
  35. package/dist/testUtil/index.js.map +1 -1
  36. package/dist/transport/impls/ws/client.cjs +170 -53
  37. package/dist/transport/impls/ws/client.cjs.map +1 -1
  38. package/dist/transport/impls/ws/client.d.cts +6 -6
  39. package/dist/transport/impls/ws/client.d.ts +6 -6
  40. package/dist/transport/impls/ws/client.js +2 -2
  41. package/dist/transport/impls/ws/server.cjs +133 -53
  42. package/dist/transport/impls/ws/server.cjs.map +1 -1
  43. package/dist/transport/impls/ws/server.d.cts +6 -6
  44. package/dist/transport/impls/ws/server.d.ts +6 -6
  45. package/dist/transport/impls/ws/server.js +2 -2
  46. package/dist/transport/index.cjs +181 -53
  47. package/dist/transport/index.cjs.map +1 -1
  48. package/dist/transport/index.d.cts +7 -7
  49. package/dist/transport/index.d.ts +7 -7
  50. package/dist/transport/index.js +2 -2
  51. package/dist/{transport-pdbkDzmJ.d.ts → transport-CCBNESLA.d.cts} +72 -65
  52. package/dist/{transport-CxT7y8Qk.d.cts → transport-kW92H6x-.d.ts} +72 -65
  53. package/package.json +1 -1
  54. package/dist/chunk-DRYCLL6N.js.map +0 -1
  55. package/dist/chunk-LECVFB2G.js.map +0 -1
@@ -1,15 +1,15 @@
1
- import { b as ProvidedClientTransportOptions, c as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, q as ServerHandshakeOptions, T as Transport, x as SessionOptions, m as ClientTransportOptions, e as SessionNoConnection, y as SessionBoundSendFn } from '../transport-CxT7y8Qk.cjs';
2
- import { C as ClientTransport } from '../client-DXJRow2s.cjs';
3
- import { S as ServerTransport } from '../server-BDSYa-CO.cjs';
4
- import { c as TransportClientId, e as PartialTransportMessage, b as OpaqueTransportMessage } from '../message-Dlsh5WDF.cjs';
1
+ import { P as ProvidedClientTransportOptions, e as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, q as ServerHandshakeOptions, T as Transport, i as SessionNoConnection, x as SessionBoundSendFn, m as ClientTransportOptions, y as SessionOptions } from '../transport-CCBNESLA.cjs';
2
+ import { C as ClientTransport } from '../client-B9aKi9Li.cjs';
3
+ import { S as ServerTransport } from '../server-BkEzDYIv.cjs';
4
+ import { T as TransportClientId, e as PartialTransportMessage, O as OpaqueTransportMessage } from '../message-DL74OqsX.cjs';
5
5
  import { TSchema, Static } from '@sinclair/typebox';
6
- import { J as BaseErrorSchemaType, v as Readable, $ as ReadableIterator, w as ReadableResult } from '../services-DqYQvm_L.cjs';
6
+ import { B as BaseErrorSchemaType, q as Readable, $ as ReadableIterator, s as ReadableResult } from '../services-zaansuuR.cjs';
7
7
  import { W as WsLike } from '../wslike-Dng9H1C7.cjs';
8
8
  import NodeWs from 'ws';
9
9
  import http from 'node:http';
10
10
  import { Duplex } from 'node:stream';
11
- import '../index-D9R6UTMl.cjs';
12
- import '../adapter-DjiEwOYi.cjs';
11
+ import '../index-Bf9PGbS4.cjs';
12
+ import '../adapter-CjgmjtUJ.cjs';
13
13
  import '@opentelemetry/api';
14
14
 
15
15
  interface TestTransportOptions {
@@ -1,15 +1,15 @@
1
- import { b as ProvidedClientTransportOptions, c as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, q as ServerHandshakeOptions, T as Transport, x as SessionOptions, m as ClientTransportOptions, e as SessionNoConnection, y as SessionBoundSendFn } from '../transport-pdbkDzmJ.js';
2
- import { C as ClientTransport } from '../client-Dw0JBBs3.js';
3
- import { S as ServerTransport } from '../server-DFOzjvLh.js';
4
- import { c as TransportClientId, e as PartialTransportMessage, b as OpaqueTransportMessage } from '../message-Dlsh5WDF.js';
1
+ import { P as ProvidedClientTransportOptions, e as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, q as ServerHandshakeOptions, T as Transport, i as SessionNoConnection, x as SessionBoundSendFn, m as ClientTransportOptions, y as SessionOptions } from '../transport-kW92H6x-.js';
2
+ import { C as ClientTransport } from '../client-BOc8blGj.js';
3
+ import { S as ServerTransport } from '../server-_bfE7LYc.js';
4
+ import { T as TransportClientId, e as PartialTransportMessage, O as OpaqueTransportMessage } from '../message-DL74OqsX.js';
5
5
  import { TSchema, Static } from '@sinclair/typebox';
6
- import { J as BaseErrorSchemaType, v as Readable, $ as ReadableIterator, w as ReadableResult } from '../services-D47_GPCH.js';
6
+ import { B as BaseErrorSchemaType, q as Readable, $ as ReadableIterator, s as ReadableResult } from '../services-KdKBWdJr.js';
7
7
  import { W as WsLike } from '../wslike-Dng9H1C7.js';
8
8
  import NodeWs from 'ws';
9
9
  import http from 'node:http';
10
10
  import { Duplex } from 'node:stream';
11
- import '../index-CSM8soK7.js';
12
- import '../adapter-Cp7_gIVA.js';
11
+ import '../index-DiAq34gk.js';
12
+ import '../adapter-Dtt4bYL-.js';
13
13
  import '@opentelemetry/api';
14
14
 
15
15
  interface TestTransportOptions {
@@ -5,12 +5,12 @@ import {
5
5
  SessionStateGraph,
6
6
  defaultClientTransportOptions,
7
7
  defaultTransportOptions
8
- } from "../chunk-DRYCLL6N.js";
8
+ } from "../chunk-3DDZCLJM.js";
9
9
  import "../chunk-CC7RN7GI.js";
10
10
  import {
11
11
  currentProtocolVersion,
12
12
  getTracer
13
- } from "../chunk-LECVFB2G.js";
13
+ } from "../chunk-NUGV5QWU.js";
14
14
 
15
15
  // testUtil/index.ts
16
16
  import NodeWs, { WebSocketServer } from "ws";
@@ -308,6 +308,8 @@ function dummySession() {
308
308
  "server",
309
309
  {
310
310
  onSessionGracePeriodElapsed: () => {
311
+ },
312
+ onMessageSendFailure: () => {
311
313
  }
312
314
  },
313
315
  testingSessionOptions,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../testUtil/index.ts","../../testUtil/observable/observable.ts","../../testUtil/duplex/duplexPair.ts","../../testUtil/fixtures/mockTransport.ts"],"sourcesContent":["import NodeWs, { WebSocketServer } from 'ws';\nimport http from 'node:http';\nimport { Static } from '@sinclair/typebox';\nimport {\n OpaqueTransportMessage,\n PartialTransportMessage,\n currentProtocolVersion,\n} from '../transport/message';\nimport { Transport } from '../transport/transport';\nimport { Readable, ReadableResult, ReadableIterator } from '../router/streams';\nimport { WsLike } from '../transport/impls/ws/wslike';\nimport {\n defaultClientTransportOptions,\n defaultTransportOptions,\n} from '../transport/options';\nimport { Connection } from '../transport/connection';\nimport { SessionState } from '../transport/sessionStateMachine/common';\nimport { SessionStateGraph } from '../transport/sessionStateMachine/transitions';\nimport { BaseErrorSchemaType } from '../router/errors';\nimport { ClientTransport } from '../transport/client';\nimport { ServerTransport } from '../transport/server';\nimport { getTracer } from '../tracing';\n\nexport {\n createMockTransportNetwork,\n InMemoryConnection,\n} from './fixtures/mockTransport';\n\n/**\n * Creates a WebSocket client that connects to a local server at the specified port.\n * This should only be used for testing.\n * @param port - The port number to connect to.\n * @returns A Promise that resolves to a WebSocket instance.\n */\nexport function createLocalWebSocketClient(port: number): WsLike {\n const sock = new NodeWs(`ws://localhost:${port}`);\n sock.binaryType = 'arraybuffer';\n\n return sock;\n}\n\n/**\n * Creates a WebSocket server instance using the provided HTTP server.\n * Only used as helper for testing.\n * @param server - The HTTP server instance to use for the WebSocket server.\n * @returns A Promise that resolves to the created WebSocket server instance.\n */\nexport function createWebSocketServer(server: http.Server) {\n return new WebSocketServer({ server });\n}\n\n/**\n * Starts listening on the given server and returns the automatically allocated port number.\n * This should only be used for testing.\n * @param server - The http server to listen on.\n * @returns A promise that resolves with the allocated port number.\n * @throws An error if a port cannot be allocated.\n */\nexport function onWsServerReady(server: http.Server): Promise<number> {\n return new Promise((resolve, reject) => {\n server.listen(() => {\n const addr = server.address();\n if (typeof addr === 'object' && addr) {\n resolve(addr.port);\n } else {\n reject(new Error(\"couldn't find a port to allocate\"));\n }\n });\n });\n}\n\nconst readableIterators = new WeakMap<\n Readable<unknown, Static<BaseErrorSchemaType>>,\n ReadableIterator<unknown, Static<BaseErrorSchemaType>>\n>();\n\n/**\n * A safe way to access {@link Readble}'s iterator multiple times in test helpers.\n *\n * If there are other iteration attempts outside of the test helpers\n * (this function, {@link readNextResult}, and {@link isReadableDone})\n * it will throw an error.\n */\nexport function getReadableIterator<T, E extends Static<BaseErrorSchemaType>>(\n readable: Readable<T, E>,\n): ReadableIterator<T, E> {\n let iter = readableIterators.get(readable) as\n | ReadableIterator<T, E>\n | undefined;\n\n if (!iter) {\n iter = readable[Symbol.asyncIterator]();\n readableIterators.set(readable, iter);\n }\n\n return iter;\n}\n\n/**\n * Retrieves the next value from {@link Readable}, or throws an error if the Readable is done.\n *\n * Calling semantics are similar to {@link getReadableIterator}\n */\nexport async function readNextResult<T, E extends Static<BaseErrorSchemaType>>(\n readable: Readable<T, E>,\n): Promise<ReadableResult<T, E>> {\n const res = await getReadableIterator(readable).next();\n\n if (res.done) {\n throw new Error('readNext from a done Readable');\n }\n\n return res.value;\n}\n\n/**\n * Checks if the readable is done iterating, it consumes an iteration in the process.\n *\n * Calling semantics are similar to {@link getReadableIterator}\n */\nexport async function isReadableDone<T, E extends Static<BaseErrorSchemaType>>(\n readable: Readable<T, E>,\n) {\n const res = await getReadableIterator(readable).next();\n\n return res.done;\n}\n\nexport function payloadToTransportMessage<Payload>(\n payload: Payload,\n): PartialTransportMessage<Payload> {\n return {\n streamId: 'stream',\n controlFlags: 0,\n payload,\n };\n}\n\nexport function createDummyTransportMessage() {\n return payloadToTransportMessage({\n msg: 'cool',\n test: Math.random(),\n });\n}\n\n/**\n * Waits for a message on the transport.\n * @param {Transport} t - The transport to listen to.\n * @param filter - An optional filter function to apply to the received messages.\n * @returns A promise that resolves with the payload of the first message that passes the filter.\n */\nexport async function waitForMessage(\n t: Transport<Connection>,\n filter?: (msg: OpaqueTransportMessage) => boolean,\n rejectMismatch?: boolean,\n) {\n return new Promise((resolve, reject) => {\n function cleanup() {\n t.removeEventListener('message', onMessage);\n }\n\n function onMessage(msg: OpaqueTransportMessage) {\n if (!filter || filter(msg)) {\n cleanup();\n resolve(msg.payload);\n } else if (rejectMismatch) {\n cleanup();\n reject(new Error('message didnt match the filter'));\n }\n }\n\n t.addEventListener('message', onMessage);\n });\n}\n\nexport const testingSessionOptions = defaultTransportOptions;\nexport const testingClientSessionOptions = defaultClientTransportOptions;\n\nexport function dummySession() {\n return SessionStateGraph.entrypoints.NoConnection(\n 'client',\n 'server',\n {\n onSessionGracePeriodElapsed: () => {\n /* noop */\n },\n },\n testingSessionOptions,\n currentProtocolVersion,\n getTracer(),\n );\n}\n\nexport function getClientSendFn(\n clientTransport: ClientTransport<Connection>,\n serverTransport: ServerTransport<Connection>,\n) {\n const session =\n clientTransport.sessions.get(serverTransport.clientId) ??\n clientTransport.createUnconnectedSession(serverTransport.clientId);\n\n return clientTransport.getSessionBoundSendFn(\n serverTransport.clientId,\n session.id,\n );\n}\n\nexport function getServerSendFn(\n serverTransport: ServerTransport<Connection>,\n clientTransport: ClientTransport<Connection>,\n) {\n const session = serverTransport.sessions.get(clientTransport.clientId);\n if (!session) {\n throw new Error('session not found');\n }\n\n return serverTransport.getSessionBoundSendFn(\n clientTransport.clientId,\n session.id,\n );\n}\n\nexport function getTransportConnections<ConnType extends Connection>(\n transport: Transport<ConnType>,\n): Array<ConnType> {\n const connections = [];\n for (const session of transport.sessions.values()) {\n if (session.state === SessionState.Connected) {\n connections.push(session.conn);\n }\n }\n\n return connections;\n}\n\nexport function numberOfConnections<ConnType extends Connection>(\n transport: Transport<ConnType>,\n): number {\n return getTransportConnections(transport).length;\n}\n\nexport function closeAllConnections<ConnType extends Connection>(\n transport: Transport<ConnType>,\n) {\n for (const conn of getTransportConnections(transport)) {\n conn.close();\n }\n}\n\n/**\n * Wraps a partial context object in a proxy that throws when accessing\n * properties that weren't provided. This is useful for test contexts where\n * you only want to mock the dependencies a test actually uses.\n *\n * Symbols and `then` are allowed through without throwing — river checks\n * for `Symbol.asyncDispose` / `Symbol.dispose` on context values during\n * `server.close()`, and `then` is checked by the JS runtime when the\n * proxy is returned from an async function.\n *\n * @example\n * ```ts\n * const ctx = createPartialContext<MyContext>({\n * database: mockDb,\n * // accessing ctx.redis will throw\n * });\n *\n * const server = createServer(transport, services, {\n * extendedContext: ctx,\n * });\n * ```\n */\nexport function createPartialContext<T extends Record<string, unknown>>(\n partial: Partial<T>,\n): T {\n return new Proxy(partial as T, {\n get(target, prop, receiver) {\n if (prop in target) {\n return Reflect.get(target, prop, receiver);\n }\n\n if (typeof prop === 'string' && prop !== 'then') {\n throw new Error(\n `${prop} is not mocked in the test context. Provide it via createPartialContext if your test needs it.`,\n );\n }\n\n return undefined;\n },\n });\n}\n","/**\n * Represents an observable value that can be subscribed to for changes.\n * This should only be used in tests\n * @template T - The type of the value being observed.\n */\nexport class Observable<T> {\n value: T;\n private listeners: Set<(val: T) => void>;\n\n constructor(initialValue: T) {\n this.value = initialValue;\n this.listeners = new Set();\n }\n\n /**\n * Gets the current value of the observable.\n */\n get() {\n return this.value;\n }\n\n /**\n * Sets the current value of the observable. All listeners will get an update with this value.\n * @param newValue - The new value to set.\n */\n set(tx: (preValue: T) => T) {\n const newValue = tx(this.value);\n this.value = newValue;\n this.listeners.forEach((listener) => listener(newValue));\n }\n\n /**\n * Subscribes to changes in the observable value.\n * @param listener - A callback function that will be called when the value changes.\n * @returns A function that can be called to unsubscribe from further notifications.\n */\n observe(listener: (val: T) => void) {\n this.listeners.add(listener);\n listener(this.get());\n\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Returns the number of listeners currently observing the observable\n */\n get listenerCount(): number {\n return this.listeners.size;\n }\n}\n","import { Duplex } from 'node:stream';\nimport assert from 'assert';\n\nconst kCallback = Symbol('Callback');\nconst kInitOtherSide = Symbol('InitOtherSide');\n\n// yoinked from https://github.com/nodejs/node/blob/c3a7b29e56a5ada6327ebb622ba746d022685742/lib/internal/streams/duplexpair.js#L55\n// but with types\nclass DuplexSide extends Duplex {\n private otherSide: DuplexSide | null;\n private [kCallback]: (() => void) | null;\n\n constructor() {\n super();\n this[kCallback] = null;\n this.otherSide = null;\n }\n\n [kInitOtherSide](otherSide: DuplexSide) {\n if (this.otherSide === null) {\n this.otherSide = otherSide;\n }\n }\n\n _read() {\n const callback = this[kCallback];\n if (callback) {\n this[kCallback] = null;\n callback();\n }\n }\n\n _write(\n chunk: Uint8Array,\n _encoding: BufferEncoding,\n callback: (error?: Error | null) => void,\n ) {\n assert(this.otherSide !== null);\n assert(this.otherSide[kCallback] === null);\n if (chunk.length === 0) {\n process.nextTick(callback);\n } else {\n this.otherSide.push(chunk);\n this.otherSide[kCallback] = callback;\n }\n }\n\n _final(callback: (error?: Error | null) => void) {\n this.otherSide?.on('end', callback);\n this.otherSide?.push(null);\n }\n}\n\nexport function duplexPair(): [DuplexSide, DuplexSide] {\n const side0 = new DuplexSide();\n const side1 = new DuplexSide();\n side0[kInitOtherSide](side1);\n side1[kInitOtherSide](side0);\n side0.on('close', () => {\n setImmediate(() => {\n side1.destroy();\n });\n });\n\n side1.on('close', () => {\n setImmediate(() => {\n side0.destroy();\n });\n });\n\n return [side0, side1];\n}\n","import { Transport, TransportClientId } from '../../transport';\nimport { ClientTransport } from '../../transport/client';\nimport { Connection } from '../../transport/connection';\nimport { ServerTransport } from '../../transport/server';\nimport { Observable } from '../observable/observable';\nimport { ProvidedServerTransportOptions } from '../../transport/options';\nimport { TestSetupHelpers, TestTransportOptions } from './transports';\nimport { Duplex } from 'node:stream';\nimport { duplexPair } from '../duplex/duplexPair';\nimport { nanoid } from 'nanoid';\nimport { TSchema } from '@sinclair/typebox';\nimport { ServerHandshakeOptions } from '../../router/handshake';\n\nexport class InMemoryConnection extends Connection {\n conn: Duplex;\n\n constructor(pipe: Duplex) {\n super();\n this.conn = pipe;\n this.conn.allowHalfOpen = false;\n\n this.conn.on('data', (data: Uint8Array) => {\n this.dataListener?.(data);\n });\n\n this.conn.on('close', () => {\n this.closeListener?.();\n });\n\n this.conn.on('error', (err) => {\n this.errorListener?.(err);\n });\n }\n\n send(payload: Uint8Array): boolean {\n setImmediate(() => {\n this.conn.write(payload);\n });\n\n return true;\n }\n\n close(): void {\n setImmediate(() => {\n this.conn.end();\n this.conn.emit('close');\n });\n }\n}\n\ninterface BidiConnection {\n id: string;\n clientToServer: Duplex;\n serverToClient: Duplex;\n clientId: TransportClientId;\n serverId: TransportClientId;\n handled: boolean;\n}\n\n// we construct a network of transports connected by node streams here\n// so that we can test the transport layer without needing to actually\n// use real network/websocket connections\n// this is useful for testing the transport layer in isolation\n// and allows us to control network conditions in a way that would be\n// difficult with real network connections (e.g. simulating a phantom\n// disconnect, .pause() vs .removeAllListeners('data'), congestion,\n// latency, differences in ws implementations between node and browsers, etc.)\nexport function createMockTransportNetwork(\n opts?: TestTransportOptions,\n): TestSetupHelpers {\n // conn id -> [client->server, server->client]\n const connections = new Observable<Record<string, BidiConnection>>({});\n\n const transports: Array<Transport<InMemoryConnection>> = [];\n class MockClientTransport extends ClientTransport<InMemoryConnection> {\n async createNewOutgoingConnection(\n to: TransportClientId,\n ): Promise<InMemoryConnection> {\n const [clientToServer, serverToClient] = duplexPair();\n await new Promise((resolve) => setImmediate(resolve));\n\n const connId = nanoid();\n connections.set((prev) => ({\n ...prev,\n [connId]: {\n id: connId,\n clientToServer,\n serverToClient,\n clientId: this.clientId,\n serverId: to,\n handled: false,\n },\n }));\n\n return new InMemoryConnection(clientToServer);\n }\n }\n\n class MockServerTransport<\n MetadataSchema extends TSchema = TSchema,\n ParsedMetadata extends object = object,\n > extends ServerTransport<\n InMemoryConnection,\n MetadataSchema,\n ParsedMetadata\n > {\n subscribeCleanup: () => void;\n\n constructor(\n clientId: TransportClientId,\n options?: ProvidedServerTransportOptions,\n ) {\n super(clientId, options);\n\n this.subscribeCleanup = connections.observe((conns) => {\n // look for any unhandled connections\n for (const conn of Object.values(conns)) {\n // if we've already handled this connection, skip it\n // or if it's not for us, skip it\n if (conn.handled || conn.serverId !== this.clientId) {\n continue;\n }\n\n conn.handled = true;\n const connection = new InMemoryConnection(conn.serverToClient);\n this.handleConnection(connection);\n }\n });\n }\n\n close() {\n this.subscribeCleanup();\n super.close();\n }\n }\n\n return {\n getClientTransport: (id, handshakeOptions) => {\n const clientTransport = new MockClientTransport(id, opts?.client);\n if (handshakeOptions) {\n clientTransport.extendHandshake(handshakeOptions);\n }\n\n transports.push(clientTransport);\n\n return clientTransport;\n },\n getServerTransport: <\n MetadataSchema extends TSchema = TSchema,\n ParsedMetadata extends object = object,\n >(\n id = 'SERVER',\n handshakeOptions:\n | ServerHandshakeOptions<MetadataSchema, ParsedMetadata>\n | undefined,\n ) => {\n const serverTransport = new MockServerTransport<\n MetadataSchema,\n ParsedMetadata\n >(id, opts?.server);\n if (handshakeOptions) {\n serverTransport.extendHandshake(handshakeOptions);\n }\n\n transports.push(serverTransport);\n\n return serverTransport;\n },\n simulatePhantomDisconnect() {\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.pause();\n conn.clientToServer.pause();\n }\n },\n async restartServer() {\n for (const transport of transports) {\n if (transport.clientId !== 'SERVER') continue;\n transport.close();\n }\n\n // kill all connections while we're at it\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.destroy();\n conn.clientToServer.destroy();\n }\n },\n cleanup() {\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.destroy();\n conn.clientToServer.destroy();\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../testUtil/index.ts","../../testUtil/observable/observable.ts","../../testUtil/duplex/duplexPair.ts","../../testUtil/fixtures/mockTransport.ts"],"sourcesContent":["import NodeWs, { WebSocketServer } from 'ws';\nimport http from 'node:http';\nimport { Static } from '@sinclair/typebox';\nimport {\n OpaqueTransportMessage,\n PartialTransportMessage,\n currentProtocolVersion,\n} from '../transport/message';\nimport { Transport } from '../transport/transport';\nimport { Readable, ReadableResult, ReadableIterator } from '../router/streams';\nimport { WsLike } from '../transport/impls/ws/wslike';\nimport {\n defaultClientTransportOptions,\n defaultTransportOptions,\n} from '../transport/options';\nimport { Connection } from '../transport/connection';\nimport { SessionState } from '../transport/sessionStateMachine/common';\nimport { SessionStateGraph } from '../transport/sessionStateMachine/transitions';\nimport { BaseErrorSchemaType } from '../router/errors';\nimport { ClientTransport } from '../transport/client';\nimport { ServerTransport } from '../transport/server';\nimport { getTracer } from '../tracing';\n\nexport {\n createMockTransportNetwork,\n InMemoryConnection,\n} from './fixtures/mockTransport';\n\n/**\n * Creates a WebSocket client that connects to a local server at the specified port.\n * This should only be used for testing.\n * @param port - The port number to connect to.\n * @returns A Promise that resolves to a WebSocket instance.\n */\nexport function createLocalWebSocketClient(port: number): WsLike {\n const sock = new NodeWs(`ws://localhost:${port}`);\n sock.binaryType = 'arraybuffer';\n\n return sock;\n}\n\n/**\n * Creates a WebSocket server instance using the provided HTTP server.\n * Only used as helper for testing.\n * @param server - The HTTP server instance to use for the WebSocket server.\n * @returns A Promise that resolves to the created WebSocket server instance.\n */\nexport function createWebSocketServer(server: http.Server) {\n return new WebSocketServer({ server });\n}\n\n/**\n * Starts listening on the given server and returns the automatically allocated port number.\n * This should only be used for testing.\n * @param server - The http server to listen on.\n * @returns A promise that resolves with the allocated port number.\n * @throws An error if a port cannot be allocated.\n */\nexport function onWsServerReady(server: http.Server): Promise<number> {\n return new Promise((resolve, reject) => {\n server.listen(() => {\n const addr = server.address();\n if (typeof addr === 'object' && addr) {\n resolve(addr.port);\n } else {\n reject(new Error(\"couldn't find a port to allocate\"));\n }\n });\n });\n}\n\nconst readableIterators = new WeakMap<\n Readable<unknown, Static<BaseErrorSchemaType>>,\n ReadableIterator<unknown, Static<BaseErrorSchemaType>>\n>();\n\n/**\n * A safe way to access {@link Readble}'s iterator multiple times in test helpers.\n *\n * If there are other iteration attempts outside of the test helpers\n * (this function, {@link readNextResult}, and {@link isReadableDone})\n * it will throw an error.\n */\nexport function getReadableIterator<T, E extends Static<BaseErrorSchemaType>>(\n readable: Readable<T, E>,\n): ReadableIterator<T, E> {\n let iter = readableIterators.get(readable) as\n | ReadableIterator<T, E>\n | undefined;\n\n if (!iter) {\n iter = readable[Symbol.asyncIterator]();\n readableIterators.set(readable, iter);\n }\n\n return iter;\n}\n\n/**\n * Retrieves the next value from {@link Readable}, or throws an error if the Readable is done.\n *\n * Calling semantics are similar to {@link getReadableIterator}\n */\nexport async function readNextResult<T, E extends Static<BaseErrorSchemaType>>(\n readable: Readable<T, E>,\n): Promise<ReadableResult<T, E>> {\n const res = await getReadableIterator(readable).next();\n\n if (res.done) {\n throw new Error('readNext from a done Readable');\n }\n\n return res.value;\n}\n\n/**\n * Checks if the readable is done iterating, it consumes an iteration in the process.\n *\n * Calling semantics are similar to {@link getReadableIterator}\n */\nexport async function isReadableDone<T, E extends Static<BaseErrorSchemaType>>(\n readable: Readable<T, E>,\n) {\n const res = await getReadableIterator(readable).next();\n\n return res.done;\n}\n\nexport function payloadToTransportMessage<Payload>(\n payload: Payload,\n): PartialTransportMessage<Payload> {\n return {\n streamId: 'stream',\n controlFlags: 0,\n payload,\n };\n}\n\nexport function createDummyTransportMessage() {\n return payloadToTransportMessage({\n msg: 'cool',\n test: Math.random(),\n });\n}\n\n/**\n * Waits for a message on the transport.\n * @param {Transport} t - The transport to listen to.\n * @param filter - An optional filter function to apply to the received messages.\n * @returns A promise that resolves with the payload of the first message that passes the filter.\n */\nexport async function waitForMessage(\n t: Transport<Connection>,\n filter?: (msg: OpaqueTransportMessage) => boolean,\n rejectMismatch?: boolean,\n) {\n return new Promise((resolve, reject) => {\n function cleanup() {\n t.removeEventListener('message', onMessage);\n }\n\n function onMessage(msg: OpaqueTransportMessage) {\n if (!filter || filter(msg)) {\n cleanup();\n resolve(msg.payload);\n } else if (rejectMismatch) {\n cleanup();\n reject(new Error('message didnt match the filter'));\n }\n }\n\n t.addEventListener('message', onMessage);\n });\n}\n\nexport const testingSessionOptions = defaultTransportOptions;\nexport const testingClientSessionOptions = defaultClientTransportOptions;\n\nexport function dummySession() {\n return SessionStateGraph.entrypoints.NoConnection(\n 'client',\n 'server',\n {\n onSessionGracePeriodElapsed: () => {\n /* noop */\n },\n onMessageSendFailure: () => {\n /* noop */\n },\n },\n testingSessionOptions,\n currentProtocolVersion,\n getTracer(),\n );\n}\n\nexport function getClientSendFn(\n clientTransport: ClientTransport<Connection>,\n serverTransport: ServerTransport<Connection>,\n) {\n const session =\n clientTransport.sessions.get(serverTransport.clientId) ??\n clientTransport.createUnconnectedSession(serverTransport.clientId);\n\n return clientTransport.getSessionBoundSendFn(\n serverTransport.clientId,\n session.id,\n );\n}\n\nexport function getServerSendFn(\n serverTransport: ServerTransport<Connection>,\n clientTransport: ClientTransport<Connection>,\n) {\n const session = serverTransport.sessions.get(clientTransport.clientId);\n if (!session) {\n throw new Error('session not found');\n }\n\n return serverTransport.getSessionBoundSendFn(\n clientTransport.clientId,\n session.id,\n );\n}\n\nexport function getTransportConnections<ConnType extends Connection>(\n transport: Transport<ConnType>,\n): Array<ConnType> {\n const connections = [];\n for (const session of transport.sessions.values()) {\n if (session.state === SessionState.Connected) {\n connections.push(session.conn);\n }\n }\n\n return connections;\n}\n\nexport function numberOfConnections<ConnType extends Connection>(\n transport: Transport<ConnType>,\n): number {\n return getTransportConnections(transport).length;\n}\n\nexport function closeAllConnections<ConnType extends Connection>(\n transport: Transport<ConnType>,\n) {\n for (const conn of getTransportConnections(transport)) {\n conn.close();\n }\n}\n\n/**\n * Wraps a partial context object in a proxy that throws when accessing\n * properties that weren't provided. This is useful for test contexts where\n * you only want to mock the dependencies a test actually uses.\n *\n * Symbols and `then` are allowed through without throwing — river checks\n * for `Symbol.asyncDispose` / `Symbol.dispose` on context values during\n * `server.close()`, and `then` is checked by the JS runtime when the\n * proxy is returned from an async function.\n *\n * @example\n * ```ts\n * const ctx = createPartialContext<MyContext>({\n * database: mockDb,\n * // accessing ctx.redis will throw\n * });\n *\n * const server = createServer(transport, services, {\n * extendedContext: ctx,\n * });\n * ```\n */\nexport function createPartialContext<T extends Record<string, unknown>>(\n partial: Partial<T>,\n): T {\n return new Proxy(partial as T, {\n get(target, prop, receiver) {\n if (prop in target) {\n return Reflect.get(target, prop, receiver);\n }\n\n if (typeof prop === 'string' && prop !== 'then') {\n throw new Error(\n `${prop} is not mocked in the test context. Provide it via createPartialContext if your test needs it.`,\n );\n }\n\n return undefined;\n },\n });\n}\n","/**\n * Represents an observable value that can be subscribed to for changes.\n * This should only be used in tests\n * @template T - The type of the value being observed.\n */\nexport class Observable<T> {\n value: T;\n private listeners: Set<(val: T) => void>;\n\n constructor(initialValue: T) {\n this.value = initialValue;\n this.listeners = new Set();\n }\n\n /**\n * Gets the current value of the observable.\n */\n get() {\n return this.value;\n }\n\n /**\n * Sets the current value of the observable. All listeners will get an update with this value.\n * @param newValue - The new value to set.\n */\n set(tx: (preValue: T) => T) {\n const newValue = tx(this.value);\n this.value = newValue;\n this.listeners.forEach((listener) => listener(newValue));\n }\n\n /**\n * Subscribes to changes in the observable value.\n * @param listener - A callback function that will be called when the value changes.\n * @returns A function that can be called to unsubscribe from further notifications.\n */\n observe(listener: (val: T) => void) {\n this.listeners.add(listener);\n listener(this.get());\n\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Returns the number of listeners currently observing the observable\n */\n get listenerCount(): number {\n return this.listeners.size;\n }\n}\n","import { Duplex } from 'node:stream';\nimport assert from 'assert';\n\nconst kCallback = Symbol('Callback');\nconst kInitOtherSide = Symbol('InitOtherSide');\n\n// yoinked from https://github.com/nodejs/node/blob/c3a7b29e56a5ada6327ebb622ba746d022685742/lib/internal/streams/duplexpair.js#L55\n// but with types\nclass DuplexSide extends Duplex {\n private otherSide: DuplexSide | null;\n private [kCallback]: (() => void) | null;\n\n constructor() {\n super();\n this[kCallback] = null;\n this.otherSide = null;\n }\n\n [kInitOtherSide](otherSide: DuplexSide) {\n if (this.otherSide === null) {\n this.otherSide = otherSide;\n }\n }\n\n _read() {\n const callback = this[kCallback];\n if (callback) {\n this[kCallback] = null;\n callback();\n }\n }\n\n _write(\n chunk: Uint8Array,\n _encoding: BufferEncoding,\n callback: (error?: Error | null) => void,\n ) {\n assert(this.otherSide !== null);\n assert(this.otherSide[kCallback] === null);\n if (chunk.length === 0) {\n process.nextTick(callback);\n } else {\n this.otherSide.push(chunk);\n this.otherSide[kCallback] = callback;\n }\n }\n\n _final(callback: (error?: Error | null) => void) {\n this.otherSide?.on('end', callback);\n this.otherSide?.push(null);\n }\n}\n\nexport function duplexPair(): [DuplexSide, DuplexSide] {\n const side0 = new DuplexSide();\n const side1 = new DuplexSide();\n side0[kInitOtherSide](side1);\n side1[kInitOtherSide](side0);\n side0.on('close', () => {\n setImmediate(() => {\n side1.destroy();\n });\n });\n\n side1.on('close', () => {\n setImmediate(() => {\n side0.destroy();\n });\n });\n\n return [side0, side1];\n}\n","import { Transport, TransportClientId } from '../../transport';\nimport { ClientTransport } from '../../transport/client';\nimport { Connection } from '../../transport/connection';\nimport { ServerTransport } from '../../transport/server';\nimport { Observable } from '../observable/observable';\nimport { ProvidedServerTransportOptions } from '../../transport/options';\nimport { TestSetupHelpers, TestTransportOptions } from './transports';\nimport { Duplex } from 'node:stream';\nimport { duplexPair } from '../duplex/duplexPair';\nimport { nanoid } from 'nanoid';\nimport { TSchema } from '@sinclair/typebox';\nimport { ServerHandshakeOptions } from '../../router/handshake';\n\nexport class InMemoryConnection extends Connection {\n conn: Duplex;\n\n constructor(pipe: Duplex) {\n super();\n this.conn = pipe;\n this.conn.allowHalfOpen = false;\n\n this.conn.on('data', (data: Uint8Array) => {\n this.dataListener?.(data);\n });\n\n this.conn.on('close', () => {\n this.closeListener?.();\n });\n\n this.conn.on('error', (err) => {\n this.errorListener?.(err);\n });\n }\n\n send(payload: Uint8Array): boolean {\n setImmediate(() => {\n this.conn.write(payload);\n });\n\n return true;\n }\n\n close(): void {\n setImmediate(() => {\n this.conn.end();\n this.conn.emit('close');\n });\n }\n}\n\ninterface BidiConnection {\n id: string;\n clientToServer: Duplex;\n serverToClient: Duplex;\n clientId: TransportClientId;\n serverId: TransportClientId;\n handled: boolean;\n}\n\n// we construct a network of transports connected by node streams here\n// so that we can test the transport layer without needing to actually\n// use real network/websocket connections\n// this is useful for testing the transport layer in isolation\n// and allows us to control network conditions in a way that would be\n// difficult with real network connections (e.g. simulating a phantom\n// disconnect, .pause() vs .removeAllListeners('data'), congestion,\n// latency, differences in ws implementations between node and browsers, etc.)\nexport function createMockTransportNetwork(\n opts?: TestTransportOptions,\n): TestSetupHelpers {\n // conn id -> [client->server, server->client]\n const connections = new Observable<Record<string, BidiConnection>>({});\n\n const transports: Array<Transport<InMemoryConnection>> = [];\n class MockClientTransport extends ClientTransport<InMemoryConnection> {\n async createNewOutgoingConnection(\n to: TransportClientId,\n ): Promise<InMemoryConnection> {\n const [clientToServer, serverToClient] = duplexPair();\n await new Promise((resolve) => setImmediate(resolve));\n\n const connId = nanoid();\n connections.set((prev) => ({\n ...prev,\n [connId]: {\n id: connId,\n clientToServer,\n serverToClient,\n clientId: this.clientId,\n serverId: to,\n handled: false,\n },\n }));\n\n return new InMemoryConnection(clientToServer);\n }\n }\n\n class MockServerTransport<\n MetadataSchema extends TSchema = TSchema,\n ParsedMetadata extends object = object,\n > extends ServerTransport<\n InMemoryConnection,\n MetadataSchema,\n ParsedMetadata\n > {\n subscribeCleanup: () => void;\n\n constructor(\n clientId: TransportClientId,\n options?: ProvidedServerTransportOptions,\n ) {\n super(clientId, options);\n\n this.subscribeCleanup = connections.observe((conns) => {\n // look for any unhandled connections\n for (const conn of Object.values(conns)) {\n // if we've already handled this connection, skip it\n // or if it's not for us, skip it\n if (conn.handled || conn.serverId !== this.clientId) {\n continue;\n }\n\n conn.handled = true;\n const connection = new InMemoryConnection(conn.serverToClient);\n this.handleConnection(connection);\n }\n });\n }\n\n close() {\n this.subscribeCleanup();\n super.close();\n }\n }\n\n return {\n getClientTransport: (id, handshakeOptions) => {\n const clientTransport = new MockClientTransport(id, opts?.client);\n if (handshakeOptions) {\n clientTransport.extendHandshake(handshakeOptions);\n }\n\n transports.push(clientTransport);\n\n return clientTransport;\n },\n getServerTransport: <\n MetadataSchema extends TSchema = TSchema,\n ParsedMetadata extends object = object,\n >(\n id = 'SERVER',\n handshakeOptions:\n | ServerHandshakeOptions<MetadataSchema, ParsedMetadata>\n | undefined,\n ) => {\n const serverTransport = new MockServerTransport<\n MetadataSchema,\n ParsedMetadata\n >(id, opts?.server);\n if (handshakeOptions) {\n serverTransport.extendHandshake(handshakeOptions);\n }\n\n transports.push(serverTransport);\n\n return serverTransport;\n },\n simulatePhantomDisconnect() {\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.pause();\n conn.clientToServer.pause();\n }\n },\n async restartServer() {\n for (const transport of transports) {\n if (transport.clientId !== 'SERVER') continue;\n transport.close();\n }\n\n // kill all connections while we're at it\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.destroy();\n conn.clientToServer.destroy();\n }\n },\n cleanup() {\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.destroy();\n conn.clientToServer.destroy();\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -295,6 +295,10 @@ var LeakyBucketRateLimit = class {
295
295
  clearInterval(this.intervalHandle);
296
296
  this.intervalHandle = void 0;
297
297
  }
298
+ resetBudget() {
299
+ this.stopLeak();
300
+ this.budgetConsumed = 0;
301
+ }
298
302
  close() {
299
303
  this.stopLeak();
300
304
  }
@@ -479,6 +483,7 @@ var IdentifiedSession = class extends CommonSession {
479
483
  telemetry;
480
484
  to;
481
485
  protocolVersion;
486
+ listeners;
482
487
  /**
483
488
  * Index of the message we will send next (excluding handshake)
484
489
  */
@@ -502,7 +507,8 @@ var IdentifiedSession = class extends CommonSession {
502
507
  telemetry,
503
508
  log,
504
509
  protocolVersion,
505
- seqSent: messagesSent
510
+ seqSent: messagesSent,
511
+ listeners
506
512
  } = props;
507
513
  super(props);
508
514
  this.id = id;
@@ -514,6 +520,7 @@ var IdentifiedSession = class extends CommonSession {
514
520
  this.log = log;
515
521
  this.protocolVersion = protocolVersion;
516
522
  this.seqSent = messagesSent;
523
+ this.listeners = listeners;
517
524
  }
518
525
  get loggingMetadata() {
519
526
  const metadata = {
@@ -530,7 +537,7 @@ var IdentifiedSession = class extends CommonSession {
530
537
  }
531
538
  return metadata;
532
539
  }
533
- constructMsg(partialMsg) {
540
+ encodeMsg(partialMsg) {
534
541
  const msg = {
535
542
  ...partialMsg,
536
543
  id: generateId(),
@@ -539,18 +546,37 @@ var IdentifiedSession = class extends CommonSession {
539
546
  seq: this.seq,
540
547
  ack: this.ack
541
548
  };
549
+ const encoded = this.codec.toBuffer(msg);
550
+ if (!encoded.ok) {
551
+ this.listeners.onMessageSendFailure(
552
+ { ...partialMsg, seq: this.seq },
553
+ encoded.reason
554
+ );
555
+ return encoded;
556
+ }
542
557
  this.seq++;
543
- return msg;
558
+ return {
559
+ ok: true,
560
+ value: {
561
+ id: msg.id,
562
+ seq: msg.seq,
563
+ msg: partialMsg,
564
+ data: encoded.value
565
+ }
566
+ };
544
567
  }
545
568
  nextSeq() {
546
569
  return this.sendBuffer.length > 0 ? this.sendBuffer[0].seq : this.seq;
547
570
  }
548
571
  send(msg) {
549
- const constructedMsg = this.constructMsg(msg);
550
- this.sendBuffer.push(constructedMsg);
572
+ const encodeResult = this.encodeMsg(msg);
573
+ if (!encodeResult.ok) {
574
+ return encodeResult;
575
+ }
576
+ this.sendBuffer.push(encodeResult.value);
551
577
  return {
552
578
  ok: true,
553
- value: constructedMsg.id
579
+ value: encodeResult.value.id
554
580
  };
555
581
  }
556
582
  _handleStateExit() {
@@ -583,23 +609,6 @@ var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
583
609
  super._handleClose();
584
610
  }
585
611
  };
586
- function sendMessage(conn, codec, msg) {
587
- const buff = codec.toBuffer(msg);
588
- if (!buff.ok) {
589
- return buff;
590
- }
591
- const sent = conn.send(buff.value);
592
- if (!sent) {
593
- return {
594
- ok: false,
595
- reason: "failed to send message"
596
- };
597
- }
598
- return {
599
- ok: true,
600
- value: msg.id
601
- };
602
- }
603
612
 
604
613
  // transport/sessionStateMachine/SessionConnecting.ts
605
614
  var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
@@ -678,7 +687,7 @@ function coerceErrorString(err) {
678
687
  }
679
688
 
680
689
  // package.json
681
- var version = "0.214.0";
690
+ var version = "0.215.1";
682
691
 
683
692
  // tracing/index.ts
684
693
  function getPropagationContext(ctx) {
@@ -761,7 +770,21 @@ var SessionWaitingForHandshake = class extends CommonSession {
761
770
  this.listeners.onHandshake(parsedMsgRes.value);
762
771
  };
763
772
  sendHandshake(msg) {
764
- return sendMessage(this.conn, this.codec, msg);
773
+ const buff = this.codec.toBuffer(msg);
774
+ if (!buff.ok) {
775
+ return buff;
776
+ }
777
+ const sent = this.conn.send(buff.value);
778
+ if (!sent) {
779
+ return {
780
+ ok: false,
781
+ reason: "failed to send handshake"
782
+ };
783
+ }
784
+ return {
785
+ ok: true,
786
+ value: msg.id
787
+ };
765
788
  }
766
789
  _handleStateExit() {
767
790
  this.conn.removeDataListener();
@@ -810,7 +833,21 @@ var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
810
833
  this.listeners.onHandshake(parsedMsgRes.value);
811
834
  };
812
835
  sendHandshake(msg) {
813
- return sendMessage(this.conn, this.codec, msg);
836
+ const buff = this.codec.toBuffer(msg);
837
+ if (!buff.ok) {
838
+ return buff;
839
+ }
840
+ const sent = this.conn.send(buff.value);
841
+ if (!sent) {
842
+ return {
843
+ ok: false,
844
+ reason: "failed to send handshake"
845
+ };
846
+ }
847
+ return {
848
+ ok: true,
849
+ value: msg.id
850
+ };
814
851
  }
815
852
  _handleStateExit() {
816
853
  super._handleStateExit();
@@ -845,28 +882,35 @@ var SessionConnected = class extends IdentifiedSession {
845
882
  }
846
883
  this.startMissingHeartbeatTimeout();
847
884
  }
848
- assertSendOrdering(constructedMsg) {
849
- if (constructedMsg.seq > this.seqSent + 1) {
850
- const msg = `invariant violation: would have sent out of order msg (seq: ${constructedMsg.seq}, expected: ${this.seqSent} + 1)`;
885
+ assertSendOrdering(encodedMsg) {
886
+ if (encodedMsg.seq > this.seqSent + 1) {
887
+ const msg = `invariant violation: would have sent out of order msg (seq: ${encodedMsg.seq}, expected: ${this.seqSent} + 1)`;
851
888
  this.log?.error(msg, {
852
889
  ...this.loggingMetadata,
853
- transportMessage: constructedMsg,
854
890
  tags: ["invariant-violation"]
855
891
  });
856
892
  throw new Error(msg);
857
893
  }
858
894
  }
859
895
  send(msg) {
860
- const constructedMsg = this.constructMsg(msg);
861
- this.assertSendOrdering(constructedMsg);
862
- this.sendBuffer.push(constructedMsg);
863
- const res = sendMessage(this.conn, this.codec, constructedMsg);
864
- if (!res.ok) {
865
- this.listeners.onMessageSendFailure(constructedMsg, res.reason);
866
- return res;
896
+ const encodeResult = this.encodeMsg(msg);
897
+ if (!encodeResult.ok) {
898
+ return encodeResult;
899
+ }
900
+ const encodedMsg = encodeResult.value;
901
+ this.assertSendOrdering(encodedMsg);
902
+ this.sendBuffer.push(encodedMsg);
903
+ const sent = this.conn.send(encodedMsg.data);
904
+ if (!sent) {
905
+ const reason = "failed to send message";
906
+ this.listeners.onMessageSendFailure(
907
+ { ...encodedMsg.msg, seq: encodedMsg.seq },
908
+ reason
909
+ );
910
+ return { ok: false, reason };
867
911
  }
868
- this.seqSent = constructedMsg.seq;
869
- return res;
912
+ this.seqSent = encodedMsg.seq;
913
+ return { ok: true, value: encodedMsg.id };
870
914
  }
871
915
  constructor(props) {
872
916
  super(props);
@@ -884,10 +928,14 @@ var SessionConnected = class extends IdentifiedSession {
884
928
  );
885
929
  for (const msg of this.sendBuffer) {
886
930
  this.assertSendOrdering(msg);
887
- const res = sendMessage(this.conn, this.codec, msg);
888
- if (!res.ok) {
889
- this.listeners.onMessageSendFailure(msg, res.reason);
890
- return res;
931
+ const sent = this.conn.send(msg.data);
932
+ if (!sent) {
933
+ const reason = "failed to send buffered message";
934
+ this.listeners.onMessageSendFailure(
935
+ { ...msg.msg, seq: msg.seq },
936
+ reason
937
+ );
938
+ return { ok: false, reason };
891
939
  }
892
940
  this.seqSent = msg.seq;
893
941
  }
@@ -1675,6 +1723,17 @@ var Transport = class {
1675
1723
  const noConnectionSession = SessionStateGraph.transition.ConnectingToNoConnection(session, {
1676
1724
  onSessionGracePeriodElapsed: () => {
1677
1725
  this.onSessionGracePeriodElapsed(noConnectionSession);
1726
+ },
1727
+ onMessageSendFailure: (msg, reason) => {
1728
+ this.log?.error(`failed to send message: ${reason}`, {
1729
+ ...noConnectionSession.loggingMetadata,
1730
+ transportMessage: msg
1731
+ });
1732
+ this.protocolError({
1733
+ type: ProtocolError.MessageSendFailure,
1734
+ message: reason
1735
+ });
1736
+ this.deleteSession(noConnectionSession, { unhealthy: true });
1678
1737
  }
1679
1738
  });
1680
1739
  this.updateSession(noConnectionSession);
@@ -1682,18 +1741,32 @@ var Transport = class {
1682
1741
  }
1683
1742
  onConnClosed(session) {
1684
1743
  let noConnectionSession;
1744
+ const listeners = {
1745
+ onSessionGracePeriodElapsed: () => {
1746
+ this.onSessionGracePeriodElapsed(noConnectionSession);
1747
+ },
1748
+ onMessageSendFailure: (msg, reason) => {
1749
+ this.log?.error(`failed to send message: ${reason}`, {
1750
+ ...noConnectionSession.loggingMetadata,
1751
+ transportMessage: msg
1752
+ });
1753
+ this.protocolError({
1754
+ type: ProtocolError.MessageSendFailure,
1755
+ message: reason
1756
+ });
1757
+ this.deleteSession(noConnectionSession, { unhealthy: true });
1758
+ }
1759
+ };
1685
1760
  if (session.state === "Handshaking" /* Handshaking */) {
1686
- noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(session, {
1687
- onSessionGracePeriodElapsed: () => {
1688
- this.onSessionGracePeriodElapsed(noConnectionSession);
1689
- }
1690
- });
1761
+ noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(
1762
+ session,
1763
+ listeners
1764
+ );
1691
1765
  } else {
1692
- noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(session, {
1693
- onSessionGracePeriodElapsed: () => {
1694
- this.onSessionGracePeriodElapsed(noConnectionSession);
1695
- }
1696
- });
1766
+ noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(
1767
+ session,
1768
+ listeners
1769
+ );
1697
1770
  }
1698
1771
  this.updateSession(noConnectionSession);
1699
1772
  return noConnectionSession;
@@ -1784,6 +1857,17 @@ var ClientTransport = class extends Transport {
1784
1857
  {
1785
1858
  onSessionGracePeriodElapsed: () => {
1786
1859
  this.onSessionGracePeriodElapsed(session);
1860
+ },
1861
+ onMessageSendFailure: (msg, reason) => {
1862
+ this.log?.error(`failed to send message: ${reason}`, {
1863
+ ...session.loggingMetadata,
1864
+ transportMessage: msg
1865
+ });
1866
+ this.protocolError({
1867
+ type: ProtocolError.MessageSendFailure,
1868
+ message: reason
1869
+ });
1870
+ this.deleteSession(session, { unhealthy: true });
1787
1871
  }
1788
1872
  },
1789
1873
  this.options,
@@ -1848,6 +1932,17 @@ var ClientTransport = class extends Transport {
1848
1932
  },
1849
1933
  onSessionGracePeriodElapsed: () => {
1850
1934
  this.onSessionGracePeriodElapsed(handshakingSession);
1935
+ },
1936
+ onMessageSendFailure: (msg, reason) => {
1937
+ this.log?.error(`failed to send message: ${reason}`, {
1938
+ ...handshakingSession.loggingMetadata,
1939
+ transportMessage: msg
1940
+ });
1941
+ this.protocolError({
1942
+ type: ProtocolError.MessageSendFailure,
1943
+ message: reason
1944
+ });
1945
+ this.deleteSession(handshakingSession, { unhealthy: true });
1851
1946
  }
1852
1947
  }
1853
1948
  );
@@ -2009,6 +2104,17 @@ var ClientTransport = class extends Transport {
2009
2104
  },
2010
2105
  onSessionGracePeriodElapsed: () => {
2011
2106
  this.onSessionGracePeriodElapsed(backingOffSession);
2107
+ },
2108
+ onMessageSendFailure: (msg, reason) => {
2109
+ this.log?.error(`failed to send message: ${reason}`, {
2110
+ ...backingOffSession.loggingMetadata,
2111
+ transportMessage: msg
2112
+ });
2113
+ this.protocolError({
2114
+ type: ProtocolError.MessageSendFailure,
2115
+ message: reason
2116
+ });
2117
+ this.deleteSession(backingOffSession, { unhealthy: true });
2012
2118
  }
2013
2119
  }
2014
2120
  );
@@ -2072,6 +2178,17 @@ var ClientTransport = class extends Transport {
2072
2178
  },
2073
2179
  onSessionGracePeriodElapsed: () => {
2074
2180
  this.onSessionGracePeriodElapsed(connectingSession);
2181
+ },
2182
+ onMessageSendFailure: (msg, reason) => {
2183
+ this.log?.error(`failed to send message: ${reason}`, {
2184
+ ...connectingSession.loggingMetadata,
2185
+ transportMessage: msg
2186
+ });
2187
+ this.protocolError({
2188
+ type: ProtocolError.MessageSendFailure,
2189
+ message: reason
2190
+ });
2191
+ this.deleteSession(connectingSession, { unhealthy: true });
2075
2192
  }
2076
2193
  }
2077
2194
  );