@replit/river 0.23.16 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +21 -20
  2. package/dist/{chunk-UDXM64QK.js → chunk-AASMR3CQ.js} +24 -18
  3. package/dist/chunk-AASMR3CQ.js.map +1 -0
  4. package/dist/chunk-JA57I7MG.js +653 -0
  5. package/dist/chunk-JA57I7MG.js.map +1 -0
  6. package/dist/chunk-KX5PQRVN.js +382 -0
  7. package/dist/chunk-KX5PQRVN.js.map +1 -0
  8. package/dist/{chunk-LTSLICON.js → chunk-KYYB4DUR.js} +68 -519
  9. package/dist/chunk-KYYB4DUR.js.map +1 -0
  10. package/dist/chunk-NLQPPDOT.js +399 -0
  11. package/dist/chunk-NLQPPDOT.js.map +1 -0
  12. package/dist/{chunk-TXSQRTZB.js → chunk-PJGGC3LV.js} +55 -41
  13. package/dist/chunk-PJGGC3LV.js.map +1 -0
  14. package/dist/chunk-RXJLI2OP.js +50 -0
  15. package/dist/chunk-RXJLI2OP.js.map +1 -0
  16. package/dist/{chunk-6LCL2ZZF.js → chunk-TAH2GVTJ.js} +1 -1
  17. package/dist/chunk-TAH2GVTJ.js.map +1 -0
  18. package/dist/chunk-ZAT3R4CU.js +277 -0
  19. package/dist/chunk-ZAT3R4CU.js.map +1 -0
  20. package/dist/{client-0926d3d6.d.ts → client-ba0d3315.d.ts} +12 -15
  21. package/dist/{connection-99a67d3e.d.ts → connection-c3a96d09.d.ts} +1 -5
  22. package/dist/connection-d33e3246.d.ts +11 -0
  23. package/dist/{handshake-75d0124f.d.ts → handshake-cdead82a.d.ts} +149 -180
  24. package/dist/logging/index.cjs.map +1 -1
  25. package/dist/logging/index.d.cts +1 -1
  26. package/dist/logging/index.d.ts +1 -1
  27. package/dist/logging/index.js +1 -1
  28. package/dist/{index-ea74cdbb.d.ts → message-e6c560fd.d.ts} +2 -2
  29. package/dist/router/index.cjs +107 -530
  30. package/dist/router/index.cjs.map +1 -1
  31. package/dist/router/index.d.cts +12 -50
  32. package/dist/router/index.d.ts +12 -50
  33. package/dist/router/index.js +2 -4
  34. package/dist/server-2ef5e6ec.d.ts +42 -0
  35. package/dist/{services-75e84a9f.d.ts → services-e1417b33.d.ts} +7 -7
  36. package/dist/transport/impls/uds/client.cjs +1242 -1223
  37. package/dist/transport/impls/uds/client.cjs.map +1 -1
  38. package/dist/transport/impls/uds/client.d.cts +4 -4
  39. package/dist/transport/impls/uds/client.d.ts +4 -4
  40. package/dist/transport/impls/uds/client.js +7 -13
  41. package/dist/transport/impls/uds/client.js.map +1 -1
  42. package/dist/transport/impls/uds/server.cjs +1301 -1151
  43. package/dist/transport/impls/uds/server.cjs.map +1 -1
  44. package/dist/transport/impls/uds/server.d.cts +4 -4
  45. package/dist/transport/impls/uds/server.d.ts +4 -4
  46. package/dist/transport/impls/uds/server.js +6 -6
  47. package/dist/transport/impls/ws/client.cjs +980 -969
  48. package/dist/transport/impls/ws/client.cjs.map +1 -1
  49. package/dist/transport/impls/ws/client.d.cts +4 -4
  50. package/dist/transport/impls/ws/client.d.ts +4 -4
  51. package/dist/transport/impls/ws/client.js +6 -7
  52. package/dist/transport/impls/ws/client.js.map +1 -1
  53. package/dist/transport/impls/ws/server.cjs +1182 -1047
  54. package/dist/transport/impls/ws/server.cjs.map +1 -1
  55. package/dist/transport/impls/ws/server.d.cts +4 -4
  56. package/dist/transport/impls/ws/server.d.ts +4 -4
  57. package/dist/transport/impls/ws/server.js +6 -6
  58. package/dist/transport/index.cjs +1434 -1360
  59. package/dist/transport/index.cjs.map +1 -1
  60. package/dist/transport/index.d.cts +4 -4
  61. package/dist/transport/index.d.ts +4 -4
  62. package/dist/transport/index.js +9 -9
  63. package/dist/util/testHelpers.cjs +743 -309
  64. package/dist/util/testHelpers.cjs.map +1 -1
  65. package/dist/util/testHelpers.d.cts +10 -7
  66. package/dist/util/testHelpers.d.ts +10 -7
  67. package/dist/util/testHelpers.js +33 -10
  68. package/dist/util/testHelpers.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/chunk-6LCL2ZZF.js.map +0 -1
  71. package/dist/chunk-JA7XGTAL.js +0 -476
  72. package/dist/chunk-JA7XGTAL.js.map +0 -1
  73. package/dist/chunk-LTSLICON.js.map +0 -1
  74. package/dist/chunk-MQCGG6KL.js +0 -335
  75. package/dist/chunk-MQCGG6KL.js.map +0 -1
  76. package/dist/chunk-R47IZD67.js +0 -59
  77. package/dist/chunk-R47IZD67.js.map +0 -1
  78. package/dist/chunk-TXSQRTZB.js.map +0 -1
  79. package/dist/chunk-UDXM64QK.js.map +0 -1
  80. package/dist/chunk-WN77AT67.js +0 -476
  81. package/dist/chunk-WN77AT67.js.map +0 -1
  82. package/dist/chunk-YXDAOVP7.js +0 -347
  83. package/dist/chunk-YXDAOVP7.js.map +0 -1
  84. package/dist/connection-d738cc08.d.ts +0 -17
  85. package/dist/server-3740c5d9.d.ts +0 -24
@@ -1 +1 @@
1
- {"version":3,"sources":["../../util/testHelpers.ts"],"sourcesContent":["import NodeWs, { WebSocketServer } from 'ws';\nimport http from 'node:http';\nimport { pushable } from 'it-pushable';\nimport {\n Err,\n PayloadType,\n Procedure,\n ServiceContext,\n ServiceContextWithTransportInfo,\n UNCAUGHT_ERROR,\n} from '../router';\nimport { RiverError, Result, RiverUncaughtSchema } from '../router/result';\nimport { Static } from '@sinclair/typebox';\nimport { nanoid } from 'nanoid';\nimport net from 'node:net';\nimport {\n OpaqueTransportMessage,\n PartialTransportMessage,\n} from '../transport/message';\nimport { coerceErrorString } from './stringify';\nimport { Connection, Session, SessionOptions } from '../transport/session';\nimport { Transport } from '../transport/transport';\nimport { WsLike } from '../transport/impls/ws/wslike';\nimport { defaultTransportOptions } from '../transport/options';\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\nexport function onUdsServeReady(\n server: net.Server,\n path: string,\n): Promise<void> {\n return new Promise<void>((resolve) => {\n server.listen(path, resolve);\n });\n}\n\n/**\n * Retrieves the next value from an async iterable iterator.\n * @param iter The async iterable iterator.\n * @returns A promise that resolves to the next value from the iterator.\n */\nexport async function iterNext<T>(iter: AsyncIterableIterator<T>) {\n return await iter.next().then((res) => res.value as T);\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\nfunction catchProcError(err: unknown) {\n const errorMsg = coerceErrorString(err);\n return Err({ code: UNCAUGHT_ERROR, message: errorMsg });\n}\n\nexport const testingSessionOptions: SessionOptions = defaultTransportOptions;\n\nexport function dummySession() {\n return new Session<Connection>(\n undefined,\n 'client',\n 'server',\n testingSessionOptions,\n );\n}\n\nfunction dummyCtx<State>(\n state: State,\n session: Session<Connection>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n): ServiceContextWithTransportInfo<State> {\n return {\n ...extendedContext,\n state,\n to: session.to,\n from: session.from,\n streamId: nanoid(),\n session,\n metadata: {},\n };\n}\n\nexport function asClientRpc<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n Init extends PayloadType | null = null,\n>(\n state: State,\n proc: Procedure<State, 'rpc', I, O, E, Init>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n return async (\n msg: Static<I>,\n ): Promise<\n Result<Static<O>, Static<E> | Static<typeof RiverUncaughtSchema>>\n > => {\n return await proc\n .handler(dummyCtx(state, session, extendedContext), msg)\n .catch(catchProcError);\n };\n}\n\nexport function asClientStream<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n Init extends PayloadType | null = null,\n>(\n state: State,\n proc: Procedure<State, 'stream', I, O, E, Init>,\n init?: Init extends PayloadType ? Static<Init> : null,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n const input = pushable<Static<I>>({ objectMode: true });\n const output = pushable<Result<Static<O>, Static<E>>>({\n objectMode: true,\n });\n\n void (async () => {\n if (init) {\n const _proc = proc as Procedure<State, 'stream', I, O, E, PayloadType>;\n await _proc\n .handler(dummyCtx(state, session, extendedContext), init, input, output)\n .catch((err: unknown) => output.push(catchProcError(err)));\n } else {\n const _proc = proc as Procedure<State, 'stream', I, O, E>;\n await _proc\n .handler(dummyCtx(state, session, extendedContext), input, output)\n .catch((err: unknown) => output.push(catchProcError(err)));\n }\n })();\n\n return [input, output] as const;\n}\n\nexport function asClientSubscription<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n>(\n state: State,\n proc: Procedure<State, 'subscription', I, O, E>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n const output = pushable<Result<Static<O>, Static<E>>>({\n objectMode: true,\n });\n\n return (msg: Static<I>) => {\n void (async () => {\n return await proc\n .handler(dummyCtx(state, session, extendedContext), msg, output)\n .catch((err: unknown) => output.push(catchProcError(err)));\n })();\n return output;\n };\n}\n\nexport function asClientUpload<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n Init extends PayloadType | null = null,\n>(\n state: State,\n proc: Procedure<State, 'upload', I, O, E, Init>,\n init?: Init extends PayloadType ? Static<Init> : null,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n const input = pushable<Static<I>>({ objectMode: true });\n if (init) {\n const _proc = proc as Procedure<State, 'upload', I, O, E, PayloadType>;\n const result = _proc\n .handler(dummyCtx(state, session, extendedContext), init, input)\n .catch(catchProcError);\n return [input, result] as const;\n } else {\n const _proc = proc as Procedure<State, 'upload', I, O, E>;\n const result = _proc\n .handler(dummyCtx(state, session, extendedContext), input)\n .catch(catchProcError);\n return [input, result] as const;\n }\n}\n\nexport const getUnixSocketPath = () => {\n // https://nodejs.org/api/net.html#identifying-paths-for-ipc-connections\n return process.platform === 'win32'\n ? `\\\\\\\\?\\\\pipe\\\\${nanoid()}`\n : `/tmp/${nanoid()}.sock`;\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../util/testHelpers.ts"],"sourcesContent":["import NodeWs, { WebSocketServer } from 'ws';\nimport http from 'node:http';\nimport { pushable } from 'it-pushable';\nimport {\n Err,\n PayloadType,\n Procedure,\n ServiceContext,\n ServiceContextWithTransportInfo,\n UNCAUGHT_ERROR,\n} from '../router';\nimport { RiverError, Result, RiverUncaughtSchema } from '../router/result';\nimport { Static } from '@sinclair/typebox';\nimport { nanoid } from 'nanoid';\nimport net from 'node:net';\nimport {\n OpaqueTransportMessage,\n PartialTransportMessage,\n} from '../transport/message';\nimport { coerceErrorString } from './stringify';\nimport { Transport } from '../transport/transport';\nimport { WsLike } from '../transport/impls/ws/wslike';\nimport { defaultTransportOptions } from '../transport/options';\nimport { generateId } from '../transport/id';\nimport { Connection } from '../transport/connection';\nimport {\n Session,\n SessionOptions,\n SessionState,\n} from '../transport/sessionStateMachine/common';\nimport { SessionStateGraph } from '../transport/sessionStateMachine';\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\nexport function onUdsServeReady(\n server: net.Server,\n path: string,\n): Promise<void> {\n return new Promise<void>((resolve) => {\n server.listen(path, resolve);\n });\n}\n\n/**\n * Retrieves the next value from an async iterable iterator.\n * @param iter The async iterable iterator.\n * @returns A promise that resolves to the next value from the iterator.\n */\nexport async function iterNext<T>(iter: AsyncIterableIterator<T>) {\n return await iter.next().then((res) => res.value as T);\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\nfunction catchProcError(err: unknown) {\n const errorMsg = coerceErrorString(err);\n return Err({ code: UNCAUGHT_ERROR, message: errorMsg });\n}\n\nexport const testingSessionOptions: SessionOptions = defaultTransportOptions;\n\nexport function dummySession() {\n return SessionStateGraph.entrypoints.NoConnection(\n 'client',\n 'server',\n {\n onSessionGracePeriodElapsed: () => {\n /* noop */\n },\n },\n testingSessionOptions,\n );\n}\n\nfunction dummyCtx<State>(\n state: State,\n session: Session<Connection>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n): ServiceContextWithTransportInfo<State> {\n return {\n ...extendedContext,\n state,\n to: session.to,\n from: session.from,\n streamId: generateId(),\n metadata: {},\n };\n}\n\nexport function asClientRpc<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n Init extends PayloadType | null = null,\n>(\n state: State,\n proc: Procedure<State, 'rpc', I, O, E, Init>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n return async (\n msg: Static<I>,\n ): Promise<\n Result<Static<O>, Static<E> | Static<typeof RiverUncaughtSchema>>\n > => {\n return await proc\n .handler(dummyCtx(state, session, extendedContext), msg)\n .catch(catchProcError);\n };\n}\n\nexport function asClientStream<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n Init extends PayloadType | null = null,\n>(\n state: State,\n proc: Procedure<State, 'stream', I, O, E, Init>,\n init?: Init extends PayloadType ? Static<Init> : null,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n const input = pushable<Static<I>>({ objectMode: true });\n const output = pushable<Result<Static<O>, Static<E>>>({\n objectMode: true,\n });\n\n void (async () => {\n if (init) {\n const _proc = proc as Procedure<State, 'stream', I, O, E, PayloadType>;\n await _proc\n .handler(dummyCtx(state, session, extendedContext), init, input, output)\n .catch((err: unknown) => output.push(catchProcError(err)));\n } else {\n const _proc = proc as Procedure<State, 'stream', I, O, E>;\n await _proc\n .handler(dummyCtx(state, session, extendedContext), input, output)\n .catch((err: unknown) => output.push(catchProcError(err)));\n }\n })();\n\n return [input, output] as const;\n}\n\nexport function asClientSubscription<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n>(\n state: State,\n proc: Procedure<State, 'subscription', I, O, E>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n const output = pushable<Result<Static<O>, Static<E>>>({\n objectMode: true,\n });\n\n return (msg: Static<I>) => {\n void (async () => {\n return await proc\n .handler(dummyCtx(state, session, extendedContext), msg, output)\n .catch((err: unknown) => output.push(catchProcError(err)));\n })();\n return output;\n };\n}\n\nexport function asClientUpload<\n State extends object,\n I extends PayloadType,\n O extends PayloadType,\n E extends RiverError,\n Init extends PayloadType | null = null,\n>(\n state: State,\n proc: Procedure<State, 'upload', I, O, E, Init>,\n init?: Init extends PayloadType ? Static<Init> : null,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n const input = pushable<Static<I>>({ objectMode: true });\n if (init) {\n const _proc = proc as Procedure<State, 'upload', I, O, E, PayloadType>;\n const result = _proc\n .handler(dummyCtx(state, session, extendedContext), init, input)\n .catch(catchProcError);\n return [input, result] as const;\n } else {\n const _proc = proc as Procedure<State, 'upload', I, O, E>;\n const result = _proc\n .handler(dummyCtx(state, session, extendedContext), input)\n .catch(catchProcError);\n return [input, result] as const;\n }\n}\n\nexport const getUnixSocketPath = () => {\n return `/tmp/${nanoid()}.sock`;\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"],"mappings":";;;;;;;;;;;;;;;;AAAA,OAAO,UAAU,uBAAuB;AAaxC,SAAS,cAAc;AAyBhB,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;AAEO,SAAS,gBACd,QACA,MACe;AACf,SAAO,IAAI,QAAc,CAAC,YAAY;AACpC,WAAO,OAAO,MAAM,OAAO;AAAA,EAC7B,CAAC;AACH;AAOA,eAAsB,SAAY,MAAgC;AAChE,SAAO,MAAM,KAAK,KAAK,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAU;AACvD;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;AAEA,SAAS,eAAe,KAAc;AACpC,QAAM,WAAW,kBAAkB,GAAG;AACtC,SAAO,IAAI,EAAE,MAAM,gBAAgB,SAAS,SAAS,CAAC;AACxD;AAEO,IAAM,wBAAwC;AAE9C,SAAS,eAAe;AAC7B,SAAO,kBAAkB,YAAY;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,MACE,6BAA6B,MAAM;AAAA,MAEnC;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,SACP,OACA,SACA,iBACwC;AACxC,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,IAAI,QAAQ;AAAA,IACZ,MAAM,QAAQ;AAAA,IACd,UAAU,WAAW;AAAA,IACrB,UAAU,CAAC;AAAA,EACb;AACF;AAEO,SAAS,YAOd,OACA,MACA,iBACA,UAA+B,aAAa,GAC5C;AACA,SAAO,OACL,QAGG;AACH,WAAO,MAAM,KACV,QAAQ,SAAS,OAAO,SAAS,eAAe,GAAG,GAAG,EACtD,MAAM,cAAc;AAAA,EACzB;AACF;AAEO,SAAS,eAOd,OACA,MACA,MACA,iBACA,UAA+B,aAAa,GAC5C;AACA,QAAM,QAAQ,SAAoB,EAAE,YAAY,KAAK,CAAC;AACtD,QAAM,SAAS,SAAuC;AAAA,IACpD,YAAY;AAAA,EACd,CAAC;AAED,QAAM,YAAY;AAChB,QAAI,MAAM;AACR,YAAM,QAAQ;AACd,YAAM,MACH,QAAQ,SAAS,OAAO,SAAS,eAAe,GAAG,MAAM,OAAO,MAAM,EACtE,MAAM,CAAC,QAAiB,OAAO,KAAK,eAAe,GAAG,CAAC,CAAC;AAAA,IAC7D,OAAO;AACL,YAAM,QAAQ;AACd,YAAM,MACH,QAAQ,SAAS,OAAO,SAAS,eAAe,GAAG,OAAO,MAAM,EAChE,MAAM,CAAC,QAAiB,OAAO,KAAK,eAAe,GAAG,CAAC,CAAC;AAAA,IAC7D;AAAA,EACF,GAAG;AAEH,SAAO,CAAC,OAAO,MAAM;AACvB;AAEO,SAAS,qBAMd,OACA,MACA,iBACA,UAA+B,aAAa,GAC5C;AACA,QAAM,SAAS,SAAuC;AAAA,IACpD,YAAY;AAAA,EACd,CAAC;AAED,SAAO,CAAC,QAAmB;AACzB,UAAM,YAAY;AAChB,aAAO,MAAM,KACV,QAAQ,SAAS,OAAO,SAAS,eAAe,GAAG,KAAK,MAAM,EAC9D,MAAM,CAAC,QAAiB,OAAO,KAAK,eAAe,GAAG,CAAC,CAAC;AAAA,IAC7D,GAAG;AACH,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAOd,OACA,MACA,MACA,iBACA,UAA+B,aAAa,GAC5C;AACA,QAAM,QAAQ,SAAoB,EAAE,YAAY,KAAK,CAAC;AACtD,MAAI,MAAM;AACR,UAAM,QAAQ;AACd,UAAM,SAAS,MACZ,QAAQ,SAAS,OAAO,SAAS,eAAe,GAAG,MAAM,KAAK,EAC9D,MAAM,cAAc;AACvB,WAAO,CAAC,OAAO,MAAM;AAAA,EACvB,OAAO;AACL,UAAM,QAAQ;AACd,UAAM,SAAS,MACZ,QAAQ,SAAS,OAAO,SAAS,eAAe,GAAG,KAAK,EACxD,MAAM,cAAc;AACvB,WAAO,CAAC,OAAO,MAAM;AAAA,EACvB;AACF;AAEO,IAAM,oBAAoB,MAAM;AACrC,SAAO,QAAQ,OAAO,CAAC;AACzB;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":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@replit/river",
3
3
  "description": "It's like tRPC but... with JSON Schema Support, duplex streaming and support for service multiplexing. Transport agnostic!",
4
- "version": "0.23.16",
4
+ "version": "0.24.0",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../logging/log.ts"],"sourcesContent":["import { ValueError } from '@sinclair/typebox/value';\nimport { OpaqueTransportMessage } from '../transport/message';\n\nconst LoggingLevels = {\n debug: -1,\n info: 0,\n warn: 1,\n error: 2,\n} as const;\nexport type LoggingLevel = keyof typeof LoggingLevels;\n\nexport type LogFn = (\n msg: string,\n ctx?: MessageMetadata,\n level?: LoggingLevel,\n) => void;\nexport type Logger = {\n [key in LoggingLevel]: (msg: string, metadata?: MessageMetadata) => void;\n};\n\nexport type Tags = 'invariant-violation';\n\nconst cleanedLogFn = (log: LogFn) => {\n return (msg: string, metadata?: MessageMetadata) => {\n // skip cloning object if metadata has no transportMessage\n if (!metadata?.transportMessage) {\n log(msg, metadata);\n return;\n }\n\n // clone metadata and clean transportMessage\n const { payload, ...rest } = metadata.transportMessage;\n metadata.transportMessage = rest;\n log(msg, metadata);\n };\n};\n\nexport type MessageMetadata = Partial<{\n protocolVersion: string;\n clientId: string;\n connectedTo: string;\n sessionId: string;\n connId: string;\n transportMessage: Partial<OpaqueTransportMessage>;\n validationErrors: Array<ValueError>;\n tags: Array<Tags>;\n telemetry: {\n traceId: string;\n spanId: string;\n };\n}>;\n\nexport class BaseLogger implements Logger {\n minLevel: LoggingLevel;\n private output: LogFn;\n\n constructor(output: LogFn, minLevel: LoggingLevel = 'info') {\n this.minLevel = minLevel;\n this.output = output;\n }\n\n debug(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {\n this.output(msg, metadata ?? {}, 'debug');\n }\n }\n\n info(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {\n this.output(msg, metadata ?? {}, 'info');\n }\n }\n\n warn(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {\n this.output(msg, metadata ?? {}, 'warn');\n }\n }\n\n error(msg: string, metadata?: MessageMetadata) {\n if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {\n this.output(msg, metadata ?? {}, 'error');\n }\n }\n}\n\nexport const stringLogger: LogFn = (msg, ctx, level = 'info') => {\n const from = ctx?.clientId ? `${ctx.clientId} -- ` : '';\n console.log(`[river:${level}] ${from}${msg}`);\n};\n\nconst colorMap = {\n debug: '\\u001b[34m',\n info: '\\u001b[32m',\n warn: '\\u001b[33m',\n error: '\\u001b[31m',\n};\n\nexport const coloredStringLogger: LogFn = (msg, ctx, level = 'info') => {\n const color = colorMap[level];\n const from = ctx?.clientId ? `${ctx.clientId} -- ` : '';\n console.log(`[river:${color}${level}\\u001b[0m] ${from}${msg}`);\n};\n\nexport const jsonLogger: LogFn = (msg, ctx, level) => {\n console.log(JSON.stringify({ msg, ctx, level }));\n};\n\nexport const createLogProxy = (log: Logger) => ({\n debug: cleanedLogFn(log.debug.bind(log)),\n info: cleanedLogFn(log.info.bind(log)),\n warn: cleanedLogFn(log.warn.bind(log)),\n error: cleanedLogFn(log.error.bind(log)),\n});\n"],"mappings":";AAGA,IAAM,gBAAgB;AAAA,EACpB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAcA,IAAM,eAAe,CAAC,QAAe;AACnC,SAAO,CAAC,KAAa,aAA+B;AAElD,QAAI,CAAC,UAAU,kBAAkB;AAC/B,UAAI,KAAK,QAAQ;AACjB;AAAA,IACF;AAGA,UAAM,EAAE,SAAS,GAAG,KAAK,IAAI,SAAS;AACtC,aAAS,mBAAmB;AAC5B,QAAI,KAAK,QAAQ;AAAA,EACnB;AACF;AAiBO,IAAM,aAAN,MAAmC;AAAA,EACxC;AAAA,EACQ;AAAA,EAER,YAAY,QAAe,WAAyB,QAAQ;AAC1D,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,KAAa,UAA4B;AAC7C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,OAAO;AACvD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,OAAO;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,KAAK,KAAa,UAA4B;AAC5C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,MAAM;AACtD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,MAAM;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,KAAK,KAAa,UAA4B;AAC5C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,MAAM;AACtD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,MAAM;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,MAAM,KAAa,UAA4B;AAC7C,QAAI,cAAc,KAAK,QAAQ,KAAK,cAAc,OAAO;AACvD,WAAK,OAAO,KAAK,YAAY,CAAC,GAAG,OAAO;AAAA,IAC1C;AAAA,EACF;AACF;AAEO,IAAM,eAAsB,CAAC,KAAK,KAAK,QAAQ,WAAW;AAC/D,QAAM,OAAO,KAAK,WAAW,GAAG,IAAI,QAAQ,SAAS;AACrD,UAAQ,IAAI,UAAU,KAAK,KAAK,IAAI,GAAG,GAAG,EAAE;AAC9C;AAEA,IAAM,WAAW;AAAA,EACf,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEO,IAAM,sBAA6B,CAAC,KAAK,KAAK,QAAQ,WAAW;AACtE,QAAM,QAAQ,SAAS,KAAK;AAC5B,QAAM,OAAO,KAAK,WAAW,GAAG,IAAI,QAAQ,SAAS;AACrD,UAAQ,IAAI,UAAU,KAAK,GAAG,KAAK,YAAc,IAAI,GAAG,GAAG,EAAE;AAC/D;AAEO,IAAM,aAAoB,CAAC,KAAK,KAAK,UAAU;AACpD,UAAQ,IAAI,KAAK,UAAU,EAAE,KAAK,KAAK,MAAM,CAAC,CAAC;AACjD;AAEO,IAAM,iBAAiB,CAAC,SAAiB;AAAA,EAC9C,OAAO,aAAa,IAAI,MAAM,KAAK,GAAG,CAAC;AAAA,EACvC,MAAM,aAAa,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EACrC,MAAM,aAAa,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EACrC,OAAO,aAAa,IAAI,MAAM,KAAK,GAAG,CAAC;AACzC;","names":[]}
@@ -1,476 +0,0 @@
1
- import {
2
- ProtocolError,
3
- Transport
4
- } from "./chunk-WN77AT67.js";
5
- import {
6
- defaultClientTransportOptions
7
- } from "./chunk-YXDAOVP7.js";
8
- import {
9
- ControlMessageHandshakeResponseSchema,
10
- SESSION_STATE_MISMATCH,
11
- coerceErrorString,
12
- getPropagationContext,
13
- handshakeRequestMessage,
14
- tracing_default
15
- } from "./chunk-TXSQRTZB.js";
16
-
17
- // transport/client.ts
18
- import { SpanStatusCode } from "@opentelemetry/api";
19
-
20
- // transport/rateLimit.ts
21
- var LeakyBucketRateLimit = class {
22
- budgetConsumed;
23
- intervalHandles;
24
- options;
25
- constructor(options) {
26
- this.options = options;
27
- this.budgetConsumed = /* @__PURE__ */ new Map();
28
- this.intervalHandles = /* @__PURE__ */ new Map();
29
- }
30
- getBackoffMs(user) {
31
- if (!this.budgetConsumed.has(user))
32
- return 0;
33
- const exponent = Math.max(0, this.getBudgetConsumed(user) - 1);
34
- const jitter = Math.floor(Math.random() * this.options.maxJitterMs);
35
- const backoffMs = Math.min(
36
- this.options.baseIntervalMs * 2 ** exponent,
37
- this.options.maxBackoffMs
38
- );
39
- return backoffMs + jitter;
40
- }
41
- get totalBudgetRestoreTime() {
42
- return this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity;
43
- }
44
- consumeBudget(user) {
45
- this.stopLeak(user);
46
- this.budgetConsumed.set(user, this.getBudgetConsumed(user) + 1);
47
- }
48
- getBudgetConsumed(user) {
49
- return this.budgetConsumed.get(user) ?? 0;
50
- }
51
- hasBudget(user) {
52
- return this.getBudgetConsumed(user) < this.options.attemptBudgetCapacity;
53
- }
54
- startRestoringBudget(user) {
55
- if (this.intervalHandles.has(user)) {
56
- return;
57
- }
58
- const restoreBudgetForUser = () => {
59
- const currentBudget = this.budgetConsumed.get(user);
60
- if (!currentBudget) {
61
- this.stopLeak(user);
62
- return;
63
- }
64
- const newBudget = currentBudget - 1;
65
- if (newBudget === 0) {
66
- this.budgetConsumed.delete(user);
67
- return;
68
- }
69
- this.budgetConsumed.set(user, newBudget);
70
- };
71
- const intervalHandle = setInterval(
72
- restoreBudgetForUser,
73
- this.options.budgetRestoreIntervalMs
74
- );
75
- this.intervalHandles.set(user, intervalHandle);
76
- }
77
- stopLeak(user) {
78
- if (!this.intervalHandles.has(user)) {
79
- return;
80
- }
81
- clearInterval(this.intervalHandles.get(user));
82
- this.intervalHandles.delete(user);
83
- }
84
- close() {
85
- for (const user of this.intervalHandles.keys()) {
86
- this.stopLeak(user);
87
- }
88
- }
89
- };
90
-
91
- // transport/client.ts
92
- import { Value } from "@sinclair/typebox/value";
93
- var ClientTransport = class extends Transport {
94
- /**
95
- * The options for this transport.
96
- */
97
- options;
98
- /**
99
- * The map of reconnect promises for each client ID.
100
- */
101
- inflightConnectionPromises;
102
- retryBudget;
103
- /**
104
- * A flag indicating whether the transport should automatically reconnect
105
- * when a connection is dropped.
106
- * Realistically, this should always be true for clients unless you are writing
107
- * tests or a special case where you don't want to reconnect.
108
- */
109
- reconnectOnConnectionDrop = true;
110
- /**
111
- * Optional handshake options for this client.
112
- */
113
- handshakeExtensions;
114
- constructor(clientId, providedOptions) {
115
- super(clientId, providedOptions);
116
- this.options = {
117
- ...defaultClientTransportOptions,
118
- ...providedOptions
119
- };
120
- this.inflightConnectionPromises = /* @__PURE__ */ new Map();
121
- this.retryBudget = new LeakyBucketRateLimit(this.options);
122
- }
123
- extendHandshake(options) {
124
- this.handshakeExtensions = options;
125
- }
126
- handleConnection(conn, to) {
127
- if (this.getStatus() !== "open")
128
- return;
129
- let session = void 0;
130
- const handshakeTimeout = setTimeout(() => {
131
- if (session)
132
- return;
133
- this.log?.warn(
134
- `connection to ${to} timed out waiting for handshake, closing`,
135
- { ...conn.loggingMetadata, clientId: this.clientId, connectedTo: to }
136
- );
137
- conn.close();
138
- }, this.options.sessionDisconnectGraceMs);
139
- const handshakeHandler = (data) => {
140
- const maybeSession = this.receiveHandshakeResponseMessage(data, conn);
141
- clearTimeout(handshakeTimeout);
142
- if (!maybeSession) {
143
- conn.close();
144
- return;
145
- } else {
146
- session = maybeSession;
147
- }
148
- conn.removeDataListener(handshakeHandler);
149
- conn.addDataListener((data2) => {
150
- const parsed = this.parseMsg(data2, conn);
151
- if (!parsed) {
152
- conn.telemetry?.span.setStatus({
153
- code: SpanStatusCode.ERROR,
154
- message: "message parse failure"
155
- });
156
- conn.close();
157
- return;
158
- }
159
- this.handleMsg(parsed, conn);
160
- });
161
- };
162
- conn.addDataListener(handshakeHandler);
163
- conn.addCloseListener(() => {
164
- if (session) {
165
- this.onDisconnect(conn, session);
166
- }
167
- const willReconnect = this.reconnectOnConnectionDrop && this.getStatus() === "open";
168
- this.log?.info(
169
- `connection to ${to} disconnected` + (willReconnect ? ", reconnecting" : ""),
170
- {
171
- ...conn.loggingMetadata,
172
- ...session?.loggingMetadata,
173
- clientId: this.clientId,
174
- connectedTo: to
175
- }
176
- );
177
- this.inflightConnectionPromises.delete(to);
178
- if (this.reconnectOnConnectionDrop) {
179
- void this.connect(to);
180
- }
181
- });
182
- conn.addErrorListener((err) => {
183
- conn.telemetry?.span.setStatus({
184
- code: SpanStatusCode.ERROR,
185
- message: "connection error"
186
- });
187
- this.log?.warn(
188
- `error in connection to ${to}: ${coerceErrorString(err)}`,
189
- {
190
- ...conn.loggingMetadata,
191
- ...session?.loggingMetadata,
192
- clientId: this.clientId,
193
- connectedTo: to
194
- }
195
- );
196
- });
197
- }
198
- receiveHandshakeResponseMessage(data, conn) {
199
- const parsed = this.parseMsg(data, conn);
200
- if (!parsed) {
201
- conn.telemetry?.span.setStatus({
202
- code: SpanStatusCode.ERROR,
203
- message: "non-transport message"
204
- });
205
- this.protocolError(
206
- ProtocolError.HandshakeFailed,
207
- "received non-transport message"
208
- );
209
- return false;
210
- }
211
- if (!Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {
212
- conn.telemetry?.span.setStatus({
213
- code: SpanStatusCode.ERROR,
214
- message: "invalid handshake response"
215
- });
216
- this.log?.warn(`received invalid handshake resp`, {
217
- ...conn.loggingMetadata,
218
- clientId: this.clientId,
219
- connectedTo: parsed.from,
220
- transportMessage: parsed,
221
- validationErrors: [
222
- ...Value.Errors(
223
- ControlMessageHandshakeResponseSchema,
224
- parsed.payload
225
- )
226
- ]
227
- });
228
- this.protocolError(
229
- ProtocolError.HandshakeFailed,
230
- "invalid handshake resp"
231
- );
232
- return false;
233
- }
234
- const previousSession = this.sessions.get(parsed.from);
235
- if (!parsed.payload.status.ok) {
236
- if (parsed.payload.status.reason === SESSION_STATE_MISMATCH) {
237
- if (previousSession) {
238
- this.deleteSession({
239
- session: previousSession,
240
- closeHandshakingConnection: true
241
- });
242
- }
243
- conn.telemetry?.span.setStatus({
244
- code: SpanStatusCode.ERROR,
245
- message: parsed.payload.status.reason
246
- });
247
- } else {
248
- conn.telemetry?.span.setStatus({
249
- code: SpanStatusCode.ERROR,
250
- message: "handshake rejected"
251
- });
252
- }
253
- this.log?.warn(
254
- `received handshake rejection: ${parsed.payload.status.reason}`,
255
- {
256
- ...conn.loggingMetadata,
257
- clientId: this.clientId,
258
- connectedTo: parsed.from,
259
- transportMessage: parsed
260
- }
261
- );
262
- this.protocolError(
263
- ProtocolError.HandshakeFailed,
264
- parsed.payload.status.reason
265
- );
266
- return false;
267
- }
268
- if (previousSession?.advertisedSessionId && previousSession.advertisedSessionId !== parsed.payload.status.sessionId) {
269
- this.deleteSession({
270
- session: previousSession,
271
- closeHandshakingConnection: true
272
- });
273
- conn.telemetry?.span.setStatus({
274
- code: SpanStatusCode.ERROR,
275
- message: "session id mismatch"
276
- });
277
- this.log?.warn(`handshake from ${parsed.from} session id mismatch`, {
278
- ...conn.loggingMetadata,
279
- clientId: this.clientId,
280
- connectedTo: parsed.from,
281
- transportMessage: parsed
282
- });
283
- this.protocolError(ProtocolError.HandshakeFailed, "session id mismatch");
284
- return false;
285
- }
286
- this.log?.debug(`handshake from ${parsed.from} ok`, {
287
- ...conn.loggingMetadata,
288
- clientId: this.clientId,
289
- connectedTo: parsed.from,
290
- transportMessage: parsed
291
- });
292
- const { session, isTransparentReconnect } = this.getOrCreateSession({
293
- to: parsed.from,
294
- conn,
295
- sessionId: parsed.payload.status.sessionId
296
- });
297
- this.onConnect(conn, session, isTransparentReconnect);
298
- this.retryBudget.startRestoringBudget(session.to);
299
- return session;
300
- }
301
- /**
302
- * Manually attempts to connect to a client.
303
- * @param to The client ID of the node to connect to.
304
- */
305
- async connect(to) {
306
- if (this.connections.has(to)) {
307
- this.log?.info(`already connected to ${to}, skipping connect attempt`, {
308
- clientId: this.clientId,
309
- connectedTo: to
310
- });
311
- return;
312
- }
313
- const canProceedWithConnection = () => this.getStatus() === "open";
314
- if (!canProceedWithConnection()) {
315
- this.log?.info(
316
- `transport state is no longer open, cancelling attempt to connect to ${to}`,
317
- { clientId: this.clientId, connectedTo: to }
318
- );
319
- return;
320
- }
321
- let reconnectPromise = this.inflightConnectionPromises.get(to);
322
- if (!reconnectPromise) {
323
- if (!this.retryBudget.hasBudget(to)) {
324
- const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
325
- const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
326
- this.log?.error(errMsg, { clientId: this.clientId, connectedTo: to });
327
- this.protocolError(ProtocolError.RetriesExceeded, errMsg);
328
- return;
329
- }
330
- let sleep = Promise.resolve();
331
- const backoffMs = this.retryBudget.getBackoffMs(to);
332
- if (backoffMs > 0) {
333
- sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
334
- }
335
- this.log?.info(
336
- `attempting connection to ${to} (${backoffMs}ms backoff)`,
337
- {
338
- clientId: this.clientId,
339
- connectedTo: to
340
- }
341
- );
342
- this.retryBudget.consumeBudget(to);
343
- reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
344
- try {
345
- span.addEvent("backoff", { backoffMs });
346
- await sleep;
347
- if (!canProceedWithConnection()) {
348
- throw new Error("transport state is no longer open");
349
- }
350
- span.addEvent("connecting");
351
- const conn = await this.createNewOutgoingConnection(to);
352
- if (!canProceedWithConnection()) {
353
- this.log?.info(
354
- `transport state is no longer open, closing pre-handshake connection to ${to}`,
355
- {
356
- ...conn.loggingMetadata,
357
- clientId: this.clientId,
358
- connectedTo: to
359
- }
360
- );
361
- conn.close();
362
- throw new Error("transport state is no longer open");
363
- }
364
- span.addEvent("sending handshake");
365
- const ok = await this.sendHandshake(to, conn);
366
- if (!ok) {
367
- conn.close();
368
- throw new Error("failed to send handshake");
369
- }
370
- return conn;
371
- } catch (err) {
372
- const errStr = coerceErrorString(err);
373
- span.recordException(errStr);
374
- span.setStatus({ code: SpanStatusCode.ERROR });
375
- throw err;
376
- } finally {
377
- span.end();
378
- }
379
- });
380
- this.inflightConnectionPromises.set(to, reconnectPromise);
381
- } else {
382
- this.log?.info(
383
- `attempting connection to ${to} (reusing previous attempt)`,
384
- {
385
- clientId: this.clientId,
386
- connectedTo: to
387
- }
388
- );
389
- }
390
- try {
391
- await reconnectPromise;
392
- } catch (error) {
393
- this.inflightConnectionPromises.delete(to);
394
- const errStr = coerceErrorString(error);
395
- if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
396
- this.log?.warn(`connection to ${to} failed (${errStr})`, {
397
- clientId: this.clientId,
398
- connectedTo: to
399
- });
400
- } else {
401
- this.log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
402
- clientId: this.clientId,
403
- connectedTo: to
404
- });
405
- await this.connect(to);
406
- }
407
- }
408
- }
409
- deleteSession({
410
- session,
411
- closeHandshakingConnection,
412
- handshakingConn
413
- }) {
414
- this.inflightConnectionPromises.delete(session.to);
415
- super.deleteSession({
416
- session,
417
- closeHandshakingConnection,
418
- handshakingConn
419
- });
420
- }
421
- async sendHandshake(to, conn) {
422
- let metadata = void 0;
423
- if (this.handshakeExtensions) {
424
- metadata = await this.handshakeExtensions.construct();
425
- if (!Value.Check(this.handshakeExtensions.schema, metadata)) {
426
- this.log?.error(`constructed handshake metadata did not match schema`, {
427
- ...conn.loggingMetadata,
428
- clientId: this.clientId,
429
- connectedTo: to,
430
- validationErrors: [
431
- ...Value.Errors(this.handshakeExtensions.schema, metadata)
432
- ],
433
- tags: ["invariant-violation"]
434
- });
435
- this.protocolError(
436
- ProtocolError.HandshakeFailed,
437
- "handshake metadata did not match schema"
438
- );
439
- conn.telemetry?.span.setStatus({
440
- code: SpanStatusCode.ERROR,
441
- message: "handshake meta mismatch"
442
- });
443
- return false;
444
- }
445
- }
446
- const { session } = this.getOrCreateSession({ to, handshakingConn: conn });
447
- const requestMsg = handshakeRequestMessage({
448
- from: this.clientId,
449
- to,
450
- sessionId: session.id,
451
- expectedSessionState: {
452
- reconnect: session.advertisedSessionId !== void 0,
453
- nextExpectedSeq: session.nextExpectedSeq
454
- },
455
- metadata,
456
- tracing: getPropagationContext(session.telemetry.ctx)
457
- });
458
- this.log?.debug(`sending handshake request to ${to}`, {
459
- ...conn.loggingMetadata,
460
- clientId: this.clientId,
461
- connectedTo: to,
462
- transportMessage: requestMsg
463
- });
464
- conn.send(this.codec.toBuffer(requestMsg));
465
- return true;
466
- }
467
- close() {
468
- this.retryBudget.close();
469
- super.close();
470
- }
471
- };
472
-
473
- export {
474
- ClientTransport
475
- };
476
- //# sourceMappingURL=chunk-JA7XGTAL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../transport/client.ts","../transport/rateLimit.ts"],"sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { ClientHandshakeOptions } from '../router/handshake';\nimport {\n ControlMessageHandshakeResponseSchema,\n SESSION_STATE_MISMATCH,\n TransportClientId,\n handshakeRequestMessage,\n} from './message';\nimport {\n ClientTransportOptions,\n ProvidedClientTransportOptions,\n defaultClientTransportOptions,\n} from './options';\nimport { LeakyBucketRateLimit } from './rateLimit';\nimport { Connection, Session } from './session';\nimport { Transport } from './transport';\nimport { coerceErrorString } from '../util/stringify';\nimport { ProtocolError } from './events';\nimport { Value } from '@sinclair/typebox/value';\nimport tracer, { getPropagationContext } from '../tracing';\n\nexport abstract class ClientTransport<\n ConnType extends Connection,\n> extends Transport<ConnType> {\n /**\n * The options for this transport.\n */\n protected options: ClientTransportOptions;\n\n /**\n * The map of reconnect promises for each client ID.\n */\n inflightConnectionPromises: Map<TransportClientId, Promise<ConnType>>;\n retryBudget: LeakyBucketRateLimit;\n\n /**\n * A flag indicating whether the transport should automatically reconnect\n * when a connection is dropped.\n * Realistically, this should always be true for clients unless you are writing\n * tests or a special case where you don't want to reconnect.\n */\n reconnectOnConnectionDrop = true;\n\n /**\n * Optional handshake options for this client.\n */\n handshakeExtensions?: ClientHandshakeOptions;\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedClientTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.options = {\n ...defaultClientTransportOptions,\n ...providedOptions,\n };\n this.inflightConnectionPromises = new Map();\n this.retryBudget = new LeakyBucketRateLimit(this.options);\n }\n\n extendHandshake(options: ClientHandshakeOptions) {\n this.handshakeExtensions = options;\n }\n\n protected handleConnection(conn: ConnType, to: TransportClientId): void {\n if (this.getStatus() !== 'open') return;\n let session: Session<ConnType> | undefined = undefined;\n\n // kill the conn after the grace period if we haven't received a handshake\n const handshakeTimeout = setTimeout(() => {\n if (session) return;\n this.log?.warn(\n `connection to ${to} timed out waiting for handshake, closing`,\n { ...conn.loggingMetadata, clientId: this.clientId, connectedTo: to },\n );\n conn.close();\n }, this.options.sessionDisconnectGraceMs);\n\n const handshakeHandler = (data: Uint8Array) => {\n const maybeSession = this.receiveHandshakeResponseMessage(data, conn);\n clearTimeout(handshakeTimeout);\n if (!maybeSession) {\n conn.close();\n return;\n } else {\n session = maybeSession;\n }\n\n // when we are done handshake sequence,\n // remove handshake listener and use the normal message listener\n conn.removeDataListener(handshakeHandler);\n conn.addDataListener((data) => {\n const parsed = this.parseMsg(data, conn);\n if (!parsed) {\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'message parse failure',\n });\n conn.close();\n return;\n }\n\n this.handleMsg(parsed, conn);\n });\n };\n\n conn.addDataListener(handshakeHandler);\n conn.addCloseListener(() => {\n if (session) {\n this.onDisconnect(conn, session);\n }\n\n const willReconnect =\n this.reconnectOnConnectionDrop && this.getStatus() === 'open';\n\n this.log?.info(\n `connection to ${to} disconnected` +\n (willReconnect ? ', reconnecting' : ''),\n {\n ...conn.loggingMetadata,\n ...session?.loggingMetadata,\n clientId: this.clientId,\n connectedTo: to,\n },\n );\n\n this.inflightConnectionPromises.delete(to);\n if (this.reconnectOnConnectionDrop) {\n void this.connect(to);\n }\n });\n conn.addErrorListener((err) => {\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'connection error',\n });\n this.log?.warn(\n `error in connection to ${to}: ${coerceErrorString(err)}`,\n {\n ...conn.loggingMetadata,\n ...session?.loggingMetadata,\n clientId: this.clientId,\n connectedTo: to,\n },\n );\n });\n }\n\n receiveHandshakeResponseMessage(\n data: Uint8Array,\n conn: ConnType,\n ): Session<ConnType> | false {\n const parsed = this.parseMsg(data, conn);\n if (!parsed) {\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'non-transport message',\n });\n this.protocolError(\n ProtocolError.HandshakeFailed,\n 'received non-transport message',\n );\n return false;\n }\n\n if (!Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'invalid handshake response',\n });\n this.log?.warn(`received invalid handshake resp`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: parsed.from,\n transportMessage: parsed,\n validationErrors: [\n ...Value.Errors(\n ControlMessageHandshakeResponseSchema,\n parsed.payload,\n ),\n ],\n });\n this.protocolError(\n ProtocolError.HandshakeFailed,\n 'invalid handshake resp',\n );\n return false;\n }\n\n const previousSession = this.sessions.get(parsed.from);\n if (!parsed.payload.status.ok) {\n if (parsed.payload.status.reason === SESSION_STATE_MISMATCH) {\n if (previousSession) {\n // The server has told us that we cannot continue with the session because it has the\n // wrong state. We should delete this session and start fresh.\n this.deleteSession({\n session: previousSession,\n closeHandshakingConnection: true,\n });\n }\n\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: parsed.payload.status.reason,\n });\n } else {\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'handshake rejected',\n });\n }\n this.log?.warn(\n `received handshake rejection: ${parsed.payload.status.reason}`,\n {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: parsed.from,\n transportMessage: parsed,\n },\n );\n this.protocolError(\n ProtocolError.HandshakeFailed,\n parsed.payload.status.reason,\n );\n return false;\n }\n\n // before we claim victory and we deem that the handshake is fully established, check that our\n // session matches the remote's. if they do not match, proactively close the connection.\n // otherwise we will end up breaking a lot of invariants.\n //\n // TODO: Remove this once we finish rolling out the handshake-initiated session agreement.\n if (\n previousSession?.advertisedSessionId &&\n previousSession.advertisedSessionId !== parsed.payload.status.sessionId\n ) {\n this.deleteSession({\n session: previousSession,\n closeHandshakingConnection: true,\n });\n\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'session id mismatch',\n });\n this.log?.warn(`handshake from ${parsed.from} session id mismatch`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: parsed.from,\n transportMessage: parsed,\n });\n this.protocolError(ProtocolError.HandshakeFailed, 'session id mismatch');\n return false;\n }\n\n this.log?.debug(`handshake from ${parsed.from} ok`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: parsed.from,\n transportMessage: parsed,\n });\n\n const { session, isTransparentReconnect } = this.getOrCreateSession({\n to: parsed.from,\n conn,\n sessionId: parsed.payload.status.sessionId,\n });\n\n this.onConnect(conn, session, isTransparentReconnect);\n\n // After a successful connection, we start restoring the budget\n // so that the next time we try to connect, we don't hit the client\n // with backoff forever.\n this.retryBudget.startRestoringBudget(session.to);\n return session;\n }\n\n /**\n * Abstract method that creates a new {@link Connection} object.\n * This should call {@link handleConnection} when the connection is created.\n * The downstream client implementation needs to implement this.\n *\n * @param to The client ID of the node to connect to.\n * @returns The new connection object.\n */\n protected abstract createNewOutgoingConnection(\n to: TransportClientId,\n ): Promise<ConnType>;\n\n /**\n * Manually attempts to connect to a client.\n * @param to The client ID of the node to connect to.\n */\n async connect(to: TransportClientId): Promise<void> {\n if (this.connections.has(to)) {\n this.log?.info(`already connected to ${to}, skipping connect attempt`, {\n clientId: this.clientId,\n connectedTo: to,\n });\n return;\n }\n\n const canProceedWithConnection = () => this.getStatus() === 'open';\n if (!canProceedWithConnection()) {\n this.log?.info(\n `transport state is no longer open, cancelling attempt to connect to ${to}`,\n { clientId: this.clientId, connectedTo: to },\n );\n return;\n }\n\n let reconnectPromise = this.inflightConnectionPromises.get(to);\n if (!reconnectPromise) {\n // check budget\n if (!this.retryBudget.hasBudget(to)) {\n const budgetConsumed = this.retryBudget.getBudgetConsumed(to);\n const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;\n this.log?.error(errMsg, { clientId: this.clientId, connectedTo: to });\n this.protocolError(ProtocolError.RetriesExceeded, errMsg);\n return;\n }\n\n let sleep = Promise.resolve();\n const backoffMs = this.retryBudget.getBackoffMs(to);\n if (backoffMs > 0) {\n sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));\n }\n\n this.log?.info(\n `attempting connection to ${to} (${backoffMs}ms backoff)`,\n {\n clientId: this.clientId,\n connectedTo: to,\n },\n );\n this.retryBudget.consumeBudget(to);\n reconnectPromise = tracer.startActiveSpan('connect', async (span) => {\n try {\n span.addEvent('backoff', { backoffMs });\n await sleep;\n if (!canProceedWithConnection()) {\n throw new Error('transport state is no longer open');\n }\n\n span.addEvent('connecting');\n const conn = await this.createNewOutgoingConnection(to);\n if (!canProceedWithConnection()) {\n this.log?.info(\n `transport state is no longer open, closing pre-handshake connection to ${to}`,\n {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: to,\n },\n );\n conn.close();\n throw new Error('transport state is no longer open');\n }\n\n span.addEvent('sending handshake');\n const ok = await this.sendHandshake(to, conn);\n if (!ok) {\n conn.close();\n throw new Error('failed to send handshake');\n }\n\n return conn;\n } catch (err) {\n // rethrow the error so that the promise is rejected\n // as it was before we wrapped it in a span\n const errStr = coerceErrorString(err);\n span.recordException(errStr);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw err;\n } finally {\n span.end();\n }\n });\n\n this.inflightConnectionPromises.set(to, reconnectPromise);\n } else {\n this.log?.info(\n `attempting connection to ${to} (reusing previous attempt)`,\n {\n clientId: this.clientId,\n connectedTo: to,\n },\n );\n }\n\n try {\n await reconnectPromise;\n } catch (error: unknown) {\n this.inflightConnectionPromises.delete(to);\n const errStr = coerceErrorString(error);\n\n if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {\n this.log?.warn(`connection to ${to} failed (${errStr})`, {\n clientId: this.clientId,\n connectedTo: to,\n });\n } else {\n this.log?.warn(`connection to ${to} failed (${errStr}), retrying`, {\n clientId: this.clientId,\n connectedTo: to,\n });\n await this.connect(to);\n }\n }\n }\n\n protected deleteSession({\n session,\n closeHandshakingConnection,\n handshakingConn,\n }: {\n session: Session<ConnType>;\n closeHandshakingConnection: boolean;\n handshakingConn?: ConnType;\n }) {\n this.inflightConnectionPromises.delete(session.to);\n super.deleteSession({\n session,\n closeHandshakingConnection,\n handshakingConn,\n });\n }\n\n protected async sendHandshake(to: TransportClientId, conn: ConnType) {\n let metadata: unknown = undefined;\n\n if (this.handshakeExtensions) {\n metadata = await this.handshakeExtensions.construct();\n if (!Value.Check(this.handshakeExtensions.schema, metadata)) {\n this.log?.error(`constructed handshake metadata did not match schema`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: to,\n validationErrors: [\n ...Value.Errors(this.handshakeExtensions.schema, metadata),\n ],\n tags: ['invariant-violation'],\n });\n this.protocolError(\n ProtocolError.HandshakeFailed,\n 'handshake metadata did not match schema',\n );\n conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: 'handshake meta mismatch',\n });\n return false;\n }\n }\n\n // don't pass conn here as we dont want the session to start using the conn\n // until we have finished the handshake. Still, let the session know that\n // it is semi-associated with the conn, and it can close it if .close() is called.\n const { session } = this.getOrCreateSession({ to, handshakingConn: conn });\n const requestMsg = handshakeRequestMessage({\n from: this.clientId,\n to,\n sessionId: session.id,\n expectedSessionState: {\n reconnect: session.advertisedSessionId !== undefined,\n nextExpectedSeq: session.nextExpectedSeq,\n },\n metadata,\n tracing: getPropagationContext(session.telemetry.ctx),\n });\n this.log?.debug(`sending handshake request to ${to}`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n connectedTo: to,\n transportMessage: requestMsg,\n });\n conn.send(this.codec.toBuffer(requestMsg));\n return true;\n }\n\n close() {\n this.retryBudget.close();\n super.close();\n }\n}\n","import { TransportClientId } from './message';\n\n/**\n * Options to control the backoff and retry behavior of the client transport's connection behaviour.\n *\n * River implements exponential backoff with jitter to prevent flooding the server\n * when there's an issue with connection establishment.\n *\n * The backoff is calculated via the following:\n * backOff = min(jitter + {@link baseIntervalMs} * 2 ^ budget_consumed, {@link maxBackoffMs})\n *\n * We use a leaky bucket rate limit with a budget of {@link attemptBudgetCapacity} reconnection attempts.\n * Budget only starts to restore after a successful handshake at a rate of one budget per {@link budgetRestoreIntervalMs}.\n */\nexport interface ConnectionRetryOptions {\n /**\n * The base interval to wait before retrying a connection.\n */\n baseIntervalMs: number;\n\n /**\n * The maximum random jitter to add to the total backoff time.\n */\n maxJitterMs: number;\n\n /**\n * The maximum amount of time to wait before retrying a connection.\n * This does not include the jitter.\n */\n maxBackoffMs: number;\n\n /**\n * The max number of times to attempt a connection before a successful handshake.\n * This persists across connections but starts restoring budget after a successful handshake.\n * The restoration interval depends on {@link budgetRestoreIntervalMs}\n */\n attemptBudgetCapacity: number;\n\n /**\n * After a successful connection attempt, how long to wait before we restore a single budget.\n */\n budgetRestoreIntervalMs: number;\n}\n\nexport class LeakyBucketRateLimit {\n private budgetConsumed: Map<TransportClientId, number>;\n private intervalHandles: Map<\n TransportClientId,\n ReturnType<typeof setInterval>\n >;\n private readonly options: ConnectionRetryOptions;\n\n constructor(options: ConnectionRetryOptions) {\n this.options = options;\n this.budgetConsumed = new Map();\n this.intervalHandles = new Map();\n }\n\n getBackoffMs(user: TransportClientId) {\n if (!this.budgetConsumed.has(user)) return 0;\n\n const exponent = Math.max(0, this.getBudgetConsumed(user) - 1);\n const jitter = Math.floor(Math.random() * this.options.maxJitterMs);\n const backoffMs = Math.min(\n this.options.baseIntervalMs * 2 ** exponent,\n this.options.maxBackoffMs,\n );\n\n return backoffMs + jitter;\n }\n\n get totalBudgetRestoreTime() {\n return (\n this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity\n );\n }\n\n consumeBudget(user: TransportClientId) {\n // If we're consuming again, let's ensure that we're not leaking\n this.stopLeak(user);\n this.budgetConsumed.set(user, this.getBudgetConsumed(user) + 1);\n }\n\n getBudgetConsumed(user: TransportClientId) {\n return this.budgetConsumed.get(user) ?? 0;\n }\n\n hasBudget(user: TransportClientId) {\n return this.getBudgetConsumed(user) < this.options.attemptBudgetCapacity;\n }\n\n startRestoringBudget(user: TransportClientId) {\n if (this.intervalHandles.has(user)) {\n return;\n }\n\n const restoreBudgetForUser = () => {\n const currentBudget = this.budgetConsumed.get(user);\n if (!currentBudget) {\n this.stopLeak(user);\n return;\n }\n\n const newBudget = currentBudget - 1;\n if (newBudget === 0) {\n this.budgetConsumed.delete(user);\n return;\n }\n\n this.budgetConsumed.set(user, newBudget);\n };\n\n const intervalHandle = setInterval(\n restoreBudgetForUser,\n this.options.budgetRestoreIntervalMs,\n );\n\n this.intervalHandles.set(user, intervalHandle);\n }\n\n private stopLeak(user: TransportClientId) {\n if (!this.intervalHandles.has(user)) {\n return;\n }\n\n clearInterval(this.intervalHandles.get(user));\n this.intervalHandles.delete(user);\n }\n\n close() {\n for (const user of this.intervalHandles.keys()) {\n this.stopLeak(user);\n }\n }\n}\n"],"mappings":"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","names":["data"]}