A social knowledge tool for researchers built on ATProto
1import { Result, ok, err } from 'src/shared/core/Result';
2import { IIdentityResolutionService } from '../../domain/services/IIdentityResolutionService';
3import { DID } from '../../domain/DID';
4import { DIDOrHandle } from '../../domain/DIDOrHandle';
5import { Handle } from '../../domain/Handle';
6import { IAgentService } from '../../application/IAgentService';
7
8export class ATProtoIdentityResolutionService
9 implements IIdentityResolutionService
10{
11 constructor(private readonly agentService: IAgentService) {}
12
13 async resolveToDID(identifier: DIDOrHandle): Promise<Result<DID>> {
14 try {
15 // If it's already a DID, return it directly
16 if (identifier.isDID) {
17 const did = identifier.getDID();
18 if (!did) {
19 return err(new Error('Invalid DID in identifier'));
20 }
21 return ok(did);
22 }
23
24 // If it's a handle, resolve it to a DID
25 const handle = identifier.getHandle();
26 if (!handle) {
27 return err(new Error('Invalid handle in identifier'));
28 }
29
30 // Get an unauthenticated agent to resolve the handle
31 const agentResult = this.agentService.getUnauthenticatedAgent();
32 if (agentResult.isErr()) {
33 return err(
34 new Error(
35 `Failed to get agent for handle resolution: ${agentResult.error.message}`,
36 ),
37 );
38 }
39
40 const agent = agentResult.value;
41
42 // Resolve the handle to get the DID
43 const profileResult = await agent.resolveHandle({ handle: handle.value });
44
45 if (!profileResult.success) {
46 return err(
47 new Error(
48 `Failed to resolve handle ${handle.value}: ${JSON.stringify(profileResult)}`,
49 ),
50 );
51 }
52
53 // Create and return the DID
54 const didResult = DID.create(profileResult.data.did);
55 if (didResult.isErr()) {
56 return err(
57 new Error(
58 `Invalid DID returned from handle resolution: ${didResult.error.message}`,
59 ),
60 );
61 }
62
63 return ok(didResult.value);
64 } catch (error) {
65 return err(
66 new Error(
67 `Error resolving identifier to DID: ${error instanceof Error ? error.message : String(error)}`,
68 ),
69 );
70 }
71 }
72
73 async resolveToHandle(identifier: DIDOrHandle): Promise<Result<Handle>> {
74 try {
75 // If it's already a handle, return it directly
76 if (identifier.isHandle) {
77 const handle = identifier.getHandle();
78 if (!handle) {
79 return err(new Error('Invalid handle in identifier'));
80 }
81 return ok(handle);
82 }
83
84 // If it's a DID, resolve it to a handle
85 const did = identifier.getDID();
86 if (!did) {
87 return err(new Error('Invalid DID in identifier'));
88 }
89
90 // Get an unauthenticated agent to resolve the DID
91 const agentResult = this.agentService.getUnauthenticatedAgent();
92 if (agentResult.isErr()) {
93 return err(
94 new Error(
95 `Failed to get agent for DID resolution: ${agentResult.error.message}`,
96 ),
97 );
98 }
99
100 const agent = agentResult.value;
101
102 // Get the profile to extract the handle
103 const profileResult = await agent.getProfile({ actor: did.value });
104
105 if (!profileResult.success) {
106 return err(
107 new Error(
108 `Failed to resolve DID ${did.value}: ${JSON.stringify(profileResult)}`,
109 ),
110 );
111 }
112
113 // Create and return the Handle
114 const handleResult = Handle.create(profileResult.data.handle);
115 if (handleResult.isErr()) {
116 return err(
117 new Error(
118 `Invalid handle returned from DID resolution: ${handleResult.error.message}`,
119 ),
120 );
121 }
122
123 return ok(handleResult.value);
124 } catch (error) {
125 return err(
126 new Error(
127 `Error resolving identifier to handle: ${error instanceof Error ? error.message : String(error)}`,
128 ),
129 );
130 }
131 }
132}