Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129import { ok } from "@atcute/client";import type { Did } from "@atcute/lexicons/syntax";import type { BobbinContext } from "$lib/api/client";import { jsonGet } from "$lib/api/_request";import { mainSchema as listKnotsSchema } from "$lib/api/lexicons/types/sh/tangled/knot/listKnots";import { mainSchema as listSpindleMembersSchema } from "$lib/api/lexicons/types/sh/tangled/spindle/listMembers";import { mainSchema as listSpindlesSchema } from "$lib/api/lexicons/types/sh/tangled/spindle/listSpindles";import { parseResourceUri } from "@atcute/lexicons/syntax";import { hostForServiceDid } from "$lib/auth/agent";
interface Page<T> { items: T[]; cursor?: string;}
interface RecordItem { uri: string; value: unknown;}
const allPages = async <T>(load: (cursor?: string) => Promise<Page<T>>): Promise<T[]> => { const items: T[] = []; let cursor: string | undefined; do { const page = await load(cursor); items.push(...page.items); cursor = page.cursor || undefined; } while (cursor); return items;};
const knotDomain = (value: unknown): string | null => { if (typeof value !== "object" || value === null || !("domain" in value)) return null; return typeof value.domain === "string" && value.domain ? value.domain : null;};
const repoFromUri = (uri: unknown): string | null => { if (typeof uri !== "string") return null; try { return parseResourceUri(uri).repo; } catch { return null; }};
const recordName = (uri: unknown): string | null => { if (typeof uri !== "string") return null; try { return parseResourceUri(uri).rkey ?? null; } catch { return null; }};
const knotMembershipHost = (item: unknown): string | null => { if (typeof item !== "object" || item === null) return null; const record = item as { uri?: unknown; value?: unknown }; const repo = repoFromUri(record.uri); const host = repo ? hostForServiceDid(repo) : null; if (host) return host; if (record.value !== undefined) { const domain = knotDomain(record.value); if (domain) return domain; } return null;};
const spindleInstance = (value: unknown): string | null => { if (typeof value !== "object" || value === null || !("instance" in value)) return null; return typeof value.instance === "string" && value.instance ? value.instance : null;};
const targetNames = ( owned: RecordItem[], memberships: RecordItem[], memberName: (item: RecordItem) => string | null): string[] => { const names = new Set<string>(); for (const item of owned) { const name = recordName(item.uri); if (name) names.add(name); } for (const item of memberships) { const name = memberName(item); if (name) names.add(name); } return [...names].sort((a, b) => a.localeCompare(b));};
export const availableKnots = async (ctx: BobbinContext, did: string): Promise<string[]> => { const [owned, memberships] = await Promise.all([ allPages((cursor) => ok( ctx.xrpc.call(listKnotsSchema, { params: { subject: did as Did, limit: 1_000, cursor } }) ) ), allPages((cursor) => jsonGet<Page<RecordItem>>(ctx, "sh.tangled.knot.listMembers", { subject: did, limit: 1_000, cursor }) ) ]); return targetNames(owned, memberships, knotMembershipHost);};
export const availableSpindles = async (ctx: BobbinContext, did: string): Promise<string[]> => { const [owned, memberships] = await Promise.all([ allPages((cursor) => ok( ctx.xrpc.call(listSpindlesSchema, { params: { subject: did as Did, limit: 1_000, cursor } }) ) ), allPages((cursor) => ok( ctx.xrpc.call(listSpindleMembersSchema, { params: { subject: did as Did, limit: 1_000, cursor } }) ) ) ]); return targetNames(owned, memberships, (item) => spindleInstance(item.value));};