UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

173 lines (172 loc) 5.7 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const predicateUtils = require("./predicate-utils.cjs"); 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 (predicateUtils.isWhereSubset(options.where, this.unlimitedWhere)) { this.onDeduplicate?.(options); return true; } } if (options.limit !== void 0) { const alreadyLoaded = this.limitedCalls.some( (loaded) => predicateUtils.isPredicateSubset(options, loaded) ); if (alreadyLoaded) { this.onDeduplicate?.(options); return true; } } const matchingInflight = this.inflightCalls.find( (inflight) => !inflight.lease.aborted && predicateUtils.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 = predicateUtils.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 = predicateUtils.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 }; } exports.DeduplicatedLoadSubset = DeduplicatedLoadSubset; exports.cloneOptions = cloneOptions; //# sourceMappingURL=subset-dedupe.cjs.map