@tanstack/optimistic
Version:
Core optimistic updates library
1 lines • 9.48 kB
Source Map (JSON)
{"version":3,"file":"compiled-query.cjs","sources":["../../../src/query/compiled-query.ts"],"sourcesContent":["import { D2, MessageType, MultiSet, output } from \"@electric-sql/d2ts\"\nimport { Effect, batch } from \"@tanstack/store\"\nimport { Collection } from \"../collection.js\"\nimport { compileQueryPipeline } from \"./pipeline-compiler.js\"\nimport type { ChangeMessage, SyncConfig } from \"../types.js\"\nimport type {\n IStreamBuilder,\n MultiSetArray,\n RootStreamBuilder,\n} from \"@electric-sql/d2ts\"\nimport type { QueryBuilder, ResultsFromContext } from \"./query-builder.js\"\nimport type { Context, Schema } from \"./types.js\"\n\nexport function compileQuery<TContext extends Context<Schema>>(\n queryBuilder: QueryBuilder<TContext>\n) {\n return new CompiledQuery<ResultsFromContext<TContext>>(queryBuilder)\n}\n\nexport class CompiledQuery<TResults extends object = Record<string, unknown>> {\n private graph: D2\n private inputs: Record<string, RootStreamBuilder<any>>\n private inputCollections: Record<string, Collection<any>>\n private resultCollection: Collection<TResults>\n public state: `compiled` | `running` | `stopped` = `compiled`\n private version = 0\n private unsubscribeEffect?: () => void\n\n constructor(queryBuilder: QueryBuilder<Context<Schema>>) {\n const query = queryBuilder._query\n const collections = query.collections\n\n if (!collections) {\n throw new Error(`No collections provided`)\n }\n\n this.inputCollections = collections\n\n const graph = new D2({ initialFrontier: this.version })\n const inputs = Object.fromEntries(\n Object.entries(collections).map(([key]) => [key, graph.newInput<any>()])\n )\n\n const sync: SyncConfig<TResults>[`sync`] = ({ begin, write, commit }) => {\n compileQueryPipeline<IStreamBuilder<[unknown, TResults]>>(\n query,\n inputs\n ).pipe(\n output(({ type, data }) => {\n if (type === MessageType.DATA) {\n begin()\n data.collection\n .getInner()\n .reduce((acc, [[key, value], multiplicity]) => {\n const changes = acc.get(key) || {\n deletes: 0,\n inserts: 0,\n value,\n }\n if (multiplicity < 0) {\n changes.deletes += Math.abs(multiplicity)\n } else if (multiplicity > 0) {\n changes.inserts += multiplicity\n changes.value = value\n }\n acc.set(key, changes)\n return acc\n }, new Map<unknown, { deletes: number; inserts: number; value: TResults }>())\n .forEach((changes, rawKey) => {\n const key = (rawKey as any).toString()\n const { deletes, inserts, value } = changes\n if (inserts && !deletes) {\n write({\n key,\n value: value,\n type: `insert`,\n })\n } else if (inserts >= deletes) {\n write({\n key,\n value: value,\n type: `update`,\n })\n } else if (deletes > 0) {\n write({\n key,\n value: value,\n type: `delete`,\n })\n }\n })\n commit()\n }\n })\n )\n graph.finalize()\n }\n\n this.graph = graph\n this.inputs = inputs\n this.resultCollection = new Collection<TResults>({\n id: crypto.randomUUID(), // TODO: remove when we don't require any more\n sync: {\n sync,\n },\n })\n }\n\n get results() {\n return this.resultCollection\n }\n\n private sendChangesToInput(inputKey: string, changes: Array<ChangeMessage>) {\n const input = this.inputs[inputKey]!\n const multiSetArray: MultiSetArray<unknown> = []\n for (const change of changes) {\n if (change.type === `insert`) {\n multiSetArray.push([change.value, 1])\n } else if (change.type === `update`) {\n multiSetArray.push([change.previousValue, -1])\n multiSetArray.push([change.value, 1])\n } else {\n // change.type === `delete`\n multiSetArray.push([change.value, -1])\n }\n }\n input.sendData(this.version, new MultiSet(multiSetArray))\n }\n\n private sendFrontierToInput(inputKey: string) {\n const input = this.inputs[inputKey]!\n input.sendFrontier(this.version)\n }\n\n private sendFrontierToAllInputs() {\n Object.entries(this.inputs).forEach(([key]) => {\n this.sendFrontierToInput(key)\n })\n }\n\n private incrementVersion() {\n this.version++\n }\n\n private runGraph() {\n this.graph.run()\n }\n\n start() {\n if (this.state === `running`) {\n throw new Error(`Query is already running`)\n } else if (this.state === `stopped`) {\n throw new Error(`Query is stopped`)\n }\n\n batch(() => {\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n this.sendChangesToInput(key, collection.currentStateAsChanges())\n })\n this.incrementVersion()\n this.sendFrontierToAllInputs()\n this.runGraph()\n })\n\n const changeEffect = new Effect({\n fn: () => {\n batch(() => {\n Object.entries(this.inputCollections).forEach(([key, collection]) => {\n this.sendChangesToInput(key, collection.derivedChanges.state)\n })\n this.incrementVersion()\n this.sendFrontierToAllInputs()\n this.runGraph()\n })\n },\n deps: Object.values(this.inputCollections).map(\n (collection) => collection.derivedChanges\n ),\n })\n this.unsubscribeEffect = changeEffect.mount()\n\n this.state = `running`\n return () => {\n this.stop()\n }\n }\n\n stop() {\n this.unsubscribeEffect?.()\n this.unsubscribeEffect = undefined\n this.state = `stopped`\n }\n}\n"],"names":["D2","compileQueryPipeline","output","MessageType","Collection","MultiSet","batch","collection","Effect"],"mappings":"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"}