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

@davecthomas/ai-api-unified-http-client

v1.6.0

Published

Typed TypeScript client for ai-api-unified-http, generated from its OpenAPI spec.

Readme

@davecthomas/ai-api-unified-http-client

TypeScript client for ai-api-unified-http.

The service describes itself at /openapi.json: every endpoint, every field you can send, every field that comes back. A tool reads that description and writes src/schema.ts from it. Nobody types those shapes by hand, which is what "generated" means here.

The editor then knows the field names, so a typo or a missing required field is an error before the code runs. Without it, the same mistake reaches the server and comes back as a 422.

client.raw.POST("/v1/completions", { body: { engine: "claude", promt: "hi" } });
//                                                             ^^^^^ caught here

CI regenerates the file on every pull request and fails when the committed version has moved, so the client cannot fall behind the service it describes.

Install

The package ships TypeScript source, so a bundler compiles it with your app.

npm install github:davecthomas/ai-api-unified-http#main

Use

import { createAiApiClient } from "@davecthomas/ai-api-unified-http-client";

const client = createAiApiClient({
  baseUrl: "https://your-service.run.app",
  apiKey: process.env.API_KEY!,
  caller: { callerId: "user-42" },   // splits provider spend per end user
});

const { data, error } = await client.raw.POST("/v1/completions", {
  body: { engine: "claude", prompt: "Name three primary colors." },
});

Paths, bodies, and responses are checked against the generated schema, so a wrong field name or a missing required property is a compile error.

Streaming

Server-sent events are outside what OpenAPI describes, so this part is hand-written:

for await (const chunk of client.streamCompletion({
  engine: "claude",
  prompt: "Count to five.",
})) {
  process.stdout.write(chunk);
}

A stream that fails mid-flight throws. The service sends a terminal error event even though the response began with 200 — the status line was already sent when the failure happened — and this client raises it so a broken stream cannot read as a short one.

Cost attribution

callerId, sessionId, and workflowId become X-Caller-Id, X-Session-Id, and X-Workflow-Id, which the service records against each call's cost. Pass them once when constructing the client, or per call:

await client.raw.POST("/v1/completions", {
  body: { engine: "claude", prompt: "..." },
  headers: client.headersFor({ callerId: "user-99" }),
});

Publishing

Authentication is npm Trusted Publishing over OIDC, so this repository holds no npm token. GitHub mints a short-lived credential scoped to one workflow, and npm records the publisher as GitHub Actions.

Version 1.0.0 went up by hand, because trusted publishing is configured on a package's own settings page and that page does not exist until the package does. That is no longer true, so configure it once at npmjs.com/package/@davecthomas/ai-api-unified-http-client/access:

| Field | Value | |---|---| | Organization or user | davecthomas | | Repository | ai-api-unified-http | | Workflow filename | publish-client.yml | | Allowed actions | npm publish |

Until that is set, a tag-triggered publish fails on authentication rather than falling back to anything.

Every publish after that is a tag:

# bump version in package.json first, then
git tag client-v1.1.0 && git push origin client-v1.1.0

The workflow refuses to publish when the tag and package.json disagree, so a tag cannot claim a version the package does not carry.

Your npm account email becomes public when you publish. It appears in the registry's maintainers field, which anyone can read. Trusted publishing keeps your address out of the per-version publisher record, and does nothing about maintainers. Use an address you are willing to make public.

Regenerate

From the repository root, after changing the service's shapes:

make client

That dumps the spec from the app object, regenerates the types, and typechecks. Commit the result; CI compares against it.

Live check

With the service running:

npm run smoke                                  # localhost:8080, local-dev-key
API_BASE=https://... API_KEY=... npm run smoke

It calls health, a completion, a token count, the model catalog, and a stream, because only a real round trip proves the generated types and the wire format agree.