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

@anyformat/sdk

v0.2.3

Published

TypeScript SDK for the AnyFormat API

Readme

@anyformat/sdk

TypeScript SDK for the AnyFormat API. Mirrors the Python SDK's fluent flow.

import { Anyformat, Schema } from "@anyformat/sdk";

const af = new Anyformat({ apiKey: process.env.ANYFORMAT_API_KEY! });

const result = await af
  .workflow("Invoice")
  .parse()
  .extract([
    Schema.string("vendor", "Vendor name on the invoice."),
    Schema.float("total", "Grand total amount."),
    Schema.enum("currency", "Currency of the totals.", [
      Schema.option("USD", "US Dollar."),
      Schema.option("EUR", "Euro."),
    ]),
  ])
  .run(file)
  .wait();

result.field("vendor")?.value; // -> "Acme Corp"

Quick parse: one call → markdown

af.parse() runs the platform's atomic /parse operation (a fast lite parse) and returns an opaque jobId. Poll getParseMarkdown(jobId) for the markdown — no workflow to author or manage.

const jobId = await af.parse(file); // File | Blob
let markdown = await af.getParseMarkdown(jobId);
while (markdown === null) {
  await new Promise((r) => setTimeout(r, 3000));
  markdown = await af.getParseMarkdown(jobId); // null until done
}
console.log(markdown);

Managing workflows

const workflows = await af.listWorkflows({ pageSize: 50 }); // -> Workflow[]
for (const wf of workflows) console.log(wf.id, wf.name);

await af.deleteWorkflow(workflows[0].id); // -> the deleted id

Smart lookup

Resolve a field by matching the document against a reference file (CSV/catalog) instead of reading it off the page — flag the field with { lookup: true } and pass the file via lookupFiles:

await af
  .workflow("Invoice + vendor lookup")
  .parse()
  .extract(
    [
      Schema.string("vendor_name", "Vendor as printed on the invoice."),
      Schema.string("vendor_id", "Canonical vendor code from the catalog, joined on vendor_name.", { lookup: true }),
    ],
    { lookupFiles: ["vendor_catalog.csv"], lookupSuggestion: "Join vendor_name → vendor_id." },
  )
  .run(file)
  .wait();

Full runnable example: examples/smart-lookup.mjs.

How it's built

The wire types and the low-level HTTP client are generated from the API's OpenAPI spec with Hey API — they are not hand-written and must not be edited. Everything in src/sdk/ is generated; the hand-written ergonomic layer (Schema, WorkflowBuilder, Result, Anyformat) wraps it.

Regenerating (and the drift guard)

bun run gen          # dump the v2 OpenAPI offline + regenerate src/sdk
bun run gen:check    # regenerate and fail if the committed output drifted

gen:check runs in CI and as a pre-commit hook (scoped to changes under anyformat/services/api and anyformat/workflow) so the committed SDK can never fall behind the backend. The SDK's own tsc then fails to compile if a backend model change alters a type the hand-written layer relies on — drift is caught as a type error, not just a diff.

Scripts

Install deps with bun install --frozen-lockfile (matches CI + the frontend toolchain). Then:

| script | what | | --- | --- | | bun run gen | dump spec + regenerate src/sdk | | bun run gen:check | regenerate + assert no drift | | bun run build | bundle to dist/ (ESM + CJS + types) via tsup | | bun run typecheck | tsc --noEmit | | bun run test | vitest |

Releasing

Cut on demand — a maintainer bumps the version in a PR, then pushes a sdk-js-vX.Y.Z tag on the release commit. Full flow + one-time setup: ../RELEASING.md. Break-glass: ./publish.sh [--build-only|--dry-run].

Node configuration knobs

Every knob is an option on .parse({...}) / .extract(fields, {...}). parse options are a mode-discriminated union, so a wrong-mode knob is a compile-time error.

.parse({...})mode ("standard"/"agentic"/"lite"), promptHint, figureEnhancement, cache; agentic-only effort ("low"/"mid"/"accurate"); lite-only ocrEffort ("medium"/"high"), region ("eu"/"global"), skipRoutingReview, figures, textFormatting, routingEffort.

.extract(fields, {...})mode ("standard"/"agentic"/"max"/"lite"), useImages, lookupSuggestion, lookupReasoningEffort ("minimal"/"low"/"medium"/"high"), lookupFiles.