'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var firebase = _interopDefault(require('@firebase/app')); var tslib_1 = require('tslib'); var util = require('@firebase/util'); var idb = require('idb'); var version = "0.2.6"; /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var PENDING_TIMEOUT_MS = 10000; var PACKAGE_VERSION = "w:" + version; var INTERNAL_AUTH_VERSION = 'FIS_v2'; var INSTALLATIONS_API_URL = 'https://firebaseinstallations.googleapis.com/v1'; var TOKEN_EXPIRATION_BUFFER = 60 * 60 * 1000; // One hour var SERVICE = 'installations'; var SERVICE_NAME = 'Installations'; /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var _a; var ERROR_DESCRIPTION_MAP = (_a = {}, _a["missing-app-config-values" /* MISSING_APP_CONFIG_VALUES */] = 'Missing App configuration values.', _a["create-installation-failed" /* CREATE_INSTALLATION_FAILED */] = 'Could not register Firebase Installation.', _a["generate-token-failed" /* GENERATE_TOKEN_FAILED */] = 'Could not generate Auth Token.', _a["not-registered" /* NOT_REGISTERED */] = 'Firebase Installation is not registered.', _a["installation-not-found" /* INSTALLATION_NOT_FOUND */] = 'Firebase Installation not found.', _a["request-failed" /* REQUEST_FAILED */] = '{$requestName} request failed with error "{$serverCode} {$serverStatus}: {$serverMessage}"', _a["app-offline" /* APP_OFFLINE */] = 'Could not process request. Application offline.', _a["delete-pending-registration" /* DELETE_PENDING_REGISTRATION */] = "Can't delete installation while there is a pending registration request.", _a); var ERROR_FACTORY = new util.ErrorFactory(SERVICE, SERVICE_NAME, ERROR_DESCRIPTION_MAP); /** Returns true if error is a FirebaseError that is based on an error from the server. */ function isServerError(error) { return (error instanceof util.FirebaseError && error.code.includes("request-failed" /* REQUEST_FAILED */)); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function extractAppConfig(app) { if (!app || !app.options) { throw ERROR_FACTORY.create("missing-app-config-values" /* MISSING_APP_CONFIG_VALUES */); } var appName = app.name; var _a = app.options, projectId = _a.projectId, apiKey = _a.apiKey, appId = _a.appId; if (!appName || !projectId || !apiKey || !appId) { throw ERROR_FACTORY.create("missing-app-config-values" /* MISSING_APP_CONFIG_VALUES */); } return { appName: appName, projectId: projectId, apiKey: apiKey, appId: appId }; } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function getInstallationsEndpoint(_a) { var projectId = _a.projectId; return INSTALLATIONS_API_URL + "/projects/" + projectId + "/installations"; } function extractAuthTokenInfoFromResponse(response) { return { token: response.token, requestStatus: 2 /* COMPLETED */, expiresIn: getExpiresInFromResponseExpiresIn(response.expiresIn), creationTime: Date.now() }; } function getErrorFromResponse(requestName, response) { return tslib_1.__awaiter(this, void 0, void 0, function () { var responseJson, errorData; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, response.json()]; case 1: responseJson = _a.sent(); errorData = responseJson.error; return [2 /*return*/, ERROR_FACTORY.create("request-failed" /* REQUEST_FAILED */, { requestName: requestName, serverCode: errorData.code, serverMessage: errorData.message, serverStatus: errorData.status })]; } }); }); } function getHeaders(_a) { var apiKey = _a.apiKey; return new Headers({ 'Content-Type': 'application/json', Accept: 'application/json', 'x-goog-api-key': apiKey }); } function getHeadersWithAuth(appConfig, _a) { var refreshToken = _a.refreshToken; var headers = getHeaders(appConfig); headers.append('Authorization', getAuthorizationHeader(refreshToken)); return headers; } /** * Calls the passed in fetch wrapper and returns the response. * If the returned response has a status of 5xx, re-runs the function once and * returns the response. */ function retryIfServerError(fn) { return tslib_1.__awaiter(this, void 0, void 0, function () { var result; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, fn()]; case 1: result = _a.sent(); if (result.status >= 500 && result.status < 600) { // Internal Server Error. Retry request. return [2 /*return*/, fn()]; } return [2 /*return*/, result]; } }); }); } function getExpiresInFromResponseExpiresIn(responseExpiresIn) { // This works because the server will never respond with fractions of a second. return Number(responseExpiresIn.replace('s', '000')); } function getAuthorizationHeader(refreshToken) { return INTERNAL_AUTH_VERSION + " " + refreshToken; } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function createInstallation(appConfig, _a) { var fid = _a.fid; return tslib_1.__awaiter(this, void 0, void 0, function () { var endpoint, headers, body, request, response, responseValue, registeredInstallationEntry; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: endpoint = getInstallationsEndpoint(appConfig); headers = getHeaders(appConfig); body = { fid: fid, authVersion: INTERNAL_AUTH_VERSION, appId: appConfig.appId, sdkVersion: PACKAGE_VERSION }; request = { method: 'POST', headers: headers, body: JSON.stringify(body) }; return [4 /*yield*/, retryIfServerError(function () { return fetch(endpoint, request); })]; case 1: response = _b.sent(); if (!response.ok) return [3 /*break*/, 3]; return [4 /*yield*/, response.json()]; case 2: responseValue = _b.sent(); registeredInstallationEntry = { fid: responseValue.fid || fid, registrationStatus: 2 /* COMPLETED */, refreshToken: responseValue.refreshToken, authToken: extractAuthTokenInfoFromResponse(responseValue.authToken) }; return [2 /*return*/, registeredInstallationEntry]; case 3: return [4 /*yield*/, getErrorFromResponse('Create Installation', response)]; case 4: throw _b.sent(); } }); }); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** Returns a promise that resolves after given time passes. */ function sleep(ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function bufferToBase64UrlSafe(array) { var b64 = btoa(String.fromCharCode.apply(String, tslib_1.__spread(array))); return b64.replace(/\+/g, '-').replace(/\//g, '_'); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var VALID_FID_PATTERN = /^[cdef][\w-]{21}$/; var INVALID_FID = ''; /** * Generates a new FID using random values from Web Crypto API. * Returns an empty string if FID generation fails for any reason. */ function generateFid() { try { // A valid FID has exactly 22 base64 characters, which is 132 bits, or 16.5 // bytes. our implementation generates a 17 byte array instead. var fidByteArray = new Uint8Array(17); var crypto_1 = self.crypto || self.msCrypto; crypto_1.getRandomValues(fidByteArray); // Replace the first 4 random bits with the constant FID header of 0b0111. fidByteArray[0] = 112 + (fidByteArray[0] % 16); var fid = encode(fidByteArray); return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID; } catch (_a) { // FID generation errored return INVALID_FID; } } /** Converts a FID Uint8Array to a base64 string representation. */ function encode(fidByteArray) { var b64String = bufferToBase64UrlSafe(fidByteArray); // Remove the 23rd character that was added because of the extra 4 bits at the // end of our 17 byte array, and the '=' padding. return b64String.substr(0, 22); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var DATABASE_NAME = 'firebase-installations-database'; var DATABASE_VERSION = 1; var OBJECT_STORE_NAME = 'firebase-installations-store'; var dbPromise = null; function getDbPromise() { if (!dbPromise) { dbPromise = idb.openDb(DATABASE_NAME, DATABASE_VERSION, function (upgradeDB) { // We don't use 'break' in this switch statement, the fall-through // behavior is what we want, because if there are multiple versions between // the old version and the current version, we want ALL the migrations // that correspond to those versions to run, not only the last one. // eslint-disable-next-line default-case switch (upgradeDB.oldVersion) { case 0: upgradeDB.createObjectStore(OBJECT_STORE_NAME); } }); } return dbPromise; } /** Assigns or overwrites the record for the given key with the given value. */ function set(appConfig, value) { return tslib_1.__awaiter(this, void 0, void 0, function () { var key, db, tx; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: key = getKey(appConfig); return [4 /*yield*/, getDbPromise()]; case 1: db = _a.sent(); tx = db.transaction(OBJECT_STORE_NAME, 'readwrite'); return [4 /*yield*/, tx.objectStore(OBJECT_STORE_NAME).put(value, key)]; case 2: _a.sent(); return [4 /*yield*/, tx.complete]; case 3: _a.sent(); return [2 /*return*/, value]; } }); }); } /** Removes record(s) from the objectStore that match the given key. */ function remove(appConfig) { return tslib_1.__awaiter(this, void 0, void 0, function () { var key, db, tx; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: key = getKey(appConfig); return [4 /*yield*/, getDbPromise()]; case 1: db = _a.sent(); tx = db.transaction(OBJECT_STORE_NAME, 'readwrite'); return [4 /*yield*/, tx.objectStore(OBJECT_STORE_NAME).delete(key)]; case 2: _a.sent(); return [4 /*yield*/, tx.complete]; case 3: _a.sent(); return [2 /*return*/]; } }); }); } /** * Atomically updates a record with the result of updateFn, which gets * called with the current value. If newValue is undefined, the record is * deleted instead. * @return Updated value */ function update(appConfig, updateFn) { return tslib_1.__awaiter(this, void 0, void 0, function () { var key, db, tx, store, oldValue, newValue; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: key = getKey(appConfig); return [4 /*yield*/, getDbPromise()]; case 1: db = _a.sent(); tx = db.transaction(OBJECT_STORE_NAME, 'readwrite'); store = tx.objectStore(OBJECT_STORE_NAME); return [4 /*yield*/, store.get(key)]; case 2: oldValue = _a.sent(); newValue = updateFn(oldValue); if (newValue === oldValue) { return [2 /*return*/, newValue]; } if (!(newValue === undefined)) return [3 /*break*/, 4]; return [4 /*yield*/, store.delete(key)]; case 3: _a.sent(); return [3 /*break*/, 6]; case 4: return [4 /*yield*/, store.put(newValue, key)]; case 5: _a.sent(); _a.label = 6; case 6: return [4 /*yield*/, tx.complete]; case 7: _a.sent(); return [2 /*return*/, newValue]; } }); }); } function getKey(appConfig) { return appConfig.appName + "!" + appConfig.appId; } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Updates and returns the InstallationEntry from the database. * Also triggers a registration request if it is necessary and possible. */ function getInstallationEntry(appConfig) { return tslib_1.__awaiter(this, void 0, void 0, function () { var registrationPromise, installationEntry, _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, update(appConfig, function (oldEntry) { var installationEntry = updateOrCreateInstallationEntry(oldEntry); var entryWithPromise = triggerRegistrationIfNecessary(appConfig, installationEntry); registrationPromise = entryWithPromise.registrationPromise; return entryWithPromise.installationEntry; })]; case 1: installationEntry = _b.sent(); if (!(installationEntry.fid === INVALID_FID)) return [3 /*break*/, 3]; _a = {}; return [4 /*yield*/, registrationPromise]; case 2: // FID generation failed. Waiting for the FID from the server. return [2 /*return*/, (_a.installationEntry = _b.sent(), _a)]; case 3: return [2 /*return*/, { installationEntry: installationEntry, registrationPromise: registrationPromise }]; } }); }); } function updateOrCreateInstallationEntry(oldEntry) { var entry = oldEntry || { fid: generateFid(), registrationStatus: 0 /* NOT_STARTED */ }; if (hasInstallationRequestTimedOut(entry)) { return { fid: entry.fid, registrationStatus: 0 /* NOT_STARTED */ }; } return entry; } /** * If the Firebase Installation is not registered yet, this will trigger the registration * and return an InProgressInstallationEntry. */ function triggerRegistrationIfNecessary(appConfig, installationEntry) { if (installationEntry.registrationStatus === 0 /* NOT_STARTED */) { if (!navigator.onLine) { // Registration required but app is offline. var registrationPromiseWithError = Promise.reject(ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */)); return { installationEntry: installationEntry, registrationPromise: registrationPromiseWithError }; } // Try registering. Change status to IN_PROGRESS. var inProgressEntry = { fid: installationEntry.fid, registrationStatus: 1 /* IN_PROGRESS */, registrationTime: Date.now() }; var registrationPromise = registerInstallation(appConfig, inProgressEntry); return { installationEntry: inProgressEntry, registrationPromise: registrationPromise }; } else if (installationEntry.registrationStatus === 1 /* IN_PROGRESS */) { return { installationEntry: installationEntry, registrationPromise: waitUntilFidRegistration(appConfig) }; } else { return { installationEntry: installationEntry }; } } /** This will be executed only once for each new Firebase Installation. */ function registerInstallation(appConfig, installationEntry) { return tslib_1.__awaiter(this, void 0, void 0, function () { var registeredInstallationEntry, e_1; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 7]); return [4 /*yield*/, createInstallation(appConfig, installationEntry)]; case 1: registeredInstallationEntry = _a.sent(); return [2 /*return*/, set(appConfig, registeredInstallationEntry)]; case 2: e_1 = _a.sent(); if (!(isServerError(e_1) && e_1.serverCode === 409)) return [3 /*break*/, 4]; // Server returned a "FID can not be used" error. // Generate a new ID next time. return [4 /*yield*/, remove(appConfig)]; case 3: // Server returned a "FID can not be used" error. // Generate a new ID next time. _a.sent(); return [3 /*break*/, 6]; case 4: // Registration failed. Set FID as not registered. return [4 /*yield*/, set(appConfig, { fid: installationEntry.fid, registrationStatus: 0 /* NOT_STARTED */ })]; case 5: // Registration failed. Set FID as not registered. _a.sent(); _a.label = 6; case 6: throw e_1; case 7: return [2 /*return*/]; } }); }); } /** Call if FID registration is pending. */ function waitUntilFidRegistration(appConfig) { return tslib_1.__awaiter(this, void 0, void 0, function () { var entry; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, updateInstallationRequest(appConfig)]; case 1: entry = _a.sent(); _a.label = 2; case 2: if (!(entry.registrationStatus === 1 /* IN_PROGRESS */)) return [3 /*break*/, 5]; // createInstallation request still in progress. return [4 /*yield*/, sleep(100)]; case 3: // createInstallation request still in progress. _a.sent(); return [4 /*yield*/, updateInstallationRequest(appConfig)]; case 4: entry = _a.sent(); return [3 /*break*/, 2]; case 5: if (entry.registrationStatus === 0 /* NOT_STARTED */) { throw ERROR_FACTORY.create("create-installation-failed" /* CREATE_INSTALLATION_FAILED */); } return [2 /*return*/, entry]; } }); }); } /** * Called only if there is a CreateInstallation request in progress. * * Updates the InstallationEntry in the DB based on the status of the * CreateInstallation request. * * Returns the updated InstallationEntry. */ function updateInstallationRequest(appConfig) { return update(appConfig, function (oldEntry) { if (!oldEntry) { throw ERROR_FACTORY.create("installation-not-found" /* INSTALLATION_NOT_FOUND */); } if (hasInstallationRequestTimedOut(oldEntry)) { return { fid: oldEntry.fid, registrationStatus: 0 /* NOT_STARTED */ }; } return oldEntry; }); } function hasInstallationRequestTimedOut(installationEntry) { return (installationEntry.registrationStatus === 1 /* IN_PROGRESS */ && installationEntry.registrationTime + PENDING_TIMEOUT_MS < Date.now()); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function generateAuthToken(appConfig, installationEntry) { return tslib_1.__awaiter(this, void 0, void 0, function () { var endpoint, headers, body, request, response, responseValue, completedAuthToken; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry); headers = getHeadersWithAuth(appConfig, installationEntry); body = { installation: { sdkVersion: PACKAGE_VERSION } }; request = { method: 'POST', headers: headers, body: JSON.stringify(body) }; return [4 /*yield*/, retryIfServerError(function () { return fetch(endpoint, request); })]; case 1: response = _a.sent(); if (!response.ok) return [3 /*break*/, 3]; return [4 /*yield*/, response.json()]; case 2: responseValue = _a.sent(); completedAuthToken = extractAuthTokenInfoFromResponse(responseValue); return [2 /*return*/, completedAuthToken]; case 3: return [4 /*yield*/, getErrorFromResponse('Generate Auth Token', response)]; case 4: throw _a.sent(); } }); }); } function getGenerateAuthTokenEndpoint(appConfig, _a) { var fid = _a.fid; return getInstallationsEndpoint(appConfig) + "/" + fid + "/authTokens:generate"; } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Returns a valid authentication token for the installation. Generates a new * token if one doesn't exist, is expired or about to expire. * * Should only be called if the Firebase Installation is registered. */ function refreshAuthToken(appConfig) { return tslib_1.__awaiter(this, void 0, void 0, function () { var tokenPromise, entry, authToken, _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, update(appConfig, function (oldEntry) { if (!isEntryRegistered(oldEntry)) { throw ERROR_FACTORY.create("not-registered" /* NOT_REGISTERED */); } var oldAuthToken = oldEntry.authToken; if (isAuthTokenValid(oldAuthToken)) { // There is a valid token in the DB. return oldEntry; } else if (oldAuthToken.requestStatus === 1 /* IN_PROGRESS */) { // There already is a token request in progress. tokenPromise = waitUntilAuthTokenRequest(appConfig); return oldEntry; } else { // No token or token expired. if (!navigator.onLine) { throw ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */); } var inProgressEntry = makeAuthTokenRequestInProgressEntry(oldEntry); tokenPromise = fetchAuthTokenFromServer(appConfig, inProgressEntry); return inProgressEntry; } })]; case 1: entry = _b.sent(); if (!tokenPromise) return [3 /*break*/, 3]; return [4 /*yield*/, tokenPromise]; case 2: _a = _b.sent(); return [3 /*break*/, 4]; case 3: _a = entry.authToken; _b.label = 4; case 4: authToken = _a; return [2 /*return*/, authToken.token]; } }); }); } /** * Call only if FID is registered and Auth Token request is in progress. */ function waitUntilAuthTokenRequest(appConfig) { return tslib_1.__awaiter(this, void 0, void 0, function () { var entry, authToken; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, updateAuthTokenRequest(appConfig)]; case 1: entry = _a.sent(); _a.label = 2; case 2: if (!(entry.authToken.requestStatus === 1 /* IN_PROGRESS */)) return [3 /*break*/, 5]; // generateAuthToken still in progress. return [4 /*yield*/, sleep(100)]; case 3: // generateAuthToken still in progress. _a.sent(); return [4 /*yield*/, updateAuthTokenRequest(appConfig)]; case 4: entry = _a.sent(); return [3 /*break*/, 2]; case 5: authToken = entry.authToken; if (authToken.requestStatus === 0 /* NOT_STARTED */) { throw ERROR_FACTORY.create("generate-token-failed" /* GENERATE_TOKEN_FAILED */); } else { return [2 /*return*/, authToken]; } return [2 /*return*/]; } }); }); } /** * Called only if there is a GenerateAuthToken request in progress. * * Updates the InstallationEntry in the DB based on the status of the * GenerateAuthToken request. * * Returns the updated InstallationEntry. */ function updateAuthTokenRequest(appConfig) { return update(appConfig, function (oldEntry) { if (!isEntryRegistered(oldEntry)) { throw ERROR_FACTORY.create("not-registered" /* NOT_REGISTERED */); } var oldAuthToken = oldEntry.authToken; if (hasAuthTokenRequestTimedOut(oldAuthToken)) { return tslib_1.__assign({}, oldEntry, { authToken: { requestStatus: 0 /* NOT_STARTED */ } }); } return oldEntry; }); } function fetchAuthTokenFromServer(appConfig, installationEntry) { return tslib_1.__awaiter(this, void 0, void 0, function () { var authToken, updatedInstallationEntry, e_1, updatedInstallationEntry; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 3, , 8]); return [4 /*yield*/, generateAuthToken(appConfig, installationEntry)]; case 1: authToken = _a.sent(); updatedInstallationEntry = tslib_1.__assign({}, installationEntry, { authToken: authToken }); return [4 /*yield*/, set(appConfig, updatedInstallationEntry)]; case 2: _a.sent(); return [2 /*return*/, authToken]; case 3: e_1 = _a.sent(); if (!(isServerError(e_1) && (e_1.serverCode === 401 || e_1.serverCode === 404))) return [3 /*break*/, 5]; // Server returned a "FID not found" or a "Invalid authentication" error. // Generate a new ID next time. return [4 /*yield*/, remove(appConfig)]; case 4: // Server returned a "FID not found" or a "Invalid authentication" error. // Generate a new ID next time. _a.sent(); return [3 /*break*/, 7]; case 5: updatedInstallationEntry = tslib_1.__assign({}, installationEntry, { authToken: { requestStatus: 0 /* NOT_STARTED */ } }); return [4 /*yield*/, set(appConfig, updatedInstallationEntry)]; case 6: _a.sent(); _a.label = 7; case 7: throw e_1; case 8: return [2 /*return*/]; } }); }); } function isEntryRegistered(installationEntry) { return (installationEntry !== undefined && installationEntry.registrationStatus === 2 /* COMPLETED */); } function isAuthTokenValid(authToken) { return (authToken.requestStatus === 2 /* COMPLETED */ && !isAuthTokenExpired(authToken)); } function isAuthTokenExpired(authToken) { var now = Date.now(); return (now < authToken.creationTime || authToken.creationTime + authToken.expiresIn < now + TOKEN_EXPIRATION_BUFFER); } /** Returns an updated InstallationEntry with an InProgressAuthToken. */ function makeAuthTokenRequestInProgressEntry(oldEntry) { var inProgressAuthToken = { requestStatus: 1 /* IN_PROGRESS */, requestTime: Date.now() }; return tslib_1.__assign({}, oldEntry, { authToken: inProgressAuthToken }); } function hasAuthTokenRequestTimedOut(authToken) { return (authToken.requestStatus === 1 /* IN_PROGRESS */ && authToken.requestTime + PENDING_TIMEOUT_MS < Date.now()); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function getId(app) { return tslib_1.__awaiter(this, void 0, void 0, function () { var appConfig, _a, installationEntry, registrationPromise; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: appConfig = extractAppConfig(app); return [4 /*yield*/, getInstallationEntry(appConfig)]; case 1: _a = _b.sent(), installationEntry = _a.installationEntry, registrationPromise = _a.registrationPromise; if (registrationPromise) { // Suppress registration errors as they are not a problem for getId. registrationPromise.catch(function () { }); } if (installationEntry.registrationStatus === 2 /* COMPLETED */) { // If the installation is already registered, update the authentication // token if needed. Suppress errors as they are not relevant to getId. refreshAuthToken(appConfig).catch(function () { }); } return [2 /*return*/, installationEntry.fid]; } }); }); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function getToken(app) { return tslib_1.__awaiter(this, void 0, void 0, function () { var appConfig; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: appConfig = extractAppConfig(app); return [4 /*yield*/, completeInstallationRegistration(appConfig)]; case 1: _a.sent(); // At this point we either have a Registered Installation in the DB, or we've // already thrown an error. return [2 /*return*/, refreshAuthToken(appConfig)]; } }); }); } function completeInstallationRegistration(appConfig) { return tslib_1.__awaiter(this, void 0, void 0, function () { var _a, installationEntry, registrationPromise; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, getInstallationEntry(appConfig)]; case 1: _a = _b.sent(), installationEntry = _a.installationEntry, registrationPromise = _a.registrationPromise; if (!registrationPromise) return [3 /*break*/, 3]; // A createInstallation request is in progress. Wait until it finishes. return [4 /*yield*/, registrationPromise]; case 2: // A createInstallation request is in progress. Wait until it finishes. _b.sent(); return [3 /*break*/, 4]; case 3: if (installationEntry.registrationStatus !== 2 /* COMPLETED */) { // Installation ID can't be registered. throw ERROR_FACTORY.create("create-installation-failed" /* CREATE_INSTALLATION_FAILED */); } _b.label = 4; case 4: return [2 /*return*/]; } }); }); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function deleteInstallation(appConfig, installationEntry) { return tslib_1.__awaiter(this, void 0, void 0, function () { var endpoint, headers, request, response; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: endpoint = getDeleteEndpoint(appConfig, installationEntry); headers = getHeadersWithAuth(appConfig, installationEntry); request = { method: 'DELETE', headers: headers }; return [4 /*yield*/, retryIfServerError(function () { return fetch(endpoint, request); })]; case 1: response = _a.sent(); if (!!response.ok) return [3 /*break*/, 3]; return [4 /*yield*/, getErrorFromResponse('Delete Installation', response)]; case 2: throw _a.sent(); case 3: return [2 /*return*/]; } }); }); } function getDeleteEndpoint(appConfig, _a) { var fid = _a.fid; return getInstallationsEndpoint(appConfig) + "/" + fid; } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function deleteInstallation$1(app) { return tslib_1.__awaiter(this, void 0, void 0, function () { var appConfig, entry; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: appConfig = extractAppConfig(app); return [4 /*yield*/, update(appConfig, function (oldEntry) { if (oldEntry && oldEntry.registrationStatus === 0 /* NOT_STARTED */) { // Delete the unregistered entry without sending a deleteInstallation request. return undefined; } return oldEntry; })]; case 1: entry = _a.sent(); if (!entry) return [3 /*break*/, 6]; if (!(entry.registrationStatus === 1 /* IN_PROGRESS */)) return [3 /*break*/, 2]; // Can't delete while trying to register. throw ERROR_FACTORY.create("delete-pending-registration" /* DELETE_PENDING_REGISTRATION */); case 2: if (!(entry.registrationStatus === 2 /* COMPLETED */)) return [3 /*break*/, 6]; if (!!navigator.onLine) return [3 /*break*/, 3]; throw ERROR_FACTORY.create("app-offline" /* APP_OFFLINE */); case 3: return [4 /*yield*/, deleteInstallation(appConfig, entry)]; case 4: _a.sent(); return [4 /*yield*/, remove(appConfig)]; case 5: _a.sent(); _a.label = 6; case 6: return [2 /*return*/]; } }); }); } /** * @license * Copyright 2019 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function registerInstallations(instance) { var installationsName = 'installations'; var factoryMethod = function (app) { // Throws if app isn't configured properly. extractAppConfig(app); return { app: app, getId: function () { return getId(app); }, getToken: function () { return getToken(app); }, delete: function () { return deleteInstallation$1(app); } }; }; instance.INTERNAL.registerService(installationsName, factoryMethod); } registerInstallations(firebase); exports.registerInstallations = registerInstallations; //# sourceMappingURL=index.cjs.js.map