@akashnetwork/chain-sdk 1.0.0-alpha.36 → 1.0.0-alpha.37

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.
@@ -44,15 +44,17 @@ function runStreamingCall(options) {
44
44
  signal
45
45
  };
46
46
  let doneCalled = false;
47
- signal.addEventListener("abort", function() {
47
+ const onAbort = () => {
48
48
  const it = options.req.message[Symbol.asyncIterator]();
49
49
  if (!doneCalled) {
50
- it.throw?.(this.reason).catch(() => {
50
+ it.throw?.(signal.reason).catch(() => {
51
51
  });
52
52
  }
53
53
  it.return?.().catch(() => {
54
54
  });
55
- });
55
+ };
56
+ signal.addEventListener("abort", onAbort, { once: true });
57
+ const removeAbortListener = () => signal.removeEventListener("abort", onAbort);
56
58
  return next(req).then((res) => {
57
59
  return {
58
60
  ...res,
@@ -62,13 +64,25 @@ function runStreamingCall(options) {
62
64
  return {
63
65
  next() {
64
66
  return it.next().then((r) => {
65
- if (r.done == true) {
67
+ if (r.done === true) {
66
68
  doneCalled = true;
69
+ removeAbortListener();
67
70
  }
68
71
  return r;
69
72
  }, abort);
73
+ },
74
+ // If a consumer abandons the stream early (e.g. `break`s out of a
75
+ // `for await`), the runtime calls throw()/return() rather than draining
76
+ // to completion. Drop the abort listener on those paths too, and forward
77
+ // to the underlying iterator so it can release its own resources.
78
+ throw(e) {
79
+ removeAbortListener();
80
+ return it.throw ? it.throw(e) : Promise.reject(e);
81
+ },
82
+ return(value) {
83
+ removeAbortListener();
84
+ return it.return ? it.return(value) : Promise.resolve({ done: true, value });
70
85
  }
71
- // We deliberately omit throw/return.
72
86
  };
73
87
  }
74
88
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/sdk/transport/runCall.ts"],
4
- "sourcesContent": ["import type { Interceptor } from \"@connectrpc/connect\";\nimport { getAbortSignalReason } from \"@connectrpc/connect/protocol\";\n\nimport type { DeepPartial } from \"../../encoding/typeEncodingHelpers.ts\";\nimport { mapStream } from \"../client/stream.ts\";\nimport type { MessageDesc, MessageInitShape, MessageShape } from \"../client/types.ts\";\nimport { TransportError } from \"./TransportError.ts\";\nimport type { StreamRequest, StreamResponse, UnaryRequest, UnaryResponse } from \"./types.ts\";\n\n/**\n * UnaryFn represents the client-side invocation of a unary RPC - a method\n * that takes a single input message, and responds with a single output\n * message.\n * A Transport implements such a function, and makes it available to\n * interceptors.\n */\ntype UnaryFn<\n I extends MessageDesc,\n O extends MessageDesc,\n> = (req: UnaryRequest<I, O>) => Promise<UnaryResponse<I, O>>;\n\n/**\n * Runs a unary method with the given interceptors. Note that this function\n * is only used when implementing a Transport.\n */\nexport function runUnaryCall<\n I extends MessageDesc,\n O extends MessageDesc,\n>(options: {\n req: Omit<UnaryRequest<I, O>, \"signal\" | \"message\"> & {\n message: MessageInitShape<I>;\n };\n next: UnaryFn<I, O>;\n timeoutMs?: number;\n signal?: AbortSignal;\n interceptors?: Interceptor[];\n}): Promise<UnaryResponse<I, O>> {\n const next = composeInterceptors(options.next, options.interceptors);\n const { signal, abort } = createAbortSignal(options);\n\n const req = {\n ...options.req,\n message: options.req.method.input.fromPartial(options.req.message as DeepPartial<I>) as MessageShape<I>,\n signal,\n };\n return next(req).catch(abort);\n}\n\n/**\n * StreamingFn represents the client-side invocation of a streaming RPC - a\n * method that takes zero or more input messages, and responds with zero or\n * more output messages.\n * A Transport implements such a function, and makes it available to\n * interceptors.\n */\ntype StreamingFn<\n I extends MessageDesc = MessageDesc,\n O extends MessageDesc = MessageDesc,\n> = (req: StreamRequest<I, O>) => Promise<StreamResponse<I, O>>;\n\n/**\n * Runs a server-streaming method with the given interceptors. Note that this\n * function is only used when implementing a Transport.\n */\nexport function runStreamingCall<\n I extends MessageDesc,\n O extends MessageDesc,\n>(options: {\n req: Omit<StreamRequest<I, O>, \"signal\" | \"message\"> & {\n message: AsyncIterable<MessageInitShape<I>>;\n };\n next: StreamingFn<I, O>;\n timeoutMs?: number;\n signal?: AbortSignal;\n interceptors?: Interceptor[];\n}): Promise<StreamResponse<I, O>> {\n const next = composeInterceptors(options.next, options.interceptors);\n const { signal, abort } = createAbortSignal(options);\n const req = {\n ...options.req,\n message: mapStream(options.req.message, (message) => options.req.method.input.fromPartial(message as DeepPartial<I>) as MessageShape<I>),\n signal,\n };\n let doneCalled = false;\n // Call return on the request iterable to indicate\n // that we will no longer consume it and it should\n // cleanup any allocated resources.\n signal.addEventListener(\"abort\", function () {\n const it = options.req.message[Symbol.asyncIterator]();\n // If the signal is aborted due to an error, we want to throw\n // the error to the request iterator.\n if (!doneCalled) {\n it.throw?.(this.reason).catch(() => {\n // throw returns a promise, which we don't care about.\n //\n // Uncaught promises are thrown at sometime/somewhere by the event loop,\n // this is to ensure error is caught and ignored.\n });\n }\n it.return?.().catch(() => {\n // return returns a promise, which we don't care about.\n //\n // Uncaught promises are thrown at sometime/somewhere by the event loop,\n // this is to ensure error is caught and ignored.\n });\n });\n return next(req).then((res) => {\n return {\n ...res,\n message: {\n [Symbol.asyncIterator]() {\n const it = res.message[Symbol.asyncIterator]();\n return {\n next() {\n return it.next().then((r) => {\n if (r.done == true) {\n doneCalled = true;\n }\n return r;\n }, abort);\n },\n // We deliberately omit throw/return.\n };\n },\n },\n };\n }, abort);\n}\n\nfunction createAbortSignal(options: {\n timeoutMs?: number;\n signal?: AbortSignal;\n}) {\n const controller = new AbortController();\n const signals: AbortSignal[] = [controller.signal];\n let timeoutSignal: AbortSignal | undefined;\n if (options.timeoutMs !== undefined) {\n timeoutSignal = AbortSignal.timeout(options.timeoutMs);\n signals.push(timeoutSignal);\n }\n if (options.signal !== undefined) {\n signals.push(options.signal);\n }\n const signal = AbortSignal.any(signals);\n return {\n signal,\n abort(reason: unknown): Promise<never> {\n // We peek at the deadline signal because fetch() will throw an error on\n // abort that discards the signal reason.\n const error = timeoutSignal?.aborted\n ? TransportError.from(getAbortSignalReason(timeoutSignal), TransportError.Code.DeadlineExceeded)\n : TransportError.from(reason);\n controller.abort(error);\n return Promise.reject(error);\n },\n };\n}\n\nfunction composeInterceptors<T>(\n next: T,\n interceptors: Interceptor[] | undefined,\n): T {\n if (!interceptors) return next;\n\n let i = interceptors.length;\n while (i--) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n next = interceptors[i](next as (() => any)) as T;\n }\n return next;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,sBAAqC;AAGrC,oBAA0B;AAE1B,4BAA+B;AAmBxB,SAAS,aAGd,SAQ+B;AAC/B,QAAM,OAAO,oBAAoB,QAAQ,MAAM,QAAQ,YAAY;AACnE,QAAM,EAAE,QAAQ,MAAM,IAAI,kBAAkB,OAAO;AAEnD,QAAM,MAAM;AAAA,IACV,GAAG,QAAQ;AAAA,IACX,SAAS,QAAQ,IAAI,OAAO,MAAM,YAAY,QAAQ,IAAI,OAAyB;AAAA,IACnF;AAAA,EACF;AACA,SAAO,KAAK,GAAG,EAAE,MAAM,KAAK;AAC9B;AAkBO,SAAS,iBAGd,SAQgC;AAChC,QAAM,OAAO,oBAAoB,QAAQ,MAAM,QAAQ,YAAY;AACnE,QAAM,EAAE,QAAQ,MAAM,IAAI,kBAAkB,OAAO;AACnD,QAAM,MAAM;AAAA,IACV,GAAG,QAAQ;AAAA,IACX,aAAS,yBAAU,QAAQ,IAAI,SAAS,CAAC,YAAY,QAAQ,IAAI,OAAO,MAAM,YAAY,OAAyB,CAAoB;AAAA,IACvI;AAAA,EACF;AACA,MAAI,aAAa;AAIjB,SAAO,iBAAiB,SAAS,WAAY;AAC3C,UAAM,KAAK,QAAQ,IAAI,QAAQ,OAAO,aAAa,EAAE;AAGrD,QAAI,CAAC,YAAY;AACf,SAAG,QAAQ,KAAK,MAAM,EAAE,MAAM,MAAM;AAAA,MAKpC,CAAC;AAAA,IACH;AACA,OAAG,SAAS,EAAE,MAAM,MAAM;AAAA,IAK1B,CAAC;AAAA,EACH,CAAC;AACD,SAAO,KAAK,GAAG,EAAE,KAAK,CAAC,QAAQ;AAC7B,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS;AAAA,QACP,CAAC,OAAO,aAAa,IAAI;AACvB,gBAAM,KAAK,IAAI,QAAQ,OAAO,aAAa,EAAE;AAC7C,iBAAO;AAAA,YACL,OAAO;AACL,qBAAO,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM;AAC3B,oBAAI,EAAE,QAAQ,MAAM;AAClB,+BAAa;AAAA,gBACf;AACA,uBAAO;AAAA,cACT,GAAG,KAAK;AAAA,YACV;AAAA;AAAA,UAEF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,KAAK;AACV;AAEA,SAAS,kBAAkB,SAGxB;AACD,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAyB,CAAC,WAAW,MAAM;AACjD,MAAI;AACJ,MAAI,QAAQ,cAAc,QAAW;AACnC,oBAAgB,YAAY,QAAQ,QAAQ,SAAS;AACrD,YAAQ,KAAK,aAAa;AAAA,EAC5B;AACA,MAAI,QAAQ,WAAW,QAAW;AAChC,YAAQ,KAAK,QAAQ,MAAM;AAAA,EAC7B;AACA,QAAM,SAAS,YAAY,IAAI,OAAO;AACtC,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAiC;AAGrC,YAAM,QAAQ,eAAe,UACzB,qCAAe,SAAK,sCAAqB,aAAa,GAAG,qCAAe,KAAK,gBAAgB,IAC7F,qCAAe,KAAK,MAAM;AAC9B,iBAAW,MAAM,KAAK;AACtB,aAAO,QAAQ,OAAO,KAAK;AAAA,IAC7B;AAAA,EACF;AACF;AAEA,SAAS,oBACP,MACA,cACG;AACH,MAAI,CAAC,aAAc,QAAO;AAE1B,MAAI,IAAI,aAAa;AACrB,SAAO,KAAK;AAEV,WAAO,aAAa,CAAC,EAAE,IAAmB;AAAA,EAC5C;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { Interceptor } from \"@connectrpc/connect\";\nimport { getAbortSignalReason } from \"@connectrpc/connect/protocol\";\n\nimport type { DeepPartial } from \"../../encoding/typeEncodingHelpers.ts\";\nimport { mapStream } from \"../client/stream.ts\";\nimport type { MessageDesc, MessageInitShape, MessageShape } from \"../client/types.ts\";\nimport { TransportError } from \"./TransportError.ts\";\nimport type { StreamRequest, StreamResponse, UnaryRequest, UnaryResponse } from \"./types.ts\";\n\n/**\n * UnaryFn represents the client-side invocation of a unary RPC - a method\n * that takes a single input message, and responds with a single output\n * message.\n * A Transport implements such a function, and makes it available to\n * interceptors.\n */\ntype UnaryFn<\n I extends MessageDesc,\n O extends MessageDesc,\n> = (req: UnaryRequest<I, O>) => Promise<UnaryResponse<I, O>>;\n\n/**\n * Runs a unary method with the given interceptors. Note that this function\n * is only used when implementing a Transport.\n */\nexport function runUnaryCall<\n I extends MessageDesc,\n O extends MessageDesc,\n>(options: {\n req: Omit<UnaryRequest<I, O>, \"signal\" | \"message\"> & {\n message: MessageInitShape<I>;\n };\n next: UnaryFn<I, O>;\n timeoutMs?: number;\n signal?: AbortSignal;\n interceptors?: Interceptor[];\n}): Promise<UnaryResponse<I, O>> {\n const next = composeInterceptors(options.next, options.interceptors);\n const { signal, abort } = createAbortSignal(options);\n\n const req = {\n ...options.req,\n message: options.req.method.input.fromPartial(options.req.message as DeepPartial<I>) as MessageShape<I>,\n signal,\n };\n return next(req).catch(abort);\n}\n\n/**\n * StreamingFn represents the client-side invocation of a streaming RPC - a\n * method that takes zero or more input messages, and responds with zero or\n * more output messages.\n * A Transport implements such a function, and makes it available to\n * interceptors.\n */\ntype StreamingFn<\n I extends MessageDesc = MessageDesc,\n O extends MessageDesc = MessageDesc,\n> = (req: StreamRequest<I, O>) => Promise<StreamResponse<I, O>>;\n\n/**\n * Runs a server-streaming method with the given interceptors. Note that this\n * function is only used when implementing a Transport.\n */\nexport function runStreamingCall<\n I extends MessageDesc,\n O extends MessageDesc,\n>(options: {\n req: Omit<StreamRequest<I, O>, \"signal\" | \"message\"> & {\n message: AsyncIterable<MessageInitShape<I>>;\n };\n next: StreamingFn<I, O>;\n timeoutMs?: number;\n signal?: AbortSignal;\n interceptors?: Interceptor[];\n}): Promise<StreamResponse<I, O>> {\n const next = composeInterceptors(options.next, options.interceptors);\n const { signal, abort } = createAbortSignal(options);\n const req = {\n ...options.req,\n message: mapStream(options.req.message, (message) => options.req.method.input.fromPartial(message as DeepPartial<I>) as MessageShape<I>),\n signal,\n };\n let doneCalled = false;\n // Call return on the request iterable to indicate\n // that we will no longer consume it and it should\n // cleanup any allocated resources.\n const onAbort = () => {\n const it = options.req.message[Symbol.asyncIterator]();\n // If the signal is aborted due to an error, we want to throw\n // the error to the request iterator.\n if (!doneCalled) {\n it.throw?.(signal.reason).catch(() => {\n // throw returns a promise, which we don't care about.\n //\n // Uncaught promises are thrown at sometime/somewhere by the event loop,\n // this is to ensure error is caught and ignored.\n });\n }\n it.return?.().catch(() => {\n // return returns a promise, which we don't care about.\n //\n // Uncaught promises are thrown at sometime/somewhere by the event loop,\n // this is to ensure error is caught and ignored.\n });\n };\n // `signal` is a composite AbortSignal produced by AbortSignal.any(). Node keeps\n // such signals reachable (via its internal `gcPersistentSignals` set) for as long\n // as they have an \"abort\" listener, so they can still fire. Registering with\n // `{ once: true }` lets Node drop this listener automatically once the signal\n // aborts; on every non-aborting terminal path we remove it explicitly via\n // `removeAbortListener()`. Without this, the listener closure keeps the entire\n // request graph reachable forever - for every stream that ends without aborting -\n // which leaks memory unboundedly on long-running processes.\n signal.addEventListener(\"abort\", onAbort, { once: true });\n const removeAbortListener = () => signal.removeEventListener(\"abort\", onAbort);\n\n return next(req).then((res) => {\n return {\n ...res,\n message: {\n [Symbol.asyncIterator]() {\n const it = res.message[Symbol.asyncIterator]();\n return {\n next() {\n return it.next().then((r) => {\n if (r.done === true) {\n doneCalled = true;\n // The stream completed normally, so the composite signal will\n // never abort and the abort listener would otherwise linger\n // forever. Remove it so the signal - and the request graph its\n // closure captures - becomes collectable.\n removeAbortListener();\n }\n return r;\n }, abort);\n },\n // If a consumer abandons the stream early (e.g. `break`s out of a\n // `for await`), the runtime calls throw()/return() rather than draining\n // to completion. Drop the abort listener on those paths too, and forward\n // to the underlying iterator so it can release its own resources.\n throw(e: unknown) {\n removeAbortListener();\n return it.throw ? it.throw(e) : Promise.reject(e);\n },\n return(value?: unknown) {\n removeAbortListener();\n return it.return ? it.return(value) : Promise.resolve({ done: true, value } as IteratorResult<MessageShape<O>>);\n },\n };\n },\n },\n };\n }, abort);\n}\n\nfunction createAbortSignal(options: {\n timeoutMs?: number;\n signal?: AbortSignal;\n}) {\n const controller = new AbortController();\n const signals: AbortSignal[] = [controller.signal];\n let timeoutSignal: AbortSignal | undefined;\n if (options.timeoutMs !== undefined) {\n timeoutSignal = AbortSignal.timeout(options.timeoutMs);\n signals.push(timeoutSignal);\n }\n if (options.signal !== undefined) {\n signals.push(options.signal);\n }\n const signal = AbortSignal.any(signals);\n return {\n signal,\n abort(reason: unknown): Promise<never> {\n // We peek at the deadline signal because fetch() will throw an error on\n // abort that discards the signal reason.\n const error = timeoutSignal?.aborted\n ? TransportError.from(getAbortSignalReason(timeoutSignal), TransportError.Code.DeadlineExceeded)\n : TransportError.from(reason);\n controller.abort(error);\n return Promise.reject(error);\n },\n };\n}\n\nfunction composeInterceptors<T>(\n next: T,\n interceptors: Interceptor[] | undefined,\n): T {\n if (!interceptors) return next;\n\n let i = interceptors.length;\n while (i--) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n next = interceptors[i](next as (() => any)) as T;\n }\n return next;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -9039,15 +9039,17 @@ function runStreamingCall(options) {
9039
9039
  signal
9040
9040
  };
9041
9041
  let doneCalled = false;
9042
- signal.addEventListener("abort", function() {
9042
+ const onAbort = () => {
9043
9043
  const it = options.req.message[Symbol.asyncIterator]();
9044
9044
  if (!doneCalled) {
9045
- it.throw?.(this.reason).catch(() => {
9045
+ it.throw?.(signal.reason).catch(() => {
9046
9046
  });
9047
9047
  }
9048
9048
  it.return?.().catch(() => {
9049
9049
  });
9050
- });
9050
+ };
9051
+ signal.addEventListener("abort", onAbort, { once: true });
9052
+ const removeAbortListener = () => signal.removeEventListener("abort", onAbort);
9051
9053
  return next(req).then((res) => {
9052
9054
  return {
9053
9055
  ...res,
@@ -9057,13 +9059,25 @@ function runStreamingCall(options) {
9057
9059
  return {
9058
9060
  next() {
9059
9061
  return it.next().then((r) => {
9060
- if (r.done == true) {
9062
+ if (r.done === true) {
9061
9063
  doneCalled = true;
9064
+ removeAbortListener();
9062
9065
  }
9063
9066
  return r;
9064
9067
  }, abort);
9068
+ },
9069
+ // If a consumer abandons the stream early (e.g. `break`s out of a
9070
+ // `for await`), the runtime calls throw()/return() rather than draining
9071
+ // to completion. Drop the abort listener on those paths too, and forward
9072
+ // to the underlying iterator so it can release its own resources.
9073
+ throw(e) {
9074
+ removeAbortListener();
9075
+ return it.throw ? it.throw(e) : Promise.reject(e);
9076
+ },
9077
+ return(value) {
9078
+ removeAbortListener();
9079
+ return it.return ? it.return(value) : Promise.resolve({ done: true, value });
9065
9080
  }
9066
- // We deliberately omit throw/return.
9067
9081
  };
9068
9082
  }
9069
9083
  }
@@ -9156,4 +9170,4 @@ export {
9156
9170
  isRetryEnabled,
9157
9171
  createTxTransport
9158
9172
  };
9159
- //# sourceMappingURL=chunk-P2FARPRM.js.map
9173
+ //# sourceMappingURL=chunk-R42YS74B.js.map