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: h, pipeline: d } = 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: w,
encodeMemberRecord: v,
encodeInvitationRecord: R,
encodeFile: S,
encodeIdentityPairingRequest: M,
encodeDmRequest: I,
encodeReaction: C,
encodeUser: E,
encodeContactInfo: K,
encodeImageFile: D,
encodeReservation: q,
encodeFileEntry: P,
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"),
{ ContactRequestStatus: Z, RoomStatus: ee } = require("../../core/contract/identity/private.js"),
{ Config: te, CONFIG_INDEX_TO_KEY: se } = require("../../core/contract/config"),
{ Settings: ie } = require("../../core/contract/internal"),
{
getChatMessages: oe,
getChatMessage: re,
getReactions: ne,
isDMRead: ae,
getCalls: ce,
} = require("./helpers.js"),
{ MemberStatus: he } = require("../../core/contract/members/index.js"),
{
MemberRoles: de,
DM_STATUSES: le,
isValidPreferenceKey: ue,
getPreferenceKeyName: me,
CHAT_MESSAGE_REPORT_COUNT: pe,
PREFERENCES: be,
GENERIC_EVENTS: ge,
} = require("@holepunchto/keet-core-api"),
{ restoreAvatar: ye } = require("../../core/contract/avatars.js"),
{ getMemberForDeviceKey: fe } = require("../../core/contract/query.js"),
ke = require("compact-encoding"),
{ DEBUG_FLAG_INDEX_BY_KEY: _e, DHT_SETTINGS: we } = require("../../core/debug/debugging.js"),
ve = require("./inspector.js"),
{ FEATURES: Re } = require("../../../schema/versions"),
Se = require("./calls.js"),
Me = f("@local/keet-state"),
Ie = 6e5,
Ce = 2e4,
Ee = "preferences_v1.json",
Ke = "keet",
De = "recent-rooms",
qe = "favorite-rooms",
Pe = "dm-rooms",
Fe = "chat-changed",
xe = "identity",
Ae = "private-profile",
Be = "identity-pairing-requests",
Le = "migration-status",
Ue = "room-preferences",
Te = "unreads",
Ne = "errors",
Oe = "force-updates",
je = {
"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 He {
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 Ve {
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 Ge {
constructor(e, { onerror: t = r } = {}) {
;((this.keet = e),
(this.context = new Ve(this.keet)),
(this._onerror = t),
(this._subscriptions = new Map()),
(this._profileSubscriptions = new He(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.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.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: ie.IsMembershipEventsDisabled,
settings: { isMembershipEventsDisabled: !0 },
})
}
_enableMembershipEvents(e) {
return this.keet.dispatch(e, "@keet/update-system-settings", {
key: ie.IsMembershipEventsDisabled,
settings: { isMembershipEventsDisabled: !1 },
})
}
_addKeetSubscription(e = Ke, 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 He(this, e)), this._subscriptions.set(e, o)), o.add(t))
}
_refreshSubscriptionsById(e) {
const t =
e === this.keet.privateProfile?.id ? this._subscriptions.get(Ae) : this._subscriptions.get(e)
t && t.refresh()
}
_handleIdentityPairingRequest(e) {
;(this._identityPairingRequestsByDeviceId.set(s.encode(e.deviceKey), e),
this._refreshSubscriptionsById(Be))
}
_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(De)
t && t.push(e)
}
_handleDmRoomsChanged() {
const e = this._subscriptions.get(Pe)
e && e.refresh()
}
_handleRoomPreferencesChanged(...e) {
const t = this._subscriptions.get(Ue)
t && t.push(e)
}
_handleUnreadAnchorsChanged() {
const e = this._subscriptions.get(Te)
e && e.refresh()
}
_handleRoomSentMessageLengthChanged(e) {
const t = this._subscriptions.get(e)
t && t.refresh({ name: "room-info" })
}
_handleIdentityChanged() {
this._refreshSubscriptionsById(xe)
}
async _handleChatChanged(...e) {
try {
this.keet.onlineBoot.opened || (await this.keet.onlineBoot.ready())
const t = this._subscriptions.get(Fe)
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(Le)
t && t.push(e)
}
_handleKeetError(e) {
const t = this._subscriptions.get(Ne)
t && t.push(e)
}
_handleForceUpdate() {
const e = this._subscriptions.get(Oe)
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: je }
}
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(Oe, {
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({ ...w(e), confirmed: !0 })
const c = v(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
)
}
_removeIdentityDevice({ profile: e }, t) {
return e.dispatch("@keet/remove-device", { key: s.decode(t) })
}
subscribeIdentity() {
return this._addKeetSubscription(xe, { 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(xe, { 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 }
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(tt(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(xe, { query: () => this.getPendingRooms() })
}
subscribePushNotificationChanges() {
return this._addKeetSubscription(Ue, {
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())
const e = []
for await (const t of this.keet.privateProfile.db.find("@keet-private-id/rooms"))
(await this.keet.local.get("@local/rooms", { roomKey: t.roomKey })) || e.push(t)
for (const t of e) this.keet.tasks.joinRoomKeys(t.roomKey, { keys: t }).runBackground()
}
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 = v(r.lastMember)
const [a, c, h] = 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 = ae(r, r.lastMessage.id)
r.lastMessage.read = e
}
const d = r.selfDevice ? i.discoveryKey(r.selfDevice.key) : null,
l = d ? await this.keet.storage.getInfo(d, { hints: !0, auth: !1, head: !1 }) : null,
u = k({ roomInfo: r, preferences: a, lastMessageMember: n, storageInfo: l, fileStats: h })
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(De, {
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(Fe, {
map: ({ roomId: t, index: s }) =>
t ? (e.includeLength || e.includeIndex ? { roomId: t, index: s } : { roomId: t }) : null,
})
}
subscribeErrors() {
return this._addKeetSubscription(Ne, { 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 === ge.CALL_HANGUP && (yield { type: ge.CALL_HANGUP })
}
if (this._processingNotifications) continue
if (this.keet.backendAPI.api.call.callIsActive) continue
const e = await this.getRoomPreferences(i, be.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) > Ie) 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 h = `${n.id.deviceId}/b${n.id.seq}`,
d = 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 = et(r.roomType) === W.DirectMessage
if (!this._processingNotifications && (m || d)) {
if (p && "call" === d) {
if (Math.abs(Date.now() - n.timestamp) > Ce) continue
if (!(await this.getActiveCalls(i)).length) continue
yield { type: ge.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: h,
roomId: i,
avatar: t,
location: s,
messageText: m,
messageType: d,
fileType: l,
},
r,
u,
)
}
}
function t(e) {
return e.event?.type === ge.CALL_STARTED
? "call"
: e.files?.length > 0
? "file"
: e.message?.generic?.locationShared
? "location"
: ""
}
}
setRoomUnread(...e) {
throw (console.log("setRoomUnread", ...e), new Error("Missing required method: setRoomUnread"))
}
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 ? w(s) : null
}
async _getDevices({ db: e }, t) {
return (await e.find("@keet/devices", O(t)).toArray()).map((e) => w(e))
}
async moveIndexer(e, t, i) {
if (!(await this.keet.internalFeatureSupported(e, Re.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 fe(t, s.decode(o))
return r ? (await ye(i, r.profile.avatar), v(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 fe(t, n.key)
return a ? (await ye(i, a.profile.avatar), v(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(w(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 h = await o.get("@keet/members", { key: c })
return h ? (await ye(r, h.profile.avatar), v(h, 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 ye(i, r.profile.avatar), v(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 ye(s, t.profile.avatar), v(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 ye(s, t.profile.avatar), v(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 ye(s, t.profile.avatar), v(t, e))))
}
async _waitUntilJoined({ roomKey: e, local: t }) {
const s = await t.get("@local/rooms", { roomKey: e })
return s ? v(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 === de.ADMIN,
isModerator: i === de.ADMIN || i === de.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 ye(s, t.profile.avatar), v(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(Pe, {
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(Ae, { 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 = Ze(t)
if (0 !== i) {
const t = { [se[i - 1]]: s }
await this.keet.dispatch(e, "@keet/update-room-config", { key: i, config: H(t) })
} else if ("roomType" === t) {
const t = et(s)
if (0 === t) throw new Error("Invalid room type")
await this.keet.dispatch(e, "@keet/update-system-settings", {
key: ie.RoomType,
settings: { roomType: t },
})
} else {
if ("canCall" !== t) throw new Error("Invalid config key")
await this.keet.dispatch(e, "@keet/update-system-settings", {
key: ie.IsCallingDisabled,
settings: { isCallingDisabled: "false" === s },
})
}
}
async _updateRoomPreferences({ profile: e }, t, i, o) {
if (!ue(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: Qe(i),
status: { [me(i)]: o },
})
}
async _updateRoomSettings(e, t, i) {
if (
t === Q.IsAttachmentsDisabled &&
!(await this.keet.internalFeatureSupported(e, Re.DISABLE_FILE_ATTACHMENTS))
)
throw new Error("Disabling file attachments not supported")
if (t === Q.IsLinksDisabled && !(await this.keet.internalFeatureSupported(e, Re.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 ue(i) ? (i ? n[me(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 = st(n),
{ isFeatureSupported: c, FEATURES: h } = this.keet.versions,
d = c(r.digest.abi.version, h.MULTI_FILES)
if (n.files.length && !d) 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 it(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),
h = st(c),
{ isFeatureSupported: d, FEATURES: l } = this.keet.versions,
u = d(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 })) &&
((h.files.length || h.pearLink.length || h.httpLink.length) && (await it(m, h, 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 [h, d] = await Promise.all([
this.keet.local.get("@local/rooms", { roomKey: e }),
this.keet.local.get("@local/rooms", { roomKey: s.decode(c) }),
])
if (!h || !d) return
const { isFeatureSupported: l, FEATURES: u } = this.keet.versions
if (!l(h.digest.abi.version, u.FORWARDED_MESSAGE))
throw new Error("Message forwarding not supported")
if (!l(d.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, Re.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 h = await this.keet.local.get("@local/rooms", { roomKey: r }),
d = 2 === n ? s.decode(a) : i.randomBytes(32),
l = c ? s.decode(c) : h.encryptionKey,
u = 1 === n ? h.discoveryKey : null,
m = Array.isArray(t?.streams) ? t.streams : []
return this.keet.dispatch(e, "@keet/start-call", {
version: n,
callKey: d,
encryptionKey: l,
discoveryKey: u,
streams: m.map((e) => s.decode(e)),
})
}
async joinCall(e, t, i = []) {
const o = await this.keet.internalFeatureSupported(e, Re.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, Re.CALLS_V2))
return this.keet.dispatch(e, "@keet/leave-call", { callKey: s.decode(t) })
}
async endCall(e, t) {
if (!(await this.keet.internalFeatureSupported(e, Re.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, Re.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, Re.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 ce(e, "@keet/active-calls-by-timestamp")
}
async _getCalls({ snapshot: e }) {
return ce(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 oe(
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 oe(
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 oe(
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 oe(
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 h = await s.get("@local/rooms", { roomKey: t })
if (!h) return []
if (!(n = n.map((e) => e.replace(/\s/g, "").toLowerCase()).filter((e) => e)).length) return []
const d = []
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 = re(t, e, i, h, o, s)
;(n.catch(r), d.push(n))
}
}
return Promise.all(d)
}
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(),
h = a.blobs.snapshot(),
d = 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 d.close(), await c.close(), await h.close(), this.keet.internalReleaseRoom(a))
}
async function m(e) {
if (!e.message?.text) return
const i = e.message.text.replace(/\s/g, "").toLowerCase()
if (!t.every((e) => i.indexOf(e) > -1)) return
const o = await re(r, c, h, n, l, e)
if (o.labels.flagged || o.labels.flags >= pe) return
const a = s.decode(o.memberId)
return (await d.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
}
}
_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: Z.IsSeen,
status: { isSeen: o },
})
}
async _getDmRequestsByStatus({ profileDb: e }, t, { reverse: s = !0 } = {}) {
if (!e) return []
const i =
{
[le.ACCEPTED]: "@keet-private-id/accepted-contact-requests-v2",
[le.REJECTED]: "@keet-private-id/rejected-contact-requests-v2",
[le.PENDING]: "@keet-private-id/pending-contact-requests-v2",
[le.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(Ae, { 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: Z.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: Z.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: Z.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 ne({ 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,
}),
h = []
for await (const { id: e } of c) h.push(d(e))
return await Promise.all(h)
async function d(t) {
const r = await s.findOne("@keet/chat", { gte: { id: t }, lte: { id: t } })
return re(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 d(
this.keet.files.subscribeAddFile(o, t, i),
new h({
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 P(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: P(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: P(r),
}
}),
)
}
_startDownload({ roomKey: e, files: t }, s) {
return t.startDownload(e, j(s))
}
_cancelDownload({ roomKey: e, files: t }, s) {
return t.cancelDownload(e, s)
}
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 === Me.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 q(o)
}
subscribeUsername() {
const e = this.keet.userRegistry.subscribeUsername()
return d(
e,
new h({
transform(e, t) {
if (!e) return t(null)
t(null, {
status: e.status,
reservation: q(e.reservation),
record: E(e.record),
error: e.error,
})
},
}),
)
}
_checkUsername(e) {
return this.keet.userRegistry.isAvailable(e)
}
subscribeSystemUpdateRequest() {
return this._addKeetSubscription(Le, {
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 = _e.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 d(
this.keet.local.find("@local/errors"),
new h({
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(Te, { 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: ee.IsFavorite,
status: { isFavorite: t },
})
}
}
class ze {
constructor(e) {
this.keet = e
}
async ready() {
return (await this.keet.ready(), s.encode(this.keet.swarm.keyPair.publicKey))
}
}
class Je {
constructor(t) {
;((this.keet = t), (this.preferences = new b(e.join(t.dir, Ee))), (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 We {
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 Ye {
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 Xe {
constructor(e) {
;((this.keet = e), (this.inspector = new ve()))
}
_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) {
if (!e) throw new Error("KEET_CORE_ERROR_UNHANDLED")
this.keet.onerror(new Error("KEET_CORE_ERROR_HANDLED"))
}
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 $e {
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: ke.json,
})
}
}
function Qe(e) {
switch (e) {
case 1:
return ee.IsFullyMuted
case 2:
return ee.IsMentionsOnlyMuted
default:
return 0
}
}
function Ze(e) {
switch (e) {
case "title":
return te.Title
case "avatar":
return te.Avatar
case "description":
return te.Description
case "supportedLanguages":
return te.SupportedLanguages
case "blindPeers":
return te.BlindPeers
default:
return 0
}
}
function et(e) {
switch (e) {
case "0":
default:
return W.Default
case "1":
return W.Broadcast
case "2":
return W.DirectMessage
}
}
function tt(e = {}) {
return {
config: { title: e.config?.title, description: e.config?.description },
settings: {
roomType: et(e.config?.roomType),
isCallingDisabled: "false" === e.config?.canCall,
},
}
}
function st(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 it(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 Ge(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 ze(this.keet, this.opts)),
(this.api.preferences = new Je(this.keet, this.opts)),
(this.api.network = new We(this.keet, this.opts)),
(this.api.error = new Ye(this.keet, this.opts)),
(this.api.debug = new Xe(this.keet, this.opts)),
(this.api.feedback = new $e(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 Se(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()
}
}