Graphical PDS migrator for AT Protocol
1import { IdResolver } from "npm:@atproto/identity";
2import { Did } from "npm:@atproto/api";
3
4interface AtprotoData {
5 did: string;
6 signingKey: string;
7 handle: string;
8 pds: string;
9}
10
11interface DidService {
12 id: string;
13 type: string;
14 serviceEndpoint: string;
15}
16
17/**
18 * ID resolver instance.
19 */
20const idResolver = createIdResolver();
21export const resolver = createBidirectionalResolver(idResolver);
22
23/**
24 * Create the ID resolver.
25 * @returns The ID resolver
26 */
27export function createIdResolver() {
28 return new IdResolver();
29}
30
31/**
32 * The bidirectional resolver.
33 * @interface
34 */
35export interface BidirectionalResolver {
36 resolveDidToHandle(did: string): Promise<string>;
37 resolveDidsToHandles(dids: string[]): Promise<Record<string, string>>;
38 resolveDidToPdsUrl(did: string): Promise<string | undefined>;
39}
40
41/**
42 * Create the bidirectional resolver.
43 * @param resolver - The ID resolver
44 * @returns The bidirectional resolver
45 */
46export function createBidirectionalResolver(resolver: IdResolver) {
47 return {
48 async resolveDidToHandle(did: string): Promise<string> {
49 const didDoc = await resolver.did.resolveAtprotoData(did) as AtprotoData;
50 const resolvedHandle = await resolver.handle.resolve(didDoc.handle);
51 if (resolvedHandle === did) {
52 return didDoc.handle;
53 }
54 return did;
55 },
56
57 async resolveHandleToDid(handle: string) {
58 return await resolver.handle.resolve(handle) as Did;
59 },
60
61 async resolveDidToPdsUrl(did: string): Promise<string | undefined> {
62 try {
63 // First try the standard resolution
64 const didDoc = await resolver.did.resolveAtprotoData(
65 did,
66 ) as AtprotoData;
67 if (didDoc.pds) {
68 return didDoc.pds;
69 } else {
70 const forcedDidDoc = await resolver.did.resolveAtprotoData(
71 did,
72 true,
73 );
74 if (forcedDidDoc.pds) {
75 return forcedDidDoc.pds;
76 }
77 }
78 } catch (err) {
79 console.error("Error resolving PDS URL:", err);
80 return undefined;
81 }
82 },
83
84 async resolveDidsToHandles(
85 dids: string[],
86 ): Promise<Record<string, string>> {
87 const didHandleMap: Record<string, string> = {};
88 const resolves = await Promise.all(
89 dids.map((did) => this.resolveDidToHandle(did).catch((_) => did)),
90 );
91 for (let i = 0; i < dids.length; i++) {
92 didHandleMap[dids[i]] = resolves[i];
93 }
94 return didHandleMap;
95 },
96 };
97}