@ethora/chat-component 26.2.2 → 26.2.5

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.
@@ -1,4 +1,8 @@
1
1
  import { User } from '../../types/types';
2
+ interface GetMyUserOptions {
3
+ token?: string;
4
+ endpoint?: string;
5
+ }
2
6
  export declare function getDocuments(walletAddress: string): Promise<import('axios').AxiosResponse<any, any>>;
3
7
  export declare function getExportMyData(): Promise<import('axios').AxiosResponse<any, any>>;
4
8
  export declare function deleteMe(): Promise<import('axios').AxiosResponse<any, any>>;
@@ -6,3 +10,5 @@ export declare function updateMe(data: any): Promise<import('axios').AxiosRespon
6
10
  export declare function updateProfile(fd: FormData): Promise<{
7
11
  user: User;
8
12
  }>;
13
+ export declare function getMyUser(options?: GetMyUserOptions): Promise<User>;
14
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { Client } from '@xmpp/client';
2
2
  import { Iso639_1Codes, XmppClientInterface, xmppSettingsInterface, IMessage } from '../types/types';
3
3
  import { AllRoomPresenceSummary } from './xmpp/allRoomPresences.xmpp';
4
+ type HistorySource = 'active' | 'send_ack' | 'background' | 'default';
4
5
  export declare class XmppClient implements XmppClientInterface {
5
6
  client: Client;
6
7
  devServer: string | undefined;
@@ -27,6 +28,20 @@ export declare class XmppClient implements XmppClientInterface {
27
28
  private recoveryRoomJid;
28
29
  private joinedRooms;
29
30
  private roomPresenceInFlight;
31
+ private historyPreloadInFlight;
32
+ private mamRequestRegistry;
33
+ private historyQueue;
34
+ private historyQueueInFlight;
35
+ private historyQueueInFlightHigh;
36
+ private historyQueueWorkerScheduled;
37
+ private roomHistoryEpoch;
38
+ private criticalSendUntil;
39
+ private activeRoomBoostUntil;
40
+ private sendStartedAtById;
41
+ private pendingSendIdsByRoom;
42
+ private maxInFlightHistory;
43
+ private softPauseAfterSendMs;
44
+ private activeRoomBoostTtlMs;
30
45
  checkOnline(): boolean;
31
46
  pingInterval: any;
32
47
  pingTimeout: any;
@@ -43,6 +58,8 @@ export declare class XmppClient implements XmppClientInterface {
43
58
  private maxOfflineReconnectAttempts;
44
59
  private reconnectBaseDelayMs;
45
60
  private pausedDueToOfflineCap;
61
+ private authFailureDetected;
62
+ private isTerminalAuthFailure;
46
63
  constructor(username: string, password: string, xmppSettings?: xmppSettingsInterface);
47
64
  initializeClient(): Promise<void>;
48
65
  disconnect(): Promise<void>;
@@ -56,6 +73,28 @@ export declare class XmppClient implements XmppClientInterface {
56
73
  ensureConnected(timeout?: number): Promise<void>;
57
74
  close(): Promise<void>;
58
75
  wrapWithConnectionCheck<T>(operation: () => Promise<T>): Promise<T>;
76
+ private clearMamRegistry;
77
+ private clearHistoryQueue;
78
+ private getRoomEpoch;
79
+ private getHistoryPriority;
80
+ private nextHistoryTaskId;
81
+ private scheduleHistoryQueue;
82
+ private pickNextHistoryTask;
83
+ private processHistoryQueue;
84
+ private executeHistoryTask;
85
+ private parseMamMessages;
86
+ private routeMamStanza;
87
+ private requestMamHistory;
88
+ promoteRoomHistory(roomJID: string): void;
89
+ onCriticalSend(roomJID: string, messageId?: string): void;
90
+ acknowledgeSentMessage(roomJID: string, messageId?: string): void;
91
+ enqueueHistoryTask(params: {
92
+ chatJID: string;
93
+ max: number;
94
+ before?: number;
95
+ id?: string;
96
+ source?: HistorySource;
97
+ }): Promise<IMessage[] | undefined>;
59
98
  private enqueue;
60
99
  private processQueue;
61
100
  private withIdLock;
@@ -74,7 +113,11 @@ export declare class XmppClient implements XmppClientInterface {
74
113
  private ensureRoomPresence;
75
114
  presenceInRoomStanza: (roomJID: string, settleDelay?: number, timeoutMs?: number, waitForJoin?: boolean) => Promise<boolean>;
76
115
  prioritizeRoomPresence: (roomJID: string) => Promise<boolean>;
77
- getHistoryStanza: (chatJID: string, max: number, before?: number, otherStanzaId?: string) => Promise<IMessage[]>;
116
+ getHistoryStanza: (chatJID: string, max: number, before?: number, otherStanzaId?: string, options?: {
117
+ coalesceRoom?: boolean;
118
+ skipIfPreloaded?: boolean;
119
+ source?: HistorySource;
120
+ }) => Promise<any>;
78
121
  getLastMessageArchiveStanza(roomJID: string): void;
79
122
  setRoomImageStanza: (roomJid: string, roomThumbnail: string, type: string, roomBackground?: string) => void;
80
123
  getRoomInfoStanza: (roomJID: string) => void;
@@ -1,26 +1,25 @@
1
1
  import { Reducer, AnyAction } from '@reduxjs/toolkit';
2
- import { roomHeapSliceState } from './roomHeapSlice';
3
2
  import { IRoom } from '../types/types';
4
3
  declare const rootReducer: Reducer<{
5
4
  chatSettingStore: any;
6
5
  rooms: any;
7
- roomHeapSlice: roomHeapSliceState & import('redux-persist/es/persistReducer').PersistPartial;
6
+ roomHeapSlice: any;
8
7
  }, import('redux').UnknownAction, Partial<{
9
8
  chatSettingStore: any;
10
9
  rooms: any;
11
- roomHeapSlice: roomHeapSliceState & import('redux-persist/es/persistReducer').PersistPartial;
10
+ roomHeapSlice: any;
12
11
  }>>;
13
12
  export type RootState = ReturnType<typeof rootReducer>;
14
13
  export declare const getActiveRoom: (state: RootState) => IRoom | null;
15
14
  export declare const store: import('@reduxjs/toolkit').EnhancedStore<{
16
15
  chatSettingStore: any;
17
16
  rooms: any;
18
- roomHeapSlice: roomHeapSliceState & import('redux-persist/es/persistReducer').PersistPartial;
17
+ roomHeapSlice: any;
19
18
  }, AnyAction, import('@reduxjs/toolkit').Tuple<[import('redux').StoreEnhancer<{
20
19
  dispatch: import('redux-thunk').ThunkDispatch<{
21
20
  chatSettingStore: any;
22
21
  rooms: any;
23
- roomHeapSlice: roomHeapSliceState & import('redux-persist/es/persistReducer').PersistPartial;
22
+ roomHeapSlice: any;
24
23
  }, undefined, import('redux').UnknownAction>;
25
24
  }>, import('redux').StoreEnhancer]>>;
26
25
  export type AppDispatch = typeof store.dispatch;
@@ -16,6 +16,12 @@ interface RoomMessagesState {
16
16
  pushSubscriptionStatus: Record<string, 'pending' | 'subscribed' | 'error' | 'blocked'>;
17
17
  loadingText?: string;
18
18
  }
19
+ interface PreloadRoomUpdate {
20
+ jid: string;
21
+ messages?: IMessage[];
22
+ unreadCapped?: boolean;
23
+ historyPreloadState?: 'idle' | 'loading' | 'done' | 'error';
24
+ }
19
25
  export declare const addRoomViaApi: import('@reduxjs/toolkit').AsyncThunk<void, {
20
26
  room: IRoom;
21
27
  xmpp: XmppClient;
@@ -96,6 +102,9 @@ export declare const roomsStore: import('@reduxjs/toolkit').Slice<RoomMessagesSt
96
102
  addRoomFromApi: (state: import('immer').WritableDraft<RoomMessagesState>, action: PayloadAction<{
97
103
  room: IRoom;
98
104
  }>) => void;
105
+ applyRoomsPreloadBatch: (state: import('immer').WritableDraft<RoomMessagesState>, action: PayloadAction<{
106
+ rooms: PreloadRoomUpdate[];
107
+ }>) => void;
99
108
  updateUsersSet: (state: import('immer').WritableDraft<RoomMessagesState>, action: PayloadAction<{
100
109
  rooms: ApiRoom[];
101
110
  }>) => void;
@@ -159,6 +168,8 @@ export declare const addRoom: import('@reduxjs/toolkit').ActionCreatorWithOption
159
168
  }, "roomMessages/insertUsers">, setPushSubscriptionStatus: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
160
169
  jid: string;
161
170
  status: "pending" | "subscribed" | "error" | "blocked";
162
- }, "roomMessages/setPushSubscriptionStatus">, clearPushSubscriptions: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"roomMessages/clearPushSubscriptions">;
171
+ }, "roomMessages/setPushSubscriptionStatus">, clearPushSubscriptions: import('@reduxjs/toolkit').ActionCreatorWithoutPayload<"roomMessages/clearPushSubscriptions">, applyRoomsPreloadBatch: import('@reduxjs/toolkit').ActionCreatorWithOptionalPayload<{
172
+ rooms: PreloadRoomUpdate[];
173
+ }, "roomMessages/applyRoomsPreloadBatch">;
163
174
  declare const _default: import('redux').Reducer<RoomMessagesState>;
164
175
  export default _default;
@@ -81,6 +81,9 @@ export interface IConfig {
81
81
  clearStoreBeforeInit?: boolean;
82
82
  disableSentLogic?: boolean;
83
83
  initBeforeLoad?: boolean;
84
+ initBeforeLoadAuth?: {
85
+ myEndpoint?: string;
86
+ };
84
87
  newArch?: boolean;
85
88
  qrUrl?: string;
86
89
  secondarySendButton?: {
@@ -160,6 +163,11 @@ export interface IConfig {
160
163
  hideSearch?: boolean;
161
164
  };
162
165
  useStoreConsoleEnabled?: boolean;
166
+ historyQoS?: {
167
+ maxInFlightHistory?: number;
168
+ softPauseAfterSendMs?: number;
169
+ activeRoomBoostTtlMs?: number;
170
+ };
163
171
  inAppNotifications?: {
164
172
  enabled?: boolean;
165
173
  showInContext?: boolean;
@@ -53,6 +53,8 @@ export interface IRoom {
53
53
  firstMessageTimestamp?: number;
54
54
  };
55
55
  historyComplete?: boolean;
56
+ historyPreloadState?: 'idle' | 'loading' | 'done' | 'error';
57
+ unreadCapped?: boolean;
56
58
  }
57
59
  export interface ApiRoom {
58
60
  name: string;
@@ -7,10 +7,15 @@ export interface XmppState {
7
7
  loading: boolean;
8
8
  }
9
9
  export interface xmppSettingsInterface {
10
- devServer: string;
11
- host: string;
10
+ devServer?: string;
11
+ host?: string;
12
12
  conference?: string;
13
13
  xmppPingOnSendEnabled?: boolean;
14
+ historyQoS?: {
15
+ maxInFlightHistory?: number;
16
+ softPauseAfterSendMs?: number;
17
+ activeRoomBoostTtlMs?: number;
18
+ };
14
19
  }
15
20
  export interface MediaUploadData {
16
21
  file: File;
@@ -43,7 +48,20 @@ export interface XmppClientInterface {
43
48
  presenceInRoomStanza(roomJID: string, settleDelay?: number, timeoutMs?: number, waitForJoin?: boolean): Promise<boolean>;
44
49
  prioritizeRoomPresence(roomJID: string): Promise<boolean>;
45
50
  recoverRoomPresenceOnly(roomJID: string): Promise<boolean>;
46
- getHistoryStanza(chatJID: string, max: number, before?: number, otherStanzaId?: string): Promise<IMessage[]>;
51
+ getHistoryStanza(chatJID: string, max: number, before?: number, otherStanzaId?: string, options?: {
52
+ coalesceRoom?: boolean;
53
+ skipIfPreloaded?: boolean;
54
+ source?: 'active' | 'send_ack' | 'background' | 'default';
55
+ }): Promise<IMessage[] | undefined>;
56
+ promoteRoomHistory(roomJID: string): void;
57
+ onCriticalSend(roomJID: string, messageId?: string): void;
58
+ enqueueHistoryTask(params: {
59
+ chatJID: string;
60
+ max: number;
61
+ before?: number;
62
+ id?: string;
63
+ source?: 'active' | 'send_ack' | 'background' | 'default';
64
+ }): Promise<IMessage[] | undefined>;
47
65
  getLastMessageArchiveStanza(roomJID: string): void;
48
66
  setRoomImageStanza(roomJid: string, roomThumbnail: string, type: string, roomBackground?: string): void;
49
67
  getRoomInfoStanza(roomJID: string): void;
@@ -7,9 +7,13 @@ declare class MessageNotificationManager {
7
7
  private pending;
8
8
  private maxPending;
9
9
  private pendingTtlMs;
10
+ private dedupeWindowMs;
11
+ private recentlyDelivered;
10
12
  addCallback(callback: MessageNotificationCallback): () => void;
11
13
  getCallbackCount(): number;
12
14
  private flushPending;
15
+ private buildDedupeKey;
16
+ private shouldSkipDuplicate;
13
17
  showNotification(message: IMessage, roomName: string, senderName: string, roomJID: string): void;
14
18
  }
15
19
  export declare const messageNotificationManager: MessageNotificationManager;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const ETHORA_CHAT_COMPONENT_VERSION = "26.02.01";
1
+ export declare const ETHORA_CHAT_COMPONENT_VERSION = "26.02.04";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethora/chat-component",
3
- "version": "26.02.02",
3
+ "version": "26.02.05",
4
4
  "type": "module",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
@@ -1,6 +1,14 @@
1
+ /* eslint-disable no-empty, no-restricted-globals */
2
+ /* eslint-env worker */
3
+ /* global firebase, clients */
4
+
1
5
  // ── Firebase SDK ──────────────────────────────────────────────
2
- importScripts('https://www.gstatic.com/firebasejs/10.12.0/firebase-app-compat.js');
3
- importScripts('https://www.gstatic.com/firebasejs/10.12.0/firebase-messaging-compat.js');
6
+ importScripts(
7
+ 'https://www.gstatic.com/firebasejs/10.12.0/firebase-app-compat.js'
8
+ );
9
+ importScripts(
10
+ 'https://www.gstatic.com/firebasejs/10.12.0/firebase-messaging-compat.js'
11
+ );
4
12
 
5
13
  // ── Dynamic Configuration ─────────────────────────────────────
6
14
  const params = new URL(self.location.href).searchParams;
@@ -27,7 +35,7 @@ if (
27
35
  }
28
36
  messaging = firebase.messaging();
29
37
  } else {
30
- // console.warn('[SW] Firebase config missing → FCM disabled');
38
+ // console.warn('[SW] Firebase config missing → FCM disabled');
31
39
  }
32
40
 
33
41
  const APP_URL = self.location.origin;
@@ -39,7 +47,7 @@ const DEDUP_TTL_MS = 60_000;
39
47
  function markHandledByFcm(id) {
40
48
  if (!id) return;
41
49
  _handledByFcm.add(id);
42
- // console.log('[SW] FCM handled:', id);
50
+ // console.log('[SW] FCM handled:', id);
43
51
  setTimeout(() => _handledByFcm.delete(id), DEDUP_TTL_MS);
44
52
  }
45
53
 
@@ -86,7 +94,7 @@ function postBridgeToClients(windowClients, payload) {
86
94
 
87
95
  function normalizeRawPushPayload(event) {
88
96
  if (!event.data) {
89
- // console.warn('[SW][EMPTY_PUSH] Push without payload');
97
+ // console.warn('[SW][EMPTY_PUSH] Push without payload');
90
98
  return {
91
99
  sourceType: 'empty',
92
100
  raw: {
@@ -102,11 +110,11 @@ function normalizeRawPushPayload(event) {
102
110
  try {
103
111
  textPayload = event.data.text();
104
112
  } catch (err) {
105
- // console.warn('[SW][PUSH_TEXT_READ_FAILED]', err);
113
+ // console.warn('[SW][PUSH_TEXT_READ_FAILED]', err);
106
114
  }
107
115
 
108
116
  if (!textPayload) {
109
- // console.warn('[SW][EMPTY_PUSH_TEXT] Push payload text is empty');
117
+ // console.warn('[SW][EMPTY_PUSH_TEXT] Push payload text is empty');
110
118
  return {
111
119
  sourceType: 'empty',
112
120
  raw: {
@@ -119,13 +127,13 @@ function normalizeRawPushPayload(event) {
119
127
 
120
128
  try {
121
129
  const parsed = JSON.parse(textPayload);
122
- // console.log('[SW][JSON_PUSH]', parsed);
130
+ // console.log('[SW][JSON_PUSH]', parsed);
123
131
  return {
124
132
  sourceType: 'json',
125
133
  raw: parsed,
126
134
  };
127
135
  } catch (err) {
128
- /* console.warn('[SW][TEXT_PUSH] Non-JSON push received, processing as plain text', {
136
+ /* console.warn('[SW][TEXT_PUSH] Non-JSON push received, processing as plain text', {
129
137
  error: err,
130
138
  rawText: textPayload,
131
139
  });*/
@@ -144,7 +152,7 @@ function normalizeRawPushPayload(event) {
144
152
  // ── FCM ───────────────────────────────────────────────────────
145
153
  if (messaging) {
146
154
  messaging.onBackgroundMessage((payload) => {
147
- // console.log('[SW][FCM_PUSH]', payload);
155
+ // console.log('[SW][FCM_PUSH]', payload);
148
156
 
149
157
  const data = payload.data || {};
150
158
  const notification = payload.notification || {};
@@ -159,8 +167,7 @@ if (messaging) {
159
167
  data.title ||
160
168
  'New Message';
161
169
 
162
- const body =
163
- notification.body || data.body || 'You have a new message.';
170
+ const body = notification.body || data.body || 'You have a new message.';
164
171
 
165
172
  const url = buildTargetUrl(data, notification, data.url);
166
173
 
@@ -205,13 +212,13 @@ if (messaging) {
205
212
  postBridgeToClients(windowClients, bridgePayload);
206
213
 
207
214
  if (!isAnyClientVisible(windowClients)) {
208
- /* console.log(
215
+ /* console.log(
209
216
  `[NotifyPolicy] source=fcm_bg action=show reason=${isSystem ? 'system' : 'background'} msgId=${messageId || ''}`
210
217
  );*/
211
218
  return self.registration.showNotification(title, options);
212
219
  }
213
220
 
214
- /* console.log(
221
+ /* console.log(
215
222
  `[NotifyPolicy] source=fcm_bg action=skip reason=tab_visible msgId=${messageId || ''}`
216
223
  );*/
217
224
  });
@@ -220,7 +227,7 @@ if (messaging) {
220
227
 
221
228
  // ── PUSH (WebPush / text / fallback) ──────────────────────────
222
229
  self.addEventListener('push', (event) => {
223
- // console.log('[SW] push event');
230
+ // console.log('[SW] push event');
224
231
 
225
232
  const { sourceType, raw } = normalizeRawPushPayload(event);
226
233
 
@@ -239,19 +246,19 @@ self.addEventListener('push', (event) => {
239
246
  null;
240
247
 
241
248
  if (wasHandledByFcm(messageId)) {
242
- // console.log(`[SW] push skipped – already handled by FCM (msgId=${messageId})`);
249
+ // console.log(`[SW] push skipped – already handled by FCM (msgId=${messageId})`);
243
250
  return;
244
251
  }
245
252
 
246
253
  if (sourceType !== 'json') {
247
- /* console.log('[SW][NON_JSON_PUSH_PROCESSED]', {
254
+ /* console.log('[SW][NON_JSON_PUSH_PROCESSED]', {
248
255
  sourceType,
249
256
  raw,
250
257
  });*/
251
258
  }
252
259
 
253
260
  if (!raw.from && !raw.fcmOptions) {
254
- // console.log('[SW][NON_FCM_PUSH]', raw);
261
+ // console.log('[SW][NON_FCM_PUSH]', raw);
255
262
  }
256
263
 
257
264
  const isSystem = isSystemPayload(payloadData);
@@ -270,10 +277,7 @@ self.addEventListener('push', (event) => {
270
277
  'You have a new message.';
271
278
 
272
279
  const icon =
273
- raw.icon ||
274
- notification.icon ||
275
- notification.image ||
276
- '/favicon.ico';
280
+ raw.icon || notification.icon || notification.image || '/favicon.ico';
277
281
 
278
282
  const url = buildTargetUrl(
279
283
  payloadData,
@@ -332,13 +336,13 @@ self.addEventListener('push', (event) => {
332
336
  postBridgeToClients(windowClients, bridgePayload);
333
337
 
334
338
  if (!isAnyClientVisible(windowClients)) {
335
- /* console.log(
339
+ /* console.log(
336
340
  `[NotifyPolicy] source=push_bg action=show type=${sourceType} msgId=${messageId || ''}`
337
341
  );*/
338
342
  return self.registration.showNotification(title, options);
339
343
  }
340
344
 
341
- /* console.log(
345
+ /* console.log(
342
346
  `[NotifyPolicy] source=push_bg action=skip reason=tab_visible type=${sourceType} msgId=${messageId || ''}`
343
347
  );*/
344
348
  })
@@ -347,13 +351,13 @@ self.addEventListener('push', (event) => {
347
351
 
348
352
  // ── Notification Click ────────────────────────────────────────
349
353
  self.addEventListener('notificationclick', (event) => {
350
- // console.log('[NotifyPolicy] source=push_bg action=click_start');
354
+ // console.log('[NotifyPolicy] source=push_bg action=click_start');
351
355
  event.notification.close();
352
356
 
353
357
  const targetUrl =
354
358
  (event.notification.data && event.notification.data.url) || APP_URL;
355
359
 
356
- /* console.log('[NotifyPolicy] source=push_bg action=click_url', {
360
+ /* console.log('[NotifyPolicy] source=push_bg action=click_url', {
357
361
  url: targetUrl,
358
362
  });*/
359
363
 
@@ -364,7 +368,7 @@ self.addEventListener('notificationclick', (event) => {
364
368
  includeUncontrolled: true,
365
369
  });
366
370
 
367
- /* console.log('[NotifyPolicy] source=push_bg action=clients_found', {
371
+ /* console.log('[NotifyPolicy] source=push_bg action=clients_found', {
368
372
  count: windowClients.length,
369
373
  });*/
370
374
 
@@ -383,7 +387,7 @@ self.addEventListener('notificationclick', (event) => {
383
387
 
384
388
  for (const client of windowClients) {
385
389
  if (client.url === targetUrl && 'focus' in client) {
386
- /* console.log('[NotifyPolicy] source=push_bg action=focus_exact', {
390
+ /* console.log('[NotifyPolicy] source=push_bg action=focus_exact', {
387
391
  url: client.url,
388
392
  });*/
389
393
  return client.focus();
@@ -396,7 +400,7 @@ self.addEventListener('notificationclick', (event) => {
396
400
  const swOrigin = self.location.origin;
397
401
 
398
402
  if (clientOrigin === swOrigin && 'focus' in client) {
399
- /* console.log(
403
+ /* console.log(
400
404
  '[NotifyPolicy] source=push_bg action=focus_and_navigate',
401
405
  { from: client.url, to: targetUrl }
402
406
  );*/
@@ -413,7 +417,7 @@ self.addEventListener('notificationclick', (event) => {
413
417
  }
414
418
 
415
419
  if (clients.openWindow) {
416
- /* console.log('[NotifyPolicy] source=push_bg action=open_new_window', {
420
+ /* console.log('[NotifyPolicy] source=push_bg action=open_new_window', {
417
421
  url: targetUrl,
418
422
  });*/
419
423
  return clients.openWindow(targetUrl);
@@ -432,25 +436,27 @@ self.addEventListener('notificationclick', (event) => {
432
436
 
433
437
  // ── Debug / Subscription ──────────────────────────────────────
434
438
  self.addEventListener('pushsubscriptionchange', (event) => {
435
- // console.warn('[SW][POSSIBLE_DROP] subscription changed');
439
+ // console.warn('[SW][POSSIBLE_DROP] subscription changed');
436
440
  event.waitUntil(
437
- clients.matchAll({ type: 'window', includeUncontrolled: true }).then((allClients) => {
438
- allClients.forEach((client) => {
439
- client.postMessage({
440
- type: 'PUSH_SUBSCRIPTION_CHANGED',
441
+ clients
442
+ .matchAll({ type: 'window', includeUncontrolled: true })
443
+ .then((allClients) => {
444
+ allClients.forEach((client) => {
445
+ client.postMessage({
446
+ type: 'PUSH_SUBSCRIPTION_CHANGED',
447
+ });
441
448
  });
442
- });
443
- })
449
+ })
444
450
  );
445
451
  });
446
452
 
447
453
  // ── Lifecycle ─────────────────────────────────────────────────
448
454
  self.addEventListener('activate', (event) => {
449
- // console.warn('[SW] activated');
455
+ // console.warn('[SW] activated');
450
456
  event.waitUntil(clients.claim());
451
457
  });
452
458
 
453
459
  self.addEventListener('install', (event) => {
454
- // console.log('[SW] installed');
460
+ // console.log('[SW] installed');
455
461
  event.waitUntil(self.skipWaiting());
456
462
  });