@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 20 kB
Source Map (JSON)
{"version":3,"file":"changes.cjs","sources":["../../../src/collection/changes.ts"],"sourcesContent":["import { NegativeActiveSubscribersError } from '../errors'\nimport { recordPublicationError, withPublicationContext } from '../scheduler.js'\nimport { runAllCallbacks } from '../utils/callbacks.js'\nimport {\n createSingleRowRefProxy,\n toExpression,\n} from '../query/builder/ref-proxy.js'\nimport { CollectionSubscription } from './subscription.js'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type { ChangeMessage, SubscribeChangesOptions } from '../types'\nimport type { CollectionLifecycleManager } from './lifecycle.js'\nimport type { CollectionSyncManager } from './sync.js'\nimport type { CollectionEventsManager } from './events.js'\nimport type { CollectionImpl } from './index.js'\nimport type { CollectionStateManager } from './state.js'\nimport type { WithVirtualProps } from '../virtual-props.js'\n\nexport type PublicationDeferral = {\n publish: () => void\n discard: () => void\n}\n\nexport class CollectionChangesManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private sync!: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private events!: CollectionEventsManager\n private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n\n public activeSubscribersCount = 0\n public changeSubscriptions = new Set<CollectionSubscription>()\n public batchedEvents: Array<ChangeMessage<TOutput, TKey>> = []\n public shouldBatchEvents = false\n private deferral:\n | {\n depth: number\n discard: boolean\n stateRevision: number\n layoutRevision: number\n publications: Array<{\n changes: Array<ChangeMessage<TOutput, TKey>>\n layoutChanged: boolean\n }>\n }\n | undefined\n private layoutChangeListeners = new Set<() => void>()\n\n /**\n * Monotonic revision of the collection's visible state, advanced once per\n * committed batch of changes and cleanup — including while nothing is subscribed.\n * Lets consumers (the live-query observer) cheaply detect \"did the data\n * change\" without subscribing, and stays untouched by subscription\n * bootstrap replays, which do not go through emitEvents.\n */\n public stateRevision = 0\n\n /**\n * Monotonic revision advanced only for explicit layout-only publications.\n * Observers use it to detect reordered rows whose values did not change.\n */\n public layoutRevision = 0\n\n /**\n * Creates a new CollectionChangesManager instance\n */\n constructor() {}\n\n public setDeps(deps: {\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n }) {\n this.lifecycle = deps.lifecycle\n this.sync = deps.sync\n this.events = deps.events\n this.collection = deps.collection\n this.state = deps.state\n }\n\n /**\n * Emit an empty ready event to notify subscribers that the collection is ready\n * This bypasses the normal empty array check in emitEvents\n */\n public emitEmptyReadyEvent(): void {\n withPublicationContext(() => {\n try {\n runAllCallbacks(\n [...this.changeSubscriptions].map(\n (subscription) => () => subscription.emitEvents([]),\n ),\n )\n } catch (error) {\n recordPublicationError(error)\n }\n })\n }\n\n /**\n * Enriches a change message with virtual properties ($synced, $origin, $key, $collectionId).\n * Uses the \"add-if-missing\" pattern to preserve virtual properties from upstream collections.\n */\n private enrichChangeWithVirtualProps(\n change: ChangeMessage<TOutput, TKey>,\n ): ChangeMessage<WithVirtualProps<TOutput, TKey>, TKey> {\n return this.state.enrichChangeMessage(change)\n }\n\n // Reduce unpublished same-key changes relative to the subscriber's last\n // visible row: keep the earliest previous row and latest value, and cancel\n // an insert followed by a delete.\n private composeBatchedChange(\n pending: ChangeMessage<TOutput, TKey> | undefined,\n change: ChangeMessage<TOutput, TKey>,\n ): ChangeMessage<TOutput, TKey> | undefined {\n if (!pending) return change\n\n if (pending.type === `insert`) {\n if (change.type === `delete`) return undefined\n return { ...change, type: `insert`, previousValue: undefined }\n }\n\n const previousValue =\n pending.type === `update`\n ? (pending.previousValue ?? pending.value)\n : pending.value\n\n if (change.type === `delete`) {\n return { ...change, value: previousValue, previousValue: undefined }\n }\n\n return { ...change, type: `update`, previousValue }\n }\n\n /**\n * Emit events either immediately or batch them for later emission\n */\n public emitEvents(\n changes: Array<ChangeMessage<TOutput, TKey>>,\n forceEmit = false,\n layoutChanged = false,\n ): void {\n // The visible state was already committed by the caller, so the revision\n // advances even when the events below end up batched for later emission.\n if (changes.length > 0) this.stateRevision++\n if (layoutChanged) this.layoutRevision++\n\n // Skip batching for user actions (forceEmit=true) to keep UI responsive\n if (this.shouldBatchEvents && !forceEmit) {\n // Snapshot virtual properties before later state changes can replace them.\n this.batchedEvents.push(\n ...changes.map((change) => this.enrichChangeWithVirtualProps(change)),\n )\n return\n }\n\n // Either we're not batching, or we're forcing emission (user action or ending batch cycle)\n let rawEvents = changes\n\n if (forceEmit) {\n // Force emit is used to end a batch (e.g. after a sync commit). Combine any\n // buffered optimistic events with the final changes so subscribers see the\n // whole picture, even if the sync diff is empty.\n if (this.batchedEvents.length > 0) {\n // Undefined tombstones retain each key's first-seen publication order\n // when an insert/delete pair cancels before a later change revives it.\n const combined = new Map<\n TKey,\n ChangeMessage<TOutput, TKey> | undefined\n >()\n for (const change of [...this.batchedEvents, ...changes]) {\n combined.set(\n change.key,\n this.composeBatchedChange(combined.get(change.key), change),\n )\n }\n rawEvents = [...combined.values()].flatMap((change) => {\n return change ? [change] : []\n })\n }\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n\n if (this.deferral) {\n this.deferral.publications.push({ changes: rawEvents, layoutChanged })\n return\n }\n\n this.publishEvents(rawEvents, layoutChanged)\n }\n\n /**\n * Defers subscriber delivery while a coherent multi-Collection publication\n * installs all of its visible state. State and indexes still commit at their\n * normal transaction boundaries.\n */\n public deferPublication(): PublicationDeferral {\n const deferral = (this.deferral ??= {\n depth: 0,\n discard: false,\n stateRevision: this.stateRevision,\n layoutRevision: this.layoutRevision,\n publications: [],\n })\n deferral.depth++\n let closed = false\n\n const close = (discard: boolean) => {\n // Cleanup can retire this handle while a later sync run owns a deferral.\n if (closed || this.deferral !== deferral) return\n closed = true\n deferral.discard ||= discard\n\n if (--deferral.depth > 0) return\n\n const publications = deferral.publications\n deferral.publications = []\n this.deferral = undefined\n if (deferral.discard) {\n this.stateRevision = deferral.stateRevision\n this.layoutRevision = deferral.layoutRevision\n return\n }\n this.publishEvents(\n publications.flatMap(({ changes }) => changes),\n publications.some(({ layoutChanged }) => layoutChanged),\n )\n }\n\n return {\n publish: () => close(false),\n discard: () => close(true),\n }\n }\n\n private publishEvents(\n rawEvents: Array<ChangeMessage<TOutput, TKey>>,\n layoutChanged: boolean,\n ): void {\n if (rawEvents.length === 0 && !layoutChanged) {\n return\n }\n\n // Enrich all change messages with virtual properties\n // This uses the \"add-if-missing\" pattern to preserve pass-through semantics\n const enrichedEvents: Array<\n ChangeMessage<WithVirtualProps<TOutput, TKey>, TKey>\n > = rawEvents.map((change) => this.enrichChangeWithVirtualProps(change))\n\n // Every subscriber sees one committed source batch before dependent query\n // graphs run. This keeps repeated aliases and sibling subqueries coherent.\n const layoutListeners = [...this.layoutChangeListeners]\n const subscriptions = [...this.changeSubscriptions]\n withPublicationContext(() => {\n const callbacks: Array<() => void> = subscriptions.map(\n (subscription) => () => subscription.emitEvents(enrichedEvents),\n )\n if (rawEvents.length === 0) {\n callbacks.unshift(...layoutListeners)\n }\n try {\n runAllCallbacks(callbacks)\n } catch (error) {\n recordPublicationError(error)\n }\n })\n }\n\n /** Subscribe to layout-only publications. Internal observer channel. */\n public subscribeLayoutChanges(listener: () => void): () => void {\n this.layoutChangeListeners.add(listener)\n return () => this.layoutChangeListeners.delete(listener)\n }\n\n /**\n * Subscribe to changes in the collection\n */\n public subscribeChanges(\n callback: (\n changes: Array<ChangeMessage<WithVirtualProps<TOutput, TKey>, TKey>>,\n ) => void,\n options: SubscribeChangesOptions<TOutput, TKey> = {},\n ): CollectionSubscription {\n // Compile where callback to whereExpression if provided\n if (options.where && options.whereExpression) {\n throw new Error(\n `Cannot specify both 'where' and 'whereExpression' options. Use one or the other.`,\n )\n }\n\n const { where, ...opts } = options\n let whereExpression = opts.whereExpression\n if (where) {\n const proxy = createSingleRowRefProxy<\n WithVirtualProps<TOutput, TKey>,\n TKey\n >()\n const result = where(proxy)\n whereExpression = toExpression(result)\n }\n\n // Acquire ownership only after all fallible option validation and\n // user-provided predicate compilation has completed.\n this.addSubscriber()\n\n let subscription: CollectionSubscription | undefined\n const setupState = { closed: false }\n try {\n subscription = new CollectionSubscription(this.collection, callback, {\n ...opts,\n whereExpression,\n onUnsubscribe: () => {\n setupState.closed = true\n this.removeSubscriber()\n if (subscription) this.changeSubscriptions.delete(subscription)\n },\n })\n\n // Register status listener BEFORE requesting snapshot to avoid race condition.\n // This ensures the listener catches all status transitions, even if the\n // loadSubset promise resolves synchronously or very quickly.\n if (options.onStatusChange) {\n subscription.on(`status:change`, options.onStatusChange)\n }\n\n if (options.includeInitialState) {\n subscription.requestSnapshot({\n trackLoadSubsetPromise: false,\n orderBy: options.orderBy,\n limit: options.limit,\n onLoadSubsetResult: options.onLoadSubsetResult,\n })\n } else if (options.includeInitialState === false) {\n // When explicitly set to false (not just undefined), mark all state as \"seen\"\n // so that all future changes (including deletes) pass through unfiltered.\n subscription.markAllStateAsSeen()\n }\n\n // Add to batched listeners\n if (!setupState.closed) this.changeSubscriptions.add(subscription)\n } catch (error) {\n if (subscription) {\n try {\n subscription.unsubscribe()\n } catch {\n // Preserve the setup error. Cleanup still releases subscriber\n // ownership and attempts every subset unload before it throws.\n }\n } else {\n this.removeSubscriber()\n }\n throw error\n }\n\n return subscription\n }\n\n /**\n * Increment the active subscribers count and start sync if needed\n */\n private addSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount++\n this.lifecycle.cancelGCTimer()\n\n try {\n // Start sync if collection was cleaned up\n if (\n this.lifecycle.status === `cleaned-up` ||\n this.lifecycle.status === `idle`\n ) {\n this.sync.startSync()\n }\n } catch (error) {\n this.activeSubscribersCount = previousSubscriberCount\n if (this.activeSubscribersCount === 0) {\n this.lifecycle.startGCTimer()\n }\n throw error\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount,\n )\n }\n\n /**\n * Decrement the active subscribers count and start GC timer if needed\n */\n private removeSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount--\n\n if (this.activeSubscribersCount === 0) {\n this.lifecycle.startGCTimer()\n } else if (this.activeSubscribersCount < 0) {\n throw new NegativeActiveSubscribersError()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount,\n )\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n // Cleanup clears visible state without publishing row changes. Detached\n // consumers may miss every status transition before an empty restart.\n this.stateRevision++\n this.batchedEvents = []\n this.shouldBatchEvents = false\n if (this.deferral) this.deferral.publications.length = 0\n this.deferral = undefined\n }\n}\n"],"names":["withPublicationContext","runAllCallbacks","subscription","recordPublicationError","callbacks","createSingleRowRefProxy","toExpression","CollectionSubscription","NegativeActiveSubscribersError"],"mappings":"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"}