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

nace-sdk

v0.1.0

Published

TypeScript SDK for the Drex API: calibrated decisions and document jobs

Readme

nace-sdk (TypeScript)

The official TypeScript client for the Drex API: calibrated answers to typed questions (POST /v1/systemone) and document jobs — parse, split, classify, extract and ground.

The client is built on the TypeSafe AI SDK (MIT; see THIRD_PARTY_NOTICES.md): systemOne, noul, choice, score, the inferred answer types, RetryPolicy and the error classes match @typesafe-ai/sdk, so TypeSafeClient code runs on NaceClient after renaming the import.

npm install nace-sdk
export NACE_API_KEY=nace_sk_...

Node 20 or later. Create a key on the API keys page and keep it on a server: it spends your account's credit.

Decisions

import { choice, NaceClient, noul, score } from "nace-sdk";

const client = new NaceClient(); // reads NACE_API_KEY, NACE_BASE_URL, NACE_DEFAULT_DECISION_MODEL, NACE_LOG_LEVEL

const { answers } = await client.systemOne({
  state: "I was charged twice for my March invoice.",
  questions: {
    wants_refund: noul("Is the customer asking for a refund?"),
    topic: choice("Which topic is it?", { billing: null, shipping: null, other: null }),
    urgency: score("How urgent is it?", ["low", "medium", "high"]),
  },
});

answers.wants_refund.noul; // number
answers.topic.choice; // "billing" | "shipping" | "other"

Documents

import { readFile } from "node:fs/promises";
import { workspaceFile } from "nace-sdk";

// An https:// URL whose last path segment names the file: its extension picks the parser.
const parsed = await client.documents.parse({
  source: "https://example.com/invoice.pdf",
  output: { formats: ["markdown"] },
});
const job = await client.jobs.wait(parsed.job_id);

// A URL that does not end in a file name needs a file_name.
await client.documents.parse({
  source: { type: "url", url: "https://arxiv.org/pdf/1706.03762", file_name: "attention.pdf" },
});

const file = await client.documents.upload(await readFile("invoice.pdf"), {
  file_name: "invoice.pdf",
  on_conflict: "new_version", // replace the file at that path instead of failing with path_conflict
});
const extracted = await client.documents.extract({
  source: workspaceFile(file),
  schema: { type: "object", properties: { total: { type: "number" } } },
  wait_seconds: 60,
});

// Or save the schema once and name it.
const saved = await client.extractionSchemas.create({
  name: "Invoice",
  schema: { type: "object", properties: { total: { type: "number" } } },
});
await client.documents.extract({ source: workspaceFile(file), schema_id: saved.schema_id });

Uploads go straight to the document service with a one-use token, never with your API key or the SDK's headers; files of 32 MiB and more go up in parts. A different file already at the path fails with path_conflict unless you pass on_conflict: "new_version": a ConflictError (409) for a file sent whole, a NaceError naming the failed upload job for one sent in parts. One sent in parts that is still assembling after ten minutes throws a NaceError: upload the same file to the same path again later to get it. To run a resumable upload yourself, use createUploadSession, uploadPart, completeUploadSession, getUploadSession (which parts landed) and abortUploadSession; createUploadGrant mints a token for another client to upload with. Every document job and upload session create carries an Idempotency-Key, so retries never start a second job or session. The /v1 routes send no Access-Control-Allow-Origin header, so a browser can't call them: keep the client on your server, let a browser upload with a grant your server mints, and hand it signed links from jobs.fileLink.

client.jobs has get, list, iter, wait, delete, events, getRequest (the request a job ran under), rows (a parsed sheet's rows, a page at a time), fileLink (a signed link to a stored file), and fetchFile and download for any job file, including full Markdown that is still being prepared. client.extractionSchemas has list, iter, get, create, listVersions and iterVersions (oldest first), getVersion and createVersion; creates are not retried unless you pass retry, since a retry could save a second schema.

Errors and retries

Every failure throws a NaceError; HTTP failures are APIError subclasses (BadRequestError, AuthenticationError, InsufficientCreditError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, OverloadedError) with status, body, requestId, type, code, issues, detail and serverRetryable. jobs.wait throws JobFailedError or JobTimeoutError. The client retries 408, 429, 5xx, dropped connections and timeouts (2 retries within a 30 s budget, 500 ms doubling to 5 s, honoring retry-after-ms), except an error the server marks "retryable": false. The budget counts from the first attempt, so with the defaults an attempt that fails after 30 s, such as one that hit the 60 s timeout, is not retried: raise retry.timeoutMs above your attempts and waits, or pass null. jobs.events is not retried, and saved-schema creates aren't unless you pass retry.

// Up to 5 attempts of up to 90 s each, and the waits between them.
const client = new NaceClient({ timeout: 90_000, retry: { maxRetries: 4, timeoutMs: 500_000 } });

logLevel: "debug" (or NACE_LOG_LEVEL=debug) logs headers and bodies, with the API key and upload tokens redacted.

Full guide, for decisions and documents: TypeScript SDK.

License

Apache-2.0