Expand media, network-stack, pear-platform, and scheduling module APIs
Add batch helpers, clear/count utilities, and management methods across content protection, FEC, flow shaper, circuit loom, trust gate, peer scheduler, and related modules. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -94,6 +94,16 @@ class HyperP2PMessageEnvelope extends EventEmitter {
|
|||||||
return { ...envelope, payload, at: envelope.at || Date.now() }
|
return { ...envelope, payload, at: envelope.at || Date.now() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrapBatch (payloads, opts = {}) {
|
||||||
|
if (!Array.isArray(payloads)) throw new Error('payloads must be an array')
|
||||||
|
return payloads.map((p) => this.wrap(p, opts))
|
||||||
|
}
|
||||||
|
|
||||||
|
unwrapBatch (envelopes) {
|
||||||
|
if (!Array.isArray(envelopes)) throw new Error('envelopes must be an array')
|
||||||
|
return envelopes.map((e) => this.unwrap(e))
|
||||||
|
}
|
||||||
|
|
||||||
resetStats () {
|
resetStats () {
|
||||||
this._stats = { wrapped: 0, unwrapped: 0, encoded: 0, decoded: 0, failed: 0 }
|
this._stats = { wrapped: 0, unwrapped: 0, encoded: 0, decoded: 0, failed: 0 }
|
||||||
return this._stats
|
return this._stats
|
||||||
|
|||||||
@@ -124,6 +124,15 @@ class HyperP2PSlaBudget extends EventEmitter {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceCount () {
|
||||||
|
return this._services.size
|
||||||
|
}
|
||||||
|
|
||||||
|
allocateBatch (allocations) {
|
||||||
|
if (!Array.isArray(allocations)) throw new Error('allocations must be an array')
|
||||||
|
return allocations.map((a) => this.allocate(a.service, a.amount))
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return {
|
return {
|
||||||
...this._stats,
|
...this._stats,
|
||||||
|
|||||||
@@ -81,6 +81,19 @@ class HyperP2PContentProtection extends EventEmitter {
|
|||||||
return payloads.map((p) => this.encryptSegment(p, keyId))
|
return payloads.map((p) => this.encryptSegment(p, keyId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decryptBatch (segments, keyId = null) {
|
||||||
|
if (!Array.isArray(segments)) throw new Error('segments array required')
|
||||||
|
return segments.map((s) => this.decryptSegment(s.data || s, keyId))
|
||||||
|
}
|
||||||
|
|
||||||
|
hasKey (keyId) {
|
||||||
|
return this._keys.has(String(keyId))
|
||||||
|
}
|
||||||
|
|
||||||
|
keyCount () {
|
||||||
|
return this._keys.size
|
||||||
|
}
|
||||||
|
|
||||||
clearKeys () {
|
clearKeys () {
|
||||||
const n = this._keys.size
|
const n = this._keys.size
|
||||||
this._keys.clear()
|
this._keys.clear()
|
||||||
|
|||||||
@@ -93,6 +93,16 @@ class HyperP2PFecVideo extends EventEmitter {
|
|||||||
return entries.map((e) => this.encodeGroup(e.groupId, e.shards))
|
return entries.map((e) => this.encodeGroup(e.groupId, e.shards))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearAll () {
|
||||||
|
const n = this._groups.size
|
||||||
|
this._groups.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
groupCount () {
|
||||||
|
return this._groups.size
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return mediaStats(this._stats, PROTOCOL, { groups: this._groups.size })
|
return mediaStats(this._stats, PROTOCOL, { groups: this._groups.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,15 @@ class HyperP2PLatencyOptimizer extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pathCount () {
|
||||||
|
return this._paths.size
|
||||||
|
}
|
||||||
|
|
||||||
|
measureBatch (entries) {
|
||||||
|
if (!Array.isArray(entries)) throw new Error('entries must be an array')
|
||||||
|
return entries.map((e) => this.measurePath(e.pathId, e.hops || []))
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return mediaStats(this._stats, PROTOCOL, { paths: this._paths.size })
|
return mediaStats(this._stats, PROTOCOL, { paths: this._paths.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,16 @@ class HyperP2POriginHybridBridge extends EventEmitter {
|
|||||||
return this._stats.originBytes + this._stats.p2pBytes
|
return this._stats.originBytes + this._stats.p2pBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearStream (streamId) {
|
||||||
|
return this._origins.delete(assertStreamId(streamId))
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAll () {
|
||||||
|
const n = this._origins.size
|
||||||
|
this._origins.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return mediaStats(this._stats, PROTOCOL, {
|
return mediaStats(this._stats, PROTOCOL, {
|
||||||
offloadRatio: this.p2pOffloadRatio(),
|
offloadRatio: this.p2pOffloadRatio(),
|
||||||
|
|||||||
@@ -94,6 +94,17 @@ class HyperP2PQosTopic extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlerCount () {
|
||||||
|
return this._handlers.size
|
||||||
|
}
|
||||||
|
|
||||||
|
peekHighest () {
|
||||||
|
for (let q = MAX_QOS; q >= 0; q--) {
|
||||||
|
if (this._queues[q].length) return { ...this._queues[q][0] }
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
_onGossip (data) {
|
_onGossip (data) {
|
||||||
if (!data || data.type !== 'qos-publish') return
|
if (!data || data.type !== 'qos-publish') return
|
||||||
this._stats.gossipIn++
|
this._stats.gossipIn++
|
||||||
|
|||||||
@@ -87,6 +87,15 @@ class HyperP2PStreamChunker extends EventEmitter {
|
|||||||
return { tail, data: out }
|
return { tail, data: out }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pendingBytes () {
|
||||||
|
return this._pending.length
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBatch (chunks) {
|
||||||
|
if (!Array.isArray(chunks)) throw new Error('chunks must be an array')
|
||||||
|
return chunks.map((c) => this.write(c))
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return {
|
return {
|
||||||
...this._stats,
|
...this._stats,
|
||||||
|
|||||||
@@ -98,6 +98,15 @@ class HyperP2PCircuitLoom extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
circuitCount () {
|
||||||
|
return this._circuits.size
|
||||||
|
}
|
||||||
|
|
||||||
|
hopCount (circuitId) {
|
||||||
|
const c = this._circuits.get(circuitId)
|
||||||
|
return c ? c.hops.length : 0
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return { ...this._stats, open: this.openCount(), total: this._circuits.size, protocol: PROTOCOL }
|
return { ...this._stats, open: this.openCount(), total: this._circuits.size, protocol: PROTOCOL }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,23 @@ class HyperP2PFlowShaper extends EventEmitter {
|
|||||||
for (const p of PRIOS) this._tokens[p] = Math.min(this._rates[p], this._tokens[p] + Math.floor(this._rates[p] / 10))
|
for (const p of PRIOS) this._tokens[p] = Math.min(this._rates[p], this._tokens[p] + Math.floor(this._rates[p] / 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearQueues () {
|
||||||
|
let n = 0
|
||||||
|
for (const p of PRIOS) {
|
||||||
|
n += this._queues[p].length
|
||||||
|
this._queues[p] = []
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
setRate (priority, rate) {
|
||||||
|
if (!PRIOS.includes(priority)) throw new Error('priority must be control|data|bulk')
|
||||||
|
if (rate < 0) throw new Error('rate must be non-negative')
|
||||||
|
this._rates[priority] = rate
|
||||||
|
this._tokens[priority] = rate
|
||||||
|
return rate
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return {
|
return {
|
||||||
...this._stats,
|
...this._stats,
|
||||||
|
|||||||
@@ -82,6 +82,16 @@ class HyperP2PSecretStreamPair {
|
|||||||
this._stats = { pairs: 0 }
|
this._stats = { pairs: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publicKeyHex () {
|
||||||
|
return require('b4a').toString(this.keyPair.publicKey, 'hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateKeyPair () {
|
||||||
|
this.destroyActive()
|
||||||
|
this.keyPair = hypercoreCrypto.keyPair()
|
||||||
|
return this.keyPair
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return { ...this._stats, protocol: PROTOCOL }
|
return { ...this._stats, protocol: PROTOCOL }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,20 @@ class HyperP2PHealthProbe extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reportBatch (entries) {
|
||||||
|
if (!Array.isArray(entries)) throw new Error('entries must be an array')
|
||||||
|
return entries.map((e) => this.report(e.peerId, e.status || e))
|
||||||
|
}
|
||||||
|
|
||||||
|
isHealthy (peerId) {
|
||||||
|
const r = this.get(peerId)
|
||||||
|
return !!(r && r.ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
peerCount () {
|
||||||
|
return this._reports.size
|
||||||
|
}
|
||||||
|
|
||||||
_onGossip (data) {
|
_onGossip (data) {
|
||||||
if (!data || data.type !== 'health' || !data.entry) return
|
if (!data || data.type !== 'health' || !data.entry) return
|
||||||
this._stats.gossipIn++
|
this._stats.gossipIn++
|
||||||
|
|||||||
@@ -85,6 +85,16 @@ class HyperBareAddonResolve extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerBatch (entries) {
|
||||||
|
if (!Array.isArray(entries)) throw new Error('entries must be an array')
|
||||||
|
return entries.map((e) => this.register(e.name, e.spec || e))
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveBatch (names) {
|
||||||
|
if (!Array.isArray(names)) throw new Error('names must be an array')
|
||||||
|
return names.map((n) => this.resolve(n))
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, {
|
return platformStats(this._stats, PROTOCOL, {
|
||||||
addons: this._addons.size,
|
addons: this._addons.size,
|
||||||
|
|||||||
@@ -68,6 +68,15 @@ class HyperBareBundleBridge extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveBatch (ids) {
|
||||||
|
if (!Array.isArray(ids)) throw new Error('ids must be an array')
|
||||||
|
return ids.map((id) => this.resolve(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
bundleCount () {
|
||||||
|
return this._bundles.size
|
||||||
|
}
|
||||||
|
|
||||||
_gossip (data) {
|
_gossip (data) {
|
||||||
if (!this._peerMsgs) return
|
if (!this._peerMsgs) return
|
||||||
gossipSend(this, data)
|
gossipSend(this, data)
|
||||||
|
|||||||
@@ -92,6 +92,16 @@ class HyperBareTargetMatrix extends EventEmitter {
|
|||||||
return this._targets.some((t) => t.id === id)
|
return this._targets.some((t) => t.id === id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetToDefaults () {
|
||||||
|
this._targets = [...DEFAULT_TARGETS]
|
||||||
|
this.host = this._targets[0]?.id || 'darwin-arm64'
|
||||||
|
return this.list()
|
||||||
|
}
|
||||||
|
|
||||||
|
targetCount () {
|
||||||
|
return this._targets.length
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, {
|
return platformStats(this._stats, PROTOCOL, {
|
||||||
host: this.host,
|
host: this.host,
|
||||||
|
|||||||
@@ -122,6 +122,21 @@ class HyperPearPreflightSync extends EventEmitter {
|
|||||||
return [...this._jobs.keys()]
|
return [...this._jobs.keys()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failTimedOut () {
|
||||||
|
let n = 0
|
||||||
|
for (const job of this.listPending()) {
|
||||||
|
if (this.timedOut(job.id)) {
|
||||||
|
this.fail(job.id, 'warmup timeout')
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
jobCount () {
|
||||||
|
return this._jobs.size
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, {
|
return platformStats(this._stats, PROTOCOL, {
|
||||||
jobs: this._jobs.size,
|
jobs: this._jobs.size,
|
||||||
|
|||||||
@@ -94,6 +94,16 @@ class HyperPearTrustGate extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkBatch (keys, autoTrust = false) {
|
||||||
|
if (!Array.isArray(keys)) throw new Error('keys must be an array')
|
||||||
|
return keys.map((k) => this.check(k, autoTrust))
|
||||||
|
}
|
||||||
|
|
||||||
|
trustBatch (keys) {
|
||||||
|
if (!Array.isArray(keys)) throw new Error('keys must be an array')
|
||||||
|
return keys.map((k) => this.trust(k))
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return platformStats(this._stats, PROTOCOL, { trusted: this._trusted.size })
|
return platformStats(this._stats, PROTOCOL, { trusted: this._trusted.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,6 +123,24 @@ class HyperP2PCircuitBreaker extends EventEmitter {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failuresFor (id) {
|
||||||
|
const c = this._circuits.get(id)
|
||||||
|
return c ? c.failures : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure (id) {
|
||||||
|
if (!this._circuits.has(id)) {
|
||||||
|
this._circuits.set(id, {
|
||||||
|
state: 'closed',
|
||||||
|
failures: 0,
|
||||||
|
successes: 0,
|
||||||
|
openedAt: 0,
|
||||||
|
halfOpenTrials: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return this.getState(id)
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return {
|
return {
|
||||||
...this._stats,
|
...this._stats,
|
||||||
|
|||||||
@@ -70,6 +70,15 @@ class HyperP2PPeerScheduler extends EventEmitter {
|
|||||||
return ids.length
|
return ids.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobIds () {
|
||||||
|
return [...this._jobs.keys()]
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleBatch (entries) {
|
||||||
|
if (!Array.isArray(entries)) throw new Error('entries must be an array')
|
||||||
|
return entries.map((e) => this.schedule(e.expr, e.id, { shard: e.shard }))
|
||||||
|
}
|
||||||
|
|
||||||
msUntil (jobId, now = Date.now()) {
|
msUntil (jobId, now = Date.now()) {
|
||||||
const job = this._jobs.get(jobId)
|
const job = this._jobs.get(jobId)
|
||||||
if (!job) return null
|
if (!job) return null
|
||||||
|
|||||||
@@ -87,6 +87,14 @@ class HyperP2PKeyRotation extends EventEmitter {
|
|||||||
return list.reduce((a, b) => (a.activateAt < b.activateAt ? a : b))
|
return list.reduce((a, b) => (a.activateAt < b.activateAt ? a : b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getActive (keyId) {
|
||||||
|
return this._active.get(String(keyId)) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingKeyIds () {
|
||||||
|
return [...this._pending.keys()]
|
||||||
|
}
|
||||||
|
|
||||||
_maybeActivate (keyId, now = Date.now()) {
|
_maybeActivate (keyId, now = Date.now()) {
|
||||||
const list = this._pending.get(keyId) || []
|
const list = this._pending.get(keyId) || []
|
||||||
for (const e of list) {
|
for (const e of list) {
|
||||||
|
|||||||
Reference in New Issue
Block a user