@milaboratories/pl-client 2.16.26 → 2.16.28
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/README.md +2 -1
- package/dist/core/PromiseTracker.cjs +1 -3
- package/dist/core/PromiseTracker.cjs.map +1 -1
- package/dist/core/PromiseTracker.d.ts.map +1 -1
- package/dist/core/PromiseTracker.js +1 -3
- package/dist/core/PromiseTracker.js.map +1 -1
- package/dist/core/StatefulPromise.cjs +4 -4
- package/dist/core/StatefulPromise.cjs.map +1 -1
- package/dist/core/StatefulPromise.d.ts +1 -1
- package/dist/core/StatefulPromise.d.ts.map +1 -1
- package/dist/core/StatefulPromise.js +4 -4
- package/dist/core/StatefulPromise.js.map +1 -1
- package/dist/core/advisory_locks.cjs +1 -1
- package/dist/core/advisory_locks.cjs.map +1 -1
- package/dist/core/advisory_locks.js +1 -1
- package/dist/core/advisory_locks.js.map +1 -1
- package/dist/core/auth.cjs.map +1 -1
- package/dist/core/auth.d.ts +1 -1
- package/dist/core/auth.js.map +1 -1
- package/dist/core/cache.d.ts +1 -1
- package/dist/core/client.cjs +14 -14
- package/dist/core/client.cjs.map +1 -1
- package/dist/core/client.d.ts +11 -11
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +14 -14
- package/dist/core/client.js.map +1 -1
- package/dist/core/config.cjs +39 -39
- package/dist/core/config.cjs.map +1 -1
- package/dist/core/config.d.ts +5 -5
- package/dist/core/config.js +39 -39
- package/dist/core/config.js.map +1 -1
- package/dist/core/default_client.cjs +23 -23
- package/dist/core/default_client.cjs.map +1 -1
- package/dist/core/default_client.d.ts +3 -3
- package/dist/core/default_client.js +23 -23
- package/dist/core/default_client.js.map +1 -1
- package/dist/core/driver.cjs +1 -1
- package/dist/core/driver.cjs.map +1 -1
- package/dist/core/driver.d.ts +5 -5
- package/dist/core/driver.js +1 -1
- package/dist/core/driver.js.map +1 -1
- package/dist/core/error_resource.cjs +2 -2
- package/dist/core/error_resource.cjs.map +1 -1
- package/dist/core/error_resource.d.ts +1 -1
- package/dist/core/error_resource.js +2 -2
- package/dist/core/error_resource.js.map +1 -1
- package/dist/core/errors.cjs +24 -24
- package/dist/core/errors.cjs.map +1 -1
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +24 -24
- package/dist/core/errors.js.map +1 -1
- package/dist/core/final.cjs +43 -43
- package/dist/core/final.cjs.map +1 -1
- package/dist/core/final.d.ts +3 -3
- package/dist/core/final.d.ts.map +1 -1
- package/dist/core/final.js +43 -43
- package/dist/core/final.js.map +1 -1
- package/dist/core/ll_client.cjs +50 -43
- package/dist/core/ll_client.cjs.map +1 -1
- package/dist/core/ll_client.d.ts +9 -9
- package/dist/core/ll_client.d.ts.map +1 -1
- package/dist/core/ll_client.js +50 -43
- package/dist/core/ll_client.js.map +1 -1
- package/dist/core/ll_transaction.cjs +9 -9
- package/dist/core/ll_transaction.cjs.map +1 -1
- package/dist/core/ll_transaction.d.ts +7 -7
- package/dist/core/ll_transaction.d.ts.map +1 -1
- package/dist/core/ll_transaction.js +9 -9
- package/dist/core/ll_transaction.js.map +1 -1
- package/dist/core/stat.cjs.map +1 -1
- package/dist/core/stat.d.ts +1 -1
- package/dist/core/stat.js.map +1 -1
- package/dist/core/transaction.cjs +46 -46
- package/dist/core/transaction.cjs.map +1 -1
- package/dist/core/transaction.d.ts +7 -7
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +46 -46
- package/dist/core/transaction.js.map +1 -1
- package/dist/core/type_conversion.cjs +22 -22
- package/dist/core/type_conversion.cjs.map +1 -1
- package/dist/core/type_conversion.d.ts +3 -3
- package/dist/core/type_conversion.d.ts.map +1 -1
- package/dist/core/type_conversion.js +22 -22
- package/dist/core/type_conversion.js.map +1 -1
- package/dist/core/types.cjs +25 -25
- package/dist/core/types.cjs.map +1 -1
- package/dist/core/types.d.ts +7 -7
- package/dist/core/types.js +25 -25
- package/dist/core/types.js.map +1 -1
- package/dist/core/unauth_client.cjs +6 -4
- package/dist/core/unauth_client.cjs.map +1 -1
- package/dist/core/unauth_client.d.ts +4 -4
- package/dist/core/unauth_client.d.ts.map +1 -1
- package/dist/core/unauth_client.js +6 -4
- package/dist/core/unauth_client.js.map +1 -1
- package/dist/core/websocket_stream.cjs +22 -20
- package/dist/core/websocket_stream.cjs.map +1 -1
- package/dist/core/websocket_stream.d.ts +3 -3
- package/dist/core/websocket_stream.d.ts.map +1 -1
- package/dist/core/websocket_stream.js +22 -20
- package/dist/core/websocket_stream.js.map +1 -1
- package/dist/core/wire.d.ts +6 -6
- package/dist/core/wire.d.ts.map +1 -1
- package/dist/helpers/pl.cjs +19 -19
- package/dist/helpers/pl.cjs.map +1 -1
- package/dist/helpers/pl.d.ts +2 -2
- package/dist/helpers/pl.js +19 -19
- package/dist/helpers/pl.js.map +1 -1
- package/dist/helpers/poll.cjs +6 -6
- package/dist/helpers/poll.cjs.map +1 -1
- package/dist/helpers/poll.d.ts +4 -4
- package/dist/helpers/poll.d.ts.map +1 -1
- package/dist/helpers/poll.js +6 -6
- package/dist/helpers/poll.js.map +1 -1
- package/dist/helpers/retry_strategy.cjs +1 -1
- package/dist/helpers/retry_strategy.cjs.map +1 -1
- package/dist/helpers/retry_strategy.d.ts.map +1 -1
- package/dist/helpers/retry_strategy.js +1 -1
- package/dist/helpers/retry_strategy.js.map +1 -1
- package/dist/helpers/state_helpers.d.ts +2 -2
- package/dist/helpers/tx_helpers.cjs +2 -2
- package/dist/helpers/tx_helpers.cjs.map +1 -1
- package/dist/helpers/tx_helpers.d.ts +2 -2
- package/dist/helpers/tx_helpers.d.ts.map +1 -1
- package/dist/helpers/tx_helpers.js +2 -2
- package/dist/helpers/tx_helpers.js.map +1 -1
- package/dist/index.d.ts +16 -16
- package/dist/proto-grpc/google/protobuf/struct.d.ts +1 -1
- package/dist/proto-grpc/google/protobuf/struct.d.ts.map +1 -1
- package/dist/proto-rest/index.cjs +4 -5
- package/dist/proto-rest/index.cjs.map +1 -1
- package/dist/proto-rest/index.d.ts +4 -4
- package/dist/proto-rest/index.d.ts.map +1 -1
- package/dist/proto-rest/index.js +4 -5
- package/dist/proto-rest/index.js.map +1 -1
- package/dist/proto-rest/plapi.d.ts.map +1 -1
- package/dist/test/tcp-proxy.cjs +11 -10
- package/dist/test/tcp-proxy.cjs.map +1 -1
- package/dist/test/tcp-proxy.d.ts +1 -1
- package/dist/test/tcp-proxy.d.ts.map +1 -1
- package/dist/test/tcp-proxy.js +11 -10
- package/dist/test/tcp-proxy.js.map +1 -1
- package/dist/test/test_config.cjs +21 -17
- package/dist/test/test_config.cjs.map +1 -1
- package/dist/test/test_config.d.ts +6 -6
- package/dist/test/test_config.d.ts.map +1 -1
- package/dist/test/test_config.js +21 -17
- package/dist/test/test_config.js.map +1 -1
- package/dist/util/pl.cjs +1 -1
- package/dist/util/pl.cjs.map +1 -1
- package/dist/util/pl.js +1 -1
- package/dist/util/pl.js.map +1 -1
- package/dist/util/util.cjs +1 -1
- package/dist/util/util.cjs.map +1 -1
- package/dist/util/util.js +1 -1
- package/dist/util/util.js.map +1 -1
- package/package.json +23 -23
- package/src/core/PromiseTracker.ts +3 -4
- package/src/core/StatefulPromise.ts +17 -8
- package/src/core/abstract_stream.ts +3 -4
- package/src/core/advisory_locks.ts +1 -1
- package/src/core/auth.ts +2 -2
- package/src/core/cache.ts +1 -1
- package/src/core/client.test.ts +25 -21
- package/src/core/client.ts +54 -45
- package/src/core/config.test.ts +44 -44
- package/src/core/config.ts +49 -49
- package/src/core/connectivity.test.ts +69 -63
- package/src/core/default_client.ts +46 -46
- package/src/core/driver.ts +6 -6
- package/src/core/error.test.ts +5 -5
- package/src/core/error_resource.ts +3 -3
- package/src/core/errors.ts +39 -31
- package/src/core/final.ts +48 -55
- package/src/core/ll_client.test.ts +53 -36
- package/src/core/ll_client.ts +125 -81
- package/src/core/ll_transaction.test.ts +75 -49
- package/src/core/ll_transaction.ts +37 -35
- package/src/core/stat.ts +1 -1
- package/src/core/transaction.test.ts +65 -65
- package/src/core/transaction.ts +91 -84
- package/src/core/type_conversion.ts +30 -31
- package/src/core/types.test.ts +6 -6
- package/src/core/types.ts +35 -35
- package/src/core/unauth_client.test.ts +18 -14
- package/src/core/unauth_client.ts +14 -12
- package/src/core/websocket_stream.test.ts +52 -52
- package/src/core/websocket_stream.ts +41 -37
- package/src/core/wire.ts +10 -8
- package/src/helpers/pl.ts +22 -22
- package/src/helpers/poll.ts +13 -27
- package/src/helpers/retry_strategy.ts +2 -4
- package/src/helpers/rich_resource_types.test.ts +2 -2
- package/src/helpers/state_helpers.ts +3 -3
- package/src/helpers/tx_helpers.ts +9 -7
- package/src/index.ts +16 -16
- package/src/proto-grpc/google/protobuf/struct.ts +1 -1
- package/src/proto-rest/index.ts +17 -18
- package/src/proto-rest/plapi.ts +1472 -1472
- package/src/test/tcp-proxy.ts +55 -54
- package/src/test/test_config.test.ts +3 -3
- package/src/test/test_config.ts +51 -46
- package/src/util/pl.ts +1 -1
- package/src/util/util.test.ts +5 -5
- package/src/util/util.ts +1 -1
- package/dist/helpers/rich_resource_types.d.ts +0 -2
- package/dist/helpers/rich_resource_types.d.ts.map +0 -1
- package/dist/helpers/smart_accessors.d.ts +0 -2
- package/dist/helpers/smart_accessors.d.ts.map +0 -1
- package/src/helpers/rich_resource_types.ts +0 -84
- package/src/helpers/smart_accessors.ts +0 -146
package/dist/core/client.js
CHANGED
|
@@ -15,7 +15,7 @@ import { plAddressToConfig } from './config.js';
|
|
|
15
15
|
const defaultTxOps = {
|
|
16
16
|
sync: false,
|
|
17
17
|
};
|
|
18
|
-
const AnonymousClientRoot =
|
|
18
|
+
const AnonymousClientRoot = "AnonymousRoot";
|
|
19
19
|
function alternativeRootFieldName(alternativeRoot) {
|
|
20
20
|
return `alternative_root_${alternativeRoot}`;
|
|
21
21
|
}
|
|
@@ -33,7 +33,7 @@ class PlClient {
|
|
|
33
33
|
_ll;
|
|
34
34
|
get ll() {
|
|
35
35
|
if (this._ll === undefined) {
|
|
36
|
-
throw new Error(
|
|
36
|
+
throw new Error("LLPlClient not initialized");
|
|
37
37
|
}
|
|
38
38
|
return this._ll;
|
|
39
39
|
}
|
|
@@ -51,7 +51,7 @@ class PlClient {
|
|
|
51
51
|
/** Resource data cache, to minimize redundant data rereading from remote db */
|
|
52
52
|
resourceDataCache;
|
|
53
53
|
constructor(configOrAddress, auth, ops = {}) {
|
|
54
|
-
const conf = typeof configOrAddress ===
|
|
54
|
+
const conf = typeof configOrAddress === "string" ? plAddressToConfig(configOrAddress) : configOrAddress;
|
|
55
55
|
this.buildLLPlClient = async (shouldUseGzip, wireProtocol) => {
|
|
56
56
|
if (wireProtocol)
|
|
57
57
|
conf.wireProtocol = wireProtocol;
|
|
@@ -65,18 +65,18 @@ class PlClient {
|
|
|
65
65
|
sizeCalculation: (v) => (v.basicData.data?.length ?? 0) + 64,
|
|
66
66
|
});
|
|
67
67
|
switch (conf.retryBackoffAlgorithm) {
|
|
68
|
-
case
|
|
68
|
+
case "exponential":
|
|
69
69
|
this.defaultRetryOptions = {
|
|
70
|
-
type:
|
|
70
|
+
type: "exponentialBackoff",
|
|
71
71
|
initialDelay: conf.retryInitialDelay,
|
|
72
72
|
maxAttempts: conf.retryMaxAttempts,
|
|
73
73
|
backoffMultiplier: conf.retryExponentialBackoffMultiplier,
|
|
74
74
|
jitter: conf.retryJitter,
|
|
75
75
|
};
|
|
76
76
|
break;
|
|
77
|
-
case
|
|
77
|
+
case "linear":
|
|
78
78
|
this.defaultRetryOptions = {
|
|
79
|
-
type:
|
|
79
|
+
type: "linearBackoff",
|
|
80
80
|
initialDelay: conf.retryInitialDelay,
|
|
81
81
|
maxAttempts: conf.retryMaxAttempts,
|
|
82
82
|
backoffStep: conf.retryLinearBackoffStep,
|
|
@@ -126,7 +126,7 @@ class PlClient {
|
|
|
126
126
|
}
|
|
127
127
|
checkInitialized() {
|
|
128
128
|
if (!this.initialized)
|
|
129
|
-
throw new Error(
|
|
129
|
+
throw new Error("Client not initialized");
|
|
130
130
|
}
|
|
131
131
|
get clientRoot() {
|
|
132
132
|
this.checkInitialized();
|
|
@@ -139,7 +139,7 @@ class PlClient {
|
|
|
139
139
|
/** Currently implements custom logic to emulate future behaviour with single root. */
|
|
140
140
|
async init() {
|
|
141
141
|
if (this.initialized)
|
|
142
|
-
throw new Error(
|
|
142
|
+
throw new Error("Already initialized");
|
|
143
143
|
// Initial client is created without gzip to perform server ping and detect optimal wire protocol.
|
|
144
144
|
// LLPlClient.build() internally calls detectOptimalWireProtocol() which starts with default 'grpc',
|
|
145
145
|
// then retries with 'rest' if ping fails, alternating until a working protocol is found.
|
|
@@ -150,13 +150,13 @@ class PlClient {
|
|
|
150
150
|
const wireProtocol = this._ll.wireProtocol;
|
|
151
151
|
// calculating reproducible root name from the username
|
|
152
152
|
const user = this._ll.authUser;
|
|
153
|
-
const mainRootName = user === null ? AnonymousClientRoot : createHash(
|
|
153
|
+
const mainRootName = user === null ? AnonymousClientRoot : createHash("sha256").update(user).digest("hex");
|
|
154
154
|
this._serverInfo = await this.ping();
|
|
155
155
|
if (this._serverInfo.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {
|
|
156
156
|
await this._ll.close();
|
|
157
157
|
this._ll = await this.buildLLPlClient(true, wireProtocol);
|
|
158
158
|
}
|
|
159
|
-
this._clientRoot = await this._withTx(
|
|
159
|
+
this._clientRoot = await this._withTx("initialization", true, NullResourceId, async (tx) => {
|
|
160
160
|
let mainRoot;
|
|
161
161
|
if (await tx.checkResourceNameExists(mainRootName))
|
|
162
162
|
mainRoot = await tx.getResourceByName(mainRootName);
|
|
@@ -175,7 +175,7 @@ class PlClient {
|
|
|
175
175
|
};
|
|
176
176
|
const altRoot = tx.createEphemeral(ClientRoot);
|
|
177
177
|
tx.lock(altRoot);
|
|
178
|
-
tx.createField(aFId,
|
|
178
|
+
tx.createField(aFId, "Dynamic");
|
|
179
179
|
tx.setField(aFId, altRoot);
|
|
180
180
|
await tx.commit();
|
|
181
181
|
return await altRoot.globalId;
|
|
@@ -186,8 +186,8 @@ class PlClient {
|
|
|
186
186
|
async deleteAlternativeRoot(alternativeRootName) {
|
|
187
187
|
this.checkInitialized();
|
|
188
188
|
if (this.ll.conf.alternativeRoot !== undefined)
|
|
189
|
-
throw new Error(
|
|
190
|
-
return await this.withWriteTx(
|
|
189
|
+
throw new Error("Initialized with alternative root.");
|
|
190
|
+
return await this.withWriteTx("delete-alternative-root", async (tx) => {
|
|
191
191
|
const fId = {
|
|
192
192
|
resourceId: tx.clientRoot,
|
|
193
193
|
fieldName: alternativeRootFieldName(alternativeRootName),
|
package/dist/core/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../src/core/client.ts"],"sourcesContent":["import type { AuthOps, PlClientConfig, PlConnectionStatusListener, wireProtocol } from './config';\nimport type { PlCallOps } from './ll_client';\nimport { LLPlClient } from './ll_client';\nimport type { AnyResourceRef } from './transaction';\nimport { PlTransaction, toGlobalResourceId, TxCommitConflict } from './transaction';\nimport { createHash } from 'node:crypto';\nimport type { OptionalResourceId, ResourceId } from './types';\nimport { ensureResourceIdNotNull, isNullResourceId, NullResourceId } from './types';\nimport { ClientRoot } from '../helpers/pl';\nimport type { MiLogger, RetryOptions } from '@milaboratories/ts-helpers';\nimport { assertNever, createRetryState, nextRetryStateOrError } from '@milaboratories/ts-helpers';\nimport type { PlDriver, PlDriverDefinition } from './driver';\nimport type { MaintenanceAPI_Ping_Response, MaintenanceAPI_License_Response } from '../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api';\nimport { MaintenanceAPI_Ping_Response_Compression } from '../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api';\nimport * as tp from 'node:timers/promises';\nimport type { Dispatcher } from 'undici';\nimport { LRUCache } from 'lru-cache';\nimport type { ResourceDataCacheRecord } from './cache';\nimport type { FinalResourceDataPredicate } from './final';\nimport { DefaultFinalResourceDataPredicate } from './final';\nimport type { AllTxStat, TxStat } from './stat';\nimport { addStat, initialTxStat } from './stat';\nimport type { WireConnection } from './wire';\nimport { advisoryLock } from './advisory_locks';\nimport { plAddressToConfig } from './config';\n\nexport type TxOps = PlCallOps & {\n sync?: boolean;\n retryOptions?: RetryOptions;\n name?: string;\n lockId?: string;\n};\n\nconst defaultTxOps = {\n sync: false,\n};\n\nconst AnonymousClientRoot = 'AnonymousRoot';\n\nfunction alternativeRootFieldName(alternativeRoot: string): string {\n return `alternative_root_${alternativeRoot}`;\n}\n\n/** Client to access core PL API. */\nexport class PlClient {\n private readonly drivers = new Map<string, PlDriver>();\n\n /** Artificial delay introduced after write transactions completion, to\n * somewhat throttle the load on pl. Delay introduced after sync, if requested. */\n private readonly txDelay: number;\n\n /** Last resort measure to solve complicated race conditions in pl. */\n private readonly forceSync: boolean;\n\n /** Last resort measure to solve complicated race conditions in pl. */\n private readonly defaultRetryOptions: RetryOptions;\n\n private readonly buildLLPlClient: (shouldUseGzip: boolean, wireProtocol?: wireProtocol) => Promise<LLPlClient>;\n private _ll?: LLPlClient;\n\n private get ll(): LLPlClient {\n if (this._ll === undefined) {\n throw new Error('LLPlClient not initialized');\n }\n return this._ll;\n }\n\n /** Stores client root (this abstraction is intended for future implementation of the security model) */\n private _clientRoot: OptionalResourceId = NullResourceId;\n\n private _serverInfo: MaintenanceAPI_Ping_Response | undefined = undefined;\n\n private _txCommittedStat: TxStat = initialTxStat();\n private _txConflictStat: TxStat = initialTxStat();\n private _txErrorStat: TxStat = initialTxStat();\n\n //\n // Caching\n //\n\n /** This function determines whether resource data can be cached */\n public readonly finalPredicate: FinalResourceDataPredicate;\n\n /** Resource data cache, to minimize redundant data rereading from remote db */\n private readonly resourceDataCache: LRUCache<ResourceId, ResourceDataCacheRecord>;\n\n private constructor(\n configOrAddress: PlClientConfig | string,\n auth: AuthOps,\n ops: {\n statusListener?: PlConnectionStatusListener;\n finalPredicate?: FinalResourceDataPredicate;\n logger?: MiLogger;\n } = {},\n ) {\n const conf = typeof configOrAddress === 'string' ? plAddressToConfig(configOrAddress) : configOrAddress;\n\n this.buildLLPlClient = async (shouldUseGzip: boolean, wireProtocol?: wireProtocol): Promise<LLPlClient> => {\n if (wireProtocol) conf.wireProtocol = wireProtocol;\n return await LLPlClient.build(conf, { auth, ...ops, shouldUseGzip });\n };\n\n this.txDelay = conf.txDelay;\n this.forceSync = conf.forceSync;\n this.finalPredicate = ops.finalPredicate ?? DefaultFinalResourceDataPredicate;\n this.resourceDataCache = new LRUCache({\n maxSize: conf.maxCacheBytes,\n sizeCalculation: (v) => (v.basicData.data?.length ?? 0) + 64,\n });\n switch (conf.retryBackoffAlgorithm) {\n case 'exponential':\n this.defaultRetryOptions = {\n type: 'exponentialBackoff',\n initialDelay: conf.retryInitialDelay,\n maxAttempts: conf.retryMaxAttempts,\n backoffMultiplier: conf.retryExponentialBackoffMultiplier,\n jitter: conf.retryJitter,\n };\n break;\n case 'linear':\n this.defaultRetryOptions = {\n type: 'linearBackoff',\n initialDelay: conf.retryInitialDelay,\n maxAttempts: conf.retryMaxAttempts,\n backoffStep: conf.retryLinearBackoffStep,\n jitter: conf.retryJitter,\n };\n break;\n default:\n assertNever(conf.retryBackoffAlgorithm);\n }\n }\n\n public get txCommittedStat(): TxStat {\n return { ...this._txCommittedStat };\n }\n\n public get txConflictStat(): TxStat {\n return { ...this._txConflictStat };\n }\n\n public get txErrorStat(): TxStat {\n return { ...this._txErrorStat };\n }\n\n public get txTotalStat(): TxStat {\n return addStat(addStat(this._txCommittedStat, this._txConflictStat), this._txErrorStat);\n }\n\n public get allTxStat(): AllTxStat {\n return {\n committed: this.txCommittedStat,\n conflict: this.txConflictStat,\n error: this.txErrorStat,\n };\n }\n\n public async ping(): Promise<MaintenanceAPI_Ping_Response> {\n return await this.ll.ping();\n }\n\n public async license(): Promise<MaintenanceAPI_License_Response> {\n return await this.ll.license();\n }\n\n public get conf(): PlClientConfig {\n return this.ll.conf;\n }\n\n public get httpDispatcher(): Dispatcher {\n return this.ll.httpDispatcher;\n }\n\n public get connectionOpts(): WireConnection {\n return this.ll.wireConnection;\n }\n\n private get initialized() {\n return !isNullResourceId(this._clientRoot);\n }\n\n private checkInitialized() {\n if (!this.initialized) throw new Error('Client not initialized');\n }\n\n public get clientRoot(): ResourceId {\n this.checkInitialized();\n return ensureResourceIdNotNull(this._clientRoot);\n }\n\n public get serverInfo(): MaintenanceAPI_Ping_Response {\n this.checkInitialized();\n return this._serverInfo!;\n }\n\n /** Currently implements custom logic to emulate future behaviour with single root. */\n private async init() {\n if (this.initialized) throw new Error('Already initialized');\n\n // Initial client is created without gzip to perform server ping and detect optimal wire protocol.\n // LLPlClient.build() internally calls detectOptimalWireProtocol() which starts with default 'grpc',\n // then retries with 'rest' if ping fails, alternating until a working protocol is found.\n // We save the detected wireProtocol here because if the server supports gzip compression,\n // we'll need to reinitialize the client with gzip enabled - passing the already-detected\n // wireProtocol avoids redundant protocol detection on reinit.\n this._ll = await this.buildLLPlClient(false);\n const wireProtocol = this._ll.wireProtocol;\n\n // calculating reproducible root name from the username\n const user = this._ll.authUser;\n const mainRootName\n = user === null ? AnonymousClientRoot : createHash('sha256').update(user).digest('hex');\n\n this._serverInfo = await this.ping();\n if (this._serverInfo.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {\n await this._ll.close();\n this._ll = await this.buildLLPlClient(true, wireProtocol);\n }\n\n this._clientRoot = await this._withTx('initialization', true, NullResourceId, async (tx) => {\n let mainRoot: AnyResourceRef;\n\n if (await tx.checkResourceNameExists(mainRootName))\n mainRoot = await tx.getResourceByName(mainRootName);\n else {\n mainRoot = tx.createRoot(ClientRoot);\n tx.setResourceName(mainRootName, mainRoot);\n }\n\n if (this.conf.alternativeRoot === undefined) {\n await tx.commit();\n return await toGlobalResourceId(mainRoot);\n } else {\n const aFId = {\n resourceId: mainRoot,\n fieldName: alternativeRootFieldName(this.conf.alternativeRoot),\n };\n\n const altRoot = tx.createEphemeral(ClientRoot);\n tx.lock(altRoot);\n tx.createField(aFId, 'Dynamic');\n tx.setField(aFId, altRoot);\n await tx.commit();\n\n return await altRoot.globalId;\n }\n });\n }\n\n /** Returns true if field existed */\n public async deleteAlternativeRoot(alternativeRootName: string): Promise<boolean> {\n this.checkInitialized();\n if (this.ll.conf.alternativeRoot !== undefined)\n throw new Error('Initialized with alternative root.');\n return await this.withWriteTx('delete-alternative-root', async (tx) => {\n const fId = {\n resourceId: tx.clientRoot,\n fieldName: alternativeRootFieldName(alternativeRootName),\n };\n const exists = tx.fieldExists(fId);\n tx.removeField(fId);\n await tx.commit();\n return await exists;\n });\n }\n\n private async _withTx<T>(\n name: string,\n writable: boolean,\n clientRoot: OptionalResourceId,\n body: (tx: PlTransaction) => Promise<T>,\n ops?: TxOps,\n ): Promise<T> {\n // for exponential / linear backoff\n let retryState = createRetryState(ops?.retryOptions ?? this.defaultRetryOptions);\n\n while (true) {\n const release = ops?.lockId ? await advisoryLock(ops.lockId) : () => {};\n\n try {\n // opening low-level tx\n const llTx = this.ll.createTx(writable, ops);\n // wrapping it into high-level tx (this also asynchronously sends initialization message)\n const tx = new PlTransaction(\n llTx,\n name,\n writable,\n clientRoot,\n this.finalPredicate,\n this.resourceDataCache,\n );\n\n let ok = false;\n let result: T | undefined = undefined;\n let txId;\n\n try {\n // executing transaction body\n result = await body(tx);\n // collecting stat\n this._txCommittedStat = addStat(this._txCommittedStat, tx.stat);\n ok = true;\n } catch (e: unknown) {\n // the only recoverable\n if (e instanceof TxCommitConflict) {\n // ignoring\n // collecting stat\n this._txConflictStat = addStat(this._txConflictStat, tx.stat);\n } else {\n // collecting stat\n this._txErrorStat = addStat(this._txErrorStat, tx.stat);\n throw e;\n }\n } finally {\n // close underlying grpc stream, if not yet done\n\n // even though we can skip two lines below for read-only transactions,\n // we don't do it to simplify reasoning about what is going on in\n // concurrent code, especially in significant latency situations\n await tx.complete();\n await tx.await();\n\n txId = await tx.getGlobalTxId();\n }\n\n if (ok) {\n // syncing on transaction if requested\n if (ops?.sync === undefined ? this.forceSync : ops?.sync)\n await this.ll.txSync(txId);\n\n // introducing artificial delay, if requested\n if (writable && this.txDelay > 0)\n await tp.setTimeout(this.txDelay, undefined, { signal: ops?.abortSignal });\n\n return result!;\n }\n } finally {\n release();\n }\n\n // we only get here after TxCommitConflict error,\n // all other errors terminate this loop instantly\n\n await tp.setTimeout(retryState.nextDelay, undefined, { signal: ops?.abortSignal });\n retryState = nextRetryStateOrError(retryState);\n }\n }\n\n private async withTx<T>(\n name: string,\n writable: boolean,\n body: (tx: PlTransaction) => Promise<T>,\n ops: Partial<TxOps> = {},\n ): Promise<T> {\n this.checkInitialized();\n return await this._withTx(name, writable, this.clientRoot, body, { ...ops, ...defaultTxOps });\n }\n\n public async withWriteTx<T>(\n name: string,\n body: (tx: PlTransaction) => Promise<T>,\n ops: Partial<TxOps> = {},\n ): Promise<T> {\n return await this.withTx(name, true, body, { ...ops, ...defaultTxOps });\n }\n\n public async withReadTx<T>(\n name: string,\n body: (tx: PlTransaction) => Promise<T>,\n ops: Partial<TxOps> = {},\n ): Promise<T> {\n return await this.withTx(name, false, body, { ...ops, ...defaultTxOps });\n }\n\n public getDriver<Drv extends PlDriver>(definition: PlDriverDefinition<Drv>): Drv {\n const attached = this.drivers.get(definition.name);\n if (attached !== undefined) return attached as Drv;\n const driver = definition.init(this, this.ll, this.httpDispatcher);\n this.drivers.set(definition.name, driver);\n return driver;\n }\n\n /** Closes underlying transport */\n public async close() {\n await this.ll.close();\n }\n\n public static async init(\n configOrAddress: PlClientConfig | string,\n auth: AuthOps,\n ops: {\n statusListener?: PlConnectionStatusListener;\n logger?: MiLogger;\n } = {},\n ) {\n const pl = new PlClient(configOrAddress, auth, ops);\n await pl.init();\n return pl;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../src/core/client.ts"],"sourcesContent":["import type { AuthOps, PlClientConfig, PlConnectionStatusListener, wireProtocol } from \"./config\";\nimport type { PlCallOps } from \"./ll_client\";\nimport { LLPlClient } from \"./ll_client\";\nimport type { AnyResourceRef } from \"./transaction\";\nimport { PlTransaction, toGlobalResourceId, TxCommitConflict } from \"./transaction\";\nimport { createHash } from \"node:crypto\";\nimport type { OptionalResourceId, ResourceId } from \"./types\";\nimport { ensureResourceIdNotNull, isNullResourceId, NullResourceId } from \"./types\";\nimport { ClientRoot } from \"../helpers/pl\";\nimport type { MiLogger, RetryOptions } from \"@milaboratories/ts-helpers\";\nimport { assertNever, createRetryState, nextRetryStateOrError } from \"@milaboratories/ts-helpers\";\nimport type { PlDriver, PlDriverDefinition } from \"./driver\";\nimport type {\n MaintenanceAPI_Ping_Response,\n MaintenanceAPI_License_Response,\n} from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport { MaintenanceAPI_Ping_Response_Compression } from \"../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api\";\nimport * as tp from \"node:timers/promises\";\nimport type { Dispatcher } from \"undici\";\nimport { LRUCache } from \"lru-cache\";\nimport type { ResourceDataCacheRecord } from \"./cache\";\nimport type { FinalResourceDataPredicate } from \"./final\";\nimport { DefaultFinalResourceDataPredicate } from \"./final\";\nimport type { AllTxStat, TxStat } from \"./stat\";\nimport { addStat, initialTxStat } from \"./stat\";\nimport type { WireConnection } from \"./wire\";\nimport { advisoryLock } from \"./advisory_locks\";\nimport { plAddressToConfig } from \"./config\";\n\nexport type TxOps = PlCallOps & {\n sync?: boolean;\n retryOptions?: RetryOptions;\n name?: string;\n lockId?: string;\n};\n\nconst defaultTxOps = {\n sync: false,\n};\n\nconst AnonymousClientRoot = \"AnonymousRoot\";\n\nfunction alternativeRootFieldName(alternativeRoot: string): string {\n return `alternative_root_${alternativeRoot}`;\n}\n\n/** Client to access core PL API. */\nexport class PlClient {\n private readonly drivers = new Map<string, PlDriver>();\n\n /** Artificial delay introduced after write transactions completion, to\n * somewhat throttle the load on pl. Delay introduced after sync, if requested. */\n private readonly txDelay: number;\n\n /** Last resort measure to solve complicated race conditions in pl. */\n private readonly forceSync: boolean;\n\n /** Last resort measure to solve complicated race conditions in pl. */\n private readonly defaultRetryOptions: RetryOptions;\n\n private readonly buildLLPlClient: (\n shouldUseGzip: boolean,\n wireProtocol?: wireProtocol,\n ) => Promise<LLPlClient>;\n private _ll?: LLPlClient;\n\n private get ll(): LLPlClient {\n if (this._ll === undefined) {\n throw new Error(\"LLPlClient not initialized\");\n }\n return this._ll;\n }\n\n /** Stores client root (this abstraction is intended for future implementation of the security model) */\n private _clientRoot: OptionalResourceId = NullResourceId;\n\n private _serverInfo: MaintenanceAPI_Ping_Response | undefined = undefined;\n\n private _txCommittedStat: TxStat = initialTxStat();\n private _txConflictStat: TxStat = initialTxStat();\n private _txErrorStat: TxStat = initialTxStat();\n\n //\n // Caching\n //\n\n /** This function determines whether resource data can be cached */\n public readonly finalPredicate: FinalResourceDataPredicate;\n\n /** Resource data cache, to minimize redundant data rereading from remote db */\n private readonly resourceDataCache: LRUCache<ResourceId, ResourceDataCacheRecord>;\n\n private constructor(\n configOrAddress: PlClientConfig | string,\n auth: AuthOps,\n ops: {\n statusListener?: PlConnectionStatusListener;\n finalPredicate?: FinalResourceDataPredicate;\n logger?: MiLogger;\n } = {},\n ) {\n const conf =\n typeof configOrAddress === \"string\" ? plAddressToConfig(configOrAddress) : configOrAddress;\n\n this.buildLLPlClient = async (\n shouldUseGzip: boolean,\n wireProtocol?: wireProtocol,\n ): Promise<LLPlClient> => {\n if (wireProtocol) conf.wireProtocol = wireProtocol;\n return await LLPlClient.build(conf, { auth, ...ops, shouldUseGzip });\n };\n\n this.txDelay = conf.txDelay;\n this.forceSync = conf.forceSync;\n this.finalPredicate = ops.finalPredicate ?? DefaultFinalResourceDataPredicate;\n this.resourceDataCache = new LRUCache({\n maxSize: conf.maxCacheBytes,\n sizeCalculation: (v) => (v.basicData.data?.length ?? 0) + 64,\n });\n switch (conf.retryBackoffAlgorithm) {\n case \"exponential\":\n this.defaultRetryOptions = {\n type: \"exponentialBackoff\",\n initialDelay: conf.retryInitialDelay,\n maxAttempts: conf.retryMaxAttempts,\n backoffMultiplier: conf.retryExponentialBackoffMultiplier,\n jitter: conf.retryJitter,\n };\n break;\n case \"linear\":\n this.defaultRetryOptions = {\n type: \"linearBackoff\",\n initialDelay: conf.retryInitialDelay,\n maxAttempts: conf.retryMaxAttempts,\n backoffStep: conf.retryLinearBackoffStep,\n jitter: conf.retryJitter,\n };\n break;\n default:\n assertNever(conf.retryBackoffAlgorithm);\n }\n }\n\n public get txCommittedStat(): TxStat {\n return { ...this._txCommittedStat };\n }\n\n public get txConflictStat(): TxStat {\n return { ...this._txConflictStat };\n }\n\n public get txErrorStat(): TxStat {\n return { ...this._txErrorStat };\n }\n\n public get txTotalStat(): TxStat {\n return addStat(addStat(this._txCommittedStat, this._txConflictStat), this._txErrorStat);\n }\n\n public get allTxStat(): AllTxStat {\n return {\n committed: this.txCommittedStat,\n conflict: this.txConflictStat,\n error: this.txErrorStat,\n };\n }\n\n public async ping(): Promise<MaintenanceAPI_Ping_Response> {\n return await this.ll.ping();\n }\n\n public async license(): Promise<MaintenanceAPI_License_Response> {\n return await this.ll.license();\n }\n\n public get conf(): PlClientConfig {\n return this.ll.conf;\n }\n\n public get httpDispatcher(): Dispatcher {\n return this.ll.httpDispatcher;\n }\n\n public get connectionOpts(): WireConnection {\n return this.ll.wireConnection;\n }\n\n private get initialized() {\n return !isNullResourceId(this._clientRoot);\n }\n\n private checkInitialized() {\n if (!this.initialized) throw new Error(\"Client not initialized\");\n }\n\n public get clientRoot(): ResourceId {\n this.checkInitialized();\n return ensureResourceIdNotNull(this._clientRoot);\n }\n\n public get serverInfo(): MaintenanceAPI_Ping_Response {\n this.checkInitialized();\n return this._serverInfo!;\n }\n\n /** Currently implements custom logic to emulate future behaviour with single root. */\n private async init() {\n if (this.initialized) throw new Error(\"Already initialized\");\n\n // Initial client is created without gzip to perform server ping and detect optimal wire protocol.\n // LLPlClient.build() internally calls detectOptimalWireProtocol() which starts with default 'grpc',\n // then retries with 'rest' if ping fails, alternating until a working protocol is found.\n // We save the detected wireProtocol here because if the server supports gzip compression,\n // we'll need to reinitialize the client with gzip enabled - passing the already-detected\n // wireProtocol avoids redundant protocol detection on reinit.\n this._ll = await this.buildLLPlClient(false);\n const wireProtocol = this._ll.wireProtocol;\n\n // calculating reproducible root name from the username\n const user = this._ll.authUser;\n const mainRootName =\n user === null ? AnonymousClientRoot : createHash(\"sha256\").update(user).digest(\"hex\");\n\n this._serverInfo = await this.ping();\n if (this._serverInfo.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {\n await this._ll.close();\n this._ll = await this.buildLLPlClient(true, wireProtocol);\n }\n\n this._clientRoot = await this._withTx(\"initialization\", true, NullResourceId, async (tx) => {\n let mainRoot: AnyResourceRef;\n\n if (await tx.checkResourceNameExists(mainRootName))\n mainRoot = await tx.getResourceByName(mainRootName);\n else {\n mainRoot = tx.createRoot(ClientRoot);\n tx.setResourceName(mainRootName, mainRoot);\n }\n\n if (this.conf.alternativeRoot === undefined) {\n await tx.commit();\n return await toGlobalResourceId(mainRoot);\n } else {\n const aFId = {\n resourceId: mainRoot,\n fieldName: alternativeRootFieldName(this.conf.alternativeRoot),\n };\n\n const altRoot = tx.createEphemeral(ClientRoot);\n tx.lock(altRoot);\n tx.createField(aFId, \"Dynamic\");\n tx.setField(aFId, altRoot);\n await tx.commit();\n\n return await altRoot.globalId;\n }\n });\n }\n\n /** Returns true if field existed */\n public async deleteAlternativeRoot(alternativeRootName: string): Promise<boolean> {\n this.checkInitialized();\n if (this.ll.conf.alternativeRoot !== undefined)\n throw new Error(\"Initialized with alternative root.\");\n return await this.withWriteTx(\"delete-alternative-root\", async (tx) => {\n const fId = {\n resourceId: tx.clientRoot,\n fieldName: alternativeRootFieldName(alternativeRootName),\n };\n const exists = tx.fieldExists(fId);\n tx.removeField(fId);\n await tx.commit();\n return await exists;\n });\n }\n\n private async _withTx<T>(\n name: string,\n writable: boolean,\n clientRoot: OptionalResourceId,\n body: (tx: PlTransaction) => Promise<T>,\n ops?: TxOps,\n ): Promise<T> {\n // for exponential / linear backoff\n let retryState = createRetryState(ops?.retryOptions ?? this.defaultRetryOptions);\n\n while (true) {\n const release = ops?.lockId ? await advisoryLock(ops.lockId) : () => {};\n\n try {\n // opening low-level tx\n const llTx = this.ll.createTx(writable, ops);\n // wrapping it into high-level tx (this also asynchronously sends initialization message)\n const tx = new PlTransaction(\n llTx,\n name,\n writable,\n clientRoot,\n this.finalPredicate,\n this.resourceDataCache,\n );\n\n let ok = false;\n let result: T | undefined = undefined;\n let txId;\n\n try {\n // executing transaction body\n result = await body(tx);\n // collecting stat\n this._txCommittedStat = addStat(this._txCommittedStat, tx.stat);\n ok = true;\n } catch (e: unknown) {\n // the only recoverable\n if (e instanceof TxCommitConflict) {\n // ignoring\n // collecting stat\n this._txConflictStat = addStat(this._txConflictStat, tx.stat);\n } else {\n // collecting stat\n this._txErrorStat = addStat(this._txErrorStat, tx.stat);\n throw e;\n }\n } finally {\n // close underlying grpc stream, if not yet done\n\n // even though we can skip two lines below for read-only transactions,\n // we don't do it to simplify reasoning about what is going on in\n // concurrent code, especially in significant latency situations\n await tx.complete();\n await tx.await();\n\n txId = await tx.getGlobalTxId();\n }\n\n if (ok) {\n // syncing on transaction if requested\n if (ops?.sync === undefined ? this.forceSync : ops?.sync) await this.ll.txSync(txId);\n\n // introducing artificial delay, if requested\n if (writable && this.txDelay > 0)\n await tp.setTimeout(this.txDelay, undefined, { signal: ops?.abortSignal });\n\n return result!;\n }\n } finally {\n release();\n }\n\n // we only get here after TxCommitConflict error,\n // all other errors terminate this loop instantly\n\n await tp.setTimeout(retryState.nextDelay, undefined, { signal: ops?.abortSignal });\n retryState = nextRetryStateOrError(retryState);\n }\n }\n\n private async withTx<T>(\n name: string,\n writable: boolean,\n body: (tx: PlTransaction) => Promise<T>,\n ops: Partial<TxOps> = {},\n ): Promise<T> {\n this.checkInitialized();\n return await this._withTx(name, writable, this.clientRoot, body, { ...ops, ...defaultTxOps });\n }\n\n public async withWriteTx<T>(\n name: string,\n body: (tx: PlTransaction) => Promise<T>,\n ops: Partial<TxOps> = {},\n ): Promise<T> {\n return await this.withTx(name, true, body, { ...ops, ...defaultTxOps });\n }\n\n public async withReadTx<T>(\n name: string,\n body: (tx: PlTransaction) => Promise<T>,\n ops: Partial<TxOps> = {},\n ): Promise<T> {\n return await this.withTx(name, false, body, { ...ops, ...defaultTxOps });\n }\n\n public getDriver<Drv extends PlDriver>(definition: PlDriverDefinition<Drv>): Drv {\n const attached = this.drivers.get(definition.name);\n if (attached !== undefined) return attached as Drv;\n const driver = definition.init(this, this.ll, this.httpDispatcher);\n this.drivers.set(definition.name, driver);\n return driver;\n }\n\n /** Closes underlying transport */\n public async close() {\n await this.ll.close();\n }\n\n public static async init(\n configOrAddress: PlClientConfig | string,\n auth: AuthOps,\n ops: {\n statusListener?: PlConnectionStatusListener;\n logger?: MiLogger;\n } = {},\n ) {\n const pl = new PlClient(configOrAddress, auth, ops);\n await pl.init();\n return pl;\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/core/config.cjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const SUPPORTED_WIRE_PROTOCOLS = [
|
|
3
|
+
const SUPPORTED_WIRE_PROTOCOLS = ["grpc", "rest"];
|
|
4
4
|
const DEFAULT_REQUEST_TIMEOUT = 5_000;
|
|
5
5
|
const DEFAULT_RO_TX_TIMEOUT = 300_000;
|
|
6
6
|
const DEFAULT_RW_TX_TIMEOUT = 60_000;
|
|
7
7
|
const DEFAULT_TOKEN_TTL_SECONDS = 31 * 24 * 60 * 60;
|
|
8
8
|
const DEFAULT_AUTH_MAX_REFRESH = 12 * 24 * 60 * 60;
|
|
9
9
|
const DEFAULT_MAX_CACHE_BYTES = 128_000_000; // 128 Mb
|
|
10
|
-
const DEFAULT_RETRY_BACKOFF_ALGORITHM =
|
|
10
|
+
const DEFAULT_RETRY_BACKOFF_ALGORITHM = "exponential";
|
|
11
11
|
const DEFAULT_RETRY_MAX_ATTEMPTS = 21; // 1st attempt + 20 retries
|
|
12
12
|
const DEFAULT_RETRY_INITIAL_DELAY = 20; // 20 ms * <jitter> of sleep after first failure
|
|
13
13
|
const DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER = 1.5; // + 50% on each round
|
|
14
14
|
const DEFAULT_RETRY_LINEAR_BACKOFF_STEP = 50; // + 50 ms
|
|
15
15
|
const DEFAULT_RETRY_JITTER = 0.3; // 30%
|
|
16
16
|
const DefaultRetryOptions = {
|
|
17
|
-
type:
|
|
17
|
+
type: "exponentialBackoff",
|
|
18
18
|
maxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,
|
|
19
19
|
initialDelay: DEFAULT_RETRY_INITIAL_DELAY,
|
|
20
20
|
backoffMultiplier: DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,
|
|
@@ -31,7 +31,7 @@ function parseInt(s) {
|
|
|
31
31
|
/** Parses pl url and creates a config object that can be passed to
|
|
32
32
|
* {@link PlClient} of {@link UnauthenticatedPlClient}. */
|
|
33
33
|
function plAddressToConfig(address, overrides = {}) {
|
|
34
|
-
if (address.indexOf(
|
|
34
|
+
if (address.indexOf("://") === -1)
|
|
35
35
|
// non-url address
|
|
36
36
|
return {
|
|
37
37
|
hostAndPort: address,
|
|
@@ -53,21 +53,21 @@ function plAddressToConfig(address, overrides = {}) {
|
|
|
53
53
|
...overrides,
|
|
54
54
|
};
|
|
55
55
|
const url = new URL(address);
|
|
56
|
-
if (url.protocol !==
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
if (url.protocol !== "https:" &&
|
|
57
|
+
url.protocol !== "http:" &&
|
|
58
|
+
url.protocol !== "grpc:" &&
|
|
59
|
+
url.protocol !== "tls:")
|
|
60
60
|
throw new Error(`Unexpected URL schema: ${url.protocol}`);
|
|
61
|
-
if (url.pathname !==
|
|
61
|
+
if (url.pathname !== "/" && url.pathname !== "")
|
|
62
62
|
throw new Error(`Unexpected URL path: ${url.pathname}`);
|
|
63
63
|
let port = url.port;
|
|
64
64
|
if (!port) {
|
|
65
65
|
switch (url.protocol) {
|
|
66
|
-
case
|
|
67
|
-
port =
|
|
66
|
+
case "http:":
|
|
67
|
+
port = "80";
|
|
68
68
|
break;
|
|
69
|
-
case
|
|
70
|
-
port =
|
|
69
|
+
case "https:":
|
|
70
|
+
port = "443";
|
|
71
71
|
break;
|
|
72
72
|
default:
|
|
73
73
|
throw new Error(`Port must be specified explicitly for ${url.protocol} protocol.`);
|
|
@@ -75,34 +75,34 @@ function plAddressToConfig(address, overrides = {}) {
|
|
|
75
75
|
}
|
|
76
76
|
return {
|
|
77
77
|
hostAndPort: `${url.hostname}:${port}`,
|
|
78
|
-
alternativeRoot: url.searchParams.get(
|
|
79
|
-
ssl: url.protocol ===
|
|
80
|
-
wireProtocol: url.searchParams.get(
|
|
81
|
-
defaultRequestTimeout: parseInt(url.searchParams.get(
|
|
82
|
-
defaultROTransactionTimeout: parseInt(url.searchParams.get(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
defaultRWTransactionTimeout: parseInt(url.searchParams.get(
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
alternativeRoot: url.searchParams.get("alternative-root") ?? undefined,
|
|
79
|
+
ssl: url.protocol === "https:" || url.protocol === "tls:",
|
|
80
|
+
wireProtocol: url.searchParams.get("wire-protocol") ?? undefined,
|
|
81
|
+
defaultRequestTimeout: parseInt(url.searchParams.get("request-timeout")) ?? DEFAULT_REQUEST_TIMEOUT,
|
|
82
|
+
defaultROTransactionTimeout: parseInt(url.searchParams.get("ro-tx-timeout")) ??
|
|
83
|
+
parseInt(url.searchParams.get("tx-timeout")) ??
|
|
84
|
+
DEFAULT_RO_TX_TIMEOUT,
|
|
85
|
+
defaultRWTransactionTimeout: parseInt(url.searchParams.get("rw-tx-timeout")) ??
|
|
86
|
+
parseInt(url.searchParams.get("tx-timeout")) ??
|
|
87
|
+
DEFAULT_RW_TX_TIMEOUT,
|
|
88
88
|
authTTLSeconds: DEFAULT_TOKEN_TTL_SECONDS,
|
|
89
89
|
authMaxRefreshSeconds: DEFAULT_AUTH_MAX_REFRESH,
|
|
90
|
-
grpcProxy: url.searchParams.get(
|
|
91
|
-
httpProxy: url.searchParams.get(
|
|
92
|
-
user: url.username ===
|
|
93
|
-
password: url.password ===
|
|
94
|
-
txDelay: parseInt(url.searchParams.get(
|
|
95
|
-
forceSync: Boolean(url.searchParams.get(
|
|
96
|
-
maxCacheBytes: parseInt(url.searchParams.get(
|
|
97
|
-
retryBackoffAlgorithm: (url.searchParams.get(
|
|
98
|
-
|
|
99
|
-
retryMaxAttempts: parseInt(url.searchParams.get(
|
|
100
|
-
retryInitialDelay: parseInt(url.searchParams.get(
|
|
101
|
-
retryExponentialBackoffMultiplier: parseInt(url.searchParams.get(
|
|
102
|
-
|
|
103
|
-
retryLinearBackoffStep: parseInt(url.searchParams.get(
|
|
104
|
-
|
|
105
|
-
retryJitter: parseInt(url.searchParams.get(
|
|
90
|
+
grpcProxy: url.searchParams.get("grpc-proxy") ?? undefined,
|
|
91
|
+
httpProxy: url.searchParams.get("http-proxy") ?? undefined,
|
|
92
|
+
user: url.username === "" ? undefined : url.username,
|
|
93
|
+
password: url.password === "" ? undefined : url.password,
|
|
94
|
+
txDelay: parseInt(url.searchParams.get("tx-delay")) ?? 0,
|
|
95
|
+
forceSync: Boolean(url.searchParams.get("force-sync")),
|
|
96
|
+
maxCacheBytes: parseInt(url.searchParams.get("max-cache-bytes")) ?? DEFAULT_MAX_CACHE_BYTES,
|
|
97
|
+
retryBackoffAlgorithm: (url.searchParams.get("retry-backoff-algorithm") ??
|
|
98
|
+
DEFAULT_RETRY_BACKOFF_ALGORITHM),
|
|
99
|
+
retryMaxAttempts: parseInt(url.searchParams.get("retry-max-attempts")) ?? DEFAULT_RETRY_MAX_ATTEMPTS,
|
|
100
|
+
retryInitialDelay: parseInt(url.searchParams.get("retry-initial-delay")) ?? DEFAULT_RETRY_INITIAL_DELAY,
|
|
101
|
+
retryExponentialBackoffMultiplier: parseInt(url.searchParams.get("retry-exp-backoff-multiplier")) ??
|
|
102
|
+
DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,
|
|
103
|
+
retryLinearBackoffStep: parseInt(url.searchParams.get("retry-linear-backoff-step")) ??
|
|
104
|
+
DEFAULT_RETRY_LINEAR_BACKOFF_STEP,
|
|
105
|
+
retryJitter: parseInt(url.searchParams.get("retry-backoff-jitter")) ?? DEFAULT_RETRY_JITTER,
|
|
106
106
|
...overrides,
|
|
107
107
|
};
|
|
108
108
|
}
|
package/dist/core/config.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs","sources":["../../src/core/config.ts"],"sourcesContent":["import type { ProxySettings } from '@milaboratories/pl-http';\nimport type { ExponentialBackoffRetryOptions } from '@milaboratories/ts-helpers';\n\nexport const SUPPORTED_WIRE_PROTOCOLS = ['grpc', 'rest'] as const;\nexport type wireProtocol = (typeof SUPPORTED_WIRE_PROTOCOLS)[number];\n\n/** Base configuration structure for PL client */\nexport interface PlClientConfig {\n /** Port and host of remote pl server */\n hostAndPort: string;\n\n /** Type of wire connection (protocol server uses for communication) */\n wireProtocol?: wireProtocol;\n\n /** If set, client will expose a nested object under a field with name `alternative_root_${alternativeRoot}` as a\n * client root. */\n alternativeRoot?: string;\n\n /** If true, client will establish tls connection to the server, using default\n * CA of node instance. */\n // Not implementing custom ssl validation logic for now.\n // Implementing it in a correct way is really nontrivial thing,\n // real use-cases should be considered.\n ssl: boolean;\n\n /** Default timeout in milliseconds for unary calls, like ping and login. */\n defaultRequestTimeout: number;\n\n /** Default timeout in milliseconds for read-write transaction, should be\n * adjusted for long round-trip or low bandwidth connections. */\n defaultRWTransactionTimeout: number;\n /** Default timeout in milliseconds for read-only transaction, should be\n * adjusted for long round-trip or low bandwidth connections. */\n defaultROTransactionTimeout: number;\n\n /** Controls what TTL will be requested from the server, when new JWT token\n * is requested. */\n authTTLSeconds: number;\n /** If token is older than this time, it will be refreshed regardless of its\n * expiration time. */\n authMaxRefreshSeconds: number;\n\n /** Proxy server config to use for pl connection. */\n grpcProxy?: string | ProxySettings;\n /** Proxy server config to use for http connections of pl drivers, like file\n * downloading. */\n httpProxy?: string | ProxySettings;\n\n /** Username extracted from pl URL. Ignored by {@link PlClient}, picked up by {@link defaultPlClient}. */\n user?: string;\n /** Password extracted from pl URL. Ignored by {@link PlClient}, picked up by {@link defaultPlClient}. */\n password?: string;\n\n /** Artificial delay introduced after write transactions completion, to\n * somewhat throttle the load on pl. Delay introduced after sync, if requested. */\n txDelay: number;\n\n /** Last resort measure to solve complicated race conditions in pl. */\n forceSync: boolean;\n\n /** Maximal number of bytes of resource state to cache */\n maxCacheBytes: number;\n\n //\n // Retry\n //\n\n /**\n * What type of backoff strategy to use in transaction retries\n * (pl uses optimistic transaction model with regular retries in write transactions)\n */\n retryBackoffAlgorithm: 'exponential' | 'linear';\n\n /** Maximal number of attempts in */\n retryMaxAttempts: number;\n\n /** Delay after first failed attempt, in ms. */\n retryInitialDelay: number;\n\n /** Each time delay will be multiplied by this number (1.5 means plus on 50% each attempt) */\n retryExponentialBackoffMultiplier: number;\n\n /** [used only for ] This value will be added to the delay from the previous step, in ms */\n retryLinearBackoffStep: number;\n\n /** Value from 0 to 1, determine level of randomness to introduce to the backoff delays sequence. (0 meaning no randomness) */\n retryJitter: number;\n}\n\nexport const DEFAULT_REQUEST_TIMEOUT = 5_000;\nexport const DEFAULT_RO_TX_TIMEOUT = 300_000;\nexport const DEFAULT_RW_TX_TIMEOUT = 60_000;\nexport const DEFAULT_TOKEN_TTL_SECONDS = 31 * 24 * 60 * 60;\nexport const DEFAULT_AUTH_MAX_REFRESH = 12 * 24 * 60 * 60;\n\nexport const DEFAULT_MAX_CACHE_BYTES = 128_000_000; // 128 Mb\n\nexport const DEFAULT_RETRY_BACKOFF_ALGORITHM = 'exponential';\nexport const DEFAULT_RETRY_MAX_ATTEMPTS = 21; // 1st attempt + 20 retries\nexport const DEFAULT_RETRY_INITIAL_DELAY = 20; // 20 ms * <jitter> of sleep after first failure\nexport const DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER = 1.5; // + 50% on each round\nexport const DEFAULT_RETRY_LINEAR_BACKOFF_STEP = 50; // + 50 ms\nexport const DEFAULT_RETRY_JITTER = 0.3; // 30%\n\nexport const DefaultRetryOptions: ExponentialBackoffRetryOptions = {\n type: 'exponentialBackoff',\n maxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,\n initialDelay: DEFAULT_RETRY_INITIAL_DELAY,\n backoffMultiplier: DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,\n jitter: DEFAULT_RETRY_JITTER,\n};\n\ntype PlConfigOverrides = Partial<\n Pick<\n PlClientConfig,\n | 'ssl'\n | 'defaultRequestTimeout'\n | 'defaultROTransactionTimeout'\n | 'defaultRWTransactionTimeout'\n | 'httpProxy'\n | 'grpcProxy'\n >\n>;\n\nfunction parseInt(s: string | null | undefined): number | undefined {\n if (!s) return undefined;\n const num = Number(s);\n if (Number.isNaN(num)) throw new Error(`Can't parse number: ${s}`);\n return num;\n}\n\n/** Parses pl url and creates a config object that can be passed to\n * {@link PlClient} of {@link UnauthenticatedPlClient}. */\nexport function plAddressToConfig(\n address: string,\n overrides: PlConfigOverrides = {},\n): PlClientConfig {\n if (address.indexOf('://') === -1)\n // non-url address\n return {\n hostAndPort: address,\n ssl: false,\n defaultRequestTimeout: DEFAULT_REQUEST_TIMEOUT,\n defaultROTransactionTimeout: DEFAULT_RO_TX_TIMEOUT,\n defaultRWTransactionTimeout: DEFAULT_RW_TX_TIMEOUT,\n authTTLSeconds: DEFAULT_TOKEN_TTL_SECONDS,\n authMaxRefreshSeconds: DEFAULT_AUTH_MAX_REFRESH,\n txDelay: 0,\n forceSync: false,\n\n maxCacheBytes: DEFAULT_MAX_CACHE_BYTES,\n\n retryBackoffAlgorithm: DEFAULT_RETRY_BACKOFF_ALGORITHM,\n retryMaxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay: DEFAULT_RETRY_INITIAL_DELAY,\n retryExponentialBackoffMultiplier: DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,\n retryLinearBackoffStep: DEFAULT_RETRY_LINEAR_BACKOFF_STEP,\n retryJitter: DEFAULT_RETRY_JITTER,\n\n ...overrides,\n };\n\n const url = new URL(address);\n\n if (\n url.protocol !== 'https:'\n && url.protocol !== 'http:'\n && url.protocol !== 'grpc:'\n && url.protocol !== 'tls:'\n )\n throw new Error(`Unexpected URL schema: ${url.protocol}`);\n\n if (url.pathname !== '/' && url.pathname !== '')\n throw new Error(`Unexpected URL path: ${url.pathname}`);\n\n let port = url.port;\n if (!port) {\n switch (url.protocol) {\n case 'http:':\n port = '80';\n break;\n case 'https:':\n port = '443';\n break;\n default:\n throw new Error(`Port must be specified explicitly for ${url.protocol} protocol.`);\n }\n }\n\n return {\n hostAndPort: `${url.hostname}:${port}`,\n alternativeRoot: url.searchParams.get('alternative-root') ?? undefined,\n ssl: url.protocol === 'https:' || url.protocol === 'tls:',\n\n wireProtocol: url.searchParams.get('wire-protocol') as wireProtocol ?? undefined,\n\n defaultRequestTimeout:\n parseInt(url.searchParams.get('request-timeout')) ?? DEFAULT_REQUEST_TIMEOUT,\n defaultROTransactionTimeout:\n parseInt(url.searchParams.get('ro-tx-timeout'))\n ?? parseInt(url.searchParams.get('tx-timeout'))\n ?? DEFAULT_RO_TX_TIMEOUT,\n defaultRWTransactionTimeout:\n parseInt(url.searchParams.get('rw-tx-timeout'))\n ?? parseInt(url.searchParams.get('tx-timeout'))\n ?? DEFAULT_RW_TX_TIMEOUT,\n authTTLSeconds: DEFAULT_TOKEN_TTL_SECONDS,\n authMaxRefreshSeconds: DEFAULT_AUTH_MAX_REFRESH,\n grpcProxy: url.searchParams.get('grpc-proxy') ?? undefined,\n httpProxy: url.searchParams.get('http-proxy') ?? undefined,\n user: url.username === '' ? undefined : url.username,\n password: url.password === '' ? undefined : url.password,\n txDelay: parseInt(url.searchParams.get('tx-delay')) ?? 0,\n forceSync: Boolean(url.searchParams.get('force-sync')),\n\n maxCacheBytes: parseInt(url.searchParams.get('max-cache-bytes')) ?? DEFAULT_MAX_CACHE_BYTES,\n\n retryBackoffAlgorithm: (url.searchParams.get('retry-backoff-algorithm')\n ?? DEFAULT_RETRY_BACKOFF_ALGORITHM) as any,\n retryMaxAttempts:\n parseInt(url.searchParams.get('retry-max-attempts')) ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n parseInt(url.searchParams.get('retry-initial-delay')) ?? DEFAULT_RETRY_INITIAL_DELAY,\n retryExponentialBackoffMultiplier:\n parseInt(url.searchParams.get('retry-exp-backoff-multiplier'))\n ?? DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,\n retryLinearBackoffStep:\n parseInt(url.searchParams.get('retry-linear-backoff-step'))\n ?? DEFAULT_RETRY_LINEAR_BACKOFF_STEP,\n retryJitter: parseInt(url.searchParams.get('retry-backoff-jitter')) ?? DEFAULT_RETRY_JITTER,\n\n ...overrides,\n };\n}\n\n/**\n * Authorization data / JWT Token.\n * Absent JWT Token tells the client to connect as anonymous user.\n */\nexport interface AuthInformation {\n /** Absent token means anonymous access */\n jwtToken?: string;\n}\n\nexport const AnonymousAuthInformation: AuthInformation = {};\n\n/** Authorization related settings to pass to {@link PlClient}. */\nexport interface AuthOps {\n /** Initial authorization information */\n authInformation: AuthInformation;\n /** Will be executed after successful authorization information refresh */\n readonly onUpdate?: (newInfo: AuthInformation) => void;\n /** Will be executed if auth-related error happens during normal client operation */\n readonly onAuthError?: () => void;\n /** Will be executed if error encountered during token update */\n readonly onUpdateError?: (error: unknown) => void;\n}\n\n/** Connection status. */\nexport type PlConnectionStatus = 'OK' | 'Disconnected' | 'Unauthenticated';\n\n/** Listener that will be called each time connection status changes. */\nexport type PlConnectionStatusListener = (status: PlConnectionStatus) => void;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.cjs","sources":["../../src/core/config.ts"],"sourcesContent":["import type { ProxySettings } from \"@milaboratories/pl-http\";\nimport type { ExponentialBackoffRetryOptions } from \"@milaboratories/ts-helpers\";\n\nexport const SUPPORTED_WIRE_PROTOCOLS = [\"grpc\", \"rest\"] as const;\nexport type wireProtocol = (typeof SUPPORTED_WIRE_PROTOCOLS)[number];\n\n/** Base configuration structure for PL client */\nexport interface PlClientConfig {\n /** Port and host of remote pl server */\n hostAndPort: string;\n\n /** Type of wire connection (protocol server uses for communication) */\n wireProtocol?: wireProtocol;\n\n /** If set, client will expose a nested object under a field with name `alternative_root_${alternativeRoot}` as a\n * client root. */\n alternativeRoot?: string;\n\n /** If true, client will establish tls connection to the server, using default\n * CA of node instance. */\n // Not implementing custom ssl validation logic for now.\n // Implementing it in a correct way is really nontrivial thing,\n // real use-cases should be considered.\n ssl: boolean;\n\n /** Default timeout in milliseconds for unary calls, like ping and login. */\n defaultRequestTimeout: number;\n\n /** Default timeout in milliseconds for read-write transaction, should be\n * adjusted for long round-trip or low bandwidth connections. */\n defaultRWTransactionTimeout: number;\n /** Default timeout in milliseconds for read-only transaction, should be\n * adjusted for long round-trip or low bandwidth connections. */\n defaultROTransactionTimeout: number;\n\n /** Controls what TTL will be requested from the server, when new JWT token\n * is requested. */\n authTTLSeconds: number;\n /** If token is older than this time, it will be refreshed regardless of its\n * expiration time. */\n authMaxRefreshSeconds: number;\n\n /** Proxy server config to use for pl connection. */\n grpcProxy?: string | ProxySettings;\n /** Proxy server config to use for http connections of pl drivers, like file\n * downloading. */\n httpProxy?: string | ProxySettings;\n\n /** Username extracted from pl URL. Ignored by {@link PlClient}, picked up by {@link defaultPlClient}. */\n user?: string;\n /** Password extracted from pl URL. Ignored by {@link PlClient}, picked up by {@link defaultPlClient}. */\n password?: string;\n\n /** Artificial delay introduced after write transactions completion, to\n * somewhat throttle the load on pl. Delay introduced after sync, if requested. */\n txDelay: number;\n\n /** Last resort measure to solve complicated race conditions in pl. */\n forceSync: boolean;\n\n /** Maximal number of bytes of resource state to cache */\n maxCacheBytes: number;\n\n //\n // Retry\n //\n\n /**\n * What type of backoff strategy to use in transaction retries\n * (pl uses optimistic transaction model with regular retries in write transactions)\n */\n retryBackoffAlgorithm: \"exponential\" | \"linear\";\n\n /** Maximal number of attempts in */\n retryMaxAttempts: number;\n\n /** Delay after first failed attempt, in ms. */\n retryInitialDelay: number;\n\n /** Each time delay will be multiplied by this number (1.5 means plus on 50% each attempt) */\n retryExponentialBackoffMultiplier: number;\n\n /** [used only for ] This value will be added to the delay from the previous step, in ms */\n retryLinearBackoffStep: number;\n\n /** Value from 0 to 1, determine level of randomness to introduce to the backoff delays sequence. (0 meaning no randomness) */\n retryJitter: number;\n}\n\nexport const DEFAULT_REQUEST_TIMEOUT = 5_000;\nexport const DEFAULT_RO_TX_TIMEOUT = 300_000;\nexport const DEFAULT_RW_TX_TIMEOUT = 60_000;\nexport const DEFAULT_TOKEN_TTL_SECONDS = 31 * 24 * 60 * 60;\nexport const DEFAULT_AUTH_MAX_REFRESH = 12 * 24 * 60 * 60;\n\nexport const DEFAULT_MAX_CACHE_BYTES = 128_000_000; // 128 Mb\n\nexport const DEFAULT_RETRY_BACKOFF_ALGORITHM = \"exponential\";\nexport const DEFAULT_RETRY_MAX_ATTEMPTS = 21; // 1st attempt + 20 retries\nexport const DEFAULT_RETRY_INITIAL_DELAY = 20; // 20 ms * <jitter> of sleep after first failure\nexport const DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER = 1.5; // + 50% on each round\nexport const DEFAULT_RETRY_LINEAR_BACKOFF_STEP = 50; // + 50 ms\nexport const DEFAULT_RETRY_JITTER = 0.3; // 30%\n\nexport const DefaultRetryOptions: ExponentialBackoffRetryOptions = {\n type: \"exponentialBackoff\",\n maxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,\n initialDelay: DEFAULT_RETRY_INITIAL_DELAY,\n backoffMultiplier: DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,\n jitter: DEFAULT_RETRY_JITTER,\n};\n\ntype PlConfigOverrides = Partial<\n Pick<\n PlClientConfig,\n | \"ssl\"\n | \"defaultRequestTimeout\"\n | \"defaultROTransactionTimeout\"\n | \"defaultRWTransactionTimeout\"\n | \"httpProxy\"\n | \"grpcProxy\"\n >\n>;\n\nfunction parseInt(s: string | null | undefined): number | undefined {\n if (!s) return undefined;\n const num = Number(s);\n if (Number.isNaN(num)) throw new Error(`Can't parse number: ${s}`);\n return num;\n}\n\n/** Parses pl url and creates a config object that can be passed to\n * {@link PlClient} of {@link UnauthenticatedPlClient}. */\nexport function plAddressToConfig(\n address: string,\n overrides: PlConfigOverrides = {},\n): PlClientConfig {\n if (address.indexOf(\"://\") === -1)\n // non-url address\n return {\n hostAndPort: address,\n ssl: false,\n defaultRequestTimeout: DEFAULT_REQUEST_TIMEOUT,\n defaultROTransactionTimeout: DEFAULT_RO_TX_TIMEOUT,\n defaultRWTransactionTimeout: DEFAULT_RW_TX_TIMEOUT,\n authTTLSeconds: DEFAULT_TOKEN_TTL_SECONDS,\n authMaxRefreshSeconds: DEFAULT_AUTH_MAX_REFRESH,\n txDelay: 0,\n forceSync: false,\n\n maxCacheBytes: DEFAULT_MAX_CACHE_BYTES,\n\n retryBackoffAlgorithm: DEFAULT_RETRY_BACKOFF_ALGORITHM,\n retryMaxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay: DEFAULT_RETRY_INITIAL_DELAY,\n retryExponentialBackoffMultiplier: DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,\n retryLinearBackoffStep: DEFAULT_RETRY_LINEAR_BACKOFF_STEP,\n retryJitter: DEFAULT_RETRY_JITTER,\n\n ...overrides,\n };\n\n const url = new URL(address);\n\n if (\n url.protocol !== \"https:\" &&\n url.protocol !== \"http:\" &&\n url.protocol !== \"grpc:\" &&\n url.protocol !== \"tls:\"\n )\n throw new Error(`Unexpected URL schema: ${url.protocol}`);\n\n if (url.pathname !== \"/\" && url.pathname !== \"\")\n throw new Error(`Unexpected URL path: ${url.pathname}`);\n\n let port = url.port;\n if (!port) {\n switch (url.protocol) {\n case \"http:\":\n port = \"80\";\n break;\n case \"https:\":\n port = \"443\";\n break;\n default:\n throw new Error(`Port must be specified explicitly for ${url.protocol} protocol.`);\n }\n }\n\n return {\n hostAndPort: `${url.hostname}:${port}`,\n alternativeRoot: url.searchParams.get(\"alternative-root\") ?? undefined,\n ssl: url.protocol === \"https:\" || url.protocol === \"tls:\",\n\n wireProtocol: (url.searchParams.get(\"wire-protocol\") as wireProtocol) ?? undefined,\n\n defaultRequestTimeout:\n parseInt(url.searchParams.get(\"request-timeout\")) ?? DEFAULT_REQUEST_TIMEOUT,\n defaultROTransactionTimeout:\n parseInt(url.searchParams.get(\"ro-tx-timeout\")) ??\n parseInt(url.searchParams.get(\"tx-timeout\")) ??\n DEFAULT_RO_TX_TIMEOUT,\n defaultRWTransactionTimeout:\n parseInt(url.searchParams.get(\"rw-tx-timeout\")) ??\n parseInt(url.searchParams.get(\"tx-timeout\")) ??\n DEFAULT_RW_TX_TIMEOUT,\n authTTLSeconds: DEFAULT_TOKEN_TTL_SECONDS,\n authMaxRefreshSeconds: DEFAULT_AUTH_MAX_REFRESH,\n grpcProxy: url.searchParams.get(\"grpc-proxy\") ?? undefined,\n httpProxy: url.searchParams.get(\"http-proxy\") ?? undefined,\n user: url.username === \"\" ? undefined : url.username,\n password: url.password === \"\" ? undefined : url.password,\n txDelay: parseInt(url.searchParams.get(\"tx-delay\")) ?? 0,\n forceSync: Boolean(url.searchParams.get(\"force-sync\")),\n\n maxCacheBytes: parseInt(url.searchParams.get(\"max-cache-bytes\")) ?? DEFAULT_MAX_CACHE_BYTES,\n\n retryBackoffAlgorithm: (url.searchParams.get(\"retry-backoff-algorithm\") ??\n DEFAULT_RETRY_BACKOFF_ALGORITHM) as any,\n retryMaxAttempts:\n parseInt(url.searchParams.get(\"retry-max-attempts\")) ?? DEFAULT_RETRY_MAX_ATTEMPTS,\n retryInitialDelay:\n parseInt(url.searchParams.get(\"retry-initial-delay\")) ?? DEFAULT_RETRY_INITIAL_DELAY,\n retryExponentialBackoffMultiplier:\n parseInt(url.searchParams.get(\"retry-exp-backoff-multiplier\")) ??\n DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,\n retryLinearBackoffStep:\n parseInt(url.searchParams.get(\"retry-linear-backoff-step\")) ??\n DEFAULT_RETRY_LINEAR_BACKOFF_STEP,\n retryJitter: parseInt(url.searchParams.get(\"retry-backoff-jitter\")) ?? DEFAULT_RETRY_JITTER,\n\n ...overrides,\n };\n}\n\n/**\n * Authorization data / JWT Token.\n * Absent JWT Token tells the client to connect as anonymous user.\n */\nexport interface AuthInformation {\n /** Absent token means anonymous access */\n jwtToken?: string;\n}\n\nexport const AnonymousAuthInformation: AuthInformation = {};\n\n/** Authorization related settings to pass to {@link PlClient}. */\nexport interface AuthOps {\n /** Initial authorization information */\n authInformation: AuthInformation;\n /** Will be executed after successful authorization information refresh */\n readonly onUpdate?: (newInfo: AuthInformation) => void;\n /** Will be executed if auth-related error happens during normal client operation */\n readonly onAuthError?: () => void;\n /** Will be executed if error encountered during token update */\n readonly onUpdateError?: (error: unknown) => void;\n}\n\n/** Connection status. */\nexport type PlConnectionStatus = \"OK\" | \"Disconnected\" | \"Unauthenticated\";\n\n/** Listener that will be called each time connection status changes. */\nexport type PlConnectionStatusListener = (status: PlConnectionStatus) => void;\n"],"names":[],"mappings":";;MAGa,wBAAwB,GAAG,CAAC,MAAM,EAAE,MAAM;AAsFhD,MAAM,uBAAuB,GAAG;AAChC,MAAM,qBAAqB,GAAG;AAC9B,MAAM,qBAAqB,GAAG;AAC9B,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;AACjD,MAAM,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;AAEhD,MAAM,uBAAuB,GAAG,YAAY;AAE5C,MAAM,+BAA+B,GAAG;AACxC,MAAM,0BAA0B,GAAG,GAAG;AACtC,MAAM,2BAA2B,GAAG,GAAG;AACvC,MAAM,4CAA4C,GAAG,IAAI;AACzD,MAAM,iCAAiC,GAAG,GAAG;AAC7C,MAAM,oBAAoB,GAAG,IAAI;AAEjC,MAAM,mBAAmB,GAAmC;AACjE,IAAA,IAAI,EAAE,oBAAoB;AAC1B,IAAA,WAAW,EAAE,0BAA0B;AACvC,IAAA,YAAY,EAAE,2BAA2B;AACzC,IAAA,iBAAiB,EAAE,4CAA4C;AAC/D,IAAA,MAAM,EAAE,oBAAoB;;AAe9B,SAAS,QAAQ,CAAC,CAA4B,EAAA;AAC5C,IAAA,IAAI,CAAC,CAAC;AAAE,QAAA,OAAO,SAAS;AACxB,IAAA,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC;AACrB,IAAA,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;AAAE,QAAA,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA,CAAE,CAAC;AAClE,IAAA,OAAO,GAAG;AACZ;AAEA;AAC0D;SAC1C,iBAAiB,CAC/B,OAAe,EACf,YAA+B,EAAE,EAAA;IAEjC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE;;QAE/B,OAAO;AACL,YAAA,WAAW,EAAE,OAAO;AACpB,YAAA,GAAG,EAAE,KAAK;AACV,YAAA,qBAAqB,EAAE,uBAAuB;AAC9C,YAAA,2BAA2B,EAAE,qBAAqB;AAClD,YAAA,2BAA2B,EAAE,qBAAqB;AAClD,YAAA,cAAc,EAAE,yBAAyB;AACzC,YAAA,qBAAqB,EAAE,wBAAwB;AAC/C,YAAA,OAAO,EAAE,CAAC;AACV,YAAA,SAAS,EAAE,KAAK;AAEhB,YAAA,aAAa,EAAE,uBAAuB;AAEtC,YAAA,qBAAqB,EAAE,+BAA+B;AACtD,YAAA,gBAAgB,EAAE,0BAA0B;AAC5C,YAAA,iBAAiB,EAAE,2BAA2B;AAC9C,YAAA,iCAAiC,EAAE,4CAA4C;AAC/E,YAAA,sBAAsB,EAAE,iCAAiC;AACzD,YAAA,WAAW,EAAE,oBAAoB;AAEjC,YAAA,GAAG,SAAS;SACb;AAEH,IAAA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC;AAE5B,IAAA,IACE,GAAG,CAAC,QAAQ,KAAK,QAAQ;QACzB,GAAG,CAAC,QAAQ,KAAK,OAAO;QACxB,GAAG,CAAC,QAAQ,KAAK,OAAO;QACxB,GAAG,CAAC,QAAQ,KAAK,MAAM;QAEvB,MAAM,IAAI,KAAK,CAAC,CAAA,uBAAA,EAA0B,GAAG,CAAC,QAAQ,CAAA,CAAE,CAAC;IAE3D,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE;QAC7C,MAAM,IAAI,KAAK,CAAC,CAAA,qBAAA,EAAwB,GAAG,CAAC,QAAQ,CAAA,CAAE,CAAC;AAEzD,IAAA,IAAI,IAAI,GAAG,GAAG,CAAC,IAAI;IACnB,IAAI,CAAC,IAAI,EAAE;AACT,QAAA,QAAQ,GAAG,CAAC,QAAQ;AAClB,YAAA,KAAK,OAAO;gBACV,IAAI,GAAG,IAAI;gBACX;AACF,YAAA,KAAK,QAAQ;gBACX,IAAI,GAAG,KAAK;gBACZ;AACF,YAAA;gBACE,MAAM,IAAI,KAAK,CAAC,CAAA,sCAAA,EAAyC,GAAG,CAAC,QAAQ,CAAA,UAAA,CAAY,CAAC;;IAExF;IAEA,OAAO;AACL,QAAA,WAAW,EAAE,CAAA,EAAG,GAAG,CAAC,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAE;QACtC,eAAe,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,SAAS;QACtE,GAAG,EAAE,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM;QAEzD,YAAY,EAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAkB,IAAI,SAAS;AAElF,QAAA,qBAAqB,EACnB,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,uBAAuB;QAC9E,2BAA2B,EACzB,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC/C,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC5C,qBAAqB;QACvB,2BAA2B,EACzB,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC/C,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAC5C,qBAAqB;AACvB,QAAA,cAAc,EAAE,yBAAyB;AACzC,QAAA,qBAAqB,EAAE,wBAAwB;QAC/C,SAAS,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,SAAS;QAC1D,SAAS,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,SAAS;AAC1D,QAAA,IAAI,EAAE,GAAG,CAAC,QAAQ,KAAK,EAAE,GAAG,SAAS,GAAG,GAAG,CAAC,QAAQ;AACpD,QAAA,QAAQ,EAAE,GAAG,CAAC,QAAQ,KAAK,EAAE,GAAG,SAAS,GAAG,GAAG,CAAC,QAAQ;AACxD,QAAA,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;QACxD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtD,QAAA,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,uBAAuB;QAE3F,qBAAqB,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,yBAAyB,CAAC;AACrE,YAAA,+BAA+B,CAAQ;AACzC,QAAA,gBAAgB,EACd,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,IAAI,0BAA0B;AACpF,QAAA,iBAAiB,EACf,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,IAAI,2BAA2B;QACtF,iCAAiC,EAC/B,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;YAC9D,4CAA4C;QAC9C,sBAAsB,EACpB,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YAC3D,iCAAiC;AACnC,QAAA,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,IAAI,oBAAoB;AAE3F,QAAA,GAAG,SAAS;KACb;AACH;AAWO,MAAM,wBAAwB,GAAoB;;;;;;;;;;;;;;;;;;;"}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ProxySettings } from
|
|
2
|
-
import type { ExponentialBackoffRetryOptions } from
|
|
1
|
+
import type { ProxySettings } from "@milaboratories/pl-http";
|
|
2
|
+
import type { ExponentialBackoffRetryOptions } from "@milaboratories/ts-helpers";
|
|
3
3
|
export declare const SUPPORTED_WIRE_PROTOCOLS: readonly ["grpc", "rest"];
|
|
4
4
|
export type wireProtocol = (typeof SUPPORTED_WIRE_PROTOCOLS)[number];
|
|
5
5
|
/** Base configuration structure for PL client */
|
|
@@ -48,7 +48,7 @@ export interface PlClientConfig {
|
|
|
48
48
|
* What type of backoff strategy to use in transaction retries
|
|
49
49
|
* (pl uses optimistic transaction model with regular retries in write transactions)
|
|
50
50
|
*/
|
|
51
|
-
retryBackoffAlgorithm:
|
|
51
|
+
retryBackoffAlgorithm: "exponential" | "linear";
|
|
52
52
|
/** Maximal number of attempts in */
|
|
53
53
|
retryMaxAttempts: number;
|
|
54
54
|
/** Delay after first failed attempt, in ms. */
|
|
@@ -73,7 +73,7 @@ export declare const DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER = 1.5;
|
|
|
73
73
|
export declare const DEFAULT_RETRY_LINEAR_BACKOFF_STEP = 50;
|
|
74
74
|
export declare const DEFAULT_RETRY_JITTER = 0.3;
|
|
75
75
|
export declare const DefaultRetryOptions: ExponentialBackoffRetryOptions;
|
|
76
|
-
type PlConfigOverrides = Partial<Pick<PlClientConfig,
|
|
76
|
+
type PlConfigOverrides = Partial<Pick<PlClientConfig, "ssl" | "defaultRequestTimeout" | "defaultROTransactionTimeout" | "defaultRWTransactionTimeout" | "httpProxy" | "grpcProxy">>;
|
|
77
77
|
/** Parses pl url and creates a config object that can be passed to
|
|
78
78
|
* {@link PlClient} of {@link UnauthenticatedPlClient}. */
|
|
79
79
|
export declare function plAddressToConfig(address: string, overrides?: PlConfigOverrides): PlClientConfig;
|
|
@@ -98,7 +98,7 @@ export interface AuthOps {
|
|
|
98
98
|
readonly onUpdateError?: (error: unknown) => void;
|
|
99
99
|
}
|
|
100
100
|
/** Connection status. */
|
|
101
|
-
export type PlConnectionStatus =
|
|
101
|
+
export type PlConnectionStatus = "OK" | "Disconnected" | "Unauthenticated";
|
|
102
102
|
/** Listener that will be called each time connection status changes. */
|
|
103
103
|
export type PlConnectionStatusListener = (status: PlConnectionStatus) => void;
|
|
104
104
|
export {};
|
package/dist/core/config.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
const SUPPORTED_WIRE_PROTOCOLS = [
|
|
1
|
+
const SUPPORTED_WIRE_PROTOCOLS = ["grpc", "rest"];
|
|
2
2
|
const DEFAULT_REQUEST_TIMEOUT = 5_000;
|
|
3
3
|
const DEFAULT_RO_TX_TIMEOUT = 300_000;
|
|
4
4
|
const DEFAULT_RW_TX_TIMEOUT = 60_000;
|
|
5
5
|
const DEFAULT_TOKEN_TTL_SECONDS = 31 * 24 * 60 * 60;
|
|
6
6
|
const DEFAULT_AUTH_MAX_REFRESH = 12 * 24 * 60 * 60;
|
|
7
7
|
const DEFAULT_MAX_CACHE_BYTES = 128_000_000; // 128 Mb
|
|
8
|
-
const DEFAULT_RETRY_BACKOFF_ALGORITHM =
|
|
8
|
+
const DEFAULT_RETRY_BACKOFF_ALGORITHM = "exponential";
|
|
9
9
|
const DEFAULT_RETRY_MAX_ATTEMPTS = 21; // 1st attempt + 20 retries
|
|
10
10
|
const DEFAULT_RETRY_INITIAL_DELAY = 20; // 20 ms * <jitter> of sleep after first failure
|
|
11
11
|
const DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER = 1.5; // + 50% on each round
|
|
12
12
|
const DEFAULT_RETRY_LINEAR_BACKOFF_STEP = 50; // + 50 ms
|
|
13
13
|
const DEFAULT_RETRY_JITTER = 0.3; // 30%
|
|
14
14
|
const DefaultRetryOptions = {
|
|
15
|
-
type:
|
|
15
|
+
type: "exponentialBackoff",
|
|
16
16
|
maxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,
|
|
17
17
|
initialDelay: DEFAULT_RETRY_INITIAL_DELAY,
|
|
18
18
|
backoffMultiplier: DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,
|
|
@@ -29,7 +29,7 @@ function parseInt(s) {
|
|
|
29
29
|
/** Parses pl url and creates a config object that can be passed to
|
|
30
30
|
* {@link PlClient} of {@link UnauthenticatedPlClient}. */
|
|
31
31
|
function plAddressToConfig(address, overrides = {}) {
|
|
32
|
-
if (address.indexOf(
|
|
32
|
+
if (address.indexOf("://") === -1)
|
|
33
33
|
// non-url address
|
|
34
34
|
return {
|
|
35
35
|
hostAndPort: address,
|
|
@@ -51,21 +51,21 @@ function plAddressToConfig(address, overrides = {}) {
|
|
|
51
51
|
...overrides,
|
|
52
52
|
};
|
|
53
53
|
const url = new URL(address);
|
|
54
|
-
if (url.protocol !==
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
if (url.protocol !== "https:" &&
|
|
55
|
+
url.protocol !== "http:" &&
|
|
56
|
+
url.protocol !== "grpc:" &&
|
|
57
|
+
url.protocol !== "tls:")
|
|
58
58
|
throw new Error(`Unexpected URL schema: ${url.protocol}`);
|
|
59
|
-
if (url.pathname !==
|
|
59
|
+
if (url.pathname !== "/" && url.pathname !== "")
|
|
60
60
|
throw new Error(`Unexpected URL path: ${url.pathname}`);
|
|
61
61
|
let port = url.port;
|
|
62
62
|
if (!port) {
|
|
63
63
|
switch (url.protocol) {
|
|
64
|
-
case
|
|
65
|
-
port =
|
|
64
|
+
case "http:":
|
|
65
|
+
port = "80";
|
|
66
66
|
break;
|
|
67
|
-
case
|
|
68
|
-
port =
|
|
67
|
+
case "https:":
|
|
68
|
+
port = "443";
|
|
69
69
|
break;
|
|
70
70
|
default:
|
|
71
71
|
throw new Error(`Port must be specified explicitly for ${url.protocol} protocol.`);
|
|
@@ -73,34 +73,34 @@ function plAddressToConfig(address, overrides = {}) {
|
|
|
73
73
|
}
|
|
74
74
|
return {
|
|
75
75
|
hostAndPort: `${url.hostname}:${port}`,
|
|
76
|
-
alternativeRoot: url.searchParams.get(
|
|
77
|
-
ssl: url.protocol ===
|
|
78
|
-
wireProtocol: url.searchParams.get(
|
|
79
|
-
defaultRequestTimeout: parseInt(url.searchParams.get(
|
|
80
|
-
defaultROTransactionTimeout: parseInt(url.searchParams.get(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
defaultRWTransactionTimeout: parseInt(url.searchParams.get(
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
alternativeRoot: url.searchParams.get("alternative-root") ?? undefined,
|
|
77
|
+
ssl: url.protocol === "https:" || url.protocol === "tls:",
|
|
78
|
+
wireProtocol: url.searchParams.get("wire-protocol") ?? undefined,
|
|
79
|
+
defaultRequestTimeout: parseInt(url.searchParams.get("request-timeout")) ?? DEFAULT_REQUEST_TIMEOUT,
|
|
80
|
+
defaultROTransactionTimeout: parseInt(url.searchParams.get("ro-tx-timeout")) ??
|
|
81
|
+
parseInt(url.searchParams.get("tx-timeout")) ??
|
|
82
|
+
DEFAULT_RO_TX_TIMEOUT,
|
|
83
|
+
defaultRWTransactionTimeout: parseInt(url.searchParams.get("rw-tx-timeout")) ??
|
|
84
|
+
parseInt(url.searchParams.get("tx-timeout")) ??
|
|
85
|
+
DEFAULT_RW_TX_TIMEOUT,
|
|
86
86
|
authTTLSeconds: DEFAULT_TOKEN_TTL_SECONDS,
|
|
87
87
|
authMaxRefreshSeconds: DEFAULT_AUTH_MAX_REFRESH,
|
|
88
|
-
grpcProxy: url.searchParams.get(
|
|
89
|
-
httpProxy: url.searchParams.get(
|
|
90
|
-
user: url.username ===
|
|
91
|
-
password: url.password ===
|
|
92
|
-
txDelay: parseInt(url.searchParams.get(
|
|
93
|
-
forceSync: Boolean(url.searchParams.get(
|
|
94
|
-
maxCacheBytes: parseInt(url.searchParams.get(
|
|
95
|
-
retryBackoffAlgorithm: (url.searchParams.get(
|
|
96
|
-
|
|
97
|
-
retryMaxAttempts: parseInt(url.searchParams.get(
|
|
98
|
-
retryInitialDelay: parseInt(url.searchParams.get(
|
|
99
|
-
retryExponentialBackoffMultiplier: parseInt(url.searchParams.get(
|
|
100
|
-
|
|
101
|
-
retryLinearBackoffStep: parseInt(url.searchParams.get(
|
|
102
|
-
|
|
103
|
-
retryJitter: parseInt(url.searchParams.get(
|
|
88
|
+
grpcProxy: url.searchParams.get("grpc-proxy") ?? undefined,
|
|
89
|
+
httpProxy: url.searchParams.get("http-proxy") ?? undefined,
|
|
90
|
+
user: url.username === "" ? undefined : url.username,
|
|
91
|
+
password: url.password === "" ? undefined : url.password,
|
|
92
|
+
txDelay: parseInt(url.searchParams.get("tx-delay")) ?? 0,
|
|
93
|
+
forceSync: Boolean(url.searchParams.get("force-sync")),
|
|
94
|
+
maxCacheBytes: parseInt(url.searchParams.get("max-cache-bytes")) ?? DEFAULT_MAX_CACHE_BYTES,
|
|
95
|
+
retryBackoffAlgorithm: (url.searchParams.get("retry-backoff-algorithm") ??
|
|
96
|
+
DEFAULT_RETRY_BACKOFF_ALGORITHM),
|
|
97
|
+
retryMaxAttempts: parseInt(url.searchParams.get("retry-max-attempts")) ?? DEFAULT_RETRY_MAX_ATTEMPTS,
|
|
98
|
+
retryInitialDelay: parseInt(url.searchParams.get("retry-initial-delay")) ?? DEFAULT_RETRY_INITIAL_DELAY,
|
|
99
|
+
retryExponentialBackoffMultiplier: parseInt(url.searchParams.get("retry-exp-backoff-multiplier")) ??
|
|
100
|
+
DEFAULT_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER,
|
|
101
|
+
retryLinearBackoffStep: parseInt(url.searchParams.get("retry-linear-backoff-step")) ??
|
|
102
|
+
DEFAULT_RETRY_LINEAR_BACKOFF_STEP,
|
|
103
|
+
retryJitter: parseInt(url.searchParams.get("retry-backoff-jitter")) ?? DEFAULT_RETRY_JITTER,
|
|
104
104
|
...overrides,
|
|
105
105
|
};
|
|
106
106
|
}
|