@fireproof/core 0.5.12 → 0.5.14
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/dist/hooks/use-fireproof.js +2 -1
- package/dist/src/blockstore.js +4 -4
- package/dist/src/clock.js +109 -37
- package/dist/src/crypto.js +1 -1
- package/dist/src/database.js +20 -19
- package/dist/src/db-index.js +6 -6
- package/dist/src/fireproof.d.ts +3 -2
- package/dist/src/fireproof.js +238 -85
- package/dist/src/fireproof.js.map +1 -1
- package/dist/src/fireproof.mjs +238 -85
- package/dist/src/fireproof.mjs.map +1 -1
- package/dist/src/listener.js +2 -3
- package/dist/src/prolly.js +74 -25
- package/dist/src/sha1.js +2 -1
- package/dist/src/sync.js +28 -9
- package/dist/src/valet.js +5 -1
- package/package.json +2 -2
- package/src/blockstore.js +1 -0
- package/src/clock.js +123 -37
- package/src/crypto.js +1 -1
- package/src/database.js +15 -12
- package/src/db-index.js +1 -1
- package/src/prolly.js +82 -24
- package/src/sync.js +12 -4
- package/src/valet.js +5 -5
package/src/clock.js
CHANGED
@@ -21,17 +21,17 @@ import { CIDCounter } from 'prolly-trees/utils'
|
|
21
21
|
|
22
22
|
/**
|
23
23
|
* @typedef {{
|
24
|
-
* type: 'put'|'del'
|
25
|
-
* key: string
|
26
|
-
* value: import('./link').AnyLink
|
27
|
-
* root: import('./link').AnyLink
|
28
|
-
* }} EventData
|
29
|
-
* @typedef {{
|
30
|
-
* root: import('./link').AnyLink
|
31
|
-
* head: import('./clock').EventLink<EventData>[]
|
32
|
-
* event: import('./clock').EventBlockView<EventData>
|
33
|
-
* }} Result
|
34
|
-
*/
|
24
|
+
* type: 'put'|'del'
|
25
|
+
* key: string
|
26
|
+
* value: import('./link').AnyLink
|
27
|
+
* root: import('./link').AnyLink
|
28
|
+
* }} EventData
|
29
|
+
* @typedef {{
|
30
|
+
* root: import('./link').AnyLink
|
31
|
+
* head: import('./clock').EventLink<EventData>[]
|
32
|
+
* event: import('./clock').EventBlockView<EventData>
|
33
|
+
* }} Result
|
34
|
+
*/
|
35
35
|
|
36
36
|
/**
|
37
37
|
* Advance the clock by adding an event.
|
@@ -45,7 +45,7 @@ import { CIDCounter } from 'prolly-trees/utils'
|
|
45
45
|
export async function advance (blocks, head, event) {
|
46
46
|
/** @type {EventFetcher<T>} */
|
47
47
|
const events = new EventFetcher(blocks)
|
48
|
-
const headmap = new Map(head.map(
|
48
|
+
const headmap = new Map(head.map(cid => [cid.toString(), cid]))
|
49
49
|
|
50
50
|
// Check if the headmap already includes the event, return head if it does
|
51
51
|
if (headmap.has(event.toString())) return { head, cids: await events.all() }
|
@@ -167,14 +167,16 @@ export async function decodeEventBlock (bytes) {
|
|
167
167
|
async function contains (events, a, b) {
|
168
168
|
if (a.toString() === b.toString()) return true
|
169
169
|
const [{ value: aevent }, { value: bevent }] = await Promise.all([events.get(a), events.get(b)])
|
170
|
-
const links = [...aevent.parents]
|
170
|
+
// const links = [...aevent.parents]
|
171
|
+
// console.log('aevent', aevent.parents)
|
172
|
+
const links = [...(aevent.parents || [])]
|
171
173
|
while (links.length) {
|
172
174
|
const link = links.shift()
|
173
175
|
if (!link) break
|
174
176
|
if (link.toString() === b.toString()) return true
|
175
177
|
// if any of b's parents are this link, then b cannot exist in any of the
|
176
178
|
// tree below, since that would create a cycle.
|
177
|
-
if (bevent.parents.some(
|
179
|
+
if (bevent.parents.some(p => link.toString() === p.toString())) continue
|
178
180
|
const { value: event } = await events.get(link)
|
179
181
|
links.push(...event.parents)
|
180
182
|
}
|
@@ -190,15 +192,19 @@ async function contains (events, a, b) {
|
|
190
192
|
*/
|
191
193
|
export async function * vis (blocks, head, options = {}) {
|
192
194
|
// @ts-ignore
|
193
|
-
const renderNodeLabel =
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
195
|
+
const renderNodeLabel =
|
196
|
+
options.renderNodeLabel ??
|
197
|
+
(b => {
|
198
|
+
// @ts-ignore
|
199
|
+
const { key, root, type } = b.value.data
|
200
|
+
return (
|
201
|
+
b.cid.toString() + '\n' + JSON.stringify({ key, root: root.cid.toString(), type }, null, 2).replace(/"/g, "'")
|
202
|
+
)
|
203
|
+
})
|
198
204
|
const events = new EventFetcher(blocks)
|
199
205
|
yield 'digraph clock {'
|
200
206
|
yield ' node [shape=point fontname="Courier"]; head;'
|
201
|
-
const hevents = await Promise.all(head.map(
|
207
|
+
const hevents = await Promise.all(head.map(link => events.get(link)))
|
202
208
|
const links = []
|
203
209
|
const nodes = new Set()
|
204
210
|
for (const e of hevents) {
|
@@ -231,17 +237,18 @@ export async function findEventsToSync (blocks, head) {
|
|
231
237
|
// console.time(callTag + '.findCommonAncestorWithSortedEvents')
|
232
238
|
const { ancestor, sorted } = await findCommonAncestorWithSortedEvents(events, head)
|
233
239
|
// console.timeEnd(callTag + '.findCommonAncestorWithSortedEvents')
|
234
|
-
// console.log('sorted', sorted.length)
|
240
|
+
// console.log('sorted', !!ancestor, sorted.length)
|
235
241
|
// console.time(callTag + '.contains')
|
236
|
-
|
242
|
+
|
243
|
+
const toSync = ancestor ? await asyncFilter(sorted, async uks => !(await contains(events, ancestor, uks.cid))) : sorted
|
237
244
|
// console.timeEnd(callTag + '.contains')
|
238
|
-
|
245
|
+
console.log('toSync', !!ancestor, sorted.length - toSync.length)
|
239
246
|
|
240
247
|
return { cids: events, events: toSync }
|
241
248
|
}
|
242
249
|
|
243
250
|
const asyncFilter = async (arr, predicate) =>
|
244
|
-
Promise.all(arr.map(predicate)).then(
|
251
|
+
Promise.all(arr.map(predicate)).then(results => arr.filter((_v, index) => results[index]))
|
245
252
|
|
246
253
|
export async function findCommonAncestorWithSortedEvents (events, children, doFull = false) {
|
247
254
|
// console.trace('findCommonAncestorWithSortedEvents')
|
@@ -252,12 +259,16 @@ export async function findCommonAncestorWithSortedEvents (events, children, doFu
|
|
252
259
|
// console.timeEnd(callTag + '.findCommonAncestor')
|
253
260
|
// console.log('ancestor', ancestor.toString())
|
254
261
|
if (!ancestor) {
|
255
|
-
|
262
|
+
console.log('no common ancestor', children)
|
263
|
+
// throw new Error('no common ancestor')
|
264
|
+
const sorted = await findSortedEvents(events, children, children, doFull)
|
265
|
+
return { ancestor: null, sorted }
|
256
266
|
}
|
257
267
|
// console.time(callTag + '.findSortedEvents')
|
258
|
-
const sorted = await findSortedEvents(events, children, ancestor, doFull)
|
268
|
+
const sorted = await findSortedEvents(events, children, [ancestor], doFull)
|
259
269
|
// console.timeEnd(callTag + '.findSortedEvents')
|
260
270
|
// console.log('sorted', sorted.length)
|
271
|
+
// console.log('ancestor', JSON.stringify(ancestor, null, 2))
|
261
272
|
return { ancestor, sorted }
|
262
273
|
}
|
263
274
|
|
@@ -268,17 +279,58 @@ export async function findCommonAncestorWithSortedEvents (events, children, doFu
|
|
268
279
|
* @param {import('./clock').EventFetcher} events
|
269
280
|
* @param {import('./clock').EventLink<EventData>[]} children
|
270
281
|
*/
|
282
|
+
// async function NEWfindCommonAncestor (events, children) {
|
283
|
+
// if (!children.length) return
|
284
|
+
// if (children.length === 1) return children[0]
|
285
|
+
|
286
|
+
// const candidates = children.map(c => [c])
|
287
|
+
// const visited = new Set()
|
288
|
+
|
289
|
+
// while (true) {
|
290
|
+
// let changed = false
|
291
|
+
// for (const c of candidates) {
|
292
|
+
// const candidate = await findAncestorCandidate(events, c[c.length - 1])
|
293
|
+
|
294
|
+
// if (!candidate) continue
|
295
|
+
|
296
|
+
// if (visited.has(candidate)) {
|
297
|
+
// return candidate // Common ancestor found
|
298
|
+
// }
|
299
|
+
|
300
|
+
// visited.add(candidate)
|
301
|
+
// changed = true
|
302
|
+
// c.push(candidate)
|
303
|
+
// }
|
304
|
+
|
305
|
+
// if (!changed) {
|
306
|
+
// // No common ancestor found, exhausted candidates
|
307
|
+
// return null
|
308
|
+
// }
|
309
|
+
// }
|
310
|
+
// }
|
311
|
+
|
271
312
|
async function findCommonAncestor (events, children) {
|
272
313
|
if (!children.length) return
|
314
|
+
children = [...new Set(children)]
|
273
315
|
if (children.length === 1) return children[0]
|
274
316
|
const candidates = children.map((c) => [c])
|
317
|
+
// console.log(
|
318
|
+
// 'og candidates',
|
319
|
+
// candidates.map((c) => c.toString())
|
320
|
+
// )
|
275
321
|
while (true) {
|
276
322
|
let changed = false
|
277
323
|
for (const c of candidates) {
|
278
324
|
const candidate = await findAncestorCandidate(events, c[c.length - 1])
|
279
325
|
if (!candidate) continue
|
326
|
+
|
327
|
+
// Check if the candidate is already in the list, and if so, skip it.
|
328
|
+
if (c.includes(candidate)) continue
|
329
|
+
|
330
|
+
// if set size is all cids, then no common ancestor
|
280
331
|
changed = true
|
281
|
-
c.push(candidate)
|
332
|
+
c.push(candidate) // make set?
|
333
|
+
// console.log('candidate', candidates.map((c) => c.toString()))
|
282
334
|
const ancestor = findCommonString(candidates)
|
283
335
|
if (ancestor) return ancestor
|
284
336
|
}
|
@@ -286,12 +338,45 @@ async function findCommonAncestor (events, children) {
|
|
286
338
|
}
|
287
339
|
}
|
288
340
|
|
341
|
+
// async function OGfindCommonAncestor (events, children) {
|
342
|
+
// if (!children.length) return
|
343
|
+
// if (children.length === 1) return children[0]
|
344
|
+
// const candidates = children.map(c => [c])
|
345
|
+
// console.log(
|
346
|
+
// 'og candidates',
|
347
|
+
// candidates.map(c => c.toString())
|
348
|
+
// )
|
349
|
+
// while (true) {
|
350
|
+
// let changed = false
|
351
|
+
// for (const c of candidates) {
|
352
|
+
// const candidate = await findAncestorCandidate(events, c[c.length - 1])
|
353
|
+
// if (!candidate) continue
|
354
|
+
// // if set size is all cids, then no common ancestor
|
355
|
+
// changed = true
|
356
|
+
// c.push(candidate) // make set?
|
357
|
+
// console.log(
|
358
|
+
// 'candidate',
|
359
|
+
// candidates.map(c => c.toString())
|
360
|
+
// )
|
361
|
+
// const ancestor = findCommonString(candidates)
|
362
|
+
// if (ancestor) return ancestor
|
363
|
+
// }
|
364
|
+
// if (!changed) return
|
365
|
+
// }
|
366
|
+
// }
|
367
|
+
|
289
368
|
/**
|
290
369
|
* @param {import('./clock').EventFetcher} events
|
291
370
|
* @param {import('./clock').EventLink<EventData>} root
|
292
371
|
*/
|
293
372
|
async function findAncestorCandidate (events, root) {
|
294
|
-
const { value: event } = await events.get(root)// .catch(() => ({ value: { parents: [] } }))
|
373
|
+
const { value: event } = await events.get(root) // .catch(() => ({ value: { parents: [] } }))
|
374
|
+
// console.log(
|
375
|
+
// 'findAncestorCandidate',
|
376
|
+
// root.toString(),
|
377
|
+
// 'parents',
|
378
|
+
// event.parents.map(p => p.toString())
|
379
|
+
// )
|
295
380
|
if (!event.parents.length) return root
|
296
381
|
return event.parents.length === 1 ? event.parents[0] : findCommonAncestor(events, event.parents)
|
297
382
|
}
|
@@ -302,13 +387,13 @@ async function findAncestorCandidate (events, root) {
|
|
302
387
|
*/
|
303
388
|
function findCommonString (arrays) {
|
304
389
|
// console.log('findCommonString', arrays.map((a) => a.map((i) => String(i))))
|
305
|
-
arrays = arrays.map(
|
390
|
+
arrays = arrays.map(a => [...a])
|
306
391
|
for (const arr of arrays) {
|
307
392
|
for (const item of arr) {
|
308
393
|
let matched = true
|
309
394
|
for (const other of arrays) {
|
310
395
|
if (arr === other) continue
|
311
|
-
matched = other.some(
|
396
|
+
matched = other.some(i => String(i) === String(item))
|
312
397
|
if (!matched) break
|
313
398
|
}
|
314
399
|
if (matched) return item
|
@@ -320,19 +405,19 @@ function findCommonString (arrays) {
|
|
320
405
|
* Find and sort events between the head(s) and the tail.
|
321
406
|
* @param {import('./clock').EventFetcher} events
|
322
407
|
* @param {any[]} head
|
323
|
-
* @param {import('./clock').EventLink<EventData>}
|
408
|
+
* @param {import('./clock').EventLink<EventData>[]} tails
|
324
409
|
*/
|
325
|
-
async function findSortedEvents (events, head,
|
410
|
+
async function findSortedEvents (events, head, tails, doFull) {
|
326
411
|
// const callTag = Math.random().toString(36).substring(7)
|
327
412
|
// get weighted events - heavier events happened first
|
328
413
|
// const callTag = Math.random().toString(36).substring(7)
|
329
414
|
|
330
415
|
/** @type {Map<string, { event: import('./clock').EventBlockView<EventData>, weight: number }>} */
|
331
416
|
const weights = new Map()
|
332
|
-
head = [...new Set([...head.map(
|
417
|
+
head = [...new Set([...head.map(h => h.toString())])]
|
333
418
|
// console.log(callTag + '.head', head.length)
|
334
419
|
|
335
|
-
const allEvents = new Set([
|
420
|
+
const allEvents = new Set([tails.map((t) => t.toString()).toString(), ...head])
|
336
421
|
if (!doFull && allEvents.size === 1) {
|
337
422
|
// console.log('head contains tail', tail.toString())
|
338
423
|
return []
|
@@ -344,7 +429,8 @@ async function findSortedEvents (events, head, tail, doFull) {
|
|
344
429
|
// console.log(callTag + '.head', head.length, [...head.map((h) => h.toString())], tail.toString())
|
345
430
|
|
346
431
|
// console.time(callTag + '.findEvents')
|
347
|
-
const all = await Promise.all(
|
432
|
+
const all = await (await Promise.all(tails.map((t) => Promise.all(head.map(h => findEvents(events, h, t)))))).flat()
|
433
|
+
// console.log('all', all.length)
|
348
434
|
// console.timeEnd(callTag + '.findEvents')
|
349
435
|
for (const arr of all) {
|
350
436
|
for (const { event, depth } of arr) {
|
@@ -392,8 +478,8 @@ async function findEvents (events, start, end, depth = 0) {
|
|
392
478
|
const acc = [{ event, depth }]
|
393
479
|
const { parents } = event.value
|
394
480
|
// if (parents.length === 1 && String(parents[0]) === send) return acc
|
395
|
-
if (parents.findIndex(
|
481
|
+
if (parents.findIndex(p => String(p) === send) !== -1) return acc
|
396
482
|
// if (parents.length === 1) return acc
|
397
|
-
const rest = await Promise.all(parents.map(
|
483
|
+
const rest = await Promise.all(parents.map(p => findEvents(events, p, end, depth + 1)))
|
398
484
|
return acc.concat(...rest)
|
399
485
|
}
|
package/src/crypto.js
CHANGED
@@ -46,7 +46,7 @@ const decrypt = async function * ({ root, get, key, cache, chunker, hasher }) {
|
|
46
46
|
const { result: nodes } = await cidset.getAllEntries()
|
47
47
|
const unwrap = async (eblock) => {
|
48
48
|
const { bytes, cid } = await codec.decrypt({ ...eblock, key }).catch(e => {
|
49
|
-
console.log('ekey', e)
|
49
|
+
// console.log('ekey', e)
|
50
50
|
throw new Error('bad key: ' + key.toString('hex'))
|
51
51
|
})
|
52
52
|
const block = await createBlock(bytes, cid)
|
package/src/database.js
CHANGED
@@ -61,8 +61,8 @@ export class Database {
|
|
61
61
|
* @memberof Fireproof
|
62
62
|
* @instance
|
63
63
|
*/
|
64
|
-
clockToJSON () {
|
65
|
-
return this.clock.map(cid => cid.toString())
|
64
|
+
clockToJSON (clock = null) {
|
65
|
+
return (clock || this.clock).map(cid => cid.toString())
|
66
66
|
}
|
67
67
|
|
68
68
|
hydrate ({ clock, name, key }) {
|
@@ -102,21 +102,21 @@ export class Database {
|
|
102
102
|
* @memberof Fireproof
|
103
103
|
* @instance
|
104
104
|
*/
|
105
|
-
async changesSince (
|
106
|
-
// console.log('events for', this.instanceId,
|
107
|
-
// console.log('changesSince', this.instanceId,
|
105
|
+
async changesSince (aClock) {
|
106
|
+
// console.log('events for', this.instanceId, aClock?.constructor.name)
|
107
|
+
// console.log('changesSince', this.instanceId, this.clockToJSON(aClock), this.clockToJSON())
|
108
108
|
let rows, dataCIDs, clockCIDs
|
109
|
-
// if (!
|
110
|
-
if (
|
111
|
-
|
112
|
-
const eventKey = JSON.stringify([...
|
109
|
+
// if (!aClock) aClock = []
|
110
|
+
if (aClock && aClock.length > 0) {
|
111
|
+
aClock = aClock.map((cid) => cid.toString())
|
112
|
+
const eventKey = JSON.stringify([...this.clockToJSON(aClock), ...this.clockToJSON()])
|
113
113
|
|
114
114
|
let resp
|
115
115
|
if (this.eventsCache.has(eventKey)) {
|
116
|
-
console.log('events from cache')
|
116
|
+
// console.log('events from cache')
|
117
117
|
resp = this.eventsCache.get(eventKey)
|
118
118
|
} else {
|
119
|
-
resp = await eventsSince(this.blocks, this.clock,
|
119
|
+
resp = await eventsSince(this.blocks, this.clock, aClock)
|
120
120
|
this.eventsCache.set(eventKey, resp)
|
121
121
|
}
|
122
122
|
const docsMap = new Map()
|
@@ -277,7 +277,9 @@ export class Database {
|
|
277
277
|
*/
|
278
278
|
async putToProllyTree (decodedEvent, clock = null) {
|
279
279
|
const event = encodeEvent(decodedEvent)
|
280
|
-
if (clock && JSON.stringify(clock) !== JSON.stringify(this.clockToJSON())) {
|
280
|
+
if (clock && JSON.stringify(this.clockToJSON(clock)) !== JSON.stringify(this.clockToJSON())) {
|
281
|
+
// console.log('this.clock', this.clockToJSON())
|
282
|
+
// console.log('that.clock', this.clockToJSON(clock))
|
281
283
|
// we need to check and see what version of the document exists at the clock specified
|
282
284
|
// if it is the same as the one we are trying to put, then we can proceed
|
283
285
|
const resp = await eventsSince(this.blocks, this.clock, event.value._clock)
|
@@ -287,6 +289,7 @@ export class Database {
|
|
287
289
|
}
|
288
290
|
}
|
289
291
|
const prevClock = [...this.clock]
|
292
|
+
// console.log('putToProllyTree', this.clockToJSON(), decodedEvent)
|
290
293
|
const result = await doTransaction(
|
291
294
|
'putToProllyTree',
|
292
295
|
this.blocks,
|
package/src/db-index.js
CHANGED
@@ -351,7 +351,7 @@ export class DbIndex {
|
|
351
351
|
)
|
352
352
|
this.indexByKey = await bulkIndex(blocks, this.indexByKey, oldIndexEntries.concat(indexEntries), dbIndexOpts)
|
353
353
|
this.dbHead = result.clock
|
354
|
-
})
|
354
|
+
}, false /* don't sync transaction -- maybe move this flag to database.indexBlocks? */)
|
355
355
|
// todo index subscriptions
|
356
356
|
// this.database.notifyExternal('dbIndex')
|
357
357
|
// console.timeEnd(callTag + '.doTransactionupdateIndex')
|
package/src/prolly.js
CHANGED
@@ -19,6 +19,8 @@ import { doTransaction } from './blockstore.js'
|
|
19
19
|
import { create as createBlock } from 'multiformats/block'
|
20
20
|
const blockOpts = { cache, chunker: bf(30), codec, hasher, compare }
|
21
21
|
|
22
|
+
// const SYNC_ROOT = 'fireproof' // change this if you want to break sync
|
23
|
+
|
22
24
|
/**
|
23
25
|
* @typedef {import('./blockstore.js').TransactionBlockstore} TransactionBlockstore
|
24
26
|
*/
|
@@ -52,13 +54,11 @@ export const makeGetBlock = blocks => {
|
|
52
54
|
async function createAndSaveNewEvent ({ inBlocks, bigPut, root, event: inEvent, head, additions, removals = [] }) {
|
53
55
|
let cids
|
54
56
|
const { key, value, del } = inEvent
|
57
|
+
// console.log('createAndSaveNewEvent', root.constructor.name, root.entryList)
|
58
|
+
// root = await root.block
|
55
59
|
const data = {
|
56
60
|
root: root
|
57
|
-
?
|
58
|
-
cid: root.cid,
|
59
|
-
bytes: root.bytes, // can we remove this?
|
60
|
-
value: root.value // can we remove this?
|
61
|
-
}
|
61
|
+
? (await root.address)
|
62
62
|
: null,
|
63
63
|
key
|
64
64
|
}
|
@@ -70,6 +70,29 @@ async function createAndSaveNewEvent ({ inBlocks, bigPut, root, event: inEvent,
|
|
70
70
|
data.value = value
|
71
71
|
data.type = 'put'
|
72
72
|
}
|
73
|
+
// console.log('head', head)
|
74
|
+
// if (head.length === 0) {
|
75
|
+
// // create an empty prolly root
|
76
|
+
// let emptyRoot
|
77
|
+
|
78
|
+
// for await (const node of create({ get: getBlock, list: [{ key: '_sync', value: SYNC_ROOT }], ...blockOpts })) {
|
79
|
+
// emptyRoot = await node.block
|
80
|
+
// bigPut(emptyRoot)
|
81
|
+
// }
|
82
|
+
// console.log('emptyRoot', emptyRoot)
|
83
|
+
// const first = await EventBlock.create(
|
84
|
+
// {
|
85
|
+
// root: emptyRoot.cid,
|
86
|
+
// key: null,
|
87
|
+
// value: null,
|
88
|
+
// type: 'del'
|
89
|
+
// },
|
90
|
+
// []
|
91
|
+
// )
|
92
|
+
// bigPut(first)
|
93
|
+
// head = [first.cid]
|
94
|
+
// }
|
95
|
+
|
73
96
|
/** @type {import('./clock').EventData} */
|
74
97
|
// @ts-ignore
|
75
98
|
const event = await EventBlock.create(data, head)
|
@@ -138,16 +161,23 @@ const prollyRootFromAncestor = async (events, ancestor, getBlock) => {
|
|
138
161
|
// console.log('prollyRootFromAncestor', ancestor)
|
139
162
|
const event = await events.get(ancestor)
|
140
163
|
const { root } = event.value.data
|
141
|
-
// console.log('prollyRootFromAncestor', root.cid, JSON.stringify(root.value))
|
142
164
|
if (root) {
|
143
|
-
return load({ cid: root
|
165
|
+
return load({ cid: root, get: getBlock, ...blockOpts })
|
144
166
|
} else {
|
145
|
-
|
167
|
+
// console.log('no root', root) // false means no common ancestor. null means empty database.
|
168
|
+
return root
|
146
169
|
}
|
147
170
|
}
|
148
171
|
|
172
|
+
// async function bigMerge (events, head, getBlock) {
|
173
|
+
// const allRoots = await Promise.all(head.map(async h => prollyRootFromAncestor(events, h, getBlock)))
|
174
|
+
// console.log('allRoots', allRoots)
|
175
|
+
// // todo query over all roots and merge them, but how do they not have a common ancestor? they all start with the _sync root
|
176
|
+
// throw new Error('not implemented')
|
177
|
+
// }
|
178
|
+
|
149
179
|
const doProllyBulk = async (inBlocks, head, event, doFull = false) => {
|
150
|
-
const { getBlock, blocks } = makeGetAndPutBlock(inBlocks)
|
180
|
+
const { getBlock, blocks } = makeGetAndPutBlock(inBlocks) // this is doubled with eventfetcher
|
151
181
|
let bulkSorted = []
|
152
182
|
let prollyRootNode = null
|
153
183
|
const events = new EventFetcher(blocks)
|
@@ -155,14 +185,22 @@ const doProllyBulk = async (inBlocks, head, event, doFull = false) => {
|
|
155
185
|
if (!doFull && head.length === 1) {
|
156
186
|
prollyRootNode = await prollyRootFromAncestor(events, head[0], getBlock)
|
157
187
|
} else {
|
158
|
-
|
159
|
-
|
160
|
-
|
188
|
+
// Otherwise, we find the common ancestor and update the root and other blocks
|
189
|
+
// todo this is returning more events than necessary, lets define the desired semantics from the top down
|
190
|
+
// good semantics mean we can cache the results of this call
|
191
|
+
// const {cids, events : bulkSorted } = await findEventsToSync(blocks, head)
|
161
192
|
const { ancestor, sorted } = await findCommonAncestorWithSortedEvents(events, head, doFull)
|
193
|
+
|
162
194
|
bulkSorted = sorted
|
163
|
-
// console.log('sorted', JSON.stringify(sorted.map(({ value: { data: { key, value } } }) => ({ key, value }))))
|
164
|
-
|
165
|
-
|
195
|
+
// console.log('sorted', !!ancestor, JSON.stringify(sorted.map(({ value: { data: { key, value } } }) => ({ key, value }))))
|
196
|
+
if (ancestor) {
|
197
|
+
prollyRootNode = await prollyRootFromAncestor(events, ancestor, getBlock)
|
198
|
+
// if (!prollyRootNode) {
|
199
|
+
// prollyRootNode = await bigMerge(events, head, getBlock)
|
200
|
+
// // throw new Error('no common ancestor')
|
201
|
+
// }
|
202
|
+
}
|
203
|
+
// console.log('event', event)
|
166
204
|
}
|
167
205
|
}
|
168
206
|
|
@@ -170,16 +208,23 @@ const doProllyBulk = async (inBlocks, head, event, doFull = false) => {
|
|
170
208
|
|
171
209
|
// if prolly root node is null, we need to create a new one
|
172
210
|
if (!prollyRootNode) {
|
211
|
+
// console.log('make new root', bulkOperations.length)
|
173
212
|
let root
|
213
|
+
// let rootNode
|
174
214
|
const newBlocks = []
|
175
215
|
// if all operations are deletes, we can just return an empty root
|
176
216
|
if (bulkOperations.every(op => op.del)) {
|
177
217
|
return { root: null, blocks: [], clockCIDs: await events.all() }
|
178
218
|
}
|
179
219
|
for await (const node of create({ get: getBlock, list: bulkOperations, ...blockOpts })) {
|
180
|
-
root = await node.block
|
181
|
-
|
220
|
+
// root = await node.block
|
221
|
+
root = node
|
222
|
+
newBlocks.push(await node.block)
|
182
223
|
}
|
224
|
+
// throw new Error('not root time')
|
225
|
+
// root.isThisOne = 'yes'
|
226
|
+
// console.log('made new root', root.constructor.name, root.block.cid.toString())
|
227
|
+
|
183
228
|
return { root, blocks: newBlocks, clockCIDs: await events.all() }
|
184
229
|
} else {
|
185
230
|
const writeResp = await prollyRootNode.bulk(bulkOperations) // { root: newProllyRootNode, blocks: newBlocks }
|
@@ -199,7 +244,7 @@ const doProllyBulk = async (inBlocks, head, event, doFull = false) => {
|
|
199
244
|
*/
|
200
245
|
export async function put (inBlocks, head, event, options) {
|
201
246
|
const { bigPut } = makeGetAndPutBlock(inBlocks)
|
202
|
-
|
247
|
+
// console.log('major put')
|
203
248
|
// If the head is empty, we create a new event and return the root and addition blocks
|
204
249
|
if (!head.length) {
|
205
250
|
const additions = new Map()
|
@@ -231,7 +276,7 @@ export async function put (inBlocks, head, event, options) {
|
|
231
276
|
return createAndSaveNewEvent({
|
232
277
|
inBlocks,
|
233
278
|
bigPut,
|
234
|
-
root:
|
279
|
+
root: newProllyRootNode, // Block
|
235
280
|
event,
|
236
281
|
head,
|
237
282
|
additions: Array.from(additions.values()) /*, todo? Array.from(removals.values()) */
|
@@ -250,20 +295,25 @@ export async function root (inBlocks, head, doFull = false) {
|
|
250
295
|
throw new Error('no head')
|
251
296
|
}
|
252
297
|
// console.log('root', head.map(h => h.toString()))
|
253
|
-
const { root: newProllyRootNode, blocks: newBlocks, clockCIDs } = await doProllyBulk(inBlocks, head, null, doFull)
|
254
298
|
// todo maybe these should go to a temp blockstore?
|
255
|
-
await doTransaction(
|
299
|
+
return await doTransaction(
|
256
300
|
'root',
|
257
301
|
inBlocks,
|
258
302
|
async transactionBlocks => {
|
259
303
|
const { bigPut } = makeGetAndPutBlock(transactionBlocks)
|
304
|
+
const { root: newProllyRootNode, blocks: newBlocks, clockCIDs } = await doProllyBulk(inBlocks, head, null, doFull)
|
305
|
+
//
|
306
|
+
// const rootBlock = await newProllyRootNode.block
|
307
|
+
// bigPut(rootBlock)
|
260
308
|
for (const nb of newBlocks) {
|
261
309
|
bigPut(nb)
|
262
310
|
}
|
311
|
+
// console.log('root root', newProllyRootNode.constructor.name, newProllyRootNode)
|
312
|
+
return { clockCIDs, node: newProllyRootNode }
|
263
313
|
},
|
264
314
|
false
|
265
315
|
)
|
266
|
-
return { clockCIDs, node: newProllyRootNode }
|
316
|
+
// return { clockCIDs, node: newProllyRootNode }
|
267
317
|
}
|
268
318
|
|
269
319
|
/**
|
@@ -279,7 +329,8 @@ export async function eventsSince (blocks, head, since) {
|
|
279
329
|
return { clockCIDs: [], result: [] }
|
280
330
|
}
|
281
331
|
// @ts-ignore
|
282
|
-
const sinceHead = [...since, ...head] // ?
|
332
|
+
const sinceHead = [...since, ...head].map(h => h.toString()) // ?
|
333
|
+
// console.log('eventsSince', sinceHead.map(h => h.toString()))
|
283
334
|
const { cids, events: unknownSorted3 } = await findEventsToSync(blocks, sinceHead)
|
284
335
|
return { clockCIDs: cids, result: unknownSorted3.map(({ value: { data } }) => data) }
|
285
336
|
}
|
@@ -318,9 +369,16 @@ async function rootOrCache (blocks, head, rootCache, doFull = false) {
|
|
318
369
|
// console.log('finding root')
|
319
370
|
// const callTag = Math.random().toString(36).substring(7)
|
320
371
|
// console.time(callTag + '.root')
|
372
|
+
//
|
373
|
+
// const prevClock = [...this.clock]
|
374
|
+
|
321
375
|
;({ node, clockCIDs } = await root(blocks, head, doFull))
|
376
|
+
|
377
|
+
// this.applyClock(prevClock, result.head)
|
378
|
+
// await this.notifyListeners([decodedEvent])
|
379
|
+
|
322
380
|
// console.timeEnd(callTag + '.root')
|
323
|
-
// console.log('found root')
|
381
|
+
// console.log('found root', node.entryList)
|
324
382
|
}
|
325
383
|
return { node, clockCIDs }
|
326
384
|
}
|
package/src/sync.js
CHANGED
@@ -23,10 +23,15 @@ export class Sync {
|
|
23
23
|
this.pushBacklogResolve = resolve
|
24
24
|
this.pushBacklogReject = reject
|
25
25
|
})
|
26
|
+
this.isReady = false
|
26
27
|
// this.pushBacklog.then(() => {
|
27
28
|
// // console.log('sync backlog resolved')
|
28
29
|
// this.database.notifyReset()
|
29
30
|
// })
|
31
|
+
// this.connected = new Promise((resolve, reject) => {
|
32
|
+
// this.readyResolve = resolve
|
33
|
+
// this.readyReject = reject
|
34
|
+
// })
|
30
35
|
}
|
31
36
|
|
32
37
|
async offer () {
|
@@ -75,6 +80,7 @@ export class Sync {
|
|
75
80
|
// console.log('not a car', data.toString())
|
76
81
|
}
|
77
82
|
if (reader) {
|
83
|
+
// console.log('got car')
|
78
84
|
this.status = 'parking car'
|
79
85
|
const blz = new Set()
|
80
86
|
for await (const block of reader.blocks()) {
|
@@ -87,7 +93,7 @@ export class Sync {
|
|
87
93
|
// this.database.clock.map(c => c.toString())
|
88
94
|
// )
|
89
95
|
// console.log(
|
90
|
-
// 'got blocks',
|
96
|
+
// 'got blocks!',
|
91
97
|
// [...blz].map(({ cid }) => cid.toString())
|
92
98
|
// )
|
93
99
|
// @ts-ignore
|
@@ -128,7 +134,7 @@ export class Sync {
|
|
128
134
|
} else if (message.clock) {
|
129
135
|
const reqCidDiff = message
|
130
136
|
// this might be a CID diff
|
131
|
-
console.log('got diff', reqCidDiff)
|
137
|
+
// console.log('got diff', reqCidDiff)
|
132
138
|
const carBlock = await Sync.makeCar(this.database, null, reqCidDiff.cids)
|
133
139
|
if (!carBlock) {
|
134
140
|
// we are full synced
|
@@ -137,9 +143,10 @@ export class Sync {
|
|
137
143
|
this.peer.send(JSON.stringify({ ok: true }))
|
138
144
|
// this.pushBacklogResolve({ ok: true })
|
139
145
|
} else {
|
140
|
-
// console.log('do send', carBlock.bytes.length)
|
146
|
+
// console.log('do send diff', carBlock.bytes.length)
|
141
147
|
this.status = 'sending diff car'
|
142
148
|
this.peer.send(carBlock.bytes)
|
149
|
+
// console.log('sent diff car')
|
143
150
|
// this.pushBacklogResolve({ ok: true })
|
144
151
|
}
|
145
152
|
}
|
@@ -153,7 +160,7 @@ export class Sync {
|
|
153
160
|
}
|
154
161
|
|
155
162
|
async sendUpdate (blockstore) {
|
156
|
-
if (!this.peer) return
|
163
|
+
if (!this.peer || !this.isReady) return
|
157
164
|
// console.log('send update from', this.database.instanceId)
|
158
165
|
// todo should send updates since last sync
|
159
166
|
const newCar = await blocksToCarBlock(blockstore.lastCid, blockstore)
|
@@ -163,6 +170,7 @@ export class Sync {
|
|
163
170
|
|
164
171
|
async startSync () {
|
165
172
|
// console.log('start sync', this.peer.initiator)
|
173
|
+
this.isReady = true
|
166
174
|
const allCIDs = await this.database.allStoredCIDs()
|
167
175
|
// console.log('allCIDs', allCIDs)
|
168
176
|
const reqCidDiff = {
|
package/src/valet.js
CHANGED
@@ -39,11 +39,11 @@ export class Valet {
|
|
39
39
|
this.name = name
|
40
40
|
this.setKeyMaterial(keyMaterial)
|
41
41
|
this.uploadQueue = cargoQueue(async (tasks, callback) => {
|
42
|
-
console.log(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
)
|
42
|
+
// console.log(
|
43
|
+
// 'queue worker',
|
44
|
+
// tasks.length,
|
45
|
+
// tasks.reduce((acc, t) => acc + t.value.length, 0)
|
46
|
+
// )
|
47
47
|
if (this.uploadFunction) {
|
48
48
|
// todo we can coalesce these into a single car file
|
49
49
|
return await this.withDB(async db => {
|