csr tangled client in solid-js
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233import type { ResourceUri } from '@atcute/lexicons/syntax';
import { normalizeServiceUrl } from './client';import { getRouteKnotRequestsThroughAppview, getTangledAppviewService,} from '../settings';
export interface AppviewRecordView<T> { uri: ResourceUri; cid?: string | null; value: T;}
export interface AppviewListResponse<T> { items: Array<AppviewRecordView<T>>; cursor?: string | null;}
export interface AppviewBulkResponse<T> { items: Array<AppviewRecordView<T>>;}
export interface AppviewStatefulRecordView<T> extends AppviewRecordView<T> { state?: string; stateUpdatedAt?: string; commentCount?: number;}
export interface AppviewStatefulListResponse<T> { items: Array<AppviewStatefulRecordView<T>>; cursor?: string | null;}
export interface AppviewSearchHit<T> extends AppviewRecordView<T> { nsid: string; score: number;}
export interface AppviewSearchResponse<T> { hits: Array<AppviewSearchHit<T>>; cursor?: string | null;}
interface AppviewCountResponse { count: number; distinctAuthors?: number; distinct_authors?: number;}
export type AppviewParamValue = string | number | boolean | null | undefined;
export class AppviewResponseError extends Error { readonly status: number;
constructor(status: number, message: string) { super(message); this.name = 'AppviewResponseError'; this.status = status; }}
export class AppviewUnavailableError extends Error { readonly cause: unknown;
constructor(cause: unknown) { super(cause instanceof Error ? cause.message : 'Appview unavailable'); this.name = 'AppviewUnavailableError'; this.cause = cause; }}
export const appviewJson = async <T>( nsid: string, params: Record<string, AppviewParamValue> = {}, signal?: AbortSignal,): Promise<T> => { const url = new URL(`/xrpc/${nsid}`, normalizeServiceUrl(getTangledAppviewService())); for (const [key, value] of Object.entries(params)) { if (value !== undefined && value !== null) { url.searchParams.set(key, String(value)); } }
let response: Response; try { response = await fetch(url, { headers: { accept: 'application/json', }, signal, }); } catch (cause) { throw new AppviewUnavailableError(cause); }
if (!response.ok) { let message = `${response.status} ${response.statusText}`.trim(); try { const body = (await response.json()) as { message?: unknown; error?: unknown }; const bodyMessage = typeof body.message === 'string' ? body.message : body.error; if (typeof bodyMessage === 'string') { message = bodyMessage; } } catch { // Keep the HTTP status message when the appview does not return JSON. }
throw new AppviewResponseError(response.status, `Appview ${nsid} failed (${response.status}): ${message}`); }
return (await response.json()) as T;};
export const appviewJsonUrl = async <T>(url: URL, signal?: AbortSignal): Promise<T> => { let response: Response; try { response = await fetch(url, { headers: { accept: 'application/json', }, signal, }); } catch (cause) { throw new AppviewUnavailableError(cause); }
if (!response.ok) { let message = `${response.status} ${response.statusText}`.trim(); try { const body = (await response.json()) as { message?: unknown; error?: unknown }; const bodyMessage = typeof body.message === 'string' ? body.message : body.error; if (typeof bodyMessage === 'string') { message = bodyMessage; } } catch { // Keep the HTTP status message when the appview does not return JSON. }
throw new AppviewResponseError(response.status, `Appview ${url.pathname.slice('/xrpc/'.length)} failed (${response.status}): ${message}`); }
return (await response.json()) as T;};
export const appviewFallback = async <T>(primary: () => Promise<T>, fallback: () => Promise<T>): Promise<T> => { try { return await primary(); } catch (cause) { if (!(cause instanceof AppviewUnavailableError)) { throw cause; }
return fallback(); }};
export const listAllAppviewRecords = async <T>( nsid: string, subject: string, extraParams: Record<string, AppviewParamValue> = {},): Promise<Array<AppviewRecordView<T>>> => { const records: Array<AppviewRecordView<T>> = []; let cursor: string | undefined;
do { const page = await appviewJson<AppviewListResponse<T>>(nsid, { subject, limit: 100, cursor, ...extraParams, }); records.push(...page.items); cursor = page.cursor ?? undefined; } while (cursor);
return records;};
export const appviewBulkRecords = async <T>( nsid: string, paramName: string, uris: ResourceUri[],): Promise<Array<AppviewRecordView<T>>> => { if (uris.length === 0) { return []; }
const records: Array<AppviewRecordView<T>> = []; for (let index = 0; index < uris.length; index += 50) { const url = new URL(`/xrpc/${nsid}`, normalizeServiceUrl(getTangledAppviewService())); for (const uri of uris.slice(index, index + 50)) { url.searchParams.append(paramName, uri); }
const page = await appviewJsonUrl<AppviewBulkResponse<T>>(url); records.push(...page.items); }
return records;};
export const getAppviewRecordCount = async (nsid: string, subject: string): Promise<number> => { const count = await appviewJson<AppviewCountResponse>(nsid, { subject }); return count.count;};
export const getDistinctAppviewRecordCount = async (nsid: string, subject: string): Promise<number> => { const count = await appviewJson<AppviewCountResponse>(nsid, { subject }); return count.distinctAuthors ?? count.distinct_authors ?? count.count;};
export const getRepoViaAppview = async <T>( nsid: string, repo: { record: { uri: string } }, params: Record<string, AppviewParamValue> = {},): Promise<T> => appviewJson<T>(nsid, { repo: repo.record.uri, ...params, });
export const appviewOptionalKnotRequest = async <T>( appview: () => Promise<T>, direct: () => Promise<T>,): Promise<T> => { if (!getRouteKnotRequestsThroughAppview()) { return direct(); }
return appviewFallback(appview, direct);};