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

@byjp/atproto-deeplink

v0.2.0

Published

Resolve at:// URIs into website/app URIs via the me.byjp.atproto.deeplink lexicons, using microcosm for record & identity lookups.

Downloads

92

Readme

@byjp/atproto-deeplink

A trivial lexicon, and a small, low-dependency TypeScript library, for declaring & resolving default/preferred sites for viewing at:// URIs.

[!WARNING] This is a prototype The lexicons live under me.byjp.atproto.deeplink.* today. If this gains traction expect them to move/rename.

Lexicons

See lexicons/ for the two lexicon definitions.

Deeplink transform

A deeplink record declares other URIs which are equivalent to at:// URIs with a given NSID.

For example, Bluesky might publish at://atproto-lexicons.bsky.social/me.byjp.atproto.deeplink.transform/app.bsky.feed.post:

{
  "$type": "me.byjp.atproto.deeplink.transform",
  "uris": ["https://bsky.app/profile/{{did}}/post/{{rkey}}"]
}

…declaring that the bsky.app URL (with {{did}} and {{rkey}} substituted) is a great way to view app.bsky.feed.post records.

Because it's hosted in the same atproto account/repo that defines the app.bsky.feed.post lexicon, it's the canonical alternate URI for that NSID.

Deeplink preference

Any account can also state a preference for how it likes to view any NSID with at://<you>/me.byjp.atproto.deeplink.preference/<nsid> records. These point at one or more transform records (which it need not own):

{
  "$type": "me.byjp.atproto.deeplink.preference",
  "deeplinks": ["at://bluepy.social/me.byjp.atproto.deeplink.transform/app.bsky.feed.post"]
}

Template tokens

uris entries are ordered (most preferred first) and support three tokens:

| Token | Value | | ---------------- | ------------------- | | {{did}} | the repo DID | | {{rkey}} | the record key | | {{collection}} | the collection NSID |

Only did (never handle) is offered, because it's stable by definition.

[!IMPORTANT] A site wanting deeplink support is expected to accept DIDs in its URLs.

Package Usage

import { resolve } from "@byjp/atproto-deeplink";

// Canonical/anonymous resolution
await resolve("at://did:plc:author/app.bsky.feed.post/abc123");
// => "https://bsky.app/profile/did:plc:author/post/abc123"

// Preferred resolution, with canonical fallback
await resolve("at://did:plc:author/app.bsky.feed.post/abc123", { as: "byjp.me" });
// => "https://bluepy.social/p/did:plc:author/abc123"

// Prefer particular scheme(s) among the candidate URIs, if they're present.
await resolve("at://did:plc:author/app.bsky.feed.post/abc123", {
  as: "byjp.me",
  schemes: ["gemini", "https"],
});
// => "gemini://deepsky.space/p/did:plc:author/abc123"

resolve returns null when no suitable URI can be found.

How resolution works

  1. The at:// URI is parsed; its handle is resolved to a DID via Slingshot (microcosm's record/identity cache), if necessary.
  2. With as: the as account's me.byjp.atproto.deeplink.preference record for the collection is fetched; each referenced transform record is followed in order and its uris collected.
  3. Otherwise / as a fallback: the NSID's lexicon authority DID is resolved via a DNS-over-HTTPS TXT lookup at _lexicon.<authority>, and its canonical transform record is read.
  4. The first suitable template (matching schemes, if given) is chosen and its tokens substituted.

Options

interface ResolveOptions {
  as?: string;          // handle or DID whose preference to consult first
  schemes?: string[];   // acceptable URI schemes, most preferred first
  slingshot?: string;   // override the Slingshot base URL
  doh?: string;         // override the DNS-over-HTTPS JSON endpoint
  fetch?: typeof fetch; // inject a fetch implementation (tests/custom transport)
}

The package has no runtime dependencies and is isomorphic — it uses the global fetch and resolves NSIDs over DNS-over-HTTPS so it runs in Node and the browser alike. Lower-level helpers (parseAtUri, nsidAuthority, resolveDid, resolveLexiconDid, getRecord, applyTemplate, …) are exported too.

Development

pnpm install
pnpm test        # vitest (behavioural, fetch is mocked)
pnpm typecheck
pnpm build       # tsup -> dual ESM/CJS + .d.ts in dist/