@milaboratories/pl-client 3.7.0 → 3.8.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.
Files changed (79) hide show
  1. package/dist/core/capabilities.cjs +9 -0
  2. package/dist/core/capabilities.cjs.map +1 -0
  3. package/dist/core/capabilities.d.ts +24 -0
  4. package/dist/core/capabilities.d.ts.map +1 -0
  5. package/dist/core/capabilities.js +9 -0
  6. package/dist/core/capabilities.js.map +1 -0
  7. package/dist/core/client.cjs +7 -25
  8. package/dist/core/client.cjs.map +1 -1
  9. package/dist/core/client.d.ts +2 -3
  10. package/dist/core/client.d.ts.map +1 -1
  11. package/dist/core/client.js +7 -25
  12. package/dist/core/client.js.map +1 -1
  13. package/dist/core/ll_client.cjs +153 -7
  14. package/dist/core/ll_client.cjs.map +1 -1
  15. package/dist/core/ll_client.d.ts +26 -0
  16. package/dist/core/ll_client.d.ts.map +1 -1
  17. package/dist/core/ll_client.js +153 -7
  18. package/dist/core/ll_client.js.map +1 -1
  19. package/dist/core/transaction.cjs +4 -2
  20. package/dist/core/transaction.cjs.map +1 -1
  21. package/dist/core/transaction.d.ts.map +1 -1
  22. package/dist/core/transaction.js +4 -2
  23. package/dist/core/transaction.js.map +1 -1
  24. package/dist/core/unauth_client.cjs +33 -1
  25. package/dist/core/unauth_client.cjs.map +1 -1
  26. package/dist/core/unauth_client.d.ts +19 -0
  27. package/dist/core/unauth_client.d.ts.map +1 -1
  28. package/dist/core/unauth_client.js +33 -1
  29. package/dist/core/unauth_client.js.map +1 -1
  30. package/dist/index.cjs +2 -0
  31. package/dist/index.d.ts +2 -1
  32. package/dist/index.js +2 -1
  33. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs.map +1 -1
  34. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js.map +1 -1
  35. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +1101 -135
  36. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
  37. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.cjs +49 -10
  38. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.cjs.map +1 -1
  39. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts +61 -1
  40. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts.map +1 -1
  41. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.js +49 -10
  42. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.js.map +1 -1
  43. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +411 -9
  44. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
  45. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +1101 -135
  46. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
  47. package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
  48. package/dist/proto-grpc/google/protobuf/timestamp.d.ts +8 -9
  49. package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -1
  50. package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
  51. package/dist/proto-grpc/google/rpc/code.cjs.map +1 -1
  52. package/dist/proto-grpc/google/rpc/code.js.map +1 -1
  53. package/dist/proto-rest/plapi.d.ts +247 -12
  54. package/dist/proto-rest/plapi.d.ts.map +1 -1
  55. package/dist/util/pl.cjs.map +1 -1
  56. package/dist/util/pl.js.map +1 -1
  57. package/package.json +4 -4
  58. package/src/core/capabilities.ts +26 -0
  59. package/src/core/client.ts +11 -29
  60. package/src/core/ll_client.test.ts +16 -3
  61. package/src/core/ll_client.ts +187 -8
  62. package/src/core/ll_transaction.test.ts +15 -9
  63. package/src/core/transaction.ts +2 -0
  64. package/src/core/unauth_client.ts +42 -3
  65. package/src/core/unauth_client_branch.test.ts +69 -0
  66. package/src/index.ts +1 -0
  67. package/src/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.ts +1 -1
  68. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.ts +85 -10
  69. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +1310 -98
  70. package/src/proto-grpc/google/api/http.ts +1 -1
  71. package/src/proto-grpc/google/protobuf/descriptor.ts +7 -240
  72. package/src/proto-grpc/google/protobuf/timestamp.ts +8 -9
  73. package/src/proto-grpc/google/protobuf/wrappers.ts +4 -38
  74. package/src/proto-grpc/google/rpc/code.ts +1 -1
  75. package/src/proto-grpc/google/rpc/error_details.ts +5 -5
  76. package/src/proto-grpc/google/rpc/http.ts +1 -1
  77. package/src/proto-grpc/google/rpc/status.ts +1 -1
  78. package/src/proto-rest/plapi.ts +263 -12
  79. package/src/util/pl.ts +5 -0
@@ -0,0 +1,9 @@
1
+ //#region src/core/capabilities.ts
2
+ /** True iff `capabilities` advertises the requested token. */
3
+ function hasCapability(capabilities, capability) {
4
+ return capabilities?.includes(capability) ?? false;
5
+ }
6
+ //#endregion
7
+ exports.hasCapability = hasCapability;
8
+
9
+ //# sourceMappingURL=capabilities.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.cjs","names":[],"sources":["../../src/core/capabilities.ts"],"sourcesContent":["/**\n * Backend capability tokens advertised by pl in\n * `MaintenanceAPI.Ping.Response.capabilities`.\n * Capabilities affect behaviour of inner ML mechanisms and allow\n * to detect incompatibilities between blocks and backend via\n * via `TemplateDataV3.requiredCapabilities`.\n *\n * Mirror of `pl/platform/api/plapiserver/server_capabilities.go` — keep\n * tokens in sync with the backend's advertised list. Format is\n * `<feature>:<version>`; bump the version when wire semantics change.\n *\n * Shared across:\n * - `@milaboratories/pl-client` — client-side capability checks\n * - `@platforma-sdk/tengo-builder` — populates `requiredCapabilities` at compile time\n * - `@platforma-sdk/block-tools` — copies it onto the published manifest\n * - `@milaboratories/pl-middle-layer` — checks it at install time\n */\nexport type BackendCapability = \"auth:v2\" | \"treeFilter:v1\" | \"wasm:v1\";\n\n/** True iff `capabilities` advertises the requested token. */\nexport function hasCapability(\n capabilities: readonly string[] | undefined,\n capability: BackendCapability,\n): boolean {\n return capabilities?.includes(capability) ?? false;\n}\n"],"mappings":";;AAoBA,SAAgB,cACd,cACA,YACS;AACT,QAAO,cAAc,SAAS,WAAW,IAAI"}
@@ -0,0 +1,24 @@
1
+ //#region src/core/capabilities.d.ts
2
+ /**
3
+ * Backend capability tokens advertised by pl in
4
+ * `MaintenanceAPI.Ping.Response.capabilities`.
5
+ * Capabilities affect behaviour of inner ML mechanisms and allow
6
+ * to detect incompatibilities between blocks and backend via
7
+ * via `TemplateDataV3.requiredCapabilities`.
8
+ *
9
+ * Mirror of `pl/platform/api/plapiserver/server_capabilities.go` — keep
10
+ * tokens in sync with the backend's advertised list. Format is
11
+ * `<feature>:<version>`; bump the version when wire semantics change.
12
+ *
13
+ * Shared across:
14
+ * - `@milaboratories/pl-client` — client-side capability checks
15
+ * - `@platforma-sdk/tengo-builder` — populates `requiredCapabilities` at compile time
16
+ * - `@platforma-sdk/block-tools` — copies it onto the published manifest
17
+ * - `@milaboratories/pl-middle-layer` — checks it at install time
18
+ */
19
+ type BackendCapability = "auth:v2" | "treeFilter:v1" | "wasm:v1";
20
+ /** True iff `capabilities` advertises the requested token. */
21
+ declare function hasCapability(capabilities: readonly string[] | undefined, capability: BackendCapability): boolean;
22
+ //#endregion
23
+ export { BackendCapability, hasCapability };
24
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","names":[],"sources":["../../src/core/capabilities.ts"],"mappings":";;AAiBA;;;;;AAGA;;;;;;;;;;;KAHY,iBAAA;;iBAGI,aAAA,CACd,YAAA,iCACA,UAAA,EAAY,iBAAA"}
@@ -0,0 +1,9 @@
1
+ //#region src/core/capabilities.ts
2
+ /** True iff `capabilities` advertises the requested token. */
3
+ function hasCapability(capabilities, capability) {
4
+ return capabilities?.includes(capability) ?? false;
5
+ }
6
+ //#endregion
7
+ export { hasCapability };
8
+
9
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","names":[],"sources":["../../src/core/capabilities.ts"],"sourcesContent":["/**\n * Backend capability tokens advertised by pl in\n * `MaintenanceAPI.Ping.Response.capabilities`.\n * Capabilities affect behaviour of inner ML mechanisms and allow\n * to detect incompatibilities between blocks and backend via\n * via `TemplateDataV3.requiredCapabilities`.\n *\n * Mirror of `pl/platform/api/plapiserver/server_capabilities.go` — keep\n * tokens in sync with the backend's advertised list. Format is\n * `<feature>:<version>`; bump the version when wire semantics change.\n *\n * Shared across:\n * - `@milaboratories/pl-client` — client-side capability checks\n * - `@platforma-sdk/tengo-builder` — populates `requiredCapabilities` at compile time\n * - `@platforma-sdk/block-tools` — copies it onto the published manifest\n * - `@milaboratories/pl-middle-layer` — checks it at install time\n */\nexport type BackendCapability = \"auth:v2\" | \"treeFilter:v1\" | \"wasm:v1\";\n\n/** True iff `capabilities` advertises the requested token. */\nexport function hasCapability(\n capabilities: readonly string[] | undefined,\n capability: BackendCapability,\n): boolean {\n return capabilities?.includes(capability) ?? false;\n}\n"],"mappings":";;AAoBA,SAAgB,cACd,cACA,YACS;AACT,QAAO,cAAc,SAAS,WAAW,IAAI"}
@@ -18,17 +18,6 @@ const defaultTxOps = { sync: false };
18
18
  function alternativeRootFieldName(alternativeRoot) {
19
19
  return `alternative_root_${alternativeRoot}`;
20
20
  }
21
- function isVersionAtLeast(version, target) {
22
- const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
23
- if (!match) return false;
24
- const parsed = [
25
- Number(match[1]),
26
- Number(match[2]),
27
- Number(match[3])
28
- ];
29
- for (let i = 0; i < 3; i++) if (parsed[i] !== target[i]) return parsed[i] > target[i];
30
- return true;
31
- }
32
21
  /** Client to access core PL API. */
33
22
  var PlClient = class PlClient {
34
23
  drivers = /* @__PURE__ */ new Map();
@@ -47,8 +36,6 @@ var PlClient = class PlClient {
47
36
  }
48
37
  /** Stores client root (this abstraction is intended for future implementation of the security model) */
49
38
  _clientRoot = "";
50
- _serverInfo = void 0;
51
- _supportsSetDefaultColor = false;
52
39
  _userResources;
53
40
  _txCommittedStat = require_stat.initialTxStat();
54
41
  _txConflictStat = require_stat.initialTxStat();
@@ -145,7 +132,11 @@ var PlClient = class PlClient {
145
132
  }
146
133
  get serverInfo() {
147
134
  this.checkInitialized();
148
- return this._serverInfo;
135
+ return this._ll.serverInfo;
136
+ }
137
+ hasCapability(capability) {
138
+ this.checkInitialized();
139
+ return this._ll.hasCapability(capability);
149
140
  }
150
141
  /** User resources index for discovering data libraries and other shared resources. */
151
142
  get userResources() {
@@ -159,12 +150,10 @@ var PlClient = class PlClient {
159
150
  if (this.initialized) throw new Error("Already initialized");
160
151
  this._ll = await this.buildLLPlClient(false);
161
152
  const wireProtocol = this._ll.wireProtocol;
162
- this._serverInfo = await this.ping();
163
- if (this._serverInfo.compression === require_api.MaintenanceAPI_Ping_Response_Compression.GZIP) {
153
+ if (this._ll.serverInfo.compression === require_api.MaintenanceAPI_Ping_Response_Compression.GZIP) {
164
154
  await this._ll.close();
165
155
  this._ll = await this.buildLLPlClient(true, wireProtocol);
166
156
  }
167
- this.detectBackendCompatibility(this._serverInfo);
168
157
  const userRoot = await this.userResources.getUserRoot({ createIfNotExists: true });
169
158
  if (this.conf.alternativeRoot === void 0) this._clientRoot = userRoot;
170
159
  else this._clientRoot = await this._withTx("initialization", true, userRoot, async (tx) => {
@@ -180,13 +169,6 @@ var PlClient = class PlClient {
180
169
  return await altRoot.globalId;
181
170
  });
182
171
  }
183
- detectBackendCompatibility(serverInfo) {
184
- this._supportsSetDefaultColor = isVersionAtLeast(serverInfo.coreVersion, [
185
- 3,
186
- 3,
187
- 0
188
- ]);
189
- }
190
172
  /** Returns true if field existed */
191
173
  async deleteAlternativeRoot(alternativeRootName) {
192
174
  this.checkInitialized();
@@ -208,7 +190,7 @@ var PlClient = class PlClient {
208
190
  const release = ops?.lockId ? await require_advisory_locks.advisoryLock(ops.lockId) : () => {};
209
191
  try {
210
192
  const tx = new require_transaction.PlTransaction(this.ll.createTx(writable, ops), name, writable, clientRoot, this.finalPredicate, this.resourceDataCache);
211
- if (this._supportsSetDefaultColor && !require_types.isNullSignedResourceId(clientRoot) && writable) {
193
+ if (this.ll.supportsSetDefaultColor && !require_types.isNullSignedResourceId(clientRoot) && writable) {
212
194
  const parsed = require_types.parseSignedResourceId(clientRoot);
213
195
  if (parsed.signature) tx.setDefaultColor(parsed.signature);
214
196
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","names":["initialTxStat","plAddressToConfig","LLPlClient","DefaultFinalResourceDataPredicate","LRUCache","addStat","isNullSignedResourceId","ensureSignedResourceIdNotNull","UserResources","MaintenanceAPI_Ping_Response_Compression","ClientRoot","advisoryLock","PlTransaction","parseSignedResourceId","TxCommitConflict","tp"],"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 { PlTransaction, TxCommitConflict } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId } from \"./types\";\nimport {\n ensureSignedResourceIdNotNull,\n isNullSignedResourceId,\n NullSignedResourceId,\n parseSignedResourceId,\n} from \"./types\";\nimport type { ColorProof } 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\";\nimport { UserResources } from \"./user_resources\";\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\nfunction alternativeRootFieldName(alternativeRoot: string): string {\n return `alternative_root_${alternativeRoot}`;\n}\n\n// Parses leading \"<major>.<minor>.<patch>\" from a version string like\n// \"3.1.1\" or \"3.1.1-rc1\" and returns true if the parsed version is >= target.\n// Returns false for unparseable versions (safer to assume an old backend).\nfunction isVersionAtLeast(version: string, target: [number, number, number]): boolean {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version);\n if (!match) return false;\n const parsed: [number, number, number] = [Number(match[1]), Number(match[2]), Number(match[3])];\n for (let i = 0; i < 3; i++) {\n if (parsed[i] !== target[i]) return parsed[i] > target[i];\n }\n return true;\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: OptionalSignedResourceId = NullSignedResourceId;\n\n private _serverInfo: MaintenanceAPI_Ping_Response | undefined = undefined;\n\n // method setDefaultColor is safe to use in transactions\n private _supportsSetDefaultColor: boolean = false;\n\n private _userResources?: UserResources;\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<SignedResourceId, 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 /**\n * Returns the user root SignedResourceId via ListUserResources.\n * @param opts.login - target user login; omit for the authenticated user.\n * @returns SignedResourceId of the user root, or undefined if the server returned no userRoot entry.\n * @throws if the backend does not implement ListUserResources (callers should catch with isUnimplementedError).\n */\n public async getUserRoot(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n public async getUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n public async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n return this.userResources.getUserRoot(opts);\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 !isNullSignedResourceId(this._clientRoot);\n }\n\n private checkInitialized() {\n if (!this.initialized) throw new Error(\"Client not initialized\");\n }\n\n public get clientRoot(): SignedResourceId {\n this.checkInitialized();\n return ensureSignedResourceIdNotNull(this._clientRoot);\n }\n\n public get serverInfo(): MaintenanceAPI_Ping_Response {\n this.checkInitialized();\n return this._serverInfo!;\n }\n\n /** User resources index for discovering data libraries and other shared resources. */\n public get userResources(): UserResources {\n if (!this._ll) throw new Error(\"Client not initialized\");\n\n if (!this._userResources) {\n this._userResources = new UserResources(this._ll, this._withTx.bind(this), this._ll.authUser);\n }\n\n return this._userResources;\n }\n\n /** Discovers or creates the user's root resource via UserResources,\n * then handles alternativeRoot if configured. */\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 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.detectBackendCompatibility(this._serverInfo);\n\n const userRoot = await this.userResources.getUserRoot({ createIfNotExists: true });\n\n if (this.conf.alternativeRoot === undefined) {\n this._clientRoot = userRoot;\n } else {\n this._clientRoot = await this._withTx(\"initialization\", true, userRoot, async (tx) => {\n const aFId = {\n resourceId: userRoot,\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 private detectBackendCompatibility(serverInfo: MaintenanceAPI_Ping_Response): void {\n this._supportsSetDefaultColor = isVersionAtLeast(serverInfo.coreVersion, [3, 3, 0]);\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: OptionalSignedResourceId,\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 // Auto-set default color proof from the client root's signature.\n // Skip when backend doesn't implement the `set_default_color` TX request.\n // See `detectBackendCompatibility`.\n if (this._supportsSetDefaultColor && !isNullSignedResourceId(clientRoot) && writable) {\n const parsed = parseSignedResourceId(clientRoot);\n if (parsed.signature) {\n tx.setDefaultColor(parsed.signature as ColorProof);\n }\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"],"mappings":"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"}
1
+ {"version":3,"file":"client.cjs","names":["initialTxStat","plAddressToConfig","LLPlClient","DefaultFinalResourceDataPredicate","LRUCache","addStat","isNullSignedResourceId","ensureSignedResourceIdNotNull","UserResources","MaintenanceAPI_Ping_Response_Compression","ClientRoot","advisoryLock","PlTransaction","parseSignedResourceId","TxCommitConflict","tp"],"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 { PlTransaction, TxCommitConflict } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId } from \"./types\";\nimport {\n ensureSignedResourceIdNotNull,\n isNullSignedResourceId,\n NullSignedResourceId,\n parseSignedResourceId,\n} from \"./types\";\nimport type { ColorProof } 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\";\nimport { UserResources } from \"./user_resources\";\nimport type { BackendCapability } from \"./capabilities\";\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\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: OptionalSignedResourceId = NullSignedResourceId;\n\n private _userResources?: UserResources;\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<SignedResourceId, 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 /**\n * Returns the user root SignedResourceId via ListUserResources.\n * @param opts.login - target user login; omit for the authenticated user.\n * @returns SignedResourceId of the user root, or undefined if the server returned no userRoot entry.\n * @throws if the backend does not implement ListUserResources (callers should catch with isUnimplementedError).\n */\n public async getUserRoot(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n public async getUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n public async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n return this.userResources.getUserRoot(opts);\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 !isNullSignedResourceId(this._clientRoot);\n }\n\n private checkInitialized() {\n if (!this.initialized) throw new Error(\"Client not initialized\");\n }\n\n public get clientRoot(): SignedResourceId {\n this.checkInitialized();\n return ensureSignedResourceIdNotNull(this._clientRoot);\n }\n\n public get serverInfo(): MaintenanceAPI_Ping_Response {\n this.checkInitialized();\n return this._ll!.serverInfo;\n }\n\n public hasCapability(capability: BackendCapability): boolean {\n this.checkInitialized();\n return this._ll!.hasCapability(capability);\n }\n\n /** User resources index for discovering data libraries and other shared resources. */\n public get userResources(): UserResources {\n if (!this._ll) throw new Error(\"Client not initialized\");\n\n if (!this._userResources) {\n this._userResources = new UserResources(this._ll, this._withTx.bind(this), this._ll.authUser);\n }\n\n return this._userResources;\n }\n\n /** Discovers or creates the user's root resource via UserResources,\n * then handles alternativeRoot if configured. */\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 // LLPlClient.build() guarantees a successful ping; read the cached response instead of pinging again.\n const info = this._ll.serverInfo;\n if (info.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {\n await this._ll.close();\n this._ll = await this.buildLLPlClient(true, wireProtocol);\n }\n\n const userRoot = await this.userResources.getUserRoot({ createIfNotExists: true });\n\n if (this.conf.alternativeRoot === undefined) {\n this._clientRoot = userRoot;\n } else {\n this._clientRoot = await this._withTx(\"initialization\", true, userRoot, async (tx) => {\n const aFId = {\n resourceId: userRoot,\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: OptionalSignedResourceId,\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 // Auto-set default color proof from the client root's signature.\n // Skip when backend doesn't implement the `set_default_color` TX request.\n if (this.ll.supportsSetDefaultColor && !isNullSignedResourceId(clientRoot) && writable) {\n const parsed = parseSignedResourceId(clientRoot);\n if (parsed.signature) {\n tx.setDefaultColor(parsed.signature as ColorProof);\n }\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"],"mappings":"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"}
@@ -4,6 +4,7 @@ import { FinalResourceDataPredicate } from "./final.js";
4
4
  import { AllTxStat, TxStat } from "./stat.js";
5
5
  import { PlTransaction } from "./transaction.js";
6
6
  import { AuthOps, PlClientConfig, PlConnectionStatusListener } from "./config.js";
7
+ import { BackendCapability } from "./capabilities.js";
7
8
  import { WireConnection } from "./wire.js";
8
9
  import { PlCallOps } from "./ll_client.js";
9
10
  import { PlDriver, PlDriverDefinition } from "./driver.js";
@@ -33,8 +34,6 @@ declare class PlClient {
33
34
  private get ll();
34
35
  /** Stores client root (this abstraction is intended for future implementation of the security model) */
35
36
  private _clientRoot;
36
- private _serverInfo;
37
- private _supportsSetDefaultColor;
38
37
  private _userResources?;
39
38
  private _txCommittedStat;
40
39
  private _txConflictStat;
@@ -72,12 +71,12 @@ declare class PlClient {
72
71
  private checkInitialized;
73
72
  get clientRoot(): SignedResourceId;
74
73
  get serverInfo(): MaintenanceAPI_Ping_Response;
74
+ hasCapability(capability: BackendCapability): boolean;
75
75
  /** User resources index for discovering data libraries and other shared resources. */
76
76
  get userResources(): UserResources;
77
77
  /** Discovers or creates the user's root resource via UserResources,
78
78
  * then handles alternativeRoot if configured. */
79
79
  private init;
80
- private detectBackendCompatibility;
81
80
  /** Returns true if field existed */
82
81
  deleteAlternativeRoot(alternativeRootName: string): Promise<boolean>;
83
82
  private _withTx;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","names":[],"sources":["../../src/core/client.ts"],"mappings":";;;;;;;;;;;;;;KAkCY,KAAA,GAAQ,SAAA;EAClB,IAAA;EACA,YAAA,GAAe,YAAA;EACf,IAAA;EACA,MAAA;AAAA;;cAyBW,QAAA;EAAA,iBACM,OAAA;EA5BF;;EAAA,iBAgCE,OAAA;EA9BX;EAAA,iBAiCW,SAAA;EARN;EAAA,iBAWM,mBAAA;EAAA,iBAEA,eAAA;EAAA,QAIT,GAAA;EAAA,YAEI,EAAA,CAAA;EAsFiB;EAAA,QA9ErB,WAAA;EAAA,QAEA,WAAA;EAAA,QAGA,wBAAA;EAAA,QAEA,cAAA;EAAA,QAEA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,YAAA;EAwGI;EAAA,SAjGI,cAAA,EAAgB,0BAAA;EAqGpB;EAAA,iBAlGK,iBAAA;EAAA,QAEV,WAAA,CAAA;EAAA,IAmDI,eAAA,CAAA,GAAmB,MAAA;EAAA,IAInB,cAAA,CAAA,GAAkB,MAAA;EAAA,IAIlB,WAAA,CAAA,GAAe,MAAA;EAAA,IAIf,WAAA,CAAA,GAAe,MAAA;EAAA,IAIf,SAAA,CAAA,GAAa,SAAA;EAQX,IAAA,CAAA,GAAQ,OAAA,CAAQ,4BAAA;EAIhB,OAAA,CAAA,GAAW,OAAA,CAAQ,+BAAA;EAyOO;;;;;;EA/N1B,WAAA,CAAY,IAAA;IACvB,KAAA;IACA,iBAAA;EAAA,IACE,OAAA,CAAQ,gBAAA;EACC,WAAA,CAAY,IAAA;IACvB,KAAA;IACA,iBAAA;EAAA,IACE,OAAA,CAAQ,gBAAA;EAAA,IAOD,IAAA,CAAA,GAAQ,cAAA;EAAA,IAIR,cAAA,CAAA,GAAkB,UAAA;EAAA,IAIlB,cAAA,CAAA,GAAkB,cAAA;EAAA,YAIjB,WAAA,CAAA;EAAA,QAIJ,gBAAA;EAAA,IAIG,UAAA,CAAA,GAAc,gBAAA;EAAA,IAKd,UAAA,CAAA,GAAc,4BAAA;EAwNZ;EAAA,IAlNF,aAAA,CAAA,GAAiB,aAAA;EAmNpB;;EAAA,QAvMM,IAAA;EAAA,QA0CN,0BAAA;EAzOS;EA8OJ,qBAAA,CAAsB,mBAAA,WAA8B,OAAA;EAAA,QAgBnD,OAAA;EAAA,QA2FA,MAAA;EAUD,WAAA,GAAA,CACX,IAAA,UACA,IAAA,GAAO,EAAA,EAAI,aAAA,KAAkB,OAAA,CAAQ,CAAA,GACrC,GAAA,GAAK,OAAA,CAAQ,KAAA,IACZ,OAAA,CAAQ,CAAA;EAIE,UAAA,GAAA,CACX,IAAA,UACA,IAAA,GAAO,EAAA,EAAI,aAAA,KAAkB,OAAA,CAAQ,CAAA,GACrC,GAAA,GAAK,OAAA,CAAQ,KAAA,IACZ,OAAA,CAAQ,CAAA;EAIJ,SAAA,aAAsB,QAAA,CAAA,CAAU,UAAA,EAAY,kBAAA,CAAmB,GAAA,IAAO,GAAA;EA3VrE;EAoWK,KAAA,CAAA,GAAK,OAAA;EAAA,OAIE,IAAA,CAClB,eAAA,EAAiB,cAAA,WACjB,IAAA,EAAM,OAAA,EACN,GAAA;IACE,cAAA,GAAiB,0BAAA;IACjB,MAAA,GAAS,QAAA;EAAA,IACL,OAAA,CAAA,QAAA;AAAA"}
1
+ {"version":3,"file":"client.d.ts","names":[],"sources":["../../src/core/client.ts"],"mappings":";;;;;;;;;;;;;;;KAmCY,KAAA,GAAQ,SAAA;EAClB,IAAA;EACA,YAAA,GAAe,YAAA;EACf,IAAA;EACA,MAAA;AAAA;;cAYW,QAAA;EAAA,iBACM,OAAA;EAfF;;EAAA,iBAmBE,OAAA;EAjBX;EAAA,iBAoBW,SAAA;EARN;EAAA,iBAWM,mBAAA;EAAA,iBAEA,eAAA;EAAA,QAIT,GAAA;EAAA,YAEI,EAAA,CAAA;EAiFiB;EAAA,QAzErB,WAAA;EAAA,QAEA,cAAA;EAAA,QAEA,gBAAA;EAAA,QACA,eAAA;EAAA,QACA,YAAA;EA2FwB;EAAA,SApFhB,cAAA,EAAgB,0BAAA;EAiGpB;EAAA,iBA9FK,iBAAA;EAAA,QAEV,WAAA,CAAA;EAAA,IAmDI,eAAA,CAAA,GAAmB,MAAA;EAAA,IAInB,cAAA,CAAA,GAAkB,MAAA;EAAA,IAIlB,WAAA,CAAA,GAAe,MAAA;EAAA,IAIf,WAAA,CAAA,GAAe,MAAA;EAAA,IAIf,SAAA,CAAA,GAAa,SAAA;EAQX,IAAA,CAAA,GAAQ,OAAA,CAAQ,4BAAA;EAIhB,OAAA,CAAA,GAAW,OAAA,CAAQ,+BAAA;EA4DJ;;;;;;EAlDf,WAAA,CAAY,IAAA;IACvB,KAAA;IACA,iBAAA;EAAA,IACE,OAAA,CAAQ,gBAAA;EACC,WAAA,CAAY,IAAA;IACvB,KAAA;IACA,iBAAA;EAAA,IACE,OAAA,CAAQ,gBAAA;EAAA,IAOD,IAAA,CAAA,GAAQ,cAAA;EAAA,IAIR,cAAA,CAAA,GAAkB,UAAA;EAAA,IAIlB,cAAA,CAAA,GAAkB,cAAA;EAAA,YAIjB,WAAA,CAAA;EAAA,QAIJ,gBAAA;EAAA,IAIG,UAAA,CAAA,GAAc,gBAAA;EAAA,IAKd,UAAA,CAAA,GAAc,4BAAA;EAKlB,aAAA,CAAc,UAAA,EAAY,iBAAA;EA+MzB;EAAA,IAzMG,aAAA,CAAA,GAAiB,aAAA;EA4Mf;;EAAA,QAhMC,IAAA;EAiMN;EAvJK,qBAAA,CAAsB,mBAAA,WAA8B,OAAA;EAAA,QAgBnD,OAAA;EAAA,QA0FA,MAAA;EAUD,WAAA,GAAA,CACX,IAAA,UACA,IAAA,GAAO,EAAA,EAAI,aAAA,KAAkB,OAAA,CAAQ,CAAA,GACrC,GAAA,GAAK,OAAA,CAAQ,KAAA,IACZ,OAAA,CAAQ,CAAA;EAIE,UAAA,GAAA,CACX,IAAA,UACA,IAAA,GAAO,EAAA,EAAI,aAAA,KAAkB,OAAA,CAAQ,CAAA,GACrC,GAAA,GAAK,OAAA,CAAQ,KAAA,IACZ,OAAA,CAAQ,CAAA;EAIJ,SAAA,aAAsB,QAAA,CAAA,CAAU,UAAA,EAAY,kBAAA,CAAmB,GAAA,IAAO,GAAA;EAjWrE;EA0WK,KAAA,CAAA,GAAK,OAAA;EAAA,OAIE,IAAA,CAClB,eAAA,EAAiB,cAAA,WACjB,IAAA,EAAM,OAAA,EACN,GAAA;IACE,cAAA,GAAiB,0BAAA;IACjB,MAAA,GAAS,QAAA;EAAA,IACL,OAAA,CAAA,QAAA;AAAA"}
@@ -16,17 +16,6 @@ const defaultTxOps = { sync: false };
16
16
  function alternativeRootFieldName(alternativeRoot) {
17
17
  return `alternative_root_${alternativeRoot}`;
18
18
  }
19
- function isVersionAtLeast(version, target) {
20
- const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
21
- if (!match) return false;
22
- const parsed = [
23
- Number(match[1]),
24
- Number(match[2]),
25
- Number(match[3])
26
- ];
27
- for (let i = 0; i < 3; i++) if (parsed[i] !== target[i]) return parsed[i] > target[i];
28
- return true;
29
- }
30
19
  /** Client to access core PL API. */
31
20
  var PlClient = class PlClient {
32
21
  drivers = /* @__PURE__ */ new Map();
@@ -45,8 +34,6 @@ var PlClient = class PlClient {
45
34
  }
46
35
  /** Stores client root (this abstraction is intended for future implementation of the security model) */
47
36
  _clientRoot = "";
48
- _serverInfo = void 0;
49
- _supportsSetDefaultColor = false;
50
37
  _userResources;
51
38
  _txCommittedStat = initialTxStat();
52
39
  _txConflictStat = initialTxStat();
@@ -143,7 +130,11 @@ var PlClient = class PlClient {
143
130
  }
144
131
  get serverInfo() {
145
132
  this.checkInitialized();
146
- return this._serverInfo;
133
+ return this._ll.serverInfo;
134
+ }
135
+ hasCapability(capability) {
136
+ this.checkInitialized();
137
+ return this._ll.hasCapability(capability);
147
138
  }
148
139
  /** User resources index for discovering data libraries and other shared resources. */
149
140
  get userResources() {
@@ -157,12 +148,10 @@ var PlClient = class PlClient {
157
148
  if (this.initialized) throw new Error("Already initialized");
158
149
  this._ll = await this.buildLLPlClient(false);
159
150
  const wireProtocol = this._ll.wireProtocol;
160
- this._serverInfo = await this.ping();
161
- if (this._serverInfo.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {
151
+ if (this._ll.serverInfo.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {
162
152
  await this._ll.close();
163
153
  this._ll = await this.buildLLPlClient(true, wireProtocol);
164
154
  }
165
- this.detectBackendCompatibility(this._serverInfo);
166
155
  const userRoot = await this.userResources.getUserRoot({ createIfNotExists: true });
167
156
  if (this.conf.alternativeRoot === void 0) this._clientRoot = userRoot;
168
157
  else this._clientRoot = await this._withTx("initialization", true, userRoot, async (tx) => {
@@ -178,13 +167,6 @@ var PlClient = class PlClient {
178
167
  return await altRoot.globalId;
179
168
  });
180
169
  }
181
- detectBackendCompatibility(serverInfo) {
182
- this._supportsSetDefaultColor = isVersionAtLeast(serverInfo.coreVersion, [
183
- 3,
184
- 3,
185
- 0
186
- ]);
187
- }
188
170
  /** Returns true if field existed */
189
171
  async deleteAlternativeRoot(alternativeRootName) {
190
172
  this.checkInitialized();
@@ -206,7 +188,7 @@ var PlClient = class PlClient {
206
188
  const release = ops?.lockId ? await advisoryLock(ops.lockId) : () => {};
207
189
  try {
208
190
  const tx = new PlTransaction(this.ll.createTx(writable, ops), name, writable, clientRoot, this.finalPredicate, this.resourceDataCache);
209
- if (this._supportsSetDefaultColor && !isNullSignedResourceId(clientRoot) && writable) {
191
+ if (this.ll.supportsSetDefaultColor && !isNullSignedResourceId(clientRoot) && writable) {
210
192
  const parsed = parseSignedResourceId(clientRoot);
211
193
  if (parsed.signature) tx.setDefaultColor(parsed.signature);
212
194
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":[],"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 { PlTransaction, TxCommitConflict } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId } from \"./types\";\nimport {\n ensureSignedResourceIdNotNull,\n isNullSignedResourceId,\n NullSignedResourceId,\n parseSignedResourceId,\n} from \"./types\";\nimport type { ColorProof } 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\";\nimport { UserResources } from \"./user_resources\";\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\nfunction alternativeRootFieldName(alternativeRoot: string): string {\n return `alternative_root_${alternativeRoot}`;\n}\n\n// Parses leading \"<major>.<minor>.<patch>\" from a version string like\n// \"3.1.1\" or \"3.1.1-rc1\" and returns true if the parsed version is >= target.\n// Returns false for unparseable versions (safer to assume an old backend).\nfunction isVersionAtLeast(version: string, target: [number, number, number]): boolean {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version);\n if (!match) return false;\n const parsed: [number, number, number] = [Number(match[1]), Number(match[2]), Number(match[3])];\n for (let i = 0; i < 3; i++) {\n if (parsed[i] !== target[i]) return parsed[i] > target[i];\n }\n return true;\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: OptionalSignedResourceId = NullSignedResourceId;\n\n private _serverInfo: MaintenanceAPI_Ping_Response | undefined = undefined;\n\n // method setDefaultColor is safe to use in transactions\n private _supportsSetDefaultColor: boolean = false;\n\n private _userResources?: UserResources;\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<SignedResourceId, 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 /**\n * Returns the user root SignedResourceId via ListUserResources.\n * @param opts.login - target user login; omit for the authenticated user.\n * @returns SignedResourceId of the user root, or undefined if the server returned no userRoot entry.\n * @throws if the backend does not implement ListUserResources (callers should catch with isUnimplementedError).\n */\n public async getUserRoot(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n public async getUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n public async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n return this.userResources.getUserRoot(opts);\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 !isNullSignedResourceId(this._clientRoot);\n }\n\n private checkInitialized() {\n if (!this.initialized) throw new Error(\"Client not initialized\");\n }\n\n public get clientRoot(): SignedResourceId {\n this.checkInitialized();\n return ensureSignedResourceIdNotNull(this._clientRoot);\n }\n\n public get serverInfo(): MaintenanceAPI_Ping_Response {\n this.checkInitialized();\n return this._serverInfo!;\n }\n\n /** User resources index for discovering data libraries and other shared resources. */\n public get userResources(): UserResources {\n if (!this._ll) throw new Error(\"Client not initialized\");\n\n if (!this._userResources) {\n this._userResources = new UserResources(this._ll, this._withTx.bind(this), this._ll.authUser);\n }\n\n return this._userResources;\n }\n\n /** Discovers or creates the user's root resource via UserResources,\n * then handles alternativeRoot if configured. */\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 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.detectBackendCompatibility(this._serverInfo);\n\n const userRoot = await this.userResources.getUserRoot({ createIfNotExists: true });\n\n if (this.conf.alternativeRoot === undefined) {\n this._clientRoot = userRoot;\n } else {\n this._clientRoot = await this._withTx(\"initialization\", true, userRoot, async (tx) => {\n const aFId = {\n resourceId: userRoot,\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 private detectBackendCompatibility(serverInfo: MaintenanceAPI_Ping_Response): void {\n this._supportsSetDefaultColor = isVersionAtLeast(serverInfo.coreVersion, [3, 3, 0]);\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: OptionalSignedResourceId,\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 // Auto-set default color proof from the client root's signature.\n // Skip when backend doesn't implement the `set_default_color` TX request.\n // See `detectBackendCompatibility`.\n if (this._supportsSetDefaultColor && !isNullSignedResourceId(clientRoot) && writable) {\n const parsed = parseSignedResourceId(clientRoot);\n if (parsed.signature) {\n tx.setDefaultColor(parsed.signature as ColorProof);\n }\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"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":[],"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 { PlTransaction, TxCommitConflict } from \"./transaction\";\nimport type { OptionalSignedResourceId, SignedResourceId } from \"./types\";\nimport {\n ensureSignedResourceIdNotNull,\n isNullSignedResourceId,\n NullSignedResourceId,\n parseSignedResourceId,\n} from \"./types\";\nimport type { ColorProof } 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\";\nimport { UserResources } from \"./user_resources\";\nimport type { BackendCapability } from \"./capabilities\";\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\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: OptionalSignedResourceId = NullSignedResourceId;\n\n private _userResources?: UserResources;\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<SignedResourceId, 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 /**\n * Returns the user root SignedResourceId via ListUserResources.\n * @param opts.login - target user login; omit for the authenticated user.\n * @returns SignedResourceId of the user root, or undefined if the server returned no userRoot entry.\n * @throws if the backend does not implement ListUserResources (callers should catch with isUnimplementedError).\n */\n public async getUserRoot(opts: {\n login?: string;\n createIfNotExists: true;\n }): Promise<SignedResourceId>;\n public async getUserRoot(opts?: {\n login?: string;\n createIfNotExists?: boolean;\n }): Promise<SignedResourceId | undefined>;\n public async getUserRoot(\n opts: { login?: string; createIfNotExists?: boolean } = {},\n ): Promise<SignedResourceId | undefined> {\n return this.userResources.getUserRoot(opts);\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 !isNullSignedResourceId(this._clientRoot);\n }\n\n private checkInitialized() {\n if (!this.initialized) throw new Error(\"Client not initialized\");\n }\n\n public get clientRoot(): SignedResourceId {\n this.checkInitialized();\n return ensureSignedResourceIdNotNull(this._clientRoot);\n }\n\n public get serverInfo(): MaintenanceAPI_Ping_Response {\n this.checkInitialized();\n return this._ll!.serverInfo;\n }\n\n public hasCapability(capability: BackendCapability): boolean {\n this.checkInitialized();\n return this._ll!.hasCapability(capability);\n }\n\n /** User resources index for discovering data libraries and other shared resources. */\n public get userResources(): UserResources {\n if (!this._ll) throw new Error(\"Client not initialized\");\n\n if (!this._userResources) {\n this._userResources = new UserResources(this._ll, this._withTx.bind(this), this._ll.authUser);\n }\n\n return this._userResources;\n }\n\n /** Discovers or creates the user's root resource via UserResources,\n * then handles alternativeRoot if configured. */\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 // LLPlClient.build() guarantees a successful ping; read the cached response instead of pinging again.\n const info = this._ll.serverInfo;\n if (info.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {\n await this._ll.close();\n this._ll = await this.buildLLPlClient(true, wireProtocol);\n }\n\n const userRoot = await this.userResources.getUserRoot({ createIfNotExists: true });\n\n if (this.conf.alternativeRoot === undefined) {\n this._clientRoot = userRoot;\n } else {\n this._clientRoot = await this._withTx(\"initialization\", true, userRoot, async (tx) => {\n const aFId = {\n resourceId: userRoot,\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: OptionalSignedResourceId,\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 // Auto-set default color proof from the client root's signature.\n // Skip when backend doesn't implement the `set_default_color` TX request.\n if (this.ll.supportsSetDefaultColor && !isNullSignedResourceId(clientRoot) && writable) {\n const parsed = parseSignedResourceId(clientRoot);\n if (parsed.signature) {\n tx.setDefaultColor(parsed.signature as ColorProof);\n }\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"],"mappings":"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"}