{"_id":"@aturi.to/waypoints","_rev":"5-ea92629bfcae1ba8a291b55b0c7b191d","name":"@aturi.to/waypoints","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@aturi.to/waypoints","version":"0.1.0","keywords":["atproto","atmosphere","bluesky","at-uri","aturi","waypoints"],"author":{"name":"dame.art"},"license":"MIT","_id":"@aturi.to/waypoints@0.1.0","maintainers":[{"name":"atpota.to","email":"contact@atpota.to"}],"homepage":"https://aturi.to","bugs":{"url":"https://github.com/atpota-to/aturi/issues"},"dist":{"shasum":"308e36bff84e06bc2f6e78484b0032312d866842","tarball":"https://registry.npmjs.org/@aturi.to/waypoints/-/waypoints-0.1.0.tgz","fileCount":9,"integrity":"sha512-i55pNzFG3OY3x3OjEzqajUlBqVDVBhZQdsmPDPZhiK0TSGtcNNEHIe3YAxs1wCnEmbYEHw5FFyiaroj+W9eukw==","signatures":[{"sig":"MEQCICrjKXWpJd69bBnfSrhzBqvF1JmlWO5fEg7xRNWEiUWGAiBh2zBnFWqGkEnJg3O3Cj9kcbIu9zMaOJ55JQdj/ANKZQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":313378},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"4b767badcc9afe4136f1837d95d7e8cc5f660c1c","scripts":{"dev":"tsup --watch","sync":"node scripts/sync.mjs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","sync:check":"node scripts/sync.mjs --check","test:watch":"vitest","prepublishOnly":"npm run sync:check && npm run build"},"_npmUser":{"name":"atpota.to","email":"contact@atpota.to"},"repository":{"url":"git+https://github.com/atpota-to/aturi.git","type":"git","directory":"packages/waypoints"},"_npmVersion":"10.9.4","description":"[Beta] Aturi's curated catalog of Atmosphere (AT Protocol) clients, with per-client \"Open in…\" link builders, recommendations, and URL/AT-URI resolution. Zero runtime dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^20"},"_npmOperationalInternal":{"tmp":"tmp/waypoints_0.1.0_1780069014094_0.7309666643275832","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aturi.to/waypoints","version":"0.1.1","keywords":["atproto","atmosphere","bluesky","at-uri","aturi","waypoints"],"author":{"name":"atpotato, LLC"},"license":"MIT","_id":"@aturi.to/waypoints@0.1.1","maintainers":[{"name":"atpota.to","email":"contact@atpota.to"}],"homepage":"https://aturi.to","bugs":{"url":"https://github.com/atpota-to/aturi/issues"},"dist":{"shasum":"e733dd47f253d15fc3b8ff28251a73d3c2d9b8bf","tarball":"https://registry.npmjs.org/@aturi.to/waypoints/-/waypoints-0.1.1.tgz","fileCount":9,"integrity":"sha512-cVLylDn5OJNc7z8fNlLWCTv6EznXuzQOsiG0aXl1vBk9doxtvsjb0E34kU+8FLXQ6Yrb5wOftHc81JEtkab/aQ==","signatures":[{"sig":"MEUCIGeoUnb4zUh5r8bi2/Tf3mL6GP+nl+pvmymcEDbFsYo4AiEA08zKckZpmIElZYFxsn/3cEr1jSTHK9jZ1muhw2yltk4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":313393},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"393bb1d349c938cae2ba334f7f13db4155700f48","scripts":{"dev":"tsup --watch","sync":"node scripts/sync.mjs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","sync:check":"node scripts/sync.mjs --check","test:watch":"vitest","prepublishOnly":"npm run sync:check && npm run build"},"_npmUser":{"name":"atpota.to","email":"contact@atpota.to"},"repository":{"url":"git+https://github.com/atpota-to/aturi.git","type":"git","directory":"packages/waypoints"},"_npmVersion":"10.9.4","description":"[Beta] Aturi's curated catalog of Atmosphere (AT Protocol) clients, with per-client \"Open in…\" link builders, recommendations, and URL/AT-URI resolution. Zero runtime dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^20"},"_npmOperationalInternal":{"tmp":"tmp/waypoints_0.1.1_1780070045349_0.9481283571760841","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aturi.to/waypoints","version":"0.1.2","keywords":["atproto","atmosphere","bluesky","at-uri","aturi","waypoints"],"author":{"name":"atpotato, LLC"},"license":"MIT","_id":"@aturi.to/waypoints@0.1.2","maintainers":[{"name":"atpota.to","email":"contact@atpota.to"}],"homepage":"https://aturi.to","bugs":{"url":"https://github.com/atpota-to/aturi/issues"},"dist":{"shasum":"f26e95ce4a15ed53f3d58e3d1ea94a8a699f6854","tarball":"https://registry.npmjs.org/@aturi.to/waypoints/-/waypoints-0.1.2.tgz","fileCount":9,"integrity":"sha512-0hZReyD5sR4JBthLBmFRCdkL2NjEcjX72g3GE25h30Uml+3aiROGZ9DCD89whjf6XezKS6CzrXxomn3+dHdIsA==","signatures":[{"sig":"MEQCIDZX9kvOab+2Zc3m3OPTl0rAWaSaqDcIWoQqzHce9ncBAiAoS+nNuyrIe5bfcAA4uJ7VPNp8IWzYWWvC19TXfvsiRw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aturi.to%2fwaypoints@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":384057},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"2d430754ac664b1fb3b7cecb97322393a2f04a3c","scripts":{"dev":"tsup --watch","sync":"node scripts/sync.mjs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","sync:check":"node scripts/sync.mjs --check","test:watch":"vitest","prepublishOnly":"npm run sync:check && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f0f49c63-ceae-4bcd-9170-8e4f1d1cc94b"}},"repository":{"url":"git+https://github.com/atpota-to/aturi.git","type":"git","directory":"packages/waypoints"},"_npmVersion":"11.19.0","description":"[Beta] Aturi's curated catalog of Atmosphere (AT Protocol) clients, with per-client \"Open in…\" link builders, recommendations, and URL/AT-URI resolution. Zero runtime dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^20"},"_npmOperationalInternal":{"tmp":"tmp/waypoints_0.1.2_1786386091135_0.10702992808904943","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aturi.to/waypoints","version":"0.1.3","keywords":["atproto","atmosphere","bluesky","at-uri","aturi","waypoints"],"author":{"name":"atpotato, LLC"},"license":"MIT","_id":"@aturi.to/waypoints@0.1.3","maintainers":[{"name":"atpota.to","email":"contact@atpota.to"}],"homepage":"https://aturi.to","bugs":{"url":"https://github.com/atpota-to/aturi/issues"},"dist":{"shasum":"40706e37de535cf6ca2e34c93244d482bed9a410","tarball":"https://registry.npmjs.org/@aturi.to/waypoints/-/waypoints-0.1.3.tgz","fileCount":9,"integrity":"sha512-WZ2BcBkQCMrKSsMrC+HpbMkAsvmQohOqe+hU5IdKdnqYhpM6SDipHsaMu6MYeZUKDsKS7a16yP+ztCXS1GeuSA==","signatures":[{"sig":"MEUCIECcPTPqmGeTgTw2/t0t/EYQ7Byoc2xX7PijsR2GDHijAiEA+t4xGE4pFGULV+SS+83ltDsgLNzDLWWqeGA0lXmp83c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aturi.to%2fwaypoints@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":528333},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"feea54c74661778da5cb22c2bb576f8c2b58e74e","scripts":{"dev":"tsup --watch","sync":"node scripts/sync.mjs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","sync:check":"node scripts/sync.mjs --check","test:watch":"vitest","prepublishOnly":"npm run sync:check && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f0f49c63-ceae-4bcd-9170-8e4f1d1cc94b"}},"repository":{"url":"git+https://github.com/atpota-to/aturi.git","type":"git","directory":"packages/waypoints"},"_npmVersion":"11.19.0","description":"[Beta] Aturi's curated catalog of Atmosphere (AT Protocol) clients, with per-client \"Open in…\" link builders, recommendations, and URL/AT-URI resolution. Zero runtime dependencies.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^20"},"_npmOperationalInternal":{"tmp":"tmp/waypoints_0.1.3_1787153449958_0.4820447712058664","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"_id":"@aturi.to/waypoints@0.1.4","bugs":{"url":"https://github.com/atpota-to/aturi/issues"},"dist":{"shasum":"ab0bb66f368399ad9af88afe4e7f4cc9475b56be","tarball":"https://registry.npmjs.org/@aturi.to/waypoints/-/waypoints-0.1.4.tgz","fileCount":15,"integrity":"sha512-MT/86TKJT1KGJ0/gbKa/F+FKsCzO/yqH+sQkNRuP0rd73vV9/fYMJDu3nXRM8qPVwFw4Kg/us68YZpqdyXN7aA==","signatures":[{"sig":"MEYCIQCFJy9lbGuO0Br7Idaq42XUsfVn2gdqKGDyf7BLP+G8zwIhAKuX7d5w+fuoTVu+mc5F4tD2txDBpfwLwlQO4twTMIVq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDKGeglmt8JoqfkcARDZuCoviziZZEQsTeqPgUnUcGIzQIhAKnzZGRe/TGxwk95lIAUusE3S6k7/RHowFFcp48CV0vy"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aturi.to%2fwaypoints@0.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":878683},"main":"./dist/index.cjs","name":"@aturi.to/waypoints","type":"module","types":"./dist/index.d.ts","author":{"name":"atpotato, LLC"},"module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./icons":{"import":{"types":"./dist/icons.d.ts","default":"./dist/icons.js"},"require":{"types":"./dist/icons.d.cts","default":"./dist/icons.cjs"}},"./package.json":"./package.json"},"gitHead":"2759b7c1aa1666d8ab6af8da0786d1d5012e125e","license":"MIT","scripts":{"dev":"tsup --watch","sync":"node scripts/sync.mjs","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","sync:check":"node scripts/sync.mjs --check","test:watch":"vitest","prepublishOnly":"npm run sync:check && npm run build"},"version":"0.1.4","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f0f49c63-ceae-4bcd-9170-8e4f1d1cc94b"}},"homepage":"https://aturi.to","keywords":["atproto","atmosphere","bluesky","at-uri","aturi","waypoints","icons","svg"],"repository":{"url":"git+https://github.com/atpota-to/aturi.git","type":"git","directory":"packages/waypoints"},"_npmVersion":"11.19.1","description":"[Beta] Aturi's curated catalog of Atmosphere (AT Protocol) clients, with per-client \"Open in…\" link builders, recommendations, URL/AT-URI resolution, and every client's brand mark as framework-agnostic SVG. Zero runtime dependencies.","directories":{},"maintainers":[{"name":"atpota.to","email":"contact@atpota.to"}],"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"typesVersions":{"*":{"icons":["./dist/icons.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^20"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/waypoints_0.1.4_1788889746202_0.06835338194407203"}}},"time":{"created":"2026-05-29T15:36:53.915Z","modified":"2026-09-08T17:49:06.675Z","0.1.0":"2026-05-29T15:36:54.231Z","0.1.1":"2026-05-29T15:54:05.522Z","0.1.2":"2026-08-10T18:21:31.294Z","0.1.3":"2026-08-19T15:30:50.113Z","0.1.4":"2026-09-08T17:49:06.289Z"},"bugs":{"url":"https://github.com/atpota-to/aturi/issues"},"author":{"name":"atpotato, LLC"},"license":"MIT","homepage":"https://aturi.to","keywords":["atproto","atmosphere","bluesky","at-uri","aturi","waypoints","icons","svg"],"repository":{"url":"git+https://github.com/atpota-to/aturi.git","type":"git","directory":"packages/waypoints"},"description":"[Beta] Aturi's curated catalog of Atmosphere (AT Protocol) clients, with per-client \"Open in…\" link builders, recommendations, URL/AT-URI resolution, and every client's brand mark as framework-agnostic SVG. Zero runtime dependencies.","maintainers":[{"name":"atpota.to","email":"contact@atpota.to"}],"readme":"# @aturi.to/waypoints\n\nAturi's curated catalog of Atmosphere (AT Protocol) clients (\"waypoints\") plus\nthe logic to turn an AT URI into per-client \"Open in…\" links, recommend the best\nclient for a record type, and reverse-resolve a pasted URL back into an AT URI.\nEach client's brand mark ships too, as framework-agnostic SVG.\n\nZero runtime dependencies. Works in the browser, Node 18+, and edge runtimes.\nShips ESM + CJS with full type definitions.\n\nFor a drop-in React picker UI, see [`@aturi.to/waypoints-react`](../waypoints-react).\n\n> **Beta: early release.** This is a `0.x` package that hasn't been\n> thoroughly tested in production yet. Expect rough edges, and possible\n> breaking changes between minor versions while the API settles. Bug reports\n> and feedback are very welcome at\n> [github.com/atpota-to/aturi/issues](https://github.com/atpota-to/aturi/issues).\n\n## Install\n\n```sh\nnpm install @aturi.to/waypoints\n```\n\nAlso mirrored to GitHub Packages as `@atpota-to/waypoints` (GitHub only accepts a\nscope matching the repository owner, and it rejects the dot in `aturi.to`). Same\nbuild, same version. GitHub Packages requires a token even for public packages,\nso installing from there needs an `.npmrc`:\n\n```sh\n@atpota-to:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}   # PAT with read:packages\n```\n\n## Quick start\n\n```ts\nimport { resolveAtUri, resolveUrl, buildWaypointsForParsed } from '@aturi.to/waypoints';\n\n// AT URI -> waypoints\nconst result = resolveAtUri('at://did:plc:abc/app.bsky.feed.post/3k7');\nresult?.waypoints; // [{ id: 'anisota', name: 'Anisota', category, url }, ...]\nresult?.recommended; // { ids: ['bluesky', 'anisota', ...], label: 'Recommended for Posts' }\n\n// Pasted page URL -> waypoints (offline pattern match)\nconst fromUrl = await resolveUrl('https://bsky.app/profile/alice.bsky.social/post/3k7');\n```\n\n## What's included\n\n- **Catalog**: `WAYPOINT_DESTINATIONS_DATA`, `WAYPOINT_ORDER`, `WAYPOINT_CATEGORIES_DATA`,\n  `COMPAT_FAMILIES`, and the `WaypointData` / `WaypointType` types.\n- **Link builders & recommendations**: `getWaypointDataForType`,\n  `getCategorizedWaypointsData`, `getRecommendedWaypointsData`,\n  `getFeaturedWaypointData`, `waypointActivity`.\n- **Compose intents**: `supportsComposeIntent`, `getComposeIntentUrl`,\n  `getComposeIntentAppUrl`, `getComposeIntentTemplate`,\n  `getComposeIntentWaypoints`, `describeComposeIntent`.\n- **AT URI parsing**: `parseURI`, `resolveHandle`, `getDisplayName`.\n- **Reverse resolution**: `matchSupportedUrl`, `parseAtUri`, `SUPPORTED_HOSTS`.\n- **High-level resolvers** (`resolve.ts`):\n  - `buildWaypointsForParsed(parsed, { did?, excludeSourceId?, composeText? })`\n  - `resolveAtUri(uri, { composeText? })`\n  - `resolveUrl(url, { fetchHead?, resolveHandle?, composeText? })`\n  - `resolveViaApi(input, { endpoint? })`: typed client for the hosted\n    `aturi.to/api/resolve` endpoint.\n- **Universal links** (`universalLinks.ts`): `buildUniversalLink`,\n  `parseUniversalLink`, `isUniversalLink`, `describeUniversalLink`,\n  `buildUniversalLinkTags`, `UNIVERSAL_LINK_ORIGIN`.\n- **Brand marks** (`@aturi.to/waypoints/icons`): `WAYPOINT_ICON_SVGS`,\n  `getWaypointIconSvg`, and a named export per waypoint. See\n  [Icons](#icons).\n\n### Compose intents\n\nbsky.app can be handed a link that opens its composer pre-filled —\n`/intent/compose?text=…`, documented at\n[docs.bsky.app](https://docs.bsky.app/docs/advanced-guides/intent-links) — and\nthe clients forked from the official social app inherit the same route. The\ncatalog records which ones do, so you can offer \"post this in your client\"\nwithout hardcoding a list.\n\n```ts\nimport {\n  WAYPOINT_DESTINATIONS_DATA,\n  getComposeIntentUrl,\n  getComposeIntentWaypoints,\n  supportsComposeIntent,\n} from '@aturi.to/waypoints';\n\ngetComposeIntentWaypoints().map((w) => w.id);\n// ['anisota', 'bluesky', 'impro', 'blacksky', 'witchsky', 'mu', 'deer', 'northsky']\n\nsupportsComposeIntent(WAYPOINT_DESTINATIONS_DATA.pdsls); // false\ngetComposeIntentUrl(WAYPOINT_DESTINATIONS_DATA.deer, 'hello from my app');\n// 'https://deer.social/intent/compose?text=hello%20from%20my%20app'\n```\n\nResolver results carry the same information per waypoint as a serializable\n`composeIntent` (`null` when the client has no confirmed route), and\n`resolveAtUri` / `resolveUrl` / `buildWaypointsForParsed` take a `composeText`\noption to pre-fill it:\n\n```ts\nconst { waypoints } = resolveAtUri(uri, { composeText: 'look at this' })!;\nwaypoints.find((w) => w.id === 'deer')?.composeIntent;\n// {\n//   url: 'https://deer.social/intent/compose?text=look%20at%20this',\n//   urlTemplate: 'https://deer.social/intent/compose?text={text}',\n//   textParam: 'text',\n//   prefillsText: true,\n// }\n```\n\nTwo things not to assume. `prefillsText` is `false` for a client that routes\nthe intent but drops the text (Impro today), so the link opens an empty\ncomposer — fine as a jump, useless as a share. And `appUrl` is only set where\nthe client publishes a native scheme (`bluesky://intent/compose`), so treat it\nas a bonus rather than a fallback.\n\nA missing `composeIntent` means \"no route we've confirmed\", not proof the\nclient lacks one. If a client you maintain handles compose intents,\n[open an issue](https://github.com/atpota-to/aturi/issues) and we'll add it.\n\n### Universal links\n\nA universal link is the client-agnostic address of a record: drop an\n`aturi.to/…` URL in a DM or a footer and the recipient gets a preview plus every\nclient that can open it, instead of being pushed into whichever app you happen\nto use. `buildUniversalLink` returns that address for anything that names a\nrecord: an AT URI, a handle, a DID, a page URL from any client in the catalog,\na `ParsedURI`. It's pure, synchronous, and never fetches.\n\n```ts\nimport { buildUniversalLink, describeUniversalLink } from '@aturi.to/waypoints';\n\nbuildUniversalLink('at://did:plc:abc/app.bsky.feed.post/3k7');\n// 'https://aturi.to/profile/did:plc:abc/post/3k7'\nbuildUniversalLink('https://bsky.app/profile/alice.bsky.social/post/3k7');\n// 'https://aturi.to/profile/alice.bsky.social/post/3k7'\nbuildUniversalLink('@alice.bsky.social');\n// 'https://aturi.to/profile/alice.bsky.social'\n```\n\nFor a copy button or a share sheet, `describeUniversalLink` returns the strings\naround the link too:\n\n```ts\nconst link = describeUniversalLink('at://alice.bsky.social/app.bsky.feed.post/3k7');\nlink.url;               // 'https://aturi.to/profile/alice.bsky.social/post/3k7'\nlink.label;             // 'Post by @alice.bsky.social'\nlink.share;             // { title, text, url }; hand it straight to navigator.share()\nlink.snippets.markdown; // '[Post by @alice.bsky.social](https://aturi.to/…)'\nlink.oembedUrl;         // hosted oEmbed endpoint (posts only; null otherwise)\n```\n\nOptions on both: `origin` (point at your own deployment), `did` + `preferDid`\n(address links by DID, which survives a handle change), and `params` for\nappended query parameters like `{ ref: 'my-app' }`.\n\nGoing the other way, `parseUniversalLink` turns an aturi.to URL back into a\n`ParsedURI`. Canonical `/profile/…` links, `/explore/…` views, and the legacy\nbare-path and `at://`-in-path spellings all resolve:\n\n```ts\nparseUniversalLink('https://aturi.to/profile/alice.bsky.social/post/3k7');\n// { type: 'post', handle: 'alice.bsky.social', collection: 'app.bsky.feed.post', rkey: '3k7', … }\n```\n\n#### Making your own pages resolvable\n\nIf your app renders atproto records, `buildUniversalLinkTags` writes the\n`<head>` tags that let the rest of the Atmosphere find its way back to them:\n\n```ts\nbuildUniversalLinkTags('at://did:plc:abc/app.bsky.feed.post/3k7').html;\n// <meta name=\"at:canonical\" content=\"at://did:plc:abc/app.bsky.feed.post/3k7\" />\n// <meta name=\"at:author\" content=\"at://did:plc:abc\" />\n// <link rel=\"alternate\" href=\"at://did:plc:abc/app.bsky.feed.post/3k7\" />\n// <link rel=\"alternate\" type=\"application/json+oembed\" href=\"https://aturi.to/api/oembed?url=…\" />\n```\n\n`at:canonical` is the [AT Tags proposal](https://tangled.org/chrisshank.com/at-tags/).\nAturi's browser extension reads it off the live page and `/api/resolve` reads it\noff your HTML, so a link to your page resolves into every other client that can\nopen the record, without your app being in the catalog at all. The\n`<link rel=\"alternate\" href=\"at://…\">` beside it is the older spelling of the\nsame declaration, kept because the resolver still falls back to it. The oEmbed\npointer is emitted for posts only, since that's all the endpoint renders.\n\nThey're static strings describing a record you already display, and serving\nthem hands nothing to aturi.to.\n\n### DID-only waypoints\n\nA handful of destinations (`pdsls`, `atptools`, `margin`, `grain`, `popfeed`)\nonly produce useful URLs when a DID is known. They're filtered out unless a DID\nis available: pass one in, or supply a `resolveHandle` to `resolveUrl`.\n\n### Hosted vs. local resolution\n\n`resolveUrl` matches URL patterns locally (no network). The optional `fetchHead`\nflag and `resolveViaApi` let you fall back to fetching the page and probing for a\n`<link href=\"at://…\">`, useful for sites without a recognizable URL shape.\n`resolveViaApi` is the right choice from a browser, where fetching arbitrary\npages is blocked by CORS.\n\nThere's a second hosted endpoint for the catalog itself — what's in it, and\nwhich clients can do what — for consumers that aren't installing the package:\n\n```http\nGET https://aturi.to/api/waypoints\nGET https://aturi.to/api/waypoints?type=post&capability=compose\n```\n\n## Icons\n\nEvery waypoint's brand mark ships as plain SVG markup, behind a subpath:\n\n```ts\nimport { WAYPOINT_ICON_SVGS, getWaypointIconSvg } from '@aturi.to/waypoints/icons';\n\ngetWaypointIconSvg('bluesky'); // '<svg xmlns=\"…\" viewBox=\"0 0 512 512\">…</svg>'\n```\n\nThe marks are a subpath rather than part of the main entry because a bundler\ncannot drop unused keys from an object literal. Importing them is opt-in, so\nnothing that only wants link builders pays for roughly 80KB of artwork.\n\nValues are markup, not components, so any framework can render them:\n\n```svelte\n{@html getWaypointIconSvg(id)}\n```\n```vue\n<span v-html=\"getWaypointIconSvg(id)\" />\n```\n```tsx\n<span dangerouslySetInnerHTML={{ __html: getWaypointIconSvg(id) ?? '' }} />\n```\n\nThe values are static package data, not user input, so none of those three is an\ninjection risk. Linters that flag `v-html` or `dangerouslySetInnerHTML` on sight\nwill still want an inline exception.\n\nIndividual marks are exported by name if you would rather pull one and let the\nrest tree-shake away:\n\n```ts\nimport { blueskyIconSvg, tangledIconSvg } from '@aturi.to/waypoints/icons';\n```\n\nEvery id in `WAYPOINT_ORDER` has a mark. React consumers who want components\ninstead can use [`@aturi.to/waypoints-react`](../waypoints-react), which ships\nthe same catalog as JSX.\n\n### Styling\n\nEach mark is 24x24 and paints in `currentColor`, so it takes the colour of the\nsurrounding text. A CSS rule beats the width and height baked into the markup,\nso size them from your stylesheet rather than rewriting the string:\n\n```css\n.waypoint-icon svg { width: 1.25rem; height: 1.25rem; }\n```\n\nThree marks (Lea, Leaflet, pckt) knock part of the shape out to the page\nbackground instead of painting it. That colour comes from\n`var(--bg-primary, white)`, so set `--bg-primary` on an ancestor if your surface\nis not white:\n\n```css\n.waypoint-icon { --bg-primary: #0c0908; }\n```\n\n`currentColor` only resolves when the SVG is part of the page. Inlining is the\nsupported path; a mark referenced as an external image or data URI through\n`<img src>` or `background-image` is a separate document with no access to your\ntext colour, and will paint black.\n\nEvery mark fills the same 24x24 box, so a row of them lines up, but the artwork\ninside does not carry equal weight. Painted area runs from roughly a third of\nthe box (Impro, Mu, Taproot) to all of it (Lea, Red Dwarf). That is the brands'\nown proportions rather than something the catalog flattens, so nudge individual\nmarks in your own CSS if you want them optically even.\n\n### Accessibility\n\nThe marks carry no title or description, and a handful set `aria-hidden` while\nmost do not. Be explicit at the call site rather than relying on what happens to\nbe in the markup.\n\nBeside a visible client name the mark is decorative, so hide it:\n\n```html\n<span class=\"waypoint-icon\" aria-hidden=\"true\"><!-- mark --></span>\n```\n\nStanding alone as the whole of a link or button, it needs an accessible name:\n\n```html\n<a href=\"…\" aria-label=\"Open in Bluesky\">\n  <span class=\"waypoint-icon\"><!-- mark --></span>\n</a>\n```\n\n### Trademarks\n\nThese are third-party brand marks, reproduced so a picker can identify each\nclient. The MIT licence on this package covers the code, not the trademarks.\nUsing a mark to identify a client is nominative use; using one to suggest that\nclient endorses your product is not. If you represent one of these projects and\nwant a mark changed or removed, open an issue.\n\n## A note on drift\n\nThe canonical logic and icon files (`waypoints.data.ts`, `uriParser.ts`,\n`reverseParsers.ts`, and the React icon catalog) are the single source of truth\ninside the [`aturi.to`](https://github.com/atpota-to/aturi) app under `src/`.\nThis package ships **copies** so it can build standalone, kept in lockstep by a\nsync script. The SVG strings above are generated from that same React catalog by\nthe same script, so the two never disagree about what a waypoint looks like:\n\n```sh\nnpm run sync        # copy the canonical files into the packages\nnpm run sync:check  # exit non-zero if any copy is stale (wire into CI/pre-publish)\n```\n\nIf you change waypoint data or parsing logic in the app, re-run `npm run sync`.\n\n## License\n\nMIT © atpotato, LLC. (The Aturi app itself is GPL-3.0; these packages are\ndual-licensed MIT by the copyright holder to remove the adoption barrier.)\n","readmeFilename":"README.md"}