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

@passband.ai/sdk

v0.1.0

Published

Typed TypeScript client for the Passband /api/v1 REST API.

Readme

@passband.ai/sdk

Typed TypeScript client for the Passband /api/v1 REST API.

The client is a thin, dependency-free wrapper over fetch. All request and response shapes are generated from the API's OpenAPI document, so the SDK cannot drift from the server.

Install

npm install @passband.ai/sdk
# or: pnpm add @passband.ai/sdk

Requires Node.js >= 18 (uses the global fetch). Ships ESM + CJS builds and types.

Usage

import { Passband } from '@passband.ai/sdk'

const pb = new Passband({ token: 'sf_...' })

// Run the content pipeline
await pb.pipeline.run()

// Review the ranked feed
const { drafts } = await pb.drafts.list({ status: 'pending_review' })

// Approve the first draft, if any
const first = drafts?.[0]
if (first) await pb.drafts.approve(first.id)

// Manage sources
await pb.sources.add({ name: 'HN', type: 'hackernews', url: 'https://news.ycombinator.com' })

Zero-config

With no options, the client reads PASSBAND_TOKEN (and PASSBAND_BASE_URL) from the environment:

// reads PASSBAND_TOKEN from process.env
const pb = new Passband()
const summary = await pb.engagement.summary()

Constructing without a token is allowed, but the first request throws PassbandAuthError — the SDK never sends an unauthenticated request.

Configuration

Constructor options take precedence over environment variables:

| Option | Env var | Default | | --------- | ------------------- | --------------------- | | token | PASSBAND_TOKEN | — | | baseUrl | PASSBAND_BASE_URL | https://passband.ai | | fetch | — | global fetch | | retry | — | disabled |

The token is sent as Authorization: Bearer <token>.

Pagination

List endpoints are cursor-paginated. list() returns a single page (with a nextCursor). For drafts, drafts.iterate() returns an async iterator that transparently follows nextCursor:

for await (const draft of pb.drafts.iterate({ status: 'pending_review' })) {
  console.log(draft.id)
}

Retries

Retries are opt-in. When enabled, the client retries 429 (honoring Retry-After) and 5xx responses with exponential backoff and full jitter. Other 4xx responses are never retried.

By default only idempotent methods are retried (GET/PUT/DELETE/PATCH). POST is not retried, to avoid duplicating side effects. If your POST endpoints are idempotent, opt in with retryNonIdempotent: true.

const pb = new Passband({
  token: 'sf_...',
  retry: { retries: 3, backoff: 250, retryNonIdempotent: false },
})

Cancellation via AbortSignal is never retried: the original AbortError (or your signal.reason) is re-thrown unchanged.

Errors

Every non-2xx response throws a typed error: PassbandAuthError (401), PassbandForbiddenError (403), PassbandNotFoundError (404), PassbandRateLimitError (429, with retryAfter), and PassbandServerError (5xx). Network and parse failures throw PassbandNetworkError. All extend PassbandError with status, code, message, body, and requestId.

import {
  Passband,
  PassbandRateLimitError,
  PassbandNotFoundError,
  PassbandError,
} from '@passband.ai/sdk'

const pb = new Passband({ token: 'sf_...' })

try {
  await pb.drafts.approve('draft_123')
} catch (err) {
  if (err instanceof PassbandRateLimitError) {
    console.warn(`Rate limited; retry after ${err.retryAfter}s`)
  } else if (err instanceof PassbandNotFoundError) {
    console.warn('Draft not found')
  } else if (err instanceof PassbandError) {
    console.error(err.status, err.code, err.message, err.requestId, err.body)
  } else {
    throw err
  }
}

Resources

| Namespace | Methods | | ---------------- | ----------------------------------------------------------------------- | | pb.pipeline | run, status(runId), stats, runs.list | | pb.drafts | list, iterate, get, create, update, approve, post, bulk | | pb.sources | list, add, update, remove, test | | pb.voice | get, update | | pb.engagement | summary, upsert | | pb.experiments | list, create |

pipeline.status(runId) has no dedicated API route — it performs an O(n) linear scan of pipeline.runs.list (capped at the most recent ~2000 runs) and throws PassbandNotFoundError when the run isn't found. Prefer paging pipeline.runs.list directly when you already know the run is recent.

Examples

Runnable end-to-end examples live in examples/ — including agent-flow.ts, the canonical loop (run pipeline → ranked feed → approve drafts → add sources). Each reads PASSBAND_TOKEN from the environment; see examples/README.md.

Docs

Generate the static API + REST reference (typedoc + Redocly) into docs/:

pnpm --filter @passband.ai/sdk run docs   # use `run docs` — bare `pnpm docs` is intercepted by pnpm

Development

pnpm --filter @passband.ai/sdk generate   # regenerate openapi.json + types
pnpm --filter @passband.ai/sdk build      # tsup → ESM + CJS + d.ts
pnpm --filter @passband.ai/sdk test       # vitest (unit + OpenAPI contract)

Types are generated from app/api/v1/docs/openapi.ts (the single source of truth, also served at /api/v1/docs). Run generate after changing the API.