@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 87.8 kB
Source Map (JSON)
{"version":3,"file":"subscription.cjs","sources":["../../../src/collection/subscription.ts"],"sourcesContent":["import { ensureIndexForExpression } from '../indexes/auto-index.js'\nimport { and, eq } from '../query/builder/functions.js'\nimport { PropRef, Value } from '../query/ir.js'\nimport { EventEmitter } from '../event-emitter.js'\nimport { compileExpression } from '../query/compiler/evaluators.js'\nimport { buildCursor, buildCursorCurrent } from '../utils/cursor.js'\nimport { deepEquals } from '../utils.js'\nimport { normalizeError } from '../utils/error.js'\nimport { runAllCallbacks } from '../utils/callbacks.js'\nimport { createDeferred } from '../deferred.js'\nimport { LoadSubsetOperationAbortedError } from '../errors.js'\nimport {\n createFilterFunctionFromExpression,\n createFilteredCallback,\n} from './change-events.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { IndexReader } from '../indexes/base-index.js'\nimport type {\n ChangeMessage,\n LoadSubsetOptions,\n LoadSubsetRequestResult,\n Subscription,\n SubscriptionEvents,\n SubscriptionLoadSubsetErrorEvent,\n SubscriptionStatus,\n SubscriptionUnsubscribedEvent,\n} from '../types.js'\nimport type { CollectionImpl } from './index.js'\nimport type { Deferred } from '../deferred.js'\n\ntype RequestSnapshotOptions = {\n where?: BasicExpression<boolean>\n signal?: AbortSignal\n optimizedOnly?: boolean\n trackLoadSubsetPromise?: boolean\n /** Optional orderBy to pass to loadSubset for backend optimization */\n orderBy?: OrderBy\n /** Optional limit to pass to loadSubset for backend optimization */\n limit?: number\n /** Callback that receives the normalized loadSubset result for internal tracking */\n onLoadSubsetResult?: SubsetResultObserver\n /** Called when the local snapshot must fall back from an index to a scan. */\n onUnoptimized?: () => void\n}\n\ntype RequestLimitedSnapshotOptions = {\n orderBy: OrderBy\n limit: number\n /** A single cursor value; composite cursor inputs are rejected. */\n minValues?: Array<unknown>\n /** Row offset for offset-based pagination (passed to sync layer) */\n offset?: number\n /** Whether to track the loadSubset promise on this subscription (default: true) */\n trackLoadSubsetPromise?: boolean\n /** Callback that receives the normalized loadSubset result for internal tracking */\n onLoadSubsetResult?: SubsetResultObserver\n}\n\nexport type ReleaseLoadSubset = (primaryFailure?: { error: unknown }) => void\n\ntype SubsetResultObserver = (\n result: LoadSubsetRequestResult,\n options: LoadSubsetOptions,\n release: ReleaseLoadSubset,\n) => void\n\ntype CollectionSubscriptionOptions = {\n includeInitialState?: boolean\n /** Pre-compiled expression for filtering changes */\n whereExpression?: BasicExpression<boolean>\n /** Callback to call when the subscription is unsubscribed */\n onUnsubscribe?: (event: SubscriptionUnsubscribedEvent) => void\n /** Callback for subset-load failures scoped to this subscription. */\n onLoadSubsetError?: (event: SubscriptionLoadSubsetErrorEvent) => void\n truncateReplayPublication?: TruncateReplayPublicationControl\n}\n\ntype TruncateReplayPublicationControl = Readonly<{\n start: () => void\n succeed: () => void\n}>\n\ntype TruncatePublicationState = {\n loadedInitialState: boolean\n snapshotSent: boolean\n limitedSnapshotRowCount: number\n lastSentKey: string | number | undefined\n}\n\ntype SubsetAcquisition = {\n options: LoadSubsetOptions\n loadSubsetSession: number\n abortController?: AbortController\n removeRequestAbortListener?: () => void\n releaseAttempted?: true\n}\n\ntype SubsetDemand = {\n requestOptions: LoadSubsetOptions\n acquisition: SubsetAcquisition\n acquisitionState: `starting` | `active` | `detached`\n initialResult?: Deferred<void>\n}\n\ntype TruncateReplayAttempt = {\n pendingCount: number\n setupComplete: boolean\n}\n\ntype TruncateReplaySession = {\n loadSubsetSession: number\n publicationState: TruncatePublicationState\n /** Direct subscribers buffer the replacement here; delegated publication has no buffer. */\n privateRows: Map<string | number, object> | undefined\n pending: Set<{ demand: SubsetDemand; attempt: TruncateReplayAttempt }>\n pendingSetups: number\n currentAttempt: TruncateReplayAttempt\n failures: Map<SubsetDemand, Error>\n completion: Deferred<void>\n}\n\nfunction createReplayCompletion(): Deferred<void> {\n const completion = createDeferred<void>()\n void completion.promise.catch(() => {})\n return completion\n}\n\nfunction cancelAcquisition(acquisition: SubsetAcquisition): void {\n acquisition.abortController?.abort()\n acquisition.removeRequestAbortListener?.()\n}\n\nexport class CollectionSubscription\n extends EventEmitter<SubscriptionEvents>\n implements Subscription\n{\n private loadedInitialState = false\n\n // Flag to skip filtering in filterAndFlipChanges.\n // This is separate from loadedInitialState because we want to allow\n // requestSnapshot to still work even when filtering is skipped.\n private skipFiltering = false\n\n // Flag to indicate that we have sent at least 1 snapshot.\n // While `snapshotSent` is false we filter out all changes from subscription to the collection.\n private snapshotSent = false\n\n /**\n * Track all loadSubset calls made by this subscription so we can unload them on cleanup.\n * We store the exact LoadSubsetOptions we passed to loadSubset to ensure symmetric unload.\n */\n private subsetDemands: Array<SubsetDemand> = []\n private primaryFailureDeliveryDepth = 0\n private readonly requestedSubsetWhere = new WeakMap<\n LoadSubsetOptions,\n BasicExpression<boolean>\n >()\n\n // Keep track of the keys we've sent (needed for join and orderBy optimizations)\n private sentKeys = new Set<string | number>()\n private publishedRows = new Map<string | number, object>()\n private stalePublishedRows = new Map<string | number, object>()\n\n // Track the count of rows sent via requestLimitedSnapshot for offset-based pagination\n private limitedSnapshotRowCount = 0\n\n // Track the last key sent via requestLimitedSnapshot for cursor-based pagination\n private lastSentKey: string | number | undefined\n\n private filteredCallback: (changes: Array<ChangeMessage<any, any>>) => boolean\n\n private orderByIndex: IndexReader<string | number> | undefined\n\n // Status tracking\n private _status: SubscriptionStatus = `ready`\n private statusRevision = 0\n private _lastError: unknown | undefined\n private pendingLoadSubsetParticipants = new Set<{\n demand: SubsetDemand\n promise: Promise<unknown>\n }>()\n\n // Cleanup function for truncate event listener\n private truncateCleanup: (() => void) | undefined\n private collectionCleanup: (() => void) | undefined\n private collectionRestartCleanup: (() => void) | undefined\n\n // One replay session owns the publication baseline, overlapping attempts,\n // and buffered changes until every attempt settles.\n private truncateReplaySession: TruncateReplaySession | undefined\n private readonly loadSubsetPromiseErrors = new WeakMap<\n Promise<unknown>,\n Error\n >()\n private truncateReplacementPending = false\n private unsubscribed = false\n\n public get status(): SubscriptionStatus {\n return this._status\n }\n\n public get lastError(): unknown | undefined {\n return this._lastError\n }\n\n constructor(\n private collection: CollectionImpl<any, any, any, any, any>,\n private callback: (changes: Array<ChangeMessage<any, any>>) => void,\n private options: CollectionSubscriptionOptions,\n ) {\n super()\n if (options.onUnsubscribe) {\n this.on(`unsubscribed`, options.onUnsubscribe)\n }\n if (options.onLoadSubsetError) {\n this.on(`loadSubset:error`, options.onLoadSubsetError)\n }\n\n // Auto-index for where expressions if enabled\n if (options.whereExpression) {\n ensureIndexForExpression(options.whereExpression, this.collection)\n }\n\n const callbackWithSentKeysTracking = (\n changes: Array<ChangeMessage<any, any>>,\n ) => {\n this.trackPublishedRows(changes)\n this.trackSentKeys(changes)\n callback(changes)\n }\n\n this.callback = callbackWithSentKeysTracking\n\n // Create a filtered callback if where clause is provided\n this.filteredCallback = options.whereExpression\n ? createFilteredCallback(this.callback, options)\n : (changes) => {\n this.callback(changes)\n return true\n }\n\n // Listen for truncate events to re-request data after must-refetch\n // When a truncate happens (e.g., from a 409 must-refetch), all collection data is cleared.\n // We need to re-request all previously loaded subsets to repopulate the data.\n this.truncateCleanup = this.collection.on(`truncate`, () => {\n this.handleTruncate()\n })\n this.collectionCleanup = this.collection.on(`status:cleaned-up`, () => {\n this.handleCollectionCleanup()\n })\n this.collectionRestartCleanup = this.collection.on(\n `status:change`,\n ({ status }) => {\n if (status !== `loading` && status !== `ready`) return\n const loadSubsetSession = this.collection._sync.getLoadSubsetSession()\n const replaySession = this.truncateReplaySession\n if (\n this.subsetDemands.some(\n (demand) => demand.acquisitionState === `detached`,\n )\n ) {\n this.setStatus(`loadingSubset`)\n }\n queueMicrotask(() => {\n if (this.truncateReplaySession === replaySession) {\n this.restartDetachedDemands(loadSubsetSession)\n }\n })\n },\n )\n }\n\n /** Detach logical demand from work owned by a discarded sync session. */\n private handleCollectionCleanup(): void {\n this.discardTruncateReplay()\n this.stalePublishedRows = new Map(this.publishedRows)\n this.pendingLoadSubsetParticipants.clear()\n\n for (const demand of [...this.subsetDemands]) {\n demand.initialResult?.reject(new LoadSubsetOperationAbortedError())\n cancelAcquisition(demand.acquisition)\n if (demand.acquisitionState === `starting`) {\n const index = this.subsetDemands.indexOf(demand)\n if (index !== -1) this.subsetDemands.splice(index, 1)\n } else {\n demand.acquisitionState = `detached`\n demand.acquisition = {\n options: demand.requestOptions,\n loadSubsetSession: demand.acquisition.loadSubsetSession,\n }\n }\n }\n this.setReadyIfIdle()\n }\n\n /** Acquire detached demand after startup or initial-error recovery. */\n private restartDetachedDemands(loadSubsetSession: number): void {\n if (\n this.unsubscribed ||\n !this.isLoadSubsetSessionCurrent(loadSubsetSession)\n ) {\n return\n }\n if (\n this.collection.status === `error` ||\n this.collection._sync.syncLoadSubsetFn === null\n ) {\n this.setReadyIfIdle()\n return\n }\n const demands = this.subsetDemands.filter(\n (demand) =>\n demand.acquisitionState === `detached` &&\n !demand.requestOptions.signal?.aborted,\n )\n if (demands.length === 0) {\n this.setReadyIfIdle()\n return\n }\n\n const session = this.createTruncateReplaySession(loadSubsetSession, () => {\n const currentRows = this.collection.currentStateAsChanges({\n optimizedOnly: false,\n })\n return new Map(\n // The API returns void for unavailable snapshots, not just undefined.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n (currentRows ?? [])\n .filter((change) => change.type !== `delete`)\n .map((change) => [change.key, change.value]),\n )\n })\n const attempt = session.currentAttempt\n this.truncateReplaySession = session\n this.setStatus(`loadingSubset`)\n if (this.truncateReplaySession !== session) return\n this.startTruncateReplayAttempt(session, attempt, demands)\n }\n\n /**\n * Handle collection truncate event by resetting state and re-requesting subsets.\n * This is called when the sync layer receives a must-refetch and clears all data.\n *\n * To prevent a flash of missing content, we buffer all changes (deletes from truncate\n * and inserts from refetch) until all loadSubset calls succeed, then emit them together.\n * A failed replay keeps the last published snapshot private until a later\n * authoritative replay succeeds.\n */\n private handleTruncate() {\n // Without a loader, replay only reconciles rows retained across cleanup.\n const hasLoadSubsetHandler = this.collection._sync.syncLoadSubsetFn !== null\n const demandsToReload = hasLoadSubsetHandler ? [...this.subsetDemands] : []\n\n // Retained rows still need the committed replacement even without demand.\n if (demandsToReload.length === 0 && this.stalePublishedRows.size === 0) {\n this.resetSnapshotTracking()\n return\n }\n\n let session = this.truncateReplaySession\n if (session) {\n if (!session.completion.isPending()) {\n session.completion = createReplayCompletion()\n }\n // Setup itself holds publication: adapter/status callbacks may reenter\n // before a request returns its promise and joins the pending set.\n session.pendingSetups++\n session.failures.clear()\n session.currentAttempt = { pendingCount: 0, setupComplete: false }\n } else {\n // Every overlapping attempt shares one publication baseline and buffer.\n session = this.createTruncateReplaySession(\n this.collection._sync.getLoadSubsetSession(),\n () => new Map(this.publishedRows),\n )\n this.truncateReplaySession = session\n }\n const attempt = session.currentAttempt\n this.setStatus(`loadingSubset`)\n\n if (this.truncateReplaySession !== session) return\n\n if (this.options.truncateReplayPublication) {\n this.truncateReplacementPending = true\n this.options.truncateReplayPublication.start()\n }\n\n // A newer replay replaces every prior acquisition for these demands. Abort\n // the old work before it can install rows into the new generation.\n for (const demand of demandsToReload) {\n demand.acquisition.abortController?.abort()\n }\n\n // Reset snapshot/pagination tracking for the replacement snapshot. Rows\n // retained from an earlier failed replay stay marked until this attempt\n // either replaces them or proves they are absent.\n this.resetSnapshotTracking()\n\n // Defer the requests so the truncate commit's deletes enter the session\n // buffer before a synchronous adapter can publish replacement rows.\n queueMicrotask(() => {\n if (this.truncateReplaySession !== session) return\n if (!this.isLoadSubsetSessionCurrent(session.loadSubsetSession)) {\n this.retireStaleTruncateReplay(session)\n return\n }\n // A newer truncate that arrived before this attempt began source work\n // already captured the active demands. Starting them now would place the\n // obsolete acquisition outside the newer abort sweep.\n this.startTruncateReplayAttempt(\n session,\n attempt,\n session.currentAttempt === attempt ? demandsToReload : [],\n )\n })\n }\n\n /** Make tentative replay ownership visible before adapter code can reenter. */\n private startTruncateReplayDemand(\n session: TruncateReplaySession,\n attempt: TruncateReplayAttempt,\n demand: SubsetDemand,\n ): void {\n const isCurrentAttempt = () =>\n this.truncateReplaySession === session &&\n session.currentAttempt === attempt\n const isCurrent = () =>\n isCurrentAttempt() &&\n this.isLoadSubsetSessionCurrent(session.loadSubsetSession) &&\n this.isDemandActive(demand)\n const fail = (error: unknown) => {\n if (isCurrent()) session.failures.set(demand, normalizeError(error))\n }\n if (demand.initialResult) {\n void session.completion.promise.then(\n demand.initialResult.resolve,\n demand.initialResult.reject,\n )\n }\n\n // Sequential handoff: retire the old physical lease while retaining its\n // logical demand. Callback reentry cannot release that lease twice.\n const previous = demand.acquisition\n const hadPreviousAcquisition = demand.acquisitionState === `active`\n demand.acquisitionState = `detached`\n if (hadPreviousAcquisition) {\n try {\n this.releaseAcquisition(previous)\n } catch (error) {\n fail(error)\n return\n }\n }\n if (!isCurrent() || demand.requestOptions.signal?.aborted) return\n\n const next = this.createSubsetAcquisition(demand)\n demand.acquisition = next\n demand.acquisitionState = `starting`\n let result: LoadSubsetRequestResult\n try {\n result = this.loadSubset(next.options, isCurrent)\n } catch (error) {\n if (demand.acquisition === next) demand.acquisitionState = `detached`\n cancelAcquisition(next)\n fail(error)\n return\n }\n\n if (!isCurrent()) {\n if (demand.acquisition === next) demand.acquisitionState = `detached`\n try {\n this.releaseAcquisition(next)\n } catch (error) {\n fail(error)\n }\n return\n }\n\n demand.acquisitionState = `active`\n this.trackTruncateReplayParticipant(session, attempt, demand, result)\n this.observeLoadSubsetResult(\n result,\n demand,\n next.options,\n true,\n () => isCurrent() && !next.options.signal?.aborted,\n )\n }\n\n private settleTruncateReplay(\n session: TruncateReplaySession,\n pending: { demand: SubsetDemand; attempt: TruncateReplayAttempt },\n ): void {\n try {\n if (this.truncateReplaySession !== session) return\n if (!this.isLoadSubsetSessionCurrent(session.loadSubsetSession)) {\n this.retireStaleTruncateReplay(session)\n return\n }\n if (session.pending.delete(pending)) pending.attempt.pendingCount--\n this.checkTruncateReplayComplete(session)\n } catch (error) {\n // Replay settlement runs from a Promise callback, so throwing here would\n // create an unobserved derived rejection. Surface subscriber errors like\n // other async collection events instead.\n queueMicrotask(() => {\n throw error\n })\n }\n }\n\n /** Keep every acquisition begun during recovery inside its publication barrier. */\n private trackTruncateReplayParticipant(\n session: TruncateReplaySession,\n attempt: TruncateReplayAttempt,\n demand: SubsetDemand,\n result: LoadSubsetRequestResult,\n ): void {\n if (\n this.truncateReplaySession !== session ||\n (session.currentAttempt !== attempt &&\n attempt.setupComplete &&\n attempt.pendingCount === 0) ||\n !(result instanceof Promise)\n ) {\n return\n }\n\n // An older attempt can still accept returning startup work while setup or\n // another participant retains it. Once drained, it cannot reopen. Shared\n // promises still get one participant per logical acquisition.\n const pending = { demand, attempt }\n attempt.pendingCount++\n session.pending.add(pending)\n void result.then(\n () => this.settleTruncateReplay(session, pending),\n (error: unknown) => {\n // A released demand no longer participates in this replacement. Its\n // cooperative AbortError must not discard rows from active demands.\n if (\n this.truncateReplaySession === session &&\n session.currentAttempt === attempt &&\n this.isLoadSubsetSessionCurrent(session.loadSubsetSession) &&\n this.subsetDemands.includes(demand)\n ) {\n const normalized = this.normalizeLoadSubsetPromiseError(result, error)\n session.failures.set(demand, normalized)\n }\n this.settleTruncateReplay(session, pending)\n },\n )\n }\n\n /** Stop obsolete logical demand from pinning a replay barrier. */\n private removeTruncateReplayParticipant(demand: SubsetDemand): void {\n const session = this.truncateReplaySession\n if (!session) return\n session.failures.delete(demand)\n for (const pending of session.pending) {\n if (pending.demand === demand) {\n session.pending.delete(pending)\n pending.attempt.pendingCount--\n }\n }\n }\n\n /** Publish only after every overlapping replay attempt has settled. */\n private checkTruncateReplayComplete(session: TruncateReplaySession): void {\n if (this.truncateReplaySession !== session) return\n if (session.pendingSetups > 0 || session.pending.size > 0) return\n\n const activeFailure = [...session.failures].find(([demand]) =>\n this.subsetDemands.includes(demand),\n )\n try {\n if (activeFailure) {\n this.abandonTruncateReplay(session, activeFailure[1])\n } else {\n this.flushTruncateReplay(session)\n }\n } finally {\n this.setReadyIfIdle()\n }\n }\n\n /**\n * Keep an incomplete replay private. The source no longer proves a complete\n * state, so only a later successful truncate replay may reopen publication.\n */\n private abandonTruncateReplay(\n session: TruncateReplaySession,\n failure: Error,\n ): void {\n if (this.truncateReplaySession !== session) return\n session.completion.reject(failure)\n // Delegated publication already delivered its rows. Only a private buffer\n // returns the caller's pagination position to the public snapshot; the\n // private rows and their sent-key tracking stay together for a retry.\n if (!session.privateRows) return\n const publicationState = session.publicationState\n this.loadedInitialState = publicationState.loadedInitialState\n this.snapshotSent = publicationState.snapshotSent\n this.limitedSnapshotRowCount = publicationState.limitedSnapshotRowCount\n this.lastSentKey = publicationState.lastSentKey\n }\n\n /** Publish the buffered replacement as one batch, or release the delegate. */\n private flushTruncateReplay(session: TruncateReplaySession): void {\n if (this.truncateReplaySession !== session) return\n this.truncateReplaySession = undefined\n this.truncateReplacementPending = false\n\n // Retained rows the source never re-delivered leave the replacement.\n const { privateRows } = session\n for (const key of this.stalePublishedRows.keys()) privateRows?.delete(key)\n this.stalePublishedRows.clear()\n try {\n if (privateRows) {\n // Diff the retained public snapshot against the applied source replacement.\n const replacement = this.createStateDiff(\n this.publishedRows,\n privateRows,\n )\n if (replacement.length > 0) this.filteredCallback(replacement)\n }\n } finally {\n // Restore tracking even when a subscriber rejects the replacement.\n this.restorePublishedSnapshotTracking()\n session.completion.resolve()\n this.options.truncateReplayPublication?.succeed()\n }\n }\n\n private restorePublishedSnapshotTracking(): void {\n this.sentKeys = new Set(this.publishedRows.keys())\n if (!this.orderByIndex) return\n\n this.limitedSnapshotRowCount = this.sentKeys.size\n const orderedSentKeys = this.orderByIndex.takeFromStart(\n this.sentKeys.size,\n (key) => this.sentKeys.has(key),\n )\n this.lastSentKey = orderedSentKeys.at(-1)\n }\n\n /** Fold changes into the private replacement; false when they publish now. */\n private bufferPrivately(\n changes: ReadonlyArray<ChangeMessage<any, any>>,\n ): boolean {\n const privateRows = this.truncateReplaySession?.privateRows\n if (!privateRows) return false\n for (const change of changes) {\n if (change.type === `delete`) privateRows.delete(change.key)\n else privateRows.set(change.key, change.value)\n }\n return true\n }\n\n private createStateDiff(\n baseline: ReadonlyMap<string | number, object>,\n finalRows: ReadonlyMap<string | number, object>,\n ): Array<ChangeMessage<any, any>> {\n const replacement: Array<ChangeMessage<any, any>> = []\n for (const [key, previousValue] of baseline) {\n const value = finalRows.get(key)\n if (value === undefined) {\n replacement.push({\n type: `delete`,\n key,\n value: previousValue,\n })\n } else if (!deepEquals(value, previousValue)) {\n replacement.push({\n type: `update`,\n key,\n value,\n previousValue,\n })\n }\n }\n for (const [key, value] of finalRows) {\n if (!baseline.has(key)) replacement.push({ type: `insert`, key, value })\n }\n return replacement\n }\n\n private get isBufferingForTruncate(): boolean {\n return this.truncateReplaySession !== undefined\n }\n\n private setReadyIfIdle(): void {\n const session = this.truncateReplaySession\n const hasPendingReplayWork =\n session && (session.pendingSetups > 0 || session.pending.size > 0)\n if (\n this.pendingLoadSubsetParticipants.size === 0 &&\n !hasPendingReplayWork\n ) {\n this.setStatus(`ready`)\n }\n }\n\n private isLoadSubsetSessionCurrent(session: number): boolean {\n return session === this.collection._sync.getLoadSubsetSession()\n }\n\n private retireStaleTruncateReplay(session: TruncateReplaySession): void {\n if (this.truncateReplaySession !== session) return\n this.discardTruncateReplay()\n this.stalePublishedRows.clear()\n }\n\n /** Drop the replay without publishing; an unfinished wait rejects as aborted. */\n private discardTruncateReplay(): void {\n const session = this.truncateReplaySession\n if (session?.completion.isPending()) {\n session.completion.reject(new LoadSubsetOperationAbortedError())\n }\n this.truncateReplaySession = undefined\n this.truncateReplacementPending = false\n }\n\n private resetSnapshotTracking(): void {\n this.snapshotSent = false\n this.loadedInitialState = false\n this.limitedSnapshotRowCount = 0\n this.lastSentKey = undefined\n }\n\n /** One replay session; only direct subscribers buffer a private replacement. */\n private createTruncateReplaySession(\n loadSubsetSession: number,\n privateRows: () => Map<string | number, object>,\n ): TruncateReplaySession {\n return {\n loadSubsetSession,\n publicationState: {\n loadedInitialState: this.loadedInitialState,\n snapshotSent: this.snapshotSent,\n limitedSnapshotRowCount: this.limitedSnapshotRowCount,\n lastSentKey: this.lastSentKey,\n },\n privateRows: this.options.truncateReplayPublication\n ? undefined\n : privateRows(),\n pending: new Set(),\n // Setup itself holds publication: adapter/status callbacks may reenter\n // before a request returns its promise and joins the pending set.\n pendingSetups: 1,\n currentAttempt: { pendingCount: 0, setupComplete: false },\n failures: new Map(),\n completion: createReplayCompletion(),\n }\n }\n\n /** Start one attempt's demands, then release the setup hold on publication. */\n private startTruncateReplayAttempt(\n session: TruncateReplaySession,\n attempt: TruncateReplayAttempt,\n demands: ReadonlyArray<SubsetDemand>,\n ): void {\n for (const demand of demands) {\n if (!this.subsetDemands.includes(demand)) continue\n this.startTruncateReplayDemand(session, attempt, demand)\n if (\n this.truncateReplaySession !== session ||\n session.currentAttempt !== attempt\n ) {\n break\n }\n }\n attempt.setupComplete = true\n session.pendingSetups--\n this.checkTruncateReplayComplete(session)\n }\n\n public get hasPendingTruncateReplacement(): boolean {\n return this.truncateReplacementPending\n }\n\n public get pendingTruncateReplacement(): Promise<void> | undefined {\n const completion = this.truncateReplaySession?.completion\n return completion?.isPending() ? completion.promise : undefined\n }\n\n public get hasFailedTruncateReplacement(): boolean {\n const completion = this.truncateReplaySession?.completion\n return (\n this.truncateReplacementPending &&\n completion !== undefined &&\n !completion.isPending()\n )\n }\n\n setOrderByIndex(index: IndexReader<any>) {\n this.orderByIndex = index\n }\n\n /**\n * Set subscription status and emit events if changed\n */\n private setStatus(newStatus: SubscriptionStatus) {\n if (this.unsubscribed) return\n if (this._status === newStatus) {\n return // No change\n }\n\n const previousStatus = this._status\n this._status = newStatus\n const revision = ++this.statusRevision\n\n // Emit status:change event\n this.emitInnerWhile(\n `status:change`,\n {\n type: `status:change`,\n subscription: this,\n previousStatus,\n status: newStatus,\n },\n () => this.statusRevision === revision,\n )\n\n // A listener may synchronously start or release demand. Do not follow that\n // newer transition with a stale specific event.\n if (this.statusRevision !== revision) return\n\n // Emit specific status event\n const eventKey: `status:${SubscriptionStatus}` = `status:${newStatus}`\n this.emitInnerWhile(\n eventKey,\n {\n type: eventKey,\n subscription: this,\n previousStatus,\n status: newStatus,\n } as SubscriptionEvents[typeof eventKey],\n () => this.statusRevision === revision,\n )\n }\n\n /** Observe an asynchronous subset load and restore status on settlement. */\n private observeLoadSubsetResult(\n syncResult: LoadSubsetRequestResult,\n demand: SubsetDemand,\n options: LoadSubsetOptions,\n trackStatus: boolean,\n shouldReportError: () => boolean = () => true,\n ): void {\n if (!(syncResult instanceof Promise)) return\n\n const loadSubsetSession = this.collection._sync.getLoadSubsetSession()\n const participant = { demand, promise: syncResult }\n\n if (trackStatus) {\n this.pendingLoadSubsetParticipants.add(participant)\n this.setStatus(`loadingSubset`)\n }\n\n const finish = () => {\n if (trackStatus) {\n this.pendingLoadSubsetParticipants.delete(participant)\n if (this.isLoadSubsetSessionCurrent(loadSubsetSession)) {\n this.setReadyIfIdle()\n }\n }\n }\n\n void syncResult.then(finish, (error: unknown) => {\n if (\n this.isLoadSubsetSessionCurrent(loadSubsetSession) &&\n shouldReportError()\n ) {\n this.recordLoadSubsetError(\n options,\n this.normalizeLoadSubsetPromiseError(syncResult, error),\n )\n }\n finish()\n })\n }\n\n /** Give every logical observer of one transport rejection the same Error. */\n private normalizeLoadSubsetPromiseError(\n promise: Promise<unknown>,\n error: unknown,\n ): Error {\n const existing = this.loadSubsetPromiseErrors.get(promise)\n if (existing) return existing\n const normalized = normalizeError(error)\n this.loadSubsetPromiseErrors.set(promise, normalized)\n return normalized\n }\n\n private stopDemandStatusParticipants(demand: SubsetDemand): void {\n for (const participant of this.pendingLoadSubsetParticipants) {\n if (participant.demand === demand) {\n this.pendingLoadSubsetParticipants.delete(participant)\n }\n }\n this.setReadyIfIdle()\n }\n\n private loadSubset(\n options: LoadSubsetOptions,\n shouldReportError: () => boolean = () => true,\n ): LoadSubsetRequestResult {\n try {\n return this.collection._sync.loadSubset(options)\n } catch (error) {\n const normalized = normalizeError(error)\n if (shouldReportError()) this.recordLoadSubsetError(options, normalized)\n throw normalized\n }\n }\n\n /** Create a fresh, abortable adapter acquisition for a replay generation. */\n private createSubsetAcquisition(\n demand: SubsetDemand,\n ): SubsetAcquisition & { abortController: AbortController } {\n const abortController = new AbortController()\n const requestSignal = demand.requestOptions.signal\n let removeRequestAbortListener: (() => void) | undefined\n\n if (requestSignal?.aborted) {\n abortController.abort(requestSignal.reason)\n } else if (requestSignal) {\n const abort = () => abortController.abort(requestSignal.reason)\n requestSignal.addEventListener(`abort`, abort, { once: true })\n removeRequestAbortListener = () =>\n requestSignal.removeEventListener(`abort`, abort)\n }\n\n return {\n options: {\n ...demand.requestOptions,\n signal: abortController.signal,\n },\n loadSubsetSession: this.collection._sync.getLoadSubsetSession(),\n abortController,\n removeRequestAbortListener,\n }\n }\n\n /** Retire an acquisition before user code; failed cleanup is not retryable. */\n private releaseAcquisition(\n acquisition: SubsetAcquisition,\n reportReleaseError = this.primaryFailureDeliveryDepth === 0,\n ): void {\n if (acquisition.releaseAttempted) return\n acquisition.releaseAttempted = true\n try {\n acquisition.abortController?.abort()\n if (this.isLoadSubsetSessionCurrent(acquisition.loadSubsetSession)) {\n this.collection._sync.unloadSubset(acquisition.options)\n }\n } catch (error) {\n const normalized = reportReleaseError\n ? this.recordLoadSubsetError(\n acquisition.options,\n normalizeError(error),\n true,\n )\n : normalizeError(error)\n throw normalized\n } finally {\n acquisition.removeRequestAbortListener?.()\n }\n }\n\n /** Start and retain the first acquisition for one logical subset demand. */\n private startSubsetDemand(requestOptions: LoadSubsetOptions): {\n demand: SubsetDemand\n result: LoadSubsetRequestResult\n started: boolean\n } {\n const demand: SubsetDemand = {\n requestOptions,\n acquisition: {\n options: requestOptions,\n loadSubsetSession: this.collection._sync.getLoadSubsetSession(),\n },\n acquisitionState: `starting`,\n }\n if (\n this.collection.status === `cleaned-up` ||\n // Ready/error callbacks can run before sync returns its loader. Idle\n // deferred starts still acquire through the sync manager's queue.\n (this.collection.config.syncMode === `on-demand` &&\n (this.collection.status === `error` ||\n (this.collection.status !== `idle` &&\n this.collection._sync.syncLoadSubsetFn === null)))\n ) {\n demand.acquisitionState = `detached`\n this.subsetDemands.push(demand)\n const initialResult = createDeferred<void>()\n demand.initialResult = initialResult\n const abort = () =>\n initialResult.reject(new LoadSubsetOperationAbortedError())\n requestOptions.signal?.addEventListener(`abort`, abort, { once: true })\n const finish = () => {\n requestOptions.signal?.removeEventListener(`abort`, abort)\n demand.initialResult = undefined\n }\n void initialResult.promise.then(finish, finish)\n return { demand, result: initialResult.promise, started: false }\n }\n const acquisition = this.createSubsetAcquisition(demand)\n demand.acquisition = acquisition\n const replaySession = this.truncateReplaySession\n const replayAttempt = replaySession?.currentAttempt\n const loadSubsetSession = this.collection._sync.getLoadSubsetSession()\n // Reentrant release must see the exact acquisition before adapter work\n // starts. A genuine load throw removes this tentative logical owner below.\n this.subsetDemands.push(demand)\n let result: LoadSubsetRequestResult\n try {\n result = this.loadSubset(\n acquisition.options,\n () =>\n this.isLoadSubsetSessionCurrent(loadSubsetSession) &&\n this.subsetDemands.includes(demand) &&\n (replaySession === undefined ||\n (this.truncateReplaySession === replaySession &&\n replaySession.currentAttempt === replayAttempt)),\n )\n } catch (error) {\n const demandIndex = this.subsetDemands.indexOf(demand)\n if (demandIndex !== -1) {\n if (\n replaySession &&\n replayAttempt &&\n this.truncateReplaySession === replaySession &&\n replaySession.currentAttempt === replayAttempt\n ) {\n replaySession.failures.set(demand, normalizeError(error))\n }\n this.subsetDemands.splice(demandIndex, 1)\n }\n cancelAcquisition(acquisition)\n throw error\n }\n\n if (!this.isLoadSubsetSessionCurrent(loadSubsetSession)) {\n const demandIndex = this.subsetDemands.indexOf(demand)\n if (demandIndex !== -1) this.subsetDemands.splice(demandIndex, 1)\n cancelAcquisition(acquisition)\n return { demand, result, started: true }\n }\n\n demand.acquisitionState = `active`\n if (!this.subsetDemands.includes(demand)) {\n this.releaseAcquisition(acquisition)\n return { demand, result, started: true }\n }\n\n if (replaySession && replayAttempt) {\n this.trackTruncateReplayParticipant(\n replaySession,\n replayAttempt,\n demand,\n result,\n )\n }\n return { demand, result, started: true }\n }\n\n /** Re-check ownership after adapter and event callbacks that may reenter. */\n private isDemandActive(demand: SubsetDemand): boolean {\n return !this.unsubscribed && this.subsetDemands.includes(demand)\n }\n\n private recordLoadSubsetError(\n options: LoadSubsetOptions,\n error: unknown,\n reportAborted = false,\n ): Error {\n const normalized = normalizeError(error)\n // Aborted subset requests are obsolete demand, not load failures. The\n // request may reject after its route has already been released.\n if (options.signal?.aborted && !reportAborted) return normalized\n\n this._lastError = normalized\n this.primaryFailureDeliveryDepth++\n try {\n this.emitInner(`loadSubset:error`, {\n type: `loadSubset:error`,\n subscription: this,\n options,\n error: normalized,\n })\n } finally {\n this.primaryFailureDeliveryDepth--\n }\n return normalized\n }\n\n emitEvents(changes: Array<ChangeMessage<any, any>>): boolean {\n if (this.unsubscribed) return false\n const newChanges = this.filterAndFlipChanges(changes)\n\n // Reconciliation can reduce a source delta to no visible change. Do not\n // wake subscribers for an empty semantic batch.\n if (changes.length > 0 && newChanges.length === 0) return false\n\n // A direct subscriber sees the replacement as one batch, not a flash of\n // missing content. Delegated publication keeps its private D2 contributions.\n if (this.bufferPrivately(newChanges)) return false\n return this.filteredCallback(newChanges)\n }\n\n /** Keep direct snapshot reads private while an authoritative replay is open. */\n private publishSnapshot(changes: Array<ChangeMessage<any, any>>): void {\n if (!this.bufferPrivately(changes)) this.callback(changes)\n }\n\n /**\n * Sends the snapshot to the callback.\n * Returns a boolean indicating if it succeeded.\n * It can only fail if there is no index to fulfill the request\n * and the optimizedOnly option is set to true,\n * or, the entire state was already loaded or the request was cancelled.\n */\n requestSnapshot(opts?: RequestSnapshotOptions): boolean {\n // Cancel before acquiring ownership or publishing a local snapshot.\n if (this.unsubscribed || opts?.signal?.aborted) return false\n if (this.loadedInitialState) {\n // Subscription was deoptimized so we already sent the entire initial state\n return false\n }\n\n const stateOpts: RequestSnapshotOptions = {\n where: this.options.whereExpression,\n optimizedOnly: opts?.optimizedOnly ?? false,\n }\n\n if (opts) {\n if (`where` in opts) {\n const snapshotWhereExp = opts.where\n if (stateOpts.where) {\n // Combine the two where expressions\n const subWhereExp = stateOpts.where\n const combinedWhereExp = and(subWhereExp, snapshotWhereExp)\n stateOpts.where = combinedWhereExp\n } else {\n stateOpts.where = snapshotWhereExp\n }\n }\n } else {\n // No options provided so it's loading the entire initial state\n this.loadedInitialState = true\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n const loadOptions: LoadSubsetOptions = {\n where: stateOpts.where,\n signal: opts?.signal,\n subscription: this,\n // Include orderBy and limit if provided so sync layer can optimize the query\n orderBy: opts?.orderBy,\n limit: opts?.limit,\n }\n\n const {\n demand,\n result: syncResult,\n started,\n } = this.startSubsetDemand(loadOptions)\n if (!this.isDemandActive(demand)) return false\n if (opts?.where) this.requestedSubsetWhere.set(loadOptions, opts.where)\n\n // Report the result synchronously, including a wait for an unavailable loader.\n opts?.onLoadSubsetResult?.(\n syncResult,\n demand.acquisition.options,\n (primaryFailure) => this.releaseDemand(demand, primaryFailure),\n )\n if (!this.isDemandActive(demand)) return false\n\n if (started) {\n this.observeLoadSubsetResult(\n syncResult,\n demand,\n demand.acquisition.options,\n opts?.trackLoadSubsetPromise ?? true,\n )\n }\n if (!this.isDemandActive(demand)) return false\n\n // Also load data immediately from the collection\n let snapshot: Array<ChangeMessage<any, any>> | void\n if (opts?.onUnoptimized) {\n snapshot = this.collection.currentStateAsChanges({\n ...stateOpts,\n optimizedOnly: true,\n })\n if (snapshot === undefined) {\n opts.onUnoptimized()\n // The callback can unsubscribe; TypeScript retains the pre-call narrowing.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.unsubscribed) return false\n snapshot = this.collection.currentStateAsChanges({\n ...stateOpts,\n optimizedOnly: false,\n })\n }\n } else {\n snapshot = this.collection.currentStateAsChanges(stateOpts)\n }\n // Snapshot evaluation may call user code that tears down the subscription.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.unsubscribed) return false\n\n if (snapshot === undefined) {\n // Couldn't load from indexes\n return false\n }\n\n // Skip known rows, except retained rows from an abandoned replay: a new\n // snapshot must reconcile those with the source, not suppress their update.\n const knownRows =\n this.truncateReplaySession?.privateRows ?? this.publishedRows\n const filteredSnapshot = snapshot.filter(\n (change) =>\n (!this.isBufferingForTruncate &&\n this.stalePublishedRows.has(change.key)) ||\n (!this.sentKeys.has(change.key) && !knownRows.has(change.key)),\n )\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of filteredSnapshot) {\n this.sentKeys.add(change.key)\n }\n\n this.snapshotSent = true\n this.publishSnapshot(\n this.isBufferingForTruncate\n ? filteredSnapshot\n : this.reconcileStalePublishedChanges(filteredSnapshot),\n )\n return true\n }\n\n /** Release one exact subset request while keeping the subscription alive. */\n releaseSnapshot(where: BasicExpression<boolean>): void {\n const index = this.subsetDemands.findIndex(\n (demand) =>\n demand.requestOptions.where === where ||\n this.requestedSubsetWhere.get(demand.requestOptions) === where,\n )\n if (index === -1) return\n\n this.releaseDemandAt(index)\n }\n\n private releaseDemand(\n demand: SubsetDemand,\n primaryFailure?: { error: unknown },\n ): void {\n if (!primaryFailure) {\n const index = this.subsetDemands.indexOf(demand)\n if (index !== -1) this.releaseDemandAt(index)\n return\n }\n\n try {\n this.recordLoadSubsetError(\n demand.acquisition.options,\n primaryFailure.error,\n true,\n )\n } finally {\n // The failed request remains the public error, even if cleanup also fails.\n const index = this.subsetDemands.indexOf(demand)\n if (index !== -1) this.releaseDemandAt(index, false)\n }\n }\n\n private releaseDemandAt(\n index: number,\n reportReleaseError = this.primaryFailureDeliveryDepth === 0,\n ): void {\n const demand = this.subsetDemands[index]\n if (!demand) return\n const replaySession = this.truncateReplaySession\n const acquisition = demand.acquisition\n this.subsetDemands.splice(index, 1)\n demand.initialResult?.reject(new LoadSubsetOperationAbortedError())\n const releaseCallbacks = [\n () => this.removeTruncateReplayParticipant(demand),\n ...(demand.acquisitionState === `active`\n ? [\n // Adapter release is a supported reentrancy boundary. A demand\n // started from unload joins this replacement before completion.\n () => this.releaseAcquisition(acquisition, reportReleaseError),\n ]\n : []),\n () => this.retireEmptyReplay(),\n () => {\n if (replaySession) this.checkTruncateReplayComplete(replaySession)\n },\n // Ready follows replacement publication, never the delete half of it.\n () => this.stopDemandStatusParticipants(demand),\n ]\n runAllCallbacks(releaseCallbacks)\n }\n\n /** A replay with no remaining logical demand cannot establish more rows. */\n private retireEmptyReplay(): void {\n if (this.subsetDemands.length !== 0 || !this.truncateReplaySession) {\n return\n }\n this.discardTruncateReplay()\n this.stalePublishedRows = new Map(this.publishedRows)\n this.restorePublishedSnapshotTracking()\n this.options.truncateReplayPublication?.succeed()\n }\n\n /** Read the applied rows in an ordered acquisition without starting demand. */\n readOrderedSnapshot(\n options: LoadSubsetOptions,\n ): Array<ChangeMessage<Record<string, unknown>, string | number>> {\n const predicates = [\n this.options.whereExpression,\n options.where,\n options.cursor?.whereFrom,\n ].filter((where) => where !== undefined)\n const snapshot = this.collection.currentStateAsChanges({\n orderBy: options.orderBy,\n limit: options.limit,\n where:\n predicates.length > 0\n ? predicates.reduce((left, right) => and(left, right))\n : undefined,\n })\n return Array.isArray(snapshot) ? snapshot : []\n }\n\n /**\n * Sends a snapshot that fulfills the `where` clause and all rows are bigger or equal to the cursor.\n * Requires a range index to be set with `setOrderByIndex` prior to calling this method.\n * It uses that range index to load the items in the order of the index.\n *\n * Cursor requests support one order term and one minValue. Multi-column\n * queries use the ordered loader's prefix-and-tie fallback instead.\n *\n * Note 1: it may load more rows than the provided LIMIT because it loads all values equal to the first cursor value + limit values greater.\n * This is needed to ensure that it does not accidentally skip duplicate values when the limit falls in the middle of some duplicated values.\n * Note 2: it does not send keys that have already been sent before.\n */\n requestLimitedSnapshot({\n orderBy,\n limit,\n minValues,\n offset,\n trackLoadSubsetPromise: shouldTrackLoadSubsetPromise = true,\n onLoadSubsetResult,\n }: RequestLimitedSnapshotOptions) {\n if (this.unsubscribed) return\n if (!limit) throw new Error(`limit is required`)\n\n if (!this.orderByIndex) {\n throw new Error(\n `Ordered snapshot was requested but no index was found. You have to call setOrderByIndex before requesting an ordered snapshot.`,\n )\n }\n\n // Validate cursor input before local delivery changes sent keys or calls user code.\n const whereFromCursor = minValues\n ? buildCursor(orderBy, minValues)\n : undefined\n\n // Check if minValues has a first element (regardless of its value)\n // This distinguishes between \"no min value provided\" vs \"min value is undefined\"\n const hasMinValue = minValues !== undefined && minValues.length > 0\n // Derive first column value from minValues (used for local index operations)\n const minValue = minValues?.[0]\n // Cast for index operations (index expects string | number)\n const minValueForIndex = minValue as string | number | undefined\n\n const index = this.orderByIndex\n const where = this.options.whereExpression\n const whereFilterFn = where\n ? createFilterFunctionFromExpression(where)\n : undefined\n\n const filterFn = (key: string | number | undefined): boolean => {\n if (key !== undefined && this.sentKeys.has(key)) {\n return false\n }\n\n const value = this.collection.get(key)\n if (value === undefined) {\n return false\n }\n\n return whereFilterFn?.(value) ?? true\n }\n\n let biggestObservedValue = minValueForIndex\n const changes: Array<ChangeMessage<any, string | number>> = []\n\n // If we have a minValue we need to handle the case\n // where there might be duplicate values equal to minValue that we need to include\n // because we can have data like this: [1, 2, 3, 3, 3, 4, 5]\n // so if minValue is 3 then the previous snapshot may not have included all 3s\n // e.g. if it was offset 0 and limit 3 it would only have loaded the first 3\n // so we load all rows equal to minValue first, to be sure we don't skip any duplicate values\n let keys: Array<string | number> = []\n if (hasMinValue) {\n // First, get all items with the same FIRST COLUMN value as minValue\n // This provides wide bounds for the local index\n const { expression } = orderBy[0]!\n const allRowsWithMinValue = this.collection.currentStateAsChanges({\n where: eq(expression, new Value(minValueForIndex)),\n })\n\n if (allRowsWithMinValue) {\n const keysWithMinValue = allRowsWithMinValue\n .map((change) => change.key)\n .filter((key) => !this.sentKeys.has(key) && filterFn(key))\n\n // Add items with the minValue first\n keys.push(...keysWithMinValue)\n\n // Then get items greater than minValue\n const keysGreaterThanMin = index.take(\n limit - keys.length,\n minValueForIndex!,\n filterFn,\n )\n keys.push(...keysGreaterThanMin)\n } else {\n keys = index.take(limit, minValueForIndex!, filterFn)\n }\n } else {\n // No min value provided, start from the beginning\n keys = index.takeFromStart(limit, filterFn)\n }\n\n const valuesNeeded = () => Math.max(limit - changes.length, 0)\n const collectionExhausted = () => keys.length === 0\n\n // Create a value extractor for the orderBy field to properly track the biggest indexed value\n const orderByExpression = orderBy[0]!.expression\n const valueExtractor =\n orderByExpression.type === `ref`\n ? compileExpression(new PropRef(orderByExpression.path), true)\n : null\n\n while (valuesNeeded() > 0 && !collectionExhausted()) {\n for (const key of keys) {\n const value = this.collection.get(key)!\n changes.push({\n type: `insert`,\n key,\n value,\n })\n // Extract the indexed value (e.g., salary) from the row, not the full row\n // This is needed for index.take() to work correctly with the BTree comparator\n biggestObservedValue = valueExtractor ? valueExtractor(value) : value\n }\n\n keys = index.take(valuesNeeded(), biggestObservedValue!, filterFn)\n }\n\n // Track row count for offset-based pagination (before sending to callback)\n // Use the current count as the offset for this load\n const currentOffset = this.limitedSnapshotRowCount\n\n // Add keys to sentKeys BEFORE calling callback to prevent race condition.\n // If a change event arrives while the callback is executing, it will see\n // the keys already in sentKeys and filter out duplicates correctly.\n for (const change of changes) {\n this.sentKeys.add(change.key)\n }\n\n this.publishSnapshot(changes)\n // A subscriber callback can synchronously tear down this subscription.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (this.unsubscribed) return\n\n // Update the row count and last key after sending (for next call's offset/cursor)\n this.limitedSnapshotRowCount = Math.max(\n this.limitedSnapshotRowCount,\n currentOffset + changes.length,\n )\n if (changes.length > 0) {\n this.lastSentKey = changes[changes.length - 1]!.key\n }\n\n // Build cursor expressions for sync layer loadSubset\n // The cursor expressions are separate from the main where clause\n // so the sync layer can choose cursor-based or offset-based pagination\n let cursorExpressions:\n | {\n whereFrom: BasicExpression<boolean>\n whereCurrent: BasicExpression<boolean>\n lastKey?: string | number\n }\n | undefined\n\n if (whereFromCursor && minValues) {\n const whereCurrentCursor = buildCursorCurrent(orderBy, minValues)\n if (whereCurrentCursor) {\n cursorExpressions = {\n whereFrom: whereFromCursor,\n whereCurrent: whereCurrentCursor,\n lastKey: this.lastSentKey,\n }\n }\n }\n\n // Request the sync layer to load more data\n // don't await it, we will load the data into the collection when it comes in\n // Note: `where` does NOT include cursor expressions - they are passed separately\n // The sync layer can choose to use cursor-based or offset-based pagination\n const loadOptions: LoadSubsetOptions = {\n where, // Main filter only, no cursor\n limit,\n orderBy,\n cursor: cursorExpressions, // Cursor expressions passed separately\n offset: offset ?? currentOffset, // Use provided offset, or auto-tracked offset\n subscription: this,\n }\n\n const {\n demand,\n result: syncResult,\n started,\n } = this.startSubsetDemand(loadOptions)\n if (!this.isDemandActive(demand)) return\n\n // Report the result synchronously, including a wait for an unavailable loader.\n onLoadSubsetResult?.(\n syncResult,\n demand.acquisition.options,\n (primaryFailure) => this.releaseDemand(demand, primaryFailure),\n )\n if (!this.isDemandActive(demand)) return\n if (started) {\n this.observeLoadSubsetResult(\n syncResult,\n demand,\n demand.acquisition.options,\n shouldTrackLoadSubsetPromise,\n )\n }\n if (!this.isDemandActive(demand)) return\n }\n\n // TODO: also add similar test but that checks that it can also load it from the collection's loadSubset function\n // and that that also works properly (i.e. does not skip duplicate values)\n\n /**\n * Filters and flips changes for keys that have not been sent yet.\n * Deletes are filtered out for keys that have not been sent yet.\n * Updates are flipped into inserts for keys that have not been sent yet.\n * Duplicate inserts are filtered out to prevent D2 multiplicity > 1.\n */\n private filterAndFlipChanges(changes: Array<ChangeMessage<any, any>>) {\n changes = this.reconcileStalePublishedChanges(changes)\n\n if (this.loadedInitialState || this.skipFiltering) {\n // We loaded the entire initial state or filtering is explicitly skipped\n // so no need to filter or flip changes\n return changes\n }\n\n // When buffering for truncate, we need all changes (including deletes) to pass through.\n // This is important because:\n // 1. If loadedInitialState was previously true, sentKeys will be empty\n // (trackSentKeys early-returns when loadedInitialState is true)\n // 2. The truncate deletes are for keys that WERE sent to the subscriber\n // 3. We're collecting all changes atomically, so filtering doesn't make sense\n const skipDeleteFilter = this.isBufferingForTruncate\n\n const newChanges = []\n for (const change of changes) {\n let newChange = change\n const keyInSentKeys = this.sentKeys.has(change.key)\n\n if (!keyInSentKeys) {\n if (change.type === `update`) {\n newChange = { ...change, type: `insert`, previousValue: undefined }\n this.sentKeys.add(change.key)\n } else if (change.type === `delete`) {\n // Filter out deletes for keys that have not been sent,\n // UNLESS we're buffering for truncate (where all deletes should pass through)\n if (!skipDeleteFilter) {\n continue\n }\n } else {\n this.sentKeys.add(change.key)\n }\n } else {\n // Key was already sent - handle based on change type\n if (change.type === `insert`) {\n // Filter out duplicate inserts - the key was already inserted.\n // This prevents D2 multiplicity from going above 1, which would\n // cause deletes to not properly remove items (multiplicity would\n // go from 2 to 1 instead of 1 to 0).\n continue\n } else if (change.type === `delete`) {\n // Remove from sentKeys so future inserts for this key are allowed\n // (e.g., after truncate + reinsert)\n this.sentKeys.delete(change.key)\n }\n }\n newChanges.push(newChange)\n }\n return newChanges\n }\n\n /**\n * After a failed replay, the source collection is empty but subscribers still\n * hold the last good publication. Reconcile the first later source delta for\n * each retained key against that publication instead of treating it as a\n * duplicate insert.\n */\n private reconcileStalePublishedChanges(\n changes: Array<ChangeMessage<any, any>>,\n ): Array<ChangeMessage<any, any>> {\n if (this.stalePublishedRows.size === 0) return changes\n\n const reconciled: Array<ChangeMessage<any, any>> = []\n for (const change of changes) {\n const previous = this.stalePublishedRows.get(change.key)\n if (previous === undefined) {\n reconciled.push(change)\n continue\n }\n\n this.stalePublishedRows.delete(change.key)\n if (change.type === `delete`) {\n reconciled.push({\n ...change,\n value: previous,\n previousValue: undefined,\n })\n } else if (!deepEquals(previous, change.value)) {\n reconciled.push({\n ...change,\n type: `update`,\n previousValue: previous,\n })\n }\n }\n // Cleanup discards rows without publishing deletes. Eager sources publish\n // their installed state; subset sources must first finish reacquisition.\n if (\n this.collection.config.syncMode !== `on-demand` &&\n !this.isBufferingForTruncate\n ) {\n for (const [key, value] of this.stalePublishedRows) {\n if (this.collection.has(key)) continue\n this.stalePublishedRows.delete(key)\n reconciled.push({ type: `delete`, key, value })\n }\n }\n return reconciled\n }\n\n private trackPublishedRows(\n changes: Array<ChangeMessage<any, string | number>>,\n ): void {\n for (const change of changes) {\n if (change.type === `delete`) {\n this.publishedRows.delete(change.key)\n } else {\n this.publishedRows.set(change.key, change.value)\n }\n }\n }\n\n private trackSentKeys(changes: Array<ChangeMessage<any, string | number>>) {\n if (this.loadedInitialState || this.skipFiltering) {\n // No need to track sent keys if we loaded the entire state or filtering is skipped.\n // Since filtering won't be applied, all keys are effectively \"observed\".\n return\n }\n\n for (const change of changes) {\n if (change.type === `delete`) {\n this.sentKeys.delete(change.key)\n } else {\n this.sentKeys.add(change.key)\n }\n }\n\n // Keep the limited snapshot offset in sync with keys we've actually sent.\n // This matters when loadSubset resolves asynchronously and requestLimitedSnapshot\n // didn't have local rows to count yet.\n if (this.orderByIndex) {\n this.limitedSnapshotRowCount = Math.max(\n this.limitedSnapshotRowCount,\n this.sentKeys.size,\n )\n }\n }\n\n /**\n * Mark that the subscription should not filter any changes.\n * This is used when includeInitialState is explicitly set to false,\n * meaning the caller doesn't want initial state but does want ALL future changes.\n */\n markAllStateAsSeen() {\n this.skipFiltering = true\n }\n\n unsubscribe() {\n if (this.unsubscribed) return\n this.unsubscribed = true\n // Stop any status listener set already being iterated. Clearing the\n // emitter's map cannot invalidate that captured Set by itself.\n this.statusRevision++\n const sourceListenerCleanups = [\n this.truncateCleanup,\n this.collectionCleanup,\n this.collectionRestartCleanup,\n ]\n this.truncateCleanup = undefined\n this.collectionCleanup = undefined\n this.collectionRestartCleanup = undefined\n\n runAllCallbacks([\n ...sourceListenerCleanups.map((cleanup) => () => cleanup?.()),\n () => {\n // Stop any buffered replay from publishing after unsubscription.\n this.discardTruncateReplay()\n this.stalePublishedRows.clear()\n\n // Retire every owner before an unload can reenter teardown.\n const acquisitions = this.subsetDemands\n .filter((demand) => demand.acquisitionState === `active`)\n .map((demand) => demand.acquisition)\n for (const demand of this.subsetDemands) {\n demand.initialResult?.reject(new LoadSubsetOperationAbortedError())\n this.stopDemandStatusParticipants(demand)\n if (demand.acquisitionState === `starting`) {\n cancelAcquisition(demand.acquisition)\n }\n }\n this.subsetDemands = []\n runAllCallbacks(\n acquisitions.map(\n (acquisition) => () => this.releaseAcquisition(acquisition),\n ),\n )\n },\n () =>\n this.emitInner(`unsubscribed`, {\n type: `unsubscribed`,\n subscription: this,\n }),\n // Clear all event listeners to prevent memory leaks\n () => this.clearListeners(),\n ])\n }\n}\n"],"names":["createDeferred","EventEmitter","ensureIndexForExpression","createFilteredCallback","LoadSubsetOperationAbortedError","error","normalizeError","deepEquals","and","runAllCallbacks","buildCursor","createFilterFunctionFromExpression","eq","Value","compileExpression","PropRef","buildCursorCurrent"],"mappings":"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"}