diff --git a/README.md b/README.md index c678758c649ca7208d36ffdfdbac74c7b96938e3..bbdf2433f8f48829ec6f5a9d28f55d90b5880ea5 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Bugs - [ ] initialize dd_keys for new node (→ bootstrap) - [ ] fix merging blocked when inode unreachable, timeout seems ignored - [ ] fix pubsub cid can not be fetched triggers pubsub abort (dirty workaround for know) +- [ ] fix `UND_ERR_HEADERS_TIMEOUT` that happen very often when pinning 📌 Features - [x] pubkey instead of ss58 address if we want data to be compatible across networks → ss58 - [ ] add periodic sync with a list of trusted peers (IPNS entries) diff --git a/src/indexer/database.ts b/src/indexer/database.ts index 468a0f972828fa4a565f1c8e865974dbb3f20230..da91c81ea38cfbe0f0fd5fe5b70a7b5e2c4428f8 100644 --- a/src/indexer/database.ts +++ b/src/indexer/database.ts @@ -87,7 +87,7 @@ const cesiumPlusProfile: QueryBuilder = { avatar = EXCLUDED.avatar, geoloc = EXCLUDED.geoloc, city = EXCLUDED.city, - socials = EXCLUDED.socials; + socials = EXCLUDED.socials WHERE EXCLUDED.time > profiles.time; `, dataGetter: defaultDataGetter, @@ -195,15 +195,14 @@ async function handleIrWithNonNullData<T>(irCID: CID, ir: IndexRequest, q: Query } q.dataGetter(dataCID) .then((data) => q.dataTransform(irCID, ir, dataCID, data).then(dataHandler<T>(q, irCID, ir, dataCID))) - .catch((e) => { - console.log('☁️ error indexing ' + dataCID) - console.error(e) + .catch((_e) => { + console.log('😭 error indexing ' + dataCID) }) } // insert index request in database export async function handleInsertRequest(irCID: CID, ir: IndexRequest) { - console.log('💾 indexing ' + irCID) + // console.debug('💾 indexing ' + irCID) switch (ir.kind) { // insert cesium plus profile @@ -220,6 +219,8 @@ export async function handleInsertRequest(irCID: CID, ir: IndexRequest) { // delete cesium plus profile case CESIUM_PLUS_PROFILE_DELETE: + // FIXME if delete instruction is received from past, this should be ignored + // i.e.: database should keep track of deleted profiles with a timestamp to allow items to be inserted in any order await client.query(`DELETE FROM profiles WHERE pubkey = $1;`, [ir.pubkey]) break diff --git a/src/indexer/handlers.ts b/src/indexer/handlers.ts index 3706a44c195d27d0c7ce38386c4761badc5cb1ac..0b4baf8f35ee1c333f5ec40776251f7f227e310f 100644 --- a/src/indexer/handlers.ts +++ b/src/indexer/handlers.ts @@ -3,7 +3,13 @@ import { CID } from 'multiformats' import EventEmitter from 'events' import { getLatestIndexedCID, handleInsertRequest, setLatestIndexedCID } from './database' import { kubo } from '../kubo' -import { getDiff, getAll } from '../interface' +import { getDiff } from '../interface' +import type { IndexRequest } from '../types' +import { arrayToVinode, mergeInodesSyncCID, timestampToKey } from '../processor' +import { DD_TAMT_OPT } from './ipns' +import type { Globals } from './start' + +const BATCH_SIZE = 1000 // === EVENTS === @@ -14,16 +20,19 @@ export const events = new EventEmitter() export enum evtype { // event to trigger collecting new index requests in queue trigger = 'trigger', + // event to trigger collecting peer index requests from queue + triggerCollect = 'triggerCollect', // event to trigger injestion of new requests in database indexDiff = 'indexThat', - // event to trigger database sync from scratch or last state - indexStart = 'start' + // event to trigger computing diff + computeDiff = 'computeDiff' } // === INDEXING === -// queue of Start waiting to be processed -const indexQueueStart: Array<CID> = [] +// queue of index requests waiting to be added to the tree +// these are the requests coming from other pods +const mergeQueue: Array<[string, CID]> = [] // FIXME at the moment the key is not used // queue of DiffData waiting to be processed const indexQueueDiff: Array<DiffData> = [] // lock to prevent multiple database edits which would fake the last root cid @@ -32,7 +41,96 @@ let isIndexing = false // === HANDLERS === -// diff indexer event handler +/// ----- pubsub message handler ----- +export function getMessageHandler(processQueue: Array<[CID, IndexRequest]>) { + async function validMessageHandler(_cid: CID, dag: IndexRequest): Promise<void> { + // re-build the index request (because the type is loosely defined) + const ir: IndexRequest = { + kind: dag.kind, + time: dag.time, + data: dag.data, + pubkey: dag.pubkey, + sig: dag.sig + } + // then store the index request locally + kubo.dag + .put(ir) + .then((cid) => { + // cids should be the same + if (cid.toString() != _cid.toString()) console.log('👾 ', cid, '!=', _cid) + console.log('adding valid index request to process queue') + // pin the index request we just added + kubo.pin.add(cid).catch(() => console.log(`📌📌 could not pin index request that we just added ${cid}`)) + // add index request to the process list + processQueue.push([cid, ir]) + // ask to process the request + events.emit(evtype.trigger) + }) + .catch(() => console.log(`📌 could not add valid index request ${_cid}`)) + return + } + return validMessageHandler +} + +/// insert a batch of index requests in tree +async function insertBatch(rootCID: CID, items: Array<[CID, IndexRequest]>): Promise<DiffData> { + // convert it to a list of [key, cid] for batch insert (merge) + const requests = items.map(([cid, ir]) => [timestampToKey(ir.time), cid]).sort() as Array<[string, CID]> + const tree = arrayToVinode(requests) + // insert them + const newCID = await mergeInodesSyncCID(rootCID, tree) + const diffData: DiffData = { oldCID: rootCID, newCID, newItems: items } + return diffData +} + +/// ----- queue event handler ----- +export function getBatchHandler(glob: Globals) { + async function handleBatchFromQueue(): Promise<void> { + // ignore event if already processing something or if queue is empty + if (glob.lockTree) { + console.log('busy') + return + } + if (glob.processQueue.length == 0) { + return + } + // if not processing, do lock process + glob.lockTree = true + // take elements from queue + let i = undefined + const items: Array<[CID, IndexRequest]> = [] + let num = 0 + while ((i = glob.processQueue.shift()) != undefined && num < BATCH_SIZE) { + num += 1 + items.push(i) + } + console.log('merge queue', mergeQueue.length, 'process queue', glob.processQueue.length) + + // try inserting items + try { + // insert batch and get diff + const diffData = await insertBatch(glob.rootCID, items) + // update root cid and publishes it + const newCID = diffData.newCID + console.log(`👉 new root CID ${newCID}`) + glob.rootCID = newCID + glob.lockTree = false + kubo.name.publish(newCID, DD_TAMT_OPT).catch(console.log) + // add new data to database + events.emit(evtype.indexDiff, diffData) + } catch (e) { + console.error(`🥊 error merging ${glob.rootCID} with ${items.length} items`, e) + glob.processQueue.push(...items) // add them back to the process queue + glob.lockTree = false + // try again to collect items + events.emit(evtype.trigger) + } + return // nothing + } + return handleBatchFromQueue +} + +// diff event handler export async function indexKnownDiff(diff: DiffData): Promise<void> { // --- 1. manage lock // if locked, add to index queue @@ -47,69 +145,91 @@ export async function indexKnownDiff(diff: DiffData): Promise<void> { const latestCID = await getLatestIndexedCID() // if not, unlock and go to indexStart with the new CID instead if (latestCID == null || diff.oldCID.toString() != latestCID?.toString()) { - console.log('diff is not based on same cid as database, re-computing diff') - isIndexing = false - await indexStart(diff.newCID) - } else { - // insert all index requests - diff.newItems.forEach((ir) => handleInsertRequest(ir[0], ir[1])) - // write new CID as latest indexed - await setLatestIndexedCID(diff.newCID) - // unlock - isIndexing = false + console.log('🤔 db is not at diff start, computing missing diff') + events.emit(evtype.computeDiff, diff.oldCID, diff.newCID) } + console.log('➕ adding', diff.newItems.length, 'items to the db') + // insert all index requests + diff.newItems.forEach((ir) => handleInsertRequest(ir[0], ir[1])) + // write new CID as latest indexed (this is purely informative) + console.log(`🦐 latest db cid ${diff.newCID}`) + await setLatestIndexedCID(diff.newCID) + // unlock + isIndexing = false // --- 3. check queues checkQueues() } -// start indexer event handler -export async function indexStart(cid: CID): Promise<void> { - // --- 1. manage lock - // if locked, add to index queue - if (isIndexing) { - indexQueueStart.push(cid) - return - } - // else lock - isIndexing = true - // --- 2. handle - // read latest indexed root CID from database - const latestCID = await getLatestIndexedCID() - console.log('👀 latest indexed cid ' + latestCID) +// compute diff with remote tree and add index requests to merge queue +export async function computeDiff(fromCID: CID, toCID: CID): Promise<void> { // if they differ - if (latestCID?.toString() != cid.toString()) { - // if defined iterate over all index requests of diff - // else iterate over all index requests of given cid - const iterator = latestCID ? getDiff(latestCID, cid) : getAll(cid) + if (fromCID.toString() != toCID.toString()) { + console.log(`👐 computing diff from ${fromCID} to ${toCID}`) + // iterate over all index requests of diff + const iterator = getDiff(fromCID, toCID) + let num = 0 for await (const leaf of iterator) { for (let irCID of leaf) { - // make sure to pin the index request to be able to serve its content later - kubo.pin.add(irCID, { recursive: true }).catch(() => console.log('📌 could not pin index request ' + irCID)) - // when the index request is there, save it to the database - kubo.dag - .get(irCID) - .then((ir) => handleInsertRequest(irCID, ir.value)) - .catch(() => console.log('☁️ could not get index request ' + irCID)) + // add it to the process queue to be added in the tree + mergeQueue.push(['key', irCID]) // FIXME get key while iterating + num += 1 + } + // make sure that collection is triggered regularly + if (num > BATCH_SIZE) { + num = 0 + events.emit(evtype.triggerCollect) } } - // write root CID as lastest indexed CID - await setLatestIndexedCID(cid) - console.log('👍 new latest indexed cid ' + cid) + events.emit(evtype.triggerCollect) } else { - console.log('👌 already indexed ' + cid) + console.log(`👌 already at ${toCID}`) } - // unlock - isIndexing = false - // --- 3. check queues - checkQueues() +} + +// get collector from mergeQueue to processQueue +export function getItemsCollector(glob: Globals) { + // takes item from merge queue, fetch index request, and put them in process queue + async function fetchItemsFromMergeQueue() { + // sort merge queue by key to give a better chance of good batching + // mergeQueue.sort() // FIXME while key is not there, we can not sort + // number of items retreived + let num = 0 + // item taker + let i = undefined + const items: Array<Promise<[CID, IndexRequest]>> = [] + // takes items in queue but no more than batch and avoid duplicates and not when queue is full + const seen: Map<string, boolean> = new Map() + while ((i = mergeQueue.shift()) != undefined && num < BATCH_SIZE && glob.processQueue.length < BATCH_SIZE) { + const [_k, cid] = i + if (!seen.get(cid.toString())) { + seen.set(cid.toString(), true) + num += 1 + items.push(Promise.all([cid, kubo.dag.get(cid).then((r) => r.value)])) + } + } + const awaitedItems = await Promise.all(items) + awaitedItems.forEach(([c, _ir]) => { + // make sure to pin the index request to be able to serve its content later + kubo.pin.add(c, { recursive: true }).catch((_e) => console.log(`📌 could not pin remote index request ${c}`)) + }) + // add index requests to process queue + glob.processQueue.push(...awaitedItems) + // ask them to be processed if not processing + if (!glob.lockTree) { + events.emit(evtype.trigger) + } + // ask next batch if any + if (mergeQueue.length) { + events.emit(evtype.triggerCollect) + } + } + return fetchItemsFromMergeQueue } // check queues function checkQueues() { - // check queue to see if diff came in the meanwile + // check queue to see if new diff came in the meanwile checkQueueDiff() - // when kown diff if clean, check sync - checkQueueStart() } // if diff came in the meanwhile, index them @@ -118,10 +238,3 @@ function checkQueueDiff() { indexKnownDiff(indexQueueDiff.shift()!) } } - -// if start came in the meanwhile, index them -function checkQueueStart() { - if (indexQueueStart.length != 0) { - indexStart(indexQueueStart.shift()!) - } -} diff --git a/src/indexer/start.ts b/src/indexer/start.ts index ebdbabaf03b6373bccf7c8fd73a7ea23da5faf36..c7065dd552b2086e606cf6457a665c574af07e0a 100644 --- a/src/indexer/start.ts +++ b/src/indexer/start.ts @@ -1,108 +1,37 @@ -import { TOPIC } from '../consts' -import { timestampToKey, arrayToVinode, mergeInodesSyncCID, resolveHist } from '../processor' +import { EMPTY_NODE_CID, TOPIC } from '../consts' +import { resolveHist } from '../processor' import { getPubSubHandler } from '../collector' import { KUBO_RPC, kubo, kubo2 } from '../kubo' import type { IndexHist, IndexRequest } from '../types' import { CID } from 'multiformats' -import { events, evtype, indexKnownDiff, indexStart } from './handlers' -import type { DdKeys, DiffData } from './types' +import { getBatchHandler, getMessageHandler, indexKnownDiff, computeDiff, getItemsCollector } from './handlers' +import { events, evtype } from './handlers' +import type { DdKeys } from './types' import { getRootCIDfromArgs } from './utils' -import { DD_TAMT_HIST_OPT, DD_TAMT_OPT, ddKeys } from './ipns' +import { DD_TAMT_HIST_OPT, ddKeys } from './ipns' import { initHistIfNull, publishKeys, trusted_peer_list } from './bootstrap' +import { getLatestIndexedCID, setLatestIndexedCID } from './database' -// === HANDLERS === - -/// ----- pubsub message handler ----- -async function validMessageHandler(_cid: CID, dag: IndexRequest): Promise<void> { - // re-build the index request (because the type is loosely defined) - const ir: IndexRequest = { - kind: dag.kind, - time: dag.time, - data: dag.data, - pubkey: dag.pubkey, - sig: dag.sig - } - // then store the index request locally - kubo.dag - .put(ir) - .then((cid) => { - // cids should be the same - if (cid.toString() != _cid.toString()) console.log('⚠️ ' + cid + ' != ' + _cid) - console.log('adding valid index request to process queue') - // pin the index request we just added - kubo.pin.add(cid).catch(() => console.log('📌 could not pin index request that we just added ' + cid)) - // add index request to the process list - processQueue.push([cid, ir]) - // ask to process the request - events.emit(evtype.trigger) - }) - .catch(() => console.log('📌 could not add valid index request ' + _cid)) - return -} -const handleMessage = getPubSubHandler(validMessageHandler) - -/// ----- queue event handler ----- -function handleBatch() { - // console.log('entering handleBatch') - // ignore event if already processing something or if queue is empty - if (isProcessingQueue) { - console.log('busy') - return - } - if (processQueue.length == 0) { - // console.log('nothing to process') - return - } - // if not processing, do lock process - isProcessingQueue = true - // console.log('processing handleBatch') - // take elements from queue - let i = undefined - const items: Array<[CID, IndexRequest]> = [] - while ((i = processQueue.shift()) != undefined) { - items.push(i) - } - // convert it to a list of [key, cid] for batch insert (merge) - const requests = items.map(([cid, ir]) => [timestampToKey(ir.time), cid]).sort() as Array<[string, CID]> - const tree = arrayToVinode(requests) - - // insert them - // console.log('merging tree on ' + rootCID) - mergeInodesSyncCID(rootCID, tree) - .then((cid) => { - // ➡️ update CID - const oldCID = rootCID - rootCID = cid - console.log(`👉 new root CID ${cid}`) - // ➡️ publish new CID - kubo.name.publish(rootCID, DD_TAMT_OPT).catch(console.log) - isProcessingQueue = false - // trigger an other event in case new requests arrived meanwhile - events.emit(evtype.trigger) - // emit event to be processed by indexer - // FIXME there is a vulnerability here since we to not check that items were not submitted twice - // this allows to submit data multiple times for repeated add in the database - // the merge function should return a list of cids that were *really* added - const diffData: DiffData = { oldCID: oldCID, newCID: rootCID, newItems: items } - events.emit(evtype.indexDiff, diffData) - }) - .catch((e) => { - console.log('error merging ' + rootCID + ' ' + requests) - isProcessingQueue = false - events.emit(evtype.trigger) - console.log(e) - }) +// === GLOBALS === +export interface Globals { + // queue of index requests waiting to be processed + // these are the requests received by the network + processQueue: Array<[CID, IndexRequest]> + // lock to avoid triggering multiple simultaneous edits + lockTree: boolean + // root CID of tree + rootCID: CID } // ----- regularly publish history function periodicHistPublish(interval: number) { setInterval(async () => { // if history is up to date, to nothing - if (hist.current_index.toString() == rootCID.toString()) return + if (hist.current_index.toString() == glob.rootCID.toString()) return // else, update the history const newHist: IndexHist = { last_history: histCID, - current_index: rootCID, + current_index: glob.rootCID, number: hist.number + 1, timestamp: Date.now() } @@ -131,7 +60,7 @@ async function peerSync(trusted_peer_list: string[]) { for await (const name of kubo.name.resolve(peerDdKeys.tamt, { nocache: true, timeout: 100 })) { const cid = CID.parse(name.slice(6)) // found peer tree, request index diff - events.emit(evtype.indexStart, cid) + events.emit(evtype.computeDiff, glob.rootCID, cid) } } } catch (e) { @@ -163,7 +92,9 @@ function anyErrorCallback(e: any) { pubsubSubscribe() } function pubsubSubscribe() { - kubo2.pubsub.subscribe(TOPIC, handleMessage, pubsubSubscribeOptions).catch(pubsubAbortCallback) + kubo2.pubsub + .subscribe(TOPIC, getPubSubHandler(getMessageHandler(glob.processQueue)), pubsubSubscribeOptions) + .catch(pubsubAbortCallback) console.log('🔌 connected to', KUBO_RPC) console.log('👂 listening on topic', TOPIC) } @@ -184,24 +115,30 @@ const HIST_PUBLISH_PERIOD = 10 * MINUTE // regularly sync form peers to compensate from network outage const PEERSYNC_PERIOD = 1 * DAY -// === GLOBALS === -// queue of index requests waiting to be processed -const processQueue: Array<[CID, IndexRequest]> = [] -// lock to avoid triggering multiple simultaneous edits -// this prevents from branching the local AMT -let isProcessingQueue = false - -/// global rootCID variable -// set it from CLI args -let rootCID = await getRootCIDfromArgs(process.argv) -await initHistIfNull(rootCID) // make sure history is available until then +// init globals +const glob: Globals = { + processQueue: [], + lockTree: false, + // set global rootCID from CLI args + rootCID: await getRootCIDfromArgs(process.argv) +} +await initHistIfNull(glob.rootCID) // make sure history is available until then let histCID: CID = await resolveHist() let hist: IndexHist = (await kubo.dag.get(histCID)).value +// get latest db CID +let latestCID = await getLatestIndexedCID() +if (latestCID == null) { + latestCID = EMPTY_NODE_CID + setLatestIndexedCID(latestCID) +} +console.log(`🛢 latest indexed cid ${latestCID}`) + // bind event handlers -events.on(evtype.trigger, handleBatch) +events.on(evtype.trigger, getBatchHandler(glob)) events.on(evtype.indexDiff, indexKnownDiff) -events.on(evtype.indexStart, indexStart) +events.on(evtype.computeDiff, computeDiff) +events.on(evtype.triggerCollect, getItemsCollector(glob)) pubsubSubscribe() // subscribe to index requests channel periodicHistPublish(HIST_PUBLISH_PERIOD) // regularly publish history periodicPeerSync(PEERSYNC_PERIOD) // regularly sync from peers @@ -209,7 +146,7 @@ periodicPeerSync(PEERSYNC_PERIOD) // regularly sync from peers // emit event to tell indexer to start indexing to database // if it is starting from scratch (emtpy database), it will iterate over all values // if it already indexed up to a given cid (last_indexed_cid in db), it will only iterate over the diff -events.emit(evtype.indexStart, rootCID) +events.emit(evtype.computeDiff, latestCID, glob.rootCID) // at startup browse peer list peerSync(trusted_peer_list) diff --git a/src/processor.ts b/src/processor.ts index bcc2a25f25e83320664bd5c0992c69f5da494c65..350418f49bebee00451501e1aee5e8e22aafe53c 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -200,11 +200,12 @@ export async function processLeaf(node: IndexLeaf, val: CID): Promise<CID> { export async function mergeInodesSyncCID(nodeACID: CID, nodeB: IndexVinode | IndexLeaf): Promise<CID> { // fail with small timeout since this data is supposed to be pinned locally // console.log('fetching ' + nodeACID) - const nodeA = (await kubo.dag.get(nodeACID, { timeout: 100 })).value + const nodeA = (await kubo.dag.get(nodeACID, { timeout: 1000 })).value // FIXME decrease this timeout without bug const newCID = await mergeInodesSync(nodeA, nodeB) // unpin old node CID if different // we do not mind if it was not pinned - if (nodeACID.toString() != newCID.toString()) kubo.pin.rm(nodeACID).catch(() => {}) + // WIP pin des not work well + // if (nodeACID.toString() != newCID.toString()) kubo.pin.rm(nodeACID).catch(() => {}) // no need to pin new node CID like: // kubo.pin.add(newCID) // this is already done with the pin option kubo.dag.put(cid, { pin: true }) @@ -224,7 +225,8 @@ export async function mergeInodesSync(nodeA: IndexInode | IndexLeaf, nodeB: Inde leaf: cidList } return kubo.dag.put(newLeaf).then((cid) => { - kubo.pin.add(cid).catch(() => console.log('could not pin newly created leaf')) + // WIP pin des not work well + // kubo.pin.add(cid).catch((_e) => console.log(`📌📌 could not pin newly created leaf ${cid}`)) return cid }) } else if (isAleaf || isBleaf) { @@ -280,7 +282,8 @@ export async function mergeInodesSync(nodeA: IndexInode | IndexLeaf, nodeB: Inde newNode.children[c.b1] = [c.nk1, childA] newNode.children[c.b2] = [c.nk2, await concretizeCid(childB)] const newNodeCid = await kubo.dag.put(newNode).then((cid) => { - kubo.pin.add(cid).catch(() => console.log('could not pin newly created node')) + // WIP pinning does not work well + // kubo.pin.add(cid).catch((_e) => console.log(`📌📌 could not pin newly created node ${cid}`)) return cid }) noda.children[b] = [c.common, newNodeCid] @@ -293,7 +296,8 @@ export async function mergeInodesSync(nodeA: IndexInode | IndexLeaf, nodeB: Inde } // now that we have the new node, we can upload it and return its cid return kubo.dag.put(noda).then((cid) => { - kubo.pin.add(cid).catch(() => console.log('could not pin newly merged node')) + // WIP pinning does not work well + // kubo.pin.add(cid).catch((e) => console.log(`📌📌 could not pin newly merged node ${cid}`)) return cid }) } diff --git a/src/scripts/index-database.ts b/src/scripts/index-database.ts index d5a208fcc34f234d0cba9f83499160387406e1a0..0dccb9dfaa1cbf8976e75021decc3a1fa708348b 100644 --- a/src/scripts/index-database.ts +++ b/src/scripts/index-database.ts @@ -1,12 +1,10 @@ import { CID } from 'multiformats' -import { indexStart } from '../indexer/handlers' import { client, handleInsertRequest } from '../indexer/database' import { getAll } from '../interface' import { kubo } from '../kubo' import type { IndexRequest } from '../types' const cid = CID.parse('bafyreiay2zpectyuxb4d5nxxkwcpdk76ivrm36qmrutpbaaxldh6yt46xi') -await indexStart(cid) // await client.end() diff --git a/src/views/DiffView.vue b/src/views/DiffView.vue index 42bd2f6fafeda8df41532f40496db35f2b56e1df..8461511cf47a15ac65d9a3be9f07bb78b71e7a6c 100644 --- a/src/views/DiffView.vue +++ b/src/views/DiffView.vue @@ -22,14 +22,19 @@ function setRightRoot() { } async function compareRoots() { + // reset diff + addedLeft.value = [] + addedRight.value = [] + // manage trivial cases if (leftRootCid.value == null || rightRootCid.value == null) { result.value = 'cid undefined' return } - if (leftRootCid.value == rightRootCid.value) { + if (leftRootCid.value.toString() == rightRootCid.value.toString()) { result.value = 'same' return } + // do compute result.value = 'computing diff...' // fetch root nodes const [leftRoot, rightRoot] = await Promise.all([kubo.dag.get(leftRootCid.value), kubo.dag.get(rightRootCid.value)]) @@ -55,7 +60,7 @@ function initWithLastCommit() { kubo.dag.get(hist.value.last_history).then((hist1) => { leftRootCid.value = hist1?.value.current_index if (leftRootCid.value) { - compareRoots() + // compareRoots() } }) } @@ -83,13 +88,13 @@ onMounted(initWithLastCommit) <h2>removed:</h2> <ul> <li v-for="e in addedLeft" :key="e[1].toString()"> - <span class="mono">{{ e[0] + '*'.repeat(KEYSIZE - e[0].length) }}</span> {{ e[1].toString() }} + <span class="mono">{{ e[0] + '*'.repeat(KEYSIZE - e[0].length) }} {{ e[1].toString() }}</span> </li> </ul> <h2>added:</h2> <ul> <li v-for="e in addedRight" :key="e[1].toString()"> - <span class="mono">{{ e[0] + '*'.repeat(KEYSIZE - e[0].length) }}</span> {{ e[1].toString() }} + <span class="mono">{{ e[0] + '*'.repeat(KEYSIZE - e[0].length) }} {{ e[1].toString() }}</span> </li> </ul> </div>