@replit/river 0.200.0-rc.12 → 0.200.0-rc.15
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/dist/{chunk-MQ6ANR3H.js → chunk-A2MGUPER.js} +1 -1
- package/dist/chunk-A2MGUPER.js.map +1 -0
- package/dist/{chunk-EVNRLJS4.js → chunk-GRDOQL3Q.js} +13 -10
- package/dist/chunk-GRDOQL3Q.js.map +1 -0
- package/dist/{chunk-B6BDN3LW.js → chunk-H2NIGO66.js} +2 -2
- package/dist/{chunk-B6BDN3LW.js.map → chunk-H2NIGO66.js.map} +1 -1
- package/dist/{chunk-25KTWI4A.js → chunk-R5KEFZGW.js} +9 -9
- package/dist/chunk-R5KEFZGW.js.map +1 -0
- package/dist/{chunk-QVJRLSUY.js → chunk-RIN5PMQD.js} +20 -12
- package/dist/chunk-RIN5PMQD.js.map +1 -0
- package/dist/{chunk-FUXFXBVJ.js → chunk-TBBQ5MGX.js} +4 -9
- package/dist/chunk-TBBQ5MGX.js.map +1 -0
- package/dist/{chunk-VXYHC666.js → chunk-YBPTJPU5.js} +1 -1
- package/dist/chunk-YBPTJPU5.js.map +1 -0
- package/dist/{chunk-JPZPQKS4.js → chunk-Z553YSPV.js} +12 -7
- package/dist/chunk-Z553YSPV.js.map +1 -0
- package/dist/{client-22a47343.d.ts → client-ebba28e8.d.ts} +2 -2
- package/dist/{connection-260e45a8.d.ts → connection-00103cbc.d.ts} +1 -1
- package/dist/{context-b4aff18f.d.ts → context-b3f0eb0e.d.ts} +8 -3
- 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/{message-7d135e38.d.ts → message-34667c7a.d.ts} +1 -1
- package/dist/router/index.cjs +3 -8
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +8 -8
- package/dist/router/index.d.ts +8 -8
- package/dist/router/index.js +2 -2
- package/dist/{server-dd6a9853.d.ts → server-aadb7ad2.d.ts} +3 -3
- package/dist/{services-f9e30ec1.d.ts → services-437a8142.d.ts} +7 -4
- package/dist/transport/impls/ws/client.cjs +38 -27
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +4 -4
- package/dist/transport/impls/ws/client.d.ts +4 -4
- package/dist/transport/impls/ws/client.js +6 -6
- package/dist/transport/impls/ws/server.cjs +34 -26
- 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 +43 -32
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +4 -4
- package/dist/transport/index.d.ts +4 -4
- package/dist/transport/index.js +6 -6
- package/dist/util/testHelpers.cjs +21 -18
- package/dist/util/testHelpers.cjs.map +1 -1
- package/dist/util/testHelpers.d.cts +4 -4
- package/dist/util/testHelpers.d.ts +4 -4
- package/dist/util/testHelpers.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-25KTWI4A.js.map +0 -1
- package/dist/chunk-EVNRLJS4.js.map +0 -1
- package/dist/chunk-FUXFXBVJ.js.map +0 -1
- package/dist/chunk-JPZPQKS4.js.map +0 -1
- package/dist/chunk-MQ6ANR3H.js.map +0 -1
- package/dist/chunk-QVJRLSUY.js.map +0 -1
- package/dist/chunk-VXYHC666.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../router/procedures.ts","../router/result.ts","../router/streams.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */\nimport { Static, TNever, TSchema, TUnion, Type } from '@sinclair/typebox';\nimport { ProcedureHandlerContext } from './context';\nimport { BaseErrorSchemaType, Result } from './result';\nimport { ReadStream, WriteStream } from './streams';\n\n/**\n * Brands a type to prevent it from being directly constructed.\n */\nexport type Branded<T> = T & { readonly __BRAND_DO_NOT_USE: unique symbol };\n\n/**\n * Unbrands a {@link Branded} type.\n */\nexport type Unbranded<T> = T extends Branded<infer U> ? U : never;\n\n/**\n * The valid {@link Procedure} types. The `stream` and `upload` types can optionally have a\n * different type for the very first initialization message. The suffixless types correspond to\n * gRPC's four combinations of stream / non-stream in each direction.\n */\nexport type ValidProcType =\n // Single message in both directions (1:1).\n | 'rpc'\n // Client-stream single message from server (n:1).\n | 'upload'\n // Single message from client, stream from server (1:n).\n | 'subscription'\n // Bidirectional stream (n:n).\n | 'stream';\n\n/**\n * Represents the payload type for {@link Procedure}s.\n */\nexport type PayloadType = TSchema;\n\n/**\n * INTERNAL_RIVER_ERROR_CODE is the code that is used when an internal error occurs,\n * this means that some invariants expected by the river server implementation have\n * been violated. When encountering this error please report this to river maintainers.\n */\nexport const INTERNAL_RIVER_ERROR_CODE = 'INTERNAL_RIVER_ERROR' as const;\n/**\n * UNCAUGHT_ERROR_CODE is the code that is used when an error is thrown\n * inside a procedure handler that's not required.\n */\nexport const UNCAUGHT_ERROR_CODE = 'UNCAUGHT_ERROR' as const;\n/**\n * UNEXPECTED_DISCONNECT_CODE is the code used the stream's session\n * disconnect unexpetedly.\n */\nexport const UNEXPECTED_DISCONNECT_CODE = 'UNEXPECTED_DISCONNECT' as const;\n/**\n * INVALID_REQUEST_CODE is the code used when a client's request is invalid.\n */\nexport const INVALID_REQUEST_CODE = 'INVALID_REQUEST' as const;\n/**\n * ABORT_CODE is the code used when either server or client aborts the stream.\n */\nexport const ABORT_CODE = 'ABORT' as const;\n\n/**\n * ResponseReaderErrorSchema is the schema for all the errors that can be\n * emitted in the ResponseData ReadStream on the client.\n */\nexport const ResponseReaderErrorSchema = Type.Object({\n code: Type.Union([\n Type.Literal(INTERNAL_RIVER_ERROR_CODE),\n Type.Literal(UNCAUGHT_ERROR_CODE),\n Type.Literal(UNEXPECTED_DISCONNECT_CODE),\n Type.Literal(INVALID_REQUEST_CODE),\n Type.Literal(ABORT_CODE),\n ]),\n message: Type.String(),\n});\n\n/**\n * RequestReaderErrorSchema is the schema for all the errors that can be\n * emitted in the RequestData ReadStream on the server.\n */\nexport const RequestReaderErrorSchema = Type.Object({\n code: Type.Union([\n Type.Literal(UNCAUGHT_ERROR_CODE),\n Type.Literal(UNEXPECTED_DISCONNECT_CODE),\n Type.Literal(INVALID_REQUEST_CODE),\n Type.Literal(ABORT_CODE),\n ]),\n message: Type.String(),\n});\n\n// Allow specific levels of nesting, otherwise typescript shits itself due to recursion\ntype ProcedureErrorUnionSchema0 = TUnion<Array<BaseErrorSchemaType>>;\ntype ProcedureErrorUnionSchema1 = TUnion<\n Array<ProcedureErrorUnionSchema0 | BaseErrorSchemaType>\n>;\ntype ProcedureErrorUnionSchema2 = TUnion<\n Array<\n | ProcedureErrorUnionSchema1\n | ProcedureErrorUnionSchema0\n | BaseErrorSchemaType\n >\n>;\n\n/**\n * Represents an acceptable schema to pass to a procedure.\n * Just a type of a schema, not an actual schema.\n */\nexport type ProcedureErrorSchemaType =\n | ProcedureErrorUnionSchema2\n | BaseErrorSchemaType\n | TNever;\n\n/**\n * Procedure for a single message in both directions (1:1).\n *\n * @template State - The context state object.\n * @template RequestInit - The TypeBox schema of the initialization object.\n * @template ResponseData - The TypeBox schema of the output object.\n * @template ResponseErr - The TypeBox schema of the error object.\n */\nexport interface RpcProcedure<\n State,\n RequestInit extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n> {\n type: 'rpc';\n requestInit: RequestInit;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler({\n ctx,\n reqInit,\n }: {\n ctx: ProcedureHandlerContext<State>;\n reqInit: Static<RequestInit>;\n }): Promise<Result<Static<ResponseData>, Static<ResponseErr>>>;\n}\n\n/**\n * Procedure for a client-stream (potentially preceded by an initialization message),\n * single message from server (n:1).\n *\n * @template State - The context state object.\n * @template RequestInit - The TypeBox schema of the initialization object.\n * @template RequestData - The TypeBox schema of the input object.\n * @template ResponseData - The TypeBox schema of the output object.\n * @template ResponseErr - The TypeBox schema of the error object.\n */\nexport interface UploadProcedure<\n State,\n RequestInit extends PayloadType,\n RequestData extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n> {\n type: 'upload';\n requestInit: RequestInit;\n requestData: RequestData;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler({\n ctx,\n reqInit,\n reqReader,\n }: {\n ctx: ProcedureHandlerContext<State>;\n reqInit: Static<RequestInit>;\n reqReader: ReadStream<\n Static<RequestData>,\n Static<typeof RequestReaderErrorSchema>\n >;\n }): Promise<Result<Static<ResponseData>, Static<ResponseErr>>>;\n}\n\n/**\n * Procedure for a single message from client, stream from server (1:n).\n *\n * @template State - The context state object.\n * @template RequestInit - The TypeBox schema of the initialization object.\n * @template ResponseData - The TypeBox schema of the output object.\n * @template ResponseErr - The TypeBox schema of the error object.\n */\nexport interface SubscriptionProcedure<\n State,\n RequestInit extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n> {\n type: 'subscription';\n requestInit: RequestInit;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler({\n ctx,\n reqInit,\n resWriter,\n }: {\n ctx: ProcedureHandlerContext<State>;\n reqInit: Static<RequestInit>;\n resWriter: WriteStream<Result<Static<ResponseData>, Static<ResponseErr>>>;\n }): Promise<void | undefined>;\n}\n\n/**\n * Procedure for a bidirectional stream (potentially preceded by an initialization message),\n * (n:n).\n *\n * @template State - The context state object.\n * @template RequestInit - The TypeBox schema of the initialization object.\n * @template RequestData - The TypeBox schema of the input object.\n * @template ResponseData - The TypeBox schema of the output object.\n * @template ResponseErr - The TypeBox schema of the error object.\n */\nexport interface StreamProcedure<\n State,\n RequestInit extends PayloadType,\n RequestData extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n> {\n type: 'stream';\n requestInit: RequestInit;\n requestData: RequestData;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler({\n ctx,\n reqInit,\n reqReader,\n resWriter,\n }: {\n ctx: ProcedureHandlerContext<State>;\n reqInit: Static<RequestInit>;\n reqReader: ReadStream<\n Static<RequestData>,\n Static<typeof RequestReaderErrorSchema>\n >;\n resWriter: WriteStream<Result<Static<ResponseData>, Static<ResponseErr>>>;\n }): Promise<void | undefined>;\n}\n\n/**\n * Defines a Procedure type that can be a:\n * - {@link RpcProcedure} for a single message in both directions (1:1)\n * - {@link UploadProcedure} for a client-stream (potentially preceded by an\n * initialization message)\n * - {@link SubscriptionProcedure} for a single message from client, stream from server (1:n)\n * - {@link StreamProcedure} for a bidirectional stream (potentially preceded by an\n * initialization message)\n *\n * @template State - The TypeBox schema of the state object.\n * @template Ty - The type of the procedure.\n * @template RequestData - The TypeBox schema of the input object.\n * @template RequestInit - The TypeBox schema of the input initialization object, if any.\n * @template ResponseData - The TypeBox schema of the output object.\n */\nexport type Procedure<\n State,\n Ty extends ValidProcType,\n RequestInit extends PayloadType,\n RequestData extends PayloadType | null,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n> = { type: Ty } & (RequestData extends PayloadType\n ? Ty extends 'upload'\n ? UploadProcedure<\n State,\n RequestInit,\n RequestData,\n ResponseData,\n ResponseErr\n >\n : Ty extends 'stream'\n ? StreamProcedure<\n State,\n RequestInit,\n RequestData,\n ResponseData,\n ResponseErr\n >\n : never\n : Ty extends 'rpc'\n ? RpcProcedure<State, RequestInit, ResponseData, ResponseErr>\n : Ty extends 'subscription'\n ? SubscriptionProcedure<State, RequestInit, ResponseData, ResponseErr>\n : never);\n\n/**\n * Represents any {@link Procedure} type.\n *\n * @template State - The context state object. You can provide this to constrain\n * the type of procedures.\n */\nexport type AnyProcedure<State = object> = Procedure<\n State,\n ValidProcType,\n PayloadType,\n PayloadType | null,\n PayloadType,\n ProcedureErrorSchemaType\n>;\n\n/**\n * Represents a map of {@link Procedure}s.\n *\n * @template State - The context state object. You can provide this to constrain\n * the type of procedures.\n */\nexport type ProcedureMap<State = object> = Record<string, AnyProcedure<State>>;\n\n// typescript is funky so with these upcoming procedure constructors, the overloads\n// which handle the `init` case _must_ come first, otherwise the `init` property\n// is not recognized as optional, for some reason\n\n/**\n * Creates an {@link RpcProcedure}.\n */\n// signature: default errors\nfunction rpc<\n State,\n RequestInit extends PayloadType,\n ResponseData extends PayloadType,\n>(def: {\n requestInit: RequestInit;\n responseData: ResponseData;\n responseError?: never;\n description?: string;\n handler: RpcProcedure<State, RequestInit, ResponseData, TNever>['handler'];\n}): Branded<RpcProcedure<State, RequestInit, ResponseData, TNever>>;\n\n// signature: explicit errors\nfunction rpc<\n State,\n RequestInit extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n>(def: {\n requestInit: RequestInit;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler: RpcProcedure<\n State,\n RequestInit,\n ResponseData,\n ResponseErr\n >['handler'];\n}): Branded<RpcProcedure<State, RequestInit, ResponseData, ResponseErr>>;\n\n// implementation\nfunction rpc({\n requestInit,\n responseData,\n responseError = Type.Never(),\n description,\n handler,\n}: {\n requestInit: PayloadType;\n responseData: PayloadType;\n responseError?: ProcedureErrorSchemaType;\n description?: string;\n handler: RpcProcedure<\n object,\n PayloadType,\n PayloadType,\n ProcedureErrorSchemaType\n >['handler'];\n}) {\n return {\n ...(description ? { description } : {}),\n type: 'rpc',\n requestInit,\n responseData,\n responseError,\n handler,\n };\n}\n\n/**\n * Creates an {@link UploadProcedure}, optionally with an initialization message.\n */\n// signature: init with default errors\nfunction upload<\n State,\n RequestInit extends PayloadType,\n RequestData extends PayloadType,\n ResponseData extends PayloadType,\n>(def: {\n requestInit: RequestInit;\n requestData: RequestData;\n responseData: ResponseData;\n responseError?: never;\n description?: string;\n handler: UploadProcedure<\n State,\n RequestInit,\n RequestData,\n ResponseData,\n TNever\n >['handler'];\n}): Branded<\n UploadProcedure<State, RequestInit, RequestData, ResponseData, TNever>\n>;\n\n// signature: init with explicit errors\nfunction upload<\n State,\n RequestInit extends PayloadType,\n RequestData extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n>(def: {\n requestInit: RequestInit;\n requestData: RequestData;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler: UploadProcedure<\n State,\n RequestInit,\n RequestData,\n ResponseData,\n ResponseErr\n >['handler'];\n}): Branded<\n UploadProcedure<State, RequestInit, RequestData, ResponseData, ResponseErr>\n>;\n\n// implementation\nfunction upload({\n requestInit,\n requestData,\n responseData,\n responseError = Type.Never(),\n description,\n handler,\n}: {\n requestInit: PayloadType;\n requestData: PayloadType;\n responseData: PayloadType;\n responseError?: ProcedureErrorSchemaType;\n description?: string;\n handler: UploadProcedure<\n object,\n PayloadType,\n PayloadType,\n PayloadType,\n ProcedureErrorSchemaType\n >['handler'];\n}) {\n return {\n type: 'upload',\n ...(description ? { description } : {}),\n requestInit,\n requestData,\n responseData,\n responseError,\n handler,\n };\n}\n\n/**\n * Creates a {@link SubscriptionProcedure}.\n */\n// signature: default errors\nfunction subscription<\n State,\n RequestInit extends PayloadType,\n ResponseData extends PayloadType,\n>(def: {\n requestInit: RequestInit;\n responseData: ResponseData;\n responseError?: never;\n description?: string;\n handler: SubscriptionProcedure<\n State,\n RequestInit,\n ResponseData,\n TNever\n >['handler'];\n}): Branded<SubscriptionProcedure<State, RequestInit, ResponseData, TNever>>;\n\n// signature: explicit errors\nfunction subscription<\n State,\n RequestInit extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n>(def: {\n requestInit: RequestInit;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler: SubscriptionProcedure<\n State,\n RequestInit,\n ResponseData,\n ResponseErr\n >['handler'];\n}): Branded<\n SubscriptionProcedure<State, RequestInit, ResponseData, ResponseErr>\n>;\n\n// implementation\nfunction subscription({\n requestInit,\n responseData,\n responseError = Type.Never(),\n description,\n handler,\n}: {\n requestInit: PayloadType;\n responseData: PayloadType;\n responseError?: ProcedureErrorSchemaType;\n description?: string;\n handler: SubscriptionProcedure<\n object,\n PayloadType,\n PayloadType,\n ProcedureErrorSchemaType\n >['handler'];\n}) {\n return {\n type: 'subscription',\n ...(description ? { description } : {}),\n requestInit,\n responseData,\n responseError,\n handler,\n };\n}\n\n/**\n * Creates a {@link StreamProcedure}, optionally with an initialization message.\n */\n// signature: with default errors\nfunction stream<\n State,\n RequestInit extends PayloadType,\n RequestData extends PayloadType,\n ResponseData extends PayloadType,\n>(def: {\n requestInit: RequestInit;\n requestData: RequestData;\n responseData: ResponseData;\n responseError?: never;\n description?: string;\n handler: StreamProcedure<\n State,\n RequestInit,\n RequestData,\n ResponseData,\n TNever\n >['handler'];\n}): Branded<\n StreamProcedure<State, RequestInit, RequestData, ResponseData, TNever>\n>;\n\n// signature: explicit errors\nfunction stream<\n State,\n RequestInit extends PayloadType,\n RequestData extends PayloadType,\n ResponseData extends PayloadType,\n ResponseErr extends ProcedureErrorSchemaType,\n>(def: {\n requestInit: RequestInit;\n requestData: RequestData;\n responseData: ResponseData;\n responseError: ResponseErr;\n description?: string;\n handler: StreamProcedure<\n State,\n RequestInit,\n RequestData,\n ResponseData,\n ResponseErr\n >['handler'];\n}): Branded<\n StreamProcedure<State, RequestInit, RequestData, ResponseData, ResponseErr>\n>;\n\n// implementation\nfunction stream({\n requestInit,\n requestData,\n responseData,\n responseError = Type.Never(),\n description,\n handler,\n}: {\n requestInit: PayloadType;\n requestData: PayloadType;\n responseData: PayloadType;\n responseError?: ProcedureErrorSchemaType;\n description?: string;\n handler: StreamProcedure<\n object,\n PayloadType,\n PayloadType,\n PayloadType,\n ProcedureErrorSchemaType\n >['handler'];\n}) {\n return {\n type: 'stream',\n ...(description ? { description } : {}),\n requestInit,\n requestData,\n responseData,\n responseError,\n handler,\n };\n}\n\n/**\n * Holds the {@link Procedure} creation functions. Use these to create\n * procedures for services. You aren't allowed to create procedures directly.\n */\nexport const Procedure = {\n rpc,\n upload,\n subscription,\n stream,\n};\n","import {\n Static,\n TLiteral,\n TObject,\n TSchema,\n TString,\n TUnion,\n Type,\n} from '@sinclair/typebox';\nimport { Client } from './client';\nimport { ReadStream } from './streams';\n\ntype TLiteralString = TLiteral<string>;\n\nexport type BaseErrorSchemaType =\n | TObject<{\n code: TLiteralString | TUnion<Array<TLiteralString>>;\n message: TLiteralString | TString;\n }>\n | TObject<{\n code: TLiteralString | TUnion<Array<TLiteralString>>;\n message: TLiteralString | TString;\n extras: TSchema;\n }>;\n\n/**\n * Takes in a specific error schema and returns a result schema the error\n */\nexport const ErrResultSchema = <T extends BaseErrorSchemaType>(t: T) =>\n Type.Object({\n ok: Type.Literal(false),\n payload: t,\n });\n\n/**\n * AnyResultSchema is a schema to validate any result.\n */\nexport const AnyResultSchema = Type.Union([\n Type.Object({\n ok: Type.Literal(false),\n payload: Type.Object({\n code: Type.String(),\n message: Type.String(),\n extras: Type.Optional(Type.Unknown()),\n }),\n }),\n\n Type.Object({\n ok: Type.Literal(true),\n payload: Type.Unknown(),\n }),\n]);\n\nexport interface OkResult<T> {\n ok: true;\n payload: T;\n}\nexport interface ErrResult<Err extends Static<BaseErrorSchemaType>> {\n ok: false;\n payload: Err;\n}\nexport type Result<T, Err extends Static<BaseErrorSchemaType>> =\n | OkResult<T>\n | ErrResult<Err>;\n\nexport function Ok<const T extends Array<unknown>>(p: T): OkResult<T>;\nexport function Ok<const T extends ReadonlyArray<unknown>>(p: T): OkResult<T>;\nexport function Ok<const T>(payload: T): OkResult<T>;\nexport function Ok<const T>(payload: T): OkResult<T> {\n return {\n ok: true,\n payload,\n };\n}\n\nexport function Err<const Err extends Static<BaseErrorSchemaType>>(\n error: Err,\n): ErrResult<Err> {\n return {\n ok: false,\n payload: error,\n };\n}\n\n/**\n * Refine a {@link Result} type to its returned payload.\n */\nexport type ResultUnwrapOk<R> = R extends Result<infer T, infer __E>\n ? T\n : never;\n\n/**\n * Unwrap a {@link Result} type and return the payload if successful,\n * otherwise throws an error.\n * @param result - The result to unwrap.\n * @throws Will throw an error if the result is not ok.\n */\nexport function unwrap<T, Err extends Static<BaseErrorSchemaType>>(\n result: Result<T, Err>,\n): T {\n if (result.ok) {\n return result.payload;\n }\n\n throw new Error(\n `Cannot non-ok result, got: ${result.payload.code} - ${result.payload.message}`,\n );\n}\n\n/**\n * Refine a {@link Result} type to its error payload.\n */\nexport type ResultUnwrapErr<R> = R extends Result<infer __T, infer Err>\n ? Err\n : never;\n\n/**\n * Retrieve the output type for a procedure, represented as a {@link Result}\n * type.\n * Example:\n * ```\n * type Message = Output<typeof client, 'serviceName', 'procedureName'>\n * ```\n */\nexport type Output<\n RiverClient,\n ServiceName extends keyof RiverClient,\n ProcedureName extends keyof RiverClient[ServiceName],\n Procedure = RiverClient[ServiceName][ProcedureName],\n Fn extends (...args: never) => unknown = (...args: never) => unknown,\n> = RiverClient extends Client<infer __ServiceSchemaMap>\n ? Procedure extends object\n ? Procedure extends object & { rpc: infer RpcFn extends Fn }\n ? Awaited<ReturnType<RpcFn>>\n : Procedure extends object & { upload: infer UploadFn extends Fn }\n ? ReturnType<UploadFn> extends {\n finalize: (...args: never) => Promise<infer UploadOutputMessage>;\n }\n ? UploadOutputMessage\n : never\n : Procedure extends object & { stream: infer StreamFn extends Fn }\n ? ReturnType<StreamFn> extends {\n resReader: ReadStream<\n infer StreamOutputMessage,\n Static<BaseErrorSchemaType>\n >;\n }\n ? StreamOutputMessage\n : never\n : Procedure extends object & {\n subscribe: infer SubscriptionFn extends Fn;\n }\n ? Awaited<ReturnType<SubscriptionFn>> extends {\n resReader: ReadStream<\n infer SubscriptionOutputMessage,\n Static<BaseErrorSchemaType>\n >;\n }\n ? SubscriptionOutputMessage\n : never\n : never\n : never\n : never;\n","import { Static } from '@sinclair/typebox';\nimport { BaseErrorSchemaType, Err, Result } from './result';\n\nexport const StreamDrainedError = {\n code: 'STREAM_DRAINED',\n message: 'Stream was drained',\n} as const;\n\ntype ReadStreamResult<T, E extends Static<BaseErrorSchemaType>> = Result<\n T,\n E | typeof StreamDrainedError\n>;\n\n/**\n * Using simple iterator here to lock down the iteration and disallow\n * `return` and `throw` to be called from the outside.\n */\nexport interface SimpleIterator<T> {\n next(): Promise<\n | {\n done: false;\n value: T;\n }\n | {\n done: true;\n value: undefined;\n }\n >;\n}\n\n/**\n * A `ReadStream` represents a stream of data.\n *\n * This stream is not closable by the reader, the reader must wait for\n * the writer to close it.\n *\n * The stream can only be locked (aka consumed) once and will remain\n * locked, trying to lock the stream again will throw an TypeError.\n */\nexport interface ReadStream<T, E extends Static<BaseErrorSchemaType>> {\n /**\n * Stream implements AsyncIterator API and can be consumed via\n * for-await-of loops. Iteration locks the stream\n *\n */\n [Symbol.asyncIterator](): SimpleIterator<ReadStreamResult<T, E>>;\n /**\n * `unwrappedIter` returns an AsyncIterator that will unwrap the results coming\n * into the stream, yielding the payload if successful, otherwise throwing.\n * We generally recommend using the normal iterator instead of this method,\n * and handling errors explicitly.\n */\n unwrappedIter(): {\n [Symbol.asyncIterator](): SimpleIterator<T>;\n };\n /**\n * `asArray` locks the stream and returns a promise that resolves\n * with an array of the stream's content when the stream is closed.\n *\n */\n asArray(): Promise<Array<ReadStreamResult<T, E>>>;\n /**\n * `drain` locks the stream and discards any existing or future data.\n *\n * If there is an existing Promise waiting for the next value,\n * `drain` causes it to resolve with a {@link StreamDrainedError} error.\n */\n drain(): undefined;\n /**\n * `isLocked` returns true if the stream is locked.\n */\n isLocked(): boolean;\n /**\n * `onClose` registers a callback that will be called when the stream\n * is closed. Returns a function that can be used to unregister the\n * listener.\n */\n onClose(cb: () => void): () => void;\n /**\n * `isClosed` returns true if the stream was closed by the writer.\n *\n * Note that the stream can still have queued data and can still be\n * consumed.\n */\n isClosed(): boolean;\n /**\n * `requestClose` sends a request to the writer to close the stream,\n * and resolves the writer closes its end of the stream..\n *\n * The stream can still receive more data after the request is sent. If you\n * no longer wish to use the stream, make sure to call `drain`.\n */\n requestClose(): Promise<undefined>;\n /**\n * `isCloseRequested` checks if the reader has requested to close the stream.\n */\n isCloseRequested(): boolean;\n}\n\n/**\n * A `WriteStream` is a streams that can be written to.\n */\nexport interface WriteStream<T> {\n /**\n * `write` writes a value to the stream. An error is thrown if writing to a closed stream.\n */\n write(value: T): undefined;\n /**\n * `close` signals the closure of the write stream, informing the reader that the stream is complete.\n * Calling close multiple times has no effect.\n */\n close(): undefined;\n /**\n * `isCloseRequested` checks if the reader has requested to close the stream.\n */\n isCloseRequested(): boolean;\n /**\n * `onCloseRequest` registers a callback that will be called when the stream's\n * reader requests a close. Returns a function that can be used to unregister the\n * listener.\n */\n onCloseRequest(cb: () => void): () => void;\n /**\n * `isClosed` returns true if the stream was closed by the writer.\n */\n isClosed(): boolean;\n /**\n * `onClose` registers a callback that will be called when the stream\n * is closed. Returns a function that can be used to unregister the\n * listener.\n */\n onClose(cb: () => void): () => void;\n}\n\n/**\n * Internal implementation of a `ReadStream`.\n * This won't be exposed as an interface to river\n * consumers directly, it has internal river methods\n * to pushed data to the stream and close it.\n */\nexport class ReadStreamImpl<T, E extends Static<BaseErrorSchemaType>>\n implements ReadStream<T, E>\n{\n /**\n * Whether the stream is closed.\n */\n private closed = false;\n /**\n * A list of listeners that will be called when the stream is closed.\n */\n private onCloseListeners: Set<() => void>;\n /**\n * Whether the user has requested to close the stream.\n */\n private closeRequested = false;\n /**\n * Used to signal to the outside world that the user has requested to close the stream.\n */\n private closeRequestCallback: () => void;\n /**\n * Whether the stream is locked.\n */\n private locked = false;\n /**\n * Whether drain was called.\n */\n private drained = false;\n /**\n * This flag allows us to avoid cases where drain was called,\n * but the stream is fully consumed and closed. We don't need\n * to signal that drain was called.\n */\n private didDrainDisposeValues = false;\n /**\n * A list of values that have been pushed to the stream but not yet emitted to the user.\n */\n private queue: Array<ReadStreamResult<T, E>> = [];\n /**\n * Used by methods in the class to signal to the iterator that it\n * should check for the next value.\n */\n private nextPromise: Promise<void> | null = null;\n /**\n * Resolves nextPromise\n */\n private resolveNextPromise: null | (() => void) = null;\n\n constructor(closeRequestCallback: () => void) {\n this.closeRequestCallback = closeRequestCallback;\n this.onCloseListeners = new Set();\n }\n\n public [Symbol.asyncIterator]() {\n if (this.isLocked()) {\n throw new TypeError('ReadStream is already locked');\n }\n\n // first iteration with drain signals an error, the following one signals end of iteration.\n let didSignalDrain = false;\n this.locked = true;\n\n return {\n next: async () => {\n if (this.drained && didSignalDrain) {\n return {\n done: true,\n value: undefined,\n } as const;\n }\n\n // Wait until we have something in the queue\n while (this.queue.length === 0) {\n if (this.isClosed() && !this.didDrainDisposeValues) {\n return {\n done: true,\n value: undefined,\n } as const;\n }\n\n if (this.drained) {\n didSignalDrain = true;\n\n return {\n done: false,\n value: Err(StreamDrainedError),\n } as const;\n }\n\n if (!this.nextPromise) {\n this.nextPromise = new Promise<void>((resolve) => {\n this.resolveNextPromise = resolve;\n });\n }\n\n await this.nextPromise;\n this.nextPromise = null;\n this.resolveNextPromise = null;\n }\n\n // Unfortunately we have to use non-null assertion here, because T can be undefined\n // we already check for array length above anyway\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const value = this.queue.shift()!;\n\n return { done: false, value } as const;\n },\n return: () => {\n this.drain();\n return { done: true, value: undefined } as const;\n },\n };\n }\n\n public unwrappedIter() {\n const iterator = this[Symbol.asyncIterator]();\n\n let unwrappedLock = false;\n return {\n [Symbol.asyncIterator]() {\n if (unwrappedLock) {\n throw new TypeError('ReadStream is already locked');\n }\n\n unwrappedLock = true;\n\n return {\n next: async (): ReturnType<SimpleIterator<T>['next']> => {\n const next = await iterator.next();\n\n if (next.done) {\n return next;\n }\n\n if (next.value.ok) {\n return { done: false, value: next.value.payload };\n }\n\n iterator.return();\n\n throw new Error(\n `Got err result in unwrappedIter: ${next.value.payload.code} - ${next.value.payload.message}`,\n );\n },\n return: () => iterator.return(),\n };\n },\n };\n }\n\n public async asArray(): Promise<Array<ReadStreamResult<T, E>>> {\n const array: Array<ReadStreamResult<T, E>> = [];\n for await (const value of this) {\n array.push(value);\n }\n\n return array;\n }\n\n public drain(): undefined {\n if (this.drained) {\n return;\n }\n\n this.locked = true;\n this.drained = true;\n this.didDrainDisposeValues = this.queue.length > 0;\n this.queue.length = 0;\n\n this.resolveNextPromise?.();\n }\n\n public isClosed(): boolean {\n return this.closed;\n }\n\n public isLocked(): boolean {\n return this.locked;\n }\n\n public onClose(cb: () => void): () => void {\n if (this.isClosed()) {\n throw new Error('Stream is already closed');\n }\n\n this.onCloseListeners.add(cb);\n\n return () => {\n this.onCloseListeners.delete(cb);\n };\n }\n\n public requestClose(): Promise<undefined> {\n if (this.isClosed()) {\n throw new Error('Cannot request close after stream already closed');\n }\n\n if (!this.closeRequested) {\n this.closeRequested = true;\n this.closeRequestCallback();\n }\n\n return new Promise<undefined>((resolve) => {\n this.onClose(() => {\n resolve(undefined);\n });\n });\n }\n\n public isCloseRequested(): boolean {\n return this.closeRequested;\n }\n\n /**\n * @internal meant for use within river, not exposed as a public API\n *\n * Pushes a value to the stream.\n */\n public pushValue(value: Result<T, E>): undefined {\n if (this.drained) {\n return;\n }\n\n if (this.closed) {\n throw new Error('Cannot push to closed stream');\n }\n\n this.queue.push(value);\n this.resolveNextPromise?.();\n }\n\n /**\n * @internal meant for use within river, not exposed as a public API\n *\n * Triggers the close of the stream. Make sure to push all remaining\n * values before calling this method.\n */\n public triggerClose(): undefined {\n if (this.isClosed()) {\n throw new Error('Unexpected closing multiple times');\n }\n\n this.closed = true;\n this.resolveNextPromise?.();\n this.onCloseListeners.forEach((cb) => cb());\n this.onCloseListeners.clear();\n\n // TODO maybe log a warn if after a certain amount of time\n // the queue was not fully consumed\n }\n\n /**\n * @internal meant for use within river, not exposed as a public API\n */\n public hasValuesInQueue(): boolean {\n return this.queue.length > 0;\n }\n}\n\n/**\n * Internal implementation of a `WriteStream`.\n * This won't be exposed as an interface to river\n * consumers directly, it has internal river methods\n * to trigger a close request, a way to pass on close\n * signals, and a way to push data to the stream.\n */\nexport class WriteStreamImpl<T> implements WriteStream<T> {\n /**\n * Passed via constructor to pass on write requests\n */\n private writeCb: (value: T) => void;\n /**\n * Whether the stream is closed.\n */\n private closed = false;\n /**\n * A list of listeners that will be called when the stream is closed.\n */\n private onCloseListeners: Set<() => void>;\n /**\n * Whether the reader has requested to close the stream.\n */\n private closeRequested = false;\n /**\n * A list of listeners that will be called when a close request is triggered.\n */\n private onCloseRequestListeners: Set<() => void>;\n\n constructor(writeCb: (value: T) => void) {\n this.writeCb = writeCb;\n this.onCloseListeners = new Set();\n this.onCloseRequestListeners = new Set();\n }\n\n public write(value: T): undefined {\n if (this.isClosed()) {\n throw new Error('Cannot write to closed stream');\n }\n\n this.writeCb(value);\n }\n\n public isClosed(): boolean {\n return this.closed;\n }\n\n onClose(cb: () => void): () => void {\n if (this.isClosed()) {\n cb();\n\n return () => undefined;\n }\n\n this.onCloseListeners.add(cb);\n\n return () => this.onCloseListeners.delete(cb);\n }\n\n public close(): undefined {\n if (this.isClosed()) {\n return;\n }\n\n this.closed = true;\n this.onCloseListeners.forEach((cb) => cb());\n\n // cleanup\n this.onCloseListeners.clear();\n this.onCloseRequestListeners.clear();\n this.writeCb = () => undefined;\n }\n\n public isCloseRequested(): boolean {\n return this.closeRequested;\n }\n\n public onCloseRequest(cb: () => void): () => void {\n if (this.isClosed()) {\n throw new Error('Stream is already closed');\n }\n\n if (this.isCloseRequested()) {\n cb();\n\n return () => undefined;\n }\n\n this.onCloseRequestListeners.add(cb);\n\n return () => this.onCloseRequestListeners.delete(cb);\n }\n\n /**\n * @internal meant for use within river, not exposed as a public API\n *\n * Triggers a close request.\n */\n public triggerCloseRequest(): undefined {\n if (this.isCloseRequested()) {\n throw new Error('Cannot trigger close request multiple times');\n }\n\n if (this.isClosed()) {\n throw new Error('Cannot trigger close request on closed stream');\n }\n\n this.closeRequested = true;\n this.onCloseRequestListeners.forEach((cb) => cb());\n this.onCloseRequestListeners.clear();\n }\n}\n"],"mappings":";AACA,SAA0C,YAAY;AAwC/C,IAAM,4BAA4B;AAKlC,IAAM,sBAAsB;AAK5B,IAAM,6BAA6B;AAInC,IAAM,uBAAuB;AAI7B,IAAM,aAAa;AAMnB,IAAM,4BAA4B,KAAK,OAAO;AAAA,EACnD,MAAM,KAAK,MAAM;AAAA,IACf,KAAK,QAAQ,yBAAyB;AAAA,IACtC,KAAK,QAAQ,mBAAmB;AAAA,IAChC,KAAK,QAAQ,0BAA0B;AAAA,IACvC,KAAK,QAAQ,oBAAoB;AAAA,IACjC,KAAK,QAAQ,UAAU;AAAA,EACzB,CAAC;AAAA,EACD,SAAS,KAAK,OAAO;AACvB,CAAC;AAMM,IAAM,2BAA2B,KAAK,OAAO;AAAA,EAClD,MAAM,KAAK,MAAM;AAAA,IACf,KAAK,QAAQ,mBAAmB;AAAA,IAChC,KAAK,QAAQ,0BAA0B;AAAA,IACvC,KAAK,QAAQ,oBAAoB;AAAA,IACjC,KAAK,QAAQ,UAAU;AAAA,EACzB,CAAC;AAAA,EACD,SAAS,KAAK,OAAO;AACvB,CAAC;AA2QD,SAAS,IAAI;AAAA,EACX;AAAA,EACA;AAAA,EACA,gBAAgB,KAAK,MAAM;AAAA,EAC3B;AAAA,EACA;AACF,GAWG;AACD,SAAO;AAAA,IACL,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACrC,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAqDA,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,KAAK,MAAM;AAAA,EAC3B;AAAA,EACA;AACF,GAaG;AACD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AA6CA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA,gBAAgB,KAAK,MAAM;AAAA,EAC3B;AAAA,EACA;AACF,GAWG;AACD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAqDA,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB,KAAK,MAAM;AAAA,EAC3B;AAAA,EACA;AACF,GAaG;AACD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,IAAM,YAAY;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACrnBA;AAAA,EAOE,QAAAA;AAAA,OACK;AAoBA,IAAM,kBAAkB,CAAgC,MAC7DA,MAAK,OAAO;AAAA,EACV,IAAIA,MAAK,QAAQ,KAAK;AAAA,EACtB,SAAS;AACX,CAAC;AAKI,IAAM,kBAAkBA,MAAK,MAAM;AAAA,EACxCA,MAAK,OAAO;AAAA,IACV,IAAIA,MAAK,QAAQ,KAAK;AAAA,IACtB,SAASA,MAAK,OAAO;AAAA,MACnB,MAAMA,MAAK,OAAO;AAAA,MAClB,SAASA,MAAK,OAAO;AAAA,MACrB,QAAQA,MAAK,SAASA,MAAK,QAAQ,CAAC;AAAA,IACtC,CAAC;AAAA,EACH,CAAC;AAAA,EAEDA,MAAK,OAAO;AAAA,IACV,IAAIA,MAAK,QAAQ,IAAI;AAAA,IACrB,SAASA,MAAK,QAAQ;AAAA,EACxB,CAAC;AACH,CAAC;AAiBM,SAAS,GAAY,SAAyB;AACnD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,EACF;AACF;AAEO,SAAS,IACd,OACgB;AAChB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,SAAS;AAAA,EACX;AACF;;;AC/EO,IAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AACX;AAsIO,IAAM,iBAAN,MAEP;AAAA;AAAA;AAAA;AAAA,EAIU,SAAS;AAAA;AAAA;AAAA;AAAA,EAIT;AAAA;AAAA;AAAA;AAAA,EAIA,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAIjB;AAAA;AAAA;AAAA;AAAA,EAIA,SAAS;AAAA;AAAA;AAAA;AAAA,EAIT,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMV,wBAAwB;AAAA;AAAA;AAAA;AAAA,EAIxB,QAAuC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxC,cAAoC;AAAA;AAAA;AAAA;AAAA,EAIpC,qBAA0C;AAAA,EAElD,YAAY,sBAAkC;AAC5C,SAAK,uBAAuB;AAC5B,SAAK,mBAAmB,oBAAI,IAAI;AAAA,EAClC;AAAA,EAEA,CAAQ,OAAO,aAAa,IAAI;AAC9B,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,UAAU,8BAA8B;AAAA,IACpD;AAGA,QAAI,iBAAiB;AACrB,SAAK,SAAS;AAEd,WAAO;AAAA,MACL,MAAM,YAAY;AAChB,YAAI,KAAK,WAAW,gBAAgB;AAClC,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,UACT;AAAA,QACF;AAGA,eAAO,KAAK,MAAM,WAAW,GAAG;AAC9B,cAAI,KAAK,SAAS,KAAK,CAAC,KAAK,uBAAuB;AAClD,mBAAO;AAAA,cACL,MAAM;AAAA,cACN,OAAO;AAAA,YACT;AAAA,UACF;AAEA,cAAI,KAAK,SAAS;AAChB,6BAAiB;AAEjB,mBAAO;AAAA,cACL,MAAM;AAAA,cACN,OAAO,IAAI,kBAAkB;AAAA,YAC/B;AAAA,UACF;AAEA,cAAI,CAAC,KAAK,aAAa;AACrB,iBAAK,cAAc,IAAI,QAAc,CAAC,YAAY;AAChD,mBAAK,qBAAqB;AAAA,YAC5B,CAAC;AAAA,UACH;AAEA,gBAAM,KAAK;AACX,eAAK,cAAc;AACnB,eAAK,qBAAqB;AAAA,QAC5B;AAKA,cAAM,QAAQ,KAAK,MAAM,MAAM;AAE/B,eAAO,EAAE,MAAM,OAAO,MAAM;AAAA,MAC9B;AAAA,MACA,QAAQ,MAAM;AACZ,aAAK,MAAM;AACX,eAAO,EAAE,MAAM,MAAM,OAAO,OAAU;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AAAA,EAEO,gBAAgB;AACrB,UAAM,WAAW,KAAK,OAAO,aAAa,EAAE;AAE5C,QAAI,gBAAgB;AACpB,WAAO;AAAA,MACL,CAAC,OAAO,aAAa,IAAI;AACvB,YAAI,eAAe;AACjB,gBAAM,IAAI,UAAU,8BAA8B;AAAA,QACpD;AAEA,wBAAgB;AAEhB,eAAO;AAAA,UACL,MAAM,YAAmD;AACvD,kBAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,gBAAI,KAAK,MAAM;AACb,qBAAO;AAAA,YACT;AAEA,gBAAI,KAAK,MAAM,IAAI;AACjB,qBAAO,EAAE,MAAM,OAAO,OAAO,KAAK,MAAM,QAAQ;AAAA,YAClD;AAEA,qBAAS,OAAO;AAEhB,kBAAM,IAAI;AAAA,cACR,oCAAoC,KAAK,MAAM,QAAQ,IAAI,MAAM,KAAK,MAAM,QAAQ,OAAO;AAAA,YAC7F;AAAA,UACF;AAAA,UACA,QAAQ,MAAM,SAAS,OAAO;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,UAAkD;AAC7D,UAAM,QAAuC,CAAC;AAC9C,qBAAiB,SAAS,MAAM;AAC9B,YAAM,KAAK,KAAK;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AAAA,EAEO,QAAmB;AACxB,QAAI,KAAK,SAAS;AAChB;AAAA,IACF;AAEA,SAAK,SAAS;AACd,SAAK,UAAU;AACf,SAAK,wBAAwB,KAAK,MAAM,SAAS;AACjD,SAAK,MAAM,SAAS;AAEpB,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEO,WAAoB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,WAAoB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,QAAQ,IAA4B;AACzC,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,SAAK,iBAAiB,IAAI,EAAE;AAE5B,WAAO,MAAM;AACX,WAAK,iBAAiB,OAAO,EAAE;AAAA,IACjC;AAAA,EACF;AAAA,EAEO,eAAmC;AACxC,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAEA,QAAI,CAAC,KAAK,gBAAgB;AACxB,WAAK,iBAAiB;AACtB,WAAK,qBAAqB;AAAA,IAC5B;AAEA,WAAO,IAAI,QAAmB,CAAC,YAAY;AACzC,WAAK,QAAQ,MAAM;AACjB,gBAAQ,MAAS;AAAA,MACnB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEO,mBAA4B;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,UAAU,OAAgC;AAC/C,QAAI,KAAK,SAAS;AAChB;AAAA,IACF;AAEA,QAAI,KAAK,QAAQ;AACf,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,SAAK,MAAM,KAAK,KAAK;AACrB,SAAK,qBAAqB;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,eAA0B;AAC/B,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAEA,SAAK,SAAS;AACd,SAAK,qBAAqB;AAC1B,SAAK,iBAAiB,QAAQ,CAAC,OAAO,GAAG,CAAC;AAC1C,SAAK,iBAAiB,MAAM;AAAA,EAI9B;AAAA;AAAA;AAAA;AAAA,EAKO,mBAA4B;AACjC,WAAO,KAAK,MAAM,SAAS;AAAA,EAC7B;AACF;AASO,IAAM,kBAAN,MAAmD;AAAA;AAAA;AAAA;AAAA,EAIhD;AAAA;AAAA;AAAA;AAAA,EAIA,SAAS;AAAA;AAAA;AAAA;AAAA,EAIT;AAAA;AAAA;AAAA;AAAA,EAIA,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAIjB;AAAA,EAER,YAAY,SAA6B;AACvC,SAAK,UAAU;AACf,SAAK,mBAAmB,oBAAI,IAAI;AAChC,SAAK,0BAA0B,oBAAI,IAAI;AAAA,EACzC;AAAA,EAEO,MAAM,OAAqB;AAChC,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAEA,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEO,WAAoB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,QAAQ,IAA4B;AAClC,QAAI,KAAK,SAAS,GAAG;AACnB,SAAG;AAEH,aAAO,MAAM;AAAA,IACf;AAEA,SAAK,iBAAiB,IAAI,EAAE;AAE5B,WAAO,MAAM,KAAK,iBAAiB,OAAO,EAAE;AAAA,EAC9C;AAAA,EAEO,QAAmB;AACxB,QAAI,KAAK,SAAS,GAAG;AACnB;AAAA,IACF;AAEA,SAAK,SAAS;AACd,SAAK,iBAAiB,QAAQ,CAAC,OAAO,GAAG,CAAC;AAG1C,SAAK,iBAAiB,MAAM;AAC5B,SAAK,wBAAwB,MAAM;AACnC,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEO,mBAA4B;AACjC,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,eAAe,IAA4B;AAChD,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,QAAI,KAAK,iBAAiB,GAAG;AAC3B,SAAG;AAEH,aAAO,MAAM;AAAA,IACf;AAEA,SAAK,wBAAwB,IAAI,EAAE;AAEnC,WAAO,MAAM,KAAK,wBAAwB,OAAO,EAAE;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,sBAAiC;AACtC,QAAI,KAAK,iBAAiB,GAAG;AAC3B,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAEA,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAEA,SAAK,iBAAiB;AACtB,SAAK,wBAAwB,QAAQ,CAAC,OAAO,GAAG,CAAC;AACjD,SAAK,wBAAwB,MAAM;AAAA,EACrC;AACF;","names":["Type"]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProtocolError,
|
|
3
3
|
Transport
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Z553YSPV.js";
|
|
5
5
|
import {
|
|
6
6
|
ClientSessionStateGraph,
|
|
7
7
|
defaultClientTransportOptions
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RIN5PMQD.js";
|
|
9
9
|
import {
|
|
10
10
|
ControlMessageHandshakeResponseSchema,
|
|
11
11
|
HandshakeErrorRetriableResponseCodes,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
getPropagationContext,
|
|
15
15
|
handshakeRequestMessage,
|
|
16
16
|
tracing_default
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-TBBQ5MGX.js";
|
|
18
18
|
|
|
19
19
|
// transport/client.ts
|
|
20
20
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
@@ -197,7 +197,7 @@ var ClientTransport = class extends Transport {
|
|
|
197
197
|
`invalid handshake: ${reason}`,
|
|
198
198
|
handshakingSession.loggingMetadata
|
|
199
199
|
);
|
|
200
|
-
this.deleteSession(session);
|
|
200
|
+
this.deleteSession(session, { unhealthy: true });
|
|
201
201
|
this.protocolError({
|
|
202
202
|
type: ProtocolError.HandshakeFailed,
|
|
203
203
|
code,
|
|
@@ -226,7 +226,7 @@ var ClientTransport = class extends Transport {
|
|
|
226
226
|
message: reason
|
|
227
227
|
});
|
|
228
228
|
this.log?.warn(reason, metadata);
|
|
229
|
-
this.deleteSession(session);
|
|
229
|
+
this.deleteSession(session, { unhealthy: true });
|
|
230
230
|
}
|
|
231
231
|
onHandshakeResponse(session, msg) {
|
|
232
232
|
if (!Value.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {
|
|
@@ -241,10 +241,10 @@ var ClientTransport = class extends Transport {
|
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
243
|
if (!msg.payload.status.ok) {
|
|
244
|
-
const retriable =
|
|
244
|
+
const retriable = Value.Check(
|
|
245
245
|
HandshakeErrorRetriableResponseCodes,
|
|
246
246
|
msg.payload.status.code
|
|
247
|
-
)
|
|
247
|
+
);
|
|
248
248
|
const reason = `handshake failed: ${msg.payload.status.reason}`;
|
|
249
249
|
const to = session.to;
|
|
250
250
|
this.rejectHandshakeResponse(session, reason, {
|
|
@@ -291,9 +291,9 @@ var ClientTransport = class extends Transport {
|
|
|
291
291
|
},
|
|
292
292
|
onMessage: (msg2) => this.handleMsg(msg2),
|
|
293
293
|
onInvalidMessage: (reason) => {
|
|
294
|
-
this.deleteSession(connectedSession);
|
|
294
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
295
295
|
this.protocolError({
|
|
296
|
-
type: ProtocolError.
|
|
296
|
+
type: ProtocolError.InvalidMessage,
|
|
297
297
|
message: reason
|
|
298
298
|
});
|
|
299
299
|
}
|
|
@@ -405,6 +405,9 @@ var ClientTransport = class extends Transport {
|
|
|
405
405
|
if (this.handshakeExtensions) {
|
|
406
406
|
metadata = await this.handshakeExtensions.construct();
|
|
407
407
|
}
|
|
408
|
+
if (session._isConsumed) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
408
411
|
const requestMsg = handshakeRequestMessage({
|
|
409
412
|
from: this.clientId,
|
|
410
413
|
to: session.to,
|
|
@@ -431,4 +434,4 @@ var ClientTransport = class extends Transport {
|
|
|
431
434
|
export {
|
|
432
435
|
ClientTransport
|
|
433
436
|
};
|
|
434
|
-
//# sourceMappingURL=chunk-
|
|
437
|
+
//# sourceMappingURL=chunk-GRDOQL3Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 HandshakeErrorRetriableResponseCodes,\n OpaqueTransportMessage,\n PartialTransportMessage,\n TransportClientId,\n currentProtocolVersion,\n handshakeRequestMessage,\n} from './message';\nimport {\n ClientTransportOptions,\n ProvidedClientTransportOptions,\n defaultClientTransportOptions,\n} from './options';\nimport { LeakyBucketRateLimit } from './rateLimit';\nimport { Transport } from './transport';\nimport { coerceErrorString } from '../util/stringify';\nimport { ProtocolError } from './events';\nimport { Value } from '@sinclair/typebox/value';\nimport tracer, { getPropagationContext } from '../tracing';\nimport { Connection } from './connection';\nimport { MessageMetadata } from '../logging';\nimport { SessionConnecting } from './sessionStateMachine/SessionConnecting';\nimport { SessionHandshaking } from './sessionStateMachine/SessionHandshaking';\nimport { SessionConnected } from './sessionStateMachine/SessionConnected';\nimport {\n ClientSession,\n ClientSessionStateGraph,\n} from './sessionStateMachine/transitions';\nimport { SessionState } from './sessionStateMachine/common';\nimport { SessionNoConnection } from './sessionStateMachine/SessionNoConnection';\nimport { SessionBackingOff } from './sessionStateMachine/SessionBackingOff';\n\nexport abstract class ClientTransport<\n ConnType extends Connection,\n> extends Transport<ConnType> {\n /**\n * The options for this transport.\n */\n protected options: ClientTransportOptions;\n\n retryBudget: LeakyBucketRateLimit;\n\n /**\n * A flag indicating whether the transport should automatically reconnect\n * when a connection is dropped.\n * Realistically, this should always be true for clients unless you are writing\n * tests or a special case where you don't want to reconnect.\n */\n reconnectOnConnectionDrop = true;\n\n /**\n * Optional handshake options for this client.\n */\n handshakeExtensions?: ClientHandshakeOptions;\n\n sessions: Map<TransportClientId, ClientSession<ConnType>>;\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedClientTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.sessions = new Map();\n this.options = {\n ...defaultClientTransportOptions,\n ...providedOptions,\n };\n this.retryBudget = new LeakyBucketRateLimit(this.options);\n }\n\n extendHandshake(options: ClientHandshakeOptions) {\n this.handshakeExtensions = options;\n }\n\n /**\n * Abstract method that creates a new {@link Connection} object.\n *\n * @param to The client ID of the node to connect to.\n * @returns The new connection object.\n */\n protected abstract createNewOutgoingConnection(\n to: TransportClientId,\n ): Promise<ConnType>;\n\n private tryReconnecting(to: TransportClientId) {\n const oldSession = this.sessions.get(to);\n if (!this.options.enableTransparentSessionReconnects && oldSession) {\n this.deleteSession(oldSession);\n }\n\n if (this.reconnectOnConnectionDrop && this.getStatus() === 'open') {\n this.connect(to);\n }\n }\n\n send(to: string, msg: PartialTransportMessage): string {\n if (this.getStatus() === 'closed') {\n const err = 'transport is closed, cant send';\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n let session = this.sessions.get(to);\n if (!session) {\n session = this.createUnconnectedSession(to);\n }\n\n return session.send(msg);\n }\n\n private createUnconnectedSession(to: string): SessionNoConnection {\n const session = ClientSessionStateGraph.entrypoint(\n to,\n this.clientId,\n {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(session);\n },\n },\n this.options,\n currentProtocolVersion,\n this.log,\n );\n\n this.updateSession(session);\n return session;\n }\n\n // listeners\n protected onConnectingFailed(session: SessionConnecting<ConnType>) {\n const noConnectionSession = super.onConnectingFailed(session);\n this.tryReconnecting(noConnectionSession.to);\n return noConnectionSession;\n }\n\n protected onConnClosed(\n session: SessionHandshaking<ConnType> | SessionConnected<ConnType>,\n ) {\n const noConnectionSession = super.onConnClosed(session);\n this.tryReconnecting(noConnectionSession.to);\n return noConnectionSession;\n }\n\n protected onConnectionEstablished(\n session: SessionConnecting<ConnType>,\n conn: ConnType,\n ): SessionHandshaking<ConnType> {\n // transition to handshaking\n const handshakingSession =\n ClientSessionStateGraph.transition.ConnectingToHandshaking(\n session,\n conn,\n {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n this.log?.error(\n `connection to ${handshakingSession.to} errored during handshake: ${errStr}`,\n handshakingSession.loggingMetadata,\n );\n },\n onConnectionClosed: () => {\n this.log?.warn(\n `connection to ${handshakingSession.to} closed during handshake`,\n handshakingSession.loggingMetadata,\n );\n this.onConnClosed(handshakingSession);\n },\n onHandshake: (msg) => {\n this.onHandshakeResponse(handshakingSession, msg);\n },\n onInvalidHandshake: (reason, code) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n handshakingSession.loggingMetadata,\n );\n this.deleteSession(session, { unhealthy: true });\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\n },\n onHandshakeTimeout: () => {\n this.log?.error(\n `connection to ${handshakingSession.to} timed out during handshake`,\n handshakingSession.loggingMetadata,\n );\n this.onConnClosed(handshakingSession);\n },\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(handshakingSession);\n },\n },\n );\n\n this.updateSession(handshakingSession);\n void this.sendHandshake(handshakingSession);\n return handshakingSession;\n }\n\n private rejectHandshakeResponse(\n session: SessionHandshaking<ConnType>,\n reason: string,\n metadata: MessageMetadata,\n ) {\n session.conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: reason,\n });\n\n this.log?.warn(reason, metadata);\n this.deleteSession(session, { unhealthy: true });\n }\n\n protected onHandshakeResponse(\n session: SessionHandshaking<ConnType>,\n msg: OpaqueTransportMessage,\n ) {\n // invariant: msg is a handshake response\n if (!Value.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {\n const reason = `received invalid handshake response`;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n validationErrors: [\n ...Value.Errors(ControlMessageHandshakeResponseSchema, msg.payload),\n ],\n });\n return;\n }\n\n // invariant: handshake response should be ok\n if (!msg.payload.status.ok) {\n const retriable = Value.Check(\n HandshakeErrorRetriableResponseCodes,\n msg.payload.status.code,\n );\n\n const reason = `handshake failed: ${msg.payload.status.reason}`;\n const to = session.to;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n\n if (retriable) {\n this.tryReconnecting(to);\n } else {\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code: msg.payload.status.code,\n message: reason,\n });\n }\n\n return;\n }\n\n // invariant: session id should match between client + server\n if (msg.payload.status.sessionId !== session.id) {\n const reason = `session id mismatch: expected ${session.id}, got ${msg.payload.status.sessionId}`;\n this.rejectHandshakeResponse(session, reason, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n return;\n }\n\n // transition to connected!\n this.log?.info(`handshake from ${msg.from} ok`, {\n ...session.loggingMetadata,\n transportMessage: msg,\n });\n\n const connectedSession =\n ClientSessionStateGraph.transition.HandshakingToConnected(session, {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n this.log?.warn(\n `connection to ${connectedSession.to} errored: ${errStr}`,\n connectedSession.loggingMetadata,\n );\n },\n onConnectionClosed: () => {\n this.log?.info(\n `connection to ${connectedSession.to} closed`,\n connectedSession.loggingMetadata,\n );\n this.onConnClosed(connectedSession);\n },\n onMessage: (msg) => this.handleMsg(msg),\n onInvalidMessage: (reason) => {\n this.deleteSession(connectedSession, { unhealthy: true });\n this.protocolError({\n type: ProtocolError.InvalidMessage,\n message: reason,\n });\n },\n });\n\n this.updateSession(connectedSession);\n this.retryBudget.startRestoringBudget();\n }\n\n /**\n * Manually attempts to connect to a client.\n * @param to The client ID of the node to connect to.\n */\n connect(to: TransportClientId) {\n if (this.getStatus() !== 'open') {\n this.log?.info(\n `transport state is no longer open, cancelling attempt to connect to ${to}`,\n );\n return;\n }\n\n // create a new session if one does not exist\n let session = this.sessions.get(to);\n session ??= this.createUnconnectedSession(to);\n\n if (session.state !== SessionState.NoConnection) {\n // already trying to connect\n this.log?.debug(\n `session to ${to} has state ${session.state}, skipping connect attempt`,\n session.loggingMetadata,\n );\n return;\n }\n\n // check budget\n if (!this.retryBudget.hasBudget()) {\n const budgetConsumed = this.retryBudget.getBudgetConsumed();\n const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;\n this.log?.error(errMsg, session.loggingMetadata);\n this.protocolError({\n type: ProtocolError.RetriesExceeded,\n message: errMsg,\n });\n return;\n }\n\n const backoffMs = this.retryBudget.getBackoffMs();\n\n this.log?.info(\n `attempting connection to ${to} (${backoffMs}ms backoff)`,\n session.loggingMetadata,\n );\n\n this.retryBudget.consumeBudget();\n const backingOffSession =\n ClientSessionStateGraph.transition.NoConnectionToBackingOff(\n session,\n backoffMs,\n {\n onBackoffFinished: () => {\n this.onBackoffFinished(backingOffSession);\n },\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(backingOffSession);\n },\n },\n );\n\n this.updateSession(backingOffSession);\n }\n\n protected onBackoffFinished(session: SessionBackingOff) {\n const connPromise = tracer.startActiveSpan('connect', async (span) => {\n try {\n return await this.createNewOutgoingConnection(session.to);\n } catch (err) {\n // rethrow the error so that the promise is rejected\n // as it was before we wrapped it in a span\n const errStr = coerceErrorString(err);\n span.recordException(errStr);\n span.setStatus({ code: SpanStatusCode.ERROR });\n throw err;\n } finally {\n span.end();\n }\n });\n\n // transition to connecting\n const connectingSession =\n ClientSessionStateGraph.transition.BackingOffToConnecting(\n session,\n connPromise,\n {\n onConnectionEstablished: (conn) => {\n this.log?.debug(\n `connection to ${connectingSession.to} established`,\n {\n ...conn.loggingMetadata,\n ...connectingSession.loggingMetadata,\n },\n );\n\n // cast here because conn can't be narrowed to ConnType\n // in the callback due to variance rules\n this.onConnectionEstablished(connectingSession, conn as ConnType);\n },\n onConnectionFailed: (error: unknown) => {\n const errStr = coerceErrorString(error);\n this.log?.error(\n `error connecting to ${connectingSession.to}: ${errStr}`,\n connectingSession.loggingMetadata,\n );\n this.onConnectingFailed(connectingSession);\n },\n onConnectionTimeout: () => {\n this.log?.error(\n `connection to ${connectingSession.to} timed out`,\n connectingSession.loggingMetadata,\n );\n this.onConnectingFailed(connectingSession);\n },\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(connectingSession);\n },\n },\n );\n\n this.updateSession(connectingSession);\n }\n\n private async sendHandshake(session: SessionHandshaking<ConnType>) {\n let metadata: unknown = undefined;\n\n if (this.handshakeExtensions) {\n metadata = await this.handshakeExtensions.construct();\n }\n\n // double-check to make sure we haven't transitioned the session yet\n if (session._isConsumed) {\n // bail out, don't need to do anything\n return;\n }\n\n const requestMsg = handshakeRequestMessage({\n from: this.clientId,\n to: session.to,\n sessionId: session.id,\n expectedSessionState: {\n nextExpectedSeq: session.ack,\n nextSentSeq: session.nextSeq(),\n },\n metadata,\n tracing: getPropagationContext(session.telemetry.ctx),\n });\n\n this.log?.debug(`sending handshake request to ${session.to}`, {\n ...session.loggingMetadata,\n transportMessage: requestMsg,\n });\n\n session.sendHandshake(requestMsg);\n }\n\n close() {\n this.retryBudget.close();\n super.close();\n }\n}\n","/**\n * Options to control the backoff and retry behavior of the client transport's connection behaviour.\n *\n * River implements exponential backoff with jitter to prevent flooding the server\n * when there's an issue with connection establishment.\n *\n * The backoff is calculated via the following:\n * backOff = min(jitter + {@link baseIntervalMs} * 2 ^ budget_consumed, {@link maxBackoffMs})\n *\n * We use a leaky bucket rate limit with a budget of {@link attemptBudgetCapacity} reconnection attempts.\n * Budget only starts to restore after a successful handshake at a rate of one budget per {@link budgetRestoreIntervalMs}.\n */\nexport interface ConnectionRetryOptions {\n /**\n * The base interval to wait before retrying a connection.\n */\n baseIntervalMs: number;\n\n /**\n * The maximum random jitter to add to the total backoff time.\n */\n maxJitterMs: number;\n\n /**\n * The maximum amount of time to wait before retrying a connection.\n * This does not include the jitter.\n */\n maxBackoffMs: number;\n\n /**\n * The max number of times to attempt a connection before a successful handshake.\n * This persists across connections but starts restoring budget after a successful handshake.\n * The restoration interval depends on {@link budgetRestoreIntervalMs}\n */\n attemptBudgetCapacity: number;\n\n /**\n * After a successful connection attempt, how long to wait before we restore a single budget.\n */\n budgetRestoreIntervalMs: number;\n}\n\nexport class LeakyBucketRateLimit {\n private budgetConsumed: number;\n private intervalHandle?: ReturnType<typeof setInterval>;\n private readonly options: ConnectionRetryOptions;\n\n constructor(options: ConnectionRetryOptions) {\n this.options = options;\n this.budgetConsumed = 0;\n }\n\n getBackoffMs() {\n if (this.getBudgetConsumed() === 0) {\n return 0;\n }\n\n const exponent = Math.max(0, this.getBudgetConsumed() - 1);\n const jitter = Math.floor(Math.random() * this.options.maxJitterMs);\n const backoffMs = Math.min(\n this.options.baseIntervalMs * 2 ** exponent,\n this.options.maxBackoffMs,\n );\n\n return backoffMs + jitter;\n }\n\n get totalBudgetRestoreTime() {\n return (\n this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity\n );\n }\n\n consumeBudget() {\n // If we're consuming again, let's ensure that we're not leaking\n this.stopLeak();\n this.budgetConsumed = this.getBudgetConsumed() + 1;\n }\n\n getBudgetConsumed() {\n return this.budgetConsumed;\n }\n\n hasBudget() {\n return this.getBudgetConsumed() < this.options.attemptBudgetCapacity;\n }\n\n startRestoringBudget() {\n if (this.intervalHandle) {\n return;\n }\n\n const restoreBudgetForUser = () => {\n const currentBudget = this.budgetConsumed;\n if (!currentBudget) {\n this.stopLeak();\n return;\n }\n\n const newBudget = currentBudget - 1;\n if (newBudget === 0) {\n return;\n }\n\n this.budgetConsumed = newBudget;\n };\n\n this.intervalHandle = setInterval(\n restoreBudgetForUser,\n this.options.budgetRestoreIntervalMs,\n );\n }\n\n private stopLeak() {\n if (!this.intervalHandle) {\n return;\n }\n\n clearInterval(this.intervalHandle);\n this.intervalHandle = undefined;\n }\n\n close() {\n this.stopLeak();\n }\n}\n"],"mappings":"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","names":["msg"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Connection
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z553YSPV.js";
|
|
4
4
|
|
|
5
5
|
// transport/impls/ws/connection.ts
|
|
6
6
|
var WebSocketConnection = class extends Connection {
|
|
@@ -47,4 +47,4 @@ var WebSocketConnection = class extends Connection {
|
|
|
47
47
|
export {
|
|
48
48
|
WebSocketConnection
|
|
49
49
|
};
|
|
50
|
-
//# sourceMappingURL=chunk-
|
|
50
|
+
//# sourceMappingURL=chunk-H2NIGO66.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../transport/impls/ws/connection.ts"],"sourcesContent":["import { Connection } from '../../connection';\nimport { WsLike } from './wslike';\n\nexport class WebSocketConnection extends Connection {\n ws: WsLike;\n\n constructor(ws: WsLike) {\n super();\n this.ws = ws;\n this.ws.binaryType = 'arraybuffer';\n\n // Websockets are kinda shitty, they emit error events with no\n // information other than it errored, so we have to do some extra\n // work to figure out what happened.\n let didError = false;\n this.ws.onerror = () => {\n didError = true;\n };\n\n this.ws.onclose = ({ code, reason }) => {\n if (didError) {\n const err = new Error(\n `websocket closed with code and reason: ${code} - ${reason}`,\n );\n\n for (const cb of this.errorListeners) {\n cb(err);\n }\n }\n\n for (const cb of this.closeListeners) {\n cb();\n }\n };\n\n this.ws.onmessage = (msg) => {\n for (const cb of this.dataListeners) {\n cb(msg.data as Uint8Array);\n }\n };\n }\n\n send(payload: Uint8Array) {\n if (this.ws.readyState !== this.ws.OPEN) {\n return false;\n }\n this.ws.send(payload);\n return true;\n }\n\n close() {\n this.ws.close();\n }\n}\n"],"mappings":";;;;;AAGO,IAAM,sBAAN,cAAkC,WAAW;AAAA,EAClD;AAAA,EAEA,YAAY,IAAY;AACtB,UAAM;AACN,SAAK,KAAK;AACV,SAAK,GAAG,aAAa;AAKrB,QAAI,WAAW;AACf,SAAK,GAAG,UAAU,MAAM;AACtB,iBAAW;AAAA,IACb;AAEA,SAAK,GAAG,UAAU,CAAC,EAAE,MAAM,OAAO,MAAM;AACtC,UAAI,UAAU;AACZ,cAAM,MAAM,IAAI;AAAA,UACd,0CAA0C,IAAI,MAAM,MAAM;AAAA,QAC5D;AAEA,mBAAW,MAAM,KAAK,gBAAgB;AACpC,aAAG,GAAG;AAAA,QACR;AAAA,MACF;AAEA,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG;AAAA,MACL;AAAA,IACF;AAEA,SAAK,GAAG,YAAY,CAAC,QAAQ;AAC3B,iBAAW,MAAM,KAAK,eAAe;AACnC,WAAG,IAAI,IAAkB;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,KAAK,SAAqB;AACxB,QAAI,KAAK,GAAG,eAAe,KAAK,GAAG,MAAM;AACvC,aAAO;AAAA,IACT;
|
|
1
|
+
{"version":3,"sources":["../transport/impls/ws/connection.ts"],"sourcesContent":["import { Connection } from '../../connection';\nimport { WsLike } from './wslike';\n\nexport class WebSocketConnection extends Connection {\n ws: WsLike;\n\n constructor(ws: WsLike) {\n super();\n this.ws = ws;\n this.ws.binaryType = 'arraybuffer';\n\n // Websockets are kinda shitty, they emit error events with no\n // information other than it errored, so we have to do some extra\n // work to figure out what happened.\n let didError = false;\n this.ws.onerror = () => {\n didError = true;\n };\n\n this.ws.onclose = ({ code, reason }) => {\n if (didError) {\n const err = new Error(\n `websocket closed with code and reason: ${code} - ${reason}`,\n );\n\n for (const cb of this.errorListeners) {\n cb(err);\n }\n }\n\n for (const cb of this.closeListeners) {\n cb();\n }\n };\n\n this.ws.onmessage = (msg) => {\n for (const cb of this.dataListeners) {\n cb(msg.data as Uint8Array);\n }\n };\n }\n\n send(payload: Uint8Array) {\n if (this.ws.readyState !== this.ws.OPEN) {\n return false;\n }\n\n this.ws.send(payload);\n return true;\n }\n\n close() {\n this.ws.close();\n }\n}\n"],"mappings":";;;;;AAGO,IAAM,sBAAN,cAAkC,WAAW;AAAA,EAClD;AAAA,EAEA,YAAY,IAAY;AACtB,UAAM;AACN,SAAK,KAAK;AACV,SAAK,GAAG,aAAa;AAKrB,QAAI,WAAW;AACf,SAAK,GAAG,UAAU,MAAM;AACtB,iBAAW;AAAA,IACb;AAEA,SAAK,GAAG,UAAU,CAAC,EAAE,MAAM,OAAO,MAAM;AACtC,UAAI,UAAU;AACZ,cAAM,MAAM,IAAI;AAAA,UACd,0CAA0C,IAAI,MAAM,MAAM;AAAA,QAC5D;AAEA,mBAAW,MAAM,KAAK,gBAAgB;AACpC,aAAG,GAAG;AAAA,QACR;AAAA,MACF;AAEA,iBAAW,MAAM,KAAK,gBAAgB;AACpC,WAAG;AAAA,MACL;AAAA,IACF;AAEA,SAAK,GAAG,YAAY,CAAC,QAAQ;AAC3B,iBAAW,MAAM,KAAK,eAAe;AACnC,WAAG,IAAI,IAAkB;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,KAAK,SAAqB;AACxB,QAAI,KAAK,GAAG,eAAe,KAAK,GAAG,MAAM;AACvC,aAAO;AAAA,IACT;AAEA,SAAK,GAAG,KAAK,OAAO;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ;AACN,SAAK,GAAG,MAAM;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ProtocolError,
|
|
3
3
|
Transport
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Z553YSPV.js";
|
|
5
5
|
import {
|
|
6
6
|
ServerSessionStateGraph,
|
|
7
7
|
defaultServerTransportOptions
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RIN5PMQD.js";
|
|
9
9
|
import {
|
|
10
10
|
ControlMessageHandshakeRequestSchema,
|
|
11
11
|
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
coerceErrorString,
|
|
14
14
|
currentProtocolVersion,
|
|
15
15
|
handshakeResponseMessage
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-TBBQ5MGX.js";
|
|
17
17
|
|
|
18
18
|
// transport/server.ts
|
|
19
19
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
@@ -74,9 +74,9 @@ var ServerTransport = class extends Transport {
|
|
|
74
74
|
pendingSession.close();
|
|
75
75
|
this.pendingSessions.delete(pendingSession);
|
|
76
76
|
}
|
|
77
|
-
deleteSession(session) {
|
|
77
|
+
deleteSession(session, options) {
|
|
78
78
|
this.sessionHandshakeMetadata.delete(session.to);
|
|
79
|
-
super.deleteSession(session);
|
|
79
|
+
super.deleteSession(session, options);
|
|
80
80
|
}
|
|
81
81
|
handleConnection(conn) {
|
|
82
82
|
if (this.getStatus() !== "open")
|
|
@@ -254,7 +254,7 @@ var ServerTransport = class extends Transport {
|
|
|
254
254
|
}
|
|
255
255
|
let connectCase = "new session";
|
|
256
256
|
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
257
|
-
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq
|
|
257
|
+
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
|
|
258
258
|
if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
|
|
259
259
|
connectCase = "transparent reconnection";
|
|
260
260
|
const ourNextSeq = oldSession.nextSeq();
|
|
@@ -370,10 +370,10 @@ var ServerTransport = class extends Transport {
|
|
|
370
370
|
onMessage: (msg2) => this.handleMsg(msg2),
|
|
371
371
|
onInvalidMessage: (reason) => {
|
|
372
372
|
this.protocolError({
|
|
373
|
-
type: ProtocolError.
|
|
373
|
+
type: ProtocolError.InvalidMessage,
|
|
374
374
|
message: reason
|
|
375
375
|
});
|
|
376
|
-
this.deleteSession(connectedSession);
|
|
376
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
377
377
|
}
|
|
378
378
|
},
|
|
379
379
|
gotVersion
|
|
@@ -388,4 +388,4 @@ var ServerTransport = class extends Transport {
|
|
|
388
388
|
export {
|
|
389
389
|
ServerTransport
|
|
390
390
|
};
|
|
391
|
-
//# sourceMappingURL=chunk-
|
|
391
|
+
//# sourceMappingURL=chunk-R5KEFZGW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../transport/server.ts"],"sourcesContent":["import { SpanStatusCode } from '@opentelemetry/api';\nimport { ParsedMetadata } from '../router/context';\nimport { ServerHandshakeOptions } from '../router/handshake';\nimport {\n ControlMessageHandshakeRequestSchema,\n HandshakeErrorCustomHandlerFatalResponseCodes,\n HandshakeErrorResponseCodes,\n OpaqueTransportMessage,\n acceptedProtocolVersions,\n PartialTransportMessage,\n TransportClientId,\n handshakeResponseMessage,\n currentProtocolVersion,\n} from './message';\nimport {\n ProvidedServerTransportOptions,\n ServerTransportOptions,\n defaultServerTransportOptions,\n} from './options';\nimport { DeleteSessionOptions, Transport } from './transport';\nimport { coerceErrorString } from '../util/stringify';\nimport { Static } from '@sinclair/typebox';\nimport { Value } from '@sinclair/typebox/value';\nimport { ProtocolError } from './events';\nimport { Connection } from './connection';\nimport { MessageMetadata } from '../logging';\nimport { SessionWaitingForHandshake } from './sessionStateMachine/SessionWaitingForHandshake';\nimport { SessionState } from './sessionStateMachine/common';\nimport {\n ServerSession,\n ServerSessionStateGraph,\n} from './sessionStateMachine/transitions';\n\nexport abstract class ServerTransport<\n ConnType extends Connection,\n> extends Transport<ConnType> {\n /**\n * The options for this transport.\n */\n protected options: ServerTransportOptions;\n\n /**\n * Optional handshake options for the server.\n */\n handshakeExtensions?: ServerHandshakeOptions;\n\n /**\n * A map of session handshake data for each session.\n */\n sessionHandshakeMetadata = new Map<TransportClientId, ParsedMetadata>();\n\n sessions = new Map<TransportClientId, ServerSession<ConnType>>();\n pendingSessions = new Set<SessionWaitingForHandshake<ConnType>>();\n\n constructor(\n clientId: TransportClientId,\n providedOptions?: ProvidedServerTransportOptions,\n ) {\n super(clientId, providedOptions);\n this.sessions = new Map();\n this.options = {\n ...defaultServerTransportOptions,\n ...providedOptions,\n };\n this.log?.info(`initiated server transport`, {\n clientId: this.clientId,\n protocolVersion: currentProtocolVersion,\n });\n }\n\n extendHandshake(options: ServerHandshakeOptions) {\n this.handshakeExtensions = options;\n }\n\n send(to: string, msg: PartialTransportMessage): string {\n if (this.getStatus() === 'closed') {\n const err = 'transport is closed, cant send';\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n const session = this.sessions.get(to);\n if (!session) {\n const err = `session to ${to} does not exist`;\n this.log?.error(err, {\n clientId: this.clientId,\n transportMessage: msg,\n tags: ['invariant-violation'],\n });\n\n throw new Error(err);\n }\n\n return session.send(msg);\n }\n\n protected deletePendingSession(\n pendingSession: SessionWaitingForHandshake<ConnType>,\n ) {\n pendingSession.close();\n // we don't dispatch a session disconnect event\n // for a non-identified session, just delete directly\n\n this.pendingSessions.delete(pendingSession);\n }\n\n protected deleteSession(\n session: ServerSession<ConnType>,\n options?: DeleteSessionOptions,\n ): void {\n this.sessionHandshakeMetadata.delete(session.to);\n super.deleteSession(session, options);\n }\n\n protected handleConnection(conn: ConnType) {\n if (this.getStatus() !== 'open') return;\n\n this.log?.info(`new incoming connection`, {\n ...conn.loggingMetadata,\n clientId: this.clientId,\n });\n\n let receivedHandshake = false;\n const pendingSession = ServerSessionStateGraph.entrypoint(\n this.clientId,\n conn,\n {\n onConnectionClosed: () => {\n this.log?.warn(\n `connection from unknown closed before handshake finished`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onConnectionErrored: (err) => {\n const errorString = coerceErrorString(err);\n this.log?.warn(\n `connection from unknown errored before handshake finished: ${errorString}`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onHandshakeTimeout: () => {\n this.log?.warn(\n `connection from unknown timed out before handshake finished`,\n pendingSession.loggingMetadata,\n );\n\n this.deletePendingSession(pendingSession);\n },\n onHandshake: (msg) => {\n if (receivedHandshake) {\n this.log?.error(\n `received multiple handshake messages from pending session`,\n {\n ...pendingSession.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n this.deletePendingSession(pendingSession);\n return;\n }\n\n // let this resolve async, we just need to make sure its only\n // called once so we don't race while transitioning to connected\n // onHandshakeRequest is async as custom validation may be async\n receivedHandshake = true;\n void this.onHandshakeRequest(pendingSession, msg);\n },\n onInvalidHandshake: (reason, code) => {\n this.log?.error(\n `invalid handshake: ${reason}`,\n pendingSession.loggingMetadata,\n );\n this.deletePendingSession(pendingSession);\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\n },\n },\n this.options,\n this.log,\n );\n\n this.pendingSessions.add(pendingSession);\n }\n\n private rejectHandshakeRequest(\n session: SessionWaitingForHandshake<ConnType>,\n to: TransportClientId,\n reason: string,\n code: Static<typeof HandshakeErrorResponseCodes>,\n metadata: MessageMetadata,\n ) {\n session.conn.telemetry?.span.setStatus({\n code: SpanStatusCode.ERROR,\n message: reason,\n });\n\n this.log?.warn(reason, metadata);\n\n session.sendHandshake(\n handshakeResponseMessage({\n from: this.clientId,\n to,\n status: {\n ok: false,\n code,\n reason,\n },\n }),\n );\n\n this.protocolError({\n type: ProtocolError.HandshakeFailed,\n code,\n message: reason,\n });\n this.deletePendingSession(session);\n }\n\n protected async onHandshakeRequest(\n session: SessionWaitingForHandshake<ConnType>,\n msg: OpaqueTransportMessage,\n ) {\n // invariant: msg is a handshake request\n if (!Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'received invalid handshake request',\n 'MALFORMED_HANDSHAKE',\n {\n ...session.loggingMetadata,\n transportMessage: msg,\n connectedTo: msg.from,\n validationErrors: [\n ...Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload),\n ],\n },\n );\n\n return;\n }\n\n // invariant: handshake request passes all the validation\n const gotVersion = msg.payload.protocolVersion;\n if (!acceptedProtocolVersions.includes(gotVersion)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `expected protocol version oneof [${acceptedProtocolVersions.toString()}], got ${gotVersion}`,\n 'PROTOCOL_VERSION_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n let oldSession = this.sessions.get(msg.from);\n\n // invariant: must pass custom validation if defined\n let parsedMetadata: ParsedMetadata = {};\n if (this.handshakeExtensions) {\n if (!Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'received malformed handshake metadata',\n 'MALFORMED_HANDSHAKE_META',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n validationErrors: [\n ...Value.Errors(\n this.handshakeExtensions.schema,\n msg.payload.metadata,\n ),\n ],\n },\n );\n\n return;\n }\n\n const previousParsedMetadata = oldSession\n ? this.sessionHandshakeMetadata.get(oldSession.to)\n : undefined;\n\n const parsedMetadataOrFailureCode =\n await this.handshakeExtensions.validate(\n msg.payload.metadata,\n previousParsedMetadata,\n );\n\n // double-check to make sure we haven't transitioned the session yet\n if (session._isConsumed) {\n // bail out, don't need to do anything\n return;\n }\n\n // handler rejected the connection\n if (\n Value.Check(\n HandshakeErrorCustomHandlerFatalResponseCodes,\n parsedMetadataOrFailureCode,\n )\n ) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n 'rejected by handshake handler',\n parsedMetadataOrFailureCode,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n clientId: this.clientId,\n },\n );\n\n return;\n }\n\n // success!\n parsedMetadata = parsedMetadataOrFailureCode;\n }\n\n // 4 connect cases\n // 1. new session\n // we dont have a session and the client is requesting a new one\n // we can create the session as normal\n // 2. client is reconnecting to an existing session but we don't have it\n // reject this handshake, there's nothing we can do to salvage it\n // 3. transparent reconnect (old session exists and is the same as the client wants)\n // assign to old session\n // 4. hard reconnect (oldSession exists but but the client wants a new one)\n // we close the old session and create a new one\n let connectCase:\n | 'new session'\n | 'unknown session'\n | 'transparent reconnection'\n | 'hard reconnection' = 'new session';\n const clientNextExpectedSeq =\n msg.payload.expectedSessionState.nextExpectedSeq;\n const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;\n\n if (\n this.options.enableTransparentSessionReconnects &&\n oldSession &&\n oldSession.id === msg.payload.sessionId\n ) {\n connectCase = 'transparent reconnection';\n\n // invariant: ordering must be correct\n const ourNextSeq = oldSession.nextSeq();\n const ourAck = oldSession.ack;\n\n // two incorrect cases where we cannot permit a reconnect:\n // - if the client is about to send a message in the future w.r.t to the server\n // - client.seq > server.ack => nextSentSeq > oldSession.ack\n if (clientNextSentSeq > ourAck) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n // - if the server is about to send a message in the future w.r.t to the client\n // - server.seq > client.ack => oldSession.nextSeq() > nextExpectedSeq\n if (ourNextSeq > clientNextExpectedSeq) {\n this.rejectHandshakeRequest(\n session,\n msg.from,\n `server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n\n return;\n }\n\n // transparent reconnect seems ok, proceed by transitioning old session\n // to not connected\n if (oldSession.state !== SessionState.NoConnection) {\n const noConnectionSession =\n ServerSessionStateGraph.transition.ConnectedToNoConnection(\n oldSession,\n {\n onSessionGracePeriodElapsed: () => {\n this.onSessionGracePeriodElapsed(noConnectionSession);\n },\n },\n );\n\n oldSession = noConnectionSession;\n }\n\n this.updateSession(oldSession);\n } else if (oldSession) {\n connectCase = 'hard reconnection';\n\n // just nuke the old session entirely and proceed as if this was new\n this.log?.info(\n `client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n sessionId: msg.payload.sessionId,\n },\n );\n this.deleteSession(oldSession);\n oldSession = undefined;\n }\n\n if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {\n // we don't have a session, but the client is trying to reconnect\n // to an old session. we can't do anything about this, so we reject\n connectCase = 'unknown session';\n\n const rejectionMessage = this.options.enableTransparentSessionReconnects\n ? `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`\n : `client is attempting a transparent reconnect to a session but the server does not support it: ${msg.payload.sessionId}`;\n\n this.rejectHandshakeRequest(\n session,\n msg.from,\n rejectionMessage,\n 'SESSION_STATE_MISMATCH',\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n transportMessage: msg,\n },\n );\n return;\n }\n\n // from this point on, we're committed to connecting\n const sessionId = msg.payload.sessionId;\n this.log?.info(\n `handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,\n {\n ...session.loggingMetadata,\n connectedTo: msg.from,\n },\n );\n\n const responseMsg = handshakeResponseMessage({\n from: this.clientId,\n to: msg.from,\n status: {\n ok: true,\n sessionId,\n },\n });\n\n session.sendHandshake(responseMsg);\n\n // transition\n const connectedSession =\n ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(\n session,\n // by this point oldSession is either no connection or we dont have an old session\n oldSession,\n sessionId,\n msg.from,\n msg.tracing,\n {\n onConnectionErrored: (err) => {\n // just log, when we error we also emit close\n const errStr = coerceErrorString(err);\n this.log?.warn(\n `connection to ${connectedSession.to} errored: ${errStr}`,\n connectedSession.loggingMetadata,\n );\n },\n onConnectionClosed: () => {\n this.log?.info(\n `connection to ${connectedSession.to} closed`,\n connectedSession.loggingMetadata,\n );\n this.onConnClosed(connectedSession);\n },\n onMessage: (msg) => this.handleMsg(msg),\n onInvalidMessage: (reason) => {\n this.protocolError({\n type: ProtocolError.InvalidMessage,\n message: reason,\n });\n this.deleteSession(connectedSession, { unhealthy: true });\n },\n },\n gotVersion,\n );\n\n this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);\n this.updateSession(connectedSession);\n this.pendingSessions.delete(session);\n connectedSession.startActiveHeartbeat();\n }\n}\n"],"mappings":"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","names":["msg"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createSessionTelemetryInfo,
|
|
4
4
|
generateId,
|
|
5
5
|
isAck
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TBBQ5MGX.js";
|
|
7
7
|
import {
|
|
8
8
|
NaiveJsonCodec
|
|
9
9
|
} from "./chunk-4PVU7J25.js";
|
|
@@ -414,13 +414,13 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
414
414
|
this.conn.addCloseListener(this.listeners.onConnectionClosed);
|
|
415
415
|
this.conn.addErrorListener(this.listeners.onConnectionErrored);
|
|
416
416
|
if (this.sendBuffer.length > 0) {
|
|
417
|
-
this.log?.
|
|
418
|
-
`sending ${this.sendBuffer.length} buffered messages`,
|
|
417
|
+
this.log?.info(
|
|
418
|
+
`sending ${this.sendBuffer.length} buffered messages, starting at seq ${this.nextSeq()}`,
|
|
419
419
|
this.loggingMetadata
|
|
420
420
|
);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
421
|
+
for (const msg of this.sendBuffer) {
|
|
422
|
+
this.conn.send(this.options.codec.toBuffer(msg));
|
|
423
|
+
}
|
|
424
424
|
}
|
|
425
425
|
this.isActivelyHeartbeating = false;
|
|
426
426
|
this.heartbeatHandle = setInterval(() => {
|
|
@@ -462,6 +462,12 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
462
462
|
}
|
|
463
463
|
});
|
|
464
464
|
}
|
|
465
|
+
closeConnection() {
|
|
466
|
+
this.conn.removeDataListener(this.onMessageData);
|
|
467
|
+
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
468
|
+
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
469
|
+
this.conn.close();
|
|
470
|
+
}
|
|
465
471
|
onMessageData = (msg) => {
|
|
466
472
|
const parsedMsg = this.parseMsg(msg);
|
|
467
473
|
if (parsedMsg === null) {
|
|
@@ -478,8 +484,8 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
478
484
|
}
|
|
479
485
|
);
|
|
480
486
|
} else {
|
|
481
|
-
const reason = `received out-of-order msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;
|
|
482
|
-
this.log?.
|
|
487
|
+
const reason = `received out-of-order msg, closing connection (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;
|
|
488
|
+
this.log?.warn(reason, {
|
|
483
489
|
...this.loggingMetadata,
|
|
484
490
|
transportMessage: parsedMsg,
|
|
485
491
|
tags: ["invariant-violation"]
|
|
@@ -488,7 +494,7 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
488
494
|
code: SpanStatusCode.ERROR,
|
|
489
495
|
message: reason
|
|
490
496
|
});
|
|
491
|
-
this.
|
|
497
|
+
this.closeConnection();
|
|
492
498
|
}
|
|
493
499
|
return;
|
|
494
500
|
}
|
|
@@ -514,8 +520,10 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
514
520
|
this.conn.removeDataListener(this.onMessageData);
|
|
515
521
|
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
516
522
|
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
517
|
-
|
|
518
|
-
|
|
523
|
+
if (this.heartbeatHandle) {
|
|
524
|
+
clearInterval(this.heartbeatHandle);
|
|
525
|
+
this.heartbeatHandle = void 0;
|
|
526
|
+
}
|
|
519
527
|
}
|
|
520
528
|
_handleClose() {
|
|
521
529
|
super._handleClose();
|
|
@@ -849,4 +857,4 @@ export {
|
|
|
849
857
|
ClientSessionStateGraph,
|
|
850
858
|
ServerSessionStateGraph
|
|
851
859
|
};
|
|
852
|
-
//# sourceMappingURL=chunk-
|
|
860
|
+
//# sourceMappingURL=chunk-RIN5PMQD.js.map
|