diff --git a/index.js b/index.js index ecbee70..d72ddcd 100644 --- a/index.js +++ b/index.js @@ -784,6 +784,10 @@ class HyperDB { if (this.updates.size > 0) await this._flush() if (this.autoClose === true) await this.close() } + + download() { + return this.engine.download() + } } function maybeClosed(db) { diff --git a/lib/engine/bee.js b/lib/engine/bee.js index a583962..33ecdcc 100644 --- a/lib/engine/bee.js +++ b/lib/engine/bee.js @@ -170,6 +170,9 @@ module.exports = class BeeEngine { await batch.flush() } + // dummy cause we are phasing bee1 out + async download() {} + clearRequests(reqs) { Hypercore.destroyRequests(reqs, null) } diff --git a/lib/engine/bee2.js b/lib/engine/bee2.js index c6620a2..1177ebe 100644 --- a/lib/engine/bee2.js +++ b/lib/engine/bee2.js @@ -202,6 +202,10 @@ module.exports = class Bee2Engine { await batch.flush() } + download() { + return this.db.download() + } + clearRequests(reqs) { Hypercore.destroyRequests(reqs, null) } diff --git a/lib/engine/rocks.js b/lib/engine/rocks.js index 9549fba..3e9c198 100644 --- a/lib/engine/rocks.js +++ b/lib/engine/rocks.js @@ -178,6 +178,10 @@ module.exports = class RocksEngine { await this.write.flush() } + download() { + // api parity + } + clearRequests(reqs) { // api parity } diff --git a/test/replicate.js b/test/replicate.js index ac9ac8a..683f090 100644 --- a/test/replicate.js +++ b/test/replicate.js @@ -192,3 +192,54 @@ test('requests are cleared on close from unclosed snap', async function ({ creat await clone.close() }) }) + +test( + 'download makes the db readable offline', + { rocks: false, bee: false }, + async function ({ create }, t) { + const db = await create() + + const tx = db.transaction() + for (let i = 0; i < 100; i++) { + await tx.insert('@db/members', { id: 'member-' + i, age: 20 + (i % 50) }) + } + await tx.flush() + + const expected = await db.find('@db/members').toArray() + t.is(expected.length, 100) + + const clone = await create({ key: db.core.key, autoUpdate: true }) + + const unreplicate = replicate(t, clone, db) + + await new Promise((resolve) => clone.core.once('append', resolve)) + + await clone.download() + await unreplicate() + + // every read must be served locally now, no peers connected + const members = await clone.find('@db/members').toArray() + t.alike(members, expected) + + const one = await clone.get('@db/members', { id: 'member-42' }) + t.alike(one, { id: 'member-42', age: 62 }) + + const range = await clone.find('@db/members', { gte: { id: 'member-9' } }).toArray() + t.is(range.length, 11) + + await clone.close() + await db.close() + } +) + +test('download is a no-op on engines without it', { bee2: false }, async function ({ create }, t) { + const db = await create() + await db.insert('@db/members', { id: 'someone', age: 40 }) + await db.flush() + + await db.download() + + t.alike(await db.get('@db/members', { id: 'someone' }), { id: 'someone', age: 40 }) + + await db.close() +})