@hiero-ledger/sdk 2.76.0 → 2.77.0
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 +426 -82
- package/dist/umd.min.js +14 -14
- package/lib/Executable.cjs +13 -3
- package/lib/Executable.js +1 -1
- package/lib/Executable.js.map +1 -1
- package/lib/channel/Channel.cjs +25 -1
- package/lib/channel/Channel.d.ts +20 -0
- package/lib/channel/Channel.js +1 -1
- package/lib/channel/Channel.js.map +1 -1
- package/lib/channel/NativeChannel.cjs +96 -2
- package/lib/channel/NativeChannel.d.ts +21 -1
- package/lib/channel/NativeChannel.js +1 -1
- package/lib/channel/NativeChannel.js.map +1 -1
- package/lib/channel/NodeChannel.cjs +4 -5
- package/lib/channel/NodeChannel.d.ts +2 -3
- package/lib/channel/NodeChannel.js +1 -1
- package/lib/channel/NodeChannel.js.map +1 -1
- package/lib/channel/WebChannel.cjs +163 -13
- package/lib/channel/WebChannel.d.ts +51 -1
- package/lib/channel/WebChannel.js +1 -1
- package/lib/channel/WebChannel.js.map +1 -1
- package/lib/client/Client.cjs +62 -5
- package/lib/client/Client.d.ts +26 -3
- package/lib/client/Client.js +1 -1
- package/lib/client/Client.js.map +1 -1
- package/lib/client/NativeClient.cjs +1 -1
- package/lib/client/NativeClient.js +1 -1
- package/lib/client/NativeClient.js.map +1 -1
- package/lib/client/NodeClient.cjs +7 -6
- package/lib/client/NodeClient.d.ts +3 -3
- package/lib/client/NodeClient.js +1 -1
- package/lib/client/NodeClient.js.map +1 -1
- package/lib/client/WebClient.cjs +55 -27
- package/lib/client/WebClient.d.ts +6 -0
- package/lib/client/WebClient.js +1 -1
- package/lib/client/WebClient.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/constants/ClientConstants.cjs +17 -1
- package/lib/constants/ClientConstants.d.ts +14 -0
- package/lib/constants/ClientConstants.js +1 -1
- package/lib/constants/ClientConstants.js.map +1 -1
- package/lib/network/AddressBookQuery.cjs +0 -4
- package/lib/network/AddressBookQuery.js +1 -1
- package/lib/network/AddressBookQuery.js.map +1 -1
- package/lib/network/AddressBookQueryWeb.cjs +1 -5
- package/lib/network/AddressBookQueryWeb.js +1 -1
- package/lib/network/AddressBookQueryWeb.js.map +1 -1
- package/lib/version.js +1 -1
- package/package.json +7 -7
- package/src/Executable.js +18 -2
- package/src/channel/Channel.js +25 -1
- package/src/channel/NativeChannel.js +111 -2
- package/src/channel/NodeChannel.js +4 -7
- package/src/channel/WebChannel.js +189 -21
- package/src/client/Client.js +79 -5
- package/src/client/NativeClient.js +1 -1
- package/src/client/NodeClient.js +7 -6
- package/src/client/WebClient.js +64 -31
- 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/constants/ClientConstants.js +16 -0
- package/src/network/AddressBookQuery.js +0 -7
- package/src/network/AddressBookQueryWeb.js +1 -8
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
export default class NativeChannel extends Channel {
|
|
2
2
|
/**
|
|
3
3
|
* @param {string} address
|
|
4
|
+
* @param {number=} grpcDeadline
|
|
4
5
|
*/
|
|
5
|
-
constructor(address: string);
|
|
6
|
+
constructor(address: string, grpcDeadline?: number | undefined);
|
|
6
7
|
/**
|
|
7
8
|
* @type {string}
|
|
8
9
|
* @private
|
|
9
10
|
*/
|
|
10
11
|
private _address;
|
|
12
|
+
/**
|
|
13
|
+
* Flag indicating if the connection is ready (health check has passed)
|
|
14
|
+
* Set to true after the first successful health check
|
|
15
|
+
*
|
|
16
|
+
* @type {boolean}
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
private _isReady;
|
|
20
|
+
/**
|
|
21
|
+
* Check if the gRPC-Web proxy is reachable and healthy
|
|
22
|
+
* Performs a POST request and verifies the response has gRPC-Web headers,
|
|
23
|
+
* which indicates the proxy is running and processing gRPC requests.
|
|
24
|
+
* Results are cached per address for the entire lifecycle.
|
|
25
|
+
*
|
|
26
|
+
* @param {Date} deadline - Deadline for the health check
|
|
27
|
+
* @returns {Promise<void>}
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
private _waitForReady;
|
|
11
31
|
}
|
|
12
32
|
import Channel from "./Channel.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import t,{encodeRequest as e,decodeUnaryResponse as a}from"./Channel.js";import{encode as s,decode as r}from"../encoding/base64.native.js";import o from"../http/HttpError.js";import
|
|
1
|
+
import t,{encodeRequest as e,decodeUnaryResponse as a}from"./Channel.js";import{encode as s,decode as r}from"../encoding/base64.native.js";import o from"../http/HttpError.js";import i from"../http/HttpStatus.js";import{SDK_NAME as n,SDK_VERSION as c}from"../version.js";import p from"../grpc/GrpcServiceError.js";import d from"../grpc/GrpcStatus.js";class l extends t{constructor(t,e){super(e),this._address=t,this._isReady=!1}close(){}async _waitForReady(t){if(this._isReady)return;const e=!(this._address.includes("localhost")||this._address.includes("127.0.0.1"))?`https://${this._address}`:`http://${this._address}`,a=t.getTime()-Date.now();if(a<=0)throw new p(d.Timeout);const r=new AbortController,o=setTimeout(()=>r.abort(),a);try{const t=await fetch(e,{method:"POST",headers:{"content-type":"application/grpc-web-text","x-user-agent":`${n}/${c}`,"x-grpc-web":"1"},body:s(new Uint8Array(0)),signal:r.signal});if(clearTimeout(o),200===t.status){const e=t.headers.get("grpc-status"),a=t.headers.get("grpc-message");if(null!=e||null!=a)return void(this._isReady=!0)}throw new p(d.Unavailable)}catch(t){if(clearTimeout(o),t instanceof Error&&"AbortError"===t.name)throw new p(d.Timeout);if(t instanceof p)throw t;throw new p(d.Unavailable)}}_createUnaryClient(t){return async(d,l,h)=>{const u=new Date,w=this._grpcDeadline;u.setMilliseconds(u.getMilliseconds()+w);try{await this._waitForReady(u);const p=s(new Uint8Array(e(l))),w=!(this._address.includes("localhost")||this._address.includes("127.0.0.1"))?`https://${this._address}`:`http://${this._address}`,f=await fetch(`${w}/proto.${t}/${d.name}`,{method:"POST",headers:{"content-type":"application/grpc-web-text","x-user-agent":`${n}/${c}`,"x-accept-content-transfer-encoding":"base64","x-grpc-web":"1"},body:p});if(!f.ok){h(new o(i._fromValue(f.status)),null)}const b=await f.blob(),m=await new Promise((t,e)=>{const a=new FileReader;a.readAsDataURL(b),a.onloadend=()=>{t(a.result)},a.onerror=e});let g;if(m.startsWith("data:application/octet-stream;base64,"))g=r(m.split("data:application/octet-stream;base64,")[1]);else{if(!m.startsWith("data:application/grpc-web+proto;base64,"))throw new Error(`Expected response data to be base64 encode with a 'data:application/octet-stream;base64,' or 'data:application/grpc-web+proto;base64,' prefix, but found: ${m}`);g=r(m.split("data:application/grpc-web+proto;base64,")[1])}h(null,a(g.buffer,g.byteOffset,g.byteLength))}catch(t){if(t instanceof p)return void h(t,null);h(t,null)}}}}export{l as default};
|
|
2
2
|
//# sourceMappingURL=NativeChannel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeChannel.js","sources":["../../src/channel/NativeChannel.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport Channel, { encodeRequest, decodeUnaryResponse } from \"./Channel.js\";\nimport * as base64 from \"../encoding/base64.native.js\";\nimport HttpError from \"../http/HttpError.js\";\nimport HttpStatus from \"../http/HttpStatus.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\n\nexport default class NativeChannel extends Channel {\n /**\n * @param {string} address\n */\n constructor(address) {\n super();\n\n /**\n * @type {string}\n * @private\n */\n this._address = address;\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 try {\n const data = base64.encode(\n new Uint8Array(encodeRequest(requestData)),\n );\n\n const shouldUseHttps = !(\n this._address.includes(\"localhost\") ||\n this._address.includes(\"127.0.0.1\")\n );\n\n const address = shouldUseHttps\n ? `https://${this._address}`\n : `http://${this._address}`;\n // this will be executed in react native environment sho\n // fetch should be available\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-text\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-accept-content-transfer-encoding\": \"base64\",\n \"x-grpc-web\": \"1\",\n },\n body: data,\n },\n );\n\n if (!response.ok) {\n const error = new HttpError(\n HttpStatus._fromValue(response.status),\n );\n callback(error, null);\n }\n\n const blob = await response.blob();\n\n /** @type {string} */\n const responseData = await new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.readAsDataURL(blob);\n reader.onloadend = () => {\n resolve(/** @type {string} */ (reader.result));\n };\n reader.onerror = reject;\n });\n\n let responseBuffer;\n if (\n responseData.startsWith(\n \"data:application/octet-stream;base64,\",\n )\n ) {\n responseBuffer = base64.decode(\n responseData.split(\n \"data:application/octet-stream;base64,\",\n )[1],\n );\n } else if (\n responseData.startsWith(\n \"data:application/grpc-web+proto;base64,\",\n )\n ) {\n responseBuffer = base64.decode(\n responseData.split(\n \"data:application/grpc-web+proto;base64,\",\n )[1],\n );\n } else {\n throw new Error(\n `Expected response data to be base64 encode with a 'data:application/octet-stream;base64,' or 'data:application/grpc-web+proto;base64,' prefix, but found: ${responseData}`,\n );\n }\n\n const unaryResponse = decodeUnaryResponse(\n // @ts-ignore\n responseBuffer.buffer,\n responseBuffer.byteOffset,\n responseBuffer.byteLength,\n );\n\n callback(null, unaryResponse);\n } catch (error) {\n callback(/** @type {Error} */ (error), null);\n }\n };\n }\n}\n"],"names":["NativeChannel","Channel","constructor","address","super","this","_address","close","_createUnaryClient","serviceName","async","method","requestData","callback","data","base64.encode","Uint8Array","encodeRequest","includes","response","fetch","name","headers","SDK_NAME","SDK_VERSION","body","ok","HttpError","HttpStatus","_fromValue","status","blob","responseData","Promise","resolve","reject","reader","FileReader","readAsDataURL","onloadend","onerror","responseBuffer","startsWith","base64.decode","split","Error","decodeUnaryResponse","buffer","byteOffset","byteLength","error"],"mappings":"8QAOe,MAAMA,UAAsBC,EAIvC,WAAAC,CAAYC,GACRC,QAMAC,KAAKC,SAAWH,CACxB,CAMI,KAAAI,GAEJ,CAQI,kBAAAC,CAAmBC,GAEf,OAAOC,MAAOC,EAAQC,EAAaC,KAC/B,IACI,MAAMC,EAAOC,EACT,IAAIC,WAAWC,EAAcL,KAQ3BT,IAJFE,KAAKC,SAASY,SAAS,cACvBb,KAAKC,SAASY,SAAS,cAIrB,WAAWb,KAAKC,WAChB,UAAUD,KAAKC,WAIfa,QAAiBC,MACnB,GAAGjB,WAAiBM,KAAeE,EAAOU,OAC1C,CACIV,OAAQ,OACRW,QAAS,CACL,eAAgB,4BAChB,eAAgB,GAAGC,KAAYC,IAC/B,qCAAsC,SACtC,aAAc,KAElBC,KAAMX,IAId,IAAKK,EAASO,GAAI,CAIdb,EAHc,IAAIc,EACdC,EAAWC,WAAWV,EAASW,SAEnB,KACpC,CAEgB,MAAMC,QAAaZ,EAASY,OAGtBC,QAAqB,IAAIC,QAAQ,CAACC,EAASC,KAC7C,MAAMC,EAAS,IAAIC,WACnBD,EAAOE,cAAcP,GACrBK,EAAOG,UAAY,KACfL,EAA+BE,EAAa,SAEhDA,EAAOI,QAAUL,IAGrB,IAAIM,EACJ,GACIT,EAAaU,WACT,yCAGJD,EAAiBE,EACbX,EAAaY,MACT,yCACF,QAEH,KACHZ,EAAaU,WACT,2CASJ,MAAM,IAAIG,MACN,6JAA6Jb,KAPjKS,EAAiBE,EACbX,EAAaY,MACT,2CACF,GAM1B,CASgB/B,EAAS,KAPaiC,EAElBL,EAAeM,OACfN,EAAeO,WACfP,EAAeQ,YAItB,CAAC,MAAOC,GACLrC,EAAQ,EAA+B,KACvD,EAEA"}
|
|
1
|
+
{"version":3,"file":"NativeChannel.js","sources":["../../src/channel/NativeChannel.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport Channel, { encodeRequest, decodeUnaryResponse } from \"./Channel.js\";\nimport * as base64 from \"../encoding/base64.native.js\";\nimport HttpError from \"../http/HttpError.js\";\nimport HttpStatus from \"../http/HttpStatus.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\nimport GrpcServiceError from \"../grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"../grpc/GrpcStatus.js\";\n\nexport default class NativeChannel 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 /**\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 /**\n * @override\n * @returns {void}\n */\n close() {\n // do nothing\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 *\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 const shouldUseHttps = !(\n this._address.includes(\"localhost\") ||\n this._address.includes(\"127.0.0.1\")\n );\n\n const address = shouldUseHttps\n ? `https://${this._address}`\n : `http://${this._address}`;\n\n // Calculate remaining time until deadline\n const timeoutMs = deadline.getTime() - Date.now();\n if (timeoutMs <= 0) {\n throw new GrpcServiceError(GrpcStatus.Timeout);\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-text\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-grpc-web\": \"1\",\n },\n body: base64.encode(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) and has gRPC headers\n if (response.status === 200) {\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 or no gRPC headers present\n // This means the proxy might not be configured correctly or not running\n throw new GrpcServiceError(GrpcStatus.Unavailable);\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new GrpcServiceError(GrpcStatus.Timeout);\n }\n\n if (error instanceof GrpcServiceError) {\n throw error;\n }\n\n // Network error - server is not reachable\n throw new GrpcServiceError(GrpcStatus.Unavailable);\n }\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 const data = base64.encode(\n new Uint8Array(encodeRequest(requestData)),\n );\n\n const shouldUseHttps = !(\n this._address.includes(\"localhost\") ||\n this._address.includes(\"127.0.0.1\")\n );\n\n const address = shouldUseHttps\n ? `https://${this._address}`\n : `http://${this._address}`;\n // this will be executed in react native environment sho\n // fetch should be available\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-text\",\n \"x-user-agent\": `${SDK_NAME}/${SDK_VERSION}`,\n \"x-accept-content-transfer-encoding\": \"base64\",\n \"x-grpc-web\": \"1\",\n },\n body: data,\n },\n );\n\n if (!response.ok) {\n const error = new HttpError(\n HttpStatus._fromValue(response.status),\n );\n callback(error, null);\n }\n\n const blob = await response.blob();\n\n /** @type {string} */\n const responseData = await new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.readAsDataURL(blob);\n reader.onloadend = () => {\n resolve(/** @type {string} */ (reader.result));\n };\n reader.onerror = reject;\n });\n\n let responseBuffer;\n if (\n responseData.startsWith(\n \"data:application/octet-stream;base64,\",\n )\n ) {\n responseBuffer = base64.decode(\n responseData.split(\n \"data:application/octet-stream;base64,\",\n )[1],\n );\n } else if (\n responseData.startsWith(\n \"data:application/grpc-web+proto;base64,\",\n )\n ) {\n responseBuffer = base64.decode(\n responseData.split(\n \"data:application/grpc-web+proto;base64,\",\n )[1],\n );\n } else {\n throw new Error(\n `Expected response data to be base64 encode with a 'data:application/octet-stream;base64,' or 'data:application/grpc-web+proto;base64,' prefix, but found: ${responseData}`,\n );\n }\n\n const unaryResponse = decodeUnaryResponse(\n // @ts-ignore\n responseBuffer.buffer,\n responseBuffer.byteOffset,\n responseBuffer.byteLength,\n );\n\n callback(null, unaryResponse);\n } catch (error) {\n if (error instanceof GrpcServiceError) {\n callback(error, null);\n return;\n }\n\n callback(/** @type {Error} */ (error), null);\n }\n };\n }\n}\n"],"names":["NativeChannel","Channel","constructor","address","grpcDeadline","super","this","_address","_isReady","close","_waitForReady","deadline","includes","timeoutMs","getTime","Date","now","GrpcServiceError","GrpcStatus","Timeout","abortController","AbortController","timeoutId","setTimeout","abort","response","fetch","method","headers","SDK_NAME","SDK_VERSION","body","base64.encode","Uint8Array","signal","clearTimeout","status","grpcStatus","get","grpcMessage","Unavailable","error","Error","name","_createUnaryClient","serviceName","async","requestData","callback","milliseconds","_grpcDeadline","setMilliseconds","getMilliseconds","data","encodeRequest","ok","HttpError","HttpStatus","_fromValue","blob","responseData","Promise","resolve","reject","reader","FileReader","readAsDataURL","onloadend","onerror","responseBuffer","startsWith","base64.decode","split","decodeUnaryResponse","buffer","byteOffset","byteLength"],"mappings":"8VASe,MAAMA,UAAsBC,EAKvC,WAAAC,CAAYC,EAASC,GACjBC,MAAMD,GAMNE,KAAKC,SAAWJ,EAShBG,KAAKE,UAAW,CACxB,CAMI,KAAAC,GAEJ,CAYI,mBAAMC,CAAcC,GAEhB,GAAIL,KAAKE,SACL,OAGJ,MAKML,IAJFG,KAAKC,SAASK,SAAS,cACvBN,KAAKC,SAASK,SAAS,cAIrB,WAAWN,KAAKC,WAChB,UAAUD,KAAKC,WAGfM,EAAYF,EAASG,UAAYC,KAAKC,MAC5C,GAAIH,GAAa,EACb,MAAM,IAAII,EAAiBC,EAAWC,SAG1C,MAAMC,EAAkB,IAAIC,gBACtBC,EAAYC,WAAW,IAAMH,EAAgBI,QAASX,GAE5D,IAII,MAAMY,QAAiBC,MAAMvB,EAAS,CAClCwB,OAAQ,OACRC,QAAS,CACL,eAAgB,4BAChB,eAAgB,GAAGC,KAAYC,IAC/B,aAAc,KAElBC,KAAMC,EAAc,IAAIC,WAAW,IACnCC,OAAQd,EAAgBc,SAM5B,GAHAC,aAAab,GAGW,MAApBG,EAASW,OAAgB,CACzB,MAAMC,EAAaZ,EAASG,QAAQU,IAAI,eAClCC,EAAcd,EAASG,QAAQU,IAAI,gBAGzC,GAAkB,MAAdD,GAAqC,MAAfE,EAGtB,YADAjC,KAAKE,UAAW,EAGpC,CAIY,MAAM,IAAIS,EAAiBC,EAAWsB,YACzC,CAAC,MAAOC,GAGL,GAFAN,aAAab,GAETmB,aAAiBC,OAAwB,eAAfD,EAAME,KAChC,MAAM,IAAI1B,EAAiBC,EAAWC,SAG1C,GAAIsB,aAAiBxB,EACjB,MAAMwB,EAIV,MAAM,IAAIxB,EAAiBC,EAAWsB,YAClD,CACA,CAQI,kBAAAI,CAAmBC,GAEf,OAAOC,MAAOnB,EAAQoB,EAAaC,KAE/B,MAAMrC,EAAW,IAAII,KACfkC,EAAe3C,KAAK4C,cAE1BvC,EAASwC,gBAAgBxC,EAASyC,kBAAoBH,GAEtD,UAEU3C,KAAKI,cAAcC,GAEzB,MAAM0C,EAAOrB,EACT,IAAIC,WAAWqB,EAAcP,KAQ3B5C,IAJFG,KAAKC,SAASK,SAAS,cACvBN,KAAKC,SAASK,SAAS,cAIrB,WAAWN,KAAKC,WAChB,UAAUD,KAAKC,WAIfkB,QAAiBC,MACnB,GAAGvB,WAAiB0C,KAAelB,EAAOgB,OAC1C,CACIhB,OAAQ,OACRC,QAAS,CACL,eAAgB,4BAChB,eAAgB,GAAGC,KAAYC,IAC/B,qCAAsC,SACtC,aAAc,KAElBC,KAAMsB,IAId,IAAK5B,EAAS8B,GAAI,CAIdP,EAHc,IAAIQ,EACdC,EAAWC,WAAWjC,EAASW,SAEnB,KACpC,CAEgB,MAAMuB,QAAalC,EAASkC,OAGtBC,QAAqB,IAAIC,QAAQ,CAACC,EAASC,KAC7C,MAAMC,EAAS,IAAIC,WACnBD,EAAOE,cAAcP,GACrBK,EAAOG,UAAY,KACfL,EAA+BE,EAAa,SAEhDA,EAAOI,QAAUL,IAGrB,IAAIM,EACJ,GACIT,EAAaU,WACT,yCAGJD,EAAiBE,EACbX,EAAaY,MACT,yCACF,QAEH,KACHZ,EAAaU,WACT,2CASJ,MAAM,IAAI5B,MACN,6JAA6JkB,KAPjKS,EAAiBE,EACbX,EAAaY,MACT,2CACF,GAM1B,CASgBxB,EAAS,KAPayB,EAElBJ,EAAeK,OACfL,EAAeM,WACfN,EAAeO,YAItB,CAAC,MAAOnC,GACL,GAAIA,aAAiBxB,EAEjB,YADA+B,EAASP,EAAO,MAIpBO,EAAQ,EAA+B,KACvD,EAEA"}
|
|
@@ -20,15 +20,14 @@ class NodeChannel extends _Channel.default {
|
|
|
20
20
|
/**
|
|
21
21
|
* @internal
|
|
22
22
|
* @param {string} address
|
|
23
|
-
* @param {number=}
|
|
23
|
+
* @param {number=} grpcDeadline
|
|
24
24
|
*/
|
|
25
|
-
constructor(address,
|
|
26
|
-
super();
|
|
25
|
+
constructor(address, grpcDeadline) {
|
|
26
|
+
super(grpcDeadline);
|
|
27
27
|
|
|
28
28
|
/** @type {Client | null} */
|
|
29
29
|
this._client = null;
|
|
30
30
|
this.address = address;
|
|
31
|
-
this.maxExecutionTime = maxExecutionTime;
|
|
32
31
|
const {
|
|
33
32
|
ip,
|
|
34
33
|
port
|
|
@@ -144,7 +143,7 @@ class NodeChannel extends _Channel.default {
|
|
|
144
143
|
return (method, requestData, callback) => {
|
|
145
144
|
this._initializeClient().then(() => {
|
|
146
145
|
const deadline = new Date();
|
|
147
|
-
const milliseconds = this.
|
|
146
|
+
const milliseconds = this.grpcDeadline;
|
|
148
147
|
deadline.setMilliseconds(deadline.getMilliseconds() + milliseconds);
|
|
149
148
|
this._client?.waitForReady(deadline, err => {
|
|
150
149
|
if (err) {
|
|
@@ -2,13 +2,12 @@ export default class NodeChannel extends Channel {
|
|
|
2
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
* @param {string} address
|
|
5
|
-
* @param {number=}
|
|
5
|
+
* @param {number=} grpcDeadline
|
|
6
6
|
*/
|
|
7
|
-
constructor(address: string,
|
|
7
|
+
constructor(address: string, grpcDeadline?: number | undefined);
|
|
8
8
|
/** @type {Client | null} */
|
|
9
9
|
_client: Client | null;
|
|
10
10
|
address: string;
|
|
11
|
-
maxExecutionTime: number | undefined;
|
|
12
11
|
nodeIp: string;
|
|
13
12
|
nodePort: string;
|
|
14
13
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"tls";import{credentials as t,Client as r,Metadata as
|
|
1
|
+
import e from"tls";import{credentials as t,Client as r,Metadata as s}from"@grpc/grpc-js";import i from"./Channel.js";import n from"../grpc/GrpcServiceError.js";import o from"../grpc/GrpcStatus.js";import{ALL_NETWORK_IPS as c}from"../constants/ClientConstants.js";import{SDK_NAME as a,SDK_VERSION as l}from"../version.js";const d={};class p extends i{constructor(e,t){super(t),this._client=null,this.address=e;const{ip:r,port:s}=this.parseAddress(e);this.nodeIp=r,this.nodePort=s}bytesToPem(e){const t=e.toString("base64");return`-----BEGIN CERTIFICATE-----\n${t.match(/.{1,64}/g)?.join("\n")||""}\n-----END CERTIFICATE-----`}parseAddress(e){const[t,r]=e.split(":");if(!t||!r)throw new Error("Invalid address format. Expected format: 'IP:Port'");return{ip:t,port:r}}async _retrieveCertificate(){return new Promise((t,r)=>{const s=e.connect({host:this.nodeIp,port:Number(this.nodePort),rejectUnauthorized:!1},()=>{try{const e=s.getPeerCertificate();e&&e.raw?t(this.bytesToPem(e.raw)):r(new Error("No certificate retrieved."))}catch(e){r(e)}finally{s.end()}});s.on("error",r)})}async _initializeClient(){if(d[this.address])return void(this._client=d[this.address]);let e;if("50212"===this.nodePort){const r=Buffer.from(await this._retrieveCertificate());e=t.createSsl(r)}else e=t.createInsecure();this._client=new r(this.address,e,{"grpc.ssl_target_name_override":"127.0.0.1","grpc.default_authority":"127.0.0.1","grpc.http_connect_creds":"0","grpc.keepalive_time_ms":1e5,"grpc.keepalive_timeout_ms":1e4,"grpc.keepalive_permit_without_calls":1,"grpc.enable_retries":1}),d[this.address]=this._client}close(){this._client&&(this._client.close(),delete d[this.address])}_createUnaryClient(e){return(t,r,i)=>{this._initializeClient().then(()=>{const d=new Date,p=this.grpcDeadline;d.setMilliseconds(d.getMilliseconds()+p),this._client?.waitForReady(d,d=>{if(d)i(new n(o.Timeout,c[`${this.nodeIp}:`]));else{const n=new s;n.set("x-user-agent",`${a}/${l}`),this._client?.makeUnaryRequest(`/proto.${e}/${t.name}`,e=>e,e=>e,Buffer.from(r),n,(e,t)=>{i(e,t)})}})}).catch(e=>{e instanceof Error?i(e):i(new Error("An unexpected error occurred"))})}}}export{p as default};
|
|
2
2
|
//# sourceMappingURL=NodeChannel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeChannel.js","sources":["../../src/channel/NodeChannel.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport tls from \"tls\";\nimport { Client, credentials, Metadata } from \"@grpc/grpc-js\";\nimport Channel from \"./Channel.js\";\nimport GrpcServicesError from \"../grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"../grpc/GrpcStatus.js\";\nimport { ALL_NETWORK_IPS } from \"../constants/ClientConstants.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\n\n/** @type {{ [key: string]: Client }} */\nconst clientCache = {};\n\nexport default class NodeChannel extends Channel {\n /**\n * @internal\n * @param {string} address\n * @param {number=}
|
|
1
|
+
{"version":3,"file":"NodeChannel.js","sources":["../../src/channel/NodeChannel.js"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport tls from \"tls\";\nimport { Client, credentials, Metadata } from \"@grpc/grpc-js\";\nimport Channel from \"./Channel.js\";\nimport GrpcServicesError from \"../grpc/GrpcServiceError.js\";\nimport GrpcStatus from \"../grpc/GrpcStatus.js\";\nimport { ALL_NETWORK_IPS } from \"../constants/ClientConstants.js\";\nimport { SDK_NAME, SDK_VERSION } from \"../version.js\";\n\n/** @type {{ [key: string]: Client }} */\nconst clientCache = {};\n\nexport default class NodeChannel extends Channel {\n /**\n * @internal\n * @param {string} address\n * @param {number=} grpcDeadline\n */\n constructor(address, grpcDeadline) {\n super(grpcDeadline);\n\n /** @type {Client | null} */\n this._client = null;\n\n this.address = address;\n\n const { ip, port } = this.parseAddress(address);\n this.nodeIp = ip;\n this.nodePort = port;\n }\n\n /**\n * Convert certificate bytes to PEM format\n * @param {Buffer} certBytes\n * @returns {string}\n */\n bytesToPem(certBytes) {\n const base64Cert = certBytes.toString(\"base64\");\n const lines = base64Cert.match(/.{1,64}/g)?.join(\"\\n\") || \"\";\n return `-----BEGIN CERTIFICATE-----\\n${lines}\\n-----END CERTIFICATE-----`;\n }\n\n /**\n * Validates and parses an address in the \"IP:Port\" format.\n * @param {string} address\n * @returns {{ ip: string, port: string }}\n */\n parseAddress(address) {\n const [ip, port] = address.split(\":\");\n if (!ip || !port) {\n throw new Error(\n \"Invalid address format. Expected format: 'IP:Port'\",\n );\n }\n return { ip, port };\n }\n\n /**\n * Retrieve the server's certificate dynamically.\n * @returns {Promise<string>}\n */\n async _retrieveCertificate() {\n return new Promise((resolve, reject) => {\n const socket = tls.connect(\n {\n host: this.nodeIp,\n port: Number(this.nodePort),\n rejectUnauthorized: false,\n },\n () => {\n try {\n const cert = socket.getPeerCertificate();\n\n if (cert && cert.raw) {\n resolve(this.bytesToPem(cert.raw));\n } else {\n reject(new Error(\"No certificate retrieved.\"));\n }\n } catch (err) {\n reject(err);\n } finally {\n socket.end();\n }\n },\n );\n\n socket.on(\"error\", reject);\n });\n }\n\n /**\n * Initialize the gRPC client\n * @returns {Promise<void>}\n */\n async _initializeClient() {\n if (clientCache[this.address]) {\n this._client = clientCache[this.address];\n return;\n }\n\n let security;\n const options = {\n \"grpc.ssl_target_name_override\": \"127.0.0.1\",\n \"grpc.default_authority\": \"127.0.0.1\",\n \"grpc.http_connect_creds\": \"0\",\n \"grpc.keepalive_time_ms\": 100000,\n \"grpc.keepalive_timeout_ms\": 10000,\n \"grpc.keepalive_permit_without_calls\": 1,\n \"grpc.enable_retries\": 1,\n };\n\n // If the port is 50212, use TLS\n if (this.nodePort === \"50212\") {\n const certificate = Buffer.from(await this._retrieveCertificate());\n\n security = credentials.createSsl(certificate);\n } else {\n security = credentials.createInsecure();\n }\n\n this._client = new Client(this.address, security, options);\n\n clientCache[this.address] = this._client;\n }\n\n /**\n * @override\n * @returns {void}\n */\n close() {\n if (this._client) {\n this._client.close();\n delete clientCache[this.address];\n }\n }\n\n /**\n * @override\n * @protected\n * @param {string} serviceName\n * @returns {import(\"protobufjs\").RPCImpl}\n */\n _createUnaryClient(serviceName) {\n return (method, requestData, callback) => {\n this._initializeClient()\n .then(() => {\n const deadline = new Date();\n const milliseconds = this.grpcDeadline;\n deadline.setMilliseconds(\n deadline.getMilliseconds() + milliseconds,\n );\n\n this._client?.waitForReady(deadline, (err) => {\n if (err) {\n callback(\n new GrpcServicesError(\n GrpcStatus.Timeout,\n // Added colons to the IP address to resolve a SonarCloud IP issue.\n ALL_NETWORK_IPS[`${this.nodeIp}:`],\n ),\n );\n } else {\n // Create metadata with user agent\n const metadata = new Metadata();\n\n metadata.set(\n \"x-user-agent\",\n `${SDK_NAME}/${SDK_VERSION}`,\n );\n\n this._client?.makeUnaryRequest(\n `/proto.${serviceName}/${method.name}`,\n (value) => value,\n (value) => value,\n Buffer.from(requestData),\n metadata,\n (e, r) => {\n callback(e, r);\n },\n );\n }\n });\n })\n .catch((err) => {\n if (err instanceof Error) {\n callback(err);\n } else {\n callback(new Error(\"An unexpected error occurred\"));\n }\n });\n };\n }\n}\n"],"names":["clientCache","NodeChannel","Channel","constructor","address","grpcDeadline","super","this","_client","ip","port","parseAddress","nodeIp","nodePort","bytesToPem","certBytes","base64Cert","toString","match","join","split","Error","_retrieveCertificate","Promise","resolve","reject","socket","tls","connect","host","Number","rejectUnauthorized","cert","getPeerCertificate","raw","err","end","on","_initializeClient","security","certificate","Buffer","from","credentials","createSsl","createInsecure","Client","close","_createUnaryClient","serviceName","method","requestData","callback","then","deadline","Date","milliseconds","setMilliseconds","getMilliseconds","waitForReady","GrpcServicesError","GrpcStatus","Timeout","ALL_NETWORK_IPS","metadata","Metadata","set","SDK_NAME","SDK_VERSION","makeUnaryRequest","name","value","e","r","catch"],"mappings":"iUAUA,MAAMA,EAAc,CAAE,EAEP,MAAMC,UAAoBC,EAMrC,WAAAC,CAAYC,EAASC,GACjBC,MAAMD,GAGNE,KAAKC,QAAU,KAEfD,KAAKH,QAAUA,EAEf,MAAMK,GAAEA,EAAEC,KAAEA,GAASH,KAAKI,aAAaP,GACvCG,KAAKK,OAASH,EACdF,KAAKM,SAAWH,CACxB,CAOI,UAAAI,CAAWC,GACP,MAAMC,EAAaD,EAAUE,SAAS,UAEtC,MAAO,gCADOD,EAAWE,MAAM,aAAaC,KAAK,OAAS,+BAElE,CAOI,YAAAR,CAAaP,GACT,MAAOK,EAAIC,GAAQN,EAAQgB,MAAM,KACjC,IAAKX,IAAOC,EACR,MAAM,IAAIW,MACN,sDAGR,MAAO,CAAEZ,KAAIC,OACrB,CAMI,0BAAMY,GACF,OAAO,IAAIC,QAAQ,CAACC,EAASC,KACzB,MAAMC,EAASC,EAAIC,QACf,CACIC,KAAMtB,KAAKK,OACXF,KAAMoB,OAAOvB,KAAKM,UAClBkB,oBAAoB,GAExB,KACI,IACI,MAAMC,EAAON,EAAOO,qBAEhBD,GAAQA,EAAKE,IACbV,EAAQjB,KAAKO,WAAWkB,EAAKE,MAE7BT,EAAO,IAAIJ,MAAM,6BAExB,CAAC,MAAOc,GACLV,EAAOU,EAC/B,CAA8B,QACNT,EAAOU,KAC/B,IAIYV,EAAOW,GAAG,QAASZ,IAE/B,CAMI,uBAAMa,GACF,GAAItC,EAAYO,KAAKH,SAEjB,YADAG,KAAKC,QAAUR,EAAYO,KAAKH,UAIpC,IAAImC,EAYJ,GAAsB,UAAlBhC,KAAKM,SAAsB,CAC3B,MAAM2B,EAAcC,OAAOC,WAAWnC,KAAKe,wBAE3CiB,EAAWI,EAAYC,UAAUJ,EAC7C,MACYD,EAAWI,EAAYE,iBAG3BtC,KAAKC,QAAU,IAAIsC,EAAOvC,KAAKH,QAASmC,EAnBxB,CACZ,gCAAiC,YACjC,yBAA0B,YAC1B,0BAA2B,IAC3B,yBAA0B,IAC1B,4BAA6B,IAC7B,sCAAuC,EACvC,sBAAuB,IAc3BvC,EAAYO,KAAKH,SAAWG,KAAKC,OACzC,CAMI,KAAAuC,GACQxC,KAAKC,UACLD,KAAKC,QAAQuC,eACN/C,EAAYO,KAAKH,SAEpC,CAQI,kBAAA4C,CAAmBC,GACf,MAAO,CAACC,EAAQC,EAAaC,KACzB7C,KAAK+B,oBACAe,KAAK,KACF,MAAMC,EAAW,IAAIC,KACfC,EAAejD,KAAKF,aAC1BiD,EAASG,gBACLH,EAASI,kBAAoBF,GAGjCjD,KAAKC,SAASmD,aAAaL,EAAWnB,IAClC,GAAIA,EACAiB,EACI,IAAIQ,EACAC,EAAWC,QAEXC,EAAgB,GAAGxD,KAAKK,iBAG7B,CAEH,MAAMoD,EAAW,IAAIC,EAErBD,EAASE,IACL,eACA,GAAGC,KAAYC,KAGnB7D,KAAKC,SAAS6D,iBACV,UAAUpB,KAAeC,EAAOoB,OAC/BC,GAAUA,EACVA,GAAUA,EACX9B,OAAOC,KAAKS,GACZa,EACA,CAACQ,EAAGC,KACArB,EAASoB,EAAGC,IAGhD,MAGiBC,MAAOvC,IACAA,aAAed,MACf+B,EAASjB,GAETiB,EAAS,IAAI/B,MAAM,mCAI3C"}
|
|
@@ -18,15 +18,161 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
18
18
|
class WebChannel extends _Channel.default {
|
|
19
19
|
/**
|
|
20
20
|
* @param {string} address
|
|
21
|
+
* @param {number=} grpcDeadline
|
|
21
22
|
*/
|
|
22
|
-
constructor(address) {
|
|
23
|
-
super();
|
|
23
|
+
constructor(address, grpcDeadline) {
|
|
24
|
+
super(grpcDeadline);
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* @type {string}
|
|
27
28
|
* @private
|
|
28
29
|
*/
|
|
29
30
|
this._address = address;
|
|
31
|
+
|
|
32
|
+
// Set the gRPC deadline using the base class method
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Flag indicating if the connection is ready (health check has passed)
|
|
36
|
+
* Set to true after the first successful health check
|
|
37
|
+
*
|
|
38
|
+
* @type {boolean}
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
this._isReady = false;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Promise that resolves when the health check is complete
|
|
45
|
+
* Used to prevent multiple concurrent health checks
|
|
46
|
+
*
|
|
47
|
+
* @type {Promise<void>|null}
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
this._healthCheckPromise = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Determines whether to use HTTPS based on the address
|
|
55
|
+
* @param {string} address - The address to check
|
|
56
|
+
* @returns {boolean} - True if HTTPS should be used, false for HTTP
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
_shouldUseHttps(address) {
|
|
60
|
+
return !(address.includes("localhost") || address.includes("127.0.0.1"));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Builds the full URL with appropriate scheme (http/https)
|
|
65
|
+
* @param {string} address - The base address
|
|
66
|
+
* @returns {string} - The full URL with scheme
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
_buildUrl(address) {
|
|
70
|
+
// Check if address already contains a scheme
|
|
71
|
+
const hasScheme = address.startsWith("http://") || address.startsWith("https://");
|
|
72
|
+
if (hasScheme) {
|
|
73
|
+
// Use the address as-is if it already has a scheme
|
|
74
|
+
return address;
|
|
75
|
+
} else {
|
|
76
|
+
// Only prepend scheme if none exists
|
|
77
|
+
const shouldUseHttps = this._shouldUseHttps(address);
|
|
78
|
+
return shouldUseHttps ? `https://${address}` : `http://${address}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Check if the gRPC-Web proxy is reachable and healthy
|
|
84
|
+
* Performs a POST request and verifies the response has gRPC-Web headers,
|
|
85
|
+
* which indicates the proxy is running and processing gRPC requests.
|
|
86
|
+
* Results are cached per address for the entire lifecycle.
|
|
87
|
+
* Uses promise-based synchronization to prevent multiple concurrent health checks.
|
|
88
|
+
*
|
|
89
|
+
* @param {Date} deadline - Deadline for the health check
|
|
90
|
+
* @returns {Promise<void>}
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
async _waitForReady(deadline) {
|
|
94
|
+
// Check if we've already validated this address
|
|
95
|
+
if (this._isReady) {
|
|
96
|
+
return; // Health check already passed for this address
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// If a health check is already in progress, wait for it to complete
|
|
100
|
+
if (this._healthCheckPromise) {
|
|
101
|
+
return this._healthCheckPromise;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Start a new health check and store the promise
|
|
105
|
+
this._healthCheckPromise = this._performHealthCheck(deadline);
|
|
106
|
+
try {
|
|
107
|
+
await this._healthCheckPromise;
|
|
108
|
+
} finally {
|
|
109
|
+
// Clear the promise when done (success or failure)
|
|
110
|
+
this._healthCheckPromise = null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Performs the actual health check request
|
|
116
|
+
* @param {Date} deadline - Deadline for the health check
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
async _performHealthCheck(deadline) {
|
|
121
|
+
const address = this._buildUrl(this._address);
|
|
122
|
+
|
|
123
|
+
// Calculate remaining time until deadline
|
|
124
|
+
const timeoutMs = deadline.getTime() - Date.now();
|
|
125
|
+
if (timeoutMs <= 0) {
|
|
126
|
+
throw new _GrpcServiceError.default(_GrpcStatus.default.Timeout, _ClientConstants.ALL_WEB_NETWORK_NODES?.[this._address]?.toString());
|
|
127
|
+
}
|
|
128
|
+
const abortController = new AbortController();
|
|
129
|
+
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
130
|
+
try {
|
|
131
|
+
// Make a POST request to verify the gRPC-Web proxy is running
|
|
132
|
+
// We use a minimal gRPC-Web compatible request
|
|
133
|
+
//eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
134
|
+
const response = await fetch(address, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: {
|
|
137
|
+
"content-type": "application/grpc-web+proto",
|
|
138
|
+
"x-user-agent": `${_version.SDK_NAME}/${_version.SDK_VERSION}`,
|
|
139
|
+
"x-grpc-web": "1"
|
|
140
|
+
},
|
|
141
|
+
body: new Uint8Array(0),
|
|
142
|
+
// Empty body for health check
|
|
143
|
+
signal: abortController.signal
|
|
144
|
+
});
|
|
145
|
+
clearTimeout(timeoutId);
|
|
146
|
+
|
|
147
|
+
// Check if response is successful (200) or indicates a redirect (3xx)
|
|
148
|
+
// 3xx status codes indicate the resource has moved, which is valid for proxies
|
|
149
|
+
if (response.ok || response.status >= 300 && response.status < 400) {
|
|
150
|
+
const grpcStatus = response.headers.get("grpc-status");
|
|
151
|
+
const grpcMessage = response.headers.get("grpc-message");
|
|
152
|
+
|
|
153
|
+
// If gRPC headers exist, the proxy is running and processing requests
|
|
154
|
+
if (grpcStatus != null || grpcMessage != null) {
|
|
155
|
+
// Mark this connection as ready
|
|
156
|
+
this._isReady = true;
|
|
157
|
+
return; // Healthy - gRPC-Web proxy is responding
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If we get here, either status isn't 200/3xx or no gRPC headers present
|
|
162
|
+
// This means the proxy might not be configured correctly or not running
|
|
163
|
+
throw new _GrpcServiceError.default(_GrpcStatus.default.Unavailable, _ClientConstants.ALL_WEB_NETWORK_NODES?.[this._address]?.toString());
|
|
164
|
+
} catch (error) {
|
|
165
|
+
clearTimeout(timeoutId);
|
|
166
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
167
|
+
throw new _GrpcServiceError.default(_GrpcStatus.default.Timeout, _ClientConstants.ALL_WEB_NETWORK_NODES?.[this._address]?.toString());
|
|
168
|
+
}
|
|
169
|
+
if (error instanceof _GrpcServiceError.default) {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Network error - server is not reachable
|
|
174
|
+
throw new _GrpcServiceError.default(_GrpcStatus.default.Unavailable, _ClientConstants.ALL_WEB_NETWORK_NODES?.[this._address]?.toString());
|
|
175
|
+
}
|
|
30
176
|
}
|
|
31
177
|
|
|
32
178
|
/**
|
|
@@ -46,18 +192,16 @@ class WebChannel extends _Channel.default {
|
|
|
46
192
|
_createUnaryClient(serviceName) {
|
|
47
193
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
48
194
|
return async (method, requestData, callback) => {
|
|
195
|
+
// Calculate deadline for connection check
|
|
196
|
+
const deadline = new Date();
|
|
197
|
+
const milliseconds = this._grpcDeadline;
|
|
198
|
+
deadline.setMilliseconds(deadline.getMilliseconds() + milliseconds);
|
|
49
199
|
try {
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
address = this._address;
|
|
56
|
-
} else {
|
|
57
|
-
// Only prepend scheme if none exists
|
|
58
|
-
const shouldUseHttps = !(this._address.includes("localhost") || this._address.includes("127.0.0.1"));
|
|
59
|
-
address = shouldUseHttps ? `https://${this._address}` : `http://${this._address}`;
|
|
60
|
-
}
|
|
200
|
+
// Wait for connection to be ready (similar to gRPC waitForReady)
|
|
201
|
+
await this._waitForReady(deadline);
|
|
202
|
+
|
|
203
|
+
// Build the full URL with appropriate scheme
|
|
204
|
+
const address = this._buildUrl(this._address);
|
|
61
205
|
// this will be executed in a browser environment so eslint is
|
|
62
206
|
// disabled for the fetch call
|
|
63
207
|
//eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
@@ -73,6 +217,7 @@ class WebChannel extends _Channel.default {
|
|
|
73
217
|
if (!response.ok) {
|
|
74
218
|
const error = new _HttpError.default(_HttpStatus.default._fromValue(response.status));
|
|
75
219
|
callback(error, null);
|
|
220
|
+
return;
|
|
76
221
|
}
|
|
77
222
|
|
|
78
223
|
// Check headers for gRPC errors
|
|
@@ -82,11 +227,16 @@ class WebChannel extends _Channel.default {
|
|
|
82
227
|
const error = new _GrpcServiceError.default(_GrpcStatus.default._fromValue(parseInt(grpcStatus)), _ClientConstants.ALL_WEB_NETWORK_NODES?.[this._address]?.toString());
|
|
83
228
|
error.message = grpcMessage;
|
|
84
229
|
callback(error, null);
|
|
230
|
+
return;
|
|
85
231
|
}
|
|
86
232
|
const responseBuffer = await response.arrayBuffer();
|
|
87
233
|
const unaryResponse = (0, _Channel.decodeUnaryResponse)(responseBuffer);
|
|
88
234
|
callback(null, unaryResponse);
|
|
89
235
|
} catch (error) {
|
|
236
|
+
if (error instanceof _GrpcServiceError.default) {
|
|
237
|
+
callback(error, null);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
90
240
|
const err = new _GrpcServiceError.default(
|
|
91
241
|
// retry on grpc web errors
|
|
92
242
|
_GrpcStatus.default._fromValue(18), _ClientConstants.ALL_WEB_NETWORK_NODES?.[this._address]?.toString());
|
|
@@ -1,12 +1,62 @@
|
|
|
1
1
|
export default class WebChannel extends Channel {
|
|
2
2
|
/**
|
|
3
3
|
* @param {string} address
|
|
4
|
+
* @param {number=} grpcDeadline
|
|
4
5
|
*/
|
|
5
|
-
constructor(address: string);
|
|
6
|
+
constructor(address: string, grpcDeadline?: number | undefined);
|
|
6
7
|
/**
|
|
7
8
|
* @type {string}
|
|
8
9
|
* @private
|
|
9
10
|
*/
|
|
10
11
|
private _address;
|
|
12
|
+
/**
|
|
13
|
+
* Flag indicating if the connection is ready (health check has passed)
|
|
14
|
+
* Set to true after the first successful health check
|
|
15
|
+
*
|
|
16
|
+
* @type {boolean}
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
private _isReady;
|
|
20
|
+
/**
|
|
21
|
+
* Promise that resolves when the health check is complete
|
|
22
|
+
* Used to prevent multiple concurrent health checks
|
|
23
|
+
*
|
|
24
|
+
* @type {Promise<void>|null}
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
private _healthCheckPromise;
|
|
28
|
+
/**
|
|
29
|
+
* Determines whether to use HTTPS based on the address
|
|
30
|
+
* @param {string} address - The address to check
|
|
31
|
+
* @returns {boolean} - True if HTTPS should be used, false for HTTP
|
|
32
|
+
* @private
|
|
33
|
+
*/
|
|
34
|
+
private _shouldUseHttps;
|
|
35
|
+
/**
|
|
36
|
+
* Builds the full URL with appropriate scheme (http/https)
|
|
37
|
+
* @param {string} address - The base address
|
|
38
|
+
* @returns {string} - The full URL with scheme
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
private _buildUrl;
|
|
42
|
+
/**
|
|
43
|
+
* Check if the gRPC-Web proxy is reachable and healthy
|
|
44
|
+
* Performs a POST request and verifies the response has gRPC-Web headers,
|
|
45
|
+
* which indicates the proxy is running and processing gRPC requests.
|
|
46
|
+
* Results are cached per address for the entire lifecycle.
|
|
47
|
+
* Uses promise-based synchronization to prevent multiple concurrent health checks.
|
|
48
|
+
*
|
|
49
|
+
* @param {Date} deadline - Deadline for the health check
|
|
50
|
+
* @returns {Promise<void>}
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
private _waitForReady;
|
|
54
|
+
/**
|
|
55
|
+
* Performs the actual health check request
|
|
56
|
+
* @param {Date} deadline - Deadline for the health check
|
|
57
|
+
* @returns {Promise<void>}
|
|
58
|
+
* @private
|
|
59
|
+
*/
|
|
60
|
+
private _performHealthCheck;
|
|
11
61
|
}
|
|
12
62
|
import Channel from "./Channel.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ALL_WEB_NETWORK_NODES as t}from"../constants/ClientConstants.js";import
|
|
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};
|
|
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 */\n constructor(address) {\n super();\n\n /**\n * @type {string}\n * @private\n */\n this._address = address;\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 try {\n // Check if address already contains a scheme\n const hasScheme =\n this._address.startsWith(\"http://\") ||\n this._address.startsWith(\"https://\");\n\n let address;\n if (hasScheme) {\n // Use the address as-is if it already has a scheme\n address = this._address;\n } else {\n // Only prepend scheme if none exists\n const shouldUseHttps = !(\n this._address.includes(\"localhost\") ||\n this._address.includes(\"127.0.0.1\")\n );\n\n address = shouldUseHttps\n ? `https://${this._address}`\n : `http://${this._address}`;\n }\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 }\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 }\n\n const responseBuffer = await response.arrayBuffer();\n const unaryResponse = decodeUnaryResponse(responseBuffer);\n\n callback(null, unaryResponse);\n } catch (error) {\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","super","this","_address","close","_createUnaryClient","serviceName","async","method","requestData","callback","startsWith","includes","response","fetch","name","headers","SDK_NAME","SDK_VERSION","body","encodeRequest","ok","HttpError","HttpStatus","_fromValue","status","grpcStatus","get","grpcMessage","error","GrpcServiceError","GrpcStatus","parseInt","ALL_WEB_NETWORK_NODES","toString","message","responseBuffer","arrayBuffer","decodeUnaryResponse"],"mappings":"oWASe,MAAMA,UAAmBC,EAIpC,WAAAC,CAAYC,GACRC,QAMAC,KAAKC,SAAWH,CACxB,CAMI,KAAAI,GAEJ,CAQI,kBAAAC,CAAmBC,GAEf,OAAOC,MAAOC,EAAQC,EAAaC,KAC/B,IAMI,IAAIV,EACJ,GAJIE,KAAKC,SAASQ,WAAW,YACzBT,KAAKC,SAASQ,WAAW,YAKzBX,EAAUE,KAAKC,aACZ,CAOHH,IAJIE,KAAKC,SAASS,SAAS,cACvBV,KAAKC,SAASS,SAAS,cAIrB,WAAWV,KAAKC,WAChB,UAAUD,KAAKC,UACzC,CAIgB,MAAMU,QAAiBC,MACnB,GAAGd,WAAiBM,KAAeE,EAAOO,OAC1C,CACIP,OAAQ,OACRQ,QAAS,CACL,eAAgB,6BAChB,eAAgB,GAAGC,KAAYC,IAC/B,aAAc,KAElBC,KAAMC,EAAcX,KAI5B,IAAKI,EAASQ,GAAI,CAIdX,EAHc,IAAIY,EACdC,EAAWC,WAAWX,EAASY,SAEnB,KACpC,CAGgB,MAAMC,EAAab,EAASG,QAAQW,IAAI,eAClCC,EAAcf,EAASG,QAAQW,IAAI,gBAEzC,GAAkB,MAAdD,GAAqC,MAAfE,EAAqB,CAC3C,MAAMC,EAAQ,IAAIC,EACdC,EAAWP,WAAWQ,SAASN,IAC/BO,IAAwB/B,KAAKC,WAAW+B,YAE5CL,EAAMM,QAAUP,EAChBlB,EAASmB,EAAO,KACpC,CAEgB,MAAMO,QAAuBvB,EAASwB,cAGtC3B,EAAS,KAFa4B,EAAoBF,GAG7C,CAAC,MAAOP,GAMLnB,EALY,IAAIoB,EAEZC,EAAWP,WAAW,IACtBS,IAAwB/B,KAAKC,WAAW+B,YAE9B,KAC9B,EAEA"}
|
|
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":"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,cAAgBR,EAAQQ,SAAS,aAE9D,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"}
|