@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 24 kB
Source Map (JSON)
{"version":3,"file":"change-events.cjs","sources":["../../../src/collection/change-events.ts"],"sourcesContent":["import {\n compileSingleRowExpression,\n toBooleanPredicate,\n} from '../query/compiler/evaluators.js'\nimport {\n findIndexForField,\n optimizeExpressionWithIndexes,\n} from '../utils/index-optimization.js'\nimport { ensureIndexForField } from '../indexes/auto-index.js'\nimport { getPropRefPropertyPath } from '../query/ir.js'\nimport { isVirtualPropName } from '../virtual-props.js'\nimport { makeComparator } from '../utils/comparison.js'\nimport { buildCompareOptions } from '../query/compiler/order-by'\nimport type {\n ChangeMessage,\n CollectionLike,\n CurrentStateAsChangesOptions,\n SubscribeChangesOptions,\n} from '../types'\nimport type { CollectionImpl } from './index.js'\nimport type { BasicExpression, OrderBy } from '../query/ir.js'\nimport type { WithVirtualProps } from '../virtual-props.js'\n\n/**\n * Yields visible entries, enriched with virtual properties, whose stored row\n * passes `prefilter`.\n */\nexport type StoredRowScan<T extends object, TKey extends string | number> = (\n prefilter: (row: object) => boolean,\n) => Iterable<[TKey, WithVirtualProps<T, TKey>]>\n\n/**\n * Returns the current state of the collection as an array of changes\n * @param collection - The collection to get changes from\n * @param options - Options including optional where filter, orderBy, and limit\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = currentStateAsChanges(collection)\n *\n * // Get only items matching a condition\n * const activeChanges = currentStateAsChanges(collection, {\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = currentStateAsChanges(collection, {\n * where: eq(row.status, 'active')\n * })\n *\n * // Get items ordered by name with limit\n * const topUsers = currentStateAsChanges(collection, {\n * orderBy: [{ expression: row.name, compareOptions: { direction: 'asc' } }],\n * limit: 10\n * })\n *\n * // Get active users ordered by score (highest score first)\n * const topActiveUsers = currentStateAsChanges(collection, {\n * where: eq(row.status, 'active'),\n * orderBy: [{ expression: row.score, compareOptions: { direction: 'desc' } }],\n * })\n */\nexport function currentStateAsChanges<\n T extends object,\n TKey extends string | number,\n>(\n collection: CollectionLike<WithVirtualProps<T, TKey>, TKey>,\n options: CurrentStateAsChangesOptions = {},\n scanStoredRows?: StoredRowScan<T, TKey>,\n): Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>> | void {\n // Helper function to collect filtered results\n const collectFilteredResults = (\n filterFn?: (value: WithVirtualProps<T, TKey>) => boolean,\n ): Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>> => {\n const result: Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>> = []\n // Reject rows by one stored field before copying them to add virtual\n // properties. Survivors still pass through the full predicate.\n const prefilter =\n scanStoredRows && options.where && compileEqualityPrefilter(options.where)\n if (filterFn && scanStoredRows && prefilter) {\n for (const [key, value] of scanStoredRows(prefilter)) {\n if (filterFn(value)) result.push({ type: `insert`, key, value })\n }\n return result\n }\n for (const [key, value] of collection.entries()) {\n // If no filter function is provided, include all items\n if (filterFn?.(value) ?? true) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n }\n\n // Validate that limit without orderBy doesn't happen\n if (options.limit !== undefined && !options.orderBy) {\n throw new Error(`limit cannot be used without orderBy`)\n }\n\n // First check if orderBy is present (optionally with limit)\n if (options.orderBy) {\n // Create where filter function if present\n const whereFilter = options.where\n ? createFilterFunctionFromExpression(options.where)\n : undefined\n\n // Get ordered keys using index optimization when possible\n const orderedKeys = getOrderedKeys(\n collection,\n options.orderBy,\n options.limit,\n whereFilter,\n options.optimizedOnly,\n )\n\n if (orderedKeys === undefined) {\n // `getOrderedKeys` returned undefined because we asked for `optimizedOnly` and there was no index to use\n return\n }\n\n // Convert keys to change messages\n const result: Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>> = []\n for (const key of orderedKeys) {\n const value = collection.get(key)\n if (value !== undefined) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n }\n\n // If no orderBy OR orderBy optimization failed, use where clause optimization\n if (!options.where) {\n // No filtering, return all items\n return collectFilteredResults()\n }\n\n // There's a where clause, let's see if we can use an index\n try {\n const expression: BasicExpression<boolean> = options.where\n\n // Try to optimize the query using indexes\n const optimizationResult = optimizeExpressionWithIndexes(\n expression,\n collection,\n )\n\n if (optimizationResult.canOptimize) {\n // Use index optimization. When the index lookup is inexact, the keys\n // are a superset of the true result (some conditions could not be\n // served by an index), so re-check each row against the full expression.\n const filterFn = optimizationResult.isExact\n ? undefined\n : createFilterFunctionFromExpression(expression)\n const result: Array<ChangeMessage<WithVirtualProps<T, TKey>, TKey>> = []\n for (const key of optimizationResult.matchingKeys) {\n const value = collection.get(key)\n if (value !== undefined && (filterFn?.(value) ?? true)) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n } else {\n if (options.optimizedOnly) {\n return\n }\n\n const filterFn = createFilterFunctionFromExpression(expression)\n return collectFilteredResults(filterFn)\n }\n } catch (error) {\n // If anything goes wrong with the where clause, fall back to full scan\n console.warn(\n `${collection.id ? `[${collection.id}] ` : ``}Error processing where clause, falling back to full scan:`,\n error,\n )\n\n const filterFn = createFilterFunctionFromExpression(options.where)\n\n if (options.optimizedOnly) {\n return\n }\n\n return collectFilteredResults(filterFn)\n }\n}\n\n/**\n * Creates a filter function from a pre-compiled expression\n * @param expression - The pre-compiled expression to evaluate\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunctionFromExpression<T extends object>(\n expression: BasicExpression<boolean>,\n): (item: T) => boolean {\n // Compile expression once when filter function is created, not on every invocation\n const evaluator = compileSingleRowExpression(expression)\n\n return (item: T): boolean => {\n try {\n const result = evaluator(item as Record<string, unknown>)\n return toBooleanPredicate(result)\n } catch {\n // If evaluation fails, exclude the item\n return false\n }\n }\n}\n\n/** A field and the string or boolean literal a top-level `eq` requires. */\nexport type EqualityRoute = {\n path: Array<string>\n /** Stable identity of `path`, for grouping routes by field. */\n pathKey: string\n expected: string | boolean\n}\n\n/** Read result for a route path whose property access threw. */\nexport const UNREADABLE_ROUTE_VALUE: unique symbol = Symbol(\n `unreadable route value`,\n)\n\n/**\n * Finds a cheap necessary condition for `expression` to be TRUE, or returns\n * undefined when the expression has none.\n *\n * A top-level conjunct `eq(field, literal)` with a string or boolean literal is\n * TRUE only when the field holds the identical string or boolean: equality\n * normalization never maps another type onto a plain string or boolean. A\n * row whose field holds anything else therefore fails the whole expression.\n *\n * With `storedRows`, the condition is read from a stored row instead of its\n * enriched copy, so conjuncts on virtual fields are skipped: stored rows need\n * not carry them.\n */\nexport function findEqualityRoute(\n expression: BasicExpression<boolean>,\n { storedRows = false }: { storedRows?: boolean } = {},\n): EqualityRoute | undefined {\n const conjuncts: Array<BasicExpression> = []\n const collect = (node: BasicExpression) => {\n if (node.type === `func` && node.name === `and`) node.args.forEach(collect)\n else conjuncts.push(node)\n }\n collect(expression)\n\n // A string literal usually rejects more rows than a boolean one.\n let best: EqualityRoute | undefined\n for (const conjunct of conjuncts) {\n if (conjunct.type !== `func` || conjunct.name !== `eq`) continue\n const [left, right] = conjunct.args\n const ref =\n left?.type === `ref` ? left : right?.type === `ref` ? right : undefined\n const literal =\n left?.type === `val` ? left : right?.type === `val` ? right : undefined\n if (!ref || !literal) continue\n const expected: unknown = literal.value\n if (typeof expected !== `string` && typeof expected !== `boolean`) continue\n\n const path = getPropRefPropertyPath(ref)\n if (storedRows && (path.length === 0 || isVirtualPropName(path[0]!))) {\n continue\n }\n if (best === undefined || typeof best.expected === `boolean`) {\n best = { path, pathKey: JSON.stringify(path), expected }\n }\n if (typeof expected === `string`) break\n }\n return best\n}\n\n/**\n * Reads a route field the way the single-row evaluator does. A throwing read\n * returns UNREADABLE_ROUTE_VALUE so callers leave the decision to the full\n * predicate.\n */\nexport function readRouteValue(\n row: unknown,\n path: ReadonlyArray<string>,\n): unknown {\n try {\n let value: unknown = row\n for (const segment of path) {\n if (value === null || value === undefined) return undefined\n value = (value as Record<string, unknown>)[segment]\n }\n return value\n } catch {\n return UNREADABLE_ROUTE_VALUE\n }\n}\n\n/**\n * Compiles the route of `expression` as a row test that is false only when\n * the full predicate must be false.\n *\n * The test reads a stored row instead of its enriched copy. The copy holds\n * each enumerable own root property of the stored row and lacks the others, so its field is either the stored value or `undefined`,\n * which never equals the literal. A read that throws passes the row to the\n * full predicate.\n */\nexport function compileEqualityPrefilter(\n expression: BasicExpression<boolean>,\n): ((row: object) => boolean) | undefined {\n const route = findEqualityRoute(expression, { storedRows: true })\n if (route === undefined) return undefined\n const { path, expected } = route\n // Most routes name one top-level field; read it without walking a path.\n if (path.length === 1) {\n const field = path[0]!\n return (row) => {\n try {\n return (row as Record<string, unknown>)[field] === expected\n } catch {\n return true\n }\n }\n }\n return (row) => {\n const value = readRouteValue(row, path)\n return value === UNREADABLE_ROUTE_VALUE || value === expected\n }\n}\n\n/**\n * Creates a filtered callback that only calls the original callback with changes that match the where clause\n * @param originalCallback - The original callback to filter\n * @param options - The subscription options containing the where clause\n * @returns A filtered callback function\n */\nexport function createFilteredCallback<\n T extends object,\n TKey extends string | number = string | number,\n>(\n originalCallback: (changes: Array<ChangeMessage<T>>) => void,\n options: SubscribeChangesOptions<T, TKey>,\n): (changes: Array<ChangeMessage<T>>) => boolean {\n const filterFn = createFilterFunctionFromExpression(options.whereExpression!)\n\n return (changes: Array<ChangeMessage<T>>) => {\n const filteredChanges: Array<ChangeMessage<T>> = []\n\n for (const change of changes) {\n if (change.type === `insert`) {\n // For inserts, check if the new value matches the filter\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n } else if (change.type === `update`) {\n // For updates, we need to check both old and new values\n const newValueMatches = filterFn(change.value)\n const oldValueMatches = change.previousValue\n ? filterFn(change.previousValue)\n : false\n\n if (newValueMatches && oldValueMatches) {\n // Both old and new match: emit update\n filteredChanges.push(change)\n } else if (newValueMatches && !oldValueMatches) {\n // New matches but old didn't: emit insert\n filteredChanges.push({\n ...change,\n type: `insert`,\n })\n } else if (!newValueMatches && oldValueMatches) {\n // Old matched but new doesn't: emit delete\n filteredChanges.push({\n ...change,\n type: `delete`,\n value: change.previousValue!, // Use the previous value for the delete\n })\n }\n // If neither matches, don't emit anything\n } else {\n // For deletes, include if the previous value would have matched\n // (so subscribers know something they were tracking was deleted)\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n }\n }\n\n // Always call the original callback if we have filtered changes OR\n // if the original changes array was empty (which indicates a ready signal)\n if (filteredChanges.length > 0 || changes.length === 0) {\n originalCallback(filteredChanges)\n return true\n }\n return false\n }\n}\n\n/**\n * Gets ordered keys from a collection using index optimization when possible\n * @param collection - The collection to get keys from\n * @param orderBy - The order by clause\n * @param limit - Optional limit on number of keys to return\n * @param whereFilter - Optional filter function to apply while traversing\n * @returns Array of keys in sorted order\n */\nfunction getOrderedKeys<T extends object, TKey extends string | number>(\n collection: CollectionLike<T, TKey>,\n orderBy: OrderBy,\n limit?: number,\n whereFilter?: (item: T) => boolean,\n optimizedOnly?: boolean,\n): Array<TKey> | undefined {\n // For single-column orderBy on a ref expression, try index optimization\n if (orderBy.length === 1) {\n const clause = orderBy[0]!\n const orderByExpression = clause.expression\n\n if (orderByExpression.type === `ref`) {\n const propRef = orderByExpression\n const fieldPath = propRef.path\n const compareOpts = buildCompareOptions(clause, collection)\n\n // Ensure index exists for this field\n ensureIndexForField(\n fieldPath[0]!,\n fieldPath,\n collection as CollectionImpl<T, TKey>,\n compareOpts,\n )\n\n // Find the index\n const index = findIndexForField(collection, fieldPath, compareOpts)\n\n if (index && index.supports(`gt`)) {\n // Use index optimization\n const filterFn = (key: TKey): boolean => {\n const value = collection.get(key)\n if (value === undefined) {\n return false\n }\n return whereFilter?.(value) ?? true\n }\n\n // Take the keys that match the filter and limit\n // if no limit is provided `index.keyCount` is used,\n // i.e. we will take all keys that match the filter\n return index.takeFromStart(limit ?? index.keyCount, filterFn)\n }\n }\n }\n\n if (optimizedOnly) {\n return\n }\n\n // Fallback: collect all items and sort in memory\n const allItems: Array<{ key: TKey; value: T }> = []\n for (const [key, value] of collection.entries()) {\n if (whereFilter?.(value) ?? true) {\n allItems.push({ key, value })\n }\n }\n\n const clauses = orderBy.map((clause) => ({\n expression: clause.expression,\n compare: makeComparator(buildCompareOptions(clause, collection)),\n }))\n const compare = (a: { key: TKey; value: T }, b: { key: TKey; value: T }) => {\n for (const clause of clauses) {\n // Extract values for comparison\n const aValue = extractValueFromItem(a.value, clause.expression)\n const bValue = extractValueFromItem(b.value, clause.expression)\n\n const result = clause.compare(aValue, bValue)\n if (result !== 0) {\n return result\n }\n }\n return 0\n }\n\n allItems.sort(compare)\n const sortedKeys = allItems.map((item) => item.key)\n\n // Apply limit if provided\n if (limit !== undefined) {\n return sortedKeys.slice(0, limit)\n }\n\n // if no limit is provided, we will return all keys\n return sortedKeys\n}\n\n/**\n * Helper function to extract a value from an item based on an expression\n */\nfunction extractValueFromItem(item: any, expression: BasicExpression): any {\n if (expression.type === `ref`) {\n const propRef = expression\n let value = item\n for (const pathPart of propRef.path) {\n value = value?.[pathPart]\n }\n return value\n } else if (expression.type === `val`) {\n return expression.value\n } else {\n // It must be a function\n const evaluator = compileSingleRowExpression(expression)\n return evaluator(item as Record<string, unknown>)\n }\n}\n"],"names":["optimizeExpressionWithIndexes","compileSingleRowExpression","toBooleanPredicate","getPropRefPropertyPath","isVirtualPropName","orderBy","buildCompareOptions","ensureIndexForField","findIndexForField","makeComparator"],"mappings":"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"}