@wireapp/core 46.29.4 → 46.30.0
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 +70 -4
- package/lib/Account.d.ts.map +1 -1
- package/lib/Account.js +194 -37
- package/lib/Account.test.js +80 -145
- package/lib/client/ClientService.d.ts +3 -2
- package/lib/client/ClientService.d.ts.map +1 -1
- package/lib/client/ClientService.js +4 -1
- package/lib/notification/NotificationBackendRepository.d.ts +1 -1
- package/lib/notification/NotificationBackendRepository.d.ts.map +1 -1
- package/lib/notification/NotificationBackendRepository.js +2 -2
- package/lib/notification/NotificationService.d.ts +1 -1
- package/lib/notification/NotificationService.d.ts.map +1 -1
- package/lib/notification/NotificationService.js +4 -11
- package/package.json +3 -3
package/lib/Account.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { RegisterData, Context, Cookie, LoginData } from '@wireapp/api-client/lib/auth';
|
|
2
|
-
import { ClientType, RegisteredClient } from '@wireapp/api-client/lib/client/';
|
|
2
|
+
import { ClientCapability, ClientType, RegisteredClient } from '@wireapp/api-client/lib/client/';
|
|
3
3
|
import * as Events from '@wireapp/api-client/lib/event';
|
|
4
|
+
import { Notification } from '@wireapp/api-client/lib/notification/';
|
|
5
|
+
import { ConsumableNotification } from '@wireapp/api-client/lib/notification/ConsumableNotification';
|
|
4
6
|
import { APIClient, BackendFeatures } from '@wireapp/api-client';
|
|
5
7
|
import { TypedEventEmitter } from '@wireapp/commons';
|
|
6
8
|
import { CRUDEngine } from '@wireapp/store-engine';
|
|
@@ -65,6 +67,12 @@ export declare enum EVENTS {
|
|
|
65
67
|
type Events = {
|
|
66
68
|
[EVENTS.NEW_SESSION]: NewClient;
|
|
67
69
|
};
|
|
70
|
+
export declare const AccountLocalStorageStore: {
|
|
71
|
+
get: (key: string) => string | undefined;
|
|
72
|
+
add: (key: string, value: string) => void;
|
|
73
|
+
remove: (key: string) => void;
|
|
74
|
+
has: (key: string) => boolean;
|
|
75
|
+
};
|
|
68
76
|
export declare class Account extends TypedEventEmitter<Events> {
|
|
69
77
|
private options;
|
|
70
78
|
private readonly apiClient;
|
|
@@ -75,6 +83,9 @@ export declare class Account extends TypedEventEmitter<Events> {
|
|
|
75
83
|
private db?;
|
|
76
84
|
private encryptedDb?;
|
|
77
85
|
private coreCallbacks?;
|
|
86
|
+
private totalPendingNotifications;
|
|
87
|
+
private remainingNotifications;
|
|
88
|
+
private connectionState;
|
|
78
89
|
service?: {
|
|
79
90
|
mls?: MLSService;
|
|
80
91
|
e2eIdentity?: E2EIServiceExternal;
|
|
@@ -157,7 +168,7 @@ export declare class Account extends TypedEventEmitter<Events> {
|
|
|
157
168
|
registerClient(loginData: LoginData, clientInfo?: ClientInfo,
|
|
158
169
|
/** will add extra manual entropy to the client's identity being created */
|
|
159
170
|
entropyData?: Uint8Array): Promise<RegisteredClient>;
|
|
160
|
-
getLocalClient(): Promise<
|
|
171
|
+
getLocalClient(): Promise<import("./client/ClientService").MetaClient | undefined> | undefined;
|
|
161
172
|
/**
|
|
162
173
|
* Will initiate all the cryptographic material of the given registered device and setup all the background tasks.
|
|
163
174
|
*
|
|
@@ -204,7 +215,7 @@ export declare class Account extends TypedEventEmitter<Events> {
|
|
|
204
215
|
* @param callbacks callbacks that will be called to handle different events
|
|
205
216
|
* @returns close a function that will disconnect from the websocket
|
|
206
217
|
*/
|
|
207
|
-
listen({ onEvent, onConnectionStateChanged, onNotificationStreamProgress, onMissedNotifications, dryRun, }?: {
|
|
218
|
+
listen({ onEvent, onConnectionStateChanged: onConnectionStateChangedCallBack, onNotificationStreamProgress, onMissedNotifications, dryRun, }?: {
|
|
208
219
|
/**
|
|
209
220
|
* Called when a new event arrives from backend
|
|
210
221
|
* @param payload the payload of the event. Contains the raw event received and the decrypted data (if event was encrypted)
|
|
@@ -234,7 +245,62 @@ export declare class Account extends TypedEventEmitter<Events> {
|
|
|
234
245
|
* When set will not decrypt and not store the last notification ID. This is useful if you only want to subscribe to unencrypted backend events
|
|
235
246
|
*/
|
|
236
247
|
dryRun?: boolean;
|
|
237
|
-
}): () => void
|
|
248
|
+
}): Promise<() => void>;
|
|
249
|
+
private createConnectionStateChangedHandler;
|
|
250
|
+
/**
|
|
251
|
+
* Creates the event handler that is invoked for each decrypted event from the backend.
|
|
252
|
+
* Responsible for handling specific event types like `MESSAGE_TIMER_UPDATE`, and then
|
|
253
|
+
* forwarding the event to the consumer via the `onEvent` callback.
|
|
254
|
+
*/
|
|
255
|
+
private createEventHandler;
|
|
256
|
+
private createLegacyNotificationHandler;
|
|
257
|
+
private createNotificationHandler;
|
|
258
|
+
/**
|
|
259
|
+
* Returns a function to handle missed notifications — i.e., when the backend indicates
|
|
260
|
+
* that some notifications were lost due to age (typically >28 days).
|
|
261
|
+
* Also handles MLS-specific epoch mismatch recovery by triggering a conversation rejoin.
|
|
262
|
+
*/
|
|
263
|
+
private createLegacyMissedNotificationsHandler;
|
|
264
|
+
/**
|
|
265
|
+
* Returns a processor function for the notification stream (legacy sync).
|
|
266
|
+
* It pauses message sending and MLS rejoining during stream handling to prevent race conditions,
|
|
267
|
+
* then resumes normal operations after sync is complete.
|
|
268
|
+
*
|
|
269
|
+
* @param handlers Various logic handlers wired to notification callbacks
|
|
270
|
+
*/
|
|
271
|
+
private createLegacyNotificationStreamProcessor;
|
|
272
|
+
/**
|
|
273
|
+
* Sets up WebSocket event listeners for:
|
|
274
|
+
* - Incoming backend messages
|
|
275
|
+
* - WebSocket state changes
|
|
276
|
+
* On each new backend message, we pass it to the notification handler.
|
|
277
|
+
* On state changes, we map raw socket states to public connection states and emit them.
|
|
278
|
+
*/
|
|
279
|
+
private setupWebSocketListeners;
|
|
280
|
+
/**
|
|
281
|
+
* Handles logic for reacting to a missed notification event.
|
|
282
|
+
*
|
|
283
|
+
* The backend sends a special "missed notification" signal if it detects
|
|
284
|
+
* that the client has missed one or more notifications. Once this signal is sent,
|
|
285
|
+
* the backend will **stop sending all further notifications** until the client
|
|
286
|
+
* acknowledges the missed state.
|
|
287
|
+
*
|
|
288
|
+
* Because our app currently lacks functionality to perform a full real-time sync
|
|
289
|
+
* while running, we must reload the application to re-fetch the entire state.
|
|
290
|
+
*
|
|
291
|
+
* On first detection of the missed notification:
|
|
292
|
+
* - We set a local storage flag (`has_missing_notification`) to mark that we've
|
|
293
|
+
* entered this state.
|
|
294
|
+
* - We reload the application so the state can be re-fetched from scratch.
|
|
295
|
+
*
|
|
296
|
+
* On the next load:
|
|
297
|
+
* - If the flag is already present, we acknowledge the missed notification via
|
|
298
|
+
* the WebSocket transport, unblocking the backend so it resumes sending updates
|
|
299
|
+
* then we remove the flag.
|
|
300
|
+
*/
|
|
301
|
+
private reactToMissedNotification;
|
|
302
|
+
getClientCapabilities(): ClientCapability[];
|
|
303
|
+
checkIsConsumable(notification: Notification | ConsumableNotification): notification is ConsumableNotification;
|
|
238
304
|
private generateDbName;
|
|
239
305
|
private generateCoreDbName;
|
|
240
306
|
private generateEncryptedDbName;
|
package/lib/Account.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Account.d.ts","sourceRoot":"","sources":["../src/Account.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,YAAY,EAGZ,OAAO,EACP,MAAM,EAEN,SAAS,EAEV,MAAM,8BAA8B,CAAC;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"Account.d.ts","sourceRoot":"","sources":["../src/Account.ts"],"names":[],"mappings":"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"}
|
package/lib/Account.js
CHANGED
|
@@ -41,10 +41,11 @@ 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.EVENTS = exports.ConnectionState = void 0;
|
|
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");
|
|
@@ -76,6 +77,7 @@ const self_1 = require("./self/");
|
|
|
76
77
|
const CoreDB_1 = require("./storage/CoreDB");
|
|
77
78
|
const team_2 = require("./team/");
|
|
78
79
|
const user_1 = require("./user/");
|
|
80
|
+
const LocalStorageStore_1 = require("./util/LocalStorageStore");
|
|
79
81
|
const RecurringTaskScheduler_1 = require("./util/RecurringTaskScheduler");
|
|
80
82
|
var ConnectionState;
|
|
81
83
|
(function (ConnectionState) {
|
|
@@ -101,6 +103,7 @@ var EVENTS;
|
|
|
101
103
|
*/
|
|
102
104
|
EVENTS["NEW_SESSION"] = "new_session";
|
|
103
105
|
})(EVENTS || (exports.EVENTS = EVENTS = {}));
|
|
106
|
+
exports.AccountLocalStorageStore = (0, LocalStorageStore_1.LocalStorageStore)('core_account');
|
|
104
107
|
class Account extends commons_1.TypedEventEmitter {
|
|
105
108
|
options;
|
|
106
109
|
apiClient;
|
|
@@ -111,6 +114,9 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
111
114
|
db;
|
|
112
115
|
encryptedDb;
|
|
113
116
|
coreCallbacks;
|
|
117
|
+
totalPendingNotifications = 0;
|
|
118
|
+
remainingNotifications = 0;
|
|
119
|
+
connectionState = ConnectionState.CLOSED;
|
|
114
120
|
service;
|
|
115
121
|
backendFeatures;
|
|
116
122
|
recurringTaskScheduler;
|
|
@@ -419,11 +425,64 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
419
425
|
* @param callbacks callbacks that will be called to handle different events
|
|
420
426
|
* @returns close a function that will disconnect from the websocket
|
|
421
427
|
*/
|
|
422
|
-
listen({ onEvent = () => { }, onConnectionStateChanged = () => { }, onNotificationStreamProgress = () => { }, onMissedNotifications = () => { }, dryRun = false, } = {}) {
|
|
428
|
+
async listen({ onEvent = () => { }, onConnectionStateChanged: onConnectionStateChangedCallBack = () => { }, onNotificationStreamProgress = () => { }, onMissedNotifications = () => { }, dryRun = false, } = {}) {
|
|
423
429
|
if (!this.currentClient) {
|
|
424
430
|
throw new Error('Client has not been initialized - please login first');
|
|
425
431
|
}
|
|
426
|
-
const
|
|
432
|
+
const onConnectionStateChanged = this.createConnectionStateChangedHandler(onConnectionStateChangedCallBack);
|
|
433
|
+
const handleEvent = this.createEventHandler(onEvent);
|
|
434
|
+
const handleLegacyNotification = this.createLegacyNotificationHandler(handleEvent, dryRun);
|
|
435
|
+
const handleNotification = this.createNotificationHandler(handleEvent, onNotificationStreamProgress, onConnectionStateChanged, dryRun);
|
|
436
|
+
const handleMissedNotifications = this.createLegacyMissedNotificationsHandler(onMissedNotifications);
|
|
437
|
+
const processNotificationStream = this.createLegacyNotificationStreamProcessor({
|
|
438
|
+
handleLegacyNotification,
|
|
439
|
+
handleMissedNotifications,
|
|
440
|
+
onNotificationStreamProgress,
|
|
441
|
+
onConnectionStateChanged,
|
|
442
|
+
});
|
|
443
|
+
this.setupWebSocketListeners(handleNotification, onConnectionStateChanged);
|
|
444
|
+
const isClientCapableOfConsumableNotifications = this.getClientCapabilities().includes(client_1.ClientCapability.CONSUMABLE_NOTIFICATIONS);
|
|
445
|
+
/*
|
|
446
|
+
* When enabling async notifications, be aware that the backend maintains a separate queue
|
|
447
|
+
* for new async notifications (/events weboscket endpoint), which only starts populating *after* the client declares support
|
|
448
|
+
* for async notifications.
|
|
449
|
+
*
|
|
450
|
+
* Therefore, after declaring support, it's necessary to perform one final fetch from the legacy
|
|
451
|
+
* system to ensure no notifications are missed—since older notifications won't
|
|
452
|
+
* appear in the new queue.
|
|
453
|
+
*
|
|
454
|
+
* These two systems are separate, and the transition timing
|
|
455
|
+
* is important to avoid missing any messages during the switch.
|
|
456
|
+
*/
|
|
457
|
+
if (!isClientCapableOfConsumableNotifications) {
|
|
458
|
+
// let the backend now client is capable of consumable notifications
|
|
459
|
+
await this.service?.client.putClientCapabilities(this.currentClient.id, {
|
|
460
|
+
capabilities: [client_1.ClientCapability.LEGAL_HOLD_IMPLICIT_CONSENT, client_1.ClientCapability.CONSUMABLE_NOTIFICATIONS],
|
|
461
|
+
});
|
|
462
|
+
// do a quick legacy sync without connecting to any websockets
|
|
463
|
+
await processNotificationStream();
|
|
464
|
+
}
|
|
465
|
+
this.apiClient.connect();
|
|
466
|
+
return () => {
|
|
467
|
+
this.apiClient.disconnect();
|
|
468
|
+
onConnectionStateChanged(ConnectionState.CLOSED);
|
|
469
|
+
this.apiClient.transport.ws.removeAllListeners();
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
createConnectionStateChangedHandler(onConnectionStateChanged) {
|
|
473
|
+
return (state) => {
|
|
474
|
+
this.connectionState = state;
|
|
475
|
+
onConnectionStateChanged(state);
|
|
476
|
+
this.logger.info(`Connection state changed to: ${state}`);
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Creates the event handler that is invoked for each decrypted event from the backend.
|
|
481
|
+
* Responsible for handling specific event types like `MESSAGE_TIMER_UPDATE`, and then
|
|
482
|
+
* forwarding the event to the consumer via the `onEvent` callback.
|
|
483
|
+
*/
|
|
484
|
+
createEventHandler(onEvent) {
|
|
485
|
+
return async (payload, source) => {
|
|
427
486
|
const { event } = payload;
|
|
428
487
|
switch (event?.type) {
|
|
429
488
|
case event_1.CONVERSATION_EVENT.MESSAGE_TIMER_UPDATE: {
|
|
@@ -433,9 +492,12 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
433
492
|
break;
|
|
434
493
|
}
|
|
435
494
|
}
|
|
436
|
-
|
|
495
|
+
// Always forward the event to the consumer
|
|
496
|
+
onEvent(payload, source);
|
|
437
497
|
};
|
|
438
|
-
|
|
498
|
+
}
|
|
499
|
+
createLegacyNotificationHandler(handleEvent, dryRun) {
|
|
500
|
+
return async (notification, source) => {
|
|
439
501
|
try {
|
|
440
502
|
const messages = this.service.notification.handleNotification(notification, source, dryRun);
|
|
441
503
|
for await (const message of messages) {
|
|
@@ -443,58 +505,153 @@ class Account extends commons_1.TypedEventEmitter {
|
|
|
443
505
|
}
|
|
444
506
|
}
|
|
445
507
|
catch (error) {
|
|
446
|
-
this.logger.error(`Failed to handle notification
|
|
508
|
+
this.logger.error(`Failed to handle legacy notification "${notification.id}": ${error.message}`, error);
|
|
447
509
|
}
|
|
448
510
|
};
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
511
|
+
}
|
|
512
|
+
createNotificationHandler(handleEvent, onNotificationStreamProgress, onConnectionStateChanged, dryRun) {
|
|
513
|
+
return async (notification, source) => {
|
|
514
|
+
try {
|
|
515
|
+
if (notification.type === ConsumableNotification_1.ConsumableEvent.MISSED) {
|
|
516
|
+
this.reactToMissedNotification();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (notification.type === ConsumableNotification_1.ConsumableEvent.MESSAGE_COUNT) {
|
|
520
|
+
const { data: { count }, } = notification;
|
|
521
|
+
onNotificationStreamProgress({ total: count, done: 0 });
|
|
522
|
+
this.totalPendingNotifications = count;
|
|
523
|
+
this.remainingNotifications = 0;
|
|
524
|
+
this.apiClient.transport.ws.acknowledgeMessageCountNotification();
|
|
525
|
+
(0, messageSender_1.pauseMessageSending)();
|
|
526
|
+
onConnectionStateChanged(ConnectionState.PROCESSING_NOTIFICATIONS);
|
|
527
|
+
if (count === 0) {
|
|
528
|
+
(0, messageSender_1.resumeMessageSending)();
|
|
529
|
+
onConnectionStateChanged(ConnectionState.LIVE);
|
|
530
|
+
}
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const event = notification.data.event;
|
|
534
|
+
const messages = this.service.notification.handleNotification(event, source, dryRun);
|
|
535
|
+
if (this.connectionState !== ConnectionState.LIVE) {
|
|
536
|
+
this.remainingNotifications++;
|
|
537
|
+
onNotificationStreamProgress({
|
|
538
|
+
done: this.remainingNotifications,
|
|
539
|
+
total: this.totalPendingNotifications,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
this.apiClient.transport.ws.acknowledgeNotification(notification);
|
|
543
|
+
for await (const message of messages) {
|
|
544
|
+
await handleEvent(message, source);
|
|
545
|
+
}
|
|
546
|
+
if (this.totalPendingNotifications === this.remainingNotifications &&
|
|
547
|
+
this.connectionState !== ConnectionState.LIVE) {
|
|
548
|
+
(0, messageSender_1.resumeMessageSending)();
|
|
549
|
+
onConnectionStateChanged(ConnectionState.LIVE);
|
|
550
|
+
}
|
|
459
551
|
}
|
|
460
|
-
|
|
461
|
-
|
|
552
|
+
catch (error) {
|
|
553
|
+
this.logger.error(`Failed to handle consumable notification "${notification.type}": ${error.message}`, error);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Returns a function to handle missed notifications — i.e., when the backend indicates
|
|
559
|
+
* that some notifications were lost due to age (typically >28 days).
|
|
560
|
+
* Also handles MLS-specific epoch mismatch recovery by triggering a conversation rejoin.
|
|
561
|
+
*/
|
|
562
|
+
createLegacyMissedNotificationsHandler(onMissedNotifications) {
|
|
563
|
+
return async (notificationId) => {
|
|
462
564
|
if (this.hasMLSDevice) {
|
|
463
565
|
(0, conversationRejoinQueue_1.queueConversationRejoin)('all-conversations', () => this.service.conversation.handleConversationsEpochMismatch());
|
|
464
566
|
}
|
|
465
567
|
return onMissedNotifications(notificationId);
|
|
466
568
|
};
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Returns a processor function for the notification stream (legacy sync).
|
|
572
|
+
* It pauses message sending and MLS rejoining during stream handling to prevent race conditions,
|
|
573
|
+
* then resumes normal operations after sync is complete.
|
|
574
|
+
*
|
|
575
|
+
* @param handlers Various logic handlers wired to notification callbacks
|
|
576
|
+
*/
|
|
577
|
+
createLegacyNotificationStreamProcessor({ handleLegacyNotification, handleMissedNotifications, onNotificationStreamProgress, onConnectionStateChanged, }) {
|
|
578
|
+
return async () => {
|
|
470
579
|
(0, messageSender_1.pauseMessageSending)();
|
|
471
580
|
// We want to avoid triggering rejoins of out-of-sync MLS conversations while we are processing the notification stream
|
|
472
581
|
(0, conversationRejoinQueue_1.pauseRejoiningMLSConversations)();
|
|
473
582
|
onConnectionStateChanged(ConnectionState.PROCESSING_NOTIFICATIONS);
|
|
474
583
|
const results = await this.service.notification.processNotificationStream(async (notification, source, progress) => {
|
|
475
|
-
await
|
|
584
|
+
await handleLegacyNotification(notification, source);
|
|
476
585
|
onNotificationStreamProgress(progress);
|
|
477
|
-
}, handleMissedNotifications
|
|
478
|
-
this.logger.info('Finished processing notifications', results);
|
|
479
|
-
if (abortHandler.signal.aborted) {
|
|
480
|
-
this.logger.warn('Ending connection process as websocket was closed');
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
onConnectionStateChanged(ConnectionState.LIVE);
|
|
484
|
-
// We can now unlock the websocket and let the new messages being handled and decrypted
|
|
485
|
-
this.apiClient.transport.ws.unlock();
|
|
586
|
+
}, handleMissedNotifications);
|
|
587
|
+
this.logger.info('Finished processing notifications from the legacy endpoint', results);
|
|
486
588
|
// We need to wait for the notification stream to be fully handled before releasing the message sending queue.
|
|
487
589
|
// 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
|
|
488
590
|
this.logger.info(`Resuming message sending. ${(0, messageSender_1.getQueueLength)()} messages to be sent`);
|
|
489
591
|
(0, messageSender_1.resumeMessageSending)();
|
|
490
592
|
(0, conversationRejoinQueue_1.resumeRejoiningMLSConversations)();
|
|
593
|
+
onConnectionStateChanged(ConnectionState.LIVE);
|
|
491
594
|
};
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Sets up WebSocket event listeners for:
|
|
598
|
+
* - Incoming backend messages
|
|
599
|
+
* - WebSocket state changes
|
|
600
|
+
* On each new backend message, we pass it to the notification handler.
|
|
601
|
+
* On state changes, we map raw socket states to public connection states and emit them.
|
|
602
|
+
*/
|
|
603
|
+
setupWebSocketListeners(handleNotification, onConnectionStateChanged) {
|
|
604
|
+
this.apiClient.transport.ws.removeAllListeners(tcp_1.WebSocketClient.TOPIC.ON_MESSAGE);
|
|
605
|
+
this.apiClient.transport.ws.on(tcp_1.WebSocketClient.TOPIC.ON_MESSAGE, notification => handleNotification(notification, notification_1.NotificationSource.WEBSOCKET));
|
|
606
|
+
this.apiClient.transport.ws.on(tcp_1.WebSocketClient.TOPIC.ON_STATE_CHANGE, wsState => {
|
|
607
|
+
const mapping = {
|
|
608
|
+
[ReconnectingWebsocket_1.WEBSOCKET_STATE.CLOSED]: ConnectionState.CLOSED,
|
|
609
|
+
[ReconnectingWebsocket_1.WEBSOCKET_STATE.CONNECTING]: ConnectionState.CONNECTING,
|
|
610
|
+
};
|
|
611
|
+
const connectionState = mapping[wsState];
|
|
612
|
+
if (connectionState) {
|
|
613
|
+
onConnectionStateChanged(connectionState);
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Handles logic for reacting to a missed notification event.
|
|
619
|
+
*
|
|
620
|
+
* The backend sends a special "missed notification" signal if it detects
|
|
621
|
+
* that the client has missed one or more notifications. Once this signal is sent,
|
|
622
|
+
* the backend will **stop sending all further notifications** until the client
|
|
623
|
+
* acknowledges the missed state.
|
|
624
|
+
*
|
|
625
|
+
* Because our app currently lacks functionality to perform a full real-time sync
|
|
626
|
+
* while running, we must reload the application to re-fetch the entire state.
|
|
627
|
+
*
|
|
628
|
+
* On first detection of the missed notification:
|
|
629
|
+
* - We set a local storage flag (`has_missing_notification`) to mark that we've
|
|
630
|
+
* entered this state.
|
|
631
|
+
* - We reload the application so the state can be re-fetched from scratch.
|
|
632
|
+
*
|
|
633
|
+
* On the next load:
|
|
634
|
+
* - If the flag is already present, we acknowledge the missed notification via
|
|
635
|
+
* the WebSocket transport, unblocking the backend so it resumes sending updates
|
|
636
|
+
* then we remove the flag.
|
|
637
|
+
*/
|
|
638
|
+
reactToMissedNotification() {
|
|
639
|
+
const localStorageKey = 'has_missing_notification';
|
|
640
|
+
// First-time handling: set flag and reload to trigger full re-fetch of state.
|
|
641
|
+
if (!exports.AccountLocalStorageStore.has(localStorageKey)) {
|
|
642
|
+
exports.AccountLocalStorageStore.add(localStorageKey, 'true');
|
|
643
|
+
window.location.reload();
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
// After reload: acknowledge the missed notification so backend resumes notifications.
|
|
647
|
+
this.apiClient.transport.ws.acknowledgeMissedNotification();
|
|
648
|
+
exports.AccountLocalStorageStore.remove(localStorageKey);
|
|
649
|
+
}
|
|
650
|
+
getClientCapabilities() {
|
|
651
|
+
return this.currentClient?.capabilities || [];
|
|
652
|
+
}
|
|
653
|
+
checkIsConsumable(notification) {
|
|
654
|
+
return 'type' in notification;
|
|
498
655
|
}
|
|
499
656
|
generateDbName(context) {
|
|
500
657
|
const clientType = context.clientType === client_1.ClientType.NONE ? '' : `@${context.clientType}`;
|
package/lib/Account.test.js
CHANGED
|
@@ -44,11 +44,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
44
44
|
const auth_1 = require("@wireapp/api-client/lib/auth");
|
|
45
45
|
const client_1 = require("@wireapp/api-client/lib/client");
|
|
46
46
|
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
47
|
-
const event_1 = require("@wireapp/api-client/lib/event");
|
|
48
47
|
const http_1 = require("@wireapp/api-client/lib/http");
|
|
49
48
|
const notification_1 = require("@wireapp/api-client/lib/notification");
|
|
49
|
+
const ConsumableNotification_1 = require("@wireapp/api-client/lib/notification/ConsumableNotification");
|
|
50
50
|
const self_1 = require("@wireapp/api-client/lib/self");
|
|
51
|
-
const tcp_1 = require("@wireapp/api-client/lib/tcp");
|
|
52
51
|
const ReconnectingWebsocket_1 = require("@wireapp/api-client/lib/tcp/ReconnectingWebsocket");
|
|
53
52
|
const http_status_codes_1 = require("http-status-codes");
|
|
54
53
|
const jest_websocket_mock_1 = require("jest-websocket-mock");
|
|
@@ -105,6 +104,8 @@ describe('Account', () => {
|
|
|
105
104
|
token_type: 'Bearer',
|
|
106
105
|
user: 'aaf9a833-ef30-4c22-86a0-9adc8a15b3b4',
|
|
107
106
|
};
|
|
107
|
+
const BACKEND_VERSION = 8;
|
|
108
|
+
const websocketServerAddress = `${MOCK_BACKEND.ws}/v${BACKEND_VERSION}/events?access_token=${accessTokenData.access_token}`;
|
|
108
109
|
beforeEach(() => {
|
|
109
110
|
(0, nock_1.default)(MOCK_BACKEND.rest)
|
|
110
111
|
.post(auth_1.AuthAPI.URL.LOGIN, body => body.email && body.password)
|
|
@@ -134,6 +135,14 @@ describe('Account', () => {
|
|
|
134
135
|
.get(`${notification_1.NotificationAPI.URL.NOTIFICATION}/${notification_1.NotificationAPI.URL.LAST}`)
|
|
135
136
|
.query({ client: CLIENT_ID })
|
|
136
137
|
.reply(http_status_codes_1.StatusCodes.OK, {});
|
|
138
|
+
(0, nock_1.default)(MOCK_BACKEND.rest)
|
|
139
|
+
.get(`/api-version`)
|
|
140
|
+
.reply(http_status_codes_1.StatusCodes.OK, {
|
|
141
|
+
supported: [BACKEND_VERSION],
|
|
142
|
+
federation: false,
|
|
143
|
+
development: [BACKEND_VERSION + 1],
|
|
144
|
+
domain: 'zinfra.io',
|
|
145
|
+
});
|
|
137
146
|
(0, nock_1.default)(MOCK_BACKEND.rest)
|
|
138
147
|
.get(notification_1.NotificationAPI.URL.NOTIFICATION)
|
|
139
148
|
.query({ client: CLIENT_ID, size: 10000 })
|
|
@@ -142,6 +151,11 @@ describe('Account', () => {
|
|
|
142
151
|
(0, nock_1.default)(MOCK_BACKEND.rest)
|
|
143
152
|
.get(client_1.ClientAPI.URL.CLIENTS)
|
|
144
153
|
.reply(http_status_codes_1.StatusCodes.OK, [{ id: CLIENT_ID }]);
|
|
154
|
+
(0, nock_1.default)(MOCK_BACKEND.rest)
|
|
155
|
+
.put(/\/clients\/[\w-]+$/, {
|
|
156
|
+
capabilities: ['legalhold-implicit-consent', 'consumable-notifications'],
|
|
157
|
+
})
|
|
158
|
+
.reply(http_status_codes_1.StatusCodes.OK);
|
|
145
159
|
(0, nock_1.default)(MOCK_BACKEND.rest)
|
|
146
160
|
.get(self_1.SelfAPI.URL.SELF)
|
|
147
161
|
.reply(http_status_codes_1.StatusCodes.OK, {
|
|
@@ -163,6 +177,7 @@ describe('Account', () => {
|
|
|
163
177
|
(0, nock_1.cleanAll)();
|
|
164
178
|
});
|
|
165
179
|
const currentClient = {
|
|
180
|
+
capabilities: [],
|
|
166
181
|
id: CLIENT_ID,
|
|
167
182
|
cookie: '',
|
|
168
183
|
time: '',
|
|
@@ -218,35 +233,9 @@ describe('Account', () => {
|
|
|
218
233
|
}
|
|
219
234
|
});
|
|
220
235
|
});
|
|
221
|
-
it('emits text messages', () => {
|
|
222
|
-
return new Promise(async (resolve) => {
|
|
223
|
-
const { account, apiClient } = await createAccount();
|
|
224
|
-
await account.login({
|
|
225
|
-
clientType: client_1.ClientType.TEMPORARY,
|
|
226
|
-
email: 'hello@example.com',
|
|
227
|
-
password: 'my-secret',
|
|
228
|
-
});
|
|
229
|
-
account['currentClient'] = currentClient;
|
|
230
|
-
jest.spyOn(apiClient, 'connect').mockImplementation();
|
|
231
|
-
jest.spyOn(account.service.notification, 'handleEvent').mockReturnValue({
|
|
232
|
-
status: 'handled',
|
|
233
|
-
payload: {
|
|
234
|
-
event: { type: event_1.CONVERSATION_EVENT.OTR_MESSAGE_ADD },
|
|
235
|
-
},
|
|
236
|
-
});
|
|
237
|
-
const kill = account.listen({
|
|
238
|
-
onEvent: ({ event }) => {
|
|
239
|
-
expect(event.type).toBe(event_1.CONVERSATION_EVENT.OTR_MESSAGE_ADD);
|
|
240
|
-
resolve();
|
|
241
|
-
},
|
|
242
|
-
});
|
|
243
|
-
apiClient.transport.ws.emit(tcp_1.WebSocketClient.TOPIC.ON_MESSAGE, { payload: [{}] });
|
|
244
|
-
kill();
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
236
|
describe('Websocket connection', () => {
|
|
248
|
-
let server;
|
|
249
237
|
let dependencies;
|
|
238
|
+
let server;
|
|
250
239
|
const mockNotifications = (size) => {
|
|
251
240
|
const notifications = Array.from(new Array(size)).map(() => ({
|
|
252
241
|
id: (0, uuid_1.v4)(),
|
|
@@ -267,12 +256,12 @@ describe('Account', () => {
|
|
|
267
256
|
return callback();
|
|
268
257
|
};
|
|
269
258
|
};
|
|
270
|
-
|
|
271
|
-
server = new jest_websocket_mock_1.WS(`${MOCK_BACKEND.ws}/await?access_token=${accessTokenData.access_token}`);
|
|
259
|
+
beforeAll(() => {
|
|
272
260
|
// Forces the reconnecting websocket not to automatically reconnect (to avoid infinitely hanging tests)
|
|
273
261
|
ReconnectingWebsocket_1.ReconnectingWebsocket['RECONNECTING_OPTIONS'].maxRetries = 0;
|
|
274
262
|
});
|
|
275
263
|
beforeEach(async () => {
|
|
264
|
+
server = new jest_websocket_mock_1.WS(websocketServerAddress); // isolate per test
|
|
276
265
|
dependencies = await createAccount();
|
|
277
266
|
const { account } = dependencies;
|
|
278
267
|
await account.login({
|
|
@@ -287,46 +276,40 @@ describe('Account', () => {
|
|
|
287
276
|
jest
|
|
288
277
|
.spyOn(dependencies.account.service.notification['database'], 'getLastNotificationId')
|
|
289
278
|
.mockResolvedValue('0');
|
|
279
|
+
await account.useAPIVersion(BACKEND_VERSION, BACKEND_VERSION);
|
|
290
280
|
});
|
|
291
281
|
afterEach(() => {
|
|
292
|
-
server.close();
|
|
282
|
+
server.close(); // ensure server shutdown
|
|
283
|
+
jest_websocket_mock_1.WS.clean();
|
|
293
284
|
});
|
|
294
285
|
describe('listen', () => {
|
|
295
|
-
it('
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
Account_1.ConnectionState.PROCESSING_NOTIFICATIONS,
|
|
300
|
-
Account_1.ConnectionState.LIVE,
|
|
301
|
-
Account_1.ConnectionState.CLOSED,
|
|
302
|
-
];
|
|
303
|
-
const disconnect = dependencies.account.listen({
|
|
304
|
-
onConnectionStateChanged: state => {
|
|
305
|
-
expect(state).toBe(expectedConnectionStates.splice(0, 1)[0]);
|
|
306
|
-
switch (state) {
|
|
307
|
-
case Account_1.ConnectionState.LIVE:
|
|
308
|
-
// We socket is live we disconnect before ending the test
|
|
309
|
-
disconnect();
|
|
310
|
-
break;
|
|
311
|
-
case Account_1.ConnectionState.CLOSED:
|
|
312
|
-
resolve();
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
it('processes notification stream upon connection', async () => {
|
|
286
|
+
it('connects to websocket after the notification stream has been processed', async () => {
|
|
287
|
+
jest
|
|
288
|
+
.spyOn(dependencies.account, 'getClientCapabilities')
|
|
289
|
+
.mockReturnValue([client_1.ClientCapability.LEGAL_HOLD_IMPLICIT_CONSENT]);
|
|
319
290
|
return new Promise(async (resolve) => {
|
|
320
291
|
const nbNotifications = 10;
|
|
321
292
|
const onNotificationStreamProgress = jest.fn();
|
|
322
|
-
const onEvent = jest.fn();
|
|
293
|
+
const onEvent = jest.fn().mockImplementation(() => { });
|
|
323
294
|
mockNotifications(nbNotifications);
|
|
324
|
-
|
|
325
|
-
onConnectionStateChanged: callWhen(Account_1.ConnectionState.LIVE, () => {
|
|
295
|
+
await dependencies.account.listen({
|
|
296
|
+
onConnectionStateChanged: callWhen(Account_1.ConnectionState.LIVE, async () => {
|
|
326
297
|
expect(onNotificationStreamProgress).toHaveBeenCalledTimes(nbNotifications);
|
|
327
298
|
expect(onEvent).toHaveBeenCalledTimes(nbNotifications);
|
|
328
299
|
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
329
|
-
|
|
300
|
+
expect(onEvent).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
301
|
+
onEvent.mockReset();
|
|
302
|
+
await server.connected;
|
|
303
|
+
jest
|
|
304
|
+
.spyOn(dependencies.account.service.notification, 'handleNotification')
|
|
305
|
+
.mockReturnValue([{ event: { testData: 1 } }]);
|
|
306
|
+
server.send(JSON.stringify({
|
|
307
|
+
type: ConsumableNotification_1.ConsumableEvent.EVENT,
|
|
308
|
+
data: { delivery_tag: 1000, event: { id: (0, uuid_1.v4)(), payload: [] } },
|
|
309
|
+
}));
|
|
310
|
+
await waitFor(() => expect(onEvent).toHaveBeenCalledTimes(1));
|
|
311
|
+
expect(onEvent).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
312
|
+
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
330
313
|
resolve();
|
|
331
314
|
}),
|
|
332
315
|
onEvent: onEvent,
|
|
@@ -334,24 +317,39 @@ describe('Account', () => {
|
|
|
334
317
|
});
|
|
335
318
|
});
|
|
336
319
|
});
|
|
337
|
-
it('
|
|
320
|
+
it('warns consumer of the connection state', async () => {
|
|
321
|
+
await new Promise(async (resolve) => {
|
|
322
|
+
mockNotifications(10);
|
|
323
|
+
const onConnectionStateChanged = jest.fn().mockImplementation((state) => {
|
|
324
|
+
switch (state) {
|
|
325
|
+
case Account_1.ConnectionState.LIVE:
|
|
326
|
+
break;
|
|
327
|
+
case Account_1.ConnectionState.CLOSED:
|
|
328
|
+
// Expect all states to have been called in order
|
|
329
|
+
expect(onConnectionStateChanged).toHaveBeenNthCalledWith(1, Account_1.ConnectionState.PROCESSING_NOTIFICATIONS);
|
|
330
|
+
expect(onConnectionStateChanged).toHaveBeenNthCalledWith(2, Account_1.ConnectionState.LIVE);
|
|
331
|
+
resolve();
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
const disconnect = await dependencies.account.listen({
|
|
336
|
+
onConnectionStateChanged,
|
|
337
|
+
});
|
|
338
|
+
await waitFor(() => expect(onConnectionStateChanged).toHaveBeenCalledWith(Account_1.ConnectionState.PROCESSING_NOTIFICATIONS));
|
|
339
|
+
disconnect();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
it('processes notification stream upon connection', async () => {
|
|
338
343
|
return new Promise(async (resolve) => {
|
|
339
344
|
const nbNotifications = 10;
|
|
340
345
|
const onNotificationStreamProgress = jest.fn();
|
|
341
346
|
const onEvent = jest.fn();
|
|
342
347
|
mockNotifications(nbNotifications);
|
|
343
|
-
|
|
344
|
-
onConnectionStateChanged: callWhen(Account_1.ConnectionState.LIVE,
|
|
348
|
+
await dependencies.account.listen({
|
|
349
|
+
onConnectionStateChanged: callWhen(Account_1.ConnectionState.LIVE, () => {
|
|
345
350
|
expect(onNotificationStreamProgress).toHaveBeenCalledTimes(nbNotifications);
|
|
346
351
|
expect(onEvent).toHaveBeenCalledTimes(nbNotifications);
|
|
347
352
|
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
348
|
-
expect(onEvent).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
349
|
-
onEvent.mockReset();
|
|
350
|
-
server.send(JSON.stringify({ id: (0, uuid_1.v4)(), payload: [{}] }));
|
|
351
|
-
await waitFor(() => expect(onEvent).toHaveBeenCalledTimes(1));
|
|
352
|
-
expect(onEvent).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
353
|
-
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
354
|
-
disconnect();
|
|
355
353
|
resolve();
|
|
356
354
|
}),
|
|
357
355
|
onEvent: onEvent,
|
|
@@ -359,61 +357,27 @@ describe('Account', () => {
|
|
|
359
357
|
});
|
|
360
358
|
});
|
|
361
359
|
});
|
|
362
|
-
it('
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
mockNotifications(nbNotifications);
|
|
367
|
-
return new Promise(async (resolve) => {
|
|
368
|
-
const disconnect = dependencies.account.listen({
|
|
369
|
-
onConnectionStateChanged: async (state) => {
|
|
370
|
-
switch (state) {
|
|
371
|
-
case Account_1.ConnectionState.PROCESSING_NOTIFICATIONS:
|
|
372
|
-
// sending a message as soon as the notificaiton stream starts to process
|
|
373
|
-
// This message should only be forwarded once the notification stream is fully processed
|
|
374
|
-
server.send(JSON.stringify({ id: (0, uuid_1.v4)(), payload: [{}] }));
|
|
375
|
-
break;
|
|
376
|
-
case Account_1.ConnectionState.LIVE:
|
|
377
|
-
expect(onNotificationStreamProgress).toHaveBeenCalledTimes(nbNotifications);
|
|
378
|
-
expect(onEvent).toHaveBeenCalledTimes(nbNotifications);
|
|
379
|
-
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
380
|
-
expect(onEvent).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
381
|
-
onEvent.mockReset();
|
|
382
|
-
await waitFor(() => expect(onEvent).toHaveBeenCalledTimes(1));
|
|
383
|
-
expect(onEvent).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
384
|
-
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
385
|
-
disconnect();
|
|
386
|
-
resolve();
|
|
387
|
-
}
|
|
388
|
-
},
|
|
389
|
-
onEvent: onEvent,
|
|
390
|
-
onNotificationStreamProgress: onNotificationStreamProgress,
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
});
|
|
394
|
-
it('does not unlock the websocket if the connection was aborted', async () => {
|
|
360
|
+
it('does not stop proccessing messages if websocket connection is aborted', async () => {
|
|
361
|
+
jest
|
|
362
|
+
.spyOn(dependencies.account, 'getClientCapabilities')
|
|
363
|
+
.mockReturnValue([client_1.ClientCapability.LEGAL_HOLD_IMPLICIT_CONSENT]);
|
|
395
364
|
const nbNotifications = 10;
|
|
396
365
|
const onNotificationStreamProgress = jest
|
|
397
366
|
.fn()
|
|
398
367
|
.mockImplementationOnce(() => { })
|
|
399
|
-
.mockImplementationOnce(() =>
|
|
368
|
+
.mockImplementationOnce(() => {
|
|
369
|
+
// abort websocket connection after the second notification is processeed
|
|
370
|
+
server.close();
|
|
371
|
+
});
|
|
400
372
|
const onEvent = jest.fn();
|
|
401
373
|
mockNotifications(nbNotifications);
|
|
402
|
-
return new Promise(async (resolve
|
|
403
|
-
dependencies.account.listen({
|
|
374
|
+
return new Promise(async (resolve) => {
|
|
375
|
+
return dependencies.account.listen({
|
|
404
376
|
onConnectionStateChanged: async (state) => {
|
|
405
377
|
switch (state) {
|
|
406
|
-
case Account_1.ConnectionState.PROCESSING_NOTIFICATIONS:
|
|
407
|
-
// sending a message as soon as the notificaiton stream starts to process
|
|
408
|
-
// This message should only be forwarded once the notification stream is fully processed
|
|
409
|
-
server.send(JSON.stringify({ id: (0, uuid_1.v4)(), payload: [{}] }));
|
|
410
|
-
break;
|
|
411
378
|
case Account_1.ConnectionState.LIVE:
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
case Account_1.ConnectionState.CLOSED:
|
|
415
|
-
expect(onNotificationStreamProgress).toHaveBeenCalledTimes(2);
|
|
416
|
-
expect(onEvent).toHaveBeenCalledTimes(2);
|
|
379
|
+
expect(onNotificationStreamProgress).toHaveBeenCalledTimes(10);
|
|
380
|
+
expect(onEvent).toHaveBeenCalledTimes(10);
|
|
417
381
|
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
418
382
|
expect(dependencies.account.service.notification.handleNotification).not.toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.WEBSOCKET);
|
|
419
383
|
resolve();
|
|
@@ -424,35 +388,6 @@ describe('Account', () => {
|
|
|
424
388
|
});
|
|
425
389
|
});
|
|
426
390
|
});
|
|
427
|
-
it('cancels notification stream process if socket is disconnected', () => {
|
|
428
|
-
const nbNotifications = 10;
|
|
429
|
-
const onNotificationStreamProgress = jest.fn();
|
|
430
|
-
const onEvent = jest
|
|
431
|
-
.fn()
|
|
432
|
-
.mockImplementationOnce(() => { })
|
|
433
|
-
.mockImplementationOnce(() => {
|
|
434
|
-
// on second message, we kill the websocket
|
|
435
|
-
server.close();
|
|
436
|
-
});
|
|
437
|
-
mockNotifications(nbNotifications);
|
|
438
|
-
return new Promise(resolve => {
|
|
439
|
-
dependencies.account.listen({
|
|
440
|
-
onConnectionStateChanged: callWhen(Account_1.ConnectionState.CLOSED, () => {
|
|
441
|
-
try {
|
|
442
|
-
expect(onNotificationStreamProgress).toHaveBeenCalledTimes(1);
|
|
443
|
-
expect(onEvent).toHaveBeenCalledTimes(2);
|
|
444
|
-
expect(onEvent).toHaveBeenCalledWith(expect.any(Object), notification_2.NotificationSource.NOTIFICATION_STREAM);
|
|
445
|
-
}
|
|
446
|
-
catch (error) {
|
|
447
|
-
throw error;
|
|
448
|
-
}
|
|
449
|
-
resolve();
|
|
450
|
-
}, 1),
|
|
451
|
-
onEvent: onEvent,
|
|
452
|
-
onNotificationStreamProgress: onNotificationStreamProgress,
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
391
|
});
|
|
457
392
|
});
|
|
458
393
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LoginData } from '@wireapp/api-client/lib/auth/';
|
|
2
|
-
import { RegisteredClient } from '@wireapp/api-client/lib/client/';
|
|
2
|
+
import { ClientCapabilityData, RegisteredClient } from '@wireapp/api-client/lib/client/';
|
|
3
3
|
import { APIClient } from '@wireapp/api-client';
|
|
4
4
|
import { CRUDEngine } from '@wireapp/store-engine';
|
|
5
5
|
import type { ProteusService } from '../messagingProtocols/proteus';
|
|
@@ -45,8 +45,9 @@ export declare class ClientService {
|
|
|
45
45
|
*
|
|
46
46
|
* @return the loaded client or undefined
|
|
47
47
|
*/
|
|
48
|
-
loadClient(): Promise<
|
|
48
|
+
loadClient(): Promise<MetaClient | undefined>;
|
|
49
49
|
private createLocalClient;
|
|
50
|
+
putClientCapabilities(clientId: string, capabilities: ClientCapabilityData): Promise<void>;
|
|
50
51
|
/**
|
|
51
52
|
* Will download all the clients of the self user (excluding the current client) and will store them in the database
|
|
52
53
|
* @param currentClient - the id of the current client (to be excluded from the list)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientService.d.ts","sourceRoot":"","sources":["../../src/client/ClientService.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,SAAS,EAAC,MAAM,+BAA+B,CAAC;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"ClientService.d.ts","sourceRoot":"","sources":["../../src/client/ClientService.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,SAAS,EAAC,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAEL,oBAAoB,EAGpB,gBAAgB,EACjB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAE9C,OAAO,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAEjD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAC,cAAc,EAAC,MAAM,2DAA2D,CAAC;AAEzF,OAAO,EAAC,UAAU,EAAoD,MAAM,IAAI,CAAC;AAEjF,MAAM,WAAW,UAAW,SAAQ,gBAAgB;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,qBAAa,aAAa;IAMtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAP9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgD;gBAGpD,SAAS,EAAE,SAAS,EACpB,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE,UAAU;IAMnC,UAAU,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAIhD;;;;;;;OAOG;IACU,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMhF;;;OAGG;IACU,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAcpD,cAAc;IAQ5B;;;;;;;OAOG;IACU,UAAU,IAAI,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAyB1D,OAAO,CAAC,iBAAiB;IAIlB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjG;;;OAGG;IACU,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAUhE,QAAQ,CACnB,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,EAAC,OAAO,EAAE,UAAU,EAAC,EAAE,cAAc,GACpC,OAAO,CAAC,gBAAgB,CAAC;CA6B7B"}
|
|
@@ -118,6 +118,9 @@ class ClientService {
|
|
|
118
118
|
createLocalClient(client, domain) {
|
|
119
119
|
return this.database.createLocalClient(client, domain);
|
|
120
120
|
}
|
|
121
|
+
putClientCapabilities(clientId, capabilities) {
|
|
122
|
+
return this.backend.putClient(clientId, capabilities);
|
|
123
|
+
}
|
|
121
124
|
/**
|
|
122
125
|
* Will download all the clients of the self user (excluding the current client) and will store them in the database
|
|
123
126
|
* @param currentClient - the id of the current client (to be excluded from the list)
|
|
@@ -137,7 +140,7 @@ class ClientService {
|
|
|
137
140
|
}
|
|
138
141
|
const newClient = {
|
|
139
142
|
class: clientInfo.classification,
|
|
140
|
-
capabilities: [client_1.ClientCapability.LEGAL_HOLD_IMPLICIT_CONSENT],
|
|
143
|
+
capabilities: [client_1.ClientCapability.LEGAL_HOLD_IMPLICIT_CONSENT, client_1.ClientCapability.CONSUMABLE_NOTIFICATIONS],
|
|
141
144
|
cookie: clientInfo.cookieLabel,
|
|
142
145
|
label: clientInfo.label,
|
|
143
146
|
lastkey: lastPrekey,
|
|
@@ -3,7 +3,7 @@ import { APIClient } from '@wireapp/api-client';
|
|
|
3
3
|
export declare class NotificationBackendRepository {
|
|
4
4
|
private readonly apiClient;
|
|
5
5
|
constructor(apiClient: APIClient);
|
|
6
|
-
getAllNotifications(clientId?: string, lastNotificationId?: string
|
|
6
|
+
getAllNotifications(clientId?: string, lastNotificationId?: string): Promise<{
|
|
7
7
|
notifications: Notification[];
|
|
8
8
|
missedNotification?: string;
|
|
9
9
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationBackendRepository.d.ts","sourceRoot":"","sources":["../../src/notification/NotificationBackendRepository.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,YAAY,EAAC,MAAM,uCAAuC,CAAC;AAEnE,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAE9C,qBAAa,6BAA6B;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAEpC,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM
|
|
1
|
+
{"version":3,"file":"NotificationBackendRepository.d.ts","sourceRoot":"","sources":["../../src/notification/NotificationBackendRepository.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,YAAY,EAAC,MAAM,uCAAuC,CAAC;AAEnE,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAE9C,qBAAa,6BAA6B;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,SAAS;IAEpC,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM;;;;IAIxE,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;CAGrE"}
|
|
@@ -24,8 +24,8 @@ class NotificationBackendRepository {
|
|
|
24
24
|
constructor(apiClient) {
|
|
25
25
|
this.apiClient = apiClient;
|
|
26
26
|
}
|
|
27
|
-
async getAllNotifications(clientId, lastNotificationId
|
|
28
|
-
return this.apiClient.api.notification.getAllNotifications(clientId, lastNotificationId
|
|
27
|
+
async getAllNotifications(clientId, lastNotificationId) {
|
|
28
|
+
return this.apiClient.api.notification.getAllNotifications(clientId, lastNotificationId);
|
|
29
29
|
}
|
|
30
30
|
getLastNotification(clientId) {
|
|
31
31
|
return this.apiClient.api.notification.getLastNotification(clientId);
|
|
@@ -55,7 +55,7 @@ export declare class NotificationService extends TypedEventEmitter<Events> {
|
|
|
55
55
|
getNotificationEventList(): Promise<BackendEvent[]>;
|
|
56
56
|
setLastEventDate(eventDate: Date): Promise<Date>;
|
|
57
57
|
private setLastNotificationId;
|
|
58
|
-
processNotificationStream(notificationHandler: NotificationHandler, onMissedNotifications: (notificationId: string) => void
|
|
58
|
+
processNotificationStream(notificationHandler: NotificationHandler, onMissedNotifications: (notificationId: string) => void): Promise<{
|
|
59
59
|
total: number;
|
|
60
60
|
error: number;
|
|
61
61
|
success: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationService.d.ts","sourceRoot":"","sources":["../../src/notification/NotificationService.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,YAAY,EAAC,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAC,MAAM,uCAAuC,CAAC;AAEnE,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAa,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,UAAU,EAA4B,MAAM,uBAAuB,CAAC;AAI5E,OAAO,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAY,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAE1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,0CAA0C;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,kEAAkE;IAClE,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAC,MAAM,EAAE,WAAW,CAAA;CAAC,GACrB;IAAC,MAAM,EAAE,SAAS,CAAA;CAAC,GACnB;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;CAAC,CAAC;AAE7D,aAAK,KAAK;IACR,kBAAkB,iDAAiD;CACpE;AAED,MAAM,MAAM,mBAAmB,GAAG,CAChC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,KACpC,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,KAAK,MAAM,GAAG;IACZ,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,iBAAiB,CAAC;CAC/C,CAAC;AAEF,qBAAa,mBAAoB,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IAU9D,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IATtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6D;IACpF,gBAAuB,KAAK,eAAS;gBAGnC,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,UAAU,EACN,mBAAmB,EAAE,mBAAmB;YAQ7C,mBAAmB;IAKjC,0DAA0D;IAC7C,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/D,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAKpC,wBAAwB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI7C,gBAAgB,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YAsB/C,qBAAqB;IAItB,yBAAyB,CACpC,mBAAmB,EAAE,mBAAmB,EACxC,qBAAqB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"NotificationService.d.ts","sourceRoot":"","sources":["../../src/notification/NotificationService.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAC,YAAY,EAAC,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAC,YAAY,EAAC,MAAM,uCAAuC,CAAC;AAEnE,OAAO,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAa,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAC,cAAc,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAC,UAAU,EAA4B,MAAM,uBAAuB,CAAC;AAI5E,OAAO,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAC,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAY,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAE1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,0CAA0C;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,kEAAkE;IAClE,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAC,MAAM,EAAE,WAAW,CAAA;CAAC,GACrB;IAAC,MAAM,EAAE,SAAS,CAAA;CAAC,GACnB;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;CAAC,CAAC;AAE7D,aAAK,KAAK;IACR,kBAAkB,iDAAiD;CACpE;AAED,MAAM,MAAM,mBAAmB,GAAG,CAChC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,KACpC,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,KAAK,MAAM,GAAG;IACZ,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,iBAAiB,CAAC;CAC/C,CAAC;AAEF,qBAAa,mBAAoB,SAAQ,iBAAiB,CAAC,MAAM,CAAC;IAU9D,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IATtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6D;IACpF,gBAAuB,KAAK,eAAS;gBAGnC,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,UAAU,EACN,mBAAmB,EAAE,mBAAmB;YAQ7C,mBAAmB;IAKjC,0DAA0D;IAC7C,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM/D,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAKpC,wBAAwB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI7C,gBAAgB,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YAsB/C,qBAAqB;IAItB,yBAAyB,CACpC,mBAAmB,EAAE,mBAAmB,EACxC,qBAAqB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,GACtD,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,CAAC;IA6B3D;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAYT,kBAAkB,CAC9B,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,GAAE,OAAe,GACtB,cAAc,CAAC,mBAAmB,CAAC;IAmCtC;;;;;OAKG;YACW,WAAW;CAc1B"}
|
|
@@ -43,9 +43,9 @@ class NotificationService extends commons_1.TypedEventEmitter {
|
|
|
43
43
|
this.backend = new NotificationBackendRepository_1.NotificationBackendRepository(this.apiClient);
|
|
44
44
|
this.database = new NotificationDatabaseRepository_1.NotificationDatabaseRepository(storeEngine);
|
|
45
45
|
}
|
|
46
|
-
async getAllNotifications(since
|
|
46
|
+
async getAllNotifications(since) {
|
|
47
47
|
const clientId = this.apiClient.clientId;
|
|
48
|
-
return this.backend.getAllNotifications(clientId, since
|
|
48
|
+
return this.backend.getAllNotifications(clientId, since);
|
|
49
49
|
}
|
|
50
50
|
/** Should only be called with a completely new client. */
|
|
51
51
|
async initializeNotificationStream(clientId) {
|
|
@@ -80,9 +80,9 @@ class NotificationService extends commons_1.TypedEventEmitter {
|
|
|
80
80
|
async setLastNotificationId(lastNotification) {
|
|
81
81
|
return this.database.updateLastNotificationId(lastNotification);
|
|
82
82
|
}
|
|
83
|
-
async processNotificationStream(notificationHandler, onMissedNotifications
|
|
83
|
+
async processNotificationStream(notificationHandler, onMissedNotifications) {
|
|
84
84
|
const lastNotificationId = await this.database.getLastNotificationId();
|
|
85
|
-
const { notifications, missedNotification } = await this.getAllNotifications(lastNotificationId
|
|
85
|
+
const { notifications, missedNotification } = await this.getAllNotifications(lastNotificationId);
|
|
86
86
|
if (missedNotification) {
|
|
87
87
|
onMissedNotifications(missedNotification);
|
|
88
88
|
}
|
|
@@ -92,13 +92,6 @@ class NotificationService extends commons_1.TypedEventEmitter {
|
|
|
92
92
|
: `No notification to process from the stream`;
|
|
93
93
|
this.logger.log(logMessage);
|
|
94
94
|
for (const [index, notification] of notifications.entries()) {
|
|
95
|
-
if (abortHandler.signal.aborted) {
|
|
96
|
-
/* Stop handling notifications if the websocket has been disconnected.
|
|
97
|
-
* Upon reconnecting we are going to restart handling the notification stream for where we left of
|
|
98
|
-
*/
|
|
99
|
-
this.logger.warn(`Stop processing notifications as connection to websocket was closed`);
|
|
100
|
-
return results;
|
|
101
|
-
}
|
|
102
95
|
try {
|
|
103
96
|
await notificationHandler(notification, Notifications_types_1.NotificationSource.NOTIFICATION_STREAM, {
|
|
104
97
|
done: index + 1,
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"./lib/cryptography/AssetCryptography/crypto.node": "./lib/cryptography/AssetCryptography/crypto.browser.js"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@wireapp/api-client": "^27.
|
|
14
|
+
"@wireapp/api-client": "^27.67.0",
|
|
15
15
|
"@wireapp/commons": "^5.4.2",
|
|
16
16
|
"@wireapp/core-crypto": "7.0.1",
|
|
17
17
|
"@wireapp/cryptobox": "12.8.0",
|
|
@@ -61,6 +61,6 @@
|
|
|
61
61
|
"test:coverage": "jest --coverage",
|
|
62
62
|
"watch": "tsc --watch"
|
|
63
63
|
},
|
|
64
|
-
"version": "46.
|
|
65
|
-
"gitHead": "
|
|
64
|
+
"version": "46.30.0",
|
|
65
|
+
"gitHead": "09304f9cc2241bdd6200926e263adbfe97ca5d68"
|
|
66
66
|
}
|