UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines • 24.4 kB
{"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 { readRouteValue } from './change-events.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 ($hasPendingWrites, $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 // An empty batch signals readiness to every subscriber.\n const routed =\n rawEvents.length > 0\n ? routeChanges(enrichedEvents, subscriptions)\n : undefined\n const callbacks: Array<() => void> = []\n for (const subscription of subscriptions) {\n const own = routed?.get(subscription)\n callbacks.push(() => {\n // An earlier callback in this publication can end routing for this\n // subscription, for example by leaving stale rows to reconcile.\n if (own === undefined || !subscription.changeRoute) {\n subscription.emitEvents(enrichedEvents)\n } else if (own.length > 0) {\n // A routed subscription with no candidate change cannot publish.\n subscription.emitEvents(own)\n }\n })\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\n/**\n * Gives each routed subscription only the changes whose value or previous\n * value holds its route literal, in batch order. Subscriptions without a\n * route are absent from the result and receive the whole batch; with no\n * routed subscription the result is undefined.\n */\nfunction routeChanges<T extends object, TKey extends string | number>(\n changes: Array<ChangeMessage<T, TKey>>,\n subscriptions: Array<CollectionSubscription>,\n): Map<CollectionSubscription, Array<ChangeMessage<T, TKey>>> | undefined {\n const routes = subscriptions.map((subscription) => subscription.changeRoute)\n if (routes.every((route) => route === undefined)) return undefined\n const routed = new Map<\n CollectionSubscription,\n Array<ChangeMessage<T, TKey>>\n >()\n const groups = new Map<\n string,\n {\n path: Array<string>\n byLiteral: Map<unknown, Array<CollectionSubscription>>\n }\n >()\n for (const [index, subscription] of subscriptions.entries()) {\n const route = routes[index]\n if (!route) continue\n routed.set(subscription, [])\n let group = groups.get(route.pathKey)\n if (!group) {\n group = { path: route.path, byLiteral: new Map() }\n groups.set(route.pathKey, group)\n }\n const peers = group.byLiteral.get(route.expected)\n if (peers) peers.push(subscription)\n else group.byLiteral.set(route.expected, [subscription])\n }\n\n const deliver = (\n targets: Array<CollectionSubscription> | undefined,\n change: ChangeMessage<T, TKey>,\n ) => {\n for (const subscription of targets ?? []) {\n routed.get(subscription)!.push(change)\n }\n }\n for (const change of changes) {\n for (const group of groups.values()) {\n const value = readRouteValue(change.value, group.path)\n const previous =\n change.previousValue === undefined\n ? undefined\n : readRouteValue(change.previousValue, group.path)\n // The where filter reads these same values, and a read that throws makes\n // its predicate false, so an unreadable value matches no literal here.\n deliver(group.byLiteral.get(value), change)\n if (previous !== value) deliver(group.byLiteral.get(previous), change)\n }\n }\n return routed\n}\n"],"names":["layoutChangeListeners","withPublicationContext","runAllCallbacks","subscription","recordPublicationError","enrichChangeWithVirtualProps","composeBatchedChange","deferral","publishEvents","callbacks","createSingleRowRefProxy","toExpression","addSubscriber","CollectionSubscription","removeSubscriber","NegativeActiveSubscribersError","readRouteValue"],"mappings":"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"}