Developer docs

Add Atmosphere “Open in…” links, smart client recommendations, and AT-URI resolution to your own app — with two MIT-licensed packages and a public API.

Overview

The same waypoint catalog, recommendations, and link logic that power aturi.to are published as two standalone, MIT-licensed npm packages so you can drop them into any Atmosphere (AT Protocol) app:

  • @aturi.to/waypoints — a zero-dependency, framework-agnostic core: the client catalog, per-client “Open in…” link builders, recommendations, and URL ⇄ AT-URI resolution. Works in the browser, Node 18+, and edge runtimes.
  • @aturi.to/waypoints-react — a headless-first React picker UI plus client icons, built on the core. Ships zero CSS by default and is fully themeable.

Both are dual-licensed MIT (the aturi.to app itself is GPL-3.0) to remove the adoption barrier. Prefer not to install anything? The hosted Resolve API does the same work over HTTP.

@aturi.to/waypoints

The zero-dependency core. Turn an AT URI into per-client links, recommend the best client for a record type, and reverse-resolve a pasted URL back into an AT URI.

bash
npm install @aturi.to/waypoints

Resolve an AT URI or a pasted URL

ts
import { resolveAtUri, resolveUrl } from '@aturi.to/waypoints';

// AT URI -> waypoints
const result = resolveAtUri('at://did:plc:abc/app.bsky.feed.post/3k7');
result?.waypoints;   // [{ id: 'anisota', name: 'Anisota', category, url }, ...]
result?.recommended; // { ids: ['bluesky', 'anisota', ...], label: 'Recommended for Posts' }

// Pasted page URL -> waypoints (offline pattern match)
const fromUrl = await resolveUrl('https://bsky.app/profile/alice.bsky.social/post/3k7');

What’s included

  • High-level resolvers: resolveAtUri, resolveUrl, buildWaypointsForParsed, and resolveViaApi (a typed client for the hosted endpoint).
  • Catalog & recommendations: getWaypointDataForType, getCategorizedWaypointsData, getRecommendedWaypointsData, and the raw WAYPOINT_DESTINATIONS_DATA catalog.
  • Parsing: parseURI, parseAtUri, matchSupportedUrl, resolveHandle.

A handful of destinations (pdsls, atp.tools, Margin, Grain, Popfeed) only produce useful URLs when a DID is known — they’re filtered out unless a DID is available, so pass one in or supply a resolveHandle to resolveUrl. Full reference in the package README.

@aturi.to/waypoints-react

A drop-in React “Open in…” picker. Headless-first: it ships zero CSS and emits stable, namespaced styling hooks, so you can use your own design system, opt into the polished theme, or drop down to a hook and render everything yourself. It re-exports the entire core, so a single install gives you the components and the resolvers.

bash
npm install @aturi.to/waypoints-react
# peers (you almost certainly already have react/react-dom):
npm install react react-dom lucide-react

1. Drop-in picker

Renders clean semantic markup with no CSS attached. Every element carries a data-aturi-wp attribute and an aturi-wp-* class; map your own via classNames, pass unstyled to drop the built-ins, or replace rows with the renderWaypoint prop.

tsx
import { WaypointPicker } from '@aturi.to/waypoints-react';

<WaypointPicker
  type="post"
  handle="alice.bsky.social"
  collection="app.bsky.feed.post"
  rkey="3k7qw..."
/>;

2. The useWaypoints hook

For full control, the hook returns render-ready data plus copy / open helpers — no markup at all.

tsx
import { useWaypoints } from '@aturi.to/waypoints-react';

function MyPicker() {
  const { recommended, categories, waypoints, copy, open } = useWaypoints({
    type: 'post',
    handle: 'alice.bsky.social',
    collection: 'app.bsky.feed.post',
    rkey: '3k7qw...',
  });

  return (
    <ul>
      {waypoints.map((w) => (
        <li key={w.id}>
          {w.icon}
          <button onClick={() => open(w.url)}>{w.name}</button>
          <button onClick={() => copy(w.url)}>Copy</button>
        </li>
      ))}
    </ul>
  );
}

3. The polished theme (opt-in)

Want the Aturi look without writing CSS? Import the stylesheet once. It targets the namespaced classes and is fully themeable via --aturi-wp-* CSS custom properties (with light/dark defaults).

tsx
import '@aturi.to/waypoints-react/styles.css';
import { WaypointPicker } from '@aturi.to/waypoints-react';

Server vs. client: the package is a client component (it carries "use client"), so it works out of the box in the Next.js App Router. For framework-agnostic helpers inside a Server Component, import them from @aturi.to/waypoints directly.

Resolve API

Don’t want to install anything? Hit the hosted endpoint from a share sheet, an Apple Shortcut, or any client — no login, no API keys. It returns the resolved waypoints and recommendations for a page URL or an AT URI.

http
GET https://aturi.to/api/resolve?url=<encoded-page-url>
GET https://aturi.to/api/resolve?atUri=at://...

The core package’s resolveViaApi() is a typed client for this endpoint. It’s the right choice from a browser, where fetching arbitrary pages is blocked by CORS.

License

@aturi.to/waypoints and @aturi.to/waypoints-react are MIT © dame.art. The aturi.to web app and browser extension are licensed GPL-3.0 — the packages are intentionally dual-licensed MIT so other Atmosphere developers can adopt them freely.

Building an Atmosphere client and want it in the catalog? See the supported waypoints or open a PR.