@compforge/harness-toolbox 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +10 -0
  2. package/dist/chunk-835fssgz.js +37 -0
  3. package/dist/{chunk-0s27bgga.js → chunk-ae84rzq5.js} +1 -1
  4. package/dist/{chunk-age65q4n.js → chunk-db4an9td.js} +1 -1
  5. package/dist/{chunk-ftkrjqkc.js → chunk-dpcge1xt.js} +1 -1
  6. package/dist/{chunk-grpygk6f.js → chunk-dx6yvj81.js} +2 -2
  7. package/dist/{chunk-sqbew04a.js → chunk-f3vq900j.js} +2 -2
  8. package/dist/{chunk-yp6tcw22.js → chunk-f98qrpx1.js} +38 -78
  9. package/dist/{chunk-c2n8zvyn.js → chunk-g2bac2jd.js} +6 -6
  10. package/dist/chunk-gm4swndx.js +106 -0
  11. package/dist/{chunk-shsabpc1.js → chunk-r3rnsvwv.js} +1 -1
  12. package/dist/{chunk-20kv4wc3.js → chunk-rkt41za2.js} +7 -1
  13. package/dist/{chunk-8mb6natq.js → chunk-sbt20n84.js} +53 -35
  14. package/dist/{chunk-h73h2b7y.js → chunk-t4hrnaj0.js} +44 -39
  15. package/dist/{chunk-kkts16w9.js → chunk-ww11q58v.js} +1 -1
  16. package/dist/{chunk-4ms037k2.js → chunk-x0yhfg29.js} +2 -2
  17. package/dist/{chunk-5zgz1z41.js → chunk-z15gwjvg.js} +1 -1
  18. package/dist/connection/acquire.d.ts +34 -0
  19. package/dist/connection/acquire.js +9 -0
  20. package/dist/datasource.js +1 -1
  21. package/dist/errors.d.ts +4 -0
  22. package/dist/errors.js +3 -1
  23. package/dist/file/client.js +2 -2
  24. package/dist/file/index.js +4 -4
  25. package/dist/file/local.js +3 -3
  26. package/dist/file/pod.js +3 -3
  27. package/dist/kubernetes/client.js +6 -6
  28. package/dist/kubernetes/compute-quantity.js +2 -2
  29. package/dist/kubernetes/compute.js +2 -2
  30. package/dist/kubernetes/environment.js +6 -6
  31. package/dist/kubernetes/exec-transport.js +2 -2
  32. package/dist/kubernetes/pod-log-datasource.js +1 -1
  33. package/dist/kubernetes/resource-transport.js +3 -3
  34. package/dist/kubernetes/resources.js +2 -2
  35. package/dist/kubernetes/workload.js +2 -2
  36. package/dist/mysql/client.d.ts +5 -2
  37. package/dist/mysql/client.js +5 -4
  38. package/dist/mysql/connection.d.ts +3 -0
  39. package/dist/mysql/connection.js +4 -2
  40. package/dist/mysql/index.js +6 -5
  41. package/dist/mysql/readonly.js +2 -2
  42. package/dist/opensearch/client.d.ts +3 -1
  43. package/dist/opensearch/client.js +3 -2
  44. package/dist/opensearch/index.js +3 -2
  45. package/dist/redis/client.js +3 -2
  46. package/dist/redis/index.js +7 -6
  47. package/dist/s3/datasource.js +1 -1
  48. package/dist/s3/index.js +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunk-136egyff.js +0 -11
package/README.md CHANGED
@@ -173,6 +173,9 @@ the root owns cancellation and must join child work before disposing clients.
173
173
 
174
174
  Catch ToolboxError from the root entry or `@compforge/harness-toolbox/errors`; use `kind` and
175
175
  `code` for decisions. Native `cause` may contain sensitive details and is debug-only.
176
+ Connection acquisition deadlines and Redis handshake/command deadlines raise `TimeoutError`
177
+ from `@compforge/harness-toolbox/errors`. It extends `ToolboxError`, retaining
178
+ `kind: "timeout"` and `code: "ETIMEDOUT"`; OpenSearch preserves it unchanged.
176
179
 
177
180
  For coordinated common/toolbox development, install and build common before installing toolbox:
178
181
 
@@ -384,3 +387,10 @@ ResourceAccess, use explicit deadlines/item/page budgets, and retain read covera
384
387
  Use `client.clusterResources()` for Nodes and all-namespace Pods; normal client methods retain their
385
388
  configured namespace. A forbidden cluster read records `permission_denied`; it does not block a
386
389
  separate namespace-scoped Workload read. See [compute collection](../../../docs/toolbox.md#kubernetes-compute-evidence).
390
+
391
+ ### Connection acquisition deadlines
392
+
393
+ MySQL, OpenSearch and Redis use the same acquisition lifecycle. `connectTimeoutMs` includes route mapping
394
+ and handshake/probe; optional `acquireTimeoutMs` bounds all routes together (default: per-route budget × route count).
395
+ Cancellation preserves the caller reason and prevents late mappings from starting a handshake. Query and command
396
+ timeouts remain separate, and failed operations are never replayed on another transport.
@@ -0,0 +1,37 @@
1
+ // src/mysql/connection.ts
2
+ import { createConnection } from "mysql2";
3
+ async function connectMysql(options, signal) {
4
+ signal?.throwIfAborted();
5
+ const native = createConnection(options);
6
+ const connection = native.promise();
7
+ let abort;
8
+ try {
9
+ await new Promise((resolve, reject) => {
10
+ abort = () => {
11
+ destroyMysqlConnection(connection);
12
+ reject(signal.reason);
13
+ };
14
+ signal?.addEventListener("abort", abort, { once: true });
15
+ native.connect((error) => error ? reject(error) : resolve());
16
+ if (signal?.aborted)
17
+ abort();
18
+ });
19
+ signal?.throwIfAborted();
20
+ return connection;
21
+ } catch (error) {
22
+ destroyMysqlConnection(connection);
23
+ throw error;
24
+ } finally {
25
+ signal?.removeEventListener("abort", abort);
26
+ }
27
+ }
28
+ function destroyMysqlConnection(connection) {
29
+ const native = connection.connection;
30
+ try {
31
+ connection.destroy();
32
+ } finally {
33
+ native.stream.destroy();
34
+ }
35
+ }
36
+
37
+ export { connectMysql, destroyMysqlConnection };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  KubernetesError
3
- } from "./chunk-20kv4wc3.js";
3
+ } from "./chunk-rkt41za2.js";
4
4
 
5
5
  // src/kubernetes/workload.ts
6
6
  function failure(message, kind) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  destroyMysqlConnection
3
- } from "./chunk-136egyff.js";
3
+ } from "./chunk-835fssgz.js";
4
4
 
5
5
  // src/mysql/readonly.ts
6
6
  function validateQueryLimits(limits) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  KubernetesError
3
- } from "./chunk-20kv4wc3.js";
3
+ } from "./chunk-rkt41za2.js";
4
4
 
5
5
  // src/kubernetes/resources.ts
6
6
  function resourcePath(query, name) {
@@ -2,10 +2,10 @@ import {
2
2
  BoundedFileClient,
3
3
  fileClientOptions,
4
4
  filePaths
5
- } from "./chunk-kkts16w9.js";
5
+ } from "./chunk-ww11q58v.js";
6
6
  import {
7
7
  FileError
8
- } from "./chunk-20kv4wc3.js";
8
+ } from "./chunk-rkt41za2.js";
9
9
 
10
10
  // src/file/pod.ts
11
11
  import { posix } from "node:path";
@@ -2,10 +2,10 @@ import {
2
2
  BoundedFileClient,
3
3
  fileClientOptions,
4
4
  filePaths
5
- } from "./chunk-kkts16w9.js";
5
+ } from "./chunk-ww11q58v.js";
6
6
  import {
7
7
  FileError
8
- } from "./chunk-20kv4wc3.js";
8
+ } from "./chunk-rkt41za2.js";
9
9
 
10
10
  // src/file/local.ts
11
11
  import { constants } from "node:fs";
@@ -1,9 +1,13 @@
1
+ import {
2
+ acquireConnection,
3
+ awaitWithSignal
4
+ } from "./chunk-gm4swndx.js";
1
5
  import {
2
6
  discoverRedisTopology
3
7
  } from "./chunk-5tbzf9wd.js";
4
8
  import {
5
- ToolboxError
6
- } from "./chunk-20kv4wc3.js";
9
+ TimeoutError
10
+ } from "./chunk-rkt41za2.js";
7
11
  import {
8
12
  isConnectionNetworkError
9
13
  } from "./chunk-h8fwx0k7.js";
@@ -41,26 +45,6 @@ function parseRedisInfo(raw) {
41
45
  }
42
46
  return result;
43
47
  }
44
- async function abortable(pending, signal, cancel) {
45
- if (signal.aborted) {
46
- cancel?.();
47
- pending.catch(() => {});
48
- signal.throwIfAborted();
49
- }
50
- let abort;
51
- const cancelled = new Promise((_, reject) => {
52
- abort = () => {
53
- cancel?.();
54
- reject(signal.reason);
55
- };
56
- signal.addEventListener("abort", abort, { once: true });
57
- });
58
- try {
59
- return await Promise.race([pending, cancelled]);
60
- } finally {
61
- signal.removeEventListener("abort", abort);
62
- }
63
- }
64
48
 
65
49
  class RedisConnection {
66
50
  endpoint;
@@ -101,7 +85,7 @@ class RedisConnection {
101
85
  lastError = err instanceof Error ? err : new Error(String(err));
102
86
  });
103
87
  const timeout = new AbortController;
104
- const timer = setTimeout(() => timeout.abort(new ToolboxError("Redis handshake timed out", { kind: "timeout", code: "ETIMEDOUT" })), config.connectTimeoutMs ?? config.timeoutMs);
88
+ const timer = setTimeout(() => timeout.abort(new TimeoutError("Redis handshake timed out")), config.connectTimeoutMs ?? config.timeoutMs);
105
89
  const signal = AbortSignal.any([
106
90
  timeout.signal,
107
91
  ...config.signal ? [config.signal] : [],
@@ -112,7 +96,7 @@ class RedisConnection {
112
96
  client.destroy();
113
97
  };
114
98
  try {
115
- await abortable(client.connect(), signal, close);
99
+ await awaitWithSignal(client.connect(), signal, close);
116
100
  signal.throwIfAborted();
117
101
  return new RedisConnection(endpoint, client, config.timeoutMs, () => lastError, config.signal);
118
102
  } catch (error) {
@@ -132,10 +116,10 @@ class RedisConnection {
132
116
  throw new Error(`Redis connection is closed${reason ? `: ${reason}` : ""}`);
133
117
  }
134
118
  const timeout = new AbortController;
135
- const timer = setTimeout(() => timeout.abort(new ToolboxError(`Redis ${operationName} timed out after ${this.timeoutMs}ms (${this.endpoint.host}:${this.endpoint.port})`, { kind: "timeout", code: "ETIMEDOUT" })), this.timeoutMs);
119
+ const timer = setTimeout(() => timeout.abort(new TimeoutError(`Redis ${operationName} timed out after ${this.timeoutMs}ms (${this.endpoint.host}:${this.endpoint.port})`)), this.timeoutMs);
136
120
  const signal = this.signal ? AbortSignal.any([this.signal, timeout.signal]) : timeout.signal;
137
121
  try {
138
- return await abortable(Promise.resolve().then(() => {
122
+ return await awaitWithSignal(Promise.resolve().then(() => {
139
123
  signal.throwIfAborted();
140
124
  return operation();
141
125
  }), signal, () => this.close());
@@ -229,58 +213,34 @@ class RedisAccess {
229
213
  });
230
214
  return pending;
231
215
  }
232
- async#open(endpoint, database, credentials) {
233
- const timeout = new AbortController;
234
- const attemptBudget = this.baseConfig.connectTimeoutMs ?? this.baseConfig.timeoutMs;
235
- const budget = this.baseConfig.acquireTimeoutMs ?? attemptBudget * this.#transports.length;
236
- const deadline = performance.now() + budget;
237
- const timer = setTimeout(() => timeout.abort(new ToolboxError(`Redis connection timed out after ${budget}ms (${endpoint.host}:${endpoint.port})`, { kind: "timeout", code: "ETIMEDOUT" })), budget);
238
- const signal = AbortSignal.any([this.#signal, timeout.signal]);
239
- let reason;
240
- try {
241
- for (let index = 0;index < this.#transports.length; index++) {
242
- signal.throwIfAborted();
243
- const transport = this.#transports[index];
244
- const attempt = new AbortController;
245
- const remaining = Math.min(attemptBudget, Math.max(1, Math.ceil(deadline - performance.now())));
246
- const attemptTimer = setTimeout(() => attempt.abort(new ToolboxError(`Redis connection attempt timed out (${transport.name})`, { kind: "timeout", code: "ETIMEDOUT" })), remaining);
247
- const attemptSignal = AbortSignal.any([signal, attempt.signal]);
248
- try {
249
- const mapped = await abortable(transport.connect(endpoint), attemptSignal);
250
- attemptSignal.throwIfAborted();
251
- const pending = this.connectionFactory(endpoint, mapped, {
252
- ...this.baseConfig,
253
- ...credentials,
254
- database,
255
- connectTimeoutMs: remaining,
256
- signal: this.#signal,
257
- connectSignal: attemptSignal
258
- });
259
- const prepared = pending.then((connection2) => {
260
- if (attemptSignal.aborted) {
261
- connection2.close();
262
- attemptSignal.throwIfAborted();
263
- }
264
- return connection2;
265
- });
266
- const connection = await abortable(prepared, attemptSignal);
267
- this.lifecycle.onRoute?.({ transport: mapped.host === endpoint.host && mapped.port === endpoint.port ? "direct" : transport.name, reason });
268
- return connection;
269
- } catch (failure) {
270
- signal.throwIfAborted();
271
- const error = attemptSignal.aborted ? attemptSignal.reason : failure;
272
- const network = isConnectionNetworkError(error) || error instanceof SocketClosedUnexpectedlyError || error instanceof ConnectionTimeoutError;
273
- if (!network || index === this.#transports.length - 1)
274
- throw error;
275
- reason = error instanceof SocketClosedUnexpectedlyError ? "connection_lost" : error instanceof ConnectionTimeoutError ? "ETIMEDOUT" : String(error.code);
276
- } finally {
277
- clearTimeout(attemptTimer);
278
- }
216
+ #open(endpoint, database, credentials) {
217
+ return acquireConnection(this.#transports, {
218
+ name: "Redis connection",
219
+ signal: this.#signal,
220
+ connectTimeoutMs: this.baseConfig.connectTimeoutMs ?? this.baseConfig.timeoutMs,
221
+ acquireTimeoutMs: this.baseConfig.acquireTimeoutMs,
222
+ onRoute: this.lifecycle.onRoute,
223
+ open: async (transport, attempt) => {
224
+ const mapped = await transport.connect(endpoint);
225
+ const connection = await this.connectionFactory(endpoint, mapped, {
226
+ ...this.baseConfig,
227
+ ...credentials,
228
+ database,
229
+ connectTimeoutMs: attempt.remainingMs(),
230
+ signal: this.#signal,
231
+ connectSignal: attempt.signal
232
+ });
233
+ return { connection, transport: mapped.host === endpoint.host && mapped.port === endpoint.port ? "direct" : transport.name };
234
+ },
235
+ close: (connection) => connection.close(),
236
+ retryReason: (error) => {
237
+ if (error instanceof SocketClosedUnexpectedlyError)
238
+ return "connection_lost";
239
+ if (error instanceof ConnectionTimeoutError)
240
+ return "ETIMEDOUT";
241
+ return isConnectionNetworkError(error) ? String(error.code) : undefined;
279
242
  }
280
- throw new Error("Redis requires a TCP transport");
281
- } finally {
282
- clearTimeout(timer);
283
- }
243
+ });
284
244
  }
285
245
  close() {
286
246
  return this.#closing ??= (async () => {
@@ -319,7 +279,7 @@ class RedisClient {
319
279
  initialize() {
320
280
  this.signal.throwIfAborted();
321
281
  return this.#initialization ??= (async () => {
322
- this.#target = await this.source.resolve();
282
+ this.#target = await awaitWithSignal(this.source.resolve(), this.signal);
323
283
  this.signal.throwIfAborted();
324
284
  if (!this.source.transports.length || this.source.transports.some((transport) => transport.kind !== "tcp")) {
325
285
  throw new Error("Redis requires TCP transports");
@@ -1,18 +1,18 @@
1
+ import {
2
+ resolveWorkload
3
+ } from "./chunk-ae84rzq5.js";
1
4
  import {
2
5
  ServicePortForwarder
3
6
  } from "./chunk-2171tajs.js";
4
7
  import {
5
8
  KubernetesResourceTransport
6
- } from "./chunk-4ms037k2.js";
9
+ } from "./chunk-x0yhfg29.js";
7
10
  import {
8
11
  KubernetesExecTransport
9
- } from "./chunk-shsabpc1.js";
10
- import {
11
- resolveWorkload
12
- } from "./chunk-0s27bgga.js";
12
+ } from "./chunk-r3rnsvwv.js";
13
13
  import {
14
14
  KubernetesError
15
- } from "./chunk-20kv4wc3.js";
15
+ } from "./chunk-rkt41za2.js";
16
16
  import {
17
17
  KubectlExecutor
18
18
  } from "./chunk-hmah9t8e.js";
@@ -0,0 +1,106 @@
1
+ import {
2
+ TimeoutError,
3
+ ToolboxError
4
+ } from "./chunk-rkt41za2.js";
5
+
6
+ // src/connection/acquire.ts
7
+ async function awaitWithSignal(pending, signal, cancel) {
8
+ if (signal.aborted) {
9
+ cancel?.();
10
+ pending.catch(() => {});
11
+ signal.throwIfAborted();
12
+ }
13
+ let abort;
14
+ const cancelled = new Promise((_, reject) => {
15
+ abort = () => {
16
+ cancel?.();
17
+ reject(signal.reason);
18
+ };
19
+ signal.addEventListener("abort", abort, { once: true });
20
+ });
21
+ try {
22
+ return await Promise.race([pending, cancelled]);
23
+ } finally {
24
+ signal.removeEventListener("abort", abort);
25
+ }
26
+ }
27
+ async function acquireConnection(routes, options) {
28
+ const budget = options.acquireTimeoutMs ?? options.connectTimeoutMs * routes.length;
29
+ if (!routes.length)
30
+ throw new ToolboxError(`${options.name} requires a transport`, { kind: "invalid_argument" });
31
+ if ([options.connectTimeoutMs, budget].some((value) => !Number.isFinite(value) || value <= 0 || value > 2147483647)) {
32
+ throw new ToolboxError(`${options.name} timeouts must be positive milliseconds within timer range`, { kind: "invalid_argument" });
33
+ }
34
+ options.signal?.throwIfAborted();
35
+ const timeout = new AbortController;
36
+ const timedOut = (message) => new TimeoutError(message);
37
+ const deadline = performance.now() + budget;
38
+ const timer = setTimeout(() => timeout.abort(timedOut(`${options.name} timed out after ${budget}ms`)), budget);
39
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout.signal]) : timeout.signal;
40
+ let reason;
41
+ try {
42
+ for (const [index, route] of routes.entries()) {
43
+ if (performance.now() >= deadline)
44
+ timeout.abort(timedOut(`${options.name} timed out after ${budget}ms`));
45
+ signal.throwIfAborted();
46
+ const attempt = new AbortController;
47
+ const end = Math.min(deadline, performance.now() + options.connectTimeoutMs);
48
+ const expire = () => attempt.abort(timedOut(`${options.name} attempt timed out (${route.name})`));
49
+ const attemptTimer = setTimeout(expire, Math.max(1, Math.ceil(end - performance.now())));
50
+ const attemptSignal = AbortSignal.any([signal, attempt.signal]);
51
+ const context = {
52
+ signal: attemptSignal,
53
+ remainingMs: () => {
54
+ if (performance.now() >= end)
55
+ expire();
56
+ attemptSignal.throwIfAborted();
57
+ return Math.max(1, Math.ceil(end - performance.now()));
58
+ }
59
+ };
60
+ let candidate;
61
+ let discarded = false;
62
+ const discard = async () => {
63
+ if (!candidate || discarded)
64
+ return;
65
+ discarded = true;
66
+ await options.close(candidate.connection);
67
+ };
68
+ let result;
69
+ try {
70
+ const prepared = options.open(route, context).then(async (value) => {
71
+ candidate = value;
72
+ if (attemptSignal.aborted) {
73
+ await discard();
74
+ attemptSignal.throwIfAborted();
75
+ }
76
+ return value;
77
+ });
78
+ result = await awaitWithSignal(prepared, attemptSignal);
79
+ } catch (failure) {
80
+ await discard();
81
+ signal.throwIfAborted();
82
+ const error = attemptSignal.aborted ? attemptSignal.reason : failure;
83
+ reason = options.retryReason(error);
84
+ if (reason === undefined || index === routes.length - 1)
85
+ throw error;
86
+ continue;
87
+ } finally {
88
+ clearTimeout(attemptTimer);
89
+ }
90
+ try {
91
+ attemptSignal.throwIfAborted();
92
+ options.onRoute?.({ transport: result.transport, reason });
93
+ attemptSignal.throwIfAborted();
94
+ return result.connection;
95
+ } catch (error) {
96
+ await discard();
97
+ throw error;
98
+ }
99
+ }
100
+ throw new Error("Connection routes exhausted");
101
+ } finally {
102
+ clearTimeout(timer);
103
+ }
104
+ }
105
+
106
+ export { awaitWithSignal, acquireConnection };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  KubernetesError,
3
3
  kubernetesHttpError
4
- } from "./chunk-20kv4wc3.js";
4
+ } from "./chunk-rkt41za2.js";
5
5
  import {
6
6
  ConcurrencyPool
7
7
  } from "./chunk-v3hxvnrc.js";
@@ -15,6 +15,12 @@ class KubernetesError extends ToolboxError {
15
15
 
16
16
  class FileError extends ToolboxError {
17
17
  }
18
+
19
+ class TimeoutError extends ToolboxError {
20
+ constructor(message, options) {
21
+ super(message, { kind: "timeout", code: "ETIMEDOUT", cause: options?.cause });
22
+ }
23
+ }
18
24
  function kubernetesHttpError(status) {
19
25
  const kind = {
20
26
  401: "authentication_failed",
@@ -41,4 +47,4 @@ function kubernetesCommandError(result) {
41
47
  return status ? kubernetesHttpError(status) : new KubernetesError("Kubernetes resource request failed", { kind: "operation_failed" });
42
48
  }
43
49
 
44
- export { ToolboxError, KubernetesError, FileError, kubernetesHttpError, kubernetesCommandError };
50
+ export { ToolboxError, KubernetesError, FileError, TimeoutError, kubernetesHttpError, kubernetesCommandError };
@@ -1,6 +1,10 @@
1
+ import {
2
+ acquireConnection,
3
+ awaitWithSignal
4
+ } from "./chunk-gm4swndx.js";
1
5
  import {
2
6
  ToolboxError
3
- } from "./chunk-20kv4wc3.js";
7
+ } from "./chunk-rkt41za2.js";
4
8
  import {
5
9
  isConnectionNetworkError
6
10
  } from "./chunk-h8fwx0k7.js";
@@ -68,6 +72,15 @@ class OpenSearchEngine {
68
72
  return this.client.close();
69
73
  }
70
74
  }
75
+ function openSearchRetryReason(error) {
76
+ if (isConnectionNetworkError(error))
77
+ return String(error.code);
78
+ if (error instanceof errors.TimeoutError)
79
+ return "ETIMEDOUT";
80
+ if (error instanceof errors.ConnectionError)
81
+ return error.name;
82
+ return;
83
+ }
71
84
 
72
85
  class OpenSearchClient {
73
86
  source;
@@ -86,44 +99,49 @@ class OpenSearchClient {
86
99
  initialize() {
87
100
  this.signal.throwIfAborted();
88
101
  return this.#initialization ??= (async () => {
89
- const target = await this.source.resolve();
102
+ const target = await awaitWithSignal(this.source.resolve(), this.signal);
90
103
  this.signal.throwIfAborted();
91
104
  const original = new URL(target.node);
92
- let reason;
93
- for (const [index, transport] of this.source.transports.entries()) {
105
+ try {
106
+ this.#engine = await acquireConnection(this.source.transports, {
107
+ name: "OpenSearch connection",
108
+ signal: this.signal,
109
+ connectTimeoutMs: target.connectTimeoutMs ?? target.requestTimeoutMs ?? 1e4,
110
+ acquireTimeoutMs: target.acquireTimeoutMs,
111
+ onRoute: this.lifecycle.onRoute,
112
+ open: async (transport, attempt) => {
113
+ if (transport.kind !== "tcp")
114
+ throw new Error("OpenSearch requires a TCP transport");
115
+ const endpoint = await transport.connect({
116
+ host: original.hostname,
117
+ port: Number(original.port || (original.protocol === "https:" ? 443 : 80)),
118
+ servername: target.servername ?? original.hostname
119
+ });
120
+ attempt.signal.throwIfAborted();
121
+ const url = new URL(original);
122
+ url.hostname = endpoint.host;
123
+ url.port = String(endpoint.port);
124
+ const engine = new OpenSearchEngine({ ...target, node: url.toString(), servername: endpoint.servername ?? target.servername ?? original.hostname });
125
+ try {
126
+ await engine.ping(attempt.signal, attempt.remainingMs());
127
+ return { connection: engine, transport: transport.name };
128
+ } catch (error) {
129
+ await engine.close();
130
+ throw error;
131
+ }
132
+ },
133
+ close: (engine) => engine.close(),
134
+ retryReason: openSearchRetryReason
135
+ });
136
+ } catch (error) {
94
137
  this.signal.throwIfAborted();
95
- if (transport.kind !== "tcp")
96
- throw new Error("OpenSearch requires a TCP transport");
97
- let engine;
98
- try {
99
- const endpoint = await transport.connect({
100
- host: original.hostname,
101
- port: Number(original.port || (original.protocol === "https:" ? 443 : 80)),
102
- servername: target.servername ?? original.hostname
103
- });
104
- this.signal.throwIfAborted();
105
- const url = new URL(original);
106
- url.hostname = endpoint.host;
107
- url.port = String(endpoint.port);
108
- engine = new OpenSearchEngine({ ...target, node: url.toString(), servername: endpoint.servername ?? target.servername ?? original.hostname });
109
- await engine.ping(this.signal, target.connectTimeoutMs ?? target.requestTimeoutMs ?? 1e4);
110
- this.signal.throwIfAborted();
111
- this.lifecycle.onRoute?.({ transport: transport.name, reason });
112
- this.#engine = engine;
113
- return;
114
- } catch (error) {
115
- await engine?.close();
116
- this.signal.throwIfAborted();
117
- const network = isConnectionNetworkError(error);
118
- if (!network && !(error instanceof errors.ConnectionError) && !(error instanceof errors.TimeoutError))
119
- throw error;
120
- reason = network ? String(error.code) : error.name;
121
- if (index === this.source.transports.length - 1) {
122
- throw new ToolboxError(`OpenSearch connect failed (${reason})`, { kind: "connection_failed", code: reason, cause: error });
123
- }
124
- }
138
+ if (error instanceof ToolboxError && error.kind === "timeout")
139
+ throw error;
140
+ const reason = openSearchRetryReason(error);
141
+ if (reason === undefined)
142
+ throw error;
143
+ throw new ToolboxError(`OpenSearch connect failed (${reason})`, { kind: "connection_failed", code: reason, cause: error });
125
144
  }
126
- throw new Error("OpenSearch DataSource has no transport");
127
145
  })();
128
146
  }
129
147
  #ready() {
@@ -5,13 +5,18 @@ import {
5
5
  import {
6
6
  queryReadonlySession,
7
7
  validateQueryLimits
8
- } from "./chunk-age65q4n.js";
8
+ } from "./chunk-db4an9td.js";
9
9
  import {
10
+ connectMysql,
10
11
  destroyMysqlConnection
11
- } from "./chunk-136egyff.js";
12
+ } from "./chunk-835fssgz.js";
13
+ import {
14
+ acquireConnection,
15
+ awaitWithSignal
16
+ } from "./chunk-gm4swndx.js";
12
17
  import {
13
18
  ToolboxError
14
- } from "./chunk-20kv4wc3.js";
19
+ } from "./chunk-rkt41za2.js";
15
20
  import {
16
21
  ConcurrencyPool
17
22
  } from "./chunk-v3hxvnrc.js";
@@ -20,7 +25,6 @@ import {
20
25
  } from "./chunk-h8fwx0k7.js";
21
26
 
22
27
  // src/mysql/client.ts
23
- import { createConnection } from "mysql2/promise";
24
28
  function mysqlOperationError(error, phase) {
25
29
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "PROTOCOL_CONNECTION_LOST")
26
30
  return error;
@@ -37,7 +41,7 @@ class MysqlDatabase {
37
41
  connect;
38
42
  #queries = new ConcurrencyPool(1);
39
43
  #connections = new Map;
40
- constructor(transports, options, connect = createConnection) {
44
+ constructor(transports, options, connect = connectMysql) {
41
45
  this.transports = transports;
42
46
  this.options = options;
43
47
  this.connect = connect;
@@ -120,39 +124,40 @@ class MysqlDatabase {
120
124
  return rows;
121
125
  });
122
126
  }
123
- async#open(target) {
124
- let reason;
125
- for (let i = 0;i < this.transports.length; i++) {
126
- const transport = this.transports[i];
127
- this.options.signal?.throwIfAborted();
128
- if (transport.kind === "python") {
129
- this.options.onRoute?.({ transport: transport.name, reason });
130
- return { kind: "python", transport };
131
- }
132
- try {
133
- const endpoint = await transport.connect(target);
134
- const connection = await this.connect({
135
- host: endpoint.host,
136
- port: endpoint.port,
137
- user: target.user,
138
- password: target.password,
139
- database: target.database,
140
- connectTimeout: this.options.connectTimeoutMs,
141
- dateStrings: true,
142
- supportBigNumbers: true,
143
- bigNumberStrings: true
144
- });
145
- this.options.onRoute?.({ transport: endpoint.host === target.host && endpoint.port === target.port ? "direct" : transport.name, reason });
146
- return { kind: "tcp", connection };
147
- } catch (error) {
148
- const failure = mysqlOperationError(error, "connect");
149
- const connectionLost = failure instanceof ToolboxError && failure.kind === "connection_lost";
150
- if (!isConnectionNetworkError(failure) && !connectionLost || i === this.transports.length - 1)
151
- throw failure;
152
- reason = String(failure.code);
153
- }
154
- }
155
- throw new Error("MySQL DataSource has no transport");
127
+ #open(target) {
128
+ return acquireConnection(this.transports, {
129
+ name: "MySQL connection",
130
+ ...this.options,
131
+ open: async (transport, attempt) => {
132
+ if (transport.kind === "python")
133
+ return { connection: { kind: "python", transport }, transport: transport.name };
134
+ try {
135
+ const endpoint = await transport.connect(target);
136
+ const connection = await this.connect({
137
+ host: endpoint.host,
138
+ port: endpoint.port,
139
+ user: target.user,
140
+ password: target.password,
141
+ database: target.database,
142
+ connectTimeout: attempt.remainingMs(),
143
+ dateStrings: true,
144
+ supportBigNumbers: true,
145
+ bigNumberStrings: true
146
+ }, attempt.signal);
147
+ return {
148
+ connection: { kind: "tcp", connection },
149
+ transport: endpoint.host === target.host && endpoint.port === target.port ? "direct" : transport.name
150
+ };
151
+ } catch (error) {
152
+ throw mysqlOperationError(error, "connect");
153
+ }
154
+ },
155
+ close: (session) => {
156
+ if (session.kind === "tcp")
157
+ destroyMysqlConnection(session.connection);
158
+ },
159
+ retryReason: (error) => isConnectionNetworkError(error) || error instanceof ToolboxError && error.kind === "connection_lost" ? String(error.code) : undefined
160
+ });
156
161
  }
157
162
  async queryOne(target, sql, values) {
158
163
  return (await this.query(target, sql, values))[0];
@@ -186,7 +191,7 @@ class MysqlClient {
186
191
  initialize() {
187
192
  this.signal.throwIfAborted();
188
193
  return this.#initialization ??= (async () => {
189
- this.#target = await this.source.resolve();
194
+ this.#target = await awaitWithSignal(this.source.resolve(), this.signal);
190
195
  this.signal.throwIfAborted();
191
196
  this.#database = new MysqlDatabase(this.source.transports, { ...this.options, signal: this.signal });
192
197
  await this.#database.initialize(this.#target);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FileError,
3
3
  ToolboxError
4
- } from "./chunk-20kv4wc3.js";
4
+ } from "./chunk-rkt41za2.js";
5
5
  import {
6
6
  ConcurrencyPool
7
7
  } from "./chunk-v3hxvnrc.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resourcePath
3
- } from "./chunk-ftkrjqkc.js";
3
+ } from "./chunk-dpcge1xt.js";
4
4
  import {
5
5
  clientKey
6
6
  } from "./chunk-a9htb6cr.js";
@@ -8,7 +8,7 @@ import {
8
8
  KubernetesError,
9
9
  ToolboxError,
10
10
  kubernetesHttpError
11
- } from "./chunk-20kv4wc3.js";
11
+ } from "./chunk-rkt41za2.js";
12
12
  import {
13
13
  ConcurrencyPool
14
14
  } from "./chunk-v3hxvnrc.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  KubernetesError
3
- } from "./chunk-20kv4wc3.js";
3
+ } from "./chunk-rkt41za2.js";
4
4
 
5
5
  // src/kubernetes/compute-quantity.ts
6
6
  function object(value) {
@@ -0,0 +1,34 @@
1
+ import type { ClientLifecycle } from "../datasource";
2
+ /** Stop waiting without leaving rejected preparation promises unobserved. */
3
+ export declare function awaitWithSignal<T>(pending: Promise<T>, signal: AbortSignal, cancel?: () => void): Promise<T>;
4
+ export interface ConnectionAttempt {
5
+ readonly signal: AbortSignal;
6
+ /** Remaining mapping + handshake budget, not a fresh timeout for each phase. */
7
+ remainingMs(): number;
8
+ }
9
+ interface Acquisition<R, C> {
10
+ name: string;
11
+ connectTimeoutMs: number;
12
+ acquireTimeoutMs?: number;
13
+ signal?: AbortSignal;
14
+ open(route: R, attempt: ConnectionAttempt): Promise<{
15
+ connection: C;
16
+ transport: string;
17
+ }>;
18
+ close(connection: C): void | Promise<void>;
19
+ /** Undefined means terminal; authentication and protocol errors must not change route. */
20
+ retryReason(error: unknown): string | undefined;
21
+ onRoute?: ClientLifecycle["onRoute"];
22
+ }
23
+ /**
24
+ * @spec One acquisition owns ordered attempts and a deadline covering mapping and handshake.
25
+ * @spec Cancellation keeps its original reason; a late successful connection is closed, never published.
26
+ * @rule Only connection preparation enters this function. Commands and queries must never be replayed.
27
+ * @why Protocol adapters own partial handles and error classification; all protocols share the same lifecycle.
28
+ * @see {@link component://docs/toolbox.md}
29
+ * @case id=connection_mapping_cancel,desc=`Cancel while mapping a route`,expect=`Acquisition exits without a late handshake`
30
+ */
31
+ export declare function acquireConnection<R extends {
32
+ readonly name: string;
33
+ }, C>(routes: readonly R[], options: Acquisition<R, C>): Promise<C>;
34
+ export {};
@@ -0,0 +1,9 @@
1
+ import {
2
+ acquireConnection,
3
+ awaitWithSignal
4
+ } from "../chunk-gm4swndx.js";
5
+ import"../chunk-rkt41za2.js";
6
+ export {
7
+ awaitWithSignal,
8
+ acquireConnection
9
+ };
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  KubernetesError,
8
8
  ToolboxError
9
- } from "./chunk-20kv4wc3.js";
9
+ } from "./chunk-rkt41za2.js";
10
10
  export {
11
11
  clientKey,
12
12
  ToolboxError,
package/dist/errors.d.ts CHANGED
@@ -13,6 +13,10 @@ export declare class KubernetesError extends ToolboxError {
13
13
  }
14
14
  export declare class FileError extends ToolboxError {
15
15
  }
16
+ /** A local deadline expiry, distinct from a connection failure reported by a driver. */
17
+ export declare class TimeoutError extends ToolboxError {
18
+ constructor(message: string, options?: ErrorOptions);
19
+ }
16
20
  export declare function kubernetesHttpError(status: number): KubernetesError;
17
21
  /**
18
22
  * Normalize a failed kubectl resource request at the transport boundary.
package/dist/errors.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import {
2
2
  FileError,
3
3
  KubernetesError,
4
+ TimeoutError,
4
5
  ToolboxError,
5
6
  kubernetesCommandError,
6
7
  kubernetesHttpError
7
- } from "./chunk-20kv4wc3.js";
8
+ } from "./chunk-rkt41za2.js";
8
9
  export {
9
10
  kubernetesHttpError,
10
11
  kubernetesCommandError,
11
12
  ToolboxError,
13
+ TimeoutError,
12
14
  KubernetesError,
13
15
  FileError
14
16
  };
@@ -2,8 +2,8 @@ import {
2
2
  BoundedFileClient,
3
3
  fileClientOptions,
4
4
  filePaths
5
- } from "../chunk-kkts16w9.js";
6
- import"../chunk-20kv4wc3.js";
5
+ } from "../chunk-ww11q58v.js";
6
+ import"../chunk-rkt41za2.js";
7
7
  import"../chunk-v3hxvnrc.js";
8
8
  export {
9
9
  filePaths,
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  PodFileClient,
3
3
  PodFileDataSource
4
- } from "../chunk-grpygk6f.js";
4
+ } from "../chunk-dx6yvj81.js";
5
5
  import {
6
6
  LocalFileClient,
7
7
  LocalFileDataSource
8
- } from "../chunk-sqbew04a.js";
9
- import"../chunk-kkts16w9.js";
8
+ } from "../chunk-f3vq900j.js";
9
+ import"../chunk-ww11q58v.js";
10
10
  import {
11
11
  FileError
12
- } from "../chunk-20kv4wc3.js";
12
+ } from "../chunk-rkt41za2.js";
13
13
  import"../chunk-v3hxvnrc.js";
14
14
 
15
15
  // src/file/index.ts
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  LocalFileClient,
3
3
  LocalFileDataSource
4
- } from "../chunk-sqbew04a.js";
5
- import"../chunk-kkts16w9.js";
6
- import"../chunk-20kv4wc3.js";
4
+ } from "../chunk-f3vq900j.js";
5
+ import"../chunk-ww11q58v.js";
6
+ import"../chunk-rkt41za2.js";
7
7
  import"../chunk-v3hxvnrc.js";
8
8
  export {
9
9
  LocalFileDataSource,
package/dist/file/pod.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  PodFileClient,
3
3
  PodFileDataSource
4
- } from "../chunk-grpygk6f.js";
5
- import"../chunk-kkts16w9.js";
6
- import"../chunk-20kv4wc3.js";
4
+ } from "../chunk-dx6yvj81.js";
5
+ import"../chunk-ww11q58v.js";
6
+ import"../chunk-rkt41za2.js";
7
7
  import"../chunk-v3hxvnrc.js";
8
8
  export {
9
9
  PodFileDataSource,
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  KubernetesClient
3
- } from "../chunk-c2n8zvyn.js";
3
+ } from "../chunk-g2bac2jd.js";
4
+ import"../chunk-ae84rzq5.js";
4
5
  import"../chunk-2171tajs.js";
5
6
  import"../chunk-j46x7j7c.js";
6
7
  import"../chunk-6fnhzhdb.js";
7
- import"../chunk-4ms037k2.js";
8
- import"../chunk-ftkrjqkc.js";
9
- import"../chunk-shsabpc1.js";
10
- import"../chunk-0s27bgga.js";
8
+ import"../chunk-x0yhfg29.js";
9
+ import"../chunk-dpcge1xt.js";
10
+ import"../chunk-r3rnsvwv.js";
11
11
  import"../chunk-a9htb6cr.js";
12
- import"../chunk-20kv4wc3.js";
12
+ import"../chunk-rkt41za2.js";
13
13
  import"../chunk-v3hxvnrc.js";
14
14
  import"../chunk-631vce5z.js";
15
15
  import"../chunk-hmah9t8e.js";
@@ -8,8 +8,8 @@ import {
8
8
  sum,
9
9
  summarizePods,
10
10
  zero
11
- } from "../chunk-5zgz1z41.js";
12
- import"../chunk-20kv4wc3.js";
11
+ } from "../chunk-z15gwjvg.js";
12
+ import"../chunk-rkt41za2.js";
13
13
  export {
14
14
  zero,
15
15
  summarizePods,
@@ -6,10 +6,10 @@ import {
6
6
  requiredString,
7
7
  sum,
8
8
  summarizePods
9
- } from "../chunk-5zgz1z41.js";
9
+ } from "../chunk-z15gwjvg.js";
10
10
  import {
11
11
  KubernetesError
12
- } from "../chunk-20kv4wc3.js";
12
+ } from "../chunk-rkt41za2.js";
13
13
 
14
14
  // src/kubernetes/compute.ts
15
15
  function requestBudget(options) {
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  KubernetesClient
3
- } from "../chunk-c2n8zvyn.js";
3
+ } from "../chunk-g2bac2jd.js";
4
+ import"../chunk-ae84rzq5.js";
4
5
  import"../chunk-2171tajs.js";
5
6
  import"../chunk-j46x7j7c.js";
6
7
  import"../chunk-6fnhzhdb.js";
7
- import"../chunk-4ms037k2.js";
8
- import"../chunk-ftkrjqkc.js";
9
- import"../chunk-shsabpc1.js";
10
- import"../chunk-0s27bgga.js";
8
+ import"../chunk-x0yhfg29.js";
9
+ import"../chunk-dpcge1xt.js";
10
+ import"../chunk-r3rnsvwv.js";
11
11
  import {
12
12
  clientKey
13
13
  } from "../chunk-a9htb6cr.js";
14
- import"../chunk-20kv4wc3.js";
14
+ import"../chunk-rkt41za2.js";
15
15
  import"../chunk-v3hxvnrc.js";
16
16
  import"../chunk-631vce5z.js";
17
17
  import"../chunk-hmah9t8e.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  KubernetesExecTransport
3
- } from "../chunk-shsabpc1.js";
4
- import"../chunk-20kv4wc3.js";
3
+ } from "../chunk-r3rnsvwv.js";
4
+ import"../chunk-rkt41za2.js";
5
5
  import"../chunk-v3hxvnrc.js";
6
6
  export {
7
7
  KubernetesExecTransport
@@ -8,7 +8,7 @@ import"../chunk-a9htb6cr.js";
8
8
  import {
9
9
  clientKey
10
10
  } from "../chunk-z5pm2emz.js";
11
- import"../chunk-20kv4wc3.js";
11
+ import"../chunk-rkt41za2.js";
12
12
  import"../chunk-v3hxvnrc.js";
13
13
 
14
14
  // src/kubernetes/pod-log-datasource.ts
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  KubernetesResourceTransport
3
- } from "../chunk-4ms037k2.js";
4
- import"../chunk-ftkrjqkc.js";
3
+ } from "../chunk-x0yhfg29.js";
4
+ import"../chunk-dpcge1xt.js";
5
5
  import"../chunk-a9htb6cr.js";
6
- import"../chunk-20kv4wc3.js";
6
+ import"../chunk-rkt41za2.js";
7
7
  import"../chunk-v3hxvnrc.js";
8
8
  export {
9
9
  KubernetesResourceTransport
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  resourcePath
3
- } from "../chunk-ftkrjqkc.js";
4
- import"../chunk-20kv4wc3.js";
3
+ } from "../chunk-dpcge1xt.js";
4
+ import"../chunk-rkt41za2.js";
5
5
  export {
6
6
  resourcePath
7
7
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  resolveWorkload
3
- } from "../chunk-0s27bgga.js";
4
- import"../chunk-20kv4wc3.js";
3
+ } from "../chunk-ae84rzq5.js";
4
+ import"../chunk-rkt41za2.js";
5
5
  export {
6
6
  resolveWorkload
7
7
  };
@@ -1,13 +1,16 @@
1
1
  import type { Client } from "../client";
2
- import { type Connection, type ConnectionOptions } from "mysql2/promise";
2
+ import type { Connection, ConnectionOptions } from "mysql2/promise";
3
3
  import type { Database, DatabaseTarget, DatabaseRow, DatabaseQueryLimits, DatabaseQueryResult, SqlStatement } from "./types";
4
4
  import type { ConnectionSource, ClientLifecycle } from "../datasource";
5
5
  import { type Transport } from "../transport";
6
6
  export interface MysqlDatabaseOptions extends ClientLifecycle {
7
+ /** Per-route mapping and handshake budget. */
7
8
  connectTimeoutMs: number;
9
+ /** Total acquisition budget; defaults to connectTimeoutMs times the route count. */
10
+ acquireTimeoutMs?: number;
8
11
  queryTimeoutMs: number;
9
12
  }
10
- type ConnectionFactory = (options: ConnectionOptions) => Promise<Connection>;
13
+ type ConnectionFactory = (options: ConnectionOptions, signal?: AbortSignal) => Promise<Connection>;
11
14
  /**
12
15
  * @spec SQL is executed once; only native connection establishment may advance to another transport.
13
16
  * @why mysql2 reports a socket closed during handshake as PROTOCOL_CONNECTION_LOST, before any SQL is sent.
@@ -2,11 +2,12 @@ import {
2
2
  MysqlClient,
3
3
  MysqlDatabase,
4
4
  openMysql
5
- } from "../chunk-h73h2b7y.js";
5
+ } from "../chunk-t4hrnaj0.js";
6
6
  import"../chunk-m91t44jh.js";
7
- import"../chunk-age65q4n.js";
8
- import"../chunk-136egyff.js";
9
- import"../chunk-20kv4wc3.js";
7
+ import"../chunk-db4an9td.js";
8
+ import"../chunk-835fssgz.js";
9
+ import"../chunk-gm4swndx.js";
10
+ import"../chunk-rkt41za2.js";
10
11
  import"../chunk-v3hxvnrc.js";
11
12
  import"../chunk-h8fwx0k7.js";
12
13
  import"../chunk-wv4jwhvj.js";
@@ -1,3 +1,6 @@
1
+ import { type ConnectionOptions } from "mysql2";
1
2
  import type { Connection } from "mysql2/promise";
3
+ /** Keep the native handle available so cancellation can destroy an unfinished handshake. */
4
+ export declare function connectMysql(options: ConnectionOptions, signal?: AbortSignal): Promise<Connection>;
2
5
  /** @why Disposal must release the socket even when the peer never finishes its TCP half-close. */
3
6
  export declare function destroyMysqlConnection(connection: Connection): void;
@@ -1,6 +1,8 @@
1
1
  import {
2
+ connectMysql,
2
3
  destroyMysqlConnection
3
- } from "../chunk-136egyff.js";
4
+ } from "../chunk-835fssgz.js";
4
5
  export {
5
- destroyMysqlConnection
6
+ destroyMysqlConnection,
7
+ connectMysql
6
8
  };
@@ -1,3 +1,4 @@
1
+ import"../chunk-pfar9cm3.js";
1
2
  import {
2
3
  withReadScope
3
4
  } from "../chunk-4sqvppz7.js";
@@ -5,9 +6,9 @@ import {
5
6
  MysqlClient,
6
7
  MysqlDatabase,
7
8
  openMysql
8
- } from "../chunk-h73h2b7y.js";
9
+ } from "../chunk-t4hrnaj0.js";
9
10
  import"../chunk-m91t44jh.js";
10
- import"../chunk-age65q4n.js";
11
+ import"../chunk-db4an9td.js";
11
12
  import {
12
13
  parseMysqlEnvTarget
13
14
  } from "../chunk-qq77x9ek.js";
@@ -16,9 +17,9 @@ import {
16
17
  limitedStatement,
17
18
  queryLimited
18
19
  } from "../chunk-1gyshqd9.js";
19
- import"../chunk-136egyff.js";
20
- import"../chunk-pfar9cm3.js";
21
- import"../chunk-20kv4wc3.js";
20
+ import"../chunk-835fssgz.js";
21
+ import"../chunk-gm4swndx.js";
22
+ import"../chunk-rkt41za2.js";
22
23
  import"../chunk-v3hxvnrc.js";
23
24
  import"../chunk-h8fwx0k7.js";
24
25
  import"../chunk-wv4jwhvj.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  queryReadonlySession,
3
3
  validateQueryLimits
4
- } from "../chunk-age65q4n.js";
5
- import"../chunk-136egyff.js";
4
+ } from "../chunk-db4an9td.js";
5
+ import"../chunk-835fssgz.js";
6
6
  export {
7
7
  validateQueryLimits,
8
8
  queryReadonlySession
@@ -9,8 +9,10 @@ export interface OpenSearchOptions {
9
9
  node: string;
10
10
  auth: OpenSearchAuth;
11
11
  requestTimeoutMs?: number;
12
- /** Deadline for the initialization probe; query deadlines remain independent. */
12
+ /** Per-route mapping and probe budget; query deadlines remain independent. */
13
13
  connectTimeoutMs?: number;
14
+ /** Total acquisition budget; defaults to the per-route budget times the route count. */
15
+ acquireTimeoutMs?: number;
14
16
  /** Original TLS identity when a transport maps the socket to localhost. */
15
17
  servername?: string;
16
18
  }
@@ -3,8 +3,9 @@ import {
3
3
  OpenSearchEngine,
4
4
  isOpenSearchReadApi,
5
5
  openOpenSearch
6
- } from "../chunk-8mb6natq.js";
7
- import"../chunk-20kv4wc3.js";
6
+ } from "../chunk-sbt20n84.js";
7
+ import"../chunk-gm4swndx.js";
8
+ import"../chunk-rkt41za2.js";
8
9
  import"../chunk-h8fwx0k7.js";
9
10
  import"../chunk-wv4jwhvj.js";
10
11
  import"../chunk-631vce5z.js";
@@ -10,8 +10,9 @@ import {
10
10
  OpenSearchEngine,
11
11
  isOpenSearchReadApi,
12
12
  openOpenSearch
13
- } from "../chunk-8mb6natq.js";
14
- import"../chunk-20kv4wc3.js";
13
+ } from "../chunk-sbt20n84.js";
14
+ import"../chunk-gm4swndx.js";
15
+ import"../chunk-rkt41za2.js";
15
16
  import"../chunk-h8fwx0k7.js";
16
17
  import"../chunk-wv4jwhvj.js";
17
18
  import"../chunk-631vce5z.js";
@@ -4,9 +4,10 @@ import {
4
4
  RedisConnection,
5
5
  openRedis,
6
6
  parseRedisInfo
7
- } from "../chunk-yp6tcw22.js";
7
+ } from "../chunk-f98qrpx1.js";
8
+ import"../chunk-gm4swndx.js";
8
9
  import"../chunk-5tbzf9wd.js";
9
- import"../chunk-20kv4wc3.js";
10
+ import"../chunk-rkt41za2.js";
10
11
  import"../chunk-h8fwx0k7.js";
11
12
  import"../chunk-wv4jwhvj.js";
12
13
  import"../chunk-631vce5z.js";
@@ -1,18 +1,19 @@
1
- import {
2
- collectRedisKeyStats,
3
- redisGroupedSampleSizes
4
- } from "../chunk-jr9y0ghr.js";
5
1
  import {
6
2
  RedisAccess,
7
3
  RedisClient,
8
4
  RedisConnection,
9
5
  openRedis,
10
6
  parseRedisInfo
11
- } from "../chunk-yp6tcw22.js";
7
+ } from "../chunk-f98qrpx1.js";
8
+ import"../chunk-gm4swndx.js";
9
+ import {
10
+ collectRedisKeyStats,
11
+ redisGroupedSampleSizes
12
+ } from "../chunk-jr9y0ghr.js";
12
13
  import {
13
14
  discoverRedisTopology
14
15
  } from "../chunk-5tbzf9wd.js";
15
- import"../chunk-20kv4wc3.js";
16
+ import"../chunk-rkt41za2.js";
16
17
  import"../chunk-h8fwx0k7.js";
17
18
  import"../chunk-wv4jwhvj.js";
18
19
  import"../chunk-631vce5z.js";
@@ -2,7 +2,7 @@ import {
2
2
  S3DataSource
3
3
  } from "../chunk-35cjec9z.js";
4
4
  import"../chunk-z5pm2emz.js";
5
- import"../chunk-20kv4wc3.js";
5
+ import"../chunk-rkt41za2.js";
6
6
  import"../chunk-zp1s833x.js";
7
7
  import"../chunk-v3hxvnrc.js";
8
8
  import"../chunk-x4ntb898.js";
package/dist/s3/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  S3DataSource
3
3
  } from "../chunk-35cjec9z.js";
4
4
  import"../chunk-z5pm2emz.js";
5
- import"../chunk-20kv4wc3.js";
5
+ import"../chunk-rkt41za2.js";
6
6
  import {
7
7
  S3Client
8
8
  } from "../chunk-zp1s833x.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compforge/harness-toolbox",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "Shared infrastructure clients, transports and bounded data capture",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,11 +0,0 @@
1
- // src/mysql/connection.ts
2
- function destroyMysqlConnection(connection) {
3
- const native = connection.connection;
4
- try {
5
- connection.destroy();
6
- } finally {
7
- native.stream.destroy();
8
- }
9
- }
10
-
11
- export { destroyMysqlConnection };