@milaboratories/pl-client 2.18.5 → 3.1.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 (213) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +12 -22
  2. package/dist/_virtual/_rolldown/runtime.js +6 -11
  3. package/dist/core/PromiseTracker.cjs +2 -3
  4. package/dist/core/PromiseTracker.cjs.map +1 -1
  5. package/dist/core/PromiseTracker.d.ts.map +1 -0
  6. package/dist/core/PromiseTracker.js +1 -2
  7. package/dist/core/PromiseTracker.js.map +1 -1
  8. package/dist/core/StatefulPromise.cjs +1 -2
  9. package/dist/core/StatefulPromise.cjs.map +1 -1
  10. package/dist/core/StatefulPromise.js +1 -1
  11. package/dist/core/StatefulPromise.js.map +1 -1
  12. package/dist/core/abstract_stream.d.ts.map +1 -0
  13. package/dist/core/advisory_locks.cjs +1 -2
  14. package/dist/core/advisory_locks.cjs.map +1 -1
  15. package/dist/core/advisory_locks.js +1 -1
  16. package/dist/core/advisory_locks.js.map +1 -1
  17. package/dist/core/auth.cjs +2 -3
  18. package/dist/core/auth.cjs.map +1 -1
  19. package/dist/core/auth.d.ts.map +1 -0
  20. package/dist/core/auth.js +1 -2
  21. package/dist/core/auth.js.map +1 -1
  22. package/dist/core/cache.d.ts.map +1 -0
  23. package/dist/core/client.cjs +63 -36
  24. package/dist/core/client.cjs.map +1 -1
  25. package/dist/core/client.d.ts +3 -1
  26. package/dist/core/client.d.ts.map +1 -0
  27. package/dist/core/client.js +54 -27
  28. package/dist/core/client.js.map +1 -1
  29. package/dist/core/config.cjs +16 -17
  30. package/dist/core/config.cjs.map +1 -1
  31. package/dist/core/config.d.ts.map +1 -0
  32. package/dist/core/config.js +16 -16
  33. package/dist/core/config.js.map +1 -1
  34. package/dist/core/default_client.cjs +6 -7
  35. package/dist/core/default_client.cjs.map +1 -1
  36. package/dist/core/default_client.d.ts.map +1 -0
  37. package/dist/core/default_client.js +1 -2
  38. package/dist/core/default_client.js.map +1 -1
  39. package/dist/core/driver.cjs +1 -2
  40. package/dist/core/driver.cjs.map +1 -1
  41. package/dist/core/driver.d.ts.map +1 -0
  42. package/dist/core/driver.js +1 -1
  43. package/dist/core/error_resource.cjs +1 -2
  44. package/dist/core/error_resource.cjs.map +1 -1
  45. package/dist/core/error_resource.js +1 -1
  46. package/dist/core/errors.cjs +11 -4
  47. package/dist/core/errors.cjs.map +1 -1
  48. package/dist/core/errors.d.ts +2 -1
  49. package/dist/core/errors.d.ts.map +1 -0
  50. package/dist/core/errors.js +9 -3
  51. package/dist/core/errors.js.map +1 -1
  52. package/dist/core/final.cjs +3 -4
  53. package/dist/core/final.cjs.map +1 -1
  54. package/dist/core/final.d.ts.map +1 -0
  55. package/dist/core/final.js +1 -2
  56. package/dist/core/final.js.map +1 -1
  57. package/dist/core/ll_client.cjs +35 -18
  58. package/dist/core/ll_client.cjs.map +1 -1
  59. package/dist/core/ll_client.d.ts +6 -1
  60. package/dist/core/ll_client.d.ts.map +1 -0
  61. package/dist/core/ll_client.js +25 -8
  62. package/dist/core/ll_client.js.map +1 -1
  63. package/dist/core/ll_transaction.cjs +4 -5
  64. package/dist/core/ll_transaction.cjs.map +1 -1
  65. package/dist/core/ll_transaction.d.ts.map +1 -0
  66. package/dist/core/ll_transaction.js +1 -2
  67. package/dist/core/ll_transaction.js.map +1 -1
  68. package/dist/core/stat.cjs +1 -2
  69. package/dist/core/stat.cjs.map +1 -1
  70. package/dist/core/stat.d.ts.map +1 -0
  71. package/dist/core/stat.js +1 -1
  72. package/dist/core/transaction.cjs +32 -21
  73. package/dist/core/transaction.cjs.map +1 -1
  74. package/dist/core/transaction.d.ts.map +1 -0
  75. package/dist/core/transaction.js +23 -12
  76. package/dist/core/transaction.js.map +1 -1
  77. package/dist/core/type_conversion.cjs +8 -9
  78. package/dist/core/type_conversion.cjs.map +1 -1
  79. package/dist/core/type_conversion.js +4 -5
  80. package/dist/core/type_conversion.js.map +1 -1
  81. package/dist/core/types.cjs +3 -4
  82. package/dist/core/types.cjs.map +1 -1
  83. package/dist/core/types.d.ts.map +1 -0
  84. package/dist/core/types.js +2 -3
  85. package/dist/core/types.js.map +1 -1
  86. package/dist/core/unauth_client.cjs +4 -5
  87. package/dist/core/unauth_client.cjs.map +1 -1
  88. package/dist/core/unauth_client.d.ts.map +1 -0
  89. package/dist/core/unauth_client.js +1 -2
  90. package/dist/core/unauth_client.js.map +1 -1
  91. package/dist/core/websocket_stream.cjs +4 -5
  92. package/dist/core/websocket_stream.cjs.map +1 -1
  93. package/dist/core/websocket_stream.js +1 -2
  94. package/dist/core/websocket_stream.js.map +1 -1
  95. package/dist/core/wire.d.ts.map +1 -0
  96. package/dist/helpers/pl.cjs +12 -14
  97. package/dist/helpers/pl.cjs.map +1 -1
  98. package/dist/helpers/pl.d.ts.map +1 -0
  99. package/dist/helpers/pl.js +5 -7
  100. package/dist/helpers/pl.js.map +1 -1
  101. package/dist/helpers/poll.cjs +3 -4
  102. package/dist/helpers/poll.cjs.map +1 -1
  103. package/dist/helpers/poll.d.ts.map +1 -0
  104. package/dist/helpers/poll.js +1 -2
  105. package/dist/helpers/poll.js.map +1 -1
  106. package/dist/helpers/retry_strategy.cjs +1 -2
  107. package/dist/helpers/retry_strategy.cjs.map +1 -1
  108. package/dist/helpers/retry_strategy.js +1 -1
  109. package/dist/helpers/retry_strategy.js.map +1 -1
  110. package/dist/helpers/tx_helpers.cjs +3 -4
  111. package/dist/helpers/tx_helpers.cjs.map +1 -1
  112. package/dist/helpers/tx_helpers.d.ts.map +1 -0
  113. package/dist/helpers/tx_helpers.js +1 -2
  114. package/dist/helpers/tx_helpers.js.map +1 -1
  115. package/dist/index.cjs +34 -34
  116. package/dist/index.d.ts +2 -2
  117. package/dist/index.js +2 -3
  118. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs +3 -4
  119. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs.map +1 -1
  120. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.d.ts.map +1 -0
  121. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js +1 -2
  122. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js.map +1 -1
  123. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +2039 -644
  124. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
  125. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.cjs +62 -13
  126. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.cjs.map +1 -1
  127. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts +78 -8
  128. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.d.ts.map +1 -0
  129. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.js +60 -11
  130. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.js.map +1 -1
  131. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +658 -51
  132. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -0
  133. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +2033 -639
  134. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
  135. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.cjs +251 -22
  136. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.cjs.map +1 -1
  137. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts +104 -10
  138. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts.map +1 -0
  139. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.js +248 -19
  140. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.js.map +1 -1
  141. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.cjs +28 -15
  142. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.cjs.map +1 -1
  143. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.d.ts +4 -0
  144. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.d.ts.map +1 -0
  145. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.js +27 -14
  146. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.js.map +1 -1
  147. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.cjs +17 -16
  148. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.cjs.map +1 -1
  149. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts +4 -0
  150. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts.map +1 -0
  151. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.js +15 -14
  152. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.js.map +1 -1
  153. package/dist/proto-grpc/google/protobuf/any.cjs +2 -3
  154. package/dist/proto-grpc/google/protobuf/any.cjs.map +1 -1
  155. package/dist/proto-grpc/google/protobuf/any.d.ts.map +1 -0
  156. package/dist/proto-grpc/google/protobuf/any.js +1 -2
  157. package/dist/proto-grpc/google/protobuf/any.js.map +1 -1
  158. package/dist/proto-grpc/google/protobuf/duration.cjs +2 -3
  159. package/dist/proto-grpc/google/protobuf/duration.cjs.map +1 -1
  160. package/dist/proto-grpc/google/protobuf/duration.d.ts.map +1 -0
  161. package/dist/proto-grpc/google/protobuf/duration.js +1 -2
  162. package/dist/proto-grpc/google/protobuf/duration.js.map +1 -1
  163. package/dist/proto-grpc/google/protobuf/timestamp.cjs +2 -3
  164. package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
  165. package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -0
  166. package/dist/proto-grpc/google/protobuf/timestamp.js +1 -2
  167. package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
  168. package/dist/proto-grpc/google/rpc/code.cjs +1 -2
  169. package/dist/proto-grpc/google/rpc/code.cjs.map +1 -1
  170. package/dist/proto-grpc/google/rpc/code.d.ts.map +1 -0
  171. package/dist/proto-grpc/google/rpc/code.js +1 -1
  172. package/dist/proto-grpc/google/rpc/code.js.map +1 -1
  173. package/dist/proto-rest/index.cjs +8 -9
  174. package/dist/proto-rest/index.cjs.map +1 -1
  175. package/dist/proto-rest/index.d.ts.map +1 -0
  176. package/dist/proto-rest/index.js +1 -2
  177. package/dist/proto-rest/index.js.map +1 -1
  178. package/dist/proto-rest/plapi.d.ts +281 -28
  179. package/dist/proto-rest/plapi.d.ts.map +1 -0
  180. package/dist/test/tcp-proxy.cjs +2 -3
  181. package/dist/test/tcp-proxy.cjs.map +1 -1
  182. package/dist/test/tcp-proxy.d.ts.map +1 -0
  183. package/dist/test/tcp-proxy.js +1 -2
  184. package/dist/test/tcp-proxy.js.map +1 -1
  185. package/dist/test/test_config.cjs +16 -19
  186. package/dist/test/test_config.cjs.map +1 -1
  187. package/dist/test/test_config.d.ts.map +1 -0
  188. package/dist/test/test_config.js +3 -6
  189. package/dist/test/test_config.js.map +1 -1
  190. package/dist/util/pl.cjs +1 -2
  191. package/dist/util/pl.cjs.map +1 -1
  192. package/dist/util/pl.js +1 -1
  193. package/dist/util/util.cjs +1 -2
  194. package/dist/util/util.cjs.map +1 -1
  195. package/dist/util/util.js +1 -1
  196. package/package.json +8 -8
  197. package/src/core/client.ts +80 -30
  198. package/src/core/errors.ts +11 -0
  199. package/src/core/ll_client.test.ts +8 -0
  200. package/src/core/ll_client.ts +30 -2
  201. package/src/core/ll_transaction.test.ts +12 -2
  202. package/src/core/transaction.ts +41 -10
  203. package/src/core/type_conversion.ts +1 -1
  204. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.client.ts +119 -15
  205. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +2274 -322
  206. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.ts +276 -20
  207. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.ts +11 -0
  208. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/import.ts +2 -2
  209. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.ts +11 -0
  210. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/ws-test.ts +3 -3
  211. package/src/proto-grpc/google/protobuf/descriptor.ts +2 -5
  212. package/src/proto-grpc/google/protobuf/struct.ts +1 -1
  213. package/src/proto-rest/plapi.ts +293 -22
@@ -1,20 +1,19 @@
1
- const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
2
- const require_types = require('../core/types.cjs');
3
- const require_config = require('../core/config.cjs');
4
- const require_auth = require('../core/auth.cjs');
5
- const require_ll_client = require('../core/ll_client.cjs');
6
- const require_client = require('../core/client.cjs');
7
- const require_unauth_client = require('../core/unauth_client.cjs');
8
- const require_tcp_proxy = require('./tcp-proxy.cjs');
1
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_types = require("../core/types.cjs");
3
+ const require_config = require("../core/config.cjs");
4
+ const require_auth = require("../core/auth.cjs");
5
+ const require_ll_client = require("../core/ll_client.cjs");
6
+ const require_client = require("../core/client.cjs");
7
+ const require_unauth_client = require("../core/unauth_client.cjs");
8
+ const require_tcp_proxy = require("./tcp-proxy.cjs");
9
9
  let node_crypto = require("node:crypto");
10
10
  let node_fs = require("node:fs");
11
11
  node_fs = require_runtime.__toESM(node_fs);
12
12
  let node_path = require("node:path");
13
13
  node_path = require_runtime.__toESM(node_path);
14
-
15
14
  //#region src/test/test_config.ts
16
15
  var test_config_exports = /* @__PURE__ */ require_runtime.__exportAll({
17
- TEST_REQUEST_TIMEOUT: () => TEST_REQUEST_TIMEOUT,
16
+ TEST_REQUEST_TIMEOUT: () => 500,
18
17
  getTestClient: () => getTestClient,
19
18
  getTestClientConf: () => getTestClientConf,
20
19
  getTestConfig: () => getTestConfig,
@@ -38,12 +37,10 @@ function getTestConfig() {
38
37
  if (conf.address === void 0) throw new Error(`can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`);
39
38
  return conf;
40
39
  }
41
- /** Default request timeout for tests (ms) */
42
- const TEST_REQUEST_TIMEOUT = 500;
43
40
  /** Returns PlClientConfig with reduced timeout for tests */
44
41
  function plAddressToTestConfig(address) {
45
42
  const plConf = require_config.plAddressToConfig(address);
46
- plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;
43
+ plConf.defaultRequestTimeout = 500;
47
44
  return plConf;
48
45
  }
49
46
  function saveAuthInfoCallback(tConf) {
@@ -159,12 +156,12 @@ async function withTempRoot(body, options = {}) {
159
156
  }
160
157
  }
161
158
  }
162
-
163
159
  //#endregion
164
- Object.defineProperty(exports, 'test_config_exports', {
165
- enumerable: true,
166
- get: function () {
167
- return test_config_exports;
168
- }
160
+ Object.defineProperty(exports, "test_config_exports", {
161
+ enumerable: true,
162
+ get: function() {
163
+ return test_config_exports;
164
+ }
169
165
  });
166
+
170
167
  //# sourceMappingURL=test_config.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"test_config.cjs","names":["path","fs","plAddressToConfig","inferAuthRefreshTime","UnauthenticatedPlClient","LLPlClient","PlClient","NullResourceId","startTcpProxy","resourceIdToString"],"sources":["../../src/test/test_config.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport { LLPlClient } from \"../core/ll_client\";\nimport type { AuthInformation, AuthOps, PlClientConfig } from \"../core/config\";\nimport { plAddressToConfig } from \"../core/config\";\nimport { UnauthenticatedPlClient } from \"../core/unauth_client\";\nimport { PlClient } from \"../core/client\";\nimport { randomUUID } from \"node:crypto\";\nimport type { OptionalResourceId } from \"../core/types\";\nimport { NullResourceId, resourceIdToString } from \"../core/types\";\nimport { inferAuthRefreshTime } from \"../core/auth\";\nimport * as path from \"node:path\";\nimport type { TestTcpProxy } from \"./tcp-proxy\";\nimport { startTcpProxy } from \"./tcp-proxy\";\n\nexport { TestTcpProxy };\n\nexport interface TestConfig {\n address: string;\n test_proxy?: string;\n test_user?: string;\n test_password?: string;\n}\n\nconst CONFIG_FILE = \"test_config.json\";\n// const AUTH_DATA_FILE = '.test_auth.json';\n\nlet authDataFilePath: string | undefined;\n\nfunction getFullAuthDataFilePath() {\n if (authDataFilePath === undefined) authDataFilePath = path.resolve(\".test_auth.json\");\n return authDataFilePath;\n}\n\nexport function getTestConfig(): TestConfig {\n let conf: Partial<TestConfig> = {};\n if (fs.existsSync(CONFIG_FILE))\n conf = JSON.parse(fs.readFileSync(CONFIG_FILE, { encoding: \"utf-8\" })) as TestConfig;\n\n if (process.env.PL_ADDRESS !== undefined) conf.address = process.env.PL_ADDRESS;\n\n if (process.env.PL_TEST_USER !== undefined) conf.test_user = process.env.PL_TEST_USER;\n\n if (process.env.PL_TEST_PASSWORD !== undefined) conf.test_password = process.env.PL_TEST_PASSWORD;\n\n if (process.env.PL_TEST_PROXY !== undefined) conf.test_proxy = process.env.PL_TEST_PROXY;\n\n if (conf.address === undefined)\n throw new Error(\n `can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`,\n );\n\n return conf as TestConfig;\n}\n\n/** Default request timeout for tests (ms) */\nexport const TEST_REQUEST_TIMEOUT = 500;\n\n/** Returns PlClientConfig with reduced timeout for tests */\nexport function plAddressToTestConfig(address: string): PlClientConfig {\n const plConf = plAddressToConfig(address);\n plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;\n return plConf;\n}\n\ninterface AuthCache {\n /** To check if config changed */\n conf: TestConfig;\n expiration: number;\n authInformation: AuthInformation;\n}\n\nfunction saveAuthInfoCallback(tConf: TestConfig): (authInformation: AuthInformation) => void {\n return (authInformation) => {\n const dst = getFullAuthDataFilePath();\n const tmpDst = getFullAuthDataFilePath() + randomUUID();\n fs.writeFileSync(\n tmpDst,\n Buffer.from(\n JSON.stringify({\n conf: tConf,\n authInformation,\n expiration: inferAuthRefreshTime(authInformation, 24 * 60 * 60),\n } as AuthCache),\n ),\n \"utf8\",\n );\n fs.renameSync(tmpDst, dst);\n };\n}\n\nconst cleanAuthInfoCallback = () => {\n console.warn(`Removing: ${getFullAuthDataFilePath()}`);\n fs.rmSync(getFullAuthDataFilePath());\n};\n\nexport async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth: AuthOps }> {\n const tConf = getTestConfig();\n\n let authInformation: AuthInformation | undefined = undefined;\n\n // try recover from cache\n if (fs.existsSync(getFullAuthDataFilePath())) {\n try {\n const cache: AuthCache = JSON.parse(\n fs.readFileSync(getFullAuthDataFilePath(), { encoding: \"utf-8\" }),\n ) as AuthCache; // TODO runtime validation\n if (\n cache.conf.address === tConf.address &&\n cache.conf.test_user === tConf.test_user &&\n cache.conf.test_password === tConf.test_password &&\n cache.expiration > Date.now()\n )\n authInformation = cache.authInformation;\n } catch {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToTestConfig(tConf.address);\n const uClient = await UnauthenticatedPlClient.build(plConf);\n\n const requireAuth = await uClient.requireAuth();\n\n if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))\n throw new Error(\n `Server require no auth, but test user name or test password are provided via (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (requireAuth && (tConf.test_user === undefined || tConf.test_password === undefined))\n throw new Error(\n `No auth information found in config (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (authInformation === undefined) {\n if (requireAuth) authInformation = await uClient.login(tConf.test_user!, tConf.test_password!);\n // No authorization is required\n else authInformation = {};\n\n // saving cache\n saveAuthInfoCallback(tConf)(authInformation);\n }\n\n return {\n conf: plConf,\n auth: {\n authInformation,\n onUpdate: saveAuthInfoCallback(tConf),\n onAuthError: cleanAuthInfoCallback,\n onUpdateError: cleanAuthInfoCallback,\n },\n };\n}\n\nexport async function getTestLLClient(confOverrides: Partial<PlClientConfig> = {}) {\n const { conf, auth } = await getTestClientConf();\n return await LLPlClient.build({ ...conf, ...confOverrides }, { auth });\n}\n\nexport async function getTestClient(\n alternativeRoot?: string,\n confOverrides: Partial<PlClientConfig> = {},\n) {\n const { conf, auth } = await getTestClientConf();\n if (alternativeRoot !== undefined && conf.alternativeRoot !== undefined)\n throw new Error(\"test pl address configured with alternative root\");\n return await PlClient.init({ ...conf, ...confOverrides, alternativeRoot }, auth);\n}\n\nexport type WithTempRootOptions =\n | {\n /** If true and PL_ADDRESS is http://localhost or http://127.0.0.1:<port>,\n * a TCP proxy will be started and PL client will connect through it. */\n viaTcpProxy: true;\n /** Artificial latency for proxy (ms). Default 0 */\n proxyLatencyMs?: number;\n }\n | {\n viaTcpProxy?: undefined;\n };\n\nexport async function withTempRoot<T>(body: (pl: PlClient) => Promise<T>): Promise<T | void>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: Awaited<ReturnType<typeof startTcpProxy>>) => Promise<T>,\n options: {\n viaTcpProxy: true;\n proxyLatencyMs?: number;\n },\n): Promise<T>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: any) => Promise<T>,\n options: WithTempRootOptions = {},\n): Promise<T | undefined> {\n const alternativeRoot = `test_${Date.now()}_${randomUUID()}`;\n let altRootId: OptionalResourceId = NullResourceId;\n // Proxy management\n let proxy: Awaited<ReturnType<typeof startTcpProxy>> | undefined;\n let confOverrides: Partial<PlClientConfig> = {};\n try {\n // Optionally start TCP proxy and rewrite PL_ADDRESS to point to proxy\n if (options.viaTcpProxy === true && process.env.PL_ADDRESS) {\n try {\n const url = new URL(process.env.PL_ADDRESS);\n const isHttp = url.protocol === \"http:\";\n const isLocal = url.hostname === \"127.0.0.1\" || url.hostname === \"localhost\";\n const port = parseInt(url.port);\n if (isHttp && isLocal && Number.isFinite(port)) {\n proxy = await startTcpProxy({ targetPort: port, latency: options.proxyLatencyMs ?? 0 });\n // Override client connection host:port to proxy\n confOverrides = { hostAndPort: `127.0.0.1:${proxy.port}` } as Partial<PlClientConfig>;\n } else {\n console.warn(\n \"*** skipping proxy-based test, PL_ADDRESS is not localhost\",\n process.env.PL_ADDRESS,\n );\n return;\n }\n } catch {\n // ignore proxy setup errors; tests will run against original address\n }\n }\n\n const client = await getTestClient(alternativeRoot, confOverrides);\n altRootId = client.clientRoot;\n try {\n const value = await body(client, proxy);\n const rawClient = await getTestClient();\n try {\n await rawClient.deleteAlternativeRoot(alternativeRoot);\n } catch (cleanupErr: any) {\n // Cleanup may fail if test intentionally deleted resources\n console.warn(`Failed to clean up alternative root ${alternativeRoot}:`, cleanupErr.message);\n } finally {\n // Close the cleanup client to avoid dangling gRPC channels that can cause\n // segfaults during process exit\n await rawClient.close();\n }\n return value;\n } finally {\n // Close the test client to avoid dangling gRPC channels\n await client.close();\n }\n } catch (err: any) {\n console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);\n throw err;\n // throw new Error('withTempRoot error: ' + err.message, { cause: err });\n } finally {\n // Stop proxy if started\n if (proxy) {\n try {\n await proxy.disconnectAll();\n } catch {\n /* ignore */\n }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch {\n /* ignore */\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"test_config.cjs","names":["path","fs","plAddressToConfig","inferAuthRefreshTime","UnauthenticatedPlClient","LLPlClient","PlClient","NullResourceId","startTcpProxy","resourceIdToString"],"sources":["../../src/test/test_config.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport { LLPlClient } from \"../core/ll_client\";\nimport type { AuthInformation, AuthOps, PlClientConfig } from \"../core/config\";\nimport { plAddressToConfig } from \"../core/config\";\nimport { UnauthenticatedPlClient } from \"../core/unauth_client\";\nimport { PlClient } from \"../core/client\";\nimport { randomUUID } from \"node:crypto\";\nimport type { OptionalResourceId } from \"../core/types\";\nimport { NullResourceId, resourceIdToString } from \"../core/types\";\nimport { inferAuthRefreshTime } from \"../core/auth\";\nimport * as path from \"node:path\";\nimport type { TestTcpProxy } from \"./tcp-proxy\";\nimport { startTcpProxy } from \"./tcp-proxy\";\n\nexport { TestTcpProxy };\n\nexport interface TestConfig {\n address: string;\n test_proxy?: string;\n test_user?: string;\n test_password?: string;\n}\n\nconst CONFIG_FILE = \"test_config.json\";\n// const AUTH_DATA_FILE = '.test_auth.json';\n\nlet authDataFilePath: string | undefined;\n\nfunction getFullAuthDataFilePath() {\n if (authDataFilePath === undefined) authDataFilePath = path.resolve(\".test_auth.json\");\n return authDataFilePath;\n}\n\nexport function getTestConfig(): TestConfig {\n let conf: Partial<TestConfig> = {};\n if (fs.existsSync(CONFIG_FILE))\n conf = JSON.parse(fs.readFileSync(CONFIG_FILE, { encoding: \"utf-8\" })) as TestConfig;\n\n if (process.env.PL_ADDRESS !== undefined) conf.address = process.env.PL_ADDRESS;\n\n if (process.env.PL_TEST_USER !== undefined) conf.test_user = process.env.PL_TEST_USER;\n\n if (process.env.PL_TEST_PASSWORD !== undefined) conf.test_password = process.env.PL_TEST_PASSWORD;\n\n if (process.env.PL_TEST_PROXY !== undefined) conf.test_proxy = process.env.PL_TEST_PROXY;\n\n if (conf.address === undefined)\n throw new Error(\n `can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`,\n );\n\n return conf as TestConfig;\n}\n\n/** Default request timeout for tests (ms) */\nexport const TEST_REQUEST_TIMEOUT = 500;\n\n/** Returns PlClientConfig with reduced timeout for tests */\nexport function plAddressToTestConfig(address: string): PlClientConfig {\n const plConf = plAddressToConfig(address);\n plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;\n return plConf;\n}\n\ninterface AuthCache {\n /** To check if config changed */\n conf: TestConfig;\n expiration: number;\n authInformation: AuthInformation;\n}\n\nfunction saveAuthInfoCallback(tConf: TestConfig): (authInformation: AuthInformation) => void {\n return (authInformation) => {\n const dst = getFullAuthDataFilePath();\n const tmpDst = getFullAuthDataFilePath() + randomUUID();\n fs.writeFileSync(\n tmpDst,\n Buffer.from(\n JSON.stringify({\n conf: tConf,\n authInformation,\n expiration: inferAuthRefreshTime(authInformation, 24 * 60 * 60),\n } as AuthCache),\n ),\n \"utf8\",\n );\n fs.renameSync(tmpDst, dst);\n };\n}\n\nconst cleanAuthInfoCallback = () => {\n console.warn(`Removing: ${getFullAuthDataFilePath()}`);\n fs.rmSync(getFullAuthDataFilePath());\n};\n\nexport async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth: AuthOps }> {\n const tConf = getTestConfig();\n\n let authInformation: AuthInformation | undefined = undefined;\n\n // try recover from cache\n if (fs.existsSync(getFullAuthDataFilePath())) {\n try {\n const cache: AuthCache = JSON.parse(\n fs.readFileSync(getFullAuthDataFilePath(), { encoding: \"utf-8\" }),\n ) as AuthCache; // TODO runtime validation\n if (\n cache.conf.address === tConf.address &&\n cache.conf.test_user === tConf.test_user &&\n cache.conf.test_password === tConf.test_password &&\n cache.expiration > Date.now()\n )\n authInformation = cache.authInformation;\n } catch {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToTestConfig(tConf.address);\n const uClient = await UnauthenticatedPlClient.build(plConf);\n\n const requireAuth = await uClient.requireAuth();\n\n if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))\n throw new Error(\n `Server require no auth, but test user name or test password are provided via (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (requireAuth && (tConf.test_user === undefined || tConf.test_password === undefined))\n throw new Error(\n `No auth information found in config (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (authInformation === undefined) {\n if (requireAuth) authInformation = await uClient.login(tConf.test_user!, tConf.test_password!);\n // No authorization is required\n else authInformation = {};\n\n // saving cache\n saveAuthInfoCallback(tConf)(authInformation);\n }\n\n return {\n conf: plConf,\n auth: {\n authInformation,\n onUpdate: saveAuthInfoCallback(tConf),\n onAuthError: cleanAuthInfoCallback,\n onUpdateError: cleanAuthInfoCallback,\n },\n };\n}\n\nexport async function getTestLLClient(confOverrides: Partial<PlClientConfig> = {}) {\n const { conf, auth } = await getTestClientConf();\n return await LLPlClient.build({ ...conf, ...confOverrides }, { auth });\n}\n\nexport async function getTestClient(\n alternativeRoot?: string,\n confOverrides: Partial<PlClientConfig> = {},\n) {\n const { conf, auth } = await getTestClientConf();\n if (alternativeRoot !== undefined && conf.alternativeRoot !== undefined)\n throw new Error(\"test pl address configured with alternative root\");\n return await PlClient.init({ ...conf, ...confOverrides, alternativeRoot }, auth);\n}\n\nexport type WithTempRootOptions =\n | {\n /** If true and PL_ADDRESS is http://localhost or http://127.0.0.1:<port>,\n * a TCP proxy will be started and PL client will connect through it. */\n viaTcpProxy: true;\n /** Artificial latency for proxy (ms). Default 0 */\n proxyLatencyMs?: number;\n }\n | {\n viaTcpProxy?: undefined;\n };\n\nexport async function withTempRoot<T>(body: (pl: PlClient) => Promise<T>): Promise<T | void>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: Awaited<ReturnType<typeof startTcpProxy>>) => Promise<T>,\n options: {\n viaTcpProxy: true;\n proxyLatencyMs?: number;\n },\n): Promise<T>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: any) => Promise<T>,\n options: WithTempRootOptions = {},\n): Promise<T | undefined> {\n const alternativeRoot = `test_${Date.now()}_${randomUUID()}`;\n let altRootId: OptionalResourceId = NullResourceId;\n // Proxy management\n let proxy: Awaited<ReturnType<typeof startTcpProxy>> | undefined;\n let confOverrides: Partial<PlClientConfig> = {};\n try {\n // Optionally start TCP proxy and rewrite PL_ADDRESS to point to proxy\n if (options.viaTcpProxy === true && process.env.PL_ADDRESS) {\n try {\n const url = new URL(process.env.PL_ADDRESS);\n const isHttp = url.protocol === \"http:\";\n const isLocal = url.hostname === \"127.0.0.1\" || url.hostname === \"localhost\";\n const port = parseInt(url.port);\n if (isHttp && isLocal && Number.isFinite(port)) {\n proxy = await startTcpProxy({ targetPort: port, latency: options.proxyLatencyMs ?? 0 });\n // Override client connection host:port to proxy\n confOverrides = { hostAndPort: `127.0.0.1:${proxy.port}` } as Partial<PlClientConfig>;\n } else {\n console.warn(\n \"*** skipping proxy-based test, PL_ADDRESS is not localhost\",\n process.env.PL_ADDRESS,\n );\n return;\n }\n } catch {\n // ignore proxy setup errors; tests will run against original address\n }\n }\n\n const client = await getTestClient(alternativeRoot, confOverrides);\n altRootId = client.clientRoot;\n try {\n const value = await body(client, proxy);\n const rawClient = await getTestClient();\n try {\n await rawClient.deleteAlternativeRoot(alternativeRoot);\n } catch (cleanupErr: any) {\n // Cleanup may fail if test intentionally deleted resources\n console.warn(`Failed to clean up alternative root ${alternativeRoot}:`, cleanupErr.message);\n } finally {\n // Close the cleanup client to avoid dangling gRPC channels that can cause\n // segfaults during process exit\n await rawClient.close();\n }\n return value;\n } finally {\n // Close the test client to avoid dangling gRPC channels\n await client.close();\n }\n } catch (err: any) {\n console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);\n throw err;\n // throw new Error('withTempRoot error: ' + err.message, { cause: err });\n } finally {\n // Stop proxy if started\n if (proxy) {\n try {\n await proxy.disconnectAll();\n } catch {\n /* ignore */\n }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch {\n /* ignore */\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test_config.d.ts","names":[],"sources":["../../src/test/test_config.ts"],"mappings":";;;;;;;;;UAgBiB,UAAA;EACf,OAAA;EACA,UAAA;EACA,SAAA;EACA,aAAA;AAAA;AAAA,iBAac,aAAA,CAAA,GAAiB,UAAA;;cAsBpB,oBAAA;;iBAGG,qBAAA,CAAsB,OAAA,WAAkB,cAAA;AAAA,iBAqClC,iBAAA,CAAA,GAAqB,OAAA;EAAU,IAAA,EAAM,cAAA;EAAgB,IAAA,EAAM,OAAA;AAAA;AAAA,iBA2D3D,eAAA,CAAgB,aAAA,GAAe,OAAA,CAAQ,cAAA,IAAoB,OAAA,CAAA,UAAA;AAAA,iBAK3D,aAAA,CACpB,eAAA,WACA,aAAA,GAAe,OAAA,CAAQ,cAAA,IAAoB,OAAA,CAAA,QAAA;AAAA,KAQjC,mBAAA;EAvJV;;EA2JI,WAAA,QAzJS;EA2JT,cAAA;AAAA;EAGA,WAAA;AAAA;AAAA,iBAGgB,YAAA,GAAA,CAAgB,IAAA,GAAO,EAAA,EAAI,QAAA,KAAa,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;AAAA,iBAE7D,YAAA,GAAA,CACpB,IAAA,GAAO,EAAA,EAAI,QAAA,EAAU,KAAA,EAAO,OAAA,CAAQ,UAAA,QAAkB,aAAA,OAAoB,OAAA,CAAQ,CAAA,GAClF,OAAA;EACE,WAAA;EACA,cAAA;AAAA,IAED,OAAA,CAAQ,CAAA"}
@@ -9,10 +9,9 @@ import { startTcpProxy } from "./tcp-proxy.js";
9
9
  import { randomUUID } from "node:crypto";
10
10
  import * as fs$1 from "node:fs";
11
11
  import * as path from "node:path";
12
-
13
12
  //#region src/test/test_config.ts
14
13
  var test_config_exports = /* @__PURE__ */ __exportAll({
15
- TEST_REQUEST_TIMEOUT: () => TEST_REQUEST_TIMEOUT,
14
+ TEST_REQUEST_TIMEOUT: () => 500,
16
15
  getTestClient: () => getTestClient,
17
16
  getTestClientConf: () => getTestClientConf,
18
17
  getTestConfig: () => getTestConfig,
@@ -36,12 +35,10 @@ function getTestConfig() {
36
35
  if (conf.address === void 0) throw new Error(`can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`);
37
36
  return conf;
38
37
  }
39
- /** Default request timeout for tests (ms) */
40
- const TEST_REQUEST_TIMEOUT = 500;
41
38
  /** Returns PlClientConfig with reduced timeout for tests */
42
39
  function plAddressToTestConfig(address) {
43
40
  const plConf = plAddressToConfig(address);
44
- plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;
41
+ plConf.defaultRequestTimeout = 500;
45
42
  return plConf;
46
43
  }
47
44
  function saveAuthInfoCallback(tConf) {
@@ -157,7 +154,7 @@ async function withTempRoot(body, options = {}) {
157
154
  }
158
155
  }
159
156
  }
160
-
161
157
  //#endregion
162
158
  export { test_config_exports };
159
+
163
160
  //# sourceMappingURL=test_config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test_config.js","names":["fs"],"sources":["../../src/test/test_config.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport { LLPlClient } from \"../core/ll_client\";\nimport type { AuthInformation, AuthOps, PlClientConfig } from \"../core/config\";\nimport { plAddressToConfig } from \"../core/config\";\nimport { UnauthenticatedPlClient } from \"../core/unauth_client\";\nimport { PlClient } from \"../core/client\";\nimport { randomUUID } from \"node:crypto\";\nimport type { OptionalResourceId } from \"../core/types\";\nimport { NullResourceId, resourceIdToString } from \"../core/types\";\nimport { inferAuthRefreshTime } from \"../core/auth\";\nimport * as path from \"node:path\";\nimport type { TestTcpProxy } from \"./tcp-proxy\";\nimport { startTcpProxy } from \"./tcp-proxy\";\n\nexport { TestTcpProxy };\n\nexport interface TestConfig {\n address: string;\n test_proxy?: string;\n test_user?: string;\n test_password?: string;\n}\n\nconst CONFIG_FILE = \"test_config.json\";\n// const AUTH_DATA_FILE = '.test_auth.json';\n\nlet authDataFilePath: string | undefined;\n\nfunction getFullAuthDataFilePath() {\n if (authDataFilePath === undefined) authDataFilePath = path.resolve(\".test_auth.json\");\n return authDataFilePath;\n}\n\nexport function getTestConfig(): TestConfig {\n let conf: Partial<TestConfig> = {};\n if (fs.existsSync(CONFIG_FILE))\n conf = JSON.parse(fs.readFileSync(CONFIG_FILE, { encoding: \"utf-8\" })) as TestConfig;\n\n if (process.env.PL_ADDRESS !== undefined) conf.address = process.env.PL_ADDRESS;\n\n if (process.env.PL_TEST_USER !== undefined) conf.test_user = process.env.PL_TEST_USER;\n\n if (process.env.PL_TEST_PASSWORD !== undefined) conf.test_password = process.env.PL_TEST_PASSWORD;\n\n if (process.env.PL_TEST_PROXY !== undefined) conf.test_proxy = process.env.PL_TEST_PROXY;\n\n if (conf.address === undefined)\n throw new Error(\n `can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`,\n );\n\n return conf as TestConfig;\n}\n\n/** Default request timeout for tests (ms) */\nexport const TEST_REQUEST_TIMEOUT = 500;\n\n/** Returns PlClientConfig with reduced timeout for tests */\nexport function plAddressToTestConfig(address: string): PlClientConfig {\n const plConf = plAddressToConfig(address);\n plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;\n return plConf;\n}\n\ninterface AuthCache {\n /** To check if config changed */\n conf: TestConfig;\n expiration: number;\n authInformation: AuthInformation;\n}\n\nfunction saveAuthInfoCallback(tConf: TestConfig): (authInformation: AuthInformation) => void {\n return (authInformation) => {\n const dst = getFullAuthDataFilePath();\n const tmpDst = getFullAuthDataFilePath() + randomUUID();\n fs.writeFileSync(\n tmpDst,\n Buffer.from(\n JSON.stringify({\n conf: tConf,\n authInformation,\n expiration: inferAuthRefreshTime(authInformation, 24 * 60 * 60),\n } as AuthCache),\n ),\n \"utf8\",\n );\n fs.renameSync(tmpDst, dst);\n };\n}\n\nconst cleanAuthInfoCallback = () => {\n console.warn(`Removing: ${getFullAuthDataFilePath()}`);\n fs.rmSync(getFullAuthDataFilePath());\n};\n\nexport async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth: AuthOps }> {\n const tConf = getTestConfig();\n\n let authInformation: AuthInformation | undefined = undefined;\n\n // try recover from cache\n if (fs.existsSync(getFullAuthDataFilePath())) {\n try {\n const cache: AuthCache = JSON.parse(\n fs.readFileSync(getFullAuthDataFilePath(), { encoding: \"utf-8\" }),\n ) as AuthCache; // TODO runtime validation\n if (\n cache.conf.address === tConf.address &&\n cache.conf.test_user === tConf.test_user &&\n cache.conf.test_password === tConf.test_password &&\n cache.expiration > Date.now()\n )\n authInformation = cache.authInformation;\n } catch {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToTestConfig(tConf.address);\n const uClient = await UnauthenticatedPlClient.build(plConf);\n\n const requireAuth = await uClient.requireAuth();\n\n if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))\n throw new Error(\n `Server require no auth, but test user name or test password are provided via (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (requireAuth && (tConf.test_user === undefined || tConf.test_password === undefined))\n throw new Error(\n `No auth information found in config (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (authInformation === undefined) {\n if (requireAuth) authInformation = await uClient.login(tConf.test_user!, tConf.test_password!);\n // No authorization is required\n else authInformation = {};\n\n // saving cache\n saveAuthInfoCallback(tConf)(authInformation);\n }\n\n return {\n conf: plConf,\n auth: {\n authInformation,\n onUpdate: saveAuthInfoCallback(tConf),\n onAuthError: cleanAuthInfoCallback,\n onUpdateError: cleanAuthInfoCallback,\n },\n };\n}\n\nexport async function getTestLLClient(confOverrides: Partial<PlClientConfig> = {}) {\n const { conf, auth } = await getTestClientConf();\n return await LLPlClient.build({ ...conf, ...confOverrides }, { auth });\n}\n\nexport async function getTestClient(\n alternativeRoot?: string,\n confOverrides: Partial<PlClientConfig> = {},\n) {\n const { conf, auth } = await getTestClientConf();\n if (alternativeRoot !== undefined && conf.alternativeRoot !== undefined)\n throw new Error(\"test pl address configured with alternative root\");\n return await PlClient.init({ ...conf, ...confOverrides, alternativeRoot }, auth);\n}\n\nexport type WithTempRootOptions =\n | {\n /** If true and PL_ADDRESS is http://localhost or http://127.0.0.1:<port>,\n * a TCP proxy will be started and PL client will connect through it. */\n viaTcpProxy: true;\n /** Artificial latency for proxy (ms). Default 0 */\n proxyLatencyMs?: number;\n }\n | {\n viaTcpProxy?: undefined;\n };\n\nexport async function withTempRoot<T>(body: (pl: PlClient) => Promise<T>): Promise<T | void>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: Awaited<ReturnType<typeof startTcpProxy>>) => Promise<T>,\n options: {\n viaTcpProxy: true;\n proxyLatencyMs?: number;\n },\n): Promise<T>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: any) => Promise<T>,\n options: WithTempRootOptions = {},\n): Promise<T | undefined> {\n const alternativeRoot = `test_${Date.now()}_${randomUUID()}`;\n let altRootId: OptionalResourceId = NullResourceId;\n // Proxy management\n let proxy: Awaited<ReturnType<typeof startTcpProxy>> | undefined;\n let confOverrides: Partial<PlClientConfig> = {};\n try {\n // Optionally start TCP proxy and rewrite PL_ADDRESS to point to proxy\n if (options.viaTcpProxy === true && process.env.PL_ADDRESS) {\n try {\n const url = new URL(process.env.PL_ADDRESS);\n const isHttp = url.protocol === \"http:\";\n const isLocal = url.hostname === \"127.0.0.1\" || url.hostname === \"localhost\";\n const port = parseInt(url.port);\n if (isHttp && isLocal && Number.isFinite(port)) {\n proxy = await startTcpProxy({ targetPort: port, latency: options.proxyLatencyMs ?? 0 });\n // Override client connection host:port to proxy\n confOverrides = { hostAndPort: `127.0.0.1:${proxy.port}` } as Partial<PlClientConfig>;\n } else {\n console.warn(\n \"*** skipping proxy-based test, PL_ADDRESS is not localhost\",\n process.env.PL_ADDRESS,\n );\n return;\n }\n } catch {\n // ignore proxy setup errors; tests will run against original address\n }\n }\n\n const client = await getTestClient(alternativeRoot, confOverrides);\n altRootId = client.clientRoot;\n try {\n const value = await body(client, proxy);\n const rawClient = await getTestClient();\n try {\n await rawClient.deleteAlternativeRoot(alternativeRoot);\n } catch (cleanupErr: any) {\n // Cleanup may fail if test intentionally deleted resources\n console.warn(`Failed to clean up alternative root ${alternativeRoot}:`, cleanupErr.message);\n } finally {\n // Close the cleanup client to avoid dangling gRPC channels that can cause\n // segfaults during process exit\n await rawClient.close();\n }\n return value;\n } finally {\n // Close the test client to avoid dangling gRPC channels\n await client.close();\n }\n } catch (err: any) {\n console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);\n throw err;\n // throw new Error('withTempRoot error: ' + err.message, { cause: err });\n } finally {\n // Stop proxy if started\n if (proxy) {\n try {\n await proxy.disconnectAll();\n } catch {\n /* ignore */\n }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch {\n /* ignore */\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"test_config.js","names":["fs"],"sources":["../../src/test/test_config.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport { LLPlClient } from \"../core/ll_client\";\nimport type { AuthInformation, AuthOps, PlClientConfig } from \"../core/config\";\nimport { plAddressToConfig } from \"../core/config\";\nimport { UnauthenticatedPlClient } from \"../core/unauth_client\";\nimport { PlClient } from \"../core/client\";\nimport { randomUUID } from \"node:crypto\";\nimport type { OptionalResourceId } from \"../core/types\";\nimport { NullResourceId, resourceIdToString } from \"../core/types\";\nimport { inferAuthRefreshTime } from \"../core/auth\";\nimport * as path from \"node:path\";\nimport type { TestTcpProxy } from \"./tcp-proxy\";\nimport { startTcpProxy } from \"./tcp-proxy\";\n\nexport { TestTcpProxy };\n\nexport interface TestConfig {\n address: string;\n test_proxy?: string;\n test_user?: string;\n test_password?: string;\n}\n\nconst CONFIG_FILE = \"test_config.json\";\n// const AUTH_DATA_FILE = '.test_auth.json';\n\nlet authDataFilePath: string | undefined;\n\nfunction getFullAuthDataFilePath() {\n if (authDataFilePath === undefined) authDataFilePath = path.resolve(\".test_auth.json\");\n return authDataFilePath;\n}\n\nexport function getTestConfig(): TestConfig {\n let conf: Partial<TestConfig> = {};\n if (fs.existsSync(CONFIG_FILE))\n conf = JSON.parse(fs.readFileSync(CONFIG_FILE, { encoding: \"utf-8\" })) as TestConfig;\n\n if (process.env.PL_ADDRESS !== undefined) conf.address = process.env.PL_ADDRESS;\n\n if (process.env.PL_TEST_USER !== undefined) conf.test_user = process.env.PL_TEST_USER;\n\n if (process.env.PL_TEST_PASSWORD !== undefined) conf.test_password = process.env.PL_TEST_PASSWORD;\n\n if (process.env.PL_TEST_PROXY !== undefined) conf.test_proxy = process.env.PL_TEST_PROXY;\n\n if (conf.address === undefined)\n throw new Error(\n `can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`,\n );\n\n return conf as TestConfig;\n}\n\n/** Default request timeout for tests (ms) */\nexport const TEST_REQUEST_TIMEOUT = 500;\n\n/** Returns PlClientConfig with reduced timeout for tests */\nexport function plAddressToTestConfig(address: string): PlClientConfig {\n const plConf = plAddressToConfig(address);\n plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;\n return plConf;\n}\n\ninterface AuthCache {\n /** To check if config changed */\n conf: TestConfig;\n expiration: number;\n authInformation: AuthInformation;\n}\n\nfunction saveAuthInfoCallback(tConf: TestConfig): (authInformation: AuthInformation) => void {\n return (authInformation) => {\n const dst = getFullAuthDataFilePath();\n const tmpDst = getFullAuthDataFilePath() + randomUUID();\n fs.writeFileSync(\n tmpDst,\n Buffer.from(\n JSON.stringify({\n conf: tConf,\n authInformation,\n expiration: inferAuthRefreshTime(authInformation, 24 * 60 * 60),\n } as AuthCache),\n ),\n \"utf8\",\n );\n fs.renameSync(tmpDst, dst);\n };\n}\n\nconst cleanAuthInfoCallback = () => {\n console.warn(`Removing: ${getFullAuthDataFilePath()}`);\n fs.rmSync(getFullAuthDataFilePath());\n};\n\nexport async function getTestClientConf(): Promise<{ conf: PlClientConfig; auth: AuthOps }> {\n const tConf = getTestConfig();\n\n let authInformation: AuthInformation | undefined = undefined;\n\n // try recover from cache\n if (fs.existsSync(getFullAuthDataFilePath())) {\n try {\n const cache: AuthCache = JSON.parse(\n fs.readFileSync(getFullAuthDataFilePath(), { encoding: \"utf-8\" }),\n ) as AuthCache; // TODO runtime validation\n if (\n cache.conf.address === tConf.address &&\n cache.conf.test_user === tConf.test_user &&\n cache.conf.test_password === tConf.test_password &&\n cache.expiration > Date.now()\n )\n authInformation = cache.authInformation;\n } catch {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToTestConfig(tConf.address);\n const uClient = await UnauthenticatedPlClient.build(plConf);\n\n const requireAuth = await uClient.requireAuth();\n\n if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))\n throw new Error(\n `Server require no auth, but test user name or test password are provided via (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (requireAuth && (tConf.test_user === undefined || tConf.test_password === undefined))\n throw new Error(\n `No auth information found in config (${CONFIG_FILE}) or env variables: PL_TEST_USER and PL_TEST_PASSWORD`,\n );\n\n if (authInformation === undefined) {\n if (requireAuth) authInformation = await uClient.login(tConf.test_user!, tConf.test_password!);\n // No authorization is required\n else authInformation = {};\n\n // saving cache\n saveAuthInfoCallback(tConf)(authInformation);\n }\n\n return {\n conf: plConf,\n auth: {\n authInformation,\n onUpdate: saveAuthInfoCallback(tConf),\n onAuthError: cleanAuthInfoCallback,\n onUpdateError: cleanAuthInfoCallback,\n },\n };\n}\n\nexport async function getTestLLClient(confOverrides: Partial<PlClientConfig> = {}) {\n const { conf, auth } = await getTestClientConf();\n return await LLPlClient.build({ ...conf, ...confOverrides }, { auth });\n}\n\nexport async function getTestClient(\n alternativeRoot?: string,\n confOverrides: Partial<PlClientConfig> = {},\n) {\n const { conf, auth } = await getTestClientConf();\n if (alternativeRoot !== undefined && conf.alternativeRoot !== undefined)\n throw new Error(\"test pl address configured with alternative root\");\n return await PlClient.init({ ...conf, ...confOverrides, alternativeRoot }, auth);\n}\n\nexport type WithTempRootOptions =\n | {\n /** If true and PL_ADDRESS is http://localhost or http://127.0.0.1:<port>,\n * a TCP proxy will be started and PL client will connect through it. */\n viaTcpProxy: true;\n /** Artificial latency for proxy (ms). Default 0 */\n proxyLatencyMs?: number;\n }\n | {\n viaTcpProxy?: undefined;\n };\n\nexport async function withTempRoot<T>(body: (pl: PlClient) => Promise<T>): Promise<T | void>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: Awaited<ReturnType<typeof startTcpProxy>>) => Promise<T>,\n options: {\n viaTcpProxy: true;\n proxyLatencyMs?: number;\n },\n): Promise<T>;\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient, proxy: any) => Promise<T>,\n options: WithTempRootOptions = {},\n): Promise<T | undefined> {\n const alternativeRoot = `test_${Date.now()}_${randomUUID()}`;\n let altRootId: OptionalResourceId = NullResourceId;\n // Proxy management\n let proxy: Awaited<ReturnType<typeof startTcpProxy>> | undefined;\n let confOverrides: Partial<PlClientConfig> = {};\n try {\n // Optionally start TCP proxy and rewrite PL_ADDRESS to point to proxy\n if (options.viaTcpProxy === true && process.env.PL_ADDRESS) {\n try {\n const url = new URL(process.env.PL_ADDRESS);\n const isHttp = url.protocol === \"http:\";\n const isLocal = url.hostname === \"127.0.0.1\" || url.hostname === \"localhost\";\n const port = parseInt(url.port);\n if (isHttp && isLocal && Number.isFinite(port)) {\n proxy = await startTcpProxy({ targetPort: port, latency: options.proxyLatencyMs ?? 0 });\n // Override client connection host:port to proxy\n confOverrides = { hostAndPort: `127.0.0.1:${proxy.port}` } as Partial<PlClientConfig>;\n } else {\n console.warn(\n \"*** skipping proxy-based test, PL_ADDRESS is not localhost\",\n process.env.PL_ADDRESS,\n );\n return;\n }\n } catch {\n // ignore proxy setup errors; tests will run against original address\n }\n }\n\n const client = await getTestClient(alternativeRoot, confOverrides);\n altRootId = client.clientRoot;\n try {\n const value = await body(client, proxy);\n const rawClient = await getTestClient();\n try {\n await rawClient.deleteAlternativeRoot(alternativeRoot);\n } catch (cleanupErr: any) {\n // Cleanup may fail if test intentionally deleted resources\n console.warn(`Failed to clean up alternative root ${alternativeRoot}:`, cleanupErr.message);\n } finally {\n // Close the cleanup client to avoid dangling gRPC channels that can cause\n // segfaults during process exit\n await rawClient.close();\n }\n return value;\n } finally {\n // Close the test client to avoid dangling gRPC channels\n await client.close();\n }\n } catch (err: any) {\n console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);\n throw err;\n // throw new Error('withTempRoot error: ' + err.message, { cause: err });\n } finally {\n // Stop proxy if started\n if (proxy) {\n try {\n await proxy.disconnectAll();\n } catch {\n /* ignore */\n }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch {\n /* ignore */\n }\n }\n }\n}\n"],"mappings":"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"}
package/dist/util/pl.cjs CHANGED
@@ -1,9 +1,8 @@
1
-
2
1
  //#region src/util/pl.ts
3
2
  function parsePlJwt(token) {
4
3
  return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
5
4
  }
6
-
7
5
  //#endregion
8
6
  exports.parsePlJwt = parsePlJwt;
7
+
9
8
  //# sourceMappingURL=pl.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pl.cjs","names":[],"sources":["../../src/util/pl.ts"],"sourcesContent":["export type PlJWTPayload = {\n user: {\n login: string;\n };\n exp: number;\n iat: number;\n};\n\nexport function parsePlJwt(token: string): PlJWTPayload {\n return JSON.parse(Buffer.from(token.split(\".\")[1], \"base64\").toString());\n}\n"],"mappings":";;AAQA,SAAgB,WAAW,OAA6B;AACtD,QAAO,KAAK,MAAM,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC"}
1
+ {"version":3,"file":"pl.cjs","names":[],"sources":["../../src/util/pl.ts"],"sourcesContent":["export type PlJWTPayload = {\n user: {\n login: string;\n };\n exp: number;\n iat: number;\n};\n\nexport function parsePlJwt(token: string): PlJWTPayload {\n return JSON.parse(Buffer.from(token.split(\".\")[1], \"base64\").toString());\n}\n"],"mappings":";AAQA,SAAgB,WAAW,OAA6B;AACtD,QAAO,KAAK,MAAM,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC"}
package/dist/util/pl.js CHANGED
@@ -2,7 +2,7 @@
2
2
  function parsePlJwt(token) {
3
3
  return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString());
4
4
  }
5
-
6
5
  //#endregion
7
6
  export { parsePlJwt };
7
+
8
8
  //# sourceMappingURL=pl.js.map
@@ -1,4 +1,3 @@
1
-
2
1
  //#region src/util/util.ts
3
2
  function isArrayBufferOrView(value) {
4
3
  return value instanceof ArrayBuffer || ArrayBuffer.isView(value);
@@ -8,7 +7,7 @@ function toBytes(value) {
8
7
  else if (isArrayBufferOrView(value)) return value;
9
8
  else throw new Error(`Unexpected type: ${value}`);
10
9
  }
11
-
12
10
  //#endregion
13
11
  exports.toBytes = toBytes;
12
+
14
13
  //# sourceMappingURL=util.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.cjs","names":[],"sources":["../../src/util/util.ts"],"sourcesContent":["function isArrayBufferOrView(value: unknown): value is ArrayBufferLike {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport function toBytes(value: string | Uint8Array): Uint8Array {\n if (typeof value === \"string\") return Buffer.from(value);\n else if (isArrayBufferOrView(value)) return value;\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n else throw new Error(`Unexpected type: ${value}`);\n}\n"],"mappings":";;AAAA,SAAS,oBAAoB,OAA0C;AACrE,QAAO,iBAAiB,eAAe,YAAY,OAAO,MAAM;;AAGlE,SAAgB,QAAQ,OAAwC;AAC9D,KAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK,MAAM;UAC/C,oBAAoB,MAAM,CAAE,QAAO;KAEvC,OAAM,IAAI,MAAM,oBAAoB,QAAQ"}
1
+ {"version":3,"file":"util.cjs","names":[],"sources":["../../src/util/util.ts"],"sourcesContent":["function isArrayBufferOrView(value: unknown): value is ArrayBufferLike {\n return value instanceof ArrayBuffer || ArrayBuffer.isView(value);\n}\n\nexport function toBytes(value: string | Uint8Array): Uint8Array {\n if (typeof value === \"string\") return Buffer.from(value);\n else if (isArrayBufferOrView(value)) return value;\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n else throw new Error(`Unexpected type: ${value}`);\n}\n"],"mappings":";AAAA,SAAS,oBAAoB,OAA0C;AACrE,QAAO,iBAAiB,eAAe,YAAY,OAAO,MAAM;;AAGlE,SAAgB,QAAQ,OAAwC;AAC9D,KAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK,MAAM;UAC/C,oBAAoB,MAAM,CAAE,QAAO;KAEvC,OAAM,IAAI,MAAM,oBAAoB,QAAQ"}
package/dist/util/util.js CHANGED
@@ -7,7 +7,7 @@ function toBytes(value) {
7
7
  else if (isArrayBufferOrView(value)) return value;
8
8
  else throw new Error(`Unexpected type: ${value}`);
9
9
  }
10
-
11
10
  //#endregion
12
11
  export { toBytes };
12
+
13
13
  //# sourceMappingURL=util.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-client",
3
- "version": "2.18.5",
3
+ "version": "3.1.0",
4
4
  "description": "New TS/JS client for Platform API",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -30,20 +30,20 @@
30
30
  "undici": "~7.16.0",
31
31
  "utility-types": "^3.11.0",
32
32
  "yaml": "^2.8.0",
33
+ "@milaboratories/pl-model-common": "1.31.1",
33
34
  "@milaboratories/pl-http": "1.2.4",
34
- "@milaboratories/ts-helpers": "1.8.1",
35
- "@milaboratories/pl-model-common": "1.31.1"
35
+ "@milaboratories/ts-helpers": "1.8.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@protobuf-ts/plugin": "2.11.1",
39
39
  "@types/node": "~24.5.2",
40
- "@vitest/coverage-istanbul": "^4.0.18",
40
+ "@vitest/coverage-istanbul": "^4.1.3",
41
41
  "openapi-typescript": "^7.10.0",
42
42
  "typescript": "~5.9.3",
43
- "vitest": "^4.0.18",
44
- "@milaboratories/ts-builder": "1.3.0",
45
- "@milaboratories/build-configs": "1.5.2",
46
- "@milaboratories/ts-configs": "1.2.2"
43
+ "vitest": "^4.1.3",
44
+ "@milaboratories/ts-builder": "1.3.1",
45
+ "@milaboratories/build-configs": "2.0.0",
46
+ "@milaboratories/ts-configs": "1.2.3"
47
47
  },
48
48
  "engines": {
49
49
  "node": ">=22.19.0"
@@ -5,8 +5,14 @@ import type { AnyResourceRef } from "./transaction";
5
5
  import { PlTransaction, toGlobalResourceId, TxCommitConflict } from "./transaction";
6
6
  import { createHash } from "node:crypto";
7
7
  import type { OptionalResourceId, ResourceId } from "./types";
8
- import { ensureResourceIdNotNull, isNullResourceId, NullResourceId } from "./types";
8
+ import {
9
+ bigintToResourceId,
10
+ ensureResourceIdNotNull,
11
+ isNullResourceId,
12
+ NullResourceId,
13
+ } from "./types";
9
14
  import { ClientRoot } from "../helpers/pl";
15
+ import { isUnimplementedError } from "./errors";
10
16
  import type { MiLogger, RetryOptions } from "@milaboratories/ts-helpers";
11
17
  import { assertNever, createRetryState, nextRetryStateOrError } from "@milaboratories/ts-helpers";
12
18
  import type { PlDriver, PlDriverDefinition } from "./driver";
@@ -203,7 +209,9 @@ export class PlClient {
203
209
  return this._serverInfo!;
204
210
  }
205
211
 
206
- /** Currently implements custom logic to emulate future behaviour with single root. */
212
+ /** Discovers or creates the user's root resource.
213
+ * Tries ListUserResources RPC first (new backend), falls back to
214
+ * legacy named-resource lookup for older backends. */
207
215
  private async init() {
208
216
  if (this.initialized) throw new Error("Already initialized");
209
217
 
@@ -216,45 +224,87 @@ export class PlClient {
216
224
  this._ll = await this.buildLLPlClient(false);
217
225
  const wireProtocol = this._ll.wireProtocol;
218
226
 
219
- // calculating reproducible root name from the username
220
- const user = this._ll.authUser;
221
- const mainRootName =
222
- user === null ? AnonymousClientRoot : createHash("sha256").update(user).digest("hex");
223
-
224
227
  this._serverInfo = await this.ping();
225
228
  if (this._serverInfo.compression === MaintenanceAPI_Ping_Response_Compression.GZIP) {
226
229
  await this._ll.close();
227
230
  this._ll = await this.buildLLPlClient(true, wireProtocol);
228
231
  }
229
232
 
230
- this._clientRoot = await this._withTx("initialization", true, NullResourceId, async (tx) => {
231
- let mainRoot: AnyResourceRef;
232
-
233
- if (await tx.checkResourceNameExists(mainRootName))
234
- mainRoot = await tx.getResourceByName(mainRootName);
235
- else {
236
- mainRoot = tx.createRoot(ClientRoot);
237
- tx.setResourceName(mainRootName, mainRoot);
233
+ // Try ListUserResources first (new backend, gRPC only)
234
+ let rootFromServer: ResourceId | undefined;
235
+ try {
236
+ const responses = await this._ll.listUserResources({ limit: 1 });
237
+ for (const msg of responses) {
238
+ if (msg.entry.oneofKind === "userRoot") {
239
+ rootFromServer = bigintToResourceId(msg.entry.userRoot.resourceId);
240
+ break;
241
+ }
238
242
  }
243
+ } catch (err) {
244
+ if (!isUnimplementedError(err)) throw err;
245
+ // Backend doesn't support ListUserResources — fall through to legacy
246
+ }
239
247
 
248
+ if (rootFromServer !== undefined) {
249
+ // New path: server created/returned the root
240
250
  if (this.conf.alternativeRoot === undefined) {
241
- await tx.commit();
242
- return await toGlobalResourceId(mainRoot);
251
+ this._clientRoot = rootFromServer;
243
252
  } else {
244
- const aFId = {
245
- resourceId: mainRoot,
246
- fieldName: alternativeRootFieldName(this.conf.alternativeRoot),
247
- };
248
-
249
- const altRoot = tx.createEphemeral(ClientRoot);
250
- tx.lock(altRoot);
251
- tx.createField(aFId, "Dynamic");
252
- tx.setField(aFId, altRoot);
253
- await tx.commit();
254
-
255
- return await altRoot.globalId;
253
+ this._clientRoot = await this._withTx(
254
+ "initialization",
255
+ true,
256
+ rootFromServer,
257
+ async (tx) => {
258
+ const aFId = {
259
+ resourceId: tx.clientRoot,
260
+ fieldName: alternativeRootFieldName(this.conf.alternativeRoot!),
261
+ };
262
+
263
+ const altRoot = tx.createEphemeral(ClientRoot);
264
+ tx.lock(altRoot);
265
+ tx.createField(aFId, "Dynamic");
266
+ tx.setField(aFId, altRoot);
267
+ await tx.commit();
268
+
269
+ return await altRoot.globalId;
270
+ },
271
+ );
256
272
  }
257
- });
273
+ } else {
274
+ // Legacy path: named resource lookup
275
+ const user = this._ll.authUser;
276
+ const mainRootName =
277
+ user === null ? AnonymousClientRoot : createHash("sha256").update(user).digest("hex");
278
+
279
+ this._clientRoot = await this._withTx("initialization", true, NullResourceId, async (tx) => {
280
+ let mainRoot: AnyResourceRef;
281
+
282
+ if (await tx.checkResourceNameExists(mainRootName))
283
+ mainRoot = await tx.getResourceByName(mainRootName);
284
+ else {
285
+ mainRoot = tx.createRoot(ClientRoot);
286
+ tx.setResourceName(mainRootName, mainRoot);
287
+ }
288
+
289
+ if (this.conf.alternativeRoot === undefined) {
290
+ await tx.commit();
291
+ return await toGlobalResourceId(mainRoot);
292
+ } else {
293
+ const aFId = {
294
+ resourceId: mainRoot,
295
+ fieldName: alternativeRootFieldName(this.conf.alternativeRoot),
296
+ };
297
+
298
+ const altRoot = tx.createEphemeral(ClientRoot);
299
+ tx.lock(altRoot);
300
+ tx.createField(aFId, "Dynamic");
301
+ tx.setField(aFId, altRoot);
302
+ await tx.commit();
303
+
304
+ return await altRoot.globalId;
305
+ }
306
+ });
307
+ }
258
308
  }
259
309
 
260
310
  /** Returns true if field existed */
@@ -68,6 +68,17 @@ export function isTimeoutOrCancelError(err: unknown, nested: boolean = false): b
68
68
  return false;
69
69
  }
70
70
 
71
+ export function isUnimplementedError(err: unknown, nested: boolean = false): boolean {
72
+ if (err === undefined || err === null) return false;
73
+
74
+ if ((err as any).name == "RpcError" && (err as any).code == "UNIMPLEMENTED") return true;
75
+ if ((err as any).name == "RESTError" && (err as any).status.code == Code.UNIMPLEMENTED)
76
+ return true;
77
+ if ((err as any).cause !== undefined && !nested)
78
+ return isUnimplementedError((err as any).cause, true);
79
+ return false;
80
+ }
81
+
71
82
  export function isNotFoundError(err: unknown, nested: boolean = false): boolean {
72
83
  if (err === undefined || err === null) return false;
73
84
 
@@ -142,3 +142,11 @@ test("test https call via proxy", async () => {
142
142
  const text = await response.body.text();
143
143
  expect(text).toEqual("pong");
144
144
  });
145
+
146
+ test("list user resources returns user root", async () => {
147
+ const client = await getTestLLClient();
148
+ const responses = await client.listUserResources({ limit: 1 });
149
+ expect(responses.length).toBe(1);
150
+ const msg = responses[0];
151
+ expect(msg.entry.oneofKind).toBe("userRoot");
152
+ });
@@ -35,6 +35,7 @@ import { notEmpty, retry, withTimeout, type RetryOptions } from "@milaboratories
35
35
  import { Code } from "../proto-grpc/google/rpc/code";
36
36
  import { WebSocketBiDiStream } from "./websocket_stream";
37
37
  import {
38
+ AuthAPI_GetJWTToken_Role,
38
39
  TxAPI_ClientMessage,
39
40
  TxAPI_ServerMessage,
40
41
  } from "../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api";
@@ -470,13 +471,19 @@ export class LLPlClient implements WireClientProviderFactory {
470
471
  const meta: Record<string, string> = {};
471
472
  if (options?.authorization) meta.authorization = options.authorization;
472
473
  return (
473
- await cl.getJWTToken({ expiration: { seconds: ttlSeconds, nanos: 0 } }, { meta }).response
474
+ await cl.getJWTToken(
475
+ {
476
+ expiration: { seconds: ttlSeconds, nanos: 0 },
477
+ requestedRole: AuthAPI_GetJWTToken_Role.USER,
478
+ },
479
+ { meta },
480
+ ).response
474
481
  ).token;
475
482
  } else {
476
483
  const headers: Record<string, string> = {};
477
484
  if (options?.authorization) headers.authorization = options.authorization;
478
485
  const resp = cl.POST("/v1/auth/jwt-token", {
479
- body: { expiration: `${ttlSeconds}s` },
486
+ body: { expiration: `${ttlSeconds}s`, requestedRole: AuthAPI_GetJWTToken_Role.USER },
480
487
  headers,
481
488
  });
482
489
  return notEmpty((await resp).data, "REST: empty response for JWT token request").token;
@@ -584,6 +591,27 @@ export class LLPlClient implements WireClientProviderFactory {
584
591
  }
585
592
  }
586
593
 
594
+ public async listUserResources(
595
+ opts: { login?: string; startFrom?: bigint; limit?: number } = {},
596
+ ): Promise<grpcTypes.AuthAPI_ListUserResources_Response[]> {
597
+ const cl = this.clientProvider.get();
598
+
599
+ if (!(cl instanceof GrpcPlApiClient)) {
600
+ throw new Error("ListUserResources requires gRPC wire protocol; REST is not supported");
601
+ }
602
+
603
+ const call = cl.listUserResources({
604
+ login: opts.login ?? "",
605
+ startFrom: opts.startFrom ?? 0n,
606
+ limit: opts.limit ?? 0,
607
+ });
608
+ const responses: grpcTypes.AuthAPI_ListUserResources_Response[] = [];
609
+ for await (const msg of call.responses) {
610
+ responses.push(msg);
611
+ }
612
+ return responses;
613
+ }
614
+
587
615
  public async txSync(txId: bigint): Promise<void> {
588
616
  const cl = this.clientProvider.get();
589
617
  if (cl instanceof GrpcPlApiClient) {
@@ -115,6 +115,7 @@ test("check timeout error type (active)", async () => {
115
115
  type: { name: "TestValue", version: "1" },
116
116
  data: rData,
117
117
  errorIfExists: false,
118
+ colorProof: new Uint8Array(0),
118
119
  },
119
120
  },
120
121
  false,
@@ -125,7 +126,11 @@ test("check timeout error type (active)", async () => {
125
126
  const vr = await tx.send(
126
127
  {
127
128
  oneofKind: "resourceGet",
128
- resourceGet: { resourceId: id, loadFields: false },
129
+ resourceGet: {
130
+ resourceId: id,
131
+ loadFields: false,
132
+ resourceSignature: new Uint8Array(0),
133
+ },
129
134
  },
130
135
  false,
131
136
  );
@@ -174,6 +179,7 @@ test("check is abort error (active)", async () => {
174
179
  type: { name: "TestValue", version: "1" },
175
180
  data: rData,
176
181
  errorIfExists: false,
182
+ colorProof: new Uint8Array(0),
177
183
  },
178
184
  },
179
185
  false,
@@ -184,7 +190,11 @@ test("check is abort error (active)", async () => {
184
190
  const vr = await tx.send(
185
191
  {
186
192
  oneofKind: "resourceGet",
187
- resourceGet: { resourceId: id, loadFields: false },
193
+ resourceGet: {
194
+ resourceId: id,
195
+ loadFields: false,
196
+ resourceSignature: new Uint8Array(0),
197
+ },
188
198
  },
189
199
  false,
190
200
  );