plc.directory mirror
1<script lang="ts">
2 import { api } from '$lib/api';
3 import { processIndexedEntryLog } from '@atcute/did-plc';
4 import {
5 CompositeHandleResolver,
6 DohJsonHandleResolver,
7 WellKnownHandleResolver,
8 DidNotFoundError,
9 InvalidResolvedHandleError,
10 AmbiguousHandleError,
11 FailedHandleResolutionError,
12 HandleResolutionError
13 } from '@atcute/identity-resolver';
14 import AuditLog from '../components/AuditLog.svelte';
15
16 let didInput = '';
17 let loading = false;
18 let result: any = null;
19 let auditLogData: { canonical: any[], nullified: any[] } | null = null;
20 let error = '';
21 let resolvedDid = '';
22
23 const handleResolver = new CompositeHandleResolver({
24 strategy: 'race',
25 methods: {
26 dns: new DohJsonHandleResolver({ dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query' }),
27 http: new WellKnownHandleResolver(),
28 },
29 });
30
31 function isDid(input: string): boolean {
32 return input.startsWith('did:plc:');
33 }
34
35 async function resolveInputToDid(input: string): Promise<string> {
36 const trimmedInput = input.trim();
37
38 if (isDid(trimmedInput)) {
39 return trimmedInput;
40 }
41
42 try {
43 const resolvedHandle = await handleResolver.resolve(trimmedInput);
44 return resolvedHandle;
45 } catch (err) {
46 if (err instanceof DidNotFoundError) {
47 throw new Error("Handle not found - DID not found for this handle");
48 }
49 if (err instanceof InvalidResolvedHandleError) {
50 throw new Error("Invalid handle format");
51 }
52 if (err instanceof AmbiguousHandleError) {
53 throw new Error("Ambiguous handle - multiple DIDs found");
54 }
55 if (err instanceof FailedHandleResolutionError) {
56 throw new Error("Failed to resolve handle");
57 }
58 if (err instanceof HandleResolutionError) {
59 throw new Error("Error resolving handle");
60 }
61 throw new Error(`Handle resolution failed: ${err.message}`);
62 }
63 }
64
65 async function searchDID() {
66 if (!didInput.trim()) return;
67
68 loading = true;
69 error = '';
70 result = null;
71 auditLogData = null;
72 resolvedDid = '';
73
74 try {
75 const did = await resolveInputToDid(didInput);
76 resolvedDid = did;
77
78 const log = await api.fetchDidAuditLog(resolvedDid);
79 auditLogData = await processIndexedEntryLog(resolvedDid, log);
80
81 result = await api.fetchDidDocument(resolvedDid);
82 } catch (err: any) {
83 error = err.message;
84 } finally {
85 loading = false;
86 }
87 }
88</script>
89
90<div class="min-h-screen bg-gray-50 dark:bg-gray-900 p-8">
91 <div class="max-w-4xl mx-auto">
92 <div class="flex justify-between items-center mb-8">
93 <h1 class="text-4xl font-bold text-gray-900 dark:text-white text-center flex-1">
94 PLC Directory
95 </h1>
96 </div>
97
98 <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-6 transition-colors">
99 <div class="flex gap-4">
100 <input
101 bind:value={didInput}
102 type="text"
103 placeholder="Enter DID (did:plc:...) or handle (user.bsky.social)"
104 class="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600
105 bg-white dark:bg-gray-700 text-gray-900 dark:text-white
106 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500
107 placeholder-gray-500 dark:placeholder-gray-400 transition-colors"
108 disabled={loading}
109 />
110 <button
111 on:click={searchDID}
112 disabled={loading || !didInput.trim()}
113 class="px-6 py-2 bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-600
114 text-white rounded-md disabled:opacity-50 transition-colors"
115 >
116 {loading ? 'Searching...' : 'Search'}
117 </button>
118 </div>
119
120 {#if error}
121 <div class="mt-4 p-4 bg-red-50 dark:bg-red-900/50 border border-red-200 dark:border-red-800 rounded-md transition-colors">
122 <p class="text-red-800 dark:text-red-200">{error}</p>
123 </div>
124 {/if}
125 </div>
126
127 <div class="mb-6">
128 <AuditLog auditData={auditLogData} />
129 </div>
130
131 {#if result}
132 <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors">
133 <h2 class="text-2xl font-semibold mb-4 text-gray-900 dark:text-white">DID Document</h2>
134<pre class="bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100
135 p-4 rounded-md overflow-x-auto text-sm transition-colors"><code>{JSON.stringify(result, null, 2)}</code></pre>
136 </div>
137 {/if}
138 </div>
139</div>