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();
};