vendor: 17,124 bytes, lines 1-444
1(function () { 2 'use strict'; 3 4 const getDefaultsFromPostinstall = () => (undefined); 5 6 /** 7 * @license 8 * Copyright 2017 Google LLC 9 * 10 * Licensed under the Apache License, Version 2.0 (the "License"); 11 * you may not use this file except in compliance with the License. 12 * You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, software 17 * distributed under the License is distributed on an "AS IS" BASIS, 18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 * See the License for the specific language governing permissions and 20 * limitations under the License. 21 */ 22 const stringToByteArray$1 = function (str) { 23 // TODO(user): Use native implementations if/when available 24 const out = []; 25 let p = 0; 26 for (let i = 0; i < str.length; i++) { 27 let c = str.charCodeAt(i); 28 if (c < 128) { 29 out[p++] = c; 30 } 31 else if (c < 2048) { 32 out[p++] = (c >> 6) | 192; 33 out[p++] = (c & 63) | 128; 34 } 35 else if ((c & 0xfc00) === 0xd800 && 36 i + 1 < str.length && 37 (str.charCodeAt(i + 1) & 0xfc00) === 0xdc00) { 38 // Surrogate Pair 39 c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff); 40 out[p++] = (c >> 18) | 240; 41 out[p++] = ((c >> 12) & 63) | 128; 42 out[p++] = ((c >> 6) & 63) | 128; 43 out[p++] = (c & 63) | 128; 44 } 45 else { 46 out[p++] = (c >> 12) | 224; 47 out[p++] = ((c >> 6) & 63) | 128; 48 out[p++] = (c & 63) | 128; 49 } 50 } 51 return out; 52 }; 53 /** 54 * Turns an array of numbers into the string given by the concatenation of the 55 * characters to which the numbers correspond. 56 * @param bytes Array of numbers representing characters. 57 * @return Stringification of the array. 58 */ 59 const byteArrayToString = function (bytes) { 60 // TODO(user): Use native implementations if/when available 61 const out = []; 62 let pos = 0, c = 0; 63 while (pos < bytes.length) { 64 const c1 = bytes[pos++]; 65 if (c1 < 128) { 66 out[c++] = String.fromCharCode(c1); 67 } 68 else if (c1 > 191 && c1 < 224) { 69 const c2 = bytes[pos++]; 70 out[c++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63)); 71 } 72 else if (c1 > 239 && c1 < 365) { 73 // Surrogate Pair 74 const c2 = bytes[pos++]; 75 const c3 = bytes[pos++]; 76 const c4 = bytes[pos++]; 77 const u = (((c1 & 7) << 18) | ((c2 & 63) << 12) | ((c3 & 63) << 6) | (c4 & 63)) - 78 0x10000; 79 out[c++] = String.fromCharCode(0xd800 + (u >> 10)); 80 out[c++] = String.fromCharCode(0xdc00 + (u & 1023)); 81 } 82 else { 83 const c2 = bytes[pos++]; 84 const c3 = bytes[pos++]; 85 out[c++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 86 } 87 } 88 return out.join(''); 89 }; 90 // We define it as an object literal instead of a class because a class compiled down to es5 can't 91 // be treeshaked. https://github.com/rollup/rollup/issues/1691 92 // Static lookup maps, lazily populated by init_() 93 // TODO(dlarocque): Define this as a class, since we no longer target ES5. 94 const base64 = { 95 /** 96 * Maps bytes to characters. 97 */ 98 byteToCharMap_: null, 99 /** 100 * Maps characters to bytes. 101 */ 102 charToByteMap_: null, 103 /** 104 * Maps bytes to websafe characters. 105 * @private 106 */ 107 byteToCharMapWebSafe_: null, 108 /** 109 * Maps websafe characters to bytes. 110 * @private 111 */ 112 charToByteMapWebSafe_: null, 113 /** 114 * Our default alphabet, shared between 115 * ENCODED_VALS and ENCODED_VALS_WEBSAFE 116 */ 117 ENCODED_VALS_BASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789', 118 /** 119 * Our default alphabet. Value 64 (=) is special; it means "nothing." 120 */ 121 get ENCODED_VALS() { 122 return this.ENCODED_VALS_BASE + '+/='; 123 }, 124 /** 125 * Our websafe alphabet. 126 */ 127 get ENCODED_VALS_WEBSAFE() { 128 return this.ENCODED_VALS_BASE + '-_.'; 129 }, 130 /** 131 * Whether this browser supports the atob and btoa functions. This extension 132 * started at Mozilla but is now implemented by many browsers. We use the 133 * ASSUME_* variables to avoid pulling in the full useragent detection library 134 * but still allowing the standard per-browser compilations. 135 * 136 */ 137 HAS_NATIVE_SUPPORT: typeof atob === 'function', 138 /** 139 * Base64-encode an array of bytes. 140 * 141 * @param input An array of bytes (numbers with 142 * value in [0, 255]) to encode. 143 * @param webSafe Boolean indicating we should use the 144 * alternative alphabet. 145 * @return The base64 encoded string. 146 */ 147 encodeByteArray(input, webSafe) { 148 if (!Array.isArray(input)) { 149 throw Error('encodeByteArray takes an array as a parameter'); 150 } 151 this.init_(); 152 const byteToCharMap = webSafe 153 ? this.byteToCharMapWebSafe_ 154 : this.byteToCharMap_; 155 const output = []; 156 for (let i = 0; i < input.length; i += 3) { 157 const byte1 = input[i]; 158 const haveByte2 = i + 1 < input.length; 159 const byte2 = haveByte2 ? input[i + 1] : 0; 160 const haveByte3 = i + 2 < input.length; 161 const byte3 = haveByte3 ? input[i + 2] : 0; 162 const outByte1 = byte1 >> 2; 163 const outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); 164 let outByte3 = ((byte2 & 0x0f) << 2) | (byte3 >> 6); 165 let outByte4 = byte3 & 0x3f; 166 if (!haveByte3) { 167 outByte4 = 64; 168 if (!haveByte2) { 169 outByte3 = 64; 170 } 171 } 172 output.push(byteToCharMap[outByte1], byteToCharMap[outByte2], byteToCharMap[outByte3], byteToCharMap[outByte4]); 173 } 174 return output.join(''); 175 }, 176 /** 177 * Base64-encode a string. 178 * 179 * @param input A string to encode. 180 * @param webSafe If true, we should use the 181 * alternative alphabet. 182 * @return The base64 encoded string. 183 */ 184 encodeString(input, webSafe) { 185 // Shortcut for Mozilla browsers that implement 186 // a native base64 encoder in the form of "btoa/atob" 187 if (this.HAS_NATIVE_SUPPORT && !webSafe) { 188 return btoa(input); 189 } 190 return this.encodeByteArray(stringToByteArray$1(input), webSafe); 191 }, 192 /** 193 * Base64-decode a string. 194 * 195 * @param input to decode. 196 * @param webSafe True if we should use the 197 * alternative alphabet. 198 * @return string representing the decoded value. 199 */ 200 decodeString(input, webSafe) { 201 // Shortcut for Mozilla browsers that implement 202 // a native base64 encoder in the form of "btoa/atob" 203 if (this.HAS_NATIVE_SUPPORT && !webSafe) { 204 return atob(input); 205 } 206 return byteArrayToString(this.decodeStringToByteArray(input, webSafe)); 207 }, 208 /** 209 * Base64-decode a string. 210 * 211 * In base-64 decoding, groups of four characters are converted into three 212 * bytes. If the encoder did not apply padding, the input length may not 213 * be a multiple of 4. 214 * 215 * In this case, the last group will have fewer than 4 characters, and 216 * padding will be inferred. If the group has one or two characters, it decodes 217 * to one byte. If the group has three characters, it decodes to two bytes. 218 * 219 * @param input Input to decode. 220 * @param webSafe True if we should use the web-safe alphabet. 221 * @return bytes representing the decoded value. 222 */ 223 decodeStringToByteArray(input, webSafe) { 224 this.init_(); 225 const charToByteMap = webSafe 226 ? this.charToByteMapWebSafe_ 227 : this.charToByteMap_; 228 const output = []; 229 for (let i = 0; i < input.length;) { 230 const byte1 = charToByteMap[input.charAt(i++)]; 231 const haveByte2 = i < input.length; 232 const byte2 = haveByte2 ? charToByteMap[input.charAt(i)] : 0; 233 ++i; 234 const haveByte3 = i < input.length; 235 const byte3 = haveByte3 ? charToByteMap[input.charAt(i)] : 64; 236 ++i; 237 const haveByte4 = i < input.length; 238 const byte4 = haveByte4 ? charToByteMap[input.charAt(i)] : 64; 239 ++i; 240 if (byte1 == null || byte2 == null || byte3 == null || byte4 == null) { 241 throw new DecodeBase64StringError(); 242 } 243 const outByte1 = (byte1 << 2) | (byte2 >> 4); 244 output.push(outByte1); 245 if (byte3 !== 64) { 246 const outByte2 = ((byte2 << 4) & 0xf0) | (byte3 >> 2); 247 output.push(outByte2); 248 if (byte4 !== 64) { 249 const outByte3 = ((byte3 << 6) & 0xc0) | byte4; 250 output.push(outByte3); 251 } 252 } 253 } 254 return output; 255 }, 256 /** 257 * Lazy static initialization function. Called before 258 * accessing any of the static map variables. 259 * @private 260 */ 261 init_() { 262 if (!this.byteToCharMap_) { 263 this.byteToCharMap_ = {}; 264 this.charToByteMap_ = {}; 265 this.byteToCharMapWebSafe_ = {}; 266 this.charToByteMapWebSafe_ = {}; 267 // We want quick mappings back and forth, so we precompute two maps. 268 for (let i = 0; i < this.ENCODED_VALS.length; i++) { 269 this.byteToCharMap_[i] = this.ENCODED_VALS.charAt(i); 270 this.charToByteMap_[this.byteToCharMap_[i]] = i; 271 this.byteToCharMapWebSafe_[i] = this.ENCODED_VALS_WEBSAFE.charAt(i); 272 this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i; 273 // Be forgiving when decoding and correctly decode both encodings. 274 if (i >= this.ENCODED_VALS_BASE.length) { 275 this.charToByteMap_[this.ENCODED_VALS_WEBSAFE.charAt(i)] = i; 276 this.charToByteMapWebSafe_[this.ENCODED_VALS.charAt(i)] = i; 277 } 278 } 279 } 280 } 281 }; 282 /** 283 * An error encountered while decoding base64 string. 284 */ 285 class DecodeBase64StringError extends Error { 286 constructor() { 287 super(...arguments); 288 this.name = 'DecodeBase64StringError'; 289 } 290 } 291 /** 292 * URL-safe base64 encoding 293 */ 294 const base64Encode = function (str) { 295 const utf8Bytes = stringToByteArray$1(str); 296 return base64.encodeByteArray(utf8Bytes, true); 297 }; 298 /** 299 * URL-safe base64 encoding (without "." padding in the end). 300 * e.g. Used in JSON Web Token (JWT) parts. 301 */ 302 const base64urlEncodeWithoutPadding = function (str) { 303 // Use base64url encoding and remove padding in the end (dot characters). 304 return base64Encode(str).replace(/\./g, ''); 305 }; 306 /** 307 * URL-safe base64 decoding 308 * 309 * NOTE: DO NOT use the global atob() function - it does NOT support the 310 * base64Url variant encoding. 311 * 312 * @param str To be decoded 313 * @return Decoded result, if possible 314 */ 315 const base64Decode = function (str) { 316 try { 317 return base64.decodeString(str, true); 318 } 319 catch (e) { 320 console.error('base64Decode failed: ', e); 321 } 322 return null; 323 }; 324 325 /** 326 * @license 327 * Copyright 2022 Google LLC 328 * 329 * Licensed under the Apache License, Version 2.0 (the "License"); 330 * you may not use this file except in compliance with the License. 331 * You may obtain a copy of the License at 332 * 333 * http://www.apache.org/licenses/LICENSE-2.0 334 * 335 * Unless required by applicable law or agreed to in writing, software 336 * distributed under the License is distributed on an "AS IS" BASIS, 337 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 338 * See the License for the specific language governing permissions and 339 * limitations under the License. 340 */ 341 /** 342 * Polyfill for `globalThis` object. 343 * @returns the `globalThis` object for the given environment. 344 * @public 345 */ 346 function getGlobal() { 347 if (typeof self !== 'undefined') { 348 return self; 349 } 350 if (typeof window !== 'undefined') { 351 return window; 352 } 353 if (typeof global !== 'undefined') { 354 return global; 355 } 356 throw new Error('Unable to locate global object.'); 357 } 358 359 /** 360 * @license 361 * Copyright 2022 Google LLC 362 * 363 * Licensed under the Apache License, Version 2.0 (the "License"); 364 * you may not use this file except in compliance with the License. 365 * You may obtain a copy of the License at 366 * 367 * http://www.apache.org/licenses/LICENSE-2.0 368 * 369 * Unless required by applicable law or agreed to in writing, software 370 * distributed under the License is distributed on an "AS IS" BASIS, 371 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 372 * See the License for the specific language governing permissions and 373 * limitations under the License. 374 */ 375 const getDefaultsFromGlobal = () => getGlobal().__FIREBASE_DEFAULTS__; 376 /** 377 * Attempt to read defaults from a JSON string provided to 378 * process(.)env(.)__FIREBASE_DEFAULTS__ or a JSON file whose path is in 379 * process(.)env(.)__FIREBASE_DEFAULTS_PATH__ 380 * The dots are in parens because certain compilers (Vite?) cannot 381 * handle seeing that variable in comments. 382 * See https://github.com/firebase/firebase-js-sdk/issues/6838 383 */ 384 const getDefaultsFromEnvVariable = () => { 385 if (typeof process === 'undefined' || typeof process.env === 'undefined') { 386 return; 387 } 388 const defaultsJsonString = process.env.__FIREBASE_DEFAULTS__; 389 if (defaultsJsonString) { 390 return JSON.parse(defaultsJsonString); 391 } 392 }; 393 const getDefaultsFromCookie = () => { 394 if (typeof document === 'undefined') { 395 return; 396 } 397 let match; 398 try { 399 match = document.cookie.match(/__FIREBASE_DEFAULTS__=([^;]+)/); 400 } 401 catch (e) { 402 // Some environments such as Angular Universal SSR have a 403 // `document` object but error on accessing `document.cookie`. 404 return; 405 } 406 const decoded = match && base64Decode(match[1]); 407 return decoded && JSON.parse(decoded); 408 }; 409 /** 410 * Get the __FIREBASE_DEFAULTS__ object. It checks in order: 411 * (1) if such an object exists as a property of `globalThis` 412 * (2) if such an object was provided on a shell environment variable 413 * (3) if such an object exists in a cookie 414 * @public 415 */ 416 const getDefaults = () => { 417 try { 418 return (getDefaultsFromPostinstall() || 419 getDefaultsFromGlobal() || 420 getDefaultsFromEnvVariable() || 421 getDefaultsFromCookie()); 422 } 423 catch (e) { 424 /** 425 * Catch-all for being unable to get __FIREBASE_DEFAULTS__ due 426 * to any environment case we have not accounted for. Log to 427 * info instead of swallowing so we can find these unknown cases 428 * and add paths for them if needed. 429 */ 430 console.info(`Unable to get __FIREBASE_DEFAULTS__ due to: ${e}`); 431 return; 432 } 433 }; 434 /** 435 * Returns Firebase app config stored in the __FIREBASE_DEFAULTS__ object. 436 * @public 437 */ 438 const getDefaultAppConfig = () => getDefaults()?.config; 439 440 /** 441 * @license 442 * Copyright 2017 Google LLC 443 * 444 * Licensed under the Apache License, Version 2.0 (the "License");
445 * you may not use this file except in compliance with the License. 446 * You may obtain a copy of the License at 447 * 448 * http://www.apache.org/licenses/LICENSE-2.0 449 * 450 * Unless required by applicable law or agreed to in writing, software 451 * distributed under the License is distributed on an "AS IS" BASIS, 452 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 453 * See the License for the specific language governing permissions and 454 * limitations under the License. 455 */ 456 class Deferred { 457 constructor() { 458 this.reject = () => { }; 459 this.resolve = () => { }; 460 this.promise = new Promise((resolve, reject) => { 461 this.resolve = resolve; 462 this.reject = reject; 463 }); 464 } 465 /** 466 * Our API internals are not promisified and cannot because our callback APIs have subtle expectations around 467 * invoking promises inline, which Promises are forbidden to do. This method accepts an optional node-style callback 468 * and returns a node-style callback which will resolve or reject the Deferred's promise. 469 */ 470 wrapCallback(callback) { 471 return (error, value) => { 472 if (error) { 473 this.reject(error); 474 } 475 else { 476 this.resolve(value); 477 } 478 if (typeof callback === 'function') { 479 // Attaching noop handler just in case developer wasn't expecting 480 // promises 481 this.promise.catch(() => { }); 482 // Some of our callbacks don't expect a value and our own tests 483 // assert that the parameter length is 1 484 if (callback.length === 1) { 485 callback(error); 486 } 487 else { 488 callback(error, value); 489 } 490 } 491 }; 492 } 493 } 494 /** 495 * This method checks if indexedDB is supported by current browser/service worker context 496 * @return true if indexedDB is supported by current browser/service worker context 497 */ 498 function isIndexedDBAvailable() { 499 try { 500 return typeof indexedDB === 'object'; 501 } 502 catch (e) { 503 return false; 504 } 505 } 506 /** 507 * This method validates browser/sw context for indexedDB by opening a dummy indexedDB database and reject 508 * if errors occur during the database open operation. 509 * 510 * @throws exception if current browser/sw context can't run idb.open (ex: Safari iframe, Firefox 511 * private browsing) 512 */ 513 function validateIndexedDBOpenable() { 514 return new Promise((resolve, reject) => { 515 try { 516 let preExist = true; 517 const DB_CHECK_NAME = 'validate-browser-context-for-indexeddb-analytics-module'; 518 const request = self.indexedDB.open(DB_CHECK_NAME); 519 request.onsuccess = () => { 520 request.result.close(); 521 // delete database only when it doesn't pre-exist 522 if (!preExist) { 523 self.indexedDB.deleteDatabase(DB_CHECK_NAME); 524 } 525 resolve(true); 526 }; 527 request.onupgradeneeded = () => { 528 preExist = false; 529 }; 530 request.onerror = () => { 531 reject(request.error?.message || ''); 532 }; 533 } 534 catch (error) { 535 reject(error); 536 } 537 }); 538 } 539 540 /** 541 * @license 542 * Copyright 2017 Google LLC 543 * 544 * Licensed under the Apache License, Version 2.0 (the "License"); 545 * you may not use this file except in compliance with the License. 546 * You may obtain a copy of the License at 547 * 548 * http://www.apache.org/licenses/LICENSE-2.0 549 * 550 * Unless required by applicable law or agreed to in writing, softw
550are 551 * distributed under the License is distributed on an "AS IS" BASIS, 552 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 553 * See the License for the specific language governing permissions and 554 * limitations under the License. 555 */ 556 /** 557 * @fileoverview Standardized Firebase Error. 558 * 559 * Usage: 560 * 561 * // TypeScript string literals for type-safe codes 562 * type Err = 563 * 'unknown' | 564 * 'object-not-found' 565 * ; 566 * 567 * // Closure enum for type-safe error codes 568 * // at-enum {string} 569 * var Err = { 570 * UNKNOWN: 'unknown', 571 * OBJECT_NOT_FOUND: 'object-not-found', 572 * } 573 * 574 * let errors: Map<Err, string> = { 575 * 'generic-error': "Unknown error", 576 * 'file-not-found': "Could not find file: {$file}", 577 * }; 578 * 579 * // Type-safe function - must pass a valid error code as param. 580 * let error = new ErrorFactory<Err>('service', 'Service', errors); 581 * 582 * ... 583 * throw error.create(Err.GENERIC); 584 * ... 585 * throw error.create(Err.FILE_NOT_FOUND, {'file': fileName}); 586 * ... 587 * // Service: Could not file file: foo.txt (service/file-not-found). 588 * 589 * catch (e) { 590 * assert(e.message === "Could not find file: foo.txt."); 591 * if ((e as FirebaseError)?.code === 'service/file-not-found') { 592 * console.log("Could not read file: " + e['file']); 593 * } 594 * } 595 */ 596 const ERROR_NAME = 'FirebaseError'; 597 // Based on code from: 598 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types 599 class FirebaseError extends Error { 600 constructor( 601 /** The error code for this error. */ 602 code, message, 603 /** Custom data for this error. */ 604 customData) { 605 super(message); 606 this.code = code; 607 this.customData = customData; 608 /** The custom name for all FirebaseErrors. */ 609 this.name = ERROR_NAME; 610 // Fix For ES5 611 // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work 612 // TODO(dlarocque): Replace this with `new.target`: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget 613 // which we can now use since we no longer target ES5. 614 Object.setPrototypeOf(this, FirebaseError.prototype); 615 // Maintains proper stack trace for where our error was thrown. 616 // Only available on V8. 617 if (Error.captureStackTrace) { 618 Error.captureStackTrace(this, ErrorFactory.prototype.create); 619 } 620 } 621 } 622 class ErrorFactory { 623 constructor(service, serviceName, errors) { 624 this.service = service; 625 this.serviceName = serviceName; 626 this.errors = errors; 627 } 628 create(code, ...data) { 629 const customData = data[0] || {}; 630 const fullCode = `${this.service}/${code}`; 631 const template = this.errors[code]; 632 const message = template ? replaceTemplate(template, customData) : 'Error'; 633 // Service Name: Error message (service/code). 634 const fullMessage = `${this.serviceName}: ${message} (${fullCode}).`; 635 const error = new FirebaseError(fullCode, fullMessage, customData); 636 return error; 637 } 638 } 639 function replaceTemplate(template, data) { 640 return template.replace(PATTERN, (_, key) => { 641 const value = data[key]; 642 return value != null ? String(value) : `<${key}?>`; 643 }); 644 } 645 const PATTERN = /\{\$([^}]+)}/g; 646 /** 647 * Deep equal two objects. Support Arrays and Objects. 648 */ 649 function deepEqual(a, b) { 650 if (a === b) { 651 return true; 652 } 653 const aKeys = Object.keys(a); 654 const bKeys = Object.keys(b); 655 for (const k of aKeys) { 656 if (!bKeys.includes(k)) { 657 return false;
vendor: 16,596 bytes, lines 658-1045
658 } 659 const aProp = a[k]; 660 const bProp = b[k]; 661 if (isObject(aProp) && isObject(bProp)) { 662 if (!deepEqual(aProp, bProp)) { 663 return false; 664 } 665 } 666 else if (aProp !== bProp) { 667 return false; 668 } 669 } 670 for (const k of bKeys) { 671 if (!aKeys.includes(k)) { 672 return false; 673 } 674 } 675 return true; 676 } 677 function isObject(thing) { 678 return thing !== null && typeof thing === 'object'; 679 } 680 681 /** 682 * @license 683 * Copyright 2021 Google LLC 684 * 685 * Licensed under the Apache License, Version 2.0 (the "License"); 686 * you may not use this file except in compliance with the License. 687 * You may obtain a copy of the License at 688 * 689 * http://www.apache.org/licenses/LICENSE-2.0 690 * 691 * Unless required by applicable law or agreed to in writing, software 692 * distributed under the License is distributed on an "AS IS" BASIS, 693 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 694 * See the License for the specific language governing permissions and 695 * limitations under the License. 696 */ 697 function getModularInstance(service) { 698 if (service && service._delegate) { 699 return service._delegate; 700 } 701 else { 702 return service; 703 } 704 } 705 706 /** 707 * Component for service name T, e.g. `auth`, `auth-internal` 708 */ 709 class Component { 710 /** 711 * 712 * @param name The public service name, e.g. app, auth, firestore, database 713 * @param instanceFactory Service factory responsible for creating the public interface 714 * @param type whether the service provided by the component is public or private 715 */ 716 constructor(name, instanceFactory, type) { 717 this.name = name; 718 this.instanceFactory = instanceFactory; 719 this.type = type; 720 this.multipleInstances = false; 721 /** 722 * Properties to be added to the service namespace 723 */ 724 this.serviceProps = {}; 725 this.instantiationMode = "LAZY" /* InstantiationMode.LAZY */; 726 this.onInstanceCreated = null; 727 } 728 setInstantiationMode(mode) { 729 this.instantiationMode = mode; 730 return this; 731 } 732 setMultipleInstances(multipleInstances) { 733 this.multipleInstances = multipleInstances; 734 return this; 735 } 736 setServiceProps(props) { 737 this.serviceProps = props; 738 return this; 739 } 740 setInstanceCreatedCallback(callback) { 741 this.onInstanceCreated = callback; 742 return this; 743 } 744 } 745 746 /** 747 * @license 748 * Copyright 2019 Google LLC 749 * 750 * Licensed under the Apache License, Version 2.0 (the "License"); 751 * you may not use this file except in compliance with the License. 752 * You may obtain a copy of the License at 753 * 754 * http://www.apache.org/licenses/LICENSE-2.0 755 * 756 * Unless required by applicable law or agreed to in writing, software 757 * distributed under the License is distributed on an "AS IS" BASIS, 758 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 759 * See the License for the specific language governing permissions and 760 * limitations under the License. 761 */ 762 const DEFAULT_ENTRY_NAME$1 = '[DEFAULT]'; 763 764 /** 765 * @license 766 * Copyright 2019 Google LLC 767 * 768 * Licensed under the Apache License, Version 2.0 (the "License"); 769 * you may not use this file except in compliance with the License. 770 * You may obtain a copy of the License at 771 * 772 * http://www.apache.org/licenses/LICENSE-2.0 773 * 774 * Unless required by applicable law or agreed to in writing, software 775 * distributed under the License is distributed on an "AS IS" BASIS, 776 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 777 * See the License for the specific language governing permissions and 778 * limitations under the License. 779 */ 780 /** 781 * Provider for instance for service name T, e.g. 'auth', 'auth-internal' 782 * NameServiceMapping[T] is an alias for the type of the instance 783 */ 784 class Provider { 785 constructor(name, container) { 786 this.name = name; 787 this.container = container; 788 this.component = null; 789 this.instances = new Map(); 790 this.instancesDeferred = new Map(); 791 this.instancesOptions = new Map(); 792 this.onInitCallbacks = new Map(); 793 } 794 /** 795 * @param identifier A provider can provide multiple instances of a service 796 * if this.component.multipleInstances is true. 797 */ 798 get(identifier) { 799 // if multipleInstances is not supported, use the default name 800 const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier); 801 if (!this.instancesDeferred.has(normalizedIdentifier)) { 802 const deferred = new Deferred(); 803 this.instancesDeferred.set(normalizedIdentifier, deferred); 804 if (this.isInitialized(normalizedIdentifier) || 805 this.shouldAutoInitialize()) { 806 // initialize the service if it can be auto-initialized 807 try { 808 const instance = this.getOrInitializeService({ 809 instanceIdentifier: normalizedIdentifier 810 }); 811 if (instance) { 812 deferred.resolve(instance); 813 } 814 } 815 catch (e) { 816 // when the instance factory throws an exception during get(), it should not cause 817 // a fatal error. We just return the unresolved promise in this case. 818 } 819 } 820 } 821 return this.instancesDeferred.get(normalizedIdentifier).promise; 822 } 823 getImmediate(options) { 824 // if multipleInstances is not supported, use the default name 825 const normalizedIdentifier = this.normalizeInstanceIdentifier(options?.identifier); 826 const optional = options?.optional ?? false; 827 if (this.isInitialized(normalizedIdentifier) || 828 this.shouldAutoInitialize()) { 829 try { 830 return this.getOrInitializeService({ 831 instanceIdentifier: normalizedIdentifier 832 }); 833 } 834 catch (e) { 835 if (optional) { 836 return null; 837 } 838 else { 839 throw e; 840 } 841 } 842 } 843 else { 844 // In case a component is not initialized and should/cannot be auto-initialized at the moment, return null if the optional flag is set, or throw 845 if (optional) { 846 return null; 847 } 848 else { 849 throw Error(`Service ${this.name} is not available`); 850 } 851 } 852 } 853 getComponent() { 854 return this.component; 855 } 856 setComponent(component) { 857 if (component.name !== this.name) { 858 throw Error(`Mismatching Component ${component.name} for Provider ${this.name}.`); 859 } 860 if (this.component) { 861 throw Error(`Component for ${this.name} has already been provided`); 862 } 863 this.component = component; 864 // return early without attempting to initialize the component if the component requires explicit initialization (calling `Provider.initialize()`) 865 if (!this.shouldAutoInitialize()) { 866 return; 867 } 868 // if the service is eager, initialize the default instance 869 if (isComponentEager(component)) { 870 try { 871 this.getOrInitializeService({ instanceIdentifier: DEFAULT_ENTRY_NAME$1 }); 872 } 873 catch (e) { 874 // when the instance factory for an eager Component throws an exception during the eager 875 // initialization, it should not cause a fatal error. 876 // TODO: Investigate if we need to make it configurable, because some component may want to cause 877 // a fatal error in this case? 878 } 879 } 880 // Create service instances for the pending promises and resolve them 881 // NOTE: if this.multipleInstances is false, only the default instance will be created 882 // and all promises with resolve with it regardless of the identifier. 883 for (const [instanceIdentifier, instanceDeferred] of this.instancesDeferred.entries()) { 884 const normalizedIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier); 885 try { 886 // `getOrInitializeService()` should always return a valid instance since a component is guaranteed. use ! to make typescript happy. 887 const instance = this.getOrInitializeService({ 888 instanceIdentifier: normalizedIdentifier 889 }); 890 instanceDeferred.resolve(instance); 891 } 892 catch (e) { 893 // when the instance factory throws an exception, it should not cause 894 // a fatal error. We just leave the promise unresolved. 895 } 896 } 897 } 898 clearInstance(identifier = DEFAULT_ENTRY_NAME$1) { 899 this.instancesDeferred.delete(identifier); 900 this.instancesOptions.delete(identifier); 901 this.instances.delete(identifier); 902 } 903 // app.delete() will call this method on every provider to delete the services 904 // TODO: should we mark the provider as deleted? 905 async delete() { 906 const services = Array.from(this.instances.values()); 907 await Promise.all([ 908 ...services 909 .filter(service => 'INTERNAL' in service) // legacy services 910 // eslint-disable-next-line @typescript-eslint/no-explicit-any 911 .map(service => service.INTERNAL.delete()), 912 ...services 913 .filter(service => '_delete' in service) // modularized services 914 // eslint-disable-next-line @typescript-eslint/no-explicit-any 915 .map(service => service._delete()) 916 ]); 917 } 918 isComponentSet() { 919 return this.component != null; 920 } 921 isInitialized(identifier = DEFAULT_ENTRY_NAME$1) { 922 return this.instances.has(identifier); 923 } 924 getOptions(identifier = DEFAULT_ENTRY_NAME$1) { 925 return this.instancesOptions.get(identifier) || {}; 926 } 927 initialize(opts = {}) { 928 const { options = {} } = opts; 929 const normalizedIdentifier = this.normalizeInstanceIdentifier(opts.instanceIdentifier); 930 if (this.isInitialized(normalizedIdentifier)) { 931 throw Error(`${this.name}(${normalizedIdentifier}) has already been initialized`); 932 } 933 if (!this.isComponentSet()) { 934 throw Error(`Component ${this.name} has not been registered yet`); 935 } 936 const instance = this.getOrInitializeService({ 937 instanceIdentifier: normalizedIdentifier, 938 options 939 }); 940 // resolve any pending promise waiting for the service instance 941 for (const [instanceIdentifier, instanceDeferred] of this.instancesDeferred.entries()) { 942 const normalizedDeferredIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier); 943 if (normalizedIdentifier === normalizedDeferredIdentifier) { 944 instanceDeferred.resolve(instance); 945 } 946 } 947 return instance; 948 } 949 /** 950 * 951 * @param callback - a function that will be invoked after the provider has been initialized by calling provider.initialize(). 952 * The function is invoked SYNCHRONOUSLY, so it should not execute any longrunning tasks in order to not block the program. 953 * 954 * @param identifier An optional instance identifier 955 * @returns a function to unregister the callback 956 */ 957 onInit(callback, identifier) { 958 const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier); 959 const existingCallbacks = this.onInitCallbacks.get(normalizedIdentifier) ?? 960 new Set(); 961 existingCallbacks.add(callback); 962 this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks); 963 const existingInstance = this.instances.get(normalizedIdentifier); 964 if (existingInstance) { 965 callback(existingInstance, normalizedIdentifier); 966 } 967 return () => { 968 existingCallbacks.delete(callback); 969 }; 970 } 971 /** 972 * Invoke onInit callbacks synchronously 973 * @param instance the service instance` 974 */ 975 invokeOnInitCallbacks(instance, identifier) { 976 const callbacks = this.onInitCallbacks.get(identifier); 977 if (!callbacks) { 978 return; 979 } 980 for (const callback of callbacks) { 981 try { 982 callback(instance, identifier); 983 } 984 catch { 985 // ignore errors in the onInit callback 986 } 987 } 988 } 989 getOrInitializeService({ instanceIdentifier, options = {} }) { 990 let instance = this.instances.get(instanceIdentifier); 991 if (!instance && this.component) { 992 instance = this.component.instanceFactory(this.container, { 993 instanceIdentifier: normalizeIdentifierForFactory(instanceIdentifier), 994 options 995 }); 996 this.instances.set(instanceIdentifier, instance); 997 this.instancesOptions.set(instanceIdentifier, options); 998 /** 999 * Invoke onInit listeners. 1000 * Note this.component.onInstanceCreated is different, which is used by the component creator, 1001 * while onInit listeners are registered by consumers of the provider. 1002 */ 1003 this.invokeOnInitCallbacks(instance, instanceIdentifier); 1004 /** 1005 * Order is important 1006 * onInstanceCreated() should be called after this.instances.set(instanceIdentifier, instance); which 1007 * makes `isInitialized()` return true. 1008 */ 1009 if (this.component.onInstanceCreated) { 1010 try { 1011 this.component.onInstanceCreated(this.container, instanceIdentifier, instance); 1012 } 1013 catch { 1014 // ignore errors in the onInstanceCreatedCallback 1015 } 1016 } 1017 } 1018 return instance || null; 1019 } 1020 normalizeInstanceIdentifier(identifier = DEFAULT_ENTRY_NAME$1) { 1021 if (this.component) { 1022 return this.component.multipleInstances ? identifier : DEFAULT_ENTRY_NAME$1; 1023 } 1024 else { 1025 return identifier; // assume multiple instances are supported before the component is provided. 1026 } 1027 } 1028 shouldAutoInitialize() { 1029 return (!!this.component && 1030 this.component.instantiationMode !== "EXPLICIT" /* InstantiationMode.EXPLICIT */); 1031 } 1032 } 1033 // undefined should be passed to the service factory for the default instance 1034 function normalizeIdentifierForFactory(identifier) { 1035 return identifier === DEFAULT_ENTRY_NAME$1 ? undefined : identifier; 1036 } 1037 function isComponentEager(component) { 1038 return component.instantiationMode === "EAGER" /* InstantiationMode.EAGER */; 1039 } 1040 1041 /** 1042 * @license 1043 * Copyright 2019 Google LLC 1044 * 1045 * Licensed under the Apache License, Version 2.0 (the "License");
1046 * you may not use this file except in compliance with the License. 1047 * You may obtain a copy of the License at 1048 * 1049 * http://www.apache.org/licenses/LICENSE-2.0 1050 * 1051 * Unless required by applicable law or agreed to in writing, software 1052 * distributed under the License is distributed on an "AS IS" BASIS, 1053 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1054 * See the License for the specific language governing permissions and 1055 * limitations under the License. 1056 */ 1057 /** 1058 * ComponentContainer that provides Providers for service name T, e.g. `auth`, `auth-internal` 1059 */ 1060 class ComponentContainer { 1061 constructor(name) { 1062 this.name = name; 1063 this.providers = new Map(); 1064 } 1065 /** 1066 * 1067 * @param component Component being added 1068 * @param overwrite When a component with the same name has already been registered, 1069 * if overwrite is true: overwrite the existing component with the new component and create a new 1070 * provider with the new component. It can be useful in tests where you want to use different mocks 1071 * for different tests. 1072 * if overwrite is false: throw an exception 1073 */ 1074 addComponent(component) { 1075 const provider = this.getProvider(component.name); 1076 if (provider.isComponentSet()) { 1077 throw new Error(`Component ${component.name} has already been registered with ${this.name}`); 1078 } 1079 provider.setComponent(component); 1080 } 1081 addOrOverwriteComponent(component) { 1082 const provider = this.getProvider(component.name); 1083 if (provider.isComponentSet()) { 1084 // delete the existing provider from the container, so we can register the new component 1085 this.providers.delete(component.name); 1086 } 1087 this.addComponent(component); 1088 } 1089 /** 1090 * getProvider provides a type safe interface where it can only be called with a field name 1091 * present in NameServiceMapping interface. 1092 * 1093 * Firebase SDKs providing services should extend NameServiceMapping interface to register 1094 * themselves. 1095 */ 1096 getProvider(name) { 1097 if (this.providers.has(name)) { 1098 return this.providers.get(name); 1099 } 1100 // create a Provider for a service that hasn't registered with Firebase 1101 const provider = new Provider(name, this); 1102 this.providers.set(name, provider); 1103 return provider; 1104 } 1105 getProviders() { 1106 return Array.from(this.providers.values()); 1107 } 1108 } 1109 1110 /** 1111 * @license 1112 * Copyright 2017 Google LLC 1113 * 1114 * Licensed under the Apache License, Version 2.0 (the "License"); 1115 * you may not use this file except in compliance with the License. 1116 * You may obtain a copy of the License at 1117 * 1118 * http://www.apache.org/licenses/LICENSE-2.0 1119 * 1120 * Unless required by applicable law or agreed to in writing, software 1121 * distributed under the License is distributed on an "AS IS" BASIS, 1122 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1123 * See the License for the specific language governing permissions and 1124 * limitations under the License. 1125 */ 1126 /** 1127 * A container for all of the Logger instances 1128 */ 1129 /** 1130 * The JS SDK supports 5 log levels and also allows a user the ability to 1131 * silence the logs altogether. 1132 * 1133 * The order is a follows: 1134 * DEBUG < VERBOSE < INFO < WARN < ERROR 1135 * 1136 * All of the log types above the current log level will be captured (i.e. if 1137 * you set the log level to `INFO`, errors will still be logged, but `DEBUG` and 1138 * `VERBOSE` logs will not) 1139 */ 1140 var LogLevel; 1141 (function (LogLevel) { 1142 LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG"; 1143 LogLevel[LogLevel["VERBOSE"] = 1] = "VERBOSE"; 1144 LogLevel[LogLevel["INFO"] = 2] = "INFO"; 1145 LogLevel[LogLevel["WARN"] = 3] = "WARN";
vendor: 10,109 bytes, lines 1146-1403
1146 LogLevel[LogLevel["ERROR"] = 4] = "ERROR"; 1147 LogLevel[LogLevel["SILENT"] = 5] = "SILENT"; 1148 })(LogLevel || (LogLevel = {})); 1149 const levelStringToEnum = { 1150 'debug': LogLevel.DEBUG, 1151 'verbose': LogLevel.VERBOSE, 1152 'info': LogLevel.INFO, 1153 'warn': LogLevel.WARN, 1154 'error': LogLevel.ERROR, 1155 'silent': LogLevel.SILENT 1156 }; 1157 /** 1158 * The default log level 1159 */ 1160 const defaultLogLevel = LogLevel.INFO; 1161 /** 1162 * By default, `console.debug` is not displayed in the developer console (in 1163 * chrome). To avoid forcing users to have to opt-in to these logs twice 1164 * (i.e. once for firebase, and once in the console), we are sending `DEBUG` 1165 * logs to the `console.log` function. 1166 */ 1167 const ConsoleMethod = { 1168 [LogLevel.DEBUG]: 'log', 1169 [LogLevel.VERBOSE]: 'log', 1170 [LogLevel.INFO]: 'info', 1171 [LogLevel.WARN]: 'warn', 1172 [LogLevel.ERROR]: 'error' 1173 }; 1174 /** 1175 * The default log handler will forward DEBUG, VERBOSE, INFO, WARN, and ERROR 1176 * messages on to their corresponding console counterparts (if the log method 1177 * is supported by the current log level) 1178 */ 1179 const defaultLogHandler = (instance, logType, ...args) => { 1180 if (logType < instance.logLevel) { 1181 return; 1182 } 1183 const now = new Date().toISOString(); 1184 const method = ConsoleMethod[logType]; 1185 if (method) { 1186 console[method](`[${now}] ${instance.name}:`, ...args); 1187 } 1188 else { 1189 throw new Error(`Attempted to log a message with an invalid logType (value: ${logType})`); 1190 } 1191 }; 1192 class Logger { 1193 /** 1194 * Gives you an instance of a Logger to capture messages according to 1195 * Firebase's logging scheme. 1196 * 1197 * @param name The name that the logs will be associated with 1198 */ 1199 constructor(name) { 1200 this.name = name; 1201 /** 1202 * The log level of the given Logger instance. 1203 */ 1204 this._logLevel = defaultLogLevel; 1205 /** 1206 * The main (internal) log handler for the Logger instance. 1207 * Can be set to a new function in internal package code but not by user. 1208 */ 1209 this._logHandler = defaultLogHandler; 1210 /** 1211 * The optional, additional, user-defined log handler for the Logger instance. 1212 */ 1213 this._userLogHandler = null; 1214 } 1215 get logLevel() { 1216 return this._logLevel; 1217 } 1218 set logLevel(val) { 1219 if (!(val in LogLevel)) { 1220 throw new TypeError(`Invalid value "${val}" assigned to \`logLevel\``); 1221 } 1222 this._logLevel = val; 1223 } 1224 // Workaround for setter/getter having to be the same type. 1225 setLogLevel(val) { 1226 this._logLevel = typeof val === 'string' ? levelStringToEnum[val] : val; 1227 } 1228 get logHandler() { 1229 return this._logHandler; 1230 } 1231 set logHandler(val) { 1232 if (typeof val !== 'function') { 1233 throw new TypeError('Value assigned to `logHandler` must be a function'); 1234 } 1235 this._logHandler = val; 1236 } 1237 get userLogHandler() { 1238 return this._userLogHandler; 1239 } 1240 set userLogHandler(val) { 1241 this._userLogHandler = val; 1242 } 1243 /** 1244 * The functions below are all based on the `console` interface 1245 */ 1246 debug(...args) { 1247 this._userLogHandler && this._userLogHandler(this, LogLevel.DEBUG, ...args); 1248 this._logHandler(this, LogLevel.DEBUG, ...args); 1249 } 1250 log(...args) { 1251 this._userLogHandler && 1252 this._userLogHandler(this, LogLevel.VERBOSE, ...args); 1253 this._logHandler(this, LogLevel.VERBOSE, ...args); 1254 } 1255 info(...args) { 1256 this._userLogHandler && this._userLogHandler(this, LogLevel.INFO, ...args); 1257 this._logHandler(this, LogLevel.INFO, ...args); 1258 } 1259 warn(...args) { 1260 this._userLogHandler && this._userLogHandler(this, LogLevel.WARN, ...args); 1261 this._logHandler(this, LogLevel.WARN, ...args); 1262 } 1263 error(...args) { 1264 this._userLogHandler && this._userLogHandler(this, LogLevel.ERROR, ...args); 1265 this._logHandler(this, LogLevel.ERROR, ...args); 1266 } 1267 } 1268 1269 const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c); 1270 1271 let idbProxyableTypes; 1272 let cursorAdvanceMethods; 1273 // This is a function to prevent it throwing up in node environments. 1274 function getIdbProxyableTypes() { 1275 return (idbProxyableTypes || 1276 (idbProxyableTypes = [ 1277 IDBDatabase, 1278 IDBObjectStore, 1279 IDBIndex, 1280 IDBCursor, 1281 IDBTransaction, 1282 ])); 1283 } 1284 // This is a function to prevent it throwing up in node environments. 1285 function getCursorAdvanceMethods() { 1286 return (cursorAdvanceMethods || 1287 (cursorAdvanceMethods = [ 1288 IDBCursor.prototype.advance, 1289 IDBCursor.prototype.continue, 1290 IDBCursor.prototype.continuePrimaryKey, 1291 ])); 1292 } 1293 const cursorRequestMap = new WeakMap(); 1294 const transactionDoneMap = new WeakMap(); 1295 const transactionStoreNamesMap = new WeakMap(); 1296 const transformCache = new WeakMap(); 1297 const reverseTransformCache = new WeakMap(); 1298 function promisifyRequest(request) { 1299 const promise = new Promise((resolve, reject) => { 1300 const unlisten = () => { 1301 request.removeEventListener('success', success); 1302 request.removeEventListener('error', error); 1303 }; 1304 const success = () => { 1305 resolve(wrap(request.result)); 1306 unlisten(); 1307 }; 1308 const error = () => { 1309 reject(request.error); 1310 unlisten(); 1311 }; 1312 request.addEventListener('success', success); 1313 request.addEventListener('error', error); 1314 }); 1315 promise 1316 .then((value) => { 1317 // Since cursoring reuses the IDBRequest (*sigh*), we cache it for later retrieval 1318 // (see wrapFunction). 1319 if (value instanceof IDBCursor) { 1320 cursorRequestMap.set(value, request); 1321 } 1322 // Catching to avoid "Uncaught Promise exceptions" 1323 }) 1324 .catch(() => { }); 1325 // This mapping exists in reverseTransformCache but doesn't doesn't exist in transformCache. This 1326 // is because we create many promises from a single IDBRequest. 1327 reverseTransformCache.set(promise, request); 1328 return promise; 1329 } 1330 function cacheDonePromiseForTransaction(tx) { 1331 // Early bail if we've already created a done promise for this transaction. 1332 if (transactionDoneMap.has(tx)) 1333 return; 1334 const done = new Promise((resolve, reject) => { 1335 const unlisten = () => { 1336 tx.removeEventListener('complete', complete); 1337 tx.removeEventListener('error', error); 1338 tx.removeEventListener('abort', error); 1339 }; 1340 const complete = () => { 1341 resolve(); 1342 unlisten(); 1343 }; 1344 const error = () => { 1345 reject(tx.error || new DOMException('AbortError', 'AbortError')); 1346 unlisten(); 1347 }; 1348 tx.addEventListener('complete', complete); 1349 tx.addEventListener('error', error); 1350 tx.addEventListener('abort', error); 1351 }); 1352 // Cache it for later retrieval. 1353 transactionDoneMap.set(tx, done); 1354 } 1355 let idbProxyTraps = { 1356 get(target, prop, receiver) { 1357 if (target instanceof IDBTransaction) { 1358 // Special handling for transaction.done. 1359 if (prop === 'done') 1360 return transactionDoneMap.get(target); 1361 // Polyfill for objectStoreNames because of Edge. 1362 if (prop === 'objectStoreNames') { 1363 return target.objectStoreNames || transactionStoreNamesMap.get(target); 1364 } 1365 // Make tx.store return the only store in the transaction, or undefined if there are many. 1366 if (prop === 'store') { 1367 return receiver.objectStoreNames[1] 1368 ? undefined 1369 : receiver.objectStore(receiver.objectStoreNames[0]); 1370 } 1371 } 1372 // Else transform whatever we get back. 1373 return wrap(target[prop]); 1374 }, 1375 set(target, prop, value) { 1376 target[prop] = value; 1377 return true; 1378 }, 1379 has(target, prop) { 1380 if (target instanceof IDBTransaction && 1381 (prop === 'done' || prop === 'store')) { 1382 return true; 1383 } 1384 return prop in target; 1385 }, 1386 }; 1387 function replaceTraps(callback) { 1388 idbProxyTraps = callback(idbProxyTraps); 1389 } 1390 function wrapFunction(func) { 1391 // Due to expected object equality (which is enforced by the caching in `wrap`), we 1392 // only create one new func per func. 1393 // Edge doesn't support objectStoreNames (booo), so we polyfill it here. 1394 if (func === IDBDatabase.prototype.transaction && 1395 !('objectStoreNames' in IDBTransaction.prototype)) { 1396 return function (storeNames, ...args) { 1397 const tx = func.call(unwrap(this), storeNames, ...args); 1398 transactionStoreNamesMap.set(tx, storeNames.sort ? storeNames.sort() : [storeNames]); 1399 return wrap(tx); 1400 }; 1401 } 1402 // Cursor methods are special, as the behaviour is a little more different to standard IDB. In 1403 // IDB, you advance the cursor and wait for a new 'success' on the ID
vendor: 6,353 bytes, lines 1403-1548
1403BRequest that gave you the 1404 // cursor. It's kinda like a promise that can resolve with many values. That doesn't make sense 1405 // with real promises, so each advance methods returns a new promise for the cursor object, or 1406 // undefined if the end of the cursor has been reached. 1407 if (getCursorAdvanceMethods().includes(func)) { 1408 return function (...args) { 1409 // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use 1410 // the original object. 1411 func.apply(unwrap(this), args); 1412 return wrap(cursorRequestMap.get(this)); 1413 }; 1414 } 1415 return function (...args) { 1416 // Calling the original function with the proxy as 'this' causes ILLEGAL INVOCATION, so we use 1417 // the original object. 1418 return wrap(func.apply(unwrap(this), args)); 1419 }; 1420 } 1421 function transformCachableValue(value) { 1422 if (typeof value === 'function') 1423 return wrapFunction(value); 1424 // This doesn't return, it just creates a 'done' promise for the transaction, 1425 // which is later returned for transaction.done (see idbObjectHandler). 1426 if (value instanceof IDBTransaction) 1427 cacheDonePromiseForTransaction(value); 1428 if (instanceOfAny(value, getIdbProxyableTypes())) 1429 return new Proxy(value, idbProxyTraps); 1430 // Return the same value back if we're not going to transform it. 1431 return value; 1432 } 1433 function wrap(value) { 1434 // We sometimes generate multiple promises from a single IDBRequest (eg when cursoring), because 1435 // IDB is weird and a single IDBRequest can yield many responses, so these can't be cached. 1436 if (value instanceof IDBRequest) 1437 return promisifyRequest(value); 1438 // If we've already transformed this value before, reuse the transformed value. 1439 // This is faster, but it also provides object equality. 1440 if (transformCache.has(value)) 1441 return transformCache.get(value); 1442 const newValue = transformCachableValue(value); 1443 // Not all types are transformed. 1444 // These may be primitive types, so they can't be WeakMap keys. 1445 if (newValue !== value) { 1446 transformCache.set(value, newValue); 1447 reverseTransformCache.set(newValue, value); 1448 } 1449 return newValue; 1450 } 1451 const unwrap = (value) => reverseTransformCache.get(value); 1452 1453 /** 1454 * Open a database. 1455 * 1456 * @param name Name of the database. 1457 * @param version Schema version. 1458 * @param callbacks Additional callbacks. 1459 */ 1460 function openDB(name, version, { blocked, upgrade, blocking, terminated } = {}) { 1461 const request = indexedDB.open(name, version); 1462 const openPromise = wrap(request); 1463 if (upgrade) { 1464 request.addEventListener('upgradeneeded', (event) => { 1465 upgrade(wrap(request.result), event.oldVersion, event.newVersion, wrap(request.transaction), event); 1466 }); 1467 } 1468 if (blocked) { 1469 request.addEventListener('blocked', (event) => blocked( 1470 // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405 1471 event.oldVersion, event.newVersion, event)); 1472 } 1473 openPromise 1474 .then((db) => { 1475 if (terminated) 1476 db.addEventListener('close', () => terminated()); 1477 if (blocking) { 1478 db.addEventListener('versionchange', (event) => blocking(event.oldVersion, event.newVersion, event)); 1479 } 1480 }) 1481 .catch(() => { }); 1482 return openPromise; 1483 } 1484 /** 1485 * Delete a database. 1486 * 1487 * @param name Name of the database. 1488 */ 1489 function deleteDB(name, { blocked } = {}) { 1490 const request = indexedDB.deleteDatabase(name); 1491 if (blocked) { 1492 request.addEventListener('blocked', (event) => blocked( 1493 // Casting due to https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1405 1494 event.oldVersion, event)); 1495 } 1496 return wrap(request).then(() => undefined); 1497 } 1498 1499 const readMethods = ['get', 'getKey', 'getAll', 'getAllKeys', 'count']; 1500 const writeMethods = ['put', 'add', 'delete', 'clear']; 1501 const cachedMethods = new Map(); 1502 function getMethod(target, prop) { 1503 if (!(target instanceof IDBDatabase && 1504 !(prop in target) && 1505 typeof prop === 'string')) { 1506 return; 1507 } 1508 if (cachedMethods.get(prop)) 1509 return cachedMethods.get(prop); 1510 const targetFuncName = prop.replace(/FromIndex$/, ''); 1511 const useIndex = prop !== targetFuncName; 1512 const isWrite = writeMethods.includes(targetFuncName); 1513 if ( 1514 // Bail if the target doesn't exist on the target. Eg, getAll isn't in Edge. 1515 !(targetFuncName in (useIndex ? IDBIndex : IDBObjectStore).prototype) || 1516 !(isWrite || readMethods.includes(targetFuncName))) { 1517 return; 1518 } 1519 const method = async function (storeName, ...args) { 1520 // isWrite ? 'readwrite' : undefined gzipps better, but fails in Edge :( 1521 const tx = this.transaction(storeName, isWrite ? 'readwrite' : 'readonly'); 1522 let target = tx.store; 1523 if (useIndex) 1524 target = target.index(args.shift()); 1525 // Must reject if op rejects. 1526 // If it's a write operation, must reject if tx.done rejects. 1527 // Must reject with op rejection first. 1528 // Must resolve with op value. 1529 // Must handle both promises (no unhandled rejections) 1530 return (await Promise.all([ 1531 target[targetFuncName](...args), 1532 isWrite && tx.done, 1533 ]))[0]; 1534 }; 1535 cachedMethods.set(prop, method); 1536 return method; 1537 } 1538 replaceTraps((oldTraps) => ({ 1539 ...oldTraps, 1540 get: (target, prop, receiver) => getMethod(target, prop) || oldTraps.get(target, prop, receiver), 1541 has: (target, prop) => !!getMethod(target, prop) || oldTraps.has(target, prop), 1542 })); 1543 1544 /** 1545 * @license 1546 * Copyright 2019 Google LLC 1547 * 1548 * Licensed under the Apache License, Version 2.0 (the "License");
1549 * you may not use this file except in compliance with the License. 1550 * You may obtain a copy of the License at 1551 * 1552 * http://www.apache.org/licenses/LICENSE-2.0 1553 * 1554 * Unless required by applicable law or agreed to in writing, software 1555 * distributed under the License is distributed on an "AS IS" BASIS, 1556 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1557 * See the License for the specific language governing permissions and 1558 * limitations under the License. 1559 */ 1560 class PlatformLoggerServiceImpl { 1561 constructor(container) { 1562 this.container = container; 1563 } 1564 // In initial implementation, this will be called by installations on 1565 // auth token refresh, and installations will send this string. 1566 getPlatformInfoString() { 1567 const providers = this.container.getProviders(); 1568 // Loop through providers and get library/version pairs from any that are 1569 // version components. 1570 return providers 1571 .map(provider => { 1572 if (isVersionServiceProvider(provider)) { 1573 const service = provider.getImmediate(); 1574 return `${service.library}/${service.version}`; 1575 } 1576 else { 1577 return null; 1578 } 1579 }) 1580 .filter(logString => logString) 1581 .join(' '); 1582 } 1583 } 1584 /** 1585 * 1586 * @param provider check if this provider provides a VersionService 1587 * 1588 * NOTE: Using Provider<'app-version'> is a hack to indicate that the provider 1589 * provides VersionService. The provider is not necessarily a 'app-version' 1590 * provider. 1591 */ 1592 function isVersionServiceProvider(provider) { 1593 const component = provider.getComponent(); 1594 return component?.type === "VERSION" /* ComponentType.VERSION */; 1595 } 1596 1597 const name$q = "@firebase/app"; 1598 const version$1$1 = "0.14.13"; 1599 1600 /** 1601 * @license 1602 * Copyright 2019 Google LLC 1603 * 1604 * Licensed under the Apache License, Version 2.0 (the "License"); 1605 * you may not use this file except in compliance with the License. 1606 * You may obtain a copy of the License at 1607 * 1608 * http://www.apache.org/licenses/LICENSE-2.0 1609 * 1610 * Unless required by applicable law or agreed to in writing, software 1611 * distributed under the License is distributed on an "AS IS" BASIS, 1612 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1613 * See the License for the specific language governing permissions and 1614 * limitations under the License. 1615 */ 1616 const logger = new Logger('@firebase/app'); 1617 1618 const name$p = "@firebase/app-compat"; 1619 1620 const name$o = "@firebase/analytics-compat"; 1621 1622 const name$n = "@firebase/analytics"; 1623 1624 const name$m = "@firebase/app-check-compat"; 1625 1626 const name$l = "@firebase/app-check"; 1627 1628 const name$k = "@firebase/auth"; 1629 1630 const name$j = "@firebase/auth-compat"; 1631 1632 const name$i = "@firebase/database"; 1633 1634 const name$h = "@firebase/data-connect"; 1635 1636 const name$g = "@firebase/database-compat"; 1637 1638 const name$f = "@firebase/functions"; 1639 1640 const name$e = "@firebase/functions-compat"; 1641 1642 const name$d = "@firebase/installations"; 1643 1644 const name$c = "@firebase/installations-compat"; 1645 1646 const name$b = "@firebase/messaging"; 1647 1648 const name$a = "@firebase/messaging-compat"; 1649 1650 const name$9 = "@firebase/performance"; 1651 1652 const name$8 = "@firebase/performance-compat"; 1653 1654 const name$7 = "@firebase/remote-config"; 1655 1656 const name$6 = "@firebase/remote-config-compat"; 1657 1658 const name$5 = "@firebase/storage"; 1659 1660 const name$4 = "@firebase/storage-compat"; 1661 1662 const name$3 = "@firebase/firestore"; 1663 1664 const name$2 = "@firebase/ai"; 1665 1666 const name$1$1 = "@firebase/firestore-compat"; 1667 1668 const name$r = "firebase"; 1669 1670 /** 1671 * @license 1672 * Copyright 2019 Google LLC 1673 * 1674 * Licensed under the Apache License, Version 2.0 (the "License"); 1675 * you may not use this file except in compliance with the License. 1676 * You may obtain a copy of the License at 1677 * 1678 * http://www.apache.org/licenses/LICENSE-2.0 1679 * 1680 * Unless required by applicable law or agreed to in writing, softw
1680are 1681 * distributed under the License is distributed on an "AS IS" BASIS, 1682 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1683 * See the License for the specific language governing permissions and 1684 * limitations under the License. 1685 */ 1686 /** 1687 * The default app name 1688 * 1689 * @internal 1690 */ 1691 const DEFAULT_ENTRY_NAME = '[DEFAULT]'; 1692 const PLATFORM_LOG_STRING = { 1693 [name$q]: 'fire-core', 1694 [name$p]: 'fire-core-compat', 1695 [name$n]: 'fire-analytics', 1696 [name$o]: 'fire-analytics-compat', 1697 [name$l]: 'fire-app-check', 1698 [name$m]: 'fire-app-check-compat', 1699 [name$k]: 'fire-auth', 1700 [name$j]: 'fire-auth-compat', 1701 [name$i]: 'fire-rtdb', 1702 [name$h]: 'fire-data-connect', 1703 [name$g]: 'fire-rtdb-compat', 1704 [name$f]: 'fire-fn', 1705 [name$e]: 'fire-fn-compat', 1706 [name$d]: 'fire-iid', 1707 [name$c]: 'fire-iid-compat', 1708 [name$b]: 'fire-fcm', 1709 [name$a]: 'fire-fcm-compat', 1710 [name$9]: 'fire-perf', 1711 [name$8]: 'fire-perf-compat', 1712 [name$7]: 'fire-rc', 1713 [name$6]: 'fire-rc-compat', 1714 [name$5]: 'fire-gcs', 1715 [name$4]: 'fire-gcs-compat', 1716 [name$3]: 'fire-fst', 1717 [name$1$1]: 'fire-fst-compat', 1718 [name$2]: 'fire-vertex', 1719 'fire-js': 'fire-js', // Platform identifier for JS SDK. 1720 [name$r]: 'fire-js-all' 1721 }; 1722 1723 /** 1724 * @license 1725 * Copyright 2019 Google LLC 1726 * 1727 * Licensed under the Apache License, Version 2.0 (the "License"); 1728 * you may not use this file except in compliance with the License. 1729 * You may obtain a copy of the License at 1730 * 1731 * http://www.apache.org/licenses/LICENSE-2.0 1732 * 1733 * Unless required by applicable law or agreed to in writing, software 1734 * distributed under the License is distributed on an "AS IS" BASIS, 1735 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1736 * See the License for the specific language governing permissions and 1737 * limitations under the License. 1738 */ 1739 /** 1740 * @internal 1741 */ 1742 const _apps = new Map(); 1743 /** 1744 * @internal 1745 */ 1746 const _serverApps = new Map(); 1747 /** 1748 * Registered components. 1749 * 1750 * @internal 1751 */ 1752 // eslint-disable-next-line @typescript-eslint/no-explicit-any 1753 const _components = new Map(); 1754 /** 1755 * @param component - the component being added to this app's container 1756 * 1757 * @internal 1758 */ 1759 function _addComponent(app, component) { 1760 try { 1761 app.container.addComponent(component); 1762 } 1763 catch (e) { 1764 logger.debug(`Component ${component.name} failed to register with FirebaseApp ${app.name}`, e); 1765 } 1766 } 1767 /** 1768 * 1769 * @param component - the component to register 1770 * @returns whether or not the component is registered successfully 1771 * 1772 * @internal 1773 */ 1774 function _registerComponent(component) { 1775 const componentName = component.name; 1776 if (_components.has(componentName)) { 1777 logger.debug(`There were multiple attempts to register component ${componentName}.`); 1778 return false; 1779 } 1780 _components.set(componentName, component); 1781 // add the component to existing app instances 1782 for (const app of _apps.values()) { 1783 _addComponent(app, component); 1784 } 1785 for (const serverApp of _serverApps.values()) { 1786 _addComponent(serverApp, component); 1787 } 1788 return true; 1789 } 1790 /** 1791 * 1792 * @param app - FirebaseApp instance 1793 * @param name - service name 1794 * 1795 * @returns the provider for the service with the matching name 1796 * 1797 * @internal 1798 */ 1799 function _getProvider(app, name) { 1800 const heartbeatController = app.container 1801 .getProvider('heartbeat') 1802 .getImmediate({ optional: true }); 1803 if (heartbeatController) { 1804 void heartbeatController.triggerHeartbeat(); 1805 } 1806 return app.container.getProvider(name); 1807 } 1808 1809 /** 1810 * @license 1811 * Copyright 2019 Google LLC 1812 * 1813 * Licensed under the Apache License, Version 2.0 (the "License");
1814 * you may not use this file except in compliance with the License. 1815 * You may obtain a copy of the License at 1816 * 1817 * http://www.apache.org/licenses/LICENSE-2.0 1818 * 1819 * Unless required by applicable law or agreed to in writing, software 1820 * distributed under the License is distributed on an "AS IS" BASIS, 1821 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1822 * See the License for the specific language governing permissions and 1823 * limitations under the License. 1824 */ 1825 const ERRORS = { 1826 ["no-app" /* AppError.NO_APP */]: "No Firebase App '{$appName}' has been created - " + 1827 'call initializeApp() first', 1828 ["bad-app-name" /* AppError.BAD_APP_NAME */]: "Illegal App name: '{$appName}'", 1829 ["duplicate-app" /* AppError.DUPLICATE_APP */]: "Firebase App named '{$appName}' already exists with different options or config", 1830 ["app-deleted" /* AppError.APP_DELETED */]: "Firebase App named '{$appName}' already deleted", 1831 ["server-app-deleted" /* AppError.SERVER_APP_DELETED */]: 'Firebase Server App has been deleted', 1832 ["no-options" /* AppError.NO_OPTIONS */]: 'Need to provide options, when not being deployed to hosting via source.', 1833 ["invalid-app-argument" /* AppError.INVALID_APP_ARGUMENT */]: 'firebase.{$appName}() takes either no argument or a ' + 1834 'Firebase App instance.', 1835 ["invalid-log-argument" /* AppError.INVALID_LOG_ARGUMENT */]: 'First argument to `onLog` must be null or a function.', 1836 ["idb-open" /* AppError.IDB_OPEN */]: 'Error thrown when opening IndexedDB. Original error: {$originalErrorMessage}.', 1837 ["idb-get" /* AppError.IDB_GET */]: 'Error thrown when reading from IndexedDB. Original error: {$originalErrorMessage}.', 1838 ["idb-set" /* AppError.IDB_WRITE */]: 'Error thrown when writing to IndexedDB. Original error: {$originalErrorMessage}.', 1839 ["idb-delete" /* AppError.IDB_DELETE */]: 'Error thrown when deleting from IndexedDB. Original error: {$originalErrorMessage}.', 1840 ["finalization-registry-not-supported" /* AppError.FINALIZATION_REGISTRY_NOT_SUPPORTED */]: 'FirebaseServerApp deleteOnDeref field defined but the JS runtime does not support FinalizationRegistry.', 1841 ["invalid-server-app-environment" /* AppError.INVALID_SERVER_APP_ENVIRONMENT */]: 'FirebaseServerApp is not for use in browser environments.' 1842 }; 1843 const ERROR_FACTORY$2 = new ErrorFactory('app', 'Firebase', ERRORS); 1844 1845 /** 1846 * @license 1847 * Copyright 2019 Google LLC 1848 * 1849 * Licensed under the Apache License, Version 2.0 (the "License"); 1850 * you may not use this file except in compliance with the License. 1851 * You may obtain a copy of the License at 1852 * 1853 * http://www.apache.org/licenses/LICENSE-2.0 1854 * 1855 * Unless required by applicable law or agreed to in writing, software 1856 * distributed under the License is distributed on an "AS IS" BASIS, 1857 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1858 * See the License for the specific language governing permissions and 1859 * limitations under the License. 1860 */ 1861 class FirebaseAppImpl { 1862 constructor(options, config, container) { 1863 this._isDeleted = false; 1864 this._options = { ...options }; 1865 this._config = { ...config }; 1866 this._name = config.name; 1867 this._automaticDataCollectionEnabled = 1868 config.automaticDataCollectionEnabled; 1869 this._container = container; 1870 this.container.addComponent(new Component('app', () => this, "PUBLIC" /* ComponentType.PUBLIC */)); 1871 } 1872 get automaticDataCollectionEnabled() { 1873 this.checkDestroyed(); 1874 return this._automaticDataCollectionEnabled; 1875 } 1876 set automaticDataCollectionEnabled(val) { 1877 this.checkDestroyed(); 1878 this._automaticDataCollectionEnabled = val; 1879 } 1880 get name() { 1881 this.checkDestroyed(); 1882 return this._name; 1883 } 1884 get options() { 1885 this.checkDestroyed(); 1886 return this._options; 1887 } 1888 get config() { 1889 this.checkDestroyed(); 1890 return this._config; 1891 } 1892 get container() { 1893 return this._container; 1894 } 1895 get isDeleted() { 1896 return this._isDeleted; 1897 } 1898 set isDeleted(val) { 1899 this._isDeleted = val; 1900 } 1901 /** 1902 * This function will throw an Error if the App has already been deleted - 1903 * use before performing API actions on the App. 1904 */ 1905 checkDestroyed() { 1906 if (this.isDeleted) { 1907 throw ERROR_FACTORY$2.create("app-deleted" /* AppError.APP_DELETED */, { appName: this._name }); 1908 } 1909 } 1910 } 1911 function initializeApp(_options, rawConfig = {}) { 1912 let options = _options; 1913 if (typeof rawConfig !== 'object') { 1914 const name = rawConfig; 1915 rawConfig = { name }; 1916 } 1917 const config = { 1918 name: DEFAULT_ENTRY_NAME, 1919 automaticDataCollectionEnabled: true, 1920 ...rawConfig 1921 }; 1922 const name = config.name; 1923 if (typeof name !== 'string' || !name) { 1924 throw ERROR_FACTORY$2.create("bad-app-name" /* AppError.BAD_APP_NAME */, { 1925 appName: String(name) 1926 }); 1927 }
1928 options || (options = getDefaultAppConfig()); 1929 if (!options) { 1930 throw ERROR_FACTORY$2.create("no-options" /* AppError.NO_OPTIONS */); 1931 } 1932 const existingApp = _apps.get(name); 1933 if (existingApp) { 1934 // return the existing app if options and config deep equal the ones in the existing app. 1935 if (deepEqual(options, existingApp.options) && 1936 deepEqual(config, existingApp.config)) { 1937 return existingApp; 1938 } 1939 else { 1940 throw ERROR_FACTORY$2.create("duplicate-app" /* AppError.DUPLICATE_APP */, { appName: name }); 1941 } 1942 } 1943 const container = new ComponentContainer(name); 1944 for (const component of _components.values()) { 1945 container.addComponent(component); 1946 } 1947 const newApp = new FirebaseAppImpl(options, config, container); 1948 _apps.set(name, newApp); 1949 return newApp; 1950 } 1951 /** 1952 * Retrieves a {@link @firebase/app#FirebaseApp} instance. 1953 * 1954 * When called with no arguments, the default app is returned. When an app name 1955 * is provided, the app corresponding to that name is returned. 1956 * 1957 * An exception is thrown if the app being retrieved has not yet been 1958 * initialized. 1959 * 1960 * @example 1961 * ```javascript 1962 * // Return the default app 1963 * const app = getApp(); 1964 * ``` 1965 * 1966 * @example 1967 * ```javascript 1968 * // Return a named app 1969 * const otherApp = getApp("otherApp"); 1970 * ``` 1971 * 1972 * @param name - Optional name of the app to return. If no name is 1973 * provided, the default is `"[DEFAULT]"`. 1974 * 1975 * @returns The app corresponding to the provided app name. 1976 * If no app name is provided, the default app is returned. 1977 * 1978 * @public 1979 */ 1980 function getApp(name = DEFAULT_ENTRY_NAME) { 1981 const app = _apps.get(name); 1982 if (!app && name === DEFAULT_ENTRY_NAME && getDefaultAppConfig()) { 1983 return initializeApp(); 1984 } 1985 if (!app) { 1986 throw ERROR_FACTORY$2.create("no-app" /* AppError.NO_APP */, { appName: name }); 1987 } 1988 return app; 1989 } 1990 /** 1991 * Registers a library's name and version for platform logging purposes. 1992 * @param library - Name of 1p or 3p library (e.g. firestore, angularfire) 1993 * @param version - Current version of that library. 1994 * @param variant - Bundle variant, e.g., node, rn, etc. 1995 * 1996 * @public 1997 */ 1998 function registerVersion(libraryKeyOrName, version, variant) { 1999 // TODO: We can use this check to whitelist strings when/if we set up 2000 // a good whitelist system. 2001 let library = PLATFORM_LOG_STRING[libraryKeyOrName] ?? libraryKeyOrName; 2002 if (variant) { 2003 library += `-${variant}`; 2004 } 2005 const libraryMismatch = library.match(/\s|\//); 2006 const versionMismatch = version.match(/\s|\//); 2007 if (libraryMismatch || versionMismatch) { 2008 const warning = [ 2009 `Unable to register library "${library}" with version "${version}":` 2010 ]; 2011 if (libraryMismatch) { 2012 warning.push(`library name "${library}" contains illegal characters (whitespace or "/")`); 2013 } 2014 if (libraryMismatch && versionMismatch) { 2015 warning.push('and'); 2016 } 2017 if (versionMismatch) { 2018 warning.push(`version name "${version}" contains illegal characters (whitespace or "/")`); 2019 } 2020 logger.warn(warning.join(' ')); 2021 return; 2022 } 2023 _registerComponent(new Component(`${library}-version`, () => ({ library, version }), "VERSION" /* ComponentType.VERSION */)); 2024 } 2025 2026 /** 2027 * @license 2028 * Copyright 2021 Google LLC 2029 * 2030 * Licensed under the Apache License, Version 2.0 (the "License"); 2031 * you may not use this file except in compliance with the License. 2032 * You may obtain a copy of the License at 2033 * 2034 * http://www.apache.org/licenses/LICENSE-2.0 2035 * 2036 * Unless required by applicable law or agreed to in writing, softw
vendor: 15,867 bytes, lines 2036-2395
2036are 2037 * distributed under the License is distributed on an "AS IS" BASIS, 2038 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2039 * See the License for the specific language governing permissions and 2040 * limitations under the License. 2041 */ 2042 const DB_NAME = 'firebase-heartbeat-database'; 2043 const DB_VERSION = 1; 2044 const STORE_NAME = 'firebase-heartbeat-store'; 2045 let dbPromise$2 = null; 2046 function getDbPromise$2() { 2047 if (!dbPromise$2) { 2048 dbPromise$2 = openDB(DB_NAME, DB_VERSION, { 2049 upgrade: (db, oldVersion) => { 2050 // We don't use 'break' in this switch statement, the fall-through 2051 // behavior is what we want, because if there are multiple versions between 2052 // the old version and the current version, we want ALL the migrations 2053 // that correspond to those versions to run, not only the last one. 2054 // eslint-disable-next-line default-case 2055 switch (oldVersion) { 2056 case 0: 2057 try { 2058 db.createObjectStore(STORE_NAME); 2059 } 2060 catch (e) { 2061 // Safari/iOS browsers throw occasional exceptions on 2062 // db.createObjectStore() that may be a bug. Avoid blocking 2063 // the rest of the app functionality. 2064 console.warn(e); 2065 } 2066 } 2067 } 2068 }).catch(e => { 2069 throw ERROR_FACTORY$2.create("idb-open" /* AppError.IDB_OPEN */, { 2070 originalErrorMessage: e.message 2071 }); 2072 }); 2073 } 2074 return dbPromise$2; 2075 } 2076 async function readHeartbeatsFromIndexedDB(app) { 2077 try { 2078 const db = await getDbPromise$2(); 2079 const tx = db.transaction(STORE_NAME); 2080 const result = await tx.objectStore(STORE_NAME).get(computeKey(app)); 2081 // We already have the value but tx.done can throw, 2082 // so we need to await it here to catch errors 2083 await tx.done; 2084 return result; 2085 } 2086 catch (e) { 2087 if (e instanceof FirebaseError) { 2088 logger.warn(e.message); 2089 } 2090 else { 2091 const idbGetError = ERROR_FACTORY$2.create("idb-get" /* AppError.IDB_GET */, { 2092 originalErrorMessage: e?.message 2093 }); 2094 logger.warn(idbGetError.message); 2095 } 2096 } 2097 } 2098 async function writeHeartbeatsToIndexedDB(app, heartbeatObject) { 2099 try { 2100 const db = await getDbPromise$2(); 2101 const tx = db.transaction(STORE_NAME, 'readwrite'); 2102 const objectStore = tx.objectStore(STORE_NAME); 2103 await objectStore.put(heartbeatObject, computeKey(app)); 2104 await tx.done; 2105 } 2106 catch (e) { 2107 if (e instanceof FirebaseError) { 2108 logger.warn(e.message); 2109 } 2110 else { 2111 const idbGetError = ERROR_FACTORY$2.create("idb-set" /* AppError.IDB_WRITE */, { 2112 originalErrorMessage: e?.message 2113 }); 2114 logger.warn(idbGetError.message); 2115 } 2116 } 2117 } 2118 function computeKey(app) { 2119 return `${app.name}!${app.options.appId}`; 2120 } 2121 2122 /** 2123 * @license 2124 * Copyright 2021 Google LLC 2125 * 2126 * Licensed under the Apache License, Version 2.0 (the "License"); 2127 * you may not use this file except in compliance with the License. 2128 * You may obtain a copy of the License at 2129 * 2130 * http://www.apache.org/licenses/LICENSE-2.0 2131 * 2132 * Unless required by applicable law or agreed to in writing, software 2133 * distributed under the License is distributed on an "AS IS" BASIS, 2134 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2135 * See the License for the specific language governing permissions and 2136 * limitations under the License. 2137 */ 2138 const MAX_HEADER_BYTES = 1024; 2139 const MAX_NUM_STORED_HEARTBEATS = 30; 2140 class HeartbeatServiceImpl { 2141 constructor(container) { 2142 this.container = container; 2143 /** 2144 * In-memory cache for heartbeats, used by getHeartbeatsHeader() to generate 2145 * the header string. 2146 * Stores one record per date. This will be consolidated into the standard 2147 * format of one record per user agent string before being sent as a header. 2148 * Populated from indexedDB when the controller is instantiated and should 2149 * be kept in sync with indexedDB. 2150 * Leave public for easier testing. 2151 */ 2152 this._heartbeatsCache = null; 2153 const app = this.container.getProvider('app').getImmediate(); 2154 this._storage = new HeartbeatStorageImpl(app); 2155 this._heartbeatsCachePromise = this._storage.read().then(result => { 2156 this._heartbeatsCache = result; 2157 return result; 2158 }); 2159 } 2160 /** 2161 * Called to report a heartbeat. The function will generate 2162 * a HeartbeatsByUserAgent object, update heartbeatsCache, and persist it 2163 * to IndexedDB. 2164 * Note that we only store one heartbeat per day. So if a heartbeat for today is 2165 * already logged, subsequent calls to this function in the same day will be ignored. 2166 */ 2167 async triggerHeartbeat() { 2168 try { 2169 const platformLogger = this.container 2170 .getProvider('platform-logger') 2171 .getImmediate(); 2172 // This is the "Firebase user agent" string from the platform logger 2173 // service, not the browser user agent. 2174 const agent = platformLogger.getPlatformInfoString(); 2175 const date = getUTCDateString(); 2176 if (this._heartbeatsCache?.heartbeats == null) { 2177 this._heartbeatsCache = await this._heartbeatsCachePromise; 2178 // If we failed to construct a heartbeats cache, then return immediately. 2179 if (this._heartbeatsCache?.heartbeats == null) { 2180 return; 2181 } 2182 } 2183 // Do not store a heartbeat if one is already stored for this day 2184 // or if a header has already been sent today. 2185 if (this._heartbeatsCache.lastSentHeartbeatDate === date || 2186 this._heartbeatsCache.heartbeats.some(singleDateHeartbeat => singleDateHeartbeat.date === date)) { 2187 return; 2188 } 2189 else { 2190 // There is no entry for this date. Create one. 2191 this._heartbeatsCache.heartbeats.push({ date, agent }); 2192 // If the number of stored heartbeats exceeds the maximum number of stored heartbeats, remove the heartbeat with the earliest date. 2193 // Since this is executed each time a heartbeat is pushed, the limit can only be exceeded by one, so only one needs to be removed. 2194 if (this._heartbeatsCache.heartbeats.length > MAX_NUM_STORED_HEARTBEATS) { 2195 const earliestHeartbeatIdx = getEarliestHeartbeatIdx(this._heartbeatsCache.heartbeats); 2196 this._heartbeatsCache.heartbeats.splice(earliestHeartbeatIdx, 1); 2197 } 2198 } 2199 return this._storage.overwrite(this._heartbeatsCache); 2200 } 2201 catch (e) { 2202 logger.warn(e); 2203 } 2204 } 2205 /** 2206 * Returns a base64 encoded string which can be attached to the heartbeat-specific header directly. 2207 * It also clears all heartbeats from memory as well as in IndexedDB. 2208 * 2209 * NOTE: Consuming product SDKs should not send the header if this method 2210 * returns an empty string. 2211 */ 2212 async getHeartbeatsHeader() { 2213 try { 2214 if (this._heartbeatsCache === null) { 2215 await this._heartbeatsCachePromise; 2216 } 2217 // If it's still null or the array is empty, there is no data to send. 2218 if (this._heartbeatsCache?.heartbeats == null || 2219 this._heartbeatsCache.heartbeats.length === 0) { 2220 return ''; 2221 } 2222 const date = getUTCDateString(); 2223 // Extract as many heartbeats from the cache as will fit under the size limit. 2224 const { heartbeatsToSend, unsentEntries } = extractHeartbeatsForHeader(this._heartbeatsCache.heartbeats); 2225 const headerString = base64urlEncodeWithoutPadding(JSON.stringify({ version: 2, heartbeats: heartbeatsToSend })); 2226 // Store last sent date to prevent another being logged/sent for the same day. 2227 this._heartbeatsCache.lastSentHeartbeatDate = date; 2228 if (unsentEntries.length > 0) { 2229 // Store any unsent entries if they exist. 2230 this._heartbeatsCache.heartbeats = unsentEntries; 2231 // This seems more likely than emptying the array (below) to lead to some odd state 2232 // since the cache isn't empty and this will be called again on the next request, 2233 // and is probably safest if we await it. 2234 await this._storage.overwrite(this._heartbeatsCache); 2235 } 2236 else { 2237 this._heartbeatsCache.heartbeats = []; 2238 // Do not wait for this, to reduce latency. 2239 void this._storage.overwrite(this._heartbeatsCache); 2240 } 2241 return headerString; 2242 } 2243 catch (e) { 2244 logger.warn(e); 2245 return ''; 2246 } 2247 } 2248 } 2249 function getUTCDateString() { 2250 const today = new Date(); 2251 // Returns date format 'YYYY-MM-DD' 2252 return today.toISOString().substring(0, 10); 2253 } 2254 function extractHeartbeatsForHeader(heartbeatsCache, maxSize = MAX_HEADER_BYTES) { 2255 // Heartbeats grouped by user agent in the standard format to be sent in 2256 // the header. 2257 const heartbeatsToSend = []; 2258 // Single date format heartbeats that are not sent. 2259 let unsentEntries = heartbeatsCache.slice(); 2260 for (const singleDateHeartbeat of heartbeatsCache) { 2261 // Look for an existing entry with the same user agent. 2262 const heartbeatEntry = heartbeatsToSend.find(hb => hb.agent === singleDateHeartbeat.agent); 2263 if (!heartbeatEntry) { 2264 // If no entry for this user agent exists, create one. 2265 heartbeatsToSend.push({ 2266 agent: singleDateHeartbeat.agent, 2267 dates: [singleDateHeartbeat.date] 2268 }); 2269 if (countBytes(heartbeatsToSend) > maxSize) { 2270 // If the header would exceed max size, remove the added heartbeat 2271 // entry and stop adding to the header. 2272 heartbeatsToSend.pop(); 2273 break; 2274 } 2275 } 2276 else { 2277 heartbeatEntry.dates.push(singleDateHeartbeat.date); 2278 // If the header would exceed max size, remove the added date 2279 // and stop adding to the header. 2280 if (countBytes(heartbeatsToSend) > maxSize) { 2281 heartbeatEntry.dates.pop(); 2282 break; 2283 } 2284 } 2285 // Pop unsent entry from queue. (Skipped if adding the entry exceeded 2286 // quota and the loop breaks early.) 2287 unsentEntries = unsentEntries.slice(1); 2288 } 2289 return { 2290 heartbeatsToSend, 2291 unsentEntries 2292 }; 2293 } 2294 class HeartbeatStorageImpl { 2295 constructor(app) { 2296 this.app = app; 2297 this._canUseIndexedDBPromise = this.runIndexedDBEnvironmentCheck(); 2298 } 2299 async runIndexedDBEnvironmentCheck() { 2300 if (!isIndexedDBAvailable()) { 2301 return false; 2302 } 2303 else { 2304 return validateIndexedDBOpenable() 2305 .then(() => true) 2306 .catch(() => false); 2307 } 2308 } 2309 /** 2310 * Read all heartbeats. 2311 */ 2312 async read() { 2313 const canUseIndexedDB = await this._canUseIndexedDBPromise; 2314 if (!canUseIndexedDB) { 2315 return { heartbeats: [] }; 2316 } 2317 else { 2318 const idbHeartbeatObject = await readHeartbeatsFromIndexedDB(this.app); 2319 if (idbHeartbeatObject?.heartbeats) { 2320 return idbHeartbeatObject; 2321 } 2322 else { 2323 return { heartbeats: [] }; 2324 } 2325 } 2326 } 2327 // overwrite the storage with the provided heartbeats 2328 async overwrite(heartbeatsObject) { 2329 const canUseIndexedDB = await this._canUseIndexedDBPromise; 2330 if (!canUseIndexedDB) { 2331 return; 2332 } 2333 else { 2334 const existingHeartbeatsObject = await this.read(); 2335 return writeHeartbeatsToIndexedDB(this.app, { 2336 lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ?? 2337 existingHeartbeatsObject.lastSentHeartbeatDate, 2338 heartbeats: heartbeatsObject.heartbeats 2339 }); 2340 } 2341 } 2342 // add heartbeats 2343 async add(heartbeatsObject) { 2344 const canUseIndexedDB = await this._canUseIndexedDBPromise; 2345 if (!canUseIndexedDB) { 2346 return; 2347 } 2348 else { 2349 const existingHeartbeatsObject = await this.read(); 2350 return writeHeartbeatsToIndexedDB(this.app, { 2351 lastSentHeartbeatDate: heartbeatsObject.lastSentHeartbeatDate ?? 2352 existingHeartbeatsObject.lastSentHeartbeatDate, 2353 heartbeats: [ 2354 ...existingHeartbeatsObject.heartbeats, 2355 ...heartbeatsObject.heartbeats 2356 ] 2357 }); 2358 } 2359 } 2360 } 2361 /** 2362 * Calculate bytes of a HeartbeatsByUserAgent array after being wrapped 2363 * in a platform logging header JSON object, stringified, and converted 2364 * to base 64. 2365 */ 2366 function countBytes(heartbeatsCache) { 2367 // base64 has a restricted set of characters, all of which should be 1 byte. 2368 return base64urlEncodeWithoutPadding( 2369 // heartbeatsCache wrapper properties 2370 JSON.stringify({ version: 2, heartbeats: heartbeatsCache })).length; 2371 } 2372 /** 2373 * Returns the index of the heartbeat with the earliest date. 2374 * If the heartbeats array is empty, -1 is returned. 2375 */ 2376 function getEarliestHeartbeatIdx(heartbeats) { 2377 if (heartbeats.length === 0) { 2378 return -1; 2379 } 2380 let earliestHeartbeatIdx = 0; 2381 let earliestHeartbeatDate = heartbeats[0].date; 2382 for (let i = 1; i < heartbeats.length; i++) { 2383 if (heartbeats[i].date < earliestHeartbeatDate) { 2384 earliestHeartbeatDate = heartbeats[i].date; 2385 earliestHeartbeatIdx = i; 2386 } 2387 } 2388 return earliestHeartbeatIdx; 2389 } 2390 2391 /** 2392 * @license 2393 * Copyright 2019 Google LLC 2394 * 2395 * Licensed under the Apache License, Version 2.0 (the "License");
2396 * you may not use this file except in compliance with the License. 2397 * You may obtain a copy of the License at 2398 * 2399 * http://www.apache.org/licenses/LICENSE-2.0 2400 * 2401 * Unless required by applicable law or agreed to in writing, software 2402 * distributed under the License is distributed on an "AS IS" BASIS, 2403 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2404 * See the License for the specific language governing permissions and 2405 * limitations under the License. 2406 */ 2407 function registerCoreComponents(variant) { 2408 _registerComponent(new Component('platform-logger', container => new PlatformLoggerServiceImpl(container), "PRIVATE" /* ComponentType.PRIVATE */)); 2409 _registerComponent(new Component('heartbeat', container => new HeartbeatServiceImpl(container), "PRIVATE" /* ComponentType.PRIVATE */)); 2410 // Register `app` package. 2411 registerVersion(name$q, version$1$1, variant); 2412 // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation 2413 registerVersion(name$q, version$1$1, 'esm2020'); 2414 // Register platform SDK identifier (no version). 2415 registerVersion('fire-js', ''); 2416 } 2417 2418 /** 2419 * Firebase App 2420 * 2421 * @remarks This package coordinates the communication between the different Firebase components 2422 * @packageDocumentation 2423 */ 2424 registerCoreComponents(''); 2425 2426 var name$1 = "firebase"; 2427 var version$2 = "12.14.0"; 2428 2429 /** 2430 * @license 2431 * Copyright 2020 Google LLC 2432 * 2433 * Licensed under the Apache License, Version 2.0 (the "License"); 2434 * you may not use this file except in compliance with the License. 2435 * You may obtain a copy of the License at 2436 * 2437 * http://www.apache.org/licenses/LICENSE-2.0 2438 * 2439 * Unless required by applicable law or agreed to in writing, software 2440 * distributed under the License is distributed on an "AS IS" BASIS, 2441 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2442 * See the License for the specific language governing permissions and 2443 * limitations under the License. 2444 */ 2445 registerVersion(name$1, version$2, 'app'); 2446 2447 const name = "@firebase/installations"; 2448 const version$1 = "0.6.22"; 2449 2450 /** 2451 * @license 2452 * Copyright 2019 Google LLC 2453 * 2454 * Licensed under the Apache License, Version 2.0 (the "License"); 2455 * you may not use this file except in compliance with the License. 2456 * You may obtain a copy of the License at 2457 * 2458 * http://www.apache.org/licenses/LICENSE-2.0 2459 * 2460 * Unless required by applicable law or agreed to in writing, software 2461 * distributed under the License is distributed on an "AS IS" BASIS, 2462 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2463 * See the License for the specific language governing permissions and 2464 * limitations under the License. 2465 */ 2466 const PENDING_TIMEOUT_MS = 10000; 2467 const PACKAGE_VERSION = `w:${version$1}`; 2468 const INTERNAL_AUTH_VERSION = 'FIS_v2'; 2469 const INSTALLATIONS_API_URL = 'https://firebaseinstallations.googleapis.com/v1'; 2470 const TOKEN_EXPIRATION_BUFFER = 60 * 60 * 1000; // One hour 2471 const SERVICE = 'installations'; 2472 const SERVICE_NAME = 'Installations'; 2473 2474 /** 2475 * @license 2476 * Copyright 2019 Google LLC 2477 * 2478 * Licensed under the Apache License, Version 2.0 (the "License"); 2479 * you may not use this file except in compliance with the License. 2480 * You may obtain a copy of the License at 2481 * 2482 * http://www.apache.org/licenses/LICENSE-2.0 2483 * 2484 * Unless required by applicable law or agreed to in writing, software 2485 * distributed under the License is distributed on an "AS IS" BASIS, 2486 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2487 * See the License for the specific language governing permissions and 2488 * limitations under the License. 2489 */ 2490 const ERROR_DESCRIPTION_MAP = { 2491 ["missing-app-config-values" /* ErrorCode.MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: "{$valueName}"', 2492 ["not-registered" /* ErrorCode.NOT_REGISTERED */]: 'Firebase Installation is not registered.', 2493 ["installation-not-found" /* ErrorCode.INSTALLATION_NOT_FOUND */]: 'Firebase Installation not found.', 2494 ["request-failed" /* ErrorCode.REQUEST_FAILED */]: '{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"', 2495 ["app-offline" /* ErrorCode.APP_OFFLINE */]: 'Could not process request. Application offline.', 2496 ["delete-pending-registration" /* ErrorCode.DELETE_PENDING_REGISTRATION */]: "Can't delete installation while there is a pending registration request." 2497 }; 2498 const ERROR_FACTORY$1 = new ErrorFactory(SERVICE, SERVICE_NAME, ERROR_DESCRIPTION_MAP); 2499 /** Returns true if error is a FirebaseError that is based on an error from the server. */ 2500 function isServerError(error) { 2501 return (error instanceof FirebaseError && 2502 error.code.includes("request-failed" /* ErrorCode.REQUEST_FAILED */)
vendor: 8,898 bytes, lines 2502-2726
2502); 2503 } 2504 2505 /** 2506 * @license 2507 * Copyright 2019 Google LLC 2508 * 2509 * Licensed under the Apache License, Version 2.0 (the "License"); 2510 * you may not use this file except in compliance with the License. 2511 * You may obtain a copy of the License at 2512 * 2513 * http://www.apache.org/licenses/LICENSE-2.0 2514 * 2515 * Unless required by applicable law or agreed to in writing, software 2516 * distributed under the License is distributed on an "AS IS" BASIS, 2517 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2518 * See the License for the specific language governing permissions and 2519 * limitations under the License. 2520 */ 2521 function getInstallationsEndpoint({ projectId }) { 2522 return `${INSTALLATIONS_API_URL}/projects/${projectId}/installations`; 2523 } 2524 function extractAuthTokenInfoFromResponse(response) { 2525 return { 2526 token: response.token, 2527 requestStatus: 2 /* RequestStatus.COMPLETED */, 2528 expiresIn: getExpiresInFromResponseExpiresIn(response.expiresIn), 2529 creationTime: Date.now() 2530 }; 2531 } 2532 async function getErrorFromResponse(requestName, response) { 2533 const responseJson = await response.json(); 2534 const errorData = responseJson.error; 2535 return ERROR_FACTORY$1.create("request-failed" /* ErrorCode.REQUEST_FAILED */, { 2536 requestName, 2537 serverCode: errorData.code, 2538 serverMessage: errorData.message, 2539 serverStatus: errorData.status 2540 }); 2541 } 2542 function getHeaders$1({ apiKey }) { 2543 return new Headers({ 2544 'Content-Type': 'application/json', 2545 Accept: 'application/json', 2546 'x-goog-api-key': apiKey 2547 }); 2548 } 2549 function getHeadersWithAuth(appConfig, { refreshToken }) { 2550 const headers = getHeaders$1(appConfig); 2551 headers.append('Authorization', getAuthorizationHeader(refreshToken)); 2552 return headers; 2553 } 2554 /** 2555 * Calls the passed in fetch wrapper and returns the response. 2556 * If the returned response has a status of 5xx, re-runs the function once and 2557 * returns the response. 2558 */ 2559 async function retryIfServerError(fn) { 2560 const result = await fn(); 2561 if (result.status >= 500 && result.status < 600) { 2562 // Internal Server Error. Retry request. 2563 return fn(); 2564 } 2565 return result; 2566 } 2567 function getExpiresInFromResponseExpiresIn(responseExpiresIn) { 2568 // This works because the server will never respond with fractions of a second. 2569 return Number(responseExpiresIn.replace('s', '000')); 2570 } 2571 function getAuthorizationHeader(refreshToken) { 2572 return `${INTERNAL_AUTH_VERSION} ${refreshToken}`; 2573 } 2574 2575 /** 2576 * @license 2577 * Copyright 2019 Google LLC 2578 * 2579 * Licensed under the Apache License, Version 2.0 (the "License"); 2580 * you may not use this file except in compliance with the License. 2581 * You may obtain a copy of the License at 2582 * 2583 * http://www.apache.org/licenses/LICENSE-2.0 2584 * 2585 * Unless required by applicable law or agreed to in writing, software 2586 * distributed under the License is distributed on an "AS IS" BASIS, 2587 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2588 * See the License for the specific language governing permissions and 2589 * limitations under the License. 2590 */ 2591 async function createInstallationRequest({ appConfig, heartbeatServiceProvider }, { fid }) { 2592 const endpoint = getInstallationsEndpoint(appConfig); 2593 const headers = getHeaders$1(appConfig); 2594 // If heartbeat service exists, add the heartbeat string to the header. 2595 const heartbeatService = heartbeatServiceProvider.getImmediate({ 2596 optional: true 2597 }); 2598 if (heartbeatService) { 2599 const heartbeatsHeader = await heartbeatService.getHeartbeatsHeader(); 2600 if (heartbeatsHeader) { 2601 headers.append('x-firebase-client', heartbeatsHeader); 2602 } 2603 } 2604 const body = { 2605 fid, 2606 authVersion: INTERNAL_AUTH_VERSION, 2607 appId: appConfig.appId, 2608 sdkVersion: PACKAGE_VERSION 2609 }; 2610 const request = { 2611 method: 'POST', 2612 headers, 2613 body: JSON.stringify(body) 2614 }; 2615 const response = await retryIfServerError(() => fetch(endpoint, request)); 2616 if (response.ok) { 2617 const responseValue = await response.json(); 2618 const registeredInstallationEntry = { 2619 fid: responseValue.fid || fid, 2620 registrationStatus: 2 /* RequestStatus.COMPLETED */, 2621 refreshToken: responseValue.refreshToken, 2622 authToken: extractAuthTokenInfoFromResponse(responseValue.authToken) 2623 }; 2624 return registeredInstallationEntry; 2625 } 2626 else { 2627 throw await getErrorFromResponse('Create Installation', response); 2628 } 2629 } 2630 2631 /** 2632 * @license 2633 * Copyright 2019 Google LLC 2634 * 2635 * Licensed under the Apache License, Version 2.0 (the "License"); 2636 * you may not use this file except in compliance with the License. 2637 * You may obtain a copy of the License at 2638 * 2639 * http://www.apache.org/licenses/LICENSE-2.0 2640 * 2641 * Unless required by applicable law or agreed to in writing, software 2642 * distributed under the License is distributed on an "AS IS" BASIS, 2643 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2644 * See the License for the specific language governing permissions and 2645 * limitations under the License. 2646 */ 2647 /** Returns a promise that resolves after given time passes. */ 2648 function sleep$1(ms) { 2649 return new Promise(resolve => { 2650 setTimeout(resolve, ms); 2651 }); 2652 } 2653 2654 /** 2655 * @license 2656 * Copyright 2019 Google LLC 2657 * 2658 * Licensed under the Apache License, Version 2.0 (the "License"); 2659 * you may not use this file except in compliance with the License. 2660 * You may obtain a copy of the License at 2661 * 2662 * http://www.apache.org/licenses/LICENSE-2.0 2663 * 2664 * Unless required by applicable law or agreed to in writing, software 2665 * distributed under the License is distributed on an "AS IS" BASIS, 2666 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2667 * See the License for the specific language governing permissions and 2668 * limitations under the License. 2669 */ 2670 function bufferToBase64UrlSafe(array) { 2671 const b64 = btoa(String.fromCharCode(...array)); 2672 return b64.replace(/\+/g, '-').replace(/\//g, '_'); 2673 } 2674 2675 /** 2676 * @license 2677 * Copyright 2019 Google LLC 2678 * 2679 * Licensed under the Apache License, Version 2.0 (the "License"); 2680 * you may not use this file except in compliance with the License. 2681 * You may obtain a copy of the License at 2682 * 2683 * http://www.apache.org/licenses/LICENSE-2.0 2684 * 2685 * Unless required by applicable law or agreed to in writing, software 2686 * distributed under the License is distributed on an "AS IS" BASIS, 2687 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2688 * See the License for the specific language governing permissions and 2689 * limitations under the License. 2690 */ 2691 const VALID_FID_PATTERN = /^[cdef][\w-]{21}$/; 2692 const INVALID_FID = ''; 2693 /** 2694 * Generates a new FID using random values from Web Crypto API. 2695 * Returns an empty string if FID generation fails for any reason. 2696 */ 2697 function generateFid() { 2698 try { 2699 // A valid FID has exactly 22 base64 characters, which is 132 bits, or 16.5 2700 // bytes. our implementation generates a 17 byte array instead. 2701 const fidByteArray = new Uint8Array(17); 2702 const crypto = self.crypto || self.msCrypto; 2703 crypto.getRandomValues(fidByteArray); 2704 // Replace the first 4 random bits with the constant FID header of 0b0111. 2705 fidByteArray[0] = 0b01110000 + (fidByteArray[0] % 0b00010000); 2706 const fid = encode(fidByteArray); 2707 return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID; 2708 } 2709 catch { 2710 // FID generation errored 2711 return INVALID_FID; 2712 } 2713 } 2714 /** Converts a FID Uint8Array to a base64 string representation. */ 2715 function encode(fidByteArray) { 2716 const b64String = bufferToBase64UrlSafe(fidByteArray); 2717 // Remove the 23rd character that was added because of the extra 4 bits at the 2718 // end of our 17 byte array, and the '=' padding. 2719 return b64String.substr(0, 22); 2720 } 2721 2722 /** 2723 * @license 2724 * Copyright 2019 Google LLC 2725 * 2726 * Licensed under the Apache License, Version 2.0 (the "License");
2727 * you may not use this file except in compliance with the License. 2728 * You may obtain a copy of the License at 2729 * 2730 * http://www.apache.org/licenses/LICENSE-2.0 2731 * 2732 * Unless required by applicable law or agreed to in writing, software 2733 * distributed under the License is distributed on an "AS IS" BASIS, 2734 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2735 * See the License for the specific language governing permissions and 2736 * limitations under the License. 2737 */ 2738 /** Returns a string key that can be used to identify the app. */ 2739 function getKey$1(appConfig) { 2740 return `${appConfig.appName}!${appConfig.appId}`; 2741 } 2742 2743 /** 2744 * @license 2745 * Copyright 2019 Google LLC 2746 * 2747 * Licensed under the Apache License, Version 2.0 (the "License"); 2748 * you may not use this file except in compliance with the License. 2749 * You may obtain a copy of the License at 2750 * 2751 * http://www.apache.org/licenses/LICENSE-2.0 2752 * 2753 * Unless required by applicable law or agreed to in writing, software 2754 * distributed under the License is distributed on an "AS IS" BASIS, 2755 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2756 * See the License for the specific language governing permissions and 2757 * limitations under the License. 2758 */ 2759 const fidChangeCallbacks = new Map(); 2760 /** 2761 * Calls the onIdChange callbacks with the new FID value, and broadcasts the 2762 * change to other tabs. 2763 */ 2764 function fidChanged(appConfig, fid) { 2765 const key = getKey$1(appConfig); 2766 callFidChangeCallbacks(key, fid); 2767 broadcastFidChange(key, fid); 2768 } 2769 function callFidChangeCallbacks(key, fid) { 2770 const callbacks = fidChangeCallbacks.get(key); 2771 if (!callbacks) { 2772 return; 2773 } 2774 for (const callback of callbacks) { 2775 callback(fid); 2776 } 2777 } 2778 function broadcastFidChange(key, fid) { 2779 const channel = getBroadcastChannel(); 2780 if (channel) { 2781 channel.postMessage({ key, fid }); 2782 } 2783 closeBroadcastChannel(); 2784 } 2785 let broadcastChannel = null; 2786 /** Opens and returns a BroadcastChannel if it is supported by the browser. */ 2787 function getBroadcastChannel() { 2788 if (!broadcastChannel && 'BroadcastChannel' in self) { 2789 broadcastChannel = new BroadcastChannel('[Firebase] FID Change'); 2790 broadcastChannel.onmessage = e => { 2791 callFidChangeCallbacks(e.data.key, e.data.fid); 2792 }; 2793 } 2794 return broadcastChannel; 2795 } 2796 function closeBroadcastChannel() { 2797 if (fidChangeCallbacks.size === 0 && broadcastChannel) { 2798 broadcastChannel.close(); 2799 broadcastChannel = null; 2800 } 2801 } 2802 2803 /** 2804 * @license 2805 * Copyright 2019 Google LLC 2806 * 2807 * Licensed under the Apache License, Version 2.0 (the "License"); 2808 * you may not use this file except in compliance with the License. 2809 * You may obtain a copy of the License at 2810 * 2811 * http://www.apache.org/licenses/LICENSE-2.0 2812 * 2813 * Unless required by applicable law or agreed to in writing, software 2814 * distributed under the License is distributed on an "AS IS" BASIS, 2815 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2816 * See the License for the specific language governing permissions and 2817 * limitations under the License. 2818 */ 2819 const DATABASE_NAME$1 = 'firebase-installations-database'; 2820 const DATABASE_VERSION$1 = 1; 2821 const OBJECT_STORE_NAME = 'firebase-installations-store'; 2822 let dbPromise$1 = null; 2823 function getDbPromise$1() { 2824 if (!dbPromise$1) { 2825 dbPromise$1 = openDB(DATABASE_NAME$1, DATABASE_VERSION$1, { 2826 upgrade: (db, oldVersion) => { 2827 // We don't use 'break' in this switch statement, the fall-through 2828 // behavior is what we want, because if there are multiple versions between 2829 // the old version and the current version, we want ALL the migrations 2830 // that correspond to those versions to run, not only the last one. 2831 // eslint-disable-next-line default-case 2832 switch (oldVersion) { 2833 case 0: 2834 db.createObjectStore(OBJECT_STORE_NAME); 2835 } 2836 } 2837 }); 2838 } 2839 return dbPromise$1; 2840 }
vendor: 17,835 bytes, lines 2841-3264
2841 /** Assigns or overwrites the record for the given key with the given value. */ 2842 async function set(appConfig, value) { 2843 const key = getKey$1(appConfig); 2844 const db = await getDbPromise$1(); 2845 const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite'); 2846 const objectStore = tx.objectStore(OBJECT_STORE_NAME); 2847 const oldValue = (await objectStore.get(key)); 2848 await objectStore.put(value, key); 2849 await tx.done; 2850 if (!oldValue || oldValue.fid !== value.fid) { 2851 fidChanged(appConfig, value.fid); 2852 } 2853 return value; 2854 } 2855 /** Removes record(s) from the objectStore that match the given key. */ 2856 async function remove(appConfig) { 2857 const key = getKey$1(appConfig); 2858 const db = await getDbPromise$1(); 2859 const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite'); 2860 await tx.objectStore(OBJECT_STORE_NAME).delete(key); 2861 await tx.done; 2862 } 2863 /** 2864 * Atomically updates a record with the result of updateFn, which gets 2865 * called with the current value. If newValue is undefined, the record is 2866 * deleted instead. 2867 * @return Updated value 2868 */ 2869 async function update(appConfig, updateFn) { 2870 const key = getKey$1(appConfig); 2871 const db = await getDbPromise$1(); 2872 const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite'); 2873 const store = tx.objectStore(OBJECT_STORE_NAME); 2874 const oldValue = (await store.get(key)); 2875 const newValue = updateFn(oldValue); 2876 if (newValue === undefined) { 2877 await store.delete(key); 2878 } 2879 else { 2880 await store.put(newValue, key); 2881 } 2882 await tx.done; 2883 if (newValue && (!oldValue || oldValue.fid !== newValue.fid)) { 2884 fidChanged(appConfig, newValue.fid); 2885 } 2886 return newValue; 2887 } 2888 2889 /** 2890 * @license 2891 * Copyright 2019 Google LLC 2892 * 2893 * Licensed under the Apache License, Version 2.0 (the "License"); 2894 * you may not use this file except in compliance with the License. 2895 * You may obtain a copy of the License at 2896 * 2897 * http://www.apache.org/licenses/LICENSE-2.0 2898 * 2899 * Unless required by applicable law or agreed to in writing, software 2900 * distributed under the License is distributed on an "AS IS" BASIS, 2901 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2902 * See the License for the specific language governing permissions and 2903 * limitations under the License. 2904 */ 2905 /** 2906 * Updates and returns the InstallationEntry from the database. 2907 * Also triggers a registration request if it is necessary and possible. 2908 */ 2909 async function getInstallationEntry(installations) { 2910 let registrationPromise; 2911 const installationEntry = await update(installations.appConfig, oldEntry => { 2912 const installationEntry = updateOrCreateInstallationEntry(oldEntry); 2913 const entryWithPromise = triggerRegistrationIfNecessary(installations, installationEntry); 2914 registrationPromise = entryWithPromise.registrationPromise; 2915 return entryWithPromise.installationEntry; 2916 }); 2917 if (installationEntry.fid === INVALID_FID) { 2918 // FID generation failed. Waiting for the FID from the server. 2919 return { installationEntry: await registrationPromise }; 2920 } 2921 return { 2922 installationEntry, 2923 registrationPromise 2924 }; 2925 } 2926 /** 2927 * Creates a new Installation Entry if one does not exist. 2928 * Also clears timed out pending requests. 2929 */ 2930 function updateOrCreateInstallationEntry(oldEntry) { 2931 const entry = oldEntry || { 2932 fid: generateFid(), 2933 registrationStatus: 0 /* RequestStatus.NOT_STARTED */ 2934 }; 2935 return clearTimedOutRequest(entry); 2936 } 2937 /** 2938 * If the Firebase Installation is not registered yet, this will trigger the 2939 * registration and return an InProgressInstallationEntry. 2940 * 2941 * If registrationPromise does not exist, the installationEntry is guaranteed 2942 * to be registered. 2943 */ 2944 function triggerRegistrationIfNecessary(installations, installationEntry) { 2945 if (installationEntry.registrationStatus === 0 /* RequestStatus.NOT_STARTED */) { 2946 if (!navigator.onLine) { 2947 // Registration required but app is offline. 2948 const registrationPromiseWithError = Promise.reject(ERROR_FACTORY$1.create("app-offline" /* ErrorCode.APP_OFFLINE */)); 2949 return { 2950 installationEntry, 2951 registrationPromise: registrationPromiseWithError 2952 }; 2953 } 2954 // Try registering. Change status to IN_PROGRESS. 2955 const inProgressEntry = { 2956 fid: installationEntry.fid, 2957 registrationStatus: 1 /* RequestStatus.IN_PROGRESS */, 2958 registrationTime: Date.now() 2959 }; 2960 const registrationPromise = registerInstallation(installations, inProgressEntry); 2961 return { installationEntry: inProgressEntry, registrationPromise }; 2962 } 2963 else if (installationEntry.registrationStatus === 1 /* RequestStatus.IN_PROGRESS */) { 2964 return { 2965 installationEntry, 2966 registrationPromise: waitUntilFidRegistration(installations) 2967 }; 2968 } 2969 else { 2970 return { installationEntry }; 2971 } 2972 } 2973 /** This will be executed only once for each new Firebase Installation. */ 2974 async function registerInstallation(installations, installationEntry) { 2975 try { 2976 const registeredInstallationEntry = await createInstallationRequest(installations, installationEntry); 2977 return set(installations.appConfig, registeredInstallationEntry); 2978 } 2979 catch (e) { 2980 if (isServerError(e) && e.customData.serverCode === 409) { 2981 // Server returned a "FID cannot be used" error. 2982 // Generate a new ID next time. 2983 await remove(installations.appConfig); 2984 } 2985 else { 2986 // Registration failed. Set FID as not registered. 2987 await set(installations.appConfig, { 2988 fid: installationEntry.fid, 2989 registrationStatus: 0 /* RequestStatus.NOT_STARTED */ 2990 }); 2991 } 2992 throw e; 2993 } 2994 } 2995 /** Call if FID registration is pending in another request. */ 2996 async function waitUntilFidRegistration(installations) { 2997 // Unfortunately, there is no way of reliably observing when a value in 2998 // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers), 2999 // so we need to poll. 3000 let entry = await updateInstallationRequest(installations.appConfig); 3001 while (entry.registrationStatus === 1 /* RequestStatus.IN_PROGRESS */) { 3002 // createInstallation request still in progress. 3003 await sleep$1(100); 3004 entry = await updateInstallationRequest(installations.appConfig); 3005 } 3006 if (entry.registrationStatus === 0 /* RequestStatus.NOT_STARTED */) { 3007 // The request timed out or failed in a different call. Try again. 3008 const { installationEntry, registrationPromise } = await getInstallationEntry(installations); 3009 if (registrationPromise) { 3010 return registrationPromise; 3011 } 3012 else { 3013 // if there is no registrationPromise, entry is registered. 3014 return installationEntry; 3015 } 3016 } 3017 return entry; 3018 } 3019 /** 3020 * Called only if there is a CreateInstallation request in progress. 3021 * 3022 * Updates the InstallationEntry in the DB based on the status of the 3023 * CreateInstallation request. 3024 * 3025 * Returns the updated InstallationEntry. 3026 */ 3027 function updateInstallationRequest(appConfig) { 3028 return update(appConfig, oldEntry => { 3029 if (!oldEntry) { 3030 throw ERROR_FACTORY$1.create("installation-not-found" /* ErrorCode.INSTALLATION_NOT_FOUND */); 3031 } 3032 return clearTimedOutRequest(oldEntry); 3033 }); 3034 } 3035 function clearTimedOutRequest(entry) { 3036 if (hasInstallationRequestTimedOut(entry)) { 3037 return { 3038 fid: entry.fid, 3039 registrationStatus: 0 /* RequestStatus.NOT_STARTED */ 3040 }; 3041 } 3042 return entry; 3043 } 3044 function hasInstallationRequestTimedOut(installationEntry) { 3045 return (installationEntry.registrationStatus === 1 /* RequestStatus.IN_PROGRESS */ && 3046 installationEntry.registrationTime + PENDING_TIMEOUT_MS < Date.now()); 3047 } 3048 3049 /** 3050 * @license 3051 * Copyright 2019 Google LLC 3052 * 3053 * Licensed under the Apache License, Version 2.0 (the "License"); 3054 * you may not use this file except in compliance with the License. 3055 * You may obtain a copy of the License at 3056 * 3057 * http://www.apache.org/licenses/LICENSE-2.0 3058 * 3059 * Unless required by applicable law or agreed to in writing, software 3060 * distributed under the License is distributed on an "AS IS" BASIS, 3061 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3062 * See the License for the specific language governing permissions and 3063 * limitations under the License. 3064 */ 3065 async function generateAuthTokenRequest({ appConfig, heartbeatServiceProvider }, installationEntry) { 3066 const endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry); 3067 const headers = getHeadersWithAuth(appConfig, installationEntry); 3068 // If heartbeat service exists, add the heartbeat string to the header. 3069 const heartbeatService = heartbeatServiceProvider.getImmediate({ 3070 optional: true 3071 }); 3072 if (heartbeatService) { 3073 const heartbeatsHeader = await heartbeatService.getHeartbeatsHeader(); 3074 if (heartbeatsHeader) { 3075 headers.append('x-firebase-client', heartbeatsHeader); 3076 } 3077 } 3078 const body = { 3079 installation: { 3080 sdkVersion: PACKAGE_VERSION, 3081 appId: appConfig.appId 3082 } 3083 }; 3084 const request = { 3085 method: 'POST', 3086 headers, 3087 body: JSON.stringify(body) 3088 }; 3089 const response = await retryIfServerError(() => fetch(endpoint, request)); 3090 if (response.ok) { 3091 const responseValue = await response.json(); 3092 const completedAuthToken = extractAuthTokenInfoFromResponse(responseValue); 3093 return completedAuthToken; 3094 } 3095 else { 3096 throw await getErrorFromResponse('Generate Auth Token', response); 3097 } 3098 } 3099 function getGenerateAuthTokenEndpoint(appConfig, { fid }) { 3100 return `${getInstallationsEndpoint(appConfig)}/${fid}/authTokens:generate`; 3101 } 3102 3103 /** 3104 * @license 3105 * Copyright 2019 Google LLC 3106 * 3107 * Licensed under the Apache License, Version 2.0 (the "License"); 3108 * you may not use this file except in compliance with the License. 3109 * You may obtain a copy of the License at 3110 * 3111 * http://www.apache.org/licenses/LICENSE-2.0 3112 * 3113 * Unless required by applicable law or agreed to in writing, software 3114 * distributed under the License is distributed on an "AS IS" BASIS, 3115 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3116 * See the License for the specific language governing permissions and 3117 * limitations under the License. 3118 */ 3119 /** 3120 * Returns a valid authentication token for the installation. Generates a new 3121 * token if one doesn't exist, is expired or about to expire. 3122 * 3123 * Should only be called if the Firebase Installation is registered. 3124 */ 3125 async function refreshAuthToken(installations, forceRefresh = false) { 3126 let tokenPromise; 3127 const entry = await update(installations.appConfig, oldEntry => { 3128 if (!isEntryRegistered(oldEntry)) { 3129 throw ERROR_FACTORY$1.create("not-registered" /* ErrorCode.NOT_REGISTERED */); 3130 } 3131 const oldAuthToken = oldEntry.authToken; 3132 if (!forceRefresh && isAuthTokenValid(oldAuthToken)) { 3133 // There is a valid token in the DB. 3134 return oldEntry; 3135 } 3136 else if (oldAuthToken.requestStatus === 1 /* RequestStatus.IN_PROGRESS */) { 3137 // There already is a token request in progress. 3138 tokenPromise = waitUntilAuthTokenRequest(installations, forceRefresh); 3139 return oldEntry; 3140 } 3141 else { 3142 // No token or token expired. 3143 if (!navigator.onLine) { 3144 throw ERROR_FACTORY$1.create("app-offline" /* ErrorCode.APP_OFFLINE */); 3145 } 3146 const inProgressEntry = makeAuthTokenRequestInProgressEntry(oldEntry); 3147 tokenPromise = fetchAuthTokenFromServer(installations, inProgressEntry); 3148 return inProgressEntry; 3149 } 3150 }); 3151 const authToken = tokenPromise 3152 ? await tokenPromise 3153 : entry.authToken; 3154 return authToken; 3155 } 3156 /** 3157 * Call only if FID is registered and Auth Token request is in progress. 3158 * 3159 * Waits until the current pending request finishes. If the request times out, 3160 * tries once in this thread as well. 3161 */ 3162 async function waitUntilAuthTokenRequest(installations, forceRefresh) { 3163 // Unfortunately, there is no way of reliably observing when a value in 3164 // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers), 3165 // so we need to poll. 3166 let entry = await updateAuthTokenRequest(installations.appConfig); 3167 while (entry.authToken.requestStatus === 1 /* RequestStatus.IN_PROGRESS */) { 3168 // generateAuthToken still in progress. 3169 await sleep$1(100); 3170 entry = await updateAuthTokenRequest(installations.appConfig); 3171 } 3172 const authToken = entry.authToken; 3173 if (authToken.requestStatus === 0 /* RequestStatus.NOT_STARTED */) { 3174 // The request timed out or failed in a different call. Try again. 3175 return refreshAuthToken(installations, forceRefresh); 3176 } 3177 else { 3178 return authToken; 3179 } 3180 } 3181 /** 3182 * Called only if there is a GenerateAuthToken request in progress. 3183 * 3184 * Updates the InstallationEntry in the DB based on the status of the 3185 * GenerateAuthToken request. 3186 * 3187 * Returns the updated InstallationEntry. 3188 */ 3189 function updateAuthTokenRequest(appConfig) { 3190 return update(appConfig, oldEntry => { 3191 if (!isEntryRegistered(oldEntry)) { 3192 throw ERROR_FACTORY$1.create("not-registered" /* ErrorCode.NOT_REGISTERED */); 3193 } 3194 const oldAuthToken = oldEntry.authToken; 3195 if (hasAuthTokenRequestTimedOut(oldAuthToken)) { 3196 return { 3197 ...oldEntry, 3198 authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ } 3199 }; 3200 } 3201 return oldEntry; 3202 }); 3203 } 3204 async function fetchAuthTokenFromServer(installations, installationEntry) { 3205 try { 3206 const authToken = await generateAuthTokenRequest(installations, installationEntry); 3207 const updatedInstallationEntry = { 3208 ...installationEntry, 3209 authToken 3210 }; 3211 await set(installations.appConfig, updatedInstallationEntry); 3212 return authToken; 3213 } 3214 catch (e) { 3215 if (isServerError(e) && 3216 (e.customData.serverCode === 401 || e.customData.serverCode === 404)) { 3217 // Server returned a "FID not found" or a "Invalid authentication" error. 3218 // Generate a new ID next time. 3219 await remove(installations.appConfig); 3220 } 3221 else { 3222 const updatedInstallationEntry = { 3223 ...installationEntry, 3224 authToken: { requestStatus: 0 /* RequestStatus.NOT_STARTED */ } 3225 }; 3226 await set(installations.appConfig, updatedInstallationEntry); 3227 } 3228 throw e; 3229 } 3230 } 3231 function isEntryRegistered(installationEntry) { 3232 return (installationEntry !== undefined && 3233 installationEntry.registrationStatus === 2 /* RequestStatus.COMPLETED */); 3234 } 3235 function isAuthTokenValid(authToken) { 3236 return (authToken.requestStatus === 2 /* RequestStatus.COMPLETED */ && 3237 !isAuthTokenExpired(authToken)); 3238 } 3239 function isAuthTokenExpired(authToken) { 3240 const now = Date.now(); 3241 return (now < authToken.creationTime || 3242 authToken.creationTime + authToken.expiresIn < now + TOKEN_EXPIRATION_BUFFER); 3243 } 3244 /** Returns an updated InstallationEntry with an InProgressAuthToken. */ 3245 function makeAuthTokenRequestInProgressEntry(oldEntry) { 3246 const inProgressAuthToken = { 3247 requestStatus: 1 /* RequestStatus.IN_PROGRESS */, 3248 requestTime: Date.now() 3249 }; 3250 return { 3251 ...oldEntry, 3252 authToken: inProgressAuthToken 3253 }; 3254 } 3255 function hasAuthTokenRequestTimedOut(authToken) { 3256 return (authToken.requestStatus === 1 /* RequestStatus.IN_PROGRESS */ && 3257 authToken.requestTime + PENDING_TIMEOUT_MS < Date.now()); 3258 } 3259 3260 /** 3261 * @license 3262 * Copyright 2019 Google LLC 3263 * 3264 * Licensed under the Apache License, Version 2.0 (the "License");
3265 * you may not use this file except in compliance with the License. 3266 * You may obtain a copy of the License at 3267 * 3268 * http://www.apache.org/licenses/LICENSE-2.0 3269 * 3270 * Unless required by applicable law or agreed to in writing, software 3271 * distributed under the License is distributed on an "AS IS" BASIS, 3272 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3273 * See the License for the specific language governing permissions and 3274 * limitations under the License. 3275 */ 3276 /** 3277 * Creates a Firebase Installation if there isn't one for the app and 3278 * returns the Installation ID. 3279 * @param installations - The `Installations` instance. 3280 * 3281 * @public 3282 */ 3283 async function getId(installations) { 3284 const installationsImpl = installations; 3285 const { installationEntry, registrationPromise } = await getInstallationEntry(installationsImpl); 3286 if (registrationPromise) { 3287 registrationPromise.catch(console.error); 3288 } 3289 else { 3290 // If the installation is already registered, update the authentication 3291 // token if needed. 3292 refreshAuthToken(installationsImpl).catch(console.error); 3293 } 3294 return installationEntry.fid; 3295 } 3296 3297 /** 3298 * @license 3299 * Copyright 2019 Google LLC 3300 * 3301 * Licensed under the Apache License, Version 2.0 (the "License"); 3302 * you may not use this file except in compliance with the License. 3303 * You may obtain a copy of the License at 3304 * 3305 * http://www.apache.org/licenses/LICENSE-2.0 3306 * 3307 * Unless required by applicable law or agreed to in writing, software 3308 * distributed under the License is distributed on an "AS IS" BASIS, 3309 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3310 * See the License for the specific language governing permissions and 3311 * limitations under the License. 3312 */ 3313 /** 3314 * Returns a Firebase Installations auth token, identifying the current 3315 * Firebase Installation. 3316 * @param installations - The `Installations` instance. 3317 * @param forceRefresh - Force refresh regardless of token expiration. 3318 * 3319 * @public 3320 */ 3321 async function getToken(installations, forceRefresh = false) { 3322 const installationsImpl = installations; 3323 await completeInstallationRegistration(installationsImpl); 3324 // At this point we either have a Registered Installation in the DB, or we've 3325 // already thrown an error. 3326 const authToken = await refreshAuthToken(installationsImpl, forceRefresh); 3327 return authToken.token; 3328 } 3329 async function completeInstallationRegistration(installations) { 3330 const { registrationPromise } = await getInstallationEntry(installations); 3331 if (registrationPromise) { 3332 // A createInstallation request is in progress. Wait until it finishes. 3333 await registrationPromise; 3334 } 3335 } 3336 3337 /** 3338 * @license 3339 * Copyright 2019 Google LLC 3340 * 3341 * Licensed under the Apache License, Version 2.0 (the "License"); 3342 * you may not use this file except in compliance with the License. 3343 * You may obtain a copy of the License at 3344 * 3345 * http://www.apache.org/licenses/LICENSE-2.0 3346 * 3347 * Unless required by applicable law or agreed to in writing, software 3348 * distributed under the License is distributed on an "AS IS" BASIS, 3349 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3350 * See the License for the specific language governing permissions and 3351 * limitations under the License. 3352 */ 3353 function extractAppConfig$1(app) { 3354 if (!app || !app.options) { 3355 throw getMissingValueError$1('App Configuration'); 3356 } 3357 if (!app.name) { 3358 throw getMissingValueError$1('App Name'); 3359 } 3360 // Required app config keys 3361 const configKeys = [ 3362 'projectId', 3363 'apiKey', 3364 'appId' 3365 ]; 3366 for (const keyName of configKeys) { 3367 if (!app.options[keyName]) { 3368 throw getMissingValueError$1(keyName); 3369 } 3370 } 3371 return { 3372 appName: app.name, 3373 projectId: app.options.projectId, 3374 apiKey: app.options.apiKey, 3375 appId: app.options.appId 3376 }; 3377 } 3378 function getMissingValueError$1(valueName) { 3379 return ERROR_FACTORY$1.create("missing-app-config-values" /* ErrorCode.MISSING_APP_CONFIG_VALUES */, { 3380 valueName 3381 }); 3382 } 3383 3384 /** 3385 * @license 3386 * Copyright 2020 Google LLC 3387 * 3388 * Licensed under the Apache License, Version 2.0 (the "License");
3389 * you may not use this file except in compliance with the License. 3390 * You may obtain a copy of the License at 3391 * 3392 * http://www.apache.org/licenses/LICENSE-2.0 3393 * 3394 * Unless required by applicable law or agreed to in writing, software 3395 * distributed under the License is distributed on an "AS IS" BASIS, 3396 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3397 * See the License for the specific language governing permissions and 3398 * limitations under the License. 3399 */ 3400 const INSTALLATIONS_NAME = 'installations'; 3401 const INSTALLATIONS_NAME_INTERNAL = 'installations-internal'; 3402 const publicFactory = (container) => { 3403 const app = container.getProvider('app').getImmediate(); 3404 // Throws if app isn't configured properly. 3405 const appConfig = extractAppConfig$1(app); 3406 const heartbeatServiceProvider = _getProvider(app, 'heartbeat'); 3407 const installationsImpl = { 3408 app, 3409 appConfig, 3410 heartbeatServiceProvider, 3411 _delete: () => Promise.resolve() 3412 }; 3413 return installationsImpl; 3414 }; 3415 const internalFactory = (container) => { 3416 const app = container.getProvider('app').getImmediate(); 3417 // Internal FIS instance relies on public FIS instance. 3418 const installations = _getProvider(app, INSTALLATIONS_NAME).getImmediate(); 3419 const installationsInternal = { 3420 getId: () => getId(installations), 3421 getToken: (forceRefresh) => getToken(installations, forceRefresh) 3422 }; 3423 return installationsInternal; 3424 }; 3425 function registerInstallations() { 3426 _registerComponent(new Component(INSTALLATIONS_NAME, publicFactory, "PUBLIC" /* ComponentType.PUBLIC */)); 3427 _registerComponent(new Component(INSTALLATIONS_NAME_INTERNAL, internalFactory, "PRIVATE" /* ComponentType.PRIVATE */)); 3428 } 3429 3430 /** 3431 * The Firebase Installations Web SDK. 3432 * This SDK does not work in a Node.js environment. 3433 * 3434 * @packageDocumentation 3435 */ 3436 registerInstallations(); 3437 registerVersion(name, version$1); 3438 // BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation 3439 registerVersion(name, version$1, 'esm2020'); 3440 3441 /** 3442 * @license 3443 * Copyright 2019 Google LLC 3444 * 3445 * Licensed under the Apache License, Version 2.0 (the "License"); 3446 * you may not use this file except in compliance with the License. 3447 * You may obtain a copy of the License at 3448 * 3449 * http://www.apache.org/licenses/LICENSE-2.0 3450 * 3451 * Unless required by applicable law or agreed to in writing, software 3452 * distributed under the License is distributed on an "AS IS" BASIS, 3453 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3454 * See the License for the specific language governing permissions and 3455 * limitations under the License. 3456 */ 3457 const DEFAULT_VAPID_KEY = 'BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoMj8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4'; 3458 const ENDPOINT = 'https://fcmregistrations.googleapis.com/v1'; 3459 /** Key of FCM Payload in Notification's data field. */ 3460 const FCM_MSG = 'FCM_MSG'; 3461 const CONSOLE_CAMPAIGN_ID = 'google.c.a.c_id'; 3462 const MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST = 1000; 3463 const MAX_RETRIES = 3; 3464 const LOG_INTERVAL_IN_MS = 86400000; //24 hour 3465 const DEFAULT_BACKOFF_TIME_MS = 5000; 3466 // FCM log source name registered at Firelog: 'FCM_CLIENT_EVENT_LOGGING'. It uniquely identifies 3467 // FCM's logging configuration. 3468 const FCM_LOG_SOURCE = 1249; 3469 // Defined as in proto/messaging_event.proto. Neglecting fields that are supported. 3470 const SDK_PLATFORM_WEB = 3; 3471 const EVENT_MESSAGE_DELIVERED = 1; 3472 var MessageType$1; 3473 (function (MessageType) { 3474 MessageType[MessageType["DATA_MESSAGE"] = 1] = "DATA_MESSAGE"; 3475 MessageType[MessageType["DISPLAY_NOTIFICATION"] = 3] = "DISPLAY_NOTIFICATION"; 3476 })(MessageType$1 || (MessageType$1 = {})); 3477 3478 /** 3479 * @license 3480 * Copyright 2018 Google LLC 3481 * 3482 * Licensed under the Apache License, Version 2.0 (the "License");
3482 you may not use this file except 3483 * in compliance with the License. You may obtain a copy of the License at 3484 * 3485 * http://www.apache.org/licenses/LICENSE-2.0 3486 * 3487 * Unless required by applicable law or agreed to in writing, software distributed under the License 3488 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 3489 * or implied. See the License for the specific language governing permissions and limitations under 3490 * the License. 3491 */ 3492 var MessageType; 3493 (function (MessageType) { 3494 MessageType["PUSH_RECEIVED"] = "push-received"; 3495 MessageType["NOTIFICATION_CLICKED"] = "notification-clicked"; 3496 MessageType["FID_REGISTERED"] = "fid-registered"; 3497 })(MessageType || (MessageType = {})); 3498 3499 /** 3500 * @license 3501 * Copyright 2017 Google LLC 3502 * 3503 * Licensed under the Apache License, Version 2.0 (the "License"); 3504 * you may not use this file except in compliance with the License. 3505 * You may obtain a copy of the License at 3506 * 3507 * http://www.apache.org/licenses/LICENSE-2.0 3508 * 3509 * Unless required by applicable law or agreed to in writing, software 3510 * distributed under the License is distributed on an "AS IS" BASIS, 3511 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3512 * See the License for the specific language governing permissions and 3513 * limitations under the License. 3514 */ 3515 function arrayToBase64(array) { 3516 const uint8Array = new Uint8Array(array); 3517 const base64String = btoa(String.fromCharCode(...uint8Array)); 3518 return base64String.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); 3519 } 3520 function base64ToArray(base64String) { 3521 const padding = '='.repeat((4 - (base64String.length % 4)) % 4); 3522 const base64 = (base64String + padding) 3523 .replace(/\-/g, '+') 3524 .replace(/_/g, '/'); 3525 const rawData = atob(base64); 3526 const outputArray = new Uint8Array(rawData.length); 3527 for (let i = 0; i < rawData.length; ++i) { 3528 outputArray[i] = rawData.charCodeAt(i); 3529 } 3530 return outputArray; 3531 } 3532 3533 /** 3534 * @license 3535 * Copyright 2019 Google LLC 3536 * 3537 * Licensed under the Apache License, Version 2.0 (the "License"); 3538 * you may not use this file except in compliance with the License. 3539 * You may obtain a copy of the License at 3540 * 3541 * http://www.apache.org/licenses/LICENSE-2.0 3542 * 3543 * Unless required by applicable law or agreed to in writing, software 3544 * distributed under the License is distributed on an "AS IS" BASIS, 3545 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3546 * See the License for the specific language governing permissions and 3547 * limitations under the License. 3548 */ 3549 const OLD_DB_NAME = 'fcm_token_details_db'; 3550 /** 3551 * The last DB version of 'fcm_token_details_db' was 4. This is one higher, so that the upgrade 3552 * callback is called for all versions of the old DB. 3553 */ 3554 const OLD_DB_VERSION = 5; 3555 const OLD_OBJECT_STORE_NAME = 'fcm_token_object_Store'; 3556 async function migrateOldDatabase(senderId) { 3557 if ('databases' in indexedDB) { 3558 // indexedDb.databases() is an IndexedDB v3 API and does not exist in all browsers. TODO: Remove 3559 // typecast when it lands in TS types. 3560 const databases = await indexedDB.databases(); 3561 const dbNames = databases.map(db => db.name); 3562 if (!dbNames.includes(OLD_DB_NAME)) { 3563 // old DB didn't exist, no need to open. 3564 return null; 3565 } 3566 } 3567 let tokenDetails = null; 3568 const db = await openDB(OLD_DB_NAME, OLD_DB_VERSION, { 3569 upgrade: async (db, oldVersion, newVersion, upgradeTransaction) => { 3570 if (oldVersion < 2) { 3571 // Database too old, skip migration. 3572 return; 3573 } 3574 if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) { 3575 // Database did not exist. Nothing to do. 3576 return; 3577 } 3578 const objectStore = upgradeTransaction.objectStore(OLD_OBJECT_STORE_NAME); 3579 const value = await objectStore.index('fcmSenderId').get(senderId); 3580 await objectStore.clear(); 3581 if (!value) { 3582 // No entry in the database, nothing to migrate. 3583 return; 3584 } 3585 if (oldVersion === 2) { 3586 const oldDetails = value; 3587 if (!oldDetails.auth || !oldDetails.p256dh || !oldDetails.endpoint) { 3588 return; 3589 } 3590 tokenDetails = { 3591 token: oldDetails.fcmToken, 3592 createTime: oldDetails.createTime ?? Date.now(), 3593 subscriptionOptions: { 3594 auth: oldDetails.auth, 3595 p256dh: oldDetails.p256dh, 3596 endpoint: oldDetails.endpoint, 3597 swScope: oldDetails.swScope, 3598 vapidKey: typeof oldDetails.vapidKey === 'string' 3599 ? oldDetails.vapidKey 3600 : arrayToBase64(oldDetails.vapidKey) 3601 } 3602 }; 3603 } 3604 else if (oldVersion === 3) { 3605 const oldDetails = value; 3606 tokenDetails = { 3607 token: oldDetails.fcmToken, 3608 createTime: oldDetails.createTime, 3609 subscriptionOptions: { 3610 auth: arrayToBase64(oldDetails.auth), 3611 p256dh: arrayToBase64(oldDetails.p256dh), 3612 endpoint: oldDetails.endpoint, 3613 swScope: oldDetails.swScope, 3614 vapidKey: arrayToBase64(oldDetails.vapidKey) 3615 } 3616 }; 3617 } 3618 else if (oldVersion === 4) { 3619 const oldDetails = value; 3620 tokenDetails = { 3621 token: oldDetails.fcmToken, 3622 createTime: oldDetails.createTime, 3623 subscriptionOptions: { 3624 auth: arrayToBase64(oldDetails.auth), 3625 p256dh: arrayToBase64(oldDetails.p256dh), 3626 endpoint: oldDetails.endpoint, 3627 swScope: oldDetails.swScope, 3628 vapidKey: arrayToBase64(oldDetails.vapidKey) 3629 } 3630 }; 3631 } 3632 } 3633 }); 3634 db.close(); 3635 // Delete all old databases. 3636 await deleteDB(OLD_DB_NAME); 3637 await deleteDB('fcm_vapid_details_db'); 3638 await deleteDB('undefined'); 3639 return checkTokenDetails(tokenDetails) ? tokenDetails : null; 3640 } 3641 function checkTokenDetails(tokenDetails) { 3642 if (!tokenDetails || !tokenDetails.subscriptionOptions) { 3643 return false;
3644 } 3645 const { subscriptionOptions } = tokenDetails; 3646 return (typeof tokenDetails.createTime === 'number' && 3647 tokenDetails.createTime > 0 && 3648 typeof tokenDetails.token === 'string' && 3649 tokenDetails.token.length > 0 && 3650 typeof subscriptionOptions.auth === 'string' && 3651 subscriptionOptions.auth.length > 0 && 3652 typeof subscriptionOptions.p256dh === 'string' && 3653 subscriptionOptions.p256dh.length > 0 && 3654 typeof subscriptionOptions.endpoint === 'string' && 3655 subscriptionOptions.endpoint.length > 0 && 3656 typeof subscriptionOptions.swScope === 'string' && 3657 subscriptionOptions.swScope.length > 0 && 3658 typeof subscriptionOptions.vapidKey === 'string' && 3659 subscriptionOptions.vapidKey.length > 0); 3660 } 3661 3662 /** 3663 * @license 3664 * Copyright 2017 Google LLC 3665 * 3666 * Licensed under the Apache License, Version 2.0 (the "License"); 3667 * you may not use this file except in compliance with the License. 3668 * You may obtain a copy of the License at 3669 * 3670 * http://www.apache.org/licenses/LICENSE-2.0 3671 * 3672 * Unless required by applicable law or agreed to in writing, software 3673 * distributed under the License is distributed on an "AS IS" BASIS, 3674 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3675 * See the License for the specific language governing permissions and 3676 * limitations under the License. 3677 */ 3678 const ERROR_MAP = { 3679 ["missing-app-config-values" /* ErrorCode.MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: "{$valueName}"', 3680 ["only-available-in-window" /* ErrorCode.AVAILABLE_IN_WINDOW */]: 'This method is available in a Window context.', 3681 ["only-available-in-sw" /* ErrorCode.AVAILABLE_IN_SW */]: 'This method is available in a service worker context.', 3682 ["permission-default" /* ErrorCode.PERMISSION_DEFAULT */]: 'The notification permission was not granted and dismissed instead.', 3683 ["permission-blocked" /* ErrorCode.PERMISSION_BLOCKED */]: 'The notification permission was not granted and blocked instead.', 3684 ["unsupported-browser" /* ErrorCode.UNSUPPORTED_BROWSER */]: "This browser doesn't support the API's required to use the Firebase SDK.", 3685 ["indexed-db-unsupported" /* ErrorCode.INDEXED_DB_UNSUPPORTED */]: "This browser doesn't support indexedDb.open() (ex. Safari iFrame, Firefox Private Browsing, etc)", 3686 ["failed-service-worker-registration" /* ErrorCode.FAILED_DEFAULT_REGISTRATION */]: 'We are unable to register the default service worker. {$browserErrorMessage}', 3687 ["token-subscribe-failed" /* ErrorCode.TOKEN_SUBSCRIBE_FAILED */]: 'A problem occurred while subscribing the user to FCM: {$errorInfo}', 3688 ["token-subscribe-no-token" /* ErrorCode.TOKEN_SUBSCRIBE_NO_TOKEN */]: 'FCM returned no token when subscribing the user to push.', 3689 ["fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */]: 'A problem occurred while creating an FCM registration via FID: {$errorInfo}', 3690 ["fid-unregister-failed" /* ErrorCode.FID_UNREGISTER_FAILED */]: 'A problem occurred while unregistering the FCM registration via FID: {$errorInfo}', 3691 ["fid-registration-idb-schema-unavailable" /* ErrorCode.FID_REGISTRATION_IDB_SCHEMA_UNAVAILABLE */]: 'Unable to read or persist FID registration metadata because the messaging ' + 3692 'IndexedDB schema is unavailable (for example, the database could not be ' + 3693 'upgraded to the latest version).', 3694 ["token-unsubscribe-failed" /* ErrorCode.TOKEN_UNSUBSCRIBE_FAILED */]: 'A problem occurred while unsubscribing the ' + 3695 'user from FCM: {$errorInfo}', 3696 ["token-update-failed" /* ErrorCode.TOKEN_UPDATE_FAILED */]: 'A problem occurred while updating the user from FCM: {$errorInfo}', 3697 ["token-update-no-token" /* ErrorCode.TOKEN_UPDATE_NO_TOKEN */]: 'FCM returned no token when updating the user to push.', 3698 ["use-sw-after-get-token" /* ErrorCode.USE_SW_AFTER_GET_TOKEN */]: 'The useServiceWorker() method may only be called once and must be ' + 3699 'called before calling getToken() to ensure your service worker is used.',
vendor: 11,649 bytes, lines 3700-3957
3700 ["invalid-sw-registration" /* ErrorCode.INVALID_SW_REGISTRATION */]: 'The input to useServiceWorker() must be a ServiceWorkerRegistration.', 3701 ["invalid-bg-handler" /* ErrorCode.INVALID_BG_HANDLER */]: 'The input to setBackgroundMessageHandler() must be a function.', 3702 ["invalid-vapid-key" /* ErrorCode.INVALID_VAPID_KEY */]: 'The public VAPID key must be a string.', 3703 ["use-vapid-key-after-get-token" /* ErrorCode.USE_VAPID_KEY_AFTER_GET_TOKEN */]: 'The usePublicVapidKey() method may only be called once and must be ' + 3704 'called before calling getToken() to ensure your VAPID key is used.', 3705 ["invalid-on-registered-handler" /* ErrorCode.INVALID_ON_REGISTERED_HANDLER */]: 'No onRegistered callback handler was provided or registered. Implement onRegistered() before register().' 3706 }; 3707 const ERROR_FACTORY = new ErrorFactory('messaging', 'Messaging', ERROR_MAP); 3708 3709 /** 3710 * @license 3711 * Copyright 2019 Google LLC 3712 * 3713 * Licensed under the Apache License, Version 2.0 (the "License"); 3714 * you may not use this file except in compliance with the License. 3715 * You may obtain a copy of the License at 3716 * 3717 * http://www.apache.org/licenses/LICENSE-2.0 3718 * 3719 * Unless required by applicable law or agreed to in writing, software 3720 * distributed under the License is distributed on an "AS IS" BASIS, 3721 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3722 * See the License for the specific language governing permissions and 3723 * limitations under the License. 3724 */ 3725 const DATABASE_NAME = 'firebase-messaging-database'; 3726 const DATABASE_VERSION = 2; 3727 const TOKEN_OBJECT_STORE_NAME = 'firebase-messaging-store'; 3728 const FID_REGISTRATION_OBJECT_STORE_NAME = 'firebase-messaging-fid-registration-store'; 3729 const defaultIdb = { openDB, deleteDB }; 3730 let idbImpl = defaultIdb; 3731 // Open v2, but fall back to v1 if upgrade/open fails. Cache as `unknown` and guard store access. 3732 let dbPromise = null; 3733 function migrateMessagingDb(upgradeDb, oldVersion, targetSchemaVersion) { 3734 // Intentional fall-through for v2: run all intermediate migrations. 3735 // eslint-disable-next-line default-case 3736 switch (oldVersion) { 3737 case 0: 3738 upgradeDb.createObjectStore(TOKEN_OBJECT_STORE_NAME); 3739 if (targetSchemaVersion === 1) { 3740 break; 3741 } 3742 // fall through 3743 case 1: 3744 if (targetSchemaVersion === 2) { 3745 upgradeDb.createObjectStore(FID_REGISTRATION_OBJECT_STORE_NAME); 3746 } 3747 } 3748 } 3749 function createOpenDbOptions(targetSchemaVersion) { 3750 return { 3751 upgrade: (upgradeDb, oldVersion) => { 3752 migrateMessagingDb(upgradeDb, oldVersion, targetSchemaVersion); 3753 }, 3754 blocked: () => { 3755 /* no-op */ 3756 }, 3757 blocking: (_currentVersion, _blockedVersion, event) => { 3758 dbPromise = null; 3759 event.target?.close(); 3760 }, 3761 terminated: () => { 3762 dbPromise = null; 3763 } 3764 }; 3765 } 3766 function getDbPromise() { 3767 if (!dbPromise) { 3768 const openLatest = idbImpl.openDB(DATABASE_NAME, DATABASE_VERSION, createOpenDbOptions(2)); 3769 // Assign synchronously to avoid concurrent openDB() calls. 3770 dbPromise = openLatest.catch(() => idbImpl.openDB(DATABASE_NAME, DATABASE_VERSION - 1, createOpenDbOptions(1))); 3771 } 3772 return dbPromise; 3773 } 3774 function hasObjectStore(db, storeName) { 3775 return db.objectStoreNames.contains(storeName); 3776 } 3777 function assertFidRegistrationObjectStore(db) { 3778 if (!hasObjectStore(db, FID_REGISTRATION_OBJECT_STORE_NAME)) { 3779 throw ERROR_FACTORY.create("fid-registration-idb-schema-unavailable" /* ErrorCode.FID_REGISTRATION_IDB_SCHEMA_UNAVAILABLE */); 3780 } 3781 } 3782 async function dbGet(firebaseDependencies) { 3783 const key = getKey(firebaseDependencies); 3784 const db = await getDbPromise(); 3785 const tokenDetails = (await db 3786 .transaction(TOKEN_OBJECT_STORE_NAME) 3787 .objectStore(TOKEN_OBJECT_STORE_NAME) 3788 .get(key)); 3789 if (tokenDetails) { 3790 return tokenDetails; 3791 } 3792 else { 3793 const oldTokenDetails = await migrateOldDatabase(firebaseDependencies.appConfig.senderId); 3794 if (oldTokenDetails) { 3795 await dbSet(firebaseDependencies, oldTokenDetails); 3796 return oldTokenDetails; 3797 } 3798 } 3799 } 3800 async function dbSet(firebaseDependencies, tokenDetails) { 3801 const key = getKey(firebaseDependencies); 3802 const db = await getDbPromise(); 3803 const stores = [TOKEN_OBJECT_STORE_NAME]; 3804 const hasFidStore = hasObjectStore(db, FID_REGISTRATION_OBJECT_STORE_NAME); 3805 if (hasFidStore) { 3806 stores.push(FID_REGISTRATION_OBJECT_STORE_NAME); 3807 } 3808 const tx = db.transaction(stores, 'readwrite'); 3809 await tx.objectStore(TOKEN_OBJECT_STORE_NAME).put(tokenDetails, key); 3810 if (hasFidStore) { 3811 await tx.objectStore(FID_REGISTRATION_OBJECT_STORE_NAME).delete(key); 3812 } 3813 await tx.done; 3814 return tokenDetails; 3815 } 3816 async function dbRemove(firebaseDependencies) { 3817 const key = getKey(firebaseDependencies); 3818 const db = await getDbPromise(); 3819 const tx = db.transaction(TOKEN_OBJECT_STORE_NAME, 'readwrite'); 3820 await tx.objectStore(TOKEN_OBJECT_STORE_NAME).delete(key); 3821 await tx.done; 3822 } 3823 async function dbGetFidRegistration(firebaseDependencies) { 3824 const key = getKey(firebaseDependencies); 3825 const db = await getDbPromise(); 3826 assertFidRegistrationObjectStore(db); 3827 return (await db 3828 .transaction(FID_REGISTRATION_OBJECT_STORE_NAME) 3829 .objectStore(FID_REGISTRATION_OBJECT_STORE_NAME) 3830 .get(key)); 3831 } 3832 async function dbSetFidRegistration(firebaseDependencies, details) { 3833 const key = getKey(firebaseDependencies); 3834 const db = await getDbPromise(); 3835 assertFidRegistrationObjectStore(db); 3836 const tx = db.transaction([TOKEN_OBJECT_STORE_NAME, FID_REGISTRATION_OBJECT_STORE_NAME], 'readwrite'); 3837 await tx.objectStore(FID_REGISTRATION_OBJECT_STORE_NAME).put(details, key); 3838 await tx.objectStore(TOKEN_OBJECT_STORE_NAME).delete(key); 3839 await tx.done; 3840 return details; 3841 } 3842 async function dbRemoveFidRegistration(firebaseDependencies) { 3843 const key = getKey(firebaseDependencies); 3844 const db = await getDbPromise(); 3845 assertFidRegistrationObjectStore(db); 3846 const tx = db.transaction(FID_REGISTRATION_OBJECT_STORE_NAME, 'readwrite'); 3847 await tx.objectStore(FID_REGISTRATION_OBJECT_STORE_NAME).delete(key); 3848 await tx.done; 3849 } 3850 function getKey({ appConfig }) { 3851 return appConfig.appId; 3852 } 3853 3854 const version = "0.13.0"; 3855 3856 /** 3857 * @license 3858 * Copyright 2019 Google LLC 3859 * 3860 * Licensed under the Apache License, Version 2.0 (the "License"); 3861 * you may not use this file except in compliance with the License. 3862 * You may obtain a copy of the License at 3863 * 3864 * http://www.apache.org/licenses/LICENSE-2.0 3865 * 3866 * Unless required by applicable law or agreed to in writing, software 3867 * distributed under the License is distributed on an "AS IS" BASIS, 3868 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3869 * See the License for the specific language governing permissions and 3870 * limitations under the License. 3871 */ 3872 /** Max attempts (initial fetch + retries) when CreateRegistration `fetch()` throws. */ 3873 const FID_REGISTRATION_FETCH_MAX_ATTEMPTS = 3; 3874 /** Base delay in ms; backoff is `BASE * 2^attempt` after each failed attempt. */ 3875 const FID_REGISTRATION_FETCH_BASE_BACKOFF_MS = 1000; 3876 async function requestGetToken(firebaseDependencies, subscriptionOptions) { 3877 const headers = await getHeaders(firebaseDependencies); 3878 const body = getBody(subscriptionOptions, firebaseDependencies.appConfig.appName, 3879 /* includeSdkVersion= */ false); 3880 const subscribeOptions = { 3881 method: 'POST', 3882 headers, 3883 body: JSON.stringify(body) 3884 }; 3885 let responseData; 3886 try { 3887 const response = await fetch(getEndpoint(firebaseDependencies.appConfig), subscribeOptions); 3888 responseData = await response.json(); 3889 } 3890 catch (err) { 3891 throw ERROR_FACTORY.create("token-subscribe-failed" /* ErrorCode.TOKEN_SUBSCRIBE_FAILED */, { 3892 errorInfo: err?.toString() 3893 }); 3894 } 3895 if (responseData.error) { 3896 const message = responseData.error.message; 3897 throw ERROR_FACTORY.create("token-subscribe-failed" /* ErrorCode.TOKEN_SUBSCRIBE_FAILED */, { 3898 errorInfo: message 3899 }); 3900 } 3901 if (!responseData.token) { 3902 throw ERROR_FACTORY.create("token-subscribe-no-token" /* ErrorCode.TOKEN_SUBSCRIBE_NO_TOKEN */); 3903 } 3904 return responseData.token; 3905 } 3906 async function requestCreateRegistration(firebaseDependencies, subscriptionOptions) { 3907 const headers = await getHeaders(firebaseDependencies); 3908 const body = getBody(subscriptionOptions, firebaseDependencies.appConfig.appName, 3909 /* includeSdkVersion= */ true); 3910 const subscribeOptions = { 3911 method: 'POST', 3912 headers, 3913 body: JSON.stringify(body) 3914 }; 3915 let response; 3916 try { 3917 response = await fetchWithExponentialRetry(() => fetch(getEndpoint(firebaseDependencies.appConfig), subscribeOptions), FID_REGISTRATION_FETCH_MAX_ATTEMPTS, FID_REGISTRATION_FETCH_BASE_BACKOFF_MS); 3918 } 3919 catch (err) { 3920 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 3921 errorInfo: err?.toString() 3922 }); 3923 } 3924 if (response.ok) { 3925 const responseFid = await parseCreateRegistrationSuccessFid(response); 3926 return { responseFid }; 3927 } 3928 // `fetch()` succeeded, but the backend returned a non-2xx response. 3929 // Best-effort parse the body to extract `error.message`, but always fail with 3930 // `FID_REGISTRATION_FAILED` to keep the error surface uniform. 3931 // Best-effort extraction of error details; the main signal is response.ok / status. 3932 let responseData; 3933 try { 3934 responseData = (await response.json()); 3935 } 3936 catch (err) { 3937 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 3938 errorInfo: response.statusText 3939 }); 3940 } 3941 const message = responseData.error?.message ?? response.statusText; 3942 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 3943 errorInfo: message 3944 }); 3945 } 3946 /** 3947 * Deletes an FCM Web registration via DeleteRegistration using the Firebase Installation ID (FID). 3948 */ 3949 async function requestDeleteRegistration(firebaseDependencies, fid) { 3950 const headers = await getHeaders(firebaseDependencies); 3951 const options = { 3952 method: 'DELETE', 3953 headers 3954 }; 3955 let response; 3956 try { 3957 response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}
3957/${fid}`, options); 3958 } 3959 catch (err) { 3960 throw ERROR_FACTORY.create("fid-unregister-failed" /* ErrorCode.FID_UNREGISTER_FAILED */, { 3961 errorInfo: err?.toString() 3962 }); 3963 } 3964 if (response.ok) { 3965 return; 3966 } 3967 // Best-effort parse error details; surface uniform error code. 3968 try { 3969 const responseData = (await response.json()); 3970 const message = responseData.error?.message ?? response.statusText; 3971 throw message; 3972 } 3973 catch (err) { 3974 // If parsing failed, fall back to status text. 3975 throw ERROR_FACTORY.create("fid-unregister-failed" /* ErrorCode.FID_UNREGISTER_FAILED */, { 3976 errorInfo: (typeof err === 'string' && err) || 3977 response.statusText || 3978 err?.toString() 3979 }); 3980 } 3981 } 3982 /** 3983 * Parses a successful CreateRegistration body. The backend must return JSON with a non-empty 3984 * string `name`: a resource name `projects/{projectId}/registrations/{fid}` 3985 */ 3986 async function parseCreateRegistrationSuccessFid(response) { 3987 const text = await response.text(); 3988 if (!text.trim()) { 3989 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 3990 errorInfo: 'CreateRegistration succeeded but response body is empty' 3991 }); 3992 } 3993 let data; 3994 try { 3995 data = JSON.parse(text); 3996 } 3997 catch { 3998 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 3999 errorInfo: 'CreateRegistration succeeded but response body is not valid JSON' 4000 }); 4001 } 4002 const name = data.name; 4003 if (typeof name !== 'string' || name.length === 0) { 4004 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 4005 errorInfo: 'CreateRegistration succeeded but response did not include a non-empty name' 4006 }); 4007 } 4008 return parseFidFromRegistrationResourceName(name); 4009 } 4010 const REGISTRATIONS_NAME_SEGMENT = '/registrations/'; 4011 /** Extracts the Firebase Installation ID from CreateRegistration `name` (resource path). */ 4012 function parseFidFromRegistrationResourceName(name) { 4013 const segmentIndex = name.indexOf(REGISTRATIONS_NAME_SEGMENT); 4014 if (segmentIndex !== -1) { 4015 const fid = name.slice(segmentIndex + REGISTRATIONS_NAME_SEGMENT.length); 4016 if (fid.length > 0) { 4017 return fid; 4018 } 4019 } 4020 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 4021 errorInfo: 'CreateRegistration succeeded but response name is not a valid registration resource name' 4022 }); 4023 } 4024 async function requestUpdateToken(firebaseDependencies, tokenDetails) { 4025 const headers = await getHeaders(firebaseDependencies); 4026 const body = getBody(tokenDetails.subscriptionOptions, firebaseDependencies.appConfig.appName, 4027 /* includeSdkVersion= */ false); 4028 const updateOptions = { 4029 method: 'PATCH', 4030 headers, 4031 body: JSON.stringify(body) 4032 }; 4033 let responseData; 4034 try { 4035 const response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}/${tokenDetails.token}`, updateOptions); 4036 responseData = await response.json(); 4037 } 4038 catch (err) { 4039 throw ERROR_FACTORY.create("token-update-failed" /* ErrorCode.TOKEN_UPDATE_FAILED */, { 4040 errorInfo: err?.toString() 4041 }); 4042 } 4043 if (responseData.error) { 4044 const message = responseData.error.message; 4045 throw ERROR_FACTORY.create("token-update-failed" /* ErrorCode.TOKEN_UPDATE_FAILED */, { 4046 errorInfo: message 4047 }); 4048 } 4049 if (!responseData.token) { 4050 throw ERROR_FACTORY.create("token-update-no-token" /* ErrorCode.TOKEN_UPDATE_NO_TOKEN */); 4051 } 4052 return responseData.token; 4053 } 4054 async function requestDeleteToken(firebaseDependencies, token) { 4055 const headers = await getHeaders(firebaseDependencies); 4056 const unsubscribeOptions = { 4057 method: 'DELETE', 4058 headers 4059 }; 4060 try { 4061 const response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}
4061/${token}`, unsubscribeOptions); 4062 const responseData = await response.json(); 4063 if (responseData.error) { 4064 const message = responseData.error.message; 4065 throw ERROR_FACTORY.create("token-unsubscribe-failed" /* ErrorCode.TOKEN_UNSUBSCRIBE_FAILED */, { 4066 errorInfo: message 4067 }); 4068 } 4069 } 4070 catch (err) { 4071 throw ERROR_FACTORY.create("token-unsubscribe-failed" /* ErrorCode.TOKEN_UNSUBSCRIBE_FAILED */, { 4072 errorInfo: err?.toString() 4073 }); 4074 } 4075 } 4076 /** 4077 * Re-runs `operation` when it throws, with exponential backoff between attempts. 4078 * Rethrows the last error if all attempts fail. 4079 */ 4080 async function fetchWithExponentialRetry(operation, maxAttempts, baseBackoffMs) { 4081 let lastError; 4082 for (let attempt = 0; attempt < maxAttempts; attempt++) { 4083 try { 4084 return await operation(); 4085 } 4086 catch (err) { 4087 lastError = err; 4088 if (attempt < maxAttempts - 1) { 4089 const delayMs = baseBackoffMs * Math.pow(2, attempt); 4090 await new Promise(resolve => setTimeout(resolve, delayMs)); 4091 } 4092 } 4093 } 4094 throw lastError; 4095 } 4096 function getEndpoint({ projectId }) { 4097 return `${ENDPOINT}/projects/${projectId}/registrations`; 4098 } 4099 async function getHeaders({ appConfig, installations }) { 4100 const authToken = await installations.getToken(); 4101 return new Headers({ 4102 'Content-Type': 'application/json', 4103 Accept: 'application/json', 4104 'x-goog-api-key': appConfig.apiKey, 4105 'x-goog-firebase-installations-auth': `FIS ${authToken}` 4106 }); 4107 } 4108 /** 4109 * Hostname for the registering web client (e.g. `www.example.com`), or the app name 4110 * (`appNameFallback`) when the scope cannot be resolved (e.g. some test environments). 4111 */ 4112 function getRegistrationOrigin(swScope, appNameFallback) { 4113 try { 4114 if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(swScope)) { 4115 return new URL(swScope).host; 4116 } 4117 } 4118 catch { 4119 // Fall through to relative-scope handling. 4120 } 4121 try { 4122 if (typeof self !== 'undefined' && self.location?.href) { 4123 return new URL(swScope, self.location.origin).host; 4124 } 4125 } 4126 catch { 4127 // Fall through. 4128 } 4129 if (typeof self !== 'undefined' && self.location?.host) { 4130 return self.location.host; 4131 } 4132 return appNameFallback; 4133 } 4134 function getBody({ p256dh, auth, endpoint, vapidKey, swScope }, appNameFallback, includeSdkVersion) { 4135 const body = { 4136 web: { 4137 origin: getRegistrationOrigin(swScope, appNameFallback), 4138 endpoint, 4139 auth, 4140 p256dh 4141 } 4142 }; 4143 if (includeSdkVersion) { 4144 // eslint-disable-next-line camelcase 4145 body.fcm_sdk_version = version; 4146 } 4147 if (vapidKey !== DEFAULT_VAPID_KEY) { 4148 body.web.applicationPubKey = vapidKey; 4149 } 4150 return body; 4151 } 4152 4153 /** 4154 * @license 4155 * Copyright 2019 Google LLC 4156 * 4157 * Licensed under the Apache License, Version 2.0 (the "License"); 4158 * you may not use this file except in compliance with the License. 4159 * You may obtain a copy of the License at 4160 * 4161 * http://www.apache.org/licenses/LICENSE-2.0 4162 * 4163 * Unless required by applicable law or agreed to in writing, software 4164 * distributed under the License is distributed on an "AS IS" BASIS, 4165 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4166 * See the License for the specific language governing permissions and 4167 * limitations under the License. 4168 */ 4169 // UpdateRegistration will be called once every week. 4170 const TOKEN_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days 4171 async function getTokenInternal(messaging) { 4172 const pushSubscription = await getPushSubscription$1(messaging.swRegistration, messaging.vapidKey); 4173 const subscriptionOptions = { 4174 vapidKey: messaging.vapidKey, 4175 swScope: messaging.swRegistration.scope, 4176 endpoint: pushSubscription.endpoint, 4177 auth: arrayToBase64(pushSubscription.getKey('auth')), 4178 p256dh: arrayToBase64(pushSubscription.getKey('p256dh')) 4179 }; 4180 const tokenDetails = await dbGet(messaging.firebaseDependencies); 4181 if (!tokenDetails) { 4182 // No token, get a new one. 4183 return getNewToken(messaging.firebaseDependencies, subscriptionOptions); 4184 } 4185 else if (!isTokenValid(tokenDetails.subscriptionOptions, subscriptionOptions)) { 4186 // Invalid token, get a new one. 4187 try { 4188 await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token); 4189 } 4190 catch (e) { 4191 // Suppress errors because of #2364 4192 console.warn(e); 4193 } 4194 return getNewToken(messaging.firebaseDependencies, subscriptionOptions); 4195 } 4196 else if (Date.now() >= tokenDetails.createTime + TOKEN_EXPIRATION_MS) { 4197 // Weekly token refresh 4198 return updateToken(messaging, { 4199 token: tokenDetails.token, 4200 createTime: Date.now(), 4201 subscriptionOptions 4202 }); 4203 } 4204 else { 4205 // Valid token, nothing to do. 4206 return tokenDetails.token; 4207 } 4208 } 4209 /** 4210 * Legacy getToken() path: there is a token row in IndexedDB. Revoke it with FCM, drop the row, and 4211 * clear any leftover FID registration metadata (apps may mix APIs). 4212 */ 4213 async function revokeLegacyFcmTokenAndClearCaches(messaging, tokenDetails) { 4214 await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token); 4215 await dbRemove(messaging.firebaseDependencies); 4216 await removeFidRegistrationBestEffort(messaging.firebaseDependencies); 4217 } 4218 /** 4219 * No legacy token row: the client may only have FID-based registration (register() flow). If so, 4220 * delete that registration on the server, always scrub local FID metadata, then surface 4221 * onUnregistered when we actually had an FID. 4222 */ 4223 async function revokeFidRegistrationIfStored(messaging) { 4224 const stored = await dbGetFidRegistration(messaging.firebaseDependencies).catch(() => undefined); 4225 const fid = stored?.fid; 4226 if (fid) { 4227 await requestDeleteRegistration(messaging.firebaseDependencies, fid); 4228 } 4229 await removeFidRegistrationBestEffort(messaging.firebaseDependencies); 4230 if (fid) { 4231 notifyOnUnregistered(messaging, fid); 4232 } 4233 } 4234 /** 4235 * Revokes the app's FCM registration: legacy token (getToken/deleteToken) and/or FI
vendor: 4,268 bytes, lines 4235-4349
4235D-based 4236 * registration (register/unregister), clears local caches, notifies onUnregistered when a stored 4237 * FID existed, then unsubscribes the push subscription when present. 4238 */ 4239 async function revokeRegistrationInternal(messaging) { 4240 const tokenDetails = await dbGet(messaging.firebaseDependencies); 4241 if (tokenDetails) { 4242 await revokeLegacyFcmTokenAndClearCaches(messaging, tokenDetails); 4243 } 4244 else { 4245 await revokeFidRegistrationIfStored(messaging); 4246 } 4247 // Unsubscribe from the push subscription. 4248 const pushSubscription = await messaging.swRegistration.pushManager.getSubscription(); 4249 if (pushSubscription) { 4250 return pushSubscription.unsubscribe(); 4251 } 4252 // If there's no SW, consider it a success. 4253 return true; 4254 } 4255 async function updateToken(messaging, tokenDetails) { 4256 try { 4257 const updatedToken = await requestUpdateToken(messaging.firebaseDependencies, tokenDetails); 4258 const updatedTokenDetails = { 4259 ...tokenDetails, 4260 token: updatedToken, 4261 createTime: Date.now() 4262 }; 4263 await dbSet(messaging.firebaseDependencies, updatedTokenDetails); 4264 return updatedToken; 4265 } 4266 catch (e) { 4267 throw e; 4268 } 4269 } 4270 async function getNewToken(firebaseDependencies, subscriptionOptions) { 4271 const token = await requestGetToken(firebaseDependencies, subscriptionOptions); 4272 const tokenDetails = { 4273 token, 4274 createTime: Date.now(), 4275 subscriptionOptions 4276 }; 4277 await dbSet(firebaseDependencies, tokenDetails); 4278 return tokenDetails.token; 4279 } 4280 /** 4281 * Gets a PushSubscription for the current user. 4282 */ 4283 async function getPushSubscription$1(swRegistration, vapidKey) { 4284 const subscription = await swRegistration.pushManager.getSubscription(); 4285 if (subscription) { 4286 return subscription; 4287 } 4288 return swRegistration.pushManager.subscribe({ 4289 userVisibleOnly: true, 4290 // Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key 4291 // submitted to pushManager#subscribe must be of type Uint8Array. 4292 applicationServerKey: base64ToArray(vapidKey) 4293 }); 4294 } 4295 /** 4296 * Checks if the saved tokenDetails object matches the configuration provided. 4297 */ 4298 function isTokenValid(dbOptions, currentOptions) { 4299 const isVapidKeyEqual = currentOptions.vapidKey === dbOptions.vapidKey; 4300 const isEndpointEqual = currentOptions.endpoint === dbOptions.endpoint; 4301 const isAuthEqual = currentOptions.auth === dbOptions.auth; 4302 const isP256dhEqual = currentOptions.p256dh === dbOptions.p256dh; 4303 return isVapidKeyEqual && isEndpointEqual && isAuthEqual && isP256dhEqual; 4304 } 4305 /** Clears FID registration metadata; apps may mix legacy getToken() with FID register/unregister. */ 4306 async function removeFidRegistrationBestEffort(firebaseDependencies) { 4307 try { 4308 await dbRemoveFidRegistration(firebaseDependencies); 4309 } 4310 catch { 4311 // Ignore. 4312 } 4313 } 4314 function notifyOnRegistered(messaging, fid) { 4315 const handler = messaging.onRegisteredHandler; 4316 if (!handler) { 4317 return; 4318 } 4319 if (typeof handler === 'function') { 4320 handler(fid); 4321 } 4322 else { 4323 handler.next(fid); 4324 } 4325 } 4326 function notifyOnUnregistered(messaging, fid) { 4327 const handler = messaging.onUnregisteredHandler; 4328 if (!handler) { 4329 return; 4330 } 4331 if (typeof handler === 'function') { 4332 handler(fid); 4333 } 4334 else { 4335 handler.next(fid); 4336 } 4337 } 4338 4339 /** 4340 * @license 4341 * Copyright 2020 Google LLC 4342 * 4343 * Licensed under the Apache License, Version 2.0 (the "License"); 4344 * you may not use this file except in compliance with the License. 4345 * You may obtain a copy of the License at 4346 * 4347 * http://www.apache.org/licenses/LICENSE-2.0 4348 * 4349 * Unless required by applicable law or agreed to in writing, softw
4349are 4350 * distributed under the License is distributed on an "AS IS" BASIS, 4351 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4352 * See the License for the specific language governing permissions and 4353 * limitations under the License. 4354 */ 4355 async function updateVapidKey(messaging, vapidKey) { 4356 if (!!vapidKey) { 4357 messaging.vapidKey = vapidKey; 4358 } 4359 else if (!messaging.vapidKey) { 4360 messaging.vapidKey = DEFAULT_VAPID_KEY; 4361 } 4362 } 4363 4364 /** 4365 * @license 4366 * Copyright 2020 Google LLC 4367 * 4368 * Licensed under the Apache License, Version 2.0 (the "License"); 4369 * you may not use this file except in compliance with the License. 4370 * You may obtain a copy of the License at 4371 * 4372 * http://www.apache.org/licenses/LICENSE-2.0 4373 * 4374 * Unless required by applicable law or agreed to in writing, software 4375 * distributed under the License is distributed on an "AS IS" BASIS, 4376 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4377 * See the License for the specific language governing permissions and 4378 * limitations under the License. 4379 */ 4380 /** Retries when CreateRegistration echoes an FID that does not match Installations.getId(). */ 4381 const FID_REGISTRATION_FID_MATCH_MAX_ATTEMPTS = 3; 4382 /** 4383 * For the new FID-based register path: 4384 * - Create (or refresh) an FCM Web registration in the backend via CreateRegistration. 4385 * - Use the FIS auth token produced by the installations instance (implicitly associated with FID). 4386 * - CreateRegistration must echo the installation in `name` (e.g. 4387 * `projects/{projectId}/registrations/{fid}`); it must match `expectedFid` from 4388 * Installations.getId(). On mismatch we refresh the auth token and retry, then fail with 4389 * `fid-registration-failed`. 4390 */ 4391 async function registerFcmRegistrationWithFid(messaging, expectedFid) { 4392 const pushSubscription = await getPushSubscription(messaging.swRegistration, messaging.vapidKey); 4393 const subscriptionOptions = { 4394 vapidKey: messaging.vapidKey, 4395 swScope: messaging.swRegistration.scope, 4396 endpoint: pushSubscription.endpoint, 4397 auth: arrayToBase64(pushSubscription.getKey('auth')), 4398 p256dh: arrayToBase64(pushSubscription.getKey('p256dh')) 4399 }; 4400 const installations = messaging.firebaseDependencies.installations; 4401 for (let attempt = 0; attempt < FID_REGISTRATION_FID_MATCH_MAX_ATTEMPTS; attempt++) { 4402 const { responseFid } = await requestCreateRegistration(messaging.firebaseDependencies, subscriptionOptions); 4403 if (responseFid === expectedFid) { 4404 return; 4405 } 4406 // If CreateRegistration echoes an unexpected FID, the FIS auth token used for the request may 4407 // be stale relative to the installation the backend associates with the call. Force-refresh 4408 // the token before retrying so the next attempt uses credentials aligned with Installations. 4409 if (attempt < FID_REGISTRATION_FID_MATCH_MAX_ATTEMPTS - 1) { 4410 await installations.getToken(true); 4411 } 4412 } 4413 throw ERROR_FACTORY.create("fid-registration-failed" /* ErrorCode.FID_REGISTRATION_FAILED */, { 4414 errorInfo: 'CreateRegistration response FID does not match Firebase Installation ID' 4415 }); 4416 } 4417 async function getPushSubscription(swRegistration, vapidKey) { 4418 const subscription = await swRegistration.pushManager.getSubscription(); 4419 if (subscription) { 4420 return subscription; 4421 } 4422 // Chrome/Firefox require applicationServerKey to be of type Uint8Array. 4423 return swRegistration.pushManager.subscribe({ 4424 userVisibleOnly: true, 4425 // `PushManager.subscribe` expects a `BufferSource`; `base64ToArray` produces a typed array. 4426 // Cast to satisfy the lib typing differences across TS DOM versions. 4427 applicationServerKey: base64ToArray(vapidKey) 4428 }); 4429 } 4430 4431 /** 4432 * @license 4433 * Copyright 2026 Google LLC 4434 * 4435 * Licensed under the Apache License, Version 2.0 (the "License");
4436 * you may not use this file except in compliance with the License. 4437 * You may obtain a copy of the License at 4438 * 4439 * http://www.apache.org/licenses/LICENSE-2.0 4440 * 4441 * Unless required by applicable law or agreed to in writing, software 4442 * distributed under the License is distributed on an "AS IS" BASIS, 4443 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4444 * See the License for the specific language governing permissions and 4445 * limitations under the License. 4446 */ 4447 /** 4448 * Re-runs FCM FID registration when push subscription keys change (e.g. `pushsubscriptionchange` 4449 * in the service worker). No-op if the app instance was never registered via `register()`. 4450 * Best-effort: callers should catch failures when permission or push may be unavailable. 4451 */ 4452 async function refreshFidRegistrationIfStored(messaging) { 4453 const stored = await dbGetFidRegistration(messaging.firebaseDependencies).catch(() => undefined); 4454 if (!stored) { 4455 return undefined; 4456 } 4457 await updateVapidKey(messaging, stored.vapidKey); 4458 const fid = await messaging.firebaseDependencies.installations.getId(); 4459 await registerFcmRegistrationWithFid(messaging, fid); 4460 await dbSetFidRegistration(messaging.firebaseDependencies, { 4461 fid, 4462 lastRegisterTime: Date.now(), 4463 vapidKey: messaging.vapidKey 4464 }); 4465 notifyOnRegistered(messaging, fid); 4466 return fid; 4467 } 4468 4469 /** 4470 * @license 4471 * Copyright 2020 Google LLC 4472 * 4473 * Licensed under the Apache License, Version 2.0 (the "License"); 4474 * you may not use this file except in compliance with the License. 4475 * You may obtain a copy of the License at 4476 * 4477 * http://www.apache.org/licenses/LICENSE-2.0 4478 * 4479 * Unless required by applicable law or agreed to in writing, software 4480 * distributed under the License is distributed on an "AS IS" BASIS, 4481 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4482 * See the License for the specific language governing permissions and 4483 * limitations under the License. 4484 */ 4485 function externalizePayload(internalPayload) { 4486 const payload = { 4487 from: internalPayload.from, 4488 // eslint-disable-next-line camelcase 4489 collapseKey: internalPayload.collapse_key, 4490 // eslint-disable-next-line camelcase 4491 messageId: internalPayload.fcmMessageId 4492 }; 4493 propagateNotificationPayload(payload, internalPayload); 4494 propagateDataPayload(payload, internalPayload); 4495 propagateFcmOptions(payload, internalPayload); 4496 return payload; 4497 } 4498 function propagateNotificationPayload(payload, messagePayloadInternal) { 4499 if (!messagePayloadInternal.notification) { 4500 return; 4501 } 4502 payload.notification = {}; 4503 const title = messagePayloadInternal.notification.title; 4504 if (!!title) { 4505 payload.notification.title = title; 4506 } 4507 const body = messagePayloadInternal.notification.body; 4508 if (!!body) { 4509 payload.notification.body = body; 4510 } 4511 const image = messagePayloadInternal.notification.image; 4512 if (!!image) { 4513 payload.notification.image = image; 4514 } 4515 const icon = messagePayloadInternal.notification.icon; 4516 if (!!icon) { 4517 payload.notification.icon = icon; 4518 } 4519 } 4520 function propagateDataPayload(payload, messagePayloadInternal) { 4521 if (!messagePayloadInternal.data) { 4522 return; 4523 } 4524 payload.data = messagePayloadInternal.data; 4525 } 4526 function propagateFcmOptions(payload, messagePayloadInternal) { 4527 // fcmOptions.link value is written into notification.click_action. see more in b/232072111 4528 if (!messagePayloadInternal.fcmOptions && 4529 !messagePayloadInternal.notification?.click_action) { 4530 return; 4531 } 4532 payload.fcmOptions = {}; 4533 const link = messagePayloadInternal.fcmOptions?.link ?? 4534 messagePayloadInternal.notification?.click_action; 4535 if (!!link) { 4536 payload.fcmOptions.link = link; 4537 } 4538 // eslint-disable-next-line camelcase 4539 const analyticsLabel = messagePayloadInternal.fcmOptions?.analytics_label; 4540 if (!!analyticsLabel) { 4541 payload.fcmOptions.analyticsLabel = analyticsLabel; 4542 } 4543 } 4544 4545 /** 4546 * @license 4547 * Copyright 2019 Google LLC 4548 * 4549 * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 9,527 bytes, lines 4550-4776
4550 * you may not use this file except in compliance with the License. 4551 * You may obtain a copy of the License at 4552 * 4553 * http://www.apache.org/licenses/LICENSE-2.0 4554 * 4555 * Unless required by applicable law or agreed to in writing, software 4556 * distributed under the License is distributed on an "AS IS" BASIS, 4557 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4558 * See the License for the specific language governing permissions and 4559 * limitations under the License. 4560 */ 4561 function isConsoleMessage(data) { 4562 // This message has a campaign ID, meaning it was sent using the Firebase Console. 4563 return typeof data === 'object' && !!data && CONSOLE_CAMPAIGN_ID in data; 4564 } 4565 4566 /** 4567 * @license 4568 * Copyright 2019 Google LLC 4569 * 4570 * Licensed under the Apache License, Version 2.0 (the "License"); 4571 * you may not use this file except in compliance with the License. 4572 * You may obtain a copy of the License at 4573 * 4574 * http://www.apache.org/licenses/LICENSE-2.0 4575 * 4576 * Unless required by applicable law or agreed to in writing, software 4577 * distributed under the License is distributed on an "AS IS" BASIS, 4578 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4579 * See the License for the specific language governing permissions and 4580 * limitations under the License. 4581 */ 4582 /** Returns a promise that resolves after given time passes. */ 4583 function sleep(ms) { 4584 return new Promise(resolve => { 4585 setTimeout(resolve, ms); 4586 }); 4587 } 4588 4589 /** 4590 * @license 4591 * Copyright 2019 Google LLC 4592 * 4593 * Licensed under the Apache License, Version 2.0 (the "License"); 4594 * you may not use this file except in compliance with the License. 4595 * You may obtain a copy of the License at 4596 * 4597 * http://www.apache.org/licenses/LICENSE-2.0 4598 * 4599 * Unless required by applicable law or agreed to in writing, software 4600 * distributed under the License is distributed on an "AS IS" BASIS, 4601 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4602 * See the License for the specific language governing permissions and 4603 * limitations under the License. 4604 */ 4605 const LOG_ENDPOINT = 'https://play.google.com/log?format=json_proto3'; 4606 /** First flush ASAP (next timer turn); `_dispatchLogEvents` reschedules with `LOG_INTERVAL_IN_MS`. */ 4607 const INITIAL_LOG_FLUSH_DELAY_MS = 0; 4608 const FCM_TRANSPORT_KEY = _mergeStrings('AzSCbw63g1R0nCw85jG8', 'Iaya3yLKwmgvh7cF0q4'); 4609 function startLoggingService(messaging) { 4610 // Start only if not already scheduled/in-flight and there is work to do. 4611 if (messaging.logQueue.state === 'stopped' && 4612 messaging.logEvents.length > 0) { 4613 _processQueue(messaging, INITIAL_LOG_FLUSH_DELAY_MS); 4614 } 4615 } 4616 /** 4617 * 4618 * @param messaging the messaging instance. 4619 * @param offsetInMs this method execute after `offsetInMs` elapsed . 4620 */ 4621 function _processQueue(messaging, offsetInMs) { 4622 if (messaging.logQueue.state === 'scheduled') { 4623 clearTimeout(messaging.logQueue.timerId); 4624 } 4625 messaging.logQueue = { state: 'stopped' }; 4626 if (!messaging.deliveryMetricsExportedToBigQueryEnabled) { 4627 messaging.logEvents = []; 4628 return; 4629 } 4630 messaging.logQueue = { 4631 state: 'scheduled', 4632 timerId: setTimeout(async () => { 4633 // Mark in-flight so stageLog/startLoggingService won't schedule duplicates mid-dispatch. 4634 messaging.logQueue = { state: 'flushing' }; 4635 if (!messaging.logEvents.length) { 4636 return _processQueue(messaging, LOG_INTERVAL_IN_MS); 4637 } 4638 await _dispatchLogEvents(messaging); 4639 }, offsetInMs) 4640 }; 4641 } 4642 async function _dispatchLogEvents(messaging) { 4643 // Swap the queue to avoid losing events added during an in-flight dispatch. 4644 const eventsToSend = messaging.logEvents; 4645 messaging.logEvents = []; 4646 for (let i = 0, n = eventsToSend.length; i < n; i += MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST) { 4647 const batch = eventsToSend.slice(i, i + MAX_NUMBER_OF_EVENTS_PER_LOG_REQUEST); 4648 if (!batch.length) { 4649 break; 4650 } 4651 const logRequest = _createLogRequest(batch); 4652 let retryCount = 0, response = {}; 4653 do { 4654 try { 4655 response = await fetch(LOG_ENDPOINT.concat('&key=', FCM_TRANSPORT_KEY), { 4656 method: 'POST', 4657 body: JSON.stringify(logRequest) 4658 }); 4659 // don't retry on 200s or non retriable errors 4660 if (response.ok || (!response.ok && !isRetriableError(response))) { 4661 break; 4662 } 4663 if (!response.ok && isRetriableError(response)) { 4664 // rethrow to retry with quota 4665 throw new Error('a retriable Non-200 code is returned in fetch to Firelog endpoint. Retry'); 4666 } 4667 } 4668 catch (error) { 4669 const isLastAttempt = retryCount === MAX_RETRIES; 4670 if (isLastAttempt) { 4671 // existing the do-while interactive retry logic because retry quota has reached. 4672 break; 4673 } 4674 } 4675 let delayInMs; 4676 try { 4677 delayInMs = Number((await response.json()).nextRequestWaitMillis); 4678 } 4679 catch (e) { 4680 delayInMs = DEFAULT_BACKOFF_TIME_MS; 4681 } 4682 await new Promise(resolve => setTimeout(resolve, delayInMs)); 4683 retryCount++; 4684 } while (retryCount < MAX_RETRIES); 4685 } 4686 // Schedule next flush. If new events arrived during this dispatch, flush ASAP. 4687 _processQueue(messaging, messaging.logEvents.length ? INITIAL_LOG_FLUSH_DELAY_MS : LOG_INTERVAL_IN_MS); 4688 } 4689 function isRetriableError(response) { 4690 const httpStatus = response.status; 4691 return (httpStatus === 429 || 4692 httpStatus === 500 || 4693 httpStatus === 503 || 4694 httpStatus === 504); 4695 } 4696 async function stageLog(messaging, internalPayload) { 4697 const fcmEvent = createFcmEvent(internalPayload, await messaging.firebaseDependencies.installations.getId()); 4698 createAndEnqueueLogEvent(messaging, fcmEvent, internalPayload.productId); 4699 startLoggingService(messaging); 4700 } 4701 function createFcmEvent(internalPayload, fid) { 4702 const fcmEvent = {}; 4703 /* eslint-disable camelcase */ 4704 // some fields should always be non-null. Still check to ensure. 4705 if (!!internalPayload.from) { 4706 fcmEvent.project_number = internalPayload.from; 4707 } 4708 if (!!internalPayload.fcmMessageId) { 4709 fcmEvent.message_id = internalPayload.fcmMessageId; 4710 } 4711 fcmEvent.instance_id = fid; 4712 if (!!internalPayload.notification) { 4713 fcmEvent.message_type = MessageType$1.DISPLAY_NOTIFICATION.toString(); 4714 } 4715 else { 4716 fcmEvent.message_type = MessageType$1.DATA_MESSAGE.toString(); 4717 } 4718 fcmEvent.sdk_platform = SDK_PLATFORM_WEB.toString(); 4719 fcmEvent.package_name = self.origin.replace(/(^\w+:|^)\/\//, ''); 4720 if (!!internalPayload.collapse_key) { 4721 fcmEvent.collapse_key = internalPayload.collapse_key; 4722 } 4723 fcmEvent.event = EVENT_MESSAGE_DELIVERED.toString(); 4724 if (!!internalPayload.fcmOptions?.analytics_label) { 4725 fcmEvent.analytics_label = internalPayload.fcmOptions?.analytics_label; 4726 } 4727 /* eslint-enable camelcase */ 4728 return fcmEvent; 4729 } 4730 function createAndEnqueueLogEvent(messaging, fcmEvent, productId) { 4731 const logEvent = {}; 4732 /* eslint-disable camelcase */ 4733 logEvent.event_time_ms = Math.floor(Date.now()).toString(); 4734 logEvent.source_extension_json_proto3 = JSON.stringify({ 4735 messaging_client_event: fcmEvent 4736 }); 4737 if (!!productId) { 4738 logEvent.compliance_data = buildComplianceData(productId); 4739 } 4740 // eslint-disable-next-line camelcase 4741 messaging.logEvents.push(logEvent); 4742 } 4743 function buildComplianceData(productId) { 4744 const complianceData = { 4745 privacy_context: { 4746 prequest: { 4747 origin_associated_product_id: productId 4748 } 4749 } 4750 }; 4751 return complianceData; 4752 } 4753 function _createLogRequest(logEventQueue) { 4754 const logRequest = {}; 4755 /* eslint-disable camelcase */ 4756 logRequest.log_source = FCM_LOG_SOURCE.toString(); 4757 logRequest.log_event = logEventQueue; 4758 /* eslint-enable camelcase */ 4759 return logRequest; 4760 } 4761 function _mergeStrings(s1, s2) { 4762 const resultArray = []; 4763 for (let i = 0; i < s1.length; i++) { 4764 resultArray.push(s1.charAt(i)); 4765 if (i < s2.length) { 4766 resultArray.push(s2.charAt(i)); 4767 } 4768 } 4769 return resultArray.join(''); 4770 } 4771 4772 /** 4773 * @license 4774 * Copyright 2017 Google LLC 4775 * 4776 * Licensed under the Apache License, Version 2.0 (the "License");
4777 * you may not use this file except in compliance with the License. 4778 * You may obtain a copy of the License at 4779 * 4780 * http://www.apache.org/licenses/LICENSE-2.0 4781 * 4782 * Unless required by applicable law or agreed to in writing, software 4783 * distributed under the License is distributed on an "AS IS" BASIS, 4784 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4785 * See the License for the specific language governing permissions and 4786 * limitations under the License. 4787 */ 4788 async function onSubChange(event, messaging) { 4789 if (!messaging.swRegistration) { 4790 messaging.swRegistration = self.registration; 4791 } 4792 const { newSubscription } = event; 4793 if (!newSubscription) { 4794 // Subscription revoked: legacy token and FID register/unregister paths both flow through 4795 // revokeRegistrationInternal (server revoke + onUnregistered when applicable). 4796 await revokeRegistrationInternal(messaging); 4797 return; 4798 } 4799 const storedFid = await dbGetFidRegistration(messaging.firebaseDependencies).catch(() => undefined); 4800 if (storedFid) { 4801 const fid = await refreshFidRegistrationIfStored(messaging).catch(() => { 4802 // Best-effort: push subscription may be unavailable after rotation. 4803 return undefined; 4804 }); 4805 if (fid) { 4806 const clientList = await getClientList(); 4807 if (hasVisibleClients(clientList)) { 4808 sendFidRegisteredToWindows(clientList, fid); 4809 } 4810 } 4811 return; 4812 } 4813 const tokenDetails = await dbGet(messaging.firebaseDependencies); 4814 await revokeRegistrationInternal(messaging); 4815 messaging.vapidKey = 4816 tokenDetails?.subscriptionOptions?.vapidKey ?? DEFAULT_VAPID_KEY; 4817 await getTokenInternal(messaging); 4818 } 4819 async function onPush(event, messaging) { 4820 const internalPayload = getMessagePayloadInternal(event); 4821 if (!internalPayload) { 4822 // Failed to get parsed MessagePayload from the PushEvent. Skip handling the push. 4823 return; 4824 } 4825 /* 4826 * Log to Firelog based on user consent. Rather than calling startLoggingService once when 4827 * deliveryMetricsExportedToBigQueryEnabled is toggled, we now call stageLog for every received push. 4828 * This ensures the first telemetry event is uploaded immediately upon enabling the flag, simplifying debugging. 4829 */ 4830 if (messaging.deliveryMetricsExportedToBigQueryEnabled) { 4831 await stageLog(messaging, internalPayload); 4832 } 4833 // foreground handling: eventually passed to onMessage hook 4834 const clientList = await getClientList(); 4835 if (hasVisibleClients(clientList)) { 4836 return sendMessagePayloadInternalToWindows(clientList, internalPayload); 4837 } 4838 // background handling: display if possible and pass to onBackgroundMessage hook 4839 if (!!internalPayload.notification) { 4840 await showNotification(wrapInternalPayload(internalPayload)); 4841 } 4842 if (!messaging) { 4843 return; 4844 } 4845 if (!!messaging.onBackgroundMessageHandler) { 4846 const payload = externalizePayload(internalPayload); 4847 if (typeof messaging.onBackgroundMessageHandler === 'function') { 4848 await messaging.onBackgroundMessageHandler(payload); 4849 } 4850 else { 4851 messaging.onBackgroundMessageHandler.next(payload); 4852 } 4853 } 4854 } 4855 async function onNotificationClick(event) { 4856 const internalPayload = event.notification?.data?.[FCM_MSG]; 4857 if (!internalPayload) { 4858 return; 4859 } 4860 else if (event.action) { 4861 // User clicked on an action button. This will allow developers to act on action button clicks 4862 // by using a custom onNotificationClick listener that they define. 4863 return; 4864 } 4865 // Prevent other listeners from receiving the event 4866 event.stopImmediatePropagation(); 4867 event.notification.close(); 4868 // Note clicking on a notification with no link set will focus the Chrome's current tab. 4869 const link = getLink(internalPayload); 4870 if (!link) { 4871 return; 4872 } 4873 // FM should only open/focus links from app's origin. 4874 const url = new URL(link, self.location.href); 4875 const originUrl = new URL(self.location.origin); 4876 if (url.host !== originUrl.host) { 4877 return; 4878 } 4879 let client = await getWindowClient(url); 4880 if (!client) { 4881 client = await self.clients.openWindow(link); 4882 // Wait three seconds for the client to initialize and set up the message handler so that it 4883 // can receive the message. 4884 await sleep(3000); 4885 } 4886 else { 4887 client = await client.focus(); 4888 } 4889 if (!client) {
4890 // Window Client will not be returned if it's for a third party origin. 4891 return; 4892 } 4893 internalPayload.messageType = MessageType.NOTIFICATION_CLICKED; 4894 internalPayload.isFirebaseMessaging = true; 4895 return client.postMessage(internalPayload); 4896 } 4897 function wrapInternalPayload(internalPayload) { 4898 const wrappedInternalPayload = { 4899 ...internalPayload.notification 4900 }; 4901 // Put the message payload under FCM_MSG name so we can identify the notification as being an FCM 4902 // notification vs a notification from somewhere else (i.e. normal web push or developer generated 4903 // notification). 4904 wrappedInternalPayload.data = { 4905 [FCM_MSG]: internalPayload 4906 }; 4907 return wrappedInternalPayload; 4908 } 4909 function getMessagePayloadInternal({ data }) { 4910 if (!data) { 4911 return null; 4912 } 4913 try { 4914 return data.json(); 4915 } 4916 catch (err) { 4917 // Not JSON so not an FCM message. 4918 return null; 4919 } 4920 } 4921 /**
vendor: 4,780 bytes, lines 4922-5049
4922 * @param url The URL to look for when focusing a client. 4923 * @return Returns an existing window client or a newly opened WindowClient. 4924 */ 4925 async function getWindowClient(url) { 4926 const clientList = await getClientList(); 4927 for (const client of clientList) { 4928 const clientUrl = new URL(client.url, self.location.href); 4929 if (url.host === clientUrl.host) { 4930 return client; 4931 } 4932 } 4933 return null; 4934 } 4935 /** 4936 * @returns If there is currently a visible WindowClient, this method will resolve to true, 4937 * otherwise false. 4938 */ 4939 function hasVisibleClients(clientList) { 4940 return clientList.some(client => client.visibilityState === 'visible' && 4941 // Ignore chrome-extension clients as that matches the background pages of extensions, which 4942 // are always considered visible for some reason. 4943 !client.url.startsWith('chrome-extension://')); 4944 } 4945 function sendMessagePayloadInternalToWindows(clientList, internalPayload) { 4946 internalPayload.isFirebaseMessaging = true; 4947 internalPayload.messageType = MessageType.PUSH_RECEIVED; 4948 for (const client of clientList) { 4949 client.postMessage(internalPayload); 4950 } 4951 } 4952 function sendFidRegisteredToWindows(clientList, fid) { 4953 const payload = { 4954 isFirebaseMessaging: true, 4955 messageType: MessageType.FID_REGISTERED, 4956 fid 4957 }; 4958 for (const client of clientList) { 4959 client.postMessage(payload); 4960 } 4961 } 4962 function getClientList() { 4963 return self.clients.matchAll({ 4964 type: 'window', 4965 includeUncontrolled: true 4966 // TS doesn't know that "type: 'window'" means it'll return WindowClient[] 4967 }); 4968 } 4969 function showNotification(notificationPayloadInternal) { 4970 // Note: Firefox does not support the maxActions property. 4971 // https://developer.mozilla.org/en-US/docs/Web/API/notification/maxActions 4972 const { actions } = notificationPayloadInternal; 4973 const { maxActions } = Notification; 4974 if (actions && maxActions && actions.length > maxActions) { 4975 console.warn(`This browser only supports ${maxActions} actions. The remaining actions will not be displayed.`); 4976 } 4977 return self.registration.showNotification( 4978 /* title= */ notificationPayloadInternal.title ?? '', notificationPayloadInternal); 4979 } 4980 function getLink(payload) { 4981 // eslint-disable-next-line camelcase 4982 const link = payload.fcmOptions?.link ?? payload.notification?.click_action; 4983 if (link) { 4984 return link; 4985 } 4986 if (isConsoleMessage(payload.data)) { 4987 // Notification created in the Firebase Console. Redirect to origin. 4988 return self.location.origin; 4989 } 4990 else { 4991 return null; 4992 } 4993 } 4994 4995 /** 4996 * @license 4997 * Copyright 2019 Google LLC 4998 * 4999 * Licensed under the Apache License, Version 2.0 (the "License"); 5000 * you may not use this file except in compliance with the License. 5001 * You may obtain a copy of the License at 5002 * 5003 * http://www.apache.org/licenses/LICENSE-2.0 5004 * 5005 * Unless required by applicable law or agreed to in writing, software 5006 * distributed under the License is distributed on an "AS IS" BASIS, 5007 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5008 * See the License for the specific language governing permissions and 5009 * limitations under the License. 5010 */ 5011 function extractAppConfig(app) { 5012 if (!app || !app.options) { 5013 throw getMissingValueError('App Configuration Object'); 5014 } 5015 if (!app.name) { 5016 throw getMissingValueError('App Name'); 5017 } 5018 // Required app config keys 5019 const configKeys = [ 5020 'projectId', 5021 'apiKey', 5022 'appId', 5023 'messagingSenderId' 5024 ]; 5025 const { options } = app; 5026 for (const keyName of configKeys) { 5027 if (!options[keyName]) { 5028 throw getMissingValueError(keyName); 5029 } 5030 } 5031 return { 5032 appName: app.name, 5033 projectId: options.projectId, 5034 apiKey: options.apiKey, 5035 appId: options.appId, 5036 senderId: options.messagingSenderId 5037 }; 5038 } 5039 function getMissingValueError(valueName) { 5040 return ERROR_FACTORY.create("missing-app-config-values" /* ErrorCode.MISSING_APP_CONFIG_VALUES */, { 5041 valueName 5042 }); 5043 } 5044 5045 /** 5046 * @license 5047 * Copyright 2020 Google LLC 5048 * 5049 * Licensed under the Apache License, Version 2.0 (the "License");
5050 * you may not use this file except in compliance with the License. 5051 * You may obtain a copy of the License at 5052 * 5053 * http://www.apache.org/licenses/LICENSE-2.0 5054 * 5055 * Unless required by applicable law or agreed to in writing, software 5056 * distributed under the License is distributed on an "AS IS" BASIS, 5057 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5058 * See the License for the specific language governing permissions and 5059 * limitations under the License. 5060 */ 5061 class MessagingService { 5062 constructor(app, installations, analyticsProvider) { 5063 // logging is only done with end user consent. Default to false. 5064 this.deliveryMetricsExportedToBigQueryEnabled = false; 5065 this.onBackgroundMessageHandler = null; 5066 this.onMessageHandler = null; 5067 /** Observer for the event that the app instance is registered with FCM via Firebase Installation ID (FID). */ 5068 this.onRegisteredHandler = null; 5069 /** Observer for the event that the app instance is unregistered from FCM (FID no longer active). */ 5070 this.onUnregisteredHandler = null; 5071 /** 5072 * Serializes the FID get + compare + notify step so concurrent register() calls
vendor: 4,334 bytes, lines 5073-5173
5073 * do not race each other. 5074 */ 5075 this._registerNotifyChain = Promise.resolve(); 5076 /** Unsubscribe from Installations `onIdChange` when messaging is deleted. */ 5077 this._fidChangeUnsubscribe = null; 5078 this.logEvents = []; 5079 /** 5080 * Single source of truth for the logging loop lifecycle. 5081 * 5082 * `scheduled` holds the active timer id; `flushing` indicates an async dispatch 5083 * is in progress (prevents duplicate starts); `stopped` means idle. 5084 */ 5085 this.logQueue = { state: 'stopped' }; 5086 const appConfig = extractAppConfig(app); 5087 this.firebaseDependencies = { 5088 app, 5089 appConfig, 5090 installations, 5091 analyticsProvider 5092 }; 5093 } 5094 _delete() { 5095 if (this._fidChangeUnsubscribe) { 5096 this._fidChangeUnsubscribe(); 5097 this._fidChangeUnsubscribe = null; 5098 } 5099 if (this.logQueue.state === 'scheduled') { 5100 clearTimeout(this.logQueue.timerId); 5101 } 5102 this.logQueue = { state: 'stopped' }; 5103 return Promise.resolve(); 5104 } 5105 } 5106 5107 /** 5108 * @license 5109 * Copyright 2020 Google LLC 5110 * 5111 * Licensed under the Apache License, Version 2.0 (the "License"); 5112 * you may not use this file except in compliance with the License. 5113 * You may obtain a copy of the License at 5114 * 5115 * http://www.apache.org/licenses/LICENSE-2.0 5116 * 5117 * Unless required by applicable law or agreed to in writing, software 5118 * distributed under the License is distributed on an "AS IS" BASIS, 5119 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5120 * See the License for the specific language governing permissions and 5121 * limitations under the License. 5122 */ 5123 const SwMessagingFactory = (container) => { 5124 const messaging = new MessagingService(container.getProvider('app').getImmediate(), container.getProvider('installations-internal').getImmediate(), container.getProvider('analytics-internal')); 5125 self.addEventListener('push', e => { 5126 e.waitUntil(onPush(e, messaging)); 5127 }); 5128 self.addEventListener('pushsubscriptionchange', e => { 5129 e.waitUntil(onSubChange(e, messaging)); 5130 }); 5131 self.addEventListener('notificationclick', e => { 5132 e.waitUntil(onNotificationClick(e)); 5133 }); 5134 return messaging; 5135 }; 5136 /** 5137 * The messaging instance registered in sw is named differently than that of in client. This is 5138 * because both `registerMessagingInWindow` and `registerMessagingInSw` would be called in 5139 * `messaging-compat` and component with the same name can only be registered once. 5140 */ 5141 function registerMessagingInSw() { 5142 _registerComponent(new Component('messaging-sw', SwMessagingFactory, "PUBLIC" /* ComponentType.PUBLIC */)); 5143 } 5144 5145 /** 5146 * @license 5147 * Copyright 2020 Google LLC 5148 * 5149 * Licensed under the Apache License, Version 2.0 (the "License"); 5150 * you may not use this file except in compliance with the License. 5151 * You may obtain a copy of the License at 5152 * 5153 * http://www.apache.org/licenses/LICENSE-2.0 5154 * 5155 * Unless required by applicable law or agreed to in writing, software 5156 * distributed under the License is distributed on an "AS IS" BASIS, 5157 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5158 * See the License for the specific language governing permissions and 5159 * limitations under the License. 5160 */ 5161 /** 5162 * Checks whether all required APIs exist within SW Context 5163 * @returns a Promise that resolves to a boolean. 5164 * 5165 * @public 5166 */ 5167 async function isSwSupported() { 5168 // firebase-js-sdk/issues/2393 reveals that idb#open in Safari iframe and Firefox private browsing 5169 // might be prohibited to run. In these contexts, an error would be thrown during the messaging 5170 // instantiating phase, informing the developers to import/call isSupported for special handling. 5171 return (isIndexedDBAvailable() && 5172 (await validateIndexedDBOpenable()) && 5173 'PushManager' in self &&
5174 'Notification' in self && 5175 ServiceWorkerRegistration.prototype.hasOwnProperty('showNotification') && 5176 PushSubscription.prototype.hasOwnProperty('getKey')); 5177 } 5178 5179 /** 5180 * @license 5181 * Copyright 2017 Google LLC 5182 * 5183 * Licensed under the Apache License, Version 2.0 (the "License"); 5184 * you may not use this file except in compliance with the License. 5185 * You may obtain a copy of the License at 5186 * 5187 * http://www.apache.org/licenses/LICENSE-2.0 5188 * 5189 * Unless required by applicable law or agreed to in writing, software 5190 * distributed under the License is distributed on an "AS IS" BASIS, 5191 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5192 * See the License for the specific language governing permissions and 5193 * limitations under the License. 5194 */ 5195 /** 5196 * Retrieves a Firebase Cloud Messaging instance. 5197 * 5198 * @returns The Firebase Cloud Messaging instance associated with the provided firebase app. 5199 * 5200 * @public 5201 */ 5202 function getMessagingInSw(app = getApp()) { 5203 // Conscious decision to make this async check non-blocking during the messaging instance 5204 // initialization phase for performance consideration. An error would be thrown latter for 5205 // developer's information. Developers can then choose to import and call `isSupported` for 5206 // special handling. 5207 isSwSupported().then(isSupported => { 5208 // If `isSwSupported()` resolved, but returned false. 5209 if (!isSupported) { 5210 throw ERROR_FACTORY.create("unsupported-browser" /* ErrorCode.UNSUPPORTED_BROWSER */); 5211 } 5212 }, _ => { 5213 // If `isSwSupported()` rejected. 5214 throw ERROR_FACTORY.create("indexed-db-unsupported" /* ErrorCode.INDEXED_DB_UNSUPPORTED */); 5215 }); 5216 return _getProvider(getModularInstance(app), 'messaging-sw').getImmediate(); 5217 } 5218 5219 /** 5220 * @license 5221 * Copyright 2017 Google LLC 5222 * 5223 * Licensed under the Apache License, Version 2.0 (the "License"); 5224 * you may not use this file except in compliance with the License. 5225 * You may obtain a copy of the License at 5226 * 5227 * http://www.apache.org/licenses/LICENSE-2.0 5228 * 5229 * Unless required by applicable law or agreed to in writing, software 5230 * distributed under the License is distributed on an "AS IS" BASIS, 5231 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5232 * See the License for the specific language governing permissions and 5233 * limitations under the License. 5234 */ 5235 registerMessagingInSw(); 5236 5237 const firebaseConfig = { 5238 apiKey: 'AIzaSyBWJoz_wRUkFn32AF-YE6ZnFDy98AUXGcs', 5239 authDomain: 'daily-cat.firebaseapp.com', 5240 databaseURL: 'https://daily-cat.firebaseio.com', 5241 projectId: 'daily-cat', 5242 storageBucket: 'daily-cat.firebasestorage.app', 5243 messagingSenderId: '982124839218', 5244 appId: '1:982124839218:web:50c60b718f60eca718572b', 5245 }; 5246 5247 /// <reference lib="webworker" /> 5248 const app = initializeApp(firebaseConfig); 5249 getMessagingInSw(app); 5250 5251})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.