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

hedgerow

v0.1.1

Published

A curated AT Protocol toolkit for publishing a site and bringing Bluesky conversations home.

Readme

hedgerow

The curated Hedgerow API. Publish a site into an AT Protocol repository, read its articles back, and show the Bluesky conversation around each article on the site itself.

npm install hedgerow

Use the feature subpaths in application code:

import { createBrowser } from "hedgerow/browser";
import { actor, permissionScope } from "hedgerow/social";

const auth = createBrowser({ clientId, scope: permissionScope });
const session = await auth.restore();
const social = session ? actor(session) : null;
flowchart LR
  App["Your site"] --> Browser["hedgerow/browser\nOAuth lifecycle"]
  Browser --> Session["scoped session"]
  Session --> Site["hedgerow/site\npublication + documents"]
  Session --> Social["hedgerow/social\nreplies + likes"]
  Node["hedgerow/node\nfrontmatter + filesystem sync"] --> Site
  Site --> PDS["author PDS"]
  Social --> PDS2["visitor PDS"]
  Social --> AppView["public Bluesky AppView"]

Why these subpaths

  • hedgerow/browser owns only browser OAuth and session lifecycle. It starts identity-only and accepts a feature's permission scope; it does not decide whether the person is a reader or an author.
  • hedgerow/social owns public thread/like reads and signed-in reply/like actions. Reading public conversations is anonymous; writing never is. A reply or like is a real Bluesky record in the visitor's own repository.
  • hedgerow/site owns publications, documents, safe Markdown decoding and a publication-bound author capability. It injects publication membership and hides arbitrary collection/rkey writes.
  • hedgerow/node is the Node-only Markdown/frontmatter synchronisation path. parseMarkdown() converts a file's YAML frontmatter and body into a ParsedPost; syncMarkdown() authenticates and reconciles those posts with the PDS. It lives separately so browser bundles never load filesystem, loopback-server or CLI dependencies.

Each feature exports permissionScope. Put the maximum union in hosted OAuth client metadata, then request only the subset needed by the current flow:

import { combineScopes, createBrowser } from "hedgerow/browser";
import { permissionScope as socialScope } from "hedgerow/social";
import { permissionScope as authorScope } from "hedgerow/site";

const clientMetadataScope = combineScopes(socialScope, authorScope);
const auth = createBrowser({ clientId, scope: socialScope });

For a dedicated author route or subdomain, request authorScope instead. The OAuth server—not TypeScript—is the security boundary; the capability wrappers add a smaller, harder-to-misuse product API on top.

Site authoring

import { author, readSite } from "hedgerow/site";

const published = await readSite(ownerDid, fetch, { publicationUri });
const writer = author(session, { ownerDid, publicationUri });

const created = await writer.createDocument({
  path: "/blog/hello",
  title: "Hello",
  publishedAt: new Date().toISOString(),
  markdown: "Hello **world**.",
});

Updates retain the same AT URI and use the loaded CID as a compare-and-swap token. If another tab or client changed the article first, the operation throws RecordConflictError instead of silently overwriting it.

What is deliberately not here

TipTap configuration, IndexedDB draft policy, preview/diff UI, site styling, URL layout and hosting-cache policy remain generated application code for now. Those are product decisions, not protocol invariants. create-hedgerow will provide the maintained starter; a reusable editor package should only exist after independent sites converge on the same runtime contract.

Pre-1.0, ESM-only. MIT.