@wovin/core 0.1.36 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -12
- package/dist/applog/applog-helpers.d.ts +12 -12
- package/dist/applog/applog-helpers.d.ts.map +1 -1
- package/dist/applog/applog-utils.d.ts +40 -6
- package/dist/applog/applog-utils.d.ts.map +1 -1
- package/dist/applog/datom-types.d.ts +67 -12
- package/dist/applog/datom-types.d.ts.map +1 -1
- package/dist/applog.d.ts +3 -3
- package/dist/applog.d.ts.map +1 -1
- package/dist/{applog.min.js → applog.js} +12 -7
- package/dist/blockstore.d.ts +1 -1
- package/dist/blockstore.d.ts.map +1 -1
- package/dist/{blockstore.min.js → blockstore.js} +1 -3
- package/dist/{blockstore.min.js.map → blockstore.js.map} +1 -1
- package/dist/chunk-22WDFLXO.js +138 -0
- package/dist/chunk-22WDFLXO.js.map +1 -0
- package/dist/chunk-3SUFNJEZ.js +1026 -0
- package/dist/chunk-3SUFNJEZ.js.map +1 -0
- package/dist/chunk-6ALNRM3J.js +435 -0
- package/dist/chunk-6ALNRM3J.js.map +1 -0
- package/dist/chunk-7Z5YDQKK.js +1 -0
- package/dist/{chunk-KXMTKPF4.min.js → chunk-BLF5MAWU.js} +8 -8
- package/dist/chunk-BLF5MAWU.js.map +1 -0
- package/dist/chunk-E46VTKTZ.js +1 -0
- package/dist/{chunk-H3VQJP56.min.js → chunk-HUIQ54TT.js} +9 -9
- package/dist/chunk-HUIQ54TT.js.map +1 -0
- package/dist/{chunk-BRC7LSM6.min.js → chunk-OC6Z6CQW.js} +5 -5
- package/dist/chunk-OC6Z6CQW.js.map +1 -0
- package/dist/chunk-SHUHRHOT.js +1923 -0
- package/dist/chunk-SHUHRHOT.js.map +1 -0
- package/dist/{chunk-QPGEBDMJ.min.js → chunk-YDAKBU6Q.js} +1 -1
- package/dist/chunk-YDAKBU6Q.js.map +1 -0
- package/dist/chunk-ZAADLBSB.js +36 -0
- package/dist/chunk-ZAADLBSB.js.map +1 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/{index.min.js → index.js} +81 -46
- package/dist/ipfs/car.d.ts +11 -11
- package/dist/ipfs/car.d.ts.map +1 -1
- package/dist/ipfs/ipfs-utils.d.ts +2 -2
- package/dist/ipfs/ipfs-utils.d.ts.map +1 -1
- package/dist/ipfs.d.ts +3 -3
- package/dist/ipfs.d.ts.map +1 -1
- package/dist/{ipfs.min.js → ipfs.js} +7 -10
- package/dist/ipns.d.ts +1 -1
- package/dist/ipns.d.ts.map +1 -1
- package/dist/ipns.js +64 -0
- package/dist/ipns.js.map +1 -0
- package/dist/pubsub/pub-pull.d.ts +3 -3
- package/dist/pubsub/pub-pull.d.ts.map +1 -1
- package/dist/pubsub/pubsub-types.d.ts +3 -3
- package/dist/pubsub/pubsub-types.d.ts.map +1 -1
- package/dist/pubsub/snap-push.d.ts +4 -4
- package/dist/pubsub/snap-push.d.ts.map +1 -1
- package/dist/pubsub/ucan.d.ts +1 -1
- package/dist/pubsub/ucan.d.ts.map +1 -1
- package/dist/pubsub.d.ts +4 -4
- package/dist/pubsub.d.ts.map +1 -1
- package/dist/{pubsub.min.js → pubsub.js} +7 -10
- package/dist/query/attr-helpers.d.ts +5 -0
- package/dist/query/attr-helpers.d.ts.map +1 -0
- package/dist/query/basic.d.ts +87 -23
- package/dist/query/basic.d.ts.map +1 -1
- package/dist/query/divergences.d.ts +5 -5
- package/dist/query/divergences.d.ts.map +1 -1
- package/dist/query/entity-collection.d.ts +19 -0
- package/dist/query/entity-collection.d.ts.map +1 -0
- package/dist/query/matchers.d.ts +12 -1
- package/dist/query/matchers.d.ts.map +1 -1
- package/dist/query/memoized.d.ts +66 -0
- package/dist/query/memoized.d.ts.map +1 -0
- package/dist/query/situations.d.ts +2 -1
- package/dist/query/situations.d.ts.map +1 -1
- package/dist/query/subscribable.d.ts +111 -0
- package/dist/query/subscribable.d.ts.map +1 -0
- package/dist/query/types.d.ts +54 -14
- package/dist/query/types.d.ts.map +1 -1
- package/dist/query.d.ts +9 -5
- package/dist/query.d.ts.map +1 -1
- package/dist/{query.min.js → query.js} +55 -34
- package/dist/retrieve/index.d.ts +1 -1
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/update-thread.d.ts +3 -3
- package/dist/retrieve/update-thread.d.ts.map +1 -1
- package/dist/retrieve.d.ts +1 -1
- package/dist/retrieve.d.ts.map +1 -1
- package/dist/retrieve.js +14 -0
- package/dist/thread/basic.d.ts +15 -19
- package/dist/thread/basic.d.ts.map +1 -1
- package/dist/thread/filters.d.ts +8 -10
- package/dist/thread/filters.d.ts.map +1 -1
- package/dist/thread/indexes.d.ts +57 -0
- package/dist/thread/indexes.d.ts.map +1 -0
- package/dist/thread/mapped.d.ts +40 -11
- package/dist/thread/mapped.d.ts.map +1 -1
- package/dist/thread/utils.d.ts +5 -5
- package/dist/thread/utils.d.ts.map +1 -1
- package/dist/thread/writeable.d.ts +2 -2
- package/dist/thread/writeable.d.ts.map +1 -1
- package/dist/thread.d.ts +6 -5
- package/dist/thread.d.ts.map +1 -1
- package/dist/{thread.min.js → thread.js} +9 -6
- package/dist/types/typescript-utils.d.ts +6 -5
- package/dist/types/typescript-utils.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/{types.min.js → types.js} +3 -4
- package/dist/utils/debug-name.d.ts +13 -0
- package/dist/utils/debug-name.d.ts.map +1 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +9 -0
- package/package.json +32 -23
- package/src/applog/applog-helpers.ts +155 -0
- package/src/applog/applog-utils.test.ts +108 -0
- package/src/applog/applog-utils.ts +551 -0
- package/src/applog/datom-types.ts +167 -0
- package/src/applog/object-values.test.ts +106 -0
- package/src/applog.ts +3 -0
- package/src/blockstore/index.ts +36 -0
- package/src/blockstore.ts +1 -0
- package/src/index.ts +8 -0
- package/src/ipfs/car.ts +291 -0
- package/src/ipfs/fetch-snapshot-chain.ts +135 -0
- package/src/ipfs/ipfs-utils.ts +132 -0
- package/src/ipfs.ts +3 -0
- package/src/ipns/ipns-record.ts +115 -0
- package/src/ipns.ts +1 -0
- package/src/pubsub/UCAN Specs Overview.md +217 -0
- package/src/pubsub/connector.ts +9 -0
- package/src/pubsub/pub-pull.ts +31 -0
- package/src/pubsub/pubsub-types.ts +90 -0
- package/src/pubsub/snap-push.ts +278 -0
- package/src/pubsub/ucan-example.ts +61 -0
- package/src/pubsub/ucan.ts +56 -0
- package/src/pubsub.ts +4 -0
- package/src/query/attr-helpers.ts +5 -0
- package/src/query/basic.ts +1245 -0
- package/src/query/divergences.ts +50 -0
- package/src/query/entity-collection.ts +132 -0
- package/src/query/liveFilterAndMap.test.ts +102 -0
- package/src/query/matchers.ts +30 -0
- package/src/query/memoized.test.ts +151 -0
- package/src/query/memoized.ts +180 -0
- package/src/query/query-steps.ts +4 -0
- package/src/query/query.test.ts +538 -0
- package/src/query/situations.ts +261 -0
- package/src/query/subscribable.test.ts +245 -0
- package/src/query/subscribable.ts +234 -0
- package/src/query/types.ts +155 -0
- package/src/query/withoutDeleted.test.ts +204 -0
- package/src/query.ts +9 -0
- package/src/retrieve/index.ts +1 -0
- package/src/retrieve/update-thread.ts +248 -0
- package/src/retrieve.ts +1 -0
- package/src/test/perf/query.1m.perf.test.ts +94 -0
- package/src/test/perf/query.perf.test.ts +389 -0
- package/src/test/perf/query.realdata.perf.test.ts +182 -0
- package/src/thread/basic.ts +209 -0
- package/src/thread/filters.ts +227 -0
- package/src/thread/indexes.ts +256 -0
- package/src/thread/joinThreads.test.ts +304 -0
- package/src/thread/mapped.ts +226 -0
- package/src/thread/utils.ts +144 -0
- package/src/thread/writeable.ts +163 -0
- package/src/thread.ts +6 -0
- package/src/types/typescript-utils.ts +64 -0
- package/src/types.ts +1 -0
- package/src/utils/debug-name.ts +54 -0
- package/src/utils.ts +4 -0
- package/dist/chunk-2Y2PYHGR.min.js +0 -65
- package/dist/chunk-2Y2PYHGR.min.js.map +0 -1
- package/dist/chunk-5MMGBK2U.min.js +0 -1
- package/dist/chunk-7IDQIMQO.min.js +0 -1
- package/dist/chunk-BRC7LSM6.min.js.map +0 -1
- package/dist/chunk-COXXILXC.min.js +0 -512
- package/dist/chunk-COXXILXC.min.js.map +0 -1
- package/dist/chunk-GDX2OO7L.min.js +0 -9080
- package/dist/chunk-GDX2OO7L.min.js.map +0 -1
- package/dist/chunk-H3VQJP56.min.js.map +0 -1
- package/dist/chunk-HYMC7W6S.min.js +0 -1549
- package/dist/chunk-HYMC7W6S.min.js.map +0 -1
- package/dist/chunk-KEHU7HGZ.min.js +0 -5216
- package/dist/chunk-KEHU7HGZ.min.js.map +0 -1
- package/dist/chunk-KXMTKPF4.min.js.map +0 -1
- package/dist/chunk-PHITDXZT.min.js +0 -36
- package/dist/chunk-QO2KMGDN.min.js +0 -3771
- package/dist/chunk-QO2KMGDN.min.js.map +0 -1
- package/dist/chunk-QPGEBDMJ.min.js.map +0 -1
- package/dist/chunk-WXLCBTHX.min.js +0 -1606
- package/dist/chunk-WXLCBTHX.min.js.map +0 -1
- package/dist/ipns.min.js +0 -6419
- package/dist/ipns.min.js.map +0 -1
- package/dist/mobx/mobx-utils.d.ts +0 -82
- package/dist/mobx/mobx-utils.d.ts.map +0 -1
- package/dist/mobx.d.ts +0 -2
- package/dist/mobx.d.ts.map +0 -1
- package/dist/mobx.min.js +0 -141
- package/dist/retrieve.min.js +0 -17
- package/dist/types.min.js.map +0 -1
- package/dist/utils.min.js +0 -10
- package/dist/utils.min.js.map +0 -1
- /package/dist/{applog.min.js.map → applog.js.map} +0 -0
- /package/dist/{chunk-5MMGBK2U.min.js.map → chunk-7Z5YDQKK.js.map} +0 -0
- /package/dist/{chunk-7IDQIMQO.min.js.map → chunk-E46VTKTZ.js.map} +0 -0
- /package/dist/{chunk-PHITDXZT.min.js.map → index.js.map} +0 -0
- /package/dist/{index.min.js.map → ipfs.js.map} +0 -0
- /package/dist/{ipfs.min.js.map → pubsub.js.map} +0 -0
- /package/dist/{mobx.min.js.map → query.js.map} +0 -0
- /package/dist/{pubsub.min.js.map → retrieve.js.map} +0 -0
- /package/dist/{query.min.js.map → thread.js.map} +0 -0
- /package/dist/{retrieve.min.js.map → types.js.map} +0 -0
- /package/dist/{thread.min.js.map → utils.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/query/types.ts","../src/query/basic.ts"],"sourcesContent":["import { joinThreads } from '../applog/applog-helpers.ts'\nimport { SearchContext } from '../applog/datom-types.ts'\nimport type { Thread } from '../thread/basic.ts'\nimport { ArrayEvent, SubscribableArray, SubscribableArrayImpl, Unsubscribe } from './subscribable.ts'\n\nexport class QueryNode {\n\tconstructor(\n\t\treadonly logsOfThisNode: Thread,\n\t\treadonly variables: SearchContext,\n\t\treadonly prevNode: QueryNode | null = null,\n\t) {}\n\tget record() {\n\t\treturn this.variables // alias for end-user consumption\n\t}\n\n\tget threadOfTrail() {\n\t\tif (!this.prevNode) return this.logsOfThisNode\n\t\treturn joinThreads([\n\t\t\tthis.logsOfThisNode,\n\t\t\tthis.prevNode.threadOfTrail,\n\t\t])\n\t}\n\tget trailLogs() {\n\t\treturn this.threadOfTrail.applogs\n\t}\n}\n\n/** Shared interface for query results (one-off and live) */\nexport interface IQueryResult {\n\treadonly nodes: readonly QueryNode[]\n\treadonly size: number\n\treadonly isEmpty: boolean\n\treadonly records: readonly SearchContext[]\n\treadonly leafNodeLogs: readonly import('../applog/datom-types').Applog[]\n\treadonly leafNodeThread: Thread\n\treadonly threadOfAllTrails: Thread\n\treadonly thread: Thread\n\treadonly allApplogs: readonly import('../applog/datom-types').Applog[]\n}\n\n/**\n * One-off query result — plain frozen snapshot.\n * No subscribe method. No stale-data risk.\n */\nexport class QueryResult implements IQueryResult {\n\tconstructor(\n\t\treadonly nodes: readonly QueryNode[],\n\t) {}\n\n\tget size() {\n\t\treturn this.nodes.length\n\t}\n\tget isEmpty() {\n\t\treturn this.nodes.length === 0\n\t}\n\tget untrackedSize() {\n\t\treturn this.nodes.length\n\t}\n\n\tget records(): readonly SearchContext[] {\n\t\treturn this.nodes.map(({ variables }) => variables)\n\t}\n\tget leafNodeThread() {\n\t\treturn joinThreads(\n\t\t\tthis.nodes.map(({ logsOfThisNode: thread }) => thread),\n\t\t)\n\t}\n\tget leafNodeLogSet() {\n\t\treturn this.nodes.map(({ logsOfThisNode: thread }) => thread.applogs)\n\t}\n\tget leafNodeLogs() {\n\t\treturn this.nodes.flatMap(({ logsOfThisNode: thread }) => thread.applogs)\n\t}\n\tget threadOfAllTrails() {\n\t\treturn joinThreads(this.nodes.map(node => node.threadOfTrail))\n\t}\n\tget thread() {\n\t\treturn this.threadOfAllTrails // alias\n\t}\n\tget allApplogs() {\n\t\treturn this.threadOfAllTrails.applogs\n\t}\n}\n\n/**\n * Live query result — eagerly activated, always up-to-date.\n *\n * `.nodes` returns the current live view.\n * `.subscribe()` receives future delta events (consistent with current state).\n * Must call `.dispose()` when done to tear down upstream subscriptions.\n */\nexport class LiveQueryResult implements IQueryResult {\n\tconstructor(\n\t\tprivate _source: SubscribableArray<QueryNode>,\n\t\tactivate = true,\n\t) {\n\t\tif (activate) {\n\t\t\t// Eagerly activate: subscribe with a no-op to start upstream.\n\t\t\t// Store unsub so dispose() can tear it down.\n\t\t\tthis._activationUnsub = this._source.subscribe(() => {})\n\t\t}\n\t}\n\n\tprivate _activationUnsub: Unsubscribe | null = null\n\n\t/** Subscribe to node change events. Callback fires on future changes only. */\n\tsubscribe(cb: (event: ArrayEvent<QueryNode>) => void, type?: 'derived' | 'reaction'): Unsubscribe {\n\t\treturn this._source.subscribe(cb, type)\n\t}\n\n\t/** Current nodes — live view, always up-to-date while not disposed */\n\tget nodes(): readonly QueryNode[] {\n\t\treturn this._source.items\n\t}\n\n\tget size() {\n\t\treturn this._source.length\n\t}\n\tget isEmpty() {\n\t\treturn this._source.length === 0\n\t}\n\tget untrackedSize() {\n\t\treturn this._source.length\n\t}\n\n\tget records(): readonly SearchContext[] {\n\t\treturn this.nodes.map(({ variables }) => variables)\n\t}\n\tget leafNodeThread() {\n\t\treturn joinThreads(\n\t\t\tthis.nodes.map(({ logsOfThisNode: thread }) => thread),\n\t\t)\n\t}\n\tget leafNodeLogSet() {\n\t\treturn this.nodes.map(({ logsOfThisNode: thread }) => thread.applogs)\n\t}\n\tget leafNodeLogs() {\n\t\treturn this.nodes.flatMap(({ logsOfThisNode: thread }) => thread.applogs)\n\t}\n\tget threadOfAllTrails() {\n\t\treturn joinThreads(this.nodes.map(node => node.threadOfTrail))\n\t}\n\tget thread() {\n\t\treturn this.threadOfAllTrails // alias\n\t}\n\tget allApplogs() {\n\t\treturn this.threadOfAllTrails.applogs\n\t}\n\n\tdispose() {\n\t\tthis._activationUnsub?.()\n\t\tthis._activationUnsub = null\n\t\tthis._source.dispose()\n\t}\n}\n","import { AgentHash, Applog, ApplogValue, CidString, DatalogQueryPattern, EntityID, SearchContext, ValueOrMatcher } from '../applog/datom-types.ts'\n\nimport { Logger } from 'besonders-logger'\n\nimport { isEmpty } from 'lodash-es'\nimport stringify from 'safe-stable-stringify'\nimport { isLaterByTsAndPv, isoDateStrCompare, isVariable, resolveOrRemoveVariables, sortApplogsByTs } from '../applog/applog-utils.ts'\nimport { createDebugName } from '../utils/debug-name.ts'\nimport { isInitEvent, StaticThread, Thread, ThreadEvent } from '../thread/basic.ts'\nimport { hasFilter, makeFilter, rollingFilter, rollingMapper, ThreadOnlyCurrent } from '../thread/filters.ts'\nimport { applogsByEntity } from '../thread/indexes.ts'\nimport { MappedThread, type ThreadDerivation } from '../thread/mapped.ts'\nimport { ThreadInMemory } from '../thread/writeable.ts'\nimport { memoizedFn } from './memoized.ts'\nimport { isArrayInitEvent, SubscribableArray, SubscribableArrayImpl, SubscribableImpl, Unsubscribe } from './subscribable.ts'\nimport { LiveQueryResult, QueryNode, QueryResult } from './types.ts'\n\nconst { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO, { prefix: '[q]' }) // eslint-disable-line no-unused-vars\n\nfunction assertLWW(thread: Thread) {\n\tif (!hasFilter(thread, 'lastWriteWins')) {\n\t\tthrow ERROR(`requires lastWriteWins-filtered thread, got filters:`, thread.filters, { name: thread.name })\n\t}\n}\n\nlet globalQueryTimeoutTime = null\n\n// util.inspect.defaultOptions.depth = 5;\n\n// export interface QueryExecutorArguments {\n// db: Thread\n// // applogs: AppLog[]\n// nodes: SearchContextWithLog[]\n// }\n// export interface QueryExecutorResult {\n// // applogs: AppLog[]\n// nodes: SearchContextWithLog[]\n// }\n// export type QueryExecutor = (args: QueryExecutorArguments) => QueryExecutorResult\n\n/////////////\n// QUERIES //\n/////////////\n\n/**\n * Keep only the latest logs for each en&at (= last write wins)\n */\nexport const lastWriteWins = memoizedFn('lastWriteWins', function lastWriteWins(\n\tthread: Thread,\n\t{ inverseToOnlyReturnFirstLogs, tolerateAlreadyFiltered }: {\n\t\tinverseToOnlyReturnFirstLogs?: boolean\n\t\ttolerateAlreadyFiltered?: boolean\n\t} = {},\n): ThreadOnlyCurrent {\n\tVERBOSE(`lastWriteWins${inverseToOnlyReturnFirstLogs ? '.inversed' : ''} < ${thread.nameAndSizeUntracked} > initializing`)\n\tif (thread.filters.includes('lastWriteWins')) {\n\t\tif (tolerateAlreadyFiltered) {\n\t\t\tDEBUG(`[lastWriteWins] already filtered, but tolerateAlreadyFiltered=true, so returning`)\n\t\t\treturn thread as ThreadOnlyCurrent\n\t\t}\n\t\tthrow ERROR(`thread already filtered lastWriteWins:`, thread.filters, { name: thread.name })\n\t}\n\n\tlet rollingMap: Map<string, Applog>\n\n\t/**\n\t * Iterate `newLogs` (already chain-aware-sorted by `sortApplogsByTs`) updating\n\t * `rollingMap` to hold the LWW winner per (en|at) key. Uses `isLaterByTsAndPv`\n\t * — pairwise pv-aware predicate — so cross-batch comparisons in mapDelta also\n\t * stay deterministic when same-ts chain links collide.\n\t */\n\tconst processLogs = (newLogs: readonly Applog[], toRemove: Applog[] | null): Applog[] => {\n\t\tconst toAdd = [] as Applog[]\n\t\tlet prevTs: string | undefined\n\t\tfor (\n\t\t\tlet i = inverseToOnlyReturnFirstLogs ? 0 : newLogs.length - 1;\n\t\t\tinverseToOnlyReturnFirstLogs ? i < newLogs.length : i >= 0;\n\t\t\tinverseToOnlyReturnFirstLogs ? i++ : i--\n\t\t) {\n\t\t\tconst log = newLogs[i]\n\t\t\tconst key = log.en + '|' + log.at\n\n\t\t\tif (prevTs !== undefined) {\n\t\t\t\tconst cmp = isoDateStrCompare(prevTs, log.ts)\n\t\t\t\tif (inverseToOnlyReturnFirstLogs ? cmp > 0 : cmp < 0) {\n\t\t\t\t\tthrow ERROR(`lastWriteWins.processLogs logs not ts-sorted:`, prevTs, inverseToOnlyReturnFirstLogs ? '>' : '<', log.ts, {\n\t\t\t\t\t\tlog,\n\t\t\t\t\t\ti,\n\t\t\t\t\t\tnewLogs,\n\t\t\t\t\t\tinverseToOnlyReturnFirstLogs,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tprevTs = log.ts\n\n\t\t\tconst existing = rollingMap.get(key)\n\t\t\tconst replaces = !existing || (inverseToOnlyReturnFirstLogs\n\t\t\t\t? isLaterByTsAndPv(existing, log)\n\t\t\t\t: isLaterByTsAndPv(log, existing))\n\t\t\tif (replaces) {\n\t\t\t\tif (existing && toRemove) toRemove.push(existing)\n\t\t\t\ttoAdd.push(log)\n\t\t\t\trollingMap.set(key, log)\n\t\t\t}\n\t\t}\n\t\treturn toAdd\n\t}\n\n\tconst lwwName = `lastWriteWins${inverseToOnlyReturnFirstLogs ? '.inversed' : ''}`\n\tconst derivation: ThreadDerivation = {\n\t\tcompute(parents) {\n\t\t\tif (parents.length !== 1) {\n\t\t\t\tthrow ERROR(`${lwwName} requires exactly one parent`, { parents: parents.length })\n\t\t\t}\n\t\t\tconst [parent] = parents\n\t\t\trollingMap = new Map()\n\t\t\tconst toAdd = processLogs(parent.applogs, null)\n\t\t\tsortApplogsByTs(toAdd)\n\t\t\tVERBOSE.isDisabled || VERBOSE(`${lwwName}<${thread.nameAndSizeUntracked}> compute`, { toAdd: toAdd.length })\n\t\t\treturn toAdd\n\t\t},\n\t\tmapDelta(delta) {\n\t\t\tconst toRemove = [] as Applog[]\n\t\t\tconst toAdd = processLogs(delta.added, toRemove)\n\t\t\tsortApplogsByTs(toAdd)\n\t\t\tVERBOSE.isDisabled || VERBOSE(`${lwwName}<${thread.nameAndSizeUntracked}> mapDelta`, { ...delta, toAdd, toRemove })\n\t\t\treturn { added: toAdd, removed: toRemove }\n\t\t},\n\t}\n\tconst mappedThread = rollingMapper(thread, derivation, { name: lwwName, extraFilterName: 'lastWriteWins' })\n\tVERBOSE.isDisabled || VERBOSE(`lastWriteWins<${thread.nameAndSizeUntracked}> filtered down to`, mappedThread.applogs.length)\n\treturn mappedThread as ThreadOnlyCurrent\n}, { argsDebugName: (thread) => createDebugName({ caller: 'lastWriteWins', thread }) })\n\nconst isDeletedAttrs = ['isDeleted', 'relation/isDeleted', 'block/isDeleted']\nfunction isDeletionLog(log: Applog) {\n\treturn log.vl === true && isDeletedAttrs.includes(log.at)\n}\n\n/**\n * Remove all applogs for entities that have an applog `{ at: 'isDeleted' | 'relation/isDeleted' | 'block/isDeleted', vl: true }`.\n *\n * Emits synthetic `removed` events for the entity's pre-existing applogs when an\n * entity becomes deleted (and synthetic `added` events for un-deletion).\n *\n * Un-deletion: canonical input is appending `{ at: 'isDeleted', vl: false }`. Requires\n * `lastWriteWins` upstream — without it, the `vl: true` log isn't superseded so we\n * can't observe a transition. Soft-warned at runtime.\n */\nexport const withoutDeleted = memoizedFn('withoutDeleted', function withoutDeleted(\n\tthread: Thread,\n) {\n\tif (VERBOSE.isEnabled) VERBOSE(`withoutDeleted<${thread.nameAndSizeUntracked}>`)\n\tif (thread.filters.includes('withoutDeleted')) {\n\t\tthrow ERROR(`thread already filtered withoutDeleted:`, thread.filters, { name: thread.name })\n\t}\n\tif (!thread.filters.includes('lastWriteWins')) {\n\t\tWARN(`withoutDeleted on non-lastWriteWins thread: un-deletion (isDeleted: false) won't take effect`, { thread: thread.name })\n\t}\n\n\t// FIFO contract: byEntity must subscribe to `thread` BEFORE `result` does, so the index\n\t// is up-to-date when our mapper runs. Calling applogsByEntity here forces that order;\n\t// memoization makes it idempotent if other code already subscribed.\n\tconst byEntity = applogsByEntity(thread)\n\n\t// Per-entity count of currently-active isDeleted-class logs. 0→1 = entity becomes hidden;\n\t// 1→0 = entity becomes visible. Handles multiple isDeleted-class attrs per entity.\n\tconst activeDeletionMarkers = new Map<EntityID, number>()\n\tfor (const log of thread.applogs) {\n\t\tif (isDeletionLog(log)) {\n\t\t\tactiveDeletionMarkers.set(log.en, (activeDeletionMarkers.get(log.en) ?? 0) + 1)\n\t\t}\n\t}\n\tconst isHidden = (en: EntityID) => activeDeletionMarkers.has(en)\n\n\tconst derivation: ThreadDerivation = {\n\t\tcompute(parents) {\n\t\t\tif (parents.length !== 1) throw ERROR(`withoutDeleted requires exactly one parent`, { parents: parents.length })\n\t\t\tconst [parent] = parents\n\t\t\tactiveDeletionMarkers.clear()\n\t\t\tfor (const log of parent.applogs) {\n\t\t\t\tif (isDeletionLog(log)) {\n\t\t\t\t\tactiveDeletionMarkers.set(log.en, (activeDeletionMarkers.get(log.en) ?? 0) + 1)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn parent.applogs.filter(log => !isHidden(log.en))\n\t\t},\n\t\tmapDelta: (delta) => {\n\t\t\t// Snapshot of which entities were hidden BEFORE this delta. Required because\n\t\t\t// pass-through correctness depends on prior visibility (logs of pre-hidden\n\t\t\t// entities were never in result, so they must not appear in `removed`),\n\t\t\t// while the post-mutation `isHidden` state determines whether new content\n\t\t\t// should be admitted. Filtering only on post-mutation state breaks both\n\t\t\t// W→N (stale isDeleted=true log slips into removed → MappedThread crash)\n\t\t\t// and F→D-with-content-removal (legitimate content removal gets filtered).\n\t\t\tconst hiddenBefore = new Set(activeDeletionMarkers.keys())\n\t\t\tconst entitiesNowHidden: EntityID[] = []\n\t\t\tconst entitiesNowVisible: EntityID[] = []\n\n\t\t\tfor (const log of delta.added) {\n\t\t\t\tif (!isDeletionLog(log)) continue\n\t\t\t\tconst prev = activeDeletionMarkers.get(log.en) ?? 0\n\t\t\t\tactiveDeletionMarkers.set(log.en, prev + 1)\n\t\t\t\tif (prev === 0) entitiesNowHidden.push(log.en)\n\t\t\t}\n\t\t\tif (delta.removed) {\n\t\t\t\tfor (const log of delta.removed) {\n\t\t\t\t\tif (!isDeletionLog(log)) continue\n\t\t\t\t\tconst prev = activeDeletionMarkers.get(log.en) ?? 0\n\t\t\t\t\tconst next = prev - 1\n\t\t\t\t\tif (next > 0) activeDeletionMarkers.set(log.en, next)\n\t\t\t\t\telse {\n\t\t\t\t\t\tactiveDeletionMarkers.delete(log.en)\n\t\t\t\t\t\tentitiesNowVisible.push(log.en)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Synthetic removals: applogs of newly-hidden entities currently in result.\n\t\t\t// byEntity index is updated for this tick already (FIFO), so we filter out\n\t\t\t// applogs added in this very tick (they were never in result).\n\t\t\tconst newAddedSet = new Set(delta.added)\n\t\t\tconst syntheticRemovals: Applog[] = []\n\t\t\tfor (const en of entitiesNowHidden) {\n\t\t\t\tconst applogs = byEntity.get(en)\n\t\t\t\tif (!applogs) continue\n\t\t\t\tfor (const log of applogs) {\n\t\t\t\t\tif (!newAddedSet.has(log)) syntheticRemovals.push(log)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Synthetic additions: all current parent applogs of newly-visible entities.\n\t\t\tconst syntheticAdditions: Applog[] = []\n\t\t\tfor (const en of entitiesNowVisible) {\n\t\t\t\tconst applogs = byEntity.get(en)\n\t\t\t\tif (applogs) syntheticAdditions.push(...applogs)\n\t\t\t}\n\n\t\t\t// Pass-through: a parent log goes through to subscribers iff the entity\n\t\t\t// wasn't hidden before AND isn't hidden now. Transitions are owned by the\n\t\t\t// synthetic streams above.\n\t\t\tconst passAdded = delta.added.filter(log =>\n\t\t\t\t!hiddenBefore.has(log.en) && !isHidden(log.en))\n\t\t\tconst passRemoved = delta.removed?.filter(log =>\n\t\t\t\t!hiddenBefore.has(log.en)) ?? []\n\n\t\t\treturn {\n\t\t\t\tadded: [...passAdded, ...syntheticAdditions],\n\t\t\t\tremoved: [...passRemoved, ...syntheticRemovals],\n\t\t\t}\n\t\t},\n\t}\n\tconst result = rollingMapper(thread, derivation, { name: 'withoutDeleted', extraFilterName: 'withoutDeleted' })\n\n\treturn result\n})\n\n///////////////////////////\n// ONE-OFF QUERY (snapshot) //\n///////////////////////////\n\n/** Shared helper: create a QueryNode from a log and its context */\nfunction makeQueryNode(\n\tlog: Applog,\n\tparentNode: QueryNode | null,\n\tvarMapper: (log: Applog) => SearchContext,\n\tthreadName: string,\n): QueryNode {\n\tconst nodeVars = Object.assign({}, parentNode?.variables, varMapper(log))\n\treturn new QueryNode(\n\t\tStaticThread.fromArray([log], threadName),\n\t\tnodeVars,\n\t\tparentNode,\n\t)\n}\n\n/**\n * One-off query — returns a plain snapshot. No subscriptions, no stale-data risk.\n */\nexport const query = memoizedFn('query', function query(\n\tthreadOrLogs: Thread | Applog[],\n\tpatternOrPatterns: DatalogQueryPattern | DatalogQueryPattern[],\n\tstartVariables: SearchContext = {},\n\topts: { debug?: boolean } = {},\n): QueryResult {\n\tthrowOnTimeout()\n\tconst thread = threadFromMaybeArray(threadOrLogs)\n\tDEBUG(`query<${thread.nameAndSizeUntracked}>:`, patternOrPatterns)\n\tconst patterns = (Array.isArray(patternOrPatterns) ? patternOrPatterns : [patternOrPatterns]) as DatalogQueryPattern[]\n\twarnIfDisjointQuerySteps(patterns)\n\n\tlet prevNodes: readonly QueryNode[] | null\n\tif (patterns.length === 1) {\n\t\tprevNodes = null\n\t} else {\n\t\tconst patternsExceptLast = patterns.slice(0, -1)\n\t\tprevNodes = query(thread, patternsExceptLast, startVariables, opts).nodes\n\t}\n\tconst lastPattern = patterns[patterns.length - 1]\n\tconst stepResult = queryStepOnce(thread, prevNodes, lastPattern, opts)\n\tVERBOSE.isDisabled || VERBOSE(`query result:`, stepResult.nodes)\n\treturn stepResult\n}, {\n\targsDebugName: (thread, pattern, startVars) =>\n\t\tcreateDebugName({ caller: 'query', thread, args: startVars ? { pattern, startVars } : pattern }),\n})\n\n/**\n * One-off query step — pure filtering via makeFilter, no subscriptions.\n */\nexport function queryStepOnce(\n\tthread: Thread,\n\tprevNodes: readonly QueryNode[] | null,\n\tpattern: DatalogQueryPattern,\n\topts: { debug?: boolean } = {},\n): QueryResult {\n\tDEBUG(`queryStepOnce<${thread.nameAndSizeUntracked}> with`, prevNodes?.length ?? 'all', 'nodes, pattern:', pattern)\n\tif (!Object.entries(pattern).length) throw new Error(`Pattern is empty`)\n\n\tfunction doQueryOnce(node: QueryNode | null): QueryNode[] {\n\t\tconst [patternWithResolvedVars, variablesToFill] = resolveOrRemoveVariables(pattern, node?.variables ?? {})\n\t\tVERBOSE(`[queryStepOnce.doQuery] patternWithoutVars: `, patternWithResolvedVars)\n\t\tconst filter = makeFilter(patternWithResolvedVars)\n\t\tconst matchingLogs = filter(thread.applogs)\n\t\tconst varMapper = createObjMapper(variablesToFill)\n\n\t\tconst nodes = matchingLogs.map(log => makeQueryNode(\n\t\t\tlog, node, varMapper,\n\t\t\tcreateDebugName({\n\t\t\t\tcaller: 'QueryNode',\n\t\t\t\tthread,\n\t\t\t\tpattern: `${stringify(Object.assign({}, node?.variables, varMapper(log)))}@${stringify(patternWithResolvedVars)}`,\n\t\t\t}),\n\t\t))\n\n\t\tif (VERBOSE.isEnabled) VERBOSE(`[queryStepOnce.doQuery] nodes:`, nodes.map(n => n.variables))\n\t\tif (opts.debug) {\n\t\t\tLOG(`[queryStepOnce] step result:`, nodes.map(({ variables, logsOfThisNode: thread }) => ({\n\t\t\t\tvariables,\n\t\t\t\tthread,\n\t\t\t})))\n\t\t}\n\n\t\treturn nodes\n\t}\n\n\tif (!prevNodes) {\n\t\treturn new QueryResult(doQueryOnce(null))\n\t}\n\n\tconst allNodes = prevNodes.flatMap(inputNode => doQueryOnce(inputNode))\n\treturn new QueryResult(allNodes)\n}\n\n///////////////////////////\n// LIVE QUERY (reactive) //\n///////////////////////////\n\n/**\n * Live query — eagerly activated, always up-to-date.\n * Returns LiveQueryResult with subscribe + dispose.\n */\nexport const liveQuery = memoizedFn('liveQuery', function liveQuery(\n\tthreadOrLogs: Thread | Applog[],\n\tpatternOrPatterns: DatalogQueryPattern | DatalogQueryPattern[],\n\tstartVariables: SearchContext = {},\n\topts: { debug?: boolean } = {},\n): LiveQueryResult {\n\tthrowOnTimeout()\n\tconst thread = threadFromMaybeArray(threadOrLogs)\n\tDEBUG(`liveQuery<${thread.nameAndSizeUntracked}>:`, patternOrPatterns)\n\tconst patterns = (Array.isArray(patternOrPatterns) ? patternOrPatterns : [patternOrPatterns]) as DatalogQueryPattern[]\n\n\tlet prevResult: LiveQueryResult | null\n\tif (patterns.length === 1) {\n\t\tprevResult = null\n\t} else {\n\t\tconst patternsExceptLast = patterns.slice(0, -1)\n\t\tprevResult = liveQuery(thread, patternsExceptLast, startVariables, opts)\n\t}\n\tconst lastPattern = patterns[patterns.length - 1]\n\tconst stepResult = liveQueryStep(thread, prevResult, lastPattern, opts)\n\tVERBOSE.isDisabled || VERBOSE(`liveQuery result:`, stepResult.nodes)\n\treturn stepResult\n}, {\n\targsDebugName: (thread, pattern, startVars) =>\n\t\tcreateDebugName({ caller: 'liveQuery', thread, args: startVars ? { pattern, startVars } : pattern }),\n})\n\nexport const liveQueryStep = memoizedFn('liveQueryStep', function liveQueryStep(\n\tthread: Thread,\n\tnodeSet: LiveQueryResult | null,\n\tpattern: DatalogQueryPattern,\n\topts: { debug?: boolean } = {},\n): LiveQueryResult {\n\tDEBUG(`liveQueryStep<${thread.nameAndSizeUntracked}> with`, nodeSet?.untrackedSize ?? 'all', 'nodes, pattern:', pattern)\n\tif (!Object.entries(pattern).length) throw new Error(`Pattern is empty`)\n\n\tfunction doQuery(node: QueryNode | null): SubscribableArray<QueryNode> {\n\t\tconst [patternWithResolvedVars, variablesToFill] = resolveOrRemoveVariables(pattern, node?.variables ?? {})\n\t\tVERBOSE(`[liveQueryStep.doQuery] patternWithoutVars: `, patternWithResolvedVars)\n\t\tconst applogsMatchingStatic = rollingFilter(thread, patternWithResolvedVars)\n\t\tconst varMapper = createObjMapper(variablesToFill)\n\n\t\tfunction makeNode(log: Applog): QueryNode {\n\t\t\treturn makeQueryNode(\n\t\t\t\tlog, node, varMapper,\n\t\t\t\tcreateDebugName({\n\t\t\t\t\tcaller: 'QueryNode',\n\t\t\t\t\tthread: applogsMatchingStatic,\n\t\t\t\t\tpattern: `${stringify(Object.assign({}, node?.variables, varMapper(log)))}@${stringify(patternWithResolvedVars)}`,\n\t\t\t\t}),\n\t\t\t)\n\t\t}\n\n\t\t// Compute initial result synchronously\n\t\tconst initialNodes = applogsMatchingStatic.applogs.map(makeNode)\n\n\t\tif (VERBOSE.isEnabled) VERBOSE(`[liveQueryStep.doQuery] initial nodes:`, initialNodes.map(n => n.variables))\n\t\tif (opts.debug) {\n\t\t\tLOG(`[liveQueryStep] step result:`, initialNodes.map(({ variables, logsOfThisNode: thread }) => ({\n\t\t\t\tvariables,\n\t\t\t\tthread,\n\t\t\t})))\n\t\t}\n\n\t\t// Upstream subscription activates lazily — only when someone subscribes to us\n\t\tconst result = new SubscribableArrayImpl<QueryNode>(\n\t\t\tinitialNodes,\n\t\t\t() => applogsMatchingStatic.subscribe((event) => {\n\t\t\t\tif (isInitEvent(event)) {\n\t\t\t\t\tresult._reset(event.init.map(makeNode))\n\t\t\t\t} else {\n\t\t\t\t\tif (event.added.length) {\n\t\t\t\t\t\tresult._push(...event.added.map(makeNode))\n\t\t\t\t\t}\n\t\t\t\t\tif (event.removed?.length) {\n\t\t\t\t\t\tconst removedCids = new Set(event.removed.map(log => log.cid))\n\t\t\t\t\t\tconst toRemove = result.items.filter(qn =>\n\t\t\t\t\t\t\tremovedCids.has(qn.logsOfThisNode.applogs[0]?.cid)\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif (toRemove.length) result._remove(toRemove)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 'derived'),\n\t\t)\n\t\treturn result\n\t}\n\n\t// ── Single-step query (nodeSet === null) ──────────────────────\n\tif (!nodeSet) {\n\t\treturn new LiveQueryResult(doQuery(null))\n\t}\n\n\t// ── Multi-step query (nodeSet !== null) ────────────────────────\n\n\t// Compute initial result synchronously\n\tconst initialInners = nodeSet.nodes.map(inputNode => ({\n\t\tinputNode,\n\t\tinner: doQuery(inputNode),\n\t}))\n\tconst initialItems = initialInners.flatMap(({ inner }) => [...inner.items])\n\n\t// Lazy activation: upstream subscriptions only created when someone subscribes\n\tconst aggregated = new SubscribableArrayImpl<QueryNode>(\n\t\tinitialItems,\n\t\t() => {\n\t\t\tconst subsByInputNode = new Map<QueryNode, {\n\t\t\t\tinner: SubscribableArray<QueryNode>,\n\t\t\t\tunsub: Unsubscribe,\n\t\t\t\tnodes: QueryNode[],\n\t\t\t}>()\n\n\t\t\tfunction wireInner(inputNode: QueryNode, inner: SubscribableArray<QueryNode>): QueryNode[] {\n\t\t\t\tconst entry = { inner, unsub: null! as Unsubscribe, nodes: [...inner.items] }\n\n\t\t\t\tentry.unsub = inner.subscribe((event) => {\n\t\t\t\t\tif (isArrayInitEvent(event)) {\n\t\t\t\t\t\tif (entry.nodes.length) aggregated._remove(entry.nodes)\n\t\t\t\t\t\tentry.nodes = [...event.init]\n\t\t\t\t\t\tif (entry.nodes.length) aggregated._push(...entry.nodes)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (event.added.length) {\n\t\t\t\t\t\t\tentry.nodes.push(...event.added)\n\t\t\t\t\t\t\taggregated._push(...event.added)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (event.removed?.length) {\n\t\t\t\t\t\t\tfor (const r of event.removed) {\n\t\t\t\t\t\t\t\tconst idx = entry.nodes.indexOf(r)\n\t\t\t\t\t\t\t\tif (idx >= 0) entry.nodes.splice(idx, 1)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\taggregated._remove(event.removed)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, 'derived')\n\n\t\t\t\tsubsByInputNode.set(inputNode, entry)\n\t\t\t\treturn entry.nodes\n\t\t\t}\n\n\t\t\tfunction addInputNode(inputNode: QueryNode): QueryNode[] {\n\t\t\t\treturn wireInner(inputNode, doQuery(inputNode))\n\t\t\t}\n\n\t\t\tfunction removeInputNode(inputNode: QueryNode): QueryNode[] {\n\t\t\t\tconst entry = subsByInputNode.get(inputNode)\n\t\t\t\tif (!entry) return []\n\t\t\t\tentry.unsub()\n\t\t\t\tentry.inner.dispose()\n\t\t\t\tconst removed = entry.nodes\n\t\t\t\tsubsByInputNode.delete(inputNode)\n\t\t\t\treturn removed\n\t\t\t}\n\n\t\t\t// Reuse pre-computed inners (no re-creation of sub-queries)\n\t\t\tfor (const { inputNode, inner } of initialInners) {\n\t\t\t\twireInner(inputNode, inner)\n\t\t\t}\n\n\t\t\t// Subscribe to previous step for FUTURE changes only (no init)\n\t\t\tconst prevUnsub = nodeSet.subscribe((event) => {\n\t\t\t\tif (isArrayInitEvent(event)) {\n\t\t\t\t\tfor (const [, entry] of subsByInputNode) {\n\t\t\t\t\t\tentry.unsub(); entry.inner.dispose()\n\t\t\t\t\t}\n\t\t\t\t\tsubsByInputNode.clear()\n\t\t\t\t\tconst allNodes: QueryNode[] = []\n\t\t\t\t\tfor (const node of event.init) {\n\t\t\t\t\t\tallNodes.push(...addInputNode(node))\n\t\t\t\t\t}\n\t\t\t\t\taggregated._reset(allNodes)\n\t\t\t\t} else {\n\t\t\t\t\tif (event.added.length) {\n\t\t\t\t\t\tconst allAdded: QueryNode[] = []\n\t\t\t\t\t\tfor (const node of event.added) {\n\t\t\t\t\t\t\tallAdded.push(...addInputNode(node))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (allAdded.length) aggregated._push(...allAdded)\n\t\t\t\t\t}\n\t\t\t\t\tif (event.removed?.length) {\n\t\t\t\t\t\tconst allRemoved: QueryNode[] = []\n\t\t\t\t\t\tfor (const node of event.removed) {\n\t\t\t\t\t\t\tallRemoved.push(...removeInputNode(node))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (allRemoved.length) aggregated._remove(allRemoved)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 'derived')\n\n\t\t\treturn () => {\n\t\t\t\tprevUnsub()\n\t\t\t\tfor (const [, entry] of subsByInputNode) {\n\t\t\t\t\tentry.unsub(); entry.inner.dispose()\n\t\t\t\t}\n\t\t\t\tsubsByInputNode.clear()\n\t\t\t}\n\t\t},\n\t)\n\n\tif (VERBOSE.isEnabled) VERBOSE(`[liveQueryStep] aggregated initial:`, [...aggregated.items])\n\treturn new LiveQueryResult(aggregated)\n}, { argsDebugName: (thread, _nodes, pattern) => createDebugName({ caller: 'liveQueryStep', thread, pattern }) })\n\nexport const queryNot = memoizedFn('queryNot', function queryNot(\n\tthread: Thread,\n\tstartNodes: QueryResult,\n\tpatternOrPatterns: DatalogQueryPattern | DatalogQueryPattern[],\n\topts: { debug?: boolean } = {},\n) {\n\tconst nodes = startNodes.nodes\n\tDEBUG(`queryNot<${thread.nameAndSizeUntracked}> from: ${nodes.length} nodes`)\n\tconst patterns = (Array.isArray(patternOrPatterns) ? patternOrPatterns : [patternOrPatterns]) as DatalogQueryPattern[]\n\n\t// For each node, run all patterns as a joined multi-step query.\n\t// Exclude the node if ANY complete binding exists across all steps.\n\tconst filtered = nodes.filter(function innerNodeFilter({ variables }) {\n\t\t// Start with a single binding from the node's variables\n\t\tlet bindings: Record<string, any>[] = [variables ?? {}]\n\n\t\tfor (const pattern of patterns) {\n\t\t\tif (!Object.entries(pattern).length) throw new Error(`Pattern is empty`)\n\t\t\tconst nextBindings: Record<string, any>[] = []\n\n\t\t\tfor (const binding of bindings) {\n\t\t\t\tconst [resolved, varsToFill] = resolveOrRemoveVariables(pattern, binding)\n\t\t\t\tconst filter = makeFilter(resolved)\n\t\t\t\tconst matchingLogs = filter(thread.applogs)\n\t\t\t\tconst varMapper = createObjMapper(varsToFill)\n\n\t\t\t\tfor (const log of matchingLogs) {\n\t\t\t\t\tnextBindings.push({ ...binding, ...varMapper(log) })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbindings = nextBindings\n\t\t\tif (bindings.length === 0) break // no matches — node is safe, skip remaining patterns\n\t\t}\n\n\t\tVERBOSE(`[queryNot] node:`, variables, '=> bindings:', bindings.length)\n\t\tif (opts.debug) LOG(`[queryNot] node result:`, variables, '=>', bindings)\n\t\treturn bindings.length === 0 // keep node if no complete match found\n\t})\n\treturn new QueryResult([...filtered])\n}, { argsDebugName: (thread, nodes, pattern) => createDebugName({ caller: 'queryNot', thread, pattern }) })\n\n/** Live variant: queryNot with incremental updates.\n * - Thread additions: O(new_applogs × included_nodes) — only checks new applogs\n * - Thread removals/resets: full recompute (rare for append-mostly logs)\n * - Upstream node additions: O(new_nodes × applogs)\n * - Upstream node removals: removed from output\n */\nexport const liveQueryNot = memoizedFn('liveQueryNot', function liveQueryNot(\n\tthread: Thread,\n\tupstream: LiveQueryResult,\n\tpatternOrPatterns: DatalogQueryPattern | DatalogQueryPattern[],\n\topts: { debug?: boolean } = {},\n) {\n\tconst patterns = (Array.isArray(patternOrPatterns) ? patternOrPatterns : [patternOrPatterns]) as DatalogQueryPattern[]\n\n\t/** Check if a node should be excluded (matches the NOT patterns as a joined multi-step query) */\n\tfunction nodeMatchesNot(node: QueryNode, applogs: readonly Applog[]): boolean {\n\t\tlet bindings: Record<string, any>[] = [node.variables ?? {}]\n\t\tfor (const pattern of patterns) {\n\t\t\tconst nextBindings: Record<string, any>[] = []\n\t\t\tfor (const binding of bindings) {\n\t\t\t\tconst [resolved, varsToFill] = resolveOrRemoveVariables(pattern, binding)\n\t\t\t\tconst filter = makeFilter(resolved)\n\t\t\t\tconst varMapper = createObjMapper(varsToFill)\n\t\t\t\tfor (const log of filter(applogs)) {\n\t\t\t\t\tnextBindings.push({ ...binding, ...varMapper(log) })\n\t\t\t\t}\n\t\t\t}\n\t\t\tbindings = nextBindings\n\t\t\tif (bindings.length === 0) return false // no matches — node passes\n\t\t}\n\t\treturn bindings.length > 0 // excluded if any complete binding exists\n\t}\n\n\t/** Full recompute: filter all upstream nodes against all thread applogs */\n\tfunction computeAll(): QueryNode[] {\n\t\treturn upstream.nodes.filter(node => !nodeMatchesNot(node, thread.applogs))\n\t}\n\n\tconst result = new SubscribableArrayImpl<QueryNode>(\n\t\tcomputeAll(),\n\t\t() => {\n\t\t\t// Subscribe to thread changes\n\t\t\tconst threadUnsub = thread.subscribe((event) => {\n\t\t\t\tif (isInitEvent(event)) {\n\t\t\t\t\t// Full reset — recompute everything\n\t\t\t\t\tresult._reset(computeAll())\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (event.removed?.length) {\n\t\t\t\t\t// Removals: a previously-excluded node might now pass — full recompute\n\t\t\t\t\tresult._reset(computeAll())\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (event.added.length) {\n\t\t\t\t\t// Additions: only check new applogs against currently-included nodes\n\t\t\t\t\tconst toRemove = result.items.filter(node => nodeMatchesNot(node, event.added))\n\t\t\t\t\tif (toRemove.length > 0) {\n\t\t\t\t\t\tresult._remove(toRemove)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 'derived')\n\n\t\t\t// Subscribe to upstream node changes\n\t\t\tconst upstreamUnsub = upstream.subscribe((event) => {\n\t\t\t\tif (isArrayInitEvent(event)) {\n\t\t\t\t\tresult._reset(computeAll())\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// New upstream nodes: check each against full thread\n\t\t\t\tif (event.added.length) {\n\t\t\t\t\tconst passing = event.added.filter(node => !nodeMatchesNot(node, thread.applogs))\n\t\t\t\t\tif (passing.length > 0) result._push(...passing)\n\t\t\t\t}\n\n\t\t\t\t// Removed upstream nodes: remove from our output\n\t\t\t\tif (event.removed?.length) {\n\t\t\t\t\tconst removedSet = new Set(event.removed)\n\t\t\t\t\tconst toRemove = result.items.filter(node => removedSet.has(node))\n\t\t\t\t\tif (toRemove.length > 0) result._remove(toRemove)\n\t\t\t\t}\n\t\t\t}, 'derived')\n\n\t\t\treturn () => { threadUnsub(); upstreamUnsub() }\n\t\t},\n\t)\n\n\treturn new LiveQueryResult(result)\n}, { argsDebugName: (thread, _nodes, pattern) => createDebugName({ caller: 'liveQueryNot', thread, pattern }) })\n\n// export function or(queries: QueryExecutor[]) {\n// return tagged(\n// `or{${stringify(queries)} } `,\n// function orExecutor(args: QueryExecutorArguments) {\n// const { db, nodes: contexts } = args\n// VERBOSE('[or]', { queries, contexts })\n// let results = []\n// for (const query of queries) {\n// const res = query(args)\n// VERBOSE('[or] query', query, 'result =>', res)\n// results.push(...res.nodes)\n// }\n// return { contexts: results }\n// }\n// )\n// }\n\n// export type Tagged<T> = T & { tag: string }\n// export function tagged<T>(tag: string, thing: T): Tagged<T> {\n// const e = thing as (T & { tag: string })\n// e.tag = tag\n// return e\n// }\n\n//////////////////////\n// COMPOSED QUERIES //\n//////////////////////\n\n/** One-off: filter thread by pattern, map to values. Returns plain array. */\nexport const filterAndMap = memoizedFn('filterAndMap', function filterAndMap<R>(\n\tthread: Thread,\n\tpattern: DatalogQueryPattern,\n\tmapper: (keyof Applog) | (Partial<{ [key in keyof Applog]: string }>) | ((applog: Applog) => R),\n) {\n\tDEBUG(`filterAndMap<${thread.nameAndSizeUntracked}>`, pattern)\n\tconst filter = makeFilter(pattern)\n\tconst filtered = filter(thread.applogs)\n\treturn mapApplogsWith(filtered, mapper)\n}, { argsDebugName: (thread, pattern) => createDebugName({ caller: 'filterAndMap', thread, pattern }) })\n\n/** Live variant: returns SubscribableArray that updates when thread changes. */\nexport const liveFilterAndMap = memoizedFn('liveFilterAndMap', function liveFilterAndMap<R>(\n\tthread: Thread,\n\tpattern: DatalogQueryPattern,\n\tmapper: (keyof Applog) | (Partial<{ [key in keyof Applog]: string }>) | ((applog: Applog) => R),\n) {\n\tDEBUG(`liveFilterAndMap<${thread.nameAndSizeUntracked}>`, pattern)\n\tconst filtered = rollingFilter(thread, pattern)\n\tconst mapFn = makeApplogMapper(mapper)\n\n\tconst cidToMapped = new Map<CidString, R>()\n\tconst mapAndTrack = (log: Applog): R => {\n\t\tconst r = mapFn(log)\n\t\tcidToMapped.set(log.cid, r)\n\t\treturn r\n\t}\n\n\tconst initial = filtered.applogs.map(mapAndTrack)\n\tconst result = new SubscribableArrayImpl<R>(\n\t\tinitial,\n\t\t() => filtered.subscribe((event) => {\n\t\t\tif (isInitEvent(event)) {\n\t\t\t\tcidToMapped.clear()\n\t\t\t\tresult._reset(event.init.map(mapAndTrack))\n\t\t\t} else {\n\t\t\t\tif (event.added.length) result._push(...event.added.map(mapAndTrack))\n\t\t\t\tif (event.removed?.length) {\n\t\t\t\t\tconst toRemove: R[] = []\n\t\t\t\t\tfor (const log of event.removed) {\n\t\t\t\t\t\tconst r = cidToMapped.get(log.cid)\n\t\t\t\t\t\tif (r === undefined) {\n\t\t\t\t\t\t\tWARN(`[liveFilterAndMap] removed log not in cidToMapped`, { log })\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcidToMapped.delete(log.cid)\n\t\t\t\t\t\ttoRemove.push(r)\n\t\t\t\t\t}\n\t\t\t\t\tif (toRemove.length) result._remove(toRemove)\n\t\t\t\t}\n\t\t\t}\n\t\t}, 'derived'),\n\t)\n\treturn result\n}, { argsDebugName: (thread, pattern) => createDebugName({ caller: 'liveFilterAndMap', thread, pattern }) })\n\n/** One-off: query and map results. Returns plain array. */\nexport const queryAndMap = memoizedFn('queryAndMap', function queryAndMap<R>(\n\tthreadOrLogs: Thread | Applog[],\n\tpatternOrPatterns: Parameters<typeof query>[1],\n\tmapDef: string | (Partial<{ [key in keyof SearchContext]: string }>) | ((record: SearchContext) => R),\n\tvariables: SearchContext = {},\n) {\n\tconst thread = threadFromMaybeArray(threadOrLogs)\n\tDEBUG(`queryAndMap<${thread.nameAndSizeUntracked}>`, { patternOrPatterns, variables, map: mapDef })\n\tconst queryResult = query(thread, patternOrPatterns)\n\treturn mapQueryResultWith(queryResult, mapDef)\n}, { argsDebugName: (thread, pattern) => createDebugName({ caller: 'queryAndMap', thread, pattern }) })\n\n/** Live variant: query and map results, returns SubscribableArray that updates reactively. */\nexport const liveQueryAndMap = memoizedFn('liveQueryAndMap', function liveQueryAndMap<R>(\n\tthread: Thread,\n\tpatternOrPatterns: Parameters<typeof liveQuery>[1],\n\tmapDef: string | (Partial<{ [key in keyof SearchContext]: string }>) | ((record: SearchContext) => R),\n) {\n\tDEBUG(`liveQueryAndMap<${thread.nameAndSizeUntracked}>`, { patternOrPatterns, map: mapDef })\n\tconst live = liveQuery(thread, patternOrPatterns)\n\n\tfunction computeAll(): R[] {\n\t\tconst snapshot = new QueryResult(live.nodes)\n\t\treturn mapQueryResultWith(snapshot, mapDef) as R[]\n\t}\n\n\tconst result = new SubscribableArrayImpl<R>(\n\t\tcomputeAll(),\n\t\t() => live.subscribe(() => {\n\t\t\tresult._reset(computeAll())\n\t\t}, 'derived'),\n\t)\n\treturn result\n}, { argsDebugName: (thread, pattern) => createDebugName({ caller: 'liveQueryAndMap', thread, pattern }) })\n\n/** One-off: query entity attributes. Returns Record or null. Requires current-state thread (LWW). */\nexport const queryEntity = memoizedFn('queryEntity', function queryEntity(\n\tthread: Thread,\n\tname: string,\n\tentityID: EntityID,\n\tattributes: readonly string[],\n) {\n\tassertLWW(thread)\n\tDEBUG(`queryEntity<${thread.nameAndSizeUntracked}>`, entityID, name)\n\tconst filter = makeFilter({ en: entityID, at: prefixAttrs(name, attributes) })\n\tconst filtered = filter(thread.applogs)\n\tVERBOSE(`queryEntity applogs:`, filtered)\n\tif (filtered.length === 0) return null\n\treturn Object.fromEntries(\n\t\tfiltered.map(({ at, vl }) => [at.slice(name.length + 1), vl]),\n\t)\n}, {\n\targsDebugName: (thread, name, entityID) => createDebugName({ caller: 'queryEntity', thread, args: { name, entityID } }),\n})\n\n/** Live variant: returns Subscribable that updates when entity attributes change. Requires current-state thread (LWW). */\nexport const liveQueryEntity = memoizedFn('liveQueryEntity', function liveQueryEntity(\n\tthread: Thread,\n\tname: string,\n\tentityID: EntityID,\n\tattributes: readonly string[],\n) {\n\tassertLWW(thread)\n\tDEBUG(`liveQueryEntity<${thread.nameAndSizeUntracked}>`, entityID, name)\n\tconst filtered = rollingFilter(thread, { en: entityID, at: prefixAttrs(name, attributes) })\n\n\tfunction compute() {\n\t\tif (filtered.isEmpty) return null\n\t\treturn Object.fromEntries(\n\t\t\tfiltered.map(({ at, vl }) => [at.slice(name.length + 1), vl]),\n\t\t)\n\t}\n\n\tconst result = new SubscribableImpl<Record<string, ApplogValue> | null>(\n\t\tcompute(),\n\t\t() => filtered.subscribe(() => {\n\t\t\tresult._set(compute())\n\t\t}, 'derived'),\n\t)\n\treturn result\n}, {\n\targsDebugName: (thread, name, entityID) => createDebugName({ caller: 'liveQueryEntity', thread, args: { name, entityID } }),\n})\n\n/** Live single-attribute query. Requires current-state thread (LWW). Returns Subscribable<T | null>. */\nexport const liveEntityAt = memoizedFn('liveEntityAt', function liveEntityAt<T extends ApplogValue>(\n\tthread: Thread,\n\tentityID: EntityID,\n\tat: string,\n) {\n\tassertLWW(thread)\n\tDEBUG(`liveEntityAt<${thread.nameAndSizeUntracked}>`, entityID, at)\n\tconst filtered = rollingFilter(thread, { en: entityID, at })\n\n\tfunction compute(): T | null {\n\t\tif (filtered.isEmpty) return null\n\t\treturn filtered.applogs[filtered.applogs.length - 1].vl as T\n\t}\n\n\tconst result = new SubscribableImpl<T | null>(\n\t\tcompute(),\n\t\t() => filtered.subscribe(() => {\n\t\t\tresult._set(compute())\n\t\t}, 'derived'),\n\t)\n\treturn result\n}, {\n\targsDebugName: (thread, entityID, at) => createDebugName({ caller: 'liveEntityAt', thread, args: { entityID, at } }),\n})\n\nexport const agentsOfThread = memoizedFn('agentsOfThread', function agentsOfThread(\n\tthread: Thread,\n) {\n\tDEBUG(`agentsOfThread<${thread.nameAndSizeUntracked}>`)\n\n\tconst mapped = new Map<string, number>()\n\tconst onEvent = (event: ThreadEvent) => {\n\t\tfor (const log of (isInitEvent(event) ? event.init : event.added)) {\n\t\t\tconst prev = mapped.get(log.ag) ?? 0\n\t\t\tmapped.set(log.ag, prev + 1)\n\t\t}\n\t\tfor (const log of (!isInitEvent(event) && event.removed || [])) {\n\t\t\tconst prev = mapped.get(log.ag)\n\t\t\tif (!prev || prev < 1) throw ERROR(`[agentsOfThread] number is now negative`, { log, event, mapped, prev })\n\t\t\tmapped.set(log.ag, prev - 1)\n\t\t}\n\t\tLOG(`agentsOfThread<${thread.nameAndSizeUntracked}> processed event`, { event, mapped })\n\t}\n\n\tonEvent({ init: thread.applogs })\n\tthread.subscribe(onEvent, 'derived')\n\t// TODO: cleanup via ref-counted disposal when no longer needed\n\n\treturn mapped\n})\n\nexport const entityOverlap = memoizedFn('entityOverlap', function entityOverlapCount(\n\tthreadA: Thread,\n\tthreadB: Thread,\n) {\n\tLOG(`entityOverlap<${threadA.nameAndSizeUntracked}, ${threadB.nameAndSizeUntracked}>`)\n\n\t// Compute once — snapshot, not reactive (TODO: migrate to Subscribable)\n\tconst entitiesA = new Set(threadA.map(log => log.en))\n\tconst entitiesB = new Set(threadB.map(log => log.en))\n\treturn [...entitiesA].filter(en => entitiesB.has(en))\n})\n\nexport const entityOverlapMap = function entityOverlapMap(\n\tthreadA: Thread,\n\tthreadB: Thread,\n\tthreadAName = 'incoming',\n\tthreadBName = 'current',\n) {\n\tconst useInferredVM = (en, thread: Thread) => en\n\tconst overlapping = entityOverlap(threadA, threadB)\n\tconst mapped = new Map()\n\toverlapping.forEach(eachEntityID => (\n\t\tmapped.set(eachEntityID, {\n\t\t\t[threadAName]: useInferredVM(eachEntityID, threadA),\n\t\t\t[threadBName]: useInferredVM(eachEntityID, threadB),\n\t\t})\n\t))\n}\n\nexport const entityOverlapCount = memoizedFn(\n\t'entityOverlapCount',\n\tfunction entityOverlapCount(threadA: Thread, threadB: Thread) {\n\t\treturn entityOverlap(threadA, threadB).length\n\t},\n)\n\n/** Live variant: entity overlap count as Subscribable<number>. */\nexport const liveEntityOverlapCount = memoizedFn(\n\t'liveEntityOverlapCount',\n\tfunction liveEntityOverlapCount(threadA: Thread, threadB: Thread) {\n\t\tfunction compute() {\n\t\t\tconst entitiesA = new Set(threadA.map(log => log.en))\n\t\t\tconst entitiesB = new Set(threadB.map(log => log.en))\n\t\t\treturn [...entitiesA].filter(en => entitiesB.has(en)).length\n\t\t}\n\n\t\tconst result = new SubscribableImpl<number>(\n\t\t\tcompute(),\n\t\t\t() => {\n\t\t\t\tconst unsub1 = threadA.subscribe(() => result._set(compute()), 'derived')\n\t\t\t\tconst unsub2 = threadB.subscribe(() => result._set(compute()), 'derived')\n\t\t\t\treturn () => { unsub1(); unsub2() }\n\t\t\t},\n\t\t)\n\t\treturn result\n\t},\n)\n\nexport const querySingle = memoizedFn('querySingle', function querySingle(\n\tthreadOrLogs: Thread | Applog[],\n\tpatternOrPatterns: Parameters<typeof query>[1],\n\tvariables: SearchContext = {},\n) {\n\tconst result = query(threadOrLogs, patternOrPatterns, variables)\n\t// Snapshot — not reactive (TODO: migrate to Subscribable<Applog | null>)\n\tif (result.isEmpty) return null\n\tif (result.size > 1) throw ERROR(`[querySingle] got`, result.size, `results:`, result)\n\tconst logsOfThisNode = result.nodes[0].logsOfThisNode\n\tif (logsOfThisNode.size != 1) throw ERROR(`[querySingle] single result, but got`, logsOfThisNode.size, `logs:`, logsOfThisNode.applogs)\n\treturn logsOfThisNode.applogs[0]\n}, {\n\targsDebugName: (thread, pattern) => createDebugName({ caller: 'querySingle', thread, pattern }),\n})\n\nexport const querySingleAndMap = memoizedFn(\n\t'querySingleAndMap',\n\tfunction querySingleAndMap<MAP extends (keyof Applog | (Partial<{ [key in keyof Applog]: string }>))>(\n\t\tthreadOrLogs: Thread | Applog[],\n\t\tpatternOrPatterns: Parameters<typeof query>[1],\n\t\tmapDef: MAP,\n\t\tvariables: SearchContext = {},\n\t) {\n\t\tconst log = querySingle(threadOrLogs, patternOrPatterns, variables)\n\t\t// Snapshot — not reactive (TODO: migrate to Subscribable<T>)\n\t\tif (!log) return undefined\n\t\tif (typeof mapDef === 'string') {\n\t\t\treturn log[mapDef as string]\n\t\t} else {\n\t\t\treturn createObjMapper(mapDef)(log)\n\t\t}\n\t},\n\t{\n\t\targsDebugName: (thread, pattern) => createDebugName({ caller: 'querySingleAndMap', thread, pattern }),\n\t},\n)\n\n/** Live variant: querySingle returning Subscribable<Applog | null>. */\nexport const liveQuerySingle = memoizedFn('liveQuerySingle', function liveQuerySingle(\n\tthread: Thread,\n\tpatternOrPatterns: Parameters<typeof liveQuery>[1],\n) {\n\tDEBUG(`liveQuerySingle<${thread.nameAndSizeUntracked}>`)\n\tconst live = liveQuery(thread, patternOrPatterns)\n\n\tfunction compute(): Applog | null {\n\t\tif (live.isEmpty) return null\n\t\tif (live.size > 1) throw ERROR(`[liveQuerySingle] got`, live.size, `results`)\n\t\tconst logsOfThisNode = live.nodes[0].logsOfThisNode\n\t\tif (logsOfThisNode.size !== 1) throw ERROR(`[liveQuerySingle] single result, but got`, logsOfThisNode.size, `logs`)\n\t\treturn logsOfThisNode.applogs[0]\n\t}\n\n\tconst result = new SubscribableImpl<Applog | null>(\n\t\tcompute(),\n\t\t() => live.subscribe(() => {\n\t\t\tresult._set(compute())\n\t\t}),\n\t)\n\treturn result\n}, {\n\targsDebugName: (thread, pattern) => createDebugName({ caller: 'liveQuerySingle', thread, pattern }),\n})\n\n/** Live variant: querySingleAndMap returning Subscribable<T | undefined>. */\nexport const liveQuerySingleAndMap = memoizedFn(\n\t'liveQuerySingleAndMap',\n\tfunction liveQuerySingleAndMap<MAP extends (keyof Applog | (Partial<{ [key in keyof Applog]: string }>))>(\n\t\tthread: Thread,\n\t\tpatternOrPatterns: Parameters<typeof liveQuery>[1],\n\t\tmapDef: MAP,\n\t) {\n\t\tDEBUG(`liveQuerySingleAndMap<${thread.nameAndSizeUntracked}>`)\n\t\tconst liveSingle = liveQuerySingle(thread, patternOrPatterns)\n\n\t\tfunction compute() {\n\t\t\tconst log = liveSingle.value\n\t\t\tif (!log) return undefined\n\t\t\tif (typeof mapDef === 'string') {\n\t\t\t\treturn log[mapDef as string]\n\t\t\t} else {\n\t\t\t\treturn createObjMapper(mapDef)(log)\n\t\t\t}\n\t\t}\n\n\t\tconst result = new SubscribableImpl<any>(\n\t\t\tcompute(),\n\t\t\t() => liveSingle.subscribe(() => {\n\t\t\t\tresult._set(compute())\n\t\t\t}),\n\t\t)\n\t\treturn result\n\t},\n\t{\n\t\targsDebugName: (thread, pattern) => createDebugName({ caller: 'liveQuerySingleAndMap', thread, pattern }),\n\t},\n)\n\n/////////////\n// HELPERS //\n/////////////\n\n/** Create a single-applog mapper function from a mapDef */\nexport function makeApplogMapper<R>(\n\tmapDef: (keyof Applog) | (Partial<{ [key in keyof Applog]: string }>) | ((applog: Applog) => R),\n): (applog: Applog) => R {\n\tif (typeof mapDef === 'function') {\n\t\treturn mapDef as (applog: Applog) => R\n\t} else if (typeof mapDef === 'string') {\n\t\treturn (log: Applog) => log[mapDef] as R\n\t} else {\n\t\treturn createObjMapper(mapDef) as (applog: Applog) => R\n\t}\n}\n\n/** Map an array of applogs using a mapDef */\nexport function mapApplogsWith<R>(\n\tapplogs: readonly Applog[],\n\tmapDef: (keyof Applog) | (Partial<{ [key in keyof Applog]: string }>) | ((applog: Applog) => R),\n) {\n\treturn applogs.map(makeApplogMapper(mapDef))\n}\n\nexport const mapThreadWith = function filterAndMapGetterFx<R>(\n\tthread: Thread,\n\tmapDef: (keyof Applog) | (Partial<{ [key in keyof Applog]: string }>) | ((applog: Applog) => R),\n) {\n\treturn mapApplogsWith(thread.applogs, mapDef)\n}\nexport const mapQueryResultWith = function filterAndMapGetterFx<R>(\n\tqueryResult: QueryResult,\n\tmapDef: string | (Partial<{ [key in keyof SearchContext]: string }>) | ((record: SearchContext) => R),\n) {\n\tif (typeof mapDef === 'function') {\n\t\treturn queryResult.records.map(mapDef)\n\t} else if (typeof mapDef === 'string') {\n\t\treturn queryResult.nodes.map((node) => {\n\t\t\tif (!Object.hasOwn(node.record, mapDef)) {\n\t\t\t\tif (node.logsOfThisNode.size !== 1) {\n\t\t\t\t\tthrow ERROR(`not sure what to map (it's not a var and a result node log count of ${node.logsOfThisNode.size})`)\n\t\t\t\t}\n\t\t\t\treturn node.logsOfThisNode.firstLog[mapDef]\n\t\t\t}\n\t\t\treturn node.record[mapDef]\n\t\t})\n\t} else {\n\t\treturn queryResult.nodes.map((node) => {\n\t\t\treturn createObjMapper(mapDef)(node.record)\n\t\t})\n\t}\n}\n/**\n * Map Applog to custom named record, e.g.:\n * { en: 'movieID', vl: 'movieName' }\n * will map the applog to { movieID: .., movieName: .. }\n */\nexport function createObjMapper<FROM extends string, TO extends string>(applogFieldMap: Partial<{ [key in FROM]: TO }>) {\n\treturn (applog: { [key in FROM]: any }) => {\n\t\treturn Object.entries(applogFieldMap).reduce((acc, [key, value]) => {\n\t\t\tacc[value as TO] = applog[key]\n\t\t\treturn acc\n\t\t}, {} as Partial<{ [key in TO]: ApplogValue }>)\n\t}\n}\n\nexport function startsWith(str: string) {\n\treturn (value) => value.startsWith(str)\n}\n\nexport function prefixAttrs(prefix: string, attrs: readonly string[]) {\n\treturn attrs.map(at => prefixAt(prefix, at))\n}\nexport function prefixAt(prefix: string, attr: string) {\n\treturn `${prefix}/${attr}`\n}\n\n/** Inverse of prefixAt — strips everything up to and including the first `/` */\nexport function stripAtPrefix(attr: string): string {\n\tconst idx = attr.indexOf('/')\n\treturn idx >= 0 ? attr.slice(idx + 1) : attr\n}\n\n/** Create a key mapper from an explicit attribute→key record */\nexport function mapAttributes<A extends string>(mapping: Record<A, string>): (attr: A) => string {\n\treturn (attr) => mapping[attr] ?? attr\n}\n\n/** Resolve key mapping options to a concrete mapper function */\nexport function resolveKeyMapper(opts?: { stripAtPrefix?: true | string; mapKeys?: (attr: string) => string }): (attr: string) => string {\n\tif (!opts) return (attr) => attr\n\tif (opts.mapKeys) return opts.mapKeys\n\tif (opts.stripAtPrefix === true) return stripAtPrefix\n\tif (typeof opts.stripAtPrefix === 'string') {\n\t\tconst prefix = opts.stripAtPrefix + '/'\n\t\treturn (attr) => attr.startsWith(prefix) ? attr.slice(prefix.length) : attr\n\t}\n\treturn (attr) => attr\n}\nexport function threadFromMaybeArray(threadOrLogs: Thread | Applog[], name?: string) {\n\tif (!Array.isArray(threadOrLogs)) {\n\t\treturn threadOrLogs\n\t}\n\treturn ThreadInMemory.fromArray(threadOrLogs, name || `threadFromArray[${threadOrLogs.length}]`, true)\n}\nexport function withTimeout<R>(timeoutMilliseconds: number, func: () => R) {\n\tif (globalQueryTimeoutTime) throw ERROR(`Nested timeout not supported`)\n\tglobalQueryTimeoutTime = performance.now() + timeoutMilliseconds\n\ttry {\n\t\treturn func()\n\t} finally {\n\t\tglobalQueryTimeoutTime = null\n\t}\n}\nfunction getPatternVariableNames(pattern: DatalogQueryPattern): Set<string> {\n\tconst vars = new Set<string>()\n\tfor (const value of Object.values(pattern)) {\n\t\tif (isVariable(value)) {\n\t\t\tvars.add((value as string).slice(1))\n\t\t}\n\t}\n\treturn vars\n}\n\n/**\n * Warn if a multi-step query has steps that are not connected via shared variables.\n * Disconnected steps produce a cartesian product instead of a join.\n */\nfunction warnIfDisjointQuerySteps(patterns: DatalogQueryPattern[]) {\n\tif (patterns.length < 2) return\n\n\tconst varSets = patterns.map(getPatternVariableNames)\n\tconst reachable = new Set(varSets[0])\n\n\tfor (let i = 1; i < varSets.length; i++) {\n\t\tconst stepVars = varSets[i]\n\t\tif (stepVars.size === 0) {\n\t\t\tWARN(\n\t\t\t\t`[query] Step ${i} has no variables — it produces identical results regardless of previous steps (cartesian product).`,\n\t\t\t\t`Patterns:`, patterns,\n\t\t\t)\n\t\t\tcontinue\n\t\t}\n\t\tconst connected = [...stepVars].some(v => reachable.has(v))\n\t\tif (!connected) {\n\t\t\tWARN(\n\t\t\t\t`[query] Step ${i} is disconnected from previous steps — no shared variable.`,\n\t\t\t\t`This produces a cartesian product instead of a join.`,\n\t\t\t\t`Step ${i} variables: {${[...stepVars].join(', ')}}`,\n\t\t\t\t`Reachable from prior steps: {${[...reachable].join(', ')}}`,\n\t\t\t\t`Patterns:`, patterns,\n\t\t\t)\n\t\t}\n\t\tfor (const v of stepVars) reachable.add(v)\n\t}\n}\n\nexport function throwOnTimeout() {\n\tif (globalQueryTimeoutTime == null) return\n\tif (performance.now() >= globalQueryTimeoutTime) {\n\t\tthrow new QueryTimeoutError(globalQueryTimeoutTime)\n\t}\n}\nclass QueryTimeoutError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t}\n}\n"],"mappings":"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","names":["lastWriteWins","withoutDeleted","query","thread","liveQuery","liveQueryStep","queryNot","liveQueryNot","filterAndMap","liveFilterAndMap","queryAndMap","liveQueryAndMap","queryEntity","liveQueryEntity","liveEntityAt","agentsOfThread","entityOverlapMap","entityOverlapCount","liveEntityOverlapCount","querySingle","querySingleAndMap","liveQuerySingle","liveQuerySingleAndMap","filterAndMapGetterFx"]}
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import {
|
|
2
|
+
keepTruthy
|
|
3
|
+
} from "./chunk-YDAKBU6Q.js";
|
|
4
|
+
import {
|
|
5
|
+
lastWriteWins
|
|
6
|
+
} from "./chunk-3SUFNJEZ.js";
|
|
7
|
+
import {
|
|
8
|
+
anyOf,
|
|
9
|
+
areCidsEqual,
|
|
10
|
+
encodeBlockOriginal,
|
|
11
|
+
ensureTsPvAndFinalizeApplog,
|
|
12
|
+
getLogsFromThread,
|
|
13
|
+
prepareForPub,
|
|
14
|
+
rollingFilter
|
|
15
|
+
} from "./chunk-SHUHRHOT.js";
|
|
16
|
+
|
|
17
|
+
// src/pubsub/snap-push.ts
|
|
18
|
+
import * as dagJson2 from "@ipld/dag-json";
|
|
19
|
+
import { Logger as Logger2 } from "besonders-logger";
|
|
20
|
+
import stringify from "safe-stable-stringify";
|
|
21
|
+
|
|
22
|
+
// src/ipfs/car.ts
|
|
23
|
+
import { CarReader, CarWriter } from "@ipld/car";
|
|
24
|
+
import * as dagJson from "@ipld/dag-json";
|
|
25
|
+
import { Logger } from "besonders-logger";
|
|
26
|
+
import { CID } from "multiformats";
|
|
27
|
+
var { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO);
|
|
28
|
+
async function decodePubFromCar(car) {
|
|
29
|
+
const decoded = await getBlocksOfCar(car);
|
|
30
|
+
return await decodePubFromBlocks(decoded);
|
|
31
|
+
}
|
|
32
|
+
async function decodePubFromBlocks({ rootCID, blockStore }, _recursionTrace = [], stopAtCID) {
|
|
33
|
+
if (!rootCID || !blockStore) {
|
|
34
|
+
throw ERROR("Empty roots/blocks", { rootCID, blockStore });
|
|
35
|
+
}
|
|
36
|
+
let allApplogs = [];
|
|
37
|
+
let firstInfo = null;
|
|
38
|
+
let currentCID = rootCID;
|
|
39
|
+
const visited = /* @__PURE__ */ new Set();
|
|
40
|
+
let applogsCID = null;
|
|
41
|
+
while (currentCID) {
|
|
42
|
+
const cidStr = currentCID.toString();
|
|
43
|
+
if (visited.has(cidStr)) {
|
|
44
|
+
throw ERROR("[decodePubFromBlocks] pub chain has a loop", {
|
|
45
|
+
currentCID: cidStr,
|
|
46
|
+
visited: [...visited]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
visited.add(cidStr);
|
|
50
|
+
const root = await getDecodedBlock(blockStore, currentCID);
|
|
51
|
+
VERBOSE(`[decodePubFromBlocks] root:`, cidStr, root, { blockStore });
|
|
52
|
+
if (!root) {
|
|
53
|
+
throw ERROR("[decodePubFromBlocks] root not found in blockStore", { blockStore, currentCID: cidStr });
|
|
54
|
+
}
|
|
55
|
+
let pubLogsArray;
|
|
56
|
+
if (root?.info) {
|
|
57
|
+
if (!applogsCID) applogsCID = root.applogs;
|
|
58
|
+
const applogsBlock = await getDecodedBlock(blockStore, root.applogs);
|
|
59
|
+
pubLogsArray = await unchunkApplogsBlock(applogsBlock, blockStore);
|
|
60
|
+
if (!firstInfo) {
|
|
61
|
+
firstInfo = await getDecodedBlock(blockStore, root.info);
|
|
62
|
+
DEBUG(`new format - infoLogs`, firstInfo.logs.map((l) => ({ [l.toString()]: l })));
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
pubLogsArray = root.applogs;
|
|
66
|
+
}
|
|
67
|
+
const resolveLogFromCidLink = async (cidOrLink) => {
|
|
68
|
+
const cid = cidOrLink;
|
|
69
|
+
const applog = await getDecodedBlock(blockStore, cid);
|
|
70
|
+
if (!applog) {
|
|
71
|
+
ERROR(`Could not find applog CID in pub blocks:`, cid.toString(), { cid, root, blockStore });
|
|
72
|
+
throw new Error(`Could not find applog CID in pub blocks: ${cid.toString()}`);
|
|
73
|
+
}
|
|
74
|
+
if (applog.pv instanceof CID) applog.pv = applog.pv.toV1().toString();
|
|
75
|
+
return {
|
|
76
|
+
...applog,
|
|
77
|
+
cid: cid.toV1().toString()
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const snapshotApplogs = await Promise.all(pubLogsArray.map(resolveLogFromCidLink));
|
|
81
|
+
allApplogs = allApplogs.concat(snapshotApplogs);
|
|
82
|
+
if (!root.prev) {
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
if (stopAtCID && areCidsEqual(root.prev, stopAtCID)) {
|
|
86
|
+
DEBUG("[decodePubFromBlocks] stopping at stopAtCID:", stopAtCID.toString());
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
const prevBytes = await blockStore.get(root.prev);
|
|
90
|
+
if (!prevBytes) {
|
|
91
|
+
throw ERROR("[decodePubFromBlocks] prev snapshot missing from blockStore", {
|
|
92
|
+
currentCID: cidStr,
|
|
93
|
+
prev: root.prev.toString(),
|
|
94
|
+
stopAtCID: stopAtCID?.toString(),
|
|
95
|
+
visited: [...visited]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
currentCID = root.prev;
|
|
99
|
+
}
|
|
100
|
+
const result = {
|
|
101
|
+
cid: rootCID,
|
|
102
|
+
info: firstInfo ? {
|
|
103
|
+
...firstInfo,
|
|
104
|
+
logs: await Promise.all(firstInfo.logs.map(async (cidOrLink) => {
|
|
105
|
+
const cid = cidOrLink;
|
|
106
|
+
const applog = await getDecodedBlock(blockStore, cid);
|
|
107
|
+
if (!applog) {
|
|
108
|
+
ERROR(`Could not find info log CID in pub blocks:`, cid.toString(), { cid, blockStore });
|
|
109
|
+
throw new Error(`Could not find info log CID in pub blocks: ${cid.toString()}`);
|
|
110
|
+
}
|
|
111
|
+
if (applog.pv instanceof CID) applog.pv = applog.pv.toV1().toString();
|
|
112
|
+
return {
|
|
113
|
+
...applog,
|
|
114
|
+
cid: cid.toV1().toString()
|
|
115
|
+
};
|
|
116
|
+
}))
|
|
117
|
+
} : null,
|
|
118
|
+
applogsCID,
|
|
119
|
+
applogs: allApplogs
|
|
120
|
+
};
|
|
121
|
+
DEBUG("[decodePubFromBlocks] result:", result, { rootCID: rootCID.toString(), blockStore, applogs: allApplogs });
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
async function getBlocksOfCar(car) {
|
|
125
|
+
const rootsFromCar = await car.getRoots();
|
|
126
|
+
const roots = rootsFromCar.map((c) => (typeof c.toV1 === "function" ? c : CID.decode(c.bytes)).toV1().toString());
|
|
127
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
128
|
+
for await (const { cid: cidFromCarblocks, bytes } of car.blocks()) {
|
|
129
|
+
const cid = typeof cidFromCarblocks.toV1 === "function" ? cidFromCarblocks : CID.decode(cidFromCarblocks.bytes);
|
|
130
|
+
VERBOSE({ cidFromCarblocks, cid });
|
|
131
|
+
blocks.set(cid.toV1().toString(), bytes);
|
|
132
|
+
}
|
|
133
|
+
if (roots.length !== 1) {
|
|
134
|
+
WARN("Unexpected roots count:", roots);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
rootCID: CID.parse(roots[0]),
|
|
138
|
+
blockStore: {
|
|
139
|
+
get: (cid) => blocks.get(cid.toV1().toString())
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async function getDecodedBlock(blockStore, cid) {
|
|
144
|
+
try {
|
|
145
|
+
var blob = await blockStore.get(cid);
|
|
146
|
+
if (!blob) {
|
|
147
|
+
WARN("returning null");
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err.message === "Not Found") return null;
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
return dagJson.decode(blob);
|
|
155
|
+
}
|
|
156
|
+
async function makeCarOut(roots, blocks) {
|
|
157
|
+
const { writer, out } = CarWriter.create(Array.isArray(roots) ? roots : [roots]);
|
|
158
|
+
VERBOSE(`Writing ${blocks.length} blocks to CAR`, { roots, blocks });
|
|
159
|
+
blocks.forEach((b) => writer.put(b));
|
|
160
|
+
writer.close();
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
async function makeCarBlob(roots, blocks) {
|
|
164
|
+
const carOut = await makeCarOut(roots, blocks);
|
|
165
|
+
const chunks = [];
|
|
166
|
+
for await (const chunk of carOut) {
|
|
167
|
+
chunks.push(chunk);
|
|
168
|
+
}
|
|
169
|
+
const blob = new Blob(chunks);
|
|
170
|
+
return blob;
|
|
171
|
+
}
|
|
172
|
+
async function carFromBlob(blob) {
|
|
173
|
+
return CarReader.fromBytes(new Uint8Array(await blob.arrayBuffer()));
|
|
174
|
+
}
|
|
175
|
+
function extractCids(value) {
|
|
176
|
+
if (value instanceof CID) return [value];
|
|
177
|
+
if (Array.isArray(value)) return value.flatMap(extractCids);
|
|
178
|
+
if (value && typeof value === "object") return Object.values(value).flatMap(extractCids);
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
var MAX_COLLECT_BLOCKS = 1e6;
|
|
182
|
+
async function collectDagBlocks(startCID, blockStore) {
|
|
183
|
+
const visited = /* @__PURE__ */ new Set();
|
|
184
|
+
const blocks = [];
|
|
185
|
+
const queue = [startCID];
|
|
186
|
+
while (queue.length > 0) {
|
|
187
|
+
if (blocks.length >= MAX_COLLECT_BLOCKS) {
|
|
188
|
+
WARN(`[collectDagBlocks] hit ${MAX_COLLECT_BLOCKS} block limit, returning partial result`);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
const cid = queue.shift();
|
|
192
|
+
const cidStr = cid.toString();
|
|
193
|
+
if (visited.has(cidStr)) continue;
|
|
194
|
+
visited.add(cidStr);
|
|
195
|
+
let bytes;
|
|
196
|
+
try {
|
|
197
|
+
bytes = await blockStore.get(cid);
|
|
198
|
+
} catch {
|
|
199
|
+
WARN(`[collectDagBlocks] block not found: ${cidStr}, stopping this branch`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (!bytes) {
|
|
203
|
+
WARN(`[collectDagBlocks] block not found: ${cidStr}, stopping this branch`);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
blocks.push({ cid, bytes });
|
|
207
|
+
if (blocks.length % 1e3 === 0) {
|
|
208
|
+
LOG(`[collectDagBlocks] collected ${blocks.length} blocks...`);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const decoded = dagJson.decode(bytes);
|
|
212
|
+
const childCids = extractCids(decoded);
|
|
213
|
+
for (const child of childCids) {
|
|
214
|
+
if (!visited.has(child.toString())) {
|
|
215
|
+
queue.push(child);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
DEBUG(`[collectDagBlocks] collected ${blocks.length} blocks from ${startCID.toString()}`);
|
|
222
|
+
return blocks;
|
|
223
|
+
}
|
|
224
|
+
function streamReaderToIterable(bodyReader) {
|
|
225
|
+
return (async function* () {
|
|
226
|
+
while (true) {
|
|
227
|
+
const { done, value } = await bodyReader.read();
|
|
228
|
+
VERBOSE(`[car] chunk`, { done, value });
|
|
229
|
+
if (done) {
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
yield value;
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/pubsub/snap-push.ts
|
|
238
|
+
var { WARN: WARN2, LOG: LOG2, DEBUG: DEBUG2, VERBOSE: VERBOSE2, ERROR: ERROR2 } = Logger2.setup(Logger2.INFO);
|
|
239
|
+
async function prepareSnapshotForPush(agent, appThread, threadToPublish, share, prevSnapCID, prevCounter) {
|
|
240
|
+
if (prevCounter !== null && !prevSnapCID) {
|
|
241
|
+
throw ERROR2("[prepareSnapshotForPush] prevCounter provided without prevSnapCID");
|
|
242
|
+
}
|
|
243
|
+
let logsToPublish = getLogsFromThread(threadToPublish);
|
|
244
|
+
DEBUG2(`[preparePubForPush] Collected ${logsToPublish.length} logs :`, {
|
|
245
|
+
logsToPublish,
|
|
246
|
+
threadOrLogsCount: threadToPublish.nameAndSizeUntracked || `[${threadToPublish.length}]`
|
|
247
|
+
});
|
|
248
|
+
const { sharedAgents, sharedKeyMap, sharedKey, pubCounter } = share ?? {};
|
|
249
|
+
const getExistingOrNewLog = (thread, share2, ag, at, vl) => {
|
|
250
|
+
let logInQuestion = rollingFilter(lastWriteWins(thread), { en: share2.id, at }).latestLog;
|
|
251
|
+
if (!logInQuestion && vl !== void 0) {
|
|
252
|
+
logInQuestion = ensureTsPvAndFinalizeApplog({ ag, en: share2.id, at, vl }, thread);
|
|
253
|
+
}
|
|
254
|
+
return logInQuestion;
|
|
255
|
+
};
|
|
256
|
+
const shareNameLog = getExistingOrNewLog(appThread, share, agent.ag, "share/name", share.name);
|
|
257
|
+
const shareCounterLog = getExistingOrNewLog(appThread, share, agent.ag, "share/counter", `${agent.did}<::>${pubCounter}`);
|
|
258
|
+
const encryptApplog = async (applog, keyToUse) => {
|
|
259
|
+
const { log: eachLog, cid } = prepareForPub(applog);
|
|
260
|
+
const enc = new TextEncoder();
|
|
261
|
+
const stringified = stringify(eachLog);
|
|
262
|
+
const stringifiedEncodedAppLogPayload = enc.encode(stringified);
|
|
263
|
+
VERBOSE2("[odd]", { eachLog, stringified, stringifiedEncodedAppLogPayload });
|
|
264
|
+
try {
|
|
265
|
+
const encPayload = await agent.crypto?.aes.encrypt(stringifiedEncodedAppLogPayload, keyToUse, "AES-GCM");
|
|
266
|
+
VERBOSE2("[odd] encrypted length:", stringifiedEncodedAppLogPayload.length, { encPayload });
|
|
267
|
+
return encPayload;
|
|
268
|
+
} catch (err) {
|
|
269
|
+
throw ERROR2("FAILED TO ENC payload length:", stringifiedEncodedAppLogPayload.length, { err });
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
let maybeEncryptedApplogs;
|
|
273
|
+
const encryptedApplogs = [];
|
|
274
|
+
const agentSharedKeyLogs = [];
|
|
275
|
+
if (sharedAgents) {
|
|
276
|
+
if (!sharedKey || !sharedKeyMap) {
|
|
277
|
+
throw ERROR2("sharedAgents but no Keys/Map", { sharedAgents, sharedKeyMap, sharedKey });
|
|
278
|
+
}
|
|
279
|
+
VERBOSE2("encrypting", { sharedAgents, sharedKeyMap });
|
|
280
|
+
for (const [eachAgent, eachEncKey] of Array.from(sharedKeyMap.entries())) {
|
|
281
|
+
VERBOSE2("adding key", { eachAgent, eachEncKey });
|
|
282
|
+
agentSharedKeyLogs.push({
|
|
283
|
+
ag: agent.ag,
|
|
284
|
+
en: eachAgent,
|
|
285
|
+
at: "share/sharedKey",
|
|
286
|
+
vl: eachEncKey
|
|
287
|
+
// these are encrypted with the derived key from the local agent private and remote agent public keys
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const CIDlist = [];
|
|
291
|
+
const pubCIDmap = {};
|
|
292
|
+
for (const eachLog of logsToPublish) {
|
|
293
|
+
VERBOSE2("[crypto] encrypting ", { eachLog, sharedKey });
|
|
294
|
+
const encPayload = await encryptApplog(eachLog, sharedKey);
|
|
295
|
+
DEBUG2("[crypto] encrypted ", { eachLog, encPayload, sharedKey });
|
|
296
|
+
encryptedApplogs.push({ enc: encPayload });
|
|
297
|
+
}
|
|
298
|
+
maybeEncryptedApplogs = encryptedApplogs;
|
|
299
|
+
} else {
|
|
300
|
+
maybeEncryptedApplogs = logsToPublish;
|
|
301
|
+
}
|
|
302
|
+
DEBUG2("adding all agent info and shareAtoms", {
|
|
303
|
+
share,
|
|
304
|
+
agent,
|
|
305
|
+
logsToPublish,
|
|
306
|
+
// threadToPublish, - very verbose
|
|
307
|
+
agentSharedKeyLogs
|
|
308
|
+
});
|
|
309
|
+
const infoLogs = [
|
|
310
|
+
...rollingFilter(lastWriteWins(appThread), {
|
|
311
|
+
// TODO: use static filter for performance
|
|
312
|
+
en: agent.ag,
|
|
313
|
+
at: anyOf("agent/ecdh", "agent/jwkd", "agent/appAgent")
|
|
314
|
+
}).applogs,
|
|
315
|
+
...shareNameLog ? [shareNameLog] : [],
|
|
316
|
+
...shareCounterLog ? [shareCounterLog] : [],
|
|
317
|
+
...agentSharedKeyLogs
|
|
318
|
+
];
|
|
319
|
+
DEBUG2(`[prepareSnapshotForPush] info logs:`, infoLogs);
|
|
320
|
+
if (!infoLogs.find(({ at }) => at === "agent/appAgent")) throw ERROR2(`[prepareSnapshotForPush] appThread missing agent/appAgent log`);
|
|
321
|
+
const applogsToEncode = keepTruthy(maybeEncryptedApplogs);
|
|
322
|
+
const infologsToEncode = keepTruthy(infoLogs);
|
|
323
|
+
if (!applogsToEncode.length) {
|
|
324
|
+
throw ERROR2("no valid applogs", { agent, maybeEncryptedApplogs, infoLogs, applogsToEncode, infologsToEncode, prevSnapCID });
|
|
325
|
+
}
|
|
326
|
+
if (!infologsToEncode.length) {
|
|
327
|
+
throw ERROR2("no valid infologs", { agent, maybeEncryptedApplogs, infoLogs, applogsToEncode, infologsToEncode, prevSnapCID });
|
|
328
|
+
}
|
|
329
|
+
const encodedSnapshot = await encodeSnapshotAsCar(agent, applogsToEncode, infologsToEncode, prevSnapCID, prevCounter);
|
|
330
|
+
DEBUG2("inPrepareSnapshotForPush", { encodedSnapshot });
|
|
331
|
+
return encodedSnapshot;
|
|
332
|
+
}
|
|
333
|
+
async function encodeSnapshotAsCar(agent, applogs, infoLogs, prevSnapCID, prevCounter) {
|
|
334
|
+
DEBUG2(`[encodeSnapshotAsCar] encoding`, { agent, applogs, infoLogs });
|
|
335
|
+
const { cids: infoLogCids, encodedApplogs: encodedInfoLogs } = await encodeApplogsAsIPLD(infoLogs);
|
|
336
|
+
const { cids: applogCids, encodedApplogs } = await encodeApplogsAsIPLD(applogs);
|
|
337
|
+
let blocks = encodedApplogs.concat(encodedInfoLogs);
|
|
338
|
+
const infoLogsWrap = await encodeBlockOriginal({ logs: infoLogCids });
|
|
339
|
+
blocks.push(infoLogsWrap);
|
|
340
|
+
const { rootCID: chunkRootCID, blocks: chunkBlocks } = await chunkApplogs(applogCids);
|
|
341
|
+
blocks = blocks.concat(chunkBlocks);
|
|
342
|
+
const infoSignature = await agent.sign(infoLogsWrap.cid.bytes);
|
|
343
|
+
const applogsSignature = await agent.sign(chunkRootCID.bytes);
|
|
344
|
+
const root = {
|
|
345
|
+
info: infoLogsWrap.cid,
|
|
346
|
+
applogs: chunkRootCID,
|
|
347
|
+
infoSignature,
|
|
348
|
+
applogsSignature,
|
|
349
|
+
prev: prevSnapCID,
|
|
350
|
+
prevCounter: !prevSnapCID ? 0 : prevCounter !== null ? prevCounter + 1 : null
|
|
351
|
+
};
|
|
352
|
+
DEBUG2("[encodeSnapshotAsCar] encoding root", { root, logCids: applogCids, infoLogCids });
|
|
353
|
+
const encodedRoot = await encodeBlockOriginal(root);
|
|
354
|
+
blocks.push(encodedRoot);
|
|
355
|
+
DEBUG2("[encodeSnapshotAsCar] => root", { encodedRoot });
|
|
356
|
+
return {
|
|
357
|
+
cid: encodedRoot.cid,
|
|
358
|
+
blob: await makeCarBlob(encodedRoot.cid, blocks),
|
|
359
|
+
// TODO: create CarBuilder (incl .encodeAndAdd({...}))
|
|
360
|
+
blocks,
|
|
361
|
+
infoLogCids,
|
|
362
|
+
applogCids
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
async function chunkApplogs(applogCids, size = 1e4) {
|
|
366
|
+
if (!applogCids.length) throw ERROR2(`[chunkApplogs] called with empty array`);
|
|
367
|
+
const chunks = [];
|
|
368
|
+
for (let i = 0; i < applogCids.length; i += size) {
|
|
369
|
+
const chunk = await encodeBlockOriginal({ logs: applogCids.slice(i, Math.min(i + applogCids.length, i + size)) });
|
|
370
|
+
chunks.push(chunk);
|
|
371
|
+
}
|
|
372
|
+
if (chunks.length === 1) return { rootCID: chunks[0].cid, blocks: chunks };
|
|
373
|
+
const root = await encodeBlockOriginal({ chunks: chunks.map((chunk) => chunk.cid) });
|
|
374
|
+
const blocks = [root, ...chunks];
|
|
375
|
+
DEBUG2(`[chunkApplogs] ${applogCids.length} logs chunked into ${chunks.length}`, { applogCids, root, blocks, chunks, dagJson: dagJson2 });
|
|
376
|
+
return { rootCID: root.cid, blocks, chunks };
|
|
377
|
+
}
|
|
378
|
+
async function unchunkApplogsBlock(block, blockStore) {
|
|
379
|
+
if (isSnapBlockChunks(block)) {
|
|
380
|
+
return (await Promise.all(
|
|
381
|
+
block.chunks.map(async (chunkCid) => {
|
|
382
|
+
const block2 = await getDecodedBlock(blockStore, chunkCid);
|
|
383
|
+
if (!block2.logs) throw ERROR2(`Weird chunk`, block2);
|
|
384
|
+
return block2.logs;
|
|
385
|
+
})
|
|
386
|
+
)).flat();
|
|
387
|
+
} else {
|
|
388
|
+
return block.logs;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function isSnapBlockChunks(block) {
|
|
392
|
+
return block.chunks;
|
|
393
|
+
}
|
|
394
|
+
async function encodeSnapshotApplogsAsCar(applogs) {
|
|
395
|
+
const encoded = await encodeApplogsAsIPLD(applogs);
|
|
396
|
+
if (!encoded) throw ERROR2("invalid applogs cannot continue", { applogs, encoded });
|
|
397
|
+
const { cids, encodedApplogs } = encoded;
|
|
398
|
+
const root = { applogs: cids };
|
|
399
|
+
const encodedRoot = await encodeBlockOriginal(root);
|
|
400
|
+
DEBUG2("[encodeSnapshotApplogsAsCar] encoded root", { cids, encodedRoot });
|
|
401
|
+
return await makeCarBlob(encodedRoot.cid, [encodedRoot, ...encodedApplogs]);
|
|
402
|
+
}
|
|
403
|
+
async function encodeApplogsAsIPLD(applogs) {
|
|
404
|
+
DEBUG2({ applogs });
|
|
405
|
+
const validApplogs = applogs.filter((eachLog) => !!eachLog);
|
|
406
|
+
DEBUG2({ validApplogs });
|
|
407
|
+
if (!validApplogs.length) throw ERROR2("no valid applogs");
|
|
408
|
+
const preppedLogs = validApplogs.map((log) => prepareForPub(log).log);
|
|
409
|
+
const encodedApplogs = await Promise.all(preppedLogs.map(encodeBlockOriginal));
|
|
410
|
+
DEBUG2("[encodeApplogsAsIpld] encoded applogs", { preppedLogs, encodedApplogs });
|
|
411
|
+
const cids = encodedApplogs.map((b) => {
|
|
412
|
+
if (!b.cid) throw ERROR2(`[publish] no cid for encoded log:`, b);
|
|
413
|
+
return b.cid;
|
|
414
|
+
});
|
|
415
|
+
return { cids, encodedApplogs };
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export {
|
|
419
|
+
prepareSnapshotForPush,
|
|
420
|
+
encodeSnapshotAsCar,
|
|
421
|
+
chunkApplogs,
|
|
422
|
+
unchunkApplogsBlock,
|
|
423
|
+
isSnapBlockChunks,
|
|
424
|
+
encodeSnapshotApplogsAsCar,
|
|
425
|
+
decodePubFromCar,
|
|
426
|
+
decodePubFromBlocks,
|
|
427
|
+
getBlocksOfCar,
|
|
428
|
+
getDecodedBlock,
|
|
429
|
+
makeCarOut,
|
|
430
|
+
makeCarBlob,
|
|
431
|
+
carFromBlob,
|
|
432
|
+
collectDagBlocks,
|
|
433
|
+
streamReaderToIterable
|
|
434
|
+
};
|
|
435
|
+
//# sourceMappingURL=chunk-6ALNRM3J.js.map
|