powerpointproto
slides.waow.tech
slides
1import { Client } from "@atcute/client";
2import {
3 CompositeDidDocumentResolver,
4 CompositeHandleResolver,
5 DohJsonHandleResolver,
6 PlcDidDocumentResolver,
7 AtprotoWebDidDocumentResolver,
8 WellKnownHandleResolver,
9} from "@atcute/identity-resolver";
10import {
11 configureOAuth,
12 defaultIdentityResolver,
13 OAuthUserAgent,
14} from "@atcute/oauth-browser-client";
15
16export const didDocumentResolver = new CompositeDidDocumentResolver({
17 methods: {
18 plc: new PlcDidDocumentResolver(),
19 web: new AtprotoWebDidDocumentResolver(),
20 },
21});
22
23export const handleResolver = new CompositeHandleResolver({
24 strategy: "dns-first",
25 methods: {
26 dns: new DohJsonHandleResolver({ dohUrl: "https://dns.google/resolve?" }),
27 http: new WellKnownHandleResolver(),
28 },
29});
30
31const OAUTH_CLIENT_ID = import.meta.env.VITE_OAUTH_CLIENT_ID;
32const OAUTH_REDIRECT_URI = import.meta.env.VITE_OAUTH_REDIRECT_URI;
33
34if (typeof window !== "undefined") {
35 configureOAuth({
36 metadata: {
37 client_id: OAUTH_CLIENT_ID,
38 redirect_uri: OAUTH_REDIRECT_URI,
39 },
40 identityResolver: defaultIdentityResolver({
41 handleResolver,
42 didDocumentResolver,
43 }),
44 });
45}
46
47// module-level state
48export let agent: OAuthUserAgent | null = null;
49export let currentDid: string | null = null;
50
51export const setAgent = (a: OAuthUserAgent | null) => { agent = a; };
52export const setCurrentDid = (did: string | null) => { currentDid = did; };
53export const getAgent = () => agent;
54export const getCurrentDid = () => currentDid;
55
56// eslint-disable-next-line @typescript-eslint/no-explicit-any
57export const getRpc = (): any => {
58 if (!agent) throw new Error("not logged in");
59 return new Client({ handler: agent });
60};
61
62export const getPdsUrl = (): string | null => {
63 if (!agent) return null;
64 // eslint-disable-next-line @typescript-eslint/no-explicit-any
65 const session = (agent as any).session;
66 return session?.info?.aud || null;
67};
68
69// handle resolution
70const handleCache = new Map<string, string>();
71
72export const resolveHandle = async (did: string): Promise<string> => {
73 if (handleCache.has(did)) return handleCache.get(did)!;
74 try {
75 const res = await fetch(`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${did}`);
76 if (res.ok) {
77 const data = await res.json();
78 if (data.handle) {
79 handleCache.set(did, data.handle);
80 return data.handle;
81 }
82 }
83 } catch { /* ignore */ }
84 return did;
85};
86
87// PDS URL resolution from DID document
88const pdsCache = new Map<string, string>();
89
90export const resolvePdsUrl = async (did: string): Promise<string> => {
91 if (pdsCache.has(did)) return pdsCache.get(did)!;
92 try {
93 // eslint-disable-next-line @typescript-eslint/no-explicit-any
94 const doc = await didDocumentResolver.resolve(did as any);
95 const pds = doc.service?.find(s => s.id === "#atproto_pds");
96 if (pds && typeof pds.serviceEndpoint === "string") {
97 pdsCache.set(did, pds.serviceEndpoint);
98 return pds.serviceEndpoint;
99 }
100 } catch { /* ignore */ }
101 return "https://bsky.social";
102};
103
104// public API fetch (no auth required)
105export const publicFetch = async (pdsUrl: string, method: string, params: Record<string, string>) => {
106 const url = new URL(`/xrpc/${method}`, pdsUrl);
107 Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
108 const res = await fetch(url.toString());
109 if (!res.ok) return null;
110 return res.json();
111};