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

@omelhorsite/sdk

v0.18.0

Published

TypeScript SDK for the omelhorsite API. Isolate-safe: no node builtins, no environment access, no stdout.

Readme

@omelhorsite/sdk

npm

TypeScript client for the omelhorsite API. Works in browsers, Bun, Node 18+, React Native and Cloudflare Workers.

bun add @omelhorsite/sdk
import { Oms } from "@omelhorsite/sdk";

const oms = new Oms({ token: "..." });

const me = await oms.account.me();
const link = await oms.shortLinks.create({ url: "https://example.com" });

Client

const oms = new Oms({
  token: "...",                     // omit for anonymous access
  baseUrl: "http://localhost:3000", // default: https://backend.omelhorsite.pt
  fetch: myFetch,                   // default: globalThis.fetch
  timeoutMs: 30_000,
  retry: { maxAttempts: 3 },        // or false
});

token can be a string, a function returning one, or a TokenProvider that refreshes itself. On a first-party page use new Oms({ sessionCookie: true }) instead.

Some things work without a token (short links, notepads, chests, IP lookup, the captcha-gated tools), at a smaller daily quota.

Examples

Listing and paging:

import { collect } from "@omelhorsite/sdk";

const page = await oms.library.books.list({
  search: { title: "maias" },
  order: "created_at:desc",
  pageSize: 50,
});

page.items;           // this page
await page.next();    // the next one, or null
await collect(page, 1000); // flatten up to a limit

Uploading a file:

import { file } from "@omelhorsite/sdk";

const roots = await oms.storage.roots();
const [node] = await oms.storage.upload({
  parentId: roots.home!,
  files: [file(blob, "relatorio.pdf")],
});

Files are values (Blob, Uint8Array, ReadableStream), never paths. On React Native pass the picker's { uri, name, type } directly.

Running a tool (transcription, upscale, background removal, ...):

const result = await oms.tools.transcription.run(
  { audio: file(blob, "entrevista.m4a"), language: "pt" },
  { onProgress: (p) => console.log(p.status) },
);

run starts the job and waits for it. Use create + oms.jobs.wait if you would rather come back to it later.

Talking to the assistant:

const chat = await oms.llm.chats.create();

for await (const event of oms.llm.chats.send(chat.id, { content: "Olá!" })) {
  if (event.type === "delta") process.stdout.write(event.delta);
}

One answer from a model, for a program rather than a person:

const answer = await oms.llm.complete({
  messages: [
    { role: "system", content: "Answer in one sentence." },
    { role: "user", content: "What changed in Lisbon this week?" },
  ],
  tools: ["web_search", "read_url"],
});
answer.text;        // the answer
answer.tool_calls;  // the searches and pages the model used, in order

Reading a web page the search returned:

const hits = await oms.search.query({ q: "mesquita central lisboa", category: "news" });
const page = await oms.search.readPage({ url: hits.results[0]!.url });

An endpoint the SDK does not wrap yet:

const rows = await oms.http.get<{ id: string }[]>("/some/path");

Namespaces

  • oms.auth - OAuth: device grant, refresh, revoke, whoami. decodeIdToken(idToken).sub is the stable identifier; email is a contact and email_verified is often false, so never find or merge users by email.
  • oms.sessions, oms.passkeys - sign-in, sign-up, OTP, passkeys.
  • oms.account - the signed-in user, profile, sessions, usage. oms.account.notificationPreferences decides, per notification kind, whether it shows in the inbox and whether it is emailed; the security kinds always email, unless the master switch is off.
  • oms.storage - files and folders: upload, download, share.
  • oms.music - songs, artists, playlists, jams.
  • oms.movies - addons, collections, watch progress.
  • oms.library - books, shelves, annotations.
  • oms.llm - models, one-shot completions and assistant chats.
  • oms.cron - your TypeScript scripts run on a schedule by the server, with this SDK in scope; runs, logs and templates.
  • oms.search - web, image, news and video search, and reading a page's text.
  • oms.social - direct messages, friends, group chats.
  • oms.content.news - news feeds: sources driven by scripts (RSS, Telegram, YouTube, a page with a selector), the items they produce, full-text and similarity search over them.
  • oms.content - blogs (several per account, public, unlisted or private with invited members; oms.content.blogs.own manages them), notifications, feedback, site status. oms.content.notifications.unsubscribe(token) honours the link at the foot of a notification email and needs no credential.
  • oms.tools - media tools, each with its own daily quota.
  • oms.jobs, oms.quotas - background jobs and account limits.
  • oms.tickets - support tickets.
  • oms.shortLinks, oms.notepads, oms.dynamicQrs, oms.chests, oms.forms, oms.linkTrees - things that end in a shareable URL.
  • oms.ipLookup - geolocation for an IP.
  • oms.realtime - WebSocket: notifications, jobs, jams.
  • oms.admin - administrator-only.
  • oms.local - client-side helpers (passwords, QR codes). No network.

Errors

Everything thrown is an OmsError:

import { OmsApiError, OmsAuthError, OmsQuotaError } from "@omelhorsite/sdk";

try {
  await oms.storage.delete(id);
} catch (e) {
  if (e instanceof OmsQuotaError) wait(e.retryAfterMs);
  else if (e instanceof OmsAuthError) signIn();
  else if (e instanceof OmsApiError) console.log(e.status, e.message);
  else throw e;
}

OmsNetworkError means the API was never reached, OmsTimeoutError that the request took too long.

Developing

bun test
bun run typecheck
bun run build