OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-core / index.js

2569 lines · javascript ref:
const e = require("keet-identity-key"),
  { ProofEncoding: t } = require("keet-identity-key/lib/encoding"),
  i = require("hypercore-storage"),
  s = require("hypercore-id-encoding"),
  r = require("ready-resource"),
  o = require("ready-guard"),
  a = require("hyperswarm"),
  n = require("hyperdht"),
  l = require("corestore"),
  c = require("hypercore"),
  h = require("hyperblobs"),
  d = require("autobase"),
  y = require("scope-lock"),
  u = require("rache"),
  p = require("protomux-wakeup"),
  m = require("promaphore"),
  g = require("safety-catch"),
  b = require("task-backoff"),
  w = require("hypercore-crypto"),
  f = require("compact-encoding"),
  v = require("z32"),
  k = require("b4a"),
  P = require("fs"),
  K = require("path"),
  _ = require("blind-peering"),
  T = require("multi-profile-store"),
  R = require("hyperconf")
;(c.enable(c.SMALL_WANTS), c.setRecoveryPeers && c.setRecoveryPeers([]))
const L = require("@holepunchto/keet-push"),
  E = require("@holepunchto/keet-unread-anchors"),
  { generateMnemonic: C } = require("bip39-mnemonic"),
  { HyperDiscoveryClient: B } = require("@holepunchto/hyperdiscovery"),
  x = require("@holepunchto/keet-indexing"),
  D = require("./schema/versions"),
  { FEATURES: I } = D,
  S = require("./lib/api/v1"),
  A = require("./lib/api/v2"),
  O = require("./lib/core/crypto"),
  q = require("./lib/core/local"),
  V = require("./lib/core/files"),
  M = require("./lib/core/tasks"),
  U = require("./lib/core/sync"),
  N = require("./lib/core/migrations/3/apply"),
  { SwarmWorker: F } = require("./lib/core/swarm"),
  {
    getWriterKey: W,
    getInvitationId: j,
    createInvitation: $,
    generateRoomCapabilities: G,
    generateIdentityPairingName: H,
    generateRoomPairingName: J,
  } = require("./lib/core/invitations"),
  { getMemberForDeviceKey: Y } = require("./lib/core/contract/query"),
  { restoreAvatar: z } = require("./lib/core/contract/avatars"),
  { RoomStatus: Q } = require("./lib/core/contract/identity/private"),
  { DEBUG_FLAGS: X, DHT_SETTINGS: Z } = require("./lib/core/debug/debugging"),
  { KeetContract: ee } = require("./lib/core/contract"),
  { KeetContractError: te } = require("./lib/core/contract/errors.js"),
  ie = require("./lib/core/log"),
  {
    contract: {
      hyperdispatch: { decode: se, DispatchError: re },
      hyperschema: { getEnum: oe, getEncoding: ae, encode: ne, decode: le },
    },
    local: {
      hyperschema: { getEnum: ce },
    },
    config: { hyperconf: he },
  } = require("./schema/spec"),
  de = ae("@keet-private-id/profile-discovery-record"),
  ye = oe("@keet/build-type"),
  ue = require("./lib/core/users"),
  pe = require("protomux-rpc-client"),
  { KeetErrorManager: me, KeetErrorV2: ge } = require("./lib/errors"),
  { Version: be } = require("bare-semver"),
  { roomMigrationsStats: we } = require("./lib/core/contract/stats/local"),
  fe = require("./lib/bundle"),
  ve = ce("@local/keet-state"),
  ke = 2592e6,
  Pe = "keet-swarm-keypair",
  Ke = "keet-core",
  _e = "keet-unreads",
  Te = "keet-bundles",
  Re = 1e4,
  Le = 3e3,
  Ee = 4,
  Ce = 5e3,
  Be = k.alloc(32),
  xe = 1,
  De = 500,
  Ie = 9,
  Se = 1728e5,
  Ae = 5,
  Oe = 10,
  qe = 1711547109544,
  Ve = require("./lib/env"),
  Me = 64,
  Ue = 3,
  Ne = 30,
  Fe = 4
class We {
  constructor(e, t) {
    ;((this.keet = e),
      (this.roomId = t),
      (this.batch = null),
      (this.length = 0),
      (this.timestamp = 0),
      (this.pushLength = 0))
  }
  get key() {
    return this.room.base.local.key
  }
  _open() {
    ;((this.room = this.keet.internalOpenExistingRoom(this.roomId)),
      (this.batch = this.room.base.batch()),
      (this.timestamp = Date.now()))
  }
  _noop() {
    return (this.close(), this.length)
  }
  async dispatch(e, t, { dryRun: i = !0 } = {}) {
    ;(this.room || this._open(), this.room.opened || (await this.room.ready()))
    const s = await this.room._internalEncodeOperation(e, t, {
      view: this.room.view,
      timestamp: this.timestamp,
    })
    return i && !(await this.keet.internalDryRun(this.room, this.room.view, [s]))
      ? this._noop()
      : ((this.length = await this.append(s)),
        !this.pushLength && Je(e) && (this.pushLength = this.length),
        this.length)
  }
  async append(e) {
    return this.batch.append(e)
  }
  async flush() {
    try {
      return (
        await this.batch.flush(),
        await this.room._postAppend({
          encoded: this.batch.blocks,
          pushLength: this.pushLength,
          timestamp: this.timestamp,
        }),
        { key: this.key, length: this.length }
      )
    } finally {
      this.close()
    }
  }
  close() {
    ;(this.room && this.keet.internalReleaseRoom(this.room),
      this.batch && this.batch.close(),
      (this.batch = null),
      (this.length = 0),
      (this.shouldPush = !1))
  }
}
class je extends r {
  constructor(e, t, i, r, o) {
    ;(super(),
      (this.keet = e),
      (this.id = t),
      (this.key = i),
      (this.base = o),
      (this.externalBlobs = null),
      (this.discoveryKey = r),
      (this.discoveryId = s.encode(this.discoveryKey)),
      (this.view = this.base.view),
      (this.db = this.view.db),
      (this.core = this.view.core),
      (this.blobs = this.view.blobs),
      (this.router = this.view.router),
      (this.push = null),
      this.keet.push && (this.push = this.keet.push.room(this.key, this.discoveryKey)),
      (this.refs = 0),
      (this.idleTicks = 0),
      (this._pushLock = new y({ debounce: !0 })),
      this.ready().catch(ze))
  }
  get deviceId() {
    return s.encode(this.deviceKey)
  }
  get deviceKey() {
    return this.base.local.key
  }
  get encryptionKey() {
    return this.base.encryptionKey
  }
  getQuickStats() {
    return {
      view: {
        peers: this.view.core.peers.length,
        length: this.view.core.length,
        signedLength: this.view.core.signedLength,
        byteLength: this.view.core.byteLength,
      },
      system: {
        peers: this.base.core.peers.length,
        length: this.base.length,
        signedLength: this.base.signedLength,
        indexedLength: this.base.indexedLength,
      },
      base: {
        opened: this.base.opened,
        closed: this.base.closed,
        opening: !!this.base.opening,
        closing: !!this.base.closing,
        writable: this.base.writable,
        indexing: this.base.ackable,
        interrupted: !!this.base.interrupted,
        appending: !!this.base.appending,
        flushing: !!this.base.flushing,
        advancing: !!this.base._advancing,
      },
    }
  }
  async getStats() {
    const [e, t, i] = await Promise.all([
        this.keet.local.get("@local/rooms", { roomKey: this.key }),
        this.keet.local.get("@local/room-preferences", { roomKey: this.key }),
        this.keet.local.get("@local/writers-by-room-key", { roomKey: this.key }),
      ]),
      { view: r, system: o, base: a } = this.getQuickStats()
    return {
      view: r,
      system: o,
      base: a,
      writer: {
        key: i.key ? s.encode(i.key) : null,
        roomKey: i?.roomKey ? s.encode(i.roomKey) : null,
      },
      status: e?.status,
      preferences: t?.status,
    }
  }
  async _createDeviceAttestation(e, t) {
    const i = await this.keet.internalEnterLocalTransaction()
    try {
      const s = this.keet.internalAttestData(e, t)
      return (
        await i.insert("@local/device-attestations", {
          deviceKey: e,
          proof: s,
          createdAt: Date.now(),
        }),
        await i.flush(),
        s
      )
    } finally {
      await this.keet.internalExitLocalTransaction()
    }
  }
  async _attestedPushNotify(e) {
    const t = this.deviceKey,
      [i, s] = await Promise.all([
        this.keet.local.get("@local/identity"),
        this.keet.local.get("@local/device-attestations", { deviceKey: t }),
      ]),
      r = s?.createdAt || 0,
      o = i.proofShortenedAt || 0,
      a = !s || r < o ? await this._createDeviceAttestation(t, i) : s.proof
    await this.push.send(this.base.local, e, { version: 1, attest: a })
  }
  async _safePushNotify(e) {
    if (
      (this._pushLengths || (this._pushLengths = []),
      this._pushLengths.push(e),
      !(await this._pushLock.lock()))
    )
      return
    if (this.closing) return
    const t = this._pushLengths
    this._pushLengths = null
    try {
      await this._attestedPushNotify(t)
    } catch (e) {
      this.keet.onerror(e, { roomKey: this.key })
    }
    this._pushLock.unlock()
  }
  async _safeBumpDiscovery(e) {
    try {
      const t = await this.keet.local.get("@local/rooms", { roomKey: this.key })
      this.keet.discovery.announce(t.capabilities.discoveryKeyPair, { bump: e })
    } catch (e) {
      this.keet.onerror(e, { roomKey: this.key })
    }
  }
  async _open() {
    ;(await this.base.ready(),
      this.internalEnsureMirroring(),
      this.base.on("writer", async (e) => {
        try {
          if (await this.keet.local.get("@local/devices", { device: { key: e.core.key } })) return
          const t = await this.db.get("@keet/devices", { key: e.core.key })
          t && (await this.db.get("@keet/members", { key: t.memberKey }))
        } catch (e) {
          g(e)
        }
      }),
      this.base.local.on("remote-contiguous-length", this._handleSentLengthChanged.bind(this)),
      this.base.on("rotate-local-writer", () => {
        this.base.local.on("remote-contiguous-length", this._handleSentLengthChanged.bind(this))
      }),
      this.db.get("@keet/room-config").then((e) => {
        this.keet.logger.debug("opened id: " + this.id + " title: " + e?.title)
      }, g),
      (this.externalBlobs = new h(
        this.keet.store.get({
          name: this.id + "/blobs",
          encryptionKey: this.encryptionKey,
          encrypted: !0,
          exclusive: !0,
          active: !1,
        }),
      )),
      await this.externalBlobs.ready(),
      await this._ensureMetadata(),
      this.internalEnsureProfileUpdated(),
      this.internalEnsureUpgraded(),
      this.internalEnsureRepaired())
  }
  async internalCloseBase() {
    ;(this.keet.internalTime(`internal-close-base-${this.id}`),
      await this.base.close(),
      this.keet.internalTimeEnd(`internal-close-base-${this.id}`))
  }
  async internalCloseExternalBlobs() {
    ;(this.keet.internalTime(`internal-close-blobs-${this.id}`),
      await this.externalBlobs.close(),
      this.keet.internalTimeEnd(`internal-close-blobs-${this.id}`))
  }
  async internalPushDestroy() {
    this.push &&
      (this.keet.internalTime(`internal-close-push-${this.id}`),
      await this.push.destroy(),
      this.keet.internalTimeEnd(`internal-close-push-${this.id}`))
  }
  _close() {
    return Promise.all([
      this.internalCloseBase(),
      this.internalCloseExternalBlobs(),
      this.internalPushDestroy(),
    ])
  }
  _handleSentLengthChanged() {
    if (!this.base.writable) return
    const { length: e, remoteContiguousLength: t } = this.base.local
    e === t && this.keet.emit("room-sent-message", this.base.id)
  }
  async _ensureMetadata() {
    const e = await this.keet.internalEnterLocalTransaction()
    try {
      const [t, i] = await Promise.all([
        e.get("@local/room-metadata", { roomKey: this.key }),
        e.get("@local/rooms", { roomKey: this.key }),
      ])
      if (t) return
      const s = i ? 0 : 1
      ;(await e.insert("@local/room-metadata", { roomKey: this.key, repairs: s }), await e.flush())
    } finally {
      await this.keet.internalExitLocalTransaction()
    }
  }
  async internalEnsureMirroring() {
    this.refs++
    try {
      const e = this.base.wakeupCapability && this.base.wakeupCapability.key,
        t = await this.keet._getMirrorKeys(this.key)
      if (this.closing) return
      this.keet.blindPeering.addAutobaseBackground(this.base, { target: e, keys: t })
    } finally {
      this.refs--
    }
  }
  async internalEnsureUpgraded({ opening: e = !1 } = {}) {
    this.refs++
    try {
      e || this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
      const [t, i] = await Promise.all([
        this.db.get("@keet/system-digest"),
        Y(this.db, this.deviceKey),
      ])
      if (!i?.status.isModerator || t?.abi.version >= this.keet.abi.autoupgrade) return
      await this.keet.internalDispatchUpgrade(this.id, this.keet.abi.autoupgrade)
    } catch (e) {
      this.keet.onerror(e, { roomKey: this.key })
    } finally {
      this.refs--
    }
  }
  async internalEnsureProfileUpdated() {
    this.refs++
    try {
      this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
      const [e, t] = await Promise.all([
        this.keet.local.get("@local/identity"),
        Y(this.db, this.deviceKey),
      ])
      if (this.closing || !e || !t || k.equals(this.key, e.privateProfileKey)) return
      const i = await this.keet.local.get("@local/rooms", { roomKey: e.privateProfileKey })
      if (this.closing) return
      if (t.profile.timestamp >= i.selfMember.profile.timestamp) return
      const s = {
        profile: i.selfMember.profile,
        attestation: { memberKey: i.selfMember.key, proof: i.selfMember.proofs.profileAttestation },
      }
      await this.dispatch("@keet/update-member", { member: s })
    } catch (e) {
      this.keet.onerror(e, { roomKey: this.key })
    } finally {
      this.refs--
    }
  }
  async internalEnsureRepaired() {
    this.refs++
    try {
      if (
        (this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
        this === this.keet.privateProfile)
      )
        return
      const e = await this.keet.local.get("@local/room-metadata", { roomKey: this.key })
      if (this.closing || e.repairs >= 1) return
      if (this.base.linearizer.size > De) return
      const t = await this.db.get("@keet-private-id/identity")
      if (this.closing || t) return
      if (((e.repairs = 1), await this.base.repair(), this.closing)) return
      const i = await this.keet.internalEnterLocalTransaction()
      try {
        ;(await i.insert("@local/room-metadata", e), await i.flush())
      } finally {
        await this.keet.internalExitLocalTransaction()
      }
    } finally {
      this.refs--
    }
  }
  async _internalEncodeOperation(e, t, { view: i = this.view, timestamp: s = Date.now() } = {}) {
    const r = await i.db.get("@keet/system-digest")
    if (!this.keet.isValidBuildType(r)) throw ge.INVALID_BUILD_TYPE()
    return (
      await this.keet.contractBundles.loadBundle(r ? r.abi.version : this.keet.abi.autoupgrade)
    ).encodeOperation(e, { header: { timestamp: s }, ...t })
  }
  async _encodeBatch(e, t = {}) {
    const i = Array.isArray(e) ? e : [e],
      s = Date.now(),
      r = []
    let o = !1
    for (const { type: e, op: a } of i)
      (r.push(this._internalEncodeOperation(e, a, { ...t, timestamp: s })), Je(e) && (o = !0))
    return { encoded: await Promise.all(r), timestamp: s, shouldPush: o }
  }
  async _postAppend({ encoded: e = [], pushLength: t = 0, timestamp: i = Date.now() }) {
    ;((this.keet.stats.ops += e.length),
      this.push && t > 0 && this._safePushNotify(t),
      this.keet.discovery && this._safeBumpDiscovery(i),
      this.db.update())
  }
  async dispatchBatch(e, { view: t = this.view, dryRun: i = !0, optimistic: s = !1 } = {}) {
    this.opened || (await this.ready())
    const { encoded: r, timestamp: o, shouldPush: a } = await this._encodeBatch(e, { view: t })
    if (i && !(await this.keet.internalDryRun(this, t, r))) return null
    const n = await this.base.append(r, { optimistic: s }),
      l = a ? n : 0
    return (
      await this._postAppend({ encoded: r, pushLength: l, timestamp: o }),
      { key: this.base.local.key, length: n }
    )
  }
  dispatch(e, t, i) {
    return this.dispatchBatch([{ type: e, op: t }], i)
  }
}
module.exports = class extends r {
  constructor(e, t = {}) {
    if (
      (super(),
      (this.dir = e),
      (this.opts = t),
      (this.isMobile = !!t.mobile),
      (this.isTesting = !!t.testing),
      (this.appVersion = t.appVersion ? be.parse(t.appVersion) : null),
      (this.forceBuildType = !!t.forceBuildType),
      !t.buildType)
    )
      throw ge.INVALID_BUILD_TYPE()
    ;((this.buildType = Ye(t.buildType)),
      (this.logger = new ie(t.logger)),
      this.isTesting && this.logger.mute())
    const s = Ve(t.buildType)
    ;((this.isNightly = this.buildType === ye.Nightly),
      (this.isExperimental = this.isTesting ? (t.experimental ?? this.isNightly) : this.isNightly),
      (this.offlineBoot = new o()),
      (this.onlineBoot = new o()),
      (this.debug = {}),
      (this.versions = D),
      (this.abi = t.testingABI || {
        max: this.isExperimental ? D.EXPERIMENTAL : D.STABLE,
        autoupgrade: this.isExperimental ? D.EXPERIMENTAL : D.AUTOUPGRADE,
        production: D.STABLE,
      }),
      (this.logout = t.logout),
      (this.storage = new i(e, t.storage)),
      (this.rootCorestore = new l(this.storage, {
        globalCache: new u({ maxSize: t.maxCacheSize }),
        manifestVersion: 2,
      })),
      (this.store = this.rootCorestore.namespace(Ke)),
      this.rootCorestore.watch(this._handleCoreOpen.bind(this)),
      (this.local = q.rocks(this.storage.rocks.columnFamily("keet/local-database"))),
      (this.discovery = null),
      (this.gatekeepers = t.gatekeepers || []),
      (this.push = null),
      (this.protomuxWakeup = new p(this._handleWakeup.bind(this))),
      (this.backoff = new b()),
      (this.rooms = new Map()),
      (this.activeRooms = new Set()),
      (this._transactionLock = new y()),
      (this._tx = null),
      (this.hyperconf = null),
      (this.config = null),
      (this.configMinVersion = null),
      (this.errorManager = new me(this, this.opts)),
      (this.swarmWorker = new F(this, this.opts.swarmWorker)),
      (this.files = new V(this, this.opts)),
      (this._onerror = t.onerror || g),
      (this.onerror = this.errorManager
        ? (e, t = {}) => {
            const i = this.errorManager.normalize(e, t)
            ;(this.errorManager.add(i), this.logger.error(i), this._onerror(i))
          }
        : this._onerror),
      (this.apiV1 = new S(this, t)),
      (this.backendAPI = this.apiV1),
      (this.api = this.backendAPI.api.core),
      (this.blindPeeringOpts = t.blindPeeringOpts || {}),
      (this.contractBundles = null),
      (this.tasks = null),
      (this.swarm = null),
      (this.swarmId = null),
      (this.blindPeering = null),
      (this.privateProfile = null),
      (this.unreads = null),
      (this.unreadsLoading = null),
      (this.discoveryCore = null),
      (this.discoveryCoreSession = null),
      (this.push = null),
      (this.state = ve.RESUME),
      (this._bundlesKey = t.bundlesKey || s.bundles.key),
      (this._bundlesLength = t.bundlesLength || s.bundles.length),
      (this._hyperconfKey = t.hyperconfKey || s.config.key),
      (this._hyperconfLength = t.hyperconfLength || s.config.length),
      (this._disabledRooms = this.isTesting ? new Set() : null),
      (this._verifiedRooms = this.isTesting ? new Set() : null),
      (this._relayKeys = this.isTesting ? t.blindRelays || [] : null),
      (this._blindPeerKeys = this.isTesting ? t.blindMirroring || [] : null),
      (this._gatekeeperKeys = this.isTesting ? t.gatekeepers || [] : null),
      (this._discoveryBootstrapKeys = this.isTesting ? t.discoveryBootstrapKeys || [] : null),
      (this._disableUnreadSync = (this.isTesting && t.disableUnreadSync) || !1),
      (this._otaResyncPrivateProfileBump = (this.isTesting && t.otaResyncPrivateProfileBump) || 0),
      (this._migratedFrom = 0),
      (this._unreadsLock = new y({ debounce: !0 })),
      (this._dhtOpts = t.dht),
      (this._swarmOpts = t.swarm),
      (this._pendingTasks = []),
      (this._swarmTasks = []),
      (this._runningTasks = new Set()),
      (this._waitingForCompletion = new Map()),
      (this._completedRequests = new Set()),
      (this._pendingCandidates = new Map()),
      (this._swarming = new Map()),
      (this._pendingTraces = []),
      (this._cacheLock = new y({ debounce: !0 })),
      (this._gcEnabled = !1 !== t.gc),
      (this._gcIdleTicks = t.gcIdleTicks || 4),
      (this._gcInterval = t.gcInterval || Ce),
      (this._gcTimer = null),
      (this._gcClosingRooms = new Set()),
      (this._boundAutobaseTrace = this._autobaseTrace.bind(this)),
      (this.internalPairingTimeout = t.pairingTimeout || Le),
      (this._profileDiscoveryTimeout = t.profileDiscoveryTimeout || Re),
      (this.migrationStatus = null),
      (this._roomMigrations = new m(5)),
      (this._discoveryLock = new y({ debounce: !0 })),
      (this._discoverySemaphore = new m(Oe)),
      (this._discovering = new Map()),
      (this.internalFastJoinSemaphore = new m(4)),
      (this.debugRunningFastJoins = 0),
      (this.debugMaxRunningFastJoins = 0),
      (this._rejectAnonymousProfiles = !1 !== t.rejectAnonymousProfiles),
      (this._registryOpts = this.isTesting ? t.userRegistry : null),
      (this.rpcClient = null),
      (this.userRegistry = null),
      (this.ackThreshold = t.ackThreshold),
      (this.ackInterval = t.ackInterval),
      (this.stats = {
        ops: 0,
        appends: 0,
        bytesAppended: 0,
        uploaded: 0,
        bytesUploaded: 0,
        downloaded: 0,
        bytesDownloaded: 0,
      }),
      (this.debugForceMigrationFailure = t.debugForceMigrationFailure || null),
      (this.debugJoinPeer = t.debugJoinPeer || this._debugJoinPeer.bind(this)),
      (this.debugLeavePeer = t.debugLeavePeer || this._debugLeavePeer.bind(this)),
      this.ready().catch(this.onerror))
  }
  async backendReady() {
    this.backendAPI.opened || (await this.backendAPI.ready())
  }
  get readOnly() {
    return this.rootCorestore.storage.readOnly || !1
  }
  get suspended() {
    return this.state === ve.SUSPEND
  }
  async _openNetworking() {
    const e = await this.getDHTSettings()
    ;((this.swarm = new a({
      deferRandomPunch: !0,
      dht: new n({ ...e, ...this._swarmOpts, ...this._dhtOpts }),
      maxPeers: Ne,
      ...this._swarmOpts,
      relayThrough: this._relayThrough.bind(this),
      keyPair: await this.rootCorestore.createKeyPair(Pe),
    })),
      (this.swarmId = s.encode(this.swarm.keyPair.publicKey)),
      this.swarm.on("connection", this._handleConnection.bind(this)),
      (this.discovery = this._discoveryBootstrapKeys.length
        ? new B(this._discoveryBootstrapKeys[0], { dht: this.swarm.dht })
        : null),
      (this.rpcClient = new pe(this.swarm.dht, {
        suspended: this.state === ve.SUSPEND,
        relayThrough: this._relayThrough.bind(this),
      })),
      (this.blindPeering = new _(this.swarm.dht, this.store, {
        pick: 2,
        wakeup: this.protomuxWakeup,
        keys: this.debug.disableMirrors ? [] : this._blindPeerKeys,
        relayThrough: this._relayThrough.bind(this),
        ...this.blindPeeringOpts,
      })),
      this._gatekeeperKeys.length > 0 &&
        (this.push = new L(this._gatekeeperKeys, { dht: this.swarm.dht })),
      await this.swarmWorker.ready(),
      await this.files.ready(),
      this.readOnly && (this.blindPeering.suspend(), this.swarm.suspend()))
  }
  async _runBootOffline() {
    if (!this.offlineBoot.enter()) return
    ;(await this._ensureDebugState(),
      await this.store.ready(),
      (this.hyperconf = new R(he, this.store.get(this._hyperconfKey), {
        minLength: this._hyperconfLength,
        production: this.buildType === ye.Production,
      })),
      this.hyperconf.on("update", this._handleConfigUpdated.bind(this)),
      await this.hyperconf.ready(),
      await this._handleConfigUpdated(),
      this.config?.versions.autoupgrade > 0 &&
        (this.abi.autoupgrade = this.config.versions.autoupgrade))
    const e = await this.local.get("@local/environment"),
      t = !!e && e.latestOfflineAbi < this.abi.production
    ;((this.contractBundles = new fe(this, Te, { hydrate: t })),
      await this.contractBundles.ready(),
      t && (await this._bumpLocalAbiVersion()),
      await this._openNetworking(),
      (this.tasks = new M(this)),
      await this.tasks.ready(),
      this._gcEnabled &&
        (this._gcTimer = setInterval(this._closeIdleRooms.bind(this), this._gcInterval)),
      this.offlineBoot.exit())
  }
  async _runBootOnline() {
    if (this.onlineBoot.enter()) {
      ;(await this.offlineBoot.ready(),
        this.discovery &&
          (this.discovery.on("announce", ({ publicKey: e }) => {
            this.scheduleDiscoverySwarming(e)
          }),
          this.discovery.on("connect", this._onDiscoveryConnect.bind(this)),
          this.discovery.connect()),
        this._refreshDiscoverySwarming(),
        (this._migratedFrom = await this._ensureMigrated()))
      try {
        await this._loadIdentity()
      } catch (e) {
        this.onerror(e)
      }
      ;(this._migratedFrom > 0 && (await this._maybeRunPostMigrations(this._migratedFrom)),
        this._registryOpts &&
          ((this.userRegistry = new ue(this, this._registryOpts)), await this.userRegistry.ready()),
        this.onlineBoot.exit())
    }
  }
  async _open() {
    !1 !== (await this._ensureBuildType()) &&
      (this._runBootOffline().catch(this.onerror),
      this._runBootOnline().catch(this.onerror),
      await this.offlineBoot.ready())
  }
  async _close() {
    ;(this.internalTime("keet-close"),
      this.internalVerboseLog("Closing keet core"),
      this._gcTimer && clearInterval(this._gcTimer),
      this.backoff.destroy(),
      this.internalTimeLog("keet-close", "destroyed backoff"),
      this.state === ve.SUSPEND && (await this.store.resume()),
      this.internalTimeLog("keet-close", "resumed from suspend"),
      this.internalVerboseLog("Closing active calls"),
      await this.backendAPI.closeActiveCalls(),
      this.internalTimeLog("keet-close", "closed active calls"),
      this.internalVerboseLog("Closing discovery cores"),
      this.discoveryCore && (await this.discoveryCore.close()),
      this.discoveryCoreSession && this.discoveryCoreSession.destroy(),
      this.internalTimeLog("keet-close", "closed discovery cores"),
      this.internalVerboseLog("Closing tasks"),
      this.tasks && (await this.tasks.close()),
      this.internalFastJoinSemaphore.destroy(),
      this.internalTimeLog("keet-close", "stopped running tasks"))
    for (const e of this._gcClosingRooms)
      (this.internalTime(`gc-room-close-${e.id}`),
        this.internalTimeLog(`gc-room-close-${e.id}`),
        this.internalVerboseLog("Closing room in gc list", e.id, e.opened),
        await e.close(),
        this.internalTimeEnd(`gc-room-close-${e.id}`, "closed"))
    ;(this.internalTimeLog("keet-close", "closed rooms in gc"),
      this.internalVerboseLog("Closing user registry"),
      this.userRegistry && (await this.userRegistry.close()),
      this.internalTimeLog("keet-close", "closed user registry"),
      this.internalVerboseLog("Closing rpc client"),
      this.rpcClient && (await this.rpcClient.close()),
      this.internalTimeLog("keet-close", "closed rpc client"),
      this.internalVerboseLog("Closing swarm worker"),
      await this.swarmWorker.close(),
      this.internalTimeLog("keet-close", "closed swarm worker"),
      this.internalVerboseLog("Closing discovery"),
      this.discovery && (await this.discovery.close()),
      this.internalTimeLog("keet-close", "closed discovery"),
      this.internalVerboseLog("Closing push"),
      this.push && (await this.push.destroy()),
      this.internalTimeLog("keet-close", "closed push"),
      this.internalVerboseLog("Closing blind peering"),
      this.blindPeering && (await this.blindPeering.close()),
      this.internalTimeLog("keet-close", "closed blind peering"),
      this.internalVerboseLog("Flushing DHT cache"),
      this.swarm && (await this._flushDHTCache()),
      this.internalTimeLog("keet-close", "flushed dht"),
      this.internalVerboseLog("Closing swarm"),
      this.swarm && (await this.swarm.destroy()),
      this.internalTimeLog("keet-close", "destroyed swarm"),
      this.internalVerboseLog("Closing files"),
      await this.files.close(),
      this.internalTimeLog("keet-close", "closed files"),
      this.internalVerboseLog("Closing all pending rooms and workers"),
      await Promise.allSettled([
        ...[...this.rooms.values()].map(async (e) => {
          ;(this.internalTime(`pending-room-close-${e.id}`),
            this.internalTimeLog(`pending-room-close-${e.id}`),
            await e.close(),
            this.internalTimeEnd(`pending-room-close-${e.id}`))
        }),
      ]),
      this.internalTimeLog("keet-close", "closed all pending rooms and workers"),
      this.internalVerboseLog("Closing backend"),
      await this.apiV1.close(),
      this.internalTimeLog("keet-close", "closed backend"),
      await this.internalEnterLocalTransaction(),
      await this.internalExitLocalTransaction(),
      this.internalVerboseLog("Closing unread anchors"),
      this.unreadsLoading && (await this.unreadsLoading),
      this.unreads && (await this.unreads.close()),
      this.internalTimeLog("keet-close", "closed unreads"),
      this.internalVerboseLog("Closing wakeup"),
      this.protomuxWakeup.destroy(),
      this.internalTimeLog("keet-close", "protomux"),
      this.internalVerboseLog("Closing hyperconf"),
      this.hyperconf && (await this.hyperconf.close()),
      this.internalVerboseLog("Closing error manager"),
      this.errorManager && (await this.errorManager.close()),
      this.internalVerboseLog("Closing local db"),
      await this.local.close(),
      this.internalVerboseLog("Closing contract bundles"),
      this.contractBundles && (await this.contractBundles.close()),
      this.internalVerboseLog("Closing store"),
      await this.store.close(),
      this.internalVerboseLog("Closing root corestore"),
      await this.rootCorestore.close(),
      this.internalTimeLog("keet-close", "closed cores"),
      this.internalVerboseLog("Close done"),
      this.internalTimeEnd("keet-close"))
  }
  async _ensureDebugState() {
    const e = await this.internalEnterLocalTransaction()
    try {
      const t = (await e.get("@local/debug")) || {}
      t.flags || (t.flags = X)
      const i = new Set(),
        s = {}
      for (const e of t.flags) (i.add(e.key), (s[e.key] = e.value))
      for (const { key: e, value: s, description: r } of X)
        i.has(e) || t.flags.push({ key: e, value: s, description: r })
      ;(await e.insert("@local/debug", t), await e.flush(), (this.debug = { ...s }))
    } finally {
      await this.internalExitLocalTransaction()
    }
  }
  isValidBuildType(e) {
    if (!e) return !0
    if (this.isTesting && !this.forceBuildType) return !0
    const t = ye.Production === this.buildType
    return !((!t && !e.settings.buildType) || (!t && e.settings.buildType !== this.buildType))
  }
  async _ensureBuildType() {
    if (this.isTesting && !this.forceBuildType) return
    const e = ye.Production === this.buildType,
      t = await this.internalEnterLocalTransaction()
    try {
      const [i, s] = await Promise.all([t.get("@local/identity"), t.get("@local/environment")])
      if (!e && i && !s?.buildType) throw ge.INVALID_BUILD_TYPE()
      if (s && s.buildType !== this.buildType) throw ge.INVALID_BUILD_TYPE()
      if (s) return
      ;(await t.insert("@local/environment", { buildType: this.buildType }), await t.flush())
    } catch (e) {
      return (this.onerror(e), !1)
    } finally {
      await this.internalExitLocalTransaction()
    }
  }
  async _bumpLocalAbiVersion() {
    const e = await this.internalEnterLocalTransaction()
    try {
      const t = await e.get("@local/environment")
      ;((t.latestOfflineAbi = this.abi.production),
        await e.insert("@local/environment", t),
        await e.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
  }
  async _onDiscoveryConnect() {
    const e = await this.local
      .find("@local/rooms-by-timestamp", { reverse: !0, limit: 256 })
      .toArray()
    this.discovery.cork()
    for (const t of e) {
      const e = t.capabilities.discoveryKeyPair.publicKey,
        i = await this.local.get("@local/background-sync", { discoveryPublicKey: e }),
        s = Math.max(t.timestamps.latestSync, i ? i.lastSynced : 0)
      this.discovery.subscribe(t.capabilities.discoveryKeyPair.publicKey, { since: s })
    }
    this.discovery.uncork()
  }
  async internalEnterLocalTransaction() {
    return (await this._transactionLock.lock(), (this._tx = this.local.transaction()), this._tx)
  }
  async internalExitLocalTransaction() {
    ;(await this._tx.close(), this._transactionLock.unlock())
  }
  async _handleConfigUpdated() {
    if (!this.isTesting || this.opts.hyperconfKey) {
      if (
        ((this.config = this.hyperconf.current),
        this.internalVerboseLog(
          "OTA config updated, dkey:",
          s.encode(this.hyperconf.core.discoveryKey),
          "length:",
          this.hyperconf.core.length,
        ),
        (this._blindPeerKeys = this.config.services.blindPeers),
        (this._relayKeys = this.config.services.blindRelays),
        (this._gatekeeperKeys = this.config.services.gatekeepers),
        (this._discoveryBootstrapKeys = this.config.services.discovery),
        c.setRecoveryPeers && c.setRecoveryPeers(this.config.services.blindPeers),
        (this._disabledRooms = new Set(this.config.rooms.disabled.map((e) => s.encode(e)))),
        (this._verifiedRooms = new Set(this.config.rooms.verified?.map((e) => s.encode(e)))),
        (this._disableUnreadSync = !0 === this.config.profile?.disableUnreadSync),
        (this._otaResyncPrivateProfileBump = this.config.profile?.otaResyncPrivateProfileBump || 0),
        this._maybeOtaResyncPrivateProfile().catch(this.onerror),
        this._maybeUpgradeOpenRooms().catch(this.onerror),
        this.config.services.userRegistries?.length &&
          (this._registryOpts = this.config.services.userRegistries[0]),
        this.blindPeering && this.blindPeering.setKeys(this._blindPeerKeys),
        this.config.versions.minVersion)
      ) {
        const { major: e, minor: t, patch: i } = this.config.versions.minVersion
        this.configMinVersion = new be(e, t, i)
      }
      ;(this.config.versions.autoupgrade > 0 &&
        (this.abi.autoupgrade = this.config.versions.autoupgrade),
        this.emit("force-update"),
        this.emit("verified-rooms-changed"))
    }
  }
  async _maybeUpgradeOpenRooms() {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const e = this.abi.autoupgrade,
      t = this.config?.versions.autoupgrade
    if (!(e < t)) return
    const i = [],
      s = async function (e, t) {
        const [i, s] = await Promise.all([e.db.get("@keet/system-digest"), Y(e.db, e.deviceKey)])
        !s?.status.isModerator ||
          i?.abi.version >= t ||
          (await this.internalDispatchUpgrade(e.id, t))
      }.bind(this)
    for (const [e, r] of this.rooms.entries()) i.push(s(r, t))
    return await Promise.all(i)
  }
  async _maybeOtaResyncPrivateProfile() {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const e = this._otaResyncPrivateProfileBump || 0,
      t = await this.internalEnterLocalTransaction()
    try {
      if (!(e > ((await t.get("@local/ota-actions"))?.otaResyncPrivateProfileBump || 0))) return
      if (null === (await this.internalResyncPrivateProfile())) return
      ;(await t.insert("@local/ota-actions", { otaResyncPrivateProfileBump: e }), await t.flush())
    } catch (e) {
      this.onerror(e)
    } finally {
      await this.internalExitLocalTransaction()
    }
  }
  shouldForceUpdate() {
    const e = this.config.versions.forceUpdate > D.FORCE_UPDATE,
      t =
        this.configMinVersion && this.appVersion
          ? this.configMinVersion.compare(this.appVersion)
          : 0
    return e || 1 === t
  }
  isRoomVerified(e) {
    return this._verifiedRooms.has(e)
  }
  async _bumpRoom(e) {
    if (this.closing) return
    if (!(await this.local.get("@local/writers-by-room-key", { roomKey: e }))) return
    if (this.closing) return
    const t = this.internalOpenExistingRoom(e)
    try {
      await t.ready()
    } finally {
      this.internalReleaseRoom(t)
    }
  }
  _handleCoreOpen(e) {
    const t = new c({ core: e, weak: !0 }),
      i = new y({ debounce: !0 })
    ;(t.on("verification-error", (e) => {
      this.onerror(e, { coreKey: t.key, prefix: "CORE_VERIFICATION_ERROR" })
    }),
      t.on("conflict", () => {
        this.onerror(new Error("Conflict in Hypercore"), {
          coreKey: t.key,
          prefix: "CORE_CONFLICT",
        })
      }),
      t.on("invalid-request", (e) => {
        this.onerror(e, { coreKey: t.key, prefix: "CORE_INVALID_REQUEST" })
      }),
      t.on("append", async () => {
        if (await i.lock())
          try {
            const t = e.state.storage.read(),
              i = [t.getUserData("referrer"), t.getUserData("autobase/view")]
            t.tryFlush()
            const [s, r] = await Promise.all(i)
            if (!s || r) return
            await this._bumpRoom(s)
          } catch (e) {
            this.onerror(e)
          } finally {
            i.unlock()
          }
      }),
      t.on("upload", (e, t) => {
        ;(this.stats.uploaded++, (this.stats.bytesUploaded += t))
      }),
      t.on("download", (e, t) => {
        ;(this.stats.downloaded++, (this.stats.bytesDownloaded += t))
      }),
      t.once("ready", () => {
        if (t.writable) {
          let e = t.length,
            i = t.byteLength
          t.on("append", () => {
            const s = t.length,
              r = t.byteLength
            ;((this.stats.appends += s - e), (this.stats.bytesAppended += r - i), (e = s), (i = r))
          })
        }
      }))
  }
  async _handleWakeup(e) {
    if (!this.discovery)
      try {
        const [t, i] = await Promise.all([
          this.local.get("@local/writers-by-wakeup-topic", { wakeupTopic: e }),
          this.local.get("@local/rooms-by-wakeup-topic", { capabilities: { wakeupTopic: e } }),
        ])
        if (!t && !i) return
        const s = t?.mailbox || i?.capabilities.mailboxKeyPair.publicKey,
          r = this.protomuxWakeup.session(s, {
            active: !1,
            discoveryKey: e,
            onpeeractive: (e) => {
              t && r.announce(e, [{ key: t.key, length: 1 }])
            },
          })
        i && (await this._bumpRoom(i.roomKey))
      } catch (e) {
        this.onerror(e)
      }
  }
  async _maybeRunMigrations(e, t) {
    ;(3 === t && (await require("./lib/core/migrations/3")(this, e)),
      4 === t && (await require("./lib/core/migrations/4")(this, e)),
      5 === t && (await require("./lib/core/migrations/4")(this, e)),
      t < 8 && (await require("./lib/core/migrations/7")(e)),
      t < 9 && (await require("./lib/core/migrations/8")(e)))
  }
  async _maybeRunPostMigrations(e) {
    ;(4 === e && (await require("./lib/core/migrations/4/post")(this)),
      5 === e && (await require("./lib/core/migrations/4/post")(this)))
  }
  async _ensureMigrated() {
    let e = 0
    const t = await this.internalEnterLocalTransaction()
    try {
      const i = await t.get("@local/migrations"),
        s = i ? i.version : 3
      ;(s < 9 &&
        ((e = s),
        await this._maybeRunMigrations(t, s),
        await t.insert("@local/migrations", { version: 9 })),
        await t.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
    const i = await this.local.find("@local/room-migrations-by-priority").toArray(),
      r = [],
      o = [],
      a = []
    for (const e of i)
      this._disabledRooms.has(s.encode(e.roomKey))
        ? a.push(e)
        : e.v3?.privateProfile
          ? r.push(e)
          : o.push(e)
    if (a.length > 0) {
      const e = await this.internalEnterLocalTransaction()
      try {
        for (const t of a) {
          const i = await e.get("@local/room-migrations", { roomKey: t.roomKey })
          ;(await e.delete("@local/room-migrations", t), await we(e, null, i))
        }
        await e.flush()
      } finally {
        await this.internalExitLocalTransaction()
      }
    }
    await Promise.all(r.map((e) => this._migrateRoom(e, { migratingRooms: o })))
    for (const e of o) this._migrateRoom(e).catch(this.onerror)
    return e
  }
  async _migrateRoom(e, { migratingRooms: t } = {}) {
    await this._roomMigrations.wait()
    try {
      const i = e.errored
      if (
        (i ||
          (this.migrationStatus || (this.migrationStatus = { remaining: 0 }),
          this.migrationStatus.remaining++,
          this.emit("migration-status", this.migrationStatus)),
        !e.timestamp)
      ) {
        const t = await this.internalEnterLocalTransaction()
        try {
          e.timestamp = Date.now()
          const i = await t.get("@local/room-migrations", e)
          ;(await t.insert("@local/room-migrations", e), await we(t, e, i), await t.flush())
        } finally {
          await this.internalExitLocalTransaction()
        }
      }
      const s = Date.now() - e.timestamp > Se
      let r = !1,
        o = !1
      try {
        e.v3 &&
          !s &&
          (await require("./lib/core/migrations/3/room")(this, e.roomKey, e.v3, {
            migratingRooms: t,
          }))
      } catch (e) {
        ;(this.onerror(e), (o = !0), (r = !!this.closing))
      }
      const a = await this.internalEnterLocalTransaction()
      try {
        const t = await a.get("@local/room-migrations", e)
        ;(o
          ? (i || (e.errored = !r), await a.insert("@local/room-migrations", e), await we(a, e, t))
          : (await a.delete("@local/room-migrations", e), await we(a, null, t)),
          await a.flush())
      } finally {
        await this.internalExitLocalTransaction()
      }
      i || (this.migrationStatus.remaining--, this.emit("migration-status", this.migrationStatus))
    } finally {
      this._roomMigrations.signal()
    }
  }
  async _closeIdleRooms() {
    const e = [],
      t = []
    for (const i of this.rooms.values())
      i.refs > 0 ||
        this.activeRooms.has(i) ||
        ++i.idleTicks < this._gcIdleTicks ||
        this.backendAPI.callsByRoomId.get(i.id) ||
        (this.rooms.delete(i.id), this._gcClosingRooms.add(i), e.push(i.close()), t.push(i))
    await Promise.allSettled(e)
    for (const e of t) this._gcClosingRooms.delete(e)
  }
  internalCloseRoom(e) {
    ;(this.rooms.delete(e.id), this._gcClosingRooms.add(e))
  }
  _debugJoinPeer(e) {
    this.logger.info("Call join:", e)
  }
  _debugLeavePeer(e) {
    this.logger.info("Call leave:", e)
  }
  debugPauseGC() {
    this._gcTimer && (clearInterval(this._gcTimer), (this._gcTimer = null))
  }
  debugResumeGC() {
    this._gcTimer ||
      (this._gcTimer = setInterval(this._closeIdleRooms.bind(this), this._gcInterval))
  }
  async internalDiscoveryCompleted(e, t) {
    try {
      const e = this._discovering.get(t)
      if (!e) return
      const i = await this.internalEnterLocalTransaction()
      try {
        const t = e.roomInfo.capabilities.discoveryKeyPair.publicKey
        e.room && this.internalReleaseRoom(e.room)
        const s = await i.get("@local/background-sync", { discoveryPublicKey: t })
        ;(s &&
          (await i.insert("@local/background-sync", { ...s, lastSynced: Date.now(), dirty: !1 })),
          await i.flush())
      } finally {
        await this.internalExitLocalTransaction()
      }
      ;(this._discovering.delete(t), this._discoverySemaphore.signal())
    } catch (e) {
      this.onerror(e)
    }
  }
  async _startDiscoverySwarming(e, t) {
    if (!(await this._discoverySemaphore.wait())) return !1
    if (this.closing) return !1
    const i = this._discovering.get(t)
    return (
      !!i &&
      (this.swarmWorker.startDiscoverySwarming(e, { roomId: t }),
      i.updated && this.swarmWorker.notifyDiscoveryUpdate(e),
      !0)
    )
  }
  async _refreshDiscoverySwarming() {
    if ((await this._discoveryLock.lock()) && !this.closing)
      try {
        const e = await this.internalEnterLocalTransaction()
        try {
          for await (const { discoveryPublicKey: t } of e.find("@local/background-sync-by-dirty", {
            reverse: !0,
          })) {
            const e = await this.local.get("@local/rooms-by-discovery", {
              capabilities: { discoveryKeyPair: { publicKey: t } },
            })
            if (!e) continue
            const i = s.encode(e.discoveryKey)
            if (this._discovering.has(i)) continue
            const r = this.internalOpenExistingRoom(e.roomKey)
            ;(this._discovering.set(i, { updated: !1, room: r, roomInfo: e }),
              this._startDiscoverySwarming(e.capabilities.swarmTopic, i) ||
                this.internalReleaseRoom(r))
          }
          await e.flush()
        } finally {
          await this.internalExitLocalTransaction()
        }
      } catch (e) {
        this.onerror(e)
      } finally {
        this._discoveryLock.unlock()
      }
  }
  async scheduleDiscoverySwarmingFromDiscoveryKey(e) {
    const t = await this.local.get("@local/rooms-by-discovery-key", { discoveryKey: e })
    if (t) return this.scheduleDiscoverySwarming(t.capabilities.discoveryKeyPair.publicKey)
  }
  async scheduleDiscoverySwarming(e) {
    const t = await this.internalEnterLocalTransaction()
    try {
      const i = await t.get("@local/background-sync", { discoveryPublicKey: e }),
        s = i?.lastSynced || 0
      ;(await t.insert("@local/background-sync", {
        discoveryPublicKey: e,
        timestamp: Date.now(),
        lastSynced: s,
        dirty: !0,
      }),
        await t.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
    this._refreshDiscoverySwarming()
  }
  async internalBlobsActiveInRoom(e) {
    const t = s.encode(e),
      i = this.rooms.get(t)
    if (!i) return
    const r = await this.local.get("@local/rooms", { roomKey: e })
    if (!r) return
    const { capabilities: o } = r
    if (!o || !o.mailboxKeyPair) return
    try {
      await i.ready()
    } catch {
      return
    }
    if (!i.externalBlobs) return
    const a = await this._getMirrorKeys(e, r)
    i.closing ||
      this.blindPeering.addCoreBackground(i.externalBlobs.core, {
        target: o.mailboxKeyPair.publicKey,
        keys: a,
        pick: 1,
      })
  }
  async _getMirrorKeys(e, t) {
    if (this.debug.disableMirrors) return []
    if (!e) return this._blindPeerKeys
    const i = t || (await this.local.get("@local/rooms", { roomKey: e }))
    return i && i.config.blindPeers?.length ? i.config.blindPeers : this._blindPeerKeys
  }
  _relayThrough(e) {
    return e || this.swarm.dht.randomized ? this._relayKeys : null
  }
  internalVerboseLog(...e) {
    if (this.isNightly || this.isTesting) return
    const t = new Date()
    this.logger.info(t, ...e)
  }
  internalTime(...e) {
    this.isTesting || console.time(...e)
  }
  internalTimeLog(...e) {
    this.isTesting || console.timeLog(...e)
  }
  internalTimeEnd(...e) {
    this.isTesting || console.timeEnd(...e)
  }
  _handleConnection(e) {
    if (this.closing) return void e.on("error", () => {})
    const t = s.encode(e.publicKey),
      i = s.encode(e.remotePublicKey),
      r = Date.now()
    let o = 0
    this.internalVerboseLog(
      `got ${e.isInitiator ? "client" : "server"} connection from ${t} to ${i}`,
    )
    let a = null
    ;(e.once("error", (e) => {
      a = e
    }),
      e.once("close", () => {
        this.internalVerboseLog(
          `connection closed to ${i}, with err: ${a} (${Date.now() - r} ms ${o} bytes recv)`,
        )
      }),
      e.on("data", (e) => {
        o += e.byteLength
      }),
      this.protomuxWakeup.addStream(e),
      this.rootCorestore.replicate(e))
  }
  async _createInitialIdentity() {
    const t = e.generateMnemonic(),
      i = this.tasks.restoreIdentity(Be, { mnemonic: t, recover: !1 })
    await i.run()
  }
  async _loadIdentity() {
    let e = await this.local.get("@local/identity")
    if (
      (e || (await this._createInitialIdentity()),
      (e = await this.local.get("@local/identity")),
      this.privateProfile)
    )
      return
    const t = await this.local.get("@local/rooms", { roomKey: e.privateProfileKey })
    ;((this.privateProfile = await this.internalOpenPrivateProfile({ opening: !0 })),
      this._shortenDeviceAttestations().catch(this.onerror),
      this.swarmWorker.startActiveSwarming(t.capabilities.swarmTopic),
      await this.internalRefreshUnreads())
  }
  async internalResyncPrivateProfile() {
    if (!this.privateProfile) return null
    const e = []
    for await (const t of this.privateProfile.db.find("@keet-private-id/rooms"))
      (await this.local.get("@local/rooms", { roomKey: t.roomKey })) || e.push(t)
    for (const t of e) this.tasks.joinRoomKeys(t.roomKey, { keys: t }).runBackground()
  }
  async _shortenDeviceAttestations() {
    const e = await this.local.get("@local/identity")
    if (!e) return
    const i = s.encode(e.privateProfileKey)
    if (!(await this.internalFeatureSupported(i, I.SHORTEN_ATTESTATION))) return
    const r = this.privateProfile.base.local.key,
      o = f.decode(t, e.deviceProof).chain.length
    for await (const i of this.privateProfile.db.find("@keet-private-id/device-identities")) {
      const s = k.equals(i.deviceKey, r),
        a = f.decode(t, i.deviceProof),
        n = a.chain.length,
        l = a.chain[n - 1].publicKey
      if (!s && o + 1 < n) {
        const t = He(e, i.deviceKey, l)
        await this.privateProfile.dispatch("@keet-private-id/shorten-device-attestation", {
          deviceKey: i.deviceKey,
          deviceProof: t.deviceProof,
          writerProof: t.privateProfileIdentity.proof,
        })
      }
      if (s && f.decode(t, i.deviceProof).chain.length < o) {
        const t = await this.internalEnterLocalTransaction()
        try {
          const s = { ...e, deviceProof: i.deviceProof, proofShortenedAt: Date.now() }
          ;(await t.insert("@local/identity", s), await t.flush())
        } finally {
          await this.internalExitLocalTransaction()
        }
      }
    }
  }
  async internalDispatchUpgrade(e, t) {
    if (!e || !t) return
    const i = this.internalOpenExistingRoom(e)
    try {
      const e = this.batch(i.id)
      if (0 === (await e.dispatch("@keet/upgrade", { version: t }))) return
      if (t >= 14) {
        const s = await i.db.get("@keet/system-digest"),
          r = "@keet/migrate-abi",
          o = {
            header: { timestamp: e.timestamp },
            from: s?.abi.version,
            to: t,
            buildType: this.buildType,
          },
          a = (await this.contractBundles.loadBundle(t)).encodeOperation(r, o)
        await e.append(a)
      }
      await e.flush()
    } finally {
      this.internalReleaseRoom(i)
    }
  }
  async _bumpCache() {
    try {
      if (!(await this._cacheLock.lock())) return
      try {
        await this._processCache()
      } finally {
        this._cacheLock.unlock()
      }
    } catch (e) {
      this.onerror(e)
    }
  }
  async _processCache() {
    const e = []
    for (; this._pendingTraces.length > 0; ) {
      const t = this._pendingTraces.pop()
      e.push(this._updateCache(t))
    }
    const t = await Promise.all(e),
      i = await this.internalEnterLocalTransaction()
    try {
      for (const e of t) e && (await i.insert(e.collection, e.record))
      await i.flush()
    } finally {
      await this.internalExitLocalTransaction()
    }
  }
  async _updateCache({ roomKey: e, collection: t, record: i }) {
    if ("@keet/members" === t) {
      if (i?.profile.avatar) {
        const t = this.internalOpenExistingRoom(e)
        try {
          await z(t.blobs, i.profile.avatar)
        } finally {
          this.internalReleaseRoom(t)
        }
      }
      return { collection: "@local/members", record: i }
    }
    return "@keet/devices" === t
      ? { collection: "@local/devices", record: { roomKey: e, device: i } }
      : null
  }
  _cacheRoomReads(e, t, i) {
    ;(this._pendingTraces.push({ roomKey: e, collection: t, record: i }), this._bumpCache())
  }
  _autobaseOpen(e, t) {
    let i = null
    t.internal || (i = (e, i) => this._cacheRoomReads(t.key, e, i))
    const s = { autoUpdate: !0, extension: !1, trace: i },
      r = x.autobaseOpen(e, t),
      o = ee.bee(e, { hyperdb: s })
    return { ...o, core: o.cores.db, legacy: r, roomKey: t.key }
  }
  _autobaseClose(e) {
    return Promise.all([e.db.close(), e.blobs.close(), e.legacy.close()])
  }
  async _autobaseUpdate(e, t) {
    const i = new U(this, e, t.base, t)
    await i.run()
    const r = s.encode(t.base.discoveryKey)
    i.runPostUpdate()
    const o = this._discovering.get(r)
    o && ((o.updated = !0), this.swarmWorker.notifyDiscoveryUpdate(r))
  }
  async _autobaseApply(e, t, i, { dryRun: s = !1 } = {}) {
    const { blobs: r, db: o, core: a, legacy: n, roomKey: l } = t,
      c = s ? null : this._boundAutobaseTrace
    let h = !1,
      d = 0
    if (!(a.length > 0)) {
      if (((d = await N.autobaseApply(e, n, i)), d === e.length)) return !0
      h = n.core.length > 0
    }
    const y = o.transaction(),
      u = r.batch()
    let p = null,
      m = !1,
      g = null
    try {
      if (h) {
        const t = e[d]
        if (s || t?.optimistic) return !0
        ;(i.preferFastForward(), await N.migrate(this.store, i.base, n, y, u, this.contractBundles))
      }
      const r = await y.get("@keet/system-digest")
      if (r) {
        const t = this.isNightly ? this.abi.max : this.abi.production
        if (r.abi.version > t) {
          if (s) return !1
          i.interrupt({ unsupportedABI: r.abi.version, digest: r, nodes: e })
        }
      }
      g = r ? r.abi.version : this.abi.autoupgrade
      let o = await this.contractBundles.loadBundle(g),
        a = await this.contractBundles.loadRouter(g)
      y.setDefinition(o.spec.contract.hyperdb)
      for (let r = d; r < e.length; r++) {
        const n = e[r]
        if (!(f.decode(f.uint, n.value) < 2))
          try {
            const r = se(n.value)
            this._maybeLogAutobaseApply(r, s, e.length, g, l)
            const h = Ge(y, u, n, i, t, a, { dryRun: s, trace: c, abi: this.abi })
            ;(await a.dispatch(r, h),
              r &&
                "@keet/upgrade" === r.name &&
                ((g = r.value.version),
                (o = await this.contractBundles.loadBundle(g)),
                (a = await this.contractBundles.loadRouter(g)),
                y.setDefinition(o.spec.contract.hyperdb)))
          } catch (e) {
            if (te.isContractError(e)) throw e
            if (re.isDispatchError(e)) throw te.DISPATCH_ERROR(e.message)
            throw e
          }
      }
    } catch (t) {
      "BUNDLE_NOT_AVAILABLE" === t.code
        ? i.interrupt({ unsupportedABI: t.abi, digest: null, nodes: e })
        : te.isContractError(t)
          ? ((m = !0), (t.abi = g), (t.roomKey = l), (s && !this.isTesting) || this.onerror(t))
          : (p = t)
    }
    const b = !p && !m && y.updated()
    if (
      (s || p || m
        ? await Promise.all([u.close(), y.close()])
        : await Promise.all([u.flush(), y.flush()]),
      p)
    )
      throw p
    return b
  }
  _maybeLogAutobaseApply(e, t, i, r, o) {
    try {
      const a = s.encode(o),
        n = this.rooms.get(a),
        l = n ? n.getQuickStats() : null
      this.logger.verboseDebug("[autobaseApply]", {
        "op.name": e.name,
        "op.id": e.id,
        dryRun: t,
        "nodes.length": i,
        abi: r,
        roomId: a,
        "roomStats.view": l?.view,
        "roomStats.system": l?.system,
        "roomStats.base": l?.base,
      })
    } catch (e) {
      console.error("Failed to log autobase apply", e)
    }
  }
  _autobaseTrace({ type: e, value: t }) {
    if ("@keet/add-device" !== e) return
    const { request: i, response: s, invitation: r } = t,
      o = r?.metadata.expiration
    if (o > 0 && o < Date.now()) return
    const a = i.member.profile.mailbox,
      n = i.keyPair
    a && n && this._processAutobaseTrace(a, n, s).catch(this.onerror)
  }
  async _processAutobaseTrace(e, t, i) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const s = await this.local.get("@local/identity"),
      r = await this.local.get("@local/rooms", { roomKey: s.privateProfileKey })
    k.equals(e, r.capabilities.mailboxKeyPair.publicKey) ||
      this.tasks
        .joinRoomResponse(t.publicKey, { mailbox: e, keyPair: t, response: i })
        .runBackground()
  }
  async _backgroundDrain(e) {
    let t = await e.next()
    try {
      for (; !t.done && !this.closing; ) {
        const i = await t.value
        if (this.closing) return
        t = await e.next(i)
      }
    } finally {
      !t.done && e.return && (await e.return())
    }
  }
  async _safeLoadLegacyUnreads() {
    try {
      const e = []
      for await (const t of this.local.find("@local/rooms")) {
        const i = await this.local.get("@local/unreads", { roomKey: t.roomKey })
        i && e.push(i)
      }
      return e
    } catch {
      return []
    }
  }
  async *_migrateUnreadAnchors() {
    const e = yield this._safeLoadLegacyUnreads(),
      t = yield this.internalEnterLocalTransaction()
    try {
      const i = this.unreads.write(),
        s = Date.now()
      for (const { roomKey: r, count: o } of e)
        (i.tryPut(r, { length: o + 1, timestamp: s }),
          yield t.delete("@local/unreads", { roomKey: r }))
      ;(yield i.flush(), yield t.flush())
    } finally {
      yield this.internalExitLocalTransaction()
    }
  }
  async *_internalRefreshUnreads() {
    if (!this.privateProfile) return
    let e = yield this.privateProfile.db.get("@keet/system-digest")
    if (
      e &&
      (yield this.privateProfile.internalEnsureUpgraded({ opening: !0 }),
      (e = yield this.privateProfile.db.get("@keet/system-digest")),
      D.isFeatureSupported(e.abi.version, D.FEATURES.UNREADS) && (yield this._unreadsLock.lock()))
    )
      try {
        const t = []
        let i = !0
        const s = D.isFeatureSupported(e.abi.version, D.FEATURES.V2_UNREADS)
            ? "@keet-private-id/unreads-v2"
            : "@keet-private-id/unreads",
          r = yield this.privateProfile.db.find(s).toArray()
        for (const { deviceKey: e, key: s } of r)
          (k.equals(e, this.privateProfile.base.local.key) && (i = !1), t.push(s))
        if (this.unreads && Xe(t, this.unreads.keys)) return
        this.unreads && (yield this.unreads.close(), (this.unreads = null))
        const o = this.privateProfile,
          a = o.base.encryptionKey,
          n = this.store.namespace(o.key),
          l = n.namespace(_e)
        yield n.close()
        const c = yield o.base.wakeupCapability && o.base.wakeupCapability.key
        ;((this.unreads = new E(l, t, {
          blindPeering: this.blindPeering,
          encryptionKey: a,
          target: c,
        })),
          this.unreads.on("remote-update", () => this.emit("unread-anchors-changed")),
          yield this.unreads.ready(),
          i &&
            (yield this._backgroundDrain(this._migrateUnreadAnchors()),
            yield this.privateProfile.dispatch("@keet-private-id/add-unreads-key", {
              key: this.unreads.local.key,
            })))
      } finally {
        this._unreadsLock.unlock()
      }
  }
  internalRefreshUnreads() {
    return this._disableUnreadSync
      ? Promise.resolve()
      : this._backgroundDrain(this._internalRefreshUnreads())
  }
  async internalLoadDiscoveryCore() {
    try {
      const e = await this.privateProfile.db.get("@keet-private-id/identity")
      if (!e || !e.discoveryCoreKey || this.closing) return
      if (this.discoveryCore) return
      ;((this.discoveryCore = this.store.get({ key: e.discoveryCoreKey })),
        await this.discoveryCore.ready(),
        this.blindPeering.addCoreBackground(this.discoveryCore, { pick: 3 }))
    } catch (e) {
      this.onerror(e)
    }
  }
  async internalCreateDiscoveryCore(e) {
    const t = e.getProfileDiscoveryEncryptionKey(),
      i = { version: 2, signers: [{ publicKey: e.profileDiscoveryKeyPair.publicKey }] }
    return (
      this.discoveryCore && (await this.discoveryCore.close()),
      (this.discoveryCore = this.store.get({
        keyPair: e.profileDiscoveryKeyPair,
        valueEncoding: de,
        encryptionKey: t,
        manifest: i,
      })),
      await this.discoveryCore.ready(),
      this.blindPeering.addCoreBackground(this.discoveryCore, { pick: 3 }),
      (this.discoveryCoreSession = this.swarm.join(this.discoveryCore.discoveryKey, {
        server: !1,
        client: !0,
      })),
      !0
    )
  }
  async internalDryRun(e, t, i) {
    const s = i.map((t, i) => ({
      length: e.base.local.length + 1 + i,
      from: e.base.local,
      value: t,
      heads: [],
    }))
    return !!(await this._autobaseApply(s, t, e.base, { dryRun: !0 }))
  }
  internalGetHeads(e, t) {
    const i = []
    let s = !1
    if (t)
      for (const r of t.base.heads())
        e && k.equals(r.key, e.key)
          ? ((s = !0), i.push({ key: r.key, length: Math.max(r.length, e.length) }))
          : i.push(r)
    return (e && !s && i.push(e), i)
  }
  async internalGetKeyPair(e) {
    const t = await this.local.get("@local/writers-by-room-key", { roomKey: e })
    return t ? t.keyPair : null
  }
  async internalGetMailbox(e) {
    const t = await this.local.get("@local/rooms", { roomKey: e })
    return t
      ? { key: t.capabilities.mailboxKeyPair.publicKey, discoveryKey: t.capabilities.wakeupTopic }
      : null
  }
  async internalGetEncryptionKey(e) {
    const t = await this.local.get("@local/rooms", { roomKey: e })
    return !t || t.status.isRemoved ? null : t.encryptionKey
  }
  internalReleaseRoom(e) {
    if (0 === e.refs) throw new Error("Cannot release a room that was not previously opened")
    e.refs -= 1
  }
  internalOpenExistingRoom(e, t = {}) {
    return this.internalOpenRoom(s.decode(e), null, t)
  }
  async internalOpenPrivateProfile({ opening: e = !1 } = {}) {
    e || this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = await this.local.get("@local/identity"),
      i = this.internalOpenExistingRoom(t.privateProfileKey, { bigBatches: !0 })
    return (
      await i.ready(),
      (this.privateProfile && k.equals(i.key, this.privateProfile.key)) ||
        (this.privateProfile && this.internalReleaseRoom(this.privateProfile),
        (this.privateProfile = this.internalOpenExistingRoom(t.privateProfileKey, {
          bigBatches: !0,
        })),
        (this.unreadsLoading = this.internalRefreshUnreads().catch(() => null))),
      this.internalLoadDiscoveryCore(),
      i
    )
  }
  async internalSafeSwapPrivateProfiles() {
    try {
      const e = await this.internalOpenPrivateProfile()
      this.internalReleaseRoom(e)
    } catch (e) {
      this.onerror(e)
    }
  }
  async internalFeatureSupported(e, t) {
    const i = s.decode(e),
      r = await this.local.get("@local/rooms", { roomKey: i })
    return !!r && this.versions.isFeatureSupported(r.digest.abi.version, t)
  }
  internalOpenRoom(e, t, { fastForwardTo: i, mailbox: r, keyPair: o, bigBatches: a } = {}) {
    if (this.closing) throw new Error("Keet is closing")
    const n = s.encode(e),
      l = this.rooms.get(n)
    if (l) return ((l.idleTicks = 0), (l.refs += 1), l)
    const c = w.discoveryKey(e),
      h = this.store.namespace(c)
    let y = null,
      u = null,
      p = null
    ;(r || ((u = this.internalGetMailbox(e)), u.catch(this.onerror)),
      o || ((y = this.internalGetKeyPair(e)), y.catch(this.onerror)),
      t || ((p = this.internalGetEncryptionKey(e)), p.catch(this.onerror)),
      r && (r = { key: r, discoveryKey: w.discoveryKey(r) }))
    const m = new d(h, e, {
      backoff: this.backoff,
      update: this._autobaseUpdate.bind(this),
      apply: this._autobaseApply.bind(this),
      close: this._autobaseClose.bind(this),
      open: this._autobaseOpen.bind(this),
      encryptionKey: t || p,
      wakeupCapability: r || u,
      keyPair: o || y,
      fastForward: !this.debug.disableFastForward && i,
      ackThreshold: this.ackThreshold,
      ackInterval: this.ackInterval,
      wakeup: this.protomuxWakeup,
      optimistic: !0,
      encrypted: !0,
      bigBatches: !!a,
    })
    ;(m.on("interrupt", (e) => {
      ;(this.logger.warn("room=", m.id, "got interrupted"), this.emit("room-interrupted", n, e))
    }),
      m.on("warning", (t) => {
        ;(this.logger.warn("[autobase warning] room=", m.id, "error=", t),
          this.onerror(t, { roomKey: e, prefix: "AUTOBASE_WARNING" }))
      }),
      m.on("error", (t) => {
        ;(this.logger.warn("[autobase error] room=", m.id, "error=", t),
          this.onerror(t, { roomKey: e, prefix: "AUTOBASE_ERROR" }))
      }))
    const g = new je(this, n, e, c, m)
    return (this.rooms.set(n, g), (g.refs += 1), g)
  }
  async updateDevice({ name: e } = {}) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = { swarmKey: this.swarm.keyPair.publicKey, name: e, isMobile: this.isMobile },
      i = await this.internalOpenPrivateProfile()
    try {
      await i.dispatch("@keet/update-device", { profile: t })
    } finally {
      this.internalReleaseRoom(i)
    }
  }
  async internalUpdateProfile(e, { forceUpdateRegistry: t = !1, opening: i = !1 } = {}) {
    const s = await this.internalGetIdentityInfo({ opening: i })
    if (this.closing) return
    if (this._rejectAnonymousProfiles && !e.displayName && !t)
      throw new Error("A member must have a profile name")
    const r = this.internalCreateMemberUpdate(s, { profile: e }),
      o = await this.internalOpenPrivateProfile({ opening: i })
    if (this.closing) return
    let a = null
    try {
      a = await o.dispatch("@keet/update-member", { member: r })
    } finally {
      this.internalReleaseRoom(o)
    }
    ;(a || t) && r.profile.username && this.userRegistry?.internalUpdateRegistryMember(r)
  }
  async updateProfile(e = {}) {
    return (
      this.onlineBoot.opened || (await this.onlineBoot.ready()),
      this.internalUpdateProfile(e)
    )
  }
  async internalVerifyOptimisticCore(e) {
    return !(await e.getUserData("referrer"))
  }
  async internalAppendOptimisticOperation(e, t) {
    const i = this.store.get({ key: e, exclusive: !0 })
    ;(await i.ready(),
      (await this.internalVerifyOptimisticCore(i))
        ? (await i.append(t), await i.close())
        : await i.close())
  }
  async internalCreateRoom(e = {}) {
    const t = e.seed || w.randomBytes(32),
      i = e.keyPair || w.keyPair(),
      s = e.encryptionKey || w.randomBytes(32),
      r = W(i.publicKey),
      o = w.discoveryKey(r),
      a = G(r, t),
      n = a.mailboxKeyPair.publicKey,
      l = await this.internalEnterLocalTransaction()
    try {
      ;(await l.insert("@local/writers", { mailbox: n, keyPair: i, key: r, roomKey: r }),
        await l.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
    const c = {
        header: { timestamp: Date.now() },
        digest: {
          abi: { version: this.isExperimental ? this.abi.production : this.abi.autoupgrade },
          creationTime: Date.now(),
          settings: {
            roomType: 1,
            ...e.settings,
            isExperimental: this.isExperimental,
            buildType: this.buildType,
          },
        },
        capabilities: { seed: t },
        config: e.config,
        identity: e.identity,
        initialMembers: e.initialMembers,
        bootstrapDevice: e.bootstrapDevice,
      },
      h = this.tasks.createRoom(o, {
        roomKey: r,
        encryptionKey: s,
        mailbox: n,
        keyPair: i,
        genesis: c,
        isPrivateProfile: !!e.isPrivateProfile,
      })
    return (
      await h.run(),
      this.discovery &&
        this.discovery.subscribe(a.discoveryKeyPair.publicKey, { since: Date.now() }),
      this.swarmWorker.refresh().catch(ze),
      this.internalOpenExistingRoom(r)
    )
  }
  async createRoom(e) {
    this.opened || (await this.ready())
    const t = await this.internalCreateRoom(e)
    return (this.internalReleaseRoom(t), t.key)
  }
  async leaveRoom(e) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = await this.local.get("@local/identity"),
      i = this.internalOpenExistingRoom(e)
    await i.ready()
    try {
      await i.dispatch("@keet/remove-member", { key: t.publicKey })
    } catch (e) {
      this.onerror(e)
    } finally {
      this.internalReleaseRoom(i)
    }
    await this.privateProfile.dispatch("@keet-private-id/update-room-status", {
      roomKey: e,
      key: Q.IsActive,
      status: { isActive: !1 },
    })
  }
  async activateRoom(e) {
    if (this.closing) return
    const t = await this.local.get("@local/rooms", { roomKey: e })
    if (!t || this.closing) return
    const i = this.internalOpenExistingRoom(e)
    ;(await i.ready(),
      this.activeRooms.has(i)
        ? this.internalReleaseRoom(i)
        : (this.activeRooms.add(i),
          this.swarmWorker.startActiveSwarming(t.capabilities.swarmTopic)))
  }
  async deactivateRoom(e) {
    const t = this.internalOpenExistingRoom(e)
    if (!this.activeRooms.has(t)) return void this.internalReleaseRoom(t)
    const i = await this.local.get("@local/rooms", { roomKey: e })
    ;(this.activeRooms.delete(t),
      this.swarmWorker.stopActiveSwarming(i.capabilities.swarmTopic),
      this.internalReleaseRoom(t),
      this.internalReleaseRoom(t))
  }
  async internalGetIdentityInfo({ opening: e = !1 } = {}) {
    e || this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = await this.local.get("@local/identity")
    if (!t) throw new Error("Identity does not exist")
    const i = await this.local.get("@local/rooms", { roomKey: t.privateProfileKey })
    if (!i) throw new Error("Private profile does not exist")
    return { identity: t, privateProfile: i }
  }
  internalGetMemberUpdate(e, { profile: t } = {}) {
    const i = e.privateProfile
    return i?.selfMember
      ? i.selfMember.proofs
        ? {
            profile: i.selfMember.profile,
            attestation: {
              memberKey: i.selfMember.key,
              proof: i.selfMember.proofs.profileAttestation,
            },
          }
        : this.internalCreateMemberUpdate(e)
      : null
  }
  internalAttestData(t, i) {
    return e.attestData(t, i.deviceKeyPair, i.deviceProof)
  }
  internalCreateDeviceUpdate(e, t, { isPrivateProfile: i = !1, buildType: s = null } = {}) {
    const r = e.identity
    let o = null
    e.privateProfile?.selfDevice && i && (o = e.privateProfile.selfDevice.profile.name)
    const a = e.privateProfile?.digest.settings.buildType,
      n = ye.Production === a
    return {
      profile: {
        swarmKey: this.swarm.keyPair.publicKey,
        name: o,
        isMobile: this.isMobile,
        buildType: n && null === s ? null : a,
      },
      attestation: { memberKey: r.publicKey, proof: this.internalAttestData(t, r) },
    }
  }
  internalCreateMemberUpdate(t, { profile: i } = {}) {
    const s = t.privateProfile?.selfMember?.profile || null,
      r = t.privateProfile?.capabilities.mailboxKeyPair.publicKey || i.mailbox,
      o = t.identity,
      a = { ...s, ...i, mailbox: r, timestamp: Date.now() },
      n = ne("@keet/member-profile", a)
    return {
      profile: a,
      attestation: {
        memberKey: o.publicKey,
        proof: e.attestData(n, o.deviceKeyPair, o.deviceProof),
      },
    }
  }
  internalGetLinkInfo(e) {
    try {
      return this.internalDecodeInvitation(e)
    } catch {}
    try {
      return this.internalDecodeContactInfo(e)
    } catch {}
    return null
  }
  internalDecodeInvitation(e) {
    try {
      const t = "string" == typeof e ? v.decode(e) : e
      return le("@keet/sendable-invitation", t)
    } catch {
      throw ge.INVALID_INVITE(`Invalid invite: ${e}`)
    }
  }
  internalDecodeContactInfo(e) {
    try {
      const t = k.isBuffer(e) ? e : k.from(e, "base64")
      return le("@keet/member-contact-info", t)
    } catch {
      throw ge.INVALID_CONTACT_INFO(`Invalid contact info: ${e}`)
    }
  }
  internalEncodeContactInfo(e) {
    return ne("@keet/member-contact-info", e)
  }
  internalCreateContactInfoStruct(t, i, s = {}) {
    const r = { ...i.selfMember.profile, ...s },
      o = ne("@keet/member-profile", r),
      a = {
        profile: r,
        attestation: {
          memberKey: i.selfMember.key,
          proof: e.attestData(o, t.deviceKeyPair, t.deviceProof),
        },
      }
    return this.internalEncodeContactInfo(a)
  }
  async wakeup() {
    ;((this.state = ve.WAKEUP),
      this.internalVerboseLog("[Keet Core]", "await .ready"),
      this.opened || (await this.ready()))
    const e = [this.resume(), this.swarmWorker.background()]
    ;(this.internalVerboseLog("[Keet Core]", "await all promise"),
      await Promise.allSettled(e),
      this.internalVerboseLog("[Keet Core]", "promise settled"))
  }
  async presuspend() {
    ;((this.state = ve.PRESUSPEND),
      this.opened || (await this.ready()),
      this.swarmWorker.setServerMode(!1))
  }
  async suspend() {
    if ((this.internalVerboseLog("[Keet Core]", "suspend called"), this.state === ve.SUSPEND))
      return
    ;((this.state = ve.SUSPEND),
      this.opened || (await this.ready()),
      this.internalVerboseLog("[Keet Core]", "suspend post ready"))
    const e = [this._flushDHTCache()]
    ;(this.blindPeering &&
      (this.internalVerboseLog("[Keet Core]", "suspend adding blind peering"),
      e.push(this.blindPeering.suspend())),
      this.push &&
        (this.internalVerboseLog("[Keet Core]", "suspend push"), e.push(this.push.suspend())),
      this.discovery &&
        (this.internalVerboseLog("[Keet Core]", "suspend discovery"),
        e.push(this.discovery.suspend())),
      await Promise.allSettled(e),
      this.internalVerboseLog("[Keet Core]", "all done"),
      await this.rpcClient.suspend(),
      this.internalVerboseLog("[Keet Core]", "rpc suspended"),
      await this.swarm.suspend(),
      this.internalVerboseLog("[Keet Core]", "swarm suspend"),
      await this.files.server.suspend(),
      this.internalVerboseLog("[Keet Core]", "files suspend"),
      await this.store.suspend(),
      this.internalVerboseLog("[Keet Core]", "store suspended, all done"))
  }
  async resume() {
    if ((this.internalVerboseLog("[Keet Core]", "resume called"), this.state === ve.RESUME)) return
    ;((this.state = ve.RESUME),
      this.internalVerboseLog("[Keet Core]", "await this.ready"),
      this.opened || (await this.ready()),
      this.internalVerboseLog("[Keet Core]", "resume store"),
      await this.store.resume(),
      this.internalVerboseLog("[Keet Core]", "resume file server"),
      await this.files.server.resume(),
      this.internalVerboseLog("[Keet Core]", "resume swarm"),
      this.readOnly || (await this.swarm.resume()),
      this.internalVerboseLog("[Keet Core]", "resume rpc client"),
      await this.rpcClient.resume())
    const e = []
    ;(this.blindPeering && !this.readOnly && e.push(this.blindPeering.resume()),
      this.push && e.push(this.push.resume()),
      this.discovery && e.push(this.discovery.resume()),
      this.swarmWorker.setServerMode(!0),
      e.push(this.swarmWorker.unbackground()),
      this.internalVerboseLog("[Keet Core]", "await all promises"),
      await Promise.allSettled(e),
      this.internalVerboseLog("[Keet Core]", "all promises settled"))
  }
  async _createInvitation(e, t) {
    let {
      expiration: i = 0,
      canAdmin: s = !1,
      canModerate: r = !1,
      identityPublicKey: o = null,
      requiresConfirmation: a = !1,
      readOnly: n = !1,
    } = t
    if ("string" == typeof i)
      if ("2h" === i) i = Date.now() + 72e5
      else if ("2d" === i) i = Date.now() + 1728e5
      else {
        if ("2w" !== i) throw ge.UNKNOWN_EXPIRATION(`Unknown expiration: ${i}`, { roomKey: e.key })
        i = Date.now() + 12096e5
      }
    await e.ready()
    const [{ seed: l }, c, h] = await Promise.all([
      e.db.get("@keet/room-capabilities"),
      Y(e.db, e.deviceKey),
      e.db.get("@keet/system-digest"),
    ])
    if ((r || s) && !c.status.isModerator)
      throw ge.MODERATORS_ONLY("Only moderators can create moderator invitations", {
        roomKey: e.key,
      })
    const d = G(e.key, l),
      y = this.swarm.dht.toArray({ limit: 3 })
    if (n)
      return ne("@keet/sendable-invitation", {
        readonly: {
          roomKey: e.key,
          encryptionKey: e.encryptionKey,
          fastForwardTo: e.base.getSystemKey(),
          seed: l,
        },
        additionalNodes: y,
      })
    const u = { key: e.key, encryptionKey: e.encryptionKey, fastForwardTo: e.base.getSystemKey() },
      { invitation: p, seed: m } = $(u, {
        requiresConfirmation: a,
        expiration: i,
        canModerate: r,
        canAdmin: s,
      })
    await e.dispatch("@keet/add-invitation", { invitation: p })
    const g = (await e.db.get("@keet/invitations", { id: p.id })).anchor,
      b = d.mailboxKeyPair.publicKey,
      w = {
        identity: null,
        room: null,
        contact: null,
        additionalNodes: y,
        buildType: h.settings.buildType,
      }
    return (
      o
        ? (w.identity = { mailbox: b, invitationSeed: m, anchor: g, identityPublicKey: o })
        : (w.room = { mailbox: b, invitationSeed: m, anchor: g }),
      ne("@keet/sendable-invitation", w)
    )
  }
  async createRoomInvitation(e, t = {}) {
    this.opened || (await this.ready())
    const i = s.decode(e),
      r = this.internalOpenExistingRoom(i)
    try {
      const e = await this._createInvitation(r, t)
      return v.encode(e)
    } finally {
      this.internalReleaseRoom(r)
    }
  }
  async createIdentityInvitation() {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const e = await this.local.get("@local/identity"),
      t = this.internalOpenExistingRoom(e.privateProfileKey)
    this.blindPeering.addAutobaseBackground(t.base)
    try {
      const i = await this._createInvitation(t, {
        expiration: "2h",
        canAdmin: !0,
        canModerate: !0,
        requiresConfirmation: !0,
        identityPublicKey: e.publicKey,
      })
      return v.encode(i)
    } finally {
      this.internalReleaseRoom(t)
    }
  }
  async _startPairing(e, { deviceKeyPair: t, buildType: i = null } = {}) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const r = j(e.invitationSeed),
      o = e.invitationSeed,
      a = e.mailbox,
      n = e.anchor,
      l = { key: null, seedId: s.encode(o) },
      c = await this.local.get("@local/rooms-by-mailbox", {
        capabilities: { mailboxKeyPair: { publicKey: a } },
      })
    let h = null
    if (c) {
      if (c.selfMember?.status.isRemoved) throw new Error("Removed member cannot rejoin")
      h = await this.local.get("@local/room-preferences", { roomKey: c.roomKey })
    }
    if (h && h.status.isActive && !h.status.isReadOnly) return ((l.key = s.encode(c.roomKey)), l)
    const d = w.keyPair(),
      y = await this.store.createKeyPair(J(d.publicKey)),
      u = this.store.get({ keyPair: y, active: !1 })
    await u.ready()
    const p = { key: u.key, length: 1 }
    await u.close()
    let m = null
    t && (m = { writerKey: u.key, publicKey: e.identityPublicKey, devicePublicKey: t.publicKey })
    const g = {
        id: r,
        mailbox: a,
        inviter: n,
        identity: m,
        initiator: p,
        responseKeyPair: d,
        invitationSeed: o,
        buildType: i,
      },
      b = await this.privateProfile.db.find("@keet-private-id/pending-invitations").toArray()
    for (const e of b)
      k.equals(e.mailbox, g.mailbox) &&
        (await this.privateProfile.dispatch("@keet-private-id/remove-pending-invitation", e))
    return (
      await this.privateProfile.dispatch("@keet-private-id/add-pending-invitation", {
        invitation: g,
      }),
      l
    )
  }
  async _openReadOnlyRoom({ roomKey: e, encryptionKey: t, seed: i, fastForwardTo: r }) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const { mailboxKeyPair: o, swarmTopic: a } = G(e, i),
      n = this.internalOpenRoom(e, t, { mailbox: o.publicKey, fastForwardTo: { key: r } })
    return (
      await n.ready(),
      await this.privateProfile.dispatch("@keet-private-id/add-room", {
        roomKey: e,
        encryptionKey: t,
        mailbox: o.publicKey,
        fastForwardTo: r,
        isReadOnly: !0,
      }),
      this.swarmWorker.startDiscoverySwarming(a, { roomId: s.encode(e) }),
      { key: s.encode(e), seedId: s.encode(i) }
    )
  }
  async startPairingRoom(e, t) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const i = this.internalDecodeInvitation(e),
      s = await this.local.get("@local/rooms", { roomKey: this.privateProfile.key }),
      r = i.buildType,
      o = s.digest.settings.buildType || ye.Production
    if (r && r !== o) throw ge.INVALID_BUILD_TYPE()
    if (i.readonly) return this._openReadOnlyRoom(i.readonly)
    if (!i.room) throw ge.INVALID_INVITATION_TYPE("Invalid room invitation")
    return this._startPairing(i.room, { ...t, buildType: r })
  }
  async startPairingIdentity(e) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = this.internalDecodeInvitation(e),
      i = await this.local.get("@local/rooms", { roomKey: this.privateProfile.key }),
      s = t.buildType,
      r = i.digest.settings.buildType || ye.Production
    if (s && s !== r) throw ge.INVALID_BUILD_TYPE()
    if (!t.identity) throw ge.INVALID_INVITATION_TYPE("Invalid device sync invitation")
    const o = H(t.identity.identityPublicKey),
      a = await this.store.createKeyPair(o)
    return this._startPairing(t.identity, { deviceKeyPair: a, buildType: s })
  }
  async stopPairing(e) {
    this.opened || (await this.ready())
    const t = await this.internalOpenPrivateProfile()
    try {
      if (!(await t.db.get("@keet-private-id/pending-invitations", { id: e }))) return
      await t.dispatch("@keet-private-id/remove-pending-invitation", { id: e })
    } finally {
      this.internalReleaseRoom(t)
    }
  }
  async rotateWriter(e) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const [t, i, s] = await Promise.all([
      this.local.get("@local/writers-by-room-key", { roomKey: e }),
      this.local.get("@local/rooms", { roomKey: e }),
      this.privateProfile.db.get("@keet-private-id/rooms", { roomKey: e }),
    ])
    if (!i || !t || !s) return
    const r = await this.internalEnterLocalTransaction()
    try {
      ;(await r.delete("@local/writers", t), await r.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
    this.tasks.joinRoomKeys(s.mailbox, { keys: s }).runBackground()
  }
  async getRecoveryPhrase() {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const e = (await this.local.get("@local/identity")).entropy
    return e ? C({ entropy: e }) : null
  }
  async getEncryptedRecoveryPhrase(e) {
    if ("string" != typeof e) throw new Error("Password required")
    if (0 === e.length) throw new Error("Password should not be empty")
    const t = await this.getRecoveryPhrase()
    if ("string" != typeof t) throw new Error("Recovery phrase reading failed")
    if (0 === t.length) throw new Error("Recovery phrase reading failed")
    return O.encryptPhrase(t, e)
  }
  decryptEncryptedRecoveryPhrase(e, t) {
    if (!ArrayBuffer.isView(e)) throw new Error("Encrypted phrase should be an ArrayBuffer view")
    if (0 === e.byteLength) throw new Error("Encrypted phrase should not be empty")
    if ("string" != typeof t) throw new Error("Password required")
    if (0 === t.length) throw new Error("Password should not be empty")
    return O.decryptPhrase(e, t)
  }
  freeRecoveryPhrase(e) {
    O.free(e)
  }
  async storeRecoveryPhrase(t) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const i = await e.from({ mnemonic: t }),
      s = await this.local.get("@local/identity"),
      r = await this.local.get("@local/rooms", { roomKey: s.privateProfileKey })
    if (!k.equals(s.publicKey, i.identityPublicKey)) throw new Error("Wrong recovery phrase")
    if (!r.identity.discoveryCoreKey) {
      if ((await this.internalCreateDiscoveryCore(i), this.discoveryCore.length > 0)) {
        const e = Math.floor((Date.now() - qe) / 1e3)
        await this.discoveryCore.truncate(this.discoveryCore.length, e)
      }
      ;(await this.discoveryCore.append({
        version: 1,
        timestamp: Date.now(),
        key: r.roomKey,
        mailbox: r.capabilities.mailboxKeyPair.publicKey,
      }),
        await this.privateProfile.dispatch("@keet-private-id/update-identity", {
          identity: { publicKey: i.identityPublicKey, discoveryCoreKey: this.discoveryCore.key },
        }))
    }
  }
  async confirmRecoveryPhrase({ persist: e = !1 } = {}) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = await this.local.get("@local/identity")
    ;(e || (t.entropy = null), (t.status.isConfirmed = !0))
    const i = await this.internalEnterLocalTransaction()
    try {
      ;(await i.insert("@local/identity", t), await i.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
    this.emit("identity-changed")
  }
  async restoreIdentity(t, { create: i = !0 } = {}) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const [s] = await Promise.all([this.local.get("@local/identity"), e.from({ mnemonic: t })]),
      r = w.discoveryKey(s.privateProfileKey),
      o = this.tasks.restoreIdentity(r, {
        mnemonic: t,
        recover: !1 === i,
        profileDiscoveryTimeout: this._profileDiscoveryTimeout,
        buildType: this.buildType,
      })
    await o.run()
    const a = await this.local.get("@local/identity"),
      n = await this.local.get("@local/rooms", { roomKey: a.privateProfileKey })
    ;((this.privateProfile = await this.internalOpenPrivateProfile()),
      this.swarmWorker.startActiveSwarming(n.capabilities.swarmTopic))
  }
  async acceptJoinRequest(e, t) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const i = this.internalOpenExistingRoom(e)
    try {
      const s = await i.db.get("@keet/join-requests", { deviceKey: t })
      if (!s) return
      const r = s.request
      let o = null
      if (r.identity) {
        const i = await this.local.get("@local/identity")
        if (!k.equals(i.privateProfileKey, e))
          throw new Error(
            "Accepting an identity device join request on a room that is not your private profile",
          )
        o = He(i, t, r.identity.devicePublicKey)
      }
      await i.dispatch("@keet/accept-join-request", { deviceKey: t, identity: o })
    } finally {
      this.internalReleaseRoom(i)
    }
  }
  async rejectJoinRequest(e, t) {
    this.opened || (await this.ready())
    const i = this.internalOpenExistingRoom(e)
    try {
      if (!(await i.db.get("@keet/join-requests", { deviceKey: t })))
        throw new Error("Join request for that device key does not exist")
      await i.dispatch("@keet/reject-join-request", { deviceKey: t })
    } finally {
      this.internalReleaseRoom(i)
    }
  }
  async _sendContactRequest(e) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const t = await this.internalOpenPrivateProfile()
    try {
      this.tasks.sendContactRequest(e.memberKey, e).runBackground()
    } finally {
      this.internalReleaseRoom(t)
    }
  }
  async sendContactRequestFromContact(e, { message: t }) {
    const i = (e = this.internalDecodeContactInfo(e)).attestation.memberKey,
      s = e.attestation.memberKey
    return this._sendContactRequest({ roomKey: i, memberKey: s, message: t, contactInfo: e })
  }
  async sendContactRequest(e, t, { message: i }) {
    return this._sendContactRequest({ roomKey: e, memberKey: t, message: i })
  }
  async blockContact(e) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const [t, i, s] = await Promise.all([
        this.privateProfile.db.findOne("@keet-private-id/contact-requests-by-member", {
          gte: { memberKey: e },
          lte: { memberKey: e },
        }),
        this.local.get("@local/contacts", { memberKey: e }),
        this.local.get("@local/members", { key: e }),
      ]),
      r = i?.mailbox || s?.profile.mailbox || t?.sender?.mailbox
    if (!r) throw new Error("Cannot find contact")
    await this.privateProfile.dispatch("@keet-private-id/add-contact", {
      contact: { memberKey: e, mailbox: r, status: { isBlocked: !0 } },
    })
  }
  async unblockContact(e) {
    this.onlineBoot.opened || (await this.onlineBoot.ready())
    const [t, i, s] = await Promise.all([
        this.privateProfile.db.findOne("@keet-private-id/contact-requests-by-member", {
          gte: { memberKey: e },
          lte: { memberKey: e },
        }),
        this.local.get("@local/contacts", { memberKey: e }),
        this.local.get("@local/members", { key: e }),
      ]),
      r = i?.mailbox || s?.profile.mailbox || t?.sender?.mailbox
    if (!r) throw new Error("Cannot find contact")
    await this.privateProfile.dispatch("@keet-private-id/add-contact", {
      contact: { memberKey: e, mailbox: r, status: { isBlocked: !1 } },
    })
  }
  async _flushDHTCache() {
    if (this.readOnly) return
    const e = this.swarm.dht.toArray({ limit: Me })
    try {
      await this._setCachedDHTNodes(e)
    } catch (e) {
      this.onerror(e)
    }
  }
  async getDHTSettings() {
    const e = (await this.local.get("@local/hyperdht")) || {}
    return {
      nodes: e.nodes || [],
      concurrency: e.concurrency || Z.concurrency,
      maxWindow: e.maxWindow || Z.maxWindow,
      randomPunchInterval: e.randomPunchInterval || Z.randomPunchInterval,
      connectionKeepAlive: e.connectionKeepAlive || Z.connectionKeepAlive,
    }
  }
  async _setCachedDHTNodes(e) {
    const t = await this.internalEnterLocalTransaction()
    try {
      const i = { ...(await this.getDHTSettings()), nodes: e }
      ;(await t.insert("@local/hyperdht", i), await t.flush())
    } finally {
      await this.internalExitLocalTransaction()
    }
  }
  batch(e) {
    return new We(this, e)
  }
  async dispatch(e, t, i) {
    this.opened || (await this.ready())
    const s = this.internalOpenExistingRoom(e)
    try {
      return await s.dispatch(t, i)
    } catch (e) {
      this.onerror(e)
    } finally {
      this.internalReleaseRoom(s)
    }
  }
  async dispatchBatch(e, t) {
    this.opened || (await this.ready())
    const i = this.internalOpenExistingRoom(e)
    try {
      await i.dispatchBatch(t)
    } finally {
      this.internalReleaseRoom(i)
    }
  }
  async selfDestruct({ confirm: e } = {}) {
    if ("do not go gentle into that good night" !== e)
      throw new Error("Must confirm self destruction")
    ;(await this.ready(), await this.close())
    const t = Math.random().toString(16).slice(2),
      i = K.join(this.dir, "gc", t)
    try {
      await P.promises.mkdir(i, { recursive: !0 })
    } catch {}
    for (const e of ["cores", "db", "cache", "app-references"])
      try {
        await P.promises.rename(K.join(this.dir, e), K.join(i, e))
      } catch {}
    await P.promises.rm(i, { recursive: !0 })
  }
  static from(e, t) {
    const i = T.migrate(e),
      s = i.active() || i.create({ name: "default" })
    i.gc({ delay: ke })
    const r = new this(s.storage, {
      ...t,
      logout: async () => {
        ;(i.remove(s),
          await new Promise((e) => setTimeout(e, 1e3)),
          await r.close(),
          i.gc({ delay: ke }))
      },
    })
    return { profiles: i, keet: r }
  }
  static registerRPC(e, t, i) {
    return new A(e, t, i)
  }
}
const $e = {
  interrupt: ze,
  addWriter: ze,
  removeWriter: ze,
  ackWriter: ze,
  preferFastForward: ze,
  removeable: Qe,
  createAnchor: (e, t) => ({ key: e, length: t }),
}
function Ge(e, t, i, s, r, o, { abi: a, trace: n, dryRun: l = !1 } = {}) {
  return new ee.OpContext(e, t, {
    fork: r.core.fork,
    writer: { key: i.from.key, seq: i.length - 1 },
    genesis: 0 === r.core.length,
    bootstrap: s.bootstrap,
    router: o,
    hostcalls: l ? $e : s,
    dryRun: l,
    abi: a,
    trace: n,
  })
}
function He(t, i, s) {
  const r = e.attestDevice(s, t.deviceKeyPair, t.deviceProof),
    o = e.attestData(i, t.deviceKeyPair, t.deviceProof)
  return { privateProfileIdentity: { memberKey: t.publicKey, proof: o }, deviceProof: r }
}
function Je(e) {
  return (
    "@keet/add-chat-message" === e ||
    "@keet/start-call" === e ||
    "@keet/join-call" === e ||
    "@keet/leave-call" === e ||
    "@keet/end-call" === e
  )
}
function Ye(e) {
  switch (e) {
    case "nightly":
      return ye.Nightly
    case "internal":
      return ye.Internal
    case "production":
      return ye.Production
    default:
      throw ge.INVALID_BUILD_TYPE()
  }
}
function ze() {}
function Qe() {
  return !0
}
function Xe(e, t) {
  if (e.length !== t.length) return !1
  for (let i = 0; i < e.length; i++) if (!k.equals(e[i], t[i])) return !1
  return !0
}