UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1,392 lines • 53.7 kB
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const utils = require("../utils.cjs");
const SortedMap = require("../SortedMap.cjs");
const virtualProps = require("../virtual-props.cjs");
const errors = require("../errors.cjs");
const transactionMetadata = require("./transaction-metadata.cjs");
class CollectionStateManager {
  /**
   * Creates a new CollectionState manager
   */
  constructor(config) {
    this.pendingSyncedTransactions = [];
    this.pendingSyncedProjection = {
      states: /* @__PURE__ */ new Map(),
      truncated: false
    };
    this.syncedMetadata = /* @__PURE__ */ new Map();
    this.syncedCollectionMetadata = /* @__PURE__ */ new Map();
    this.hydrationSeedKeys = /* @__PURE__ */ new Set();
    this.hydratedKeys = /* @__PURE__ */ new Set();
    this.hasAppliedAdapterTruncate = false;
    this.optimisticUpserts = /* @__PURE__ */ new Map();
    this.optimisticDeletes = /* @__PURE__ */ new Set();
    this.pendingOptimisticUpserts = /* @__PURE__ */ new Map();
    this.pendingOptimisticDeletes = /* @__PURE__ */ new Set();
    this.pendingOptimisticDirectUpserts = /* @__PURE__ */ new Set();
    this.pendingOptimisticDirectDeletes = /* @__PURE__ */ new Set();
    this.acknowledgedInserts = /* @__PURE__ */ new WeakSet();
    this.rowOrigins = /* @__PURE__ */ new Map();
    this.pendingLocalChanges = /* @__PURE__ */ new Set();
    this.pendingLocalOrigins = /* @__PURE__ */ new Set();
    this.virtualPropsCache = /* @__PURE__ */ new WeakMap();
    this.size = 0;
    this.preSyncVisibleState = /* @__PURE__ */ new Map();
    this.preSyncVirtualState = /* @__PURE__ */ new Map();
    this.recentlySyncedKeys = /* @__PURE__ */ new Set();
    this.hasReceivedFirstCommit = false;
    this.isCommittingSyncTransactions = false;
    this.isDrainingSyncTransactions = false;
    this.syncRunGeneration = 0;
    this.isLocalOnly = false;
    this.commitPendingTransactions = () => {
      if (this.isDrainingSyncTransactions) return;
      this.isDrainingSyncTransactions = true;
      let failed = false;
      let firstError;
      try {
        let result;
        do {
          result = this.commitNextPendingTransactionBatch();
          if (result.failure && !failed) {
            failed = true;
            firstError = result.failure.error;
          }
        } while (result.processed);
      } finally {
        this.isDrainingSyncTransactions = false;
      }
      if (failed) throw firstError;
    };
    this.config = config;
    this.transactions = new SortedMap.SortedMap(
      (a, b) => a.compareCreatedAt(b)
    );
    this.syncedData = new SortedMap.SortedMap(config.compare);
  }
  setDeps(deps) {
    this.collection = deps.collection;
    this.lifecycle = deps.lifecycle;
    this.changes = deps.changes;
    this.indexes = deps.indexes;
    this._events = deps.events;
  }
  /**
   * Checks whether this row currently has no pending local optimistic writes.
   *
   * This is local mutation status, not backend confirmation: `true` means the
   * row is not currently affected by an optimistic transaction in this
   * collection's visible state.
   *
   * Used to compute the $synced virtual property.
   */
  isRowSynced(key) {
    if (this.isLocalOnly) {
      return true;
    }
    return !this.optimisticUpserts.has(key) && !this.optimisticDeletes.has(key);
  }
  /**
   * Gets the origin of the last confirmed change to a row.
   * Returns 'local' if the row has optimistic mutations (optimistic changes are local).
   * Used to compute the $origin virtual property.
   */
  getRowOrigin(key) {
    if (this.isLocalOnly) {
      return "local";
    }
    if (this.optimisticUpserts.has(key) || this.optimisticDeletes.has(key)) {
      return "local";
    }
    return this.rowOrigins.get(key) ?? "remote";
  }
  createVirtualPropsSnapshot(key, overrides) {
    return {
      $synced: overrides?.$synced ?? this.isRowSynced(key),
      $origin: overrides?.$origin ?? this.getRowOrigin(key),
      $key: overrides?.$key ?? key,
      $collectionId: overrides?.$collectionId ?? this.collection.id
    };
  }
  getVirtualPropsSnapshotForState(key, options) {
    if (this.isLocalOnly) {
      return this.createVirtualPropsSnapshot(key, {
        $synced: true,
        $origin: "local"
      });
    }
    const optimisticUpserts = options?.optimisticUpserts ?? this.optimisticUpserts;
    const optimisticDeletes = options?.optimisticDeletes ?? this.optimisticDeletes;
    const hasOptimisticChange = optimisticUpserts.has(key) || optimisticDeletes.has(key) || options?.completedOptimisticKeys?.has(key) === true;
    return this.createVirtualPropsSnapshot(key, {
      $synced: !hasOptimisticChange,
      $origin: hasOptimisticChange ? "local" : (options?.rowOrigins ?? this.rowOrigins).get(key) ?? "remote"
    });
  }
  snapshotRowOriginsForKeys(keys) {
    const rowOrigins = /* @__PURE__ */ new Map();
    for (const key of keys) {
      const origin = this.rowOrigins.get(key);
      if (origin !== void 0) {
        rowOrigins.set(key, origin);
      }
    }
    return rowOrigins;
  }
  enrichWithVirtualPropsSnapshot(row, virtualProps2) {
    const existingRow = row;
    const synced = existingRow.$synced ?? virtualProps2.$synced;
    const origin = existingRow.$origin ?? virtualProps2.$origin;
    const resolvedKey = existingRow.$key ?? virtualProps2.$key;
    const collectionId = existingRow.$collectionId ?? virtualProps2.$collectionId;
    const cached = this.virtualPropsCache.get(row);
    if (cached && cached.synced === synced && cached.origin === origin && cached.key === resolvedKey && cached.collectionId === collectionId) {
      return cached.enriched;
    }
    const enriched = {
      ...row,
      $synced: synced,
      $origin: origin,
      $key: resolvedKey,
      $collectionId: collectionId
    };
    this.virtualPropsCache.set(row, {
      synced,
      origin,
      key: resolvedKey,
      collectionId,
      enriched
    });
    return enriched;
  }
  clearOriginTrackingState() {
    this.rowOrigins.clear();
    this.pendingLocalChanges.clear();
    this.pendingLocalOrigins.clear();
  }
  /**
   * Enriches a row with virtual properties using the "add-if-missing" pattern.
   * If the row already has virtual properties (from an upstream collection),
   * they are preserved. Otherwise, new values are computed.
   */
  enrichWithVirtualProps(row, key) {
    return this.enrichWithVirtualPropsSnapshot(
      row,
      this.createVirtualPropsSnapshot(key)
    );
  }
  /**
   * Creates a change message with virtual properties.
   * Uses the "add-if-missing" pattern so that pass-through from upstream
   * collections works correctly.
   */
  enrichChangeMessage(change) {
    const { __virtualProps } = change;
    const enrichedValue = __virtualProps?.value ? this.enrichWithVirtualPropsSnapshot(change.value, __virtualProps.value) : this.enrichWithVirtualProps(change.value, change.key);
    const enrichedPreviousValue = change.previousValue ? __virtualProps?.previousValue ? this.enrichWithVirtualPropsSnapshot(
      change.previousValue,
      __virtualProps.previousValue
    ) : this.enrichWithVirtualProps(change.previousValue, change.key) : void 0;
    return {
      key: change.key,
      type: change.type,
      value: enrichedValue,
      previousValue: enrichedPreviousValue,
      metadata: change.metadata
    };
  }
  /**
   * Get the current value for a key enriched with virtual properties.
   */
  getWithVirtualProps(key) {
    const value = this.get(key);
    if (value === void 0) {
      return void 0;
    }
    return this.enrichWithVirtualProps(value, key);
  }
  /**
   * Get the current value for a key (virtual derived state)
   */
  get(key) {
    const { optimisticDeletes, optimisticUpserts, syncedData } = this;
    if (optimisticDeletes.has(key)) {
      return void 0;
    }
    if (optimisticUpserts.has(key)) {
      return optimisticUpserts.get(key);
    }
    return syncedData.get(key);
  }
  /**
   * Check if a key exists in the collection (virtual derived state)
   */
  has(key) {
    const { optimisticDeletes, optimisticUpserts, syncedData } = this;
    if (optimisticDeletes.has(key)) {
      return false;
    }
    if (optimisticUpserts.has(key)) {
      return true;
    }
    return syncedData.has(key);
  }
  /**
   * Get all keys (virtual derived state)
   */
  *keys() {
    const { syncedData, optimisticDeletes, optimisticUpserts } = this;
    for (const key of syncedData.keys()) {
      if (!optimisticDeletes.has(key)) {
        yield key;
      }
    }
    for (const key of optimisticUpserts.keys()) {
      if (!syncedData.has(key) && !optimisticDeletes.has(key)) {
        yield key;
      }
    }
  }
  /**
   * Get all values (virtual derived state)
   */
  *values() {
    for (const key of this.keys()) {
      const value = this.get(key);
      if (value !== void 0) {
        yield value;
      }
    }
  }
  /**
   * Get all entries (virtual derived state)
   */
  *entries() {
    for (const key of this.keys()) {
      const value = this.get(key);
      if (value !== void 0) {
        yield [key, value];
      }
    }
  }
  /**
   * Get all entries (virtual derived state)
   */
  *[Symbol.iterator]() {
    for (const [key, value] of this.entries()) {
      yield [key, value];
    }
  }
  /**
   * Execute a callback for each entry in the collection
   */
  forEach(callbackfn) {
    let index = 0;
    for (const [key, value] of this.entries()) {
      callbackfn(value, key, index++);
    }
  }
  /**
   * Create a new array with the results of calling a function for each entry in the collection
   */
  map(callbackfn) {
    const result = [];
    let index = 0;
    for (const [key, value] of this.entries()) {
      result.push(callbackfn(value, key, index++));
    }
    return result;
  }
  /**
   * Check if the given collection is this collection
   * @param collection The collection to check
   * @returns True if the given collection is this collection, false otherwise
   */
  isThisCollection(collection) {
    return collection === this.collection;
  }
  /**
   * Recompute optimistic state from active transactions
   */
  recomputeOptimisticState(triggeredByUserAction = false) {
    if (this.isCommittingSyncTransactions && !triggeredByUserAction) {
      return;
    }
    const previousState = new Map(this.optimisticUpserts);
    const previousDeletes = new Set(this.optimisticDeletes);
    const previousRowOrigins = this.rowOrigins;
    const pendingInserts = /* @__PURE__ */ new Map();
    for (const transaction of this.transactions.values()) {
      const isDirectTransaction = transaction.metadata[transactionMetadata.DIRECT_TRANSACTION_METADATA_KEY] === true;
      if (transaction.state === `completed`) {
        for (const mutation of transaction.mutations) {
          if (!this.isThisCollection(mutation.collection)) {
            continue;
          }
          if (isDirectTransaction && mutation.type === `insert` && this.acknowledgedInserts.has(mutation)) {
            continue;
          }
          this.pendingLocalOrigins.add(mutation.key);
          if (!mutation.optimistic) {
            continue;
          }
          switch (mutation.type) {
            case `insert`:
            case `update`: {
              const previous = this.pendingOptimisticUpserts.get(mutation.key);
              const confirmsInsert = previous?.insertDependency?.mutation === mutation;
              const insertDependency = mutation.type === `update` ? previous?.insertDependency ?? pendingInserts.get(mutation.key) : void 0;
              this.pendingOptimisticUpserts.set(mutation.key, {
                key: mutation.key,
                modified: confirmsInsert ? previous.modified : mutation.modified,
                insertDependency
              });
              if (!insertDependency) {
                this.pendingOptimisticDeletes.delete(mutation.key);
                this.pendingOptimisticDirectDeletes.delete(mutation.key);
              }
              if (isDirectTransaction) {
                this.pendingOptimisticDirectUpserts.add(mutation.key);
              } else {
                this.pendingOptimisticDirectUpserts.delete(mutation.key);
              }
              break;
            }
            case `delete`: {
              const hasDependentEdit = this.pendingOptimisticUpserts.get(mutation.key)?.insertDependency !== void 0;
              if (!hasDependentEdit) {
                this.pendingOptimisticUpserts.delete(mutation.key);
                this.pendingOptimisticDirectUpserts.delete(mutation.key);
              }
              this.pendingOptimisticDeletes.add(mutation.key);
              if (isDirectTransaction) {
                this.pendingOptimisticDirectDeletes.add(mutation.key);
              } else {
                this.pendingOptimisticDirectDeletes.delete(mutation.key);
              }
              break;
            }
          }
        }
      } else {
        for (const mutation of transaction.mutations) {
          if (!this.isThisCollection(mutation.collection) || mutation.type !== `insert` || !mutation.optimistic)
            continue;
          if (transaction.state !== `failed` && !this.acknowledgedInserts.has(mutation)) {
            pendingInserts.set(mutation.key, { mutation, transaction });
          } else if (this.pendingOptimisticUpserts.get(mutation.key)?.insertDependency?.mutation === mutation) {
            this.pendingOptimisticUpserts.delete(mutation.key);
            this.pendingOptimisticDirectUpserts.delete(mutation.key);
          }
        }
      }
    }
    this.optimisticUpserts.clear();
    this.optimisticDeletes.clear();
    this.pendingLocalChanges.clear();
    const pendingSyncKeys = /* @__PURE__ */ new Set();
    for (const transaction of this.pendingSyncedTransactions) {
      for (const operation of transaction.operations) {
        pendingSyncKeys.add(operation.key);
      }
    }
    const staleOptimisticUpserts = [];
    for (const [key, value] of this.pendingOptimisticUpserts) {
      if (pendingSyncKeys.has(key) || this.pendingOptimisticDirectUpserts.has(key)) {
        this.optimisticUpserts.set(key, this.resolveOptimisticUpsert(value));
      } else {
        staleOptimisticUpserts.push(key);
      }
    }
    for (const key of staleOptimisticUpserts) {
      this.pendingOptimisticUpserts.delete(key);
      this.pendingLocalOrigins.delete(key);
    }
    const staleOptimisticDeletes = [];
    for (const key of this.pendingOptimisticDeletes) {
      if (pendingSyncKeys.has(key) || this.pendingOptimisticDirectDeletes.has(key)) {
        this.optimisticDeletes.add(key);
      } else {
        staleOptimisticDeletes.push(key);
      }
    }
    for (const key of staleOptimisticDeletes) {
      this.pendingOptimisticDeletes.delete(key);
      this.pendingLocalOrigins.delete(key);
    }
    const activeTransactions = [];
    for (const transaction of this.transactions.values()) {
      if (![`completed`, `failed`].includes(transaction.state)) {
        activeTransactions.push(transaction);
      }
    }
    for (const transaction of activeTransactions) {
      for (const mutation of transaction.mutations) {
        if (!this.isThisCollection(mutation.collection)) {
          continue;
        }
        this.pendingLocalChanges.add(mutation.key);
        if (mutation.optimistic) {
          switch (mutation.type) {
            case `insert`:
            case `update`:
              this.optimisticUpserts.set(
                mutation.key,
                this.resolveOptimisticUpsert(mutation)
              );
              this.optimisticDeletes.delete(mutation.key);
              break;
            case `delete`:
              this.optimisticUpserts.delete(mutation.key);
              this.optimisticDeletes.add(mutation.key);
              break;
          }
        }
      }
    }
    this.size = this.calculateSize();
    const events = [];
    this.collectOptimisticChanges(
      previousState,
      previousDeletes,
      previousRowOrigins,
      events
    );
    const filteredEventsBySyncStatus = events.filter((event) => {
      if (!this.recentlySyncedKeys.has(event.key)) {
        return true;
      }
      if (triggeredByUserAction) {
        return true;
      }
      return false;
    });
    if (this.changes.shouldBatchEvents && !triggeredByUserAction) {
      const pendingSyncKeysForFilter = /* @__PURE__ */ new Set();
      for (const transaction of this.pendingSyncedTransactions) {
        for (const operation of transaction.operations) {
          pendingSyncKeysForFilter.add(operation.key);
        }
      }
      const filteredEvents = filteredEventsBySyncStatus.filter((event) => {
        if (event.type === `delete` && pendingSyncKeysForFilter.has(event.key)) {
          const hasActiveOptimisticMutation = activeTransactions.some(
            (tx) => tx.mutations.some(
              (m) => this.isThisCollection(m.collection) && m.key === event.key
            )
          );
          if (!hasActiveOptimisticMutation) {
            return false;
          }
        }
        return true;
      });
      if (filteredEvents.length > 0) {
        this.indexes.updateIndexes(filteredEvents);
      }
      this.changes.emitEvents(filteredEvents, triggeredByUserAction);
    } else {
      if (filteredEventsBySyncStatus.length > 0) {
        this.indexes.updateIndexes(filteredEventsBySyncStatus);
      }
      this.changes.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction);
    }
  }
  /**
   * Calculate the current size based on synced data and optimistic changes
   */
  calculateSize() {
    const syncedSize = this.syncedData.size;
    const deletesFromSynced = Array.from(this.optimisticDeletes).filter(
      (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key)
    ).length;
    const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(
      (key) => !this.syncedData.has(key)
    ).length;
    return syncedSize - deletesFromSynced + upsertsNotInSynced;
  }
  /**
   * Collect events for optimistic changes
   */
  collectOptimisticChanges(previousUpserts, previousDeletes, previousRowOrigins, events) {
    const allKeys = /* @__PURE__ */ new Set([
      ...previousUpserts.keys(),
      ...this.optimisticUpserts.keys(),
      ...previousDeletes,
      ...this.optimisticDeletes
    ]);
    for (const key of allKeys) {
      const currentValue = this.get(key);
      const previousValue = this.getPreviousValue(
        key,
        previousUpserts,
        previousDeletes
      );
      const previousVirtualProps = this.getVirtualPropsSnapshotForState(key, {
        rowOrigins: previousRowOrigins,
        optimisticUpserts: previousUpserts,
        optimisticDeletes: previousDeletes
      });
      const nextVirtualProps = this.getVirtualPropsSnapshotForState(key);
      if (previousValue !== void 0 && currentValue === void 0) {
        events.push({
          type: `delete`,
          key,
          value: previousValue,
          __virtualProps: {
            value: previousVirtualProps
          }
        });
      } else if (previousValue === void 0 && currentValue !== void 0) {
        events.push({
          type: `insert`,
          key,
          value: currentValue,
          __virtualProps: {
            value: nextVirtualProps
          }
        });
      } else if (previousValue !== void 0 && currentValue !== void 0 && (!utils.deepEquals(previousValue, currentValue) || previousVirtualProps.$origin !== nextVirtualProps.$origin || previousVirtualProps.$synced !== nextVirtualProps.$synced)) {
        events.push({
          type: `update`,
          key,
          value: currentValue,
          previousValue,
          __virtualProps: {
            value: nextVirtualProps,
            previousValue: previousVirtualProps
          }
        });
      }
    }
  }
  // Optimistic mutations keep their full validated snapshot. Mixing in newer
  // synced fields could produce a row neither the user nor the server created.
  resolveOptimisticUpsert(mutation) {
    const key = mutation.key;
    const dependent = this.pendingOptimisticUpserts.get(key);
    return dependent?.insertDependency?.mutation === mutation ? dependent.modified : mutation.modified;
  }
  /**
   * Capture the optimistic layers that exist when a truncate is authored.
   * Keeping their exact owners lets application discard a later rollback
   * without reviving a failed same-key winner or retaining post-capture work.
   */
  captureTruncateOptimisticSnapshot() {
    const upserts = new Map(this.optimisticUpserts);
    const deletes = new Set(this.optimisticDeletes);
    const ownership = /* @__PURE__ */ new Map();
    const capturedKeys = /* @__PURE__ */ new Set([...upserts.keys(), ...deletes]);
    const addLayer = (key, layer) => {
      if (!capturedKeys.has(key)) return;
      const layers = ownership.get(key) ?? [];
      layers.push(layer);
      ownership.set(key, layers);
    };
    for (const key of capturedKeys) {
      const acceptedUpsert = this.pendingOptimisticUpserts.get(key);
      if (this.pendingOptimisticDeletes.has(key)) {
        addLayer(key, { type: `delete` });
      }
      if (acceptedUpsert) {
        addLayer(key, {
          type: `upsert`,
          value: this.resolveOptimisticUpsert(acceptedUpsert),
          transaction: acceptedUpsert.insertDependency?.transaction
        });
      }
    }
    for (const transaction of this.transactions.values()) {
      if ([`completed`, `failed`].includes(transaction.state)) continue;
      for (const mutation of transaction.mutations) {
        if (!mutation.optimistic || !this.isThisCollection(mutation.collection) || !capturedKeys.has(mutation.key))
          continue;
        if (mutation.type === `delete`) {
          addLayer(mutation.key, { type: `delete`, transaction });
        } else {
          addLayer(mutation.key, {
            type: `upsert`,
            value: this.resolveOptimisticUpsert(mutation),
            transaction
          });
        }
      }
    }
    return { upserts, deletes, ownership };
  }
  /** Build once per output flush; queued membership excludes optimistic edits. */
  createSyncedKeyLookup() {
    if (this.pendingSyncedTransactions.length === 0)
      return (key) => this.syncedData.has(key);
    const queued = /* @__PURE__ */ new Map();
    let truncated = false;
    for (const transaction of this.pendingSyncedTransactions) {
      if (!transaction.committed) continue;
      if (transaction.truncate) {
        queued.clear();
        truncated = true;
      }
      for (const operation of transaction.operations) {
        queued.set(operation.key, operation.type !== `delete`);
      }
    }
    return (key) => queued.get(key) ?? (!truncated && this.syncedData.has(key));
  }
  enableHydrationAuthorityTracking() {
    this.appliedAdapterDeletedKeys ??= /* @__PURE__ */ new Set();
  }
  /** A late hydration seed cannot supersede committed adapter work. */
  createAdapterAuthorityLookup() {
    const queuedKeys = /* @__PURE__ */ new Set();
    let queuedTruncate = false;
    for (const transaction of this.pendingSyncedTransactions) {
      if (!transaction.committed || transaction.preserveHydrationSeedKeys)
        continue;
      if (transaction.truncate) queuedTruncate = true;
      for (const operation of transaction.operations) {
        queuedKeys.add(operation.key);
      }
    }
    return (key) => this.hasAppliedAdapterTruncate || queuedTruncate || queuedKeys.has(key) || this.appliedAdapterDeletedKeys?.has(key) === true || this.syncedData.has(key) && !this.hydrationSeedKeys.has(key);
  }
  getProjectedSyncedKeyState(projection, key) {
    const state = projection.states.get(key);
    if (state !== void 0) return state;
    if (projection.truncated) return { exists: false, value: void 0 };
    return {
      exists: this.syncedData.has(key),
      value: this.syncedData.get(key)
    };
  }
  classifyProjectedInsert(projection, key, value) {
    const current = this.getProjectedSyncedKeyState(projection, key);
    if (!current.exists) return `insert`;
    if (current.value !== void 0 && utils.deepEquals(current.value, value) || this.hydrationSeedKeys.has(key)) {
      return `update`;
    }
    return `duplicate`;
  }
  /** Classify an adapter insert against retained and queued source state. */
  classifyPendingSyncedInsert(key, value) {
    return this.classifyProjectedInsert(
      this.pendingSyncedProjection,
      key,
      value
    );
  }
  applyPendingSyncOperation(projection, operation) {
    const key = operation.key;
    const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`;
    if (operation.type === `delete`) {
      projection.states.set(key, { exists: false, value: void 0 });
    } else if (operation.type === `update` && rowUpdateMode === `partial`) {
      const current = this.getProjectedSyncedKeyState(projection, key);
      projection.states.set(key, {
        exists: true,
        value: Object.assign({}, current.value, operation.value)
      });
    } else {
      projection.states.set(key, { exists: true, value: operation.value });
    }
  }
  /** Extend the queued projection after admitting one sync operation. */
  stagePendingSyncOperation(operation) {
    if (operation.type === `update` && operation.originalSyncType !== `insert` && this.config.sync.rowUpdateMode !== `full`) {
      operation.admittedAgainstExistingRow = this.getProjectedSyncedKeyState(
        this.pendingSyncedProjection,
        operation.key
      ).exists;
    }
    this.applyPendingSyncOperation(this.pendingSyncedProjection, operation);
  }
  /**
   * Get the previous value for a key given previous optimistic state
   */
  getPreviousValue(key, previousUpserts, previousDeletes) {
    if (previousDeletes.has(key)) {
      return void 0;
    }
    if (previousUpserts.has(key)) {
      return previousUpserts.get(key);
    }
    return this.syncedData.get(key);
  }
  rebuildAutomaticRowMetadataWrites(transaction) {
    const explicitKeys = transaction.explicitRowMetadataWriteKeys ?? /* @__PURE__ */ new Set();
    const operationKeys = new Set(
      transaction.operations.map((operation) => operation.key)
    );
    for (const key of operationKeys) {
      if (!explicitKeys.has(key)) transaction.rowMetadataWrites.delete(key);
    }
    for (const operation of transaction.operations) {
      const key = operation.key;
      if (explicitKeys.has(key)) continue;
      if (operation.type === `delete`) {
        transaction.rowMetadataWrites.set(key, { type: `delete` });
      } else if (operation.metadata !== void 0) {
        transaction.rowMetadataWrites.set(key, {
          type: `set`,
          value: operation.metadata
        });
      } else if (operation.type === `insert`) {
        transaction.rowMetadataWrites.set(key, { type: `delete` });
      }
    }
  }
  rebuildPendingSyncedProjection() {
    const projection = {
      states: /* @__PURE__ */ new Map(),
      truncated: false
    };
    const invalidCommitted = [];
    for (const transaction of this.pendingSyncedTransactions) {
      if (transaction.invalidationError !== void 0) continue;
      const wasTruncated = projection.truncated;
      const statesBeforeTruncate = transaction.truncate ? projection.states : void 0;
      const previousStates = /* @__PURE__ */ new Map();
      if (transaction.truncate) {
        projection.states = /* @__PURE__ */ new Map();
        projection.truncated = true;
      }
      let invalidKey;
      let invalidReason;
      for (const operation of transaction.operations) {
        const key = operation.key;
        if (!previousStates.has(key)) {
          previousStates.set(key, projection.states.get(key));
        }
        if (operation.originalSyncType === `insert` && operation.type !== `delete`) {
          const disposition = this.classifyProjectedInsert(
            projection,
            key,
            operation.value
          );
          if (disposition === `duplicate`) {
            invalidKey = key;
            break;
          }
          operation.type = disposition;
        }
        if (operation.admittedAgainstExistingRow === true && !this.getProjectedSyncedKeyState(projection, key).exists) {
          invalidKey = key;
          invalidReason = new errors.SyncTransactionAbortedError();
          break;
        }
        this.applyPendingSyncOperation(projection, operation);
      }
      if (invalidKey !== void 0) {
        if (statesBeforeTruncate !== void 0) {
          projection.states = statesBeforeTruncate;
        } else {
          for (const [key, previous] of previousStates) {
            if (previous === void 0) projection.states.delete(key);
            else projection.states.set(key, previous);
          }
        }
        projection.truncated = wasTruncated;
        const error = invalidReason ?? transaction.duplicateKeyError?.(invalidKey) ?? new errors.SyncTransactionAbortedError();
        if (transaction.committed) {
          invalidCommitted.push({ transaction, key: invalidKey, error });
          continue;
        }
        transaction.invalidationError = error;
        continue;
      }
      this.rebuildAutomaticRowMetadataWrites(transaction);
    }
    this.pendingSyncedProjection = projection;
    return invalidCommitted;
  }
  rejectInvalidCommittedTransactions(invalid) {
    const rejected = new Set(invalid.map(({ transaction }) => transaction));
    this.pendingSyncedTransactions = this.pendingSyncedTransactions.filter(
      (transaction) => !rejected.has(transaction)
    );
    const canceledKeys = /* @__PURE__ */ new Set();
    for (const { transaction, error } of invalid) {
      for (const operation of transaction.operations) {
        canceledKeys.add(operation.key);
      }
      transaction.applied.reject(error);
    }
    return canceledKeys;
  }
  /** Rebuild after truncate or application changes queue history. */
  refreshPendingSyncedProjection() {
    const invalid = this.rebuildPendingSyncedProjection();
    if (invalid.length === 0) return;
    this.rejectInvalidCommittedTransactions(invalid);
    throw invalid[0].error;
  }
  commitNextPendingTransactionBatch() {
    const syncRunGeneration = this.syncRunGeneration;
    let hasPersistingTransaction = false;
    for (const transaction of this.transactions.values()) {
      if (transaction.state === `persisting`) {
        hasPersistingTransaction = true;
        break;
      }
    }
    const {
      committedSyncedTransactions,
      uncommittedSyncedTransactions,
      hasTruncateSync,
      hasImmediateSync,
      layoutChanged
    } = this.pendingSyncedTransactions.reduce(
      (acc, t) => {
        if (t.committed) {
          acc.committedSyncedTransactions.push(t);
          acc.layoutChanged ||= t.layoutChanged;
          if (t.truncate) {
            acc.hasTruncateSync = true;
          }
          if (t.immediate) {
            acc.hasImmediateSync = true;
          }
        } else {
          acc.uncommittedSyncedTransactions.push(t);
        }
        return acc;
      },
      {
        committedSyncedTransactions: [],
        uncommittedSyncedTransactions: [],
        hasTruncateSync: false,
        hasImmediateSync: false,
        layoutChanged: false
      }
    );
    if (committedSyncedTransactions.length === 0) {
      return { processed: false };
    }
    if (!hasPersistingTransaction || hasTruncateSync || hasImmediateSync) {
      const previousLayout = layoutChanged ? [...this.keys()] : void 0;
      this.pendingSyncedTransactions = uncommittedSyncedTransactions;
      const deferOrder = committedSyncedTransactions.reduce(
        (count, transaction) => count + transaction.operations.length,
        0
      ) >= 512;
      for (const transaction of committedSyncedTransactions) {
        transaction.applicationStarted = true;
      }
      this.isCommittingSyncTransactions = true;
      const truncateOptimisticSnapshot = hasTruncateSync ? committedSyncedTransactions.find((t) => t.truncate)?.optimisticSnapshot : null;
      const retainedTruncateLayer = (key) => {
        const layers = truncateOptimisticSnapshot?.ownership.get(key) ?? [];
        for (let index = layers.length - 1; index >= 0; index--) {
          const layer = layers[index];
          if (layer.transaction === void 0 || layer.transaction.state !== `failed`) {
            return layer;
          }
        }
        return void 0;
      };
      let truncatePendingLocalChanges;
      let truncatePendingLocalOrigins;
      const changedKeys = /* @__PURE__ */ new Set();
      const syncedInsertedOrUpdatedKeys = /* @__PURE__ */ new Set();
      const firstSyncOperations = /* @__PURE__ */ new Map();
      for (const transaction of committedSyncedTransactions) {
        for (const operation of transaction.operations) {
          const key = operation.key;
          changedKeys.add(key);
          if (!firstSyncOperations.has(key))
            firstSyncOperations.set(key, operation);
          if (operation.type !== `delete`) syncedInsertedOrUpdatedKeys.add(key);
        }
        for (const [key] of transaction.rowMetadataWrites) {
          changedKeys.add(key);
        }
      }
      const virtualSnapshotKeys = new Set(changedKeys);
      for (const key of this.pendingOptimisticDirectUpserts) {
        virtualSnapshotKeys.add(key);
      }
      for (const key of this.pendingOptimisticDirectDeletes) {
        virtualSnapshotKeys.add(key);
      }
      const previousRowOrigins = this.snapshotRowOriginsForKeys(virtualSnapshotKeys);
      const previousOptimisticUpserts = new Map(this.optimisticUpserts);
      const previousOptimisticDeletes = new Set(this.optimisticDeletes);
      const completedDirectUpserts = new Set(
        this.pendingOptimisticDirectUpserts
      );
      const completedDirectDeletes = new Set(
        this.pendingOptimisticDirectDeletes
      );
      let currentVisibleState = this.preSyncVisibleState;
      if (currentVisibleState.size === 0) {
        currentVisibleState = /* @__PURE__ */ new Map();
        for (const key of changedKeys) {
          const currentValue = this.get(key);
          if (currentValue !== void 0) {
            currentVisibleState.set(key, currentValue);
          }
        }
      }
      const events = [];
      if (hasTruncateSync) {
        for (const [key, value] of this.entries()) {
          events.push({
            type: `delete`,
            key,
            value: this.enrichWithVirtualPropsSnapshot(
              value,
              this.getVirtualPropsSnapshotForState(key)
            )
          });
        }
      }
      const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`;
      const completedOptimisticOps = /* @__PURE__ */ new Map();
      for (const transaction of this.transactions.values()) {
        if (transaction.state === `completed`) {
          for (const mutation of transaction.mutations) {
            if (this.isThisCollection(mutation.collection)) {
              if (mutation.optimistic) {
                completedOptimisticOps.set(mutation.key, {
                  type: mutation.type,
                  value: mutation.modified
                });
              }
            }
          }
        }
      }
      for (const transaction of committedSyncedTransactions) {
        if (transaction.truncate) {
          truncatePendingLocalChanges = new Set(this.pendingLocalChanges);
          truncatePendingLocalOrigins = new Set(this.pendingLocalOrigins);
          this.syncedData.clear();
          this.syncedMetadata.clear();
          this.hydrationSeedKeys.clear();
          this.hydratedKeys.clear();
          if (!transaction.preserveHydrationSeedKeys && this.appliedAdapterDeletedKeys) {
            this.hasAppliedAdapterTruncate = true;
            this.appliedAdapterDeletedKeys.clear();
          }
          this.clearOriginTrackingState();
          for (const key of changedKeys) {
            currentVisibleState.delete(key);
          }
          this._events.emit(`truncate`, {
            type: `truncate`,
            collection: this.collection
          });
        }
        const localKeys = /* @__PURE__ */ new Set();
        for (const operation of transaction.operations) {
          const key = operation.key;
          const retainedLocalOrigin = truncatePendingLocalChanges?.has(key) === true || truncatePendingLocalOrigins?.has(key) === true && !completedDirectUpserts.has(key) && !completedDirectDeletes.has(key);
          const origin = this.isLocalOnly || this.pendingLocalChanges.has(key) || this.pendingLocalOrigins.has(key) || localKeys.has(key) || retainedLocalOrigin ? "local" : "remote";
          if (origin === `local`) localKeys.add(key);
          if (operation.type !== `delete`)
            this.virtualPropsCache.delete(operation.value);
          switch (operation.type) {
            case `insert`:
              this.syncedData.set(key, operation.value, deferOrder);
              this.rowOrigins.set(key, origin);
              this.pendingLocalChanges.delete(key);
              this.pendingLocalOrigins.delete(key);
              this.pendingOptimisticUpserts.delete(key);
              this.pendingOptimisticDeletes.delete(key);
              this.pendingOptimisticDirectUpserts.delete(key);
              this.pendingOptimisticDirectDeletes.delete(key);
              break;
            case `update`: {
              if (rowUpdateMode === `partial`) {
                const updatedValue = Object.assign(
                  {},
                  this.syncedData.get(key),
                  operation.value
                );
                this.syncedData.set(key, updatedValue, deferOrder);
              } else {
                this.syncedData.set(key, operation.value, deferOrder);
              }
              this.rowOrigins.set(key, origin);
              this.pendingLocalChanges.delete(key);
              this.pendingLocalOrigins.delete(key);
              this.pendingOptimisticUpserts.delete(key);
              this.pendingOptimisticDeletes.delete(key);
              this.pendingOptimisticDirectUpserts.delete(key);
              this.pendingOptimisticDirectDeletes.delete(key);
              break;
            }
            case `delete`:
              this.syncedData.delete(key, deferOrder);
              this.syncedMetadata.delete(key);
              this.rowOrigins.delete(key);
              this.pendingLocalChanges.delete(key);
              this.pendingLocalOrigins.delete(key);
              this.pendingOptimisticUpserts.delete(key);
              this.pendingOptimisticDeletes.delete(key);
              this.pendingOptimisticDirectUpserts.delete(key);
              this.pendingOptimisticDirectDeletes.delete(key);
              break;
          }
          if (!transaction.preserveHydrationSeedKeys) {
            this.hydrationSeedKeys.delete(key);
            this.hydratedKeys.delete(key);
            if (operation.type === `delete`)
              this.appliedAdapterDeletedKeys?.add(key);
            else this.appliedAdapterDeletedKeys?.delete(key);
          }
        }
        for (const [key, metadataWrite] of transaction.rowMetadataWrites) {
          if (metadataWrite.type === `delete`) {
            this.syncedMetadata.delete(key);
            continue;
          }
          this.syncedMetadata.set(key, metadataWrite.value);
        }
        for (const [
          key,
          metadataWrite
        ] of transaction.collectionMetadataWrites) {
          if (metadataWrite.type === `delete`) {
            this.syncedCollectionMetadata.delete(key);
            continue;
          }
          this.syncedCollectionMetadata.set(key, metadataWrite.value);
        }
      }
      this.syncedData.restoreOrder();
      this.refreshPendingSyncedProjection();
      for (const key of this.pendingOptimisticDirectUpserts) {
        if (hasTruncateSync && this.pendingOptimisticUpserts.has(key) && (truncateOptimisticSnapshot?.upserts.has(key) === true || truncateOptimisticSnapshot?.deletes.has(key) === true) && !changedKeys.has(key))
          continue;
        if (!changedKeys.has(key)) {
          changedKeys.add(key);
          if (!hasTruncateSync && !currentVisibleState.has(key)) {
            const previousValue = previousOptimisticUpserts.get(key);
            if (previousValue !== void 0) {
              currentVisibleState.set(key, previousValue);
            }
          }
          this.pendingOptimisticUpserts.delete(key);
          this.pendingLocalOrigins.delete(key);
        }
        this.pendingOptimisticDirectUpserts.delete(key);
      }
      for (const key of this.pendingOptimisticDirectDeletes) {
        if (hasTruncateSync && (truncateOptimisticSnapshot?.deletes.has(key) || truncateOptimisticSnapshot?.ownership.get(key)?.some(
          (layer) => layer.type === `delete` && !layer.transaction
        )) && !changedKeys.has(key))
          continue;
        if (!changedKeys.has(key)) {
          changedKeys.add(key);
        }
        this.pendingOptimisticDeletes.delete(key);
        this.pendingLocalOrigins.delete(key);
        this.pendingOptimisticDirectDeletes.delete(key);
      }
      this.optimisticUpserts.clear();
      this.optimisticDeletes.clear();
      this.isCommittingSyncTransactions = false;
      if (hasTruncateSync && truncateOptimisticSnapshot) {
        for (const key of truncateOptimisticSnapshot.ownership.keys()) {
          const layer = retainedTruncateLayer(key);
          if (!layer) continue;
          if (layer.type === `upsert`) {
            if (completedDirectUpserts.has(key) && changedKeys.has(key))
              continue;
            this.optimisticUpserts.set(key, layer.value);
          } else {
            if (completedDirectDeletes.has(key) && changedKeys.has(key))
              continue;
            this.optimisticDeletes.add(key);
          }
        }
      }
      for (const transaction of this.transactions.values()) {
        if (![`completed`, `failed`].includes(transaction.state)) {
          for (const mutation of transaction.mutations) {
            if (this.isThisCollection(mutation.collection))
              this.pendingLocalChanges.add(mutation.key);
            if (this.isThisCollection(mutation.collection) && mutation.type === `insert` && syncedInsertedOrUpdatedKeys.has(mutation.key)) {
              this.acknowledgedInserts.add(mutation);
            }
            if (this.isThisCollection(mutation.collection) && mutation.optimistic) {
              switch (mutation.type) {
                case `insert`:
                case `update`:
                  this.optimisticUpserts.set(
                    mutation.key,
                    this.resolveOptimisticUpsert(mutation)
                  );
                  this.optimisticDeletes.delete(mutation.key);
                  break;
                case `delete`:
                  this.optimisticUpserts.delete(mutation.key);
                  this.optimisticDeletes.add(mutation.key);
                  break;
              }
            }
          }
        }
      }
      if (hasTruncateSync) {
        const reapplyUpserts = this.optimisticUpserts;
        const reapplyDeletes = this.optimisticDeletes;
        for (const [key, value] of reapplyUpserts) {
          if (reapplyDeletes.has(key)) continue;
          if (syncedInsertedOrUpdatedKeys.has(key)) {
            let foundInsert = false;
            for (let i = events.length - 1; i >= 0; i--) {
              const evt = events[i];
              if (evt.key === key && evt.type === `insert`) {
                evt.value = value;
                foundInsert = true;
                break;
              }
            }
            if (!foundInsert) {
              events.push({ type: `insert`, key, value });
            }
          } else {
            events.push({ type: `insert`, key, value });
          }
        }
        if (events.length > 0 && reapplyDeletes.size > 0) {
          const filtered = [];
          for (const evt of events) {
            if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {
              continue;
            }
            filtered.push(evt);
          }
          events.length = 0;
          events.push(...filtered);
        }
        if (this.lifecycle.status !== `ready`) {
          this.lifecycle.markReady();
        }
      }
      for (const key of changedKeys) {
        const firstSyncOperation = firstSyncOperations.get(key);
        const syncPreviousValue = firstSyncOperation?.type === `update` && currentVisibleState.get(key) === firstSyncOperation.value ? firstSyncOperation.previousValue : void 0;
        const previousVisibleValue = !hasTruncateSync && !previousOptimisticUpserts.has(key) && !previousOptimisticDeletes.has(key) && syncPreviousValue !== void 0 ? syncPreviousValue : currentVisibleState.get(key);
        const newVisibleValue = this.get(key);
        const previousVirtualProps = this.preSyncVirtualState.get(key) ?? this.getVirtualPropsSnapshotForState(key, {
          rowOrigins: previousRowOrigins,
          optimisticUpserts: previousOptimisticUpserts,
          optimisticDeletes: previousOptimisticDeletes,
          completedOptimisticKeys: completedOptimisticOps
        });
        const nextVirtualProps = this.getVirtualPropsSnapshotForState(key);
        const virtualChanged = previousVirtualProps.$synced !== nextVirtualProps.$synced || previousVirtualProps.$origin !== nextVirtualProps.$origin;
        const previousValueWithVirtual = previousVisibleValue !== void 0 ? virtualProps.enrichRowWithVirtualProps(
          previousVisibleValue,
          key,
          this.collection.id,
          () => previousVirtualProps.$synced,
          () => previousVirtualProps.$origin
        ) : void 0;
        const shouldEmitVirtualUpdate = virtualChanged && previousVisibleValue !== void 0 && newVisibleValue !== void 0 && utils.deepEquals(previousVisibleValue, newVisibleValue);
        if (previousVisibleValue === void 0 && newVisibleValue !== void 0) {
          const completedOptimisticOp = completedOptimisticOps.get(key);
          if (completedOptimisticOp) {
            const previousValueFromCompleted = completedOptimisticOp.value;
            const previousValueWithVirtualFromCompleted = virtualProps.enrichRowWithVirtualProps(
              previousValueFromCompleted,
              key,
              this.collection.id,
              () => previousVirtualProps.$synced,
              () => previousVirtualProps.$origin
            );
            events.push({
              type: `update`,
              key,
              value: newVisibleValue,
              previousValue: previousValueWithVirtualFromCompleted
            });
          } else {
            events.push({
              type: `insert`,
              key,
              value: newVisibleValue
            });
          }
        } else if (previousVisibleValue !== void 0 && newVisibleValue === void 0) {
          events.push({
            type: `delete`,
            key,
            value: previousValueWithVirtual ?? previousVisibleValue
          });
        } else if (previousVisibleValue !== void 0 && newVisibleValue !== void 0 && (!utils.deepEquals(previousVisibleValue, newVisibleValue) || shouldEmitVirtualUpdate)) {
          events.push({
            type: `update`,
            key,
            value: newVisibleValue,
            previousValue: previousValueWithVirtual ?? previousVisibleValue
          });
        }
      }
      this.size = this.calculateSize();
      if (events.length > 0) {
        this.indexes.updateIndexes(events);
      }
      let failure;
      try {
        const visibleLayoutChanged = previousLayout !== void 0 && (previousLayout.length !== this.size || [...this.keys()].some(
          (key, index) => key !== previousLayout[index]
        ));
        this.changes.emitEvents(events, true, visibleLayoutChanged);
      } catch (error) {
        failure = { error };
      }
      if (this.syncRunGeneration === syncRunGeneration) {
        this.preSyncVisibleState.clear();
        this.preSyncVirtualState.clear();
        Promise.resolve().then(() => {
          if (this.syncRunGeneration === syncRunGeneration) {
            this.recentlySyncedKeys.clear();
          }
        });
        if (!this.hasReceivedFirstCommit) this.hasReceivedFirstCommit = true;
      }
      for (const transaction of committedSyncedTransactions) {
        transaction.applied.resolve();
      }
      return { processed: true, failure };
    }
    return { processed: false };
  }
  /** Abandons a queued transaction and invalidated dependents before visibility. */
  cancelPendingSyncedTransaction(transaction, reason = new errors.SyncTransactionAbortedError()) {
    if (transaction.applicationStarted) return;
    const index = this.pendingSyncedTransactions.indexOf(transaction);
    if (index === -1) return;
    const canceledKeys = /* @__PURE__ */ new Set();
    const cancel = (pending, error) => {
      const pendingIndex = this.pendingSyncedTransactions.indexOf(pending);
      if (pendingIndex === -1 || pending.applicationStarted) return;
      this.pendingSyncedTransactions.splice(pendingIndex, 1);
      for (const operation of pending.operations) {
        canceledKeys.add(operation.key);
      }
      pending.applied.reject(error);
    };
    cancel(transaction, reason);
    const invalid = this.rebuildPendingSyncedProjection();
    for (const key of this.rejectInvalidCommittedTransactions(invalid)) {
      canceledKeys.add(key);
    }
    const remainingPendingKeys = /* @__PURE__ */ new Set();
    for (const pending of this.pendingSyncedTransactions) {
      if (pending.invalidationError !== void 0) continue;
      for (const operation of pending.operations) {
        remainingPendingKeys.add(operation.key);
      }
    }
    for (const key of canceledKeys) {
      if (!remainingPendingKeys.has(key)) {
        this.recentlySyncedKeys.delete(key);
        this.preSyncVisibleState.delete(key);
        this.preSyncVirtualState.delete(key);
      }
    }
    if (this.pendingSyncedTransactions.length === 0) {
      this.preSyncVisibleState.clear();
      this.preSyncVirtualState.clear();
      this.recentlySyncedKeys.clear();
      this.changes.emitEvents([], true);
    } else {
      this.recomputeOptimisticState(false);
    }
  }
  /**
   * Schedule cleanup of a transaction when it completes
   */
  scheduleTransactionCleanup(transaction) {
    if (transaction.state === `completed`) {
      this.transactions.delete(transaction.id);
      return;
    }
    transaction.isPersisted.promise.then(() => {
      this.transactions.delete(transaction.id);
    }).catch(() => {
    });
  }
  /**
   * Capture visible state for keys that will be affected by pending sync operations
   * This must be called BEFORE onTransactionStateChange clears optimistic state
   */
  capturePreSyncVisibleState() {
    if (this.pendingSyncedTransactions.length === 0) return;
    const syncedKeys = /* @__PURE__ */ new Set();
    for (const transaction of this.pendingSyncedTransactions) {
      for (const operation of transaction.operations) {
        syncedKeys.add(operation.key);
      }
    }
    for (const key of syncedKeys) {
      this.recentlySyncedKeys.add(key);
    }
    for (const key of syncedKeys) {
      if (!this.preSyncVisibleState.has(key)) {
        const currentValue = this.get(key);
        this.preSyncVisibleState.set(key, currentValue);
        if (currentValue !== void 0) {
          this.preSyncVirtualState.set(
            key,
            this.getVirtualPropsSnapshotForState(key)
          );
        }
      }
    }
  }
  /**
   * Trigger a recomputation when transactions change
   * This method should be called by the Transaction class when state changes
   */
  onTransactionStateChange() {
    const hasPersistingTransaction = [...this.transactions.values()].some(
      (transaction) => transaction.state === `persisting`
    );
    this.changes.shouldBatchEvents = this.pendingSyncedTransactions.some(
      (transaction) => transaction.committed && (!hasPersistingTransaction || transaction.immediate || transaction.truncate)
    );
    if (this.changes.shouldBatchEvents) this.capturePreSyncVisibleState();
    this.recomputeOptimisticState(false);
  }
  /**
   * Clean up the collection by stopping sync and clearing data
   * This can be called manually or automatically by garbage collection
   */
  cleanup() {
    this.syncRunGeneration++;
    for (const transaction of this.pendingSyncedTransactions) {
      transaction.applied.reject(new errors.SyncTransactionAbortedError());
    }
    this.syncedData.clear();
    this.syncedMetadata.clear();
    this.syncedCollectionMetadata.clear();
    this.optimisticUpserts.clear();
    this.optimisticDeletes.clear();
    this.pendingOptimisticUpserts.clear();
    this.pendingOptimisticDeletes.clear();
    this.pendingOptimisticDirectUpserts.clear();
    this.pendingOptimisticDirectDeletes.clear();
    this.hydrationSeedKeys.clear();
    this.hydratedKeys.clear();
    this.appliedAdapterDeletedKeys?.clear();
    this.hasAppliedAdapterTruncate = false;
    this.clearOriginTrackingState();
    this.isLocalOnly = false;
    this.size = 0;
    this.pendingSyncedTransactions = [];
    this.pendingSyncedProjection = { states: /* @__PURE__ */ new Map(), truncated: false };
    this.preSyncVisibleState.clear();
    this.preSyncVirtualState.clear();
    this.recentlySyncedKeys.clear();
    this.hasReceivedFirstCommit = false;
  }
}
exports.CollectionStateManager = CollectionStateManager;
//# sourceMappingURL=state.cjs.map