UNPKG

@tanstack/optimistic

Version:

Core optimistic updates library

163 lines (162 loc) 5 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const d2ts = require("@electric-sql/d2ts"); const store = require("@tanstack/store"); 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.version = 0; const query = queryBuilder._query; const collections = query.collections; if (!collections) { throw new Error(`No collections provided`); } this.inputCollections = collections; const graph = new d2ts.D2({ initialFrontier: this.version }); const inputs = Object.fromEntries( Object.entries(collections).map(([key]) => [key, graph.newInput()]) ); const sync = ({ begin, write, commit }) => { pipelineCompiler.compileQueryPipeline( query, inputs ).pipe( d2ts.output(({ type, data }) => { if (type === d2ts.MessageType.DATA) { begin(); data.collection.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 key = rawKey.toString(); const { deletes, inserts, value } = changes; if (inserts && !deletes) { write({ key, value, type: `insert` }); } else if (inserts >= deletes) { write({ key, value, type: `update` }); } else if (deletes > 0) { write({ key, value, type: `delete` }); } }); commit(); } }) ); graph.finalize(); }; this.graph = graph; this.inputs = inputs; this.resultCollection = new collection.Collection({ id: crypto.randomUUID(), // TODO: remove when we don't require any more sync: { sync } }); } get results() { return this.resultCollection; } sendChangesToInput(inputKey, changes) { const input = this.inputs[inputKey]; const multiSetArray = []; for (const change of changes) { if (change.type === `insert`) { multiSetArray.push([change.value, 1]); } else if (change.type === `update`) { multiSetArray.push([change.previousValue, -1]); multiSetArray.push([change.value, 1]); } else { multiSetArray.push([change.value, -1]); } } input.sendData(this.version, new d2ts.MultiSet(multiSetArray)); } sendFrontierToInput(inputKey) { const input = this.inputs[inputKey]; input.sendFrontier(this.version); } sendFrontierToAllInputs() { Object.entries(this.inputs).forEach(([key]) => { this.sendFrontierToInput(key); }); } incrementVersion() { this.version++; } 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`); } store.batch(() => { Object.entries(this.inputCollections).forEach(([key, collection2]) => { this.sendChangesToInput(key, collection2.currentStateAsChanges()); }); this.incrementVersion(); this.sendFrontierToAllInputs(); this.runGraph(); }); const changeEffect = new store.Effect({ fn: () => { store.batch(() => { Object.entries(this.inputCollections).forEach(([key, collection2]) => { this.sendChangesToInput(key, collection2.derivedChanges.state); }); this.incrementVersion(); this.sendFrontierToAllInputs(); this.runGraph(); }); }, deps: Object.values(this.inputCollections).map( (collection2) => collection2.derivedChanges ) }); this.unsubscribeEffect = changeEffect.mount(); this.state = `running`; return () => { this.stop(); }; } stop() { var _a; (_a = this.unsubscribeEffect) == null ? void 0 : _a.call(this); this.unsubscribeEffect = void 0; this.state = `stopped`; } } exports.CompiledQuery = CompiledQuery; exports.compileQuery = compileQuery; //# sourceMappingURL=compiled-query.cjs.map