@milaboratories/pl-client 2.16.13 → 2.16.15

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 (63) hide show
  1. package/dist/__external/.pnpm/{@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3 → @rollup_plugin-typescript@12.3.0_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3}/__external/tslib/tslib.es6.cjs.map +1 -1
  2. package/dist/__external/.pnpm/{@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3 → @rollup_plugin-typescript@12.3.0_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3}/__external/tslib/tslib.es6.js.map +1 -1
  3. package/dist/core/client.cjs +31 -16
  4. package/dist/core/client.cjs.map +1 -1
  5. package/dist/core/client.d.ts +3 -2
  6. package/dist/core/client.d.ts.map +1 -1
  7. package/dist/core/client.js +31 -16
  8. package/dist/core/client.js.map +1 -1
  9. package/dist/core/default_client.cjs +1 -1
  10. package/dist/core/default_client.cjs.map +1 -1
  11. package/dist/core/default_client.js +1 -1
  12. package/dist/core/default_client.js.map +1 -1
  13. package/dist/core/errors.cjs +71 -26
  14. package/dist/core/errors.cjs.map +1 -1
  15. package/dist/core/errors.d.ts +10 -3
  16. package/dist/core/errors.d.ts.map +1 -1
  17. package/dist/core/errors.js +68 -27
  18. package/dist/core/errors.js.map +1 -1
  19. package/dist/core/ll_client.cjs +48 -18
  20. package/dist/core/ll_client.cjs.map +1 -1
  21. package/dist/core/ll_client.d.ts +12 -3
  22. package/dist/core/ll_client.d.ts.map +1 -1
  23. package/dist/core/ll_client.js +49 -19
  24. package/dist/core/ll_client.js.map +1 -1
  25. package/dist/core/transaction.cjs +1 -1
  26. package/dist/core/transaction.js +1 -1
  27. package/dist/core/unauth_client.cjs +6 -2
  28. package/dist/core/unauth_client.cjs.map +1 -1
  29. package/dist/core/unauth_client.d.ts +2 -1
  30. package/dist/core/unauth_client.d.ts.map +1 -1
  31. package/dist/core/unauth_client.js +6 -2
  32. package/dist/core/unauth_client.js.map +1 -1
  33. package/dist/core/websocket_stream.cjs +23 -2
  34. package/dist/core/websocket_stream.cjs.map +1 -1
  35. package/dist/core/websocket_stream.d.ts.map +1 -1
  36. package/dist/core/websocket_stream.js +23 -2
  37. package/dist/core/websocket_stream.js.map +1 -1
  38. package/dist/index.cjs +4 -0
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/proto-rest/index.cjs +1 -1
  42. package/dist/proto-rest/index.cjs.map +1 -1
  43. package/dist/proto-rest/index.js +2 -2
  44. package/dist/proto-rest/index.js.map +1 -1
  45. package/dist/test/test_config.cjs +13 -3
  46. package/dist/test/test_config.cjs.map +1 -1
  47. package/dist/test/test_config.d.ts +4 -0
  48. package/dist/test/test_config.d.ts.map +1 -1
  49. package/dist/test/test_config.js +12 -4
  50. package/dist/test/test_config.js.map +1 -1
  51. package/package.json +10 -8
  52. package/src/core/client.ts +40 -21
  53. package/src/core/default_client.ts +1 -1
  54. package/src/core/errors.ts +61 -34
  55. package/src/core/ll_client.test.ts +18 -3
  56. package/src/core/ll_client.ts +63 -22
  57. package/src/core/unauth_client.test.ts +4 -4
  58. package/src/core/unauth_client.ts +7 -2
  59. package/src/core/websocket_stream.ts +22 -1
  60. package/src/proto-rest/index.ts +2 -2
  61. package/src/test/test_config.ts +13 -4
  62. /package/dist/__external/.pnpm/{@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3 → @rollup_plugin-typescript@12.3.0_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3}/__external/tslib/tslib.es6.cjs +0 -0
  63. /package/dist/__external/.pnpm/{@rollup_plugin-typescript@12.1.4_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3 → @rollup_plugin-typescript@12.3.0_rollup@4.52.4_tslib@2.8.1_typescript@5.6.3}/__external/tslib/tslib.es6.js +0 -0
@@ -1,6 +1,7 @@
1
1
  import { WebSocket, ErrorEvent } from 'undici';
2
2
  import Denque from 'denque';
3
3
  import { RetryStrategy } from '../helpers/retry_strategy.js';
4
+ import { DisconnectedError } from './errors.js';
4
5
 
5
6
  var ConnectionState;
6
7
  (function (ConnectionState) {
@@ -142,6 +143,19 @@ class WebSocketBiDiStream {
142
143
  }
143
144
  onClose() {
144
145
  this.progressConnectionState(ConnectionState.CLOSED);
146
+ // If abort signal was triggered, use that as the error source
147
+ if (this.options.abortSignal?.aborted && !this.lastError) {
148
+ const reason = this.options.abortSignal.reason;
149
+ if (reason instanceof Error) {
150
+ this.lastError = reason;
151
+ }
152
+ else if (reason !== undefined) {
153
+ this.lastError = new Error(String(reason), { cause: reason });
154
+ }
155
+ else {
156
+ this.lastError = this.createStreamClosedError();
157
+ }
158
+ }
145
159
  if (!this.lastError) {
146
160
  this.rejectAllSendOperations(this.createStreamClosedError());
147
161
  this.resolveAllPendingResponses(); // unblock active async iterator
@@ -305,8 +319,15 @@ class WebSocketBiDiStream {
305
319
  toError(error) {
306
320
  if (error instanceof Error)
307
321
  return error;
308
- if (error instanceof ErrorEvent)
309
- return error.error;
322
+ if (error instanceof ErrorEvent) {
323
+ const err = error.error;
324
+ // undici WebSocket throws TypeError with empty message on socket close
325
+ // (e.g., when connection is lost or server disconnects)
326
+ if (err instanceof TypeError && !err.message) {
327
+ return new DisconnectedError('WebSocket connection closed unexpectedly');
328
+ }
329
+ return err instanceof Error ? err : new Error('WebSocket error', { cause: error });
330
+ }
310
331
  return new Error(String(error));
311
332
  }
312
333
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"websocket_stream.js","sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from 'undici';\nimport type { BiDiStream } from './abstract_stream';\nimport Denque from 'denque';\nimport type { RetryConfig } from '../helpers/retry_strategy';\nimport { RetryStrategy } from '../helpers/retry_strategy';\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<ClientMsg extends object, ServerMsg extends object> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch (_: unknown) {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => { void this.connect(); },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.options.abortSignal?.addEventListener('abort', () => this.close());\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener('open', () => this.onOpen());\n this.ws.addEventListener('message', (event) => this.onMessage(event.data));\n this.ws.addEventListener('error', (error) => this.onError(error));\n this.ws.addEventListener('close', () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken) options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = 'arraybuffer';\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error('Cannot send: stream already completed');\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error('Cannot send: stream aborted');\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error('WebSocket is not connected');\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(\n () => this.sendQueue.length === 0,\n POLL_INTERVAL_MS,\n );\n }\n }\n\n private waitForCondition(\n condition: () => boolean,\n intervalMs: number,\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error('Stream aborted'));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error('Stream aborted'));\n };\n\n this.options.abortSignal?.addEventListener('abort', onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener('abort', onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error('Stream aborted', { cause: reason });\n }\n\n return new Error('Stream closed');\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false;\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) return error.error;\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"websocket_stream.js","sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from 'undici';\nimport type { BiDiStream } from './abstract_stream';\nimport Denque from 'denque';\nimport type { RetryConfig } from '../helpers/retry_strategy';\nimport { RetryStrategy } from '../helpers/retry_strategy';\nimport { DisconnectedError } from './errors';\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<ClientMsg extends object, ServerMsg extends object> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch (_: unknown) {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => { void this.connect(); },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.options.abortSignal?.addEventListener('abort', () => this.close());\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener('open', () => this.onOpen());\n this.ws.addEventListener('message', (event) => this.onMessage(event.data));\n this.ws.addEventListener('error', (error) => this.onError(error));\n this.ws.addEventListener('close', () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken) options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = 'arraybuffer';\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n // If abort signal was triggered, use that as the error source\n if (this.options.abortSignal?.aborted && !this.lastError) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n this.lastError = reason;\n } else if (reason !== undefined) {\n this.lastError = new Error(String(reason), { cause: reason });\n } else {\n this.lastError = this.createStreamClosedError();\n }\n }\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error('Cannot send: stream already completed');\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error('Cannot send: stream aborted');\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error('WebSocket is not connected');\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(\n () => this.sendQueue.length === 0,\n POLL_INTERVAL_MS,\n );\n }\n }\n\n private waitForCondition(\n condition: () => boolean,\n intervalMs: number,\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error('Stream aborted'));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error('Stream aborted'));\n };\n\n this.options.abortSignal?.addEventListener('abort', onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener('abort', onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error('Stream aborted', { cause: reason });\n }\n\n return new Error('Stream closed');\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false;\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) {\n const err = error.error;\n // undici WebSocket throws TypeError with empty message on socket close\n // (e.g., when connection is lost or server disconnects)\n if (err instanceof TypeError && !err.message) {\n return new DisconnectedError('WebSocket connection closed unexpectedly');\n }\n return err instanceof Error ? err : new Error('WebSocket error', { cause: error });\n }\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -77,11 +77,15 @@ exports.toResourceId = transaction.toResourceId;
77
77
  exports.DisconnectedError = errors.DisconnectedError;
78
78
  exports.PlError = errors.PlError;
79
79
  exports.PlErrorCodeNotFound = errors.PlErrorCodeNotFound;
80
+ exports.RESTError = errors.RESTError;
80
81
  exports.RecoverablePlError = errors.RecoverablePlError;
81
82
  exports.UnauthenticatedError = errors.UnauthenticatedError;
82
83
  exports.UnrecoverablePlError = errors.UnrecoverablePlError;
84
+ exports.isAbortedError = errors.isAbortedError;
85
+ exports.isCancelError = errors.isCancelError;
83
86
  exports.isConnectionProblem = errors.isConnectionProblem;
84
87
  exports.isNotFoundError = errors.isNotFoundError;
88
+ exports.isTimeoutError = errors.isTimeoutError;
85
89
  exports.isTimeoutOrCancelError = errors.isTimeoutOrCancelError;
86
90
  exports.isUnauthenticated = errors.isUnauthenticated;
87
91
  exports.rethrowMeaningfulError = errors.rethrowMeaningfulError;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ export { AnonymousAuthInformation, DEFAULT_AUTH_MAX_REFRESH, DEFAULT_MAX_CACHE_B
5
5
  export { PlClient } from './core/client.js';
6
6
  export { addRTypeToMetadata, createRTypeRoutingHeader } from './core/driver.js';
7
7
  export { PlTransaction, TxCommitConflict, field, isField, isFieldRef, isResource, isResourceId, isResourceRef, toFieldId, toGlobalFieldId, toGlobalResourceId, toResourceId } from './core/transaction.js';
8
- export { DisconnectedError, PlError, PlErrorCodeNotFound, RecoverablePlError, UnauthenticatedError, UnrecoverablePlError, isConnectionProblem, isNotFoundError, isTimeoutOrCancelError, isUnauthenticated, rethrowMeaningfulError, throwPlNotFoundError } from './core/errors.js';
8
+ export { DisconnectedError, PlError, PlErrorCodeNotFound, RESTError, RecoverablePlError, UnauthenticatedError, UnrecoverablePlError, isAbortedError, isCancelError, isConnectionProblem, isNotFoundError, isTimeoutError, isTimeoutOrCancelError, isUnauthenticated, rethrowMeaningfulError, throwPlNotFoundError } from './core/errors.js';
9
9
  export { defaultPlClient, tryGetFileConfig } from './core/default_client.js';
10
10
  export { UnauthenticatedPlClient } from './core/unauth_client.js';
11
11
  export { expirationFromAuthInformation, inferAuthRefreshTime } from './core/auth.js';
@@ -68,7 +68,7 @@ function errorHandlerMiddleware() {
68
68
  if (typeof respErr.error === 'string') {
69
69
  throw new Error(respErr.error);
70
70
  }
71
- errors.rethrowMeaningfulError(respErr.error);
71
+ errors.rethrowMeaningfulError(new errors.RESTError(respErr.error));
72
72
  },
73
73
  };
74
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n // If body has already been consumed, clone the request\n const request = input.bodyUsed ? input.clone() : input;\n\n return undiciFetch(request.url, {\n body: request.body,\n cache: request.cache,\n credentials: request.credentials,\n dispatcher: opts.dispatcher,\n duplex: request.duplex,\n headers: request.headers,\n integrity: request.integrity,\n keepalive: request.keepalive,\n method: request.method,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: request.signal,\n });\n },\n });\n client.use(errorHandlerMiddleware(), ...opts.middlewares);\n return client;\n}\n\nexport type ErrorResponse = {\n code: Code,\n message: string,\n details: any[],\n}\nexport async function parseResponseError(response: Response): Promise<{\n error?: ErrorResponse | string,\n origBody?: string,\n}>{\n if (response.status < 400) {\n return {};\n }\n\n let error: any = await response.clone().text();\n const origBody = error;\n try {\n error = JSON.parse(error) as ErrorResponse;\n } catch {\n }\n return {\n error: error,\n origBody\n };\n}\n\n/**\n * Parses all API responses and thrown error in case of error response.\n * Allows all callers to not bother about .error response field checking.\n */\nfunction errorHandlerMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n const { body, ...resOptions } = response;\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === 'string') {\n throw new Error(respErr.error);\n }\n\n rethrowMeaningfulError(respErr.error);\n },\n };\n}\n"],"names":["undiciFetch","rethrowMeaningfulError"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AAkBM,SAAU,YAAY,CAAmB,IAAsB,EAAA;AACnE,IAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,SAAS;IAChD,MAAM,MAAM,GAAG,mBAAmB,CAAQ;AACxC,QAAA,OAAO,EAAE,CAAA,EAAG,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA,CAAE;AACvC,QAAA,KAAK,EAAE,CAAC,KAAc,KAAuB;;AAE3C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK;AAEtD,YAAA,OAAOA,YAAW,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;AACvB,aAAA,CAAC;QACJ,CAAC;AACF,KAAA,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;AACzD,IAAA,OAAO,MAAM;AACf;AAOO,eAAe,kBAAkB,CAAC,QAAkB,EAAA;AAIzD,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;AACzB,QAAA,OAAO,EAAE;IACX;IAEA,IAAI,KAAK,GAAQ,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;IAC9C,MAAM,QAAQ,GAAG,KAAK;AACtB,IAAA,IAAI;AACF,QAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAkB;IAC5C;AAAE,IAAA,MAAM;IACR;IACA,OAAO;AACL,QAAA,KAAK,EAAE,KAAK;QACZ;KACD;AACH;AAEA;;;AAGG;AACH,SAAS,sBAAsB,GAAA;IAC7B,OAAO;AACL,QAAA,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACvE,YAAA,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC;AAClD,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAClB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,QAAQ;AACxC,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACvE;AAEA,YAAA,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC;AAEA,YAAAC,6BAAsB,CAAC,OAAO,CAAC,KAAK,CAAC;QACvC,CAAC;KACF;AACH;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { RESTError, rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n // If body has already been consumed, clone the request\n const request = input.bodyUsed ? input.clone() : input;\n\n return undiciFetch(request.url, {\n body: request.body,\n cache: request.cache,\n credentials: request.credentials,\n dispatcher: opts.dispatcher,\n duplex: request.duplex,\n headers: request.headers,\n integrity: request.integrity,\n keepalive: request.keepalive,\n method: request.method,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: request.signal,\n });\n },\n });\n client.use(errorHandlerMiddleware(), ...opts.middlewares);\n return client;\n}\n\nexport type ErrorResponse = {\n code: Code,\n message: string,\n details: any[],\n}\nexport async function parseResponseError(response: Response): Promise<{\n error?: ErrorResponse | string,\n origBody?: string,\n}>{\n if (response.status < 400) {\n return {};\n }\n\n let error: any = await response.clone().text();\n const origBody = error;\n try {\n error = JSON.parse(error) as ErrorResponse;\n } catch {\n }\n return {\n error: error,\n origBody\n };\n}\n\n/**\n * Parses all API responses and thrown error in case of error response.\n * Allows all callers to not bother about .error response field checking.\n */\nfunction errorHandlerMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n const { body, ...resOptions } = response;\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === 'string') {\n throw new Error(respErr.error);\n }\n\n rethrowMeaningfulError(new RESTError(respErr.error));\n },\n };\n}\n"],"names":["undiciFetch","rethrowMeaningfulError","RESTError"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AAkBM,SAAU,YAAY,CAAmB,IAAsB,EAAA;AACnE,IAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,SAAS;IAChD,MAAM,MAAM,GAAG,mBAAmB,CAAQ;AACxC,QAAA,OAAO,EAAE,CAAA,EAAG,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA,CAAE;AACvC,QAAA,KAAK,EAAE,CAAC,KAAc,KAAuB;;AAE3C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK;AAEtD,YAAA,OAAOA,YAAW,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;AACvB,aAAA,CAAC;QACJ,CAAC;AACF,KAAA,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;AACzD,IAAA,OAAO,MAAM;AACf;AAOO,eAAe,kBAAkB,CAAC,QAAkB,EAAA;AAIzD,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;AACzB,QAAA,OAAO,EAAE;IACX;IAEA,IAAI,KAAK,GAAQ,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;IAC9C,MAAM,QAAQ,GAAG,KAAK;AACtB,IAAA,IAAI;AACF,QAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAkB;IAC5C;AAAE,IAAA,MAAM;IACR;IACA,OAAO;AACL,QAAA,KAAK,EAAE,KAAK;QACZ;KACD;AACH;AAEA;;;AAGG;AACH,SAAS,sBAAsB,GAAA;IAC7B,OAAO;AACL,QAAA,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACvE,YAAA,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC;AAClD,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAClB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,QAAQ;AACxC,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACvE;AAEA,YAAA,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC;YAEAC,6BAAsB,CAAC,IAAIC,gBAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC;KACF;AACH;;;;;"}
@@ -1,6 +1,6 @@
1
1
  import createOpenApiClient from 'openapi-fetch';
2
2
  import { fetch } from 'undici';
3
- import { rethrowMeaningfulError } from '../core/errors.js';
3
+ import { rethrowMeaningfulError, RESTError } from '../core/errors.js';
4
4
 
5
5
  //
6
6
  // This file is NOT autogenerated!
@@ -66,7 +66,7 @@ function errorHandlerMiddleware() {
66
66
  if (typeof respErr.error === 'string') {
67
67
  throw new Error(respErr.error);
68
68
  }
69
- rethrowMeaningfulError(respErr.error);
69
+ rethrowMeaningfulError(new RESTError(respErr.error));
70
70
  },
71
71
  };
72
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n // If body has already been consumed, clone the request\n const request = input.bodyUsed ? input.clone() : input;\n\n return undiciFetch(request.url, {\n body: request.body,\n cache: request.cache,\n credentials: request.credentials,\n dispatcher: opts.dispatcher,\n duplex: request.duplex,\n headers: request.headers,\n integrity: request.integrity,\n keepalive: request.keepalive,\n method: request.method,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: request.signal,\n });\n },\n });\n client.use(errorHandlerMiddleware(), ...opts.middlewares);\n return client;\n}\n\nexport type ErrorResponse = {\n code: Code,\n message: string,\n details: any[],\n}\nexport async function parseResponseError(response: Response): Promise<{\n error?: ErrorResponse | string,\n origBody?: string,\n}>{\n if (response.status < 400) {\n return {};\n }\n\n let error: any = await response.clone().text();\n const origBody = error;\n try {\n error = JSON.parse(error) as ErrorResponse;\n } catch {\n }\n return {\n error: error,\n origBody\n };\n}\n\n/**\n * Parses all API responses and thrown error in case of error response.\n * Allows all callers to not bother about .error response field checking.\n */\nfunction errorHandlerMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n const { body, ...resOptions } = response;\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === 'string') {\n throw new Error(respErr.error);\n }\n\n rethrowMeaningfulError(respErr.error);\n },\n };\n}\n"],"names":["undiciFetch"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AAkBM,SAAU,YAAY,CAAmB,IAAsB,EAAA;AACnE,IAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,SAAS;IAChD,MAAM,MAAM,GAAG,mBAAmB,CAAQ;AACxC,QAAA,OAAO,EAAE,CAAA,EAAG,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA,CAAE;AACvC,QAAA,KAAK,EAAE,CAAC,KAAc,KAAuB;;AAE3C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK;AAEtD,YAAA,OAAOA,KAAW,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;AACvB,aAAA,CAAC;QACJ,CAAC;AACF,KAAA,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;AACzD,IAAA,OAAO,MAAM;AACf;AAOO,eAAe,kBAAkB,CAAC,QAAkB,EAAA;AAIzD,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;AACzB,QAAA,OAAO,EAAE;IACX;IAEA,IAAI,KAAK,GAAQ,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;IAC9C,MAAM,QAAQ,GAAG,KAAK;AACtB,IAAA,IAAI;AACF,QAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAkB;IAC5C;AAAE,IAAA,MAAM;IACR;IACA,OAAO;AACL,QAAA,KAAK,EAAE,KAAK;QACZ;KACD;AACH;AAEA;;;AAGG;AACH,SAAS,sBAAsB,GAAA;IAC7B,OAAO;AACL,QAAA,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACvE,YAAA,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC;AAClD,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAClB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,QAAQ;AACxC,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACvE;AAEA,YAAA,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC;AAEA,YAAA,sBAAsB,CAAC,OAAO,CAAC,KAAK,CAAC;QACvC,CAAC;KACF;AACH;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { RESTError, rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n // If body has already been consumed, clone the request\n const request = input.bodyUsed ? input.clone() : input;\n\n return undiciFetch(request.url, {\n body: request.body,\n cache: request.cache,\n credentials: request.credentials,\n dispatcher: opts.dispatcher,\n duplex: request.duplex,\n headers: request.headers,\n integrity: request.integrity,\n keepalive: request.keepalive,\n method: request.method,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: request.signal,\n });\n },\n });\n client.use(errorHandlerMiddleware(), ...opts.middlewares);\n return client;\n}\n\nexport type ErrorResponse = {\n code: Code,\n message: string,\n details: any[],\n}\nexport async function parseResponseError(response: Response): Promise<{\n error?: ErrorResponse | string,\n origBody?: string,\n}>{\n if (response.status < 400) {\n return {};\n }\n\n let error: any = await response.clone().text();\n const origBody = error;\n try {\n error = JSON.parse(error) as ErrorResponse;\n } catch {\n }\n return {\n error: error,\n origBody\n };\n}\n\n/**\n * Parses all API responses and thrown error in case of error response.\n * Allows all callers to not bother about .error response field checking.\n */\nfunction errorHandlerMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n const { body, ...resOptions } = response;\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === 'string') {\n throw new Error(respErr.error);\n }\n\n rethrowMeaningfulError(new RESTError(respErr.error));\n },\n };\n}\n"],"names":["undiciFetch"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AAkBM,SAAU,YAAY,CAAmB,IAAsB,EAAA;AACnE,IAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,SAAS;IAChD,MAAM,MAAM,GAAG,mBAAmB,CAAQ;AACxC,QAAA,OAAO,EAAE,CAAA,EAAG,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA,CAAE;AACvC,QAAA,KAAK,EAAE,CAAC,KAAc,KAAuB;;AAE3C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK;AAEtD,YAAA,OAAOA,KAAW,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;AACvB,aAAA,CAAC;QACJ,CAAC;AACF,KAAA,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;AACzD,IAAA,OAAO,MAAM;AACf;AAOO,eAAe,kBAAkB,CAAC,QAAkB,EAAA;AAIzD,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;AACzB,QAAA,OAAO,EAAE;IACX;IAEA,IAAI,KAAK,GAAQ,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;IAC9C,MAAM,QAAQ,GAAG,KAAK;AACtB,IAAA,IAAI;AACF,QAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAkB;IAC5C;AAAE,IAAA,MAAM;IACR;IACA,OAAO;AACL,QAAA,KAAK,EAAE,KAAK;QACZ;KACD;AACH;AAEA;;;AAGG;AACH,SAAS,sBAAsB,GAAA;IAC7B,OAAO;AACL,QAAA,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACvE,YAAA,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC;AAClD,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAClB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,QAAQ;AACxC,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACvE;AAEA,YAAA,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC;YAEA,sBAAsB,CAAC,IAAI,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC;KACF;AACH;;;;"}
@@ -55,6 +55,14 @@ function getTestConfig() {
55
55
  throw new Error(`can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`);
56
56
  return conf;
57
57
  }
58
+ /** Default request timeout for tests (ms) */
59
+ const TEST_REQUEST_TIMEOUT = 500;
60
+ /** Returns PlClientConfig with reduced timeout for tests */
61
+ function plAddressToTestConfig(address) {
62
+ const plConf = config.plAddressToConfig(address);
63
+ plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;
64
+ return plConf;
65
+ }
58
66
  function saveAuthInfoCallback(tConf) {
59
67
  return (authInformation) => {
60
68
  const dst = getFullAuthDataFilePath();
@@ -89,8 +97,8 @@ async function getTestClientConf() {
89
97
  fs__namespace.rmSync(getFullAuthDataFilePath());
90
98
  }
91
99
  }
92
- const plConf = config.plAddressToConfig(tConf.address);
93
- const uClient = new unauth_client.UnauthenticatedPlClient(plConf);
100
+ const plConf = plAddressToTestConfig(tConf.address);
101
+ const uClient = await unauth_client.UnauthenticatedPlClient.build(plConf);
94
102
  const requireAuth = await uClient.requireAuth();
95
103
  if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))
96
104
  throw new Error(`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`);
@@ -117,7 +125,7 @@ async function getTestClientConf() {
117
125
  }
118
126
  async function getTestLLClient(confOverrides = {}) {
119
127
  const { conf, auth } = await getTestClientConf();
120
- return new ll_client.LLPlClient({ ...conf, ...confOverrides }, { auth });
128
+ return await ll_client.LLPlClient.build({ ...conf, ...confOverrides }, { auth });
121
129
  }
122
130
  async function getTestClient(alternativeRoot, confOverrides = {}) {
123
131
  const { conf, auth } = await getTestClientConf();
@@ -186,9 +194,11 @@ async function withTempRoot(body, options = {}) {
186
194
  }
187
195
  }
188
196
 
197
+ exports.TEST_REQUEST_TIMEOUT = TEST_REQUEST_TIMEOUT;
189
198
  exports.getTestClient = getTestClient;
190
199
  exports.getTestClientConf = getTestClientConf;
191
200
  exports.getTestConfig = getTestConfig;
192
201
  exports.getTestLLClient = getTestLLClient;
202
+ exports.plAddressToTestConfig = plAddressToTestConfig;
193
203
  exports.withTempRoot = withTempRoot;
194
204
  //# sourceMappingURL=test_config.cjs.map
@@ -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 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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"}
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\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 (_e) {\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 /** 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","plAddressToConfig","randomUUID","inferAuthRefreshTime","UnauthenticatedPlClient","LLPlClient","PlClient","NullResourceId","startTcpProxy","resourceIdToString"],"mappings":"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"}
@@ -11,6 +11,10 @@ export interface TestConfig {
11
11
  test_password?: string;
12
12
  }
13
13
  export declare function getTestConfig(): TestConfig;
14
+ /** Default request timeout for tests (ms) */
15
+ export declare const TEST_REQUEST_TIMEOUT = 500;
16
+ /** Returns PlClientConfig with reduced timeout for tests */
17
+ export declare function plAddressToTestConfig(address: string): PlClientConfig;
14
18
  export declare function getTestClientConf(): Promise<{
15
19
  conf: PlClientConfig;
16
20
  auth: AuthOps;
@@ -1 +1 @@
1
- {"version":3,"file":"test_config.d.ts","sourceRoot":"","sources":["../../src/test/test_config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAmB,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG/E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EACL,YAAY,GACb,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAYD,wBAAgB,aAAa,IAAI,UAAU,CAmB1C;AAiCD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CA0D1F;AAED,wBAAsB,eAAe,CAAC,aAAa,GAAE,OAAO,CAAC,cAAc,CAAM,uBAGhF;AAED,wBAAsB,aAAa,CACjC,eAAe,CAAC,EAAE,MAAM,EACxB,aAAa,GAAE,OAAO,CAAC,cAAc,CAAM,qBAM5C;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC;4EACwE;IACxE,WAAW,EAAE,IAAI,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG;IACF,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB,CAAC;AAEF,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAErB,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EACpF,OAAO,EAAE;IACP,WAAW,EAAE,IAAI,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,OAAO,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"test_config.d.ts","sourceRoot":"","sources":["../../src/test/test_config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAmB,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG/E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EACL,YAAY,GACb,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAYD,wBAAgB,aAAa,IAAI,UAAU,CAmB1C;AAED,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,4DAA4D;AAC5D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAIrE;AAiCD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAyD1F;AAED,wBAAsB,eAAe,CAAC,aAAa,GAAE,OAAO,CAAC,cAAc,CAAM,uBAGhF;AAED,wBAAsB,aAAa,CACjC,eAAe,CAAC,EAAE,MAAM,EACxB,aAAa,GAAE,OAAO,CAAC,cAAc,CAAM,qBAM5C;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC;4EACwE;IACxE,WAAW,EAAE,IAAI,CAAC;IAClB,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG;IACF,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB,CAAC;AAEF,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAErB,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EACpF,OAAO,EAAE;IACP,WAAW,EAAE,IAAI,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,OAAO,CAAC,CAAC,CAAC,CAAC"}
@@ -33,6 +33,14 @@ function getTestConfig() {
33
33
  throw new Error(`can't resolve platform address (checked ${CONFIG_FILE} file and PL_ADDRESS environment var)`);
34
34
  return conf;
35
35
  }
36
+ /** Default request timeout for tests (ms) */
37
+ const TEST_REQUEST_TIMEOUT = 500;
38
+ /** Returns PlClientConfig with reduced timeout for tests */
39
+ function plAddressToTestConfig(address) {
40
+ const plConf = plAddressToConfig(address);
41
+ plConf.defaultRequestTimeout = TEST_REQUEST_TIMEOUT;
42
+ return plConf;
43
+ }
36
44
  function saveAuthInfoCallback(tConf) {
37
45
  return (authInformation) => {
38
46
  const dst = getFullAuthDataFilePath();
@@ -67,8 +75,8 @@ async function getTestClientConf() {
67
75
  fs.rmSync(getFullAuthDataFilePath());
68
76
  }
69
77
  }
70
- const plConf = plAddressToConfig(tConf.address);
71
- const uClient = new UnauthenticatedPlClient(plConf);
78
+ const plConf = plAddressToTestConfig(tConf.address);
79
+ const uClient = await UnauthenticatedPlClient.build(plConf);
72
80
  const requireAuth = await uClient.requireAuth();
73
81
  if (!requireAuth && (tConf.test_user !== undefined || tConf.test_password !== undefined))
74
82
  throw new Error(`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`);
@@ -95,7 +103,7 @@ async function getTestClientConf() {
95
103
  }
96
104
  async function getTestLLClient(confOverrides = {}) {
97
105
  const { conf, auth } = await getTestClientConf();
98
- return new LLPlClient({ ...conf, ...confOverrides }, { auth });
106
+ return await LLPlClient.build({ ...conf, ...confOverrides }, { auth });
99
107
  }
100
108
  async function getTestClient(alternativeRoot, confOverrides = {}) {
101
109
  const { conf, auth } = await getTestClientConf();
@@ -164,5 +172,5 @@ async function withTempRoot(body, options = {}) {
164
172
  }
165
173
  }
166
174
 
167
- export { getTestClient, getTestClientConf, getTestConfig, getTestLLClient, withTempRoot };
175
+ export { TEST_REQUEST_TIMEOUT, getTestClient, getTestClientConf, getTestConfig, getTestLLClient, plAddressToTestConfig, withTempRoot };
168
176
  //# sourceMappingURL=test_config.js.map
@@ -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 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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"}
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\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 (_e) {\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 /** 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.13",
3
+ "version": "2.16.15",
4
4
  "engines": {
5
5
  "node": ">=22.19.0"
6
6
  },
@@ -35,21 +35,23 @@
35
35
  "undici": "~7.16.0",
36
36
  "utility-types": "^3.11.0",
37
37
  "yaml": "^2.8.0",
38
- "@milaboratories/pl-http": "1.2.0",
38
+ "@milaboratories/pl-model-common": "1.21.10",
39
39
  "@milaboratories/ts-helpers": "1.5.4",
40
- "@milaboratories/pl-model-common": "1.21.10"
40
+ "@milaboratories/pl-http": "1.2.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@protobuf-ts/plugin": "2.11.1",
44
44
  "openapi-typescript": "^7.10.0",
45
45
  "@types/http-proxy": "^1.17.16",
46
46
  "@types/node": "~24.5.2",
47
- "@vitest/coverage-v8": "^4.0.7",
48
- "vitest": "^4.0.7",
47
+ "@vitest/coverage-istanbul": "^4.0.16",
48
+ "vitest": "^4.0.16",
49
+ "eslint": "^9.25.1",
49
50
  "typescript": "~5.6.3",
50
- "@milaboratories/ts-builder": "1.0.6",
51
- "@milaboratories/build-configs": "1.0.9",
52
- "@milaboratories/ts-configs": "1.0.6"
51
+ "@milaboratories/ts-builder": "1.2.1",
52
+ "@milaboratories/build-configs": "1.2.1",
53
+ "@milaboratories/eslint-config": "1.0.5",
54
+ "@milaboratories/ts-configs": "1.2.0"
53
55
  },
54
56
  "scripts": {
55
57
  "type-check": "ts-builder types --target node",