@aturi.to/waypoints 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -3
- package/dist/index.cjs +383 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +265 -4
- package/dist/index.d.ts +265 -4
- package/dist/index.js +370 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,16 @@ For a drop-in React picker UI, see [`@aturi.to/waypoints-react`](../waypoints-re
|
|
|
21
21
|
npm install @aturi.to/waypoints
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
Also mirrored to GitHub Packages as `@atpota-to/waypoints` (GitHub only accepts a
|
|
25
|
+
scope matching the repository owner, and it rejects the dot in `aturi.to`). Same
|
|
26
|
+
build, same version. GitHub Packages requires a token even for public packages,
|
|
27
|
+
so installing from there needs an `.npmrc`:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
@atpota-to:registry=https://npm.pkg.github.com
|
|
31
|
+
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # PAT with read:packages
|
|
32
|
+
```
|
|
33
|
+
|
|
24
34
|
## Quick start
|
|
25
35
|
|
|
26
36
|
```ts
|
|
@@ -42,14 +52,140 @@ const fromUrl = await resolveUrl('https://bsky.app/profile/alice.bsky.social/pos
|
|
|
42
52
|
- **Link builders & recommendations**: `getWaypointDataForType`,
|
|
43
53
|
`getCategorizedWaypointsData`, `getRecommendedWaypointsData`,
|
|
44
54
|
`getFeaturedWaypointData`, `waypointActivity`.
|
|
55
|
+
- **Compose intents**: `supportsComposeIntent`, `getComposeIntentUrl`,
|
|
56
|
+
`getComposeIntentAppUrl`, `getComposeIntentTemplate`,
|
|
57
|
+
`getComposeIntentWaypoints`, `describeComposeIntent`.
|
|
45
58
|
- **AT URI parsing**: `parseURI`, `resolveHandle`, `getDisplayName`.
|
|
46
59
|
- **Reverse resolution**: `matchSupportedUrl`, `parseAtUri`, `SUPPORTED_HOSTS`.
|
|
47
60
|
- **High-level resolvers** (`resolve.ts`):
|
|
48
|
-
- `buildWaypointsForParsed(parsed, { did?, excludeSourceId? })`
|
|
49
|
-
- `resolveAtUri(uri)`
|
|
50
|
-
- `resolveUrl(url, { fetchHead?, resolveHandle? })`
|
|
61
|
+
- `buildWaypointsForParsed(parsed, { did?, excludeSourceId?, composeText? })`
|
|
62
|
+
- `resolveAtUri(uri, { composeText? })`
|
|
63
|
+
- `resolveUrl(url, { fetchHead?, resolveHandle?, composeText? })`
|
|
51
64
|
- `resolveViaApi(input, { endpoint? })`: typed client for the hosted
|
|
52
65
|
`aturi.to/api/resolve` endpoint.
|
|
66
|
+
- **Universal links** (`universalLinks.ts`): `buildUniversalLink`,
|
|
67
|
+
`parseUniversalLink`, `isUniversalLink`, `describeUniversalLink`,
|
|
68
|
+
`buildUniversalLinkTags`, `UNIVERSAL_LINK_ORIGIN`.
|
|
69
|
+
|
|
70
|
+
### Compose intents
|
|
71
|
+
|
|
72
|
+
bsky.app can be handed a link that opens its composer pre-filled —
|
|
73
|
+
`/intent/compose?text=…`, documented at
|
|
74
|
+
[docs.bsky.app](https://docs.bsky.app/docs/advanced-guides/intent-links) — and
|
|
75
|
+
the clients forked from the official social app inherit the same route. The
|
|
76
|
+
catalog records which ones do, so you can offer "post this in your client"
|
|
77
|
+
without hardcoding a list.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import {
|
|
81
|
+
WAYPOINT_DESTINATIONS_DATA,
|
|
82
|
+
getComposeIntentUrl,
|
|
83
|
+
getComposeIntentWaypoints,
|
|
84
|
+
supportsComposeIntent,
|
|
85
|
+
} from '@aturi.to/waypoints';
|
|
86
|
+
|
|
87
|
+
getComposeIntentWaypoints().map((w) => w.id);
|
|
88
|
+
// ['anisota', 'bluesky', 'impro', 'blacksky', 'witchsky', 'mu', 'deer', 'northsky']
|
|
89
|
+
|
|
90
|
+
supportsComposeIntent(WAYPOINT_DESTINATIONS_DATA.pdsls); // false
|
|
91
|
+
getComposeIntentUrl(WAYPOINT_DESTINATIONS_DATA.deer, 'hello from my app');
|
|
92
|
+
// 'https://deer.social/intent/compose?text=hello%20from%20my%20app'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Resolver results carry the same information per waypoint as a serializable
|
|
96
|
+
`composeIntent` (`null` when the client has no confirmed route), and
|
|
97
|
+
`resolveAtUri` / `resolveUrl` / `buildWaypointsForParsed` take a `composeText`
|
|
98
|
+
option to pre-fill it:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const { waypoints } = resolveAtUri(uri, { composeText: 'look at this' })!;
|
|
102
|
+
waypoints.find((w) => w.id === 'deer')?.composeIntent;
|
|
103
|
+
// {
|
|
104
|
+
// url: 'https://deer.social/intent/compose?text=look%20at%20this',
|
|
105
|
+
// urlTemplate: 'https://deer.social/intent/compose?text={text}',
|
|
106
|
+
// textParam: 'text',
|
|
107
|
+
// prefillsText: true,
|
|
108
|
+
// }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Two things not to assume. `prefillsText` is `false` for a client that routes
|
|
112
|
+
the intent but drops the text (Impro today), so the link opens an empty
|
|
113
|
+
composer — fine as a jump, useless as a share. And `appUrl` is only set where
|
|
114
|
+
the client publishes a native scheme (`bluesky://intent/compose`), so treat it
|
|
115
|
+
as a bonus rather than a fallback.
|
|
116
|
+
|
|
117
|
+
A missing `composeIntent` means "no route we've confirmed", not proof the
|
|
118
|
+
client lacks one. If a client you maintain handles compose intents,
|
|
119
|
+
[open an issue](https://github.com/atpota-to/aturi/issues) and we'll add it.
|
|
120
|
+
|
|
121
|
+
### Universal links
|
|
122
|
+
|
|
123
|
+
A universal link is the client-agnostic address of a record: drop an
|
|
124
|
+
`aturi.to/…` URL in a DM or a footer and the recipient gets a preview plus every
|
|
125
|
+
client that can open it, instead of being pushed into whichever app you happen
|
|
126
|
+
to use. `buildUniversalLink` returns that address for anything that names a
|
|
127
|
+
record: an AT URI, a handle, a DID, a page URL from any client in the catalog,
|
|
128
|
+
a `ParsedURI`. It's pure, synchronous, and never fetches.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { buildUniversalLink, describeUniversalLink } from '@aturi.to/waypoints';
|
|
132
|
+
|
|
133
|
+
buildUniversalLink('at://did:plc:abc/app.bsky.feed.post/3k7');
|
|
134
|
+
// 'https://aturi.to/profile/did:plc:abc/post/3k7'
|
|
135
|
+
buildUniversalLink('https://bsky.app/profile/alice.bsky.social/post/3k7');
|
|
136
|
+
// 'https://aturi.to/profile/alice.bsky.social/post/3k7'
|
|
137
|
+
buildUniversalLink('@alice.bsky.social');
|
|
138
|
+
// 'https://aturi.to/profile/alice.bsky.social'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For a copy button or a share sheet, `describeUniversalLink` returns the strings
|
|
142
|
+
around the link too:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const link = describeUniversalLink('at://alice.bsky.social/app.bsky.feed.post/3k7');
|
|
146
|
+
link.url; // 'https://aturi.to/profile/alice.bsky.social/post/3k7'
|
|
147
|
+
link.label; // 'Post by @alice.bsky.social'
|
|
148
|
+
link.share; // { title, text, url }; hand it straight to navigator.share()
|
|
149
|
+
link.snippets.markdown; // '[Post by @alice.bsky.social](https://aturi.to/…)'
|
|
150
|
+
link.oembedUrl; // hosted oEmbed endpoint (posts only; null otherwise)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Options on both: `origin` (point at your own deployment), `did` + `preferDid`
|
|
154
|
+
(address links by DID, which survives a handle change), and `params` for
|
|
155
|
+
appended query parameters like `{ ref: 'my-app' }`.
|
|
156
|
+
|
|
157
|
+
Going the other way, `parseUniversalLink` turns an aturi.to URL back into a
|
|
158
|
+
`ParsedURI`. Canonical `/profile/…` links, `/explore/…` views, and the legacy
|
|
159
|
+
bare-path and `at://`-in-path spellings all resolve:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
parseUniversalLink('https://aturi.to/profile/alice.bsky.social/post/3k7');
|
|
163
|
+
// { type: 'post', handle: 'alice.bsky.social', collection: 'app.bsky.feed.post', rkey: '3k7', … }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Making your own pages resolvable
|
|
167
|
+
|
|
168
|
+
If your app renders atproto records, `buildUniversalLinkTags` writes the
|
|
169
|
+
`<head>` tags that let the rest of the Atmosphere find its way back to them:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
buildUniversalLinkTags('at://did:plc:abc/app.bsky.feed.post/3k7').html;
|
|
173
|
+
// <meta name="at:canonical" content="at://did:plc:abc/app.bsky.feed.post/3k7" />
|
|
174
|
+
// <meta name="at:author" content="at://did:plc:abc" />
|
|
175
|
+
// <link rel="alternate" href="at://did:plc:abc/app.bsky.feed.post/3k7" />
|
|
176
|
+
// <link rel="alternate" type="application/json+oembed" href="https://aturi.to/api/oembed?url=…" />
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`at:canonical` is the [AT Tags proposal](https://tangled.org/chrisshank.com/at-tags/).
|
|
180
|
+
Aturi's browser extension reads it off the live page and `/api/resolve` reads it
|
|
181
|
+
off your HTML, so a link to your page resolves into every other client that can
|
|
182
|
+
open the record, without your app being in the catalog at all. The
|
|
183
|
+
`<link rel="alternate" href="at://…">` beside it is the older spelling of the
|
|
184
|
+
same declaration, kept because the resolver still falls back to it. The oEmbed
|
|
185
|
+
pointer is emitted for posts only, since that's all the endpoint renders.
|
|
186
|
+
|
|
187
|
+
They're static strings describing a record you already display, and serving
|
|
188
|
+
them hands nothing to aturi.to.
|
|
53
189
|
|
|
54
190
|
### DID-only waypoints
|
|
55
191
|
|
|
@@ -65,6 +201,14 @@ flag and `resolveViaApi` let you fall back to fetching the page and probing for
|
|
|
65
201
|
`resolveViaApi` is the right choice from a browser, where fetching arbitrary
|
|
66
202
|
pages is blocked by CORS.
|
|
67
203
|
|
|
204
|
+
There's a second hosted endpoint for the catalog itself — what's in it, and
|
|
205
|
+
which clients can do what — for consumers that aren't installing the package:
|
|
206
|
+
|
|
207
|
+
```http
|
|
208
|
+
GET https://aturi.to/api/waypoints
|
|
209
|
+
GET https://aturi.to/api/waypoints?type=post&capability=compose
|
|
210
|
+
```
|
|
211
|
+
|
|
68
212
|
## A note on drift
|
|
69
213
|
|
|
70
214
|
The four canonical logic/icon files (`waypoints.data.ts`, `uriParser.ts`,
|