// T2 — pass-through wrapper around hypercore-storage.
//
// Core takes its per-core storage from exactly two calls (hypercore/lib/core.js):
// :162 await this.db.resumeCore(discoveryKey)
// :232 await this.db.createCore({...})
// so subclassing CorestoreStorage and wrapping those two return values puts us
// under every block read/write without touching anything above.
//
// Objects that construct more of themselves (snapshot/atomize) must be re-wrapped,
// and read()/write() must return wrapped RX/TX — that is where T3 will redirect
// getBlock/putBlock. Here everything is pure delegation.
const CorestoreStorage = require('hypercore-storage')
const stats = { getBlock: 0, putBlock: 0, read: 0, write: 0, wrapped: 0 }
function delegate (target, overrides) {
return new Proxy(target, {
get (t, prop) {
if (prop === '__unwrapped') return t
if (overrides && prop in overrides) return overrides[prop]
// NB: no receiver — getters must run with `this` bound to the real object
const v = Reflect.get(t, prop)
return typeof v === 'function' ? v.bind(t) : v
},
set (t, prop, value) {
t[prop] = value
return true
}
})
}
function wrapRX (rx) {
stats.read++
return delegate(rx, {
getBlock (index) {
stats.getBlock++
return rx.getBlock(index)
}
})
}
function wrapTX (tx) {
stats.write++
return delegate(tx, {
putBlock (index, data) {
stats.putBlock++
return tx.putBlock(index, data)
}
})
}
function wrapCoreStorage (inner) {
if (!inner) return inner
stats.wrapped++
const overrides = {
read: (...args) => wrapRX(inner.read(...args)),
write: (...args) => wrapTX(inner.write(...args)),
snapshot: (...args) => wrapCoreStorage(inner.snapshot(...args)),
atomize: (...args) => wrapCoreStorage(inner.atomize(...args))
}
return delegate(inner, overrides)
}
class WrappedCorestoreStorage extends CorestoreStorage {
async resumeCore (discoveryKey) {
return wrapCoreStorage(await super.resumeCore(discoveryKey))
}
async createCore (data) {
return wrapCoreStorage(await super.createCore(data))
}
}
module.exports = { WrappedCorestoreStorage, stats }