@tanstack/db
Version:
A reactive client store for building super fast apps on sync
312 lines (311 loc) • 11.3 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const dbIvm = require("@tanstack/db-ivm");
const index = require("../compiler/index.cjs");
const virtualProps = require("../../virtual-props.cjs");
const utils = require("../../utils.cjs");
const BUCKET_FACADE_REF = /* @__PURE__ */ Symbol(`bucketFacadeRef`);
let nextBucketFacadeEdgeId = 0;
function materializeCompilation(compilation, getRootKey, reduceJoinedPublicKeys = false) {
if (!compilation.includes?.length && !(getRootKey && reduceJoinedPublicKeys)) {
return { pipeline: compilation.pipeline, facades: [] };
}
const built = /* @__PURE__ */ new WeakMap();
const materialized = materializeRelation(
compilation,
getRootKey,
built,
`root`
);
return {
...materialized,
facades: dedupeFacades(materialized.facades)
};
}
function materializeRelation(compilation, getKey, built, scope) {
const cached = built.get(compilation)?.[scope];
if (cached) return cached;
let pipeline = canonicalizeByPublicKey(
exposeRouting(compilation.pipeline),
getKey,
scope
);
const facades = [];
for (const include of compilation.includes ?? []) {
const child = materializeRelation(
include.childCompilationResult,
void 0,
built,
`child`
);
facades.push(...child.facades);
const bucketRows = createBucketRows(child.pipeline);
if (include.materialization === `collection`) {
const edgeId = `bucket-facade-${++nextBucketFacadeEdgeId}`;
const activeBuckets = createActiveBuckets(pipeline, include);
const activeBucketRows = activeBuckets.pipe(
dbIvm.join(bucketRows),
dbIvm.map(([bucketKey, [, row]]) => [bucketKey, row])
);
facades.push({
edgeId,
rows: activeBucketRows,
activeBuckets,
hasOrderBy: include.hasOrderBy
});
pipeline = attachCollectionInclude(pipeline, include, edgeId, scope);
} else {
pipeline = attachInlineInclude(pipeline, bucketRows, include, scope);
}
}
const result = { pipeline, facades };
built.set(compilation, { ...built.get(compilation), [scope]: result });
return result;
}
function dedupeFacades(facades) {
return [...new Map(facades.map((facade) => [facade.edgeId, facade])).values()];
}
function exposeRouting(pipeline) {
return pipeline.pipe(
dbIvm.map(([key, rawTuple]) => {
const tuple = rawTuple;
return [
key,
[
tuple[0],
tuple[1],
tuple[2],
tuple[3],
tuple[0][index.INCLUDES_ROUTING],
tuple[4]
]
];
})
);
}
function canonicalizeByPublicKey(pipeline, getKey, scope) {
return pipeline.pipe(
dbIvm.map(([internalKey, rawTuple]) => {
const tuple = rawTuple;
const publicKey = getKey ? getKey(tuple[0]) : tuple[5] ?? internalKey;
const relationKey = scope === `root` ? dbIvm.serializeValue([`root`, publicKey]) : dbIvm.serializeValue([routeKey(tuple[2], tuple[3]), publicKey]);
return [relationKey, { publicKey, tuple }];
}),
dbIvm.reduce((values) => {
const totalMultiplicity = values.reduce(
(total, [, multiplicity]) => total + multiplicity,
0
);
if (totalMultiplicity === 0) return [];
if (totalMultiplicity < 0) {
throw new Error(`Canonical query row has negative multiplicity`);
}
const visible = values.find(([, multiplicity]) => multiplicity > 0)?.[0];
if (!visible) {
throw new Error(`Canonical query row has no positive contributor`);
}
for (const [candidate, multiplicity] of values) {
if (multiplicity <= 0) continue;
assertCongruentContributors(visible, candidate);
}
return [[visible, 1]];
}),
dbIvm.map(([relationKey, { publicKey, tuple }]) => [
scope === `root` ? publicKey : relationKey,
tuple
])
);
}
function assertCongruentContributors(left, right) {
const [leftValue, leftOrder, leftCorrelation, leftContext, leftRouting] = left.tuple;
const [rightValue, rightOrder, rightCorrelation, rightContext, rightRouting] = right.tuple;
if (leftOrder !== rightOrder || !utils.deepEquals(leftValue, rightValue) || !utils.deepEquals(leftCorrelation, rightCorrelation) || !utils.deepEquals(leftContext, rightContext) || !utils.deepEquals(leftRouting, rightRouting)) {
throw new Error(
`Query contributors for public key ${dbIvm.serializeValue(left.publicKey)} are not congruent`
);
}
}
function attachInlineInclude(parentPipeline, bucketRows, include, scope) {
const bucketValues = bucketRows.pipe(
dbIvm.reduce((values) => {
const rows = [];
for (const [row, multiplicity] of values) {
if (multiplicity < 0) {
throw new Error(
`Materialization bucket row has negative multiplicity`
);
}
for (let index2 = 0; index2 < multiplicity; index2++) rows.push(row);
}
if (rows.length === 0) return [];
rows.sort(compareBucketRows);
return [[{ value: materializeRows(rows, include) }, 1]];
})
);
const routedParents = parentPipeline.pipe(
dbIvm.map(([parentKey, rawTuple]) => {
const tuple = rawTuple;
const routing = getIncludeRoute(tuple, include.fieldName);
return [
routing?.active !== true ? `inactive:${dbIvm.serializeValue(parentKey)}` : routeKey(routing.correlationKey, routing.parentContext),
{ parentKey, tuple }
];
}),
dbIvm.join(bucketValues, `left`),
dbIvm.map(([_bucketKey, [parent, bucketValue]]) => {
const [value, order, correlationKey, parentContext, routing, publicKey] = parent.tuple;
const edgeRouting = routing?.[include.fieldName];
if (edgeRouting?.active !== true) {
return [
parent.parentKey,
[value, order, correlationKey, parentContext, routing, publicKey]
];
}
const materialized = bucketValue === null ? emptyMaterializedValue(include.materialization) : bucketValue.value;
return [
parent.parentKey,
[
setMaterializedInclude(value, include.resultPath, materialized),
order,
correlationKey,
parentContext,
routing,
publicKey
]
];
})
);
return canonicalizeByPublicKey(
routedParents,
void 0,
scope
);
}
function createBucketRows(childPipeline) {
return childPipeline.pipe(
dbIvm.map(([internalKey, rawTuple]) => {
const [value, order, correlationKey, parentContext, , publicKey] = rawTuple;
return [
routeKey(correlationKey, parentContext),
{ publicKey: publicKey ?? internalKey, value, order }
];
})
);
}
function attachCollectionInclude(parentPipeline, include, edgeId, scope) {
const routedParents = parentPipeline.pipe(
dbIvm.map(([parentKey, rawTuple]) => {
const tuple = rawTuple;
const routing = getIncludeRoute(tuple, include.fieldName);
if (routing?.active !== true) return [parentKey, tuple];
const facade = createBucketFacadeRef(
edgeId,
routeKey(routing.correlationKey, routing.parentContext)
);
return [
parentKey,
[
setMaterializedInclude(tuple[0], include.resultPath, facade),
tuple[1],
tuple[2],
tuple[3],
tuple[4],
tuple[5]
]
];
})
);
return canonicalizeByPublicKey(
routedParents,
void 0,
scope
);
}
function createActiveBuckets(parentPipeline, include) {
return parentPipeline.pipe(
dbIvm.map(([parentKey, rawTuple]) => {
const tuple = rawTuple;
const routing = getIncludeRoute(tuple, include.fieldName);
const bucketKey = routing?.active === true ? routeKey(routing.correlationKey, routing.parentContext) : void 0;
return [parentKey, bucketKey];
}),
dbIvm.filter(([, bucketKey]) => bucketKey !== void 0),
dbIvm.distinct(([, bucketKey]) => bucketKey),
dbIvm.map(([, bucketKey]) => [bucketKey, true])
);
}
function createBucketFacadeRef(edgeId, bucketKey) {
return { [BUCKET_FACADE_REF]: { edgeId, bucketKey } };
}
function getIncludeRoute(tuple, fieldName) {
return tuple[4]?.[fieldName];
}
function routeKey(correlationKey, parentContext) {
return dbIvm.serializeValue([correlationKey ?? null, parentContext ?? null]);
}
function compareBucketRows(left, right) {
if (left.order !== right.order) {
if (left.order === void 0) return 1;
if (right.order === void 0) return -1;
return left.order < right.order ? -1 : 1;
}
if ((typeof left.publicKey === `string` || typeof left.publicKey === `number`) && (typeof right.publicKey === `string` || typeof right.publicKey === `number`)) {
return dbIvm.compareKeys(left.publicKey, right.publicKey);
}
const leftKey = dbIvm.serializeValue(left.publicKey);
const rightKey = dbIvm.serializeValue(right.publicKey);
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
}
function materializeRows(rows, include) {
const scalarField = include.scalarField;
const values = scalarField ? rows.map(({ value }) => value[scalarField]) : rows.map(({ value }) => value);
if (include.materialization === `array`) return values;
if (include.materialization === `singleton`) return values[0];
return values.map((value) => String(value ?? ``)).join(``);
}
function emptyMaterializedValue(materialization) {
if (materialization === `array`) return [];
if (materialization === `concat`) return ``;
if (materialization === `singleton`) return void 0;
throw new Error(`Collection includes require a bucket facade`);
}
function setNestedValue(source, path, value) {
const root = { ...source };
let target = root;
let current = source;
for (let index2 = 0; index2 < path.length - 1; index2++) {
const part = path[index2];
const currentChild = current?.[part];
const next = Array.isArray(currentChild) ? [...currentChild] : { ...currentChild ?? {} };
target[part] = next;
target = next;
current = currentChild;
}
target[path[path.length - 1]] = value;
return root;
}
function setMaterializedInclude(value, path, materialized) {
const state = value[index.FN_SELECT_STATE];
if (!state) return setNestedValue(value, path, materialized);
const sourceRow = setNestedValue(state.sourceRow, path, materialized);
const selectedValue = state.fnSelect(sourceRow);
if (!selectedValue || typeof selectedValue !== `object`) {
throw new Error(`fn.select must return an object when it projects includes`);
}
const selected = Array.isArray(selectedValue) ? [...selectedValue] : { ...selectedValue };
for (const property of virtualProps.VIRTUAL_PROP_NAMES) {
if (property in value && !(property in selected)) {
selected[property] = value[property];
}
}
selected[index.INCLUDES_ROUTING] = value[index.INCLUDES_ROUTING];
Object.defineProperty(selected, index.FN_SELECT_STATE, {
value: { sourceRow, fnSelect: state.fnSelect },
enumerable: true,
configurable: true
});
return selected;
}
exports.BUCKET_FACADE_REF = BUCKET_FACADE_REF;
exports.materializeCompilation = materializeCompilation;
//# sourceMappingURL=materialized-pipeline.cjs.map