@tanstack/db
Version:
A reactive client store for building super fast apps on sync
139 lines (138 loc) • 4.31 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const d2mini = require("@electric-sql/d2mini");
const collection = require("../collection.cjs");
const pipelineCompiler = require("./pipeline-compiler.cjs");
function compileQuery(queryBuilder) {
return new CompiledQuery(queryBuilder);
}
class CompiledQuery {
constructor(queryBuilder) {
this.state = `compiled`;
this.unsubscribeCallbacks = [];
const query = queryBuilder._query;
const collections = query.collections;
if (!collections) {
throw new Error(`No collections provided`);
}
this.inputCollections = collections;
const graph = new d2mini.D2();
const inputs = Object.fromEntries(
Object.entries(collections).map(([key]) => [key, graph.newInput()])
);
const sync = ({ begin, write, commit }) => {
pipelineCompiler.compileQueryPipeline(
query,
inputs
).pipe(
d2mini.output((data) => {
begin();
data.getInner().reduce((acc, [[key, value], multiplicity]) => {
const changes = acc.get(key) || {
deletes: 0,
inserts: 0,
value
};
if (multiplicity < 0) {
changes.deletes += Math.abs(multiplicity);
} else if (multiplicity > 0) {
changes.inserts += multiplicity;
changes.value = value;
}
acc.set(key, changes);
return acc;
}, /* @__PURE__ */ new Map()).forEach((changes, rawKey) => {
const { deletes, inserts, value } = changes;
const valueWithKey = { ...value, _key: rawKey };
if (inserts && !deletes) {
write({
value: valueWithKey,
type: `insert`
});
} else if (inserts >= deletes) {
write({
value: valueWithKey,
type: `update`
});
} else if (deletes > 0) {
write({
value: valueWithKey,
type: `delete`
});
}
});
commit();
})
);
graph.finalize();
};
this.graph = graph;
this.inputs = inputs;
this.resultCollection = collection.createCollection({
id: crypto.randomUUID(),
// TODO: remove when we don't require any more
getKey: (val) => {
return val._key;
},
sync: {
sync
}
});
}
get results() {
return this.resultCollection;
}
sendChangesToInput(inputKey, changes, getKey) {
const input = this.inputs[inputKey];
const multiSetArray = [];
for (const change of changes) {
const key = getKey(change.value);
if (change.type === `insert`) {
multiSetArray.push([[key, change.value], 1]);
} else if (change.type === `update`) {
multiSetArray.push([[key, change.previousValue], -1]);
multiSetArray.push([[key, change.value], 1]);
} else {
multiSetArray.push([[key, change.value], -1]);
}
}
input.sendData(new d2mini.MultiSet(multiSetArray));
}
runGraph() {
this.graph.run();
}
start() {
if (this.state === `running`) {
throw new Error(`Query is already running`);
} else if (this.state === `stopped`) {
throw new Error(`Query is stopped`);
}
Object.entries(this.inputCollections).forEach(([key, collection2]) => {
this.sendChangesToInput(
key,
collection2.currentStateAsChanges(),
collection2.config.getKey
);
});
this.runGraph();
Object.entries(this.inputCollections).forEach(([key, collection2]) => {
const unsubscribe = collection2.subscribeChanges((changes) => {
this.sendChangesToInput(key, changes, collection2.config.getKey);
this.runGraph();
});
this.unsubscribeCallbacks.push(unsubscribe);
});
this.state = `running`;
return () => {
this.stop();
};
}
stop() {
this.unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe());
this.unsubscribeCallbacks = [];
this.state = `stopped`;
}
}
exports.CompiledQuery = CompiledQuery;
exports.compileQuery = compileQuery;
//# sourceMappingURL=compiled-query.cjs.map