UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines • 17.7 kB
{"version":3,"file":"select.cjs","sources":["../../../../src/query/compiler/select.ts"],"sourcesContent":["import { map } from '@tanstack/db-ivm'\nimport {\n ConditionalSelect,\n PropRef,\n Value as ValClass,\n isExpressionLike,\n} from '../ir.js'\nimport { UnsafeAliasPathError } from '../../errors.js'\nimport { compileExpression, isCaseWhenConditionTrue } from './evaluators.js'\nimport type { BasicExpression, Select, SelectValueExpression } 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 | { kind: `object`; targetPath: Array<string> }\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\nconst UNSAFE_ALIAS_SEGMENTS = new Set([`__proto__`, `prototype`, `constructor`])\n\nfunction assertSafeAliasSegments(segments: ReadonlyArray<string>): void {\n for (const seg of segments) {\n if (UNSAFE_ALIAS_SEGMENTS.has(seg)) {\n throw new UnsafeAliasPathError(seg)\n }\n }\n}\n\n// A source spread copies enumerable own data, including user symbols. Defining\n// fields avoids invoking inherited setters for ordinary source property names.\nfunction mergeSourceFields(target: object, source: object): void {\n for (const key of Reflect.ownKeys(source)) {\n if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue\n defineSelectField(target, key, unwrapVal(Reflect.get(source, key)))\n }\n}\n\n// Only containers created by this evaluation may be modified. A spread or ref\n// can borrow a source object, so copy each borrowed destination at most once.\nfunction ownedDestination(\n result: object,\n path: ReadonlyArray<string>,\n owned: WeakSet<object>,\n): object {\n let cursor = result\n for (const segment of path) {\n const next = Object.hasOwn(cursor, segment)\n ? Reflect.get(cursor, segment)\n : undefined\n if (next != null && typeof next === `object` && owned.has(next)) {\n cursor = next\n continue\n }\n const destination = Array.isArray(next)\n ? new Array<unknown>(next.length)\n : {}\n if (Array.isArray(next)) {\n Object.setPrototypeOf(destination, Object.getPrototypeOf(next))\n }\n if (next != null && typeof next === `object`) {\n mergeSourceFields(destination, next)\n }\n owned.add(destination)\n defineSelectField(cursor, segment, destination)\n cursor = destination\n }\n return cursor\n}\n\nfunction defineSelectField(\n target: object,\n key: PropertyKey,\n value: unknown,\n): void {\n // Owned fields are normalized data. Preserve intrinsic array-length attributes\n // when replacing its value; only newly created fields need ordinary defaults.\n Object.defineProperty(\n target,\n key,\n Object.hasOwn(target, key)\n ? { value }\n : { value, enumerable: true, configurable: true, writable: true },\n )\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 owned: WeakSet<object>,\n): void {\n assertSafeAliasSegments(op.targetPath)\n const value = op.source(namespacedRow)\n if (value && typeof value === `object`) {\n mergeSourceFields(\n ownedDestination(selectResults, op.targetPath, owned),\n value,\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 owned: WeakSet<object>,\n): void {\n // Support nested alias paths like \"meta.author.name\"\n const path = op.alias.split(`.`)\n assertSafeAliasSegments(path)\n const target = ownedDestination(selectResults, path.slice(0, -1), owned)\n defineSelectField(\n target,\n path[path.length - 1]!,\n unwrapVal(op.compiled(namespacedRow)),\n )\n}\n\nfunction evaluateSelectObject(\n namespacedRow: NamespacedRow,\n ops: Array<SelectOp>,\n): Record<string, unknown> {\n const selectResults: Record<string, unknown> = {}\n const owned = new WeakSet<object>([selectResults])\n for (const op of ops) {\n if (op.kind === `merge`) {\n processMerge(op, namespacedRow, selectResults, owned)\n } else if (op.kind === `field`) {\n processNonMergeOp(op, namespacedRow, selectResults, owned)\n } else {\n assertSafeAliasSegments(op.targetPath)\n const target = ownedDestination(\n selectResults,\n op.targetPath.slice(0, -1),\n owned,\n )\n const destination = {}\n owned.add(destination)\n defineSelectField(\n target,\n op.targetPath[op.targetPath.length - 1]!,\n destination,\n )\n }\n }\n return selectResults\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 = evaluateSelectObject(namespacedRow, ops)\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 if (!isNestedSelectObject(select)) {\n const compiled = compileSelectValue(select as SelectValueExpression)\n return pipeline.pipe(\n map(([key, namespacedRow]) => [\n key,\n { ...namespacedRow, $selected: compiled(namespacedRow) },\n ]),\n ) as NamespacedAndKeyedStream\n }\n\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\nfunction compileSelectObject(\n obj: Record<string, any>,\n): (row: NamespacedRow) => any {\n const ops: Array<SelectOp> = []\n addFromObject([], obj, ops)\n\n return (row) => evaluateSelectObject(row, ops)\n}\n\nfunction compileSelectValue(\n value: SelectValueExpression | null | undefined,\n): (row: NamespacedRow) => any {\n if (value == null) {\n return () => value\n }\n\n if (isConditionalSelectValue(value)) {\n return compileConditionalSelect(value)\n }\n\n if (value instanceof ValClass) {\n return () => value.value\n }\n\n if (value.type === `includesSubquery`) {\n return () => null\n }\n\n if (isNestedSelectObject(value)) {\n return compileSelectObject(value)\n }\n\n if (!isExpressionLike(value)) {\n return () => value\n }\n\n return compileExpression(value as BasicExpression)\n}\n\nfunction compileConditionalSelect(\n conditional: ConditionalSelect,\n): (row: NamespacedRow) => any {\n const branches = conditional.branches.map((branch) => ({\n condition: compileExpression(branch.condition),\n value: compileSelectValue(branch.value),\n }))\n const defaultFn =\n conditional.defaultValue === undefined\n ? undefined\n : compileSelectValue(conditional.defaultValue)\n\n return (row) => {\n for (const branch of branches) {\n if (isCaseWhenConditionTrue(branch.condition(row))) {\n return branch.value(row)\n }\n }\n\n return defaultFn !== undefined ? defaultFn(row) : null\n }\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 assertSafeAliasSegments(key.split(`.`))\n }\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 path = pathStr.split(`.`)\n const expr = isRefExpr\n ? (value as BasicExpression)\n : (new PropRef(path, path[0]) 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 (isConditionalSelectValue(expression)) {\n ops.push({\n kind: `field`,\n alias: [...prefixPath, key].join(`.`),\n compiled: compileConditionalSelect(expression),\n })\n continue\n }\n\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 // An authored object replaces any value supplied by an earlier spread.\n const targetPath = [...prefixPath, ...key.split(`.`)]\n ops.push({ kind: `object`, targetPath })\n addFromObject(targetPath, expression, ops)\n continue\n }\n\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\nfunction isConditionalSelectValue(value: unknown): value is ConditionalSelect {\n return (\n value instanceof ConditionalSelect ||\n (value != null &&\n typeof value === `object` &&\n (value as { type?: unknown }).type === `conditionalSelect` &&\n Array.isArray((value as { branches?: unknown }).branches))\n )\n}\n"],"names":["ValClass","UnsafeAliasPathError","map","isExpressionLike","compileExpression","isCaseWhenConditionTrue","PropRef","ConditionalSelect"],"mappings":"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"}