shelving
Version:
Toolkit for using data in JavaScript.
72 lines (71 loc) • 2.72 kB
JavaScript
import { RequiredError } from "../error/RequiredError.js";
import { BooleanStore } from "../store/BooleanStore.js";
import { OptionalDataStore } from "../store/DataStore.js";
import { getGetter } from "../util/class.js";
import { NONE } from "../util/constants.js";
import { getItem } from "../util/item.js";
import { runSequence } from "../util/sequence.js";
/** Store a single item. */
export class ItemStore extends OptionalDataStore {
provider;
collection;
id;
busy = new BooleanStore();
/** Get the data of this store (throws `RequiredError` if item doesn't exist). */
get data() {
const item = this.value;
if (!item)
throw new RequiredError(`Item does not exist in collection "${this.collection}"`, {
store: this,
provider: this.provider,
collection: this.collection,
id: this.id,
caller: getGetter(this, "data"),
});
return item;
}
/** Set the data of this store. */
set data(data) {
this.value = getItem(this.id, data);
}
constructor(collection, id, provider, memory) {
const time = memory?.getItemTime(collection, id);
const item = memory?.getItem(collection, id);
super(typeof time === "number" || item ? item : NONE, time); // Use the cached value if it was definitely cached or is not undefined.
if (memory)
this.starter = store => runSequence(store.through(memory.getCachedItemSequence(collection, id)));
this.provider = provider;
this.collection = collection;
this.id = id;
// Start loading the value from the provider if it wasn't cached.
if (typeof time !== "number")
this.refresh();
}
/** Refresh this store from the source provider. */
refresh(provider = this.provider) {
if (!this.busy.value)
void this._refresh(provider);
}
async _refresh(provider) {
this.busy.value = true;
this.reason = undefined; // Optimistically clear the error.
try {
this.value = await provider.getItem(this.collection, this.id);
}
catch (thrown) {
this.reason = thrown;
}
finally {
this.busy.value = false;
}
}
/** Refresh this store if data in the cache is older than `maxAge` (in milliseconds). */
refreshStale(maxAge, provider) {
if (this.age > maxAge)
this.refresh(provider);
}
/** Subscribe this store to a provider. */
connect(provider = this.provider) {
return runSequence(this.through(provider.getItemSequence(this.collection, this.id)));
}
}