UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

270 lines (269 loc) • 8.81 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const expressions = require("../compiler/expressions.cjs"); const orderedSourceLoader = require("./ordered-source-loader.cjs"); const utils = require("./utils.cjs"); const subsetDemandController = require("./subset-demand-controller.cjs"); class CollectionSubscriber { constructor(sourceId, alias, collection, collectionConfigBuilder) { this.sourceId = sourceId; this.alias = alias; this.collection = collection; this.fQ = collectionConfigBuilder; this.az = /* @__PURE__ */ new Map(); this.fh = /* @__PURE__ */ new Map(); this.demand = new subsetDemandController.SubsetDemandController(); } subscribe() { const whereClause = this.fQ.sourceWhereClausesCache?.get(this.sourceId); return this.fi( whereClause ? expressions.normalizeExpressionPaths(whereClause, this.alias) : void 0 ); } fi(whereExpression) { const orderByInfo = this.fQ.optimizableOrderByCollections[this.sourceId]; const trackLoadResult = (result) => { if (result instanceof Promise) { const trackedResult = result.catch(async (error) => { await Promise.resolve(); throw error; }); this.fQ.trackSubsetLoadPromise(trackedResult); } }; const onStatusChange = (event) => { if (this.fQ.isLazySource(this.sourceId)) return; const subscription2 = event.subscription; if (event.status === `loadingSubset`) { this.d$(subscription2); } else { const deferred = this.az.get(subscription2); if (deferred) { this.az.delete(subscription2); deferred.resolve(); } } }; const onLoadSubsetError = (event) => { this.fQ.recordSubsetError( event.error, // Lazy demand owns its fatal-error path. For eager sources, one // successful page does not finish initial ordered refinement. !this.fQ.isLazySource(this.sourceId) && this.fQ.liveQueryCollection?.status === `loading` ); }; let subscription; if (orderByInfo) { subscription = this.da( whereExpression, orderByInfo, onStatusChange, trackLoadResult, onLoadSubsetError ); } else { const includeInitialState = (this.collection.config.syncMode !== `on-demand` || this.fQ.query.limit !== 0) && !this.fQ.isLazySource(this.sourceId); subscription = this.ek( whereExpression, includeInitialState, onStatusChange, trackLoadResult, onLoadSubsetError ); this.cQ(subscription); } if (!this.fQ.isLazySource(this.sourceId) && subscription.status === `loadingSubset`) { this.d$(subscription); } return subscription; } cQ(subscription) { const unsubscribe = () => { const deferred = this.az.get(subscription); if (deferred) { this.az.delete(subscription); deferred.resolve(); } try { this.demand.clear(); } finally { subscription.unsubscribe(); } }; this.fQ.currentSyncState.unsubscribeCallbacks.add( unsubscribe ); } setDemand(subscription, plan, keys) { let update; try { update = this.demand.setDemand(subscription, plan, keys); } catch (error) { if (!Object.is(subscription.lastError, error)) throw error; const isInitialSync = this.fQ.liveQueryCollection?.status === `loading`; const generation2 = this.fQ.beginDemand(plan.id); this.fQ.failDemand(plan.id, generation2, error); if (isInitialSync) throw error; return; } if (!update.changed) return; if (update.empty) { this.fQ.retireDemand(plan.id); return; } const generation = this.fQ.beginDemand( plan.id, update.ready instanceof Promise ); if (update.ready instanceof Promise) { void update.ready.then( () => this.fQ.settleDemand(plan.id, generation), (error) => this.fQ.failDemand(plan.id, generation, error) ); } else { this.fQ.settleDemand(plan.id, generation); } } dV(changes) { const changesArray = Array.isArray(changes) ? changes : [...changes]; const reconciledChanges = utils.reconcileChangesForD2( changesArray, this.fh ); const input = this.fQ.currentSyncState.inputs[this.sourceId]; if (utils.sendChangesToInput(input, reconciledChanges) > 0) { this.fQ.advanceGraphInputRevision(); } this.fQ.scheduleGraphRun(); } ek(whereExpression, includeInitialState, onStatusChange, onLoadSubsetResult, onLoadSubsetError) { const sendChanges = (changes) => { this.dV(changes); }; const hints = utils.computeSubscriptionOrderByHints( this.fQ.query, this.alias, this.fQ.queryCompareOptions ); const subscription = this.collection.subscribeChanges(sendChanges, { ...includeInitialState && { includeInitialState }, whereExpression, onStatusChange, onLoadSubsetError, truncateReplayPublication: this.ci(), orderBy: hints.orderBy, limit: hints.limit, onLoadSubsetResult: includeInitialState ? onLoadSubsetResult : void 0 }); return subscription; } da(whereExpression, orderByInfo, onStatusChange, onLoadSubsetResult, onLoadSubsetError) { const subscriptionHolder = {}; const sendChangesInRange = (changes) => { const subscription2 = subscriptionHolder.current; if (!subscription2) return; const changesArray = Array.isArray(changes) ? changes : [...changes]; this.bk?.onSourceChanges(changesArray, this.fh); const splittedChanges = utils.splitUpdates(changesArray); this.dV(splittedChanges); }; const subscription = this.collection.subscribeChanges(sendChangesInRange, { whereExpression, onStatusChange, onLoadSubsetError, truncateReplayPublication: this.ci(() => { const loader = this.bk; this.fQ.trackOrderedLoadPromise( Promise.resolve().then(() => loader?.loadFullSource()), true ); }) }); subscriptionHolder.current = subscription; this.cQ(subscription); const truncateUnsubscribe = this.collection.on(`truncate`, () => { this.bk?.resetCursor(); }); subscription.on(`unsubscribed`, () => { truncateUnsubscribe(); subscriptionHolder.current = void 0; this.bk?.dispose(); this.bk = void 0; }); this.bk = new orderedSourceLoader.OrderedSourceLoader( orderByInfo, subscription, this.alias, (result, holdPublication, settlesAsync) => { if (result instanceof Promise) { this.fQ.trackOrderedLoadPromise( result, holdPublication && settlesAsync && !subscription.hasPendingTruncateReplacement ); } onLoadSubsetResult(result); }, () => this.fQ.liveQueryCollection?.status === `ready` && !this.fQ.hasActiveWindowOperation(), () => this.fQ.getGraphInputRevision(), () => orderByInfo.joinedFilterSourceId !== void 0 && this.fQ.hasPendingJoinedWork( orderByInfo.joinedFilterSourceId ) ); this.bk.start(); return subscription; } ci(onStart) { const syncRunGeneration = this.fQ.getSyncRunGeneration(); return { start: () => { onStart?.(); }, succeed: () => { if (syncRunGeneration !== this.fQ.getSyncRunGeneration()) { return; } this.bk?.settleFullSourceReplay(); this.fQ.scheduleGraphRunIfSyncRunCurrent( syncRunGeneration ); } }; } // This function is called by maybeRunGraph // after each iteration of the query pipeline // to ensure that the orderBy operator has enough data to work with loadMoreIfNeeded(subscription) { if (subscription.hasPendingTruncateReplacement && !this.fQ.hasActiveWindowOperation()) { return; } const orderByInfo = this.fQ.optimizableOrderByCollections[this.sourceId]; if (!orderByInfo) { return; } try { const pending = this.bk?.loadMore( this.fQ.getActiveWindowOperationGeneration() ); if (pending) { this.fQ.trackSubsetLoadOperationPromise(pending); } } catch (error) { if (!Object.is(subscription.lastError, error)) throw error; } } d$(subscription) { if (this.az.has(subscription)) { return; } let resolve; const promise = new Promise((res) => { resolve = res; }); this.az.set(subscription, { resolve }); this.fQ.trackSubsetLoadPromise(promise); } } exports.CollectionSubscriber = CollectionSubscriber; //# sourceMappingURL=collection-subscriber.cjs.map