UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines • 45.6 kB
{"version":3,"file":"pooled-live-query.cjs","sources":["../../../src/query/pooled-live-query.ts"],"sourcesContent":["import { SortedMap } from '../SortedMap.js'\nimport { codedMessage, devBuild } from '../error-message.js'\nimport { CleanupQueue } from '../collection/cleanup-queue.js'\nimport {\n UNSUBSCRIBED_GC_FLOOR_MS,\n describeProblem,\n problemOf,\n} from '../collection/lifecycle.js'\nimport { buildHealthTree, healthNode } from '../collection/health-tree.js'\nimport { makeComparator } from '../utils/comparison.js'\nimport { reportUncaught, runAllCallbacks } from '../utils/callbacks.js'\nimport { isVirtualPropName } from '../virtual-props.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 { registerPooledSource } from './pooled-source.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 { CollectionSubscription } from '../collection/subscription.js'\nimport type {\n ChangeMessage,\n CollectionDependent,\n CollectionStatus,\n HealthNode,\n HealthProblem,\n} 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: CollectionSubscription | 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 // Views built on this partition and not yet cleaned up.\n private views = 0\n private gcTime = 0\n /**\n * Set when the partition ends for good: `crashed` when its source starts\n * cleanup under it (groups keep their last rows), `cleaned-up` when the\n * source's cleanup ends its dependents first (groups empty, as a cleaned-up\n * live-query Collection's rows do).\n */\n terminated: `crashed` | `cleaned-up` | undefined = undefined\n // This partition as its source's `subscribers` list it: one pooled live\n // query for every view it serves.\n private readonly dependent: CollectionDependent = ((partition) => ({\n kind: `live-query` as const,\n get active(): boolean {\n return partition.listenerCount > 0\n },\n end: () => {\n partition.end()\n return Promise.resolve()\n },\n }))(this)\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 this.views++\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 // Building a view reads the rows the source already holds; it starts no\n // provider work until a view has a subscriber or a preload.\n this.subscribe(true)\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(deferAcquisition = false): void {\n if (!deferAcquisition) this.subscription?.resumeDeferredAcquisition()\n if (!this.terminated && !this.subscription) {\n // A released partition that subscribes again serves new mounts too.\n this.registry.add()\n const stopStatus = this.source.on(`status:change`, (event) => {\n this.deliverStatus(event)\n })\n this.stopStatusEvents = stopStatus\n // Like a live query, a pooled view crashes when its source starts\n // cleanup, before the adapter's cleanup settles; queries mounted later\n // get a new partition.\n // A cleanup already under way ends the partition inside this\n // registration, so a listener's throw fails the caller's attachment.\n let registering = true\n const stopCleanupStart = this.source._onCleanupStart(() =>\n this.end(`crashed`, registering),\n )\n registering = false\n // Late registration can terminate before either release handle exists.\n if (this.terminated as Partition[`terminated`]) {\n stopCleanupStart()\n return\n }\n this.stopStatusEvents = () => {\n stopStatus()\n stopCleanupStart()\n }\n try {\n const subscription = this.source.subscribeChanges(\n (changes) =>\n this.apply(changes as Array<ChangeMessage<Row, string | number>>),\n {\n includeInitialState: true,\n deferAcquisition,\n owner: this.dependent,\n },\n )\n // Cleanup can re-enter while source sync starts, before assignment.\n if (this.terminated as Partition[`terminated`])\n subscription.unsubscribe()\n else this.subscription = subscription\n } catch (error) {\n // Failed admission transfers no ownership; the partition is over.\n this.terminate(`crashed`)\n throw error\n }\n }\n }\n\n private terminate(status: `crashed` | `cleaned-up`): void {\n this.terminated = status\n CleanupQueue.getInstance().cancel(this)\n this.release()\n }\n\n /** End the partition for good and tell its views. */\n private end(\n status: `crashed` | `cleaned-up` = `cleaned-up`,\n rethrow = false,\n ): void {\n if (this.terminated) return\n const previousStatus = this.source.status\n this.terminate(status)\n if (status === `cleaned-up`) {\n // Emptying is a state change, as a live-query Collection's cleanup\n // advances its revision, so a detached reader re-reads the rows.\n for (const group of this.groups.values()) {\n group.rows.clear()\n group.revision = group.layoutRevision = ++this.clock\n }\n }\n this.deliverStatus(\n {\n type: `status:change`,\n collection: this.source as unknown as Collection,\n previousStatus,\n status,\n },\n rethrow,\n )\n }\n\n private deliverStatus(\n event: CollectionStatusChangeEvent,\n rethrow = false,\n ): void {\n const delivered = this.terminated\n ? { ...event, status: this.terminated }\n : event\n // Each listener gets its own call. During a source event, as from a\n // Collection's emitter, a throw is reported uncaught; during the\n // caller's own registration it is rethrown after every call. `release()`\n // already stopped source events for a terminated partition.\n const calls = [...this.statusListeners].map(\n (listener) => () => listener(delivered),\n )\n if (rethrow) runAllCallbacks(calls)\n else\n for (const call of calls)\n try {\n call()\n } catch (error) {\n reportUncaught(error)\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 release(): void {\n this.stopStatusEvents?.()\n this.stopStatusEvents = undefined\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 /**\n * One view was cleaned up. The last one releases the source subscription\n * now if no view listens, as cleaning up a live-query Collection releases\n * its source subscriptions at once; a sibling view keeps its rows and its\n * own grace period. A later mount subscribes again.\n */\n releaseView(): void {\n if (--this.views > 0) return\n CleanupQueue.getInstance().cancel(this)\n this.releaseIfUnused()\n }\n\n private readonly releaseIfUnused = (): void => {\n if (this.listenerCount > 0) return\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 // This view's share of its partition ends once, at its first cleanup.\n private released = false\n private collectionHold: { unsubscribe: () => void } | undefined = undefined\n\n constructor(\n 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 private readonly alias: string,\n ) {\n partition.retain(gcTime)\n registerPooledSource(this, source)\n }\n\n // Cleanup discards a view's state, as it does any Collection's (law 14).\n get status(): CollectionStatus {\n return this.released\n ? `cleaned-up`\n : (this.partition.terminated ?? this.source.status)\n }\n\n private endedProblem: HealthProblem | undefined\n\n /** The same health tree a live-query Collection over this source exposes. */\n get health(): HealthNode {\n // Cache keys apart from the alias, which a query may name `root`.\n const source = buildHealthTree(\n this.source,\n { alias: this.alias, id: `source` },\n this,\n )\n // The view shows its source's status error. A terminated partition also\n // ended this view, for example when its source was cleaned up under it.\n const error =\n this.source._lifecycle.statusProblem ??\n (this.partition.terminated === `crashed`\n ? (this.endedProblem ??= problemOf([\n describeProblem(\n new Error(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `The pooled live query's source ended`\n : codedMessage(258),\n ),\n ),\n ]))\n : null)\n return healthNode(this, `view`, {\n alias: `root`,\n collectionId: this.source.id,\n kind: `live-query`,\n status: this.status,\n failureReason: null,\n error,\n reloading: false,\n sources: [source],\n })\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 // A cleaned-up view's revisions never match a group's, which start at 0,\n // so a reader that cached its rows reads them again.\n get _stateRevision(): number {\n return this.released ? -1 : this.group.revision\n }\n\n get _layoutRevision(): number {\n return this.released ? -1 : this.group.layoutRevision\n }\n\n entries(): Iterable<[string | number, Row]> {\n if (this.released) return []\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 let subscribed = true\n const 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 try {\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 } catch (error) {\n // A failed bootstrap returned no handle; retire its listener now.\n unsubscribe()\n throw error\n }\n return { unsubscribe }\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 // A partition that ended stops following its source, as a cleaned-up or\n // crashed live-query Collection does: its views announce no later change.\n if (event === `health:change`)\n return this.source.on(event, (change) => {\n if (!this.partition.terminated) listener(change as never)\n })\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(options?: { dependents?: boolean }): Promise<void> {\n const cleanup = this.collection?.cleanup(options) ?? Promise.resolve()\n // A cleanup that a live query reading the built Collection refuses\n // changes nothing (law 14): the view keeps its share of the partition.\n if (\n !this.released &&\n (this.collection?._lifecycle.hasStartedCleanup() ?? true)\n ) {\n this.released = true\n this.partition.releaseView()\n }\n return cleanup\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 // On-demand loading needs the live-query Collection.\n if (source.config.syncMode === `on-demand`) return undefined\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 ir.from.alias,\n ) as unknown as Collection<any, any, any>\n}\n\n/**\n * Pooled live queries for framework hooks. Pass it as `pool` in a hook's\n * query config to serve equality-filtered queries of one shape from a shared\n * partition. Apps that do not pass it do not bundle pooling.\n */\nexport const pooledLiveQueries = {\n createPooledLiveQuery,\n getPooledQueryIdentity,\n}\n\nexport type PooledLiveQueries = typeof pooledLiveQueries\n"],"names":["SortedMap","compareRows","registry","clock","views","releaseIfUnused","equalityKey","readPath","sameFields","dropIfUnused","scheduleRelease","UNSUBSCRIBED_GC_FLOOR_MS","deliverStatus","stopStatusEvents","CleanupQueue","reportUncaught","isVirtualPropName","equalityConjunct","compileExpression","toBooleanPredicate","getWhereExpression","orderBy","buildCompareOptions","makeComparator","passes","collectionHold","registerPooledSource","buildHealthTree","endedProblem","problemOf","describeProblem","devBuild","codedMessage","healthNode","filterChanges","holdCollection","createLiveQueryCollection","ir"],"mappings":"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"}