{"_id":"@byjp/atproto-deeplink","name":"@byjp/atproto-deeplink","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"@byjp/atproto-deeplink","version":"0.2.0","description":"Resolve at:// URIs into website/app URIs via the me.byjp.atproto.deeplink lexicons, using microcosm for record & identity lookups.","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./lexicons/*":"./lexicons/*"},"publishConfig":{"access":"public"},"sideEffects":false,"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"pnpm run build"},"keywords":["atproto","at-uri","deeplink","lexicon","bluesky","microcosm"],"author":{"name":"JP Hastings-Spital"},"license":"MIT","devDependencies":{"tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^2.1.8"},"packageManager":"pnpm@9.15.0","gitHead":"b0340dda94d2ac1ad9fa5d2fb36c011b981dec39","_id":"@byjp/atproto-deeplink@0.2.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-7uD1yhbqXem8PDABs0i3PrCt4zD/f+sH0okCgAAVoqNZISHlBtBtaN1bToKXpFvLpj6Az5j5isU6x9xSozoOSQ==","shasum":"78dfc6a200958cc68565e5dd69f5802649429923","tarball":"https://registry.npmjs.org/@byjp/atproto-deeplink/-/atproto-deeplink-0.2.0.tgz","fileCount":10,"unpackedSize":74713,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID9NaRvIjq9/vSLXiIehKFp9s7f3M3CvzlL/kZMyZUHvAiBmatrh8Y2eSx77JbJ+oddeVKvQD5m7INlhiUh9lHsX4w=="}]},"_npmUser":{"name":"jphastings","email":"jphastings@gmail.com"},"directories":{},"maintainers":[{"name":"jphastings","email":"jphastings@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/atproto-deeplink_0.2.0_1780161392015_0.5421948211525633"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-30T17:16:31.814Z","0.2.0":"2026-05-30T17:16:32.215Z","modified":"2026-05-30T17:16:32.468Z"},"maintainers":[{"name":"jphastings","email":"jphastings@gmail.com"}],"description":"Resolve at:// URIs into website/app URIs via the me.byjp.atproto.deeplink lexicons, using microcosm for record & identity lookups.","keywords":["atproto","at-uri","deeplink","lexicon","bluesky","microcosm"],"author":{"name":"JP Hastings-Spital"},"license":"MIT","readme":"# `@byjp/atproto-deeplink`\n\nA trivial lexicon, and a small, low-dependency TypeScript library, for declaring & resolving default/preferred sites for viewing `at://` URIs.\n\n> [!WARNING] This is a prototype\n> The lexicons live under `me.byjp.atproto.deeplink.*` today. If this gains traction expect them to move/rename.\n\n## Lexicons\n\nSee [`lexicons/`](./lexicons) for the two lexicon definitions.\n\n### Deeplink transform\n\nA **deeplink** record declares other URIs which are equivalent to `at://` URIs with a given NSID.\n\nFor example, Bluesky might publish `at://atproto-lexicons.bsky.social/me.byjp.atproto.deeplink.transform/app.bsky.feed.post`:\n\n```json\n{\n  \"$type\": \"me.byjp.atproto.deeplink.transform\",\n  \"uris\": [\"https://bsky.app/profile/{{did}}/post/{{rkey}}\"]\n}\n```\n\n…declaring that the `bsky.app` URL (with `{{did}}` and `{{rkey}}` substituted) is a great way to view `app.bsky.feed.post` records.\n\nBecause 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.\n\n### Deeplink preference\n\nAny 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):\n\n```json\n{\n  \"$type\": \"me.byjp.atproto.deeplink.preference\",\n  \"deeplinks\": [\"at://bluepy.social/me.byjp.atproto.deeplink.transform/app.bsky.feed.post\"]\n}\n```\n\n## Template tokens\n\n`uris` entries are ordered (most preferred first) and support three tokens:\n\n| Token            | Value               |\n| ---------------- | ------------------- |\n| `{{did}}`        | the repo DID        |\n| `{{rkey}}`       | the record key      |\n| `{{collection}}` | the collection NSID |\n\nOnly `did` (never `handle`) is offered, because it's stable by definition.\n\n> [!IMPORTANT]\n> A site wanting deeplink support is expected to accept DIDs in its URLs.\n\n## Package Usage\n\n```ts\nimport { resolve } from \"@byjp/atproto-deeplink\";\n\n// Canonical/anonymous resolution\nawait resolve(\"at://did:plc:author/app.bsky.feed.post/abc123\");\n// => \"https://bsky.app/profile/did:plc:author/post/abc123\"\n\n// Preferred resolution, with canonical fallback\nawait resolve(\"at://did:plc:author/app.bsky.feed.post/abc123\", { as: \"byjp.me\" });\n// => \"https://bluepy.social/p/did:plc:author/abc123\"\n\n// Prefer particular scheme(s) among the candidate URIs, if they're present.\nawait resolve(\"at://did:plc:author/app.bsky.feed.post/abc123\", {\n  as: \"byjp.me\",\n  schemes: [\"gemini\", \"https\"],\n});\n// => \"gemini://deepsky.space/p/did:plc:author/abc123\"\n```\n\n`resolve` returns `null` when no suitable URI can be found.\n\n### How resolution works\n\n1. The `at://` URI is parsed; its handle is resolved to a DID via [Slingshot](https://slingshot.microcosm.blue) (microcosm's record/identity cache), if necessary.\n2. **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.\n3. **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.\n4. The first **suitable** template (matching `schemes`, if given) is chosen and its tokens substituted.\n\n### Options\n\n```ts\ninterface ResolveOptions {\n  as?: string;          // handle or DID whose preference to consult first\n  schemes?: string[];   // acceptable URI schemes, most preferred first\n  slingshot?: string;   // override the Slingshot base URL\n  doh?: string;         // override the DNS-over-HTTPS JSON endpoint\n  fetch?: typeof fetch; // inject a fetch implementation (tests/custom transport)\n}\n```\n\nThe 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.\n\n## Development\n\n```sh\npnpm install\npnpm test        # vitest (behavioural, fetch is mocked)\npnpm typecheck\npnpm build       # tsup -> dual ESM/CJS + .d.ts in dist/\n```\n","readmeFilename":"README.md","_rev":"1-1298c84a4a631144a492eb1b0ba84f4d"}