Expand economy, autobase, network, observability, and scheduling modules.
Add auction withdraw/cancel, marketplace search helpers, update gossip history, autobase fork/lease/indexer/view APIs, link-probe and circuit-loom utilities, trace trees, pheromone ranking, and scheduling/measurement helpers with tests. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -16,6 +16,13 @@ Autobase **coordination** for multi-writer logs: fork selection, view sync gossi
|
||||
| [hyper-p2p-autobase-indexer-bus](./hyper-p2p-autobase-indexer-bus/) | `autobase-indexer-bus/v1` | yes |
|
||||
| [hyper-p2p-autobase-light-writer](./hyper-p2p-autobase-light-writer/) | `autobase-light-writer/v1` | no |
|
||||
|
||||
## Recent API helpers
|
||||
|
||||
- **fork-choice:** `hasView`, `removeView`, `scoreFork`
|
||||
- **writer-lease:** `leaseRemainingMs`, `currentHolder`, `expireStaleLeases`
|
||||
- **indexer-bus:** `filterByType`, `lastEvent`, `clearEvents`, `peekEvents`
|
||||
- **view-sync:** `viewHash`, `ageMs`, `behindBy`, `isAtLeast`
|
||||
|
||||
## Composition
|
||||
|
||||
```text
|
||||
|
||||
@@ -63,6 +63,21 @@ class HyperP2PAutobaseForkChoice extends EventEmitter {
|
||||
return this._views.get(this._chosen) || null
|
||||
}
|
||||
|
||||
hasView (forkId) {
|
||||
return this._views.has(forkId)
|
||||
}
|
||||
|
||||
removeView (forkId) {
|
||||
const ok = this._views.delete(forkId)
|
||||
if (ok && this._chosen === forkId) this._chosen = null
|
||||
return ok
|
||||
}
|
||||
|
||||
scoreFork (forkId) {
|
||||
const v = this._views.get(forkId)
|
||||
return v ? v.weight : null
|
||||
}
|
||||
|
||||
_onGossip (d) {
|
||||
if (!d || d.type !== 'view-register' || !d.view) return
|
||||
this._stats.gossipIn++
|
||||
|
||||
@@ -36,3 +36,13 @@ test('getStats', async (t) => {
|
||||
t.is(m.getStats().protocol, 'autobase-fork-choice/v1')
|
||||
await m.close()
|
||||
})
|
||||
|
||||
test('hasView removeView scoreFork', async (t) => {
|
||||
const m = new HyperP2PAutobaseForkChoice()
|
||||
m.registerView('f1', 1, { weight: 3 })
|
||||
t.ok(m.hasView('f1'))
|
||||
t.is(m.scoreFork('f1'), 3)
|
||||
t.ok(m.removeView('f1'))
|
||||
t.not(m.hasView('f1'))
|
||||
await m.close()
|
||||
})
|
||||
|
||||
@@ -64,6 +64,22 @@ class HyperP2PAutobaseIndexerBus extends EventEmitter {
|
||||
return this._queue.slice(0, n).map((e) => ({ ...e }))
|
||||
}
|
||||
|
||||
filterByType (type) {
|
||||
assertNonEmpty(type, 'type')
|
||||
return this._queue.filter((e) => e.type === type).map((e) => ({ ...e }))
|
||||
}
|
||||
|
||||
lastEvent () {
|
||||
const e = this._queue[this._queue.length - 1]
|
||||
return e ? { ...e } : null
|
||||
}
|
||||
|
||||
clearEvents () {
|
||||
const n = this._queue.length
|
||||
this._queue = []
|
||||
return n
|
||||
}
|
||||
|
||||
_onGossip (d) {
|
||||
if (!d || d.type !== 'index-event' || !d.evt) return
|
||||
this._stats.gossipIn++
|
||||
|
||||
@@ -37,3 +37,13 @@ test('getStats', async (t) => {
|
||||
t.is(m.getStats().protocol, 'autobase-indexer-bus/v1')
|
||||
await m.close()
|
||||
})
|
||||
|
||||
test('filterByType lastEvent clearEvents', async (t) => {
|
||||
const m = new HyperP2PAutobaseIndexerBus()
|
||||
m.publishIndexEvent('a', { n: 1 })
|
||||
m.publishIndexEvent('b', { n: 2 })
|
||||
t.is(m.filterByType('a').length, 1)
|
||||
t.is(m.lastEvent().type, 'b')
|
||||
t.is(m.clearEvents(), 2)
|
||||
await m.close()
|
||||
})
|
||||
|
||||
@@ -67,6 +67,15 @@ class HyperP2PAutobaseViewSync extends EventEmitter {
|
||||
return Math.max(0, version - this._view.version)
|
||||
}
|
||||
|
||||
viewHash () {
|
||||
return this._view.hash
|
||||
}
|
||||
|
||||
ageMs () {
|
||||
if (!this._view.at) return 0
|
||||
return Math.max(0, Date.now() - this._view.at)
|
||||
}
|
||||
|
||||
_onGossip (d) {
|
||||
if (!d || d.type !== 'view-sync' || !d.view) return
|
||||
this._stats.gossipIn++
|
||||
|
||||
@@ -36,3 +36,13 @@ test('getStats', async (t) => {
|
||||
t.is(m.getStats().protocol, 'autobase-view-sync/v1')
|
||||
await m.close()
|
||||
})
|
||||
|
||||
test('viewHash ageMs behindBy', async (t) => {
|
||||
const m = new HyperP2PAutobaseViewSync()
|
||||
m.publishView(4)
|
||||
t.ok(m.viewHash())
|
||||
t.ok(m.ageMs() >= 0)
|
||||
t.is(m.behindBy(10), 6)
|
||||
t.ok(m.isAtLeast(4))
|
||||
await m.close()
|
||||
})
|
||||
|
||||
@@ -61,6 +61,17 @@ class HyperP2PAutobaseWriterLease extends EventEmitter {
|
||||
return lease.holder === writerId
|
||||
}
|
||||
|
||||
leaseRemainingMs (writerId) {
|
||||
const lease = this._leases.get(writerId)
|
||||
if (!lease) return 0
|
||||
return Math.max(0, lease.expiresAt - Date.now())
|
||||
}
|
||||
|
||||
currentHolder (writerId) {
|
||||
if (!this.hasLease(writerId)) return null
|
||||
return this._leases.get(writerId).holder
|
||||
}
|
||||
|
||||
listActiveLeases () {
|
||||
const now = Date.now()
|
||||
const out = []
|
||||
|
||||
@@ -36,3 +36,11 @@ test('getStats', async (t) => {
|
||||
t.is(m.getStats().protocol, 'autobase-writer-lease/v1')
|
||||
await m.close()
|
||||
})
|
||||
|
||||
test('leaseRemainingMs currentHolder', async (t) => {
|
||||
const m = new HyperP2PAutobaseWriterLease()
|
||||
m.acquireLease('w1', 60000)
|
||||
t.ok(m.leaseRemainingMs('w1') > 0)
|
||||
t.is(m.currentHolder('w1'), 'w1')
|
||||
await m.close()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user