import { ok } from "@atcute/client"; import { ClientResponseError, type BobbinContext } from "#lib/api/client.ts"; import type { RawCid, RawResourceUri } from "#lib/api/syntax.ts"; import { orNull } from "#lib/api/load.ts"; import { mainSchema as awaitRecordSchema } from "#lib/api/lexicons/types/org/tangled/bobbin/awaitRecord.ts"; export interface RecordRevision { uri: RawResourceUri; cid?: RawCid; } export const awaitRecord = async (ctx: BobbinContext, revision: RecordRevision) => { return ok( ctx.xrpc.call(awaitRecordSchema, { params: { uri: revision.uri, ...(revision.cid ? { cid: revision.cid } : {}) } }) ); }; export const awaitIndexedRecord = async ( ctx: BobbinContext, revision: RecordRevision ): Promise => { const result = await awaitRecord(ctx, revision); if (result.status !== "indexed") { throw new Error(result.detail || result.reason || "Bobbin rejected the record."); } }; export const awaitIndexedOrSaved = async ( ctx: BobbinContext, revision: RecordRevision ): Promise => { try { await awaitIndexedRecord(ctx, revision); } catch (cause) { if (cause instanceof ClientResponseError || cause instanceof TypeError) { throw new Error("Saved. It can take a minute to show up.", { cause }); } throw cause; } }; const DELETION_BUDGET_MS = 5000; const DELETION_POLL_MS = 150; export const awaitDeletedRecord = async ( ctx: BobbinContext, uri: RawResourceUri, { budgetMs = DELETION_BUDGET_MS, pollMs = DELETION_POLL_MS } = {} ): Promise => { const deadline = Date.now() + budgetMs; for (;;) { const result = await orNull(awaitRecord(ctx, { uri })); if (result === null) return false; if (result.status !== "indexed") return true; if (Date.now() >= deadline) return false; const { promise, resolve } = Promise.withResolvers(); setTimeout(resolve, pollMs); await promise; } };