Various AT Protocol integrations with obsidian
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

resolve actors with slingshot

+77 -6
+3
bun.lock
··· 10 10 "@atcute/client": "^4.2.1", 11 11 "@atcute/identity-resolver": "^1.2.2", 12 12 "@atcute/leaflet": "^1.0.17", 13 + "@atcute/microcosm": "^1.0.1", 13 14 "@atcute/oauth-browser-client": "^2.0.3", 14 15 "@atcute/pckt": "^0.1.5", 15 16 "@atcute/standard-site": "^1.0.0", ··· 60 61 "@atcute/lexicon-resolver": ["@atcute/lexicon-resolver@0.1.6", "", { "dependencies": { "@atcute/crypto": "^2.3.0", "@atcute/lexicon-doc": "^2.0.6", "@atcute/lexicons": "^1.2.6", "@atcute/repo": "^0.1.1", "@atcute/util-fetch": "^1.0.5", "@badrap/valita": "^0.4.6" }, "peerDependencies": { "@atcute/identity": "^1.1.0", "@atcute/identity-resolver": "^1.1.3" } }, "sha512-wJC/ChmpP7k+ywpOd07CMvioXjIGaFpF3bDwXLi/086LYjSWHOvtW6pyC+mqP5wLhjyH2hn4wmi77Buew1l1aw=="], 61 62 62 63 "@atcute/lexicons": ["@atcute/lexicons@1.2.7", "", { "dependencies": { "@atcute/uint8array": "^1.1.0", "@atcute/util-text": "^1.1.0", "@standard-schema/spec": "^1.1.0", "esm-env": "^1.2.2" } }, "sha512-gCvkSMI1F1zx7xXa59iPiSKMH3L5Hga6iurGqQjaQbE2V/np/2QuDqQzt96TNbWfaFAXE9f9oY+0z3ljf/bweA=="], 64 + 65 + "@atcute/microcosm": ["@atcute/microcosm@1.0.1", "", { "dependencies": { "@atcute/lexicons": "^1.2.7" } }, "sha512-siyreLgOCZ6gT3x5tajTw1MrlR0s4SDNlUvaRYQZrAUZS1xuuLx1Ko/cwsf+/QQzEN6K1wgtTC0J6HqtRZwWVg=="], 63 66 64 67 "@atcute/mst": ["@atcute/mst@0.1.2", "", { "dependencies": { "@atcute/cbor": "^2.3.0", "@atcute/cid": "^2.4.0", "@atcute/uint8array": "^1.0.6" } }, "sha512-Oz5CZTjqauEJLT9B+zkoy/mjl216DrjCxJFrguRV3N+1NkIbCfAcSRf3UDSNjfzDzBkJvC1WjA/3oQkm83duPg=="], 65 68
+1
package.json
··· 30 30 "@atcute/client": "^4.2.1", 31 31 "@atcute/identity-resolver": "^1.2.2", 32 32 "@atcute/leaflet": "^1.0.17", 33 + "@atcute/microcosm": "^1.0.1", 33 34 "@atcute/oauth-browser-client": "^2.0.3", 34 35 "@atcute/pckt": "^0.1.5", 35 36 "@atcute/standard-site": "^1.0.0",
+1
src/env.d.ts
··· 2 2 /// <reference types="@atcute/atproto" /> 3 3 /// <reference types="@atcute/standard-site" /> 4 4 /// <reference types="@atcute/leaflet" /> 5 + /// <reference types="@atcute/microcosm" />
+12 -4
src/lib/client.ts
··· 5 5 6 6 const DEFAULT_SERVICE = "https://bsky.social"; 7 7 8 + // Custom fetch function using Obsidian's requestUrl to avoid CORS issues 8 9 export interface Credentials { 9 10 identifier: string; 10 11 password: string; ··· 12 13 13 14 export class ATClient extends Client { 14 15 hh: Handler; 16 + slingshot: Client 15 17 16 18 constructor(creds?: Credentials) { 17 19 const handler = new Handler(creds); ··· 26 28 return this.hh.cm.session; 27 29 } 28 30 29 - getActor(identifier: string): Promise<ResolvedActor> { 31 + async getActor(identifier: string): Promise<ResolvedActor> { 30 32 return this.hh.getActor(identifier); 31 33 } 32 34 } ··· 49 51 return cached; 50 52 } 51 53 if (isActorIdentifier(identifier)) { 52 - const res = await resolveActor(identifier); 53 - this.cache.set(key, res); 54 - return res; 54 + try { 55 + const res = await resolveActor(identifier); 56 + this.cache.set(key, res); 57 + return res; 58 + } 59 + catch (e) { 60 + console.error("Error resolving actor:", e) 61 + throw new Error("Failed to resolve actor: " + JSON.stringify(identifier)); 62 + } 55 63 } else { 56 64 throw new Error("Invalid actor identifier: " + JSON.stringify(identifier)); 57 65 }
+60 -2
src/lib/identity.ts
··· 4 4 DohJsonHandleResolver, 5 5 LocalActorResolver, 6 6 WellKnownHandleResolver, 7 + type ActorResolver, 8 + type ResolveActorOptions, 9 + type ResolvedActor, 7 10 } from '@atcute/identity-resolver'; 8 11 9 12 import { ··· 11 14 PlcDidDocumentResolver, 12 15 WebDidDocumentResolver, 13 16 } from '@atcute/identity-resolver'; 17 + 18 + import { Client, ok, simpleFetchHandler } from "@atcute/client"; 19 + 14 20 15 21 const handleResolver = new CompositeHandleResolver({ 16 22 methods: { ··· 26 32 }, 27 33 }); 28 34 29 - const actorResolver = new LocalActorResolver({ 35 + const localActorResolver = new LocalActorResolver({ 30 36 handleResolver, 31 37 didDocumentResolver: didResolver, 32 38 }); 33 39 40 + export class SlingshotActorResolver implements ActorResolver { 41 + private client: Client; 42 + 43 + constructor() { 44 + this.client = new Client({ 45 + handler: simpleFetchHandler({ 46 + service: "https://slingshot.microcosm.blue", 47 + // fetch: obsidianFetch 48 + }) 49 + }); 50 + } 51 + 52 + async resolve(actor: ActorIdentifier, options?: ResolveActorOptions): Promise<ResolvedActor> { 53 + const resolved = await ok( 54 + this.client.get("blue.microcosm.identity.resolveMiniDoc", { 55 + params: { identifier: actor }, 56 + signal: options?.signal, 57 + }) 58 + ); 59 + 60 + return { 61 + did: resolved.did, 62 + handle: resolved.handle, 63 + pds: resolved.pds, 64 + }; 65 + } 66 + } 67 + 68 + class CompositeActorResolver implements ActorResolver { 69 + private slingshotResolver: SlingshotActorResolver; 70 + private localResolver: LocalActorResolver; 71 + 72 + constructor(slingshotResolver: SlingshotActorResolver, localResolver: LocalActorResolver) { 73 + this.slingshotResolver = slingshotResolver; 74 + this.localResolver = localResolver; 75 + } 76 + 77 + async resolve(actor: ActorIdentifier, options?: ResolveActorOptions): Promise<ResolvedActor> { 78 + try { 79 + const resolved = await this.slingshotResolver.resolve(actor, options); 80 + return resolved; 81 + } catch (error) { 82 + console.warn("Slingshot actor resolution failed, falling back to local resolver:", error); 83 + return await this.localResolver.resolve(actor, options); 84 + } 85 + } 86 + } 87 + 88 + const slingshotResolver = new SlingshotActorResolver(); 89 + 90 + const compositeResolver = new CompositeActorResolver(slingshotResolver, localActorResolver); 91 + 34 92 export async function resolveActor(identifier: ActorIdentifier) { 35 - return actorResolver.resolve(identifier); 93 + return compositeResolver.resolve(identifier); 36 94 } 37 95 38 96