{"version":3,"file":"select.cjs","sources":["../../../../src/query/compiler/select.ts"],"sourcesContent":["import { map } from '@tanstack/db-ivm'\nimport { PropRef, Value as ValClass, isExpressionLike } from '../ir.js'\nimport { AggregateNotSupportedError } from '../../errors.js'\nimport { compileExpression } from './evaluators.js'\nimport { containsAggregate } from './group-by.js'\nimport type { Aggregate, BasicExpression, Select } from '../ir.js'\nimport type {\n  KeyedStream,\n  NamespacedAndKeyedStream,\n  NamespacedRow,\n} from '../../types.js'\n\n/**\n * Type for operations array used in select processing\n */\ntype SelectOp =\n  | {\n      kind: `merge`\n      targetPath: Array<string>\n      source: (row: NamespacedRow) => any\n    }\n  | { kind: `field`; alias: string; compiled: (row: NamespacedRow) => any }\n\n/**\n * Unwraps any Value expressions\n */\nfunction unwrapVal(input: any): any {\n  if (input instanceof ValClass) return input.value\n  return input\n}\n\n/**\n * Processes a merge operation by merging source values into the target path\n */\nfunction processMerge(\n  op: Extract<SelectOp, { kind: `merge` }>,\n  namespacedRow: NamespacedRow,\n  selectResults: Record<string, any>,\n): void {\n  const value = op.source(namespacedRow)\n  if (value && typeof value === `object`) {\n    // Ensure target object exists\n    let cursor: any = selectResults\n    const path = op.targetPath\n    if (path.length === 0) {\n      // Top-level merge\n      for (const [k, v] of Object.entries(value)) {\n        selectResults[k] = unwrapVal(v)\n      }\n    } else {\n      for (let i = 0; i < path.length; i++) {\n        const seg = path[i]!\n        if (i === path.length - 1) {\n          const dest = (cursor[seg] ??= {})\n          if (typeof dest === `object`) {\n            for (const [k, v] of Object.entries(value)) {\n              dest[k] = unwrapVal(v)\n            }\n          }\n        } else {\n          const next = cursor[seg]\n          if (next == null || typeof next !== `object`) {\n            cursor[seg] = {}\n          }\n          cursor = cursor[seg]\n        }\n      }\n    }\n  }\n}\n\n/**\n * Processes a non-merge operation by setting the field value at the specified alias path\n */\nfunction processNonMergeOp(\n  op: Extract<SelectOp, { kind: `field` }>,\n  namespacedRow: NamespacedRow,\n  selectResults: Record<string, any>,\n): void {\n  // Support nested alias paths like \"meta.author.name\"\n  const path = op.alias.split(`.`)\n  if (path.length === 1) {\n    selectResults[op.alias] = op.compiled(namespacedRow)\n  } else {\n    let cursor: any = selectResults\n    for (let i = 0; i < path.length - 1; i++) {\n      const seg = path[i]!\n      const next = cursor[seg]\n      if (next == null || typeof next !== `object`) {\n        cursor[seg] = {}\n      }\n      cursor = cursor[seg]\n    }\n    cursor[path[path.length - 1]!] = unwrapVal(op.compiled(namespacedRow))\n  }\n}\n\n/**\n * Processes a single row to generate select results\n */\nfunction processRow(\n  [key, namespacedRow]: [unknown, NamespacedRow],\n  ops: Array<SelectOp>,\n): [unknown, typeof namespacedRow & { $selected: any }] {\n  const selectResults: Record<string, any> = {}\n\n  for (const op of ops) {\n    if (op.kind === `merge`) {\n      processMerge(op, namespacedRow, selectResults)\n    } else {\n      processNonMergeOp(op, namespacedRow, selectResults)\n    }\n  }\n\n  // Return the namespaced row with $selected added\n  return [\n    key,\n    {\n      ...namespacedRow,\n      $selected: selectResults,\n    },\n  ] as [unknown, typeof namespacedRow & { $selected: typeof selectResults }]\n}\n\n/**\n * Processes the SELECT clause and places results in $selected\n * while preserving the original namespaced row for ORDER BY access\n */\nexport function processSelect(\n  pipeline: NamespacedAndKeyedStream,\n  select: Select,\n  _allInputs: Record<string, KeyedStream>,\n): NamespacedAndKeyedStream {\n  // Build ordered operations to preserve authoring order (spreads and fields)\n  const ops: Array<SelectOp> = []\n\n  addFromObject([], select, ops)\n\n  return pipeline.pipe(map((row) => processRow(row, ops)))\n}\n\n/**\n * Helper function to check if an expression is an aggregate\n */\nfunction isAggregateExpression(\n  expr: BasicExpression | Aggregate,\n): expr is Aggregate {\n  return expr.type === `agg`\n}\n\n/**\n * Processes a single argument in a function context\n */\nexport function processArgument(\n  arg: BasicExpression | Aggregate,\n  namespacedRow: NamespacedRow,\n): any {\n  if (isAggregateExpression(arg)) {\n    throw new AggregateNotSupportedError()\n  }\n\n  // Pre-compile the expression and evaluate immediately\n  const compiledExpression = compileExpression(arg)\n  const value = compiledExpression(namespacedRow)\n\n  return value\n}\n\n/**\n * Helper function to check if an object is a nested select object\n *\n * .select({\n *   id: users.id,\n *   profile: { // <-- this is a nested select object\n *     name: users.name,\n *     email: users.email\n *   }\n * })\n */\nfunction isNestedSelectObject(obj: any): boolean {\n  return obj && typeof obj === `object` && !isExpressionLike(obj)\n}\n\n/**\n * Helper function to process select objects and build operations array\n */\nfunction addFromObject(\n  prefixPath: Array<string>,\n  obj: any,\n  ops: Array<SelectOp>,\n) {\n  for (const [key, value] of Object.entries(obj)) {\n    if (key.startsWith(`__SPREAD_SENTINEL__`)) {\n      const rest = key.slice(`__SPREAD_SENTINEL__`.length)\n      const splitIndex = rest.lastIndexOf(`__`)\n      const pathStr = splitIndex >= 0 ? rest.slice(0, splitIndex) : rest\n      const isRefExpr =\n        value &&\n        typeof value === `object` &&\n        `type` in (value as any) &&\n        (value as any).type === `ref`\n      if (pathStr.includes(`.`) || isRefExpr) {\n        // Merge into the current destination (prefixPath) from the referenced source path\n        const targetPath = [...prefixPath]\n        const expr = isRefExpr\n          ? (value as BasicExpression)\n          : (new PropRef(pathStr.split(`.`)) as BasicExpression)\n        const compiled = compileExpression(expr)\n        ops.push({ kind: `merge`, targetPath, source: compiled })\n      } else {\n        // Table-level: pathStr is the alias; merge from namespaced row at the current prefix\n        const tableAlias = pathStr\n        const targetPath = [...prefixPath]\n        ops.push({\n          kind: `merge`,\n          targetPath,\n          source: (row) => (row as any)[tableAlias],\n        })\n      }\n      continue\n    }\n\n    const expression = value as any\n    if (expression && expression.type === `includesSubquery`) {\n      // Placeholder — field will be set to a child Collection by the output layer\n      ops.push({\n        kind: `field`,\n        alias: [...prefixPath, key].join(`.`),\n        compiled: () => null,\n      })\n      continue\n    }\n    if (isNestedSelectObject(expression)) {\n      // Nested selection object\n      addFromObject([...prefixPath, key], expression, ops)\n      continue\n    }\n\n    if (isAggregateExpression(expression) || containsAggregate(expression)) {\n      // Placeholder for group-by processing later.\n      // Both plain aggregates (count(...)) and expressions wrapping\n      // aggregates (coalesce(count(...), 0)) are deferred to processGroupBy.\n      ops.push({\n        kind: `field`,\n        alias: [...prefixPath, key].join(`.`),\n        compiled: () => null,\n      })\n    } else {\n      if (expression === undefined || !isExpressionLike(expression)) {\n        ops.push({\n          kind: `field`,\n          alias: [...prefixPath, key].join(`.`),\n          compiled: () => expression,\n        })\n        continue\n      }\n      // If the expression is a Value wrapper, embed the literal to avoid re-compilation mishaps\n      if (expression instanceof ValClass) {\n        const val = expression.value\n        ops.push({\n          kind: `field`,\n          alias: [...prefixPath, key].join(`.`),\n          compiled: () => val,\n        })\n      } else {\n        ops.push({\n          kind: `field`,\n          alias: [...prefixPath, key].join(`.`),\n          compiled: compileExpression(expression as BasicExpression),\n        })\n      }\n    }\n  }\n}\n"],"names":["ValClass","map","isExpressionLike","PropRef","compileExpression","containsAggregate"],"mappings":"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"}