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 plcUrl: Deno.env.get("PLC_URL") ?? "https://plc.directory",
30 });
31}
32
33/**
34 * The bidirectional resolver.
35 * @interface
36 */
37export interface BidirectionalResolver {
38 resolveDidToHandle(did: string): Promise<string>;
39 resolveDidsToHandles(dids: string[]): Promise<Record<string, string>>;
40 resolveDidToPdsUrl(did: string): Promise<string | undefined>;
41}
42
43/**
44 * Create the bidirectional resolver.
45 * @param resolver - The ID resolver
46 * @returns The bidirectional resolver
47 */
48export function createBidirectionalResolver(resolver: IdResolver) {
49 return {
50 async resolveDidToHandle(did: string): Promise<string> {
51 const didDoc = await resolver.did.resolveAtprotoData(did) as AtprotoData;
52 const resolvedHandle = await resolver.handle.resolve(didDoc.handle);
53 if (resolvedHandle === did) {
54 return didDoc.handle;
55 }
56 return did;
57 },
58
59 async resolveHandleToDid(handle: string) {
60 return await resolver.handle.resolve(handle) as Did;
61 },
62
63 async resolveDidToPdsUrl(did: string): Promise<string | undefined> {
64 try {
65 // First try the standard resolution
66 const didDoc = await resolver.did.resolveAtprotoData(
67 did,
68 ) as AtprotoData;
69 if (didDoc.pds) {
70 return didDoc.pds;
71 } else {
72 const forcedDidDoc = await resolver.did.resolveAtprotoData(
73 did,
74 true,
75 );
76 if (forcedDidDoc.pds) {
77 return forcedDidDoc.pds;
78 }
79 }
80 } catch (err) {
81 console.error("Error resolving PDS URL:", err);
82 return undefined;
83 }
84 },
85
86 async resolveDidsToHandles(
87 dids: string[],
88 ): Promise<Record<string, string>> {
89 const didHandleMap: Record<string, string> = {};
90 const resolves = await Promise.all(
91 dids.map((did) => this.resolveDidToHandle(did).catch((_) => did)),
92 );
93 for (let i = 0; i < dids.length; i++) {
94 didHandleMap[dids[i]] = resolves[i];
95 }
96 return didHandleMap;
97 },
98 };
99}