@hiero-ledger/sdk 2.78.0-beta.3 → 2.79.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/umd.js +45 -10
- package/dist/umd.min.js +3 -3
- package/lib/Executable.cjs +27 -0
- package/lib/Executable.js +1 -1
- package/lib/Executable.js.map +1 -1
- package/lib/channel/WebChannel.cjs +1 -1
- package/lib/channel/WebChannel.js +1 -1
- package/lib/channel/WebChannel.js.map +1 -1
- package/lib/client/Network.cjs +1 -4
- package/lib/client/Network.js +1 -1
- package/lib/client/Network.js.map +1 -1
- package/lib/client/addressbooks/mainnet.cjs +1 -1
- package/lib/client/addressbooks/mainnet.d.ts +1 -1
- package/lib/client/addressbooks/mainnet.js +1 -1
- package/lib/client/addressbooks/mainnet.js.map +1 -1
- package/lib/client/addressbooks/previewnet.cjs +1 -1
- package/lib/client/addressbooks/previewnet.d.ts +1 -1
- package/lib/client/addressbooks/previewnet.js +1 -1
- package/lib/client/addressbooks/previewnet.js.map +1 -1
- package/lib/client/addressbooks/testnet.cjs +1 -1
- package/lib/client/addressbooks/testnet.d.ts +1 -1
- package/lib/client/addressbooks/testnet.js +1 -1
- package/lib/client/addressbooks/testnet.js.map +1 -1
- package/lib/transaction/Transaction.cjs +1 -0
- package/lib/transaction/Transaction.js +1 -1
- package/lib/transaction/Transaction.js.map +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/Executable.js +37 -0
- package/src/channel/WebChannel.js +3 -1
- package/src/client/Network.js +1 -6
- package/src/client/addressbooks/mainnet.js +1 -1
- package/src/client/addressbooks/previewnet.js +1 -1
- package/src/client/addressbooks/testnet.js +1 -1
- package/src/transaction/Transaction.js +1 -0
package/lib/Executable.cjs
CHANGED
|
@@ -684,6 +684,33 @@ class Executable {
|
|
|
684
684
|
// Determine by the executing state what we should do
|
|
685
685
|
switch (shouldRetry) {
|
|
686
686
|
case ExecutionState.Retry:
|
|
687
|
+
// Special handling for INVALID_NODE_ACCOUNT: mark node as unusable
|
|
688
|
+
// and update network to get latest node account IDs
|
|
689
|
+
if (status === _Status.default.InvalidNodeAccount) {
|
|
690
|
+
if (this._logger) {
|
|
691
|
+
this._logger.debug(`[${this._getLogId()}] node with accountId: ${node.accountId.toString()} and proxy IP: ${node.address.toString()} has invalid node account ID, marking as unhealthy and updating network`);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Mark the node as unusable by increasing its backoff and removing it from the healthy nodes list
|
|
695
|
+
client._network.increaseBackoff(node);
|
|
696
|
+
|
|
697
|
+
// Initiate addressbook query and update the client's network
|
|
698
|
+
// This will make the SDK client have the latest node account IDs for subsequent transactions
|
|
699
|
+
try {
|
|
700
|
+
if (client.mirrorNetwork.length > 0) {
|
|
701
|
+
await client.updateNetwork();
|
|
702
|
+
} else {
|
|
703
|
+
if (this._logger) {
|
|
704
|
+
this._logger.warn("Cannot update address book: no mirror network configured. Retrying with existing network configuration.");
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (this._logger) {
|
|
709
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
710
|
+
this._logger.trace(`failed to update client address book after INVALID_NODE_ACCOUNT_ID: ${errorMessage}`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
687
714
|
await delayForAttempt(isLocalNode, attempt, this._minBackoff, this._maxBackoff);
|
|
688
715
|
continue;
|
|
689
716
|
case ExecutionState.Finished:
|
package/lib/Executable.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import t from"./grpc/GrpcServiceError.js";import e from"./grpc/GrpcStatus.js";import
|
|
1
|
+
import t from"./grpc/GrpcServiceError.js";import e from"./grpc/GrpcStatus.js";import o from"./transaction/List.js";import{encode as n}from"./encoding/hex.js";import r from"./http/HttpError.js";import i from"./Status.js";import s from"./MaxAttemptsOrTimeoutError.js";const a={Finished:"Finished",Retry:"Retry",Error:"Error"},c=/\brst[^0-9a-zA-Z]stream\b/i,h=10;class d{constructor(){this._maxAttempts=10,this._nodeAccountIds=new o,this.transactionNodeIds=[],this._signOnDemand=!1,this._minBackoff=null,this._maxBackoff=8e3,this._operator=null,this._requestTimeout=null,this._grpcDeadline=null,this._logger=null}get nodeAccountIds(){return this._nodeAccountIds.isEmpty?null:(this._nodeAccountIds.setLocked(),this._nodeAccountIds.list)}setNodeAccountIds(t){return this._nodeAccountIds.setList(t).setLocked(),this}get maxRetries(){return console.warn("Deprecated: use maxAttempts instead"),this.maxAttempts}setMaxRetries(t){return console.warn("Deprecated: use setMaxAttempts() instead"),this.setMaxAttempts(t)}get maxAttempts(){return this._maxAttempts}setMaxAttempts(t){return this._maxAttempts=t,this}get grpcDeadline(){return this._grpcDeadline}setGrpcDeadline(t){return this._grpcDeadline=t,this}setMinBackoff(t){if(null==t)throw new Error("minBackoff cannot be null.");if(null!=this._maxBackoff&&t>this._maxBackoff)throw new Error("minBackoff cannot be larger than maxBackoff.");return this._minBackoff=t,this}get minBackoff(){return this._minBackoff}setMaxBackoff(t){if(null==t)throw new Error("maxBackoff cannot be null.");if(null!=this._minBackoff&&t<this._minBackoff)throw new Error("maxBackoff cannot be smaller than minBackoff.");return this._maxBackoff=t,this}get maxBackoff(){return this._maxBackoff}_beforeExecute(t){throw new Error("not implemented")}_makeRequestAsync(){throw new Error("not implemented")}_mapStatusError(t,e,o){throw new Error("not implemented")}_mapResponse(t,e,o){throw new Error("not implemented")}_execute(t,e){throw new Error("not implemented")}_getTransactionId(){throw new Error("not implemented")}_getLogId(){throw new Error("not implemented")}_requestToBytes(t){throw new Error("not implemented")}_responseToBytes(t){throw new Error("not implemented")}_shouldRetry(t,e){throw new Error("not implemented")}_shouldRetryExceptionally(o){return!(o instanceof t)||(o.status._code===e.Timeout._code||o.status._code===e.DeadlineExceeded._code||o.status._code===e.Unavailable._code||o.status._code===e.ResourceExhausted._code||o.status._code===e.GrpcWeb._code||o.status._code===e.Internal._code&&c.test(o.message))}_setOperatorWith(t,e,o){return this._operator={transactionSigner:o,accountId:t,publicKey:e},this}async executeWithSigner(t){return t.call(this)}isBatchedAndNotBatchTransaction(){return!1}async execute(o,c){const h=null!=o.network["127.0.0.1:50211"];if(this.isBatchedAndNotBatchTransaction())throw new Error("Cannot execute batchified transaction outside of BatchTransaction");this._logger=null==this._logger?null!=o._logger?o._logger:null:this._logger,null==this._requestTimeout&&(this._requestTimeout=null!=c?c:o.requestTimeout),null==this._grpcDeadline&&(this._grpcDeadline=o.grpcDeadline),await this._beforeExecute(o),null==this._maxBackoff&&(this._maxBackoff=o.maxBackoff),null==this._minBackoff&&(this._minBackoff=o.minBackoff);const d=Date.now();let g=null;const _=h?1e3:o._maxAttempts??this._maxAttempts;if(this.transactionNodeIds.length){const t=this._nodeAccountIds.list.map(t=>t.toString());if(!this.transactionNodeIds.some(e=>t.includes(e))){const e=t.length>2?`${t.slice(0,2).join(", ")} ...`:t.join(", "),o=1===t.length;throw new Error(`Attempting to execute a transaction against node${o?"":"s"} ${e}, which ${o?"is":"are"} not included in the Client's node list. Please review your Client configuration.`)}}for(let c=1;c<=_;c+=1){if(null!=this._requestTimeout&&d+this._requestTimeout<=Date.now())throw new s("timeout exceeded",this._nodeAccountIds.isEmpty?"No node account ID set":this._nodeAccountIds.current.toString());let m,f;if(this._nodeAccountIds.isEmpty?(f=o._network.getNode(),m=f.accountId,this._nodeAccountIds.setList([m])):(m=this._nodeAccountIds.current,f=o._network.getNode(m)),null==f)throw new Error(`NodeAccountId not recognized: ${m.toString()}`);if(this.transactionNodeIds.length){if(!this.transactionNodeIds.includes(m.toString())){console.error(`Attempting to execute a transaction against node ${m.toString()}, which is not included in the Client's node list. Please review your Client configuration.`),this._nodeAccountIds.advance();continue}}const p=this._getLogId();this._logger&&this._logger.debug(`[${p}] Node AccountID: ${f.accountId.toString()}, IP: ${f.address.toString()}`);const w=f.getChannel();null!=this._grpcDeadline&&w.setGrpcDeadline(this._grpcDeadline);const x=await this._makeRequestAsync();let k;if(!f.isHealthy()){const t=this._nodeAccountIds.index===this._nodeAccountIds.list.length-1;if(u(x)||h){await l(h,c,this._minBackoff,this._maxBackoff);continue}if(t||this._nodeAccountIds.length<=1)throw new Error(`Network connectivity issue: All nodes are unhealthy. Original node list: ${this._nodeAccountIds.list.join(", ")}`);this._logger&&this._logger.debug(`[${p}] Node is not healthy, trying the next node.`),this._nodeAccountIds.advance();continue}this._nodeAccountIds.advance();try{const o=[];null!=this._grpcDeadline&&o.push(new Promise((o,n)=>setTimeout(()=>n(new t(e.DeadlineExceeded)),this._grpcDeadline))),this._logger&&this._logger.trace(`[${this._getLogId()}] sending protobuf ${n(this._requestToBytes(x))}`),o.push(this._execute(w,x)),k=await Promise.race(o)}catch(e){const n=t._fromResponse(e);if(g=n,this._logger&&this._logger.debug(`[${p}] received error ${JSON.stringify(n)}`),(n instanceof t||n instanceof r)&&this._shouldRetryExceptionally(n)&&c<=_){this._logger&&this._logger.debug(`[${this._getLogId()}] node with accountId: ${f.accountId.toString()} and proxy IP: ${f.address.toString()} is unhealthy`),o._network.increaseBackoff(f);continue}throw e}this._logger&&this._logger.trace(`[${this._getLogId()}] sending protobuf ${n(this._responseToBytes(k))}`),o._network.decreaseBackoff(f);const[I,A]=this._shouldRetry(x,k);switch(I.toString()!==i.Ok.toString()&&I.toString()!==i.Success.toString()&&(g=I),A){case a.Retry:if(I===i.InvalidNodeAccount){this._logger&&this._logger.debug(`[${this._getLogId()}] node with accountId: ${f.accountId.toString()} and proxy IP: ${f.address.toString()} has invalid node account ID, marking as unhealthy and updating network`),o._network.increaseBackoff(f);try{o.mirrorNetwork.length>0?await o.updateNetwork():this._logger&&this._logger.warn("Cannot update address book: no mirror network configured. Retrying with existing network configuration.")}catch(t){if(this._logger){const e=t instanceof Error?t.message:String(t);this._logger.trace(`failed to update client address book after INVALID_NODE_ACCOUNT_ID: ${e}`)}}}await l(h,c,this._minBackoff,this._maxBackoff);continue;case a.Finished:return this._mapResponse(k,m,x);case a.Error:throw this._mapStatusError(x,k,m);default:throw new Error("(BUG) non-exhaustive switch statement for `ExecutionState`")}}throw new s(`max attempts of ${_.toString()} was reached for request with last error being: ${null!=g?g.toString():""}`,this._nodeAccountIds.current.toString())}toBytes(){throw new Error("not implemented")}setLogger(t){return this._logger=t,this}get logger(){return this._logger}}function u(t){return"object"==typeof t&&null!==t&&("transactionGetReceipt"in t||"transactionGetRecord"in t)}function l(t,e,o,n){if(t)return new Promise(t=>setTimeout(t,o));const r=Math.min(Math.floor(o*Math.pow(2,e)),n);return new Promise(t=>setTimeout(t,r))}export{h as DEFAULT_MAX_ATTEMPTS,a as ExecutionState,c as RST_STREAM,d as default};
|
|
2
2
|
//# sourceMappingURL=Executable.js.map
|
package/lib/Executable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Executable.js","sources":["../src/Executable.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nimport GrpcServiceError from \"./grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"./grpc/GrpcStatus.js\";\nimport List from \"./transaction/List.js\";\nimport * as hex from \"./encoding/hex.js\";\nimport HttpError from \"./http/HttpError.js\";\nimport Status from \"./Status.js\";\nimport MaxAttemptsOrTimeoutError from \"./MaxAttemptsOrTimeoutError.js\";\n\n/**\n * @typedef {import(\"./account/AccountId.js\").default} AccountId\n * @typedef {import(\"./channel/Channel.js\").default} Channel\n * @typedef {import(\"./channel/MirrorChannel.js\").default} MirrorChannel\n * @typedef {import(\"./transaction/TransactionId.js\").default} TransactionId\n * @typedef {import(\"./client/Client.js\").ClientOperator} ClientOperator\n * @typedef {import(\"./Signer.js\").Signer} Signer\n * @typedef {import(\"./PublicKey.js\").default} PublicKey\n * @typedef {import(\"./logger/Logger.js\").default} Logger\n */\n\n/**\n * @enum {string}\n */\nexport const ExecutionState = {\n Finished: \"Finished\",\n Retry: \"Retry\",\n Error: \"Error\",\n};\n\nexport const RST_STREAM = /\\brst[^0-9a-zA-Z]stream\\b/i;\nexport const DEFAULT_MAX_ATTEMPTS = 10;\n\n/**\n * @abstract\n * @internal\n * @template RequestT\n * @template ResponseT\n * @template OutputT\n */\nexport default class Executable {\n constructor() {\n /**\n * The number of times we can retry the grpc call\n *\n * @internal\n * @type {number}\n */\n this._maxAttempts = DEFAULT_MAX_ATTEMPTS;\n\n /**\n * List of node account IDs for each transaction that has been\n * built.\n *\n * @internal\n * @type {List<AccountId>}\n */\n this._nodeAccountIds = new List();\n\n /**\n * List of the transaction node account IDs to check if\n * the node account ID of the request is in the list\n *\n * @protected\n * @type {Array<string>}\n */\n this.transactionNodeIds = [];\n\n /**\n * @internal\n */\n this._signOnDemand = false;\n\n /**\n * This is the request's min backoff\n *\n * @internal\n * @type {number | null}\n */\n this._minBackoff = null;\n\n /**\n * This is the request's max backoff\n *\n * @internal\n * @type {number}\n */\n this._maxBackoff = 8000;\n\n /**\n * The operator that was used to execute this request.\n * The reason we save the operator in the request is because of the signing on\n * demand feature. This feature requires us to sign new request on each attempt\n * meaning if a client with an operator was used we'd need to sign with the operator\n * on each attempt.\n *\n * @internal\n * @type {ClientOperator | null}\n */\n this._operator = null;\n\n /**\n * The complete timeout for running the `execute()` method\n *\n * @internal\n * @type {number | null}\n */\n this._requestTimeout = null;\n\n /**\n * The grpc request timeout aka deadline.\n *\n * The reason we have this is because there were times that consensus nodes held the grpc\n * connection, but didn't return anything; not error nor regular response. This resulted\n * in some weird behavior in the SDKs. To fix this we've added a grpc deadline to prevent\n * nodes from stalling the executing of a request.\n *\n * @internal\n * @type {number | null}\n */\n this._grpcDeadline = null;\n\n /**\n * Logger\n *\n * @protected\n * @type {Logger | null}\n */\n this._logger = null;\n }\n\n /**\n * Get the list of node account IDs on the request. If no nodes are set, then null is returned.\n * The reasoning for this is simply \"legacy behavior\".\n *\n * @returns {?AccountId[]}\n */\n get nodeAccountIds() {\n if (this._nodeAccountIds.isEmpty) {\n return null;\n } else {\n this._nodeAccountIds.setLocked();\n return this._nodeAccountIds.list;\n }\n }\n\n /**\n * Set the node account IDs on the request\n *\n * @param {AccountId[]} nodeIds\n * @returns {this}\n */\n setNodeAccountIds(nodeIds) {\n // Set the node account IDs, and lock the list. This will require `execute`\n // to use these nodes instead of random nodes from the network.\n this._nodeAccountIds.setList(nodeIds).setLocked();\n return this;\n }\n\n /**\n * @deprecated\n * @returns {number}\n */\n get maxRetries() {\n console.warn(\"Deprecated: use maxAttempts instead\");\n return this.maxAttempts;\n }\n\n /**\n * @param {number} maxRetries\n * @returns {this}\n */\n setMaxRetries(maxRetries) {\n console.warn(\"Deprecated: use setMaxAttempts() instead\");\n return this.setMaxAttempts(maxRetries);\n }\n\n /**\n * Get the max attempts on the request\n *\n * @returns {number}\n */\n get maxAttempts() {\n return this._maxAttempts;\n }\n\n /**\n * Set the max attempts on the request\n *\n * @param {number} maxAttempts\n * @returns {this}\n */\n setMaxAttempts(maxAttempts) {\n this._maxAttempts = maxAttempts;\n\n return this;\n }\n\n /**\n * Get the grpc deadline\n *\n * @returns {?number}\n */\n get grpcDeadline() {\n return this._grpcDeadline;\n }\n\n /**\n * Set the grpc deadline\n *\n * @param {number} grpcDeadline\n * @returns {this}\n */\n setGrpcDeadline(grpcDeadline) {\n this._grpcDeadline = grpcDeadline;\n\n return this;\n }\n\n /**\n * Set the min backoff for the request\n *\n * @param {number} minBackoff\n * @returns {this}\n */\n setMinBackoff(minBackoff) {\n // Honestly we shouldn't be checking for null since that should be TypeScript's job.\n // Also verify that min backoff is not greater than max backoff.\n if (minBackoff == null) {\n throw new Error(\"minBackoff cannot be null.\");\n } else if (this._maxBackoff != null && minBackoff > this._maxBackoff) {\n throw new Error(\"minBackoff cannot be larger than maxBackoff.\");\n }\n this._minBackoff = minBackoff;\n return this;\n }\n\n /**\n * Get the min backoff\n *\n * @returns {number | null}\n */\n get minBackoff() {\n return this._minBackoff;\n }\n\n /**\n * Set the max backoff for the request\n *\n * @param {?number} maxBackoff\n * @returns {this}\n */\n setMaxBackoff(maxBackoff) {\n // Honestly we shouldn't be checking for null since that should be TypeScript's job.\n // Also verify that max backoff is not less than min backoff.\n if (maxBackoff == null) {\n throw new Error(\"maxBackoff cannot be null.\");\n } else if (this._minBackoff != null && maxBackoff < this._minBackoff) {\n throw new Error(\"maxBackoff cannot be smaller than minBackoff.\");\n }\n this._maxBackoff = maxBackoff;\n return this;\n }\n\n /**\n * Get the max backoff\n *\n * @returns {number}\n */\n get maxBackoff() {\n return this._maxBackoff;\n }\n\n /**\n * This method is responsible for doing any work before the executing process begins.\n * For paid queries this will result in executing a cost query, for transactions this\n * will make sure we save the operator and sign any requests that need to be signed\n * in case signing on demand is disabled.\n *\n * @abstract\n * @protected\n * @param {import(\"./client/Client.js\").default<Channel, *>} client\n * @returns {Promise<void>}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _beforeExecute(client) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Create a protobuf request which will be passed into the `_execute()` method\n *\n * @abstract\n * @protected\n * @returns {Promise<RequestT>}\n */\n _makeRequestAsync() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * This name is a bit wrong now, but the purpose of this method is to map the\n * request and response into an error. This method will only be called when\n * `_shouldRetry` returned `ExecutionState.Error`\n *\n * @abstract\n * @internal\n * @param {RequestT} request\n * @param {ResponseT} response\n * @param {AccountId} nodeId\n * @returns {Error}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _mapStatusError(request, response, nodeId) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Map the request, response, and the node account ID used for this attempt into a response.\n * This method will only be called when `_shouldRetry` returned `ExecutionState.Finished`\n *\n * @abstract\n * @protected\n * @param {ResponseT} response\n * @param {AccountId} nodeAccountId\n * @param {RequestT} request\n * @returns {Promise<OutputT>}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _mapResponse(response, nodeAccountId, request) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Perform a single grpc call with the given request. Each request has it's own\n * required service so we just pass in channel, and it'$ the request's responsiblity\n * to use the right service and call the right grpc method.\n *\n * @abstract\n * @internal\n * @param {Channel} channel\n * @param {RequestT} request\n * @returns {Promise<ResponseT>}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _execute(channel, request) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Return the current transaction ID for the request. All requests which are\n * use the same transaction ID for each node, but the catch is that `Transaction`\n * implicitly supports chunked transactions. Meaning there could be multiple\n * transaction IDs stored in the request, and a different transaction ID will be used\n * on subsequent calls to `execute()`\n *\n * FIXME: This method can most likely be removed, although some further inspection\n * is required.\n *\n * @abstract\n * @protected\n * @returns {TransactionId}\n */\n _getTransactionId() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Return the log ID for this particular request\n *\n * Log IDs are simply a string constructed to make it easy to track each request's\n * execution even when mulitple requests are executing in parallel. Typically, this\n * method returns the format of `[<request type>.<timestamp of the transaction ID>]`\n *\n * Maybe we should deduplicate this using ${this.consturtor.name}\n *\n * @abstract\n * @internal\n * @returns {string}\n */\n _getLogId() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Serialize the request into bytes\n *\n * @abstract\n * @param {RequestT} request\n * @returns {Uint8Array}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _requestToBytes(request) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Serialize the response into bytes\n *\n * @abstract\n * @param {ResponseT} response\n * @returns {Uint8Array}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _responseToBytes(response) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Determine if we should continue the execution process, error, or finish.\n *\n * FIXME: This method should really be called something else. Initially it returned\n * a boolean so `shouldRetry` made sense, but now it returns an enum, so the name\n * no longer makes sense.\n *\n * @abstract\n * @protected\n * @param {RequestT} request\n * @param {ResponseT} response\n * @returns {[Status, ExecutionState]}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _shouldRetry(request, response) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Determine if we should error based on the gRPC status\n *\n * Unlike `shouldRetry` this method does in fact still return a boolean\n *\n * @protected\n * @param {Error} error\n * @returns {boolean}\n */\n _shouldRetryExceptionally(error) {\n if (error instanceof GrpcServiceError) {\n return (\n error.status._code === GrpcStatus.Timeout._code ||\n error.status._code === GrpcStatus.DeadlineExceeded._code ||\n error.status._code === GrpcStatus.Unavailable._code ||\n error.status._code === GrpcStatus.ResourceExhausted._code ||\n error.status._code === GrpcStatus.GrpcWeb._code ||\n (error.status._code === GrpcStatus.Internal._code &&\n RST_STREAM.test(error.message))\n );\n } else {\n // if we get to the 'else' statement, the 'error' is instanceof 'HttpError'\n // and in this case, we have to retry always\n return true;\n }\n }\n\n /**\n * A helper method for setting the operator on the request\n *\n * @internal\n * @param {AccountId} accountId\n * @param {PublicKey} publicKey\n * @param {(message: Uint8Array) => Promise<Uint8Array>} transactionSigner\n * @returns {this}\n */\n _setOperatorWith(accountId, publicKey, transactionSigner) {\n this._operator = {\n transactionSigner,\n accountId,\n publicKey,\n };\n return this;\n }\n\n /**\n * Execute this request using the signer\n *\n * This method is part of the signature providers feature\n * https://hips.hedera.com/hip/hip-338\n *\n * @param {Signer} signer\n * @returns {Promise<OutputT>}\n */\n async executeWithSigner(signer) {\n return signer.call(this);\n }\n\n /**\n * @returns {boolean}\n * @abstract\n * @protected\n */\n isBatchedAndNotBatchTransaction() {\n return false;\n }\n\n /**\n * Execute the request using a client and an optional request timeout\n *\n * @template {Channel} ChannelT\n * @template {MirrorChannel} MirrorChannelT\n * @param {import(\"./client/Client.js\").default<ChannelT, MirrorChannelT>} client\n * @param {number=} requestTimeout\n * @returns {Promise<OutputT>}\n */\n async execute(client, requestTimeout) {\n // we check if its local node then backoff mechanism should be disabled\n // and we increase the retry attempts\n const isLocalNode = client.network[\"127.0.0.1:50211\"] != null;\n\n if (this.isBatchedAndNotBatchTransaction()) {\n throw new Error(\n \"Cannot execute batchified transaction outside of BatchTransaction\",\n );\n }\n\n // If the logger on the request is not set, use the logger in client\n // (if set, otherwise do not use logger)\n this._logger =\n this._logger == null\n ? client._logger != null\n ? client._logger\n : null\n : this._logger;\n\n // If the request timeout is set on the request we'll prioritize that instead\n // of the parameter provided, and if the parameter isn't provided we'll\n // use the default request timeout on client\n if (this._requestTimeout == null) {\n this._requestTimeout =\n requestTimeout != null ? requestTimeout : client.requestTimeout;\n }\n\n // If the grpc deadline is not set on the request, use the default value from client\n if (this._grpcDeadline == null) {\n this._grpcDeadline = client.grpcDeadline;\n }\n\n // Some request need to perform additional requests before the executing\n // such as paid queries need to fetch the cost of the query before\n // finally executing the actual query.\n await this._beforeExecute(client);\n\n // If the max backoff on the request is not set, use the default value in client\n if (this._maxBackoff == null) {\n this._maxBackoff = client.maxBackoff;\n }\n\n // If the min backoff on the request is not set, use the default value in client\n if (this._minBackoff == null) {\n this._minBackoff = client.minBackoff;\n }\n\n // Save the start time to be used later with request timeout\n const startTime = Date.now();\n\n // Saves each error we get so when we err due to max attempts exceeded we'll have\n // the last error that was returned by the consensus node\n let persistentError = null;\n\n // If the max attempts on the request is not set, use the default value in client\n // If the default value in client is not set, use a default of 10.\n //\n // FIXME: current implementation is wrong, update to follow comment above.\n // ... existing code ...\n const LOCAL_NODE_ATTEMPTS = 1000;\n const maxAttempts = isLocalNode\n ? LOCAL_NODE_ATTEMPTS\n : client._maxAttempts ?? this._maxAttempts;\n\n // Checks if has a valid nodes to which the TX can be sent\n if (this.transactionNodeIds.length) {\n const nodeAccountIds = this._nodeAccountIds.list.map((nodeId) =>\n nodeId.toString(),\n );\n\n const hasValidNodes = this.transactionNodeIds.some((nodeId) =>\n nodeAccountIds.includes(nodeId),\n );\n\n if (!hasValidNodes) {\n const displayNodeAccountIds =\n nodeAccountIds.length > 2\n ? `${nodeAccountIds.slice(0, 2).join(\", \")} ...`\n : nodeAccountIds.join(\", \");\n const isSingleNode = nodeAccountIds.length === 1;\n\n throw new Error(\n `Attempting to execute a transaction against node${\n isSingleNode ? \"\" : \"s\"\n } ${displayNodeAccountIds}, ` +\n `which ${\n isSingleNode ? \"is\" : \"are\"\n } not included in the Client's node list. Please review your Client configuration.`,\n );\n }\n }\n\n // The retry loop\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n // Determine if we've exceeded request timeout\n if (\n this._requestTimeout != null &&\n startTime + this._requestTimeout <= Date.now()\n ) {\n throw new MaxAttemptsOrTimeoutError(\n `timeout exceeded`,\n this._nodeAccountIds.isEmpty\n ? \"No node account ID set\"\n : this._nodeAccountIds.current.toString(),\n );\n }\n\n let nodeAccountId;\n let node;\n\n if (this._nodeAccountIds.isEmpty) {\n node = client._network.getNode();\n nodeAccountId = node.accountId;\n this._nodeAccountIds.setList([nodeAccountId]);\n } else {\n nodeAccountId = this._nodeAccountIds.current;\n node = client._network.getNode(nodeAccountId);\n }\n\n if (node == null) {\n throw new Error(\n `NodeAccountId not recognized: ${nodeAccountId.toString()}`,\n );\n }\n\n if (this.transactionNodeIds.length) {\n const isNodeAccountIdValid = this.transactionNodeIds.includes(\n nodeAccountId.toString(),\n );\n\n if (!isNodeAccountIdValid) {\n console.error(\n `Attempting to execute a transaction against node ${nodeAccountId.toString()}, which is not included in the Client's node list. Please review your Client configuration.`,\n );\n\n this._nodeAccountIds.advance();\n continue;\n }\n }\n\n // Get the log ID for the request.\n const logId = this._getLogId();\n if (this._logger) {\n this._logger.debug(\n `[${logId}] Node AccountID: ${node.accountId.toString()}, IP: ${node.address.toString()}`,\n );\n }\n\n const channel = node.getChannel();\n\n // Set the gRPC deadline on the channel if this query has a custom deadline\n if (this._grpcDeadline != null) {\n channel.setGrpcDeadline(this._grpcDeadline);\n }\n\n const request = await this._makeRequestAsync();\n\n let response;\n\n if (!node.isHealthy()) {\n const isLastNode =\n this._nodeAccountIds.index ===\n this._nodeAccountIds.list.length - 1;\n\n // Check if the request is a transaction receipt or record\n // request to retry 10 times, because getReceiptQuery/getRecordQuery\n // are single node requests\n if (\n isTransactionReceiptOrRecordRequest(request) ||\n isLocalNode\n ) {\n await delayForAttempt(\n isLocalNode,\n attempt,\n this._minBackoff,\n this._maxBackoff,\n );\n continue;\n }\n\n if (isLastNode || this._nodeAccountIds.length <= 1) {\n throw new Error(\n `Network connectivity issue: All nodes are unhealthy. Original node list: ${this._nodeAccountIds.list.join(\n \", \",\n )}`,\n );\n }\n\n if (this._logger) {\n this._logger.debug(\n `[${logId}] Node is not healthy, trying the next node.`,\n );\n }\n\n this._nodeAccountIds.advance();\n continue;\n }\n\n this._nodeAccountIds.advance();\n\n try {\n // Race the execution promise against the grpc timeout to prevent grpc connections\n // from blocking this request\n const promises = [];\n\n // If a grpc deadline is set, we should race it, otherwise the only thing in the\n // list of promises will be the execution promise.\n if (this._grpcDeadline != null) {\n promises.push(\n // eslint-disable-next-line ie11/no-loop-func\n new Promise((_, reject) =>\n setTimeout(\n // eslint-disable-next-line ie11/no-loop-func\n () =>\n reject(\n new GrpcServiceError(\n GrpcStatus.DeadlineExceeded,\n ),\n ),\n /** @type {number=} */ (this._grpcDeadline),\n ),\n ),\n );\n }\n if (this._logger) {\n this._logger.trace(\n `[${this._getLogId()}] sending protobuf ${hex.encode(\n this._requestToBytes(request),\n )}`,\n );\n }\n\n promises.push(this._execute(channel, request));\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n response = /** @type {ResponseT} */ (\n await Promise.race(promises)\n );\n } catch (err) {\n // If we received a grpc status error we need to determine if\n // we should retry on this error, or err from the request entirely.\n const error = GrpcServiceError._fromResponse(\n /** @type {Error} */ (err),\n );\n\n // Save the error in case we retry\n persistentError = error;\n if (this._logger) {\n this._logger.debug(\n `[${logId}] received error ${JSON.stringify(error)}`,\n );\n }\n\n if (\n (error instanceof GrpcServiceError ||\n error instanceof HttpError) &&\n this._shouldRetryExceptionally(error) &&\n attempt <= maxAttempts\n ) {\n // Increase the backoff for the particular node and remove it from\n // the healthy node list\n if (this._logger) {\n this._logger.debug(\n `[${this._getLogId()}] node with accountId: ${node.accountId.toString()} and proxy IP: ${node.address.toString()} is unhealthy`,\n );\n }\n\n client._network.increaseBackoff(node);\n continue;\n }\n\n throw err;\n }\n if (this._logger) {\n this._logger.trace(\n `[${this._getLogId()}] sending protobuf ${hex.encode(\n this._responseToBytes(response),\n )}`,\n );\n }\n\n // If we didn't receive an error we should decrease the current nodes backoff\n // in case it is a recovering node\n client._network.decreaseBackoff(node);\n\n // Determine what execution state we're in by the response\n // For transactions this would be as simple as checking the response status is `OK`\n // while for _most_ queries it would check if the response status is `SUCCESS`\n // The only odd balls are `TransactionReceiptQuery` and `TransactionRecordQuery`\n const [status, shouldRetry] = this._shouldRetry(request, response);\n if (\n status.toString() !== Status.Ok.toString() &&\n status.toString() !== Status.Success.toString()\n ) {\n persistentError = status;\n }\n\n // Determine by the executing state what we should do\n switch (shouldRetry) {\n case ExecutionState.Retry:\n await delayForAttempt(\n isLocalNode,\n attempt,\n this._minBackoff,\n this._maxBackoff,\n );\n continue;\n case ExecutionState.Finished:\n return this._mapResponse(response, nodeAccountId, request);\n case ExecutionState.Error:\n throw this._mapStatusError(\n request,\n response,\n nodeAccountId,\n );\n default:\n throw new Error(\n \"(BUG) non-exhaustive switch statement for `ExecutionState`\",\n );\n }\n }\n\n // We'll only get here if we've run out of attempts, so we return an error wrapping the\n // persistent error we saved before.\n\n throw new MaxAttemptsOrTimeoutError(\n `max attempts of ${maxAttempts.toString()} was reached for request with last error being: ${\n persistentError != null ? persistentError.toString() : \"\"\n }`,\n this._nodeAccountIds.current.toString(),\n );\n }\n\n /**\n * The current purpose of this method is to easily support signature providers since\n * signature providers need to serialize _any_ request into bytes. `Query` and `Transaction`\n * already implement `toBytes()` so it only made sense to make it available here too.\n *\n * @abstract\n * @returns {Uint8Array}\n */\n toBytes() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Set logger\n *\n * @param {Logger} logger\n * @returns {this}\n */\n setLogger(logger) {\n this._logger = logger;\n return this;\n }\n\n /**\n * Get logger if set\n *\n * @returns {?Logger}\n */\n get logger() {\n return this._logger;\n }\n}\n\n/**\n * Checks if the request is a transaction receipt or record request\n *\n * @template T\n * @param {T} request - The request to check\n * @returns {boolean} - True if the request is a transaction receipt or record\n */\nfunction isTransactionReceiptOrRecordRequest(request) {\n if (typeof request !== \"object\" || request === null) {\n return false;\n }\n\n return (\n \"transactionGetReceipt\" in request || \"transactionGetRecord\" in request\n );\n}\n\n/**\n * A simple function that returns a promise timeout for a specific period of time\n *\n * @param {boolean} isLocalNode\n * @param {number} attempt\n * @param {number} minBackoff\n * @param {number} maxBackoff\n * @returns {Promise<void>}\n */\nfunction delayForAttempt(isLocalNode, attempt, minBackoff, maxBackoff) {\n if (isLocalNode) {\n return new Promise((resolve) => setTimeout(resolve, minBackoff));\n }\n\n // 0.1s, 0.2s, 0.4s, 0.8s, ...\n const ms = Math.min(\n Math.floor(minBackoff * Math.pow(2, attempt)),\n maxBackoff,\n );\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["ExecutionState","Finished","Retry","Error","RST_STREAM","DEFAULT_MAX_ATTEMPTS","Executable","constructor","this","_maxAttempts","_nodeAccountIds","List","transactionNodeIds","_signOnDemand","_minBackoff","_maxBackoff","_operator","_requestTimeout","_grpcDeadline","_logger","nodeAccountIds","isEmpty","setLocked","list","setNodeAccountIds","nodeIds","setList","maxRetries","console","warn","maxAttempts","setMaxRetries","setMaxAttempts","grpcDeadline","setGrpcDeadline","setMinBackoff","minBackoff","setMaxBackoff","maxBackoff","_beforeExecute","client","_makeRequestAsync","_mapStatusError","request","response","nodeId","_mapResponse","nodeAccountId","_execute","channel","_getTransactionId","_getLogId","_requestToBytes","_responseToBytes","_shouldRetry","_shouldRetryExceptionally","error","GrpcServiceError","status","_code","GrpcStatus","Timeout","DeadlineExceeded","Unavailable","ResourceExhausted","GrpcWeb","Internal","test","message","_setOperatorWith","accountId","publicKey","transactionSigner","executeWithSigner","signer","call","isBatchedAndNotBatchTransaction","execute","requestTimeout","isLocalNode","network","startTime","Date","now","persistentError","length","map","toString","some","includes","displayNodeAccountIds","slice","join","isSingleNode","attempt","MaxAttemptsOrTimeoutError","current","node","_network","getNode","advance","logId","debug","address","getChannel","isHealthy","isLastNode","index","isTransactionReceiptOrRecordRequest","delayForAttempt","promises","push","Promise","_","reject","setTimeout","trace","hex.encode","race","err","_fromResponse","JSON","stringify","HttpError","increaseBackoff","decreaseBackoff","shouldRetry","Status","Ok","Success","toBytes","setLogger","logger","resolve","ms","Math","min","floor","pow"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Executable.js","sources":["../src/Executable.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nimport GrpcServiceError from \"./grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"./grpc/GrpcStatus.js\";\nimport List from \"./transaction/List.js\";\nimport * as hex from \"./encoding/hex.js\";\nimport HttpError from \"./http/HttpError.js\";\nimport Status from \"./Status.js\";\nimport MaxAttemptsOrTimeoutError from \"./MaxAttemptsOrTimeoutError.js\";\n\n/**\n * @typedef {import(\"./account/AccountId.js\").default} AccountId\n * @typedef {import(\"./channel/Channel.js\").default} Channel\n * @typedef {import(\"./channel/MirrorChannel.js\").default} MirrorChannel\n * @typedef {import(\"./transaction/TransactionId.js\").default} TransactionId\n * @typedef {import(\"./client/Client.js\").ClientOperator} ClientOperator\n * @typedef {import(\"./Signer.js\").Signer} Signer\n * @typedef {import(\"./PublicKey.js\").default} PublicKey\n * @typedef {import(\"./logger/Logger.js\").default} Logger\n */\n\n/**\n * @enum {string}\n */\nexport const ExecutionState = {\n Finished: \"Finished\",\n Retry: \"Retry\",\n Error: \"Error\",\n};\n\nexport const RST_STREAM = /\\brst[^0-9a-zA-Z]stream\\b/i;\nexport const DEFAULT_MAX_ATTEMPTS = 10;\n\n/**\n * @abstract\n * @internal\n * @template RequestT\n * @template ResponseT\n * @template OutputT\n */\nexport default class Executable {\n constructor() {\n /**\n * The number of times we can retry the grpc call\n *\n * @internal\n * @type {number}\n */\n this._maxAttempts = DEFAULT_MAX_ATTEMPTS;\n\n /**\n * List of node account IDs for each transaction that has been\n * built.\n *\n * @internal\n * @type {List<AccountId>}\n */\n this._nodeAccountIds = new List();\n\n /**\n * List of the transaction node account IDs to check if\n * the node account ID of the request is in the list\n *\n * @protected\n * @type {Array<string>}\n */\n this.transactionNodeIds = [];\n\n /**\n * @internal\n */\n this._signOnDemand = false;\n\n /**\n * This is the request's min backoff\n *\n * @internal\n * @type {number | null}\n */\n this._minBackoff = null;\n\n /**\n * This is the request's max backoff\n *\n * @internal\n * @type {number}\n */\n this._maxBackoff = 8000;\n\n /**\n * The operator that was used to execute this request.\n * The reason we save the operator in the request is because of the signing on\n * demand feature. This feature requires us to sign new request on each attempt\n * meaning if a client with an operator was used we'd need to sign with the operator\n * on each attempt.\n *\n * @internal\n * @type {ClientOperator | null}\n */\n this._operator = null;\n\n /**\n * The complete timeout for running the `execute()` method\n *\n * @internal\n * @type {number | null}\n */\n this._requestTimeout = null;\n\n /**\n * The grpc request timeout aka deadline.\n *\n * The reason we have this is because there were times that consensus nodes held the grpc\n * connection, but didn't return anything; not error nor regular response. This resulted\n * in some weird behavior in the SDKs. To fix this we've added a grpc deadline to prevent\n * nodes from stalling the executing of a request.\n *\n * @internal\n * @type {number | null}\n */\n this._grpcDeadline = null;\n\n /**\n * Logger\n *\n * @protected\n * @type {Logger | null}\n */\n this._logger = null;\n }\n\n /**\n * Get the list of node account IDs on the request. If no nodes are set, then null is returned.\n * The reasoning for this is simply \"legacy behavior\".\n *\n * @returns {?AccountId[]}\n */\n get nodeAccountIds() {\n if (this._nodeAccountIds.isEmpty) {\n return null;\n } else {\n this._nodeAccountIds.setLocked();\n return this._nodeAccountIds.list;\n }\n }\n\n /**\n * Set the node account IDs on the request\n *\n * @param {AccountId[]} nodeIds\n * @returns {this}\n */\n setNodeAccountIds(nodeIds) {\n // Set the node account IDs, and lock the list. This will require `execute`\n // to use these nodes instead of random nodes from the network.\n this._nodeAccountIds.setList(nodeIds).setLocked();\n return this;\n }\n\n /**\n * @deprecated\n * @returns {number}\n */\n get maxRetries() {\n console.warn(\"Deprecated: use maxAttempts instead\");\n return this.maxAttempts;\n }\n\n /**\n * @param {number} maxRetries\n * @returns {this}\n */\n setMaxRetries(maxRetries) {\n console.warn(\"Deprecated: use setMaxAttempts() instead\");\n return this.setMaxAttempts(maxRetries);\n }\n\n /**\n * Get the max attempts on the request\n *\n * @returns {number}\n */\n get maxAttempts() {\n return this._maxAttempts;\n }\n\n /**\n * Set the max attempts on the request\n *\n * @param {number} maxAttempts\n * @returns {this}\n */\n setMaxAttempts(maxAttempts) {\n this._maxAttempts = maxAttempts;\n\n return this;\n }\n\n /**\n * Get the grpc deadline\n *\n * @returns {?number}\n */\n get grpcDeadline() {\n return this._grpcDeadline;\n }\n\n /**\n * Set the grpc deadline\n *\n * @param {number} grpcDeadline\n * @returns {this}\n */\n setGrpcDeadline(grpcDeadline) {\n this._grpcDeadline = grpcDeadline;\n\n return this;\n }\n\n /**\n * Set the min backoff for the request\n *\n * @param {number} minBackoff\n * @returns {this}\n */\n setMinBackoff(minBackoff) {\n // Honestly we shouldn't be checking for null since that should be TypeScript's job.\n // Also verify that min backoff is not greater than max backoff.\n if (minBackoff == null) {\n throw new Error(\"minBackoff cannot be null.\");\n } else if (this._maxBackoff != null && minBackoff > this._maxBackoff) {\n throw new Error(\"minBackoff cannot be larger than maxBackoff.\");\n }\n this._minBackoff = minBackoff;\n return this;\n }\n\n /**\n * Get the min backoff\n *\n * @returns {number | null}\n */\n get minBackoff() {\n return this._minBackoff;\n }\n\n /**\n * Set the max backoff for the request\n *\n * @param {?number} maxBackoff\n * @returns {this}\n */\n setMaxBackoff(maxBackoff) {\n // Honestly we shouldn't be checking for null since that should be TypeScript's job.\n // Also verify that max backoff is not less than min backoff.\n if (maxBackoff == null) {\n throw new Error(\"maxBackoff cannot be null.\");\n } else if (this._minBackoff != null && maxBackoff < this._minBackoff) {\n throw new Error(\"maxBackoff cannot be smaller than minBackoff.\");\n }\n this._maxBackoff = maxBackoff;\n return this;\n }\n\n /**\n * Get the max backoff\n *\n * @returns {number}\n */\n get maxBackoff() {\n return this._maxBackoff;\n }\n\n /**\n * This method is responsible for doing any work before the executing process begins.\n * For paid queries this will result in executing a cost query, for transactions this\n * will make sure we save the operator and sign any requests that need to be signed\n * in case signing on demand is disabled.\n *\n * @abstract\n * @protected\n * @param {import(\"./client/Client.js\").default<Channel, *>} client\n * @returns {Promise<void>}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _beforeExecute(client) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Create a protobuf request which will be passed into the `_execute()` method\n *\n * @abstract\n * @protected\n * @returns {Promise<RequestT>}\n */\n _makeRequestAsync() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * This name is a bit wrong now, but the purpose of this method is to map the\n * request and response into an error. This method will only be called when\n * `_shouldRetry` returned `ExecutionState.Error`\n *\n * @abstract\n * @internal\n * @param {RequestT} request\n * @param {ResponseT} response\n * @param {AccountId} nodeId\n * @returns {Error}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _mapStatusError(request, response, nodeId) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Map the request, response, and the node account ID used for this attempt into a response.\n * This method will only be called when `_shouldRetry` returned `ExecutionState.Finished`\n *\n * @abstract\n * @protected\n * @param {ResponseT} response\n * @param {AccountId} nodeAccountId\n * @param {RequestT} request\n * @returns {Promise<OutputT>}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _mapResponse(response, nodeAccountId, request) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Perform a single grpc call with the given request. Each request has it's own\n * required service so we just pass in channel, and it'$ the request's responsiblity\n * to use the right service and call the right grpc method.\n *\n * @abstract\n * @internal\n * @param {Channel} channel\n * @param {RequestT} request\n * @returns {Promise<ResponseT>}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _execute(channel, request) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Return the current transaction ID for the request. All requests which are\n * use the same transaction ID for each node, but the catch is that `Transaction`\n * implicitly supports chunked transactions. Meaning there could be multiple\n * transaction IDs stored in the request, and a different transaction ID will be used\n * on subsequent calls to `execute()`\n *\n * FIXME: This method can most likely be removed, although some further inspection\n * is required.\n *\n * @abstract\n * @protected\n * @returns {TransactionId}\n */\n _getTransactionId() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Return the log ID for this particular request\n *\n * Log IDs are simply a string constructed to make it easy to track each request's\n * execution even when mulitple requests are executing in parallel. Typically, this\n * method returns the format of `[<request type>.<timestamp of the transaction ID>]`\n *\n * Maybe we should deduplicate this using ${this.consturtor.name}\n *\n * @abstract\n * @internal\n * @returns {string}\n */\n _getLogId() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Serialize the request into bytes\n *\n * @abstract\n * @param {RequestT} request\n * @returns {Uint8Array}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _requestToBytes(request) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Serialize the response into bytes\n *\n * @abstract\n * @param {ResponseT} response\n * @returns {Uint8Array}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _responseToBytes(response) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Determine if we should continue the execution process, error, or finish.\n *\n * FIXME: This method should really be called something else. Initially it returned\n * a boolean so `shouldRetry` made sense, but now it returns an enum, so the name\n * no longer makes sense.\n *\n * @abstract\n * @protected\n * @param {RequestT} request\n * @param {ResponseT} response\n * @returns {[Status, ExecutionState]}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _shouldRetry(request, response) {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Determine if we should error based on the gRPC status\n *\n * Unlike `shouldRetry` this method does in fact still return a boolean\n *\n * @protected\n * @param {Error} error\n * @returns {boolean}\n */\n _shouldRetryExceptionally(error) {\n if (error instanceof GrpcServiceError) {\n return (\n error.status._code === GrpcStatus.Timeout._code ||\n error.status._code === GrpcStatus.DeadlineExceeded._code ||\n error.status._code === GrpcStatus.Unavailable._code ||\n error.status._code === GrpcStatus.ResourceExhausted._code ||\n error.status._code === GrpcStatus.GrpcWeb._code ||\n (error.status._code === GrpcStatus.Internal._code &&\n RST_STREAM.test(error.message))\n );\n } else {\n // if we get to the 'else' statement, the 'error' is instanceof 'HttpError'\n // and in this case, we have to retry always\n return true;\n }\n }\n\n /**\n * A helper method for setting the operator on the request\n *\n * @internal\n * @param {AccountId} accountId\n * @param {PublicKey} publicKey\n * @param {(message: Uint8Array) => Promise<Uint8Array>} transactionSigner\n * @returns {this}\n */\n _setOperatorWith(accountId, publicKey, transactionSigner) {\n this._operator = {\n transactionSigner,\n accountId,\n publicKey,\n };\n return this;\n }\n\n /**\n * Execute this request using the signer\n *\n * This method is part of the signature providers feature\n * https://hips.hedera.com/hip/hip-338\n *\n * @param {Signer} signer\n * @returns {Promise<OutputT>}\n */\n async executeWithSigner(signer) {\n return signer.call(this);\n }\n\n /**\n * @returns {boolean}\n * @abstract\n * @protected\n */\n isBatchedAndNotBatchTransaction() {\n return false;\n }\n\n /**\n * Execute the request using a client and an optional request timeout\n *\n * @template {Channel} ChannelT\n * @template {MirrorChannel} MirrorChannelT\n * @param {import(\"./client/Client.js\").default<ChannelT, MirrorChannelT>} client\n * @param {number=} requestTimeout\n * @returns {Promise<OutputT>}\n */\n async execute(client, requestTimeout) {\n // we check if its local node then backoff mechanism should be disabled\n // and we increase the retry attempts\n const isLocalNode = client.network[\"127.0.0.1:50211\"] != null;\n\n if (this.isBatchedAndNotBatchTransaction()) {\n throw new Error(\n \"Cannot execute batchified transaction outside of BatchTransaction\",\n );\n }\n\n // If the logger on the request is not set, use the logger in client\n // (if set, otherwise do not use logger)\n this._logger =\n this._logger == null\n ? client._logger != null\n ? client._logger\n : null\n : this._logger;\n\n // If the request timeout is set on the request we'll prioritize that instead\n // of the parameter provided, and if the parameter isn't provided we'll\n // use the default request timeout on client\n if (this._requestTimeout == null) {\n this._requestTimeout =\n requestTimeout != null ? requestTimeout : client.requestTimeout;\n }\n\n // If the grpc deadline is not set on the request, use the default value from client\n if (this._grpcDeadline == null) {\n this._grpcDeadline = client.grpcDeadline;\n }\n\n // Some request need to perform additional requests before the executing\n // such as paid queries need to fetch the cost of the query before\n // finally executing the actual query.\n await this._beforeExecute(client);\n\n // If the max backoff on the request is not set, use the default value in client\n if (this._maxBackoff == null) {\n this._maxBackoff = client.maxBackoff;\n }\n\n // If the min backoff on the request is not set, use the default value in client\n if (this._minBackoff == null) {\n this._minBackoff = client.minBackoff;\n }\n\n // Save the start time to be used later with request timeout\n const startTime = Date.now();\n\n // Saves each error we get so when we err due to max attempts exceeded we'll have\n // the last error that was returned by the consensus node\n let persistentError = null;\n\n // If the max attempts on the request is not set, use the default value in client\n // If the default value in client is not set, use a default of 10.\n //\n // FIXME: current implementation is wrong, update to follow comment above.\n // ... existing code ...\n const LOCAL_NODE_ATTEMPTS = 1000;\n const maxAttempts = isLocalNode\n ? LOCAL_NODE_ATTEMPTS\n : client._maxAttempts ?? this._maxAttempts;\n\n // Checks if has a valid nodes to which the TX can be sent\n if (this.transactionNodeIds.length) {\n const nodeAccountIds = this._nodeAccountIds.list.map((nodeId) =>\n nodeId.toString(),\n );\n\n const hasValidNodes = this.transactionNodeIds.some((nodeId) =>\n nodeAccountIds.includes(nodeId),\n );\n\n if (!hasValidNodes) {\n const displayNodeAccountIds =\n nodeAccountIds.length > 2\n ? `${nodeAccountIds.slice(0, 2).join(\", \")} ...`\n : nodeAccountIds.join(\", \");\n const isSingleNode = nodeAccountIds.length === 1;\n\n throw new Error(\n `Attempting to execute a transaction against node${\n isSingleNode ? \"\" : \"s\"\n } ${displayNodeAccountIds}, ` +\n `which ${\n isSingleNode ? \"is\" : \"are\"\n } not included in the Client's node list. Please review your Client configuration.`,\n );\n }\n }\n\n // The retry loop\n for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n // Determine if we've exceeded request timeout\n if (\n this._requestTimeout != null &&\n startTime + this._requestTimeout <= Date.now()\n ) {\n throw new MaxAttemptsOrTimeoutError(\n `timeout exceeded`,\n this._nodeAccountIds.isEmpty\n ? \"No node account ID set\"\n : this._nodeAccountIds.current.toString(),\n );\n }\n\n let nodeAccountId;\n let node;\n\n if (this._nodeAccountIds.isEmpty) {\n node = client._network.getNode();\n nodeAccountId = node.accountId;\n this._nodeAccountIds.setList([nodeAccountId]);\n } else {\n nodeAccountId = this._nodeAccountIds.current;\n node = client._network.getNode(nodeAccountId);\n }\n\n if (node == null) {\n throw new Error(\n `NodeAccountId not recognized: ${nodeAccountId.toString()}`,\n );\n }\n\n if (this.transactionNodeIds.length) {\n const isNodeAccountIdValid = this.transactionNodeIds.includes(\n nodeAccountId.toString(),\n );\n\n if (!isNodeAccountIdValid) {\n console.error(\n `Attempting to execute a transaction against node ${nodeAccountId.toString()}, which is not included in the Client's node list. Please review your Client configuration.`,\n );\n\n this._nodeAccountIds.advance();\n continue;\n }\n }\n\n // Get the log ID for the request.\n const logId = this._getLogId();\n if (this._logger) {\n this._logger.debug(\n `[${logId}] Node AccountID: ${node.accountId.toString()}, IP: ${node.address.toString()}`,\n );\n }\n\n const channel = node.getChannel();\n\n // Set the gRPC deadline on the channel if this query has a custom deadline\n if (this._grpcDeadline != null) {\n channel.setGrpcDeadline(this._grpcDeadline);\n }\n\n const request = await this._makeRequestAsync();\n\n let response;\n\n if (!node.isHealthy()) {\n const isLastNode =\n this._nodeAccountIds.index ===\n this._nodeAccountIds.list.length - 1;\n\n // Check if the request is a transaction receipt or record\n // request to retry 10 times, because getReceiptQuery/getRecordQuery\n // are single node requests\n if (\n isTransactionReceiptOrRecordRequest(request) ||\n isLocalNode\n ) {\n await delayForAttempt(\n isLocalNode,\n attempt,\n this._minBackoff,\n this._maxBackoff,\n );\n continue;\n }\n\n if (isLastNode || this._nodeAccountIds.length <= 1) {\n throw new Error(\n `Network connectivity issue: All nodes are unhealthy. Original node list: ${this._nodeAccountIds.list.join(\n \", \",\n )}`,\n );\n }\n\n if (this._logger) {\n this._logger.debug(\n `[${logId}] Node is not healthy, trying the next node.`,\n );\n }\n\n this._nodeAccountIds.advance();\n continue;\n }\n\n this._nodeAccountIds.advance();\n\n try {\n // Race the execution promise against the grpc timeout to prevent grpc connections\n // from blocking this request\n const promises = [];\n\n // If a grpc deadline is set, we should race it, otherwise the only thing in the\n // list of promises will be the execution promise.\n if (this._grpcDeadline != null) {\n promises.push(\n // eslint-disable-next-line ie11/no-loop-func\n new Promise((_, reject) =>\n setTimeout(\n // eslint-disable-next-line ie11/no-loop-func\n () =>\n reject(\n new GrpcServiceError(\n GrpcStatus.DeadlineExceeded,\n ),\n ),\n /** @type {number=} */ (this._grpcDeadline),\n ),\n ),\n );\n }\n if (this._logger) {\n this._logger.trace(\n `[${this._getLogId()}] sending protobuf ${hex.encode(\n this._requestToBytes(request),\n )}`,\n );\n }\n\n promises.push(this._execute(channel, request));\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n response = /** @type {ResponseT} */ (\n await Promise.race(promises)\n );\n } catch (err) {\n // If we received a grpc status error we need to determine if\n // we should retry on this error, or err from the request entirely.\n const error = GrpcServiceError._fromResponse(\n /** @type {Error} */ (err),\n );\n\n // Save the error in case we retry\n persistentError = error;\n if (this._logger) {\n this._logger.debug(\n `[${logId}] received error ${JSON.stringify(error)}`,\n );\n }\n\n if (\n (error instanceof GrpcServiceError ||\n error instanceof HttpError) &&\n this._shouldRetryExceptionally(error) &&\n attempt <= maxAttempts\n ) {\n // Increase the backoff for the particular node and remove it from\n // the healthy node list\n if (this._logger) {\n this._logger.debug(\n `[${this._getLogId()}] node with accountId: ${node.accountId.toString()} and proxy IP: ${node.address.toString()} is unhealthy`,\n );\n }\n\n client._network.increaseBackoff(node);\n continue;\n }\n\n throw err;\n }\n if (this._logger) {\n this._logger.trace(\n `[${this._getLogId()}] sending protobuf ${hex.encode(\n this._responseToBytes(response),\n )}`,\n );\n }\n\n // If we didn't receive an error we should decrease the current nodes backoff\n // in case it is a recovering node\n client._network.decreaseBackoff(node);\n\n // Determine what execution state we're in by the response\n // For transactions this would be as simple as checking the response status is `OK`\n // while for _most_ queries it would check if the response status is `SUCCESS`\n // The only odd balls are `TransactionReceiptQuery` and `TransactionRecordQuery`\n const [status, shouldRetry] = this._shouldRetry(request, response);\n if (\n status.toString() !== Status.Ok.toString() &&\n status.toString() !== Status.Success.toString()\n ) {\n persistentError = status;\n }\n\n // Determine by the executing state what we should do\n switch (shouldRetry) {\n case ExecutionState.Retry:\n // Special handling for INVALID_NODE_ACCOUNT: mark node as unusable\n // and update network to get latest node account IDs\n if (status === Status.InvalidNodeAccount) {\n if (this._logger) {\n this._logger.debug(\n `[${this._getLogId()}] node with accountId: ${node.accountId.toString()} and proxy IP: ${node.address.toString()} has invalid node account ID, marking as unhealthy and updating network`,\n );\n }\n\n // Mark the node as unusable by increasing its backoff and removing it from the healthy nodes list\n client._network.increaseBackoff(node);\n\n // Initiate addressbook query and update the client's network\n // This will make the SDK client have the latest node account IDs for subsequent transactions\n try {\n if (client.mirrorNetwork.length > 0) {\n await client.updateNetwork();\n } else {\n if (this._logger) {\n this._logger.warn(\n \"Cannot update address book: no mirror network configured. Retrying with existing network configuration.\",\n );\n }\n }\n } catch (error) {\n if (this._logger) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : String(error);\n this._logger.trace(\n `failed to update client address book after INVALID_NODE_ACCOUNT_ID: ${errorMessage}`,\n );\n }\n }\n }\n\n await delayForAttempt(\n isLocalNode,\n attempt,\n this._minBackoff,\n this._maxBackoff,\n );\n continue;\n case ExecutionState.Finished:\n return this._mapResponse(response, nodeAccountId, request);\n case ExecutionState.Error:\n throw this._mapStatusError(\n request,\n response,\n nodeAccountId,\n );\n default:\n throw new Error(\n \"(BUG) non-exhaustive switch statement for `ExecutionState`\",\n );\n }\n }\n\n // We'll only get here if we've run out of attempts, so we return an error wrapping the\n // persistent error we saved before.\n\n throw new MaxAttemptsOrTimeoutError(\n `max attempts of ${maxAttempts.toString()} was reached for request with last error being: ${\n persistentError != null ? persistentError.toString() : \"\"\n }`,\n this._nodeAccountIds.current.toString(),\n );\n }\n\n /**\n * The current purpose of this method is to easily support signature providers since\n * signature providers need to serialize _any_ request into bytes. `Query` and `Transaction`\n * already implement `toBytes()` so it only made sense to make it available here too.\n *\n * @abstract\n * @returns {Uint8Array}\n */\n toBytes() {\n throw new Error(\"not implemented\");\n }\n\n /**\n * Set logger\n *\n * @param {Logger} logger\n * @returns {this}\n */\n setLogger(logger) {\n this._logger = logger;\n return this;\n }\n\n /**\n * Get logger if set\n *\n * @returns {?Logger}\n */\n get logger() {\n return this._logger;\n }\n}\n\n/**\n * Checks if the request is a transaction receipt or record request\n *\n * @template T\n * @param {T} request - The request to check\n * @returns {boolean} - True if the request is a transaction receipt or record\n */\nfunction isTransactionReceiptOrRecordRequest(request) {\n if (typeof request !== \"object\" || request === null) {\n return false;\n }\n\n return (\n \"transactionGetReceipt\" in request || \"transactionGetRecord\" in request\n );\n}\n\n/**\n * A simple function that returns a promise timeout for a specific period of time\n *\n * @param {boolean} isLocalNode\n * @param {number} attempt\n * @param {number} minBackoff\n * @param {number} maxBackoff\n * @returns {Promise<void>}\n */\nfunction delayForAttempt(isLocalNode, attempt, minBackoff, maxBackoff) {\n if (isLocalNode) {\n return new Promise((resolve) => setTimeout(resolve, minBackoff));\n }\n\n // 0.1s, 0.2s, 0.4s, 0.8s, ...\n const ms = Math.min(\n Math.floor(minBackoff * Math.pow(2, attempt)),\n maxBackoff,\n );\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"names":["ExecutionState","Finished","Retry","Error","RST_STREAM","DEFAULT_MAX_ATTEMPTS","Executable","constructor","this","_maxAttempts","_nodeAccountIds","List","transactionNodeIds","_signOnDemand","_minBackoff","_maxBackoff","_operator","_requestTimeout","_grpcDeadline","_logger","nodeAccountIds","isEmpty","setLocked","list","setNodeAccountIds","nodeIds","setList","maxRetries","console","warn","maxAttempts","setMaxRetries","setMaxAttempts","grpcDeadline","setGrpcDeadline","setMinBackoff","minBackoff","setMaxBackoff","maxBackoff","_beforeExecute","client","_makeRequestAsync","_mapStatusError","request","response","nodeId","_mapResponse","nodeAccountId","_execute","channel","_getTransactionId","_getLogId","_requestToBytes","_responseToBytes","_shouldRetry","_shouldRetryExceptionally","error","GrpcServiceError","status","_code","GrpcStatus","Timeout","DeadlineExceeded","Unavailable","ResourceExhausted","GrpcWeb","Internal","test","message","_setOperatorWith","accountId","publicKey","transactionSigner","executeWithSigner","signer","call","isBatchedAndNotBatchTransaction","execute","requestTimeout","isLocalNode","network","startTime","Date","now","persistentError","length","map","toString","some","includes","displayNodeAccountIds","slice","join","isSingleNode","attempt","MaxAttemptsOrTimeoutError","current","node","_network","getNode","advance","logId","debug","address","getChannel","isHealthy","isLastNode","index","isTransactionReceiptOrRecordRequest","delayForAttempt","promises","push","Promise","_","reject","setTimeout","trace","hex.encode","race","err","_fromResponse","JSON","stringify","HttpError","increaseBackoff","decreaseBackoff","shouldRetry","Status","Ok","Success","InvalidNodeAccount","mirrorNetwork","updateNetwork","errorMessage","String","toBytes","setLogger","logger","resolve","ms","Math","min","floor","pow"],"mappings":"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"}
|
|
@@ -57,7 +57,7 @@ class WebChannel extends _Channel.default {
|
|
|
57
57
|
* @private
|
|
58
58
|
*/
|
|
59
59
|
_shouldUseHttps(address) {
|
|
60
|
-
return !(address.includes("localhost") || address.includes("127.0.0.1"));
|
|
60
|
+
return !(address.includes("localhost") || address.includes("127.0.0.1") || address.includes(".cluster.local"));
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ALL_WEB_NETWORK_NODES as t}from"../constants/ClientConstants.js";import e from"../grpc/GrpcServiceError.js";import s from"../grpc/GrpcStatus.js";import r from"../http/HttpError.js";import i from"../http/HttpStatus.js";import{SDK_NAME as a,SDK_VERSION as o}from"../version.js";import n,{encodeRequest as h,decodeUnaryResponse as l}from"./Channel.js";class c extends n{constructor(t,e){super(e),this._address=t,this._isReady=!1,this._healthCheckPromise=null}_shouldUseHttps(t){return!(t.includes("localhost")||t.includes("127.0.0.1"))}_buildUrl(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;return this._shouldUseHttps(t)?`https://${t}`:`http://${t}`}async _waitForReady(t){if(!this._isReady){if(this._healthCheckPromise)return this._healthCheckPromise;this._healthCheckPromise=this._performHealthCheck(t);try{await this._healthCheckPromise}finally{this._healthCheckPromise=null}}}async _performHealthCheck(r){const i=this._buildUrl(this._address),n=r.getTime()-Date.now();if(n<=0)throw new e(s.Timeout,t?.[this._address]?.toString());const h=new AbortController,l=setTimeout(()=>h.abort(),n);try{const r=await fetch(i,{method:"POST",headers:{"content-type":"application/grpc-web+proto","x-user-agent":`${a}/${o}`,"x-grpc-web":"1"},body:new Uint8Array(0),signal:h.signal});if(clearTimeout(l),r.ok||r.status>=300&&r.status<400){const t=r.headers.get("grpc-status"),e=r.headers.get("grpc-message");if(null!=t||null!=e)return void(this._isReady=!0)}throw new e(s.Unavailable,t?.[this._address]?.toString())}catch(r){if(clearTimeout(l),r instanceof Error&&"AbortError"===r.name)throw new e(s.Timeout,t?.[this._address]?.toString());if(r instanceof e)throw r;throw new e(s.Unavailable,t?.[this._address]?.toString())}}close(){}_createUnaryClient(n){return async(c,d,u)=>{const p=new Date,m=this._grpcDeadline;p.setMilliseconds(p.getMilliseconds()+m);try{await this._waitForReady(p);const m=this._buildUrl(this._address),f=await fetch(`${m}/proto.${n}/${c.name}`,{method:"POST",headers:{"content-type":"application/grpc-web+proto","x-user-agent":`${a}/${o}`,"x-grpc-web":"1"},body:h(d)});if(!f.ok){return void u(new r(i._fromValue(f.status)),null)}const _=f.headers.get("grpc-status"),g=f.headers.get("grpc-message");if(null!=_&&null!=g){const r=new e(s._fromValue(parseInt(_)),t?.[this._address]?.toString());return r.message=g,void u(r,null)}const w=await f.arrayBuffer();u(null,l(w))}catch(r){if(r instanceof e)return void u(r,null);u(new e(s._fromValue(18),t?.[this._address]?.toString()),null)}}}}export{c as default};
|
|
1
|
+
import{ALL_WEB_NETWORK_NODES as t}from"../constants/ClientConstants.js";import e from"../grpc/GrpcServiceError.js";import s from"../grpc/GrpcStatus.js";import r from"../http/HttpError.js";import i from"../http/HttpStatus.js";import{SDK_NAME as a,SDK_VERSION as o}from"../version.js";import n,{encodeRequest as h,decodeUnaryResponse as l}from"./Channel.js";class c extends n{constructor(t,e){super(e),this._address=t,this._isReady=!1,this._healthCheckPromise=null}_shouldUseHttps(t){return!(t.includes("localhost")||t.includes("127.0.0.1")||t.includes(".cluster.local"))}_buildUrl(t){if(t.startsWith("http://")||t.startsWith("https://"))return t;return this._shouldUseHttps(t)?`https://${t}`:`http://${t}`}async _waitForReady(t){if(!this._isReady){if(this._healthCheckPromise)return this._healthCheckPromise;this._healthCheckPromise=this._performHealthCheck(t);try{await this._healthCheckPromise}finally{this._healthCheckPromise=null}}}async _performHealthCheck(r){const i=this._buildUrl(this._address),n=r.getTime()-Date.now();if(n<=0)throw new e(s.Timeout,t?.[this._address]?.toString());const h=new AbortController,l=setTimeout(()=>h.abort(),n);try{const r=await fetch(i,{method:"POST",headers:{"content-type":"application/grpc-web+proto","x-user-agent":`${a}/${o}`,"x-grpc-web":"1"},body:new Uint8Array(0),signal:h.signal});if(clearTimeout(l),r.ok||r.status>=300&&r.status<400){const t=r.headers.get("grpc-status"),e=r.headers.get("grpc-message");if(null!=t||null!=e)return void(this._isReady=!0)}throw new e(s.Unavailable,t?.[this._address]?.toString())}catch(r){if(clearTimeout(l),r instanceof Error&&"AbortError"===r.name)throw new e(s.Timeout,t?.[this._address]?.toString());if(r instanceof e)throw r;throw new e(s.Unavailable,t?.[this._address]?.toString())}}close(){}_createUnaryClient(n){return async(c,d,u)=>{const p=new Date,m=this._grpcDeadline;p.setMilliseconds(p.getMilliseconds()+m);try{await this._waitForReady(p);const m=this._buildUrl(this._address),f=await fetch(`${m}/proto.${n}/${c.name}`,{method:"POST",headers:{"content-type":"application/grpc-web+proto","x-user-agent":`${a}/${o}`,"x-grpc-web":"1"},body:h(d)});if(!f.ok){return void u(new r(i._fromValue(f.status)),null)}const _=f.headers.get("grpc-status"),g=f.headers.get("grpc-message");if(null!=_&&null!=g){const r=new e(s._fromValue(parseInt(_)),t?.[this._address]?.toString());return r.message=g,void u(r,null)}const w=await f.arrayBuffer();u(null,l(w))}catch(r){if(r instanceof e)return void u(r,null);u(new e(s._fromValue(18),t?.[this._address]?.toString()),null)}}}}export{c as default};
|
|
2
2
|
//# sourceMappingURL=WebChannel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebChannel.js","sources":["../../src/channel/WebChannel.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { ALL_WEB_NETWORK_NODES } from \"../constants/ClientConstants.js\";\nimport GrpcServiceError from \"../grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"../grpc/GrpcStatus.js\";\nimport HttpError from \"../http/HttpError.js\";\nimport HttpStatus from \"../http/HttpStatus.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\nimport Channel, { encodeRequest, decodeUnaryResponse } from \"./Channel.js\";\n\nexport default class WebChannel extends Channel {\n /**\n * @param {string} address\n * @param {number=} grpcDeadline\n */\n constructor(address, grpcDeadline) {\n super(grpcDeadline);\n\n /**\n * @type {string}\n * @private\n */\n this._address = address;\n\n // Set the gRPC deadline using the base class method\n\n /**\n * Flag indicating if the connection is ready (health check has passed)\n * Set to true after the first successful health check\n *\n * @type {boolean}\n * @private\n */\n this._isReady = false;\n\n /**\n * Promise that resolves when the health check is complete\n * Used to prevent multiple concurrent health checks\n *\n * @type {Promise<void>|null}\n * @private\n */\n this._healthCheckPromise = null;\n }\n\n /**\n * Determines whether to use HTTPS based on the address\n * @param {string} address - The address to check\n * @returns {boolean} - True if HTTPS should be used, false for HTTP\n * @private\n */\n _shouldUseHttps(address) {\n return !(\n address.includes(\"localhost\") || address.includes(\"127.0.0.1\")\n );\n }\n\n /**\n * Builds the full URL with appropriate scheme (http/https)\n * @param {string} address - The base address\n * @returns {string} - The full URL with scheme\n * @private\n */\n _buildUrl(address) {\n // Check if address already contains a scheme\n const hasScheme =\n address.startsWith(\"http://\") || address.startsWith(\"https://\");\n\n if (hasScheme) {\n // Use the address as-is if it already has a scheme\n return address;\n } else {\n // Only prepend scheme if none exists\n const shouldUseHttps = this._shouldUseHttps(address);\n return shouldUseHttps ? `https://${address}` : `http://${address}`;\n }\n }\n\n /**\n * Check if the gRPC-Web proxy is reachable and healthy\n * Performs a POST request and verifies the response has gRPC-Web headers,\n * which indicates the proxy is running and processing gRPC requests.\n * Results are cached per address for the entire lifecycle.\n * Uses promise-based synchronization to prevent multiple concurrent health checks.\n *\n * @param {Date} deadline - Deadline for the health check\n * @returns {Promise<void>}\n * @private\n */\n async _waitForReady(deadline) {\n // Check if we've already validated this address\n if (this._isReady) {\n return; // Health check already passed for this address\n }\n\n // If a health check is already in progress, wait for it to complete\n if (this._healthCheckPromise) {\n return this._healthCheckPromise;\n }\n\n // Start a new health check and store the promise\n this._healthCheckPromise = this._performHealthCheck(deadline);\n\n try {\n await this._healthCheckPromise;\n } finally {\n // Clear the promise when done (success or failure)\n this._healthCheckPromise = null;\n }\n }\n\n /**\n * Performs the actual health check request\n * @param {Date} deadline - Deadline for the health check\n * @returns {Promise<void>}\n * @private\n */\n async _performHealthCheck(deadline) {\n const address = this._buildUrl(this._address);\n\n // Calculate remaining time until deadline\n const timeoutMs = deadline.getTime() - Date.now();\n if (timeoutMs <= 0) {\n throw new GrpcServiceError(\n GrpcStatus.Timeout,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n }\n\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);\n\n try {\n // Make a POST request to verify the gRPC-Web proxy is running\n // We use a minimal gRPC-Web compatible request\n //eslint-disable-next-line n/no-unsupported-features/node-builtins\n const response = await fetch(address, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/grpc-web+proto\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-grpc-web\": \"1\",\n },\n body: new Uint8Array(0), // Empty body for health check\n signal: abortController.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Check if response is successful (200) or indicates a redirect (3xx)\n // 3xx status codes indicate the resource has moved, which is valid for proxies\n if (\n response.ok ||\n (response.status >= 300 && response.status < 400)\n ) {\n const grpcStatus = response.headers.get(\"grpc-status\");\n const grpcMessage = response.headers.get(\"grpc-message\");\n\n // If gRPC headers exist, the proxy is running and processing requests\n if (grpcStatus != null || grpcMessage != null) {\n // Mark this connection as ready\n this._isReady = true;\n return; // Healthy - gRPC-Web proxy is responding\n }\n }\n\n // If we get here, either status isn't 200/3xx or no gRPC headers present\n // This means the proxy might not be configured correctly or not running\n throw new GrpcServiceError(\n GrpcStatus.Unavailable,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new GrpcServiceError(\n GrpcStatus.Timeout,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n }\n\n if (error instanceof GrpcServiceError) {\n throw error;\n }\n\n // Network error - server is not reachable\n throw new GrpcServiceError(\n GrpcStatus.Unavailable,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n }\n }\n\n /**\n * @override\n * @returns {void}\n */\n close() {\n // do nothing\n }\n\n /**\n * @override\n * @protected\n * @param {string} serviceName\n * @returns {import(\"protobufjs\").RPCImpl}\n */\n _createUnaryClient(serviceName) {\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n return async (method, requestData, callback) => {\n // Calculate deadline for connection check\n const deadline = new Date();\n const milliseconds = this._grpcDeadline;\n\n deadline.setMilliseconds(deadline.getMilliseconds() + milliseconds);\n\n try {\n // Wait for connection to be ready (similar to gRPC waitForReady)\n await this._waitForReady(deadline);\n\n // Build the full URL with appropriate scheme\n const address = this._buildUrl(this._address);\n // this will be executed in a browser environment so eslint is\n // disabled for the fetch call\n //eslint-disable-next-line n/no-unsupported-features/node-builtins\n const response = await fetch(\n `${address}/proto.${serviceName}/${method.name}`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/grpc-web+proto\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-grpc-web\": \"1\",\n },\n body: encodeRequest(requestData),\n },\n );\n\n if (!response.ok) {\n const error = new HttpError(\n HttpStatus._fromValue(response.status),\n );\n callback(error, null);\n return;\n }\n\n // Check headers for gRPC errors\n const grpcStatus = response.headers.get(\"grpc-status\");\n const grpcMessage = response.headers.get(\"grpc-message\");\n\n if (grpcStatus != null && grpcMessage != null) {\n const error = new GrpcServiceError(\n GrpcStatus._fromValue(parseInt(grpcStatus)),\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n error.message = grpcMessage;\n callback(error, null);\n return;\n }\n\n const responseBuffer = await response.arrayBuffer();\n const unaryResponse = decodeUnaryResponse(responseBuffer);\n\n callback(null, unaryResponse);\n } catch (error) {\n if (error instanceof GrpcServiceError) {\n callback(error, null);\n return;\n }\n\n const err = new GrpcServiceError(\n // retry on grpc web errors\n GrpcStatus._fromValue(18),\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n callback(err, null);\n }\n };\n }\n}\n"],"names":["WebChannel","Channel","constructor","address","grpcDeadline","super","this","_address","_isReady","_healthCheckPromise","_shouldUseHttps","includes","_buildUrl","startsWith","_waitForReady","deadline","_performHealthCheck","timeoutMs","getTime","Date","now","GrpcServiceError","GrpcStatus","Timeout","ALL_WEB_NETWORK_NODES","toString","abortController","AbortController","timeoutId","setTimeout","abort","response","fetch","method","headers","SDK_NAME","SDK_VERSION","body","Uint8Array","signal","clearTimeout","ok","status","grpcStatus","get","grpcMessage","Unavailable","error","Error","name","close","_createUnaryClient","serviceName","async","requestData","callback","milliseconds","_grpcDeadline","setMilliseconds","getMilliseconds","encodeRequest","HttpError","HttpStatus","_fromValue","parseInt","message","responseBuffer","arrayBuffer","decodeUnaryResponse"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WebChannel.js","sources":["../../src/channel/WebChannel.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { ALL_WEB_NETWORK_NODES } from \"../constants/ClientConstants.js\";\nimport GrpcServiceError from \"../grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"../grpc/GrpcStatus.js\";\nimport HttpError from \"../http/HttpError.js\";\nimport HttpStatus from \"../http/HttpStatus.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\nimport Channel, { encodeRequest, decodeUnaryResponse } from \"./Channel.js\";\n\nexport default class WebChannel extends Channel {\n /**\n * @param {string} address\n * @param {number=} grpcDeadline\n */\n constructor(address, grpcDeadline) {\n super(grpcDeadline);\n\n /**\n * @type {string}\n * @private\n */\n this._address = address;\n\n // Set the gRPC deadline using the base class method\n\n /**\n * Flag indicating if the connection is ready (health check has passed)\n * Set to true after the first successful health check\n *\n * @type {boolean}\n * @private\n */\n this._isReady = false;\n\n /**\n * Promise that resolves when the health check is complete\n * Used to prevent multiple concurrent health checks\n *\n * @type {Promise<void>|null}\n * @private\n */\n this._healthCheckPromise = null;\n }\n\n /**\n * Determines whether to use HTTPS based on the address\n * @param {string} address - The address to check\n * @returns {boolean} - True if HTTPS should be used, false for HTTP\n * @private\n */\n _shouldUseHttps(address) {\n return !(\n address.includes(\"localhost\") ||\n address.includes(\"127.0.0.1\") ||\n address.includes(\".cluster.local\")\n );\n }\n\n /**\n * Builds the full URL with appropriate scheme (http/https)\n * @param {string} address - The base address\n * @returns {string} - The full URL with scheme\n * @private\n */\n _buildUrl(address) {\n // Check if address already contains a scheme\n const hasScheme =\n address.startsWith(\"http://\") || address.startsWith(\"https://\");\n\n if (hasScheme) {\n // Use the address as-is if it already has a scheme\n return address;\n } else {\n // Only prepend scheme if none exists\n const shouldUseHttps = this._shouldUseHttps(address);\n return shouldUseHttps ? `https://${address}` : `http://${address}`;\n }\n }\n\n /**\n * Check if the gRPC-Web proxy is reachable and healthy\n * Performs a POST request and verifies the response has gRPC-Web headers,\n * which indicates the proxy is running and processing gRPC requests.\n * Results are cached per address for the entire lifecycle.\n * Uses promise-based synchronization to prevent multiple concurrent health checks.\n *\n * @param {Date} deadline - Deadline for the health check\n * @returns {Promise<void>}\n * @private\n */\n async _waitForReady(deadline) {\n // Check if we've already validated this address\n if (this._isReady) {\n return; // Health check already passed for this address\n }\n\n // If a health check is already in progress, wait for it to complete\n if (this._healthCheckPromise) {\n return this._healthCheckPromise;\n }\n\n // Start a new health check and store the promise\n this._healthCheckPromise = this._performHealthCheck(deadline);\n\n try {\n await this._healthCheckPromise;\n } finally {\n // Clear the promise when done (success or failure)\n this._healthCheckPromise = null;\n }\n }\n\n /**\n * Performs the actual health check request\n * @param {Date} deadline - Deadline for the health check\n * @returns {Promise<void>}\n * @private\n */\n async _performHealthCheck(deadline) {\n const address = this._buildUrl(this._address);\n\n // Calculate remaining time until deadline\n const timeoutMs = deadline.getTime() - Date.now();\n if (timeoutMs <= 0) {\n throw new GrpcServiceError(\n GrpcStatus.Timeout,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n }\n\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);\n\n try {\n // Make a POST request to verify the gRPC-Web proxy is running\n // We use a minimal gRPC-Web compatible request\n //eslint-disable-next-line n/no-unsupported-features/node-builtins\n const response = await fetch(address, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/grpc-web+proto\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-grpc-web\": \"1\",\n },\n body: new Uint8Array(0), // Empty body for health check\n signal: abortController.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Check if response is successful (200) or indicates a redirect (3xx)\n // 3xx status codes indicate the resource has moved, which is valid for proxies\n if (\n response.ok ||\n (response.status >= 300 && response.status < 400)\n ) {\n const grpcStatus = response.headers.get(\"grpc-status\");\n const grpcMessage = response.headers.get(\"grpc-message\");\n\n // If gRPC headers exist, the proxy is running and processing requests\n if (grpcStatus != null || grpcMessage != null) {\n // Mark this connection as ready\n this._isReady = true;\n return; // Healthy - gRPC-Web proxy is responding\n }\n }\n\n // If we get here, either status isn't 200/3xx or no gRPC headers present\n // This means the proxy might not be configured correctly or not running\n throw new GrpcServiceError(\n GrpcStatus.Unavailable,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new GrpcServiceError(\n GrpcStatus.Timeout,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n }\n\n if (error instanceof GrpcServiceError) {\n throw error;\n }\n\n // Network error - server is not reachable\n throw new GrpcServiceError(\n GrpcStatus.Unavailable,\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n }\n }\n\n /**\n * @override\n * @returns {void}\n */\n close() {\n // do nothing\n }\n\n /**\n * @override\n * @protected\n * @param {string} serviceName\n * @returns {import(\"protobufjs\").RPCImpl}\n */\n _createUnaryClient(serviceName) {\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n return async (method, requestData, callback) => {\n // Calculate deadline for connection check\n const deadline = new Date();\n const milliseconds = this._grpcDeadline;\n\n deadline.setMilliseconds(deadline.getMilliseconds() + milliseconds);\n\n try {\n // Wait for connection to be ready (similar to gRPC waitForReady)\n await this._waitForReady(deadline);\n\n // Build the full URL with appropriate scheme\n const address = this._buildUrl(this._address);\n // this will be executed in a browser environment so eslint is\n // disabled for the fetch call\n //eslint-disable-next-line n/no-unsupported-features/node-builtins\n const response = await fetch(\n `${address}/proto.${serviceName}/${method.name}`,\n {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/grpc-web+proto\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-grpc-web\": \"1\",\n },\n body: encodeRequest(requestData),\n },\n );\n\n if (!response.ok) {\n const error = new HttpError(\n HttpStatus._fromValue(response.status),\n );\n callback(error, null);\n return;\n }\n\n // Check headers for gRPC errors\n const grpcStatus = response.headers.get(\"grpc-status\");\n const grpcMessage = response.headers.get(\"grpc-message\");\n\n if (grpcStatus != null && grpcMessage != null) {\n const error = new GrpcServiceError(\n GrpcStatus._fromValue(parseInt(grpcStatus)),\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n error.message = grpcMessage;\n callback(error, null);\n return;\n }\n\n const responseBuffer = await response.arrayBuffer();\n const unaryResponse = decodeUnaryResponse(responseBuffer);\n\n callback(null, unaryResponse);\n } catch (error) {\n if (error instanceof GrpcServiceError) {\n callback(error, null);\n return;\n }\n\n const err = new GrpcServiceError(\n // retry on grpc web errors\n GrpcStatus._fromValue(18),\n ALL_WEB_NETWORK_NODES?.[this._address]?.toString(),\n );\n callback(err, null);\n }\n };\n }\n}\n"],"names":["WebChannel","Channel","constructor","address","grpcDeadline","super","this","_address","_isReady","_healthCheckPromise","_shouldUseHttps","includes","_buildUrl","startsWith","_waitForReady","deadline","_performHealthCheck","timeoutMs","getTime","Date","now","GrpcServiceError","GrpcStatus","Timeout","ALL_WEB_NETWORK_NODES","toString","abortController","AbortController","timeoutId","setTimeout","abort","response","fetch","method","headers","SDK_NAME","SDK_VERSION","body","Uint8Array","signal","clearTimeout","ok","status","grpcStatus","get","grpcMessage","Unavailable","error","Error","name","close","_createUnaryClient","serviceName","async","requestData","callback","milliseconds","_grpcDeadline","setMilliseconds","getMilliseconds","encodeRequest","HttpError","HttpStatus","_fromValue","parseInt","message","responseBuffer","arrayBuffer","decodeUnaryResponse"],"mappings":"oWASe,MAAMA,UAAmBC,EAKpC,WAAAC,CAAYC,EAASC,GACjBC,MAAMD,GAMNE,KAAKC,SAAWJ,EAWhBG,KAAKE,UAAW,EAShBF,KAAKG,oBAAsB,IACnC,CAQI,eAAAC,CAAgBP,GACZ,QACIA,EAAQQ,SAAS,cACjBR,EAAQQ,SAAS,cACjBR,EAAQQ,SAAS,kBAE7B,CAQI,SAAAC,CAAUT,GAKN,GAFIA,EAAQU,WAAW,YAAcV,EAAQU,WAAW,YAIpD,OAAOV,EAIP,OADuBG,KAAKI,gBAAgBP,GACpB,WAAWA,IAAY,UAAUA,GAErE,CAaI,mBAAMW,CAAcC,GAEhB,IAAIT,KAAKE,SAAT,CAKA,GAAIF,KAAKG,oBACL,OAAOH,KAAKG,oBAIhBH,KAAKG,oBAAsBH,KAAKU,oBAAoBD,GAEpD,UACUT,KAAKG,mBACvB,CAAkB,QAENH,KAAKG,oBAAsB,IACvC,CAfA,CAgBA,CAQI,yBAAMO,CAAoBD,GACtB,MAAMZ,EAAUG,KAAKM,UAAUN,KAAKC,UAG9BU,EAAYF,EAASG,UAAYC,KAAKC,MAC5C,GAAIH,GAAa,EACb,MAAM,IAAII,EACNC,EAAWC,QACXC,IAAwBlB,KAAKC,WAAWkB,YAIhD,MAAMC,EAAkB,IAAIC,gBACtBC,EAAYC,WAAW,IAAMH,EAAgBI,QAASb,GAE5D,IAII,MAAMc,QAAiBC,MAAM7B,EAAS,CAClC8B,OAAQ,OACRC,QAAS,CACL,eAAgB,6BAChB,eAAgB,GAAGC,KAAYC,IAC/B,aAAc,KAElBC,KAAM,IAAIC,WAAW,GACrBC,OAAQb,EAAgBa,SAO5B,GAJAC,aAAaZ,GAKTG,EAASU,IACRV,EAASW,QAAU,KAAOX,EAASW,OAAS,IAC/C,CACE,MAAMC,EAAaZ,EAASG,QAAQU,IAAI,eAClCC,EAAcd,EAASG,QAAQU,IAAI,gBAGzC,GAAkB,MAAdD,GAAqC,MAAfE,EAGtB,YADAvC,KAAKE,UAAW,EAGpC,CAIY,MAAM,IAAIa,EACNC,EAAWwB,YACXtB,IAAwBlB,KAAKC,WAAWkB,WAE/C,CAAC,MAAOsB,GAGL,GAFAP,aAAaZ,GAETmB,aAAiBC,OAAwB,eAAfD,EAAME,KAChC,MAAM,IAAI5B,EACNC,EAAWC,QACXC,IAAwBlB,KAAKC,WAAWkB,YAIhD,GAAIsB,aAAiB1B,EACjB,MAAM0B,EAIV,MAAM,IAAI1B,EACNC,EAAWwB,YACXtB,IAAwBlB,KAAKC,WAAWkB,WAExD,CACA,CAMI,KAAAyB,GAEJ,CAQI,kBAAAC,CAAmBC,GAEf,OAAOC,MAAOpB,EAAQqB,EAAaC,KAE/B,MAAMxC,EAAW,IAAII,KACfqC,EAAelD,KAAKmD,cAE1B1C,EAAS2C,gBAAgB3C,EAAS4C,kBAAoBH,GAEtD,UAEUlD,KAAKQ,cAAcC,GAGzB,MAAMZ,EAAUG,KAAKM,UAAUN,KAAKC,UAI9BwB,QAAiBC,MACnB,GAAG7B,WAAiBiD,KAAenB,EAAOgB,OAC1C,CACIhB,OAAQ,OACRC,QAAS,CACL,eAAgB,6BAChB,eAAgB,GAAGC,KAAYC,IAC/B,aAAc,KAElBC,KAAMuB,EAAcN,KAI5B,IAAKvB,EAASU,GAAI,CAKd,YADAc,EAHc,IAAIM,EACdC,EAAWC,WAAWhC,EAASW,SAEnB,KAEpC,CAGgB,MAAMC,EAAaZ,EAASG,QAAQU,IAAI,eAClCC,EAAcd,EAASG,QAAQU,IAAI,gBAEzC,GAAkB,MAAdD,GAAqC,MAAfE,EAAqB,CAC3C,MAAME,EAAQ,IAAI1B,EACdC,EAAWyC,WAAWC,SAASrB,IAC/BnB,IAAwBlB,KAAKC,WAAWkB,YAI5C,OAFAsB,EAAMkB,QAAUpB,OAChBU,EAASR,EAAO,KAEpC,CAEgB,MAAMmB,QAAuBnC,EAASoC,cAGtCZ,EAAS,KAFaa,EAAoBF,GAG7C,CAAC,MAAOnB,GACL,GAAIA,aAAiB1B,EAEjB,YADAkC,EAASR,EAAO,MASpBQ,EALY,IAAIlC,EAEZC,EAAWyC,WAAW,IACtBvC,IAAwBlB,KAAKC,WAAWkB,YAE9B,KAC9B,EAEA"}
|
package/lib/client/Network.cjs
CHANGED
|
@@ -240,10 +240,7 @@ class Network extends _ManagedNetwork.default {
|
|
|
240
240
|
if (this._maxNodesPerTransaction > 0) {
|
|
241
241
|
return this._maxNodesPerTransaction;
|
|
242
242
|
}
|
|
243
|
-
|
|
244
|
-
// if we round up, we will eventually take one more healthy node for execution
|
|
245
|
-
// and we would hit the 'nodes.length == count' check in _getNumberOfMostHealthyNodes() less often
|
|
246
|
-
return this._nodes.length <= 9 ? this._nodes.length : Math.floor((this._nodes.length + 3 - 1) / 3);
|
|
243
|
+
return this._nodes.length;
|
|
247
244
|
}
|
|
248
245
|
|
|
249
246
|
/**
|
package/lib/client/Network.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import t from"../account/AccountId.js";import e from"../Node.js";import{PREVIEWNET_ADDRESS_BOOK as s,TESTNET_ADDRESS_BOOK as r,MAINNET_ADDRESS_BOOK as o}from"../address_book/AddressBooks.js";import n from"./ManagedNetwork.js";class i extends n{constructor(t){super(t),this._maxNodesPerTransaction=-1,this._addressBook=null,this._transportSecurity=!1}setNetwork(e){this._setNetwork(new Map(Object.entries(e).map(([e,s])=>[e,"string"==typeof s?t.fromString(s):s])))}setNetworkFromAddressBook(t){const e={},s=this.isTransportSecurity()?50212:50211;for(const r of t.nodeAddresses)for(const t of r.addresses)t.port===s&&null!=r.accountId&&(e[t.toString()]=r.accountId);return this.setNetwork(e),this}get network(){var t={};for(const e of this._nodes)t[e.address.toString()]=e.accountId;return t}setNetworkName(t){switch(super.setLedgerId(t),t){case"mainnet":this._addressBook=o;break;case"testnet":this._addressBook=r;break;case"previewnet":this._addressBook=s}if(null!=this._addressBook)for(const t of this._nodes)for(const e of this._addressBook.nodeAddresses)null!=e.accountId&&e.accountId.toString()===t.accountId.toString()&&t.setNodeAddress(e);return this}get networkName(){return null!=this._ledgerId?this._ledgerId.toString():null}_createNodeFromNetworkEntry(s){const r="string"==typeof s[1]?t.fromString(s[1]):s[1];return new e({newNode:{address:s[0],accountId:r,channelInitFunction:this._createNetworkChannel}}).setMinBackoff(this._minBackoff)}_getNodesToRemove(t){const e=[];for(let s=this._nodes.length-1;s>=0;s--){const r=this._nodes[s],o=t.get(r.address.toString());null!=o&&o.toString()===r.accountId.toString()||e.push(s)}return e}_checkNetworkContainsEntry(t){for(const e of this._nodes)if(e.address.toString()===t[0])return!0;return!1}get maxNodesPerTransaction(){return this._maxNodesPerTransaction}setMaxNodesPerTransaction(t){return this._maxNodesPerTransaction=t,this}get maxNodeAttempts(){return this._maxNodeAttempts}setMaxNodeAttempts(t){return this._maxNodeAttempts=t,this}isTransportSecurity(){return this._transportSecurity}setTransportSecurity(t){if(this._transportSecurity==t)return this;this._network.clear();for(let e=0;e<this._nodes.length;e++){let s=this._nodes[e];s.close(),s=t?s.toSecure().setCert(null!=this._ledgerId?this._ledgerId.toString():""):s.toInsecure(),this._nodes[e]=s;const r=null!=this._network.get(s.getKey())?this._network.get(s.getKey()):[];r.push(s),this._network.set(s.getKey(),r)}return this._healthyNodes=[...this._nodes],this._transportSecurity=t,this}getNumberOfNodesForTransaction(){return this._maxNodesPerTransaction>0?this._maxNodesPerTransaction:this._nodes.length
|
|
1
|
+
import t from"../account/AccountId.js";import e from"../Node.js";import{PREVIEWNET_ADDRESS_BOOK as s,TESTNET_ADDRESS_BOOK as r,MAINNET_ADDRESS_BOOK as o}from"../address_book/AddressBooks.js";import n from"./ManagedNetwork.js";class i extends n{constructor(t){super(t),this._maxNodesPerTransaction=-1,this._addressBook=null,this._transportSecurity=!1}setNetwork(e){this._setNetwork(new Map(Object.entries(e).map(([e,s])=>[e,"string"==typeof s?t.fromString(s):s])))}setNetworkFromAddressBook(t){const e={},s=this.isTransportSecurity()?50212:50211;for(const r of t.nodeAddresses)for(const t of r.addresses)t.port===s&&null!=r.accountId&&(e[t.toString()]=r.accountId);return this.setNetwork(e),this}get network(){var t={};for(const e of this._nodes)t[e.address.toString()]=e.accountId;return t}setNetworkName(t){switch(super.setLedgerId(t),t){case"mainnet":this._addressBook=o;break;case"testnet":this._addressBook=r;break;case"previewnet":this._addressBook=s}if(null!=this._addressBook)for(const t of this._nodes)for(const e of this._addressBook.nodeAddresses)null!=e.accountId&&e.accountId.toString()===t.accountId.toString()&&t.setNodeAddress(e);return this}get networkName(){return null!=this._ledgerId?this._ledgerId.toString():null}_createNodeFromNetworkEntry(s){const r="string"==typeof s[1]?t.fromString(s[1]):s[1];return new e({newNode:{address:s[0],accountId:r,channelInitFunction:this._createNetworkChannel}}).setMinBackoff(this._minBackoff)}_getNodesToRemove(t){const e=[];for(let s=this._nodes.length-1;s>=0;s--){const r=this._nodes[s],o=t.get(r.address.toString());null!=o&&o.toString()===r.accountId.toString()||e.push(s)}return e}_checkNetworkContainsEntry(t){for(const e of this._nodes)if(e.address.toString()===t[0])return!0;return!1}get maxNodesPerTransaction(){return this._maxNodesPerTransaction}setMaxNodesPerTransaction(t){return this._maxNodesPerTransaction=t,this}get maxNodeAttempts(){return this._maxNodeAttempts}setMaxNodeAttempts(t){return this._maxNodeAttempts=t,this}isTransportSecurity(){return this._transportSecurity}setTransportSecurity(t){if(this._transportSecurity==t)return this;this._network.clear();for(let e=0;e<this._nodes.length;e++){let s=this._nodes[e];s.close(),s=t?s.toSecure().setCert(null!=this._ledgerId?this._ledgerId.toString():""):s.toInsecure(),this._nodes[e]=s;const r=null!=this._network.get(s.getKey())?this._network.get(s.getKey()):[];r.push(s),this._network.set(s.getKey(),r)}return this._healthyNodes=[...this._nodes],this._transportSecurity=t,this}getNumberOfNodesForTransaction(){return this._maxNodesPerTransaction>0?this._maxNodesPerTransaction:this._nodes.length}getNodeAccountIdsForExecute(){return this._getNumberOfMostHealthyNodes(this.getNumberOfNodesForTransaction()).map(t=>t.accountId)}}export{i as default};
|
|
2
2
|
//# sourceMappingURL=Network.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Network.js","sources":["../../src/client/Network.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nimport AccountId from \"../account/AccountId.js\";\nimport Node from \"../Node.js\";\nimport {\n PREVIEWNET_ADDRESS_BOOK,\n TESTNET_ADDRESS_BOOK,\n MAINNET_ADDRESS_BOOK,\n} from \"../address_book/AddressBooks.js\";\nimport ManagedNetwork from \"./ManagedNetwork.js\";\n\n/**\n * @typedef {import(\"../channel/Channel.js\").default} Channel\n * @typedef {import(\"../address_book/NodeAddressBook.js\").default} NodeAddressBook\n */\n\n/**\n * @augments {ManagedNetwork<Channel, Node, AccountId>}\n */\nexport default class Network extends ManagedNetwork {\n /**\n * @param {(address: string) => Channel} createNetworkChannel\n */\n constructor(createNetworkChannel) {\n super(createNetworkChannel);\n\n this._maxNodesPerTransaction = -1;\n\n /** @type {NodeAddressBook | null} */\n this._addressBook = null;\n\n /** @type {boolean} */\n this._transportSecurity = false;\n }\n\n /**\n * @param {{[key: string]: (string | AccountId)}} network\n */\n setNetwork(network) {\n this._setNetwork(\n // eslint-disable-next-line ie11/no-collection-args\n new Map(\n // eslint-disable-next-line ie11/no-collection-args\n Object.entries(network).map(([key, value]) => {\n return [\n key,\n typeof value === \"string\"\n ? AccountId.fromString(value)\n : value,\n ];\n }),\n ),\n );\n }\n\n /**\n * @param {NodeAddressBook} addressBook\n * @returns {this}\n */\n setNetworkFromAddressBook(addressBook) {\n /** @type {Record<string, AccountId>} */\n const network = {};\n const port = this.isTransportSecurity() ? 50212 : 50211;\n\n for (const nodeAddress of addressBook.nodeAddresses) {\n for (const endpoint of nodeAddress.addresses) {\n // TODO: We hard code ports too much, should fix\n if (endpoint.port === port && nodeAddress.accountId != null) {\n network[endpoint.toString()] = nodeAddress.accountId;\n }\n }\n }\n\n this.setNetwork(network);\n return this;\n }\n\n /**\n * @returns {{[key: string]: (string | AccountId)}}\n */\n get network() {\n /**\n * @type {{[key: string]: (string | AccountId)}}\n */\n var n = {};\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n for (const node of this._nodes) {\n n[node.address.toString()] = node.accountId;\n }\n\n return n;\n }\n\n /**\n * @param {string} networkName\n * @returns {this}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n setNetworkName(networkName) {\n super.setLedgerId(networkName);\n\n switch (networkName) {\n case \"mainnet\":\n this._addressBook = MAINNET_ADDRESS_BOOK;\n break;\n case \"testnet\":\n this._addressBook = TESTNET_ADDRESS_BOOK;\n break;\n case \"previewnet\":\n this._addressBook = PREVIEWNET_ADDRESS_BOOK;\n break;\n }\n\n if (this._addressBook != null) {\n for (const node of this._nodes) {\n for (const address of this._addressBook.nodeAddresses) {\n if (\n address.accountId != null &&\n address.accountId.toString() ===\n node.accountId.toString()\n ) {\n node.setNodeAddress(address);\n }\n }\n }\n }\n\n return this;\n }\n\n /**\n * @returns {string | null}\n */\n get networkName() {\n return this._ledgerId != null ? this._ledgerId.toString() : null;\n }\n\n /**\n * @abstract\n * @param {[string, (string | AccountId)]} entry\n * @returns {Node}\n */\n _createNodeFromNetworkEntry(entry) {\n const accountId =\n typeof entry[1] === \"string\"\n ? AccountId.fromString(entry[1])\n : entry[1];\n\n return new Node({\n newNode: {\n address: entry[0],\n accountId,\n channelInitFunction: this._createNetworkChannel,\n },\n }).setMinBackoff(this._minBackoff);\n }\n\n /**\n * @abstract\n * @param {Map<string, AccountId>} network\n * @returns {number[]}\n */\n _getNodesToRemove(network) {\n const indexes = [];\n\n for (let i = this._nodes.length - 1; i >= 0; i--) {\n const node = this._nodes[i];\n const accountId = network.get(node.address.toString());\n\n if (\n accountId == null ||\n accountId.toString() !== node.accountId.toString()\n ) {\n indexes.push(i);\n }\n }\n\n return indexes;\n }\n\n /**\n * @abstract\n * @param {[string, (string | AccountId)]} entry\n * @returns {boolean}\n */\n _checkNetworkContainsEntry(entry) {\n for (const node of this._nodes) {\n if (node.address.toString() === entry[0]) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * @returns {number}\n */\n get maxNodesPerTransaction() {\n return this._maxNodesPerTransaction;\n }\n\n /**\n * @param {number} maxNodesPerTransaction\n * @returns {this}\n */\n setMaxNodesPerTransaction(maxNodesPerTransaction) {\n this._maxNodesPerTransaction = maxNodesPerTransaction;\n return this;\n }\n\n /**\n * @returns {number}\n */\n get maxNodeAttempts() {\n return this._maxNodeAttempts;\n }\n\n /**\n * @param {number} maxNodeAttempts\n * @returns {this}\n */\n setMaxNodeAttempts(maxNodeAttempts) {\n this._maxNodeAttempts = maxNodeAttempts;\n return this;\n }\n\n /**\n * @returns {boolean}\n */\n isTransportSecurity() {\n return this._transportSecurity;\n }\n\n /**\n * @param {boolean} transportSecurity\n * @returns {this}\n */\n setTransportSecurity(transportSecurity) {\n if (this._transportSecurity == transportSecurity) {\n return this;\n }\n\n this._network.clear();\n\n for (let i = 0; i < this._nodes.length; i++) {\n let node = this._nodes[i];\n node.close();\n\n node = /** @type {Node} */ (\n transportSecurity\n ? node\n .toSecure()\n .setCert(\n this._ledgerId != null\n ? this._ledgerId.toString()\n : \"\",\n )\n : node.toInsecure()\n );\n this._nodes[i] = node;\n\n const nodes =\n this._network.get(node.getKey()) != null\n ? /** @type {Node[]} */ (this._network.get(node.getKey()))\n : [];\n nodes.push(node);\n this._network.set(node.getKey(), nodes);\n }\n\n // Overwrite healthy node list since new ports might make the node work again\n this._healthyNodes = [...this._nodes];\n\n this._transportSecurity = transportSecurity;\n return this;\n }\n\n /**\n * @internal\n * @returns {number}\n */\n getNumberOfNodesForTransaction() {\n if (this._maxNodesPerTransaction > 0) {\n return this._maxNodesPerTransaction;\n }\n // ultimately it does not matter if we round up or down\n // if we round up, we will eventually take one more healthy node for execution\n // and we would hit the 'nodes.length == count' check in _getNumberOfMostHealthyNodes() less often\n return this._nodes.length <= 9\n ? this._nodes.length\n : Math.floor((this._nodes.length + 3 - 1) / 3);\n }\n\n /**\n * @internal\n * @returns {AccountId[]}\n */\n getNodeAccountIdsForExecute() {\n return this._getNumberOfMostHealthyNodes(\n this.getNumberOfNodesForTransaction(),\n ).map((node) => node.accountId);\n }\n}\n"],"names":["Network","ManagedNetwork","constructor","createNetworkChannel","super","this","_maxNodesPerTransaction","_addressBook","_transportSecurity","setNetwork","network","_setNetwork","Map","Object","entries","map","key","value","AccountId","fromString","setNetworkFromAddressBook","addressBook","port","isTransportSecurity","nodeAddress","nodeAddresses","endpoint","addresses","accountId","toString","n","node","_nodes","address","setNetworkName","networkName","setLedgerId","MAINNET_ADDRESS_BOOK","TESTNET_ADDRESS_BOOK","PREVIEWNET_ADDRESS_BOOK","setNodeAddress","_ledgerId","_createNodeFromNetworkEntry","entry","Node","newNode","channelInitFunction","_createNetworkChannel","setMinBackoff","_minBackoff","_getNodesToRemove","indexes","i","length","get","push","_checkNetworkContainsEntry","maxNodesPerTransaction","setMaxNodesPerTransaction","maxNodeAttempts","_maxNodeAttempts","setMaxNodeAttempts","setTransportSecurity","transportSecurity","_network","clear","close","toSecure","setCert","toInsecure","nodes","getKey","set","_healthyNodes","getNumberOfNodesForTransaction","Math","floor","getNodeAccountIdsForExecute","_getNumberOfMostHealthyNodes"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Network.js","sources":["../../src/client/Network.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\nimport AccountId from \"../account/AccountId.js\";\nimport Node from \"../Node.js\";\nimport {\n PREVIEWNET_ADDRESS_BOOK,\n TESTNET_ADDRESS_BOOK,\n MAINNET_ADDRESS_BOOK,\n} from \"../address_book/AddressBooks.js\";\nimport ManagedNetwork from \"./ManagedNetwork.js\";\n\n/**\n * @typedef {import(\"../channel/Channel.js\").default} Channel\n * @typedef {import(\"../address_book/NodeAddressBook.js\").default} NodeAddressBook\n */\n\n/**\n * @augments {ManagedNetwork<Channel, Node, AccountId>}\n */\nexport default class Network extends ManagedNetwork {\n /**\n * @param {(address: string) => Channel} createNetworkChannel\n */\n constructor(createNetworkChannel) {\n super(createNetworkChannel);\n\n this._maxNodesPerTransaction = -1;\n\n /** @type {NodeAddressBook | null} */\n this._addressBook = null;\n\n /** @type {boolean} */\n this._transportSecurity = false;\n }\n\n /**\n * @param {{[key: string]: (string | AccountId)}} network\n */\n setNetwork(network) {\n this._setNetwork(\n // eslint-disable-next-line ie11/no-collection-args\n new Map(\n // eslint-disable-next-line ie11/no-collection-args\n Object.entries(network).map(([key, value]) => {\n return [\n key,\n typeof value === \"string\"\n ? AccountId.fromString(value)\n : value,\n ];\n }),\n ),\n );\n }\n\n /**\n * @param {NodeAddressBook} addressBook\n * @returns {this}\n */\n setNetworkFromAddressBook(addressBook) {\n /** @type {Record<string, AccountId>} */\n const network = {};\n const port = this.isTransportSecurity() ? 50212 : 50211;\n\n for (const nodeAddress of addressBook.nodeAddresses) {\n for (const endpoint of nodeAddress.addresses) {\n // TODO: We hard code ports too much, should fix\n if (endpoint.port === port && nodeAddress.accountId != null) {\n network[endpoint.toString()] = nodeAddress.accountId;\n }\n }\n }\n\n this.setNetwork(network);\n return this;\n }\n\n /**\n * @returns {{[key: string]: (string | AccountId)}}\n */\n get network() {\n /**\n * @type {{[key: string]: (string | AccountId)}}\n */\n var n = {};\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n for (const node of this._nodes) {\n n[node.address.toString()] = node.accountId;\n }\n\n return n;\n }\n\n /**\n * @param {string} networkName\n * @returns {this}\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n setNetworkName(networkName) {\n super.setLedgerId(networkName);\n\n switch (networkName) {\n case \"mainnet\":\n this._addressBook = MAINNET_ADDRESS_BOOK;\n break;\n case \"testnet\":\n this._addressBook = TESTNET_ADDRESS_BOOK;\n break;\n case \"previewnet\":\n this._addressBook = PREVIEWNET_ADDRESS_BOOK;\n break;\n }\n\n if (this._addressBook != null) {\n for (const node of this._nodes) {\n for (const address of this._addressBook.nodeAddresses) {\n if (\n address.accountId != null &&\n address.accountId.toString() ===\n node.accountId.toString()\n ) {\n node.setNodeAddress(address);\n }\n }\n }\n }\n\n return this;\n }\n\n /**\n * @returns {string | null}\n */\n get networkName() {\n return this._ledgerId != null ? this._ledgerId.toString() : null;\n }\n\n /**\n * @abstract\n * @param {[string, (string | AccountId)]} entry\n * @returns {Node}\n */\n _createNodeFromNetworkEntry(entry) {\n const accountId =\n typeof entry[1] === \"string\"\n ? AccountId.fromString(entry[1])\n : entry[1];\n\n return new Node({\n newNode: {\n address: entry[0],\n accountId,\n channelInitFunction: this._createNetworkChannel,\n },\n }).setMinBackoff(this._minBackoff);\n }\n\n /**\n * @abstract\n * @param {Map<string, AccountId>} network\n * @returns {number[]}\n */\n _getNodesToRemove(network) {\n const indexes = [];\n\n for (let i = this._nodes.length - 1; i >= 0; i--) {\n const node = this._nodes[i];\n const accountId = network.get(node.address.toString());\n\n if (\n accountId == null ||\n accountId.toString() !== node.accountId.toString()\n ) {\n indexes.push(i);\n }\n }\n\n return indexes;\n }\n\n /**\n * @abstract\n * @param {[string, (string | AccountId)]} entry\n * @returns {boolean}\n */\n _checkNetworkContainsEntry(entry) {\n for (const node of this._nodes) {\n if (node.address.toString() === entry[0]) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * @returns {number}\n */\n get maxNodesPerTransaction() {\n return this._maxNodesPerTransaction;\n }\n\n /**\n * @param {number} maxNodesPerTransaction\n * @returns {this}\n */\n setMaxNodesPerTransaction(maxNodesPerTransaction) {\n this._maxNodesPerTransaction = maxNodesPerTransaction;\n return this;\n }\n\n /**\n * @returns {number}\n */\n get maxNodeAttempts() {\n return this._maxNodeAttempts;\n }\n\n /**\n * @param {number} maxNodeAttempts\n * @returns {this}\n */\n setMaxNodeAttempts(maxNodeAttempts) {\n this._maxNodeAttempts = maxNodeAttempts;\n return this;\n }\n\n /**\n * @returns {boolean}\n */\n isTransportSecurity() {\n return this._transportSecurity;\n }\n\n /**\n * @param {boolean} transportSecurity\n * @returns {this}\n */\n setTransportSecurity(transportSecurity) {\n if (this._transportSecurity == transportSecurity) {\n return this;\n }\n\n this._network.clear();\n\n for (let i = 0; i < this._nodes.length; i++) {\n let node = this._nodes[i];\n node.close();\n\n node = /** @type {Node} */ (\n transportSecurity\n ? node\n .toSecure()\n .setCert(\n this._ledgerId != null\n ? this._ledgerId.toString()\n : \"\",\n )\n : node.toInsecure()\n );\n this._nodes[i] = node;\n\n const nodes =\n this._network.get(node.getKey()) != null\n ? /** @type {Node[]} */ (this._network.get(node.getKey()))\n : [];\n nodes.push(node);\n this._network.set(node.getKey(), nodes);\n }\n\n // Overwrite healthy node list since new ports might make the node work again\n this._healthyNodes = [...this._nodes];\n\n this._transportSecurity = transportSecurity;\n return this;\n }\n\n /**\n * @internal\n * @returns {number}\n */\n getNumberOfNodesForTransaction() {\n if (this._maxNodesPerTransaction > 0) {\n return this._maxNodesPerTransaction;\n }\n return this._nodes.length;\n }\n\n /**\n * @internal\n * @returns {AccountId[]}\n */\n getNodeAccountIdsForExecute() {\n return this._getNumberOfMostHealthyNodes(\n this.getNumberOfNodesForTransaction(),\n ).map((node) => node.accountId);\n }\n}\n"],"names":["Network","ManagedNetwork","constructor","createNetworkChannel","super","this","_maxNodesPerTransaction","_addressBook","_transportSecurity","setNetwork","network","_setNetwork","Map","Object","entries","map","key","value","AccountId","fromString","setNetworkFromAddressBook","addressBook","port","isTransportSecurity","nodeAddress","nodeAddresses","endpoint","addresses","accountId","toString","n","node","_nodes","address","setNetworkName","networkName","setLedgerId","MAINNET_ADDRESS_BOOK","TESTNET_ADDRESS_BOOK","PREVIEWNET_ADDRESS_BOOK","setNodeAddress","_ledgerId","_createNodeFromNetworkEntry","entry","Node","newNode","channelInitFunction","_createNetworkChannel","setMinBackoff","_minBackoff","_getNodesToRemove","indexes","i","length","get","push","_checkNetworkContainsEntry","maxNodesPerTransaction","setMaxNodesPerTransaction","maxNodeAttempts","_maxNodeAttempts","setMaxNodeAttempts","setTransportSecurity","transportSecurity","_network","clear","close","toSecure","setCert","toInsecure","nodes","getKey","set","_healthyNodes","getNumberOfNodesForTransaction","getNodeAccountIdsForExecute","_getNumberOfMostHealthyNodes"],"mappings":"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"}
|