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

@spectratools/cli-shared

v0.4.1

Published

Shared middleware, utilities, and testing helpers for spectra CLI tools

Downloads

529

Readme

@spectra-the-bot/cli-shared

Shared middleware, utilities, and testing helpers used across @spectra-the-bot CLI packages.

Installation

pnpm add @spectra-the-bot/cli-shared

Import paths

import { ... } from '@spectra-the-bot/cli-shared';
import { ... } from '@spectra-the-bot/cli-shared/middleware';
import { ... } from '@spectra-the-bot/cli-shared/utils';
import { ... } from '@spectra-the-bot/cli-shared/testing';

Middleware

apiKeyAuth + MissingApiKeyError (auth)

Reads an API key from an env var and throws a typed error if missing.

import { apiKeyAuth, MissingApiKeyError } from '@spectra-the-bot/cli-shared/middleware';

try {
  const { apiKey } = apiKeyAuth('ETHERSCAN_API_KEY');
  // use apiKey in request headers
} catch (err) {
  if (err instanceof MissingApiKeyError) {
    console.error(err.message);
  }
}

withRetry (retry)

Wraps async calls with exponential backoff + jitter.

  • Retries up to maxRetries
  • Delay grows from baseMs to maxMs
  • For HttpError 429/503, respects Retry-After headers
import { withRetry } from '@spectra-the-bot/cli-shared/middleware';

const result = await withRetry(
  () => fetch('https://example.com/data').then((r) => r.json()),
  {
    maxRetries: 4,
    baseMs: 200,
    maxMs: 5_000,
  },
);

createRateLimiter + withRateLimit (rate-limit)

Token-bucket rate limiter for request throughput control.

import { createRateLimiter, withRateLimit } from '@spectra-the-bot/cli-shared/middleware';

const acquire = createRateLimiter({ requestsPerSecond: 5 });

const data = await withRateLimit(
  () => fetch('https://example.com/items').then((r) => r.json()),
  acquire,
);

paginateCursor + paginateOffset (pagination)

Async iterators that flatten paged APIs.

Cursor pagination

import { paginateCursor } from '@spectra-the-bot/cli-shared/middleware';

for await (const item of paginateCursor({
  fetchPage: async (cursor) => {
    const res = await fetch(`/api/items?cursor=${cursor ?? ''}`).then((r) => r.json());
    return { items: res.items, nextCursor: res.nextCursor };
  },
})) {
  console.log(item);
}

Offset pagination

import { paginateOffset } from '@spectra-the-bot/cli-shared/middleware';

for await (const item of paginateOffset({
  limit: 100,
  fetchPage: async (offset, limit) => {
    const res = await fetch(`/api/items?offset=${offset}&limit=${limit}`).then((r) => r.json());
    return { items: res.items, total: res.total };
  },
})) {
  console.log(item);
}

Utils

createHttpClient + HttpError (http client)

Typed fetch wrapper with:

  • baseUrl
  • default and per-request headers
  • query serialization
  • JSON request body encoding
  • HttpError on non-2xx responses
import { createHttpClient, HttpError } from '@spectra-the-bot/cli-shared/utils';

type Proposal = { id: string; title: string };

const client = createHttpClient({
  baseUrl: 'https://api.assembly.abs.xyz',
  defaultHeaders: {
    'X-Api-Key': process.env.ASSEMBLY_API_KEY ?? '',
  },
});

try {
  const proposals = await client.request<Proposal[]>('/v1/proposals', {
    query: { status: 'active' },
  });
  console.log(proposals);
} catch (err) {
  if (err instanceof HttpError) {
    console.error(err.status, err.statusText, err.body);
  }
}

Format helpers (weiToEth, checksumAddress, formatTimestamp, truncate) (format)

import {
  weiToEth,
  checksumAddress,
  formatTimestamp,
  truncate,
} from '@spectra-the-bot/cli-shared/utils';

weiToEth('1234500000000000000');
// => "1.2345"

checksumAddress('0x742d35cc6634c0532925a3b844bc454e4438f44e');
// => EIP-55 checksummed address

formatTimestamp(1700000000);
// => "2023-11-14T22:13:20.000Z"

truncate('0x742d35cc6634c0532925a3b844bc454e4438f44e');
// => "0x742d...f44e"

Testing

createMockServer (mock-server)

Spin up a lightweight local HTTP server for integration tests.

  • Configure route responses (addRoute)
  • Record incoming requests (requests)
  • Cleanly close after tests (close)
import { createMockServer } from '@spectra-the-bot/cli-shared/testing';

const server = await createMockServer();

server.addRoute('GET', '/v1/proposals', {
  status: 200,
  body: [{ id: '1', title: 'Test Proposal' }],
});

const res = await fetch(`${server.url}/v1/proposals`);
const data = await res.json();

console.log(data);
console.log(server.requests[0]); // method/url/headers/body

await server.close();

Telemetry (OpenTelemetry)

Built-in OpenTelemetry instrumentation for distributed tracing and metrics. Zero overhead when disabled — OTEL modules are lazy-loaded only when telemetry is active.

Initialize telemetry

import { initTelemetry, shutdownTelemetry } from '@spectra-the-bot/cli-shared/telemetry';

// Initialize at CLI startup — no-op if OTEL env vars are not set
initTelemetry('my-cli');

// Shut down gracefully at exit — safe to call even if never initialized
await shutdownTelemetry();

Telemetry is enabled when OTEL_EXPORTER_OTLP_ENDPOINT is set or SPECTRA_OTEL_ENABLED=true.

Command spans

import { createCommandSpan, withCommandSpan } from '@spectra-the-bot/cli-shared/telemetry';

// Option 1: Manual span management
const span = createCommandSpan('account balance', { address: '0x...', format: 'json' });
try {
  // ... execute command
  span.setStatus({ code: SpanStatusCode.OK });
} catch (err) {
  recordError(span, err);
  throw err;
} finally {
  span.end();
}

// Option 2: Automatic span lifecycle
const result = await withCommandSpan('account balance', { address: '0x...' }, async () => {
  // ... execute command
  return data;
});

Child spans

import { withSpan } from '@spectra-the-bot/cli-shared/telemetry';

const result = await withSpan('parse-response', async (span) => {
  // span is automatically ended and errors are recorded
  return parseData(raw);
});

HTTP spans (automatic)

The createHttpClient utility automatically creates child spans for every HTTP request when OTEL is active:

import { createHttpClient } from '@spectra-the-bot/cli-shared/utils';

const client = createHttpClient({ baseUrl: 'https://api.example.com' });

// This request automatically creates an HTTP span with method, URL, and status code
const data = await client.request('/v1/items');

Attribute sanitization

Span attributes are automatically sanitized — keys or values matching sensitive patterns (private keys, passwords, API keys, mnemonics, tokens, secrets) are stripped before being attached to spans.

import { sanitizeAttributes } from '@spectra-the-bot/cli-shared/telemetry';

const safe = sanitizeAttributes({
  address: '0x1234',      // ✓ kept
  private_key: '0xdead',  // ✗ stripped
  api_key: 'sk-secret',   // ✗ stripped
  format: 'json',         // ✓ kept
});
// => { address: '0x1234', format: 'json' }

Export summary

  • Middleware: apiKeyAuth, MissingApiKeyError, withRetry, createRateLimiter, withRateLimit, paginateCursor, paginateOffset
  • Utils: createHttpClient, HttpError, weiToEth, checksumAddress, formatTimestamp, truncate
  • Telemetry: initTelemetry, shutdownTelemetry, createCommandSpan, withCommandSpan, withSpan, recordError, sanitizeAttributes, createHttpSpan, endHttpSpan, endHttpSpanWithError, extractPath
  • Testing: createMockServer