npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@compforge/harness-toolbox

v0.4.7

Published

Shared infrastructure clients, transports and bounded data capture

Readme

Harness Toolbox

Client, ClientProvider, DataSource, EnvironmentContext and ClientManager are owned by the sibling TypeScript package @compforge/harness-common. Toolbox provides protocol implementations and transports; its lifecycle entrypoints re-export the same common symbols.

Shared infrastructure for diagnostic tools, test runners and business adapters. Use the same clients to access Kubernetes, MySQL, Redis, OpenSearch and S3-compatible object storage without depending on a particular command, plugin protocol, test model or verdict.

The package supports Node.js 22+ and Bun. Install with npm install @compforge/harness-toolbox. Use @compforge/harness-toolbox/duration to parse a positive duration such as 30m or 2d into milliseconds. searchAfterPages from @compforge/harness-toolbox/opensearch/search-after yields raw OpenSearch hit pages with a caller supplied query and stable sort. It streams pages without owning the client or claiming snapshot consistency; callers supply cancellation and decide how to persist or verify results. Protocol adapters have separate entry points; importing lifecycle or transport contracts does not load database or Kubernetes drivers.

File sources

A FileDataSource is a caller-declared acquisition group. Use LocalFileDataSource for local paths or PodFileDataSource for paths in one explicit namespace/Pod/container. Both extend Common's FileDataSource; no additional id/kind wrapper is required.

import { ClientManager } from "@compforge/harness-common";
import { LocalFileDataSource, PodFileDataSource } from "@compforge/harness-toolbox/file";

const source = new LocalFileDataSource(["./config.yaml", "./secret/config.yaml"], { concurrency: 2 });
const clients = new ClientManager();
try {
  const file = await clients.get(source);
  const results = await file.read({ maxBytes: 1024 * 1024, maxTotalBytes: 2 * 1024 * 1024, timeoutMs: 15_000 });
  for (const result of results) {
    if (result.status === "error") console.error(result.path, result.error.message);
    else console.log(result.path, new TextDecoder("utf-8", { fatal: true }).decode(result.bytes));
  }
} finally { await clients.dispose(); }

// Resolve Workloads first. This entire group is fetched through one exec session.
const podSource = new PodFileDataSource(environment,
  { namespace: "app", pod: "worker-abc", container: "worker" },
  ["/etc/app/config.yaml", "/etc/app/secret/config.yaml"], { concurrency: 2 });

Every read reopens all declared paths and returns one ordered result per path. Empty files succeed; missing, denied, nonregular and oversized files return individual errors with no partial bytes. An error in one file does not discard sibling results. Per-file limits reset for each path; the total budget bounds retained decoded bytes, including incomplete files. Queueing and the whole group share one deadline. Cancellation, total-budget exhaustion, deadline and transport/protocol failure reject the operation. Sources share clients, never previous file contents.

Pod groups use one exec and require sh, head -c, base64 and tr. Paths remain positional arguments even when they contain shell syntax or newlines. Indexed base64 records carry raw binary bytes plus each file's exit status; truncated or malformed records reject without replay. No remote temporary files are written. Local reads allow symlinks to regular files and reject nonregular files. Keep the path and Pod target on evidence; grouping guarantees neither an atomic filesystem snapshot nor application configuration precedence. A Pod name is not a UID fence; callers needing incarnation guarantees must check discovery around collection. File clients borrow root Kubernetes access and never close it themselves.

Version 0.4.2 requires Common 0.5.4. Replace single path arguments with arrays, supply maxTotalBytes, and handle per-file results. Publish Common before Toolbox; this change has no single-file legacy adapter.

Redis sources

RedisClient owns resolved configuration and reusable node connections. Use clusterType to select single, cluster or sentinel; endpoints contains that mode's seed addresses. Sentinel configuration additionally provides sentinelMasterName and optional Sentinel credentials, separate from data-node credentials. Cluster connections require database zero.

Supply ordered TCP transports through ConnectionSource. Only mapping or handshake network failures advance to the next transport; authentication and TLS verification failures stop immediately. connectTimeoutMs bounds each route (mapping and handshake); it defaults to the command timeoutMs. acquireTimeoutMs bounds all candidates together and defaults to the per-route budget times the number of routes. A spent total budget does not start another route. onRoute reports the selected route and the preceding failure code without credentials.

client.topology() refreshes topology using the same connections and route policy as client.access.connection(endpoint, database). A root signal cancels connection preparation and active commands. Commands and pipelines are sent once, with no offline queue or automatic replay; a subsequent acquisition replaces an unhealthy connection. The caller owns dispose() and transport cleanup, and diagnostics own sampling policy and collected observations.

Kubernetes resources

Borrow a root KubernetesClient through KubernetesEnvironment and ClientManager. Use get(ref) and list(query) for resource reads; inNamespace(namespace) binds a borrowed view to a namespace without creating a second client or pool:

const kube = await clients.get(environment);
const ns = kube.inNamespace("app");
const service = await ns.get({ apiVersion: "v1", resource: "services", name: "api" });
const pods = await ns.list({ apiVersion: "v1", resource: "pods", labelSelector: "app=api" },
  { timeoutMs: 15_000, signal });

resource is the plural REST resource name, not a Kind or kubectl alias. Explicit API versions support built-in resources and custom resources without a discovery request or local allowlist. The root uses its configured namespace unless the query overrides it; namespace views enforce their bound namespace. Resource objects and list metadata are returned intact. List requests accept label/field selectors and explicit limit/continue pages; they do not silently read all pages. Errors are KubernetesError objects carrying a stable kind and the HTTP status when available. Callers retain the query with their evidence; resource operations do not produce shell command results.

The root owns one HTTP transport with keep-alive connections shared across namespaces and resource kinds. Authentication refreshes on each request. TLS/proxy identity changes select a new pool and retire the previous one after its active reads finish. Requests have bounded bytes and a deadline covering queueing, authentication and body consumption. dispose() cancels work, waits for tracked operations, and closes connections. Views and injected borrowed resource readers have no ownership of that lifecycle. No query results are cached and failures are never replayed through another transport.

Native resource access uses kubeconfig authentication, TLS and explicit proxy-url. It does not select transports based on shell proxy environment variables. exec and forward are separate streaming operations; their implementation does not intercept resource reads. KubernetesClient has no run(argv) method. Concurrency remains caller-configured rather than inferred from pool size.

Workload discovery

Borrow a KubernetesClient through KubernetesEnvironment and the shared ClientManager lifecycle, then call client.resolveWorkload(workload, environmentId). The ID is a stable target identity from your environment registry, not a display name or kubeconfig path. The Workload's namespace overrides the client's default. Resource, Service selector and labels locations return all matching Pod incarnations without readiness filtering; successful empty discovery returns an empty array. Missing resources, selectorless Services and failed access raise KubernetesError.

The resource path uses client-node kubeconfig authentication/TLS and bounded native JSON reads, not kubectl error text. Configure request timeout, concurrency and response bytes with the client's ResourceLimits constructor argument. Native exec uses the same root capacity/byte policy with a separate slot pool; its per-call timeout defaults to that policy and covers queueing, authentication, upgrade and execution. Explicit per-call timeouts support longer commands.

Exec requires get pods/exec permission for the WebSocket upgrade (kubectl/SPDY paths may use create pods/exec). It connects directly to the Pod exec WebSocket endpoint without a preliminary Pod GET or kubectl subprocess. Each command owns a stream session; failures are never retried or replayed. Kubeconfig controls authentication, TLS and proxy access. stdout/stderr and remote exit status remain separate; retained output and individual messages are bounded. Streaming callers may set collectStdout: false and enforce their own total byte budget. Root disposal cancels active and queued sessions. Protocol v4 is accepted for output-only commands; stdin requires v5 to close input without losing remaining output/status. Bun does not expose HTTP upgrade rejection status, so those failures retain an unknown connection error. Native exec with a kubeconfig proxy requires Node: Bun cannot reliably release proxy sockets or preserve TLS name overrides. It fails before opening a session on Bun; use Node or explicitly provide an Executor adapter. The transport never bypasses the proxy or TLS checks. Port-forward still uses kubectl. See the shared Workload model.

import { ClientManager, EnvironmentContext } from "@compforge/harness-toolbox";
import { KubernetesEnvironment } from "@compforge/harness-toolbox/kubernetes/environment";

const clients = new ClientManager();
try {
  const ctx = new EnvironmentContext(
    new KubernetesEnvironment("dev",
      { kubeconfig: "/config/cluster", context: "dev", namespace: "app" },
      { timeoutMs: 10_000, concurrency: 4, maxBytes: 8 * 1024 * 1024 }),
    clients, performance.now() + 30_000,
  );
  const kube = await ctx.clients.get(ctx.environment);
  const pods = await kube.resolveWorkload({
    name: "api", platform: "kubernetes",
    location: { kind: "resource", resource_kind: "Deployment", name: "api" },
  }, "cluster-a/runtime");
} finally {
  await clients.dispose();
}

The context does not cancel work automatically. Its budget is available as remainingMs; the root owns cancellation and must join child work before disposing clients.

Catch ToolboxError from the root entry or @compforge/harness-toolbox/errors; use kind and code for decisions. Native cause may contain sensitive details and is debug-only. Connection acquisition deadlines and Redis handshake/command deadlines raise TimeoutError from @compforge/harness-toolbox/errors. It extends ToolboxError, retaining kind: "timeout" and code: "ETIMEDOUT"; OpenSearch preserves it unchanged.

For coordinated common/toolbox development, install and build common before installing toolbox:

# From sdks/typescript/toolbox
(cd ../common && bun install --frozen-lockfile && make build)
bun install --frozen-lockfile
make lint
make test

The toolbox root override resolves common from ../common only for local development; the published dependency remains a registry version. Publish common before toolbox. The Makefile builds common before typechecking or building toolbox.

A ClientProvider identifies a reusable client and constructs it; DataSource adds data-access semantics. Accessible environments implement ClientProvider directly without becoming data sources. The Client owns initialization and idempotent disposal; a ClientManager joins concurrent initialization for the same key and closes owned clients when the caller finishes. A ConnectionSource resolves connection settings and available Transport paths. Clients execute protocol operations through those paths.

import { ClientManager, clientKey, type DataSource } from "@compforge/harness-toolbox";
import { MysqlClient } from "@compforge/harness-toolbox/mysql";
import { DirectTransport } from "@compforge/harness-toolbox/transport";

const target = { host: "localhost", port: 3306, database: "app", user: "reader", password: "..." };
const source: DataSource<MysqlClient> = {
  clientKey: clientKey("mysql", target),
  createClient: (_clients, signal) => new MysqlClient({
    resolve: async () => target,
    transports: [new DirectTransport()],
  }, { signal, connectTimeoutMs: 5_000, queryTimeoutMs: 10_000 }),
};
const clients = new ClientManager();
try {
  const db = await clients.get(source);
  const rows = await db.database.query(db.target, "SELECT 1 AS value", []);
} finally {
  await clients.dispose();
}

Callers own credentials, authorization, query semantics, capacity and timeouts. MySQL can use TCP or a Python-capable Pod; the latter needs PyMySQL. Transport fallback occurs only when establishing a connection, never by replaying a failed SQL operation. Pod inputs use stdin.

Pod log capture shares an identical Pod/container instance and absolute time window once per client, while consumers filter independently and retain their own raw copies. Supply an explicit capture policy, shared concurrency pool and byte budget; the package does not choose product limits. Temporary capture files live until client disposal.

See toolbox contracts for ownership and cross-language semantics.

Read S3 objects

S3 uses the official AWS SDK behind the same managed lifecycle. Supply resolved connection settings and explicit limits; the package does not discover business configuration or credentials.

import { ClientManager } from "@compforge/harness-toolbox";
import { S3DataSource } from "@compforge/harness-toolbox/s3";

const clients = new ClientManager();
try {
  const s3 = await clients.get(new S3DataSource({
    endpoint: "https://s3.example.com", region: "us-east-1",
    credentials: { accessKeyId: "...", secretAccessKey: "..." },
  }, { concurrency: 4, connectTimeoutMs: 5_000, requestTimeoutMs: 15_000 }));
  const object = await s3.headObject("artifacts", "runs/example.json");
  const prefix = await s3.readObject("artifacts", "runs/example.json", { maxBytes: 65_536 });
  // prefix.truncated distinguishes a bounded prefix from the complete object.
} finally {
  await clients.dispose();
}

listObjects and listBuckets return one explicitly bounded page with continuation tokens; callers own traversal and total scan budgets. Bucket HEAD and versioning are also available. Errors preserve SDK status/code: a failed HEAD is not converted to exists=false. Reads stop at the byte cap and close the body; request deadlines include queueing and body consumption. No write operations, automatic retries or implicit region redirects are exposed.

For a mapped TCP transport, pass { key: "cluster/context identity", transport } as the third DataSource argument. The caller owns that transport's lifetime. Path-style addressing is required for mapped routes; direct endpoints also support virtual-hosted buckets. Signed Host and TLS identity remain the logical endpoint, not the forwarded address. Private CAs may be supplied explicitly with ca; certificate verification stays enabled. Initialization prepares the client and route without listing buckets or claiming that credentials have been validated.

Bounded MySQL reads

MysqlClient.database.queryReadonly(target, sql, values, { timeoutMs, maxRows, maxBytes }) uses the shared query slot and a disposable native session with NO_BACKSLASH_ESCAPES, a SELECT deadline, and a READ ONLY transaction. Prepared execution binds values and retains rows incrementally, returning rows, columns, bytes, truncated, and truncation. An extra row proves row truncation; a row exceeding the remaining byte budget is not retained.

Callers own SQL classification, function policy, and least-privilege identities: READ ONLY alone is not an arbitrary-SQL sandbox. This API requires MySQL 5.7.8+ max_execution_time and does not fall back to Pod Python. Completion, truncation, timeout, and cancellation destroy the disposable session without replay or changes to ordinary borrowed sessions. Disconnect does not prove immediate server cancellation. The byte budget limits retained JSON rows, not individual wire packets or fields.

Reusable bounded queries and read caches

For trusted, caller-owned SELECT statements, queryLimited(database, target, statement, maxRows) appends an integer LIMIT maxRows + 1 and returns { rows, truncated }. SQL must not already have LIMIT or a trailing terminator; the helper does not parse or rewrite arbitrary SQL. Business values stay bound parameters. limitedStatement and limitedRows expose the same preparation/projection for Database.queryBatch. maxRows must be a positive integer below 2147483647. These helpers bound row count only: use queryReadonly when a disposable readonly session and byte/time budgets are required.

DataLoader shares results, failures and in-flight work within one explicit read scope, similar to Guava's LoadingCache but scoped to a single collection round. Consumers must not mutate shared results. Use withReadScope to bind caller-approved database reads; do not use it for writes or polling.

import { DataLoader } from '@compforge/harness-toolbox/data-loader';
import { withReadScope, queryLimited } from '@compforge/harness-toolbox/mysql';

const scope = new DataLoader({
  maxEntries: 256, maxBytes: 4 * 1024 * 1024, signal: client.signal,
});
const reads = withReadScope(client.database, scope);
try {
  const page = await queryLimited(reads, client.target, {
    sql: 'SELECT id FROM items WHERE owner = ? ORDER BY id', values: [owner],
  }, 100);
} finally {
  await scope.close(); // Root still owns client.dispose().
}

Keys include full database identity, SQL and typed parameters as an opaque digest. Nonserializable parameters bypass caching. Batch reads reuse cached entries and send misses as one ordered native batch, or sequential queries when batching is unavailable. Failures are shared without replay; use a new scope for fresh observations. This is not a consistent database snapshot.

Entry capacity includes failures and pending reads; new keys bypass caching when full. Serialized result bytes bound retained results, excluding keys/errors and active operation memory. Oversized or nonserializable results are returned but not retained. Scope close signals cancellation and joins operations, including uncached reads. Operations must honor the signal for prompt cancellation; MySQL reads retain their underlying client's timeout/cancellation policy. Adapter close owns neither the scope nor the database. A waiter's optional signal to scope.read cancels only that waiter.

Shared Pod relay transport

PodRelayTransport opens TCP routes through an existing Running/Ready Pod. It discovers an eligible container in the selected namespace (optionally restricted by a label selector); the current implementation uses Python 3.8+ standard-library asyncio. Callers supply endpoints, not a Pod name or interpreter. It creates no Pods and installs no packages. The selected Pod must be able to resolve and reach each destination.

Create one transport in the root execution's ClientManager and lend it to all Service/protocol clients. Its key describes cluster access and relay policy, never a Service or database identity. Concurrent callers share Pod discovery, one relay process and one forward per host/port. Database credentials and connections remain owned by their protocol clients.

import { ClientManager } from '@compforge/harness-toolbox';
import { PodRelayTransport } from '@compforge/harness-toolbox/transport';

const clients = new ClientManager();
try {
  const relay = await clients.get({
    clientKey: 'transport:customer-cluster:app',
    createClient: (_clients, signal) => new PodRelayTransport({
      kubeconfig: '/config/cluster', context: 'customer', namespace: 'app',
      selector: 'diagnostic-relay=eligible',
      startupTimeoutMs: 5000, connectTimeoutMs: 5000,
      maxConnections: 16, maxTargets: 8, maxCandidatePods: 8, signal,
    }),
  });
  // The same instance belongs in each protocol client's ConnectionSource.transports.
  const address = await relay.connect({host: 'database.internal', port: 3306});
  // Connect the native protocol client to address; do not dispose relay in a Service.
} finally {
  await clients.dispose();
}

The host must authorize list pods, create pods/exec and create pods/portforward, including running a temporary process in the selected container. List/discovery and each readiness operation use startupTimeoutMs; candidate Pod count, target count and aggregate live relay connections are bounded separately. List pods is namespace scoped. Relay listeners bind only to Pod loopback and local forwards only to host loopback. Control messages contain destinations, never DB credentials.

Disposal closes forwards and the exec stream; EOF closes remote listeners/connections. If an exec connection is lost without EOF, a 30-second heartbeat lease bounds remote process lifetime. A dead relay invalidates its routes and fails callers; no SQL or other protocol operation is replayed. The root should finish or cancel protocol work before disposing the transport.

OpenSearch connection preparation

openOpenSearch probes the resolved endpoint before returning. It tries the supplied TCP transports in order only for connection failures; HTTP authorization/server errors stop preparation, and failed reads are never replayed on another route. onRoute reports the selected transport and safe fallback reason. The caller owns shared transports such as PodRelayTransport and their cleanup.

Routed HTTPS preserves the original TLS server name. Bun disables HTTP keep-alive because its reused socket recovery can replay a failed request below the driver; Node retains connection pooling.

Kubernetes compute evidence

kubernetes/compute exports collectClusterCompute and collectWorkloadCompute. They borrow ResourceAccess, use explicit deadlines/item/page budgets, and retain read coverage independently. Use client.clusterResources() for Nodes and all-namespace Pods; normal client methods retain their configured namespace. A forbidden cluster read records permission_denied; it does not block a separate namespace-scoped Workload read. See compute collection.

Connection acquisition deadlines

MySQL, OpenSearch and Redis use the same acquisition lifecycle. connectTimeoutMs includes route mapping and handshake/probe; optional acquireTimeoutMs bounds all routes together (default: per-route budget × route count). Cancellation preserves the caller reason and prevents late mappings from starting a handshake. Query and command timeouts remain separate, and failed operations are never replayed on another transport.