Something went wrong. Try again.
A local-first sync engine for atproto
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208import { AtUri, type AtUriString } from '@atproto/syntax';import { type Arr, arr, Cell, cell, type Num, num, type Writable } from 'bireactive';import { type ATCache, MicrocosmCache } from './cache';import type { CidString, CollectionURI, DidString, HandleString, LexiconMap, LexiconNsids, NsidString, RecordKeyString, RecordPath, RecordURI, RepoURI,} from './types';
let atCache: ATCache = new MicrocosmCache();
export function isRecordOfCollection<N extends NsidString = NsidString>(nsid: N, record: unknown): record is Record<N> { return record instanceof Record && record.collection.nsid === nsid;}
export class Record< N extends NsidString = NsidString, T = N extends keyof LexiconMap ? LexiconMap[N] : any,> extends Cell<Readonly<T> | null> { static setCache(cache: ATCache) { atCache = cache; } #parsedUri;
#uri; get uri(): AtUriString { return this.#uri; }
#repo; get repo(): Repo { return this.#repo; }
#collection; get collection(): Collection<N> { return this.#collection; }
#rkey; get rkey(): RecordKeyString { return this.#rkey; }
#cid = ''; get cid(): CidString { return this.#cid; }
constructor(uri: RecordURI<N>) { super(null);
this.#parsedUri = new AtUri(uri); this.#uri = this.#parsedUri.toString();
const repoURI = `at://${this.#parsedUri.did}` as RepoURI; this.#repo = repo(repoURI);
const collectionURI = `${repoURI}/${this.#parsedUri.collectionSafe}` as CollectionURI<N>; this.#collection = collection(collectionURI);
this.#rkey = this.#parsedUri.rkeySafe;
atCache.getRecord(uri).then((r) => { if (r === null || r.uri !== this.#uri) return;
this.#cid = r.cid; (this as unknown as Writable<Record>).value = r.value; }); }
backlinks<N extends LexiconNsids>(nsid: N, field: RecordPath): Arr<Record<N>>; backlinks<N extends NsidString>(nsid: N, field: RecordPath): Arr<Record<N>>; backlinks(nsid: NsidString, field: RecordPath): Arr<Record> { return arr(); }
backlinksCount<N extends LexiconNsids>(nsid: N, field: RecordPath): Num; backlinksCount<N extends NsidString = NsidString>(nsid: N, field: RecordPath): Num; backlinksCount(nsid: NsidString, field: RecordPath): Num { return num(); }}
const records = new Map<RecordURI, Record>();
export function record<N extends NsidString = NsidString>(uri: RecordURI<N>): Record<N> { let record = records.get(uri as RecordURI) as Record<N> | undefined;
if (record === undefined) { record = new Record(uri); records.set(uri as RecordURI, record as unknown as Record); }
return record as Record<N>;}
export class Collection<N extends NsidString = NsidString> { static setCache(cache: ATCache) { atCache = cache; }
#uri; get uri() { return this.#uri; }
#repo; get repo(): Repo { return this.#repo; }
#nsid; get nsid(): N { return this.#nsid; }
constructor(uri: CollectionURI<N>) { this.#uri = uri;
const parsedURI = new AtUri(uri);
const repoURI = `at://${parsedURI.did}` as RepoURI; this.#repo = repo(repoURI);
this.#nsid = parsedURI.collectionSafe as N; }
get records(): Arr<Record<N>> { return arr(); }}
const collections = new Map<CollectionURI, Collection>();
export function collection<N extends NsidString = NsidString>(uri: CollectionURI<N>): Collection<N> { let collection = collections.get(uri as CollectionURI) as Collection<N> | undefined;
if (collection === undefined) { collection = new Collection(uri); collections.set(uri as CollectionURI, collection as unknown as Collection); }
return collection as Collection<N>;}
export class Repo { static setCache(cache: ATCache) { atCache = cache; }
#uri; get uri(): RepoURI { return this.#uri; }
#did; get did() { return this.#did; }
#handle = cell<HandleString | null>(null); get handle() { return this.#handle; }
#pds = cell<string | null>(null); get pds() { return this.#pds; }
constructor(uri: RepoURI) { this.#uri = uri;
const parsedURI = new AtUri(uri);
this.#did = cell<DidString | null>(parsedURI.did); }
collection<N extends LexiconNsids>(nsid: N): Collection<N>; collection<N extends NsidString = NsidString>(nsid: N): Collection<N>; collection(nsid: NsidString): Collection { const collectionUri = `${this.#uri}/${nsid}` as CollectionURI; return collection(collectionUri); }}
const repos = new Map<RepoURI, Repo>();
export function repo(uri: RepoURI): Repo { let repo = repos.get(uri);
if (repo === undefined) { repo = new Repo(uri); repos.set(uri, repo); }
return repo;}