OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / lib / services / pending-pairings.js

55 lines · javascript ref:
// 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] || {})
  }
}