// Persists outstanding blind-pairing invite descriptors so the daemon can
// re-register their pairing members on boot — an already-shared invite keeps
// working across a daemon restart (RoomPairing.resumeRoomPairing).
//
// Only the invite secret (seed) is stored, owner-side; no writer/identity
// secret. JSON-backed (NOT replicated): { orgs: {name: descriptor}, repos: {...} }.
import fs from '#node:fs'
import path from '#node:path'
export class PendingPairings {
constructor (filePath) {
this._path = filePath
this._data = this._load()
}
_load () {
try {
const parsed = JSON.parse(fs.readFileSync(this._path, 'utf8'))
return { orgs: parsed.orgs || {}, repos: parsed.repos || {} }
} catch {
return { orgs: {}, repos: {} }
}
}
_save () {
try {
fs.mkdirSync(path.dirname(this._path), { recursive: true })
fs.writeFileSync(this._path, JSON.stringify(this._data, null, 2))
} catch (err) {
// eslint-disable-next-line no-console
console.error('[pending-pairings] save failed:', err && err.message)
}
}
// scope: 'orgs' | 'repos'. Last invite per name wins (one live member per room).
add (scope, name, descriptor) {
if (!this._data[scope]) this._data[scope] = {}
this._data[scope][name] = descriptor
this._save()
}
remove (scope, name) {
if (this._data[scope] && this._data[scope][name]) {
delete this._data[scope][name]
this._save()
}
}
/** @returns {Array<[string, object]>} [name, descriptor] pairs */
list (scope) {
return Object.entries(this._data[scope] || {})
}
}