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

@skhema/sdk

v0.2.4

Published

Thin typed client for the Skhema Public API (api.skhema.com/v1)

Readme

@skhema/sdk

A thin, typed, zero-runtime-dependency client for the Skhema Public API (https://api.skhema.com/v1). One method per gateway route, transparent API-key exchange, and a small, predictable error taxonomy.

Looking for the terminal experience instead? The same API is available as the @skhema/cli skhema command.

Install

npm i @skhema/sdk

Requires Node ≥ 18 (uses the global fetch).

Quickstart

import { createSkhemaClient } from '@skhema/sdk'

const skhema = createSkhemaClient({ apiKey: process.env.SKHEMA_API_KEY! })

// The organization is baked into your key — you never pass an org id.
const { workspaces } = await skhema.workspaces.list<{
  workspaces: { id: string; name: string }[]
}>()

for (const ws of workspaces) {
  console.log(ws.id, ws.name)
}

Credential modes

Pass exactly one credential to createSkhemaClient:

| Mode | Pass | Behaviour | Use for | | -------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | API key | { apiKey: 'sk_live_…' } | Exchanged for a 1-hour JWT lazily on the first call; cached and re-exchanged transparently 60 s before expiry; a mid-window 401 forces one re-exchange + retry. | CI, agents, headless scripts | | User JWT | { token: '<jwt>' } | Passed through untouched; never refreshed. A 401 throws immediately. | An app that already holds a user OAuth token (e.g. the Skhema CLI) |

// API key (CI / agents)
const skhema = createSkhemaClient({ apiKey: 'sk_live_…' })

// Pre-obtained user JWT (interactive session)
const skhema = createSkhemaClient({ token: userJwt })

// Overrides (staging/preview or tests)
const skhema = createSkhemaClient(
  { apiKey: 'sk_live_…' },
  {
    baseUrl: 'https://api.staging.skhema.com',
    authBaseUrl: 'https://auth.staging.skhema.com',
  }
)

skhema.getContext() returns { organizationId, permission } observed at the last API-key exchange (empty in token mode — the org is inside your JWT).

Resource groups

Every method maps to exactly one gateway route.

  • workspaces — CRUD, members (add/remove/role), and compliance (list/create/update/delete, matrix, member completion)
  • elements — CRUD, validate / rephrase / fork, and elements.resources (list, upload-url, confirm, download-url, delete). elements.create takes an optional componentId to target a specific component instance (list ids via components.list); omitted, the element lands in the first instance of its componentType by position (auto-created if none exists)
  • links — element links (create/update/delete)
  • components — CRUD, duplicate / reorder / export, and components.links (component-to-component links)
  • strategies — list/get/create/update, commit / activate / export
  • exportsexports.workspace(...) full-portability export (xlsx/csv/json)
  • webhooks — create/list/delete subscriptions
  • membersmembers.list() organization members
  • metahealth(), openapi()
  • imports — proposal-first URL/file import sessions (create, list, get, confirmUpload, updateDecisions, apply, discard, retry)

Request and response shapes

The SDK is a thin pass-through: the gateway forwards each domain edge function's JSON unchanged, so every method returns that raw shape typed as T (default unknown). Supply the concrete type at the call site:

const res = await skhema.elements.list<{ elements: Element[] }>('ws_1')

Write methods take a plain JSON body; well-known fields are documented in each method's JSDoc. For full payloads, pass the object straight through.

Error handling

Every failed call throws a SkhemaApiError (or a subclass), each carrying status, code, message, and requestId (from x-request-id).

| Class | Status | Notes | | ------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | AuthError | 401 | Missing/malformed/revoked key, or JWT rejected after re-exchange. Not retried. Also thrown client-side for a key without the sk_live_ prefix. | | WriteLockedError | 402 | Org can't write (trial expired / past due / cancelled / no plan). Carries state and upgradeUrl. Reads still work. | | RateLimitedError | 429 | Per-minute budget exhausted. Carries retryAfter (seconds). Idempotent GETs are retried once automatically; writes throw. | | SkhemaApiError | other | Base class for every other non-2xx (not_found, validation_error, …). |

import { RateLimitedError, WriteLockedError } from '@skhema/sdk'

try {
  await skhema.workspaces.create({ name: 'Q3 plan' })
} catch (err) {
  if (err instanceof WriteLockedError)
    console.error('Upgrade at', err.upgradeUrl)
  else if (err instanceof RateLimitedError)
    console.error('Retry after', err.retryAfter, 's')
  else throw err
}

Which client do I want?

| Package | Purpose | | ------------------------ | ------------------------------------------------------------------------------------------------------------ | | @skhema/sdk (this) | The Skhema Public API over api.skhema.com/v1. API-key or user-JWT auth. For your code, connectors, and CI. | | @skhema/cli | The same API as a terminal command (skhema), plus agent onboarding (skhema init). | | @skhema/agent-sdk | Registered-agent enrollment and request signing for the Skhema agent platform. Not a data-access client. |

The API surface is self-describing: GET https://api.skhema.com/v1/openapi.json (also available as skhema.meta.openapi()).

License

MIT