Phase 747: extract sparse QoS, contacts presence, voice mesh mixins
Non-breaking refactor: move channel core key filtering, sparse ack defer policy, guild sync ack gossip, debounced contacts presence reconcile, and voice speaking/mute mesh state helpers out of platform-pearcord-platform-class.js. Manifest rows 82→85; runtime registry 22→25. Sparse sync, presence, and voice mesh behavior unchanged. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -54,6 +54,8 @@ Application facade: one `PearcordPlatform` class that wires identity, database,
|
||||
|
||||
**Phase 723 (v0.8.699):** Dedicated slash registry & invoke audit export JSON — `slash-registry-json-mixin.js`, `slash-invoke-audit-json-mixin.js`, spans `slash-registry.json` / `slash-invoke-audit.json`, `getSlashInvokeAuditExportJsonExport`, `clearSlashInvokeAuditExportJsonExports`, deep links `openSlashRegistryJson` / `openSlashInvokeAuditJson`. Bundle: `npm run test:ci-phase723`.
|
||||
|
||||
**Phase 747 (v0.8.723):** `platform-guild-sparse-sync-qos-mixin.js`, `platform-guild-contacts-presence-mixin.js`, `platform-voice-mesh-state-mixin.js` (85 mixin rows, 25 runtime mixins); sparse sync QoS, contacts presence reconcile, voice speaking/mute mesh gossip. Bundle: `npm run test:ci-phase747`.
|
||||
|
||||
**Phase 746 (v0.8.722):** `platform-guild-presence-vector-mixin.js`, `platform-settings-mesh-health-mixin.js`, `platform-guild-sync-wire-sparse-mixin.js` (82 mixin rows, 22 runtime mixins); presence vector sync, settings mesh health gossip, bundle wire sanitize, sparse open limits. Bundle: `npm run test:ci-phase746`.
|
||||
|
||||
**Phase 745 (v0.8.721):** `platform-guild-sync-health-patch-mixin.js`, `platform-guild-sync-gossip-view-mixin.js`, `platform-guild-sync-push-schedule-mixin.js` (79 mixin rows, 19 runtime mixins); sync health patch, gossip lane alerts, sparse ack view, push/request burst scheduling. Bundle: `npm run test:ci-phase745`.
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
'use strict'
|
||||
|
||||
const sharedScope = require('pearcord-shared')
|
||||
|
||||
const platformGuildContactsPresenceMixin = {
|
||||
_reconcileContactsPresenceDebounced (guildId) {
|
||||
if (this._contactsPresenceDebounce) clearTimeout(this._contactsPresenceDebounce)
|
||||
const scheduledAt = Date.now()
|
||||
const health = this.getGuildSyncHealth(guildId)
|
||||
const rosterTotal = Math.max(
|
||||
0,
|
||||
Number(health?.memberTotal) || Number(health?.memberCount) || 0
|
||||
)
|
||||
const debounceMs = Math.min(
|
||||
1200,
|
||||
400 + Math.floor(Math.min(rosterTotal, 500) / 50) * 50
|
||||
)
|
||||
this._contactsPresenceDebounce = setTimeout(() => {
|
||||
this._contactsPresenceDebounce = null
|
||||
const debounceMs = Date.now() - scheduledAt
|
||||
void this._reconcileContactsPresenceWithGuild(guildId)
|
||||
.then((applied) => {
|
||||
this.log.info('guild.mesh.stability', {
|
||||
spanKind: 'guild.mesh.stability',
|
||||
action: 'contacts-presence-reconcile',
|
||||
guildId,
|
||||
applied,
|
||||
debounceMs
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}, debounceMs)
|
||||
},
|
||||
|
||||
async _reconcileContactsPresenceWithGuild (guildId) {
|
||||
if (!guildId || !this.contacts?.getFriendPresenceMap) return 0
|
||||
const presenceMap = this.contacts.getFriendPresenceMap()
|
||||
let applied = 0
|
||||
for (const [userId, fp] of Object.entries(presenceMap || {})) {
|
||||
if (!userId || !fp) continue
|
||||
const mem = await this.db
|
||||
.get(sharedScope.COLLECTIONS.MEMBERS, { guildId, userId })
|
||||
.catch(() => null)
|
||||
if (!mem) continue
|
||||
if (
|
||||
this._reconcilePresenceVectorEntry(guildId, {
|
||||
userId,
|
||||
status: fp.status || 'offline',
|
||||
at: fp.updatedAt || fp.at || Date.now(),
|
||||
vectorSeq: fp.vectorSeq
|
||||
})
|
||||
) {
|
||||
this._ingestPresenceWithProfile({
|
||||
userId,
|
||||
status: fp.status,
|
||||
updatedAt: fp.updatedAt || Date.now()
|
||||
})
|
||||
applied++
|
||||
}
|
||||
}
|
||||
return applied
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { platformGuildContactsPresenceMixin }
|
||||
@@ -0,0 +1,46 @@
|
||||
'use strict'
|
||||
|
||||
const platformGuildSparseSyncQosMixin = {
|
||||
_filterChannelCoreKeysForSync (payload, opts = {}) {
|
||||
const keys = payload?.channelCoreKeys || []
|
||||
if (!keys.length) return []
|
||||
const priorityId =
|
||||
opts.channelId || opts.priorityChannelId || this.activeChannelId || null
|
||||
if (!payload?.syncDelta) return keys
|
||||
const active = new Set(
|
||||
(payload.messages || []).map((m) => m.channelId).filter(Boolean)
|
||||
)
|
||||
if (priorityId) active.add(priorityId)
|
||||
if (!active.size) return keys.filter((k) => k.channelId === priorityId)
|
||||
return keys.filter((k) => k.channelId === priorityId || active.has(k.channelId))
|
||||
},
|
||||
|
||||
_sparseSyncAckAllows (guildId, opts = {}) {
|
||||
if (opts.forceSparse) return true
|
||||
const deferAck =
|
||||
process.env.PEARCORD_GUILD_SPARSE_DEFER_ACK === '1' || opts.deferUntilAck === true
|
||||
if (!deferAck) return true
|
||||
const wm = this._getGuildSyncWatermark(guildId)
|
||||
const acks = this.getGuildSyncHealth(guildId)?.ackPeers || []
|
||||
return !!(wm.sinceTimestamp || acks.length)
|
||||
},
|
||||
|
||||
_gossipGuildSyncAck (channelId) {
|
||||
const guildId = this.guild?.guild?.id
|
||||
const userId = this.identity?.user?.id
|
||||
if (!guildId || !userId || !this.guild?.gossipGuildSyncAck) return
|
||||
if (this.guild.guild.ownerId === userId) return
|
||||
const wm = this._getGuildSyncWatermark(guildId)
|
||||
this.guild
|
||||
.gossipGuildSyncAck({
|
||||
guildId,
|
||||
userId,
|
||||
channelId: channelId || this.activeChannelId || null,
|
||||
sinceTimestamp: wm.sinceTimestamp || 0,
|
||||
sinceMessageId: wm.sinceMessageId || null
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { platformGuildSparseSyncQosMixin }
|
||||
@@ -4,7 +4,7 @@
|
||||
* Ordered PearcordPlatform prototype mixin registration (Phase 730).
|
||||
* Preserves assign order from index.js / apply-platform-mixins (Phase 727).
|
||||
*/
|
||||
const PLATFORM_MIXIN_ASSIGNMENT_COUNT = 82
|
||||
const PLATFORM_MIXIN_ASSIGNMENT_COUNT = 85
|
||||
|
||||
const PLATFORM_MIXIN_ASSIGNMENTS = [
|
||||
{ module: './polls-scheduling', export: 'pollSchedulingMixin' },
|
||||
@@ -88,6 +88,9 @@ const PLATFORM_MIXIN_ASSIGNMENTS = [
|
||||
{ module: './platform-guild-presence-vector-mixin', export: 'platformGuildPresenceVectorMixin' },
|
||||
{ module: './platform-settings-mesh-health-mixin', export: 'platformSettingsMeshHealthMixin' },
|
||||
{ module: './platform-guild-sync-wire-sparse-mixin', export: 'platformGuildSyncWireSparseMixin' },
|
||||
{ module: './platform-guild-sparse-sync-qos-mixin', export: 'platformGuildSparseSyncQosMixin' },
|
||||
{ module: './platform-guild-contacts-presence-mixin', export: 'platformGuildContactsPresenceMixin' },
|
||||
{ module: './platform-voice-mesh-state-mixin', export: 'platformVoiceMeshStateMixin' },
|
||||
{ module: './platform-diagnostics-mixin', export: 'platformDiagnosticsMixin' }
|
||||
]
|
||||
|
||||
|
||||
@@ -4280,38 +4280,13 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
_ingestVoiceSpeakingMesh (payload) {
|
||||
if (!payload?.channelId || !payload?.userId) return
|
||||
const ch = String(payload.channelId)
|
||||
const uid = String(payload.userId)
|
||||
if (!this._voiceSpeakingMesh[ch]) this._voiceSpeakingMesh[ch] = {}
|
||||
if (payload.speaking) {
|
||||
this._voiceSpeakingMesh[ch][uid] = Date.now() + 1500
|
||||
} else {
|
||||
delete this._voiceSpeakingMesh[ch][uid]
|
||||
}
|
||||
}
|
||||
|
||||
_meshUserSpeaking (channelId, userId) {
|
||||
const until = this._voiceSpeakingMesh[channelId]?.[userId]
|
||||
return !!(until && until > Date.now())
|
||||
}
|
||||
|
||||
_ingestVoiceMuteMesh (payload) {
|
||||
if (!payload?.channelId || !payload?.userId) return
|
||||
const ch = String(payload.channelId)
|
||||
const uid = String(payload.userId)
|
||||
if (!this._voiceMuteMesh[ch]) this._voiceMuteMesh[ch] = {}
|
||||
this._voiceMuteMesh[ch][uid] = {
|
||||
muted: !!payload.muted,
|
||||
deafened: !!payload.deafened,
|
||||
at: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
_meshUserVoiceMute (channelId, userId) {
|
||||
return this._voiceMuteMesh[channelId]?.[userId] || null
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_ingestDiscoveryExploreBaselineFromPrefs (prefs) {
|
||||
if (!prefs) return
|
||||
@@ -4456,32 +4431,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
_maybeGossipVoiceSpeaking () {
|
||||
const ch = this.voiceMedia?.channelId
|
||||
const uid = this.identity?.user?.id
|
||||
if (!ch || !uid || !this.guild?.guild) return
|
||||
const snap = this.voiceMedia.snapshot()
|
||||
const speaking = (snap.speakingPeers || []).includes(uid)
|
||||
const key = `${ch}:${speaking ? 1 : 0}`
|
||||
const now = Date.now()
|
||||
if (this._lastVoiceSpeakingGossipKey === key && now - this._lastVoiceSpeakingGossipAt < 350) {
|
||||
return
|
||||
}
|
||||
this._lastVoiceSpeakingGossipKey = key
|
||||
this._lastVoiceSpeakingGossipAt = now
|
||||
this._ingestVoiceSpeakingMesh({
|
||||
channelId: ch,
|
||||
userId: uid,
|
||||
speaking
|
||||
})
|
||||
this.guild.gossipVoiceSpeaking({
|
||||
guildId: this.guild.guild.id,
|
||||
channelId: ch,
|
||||
userId: uid,
|
||||
speaking,
|
||||
at: now
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
_invalidateDiscoveryListingsCache () {
|
||||
this._cachedPublicListings = null
|
||||
@@ -5428,29 +5378,9 @@ class PearcordPlatform extends EventEmitter {
|
||||
return applied
|
||||
}
|
||||
|
||||
_filterChannelCoreKeysForSync (payload, opts = {}) {
|
||||
const keys = payload?.channelCoreKeys || []
|
||||
if (!keys.length) return []
|
||||
const priorityId =
|
||||
opts.channelId || opts.priorityChannelId || this.activeChannelId || null
|
||||
if (!payload?.syncDelta) return keys
|
||||
const active = new Set(
|
||||
(payload.messages || []).map((m) => m.channelId).filter(Boolean)
|
||||
)
|
||||
if (priorityId) active.add(priorityId)
|
||||
if (!active.size) return keys.filter((k) => k.channelId === priorityId)
|
||||
return keys.filter((k) => k.channelId === priorityId || active.has(k.channelId))
|
||||
}
|
||||
|
||||
_sparseSyncAckAllows (guildId, opts = {}) {
|
||||
if (opts.forceSparse) return true
|
||||
const deferAck =
|
||||
process.env.PEARCORD_GUILD_SPARSE_DEFER_ACK === '1' || opts.deferUntilAck === true
|
||||
if (!deferAck) return true
|
||||
const wm = this._getGuildSyncWatermark(guildId)
|
||||
const acks = this.getGuildSyncHealth(guildId)?.ackPeers || []
|
||||
return !!(wm.sinceTimestamp || acks.length)
|
||||
}
|
||||
|
||||
|
||||
|
||||
async _eagerOpenAnnouncedCores (guildId, keys = [], opts = {}) {
|
||||
if (!guildId || !keys?.length || !GuildReplicator.isAvailable()) return 0
|
||||
@@ -5881,63 +5811,9 @@ class PearcordPlatform extends EventEmitter {
|
||||
return added
|
||||
}
|
||||
|
||||
_reconcileContactsPresenceDebounced (guildId) {
|
||||
if (this._contactsPresenceDebounce) clearTimeout(this._contactsPresenceDebounce)
|
||||
const scheduledAt = Date.now()
|
||||
const health = this.getGuildSyncHealth(guildId)
|
||||
const rosterTotal = Math.max(
|
||||
0,
|
||||
Number(health?.memberTotal) || Number(health?.memberCount) || 0
|
||||
)
|
||||
const debounceMs = Math.min(
|
||||
1200,
|
||||
400 + Math.floor(Math.min(rosterTotal, 500) / 50) * 50
|
||||
)
|
||||
this._contactsPresenceDebounce = setTimeout(() => {
|
||||
this._contactsPresenceDebounce = null
|
||||
const debounceMs = Date.now() - scheduledAt
|
||||
void this._reconcileContactsPresenceWithGuild(guildId)
|
||||
.then((applied) => {
|
||||
this.log.info('guild.mesh.stability', {
|
||||
spanKind: 'guild.mesh.stability',
|
||||
action: 'contacts-presence-reconcile',
|
||||
guildId,
|
||||
applied,
|
||||
debounceMs
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}, debounceMs)
|
||||
}
|
||||
|
||||
async _reconcileContactsPresenceWithGuild (guildId) {
|
||||
if (!guildId || !this.contacts?.getFriendPresenceMap) return 0
|
||||
const presenceMap = this.contacts.getFriendPresenceMap()
|
||||
let applied = 0
|
||||
for (const [userId, fp] of Object.entries(presenceMap || {})) {
|
||||
if (!userId || !fp) continue
|
||||
const mem = await this.db
|
||||
.get(sharedScope.COLLECTIONS.MEMBERS, { guildId, userId })
|
||||
.catch(() => null)
|
||||
if (!mem) continue
|
||||
if (
|
||||
this._reconcilePresenceVectorEntry(guildId, {
|
||||
userId,
|
||||
status: fp.status || 'offline',
|
||||
at: fp.updatedAt || fp.at || Date.now(),
|
||||
vectorSeq: fp.vectorSeq
|
||||
})
|
||||
) {
|
||||
this._ingestPresenceWithProfile({
|
||||
userId,
|
||||
status: fp.status,
|
||||
updatedAt: fp.updatedAt || Date.now()
|
||||
})
|
||||
applied++
|
||||
}
|
||||
}
|
||||
return applied
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -6753,22 +6629,7 @@ class PearcordPlatform extends EventEmitter {
|
||||
await rep.appendMessage(channelId, message).catch(() => {})
|
||||
}
|
||||
|
||||
_gossipGuildSyncAck (channelId) {
|
||||
const guildId = this.guild?.guild?.id
|
||||
const userId = this.identity?.user?.id
|
||||
if (!guildId || !userId || !this.guild?.gossipGuildSyncAck) return
|
||||
if (this.guild.guild.ownerId === userId) return
|
||||
const wm = this._getGuildSyncWatermark(guildId)
|
||||
this.guild
|
||||
.gossipGuildSyncAck({
|
||||
guildId,
|
||||
userId,
|
||||
channelId: channelId || this.activeChannelId || null,
|
||||
sinceTimestamp: wm.sinceTimestamp || 0,
|
||||
sinceMessageId: wm.sinceMessageId || null
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
|
||||
async _buildGuildSyncBundle (limitPerChannel, opts = {}) {
|
||||
const guild = this.guild?.guild
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
/** Prototype mixins extracted from PearcordPlatform class body (Phases 738–746). */
|
||||
/** Prototype mixins extracted from PearcordPlatform class body (Phases 738–747). */
|
||||
const PLATFORM_RUNTIME_MIXIN_MODULES = [
|
||||
'./platform-hyperswarm-runtime-mixin',
|
||||
'./platform-session-startup-mixin',
|
||||
@@ -23,7 +23,10 @@ const PLATFORM_RUNTIME_MIXIN_MODULES = [
|
||||
'./platform-guild-sync-push-schedule-mixin',
|
||||
'./platform-guild-presence-vector-mixin',
|
||||
'./platform-settings-mesh-health-mixin',
|
||||
'./platform-guild-sync-wire-sparse-mixin'
|
||||
'./platform-guild-sync-wire-sparse-mixin',
|
||||
'./platform-guild-sparse-sync-qos-mixin',
|
||||
'./platform-guild-contacts-presence-mixin',
|
||||
'./platform-voice-mesh-state-mixin'
|
||||
]
|
||||
|
||||
const PLATFORM_RUNTIME_MIXIN_COUNT = PLATFORM_RUNTIME_MIXIN_MODULES.length
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
'use strict'
|
||||
|
||||
const platformVoiceMeshStateMixin = {
|
||||
_ingestVoiceSpeakingMesh (payload) {
|
||||
if (!payload?.channelId || !payload?.userId) return
|
||||
const ch = String(payload.channelId)
|
||||
const uid = String(payload.userId)
|
||||
if (!this._voiceSpeakingMesh[ch]) this._voiceSpeakingMesh[ch] = {}
|
||||
if (payload.speaking) {
|
||||
this._voiceSpeakingMesh[ch][uid] = Date.now() + 1500
|
||||
} else {
|
||||
delete this._voiceSpeakingMesh[ch][uid]
|
||||
}
|
||||
},
|
||||
|
||||
_meshUserSpeaking (channelId, userId) {
|
||||
const until = this._voiceSpeakingMesh[channelId]?.[userId]
|
||||
return !!(until && until > Date.now())
|
||||
},
|
||||
|
||||
_ingestVoiceMuteMesh (payload) {
|
||||
if (!payload?.channelId || !payload?.userId) return
|
||||
const ch = String(payload.channelId)
|
||||
const uid = String(payload.userId)
|
||||
if (!this._voiceMuteMesh[ch]) this._voiceMuteMesh[ch] = {}
|
||||
this._voiceMuteMesh[ch][uid] = {
|
||||
muted: !!payload.muted,
|
||||
deafened: !!payload.deafened,
|
||||
at: Date.now()
|
||||
}
|
||||
},
|
||||
|
||||
_meshUserVoiceMute (channelId, userId) {
|
||||
return this._voiceMuteMesh[channelId]?.[userId] || null
|
||||
},
|
||||
|
||||
_maybeGossipVoiceSpeaking () {
|
||||
const ch = this.voiceMedia?.channelId
|
||||
const uid = this.identity?.user?.id
|
||||
if (!ch || !uid || !this.guild?.guild) return
|
||||
const snap = this.voiceMedia.snapshot()
|
||||
const speaking = (snap.speakingPeers || []).includes(uid)
|
||||
const key = `${ch}:${speaking ? 1 : 0}`
|
||||
const now = Date.now()
|
||||
if (this._lastVoiceSpeakingGossipKey === key && now - this._lastVoiceSpeakingGossipAt < 350) {
|
||||
return
|
||||
}
|
||||
this._lastVoiceSpeakingGossipKey = key
|
||||
this._lastVoiceSpeakingGossipAt = now
|
||||
this._ingestVoiceSpeakingMesh({
|
||||
channelId: ch,
|
||||
userId: uid,
|
||||
speaking
|
||||
})
|
||||
this.guild.gossipVoiceSpeaking({
|
||||
guildId: this.guild.guild.id,
|
||||
channelId: ch,
|
||||
userId: uid,
|
||||
speaking,
|
||||
at: now
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { platformVoiceMeshStateMixin }
|
||||
Reference in New Issue
Block a user