{"version":3,"file":"firebase-messaging.js","sources":["../../node_modules/tslib/tslib.es6.js","../util/src/errors.ts","../util/src/sha1.ts","../util/src/hash.ts","../util/src/subscribe.ts","../messaging/src/models/errors.ts","../messaging/src/models/worker-page-message.ts","../messaging/src/models/fcm-details.ts","../messaging/src/helpers/is-array-buffer-equal.ts","../messaging/src/helpers/array-buffer-to-base64.ts","../messaging/src/models/iid-model.ts","../messaging/src/helpers/base64-to-array-buffer.ts","../messaging/src/models/clean-v1-undefined.ts","../messaging/src/models/db-interface.ts","../messaging/src/models/token-details-model.ts","../messaging/src/models/vapid-details-model.ts","../messaging/src/controllers/base-controller.ts","../messaging/src/controllers/sw-controller.ts","../messaging/src/models/default-sw.ts","../messaging/index.ts","../messaging/src/controllers/window-controller.ts"],"sourcesContent":["/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation. All rights reserved.\r\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use\r\nthis file except in compliance with the License. You may obtain a copy of the\r\nLicense at http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nTHIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\nKIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED\r\nWARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,\r\nMERCHANTABLITY OR NON-INFRINGEMENT.\r\n\r\nSee the Apache Version 2.0 License for specific language governing permissions\r\nand limitations under the License.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)\r\n t[p[i]] = s[p[i]];\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport function __exportStar(m, exports) {\r\n for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];\r\n}\r\n\r\nexport function __values(o) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator], i = 0;\r\n if (m) return m.call(o);\r\n return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\r\n result.default = mod;\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * @fileoverview Standardized Firebase Error.\n *\n * Usage:\n *\n * // Typescript string literals for type-safe codes\n * type Err =\n * 'unknown' |\n * 'object-not-found'\n * ;\n *\n * // Closure enum for type-safe error codes\n * // at-enum {string}\n * var Err = {\n * UNKNOWN: 'unknown',\n * OBJECT_NOT_FOUND: 'object-not-found',\n * }\n *\n * let errors: Map = {\n * 'generic-error': \"Unknown error\",\n * 'file-not-found': \"Could not find file: {$file}\",\n * };\n *\n * // Type-safe function - must pass a valid error code as param.\n * let error = new ErrorFactory('service', 'Service', errors);\n *\n * ...\n * throw error.create(Err.GENERIC);\n * ...\n * throw error.create(Err.FILE_NOT_FOUND, {'file': fileName});\n * ...\n * // Service: Could not file file: foo.txt (service/file-not-found).\n *\n * catch (e) {\n * assert(e.message === \"Could not find file: foo.txt.\");\n * if (e.code === 'service/file-not-found') {\n * console.log(\"Could not read file: \" + e['file']);\n * }\n * }\n */\nexport type ErrorList = { [code: string]: string };\n\nconst ERROR_NAME = 'FirebaseError';\n\nexport interface StringLike {\n toString: () => string;\n}\n\nlet captureStackTrace: (obj: Object, fn?: Function) => void = (Error as any)\n .captureStackTrace;\n\n// Export for faking in tests\nexport function patchCapture(captureFake?: any): any {\n let result: any = captureStackTrace;\n captureStackTrace = captureFake;\n return result;\n}\n\nexport interface FirebaseError {\n // Unique code for error - format is service/error-code-string\n code: string;\n\n // Developer-friendly error message.\n message: string;\n\n // Always 'FirebaseError'\n name: string;\n\n // Where available - stack backtrace in a string\n stack: string;\n}\n\nexport class FirebaseError implements FirebaseError {\n public stack: string;\n public name: string;\n\n constructor(public code: string, public message: string) {\n let stack: string;\n // We want the stack value, if implemented by Error\n if (captureStackTrace) {\n // Patches this.stack, omitted calls above ErrorFactory#create\n captureStackTrace(this, ErrorFactory.prototype.create);\n } else {\n try {\n // In case of IE11, stack will be set only after error is raised.\n // https://docs.microsoft.com/en-us/scripting/javascript/reference/stack-property-error-javascript\n throw Error.apply(this, arguments);\n } catch (err) {\n this.name = ERROR_NAME;\n // Make non-enumerable getter for the property.\n Object.defineProperty(this, 'stack', {\n get: function() {\n return err.stack;\n }\n });\n }\n }\n }\n}\n\n// Back-door inheritance\nFirebaseError.prototype = Object.create(Error.prototype) as FirebaseError;\nFirebaseError.prototype.constructor = FirebaseError;\n(FirebaseError.prototype as any).name = ERROR_NAME;\n\nexport class ErrorFactory {\n // Matches {$name}, by default.\n public pattern = /\\{\\$([^}]+)}/g;\n\n constructor(\n private service: string,\n private serviceName: string,\n private errors: ErrorList\n ) {\n // empty\n }\n\n create(code: T, data?: { [prop: string]: StringLike }): FirebaseError {\n if (data === undefined) {\n data = {};\n }\n\n let template = this.errors[code as string];\n\n let fullCode = this.service + '/' + code;\n let message: string;\n\n if (template === undefined) {\n message = 'Error';\n } else {\n message = template.replace(this.pattern, (match, key) => {\n let value = data![key];\n return value !== undefined ? value.toString() : '<' + key + '?>';\n });\n }\n\n // Service: Error message (service/code).\n message = this.serviceName + ': ' + message + ' (' + fullCode + ').';\n let err = new FirebaseError(fullCode, message);\n\n // Populate the Error object with message parts for programmatic\n // accesses (e.g., e.file).\n for (let prop in data) {\n if (!data.hasOwnProperty(prop) || prop.slice(-1) === '_') {\n continue;\n }\n (err as any)[prop] = data[prop];\n }\n\n return err;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Hash } from './hash';\n\n/**\n * @fileoverview SHA-1 cryptographic hash.\n * Variable names follow the notation in FIPS PUB 180-3:\n * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.\n *\n * Usage:\n * var sha1 = new sha1();\n * sha1.update(bytes);\n * var hash = sha1.digest();\n *\n * Performance:\n * Chrome 23: ~400 Mbit/s\n * Firefox 16: ~250 Mbit/s\n *\n */\n\n/**\n * SHA-1 cryptographic hash constructor.\n *\n * The properties declared here are discussed in the above algorithm document.\n * @constructor\n * @extends {Hash}\n * @final\n * @struct\n */\nexport class Sha1 extends Hash {\n /**\n * Holds the previous values of accumulated variables a-e in the compress_\n * function.\n * @type {!Array}\n * @private\n */\n private chain_: Array = [];\n\n /**\n * A buffer holding the partially computed hash result.\n * @type {!Array}\n * @private\n */\n private buf_: Array = [];\n\n /**\n * An array of 80 bytes, each a part of the message to be hashed. Referred to\n * as the message schedule in the docs.\n * @type {!Array}\n * @private\n */\n private W_: Array = [];\n\n /**\n * Contains data needed to pad messages less than 64 bytes.\n * @type {!Array}\n * @private\n */\n private pad_: Array = [];\n\n /**\n * @private {number}\n */\n private inbuf_: number = 0;\n\n /**\n * @private {number}\n */\n private total_: number = 0;\n\n constructor() {\n super();\n\n this.blockSize = 512 / 8;\n\n this.pad_[0] = 128;\n for (var i = 1; i < this.blockSize; ++i) {\n this.pad_[i] = 0;\n }\n\n this.reset();\n }\n\n reset() {\n this.chain_[0] = 0x67452301;\n this.chain_[1] = 0xefcdab89;\n this.chain_[2] = 0x98badcfe;\n this.chain_[3] = 0x10325476;\n this.chain_[4] = 0xc3d2e1f0;\n\n this.inbuf_ = 0;\n this.total_ = 0;\n }\n\n /**\n * Internal compress helper function.\n * @param {!Array|!Uint8Array|string} buf Block to compress.\n * @param {number=} opt_offset Offset of the block in the buffer.\n * @private\n */\n compress_(buf, opt_offset?) {\n if (!opt_offset) {\n opt_offset = 0;\n }\n\n var W = this.W_;\n\n // get 16 big endian words\n if (typeof buf === 'string') {\n for (var i = 0; i < 16; i++) {\n // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS\n // have a bug that turns the post-increment ++ operator into pre-increment\n // during JIT compilation. We have code that depends heavily on SHA-1 for\n // correctness and which is affected by this bug, so I've removed all uses\n // of post-increment ++ in which the result value is used. We can revert\n // this change once the Safari bug\n // (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and\n // most clients have been updated.\n W[i] =\n (buf.charCodeAt(opt_offset) << 24) |\n (buf.charCodeAt(opt_offset + 1) << 16) |\n (buf.charCodeAt(opt_offset + 2) << 8) |\n buf.charCodeAt(opt_offset + 3);\n opt_offset += 4;\n }\n } else {\n for (var i = 0; i < 16; i++) {\n W[i] =\n (buf[opt_offset] << 24) |\n (buf[opt_offset + 1] << 16) |\n (buf[opt_offset + 2] << 8) |\n buf[opt_offset + 3];\n opt_offset += 4;\n }\n }\n\n // expand to 80 words\n for (var i = 16; i < 80; i++) {\n var t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];\n W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;\n }\n\n var a = this.chain_[0];\n var b = this.chain_[1];\n var c = this.chain_[2];\n var d = this.chain_[3];\n var e = this.chain_[4];\n var f, k;\n\n // TODO(user): Try to unroll this loop to speed up the computation.\n for (var i = 0; i < 80; i++) {\n if (i < 40) {\n if (i < 20) {\n f = d ^ (b & (c ^ d));\n k = 0x5a827999;\n } else {\n f = b ^ c ^ d;\n k = 0x6ed9eba1;\n }\n } else {\n if (i < 60) {\n f = (b & c) | (d & (b | c));\n k = 0x8f1bbcdc;\n } else {\n f = b ^ c ^ d;\n k = 0xca62c1d6;\n }\n }\n\n var t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;\n e = d;\n d = c;\n c = ((b << 30) | (b >>> 2)) & 0xffffffff;\n b = a;\n a = t;\n }\n\n this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;\n this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;\n this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;\n this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;\n this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;\n }\n\n update(bytes, opt_length?) {\n // TODO(johnlenz): tighten the function signature and remove this check\n if (bytes == null) {\n return;\n }\n\n if (opt_length === undefined) {\n opt_length = bytes.length;\n }\n\n var lengthMinusBlock = opt_length - this.blockSize;\n var n = 0;\n // Using local instead of member variables gives ~5% speedup on Firefox 16.\n var buf = this.buf_;\n var inbuf = this.inbuf_;\n\n // The outer while loop should execute at most twice.\n while (n < opt_length) {\n // When we have no data in the block to top up, we can directly process the\n // input buffer (assuming it contains sufficient data). This gives ~25%\n // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that\n // the data is provided in large chunks (or in multiples of 64 bytes).\n if (inbuf == 0) {\n while (n <= lengthMinusBlock) {\n this.compress_(bytes, n);\n n += this.blockSize;\n }\n }\n\n if (typeof bytes === 'string') {\n while (n < opt_length) {\n buf[inbuf] = bytes.charCodeAt(n);\n ++inbuf;\n ++n;\n if (inbuf == this.blockSize) {\n this.compress_(buf);\n inbuf = 0;\n // Jump to the outer loop so we use the full-block optimization.\n break;\n }\n }\n } else {\n while (n < opt_length) {\n buf[inbuf] = bytes[n];\n ++inbuf;\n ++n;\n if (inbuf == this.blockSize) {\n this.compress_(buf);\n inbuf = 0;\n // Jump to the outer loop so we use the full-block optimization.\n break;\n }\n }\n }\n }\n\n this.inbuf_ = inbuf;\n this.total_ += opt_length;\n }\n\n /** @override */\n digest() {\n var digest = [];\n var totalBits = this.total_ * 8;\n\n // Add pad 0x80 0x00*.\n if (this.inbuf_ < 56) {\n this.update(this.pad_, 56 - this.inbuf_);\n } else {\n this.update(this.pad_, this.blockSize - (this.inbuf_ - 56));\n }\n\n // Add # bits.\n for (var i = this.blockSize - 1; i >= 56; i--) {\n this.buf_[i] = totalBits & 255;\n totalBits /= 256; // Don't use bit-shifting here!\n }\n\n this.compress_(this.buf_);\n\n var n = 0;\n for (var i = 0; i < 5; i++) {\n for (var j = 24; j >= 0; j -= 8) {\n digest[n] = (this.chain_[i] >> j) & 255;\n ++n;\n }\n }\n return digest;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Copyright 2011 The Closure Library Authors. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS-IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * @fileoverview Abstract cryptographic hash interface.\n *\n * See Sha1 and Md5 for sample implementations.\n *\n */\n\n/**\n * Create a cryptographic hash instance.\n *\n * @constructor\n * @struct\n */\nexport class Hash {\n /**\n * The block size for the hasher.\n * @type {number}\n */\n blockSize: number = -1;\n\n constructor() {}\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport type NextFn = (value: T) => void;\nexport type ErrorFn = (error: Error) => void;\nexport type CompleteFn = () => void;\n\nexport interface Observer {\n // Called once for each value in a stream of values.\n next: NextFn;\n\n // A stream terminates by a single call to EITHER error() or complete().\n error: ErrorFn;\n\n // No events will be sent to next() once complete() is called.\n complete: CompleteFn;\n}\n\nexport type PartialObserver = Partial>;\n\n// TODO: Support also Unsubscribe.unsubscribe?\nexport type Unsubscribe = () => void;\n\n/**\n * The Subscribe interface has two forms - passing the inline function\n * callbacks, or a object interface with callback properties.\n */\nexport interface Subscribe {\n (next?: NextFn, error?: ErrorFn, complete?: CompleteFn): Unsubscribe;\n (observer: PartialObserver): Unsubscribe;\n}\n\nexport interface Observable {\n // Subscribe method\n subscribe: Subscribe;\n}\n\nexport type Executor = (observer: Observer) => void;\n\n/**\n * Helper to make a Subscribe function (just like Promise helps make a\n * Thenable).\n *\n * @param executor Function which can make calls to a single Observer\n * as a proxy.\n * @param onNoObservers Callback when count of Observers goes to zero.\n */\nexport function createSubscribe(\n executor: Executor,\n onNoObservers?: Executor\n): Subscribe {\n let proxy = new ObserverProxy(executor, onNoObservers);\n return proxy.subscribe.bind(proxy);\n}\n\n/**\n * Implement fan-out for any number of Observers attached via a subscribe\n * function.\n */\nclass ObserverProxy implements Observer {\n private observers: Array> | undefined = [];\n private unsubscribes: Unsubscribe[] = [];\n private onNoObservers: Executor | undefined;\n private observerCount = 0;\n // Micro-task scheduling by calling task.then().\n private task = Promise.resolve();\n private finalized = false;\n private finalError: Error;\n\n /**\n * @param executor Function which can make calls to a single Observer\n * as a proxy.\n * @param onNoObservers Callback when count of Observers goes to zero.\n */\n constructor(executor: Executor, onNoObservers?: Executor) {\n this.onNoObservers = onNoObservers;\n // Call the executor asynchronously so subscribers that are called\n // synchronously after the creation of the subscribe function\n // can still receive the very first value generated in the executor.\n this.task\n .then(() => {\n executor(this);\n })\n .catch(e => {\n this.error(e);\n });\n }\n\n next(value: T) {\n this.forEachObserver((observer: Observer) => {\n observer.next(value);\n });\n }\n\n error(error: Error) {\n this.forEachObserver((observer: Observer) => {\n observer.error(error);\n });\n this.close(error);\n }\n\n complete() {\n this.forEachObserver((observer: Observer) => {\n observer.complete();\n });\n this.close();\n }\n\n /**\n * Subscribe function that can be used to add an Observer to the fan-out list.\n *\n * - We require that no event is sent to a subscriber sychronously to their\n * call to subscribe().\n */\n subscribe(\n nextOrObserver: PartialObserver | Function,\n error?: ErrorFn,\n complete?: CompleteFn\n ): Unsubscribe {\n let observer: Observer;\n\n if (\n nextOrObserver === undefined &&\n error === undefined &&\n complete === undefined\n ) {\n throw new Error('Missing Observer.');\n }\n\n // Assemble an Observer object when passed as callback functions.\n if (implementsAnyMethods(nextOrObserver, ['next', 'error', 'complete'])) {\n observer = nextOrObserver as Observer;\n } else {\n observer = {\n next: (nextOrObserver as any) as NextFn,\n error: error,\n complete: complete\n } as Observer;\n }\n\n if (observer.next === undefined) {\n observer.next = noop as NextFn;\n }\n if (observer.error === undefined) {\n observer.error = noop as ErrorFn;\n }\n if (observer.complete === undefined) {\n observer.complete = noop as CompleteFn;\n }\n\n let unsub = this.unsubscribeOne.bind(this, this.observers!.length);\n\n // Attempt to subscribe to a terminated Observable - we\n // just respond to the Observer with the final error or complete\n // event.\n if (this.finalized) {\n this.task.then(() => {\n try {\n if (this.finalError) {\n observer.error(this.finalError);\n } else {\n observer.complete();\n }\n } catch (e) {\n // nothing\n }\n return;\n });\n }\n\n this.observers!.push(observer as Observer);\n\n return unsub;\n }\n\n // Unsubscribe is synchronous - we guarantee that no events are sent to\n // any unsubscribed Observer.\n private unsubscribeOne(i: number) {\n if (this.observers === undefined || this.observers[i] === undefined) {\n return;\n }\n\n delete this.observers[i];\n\n this.observerCount -= 1;\n if (this.observerCount === 0 && this.onNoObservers !== undefined) {\n this.onNoObservers(this);\n }\n }\n\n private forEachObserver(fn: (observer: Observer) => void): void {\n if (this.finalized) {\n // Already closed by previous event....just eat the additional values.\n return;\n }\n\n // Since sendOne calls asynchronously - there is no chance that\n // this.observers will become undefined.\n for (let i = 0; i < this.observers!.length; i++) {\n this.sendOne(i, fn);\n }\n }\n\n // Call the Observer via one of it's callback function. We are careful to\n // confirm that the observe has not been unsubscribed since this asynchronous\n // function had been queued.\n private sendOne(i: number, fn: (observer: Observer) => void): void {\n // Execute the callback asynchronously\n this.task.then(() => {\n if (this.observers !== undefined && this.observers[i] !== undefined) {\n try {\n fn(this.observers[i]);\n } catch (e) {\n // Ignore exceptions raised in Observers or missing methods of an\n // Observer.\n // Log error to console. b/31404806\n if (typeof console !== 'undefined' && console.error) {\n console.error(e);\n }\n }\n }\n });\n }\n\n private close(err?: Error): void {\n if (this.finalized) {\n return;\n }\n this.finalized = true;\n if (err !== undefined) {\n this.finalError = err;\n }\n // Proxy is no longer needed - garbage collect references\n this.task.then(() => {\n this.observers = undefined;\n this.onNoObservers = undefined;\n });\n }\n}\n\n/** Turn synchronous function into one called asynchronously. */\nexport function async(fn: Function, onError?: ErrorFn): Function {\n return (...args: any[]) => {\n Promise.resolve(true)\n .then(() => {\n fn(...args);\n })\n .catch((error: Error) => {\n if (onError) {\n onError(error);\n }\n });\n };\n}\n\n/**\n * Return true if the object passed in implements any of the named methods.\n */\nfunction implementsAnyMethods(obj: any, methods: string[]): boolean {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n for (let method of methods) {\n if (method in obj && typeof obj[method] === 'function') {\n return true;\n }\n }\n\n return false;\n}\n\nfunction noop(): void {\n // do nothing\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ErrorFactory } from '@firebase/util';\n\nexport const ERROR_CODES = {\n AVAILABLE_IN_WINDOW: 'only-available-in-window',\n AVAILABLE_IN_SW: 'only-available-in-sw',\n SHOULD_BE_INHERITED: 'should-be-overriden',\n BAD_SENDER_ID: 'bad-sender-id',\n INCORRECT_GCM_SENDER_ID: 'incorrect-gcm-sender-id',\n PERMISSION_DEFAULT: 'permission-default',\n PERMISSION_BLOCKED: 'permission-blocked',\n UNSUPPORTED_BROWSER: 'unsupported-browser',\n NOTIFICATIONS_BLOCKED: 'notifications-blocked',\n FAILED_DEFAULT_REGISTRATION: 'failed-serviceworker-registration',\n SW_REGISTRATION_EXPECTED: 'sw-registration-expected',\n GET_SUBSCRIPTION_FAILED: 'get-subscription-failed',\n INVALID_SAVED_TOKEN: 'invalid-saved-token',\n SW_REG_REDUNDANT: 'sw-reg-redundant',\n TOKEN_SUBSCRIBE_FAILED: 'token-subscribe-failed',\n TOKEN_SUBSCRIBE_NO_TOKEN: 'token-subscribe-no-token',\n TOKEN_SUBSCRIBE_NO_PUSH_SET: 'token-subscribe-no-push-set',\n TOKEN_UNSUBSCRIBE_FAILED: 'token-unsubscribe-failed',\n TOKEN_UPDATE_FAILED: 'token-update-failed',\n TOKEN_UPDATE_NO_TOKEN: 'token-update-no-token',\n USE_SW_BEFORE_GET_TOKEN: 'use-sw-before-get-token',\n INVALID_DELETE_TOKEN: 'invalid-delete-token',\n DELETE_TOKEN_NOT_FOUND: 'delete-token-not-found',\n DELETE_SCOPE_NOT_FOUND: 'delete-scope-not-found',\n BG_HANDLER_FUNCTION_EXPECTED: 'bg-handler-function-expected',\n NO_WINDOW_CLIENT_TO_MSG: 'no-window-client-to-msg',\n UNABLE_TO_RESUBSCRIBE: 'unable-to-resubscribe',\n NO_FCM_TOKEN_FOR_RESUBSCRIBE: 'no-fcm-token-for-resubscribe',\n FAILED_TO_DELETE_TOKEN: 'failed-to-delete-token',\n NO_SW_IN_REG: 'no-sw-in-reg',\n BAD_SCOPE: 'bad-scope',\n BAD_VAPID_KEY: 'bad-vapid-key',\n BAD_SUBSCRIPTION: 'bad-subscription',\n BAD_TOKEN: 'bad-token',\n BAD_PUSH_SET: 'bad-push-set',\n FAILED_DELETE_VAPID_KEY: 'failed-delete-vapid-key',\n INVALID_PUBLIC_VAPID_KEY: 'invalid-public-vapid-key',\n USE_PUBLIC_KEY_BEFORE_GET_TOKEN: 'use-public-key-before-get-token',\n PUBLIC_KEY_DECRYPTION_FAILED: 'public-vapid-key-decryption-failed'\n};\n\nexport const ERROR_MAP = {\n [ERROR_CODES.AVAILABLE_IN_WINDOW]:\n 'This method is available in a Window context.',\n [ERROR_CODES.AVAILABLE_IN_SW]:\n 'This method is available in a service worker ' + 'context.',\n [ERROR_CODES.SHOULD_BE_INHERITED]:\n 'This method should be overriden by ' + 'extended classes.',\n [ERROR_CODES.BAD_SENDER_ID]:\n \"Please ensure that 'messagingSenderId' is set \" +\n 'correctly in the options passed into firebase.initializeApp().',\n [ERROR_CODES.PERMISSION_DEFAULT]:\n 'The required permissions were not granted and ' + 'dismissed instead.',\n [ERROR_CODES.PERMISSION_BLOCKED]:\n 'The required permissions were not granted and ' + 'blocked instead.',\n [ERROR_CODES.UNSUPPORTED_BROWSER]:\n \"This browser doesn't support the API's \" +\n 'required to use the firebase SDK.',\n [ERROR_CODES.NOTIFICATIONS_BLOCKED]: 'Notifications have been blocked.',\n [ERROR_CODES.FAILED_DEFAULT_REGISTRATION]:\n 'We are unable to register the ' +\n 'default service worker. {$browserErrorMessage}',\n [ERROR_CODES.SW_REGISTRATION_EXPECTED]:\n 'A service worker registration was the ' + 'expected input.',\n [ERROR_CODES.GET_SUBSCRIPTION_FAILED]:\n 'There was an error when trying to get ' +\n 'any existing Push Subscriptions.',\n [ERROR_CODES.INVALID_SAVED_TOKEN]:\n 'Unable to access details of the saved token.',\n [ERROR_CODES.SW_REG_REDUNDANT]:\n 'The service worker being used for push was made ' + 'redundant.',\n [ERROR_CODES.TOKEN_SUBSCRIBE_FAILED]:\n 'A problem occured while subscribing the ' + 'user to FCM: {$message}',\n [ERROR_CODES.TOKEN_SUBSCRIBE_NO_TOKEN]:\n 'FCM returned no token when subscribing ' + 'the user to push.',\n [ERROR_CODES.TOKEN_SUBSCRIBE_NO_PUSH_SET]:\n 'FCM returned an invalid response ' + 'when getting an FCM token.',\n [ERROR_CODES.TOKEN_UNSUBSCRIBE_FAILED]:\n 'A problem occured while unsubscribing the ' + 'user from FCM: {$message}',\n [ERROR_CODES.TOKEN_UPDATE_FAILED]:\n 'A problem occured while updating the ' + 'user from FCM: {$message}',\n [ERROR_CODES.TOKEN_UPDATE_NO_TOKEN]:\n 'FCM returned no token when updating ' + 'the user to push.',\n [ERROR_CODES.USE_SW_BEFORE_GET_TOKEN]:\n 'The useServiceWorker() method may only be called once and must be ' +\n 'called before calling getToken() to ensure your service worker is used.',\n [ERROR_CODES.INVALID_DELETE_TOKEN]:\n 'You must pass a valid token into ' +\n 'deleteToken(), i.e. the token from getToken().',\n [ERROR_CODES.DELETE_TOKEN_NOT_FOUND]:\n 'The deletion attempt for token could not ' +\n 'be performed as the token was not found.',\n [ERROR_CODES.DELETE_SCOPE_NOT_FOUND]:\n 'The deletion attempt for service worker ' +\n 'scope could not be performed as the scope was not found.',\n [ERROR_CODES.BG_HANDLER_FUNCTION_EXPECTED]:\n 'The input to ' + 'setBackgroundMessageHandler() must be a function.',\n [ERROR_CODES.NO_WINDOW_CLIENT_TO_MSG]:\n 'An attempt was made to message a ' + 'non-existant window client.',\n [ERROR_CODES.UNABLE_TO_RESUBSCRIBE]:\n 'There was an error while re-subscribing ' +\n 'the FCM token for push messaging. Will have to resubscribe the ' +\n 'user on next visit. {$message}',\n [ERROR_CODES.NO_FCM_TOKEN_FOR_RESUBSCRIBE]:\n 'Could not find an FCM token ' +\n 'and as a result, unable to resubscribe. Will have to resubscribe the ' +\n 'user on next visit.',\n [ERROR_CODES.FAILED_TO_DELETE_TOKEN]:\n 'Unable to delete the currently saved token.',\n [ERROR_CODES.NO_SW_IN_REG]:\n 'Even though the service worker registration was ' +\n 'successful, there was a problem accessing the service worker itself.',\n [ERROR_CODES.INCORRECT_GCM_SENDER_ID]:\n \"Please change your web app manifest's \" +\n \"'gcm_sender_id' value to '103953800507' to use Firebase messaging.\",\n [ERROR_CODES.BAD_SCOPE]:\n 'The service worker scope must be a string with at ' +\n 'least one character.',\n [ERROR_CODES.BAD_VAPID_KEY]:\n 'The public VAPID key is not a Uint8Array with 65 bytes.',\n [ERROR_CODES.BAD_SUBSCRIPTION]:\n 'The subscription must be a valid ' + 'PushSubscription.',\n [ERROR_CODES.BAD_TOKEN]:\n 'The FCM Token used for storage / lookup was not ' +\n 'a valid token string.',\n [ERROR_CODES.BAD_PUSH_SET]:\n 'The FCM push set used for storage / lookup was not ' +\n 'not a valid push set string.',\n [ERROR_CODES.FAILED_DELETE_VAPID_KEY]: 'The VAPID key could not be deleted.',\n [ERROR_CODES.INVALID_PUBLIC_VAPID_KEY]:\n 'The public VAPID key must be a string.',\n [ERROR_CODES.PUBLIC_KEY_DECRYPTION_FAILED]:\n 'The public VAPID key did not equal ' + '65 bytes when decrypted.'\n};\n\nexport const errorFactory: ErrorFactory = new ErrorFactory(\n 'messaging',\n 'Messaging',\n ERROR_MAP\n);\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MessagePayload } from '../interfaces/message-payload';\n\nexport enum MessageParameter {\n TYPE_OF_MSG = 'firebase-messaging-msg-type',\n DATA = 'firebase-messaging-msg-data'\n}\n\nexport enum MessageType {\n PUSH_MSG_RECEIVED = 'push-msg-received',\n NOTIFICATION_CLICKED = 'notification-clicked'\n}\n\nexport interface InternalMessage {\n [MessageParameter.TYPE_OF_MSG]: MessageType;\n [MessageParameter.DATA]: MessagePayload;\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const DEFAULT_PUBLIC_VAPID_KEY = new Uint8Array([\n 0x04,\n 0x33,\n 0x94,\n 0xf7,\n 0xdf,\n 0xa1,\n 0xeb,\n 0xb1,\n 0xdc,\n 0x03,\n 0xa2,\n 0x5e,\n 0x15,\n 0x71,\n 0xdb,\n 0x48,\n 0xd3,\n 0x2e,\n 0xed,\n 0xed,\n 0xb2,\n 0x34,\n 0xdb,\n 0xb7,\n 0x47,\n 0x3a,\n 0x0c,\n 0x8f,\n 0xc4,\n 0xcc,\n 0xe1,\n 0x6f,\n 0x3c,\n 0x8c,\n 0x84,\n 0xdf,\n 0xab,\n 0xb6,\n 0x66,\n 0x3e,\n 0xf2,\n 0x0c,\n 0xd4,\n 0x8b,\n 0xfe,\n 0xe3,\n 0xf9,\n 0x76,\n 0x2f,\n 0x14,\n 0x1c,\n 0x63,\n 0x08,\n 0x6a,\n 0x6f,\n 0x2d,\n 0xb1,\n 0x1a,\n 0x95,\n 0xb0,\n 0xce,\n 0x37,\n 0xc0,\n 0x9c,\n 0x6e\n]);\n\nexport const SUBSCRIPTION_DETAILS = {\n userVisibleOnly: true,\n applicationServerKey: DEFAULT_PUBLIC_VAPID_KEY\n};\n\nexport const ENDPOINT = 'https://fcm.googleapis.com';\n","/**\n * @license\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport function isArrayBufferEqual(\n a: ArrayBufferLike | undefined | null,\n b: ArrayBufferLike | undefined | null\n): boolean {\n if (a == null || b == null) {\n return false;\n }\n\n if (a === b) {\n return true;\n }\n\n if (a.byteLength !== b.byteLength) {\n return false;\n }\n\n const viewA = new DataView(a);\n const viewB = new DataView(b);\n\n for (let i = 0; i < a.byteLength; i++) {\n if (viewA.getUint8(i) !== viewB.getUint8(i)) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nfunction toBase64(arrayBuffer: ArrayBuffer | Uint8Array): string {\n const uint8Version = new Uint8Array(arrayBuffer);\n return btoa(String.fromCharCode(...uint8Version));\n}\n\nexport function arrayBufferToBase64(\n arrayBuffer: ArrayBuffer | Uint8Array\n): string {\n const base64String = toBase64(arrayBuffer);\n return base64String\n .replace(/=/g, '')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_');\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { arrayBufferToBase64 } from '../helpers/array-buffer-to-base64';\nimport { isArrayBufferEqual } from '../helpers/is-array-buffer-equal';\nimport { ERROR_CODES, errorFactory } from './errors';\nimport { DEFAULT_PUBLIC_VAPID_KEY, ENDPOINT } from './fcm-details';\n\nexport interface IidDetails {\n token: string;\n pushSet: string;\n}\n\ninterface ApiResponse extends Partial {\n error?: { message: string };\n}\n\nexport class IidModel {\n async getToken(\n senderId: string,\n subscription: PushSubscription,\n publicVapidKey: Uint8Array\n ): Promise {\n const p256dh = arrayBufferToBase64(subscription.getKey('p256dh')!);\n const auth = arrayBufferToBase64(subscription.getKey('auth')!);\n\n let fcmSubscribeBody =\n `authorized_entity=${senderId}&` +\n `endpoint=${subscription.endpoint}&` +\n `encryption_key=${p256dh}&` +\n `encryption_auth=${auth}`;\n\n if (\n !isArrayBufferEqual(\n publicVapidKey.buffer,\n DEFAULT_PUBLIC_VAPID_KEY.buffer\n )\n ) {\n const applicationPubKey = arrayBufferToBase64(publicVapidKey);\n fcmSubscribeBody += `&application_pub_key=${applicationPubKey}`;\n }\n\n const headers = new Headers();\n headers.append('Content-Type', 'application/x-www-form-urlencoded');\n\n const subscribeOptions = {\n method: 'POST',\n headers: headers,\n body: fcmSubscribeBody\n };\n\n let responseData: ApiResponse;\n try {\n const response = await fetch(\n ENDPOINT + '/fcm/connect/subscribe',\n subscribeOptions\n );\n\n responseData = await response.json();\n } catch (err) {\n throw errorFactory.create(ERROR_CODES.TOKEN_SUBSCRIBE_FAILED);\n }\n\n if (responseData.error) {\n const message = responseData.error.message;\n throw errorFactory.create(ERROR_CODES.TOKEN_SUBSCRIBE_FAILED, {\n message: message\n });\n }\n\n if (!responseData.token) {\n throw errorFactory.create(ERROR_CODES.TOKEN_SUBSCRIBE_NO_TOKEN);\n }\n\n if (!responseData.pushSet) {\n throw errorFactory.create(ERROR_CODES.TOKEN_SUBSCRIBE_NO_PUSH_SET);\n }\n\n return {\n token: responseData.token,\n pushSet: responseData.pushSet\n };\n }\n\n /**\n * Update the underlying token details for fcmToken.\n */\n async updateToken(\n senderId: string,\n fcmToken: string,\n fcmPushSet: string,\n subscription: PushSubscription,\n publicVapidKey: Uint8Array\n ): Promise {\n const p256dh = arrayBufferToBase64(subscription.getKey('p256dh')!);\n const auth = arrayBufferToBase64(subscription.getKey('auth')!);\n\n let fcmUpdateBody =\n `push_set=${fcmPushSet}&` +\n `token=${fcmToken}&` +\n `authorized_entity=${senderId}&` +\n `endpoint=${subscription.endpoint}&` +\n `encryption_key=${p256dh}&` +\n `encryption_auth=${auth}`;\n\n if (\n !isArrayBufferEqual(\n publicVapidKey.buffer,\n DEFAULT_PUBLIC_VAPID_KEY.buffer\n )\n ) {\n const applicationPubKey = arrayBufferToBase64(publicVapidKey);\n fcmUpdateBody += `&application_pub_key=${applicationPubKey}`;\n }\n\n const headers = new Headers();\n headers.append('Content-Type', 'application/x-www-form-urlencoded');\n\n const updateOptions = {\n method: 'POST',\n headers: headers,\n body: fcmUpdateBody\n };\n\n let responseData: ApiResponse;\n try {\n const response = await fetch(\n ENDPOINT + '/fcm/connect/subscribe',\n updateOptions\n );\n responseData = await response.json();\n } catch (err) {\n throw errorFactory.create(ERROR_CODES.TOKEN_UPDATE_FAILED);\n }\n\n if (responseData.error) {\n const message = responseData.error.message;\n throw errorFactory.create(ERROR_CODES.TOKEN_UPDATE_FAILED, {\n message: message\n });\n }\n\n if (!responseData.token) {\n throw errorFactory.create(ERROR_CODES.TOKEN_UPDATE_NO_TOKEN);\n }\n\n return responseData.token;\n }\n\n /**\n * Given a fcmToken, pushSet and messagingSenderId, delete an FCM token.\n */\n async deleteToken(\n senderId: string,\n fcmToken: string,\n fcmPushSet: string\n ): Promise {\n const fcmUnsubscribeBody =\n `authorized_entity=${senderId}&` +\n `token=${fcmToken}&` +\n `pushSet=${fcmPushSet}`;\n\n const headers = new Headers();\n headers.append('Content-Type', 'application/x-www-form-urlencoded');\n\n const unsubscribeOptions = {\n method: 'POST',\n headers: headers,\n body: fcmUnsubscribeBody\n };\n\n try {\n const response = await fetch(\n ENDPOINT + '/fcm/connect/unsubscribe',\n unsubscribeOptions\n );\n const responseData: ApiResponse = await response.json();\n if (responseData.error) {\n const message = responseData.error.message;\n throw errorFactory.create(ERROR_CODES.TOKEN_UNSUBSCRIBE_FAILED, {\n message: message\n });\n }\n } catch (err) {\n throw errorFactory.create(ERROR_CODES.TOKEN_UNSUBSCRIBE_FAILED);\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport function base64ToArrayBuffer(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - base64String.length % 4) % 4);\n const base64 = (base64String + padding)\n .replace(/\\-/g, '+')\n .replace(/_/g, '/');\n\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * There seems to have been a bug in the messaging SDK versions <= 4.9.x\n * where the IndexedDB model was using a database name of 'undefined'.\n *\n * In 4.10.x we changed the model implementation, but kept the database\n * name as it should have been. This however introduced an issue where\n * two tokens were pointing to the same underlying PushSubscription.\n *\n * This code will look for the undefined database and delete any of the\n * underlying tokens.\n */\n\nimport { IidModel } from '../models/iid-model';\n\nconst OLD_DB_NAME = 'undefined';\nconst OLD_OBJECT_STORE_NAME = 'fcm_token_object_Store';\n\nfunction handleDb(db: IDBDatabase): void {\n if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) {\n // We found a database with the name 'undefined', but our expected object\n // store isn't defined.\n return;\n }\n\n const transaction = db.transaction(OLD_OBJECT_STORE_NAME);\n const objectStore = transaction.objectStore(OLD_OBJECT_STORE_NAME);\n\n const iidModel = new IidModel();\n\n const openCursorRequest: IDBRequest = objectStore.openCursor();\n openCursorRequest.onerror = event => {\n // NOOP - Nothing we can do.\n console.warn('Unable to cleanup old IDB.', event);\n };\n\n openCursorRequest.onsuccess = () => {\n const cursor = openCursorRequest.result;\n if (cursor) {\n // cursor.value contains the current record being iterated through\n // this is where you'd do something with the result\n const tokenDetails = cursor.value;\n\n iidModel.deleteToken(\n tokenDetails.fcmSenderId,\n tokenDetails.fcmToken,\n tokenDetails.fcmPushSet\n );\n\n cursor.continue();\n } else {\n db.close();\n indexedDB.deleteDatabase(OLD_DB_NAME);\n }\n };\n}\n\nexport function cleanV1(): void {\n const request: IDBOpenDBRequest = indexedDB.open(OLD_DB_NAME);\n request.onerror = event => {\n // NOOP - Nothing we can do.\n };\n request.onsuccess = event => {\n const db = request.result;\n handleDb(db);\n };\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport abstract class DbInterface {\n private dbPromise: Promise | null = null;\n\n protected abstract readonly dbName: string;\n protected abstract readonly dbVersion: number;\n protected abstract readonly objectStoreName: string;\n\n /**\n * Database initialization.\n *\n * This function should create and update object stores.\n */\n protected abstract onDbUpgrade(\n request: IDBOpenDBRequest,\n event: IDBVersionChangeEvent\n ): void;\n\n /** Gets record(s) from the objectStore that match the given key. */\n get(key: IDBValidKey): Promise {\n return this.createTransaction(objectStore => objectStore.get(key));\n }\n\n /** Gets record(s) from the objectStore that match the given index. */\n getIndex(index: string, key: IDBValidKey): Promise {\n function runRequest(objectStore: IDBObjectStore): IDBRequest {\n const idbIndex = objectStore.index(index);\n return idbIndex.get(key);\n }\n\n return this.createTransaction(runRequest);\n }\n\n /** Assigns or overwrites the record for the given value. */\n // tslint:disable-next-line:no-any IndexedDB values are of type \"any\"\n put(value: any): Promise {\n return this.createTransaction(\n objectStore => objectStore.put(value),\n 'readwrite'\n );\n }\n\n /** Deletes record(s) from the objectStore that match the given key. */\n delete(key: IDBValidKey | IDBKeyRange): Promise {\n return this.createTransaction(\n objectStore => objectStore.delete(key),\n 'readwrite'\n );\n }\n\n /**\n * Close the currently open database.\n */\n async closeDatabase(): Promise {\n if (this.dbPromise) {\n const db = await this.dbPromise;\n db.close();\n this.dbPromise = null;\n }\n }\n\n /**\n * Creates an IndexedDB Transaction and passes its objectStore to the\n * runRequest function, which runs the database request.\n *\n * @return Promise that resolves with the result of the runRequest function\n */\n private async createTransaction(\n runRequest: (objectStore: IDBObjectStore) => IDBRequest,\n mode: 'readonly' | 'readwrite' = 'readonly'\n ): Promise {\n const db = await this.getDb();\n const transaction = db.transaction(this.objectStoreName, mode);\n const request = transaction.objectStore(this.objectStoreName);\n const result = await promisify(runRequest(request));\n\n return new Promise((resolve, reject) => {\n transaction.oncomplete = () => {\n resolve(result);\n };\n transaction.onerror = () => {\n reject(transaction.error);\n };\n });\n }\n\n /** Gets the cached db connection or opens a new one. */\n private getDb(): Promise {\n if (!this.dbPromise) {\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(this.dbName, this.dbVersion);\n request.onsuccess = () => {\n resolve(request.result);\n };\n request.onerror = () => {\n this.dbPromise = null;\n reject(request.error);\n };\n request.onupgradeneeded = event => this.onDbUpgrade(request, event);\n });\n }\n\n return this.dbPromise;\n }\n}\n\n/** Promisifies an IDBRequest. Resolves with the IDBRequest's result. */\nfunction promisify(request: IDBRequest): Promise {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n resolve(request.result);\n };\n request.onerror = () => {\n reject(request.error);\n };\n });\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { base64ToArrayBuffer } from '../helpers/base64-to-array-buffer';\nimport { TokenDetails } from '../interfaces/token-details';\nimport { cleanV1 } from './clean-v1-undefined';\nimport { DbInterface } from './db-interface';\nimport { ERROR_CODES, errorFactory } from './errors';\n\nexport class TokenDetailsModel extends DbInterface {\n protected readonly dbName: string = 'fcm_token_details_db';\n protected readonly dbVersion: number = 3;\n protected readonly objectStoreName: string = 'fcm_token_object_Store';\n\n protected onDbUpgrade(\n request: IDBOpenDBRequest,\n event: IDBVersionChangeEvent\n ): void {\n const db: IDBDatabase = request.result;\n\n // Lack of 'break' statements is intentional.\n switch (event.oldVersion) {\n case 0: {\n // New IDB instance\n const objectStore = db.createObjectStore(this.objectStoreName, {\n keyPath: 'swScope'\n });\n\n // Make sure the sender ID can be searched\n objectStore.createIndex('fcmSenderId', 'fcmSenderId', {\n unique: false\n });\n\n objectStore.createIndex('fcmToken', 'fcmToken', { unique: true });\n }\n\n case 1: {\n // Prior to version 2, we were using either 'fcm_token_details_db'\n // or 'undefined' as the database name due to bug in the SDK\n // So remove the old tokens and databases.\n cleanV1();\n }\n\n case 2: {\n const objectStore = request.transaction!.objectStore(\n this.objectStoreName\n );\n const cursorRequest = objectStore.openCursor();\n cursorRequest.onsuccess = () => {\n const cursor: IDBCursorWithValue | null = cursorRequest.result;\n if (cursor) {\n const value = cursor.value;\n const newValue: Partial = { ...value };\n\n if (!value.createTime) {\n newValue.createTime = Date.now();\n }\n\n if (typeof value.vapidKey === 'string') {\n newValue.vapidKey = base64ToArrayBuffer(value.vapidKey);\n }\n\n if (typeof value.auth === 'string') {\n newValue.auth = base64ToArrayBuffer(value.auth).buffer;\n }\n\n if (typeof value.auth === 'string') {\n newValue.p256dh = base64ToArrayBuffer(value.p256dh).buffer;\n }\n\n cursor.update(newValue);\n cursor.continue();\n }\n };\n }\n }\n }\n\n /**\n * Given a token, this method will look up the details in indexedDB.\n */\n async getTokenDetailsFromToken(\n fcmToken: string\n ): Promise {\n if (!fcmToken) {\n throw errorFactory.create(ERROR_CODES.BAD_TOKEN);\n }\n\n validateInputs({ fcmToken });\n\n return this.getIndex('fcmToken', fcmToken);\n }\n\n /**\n * Given a service worker scope, this method will look up the details in\n * indexedDB.\n * @return The details associated with that token.\n */\n async getTokenDetailsFromSWScope(\n swScope: string\n ): Promise {\n if (!swScope) {\n throw errorFactory.create(ERROR_CODES.BAD_SCOPE);\n }\n\n validateInputs({ swScope });\n\n return this.get(swScope);\n }\n\n /**\n * Save the details for the fcm token for re-use at a later date.\n * @param input A plain js object containing args to save.\n */\n async saveTokenDetails(tokenDetails: TokenDetails): Promise {\n if (!tokenDetails.swScope) {\n throw errorFactory.create(ERROR_CODES.BAD_SCOPE);\n }\n\n if (!tokenDetails.vapidKey) {\n throw errorFactory.create(ERROR_CODES.BAD_VAPID_KEY);\n }\n\n if (!tokenDetails.endpoint || !tokenDetails.auth || !tokenDetails.p256dh) {\n throw errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);\n }\n\n if (!tokenDetails.fcmSenderId) {\n throw errorFactory.create(ERROR_CODES.BAD_SENDER_ID);\n }\n\n if (!tokenDetails.fcmToken) {\n throw errorFactory.create(ERROR_CODES.BAD_TOKEN);\n }\n\n if (!tokenDetails.fcmPushSet) {\n throw errorFactory.create(ERROR_CODES.BAD_PUSH_SET);\n }\n\n validateInputs(tokenDetails);\n\n return this.put(tokenDetails);\n }\n\n /**\n * This method deletes details of the current FCM token.\n * It's returning a promise in case we need to move to an async\n * method for deleting at a later date.\n *\n * @return Resolves once the FCM token details have been deleted and returns\n * the deleted details.\n */\n async deleteToken(token: string): Promise {\n if (typeof token !== 'string' || token.length === 0) {\n return Promise.reject(\n errorFactory.create(ERROR_CODES.INVALID_DELETE_TOKEN)\n );\n }\n\n const details = await this.getTokenDetailsFromToken(token);\n if (!details) {\n throw errorFactory.create(ERROR_CODES.DELETE_TOKEN_NOT_FOUND);\n }\n\n await this.delete(details.swScope);\n return details;\n }\n}\n\n/**\n * This method takes an object and will check for known arguments and\n * validate the input.\n * @return Promise that resolves if input is valid, rejects otherwise.\n */\nfunction validateInputs(input: Partial): void {\n if (input.fcmToken) {\n if (typeof input.fcmToken !== 'string' || input.fcmToken.length === 0) {\n throw errorFactory.create(ERROR_CODES.BAD_TOKEN);\n }\n }\n\n if (input.swScope) {\n if (typeof input.swScope !== 'string' || input.swScope.length === 0) {\n throw errorFactory.create(ERROR_CODES.BAD_SCOPE);\n }\n }\n\n if (input.vapidKey) {\n if (\n !(input.vapidKey instanceof Uint8Array) ||\n input.vapidKey.length !== 65\n ) {\n throw errorFactory.create(ERROR_CODES.BAD_VAPID_KEY);\n }\n }\n\n if (input.endpoint) {\n if (typeof input.endpoint !== 'string' || input.endpoint.length === 0) {\n throw errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);\n }\n }\n\n if (input.auth) {\n if (!(input.auth instanceof ArrayBuffer)) {\n throw errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);\n }\n }\n\n if (input.p256dh) {\n if (!(input.p256dh instanceof ArrayBuffer)) {\n throw errorFactory.create(ERROR_CODES.BAD_SUBSCRIPTION);\n }\n }\n\n if (input.fcmSenderId) {\n if (\n typeof input.fcmSenderId !== 'string' ||\n input.fcmSenderId.length === 0\n ) {\n throw errorFactory.create(ERROR_CODES.BAD_SENDER_ID);\n }\n }\n\n if (input.fcmPushSet) {\n if (typeof input.fcmPushSet !== 'string' || input.fcmPushSet.length === 0) {\n throw errorFactory.create(ERROR_CODES.BAD_PUSH_SET);\n }\n }\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { VapidDetails } from '../interfaces/vapid-details';\nimport { DbInterface } from './db-interface';\nimport { ERROR_CODES, errorFactory } from './errors';\n\nconst UNCOMPRESSED_PUBLIC_KEY_SIZE = 65;\n\nexport class VapidDetailsModel extends DbInterface {\n protected readonly dbName: string = 'fcm_vapid_details_db';\n protected readonly dbVersion: number = 1;\n protected readonly objectStoreName: string = 'fcm_vapid_object_Store';\n\n protected onDbUpgrade(request: IDBOpenDBRequest): void {\n const db: IDBDatabase = request.result;\n db.createObjectStore(this.objectStoreName, { keyPath: 'swScope' });\n }\n\n /**\n * Given a service worker scope, this method will look up the vapid key\n * in indexedDB.\n */\n async getVapidFromSWScope(swScope: string): Promise {\n if (typeof swScope !== 'string' || swScope.length === 0) {\n throw errorFactory.create(ERROR_CODES.BAD_SCOPE);\n }\n\n const result = await this.get(swScope);\n return result ? result.vapidKey : undefined;\n }\n\n /**\n * Save a vapid key against a swScope for later date.\n */\n async saveVapidDetails(swScope: string, vapidKey: Uint8Array): Promise {\n if (typeof swScope !== 'string' || swScope.length === 0) {\n throw errorFactory.create(ERROR_CODES.BAD_SCOPE);\n }\n\n if (vapidKey === null || vapidKey.length !== UNCOMPRESSED_PUBLIC_KEY_SIZE) {\n throw errorFactory.create(ERROR_CODES.BAD_VAPID_KEY);\n }\n\n const details: VapidDetails = {\n swScope,\n vapidKey\n };\n\n return this.put(details);\n }\n\n /**\n * This method deletes details of the current FCM VAPID key for a SW scope.\n * Resolves once the scope/vapid details have been deleted and returns the\n * deleted vapid key.\n */\n async deleteVapidDetails(swScope: string): Promise {\n const vapidKey = await this.getVapidFromSWScope(swScope);\n if (!vapidKey) {\n throw errorFactory.create(ERROR_CODES.DELETE_SCOPE_NOT_FOUND);\n }\n\n await this.delete(swScope);\n return vapidKey;\n }\n}\n","/**\n * @license\n * Copyright 2017 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { FirebaseApp } from '@firebase/app-types';\nimport { FirebaseServiceInternals } from '@firebase/app-types/private';\nimport { FirebaseMessaging } from '@firebase/messaging-types';\nimport {\n CompleteFn,\n ErrorFn,\n NextFn,\n Observer,\n Unsubscribe\n} from '@firebase/util';\n\nimport { isArrayBufferEqual } from '../helpers/is-array-buffer-equal';\nimport { MessagePayload } from '../interfaces/message-payload';\nimport { TokenDetails } from '../interfaces/token-details';\nimport { ERROR_CODES, errorFactory } from '../models/errors';\nimport { IidModel } from '../models/iid-model';\nimport { TokenDetailsModel } from '../models/token-details-model';\nimport { VapidDetailsModel } from '../models/vapid-details-model';\n\n// tslint:disable-next-line no-any User can return any type of promise.\nexport type BgMessageHandler = (payload: MessagePayload) => Promise | void;\n\nconst SENDER_ID_OPTION_NAME = 'messagingSenderId';\n// Database cache should be invalidated once a week.\nexport const TOKEN_EXPIRATION_MILLIS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\nexport abstract class BaseController implements FirebaseMessaging {\n app: FirebaseApp;\n INTERNAL: FirebaseServiceInternals;\n private readonly messagingSenderId: string;\n private readonly tokenDetailsModel: TokenDetailsModel;\n private readonly vapidDetailsModel: VapidDetailsModel;\n private readonly iidModel: IidModel;\n\n /**\n * An interface of the Messaging Service API\n */\n constructor(app: FirebaseApp) {\n if (\n !app.options[SENDER_ID_OPTION_NAME] ||\n typeof app.options[SENDER_ID_OPTION_NAME] !== 'string'\n ) {\n throw errorFactory.create(ERROR_CODES.BAD_SENDER_ID);\n }\n\n this.messagingSenderId = app.options[SENDER_ID_OPTION_NAME]!;\n\n this.tokenDetailsModel = new TokenDetailsModel();\n this.vapidDetailsModel = new VapidDetailsModel();\n this.iidModel = new IidModel();\n\n this.app = app;\n this.INTERNAL = {\n delete: () => this.delete()\n };\n }\n\n /**\n * @export\n */\n async getToken(): Promise {\n // Check with permissions\n const currentPermission = this.getNotificationPermission_();\n if (currentPermission === 'denied') {\n throw errorFactory.create(ERROR_CODES.NOTIFICATIONS_BLOCKED);\n } else if (currentPermission !== 'granted') {\n // We must wait for permission to be granted\n return null;\n }\n\n const swReg = await this.getSWRegistration_();\n const publicVapidKey = await this.getPublicVapidKey_();\n // If a PushSubscription exists it's returned, otherwise a new subscription\n // is generated and returned.\n const pushSubscription = await this.getPushSubscription(\n swReg,\n publicVapidKey\n );\n const tokenDetails = await this.tokenDetailsModel.getTokenDetailsFromSWScope(\n swReg.scope\n );\n\n if (tokenDetails) {\n return this.manageExistingToken(\n swReg,\n pushSubscription,\n publicVapidKey,\n tokenDetails\n );\n }\n return this.getNewToken(swReg, pushSubscription, publicVapidKey);\n }\n\n /**\n * manageExistingToken is triggered if there's an existing FCM token in the\n * database and it can take 3 different actions:\n * 1) Retrieve the existing FCM token from the database.\n * 2) If VAPID details have changed: Delete the existing token and create a\n * new one with the new VAPID key.\n * 3) If the database cache is invalidated: Send a request to FCM to update\n * the token, and to check if the token is still valid on FCM-side.\n */\n private async manageExistingToken(\n swReg: ServiceWorkerRegistration,\n pushSubscription: PushSubscription,\n publicVapidKey: Uint8Array,\n tokenDetails: TokenDetails\n ): Promise {\n const isTokenValid = isTokenStillValid(\n pushSubscription,\n publicVapidKey,\n tokenDetails\n );\n if (isTokenValid) {\n const now = Date.now();\n if (now < tokenDetails.createTime + TOKEN_EXPIRATION_MILLIS) {\n return tokenDetails.fcmToken;\n } else {\n return this.updateToken(\n swReg,\n pushSubscription,\n publicVapidKey,\n tokenDetails\n );\n }\n }\n\n // If the token is no longer valid (for example if the VAPID details\n // have changed), delete the existing token from the FCM client and server\n // database. No need to unsubscribe from the Service Worker as we have a\n // good push subscription that we'd like to use in getNewToken.\n await this.deleteTokenFromDB(tokenDetails.fcmToken);\n return this.getNewToken(swReg, pushSubscription, publicVapidKey);\n }\n\n private async updateToken(\n swReg: ServiceWorkerRegistration,\n pushSubscription: PushSubscription,\n publicVapidKey: Uint8Array,\n tokenDetails: TokenDetails\n ): Promise {\n try {\n const updatedToken = await this.iidModel.updateToken(\n this.messagingSenderId,\n tokenDetails.fcmToken,\n tokenDetails.fcmPushSet,\n pushSubscription,\n publicVapidKey\n );\n\n const allDetails: TokenDetails = {\n swScope: swReg.scope,\n vapidKey: publicVapidKey,\n fcmSenderId: this.messagingSenderId,\n fcmToken: updatedToken,\n fcmPushSet: tokenDetails.fcmPushSet,\n createTime: Date.now(),\n endpoint: pushSubscription.endpoint,\n auth: pushSubscription.getKey('auth')!,\n p256dh: pushSubscription.getKey('p256dh')!\n };\n\n await this.tokenDetailsModel.saveTokenDetails(allDetails);\n await this.vapidDetailsModel.saveVapidDetails(\n swReg.scope,\n publicVapidKey\n );\n return updatedToken;\n } catch (e) {\n await this.deleteToken(tokenDetails.fcmToken);\n throw e;\n }\n }\n\n private async getNewToken(\n swReg: ServiceWorkerRegistration,\n pushSubscription: PushSubscription,\n publicVapidKey: Uint8Array\n ): Promise {\n const tokenDetails = await this.iidModel.getToken(\n this.messagingSenderId,\n pushSubscription,\n publicVapidKey\n );\n const allDetails: TokenDetails = {\n swScope: swReg.scope,\n vapidKey: publicVapidKey,\n fcmSenderId: this.messagingSenderId,\n fcmToken: tokenDetails.token,\n fcmPushSet: tokenDetails.pushSet,\n createTime: Date.now(),\n endpoint: pushSubscription.endpoint,\n auth: pushSubscription.getKey('auth')!,\n p256dh: pushSubscription.getKey('p256dh')!\n };\n await this.tokenDetailsModel.saveTokenDetails(allDetails);\n await this.vapidDetailsModel.saveVapidDetails(swReg.scope, publicVapidKey);\n return tokenDetails.token;\n }\n\n /**\n * This method deletes tokens that the token manager looks after,\n * unsubscribes the token from FCM and then unregisters the push\n * subscription if it exists. It returns a promise that indicates\n * whether or not the unsubscribe request was processed successfully.\n */\n async deleteToken(token: string): Promise {\n // Delete the token details from the database.\n await this.deleteTokenFromDB(token);\n // Unsubscribe from the SW.\n const registration = await this.getSWRegistration_();\n if (registration) {\n const pushSubscription = await registration.pushManager.getSubscription();\n if (pushSubscription) {\n return pushSubscription.unsubscribe();\n }\n }\n // If there's no SW, consider it a success.\n return true;\n }\n\n /**\n * This method will delete the token from the client database, and make a\n * call to FCM to remove it from the server DB. Does not temper with the\n * push subscription.\n */\n private async deleteTokenFromDB(token: string): Promise {\n const details = await this.tokenDetailsModel.deleteToken(token);\n await this.iidModel.deleteToken(\n details.fcmSenderId,\n details.fcmToken,\n details.fcmPushSet\n );\n }\n\n // Visible for testing\n // TODO: Make protected\n abstract getSWRegistration_(): Promise;\n\n // Visible for testing\n // TODO: Make protected\n abstract getPublicVapidKey_(): Promise;\n\n /**\n * Gets a PushSubscription for the current user.\n */\n getPushSubscription(\n swRegistration: ServiceWorkerRegistration,\n publicVapidKey: Uint8Array\n ): Promise {\n return swRegistration.pushManager.getSubscription().then(subscription => {\n if (subscription) {\n return subscription;\n }\n\n return swRegistration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: publicVapidKey\n });\n });\n }\n\n //\n // The following methods should only be available in the window.\n //\n\n requestPermission(): Promise {\n throw errorFactory.create(ERROR_CODES.AVAILABLE_IN_WINDOW);\n }\n\n useServiceWorker(registration: ServiceWorkerRegistration): void {\n throw errorFactory.create(ERROR_CODES.AVAILABLE_IN_WINDOW);\n }\n\n usePublicVapidKey(b64PublicKey: string): void {\n throw errorFactory.create(ERROR_CODES.AVAILABLE_IN_WINDOW);\n }\n\n onMessage(\n nextOrObserver: NextFn