@tanstack/db
Version:
A reactive client store for building super fast apps on sync
173 lines (172 loc) • 5.56 kB
JavaScript
import { isWhereSubset, isPredicateSubset, minusWherePredicates, unionWherePredicates } from "./predicate-utils.js";
class DeduplicatedLoadSubset {
constructor(opts) {
this.unlimitedWhere = void 0;
this.hasLoadedAllData = false;
this.limitedCalls = [];
this.inflightCalls = [];
this.generation = 0;
this.loadSubset = (options) => {
if (this.hasLoadedAllData) {
this.onDeduplicate?.(options);
return true;
}
if (this.unlimitedWhere !== void 0 && options.where !== void 0) {
if (isWhereSubset(options.where, this.unlimitedWhere)) {
this.onDeduplicate?.(options);
return true;
}
}
if (options.limit !== void 0) {
const alreadyLoaded = this.limitedCalls.some(
(loaded) => isPredicateSubset(options, loaded)
);
if (alreadyLoaded) {
this.onDeduplicate?.(options);
return true;
}
}
const matchingInflight = this.inflightCalls.find(
(inflight) => !inflight.lease.aborted && isPredicateSubset(options, inflight.options)
);
if (matchingInflight !== void 0) {
matchingInflight.lease.attach(options.signal);
const prom = matchingInflight.promise;
prom.then(() => this.onDeduplicate?.(options)).catch();
return prom;
}
const lease = createSharedAbortLease(options.signal);
const trackingOptions = cloneOptions({ ...options, signal: lease.signal });
const loadOptions = cloneOptions({ ...options, signal: lease.signal });
if (this.unlimitedWhere !== void 0 && options.limit === void 0) {
loadOptions.where = minusWherePredicates(loadOptions.where, this.unlimitedWhere) ?? loadOptions.where;
}
let resultPromise;
try {
resultPromise = this._loadSubset(loadOptions);
} catch (error) {
lease.dispose();
throw error;
}
if (resultPromise === true) {
if (!lease.aborted) this.updateTracking(trackingOptions);
lease.dispose();
return true;
} else {
const capturedGeneration = this.generation;
const inflightEntry = {
options: trackingOptions,
lease,
promise: resultPromise.then((result) => {
if (capturedGeneration === this.generation && !lease.aborted) {
this.updateTracking(trackingOptions);
}
return result;
}).finally(() => {
const index = this.inflightCalls.indexOf(inflightEntry);
if (index !== -1) {
this.inflightCalls.splice(index, 1);
}
lease.dispose();
})
};
this.inflightCalls.push(inflightEntry);
return inflightEntry.promise;
}
};
this._loadSubset = opts.loadSubset;
this.onDeduplicate = opts.onDeduplicate;
}
/**
* Reset all tracking state.
* Clears the history of loaded predicates and in-flight calls.
* Use this when you want to start fresh, for example after clearing the underlying data store.
*
* Note: Any in-flight requests will still complete, but they will not update the tracking
* state after the reset. This prevents old requests from repopulating cleared state.
*/
reset() {
this.unlimitedWhere = void 0;
this.hasLoadedAllData = false;
this.limitedCalls = [];
this.inflightCalls = [];
this.generation++;
}
updateTracking(options) {
if (options.limit === void 0) {
if (options.where === void 0) {
this.hasLoadedAllData = true;
this.unlimitedWhere = void 0;
this.limitedCalls = [];
this.inflightCalls = [];
} else if (this.unlimitedWhere === void 0) {
this.unlimitedWhere = options.where;
} else {
this.unlimitedWhere = unionWherePredicates([
this.unlimitedWhere,
options.where
]);
}
} else {
this.limitedCalls.push(options);
}
}
}
function createSharedAbortLease(initialSignal) {
const controller = initialSignal ? new AbortController() : void 0;
const listeners = /* @__PURE__ */ new Map();
let hasUnabortableOwner = initialSignal === void 0;
let activeAbortableOwners = 0;
const abortIfUnused = (reason) => {
if (!hasUnabortableOwner && listeners.size > 0 && activeAbortableOwners === 0) {
controller?.abort(reason);
}
};
const attach = (signal) => {
if (!signal) {
hasUnabortableOwner = true;
return;
}
if (listeners.has(signal)) return;
const onAbort = () => {
activeAbortableOwners -= 1;
abortIfUnused(signal.reason);
};
listeners.set(signal, onAbort);
if (signal.aborted) {
abortIfUnused(signal.reason);
} else {
activeAbortableOwners += 1;
signal.addEventListener(`abort`, onAbort, { once: true });
}
};
attach(initialSignal);
return {
signal: controller?.signal,
get aborted() {
return controller?.signal.aborted ?? false;
},
attach,
dispose: () => {
for (const [signal, listener] of listeners) {
signal.removeEventListener(`abort`, listener);
}
listeners.clear();
}
};
}
function cloneOptions(options) {
return {
...options,
orderBy: options.orderBy?.map((clause) => ({
...clause,
compareOptions: { ...clause.compareOptions }
})),
cursor: options.cursor ? { ...options.cursor } : void 0
};
}
export {
DeduplicatedLoadSubset,
cloneOptions
};
//# sourceMappingURL=subset-dedupe.js.map