@firebase/messaging 0.12.26-20260505185142 → 0.12.26-eap-crashlytics.3dd4e65d9

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.
Files changed (51) hide show
  1. package/dist/esm/index.esm.js +773 -79
  2. package/dist/esm/index.esm.js.map +1 -1
  3. package/dist/esm/index.sw.esm.js +496 -77
  4. package/dist/esm/index.sw.esm.js.map +1 -1
  5. package/dist/esm/src/api/onRegistered.d.ts +27 -0
  6. package/dist/esm/src/api/onUnregistered.d.ts +27 -0
  7. package/dist/esm/src/api/register.d.ts +31 -0
  8. package/dist/esm/src/api/unregister.d.ts +25 -0
  9. package/dist/esm/src/api.d.ts +59 -2
  10. package/dist/esm/src/helpers/fid-change-registration.d.ts +24 -0
  11. package/dist/esm/src/helpers/logToFirelog.d.ts +2 -0
  12. package/dist/esm/src/helpers/migrate-old-database.d.ts +1 -1
  13. package/dist/esm/src/index.d.ts +1 -1
  14. package/dist/esm/src/index.sw.d.ts +1 -1
  15. package/dist/esm/src/interfaces/public-types.d.ts +11 -0
  16. package/dist/esm/src/internals/idb-manager.d.ts +16 -4
  17. package/dist/esm/src/internals/register-fid.d.ts +27 -0
  18. package/dist/esm/src/internals/requests.d.ts +35 -1
  19. package/dist/esm/src/messaging-service.d.ts +27 -2
  20. package/dist/esm/src/testing/fakes/token-details.d.ts +1 -1
  21. package/dist/esm/src/util/errors.d.ts +11 -1
  22. package/dist/index-public.d.ts +68 -0
  23. package/dist/index.cjs.js +776 -78
  24. package/dist/index.cjs.js.map +1 -1
  25. package/dist/index.sw.cjs +497 -76
  26. package/dist/index.sw.cjs.map +1 -1
  27. package/dist/internal.d.ts +73 -0
  28. package/dist/private.d.ts +73 -0
  29. package/dist/src/api/onRegistered.d.ts +27 -0
  30. package/dist/src/api/onUnregistered.d.ts +27 -0
  31. package/dist/src/api/register.d.ts +31 -0
  32. package/dist/src/api/unregister.d.ts +25 -0
  33. package/dist/src/api.d.ts +59 -2
  34. package/dist/src/helpers/fid-change-registration.d.ts +24 -0
  35. package/dist/src/helpers/logToFirelog.d.ts +2 -0
  36. package/dist/src/helpers/migrate-old-database.d.ts +1 -1
  37. package/dist/src/index.d.ts +1 -1
  38. package/dist/src/index.sw.d.ts +1 -1
  39. package/dist/src/interfaces/public-types.d.ts +11 -0
  40. package/dist/src/internals/idb-manager.d.ts +16 -4
  41. package/dist/src/internals/register-fid.d.ts +27 -0
  42. package/dist/src/internals/requests.d.ts +35 -1
  43. package/dist/src/messaging-service.d.ts +27 -2
  44. package/dist/src/testing/fakes/token-details.d.ts +1 -1
  45. package/dist/src/util/errors.d.ts +11 -1
  46. package/dist/sw/index-public.d.ts +35 -1
  47. package/dist/sw/internal.d.ts +38 -1
  48. package/dist/sw/private.d.ts +38 -1
  49. package/package.json +7 -7
  50. /package/dist/esm/src/interfaces/{token-details.d.ts → registration-details.d.ts} +0 -0
  51. /package/dist/src/interfaces/{token-details.d.ts → registration-details.d.ts} +0 -0
@@ -25,6 +25,13 @@ const ENDPOINT = 'https://fcmregistrations.googleapis.com/v1';
25
25
  /** Key of FCM Payload in Notification's data field. */
26
26
  const FCM_MSG = 'FCM_MSG';
27
27
  const CONSOLE_CAMPAIGN_ID = 'google.c.a.c_id';
28
+ const MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST = 1000;
29
+ const MAX_RETRIES = 3;
30
+ const LOG_INTERVAL_IN_MS = 86400000; //24 hour
31
+ const DEFAULT_BACKOFF_TIME_MS = 5000;
32
+ // FCM log source name registered at Firelog: 'FCM_CLIENT_EVENT_LOGGING'. It uniquely identifies
33
+ // FCM's logging configuration.
34
+ const FCM_LOG_SOURCE = 1249;
28
35
  // Defined as in proto/messaging_event.proto. Neglecting fields that are supported.
29
36
  const SDK_PLATFORM_WEB = 3;
30
37
  const EVENT_MESSAGE_DELIVERED = 1;
@@ -217,6 +224,53 @@ function checkTokenDetails(tokenDetails) {
217
224
  subscriptionOptions.vapidKey.length > 0);
218
225
  }
219
226
 
227
+ /**
228
+ * @license
229
+ * Copyright 2017 Google LLC
230
+ *
231
+ * Licensed under the Apache License, Version 2.0 (the "License");
232
+ * you may not use this file except in compliance with the License.
233
+ * You may obtain a copy of the License at
234
+ *
235
+ * http://www.apache.org/licenses/LICENSE-2.0
236
+ *
237
+ * Unless required by applicable law or agreed to in writing, software
238
+ * distributed under the License is distributed on an "AS IS" BASIS,
239
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
240
+ * See the License for the specific language governing permissions and
241
+ * limitations under the License.
242
+ */
243
+ const ERROR_MAP = {
244
+ ["missing-app-config-values" /* ErrorCode.MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: "{$valueName}"',
245
+ ["only-available-in-window" /* ErrorCode.AVAILABLE_IN_WINDOW */]: 'This method is available in a Window context.',
246
+ ["only-available-in-sw" /* ErrorCode.AVAILABLE_IN_SW */]: 'This method is available in a service worker context.',
247
+ ["permission-default" /* ErrorCode.PERMISSION_DEFAULT */]: 'The notification permission was not granted and dismissed instead.',
248
+ ["permission-blocked" /* ErrorCode.PERMISSION_BLOCKED */]: 'The notification permission was not granted and blocked instead.',
249
+ ["unsupported-browser" /* ErrorCode.UNSUPPORTED_BROWSER */]: "This browser doesn't support the API's required to use the Firebase SDK.",
250
+ ["indexed-db-unsupported" /* ErrorCode.INDEXED_DB_UNSUPPORTED */]: "This browser doesn't support indexedDb.open() (ex. Safari iFrame, Firefox Private Browsing, etc)",
251
+ ["failed-service-worker-registration" /* ErrorCode.FAILED_DEFAULT_REGISTRATION */]: 'We are unable to register the default service worker. {$browserErrorMessage}',
252
+ ["token-subscribe-failed" /* ErrorCode.TOKEN_SUBSCRIBE_FAILED */]: 'A problem occurred while subscribing the user to FCM: {$errorInfo}',
253
+ ["token-subscribe-no-token" /* ErrorCode.TOKEN_SUBSCRIBE_NO_TOKEN */]: 'FCM returned no token when subscribing the user to push.',
254
+ ["fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */]: 'A problem occurred while creating an FCM registration via FID: {$errorInfo}',
255
+ ["fid-unregister-failed" /* ErrorCode.FID_UNREGISTER_FAILED */]: 'A problem occurred while unregistering the FCM registration via FID: {$errorInfo}',
256
+ ["fid-registration-idb-schema-unavailable" /* ErrorCode.FID_REGISTRATION_IDB_SCHEMA_UNAVAILABLE */]: 'Unable to read or persist FID registration metadata because the messaging ' +
257
+ 'IndexedDB schema is unavailable (for example, the database could not be ' +
258
+ 'upgraded to the latest version).',
259
+ ["token-unsubscribe-failed" /* ErrorCode.TOKEN_UNSUBSCRIBE_FAILED */]: 'A problem occurred while unsubscribing the ' +
260
+ 'user from FCM: {$errorInfo}',
261
+ ["token-update-failed" /* ErrorCode.TOKEN_UPDATE_FAILED */]: 'A problem occurred while updating the user from FCM: {$errorInfo}',
262
+ ["token-update-no-token" /* ErrorCode.TOKEN_UPDATE_NO_TOKEN */]: 'FCM returned no token when updating the user to push.',
263
+ ["use-sw-after-get-token" /* ErrorCode.USE_SW_AFTER_GET_TOKEN */]: 'The useServiceWorker() method may only be called once and must be ' +
264
+ 'called before calling getToken() to ensure your service worker is used.',
265
+ ["invalid-sw-registration" /* ErrorCode.INVALID_SW_REGISTRATION */]: 'The input to useServiceWorker() must be a ServiceWorkerRegistration.',
266
+ ["invalid-bg-handler" /* ErrorCode.INVALID_BG_HANDLER */]: 'The input to setBackgroundMessageHandler() must be a function.',
267
+ ["invalid-vapid-key" /* ErrorCode.INVALID_VAPID_KEY */]: 'The public VAPID key must be a string.',
268
+ ["use-vapid-key-after-get-token" /* ErrorCode.USE_VAPID_KEY_AFTER_GET_TOKEN */]: 'The usePublicVapidKey() method may only be called once and must be ' +
269
+ 'called before calling getToken() to ensure your VAPID key is used.',
270
+ ["invalid-on-registered-handler" /* ErrorCode.INVALID_ON_REGISTERED_HANDLER */]: 'No onRegistered callback handler was provided or registered. Implement onRegistered() before register().'
271
+ };
272
+ const ERROR_FACTORY = new ErrorFactory('messaging', 'Messaging', ERROR_MAP);
273
+
220
274
  /**
221
275
  * @license
222
276
  * Copyright 2019 Google LLC
@@ -233,41 +287,74 @@ function checkTokenDetails(tokenDetails) {
233
287
  * See the License for the specific language governing permissions and
234
288
  * limitations under the License.
235
289
  */
236
- // Exported for tests.
237
290
  const DATABASE_NAME = 'firebase-messaging-database';
238
- const DATABASE_VERSION = 1;
239
- const OBJECT_STORE_NAME = 'firebase-messaging-store';
291
+ const DATABASE_VERSION = 2;
292
+ const TOKEN_OBJECT_STORE_NAME = 'firebase-messaging-store';
293
+ const FID_REGISTRATION_OBJECT_STORE_NAME = 'firebase-messaging-fid-registration-store';
294
+ const defaultIdb = { openDB, deleteDB };
295
+ let idbImpl = defaultIdb;
296
+ // Open v2, but fall back to v1 if upgrade/open fails. Cache as `unknown` and guard store access.
240
297
  let dbPromise = null;
298
+ function migrateMessagingDb(upgradeDb, oldVersion, targetSchemaVersion) {
299
+ // Intentional fall-through for v2: run all intermediate migrations.
300
+ // eslint-disable-next-line default-case
301
+ switch (oldVersion) {
302
+ case 0:
303
+ upgradeDb.createObjectStore(TOKEN_OBJECT_STORE_NAME);
304
+ if (targetSchemaVersion === 1) {
305
+ break;
306
+ }
307
+ // fall through
308
+ case 1:
309
+ if (targetSchemaVersion === 2) {
310
+ upgradeDb.createObjectStore(FID_REGISTRATION_OBJECT_STORE_NAME);
311
+ }
312
+ }
313
+ }
314
+ function createOpenDbOptions(targetSchemaVersion) {
315
+ return {
316
+ upgrade: (upgradeDb, oldVersion) => {
317
+ migrateMessagingDb(upgradeDb, oldVersion, targetSchemaVersion);
318
+ },
319
+ blocked: () => {
320
+ /* no-op */
321
+ },
322
+ blocking: (_currentVersion, _blockedVersion, event) => {
323
+ dbPromise = null;
324
+ event.target?.close();
325
+ },
326
+ terminated: () => {
327
+ dbPromise = null;
328
+ }
329
+ };
330
+ }
241
331
  function getDbPromise() {
242
332
  if (!dbPromise) {
243
- dbPromise = openDB(DATABASE_NAME, DATABASE_VERSION, {
244
- upgrade: (upgradeDb, oldVersion) => {
245
- // We don't use 'break' in this switch statement, the fall-through behavior is what we want,
246
- // because if there are multiple versions between the old version and the current version, we
247
- // want ALL the migrations that correspond to those versions to run, not only the last one.
248
- // eslint-disable-next-line default-case
249
- switch (oldVersion) {
250
- case 0:
251
- upgradeDb.createObjectStore(OBJECT_STORE_NAME);
252
- }
253
- }
254
- });
333
+ const openLatest = idbImpl.openDB(DATABASE_NAME, DATABASE_VERSION, createOpenDbOptions(2));
334
+ // Assign synchronously to avoid concurrent openDB() calls.
335
+ dbPromise = openLatest.catch(() => idbImpl.openDB(DATABASE_NAME, DATABASE_VERSION - 1, createOpenDbOptions(1)));
255
336
  }
256
337
  return dbPromise;
257
338
  }
258
- /** Gets record(s) from the objectStore that match the given key. */
339
+ function hasObjectStore(db, storeName) {
340
+ return db.objectStoreNames.contains(storeName);
341
+ }
342
+ function assertFidRegistrationObjectStore(db) {
343
+ if (!hasObjectStore(db, FID_REGISTRATION_OBJECT_STORE_NAME)) {
344
+ throw ERROR_FACTORY.create("fid-registration-idb-schema-unavailable" /* ErrorCode.FID_REGISTRATION_IDB_SCHEMA_UNAVAILABLE */);
345
+ }
346
+ }
259
347
  async function dbGet(firebaseDependencies) {
260
348
  const key = getKey(firebaseDependencies);
261
349
  const db = await getDbPromise();
262
350
  const tokenDetails = (await db
263
- .transaction(OBJECT_STORE_NAME)
264
- .objectStore(OBJECT_STORE_NAME)
351
+ .transaction(TOKEN_OBJECT_STORE_NAME)
352
+ .objectStore(TOKEN_OBJECT_STORE_NAME)
265
353
  .get(key));
266
354
  if (tokenDetails) {
267
355
  return tokenDetails;
268
356
  }
269
357
  else {
270
- // Check if there is a tokenDetails object in the old DB.
271
358
  const oldTokenDetails = await migrateOldDatabase(firebaseDependencies.appConfig.senderId);
272
359
  if (oldTokenDetails) {
273
360
  await dbSet(firebaseDependencies, oldTokenDetails);
@@ -275,67 +362,43 @@ async function dbGet(firebaseDependencies) {
275
362
  }
276
363
  }
277
364
  }
278
- /** Assigns or overwrites the record for the given key with the given value. */
279
365
  async function dbSet(firebaseDependencies, tokenDetails) {
280
366
  const key = getKey(firebaseDependencies);
281
367
  const db = await getDbPromise();
282
- const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
283
- await tx.objectStore(OBJECT_STORE_NAME).put(tokenDetails, key);
368
+ const tx = db.transaction(TOKEN_OBJECT_STORE_NAME, 'readwrite');
369
+ await tx.objectStore(TOKEN_OBJECT_STORE_NAME).put(tokenDetails, key);
284
370
  await tx.done;
285
371
  return tokenDetails;
286
372
  }
287
- /** Removes record(s) from the objectStore that match the given key. */
288
373
  async function dbRemove(firebaseDependencies) {
289
374
  const key = getKey(firebaseDependencies);
290
375
  const db = await getDbPromise();
291
- const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');
292
- await tx.objectStore(OBJECT_STORE_NAME).delete(key);
376
+ const tx = db.transaction(TOKEN_OBJECT_STORE_NAME, 'readwrite');
377
+ await tx.objectStore(TOKEN_OBJECT_STORE_NAME).delete(key);
378
+ await tx.done;
379
+ }
380
+ async function dbGetFidRegistration(firebaseDependencies) {
381
+ const key = getKey(firebaseDependencies);
382
+ const db = await getDbPromise();
383
+ assertFidRegistrationObjectStore(db);
384
+ return (await db
385
+ .transaction(FID_REGISTRATION_OBJECT_STORE_NAME)
386
+ .objectStore(FID_REGISTRATION_OBJECT_STORE_NAME)
387
+ .get(key));
388
+ }
389
+ async function dbRemoveFidRegistration(firebaseDependencies) {
390
+ const key = getKey(firebaseDependencies);
391
+ const db = await getDbPromise();
392
+ assertFidRegistrationObjectStore(db);
393
+ const tx = db.transaction(FID_REGISTRATION_OBJECT_STORE_NAME, 'readwrite');
394
+ await tx.objectStore(FID_REGISTRATION_OBJECT_STORE_NAME).delete(key);
293
395
  await tx.done;
294
396
  }
295
397
  function getKey({ appConfig }) {
296
398
  return appConfig.appId;
297
399
  }
298
400
 
299
- /**
300
- * @license
301
- * Copyright 2017 Google LLC
302
- *
303
- * Licensed under the Apache License, Version 2.0 (the "License");
304
- * you may not use this file except in compliance with the License.
305
- * You may obtain a copy of the License at
306
- *
307
- * http://www.apache.org/licenses/LICENSE-2.0
308
- *
309
- * Unless required by applicable law or agreed to in writing, software
310
- * distributed under the License is distributed on an "AS IS" BASIS,
311
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
312
- * See the License for the specific language governing permissions and
313
- * limitations under the License.
314
- */
315
- const ERROR_MAP = {
316
- ["missing-app-config-values" /* ErrorCode.MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: "{$valueName}"',
317
- ["only-available-in-window" /* ErrorCode.AVAILABLE_IN_WINDOW */]: 'This method is available in a Window context.',
318
- ["only-available-in-sw" /* ErrorCode.AVAILABLE_IN_SW */]: 'This method is available in a service worker context.',
319
- ["permission-default" /* ErrorCode.PERMISSION_DEFAULT */]: 'The notification permission was not granted and dismissed instead.',
320
- ["permission-blocked" /* ErrorCode.PERMISSION_BLOCKED */]: 'The notification permission was not granted and blocked instead.',
321
- ["unsupported-browser" /* ErrorCode.UNSUPPORTED_BROWSER */]: "This browser doesn't support the API's required to use the Firebase SDK.",
322
- ["indexed-db-unsupported" /* ErrorCode.INDEXED_DB_UNSUPPORTED */]: "This browser doesn't support indexedDb.open() (ex. Safari iFrame, Firefox Private Browsing, etc)",
323
- ["failed-service-worker-registration" /* ErrorCode.FAILED_DEFAULT_REGISTRATION */]: 'We are unable to register the default service worker. {$browserErrorMessage}',
324
- ["token-subscribe-failed" /* ErrorCode.TOKEN_SUBSCRIBE_FAILED */]: 'A problem occurred while subscribing the user to FCM: {$errorInfo}',
325
- ["token-subscribe-no-token" /* ErrorCode.TOKEN_SUBSCRIBE_NO_TOKEN */]: 'FCM returned no token when subscribing the user to push.',
326
- ["token-unsubscribe-failed" /* ErrorCode.TOKEN_UNSUBSCRIBE_FAILED */]: 'A problem occurred while unsubscribing the ' +
327
- 'user from FCM: {$errorInfo}',
328
- ["token-update-failed" /* ErrorCode.TOKEN_UPDATE_FAILED */]: 'A problem occurred while updating the user from FCM: {$errorInfo}',
329
- ["token-update-no-token" /* ErrorCode.TOKEN_UPDATE_NO_TOKEN */]: 'FCM returned no token when updating the user to push.',
330
- ["use-sw-after-get-token" /* ErrorCode.USE_SW_AFTER_GET_TOKEN */]: 'The useServiceWorker() method may only be called once and must be ' +
331
- 'called before calling getToken() to ensure your service worker is used.',
332
- ["invalid-sw-registration" /* ErrorCode.INVALID_SW_REGISTRATION */]: 'The input to useServiceWorker() must be a ServiceWorkerRegistration.',
333
- ["invalid-bg-handler" /* ErrorCode.INVALID_BG_HANDLER */]: 'The input to setBackgroundMessageHandler() must be a function.',
334
- ["invalid-vapid-key" /* ErrorCode.INVALID_VAPID_KEY */]: 'The public VAPID key must be a string.',
335
- ["use-vapid-key-after-get-token" /* ErrorCode.USE_VAPID_KEY_AFTER_GET_TOKEN */]: 'The usePublicVapidKey() method may only be called once and must be ' +
336
- 'called before calling getToken() to ensure your VAPID key is used.'
337
- };
338
- const ERROR_FACTORY = new ErrorFactory('messaging', 'Messaging', ERROR_MAP);
401
+ const version = "0.12.26-eap-crashlytics.3dd4e65d9";
339
402
 
340
403
  /**
341
404
  * @license
@@ -355,7 +418,8 @@ const ERROR_FACTORY = new ErrorFactory('messaging', 'Messaging', ERROR_MAP);
355
418
  */
356
419
  async function requestGetToken(firebaseDependencies, subscriptionOptions) {
357
420
  const headers = await getHeaders(firebaseDependencies);
358
- const body = getBody(subscriptionOptions);
421
+ const body = getBody(subscriptionOptions, firebaseDependencies.appConfig.appName,
422
+ /* includeSdkVersion= */ false);
359
423
  const subscribeOptions = {
360
424
  method: 'POST',
361
425
  headers,
@@ -382,9 +446,46 @@ async function requestGetToken(firebaseDependencies, subscriptionOptions) {
382
446
  }
383
447
  return responseData.token;
384
448
  }
449
+ /**
450
+ * Deletes an FCM Web registration via DeleteRegistration using the Firebase Installation ID (FID).
451
+ */
452
+ async function requestDeleteRegistration(firebaseDependencies, fid) {
453
+ const headers = await getHeaders(firebaseDependencies);
454
+ const options = {
455
+ method: 'DELETE',
456
+ headers
457
+ };
458
+ let response;
459
+ try {
460
+ response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}/${fid}`, options);
461
+ }
462
+ catch (err) {
463
+ throw ERROR_FACTORY.create("fid-unregister-failed" /* ErrorCode.FID_UNREGISTER_FAILED */, {
464
+ errorInfo: err?.toString()
465
+ });
466
+ }
467
+ if (response.ok) {
468
+ return;
469
+ }
470
+ // Best-effort parse error details; surface uniform error code.
471
+ try {
472
+ const responseData = (await response.json());
473
+ const message = responseData.error?.message ?? response.statusText;
474
+ throw message;
475
+ }
476
+ catch (err) {
477
+ // If parsing failed, fall back to status text.
478
+ throw ERROR_FACTORY.create("fid-unregister-failed" /* ErrorCode.FID_UNREGISTER_FAILED */, {
479
+ errorInfo: (typeof err === 'string' && err) ||
480
+ response.statusText ||
481
+ err?.toString()
482
+ });
483
+ }
484
+ }
385
485
  async function requestUpdateToken(firebaseDependencies, tokenDetails) {
386
486
  const headers = await getHeaders(firebaseDependencies);
387
- const body = getBody(tokenDetails.subscriptionOptions);
487
+ const body = getBody(tokenDetails.subscriptionOptions, firebaseDependencies.appConfig.appName,
488
+ /* includeSdkVersion= */ false);
388
489
  const updateOptions = {
389
490
  method: 'PATCH',
390
491
  headers,
@@ -445,14 +546,45 @@ async function getHeaders({ appConfig, installations }) {
445
546
  'x-goog-firebase-installations-auth': `FIS ${authToken}`
446
547
  });
447
548
  }
448
- function getBody({ p256dh, auth, endpoint, vapidKey }) {
549
+ /**
550
+ * Hostname for the registering web client (e.g. `www.example.com`), or the app name
551
+ * (`appNameFallback`) when the scope cannot be resolved (e.g. some test environments).
552
+ */
553
+ function getRegistrationOrigin(swScope, appNameFallback) {
554
+ try {
555
+ if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(swScope)) {
556
+ return new URL(swScope).host;
557
+ }
558
+ }
559
+ catch {
560
+ // Fall through to relative-scope handling.
561
+ }
562
+ try {
563
+ if (typeof self !== 'undefined' && self.location?.href) {
564
+ return new URL(swScope, self.location.origin).host;
565
+ }
566
+ }
567
+ catch {
568
+ // Fall through.
569
+ }
570
+ if (typeof self !== 'undefined' && self.location?.host) {
571
+ return self.location.host;
572
+ }
573
+ return appNameFallback;
574
+ }
575
+ function getBody({ p256dh, auth, endpoint, vapidKey, swScope }, appNameFallback, includeSdkVersion) {
449
576
  const body = {
450
577
  web: {
578
+ origin: getRegistrationOrigin(swScope, appNameFallback),
451
579
  endpoint,
452
580
  auth,
453
581
  p256dh
454
582
  }
455
583
  };
584
+ if (includeSdkVersion) {
585
+ // eslint-disable-next-line camelcase
586
+ body.fcm_sdk_version = version;
587
+ }
456
588
  if (vapidKey !== DEFAULT_VAPID_KEY) {
457
589
  body.web.applicationPubKey = vapidKey;
458
590
  }
@@ -486,6 +618,8 @@ async function getTokenInternal(messaging) {
486
618
  auth: arrayToBase64(pushSubscription.getKey('auth')),
487
619
  p256dh: arrayToBase64(pushSubscription.getKey('p256dh'))
488
620
  };
621
+ // Best-effort cleanup when switching from FID register/unregister to legacy getToken().
622
+ await removeFidRegistrationBestEffort(messaging.firebaseDependencies);
489
623
  const tokenDetails = await dbGet(messaging.firebaseDependencies);
490
624
  if (!tokenDetails) {
491
625
  // No token, get a new one.
@@ -515,6 +649,31 @@ async function getTokenInternal(messaging) {
515
649
  return tokenDetails.token;
516
650
  }
517
651
  }
652
+ /**
653
+ * Legacy getToken() path: there is a token row in IndexedDB. Revoke it with FCM, drop the row, and
654
+ * clear any leftover FID registration metadata (apps may mix APIs).
655
+ */
656
+ async function revokeLegacyFcmTokenAndClearCaches(messaging, tokenDetails) {
657
+ await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token);
658
+ await dbRemove(messaging.firebaseDependencies);
659
+ await removeFidRegistrationBestEffort(messaging.firebaseDependencies);
660
+ }
661
+ /**
662
+ * No legacy token row: the client may only have FID-based registration (register() flow). If so,
663
+ * delete that registration on the server, always scrub local FID metadata, then surface
664
+ * onUnregistered when we actually had an FID.
665
+ */
666
+ async function revokeFidRegistrationIfStored(messaging) {
667
+ const stored = await dbGetFidRegistration(messaging.firebaseDependencies).catch(() => undefined);
668
+ const fid = stored?.fid;
669
+ if (fid) {
670
+ await requestDeleteRegistration(messaging.firebaseDependencies, fid);
671
+ }
672
+ await removeFidRegistrationBestEffort(messaging.firebaseDependencies);
673
+ if (fid) {
674
+ notifyOnUnregistered(messaging, fid);
675
+ }
676
+ }
518
677
  /**
519
678
  * This method deletes the token from the database, unsubscribes the token from FCM, and unregisters
520
679
  * the push subscription if it exists.
@@ -522,8 +681,10 @@ async function getTokenInternal(messaging) {
522
681
  async function deleteTokenInternal(messaging) {
523
682
  const tokenDetails = await dbGet(messaging.firebaseDependencies);
524
683
  if (tokenDetails) {
525
- await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token);
526
- await dbRemove(messaging.firebaseDependencies);
684
+ await revokeLegacyFcmTokenAndClearCaches(messaging, tokenDetails);
685
+ }
686
+ else {
687
+ await revokeFidRegistrationIfStored(messaging);
527
688
  }
528
689
  // Unsubscribe from the push subscription.
529
690
  const pushSubscription = await messaging.swRegistration.pushManager.getSubscription();
@@ -583,6 +744,27 @@ function isTokenValid(dbOptions, currentOptions) {
583
744
  const isP256dhEqual = currentOptions.p256dh === dbOptions.p256dh;
584
745
  return isVapidKeyEqual && isEndpointEqual && isAuthEqual && isP256dhEqual;
585
746
  }
747
+ /** Clears FID registration metadata; apps may mix legacy getToken() with FID register/unregister. */
748
+ async function removeFidRegistrationBestEffort(firebaseDependencies) {
749
+ try {
750
+ await dbRemoveFidRegistration(firebaseDependencies);
751
+ }
752
+ catch {
753
+ // Ignore.
754
+ }
755
+ }
756
+ function notifyOnUnregistered(messaging, fid) {
757
+ const handler = messaging.onUnregisteredHandler;
758
+ if (!handler) {
759
+ return;
760
+ }
761
+ if (typeof handler === 'function') {
762
+ handler(fid);
763
+ }
764
+ else {
765
+ handler.next(fid);
766
+ }
767
+ }
586
768
 
587
769
  /**
588
770
  * @license
@@ -720,10 +902,109 @@ function sleep(ms) {
720
902
  * See the License for the specific language governing permissions and
721
903
  * limitations under the License.
722
904
  */
723
- _mergeStrings('AzSCbw63g1R0nCw85jG8', 'Iaya3yLKwmgvh7cF0q4');
905
+ const LOG_ENDPOINT = 'https://play.google.com/log?format=json_proto3';
906
+ /** First flush ASAP (next timer turn); `_dispatchLogEvents` reschedules with `LOG_INTERVAL_IN_MS`. */
907
+ const INITIAL_LOG_FLUSH_DELAY_MS = 0;
908
+ const FCM_TRANSPORT_KEY = _mergeStrings('AzSCbw63g1R0nCw85jG8', 'Iaya3yLKwmgvh7cF0q4');
909
+ function startLoggingService(messaging) {
910
+ // Start only if not already scheduled/in-flight and there is work to do.
911
+ if (messaging.logQueue.state === 'stopped' &&
912
+ messaging.logEvents.length > 0) {
913
+ _processQueue(messaging, INITIAL_LOG_FLUSH_DELAY_MS);
914
+ }
915
+ }
916
+ /** Clears queued Firelog events, cancels any pending flush timer, and stops the logging loop. */
917
+ function stopLoggingServiceAndClearQueue(messaging) {
918
+ if (messaging.logQueue.state === 'scheduled') {
919
+ clearTimeout(messaging.logQueue.timerId);
920
+ }
921
+ messaging.logQueue = { state: 'stopped' };
922
+ messaging.logEvents = [];
923
+ }
924
+ /**
925
+ *
926
+ * @param messaging the messaging instance.
927
+ * @param offsetInMs this method execute after `offsetInMs` elapsed .
928
+ */
929
+ function _processQueue(messaging, offsetInMs) {
930
+ if (messaging.logQueue.state === 'scheduled') {
931
+ clearTimeout(messaging.logQueue.timerId);
932
+ }
933
+ messaging.logQueue = { state: 'stopped' };
934
+ if (!messaging.deliveryMetricsExportedToBigQueryEnabled) {
935
+ messaging.logEvents = [];
936
+ return;
937
+ }
938
+ messaging.logQueue = {
939
+ state: 'scheduled',
940
+ timerId: setTimeout(async () => {
941
+ // Mark in-flight so stageLog/startLoggingService won't schedule duplicates mid-dispatch.
942
+ messaging.logQueue = { state: 'flushing' };
943
+ if (!messaging.logEvents.length) {
944
+ return _processQueue(messaging, LOG_INTERVAL_IN_MS);
945
+ }
946
+ await _dispatchLogEvents(messaging);
947
+ }, offsetInMs)
948
+ };
949
+ }
950
+ async function _dispatchLogEvents(messaging) {
951
+ // Swap the queue to avoid losing events added during an in-flight dispatch.
952
+ const eventsToSend = messaging.logEvents;
953
+ messaging.logEvents = [];
954
+ for (let i = 0, n = eventsToSend.length; i < n; i += MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST) {
955
+ const batch = eventsToSend.slice(i, i + MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST);
956
+ if (!batch.length) {
957
+ break;
958
+ }
959
+ const logRequest = _createLogRequest(batch);
960
+ let retryCount = 0, response = {};
961
+ do {
962
+ try {
963
+ response = await fetch(LOG_ENDPOINT.concat('&key=', FCM_TRANSPORT_KEY), {
964
+ method: 'POST',
965
+ body: JSON.stringify(logRequest)
966
+ });
967
+ // don't retry on 200s or non retriable errors
968
+ if (response.ok || (!response.ok && !isRetriableError(response))) {
969
+ break;
970
+ }
971
+ if (!response.ok && isRetriableError(response)) {
972
+ // rethrow to retry with quota
973
+ throw new Error('a retriable Non-200 code is returned in fetch to Firelog endpoint. Retry');
974
+ }
975
+ }
976
+ catch (error) {
977
+ const isLastAttempt = retryCount === MAX_RETRIES;
978
+ if (isLastAttempt) {
979
+ // existing the do-while interactive retry logic because retry quota has reached.
980
+ break;
981
+ }
982
+ }
983
+ let delayInMs;
984
+ try {
985
+ delayInMs = Number((await response.json()).nextRequestWaitMillis);
986
+ }
987
+ catch (e) {
988
+ delayInMs = DEFAULT_BACKOFF_TIME_MS;
989
+ }
990
+ await new Promise(resolve => setTimeout(resolve, delayInMs));
991
+ retryCount++;
992
+ } while (retryCount < MAX_RETRIES);
993
+ }
994
+ // Schedule next flush. If new events arrived during this dispatch, flush ASAP.
995
+ _processQueue(messaging, messaging.logEvents.length ? INITIAL_LOG_FLUSH_DELAY_MS : LOG_INTERVAL_IN_MS);
996
+ }
997
+ function isRetriableError(response) {
998
+ const httpStatus = response.status;
999
+ return (httpStatus === 429 ||
1000
+ httpStatus === 500 ||
1001
+ httpStatus === 503 ||
1002
+ httpStatus === 504);
1003
+ }
724
1004
  async function stageLog(messaging, internalPayload) {
725
1005
  const fcmEvent = createFcmEvent(internalPayload, await messaging.firebaseDependencies.installations.getId());
726
1006
  createAndEnqueueLogEvent(messaging, fcmEvent, internalPayload.productId);
1007
+ startLoggingService(messaging);
727
1008
  }
728
1009
  function createFcmEvent(internalPayload, fid) {
729
1010
  const fcmEvent = {};
@@ -777,6 +1058,14 @@ function buildComplianceData(productId) {
777
1058
  };
778
1059
  return complianceData;
779
1060
  }
1061
+ function _createLogRequest(logEventQueue) {
1062
+ const logRequest = {};
1063
+ /* eslint-disable camelcase */
1064
+ logRequest.log_source = FCM_LOG_SOURCE.toString();
1065
+ logRequest.log_event = logEventQueue;
1066
+ /* eslint-enable camelcase */
1067
+ return logRequest;
1068
+ }
780
1069
  function _mergeStrings(s1, s2) {
781
1070
  const resultArray = [];
782
1071
  for (let i = 0; i < s1.length; i++) {
@@ -823,7 +1112,11 @@ async function onPush(event, messaging) {
823
1112
  // Failed to get parsed MessagePayload from the PushEvent. Skip handling the push.
824
1113
  return;
825
1114
  }
826
- // log to Firelog with user consent
1115
+ /*
1116
+ * Log to Firelog based on user consent. Rather than calling startLoggingService once when
1117
+ * deliveryMetricsExportedToBigQueryEnabled is toggled, we now call stageLog for every received push.
1118
+ * This ensures the first telemetry event is uploaded immediately upon enabling the flag, simplifying debugging.
1119
+ */
827
1120
  if (messaging.deliveryMetricsExportedToBigQueryEnabled) {
828
1121
  await stageLog(messaging, internalPayload);
829
1122
  }
@@ -1051,8 +1344,25 @@ class MessagingService {
1051
1344
  this.deliveryMetricsExportedToBigQueryEnabled = false;
1052
1345
  this.onBackgroundMessageHandler = null;
1053
1346
  this.onMessageHandler = null;
1347
+ /** Observer for the event that the app instance is registered with FCM via Firebase Installation ID (FID). */
1348
+ this.onRegisteredHandler = null;
1349
+ /** Observer for the event that the app instance is unregistered from FCM (FID no longer active). */
1350
+ this.onUnregisteredHandler = null;
1351
+ /**
1352
+ * Serializes the FID get + compare + notify step so concurrent register() calls
1353
+ * do not race each other.
1354
+ */
1355
+ this._registerNotifyChain = Promise.resolve();
1356
+ /** Unsubscribe from Installations `onIdChange` when messaging is deleted. */
1357
+ this._fidChangeUnsubscribe = null;
1054
1358
  this.logEvents = [];
1055
- this.isLogServiceStarted = false;
1359
+ /**
1360
+ * Single source of truth for the logging loop lifecycle.
1361
+ *
1362
+ * `scheduled` holds the active timer id; `flushing` indicates an async dispatch
1363
+ * is in progress (prevents duplicate starts); `stopped` means idle.
1364
+ */
1365
+ this.logQueue = { state: 'stopped' };
1056
1366
  const appConfig = extractAppConfig(app);
1057
1367
  this.firebaseDependencies = {
1058
1368
  app,
@@ -1062,6 +1372,14 @@ class MessagingService {
1062
1372
  };
1063
1373
  }
1064
1374
  _delete() {
1375
+ if (this._fidChangeUnsubscribe) {
1376
+ this._fidChangeUnsubscribe();
1377
+ this._fidChangeUnsubscribe = null;
1378
+ }
1379
+ if (this.logQueue.state === 'scheduled') {
1380
+ clearTimeout(this.logQueue.timerId);
1381
+ }
1382
+ this.logQueue = { state: 'stopped' };
1065
1383
  return Promise.resolve();
1066
1384
  }
1067
1385
  }
@@ -1164,6 +1482,72 @@ function onBackgroundMessage$1(messaging, nextOrObserver) {
1164
1482
  };
1165
1483
  }
1166
1484
 
1485
+ /**
1486
+ * @license
1487
+ * Copyright 2020 Google LLC
1488
+ *
1489
+ * Licensed under the Apache License, Version 2.0 (the "License");
1490
+ * you may not use this file except in compliance with the License.
1491
+ * You may obtain a copy of the License at
1492
+ *
1493
+ * http://www.apache.org/licenses/LICENSE-2.0
1494
+ *
1495
+ * Unless required by applicable law or agreed to in writing, software
1496
+ * distributed under the License is distributed on an "AS IS" BASIS,
1497
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1498
+ * See the License for the specific language governing permissions and
1499
+ * limitations under the License.
1500
+ */
1501
+ /**
1502
+ * Subscribes to an event that the app instance is registered with FCM via Firebase Installation ID (FID).
1503
+ * Use the FID passed to the callback to upload it to your application server.
1504
+ *
1505
+ * @param messaging - The {@link MessagingService} instance.
1506
+ * @param nextOrObserver - A function or observer object called when an FID is registered.
1507
+ * @returns Unsubscribe function to stop listening.
1508
+ */
1509
+ function onRegistered$1(messaging, nextOrObserver) {
1510
+ messaging.onRegisteredHandler = nextOrObserver;
1511
+ return () => {
1512
+ if (messaging.onRegisteredHandler === nextOrObserver) {
1513
+ messaging.onRegisteredHandler = null;
1514
+ }
1515
+ };
1516
+ }
1517
+
1518
+ /**
1519
+ * @license
1520
+ * Copyright 2026 Google LLC
1521
+ *
1522
+ * Licensed under the Apache License, Version 2.0 (the "License");
1523
+ * you may not use this file except in compliance with the License.
1524
+ * You may obtain a copy of the License at
1525
+ *
1526
+ * http://www.apache.org/licenses/LICENSE-2.0
1527
+ *
1528
+ * Unless required by applicable law or agreed to in writing, software
1529
+ * distributed under the License is distributed on an "AS IS" BASIS,
1530
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1531
+ * See the License for the specific language governing permissions and
1532
+ * limitations under the License.
1533
+ */
1534
+ /**
1535
+ * Subscribes to an event that the app instance is unregistered from FCM so the FID is no longer active.
1536
+ * Use this to notify your backend to remove this FID to prevent 404 errors on send.
1537
+ *
1538
+ * @param messaging - The {@link MessagingService} instance.
1539
+ * @param nextOrObserver - A function or observer object called with the unregistered FID.
1540
+ * @returns Unsubscribe function to stop listening.
1541
+ */
1542
+ function onUnregistered$1(messaging, nextOrObserver) {
1543
+ messaging.onUnregisteredHandler = nextOrObserver;
1544
+ return () => {
1545
+ if (messaging.onUnregisteredHandler === nextOrObserver) {
1546
+ messaging.onUnregisteredHandler = null;
1547
+ }
1548
+ };
1549
+ }
1550
+
1167
1551
  /**
1168
1552
  * @license
1169
1553
  * Copyright 2020 Google LLC
@@ -1181,8 +1565,14 @@ function onBackgroundMessage$1(messaging, nextOrObserver) {
1181
1565
  * limitations under the License.
1182
1566
  */
1183
1567
  function _setDeliveryMetricsExportedToBigQueryEnabled(messaging, enable) {
1184
- messaging.deliveryMetricsExportedToBigQueryEnabled =
1185
- enable;
1568
+ const messagingService = messaging;
1569
+ messagingService.deliveryMetricsExportedToBigQueryEnabled = enable;
1570
+ if (enable) {
1571
+ startLoggingService(messagingService);
1572
+ }
1573
+ else {
1574
+ stopLoggingServiceAndClearQueue(messagingService);
1575
+ }
1186
1576
  }
1187
1577
 
1188
1578
  /**
@@ -1232,7 +1622,7 @@ function getMessagingInSw(app = getApp()) {
1232
1622
  * @param nextOrObserver - This function, or observer object with `next` defined, is called when a
1233
1623
  * message is received and the app is currently in the background.
1234
1624
  *
1235
- * @returns To stop listening for messages execute this returned function
1625
+ * @returns To stop listening for messages execute this returned function.
1236
1626
  *
1237
1627
  * @public
1238
1628
  */
@@ -1240,6 +1630,35 @@ function onBackgroundMessage(messaging, nextOrObserver) {
1240
1630
  messaging = getModularInstance(messaging);
1241
1631
  return onBackgroundMessage$1(messaging, nextOrObserver);
1242
1632
  }
1633
+ /**
1634
+ * Subscribes to an event that the app instance is registered with FCM via Firebase Installation ID (FID).
1635
+ * Use the FID passed to the callback to upload it to your application server. When you receive an FID
1636
+ * after calling {@link register}, instruct your backend to remove any legacy token for this instance.
1637
+ *
1638
+ * @param messaging - The {@link Messaging} instance.
1639
+ * @param nextOrObserver - A function or observer object called when an FID is registered.
1640
+ * @returns Unsubscribe function to stop listening.
1641
+ *
1642
+ * @public
1643
+ */
1644
+ function onRegistered(messaging, nextOrObserver) {
1645
+ messaging = getModularInstance(messaging);
1646
+ return onRegistered$1(messaging, nextOrObserver);
1647
+ }
1648
+ /**
1649
+ * Subscribes to an event that the app instance is unregistered from FCM (FID no longer active).
1650
+ * Use this to notify your backend to remove this FID to prevent 404 errors on send.
1651
+ *
1652
+ * @param messaging - The {@link Messaging} instance.
1653
+ * @param nextOrObserver - A function or observer object called with the unregistered FID.
1654
+ * @returns Unsubscribe function to stop listening.
1655
+ *
1656
+ * @public
1657
+ */
1658
+ function onUnregistered(messaging, nextOrObserver) {
1659
+ messaging = getModularInstance(messaging);
1660
+ return onUnregistered$1(messaging, nextOrObserver);
1661
+ }
1243
1662
  /**
1244
1663
  * Enables or disables Firebase Cloud Messaging message delivery metrics export to BigQuery. By
1245
1664
  * default, message delivery metrics are not exported to BigQuery. Use this method to enable or
@@ -1274,5 +1693,5 @@ function experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, enab
1274
1693
  */
1275
1694
  registerMessagingInSw();
1276
1695
 
1277
- export { experimentalSetDeliveryMetricsExportedToBigQueryEnabled, getMessagingInSw as getMessaging, isSwSupported as isSupported, onBackgroundMessage };
1696
+ export { experimentalSetDeliveryMetricsExportedToBigQueryEnabled, getMessagingInSw as getMessaging, isSwSupported as isSupported, onBackgroundMessage, onRegistered, onUnregistered };
1278
1697
  //# sourceMappingURL=index.sw.esm.js.map