This repository has no description
1import { Client, simpleFetchHandler } from "@atcute/client";
2import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
3import { mintServiceAuth, serviceDidForHost } from "$lib/auth/agent";
4
5export { ClientResponseError, isXRPCErrorPayload, ok } from "@atcute/client";
6export type { XRPCErrorPayload } from "@atcute/client";
7
8// generated lexicons register ambient types; don't import the barrel at runtime.
9
10export interface BobbinContext {
11 readonly xrpc: Client;
12 readonly serviceUrl: string;
13 readonly fetch: typeof globalThis.fetch;
14}
15
16export interface CreateBobbinOptions {
17 serviceUrl: string;
18 fetch?: typeof globalThis.fetch;
19 /** when set, every xrpc request carries a freshly minted service-auth jwt */
20 agent?: OAuthUserAgent;
21}
22
23const nsidOf = (input: RequestInfo | URL): string | null => {
24 const href = input instanceof Request ? input.url : String(input);
25 let pathname: string;
26 try {
27 pathname = new URL(href).pathname;
28 } catch {
29 return null;
30 }
31 const [, prefix, nsid, ...rest] = pathname.split("/");
32 return prefix === "xrpc" && nsid && rest.length === 0 ? nsid : null;
33};
34
35const withServiceAuth = (
36 base: typeof globalThis.fetch,
37 agent: OAuthUserAgent,
38 origin: string
39): typeof globalThis.fetch => {
40 const aud = serviceDidForHost(new URL(origin).host);
41 return async (input, init) => {
42 const lxm = nsidOf(input);
43 if (!lxm) return base(input, init);
44
45 const token = await mintServiceAuth(agent, { aud, lxm });
46 const headers = new Headers(init?.headers);
47 headers.set("authorization", `Bearer ${token}`);
48 return base(input, { ...init, headers });
49 };
50};
51
52export const createBobbinClient = ({ serviceUrl, fetch, agent }: CreateBobbinOptions): BobbinContext => {
53 const origin = serviceUrl.replace(/\/+$/, "");
54 const baseFetch = fetch ?? globalThis.fetch;
55 const boundFetch = agent ? withServiceAuth(baseFetch, agent, origin) : baseFetch;
56 const xrpc = new Client({ handler: simpleFetchHandler({ service: origin, fetch: boundFetch }) });
57 return { xrpc, serviceUrl: origin, fetch: boundFetch };
58};
59
60export type QueryValue = string | number | boolean | readonly (string | number)[] | undefined;
61
62export interface XrpcRequestInit {
63 signal?: AbortSignal;
64 headers?: HeadersInit;
65}