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