feat(routing,collab,trust): manually expand sticky-session and collab modules
Hyper-P2P Module Tests / unit-all (push) Has been cancelled

- sticky-session: unbind, listSessions, bindBatch, remote remove gossip
- cursor-presence: peersInDoc, pruneStale
- whiteboard-op: listRooms, opCount, pruneRoom
- key-rotation: listActiveKeys, revoke, tick activation sweep

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-21 02:19:01 -04:00
co-authored by Cursor
parent cd048cb8c8
commit 3de911fc2e
4 changed files with 83 additions and 2 deletions
@@ -48,6 +48,22 @@ class HyperP2PCursorPresence extends EventEmitter {
return [...this._cursors.values()].filter((c) => c.docId === docId) return [...this._cursors.values()].filter((c) => c.docId === docId)
} }
peersInDoc (docId) {
return [...new Set(this.listCursors(docId).map((c) => c.peer))]
}
pruneStale (maxAgeMs = 60000) {
const cutoff = Date.now() - maxAgeMs
let n = 0
for (const [key, c] of this._cursors) {
if ((c.at || 0) < cutoff) {
this._cursors.delete(key)
n++
}
}
return n
}
removeCursor (docId, peerHex = this.peerHex) { removeCursor (docId, peerHex = this.peerHex) {
const key = `${docId}:${peerHex}` const key = `${docId}:${peerHex}`
const ok = this._cursors.delete(key) const ok = this._cursors.delete(key)
@@ -53,6 +53,24 @@ class HyperP2PWhiteboardOp extends EventEmitter {
return entries.length ? entries[0].seq : -1 return entries.length ? entries[0].seq : -1
} }
listRooms () {
return [...new Set(this._log.map((e) => e.roomId))]
}
opCount (roomId = null) {
if (roomId) return this.history(roomId, Infinity).length
return this._log.length
}
pruneRoom (roomId, keep = 500) {
assertNonEmpty(roomId, 'roomId')
const roomOps = this._log.filter((e) => e.roomId === roomId)
if (roomOps.length <= keep) return 0
const drop = roomOps.length - keep
this._log = this._log.filter((e) => e.roomId !== roomId).concat(roomOps.slice(-keep))
return drop
}
mergeRemote (entry) { mergeRemote (entry) {
if (!entry || !entry.roomId) return false if (!entry || !entry.roomId) return false
this._stats.gossipIn++ this._stats.gossipIn++
@@ -32,9 +32,40 @@ class HyperP2PStickySession extends EventEmitter {
return this._bindings.get(sessionId) || null return this._bindings.get(sessionId) || null
} }
unbind (sessionId) {
assertNonEmpty(sessionId, 'sessionId')
const ok = this._bindings.delete(sessionId)
if (ok) {
gossipSend(this, { type: 'sticky-session-remove', sessionId })
this._stats.gossipOut++
}
return ok
}
hasSession (sessionId) {
return this._bindings.has(sessionId)
}
listSessions () {
return [...this._bindings.entries()].map(([sessionId, peerId]) => ({ sessionId, peerId }))
}
bindBatch (pairs) {
if (!Array.isArray(pairs)) throw new Error('pairs must be an array')
let n = 0
for (const p of pairs) {
if (p && this.bind(p.sessionId, p.peerId)) n++
}
return n
}
_onGossip (d) { _onGossip (d) {
if (!d || d.type !== 'sticky-session-sync' || !d.sessionId) return if (!d || d.type !== 'sticky-session-sync' || !d.sessionId) return
this._stats.gossipIn++ this._stats.gossipIn++
if (d.type === 'sticky-session-remove') {
this._bindings.delete(d.sessionId)
return
}
if (d.peerId) this._bindings.set(d.sessionId, d.peerId) if (d.peerId) this._bindings.set(d.sessionId, d.peerId)
} }
+18 -2
View File
@@ -49,9 +49,25 @@ class HyperP2PKeyRotation extends EventEmitter {
return out return out
} }
_maybeActivate (keyId) { listActiveKeys () {
return [...this._active.keys()]
}
revoke (keyId) {
assertNonEmpty(keyId, 'keyId')
const ok = this._active.delete(keyId)
this._pending.delete(keyId)
if (ok) this.emit('revoked', { keyId })
return ok
}
tick (now = Date.now()) {
for (const keyId of this._pending.keys()) this._maybeActivate(keyId, now)
return this._active.size
}
_maybeActivate (keyId, now = Date.now()) {
const list = this._pending.get(keyId) || [] const list = this._pending.get(keyId) || []
const now = Date.now()
for (const e of list) { for (const e of list) {
if (e.activateAt <= now) { if (e.activateAt <= now) {
const active = { keyId, material: e.newMaterial, activatedAt: now } const active = { keyId, material: e.newMaterial, activatedAt: now }