@milaboratories/pl-client 2.17.8 → 2.17.9

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.
@@ -41,7 +41,7 @@ function isAbortedError(err, nested = false) {
41
41
  if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true;
42
42
  if (err.name == "RpcError" && err.code == "ABORTED") return true;
43
43
  if (err.name == "RESTError" && err.status.code == require_code.Code.ABORTED) return true;
44
- if (err.cause !== void 0 && !nested) isAbortedError(err.cause, true);
44
+ if (err.cause !== void 0 && !nested) return isAbortedError(err.cause, true);
45
45
  return false;
46
46
  }
47
47
  function isTimeoutOrCancelError(err, nested = false) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":["Code","Aborted"],"sources":["../../src/core/errors.ts"],"sourcesContent":["import type { Status } from \"../proto-grpc/github.com/googleapis/googleapis/google/rpc/status\";\nimport { Aborted } from \"@milaboratories/ts-helpers\";\nimport { Code } from \"../proto-grpc/google/rpc/code\";\n\nexport function isConnectionProblem(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof DisconnectedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAVAILABLE\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAVAILABLE) return true;\n if ((err as any).cause !== undefined && !nested)\n return isConnectionProblem((err as any).cause, true);\n return false;\n}\n\nexport function isUnauthenticated(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof UnauthenticatedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAUTHENTICATED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAUTHENTICATED)\n return true;\n if ((err as any).cause !== undefined && !nested)\n return isUnauthenticated((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"TimeoutError\") return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"DEADLINE_EXCEEDED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.DEADLINE_EXCEEDED)\n return true;\n if ((err as any).cause !== undefined && !nested) return isTimeoutError((err as any).cause, true);\n return false;\n}\n\nexport function isCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"CANCELLED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.CANCELLED) return true;\n if ((err as any).cause !== undefined && !nested) return isCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isAbortedError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof Aborted || (err as any).name == \"AbortError\") return true;\n if ((err as any).code == \"ABORT_ERR\") return true;\n if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true; // WebSocket error\n if ((err as any).name == \"RpcError\" && (err as any).code == \"ABORTED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.ABORTED) return true;\n if ((err as any).cause !== undefined && !nested) isAbortedError((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutOrCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (isAbortedError(err, true)) return true;\n if (isTimeoutError(err, true)) return true;\n if (isCancelError(err, true)) return true;\n if ((err as any).cause !== undefined && !nested)\n return isTimeoutOrCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isNotFoundError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"NOT_FOUND\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.NOT_FOUND) return true;\n if ((err as any).cause !== undefined && !nested) return isNotFoundError((err as any).cause, true);\n return err instanceof RecoverablePlError && err.status.code === PlErrorCodeNotFound;\n}\n\nexport const PlErrorCodeNotFound: number = Code.NOT_FOUND;\n\nexport class PlError extends Error {\n name = \"PlError\";\n constructor(\n public readonly status: Status,\n opts?: ErrorOptions,\n ) {\n super(`code=${status.code} ${status.message}`, opts);\n }\n}\n\nexport function throwPlNotFoundError(message: string): never {\n throw new RecoverablePlError({ code: PlErrorCodeNotFound, message, details: [] });\n}\n\nexport class RecoverablePlError extends PlError {\n name = \"RecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnrecoverablePlError extends PlError {\n name = \"UnrecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnauthenticatedError extends Error {\n name = \"UnauthenticatedError\";\n constructor(message: string) {\n super(\"LoginFailed: \" + message);\n }\n}\n\nexport class DisconnectedError extends Error {\n name = \"DisconnectedError\";\n constructor(message: string) {\n super(\"Disconnected: \" + message);\n }\n}\n\nexport class RESTError extends PlError {\n name = \"RESTError\";\n constructor(status: Status, opts?: ErrorOptions) {\n super(status, opts);\n }\n}\n\nexport function rethrowMeaningfulError(error: any, wrapIfUnknown: boolean = false): never {\n if (isUnauthenticated(error)) {\n if (error instanceof UnauthenticatedError) throw error;\n throw new UnauthenticatedError(error.message);\n }\n if (isConnectionProblem(error)) {\n if (error instanceof DisconnectedError) throw error;\n throw new DisconnectedError(error.message);\n }\n if (isTimeoutOrCancelError(error)) throw new Aborted(error);\n if (wrapIfUnknown) {\n const message = error.message || String(error) || \"Unknown error\";\n throw new Error(message, { cause: error });\n } else throw error;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"errors.cjs","names":["Code","Aborted"],"sources":["../../src/core/errors.ts"],"sourcesContent":["import type { Status } from \"../proto-grpc/github.com/googleapis/googleapis/google/rpc/status\";\nimport { Aborted } from \"@milaboratories/ts-helpers\";\nimport { Code } from \"../proto-grpc/google/rpc/code\";\n\nexport function isConnectionProblem(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof DisconnectedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAVAILABLE\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAVAILABLE) return true;\n if ((err as any).cause !== undefined && !nested)\n return isConnectionProblem((err as any).cause, true);\n return false;\n}\n\nexport function isUnauthenticated(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof UnauthenticatedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAUTHENTICATED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAUTHENTICATED)\n return true;\n if ((err as any).cause !== undefined && !nested)\n return isUnauthenticated((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"TimeoutError\") return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"DEADLINE_EXCEEDED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.DEADLINE_EXCEEDED)\n return true;\n if ((err as any).cause !== undefined && !nested) return isTimeoutError((err as any).cause, true);\n return false;\n}\n\nexport function isCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"CANCELLED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.CANCELLED) return true;\n if ((err as any).cause !== undefined && !nested) return isCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isAbortedError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof Aborted || (err as any).name == \"AbortError\") return true;\n if ((err as any).code == \"ABORT_ERR\") return true;\n if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true; // WebSocket error\n if ((err as any).name == \"RpcError\" && (err as any).code == \"ABORTED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.ABORTED) return true;\n if ((err as any).cause !== undefined && !nested) return isAbortedError((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutOrCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (isAbortedError(err, true)) return true;\n if (isTimeoutError(err, true)) return true;\n if (isCancelError(err, true)) return true;\n if ((err as any).cause !== undefined && !nested)\n return isTimeoutOrCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isNotFoundError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"NOT_FOUND\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.NOT_FOUND) return true;\n if ((err as any).cause !== undefined && !nested) return isNotFoundError((err as any).cause, true);\n return err instanceof RecoverablePlError && err.status.code === PlErrorCodeNotFound;\n}\n\nexport const PlErrorCodeNotFound: number = Code.NOT_FOUND;\n\nexport class PlError extends Error {\n name = \"PlError\";\n constructor(\n public readonly status: Status,\n opts?: ErrorOptions,\n ) {\n super(`code=${status.code} ${status.message}`, opts);\n }\n}\n\nexport function throwPlNotFoundError(message: string): never {\n throw new RecoverablePlError({ code: PlErrorCodeNotFound, message, details: [] });\n}\n\nexport class RecoverablePlError extends PlError {\n name = \"RecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnrecoverablePlError extends PlError {\n name = \"UnrecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnauthenticatedError extends Error {\n name = \"UnauthenticatedError\";\n constructor(message: string) {\n super(\"LoginFailed: \" + message);\n }\n}\n\nexport class DisconnectedError extends Error {\n name = \"DisconnectedError\";\n constructor(message: string) {\n super(\"Disconnected: \" + message);\n }\n}\n\nexport class RESTError extends PlError {\n name = \"RESTError\";\n constructor(status: Status, opts?: ErrorOptions) {\n super(status, opts);\n }\n}\n\nexport function rethrowMeaningfulError(error: any, wrapIfUnknown: boolean = false): never {\n if (isUnauthenticated(error)) {\n if (error instanceof UnauthenticatedError) throw error;\n throw new UnauthenticatedError(error.message);\n }\n if (isConnectionProblem(error)) {\n if (error instanceof DisconnectedError) throw error;\n throw new DisconnectedError(error.message);\n }\n if (isTimeoutOrCancelError(error)) throw new Aborted(error);\n if (wrapIfUnknown) {\n const message = error.message || String(error) || \"Unknown error\";\n throw new Error(message, { cause: error });\n } else throw error;\n}\n"],"mappings":"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"}
@@ -40,7 +40,7 @@ function isAbortedError(err, nested = false) {
40
40
  if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true;
41
41
  if (err.name == "RpcError" && err.code == "ABORTED") return true;
42
42
  if (err.name == "RESTError" && err.status.code == Code.ABORTED) return true;
43
- if (err.cause !== void 0 && !nested) isAbortedError(err.cause, true);
43
+ if (err.cause !== void 0 && !nested) return isAbortedError(err.cause, true);
44
44
  return false;
45
45
  }
46
46
  function isTimeoutOrCancelError(err, nested = false) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["import type { Status } from \"../proto-grpc/github.com/googleapis/googleapis/google/rpc/status\";\nimport { Aborted } from \"@milaboratories/ts-helpers\";\nimport { Code } from \"../proto-grpc/google/rpc/code\";\n\nexport function isConnectionProblem(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof DisconnectedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAVAILABLE\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAVAILABLE) return true;\n if ((err as any).cause !== undefined && !nested)\n return isConnectionProblem((err as any).cause, true);\n return false;\n}\n\nexport function isUnauthenticated(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof UnauthenticatedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAUTHENTICATED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAUTHENTICATED)\n return true;\n if ((err as any).cause !== undefined && !nested)\n return isUnauthenticated((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"TimeoutError\") return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"DEADLINE_EXCEEDED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.DEADLINE_EXCEEDED)\n return true;\n if ((err as any).cause !== undefined && !nested) return isTimeoutError((err as any).cause, true);\n return false;\n}\n\nexport function isCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"CANCELLED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.CANCELLED) return true;\n if ((err as any).cause !== undefined && !nested) return isCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isAbortedError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof Aborted || (err as any).name == \"AbortError\") return true;\n if ((err as any).code == \"ABORT_ERR\") return true;\n if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true; // WebSocket error\n if ((err as any).name == \"RpcError\" && (err as any).code == \"ABORTED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.ABORTED) return true;\n if ((err as any).cause !== undefined && !nested) isAbortedError((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutOrCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (isAbortedError(err, true)) return true;\n if (isTimeoutError(err, true)) return true;\n if (isCancelError(err, true)) return true;\n if ((err as any).cause !== undefined && !nested)\n return isTimeoutOrCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isNotFoundError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"NOT_FOUND\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.NOT_FOUND) return true;\n if ((err as any).cause !== undefined && !nested) return isNotFoundError((err as any).cause, true);\n return err instanceof RecoverablePlError && err.status.code === PlErrorCodeNotFound;\n}\n\nexport const PlErrorCodeNotFound: number = Code.NOT_FOUND;\n\nexport class PlError extends Error {\n name = \"PlError\";\n constructor(\n public readonly status: Status,\n opts?: ErrorOptions,\n ) {\n super(`code=${status.code} ${status.message}`, opts);\n }\n}\n\nexport function throwPlNotFoundError(message: string): never {\n throw new RecoverablePlError({ code: PlErrorCodeNotFound, message, details: [] });\n}\n\nexport class RecoverablePlError extends PlError {\n name = \"RecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnrecoverablePlError extends PlError {\n name = \"UnrecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnauthenticatedError extends Error {\n name = \"UnauthenticatedError\";\n constructor(message: string) {\n super(\"LoginFailed: \" + message);\n }\n}\n\nexport class DisconnectedError extends Error {\n name = \"DisconnectedError\";\n constructor(message: string) {\n super(\"Disconnected: \" + message);\n }\n}\n\nexport class RESTError extends PlError {\n name = \"RESTError\";\n constructor(status: Status, opts?: ErrorOptions) {\n super(status, opts);\n }\n}\n\nexport function rethrowMeaningfulError(error: any, wrapIfUnknown: boolean = false): never {\n if (isUnauthenticated(error)) {\n if (error instanceof UnauthenticatedError) throw error;\n throw new UnauthenticatedError(error.message);\n }\n if (isConnectionProblem(error)) {\n if (error instanceof DisconnectedError) throw error;\n throw new DisconnectedError(error.message);\n }\n if (isTimeoutOrCancelError(error)) throw new Aborted(error);\n if (wrapIfUnknown) {\n const message = error.message || String(error) || \"Unknown error\";\n throw new Error(message, { cause: error });\n } else throw error;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["import type { Status } from \"../proto-grpc/github.com/googleapis/googleapis/google/rpc/status\";\nimport { Aborted } from \"@milaboratories/ts-helpers\";\nimport { Code } from \"../proto-grpc/google/rpc/code\";\n\nexport function isConnectionProblem(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof DisconnectedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAVAILABLE\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAVAILABLE) return true;\n if ((err as any).cause !== undefined && !nested)\n return isConnectionProblem((err as any).cause, true);\n return false;\n}\n\nexport function isUnauthenticated(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof UnauthenticatedError) return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"UNAUTHENTICATED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.UNAUTHENTICATED)\n return true;\n if ((err as any).cause !== undefined && !nested)\n return isUnauthenticated((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"TimeoutError\") return true;\n if ((err as any).name == \"RpcError\" && (err as any).code == \"DEADLINE_EXCEEDED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.DEADLINE_EXCEEDED)\n return true;\n if ((err as any).cause !== undefined && !nested) return isTimeoutError((err as any).cause, true);\n return false;\n}\n\nexport function isCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"CANCELLED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.CANCELLED) return true;\n if ((err as any).cause !== undefined && !nested) return isCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isAbortedError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (err instanceof Aborted || (err as any).name == \"AbortError\") return true;\n if ((err as any).code == \"ABORT_ERR\") return true;\n if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true; // WebSocket error\n if ((err as any).name == \"RpcError\" && (err as any).code == \"ABORTED\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.ABORTED) return true;\n if ((err as any).cause !== undefined && !nested) return isAbortedError((err as any).cause, true);\n return false;\n}\n\nexport function isTimeoutOrCancelError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if (isAbortedError(err, true)) return true;\n if (isTimeoutError(err, true)) return true;\n if (isCancelError(err, true)) return true;\n if ((err as any).cause !== undefined && !nested)\n return isTimeoutOrCancelError((err as any).cause, true);\n return false;\n}\n\nexport function isNotFoundError(err: unknown, nested: boolean = false): boolean {\n if (err === undefined || err === null) return false;\n\n if ((err as any).name == \"RpcError\" && (err as any).code == \"NOT_FOUND\") return true;\n if ((err as any).name == \"RESTError\" && (err as any).status.code == Code.NOT_FOUND) return true;\n if ((err as any).cause !== undefined && !nested) return isNotFoundError((err as any).cause, true);\n return err instanceof RecoverablePlError && err.status.code === PlErrorCodeNotFound;\n}\n\nexport const PlErrorCodeNotFound: number = Code.NOT_FOUND;\n\nexport class PlError extends Error {\n name = \"PlError\";\n constructor(\n public readonly status: Status,\n opts?: ErrorOptions,\n ) {\n super(`code=${status.code} ${status.message}`, opts);\n }\n}\n\nexport function throwPlNotFoundError(message: string): never {\n throw new RecoverablePlError({ code: PlErrorCodeNotFound, message, details: [] });\n}\n\nexport class RecoverablePlError extends PlError {\n name = \"RecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnrecoverablePlError extends PlError {\n name = \"UnrecoverablePlError\";\n constructor(status: Status) {\n super(status);\n }\n}\n\nexport class UnauthenticatedError extends Error {\n name = \"UnauthenticatedError\";\n constructor(message: string) {\n super(\"LoginFailed: \" + message);\n }\n}\n\nexport class DisconnectedError extends Error {\n name = \"DisconnectedError\";\n constructor(message: string) {\n super(\"Disconnected: \" + message);\n }\n}\n\nexport class RESTError extends PlError {\n name = \"RESTError\";\n constructor(status: Status, opts?: ErrorOptions) {\n super(status, opts);\n }\n}\n\nexport function rethrowMeaningfulError(error: any, wrapIfUnknown: boolean = false): never {\n if (isUnauthenticated(error)) {\n if (error instanceof UnauthenticatedError) throw error;\n throw new UnauthenticatedError(error.message);\n }\n if (isConnectionProblem(error)) {\n if (error instanceof DisconnectedError) throw error;\n throw new DisconnectedError(error.message);\n }\n if (isTimeoutOrCancelError(error)) throw new Aborted(error);\n if (wrapIfUnknown) {\n const message = error.message || String(error) || \"Unknown error\";\n throw new Error(message, { cause: error });\n } else throw error;\n}\n"],"mappings":"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"}
@@ -28,6 +28,7 @@ var WebSocketBiDiStream = class {
28
28
  responseQueue = new denque.default();
29
29
  responseResolvers = [];
30
30
  lastError;
31
+ abortHandler;
31
32
  requests = {
32
33
  send: async (message) => {
33
34
  return await this.enqueueSend(message);
@@ -68,7 +69,8 @@ var WebSocketBiDiStream = class {
68
69
  this.progressConnectionState(ConnectionState.CLOSED);
69
70
  return;
70
71
  }
71
- this.options.abortSignal?.addEventListener("abort", () => this.close());
72
+ this.abortHandler = () => this.close();
73
+ this.options.abortSignal?.addEventListener("abort", this.abortHandler, { once: true });
72
74
  this.connect();
73
75
  }
74
76
  connect() {
@@ -119,6 +121,7 @@ var WebSocketBiDiStream = class {
119
121
  }
120
122
  onClose() {
121
123
  this.progressConnectionState(ConnectionState.CLOSED);
124
+ if (this.abortHandler) this.options.abortSignal?.removeEventListener("abort", this.abortHandler);
122
125
  if (this.options.abortSignal?.aborted && !this.lastError) {
123
126
  const reason = this.options.abortSignal.reason;
124
127
  if (reason instanceof Error) this.lastError = reason;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket_stream.cjs","names":["Denque","RetryStrategy","WebSocket","ErrorEvent","DisconnectedError"],"sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from \"undici\";\nimport type { BiDiStream } from \"./abstract_stream\";\nimport Denque from \"denque\";\nimport type { RetryConfig } from \"../helpers/retry_strategy\";\nimport { RetryStrategy } from \"../helpers/retry_strategy\";\nimport { DisconnectedError } from \"./errors\";\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<\n ClientMsg extends object,\n ServerMsg extends object,\n> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (\n stream: WebSocketBiDiStream<ClientMsg, ServerMsg>,\n ) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => {\n void this.connect();\n },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.options.abortSignal?.addEventListener(\"abort\", () => this.close());\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener(\"open\", () => this.onOpen());\n this.ws.addEventListener(\"message\", (event) => this.onMessage(event.data));\n this.ws.addEventListener(\"error\", (error) => this.onError(error));\n this.ws.addEventListener(\"close\", () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken)\n options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = \"arraybuffer\";\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n // If abort signal was triggered, use that as the error source\n if (this.options.abortSignal?.aborted && !this.lastError) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n this.lastError = reason;\n } else if (reason !== undefined) {\n this.lastError = new Error(String(reason), { cause: reason });\n } else {\n this.lastError = this.createStreamClosedError();\n }\n }\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error(\"Cannot send: stream already completed\");\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error(\"Cannot send: stream aborted\");\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error(\"WebSocket is not connected\");\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(() => this.sendQueue.length === 0, POLL_INTERVAL_MS);\n }\n }\n\n private waitForCondition(condition: () => boolean, intervalMs: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error(\"Stream aborted\"));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error(\"Stream aborted\"));\n };\n\n this.options.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error(\"Stream aborted\", { cause: reason });\n }\n\n return new Error(\"Stream closed\");\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return (\n this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false\n );\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) {\n const err = error.error;\n // undici WebSocket throws TypeError with empty message on socket close\n // (e.g., when connection is lost or server disconnects)\n if (err instanceof TypeError && !err.message) {\n return new DisconnectedError(\"WebSocket connection closed unexpectedly\");\n }\n return err instanceof Error ? err : new Error(\"WebSocket error\", { cause: error });\n }\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"websocket_stream.cjs","names":["Denque","RetryStrategy","WebSocket","ErrorEvent","DisconnectedError"],"sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from \"undici\";\nimport type { BiDiStream } from \"./abstract_stream\";\nimport Denque from \"denque\";\nimport type { RetryConfig } from \"../helpers/retry_strategy\";\nimport { RetryStrategy } from \"../helpers/retry_strategy\";\nimport { DisconnectedError } from \"./errors\";\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<\n ClientMsg extends object,\n ServerMsg extends object,\n> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (\n stream: WebSocketBiDiStream<ClientMsg, ServerMsg>,\n ) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // Abort listener reference for cleanup\n private readonly abortHandler?: () => void;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => {\n void this.connect();\n },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.abortHandler = () => this.close();\n this.options.abortSignal?.addEventListener(\"abort\", this.abortHandler, { once: true });\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener(\"open\", () => this.onOpen());\n this.ws.addEventListener(\"message\", (event) => this.onMessage(event.data));\n this.ws.addEventListener(\"error\", (error) => this.onError(error));\n this.ws.addEventListener(\"close\", () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken)\n options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = \"arraybuffer\";\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n // Clean up abort listener to prevent memory leaks\n if (this.abortHandler) {\n this.options.abortSignal?.removeEventListener(\"abort\", this.abortHandler);\n }\n\n // If abort signal was triggered, use that as the error source\n if (this.options.abortSignal?.aborted && !this.lastError) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n this.lastError = reason;\n } else if (reason !== undefined) {\n this.lastError = new Error(String(reason), { cause: reason });\n } else {\n this.lastError = this.createStreamClosedError();\n }\n }\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error(\"Cannot send: stream already completed\");\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error(\"Cannot send: stream aborted\");\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error(\"WebSocket is not connected\");\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(() => this.sendQueue.length === 0, POLL_INTERVAL_MS);\n }\n }\n\n private waitForCondition(condition: () => boolean, intervalMs: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error(\"Stream aborted\"));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error(\"Stream aborted\"));\n };\n\n this.options.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error(\"Stream aborted\", { cause: reason });\n }\n\n return new Error(\"Stream closed\");\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return (\n this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false\n );\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) {\n const err = error.error;\n // undici WebSocket throws TypeError with empty message on socket close\n // (e.g., when connection is lost or server disconnects)\n if (err instanceof TypeError && !err.message) {\n return new DisconnectedError(\"WebSocket connection closed unexpectedly\");\n }\n return err instanceof Error ? err : new Error(\"WebSocket error\", { cause: error });\n }\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"mappings":"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"}
@@ -26,6 +26,7 @@ var WebSocketBiDiStream = class {
26
26
  responseQueue = new Denque();
27
27
  responseResolvers = [];
28
28
  lastError;
29
+ abortHandler;
29
30
  requests = {
30
31
  send: async (message) => {
31
32
  return await this.enqueueSend(message);
@@ -66,7 +67,8 @@ var WebSocketBiDiStream = class {
66
67
  this.progressConnectionState(ConnectionState.CLOSED);
67
68
  return;
68
69
  }
69
- this.options.abortSignal?.addEventListener("abort", () => this.close());
70
+ this.abortHandler = () => this.close();
71
+ this.options.abortSignal?.addEventListener("abort", this.abortHandler, { once: true });
70
72
  this.connect();
71
73
  }
72
74
  connect() {
@@ -117,6 +119,7 @@ var WebSocketBiDiStream = class {
117
119
  }
118
120
  onClose() {
119
121
  this.progressConnectionState(ConnectionState.CLOSED);
122
+ if (this.abortHandler) this.options.abortSignal?.removeEventListener("abort", this.abortHandler);
120
123
  if (this.options.abortSignal?.aborted && !this.lastError) {
121
124
  const reason = this.options.abortSignal.reason;
122
125
  if (reason instanceof Error) this.lastError = reason;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket_stream.js","names":[],"sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from \"undici\";\nimport type { BiDiStream } from \"./abstract_stream\";\nimport Denque from \"denque\";\nimport type { RetryConfig } from \"../helpers/retry_strategy\";\nimport { RetryStrategy } from \"../helpers/retry_strategy\";\nimport { DisconnectedError } from \"./errors\";\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<\n ClientMsg extends object,\n ServerMsg extends object,\n> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (\n stream: WebSocketBiDiStream<ClientMsg, ServerMsg>,\n ) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => {\n void this.connect();\n },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.options.abortSignal?.addEventListener(\"abort\", () => this.close());\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener(\"open\", () => this.onOpen());\n this.ws.addEventListener(\"message\", (event) => this.onMessage(event.data));\n this.ws.addEventListener(\"error\", (error) => this.onError(error));\n this.ws.addEventListener(\"close\", () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken)\n options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = \"arraybuffer\";\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n // If abort signal was triggered, use that as the error source\n if (this.options.abortSignal?.aborted && !this.lastError) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n this.lastError = reason;\n } else if (reason !== undefined) {\n this.lastError = new Error(String(reason), { cause: reason });\n } else {\n this.lastError = this.createStreamClosedError();\n }\n }\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error(\"Cannot send: stream already completed\");\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error(\"Cannot send: stream aborted\");\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error(\"WebSocket is not connected\");\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(() => this.sendQueue.length === 0, POLL_INTERVAL_MS);\n }\n }\n\n private waitForCondition(condition: () => boolean, intervalMs: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error(\"Stream aborted\"));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error(\"Stream aborted\"));\n };\n\n this.options.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error(\"Stream aborted\", { cause: reason });\n }\n\n return new Error(\"Stream closed\");\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return (\n this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false\n );\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) {\n const err = error.error;\n // undici WebSocket throws TypeError with empty message on socket close\n // (e.g., when connection is lost or server disconnects)\n if (err instanceof TypeError && !err.message) {\n return new DisconnectedError(\"WebSocket connection closed unexpectedly\");\n }\n return err instanceof Error ? err : new Error(\"WebSocket error\", { cause: error });\n }\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"mappings":";;;;;;AAkBA,IAAK,kBAAL;AACE;AACA;AACA;AACA;AACA;;EALG,sBAMJ;;;;;AAgBD,IAAa,sBAAb,MAG8C;CAE5C,AAAQ,KAAuB;CAC/B,AAAQ,kBAAmC,gBAAgB;CAC3D,AAAiB;CAGjB,AAAiB,YAAY,IAAI,QAAkC;CACnE,AAAQ,gBAAgB;CACxB,AAAiB;CAKjB,AAAiB,gBAAgB,IAAI,QAAmB;CACxD,AAAQ,oBAAmD,EAAE;CAG7D,AAAQ;CAIR,AAAgB,WAAW;EACzB,MAAM,OAAO,YAAsC;AACjD,UAAO,MAAM,KAAK,YAAY,QAAQ;;EAGxC,UAAU,YAA2B;AACnC,OAAI,KAAK,cAAe;AAExB,SAAM,KAAK,gBAAgB;AAC3B,OAAI;AACF,UAAM,KAAK,WAAW,KAAK;WACrB;AAKR,QAAK,gBAAgB;;EAExB;CAED,AAAgB,YAAsC,GACnD,OAAO,sBAAsB,KAAK,wBAAwB,EAC5D;CAED,AAAO,QAAc;AACnB,OAAK,aAAa,QAAQ;AAE1B,MAAI,KAAK,kBAAkB,gBAAgB,WAAW;AAEpD,QAAK,IAAI,OAAO;AAChB,QAAK,SAAS;AACd;;AAGF,MAAI,CAAC,KAAK,wBAAwB,gBAAgB,QAAQ,CAAE;AAC5D,OAAK,GAAI,OAAO;;CAGlB,YACE,AAAiB,KACjB,AAAiB,wBACjB,AAAiB,oBACjB,AAAiB,UAAiD,EAAE,EACpE;EAJiB;EACA;EACA;EACA;AAEjB,OAAK,aAAa,KAAK,QAAQ,gBAAgB,WAAW,OAAO,OAAO;AAGxE,OAAK,eAAe,IAAI,cADJ,KAAK,QAAQ,eAAe,EAAE,EACC;GACjD,eAAe;AACb,IAAK,KAAK,SAAS;;GAErB,uBAAuB,UAAU,KAAK,YAAY,MAAM;GACzD,CAAC;AAEF,MAAI,KAAK,QAAQ,aAAa,SAAS;AACrC,QAAK,wBAAwB,gBAAgB,OAAO;AACpD;;AAGF,OAAK,QAAQ,aAAa,iBAAiB,eAAe,KAAK,OAAO,CAAC;AACvE,OAAK,SAAS;;CAKhB,AAAQ,UAAgB;AACtB,MAAI,KAAK,QAAQ,aAAa,QAAS;AAGvC,MAAI,CAAC,KAAK,wBAAwB,gBAAgB,WAAW,CAAE;AAE/D,MAAI;AACF,QAAK,KAAK,KAAK,iBAAiB;AAEhC,QAAK,GAAG,iBAAiB,cAAc,KAAK,QAAQ,CAAC;AACrD,QAAK,GAAG,iBAAiB,YAAY,UAAU,KAAK,UAAU,MAAM,KAAK,CAAC;AAC1E,QAAK,GAAG,iBAAiB,UAAU,UAAU,KAAK,QAAQ,MAAM,CAAC;AACjE,QAAK,GAAG,iBAAiB,eAAe,KAAK,SAAS,CAAC;WAChD,OAAO;AACd,QAAK,YAAY,KAAK,QAAQ,MAAM;AACpC,QAAK,aAAa,UAAU;;;CAIhC,AAAQ,kBAA6B;EACnC,MAAM,UAAyB,EAAE;AAEjC,MAAI,KAAK,QAAQ,SACf,SAAQ,UAAU,EAAE,eAAe,UAAU,KAAK,QAAQ,YAAY;AACxE,MAAI,KAAK,QAAQ,WAAY,SAAQ,aAAa,KAAK,QAAQ;EAE/D,MAAM,KAAK,IAAI,UAAU,KAAK,KAAK,QAAQ;AAC3C,KAAG,aAAa;AAChB,SAAO;;CAGT,AAAQ,SAAe;AACrB,OAAK,wBAAwB,gBAAgB,UAAU;AACvD,OAAK,kBAAkB;;CAGzB,AAAQ,UAAU,MAAqB;AACrC,MAAI,EAAE,gBAAgB,cAAc;AAClC,QAAK,4BAAY,IAAI,MAAM,iCAAiC,OAAO,OAAO,CAAC;AAC3E;;AAGF,MAAI;GACF,MAAM,UAAU,KAAK,mBAAmB,IAAI,WAAW,KAAK,CAAC;AAC7D,QAAK,gBAAgB,QAAQ;WACtB,OAAO;AACd,QAAK,YAAY,KAAK,QAAQ,MAAM,CAAC;;;CAIzC,AAAQ,QAAQ,OAAsB;AACpC,MAAI,KAAK,kBAAkB,gBAAgB,WAAW;AAEpD,QAAK,YAAY,KAAK,QAAQ,MAAM;AACpC,QAAK,aAAa,UAAU;AAC5B;;AAGF,OAAK,YAAY,KAAK,QAAQ,MAAM,CAAC;;CAGvC,AAAQ,UAAgB;AACtB,OAAK,wBAAwB,gBAAgB,OAAO;AAGpD,MAAI,KAAK,QAAQ,aAAa,WAAW,CAAC,KAAK,WAAW;GACxD,MAAM,SAAS,KAAK,QAAQ,YAAY;AACxC,OAAI,kBAAkB,MACpB,MAAK,YAAY;YACR,WAAW,OACpB,MAAK,YAAY,IAAI,MAAM,OAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,CAAC;OAE7D,MAAK,YAAY,KAAK,yBAAyB;;AAInD,MAAI,CAAC,KAAK,WAAW;AACnB,QAAK,wBAAwB,KAAK,yBAAyB,CAAC;AAC5D,QAAK,4BAA4B;QAEjC,MAAK,2BAA2B,KAAK,UAAU;;CAMnD,AAAQ,YAAY,SAAmC;AACrD,MAAI,KAAK,cACP,OAAM,IAAI,MAAM,wCAAwC;AAG1D,MAAI,KAAK,QAAQ,aAAa,QAC5B,OAAM,IAAI,MAAM,8BAA8B;AAGhD,SAAO,IAAI,SAAe,SAAS,WAAW;AAC5C,QAAK,UAAU,KAAK;IAAE;IAAS;IAAS;IAAQ,CAAC;AACjD,QAAK,kBAAkB;IACvB;;CAGJ,AAAQ,mBAAyB;AAC/B,MAAI,CAAC,KAAK,iBAAiB,CAAE;AAE7B,SAAO,KAAK,UAAU,SAAS,GAAG;GAChC,MAAM,SAAS,KAAK,UAAU,OAAO;AACrC,QAAK,kBAAkB,OAAO;;;CAIlC,AAAQ,kBAA2B;AACjC,SAAO,KAAK,oBAAoB,gBAAgB;;CAGlD,AAAQ,kBAAkB,QAAwC;AAChE,MAAI;GACF,MAAM,KAAK,KAAK;AAChB,OAAI,CAAC,GACH,OAAM,IAAI,MAAM,6BAA6B;AAI/C,OAAI,GAAG,eAAe,UAAU,KAC9B,OAAM,IAAI,MAAM,sCAAsC,GAAG,WAAW,GAAG;GAGzE,MAAM,SAAS,KAAK,uBAAuB,OAAO,QAAQ;AAC1D,MAAG,KAAK,OAAO;AACf,UAAO,SAAS;WACT,OAAO;AACd,UAAO,OAAO,KAAK,QAAQ,MAAM,CAAC;;;CAItC,MAAc,iBAAgC;EAC5C,MAAM,mBAAmB;AAEzB,SAAO,KAAK,UAAU,SAAS,EAC7B,OAAM,KAAK,uBAAuB,KAAK,UAAU,WAAW,GAAG,iBAAiB;;CAIpF,AAAQ,iBAAiB,WAA0B,YAAmC;AACpF,SAAO,IAAI,SAAe,SAAS,WAAW;AAC5C,OAAI,KAAK,QAAQ,aAAa,QAC5B,QAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,YAAY,OAAO,oBAAI,IAAI,MAAM,iBAAiB,CAAC;GAG7F,IAAI;GACJ,MAAM,gBAAgB;AACpB,iBAAa,UAAU;AACvB,WAAO,KAAK,QAAQ,KAAK,QAAQ,aAAa,OAAO,oBAAI,IAAI,MAAM,iBAAiB,CAAC;;AAGvF,QAAK,QAAQ,aAAa,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;GAE5E,MAAM,cAAc;AAClB,QAAI,WAAW,IAAI,KAAK,eAAe,EAAE;AACvC,UAAK,QAAQ,aAAa,oBAAoB,SAAS,QAAQ;AAC/D,cAAS;UAET,aAAY,WAAW,OAAO,WAAW;;AAI7C,UAAO;IACP;;CAKJ,AAAQ,gBAAgB,SAA0B;AAChD,MAAI,KAAK,kBAAkB,SAAS,EAElC,CADiB,KAAK,kBAAkB,OAAO,CACtC,QAAQ;GAAE,OAAO;GAAS,MAAM;GAAO,CAAC;MAEjD,MAAK,cAAc,KAAK,QAAQ;;CAIpC,OAAe,yBAAmD;AAChE,SAAO,MAAM;GACX,MAAM,SAAS,MAAM,KAAK,cAAc;AAExC,OAAI,OAAO,KAAM;AAEjB,SAAM,OAAO;;;CAIjB,AAAQ,eAAmD;AACzD,SAAO,IAAI,SAAoC,SAAS,WAAW;AAEjE,OAAI,KAAK,cAAc,SAAS,GAAG;AAEjC,YAAQ;KAAE,OADM,KAAK,cAAc,OAAO;KAChB,MAAM;KAAO,CAAC;AACxC;;AAIF,OAAI,KAAK,eAAe,EAAE;AACxB,QAAI,KAAK,UACP,QAAO,KAAK,UAAU;QAEtB,SAAQ;KAAE,OAAO;KAAkB,MAAM;KAAM,CAAC;AAElD;;AAIF,QAAK,kBAAkB,KAAK;IAAE;IAAS;IAAQ,CAAC;IAChD;;CAGJ,AAAQ,6BAAmC;AACzC,SAAO,KAAK,kBAAkB,SAAS,EAErC,CADiB,KAAK,kBAAkB,OAAO,CACtC,QAAQ;GAAE,OAAO;GAAkB,MAAM;GAAM,CAAC;;CAM7D,AAAQ,YAAY,OAAoB;AACtC,OAAK,YAAY;AACjB,OAAK,OAAO;;CAGd,AAAQ,2BAA2B,OAAoB;AACrD,OAAK,wBAAwB,MAAM;AACnC,OAAK,2BAA2B,MAAM;;CAGxC,AAAQ,wBAAwB,OAAoB;AAClD,SAAO,KAAK,UAAU,SAAS,EAE7B,CADe,KAAK,UAAU,OAAO,CAC9B,OAAO,MAAM;;CAIxB,AAAQ,2BAA2B,OAAoB;AACrD,SAAO,KAAK,kBAAkB,SAAS,EAErC,CADiB,KAAK,kBAAkB,OAAO,CACtC,OAAO,MAAM;;CAI1B,AAAQ,0BAAiC;AACvC,MAAI,KAAK,QAAQ,aAAa,SAAS;GACrC,MAAM,SAAS,KAAK,QAAQ,YAAY;AACxC,OAAI,kBAAkB,MACpB,QAAO;AAET,UAAO,IAAI,MAAM,kBAAkB,EAAE,OAAO,QAAQ,CAAC;;AAGvD,yBAAO,IAAI,MAAM,gBAAgB;;CAKnC,AAAQ,gBAAyB;AAC/B,SACE,KAAK,oBAAoB,gBAAgB,UAAU,KAAK,QAAQ,aAAa,WAAW;;CAI5F,AAAQ,QAAQ,OAAuB;AACrC,MAAI,iBAAiB,MAAO,QAAO;AACnC,MAAI,iBAAiB,YAAY;GAC/B,MAAM,MAAM,MAAM;AAGlB,OAAI,eAAe,aAAa,CAAC,IAAI,QACnC,QAAO,IAAI,kBAAkB,2CAA2C;AAE1E,UAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,mBAAmB,EAAE,OAAO,OAAO,CAAC;;AAEpF,SAAO,IAAI,MAAM,OAAO,MAAM,CAAC;;;;;;;;CASjC,AAAQ,wBAAwB,UAAoC;AAClE,MAAI,WAAW,KAAK,gBAClB,QAAO;AAET,OAAK,kBAAkB;AACvB,SAAO"}
1
+ {"version":3,"file":"websocket_stream.js","names":[],"sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from \"undici\";\nimport type { BiDiStream } from \"./abstract_stream\";\nimport Denque from \"denque\";\nimport type { RetryConfig } from \"../helpers/retry_strategy\";\nimport { RetryStrategy } from \"../helpers/retry_strategy\";\nimport { DisconnectedError } from \"./errors\";\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<\n ClientMsg extends object,\n ServerMsg extends object,\n> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (\n stream: WebSocketBiDiStream<ClientMsg, ServerMsg>,\n ) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // Abort listener reference for cleanup\n private readonly abortHandler?: () => void;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => {\n void this.connect();\n },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.abortHandler = () => this.close();\n this.options.abortSignal?.addEventListener(\"abort\", this.abortHandler, { once: true });\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener(\"open\", () => this.onOpen());\n this.ws.addEventListener(\"message\", (event) => this.onMessage(event.data));\n this.ws.addEventListener(\"error\", (error) => this.onError(error));\n this.ws.addEventListener(\"close\", () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken)\n options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = \"arraybuffer\";\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n // Clean up abort listener to prevent memory leaks\n if (this.abortHandler) {\n this.options.abortSignal?.removeEventListener(\"abort\", this.abortHandler);\n }\n\n // If abort signal was triggered, use that as the error source\n if (this.options.abortSignal?.aborted && !this.lastError) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n this.lastError = reason;\n } else if (reason !== undefined) {\n this.lastError = new Error(String(reason), { cause: reason });\n } else {\n this.lastError = this.createStreamClosedError();\n }\n }\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error(\"Cannot send: stream already completed\");\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error(\"Cannot send: stream aborted\");\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error(\"WebSocket is not connected\");\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(() => this.sendQueue.length === 0, POLL_INTERVAL_MS);\n }\n }\n\n private waitForCondition(condition: () => boolean, intervalMs: number): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error(\"Stream aborted\"));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error(\"Stream aborted\"));\n };\n\n this.options.abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error(\"Stream aborted\", { cause: reason });\n }\n\n return new Error(\"Stream closed\");\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return (\n this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false\n );\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) {\n const err = error.error;\n // undici WebSocket throws TypeError with empty message on socket close\n // (e.g., when connection is lost or server disconnects)\n if (err instanceof TypeError && !err.message) {\n return new DisconnectedError(\"WebSocket connection closed unexpectedly\");\n }\n return err instanceof Error ? err : new Error(\"WebSocket error\", { cause: error });\n }\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-client",
3
- "version": "2.17.8",
3
+ "version": "2.17.9",
4
4
  "description": "New TS/JS client for Platform API",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -30,9 +30,9 @@
30
30
  "undici": "~7.16.0",
31
31
  "utility-types": "^3.11.0",
32
32
  "yaml": "^2.8.0",
33
+ "@milaboratories/pl-http": "1.2.4",
33
34
  "@milaboratories/pl-model-common": "1.25.2",
34
- "@milaboratories/ts-helpers": "1.7.3",
35
- "@milaboratories/pl-http": "1.2.4"
35
+ "@milaboratories/ts-helpers": "1.7.3"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@protobuf-ts/plugin": "2.11.1",
@@ -41,8 +41,8 @@
41
41
  "openapi-typescript": "^7.10.0",
42
42
  "typescript": "~5.9.3",
43
43
  "vitest": "^4.0.18",
44
- "@milaboratories/build-configs": "1.5.1",
45
- "@milaboratories/ts-builder": "1.2.13",
44
+ "@milaboratories/ts-builder": "1.2.14",
45
+ "@milaboratories/build-configs": "1.5.2",
46
46
  "@milaboratories/ts-configs": "1.2.2"
47
47
  },
48
48
  "engines": {
@@ -53,7 +53,7 @@ export function isAbortedError(err: unknown, nested: boolean = false): boolean {
53
53
  if (err instanceof DOMException && err.code === DOMException.ABORT_ERR) return true; // WebSocket error
54
54
  if ((err as any).name == "RpcError" && (err as any).code == "ABORTED") return true;
55
55
  if ((err as any).name == "RESTError" && (err as any).status.code == Code.ABORTED) return true;
56
- if ((err as any).cause !== undefined && !nested) isAbortedError((err as any).cause, true);
56
+ if ((err as any).cause !== undefined && !nested) return isAbortedError((err as any).cause, true);
57
57
  return false;
58
58
  }
59
59
 
@@ -61,6 +61,9 @@ export class WebSocketBiDiStream<
61
61
  // Error tracking
62
62
  private lastError?: Error;
63
63
 
64
+ // Abort listener reference for cleanup
65
+ private readonly abortHandler?: () => void;
66
+
64
67
  // === Public API ===
65
68
 
66
69
  public readonly requests = {
@@ -122,7 +125,8 @@ export class WebSocketBiDiStream<
122
125
  return;
123
126
  }
124
127
 
125
- this.options.abortSignal?.addEventListener("abort", () => this.close());
128
+ this.abortHandler = () => this.close();
129
+ this.options.abortSignal?.addEventListener("abort", this.abortHandler, { once: true });
126
130
  this.connect();
127
131
  }
128
132
 
@@ -192,6 +196,11 @@ export class WebSocketBiDiStream<
192
196
  private onClose(): void {
193
197
  this.progressConnectionState(ConnectionState.CLOSED);
194
198
 
199
+ // Clean up abort listener to prevent memory leaks
200
+ if (this.abortHandler) {
201
+ this.options.abortSignal?.removeEventListener("abort", this.abortHandler);
202
+ }
203
+
195
204
  // If abort signal was triggered, use that as the error source
196
205
  if (this.options.abortSignal?.aborted && !this.lastError) {
197
206
  const reason = this.options.abortSignal.reason;