shelving
Version:
Toolkit for using data in JavaScript.
59 lines (58 loc) • 2.41 kB
JavaScript
import { AVOID_REFRESH } from "../../store/FetchStore.js";
import { awaitValues } from "../../util/async.js";
import { awaitDispose } from "../../util/dispose.js";
import { setMapItem } from "../../util/map.js";
import { EndpointStore } from "../store/EndpointStore.js";
/**
* Cache of `EndpointStore` objects for a single endpoint, keyed by serialized payload.
* - Use `get(payload)` to retrieve or create the `EndpointStore` for a given payload.
*/
export class EndpointCache {
_endpoints = new Map();
endpoint;
provider;
constructor(endpoint, provider) {
this.endpoint = endpoint;
this.provider = provider;
}
/** Get (or create) the `EndpointStore` for the given payload. */
get(payload, caller = this.get) {
const url = this.provider.renderURL(this.endpoint, payload, caller).href;
return this._endpoints.get(url) || setMapItem(this._endpoints, url, new EndpointStore(this.endpoint, payload, this.provider));
}
/**
* Fetch (or return a cached result) for the given payload.
* - Returns the cached value immediately if one exists.
* - Waits for the in-flight fetch if the store is loading.
* - Throws if the fetch fails, matching `APIProvider.call` behaviour.
*
* @param maxAge The maximum age (defaults to only refreshing if the value is still in a loading state).
*/
async call(payload, maxAge = AVOID_REFRESH, caller = this.call) {
const store = this.get(payload, caller);
await store.refresh(maxAge);
return store.value;
}
/** Invalidate a specific store. */
invalidate(payload, caller = this.invalidate) {
this.get(payload, caller)?.invalidate();
}
/** Invalidate all stores. */
invalidateAll() {
for (const store of this._endpoints.values())
store.invalidate();
}
/** Trigger a refetch on a specific store. */
async refresh(payload, maxAge, caller = this.invalidate) {
await this.get(payload, caller)?.refresh(maxAge);
}
/** Trigger a refetch on all stores. */
async refreshAll(maxAge) {
await awaitValues(...this._endpoints.values().map(store => store.refresh(maxAge)));
}
// Implement `AsyncDisposable`
[Symbol.asyncDispose]() {
return awaitDispose(...this._endpoints.values(), // Dispose all endpoints.
() => this._endpoints.clear());
}
}