import { 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(nsid: N, record: unknown): record is Record { 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 | 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 { return this.#collection; } #rkey; get rkey(): RecordKeyString { return this.#rkey; } #cid = ''; get cid(): CidString { return this.#cid; } constructor(uri: RecordURI) { 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; 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).value = r.value; }); } backlinks(nsid: N, field: RecordPath): Arr>; backlinks(nsid: N, field: RecordPath): Arr>; backlinks(nsid: NsidString, field: RecordPath): Arr { return arr(); } backlinksCount(nsid: N, field: RecordPath): Num; backlinksCount(nsid: N, field: RecordPath): Num; backlinksCount(nsid: NsidString, field: RecordPath): Num { return num(); } } const records = new Map(); export function record(uri: RecordURI): Record { let record = records.get(uri as RecordURI) as Record | undefined; if (record === undefined) { record = new Record(uri); records.set(uri as RecordURI, record as unknown as Record); } return record as Record; } export class Collection { 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) { 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> { return arr(); } } const collections = new Map(); export function collection(uri: CollectionURI): Collection { let collection = collections.get(uri as CollectionURI) as Collection | undefined; if (collection === undefined) { collection = new Collection(uri); collections.set(uri as CollectionURI, collection as unknown as Collection); } return collection as Collection; } export class Repo { static setCache(cache: ATCache) { atCache = cache; } #uri; get uri(): RepoURI { return this.#uri; } #did; get did() { return this.#did; } #handle = cell(null); get handle() { return this.#handle; } #pds = cell(null); get pds() { return this.#pds; } constructor(uri: RepoURI) { this.#uri = uri; const parsedURI = new AtUri(uri); this.#did = cell(parsedURI.did); } collection(nsid: N): Collection; collection(nsid: N): Collection; collection(nsid: NsidString): Collection { const collectionUri = `${this.#uri}/${nsid}` as CollectionURI; return collection(collectionUri); } } const repos = new Map(); export function repo(uri: RepoURI): Repo { let repo = repos.get(uri); if (repo === undefined) { repo = new Repo(uri); repos.set(uri, repo); } return repo; }