@wireapp/core 46.46.6-beta.14.f6fd03fe6 → 46.46.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Account.d.ts +156 -51
- package/lib/Account.d.ts.map +1 -1
- package/lib/Account.js +503 -127
- package/lib/Account.test.js +158 -147
- package/lib/broadcast/AvailabilityType.d.ts +1 -1
- package/lib/broadcast/AvailabilityType.d.ts.map +1 -1
- package/lib/broadcast/BroadcastService.d.ts +1 -1
- package/lib/broadcast/BroadcastService.d.ts.map +1 -1
- package/lib/broadcast/BroadcastService.js +1 -1
- package/lib/client/ClientService.d.ts +4 -3
- package/lib/client/ClientService.d.ts.map +1 -1
- package/lib/client/ClientService.js +19 -5
- package/lib/conversation/AbortReason.d.ts +1 -1
- package/lib/conversation/AbortReason.d.ts.map +1 -1
- package/lib/conversation/AssetService/AssetService.d.ts +12 -30
- package/lib/conversation/AssetService/AssetService.d.ts.map +1 -1
- package/lib/conversation/AssetService/AssetService.js +1 -10
- package/lib/conversation/AssetService/AssetService.test.js +8 -3
- package/lib/conversation/ClientActionType.d.ts +1 -1
- package/lib/conversation/ClientActionType.d.ts.map +1 -1
- package/lib/conversation/ClientActionType.js +1 -1
- package/lib/conversation/ConversationService/ConversationService.d.ts +98 -14
- package/lib/conversation/ConversationService/ConversationService.d.ts.map +1 -1
- package/lib/conversation/ConversationService/ConversationService.js +314 -101
- package/lib/conversation/ConversationService/ConversationService.test.js +441 -47
- package/lib/conversation/ConversationService/ConversationService.types.d.ts +5 -4
- package/lib/conversation/ConversationService/ConversationService.types.d.ts.map +1 -1
- package/lib/conversation/ConversationService/Utility/getConversationQualifiedMembers.d.ts.map +1 -1
- package/lib/conversation/ConversationService/Utility/getConversationQualifiedMembers.js +6 -3
- package/lib/conversation/SubconversationService/SubconversationService.d.ts.map +1 -1
- package/lib/conversation/SubconversationService/SubconversationService.js +158 -11
- package/lib/conversation/SubconversationService/SubconversationService.test.js +8 -2
- package/lib/conversation/content/AssetContent.d.ts +1 -1
- package/lib/conversation/content/AssetContent.d.ts.map +1 -1
- package/lib/conversation/content/ButtonActionConfirmationContent.d.ts +1 -1
- package/lib/conversation/content/ButtonActionConfirmationContent.d.ts.map +1 -1
- package/lib/conversation/content/ButtonActionContent.d.ts +1 -1
- package/lib/conversation/content/ButtonActionContent.d.ts.map +1 -1
- package/lib/conversation/content/ClearedContent.d.ts +1 -1
- package/lib/conversation/content/ClearedContent.d.ts.map +1 -1
- package/lib/conversation/content/ClientActionContent.d.ts +1 -1
- package/lib/conversation/content/ClientActionContent.d.ts.map +1 -1
- package/lib/conversation/content/CompositeContent.d.ts +1 -1
- package/lib/conversation/content/CompositeContent.d.ts.map +1 -1
- package/lib/conversation/content/ConfirmationContent.d.ts +1 -1
- package/lib/conversation/content/ConfirmationContent.d.ts.map +1 -1
- package/lib/conversation/content/DeletedContent.d.ts +1 -1
- package/lib/conversation/content/DeletedContent.d.ts.map +1 -1
- package/lib/conversation/content/HiddenContent.d.ts +1 -1
- package/lib/conversation/content/HiddenContent.d.ts.map +1 -1
- package/lib/conversation/content/KnockContent.d.ts +1 -1
- package/lib/conversation/content/KnockContent.d.ts.map +1 -1
- package/lib/conversation/content/LinkPreviewContent.d.ts +1 -1
- package/lib/conversation/content/LinkPreviewContent.d.ts.map +1 -1
- package/lib/conversation/content/MentionContent.d.ts +1 -1
- package/lib/conversation/content/MentionContent.d.ts.map +1 -1
- package/lib/conversation/content/MultipartContent.d.ts +1 -1
- package/lib/conversation/content/MultipartContent.d.ts.map +1 -1
- package/lib/conversation/content/QuoteContent.d.ts +1 -1
- package/lib/conversation/content/QuoteContent.d.ts.map +1 -1
- package/lib/conversation/content/TweetContent.d.ts +1 -1
- package/lib/conversation/content/TweetContent.d.ts.map +1 -1
- package/lib/conversation/content/index.d.ts +1 -1
- package/lib/conversation/content/index.d.ts.map +1 -1
- package/lib/conversation/content/index.js +1 -1
- package/lib/conversation/message/MessageBuilder.d.ts +1 -1
- package/lib/conversation/message/MessageBuilder.d.ts.map +1 -1
- package/lib/conversation/message/MessageBuilder.js +1 -1
- package/lib/conversation/message/MessageService.d.ts.map +1 -1
- package/lib/conversation/message/MessageService.js +1 -1
- package/lib/conversation/message/MessageService.test.js +7 -1
- package/lib/conversation/message/MessageToProtoMapper.d.ts +1 -1
- package/lib/conversation/message/MessageToProtoMapper.d.ts.map +1 -1
- package/lib/conversation/message/MessageToProtoMapper.js +1 -1
- package/lib/conversation/message/messageSender.js +2 -2
- package/lib/cryptography/AssetCryptography/EncryptedAsset.d.ts +2 -2
- package/lib/cryptography/AssetCryptography/EncryptedAsset.d.ts.map +1 -1
- package/lib/messagingProtocols/common.types.d.ts +9 -0
- package/lib/messagingProtocols/common.types.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts +2 -2
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.js +2 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.js +13 -11
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.test.js +21 -16
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts +9 -3
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.js +31 -12
- package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.d.ts +6 -0
- package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.js +19 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.d.ts +7 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.js +48 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/index.d.ts +2 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/{IncomingMessagesQueue → IncomingProposalsQueue}/index.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/{IncomingMessagesQueue → IncomingProposalsQueue}/index.js +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.d.ts +0 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.js +0 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.js +23 -14
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.js +5 -2
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.test.js +13 -3
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.d.ts +38 -2
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.js +41 -6
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.d.ts +2 -0
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.js +124 -0
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts +38 -34
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/MLSService/MLSService.js +267 -208
- package/lib/messagingProtocols/mls/MLSService/MLSService.test.js +157 -160
- package/lib/messagingProtocols/mls/MLSService/commitBundleUtil.js +3 -3
- package/lib/messagingProtocols/mls/MLSService/commitBundleUtil.test.js +5 -5
- package/lib/messagingProtocols/mls/conversationRejoinQueue.js +2 -2
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.d.ts +78 -0
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.js +173 -0
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.d.ts +2 -0
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.js +117 -0
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.d.ts +167 -0
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.js +317 -0
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.d.ts +2 -0
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.js +248 -0
- package/lib/messagingProtocols/mls/recovery/index.d.ts +5 -0
- package/lib/messagingProtocols/mls/recovery/index.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/recovery/index.js +28 -0
- package/lib/messagingProtocols/mls/types.d.ts +0 -8
- package/lib/messagingProtocols/mls/types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/EventHandler/events/otrMessageAdd/otrMessageAdd.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/EventHandler/events/otrMessageAdd/otrMessageAdd.js +7 -1
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts +8 -15
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.js +97 -62
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts +0 -6
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.d.ts +1 -6
- package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.js +19 -22
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts +5 -3
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.js +11 -24
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts +1 -0
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.js +11 -2
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.test.js +13 -9
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.types.d.ts +3 -2
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/WithMockedGenerics.test.js +11 -4
- package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.d.ts +0 -4
- package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.js +0 -5
- package/lib/messagingProtocols/proteus/ProteusService/identityClearer.d.ts +2 -1
- package/lib/messagingProtocols/proteus/ProteusService/identityClearer.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/identityClearer.js +8 -2
- package/lib/messagingProtocols/proteus/Utility/SessionHandler/SessionHandler.test.js +4 -0
- package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.d.ts +1 -1
- package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.js +1 -1
- package/lib/notification/NotificationService.d.ts +20 -6
- package/lib/notification/NotificationService.d.ts.map +1 -1
- package/lib/notification/NotificationService.js +23 -14
- package/lib/notification/NotificationService.test.js +8 -0
- package/lib/secretStore/secretKeyGenerator.d.ts +1 -0
- package/lib/secretStore/secretKeyGenerator.d.ts.map +1 -1
- package/lib/secretStore/secretKeyGenerator.js +3 -1
- package/lib/self/SelfService.d.ts +2 -2
- package/lib/self/SelfService.d.ts.map +1 -1
- package/lib/self/SelfService.test.js +5 -2
- package/lib/team/TeamService.d.ts +5 -2
- package/lib/team/TeamService.d.ts.map +1 -1
- package/lib/team/TeamService.js +12 -2
- package/lib/test/StoreHelper.d.ts +2 -0
- package/lib/test/StoreHelper.d.ts.map +1 -0
- package/lib/test/StoreHelper.js +27 -0
- package/lib/user/UserService.d.ts +2 -2
- package/lib/user/UserService.d.ts.map +1 -1
- package/lib/user/UserService.js +3 -3
- package/lib/util/TypePredicateUtil.d.ts.map +1 -1
- package/lib/util/TypePredicateUtil.js +2 -2
- package/package.json +3 -3
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.d.ts +0 -4
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.js +0 -69
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/index.d.ts +0 -2
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.d.ts +0 -2
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.js +0 -98
package/lib/Account.js
CHANGED
|
@@ -41,16 +41,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
41
41
|
return result;
|
|
42
42
|
};
|
|
43
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
-
exports.Account = exports.
|
|
44
|
+
exports.Account = exports.AccountLocalStorageStore = exports.EVENTS = exports.ConnectionState = void 0;
|
|
45
45
|
const auth_1 = require("@wireapp/api-client/lib/auth");
|
|
46
46
|
const client_1 = require("@wireapp/api-client/lib/client/");
|
|
47
47
|
const event_1 = require("@wireapp/api-client/lib/event");
|
|
48
|
+
const ConsumableNotification_1 = require("@wireapp/api-client/lib/notification/ConsumableNotification");
|
|
48
49
|
const tcp_1 = require("@wireapp/api-client/lib/tcp/");
|
|
49
50
|
const ReconnectingWebsocket_1 = require("@wireapp/api-client/lib/tcp/ReconnectingWebsocket");
|
|
50
51
|
const team_1 = require("@wireapp/api-client/lib/team");
|
|
51
52
|
const TimeUtil_1 = require("@wireapp/commons/lib/util/TimeUtil");
|
|
52
53
|
const api_client_1 = require("@wireapp/api-client");
|
|
53
54
|
const commons_1 = require("@wireapp/commons");
|
|
55
|
+
const promise_queue_1 = require("@wireapp/promise-queue");
|
|
54
56
|
const store_engine_1 = require("@wireapp/store-engine");
|
|
55
57
|
const account_1 = require("./account/");
|
|
56
58
|
const auth_2 = require("./auth/");
|
|
@@ -65,8 +67,11 @@ const linkPreview_1 = require("./linkPreview");
|
|
|
65
67
|
const mls_1 = require("./messagingProtocols/mls");
|
|
66
68
|
const conversationRejoinQueue_1 = require("./messagingProtocols/mls/conversationRejoinQueue");
|
|
67
69
|
const E2EIdentityService_1 = require("./messagingProtocols/mls/E2EIdentityService");
|
|
70
|
+
const IncomingProposalsQueue_1 = require("./messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue");
|
|
68
71
|
const proteus_1 = require("./messagingProtocols/proteus");
|
|
69
72
|
const CryptoClient_1 = require("./messagingProtocols/proteus/ProteusService/CryptoClient");
|
|
73
|
+
const CoreCryptoWrapper_1 = require("./messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper");
|
|
74
|
+
const identityClearer_1 = require("./messagingProtocols/proteus/ProteusService/identityClearer");
|
|
70
75
|
const notification_1 = require("./notification/");
|
|
71
76
|
const encryptedStore_1 = require("./secretStore/encryptedStore");
|
|
72
77
|
const secretKeyGenerator_1 = require("./secretStore/secretKeyGenerator");
|
|
@@ -74,24 +79,17 @@ const self_1 = require("./self/");
|
|
|
74
79
|
const CoreDB_1 = require("./storage/CoreDB");
|
|
75
80
|
const team_2 = require("./team/");
|
|
76
81
|
const user_1 = require("./user/");
|
|
82
|
+
const LocalStorageStore_1 = require("./util/LocalStorageStore");
|
|
77
83
|
const RecurringTaskScheduler_1 = require("./util/RecurringTaskScheduler");
|
|
78
|
-
var EVENTS;
|
|
79
|
-
(function (EVENTS) {
|
|
80
|
-
/**
|
|
81
|
-
* event triggered when a message from an unknown client is received.
|
|
82
|
-
* An unknown client is a client we don't yet have a session with
|
|
83
|
-
*/
|
|
84
|
-
EVENTS["NEW_SESSION"] = "new_session";
|
|
85
|
-
})(EVENTS || (exports.EVENTS = EVENTS = {}));
|
|
86
84
|
var ConnectionState;
|
|
87
85
|
(function (ConnectionState) {
|
|
88
|
-
/** The
|
|
86
|
+
/** The WebSocket is closed and no notifications are being processed */
|
|
89
87
|
ConnectionState["CLOSED"] = "closed";
|
|
90
|
-
/** The
|
|
88
|
+
/** The WebSocket is being opened or reconnected */
|
|
91
89
|
ConnectionState["CONNECTING"] = "connecting";
|
|
92
90
|
/** The websocket is open but locked and notifications stream is being processed */
|
|
93
91
|
ConnectionState["PROCESSING_NOTIFICATIONS"] = "processing_notifications";
|
|
94
|
-
/** The
|
|
92
|
+
/** The WebSocket is open and new messages are processed live in real time */
|
|
95
93
|
ConnectionState["LIVE"] = "live";
|
|
96
94
|
})(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
|
|
97
95
|
const coreDefaultClient = {
|
|
@@ -99,17 +97,31 @@ const coreDefaultClient = {
|
|
|
99
97
|
cookieLabel: 'default',
|
|
100
98
|
model: '@wireapp/core',
|
|
101
99
|
};
|
|
100
|
+
var EVENTS;
|
|
101
|
+
(function (EVENTS) {
|
|
102
|
+
/**
|
|
103
|
+
* event triggered when a message from an unknown client is received.
|
|
104
|
+
* An unknown client is a client we don't yet have a session with
|
|
105
|
+
*/
|
|
106
|
+
EVENTS["NEW_SESSION"] = "new_session";
|
|
107
|
+
})(EVENTS || (exports.EVENTS = EVENTS = {}));
|
|
108
|
+
exports.AccountLocalStorageStore = (0, LocalStorageStore_1.LocalStorageStore)('core_account');
|
|
102
109
|
class Account extends commons_1.TypedEventEmitter {
|
|
103
110
|
options;
|
|
104
111
|
apiClient;
|
|
105
112
|
logger;
|
|
106
|
-
coreCryptoConfig;
|
|
107
113
|
/** this is the client the consumer is currently using. Will be set as soon as `initClient` is called and will be rest upon logout */
|
|
108
114
|
currentClient;
|
|
109
115
|
storeEngine;
|
|
110
116
|
db;
|
|
111
117
|
encryptedDb;
|
|
112
118
|
coreCallbacks;
|
|
119
|
+
connectionState = ConnectionState.CLOSED;
|
|
120
|
+
notificationProcessingQueue = new promise_queue_1.PromiseQueue({
|
|
121
|
+
name: 'notification-processing-queue',
|
|
122
|
+
paused: true,
|
|
123
|
+
});
|
|
124
|
+
setMaxCoreCryptoLogLevel = () => undefined;
|
|
113
125
|
service;
|
|
114
126
|
backendFeatures;
|
|
115
127
|
recurringTaskScheduler;
|
|
@@ -117,12 +129,14 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
117
129
|
* @param apiClient The apiClient instance to use in the core (will create a new new one if undefined)
|
|
118
130
|
* @param accountOptions
|
|
119
131
|
*/
|
|
120
|
-
constructor(apiClient = new api_client_1.APIClient(), options = {
|
|
132
|
+
constructor(apiClient = new api_client_1.APIClient(), options = {
|
|
133
|
+
nbPrekeys: 100,
|
|
134
|
+
coreCryptoConfig: { wasmFilePath: '', enabled: false },
|
|
135
|
+
}) {
|
|
121
136
|
super();
|
|
122
137
|
this.options = options;
|
|
123
138
|
this.apiClient = apiClient;
|
|
124
139
|
this.backendFeatures = this.apiClient.backendFeatures;
|
|
125
|
-
this.coreCryptoConfig = options.coreCryptoConfig;
|
|
126
140
|
this.recurringTaskScheduler = new RecurringTaskScheduler_1.RecurringTaskScheduler({
|
|
127
141
|
get: async (key) => {
|
|
128
142
|
const task = await this.db?.get('recurringTasks', key);
|
|
@@ -141,7 +155,7 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
141
155
|
await this.persistCookie(this.storeEngine, cookie);
|
|
142
156
|
}
|
|
143
157
|
catch (error) {
|
|
144
|
-
this.logger.error(
|
|
158
|
+
this.logger.error('Failed to save cookie:', error);
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
161
|
});
|
|
@@ -159,16 +173,16 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
159
173
|
* - useVersion(0, 1, true) > version 1 is used
|
|
160
174
|
* @return The highest version that is both supported by client and backend
|
|
161
175
|
*/
|
|
162
|
-
async
|
|
176
|
+
useAPIVersion = async (min, max, allowDev) => {
|
|
163
177
|
const features = await this.apiClient.useVersion(min, max, allowDev);
|
|
164
178
|
this.backendFeatures = features;
|
|
165
179
|
return features;
|
|
166
|
-
}
|
|
167
|
-
persistCookie(storeEngine, cookie) {
|
|
180
|
+
};
|
|
181
|
+
persistCookie = (storeEngine, cookie) => {
|
|
168
182
|
const entity = { expiration: cookie.expiration, zuid: cookie.zuid };
|
|
169
183
|
return storeEngine.updateOrCreate(auth_1.AUTH_TABLE_NAME, auth_1.AUTH_COOKIE_KEY, entity);
|
|
170
|
-
}
|
|
171
|
-
async
|
|
184
|
+
};
|
|
185
|
+
enrollE2EI = async ({ displayName, handle, teamId, discoveryUrl, getOAuthToken, getAllConversations, certificateTtl = 90 * (TimeUtil_1.TimeInMillis.DAY / 1000), }) => {
|
|
172
186
|
const context = this.apiClient.context;
|
|
173
187
|
const domain = context?.domain ?? '';
|
|
174
188
|
if (!this.currentClient) {
|
|
@@ -184,8 +198,8 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
184
198
|
teamId,
|
|
185
199
|
id: this.userId,
|
|
186
200
|
};
|
|
187
|
-
return this.service.mls.enrollE2EI(discoveryUrl, user, this.currentClient, this.options.nbPrekeys, certificateTtl, getOAuthToken);
|
|
188
|
-
}
|
|
201
|
+
return this.service.mls.enrollE2EI(discoveryUrl, user, this.currentClient, this.options.nbPrekeys, certificateTtl, getOAuthToken, getAllConversations);
|
|
202
|
+
};
|
|
189
203
|
get clientId() {
|
|
190
204
|
return this.apiClient.validatedClientId;
|
|
191
205
|
}
|
|
@@ -198,52 +212,57 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
198
212
|
* @param registration The user's data
|
|
199
213
|
* @param clientType Type of client to create (temporary or permanent)
|
|
200
214
|
*/
|
|
201
|
-
async
|
|
215
|
+
register = async (registration, clientType) => {
|
|
202
216
|
const context = await this.apiClient.register(registration, clientType);
|
|
203
217
|
await this.initServices(context);
|
|
204
218
|
return context;
|
|
205
|
-
}
|
|
219
|
+
};
|
|
206
220
|
/**
|
|
207
221
|
* Will init the core with an already logged in user
|
|
208
222
|
*
|
|
209
223
|
* @param clientType The type of client the user is using (temporary or permanent)
|
|
210
224
|
*/
|
|
211
|
-
async
|
|
225
|
+
init = async (clientType, { cookie } = {}) => {
|
|
212
226
|
const context = await this.apiClient.init(clientType, cookie);
|
|
213
227
|
await this.initServices(context);
|
|
214
228
|
return context;
|
|
215
|
-
}
|
|
229
|
+
};
|
|
216
230
|
/**
|
|
217
231
|
* Will log the user in with the given credential.
|
|
218
232
|
*
|
|
219
233
|
* @param loginData The credentials of the user
|
|
220
234
|
* @param clientInfo Info about the client to create (name, type...)
|
|
221
235
|
*/
|
|
222
|
-
async
|
|
236
|
+
login = async (loginData) => {
|
|
223
237
|
this.resetContext();
|
|
224
238
|
auth_2.LoginSanitizer.removeNonPrintableCharacters(loginData);
|
|
225
239
|
const context = await this.apiClient.login(loginData);
|
|
226
240
|
await this.initServices(context);
|
|
227
241
|
return context;
|
|
228
|
-
}
|
|
242
|
+
};
|
|
229
243
|
/**
|
|
230
244
|
* Will register a new client for the current user
|
|
231
245
|
*/
|
|
232
|
-
async
|
|
246
|
+
registerClient = async (loginData, useLegacyNotificationStream,
|
|
233
247
|
/** will add extra manual entropy to the client's identity being created */
|
|
234
|
-
entropyData) {
|
|
248
|
+
entropyData, clientInfo = coreDefaultClient) => {
|
|
235
249
|
if (!this.service || !this.apiClient.context || !this.storeEngine) {
|
|
236
250
|
throw new Error('Services are not set or context not initialized.');
|
|
237
251
|
}
|
|
252
|
+
if (typeof useLegacyNotificationStream !== 'boolean') {
|
|
253
|
+
throw new Error('use of legacy notifications must be explicitly set to true or false');
|
|
254
|
+
}
|
|
238
255
|
// we reset the services to re-instantiate a new CryptoClient instance
|
|
239
256
|
await this.initServices(this.apiClient.context);
|
|
240
257
|
const initialPreKeys = await this.service.proteus.createClient(entropyData);
|
|
241
|
-
const client = await this.service.client.register(loginData, clientInfo, initialPreKeys);
|
|
258
|
+
const client = await this.service.client.register(loginData, clientInfo, initialPreKeys, useLegacyNotificationStream);
|
|
242
259
|
const clientId = client.id;
|
|
243
|
-
|
|
260
|
+
if (useLegacyNotificationStream) {
|
|
261
|
+
await this.service.notification.legacyInitializeNotificationStream(clientId);
|
|
262
|
+
}
|
|
244
263
|
await this.service.client.synchronizeClients(clientId);
|
|
245
264
|
return client;
|
|
246
|
-
}
|
|
265
|
+
};
|
|
247
266
|
getLocalClient() {
|
|
248
267
|
return this.service?.client.loadClient();
|
|
249
268
|
}
|
|
@@ -252,14 +271,14 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
252
271
|
*
|
|
253
272
|
* @returns The local existing client or undefined if the client does not exist or is not valid (non existing on backend)
|
|
254
273
|
*/
|
|
255
|
-
async
|
|
274
|
+
initClient = async (client, mlsConfig) => {
|
|
256
275
|
if (!this.service || !this.apiClient.context || !this.storeEngine) {
|
|
257
276
|
throw new Error('Services are not set.');
|
|
258
277
|
}
|
|
259
278
|
this.apiClient.context.clientId = client.id;
|
|
260
279
|
// Call /access endpoint with client_id after client initialisation
|
|
261
280
|
await this.apiClient.transport.http.associateClientWithSession(client.id);
|
|
262
|
-
await this.service.proteus.initClient(this.
|
|
281
|
+
await this.service.proteus.initClient(this.apiClient.context);
|
|
263
282
|
if ((await this.isMLSActiveForClient()) && this.service.mls && mlsConfig) {
|
|
264
283
|
const { userId, domain = '' } = this.apiClient.context;
|
|
265
284
|
await this.service.mls.initClient({ id: userId, domain }, client, mlsConfig);
|
|
@@ -272,8 +291,8 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
272
291
|
}
|
|
273
292
|
this.currentClient = client;
|
|
274
293
|
return client;
|
|
275
|
-
}
|
|
276
|
-
async
|
|
294
|
+
};
|
|
295
|
+
buildCryptoClient = async (context, storeEngine, encryptedStore) => {
|
|
277
296
|
const baseConfig = {
|
|
278
297
|
nbPrekeys: this.options.nbPrekeys,
|
|
279
298
|
onNewPrekeys: async (prekeys) => {
|
|
@@ -282,20 +301,19 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
282
301
|
this.logger.debug(`Successfully uploaded '${prekeys.length}' PreKeys.`);
|
|
283
302
|
},
|
|
284
303
|
};
|
|
285
|
-
|
|
286
|
-
if (coreCryptoConfig) {
|
|
304
|
+
if (this.options.coreCryptoConfig?.enabled) {
|
|
287
305
|
const { buildClient } = await Promise.resolve().then(() => __importStar(require('./messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper')));
|
|
288
306
|
const client = await buildClient(storeEngine, {
|
|
289
307
|
...baseConfig,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
308
|
+
generateSecretKey: (keyId, keySize) => (0, secretKeyGenerator_1.generateSecretKey)({ keyId, keySize, secretsDb: encryptedStore }),
|
|
309
|
+
}, this.options.coreCryptoConfig);
|
|
310
|
+
this.setMaxCoreCryptoLogLevel = client.setMaxLogLevel;
|
|
293
311
|
return [CryptoClient_1.CryptoClientType.CORE_CRYPTO, client];
|
|
294
312
|
}
|
|
295
313
|
const { buildClient } = await Promise.resolve().then(() => __importStar(require('./messagingProtocols/proteus/ProteusService/CryptoClient/CryptoboxWrapper')));
|
|
296
314
|
const client = buildClient(storeEngine, baseConfig);
|
|
297
315
|
return [CryptoClient_1.CryptoClientType.CRYPTOBOX, client];
|
|
298
|
-
}
|
|
316
|
+
};
|
|
299
317
|
/**
|
|
300
318
|
* In order to be able to send MLS messages, the core needs a few information from the consumer.
|
|
301
319
|
* Namely:
|
|
@@ -303,10 +321,10 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
303
321
|
* - what is the groupId of a conversation
|
|
304
322
|
* @param coreCallbacks
|
|
305
323
|
*/
|
|
306
|
-
configureCoreCallbacks(coreCallbacks) {
|
|
324
|
+
configureCoreCallbacks = (coreCallbacks) => {
|
|
307
325
|
this.coreCallbacks = coreCallbacks;
|
|
308
|
-
}
|
|
309
|
-
async
|
|
326
|
+
};
|
|
327
|
+
initServices = async (context) => {
|
|
310
328
|
const encryptedStoreName = this.generateEncryptedDbName(context);
|
|
311
329
|
this.encryptedDb = this.options.systemCrypto
|
|
312
330
|
? await (0, encryptedStore_1.createCustomEncryptedStore)(encryptedStoreName, this.options.systemCrypto)
|
|
@@ -322,7 +340,7 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
322
340
|
const proteusService = new proteus_1.ProteusService(this.apiClient, cryptoClient, {
|
|
323
341
|
onNewClient: payload => this.emit(EVENTS.NEW_SESSION, payload),
|
|
324
342
|
nbPrekeys: this.options.nbPrekeys,
|
|
325
|
-
});
|
|
343
|
+
}, this.storeEngine);
|
|
326
344
|
const clientService = new client_2.ClientService(this.apiClient, proteusService, this.storeEngine);
|
|
327
345
|
if (clientType === CryptoClient_1.CryptoClientType.CORE_CRYPTO && (await this.apiClient.supportsMLS())) {
|
|
328
346
|
mlsService = new mls_1.MLSService(this.apiClient, cryptoClient.getNativeClient(), this.db, this.recurringTaskScheduler);
|
|
@@ -332,7 +350,7 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
332
350
|
const giphyService = new giphy_1.GiphyService(this.apiClient);
|
|
333
351
|
const linkPreviewService = new linkPreview_1.LinkPreviewService(assetService);
|
|
334
352
|
const subconversationService = new SubconversationService_1.SubconversationService(this.apiClient, this.db, mlsService);
|
|
335
|
-
const conversationService = new conversation_1.ConversationService(this.apiClient, proteusService, this.db, this.groupIdFromConversationId, subconversationService, mlsService);
|
|
353
|
+
const conversationService = new conversation_1.ConversationService(this.apiClient, proteusService, this.db, this.groupIdFromConversationId, subconversationService, this.isMLSConversationRecoveryEnabled, mlsService);
|
|
336
354
|
const notificationService = new notification_1.NotificationService(this.apiClient, this.storeEngine, conversationService);
|
|
337
355
|
const selfService = new self_1.SelfService(this.apiClient);
|
|
338
356
|
const teamService = new team_2.TeamService(this.apiClient);
|
|
@@ -356,35 +374,88 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
356
374
|
team: teamService,
|
|
357
375
|
user: userService,
|
|
358
376
|
};
|
|
359
|
-
}
|
|
360
|
-
resetContext() {
|
|
377
|
+
};
|
|
378
|
+
resetContext = () => {
|
|
361
379
|
this.currentClient = undefined;
|
|
362
380
|
delete this.apiClient.context;
|
|
363
381
|
delete this.service;
|
|
364
|
-
}
|
|
382
|
+
};
|
|
365
383
|
/**
|
|
366
384
|
* Will logout the current user
|
|
367
385
|
* @param clearData if set to `true` will completely wipe any database that was created by the Account
|
|
368
386
|
*/
|
|
369
|
-
|
|
387
|
+
logout = async (data) => {
|
|
370
388
|
this.db?.close();
|
|
371
389
|
this.encryptedDb?.close();
|
|
372
|
-
if (
|
|
373
|
-
await this.
|
|
390
|
+
if (data?.clearAllData) {
|
|
391
|
+
await this.wipeAllData();
|
|
392
|
+
}
|
|
393
|
+
else if (data?.clearCryptoData) {
|
|
394
|
+
await this.wipeCryptoData();
|
|
374
395
|
}
|
|
375
396
|
await this.apiClient.logout();
|
|
376
397
|
this.resetContext();
|
|
377
|
-
}
|
|
398
|
+
};
|
|
399
|
+
wipeCommonData = async () => {
|
|
400
|
+
try {
|
|
401
|
+
await this.service?.client.deleteLocalClient();
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
this.logger.error('Failed to delete local client during logout cleanup:', error);
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
if (this.storeEngine) {
|
|
408
|
+
await (0, CoreCryptoWrapper_1.wipeCoreCryptoDb)(this.storeEngine);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
this.logger.error('Failed to wipe crypto database during logout cleanup:', error);
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
// needs to be wiped last
|
|
416
|
+
await this.encryptedDb?.wipe();
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
this.logger.error('Failed to delete encrypted database during logout cleanup:', error);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
378
422
|
/**
|
|
379
|
-
* Will delete the identity of the current user
|
|
423
|
+
* Will delete the identity and history of the current user
|
|
380
424
|
*/
|
|
381
|
-
async
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
425
|
+
wipeAllData = async () => {
|
|
426
|
+
try {
|
|
427
|
+
if (this.storeEngine) {
|
|
428
|
+
await (0, identityClearer_1.deleteIdentity)(this.storeEngine, false);
|
|
429
|
+
}
|
|
385
430
|
}
|
|
386
|
-
|
|
387
|
-
|
|
431
|
+
catch (error) {
|
|
432
|
+
this.logger.error('Failed to delete identity during logout cleanup:', error);
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
if (this.db) {
|
|
436
|
+
await (0, CoreDB_1.deleteDB)(this.db);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
this.logger.error('Failed to delete database during logout cleanup:', error);
|
|
441
|
+
}
|
|
442
|
+
await this.wipeCommonData();
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* Will delete the cryptography and client of the current user
|
|
446
|
+
* Will keep the history intact
|
|
447
|
+
*/
|
|
448
|
+
wipeCryptoData = async () => {
|
|
449
|
+
try {
|
|
450
|
+
if (this.storeEngine) {
|
|
451
|
+
await (0, identityClearer_1.deleteIdentity)(this.storeEngine, true);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
this.logger.error('Failed to delete identity during logout cleanup:', error);
|
|
456
|
+
}
|
|
457
|
+
await this.wipeCommonData();
|
|
458
|
+
};
|
|
388
459
|
/**
|
|
389
460
|
* return true if the current user has a MLS device that is initialized and ready to use
|
|
390
461
|
*/
|
|
@@ -398,11 +469,105 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
398
469
|
* @param callbacks callbacks that will be called to handle different events
|
|
399
470
|
* @returns close a function that will disconnect from the websocket
|
|
400
471
|
*/
|
|
401
|
-
listen({ onEvent = () => { }, onConnectionStateChanged = () => { }, onNotificationStreamProgress = () => { }, onMissedNotifications = () => { },
|
|
472
|
+
listen = async ({ useLegacy, onEvent = async () => { }, onConnectionStateChanged: onConnectionStateChangedCallBack = () => { }, onNotificationStreamProgress = () => { }, onMissedNotifications = () => { }, } = {}) => {
|
|
402
473
|
if (!this.currentClient) {
|
|
403
474
|
throw new Error('Client has not been initialized - please login first');
|
|
404
475
|
}
|
|
405
|
-
|
|
476
|
+
if (typeof useLegacy !== 'boolean') {
|
|
477
|
+
throw new Error('use of legacy notifications must be explicitly set to true or false');
|
|
478
|
+
}
|
|
479
|
+
const onConnectionStateChanged = this.createConnectionStateChangedHandler(onConnectionStateChangedCallBack);
|
|
480
|
+
const handleEvent = this.createEventHandler(onEvent);
|
|
481
|
+
const handleLegacyNotification = this.createLegacyNotificationHandler(handleEvent, onNotificationStreamProgress);
|
|
482
|
+
const handleNotification = this.createNotificationHandler(handleEvent, onNotificationStreamProgress, onConnectionStateChanged);
|
|
483
|
+
const handleMissedNotifications = this.createLegacyMissedNotificationsHandler(onMissedNotifications);
|
|
484
|
+
const legacyProcessNotificationStream = this.createLegacyNotificationStreamProcessor({
|
|
485
|
+
handleLegacyNotification,
|
|
486
|
+
handleMissedNotifications,
|
|
487
|
+
onConnectionStateChanged,
|
|
488
|
+
});
|
|
489
|
+
this.setupWebSocketListeners(onConnectionStateChanged, handleNotification, handleLegacyNotification, useLegacy);
|
|
490
|
+
const isClientCapableOfConsumableNotifications = this.getClientCapabilities().includes(client_1.ClientCapability.CONSUMABLE_NOTIFICATIONS);
|
|
491
|
+
const capabilities = [client_1.ClientCapability.LEGAL_HOLD_IMPLICIT_CONSENT];
|
|
492
|
+
if (!useLegacy) {
|
|
493
|
+
// let the backend now client is capable of consumable notifications
|
|
494
|
+
capabilities.push(client_1.ClientCapability.CONSUMABLE_NOTIFICATIONS);
|
|
495
|
+
this.apiClient.transport.ws.useAsyncNotificationsSocket();
|
|
496
|
+
}
|
|
497
|
+
this.logger.info(`Client is using the ${useLegacy ? 'legacy' : 'async'} notification stream`);
|
|
498
|
+
await this.service?.client.putClientCapabilities(this.currentClient.id, { capabilities });
|
|
499
|
+
/*
|
|
500
|
+
* When enabling async notifications, be aware that the backend maintains a separate queue
|
|
501
|
+
* for new async notifications (/events weboscket endpoint), which only starts populating *after* the client declares support
|
|
502
|
+
* for async notifications.
|
|
503
|
+
*
|
|
504
|
+
* Therefore, after declaring support, it's necessary to perform one final fetch from the legacy
|
|
505
|
+
* system to ensure no notifications are missed—since older notifications won't
|
|
506
|
+
* appear in the new queue.
|
|
507
|
+
*
|
|
508
|
+
* These two systems are separate, and the transition timing
|
|
509
|
+
* is important to avoid missing any messages during the switch.
|
|
510
|
+
*
|
|
511
|
+
* @todo This can be removed when all clients are capable of consumable notifications.
|
|
512
|
+
*/
|
|
513
|
+
if (!isClientCapableOfConsumableNotifications && !useLegacy) {
|
|
514
|
+
// do the last legacy sync without connecting to any websockets
|
|
515
|
+
await legacyProcessNotificationStream();
|
|
516
|
+
this.logger.info('Completed final legacy notification stream processing after enabling async notifications');
|
|
517
|
+
}
|
|
518
|
+
if (useLegacy) {
|
|
519
|
+
/**
|
|
520
|
+
* immediately lock the websocket to prevent any new messages from being received
|
|
521
|
+
* before legacy notifications endpoint is fetched otherwise it'll update the last notification ID
|
|
522
|
+
* and fetching legacy notifications will return an empty list
|
|
523
|
+
*/
|
|
524
|
+
this.apiClient.transport.ws.lock();
|
|
525
|
+
}
|
|
526
|
+
this.apiClient.connect(async (abortController) => {
|
|
527
|
+
// this call back is called every single time the websocket connection is (re)established
|
|
528
|
+
this.logger.info('Connection established with websocket, starting notification stream processing');
|
|
529
|
+
/**
|
|
530
|
+
* This is to avoid passing proposals too early to core crypto
|
|
531
|
+
* @See WPB-18995
|
|
532
|
+
*/
|
|
533
|
+
(0, IncomingProposalsQueue_1.pauseProposalProcessing)();
|
|
534
|
+
(0, messageSender_1.pauseMessageSending)(); // pause message sending while processing notifications, it will be resumed once the processing is done and we have the marker token
|
|
535
|
+
(0, conversationRejoinQueue_1.pauseRejoiningMLSConversations)(); // We want to avoid triggering rejoins of out-of-sync MLS conversations while we are processing the notification stream
|
|
536
|
+
/**
|
|
537
|
+
* resume the notification processing queue
|
|
538
|
+
* it will start processing notifications immediately and pause if web socket connection drops
|
|
539
|
+
* we should start decryption and therefore acknowledging the notifications in order for the backend to
|
|
540
|
+
* send us the next batch of notifications, currently total size of notifications coming from web socket is limited to 500
|
|
541
|
+
* so we need to acknowledge the notifications to let the backend know we are ready for the next batch
|
|
542
|
+
*/
|
|
543
|
+
this.notificationProcessingQueue.resume();
|
|
544
|
+
if (useLegacy) {
|
|
545
|
+
await legacyProcessNotificationStream(abortController);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
return () => {
|
|
549
|
+
this.logger.info('Disconnecting from backend as requested by consumer');
|
|
550
|
+
(0, IncomingProposalsQueue_1.flushProposalsQueue)();
|
|
551
|
+
this.pauseAndFlushNotificationQueue();
|
|
552
|
+
this.apiClient.disconnect();
|
|
553
|
+
onConnectionStateChanged(ConnectionState.CLOSED);
|
|
554
|
+
this.apiClient.transport.ws.removeAllListeners();
|
|
555
|
+
};
|
|
556
|
+
};
|
|
557
|
+
createConnectionStateChangedHandler = (onConnectionStateChanged) => {
|
|
558
|
+
return (state) => {
|
|
559
|
+
this.connectionState = state;
|
|
560
|
+
onConnectionStateChanged(state);
|
|
561
|
+
this.logger.info(`Connection state changed to: ${state}`);
|
|
562
|
+
};
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* Creates the event handler that is invoked for each decrypted event from the backend.
|
|
566
|
+
* Responsible for handling specific event types like `MESSAGE_TIMER_UPDATE`, and then
|
|
567
|
+
* forwarding the event to the consumer via the `onEvent` callback.
|
|
568
|
+
*/
|
|
569
|
+
createEventHandler = (onEvent) => {
|
|
570
|
+
return async (payload, source) => {
|
|
406
571
|
const { event } = payload;
|
|
407
572
|
switch (event?.type) {
|
|
408
573
|
case event_1.CONVERSATION_EVENT.MESSAGE_TIMER_UPDATE: {
|
|
@@ -412,80 +577,290 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
412
577
|
break;
|
|
413
578
|
}
|
|
414
579
|
}
|
|
580
|
+
// Always forward the event to the consumer
|
|
415
581
|
await onEvent(payload, source);
|
|
416
582
|
};
|
|
417
|
-
|
|
583
|
+
};
|
|
584
|
+
/**
|
|
585
|
+
* @deprecated This method is used to handle legacy notifications from the backend.
|
|
586
|
+
* It processes notifications from the legacy system, decrypts them, and emits events.
|
|
587
|
+
* It can be replaced with the new notification handling system using `ConsumableNotification`
|
|
588
|
+
* when all clients are capable of handling consumable notifications.
|
|
589
|
+
*/
|
|
590
|
+
createLegacyNotificationHandler = (handleEvent, onNotificationStreamProgress) => {
|
|
591
|
+
return async (notification, source) => {
|
|
592
|
+
void this.notificationProcessingQueue
|
|
593
|
+
.push(async () => {
|
|
594
|
+
try {
|
|
595
|
+
const start = Date.now();
|
|
596
|
+
const notificationTime = this.getNotificationEventTime(notification.payload[0]);
|
|
597
|
+
this.logger.info(`Processing legacy notification "${notification.id}" at ${notificationTime}`, {
|
|
598
|
+
notification,
|
|
599
|
+
});
|
|
600
|
+
this.logger.info(`Total notifications queue length: ${this.notificationProcessingQueue.getLength()}`);
|
|
601
|
+
this.logger.info(`Total pending proposals queue length: ${(0, IncomingProposalsQueue_1.getProposalQueueLength)()}`);
|
|
602
|
+
if (notificationTime) {
|
|
603
|
+
onNotificationStreamProgress(notificationTime);
|
|
604
|
+
}
|
|
605
|
+
const messages = this.service.notification.handleNotification(notification, source);
|
|
606
|
+
for await (const message of messages) {
|
|
607
|
+
await handleEvent(message, source);
|
|
608
|
+
}
|
|
609
|
+
this.logger.info(`Finished processing legacy notification "${notification.id}" in ${Date.now() - start}ms`);
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
this.logger.error(`Failed to handle legacy notification "${notification.id}": ${error.message}`, error);
|
|
613
|
+
}
|
|
614
|
+
})
|
|
615
|
+
.catch(this.handleNotificationQueueError);
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
createNotificationHandler = (handleEvent, onNotificationStreamProgress, onConnectionStateChanged) => {
|
|
619
|
+
return async (notification, source) => {
|
|
620
|
+
this.logger.info(`Received consumable notification of type "${notification.type}"`, { notification });
|
|
418
621
|
try {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
622
|
+
if (notification.type === ConsumableNotification_1.ConsumableEvent.MISSED) {
|
|
623
|
+
this.reactToMissedNotification();
|
|
624
|
+
return;
|
|
422
625
|
}
|
|
626
|
+
if (notification.type === ConsumableNotification_1.ConsumableEvent.SYNCHRONIZATION) {
|
|
627
|
+
this.notificationProcessingQueue
|
|
628
|
+
.push(() => this.handleSynchronizationNotification(notification, onConnectionStateChanged))
|
|
629
|
+
.catch(this.handleNotificationQueueError);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
this.notificationProcessingQueue
|
|
633
|
+
.push(() => this.decryptAckEmitNotification(notification, handleEvent, source, onNotificationStreamProgress))
|
|
634
|
+
.catch(this.handleNotificationQueueError);
|
|
423
635
|
}
|
|
424
636
|
catch (error) {
|
|
425
|
-
this.logger.error(`Failed to handle notification
|
|
637
|
+
this.logger.error(`Failed to handle notification "${notification.type}": ${error.message}`, error);
|
|
426
638
|
}
|
|
427
639
|
};
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
640
|
+
};
|
|
641
|
+
handleNotificationQueueError = (error) => {
|
|
642
|
+
if (!(error instanceof Error)) {
|
|
643
|
+
throw error;
|
|
644
|
+
}
|
|
645
|
+
switch (error.cause) {
|
|
646
|
+
case promise_queue_1.PromiseQueue.ERROR_CAUSES.TIMEOUT:
|
|
647
|
+
this.logger.warn('Notification decryption task timed out', error);
|
|
648
|
+
break;
|
|
649
|
+
case promise_queue_1.PromiseQueue.ERROR_CAUSES.FLUSHED:
|
|
650
|
+
this.logger.info('Notification processing queue was flushed, ignoring error', error);
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
acknowledgeSynchronizationNotification = (notification) => {
|
|
655
|
+
this.apiClient.transport.ws.acknowledgeConsumableNotificationSynchronization(notification);
|
|
656
|
+
};
|
|
657
|
+
handleSynchronizationNotification = async (notification, onConnectionStateChanged) => {
|
|
658
|
+
this.logger.info('acknowledging synchronization notification', { notification });
|
|
659
|
+
this.acknowledgeSynchronizationNotification(notification);
|
|
660
|
+
const markerId = notification.data.marker_id;
|
|
661
|
+
const currentMarkerId = this.apiClient.transport.http.accessTokenStore.markerToken;
|
|
662
|
+
this.logger.info(`Handling synchronization notification with marker ID: ${markerId} current marker ID: ${currentMarkerId}`);
|
|
663
|
+
/**
|
|
664
|
+
* There is a chance that there might be multiple synchronization notifications (markers)
|
|
665
|
+
* in the queue in case websocket connection drops a few times
|
|
666
|
+
* Hence we only want to resume message sending and set the connection state to LIVE
|
|
667
|
+
* if the marker ID matches the current marker ID.
|
|
668
|
+
*/
|
|
669
|
+
if (markerId === currentMarkerId) {
|
|
670
|
+
(0, IncomingProposalsQueue_1.resumeProposalProcessing)();
|
|
671
|
+
(0, messageSender_1.resumeMessageSending)();
|
|
672
|
+
(0, conversationRejoinQueue_1.resumeRejoiningMLSConversations)();
|
|
673
|
+
onConnectionStateChanged(ConnectionState.LIVE);
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
decryptAckEmitNotification = async (notification, handleEvent, source, onNotificationStreamProgress) => {
|
|
677
|
+
try {
|
|
678
|
+
this.logger.info(`Sending consumable notification for decryption`, notification.data.event.id);
|
|
679
|
+
const payloads = this.service.notification.handleNotification(notification.data.event, source);
|
|
680
|
+
const notificationTime = this.getNotificationEventTime(notification.data.event.payload[0]);
|
|
681
|
+
if (this.connectionState !== ConnectionState.LIVE && notificationTime) {
|
|
682
|
+
onNotificationStreamProgress(notificationTime);
|
|
438
683
|
}
|
|
439
|
-
|
|
440
|
-
|
|
684
|
+
for await (const payload of payloads ?? []) {
|
|
685
|
+
await handleEvent(payload, source);
|
|
686
|
+
}
|
|
687
|
+
this.logger.info(`Acknowledging consumable notification on the backend "${notification.data.delivery_tag}"`);
|
|
688
|
+
this.apiClient.transport.ws.acknowledgeNotification(notification);
|
|
689
|
+
}
|
|
690
|
+
catch (err) {
|
|
691
|
+
this.logger.error(`Failed to process notification ${notification.data.delivery_tag}`, err);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
getNotificationEventTime = (backendEvent) => {
|
|
695
|
+
if ('time' in backendEvent && typeof backendEvent.time === 'string') {
|
|
696
|
+
return backendEvent.time;
|
|
697
|
+
}
|
|
698
|
+
return null;
|
|
699
|
+
};
|
|
700
|
+
/**
|
|
701
|
+
* Returns a function to handle missed notifications — i.e., when the backend indicates
|
|
702
|
+
* that some notifications were lost due to age (typically >28 days).
|
|
703
|
+
* Also handles MLS-specific epoch mismatch recovery by triggering a conversation rejoin.
|
|
704
|
+
*
|
|
705
|
+
* @deprecated This is used to handle legacy missed notifications.
|
|
706
|
+
* It should be replaced with the new notification handling system using `ConsumableNotification`.
|
|
707
|
+
* when all clients are capable of handling consumable notifications.
|
|
708
|
+
*/
|
|
709
|
+
createLegacyMissedNotificationsHandler = (onMissedNotifications) => {
|
|
710
|
+
return async (notificationId) => {
|
|
441
711
|
if (this.hasMLSDevice) {
|
|
442
|
-
(0, conversationRejoinQueue_1.queueConversationRejoin)('all-conversations', () => this.service.conversation.handleConversationsEpochMismatch());
|
|
712
|
+
void (0, conversationRejoinQueue_1.queueConversationRejoin)('all-conversations', () => this.service.conversation.handleConversationsEpochMismatch());
|
|
443
713
|
}
|
|
444
714
|
return onMissedNotifications(notificationId);
|
|
445
715
|
};
|
|
446
|
-
|
|
447
|
-
|
|
716
|
+
};
|
|
717
|
+
/**
|
|
718
|
+
* Returns a processor function for the notification stream (legacy sync).
|
|
719
|
+
* It pauses message sending and MLS rejoining during stream handling to prevent race conditions,
|
|
720
|
+
* then resumes normal operations after sync is complete.
|
|
721
|
+
*
|
|
722
|
+
* @deprecated This is used to do a final sync of the legacy notification stream
|
|
723
|
+
* before switching to the new notification handling system using `ConsumableNotification`.
|
|
724
|
+
* It should be replaced with the new notification handling system when all clients are capable of handling consumable notifications.
|
|
725
|
+
*
|
|
726
|
+
* @param handlers Various logic handlers wired to notification callbacks
|
|
727
|
+
*/
|
|
728
|
+
createLegacyNotificationStreamProcessor = ({ handleLegacyNotification, handleMissedNotifications, onConnectionStateChanged, }) => {
|
|
729
|
+
return async (abortController) => {
|
|
448
730
|
this.apiClient.transport.ws.lock();
|
|
731
|
+
(0, IncomingProposalsQueue_1.pauseProposalProcessing)();
|
|
449
732
|
(0, messageSender_1.pauseMessageSending)();
|
|
450
|
-
// We want to avoid triggering rejoins of out-of-sync MLS conversations while we are processing the notification stream
|
|
451
733
|
(0, conversationRejoinQueue_1.pauseRejoiningMLSConversations)();
|
|
452
734
|
onConnectionStateChanged(ConnectionState.PROCESSING_NOTIFICATIONS);
|
|
453
|
-
const results = await this.service.notification.
|
|
454
|
-
await
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
this.logger.info('Finished processing notifications', results);
|
|
458
|
-
if (abortHandler.signal.aborted) {
|
|
459
|
-
this.logger.warn('Ending connection process as websocket was closed');
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
onConnectionStateChanged(ConnectionState.LIVE);
|
|
463
|
-
// We can now unlock the websocket and let the new messages being handled and decrypted
|
|
464
|
-
this.apiClient.transport.ws.unlock();
|
|
735
|
+
const results = await this.service.notification.legacyProcessNotificationStream(async (notification, source) => {
|
|
736
|
+
await handleLegacyNotification(notification, source);
|
|
737
|
+
}, handleMissedNotifications, abortController);
|
|
738
|
+
this.logger.info('Finished inserting notifications for decryption from the legacy endpoint to the process queue', results);
|
|
465
739
|
// We need to wait for the notification stream to be fully handled before releasing the message sending queue.
|
|
466
740
|
// This is due to the nature of how message are encrypted, any change in mls epoch needs to happen before we start encrypting any kind of messages
|
|
467
|
-
this.
|
|
468
|
-
|
|
469
|
-
|
|
741
|
+
void this.notificationProcessingQueue
|
|
742
|
+
.push(async () => {
|
|
743
|
+
this.logger.info(`Resuming message sending. ${(0, messageSender_1.getQueueLength)()} messages to be sent`);
|
|
744
|
+
(0, IncomingProposalsQueue_1.resumeProposalProcessing)();
|
|
745
|
+
(0, messageSender_1.resumeMessageSending)();
|
|
746
|
+
(0, conversationRejoinQueue_1.resumeRejoiningMLSConversations)();
|
|
747
|
+
onConnectionStateChanged(ConnectionState.LIVE);
|
|
748
|
+
this.apiClient.transport.ws.unlock();
|
|
749
|
+
})
|
|
750
|
+
.catch(this.handleNotificationQueueError);
|
|
470
751
|
};
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
752
|
+
};
|
|
753
|
+
/**
|
|
754
|
+
* In case of a closed connection, we flush the notification processing queue.
|
|
755
|
+
* As we are not acknowledging them before decryption is done
|
|
756
|
+
* they will be resent next time the connection is opened
|
|
757
|
+
* this is to avoid duplicate decryption of notifications
|
|
758
|
+
*/
|
|
759
|
+
pauseAndFlushNotificationQueue = () => {
|
|
760
|
+
this.notificationProcessingQueue.pause();
|
|
761
|
+
this.notificationProcessingQueue.flush();
|
|
762
|
+
this.logger.info('Notification processing queue paused and flushed');
|
|
763
|
+
};
|
|
764
|
+
pauseNotificationQueue = () => {
|
|
765
|
+
this.notificationProcessingQueue.pause();
|
|
766
|
+
this.logger.info('Notification processing queue paused');
|
|
767
|
+
};
|
|
768
|
+
resumeNotificationQueue = () => {
|
|
769
|
+
this.notificationProcessingQueue.resume();
|
|
770
|
+
this.logger.info('Notification processing queue resumed');
|
|
771
|
+
};
|
|
772
|
+
/**
|
|
773
|
+
* Sets up WebSocket event listeners for:
|
|
774
|
+
* - Incoming backend messages
|
|
775
|
+
* - WebSocket state changes
|
|
776
|
+
* On each new backend message, we pass it to the notification handler.
|
|
777
|
+
* On state changes, we map raw socket states to public connection states and emit them.
|
|
778
|
+
*/
|
|
779
|
+
setupWebSocketListeners = (onConnectionStateChanged, handleNotification, handleLegacyNotification, useLegacy) => {
|
|
780
|
+
this.logger.info('Setting up WebSocket listeners');
|
|
781
|
+
this.apiClient.transport.ws.removeAllListeners(tcp_1.WebSocketClient.TOPIC.ON_MESSAGE);
|
|
782
|
+
this.apiClient.transport.ws.on(tcp_1.WebSocketClient.TOPIC.ON_MESSAGE, notification => {
|
|
783
|
+
this.logger.info('Received new notification from backend', { notification });
|
|
784
|
+
if (Account.checkIsConsumable(notification)) {
|
|
785
|
+
void handleNotification(notification, notification_1.NotificationSource.WEBSOCKET);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
void handleLegacyNotification(notification, notification_1.NotificationSource.WEBSOCKET);
|
|
789
|
+
});
|
|
790
|
+
this.apiClient.transport.ws.on(tcp_1.WebSocketClient.TOPIC.ON_STATE_CHANGE, wsState => {
|
|
791
|
+
const mapping = {
|
|
792
|
+
[ReconnectingWebsocket_1.WEBSOCKET_STATE.CLOSED]: ConnectionState.CLOSED,
|
|
793
|
+
[ReconnectingWebsocket_1.WEBSOCKET_STATE.CONNECTING]: ConnectionState.CONNECTING,
|
|
794
|
+
};
|
|
795
|
+
const connectionState = mapping[wsState];
|
|
796
|
+
if (connectionState === ConnectionState.CLOSED) {
|
|
797
|
+
(0, IncomingProposalsQueue_1.flushProposalsQueue)();
|
|
798
|
+
this.pauseAndFlushNotificationQueue();
|
|
799
|
+
if (useLegacy) {
|
|
800
|
+
this.apiClient.transport.ws.lock();
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (connectionState) {
|
|
804
|
+
onConnectionStateChanged(connectionState);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
};
|
|
808
|
+
/**
|
|
809
|
+
* Handles logic for reacting to a missed notification event.
|
|
810
|
+
*
|
|
811
|
+
* The backend sends a special "missed notification" signal if it detects
|
|
812
|
+
* that the client has missed one or more notifications. Once this signal is sent,
|
|
813
|
+
* the backend will **stop sending all further notifications** until the client
|
|
814
|
+
* acknowledges the missed state.
|
|
815
|
+
*
|
|
816
|
+
* Because our app currently lacks functionality to perform a full real-time sync
|
|
817
|
+
* while running, we must reload the application to re-fetch the entire state.
|
|
818
|
+
*
|
|
819
|
+
* On first detection of the missed notification:
|
|
820
|
+
* - We set a local storage flag (`has_missing_notification`) to mark that we've
|
|
821
|
+
* entered this state.
|
|
822
|
+
* - We reload the application so the state can be re-fetched from scratch.
|
|
823
|
+
*
|
|
824
|
+
* On the next load:
|
|
825
|
+
* - If the flag is already present, we acknowledge the missed notification via
|
|
826
|
+
* the WebSocket transport, unblocking the backend so it resumes sending updates
|
|
827
|
+
* then we remove the flag.
|
|
828
|
+
*/
|
|
829
|
+
reactToMissedNotification = () => {
|
|
830
|
+
this.logger.info('Reacting to missed notification from consumable async websocket');
|
|
831
|
+
const localStorageKey = 'has_missing_notification';
|
|
832
|
+
// First-time handling: set flag and reload to trigger full re-fetch of state.
|
|
833
|
+
if (!exports.AccountLocalStorageStore.has(localStorageKey)) {
|
|
834
|
+
this.logger.info('First missed notification detected, reloading to recover state');
|
|
835
|
+
exports.AccountLocalStorageStore.add(localStorageKey, 'true');
|
|
836
|
+
window.location.reload();
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
this.logger.info('Missed notification previously detected, acknowledging to resume updates');
|
|
840
|
+
// After reload: acknowledge the missed notification so backend resumes notifications.
|
|
841
|
+
this.apiClient.transport.ws.acknowledgeMissedNotification();
|
|
842
|
+
exports.AccountLocalStorageStore.remove(localStorageKey);
|
|
843
|
+
};
|
|
844
|
+
getClientCapabilities = () => {
|
|
845
|
+
return this.currentClient?.capabilities || [];
|
|
846
|
+
};
|
|
847
|
+
static checkIsConsumable = (notification) => {
|
|
848
|
+
return 'type' in notification;
|
|
849
|
+
};
|
|
850
|
+
static checkIsLegacyNotification = (notification) => {
|
|
851
|
+
return !Account.checkIsConsumable(notification);
|
|
852
|
+
};
|
|
853
|
+
generateDbName = (context) => {
|
|
479
854
|
const clientType = context.clientType === client_1.ClientType.NONE ? '' : `@${context.clientType}`;
|
|
480
855
|
return `wire@${this.apiClient.config.urls.name}@${context.userId}${clientType}`;
|
|
481
|
-
}
|
|
482
|
-
generateCoreDbName(context) {
|
|
856
|
+
};
|
|
857
|
+
generateCoreDbName = (context) => {
|
|
483
858
|
return `core-${this.generateDbName(context)}`;
|
|
484
|
-
}
|
|
485
|
-
generateEncryptedDbName(context) {
|
|
859
|
+
};
|
|
860
|
+
generateEncryptedDbName = (context) => {
|
|
486
861
|
return `secrets-${this.generateDbName(context)}`;
|
|
487
|
-
}
|
|
488
|
-
async
|
|
862
|
+
};
|
|
863
|
+
initEngine = async (context, encryptedStore) => {
|
|
489
864
|
const dbName = this.generateDbName(context);
|
|
490
865
|
this.logger.debug(`Initialising store with name "${dbName}"...`);
|
|
491
866
|
const openDb = async () => {
|
|
@@ -506,28 +881,29 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
506
881
|
await this.persistCookie(storeEngine, cookie);
|
|
507
882
|
}
|
|
508
883
|
return storeEngine;
|
|
509
|
-
}
|
|
884
|
+
};
|
|
510
885
|
groupIdFromConversationId = async (conversationId, subconversationId) => {
|
|
511
886
|
if (!subconversationId) {
|
|
512
887
|
return this.coreCallbacks?.groupIdFromConversationId(conversationId);
|
|
513
888
|
}
|
|
514
889
|
return this.service?.subconversation.getSubconversationGroupId(conversationId, subconversationId);
|
|
515
890
|
};
|
|
516
|
-
async
|
|
517
|
-
//
|
|
518
|
-
|
|
519
|
-
if (!isMLSServiceInitialized) {
|
|
891
|
+
isMLSActiveForClient = async () => {
|
|
892
|
+
// Check for CoreCrypto library, it is required for MLS
|
|
893
|
+
if (!this.options.coreCryptoConfig?.enabled) {
|
|
520
894
|
return false;
|
|
521
895
|
}
|
|
522
|
-
//
|
|
523
|
-
|
|
524
|
-
if (!isMLSSupported) {
|
|
896
|
+
// Check if the backend supports MLS trough removal keys
|
|
897
|
+
if (!(await this.apiClient.supportsMLS())) {
|
|
525
898
|
return false;
|
|
526
899
|
}
|
|
527
|
-
// MLS is enabled for the public via feature flag
|
|
900
|
+
// Check if MLS is enabled for the public via backend feature flag
|
|
528
901
|
const commonConfig = (await this.service?.team.getCommonFeatureConfig()) ?? {};
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
902
|
+
return commonConfig[team_1.FEATURE_KEY.MLS]?.status === team_1.FEATURE_STATUS.ENABLED;
|
|
903
|
+
};
|
|
904
|
+
isMLSConversationRecoveryEnabled = async () => {
|
|
905
|
+
const commonConfig = (await this.service?.team.getCommonFeatureConfig()) ?? {};
|
|
906
|
+
return commonConfig[team_1.FEATURE_KEY.ALLOWED_GLOBAL_OPERATIONS]?.config?.mlsConversationReset === true;
|
|
907
|
+
};
|
|
532
908
|
}
|
|
533
909
|
exports.Account = Account;
|