OverviewFilesCommitsIssuesDocsReleasesPeers

keet-deobfuscated / @holepunchto / keet-core / lib / api / v1 / index.js

2639 lines · javascript ref:
const e = require("path"),
  t = require("ready-resource"),
  s = require("hypercore-id-encoding"),
  i = require("hypercore-crypto"),
  o = require("debounceify"),
  r = require("safety-catch"),
  n = require("b4a"),
  a = require("uncaughts"),
  { Readable: c, Transform: d, pipeline: h } = require("streamx"),
  { Session: l, HeapSnapshot: u } = require("bare-inspector"),
  m = require("bare-fs"),
  p = require("process-top")(),
  b = require("tiny-preferences"),
  g = require("@holepunchto/keet-call/server"),
  {
    contract: {
      hyperschema: { getEnum: y },
    },
    local: {
      hyperschema: { getEnum: f },
    },
  } = require("../../../schema/spec"),
  {
    encodeRoomRecord: k,
    encodeConfig: _,
    encodeDevice: v,
    encodeMemberRecord: w,
    encodeInvitationRecord: R,
    encodeFile: S,
    encodeIdentityPairingRequest: M,
    encodeDmRequest: I,
    encodeReaction: C,
    encodeUser: E,
    encodeContactInfo: K,
    encodeImageFile: D,
    encodeReservation: P,
    encodeFileEntry: q,
    encodeUserSettings: F,
    encodeErrorRecord: x,
    encodeNotification: A,
    encodeHyperDHTSettings: B,
    encodeCallRecord: L,
  } = require("./encode.js"),
  {
    decodeChatRangeOptions: U,
    decodeChatMessage: T,
    decodeMessageId: N,
    decodeKeyRangeOptions: O,
    decodeFile: j,
    decodeConfig: H,
    decodeImageFile: V,
    decodeFileRangeOptions: G,
    decodeUserSettings: z,
  } = require("./decode.js"),
  J = y("@keet/build-type"),
  W = y("@keet/room-type"),
  Y = y("@keet/display-type"),
  X = y("@keet/label-type"),
  $ = y("@keet/member-label-type"),
  Q = y("@keet/system-settings-keys"),
  Z = y("@keet-private-id/user-settings-key"),
  { ContactRequestStatus: ee, RoomStatus: te } = require("../../core/contract/identity/private.js"),
  { Config: se, CONFIG_INDEX_TO_KEY: ie } = require("../../core/contract/config"),
  { Settings: oe } = require("../../core/contract/internal"),
  {
    getChatMessages: re,
    getChatMessage: ne,
    getReactions: ae,
    isDMRead: ce,
    getCalls: de,
  } = require("./helpers.js"),
  { MemberStatus: he } = require("../../core/contract/members/index.js"),
  {
    MemberRoles: le,
    DM_STATUSES: ue,
    isValidPreferenceKey: me,
    getPreferenceKeyName: pe,
    CHAT_MESSAGE_REPORT_COUNT: be,
    PREFERENCES: ge,
    GENERIC_EVENTS: ye,
  } = require("@holepunchto/keet-core-api"),
  { restoreAvatar: fe } = require("../../core/contract/avatars.js"),
  { getMemberForDeviceKey: ke } = require("../../core/contract/query.js"),
  _e = require("compact-encoding"),
  { DEBUG_FLAG_INDEX_BY_KEY: ve, DHT_SETTINGS: we } = require("../../core/debug/debugging.js"),
  Re = require("./inspector.js"),
  { FEATURES: Se } = require("../../../schema/versions"),
  Me = require("./calls.js"),
  Ie = f("@local/keet-state"),
  Ce = 6e5,
  Ee = 2e4,
  Ke = "preferences_v1.json",
  De = "keet",
  Pe = "recent-rooms",
  qe = "favorite-rooms",
  Fe = "dm-rooms",
  xe = "chat-changed",
  Ae = "identity",
  Be = "private-profile",
  Le = "identity-pairing-requests",
  Ue = "migration-status",
  Te = "room-preferences",
  Ne = "unreads",
  Oe = "errors",
  je = "force-updates",
  He = {
    "keet-core": require("../../../package.json").version,
    autobase: require("autobase/package.json").version,
    hypercore: require("hypercore/package.json").version,
    hyperbee: require("hyperbee/package.json").version,
    hyperbee2: require("hyperbee2/package.json").version,
    "keet-identity-key": require("keet-identity-key/package.json").version,
    "keet-push": require("@holepunchto/keet-push/package.json").version,
    corestore: require("corestore/package.json").version,
  }
class Ve {
  constructor(e, t) {
    ;((this.api = e), (this.id = t), (this.subscriptions = []))
  }
  destroy() {
    for (const { stream: e } of this.subscriptions) e.destroy()
  }
  refresh(e) {
    for (const t of this.subscriptions) (e && e.name && e.name !== t.name) || (t.query && t.query())
  }
  push(e, t) {
    for (const s of this.subscriptions)
      (t && t.name && t.name !== s.name) || (s.map && s.stream.push(s.map(e)))
  }
  _onDestroy(e, t) {
    const s = this.subscriptions.pop()
    ;(s !== e && ((this.subscriptions[e.idx] = s), (s.idx = e.idx)),
      0 === this.subscriptions.length &&
        this.api._subscriptions.get(this.id) === this &&
        this.api._subscriptions.delete(this.id),
      t(null))
  }
  _create(e) {
    const t = { name: e || null, stream: null, query: null, map: null, idx: 0 }
    return (
      (t.stream = new c({ destroy: (e) => this._onDestroy(t, e) })),
      (t.idx = this.subscriptions.push(t) - 1),
      t
    )
  }
  add({ query: e, map: t, name: s }) {
    if (!e && !t) throw new Error("Must have either a query or a map")
    const i = this._create(s)
    return (
      e &&
        ((i.query = o(async () => {
          try {
            const t = await e()
            null !== t && i.stream.push(t)
          } catch (e) {
            i.stream.destroy(e)
          }
        })),
        i.query()),
      (i.map = t),
      i.stream
    )
  }
}
class Ge {
  constructor(e) {
    this.keet = e
  }
  create(e, t) {
    return async (s, ...i) => {
      if (this.keet.closing) return null
      const o = ["files", "base", "drive", "db", "snapshot", "blobs", "blobsSnapshot"],
        r = !!t.find((e) => o.includes(e))
      if (r && !s) throw new Error("Room ID is required for methods that require room resources")
      ;(("object" == typeof s && null !== s) || !r) && ((i = [s, ...i]), (s = null))
      const n = s ? this.keet.internalOpenExistingRoom(s) : null
      if ((n && (await n.ready(), await n.base.flush()), this.keet.closing)) return null
      const a = { roomId: s, roomKey: n?.key || null, discoveryKey: n?.discoveryKey || null }
      let c = null
      try {
        for (const e of t)
          (n && "db" === e && (a.db = n.db),
            n && "snapshot" === e && (a.snapshot = n.db.snapshot()),
            n && "base" === e && (a.base = n.base),
            n && "drive" === e && (a.drive = n.drive),
            n && "blobs" === e && (a.blobs = n.blobs),
            n && "blobsSnapshot" === e && (a.blobsSnapshot = n.blobs.snapshot()),
            "local" === e && (a.local = this.keet.local),
            "localSnapshot" === e && (a.localSnapshot = this.keet.local.snapshot()),
            "files" === e && (a.files = this.keet.files),
            "myKey" === e &&
              (this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
              a.localSnapshot || (a.localSnapshot = this.keet.local.snapshot()),
              (a.id = await a.localSnapshot.get("@local/identity")),
              (a.profileInfo = await a.localSnapshot.get("@local/rooms", {
                roomKey: a.id.privateProfileKey,
              })),
              (a.myKey = a.profileInfo?.selfMember?.key || null)),
            "profile" === e &&
              (this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
              a.localSnapshot || (a.localSnapshot = this.keet.local.snapshot()),
              a.id || (a.id = await a.localSnapshot.get("@local/identity")),
              a.profileInfo ||
                (a.profileInfo = await a.localSnapshot.get("@local/rooms", {
                  roomKey: a.id.privateProfileKey,
                })),
              (a.profile = this.keet.internalOpenExistingRoom(a.id.privateProfileKey)),
              await a.profile.ready()),
            a.profile && "profileDb" === e && (a.profileDb = a.profile.db),
            a.profile && "profileSnapshot" === e && (a.profileSnapshot = a.profile.db.snapshot()),
            a.profile && "profileBlobs" === e && (a.profileBlobs = a.profile.profileBlobs),
            a.profile &&
              "profileBlobsSnapshot" === e &&
              (a.profileBlobsSnapshot = a.profile.blobs.snapshot()))
        const s = Date.now()
        return (
          (c = await e(a, ...i)),
          this.keet.logger.debug("[Keet API]", e.name, "duration:", Date.now() - s, "args:", i),
          c
        )
      } catch (e) {
        if (this.keet.closing) return null
        if (n?.base.getLastError())
          throw (this.keet.onerror(n?.base.getLastError()), n.base.getLastError())
        throw (this.keet.onerror(e), e)
      } finally {
        ;(a.snapshot && (await a.snapshot.close()),
          a.blobsSnapshot && (await a.blobsSnapshot.close()),
          a.localSnapshot && (await a.localSnapshot.close()),
          a.profileSnapshot && (await a.profileSnapshot.close()),
          a.profileBlobsSnapshot && (await a.profileBlobsSnapshot.close()),
          a.profile && (await this.keet.internalReleaseRoom(a.profile)),
          n && (await this.keet.internalReleaseRoom(n)))
      }
    }
  }
}
class ze {
  constructor(e, { onerror: t = r } = {}) {
    ;((this.keet = e),
      (this.context = new Ge(this.keet)),
      (this._onerror = t),
      (this._subscriptions = new Map()),
      (this._profileSubscriptions = new Ve(this, null, { isProfileSub: !0 })),
      this.keet.on("room-interrupted", this._handleRoomInterrupted.bind(this)),
      this.keet.on("room-changed", this._handleRoomChanged.bind(this)),
      this.keet.on("identity-changed", this._handleIdentityChanged.bind(this)),
      this.keet.on("recent-rooms-changed", this._handleRecentRoomsChanged.bind(this)),
      this.keet.on("dm-rooms-changed", this._handleDmRoomsChanged.bind(this)),
      this.keet.on("chat-changed", this._handleChatChanged.bind(this)),
      this.keet.on("identity-pairing-request", this._handleIdentityPairingRequest.bind(this)),
      this.keet.on("migration-status", this._handleMigrationStatus.bind(this)),
      this.keet.on("room-preferences-changed", this._handleRoomPreferencesChanged.bind(this)),
      this.keet.on("unread-anchors-changed", this._handleUnreadAnchorsChanged.bind(this)),
      this.keet.on("room-sent-message", this._handleRoomSentMessageLengthChanged.bind(this)),
      this.keet.on("caught-error", this._handleKeetError.bind(this)),
      this.keet.on("force-update", this._handleForceUpdate.bind(this)),
      this.keet.on("verified-rooms-changed", this._handleVerifiedRoomsChanged.bind(this)),
      this.keet.on("local-files-changed", this._handleLocalFilesChanged.bind(this)),
      this.keet.on("favorite-rooms-changed", this._handleFavoriteRoomsChanged.bind(this)),
      (this.boot = this._timed(this._boot.bind(this))),
      (this.createInvitation = this._timed(this._createInvitation.bind(this))),
      (this.confirmPairingRequest = this._timed(this._confirmPairingRequest.bind(this))),
      (this.startPairingRoom = this._timed(this._startPairingRoom.bind(this))),
      (this.stopPairingRoom = this.context.create(this._stopPairingRoom.bind(this), ["profile"])),
      (this.searchRooms = this.context.create(this._searchRooms.bind(this), ["local"])),
      (this.getRoomInfo = this.context.create(this._getRoomInfo.bind(this), ["localSnapshot"])),
      (this.getRecentRooms = this.context.create(this._getRecentRooms.bind(this), ["local"])),
      (this.getPendingRooms = this.context.create(this._getPendingRooms.bind(this), ["profile"])),
      (this.getLocalRoomMetadata = this.context.create(this._getLocalRoomMetadata.bind(this), [
        "local",
      ])),
      (this.getConfig = this.context.create(this._getConfig.bind(this), ["snapshot", "local"])),
      (this.updateRoomSettings = this._updateRoomSettings.bind(this)),
      (this.getChatMessage = this.context.create(this._getChatMessage.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "files",
        "local",
      ])),
      (this.getChatMessageById = this.context.create(this._getChatMessageById.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "files",
        "local",
      ])),
      (this.getChatMessages = this.context.create(this._getChatMessages.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "files",
        "local",
      ])),
      (this.getChatLength = this.context.create(this._getChatLength.bind(this), ["db"])),
      (this.removeChatMessage = this.context.create(this._removeChatMessage.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "files",
        "local",
      ])),
      (this.searchChatMessages = this.context.create(this._searchChatMessages.bind(this), [
        "local",
        "snapshot",
        "blobsSnapshot",
        "files",
      ])),
      (this.addChatMessage = this._timed(this._addChatMessage.bind(this))),
      (this.updateChatMessage = this._timed(this._updateChatMessage.bind(this))),
      (this.forwardChatMessage = this.context.create(this._forwardChatMessage.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "files",
        "local",
      ])),
      (this.pinChatMessage = this._timed(this._pinChatMessage.bind(this))),
      (this.unpinChatMessage = this._timed(this._unpinChatMessage.bind(this))),
      (this.addCallStartedEvent = this._timed(this._addCallStartedEvent.bind(this))),
      (this.addCallHangupEvent = this._timed(this._addCallHangupEvent.bind(this))),
      (this.addCallMissedEvent = this._timed(this._addCallMissedEvent.bind(this))),
      (this.addLocationSharedEvent = this._timed(this._addLocationSharedEvent.bind(this))),
      (this.getActiveCalls = this.context.create(this._getActiveCalls.bind(this), ["snapshot"])),
      (this.getCalls = this.context.create(this._getCalls.bind(this), ["snapshot"])),
      (this.getReactions = this.context.create(this._getReactions.bind(this), [
        "snapshot",
        "local",
      ])),
      (this.getLatestReactions = this.context.create(this._getLatestReactions.bind(this), [
        "snapshot",
      ])),
      (this.addReaction = this._timed(this._addReaction.bind(this))),
      (this.removeReaction = this._timed(this._removeReaction.bind(this))),
      (this.getReportedMembers = this.context.create(this._getReportedMembers.bind(this), [
        "snapshot",
        "blobsSnapshot",
      ])),
      (this.addChatReport = this._timed(this._addChatReport.bind(this))),
      (this.removeChatReport = this._timed(this._removeChatReport.bind(this))),
      (this.getReportedMessages = this.context.create(this._getReportedMessages.bind(this), [
        "roomKey",
        "local",
        "snapshot",
        "blobsSnapshot",
        "files",
      ])),
      (this.getPinnedChatMessages = this.context.create(this._getPinnedChatMessages.bind(this), [
        "local",
        "snapshot",
        "blobsSnapshot",
        "files",
      ])),
      (this.getActiveMember = this.context.create(this._getActiveMember.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.getActiveMembers = this.context.create(this._getActiveMembers.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.getMembers = this.context.create(this._getMembers.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.getMember = this.context.create(this._getMember.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.getMemberCount = this.context.create(this._getMemberCount.bind(this), ["db"])),
      (this.getMemberBySwarmId = this.context.create(this._getMemberBySwarmId.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.getMemberByDeviceId = this.context.create(this._getMemberByDeviceId.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.waitUntilJoined = this.context.create(this._waitUntilJoined.bind(this), ["local"])),
      (this.isMemberMuted = this.context.create(this._isMemberMuted.bind(this), ["db"])),
      (this.getSearchMembers = this.context.create(this._getSearchMembers.bind(this), [
        "db",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.updateMember = this._timed(this._updateMember.bind(this))),
      (this.removeMember = this._timed(this._removeMember.bind(this))),
      (this.changeMemberRole = this._timed(this._changeMemberRole.bind(this))),
      (this.muteMember = this._timed(this._muteMember.bind(this))),
      (this.unmuteMember = this._timed(this._unmuteMember.bind(this))),
      (this.blockMember = this._timed(this._blockMember.bind(this))),
      (this.unblockMember = this._timed(this._unblockMember.bind(this))),
      (this.getDevices = this.context.create(this._getDevices.bind(this), ["db"])),
      (this.getDevice = this.context.create(this._getDevice.bind(this), ["db"])),
      (this.getDevicesByMemberId = this.context.create(this._getDevicesByMemberId.bind(this), [
        "db",
      ])),
      (this.getModerators = this.context.create(this._getModerators.bind(this), [
        "snapshot",
        "blobsSnapshot",
        "myKey",
      ])),
      (this.saveFile = this.context.create(this._saveFile.bind(this), ["files"])),
      (this.saveFileBlob = this.context.create(this._saveFileBlob.bind(this), ["files"])),
      (this.getFileEntry = this.context.create(this._getFileEntry.bind(this), ["files"])),
      (this.getFileCleared = this.context.create(this._getFileCleared.bind(this), ["files"])),
      (this.clearFile = this.context.create(this._clearFile.bind(this), ["files"])),
      (this.unclearFile = this.context.create(this._unclearFile.bind(this), ["files"])),
      (this.clearAllFiles = this.context.create(this._clearAllFiles.bind(this), ["files"])),
      (this.clearOldFiles = this._timed(this._clearOldFiles.bind(this))),
      (this.getLocalFiles = this.context.create(this._getLocalFiles.bind(this), [
        "files",
        "snapshot",
        "blobsSnapshot",
      ])),
      (this.getFiles = this.context.create(this._getFiles.bind(this), [
        "files",
        "snapshot",
        "blobsSnapshot",
      ])),
      (this.startDownload = this.context.create(this._startDownload.bind(this), ["files"])),
      (this.cancelDownload = this.context.create(this._cancelDownload.bind(this), ["files"])),
      (this.readFileStream = this.context.create(this._readFileStream.bind(this), ["files"])),
      (this.saveToDisk = this.context.create(this._saveToDisk.bind(this), ["files"])),
      (this.sendFile = this._timed(this._sendFile.bind(this))),
      (this.sendFiles = this._timed(this._sendFiles.bind(this))),
      (this.refreshFileLinks = this._timed(this._refreshFileLinks.bind(this))),
      (this.createRoom = this._timed(this._createRoom.bind(this))),
      (this.leaveRoom = this._timed(this._leaveRoom.bind(this))),
      (this.getSystemDigest = this.context.create(this._getSystemDigest.bind(this), ["db"])),
      (this.getTipSize = this.context.create(this._getTipSize.bind(this), ["base"])),
      (this.getRoomKeys = this._timed(this._getRoomKeys.bind(this))),
      (this.resyncPrivateProfile = this._timed(this._resyncPrivateProfile.bind(this))),
      (this.restoreIdentity = this._timed(this._restoreIdentity.bind(this))),
      (this.getRecoveryPhrase = this._timed(this._getRecoveryPhrase.bind(this))),
      (this.getEncryptedRecoveryPhrase = this._timed(this._getEncryptedRecoveryPhrase.bind(this))),
      (this.decryptEncryptedRecoveryPhrase = this._timed(
        this._decryptEncryptedRecoveryPhrase.bind(this),
      )),
      (this.freeRecoveryPhrase = this._timed(this._freeRecoveryPhrase.bind(this))),
      (this.confirmRecoveryPhrase = this._timed(this._confirmRecoveryPhrase.bind(this))),
      (this.storeRecoveryPhrase = this._timed(this._storeRecoveryPhrase.bind(this))),
      (this.getIdentity = this.context.create(this._getIdentity.bind(this), [
        "local",
        "profile",
        "profileSnapshot",
      ])),
      (this.getSelfContactInfo = this.context.create(this._getSelfContactInfo.bind(this), [
        "local",
        "profile",
      ])),
      (this.removeIdentityDevice = this.context.create(this._removeIdentityDevice.bind(this), [
        "profile",
      ])),
      (this.createIdentityInvitation = this._timed(this._createIdentityInvitation.bind(this))),
      (this.startPairingIdentity = this._timed(this._startPairingIdentity.bind(this))),
      (this.updateIdentityProfile = this._timed(this._updateIdentityProfile.bind(this))),
      (this.updateIdentityDevice = this._timed(this._updateIdentityDevice.bind(this))),
      (this.getIdentityPairingRequest = this._timed(this._getIdentityPairingRequest.bind(this))),
      (this.updateUserSettings = this._timed(this._updateUserSettings.bind(this))),
      (this.updateDHTSettings = this._timed(this._updateDHTSettings.bind(this))),
      (this.getDHTSettings = this._timed(this._getDHTSettings.bind(this))),
      (this.getContactInfo = this.context.create(this._getContactInfo.bind(this), [
        "local",
        "profile",
      ])),
      (this.getBlockedMembers = this.context.create(this._getBlockedMembers.bind(this), ["local"])),
      (this.getDmRequestsByStatus = this.context.create(this._getDmRequestsByStatus.bind(this), [
        "profile",
        "profileDb",
        "profileBlobsSnapshot",
      ])),
      (this.sendDMFromContact = this._timed(this._sendDMFromContact.bind(this))),
      (this.sendDMRequest = this._timed(this._sendDMRequest.bind(this))),
      (this.sendInvitations = this._timed(this._sendInvitations.bind(this))),
      (this.updatePrivateMailboxRecord = this._timed(this._updatePrivateMailboxRecord.bind(this))),
      (this.acceptDmRequest = this._timed(this._acceptDmRequest.bind(this))),
      (this.rejectDmRequest = this._timed(this._rejectDmRequest.bind(this))),
      (this.getRoomPreferences = this.context.create(this._getRoomPreferences.bind(this), [
        "local",
      ])),
      (this.updateRoomPreferences = this.context.create(this._updateRoomPreferences.bind(this), [
        "profile",
      ])),
      (this.getBlockedMembers = this.context.create(this._getBlockedMembers.bind(this), ["local"])),
      (this.updateConfig = this._timed(this._updateConfig.bind(this))),
      (this.disableMembershipEvents = this._timed(this._disableMembershipEvents.bind(this))),
      (this.enableMembershipEvents = this._timed(this._enableMembershipEvents.bind(this))),
      (this.setRoomUnread = this._timed(this._setRoomUnread.bind(this))),
      (this.getLinkInfo = this._timed(this._getLinkInfo.bind(this))),
      (this.lookupUsername = this._timed(this._lookupUsername.bind(this))),
      (this.lookupUserByMemberId = this._timed(this._lookupUserByMemberId.bind(this))),
      (this.registerUsername = this._timed(this._registerUsername.bind(this))),
      (this.updateUsername = this._timed(this._updateUsername.bind(this))),
      (this.searchUsername = this._timed(this._searchUsername.bind(this))),
      (this.getReservationsByKey = this._timed(this._getReservationsByKey.bind(this))),
      (this.checkUsername = this._timed(this._checkUsername.bind(this))),
      (this.repair = this._timed(this._repair.bind(this))),
      (this.rotateWriter = this._timed(this._rotateWriter.bind(this))),
      (this.audit = this._timed(this._audit.bind(this))),
      (this.setUnread = this._setUnread.bind(this)),
      (this.getUnread = this._getUnread.bind(this)),
      (this.setUnreadAnchor = this._setUnreadAnchor.bind(this)),
      (this.getUnreadAnchor = this._getUnreadAnchor.bind(this)),
      (this.subscribeUnreadAnchor = this._subscribeUnreadAnchor.bind(this)),
      (this.setRoomFavorite = this._setRoomFavorite.bind(this)))
  }
  _timed(e) {
    return async function (...t) {
      const s = Date.now()
      try {
        const i = await e(...t)
        return (
          this.keet.logger.debug("[Keet API]", e.name, "duration:", Date.now() - s, "args:", t),
          i
        )
      } catch (e) {
        throw (this.keet.onerror(e), e)
      }
    }
  }
  _boot() {
    return this.keet.backendReady()
  }
  _disableMembershipEvents(e) {
    return this.keet.dispatch(e, "@keet/update-system-settings", {
      key: oe.IsMembershipEventsDisabled,
      settings: { isMembershipEventsDisabled: !0 },
    })
  }
  _enableMembershipEvents(e) {
    return this.keet.dispatch(e, "@keet/update-system-settings", {
      key: oe.IsMembershipEventsDisabled,
      settings: { isMembershipEventsDisabled: !1 },
    })
  }
  _addKeetSubscription(e = De, t) {
    return this._addRoomSubscription(e, t)
  }
  _addRoomSubscription(e, t, { isProfileSub: s = !1, _isRoomSub: i = !0 } = {}) {
    if (null === e && s) return this._profileSubscriptions.add(t)
    let o = this._subscriptions.get(e)
    return (o || ((o = new Ve(this, e)), this._subscriptions.set(e, o)), o.add(t))
  }
  _refreshSubscriptionsById(e) {
    const t =
      e === this.keet.privateProfile?.id ? this._subscriptions.get(Be) : this._subscriptions.get(e)
    t && t.refresh()
  }
  _handleIdentityPairingRequest(e) {
    ;(this._identityPairingRequestsByDeviceId.set(s.encode(e.deviceKey), e),
      this._refreshSubscriptionsById(Le))
  }
  _handleRoomInterrupted(e, { unsupportedABI: t, digest: s }) {
    const i = this._subscriptions.get(e)
    if (i)
      for (const e of i.subscriptions)
        "room-version" === e.name &&
          e.stream.push({ version: t, experimental: s && s.settings.isExperimental })
  }
  async _handleRoomChanged(e) {
    try {
      ;(this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
        this._refreshSubscriptionsById(e))
    } catch (e) {
      this.keet.onerror(e)
    }
  }
  _handleRecentRoomsChanged(...e) {
    const t = this._subscriptions.get(Pe)
    t && t.push(e)
  }
  _handleDmRoomsChanged() {
    const e = this._subscriptions.get(Fe)
    e && e.refresh()
  }
  _handleRoomPreferencesChanged(...e) {
    const t = this._subscriptions.get(Te)
    t && t.push(e)
  }
  _handleUnreadAnchorsChanged() {
    const e = this._subscriptions.get(Ne)
    e && e.refresh()
  }
  _handleRoomSentMessageLengthChanged(e) {
    const t = this._subscriptions.get(e)
    t && t.refresh({ name: "room-info" })
  }
  _handleIdentityChanged() {
    this._refreshSubscriptionsById(Ae)
  }
  async _handleChatChanged(...e) {
    try {
      this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
      const t = this._subscriptions.get(xe)
      if (!t) return
      const [s, i] = e
      if (s === this.keet.privateProfile.id) return
      t.push({ roomId: s, index: i })
    } catch (e) {
      this.keet.onerror(e)
    }
  }
  _handleMigrationStatus(e) {
    const t = this._subscriptions.get(Ue)
    t && t.push(e)
  }
  _handleKeetError(e) {
    const t = this._subscriptions.get(Oe)
    t && t.push(e)
  }
  _handleForceUpdate() {
    const e = this._subscriptions.get(je)
    e && e.refresh()
  }
  _handleVerifiedRoomsChanged() {
    for (const e of this._subscriptions.values()) e.refresh({ name: "room-info" })
  }
  _handleLocalFilesChanged(e) {
    const t = this._subscriptions.get(e)
    t && t.refresh({ name: "room-info" })
  }
  _handleFavoriteRoomsChanged(...e) {
    const t = this._subscriptions.get(qe)
    t && t.push(e)
  }
  getVersion() {
    return { buildType: this.keet.buildType, abi: this.keet.abi, modules: He }
  }
  async _getSystemDigest({ db: e }) {
    return await e.get("@keet/system-digest")
  }
  subscribeSentLength(e) {
    return this._addRoomSubscription(e, {
      query: async () => {
        const t = this.keet.internalOpenExistingRoom(e)
        await t.ready()
        const s = { deviceId: t.base.local.id, length: t.base.local.remoteContiguousLength }
        return (this.keet.internalReleaseRoom(t), s)
      },
      name: "room-sent-message",
    })
  }
  subscribeRoomVersion(e) {
    return this._addRoomSubscription(e, {
      query: async () => {
        const t = await this.getSystemDigest(e)
        return { version: t.abi.version, experimental: t.settings.isExperimental }
      },
      name: "room-version",
    })
  }
  subscribeForceUpdates() {
    return this._addKeetSubscription(je, {
      query: () => ({ showBanner: this.keet.shouldForceUpdate() }),
    })
  }
  async upgradeRoom(e) {
    this.keet.opened || (await this.keet.ready())
    const t = this.keet.isNightly ? this.keet.abi.max : this.keet.abi.production
    await this.keet.internalDispatchUpgrade(e, t)
  }
  async _getLinkInfo(e) {
    const t = this.keet.internalGetLinkInfo(e)
    if (!t) return null
    if (t.identity)
      return {
        isIdentityInvitation: !0,
        memberId: s.encode(t.identity.identityPublicKey),
        invitationSeed: s.encode(t.identity.invitationSeed),
        additionalNodes: t.additionalNodes,
      }
    if (t.room) {
      const e = { capabilities: { mailboxKeyPair: { publicKey: t.room.mailbox } } },
        i = await this.keet.local.get("@local/rooms-by-mailbox", e)
      return {
        isRoomInvitation: !0,
        roomId: i ? s.encode(i.roomKey) : null,
        title: i ? i.config.title : null,
        description: i ? i.config.description : null,
        avatar: i ? D(i.config.avatar) : null,
        invitationSeed: s.encode(t.room.invitationSeed),
        additionalNodes: t.additionalNodes,
      }
    }
    return t.profile
      ? {
          isContactInfo: !0,
          memberId: s.encode(t.attestation.memberKey),
          displayName: t.profile.displayName,
          username: t.profile.username,
          additionalNodes: t.additionalNodes,
        }
      : t.readonly
        ? { isReadOnly: !0, additionalNodes: t.additionalNodes }
        : void 0
  }
  async _getSelfContactInfo({ id: e, profileInfo: t }) {
    const s = this.keet.internalCreateContactInfoStruct(e, t, { avatar: null, timestamp: 0 })
    return n.toString(s, "base64")
  }
  async _getIdentity({ id: e, profile: t, profileSnapshot: i, profileInfo: o }) {
    const [r, n] = await Promise.all([
        i.find("@keet/devices").toArray(),
        i.get("@keet-private-id/settings"),
      ]),
      a = []
    for (const e of r) e.status.isActive && a.push({ ...v(e), confirmed: !0 })
    const c = w(o.selfMember, o.selfMember.key)
    return (
      (c.hasDiscoveryKey = !!o.identity.discoveryCoreKey),
      (c.anonymous = !1),
      (c.confirmed = e.status.isConfirmed),
      (c.devices = a),
      (c.profileId = t.id),
      (c.profileDiscoveryId = t.discoveryId),
      (c.local = { deviceId: s.encode(t.deviceKey) }),
      (c.settings = F(n)),
      (c.isLoggedOut = o.selfDevice && !o.selfDevice.status.isActive),
      (c.version = o?.digest.abi.version),
      c
    )
  }
  _removeIdentityDevice({ profile: e }, t) {
    return e.dispatch("@keet/remove-device", { key: s.decode(t) })
  }
  subscribeIdentity() {
    return this._addKeetSubscription(Ae, { query: () => this.getIdentity() })
  }
  async _restoreIdentity(e, t = {}) {
    await this.keet.restoreIdentity(e, t)
  }
  _getRecoveryPhrase() {
    return this.keet.getRecoveryPhrase()
  }
  _getEncryptedRecoveryPhrase(e) {
    return this.keet.getEncryptedRecoveryPhrase(e)
  }
  _decryptEncryptedRecoveryPhrase(e, t) {
    return this.keet.decryptEncryptedRecoveryPhrase(e, t)
  }
  _freeRecoveryPhrase(e) {
    return this.keet.freeRecoveryPhrase(e)
  }
  _confirmRecoveryPhrase(e) {
    return this.keet.confirmRecoveryPhrase(e)
  }
  _storeRecoveryPhrase(e) {
    return this.keet.storeRecoveryPhrase(e)
  }
  confirmIdentity(...e) {
    throw (
      console.log("confirmIdentity", ...e),
      new Error("Missing required method: confirmIdentity")
    )
  }
  async _confirmPairingRequest({ deviceId: e }, t = !0) {
    this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
    const i = this.keet.privateProfile.key,
      o = s.decode(e)
    return t ? this.keet.acceptJoinRequest(i, o) : this.keet.rejectJoinRequest(i, o)
  }
  async _getIdentityPairingRequest() {
    const e = await this.keet.internalOpenPrivateProfile(),
      t = await e.db.find("@keet/join-requests").toArray()
    return (this.keet.internalReleaseRoom(e), 0 === t.length ? null : M(t[0]))
  }
  subscribeIdentityPairingRequests(...e) {
    return this._addKeetSubscription(Ae, { query: () => this.getIdentityPairingRequest() })
  }
  async _updateIdentityProfile({ displayName: e, avatar: t }) {
    await this.updateMember(e, t)
  }
  _updateIdentityDevice(e) {
    return this.keet.updateDevice(e)
  }
  _startPairingIdentity(e) {
    return this.keet.startPairingIdentity(e)
  }
  pairIdentity(...e) {
    throw (console.log("pairIdentity", ...e), new Error("Missing required method: pairIdentity"))
  }
  async _updateUserSettings(e, t) {
    this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
    const s = { [e]: t }
    if (
      e === Z.stripAttachmentMetadata &&
      !(await this.keet.internalFeatureSupported(
        this.keet.privateProfile.id,
        Se.STRIP_ATTACHMENT_METADATA,
      ))
    )
      throw new Error("Stripping attachment metadata not supported")
    return this.keet.privateProfile.dispatch("@keet-private-id/update-settings", {
      key: e,
      settings: z(s),
    })
  }
  async _getDHTSettings() {
    const e = await this.keet.getDHTSettings()
    return B(e)
  }
  async _updateDHTSettings(e, t) {
    if (!(e in we)) return
    const s = await this.keet.internalEnterLocalTransaction()
    try {
      const i = await this.keet.getDHTSettings()
      ;((i[e] = t), await s.insert("@local/hyperdht", i), await s.flush())
    } finally {
      await this.keet.internalExitLocalTransaction()
    }
  }
  async _createRoom(e = {}) {
    const t = await this.keet.createRoom(st(e))
    return s.encode(t)
  }
  _pairRoom(...e) {
    throw (console.log("pairRoom", ...e), new Error("Missing required method: pairRoom"))
  }
  _leaveRoom(e, t = {}) {
    return this.keet.leaveRoom(s.decode(e))
  }
  async _startPairingRoom(e, t = {}) {
    return await this.keet.startPairingRoom(e, t)
  }
  async _stopPairingRoom({ profile: e }, t) {
    const i = s.decode(t),
      o = await e.db.find("@keet-private-id/pending-invitations").toArray()
    for (const e of o)
      if (n.equals(e.invitationSeed, i)) {
        await this.keet.stopPairing(e.id)
        break
      }
  }
  subscribePendingRooms() {
    return this._addKeetSubscription(Ae, { query: () => this.getPendingRooms() })
  }
  subscribePushNotificationChanges() {
    return this._addKeetSubscription(Te, {
      map: function (e) {
        const [t, o, r] = e,
          n = r || i.discoveryKey(o)
        return { type: t, roomId: s.encode(o), discoveryId: s.encode(n) }
      },
    })
  }
  subscribeActivateRoom(e) {
    const t = s.decode(e)
    return new c({
      eagerOpen: !0,
      open: async (e) => {
        let s = null
        try {
          await this.keet.activateRoom(t)
        } catch (e) {
          s = e
        }
        e(s)
      },
      destroy: async (e) => {
        let s = null
        try {
          await this.keet.deactivateRoom(t)
        } catch (e) {
          s = e
        }
        e(s)
      },
    })
  }
  _getRoomKeys(e) {
    try {
      const t = s.decode(e)
      return { discoveryKey: n.toString(i.discoveryKey(t), "hex") }
    } catch {
      return { discoveryKey: "" }
    }
  }
  _getTipSize({ base: e }) {
    return e.linearizer.size
  }
  async _resyncPrivateProfile() {
    ;(this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
      await this.keet.internalResyncPrivateProfile())
  }
  bookmark(...e) {
    throw (console.log("bookmark", ...e), new Error("Missing required method: bookmark"))
  }
  unbookmark(...e) {
    throw (console.log("unbookmark", ...e), new Error("Missing required method: unbookmark"))
  }
  subscribeBookmarks(...e) {
    return (console.log("subscribeBookmarks", ...e), new c())
  }
  async _loadRecents(e, t = {}) {
    const { index: s, range: i } = (function (e) {
        return e.favorites
          ? { index: "@local/rooms-by-favorite-and-timestamp", range: {} }
          : e.default || e.dms || e.broadcast
            ? {
                index: "@local/rooms-by-type-and-timestamp",
                range: { lte: { type: n(e) }, gte: { type: n(e) } },
              }
            : { index: "@local/rooms-by-timestamp", range: {} }
      })(t),
      o = await e.find(s, { ...t, ...i, reverse: !0 }).toArray(),
      r = await Promise.all(o.map((t) => e.get("@local/room-preferences", { roomKey: t.roomKey })))
    for (let e = 0; e < r.length; e++) {
      const t = r[e]
      t && !t.status.isActive && (o[e] = null)
    }
    return o
    function n(e) {
      return e.default ? W.Default : e.dms ? W.DirectMessage : e.broadcast ? W.Broadcast : void 0
    }
  }
  async _getRoomInfo({ localSnapshot: e }, t) {
    const o = s.decode(t),
      r = await e.get("@local/rooms", { roomKey: o })
    if (!r) return null
    let n = w(r.lastMember)
    const [a, c, d] = await Promise.all([
      e.get("@local/room-preferences", { roomKey: r.roomKey }),
      r.lastMessage && !n
        ? e.get("@local/devices", { device: { key: r.lastMessage.oplog.key } })
        : null,
      e.get("@local/file-stats", { roomKey: r.roomKey }),
    ])
    if (c) {
      const t = await e.get("@local/members", { key: c.device.memberKey })
      t &&
        (n = { id: s.encode(t.key), memberId: s.encode(t.key), displayName: t.profile.displayName })
    }
    if (r.lastMessage) {
      const e = ce(r, r.lastMessage.id)
      r.lastMessage.read = e
    }
    if (r.lastMessage?.event?.memberRemoved) {
      const t = await e.get("@local/members", { key: r.lastMessage.event.memberRemoved.memberKey }),
        s = t?.profile?.displayName
      r.lastMessage.event.memberRemoved.displayName = s
    }
    const h = r.selfDevice ? i.discoveryKey(r.selfDevice.key) : null,
      l = h ? await this.keet.storage.getInfo(h, { hints: !0, auth: !1, head: !1 }) : null,
      u = k({ roomInfo: r, preferences: a, lastMessageMember: n, storageInfo: l, fileStats: d })
    return ((u.isVerified = this.keet.isRoomVerified(t)), u)
  }
  async _searchRooms({ local: e }) {
    const t = await this._loadRecents(e),
      i = []
    for (const e of t) e && i.push({ title: e.config.title, roomId: s.encode(e.roomKey) })
    return i
  }
  async _getRecentRooms({ local: e }, t) {
    const [i, o] = await Promise.all([this._loadRecents(e, t), e.get("@local/stats")]),
      r = []
    for (const e of i)
      e &&
        r.push({
          timestamp: e.timestamps.latestChatMessage || e.timestamps.latestSync,
          roomId: s.encode(e.roomKey),
        })
    return { type: "all", total: o ? o.activeRooms : 0, rooms: r }
  }
  async _getPendingRooms({ profile: e }) {
    return (await e.db.find("@keet-private-id/pending-invitations").toArray()).map(R)
  }
  _getLocalRoomMetadata({ local: e }, t) {
    return e.get("@local/rooms", { discoveryKey: s.decode(t) })
  }
  subscribeRecentRooms(e) {
    return this._addKeetSubscription(Pe, {
      map: function (e) {
        const [t, s, i] = e
        return { type: t, timestamp: s, roomId: i }
      },
      query: () => this.getRecentRooms(e),
    })
  }
  subscribeFavoriteRooms() {
    return this._addKeetSubscription(qe, {
      map: function (e) {
        const [t, s, i] = e
        return { type: t, timestamp: s, roomId: i }
      },
      query: () => this.getRecentRooms({ favorites: !0 }),
    })
  }
  subscribeRoomInfo(e, t) {
    return this._addRoomSubscription(e, { query: () => this.getRoomInfo(e, t), name: "room-info" })
  }
  subscribeUnreadActivity(e = {}) {
    return this._addKeetSubscription(xe, {
      map: ({ roomId: t, index: s }) =>
        t ? (e.includeLength || e.includeIndex ? { roomId: t, index: s } : { roomId: t }) : null,
    })
  }
  subscribeErrors() {
    return this._addKeetSubscription(Oe, { map: (e) => x(e) })
  }
  subscribeNotifications() {
    const e = this.subscribeUnreadActivity({ includeIndex: !0 }),
      t = c.from(this._subscribeNotifications(e))
    return (
      t.once("close", () => {
        e.destroy()
      }),
      t
    )
  }
  setCallRingingId(e) {
    this._callRingingRoomId = e
  }
  setProcessingNotifications(e) {
    this._processingNotifications = e
  }
  async *_subscribeNotifications(e) {
    for await (const { roomId: i, index: o } of e) {
      if (this.keet.closing) return
      if (this._callRingingRoomId) {
        if (i !== this._callRingingRoomId) continue
        const e = await this.getChatMessage(i, o)
        if (this.keet.closing) return
        e?.event?.type === ye.CALL_HANGUP && (yield { type: ye.CALL_HANGUP })
      }
      if (this._processingNotifications) continue
      if (this.keet.backendAPI.api.call.callIsActive) continue
      const e = await this.getRoomPreferences(i, ge.IS_FULLY_MUTED)
      if (this.keet.closing) return
      if (e || this._processingNotifications) continue
      const [r, n, a] = await Promise.all([
        this.getConfig(i),
        this.getChatMessage(i, o),
        this.keet.local.get("@local/identity"),
      ])
      if (this.keet.closing) return
      if (!n || Math.abs(Date.now() - n.timestamp) > Ce) continue
      const c = await this.getBlockedMembers()
      if (this.keet.closing) return
      if (c.some((e) => e.memberId === n.memberId) || this._processingNotifications) continue
      if (a && s.encode(a.publicKey) === n.memberId) continue
      const d = `${n.id.deviceId}/b${n.id.seq}`,
        h = t(n),
        l = (n.files.length > 0 && n.files[0].metadata?.mimetype) || "",
        u = await this.getMember(i, n.memberId)
      if (this.keet.closing) return
      const m = n.chat?.text || "",
        p = tt(r.roomType) === W.DirectMessage
      if (!this._processingNotifications && (m || h)) {
        if (p && "call" === h) {
          if (Math.abs(Date.now() - n.timestamp) > Ee) continue
          if (!(await this.getActiveCalls(i)).length) continue
          yield { type: ye.CALL_STARTED }
        }
        const e = await this.getRoomInfo(i)
        if (this.keet.closing) return
        const t = e?.config?.avatar?.small?.pointer?.inlined ?? "",
          s = n.message?.generic?.locationShared
        yield A(
          {
            messageId: d,
            roomId: i,
            avatar: t,
            location: s,
            messageText: m,
            messageType: h,
            fileType: l,
          },
          r,
          u,
        )
      }
    }
    function t(e) {
      return e.event?.type === ye.CALL_STARTED
        ? "call"
        : e.files?.length > 0
          ? "file"
          : e.message?.generic?.locationShared
            ? "location"
            : ""
    }
  }
  async _setRoomUnread(e, t = !0) {
    this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
    const { privateProfileKey: i } = await this.keet.local.get("@local/identity"),
      o = await this.keet.local.get("@local/rooms", { roomKey: i })
    if (!o) return
    const { isFeatureSupported: r, FEATURES: n } = this.keet.versions
    if (!r(o.digest.abi.version, n.ROOM_MARK_UNREAD))
      throw new Error("Manual unread is not supported")
    await this.keet.privateProfile.dispatch("@keet-private-id/update-room-status", {
      roomKey: s.decode(e),
      key: te.IsUnread,
      status: { isUnread: t },
    })
  }
  bumpRoomPriority(...e) {
    throw (
      console.log("bumpRoomPriority", ...e),
      new Error("Missing required method: bumpRoomPriority")
    )
  }
  addDevice(...e) {
    throw (console.log("addDevice", ...e), new Error("Missing required method: addDevice"))
  }
  removeDevice(...e) {
    throw (console.log("removeDevice", ...e), new Error("Missing required method: removeDevice"))
  }
  async _getDevice({ db: e }, t) {
    const s = await e.findOne("@keet/devices", O({ gte: t, lte: t }))
    return s ? v(s) : null
  }
  async _getDevices({ db: e }, t) {
    return (await e.find("@keet/devices", O(t)).toArray()).map((e) => v(e))
  }
  async moveIndexer(e, t, i) {
    if (!(await this.keet.internalFeatureSupported(e, Se.MOVE_INDEXER)))
      throw new Error("Move indexer not supported")
    const o = s.decode(t),
      r = s.decode(i)
    return this.keet.dispatch(e, "@keet/move-indexer", { deviceKey: o, fromDeviceKey: r })
  }
  subscribeDevice(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getDevice(e, ...t) })
  }
  subscribeDevices(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getDevices(e, ...t) })
  }
  async _createIdentityInvitation() {
    return this.keet.createIdentityInvitation()
  }
  _createInvitation(e, t = {}) {
    return (!0 === t.canIndex && (t.canAdmin = !0), this.keet.createRoomInvitation(s.decode(e), t))
  }
  async _updateMember(e, t) {
    this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
    const i = await this.keet.local.get("@local/identity")
    if (i && s.encode(i.privateProfileKey))
      return ((t = V(t)), this.keet.updateProfile({ displayName: e, avatar: t }))
  }
  async _muteMember(e, t) {
    const i = s.decode(t)
    return this.keet.dispatch(e, "@keet/change-member-status", {
      memberKey: i,
      key: he.IsMuted,
      status: { isMuted: !0 },
    })
  }
  async _unmuteMember(e, t) {
    const i = s.decode(t)
    return this.keet.dispatch(e, "@keet/change-member-status", {
      memberKey: i,
      key: he.IsMuted,
      status: { isMuted: !1 },
    })
  }
  async _isMemberMuted({ db: e }, t) {
    const i = s.decode(t)
    return !!(await e.get("@keet/muted-members", { key: i }))
  }
  async _getMemberByDeviceId({ myKey: e, snapshot: t, blobsSnapshot: i }, o) {
    const r = await ke(t, s.decode(o))
    return r ? (await fe(i, r.profile.avatar), w(r, e)) : null
  }
  async _getMemberBySwarmId({ myKey: e, snapshot: t, blobsSnapshot: i }, o) {
    const r = s.decode(o),
      n = await t.get("@keet/devices-by-swarm-key", { profile: { swarmKey: r } })
    if (!n) return null
    const a = await ke(t, n.key)
    return a ? (await fe(i, a.profile.avatar), w(a, e)) : null
  }
  async _getMemberCount({ db: e }) {
    const t = await e.get("@keet/threads", { id: 0 })
    return t ? t.stats.members.active : 0
  }
  async _getDevicesByMemberId({ db: e }, t) {
    const i = [],
      o = s.decode(t),
      r = { gte: { memberKey: o }, lte: { memberKey: o } }
    for await (const t of e.find("@keet/devices-by-member-key", r)) i.push(v(t))
    return i
  }
  getCurrentDeviceId(e) {
    const t = this.keet.internalOpenExistingRoom(e)
    try {
      return t.deviceId
    } finally {
      this.keet.internalReleaseRoom(t)
    }
  }
  async _getMember({ roomId: e, myKey: t, base: i, snapshot: o, blobsSnapshot: r }, n, a = {}) {
    const c = s.decode(n)
    if (!0 === a.swarmId) return this.getMemberBySwarmId(e, n)
    const d = await o.get("@keet/members", { key: c })
    return d ? (await fe(r, d.profile.avatar), w(d, t)) : null
  }
  async _getActiveMember({ myKey: e, snapshot: t, blobsSnapshot: i }, o) {
    const r = await t.get("@keet/active-members", { key: s.decode(o) })
    return r ? (await fe(i, r.profile.avatar), w(r, e)) : null
  }
  async _removeMember(e, t) {
    const i = s.decode(e),
      o = await this.keet.local.get("@local/rooms", { roomKey: i })
    if (!o) return
    const { isFeatureSupported: r, FEATURES: n } = this.keet.versions
    return r(o.digest.abi.version, n.REMOVE_MEMBER_V2)
      ? this.keet.dispatch(e, "@keet/remove-member", { key: s.decode(t) })
      : void 0
  }
  async _getMembers({ myKey: e, snapshot: t, blobsSnapshot: s }, i = {}) {
    const o = await t.find("@keet/members", O(i)).toArray()
    return Promise.all(o.map(async (t) => (await fe(s, t.profile.avatar), w(t, e))))
  }
  async _getActiveMembers({ myKey: e, snapshot: t, blobsSnapshot: s }, i = {}) {
    const o = await t.find("@keet/active-members", O(i)).toArray()
    return Promise.all(o.map(async (t) => (await fe(s, t.profile.avatar), w(t, e))))
  }
  async _getModerators({ myKey: e, snapshot: t, blobsSnapshot: s }, i = {}) {
    const o = await t.find("@keet/moderators", O(i)).toArray()
    return Promise.all(o.map(async (t) => (await fe(s, t.profile.avatar), w(t, e))))
  }
  async _waitUntilJoined({ roomKey: e, local: t }) {
    const s = await t.get("@local/rooms", { roomKey: e })
    return s ? w(s.selfMember) : null
  }
  getMemberForDevice(...e) {
    throw (
      console.log("getMemberForDevice", ...e),
      new Error("Missing required method: getMemberForDevice")
    )
  }
  subscribeMemberCount(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getMemberCount(e, ...t) })
  }
  subscribeMember(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getMember(e, ...t) })
  }
  subscribeActiveMember(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getActiveMember(e, ...t) })
  }
  subscribeActiveMembers(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getActiveMembers(e, ...t) })
  }
  subscribeMembers(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getMembers(e, ...t) })
  }
  subscribeModerators(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getModerators(e, ...t) })
  }
  subscribeMemberForDevice(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getMemberByDeviceId(e, ...t) })
  }
  _changeMemberRole(e, { memberId: t, role: i }) {
    return this.keet.dispatch(e, "@keet/change-member-role", {
      key: s.decode(t),
      isAdmin: i === le.ADMIN,
      isModerator: i === le.ADMIN || i === le.MODERATOR,
    })
  }
  async _getSearchMembers({ myKey: e, db: t, blobsSnapshot: s }, i, { limit: o = 50 } = {}) {
    i = i.toLowerCase()
    const r = await t.find("@keet/members-by-member-name", { gte: { name: i }, limit: o }).toArray()
    return (await Promise.all(r.map(async (t) => (await fe(s, t.profile.avatar), w(t, e))))).filter(
      (e) => e.name.startsWith(i),
    )
  }
  getPushNotificationMembers(...e) {
    throw (
      console.log("getPushNotificationMembers", ...e),
      new Error("Missing required method: getPushNotificationMembers")
    )
  }
  subscribeSearchMembers(...e) {
    return (console.log("subscribeSearchMembers", ...e), new c())
  }
  subscribePushNotificationMembers(...e) {
    return (console.log("subscribePushNotificationMembers", ...e), new c())
  }
  async _getContactInfo({ local: e, profile: t }, i) {
    const o = s.decode(i),
      [r, n, a] = await Promise.all([
        e.get("@local/contacts", { memberKey: o }),
        e.findOne("@local/rooms-by-contact", { gte: { contact: o }, lte: { contact: o } }),
        t.db.findOne("@keet-private-id/pending-contact-requests-by-member", {
          gte: { memberKey: o },
          lte: { memberKey: o },
        }),
      ])
    return K(i, r, n, a)
  }
  subscribeContactInfo(e) {
    return this._addRoomSubscription(Fe, {
      query: async () => {
        const t = this.keet.local.snapshot()
        try {
          const i = s.decode(e),
            [o, r] = await Promise.all([
              t.get("@local/contacts", { memberKey: i }),
              t.findOne("@local/rooms-by-contact", { gte: { contact: i }, lte: { contact: i } }),
            ])
          return K(e, o, r, null)
        } finally {
          await t.close()
        }
      },
    })
  }
  async _blockMember(e, t) {
    const i = s.decode(t)
    return this.keet.blockContact(i)
  }
  async _unblockMember(e, t) {
    const i = s.decode(t)
    return this.keet.unblockContact(i)
  }
  async _getBlockedMembers({ local: e }) {
    return (await e.find("@local/blocked-contacts").toArray()).map(({ memberKey: e }) => ({
      memberId: s.encode(e),
    }))
  }
  subscribeBlockedMembers() {
    return this._addRoomSubscription(Be, { query: () => this.getBlockedMembers() })
  }
  getConnectedMembers(...e) {
    throw (
      console.log("getConnectedMembers", ...e),
      new Error("Missing required method: getConnectedMembers")
    )
  }
  subscribeConnectedMembers(...e) {
    return (console.log("subscribeConnectedMembers", ...e), new c())
  }
  async _updateConfig(e, t, s) {
    const i = et(t)
    if (0 !== i) {
      const t = { [ie[i - 1]]: s }
      await this.keet.dispatch(e, "@keet/update-room-config", { key: i, config: H(t) })
    } else if ("roomType" === t) {
      const t = tt(s)
      if (0 === t) throw new Error("Invalid room type")
      await this.keet.dispatch(e, "@keet/update-system-settings", {
        key: oe.RoomType,
        settings: { roomType: t },
      })
    } else {
      if ("canCall" !== t) throw new Error("Invalid config key")
      await this.keet.dispatch(e, "@keet/update-system-settings", {
        key: oe.IsCallingDisabled,
        settings: { isCallingDisabled: "false" === s },
      })
    }
  }
  async _updateRoomPreferences({ profile: e }, t, i, o) {
    if (!me(i)) throw new Error(`invalid preference key: ${i}`)
    const r = s.decode(t)
    await this.keet.dispatch(e.id, "@keet-private-id/update-room-status", {
      roomKey: r,
      key: Ze(i),
      status: { [pe(i)]: o },
    })
  }
  async _updateRoomSettings(e, t, i) {
    if (
      t === Q.IsAttachmentsDisabled &&
      !(await this.keet.internalFeatureSupported(e, Se.DISABLE_FILE_ATTACHMENTS))
    )
      throw new Error("Disabling file attachments not supported")
    if (t === Q.IsLinksDisabled && !(await this.keet.internalFeatureSupported(e, Se.DISABLE_LINKS)))
      throw new Error("Disabling links not supported")
    const o = s.decode(e)
    return this.keet.dispatch(o, "@keet/update-system-settings", { key: t, settings: i })
  }
  async _getRoomSettings(e) {
    const t = s.decode(e),
      i = this.keet.internalOpenExistingRoom(t)
    try {
      return (await i.db.get("@keet/system-digest")).settings
    } finally {
      this.keet.internalReleaseRoom(i)
    }
  }
  async _getRoomPreferences({ local: e }, t, i) {
    const o = s.decode(t),
      r = await e.get("@local/room-preferences", { roomKey: o })
    if (!r) return null
    const { status: n } = r
    return me(i) ? (i ? n[pe(i)] : n) : null
  }
  async _getConfig({ roomKey: e, local: t }, s) {
    const i = await t.get("@local/rooms", { roomKey: e })
    if (!i) return null
    const { config: o, digest: r } = i,
      n = _(o, r) || {}
    return s ? n[s] : n
  }
  subscribeConfig(e) {
    return this._addRoomSubscription(e, { query: () => this.getConfig(e) })
  }
  async _addChatMessage(e, t, i = {}) {
    const o = s.decode(e),
      r = await this.keet.local.get("@local/rooms", { roomKey: o })
    if (!r) return
    const n = T(t, i),
      a = it(n),
      { isFeatureSupported: c, FEATURES: d } = this.keet.versions,
      h = c(r.digest.abi.version, d.MULTI_FILES)
    if (n.files.length && !h) throw new Error("multi files not supported")
    const l = this.keet.batch(e)
    return 0 !== (await l.dispatch("@keet/add-chat-message", { timestamp: Date.now(), message: n }))
      ? ((a.files.length || a.pearLink.length || a.httpLink.length) && (await ot(l, a)),
        await l.flush())
      : void 0
  }
  async _updateChatMessage(e, t, i, o = {}) {
    const r = s.decode(e),
      n = await this.keet.local.get("@local/rooms", { roomKey: r })
    if (!n) return
    const a = N(t),
      c = T(i, o),
      d = it(c),
      { isFeatureSupported: h, FEATURES: l } = this.keet.versions,
      u = h(n.digest.abi.version, l.MULTI_FILES)
    if (c.files.length && !u) throw new Error("multi files not supported")
    const m = this.keet.batch(e)
    0 !== (await m.dispatch("@keet/update-chat-message", { oplog: a, message: c })) &&
      ((d.files.length || d.pearLink.length || d.httpLink.length) && (await ot(m, d, a)),
      await m.flush())
  }
  async _removeChatMessage(
    { roomKey: e, local: t, roomId: s, snapshot: i, blobsSnapshot: o, files: r },
    n,
  ) {
    const a = await this._getChatMessageById(
      { roomKey: e, local: t, snapshot: i, blobsSnapshot: o, files: r },
      n,
    )
    if (!a) return null
    if (a.files.length) {
      const t = []
      for (const s of a.files) t.push(this._clearFile({ roomKey: e, files: r }, s))
      await Promise.all(t)
    }
    return this.keet.dispatch(s, "@keet/remove-chat-message", { oplog: N(n) })
  }
  async _forwardChatMessage(
    { roomKey: e, local: t, roomId: i, snapshot: o, blobsSnapshot: r, files: n },
    a,
    c,
  ) {
    const [d, h] = await Promise.all([
      this.keet.local.get("@local/rooms", { roomKey: e }),
      this.keet.local.get("@local/rooms", { roomKey: s.decode(c) }),
    ])
    if (!d || !h) return
    const { isFeatureSupported: l, FEATURES: u } = this.keet.versions
    if (!l(d.digest.abi.version, u.FORWARDED_MESSAGE))
      throw new Error("Message forwarding not supported")
    if (!l(h.digest.abi.version, u.FORWARDED_MESSAGE))
      throw new Error("Message forwarding not supported")
    const m = await o.get("@keet/chat-by-message-id", { oplog: N(a) })
    if (!m) throw new Error("Source message not found")
    if (!m.message.text) throw new Error("Source message has no text")
    const p = m.message.display || {}
    for (const e of p.httpLink || []) e.preview = null
    for (const e of p.pearLink || []) e.preview = null
    const b = {
      text: m.message.text,
      forwarded: !0,
      files: [],
      display: p,
      replyTo: null,
      event: null,
    }
    return this.keet.dispatch(c, "@keet/add-chat-message", { timestamp: Date.now(), message: b })
  }
  _pinChatMessage(e, t) {
    return this.keet.dispatch(e, "@keet/pin-chat-message", { oplog: N(t), isPinned: !0 })
  }
  _unpinChatMessage(e, t) {
    return this.keet.dispatch(e, "@keet/pin-chat-message", { oplog: N(t), isPinned: !1 })
  }
  async startCall(e, t = {}) {
    const o = await this.keet.internalFeatureSupported(e, Se.CALLS_V2)
    if ((this.keet.backendAPI.call.setSkipJoinLeave(o), !o)) return this.addCallStartedEvent(e)
    const r = s.decode(e),
      { version: n, callId: a, encryptionId: c } = t
    if (!n) throw new Error("Call version required")
    if (2 === n && !a) throw new Error("Calls v2 requires callId")
    const d = await this.keet.local.get("@local/rooms", { roomKey: r }),
      h = 2 === n ? s.decode(a) : i.randomBytes(32),
      l = c ? s.decode(c) : d.encryptionKey,
      u = 1 === n ? d.discoveryKey : null,
      m = Array.isArray(t?.streams) ? t.streams : []
    return this.keet.dispatch(e, "@keet/start-call", {
      version: n,
      callKey: h,
      encryptionKey: l,
      discoveryKey: u,
      streams: m.map((e) => s.decode(e)),
    })
  }
  async joinCall(e, t, i = []) {
    const o = await this.keet.internalFeatureSupported(e, Se.CALLS_V2)
    if ((this.keet.backendAPI.call.setSkipJoinLeave(o), o))
      return this.keet.dispatch(e, "@keet/join-call", {
        callKey: s.decode(t),
        streams: i.map((e) => s.decode(e)),
      })
  }
  async leaveCall(e, t) {
    if (await this.keet.internalFeatureSupported(e, Se.CALLS_V2))
      return this.keet.dispatch(e, "@keet/leave-call", { callKey: s.decode(t) })
  }
  async endCall(e, t) {
    if (!(await this.keet.internalFeatureSupported(e, Se.CALLS_V2))) {
      const t = s.decode(e)
      if (!(await this.keet.local.get("@local/rooms", { roomKey: t })).dm) return
      return this.addCallHangupEvent(e)
    }
    return this.keet.dispatch(e, "@keet/end-call", { callKey: s.decode(t) })
  }
  async addCallStream(e, t, i) {
    if (!(await this.keet.internalFeatureSupported(e, Se.CALLS_V2)))
      throw new Error("Calls v2 is not supported")
    return this.keet.dispatch(e, "@keet/add-call-stream", {
      callKey: s.decode(t),
      streamKey: s.decode(i),
    })
  }
  async removeCallStream(e, t, i) {
    if (!(await this.keet.internalFeatureSupported(e, Se.CALLS_V2)))
      throw new Error("Calls v2 is not supported")
    return this.keet.dispatch(e, "@keet/remove-call-stream", {
      callKey: s.decode(t),
      streamKey: s.decode(i),
    })
  }
  async _getActiveCalls({ snapshot: e }) {
    return de(e, "@keet/active-calls-by-timestamp")
  }
  async _getCalls({ snapshot: e }) {
    return de(e, "@keet/calls-by-timestamp")
  }
  subscribeActiveCalls(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getActiveCalls(e, ...t) })
  }
  subscribeCalls(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getCalls(e, ...t) })
  }
  _addCallStartedEvent(e) {
    return this.addChatMessage(e, null, { callStarted: !0 })
  }
  _addCallHangupEvent(e) {
    return this.addChatMessage(e, null, { callHangup: !0 })
  }
  _addCallMissedEvent(e) {
    return this.addChatMessage(e, null, { callMissed: !0 })
  }
  async _addLocationSharedEvent(e, t, { locationShared: { latitude: i, longitude: o } }) {
    const r = s.decode(e),
      n = await this.keet.local.get("@local/rooms", { roomKey: r })
    if (!n) return
    const { isFeatureSupported: a, FEATURES: c } = this.keet.versions
    if (!a(n.digest.abi.version, c.LOCATION_SHARED_EVENT))
      throw new Error("Location sharing not supported")
    return this.addChatMessage(e, t, { locationShared: { latitude: i, longitude: o } })
  }
  addRoomAvatarChangedEvent(...e) {
    ;(console.log("addRoomAvatarChangedEvent", ...e),
      console.warn("Missing required method: addRoomAvatarChangedEvent"))
  }
  addEvent(...e) {
    ;(console.log("addEvent", ...e), console.warn("Missing required method: addEvent"))
  }
  removeEvent(...e) {
    ;(console.log("removeEvent", ...e), console.warn("Missing required method: removeEvent"))
  }
  async _getChatMessage({ roomKey: e, local: t, snapshot: s, blobsSnapshot: i, files: o }, r) {
    return (
      await re(
        async function () {
          const t = U(e, { gte: r, lte: r })
          return await s.findOne("@keet/chat", t)
        },
        e,
        t,
        s,
        i,
        o,
      )
    )[0]
  }
  async _getChatMessageById({ roomKey: e, local: t, snapshot: s, blobsSnapshot: i, files: o }, r) {
    return (
      await re(
        async function () {
          return await s.get("@keet/chat-by-message-id", { oplog: N(r) })
        },
        e,
        t,
        s,
        i,
        o,
      )
    )[0]
  }
  async _getChatMessages({ roomKey: e, local: t, snapshot: s, blobsSnapshot: i, files: o }, r) {
    return await re(
      async function () {
        const t = U(e, r)
        return await s.find("@keet/chat", t).toArray()
      },
      e,
      t,
      s,
      i,
      o,
    )
  }
  async _getPinnedChatMessages(
    { roomKey: e, local: t, snapshot: s, blobsSnapshot: i, files: o },
    r,
  ) {
    return await re(
      async function () {
        return await s.find("@keet/pinned-chat-messages", r).toArray()
      },
      e,
      t,
      s,
      i,
      o,
    )
  }
  async _getChatLength({ db: e }) {
    const t = await e.get("@keet/threads", { id: 0 })
    return { length: t ? t.count : 0 }
  }
  subscribeChatMessages(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getChatMessages(e, ...t) })
  }
  subscribeChatLength(e, t) {
    let s = 0
    return this._addRoomSubscription(e, {
      query: async () => {
        if (!t || !t.reorders) return this.getChatLength(e)
        if (this.keet.closing) return null
        const i = this.keet.internalOpenExistingRoom(e)
        try {
          const { length: t } = await this.getChatLength(e),
            o = i.base.view.core.fork !== s
          return ((s = i.base.view.core.fork), { length: t, reordered: o })
        } finally {
          this.keet.internalReleaseRoom(i)
        }
      },
    })
  }
  async _searchChatMessages(
    { snapshot: e, roomKey: t, local: s, blobsSnapshot: i, files: o },
    n,
    { limit: a = 1e3, reverse: c = !0 } = {},
  ) {
    if (!n || !n.length) return []
    const d = await s.get("@local/rooms", { roomKey: t })
    if (!d) return []
    if (!(n = n.map((e) => e.replace(/\s/g, "").toLowerCase()).filter((e) => e)).length) return []
    const h = []
    for await (const s of e.find("@keet/chat", { limit: a, reverse: c })) {
      if (!s.message?.text) continue
      const a = s.message.text.replace(/\s/g, "").toLowerCase()
      if (n.every((e) => a.indexOf(e) > -1)) {
        const n = ne(t, e, i, d, o, s)
        ;(n.catch(r), h.push(n))
      }
    }
    return Promise.all(h)
  }
  createChatSearchStream(e, t, s) {
    return c.from(this._createChatSearchGenerator(e, t, s))
  }
  async *_createChatSearchGenerator(e, t, { limit: i = 1e3, reverse: o = !0 } = {}) {
    if (!t || !t.length) return
    if (!(t = t.map((e) => e.replace(/\s/g, "").toLowerCase()).filter((e) => e)).length) return
    const r = s.decode(e),
      n = await this.keet.local.get("@local/rooms", { roomKey: r })
    if (!n) return
    const a = this.keet.internalOpenExistingRoom(r)
    await a.ready()
    const c = a.db.snapshot(),
      d = a.blobs.snapshot(),
      h = this.keet.local.snapshot(),
      l = this.keet.files,
      u = c.find("@keet/chat", { limit: i, reverse: o })
    try {
      for await (const e of u) {
        const t = await m(e)
        t && (yield t)
      }
    } catch (e) {
      this.keet.onerror(e)
    } finally {
      ;(await h.close(), await c.close(), await d.close(), this.keet.internalReleaseRoom(a))
    }
    async function m(e) {
      const t = p(e.message?.text),
        i = !!e.message?.files.length
      if (!i && !b(t)) return
      const o = await ne(r, c, d, n, l, e)
      if (
        i &&
        !b(t) &&
        !(function (e) {
          return e.files.some((e) => b(p(e.metadata?.name)))
        })(o)
      )
        return
      if (o.labels.flagged || o.labels.flags >= be) return
      const a = s.decode(o.memberId)
      return (await h.get("@local/blocked-contacts", { memberKey: a })) ||
        (await c.get("@keet/muted-members", { key: a })) ||
        o.reactions.digest.reactions.find((e) => "⚠️" === e.text)
        ? void 0
        : o
    }
    function p(e) {
      return e ? e.replace(/\s/g, "").toLowerCase() : ""
    }
    function b(e) {
      return !!e && t.every((t) => e.indexOf(t) > -1)
    }
  }
  _sendDMFromContact(e, { message: t = null } = {}) {
    return this.keet.sendContactRequestFromContact(e, { message: t })
  }
  _sendDMRequest(e, t, { message: s = null } = {}) {
    return this.sendInvitations(e, { receivers: [t], message: s })
  }
  async _sendInvitations(e, { receivers: t, message: i }) {
    const o = s.decode(e),
      r = Array.isArray(t) ? t[0] : t,
      n = s.decode(r)
    await this.keet.sendContactRequest(o, n, { message: i })
  }
  async _updatePrivateMailboxRecord({ memberId: e, roomId: t }, i = {}) {
    this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
    const o = i.isSeen || 1 === i.seen
    return this.keet.privateProfile.dispatch("@keet-private-id/update-contact-request-status", {
      memberKey: s.decode(e),
      roomKey: s.decode(t),
      key: ee.IsSeen,
      status: { isSeen: o },
    })
  }
  async _getDmRequestsByStatus({ profileDb: e }, t, { reverse: s = !0 } = {}) {
    if (!e) return []
    const i =
      {
        [ue.ACCEPTED]: "@keet-private-id/accepted-contact-requests-v2",
        [ue.REJECTED]: "@keet-private-id/rejected-contact-requests-v2",
        [ue.PENDING]: "@keet-private-id/pending-contact-requests-v2",
        [ue.DISCARDED]: "@keet-private-id/discarded-contact-requests-v2",
      }[t] || "@keet-private-id/pending-contact-requests-v2"
    return (await e.find(i, { reverse: s }).toArray()).map(I)
  }
  subscribeDmRequestsByStatus(e, t = {}) {
    return this._addRoomSubscription(Be, { query: () => this.getDmRequestsByStatus(e, t) })
  }
  async _getPrivateMailboxRecords({ profile: e }, t = {}) {
    console.warn("Deprecated: getPrivateMailboxRecords")
  }
  subscribePrivateMailbox() {
    return (console.warn("Deprecated: subscribePrivateMailbox"), new c())
  }
  getSentPrivateMailboxRecords(...e) {
    console.warn("Deprecated: getSentPrivateMailboxRecords")
  }
  subscribeSentPrivateMailbox(...e) {
    return (console.warn("Deprecated: subscribeSentPrivateMailbox"), new c())
  }
  async _getDmByMemberId({ profileSnapshot: e }, t) {
    const o = s.decode(t),
      [r, n] = await Promise.all([
        e.get("@keet-private-id/contacts", { memberKey: o }),
        e.get("@keet-private-id/contact-requests", { memberKey: o }),
      ])
    let a = null
    return (
      r && r.roomKey ? (a = r.roomKey) : n && (a = n.keys.key),
      {
        roomId: a ? s.encode(a) : null,
        discoveryId: a ? s.encode(i.discoveryKey(a)) : null,
        dmMemberId: t,
        recipient: t,
      }
    )
  }
  async updateRoomInvitationSeenStatus({ discoveryId: e, invitationId: t }, i) {
    return this.keet.updateRoomInvitationSeenStatus(s.decode(e), s.decode(t), i)
  }
  async _acceptDmRequest({ memberId: e, roomId: t }) {
    return (
      this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
      this.keet.privateProfile.dispatch("@keet-private-id/update-contact-request-status", {
        memberKey: s.decode(e),
        roomKey: s.decode(t),
        key: ee.IsAccepted,
        status: { isAccepted: !0 },
      })
    )
  }
  async _rejectDmRequest({ memberId: e, roomId: t }) {
    return (
      this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
      this.keet.privateProfile.dispatch("@keet-private-id/update-contact-request-status", {
        memberKey: s.decode(e),
        roomKey: s.decode(t),
        key: ee.IsRejected,
        status: { isRejected: !0 },
      })
    )
  }
  async discardDmRequest({ memberId: e, roomId: t }) {
    return (
      this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
      this.keet.privateProfile.dispatch("@keet-private-id/update-contact-request-status", {
        memberKey: s.decode(e),
        roomKey: s.decode(t),
        key: ee.IsDiscarded,
        status: { isDiscarded: !0 },
      })
    )
  }
  _addReaction(e, t, i) {
    return this.keet.dispatch(e, "@keet/add-reaction", {
      oplog: { key: s.decode(t.deviceId), seq: t.seq },
      reaction: i,
    })
  }
  _removeReaction(e, t, i) {
    return this.keet.dispatch(e, "@keet/remove-reaction", {
      oplog: { key: s.decode(t.deviceId), seq: t.seq },
      reaction: i,
    })
  }
  async _getLatestReactions({ snapshot: e }, t, s, { limit: i } = {}) {
    const { id: o } = await e.get("@keet/chat-by-message-id", { oplog: N(t) })
    return (
      await e
        .find("@keet/reactions-by-clock", {
          gte: { id: o, reaction: s },
          lte: { id: o, reaction: s },
          limit: i,
          reverse: !0,
        })
        .toArray()
    ).map((e) => C(e))
  }
  async _getReactions({ roomKey: e, snapshot: t, local: s }, i) {
    const o = N(i)
    return ae({ roomKey: e, snapshot: t, local: s, oplog: o })
  }
  subscribeReactions(e, t) {
    return this._addRoomSubscription(e, { query: () => this.getReactions(e, t) })
  }
  subscribeReportedMessages(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getReportedMessages(e, ...t) })
  }
  subscribeReportedMembers(e, ...t) {
    return this._addRoomSubscription(e, { query: () => this.getReportedMembers(e, ...t) })
  }
  async _getReportedMembers({ snapshot: e, blobsSnapshot: t }, { clock: i, limit: o = 50 } = {}) {
    const r = await e.find("@keet/member-label-digests-by-clock", {
        gte: { label: $.Flag },
        lte: { label: $.Flag, clock: i },
        limit: o <= 50 ? o : 50,
        reverse: !0,
      }),
      n = []
    for await (const e of r) {
      const t = a.bind(this)
      n.push(t(e))
    }
    return await Promise.all(n)
    async function a(i) {
      const { key: o, total: r, flagsClock: n } = i,
        a = await this._getMember({ snapshot: e, blobsSnapshot: t }, s.encode(o))
      return ((a.labels = { total: r, flagsClock: n }), a)
    }
  }
  reportMember(e, t) {
    return this.keet.dispatch(e, "@keet/add-member-label", { key: s.decode(t), label: $.Flag })
  }
  async _getReportedMessages(
    { roomKey: e, local: t, snapshot: s, blobsSnapshot: i, files: o },
    { clock: r, limit: n = 50 } = {},
  ) {
    const a = await t.get("@local/rooms", { roomKey: e })
    if (!a) return
    const c = await s.find("@keet/chat-label-digests-by-clock", {
        gte: { label: X.Flag },
        lte: { label: X.Flag, clock: r },
        limit: n <= 50 ? n : 50,
        reverse: !0,
      }),
      d = []
    for await (const { id: e } of c) d.push(h(e))
    return await Promise.all(d)
    async function h(t) {
      const r = await s.findOne("@keet/chat", { gte: { id: t }, lte: { id: t } })
      return ne(e, s, i, a, o, r)
    }
  }
  _addChatReport(e, t) {
    const s = N(t)
    return this.keet.dispatch(e, "@keet/add-chat-label", { oplog: s, label: X.Flag })
  }
  _removeChatReport(e, t) {
    const s = N(t)
    return this.keet.dispatch(e, "@keet/remove-chat-label", { oplog: s, label: X.Flag })
  }
  addFileBlob(...e) {
    throw (console.log("addFileBlob", ...e), new Error("Missing required method: addFileBlob"))
  }
  addFile(...e) {
    throw (console.log("addFile", ...e), new Error("Missing required method: addFile"))
  }
  async _saveFileBlob({ roomKey: e, files: t }, s, i) {
    const o = await t.saveFileBlob(e, s, i)
    return S(o)
  }
  async _saveFile({ roomKey: e, files: t }, s, i) {
    const o = await t.saveFile(e, s, i)
    return S(o)
  }
  subscribeAddFile(e, t, i) {
    const o = s.decode(e)
    return h(
      this.keet.files.subscribeAddFile(o, t, i),
      new d({
        transform(e, t) {
          t(null, { ...e, result: S(e.result) })
        },
      }),
    )
  }
  async _sendFile(e, t) {
    const s = t.preview
    t.preview = null
    const i = this.keet.batch(e),
      o = await i.dispatch("@keet/add-chat-message", {
        timestamp: Date.now(),
        message: T(null, { files: [t] }),
      })
    0 !== o &&
      (s &&
        (await i.dispatch(
          "@keet/add-file-preview",
          { oplog: { key: i.key, seq: o - 1 }, index: 0, preview: V(s) },
          { dryRun: !1 },
        )),
      await i.flush())
  }
  async _sendFiles(e, t = []) {
    const s = []
    for (const e of t) (s.push(e.preview), (e.preview = null))
    const i = this.keet.batch(e),
      o = await i.dispatch("@keet/add-chat-message", {
        timestamp: Date.now(),
        message: T(null, { files: t }),
      })
    if (0 === o) return
    const r = { key: i.key, seq: o - 1 }
    for (let e = 0; e < s.length; e++) {
      const t = s[e]
      t &&
        (await i.dispatch(
          "@keet/add-file-preview",
          { oplog: r, index: e, preview: V(t) },
          { dryRun: !1 },
        ))
    }
    await i.flush()
  }
  removeFile(...e) {
    throw (console.log("removeFile", ...e), new Error("Missing required method: removeFile"))
  }
  subscribeFileInfo(e, t) {
    const i = s.decode(e),
      o = this
    let r = null
    return new c({
      async open(e) {
        let s = null
        try {
          ;((r = await o.keet.files.monitor(i, j(t))),
            r.on("update", (e) => {
              this.push(r.stats)
            }))
        } catch (e) {
          s = e
        }
        e(s)
      },
      async destroy(e) {
        ;(r && r.close(), e(null))
      },
    })
  }
  async _clearAllFiles({ roomId: e, roomKey: t, files: s }, i) {
    const o = await s.clearAllFiles(t, i)
    return (this.keet.emit("local-files-changed", e), o)
  }
  async _clearOldFiles() {
    return this.keet.files.clearOldFiles()
  }
  async _getFileEntry({ roomKey: e, files: t }, s) {
    const i = await t.getFileEntry(e, j(s))
    return q(i)
  }
  async _getLocalFiles({ roomKey: e, files: t, blobsSnapshot: s }) {
    const i = await t.getLocalFiles(e)
    return Promise.all(
      i.map(async (i) => {
        const o = await t.getFileEntryWithPreview(e, i.file, { loadBlob: (e) => s.get(e) })
        return { ...S(o.file), entry: q(o) }
      }),
    )
  }
  async _getFiles({ roomKey: e, files: t, snapshot: i, blobsSnapshot: o }, r = {}) {
    const n = G(r),
      a = await i.find(r.type ? "@keet/files-by-major-type" : "@keet/active-files", n).toArray()
    return Promise.all(
      a.map(async (i) => {
        const r = await t.getFileEntryWithPreview(e, i.file, { loadBlob: (e) => o.get(e) })
        return {
          ...S(r.file),
          id: i.id,
          messageId: { deviceId: s.encode(i.oplog.key), seq: i.oplog.seq },
          entry: q(r),
        }
      }),
    )
  }
  _startDownload({ roomKey: e, files: t }, s) {
    return t.startDownload(e, j(s))
  }
  _cancelDownload({ roomKey: e, files: t }, s) {
    return t.cancelDownload(e, s)
  }
  _readFileStream({ roomKey: e, files: t }, s) {
    return t.readFileStream(e, j(s))
  }
  _saveToDisk({ roomKey: e, files: t }, s, i) {
    return t.saveToDisk(e, j(s), i)
  }
  async _clearFile({ roomId: e, roomKey: t, files: s }, i) {
    ;(i.preview?.large?.pointer?.externalBlob && (await s.clearFile(t, j(i.preview.large))),
      await s.clearFile(t, j(i)),
      this.keet.emit("local-files-changed", e))
  }
  async _unclearFile({ roomId: e, roomKey: t, files: s }, i) {
    ;(i.preview?.large?.pointer?.externalBlob && (await s.unclearFile(t, j(i.preview.large))),
      await s.unclearFile(t, j(i)),
      this.keet.emit("local-files-changed", e))
  }
  _getFileCleared({ roomKey: e, files: t }, s) {
    return t.getFileCleared(e, j(s))
  }
  _refreshFileLinks(e) {
    return this.keet.files.refreshLinks(e)
  }
  subscribeFileCleared(e, t) {
    return this._addRoomSubscription(e, { query: () => this.getFileCleared(e, t) })
  }
  subscribeFiles(e, ...t) {
    return this._addRoomSubscription(e, { query: async () => this.getFiles(e, ...t) })
  }
  getStats() {
    return {
      suspended: this.keet.state === Ie.SUSPEND,
      connections: this.keet.swarm.connections.size,
      openRooms: this.keet.rooms.size,
      openCores: this.keet.store.cores.size,
      activeRooms: this.keet.activeRooms.size,
      roomSubscriptions: [...this._subscriptions.values()].filter((e) => e.isRoomSub).size,
      backgroundSwarmingRooms: [...this.keet.swarmWorker.sessions.keys()],
      dht: this.keet.swarm.dht.stats,
      swarm: this.keet.swarm.stats,
      ops: this.keet.stats.ops,
      uploaded: this.keet.stats.uploaded,
      bytesUploaded: this.keet.stats.bytesUploaded,
      downloaded: this.keet.stats.downloaded,
      bytesDownloaded: this.keet.stats.bytesDownloaded,
      appends: this.keet.stats.appends,
      bytesAppended: this.keet.stats.bytesAppended,
      ipc: { sent: 0, recv: 0 },
      threads: [],
    }
  }
  async getRoomStats(e) {
    const t = this.keet.internalOpenExistingRoom(s.decode(e))
    try {
      return await t.getStats()
    } finally {
      this.keet.internalReleaseRoom(t)
    }
  }
  async _lookupUsername(e) {
    if (!e) return
    const t = await this.keet.userRegistry.lookup(e)
    return E(t)
  }
  async _lookupUserByMemberId(e) {
    if (!e) return
    const t = s.decode(e),
      i = await this.keet.userRegistry.lookupByKey(t)
    return E(i)
  }
  async _searchUsername(e) {
    return (await this.keet.userRegistry.search(e)).map(E)
  }
  _registerUsername(e) {
    return this.keet.userRegistry.update(e)
  }
  _updateUsername(e) {
    return this.keet.userRegistry.update(e)
  }
  async _getReservationsByKey(e) {
    if (!e) return
    const t = s.decode(e),
      i = await this.keet.userRegistry.getReservationsByKey(t)
    if (!i || !i.length) return null
    const o = i[0]
    return P(o)
  }
  subscribeUsername() {
    const e = this.keet.userRegistry.subscribeUsername()
    return h(
      e,
      new d({
        transform(e, t) {
          if (!e) return t(null)
          t(null, {
            status: e.status,
            reservation: P(e.reservation),
            record: E(e.record),
            error: e.error,
          })
        },
      }),
    )
  }
  _checkUsername(e) {
    return this.keet.userRegistry.isAvailable(e)
  }
  subscribeSystemUpdateRequest() {
    return this._addKeetSubscription(Ue, {
      query: () => this.keet.migrationStatus || { remaining: 0 },
    })
  }
  close() {
    return this.keet.close()
  }
  suspend() {
    return this.keet.suspend()
  }
  resume() {
    return this.keet.resume()
  }
  async getDebugFlags() {
    const e = await this.keet.local.get("@local/debug")
    return e?.flags || []
  }
  async updateDebugFlag(e, t) {
    const s = ve.get(e)
    if (!(s >= 0)) return
    const i = await this.keet.internalEnterLocalTransaction()
    try {
      const e = await i.get("@local/debug")
      ;((e.flags[s].value = !0 === t), await i.insert("@local/debug", e), await i.flush())
    } finally {
      await this.keet.internalExitLocalTransaction()
    }
  }
  createErrorStream() {
    return h(
      this.keet.local.find("@local/errors"),
      new d({
        transform(e, t) {
          t(null, x(e))
        },
      }),
    )
  }
  async _repair(e) {
    const t = this.keet.internalOpenExistingRoom(s.decode(e))
    try {
      ;(await t.ready(), await t.base.repair())
    } finally {
      this.keet.internalReleaseRoom(t)
    }
  }
  async _audit(e) {
    const t = this.keet.rootCorestore.audit(e),
      s = []
    for await (const e of t) s.push(e)
    return s
  }
  async _rotateWriter(e) {
    return this.keet.rotateWriter(s.decode(e))
  }
  compact() {
    return this.keet.storage.compact()
  }
  async _setUnread(e, t) {
    const i = s.decode(e),
      o = await this.keet.internalEnterLocalTransaction()
    try {
      ;(await o.insert("@local/unreads", { roomKey: i, count: t }), await o.flush())
    } finally {
      await this.keet.internalExitLocalTransaction()
    }
  }
  async _getUnread(e) {
    const t = s.decode(e),
      i = await this.keet.local.get("@local/unreads", { roomKey: t })
    return i ? i.count : 0
  }
  async _setUnreadAnchor(e, t) {
    const i = s.decode(e)
    ;(this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()),
      this.keet.unreads && (await this.keet.unreads.put(i, { timestamp: Date.now(), length: t })))
    const o = await this.keet.local.get("@local/rooms", { roomKey: i })
    if (!o?.dm?.length) return
    const { isFeatureSupported: r, FEATURES: n } = this.keet.versions
    if (!r(o.digest.abi.version, n.READ_LENGTH)) return
    const a = await this.keet.internalOpenPrivateProfile()
    try {
      const s = await a.db.get("@keet-private-id/settings")
      if (s?.disableReadReceipts) return
      await this.keet.dispatch(e, "@keet/update-read-length", { length: t })
    } finally {
      this.keet.internalReleaseRoom(a)
    }
  }
  async _getUnreadAnchor(e) {
    if ((this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()), !this.keet.unreads))
      return null
    const t = s.decode(e)
    return this.keet.unreads.get(t)
  }
  _subscribeUnreadAnchor(e) {
    return this._addKeetSubscription(Ne, { query: () => this.getUnreadAnchor(e) })
  }
  async _setRoomFavorite(e, t = !0) {
    this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
    const { privateProfileKey: i } = await this.keet.local.get("@local/identity"),
      o = await this.keet.local.get("@local/rooms", { roomKey: i })
    if (!o) return
    const { isFeatureSupported: r, FEATURES: n } = this.keet.versions
    if (!r(o.digest.abi.version, n.FAVORITE_ROOMS))
      throw new Error("Favorite rooms is not supported")
    await this.keet.privateProfile.dispatch("@keet-private-id/update-room-status", {
      roomKey: s.decode(e),
      key: te.IsFavorite,
      status: { isFavorite: t },
    })
  }
}
class Je {
  constructor(e) {
    this.keet = e
  }
  async ready() {
    return (await this.keet.ready(), s.encode(this.keet.swarm.keyPair.publicKey))
  }
}
class We {
  constructor(t) {
    ;((this.keet = t), (this.preferences = new b(e.join(t.dir, Ke))), (this._subs = []))
  }
  async _bump() {
    const e = await this.preferences.list()
    for (const t of this._subs) t.push(e)
  }
  async update(e, t) {
    ;(await this.preferences.set(e, t), await this._bump())
  }
  query() {
    return this.preferences.list()
  }
  subscribe() {
    const e = this,
      t = new c({
        async open(e) {
          let t = null
          try {
            await this._bump()
          } catch (e) {
            t = e
          }
          e(t)
        },
        destroy(t) {
          const s = e._subs.indexOf(this)
          ;(-1 !== s && e._subs.splice(s, 1), t(null))
        },
      })
    return (this._subs.push(t), t)
  }
}
class Ye {
  constructor(e, { onerror: t = r } = {}) {
    ;((this.keet = e),
      (this._status = null),
      (this._subs = []),
      this.keet.swarm.on("update", this._handleUpdate.bind(this)),
      this.keet.swarm.dht.on("network-update", this._handleUpdate.bind(this)))
  }
  _handleUpdate() {
    this._status = this.query()
    for (const e of this._subs) e.push(this._status)
  }
  query() {
    const e = []
    for (const [t, i] of this.keet.swarm.peers) e.push(s.encode(i.publicKey))
    return {
      host: this.keet.swarm.dht.host,
      port: this.keet.swarm.dht.port,
      online: this.keet.swarm.dht.online,
      degraded: this.keet.swarm.dht.degraded,
      connections: this.keet.swarm.connections.size,
      peers: e,
    }
  }
  subscribe() {
    const e = this,
      t = new c({
        open(t) {
          ;(e._status && this.push(e._status), t(null))
        },
        destroy(t) {
          const s = e._subs.indexOf(this)
          ;(-1 !== s && e._subs.splice(s, 1), t(null))
        },
      })
    return (this._subs.push(t), t)
  }
  subscribeDebug(e = 5e3, t = 0) {
    const s = this
    let i = null,
      o = 0
    const r = new c({
      open(t) {
        ;(n(),
          (i = setInterval(() => {
            n()
          }, e)),
          t(null))
      },
      destroy(e) {
        ;(i && (clearInterval(i), (i = null)), e(null))
      },
    })
    return r
    async function n() {
      const e = await s.debug()
      ;(r.push(e), (o += 1), t && o >= t && r.push(null))
    }
  }
  async debug() {
    return {
      tick: this.keet.swarm.dht._tick,
      localAddress: this.keet.swarm.dht.localAddress(),
      remoteAddress: this.keet.swarm.dht.remoteAddress(),
      connection: this.query(),
      stats: this.keet.swarm.dht.stats,
      health: this.keet.swarm.dht.health.stats,
      nodes: this.keet.swarm.dht.nodes.length,
      config: this.keet.swarm.dht.config,
      rttStats: await this.keet.swarm.dht.rttStats(),
    }
  }
}
class Xe {
  constructor() {
    ;((this.subscriptions = []),
      (this._handleError = this._handleError.bind(this)),
      (this._listenersAttached = !1))
  }
  _handleError(e) {
    console.error("uncaught on worker", e)
    for (const t of this.subscriptions) t.push({ message: e.message, stack: e.stack + "" })
  }
  subscribe(e) {
    const t = this
    this._listenersAttached || (a.on(this._handleError), (this._listenersAttached = !0))
    const s = new c({
      ...e,
      destroy(e) {
        const s = t.subscriptions.indexOf(this)
        ;(-1 !== s && t.subscriptions.splice(s, 1), e(null))
      },
    })
    return (this.subscriptions.push(s), s)
  }
}
class $e {
  constructor(e) {
    ;((this.keet = e), (this.inspector = new Re()))
  }
  _getCurrentRoom() {
    let e = null
    for (const t of this.keet.rooms.values()) (!e || t.refs > e.refs) && (e = t)
    return e
  }
  async getCurrentRoomStats() {
    const e = this._getCurrentRoom()
    return { hash: (await e?.base.core.treeHash()).toString("hex"), length: e?.base.length }
  }
  enableInspector() {
    return this.inspector?.enable()
  }
  disableInspector() {
    return this.inspector?.disable()
  }
  getInspectorKey() {
    return this.inspector?.key
  }
  processTop() {
    return p.toJSON()
  }
  hash(e) {
    return i.hash(e)
  }
  generateSyntheticError(e, t = {}) {
    const s = new Error(t.message ?? t.code ?? "KEET_SYNTHETIC_ERROR")
    if (((s.code = t.code ?? s.message), Object.assign(s, t), !e)) throw s
    this.keet.onerror(s)
  }
  async heapSnapshot(
    e = "/tmp/keet-heapsnapshot-" + Bare.pid + "." + Date.now() + ".heapsnapshot",
  ) {
    const t = new l()
    t.connect()
    const s = new u(t),
      i = m.createWriteStream(e)
    return (s.pipe(i), await new Promise((e) => i.once("close", e)), t.destroy(), e)
  }
  async resetUnreads() {
    if ((this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready()), !this.keet.unreads))
      return null
    await this.keet.unreads.reset()
  }
}
class Qe {
  constructor(e) {
    ;((this._rpcClient = e.rpcClient), (this.keet = e))
  }
  send(e) {
    if (this.keet.buildType === J.Production)
      return this._rpcClient.makeRequest(this.keet.config.services.feedback, "send", e, {
        requestEncoding: _e.json,
      })
  }
}
function Ze(e) {
  switch (e) {
    case 1:
      return te.IsFullyMuted
    case 2:
      return te.IsMentionsOnlyMuted
    default:
      return 0
  }
}
function et(e) {
  switch (e) {
    case "title":
      return se.Title
    case "avatar":
      return se.Avatar
    case "description":
      return se.Description
    case "supportedLanguages":
      return se.SupportedLanguages
    case "blindPeers":
      return se.BlindPeers
    default:
      return 0
  }
}
function tt(e) {
  switch (e) {
    case "0":
    default:
      return W.Default
    case "1":
      return W.Broadcast
    case "2":
      return W.DirectMessage
  }
}
function st(e = {}) {
  return {
    config: { title: e.config?.title, description: e.config?.description },
    settings: {
      roomType: tt(e.config?.roomType),
      isCallingDisabled: "false" === e.config?.canCall,
    },
  }
}
function it(e) {
  const t = { files: [], httpLink: [], pearLink: [] },
    s = e.files || []
  for (let e = 0; e < s.length; e++) {
    const i = s[e].preview
    i && ((s[e].preview = null), t.files.push({ index: e, preview: i }))
  }
  const i = e.display?.httpLink || []
  for (let e = 0; e < i.length; e++) {
    const s = r(i[e])
    s && t.httpLink.push({ index: e, preview: s })
  }
  const o = e.display?.pearLink || []
  for (let e = 0; e < o.length; e++) {
    const s = r(o[e])
    s && t.pearLink.push({ index: e, preview: s })
  }
  return t
  function r(e) {
    if (!e.preview?.file?.preview) return null
    const t = e.preview.file.preview
    return ((e.preview.file.preview = null), t)
  }
}
async function ot(e, { files: t, httpLink: s, pearLink: i }, o = null) {
  const r = o || { key: e.key, seq: e.length - 1 }
  if (t.length)
    for (const { index: s, preview: i } of t)
      await e.dispatch(
        "@keet/add-file-preview",
        { oplog: r, index: s, preview: V(i) },
        { dryRun: !1 },
      )
  if (s.length)
    for (const { index: t, preview: i } of s)
      await e.dispatch(
        "@keet/add-file-preview",
        { oplog: r, index: t, preview: V(i), displayType: Y.HttpLink },
        { dryRun: !1 },
      )
  if (i.length)
    for (const { index: t, preview: s } of i)
      await e.dispatch(
        "@keet/add-file-preview",
        { oplog: r, index: t, preview: V(s), displayType: Y.PearLink },
        { dryRun: !1 },
      )
}
module.exports = class extends t {
  constructor(e, t) {
    ;(super(),
      (this.keet = e),
      (this.opts = t),
      (this.api = {
        core: new ze(e, t),
        swarm: null,
        preferences: null,
        network: null,
        error: null,
        debug: null,
        console: null,
        call: null,
        pear: {
          teardown: () => {
            console.log("[api] teardown initiated")
            const t = this.keet.close()
            return (t.catch(e.onerror), t)
          },
          selfDestruct: (e) => this.keet.selfDestruct(e),
        },
      }),
      (this._callBootstraps = null),
      (this.callsByRoomId = new Map()),
      this.ready().catch(t?.onerror || r))
  }
  async _open() {
    ;(await this.keet.ready(),
      (this.api.swarm = new Je(this.keet, this.opts)),
      (this.api.preferences = new We(this.keet, this.opts)),
      (this.api.network = new Ye(this.keet, this.opts)),
      (this.api.error = new Xe(this.keet, this.opts)),
      (this.api.debug = new $e(this.keet, this.opts)),
      (this.api.feedback = new Qe(this.keet)))
    const { isFeatureSupported: e, FEATURES: t } = this.keet.versions
    ;((this.call = this.api.call =
      new g(this.keet.swarm, { skipJoinLeave: e(this.keet.abi.autoupgrade, t.CALLS_V2) })),
      this.keet.on("call-changed", this._handleCallChanged.bind(this)))
  }
  async _close() {
    ;(this.call && this.call.close(), await this.api.debug.disableInspector())
  }
  async closeActiveCalls() {
    if (this.callsByRoomId.size)
      for (const [e, t] of this.callsByRoomId) {
        const i = s.decode(e),
          o = await this.keet.local.get("@local/rooms", { roomKey: i })
        if (!o) continue
        const r = this.keet.rooms.get(e)
        if (!r) continue
        const { isFeatureSupported: n, FEATURES: a } = this.keet.versions
        if (!n(o.digest.abi.version, a.CALLS_V2)) return
        const c = o.dm ? "@keet/end-call" : "@keet/leave-call"
        await r.dispatch(c, { callKey: s.decode(t.callId) })
      }
  }
  _handleCallChanged(e, t, s) {
    const i = L(t),
      o = this.callsByRoomId.get(e),
      r = s?.deviceKey || null
    if (!s && !o) return
    const n = { joinPeer: this.keet.debugJoinPeer, leavePeer: this.keet.debugLeavePeer },
      a = new Me(this.keet.swarm, { call: i, prev: o, deviceKey: r, debug: n })
    if ((a.refresh(), a.ended)) return this.callsByRoomId.delete(e)
    this.callsByRoomId.set(e, a)
  }
  async suspend() {
    await this.keet.suspend()
  }
  async resume() {
    await this.keet.resume()
  }
}