113 lines
3.0 KiB
TypeScript
113 lines
3.0 KiB
TypeScript
import {
|
|
type CollectionId,
|
|
type AnyDocument,
|
|
CollectionIds,
|
|
type Relationship,
|
|
type DocumentId,
|
|
type RelationshipId,
|
|
type DbRecord,
|
|
} from "./types";
|
|
|
|
export type CacheKey<C extends CollectionId = CollectionId> = `${C}:${string}`;
|
|
export type CacheValue<C extends CollectionId = CollectionId> = {
|
|
record: Promise<DbRecord<C>>;
|
|
subscriptions: ((record: DbRecord<C> | null) => void)[];
|
|
};
|
|
type DocumentKey = CacheKey<typeof CollectionIds.Documents>;
|
|
type RelationshipKey = CacheKey<typeof CollectionIds.Relationships>;
|
|
|
|
export class RecordCache {
|
|
private cache: Record<`${CollectionId}:${string}`, CacheValue> = {};
|
|
|
|
static makeKey<C extends CollectionId>(
|
|
collectionId: C,
|
|
id: string,
|
|
): CacheKey<C> {
|
|
return `${collectionId}:${id}`;
|
|
}
|
|
|
|
static docKey = (id: DocumentId): DocumentKey =>
|
|
RecordCache.makeKey(CollectionIds.Documents, id);
|
|
|
|
static relationKey = (id: RelationshipId): RelationshipKey =>
|
|
RecordCache.makeKey(CollectionIds.Relationships, id);
|
|
|
|
get = <C extends CollectionId>(
|
|
key: CacheKey<C>,
|
|
): Promise<DbRecord<C>> | undefined =>
|
|
this.cache[key]?.record as Promise<DbRecord<C>> | undefined;
|
|
|
|
pending = <C extends CollectionId>(
|
|
key: CacheKey<C>,
|
|
record: Promise<DbRecord<C>>,
|
|
) => {
|
|
if (this.cache[key] === undefined) {
|
|
this.cache[key] = {
|
|
record: record,
|
|
subscriptions: [],
|
|
};
|
|
}
|
|
const entry = this.cache[key];
|
|
|
|
entry.record = record;
|
|
|
|
record.then((record) => {
|
|
for (const subscription of entry.subscriptions) {
|
|
subscription(record);
|
|
}
|
|
for (const doc of record.expand?.secondary ?? []) {
|
|
this.set(doc);
|
|
}
|
|
for (const rel of record.expand?.relationships_via_primary ?? []) {
|
|
this.set(rel);
|
|
}
|
|
});
|
|
};
|
|
|
|
set = <C extends CollectionId>(record: DbRecord<C>) => {
|
|
const key = RecordCache.makeKey(
|
|
record.collectionName as CollectionId,
|
|
record.id,
|
|
);
|
|
if (this.cache[key] === undefined) {
|
|
this.cache[key] = {
|
|
record: Promise.resolve(record),
|
|
subscriptions: [],
|
|
};
|
|
}
|
|
const entry = this.cache[key];
|
|
|
|
entry.record = Promise.resolve(record);
|
|
|
|
for (const subscription of entry.subscriptions) {
|
|
subscription(record);
|
|
}
|
|
|
|
for (const doc of record.expand?.secondary ?? []) {
|
|
this.set(doc);
|
|
}
|
|
for (const rel of record.expand?.relationships_via_primary ?? []) {
|
|
this.set(rel);
|
|
}
|
|
};
|
|
|
|
remove = (key: CacheKey) => {
|
|
const entry = this.cache[key];
|
|
delete this.cache[key];
|
|
for (const subscription of entry.subscriptions) {
|
|
subscription(null);
|
|
}
|
|
};
|
|
|
|
getDocument = (id: DocumentId): AnyDocument | undefined =>
|
|
this.get(RecordCache.docKey(id)) as AnyDocument | undefined;
|
|
|
|
getRelationship = (id: RelationshipId): Relationship | undefined =>
|
|
this.get(RecordCache.relationKey(id)) as Relationship | undefined;
|
|
|
|
removeDocument = (id: DocumentId) => this.remove(RecordCache.docKey(id));
|
|
|
|
removeRelationship = (id: RelationshipId) =>
|
|
this.remove(RecordCache.relationKey(id));
|
|
}
|