~james/core

core/web/src/lib/api/write.ts -rw-r--r-- 2.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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { ok } from "@atcute/client";
import { mainSchema as createRecordSchema } from "@atcute/atproto/types/repo/createRecord";
import { mainSchema as getRecordSchema } from "@atcute/atproto/types/repo/getRecord";
import { mainSchema as deleteRecordSchema } from "@atcute/atproto/types/repo/deleteRecord";
import { mainSchema as putRecordSchema } from "@atcute/atproto/types/repo/putRecord";
import type { OAuthUserAgent } from "@atcute/oauth-browser-client";
import { createClient } from "#lib/auth/agent.ts";
import { clear } from "#lib/api/cache.ts";
import { atUriOf, cidOf, type AtUri, type Cid, type Nsid, type Rkey } from "#lib/api/syntax.ts";

export interface WrittenRecord {
	uri: AtUri;
	cid: Cid;
}

interface ReadRecord<T> {
	value: T;
	/** getRecord returns cid only when present, null can't be used as swapRecord */
	written: WrittenRecord | null;
}

export const readRecord = async <T>(
	agent: OAuthUserAgent,
	collection: Nsid,
	rkey: Rkey
): Promise<ReadRecord<T> | null> => {
	const response = await createClient(agent).call(getRecordSchema, {
		params: { repo: agent.sub, collection, rkey }
	});
	if (response.ok)
		return {
			value: response.data.value as T,
			written: response.data.cid
				? { uri: atUriOf(response.data.uri), cid: cidOf(response.data.cid) }
				: null
		};
	const error = (response.data as { error?: string } | undefined)?.error;
	if (error === "RecordNotFound" || error === "CouldNotFindRecord") return null;
	await ok(response);
	return null;
};

// every record write in the app goes through here, so the read cache is dropped
// on any mutation instead of each write site remembering which keys it dirties
export const createRecord = async <T extends { $type: string }>(
	agent: OAuthUserAgent,
	collection: Nsid,
	record: T,
	rkey?: Rkey
): Promise<WrittenRecord> => {
	const { uri, cid } = await ok(
		createClient(agent).call(createRecordSchema, {
			input: {
				repo: agent.sub,
				collection,
				...(rkey ? { rkey } : {}),
				record
			}
		})
	);
	clear();
	return { uri: atUriOf(uri), cid: cidOf(cid) };
};

export const putRecord = async <T extends { $type: string }>(
	agent: OAuthUserAgent,
	collection: Nsid,
	rkey: Rkey,
	record: T,
	// the write fails if the record moved since it was read
	swapRecord?: Cid
): Promise<WrittenRecord> => {
	const { uri, cid } = await ok(
		createClient(agent).call(putRecordSchema, {
			input: {
				repo: agent.sub,
				collection,
				rkey,
				...(swapRecord ? { swapRecord } : {}),
				record
			}
		})
	);
	clear();
	return { uri: atUriOf(uri), cid: cidOf(cid) };
};

export const deleteRecord = async (
	agent: OAuthUserAgent,
	collection: Nsid,
	rkey: Rkey,
	swapRecord?: Cid
): Promise<void> => {
	await ok(
		createClient(agent).call(deleteRecordSchema, {
			input: {
				repo: agent.sub,
				collection,
				rkey,
				...(swapRecord ? { swapRecord } : {})
			}
		})
	);
	clear();
};