@milaboratories/pl-client 2.16.3 → 2.16.5

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.
@@ -37,7 +37,7 @@ async function startTcpProxy(options) {
37
37
  return state.latency;
38
38
  };
39
39
  const connections = new Set();
40
- async function disconnectAll() {
40
+ async function disconnectAll(delayMs = undefined) {
41
41
  const kill = () => {
42
42
  for (const { socket, client } of connections) {
43
43
  if (!socket.destroyed)
@@ -47,7 +47,8 @@ async function startTcpProxy(options) {
47
47
  }
48
48
  connections.clear();
49
49
  };
50
- await tp__namespace.setTimeout(1);
50
+ if (delayMs !== undefined)
51
+ await tp__namespace.setTimeout(delayMs);
51
52
  kill();
52
53
  }
53
54
  const server = net__namespace
@@ -94,7 +95,7 @@ async function startTcpProxy(options) {
94
95
  });
95
96
  });
96
97
  server.listen({ port: port ?? 0 }, () => {
97
- console.log('opened server on', server.address());
98
+ console.log('Test TCP proxy server started on', server.address());
98
99
  });
99
100
  // Wait for proxy to be ready
100
101
  await new Promise((resolve, reject) => {
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-proxy.cjs","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() {\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 await timers.setTimeout(1);\n kill();\n };\n\n const server = net\n .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('opened server 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() { return (server.address() as AddressInfo)?.port; },\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"],"names":["timers","net","Transform","pipeline"],"mappings":"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"}
1
+ {"version":3,"file":"tcp-proxy.cjs","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\n .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() { return (server.address() as AddressInfo)?.port; },\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"],"names":["timers","net","Transform","pipeline"],"mappings":"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"}
@@ -10,7 +10,7 @@ export declare function startTcpProxy(options: TcpProxyOptions): Promise<{
10
10
  readonly port: number;
11
11
  setLatency: (latency: number) => void;
12
12
  getLatency: () => number;
13
- disconnectAll: () => Promise<void>;
13
+ disconnectAll: (delayMs?: number | undefined) => Promise<void>;
14
14
  close: () => Promise<void>;
15
15
  }>;
16
16
  export type TestTcpProxy = Awaited<ReturnType<typeof startTcpProxy>>;
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-proxy.d.ts","sourceRoot":"","sources":["../../src/test/tcp-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAOhC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe;;;0BAO7B,MAAM;;;;GAsGpC;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"tcp-proxy.d.ts","sourceRoot":"","sources":["../../src/test/tcp-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAOhC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe;;;0BAO7B,MAAM;;8BAUG,MAAM,GAAG,SAAS;;GA4FzD;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAC"}
@@ -15,7 +15,7 @@ async function startTcpProxy(options) {
15
15
  return state.latency;
16
16
  };
17
17
  const connections = new Set();
18
- async function disconnectAll() {
18
+ async function disconnectAll(delayMs = undefined) {
19
19
  const kill = () => {
20
20
  for (const { socket, client } of connections) {
21
21
  if (!socket.destroyed)
@@ -25,7 +25,8 @@ async function startTcpProxy(options) {
25
25
  }
26
26
  connections.clear();
27
27
  };
28
- await tp.setTimeout(1);
28
+ if (delayMs !== undefined)
29
+ await tp.setTimeout(delayMs);
29
30
  kill();
30
31
  }
31
32
  const server = net
@@ -72,7 +73,7 @@ async function startTcpProxy(options) {
72
73
  });
73
74
  });
74
75
  server.listen({ port: port ?? 0 }, () => {
75
- console.log('opened server on', server.address());
76
+ console.log('Test TCP proxy server started on', server.address());
76
77
  });
77
78
  // Wait for proxy to be ready
78
79
  await new Promise((resolve, reject) => {
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-proxy.js","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() {\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 await timers.setTimeout(1);\n kill();\n };\n\n const server = net\n .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('opened server 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() { return (server.address() as AddressInfo)?.port; },\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"],"names":["timers"],"mappings":"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"}
1
+ {"version":3,"file":"tcp-proxy.js","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\n .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() { return (server.address() as AddressInfo)?.port; },\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"],"names":["timers"],"mappings":"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"}
@@ -155,7 +155,6 @@ async function withTempRoot(body, options = {}) {
155
155
  }
156
156
  const client = await getTestClient(alternativeRoot, confOverrides);
157
157
  altRootId = client.clientRoot;
158
- console.log('altRootId', altRootId, altRootId.toString(16));
159
158
  const value = await body(client, proxy);
160
159
  const rawClient = await getTestClient();
161
160
  try {
@@ -168,7 +167,6 @@ async function withTempRoot(body, options = {}) {
168
167
  return value;
169
168
  }
170
169
  catch (err) {
171
- console.log('ERROR stack trace:', err.stack);
172
170
  console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${types.resourceIdToString(altRootId)})`);
173
171
  throw err;
174
172
  // throw new Error('withTempRoot error: ' + err.message, { cause: err });
@@ -1 +1 @@
1
- {"version":3,"file":"test_config.cjs","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 {\n TestTcpProxy,\n};\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\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 (_e) {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToConfig(tConf.address);\n\n const uClient = new UnauthenticatedPlClient(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 new LLPlClient({ ...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 /** 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 viaTcpProxy?: undefined;\n};\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient) => Promise<T>\n): 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('*** skipping proxy-based test, PL_ADDRESS is not localhost', process.env.PL_ADDRESS);\n return;\n }\n } catch (_e) {\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 console.log('altRootId', altRootId, altRootId.toString(16));\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 }\n return value;\n } catch (err: any) {\n console.log('ERROR stack trace:', err.stack);\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 (_e) { /* ignore */ }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch (_e) { /* ignore */ }\n }\n }\n}\n"],"names":["path","fs","randomUUID","inferAuthRefreshTime","plAddressToConfig","UnauthenticatedPlClient","LLPlClient","PlClient","NullResourceId","startTcpProxy","resourceIdToString"],"mappings":"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"}
1
+ {"version":3,"file":"test_config.cjs","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 {\n TestTcpProxy,\n};\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\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 (_e) {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToConfig(tConf.address);\n\n const uClient = new UnauthenticatedPlClient(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 new LLPlClient({ ...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 /** 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 viaTcpProxy?: undefined;\n};\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient) => Promise<T>\n): 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('*** skipping proxy-based test, PL_ADDRESS is not localhost', process.env.PL_ADDRESS);\n return;\n }\n } catch (_e) {\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 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 }\n return value;\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 (_e) { /* ignore */ }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch (_e) { /* ignore */ }\n }\n }\n}\n"],"names":["path","fs","randomUUID","inferAuthRefreshTime","plAddressToConfig","UnauthenticatedPlClient","LLPlClient","PlClient","NullResourceId","startTcpProxy","resourceIdToString"],"mappings":"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"}
@@ -133,7 +133,6 @@ async function withTempRoot(body, options = {}) {
133
133
  }
134
134
  const client = await getTestClient(alternativeRoot, confOverrides);
135
135
  altRootId = client.clientRoot;
136
- console.log('altRootId', altRootId, altRootId.toString(16));
137
136
  const value = await body(client, proxy);
138
137
  const rawClient = await getTestClient();
139
138
  try {
@@ -146,7 +145,6 @@ async function withTempRoot(body, options = {}) {
146
145
  return value;
147
146
  }
148
147
  catch (err) {
149
- console.log('ERROR stack trace:', err.stack);
150
148
  console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);
151
149
  throw err;
152
150
  // throw new Error('withTempRoot error: ' + err.message, { cause: err });
@@ -1 +1 @@
1
- {"version":3,"file":"test_config.js","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 {\n TestTcpProxy,\n};\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\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 (_e) {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToConfig(tConf.address);\n\n const uClient = new UnauthenticatedPlClient(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 new LLPlClient({ ...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 /** 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 viaTcpProxy?: undefined;\n};\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient) => Promise<T>\n): 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('*** skipping proxy-based test, PL_ADDRESS is not localhost', process.env.PL_ADDRESS);\n return;\n }\n } catch (_e) {\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 console.log('altRootId', altRootId, altRootId.toString(16));\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 }\n return value;\n } catch (err: any) {\n console.log('ERROR stack trace:', err.stack);\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 (_e) { /* ignore */ }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch (_e) { /* ignore */ }\n }\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test_config.js","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 {\n TestTcpProxy,\n};\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\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 (_e) {\n // removing cache file on any error\n fs.rmSync(getFullAuthDataFilePath());\n }\n }\n\n const plConf = plAddressToConfig(tConf.address);\n\n const uClient = new UnauthenticatedPlClient(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 new LLPlClient({ ...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 /** 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 viaTcpProxy?: undefined;\n};\n\nexport async function withTempRoot<T>(\n body: (pl: PlClient) => Promise<T>\n): 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('*** skipping proxy-based test, PL_ADDRESS is not localhost', process.env.PL_ADDRESS);\n return;\n }\n } catch (_e) {\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 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 }\n return value;\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 (_e) { /* ignore */ }\n try {\n await new Promise<void>((resolve) => proxy!.server.close(() => resolve()));\n } catch (_e) { /* ignore */ }\n }\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-client",
3
- "version": "2.16.3",
3
+ "version": "2.16.5",
4
4
  "engines": {
5
5
  "node": ">=22.19.0"
6
6
  },
@@ -35,8 +35,8 @@
35
35
  "utility-types": "^3.11.0",
36
36
  "yaml": "^2.8.0",
37
37
  "@milaboratories/pl-http": "1.2.0",
38
- "@milaboratories/pl-model-common": "1.21.5",
39
- "@milaboratories/ts-helpers": "1.5.2"
38
+ "@milaboratories/ts-helpers": "1.5.3",
39
+ "@milaboratories/pl-model-common": "1.21.5"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@protobuf-ts/plugin": "2.11.1",
@@ -5,7 +5,7 @@ import { test, expect } from 'vitest';
5
5
  import { sleep } from '@milaboratories/ts-helpers';
6
6
  import { DisconnectedError } from './errors';
7
7
 
8
- test('connectivity: disconnect', async () => {
8
+ test('connectivity: disconnect during tx', async () => {
9
9
  await withTempRoot(async (pl, proxy) => {
10
10
  await expect(async () => {
11
11
  await pl.withWriteTx('resource1', async (tx) => {
@@ -21,7 +21,7 @@ test('connectivity: disconnect', async () => {
21
21
 
22
22
  await proxy?.disconnectAll();
23
23
 
24
- await sleep(100);
24
+ await sleep(1);
25
25
 
26
26
  const theField1 = { resourceId: r1, fieldName: 'theField' };
27
27
  tx.createField(theField1, 'Input');
@@ -29,7 +29,7 @@ export async function startTcpProxy(options: TcpProxyOptions) {
29
29
 
30
30
  const connections = new Set<{ socket: net.Socket; client: net.Socket }>();
31
31
 
32
- async function disconnectAll() {
32
+ async function disconnectAll(delayMs: number | undefined = undefined) {
33
33
  const kill = () => {
34
34
  for (const { socket, client } of connections) {
35
35
  if (!socket.destroyed) socket.destroy();
@@ -37,7 +37,7 @@ export async function startTcpProxy(options: TcpProxyOptions) {
37
37
  }
38
38
  connections.clear();
39
39
  };
40
- await timers.setTimeout(1);
40
+ if (delayMs !== undefined) await timers.setTimeout(delayMs);
41
41
  kill();
42
42
  };
43
43
 
@@ -91,7 +91,7 @@ export async function startTcpProxy(options: TcpProxyOptions) {
91
91
  });
92
92
 
93
93
  server.listen({ port: port ?? 0 }, () => {
94
- console.log('opened server on', server.address());
94
+ console.log('Test TCP proxy server started on', server.address());
95
95
  });
96
96
 
97
97
  // Wait for proxy to be ready
@@ -214,7 +214,6 @@ export async function withTempRoot<T>(
214
214
 
215
215
  const client = await getTestClient(alternativeRoot, confOverrides);
216
216
  altRootId = client.clientRoot;
217
- console.log('altRootId', altRootId, altRootId.toString(16));
218
217
  const value = await body(client, proxy);
219
218
  const rawClient = await getTestClient();
220
219
  try {
@@ -225,7 +224,6 @@ export async function withTempRoot<T>(
225
224
  }
226
225
  return value;
227
226
  } catch (err: any) {
228
- console.log('ERROR stack trace:', err.stack);
229
227
  console.log(`ALTERNATIVE ROOT: ${alternativeRoot} (${resourceIdToString(altRootId)})`);
230
228
  throw err;
231
229
  // throw new Error('withTempRoot error: ' + err.message, { cause: err });