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

typesafe-ish

v0.1.0

Published

TypeSafe-compatible question and answer client backed by AI SDK structured output

Readme

typesafe-ish

A TypeSafe-compatible question/answer client backed by AI SDK structured output. The current target is @typesafe-ai/sdk 0.6.0 and AI SDK 7. systemOne() returns validated answers through the official APIPromise. models.list() discovers provider models as official ModelCard objects.

The package supports Node.js 22.18+ and Bun, with ESM, CommonJS, and TypeScript declarations. TypeScript is a development dependency, not a required consumer peer. Node and JSON Schema typings required by AI SDK are included as dependencies. The generated declarations load Node typings explicitly, so consumers can keep types: [] and full declaration checking.

bun install --frozen-lockfile
bun run check       # Lint, types, tests, build, and package smoke checks
bun run build       # JavaScript and declarations in dist/
bun run test:package # Build and check Node imports and declarations
bun run lint        # Lint source, tests, and examples
bun run format      # Format with Biome
bun run test:compat # Official SDK export, resource, and type parity

Packaging and release

bun run check verifies the built package's import and require entry points, checks their authentication error contracts, and compiles usage examples against its ESM and CommonJS declarations. These smoke checks use the local build without packing, installing, or making network requests. CI runs this gate on Node.js 22.18 and 24.

npm pack builds the package automatically. Only dist/, README.md, LICENSE, and package.json are included. For a local install before publication:

npm pack
# In your application, using the path printed by npm pack:
npm install /path/to/typesafe-ish-0.1.0.tgz

npm publish runs the complete check before publishing. Building and checking the package do not publish it. The behavioral limitations below remain outside the MVP release gate.

Usage contract

import { TypeSafeClient, choice } from "typesafe-ish";
import type { TypeSafeClientConfig } from "@typesafe-ai/sdk";

// For example: { defaultModel: "openai:gpt-6-luna", apiKey: "..." }
async function classify(config: TypeSafeClientConfig) {
  const client = new TypeSafeClient(config);
  const response = await client.systemOne({
    state: { document: "I was charged twice." },
    questions: {
      category: choice("What is this about?", {
        billing: null,
        technical: null,
        other: null,
      }),
    },
  });
  return response.answers.category.choice; // "billing" | "technical" | "other"
}

examples/basic.ts covers all three question primitives.

Cancellation

Pass the native SDK's RequestOptions.signal as the second argument to systemOne, or the options argument to models.list:

await client.systemOne(request, { signal: AbortSignal.timeout(10_000) });
await client.models.list({ signal: AbortSignal.timeout(10_000) });

Use an AbortController's signal for manual cancellation. Cancellation covers provider requests, response body reads, inference retry waits, and model discovery pagination. Already-aborted signals reject before fetching. Caller cancellation rejects with the official APIUserAbortError, retaining the underlying failure as cause; asResponse() and withResponse() share that error. Custom fetch implementations must honor the supplied signal. An external timeout signal also counts as caller cancellation; the SDK's timeout option remains ignored.

Optional reasoning level

Prefer omitting reasoning settings: typesafe-ish leaves them unset so AI SDK and the selected provider handle the default. To opt in for a particular request:

import { TypeSafeClient, noul } from "typesafe-ish";

const client = new TypeSafeClient({ defaultModel: "openai:gpt-6-luna" });
const request = {
  state: "The invoice was paid yesterday.",
  questions: { paid: noul("Has the invoice been paid?") },
};

await client.systemOne(request); // Uses the provider's default reasoning.
await client.systemOne({ ...request, reasoning: "high" }); // Explicit override.

For a deliberate client-wide setting, pass defaultReasoning in the constructor. request.reasoning overrides it; an omitted or undefined request value inherits it. Use reasoning: "provider-default" to bypass a client default for one call. "none" requests disabled reasoning where supported; it does not mean default.

Reasoning settings use AI SDK's LanguageModelCallOptions["reasoning"] type directly. Accepted levels follow the installed SDK's reasoning option. AI SDK handles provider/model support and mapping. The installed OpenRouter adapter needs a compatibility bridge: explicit levels are forwarded as reasoning.effort, while omission and "provider-default" leave that field unset. Actual support still depends on the selected model.

Import TypeSafeClientConfig and SystemOneRequest from typesafe-ish when using these optional extensions. Existing configurations and requests typed with @typesafe-ai/sdk remain accepted, and answer inference is preserved.

Structure

src/
  index.ts                       Package entry point; public exports and SDK helpers
  types.ts                       Compatible config/request extensions
  client.ts                      TypeSafeClient facade
  resources/models.ts            models.list() facade
  providers/resolve-model.ts     Built-in provider instances and model overrides
  system-one/
    prompt.ts                    State/question prompt and typed answer schema
    execute.ts                   AI SDK structured-output execution
    result.ts                    Answer validation and usage mapping
examples/basic.ts                Typechecked usage with choice, noul and score
tsdown.config.ts                 ESM/CommonJS and declaration build
tests/
  prompt.test.ts                 Prompt serialization and answer schema
  result.test.ts                 Answer, confidence, rubric, and usage mapping
  errors.test.ts                 Inference errors and response-helper rejection contracts
  abort.test.ts                  Cancellation, retry waits, pagination, and response helpers
  reasoning.test.ts              Reasoning defaults, overrides, and provider request bodies
  compatibility.test.ts          Official SDK exports and native Models resource
  compatibility.types.ts         Official SDK compatibility and shared type parity
  package/                       Node import and declaration smoke checks

Implementation boundaries

Question types, result types, and the choice, noul, and score helpers come from the official SDK. Configuration and request types extend the official types with optional reasoning settings. Set defaultModel in the client configuration or model in the request. No model is selected implicitly. AI SDK model instances and custom resolveModel callbacks are not client options.

An AI SDK provider registry resolves anthropic:<model-id>, google:<model-id>, openai:<model-id>, xai:<model-id>, openrouter:<model-id>, deepseek:<model-id>, and gateway:<creator>/<model-id> without local model allowlists. Non-gateway IDs call providers directly. Model IDs can include colons, such as openrouter:nvidia/nemotron-3-ultra-550b-a55b:free. Credentials come from apiKey or the provider's environment variable (ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, OPENAI_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY, DEEPSEEK_API_KEY, or AI_GATEWAY_API_KEY), in that order. Providers validate credentials and model availability when making requests. Malformed IDs and unregistered providers produce AI SDK registry errors.

The pipeline is resolve model → build prompt and Zod schema → generateText with Output.object → validate/map answers and usage → adapt to APIPromise. The schema is passed directly to the AI SDK, not embedded in the prompt.

Choice and score probabilities must sum to one within 1e-6. Choice labels must agree with the highest probability. Confidence, expected scores, and rubric legends are computed locally. Confidence follows the official TypeSafe formulas: choice uses the top probability relative to an even split; score uses the probability-weighted distance from the most likely level, normalized by the uniform distribution's spread. Confidence is excluded from the LLM prompt and output schema. Noul answers have no confidence field. Missing input or output token counts cause an error.

Response helpers expose a synthesized HTTP 200 response containing normalized result JSON, not the provider's raw response. Provider request IDs are not fabricated. Structured-output and provider errors reject the result and helpers.

Inference translates AI SDK APICallError failures with status 400 or higher into the corresponding official APIError subclass. This also applies when a RetryError ends with such an APICallError. The translated error retains status, headers, the provider's parsed error data (or raw response body), and the original error as cause. Caller cancellation rejects with APIUserAbortError; other errors, including transport, structured-output, and Gateway-specific errors, bubble unchanged.

The client's fetch and baseURL settings apply to both inference and model discovery. Custom endpoints must support the selected provider's API. The official configuration and request-option types are accepted. Request-option signal is supported; custom headers, timeout, and retry settings are currently ignored. Score rubrics must be arrays with at least two entries; invalid rubrics throw TypeSafeError before model resolution.

Model discovery

Models is re-exported directly from the official SDK. client.models is a native SDK resource whose list() method is replaced with provider discovery. The resource is created through an SDK client with its TypeSafe API transport disabled.

models.list() queries the provider selected by defaultModel, or the public AI Gateway catalog when no default is set. Card names are namespaced model IDs that can be passed to systemOne. Anthropic and Google pagination is followed. Missing metadata is an empty string; release_date uses the catalog's creation date when available, not a verified model release date.

Discovery uses provider HTTP catalogs because AI SDK providers do not expose a common listing method. The public Gateway catalog also includes creation dates that gateway.getAvailableModels() omits. Discovery uses the configured fetch (or global fetch by default) and honors baseURL and request-option signal. Custom headers, timeout and retry settings are ignored. Response helpers expose normalized { models: [...] } JSON.

Tests

The suite checks prompt construction and result mapping, plus export, resource, and type compatibility with the installed official SDK. Reasoning tests capture provider requests to check defaults, overrides, and OpenRouter compatibility. Error tests use custom fetch responses to check HTTP error translation, retries, response helpers, and unchanged propagation of unrelated errors. Live inference and catalog discovery are outside this suite.

Live provider evaluations should be a separate, explicitly enabled suite. Compare decisions and schema conformance; predictions, probabilities, confidence, usage and model metadata are not expected to be identical across models. Returning compatible numeric fields will not by itself establish TypeSafe-style probability calibration.

References