@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 55.7 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../../../src/query/compiler/index.ts"],"sourcesContent":["import {\n distinct,\n filter,\n join as joinOperator,\n map,\n reduce,\n} from '@tanstack/db-ivm'\nimport { optimizeQuery } from '../optimizer.js'\nimport {\n CollectionInputNotFoundError,\n DistinctRequiresSelectError,\n DuplicateAliasInSubqueryError,\n FnSelectWithGroupByError,\n HavingRequiresGroupByError,\n LimitOffsetRequireOrderByError,\n UnsupportedFromTypeError,\n} from '../../errors.js'\nimport { VIRTUAL_PROP_NAMES } from '../../virtual-props.js'\nimport {\n IncludesSubquery,\n PropRef,\n Value as ValClass,\n getWhereExpression,\n} from '../ir.js'\nimport { compileExpression, toBooleanPredicate } from './evaluators.js'\nimport { processJoins } from './joins.js'\nimport { containsAggregate, processGroupBy } from './group-by.js'\nimport { processOrderBy } from './order-by.js'\nimport { processSelect } from './select.js'\nimport type { CollectionSubscription } from '../../collection/subscription.js'\nimport type { OrderByOptimizationInfo } from './order-by.js'\nimport type {\n BasicExpression,\n CollectionRef,\n IncludesMaterialization,\n QueryIR,\n QueryRef,\n} from '../ir.js'\nimport type { LazyCollectionCallbacks } from './joins.js'\nimport type { Collection } from '../../collection/index.js'\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n ResultStream,\n} from '../../types.js'\nimport type { QueryCache, QueryMapping, WindowOptions } from './types.js'\n\nexport type { WindowOptions } from './types.js'\n\n/** Symbol used to tag parent $selected with routing metadata for includes */\nexport const INCLUDES_ROUTING = Symbol(`includesRouting`)\n\n/**\n * Result of compiling an includes subquery, including the child pipeline\n * and metadata needed to route child results to parent-scoped Collections.\n */\nexport interface IncludesCompilationResult {\n /** Filtered child pipeline (post inner-join with parent keys) */\n pipeline: ResultStream\n /** Result field name on parent (e.g., \"issues\") */\n fieldName: string\n /** Parent-side correlation ref (e.g., project.id) */\n correlationField: PropRef\n /** Child-side correlation ref (e.g., issue.projectId) */\n childCorrelationField: PropRef\n /** Whether the child query has an ORDER BY clause */\n hasOrderBy: boolean\n /** Full compilation result for the child query (for nested includes + alias tracking) */\n childCompilationResult: CompilationResult\n /** Parent-side projection refs for parent-referencing filters */\n parentProjection?: Array<PropRef>\n /** How the output layer materializes the child result on the parent row */\n materialization: IncludesMaterialization\n /** Internal field used to unwrap scalar child selects */\n scalarField?: string\n}\n\n/**\n * Result of query compilation including both the pipeline and source-specific WHERE clauses\n */\nexport interface CompilationResult {\n /** The ID of the main collection */\n collectionId: string\n\n /** The compiled query pipeline (D2 stream) */\n pipeline: ResultStream\n\n /** Map of source aliases to their WHERE clauses for index optimization */\n sourceWhereClauses: Map<string, BasicExpression<boolean>>\n\n /**\n * Maps each source alias to its collection ID. Enables per-alias subscriptions for self-joins.\n * Example: `{ employee: 'employees-col-id', manager: 'employees-col-id' }`\n */\n aliasToCollectionId: Record<string, string>\n\n /**\n * Flattened mapping from outer alias to innermost alias for subqueries.\n * Always provides one-hop lookups, never recursive chains.\n *\n * Example: `{ activeUser: 'user' }` when `.from({ activeUser: subquery })`\n * where the subquery uses `.from({ user: collection })`.\n *\n * For deeply nested subqueries, the mapping goes directly to the innermost alias:\n * `{ author: 'user' }` (not `{ author: 'activeUser' }`), so `aliasRemapping[alias]`\n * always resolves in a single lookup.\n *\n * Used to resolve subscriptions during lazy loading when join aliases differ from\n * the inner aliases where collection subscriptions were created.\n */\n aliasRemapping: Record<string, string>\n\n /** Child pipelines for includes subqueries */\n includes?: Array<IncludesCompilationResult>\n}\n\n/**\n * Compiles a query IR into a D2 pipeline\n * @param rawQuery The query IR to compile\n * @param inputs Mapping of source aliases to input streams (e.g., `{ employee: input1, manager: input2 }`)\n * @param collections Mapping of collection IDs to Collection instances\n * @param subscriptions Mapping of source aliases to CollectionSubscription instances\n * @param callbacks Mapping of source aliases to lazy loading callbacks\n * @param lazySources Set of source aliases that should load data lazily\n * @param optimizableOrderByCollections Map of collection IDs to order-by optimization info\n * @param cache Optional cache for compiled subqueries (used internally for recursion)\n * @param queryMapping Optional mapping from optimized queries to original queries\n * @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata\n */\nexport function compileQuery(\n rawQuery: QueryIR,\n inputs: Record<string, KeyedStream>,\n collections: Record<string, Collection<any, any, any, any, any>>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n cache: QueryCache = new WeakMap(),\n queryMapping: QueryMapping = new WeakMap(),\n // For includes: parent key stream to inner-join with this query's FROM\n parentKeyStream?: KeyedStream,\n childCorrelationField?: PropRef,\n): CompilationResult {\n // Check if the original raw query has already been compiled\n const cachedResult = cache.get(rawQuery)\n if (cachedResult) {\n return cachedResult\n }\n\n // Validate the raw query BEFORE optimization to check user's original structure.\n // This must happen before optimization because the optimizer may create internal\n // subqueries (e.g., for predicate pushdown) that reuse aliases, which is fine.\n validateQueryStructure(rawQuery)\n\n // Optimize the query before compilation\n const { optimizedQuery, sourceWhereClauses } = optimizeQuery(rawQuery)\n // Use a mutable binding so we can shallow-clone select before includes mutation\n let query = optimizedQuery\n\n // Create mapping from optimized query to original for caching\n queryMapping.set(query, rawQuery)\n mapNestedQueries(query, rawQuery, queryMapping)\n\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Track alias to collection id relationships discovered during compilation.\n // This includes all user-declared aliases plus inner aliases from subqueries.\n const aliasToCollectionId: Record<string, string> = {}\n\n // Track alias remapping for subqueries (outer alias → inner alias)\n // e.g., when .join({ activeUser: subquery }) where subquery uses .from({ user: collection })\n // we store: aliasRemapping['activeUser'] = 'user'\n const aliasRemapping: Record<string, string> = {}\n\n // Create a map of source aliases to input streams.\n // Inputs MUST be keyed by alias (e.g., `{ employee: input1, manager: input2 }`),\n // not by collection ID. This enables per-alias subscriptions where different aliases\n // of the same collection (e.g., self-joins) maintain independent filtered streams.\n const sources: Record<string, KeyedStream> = {}\n\n // Process the FROM clause to get the main source\n const {\n alias: mainSource,\n input: mainInput,\n collectionId: mainCollectionId,\n } = processFrom(\n query.from,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n aliasToCollectionId,\n aliasRemapping,\n sourceWhereClauses,\n )\n sources[mainSource] = mainInput\n\n // If this is an includes child query, inner-join the raw input with parent keys.\n // This filters the child collection to only rows matching parents in the result set.\n // The inner join happens BEFORE namespace wrapping / WHERE / SELECT / ORDER BY,\n // so the child pipeline only processes rows that match parents.\n let filteredMainInput = mainInput\n if (parentKeyStream && childCorrelationField) {\n // Re-key child input by correlation field: [correlationValue, [childKey, childRow]]\n const childFieldPath = childCorrelationField.path.slice(1) // remove alias prefix\n const childRekeyed = mainInput.pipe(\n map(([key, row]: [unknown, any]) => {\n const correlationValue = getNestedValue(row, childFieldPath)\n return [correlationValue, [key, row]] as [unknown, [unknown, any]]\n }),\n )\n\n // Inner join: only children whose correlation key exists in parent keys pass through\n const joined = childRekeyed.pipe(joinOperator(parentKeyStream, `inner`))\n\n // Extract: [correlationValue, [[childKey, childRow], parentContext]] → [childKey, childRow]\n // Tag the row with __correlationKey for output routing\n // If parentSide is non-null (parent context projected), attach as __parentContext\n filteredMainInput = joined.pipe(\n filter(([_correlationValue, [childSide]]: any) => {\n return childSide != null\n }),\n map(([correlationValue, [childSide, parentSide]]: any) => {\n const [childKey, childRow] = childSide\n const tagged: any = { ...childRow, __correlationKey: correlationValue }\n if (parentSide != null) {\n tagged.__parentContext = parentSide\n }\n const effectiveKey =\n parentSide != null\n ? `${String(childKey)}::${JSON.stringify(parentSide)}`\n : childKey\n return [effectiveKey, tagged]\n }),\n )\n\n // Update sources so the rest of the pipeline uses the filtered input\n sources[mainSource] = filteredMainInput\n }\n\n // Prepare the initial pipeline with the main source wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = filteredMainInput.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n // If __parentContext exists (from parent-referencing includes), merge parent\n // aliases into the namespaced row so WHERE can resolve parent refs\n const { __parentContext, ...cleanRow } = row as any\n const nsRow: Record<string, any> = { [mainSource]: cleanRow }\n if (__parentContext) {\n Object.assign(nsRow, __parentContext)\n ;(nsRow as any).__parentContext = __parentContext\n }\n const ret = [key, nsRow] as [string, Record<string, typeof row>]\n return ret\n }),\n )\n\n // Process JOIN clauses if they exist\n if (query.join && query.join.length > 0) {\n pipeline = processJoins(\n pipeline,\n query.join,\n sources,\n mainCollectionId,\n mainSource,\n allInputs,\n cache,\n queryMapping,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n rawQuery,\n compileQuery,\n aliasToCollectionId,\n aliasRemapping,\n sourceWhereClauses,\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where && query.where.length > 0) {\n // Apply each WHERE condition as a filter (they are ANDed together)\n for (const where of query.where) {\n const whereExpression = getWhereExpression(where)\n const compiledWhere = compileExpression(whereExpression)\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return toBooleanPredicate(compiledWhere(namespacedRow))\n }),\n )\n }\n }\n\n // Process functional WHERE clauses if they exist\n if (query.fnWhere && query.fnWhere.length > 0) {\n for (const fnWhere of query.fnWhere) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return toBooleanPredicate(fnWhere(namespacedRow))\n }),\n )\n }\n }\n\n // Extract includes from SELECT, compile child pipelines, and replace with placeholders.\n // This must happen AFTER WHERE (so parent pipeline is filtered) but BEFORE processSelect\n // (so IncludesSubquery nodes are stripped before select compilation).\n const includesResults: Array<IncludesCompilationResult> = []\n const includesRoutingFns: Array<{\n fieldName: string\n getRouting: (nsRow: any) => {\n correlationKey: unknown\n parentContext: Record<string, any> | null\n }\n }> = []\n if (query.select) {\n const includesEntries = extractIncludesFromSelect(query.select)\n // Shallow-clone select before mutating so we don't modify the shared IR\n // (the optimizer copies select by reference, so rawQuery.select === query.select)\n if (includesEntries.length > 0) {\n query = { ...query, select: { ...query.select } }\n }\n for (const { key, subquery } of includesEntries) {\n // Branch parent pipeline: map to [correlationValue, parentContext]\n // When parentProjection exists, project referenced parent fields; otherwise null (zero overhead)\n const compiledCorrelation = compileExpression(subquery.correlationField)\n let parentKeys: any\n if (subquery.parentProjection && subquery.parentProjection.length > 0) {\n const compiledProjections = subquery.parentProjection.map((ref) => ({\n alias: ref.path[0]!,\n field: ref.path.slice(1),\n compiled: compileExpression(ref),\n }))\n parentKeys = pipeline.pipe(\n map(([_key, nsRow]: any) => {\n const parentContext: Record<string, Record<string, any>> = {}\n for (const proj of compiledProjections) {\n if (!parentContext[proj.alias]) {\n parentContext[proj.alias] = {}\n }\n const value = proj.compiled(nsRow)\n // Set nested field in the alias namespace\n let target = parentContext[proj.alias]!\n for (let i = 0; i < proj.field.length - 1; i++) {\n if (!target[proj.field[i]!]) {\n target[proj.field[i]!] = {}\n }\n target = target[proj.field[i]!]\n }\n target[proj.field[proj.field.length - 1]!] = value\n }\n return [compiledCorrelation(nsRow), parentContext] as any\n }),\n )\n } else {\n parentKeys = pipeline.pipe(\n map(\n ([_key, nsRow]: any) => [compiledCorrelation(nsRow), null] as any,\n ),\n )\n }\n\n // Deduplicate: when multiple parents share the same correlation key (and\n // parentContext), clamp multiplicity to 1 so the inner join doesn't\n // produce duplicate child entries that cause incorrect deletions.\n parentKeys = parentKeys.pipe(\n reduce((values: Array<[any, number]>) =>\n values.map(([v, mult]) => [v, mult > 0 ? 1 : 0] as [any, number]),\n ),\n )\n\n // If parent filters exist, append them to the child query's WHERE\n const childQuery =\n subquery.parentFilters && subquery.parentFilters.length > 0\n ? {\n ...subquery.query,\n where: [\n ...(subquery.query.where || []),\n ...subquery.parentFilters,\n ],\n }\n : subquery.query\n\n // Recursively compile child query WITH the parent key stream\n const childResult = compileQuery(\n childQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n parentKeys,\n subquery.childCorrelationField,\n )\n\n // Merge child's alias metadata into parent's\n Object.assign(aliasToCollectionId, childResult.aliasToCollectionId)\n Object.assign(aliasRemapping, childResult.aliasRemapping)\n\n includesResults.push({\n pipeline: childResult.pipeline,\n fieldName: subquery.fieldName,\n correlationField: subquery.correlationField,\n childCorrelationField: subquery.childCorrelationField,\n hasOrderBy: !!(\n subquery.query.orderBy && subquery.query.orderBy.length > 0\n ),\n childCompilationResult: childResult,\n parentProjection: subquery.parentProjection,\n materialization: subquery.materialization,\n scalarField: subquery.scalarField,\n })\n\n // Capture routing function for INCLUDES_ROUTING tagging\n if (subquery.parentProjection && subquery.parentProjection.length > 0) {\n const compiledProjs = subquery.parentProjection.map((ref) => ({\n alias: ref.path[0]!,\n field: ref.path.slice(1),\n compiled: compileExpression(ref),\n }))\n const compiledCorr = compiledCorrelation\n includesRoutingFns.push({\n fieldName: subquery.fieldName,\n getRouting: (nsRow: any) => {\n const parentContext: Record<string, Record<string, any>> = {}\n for (const proj of compiledProjs) {\n if (!parentContext[proj.alias]) {\n parentContext[proj.alias] = {}\n }\n const value = proj.compiled(nsRow)\n let target = parentContext[proj.alias]!\n for (let i = 0; i < proj.field.length - 1; i++) {\n if (!target[proj.field[i]!]) {\n target[proj.field[i]!] = {}\n }\n target = target[proj.field[i]!]\n }\n target[proj.field[proj.field.length - 1]!] = value\n }\n return { correlationKey: compiledCorr(nsRow), parentContext }\n },\n })\n } else {\n includesRoutingFns.push({\n fieldName: subquery.fieldName,\n getRouting: (nsRow: any) => ({\n correlationKey: compiledCorrelation(nsRow),\n parentContext: null,\n }),\n })\n }\n\n // Replace includes entry in select with a null placeholder\n replaceIncludesInSelect(query.select!, key)\n }\n }\n\n if (query.distinct && !query.fnSelect && !query.select) {\n throw new DistinctRequiresSelectError()\n }\n\n if (query.fnSelect && query.groupBy && query.groupBy.length > 0) {\n throw new FnSelectWithGroupByError()\n }\n\n // Process the SELECT clause early - always create $selected\n // This eliminates duplication and allows for DISTINCT implementation\n if (query.fnSelect) {\n // Handle functional select - apply the function to transform the row\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults = query.fnSelect!(namespacedRow)\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [string, typeof namespacedRow & { $selected: any }]\n }),\n )\n } else if (query.select) {\n pipeline = processSelect(pipeline, query.select, allInputs)\n } else {\n // If no SELECT clause, create $selected with the main table data\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults =\n !query.join && !query.groupBy\n ? namespacedRow[mainSource]\n : namespacedRow\n\n return [\n key,\n {\n ...namespacedRow,\n $selected: selectResults,\n },\n ] as [string, typeof namespacedRow & { $selected: any }]\n }),\n )\n }\n\n // Tag $selected with routing metadata for includes.\n // This lets collection-config-builder extract routing info (correlationKey + parentContext)\n // from parent results without depending on the user's select.\n if (includesRoutingFns.length > 0) {\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]: any) => {\n const routing: Record<\n string,\n { correlationKey: unknown; parentContext: Record<string, any> | null }\n > = {}\n for (const { fieldName, getRouting } of includesRoutingFns) {\n routing[fieldName] = getRouting(namespacedRow)\n }\n namespacedRow.$selected[INCLUDES_ROUTING] = routing\n return [key, namespacedRow]\n }),\n )\n }\n\n // Process the GROUP BY clause if it exists.\n // When in includes mode (parentKeyStream), pass mainSource so that groupBy\n // preserves __correlationKey for per-parent aggregation.\n const groupByMainSource = parentKeyStream ? mainSource : undefined\n if (query.groupBy && query.groupBy.length > 0) {\n pipeline = processGroupBy(\n pipeline,\n query.groupBy,\n query.having,\n query.select,\n query.fnHaving,\n mainCollectionId,\n groupByMainSource,\n )\n } else if (query.select) {\n // Check if SELECT contains aggregates but no GROUP BY (implicit single-group aggregation)\n const hasAggregates = Object.values(query.select).some(\n (expr) => expr.type === `agg` || containsAggregate(expr),\n )\n if (hasAggregates) {\n // Handle implicit single-group aggregation\n pipeline = processGroupBy(\n pipeline,\n [], // Empty group by means single group\n query.having,\n query.select,\n query.fnHaving,\n mainCollectionId,\n groupByMainSource,\n )\n }\n }\n\n // Process the HAVING clause if it exists (only applies after GROUP BY)\n if (query.having && (!query.groupBy || query.groupBy.length === 0)) {\n // Check if we have aggregates in SELECT that would trigger implicit grouping\n const hasAggregates = query.select\n ? Object.values(query.select).some((expr) => expr.type === `agg`)\n : false\n\n if (!hasAggregates) {\n throw new HavingRequiresGroupByError()\n }\n }\n\n // Process functional HAVING clauses outside of GROUP BY (treat as additional WHERE filters)\n if (\n query.fnHaving &&\n query.fnHaving.length > 0 &&\n (!query.groupBy || query.groupBy.length === 0)\n ) {\n // If there's no GROUP BY but there are fnHaving clauses, apply them as filters\n for (const fnHaving of query.fnHaving) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnHaving(namespacedRow)\n }),\n )\n }\n }\n\n // Process the DISTINCT clause if it exists\n if (query.distinct) {\n pipeline = pipeline.pipe(distinct(([_key, row]) => row.$selected))\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy && query.orderBy.length > 0) {\n // When in includes mode with limit/offset, use grouped ordering so that\n // the limit is applied per parent (per correlation key), not globally.\n const includesGroupKeyFn =\n parentKeyStream &&\n (query.limit !== undefined || query.offset !== undefined)\n ? (_key: unknown, row: unknown) => {\n const correlationKey = (row as any)?.[mainSource]?.__correlationKey\n const parentContext = (row as any)?.__parentContext\n if (parentContext != null) {\n return JSON.stringify([correlationKey, parentContext])\n }\n return correlationKey\n }\n : undefined\n\n const orderedPipeline = processOrderBy(\n rawQuery,\n pipeline,\n query.orderBy,\n query.select || {},\n collections[mainCollectionId]!,\n optimizableOrderByCollections,\n setWindowFn,\n query.limit,\n query.offset,\n includesGroupKeyFn,\n )\n\n // Final step: extract the $selected and include orderBy index\n const resultPipeline: ResultStream = orderedPipeline.pipe(\n map(([key, [row, orderByIndex]]) => {\n // Extract the final results from $selected and include orderBy index\n const raw = (row as any).$selected\n const finalResults = attachVirtualPropsToSelected(\n unwrapValue(raw),\n row as Record<string, any>,\n )\n // When in includes mode, embed the correlation key and parentContext\n if (parentKeyStream) {\n const correlationKey = (row as any)[mainSource]?.__correlationKey\n const parentContext = (row as any).__parentContext ?? null\n // Strip internal routing properties that may leak via spread selects\n delete finalResults.__correlationKey\n delete finalResults.__parentContext\n return [\n key,\n [finalResults, orderByIndex, correlationKey, parentContext],\n ] as any\n }\n return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]\n }),\n ) as ResultStream\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult: CompilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n includes: includesResults.length > 0 ? includesResults : undefined,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new LimitOffsetRequireOrderByError()\n }\n\n // Final step: extract the $selected and return tuple format (no orderBy)\n const resultPipeline: ResultStream = pipeline.pipe(\n map(([key, row]) => {\n // Extract the final results from $selected and return [key, [results, undefined]]\n const raw = (row as any).$selected\n const finalResults = attachVirtualPropsToSelected(\n unwrapValue(raw),\n row as Record<string, any>,\n )\n // When in includes mode, embed the correlation key and parentContext\n if (parentKeyStream) {\n const correlationKey = (row as any)[mainSource]?.__correlationKey\n const parentContext = (row as any).__parentContext ?? null\n // Strip internal routing properties that may leak via spread selects\n delete finalResults.__correlationKey\n delete finalResults.__parentContext\n return [\n key,\n [finalResults, undefined, correlationKey, parentContext],\n ] as any\n }\n return [key, [finalResults, undefined]] as [\n unknown,\n [any, string | undefined],\n ]\n }),\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult: CompilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n includes: includesResults.length > 0 ? includesResults : undefined,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n}\n\n/**\n * Collects aliases used for DIRECT collection references (not subqueries).\n * Used to validate that subqueries don't reuse parent query collection aliases.\n * Only direct CollectionRef aliases matter - QueryRef aliases don't cause conflicts.\n */\nfunction collectDirectCollectionAliases(query: QueryIR): Set<string> {\n const aliases = new Set<string>()\n\n // Collect FROM alias only if it's a direct collection reference\n if (query.from.type === `collectionRef`) {\n aliases.add(query.from.alias)\n }\n\n // Collect JOIN aliases only for direct collection references\n if (query.join) {\n for (const joinClause of query.join) {\n if (joinClause.from.type === `collectionRef`) {\n aliases.add(joinClause.from.alias)\n }\n }\n }\n\n return aliases\n}\n\n/**\n * Validates the structure of a query and its subqueries.\n * Checks that subqueries don't reuse collection aliases from parent queries.\n * This must be called on the RAW query before optimization.\n */\nfunction validateQueryStructure(\n query: QueryIR,\n parentCollectionAliases: Set<string> = new Set(),\n): void {\n // Collect direct collection aliases from this query level\n const currentLevelAliases = collectDirectCollectionAliases(query)\n\n // Check if any current alias conflicts with parent aliases\n for (const alias of currentLevelAliases) {\n if (parentCollectionAliases.has(alias)) {\n throw new DuplicateAliasInSubqueryError(\n alias,\n Array.from(parentCollectionAliases),\n )\n }\n }\n\n // Combine parent and current aliases for checking nested subqueries\n const combinedAliases = new Set([\n ...parentCollectionAliases,\n ...currentLevelAliases,\n ])\n\n // Recursively validate FROM subquery\n if (query.from.type === `queryRef`) {\n validateQueryStructure(query.from.query, combinedAliases)\n }\n\n // Recursively validate JOIN subqueries\n if (query.join) {\n for (const joinClause of query.join) {\n if (joinClause.from.type === `queryRef`) {\n validateQueryStructure(joinClause.from.query, combinedAliases)\n }\n }\n }\n}\n\n/**\n * Processes the FROM clause, handling direct collection references and subqueries.\n * Populates `aliasToCollectionId` and `aliasRemapping` for per-alias subscription tracking.\n */\nfunction processFrom(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n collections: Record<string, Collection>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n setWindowFn: (windowFn: (options: WindowOptions) => void) => void,\n cache: QueryCache,\n queryMapping: QueryMapping,\n aliasToCollectionId: Record<string, string>,\n aliasRemapping: Record<string, string>,\n sourceWhereClauses: Map<string, BasicExpression<boolean>>,\n): { alias: string; input: KeyedStream; collectionId: string } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.alias]\n if (!input) {\n throw new CollectionInputNotFoundError(\n from.alias,\n from.collection.id,\n Object.keys(allInputs),\n )\n }\n aliasToCollectionId[from.alias] = from.collection.id\n return { alias: from.alias, input, collectionId: from.collection.id }\n }\n case `queryRef`: {\n // Find the original query for caching purposes\n const originalQuery = queryMapping.get(from.query) || from.query\n\n // Recursively compile the sub-query with cache\n const subQueryResult = compileQuery(\n originalQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n setWindowFn,\n cache,\n queryMapping,\n )\n\n // Pull up alias mappings from subquery to parent scope.\n // This includes both the innermost alias-to-collection mappings AND\n // any existing remappings from nested subquery levels.\n Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId)\n Object.assign(aliasRemapping, subQueryResult.aliasRemapping)\n\n // Pull up source WHERE clauses from subquery to parent scope.\n // This enables loadSubset to receive the correct where clauses for subquery collections.\n //\n // IMPORTANT: Skip pull-up for optimizer-created subqueries. These are detected when:\n // 1. The outer alias (from.alias) matches the inner alias (from.query.from.alias)\n // 2. The subquery was found in queryMapping (it's a user-defined subquery, not optimizer-created)\n //\n // For optimizer-created subqueries, the parent already has the sourceWhereClauses\n // extracted from the original raw query, so pulling up would be redundant.\n // More importantly, pulling up for optimizer-created subqueries can cause issues\n // when the optimizer has restructured the query.\n const isUserDefinedSubquery = queryMapping.has(from.query)\n const subqueryFromAlias = from.query.from.alias\n const isOptimizerCreated =\n !isUserDefinedSubquery && from.alias === subqueryFromAlias\n\n if (!isOptimizerCreated) {\n for (const [alias, whereClause] of subQueryResult.sourceWhereClauses) {\n sourceWhereClauses.set(alias, whereClause)\n }\n }\n\n // Create a FLATTENED remapping from outer alias to innermost alias.\n // For nested subqueries, this ensures one-hop lookups (not recursive chains).\n //\n // Example with 3-level nesting:\n // Inner: .from({ user: usersCollection })\n // Middle: .from({ activeUser: innerSubquery }) → creates: activeUser → user\n // Outer: .from({ author: middleSubquery }) → creates: author → user (not author → activeUser)\n //\n // The key insight: We search through the PULLED-UP aliasToCollectionId (which contains\n // the innermost 'user' alias), so we always map directly to the deepest level.\n // This means aliasRemapping[alias] is always a single lookup, never recursive.\n // Needed for subscription resolution during lazy loading.\n const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(\n (alias) =>\n subQueryResult.aliasToCollectionId[alias] ===\n subQueryResult.collectionId,\n )\n if (innerAlias && innerAlias !== from.alias) {\n aliasRemapping[from.alias] = innerAlias\n }\n\n // Extract the pipeline from the compilation result\n const subQueryInput = subQueryResult.pipeline\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n // Unwrap Value expressions that might have leaked through as the entire row\n const unwrapped = unwrapValue(value)\n return [key, unwrapped] as [unknown, any]\n }),\n )\n\n return {\n alias: from.alias,\n input: extractedInput,\n collectionId: subQueryResult.collectionId,\n }\n }\n default:\n throw new UnsupportedFromTypeError((from as any).type)\n }\n}\n\n// Helper to check if a value is a Value expression\nfunction isValue(raw: any): boolean {\n return (\n raw instanceof ValClass ||\n (raw && typeof raw === `object` && `type` in raw && raw.type === `val`)\n )\n}\n\n// Helper to unwrap a Value expression or return the value itself\nfunction unwrapValue(value: any): any {\n return isValue(value) ? value.value : value\n}\n\nfunction attachVirtualPropsToSelected(\n selected: any,\n row: Record<string, any>,\n): any {\n if (!selected || typeof selected !== `object`) {\n return selected\n }\n\n let needsMerge = false\n for (const prop of VIRTUAL_PROP_NAMES) {\n if (selected[prop] == null && prop in row) {\n needsMerge = true\n break\n }\n }\n\n if (!needsMerge) {\n return selected\n }\n\n for (const prop of VIRTUAL_PROP_NAMES) {\n if (selected[prop] == null && prop in row) {\n selected[prop] = row[prop]\n }\n }\n\n return selected\n}\n\n/**\n * Recursively maps optimized subqueries to their original queries for proper caching.\n * This ensures that when we encounter the same QueryRef object in different contexts,\n * we can find the original query to check the cache.\n */\nfunction mapNestedQueries(\n optimizedQuery: QueryIR,\n originalQuery: QueryIR,\n queryMapping: QueryMapping,\n): void {\n // Map the FROM clause if it's a QueryRef\n if (\n optimizedQuery.from.type === `queryRef` &&\n originalQuery.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedQuery.from.query, originalQuery.from.query)\n // Recursively map nested queries\n mapNestedQueries(\n optimizedQuery.from.query,\n originalQuery.from.query,\n queryMapping,\n )\n }\n\n // Map JOIN clauses if they exist\n if (optimizedQuery.join && originalQuery.join) {\n for (\n let i = 0;\n i < optimizedQuery.join.length && i < originalQuery.join.length;\n i++\n ) {\n const optimizedJoin = optimizedQuery.join[i]!\n const originalJoin = originalQuery.join[i]!\n\n if (\n optimizedJoin.from.type === `queryRef` &&\n originalJoin.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedJoin.from.query, originalJoin.from.query)\n // Recursively map nested queries in joins\n mapNestedQueries(\n optimizedJoin.from.query,\n originalJoin.from.query,\n queryMapping,\n )\n }\n }\n }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n\n/**\n * Walks a Select object to find IncludesSubquery entries at the top level.\n * Throws if an IncludesSubquery is found nested inside a sub-object, since\n * the compiler only supports includes at the top level of a select.\n */\nfunction extractIncludesFromSelect(\n select: Record<string, any>,\n): Array<{ key: string; subquery: IncludesSubquery }> {\n const results: Array<{ key: string; subquery: IncludesSubquery }> = []\n for (const [key, value] of Object.entries(select)) {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) continue\n if (value instanceof IncludesSubquery) {\n results.push({ key, subquery: value })\n } else if (isNestedSelectObject(value)) {\n // Check nested objects for IncludesSubquery — not supported yet\n assertNoNestedIncludes(value, key)\n }\n }\n return results\n}\n\n/** Check if a value is a nested plain object in a select (not an IR expression node) */\nfunction isNestedSelectObject(value: any): value is Record<string, any> {\n return (\n value != null &&\n typeof value === `object` &&\n !Array.isArray(value) &&\n typeof value.type !== `string`\n )\n}\n\nfunction assertNoNestedIncludes(\n obj: Record<string, any>,\n parentPath: string,\n): void {\n for (const [key, value] of Object.entries(obj)) {\n if (key.startsWith(`__SPREAD_SENTINEL__`)) continue\n if (value instanceof IncludesSubquery) {\n throw new Error(\n `Includes subqueries must be at the top level of select(). ` +\n `Found nested includes at \"${parentPath}.${key}\".`,\n )\n }\n if (isNestedSelectObject(value)) {\n assertNoNestedIncludes(value, `${parentPath}.${key}`)\n }\n }\n}\n\n/**\n * Replaces an IncludesSubquery entry in the select object with a null Value placeholder.\n * This ensures processSelect() doesn't encounter it.\n */\nfunction replaceIncludesInSelect(\n select: Record<string, any>,\n key: string,\n): void {\n select[key] = new ValClass(null)\n}\n\n/**\n * Gets a nested value from an object by path segments.\n * For v1 with single-level correlation fields (e.g., `projectId`), it's just `obj[path[0]]`.\n */\nfunction getNestedValue(obj: any, path: Array<string>): any {\n let value = obj\n for (const segment of path) {\n if (value == null) return value\n value = value[segment]\n }\n return value\n}\n\nexport type CompileQueryFn = typeof compileQuery\n"],"names":["optimizeQuery","map","joinOperator","filter","processJoins","getWhereExpression","compileExpression","toBooleanPredicate","reduce","DistinctRequiresSelectError","FnSelectWithGroupByError","processSelect","processGroupBy","containsAggregate","HavingRequiresGroupByError","distinct","processOrderBy","resultPipeline","result","compilationResult","LimitOffsetRequireOrderByError","DuplicateAliasInSubqueryError","CollectionInputNotFoundError","UnsupportedFromTypeError","ValClass","VIRTUAL_PROP_NAMES","select","IncludesSubquery"],"mappings":"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