~james/core

core/web/src/lib/api/awaitRecord.ts -rw-r--r-- 1.8 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 15 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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<void> => {
	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<void> => {
	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<boolean> => {
	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<void>();
		setTimeout(resolve, pollMs);
		await promise;
	}
};