@tanstack/db
Version:
A reactive client store for building super fast apps on sync
246 lines (245 loc) • 7.85 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const dbIvm = require("@tanstack/db-ivm");
const ir = require("../ir.cjs");
const errors = require("../../errors.cjs");
const evaluators = require("./evaluators.cjs");
function unwrapVal(input) {
if (input instanceof ir.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 errors.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(
dbIvm.map(([key, namespacedRow]) => [
key,
{ ...namespacedRow, $selected: compiled(namespacedRow) }
])
);
}
const ops = [];
addFromObject([], select, ops);
return pipeline.pipe(dbIvm.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 ir.Value) {
return () => value.value;
}
if (value.type === `includesSubquery`) {
return () => null;
}
if (isNestedSelectObject(value)) {
return compileSelectObject(value);
}
if (!ir.isExpressionLike(value)) {
return () => value;
}
return evaluators.compileExpression(value);
}
function compileConditionalSelect(conditional) {
const branches = conditional.branches.map((branch) => ({
condition: evaluators.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 (evaluators.isCaseWhenConditionTrue(branch.condition(row))) {
return branch.value(row);
}
}
return defaultFn !== void 0 ? defaultFn(row) : null;
};
}
function isNestedSelectObject(obj) {
return obj && typeof obj === `object` && !ir.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 ir.PropRef(path, path[0]);
const compiled = evaluators.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 || !ir.isExpressionLike(expression)) {
ops.push({
kind: `field`,
alias: [...prefixPath, key].join(`.`),
compiled: () => expression
});
continue;
}
if (expression instanceof ir.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: evaluators.compileExpression(expression)
});
}
}
}
function isConditionalSelectValue(value) {
return value instanceof ir.ConditionalSelect || value != null && typeof value === `object` && value.type === `conditionalSelect` && Array.isArray(value.branches);
}
exports.processSelect = processSelect;
//# sourceMappingURL=select.cjs.map