UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines 58.3 kB
{"version":3,"file":"subscription.cjs","sources":["../../../src/collection/subscription.ts"],"sourcesContent":["import { ensureIndexForExpression } from '../indexes/auto-index.js'\nimport { and, eq, gte, lt } 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 } from '../utils/cursor.js'\nimport { deepEquals } from '../utils.js'\nimport {\n  createFilterFunctionFromExpression,\n  createFilteredCallback,\n} from './change-events.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { IndexInterface } from '../indexes/base-index.js'\nimport type {\n  ChangeMessage,\n  LoadSubsetOptions,\n  Subscription,\n  SubscriptionEvents,\n  SubscriptionLoadSubsetErrorEvent,\n  SubscriptionStatus,\n  SubscriptionUnsubscribedEvent,\n} from '../types.js'\nimport type { CollectionImpl } from './index.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 raw loadSubset result for external tracking */\n  onLoadSubsetResult?: (result: Promise<void> | true) => void\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  /** All column values for cursor (first value used for local index, all values for sync layer) */\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 raw loadSubset result for external tracking */\n  onLoadSubsetResult?: (result: Promise<void> | true) => void\n}\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}\n\ntype TruncatePublicationState = {\n  loadedInitialState: boolean\n  snapshotSent: boolean\n  sentKeys: Set<string | number>\n  publishedRows: Map<string | number, object>\n  limitedSnapshotRowCount: number\n  lastSentKey: string | number | undefined\n}\n\ntype SubsetAcquisition = {\n  options: LoadSubsetOptions\n  abortController?: AbortController\n  removeRequestAbortListener?: () => void\n}\n\ntype SubsetDemand = SubsetAcquisition & {\n  requestOptions: LoadSubsetOptions\n}\n\ntype TruncateReplayAttempt = {\n  pending: Set<{ promise: Promise<void> }>\n  failed: boolean\n  setupComplete: boolean\n}\n\ntype TruncateReplaySession = {\n  publicationState: TruncatePublicationState\n  buffer: Array<Array<ChangeMessage<any, any>>>\n  attempts: Set<TruncateReplayAttempt>\n  currentAttempt: TruncateReplayAttempt\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 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: IndexInterface<string | number> | undefined\n\n  // Status tracking\n  private _status: SubscriptionStatus = `ready`\n  private _lastError: unknown | undefined\n  private pendingLoadSubsetPromises: Set<Promise<void>> = new Set()\n\n  // Cleanup function for truncate event listener\n  private truncateCleanup: (() => 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\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  }\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, resumes ordinary deltas,\n   * and retains subset ownership so a later truncate can retry the replay.\n   */\n  private handleTruncate() {\n    const demandsToReload = [...this.subsetDemands]\n\n    // Only buffer if there's an actual loadSubset handler that can do async work.\n    // Without a loadSubset handler, there's nothing to re-request and no reason to buffer.\n    // This prevents unnecessary buffering in eager sync mode or when loadSubset isn't implemented.\n    const hasLoadSubsetHandler = this.collection._sync.syncLoadSubsetFn !== null\n\n    // If there are no subsets to reload OR no loadSubset handler, just reset state\n    if (demandsToReload.length === 0 || !hasLoadSubsetHandler) {\n      this.snapshotSent = false\n      this.loadedInitialState = false\n      this.limitedSnapshotRowCount = 0\n      this.lastSentKey = undefined\n      return\n    }\n\n    const attempt: TruncateReplayAttempt = {\n      pending: new Set(),\n      failed: false,\n      setupComplete: false,\n    }\n    let session = this.truncateReplaySession\n    if (!session) {\n      session = {\n        publicationState: {\n          loadedInitialState: this.loadedInitialState,\n          snapshotSent: this.snapshotSent,\n          sentKeys: new Set(this.sentKeys),\n          publishedRows: new Map(this.publishedRows),\n          limitedSnapshotRowCount: this.limitedSnapshotRowCount,\n          lastSentKey: this.lastSentKey,\n        },\n        buffer: [],\n        attempts: new Set(),\n        currentAttempt: attempt,\n      }\n      this.truncateReplaySession = session\n    }\n    session.attempts.add(attempt)\n    session.currentAttempt = attempt\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.abortController?.abort()\n    }\n\n    // Start buffering before the truncate commit publishes its deletes. Every\n    // overlapping attempt shares this one publication baseline and buffer.\n    // Retained rows from an earlier failed replay stay marked until this\n    // attempt either replaces them or proves they are absent.\n\n    // Reset snapshot/pagination tracking state for the replacement snapshot.\n    this.snapshotSent = false\n    this.loadedInitialState = false\n    this.limitedSnapshotRowCount = 0\n    this.lastSentKey = undefined\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\n      for (const demand of demandsToReload) {\n        if (!this.subsetDemands.includes(demand)) continue\n\n        const isCurrentAttempt = () =>\n          this.truncateReplaySession === session &&\n          session.currentAttempt === attempt\n        const nextAcquisition = this.createSubsetAcquisition(demand)\n        let syncResult: Promise<void> | true\n        try {\n          syncResult = this.loadSubset(\n            nextAcquisition.options,\n            isCurrentAttempt,\n          )\n        } catch {\n          nextAcquisition.abortController.abort()\n          nextAcquisition.removeRequestAbortListener?.()\n          attempt.failed = true\n          continue\n        }\n\n        this.observeLoadSubsetResult(\n          syncResult,\n          nextAcquisition.options,\n          true,\n          () => isCurrentAttempt() && !nextAcquisition.options.signal?.aborted,\n        )\n\n        if (syncResult instanceof Promise) {\n          // A transport promise may be shared by several deduplicated logical\n          // demands. Track each demand separately so one settlement observer\n          // cannot complete the attempt before the others apply their result.\n          const pending = { promise: syncResult }\n          attempt.pending.add(pending)\n          void syncResult.then(\n            () => this.settleTruncateReplay(session, attempt, pending),\n            () => {\n              // A released demand no longer participates in the current\n              // replacement. Its cooperative AbortError must not discard the\n              // successful rows from demands that are still active.\n              if (\n                this.subsetDemands.includes(demand) &&\n                !nextAcquisition.options.signal?.aborted\n              ) {\n                attempt.failed = true\n              }\n              this.settleTruncateReplay(session, attempt, pending)\n            },\n          )\n        }\n\n        try {\n          this.replaceSubsetAcquisition(demand, nextAcquisition)\n        } catch (error) {\n          // The old lease is still owned because its release failed. Abort and\n          // release the new acquisition, but keep observing its work so rows\n          // from a non-cooperative adapter cannot escape the replay buffer.\n          nextAcquisition.abortController.abort()\n          nextAcquisition.removeRequestAbortListener?.()\n          try {\n            this.collection._sync.unloadSubset(nextAcquisition.options)\n          } catch {\n            // Preserve the first ownership error. The demand still retains the\n            // old acquisition so normal cleanup can retry that release.\n          }\n          this.recordLoadSubsetError(demand.options, error, true)\n          attempt.failed = true\n        }\n      }\n\n      attempt.setupComplete = true\n      this.checkTruncateReplayComplete(session)\n    })\n  }\n\n  private settleTruncateReplay(\n    session: TruncateReplaySession,\n    attempt: TruncateReplayAttempt,\n    pending: { promise: Promise<void> },\n  ): void {\n    if (this.truncateReplaySession !== session) return\n    attempt.pending.delete(pending)\n    this.checkTruncateReplayComplete(session)\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    for (const attempt of session.attempts) {\n      if (!attempt.setupComplete || attempt.pending.size > 0) return\n    }\n\n    if (session.currentAttempt.failed) {\n      this.abandonTruncateReplay(session)\n    } else {\n      this.flushTruncateReplay(session)\n    }\n  }\n\n  /**\n   * Discard an incomplete current replay and restore the last publication.\n   * Rows in that publication remain stale until a later source delta or replay\n   * reconciles them with the source collection.\n   */\n  private abandonTruncateReplay(session: TruncateReplaySession): void {\n    if (this.truncateReplaySession !== session) return\n    const publicationState = session.publicationState\n    this.loadedInitialState = publicationState.loadedInitialState\n    this.snapshotSent = publicationState.snapshotSent\n    this.sentKeys = new Set(publicationState.sentKeys)\n    this.publishedRows = new Map(publicationState.publishedRows)\n    this.stalePublishedRows = new Map(publicationState.publishedRows)\n    this.limitedSnapshotRowCount = publicationState.limitedSnapshotRowCount\n    this.lastSentKey = publicationState.lastSentKey\n    this.truncateReplaySession = undefined\n  }\n\n  /** Publish the complete buffered replacement as one subscriber batch. */\n  private flushTruncateReplay(session: TruncateReplaySession): void {\n    if (this.truncateReplaySession !== session) return\n    this.truncateReplaySession = undefined\n\n    const retainedDeletes = [...this.stalePublishedRows].map(\n      ([key, value]): ChangeMessage<any, any> => ({\n        type: `delete`,\n        key,\n        value,\n      }),\n    )\n    this.stalePublishedRows.clear()\n\n    const merged = [...session.buffer.flat(), ...retainedDeletes]\n    const activeDemandFilters = this.subsetDemands.map((demand) =>\n      demand.requestOptions.where\n        ? createFilterFunctionFromExpression(demand.requestOptions.where)\n        : undefined,\n    )\n    const replacement = this.createPublicationDiff(\n      session.publicationState.publishedRows,\n      merged,\n      (value) => activeDemandFilters.some((filter) => filter?.(value) ?? true),\n    )\n    if (replacement.length > 0) this.filteredCallback(replacement)\n    // Buffering records every source key before active-demand filtering. Reset\n    // the dedupe set to what the subscriber actually received so a later\n    // request can publish a row that belonged only to a released demand.\n    this.sentKeys = new Set(this.publishedRows.keys())\n    if (this.orderByIndex) {\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\n  /** Reduce a replay's raw delete/insert stream to one exact semantic delta. */\n  private createPublicationDiff(\n    baseline: ReadonlyMap<string | number, object>,\n    changes: ReadonlyArray<ChangeMessage<any, any>>,\n    isCoveredByActiveDemand: (value: object) => boolean,\n  ): Array<ChangeMessage<any, any>> {\n    const finalRows = new Map(baseline)\n    for (const change of changes) {\n      if (change.type === `delete`) finalRows.delete(change.key)\n      else finalRows.set(change.key, change.value)\n    }\n    for (const [key, value] of finalRows) {\n      if (!isCoveredByActiveDemand(value)) finalRows.delete(key)\n    }\n\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  setOrderByIndex(index: IndexInterface<any>) {\n    this.orderByIndex = index\n  }\n\n  /**\n   * Check if an orderBy index has been set for this subscription\n   */\n  hasOrderByIndex(): boolean {\n    return this.orderByIndex !== undefined\n  }\n\n  /**\n   * Set subscription status and emit events if changed\n   */\n  private setStatus(newStatus: SubscriptionStatus) {\n    if (this._status === newStatus) {\n      return // No change\n    }\n\n    const previousStatus = this._status\n    this._status = newStatus\n\n    // Emit status:change event\n    this.emitInner(`status:change`, {\n      type: `status:change`,\n      subscription: this,\n      previousStatus,\n      status: newStatus,\n    })\n\n    // Emit specific status event\n    const eventKey: `status:${SubscriptionStatus}` = `status:${newStatus}`\n    this.emitInner(eventKey, {\n      type: eventKey,\n      subscription: this,\n      previousStatus,\n      status: newStatus,\n    } as SubscriptionEvents[typeof eventKey])\n  }\n\n  /** Observe an asynchronous subset load and restore status on settlement. */\n  private observeLoadSubsetResult(\n    syncResult: Promise<void> | true,\n    options: LoadSubsetOptions,\n    trackStatus: boolean,\n    shouldReportError: () => boolean = () => true,\n  ) {\n    if (!(syncResult instanceof Promise)) return\n\n    if (trackStatus) {\n      this.pendingLoadSubsetPromises.add(syncResult)\n      this.setStatus(`loadingSubset`)\n    }\n\n    const finish = () => {\n      if (trackStatus) {\n        this.pendingLoadSubsetPromises.delete(syncResult)\n        if (this.pendingLoadSubsetPromises.size === 0) {\n          this.setStatus(`ready`)\n        }\n      }\n    }\n\n    void syncResult.then(finish, (error: unknown) => {\n      if (shouldReportError()) this.recordLoadSubsetError(options, error)\n      finish()\n    })\n  }\n\n  private loadSubset(\n    options: LoadSubsetOptions,\n    shouldReportError: () => boolean = () => true,\n  ): Promise<void> | true {\n    try {\n      return this.collection._sync.loadSubset(options)\n    } catch (error) {\n      if (shouldReportError()) this.recordLoadSubsetError(options, error)\n      throw error\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      abortController,\n      removeRequestAbortListener,\n    }\n  }\n\n  /** Replace the adapter lease held for one logical subset demand. */\n  private replaceSubsetAcquisition(\n    demand: SubsetDemand,\n    next: SubsetAcquisition & { abortController: AbortController },\n  ): void {\n    const previousOptions = demand.options\n    const removePreviousAbortListener = demand.removeRequestAbortListener\n    this.collection._sync.unloadSubset(previousOptions)\n    removePreviousAbortListener?.()\n    demand.options = next.options\n    demand.abortController = next.abortController\n    demand.removeRequestAbortListener = next.removeRequestAbortListener\n  }\n\n  /** Abort and release one current adapter acquisition. */\n  private releaseSubsetDemand(demand: SubsetDemand): void {\n    demand.abortController?.abort()\n    try {\n      this.collection._sync.unloadSubset(demand.options)\n    } finally {\n      demand.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: Promise<void> | true\n  } {\n    const demand: SubsetDemand = {\n      requestOptions,\n      options: requestOptions,\n    }\n    const acquisition = this.createSubsetAcquisition(demand)\n    try {\n      const result = this.loadSubset(acquisition.options)\n      demand.options = acquisition.options\n      demand.abortController = acquisition.abortController\n      demand.removeRequestAbortListener = acquisition.removeRequestAbortListener\n      this.subsetDemands.push(demand)\n      return { demand, result }\n    } catch (error) {\n      acquisition.abortController.abort()\n      acquisition.removeRequestAbortListener?.()\n      throw error\n    }\n  }\n\n  private recordLoadSubsetError(\n    options: LoadSubsetOptions,\n    error: unknown,\n    reportAborted = false,\n  ): void {\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\n\n    this._lastError = error\n    this.emitInner(`loadSubset:error`, {\n      type: `loadSubset:error`,\n      subscription: this,\n      options,\n      error,\n    })\n  }\n\n  hasLoadedInitialState() {\n    return this.loadedInitialState\n  }\n\n  hasSentAtLeastOneSnapshot() {\n    return this.snapshotSent\n  }\n\n  emitEvents(changes: Array<ChangeMessage<any, any>>): boolean {\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    if (this.isBufferingForTruncate) {\n      // Buffer the changes instead of emitting immediately\n      // This prevents a flash of missing content during truncate/refetch\n      if (newChanges.length > 0) {\n        this.truncateReplaySession!.buffer.push(newChanges)\n      }\n      return false\n    } else {\n      return this.filteredCallback(newChanges)\n    }\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.\n   */\n  requestSnapshot(opts?: RequestSnapshotOptions): boolean {\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 { demand, result: syncResult } = this.startSubsetDemand(loadOptions)\n    if (opts?.where) this.requestedSubsetWhere.set(loadOptions, opts.where)\n\n    // Pass the raw loadSubset result to the caller for external tracking\n    opts?.onLoadSubsetResult?.(syncResult)\n\n    this.observeLoadSubsetResult(\n      syncResult,\n      demand.options,\n      opts?.trackLoadSubsetPromise ?? true,\n    )\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        snapshot = this.collection.currentStateAsChanges({\n          ...stateOpts,\n          optimizedOnly: false,\n        })\n      }\n    } else {\n      snapshot = this.collection.currentStateAsChanges(stateOpts)\n    }\n\n    if (snapshot === undefined) {\n      // Couldn't load from indexes\n      return false\n    }\n\n    // Only send changes that have not been sent yet\n    const filteredSnapshot = snapshot.filter(\n      (change) => !this.sentKeys.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.callback(filteredSnapshot)\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    const [demand] = this.subsetDemands.splice(index, 1)\n    if (demand) this.releaseSubsetDemand(demand)\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   * For multi-column orderBy:\n   * - Uses first value from `minValues` for LOCAL index operations (wide bounds, ensures no missed rows)\n   * - Uses all `minValues` to build a precise composite cursor for SYNC layer loadSubset\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 (!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    // 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    //\n    // For multi-column orderBy, we use the first column value for index operations (wide bounds)\n    // This may load some duplicates but ensures we never miss any rows.\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      const insertedKeys = new Set<string | number>() // Track keys we add to `changes` in this iteration\n\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        insertedKeys.add(key) // Track this key\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.callback(changes)\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 (minValues !== undefined && minValues.length > 0) {\n      const whereFromCursor = buildCursor(orderBy, minValues)\n\n      if (whereFromCursor) {\n        const { expression } = orderBy[0]!\n        const cursorMinValue = minValues[0]\n\n        // Build the whereCurrent expression for the first orderBy column\n        // For Date values, we need to handle precision differences between JS (ms) and backends (μs)\n        // A JS Date represents a 1ms range, so we query for all values within that range\n        let whereCurrentCursor: BasicExpression<boolean>\n        if (cursorMinValue instanceof Date) {\n          const cursorMinValuePlus1ms = new Date(cursorMinValue.getTime() + 1)\n          whereCurrentCursor = and(\n            gte(expression, new Value(cursorMinValue)),\n            lt(expression, new Value(cursorMinValuePlus1ms)),\n          )\n        } else {\n          whereCurrentCursor = eq(expression, new Value(cursorMinValue))\n        }\n\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 { demand, result: syncResult } = this.startSubsetDemand(loadOptions)\n\n    // Pass the raw loadSubset result to the caller for external tracking\n    onLoadSubsetResult?.(syncResult)\n    this.observeLoadSubsetResult(\n      syncResult,\n      demand.options,\n      shouldTrackLoadSubsetPromise,\n    )\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    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    let firstCleanupError: unknown\n\n    // Clean up truncate event listener\n    try {\n      this.truncateCleanup?.()\n    } catch (error) {\n      firstCleanupError = error\n    }\n    this.truncateCleanup = undefined\n\n    // Stop any buffered replay from publishing after unsubscription.\n    this.truncateReplaySession = undefined\n    this.stalePublishedRows.clear()\n\n    // Release the current adapter acquisition for each logical subset demand.\n    for (const demand of this.subsetDemands) {\n      try {\n        this.releaseSubsetDemand(demand)\n      } catch (error) {\n        firstCleanupError ??= error\n      }\n    }\n    this.subsetDemands = []\n\n    try {\n      this.emitInner(`unsubscribed`, {\n        type: `unsubscribed`,\n        subscription: this,\n      })\n    } catch (error) {\n      firstCleanupError ??= error\n    } finally {\n      // Clear all event listeners to prevent memory leaks\n      this.clearListeners()\n    }\n\n    if (firstCleanupError !== undefined) throw firstCleanupError\n  }\n}\n"],"names":["EventEmitter","ensureIndexForExpression","createFilteredCallback","createFilterFunctionFromExpression","deepEquals","and","demand","eq","Value","compileExpression","PropRef","buildCursor","gte","lt"],"mappings":"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"}