@replit/river 0.205.1 → 0.205.2

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 (43) hide show
  1. package/dist/{chunk-HNBYBRJ7.js → chunk-BYA34LH3.js} +2 -2
  2. package/dist/{chunk-HNBYBRJ7.js.map → chunk-BYA34LH3.js.map} +1 -1
  3. package/dist/{chunk-356UADV3.js → chunk-FCN2JPOE.js} +2 -2
  4. package/dist/{chunk-Y2N4EJYG.js → chunk-FTOB2HQN.js} +39 -6
  5. package/dist/chunk-FTOB2HQN.js.map +1 -0
  6. package/dist/{chunk-OVLZDQF4.js → chunk-TP4OJAKI.js} +4 -4
  7. package/dist/{chunk-OVLZDQF4.js.map → chunk-TP4OJAKI.js.map} +1 -1
  8. package/dist/{chunk-3XCLUGDM.js → chunk-W337YQLT.js} +3 -3
  9. package/dist/{client-c44d1c69.d.ts → client-8ab60722.d.ts} +1 -1
  10. package/dist/{connection-03726441.d.ts → connection-8ed39cb5.d.ts} +1 -1
  11. package/dist/{context-ebf88958.d.ts → context-2c4bb66b.d.ts} +11 -0
  12. package/dist/router/index.cjs +1 -1
  13. package/dist/router/index.cjs.map +1 -1
  14. package/dist/router/index.d.cts +7 -7
  15. package/dist/router/index.d.ts +7 -7
  16. package/dist/router/index.js +1 -1
  17. package/dist/{server-b169b234.d.ts → server-dc261419.d.ts} +1 -1
  18. package/dist/{services-47b312aa.d.ts → services-036dcc97.d.ts} +2 -2
  19. package/dist/testUtil/index.cjs +43 -7
  20. package/dist/testUtil/index.cjs.map +1 -1
  21. package/dist/testUtil/index.d.cts +4 -4
  22. package/dist/testUtil/index.d.ts +4 -4
  23. package/dist/testUtil/index.js +8 -5
  24. package/dist/testUtil/index.js.map +1 -1
  25. package/dist/transport/impls/ws/client.cjs +38 -5
  26. package/dist/transport/impls/ws/client.cjs.map +1 -1
  27. package/dist/transport/impls/ws/client.d.cts +3 -3
  28. package/dist/transport/impls/ws/client.d.ts +3 -3
  29. package/dist/transport/impls/ws/client.js +4 -4
  30. package/dist/transport/impls/ws/server.cjs +39 -6
  31. package/dist/transport/impls/ws/server.cjs.map +1 -1
  32. package/dist/transport/impls/ws/server.d.cts +3 -3
  33. package/dist/transport/impls/ws/server.d.ts +3 -3
  34. package/dist/transport/impls/ws/server.js +4 -4
  35. package/dist/transport/index.cjs +39 -6
  36. package/dist/transport/index.cjs.map +1 -1
  37. package/dist/transport/index.d.cts +3 -3
  38. package/dist/transport/index.d.ts +3 -3
  39. package/dist/transport/index.js +4 -4
  40. package/package.json +4 -4
  41. package/dist/chunk-Y2N4EJYG.js.map +0 -1
  42. /package/dist/{chunk-356UADV3.js.map → chunk-FCN2JPOE.js.map} +0 -0
  43. /package/dist/{chunk-3XCLUGDM.js.map → chunk-W337YQLT.js.map} +0 -0
@@ -1,9 +1,9 @@
1
- import { b as ProvidedClientTransportOptions, c as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, r as ServerHandshakeOptions, T as Transport, A as SessionOptions, m as ClientTransportOptions, e as SessionNoConnection, B as SessionBoundSendFn } from '../context-ebf88958.js';
2
- import { C as ClientTransport } from '../client-c44d1c69.js';
3
- import { S as ServerTransport } from '../server-b169b234.js';
1
+ import { b as ProvidedClientTransportOptions, c as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, r as ServerHandshakeOptions, T as Transport, A as SessionOptions, m as ClientTransportOptions, e as SessionNoConnection, B as SessionBoundSendFn } from '../context-2c4bb66b.js';
2
+ import { C as ClientTransport } from '../client-8ab60722.js';
3
+ import { S as ServerTransport } from '../server-dc261419.js';
4
4
  import { c as TransportClientId, f as PartialTransportMessage, b as OpaqueTransportMessage } from '../message-865bba35.js';
5
5
  import { Static } from '@sinclair/typebox';
6
- import { E as BaseErrorSchemaType, u as Readable, X as ReadableIterator, Y as ReadableResult } from '../services-47b312aa.js';
6
+ import { E as BaseErrorSchemaType, u as Readable, X as ReadableIterator, Y as ReadableResult } from '../services-036dcc97.js';
7
7
  import NodeWs from 'ws';
8
8
  import http from 'node:http';
9
9
  import { W as WsLike } from '../wslike-e0b32dd5.js';
@@ -1,9 +1,9 @@
1
- import { b as ProvidedClientTransportOptions, c as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, r as ServerHandshakeOptions, T as Transport, A as SessionOptions, m as ClientTransportOptions, e as SessionNoConnection, B as SessionBoundSendFn } from '../context-ebf88958.js';
2
- import { C as ClientTransport } from '../client-c44d1c69.js';
3
- import { S as ServerTransport } from '../server-b169b234.js';
1
+ import { b as ProvidedClientTransportOptions, c as ProvidedServerTransportOptions, n as ClientHandshakeOptions, C as Connection, r as ServerHandshakeOptions, T as Transport, A as SessionOptions, m as ClientTransportOptions, e as SessionNoConnection, B as SessionBoundSendFn } from '../context-2c4bb66b.js';
2
+ import { C as ClientTransport } from '../client-8ab60722.js';
3
+ import { S as ServerTransport } from '../server-dc261419.js';
4
4
  import { c as TransportClientId, f as PartialTransportMessage, b as OpaqueTransportMessage } from '../message-865bba35.js';
5
5
  import { Static } from '@sinclair/typebox';
6
- import { E as BaseErrorSchemaType, u as Readable, X as ReadableIterator, Y as ReadableResult } from '../services-47b312aa.js';
6
+ import { E as BaseErrorSchemaType, u as Readable, X as ReadableIterator, Y as ReadableResult } from '../services-036dcc97.js';
7
7
  import NodeWs from 'ws';
8
8
  import http from 'node:http';
9
9
  import { W as WsLike } from '../wslike-e0b32dd5.js';
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  ClientTransport
3
- } from "../chunk-3XCLUGDM.js";
3
+ } from "../chunk-W337YQLT.js";
4
4
  import {
5
5
  ServerTransport
6
- } from "../chunk-OVLZDQF4.js";
6
+ } from "../chunk-TP4OJAKI.js";
7
7
  import {
8
8
  Connection,
9
9
  SessionStateGraph,
10
10
  defaultClientTransportOptions,
11
11
  defaultTransportOptions
12
- } from "../chunk-Y2N4EJYG.js";
12
+ } from "../chunk-FTOB2HQN.js";
13
13
  import {
14
14
  currentProtocolVersion,
15
15
  getTracer
16
- } from "../chunk-HNBYBRJ7.js";
16
+ } from "../chunk-BYA34LH3.js";
17
17
  import "../chunk-CC7RN7GI.js";
18
18
  import "../chunk-AJGIY2UB.js";
19
19
 
@@ -116,12 +116,13 @@ var InMemoryConnection = class extends Connection {
116
116
  constructor(pipe) {
117
117
  super();
118
118
  this.conn = pipe;
119
+ this.conn.allowHalfOpen = false;
119
120
  this.conn.on("data", (data) => {
120
121
  for (const cb of this.dataListeners) {
121
122
  cb(data);
122
123
  }
123
124
  });
124
- this.conn.on("end", () => {
125
+ this.conn.on("close", () => {
125
126
  for (const cb of this.closeListeners) {
126
127
  cb();
127
128
  }
@@ -141,6 +142,7 @@ var InMemoryConnection = class extends Connection {
141
142
  close() {
142
143
  setImmediate(() => {
143
144
  this.conn.end();
145
+ this.conn.emit("close");
144
146
  });
145
147
  }
146
148
  };
@@ -206,6 +208,7 @@ function createMockTransportNetwork(opts) {
206
208
  simulatePhantomDisconnect() {
207
209
  for (const conn of Object.values(connections.get())) {
208
210
  conn.serverToClient.pause();
211
+ conn.clientToServer.pause();
209
212
  }
210
213
  },
211
214
  async restartServer() {
@@ -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 * 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\n return [side0, side1];\n}\n","import { 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';\n\nexport class InMemoryConnection extends Connection {\n conn: Duplex;\n\n constructor(pipe: Duplex) {\n super();\n this.conn = pipe;\n\n this.conn.on('data', (data: Uint8Array) => {\n for (const cb of this.dataListeners) {\n cb(data);\n }\n });\n\n this.conn.on('end', () => {\n for (const cb of this.closeListeners) {\n cb();\n }\n });\n\n this.conn.on('error', (err) => {\n for (const cb of this.errorListeners) {\n cb(err);\n }\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 });\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<MockClientTransport | MockServerTransport> = [];\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 extends ServerTransport<InMemoryConnection> {\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: (id = 'SERVER', handshakeOptions) => {\n const serverTransport = new MockServerTransport(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 }\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.end();\n conn.clientToServer.end();\n }\n },\n cleanup() {\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.end();\n conn.clientToServer.end();\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU,uBAAuB;;;ACKjC,IAAM,aAAN,MAAoB;AAAA,EACzB;AAAA,EACQ;AAAA,EAER,YAAY,cAAiB;AAC3B,SAAK,QAAQ;AACb,SAAK,YAAY,oBAAI,IAAI;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM;AACJ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,IAAwB;AAC1B,UAAM,WAAW,GAAG,KAAK,KAAK;AAC9B,SAAK,QAAQ;AACb,SAAK,UAAU,QAAQ,CAAC,aAAa,SAAS,QAAQ,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,UAA4B;AAClC,SAAK,UAAU,IAAI,QAAQ;AAC3B,aAAS,KAAK,IAAI,CAAC;AAEnB,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,gBAAwB;AAC1B,WAAO,KAAK,UAAU;AAAA,EACxB;AACF;;;ACjDA,SAAS,cAAc;AACvB,OAAO,YAAY;AAEnB,IAAM,YAAY,OAAO,UAAU;AACnC,IAAM,iBAAiB,OAAO,eAAe;AAI7C,IAAM,aAAN,cAAyB,OAAO;AAAA,EACtB;AAAA,EACR,CAAS,SAAS;AAAA,EAElB,cAAc;AACZ,UAAM;AACN,SAAK,SAAS,IAAI;AAClB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,CAAC,cAAc,EAAE,WAAuB;AACtC,QAAI,KAAK,cAAc,MAAM;AAC3B,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,UAAM,WAAW,KAAK,SAAS;AAC/B,QAAI,UAAU;AACZ,WAAK,SAAS,IAAI;AAClB,eAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,OACE,OACA,WACA,UACA;AACA,WAAO,KAAK,cAAc,IAAI;AAC9B,WAAO,KAAK,UAAU,SAAS,MAAM,IAAI;AACzC,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,SAAS,QAAQ;AAAA,IAC3B,OAAO;AACL,WAAK,UAAU,KAAK,KAAK;AACzB,WAAK,UAAU,SAAS,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,OAAO,UAA0C;AAC/C,SAAK,WAAW,GAAG,OAAO,QAAQ;AAClC,SAAK,WAAW,KAAK,IAAI;AAAA,EAC3B;AACF;AAEO,SAAS,aAAuC;AACrD,QAAM,QAAQ,IAAI,WAAW;AAC7B,QAAM,QAAQ,IAAI,WAAW;AAC7B,QAAM,cAAc,EAAE,KAAK;AAC3B,QAAM,cAAc,EAAE,KAAK;AAE3B,SAAO,CAAC,OAAO,KAAK;AACtB;;;ACnDA,SAAS,cAAc;AAEhB,IAAM,qBAAN,cAAiC,WAAW;AAAA,EACjD;AAAA,EAEA,YAAY,MAAc;AACxB,UAAM;AACN,SAAK,OAAO;AAEZ,SAAK,KAAK,GAAG,QAAQ,CAAC,SAAqB;AACzC,iBAAW,MAAM,KAAK,eAAe;AACnC,WAAG,IAAI;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,KAAK,GAAG,OAAO,MAAM;AACxB,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG;AAAA,MACL;AAAA,IACF,CAAC;AAED,SAAK,KAAK,GAAG,SAAS,CAAC,QAAQ;AAC7B,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG,GAAG;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,SAA8B;AACjC,iBAAa,MAAM;AACjB,WAAK,KAAK,MAAM,OAAO;AAAA,IACzB,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,iBAAa,MAAM;AACjB,WAAK,KAAK,IAAI;AAAA,IAChB,CAAC;AAAA,EACH;AACF;AAmBO,SAAS,2BACd,MACkB;AAElB,QAAM,cAAc,IAAI,WAA2C,CAAC,CAAC;AAErE,QAAM,aAA+D,CAAC;AAAA,EACtE,MAAM,4BAA4B,gBAAoC;AAAA,IACpE,MAAM,4BACJ,IAC6B;AAC7B,YAAM,CAAC,gBAAgB,cAAc,IAAI,WAAW;AACpD,YAAM,IAAI,QAAQ,CAAC,YAAY,aAAa,OAAO,CAAC;AAEpD,YAAM,SAAS,OAAO;AACtB,kBAAY,IAAI,CAAC,UAAU;AAAA,QACzB,GAAG;AAAA,QACH,CAAC,MAAM,GAAG;AAAA,UACR,IAAI;AAAA,UACJ;AAAA,UACA;AAAA,UACA,UAAU,KAAK;AAAA,UACf,UAAU;AAAA,UACV,SAAS;AAAA,QACX;AAAA,MACF,EAAE;AAEF,aAAO,IAAI,mBAAmB,cAAc;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAM,4BAA4B,gBAAoC;AAAA,IACpE;AAAA,IAEA,YACE,UACA,SACA;AACA,YAAM,UAAU,OAAO;AAEvB,WAAK,mBAAmB,YAAY,QAAQ,CAAC,UAAU;AAErD,mBAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AAGvC,cAAI,KAAK,WAAW,KAAK,aAAa,KAAK,UAAU;AACnD;AAAA,UACF;AAEA,eAAK,UAAU;AACf,gBAAM,aAAa,IAAI,mBAAmB,KAAK,cAAc;AAC7D,eAAK,iBAAiB,UAAU;AAAA,QAClC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,QAAQ;AACN,WAAK,iBAAiB;AACtB,YAAM,MAAM;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AAAA,IACL,oBAAoB,CAAC,IAAI,qBAAqB;AAC5C,YAAM,kBAAkB,IAAI,oBAAoB,IAAI,MAAM,MAAM;AAChE,UAAI,kBAAkB;AACpB,wBAAgB,gBAAgB,gBAAgB;AAAA,MAClD;AAEA,iBAAW,KAAK,eAAe;AAE/B,aAAO;AAAA,IACT;AAAA,IACA,oBAAoB,CAAC,KAAK,UAAU,qBAAqB;AACvD,YAAM,kBAAkB,IAAI,oBAAoB,IAAI,MAAM,MAAM;AAChE,UAAI,kBAAkB;AACpB,wBAAgB,gBAAgB,gBAAgB;AAAA,MAClD;AAEA,iBAAW,KAAK,eAAe;AAE/B,aAAO;AAAA,IACT;AAAA,IACA,4BAA4B;AAC1B,iBAAW,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,GAAG;AACnD,aAAK,eAAe,MAAM;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,MAAM,gBAAgB;AACpB,iBAAW,aAAa,YAAY;AAClC,YAAI,UAAU,aAAa;AAAU;AACrC,kBAAU,MAAM;AAAA,MAClB;AAGA,iBAAW,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,GAAG;AACnD,aAAK,eAAe,IAAI;AACxB,aAAK,eAAe,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,IACA,UAAU;AACR,iBAAW,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,GAAG;AACnD,aAAK,eAAe,IAAI;AACxB,aAAK,eAAe,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;;;AH9IO,SAAS,2BAA2B,MAAsB;AAC/D,QAAM,OAAO,IAAI,OAAO,kBAAkB,IAAI,EAAE;AAChD,OAAK,aAAa;AAElB,SAAO;AACT;AAQO,SAAS,sBAAsB,QAAqB;AACzD,SAAO,IAAI,gBAAgB,EAAE,OAAO,CAAC;AACvC;AASO,SAAS,gBAAgB,QAAsC;AACpE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAO,OAAO,MAAM;AAClB,YAAM,OAAO,OAAO,QAAQ;AAC5B,UAAI,OAAO,SAAS,YAAY,MAAM;AACpC,gBAAQ,KAAK,IAAI;AAAA,MACnB,OAAO;AACL,eAAO,IAAI,MAAM,kCAAkC,CAAC;AAAA,MACtD;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,IAAM,oBAAoB,oBAAI,QAG5B;AASK,SAAS,oBACd,UACwB;AACxB,MAAI,OAAO,kBAAkB,IAAI,QAAQ;AAIzC,MAAI,CAAC,MAAM;AACT,WAAO,SAAS,OAAO,aAAa,EAAE;AACtC,sBAAkB,IAAI,UAAU,IAAI;AAAA,EACtC;AAEA,SAAO;AACT;AAOA,eAAsB,eACpB,UAC+B;AAC/B,QAAM,MAAM,MAAM,oBAAoB,QAAQ,EAAE,KAAK;AAErD,MAAI,IAAI,MAAM;AACZ,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,SAAO,IAAI;AACb;AAOA,eAAsB,eACpB,UACA;AACA,QAAM,MAAM,MAAM,oBAAoB,QAAQ,EAAE,KAAK;AAErD,SAAO,IAAI;AACb;AAEO,SAAS,0BACd,SACkC;AAClC,SAAO;AAAA,IACL,UAAU;AAAA,IACV,cAAc;AAAA,IACd;AAAA,EACF;AACF;AAEO,SAAS,8BAA8B;AAC5C,SAAO,0BAA0B;AAAA,IAC/B,KAAK;AAAA,IACL,MAAM,KAAK,OAAO;AAAA,EACpB,CAAC;AACH;AAQA,eAAsB,eACpB,GACA,QACA,gBACA;AACA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,aAAS,UAAU;AACjB,QAAE,oBAAoB,WAAW,SAAS;AAAA,IAC5C;AAEA,aAAS,UAAU,KAA6B;AAC9C,UAAI,CAAC,UAAU,OAAO,GAAG,GAAG;AAC1B,gBAAQ;AACR,gBAAQ,IAAI,OAAO;AAAA,MACrB,WAAW,gBAAgB;AACzB,gBAAQ;AACR,eAAO,IAAI,MAAM,gCAAgC,CAAC;AAAA,MACpD;AAAA,IACF;AAEA,MAAE,iBAAiB,WAAW,SAAS;AAAA,EACzC,CAAC;AACH;AAEO,IAAM,wBAAwB;AAC9B,IAAM,8BAA8B;AAEpC,SAAS,eAAe;AAC7B,SAAO,kBAAkB,YAAY;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,MACE,6BAA6B,MAAM;AAAA,MAEnC;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAEO,SAAS,gBACd,iBACA,iBACA;AACA,QAAM,UACJ,gBAAgB,SAAS,IAAI,gBAAgB,QAAQ,KACrD,gBAAgB,yBAAyB,gBAAgB,QAAQ;AAEnE,SAAO,gBAAgB;AAAA,IACrB,gBAAgB;AAAA,IAChB,QAAQ;AAAA,EACV;AACF;AAEO,SAAS,gBACd,iBACA,iBACA;AACA,QAAM,UAAU,gBAAgB,SAAS,IAAI,gBAAgB,QAAQ;AACrE,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,mBAAmB;AAAA,EACrC;AAEA,SAAO,gBAAgB;AAAA,IACrB,gBAAgB;AAAA,IAChB,QAAQ;AAAA,EACV;AACF;AAEO,SAAS,wBACd,WACiB;AACjB,QAAM,cAAc,CAAC;AACrB,aAAW,WAAW,UAAU,SAAS,OAAO,GAAG;AACjD,QAAI,QAAQ,uCAAkC;AAC5C,kBAAY,KAAK,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,oBACd,WACQ;AACR,SAAO,wBAAwB,SAAS,EAAE;AAC5C;AAEO,SAAS,oBACd,WACA;AACA,aAAW,QAAQ,wBAAwB,SAAS,GAAG;AACrD,SAAK,MAAM;AAAA,EACb;AACF;","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 },\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 * 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\n return [side0, side1];\n}\n","import { 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';\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 for (const cb of this.dataListeners) {\n cb(data);\n }\n });\n\n this.conn.on('close', () => {\n for (const cb of this.closeListeners) {\n cb();\n }\n });\n\n this.conn.on('error', (err) => {\n for (const cb of this.errorListeners) {\n cb(err);\n }\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<MockClientTransport | MockServerTransport> = [];\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 extends ServerTransport<InMemoryConnection> {\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: (id = 'SERVER', handshakeOptions) => {\n const serverTransport = new MockServerTransport(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.end();\n conn.clientToServer.end();\n }\n },\n cleanup() {\n for (const conn of Object.values(connections.get())) {\n conn.serverToClient.end();\n conn.clientToServer.end();\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU,uBAAuB;;;ACKjC,IAAM,aAAN,MAAoB;AAAA,EACzB;AAAA,EACQ;AAAA,EAER,YAAY,cAAiB;AAC3B,SAAK,QAAQ;AACb,SAAK,YAAY,oBAAI,IAAI;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM;AACJ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,IAAwB;AAC1B,UAAM,WAAW,GAAG,KAAK,KAAK;AAC9B,SAAK,QAAQ;AACb,SAAK,UAAU,QAAQ,CAAC,aAAa,SAAS,QAAQ,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,UAA4B;AAClC,SAAK,UAAU,IAAI,QAAQ;AAC3B,aAAS,KAAK,IAAI,CAAC;AAEnB,WAAO,MAAM,KAAK,UAAU,OAAO,QAAQ;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,gBAAwB;AAC1B,WAAO,KAAK,UAAU;AAAA,EACxB;AACF;;;ACjDA,SAAS,cAAc;AACvB,OAAO,YAAY;AAEnB,IAAM,YAAY,OAAO,UAAU;AACnC,IAAM,iBAAiB,OAAO,eAAe;AAI7C,IAAM,aAAN,cAAyB,OAAO;AAAA,EACtB;AAAA,EACR,CAAS,SAAS;AAAA,EAElB,cAAc;AACZ,UAAM;AACN,SAAK,SAAS,IAAI;AAClB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,CAAC,cAAc,EAAE,WAAuB;AACtC,QAAI,KAAK,cAAc,MAAM;AAC3B,WAAK,YAAY;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,QAAQ;AACN,UAAM,WAAW,KAAK,SAAS;AAC/B,QAAI,UAAU;AACZ,WAAK,SAAS,IAAI;AAClB,eAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,OACE,OACA,WACA,UACA;AACA,WAAO,KAAK,cAAc,IAAI;AAC9B,WAAO,KAAK,UAAU,SAAS,MAAM,IAAI;AACzC,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,SAAS,QAAQ;AAAA,IAC3B,OAAO;AACL,WAAK,UAAU,KAAK,KAAK;AACzB,WAAK,UAAU,SAAS,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,OAAO,UAA0C;AAC/C,SAAK,WAAW,GAAG,OAAO,QAAQ;AAClC,SAAK,WAAW,KAAK,IAAI;AAAA,EAC3B;AACF;AAEO,SAAS,aAAuC;AACrD,QAAM,QAAQ,IAAI,WAAW;AAC7B,QAAM,QAAQ,IAAI,WAAW;AAC7B,QAAM,cAAc,EAAE,KAAK;AAC3B,QAAM,cAAc,EAAE,KAAK;AAE3B,SAAO,CAAC,OAAO,KAAK;AACtB;;;ACnDA,SAAS,cAAc;AAEhB,IAAM,qBAAN,cAAiC,WAAW;AAAA,EACjD;AAAA,EAEA,YAAY,MAAc;AACxB,UAAM;AACN,SAAK,OAAO;AACZ,SAAK,KAAK,gBAAgB;AAE1B,SAAK,KAAK,GAAG,QAAQ,CAAC,SAAqB;AACzC,iBAAW,MAAM,KAAK,eAAe;AACnC,WAAG,IAAI;AAAA,MACT;AAAA,IACF,CAAC;AAED,SAAK,KAAK,GAAG,SAAS,MAAM;AAC1B,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG;AAAA,MACL;AAAA,IACF,CAAC;AAED,SAAK,KAAK,GAAG,SAAS,CAAC,QAAQ;AAC7B,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG,GAAG;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,SAA8B;AACjC,iBAAa,MAAM;AACjB,WAAK,KAAK,MAAM,OAAO;AAAA,IACzB,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,iBAAa,MAAM;AACjB,WAAK,KAAK,IAAI;AACd,WAAK,KAAK,KAAK,OAAO;AAAA,IACxB,CAAC;AAAA,EACH;AACF;AAmBO,SAAS,2BACd,MACkB;AAElB,QAAM,cAAc,IAAI,WAA2C,CAAC,CAAC;AAErE,QAAM,aAA+D,CAAC;AAAA,EACtE,MAAM,4BAA4B,gBAAoC;AAAA,IACpE,MAAM,4BACJ,IAC6B;AAC7B,YAAM,CAAC,gBAAgB,cAAc,IAAI,WAAW;AACpD,YAAM,IAAI,QAAQ,CAAC,YAAY,aAAa,OAAO,CAAC;AAEpD,YAAM,SAAS,OAAO;AACtB,kBAAY,IAAI,CAAC,UAAU;AAAA,QACzB,GAAG;AAAA,QACH,CAAC,MAAM,GAAG;AAAA,UACR,IAAI;AAAA,UACJ;AAAA,UACA;AAAA,UACA,UAAU,KAAK;AAAA,UACf,UAAU;AAAA,UACV,SAAS;AAAA,QACX;AAAA,MACF,EAAE;AAEF,aAAO,IAAI,mBAAmB,cAAc;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAM,4BAA4B,gBAAoC;AAAA,IACpE;AAAA,IAEA,YACE,UACA,SACA;AACA,YAAM,UAAU,OAAO;AAEvB,WAAK,mBAAmB,YAAY,QAAQ,CAAC,UAAU;AAErD,mBAAW,QAAQ,OAAO,OAAO,KAAK,GAAG;AAGvC,cAAI,KAAK,WAAW,KAAK,aAAa,KAAK,UAAU;AACnD;AAAA,UACF;AAEA,eAAK,UAAU;AACf,gBAAM,aAAa,IAAI,mBAAmB,KAAK,cAAc;AAC7D,eAAK,iBAAiB,UAAU;AAAA,QAClC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,QAAQ;AACN,WAAK,iBAAiB;AACtB,YAAM,MAAM;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AAAA,IACL,oBAAoB,CAAC,IAAI,qBAAqB;AAC5C,YAAM,kBAAkB,IAAI,oBAAoB,IAAI,MAAM,MAAM;AAChE,UAAI,kBAAkB;AACpB,wBAAgB,gBAAgB,gBAAgB;AAAA,MAClD;AAEA,iBAAW,KAAK,eAAe;AAE/B,aAAO;AAAA,IACT;AAAA,IACA,oBAAoB,CAAC,KAAK,UAAU,qBAAqB;AACvD,YAAM,kBAAkB,IAAI,oBAAoB,IAAI,MAAM,MAAM;AAChE,UAAI,kBAAkB;AACpB,wBAAgB,gBAAgB,gBAAgB;AAAA,MAClD;AAEA,iBAAW,KAAK,eAAe;AAE/B,aAAO;AAAA,IACT;AAAA,IACA,4BAA4B;AAC1B,iBAAW,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,GAAG;AACnD,aAAK,eAAe,MAAM;AAC1B,aAAK,eAAe,MAAM;AAAA,MAC5B;AAAA,IACF;AAAA,IACA,MAAM,gBAAgB;AACpB,iBAAW,aAAa,YAAY;AAClC,YAAI,UAAU,aAAa;AAAU;AACrC,kBAAU,MAAM;AAAA,MAClB;AAGA,iBAAW,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,GAAG;AACnD,aAAK,eAAe,IAAI;AACxB,aAAK,eAAe,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,IACA,UAAU;AACR,iBAAW,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,GAAG;AACnD,aAAK,eAAe,IAAI;AACxB,aAAK,eAAe,IAAI;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;;;AHjJO,SAAS,2BAA2B,MAAsB;AAC/D,QAAM,OAAO,IAAI,OAAO,kBAAkB,IAAI,EAAE;AAChD,OAAK,aAAa;AAElB,SAAO;AACT;AAQO,SAAS,sBAAsB,QAAqB;AACzD,SAAO,IAAI,gBAAgB,EAAE,OAAO,CAAC;AACvC;AASO,SAAS,gBAAgB,QAAsC;AACpE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAO,OAAO,MAAM;AAClB,YAAM,OAAO,OAAO,QAAQ;AAC5B,UAAI,OAAO,SAAS,YAAY,MAAM;AACpC,gBAAQ,KAAK,IAAI;AAAA,MACnB,OAAO;AACL,eAAO,IAAI,MAAM,kCAAkC,CAAC;AAAA,MACtD;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,IAAM,oBAAoB,oBAAI,QAG5B;AASK,SAAS,oBACd,UACwB;AACxB,MAAI,OAAO,kBAAkB,IAAI,QAAQ;AAIzC,MAAI,CAAC,MAAM;AACT,WAAO,SAAS,OAAO,aAAa,EAAE;AACtC,sBAAkB,IAAI,UAAU,IAAI;AAAA,EACtC;AAEA,SAAO;AACT;AAOA,eAAsB,eACpB,UAC+B;AAC/B,QAAM,MAAM,MAAM,oBAAoB,QAAQ,EAAE,KAAK;AAErD,MAAI,IAAI,MAAM;AACZ,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AAEA,SAAO,IAAI;AACb;AAOA,eAAsB,eACpB,UACA;AACA,QAAM,MAAM,MAAM,oBAAoB,QAAQ,EAAE,KAAK;AAErD,SAAO,IAAI;AACb;AAEO,SAAS,0BACd,SACkC;AAClC,SAAO;AAAA,IACL,UAAU;AAAA,IACV,cAAc;AAAA,IACd;AAAA,EACF;AACF;AAEO,SAAS,8BAA8B;AAC5C,SAAO,0BAA0B;AAAA,IAC/B,KAAK;AAAA,IACL,MAAM,KAAK,OAAO;AAAA,EACpB,CAAC;AACH;AAQA,eAAsB,eACpB,GACA,QACA,gBACA;AACA,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,aAAS,UAAU;AACjB,QAAE,oBAAoB,WAAW,SAAS;AAAA,IAC5C;AAEA,aAAS,UAAU,KAA6B;AAC9C,UAAI,CAAC,UAAU,OAAO,GAAG,GAAG;AAC1B,gBAAQ;AACR,gBAAQ,IAAI,OAAO;AAAA,MACrB,WAAW,gBAAgB;AACzB,gBAAQ;AACR,eAAO,IAAI,MAAM,gCAAgC,CAAC;AAAA,MACpD;AAAA,IACF;AAEA,MAAE,iBAAiB,WAAW,SAAS;AAAA,EACzC,CAAC;AACH;AAEO,IAAM,wBAAwB;AAC9B,IAAM,8BAA8B;AAEpC,SAAS,eAAe;AAC7B,SAAO,kBAAkB,YAAY;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,MACE,6BAA6B,MAAM;AAAA,MAEnC;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAEO,SAAS,gBACd,iBACA,iBACA;AACA,QAAM,UACJ,gBAAgB,SAAS,IAAI,gBAAgB,QAAQ,KACrD,gBAAgB,yBAAyB,gBAAgB,QAAQ;AAEnE,SAAO,gBAAgB;AAAA,IACrB,gBAAgB;AAAA,IAChB,QAAQ;AAAA,EACV;AACF;AAEO,SAAS,gBACd,iBACA,iBACA;AACA,QAAM,UAAU,gBAAgB,SAAS,IAAI,gBAAgB,QAAQ;AACrE,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,mBAAmB;AAAA,EACrC;AAEA,SAAO,gBAAgB;AAAA,IACrB,gBAAgB;AAAA,IAChB,QAAQ;AAAA,EACV;AACF;AAEO,SAAS,wBACd,WACiB;AACjB,QAAM,cAAc,CAAC;AACrB,aAAW,WAAW,UAAU,SAAS,OAAO,GAAG;AACjD,QAAI,QAAQ,uCAAkC;AAC5C,kBAAY,KAAK,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,oBACd,WACQ;AACR,SAAO,wBAAwB,SAAS,EAAE;AAC5C;AAEO,SAAS,oBACd,WACA;AACA,aAAW,QAAQ,wBAAwB,SAAS,GAAG;AACrD,SAAK,MAAM;AAAA,EACb;AACF;","names":[]}
@@ -499,13 +499,27 @@ var IdentifiedSession = class extends CommonSession {
499
499
  * Index of the message we will send next (excluding handshake)
500
500
  */
501
501
  seq;
502
+ /**
503
+ * Last seq we sent over the wire this session (excluding handshake) and retransmissions
504
+ */
505
+ seqSent;
502
506
  /**
503
507
  * Number of unique messages we've received this session (excluding handshake)
504
508
  */
505
509
  ack;
506
510
  sendBuffer;
507
511
  constructor(props) {
508
- const { id, to, seq, ack, sendBuffer, telemetry, log, protocolVersion } = props;
512
+ const {
513
+ id,
514
+ to,
515
+ seq,
516
+ ack,
517
+ sendBuffer,
518
+ telemetry,
519
+ log,
520
+ protocolVersion,
521
+ seqSent: messagesSent
522
+ } = props;
509
523
  super(props);
510
524
  this.id = id;
511
525
  this.to = to;
@@ -515,6 +529,7 @@ var IdentifiedSession = class extends CommonSession {
515
529
  this.telemetry = telemetry;
516
530
  this.log = log;
517
531
  this.protocolVersion = protocolVersion;
532
+ this.seqSent = messagesSent;
518
533
  }
519
534
  get loggingMetadata() {
520
535
  const metadata = {
@@ -661,7 +676,7 @@ function coerceErrorString(err) {
661
676
  }
662
677
 
663
678
  // package.json
664
- var version = "0.205.1";
679
+ var version = "0.205.2";
665
680
 
666
681
  // tracing/index.ts
667
682
  function getPropagationContext(ctx) {
@@ -825,10 +840,23 @@ var SessionConnected = class extends IdentifiedSession {
825
840
  this.ack = seq + 1;
826
841
  this.heartbeatMisses = 0;
827
842
  }
843
+ assertSendOrdering(constructedMsg) {
844
+ if (constructedMsg.seq > this.seqSent + 1) {
845
+ const msg = `invariant violation: would have sent out of order msg (seq: ${constructedMsg.seq}, expected: ${this.seqSent} + 1)`;
846
+ this.log?.error(msg, {
847
+ ...this.loggingMetadata,
848
+ transportMessage: constructedMsg,
849
+ tags: ["invariant-violation"]
850
+ });
851
+ throw new Error(msg);
852
+ }
853
+ }
828
854
  send(msg) {
829
855
  const constructedMsg = this.constructMsg(msg);
856
+ this.assertSendOrdering(constructedMsg);
830
857
  this.sendBuffer.push(constructedMsg);
831
858
  this.conn.send(this.options.codec.toBuffer(constructedMsg));
859
+ this.seqSent = constructedMsg.seq;
832
860
  return constructedMsg.id;
833
861
  }
834
862
  constructor(props) {
@@ -844,7 +872,9 @@ var SessionConnected = class extends IdentifiedSession {
844
872
  this.loggingMetadata
845
873
  );
846
874
  for (const msg of this.sendBuffer) {
875
+ this.assertSendOrdering(msg);
847
876
  this.conn.send(this.options.codec.toBuffer(msg));
877
+ this.seqSent = msg.seq;
848
878
  }
849
879
  }
850
880
  this.isActivelyHeartbeating = false;
@@ -910,7 +940,7 @@ var SessionConnected = class extends IdentifiedSession {
910
940
  );
911
941
  } else {
912
942
  const reason = `received out-of-order msg, closing connection (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;
913
- this.log?.warn(reason, {
943
+ this.log?.error(reason, {
914
944
  ...this.loggingMetadata,
915
945
  transportMessage: parsedMsg,
916
946
  tags: ["invariant-violation"]
@@ -988,6 +1018,7 @@ function inheritSharedSession(session) {
988
1018
  to: session.to,
989
1019
  seq: session.seq,
990
1020
  ack: session.ack,
1021
+ seqSent: session.seqSent,
991
1022
  sendBuffer: session.sendBuffer,
992
1023
  telemetry: session.telemetry,
993
1024
  options: session.options,
@@ -1015,6 +1046,7 @@ var SessionStateGraph = {
1015
1046
  to,
1016
1047
  seq: 0,
1017
1048
  ack: 0,
1049
+ seqSent: 0,
1018
1050
  graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
1019
1051
  sendBuffer,
1020
1052
  telemetry,
@@ -1137,6 +1169,7 @@ var SessionStateGraph = {
1137
1169
  to,
1138
1170
  seq: 0,
1139
1171
  ack: 0,
1172
+ seqSent: 0,
1140
1173
  sendBuffer: [],
1141
1174
  telemetry: createSessionTelemetryInfo(
1142
1175
  pendingSession.tracer,
@@ -1396,7 +1429,7 @@ var Transport = class {
1396
1429
  });
1397
1430
  this.eventDispatcher.dispatchEvent("sessionTransition", {
1398
1431
  state: session.state,
1399
- session
1432
+ id: session.id
1400
1433
  });
1401
1434
  }
1402
1435
  updateSession(session) {
@@ -1420,7 +1453,7 @@ var Transport = class {
1420
1453
  this.sessions.set(session.to, session);
1421
1454
  this.eventDispatcher.dispatchEvent("sessionTransition", {
1422
1455
  state: session.state,
1423
- session
1456
+ id: session.id
1424
1457
  });
1425
1458
  }
1426
1459
  deleteSession(session, options) {