@aturi.to/waypoints 0.1.0 → 0.1.2
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/LICENSE +1 -1
- package/README.md +6 -6
- package/dist/index.cjs +321 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +320 -39
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @aturi.to/waypoints
|
|
2
2
|
|
|
3
|
-
Aturi's curated catalog of Atmosphere (AT Protocol) clients
|
|
3
|
+
Aturi's curated catalog of Atmosphere (AT Protocol) clients ("waypoints") plus
|
|
4
4
|
the logic to turn an AT URI into per-client "Open in…" links, recommend the best
|
|
5
5
|
client for a record type, and reverse-resolve a pasted URL back into an AT URI.
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ Ships ESM + CJS with full type definitions.
|
|
|
9
9
|
|
|
10
10
|
For a drop-in React picker UI, see [`@aturi.to/waypoints-react`](../waypoints-react).
|
|
11
11
|
|
|
12
|
-
> **Beta
|
|
12
|
+
> **Beta: early release.** This is a `0.x` package that hasn't been
|
|
13
13
|
> thoroughly tested in production yet. Expect rough edges, and possible
|
|
14
14
|
> breaking changes between minor versions while the API settles. Bug reports
|
|
15
15
|
> and feedback are very welcome at
|
|
@@ -48,20 +48,20 @@ const fromUrl = await resolveUrl('https://bsky.app/profile/alice.bsky.social/pos
|
|
|
48
48
|
- `buildWaypointsForParsed(parsed, { did?, excludeSourceId? })`
|
|
49
49
|
- `resolveAtUri(uri)`
|
|
50
50
|
- `resolveUrl(url, { fetchHead?, resolveHandle? })`
|
|
51
|
-
- `resolveViaApi(input, { endpoint? })
|
|
51
|
+
- `resolveViaApi(input, { endpoint? })`: typed client for the hosted
|
|
52
52
|
`aturi.to/api/resolve` endpoint.
|
|
53
53
|
|
|
54
54
|
### DID-only waypoints
|
|
55
55
|
|
|
56
56
|
A handful of destinations (`pdsls`, `atptools`, `margin`, `grain`, `popfeed`)
|
|
57
57
|
only produce useful URLs when a DID is known. They're filtered out unless a DID
|
|
58
|
-
is available
|
|
58
|
+
is available: pass one in, or supply a `resolveHandle` to `resolveUrl`.
|
|
59
59
|
|
|
60
60
|
### Hosted vs. local resolution
|
|
61
61
|
|
|
62
62
|
`resolveUrl` matches URL patterns locally (no network). The optional `fetchHead`
|
|
63
63
|
flag and `resolveViaApi` let you fall back to fetching the page and probing for a
|
|
64
|
-
`<link href="at://…"
|
|
64
|
+
`<link href="at://…">`, useful for sites without a recognizable URL shape.
|
|
65
65
|
`resolveViaApi` is the right choice from a browser, where fetching arbitrary
|
|
66
66
|
pages is blocked by CORS.
|
|
67
67
|
|
|
@@ -82,5 +82,5 @@ If you change waypoint data or parsing logic in the app, re-run `npm run sync`.
|
|
|
82
82
|
|
|
83
83
|
## License
|
|
84
84
|
|
|
85
|
-
MIT ©
|
|
85
|
+
MIT © atpotato, LLC. (The Aturi app itself is GPL-3.0; these packages are
|
|
86
86
|
dual-licensed MIT by the copyright holder to remove the adoption barrier.)
|
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,11 @@ var COMPAT_FAMILIES = {
|
|
|
56
56
|
id: "blento",
|
|
57
57
|
name: "Blento",
|
|
58
58
|
description: "Blento profiles."
|
|
59
|
+
},
|
|
60
|
+
"atproto-explorer": {
|
|
61
|
+
id: "atproto-explorer",
|
|
62
|
+
name: "Record explorers",
|
|
63
|
+
description: "Raw AT Protocol record explorers (pdsls, atp.tools, Aturi Explore) that render any record by its AT URI."
|
|
59
64
|
}
|
|
60
65
|
};
|
|
61
66
|
var COMPAT_FAMILY_ORDER = [
|
|
@@ -69,7 +74,8 @@ var COMPAT_FAMILY_ORDER = [
|
|
|
69
74
|
"streamplace",
|
|
70
75
|
"popfeed",
|
|
71
76
|
"sifa",
|
|
72
|
-
"blento"
|
|
77
|
+
"blento",
|
|
78
|
+
"atproto-explorer"
|
|
73
79
|
];
|
|
74
80
|
var WAYPOINT_DESTINATIONS_DATA = {
|
|
75
81
|
aturi: {
|
|
@@ -195,7 +201,32 @@ var WAYPOINT_DESTINATIONS_DATA = {
|
|
|
195
201
|
return `https://reddwarf.app/profile/${handle}`;
|
|
196
202
|
},
|
|
197
203
|
supportedTypes: ["post", "profile", "list", "record"],
|
|
198
|
-
category: "
|
|
204
|
+
category: "blueskyClients",
|
|
205
|
+
redirectCompat: ["bluesky-social"],
|
|
206
|
+
expectedCollections: ["app.bsky."]
|
|
207
|
+
},
|
|
208
|
+
impro: {
|
|
209
|
+
id: "impro",
|
|
210
|
+
name: "Impro",
|
|
211
|
+
description: (collection) => {
|
|
212
|
+
if (collection === "app.bsky.feed.post") return "View post on impro.social";
|
|
213
|
+
if (collection === "app.bsky.graph.list") return "View list on impro.social";
|
|
214
|
+
return "View profile on impro.social";
|
|
215
|
+
},
|
|
216
|
+
getUrl: (handle, collection, rkey) => {
|
|
217
|
+
if (collection && rkey) {
|
|
218
|
+
if (collection === "app.bsky.feed.post") {
|
|
219
|
+
return `https://impro.social/profile/${handle}/post/${rkey}`;
|
|
220
|
+
}
|
|
221
|
+
if (collection === "app.bsky.graph.list") {
|
|
222
|
+
return `https://impro.social/profile/${handle}/lists/${rkey}`;
|
|
223
|
+
}
|
|
224
|
+
return `https://impro.social/profile/${handle}`;
|
|
225
|
+
}
|
|
226
|
+
return `https://impro.social/profile/${handle}`;
|
|
227
|
+
},
|
|
228
|
+
supportedTypes: ["post", "profile", "list", "record"],
|
|
229
|
+
category: "blueskyClients",
|
|
199
230
|
redirectCompat: ["bluesky-social"],
|
|
200
231
|
expectedCollections: ["app.bsky."]
|
|
201
232
|
},
|
|
@@ -224,7 +255,7 @@ var WAYPOINT_DESTINATIONS_DATA = {
|
|
|
224
255
|
},
|
|
225
256
|
supportedTypes: ["post", "profile", "list", "record"],
|
|
226
257
|
category: "devTools",
|
|
227
|
-
redirectCompat: []
|
|
258
|
+
redirectCompat: ["atproto-explorer"]
|
|
228
259
|
},
|
|
229
260
|
aturiExplore: {
|
|
230
261
|
id: "aturiExplore",
|
|
@@ -245,7 +276,7 @@ var WAYPOINT_DESTINATIONS_DATA = {
|
|
|
245
276
|
},
|
|
246
277
|
supportedTypes: ["post", "profile", "list", "record"],
|
|
247
278
|
category: "devTools",
|
|
248
|
-
redirectCompat: []
|
|
279
|
+
redirectCompat: ["atproto-explorer"]
|
|
249
280
|
},
|
|
250
281
|
atptools: {
|
|
251
282
|
id: "atptools",
|
|
@@ -260,7 +291,7 @@ var WAYPOINT_DESTINATIONS_DATA = {
|
|
|
260
291
|
},
|
|
261
292
|
supportedTypes: ["post", "profile", "list", "record"],
|
|
262
293
|
category: "devTools",
|
|
263
|
-
redirectCompat: []
|
|
294
|
+
redirectCompat: ["atproto-explorer"]
|
|
264
295
|
},
|
|
265
296
|
bluepy: {
|
|
266
297
|
id: "bluepy",
|
|
@@ -305,44 +336,44 @@ var WAYPOINT_DESTINATIONS_DATA = {
|
|
|
305
336
|
redirectCompat: ["bluesky-social"],
|
|
306
337
|
expectedCollections: ["app.bsky."]
|
|
307
338
|
},
|
|
308
|
-
|
|
309
|
-
id: "
|
|
310
|
-
name: "
|
|
339
|
+
deer: {
|
|
340
|
+
id: "deer",
|
|
341
|
+
name: "Deer",
|
|
311
342
|
description: (collection) => {
|
|
312
|
-
if (collection === "app.bsky.feed.post") return "View post on
|
|
313
|
-
if (collection === "app.bsky.graph.list") return "View list on
|
|
314
|
-
return "View profile on
|
|
343
|
+
if (collection === "app.bsky.feed.post") return "View post on deer.social";
|
|
344
|
+
if (collection === "app.bsky.graph.list") return "View list on deer.social";
|
|
345
|
+
return "View profile on deer.social";
|
|
315
346
|
},
|
|
316
347
|
getUrl: (handle, collection, rkey) => {
|
|
317
348
|
if (collection && rkey) {
|
|
318
349
|
if (collection === "app.bsky.feed.post") {
|
|
319
|
-
return `https://
|
|
350
|
+
return `https://deer.social/profile/${handle}/post/${rkey}`;
|
|
320
351
|
}
|
|
321
|
-
return `https://
|
|
352
|
+
return `https://deer.social/profile/${handle}`;
|
|
322
353
|
}
|
|
323
|
-
return `https://
|
|
354
|
+
return `https://deer.social/profile/${handle}`;
|
|
324
355
|
},
|
|
325
356
|
supportedTypes: ["post", "profile", "list", "record"],
|
|
326
357
|
category: "blueskyForks",
|
|
327
358
|
redirectCompat: ["bluesky-social"],
|
|
328
359
|
expectedCollections: ["app.bsky."]
|
|
329
360
|
},
|
|
330
|
-
|
|
331
|
-
id: "
|
|
332
|
-
name: "
|
|
361
|
+
mu: {
|
|
362
|
+
id: "mu",
|
|
363
|
+
name: "Mu",
|
|
333
364
|
description: (collection) => {
|
|
334
|
-
if (collection === "app.bsky.feed.post") return "View post on
|
|
335
|
-
if (collection === "app.bsky.graph.list") return "View list on
|
|
336
|
-
return "View profile on
|
|
365
|
+
if (collection === "app.bsky.feed.post") return "View post on mu.social";
|
|
366
|
+
if (collection === "app.bsky.graph.list") return "View list on mu.social";
|
|
367
|
+
return "View profile on mu.social";
|
|
337
368
|
},
|
|
338
369
|
getUrl: (handle, collection, rkey) => {
|
|
339
370
|
if (collection && rkey) {
|
|
340
371
|
if (collection === "app.bsky.feed.post") {
|
|
341
|
-
return `https://
|
|
372
|
+
return `https://mu.social/profile/${handle}/post/${rkey}`;
|
|
342
373
|
}
|
|
343
|
-
return `https://
|
|
374
|
+
return `https://mu.social/profile/${handle}`;
|
|
344
375
|
}
|
|
345
|
-
return `https://
|
|
376
|
+
return `https://mu.social/profile/${handle}`;
|
|
346
377
|
},
|
|
347
378
|
supportedTypes: ["post", "profile", "list", "record"],
|
|
348
379
|
category: "blueskyForks",
|
|
@@ -555,14 +586,54 @@ var WAYPOINT_DESTINATIONS_DATA = {
|
|
|
555
586
|
category: "publications",
|
|
556
587
|
redirectCompat: ["standard-site"],
|
|
557
588
|
expectedCollections: ["pub.leaflet.", "site.standard."]
|
|
589
|
+
},
|
|
590
|
+
standardReader: {
|
|
591
|
+
id: "standardReader",
|
|
592
|
+
name: "Standard Reader",
|
|
593
|
+
description: (collection) => {
|
|
594
|
+
if (collection?.startsWith("site.standard.") || collection?.startsWith("pub.leaflet.")) {
|
|
595
|
+
return "Read document on standard-reader.app";
|
|
596
|
+
}
|
|
597
|
+
return "View documents on standard-reader.app";
|
|
598
|
+
},
|
|
599
|
+
getUrl: (handle, collection, rkey, did) => {
|
|
600
|
+
const identifier = did || handle;
|
|
601
|
+
if (collection && rkey && (collection.startsWith("site.standard.") || collection.startsWith("pub.leaflet."))) {
|
|
602
|
+
return `https://standard-reader.app/a/${identifier}/${rkey}`;
|
|
603
|
+
}
|
|
604
|
+
return `https://standard-reader.app/u/${identifier}`;
|
|
605
|
+
},
|
|
606
|
+
supportedTypes: ["post", "profile", "list", "record"],
|
|
607
|
+
category: "publications",
|
|
608
|
+
redirectCompat: ["standard-site"],
|
|
609
|
+
expectedCollections: ["pub.leaflet.", "site.standard."]
|
|
610
|
+
},
|
|
611
|
+
taproot: {
|
|
612
|
+
id: "taproot",
|
|
613
|
+
name: "Taproot",
|
|
614
|
+
description: (collection) => {
|
|
615
|
+
if (collection) return "Inspect record on atproto.at";
|
|
616
|
+
return "Browse repo on atproto.at";
|
|
617
|
+
},
|
|
618
|
+
getUrl: (handle, collection, rkey, did) => {
|
|
619
|
+
const identifier = did || handle;
|
|
620
|
+
if (collection && rkey) {
|
|
621
|
+
return `https://atproto.at/uri/at://${identifier}/${collection}/${rkey}`;
|
|
622
|
+
}
|
|
623
|
+
return `https://atproto.at/uri/at://${identifier}`;
|
|
624
|
+
},
|
|
625
|
+
supportedTypes: ["post", "profile", "list", "record"],
|
|
626
|
+
category: "devTools",
|
|
627
|
+
redirectCompat: []
|
|
558
628
|
}
|
|
559
629
|
};
|
|
560
630
|
var WAYPOINT_ORDER = [
|
|
561
631
|
"anisota",
|
|
562
632
|
"bluesky",
|
|
563
633
|
"bluepy",
|
|
564
|
-
"blacksky",
|
|
565
634
|
"reddwarf",
|
|
635
|
+
"impro",
|
|
636
|
+
"blacksky",
|
|
566
637
|
"leaflet",
|
|
567
638
|
"aturi",
|
|
568
639
|
"pinksky",
|
|
@@ -576,12 +647,14 @@ var WAYPOINT_ORDER = [
|
|
|
576
647
|
"anisotaReader",
|
|
577
648
|
"offprint",
|
|
578
649
|
"pckt",
|
|
650
|
+
"standardReader",
|
|
579
651
|
"aturiExplore",
|
|
580
652
|
"pdsls",
|
|
581
653
|
"tangled",
|
|
582
654
|
"atptools",
|
|
655
|
+
"taproot",
|
|
583
656
|
"witchsky",
|
|
584
|
-
"
|
|
657
|
+
"mu",
|
|
585
658
|
"deer"
|
|
586
659
|
];
|
|
587
660
|
function getWaypointDataForType(type) {
|
|
@@ -676,13 +749,13 @@ var RECOMMENDED_WAYPOINTS = {
|
|
|
676
749
|
label: "Recommended for Repos"
|
|
677
750
|
},
|
|
678
751
|
"record": {
|
|
679
|
-
waypointIds: ["aturiExplore", "pdsls", "atptools"],
|
|
752
|
+
waypointIds: ["aturiExplore", "pdsls", "atptools", "taproot"],
|
|
680
753
|
label: "Recommended for Records"
|
|
681
754
|
}
|
|
682
755
|
};
|
|
683
756
|
var RECOMMENDED_NAMESPACE_PREFIXES = {
|
|
684
757
|
"site.standard": {
|
|
685
|
-
waypointIds: ["leaflet", "anisotaReader", "offprint", "pckt", "pdsls"],
|
|
758
|
+
waypointIds: ["leaflet", "standardReader", "anisotaReader", "offprint", "pckt", "pdsls"],
|
|
686
759
|
label: "Recommended for Publications"
|
|
687
760
|
},
|
|
688
761
|
"pub.leaflet": {
|
|
@@ -750,6 +823,26 @@ function waypointActivity(waypoint, repoCollections) {
|
|
|
750
823
|
return "absent";
|
|
751
824
|
}
|
|
752
825
|
|
|
826
|
+
// src/upstreamFetch.ts
|
|
827
|
+
var RETRY_DELAY_MS = 250;
|
|
828
|
+
async function upstreamFetch(url, { timeoutMs = 8e3, retries = 1, ...init } = {}) {
|
|
829
|
+
let lastError;
|
|
830
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
831
|
+
try {
|
|
832
|
+
return await fetch(url, {
|
|
833
|
+
...init,
|
|
834
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
835
|
+
});
|
|
836
|
+
} catch (error) {
|
|
837
|
+
lastError = error;
|
|
838
|
+
if (attempt < retries) {
|
|
839
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
throw lastError;
|
|
844
|
+
}
|
|
845
|
+
|
|
753
846
|
// src/uriParser.ts
|
|
754
847
|
function parseURI(handle, collection, rkey) {
|
|
755
848
|
if (!handle) {
|
|
@@ -791,25 +884,29 @@ function parseURI(handle, collection, rkey) {
|
|
|
791
884
|
error: "Invalid URI structure"
|
|
792
885
|
};
|
|
793
886
|
}
|
|
794
|
-
async function
|
|
887
|
+
async function resolveHandleStatus(handle) {
|
|
795
888
|
if (handle.startsWith("did:")) {
|
|
796
|
-
return handle;
|
|
889
|
+
return { did: handle };
|
|
797
890
|
}
|
|
798
891
|
try {
|
|
799
892
|
const apiUrl = process.env.NEXT_PUBLIC_BSKY_API_URL || "https://public.api.bsky.app";
|
|
800
|
-
const response = await
|
|
893
|
+
const response = await upstreamFetch(
|
|
801
894
|
`${apiUrl}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`
|
|
802
895
|
);
|
|
803
896
|
if (!response.ok) {
|
|
804
|
-
return null;
|
|
897
|
+
return { did: null, reason: response.status >= 500 ? "unavailable" : "not-found" };
|
|
805
898
|
}
|
|
806
899
|
const data = await response.json();
|
|
807
|
-
return data.did
|
|
900
|
+
if (data.did) return { did: data.did };
|
|
901
|
+
return { did: null, reason: "not-found" };
|
|
808
902
|
} catch (error) {
|
|
809
903
|
console.error("Error resolving handle:", error);
|
|
810
|
-
return null;
|
|
904
|
+
return { did: null, reason: "unavailable" };
|
|
811
905
|
}
|
|
812
906
|
}
|
|
907
|
+
async function resolveHandle(handle) {
|
|
908
|
+
return (await resolveHandleStatus(handle)).did;
|
|
909
|
+
}
|
|
813
910
|
function getDisplayName(handle, did) {
|
|
814
911
|
if (handle.startsWith("did:")) {
|
|
815
912
|
return did ? `@${did.slice(0, 16)}...` : "Unknown";
|
|
@@ -822,16 +919,28 @@ var BLUESKY_FAMILY = [
|
|
|
822
919
|
{ source: "bluesky", hosts: ["bsky.app"] },
|
|
823
920
|
{ source: "blacksky", hosts: ["blacksky.community"] },
|
|
824
921
|
{ source: "reddwarf", hosts: ["reddwarf.app"] },
|
|
922
|
+
{ source: "impro", hosts: ["impro.social"] },
|
|
825
923
|
{ source: "witchsky", hosts: ["witchsky.app"] },
|
|
826
|
-
{ source: "catsky", hosts: ["catsky.social"] },
|
|
827
924
|
{ source: "deer", hosts: ["deer.social"] },
|
|
828
|
-
{ source: "
|
|
925
|
+
{ source: "mu", hosts: ["mu.social"] },
|
|
926
|
+
{ source: "anisota", hosts: ["anisota.net"], matchSubdomains: true }
|
|
829
927
|
];
|
|
928
|
+
var SUBDOMAIN_HOSTS = BLUESKY_FAMILY.filter((f) => f.matchSubdomains).flatMap(
|
|
929
|
+
(f) => f.hosts
|
|
930
|
+
);
|
|
830
931
|
function normalizeHost(host) {
|
|
831
932
|
return host.toLowerCase().replace(/^www\./, "");
|
|
832
933
|
}
|
|
934
|
+
function isSubdomainOf(host, base) {
|
|
935
|
+
return host.endsWith(`.${base}`);
|
|
936
|
+
}
|
|
937
|
+
function hostMatchesConfig(host, config) {
|
|
938
|
+
if (config.hosts.includes(host)) return true;
|
|
939
|
+
if (!config.matchSubdomains) return false;
|
|
940
|
+
return config.hosts.some((h) => isSubdomainOf(host, h));
|
|
941
|
+
}
|
|
833
942
|
function matchBlueskyFamily(host, parts) {
|
|
834
|
-
const entry = BLUESKY_FAMILY.find((f) =>
|
|
943
|
+
const entry = BLUESKY_FAMILY.find((f) => hostMatchesConfig(host, f));
|
|
835
944
|
if (!entry) return null;
|
|
836
945
|
if (parts[0] !== "profile" || !parts[1]) return null;
|
|
837
946
|
const handle = parts[1];
|
|
@@ -873,6 +982,19 @@ function matchBlueskyFamily(host, parts) {
|
|
|
873
982
|
}
|
|
874
983
|
};
|
|
875
984
|
}
|
|
985
|
+
if (parts[2] === "document" && parts[3]) {
|
|
986
|
+
return {
|
|
987
|
+
source: entry.source,
|
|
988
|
+
parsed: {
|
|
989
|
+
type: "record",
|
|
990
|
+
uri: `at://${handle}/site.standard.document/${parts[3]}`,
|
|
991
|
+
handle,
|
|
992
|
+
did,
|
|
993
|
+
collection: "site.standard.document",
|
|
994
|
+
rkey: parts[3]
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
}
|
|
876
998
|
return {
|
|
877
999
|
source: entry.source,
|
|
878
1000
|
parsed: {
|
|
@@ -1143,11 +1265,160 @@ function matchBlento(host, parts) {
|
|
|
1143
1265
|
parsed: { type: "profile", uri: `at://${handle}`, handle, did }
|
|
1144
1266
|
};
|
|
1145
1267
|
}
|
|
1268
|
+
function matchStandardReader(host, parts) {
|
|
1269
|
+
if (host !== "standard-reader.app") return null;
|
|
1270
|
+
if (parts[0] === "a" && parts[1] && parts[2]) {
|
|
1271
|
+
const handle = parts[1];
|
|
1272
|
+
const did = handle.startsWith("did:") ? handle : void 0;
|
|
1273
|
+
const rkey = parts[2];
|
|
1274
|
+
return {
|
|
1275
|
+
source: "standardReader",
|
|
1276
|
+
parsed: {
|
|
1277
|
+
type: "record",
|
|
1278
|
+
uri: `at://${handle}/site.standard.document/${rkey}`,
|
|
1279
|
+
handle,
|
|
1280
|
+
did,
|
|
1281
|
+
collection: "site.standard.document",
|
|
1282
|
+
rkey
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
if (parts[0] === "u" && parts[1]) {
|
|
1287
|
+
const handle = parts[1];
|
|
1288
|
+
const did = handle.startsWith("did:") ? handle : void 0;
|
|
1289
|
+
return {
|
|
1290
|
+
source: "standardReader",
|
|
1291
|
+
parsed: { type: "profile", uri: `at://${handle}`, handle, did }
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
function matchFlatRecordHost(host, parts, target) {
|
|
1297
|
+
if (host !== target.host) return null;
|
|
1298
|
+
const [handle, collection, rkey] = parts;
|
|
1299
|
+
if (!handle || !collection || !rkey) return null;
|
|
1300
|
+
if (!collection.includes(".")) return null;
|
|
1301
|
+
const did = handle.startsWith("did:") ? handle : void 0;
|
|
1302
|
+
return {
|
|
1303
|
+
source: target.source,
|
|
1304
|
+
parsed: {
|
|
1305
|
+
type: inferType(collection),
|
|
1306
|
+
uri: `at://${handle}/${collection}/${rkey}`,
|
|
1307
|
+
handle,
|
|
1308
|
+
did,
|
|
1309
|
+
collection,
|
|
1310
|
+
rkey
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
function matchTaproot(host, pathname) {
|
|
1315
|
+
if (host !== "atproto.at") return null;
|
|
1316
|
+
const m = pathname.replace(/^\/+/, "").match(/^uri\/at:\/{1,2}(.+)$/);
|
|
1317
|
+
if (!m) return null;
|
|
1318
|
+
const segments = m[1].split("/").filter(Boolean);
|
|
1319
|
+
if (segments.length === 0) return null;
|
|
1320
|
+
const handle = segments[0];
|
|
1321
|
+
const collection = segments[1];
|
|
1322
|
+
const rkey = segments[2];
|
|
1323
|
+
const did = handle.startsWith("did:") ? handle : void 0;
|
|
1324
|
+
if (collection && rkey) {
|
|
1325
|
+
return {
|
|
1326
|
+
source: "taproot",
|
|
1327
|
+
parsed: {
|
|
1328
|
+
type: inferType(collection),
|
|
1329
|
+
uri: `at://${handle}/${collection}/${rkey}`,
|
|
1330
|
+
handle,
|
|
1331
|
+
did,
|
|
1332
|
+
collection,
|
|
1333
|
+
rkey
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
return {
|
|
1338
|
+
source: "taproot",
|
|
1339
|
+
parsed: { type: "profile", uri: `at://${handle}`, handle, did }
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
function matchAturi(host, parts) {
|
|
1343
|
+
if (host !== "aturi.to") return null;
|
|
1344
|
+
const isIdentifier = (seg) => !!seg && (seg.startsWith("did:") || seg.includes("."));
|
|
1345
|
+
if (parts[0] === "explore" && isIdentifier(parts[1])) {
|
|
1346
|
+
const handle = parts[1];
|
|
1347
|
+
const did = handle.startsWith("did:") ? handle : void 0;
|
|
1348
|
+
const collection = parts[2];
|
|
1349
|
+
const rkey = parts[3];
|
|
1350
|
+
if (collection && rkey) {
|
|
1351
|
+
return {
|
|
1352
|
+
source: "aturiExplore",
|
|
1353
|
+
parsed: {
|
|
1354
|
+
type: inferType(collection),
|
|
1355
|
+
uri: `at://${handle}/${collection}/${rkey}`,
|
|
1356
|
+
handle,
|
|
1357
|
+
did,
|
|
1358
|
+
collection,
|
|
1359
|
+
rkey
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
return {
|
|
1364
|
+
source: "aturiExplore",
|
|
1365
|
+
parsed: { type: "profile", uri: `at://${handle}`, handle, did }
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
if (parts[0] === "profile" && isIdentifier(parts[1])) {
|
|
1369
|
+
const handle = parts[1];
|
|
1370
|
+
const did = handle.startsWith("did:") ? handle : void 0;
|
|
1371
|
+
if (parts[2] === "post" && parts[3]) {
|
|
1372
|
+
return {
|
|
1373
|
+
source: "aturi",
|
|
1374
|
+
parsed: {
|
|
1375
|
+
type: "post",
|
|
1376
|
+
uri: `at://${handle}/app.bsky.feed.post/${parts[3]}`,
|
|
1377
|
+
handle,
|
|
1378
|
+
did,
|
|
1379
|
+
collection: "app.bsky.feed.post",
|
|
1380
|
+
rkey: parts[3]
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
if (parts[2] === "lists" && parts[3]) {
|
|
1385
|
+
return {
|
|
1386
|
+
source: "aturi",
|
|
1387
|
+
parsed: {
|
|
1388
|
+
type: "list",
|
|
1389
|
+
uri: `at://${handle}/app.bsky.graph.list/${parts[3]}`,
|
|
1390
|
+
handle,
|
|
1391
|
+
did,
|
|
1392
|
+
collection: "app.bsky.graph.list",
|
|
1393
|
+
rkey: parts[3]
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
if (parts[2] && parts[2].includes(".") && parts[3]) {
|
|
1398
|
+
return {
|
|
1399
|
+
source: "aturi",
|
|
1400
|
+
parsed: {
|
|
1401
|
+
type: inferType(parts[2]),
|
|
1402
|
+
uri: `at://${handle}/${parts[2]}/${parts[3]}`,
|
|
1403
|
+
handle,
|
|
1404
|
+
did,
|
|
1405
|
+
collection: parts[2],
|
|
1406
|
+
rkey: parts[3]
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
return {
|
|
1411
|
+
source: "aturi",
|
|
1412
|
+
parsed: { type: "profile", uri: `at://${handle}`, handle, did }
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1146
1417
|
function matchSupportedUrl(url) {
|
|
1147
1418
|
const host = normalizeHost(url.hostname);
|
|
1148
1419
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
1149
1420
|
const search = url.searchParams;
|
|
1150
|
-
return matchBlueskyFamily(host, parts) || matchPinksky(host, parts, search) || matchLeaflet(host, parts) || matchTangled(host, parts) || matchMargin(host, parts) || matchPdsls(host, url.pathname) || matchAtpTools(host, url.pathname) || matchBluepy(host, url.pathname) || matchSemble(host, parts) || matchStreamplace(host, parts) || matchGrain(host, parts) || matchPopfeed(host, parts) || matchSifa(host, parts) || matchBlento(host, parts);
|
|
1421
|
+
return matchAturi(host, parts) || matchBlueskyFamily(host, parts) || matchPinksky(host, parts, search) || matchLeaflet(host, parts) || matchTangled(host, parts) || matchMargin(host, parts) || matchPdsls(host, url.pathname) || matchAtpTools(host, url.pathname) || matchBluepy(host, url.pathname) || matchSemble(host, parts) || matchStreamplace(host, parts) || matchGrain(host, parts) || matchPopfeed(host, parts) || matchSifa(host, parts) || matchBlento(host, parts) || matchStandardReader(host, parts) || matchFlatRecordHost(host, parts, { host: "offprint.app", source: "offprint" }) || matchFlatRecordHost(host, parts, { host: "pckt.blog", source: "pckt" }) || matchTaproot(host, url.pathname);
|
|
1151
1422
|
}
|
|
1152
1423
|
function parseAtUri(uri) {
|
|
1153
1424
|
if (!uri.startsWith("at://")) return null;
|
|
@@ -1177,6 +1448,7 @@ function parseAtUri(uri) {
|
|
|
1177
1448
|
};
|
|
1178
1449
|
}
|
|
1179
1450
|
var SUPPORTED_HOSTS = [
|
|
1451
|
+
"aturi.to",
|
|
1180
1452
|
...BLUESKY_FAMILY.flatMap((e) => e.hosts),
|
|
1181
1453
|
"pinkleap.app",
|
|
1182
1454
|
"leaflet.pub",
|
|
@@ -1190,8 +1462,17 @@ var SUPPORTED_HOSTS = [
|
|
|
1190
1462
|
"grain.social",
|
|
1191
1463
|
"popfeed.social",
|
|
1192
1464
|
"sifa.id",
|
|
1193
|
-
"blento.app"
|
|
1465
|
+
"blento.app",
|
|
1466
|
+
"standard-reader.app",
|
|
1467
|
+
"offprint.app",
|
|
1468
|
+
"pckt.blog",
|
|
1469
|
+
"atproto.at"
|
|
1194
1470
|
];
|
|
1471
|
+
function isSupportedHost(host) {
|
|
1472
|
+
const h = normalizeHost(host);
|
|
1473
|
+
if (SUPPORTED_HOSTS.includes(h)) return true;
|
|
1474
|
+
return SUBDOMAIN_HOSTS.some((base) => isSubdomainOf(h, base));
|
|
1475
|
+
}
|
|
1195
1476
|
|
|
1196
1477
|
// src/resolve.ts
|
|
1197
1478
|
var DID_REQUIRED_WAYPOINTS = /* @__PURE__ */ new Set([
|
|
@@ -1339,11 +1620,13 @@ exports.getFeaturedWaypointData = getFeaturedWaypointData;
|
|
|
1339
1620
|
exports.getRecommendedWaypointsData = getRecommendedWaypointsData;
|
|
1340
1621
|
exports.getWaypointCountData = getWaypointCountData;
|
|
1341
1622
|
exports.getWaypointDataForType = getWaypointDataForType;
|
|
1623
|
+
exports.isSupportedHost = isSupportedHost;
|
|
1342
1624
|
exports.matchSupportedUrl = matchSupportedUrl;
|
|
1343
1625
|
exports.parseAtUri = parseAtUri;
|
|
1344
1626
|
exports.parseURI = parseURI;
|
|
1345
1627
|
exports.resolveAtUri = resolveAtUri;
|
|
1346
1628
|
exports.resolveHandle = resolveHandle;
|
|
1629
|
+
exports.resolveHandleStatus = resolveHandleStatus;
|
|
1347
1630
|
exports.resolveUrl = resolveUrl;
|
|
1348
1631
|
exports.resolveViaApi = resolveViaApi;
|
|
1349
1632
|
exports.waypointActivity = waypointActivity;
|