feat(media,messaging,network): deepen streaming and transport APIs
Hyper-P2P Module Tests / unit-all (push) Failing after 1h16m23s
Hyper-P2P Module Tests / unit-all (push) Failing after 1h16m23s
Origin hybrid weighted select, latency path stats, live-edge listing; topic channel batch publish and retained clear; wakeup reschedule and cancelAll; electron bridge handler registry helpers. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -57,6 +57,21 @@ class HyperP2PLatencyOptimizer extends EventEmitter {
|
|||||||
return [...this._paths.keys()]
|
return [...this._paths.keys()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearPath (pathId) {
|
||||||
|
return this._paths.delete(String(pathId))
|
||||||
|
}
|
||||||
|
|
||||||
|
averageRtt () {
|
||||||
|
const vals = [...this._paths.values()]
|
||||||
|
if (!vals.length) return 0
|
||||||
|
return vals.reduce((s, p) => s + p.rttMs, 0) / vals.length
|
||||||
|
}
|
||||||
|
|
||||||
|
isWithinTarget (plan) {
|
||||||
|
const glass = plan?.estimatedGlassMs ?? this.averageRtt() + 200
|
||||||
|
return glass <= this.targetGlassMs
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return mediaStats(this._stats, PROTOCOL, { paths: this._paths.size })
|
return mediaStats(this._stats, PROTOCOL, { paths: this._paths.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,23 @@ class HyperP2PLiveEdgeManager extends EventEmitter {
|
|||||||
return this.setLiveEdge(streamId, e.seq + (delta | 0), Date.now())
|
return this.setLiveEdge(streamId, e.seq + (delta | 0), Date.now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeEdge (streamId) {
|
||||||
|
return this._edges.delete(assertStreamId(streamId))
|
||||||
|
}
|
||||||
|
|
||||||
|
listEdges () {
|
||||||
|
return [...this._edges.values()]
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLagAcrossStreams (now = Date.now()) {
|
||||||
|
let max = 0
|
||||||
|
for (const sid of this._edges.keys()) {
|
||||||
|
const lag = this.edgeLagMs(sid, now)
|
||||||
|
if (lag != null && lag > max) max = lag
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
getStats () {
|
getStats () {
|
||||||
return mediaStats(this._stats, PROTOCOL, { streams: this._edges.size })
|
return mediaStats(this._stats, PROTOCOL, { streams: this._edges.size })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,33 @@ class HyperP2POriginHybridBridge extends EventEmitter {
|
|||||||
return [...(this._origins.get(assertStreamId(streamId)) || [])]
|
return [...(this._origins.get(assertStreamId(streamId)) || [])]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeOrigin (streamId, url) {
|
||||||
|
const sid = assertStreamId(streamId)
|
||||||
|
const list = this._origins.get(sid)
|
||||||
|
if (!list) return false
|
||||||
|
const idx = list.findIndex((o) => o.url === url)
|
||||||
|
if (idx < 0) return false
|
||||||
|
list.splice(idx, 1)
|
||||||
|
if (!list.length) this._origins.delete(sid)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
listStreamIds () {
|
||||||
|
return [...this._origins.keys()]
|
||||||
|
}
|
||||||
|
|
||||||
|
weightedSelect (streamId) {
|
||||||
|
const list = this._origins.get(assertStreamId(streamId))
|
||||||
|
if (!list || !list.length) return null
|
||||||
|
const total = list.reduce((s, o) => s + o.weight, 0)
|
||||||
|
let r = Math.random() * total
|
||||||
|
for (const o of list) {
|
||||||
|
r -= o.weight
|
||||||
|
if (r <= 0) return o
|
||||||
|
}
|
||||||
|
return list[0]
|
||||||
|
}
|
||||||
|
|
||||||
totalBytes () {
|
totalBytes () {
|
||||||
return this._stats.originBytes + this._stats.p2pBytes
|
return this._stats.originBytes + this._stats.p2pBytes
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,23 @@ class HyperP2PTopicChannel extends EventEmitter {
|
|||||||
|
|
||||||
retainedCount () { return this._retained.size }
|
retainedCount () { return this._retained.size }
|
||||||
|
|
||||||
|
clearRetained (channel) {
|
||||||
|
if (channel) return this._retained.delete(String(channel))
|
||||||
|
const n = this._retained.size
|
||||||
|
this._retained.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
publishBatch (channel, payloads) {
|
||||||
|
assertNonEmpty(channel, 'channel')
|
||||||
|
if (!Array.isArray(payloads)) throw new Error('payloads array required')
|
||||||
|
return payloads.map((p) => this.publish(channel, p))
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriberCount () {
|
||||||
|
return this._subs.size
|
||||||
|
}
|
||||||
|
|
||||||
_deliverLocal (channel, payload, meta) {
|
_deliverLocal (channel, payload, meta) {
|
||||||
const handler = this._subs.get(channel)
|
const handler = this._subs.get(channel)
|
||||||
if (handler) {
|
if (handler) {
|
||||||
|
|||||||
@@ -55,6 +55,26 @@ class HyperP2PWakeupChannel extends EventEmitter {
|
|||||||
.sort((a, b) => a.at - b.at)
|
.sort((a, b) => a.at - b.at)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextWakeup () {
|
||||||
|
const list = this.pending()
|
||||||
|
return list.length ? list[0] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPending (peerId) {
|
||||||
|
return this._pending.has(String(peerId))
|
||||||
|
}
|
||||||
|
|
||||||
|
reschedule (peerId, at) {
|
||||||
|
if (!this.hasPending(peerId)) return null
|
||||||
|
return this.scheduleWakeup(peerId, at)
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelAll () {
|
||||||
|
const ids = [...this._pending.keys()]
|
||||||
|
for (const id of ids) this.cancel(id)
|
||||||
|
return ids.length
|
||||||
|
}
|
||||||
|
|
||||||
cancel (peerId, opts = {}) {
|
cancel (peerId, opts = {}) {
|
||||||
assertNonEmpty(peerId, 'peerId')
|
assertNonEmpty(peerId, 'peerId')
|
||||||
const had = this._pending.has(peerId)
|
const had = this._pending.has(peerId)
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ class HyperPearElectronBridge extends EventEmitter {
|
|||||||
return [...this._channels.keys()]
|
return [...this._channels.keys()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unregisterAll () {
|
||||||
|
const n = this._channels.size
|
||||||
|
this._channels.clear()
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerCount () {
|
||||||
|
return this._channels.size
|
||||||
|
}
|
||||||
|
|
||||||
invoke (name, payload) {
|
invoke (name, payload) {
|
||||||
const h = this._channels.get(assertId(name))
|
const h = this._channels.get(assertId(name))
|
||||||
if (!h) throw new Error(`ipc not registered: ${name}`)
|
if (!h) throw new Error(`ipc not registered: ${name}`)
|
||||||
|
|||||||
Reference in New Issue
Block a user