@replit/river 0.200.0-rc.2 → 0.200.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -20
- package/dist/{chunk-S5RL45KH.js → chunk-3CCOX55A.js} +76 -42
- package/dist/chunk-3CCOX55A.js.map +1 -0
- package/dist/chunk-GWYJZFCW.js +653 -0
- package/dist/chunk-GWYJZFCW.js.map +1 -0
- package/dist/chunk-O4O2E6DJ.js +277 -0
- package/dist/chunk-O4O2E6DJ.js.map +1 -0
- package/dist/chunk-QXLXDJD5.js +382 -0
- package/dist/chunk-QXLXDJD5.js.map +1 -0
- package/dist/{chunk-4VNY34QG.js → chunk-UFMDEG44.js} +24 -18
- package/dist/chunk-UFMDEG44.js.map +1 -0
- package/dist/{chunk-7CKIN3JT.js → chunk-UIYGPURD.js} +31 -484
- package/dist/chunk-UIYGPURD.js.map +1 -0
- package/dist/{chunk-QMM35C3H.js → chunk-VXYHC666.js} +1 -1
- package/dist/chunk-VXYHC666.js.map +1 -0
- package/dist/chunk-WSCAA7VY.js +50 -0
- package/dist/chunk-WSCAA7VY.js.map +1 -0
- package/dist/chunk-ZQVKFLAB.js +399 -0
- package/dist/chunk-ZQVKFLAB.js.map +1 -0
- package/dist/client-a84783be.d.ts +49 -0
- package/dist/{connection-f900e390.d.ts → connection-320fb130.d.ts} +1 -5
- package/dist/connection-d0b488e6.d.ts +11 -0
- package/dist/context-c9668e95.d.ts +527 -0
- package/dist/logging/index.cjs.map +1 -1
- package/dist/logging/index.d.cts +1 -1
- package/dist/logging/index.d.ts +1 -1
- package/dist/logging/index.js +1 -1
- package/dist/{index-10ebd26a.d.ts → message-fd349b27.d.ts} +31 -31
- package/dist/router/index.cjs +83 -496
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +11 -46
- package/dist/router/index.d.ts +11 -46
- package/dist/router/index.js +2 -4
- package/dist/server-dbad597e.d.ts +42 -0
- package/dist/{services-970f97bb.d.ts → services-690e5553.d.ts} +5 -602
- package/dist/transport/impls/uds/client.cjs +1240 -1237
- package/dist/transport/impls/uds/client.cjs.map +1 -1
- package/dist/transport/impls/uds/client.d.cts +4 -3
- package/dist/transport/impls/uds/client.d.ts +4 -3
- package/dist/transport/impls/uds/client.js +7 -13
- package/dist/transport/impls/uds/client.js.map +1 -1
- package/dist/transport/impls/uds/server.cjs +1302 -1168
- package/dist/transport/impls/uds/server.cjs.map +1 -1
- package/dist/transport/impls/uds/server.d.cts +4 -4
- package/dist/transport/impls/uds/server.d.ts +4 -4
- package/dist/transport/impls/uds/server.js +6 -6
- package/dist/transport/impls/ws/client.cjs +981 -986
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +6 -5
- package/dist/transport/impls/ws/client.d.ts +6 -5
- package/dist/transport/impls/ws/client.js +6 -7
- package/dist/transport/impls/ws/client.js.map +1 -1
- package/dist/transport/impls/ws/server.cjs +1183 -1064
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +4 -4
- package/dist/transport/impls/ws/server.d.ts +4 -4
- package/dist/transport/impls/ws/server.js +6 -6
- package/dist/transport/index.cjs +1435 -1377
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +4 -26
- package/dist/transport/index.d.ts +4 -26
- package/dist/transport/index.js +9 -9
- package/dist/util/testHelpers.cjs +738 -302
- package/dist/util/testHelpers.cjs.map +1 -1
- package/dist/util/testHelpers.d.cts +9 -4
- package/dist/util/testHelpers.d.ts +9 -4
- package/dist/util/testHelpers.js +32 -8
- package/dist/util/testHelpers.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-47TFNAY2.js +0 -476
- package/dist/chunk-47TFNAY2.js.map +0 -1
- package/dist/chunk-4VNY34QG.js.map +0 -1
- package/dist/chunk-7CKIN3JT.js.map +0 -1
- package/dist/chunk-CZP4LK3F.js +0 -335
- package/dist/chunk-CZP4LK3F.js.map +0 -1
- package/dist/chunk-DJCW3SKT.js +0 -59
- package/dist/chunk-DJCW3SKT.js.map +0 -1
- package/dist/chunk-NQWDT6GS.js +0 -347
- package/dist/chunk-NQWDT6GS.js.map +0 -1
- package/dist/chunk-ONUXWVRC.js +0 -492
- package/dist/chunk-ONUXWVRC.js.map +0 -1
- package/dist/chunk-QMM35C3H.js.map +0 -1
- package/dist/chunk-S5RL45KH.js.map +0 -1
- package/dist/connection-3f117047.d.ts +0 -17
package/dist/util/testHelpers.js
CHANGED
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
ReadStreamImpl,
|
|
5
5
|
UNCAUGHT_ERROR_CODE,
|
|
6
6
|
WriteStreamImpl
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-UIYGPURD.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
SessionStateGraph,
|
|
10
10
|
defaultTransportOptions
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-GWYJZFCW.js";
|
|
12
12
|
import {
|
|
13
13
|
coerceErrorString
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-3CCOX55A.js";
|
|
15
15
|
import "../chunk-4PVU7J25.js";
|
|
16
16
|
|
|
17
17
|
// util/testHelpers.ts
|
|
@@ -84,18 +84,22 @@ function catchProcError(err) {
|
|
|
84
84
|
}
|
|
85
85
|
var testingSessionOptions = defaultTransportOptions;
|
|
86
86
|
function dummySession() {
|
|
87
|
-
return
|
|
88
|
-
void 0,
|
|
87
|
+
return SessionStateGraph.entrypoints.NoConnection(
|
|
89
88
|
"client",
|
|
90
89
|
"server",
|
|
90
|
+
{
|
|
91
|
+
onSessionGracePeriodElapsed: () => {
|
|
92
|
+
}
|
|
93
|
+
},
|
|
91
94
|
testingSessionOptions
|
|
92
95
|
);
|
|
93
96
|
}
|
|
94
97
|
function dummyCtx(state, session, extendedContext) {
|
|
95
98
|
return {
|
|
96
99
|
...extendedContext,
|
|
97
|
-
from: session.from,
|
|
98
100
|
state,
|
|
101
|
+
sessionId: session.id,
|
|
102
|
+
from: session.from,
|
|
99
103
|
metadata: {},
|
|
100
104
|
abortController: new AbortController(),
|
|
101
105
|
clientAbortSignal: new AbortController().signal,
|
|
@@ -173,20 +177,40 @@ function asClientUpload(state, proc, init, extendedContext, session = dummySessi
|
|
|
173
177
|
return [inputPipe.writer, () => result];
|
|
174
178
|
}
|
|
175
179
|
var getUnixSocketPath = () => {
|
|
176
|
-
return
|
|
180
|
+
return `/tmp/${nanoid()}.sock`;
|
|
177
181
|
};
|
|
182
|
+
function getTransportConnections(transport) {
|
|
183
|
+
const connections = [];
|
|
184
|
+
for (const session of transport.sessions.values()) {
|
|
185
|
+
if (session.state === "Connected" /* Connected */) {
|
|
186
|
+
connections.push(session.conn);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return connections;
|
|
190
|
+
}
|
|
191
|
+
function numberOfConnections(transport) {
|
|
192
|
+
return getTransportConnections(transport).length;
|
|
193
|
+
}
|
|
194
|
+
function closeAllConnections(transport) {
|
|
195
|
+
for (const conn of getTransportConnections(transport)) {
|
|
196
|
+
conn.close();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
178
199
|
export {
|
|
179
200
|
asClientRpc,
|
|
180
201
|
asClientStream,
|
|
181
202
|
asClientSubscription,
|
|
182
203
|
asClientUpload,
|
|
204
|
+
closeAllConnections,
|
|
183
205
|
createDummyTransportMessage,
|
|
184
206
|
createLocalWebSocketClient,
|
|
185
207
|
createWebSocketServer,
|
|
186
208
|
dummySession,
|
|
187
209
|
getIteratorFromStream,
|
|
210
|
+
getTransportConnections,
|
|
188
211
|
getUnixSocketPath,
|
|
189
212
|
iterNext,
|
|
213
|
+
numberOfConnections,
|
|
190
214
|
onUdsServeReady,
|
|
191
215
|
onWsServerReady,
|
|
192
216
|
payloadToTransportMessage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../util/testHelpers.ts"],"sourcesContent":["import NodeWs, { WebSocketServer } from 'ws';\nimport http from 'node:http';\nimport {\n Err,\n Ok,\n PayloadType,\n Procedure,\n Result,\n ProcedureErrorSchemaType,\n InputReaderErrorSchema,\n OutputReaderErrorSchema,\n ServiceContext,\n ProcedureHandlerContext,\n UNCAUGHT_ERROR_CODE,\n} from '../router';\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 {\n ReadStream,\n ReadStreamImpl,\n WriteStream,\n WriteStreamImpl,\n} from '../router/streams';\nimport { WsLike } from '../transport/impls/ws/wslike';\nimport { defaultTransportOptions } from '../transport/options';\nimport { BaseErrorSchemaType } from '../router/result';\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\nexport function getIteratorFromStream<T, E extends Static<BaseErrorSchemaType>>(\n readStream: ReadStream<T, E>,\n) {\n return readStream[Symbol.asyncIterator]();\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: {\n next(): Promise<\n | {\n done: false;\n value: T;\n }\n | {\n done: true;\n value: undefined;\n }\n >;\n}) {\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_CODE, 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): ProcedureHandlerContext<State> {\n return {\n ...extendedContext,\n from: session.from,\n state,\n metadata: {},\n abortController: new AbortController(),\n clientAbortSignal: new AbortController().signal,\n onRequestFinished: () => undefined,\n };\n}\n\nexport function asClientRpc<\n State extends object,\n Init extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'rpc', Init, null, Output, Err>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n return async (\n msg: Static<Init>,\n ): Promise<\n Result<Static<Output>, Static<Err> | Static<typeof OutputReaderErrorSchema>>\n > => {\n return proc\n .handler(dummyCtx(state, session, extendedContext), msg)\n .catch(catchProcError);\n };\n}\n\nfunction createOutputPipe<\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(): {\n reader: ReadStream<\n Static<Output>,\n Static<Err> | Static<typeof OutputReaderErrorSchema>\n >;\n writer: WriteStream<Result<Static<Output>, Static<Err>>>;\n} {\n const reader = new ReadStreamImpl<\n Static<Output>,\n Static<Err> | Static<typeof OutputReaderErrorSchema>\n >(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n writer.triggerCloseRequest();\n });\n });\n const writer = new WriteStreamImpl<Result<Static<Output>, Static<Err>>>(\n (v) => {\n reader.pushValue(v);\n },\n );\n writer.onClose(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n reader.triggerClose();\n });\n });\n\n return { reader, writer };\n}\n\nfunction createInputPipe<Input extends PayloadType>(): {\n reader: ReadStream<Static<Input>, Static<typeof InputReaderErrorSchema>>;\n writer: WriteStream<Static<Input>>;\n} {\n const reader = new ReadStreamImpl<\n Static<Input>,\n Static<typeof InputReaderErrorSchema>\n >(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n writer.triggerCloseRequest();\n });\n });\n const writer = new WriteStreamImpl<Static<Input>>((v) => {\n reader.pushValue(Ok(v));\n });\n writer.onClose(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n reader.triggerClose();\n });\n });\n\n return { reader, writer };\n}\n\nexport function asClientStream<\n State extends object,\n Init extends PayloadType,\n Input extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'stream', Init, Input, Output, Err>,\n init?: Static<Init>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n): [WriteStream<Static<Input>>, ReadStream<Static<Output>, Static<Err>>] {\n const inputPipe = createInputPipe<Input>();\n const outputPipe = createOutputPipe<Output, Err>();\n\n void proc\n .handler(\n dummyCtx(state, session, extendedContext),\n init ?? {},\n inputPipe.reader,\n outputPipe.writer,\n )\n .catch((err: unknown) => outputPipe.writer.write(catchProcError(err)));\n\n return [inputPipe.writer, outputPipe.reader];\n}\n\nexport function asClientSubscription<\n State extends object,\n Init extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'subscription', Init, null, Output, Err>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n): (msg: Static<Init>) => ReadStream<Static<Output>, Static<Err>> {\n const outputPipe = createOutputPipe<Output, Err>();\n\n return (msg: Static<Init>) => {\n void proc\n .handler(\n dummyCtx(state, session, extendedContext),\n msg,\n outputPipe.writer,\n )\n .catch((err: unknown) => outputPipe.writer.write(catchProcError(err)));\n\n return outputPipe.reader;\n };\n}\n\nexport function asClientUpload<\n State extends object,\n Init extends PayloadType,\n Input extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'upload', Init, Input, Output, Err>,\n init?: Static<Init>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n): [\n WriteStream<Static<Input>>,\n () => Promise<Result<Static<Output>, Static<Err>>>,\n] {\n const inputPipe = createInputPipe<Input>();\n const result = proc\n .handler(\n dummyCtx(state, session, extendedContext),\n init ?? {},\n inputPipe.reader,\n )\n .catch(catchProcError);\n\n return [inputPipe.writer, () => result];\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 {\n Err,\n Ok,\n PayloadType,\n Procedure,\n Result,\n ProcedureErrorSchemaType,\n InputReaderErrorSchema,\n OutputReaderErrorSchema,\n ServiceContext,\n ProcedureHandlerContext,\n UNCAUGHT_ERROR_CODE,\n} from '../router';\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 {\n ReadStream,\n ReadStreamImpl,\n WriteStream,\n WriteStreamImpl,\n} from '../router/streams';\nimport { WsLike } from '../transport/impls/ws/wslike';\nimport { defaultTransportOptions } from '../transport/options';\nimport { BaseErrorSchemaType } from '../router/result';\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\nexport function getIteratorFromStream<T, E extends Static<BaseErrorSchemaType>>(\n readStream: ReadStream<T, E>,\n) {\n return readStream[Symbol.asyncIterator]();\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: {\n next(): Promise<\n | {\n done: false;\n value: T;\n }\n | {\n done: true;\n value: undefined;\n }\n >;\n}) {\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_CODE, 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): ProcedureHandlerContext<State> {\n return {\n ...extendedContext,\n state,\n sessionId: session.id,\n from: session.from,\n metadata: {},\n abortController: new AbortController(),\n clientAbortSignal: new AbortController().signal,\n onRequestFinished: () => undefined,\n };\n}\n\nexport function asClientRpc<\n State extends object,\n Init extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'rpc', Init, null, Output, Err>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n) {\n return async (\n msg: Static<Init>,\n ): Promise<\n Result<Static<Output>, Static<Err> | Static<typeof OutputReaderErrorSchema>>\n > => {\n return proc\n .handler(dummyCtx(state, session, extendedContext), msg)\n .catch(catchProcError);\n };\n}\n\nfunction createOutputPipe<\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(): {\n reader: ReadStream<\n Static<Output>,\n Static<Err> | Static<typeof OutputReaderErrorSchema>\n >;\n writer: WriteStream<Result<Static<Output>, Static<Err>>>;\n} {\n const reader = new ReadStreamImpl<\n Static<Output>,\n Static<Err> | Static<typeof OutputReaderErrorSchema>\n >(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n writer.triggerCloseRequest();\n });\n });\n const writer = new WriteStreamImpl<Result<Static<Output>, Static<Err>>>(\n (v) => {\n reader.pushValue(v);\n },\n );\n writer.onClose(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n reader.triggerClose();\n });\n });\n\n return { reader, writer };\n}\n\nfunction createInputPipe<Input extends PayloadType>(): {\n reader: ReadStream<Static<Input>, Static<typeof InputReaderErrorSchema>>;\n writer: WriteStream<Static<Input>>;\n} {\n const reader = new ReadStreamImpl<\n Static<Input>,\n Static<typeof InputReaderErrorSchema>\n >(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n writer.triggerCloseRequest();\n });\n });\n const writer = new WriteStreamImpl<Static<Input>>((v) => {\n reader.pushValue(Ok(v));\n });\n writer.onClose(() => {\n // Make it async to simulate request going over the wire\n // using promises so that we don't get affected by fake timers.\n void Promise.resolve().then(() => {\n reader.triggerClose();\n });\n });\n\n return { reader, writer };\n}\n\nexport function asClientStream<\n State extends object,\n Init extends PayloadType,\n Input extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'stream', Init, Input, Output, Err>,\n init?: Static<Init>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n): [WriteStream<Static<Input>>, ReadStream<Static<Output>, Static<Err>>] {\n const inputPipe = createInputPipe<Input>();\n const outputPipe = createOutputPipe<Output, Err>();\n\n void proc\n .handler(\n dummyCtx(state, session, extendedContext),\n init ?? {},\n inputPipe.reader,\n outputPipe.writer,\n )\n .catch((err: unknown) => outputPipe.writer.write(catchProcError(err)));\n\n return [inputPipe.writer, outputPipe.reader];\n}\n\nexport function asClientSubscription<\n State extends object,\n Init extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'subscription', Init, null, Output, Err>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n): (msg: Static<Init>) => ReadStream<Static<Output>, Static<Err>> {\n const outputPipe = createOutputPipe<Output, Err>();\n\n return (msg: Static<Init>) => {\n void proc\n .handler(\n dummyCtx(state, session, extendedContext),\n msg,\n outputPipe.writer,\n )\n .catch((err: unknown) => outputPipe.writer.write(catchProcError(err)));\n\n return outputPipe.reader;\n };\n}\n\nexport function asClientUpload<\n State extends object,\n Init extends PayloadType,\n Input extends PayloadType,\n Output extends PayloadType,\n Err extends ProcedureErrorSchemaType,\n>(\n state: State,\n proc: Procedure<State, 'upload', Init, Input, Output, Err>,\n init?: Static<Init>,\n extendedContext?: Omit<ServiceContext, 'state'>,\n session: Session<Connection> = dummySession(),\n): [\n WriteStream<Static<Input>>,\n () => Promise<Result<Static<Output>, Static<Err>>>,\n] {\n const inputPipe = createInputPipe<Input>();\n const result = proc\n .handler(\n dummyCtx(state, session, extendedContext),\n init ?? {},\n inputPipe.reader,\n )\n .catch(catchProcError);\n\n return [inputPipe.writer, () => result];\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":"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","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.200.0-rc.
|
|
4
|
+
"version": "0.200.0-rc.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
package/dist/chunk-47TFNAY2.js
DELETED
|
@@ -1,476 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ProtocolError,
|
|
3
|
-
Transport
|
|
4
|
-
} from "./chunk-ONUXWVRC.js";
|
|
5
|
-
import {
|
|
6
|
-
defaultClientTransportOptions
|
|
7
|
-
} from "./chunk-NQWDT6GS.js";
|
|
8
|
-
import {
|
|
9
|
-
ControlMessageHandshakeResponseSchema,
|
|
10
|
-
SESSION_STATE_MISMATCH,
|
|
11
|
-
coerceErrorString,
|
|
12
|
-
getPropagationContext,
|
|
13
|
-
handshakeRequestMessage,
|
|
14
|
-
tracing_default
|
|
15
|
-
} from "./chunk-S5RL45KH.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-47TFNAY2.js.map
|