Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129import { ClientResponseError, isXRPCErrorPayload, type XRPCErrorPayload } from "@atcute/client";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { createBobbinClient, type BobbinContext, type QueryValue, type XrpcRequestInit} from "$lib/api/client";import { hostForServiceDid } from "$lib/auth/agent";
export const serviceUrlFor = (host: string): string => /^[a-z][a-z0-9+.-]*:\/\//i.test(host) ? host.replace(/\/+$/, "") : `https://${host}`;
export const serviceClient = (agent: OAuthUserAgent, host: string): BobbinContext => createBobbinClient({ serviceUrl: serviceUrlFor(host), agent });
const BARE_HOST_PATTERN = /^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?::[0-9]{1,5})?$/i;
const isBareHost = (value: string): boolean => { if (!value || !BARE_HOST_PATTERN.test(value)) return false; const colon = value.indexOf(":"); if (colon !== -1) { const port = Number(value.slice(colon + 1)); if (port < 1 || port > 65535) return false; } return true;};
export const hostForKnot = (knot: string): string => { if (knot.startsWith("did:web:")) { if (knot.slice("did:web:".length).includes(":")) { throw new Error(`invalid knot identifier: ${knot}`); } const host = hostForServiceDid(knot); if (host && isBareHost(host)) return host; throw new Error(`invalid knot identifier: ${knot}`); } if (isBareHost(knot)) return knot; throw new Error(`invalid knot identifier: ${knot}`);};
export const buildUrl = ( origin: string, nsid: string, params?: Record<string, QueryValue>): URL => { const url = new URL(`/xrpc/${nsid}`, `${origin}/`); if (params) { for (const [key, value] of Object.entries(params)) { if (value === undefined) continue; if (Array.isArray(value)) { for (const item of value) url.searchParams.append(key, String(item)); } else { url.searchParams.set(key, String(value)); } } } return url;};
export const toResponseError = async (response: Response): Promise<ClientResponseError> => { let data: XRPCErrorPayload = { error: "XRPCError", message: response.statusText }; try { const body: unknown = await response.json(); if (isXRPCErrorPayload(body)) data = body; } catch { // keeps the status-line fallback for non-json bodies } return new ClientResponseError({ status: response.status, headers: response.headers, data });};
export const jsonGet = async <T>( ctx: BobbinContext, nsid: string, params?: Record<string, QueryValue>, init?: XrpcRequestInit): Promise<T> => { const response = await ctx.fetch(buildUrl(ctx.serviceUrl, nsid, params), { headers: { accept: "application/json", ...init?.headers }, signal: init?.signal }); if (!response.ok) throw await toResponseError(response); if (typeof response.text === "function") { const text = await response.text(); return (text.length > 0 ? JSON.parse(text) : undefined) as T; } return (await response.json()) as T;};
export const jsonPost = async <T>( ctx: BobbinContext, nsid: string, body: unknown, init?: XrpcRequestInit): Promise<T> => { const response = await ctx.fetch(buildUrl(ctx.serviceUrl, nsid), { method: "POST", headers: { ...(body !== undefined ? { "content-type": "application/json" } : {}), accept: "application/json", ...init?.headers }, body: body !== undefined ? JSON.stringify(body) : undefined, signal: init?.signal }); if (!response.ok) throw await toResponseError(response); if (typeof response.text === "function") { const text = await response.text(); return (text.length > 0 ? JSON.parse(text) : undefined) as T; } return (await response.json()) as T;};
export const rawGet = async ( ctx: BobbinContext, nsid: string, params?: Record<string, QueryValue>, init?: XrpcRequestInit): Promise<Response> => { const response = await ctx.fetch(buildUrl(ctx.serviceUrl, nsid, params), { headers: init?.headers, signal: init?.signal }); if (!response.ok) throw await toResponseError(response); return response;};