@wovin/core 0.1.35 → 0.2.0
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 +25 -6
- package/dist/applog/applog-utils.d.ts.map +1 -1
- package/dist/applog/datom-types.d.ts +4 -5
- 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} +6 -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-KXMTKPF4.min.js → chunk-3JZMOEOD.js} +8 -8
- package/dist/chunk-3JZMOEOD.js.map +1 -0
- package/dist/chunk-3WZVG277.js +434 -0
- package/dist/chunk-3WZVG277.js.map +1 -0
- package/dist/chunk-7Z5YDQKK.js +1 -0
- package/dist/chunk-CPSDKFBG.js +147 -0
- package/dist/chunk-CPSDKFBG.js.map +1 -0
- package/dist/chunk-E46VTKTZ.js +1 -0
- package/dist/{chunk-H3VQJP56.min.js → chunk-J2FDHGOZ.js} +9 -9
- package/dist/chunk-J2FDHGOZ.js.map +1 -0
- package/dist/chunk-L5EEEGE6.js +1862 -0
- package/dist/chunk-L5EEEGE6.js.map +1 -0
- package/dist/{chunk-BRC7LSM6.min.js → chunk-PD3C7XUM.js} +5 -5
- package/dist/chunk-PD3C7XUM.js.map +1 -0
- package/dist/chunk-QZXKQCAY.js +1026 -0
- package/dist/chunk-QZXKQCAY.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} +73 -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 +85 -21
- 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 +1 -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} +51 -32
- 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 +56 -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 +507 -0
- package/src/applog/datom-types.ts +148 -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 +277 -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 +131 -0
- package/src/query/liveFilterAndMap.test.ts +102 -0
- package/src/query/matchers.ts +8 -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 +250 -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,248 @@
|
|
|
1
|
+
import { CarReader } from '@ipld/car'
|
|
2
|
+
import * as dagJson from '@ipld/dag-json'
|
|
3
|
+
import { Logger } from 'besonders-logger'
|
|
4
|
+
import { CID } from 'multiformats/cid'
|
|
5
|
+
import type { Applog } from '../applog/datom-types.ts'
|
|
6
|
+
import { removeDuplicateAppLogs } from '../applog/applog-utils.ts'
|
|
7
|
+
import type { SnapRootBlock, SnapBlockLogsOrChunks } from '../pubsub/pubsub-types.ts'
|
|
8
|
+
import { unchunkApplogsBlock } from '../pubsub/snap-push.ts'
|
|
9
|
+
import { areCidsEqual } from '../ipfs/ipfs-utils.ts'
|
|
10
|
+
import type { WriteableThread } from '../thread/writeable.ts'
|
|
11
|
+
import type { BlockStore } from '../blockstore/index.ts'
|
|
12
|
+
|
|
13
|
+
const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line no-unused-vars
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Block retrieval abstraction - fetch or get blocks.
|
|
17
|
+
* Implemented by gateway retriever or local blockstore.
|
|
18
|
+
*/
|
|
19
|
+
export interface BlockRetriever {
|
|
20
|
+
/** Get single block by CID */
|
|
21
|
+
get(cid: CID): Promise<Uint8Array>
|
|
22
|
+
/** Get all blocks in DAG rooted at CID (for applogs/info sub-DAGs) */
|
|
23
|
+
getDag(cid: CID): AsyncIterable<{ cid: CID; bytes: Uint8Array }>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Wrap a BlockRetriever so fetched blocks flow through a BlockStore.
|
|
28
|
+
* - get: delegates to store.get() (which handles local-first / remote fallback)
|
|
29
|
+
* - getDag: streams from source, puts each block into store
|
|
30
|
+
*/
|
|
31
|
+
export function withBlockCache(
|
|
32
|
+
source: BlockRetriever,
|
|
33
|
+
store: BlockStore,
|
|
34
|
+
): BlockRetriever {
|
|
35
|
+
return {
|
|
36
|
+
get: (cid) => store.get(cid),
|
|
37
|
+
async *getDag(cid) {
|
|
38
|
+
for await (const block of source.getDag(cid)) {
|
|
39
|
+
await store.put(block.cid, block.bytes)
|
|
40
|
+
yield block
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Options for updateThreadFromSnapshot
|
|
48
|
+
*/
|
|
49
|
+
export interface UpdateOptions {
|
|
50
|
+
/** CID of last included snapshot - exclude this and older snapshots */
|
|
51
|
+
excludeSnapshotCID?: CID
|
|
52
|
+
/** Stop when we reach this counter (walking backwards) */
|
|
53
|
+
stopAtCounter?: number
|
|
54
|
+
/** Maximum number of snapshots to traverse (default: 100) */
|
|
55
|
+
maxDepth?: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Result from updateThreadFromSnapshot
|
|
60
|
+
*/
|
|
61
|
+
export interface UpdateResult {
|
|
62
|
+
/** Root CID that was fetched */
|
|
63
|
+
cid: CID
|
|
64
|
+
/** All applogs decoded from the chain */
|
|
65
|
+
applogs: Applog[]
|
|
66
|
+
/** Count of applogs actually inserted (not duplicates) */
|
|
67
|
+
insertedCount: number
|
|
68
|
+
/** Number of snapshots traversed */
|
|
69
|
+
snapshotCount: number
|
|
70
|
+
/** Counter range encountered (min/max) */
|
|
71
|
+
counterRange?: { minCounter: number; maxCounter: number }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Simple in-memory block store used during snapshot chain fetch.
|
|
76
|
+
*/
|
|
77
|
+
interface MemoryBlockStore {
|
|
78
|
+
get(cid: CID): Uint8Array | undefined
|
|
79
|
+
put(cid: CID, bytes: Uint8Array): void
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function createMemoryBlockStore(): MemoryBlockStore {
|
|
83
|
+
const blocks = new Map<string, Uint8Array>()
|
|
84
|
+
return {
|
|
85
|
+
get(cid: CID) {
|
|
86
|
+
return blocks.get(cid.toV1().toString())
|
|
87
|
+
},
|
|
88
|
+
put(cid: CID, bytes: Uint8Array) {
|
|
89
|
+
blocks.set(cid.toV1().toString(), bytes)
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function getDecodedBlock<T>(blockStore: MemoryBlockStore, cid: CID): Promise<T | null> {
|
|
95
|
+
const bytes = blockStore.get(cid)
|
|
96
|
+
if (!bytes) return null
|
|
97
|
+
return dagJson.decode(bytes) as T
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Fetch snapshot chain from CID using a BlockRetriever, decode applogs, insert into thread.
|
|
102
|
+
* Stops before excludeSnapshotCID if provided (incremental update).
|
|
103
|
+
*
|
|
104
|
+
* @param thread - WriteableThread to insert applogs into
|
|
105
|
+
* @param cid - Root CID of the snapshot to start from
|
|
106
|
+
* @param retriever - BlockRetriever for fetching blocks
|
|
107
|
+
* @param options - Optional configuration
|
|
108
|
+
* @returns UpdateResult with applogs and counts
|
|
109
|
+
*/
|
|
110
|
+
export async function updateThreadFromSnapshot(
|
|
111
|
+
thread: WriteableThread,
|
|
112
|
+
cid: CID,
|
|
113
|
+
retriever: BlockRetriever,
|
|
114
|
+
options?: UpdateOptions
|
|
115
|
+
): Promise<UpdateResult> {
|
|
116
|
+
const { excludeSnapshotCID, stopAtCounter, maxDepth = 100 } = options ?? {}
|
|
117
|
+
|
|
118
|
+
DEBUG('[updateThreadFromSnapshot] starting from', cid.toString(), {
|
|
119
|
+
excludeSnapshotCID: excludeSnapshotCID?.toString(),
|
|
120
|
+
stopAtCounter,
|
|
121
|
+
maxDepth,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const blockStore = createMemoryBlockStore()
|
|
125
|
+
const visited = new Set<string>()
|
|
126
|
+
let currentCID: CID | undefined = cid
|
|
127
|
+
let snapshotCount = 0
|
|
128
|
+
const allApplogs: Applog[] = []
|
|
129
|
+
let minCounter = Infinity
|
|
130
|
+
let maxCounter = -Infinity
|
|
131
|
+
let lastCounter: number | undefined
|
|
132
|
+
|
|
133
|
+
while (currentCID && snapshotCount < maxDepth) {
|
|
134
|
+
const cidStr = currentCID.toString()
|
|
135
|
+
|
|
136
|
+
// Loop detection
|
|
137
|
+
if (visited.has(cidStr)) {
|
|
138
|
+
throw ERROR('[updateThreadFromSnapshot] snapshot chain has a loop', {
|
|
139
|
+
currentCID: cidStr,
|
|
140
|
+
visited: [...visited],
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
visited.add(cidStr)
|
|
144
|
+
|
|
145
|
+
// Check stop condition BEFORE fetching content
|
|
146
|
+
if (excludeSnapshotCID && areCidsEqual(currentCID, excludeSnapshotCID)) {
|
|
147
|
+
DEBUG('[updateThreadFromSnapshot] reached excludeSnapshotCID, stopping', excludeSnapshotCID.toString())
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 1. Fetch root block
|
|
152
|
+
DEBUG('[updateThreadFromSnapshot] fetching root block', cidStr)
|
|
153
|
+
const rootBytes = await retriever.get(currentCID)
|
|
154
|
+
blockStore.put(currentCID, rootBytes)
|
|
155
|
+
|
|
156
|
+
// Parse root to get applogs, info, prev CIDs
|
|
157
|
+
const root = dagJson.decode(rootBytes) as SnapRootBlock
|
|
158
|
+
|
|
159
|
+
// Track counter range and validate sequentiality
|
|
160
|
+
if (typeof root.prevCounter === 'number') {
|
|
161
|
+
minCounter = Math.min(minCounter, root.prevCounter)
|
|
162
|
+
maxCounter = Math.max(maxCounter, root.prevCounter)
|
|
163
|
+
|
|
164
|
+
// Validate sequentiality (walking backwards, counter should decrease)
|
|
165
|
+
if (lastCounter !== undefined && root.prevCounter !== lastCounter - 1) {
|
|
166
|
+
WARN('[updateThreadFromSnapshot] counter gap detected', {
|
|
167
|
+
expected: lastCounter - 1,
|
|
168
|
+
got: root.prevCounter,
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
lastCounter = root.prevCounter
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Stop condition based on counter
|
|
175
|
+
if (stopAtCounter !== undefined && typeof root.prevCounter === 'number' && root.prevCounter <= stopAtCounter) {
|
|
176
|
+
DEBUG('[updateThreadFromSnapshot] reached stopAtCounter', { stopAtCounter, prevCounter: root.prevCounter })
|
|
177
|
+
break
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 2. Fetch applogs DAG
|
|
181
|
+
DEBUG('[updateThreadFromSnapshot] fetching applogs', root.applogs.toString())
|
|
182
|
+
for await (const { cid: blockCid, bytes } of retriever.getDag(root.applogs)) {
|
|
183
|
+
blockStore.put(blockCid, bytes)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 3. Fetch info DAG
|
|
187
|
+
DEBUG('[updateThreadFromSnapshot] fetching info', root.info.toString())
|
|
188
|
+
for await (const { cid: blockCid, bytes } of retriever.getDag(root.info)) {
|
|
189
|
+
blockStore.put(blockCid, bytes)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Decode applogs from this snapshot
|
|
193
|
+
const applogsBlock = await getDecodedBlock<SnapBlockLogsOrChunks>(blockStore, root.applogs)
|
|
194
|
+
if (!applogsBlock) {
|
|
195
|
+
throw ERROR('[updateThreadFromSnapshot] applogs block not found', { cid: root.applogs.toString() })
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Use the unchunk helper which handles both chunked and non-chunked formats
|
|
199
|
+
const applogCIDs = await unchunkApplogsBlock(applogsBlock, {
|
|
200
|
+
get: async (cid: CID) => blockStore.get(cid)!,
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// Resolve each applog CID to actual applog data
|
|
204
|
+
for (const applogCID of applogCIDs) {
|
|
205
|
+
const applog = await getDecodedBlock<Applog>(blockStore, applogCID)
|
|
206
|
+
if (!applog) {
|
|
207
|
+
WARN('[updateThreadFromSnapshot] applog not found:', applogCID.toString())
|
|
208
|
+
continue
|
|
209
|
+
}
|
|
210
|
+
// Normalize pv field if it's a CID instance
|
|
211
|
+
if ((applog.pv as any) instanceof CID) {
|
|
212
|
+
applog.pv = (applog.pv as any as CID).toV1().toString()
|
|
213
|
+
}
|
|
214
|
+
allApplogs.push({
|
|
215
|
+
...applog,
|
|
216
|
+
cid: applogCID.toV1().toString(),
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
snapshotCount++
|
|
221
|
+
currentCID = root.prev // Move to previous snapshot
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
DEBUG('[updateThreadFromSnapshot] fetched', {
|
|
225
|
+
snapshotCount,
|
|
226
|
+
applogCount: allApplogs.length,
|
|
227
|
+
rootCID: cid.toString(),
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Deduplicate applogs (in case of overlapping snapshots)
|
|
231
|
+
const deduplicated = removeDuplicateAppLogs(allApplogs, 'cleanup')
|
|
232
|
+
|
|
233
|
+
// Insert into thread
|
|
234
|
+
const inserted = thread.insertMissing(deduplicated, false)
|
|
235
|
+
|
|
236
|
+
DEBUG('[updateThreadFromSnapshot] inserted', {
|
|
237
|
+
insertedCount: inserted.length,
|
|
238
|
+
duplicateCount: deduplicated.length - inserted.length,
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
cid,
|
|
243
|
+
applogs: deduplicated,
|
|
244
|
+
insertedCount: inserted.length,
|
|
245
|
+
snapshotCount,
|
|
246
|
+
counterRange: minCounter !== Infinity ? { minCounter, maxCounter } : undefined,
|
|
247
|
+
}
|
|
248
|
+
}
|
package/src/retrieve.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './retrieve/update-thread.ts'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1M applog stress test — static query + live query incremental update.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest'
|
|
5
|
+
import type { Applog, ApplogForInsert } from '../../applog/datom-types.ts'
|
|
6
|
+
import { ThreadInMemory } from '../../thread/writeable.ts'
|
|
7
|
+
import { liveQuery, query } from '../../query/basic.ts'
|
|
8
|
+
|
|
9
|
+
const AGENT = 'stress-agent'
|
|
10
|
+
|
|
11
|
+
function generateLarge(entityCount: number): Applog[] {
|
|
12
|
+
const inputs: ApplogForInsert[] = []
|
|
13
|
+
const types = ['block', 'page', 'image', 'link', 'heading']
|
|
14
|
+
let ts = Date.now() - entityCount * 6
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < entityCount; i++) {
|
|
17
|
+
const en = `e${i}`
|
|
18
|
+
const nextTs = () => new Date(ts++).toISOString()
|
|
19
|
+
inputs.push({ en, at: 'entity/name', vl: `Entity ${i}`, ag: AGENT, ts: nextTs() } as any)
|
|
20
|
+
inputs.push({ en, at: 'entity/type', vl: types[i % types.length], ag: AGENT, ts: nextTs() } as any)
|
|
21
|
+
inputs.push({ en, at: 'entity/status', vl: i % 3 === 0 ? 'active' : 'draft', ag: AGENT, ts: nextTs() } as any)
|
|
22
|
+
if (i % 2 === 0) {
|
|
23
|
+
inputs.push({ en, at: 'entity/content', vl: `Content ${i}`, ag: AGENT, ts: nextTs() } as any)
|
|
24
|
+
}
|
|
25
|
+
if (i % 4 === 0) {
|
|
26
|
+
inputs.push({ en, at: 'relation/parent', vl: `e${Math.floor(i / 4)}`, ag: AGENT, ts: nextTs() } as any)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Already sorted by construction (ts is monotonically increasing)
|
|
31
|
+
return inputs.map(i => ({ pv: null, ...i })) as Applog[]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('1M applog stress test', () => {
|
|
35
|
+
let db: ReturnType<typeof ThreadInMemory.fromArray>
|
|
36
|
+
|
|
37
|
+
it('generate ~1M applogs + load', () => {
|
|
38
|
+
const start = performance.now()
|
|
39
|
+
const dataset = generateLarge(200_000)
|
|
40
|
+
const genTime = performance.now() - start
|
|
41
|
+
|
|
42
|
+
const start2 = performance.now()
|
|
43
|
+
db = ThreadInMemory.fromArray(dataset, 'stress-1m')
|
|
44
|
+
const loadTime = performance.now() - start2
|
|
45
|
+
|
|
46
|
+
console.log(`\n [1M] Generated ${dataset.length.toLocaleString()} applogs in ${genTime.toFixed(0)}ms`)
|
|
47
|
+
console.log(` [1M] ThreadInMemory.fromArray: ${loadTime.toFixed(0)}ms`)
|
|
48
|
+
console.log(` [1M] Total thread size: ${db.size.toLocaleString()}`)
|
|
49
|
+
expect(db.size).toBeGreaterThan(700_000)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('static query() — single step on ~750K applogs', () => {
|
|
53
|
+
const start = performance.now()
|
|
54
|
+
const result = query(db, [{ at: 'entity/type', vl: 'block' }])
|
|
55
|
+
const elapsed = performance.now() - start
|
|
56
|
+
|
|
57
|
+
console.log(` [1M] query() single-step: ${elapsed.toFixed(1)}ms → ${result.nodes.length.toLocaleString()} results`)
|
|
58
|
+
expect(result.nodes.length).toBe(40_000) // 200K / 5 types
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('liveQuery() — setup + single insert propagation on ~750K applogs', () => {
|
|
62
|
+
// Setup
|
|
63
|
+
const setupStart = performance.now()
|
|
64
|
+
const live = liveQuery(db, [{ at: 'entity/type', vl: 'block' }])
|
|
65
|
+
const setupTime = performance.now() - setupStart
|
|
66
|
+
const initialCount = live.nodes.length
|
|
67
|
+
|
|
68
|
+
console.log(` [1M] liveQuery() setup: ${setupTime.toFixed(1)}ms → ${initialCount.toLocaleString()} initial results`)
|
|
69
|
+
|
|
70
|
+
// Insert one matching applog and measure propagation
|
|
71
|
+
let eventTime: number | null = null
|
|
72
|
+
live.subscribe(() => {
|
|
73
|
+
eventTime = performance.now()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const insertStart = performance.now()
|
|
77
|
+
db.insert([{
|
|
78
|
+
en: 'new-entity-1m',
|
|
79
|
+
at: 'entity/type',
|
|
80
|
+
vl: 'block',
|
|
81
|
+
ag: AGENT,
|
|
82
|
+
}])
|
|
83
|
+
const totalTime = performance.now() - insertStart
|
|
84
|
+
const subscribeDelta = eventTime ? eventTime - insertStart : null
|
|
85
|
+
|
|
86
|
+
const newCount = live.nodes.length
|
|
87
|
+
console.log(` [1M] Insert→result updated: ${totalTime.toFixed(3)}ms`)
|
|
88
|
+
console.log(` [1M] Insert→subscribe fired: ${subscribeDelta?.toFixed(3) ?? 'N/A'}ms`)
|
|
89
|
+
console.log(` [1M] Nodes: ${initialCount.toLocaleString()} → ${newCount.toLocaleString()}`)
|
|
90
|
+
|
|
91
|
+
expect(newCount).toBe(initialCount + 1)
|
|
92
|
+
live.dispose()
|
|
93
|
+
})
|
|
94
|
+
}, { timeout: 120_000 })
|