Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221import * as v from "@atcute/lexicons/validations";import type { Did, Handle } from "@atcute/lexicons/syntax";import type { OAuthUserAgent } from "@atcute/oauth-browser-client";import { buildUrl, toResponseError } from "$lib/api/_request";import type { QueryValue, XrpcRequestInit } from "$lib/api/client";
export const CREATE_DELEGATE_NSID = "farm.tranquil.delegation.createAccount";export const LIST_DELEGATES_NSID = "farm.tranquil.delegation.listControlledAccounts";
export const LIST_DELEGATES_ENDPOINT = "_delegation.listControlledAccounts";export const CREATE_DELEGATE_ENDPOINT = "_delegation.createDelegatedAccount";
const delegatedAccountSchema = v.object({ did: v.didString(), grantedAt: v.datetimeString(), grantedScopes: v.string(), handle: v.optional(v.handleString())});
export const listDelegatesSchema = v.object({ get accounts() { return v.array(delegatedAccountSchema); }});
export type DelegatedAccountInfo = v.InferInput<typeof delegatedAccountSchema>;
export interface CreateDelegatedAccountInput { /** Full handle or local handle prefix. */ handle: string; /** Scopes granted to the initial controller. */ controllerScopes: string; email?: string;}
export interface CreateDelegatedAccountOutput { did: Did; handle: Handle;}
// agent.handle resolves a relative path against the session's PDS, so only// the path and query of the built URL are kept; the origin is a placeholder.const pathFor = (nsid: string, params?: Record<string, QueryValue>): string => { const url = buildUrl("http://pds.invalid", nsid, params); return `${url.pathname}${url.search}`;};
const tranquilGet = async <T>( agent: OAuthUserAgent, nsid: string, params?: Record<string, QueryValue>, init?: XrpcRequestInit): Promise<T> => { const headers = new Headers(init?.headers); if (!headers.has("accept")) headers.set("accept", "application/json");
const response = await agent.handle(pathFor(nsid, params), { headers, signal: init?.signal }); if (!response.ok) throw await toResponseError(response); return (await response.json()) as T;};
const tranquilPost = async <T>( agent: OAuthUserAgent, nsid: string, body?: unknown, init?: XrpcRequestInit): Promise<T> => { const headers = new Headers(init?.headers); if (!headers.has("content-type")) headers.set("content-type", "application/json"); if (!headers.has("accept")) headers.set("accept", "application/json");
const response = await agent.handle(pathFor(nsid), { method: "POST", headers, body: body === undefined ? undefined : JSON.stringify(body), signal: init?.signal }); if (!response.ok) throw await toResponseError(response); return (await response.json()) as T;};
// ---- shared types, mirrored from tranquil-pds's delegation api ----
export type DelegationScope = string;
export interface ScopePreset { name: string; label: string; description: string; scopes: DelegationScope;}
export interface ControllerInfo { did: Did; handle?: Handle; grantedScopes: DelegationScope; grantedAt: string; isActive: boolean; isLocal: boolean;}
export type DelegationActionType = | "GrantCreated" | "GrantRevoked" | "ScopesModified" | "TokenIssued" | "RepoWrite" | "BlobUpload" | "AccountAction";
export interface AuditLogEntry { id: string; delegatedDid: Did; actorDid: Did; controllerDid?: Did; actionType: DelegationActionType; actionDetails?: unknown; createdAt: string;}
export interface ResolvedIdentity { did: Did; handle?: Handle; pdsUrl?: string; isLocal: boolean;}
// ---- controllers on *this* account ----
export const listControllers = ( agent: OAuthUserAgent, init?: XrpcRequestInit): Promise<ControllerInfo[]> => tranquilGet<{ controllers: ControllerInfo[] }>( agent, "_delegation.listControllers", undefined, init ).then((r) => r.controllers);
export const addController = ( agent: OAuthUserAgent, input: { controllerDid: Did; grantedScopes: DelegationScope }, init?: XrpcRequestInit): Promise<{ success: boolean }> => tranquilPost( agent, "_delegation.addController", { controller_did: input.controllerDid, granted_scopes: input.grantedScopes }, init );
export const removeController = ( agent: OAuthUserAgent, controllerDid: Did, init?: XrpcRequestInit): Promise<{ success: boolean }> => tranquilPost(agent, "_delegation.removeController", { controller_did: controllerDid }, init);
export const updateControllerScopes = ( agent: OAuthUserAgent, input: { controllerDid: Did; grantedScopes: DelegationScope }, init?: XrpcRequestInit): Promise<{ success: boolean }> => tranquilPost( agent, "_delegation.updateControllerScopes", { controller_did: input.controllerDid, granted_scopes: input.grantedScopes }, init );
// ---- accounts this account controls ----
export const listControlledAccounts = ( agent: OAuthUserAgent, init?: XrpcRequestInit): Promise<DelegatedAccountInfo[]> => tranquilGet<{ accounts: DelegatedAccountInfo[] }>( agent, LIST_DELEGATES_ENDPOINT, undefined, init ).then((r) => r.accounts);
export const createDelegatedAccount = ( agent: OAuthUserAgent, input: CreateDelegatedAccountInput, init?: XrpcRequestInit): Promise<CreateDelegatedAccountOutput> => tranquilPost(agent, CREATE_DELEGATE_ENDPOINT, input, init);
// ---- audit log, scope presets, identity resolution ----
export const getAuditLog = ( agent: OAuthUserAgent, params?: { limit?: number; offset?: number }, init?: XrpcRequestInit): Promise<{ entries: AuditLogEntry[]; total: number }> => tranquilGet(agent, "_delegation.getAuditLog", params as Record<string, QueryValue>, init);
export const getScopePresets = ( agent: OAuthUserAgent, init?: XrpcRequestInit): Promise<ScopePreset[]> => tranquilGet<{ presets: ScopePreset[] }>( agent, "_delegation.getScopePresets", undefined, init ).then((r) => r.presets);
export const resolveController = ( agent: OAuthUserAgent, identifier: string, init?: XrpcRequestInit): Promise<ResolvedIdentity> => tranquilGet(agent, "_delegation.resolveController", { identifier }, init);