@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 36.7 kB
Source Map (JSON)
{"version":3,"file":"pooled-live-query.cjs","sources":["../../../src/query/pooled-live-query.ts"],"sourcesContent":["import { SortedMap } from '../SortedMap.js'\nimport { CleanupQueue } from '../collection/cleanup-queue.js'\nimport { UNSUBSCRIBED_GC_FLOOR_MS } from '../collection/lifecycle.js'\nimport { makeComparator } from '../utils/comparison.js'\nimport { isVirtualPropName } from '../virtual-props.js'\nimport { getPersistedReadinessSource } from '../persisted-readiness.js'\nimport { getWhereExpression } from './ir.js'\nimport { equalityConjunct, equalityKey, readPath } from './equality-conjunct.js'\nimport { compileExpression, toBooleanPredicate } from './compiler/evaluators.js'\nimport { buildCompareOptions } from './compiler/order-by.js'\nimport { createLiveQueryCollection } from './live-query-collection.js'\nimport type { BasicExpression, OrderBy, QueryIR } from './ir.js'\nimport type { BaseQueryBuilder } from './builder/index.js'\nimport type { Collection, CollectionImpl } from '../collection/index.js'\nimport type { ChangeMessage, CollectionStatus } from '../types.js'\nimport type {\n CollectionEventHandler,\n CollectionStatusChangeEvent,\n} from '../collection/events.js'\n\n/**\n * Live queries that filter one source Collection only by `eq(field, literal)`\n * share one partition of that source per filtered field set. Each query reads\n * the group for its literal tuple, so mounting many queries of one shape\n * costs a lookup each instead of a compiled graph and a source subscription.\n *\n * A group holds the rows the partition's source subscription has published,\n * keyed by the same normalized equality that `eq` uses: a Date equals its\n * timestamp, `NaN` equals `NaN`, `-0` equals `0`, and nullish values match no\n * literal.\n */\n\ntype Row = Record<string, unknown>\ntype Listener = (changes: Array<ChangeMessage<Row, string | number>>) => void\ntype StatusListener = CollectionEventHandler<`status:change`>\n\ninterface PartitionGroup {\n key: string\n // Key order, as in a live-query Collection without orderBy.\n rows: SortedMap<string | number, Row>\n listeners: Set<Listener>\n revision: number\n layoutRevision: number\n}\n\nconst partitionsBySource = new WeakMap<object, Map<string, Partition>>()\n\n// What a view reads for a value with no rows and no watchers. Real groups\n// draw revisions from their partition's clock, which starts above 0.\nconst EMPTY_GROUP: PartitionGroup = {\n key: ``,\n rows: new SortedMap(),\n listeners: new Set(),\n revision: 0,\n layoutRevision: 0,\n}\n\n// Length-prefixed, so no two part lists share an encoding.\nfunction appendGroupKeyPart(groupKey: string, part: string): string {\n return `${groupKey}${part.length}:${part}`\n}\n\nclass Partition {\n private readonly groups = new Map<string, PartitionGroup>()\n // Revisions for every group, so a recreated group never repeats one.\n private clock = 0\n private subscription: { unsubscribe: () => void } | undefined\n private stopStatusEvents: (() => void) | undefined\n // One source status listener serves every view of this partition.\n readonly statusListeners = new Set<StatusListener>()\n private listenerCount = 0\n private gcTime = 0\n /** Set when the source starts cleanup; groups keep their last rows. */\n terminated = false\n\n constructor(\n private readonly source: CollectionImpl<any, any, any, any, any>,\n private readonly paths: Array<Array<string>>,\n // Row order for an `orderBy` shape; key order otherwise.\n private readonly compareRows: ((a: Row, b: Row) => number) | undefined,\n // The partition's entry in its source's map, which a mount looks up.\n private readonly registry: { add: () => void; remove: () => void },\n ) {}\n\n groupKeyOf(row: Row | undefined): string | undefined {\n if (row === undefined) return undefined\n let groupKey = ``\n for (const path of this.paths) {\n const key = equalityKey(readPath(row, path))\n if (key === undefined) return undefined\n groupKey = appendGroupKeyPart(groupKey, key)\n }\n return groupKey\n }\n\n // Whether two versions of a row hold the same value in every field.\n private sameFields(a: Row, b: Row): boolean {\n return this.paths.every((path) =>\n Object.is(readPath(a, path), readPath(b, path)),\n )\n }\n\n group(key: string): PartitionGroup {\n let group = this.groups.get(key)\n if (!group) {\n const revision = ++this.clock\n group = {\n key,\n rows: new SortedMap(this.compareRows),\n listeners: new Set(),\n revision,\n layoutRevision: revision,\n }\n this.groups.set(key, group)\n }\n return group\n }\n\n /** A group for reading, without creating one. */\n peek(key: string): PartitionGroup {\n return this.groups.get(key) ?? EMPTY_GROUP\n }\n\n // A group with no rows and no watchers holds nothing anyone can read.\n private dropIfUnused(group: PartitionGroup): void {\n if (group.rows.size === 0 && group.listeners.size === 0) {\n this.groups.delete(group.key)\n }\n }\n\n /**\n * Keep the shared source subscription open. Each view brings its query's\n * `gcTime`; the partition keeps the longest, so it never releases before\n * one of its views' own live-query Collection would have.\n */\n retain(gcTime?: number): void {\n if (gcTime !== undefined) {\n // As for a Collection, a non-positive or non-finite gcTime disables GC.\n const delay = gcTime > 0 && Number.isFinite(gcTime) ? gcTime : Infinity\n this.gcTime = Math.max(this.gcTime, delay)\n }\n if (this.terminated) return\n this.subscribe()\n // Like a Collection that synced before anything subscribed, a view built\n // during a render gets a grace period to subscribe when it commits.\n this.scheduleRelease(UNSUBSCRIBED_GC_FLOOR_MS)\n }\n\n subscribe(): void {\n if (!this.terminated && !this.subscription) {\n // A released partition that subscribes again serves new mounts too.\n this.registry.add()\n this.subscription = this.source.subscribeChanges(\n (changes) =>\n this.apply(changes as Array<ChangeMessage<Row, string | number>>),\n { includeInitialState: true },\n )\n const stopStatus = this.source.on(`status:change`, (event) => {\n this.deliverStatus(event)\n })\n // Like a live query, a pooled view fails for good when its source\n // starts cleanup, before the adapter's cleanup settles; queries\n // mounted later get a new partition.\n const stopCleanupStart = this.source._onCleanupStart(() => {\n const previousStatus = this.source.status\n this.terminate()\n this.deliverStatus({\n type: `status:change`,\n collection: this.source as unknown as Collection,\n previousStatus,\n status: `error`,\n })\n })\n this.stopStatusEvents = () => {\n stopStatus()\n stopCleanupStart()\n }\n }\n }\n\n private deliverStatus(event: CollectionStatusChangeEvent): void {\n const delivered = this.terminated\n ? { ...event, status: `error` as const }\n : event\n for (const listener of [...this.statusListeners]) listener(delivered)\n if (this.terminated) {\n this.stopStatusEvents?.()\n this.stopStatusEvents = undefined\n }\n }\n\n addListener(group: PartitionGroup, listener: Listener): void {\n this.subscribe()\n group.listeners.add(listener)\n this.listenerCount++\n }\n\n removeListener(group: PartitionGroup, listener: Listener): void {\n if (!group.listeners.delete(listener)) return\n this.listenerCount--\n this.dropIfUnused(group)\n this.scheduleRelease(0)\n }\n\n private terminate(): void {\n this.terminated = true\n CleanupQueue.getInstance().cancel(this)\n this.release()\n }\n\n private release(): void {\n this.subscription?.unsubscribe()\n this.subscription = undefined\n this.registry.remove()\n }\n\n // Releases on the Collections' shared GC queue, after the longest\n // `gcTime` of this partition's views.\n private scheduleRelease(minDelay: number): void {\n if (this.listenerCount > 0 || !Number.isFinite(this.gcTime)) return\n const delay = Math.max(this.gcTime, minDelay)\n CleanupQueue.getInstance().schedule(this, delay, this.releaseIfUnused)\n }\n\n private readonly releaseIfUnused = (): void => {\n if (this.listenerCount > 0) return\n this.stopStatusEvents?.()\n this.stopStatusEvents = undefined\n // Views read groups by key, so a later subscription refills new ones.\n this.groups.clear()\n this.release()\n }\n\n private apply(changes: Array<ChangeMessage<Row, string | number>>): void {\n const touched = new Map<\n PartitionGroup,\n Array<ChangeMessage<Row, string | number>>\n >()\n const record = (\n group: PartitionGroup,\n change: ChangeMessage<Row, string | number>,\n ) => {\n const list = touched.get(group)\n if (list) list.push(change)\n else touched.set(group, [change])\n if (change.type !== `update`) group.layoutRevision = ++this.clock\n }\n for (const change of changes) {\n const next =\n change.type === `delete` ? undefined : this.groupKeyOf(change.value)\n const previous =\n change.type === `insert`\n ? undefined\n : change.type === `update` &&\n change.previousValue !== undefined &&\n this.sameFields(change.value, change.previousValue)\n ? next\n : this.groupKeyOf(\n change.type === `delete` ? change.value : change.previousValue,\n )\n if (previous !== undefined && previous !== next) {\n const group = this.group(previous)\n const old = group.rows.get(change.key)\n if (group.rows.delete(change.key)) {\n record(group, { type: `delete`, key: change.key, value: old! })\n }\n }\n if (next !== undefined) {\n const group = this.group(next)\n const existed = group.rows.has(change.key)\n group.rows.set(change.key, change.value)\n record(\n group,\n existed\n ? change.type === `update`\n ? change\n : { ...change, type: `update` }\n : { type: `insert`, key: change.key, value: change.value },\n )\n }\n }\n for (const [group, groupChanges] of touched) {\n group.revision = ++this.clock\n for (const listener of [...group.listeners]) listener(groupChanges)\n this.dropIfUnused(group)\n }\n }\n}\n\ntype Conjunct = { path: Array<string>; pathKey: string; literalKey: string }\n\ntype PoolableShape = {\n paths: Array<Array<string>>\n shapeKey: string\n groupKey: string\n // Conjuncts each view evaluates over its group's rows.\n residual: Array<BasicExpression>\n orderBy: OrderBy | undefined\n}\n\n// Whether an expression reads only this query's own row fields, so a view\n// can evaluate it with the compiler's evaluator.\nfunction readsOnlyRow(expression: BasicExpression, alias: string): boolean {\n if (expression.type === `val`) return true\n if (expression.type === `ref`) {\n const [root, field] = expression.path\n return root === alias && field !== undefined && !isVirtualPropName(field)\n }\n return expression.args.every((arg) => readsOnlyRow(arg, alias))\n}\n\n// Splits a conjunct into `eq(alias.field, literal)` groups and residual\n// conjuncts; false for an expression a view cannot evaluate.\nfunction collectConjuncts(\n expression: BasicExpression,\n alias: string,\n out: Array<Conjunct>,\n residual: Array<BasicExpression>,\n): boolean {\n if (expression.type === `func` && expression.name === `and`) {\n for (const arg of expression.args) {\n if (!collectConjuncts(arg, alias, out, residual)) return false\n }\n return true\n }\n const conjunct = equalityConjunctOf(expression, alias)\n if (conjunct) out.push(conjunct)\n else if (readsOnlyRow(expression, alias)) residual.push(expression)\n else return false\n return true\n}\n\nfunction equalityConjunctOf(\n expression: BasicExpression,\n alias: string,\n): Conjunct | undefined {\n const conjunct = equalityConjunct(expression, (ref) =>\n ref.path[0] === alias ? ref.path.slice(1) : undefined,\n )\n return conjunct && { ...conjunct, pathKey: JSON.stringify(conjunct.path) }\n}\n\n// Whether a row passes every residual conjunct, as a WHERE filter decides.\nfunction rowPredicate(\n residual: Array<BasicExpression>,\n alias: string,\n): (row: Row) => boolean {\n const conjuncts = residual.map((expression) => compileExpression(expression))\n // One namespaced row, reused so a check allocates nothing.\n const namespaced: Record<string, unknown> = {}\n return (row) => {\n namespaced[alias] = row\n return conjuncts.every((conjunct) =>\n toBooleanPredicate(conjunct(namespaced as any)),\n )\n }\n}\n\n/**\n * The equality conjuncts of a query that a partition can serve, or undefined\n * when any other clause or operand is present.\n */\nfunction poolableShape(query: QueryIR): PoolableShape | undefined {\n if (\n query.from.type !== `collectionRef` ||\n query.select ||\n query.join ||\n query.groupBy ||\n query.having ||\n query.limit !== undefined ||\n query.offset !== undefined ||\n query.distinct ||\n query.singleResult ||\n query.fnSelect ||\n query.fnWhere?.length ||\n query.fnHaving?.length ||\n !query.where?.length\n ) {\n return undefined\n }\n const conjuncts: Array<Conjunct> = []\n const residual: Array<BasicExpression> = []\n for (const where of query.where) {\n if (\n !collectConjuncts(\n getWhereExpression(where),\n query.from.alias,\n conjuncts,\n residual,\n )\n ) {\n return undefined\n }\n }\n // A partition needs at least one equality to group by.\n if (conjuncts.length === 0) return undefined\n const orderBy = query.orderBy?.length ? query.orderBy : undefined\n const orderKey = orderBy\n ? orderByKey(orderBy, query.from.alias, query.from.collection)\n : ``\n if (orderKey === undefined) return undefined\n // Most shapes have one or two fields; a general sort costs more than both.\n if (conjuncts.length === 2) {\n if (conjuncts[1]!.pathKey < conjuncts[0]!.pathKey) conjuncts.reverse()\n } else if (conjuncts.length > 2) {\n conjuncts.sort((a, b) => (a.pathKey < b.pathKey ? -1 : 1))\n }\n const paths: Array<Array<string>> = []\n let shapeKey = ``\n let groupKey = ``\n for (const { path, pathKey, literalKey } of conjuncts) {\n paths.push(path)\n // Each JSON path delimits itself, so concatenation stays unambiguous.\n shapeKey += pathKey\n groupKey = appendGroupKeyPart(groupKey, literalKey)\n }\n // Groups of one shape share a row order, so the order is part of it.\n if (orderKey) shapeKey += `|${orderKey}`\n return { paths, shapeKey, groupKey, residual, orderBy }\n}\n\n// A key for an `orderBy` over this query's own row fields, or undefined for\n// one a partition cannot share by value, such as a custom string comparator.\nfunction orderByKey(\n orderBy: OrderBy,\n alias: string,\n source: CollectionImpl<any, any, any, any, any>,\n): string | undefined {\n let key = ``\n for (const clause of orderBy) {\n const { expression } = clause\n if (\n expression.type !== `ref` ||\n !readsOnlyRow(expression, alias) ||\n expression.path.length < 2\n ) {\n return undefined\n }\n const options = buildCompareOptions(clause, source)\n if (options.stringSort === `custom`) return undefined\n key += JSON.stringify([expression.path.slice(1), options])\n }\n return key\n}\n\n// Orders rows as a live-query Collection's `orderBy` does; the group's\n// SortedMap breaks ties by key.\nfunction rowComparator(\n orderBy: OrderBy,\n alias: string,\n source: CollectionImpl<any, any, any, any, any>,\n): (a: Row, b: Row) => number {\n const terms = orderBy.map((clause) => ({\n read: compileExpression(clause.expression),\n compare: makeComparator(buildCompareOptions(clause, source)),\n }))\n // One namespaced row, reused so a comparison allocates nothing.\n const namespaced: Record<string, unknown> = {}\n return (a, b) => {\n for (const { read, compare } of terms) {\n namespaced[alias] = a\n const left = read(namespaced as any)\n namespaced[alias] = b\n const result = compare(left, read(namespaced as any))\n if (result !== 0) return result\n }\n return 0\n }\n}\n\n/**\n * One query's view of its group, read by the live-query observer. Users get\n * `publicCollection` instead, which builds the query's live-query Collection\n * on first use and forwards every member to it.\n */\nclass PooledLiveQuery {\n readonly isLoadingSubset = false\n // No persisted readiness, single-result config, or layout channel.\n readonly config = undefined\n readonly _subscribeLayoutChanges = undefined\n private collection: Collection<any, any, any> | undefined = undefined\n private listenerCount = 0\n private collectionHold: { unsubscribe: () => void } | undefined = undefined\n\n constructor(\n private readonly source: CollectionImpl<any, any, any, any, any>,\n private readonly query: BaseQueryBuilder,\n private readonly partition: Partition,\n private readonly groupKey: string,\n private readonly gcTime: number,\n // The query's conjuncts beyond its group's equalities, if any.\n private readonly passes: ((row: Row) => boolean) | undefined,\n ) {\n partition.retain(gcTime)\n }\n\n get status(): CollectionStatus {\n return this.partition.terminated ? `error` : this.source.status\n }\n\n // Read by key: the partition drops a group nobody watches once it empties.\n private get group(): PartitionGroup {\n return this.partition.peek(this.groupKey)\n }\n\n get _stateRevision(): number {\n return this.group.revision\n }\n\n get _layoutRevision(): number {\n return this.group.layoutRevision\n }\n\n entries(): Iterable<[string | number, Row]> {\n const rows = this.group.rows.entries()\n const passes = this.passes\n return passes ? [...rows].filter(([, row]) => passes(row)) : rows\n }\n\n subscribeChanges(\n callback: Listener,\n options: { includeInitialState?: boolean } = {},\n ): { unsubscribe: () => void } {\n // A released partition refills its group here, so seed the filter after.\n this.partition.subscribe()\n const listener = this.passes ? this.filterChanges(callback) : callback\n const group = this.partition.group(this.groupKey)\n this.partition.addListener(group, listener)\n this.listenerCount++\n this.holdCollection()\n if (options.includeInitialState) {\n callback(\n Array.from(this.entries(), ([key, value]) => ({\n type: `insert`,\n key,\n value,\n })),\n )\n }\n let subscribed = true\n return {\n unsubscribe: () => {\n if (!subscribed) return\n subscribed = false\n this.partition.removeListener(group, listener)\n if (--this.listenerCount === 0) {\n this.collectionHold?.unsubscribe()\n this.collectionHold = undefined\n }\n },\n }\n }\n\n // While the view is observed, its built Collection stays subscribed, as\n // the Collection would be if it served the observer itself.\n private holdCollection(): void {\n if (this.collection && this.listenerCount > 0) {\n this.collectionHold ??= this.collection.subscribeChanges(() => {})\n }\n }\n\n // Turns the group's changes into this query's, tracking which rows have\n // passed its residual conjuncts for this subscription.\n private filterChanges(callback: Listener): Listener {\n const passes = this.passes!\n const visible = new Map(this.entries())\n return (changes) => {\n const out: Array<ChangeMessage<Row, string | number>> = []\n for (const change of changes) {\n const { key, value } = change\n const previous = visible.get(key)\n const next = change.type !== `delete` && passes(value)\n if (next) visible.set(key, value)\n else visible.delete(key)\n if (previous && next) {\n out.push({ type: `update`, key, value, previousValue: previous })\n } else if (previous) {\n out.push({ type: `delete`, key, value: previous })\n } else if (next) {\n out.push({ type: `insert`, key, value })\n }\n }\n if (out.length > 0) callback(out)\n }\n }\n\n on(...args: Parameters<CollectionImpl[`on`]>): () => void {\n const [event, listener] = args\n if (event !== `status:change`) return this.source.on(...args)\n const listeners = this.partition.statusListeners\n listeners.add(listener as StatusListener)\n return () => listeners.delete(listener as StatusListener)\n }\n\n preload(): Promise<void> {\n return this.source.preload()\n }\n\n cleanup(): Promise<void> {\n return this.collection?.cleanup() ?? Promise.resolve()\n }\n\n private proxy: Collection<any, any, any> | undefined = undefined\n\n /** This view as the Collection it stands in for. */\n get publicCollection(): Collection<any, any, any> {\n return (this.proxy ??= new Proxy(\n this,\n forwardToCollection,\n ) as unknown as Collection<any, any, any>)\n }\n\n materialize(): Collection<any, any, any> {\n if (!this.collection) {\n this.collection = createLiveQueryCollection({\n query: this.query,\n startSync: true,\n gcTime: this.gcTime,\n })\n this.holdCollection()\n }\n return this.collection\n }\n}\n\n// The observer reads the view itself; users get the live-query Collection.\nconst forwardToCollection: ProxyHandler<PooledLiveQuery> = {\n get(view, property) {\n const collection = view.materialize()\n const value = Reflect.get(collection, property, collection)\n return typeof value === `function` ? value.bind(collection) : value\n },\n has(view, property) {\n return Reflect.has(view.materialize(), property)\n },\n // So `instanceof` and query sources accept it as the Collection it is.\n getPrototypeOf(view) {\n return Reflect.getPrototypeOf(view.materialize())\n },\n}\n\n/**\n * The identity of a query a partition can serve with no residual conjunct:\n * its source and its `eq` fields and literals, which determine its rows.\n * Undefined for any other query, which keeps the full structural identity.\n */\nexport function getPooledQueryIdentity(\n query: BaseQueryBuilder,\n): string | undefined {\n const ir = query._getQuery()\n if (ir.from.type !== `collectionRef`) return undefined\n const shape = poolableShape(ir)\n if (!shape || shape.residual.length > 0) return undefined\n return JSON.stringify([ir.from.collection.id, shape.shapeKey, shape.groupKey])\n}\n\n/**\n * A pooled view for a query a partition can serve, or undefined. The view is\n * typed as the Collection it stands in for.\n */\nexport function createPooledLiveQuery(\n query: BaseQueryBuilder,\n // A live-query Collection's default when the adapter gives none.\n { gcTime = 5_000 }: { gcTime?: number } = {},\n): Collection<any, any, any> | undefined {\n const ir = query._getQuery()\n const shape = poolableShape(ir)\n if (!shape || ir.from.type !== `collectionRef`) return undefined\n const source = ir.from.collection\n // Persisted restore and on-demand loading need the live-query Collection.\n if (\n source.config.syncMode === `on-demand` ||\n getPersistedReadinessSource(source.config)\n ) {\n return undefined\n }\n let partitions = partitionsBySource.get(source)\n if (!partitions) {\n partitions = new Map()\n partitionsBySource.set(source, partitions)\n }\n const { shapeKey } = shape\n let partition = partitions.get(shapeKey)\n if (!partition) {\n const owner = partitions\n // A released partition may subscribe again; it must not then replace or\n // remove a newer partition created under its key.\n const created: Partition = new Partition(\n source,\n shape.paths,\n shape.orderBy && rowComparator(shape.orderBy, ir.from.alias, source),\n {\n add: () => {\n if (!owner.has(shapeKey)) owner.set(shapeKey, created)\n },\n remove: () => {\n if (owner.get(shapeKey) === created) owner.delete(shapeKey)\n },\n },\n )\n partition = created\n partitions.set(shapeKey, partition)\n }\n // Observers read the view directly; users get its `publicCollection`.\n return new PooledLiveQuery(\n source,\n query,\n partition,\n shape.groupKey,\n gcTime,\n shape.residual.length > 0\n ? rowPredicate(shape.residual, ir.from.alias)\n : undefined,\n ) as unknown as Collection<any, any, any>\n}\n"],"names":["SortedMap","compareRows","registry","clock","releaseIfUnused","stopStatusEvents","equalityKey","readPath","sameFields","dropIfUnused","scheduleRelease","UNSUBSCRIBED_GC_FLOOR_MS","deliverStatus","CleanupQueue","isVirtualPropName","equalityConjunct","compileExpression","toBooleanPredicate","getWhereExpression","orderBy","buildCompareOptions","makeComparator","passes","collectionHold","filterChanges","holdCollection","createLiveQueryCollection","ir","getPersistedReadinessSource"],"mappings":"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"}