From e09043c23549e48edcc1e35448b0bfeff9916db8 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 26 Sep 2022 16:21:47 -0700 Subject: [PATCH] Fixing up for merge --- common/api-review/util.api.md | 11 +-- config/.eslintrc.js | 2 +- packages/app/src/api.ts | 10 ++- packages/app/src/errors.ts | 2 + packages/auth/src/platform_browser/index.ts | 5 +- packages/database/src/api/Database.ts | 10 ++- packages/firestore/src/api/database.ts | 25 +++--- packages/firestore/src/lite-api/database.ts | 11 ++- packages/functions/src/api.ts | 3 +- packages/storage/src/api.ts | 10 ++- packages/util/src/defaults.ts | 95 +++++++++++++++------ 11 files changed, 121 insertions(+), 63 deletions(-) diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index eb09b5d2ce7..f81a0575142 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -4,11 +4,6 @@ ```ts -// Warning: (ae-missing-release-tag) "__PRIVATE_getDefaultEmulatorHost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const __PRIVATE_getDefaultEmulatorHost: (name: string) => string | undefined; - // Warning: (ae-missing-release-tag) "areCookiesEnabled" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -203,17 +198,19 @@ export type FirebaseSignInProvider = 'custom' | 'email' | 'password' | 'phone' | // Warning: (ae-missing-release-tag) "getDefaultAppConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const getDefaultAppConfig: () => Object | undefined; +export const getDefaultAppConfig: () => Record | undefined; // Warning: (ae-missing-release-tag) "getDefaultEmulatorHost" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const getDefaultEmulatorHost: (name: string) => string | undefined; +// Warning: (ae-forgotten-export) The symbol "ExperimentalKey" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "FirebaseDefaults" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "getExperimentalSetting" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const getExperimentalSetting: (name: string) => any; +export const getExperimentalSetting: (name: T) => FirebaseDefaults[`_${T}`]; // Warning: (ae-missing-release-tag) "getGlobal" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/config/.eslintrc.js b/config/.eslintrc.js index 04de0f9d10c..97f6df9be8f 100644 --- a/config/.eslintrc.js +++ b/config/.eslintrc.js @@ -129,7 +129,7 @@ module.exports = { { // Check dependencies from both local package.json // and from root package.json. - 'packageDir': [path.join(__dirname, '../'), './'], + 'packageDir': [context.getFilename(), path.join(__dirname, '../'), './'], 'devDependencies': [ '**/*.test.ts', '**/test/**/*.ts', diff --git a/packages/app/src/api.ts b/packages/app/src/api.ts index 443211e777b..b76a00e2d1b 100644 --- a/packages/app/src/api.ts +++ b/packages/app/src/api.ts @@ -115,7 +115,7 @@ export function initializeApp( * * @public */ - export function initializeApp(): FirebaseApp; +export function initializeApp(): FirebaseApp; export function initializeApp( _options?: FirebaseOptions, rawConfig = {} @@ -142,7 +142,9 @@ export function initializeApp( options ||= getDefaultAppConfig(); - if (!options) throw 'Need to provide options, when not being deployed to hosting via source.'; + if (!options) { + throw ERROR_FACTORY.create(AppError.NO_OPTIONS); + } const existingApp = _apps.get(name) as FirebaseAppImpl; if (existingApp) { @@ -200,7 +202,9 @@ export function initializeApp( */ export function getApp(name: string = DEFAULT_ENTRY_NAME): FirebaseApp { const app = _apps.get(name); - if (!app && name === DEFAULT_ENTRY_NAME) return initializeApp(); + if (!app && name === DEFAULT_ENTRY_NAME) { + return initializeApp(); + } if (!app) { throw ERROR_FACTORY.create(AppError.NO_APP, { appName: name }); } diff --git a/packages/app/src/errors.ts b/packages/app/src/errors.ts index ed307085b09..8d6f5983ccf 100644 --- a/packages/app/src/errors.ts +++ b/packages/app/src/errors.ts @@ -22,6 +22,7 @@ export const enum AppError { BAD_APP_NAME = 'bad-app-name', DUPLICATE_APP = 'duplicate-app', APP_DELETED = 'app-deleted', + NO_OPTIONS = 'no-options', INVALID_APP_ARGUMENT = 'invalid-app-argument', INVALID_LOG_ARGUMENT = 'invalid-log-argument', IDB_OPEN = 'idb-open', @@ -38,6 +39,7 @@ const ERRORS: ErrorMap = { [AppError.DUPLICATE_APP]: "Firebase App named '{$appName}' already exists with different options or config", [AppError.APP_DELETED]: "Firebase App named '{$appName}' already deleted", + [AppError.NO_OPTIONS]: "Need to provide options, when not being deployed to hosting via source.", [AppError.INVALID_APP_ARGUMENT]: 'firebase.{$appName}() takes either no argument or a ' + 'Firebase App instance.', diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index a861353b922..102e503ba96 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -27,14 +27,15 @@ import { browserPopupRedirectResolver } from './popup_redirect'; import { Auth, User } from '../model/public_types'; import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util'; -const ID_TOKEN_MAX_AGE = 5 * 60; +const DEFAULT_ID_TOKEN_MAX_AGE = 5 * 60; +const authIdTokenMaxAge = getExperimentalSetting('authIdTokenMaxAge') || DEFAULT_ID_TOKEN_MAX_AGE; let lastPostedIdToken: string|undefined|null = null; const mintCookieFactory = (url:string) => async (user: User|null) => { const idTokenResult = user && await user.getIdTokenResult(); const idTokenAge = idTokenResult && (new Date().getTime() - Date.parse(idTokenResult.issuedAtTime)) / 1_000; - if (idTokenAge && idTokenAge > ID_TOKEN_MAX_AGE) return; + if (idTokenAge && idTokenAge > authIdTokenMaxAge) return; // Specifically trip null => undefined when logged out, to delete any existing cookie const idToken = idTokenResult?.token; if (lastPostedIdToken === idToken) return; diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index 476da57a8a9..a0c2a816597 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -27,7 +27,8 @@ import { Provider } from '@firebase/component'; import { getModularInstance, createMockUserToken, - EmulatorMockTokenOptions + EmulatorMockTokenOptions, + getDefaultEmulatorHost } from '@firebase/util'; import { AppCheckTokenProvider } from '../core/AppCheckTokenProvider'; @@ -53,7 +54,10 @@ import { WebSocketConnection } from '../realtime/WebSocketConnection'; import { ReferenceImpl } from './Reference_impl'; -export { EmulatorMockTokenOptions, getDefaultEmulatorHost } from '@firebase/util'; +export { + EmulatorMockTokenOptions, + getDefaultEmulatorHost +} from '@firebase/util'; /** * This variable is also defined in the firebase Node.js Admin SDK. Before * modifying this definition, consult the definition in: @@ -321,7 +325,7 @@ export function getDatabase( }) as Database; const databaseEmulatorHost = getDefaultEmulatorHost('database'); if (databaseEmulatorHost) { - const [ host, port ] = databaseEmulatorHost.split(':'); + const [host, port] = databaseEmulatorHost.split(':'); connectDatabaseEmulator(db, host, parseInt(port, 10)); } return db; diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index a6218f857ba..17178579e68 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { _getProvider, _removeServiceInstance, @@ -43,7 +42,10 @@ import { setOnlineComponentProvider } from '../core/firestore_client'; import { makeDatabaseInfo } from '../lite-api/components'; -import { Firestore as LiteFirestore } from '../lite-api/database'; +import { + Firestore as LiteFirestore, + connectFirestoreEmulator +} from '../lite-api/database'; import { Query } from '../lite-api/reference'; import { indexedDbClearPersistence, @@ -66,7 +68,6 @@ export { connectFirestoreEmulator, EmulatorMockTokenOptions } from '../lite-api/database'; -import { connectFirestoreEmulator } from '../lite-api/database'; declare module '@firebase/component' { interface NameServiceMapping { @@ -153,9 +154,9 @@ export function initializeFirestore( throw new FirestoreError( Code.FAILED_PRECONDITION, 'initializeFirestore() has already been called with ' + - 'different options. To avoid this error, call initializeFirestore() with the ' + - 'same options as when it was originally called, or call getFirestore() to return the' + - ' already initialized instance.' + 'different options. To avoid this error, call initializeFirestore() with the ' + + 'same options as when it was originally called, or call getFirestore() to return the' + + ' already initialized instance.' ); } } @@ -188,7 +189,7 @@ export function getFirestore(app: FirebaseApp = getApp()): Firestore { if (!db._initialized) { const firestoreEmulatorHost = getDefaultEmulatorHost('firestore'); if (firestoreEmulatorHost) { - const [ host, port ] = firestoreEmulatorHost.split(':'); + const [host, port] = firestoreEmulatorHost.split(':'); connectFirestoreEmulator(db, host, parseInt(port, 10)); } } @@ -344,8 +345,8 @@ function setPersistenceProviders( } logWarn( 'Error enabling offline persistence. Falling back to ' + - 'persistence disabled: ' + - error + 'persistence disabled: ' + + error ); persistenceResult.reject(error); } @@ -418,7 +419,7 @@ export function clearIndexedDbPersistence(firestore: Firestore): Promise { throw new FirestoreError( Code.FAILED_PRECONDITION, 'Persistence can only be cleared before a Firestore instance is ' + - 'initialized or after it is terminated.' + 'initialized or after it is terminated.' ); } @@ -570,8 +571,8 @@ function verifyNotInitialized(firestore: Firestore): void { throw new FirestoreError( Code.FAILED_PRECONDITION, 'Firestore has already been started and persistence can no longer be ' + - 'enabled. You can only enable persistence before calling any other ' + - 'methods on a Firestore object.' + 'enabled. You can only enable persistence before calling any other ' + + 'methods on a Firestore object.' ); } } diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index cc0c2a8c03f..5df267da6f2 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -22,7 +22,11 @@ import { FirebaseApp, getApp } from '@firebase/app'; -import { createMockUserToken, EmulatorMockTokenOptions, getDefaultEmulatorHost } from '@firebase/util'; +import { + createMockUserToken, + EmulatorMockTokenOptions, + getDefaultEmulatorHost +} from '@firebase/util'; import { CredentialsProvider, @@ -44,6 +48,8 @@ import { FirestoreSettings } from './settings'; +export { EmulatorMockTokenOptions } from '@firebase/util'; + declare module '@firebase/component' { interface NameServiceMapping { 'firestore/lite': Firestore; @@ -215,14 +221,13 @@ export function getFirestore(app: FirebaseApp = getApp()): Firestore { if (!db._initialized) { const firestoreEmulatorHost = getDefaultEmulatorHost('firestore'); if (firestoreEmulatorHost) { - const [ host, port ] = firestoreEmulatorHost.split(':'); + const [host, port] = firestoreEmulatorHost.split(':'); connectFirestoreEmulator(db, host, parseInt(port, 10)); } } return db; } -export { EmulatorMockTokenOptions } from '@firebase/util'; /** * Modify this instance to communicate with the Cloud Firestore emulator. * diff --git a/packages/functions/src/api.ts b/packages/functions/src/api.ts index d64c6aa6c2e..f6b5066b9a8 100644 --- a/packages/functions/src/api.ts +++ b/packages/functions/src/api.ts @@ -53,7 +53,8 @@ export function getFunctions( }); const functionsEmulatorHost = getDefaultEmulatorHost('functions'); if (functionsEmulatorHost) { - const [ host, port ] = functionsEmulatorHost.split(':'); + const [host, port] = functionsEmulatorHost.split(':'); + // eslint-disable-next-line no-restricted-globals connectFunctionsEmulator(functionsInstance, host, parseInt(port, 10)); } return functionsInstance; diff --git a/packages/storage/src/api.ts b/packages/storage/src/api.ts index fce8cae9f2f..4b59c310543 100644 --- a/packages/storage/src/api.ts +++ b/packages/storage/src/api.ts @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { _getProvider, FirebaseApp, getApp } from '@firebase/app'; import { @@ -51,7 +50,11 @@ import { getBytesInternal } from './reference'; import { STORAGE_TYPE } from './constants'; -import { EmulatorMockTokenOptions, getModularInstance, getDefaultEmulatorHost } from '@firebase/util'; +import { + EmulatorMockTokenOptions, + getModularInstance, + getDefaultEmulatorHost +} from '@firebase/util'; import { StringFormat } from './implementation/string'; export { EmulatorMockTokenOptions } from '@firebase/util'; @@ -333,7 +336,8 @@ export function getStorage( }); const storageEmulatorHost = getDefaultEmulatorHost('storage'); if (storageEmulatorHost) { - const [ host, port ] = storageEmulatorHost.split(':'); + const [host, port] = storageEmulatorHost.split(':'); + // eslint-disable-next-line no-restricted-globals connectStorageEmulator(storageInstance, host, parseInt(port, 10)); } return storageInstance; diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index 2f6e1b88aaf..0eb4a6a7f9f 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -1,43 +1,82 @@ +/** + * @license + * Copyright 2022 Google LLC + * + * 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. + */ -import { base64Decode } from "./crypt"; -import { getGlobal } from "./environment"; +import { base64Decode } from './crypt'; +import { getGlobal } from './environment'; -type FirebaseDefaults = { - config?: Object, - emulatorHosts?: Record, - _authTokenSyncURL?: string, +type ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'; + +interface FirebaseDefaults { + config?: Record; + emulatorHosts?: Record; + _authTokenSyncURL?: string; + _authIdTokenMaxAge?: number; + [key: string]: unknown; } declare global { - var __FIREBASE_DEFAULTS__: FirebaseDefaults|undefined; + // Need `var` for this to work. + // eslint-disable-next-line no-var + var __FIREBASE_DEFAULTS__: FirebaseDefaults | undefined; } -const getDefaultsFromGlobal = (): FirebaseDefaults|undefined => getGlobal().__FIREBASE_DEFAULTS__; +const getDefaultsFromGlobal = (): FirebaseDefaults | undefined => + getGlobal().__FIREBASE_DEFAULTS__; -const getDefaultsFromEnvVariable = (): FirebaseDefaults|undefined => { - if (typeof process === 'undefined') return; - const fromEnv = process.env.__FIREBASE_DEFAULTS__; - return fromEnv && JSON.parse(fromEnv); +/** + * Attempt to read defaults from a JSON file whose path is in + * process.env.__FIREBASE_DEFAULTS_PATH__ + */ +const getDefaultsFromEnvVariable = (): FirebaseDefaults | undefined => { + if (typeof process === 'undefined') { + return; + } + const jsonPath = process.env.__FIREBASE_DEFAULTS_PATH__; + if (jsonPath && typeof require !== 'undefined') { + try { + const json = require(jsonPath); + return json; + } catch(e) { + `Unable to read defaults from file: ${jsonPath}.` + } + } }; -const getDefaultsFromCookie = (): FirebaseDefaults|undefined => { - if (typeof document === 'undefined') return; - const match = document.cookie.match(/__FIREBASE_DEFAULTS__=([^;]+)/); - const decoded = match && base64Decode(match[1]); - return decoded && JSON.parse(decoded); +const getDefaultsFromCookie = (): FirebaseDefaults | undefined => { + if (typeof document === 'undefined') { + return; + } + const match = document.cookie.match(/__FIREBASE_DEFAULTS__=([^;]+)/); + const decoded = match && base64Decode(match[1]); + return decoded && JSON.parse(decoded); }; -const getDefaults = () => getDefaultsFromGlobal() || - getDefaultsFromEnvVariable() || - getDefaultsFromCookie(); - -export const getDefaultEmulatorHost = (name:string) => getDefaults()?.emulatorHosts?.[name]; +const getDefaults = (): FirebaseDefaults | undefined => + getDefaultsFromGlobal() || + getDefaultsFromEnvVariable() || + getDefaultsFromCookie(); -// I don't know why I can't get firestore to reconigze this in the externs... -// adding the __PRIVATE_ prefix for now -export const __PRIVATE_getDefaultEmulatorHost = getDefaultEmulatorHost; +export const getDefaultEmulatorHost = (name: string): string | undefined => + getDefaults()?.emulatorHosts?.[name]; -export const getDefaultAppConfig = () => getDefaults()?.config; +export const getDefaultAppConfig = (): Record | undefined => + getDefaults()?.config; -// TODO fix the type -export const getExperimentalSetting = (name: string) => (getDefaults as any)()?.[`_${name}`]; +export const getExperimentalSetting = ( + name: T +): FirebaseDefaults[`_${T}`] => + getDefaults()?.[`_${name}`] as FirebaseDefaults[`_${T}`];