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

@getopenpost/sdk

v0.5.1

Published

Official TypeScript SDK for the OpenPost API. Create publications, manage renditions, upload media, and track publishing jobs.

Readme

@getopenpost/sdk

Official TypeScript SDK for the OpenPost API. Create publications, manage per-account renditions, upload media, and track publishing jobs from Node 20+, Bun, or Cloudflare Workers. Zero runtime dependencies: only fetch is used.

import { OpenPost } from "@getopenpost/sdk";

const openpost = new OpenPost({
  baseUrl: "https://app.openpo.st",
  token: process.env.OPENPOST_TOKEN,
  workspaceId: process.env.OPENPOST_WORKSPACE_ID,
});

const draft = await openpost.publications.create({
  workspace_id: await openpost.workspaceId(),
  title: "Launch day",
  content_profile: "short_text",
  source_text: "We just shipped our TypeScript SDK.",
  social_account_ids: ["acc_..."],
});

await openpost.publications.validate(draft.id, { throwOnInvalid: true });
const action = await openpost.publications.publishNow(draft.id, draft.revision);
if (action.job_id) await openpost.jobs.wait(action.job_id);
const done = await openpost.publications.wait(draft.id);
console.log(done.status); // published

Install

npm install @getopenpost/sdk

Mint a workspace API token in OpenPost (API tokens settings) and pass it as token, or set OPENPOST_TOKEN. Override the instance with baseUrl or OPENPOST_URL. The default workspace comes from workspaceId, OPENPOST_WORKSPACE_ID, or OPENPOST_WORKSPACE (shared with the Go CLI).

Publications

Publications are the authored record; renditions are the per-account versions. Copy Social Set defaults into renditions on create, let explicit post values win, and never rewrite existing publications when a set changes.

// Destination-specific text plus provider settings for one account.
const options = await openpost.accounts.destinationOptions("acc_...");
console.dir(options, { depth: null });

await openpost.publications.upsertRenditions(draft.id, draft.revision, [
  {
    social_account_id: "acc_...",
    body: "We just shipped our TypeScript SDK. Try it:",
    settings: { url: "https://openpo.st" },
  },
]);

// Lifecycle actions all take the revision you last saw, so stale
// automation fails with a conflict instead of clobbering edits.
await openpost.publications.publishNow(draft.id, draft.revision);
await openpost.publications.cancel(draft.id, draft.revision);
await openpost.publications.retryFailed(draft.id);
const events = await openpost.publications.events(draft.id);

Media

upload() hides the three-step session flow: reserve the row, PUT the bytes to the storage target, then complete. Bytes go to external storage targets without the API token.

const asset = await openpost.media.upload({
  workspaceId: "ws_...",
  file: new Uint8Array(await Bun.file("cover.png").arrayBuffer()),
  filename: "cover.png",
  mimeType: "image/png",
  altText: "Launch cover art",
});

Accounts, Social Sets, jobs

const accounts = await openpost.accounts.list("ws_...");
const options = await openpost.accounts.destinationOptions("acc_...");
const sets = await openpost.socialSets.list("ws_...");
const failedJobs = await openpost.jobs.list({ workspace_id: "ws_...", status: "failed" });

Publication responses expose creation_source. Records created through this package use sdk.

Errors

import { OpenPost, OpenPostError } from "@getopenpost/sdk";

try {
  await openpost.publications.schedule(id, revision);
} catch (error) {
  if (error instanceof OpenPostError && error.code === "conflict") {
    // Someone edited the publication; re-read it and retry.
  }
}

Every error carries a disposition automation can switch on (never | after-delay | after-reconnect | reconcile-first). reconcile-first means the request may have executed: re-read state before retrying, never blind-retry. toJSON() serializes the code, status, disposition, and details.

| Code | HTTP | Disposition | Retry? | | ---------------- | --------- | ----------------- | --------------------------------------- | | missing_config | — | never | no, set token/workspace | | unauthorized | 401 | after-reconnect | no, reconnect first | | forbidden | 403 | never | no | | not_found | 404 | never | no | | conflict | 409 | never | no, re-read first | | validation | 400 / 422 | never | no, fix the input | | rate_limited | 429 | after-delay | yes (retryAfterMs) | | server | 5xx | after-delay | GET retries once; mutations never retry | | ambiguous | — | reconcile-first | no, reconcile first | | timeout | — | after-delay | caller decides | | network | — | after-delay | caller decides |

Outcomes

Renditions are the per-destination truth. describeRendition maps one rendition to an exhaustive outcome: published requires the provider native id, a published row without one (or a failed row with no error detail) reports unknown with a reconcile-first disposition. summarizePublication derives one aggregate (pending | complete | partial); partial success is first-class, so mixed results never read as published. A publication where every rendition failed also reports partial: the counts (published, failed, pending, unknown) distinguish it from mixed results.

import { describeRendition, summarizePublication } from "@getopenpost/sdk";

const summary = summarizePublication(publication);
if (summary.status === "partial") {
  for (const outcome of summary.outcomes) {
    console.log(outcome.state, outcome.disposition);
  }
}

Testing

@getopenpost/sdk/testing ships a deterministic mock of the automation surface: no network, fixed ids and timestamps, and one scenario string plus an advance() latch for async pipelines. The subpath is separate from the root import so production bundles never carry it.

import { MockOpenPost } from "@getopenpost/sdk/testing";

const mock = new MockOpenPost("processing-then-success");
const draft = await mock.publications.create({
  workspace_id: "ws_...",
  title: "Launch",
  content_profile: "short_text",
  source_text: "We shipped.",
  social_account_ids: ["acc_..."],
});
await mock.publications.publishNow(draft.id, draft.revision);
mock.advance(); // complete pending provider work without timers
const done = await mock.publications.wait(draft.id);

Scenarios: immediate-success, processing-then-success, mixed-success-failure, rate-limited, reconnect-required, ambiguous-accept. history() records every operation with a sequence number; reset() clears state and counters.

Automation surface

| Surface | Use when | | ------------------ | ------------------------------------------------------- | | SDK (this pkg) | CI jobs, scripts, and agents calling OpenPost over HTTP | | CLI | Terminal publishing and login via openpost | | MCP | Agentic tools inside a running OpenPost instance | | n8n | Visual workflows with @getopenpost/n8n-nodes-openpost |

License

MIT. See LICENSE.