@milaboratories/pl-client 3.0.0 → 3.1.1

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 (198) 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 +24 -13
  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 +13 -2
  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 +30 -19
  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 +21 -10
  76. package/dist/core/transaction.js.map +1 -1
  77. package/dist/core/type_conversion.cjs +7 -8
  78. package/dist/core/type_conversion.cjs.map +1 -1
  79. package/dist/core/type_conversion.js +3 -4
  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 +115 -421
  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 +3 -4
  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 +4 -4
  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 +1 -2
  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.map +1 -0
  132. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +108 -414
  133. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
  134. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.cjs +5 -6
  135. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.cjs.map +1 -1
  136. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.d.ts.map +1 -0
  137. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.js +1 -2
  138. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api_types.js.map +1 -1
  139. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.cjs +2 -3
  140. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.cjs.map +1 -1
  141. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.d.ts.map +1 -0
  142. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.js +1 -2
  143. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/base_types.js.map +1 -1
  144. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.cjs +6 -16
  145. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.cjs.map +1 -1
  146. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.d.ts.map +1 -0
  147. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.js +4 -14
  148. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/resource_types.js.map +1 -1
  149. package/dist/proto-grpc/google/protobuf/any.cjs +2 -3
  150. package/dist/proto-grpc/google/protobuf/any.cjs.map +1 -1
  151. package/dist/proto-grpc/google/protobuf/any.d.ts.map +1 -0
  152. package/dist/proto-grpc/google/protobuf/any.js +1 -2
  153. package/dist/proto-grpc/google/protobuf/any.js.map +1 -1
  154. package/dist/proto-grpc/google/protobuf/duration.cjs +2 -3
  155. package/dist/proto-grpc/google/protobuf/duration.cjs.map +1 -1
  156. package/dist/proto-grpc/google/protobuf/duration.d.ts.map +1 -0
  157. package/dist/proto-grpc/google/protobuf/duration.js +1 -2
  158. package/dist/proto-grpc/google/protobuf/duration.js.map +1 -1
  159. package/dist/proto-grpc/google/protobuf/timestamp.cjs +2 -3
  160. package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
  161. package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -0
  162. package/dist/proto-grpc/google/protobuf/timestamp.js +1 -2
  163. package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
  164. package/dist/proto-grpc/google/rpc/code.cjs +1 -2
  165. package/dist/proto-grpc/google/rpc/code.cjs.map +1 -1
  166. package/dist/proto-grpc/google/rpc/code.d.ts.map +1 -0
  167. package/dist/proto-grpc/google/rpc/code.js +1 -1
  168. package/dist/proto-grpc/google/rpc/code.js.map +1 -1
  169. package/dist/proto-rest/index.cjs +8 -9
  170. package/dist/proto-rest/index.cjs.map +1 -1
  171. package/dist/proto-rest/index.d.ts.map +1 -0
  172. package/dist/proto-rest/index.js +1 -2
  173. package/dist/proto-rest/index.js.map +1 -1
  174. package/dist/proto-rest/plapi.d.ts.map +1 -0
  175. package/dist/test/tcp-proxy.cjs +2 -3
  176. package/dist/test/tcp-proxy.cjs.map +1 -1
  177. package/dist/test/tcp-proxy.d.ts.map +1 -0
  178. package/dist/test/tcp-proxy.js +1 -2
  179. package/dist/test/tcp-proxy.js.map +1 -1
  180. package/dist/test/test_config.cjs +16 -19
  181. package/dist/test/test_config.cjs.map +1 -1
  182. package/dist/test/test_config.d.ts.map +1 -0
  183. package/dist/test/test_config.js +3 -6
  184. package/dist/test/test_config.js.map +1 -1
  185. package/dist/util/pl.cjs +1 -2
  186. package/dist/util/pl.cjs.map +1 -1
  187. package/dist/util/pl.js +1 -1
  188. package/dist/util/util.cjs +1 -2
  189. package/dist/util/util.cjs.map +1 -1
  190. package/dist/util/util.js +1 -1
  191. package/package.json +7 -7
  192. package/src/core/client.ts +80 -30
  193. package/src/core/errors.ts +11 -0
  194. package/src/core/ll_client.test.ts +15 -0
  195. package/src/core/ll_client.ts +22 -1
  196. package/src/core/ll_transaction.test.ts +12 -2
  197. package/src/core/transaction.ts +8 -2
  198. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-proxy.cjs","names":["timers","net","Transform"],"sources":["../../src/test/tcp-proxy.ts"],"sourcesContent":["import * as net from \"node:net\";\nimport type { AddressInfo } from \"node:net\";\nimport { Transform } from \"node:stream\";\nimport type { TransformCallback } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as timers from \"node:timers/promises\";\n\nexport type TcpProxyOptions = {\n port?: number;\n targetPort: number;\n latency: number;\n verbose?: boolean;\n};\n\nexport async function startTcpProxy(options: TcpProxyOptions) {\n const { port, targetPort } = options;\n\n const state = {\n latency: options.latency,\n };\n\n const setLatency = (latency: number) => {\n state.latency = latency;\n };\n\n const getLatency = () => {\n return state.latency;\n };\n\n const connections = new Set<{ socket: net.Socket; client: net.Socket }>();\n\n async function disconnectAll(delayMs: number | undefined = undefined) {\n const kill = () => {\n for (const { socket, client } of connections) {\n if (!socket.destroyed) socket.destroy();\n if (!client.destroyed) client.destroy();\n }\n connections.clear();\n };\n if (delayMs !== undefined) await timers.setTimeout(delayMs);\n kill();\n }\n\n const server = net.createServer((socket: net.Socket) => {\n const client = net.createConnection({ port: targetPort }, () => {\n if (options.verbose) console.log(`connected to ${targetPort}`);\n });\n\n const pair = { socket, client };\n connections.add(pair);\n const onClose = () => connections.delete(pair);\n socket.on(\"close\", onClose);\n client.on(\"close\", onClose);\n\n class LatencyTransform extends Transform {\n private pendingTimer?: NodeJS.Timeout;\n constructor() {\n super();\n }\n\n _transform(chunk: Buffer, _enc: BufferEncoding, callback: TransformCallback) {\n // Backpressure is respected by delaying the callback until after push\n this.pendingTimer = setTimeout(() => {\n this.pendingTimer = undefined;\n this.push(chunk);\n callback();\n }, state.latency);\n }\n\n _destroy(err: Error | null, cb: (error?: Error | null) => void) {\n if (this.pendingTimer) clearTimeout(this.pendingTimer);\n this.pendingTimer = undefined;\n cb(err);\n }\n }\n\n const toClientLatency = new LatencyTransform();\n const toTargetLatency = new LatencyTransform();\n\n // Bidirectional pipelines with latency and error propagation\n pipeline(socket, toTargetLatency, client).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n\n pipeline(client, toClientLatency, socket).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n });\n\n server.listen({ port: port ?? 0 }, () => {\n console.log(\"Test TCP proxy server started on\", server.address());\n });\n\n // Wait for proxy to be ready\n await new Promise<void>((resolve, reject) => {\n if (server.listening) return resolve();\n const onError = (err: Error) => {\n server.off(\"listening\", onListening);\n reject(err);\n };\n const onListening = () => {\n server.off(\"error\", onError);\n resolve();\n };\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n });\n\n return {\n server,\n get port() {\n return (server.address() as AddressInfo)?.port;\n },\n setLatency,\n getLatency,\n disconnectAll,\n close: async () => {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n },\n };\n}\n\nexport type TestTcpProxy = Awaited<ReturnType<typeof startTcpProxy>>;\n"],"mappings":";;;;;;;;;AAcA,eAAsB,cAAc,SAA0B;CAC5D,MAAM,EAAE,MAAM,eAAe;CAE7B,MAAM,QAAQ,EACZ,SAAS,QAAQ,SAClB;CAED,MAAM,cAAc,YAAoB;AACtC,QAAM,UAAU;;CAGlB,MAAM,mBAAmB;AACvB,SAAO,MAAM;;CAGf,MAAM,8BAAc,IAAI,KAAiD;CAEzE,eAAe,cAAc,UAA8B,QAAW;EACpE,MAAM,aAAa;AACjB,QAAK,MAAM,EAAE,QAAQ,YAAY,aAAa;AAC5C,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;AACvC,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;;AAEzC,eAAY,OAAO;;AAErB,MAAI,YAAY,OAAW,OAAMA,qBAAO,WAAW,QAAQ;AAC3D,QAAM;;CAGR,MAAM,SAASC,SAAI,cAAc,WAAuB;EACtD,MAAM,SAASA,SAAI,iBAAiB,EAAE,MAAM,YAAY,QAAQ;AAC9D,OAAI,QAAQ,QAAS,SAAQ,IAAI,gBAAgB,aAAa;IAC9D;EAEF,MAAM,OAAO;GAAE;GAAQ;GAAQ;AAC/B,cAAY,IAAI,KAAK;EACrB,MAAM,gBAAgB,YAAY,OAAO,KAAK;AAC9C,SAAO,GAAG,SAAS,QAAQ;AAC3B,SAAO,GAAG,SAAS,QAAQ;EAE3B,MAAM,yBAAyBC,sBAAU;GACvC,AAAQ;GACR,cAAc;AACZ,WAAO;;GAGT,WAAW,OAAe,MAAsB,UAA6B;AAE3E,SAAK,eAAe,iBAAiB;AACnC,UAAK,eAAe;AACpB,UAAK,KAAK,MAAM;AAChB,eAAU;OACT,MAAM,QAAQ;;GAGnB,SAAS,KAAmB,IAAoC;AAC9D,QAAI,KAAK,aAAc,cAAa,KAAK,aAAa;AACtD,SAAK,eAAe;AACpB,OAAG,IAAI;;;EAIX,MAAM,kBAAkB,IAAI,kBAAkB;AAI9C,qCAAS,QAHe,IAAI,kBAAkB,EAGZ,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;AAEF,qCAAS,QAAQ,iBAAiB,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;GACF;AAEF,QAAO,OAAO,EAAE,MAAM,QAAQ,GAAG,QAAQ;AACvC,UAAQ,IAAI,oCAAoC,OAAO,SAAS,CAAC;GACjE;AAGF,OAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,MAAI,OAAO,UAAW,QAAO,SAAS;EACtC,MAAM,WAAW,QAAe;AAC9B,UAAO,IAAI,aAAa,YAAY;AACpC,UAAO,IAAI;;EAEb,MAAM,oBAAoB;AACxB,UAAO,IAAI,SAAS,QAAQ;AAC5B,YAAS;;AAEX,SAAO,KAAK,SAAS,QAAQ;AAC7B,SAAO,KAAK,aAAa,YAAY;GACrC;AAEF,QAAO;EACL;EACA,IAAI,OAAO;AACT,UAAQ,OAAO,SAAS,EAAkB;;EAE5C;EACA;EACA;EACA,OAAO,YAAY;AACjB,SAAM,IAAI,SAAe,YAAY;AACnC,WAAO,YAAY,SAAS,CAAC;KAC7B;;EAEL"}
1
+ {"version":3,"file":"tcp-proxy.cjs","names":["timers","net","Transform"],"sources":["../../src/test/tcp-proxy.ts"],"sourcesContent":["import * as net from \"node:net\";\nimport type { AddressInfo } from \"node:net\";\nimport { Transform } from \"node:stream\";\nimport type { TransformCallback } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as timers from \"node:timers/promises\";\n\nexport type TcpProxyOptions = {\n port?: number;\n targetPort: number;\n latency: number;\n verbose?: boolean;\n};\n\nexport async function startTcpProxy(options: TcpProxyOptions) {\n const { port, targetPort } = options;\n\n const state = {\n latency: options.latency,\n };\n\n const setLatency = (latency: number) => {\n state.latency = latency;\n };\n\n const getLatency = () => {\n return state.latency;\n };\n\n const connections = new Set<{ socket: net.Socket; client: net.Socket }>();\n\n async function disconnectAll(delayMs: number | undefined = undefined) {\n const kill = () => {\n for (const { socket, client } of connections) {\n if (!socket.destroyed) socket.destroy();\n if (!client.destroyed) client.destroy();\n }\n connections.clear();\n };\n if (delayMs !== undefined) await timers.setTimeout(delayMs);\n kill();\n }\n\n const server = net.createServer((socket: net.Socket) => {\n const client = net.createConnection({ port: targetPort }, () => {\n if (options.verbose) console.log(`connected to ${targetPort}`);\n });\n\n const pair = { socket, client };\n connections.add(pair);\n const onClose = () => connections.delete(pair);\n socket.on(\"close\", onClose);\n client.on(\"close\", onClose);\n\n class LatencyTransform extends Transform {\n private pendingTimer?: NodeJS.Timeout;\n constructor() {\n super();\n }\n\n _transform(chunk: Buffer, _enc: BufferEncoding, callback: TransformCallback) {\n // Backpressure is respected by delaying the callback until after push\n this.pendingTimer = setTimeout(() => {\n this.pendingTimer = undefined;\n this.push(chunk);\n callback();\n }, state.latency);\n }\n\n _destroy(err: Error | null, cb: (error?: Error | null) => void) {\n if (this.pendingTimer) clearTimeout(this.pendingTimer);\n this.pendingTimer = undefined;\n cb(err);\n }\n }\n\n const toClientLatency = new LatencyTransform();\n const toTargetLatency = new LatencyTransform();\n\n // Bidirectional pipelines with latency and error propagation\n pipeline(socket, toTargetLatency, client).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n\n pipeline(client, toClientLatency, socket).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n });\n\n server.listen({ port: port ?? 0 }, () => {\n console.log(\"Test TCP proxy server started on\", server.address());\n });\n\n // Wait for proxy to be ready\n await new Promise<void>((resolve, reject) => {\n if (server.listening) return resolve();\n const onError = (err: Error) => {\n server.off(\"listening\", onListening);\n reject(err);\n };\n const onListening = () => {\n server.off(\"error\", onError);\n resolve();\n };\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n });\n\n return {\n server,\n get port() {\n return (server.address() as AddressInfo)?.port;\n },\n setLatency,\n getLatency,\n disconnectAll,\n close: async () => {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n },\n };\n}\n\nexport type TestTcpProxy = Awaited<ReturnType<typeof startTcpProxy>>;\n"],"mappings":";;;;;;;;AAcA,eAAsB,cAAc,SAA0B;CAC5D,MAAM,EAAE,MAAM,eAAe;CAE7B,MAAM,QAAQ,EACZ,SAAS,QAAQ,SAClB;CAED,MAAM,cAAc,YAAoB;AACtC,QAAM,UAAU;;CAGlB,MAAM,mBAAmB;AACvB,SAAO,MAAM;;CAGf,MAAM,8BAAc,IAAI,KAAiD;CAEzE,eAAe,cAAc,UAA8B,KAAA,GAAW;EACpE,MAAM,aAAa;AACjB,QAAK,MAAM,EAAE,QAAQ,YAAY,aAAa;AAC5C,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;AACvC,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;;AAEzC,eAAY,OAAO;;AAErB,MAAI,YAAY,KAAA,EAAW,OAAMA,qBAAO,WAAW,QAAQ;AAC3D,QAAM;;CAGR,MAAM,SAASC,SAAI,cAAc,WAAuB;EACtD,MAAM,SAASA,SAAI,iBAAiB,EAAE,MAAM,YAAY,QAAQ;AAC9D,OAAI,QAAQ,QAAS,SAAQ,IAAI,gBAAgB,aAAa;IAC9D;EAEF,MAAM,OAAO;GAAE;GAAQ;GAAQ;AAC/B,cAAY,IAAI,KAAK;EACrB,MAAM,gBAAgB,YAAY,OAAO,KAAK;AAC9C,SAAO,GAAG,SAAS,QAAQ;AAC3B,SAAO,GAAG,SAAS,QAAQ;EAE3B,MAAM,yBAAyBC,YAAAA,UAAU;GACvC;GACA,cAAc;AACZ,WAAO;;GAGT,WAAW,OAAe,MAAsB,UAA6B;AAE3E,SAAK,eAAe,iBAAiB;AACnC,UAAK,eAAe,KAAA;AACpB,UAAK,KAAK,MAAM;AAChB,eAAU;OACT,MAAM,QAAQ;;GAGnB,SAAS,KAAmB,IAAoC;AAC9D,QAAI,KAAK,aAAc,cAAa,KAAK,aAAa;AACtD,SAAK,eAAe,KAAA;AACpB,OAAG,IAAI;;;EAIX,MAAM,kBAAkB,IAAI,kBAAkB;AAI9C,GAAA,GAAA,qBAAA,UAAS,QAHe,IAAI,kBAAkB,EAGZ,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;AAEF,GAAA,GAAA,qBAAA,UAAS,QAAQ,iBAAiB,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;GACF;AAEF,QAAO,OAAO,EAAE,MAAM,QAAQ,GAAG,QAAQ;AACvC,UAAQ,IAAI,oCAAoC,OAAO,SAAS,CAAC;GACjE;AAGF,OAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,MAAI,OAAO,UAAW,QAAO,SAAS;EACtC,MAAM,WAAW,QAAe;AAC9B,UAAO,IAAI,aAAa,YAAY;AACpC,UAAO,IAAI;;EAEb,MAAM,oBAAoB;AACxB,UAAO,IAAI,SAAS,QAAQ;AAC5B,YAAS;;AAEX,SAAO,KAAK,SAAS,QAAQ;AAC7B,SAAO,KAAK,aAAa,YAAY;GACrC;AAEF,QAAO;EACL;EACA,IAAI,OAAO;AACT,UAAQ,OAAO,SAAS,EAAkB;;EAE5C;EACA;EACA;EACA,OAAO,YAAY;AACjB,SAAM,IAAI,SAAe,YAAY;AACnC,WAAO,YAAY,SAAS,CAAC;KAC7B;;EAEL"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tcp-proxy.d.ts","names":[],"sources":["../../src/test/tcp-proxy.ts"],"mappings":";;;KAOY,eAAA;EACV,IAAA;EACA,UAAA;EACA,OAAA;EACA,OAAA;AAAA;AAAA,iBAGoB,aAAA,CAAc,OAAA,EAAS,eAAA,GAAe,OAAA;;;;;mDAiBF,OAAA;;;KA+F9C,YAAA,GAAe,OAAA,CAAQ,UAAA,QAAkB,aAAA"}
@@ -2,7 +2,6 @@ import * as timers from "node:timers/promises";
2
2
  import * as net from "node:net";
3
3
  import { Transform } from "node:stream";
4
4
  import { pipeline } from "node:stream/promises";
5
-
6
5
  //#region src/test/tcp-proxy.ts
7
6
  async function startTcpProxy(options) {
8
7
  const { port, targetPort } = options;
@@ -96,7 +95,7 @@ async function startTcpProxy(options) {
96
95
  }
97
96
  };
98
97
  }
99
-
100
98
  //#endregion
101
99
  export { startTcpProxy };
100
+
102
101
  //# sourceMappingURL=tcp-proxy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-proxy.js","names":[],"sources":["../../src/test/tcp-proxy.ts"],"sourcesContent":["import * as net from \"node:net\";\nimport type { AddressInfo } from \"node:net\";\nimport { Transform } from \"node:stream\";\nimport type { TransformCallback } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as timers from \"node:timers/promises\";\n\nexport type TcpProxyOptions = {\n port?: number;\n targetPort: number;\n latency: number;\n verbose?: boolean;\n};\n\nexport async function startTcpProxy(options: TcpProxyOptions) {\n const { port, targetPort } = options;\n\n const state = {\n latency: options.latency,\n };\n\n const setLatency = (latency: number) => {\n state.latency = latency;\n };\n\n const getLatency = () => {\n return state.latency;\n };\n\n const connections = new Set<{ socket: net.Socket; client: net.Socket }>();\n\n async function disconnectAll(delayMs: number | undefined = undefined) {\n const kill = () => {\n for (const { socket, client } of connections) {\n if (!socket.destroyed) socket.destroy();\n if (!client.destroyed) client.destroy();\n }\n connections.clear();\n };\n if (delayMs !== undefined) await timers.setTimeout(delayMs);\n kill();\n }\n\n const server = net.createServer((socket: net.Socket) => {\n const client = net.createConnection({ port: targetPort }, () => {\n if (options.verbose) console.log(`connected to ${targetPort}`);\n });\n\n const pair = { socket, client };\n connections.add(pair);\n const onClose = () => connections.delete(pair);\n socket.on(\"close\", onClose);\n client.on(\"close\", onClose);\n\n class LatencyTransform extends Transform {\n private pendingTimer?: NodeJS.Timeout;\n constructor() {\n super();\n }\n\n _transform(chunk: Buffer, _enc: BufferEncoding, callback: TransformCallback) {\n // Backpressure is respected by delaying the callback until after push\n this.pendingTimer = setTimeout(() => {\n this.pendingTimer = undefined;\n this.push(chunk);\n callback();\n }, state.latency);\n }\n\n _destroy(err: Error | null, cb: (error?: Error | null) => void) {\n if (this.pendingTimer) clearTimeout(this.pendingTimer);\n this.pendingTimer = undefined;\n cb(err);\n }\n }\n\n const toClientLatency = new LatencyTransform();\n const toTargetLatency = new LatencyTransform();\n\n // Bidirectional pipelines with latency and error propagation\n pipeline(socket, toTargetLatency, client).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n\n pipeline(client, toClientLatency, socket).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n });\n\n server.listen({ port: port ?? 0 }, () => {\n console.log(\"Test TCP proxy server started on\", server.address());\n });\n\n // Wait for proxy to be ready\n await new Promise<void>((resolve, reject) => {\n if (server.listening) return resolve();\n const onError = (err: Error) => {\n server.off(\"listening\", onListening);\n reject(err);\n };\n const onListening = () => {\n server.off(\"error\", onError);\n resolve();\n };\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n });\n\n return {\n server,\n get port() {\n return (server.address() as AddressInfo)?.port;\n },\n setLatency,\n getLatency,\n disconnectAll,\n close: async () => {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n },\n };\n}\n\nexport type TestTcpProxy = Awaited<ReturnType<typeof startTcpProxy>>;\n"],"mappings":";;;;;;AAcA,eAAsB,cAAc,SAA0B;CAC5D,MAAM,EAAE,MAAM,eAAe;CAE7B,MAAM,QAAQ,EACZ,SAAS,QAAQ,SAClB;CAED,MAAM,cAAc,YAAoB;AACtC,QAAM,UAAU;;CAGlB,MAAM,mBAAmB;AACvB,SAAO,MAAM;;CAGf,MAAM,8BAAc,IAAI,KAAiD;CAEzE,eAAe,cAAc,UAA8B,QAAW;EACpE,MAAM,aAAa;AACjB,QAAK,MAAM,EAAE,QAAQ,YAAY,aAAa;AAC5C,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;AACvC,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;;AAEzC,eAAY,OAAO;;AAErB,MAAI,YAAY,OAAW,OAAM,OAAO,WAAW,QAAQ;AAC3D,QAAM;;CAGR,MAAM,SAAS,IAAI,cAAc,WAAuB;EACtD,MAAM,SAAS,IAAI,iBAAiB,EAAE,MAAM,YAAY,QAAQ;AAC9D,OAAI,QAAQ,QAAS,SAAQ,IAAI,gBAAgB,aAAa;IAC9D;EAEF,MAAM,OAAO;GAAE;GAAQ;GAAQ;AAC/B,cAAY,IAAI,KAAK;EACrB,MAAM,gBAAgB,YAAY,OAAO,KAAK;AAC9C,SAAO,GAAG,SAAS,QAAQ;AAC3B,SAAO,GAAG,SAAS,QAAQ;EAE3B,MAAM,yBAAyB,UAAU;GACvC,AAAQ;GACR,cAAc;AACZ,WAAO;;GAGT,WAAW,OAAe,MAAsB,UAA6B;AAE3E,SAAK,eAAe,iBAAiB;AACnC,UAAK,eAAe;AACpB,UAAK,KAAK,MAAM;AAChB,eAAU;OACT,MAAM,QAAQ;;GAGnB,SAAS,KAAmB,IAAoC;AAC9D,QAAI,KAAK,aAAc,cAAa,KAAK,aAAa;AACtD,SAAK,eAAe;AACpB,OAAG,IAAI;;;EAIX,MAAM,kBAAkB,IAAI,kBAAkB;AAI9C,WAAS,QAHe,IAAI,kBAAkB,EAGZ,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;AAEF,WAAS,QAAQ,iBAAiB,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;GACF;AAEF,QAAO,OAAO,EAAE,MAAM,QAAQ,GAAG,QAAQ;AACvC,UAAQ,IAAI,oCAAoC,OAAO,SAAS,CAAC;GACjE;AAGF,OAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,MAAI,OAAO,UAAW,QAAO,SAAS;EACtC,MAAM,WAAW,QAAe;AAC9B,UAAO,IAAI,aAAa,YAAY;AACpC,UAAO,IAAI;;EAEb,MAAM,oBAAoB;AACxB,UAAO,IAAI,SAAS,QAAQ;AAC5B,YAAS;;AAEX,SAAO,KAAK,SAAS,QAAQ;AAC7B,SAAO,KAAK,aAAa,YAAY;GACrC;AAEF,QAAO;EACL;EACA,IAAI,OAAO;AACT,UAAQ,OAAO,SAAS,EAAkB;;EAE5C;EACA;EACA;EACA,OAAO,YAAY;AACjB,SAAM,IAAI,SAAe,YAAY;AACnC,WAAO,YAAY,SAAS,CAAC;KAC7B;;EAEL"}
1
+ {"version":3,"file":"tcp-proxy.js","names":[],"sources":["../../src/test/tcp-proxy.ts"],"sourcesContent":["import * as net from \"node:net\";\nimport type { AddressInfo } from \"node:net\";\nimport { Transform } from \"node:stream\";\nimport type { TransformCallback } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport * as timers from \"node:timers/promises\";\n\nexport type TcpProxyOptions = {\n port?: number;\n targetPort: number;\n latency: number;\n verbose?: boolean;\n};\n\nexport async function startTcpProxy(options: TcpProxyOptions) {\n const { port, targetPort } = options;\n\n const state = {\n latency: options.latency,\n };\n\n const setLatency = (latency: number) => {\n state.latency = latency;\n };\n\n const getLatency = () => {\n return state.latency;\n };\n\n const connections = new Set<{ socket: net.Socket; client: net.Socket }>();\n\n async function disconnectAll(delayMs: number | undefined = undefined) {\n const kill = () => {\n for (const { socket, client } of connections) {\n if (!socket.destroyed) socket.destroy();\n if (!client.destroyed) client.destroy();\n }\n connections.clear();\n };\n if (delayMs !== undefined) await timers.setTimeout(delayMs);\n kill();\n }\n\n const server = net.createServer((socket: net.Socket) => {\n const client = net.createConnection({ port: targetPort }, () => {\n if (options.verbose) console.log(`connected to ${targetPort}`);\n });\n\n const pair = { socket, client };\n connections.add(pair);\n const onClose = () => connections.delete(pair);\n socket.on(\"close\", onClose);\n client.on(\"close\", onClose);\n\n class LatencyTransform extends Transform {\n private pendingTimer?: NodeJS.Timeout;\n constructor() {\n super();\n }\n\n _transform(chunk: Buffer, _enc: BufferEncoding, callback: TransformCallback) {\n // Backpressure is respected by delaying the callback until after push\n this.pendingTimer = setTimeout(() => {\n this.pendingTimer = undefined;\n this.push(chunk);\n callback();\n }, state.latency);\n }\n\n _destroy(err: Error | null, cb: (error?: Error | null) => void) {\n if (this.pendingTimer) clearTimeout(this.pendingTimer);\n this.pendingTimer = undefined;\n cb(err);\n }\n }\n\n const toClientLatency = new LatencyTransform();\n const toTargetLatency = new LatencyTransform();\n\n // Bidirectional pipelines with latency and error propagation\n pipeline(socket, toTargetLatency, client).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n\n pipeline(client, toClientLatency, socket).catch((err) => {\n socket.destroy(err);\n client.destroy(err);\n });\n });\n\n server.listen({ port: port ?? 0 }, () => {\n console.log(\"Test TCP proxy server started on\", server.address());\n });\n\n // Wait for proxy to be ready\n await new Promise<void>((resolve, reject) => {\n if (server.listening) return resolve();\n const onError = (err: Error) => {\n server.off(\"listening\", onListening);\n reject(err);\n };\n const onListening = () => {\n server.off(\"error\", onError);\n resolve();\n };\n server.once(\"error\", onError);\n server.once(\"listening\", onListening);\n });\n\n return {\n server,\n get port() {\n return (server.address() as AddressInfo)?.port;\n },\n setLatency,\n getLatency,\n disconnectAll,\n close: async () => {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n },\n };\n}\n\nexport type TestTcpProxy = Awaited<ReturnType<typeof startTcpProxy>>;\n"],"mappings":";;;;;AAcA,eAAsB,cAAc,SAA0B;CAC5D,MAAM,EAAE,MAAM,eAAe;CAE7B,MAAM,QAAQ,EACZ,SAAS,QAAQ,SAClB;CAED,MAAM,cAAc,YAAoB;AACtC,QAAM,UAAU;;CAGlB,MAAM,mBAAmB;AACvB,SAAO,MAAM;;CAGf,MAAM,8BAAc,IAAI,KAAiD;CAEzE,eAAe,cAAc,UAA8B,KAAA,GAAW;EACpE,MAAM,aAAa;AACjB,QAAK,MAAM,EAAE,QAAQ,YAAY,aAAa;AAC5C,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;AACvC,QAAI,CAAC,OAAO,UAAW,QAAO,SAAS;;AAEzC,eAAY,OAAO;;AAErB,MAAI,YAAY,KAAA,EAAW,OAAM,OAAO,WAAW,QAAQ;AAC3D,QAAM;;CAGR,MAAM,SAAS,IAAI,cAAc,WAAuB;EACtD,MAAM,SAAS,IAAI,iBAAiB,EAAE,MAAM,YAAY,QAAQ;AAC9D,OAAI,QAAQ,QAAS,SAAQ,IAAI,gBAAgB,aAAa;IAC9D;EAEF,MAAM,OAAO;GAAE;GAAQ;GAAQ;AAC/B,cAAY,IAAI,KAAK;EACrB,MAAM,gBAAgB,YAAY,OAAO,KAAK;AAC9C,SAAO,GAAG,SAAS,QAAQ;AAC3B,SAAO,GAAG,SAAS,QAAQ;EAE3B,MAAM,yBAAyB,UAAU;GACvC;GACA,cAAc;AACZ,WAAO;;GAGT,WAAW,OAAe,MAAsB,UAA6B;AAE3E,SAAK,eAAe,iBAAiB;AACnC,UAAK,eAAe,KAAA;AACpB,UAAK,KAAK,MAAM;AAChB,eAAU;OACT,MAAM,QAAQ;;GAGnB,SAAS,KAAmB,IAAoC;AAC9D,QAAI,KAAK,aAAc,cAAa,KAAK,aAAa;AACtD,SAAK,eAAe,KAAA;AACpB,OAAG,IAAI;;;EAIX,MAAM,kBAAkB,IAAI,kBAAkB;AAI9C,WAAS,QAHe,IAAI,kBAAkB,EAGZ,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;AAEF,WAAS,QAAQ,iBAAiB,OAAO,CAAC,OAAO,QAAQ;AACvD,UAAO,QAAQ,IAAI;AACnB,UAAO,QAAQ,IAAI;IACnB;GACF;AAEF,QAAO,OAAO,EAAE,MAAM,QAAQ,GAAG,QAAQ;AACvC,UAAQ,IAAI,oCAAoC,OAAO,SAAS,CAAC;GACjE;AAGF,OAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,MAAI,OAAO,UAAW,QAAO,SAAS;EACtC,MAAM,WAAW,QAAe;AAC9B,UAAO,IAAI,aAAa,YAAY;AACpC,UAAO,IAAI;;EAEb,MAAM,oBAAoB;AACxB,UAAO,IAAI,SAAS,QAAQ;AAC5B,YAAS;;AAEX,SAAO,KAAK,SAAS,QAAQ;AAC7B,SAAO,KAAK,aAAa,YAAY;GACrC;AAEF,QAAO;EACL;EACA,IAAI,OAAO;AACT,UAAQ,OAAO,SAAS,EAAkB;;EAE5C;EACA;EACA;EACA,OAAO,YAAY;AACjB,SAAM,IAAI,SAAe,YAAY;AACnC,WAAO,YAAY,SAAS,CAAC;KAC7B;;EAEL"}
@@ -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": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "New TS/JS client for Platform API",
5
5
  "files": [
6
6
  "./dist/**/*",
@@ -32,18 +32,18 @@
32
32
  "yaml": "^2.8.0",
33
33
  "@milaboratories/pl-http": "1.2.4",
34
34
  "@milaboratories/ts-helpers": "1.8.1",
35
- "@milaboratories/pl-model-common": "1.31.1"
35
+ "@milaboratories/pl-model-common": "1.31.2"
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/build-configs": "1.5.2",
45
- "@milaboratories/ts-builder": "1.3.0",
46
- "@milaboratories/ts-configs": "1.2.2"
43
+ "vitest": "^4.1.3",
44
+ "@milaboratories/build-configs": "2.0.0",
45
+ "@milaboratories/ts-builder": "1.3.1",
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,18 @@ 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
+
150
+ // First message is always the user root.
151
+ expect(responses.length).toBeGreaterThanOrEqual(1);
152
+ expect(responses[0].entry.oneofKind).toBe("userRoot");
153
+
154
+ // If there are more resources, the last message is a continuation token.
155
+ if (responses.length > 1) {
156
+ const last = responses[responses.length - 1];
157
+ expect(last.nextResourceId).not.toBe(0n);
158
+ }
159
+ });