@aturi.to/waypoints 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,1351 @@
1
+ 'use strict';
2
+
3
+ // src/waypoints.data.ts
4
+ var COMPAT_FAMILIES = {
5
+ "bluesky-social": {
6
+ id: "bluesky-social",
7
+ name: "Bluesky clients",
8
+ description: "Apps that render bsky posts, profiles, and lists at /profile/:handle."
9
+ },
10
+ "standard-site": {
11
+ id: "standard-site",
12
+ name: "Publications",
13
+ description: "Readers for Standard Site and Leaflet publications."
14
+ },
15
+ tangled: {
16
+ id: "tangled",
17
+ name: "Tangled",
18
+ description: "Tangled repositories and related records."
19
+ },
20
+ margin: {
21
+ id: "margin",
22
+ name: "Margin",
23
+ description: "Annotations, highlights, and bookmarks on margin.at."
24
+ },
25
+ grain: {
26
+ id: "grain",
27
+ name: "Grain",
28
+ description: "Photo galleries on grain.social."
29
+ },
30
+ pinksky: {
31
+ id: "pinksky",
32
+ name: "Pinkleap",
33
+ description: "Pinkleap browsing experience."
34
+ },
35
+ semble: {
36
+ id: "semble",
37
+ name: "Semble",
38
+ description: "Semble profiles."
39
+ },
40
+ streamplace: {
41
+ id: "streamplace",
42
+ name: "Streamplace",
43
+ description: "Streamplace profiles."
44
+ },
45
+ popfeed: {
46
+ id: "popfeed",
47
+ name: "Popfeed",
48
+ description: "Popfeed profiles."
49
+ },
50
+ sifa: {
51
+ id: "sifa",
52
+ name: "Sifa",
53
+ description: "Sifa profiles."
54
+ },
55
+ blento: {
56
+ id: "blento",
57
+ name: "Blento",
58
+ description: "Blento profiles."
59
+ }
60
+ };
61
+ var COMPAT_FAMILY_ORDER = [
62
+ "bluesky-social",
63
+ "standard-site",
64
+ "pinksky",
65
+ "tangled",
66
+ "margin",
67
+ "grain",
68
+ "semble",
69
+ "streamplace",
70
+ "popfeed",
71
+ "sifa",
72
+ "blento"
73
+ ];
74
+ var WAYPOINT_DESTINATIONS_DATA = {
75
+ aturi: {
76
+ id: "aturi",
77
+ name: "Aturi",
78
+ description: (collection) => {
79
+ if (collection === "app.bsky.feed.post") return "View post on aturi.to";
80
+ if (collection === "app.bsky.graph.list") return "View list on aturi.to";
81
+ if (collection?.startsWith("site.standard.") || collection?.startsWith("pub.leaflet.")) {
82
+ return "View document on aturi.to";
83
+ }
84
+ if (collection) return "View record on aturi.to";
85
+ return "View profile on aturi.to";
86
+ },
87
+ getUrl: (handle, collection, rkey, did) => {
88
+ if (collection && rkey) {
89
+ if (collection === "app.bsky.feed.post") {
90
+ return `https://aturi.to/profile/${handle}/post/${rkey}`;
91
+ }
92
+ if (collection === "app.bsky.graph.list") {
93
+ return `https://aturi.to/profile/${handle}/lists/${rkey}`;
94
+ }
95
+ const identifier = did || handle;
96
+ return `https://aturi.to/profile/${identifier}/${collection}/${rkey}`;
97
+ }
98
+ return `https://aturi.to/profile/${handle}`;
99
+ },
100
+ supportedTypes: ["post", "profile", "list", "record"],
101
+ category: "atmosphereApps",
102
+ redirectCompat: ["bluesky-social", "standard-site"]
103
+ },
104
+ anisota: {
105
+ id: "anisota",
106
+ name: "Anisota",
107
+ description: (collection) => {
108
+ if (collection === "app.bsky.feed.post") return "View post on anisota.net";
109
+ if (collection === "app.bsky.graph.list") return "View list on anisota.net";
110
+ if (collection?.startsWith("site.standard.") || collection?.startsWith("pub.leaflet.")) {
111
+ return "View document on anisota.net";
112
+ }
113
+ return "View profile on anisota.net";
114
+ },
115
+ getUrl: (handle, collection, rkey, did) => {
116
+ if (collection && rkey) {
117
+ if (collection === "app.bsky.feed.post") {
118
+ return `https://anisota.net/profile/${handle}/post/${rkey}`;
119
+ }
120
+ if (collection === "app.bsky.graph.list") {
121
+ return `https://anisota.net/profile/${handle}/lists/${rkey}`;
122
+ }
123
+ if (collection.startsWith("site.standard.") || collection.startsWith("pub.leaflet.")) {
124
+ const identifier = did || handle;
125
+ return `https://anisota.net/profile/${identifier}/document/${rkey}`;
126
+ }
127
+ return `https://anisota.net/profile/${handle}`;
128
+ }
129
+ return `https://anisota.net/profile/${handle}`;
130
+ },
131
+ supportedTypes: ["post", "profile", "list", "record"],
132
+ category: "blueskyClients",
133
+ redirectCompat: ["bluesky-social", "standard-site"],
134
+ expectedCollections: ["app.bsky.", "net.anisota."]
135
+ },
136
+ bluesky: {
137
+ id: "bluesky",
138
+ name: "Bluesky",
139
+ description: (collection) => {
140
+ if (collection === "app.bsky.feed.post") return "View post on bsky.app";
141
+ if (collection === "app.bsky.graph.list") return "View list on bsky.app";
142
+ return "View profile on bsky.app";
143
+ },
144
+ getUrl: (handle, collection, rkey) => {
145
+ if (collection && rkey) {
146
+ if (collection === "app.bsky.feed.post") {
147
+ return `https://bsky.app/profile/${handle}/post/${rkey}`;
148
+ }
149
+ return `https://bsky.app/profile/${handle}`;
150
+ }
151
+ return `https://bsky.app/profile/${handle}`;
152
+ },
153
+ supportedTypes: ["post", "profile", "list", "record"],
154
+ category: "blueskyClients",
155
+ redirectCompat: ["bluesky-social"],
156
+ expectedCollections: ["app.bsky."]
157
+ },
158
+ blacksky: {
159
+ id: "blacksky",
160
+ name: "Blacksky",
161
+ description: (collection) => {
162
+ if (collection === "app.bsky.feed.post") return "View post on blacksky.community";
163
+ if (collection === "app.bsky.graph.list") return "View list on blacksky.community";
164
+ return "View profile on blacksky.community";
165
+ },
166
+ getUrl: (handle, collection, rkey) => {
167
+ if (collection && rkey) {
168
+ if (collection === "app.bsky.feed.post") {
169
+ return `https://blacksky.community/profile/${handle}/post/${rkey}`;
170
+ }
171
+ return `https://blacksky.community/profile/${handle}`;
172
+ }
173
+ return `https://blacksky.community/profile/${handle}`;
174
+ },
175
+ supportedTypes: ["post", "profile", "list", "record"],
176
+ category: "blueskyForks",
177
+ redirectCompat: ["bluesky-social"],
178
+ expectedCollections: ["app.bsky."]
179
+ },
180
+ reddwarf: {
181
+ id: "reddwarf",
182
+ name: "Red Dwarf",
183
+ description: (collection) => {
184
+ if (collection === "app.bsky.feed.post") return "View post on reddwarf.app";
185
+ if (collection === "app.bsky.graph.list") return "View list on reddwarf.app";
186
+ return "View profile on reddwarf.app";
187
+ },
188
+ getUrl: (handle, collection, rkey) => {
189
+ if (collection && rkey) {
190
+ if (collection === "app.bsky.feed.post") {
191
+ return `https://reddwarf.app/profile/${handle}/post/${rkey}`;
192
+ }
193
+ return `https://reddwarf.app/profile/${handle}`;
194
+ }
195
+ return `https://reddwarf.app/profile/${handle}`;
196
+ },
197
+ supportedTypes: ["post", "profile", "list", "record"],
198
+ category: "blueskyForks",
199
+ redirectCompat: ["bluesky-social"],
200
+ expectedCollections: ["app.bsky."]
201
+ },
202
+ leaflet: {
203
+ id: "leaflet",
204
+ name: "Leaflet",
205
+ description: "View profile on leaflet.pub",
206
+ getUrl: (handle) => {
207
+ return `https://leaflet.pub/p/${handle}`;
208
+ },
209
+ supportedTypes: ["post", "profile", "list", "record"],
210
+ category: "publications",
211
+ redirectCompat: ["standard-site"],
212
+ expectedCollections: ["pub.leaflet.", "site.standard."]
213
+ },
214
+ pdsls: {
215
+ id: "pdsls",
216
+ name: "PDSls",
217
+ description: "View raw record on pdsls.dev",
218
+ getUrl: (handle, collection, rkey, did) => {
219
+ const identifier = did || handle;
220
+ if (collection && rkey) {
221
+ return `https://pdsls.dev/at://${identifier}/${collection}/${rkey}`;
222
+ }
223
+ return `https://pdsls.dev/at://${identifier}`;
224
+ },
225
+ supportedTypes: ["post", "profile", "list", "record"],
226
+ category: "devTools",
227
+ redirectCompat: []
228
+ },
229
+ aturiExplore: {
230
+ id: "aturiExplore",
231
+ name: "Aturi Explore",
232
+ description: (collection) => {
233
+ if (collection) return "Inspect record on aturi.to/explore";
234
+ return "Browse repo on aturi.to/explore";
235
+ },
236
+ getUrl: (handle, collection, rkey, did) => {
237
+ const identifier = did || handle;
238
+ if (collection && rkey) {
239
+ return `https://aturi.to/explore/${identifier}/${collection}/${encodeURIComponent(rkey)}`;
240
+ }
241
+ if (collection) {
242
+ return `https://aturi.to/explore/${identifier}/${collection}`;
243
+ }
244
+ return `https://aturi.to/explore/${identifier}`;
245
+ },
246
+ supportedTypes: ["post", "profile", "list", "record"],
247
+ category: "devTools",
248
+ redirectCompat: []
249
+ },
250
+ atptools: {
251
+ id: "atptools",
252
+ name: "atp.tools",
253
+ description: "View raw record on atp.tools",
254
+ getUrl: (handle, collection, rkey, did) => {
255
+ const identifier = did || handle;
256
+ if (collection && rkey) {
257
+ return `https://atp.tools/at:/${identifier}/${collection}/${rkey}`;
258
+ }
259
+ return `https://atp.tools/at:/${identifier}`;
260
+ },
261
+ supportedTypes: ["post", "profile", "list", "record"],
262
+ category: "devTools",
263
+ redirectCompat: []
264
+ },
265
+ bluepy: {
266
+ id: "bluepy",
267
+ name: "Bluepy",
268
+ description: (collection) => {
269
+ if (collection === "app.bsky.feed.post") return "View post on bluepy.social";
270
+ if (collection === "app.bsky.graph.list") return "View list on bluepy.social";
271
+ if (collection) return "View record on bluepy.social";
272
+ return "View profile on bluepy.social";
273
+ },
274
+ getUrl: (handle, collection, rkey, did) => {
275
+ const identifier = did || handle;
276
+ if (collection && rkey) {
277
+ return `https://bluepy.social/at://${identifier}/${collection}/${rkey}`;
278
+ }
279
+ return `https://bluepy.social/at://${identifier}/app.bsky.actor.profile/self`;
280
+ },
281
+ supportedTypes: ["post", "profile", "list", "record"],
282
+ category: "blueskyClients",
283
+ redirectCompat: ["bluesky-social"],
284
+ expectedCollections: ["app.bsky."]
285
+ },
286
+ witchsky: {
287
+ id: "witchsky",
288
+ name: "Witchsky",
289
+ description: (collection) => {
290
+ if (collection === "app.bsky.feed.post") return "View post on witchsky.app";
291
+ if (collection === "app.bsky.graph.list") return "View list on witchsky.app";
292
+ return "View profile on witchsky.app";
293
+ },
294
+ getUrl: (handle, collection, rkey) => {
295
+ if (collection && rkey) {
296
+ if (collection === "app.bsky.feed.post") {
297
+ return `https://witchsky.app/profile/${handle}/post/${rkey}`;
298
+ }
299
+ return `https://witchsky.app/profile/${handle}`;
300
+ }
301
+ return `https://witchsky.app/profile/${handle}`;
302
+ },
303
+ supportedTypes: ["post", "profile", "list", "record"],
304
+ category: "blueskyForks",
305
+ redirectCompat: ["bluesky-social"],
306
+ expectedCollections: ["app.bsky."]
307
+ },
308
+ catsky: {
309
+ id: "catsky",
310
+ name: "Catsky",
311
+ description: (collection) => {
312
+ if (collection === "app.bsky.feed.post") return "View post on catsky.social";
313
+ if (collection === "app.bsky.graph.list") return "View list on catsky.social";
314
+ return "View profile on catsky.social";
315
+ },
316
+ getUrl: (handle, collection, rkey) => {
317
+ if (collection && rkey) {
318
+ if (collection === "app.bsky.feed.post") {
319
+ return `https://catsky.social/profile/${handle}/post/${rkey}`;
320
+ }
321
+ return `https://catsky.social/profile/${handle}`;
322
+ }
323
+ return `https://catsky.social/profile/${handle}`;
324
+ },
325
+ supportedTypes: ["post", "profile", "list", "record"],
326
+ category: "blueskyForks",
327
+ redirectCompat: ["bluesky-social"],
328
+ expectedCollections: ["app.bsky."]
329
+ },
330
+ deer: {
331
+ id: "deer",
332
+ name: "Deer",
333
+ description: (collection) => {
334
+ if (collection === "app.bsky.feed.post") return "View post on deer.social";
335
+ if (collection === "app.bsky.graph.list") return "View list on deer.social";
336
+ return "View profile on deer.social";
337
+ },
338
+ getUrl: (handle, collection, rkey) => {
339
+ if (collection && rkey) {
340
+ if (collection === "app.bsky.feed.post") {
341
+ return `https://deer.social/profile/${handle}/post/${rkey}`;
342
+ }
343
+ return `https://deer.social/profile/${handle}`;
344
+ }
345
+ return `https://deer.social/profile/${handle}`;
346
+ },
347
+ supportedTypes: ["post", "profile", "list", "record"],
348
+ category: "blueskyForks",
349
+ redirectCompat: ["bluesky-social"],
350
+ expectedCollections: ["app.bsky."]
351
+ },
352
+ tangled: {
353
+ id: "tangled",
354
+ name: "Tangled",
355
+ description: "View profile on tangled.org",
356
+ getUrl: (handle) => {
357
+ return `https://tangled.org/${handle}`;
358
+ },
359
+ supportedTypes: ["post", "profile", "list", "record"],
360
+ category: "atmosphereApps",
361
+ redirectCompat: ["tangled"],
362
+ expectedCollections: ["sh.tangled."]
363
+ },
364
+ pinksky: {
365
+ id: "pinksky",
366
+ name: "Pinkleap",
367
+ description: (collection) => {
368
+ if (collection === "app.bsky.feed.post") return "View post on pinkleap.app";
369
+ return "View profile on pinkleap.app";
370
+ },
371
+ getUrl: (handle, collection, rkey, did) => {
372
+ if (collection && rkey) {
373
+ if (collection === "app.bsky.feed.post") {
374
+ const identifier = did || handle;
375
+ const atUri = `at://${identifier}/${collection}/${rkey}`;
376
+ const encodedUri = encodeURIComponent(atUri);
377
+ const encodedDid = encodeURIComponent(identifier);
378
+ return `https://pinkleap.app/feed?uri=${encodedUri}&src=profile&index=1&did=${encodedDid}&showThreads=${encodedDid}`;
379
+ }
380
+ return `https://pinkleap.app/profile/${handle}`;
381
+ }
382
+ return `https://pinkleap.app/profile/${handle}`;
383
+ },
384
+ supportedTypes: ["post", "profile", "list", "record"],
385
+ category: "atmosphereApps",
386
+ redirectCompat: ["pinksky"],
387
+ expectedCollections: ["app.bsky."]
388
+ },
389
+ margin: {
390
+ id: "margin",
391
+ name: "Margin",
392
+ description: (collection) => {
393
+ if (collection === "at.margin.annotation") return "View annotation on margin.at";
394
+ if (collection === "at.margin.highlight") return "View highlight on margin.at";
395
+ if (collection === "at.margin.bookmark") return "View bookmark on margin.at";
396
+ if (collection?.startsWith("at.margin.")) return "View on margin.at";
397
+ return "View profile on margin.at";
398
+ },
399
+ getUrl: (handle, collection, rkey, did) => {
400
+ if (collection && rkey && collection.startsWith("at.margin.")) {
401
+ const recordType = collection.replace("at.margin.", "");
402
+ const identifier2 = did || handle;
403
+ if (recordType === "annotation" || recordType === "highlight" || recordType === "bookmark") {
404
+ const handleLooksLikeDomain = handle.includes(".") && !handle.startsWith("did:");
405
+ const domain = handleLooksLikeDomain ? handle : identifier2;
406
+ return `https://margin.at/${domain}/${recordType}/${rkey}`;
407
+ }
408
+ return `https://margin.at/profile/${identifier2}`;
409
+ }
410
+ const identifier = did || handle;
411
+ return `https://margin.at/profile/${identifier}`;
412
+ },
413
+ supportedTypes: ["post", "profile", "list", "record"],
414
+ category: "atmosphereApps",
415
+ redirectCompat: ["margin"],
416
+ expectedCollections: ["at.margin."]
417
+ },
418
+ semble: {
419
+ id: "semble",
420
+ name: "Semble",
421
+ description: "View profile on semble.so",
422
+ getUrl: (handle) => {
423
+ return `https://semble.so/profile/${handle}`;
424
+ },
425
+ supportedTypes: ["post", "profile", "list", "record"],
426
+ category: "atmosphereApps",
427
+ redirectCompat: ["semble"],
428
+ expectedCollections: ["so.semble."]
429
+ },
430
+ streamplace: {
431
+ id: "streamplace",
432
+ name: "Streamplace",
433
+ description: "View profile on stream.place",
434
+ getUrl: (handle) => {
435
+ return `https://stream.place/${handle}`;
436
+ },
437
+ supportedTypes: ["post", "profile", "list", "record"],
438
+ category: "atmosphereApps",
439
+ redirectCompat: ["streamplace"],
440
+ expectedCollections: ["place.stream."]
441
+ },
442
+ grain: {
443
+ id: "grain",
444
+ name: "Grain",
445
+ description: (collection) => {
446
+ if (collection === "social.grain.gallery") return "View gallery on grain.social";
447
+ return "View profile on grain.social";
448
+ },
449
+ getUrl: (handle, collection, rkey, did) => {
450
+ const identifier = did || handle;
451
+ if (collection === "social.grain.gallery" && rkey) {
452
+ return `https://grain.social/profile/${identifier}/gallery/${rkey}`;
453
+ }
454
+ return `https://grain.social/profile/${identifier}`;
455
+ },
456
+ supportedTypes: ["post", "profile", "list", "record"],
457
+ category: "atmosphereApps",
458
+ redirectCompat: ["grain"],
459
+ expectedCollections: ["social.grain."]
460
+ },
461
+ popfeed: {
462
+ id: "popfeed",
463
+ name: "Popfeed",
464
+ description: "View profile on popfeed.social",
465
+ getUrl: (handle, collection, rkey, did) => {
466
+ const identifier = did || handle;
467
+ return `https://popfeed.social/profile/${identifier}`;
468
+ },
469
+ supportedTypes: ["post", "profile", "list", "record"],
470
+ category: "atmosphereApps",
471
+ redirectCompat: ["popfeed"],
472
+ expectedCollections: ["social.popfeed."]
473
+ },
474
+ sifa: {
475
+ id: "sifa",
476
+ name: "Sifa",
477
+ description: "View profile on sifa.id",
478
+ getUrl: (handle) => {
479
+ return `https://sifa.id/p/${handle}`;
480
+ },
481
+ supportedTypes: ["post", "profile", "list", "record"],
482
+ category: "atmosphereApps",
483
+ redirectCompat: ["sifa"],
484
+ expectedCollections: ["id.sifa."]
485
+ },
486
+ blento: {
487
+ id: "blento",
488
+ name: "Blento",
489
+ description: "View profile on blento.app",
490
+ getUrl: (handle) => {
491
+ return `https://blento.app/${handle}`;
492
+ },
493
+ supportedTypes: ["post", "profile", "list", "record"],
494
+ category: "atmosphereApps",
495
+ redirectCompat: ["blento"],
496
+ expectedCollections: ["app.blento."]
497
+ },
498
+ anisotaReader: {
499
+ id: "anisotaReader",
500
+ name: "Anisota Reader",
501
+ description: (collection) => {
502
+ if (collection?.startsWith("site.standard.") || collection?.startsWith("pub.leaflet.")) {
503
+ return "Read document on anisota.net";
504
+ }
505
+ return "View publications on anisota.net";
506
+ },
507
+ getUrl: (handle, collection, rkey, did) => {
508
+ if (collection && rkey) {
509
+ if (collection.startsWith("site.standard.") || collection.startsWith("pub.leaflet.")) {
510
+ const identifier = did || handle;
511
+ return `https://anisota.net/profile/${identifier}/document/${rkey}`;
512
+ }
513
+ }
514
+ return `https://anisota.net/profile/${handle}`;
515
+ },
516
+ supportedTypes: ["post", "profile", "list", "record"],
517
+ category: "publications",
518
+ redirectCompat: ["standard-site"],
519
+ expectedCollections: ["pub.leaflet.", "site.standard."]
520
+ },
521
+ offprint: {
522
+ id: "offprint",
523
+ name: "Offprint",
524
+ description: (collection) => {
525
+ if (collection?.startsWith("site.standard.") || collection?.startsWith("pub.leaflet.")) {
526
+ return "Read document on offprint.app";
527
+ }
528
+ return "View on offprint.app";
529
+ },
530
+ getUrl: (handle, collection, rkey, did) => {
531
+ if (!collection || !rkey) return null;
532
+ const identifier = did || handle;
533
+ return `https://offprint.app/${identifier}/${collection}/${rkey}`;
534
+ },
535
+ supportedTypes: ["post", "profile", "list", "record"],
536
+ category: "publications",
537
+ redirectCompat: ["standard-site"],
538
+ expectedCollections: ["pub.leaflet.", "site.standard."]
539
+ },
540
+ pckt: {
541
+ id: "pckt",
542
+ name: "pckt",
543
+ description: (collection) => {
544
+ if (collection?.startsWith("site.standard.") || collection?.startsWith("pub.leaflet.")) {
545
+ return "Read document on pckt.blog";
546
+ }
547
+ return "View on pckt.blog";
548
+ },
549
+ getUrl: (handle, collection, rkey, did) => {
550
+ if (!collection || !rkey) return null;
551
+ const identifier = did || handle;
552
+ return `https://pckt.blog/${identifier}/${collection}/${rkey}`;
553
+ },
554
+ supportedTypes: ["post", "profile", "list", "record"],
555
+ category: "publications",
556
+ redirectCompat: ["standard-site"],
557
+ expectedCollections: ["pub.leaflet.", "site.standard."]
558
+ }
559
+ };
560
+ var WAYPOINT_ORDER = [
561
+ "anisota",
562
+ "bluesky",
563
+ "bluepy",
564
+ "blacksky",
565
+ "reddwarf",
566
+ "leaflet",
567
+ "aturi",
568
+ "pinksky",
569
+ "margin",
570
+ "semble",
571
+ "streamplace",
572
+ "grain",
573
+ "popfeed",
574
+ "sifa",
575
+ "blento",
576
+ "anisotaReader",
577
+ "offprint",
578
+ "pckt",
579
+ "aturiExplore",
580
+ "pdsls",
581
+ "tangled",
582
+ "atptools",
583
+ "witchsky",
584
+ "catsky",
585
+ "deer"
586
+ ];
587
+ function getWaypointDataForType(type) {
588
+ return WAYPOINT_ORDER.map((id) => WAYPOINT_DESTINATIONS_DATA[id]).filter((waypoint) => waypoint.supportedTypes.includes(type));
589
+ }
590
+ function getWaypointCountData() {
591
+ return WAYPOINT_ORDER.length;
592
+ }
593
+ var WAYPOINT_CATEGORIES_DATA = {
594
+ blueskyClients: {
595
+ id: "blueskyClients",
596
+ name: "Bluesky Clients",
597
+ description: "Official and alternative Bluesky clients",
598
+ defaultWaypointId: "bluesky",
599
+ subcategories: [{
600
+ id: "blueskyForks",
601
+ name: "Bluesky Forks",
602
+ description: "Community-built Bluesky variants",
603
+ defaultWaypointId: "blacksky"
604
+ }]
605
+ },
606
+ blueskyForks: {
607
+ id: "blueskyForks",
608
+ name: "Bluesky Forks",
609
+ description: "Community-built Bluesky variants",
610
+ defaultWaypointId: "blacksky"
611
+ },
612
+ publications: {
613
+ id: "publications",
614
+ name: "Publications",
615
+ description: "Readers for Standard Site and Leaflet publications",
616
+ defaultWaypointId: "leaflet"
617
+ },
618
+ atmosphereApps: {
619
+ id: "atmosphereApps",
620
+ name: "Atmosphere",
621
+ description: "Apps built on the AT Protocol",
622
+ defaultWaypointId: "tangled"
623
+ },
624
+ devTools: {
625
+ id: "devTools",
626
+ name: "Dev Tools",
627
+ description: "Tools for developers and debugging",
628
+ defaultWaypointId: "aturiExplore"
629
+ }
630
+ };
631
+ var CATEGORY_ORDER = [
632
+ "blueskyClients",
633
+ "blueskyForks",
634
+ "publications",
635
+ "atmosphereApps",
636
+ "devTools"
637
+ ];
638
+ function getCategorizedWaypointsData(type) {
639
+ const availableWaypoints = getWaypointDataForType(type);
640
+ const categorized = [];
641
+ const subcategoryIds = /* @__PURE__ */ new Set();
642
+ for (const category of Object.values(WAYPOINT_CATEGORIES_DATA)) {
643
+ for (const subcat of category.subcategories ?? []) {
644
+ subcategoryIds.add(subcat.id);
645
+ }
646
+ }
647
+ for (const categoryId of CATEGORY_ORDER) {
648
+ if (subcategoryIds.has(categoryId)) continue;
649
+ const category = WAYPOINT_CATEGORIES_DATA[categoryId];
650
+ const waypoints = availableWaypoints.filter((w) => w.category === categoryId);
651
+ if (waypoints.length > 0) {
652
+ categorized.push({ category, waypoints });
653
+ }
654
+ }
655
+ return categorized;
656
+ }
657
+ var RECOMMENDED_WAYPOINTS = {
658
+ "app.bsky.feed.post": {
659
+ waypointIds: ["bluesky", "anisota", "blacksky"],
660
+ label: "Recommended for Posts"
661
+ },
662
+ "profile": {
663
+ waypointIds: ["bluesky", "anisota"],
664
+ label: "Recommended for Profiles"
665
+ },
666
+ "app.bsky.graph.list": {
667
+ waypointIds: ["bluesky", "anisota"],
668
+ label: "Recommended for Lists"
669
+ },
670
+ "community.lexicon.calendar.event": {
671
+ waypointIds: ["aturiExplore", "pdsls", "atptools"],
672
+ label: "Recommended for Events"
673
+ },
674
+ "sh.tangled.repo": {
675
+ waypointIds: ["tangled"],
676
+ label: "Recommended for Repos"
677
+ },
678
+ "record": {
679
+ waypointIds: ["aturiExplore", "pdsls", "atptools"],
680
+ label: "Recommended for Records"
681
+ }
682
+ };
683
+ var RECOMMENDED_NAMESPACE_PREFIXES = {
684
+ "site.standard": {
685
+ waypointIds: ["leaflet", "anisotaReader", "offprint", "pckt", "pdsls"],
686
+ label: "Recommended for Publications"
687
+ },
688
+ "pub.leaflet": {
689
+ waypointIds: ["leaflet", "anisotaReader", "offprint", "pckt", "pdsls"],
690
+ label: "Recommended for Publications"
691
+ },
692
+ "sh.tangled": {
693
+ waypointIds: ["tangled", "pdsls", "atptools"],
694
+ label: "Recommended for Tangled"
695
+ },
696
+ "at.margin": {
697
+ waypointIds: ["margin", "pdsls", "atptools"],
698
+ label: "Recommended for Margin"
699
+ },
700
+ "social.grain": {
701
+ waypointIds: ["grain", "pdsls", "atptools"],
702
+ label: "Recommended for Grain"
703
+ }
704
+ };
705
+ function findNamespacePrefixMatch(collection) {
706
+ const segments = collection.split(".");
707
+ for (let i = segments.length - 1; i >= 2; i--) {
708
+ const prefix = segments.slice(0, i).join(".");
709
+ if (RECOMMENDED_NAMESPACE_PREFIXES[prefix]) {
710
+ return RECOMMENDED_NAMESPACE_PREFIXES[prefix];
711
+ }
712
+ }
713
+ return void 0;
714
+ }
715
+ function getRecommendedWaypointsData(type, collection) {
716
+ let config;
717
+ if (collection && RECOMMENDED_WAYPOINTS[collection]) {
718
+ config = RECOMMENDED_WAYPOINTS[collection];
719
+ } else if (collection) {
720
+ config = findNamespacePrefixMatch(collection);
721
+ }
722
+ if (!config && RECOMMENDED_WAYPOINTS[type]) {
723
+ config = RECOMMENDED_WAYPOINTS[type];
724
+ }
725
+ if (!config) {
726
+ config = {
727
+ waypointIds: ["bluesky"],
728
+ label: "Recommended"
729
+ };
730
+ }
731
+ const waypoints = config.waypointIds.map((id) => WAYPOINT_DESTINATIONS_DATA[id]).filter(Boolean);
732
+ return {
733
+ waypoints,
734
+ label: config.label || "Recommended"
735
+ };
736
+ }
737
+ function getFeaturedWaypointData(type, collection) {
738
+ const { waypoints } = getRecommendedWaypointsData(type, collection);
739
+ return waypoints[0] || null;
740
+ }
741
+ function waypointActivity(waypoint, repoCollections) {
742
+ if (!repoCollections) return "unknown";
743
+ const expected = waypoint.expectedCollections;
744
+ if (!expected || expected.length === 0) return "unknown";
745
+ for (const c of repoCollections) {
746
+ for (const prefix of expected) {
747
+ if (c === prefix || c.startsWith(prefix)) return "present";
748
+ }
749
+ }
750
+ return "absent";
751
+ }
752
+
753
+ // src/uriParser.ts
754
+ function parseURI(handle, collection, rkey) {
755
+ if (!handle) {
756
+ return {
757
+ type: "unknown",
758
+ uri: "",
759
+ handle: "",
760
+ error: "Handle or DID is required"
761
+ };
762
+ }
763
+ if (!collection && !rkey) {
764
+ return {
765
+ type: "profile",
766
+ uri: `at://${handle}`,
767
+ handle,
768
+ did: handle.startsWith("did:") ? handle : void 0
769
+ };
770
+ }
771
+ if (collection && rkey) {
772
+ let type = "record";
773
+ if (collection === "app.bsky.feed.post") {
774
+ type = "post";
775
+ } else if (collection === "app.bsky.graph.list") {
776
+ type = "list";
777
+ }
778
+ return {
779
+ type,
780
+ uri: `at://${handle}/${collection}/${rkey}`,
781
+ handle,
782
+ did: handle.startsWith("did:") ? handle : void 0,
783
+ collection,
784
+ rkey
785
+ };
786
+ }
787
+ return {
788
+ type: "unknown",
789
+ uri: "",
790
+ handle,
791
+ error: "Invalid URI structure"
792
+ };
793
+ }
794
+ async function resolveHandle(handle) {
795
+ if (handle.startsWith("did:")) {
796
+ return handle;
797
+ }
798
+ try {
799
+ const apiUrl = process.env.NEXT_PUBLIC_BSKY_API_URL || "https://public.api.bsky.app";
800
+ const response = await fetch(
801
+ `${apiUrl}/xrpc/com.atproto.identity.resolveHandle?handle=${encodeURIComponent(handle)}`
802
+ );
803
+ if (!response.ok) {
804
+ return null;
805
+ }
806
+ const data = await response.json();
807
+ return data.did || null;
808
+ } catch (error) {
809
+ console.error("Error resolving handle:", error);
810
+ return null;
811
+ }
812
+ }
813
+ function getDisplayName(handle, did) {
814
+ if (handle.startsWith("did:")) {
815
+ return did ? `@${did.slice(0, 16)}...` : "Unknown";
816
+ }
817
+ return `@${handle}`;
818
+ }
819
+
820
+ // src/reverseParsers.ts
821
+ var BLUESKY_FAMILY = [
822
+ { source: "bluesky", hosts: ["bsky.app"] },
823
+ { source: "blacksky", hosts: ["blacksky.community"] },
824
+ { source: "reddwarf", hosts: ["reddwarf.app"] },
825
+ { source: "witchsky", hosts: ["witchsky.app"] },
826
+ { source: "catsky", hosts: ["catsky.social"] },
827
+ { source: "deer", hosts: ["deer.social"] },
828
+ { source: "anisota", hosts: ["anisota.net"] }
829
+ ];
830
+ function normalizeHost(host) {
831
+ return host.toLowerCase().replace(/^www\./, "");
832
+ }
833
+ function matchBlueskyFamily(host, parts) {
834
+ const entry = BLUESKY_FAMILY.find((f) => f.hosts.includes(host));
835
+ if (!entry) return null;
836
+ if (parts[0] !== "profile" || !parts[1]) return null;
837
+ const handle = parts[1];
838
+ const did = handle.startsWith("did:") ? handle : void 0;
839
+ if (!parts[2]) {
840
+ return {
841
+ source: entry.source,
842
+ parsed: {
843
+ type: "profile",
844
+ uri: `at://${handle}`,
845
+ handle,
846
+ did
847
+ }
848
+ };
849
+ }
850
+ if (parts[2] === "post" && parts[3]) {
851
+ return {
852
+ source: entry.source,
853
+ parsed: {
854
+ type: "post",
855
+ uri: `at://${handle}/app.bsky.feed.post/${parts[3]}`,
856
+ handle,
857
+ did,
858
+ collection: "app.bsky.feed.post",
859
+ rkey: parts[3]
860
+ }
861
+ };
862
+ }
863
+ if ((parts[2] === "lists" || parts[2] === "list") && parts[3]) {
864
+ return {
865
+ source: entry.source,
866
+ parsed: {
867
+ type: "list",
868
+ uri: `at://${handle}/app.bsky.graph.list/${parts[3]}`,
869
+ handle,
870
+ did,
871
+ collection: "app.bsky.graph.list",
872
+ rkey: parts[3]
873
+ }
874
+ };
875
+ }
876
+ return {
877
+ source: entry.source,
878
+ parsed: {
879
+ type: "profile",
880
+ uri: `at://${handle}`,
881
+ handle,
882
+ did
883
+ }
884
+ };
885
+ }
886
+ function inferType(collection) {
887
+ if (collection === "app.bsky.feed.post") return "post";
888
+ if (collection === "app.bsky.graph.list") return "list";
889
+ return "record";
890
+ }
891
+ function matchPinksky(host, parts, search) {
892
+ if (host !== "pinkleap.app") return null;
893
+ if (parts[0] === "feed") {
894
+ const uri = search.get("uri");
895
+ if (uri && uri.startsWith("at://")) {
896
+ const stripped = uri.slice("at://".length);
897
+ const [handle, collection, rkey] = stripped.split("/");
898
+ if (handle) {
899
+ const did = handle.startsWith("did:") ? handle : void 0;
900
+ if (collection && rkey) {
901
+ return {
902
+ source: "pinksky",
903
+ parsed: {
904
+ type: inferType(collection),
905
+ uri,
906
+ handle,
907
+ did,
908
+ collection,
909
+ rkey
910
+ }
911
+ };
912
+ }
913
+ return {
914
+ source: "pinksky",
915
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
916
+ };
917
+ }
918
+ }
919
+ }
920
+ if (parts[0] === "profile" && parts[1]) {
921
+ const handle = parts[1];
922
+ const did = handle.startsWith("did:") ? handle : void 0;
923
+ return {
924
+ source: "pinksky",
925
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
926
+ };
927
+ }
928
+ return null;
929
+ }
930
+ function matchLeaflet(host, parts) {
931
+ if (host !== "leaflet.pub") return null;
932
+ if (parts[0] !== "p" || !parts[1]) return null;
933
+ const handle = parts[1];
934
+ const did = handle.startsWith("did:") ? handle : void 0;
935
+ return {
936
+ source: "leaflet",
937
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
938
+ };
939
+ }
940
+ function matchTangled(host, parts) {
941
+ if (host !== "tangled.org") return null;
942
+ if (!parts[0]) return null;
943
+ const handle = parts[0];
944
+ const did = handle.startsWith("did:") ? handle : void 0;
945
+ return {
946
+ source: "tangled",
947
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
948
+ };
949
+ }
950
+ function matchMargin(host, parts) {
951
+ if (host !== "margin.at") return null;
952
+ if (parts[0] === "profile" && parts[1]) {
953
+ const handle = parts[1];
954
+ const did = handle.startsWith("did:") ? handle : void 0;
955
+ return {
956
+ source: "margin",
957
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
958
+ };
959
+ }
960
+ if (parts[0] && parts[1] && parts[2]) {
961
+ const recordType = parts[1];
962
+ if (recordType === "annotation" || recordType === "highlight" || recordType === "bookmark") {
963
+ const handle = parts[0];
964
+ const did = handle.startsWith("did:") ? handle : void 0;
965
+ const collection = `at.margin.${recordType}`;
966
+ const rkey = parts[2];
967
+ return {
968
+ source: "margin",
969
+ parsed: {
970
+ type: "record",
971
+ uri: `at://${handle}/${collection}/${rkey}`,
972
+ handle,
973
+ did,
974
+ collection,
975
+ rkey
976
+ }
977
+ };
978
+ }
979
+ }
980
+ return null;
981
+ }
982
+ function parseAtUriPath(pathname) {
983
+ const cleaned = pathname.replace(/^\/+/, "");
984
+ const match = cleaned.match(/^at:\/{1,2}(.+)$/);
985
+ if (!match) return null;
986
+ const rest = match[1];
987
+ const segments = rest.split("/").filter(Boolean);
988
+ if (segments.length === 0) return null;
989
+ return {
990
+ handle: segments[0],
991
+ collection: segments[1],
992
+ rkey: segments[2]
993
+ };
994
+ }
995
+ function matchPdsls(host, pathname) {
996
+ if (host !== "pdsls.dev") return null;
997
+ const atUri = parseAtUriPath(pathname);
998
+ if (!atUri) return null;
999
+ const { handle, collection, rkey } = atUri;
1000
+ const did = handle.startsWith("did:") ? handle : void 0;
1001
+ if (collection && rkey) {
1002
+ return {
1003
+ source: "pdsls",
1004
+ parsed: {
1005
+ type: inferType(collection),
1006
+ uri: `at://${handle}/${collection}/${rkey}`,
1007
+ handle,
1008
+ did,
1009
+ collection,
1010
+ rkey
1011
+ }
1012
+ };
1013
+ }
1014
+ return {
1015
+ source: "pdsls",
1016
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1017
+ };
1018
+ }
1019
+ function matchAtpTools(host, pathname) {
1020
+ if (host !== "atp.tools") return null;
1021
+ const atUri = parseAtUriPath(pathname);
1022
+ if (!atUri) return null;
1023
+ const { handle, collection, rkey } = atUri;
1024
+ const did = handle.startsWith("did:") ? handle : void 0;
1025
+ if (collection && rkey) {
1026
+ return {
1027
+ source: "atptools",
1028
+ parsed: {
1029
+ type: inferType(collection),
1030
+ uri: `at://${handle}/${collection}/${rkey}`,
1031
+ handle,
1032
+ did,
1033
+ collection,
1034
+ rkey
1035
+ }
1036
+ };
1037
+ }
1038
+ return {
1039
+ source: "atptools",
1040
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1041
+ };
1042
+ }
1043
+ function matchBluepy(host, pathname) {
1044
+ if (host !== "bluepy.social") return null;
1045
+ const atUri = parseAtUriPath(pathname);
1046
+ if (!atUri) return null;
1047
+ const { handle, collection, rkey } = atUri;
1048
+ const did = handle.startsWith("did:") ? handle : void 0;
1049
+ if (collection === "app.bsky.actor.profile") {
1050
+ return {
1051
+ source: "bluepy",
1052
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1053
+ };
1054
+ }
1055
+ if (collection && rkey) {
1056
+ return {
1057
+ source: "bluepy",
1058
+ parsed: {
1059
+ type: inferType(collection),
1060
+ uri: `at://${handle}/${collection}/${rkey}`,
1061
+ handle,
1062
+ did,
1063
+ collection,
1064
+ rkey
1065
+ }
1066
+ };
1067
+ }
1068
+ return {
1069
+ source: "bluepy",
1070
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1071
+ };
1072
+ }
1073
+ function matchSemble(host, parts) {
1074
+ if (host !== "semble.so") return null;
1075
+ if (parts[0] !== "profile" || !parts[1]) return null;
1076
+ const handle = parts[1];
1077
+ const did = handle.startsWith("did:") ? handle : void 0;
1078
+ return {
1079
+ source: "semble",
1080
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1081
+ };
1082
+ }
1083
+ function matchStreamplace(host, parts) {
1084
+ if (host !== "stream.place") return null;
1085
+ if (!parts[0]) return null;
1086
+ const handle = parts[0];
1087
+ const did = handle.startsWith("did:") ? handle : void 0;
1088
+ return {
1089
+ source: "streamplace",
1090
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1091
+ };
1092
+ }
1093
+ function matchGrain(host, parts) {
1094
+ if (host !== "grain.social") return null;
1095
+ if (parts[0] !== "profile" || !parts[1]) return null;
1096
+ const handle = parts[1];
1097
+ const did = handle.startsWith("did:") ? handle : void 0;
1098
+ if (parts[2] === "gallery" && parts[3]) {
1099
+ return {
1100
+ source: "grain",
1101
+ parsed: {
1102
+ type: "record",
1103
+ uri: `at://${handle}/social.grain.gallery/${parts[3]}`,
1104
+ handle,
1105
+ did,
1106
+ collection: "social.grain.gallery",
1107
+ rkey: parts[3]
1108
+ }
1109
+ };
1110
+ }
1111
+ return {
1112
+ source: "grain",
1113
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1114
+ };
1115
+ }
1116
+ function matchPopfeed(host, parts) {
1117
+ if (host !== "popfeed.social") return null;
1118
+ if (parts[0] !== "profile" || !parts[1]) return null;
1119
+ const handle = parts[1];
1120
+ const did = handle.startsWith("did:") ? handle : void 0;
1121
+ return {
1122
+ source: "popfeed",
1123
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1124
+ };
1125
+ }
1126
+ function matchSifa(host, parts) {
1127
+ if (host !== "sifa.id") return null;
1128
+ if (parts[0] !== "p" || !parts[1]) return null;
1129
+ const handle = parts[1];
1130
+ const did = handle.startsWith("did:") ? handle : void 0;
1131
+ return {
1132
+ source: "sifa",
1133
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1134
+ };
1135
+ }
1136
+ function matchBlento(host, parts) {
1137
+ if (host !== "blento.app") return null;
1138
+ if (!parts[0]) return null;
1139
+ const handle = parts[0];
1140
+ const did = handle.startsWith("did:") ? handle : void 0;
1141
+ return {
1142
+ source: "blento",
1143
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1144
+ };
1145
+ }
1146
+ function matchSupportedUrl(url) {
1147
+ const host = normalizeHost(url.hostname);
1148
+ const parts = url.pathname.split("/").filter(Boolean);
1149
+ 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);
1151
+ }
1152
+ function parseAtUri(uri) {
1153
+ if (!uri.startsWith("at://")) return null;
1154
+ const rest = uri.slice("at://".length);
1155
+ const segments = rest.split("/").filter(Boolean);
1156
+ if (segments.length === 0) return null;
1157
+ const handle = segments[0];
1158
+ const did = handle.startsWith("did:") ? handle : void 0;
1159
+ const collection = segments[1];
1160
+ const rkey = segments[2];
1161
+ if (collection && rkey) {
1162
+ return {
1163
+ source: "headDetected",
1164
+ parsed: {
1165
+ type: inferType(collection),
1166
+ uri,
1167
+ handle,
1168
+ did,
1169
+ collection,
1170
+ rkey
1171
+ }
1172
+ };
1173
+ }
1174
+ return {
1175
+ source: "headDetected",
1176
+ parsed: { type: "profile", uri: `at://${handle}`, handle, did }
1177
+ };
1178
+ }
1179
+ var SUPPORTED_HOSTS = [
1180
+ ...BLUESKY_FAMILY.flatMap((e) => e.hosts),
1181
+ "pinkleap.app",
1182
+ "leaflet.pub",
1183
+ "tangled.org",
1184
+ "margin.at",
1185
+ "pdsls.dev",
1186
+ "atp.tools",
1187
+ "bluepy.social",
1188
+ "semble.so",
1189
+ "stream.place",
1190
+ "grain.social",
1191
+ "popfeed.social",
1192
+ "sifa.id",
1193
+ "blento.app"
1194
+ ];
1195
+
1196
+ // src/resolve.ts
1197
+ var DID_REQUIRED_WAYPOINTS = /* @__PURE__ */ new Set([
1198
+ "pdsls",
1199
+ "atptools",
1200
+ "margin",
1201
+ "grain",
1202
+ "popfeed"
1203
+ ]);
1204
+ function buildWaypointsForParsed(parsed, options = {}) {
1205
+ const did = options.did ?? parsed.did;
1206
+ const exclude = options.excludeSourceId;
1207
+ const type = parsed.type === "unknown" ? "profile" : parsed.type;
1208
+ const waypoints = WAYPOINT_ORDER.map((id) => WAYPOINT_DESTINATIONS_DATA[id]).filter((w) => !!w).filter((w) => w.id !== exclude).filter((w) => w.supportedTypes.includes(type)).map((w) => {
1209
+ if (DID_REQUIRED_WAYPOINTS.has(w.id) && !did) return null;
1210
+ const url = w.getUrl(parsed.handle, parsed.collection, parsed.rkey, did);
1211
+ if (!url) return null;
1212
+ return { id: w.id, name: w.name, category: w.category, url };
1213
+ }).filter((w) => !!w);
1214
+ const recommendedRaw = getRecommendedWaypointsData(type, parsed.collection);
1215
+ const availableIds = new Set(waypoints.map((w) => w.id));
1216
+ const ids = recommendedRaw.waypoints.map((w) => w.id).filter((id) => id !== exclude && availableIds.has(id));
1217
+ return { waypoints, recommended: { ids, label: recommendedRaw.label } };
1218
+ }
1219
+ function resolveAtUri(uri) {
1220
+ const match = parseAtUri(uri);
1221
+ if (!match) return null;
1222
+ const { parsed, source } = match;
1223
+ const { waypoints, recommended } = buildWaypointsForParsed(parsed);
1224
+ return {
1225
+ parsed,
1226
+ source,
1227
+ did: parsed.did ?? null,
1228
+ didResolved: false,
1229
+ waypoints,
1230
+ recommended
1231
+ };
1232
+ }
1233
+ async function resolveUrl(url, options = {}) {
1234
+ let target;
1235
+ try {
1236
+ target = typeof url === "string" ? new URL(url) : url;
1237
+ } catch {
1238
+ return null;
1239
+ }
1240
+ if (!/^https?:$/.test(target.protocol)) return null;
1241
+ let match = matchSupportedUrl(target);
1242
+ if (!match && options.fetchHead) {
1243
+ const headUri = await detectAtUriInHead(
1244
+ target.toString(),
1245
+ options.fetchHeadTimeoutMs ?? 4e3
1246
+ );
1247
+ if (headUri) match = parseAtUri(headUri);
1248
+ }
1249
+ if (!match) return null;
1250
+ const { parsed, source } = match;
1251
+ let did = parsed.did;
1252
+ let didResolved = false;
1253
+ if (!did && options.resolveHandle) {
1254
+ try {
1255
+ const resolved = await options.resolveHandle(parsed.handle);
1256
+ if (resolved) {
1257
+ did = resolved;
1258
+ didResolved = true;
1259
+ }
1260
+ } catch {
1261
+ }
1262
+ }
1263
+ const { waypoints, recommended } = buildWaypointsForParsed(parsed, {
1264
+ did,
1265
+ excludeSourceId: source
1266
+ });
1267
+ return {
1268
+ parsed: { ...parsed, did },
1269
+ source,
1270
+ did: did ?? null,
1271
+ didResolved,
1272
+ waypoints,
1273
+ recommended
1274
+ };
1275
+ }
1276
+ async function resolveViaApi(input, options = {}) {
1277
+ const endpoint = options.endpoint ?? "https://aturi.to/api/resolve";
1278
+ const fetchImpl = options.fetch ?? fetch;
1279
+ const params = new URLSearchParams();
1280
+ if (input.atUri) {
1281
+ params.set("atUri", input.atUri);
1282
+ } else if (input.url) {
1283
+ params.set("url", input.url);
1284
+ } else {
1285
+ throw new Error("resolveViaApi requires either `url` or `atUri`");
1286
+ }
1287
+ if (input.headDetect === false) params.set("headDetect", "false");
1288
+ const res = await fetchImpl(`${endpoint}?${params.toString()}`, {
1289
+ method: "GET",
1290
+ headers: { Accept: "application/json" },
1291
+ signal: options.signal
1292
+ });
1293
+ return await res.json();
1294
+ }
1295
+ async function detectAtUriInHead(url, timeoutMs) {
1296
+ if (typeof fetch === "undefined") return null;
1297
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
1298
+ const timeout = controller ? setTimeout(() => controller.abort(), timeoutMs) : null;
1299
+ try {
1300
+ const response = await fetch(url, {
1301
+ signal: controller?.signal,
1302
+ redirect: "follow",
1303
+ headers: {
1304
+ "User-Agent": "Mozilla/5.0 (compatible; AturiResolver/1.0; +https://aturi.to)",
1305
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.5"
1306
+ }
1307
+ });
1308
+ if (!response.ok) return null;
1309
+ const ct = response.headers.get("content-type") || "";
1310
+ if (!/text\/html|application\/xhtml/i.test(ct)) return null;
1311
+ const html = await response.text();
1312
+ const headMatch = html.match(/<head[\s\S]*?<\/head>/i);
1313
+ const haystack = headMatch ? headMatch[0] : html.slice(0, 256 * 1024);
1314
+ const linkRe = /<link\b[^>]*\bhref\s*=\s*["'](at:\/\/[^"']+)["'][^>]*>/gi;
1315
+ let m;
1316
+ while ((m = linkRe.exec(haystack)) !== null) {
1317
+ if (m[1].startsWith("at://")) return m[1];
1318
+ }
1319
+ return null;
1320
+ } catch {
1321
+ return null;
1322
+ } finally {
1323
+ if (timeout) clearTimeout(timeout);
1324
+ }
1325
+ }
1326
+
1327
+ exports.CATEGORY_ORDER = CATEGORY_ORDER;
1328
+ exports.COMPAT_FAMILIES = COMPAT_FAMILIES;
1329
+ exports.COMPAT_FAMILY_ORDER = COMPAT_FAMILY_ORDER;
1330
+ exports.DID_REQUIRED_WAYPOINTS = DID_REQUIRED_WAYPOINTS;
1331
+ exports.SUPPORTED_HOSTS = SUPPORTED_HOSTS;
1332
+ exports.WAYPOINT_CATEGORIES_DATA = WAYPOINT_CATEGORIES_DATA;
1333
+ exports.WAYPOINT_DESTINATIONS_DATA = WAYPOINT_DESTINATIONS_DATA;
1334
+ exports.WAYPOINT_ORDER = WAYPOINT_ORDER;
1335
+ exports.buildWaypointsForParsed = buildWaypointsForParsed;
1336
+ exports.getCategorizedWaypointsData = getCategorizedWaypointsData;
1337
+ exports.getDisplayName = getDisplayName;
1338
+ exports.getFeaturedWaypointData = getFeaturedWaypointData;
1339
+ exports.getRecommendedWaypointsData = getRecommendedWaypointsData;
1340
+ exports.getWaypointCountData = getWaypointCountData;
1341
+ exports.getWaypointDataForType = getWaypointDataForType;
1342
+ exports.matchSupportedUrl = matchSupportedUrl;
1343
+ exports.parseAtUri = parseAtUri;
1344
+ exports.parseURI = parseURI;
1345
+ exports.resolveAtUri = resolveAtUri;
1346
+ exports.resolveHandle = resolveHandle;
1347
+ exports.resolveUrl = resolveUrl;
1348
+ exports.resolveViaApi = resolveViaApi;
1349
+ exports.waypointActivity = waypointActivity;
1350
+ //# sourceMappingURL=index.cjs.map
1351
+ //# sourceMappingURL=index.cjs.map