UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

246 lines (245 loc) • 7.74 kB
import { map } from "@tanstack/db-ivm"; import { isExpressionLike, Value, PropRef, ConditionalSelect } from "../ir.js"; import { UnsafeAliasPathError } from "../../errors.js"; import { compileExpression, isCaseWhenConditionTrue } from "./evaluators.js"; function unwrapVal(input) { if (input instanceof Value) return input.value; return input; } const UNSAFE_ALIAS_SEGMENTS = /* @__PURE__ */ new Set([`__proto__`, `prototype`, `constructor`]); function assertSafeAliasSegments(segments) { for (const seg of segments) { if (UNSAFE_ALIAS_SEGMENTS.has(seg)) { throw new UnsafeAliasPathError(seg); } } } function mergeSourceFields(target, source) { for (const key of Reflect.ownKeys(source)) { if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; defineSelectField(target, key, unwrapVal(Reflect.get(source, key))); } } function ownedDestination(result, path, owned) { let cursor = result; for (const segment of path) { const next = Object.hasOwn(cursor, segment) ? Reflect.get(cursor, segment) : void 0; if (next != null && typeof next === `object` && owned.has(next)) { cursor = next; continue; } const destination = Array.isArray(next) ? new Array(next.length) : {}; if (Array.isArray(next)) { Object.setPrototypeOf(destination, Object.getPrototypeOf(next)); } if (next != null && typeof next === `object`) { mergeSourceFields(destination, next); } owned.add(destination); defineSelectField(cursor, segment, destination); cursor = destination; } return cursor; } function defineSelectField(target, key, value) { Object.defineProperty( target, key, Object.hasOwn(target, key) ? { value } : { value, enumerable: true, configurable: true, writable: true } ); } function processMerge(op, namespacedRow, selectResults, owned) { assertSafeAliasSegments(op.targetPath); const value = op.source(namespacedRow); if (value && typeof value === `object`) { mergeSourceFields( ownedDestination(selectResults, op.targetPath, owned), value ); } } function processNonMergeOp(op, namespacedRow, selectResults, owned) { const path = op.alias.split(`.`); assertSafeAliasSegments(path); const target = ownedDestination(selectResults, path.slice(0, -1), owned); defineSelectField( target, path[path.length - 1], unwrapVal(op.compiled(namespacedRow)) ); } function evaluateSelectObject(namespacedRow, ops) { const selectResults = {}; const owned = new WeakSet([selectResults]); for (const op of ops) { if (op.kind === `merge`) { processMerge(op, namespacedRow, selectResults, owned); } else if (op.kind === `field`) { processNonMergeOp(op, namespacedRow, selectResults, owned); } else { assertSafeAliasSegments(op.targetPath); const target = ownedDestination( selectResults, op.targetPath.slice(0, -1), owned ); const destination = {}; owned.add(destination); defineSelectField( target, op.targetPath[op.targetPath.length - 1], destination ); } } return selectResults; } function processRow([key, namespacedRow], ops) { const selectResults = evaluateSelectObject(namespacedRow, ops); return [ key, { ...namespacedRow, $selected: selectResults } ]; } function processSelect(pipeline, select, _allInputs) { if (!isNestedSelectObject(select)) { const compiled = compileSelectValue(select); return pipeline.pipe( map(([key, namespacedRow]) => [ key, { ...namespacedRow, $selected: compiled(namespacedRow) } ]) ); } const ops = []; addFromObject([], select, ops); return pipeline.pipe(map((row) => processRow(row, ops))); } function compileSelectObject(obj) { const ops = []; addFromObject([], obj, ops); return (row) => evaluateSelectObject(row, ops); } function compileSelectValue(value) { if (value == null) { return () => value; } if (isConditionalSelectValue(value)) { return compileConditionalSelect(value); } if (value instanceof Value) { return () => value.value; } if (value.type === `includesSubquery`) { return () => null; } if (isNestedSelectObject(value)) { return compileSelectObject(value); } if (!isExpressionLike(value)) { return () => value; } return compileExpression(value); } function compileConditionalSelect(conditional) { const branches = conditional.branches.map((branch) => ({ condition: compileExpression(branch.condition), value: compileSelectValue(branch.value) })); const defaultFn = conditional.defaultValue === void 0 ? void 0 : compileSelectValue(conditional.defaultValue); return (row) => { for (const branch of branches) { if (isCaseWhenConditionTrue(branch.condition(row))) { return branch.value(row); } } return defaultFn !== void 0 ? defaultFn(row) : null; }; } function isNestedSelectObject(obj) { return obj && typeof obj === `object` && !isExpressionLike(obj); } function addFromObject(prefixPath, obj, ops) { for (const [key, value] of Object.entries(obj)) { if (!key.startsWith(`__SPREAD_SENTINEL__`)) { assertSafeAliasSegments(key.split(`.`)); } if (key.startsWith(`__SPREAD_SENTINEL__`)) { const rest = key.slice(`__SPREAD_SENTINEL__`.length); const splitIndex = rest.lastIndexOf(`__`); const pathStr = splitIndex >= 0 ? rest.slice(0, splitIndex) : rest; const isRefExpr = value && typeof value === `object` && `type` in value && value.type === `ref`; if (pathStr.includes(`.`) || isRefExpr) { const targetPath = [...prefixPath]; const path = pathStr.split(`.`); const expr = isRefExpr ? value : new PropRef(path, path[0]); const compiled = compileExpression(expr); ops.push({ kind: `merge`, targetPath, source: compiled }); } else { const tableAlias = pathStr; const targetPath = [...prefixPath]; ops.push({ kind: `merge`, targetPath, source: (row) => row[tableAlias] }); } continue; } const expression = value; if (isConditionalSelectValue(expression)) { ops.push({ kind: `field`, alias: [...prefixPath, key].join(`.`), compiled: compileConditionalSelect(expression) }); continue; } if (expression && expression.type === `includesSubquery`) { ops.push({ kind: `field`, alias: [...prefixPath, key].join(`.`), compiled: () => null }); continue; } if (isNestedSelectObject(expression)) { const targetPath = [...prefixPath, ...key.split(`.`)]; ops.push({ kind: `object`, targetPath }); addFromObject(targetPath, expression, ops); continue; } if (expression === void 0 || !isExpressionLike(expression)) { ops.push({ kind: `field`, alias: [...prefixPath, key].join(`.`), compiled: () => expression }); continue; } if (expression instanceof Value) { const val = expression.value; ops.push({ kind: `field`, alias: [...prefixPath, key].join(`.`), compiled: () => val }); } else { ops.push({ kind: `field`, alias: [...prefixPath, key].join(`.`), compiled: compileExpression(expression) }); } } } function isConditionalSelectValue(value) { return value instanceof ConditionalSelect || value != null && typeof value === `object` && value.type === `conditionalSelect` && Array.isArray(value.branches); } export { processSelect }; //# sourceMappingURL=select.js.map