@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
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
const mls_1 = require("@
|
|
21
|
+
const mls_1 = require("@wireapp/protocol-messaging/web/mls");
|
|
22
22
|
const bazinga64_1 = require("bazinga64");
|
|
23
23
|
const core_crypto_1 = require("@wireapp/core-crypto");
|
|
24
24
|
const commitBundleUtil_1 = require("./commitBundleUtil");
|
|
@@ -26,18 +26,18 @@ describe('toProtobufCommitBundle', () => {
|
|
|
26
26
|
it('encode commit bundles', () => {
|
|
27
27
|
const payload = {
|
|
28
28
|
commit: Uint8Array.from([0]),
|
|
29
|
-
welcome: Uint8Array.from([1]),
|
|
29
|
+
welcome: new core_crypto_1.Welcome(Uint8Array.from([1])),
|
|
30
30
|
groupInfo: {
|
|
31
31
|
ratchetTreeType: core_crypto_1.RatchetTreeType.Full,
|
|
32
|
-
payload: Uint8Array.from([2]),
|
|
32
|
+
payload: new core_crypto_1.GroupInfo(Uint8Array.from([2])),
|
|
33
33
|
encryptionType: core_crypto_1.GroupInfoEncryptionType.Plaintext,
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
const result = (0, commitBundleUtil_1.toProtobufCommitBundle)(payload);
|
|
37
37
|
const { commit, welcome, groupInfoBundle } = mls_1.mls.CommitBundle.decode(result);
|
|
38
38
|
expect(bazinga64_1.Encoder.toBase64(commit)).toEqual(bazinga64_1.Encoder.toBase64(payload.commit));
|
|
39
|
-
expect(bazinga64_1.Encoder.toBase64(welcome)).toEqual(bazinga64_1.Encoder.toBase64(payload.welcome));
|
|
40
|
-
expect(bazinga64_1.Encoder.toBase64(groupInfoBundle.groupInfo)).toEqual(bazinga64_1.Encoder.toBase64(payload.groupInfo.payload));
|
|
39
|
+
expect(bazinga64_1.Encoder.toBase64(welcome)).toEqual(bazinga64_1.Encoder.toBase64(payload.welcome?.copyBytes()));
|
|
40
|
+
expect(bazinga64_1.Encoder.toBase64(groupInfoBundle.groupInfo)).toEqual(bazinga64_1.Encoder.toBase64(payload.groupInfo.payload.copyBytes()));
|
|
41
41
|
expect(groupInfoBundle.ratchetTreeType).toEqual(mls_1.mls.RatchetTreeType.FULL);
|
|
42
42
|
expect(groupInfoBundle.groupInfoType).toEqual(mls_1.mls.GroupInfoType.PUBLIC_GROUP_STATE);
|
|
43
43
|
});
|
|
@@ -38,8 +38,8 @@ async function queueConversationRejoin(groupId, rejoinFn) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
function resumeRejoiningMLSConversations() {
|
|
41
|
-
sendingQueue.
|
|
41
|
+
sendingQueue.resume();
|
|
42
42
|
}
|
|
43
43
|
function pauseRejoiningMLSConversations() {
|
|
44
|
-
sendingQueue.pause(
|
|
44
|
+
sendingQueue.pause();
|
|
45
45
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, library-free Chain of Responsibility to normalize diverse MLS/core-crypto/backend
|
|
3
|
+
* errors into a closed {@link DomainMlsError} union. This module is side-effect free and only
|
|
4
|
+
* classifies errors; recovery actions are implemented by a separate orchestrator.
|
|
5
|
+
*/
|
|
6
|
+
import { SUBCONVERSATION_ID } from '@wireapp/api-client/lib/conversation';
|
|
7
|
+
import { QualifiedId } from '@wireapp/api-client/lib/user';
|
|
8
|
+
import { MlsErrorType } from '@wireapp/core-crypto';
|
|
9
|
+
/**
|
|
10
|
+
* Domain error taxonomy used by policies and orchestrator.
|
|
11
|
+
*
|
|
12
|
+
* These are intentionally decoupled from concrete error classes to keep the orchestrator stable.
|
|
13
|
+
*/
|
|
14
|
+
export type DomainMlsErrorType = MlsErrorType | 'KeyMaterialUpdateFailure' | 'GroupOutOfSync' | 'GroupNotEstablished';
|
|
15
|
+
/**
|
|
16
|
+
* Normalized error shape produced by the mapper.
|
|
17
|
+
*/
|
|
18
|
+
export type DomainMlsError = {
|
|
19
|
+
/** The domain classification. */
|
|
20
|
+
type: DomainMlsErrorType;
|
|
21
|
+
/** Optional human-friendly message for logging only. */
|
|
22
|
+
message?: string;
|
|
23
|
+
/** The original error value, preserved for debugging. */
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
/** Lightweight, structured context to inform recovery. */
|
|
26
|
+
context?: {
|
|
27
|
+
/** Conversation qualified id if known. */
|
|
28
|
+
qualifiedConversationId?: QualifiedId;
|
|
29
|
+
/** MLS group id (base64) if available. */
|
|
30
|
+
groupId?: string;
|
|
31
|
+
/** Expected/observed epoch number if relevant. */
|
|
32
|
+
epoch?: number | bigint;
|
|
33
|
+
/** Users reported as missing by the backend or MLS layer. */
|
|
34
|
+
missingUsers?: QualifiedId[];
|
|
35
|
+
/** Subconversation context (e.g. conference). */
|
|
36
|
+
subconvId?: SUBCONVERSATION_ID;
|
|
37
|
+
/** HTTP status when the source error came from the backend. */
|
|
38
|
+
httpStatus?: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Optional context supplied by the caller to improve mapping precision.
|
|
43
|
+
*/
|
|
44
|
+
export type ErrorContextInput = {
|
|
45
|
+
qualifiedConversationId?: QualifiedId;
|
|
46
|
+
groupId?: string;
|
|
47
|
+
subconvId?: SUBCONVERSATION_ID;
|
|
48
|
+
};
|
|
49
|
+
/** One handler in the chain. Must be side-effect free. */
|
|
50
|
+
export interface ErrorHandler {
|
|
51
|
+
/** True if this handler can map the provided error given optional context. */
|
|
52
|
+
canHandle(error: unknown, context?: ErrorContextInput): boolean;
|
|
53
|
+
/** Return a mapped error or undefined to defer to later handlers. */
|
|
54
|
+
map(error: unknown, context?: ErrorContextInput): DomainMlsError;
|
|
55
|
+
}
|
|
56
|
+
/** Public mapper interface returning a normalized {@link DomainMlsError}. */
|
|
57
|
+
export interface MlsErrorMapper {
|
|
58
|
+
map(error: unknown, context?: ErrorContextInput): DomainMlsError;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Deterministic chain execution
|
|
62
|
+
*
|
|
63
|
+
* The first handler that both
|
|
64
|
+
* canHandle and returns a non-undefined mapping wins. If none map, an Unknown error is returned.
|
|
65
|
+
*/
|
|
66
|
+
export declare class ChainedMlsErrorMapper implements MlsErrorMapper {
|
|
67
|
+
private readonly handlers;
|
|
68
|
+
constructor(handlers: ErrorHandler[]);
|
|
69
|
+
map(error: unknown, context?: ErrorContextInput): DomainMlsError;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Factory for the default mapper chain used by the orchestrator.
|
|
73
|
+
*
|
|
74
|
+
* Order matters: earlier handlers have higher precedence. The selection reflects
|
|
75
|
+
* the most common recovery decisions needed by the orchestrator.
|
|
76
|
+
*/
|
|
77
|
+
export declare function createDefaultMlsErrorMapper(): MlsErrorMapper;
|
|
78
|
+
//# sourceMappingURL=MlsErrorMapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MlsErrorMapper.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/recovery/MlsErrorMapper.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AAEH,OAAO,EAAC,kBAAkB,EAA+C,MAAM,sCAAsC,CAAC;AACtH,OAAO,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAC;AAGzD,OAAO,EAA+D,YAAY,EAAC,MAAM,sBAAsB,CAAC;AAYhH;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,0BAA0B,GAAG,gBAAgB,GAAG,qBAAqB,CAAC;AAEtH;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,iCAAiC;IACjC,IAAI,EAAE,kBAAkB,CAAC;IACzB,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,OAAO,CAAC,EAAE;QACR,0CAA0C;QAC1C,uBAAuB,CAAC,EAAE,WAAW,CAAC;QACtC,0CAA0C;QAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,kDAAkD;QAClD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QACxB,6DAA6D;QAC7D,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;QAC7B,iDAAiD;QACjD,SAAS,CAAC,EAAE,kBAAkB,CAAC;QAC/B,+DAA+D;QAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uBAAuB,CAAC,EAAE,WAAW,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAChE,qEAAqE;IACrE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;CAClE;AAED,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;CAClE;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;gBAE9B,QAAQ,EAAE,YAAY,EAAE;IAIpC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc;CAYjE;AA4FD;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,cAAc,CAS5D"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2025 Wire Swiss GmbH
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with this program. If not, see http://www.gnu.org/licenses/.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.ChainedMlsErrorMapper = void 0;
|
|
22
|
+
exports.createDefaultMlsErrorMapper = createDefaultMlsErrorMapper;
|
|
23
|
+
/**
|
|
24
|
+
* A small, library-free Chain of Responsibility to normalize diverse MLS/core-crypto/backend
|
|
25
|
+
* errors into a closed {@link DomainMlsError} union. This module is side-effect free and only
|
|
26
|
+
* classifies errors; recovery actions are implemented by a separate orchestrator.
|
|
27
|
+
*/
|
|
28
|
+
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
29
|
+
const bazinga64_1 = require("bazinga64");
|
|
30
|
+
const core_crypto_1 = require("@wireapp/core-crypto");
|
|
31
|
+
// Reuse existing type-guards from the MLS layer
|
|
32
|
+
const CoreCryptoMLSError_1 = require("../MLSService/CoreCryptoMLSError");
|
|
33
|
+
/**
|
|
34
|
+
* Deterministic chain execution
|
|
35
|
+
*
|
|
36
|
+
* The first handler that both
|
|
37
|
+
* canHandle and returns a non-undefined mapping wins. If none map, an Unknown error is returned.
|
|
38
|
+
*/
|
|
39
|
+
class ChainedMlsErrorMapper {
|
|
40
|
+
handlers;
|
|
41
|
+
constructor(handlers) {
|
|
42
|
+
this.handlers = handlers.slice();
|
|
43
|
+
}
|
|
44
|
+
map(error, context) {
|
|
45
|
+
for (const handler of this.handlers) {
|
|
46
|
+
if (handler.canHandle(error, context)) {
|
|
47
|
+
const mapped = handler.map(error, context);
|
|
48
|
+
if (mapped) {
|
|
49
|
+
return mapped;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return FallbackHandler.map(error, context);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.ChainedMlsErrorMapper = ChainedMlsErrorMapper;
|
|
57
|
+
/** ---------------------- Concrete handlers ---------------------- */
|
|
58
|
+
/** Wrong epoch or stale message from MLS/core-crypto/backend. */
|
|
59
|
+
const WrongEpochHandler = {
|
|
60
|
+
canHandle: err => (0, CoreCryptoMLSError_1.isCoreCryptoMLSWrongEpochError)?.(err) || (0, CoreCryptoMLSError_1.isMLSStaleMessageError)?.(err) || err instanceof conversation_1.MLSStaleMessageError,
|
|
61
|
+
map: (err, context) => ({
|
|
62
|
+
type: core_crypto_1.MlsErrorType.WrongEpoch,
|
|
63
|
+
message: 'Epoch mismatch or stale message',
|
|
64
|
+
cause: err,
|
|
65
|
+
context: {
|
|
66
|
+
qualifiedConversationId: context?.qualifiedConversationId,
|
|
67
|
+
groupId: context?.groupId,
|
|
68
|
+
subconvId: context?.subconvId,
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
/** Local MLS state indicates the conversation is broken/not established. */
|
|
73
|
+
const BrokenConversationHandler = {
|
|
74
|
+
canHandle: err => (0, CoreCryptoMLSError_1.isBrokenMLSConversationError)?.(err) === true,
|
|
75
|
+
map: (err, context) => ({
|
|
76
|
+
type: 'GroupNotEstablished',
|
|
77
|
+
message: 'Broken MLS conversation',
|
|
78
|
+
cause: err,
|
|
79
|
+
context: { qualifiedConversationId: context?.qualifiedConversationId, groupId: context?.groupId },
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
/** Backend/MLS reports missing users; group is out-of-sync. */
|
|
83
|
+
const GroupOutOfSyncHandler = {
|
|
84
|
+
canHandle: err => (0, CoreCryptoMLSError_1.isMLSGroupOutOfSyncError)?.(err) === true || err instanceof conversation_1.MLSGroupOutOfSyncError,
|
|
85
|
+
map: (err, context) => {
|
|
86
|
+
let missingUsers = [];
|
|
87
|
+
if ((0, CoreCryptoMLSError_1.isMLSGroupOutOfSyncError)?.(err)) {
|
|
88
|
+
missingUsers = (0, CoreCryptoMLSError_1.getMLSGroupOutOfSyncErrorMissingUsers)(err);
|
|
89
|
+
}
|
|
90
|
+
else if (err instanceof conversation_1.MLSGroupOutOfSyncError) {
|
|
91
|
+
missingUsers = err.missing_users;
|
|
92
|
+
}
|
|
93
|
+
if (missingUsers.length === 0) {
|
|
94
|
+
throw new Error('Error is not a GroupOutOfSync error with missing users');
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
type: 'GroupOutOfSync',
|
|
98
|
+
message: 'Group out of sync; missing users detected',
|
|
99
|
+
cause: err,
|
|
100
|
+
context: { qualifiedConversationId: context?.qualifiedConversationId, groupId: context?.groupId, missingUsers },
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
/** core-crypto indicates a local group already exists for the welcome's group id. */
|
|
105
|
+
const ConversationAlreadyExistsHandler = {
|
|
106
|
+
canHandle: error => (0, core_crypto_1.isMlsConversationAlreadyExistsError)?.(error) === true,
|
|
107
|
+
map: (error, context) => {
|
|
108
|
+
if (!(0, core_crypto_1.isMlsConversationAlreadyExistsError)(error)) {
|
|
109
|
+
throw new Error('Error is not a ConversationAlreadyExists error');
|
|
110
|
+
}
|
|
111
|
+
const groupId = tryExtractGroupIdFromCoreCryptoError(error);
|
|
112
|
+
return {
|
|
113
|
+
type: core_crypto_1.MlsErrorType.ConversationAlreadyExists,
|
|
114
|
+
message: 'Conversation already exists',
|
|
115
|
+
cause: error,
|
|
116
|
+
context: { groupId, qualifiedConversationId: context?.qualifiedConversationId },
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
/** Orphan welcome (no matching state); caller should try to join. */
|
|
121
|
+
const OrphanWelcomeHandler = {
|
|
122
|
+
canHandle: err => (0, core_crypto_1.isMlsOrphanWelcomeError)?.(err) === true,
|
|
123
|
+
map: (err, context) => ({
|
|
124
|
+
type: core_crypto_1.MlsErrorType.OrphanWelcome,
|
|
125
|
+
message: 'Orphan welcome message',
|
|
126
|
+
cause: err,
|
|
127
|
+
context: { qualifiedConversationId: context?.qualifiedConversationId },
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
/** Fallback classification when no handler matches. */
|
|
131
|
+
const FallbackHandler = {
|
|
132
|
+
canHandle: () => true,
|
|
133
|
+
map: (err, context) => err instanceof Error
|
|
134
|
+
? { type: core_crypto_1.MlsErrorType.Other, message: err.message, cause: err, context }
|
|
135
|
+
: { type: core_crypto_1.MlsErrorType.Other, message: String(err), cause: err, context },
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Factory for the default mapper chain used by the orchestrator.
|
|
139
|
+
*
|
|
140
|
+
* Order matters: earlier handlers have higher precedence. The selection reflects
|
|
141
|
+
* the most common recovery decisions needed by the orchestrator.
|
|
142
|
+
*/
|
|
143
|
+
function createDefaultMlsErrorMapper() {
|
|
144
|
+
return new ChainedMlsErrorMapper([
|
|
145
|
+
WrongEpochHandler,
|
|
146
|
+
BrokenConversationHandler,
|
|
147
|
+
GroupOutOfSyncHandler,
|
|
148
|
+
ConversationAlreadyExistsHandler,
|
|
149
|
+
OrphanWelcomeHandler,
|
|
150
|
+
FallbackHandler,
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
/** ---------------------- helpers ---------------------- */
|
|
154
|
+
/**
|
|
155
|
+
* Extract the base64 group id from a core-crypto error if present.
|
|
156
|
+
*
|
|
157
|
+
* core-crypto encodes the conversation id bytes under `error.context.context.conversationId`.
|
|
158
|
+
* We convert the byte array to base64 for uniform handling in higher layers.
|
|
159
|
+
*/
|
|
160
|
+
function tryExtractGroupIdFromCoreCryptoError(err) {
|
|
161
|
+
try {
|
|
162
|
+
// core-crypto error.context?.context?.conversationId is a byte array (number[])
|
|
163
|
+
const conversationIdArray = err?.context?.context?.conversationId;
|
|
164
|
+
if (!conversationIdArray) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
return bazinga64_1.Encoder.toBase64(new Uint8Array(conversationIdArray)).asString;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Intentionally not handling HTTP/network errors here; let them bubble up unmodified.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MlsErrorMapper.test.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/recovery/MlsErrorMapper.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Wire
|
|
4
|
+
* Copyright (C) 2025 Wire Swiss GmbH
|
|
5
|
+
*
|
|
6
|
+
* This program is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* This program is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with this program. If not, see http://www.gnu.org/licenses/.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
22
|
+
const bazinga64_1 = require("bazinga64");
|
|
23
|
+
const core_crypto_1 = require("@wireapp/core-crypto");
|
|
24
|
+
const MlsErrorMapper_1 = require("./MlsErrorMapper");
|
|
25
|
+
const CoreCryptoMLSError_1 = require("../MLSService/CoreCryptoMLSError");
|
|
26
|
+
// Helpers
|
|
27
|
+
const id = { id: 'conv-id', domain: 'wire.test' };
|
|
28
|
+
const groupIdBase64 = bazinga64_1.Encoder.toBase64(new Uint8Array([1, 2, 3, 4])).asString;
|
|
29
|
+
const mapper = (0, MlsErrorMapper_1.createDefaultMlsErrorMapper)();
|
|
30
|
+
function map(error, extra) {
|
|
31
|
+
return mapper.map(error, { qualifiedConversationId: id, groupId: extra?.groupId });
|
|
32
|
+
}
|
|
33
|
+
describe('MlsErrorMapper', () => {
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
jest.restoreAllMocks();
|
|
36
|
+
});
|
|
37
|
+
it('maps WrongEpoch via stale message class instance', () => {
|
|
38
|
+
const err = new conversation_1.MLSStaleMessageError('stale');
|
|
39
|
+
const mapped = map(err, { groupId: groupIdBase64 });
|
|
40
|
+
expect(mapped.type).toBe('WrongEpoch');
|
|
41
|
+
expect(mapped.context?.qualifiedConversationId).toEqual(id);
|
|
42
|
+
expect(mapped.context?.groupId).toBe(groupIdBase64);
|
|
43
|
+
});
|
|
44
|
+
it('maps GroupNotEstablished for broken conversation error', () => {
|
|
45
|
+
const err = {
|
|
46
|
+
type: core_crypto_1.ErrorType.Mls,
|
|
47
|
+
context: {
|
|
48
|
+
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
49
|
+
context: { reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.BROKEN_MLS_CONVERSATION }) },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
const mapped = map(err);
|
|
53
|
+
expect(mapped.type).toBe('GroupNotEstablished');
|
|
54
|
+
});
|
|
55
|
+
it('maps GroupOutOfSync with missing users list', () => {
|
|
56
|
+
const missing = [
|
|
57
|
+
{ id: 'u1', domain: 'wire.test' },
|
|
58
|
+
{ id: 'u2', domain: 'wire.test' },
|
|
59
|
+
];
|
|
60
|
+
const err = {
|
|
61
|
+
type: core_crypto_1.ErrorType.Mls,
|
|
62
|
+
context: {
|
|
63
|
+
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
64
|
+
context: {
|
|
65
|
+
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({
|
|
66
|
+
message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_GROUP_OUT_OF_SYNC,
|
|
67
|
+
missing_users: missing,
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const mapped = map(err, { groupId: groupIdBase64 });
|
|
73
|
+
expect(mapped.type).toBe('GroupOutOfSync');
|
|
74
|
+
expect(mapped.context?.missingUsers).toEqual(missing);
|
|
75
|
+
expect(mapped.context?.groupId).toBe(groupIdBase64);
|
|
76
|
+
});
|
|
77
|
+
it('maps ConversationAlreadyExists and extracts groupId bytes', () => {
|
|
78
|
+
const conversationIdArray = [1, 2, 3, 4];
|
|
79
|
+
const expectedGroupId = bazinga64_1.Encoder.toBase64(new Uint8Array(conversationIdArray)).asString;
|
|
80
|
+
const err = {
|
|
81
|
+
type: core_crypto_1.ErrorType.Mls,
|
|
82
|
+
context: { type: core_crypto_1.MlsErrorType.ConversationAlreadyExists, context: { conversationId: conversationIdArray } },
|
|
83
|
+
};
|
|
84
|
+
const mapped = map(err);
|
|
85
|
+
expect(mapped.type).toBe('ConversationAlreadyExists');
|
|
86
|
+
expect(mapped.context?.groupId).toBe(expectedGroupId);
|
|
87
|
+
});
|
|
88
|
+
it('maps OrphanWelcome', () => {
|
|
89
|
+
const err = { type: core_crypto_1.ErrorType.Mls, context: { type: core_crypto_1.MlsErrorType.OrphanWelcome } };
|
|
90
|
+
const mapped = map(err);
|
|
91
|
+
expect(mapped.type).toBe('OrphanWelcome');
|
|
92
|
+
expect(mapped.context?.qualifiedConversationId).toEqual(id);
|
|
93
|
+
});
|
|
94
|
+
it('falls back to Unknown for unmapped error', () => {
|
|
95
|
+
const err = new Error('random');
|
|
96
|
+
const mapped = map(err);
|
|
97
|
+
expect(mapped.type).toBe(core_crypto_1.MlsErrorType.Other);
|
|
98
|
+
expect(mapped.cause).toBe(err);
|
|
99
|
+
});
|
|
100
|
+
it('honors handler priority (WrongEpoch before GroupOutOfSync)', () => {
|
|
101
|
+
const err = new Error('ambiguous');
|
|
102
|
+
err.name = CoreCryptoMLSError_1.CORE_CRYPTO_ERROR_NAMES.MlsErrorWrongEpoch; // triggers WrongEpoch guard
|
|
103
|
+
// Also craft message-rejected with out-of-sync to trigger GroupOutOfSync guard
|
|
104
|
+
err.type = core_crypto_1.ErrorType.Mls;
|
|
105
|
+
err.context = {
|
|
106
|
+
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
107
|
+
context: {
|
|
108
|
+
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({
|
|
109
|
+
message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_GROUP_OUT_OF_SYNC,
|
|
110
|
+
missing_users: [],
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
const mapped = map(err);
|
|
115
|
+
expect(mapped.type).toBe('WrongEpoch');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { SUBCONVERSATION_ID } from '@wireapp/api-client/lib/conversation';
|
|
2
|
+
import { QualifiedId } from '@wireapp/api-client/lib/user';
|
|
3
|
+
import { DomainMlsErrorType, MlsErrorMapper } from './MlsErrorMapper';
|
|
4
|
+
import { BaseCreateConversationResponse } from '../../../conversation';
|
|
5
|
+
/**
|
|
6
|
+
* Coordinates recovery actions for MLS operations.
|
|
7
|
+
*
|
|
8
|
+
* The orchestrator takes an arbitrary async "operation" (e.g. send, add users, handle welcome),
|
|
9
|
+
* executes it, and on failure maps the thrown error to a DomainMlsError via {@link MlsErrorMapper}.
|
|
10
|
+
* A recovery policy is then resolved and executed exactly-once per unique key, with optional
|
|
11
|
+
* re-run of the original operation after recovery.
|
|
12
|
+
*
|
|
13
|
+
* Key properties:
|
|
14
|
+
* - Single-flight: duplicate recoveries (same action+conversation+group+subconv) are deduplicated.
|
|
15
|
+
* - Per-operation policies: the same DomainMlsError can map to different actions depending on operation.
|
|
16
|
+
* - Non-invasive: the orchestrator has no MLS logic; it calls provided deps to perform actions.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Concrete recovery actions the orchestrator can trigger.
|
|
20
|
+
*/
|
|
21
|
+
export declare enum RecoveryActionKind {
|
|
22
|
+
/**
|
|
23
|
+
* Issue an external-commit join for the conversation.
|
|
24
|
+
*/
|
|
25
|
+
JoinViaExternalCommit = "JoinViaExternalCommit",
|
|
26
|
+
/**
|
|
27
|
+
* Fetch missing epoch state and reconcile.
|
|
28
|
+
*/
|
|
29
|
+
RecoverFromEpochMismatch = "RecoverFromEpochMismatch",
|
|
30
|
+
/**
|
|
31
|
+
* Tear down local MLS state and re-create the group.
|
|
32
|
+
*/
|
|
33
|
+
ResetAndReestablish = "ResetAndReestablish",
|
|
34
|
+
/**
|
|
35
|
+
* Add users reported as missing to get the group back in sync.
|
|
36
|
+
*/
|
|
37
|
+
AddMissingUsers = "AddMissingUsers",
|
|
38
|
+
/**
|
|
39
|
+
* Remove local MLS artifacts for the group and retry welcome handling.
|
|
40
|
+
*/
|
|
41
|
+
WipeAndReprocessWelcome = "WipeAndReprocessWelcome",
|
|
42
|
+
/**
|
|
43
|
+
* No matching policy found; treated as no-op and the original error is re-thrown.
|
|
44
|
+
*/
|
|
45
|
+
Unknown = "Unknown"
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Retry behavior for an operation under recovery.
|
|
49
|
+
*/
|
|
50
|
+
export type RetryPolicy = {
|
|
51
|
+
maxAttempts: number;
|
|
52
|
+
delayMs?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Whether to retry the original operation once after a successful recovery
|
|
55
|
+
*/
|
|
56
|
+
reRunOriginalOperation?: boolean;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* A single recovery policy that pairs an action with a retry configuration.
|
|
60
|
+
*/
|
|
61
|
+
export type RecoveryPolicy = {
|
|
62
|
+
action: RecoveryActionKind;
|
|
63
|
+
retryConfig: RetryPolicy;
|
|
64
|
+
};
|
|
65
|
+
type PerOperationPolicies = Partial<Record<OperationName, RecoveryPolicy>>;
|
|
66
|
+
/**
|
|
67
|
+
* Global policy table from DomainMlsErrorType to RecoveryPolicy.
|
|
68
|
+
*
|
|
69
|
+
* A value can be either a flat policy (applies to all operations) or a per-operation
|
|
70
|
+
* map that allows different actions depending on the invoking operation.
|
|
71
|
+
*/
|
|
72
|
+
export type PolicyTable = Partial<Record<DomainMlsErrorType, RecoveryPolicy | PerOperationPolicies>>;
|
|
73
|
+
export declare enum OperationName {
|
|
74
|
+
send = "send",
|
|
75
|
+
addUsers = "addUsers",
|
|
76
|
+
removeUsers = "removeUsers",
|
|
77
|
+
joinExternalCommit = "joinExternalCommit",
|
|
78
|
+
handleWelcome = "handleWelcome",
|
|
79
|
+
handleMessageAdd = "handleMessageAdd",
|
|
80
|
+
keyMaterialUpdate = "keyMaterialUpdate"
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Context about the operation being orchestrated. Used for policy selection and keying.
|
|
84
|
+
*/
|
|
85
|
+
export type OperationContext = {
|
|
86
|
+
operationName: OperationName;
|
|
87
|
+
qualifiedConversationId?: QualifiedId;
|
|
88
|
+
groupId?: string;
|
|
89
|
+
subconvId?: SUBCONVERSATION_ID;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Public orchestrator interface. Wrap your async operation with {@link execute}.
|
|
93
|
+
*
|
|
94
|
+
* The callback is invoked immediately. If it throws, the orchestrator maps the error,
|
|
95
|
+
* performs recovery according to policies, optionally waits, and may re-run the callback once.
|
|
96
|
+
*/
|
|
97
|
+
export interface MlsRecoveryOrchestrator {
|
|
98
|
+
execute(params: executeParams<void>): Promise<void>;
|
|
99
|
+
execute<T>(params: executeParams<T>): Promise<T>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Concrete side-effecting functions the orchestrator uses to perform recovery.
|
|
103
|
+
* Provided by the MLS layer/service.
|
|
104
|
+
*/
|
|
105
|
+
export type OrchestratorDeps = {
|
|
106
|
+
joinViaExternalCommit: (conversationId: QualifiedId) => Promise<void>;
|
|
107
|
+
resetAndReestablish: (conversationId: QualifiedId) => Promise<void>;
|
|
108
|
+
recoverFromEpochMismatch: (conversationId: QualifiedId, subconvId?: SUBCONVERSATION_ID) => Promise<void>;
|
|
109
|
+
addMissingUsers: (conversationId: QualifiedId, groupId: string, users: QualifiedId[]) => Promise<BaseCreateConversationResponse>;
|
|
110
|
+
wipeMLSConversation: (groupId: string) => Promise<void>;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Parameters for {@link execute}.
|
|
114
|
+
*/
|
|
115
|
+
type executeParams<T> = {
|
|
116
|
+
context: OperationContext;
|
|
117
|
+
callBack: () => Promise<T>;
|
|
118
|
+
retry?: boolean;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Default implementation with in-process deduplication keyed by action and context.
|
|
122
|
+
*/
|
|
123
|
+
export declare class MlsRecoveryOrchestratorImpl implements MlsRecoveryOrchestrator {
|
|
124
|
+
private readonly mapper;
|
|
125
|
+
private readonly policies;
|
|
126
|
+
private readonly deps;
|
|
127
|
+
private readonly inProgressRecoveries;
|
|
128
|
+
private readonly logger;
|
|
129
|
+
constructor(mapper: MlsErrorMapper, policies: PolicyTable, deps: OrchestratorDeps, inProgressRecoveries?: Set<string>, logger?: import("logdown").Logger);
|
|
130
|
+
/**
|
|
131
|
+
* Execute the provided callback, and on failure, map and perform a configured recovery.
|
|
132
|
+
* If {@link RetryPolicy.reRunOriginalOperation} is true, the callback is re-invoked once after recovery.
|
|
133
|
+
*/
|
|
134
|
+
execute(params: executeParams<void>): Promise<void>;
|
|
135
|
+
execute<T>(params: executeParams<T>): Promise<T>;
|
|
136
|
+
/** Resolve the effective policy for the mapped error and operation. Supports per-operation policies. */
|
|
137
|
+
private getPolicyFor;
|
|
138
|
+
/**
|
|
139
|
+
* Execute the concrete recovery action once per unique recovery key. Throws if required context is missing.
|
|
140
|
+
* For WipeAndReprocessWelcome, attempts to derive groupId from either the operation context or error context.
|
|
141
|
+
*/
|
|
142
|
+
private performRecovery;
|
|
143
|
+
/**
|
|
144
|
+
* Build a stable deduplication key from action and operation context.
|
|
145
|
+
*/
|
|
146
|
+
private getRecoveryKey;
|
|
147
|
+
/**
|
|
148
|
+
* Ensure the provided async task runs at most once for the given key at a time.
|
|
149
|
+
*/
|
|
150
|
+
private runOnceWithKey;
|
|
151
|
+
/** Optionally wait before re-invoking the original operation. */
|
|
152
|
+
private maybeDelay;
|
|
153
|
+
private sleep;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Minimal default policies used by the initial integration. These can be extended over time.
|
|
157
|
+
*
|
|
158
|
+
* Highlights:
|
|
159
|
+
* - WrongEpoch: reconcile and retry for typed ops; do not re-run for join.
|
|
160
|
+
* - GroupNotEstablished: reset and re-establish; typed ops re-run once.
|
|
161
|
+
* - GroupOutOfSync: add missing users and retry for typed ops.
|
|
162
|
+
* - ConversationAlreadyExists (welcome): wipe local MLS state and re-run welcome once.
|
|
163
|
+
* - OrphanWelcome (welcome): attempt join via external commit; do not re-run.
|
|
164
|
+
*/
|
|
165
|
+
export declare const minimalDefaultPolicies: PolicyTable;
|
|
166
|
+
export {};
|
|
167
|
+
//# sourceMappingURL=MlsRecoveryOrchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MlsRecoveryOrchestrator.d.ts","sourceRoot":"","sources":["../../../../src/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.ts"],"names":[],"mappings":"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"}
|