import { DidResolver, HandleResolver } from "npm:@atproto/identity"; import { Database } from "jsr:@db/sqlite@0.11"; import { AtUri } from "npm:@atproto/api"; const systemDB = new Database("./system.db") // TODO: temporary shim. should seperate this to its own central system db instead of the now instantiated system dbs type DidMethod = "web" | "plc"; type DidDoc = { "@context"?: unknown; id: string; service?: Array<{ id?: string; type: string; serviceEndpoint: unknown; }>; alsoKnownAs?: string[]; [key: string]: unknown; }; /** * Extracts method from DID string */ function extractDidMethod(did: string): DidMethod { const match = did.match(/^did:(web|plc):/); if (!match) { throw new Error(`Unsupported DID method in ${did}`); } return match[1] as DidMethod; } async function resolveDidFromHandle( handle: string ): Promise<{ did: string }> { const resolver = new HandleResolver({}); const did = (await resolver.resolve(handle)) as string | null; if (!did) throw new Error(`Failed to resolve handle: ${handle}`); return { did }; } function getDidFromHandle(handle: string): { did: string } | null { const row = systemDB.prepare("SELECT did FROM did WHERE handle = ?").get(handle); if (!row) return null; try { const did = JSON.parse(row.did as string); if (typeof row.did !== "string") { throw new Error(`Unknown did type for handle: '${handle}'`); } return { did, }; } catch (err) { console.error(`Invalid did for ${handle}:`, err); return null; } } async function resolveDidDoc( did: string ): Promise<{ didmethod: DidMethod; diddoc: DidDoc }> { const didmethod = extractDidMethod(did); const resolver = new DidResolver({}); const diddoc = (await resolver.resolve(did)) as DidDoc | null; if (!diddoc) throw new Error(`Failed to resolve DID: ${did}`); return { didmethod, diddoc }; } function getDidDoc( did: string ): { method: "web" | "plc"; doc: DidDoc } | null { const row = systemDB.prepare("SELECT method, doc FROM did WHERE did = ?").get(did); if (!row) return null; try { const diddoc = JSON.parse(row.doc as string); if (row.method !== "web" && row.method !== "plc") { throw new Error(`Unknown didmethod '${row.didmethod}'`); } return { method: row.method, doc: diddoc, }; } catch (err) { console.error(`Invalid diddoc for ${did}:`, err); return null; } } // async function extractPDSAndHandleFromDid( // did: string // ): Promise<{ pds: string; handle?: string }> { // const item = getDidDoc(did); // const doc = item?.doc; // const rawServiceEndpoint = doc.service?.find( // (s: Record) => s.type === "AtprotoPersonalDataServer" // )?.serviceEndpoint; // if (!rawServiceEndpoint) { // throw new Error(`No AtprotoPersonalDataServer found in DID doc for ${did}`); // } // const serviceEndpoint = // typeof rawServiceEndpoint === "string" // ? rawServiceEndpoint // : typeof rawServiceEndpoint === "object" && // rawServiceEndpoint && // "uri" in rawServiceEndpoint && // typeof rawServiceEndpoint.uri === "string" // ? rawServiceEndpoint.uri // : (() => { // throw new Error( // `Unsupported serviceEndpoint format: ${JSON.stringify( // rawServiceEndpoint // )}` // ); // })(); // let handle: string | undefined = undefined; // if (Array.isArray(doc.alsoKnownAs)) { // const aka = doc.alsoKnownAs.find( // (a: unknown) => typeof a === "string" && a.startsWith("at://") // ); // if (aka) handle = aka.slice("at://".length); // } // return { pds: serviceEndpoint, handle }; // } function getPDSAndHandleFromDid( did: string ): { pds: string; handle?: string } | null { const row = systemDB.prepare("SELECT pds, handle FROM did WHERE did = ?").get(did); if (!row) return null; return { pds: row.pds as string, handle: (row.handle as string) ?? undefined, }; } // FINE (Fetch If Not Exists) variants export async function FINEDidFromHandle(handle: string): Promise<{ did: string } | null> { const local = getDidFromHandle(handle); if (local) return local; try { const { did } = await resolveDidFromHandle(handle); const { diddoc, didmethod } = await resolveDidDoc(did); systemDB.prepare( "INSERT OR REPLACE INTO did (did, handle, doc, method) VALUES (?, ?, ?, ?)" ).run(did, handle, JSON.stringify(diddoc), didmethod); return { did }; } catch (err) { console.error(`Failed to resolve/store DID from handle '${handle}':`, err); return null; } } export async function FINEDidDoc(did: string): Promise<{ method: "web" | "plc"; doc: DidDoc } | null> { const local = getDidDoc(did); if (local) return local; try { const { diddoc, didmethod } = await resolveDidDoc(did); systemDB.prepare( "INSERT OR REPLACE INTO did (did, doc, method) VALUES (?, ?, ?)" ).run(did, JSON.stringify(diddoc), didmethod); return { method: didmethod, doc: diddoc }; } catch (err) { console.error(`Failed to resolve/store DID doc for '${did}':`, err); return null; } } export async function FINEPDSAndHandleFromDid( did: string ): Promise<{ pds: string; handle?: string } | null> { const local = getPDSAndHandleFromDid(did); if (local) return local; const diddoc = await FINEDidDoc(did); if (!diddoc) return null; try { const rawServiceEndpoint = diddoc.doc.service?.find( (s: Record) => s.type === "AtprotoPersonalDataServer" )?.serviceEndpoint; if (!rawServiceEndpoint) { throw new Error(`No AtprotoPersonalDataServer found in DID doc for ${did}`); } const serviceEndpoint = typeof rawServiceEndpoint === "string" ? rawServiceEndpoint : typeof rawServiceEndpoint === "object" && rawServiceEndpoint && "uri" in rawServiceEndpoint && typeof rawServiceEndpoint.uri === "string" ? rawServiceEndpoint.uri : (() => { throw new Error( `Unsupported serviceEndpoint format: ${JSON.stringify( rawServiceEndpoint )}` ); })(); let handle: string | undefined = undefined; if (Array.isArray(diddoc.doc.alsoKnownAs)) { const aka = diddoc.doc.alsoKnownAs.find( (a: unknown) => typeof a === "string" && a.startsWith("at://") ); if (aka) handle = aka.slice("at://".length); } systemDB.prepare( "INSERT OR REPLACE INTO did (did, pds, handle) VALUES (?, ?, ?)" ).run(did, serviceEndpoint, handle ?? null); return { pds: serviceEndpoint, handle }; } catch (err) { console.error(`Failed to extract/store PDS and handle for '${did}':`, err); return null; } } export function extractDid(input: string): string { if (input.startsWith('did:')) { return input } try { const uri = new AtUri(input) return uri.host } catch (e) { throw new Error(`Invalid input: expected a DID or a valid AT URI, got "${input}"`) } }