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

ghostoff

v0.1.0

Published

Export Ghost CMS posts to Offprint / Standard.site over AT Protocol

Downloads

158

Readme

GhostOff

Export public posts from a Ghost CMS and upload them to Offprint via the AT Protocol / Standard.site lexicons.

The live test publication is at https://ghostoff.offprint.app.

Installation

Install globally from npm:

npm install -g ghostoff

Or run directly with npx:

npx ghostoff -- --help

What it does

  • Paginates through Ghost's public Content API for posts.
  • Downloads each post's images, resizes them to fit atproto's blob limits, and uploads them as blobs to a PDS.
  • Converts Ghost HTML bodies into Offprint's block-based format (app.offprint.content).
  • Writes a site.standard.document record per post and an app.offprint.document.article record that points back to it.
  • Tracks progress in a local state file so reruns update existing records instead of duplicating them.

Quick start

Copy .env.example to .env and fill in the secrets:

cp .env.example .env
GHOST_URL=https://your-ghost-site.com
GHOST_API_KEY=your-ghost-content-api-key
ATPUBLICATION_AT_URI=at://did:plc:.../site.standard.publication/...

# Auth: OAuth is the default. Set only if you want password auth.
ATP_IDENTIFIER=your-handle-or-did
# ATP_APP_PASSWORD=your-app-password

Run a dry first pass to inspect what would be uploaded:

npm install
npm run dev -- --dry-run

When you're ready, run the real migration:

npm run dev

If you haven't set ATP_APP_PASSWORD, the CLI will ask:

No app password found in .env or --atproto-app-password. Would you like to authenticate via OAuth [Y/n]?

Answer Y and it will open a browser so you can authenticate with your PDS. If you set ATP_APP_PASSWORD, it uses password auth instead.

Rerunning the same command will putRecord existing records instead of creating duplicates.

CLI options

| Flag | Environment variable | Description | |------|------------------------|-------------| | --ghost-url | GHOST_URL | Ghost publication URL | | --ghost-api-key | GHOST_API_KEY | Ghost Content API key | | --atproto-identifier | ATP_IDENTIFIER | atproto handle or DID | | --atproto-app-password | ATP_APP_PASSWORD | atproto app password | | --atproto-service | ATP_SERVICE | PDS/service URL (password auth only) | | --publication-at-uri | ATPUBLICATION_AT_URI | Existing site.standard.publication AT-URI | | --oauth-client-id | GHOSTOFF_OAUTH_CLIENT_ID | OAuth client_id URL (dev default: http://localhost) | | --oauth-scope | GHOSTOFF_OAUTH_SCOPE | OAuth scope (default: atproto transition:generic) | | --oauth-session-file | — | OAuth session cache (default: ghostoff-oauth-session.json) | | --oauth-state-file | — | OAuth transient state cache (default: ghostoff-oauth-state.json) | | --dry-run | — | Build records without uploading | | --export-dir | — | Local asset cache directory (default: ghostoff-export) | | --state-file | — | Idempotency state file (default: ghostoff-state.json) | | --session-file | — | Password session cache (default: ghostoff-session.json) | | --verbose | — | Verbose logging |

Scripts

npm run dev          # run the migration
npm run build        # compile TypeScript to dist/
npm run typecheck    # type-check without emitting

Project layout

src/
├── index.ts              # CLI orchestration
├── config.ts             # env + argv loading
├── ghost.ts              # Ghost Content API client
├── assets.ts             # image download, cache, resize, blob upload
├── html-to-offprint.ts   # Ghost HTML -> Offprint blocks
├── facets.ts             # inline formatting -> richtext facets
├── atproto.ts            # auth adapters (OAuth + password), record ops, validation
├── oauth.ts              # loopback OAuth server and file-backed stores
├── state.ts              # idempotency state file
├── rate-limit.ts         # retry + rate-limit helpers
└── types.ts              # shared type definitions

Learning more

  • PLAN.md — the original migration plan.
  • docs/session-summary.md — a narrative of how the first version was built.
  • docs/development.md — how the codebase is organized.
  • docs/next-steps.md — ideas for what to add or improve next.