Expand pear, encoding, consensus, supercomputer, and CRDT modules
Add manual API helpers across categories: pear link resolver cache, applink reload, storage layout defaults, compact codec decodeBatch, schema allValid/getSchema, session bridge removePair, udx sendRecvRatio, cluster affinity and cpu-share clearAll, raft truncateLog/currentTerm, quorum vote tally, silence and whisper seen helpers, qos unsubscribeAll, LWW register clearAll, peer selector topRegions, graph hasPath, fork choice clearViews, and trust gate untrustAll. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -161,6 +161,23 @@ class HyperP2PQuorumPool extends EventEmitter {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasVoted (proposalId, voterId) {
|
||||||
|
const votes = this._votes.get(proposalId)
|
||||||
|
return votes ? votes.has(voterId) : false
|
||||||
|
}
|
||||||
|
|
||||||
|
voteTally (proposalId) {
|
||||||
|
const votes = this._votes.get(proposalId)
|
||||||
|
if (!votes) return { yes: 0, no: 0, total: 0 }
|
||||||
|
let yes = 0
|
||||||
|
let no = 0
|
||||||
|
for (const accept of votes.values()) {
|
||||||
|
if (accept) yes++
|
||||||
|
else no++
|
||||||
|
}
|
||||||
|
return { yes, no, total: yes + no }
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
const open = this.openProposals().length
|
const open = this.openProposals().length
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -52,6 +52,18 @@ class HyperP2PRaftLite extends EventEmitter {
|
|||||||
return this._commitIndex
|
return this._commitIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
truncateLog (keep = 0) {
|
||||||
|
const n = Math.max(0, keep | 0)
|
||||||
|
const removed = this._log.length - n
|
||||||
|
if (removed > 0) this._log = this._log.slice(-n)
|
||||||
|
if (this._commitIndex >= this._log.length) this._commitIndex = this._log.length - 1
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTerm () {
|
||||||
|
return this._currentTerm
|
||||||
|
}
|
||||||
|
|
||||||
role () {
|
role () {
|
||||||
return this._role
|
return this._role
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,16 @@ class HyperP2PSessionBridge extends EventEmitter {
|
|||||||
|
|
||||||
pairCount () { return this._tokens.size }
|
pairCount () { return this._tokens.size }
|
||||||
|
|
||||||
|
async removePair (token) {
|
||||||
|
const entry = this._tokens.get(token)
|
||||||
|
if (!entry) return false
|
||||||
|
try { await entry.initiator.destroy() } catch (_) {}
|
||||||
|
try { await entry.responder.destroy() } catch (_) {}
|
||||||
|
this._tokens.delete(token)
|
||||||
|
this.emit('pair-removed', { token })
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
if (this.swarm || !this.topic) return this
|
if (this.swarm || !this.topic) return this
|
||||||
await initModuleSwarm(this, {
|
await initModuleSwarm(this, {
|
||||||
|
|||||||
@@ -50,6 +50,15 @@ class HyperP2PCompactCodecBridge extends EventEmitter {
|
|||||||
return values.map((v) => this.encode(codecId, v))
|
return values.map((v) => this.encode(codecId, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decodeBatch (codecId, buffers) {
|
||||||
|
if (!Array.isArray(buffers)) throw new Error('buffers must be an array')
|
||||||
|
return buffers.map((b) => this.decode(codecId, b))
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCodec (codecId) {
|
||||||
|
return this.registry.listCodecs().includes(codecId)
|
||||||
|
}
|
||||||
|
|
||||||
listCodecs () {
|
listCodecs () {
|
||||||
return this.registry.listCodecs()
|
return this.registry.listCodecs()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,15 @@ class HyperP2PSchemaValidator extends EventEmitter {
|
|||||||
return values.map((v) => this.validate(name, v))
|
return values.map((v) => this.validate(name, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allValid (name, values) {
|
||||||
|
return this.validateBatch(name, values).every((r) => r.ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchema (name) {
|
||||||
|
const s = this._schemas.get(name)
|
||||||
|
return s ? { ...s } : null
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return { ...this._stats, schemas: this._schemas.size, protocol: PROTOCOL }
|
return { ...this._stats, schemas: this._schemas.size, protocol: PROTOCOL }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,16 @@ class HyperP2PSilenceProtocol extends EventEmitter {
|
|||||||
return this._absent.delete(String(id))
|
return this._absent.delete(String(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seenCount () {
|
||||||
|
return this._seen.size
|
||||||
|
}
|
||||||
|
|
||||||
|
markSeenBatch (ids) {
|
||||||
|
if (!Array.isArray(ids)) throw new Error('ids array required')
|
||||||
|
for (const id of ids) this.markSeen(id)
|
||||||
|
return ids.length
|
||||||
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
if (this.swarm || !this.topic) return this
|
if (this.swarm || !this.topic) return this
|
||||||
await initModuleSwarm(this, {
|
await initModuleSwarm(this, {
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ class HyperP2PWhisperMesh extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSeen (id) {
|
||||||
|
return this._seen.has(String(id))
|
||||||
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
if (this.swarm || !this.topic) return this
|
if (this.swarm || !this.topic) return this
|
||||||
await initModuleSwarm(this, {
|
await initModuleSwarm(this, {
|
||||||
|
|||||||
@@ -128,6 +128,10 @@ class HyperP2PGraphIndex extends EventEmitter {
|
|||||||
return this.bfs(start, depth).order.length
|
return this.bfs(start, depth).order.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasPath (from, to) {
|
||||||
|
return this.shortestPath(from, to) != null
|
||||||
|
}
|
||||||
|
|
||||||
toJSON () {
|
toJSON () {
|
||||||
const edges = []
|
const edges = []
|
||||||
for (const [from, list] of this._adj) {
|
for (const [from, list] of this._adj) {
|
||||||
|
|||||||
@@ -100,6 +100,18 @@ class HyperP2PPeerSelectorStreaming extends EventEmitter {
|
|||||||
return this._peers.size
|
return this._peers.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topRegions (limit = 5) {
|
||||||
|
const counts = new Map()
|
||||||
|
for (const p of this._peers.values()) {
|
||||||
|
const r = p.region || 'unknown'
|
||||||
|
counts.set(r, (counts.get(r) || 0) + 1)
|
||||||
|
}
|
||||||
|
return [...counts.entries()]
|
||||||
|
.sort((a, b) => b[1] - a[1])
|
||||||
|
.slice(0, Math.max(0, limit | 0))
|
||||||
|
.map(([region, count]) => ({ region, count }))
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return mediaStats(this._stats, PROTOCOL, { peers: this._peers.size })
|
return mediaStats(this._stats, PROTOCOL, { peers: this._peers.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,12 @@ class HyperP2PQosTopic extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsubscribeAll () {
|
||||||
|
const n = this._handlers.size
|
||||||
|
this._handlers.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
_onGossip (data) {
|
_onGossip (data) {
|
||||||
if (!data || data.type !== 'qos-publish') return
|
if (!data || data.type !== 'qos-publish') return
|
||||||
this._stats.gossipIn++
|
this._stats.gossipIn++
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ class HyperP2PSecretStreamPair {
|
|||||||
return !!this._pair
|
return !!this._pair
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getActivePair () {
|
||||||
|
return this._pair
|
||||||
|
}
|
||||||
|
|
||||||
pairCount () {
|
pairCount () {
|
||||||
return this._stats.pairs
|
return this._stats.pairs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ class HyperP2PUdxMetrics extends EventEmitter {
|
|||||||
return this.windowMs
|
return this.windowMs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendRecvRatio () {
|
||||||
|
const r = this.rates()
|
||||||
|
if (!r.recvBps) return r.sendBps > 0 ? Infinity : 0
|
||||||
|
return r.sendBps / r.recvBps
|
||||||
|
}
|
||||||
|
|
||||||
_trimWindow (samples, now) {
|
_trimWindow (samples, now) {
|
||||||
const cutoff = now - this.windowMs
|
const cutoff = now - this.windowMs
|
||||||
while (samples.length && samples[0].at < cutoff) samples.shift()
|
while (samples.length && samples[0].at < cutoff) samples.shift()
|
||||||
|
|||||||
@@ -74,6 +74,15 @@ class HyperPearApplinkConfig extends EventEmitter {
|
|||||||
return this._config ? JSON.parse(JSON.stringify(this._config)) : null
|
return this._config ? JSON.parse(JSON.stringify(this._config)) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applinkHref () {
|
||||||
|
return this._config ? this._config.applink : null
|
||||||
|
}
|
||||||
|
|
||||||
|
reload (pkg) {
|
||||||
|
this._config = null
|
||||||
|
return this.load(pkg)
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, { loaded: !!this._config })
|
return platformStats(this._stats, PROTOCOL, { loaded: !!this._config })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,16 @@ class HyperPearLinkResolver extends EventEmitter {
|
|||||||
return this.parse(href)
|
return this.parse(href)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withoutFork (parts) {
|
||||||
|
const p = { ...parts }
|
||||||
|
delete p.fork
|
||||||
|
return this.serialize(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheSize () {
|
||||||
|
return this._cache.size
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, { cached: this._cache.size })
|
return platformStats(this._stats, PROTOCOL, { cached: this._cache.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,15 @@ class HyperPearStorageLayout extends EventEmitter {
|
|||||||
return Object.fromEntries(this._apps)
|
return Object.fromEntries(this._apps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDefaultApp (appKey) {
|
||||||
|
this.defaultApp = assertId(appKey, 'appKey')
|
||||||
|
return this.defaultApp
|
||||||
|
}
|
||||||
|
|
||||||
|
appCount () {
|
||||||
|
return this._apps.size
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, { apps: this._apps.size })
|
return platformStats(this._stats, PROTOCOL, { apps: this._apps.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ class HyperPearTrustGate extends EventEmitter {
|
|||||||
return this._trusted.delete(String(key))
|
return this._trusted.delete(String(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
untrustAll () {
|
||||||
|
const n = this._trusted.size
|
||||||
|
this._trusted.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
check (key, autoTrust = false) {
|
check (key, autoTrust = false) {
|
||||||
const k = String(key)
|
const k = String(key)
|
||||||
this._stats.checks++
|
this._stats.checks++
|
||||||
|
|||||||
@@ -92,6 +92,16 @@ class HyperP2PCrdtLwwRegister extends EventEmitter {
|
|||||||
return ts - e.ts
|
return ts - e.ts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearAll () {
|
||||||
|
const n = this._values.size
|
||||||
|
this._values.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
liveCount () {
|
||||||
|
return this.keys().length
|
||||||
|
}
|
||||||
|
|
||||||
_onGossip (d) {
|
_onGossip (d) {
|
||||||
if (!d || d.type !== 'crdt-lww-register-sync' || !d.key) return
|
if (!d || d.type !== 'crdt-lww-register-sync' || !d.key) return
|
||||||
this._stats.gossipIn++
|
this._stats.gossipIn++
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ class HyperP2PAutobaseForkChoice extends EventEmitter {
|
|||||||
return this._views.get(forkId)
|
return this._views.get(forkId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearViews () {
|
||||||
|
const n = this._views.size
|
||||||
|
this._views.clear()
|
||||||
|
this._chosen = null
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
_onGossip (d) {
|
_onGossip (d) {
|
||||||
if (!d || d.type !== 'view-register' || !d.view) return
|
if (!d || d.type !== 'view-register' || !d.view) return
|
||||||
this._stats.gossipIn++
|
this._stats.gossipIn++
|
||||||
|
|||||||
@@ -92,6 +92,17 @@ class HyperP2PClusterAffinity extends EventEmitter {
|
|||||||
return this.tagPeer(id, merged, this._latency.get(id))
|
return this.tagPeer(id, merged, this._latency.get(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearAll () {
|
||||||
|
const n = this._tags.size
|
||||||
|
this._tags.clear()
|
||||||
|
this._latency.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
peersWithTag (tag) {
|
||||||
|
return this.listTagged().filter((r) => r.tags.includes(tag)).map((r) => r.peerId)
|
||||||
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
if (this.swarm || !this.topic) return this
|
if (this.swarm || !this.topic) return this
|
||||||
await initModuleSwarm(this, {
|
await initModuleSwarm(this, {
|
||||||
|
|||||||
@@ -76,6 +76,12 @@ class HyperP2PCpuShare extends EventEmitter {
|
|||||||
return had
|
return had
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearAll () {
|
||||||
|
const n = this._balances.size
|
||||||
|
this._balances.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
if (this.swarm || !this.topic) return this
|
if (this.swarm || !this.topic) return this
|
||||||
await initModuleSwarm(this, {
|
await initModuleSwarm(this, {
|
||||||
|
|||||||
Reference in New Issue
Block a user