From 1ee90c73049f386a2626546214e357f0b19b853d Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 26 Sep 2022 17:00:11 -0700 Subject: [PATCH 01/15] Fresh branch for jamesdaniels_ssr_autoconfig --- config/.eslintrc.js | 2 +- packages/app/src/api.ts | 21 +++++- packages/app/src/errors.ts | 2 + packages/auth/src/platform_browser/index.ts | 44 ++++++++++- packages/auth/src/platform_node/index.ts | 12 ++- packages/database/src/api/Database.ts | 16 +++- packages/firestore/src/api/database.ts | 44 +++++++---- packages/firestore/src/lite-api/database.ts | 19 ++++- packages/functions/src/api.ts | 8 +- packages/storage/src/api.ts | 13 +++- packages/util/index.node.ts | 1 + packages/util/index.ts | 1 + packages/util/src/defaults.ts | 82 +++++++++++++++++++++ 13 files changed, 235 insertions(+), 30 deletions(-) create mode 100644 packages/util/src/defaults.ts 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 1a713b242c1..b76a00e2d1b 100644 --- a/packages/app/src/api.ts +++ b/packages/app/src/api.ts @@ -39,7 +39,7 @@ import { LogOptions, setUserLogHandler } from '@firebase/logger'; -import { deepEqual } from '@firebase/util'; +import { deepEqual, getDefaultAppConfig } from '@firebase/util'; export { FirebaseError } from '@firebase/util'; @@ -110,10 +110,18 @@ export function initializeApp( options: FirebaseOptions, config?: FirebaseAppSettings ): FirebaseApp; +/** + * Creates and initializes a FirebaseApp instance. + * + * @public + */ +export function initializeApp(): FirebaseApp; export function initializeApp( - options: FirebaseOptions, + _options?: FirebaseOptions, rawConfig = {} ): FirebaseApp { + let options = _options; + if (typeof rawConfig !== 'object') { const name = rawConfig; rawConfig = { name }; @@ -132,6 +140,12 @@ export function initializeApp( }); } + options ||= getDefaultAppConfig(); + + if (!options) { + throw ERROR_FACTORY.create(AppError.NO_OPTIONS); + } + const existingApp = _apps.get(name) as FirebaseAppImpl; if (existingApp) { // return the existing app if options and config deep equal the ones in the existing app. @@ -188,6 +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) { 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 3d206c3706d..102e503ba96 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -17,14 +17,36 @@ import { FirebaseApp, getApp, _getProvider } from '@firebase/app'; -import { initializeAuth } from '..'; +import { initializeAuth, beforeAuthStateChanged, onIdTokenChanged, connectAuthEmulator } from '..'; import { registerAuth } from '../core/auth/register'; import { ClientPlatform } from '../core/util/version'; import { browserLocalPersistence } from './persistence/local_storage'; import { browserSessionPersistence } from './persistence/session_storage'; import { indexedDBLocalPersistence } from './persistence/indexed_db'; import { browserPopupRedirectResolver } from './popup_redirect'; -import { Auth } from '../model/public_types'; +import { Auth, User } from '../model/public_types'; +import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util'; + +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 > authIdTokenMaxAge) return; + // Specifically trip null => undefined when logged out, to delete any existing cookie + const idToken = idTokenResult?.token; + if (lastPostedIdToken === idToken) return; + lastPostedIdToken = idToken; + await fetch(url, { + method: idToken ? 'POST' : 'DELETE', + headers: idToken ? { + 'Authorization': `Bearer ${idToken}`, + } : {} + }); +}; /** * Returns the Auth instance associated with the provided {@link @firebase/app#FirebaseApp}. @@ -41,7 +63,7 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { return provider.getImmediate(); } - return initializeAuth(app, { + const auth = initializeAuth(app, { popupRedirectResolver: browserPopupRedirectResolver, persistence: [ indexedDBLocalPersistence, @@ -49,6 +71,22 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { browserSessionPersistence ] }); + + const authTokenSyncUrl = getExperimentalSetting('authTokenSyncURL'); + if (authTokenSyncUrl) { + const mintCookie = mintCookieFactory(authTokenSyncUrl); + beforeAuthStateChanged(auth, mintCookie, () => { + mintCookie(auth.currentUser) + }); + onIdTokenChanged(auth, user => mintCookie(user)); + } + + const authEmulatorHost = getDefaultEmulatorHost('auth'); + if (authEmulatorHost) { + connectAuthEmulator(auth, `http://${authEmulatorHost}`); + } + + return auth; } registerAuth(ClientPlatform.BROWSER); diff --git a/packages/auth/src/platform_node/index.ts b/packages/auth/src/platform_node/index.ts index 14f5a531b93..570f1e0589f 100644 --- a/packages/auth/src/platform_node/index.ts +++ b/packages/auth/src/platform_node/index.ts @@ -21,13 +21,14 @@ import { _createError } from '../core/util/assert'; import { FirebaseApp, getApp, _getProvider } from '@firebase/app'; import { Auth } from '../model/public_types'; -import { initializeAuth, inMemoryPersistence } from '..'; +import { initializeAuth, inMemoryPersistence, connectAuthEmulator } from '..'; import { registerAuth } from '../core/auth/register'; import { ClientPlatform } from '../core/util/version'; import { AuthImpl } from '../core/auth/auth_impl'; import { FetchProvider } from '../core/util/fetch_provider'; import * as fetchImpl from 'node-fetch'; +import { getDefaultEmulatorHost } from '@firebase/util'; // Initialize the fetch polyfill, the types are slightly off so just cast and hope for the best FetchProvider.initialize( @@ -46,7 +47,14 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { return provider.getImmediate(); } - return initializeAuth(app); + const auth = initializeAuth(app); + + const authEmulatorHost = getDefaultEmulatorHost('auth'); + if (authEmulatorHost) { + connectAuthEmulator(auth, `http://${authEmulatorHost}`); + } + + return auth; } registerAuth(ClientPlatform.NODE); diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index 840e1be5aff..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 } 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: @@ -316,9 +320,15 @@ export function getDatabase( app: FirebaseApp = getApp(), url?: string ): Database { - return _getProvider(app, 'database').getImmediate({ + const db = _getProvider(app, 'database').getImmediate({ identifier: url }) as Database; + const databaseEmulatorHost = getDefaultEmulatorHost('database'); + if (databaseEmulatorHost) { + 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 6dbd4463cb3..6ffda1b044f 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -15,14 +15,13 @@ * limitations under the License. */ -// eslint-disable-next-line import/no-extraneous-dependencies import { _getProvider, _removeServiceInstance, FirebaseApp, getApp } from '@firebase/app'; -import { deepEqual } from '@firebase/util'; +import { deepEqual, getDefaultEmulatorHost } from '@firebase/util'; import { User } from '../auth/user'; import { @@ -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, @@ -162,9 +164,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.' ); } } @@ -215,7 +217,15 @@ export function getFirestore(app: FirebaseApp): Firestore; */ export function getFirestore(databaseId: string): Firestore; /** - * Returns the existing {@link Firestore} instance that is associated with the + * Returns the existing default {@link Firestore} instance that is associated with the + * default {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new + * instance with default settings. + * + * @returns The {@link Firestore} instance of the provided app. + */ +export function getFirestore(): Firestore; +/** + * Returns the existing default {@link Firestore} instance that is associated with the * provided {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new * instance with default settings. * @@ -236,9 +246,17 @@ export function getFirestore( typeof appOrDatabaseId === 'string' ? appOrDatabaseId : optionalDatabaseId || DEFAULT_DATABASE_NAME; - return _getProvider(app, 'firestore').getImmediate({ + const db = _getProvider(app, 'firestore').getImmediate({ identifier: databaseId }) as Firestore; + if (!db._initialized) { + const firestoreEmulatorHost = getDefaultEmulatorHost('firestore'); + if (firestoreEmulatorHost) { + const [host, port] = firestoreEmulatorHost.split(':'); + connectFirestoreEmulator(db, host, parseInt(port, 10)); + } + } + return db; } /** @@ -390,8 +408,8 @@ function setPersistenceProviders( } logWarn( 'Error enabling offline persistence. Falling back to ' + - 'persistence disabled: ' + - error + 'persistence disabled: ' + + error ); persistenceResult.reject(error); } @@ -464,7 +482,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.' ); } @@ -620,8 +638,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 c7ab4d5b437..dc6d509a6b5 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 } 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; @@ -260,12 +266,19 @@ export function getFirestore( typeof appOrDatabaseId === 'string' ? appOrDatabaseId : optionalDatabaseId || '(default)'; - return _getProvider(app, 'firestore/lite').getImmediate({ + const db = _getProvider(app, 'firestore/lite').getImmediate({ identifier: databaseId }) as Firestore; + if (!db._initialized) { + const firestoreEmulatorHost = getDefaultEmulatorHost('firestore'); + if (firestoreEmulatorHost) { + 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 52913263041..f6b5066b9a8 100644 --- a/packages/functions/src/api.ts +++ b/packages/functions/src/api.ts @@ -27,7 +27,7 @@ import { httpsCallable as _httpsCallable, httpsCallableFromURL as _httpsCallableFromURL } from './service'; -import { getModularInstance } from '@firebase/util'; +import { getModularInstance, getDefaultEmulatorHost } from '@firebase/util'; export * from './public-types'; @@ -51,6 +51,12 @@ export function getFunctions( const functionsInstance = functionsProvider.getImmediate({ identifier: regionOrCustomDomain }); + const functionsEmulatorHost = getDefaultEmulatorHost('functions'); + if (functionsEmulatorHost) { + 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 a489cacccb8..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 } from '@firebase/util'; +import { + EmulatorMockTokenOptions, + getModularInstance, + getDefaultEmulatorHost +} from '@firebase/util'; import { StringFormat } from './implementation/string'; export { EmulatorMockTokenOptions } from '@firebase/util'; @@ -331,6 +334,12 @@ export function getStorage( const storageInstance = storageProvider.getImmediate({ identifier: bucketUrl }); + const storageEmulatorHost = getDefaultEmulatorHost('storage'); + if (storageEmulatorHost) { + 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/index.node.ts b/packages/util/index.node.ts index d39d5253c1f..d63584284ac 100644 --- a/packages/util/index.node.ts +++ b/packages/util/index.node.ts @@ -24,6 +24,7 @@ export * from './src/assert'; export * from './src/crypt'; export * from './src/constants'; export * from './src/deepCopy'; +export * from './src/defaults'; export * from './src/deferred'; export * from './src/emulator'; export * from './src/environment'; diff --git a/packages/util/index.ts b/packages/util/index.ts index 3f38c307eb3..c529580b24e 100644 --- a/packages/util/index.ts +++ b/packages/util/index.ts @@ -19,6 +19,7 @@ export * from './src/assert'; export * from './src/crypt'; export * from './src/constants'; export * from './src/deepCopy'; +export * from './src/defaults'; export * from './src/deferred'; export * from './src/emulator'; export * from './src/environment'; diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts new file mode 100644 index 00000000000..0eb4a6a7f9f --- /dev/null +++ b/packages/util/src/defaults.ts @@ -0,0 +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'; + +type ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'; + +interface FirebaseDefaults { + config?: Record; + emulatorHosts?: Record; + _authTokenSyncURL?: string; + _authIdTokenMaxAge?: number; + [key: string]: unknown; +} + +declare global { + // Need `var` for this to work. + // eslint-disable-next-line no-var + var __FIREBASE_DEFAULTS__: FirebaseDefaults | undefined; +} + +const getDefaultsFromGlobal = (): FirebaseDefaults | undefined => + getGlobal().__FIREBASE_DEFAULTS__; + +/** + * 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 getDefaults = (): FirebaseDefaults | undefined => + getDefaultsFromGlobal() || + getDefaultsFromEnvVariable() || + getDefaultsFromCookie(); + +export const getDefaultEmulatorHost = (name: string): string | undefined => + getDefaults()?.emulatorHosts?.[name]; + +export const getDefaultAppConfig = (): Record | undefined => + getDefaults()?.config; + +export const getExperimentalSetting = ( + name: T +): FirebaseDefaults[`_${T}`] => + getDefaults()?.[`_${name}`] as FirebaseDefaults[`_${T}`]; From 233cc69b7aa356d79e319eeebd38149c672d0540 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 26 Sep 2022 17:07:22 -0700 Subject: [PATCH 02/15] Some missed files --- common/api-review/app.api.md | 3 +++ common/api-review/util.api.md | 17 +++++++++++++++++ packages/app-types/index.d.ts | 4 ++++ packages/firestore/externs.json | 1 + 4 files changed, 25 insertions(+) diff --git a/common/api-review/app.api.md b/common/api-review/app.api.md index 8d35bc4096f..52f134dac16 100644 --- a/common/api-review/app.api.md +++ b/common/api-review/app.api.md @@ -93,6 +93,9 @@ export function initializeApp(options: FirebaseOptions, name?: string): Firebase // @public export function initializeApp(options: FirebaseOptions, config?: FirebaseAppSettings): FirebaseApp; +// @public +export function initializeApp(): FirebaseApp; + // @public export function onLog(logCallback: LogCallback | null, options?: LogOptions): void; diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index 25657980b0e..f81a0575142 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -195,6 +195,23 @@ export class FirebaseError extends Error { // @public export type FirebaseSignInProvider = 'custom' | 'email' | 'password' | 'phone' | 'anonymous' | 'google.com' | 'facebook.com' | 'github.com' | 'twitter.com' | 'microsoft.com' | 'apple.com'; +// 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: () => 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: 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) // // @public diff --git a/packages/app-types/index.d.ts b/packages/app-types/index.d.ts index 72b8fa68f9d..088c64fad63 100644 --- a/packages/app-types/index.d.ts +++ b/packages/app-types/index.d.ts @@ -74,6 +74,10 @@ export interface FirebaseNamespace { * omitted) */ initializeApp(options: FirebaseOptions, name?: string): FirebaseApp; + /** + * Create (and initialize) a FirebaseApp. + */ + initializeApp(): FirebaseApp; app: { /** diff --git a/packages/firestore/externs.json b/packages/firestore/externs.json index a4c36074978..fcd6408548c 100644 --- a/packages/firestore/externs.json +++ b/packages/firestore/externs.json @@ -26,6 +26,7 @@ "packages/logger/dist/src/logger.d.ts", "packages/webchannel-wrapper/src/index.d.ts", "packages/util/dist/src/crypt.d.ts", + "packages/util/dist/src/defaults.d.ts", "packages/util/dist/src/emulator.d.ts", "packages/util/dist/src/environment.d.ts", "packages/util/dist/src/compat.d.ts", From 4ad621362e0b1043d417bd617cc6afd26fce06a3 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 26 Sep 2022 17:14:22 -0700 Subject: [PATCH 03/15] Changeset and formatting --- .changeset/mighty-insects-judge.md | 12 ++++++++ config/.eslintrc.js | 6 +++- packages/app/src/errors.ts | 3 +- packages/auth/src/platform_browser/index.ts | 32 ++++++++++++++------- packages/firestore/src/api/database.ts | 16 +++++------ packages/util/src/defaults.ts | 4 +-- 6 files changed, 50 insertions(+), 23 deletions(-) create mode 100644 .changeset/mighty-insects-judge.md diff --git a/.changeset/mighty-insects-judge.md b/.changeset/mighty-insects-judge.md new file mode 100644 index 00000000000..ea01480e052 --- /dev/null +++ b/.changeset/mighty-insects-judge.md @@ -0,0 +1,12 @@ +--- +'@firebase/app': minor +'@firebase/app-types': minor +'@firebase/util': minor +'@firebase/auth': patch +'@firebase/database': patch +'@firebase/firestore': patch +'@firebase/functions': patch +'@firebase/storage': patch +--- + +Add functionality to auto-initialize project config and emulator settings from global defaults provided by framework tooling. diff --git a/config/.eslintrc.js b/config/.eslintrc.js index 97f6df9be8f..4b71884c346 100644 --- a/config/.eslintrc.js +++ b/config/.eslintrc.js @@ -129,7 +129,11 @@ module.exports = { { // Check dependencies from both local package.json // and from root package.json. - 'packageDir': [context.getFilename(), path.join(__dirname, '../'), './'], + 'packageDir': [ + context.getFilename(), + path.join(__dirname, '../'), + './' + ], 'devDependencies': [ '**/*.test.ts', '**/test/**/*.ts', diff --git a/packages/app/src/errors.ts b/packages/app/src/errors.ts index 8d6f5983ccf..6cd50e90942 100644 --- a/packages/app/src/errors.ts +++ b/packages/app/src/errors.ts @@ -39,7 +39,8 @@ 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.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 102e503ba96..14fda37d580 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -17,7 +17,12 @@ import { FirebaseApp, getApp, _getProvider } from '@firebase/app'; -import { initializeAuth, beforeAuthStateChanged, onIdTokenChanged, connectAuthEmulator } from '..'; +import { + initializeAuth, + beforeAuthStateChanged, + onIdTokenChanged, + connectAuthEmulator +} from '..'; import { registerAuth } from '../core/auth/register'; import { ClientPlatform } from '../core/util/version'; import { browserLocalPersistence } from './persistence/local_storage'; @@ -28,23 +33,28 @@ import { Auth, User } from '../model/public_types'; import { getDefaultEmulatorHost, getExperimentalSetting } from '@firebase/util'; const DEFAULT_ID_TOKEN_MAX_AGE = 5 * 60; -const authIdTokenMaxAge = getExperimentalSetting('authIdTokenMaxAge') || DEFAULT_ID_TOKEN_MAX_AGE; +const authIdTokenMaxAge = + getExperimentalSetting('authIdTokenMaxAge') || DEFAULT_ID_TOKEN_MAX_AGE; -let lastPostedIdToken: string|undefined|null = null; +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; +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 > authIdTokenMaxAge) return; // Specifically trip null => undefined when logged out, to delete any existing cookie const idToken = idTokenResult?.token; if (lastPostedIdToken === idToken) return; lastPostedIdToken = idToken; await fetch(url, { - method: idToken ? 'POST' : 'DELETE', - headers: idToken ? { - 'Authorization': `Bearer ${idToken}`, - } : {} + method: idToken ? 'POST' : 'DELETE', + headers: idToken + ? { + 'Authorization': `Bearer ${idToken}` + } + : {} }); }; @@ -76,7 +86,7 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { if (authTokenSyncUrl) { const mintCookie = mintCookieFactory(authTokenSyncUrl); beforeAuthStateChanged(auth, mintCookie, () => { - mintCookie(auth.currentUser) + mintCookie(auth.currentUser); }); onIdTokenChanged(auth, user => mintCookie(user)); } diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 6ffda1b044f..c9004a85426 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -164,9 +164,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.' ); } } @@ -408,8 +408,8 @@ function setPersistenceProviders( } logWarn( 'Error enabling offline persistence. Falling back to ' + - 'persistence disabled: ' + - error + 'persistence disabled: ' + + error ); persistenceResult.reject(error); } @@ -482,7 +482,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.' ); } @@ -638,8 +638,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/util/src/defaults.ts b/packages/util/src/defaults.ts index 0eb4a6a7f9f..3fcb1507397 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -50,8 +50,8 @@ const getDefaultsFromEnvVariable = (): FirebaseDefaults | undefined => { try { const json = require(jsonPath); return json; - } catch(e) { - `Unable to read defaults from file: ${jsonPath}.` + } catch (e) { + `Unable to read defaults from file: ${jsonPath}.`; } } }; From 3bf4ce6b7e0e46f3ca580697722cd81e7717f639 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Mon, 26 Sep 2022 22:37:49 -0700 Subject: [PATCH 04/15] Revert eslintrc.js and add firebase minor bump --- .changeset/mighty-insects-judge.md | 1 + config/.eslintrc.js | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.changeset/mighty-insects-judge.md b/.changeset/mighty-insects-judge.md index ea01480e052..701e3b28e27 100644 --- a/.changeset/mighty-insects-judge.md +++ b/.changeset/mighty-insects-judge.md @@ -7,6 +7,7 @@ '@firebase/firestore': patch '@firebase/functions': patch '@firebase/storage': patch +'firebase': minor --- Add functionality to auto-initialize project config and emulator settings from global defaults provided by framework tooling. diff --git a/config/.eslintrc.js b/config/.eslintrc.js index 4b71884c346..04de0f9d10c 100644 --- a/config/.eslintrc.js +++ b/config/.eslintrc.js @@ -129,11 +129,7 @@ module.exports = { { // Check dependencies from both local package.json // and from root package.json. - 'packageDir': [ - context.getFilename(), - path.join(__dirname, '../'), - './' - ], + 'packageDir': [path.join(__dirname, '../'), './'], 'devDependencies': [ '**/*.test.ts', '**/test/**/*.ts', From 5456dbfa0fb7c42c0d23c02954f91ddfaea28e1f Mon Sep 17 00:00:00 2001 From: hsubox76 Date: Tue, 27 Sep 2022 05:57:22 +0000 Subject: [PATCH 05/15] Update API reports --- common/api-review/firestore.api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 3b6538d867b..8cbe775d731 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -233,6 +233,9 @@ export function getFirestore(): Firestore; // @public export function getFirestore(app: FirebaseApp): Firestore; +// @public +export function getFirestore(): Firestore; + // @public export function increment(n: number): FieldValue; From f46b60f9ebb1de4ef6073b1d9149b145277e39d2 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 09:18:05 -0700 Subject: [PATCH 06/15] Fix some lint errors --- packages/auth/src/platform_browser/index.ts | 10 +++++----- packages/util/src/defaults.ts | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index 14fda37d580..6f6717819be 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -43,17 +43,17 @@ const mintCookieFactory = (url: string) => async (user: User | null) => { const idTokenAge = idTokenResult && (new Date().getTime() - Date.parse(idTokenResult.issuedAtTime)) / 1_000; - if (idTokenAge && idTokenAge > authIdTokenMaxAge) 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; + if (lastPostedIdToken === idToken) { return; } lastPostedIdToken = idToken; await fetch(url, { method: idToken ? 'POST' : 'DELETE', headers: idToken ? { - 'Authorization': `Bearer ${idToken}` - } + 'Authorization': `Bearer ${idToken}` + } : {} }); }; @@ -86,7 +86,7 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { if (authTokenSyncUrl) { const mintCookie = mintCookieFactory(authTokenSyncUrl); beforeAuthStateChanged(auth, mintCookie, () => { - mintCookie(auth.currentUser); + void mintCookie(auth.currentUser); }); onIdTokenChanged(auth, user => mintCookie(user)); } diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index 3fcb1507397..ccbcb8e06c3 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -48,10 +48,11 @@ const getDefaultsFromEnvVariable = (): FirebaseDefaults | undefined => { const jsonPath = process.env.__FIREBASE_DEFAULTS_PATH__; if (jsonPath && typeof require !== 'undefined') { try { + // eslint-disable-next-line @typescript-eslint/no-require-imports const json = require(jsonPath); return json; } catch (e) { - `Unable to read defaults from file: ${jsonPath}.`; + console.warn(`Unable to read defaults from file: ${jsonPath}.`); } } }; From 89dfe97b7006e06a142633ddcedcb3166a0f2b07 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 09:26:53 -0700 Subject: [PATCH 07/15] formatting, again --- packages/auth/src/platform_browser/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index 6f6717819be..5d4feb313bc 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -43,17 +43,21 @@ const mintCookieFactory = (url: string) => async (user: User | null) => { const idTokenAge = idTokenResult && (new Date().getTime() - Date.parse(idTokenResult.issuedAtTime)) / 1_000; - if (idTokenAge && idTokenAge > authIdTokenMaxAge) { 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; } + if (lastPostedIdToken === idToken) { + return; + } lastPostedIdToken = idToken; await fetch(url, { method: idToken ? 'POST' : 'DELETE', headers: idToken ? { - 'Authorization': `Bearer ${idToken}` - } + 'Authorization': `Bearer ${idToken}` + } : {} }); }; From 5cd1a55df4031796a8d45715b61bd77d825ffd8f Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 10:12:51 -0700 Subject: [PATCH 08/15] Add back JSON string --- packages/util/src/defaults.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index ccbcb8e06c3..8b14cdee8b6 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -38,21 +38,36 @@ const getDefaultsFromGlobal = (): FirebaseDefaults | undefined => getGlobal().__FIREBASE_DEFAULTS__; /** - * Attempt to read defaults from a JSON file whose path is in + * Attempt to read defaults from a JSON string provided to + * process.env.__FIREBASE_DEFAULTS__ or 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') { + const defaultsJsonString = process.env.__FIREBASE_DEFAULTS__; + const defaultsJsonPath = process.env.__FIREBASE_DEFAULTS_PATH__; + if (defaultsJsonString) { + if (defaultsJsonPath) { + console.warn( + `Values were provided for both __FIREBASE_DEFAULTS__ ` + + `and __FIREBASE_DEFAULTS_PATH__. __FIREBASE_DEFAULTS_PATH__ ` + + `will be ignored.` + ); + } + return JSON.parse(defaultsJsonString); + } + if (defaultsJsonPath && typeof require !== 'undefined') { try { // eslint-disable-next-line @typescript-eslint/no-require-imports - const json = require(jsonPath); + const json = require(defaultsJsonPath); return json; } catch (e) { - console.warn(`Unable to read defaults from file: ${jsonPath}.`); + console.warn( + `Unable to read defaults from file provided to ` + + `__FIREBASE_DEFAULTS_PATH__: ${defaultsJsonPath}` + ); } } }; From 6acc816cf0a286afd5d53e3031f3b3ac26821934 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 10:26:51 -0700 Subject: [PATCH 09/15] revert app-types --- packages/app-types/index.d.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/app-types/index.d.ts b/packages/app-types/index.d.ts index 088c64fad63..72b8fa68f9d 100644 --- a/packages/app-types/index.d.ts +++ b/packages/app-types/index.d.ts @@ -74,10 +74,6 @@ export interface FirebaseNamespace { * omitted) */ initializeApp(options: FirebaseOptions, name?: string): FirebaseApp; - /** - * Create (and initialize) a FirebaseApp. - */ - initializeApp(): FirebaseApp; app: { /** From c1757f7335d999c57b1e9a26d040803ff344a471 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 14:19:04 -0700 Subject: [PATCH 10/15] Remove unnecessary export --- packages/database/src/api/Database.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/database/src/api/Database.ts b/packages/database/src/api/Database.ts index a0c2a816597..db0273b1721 100644 --- a/packages/database/src/api/Database.ts +++ b/packages/database/src/api/Database.ts @@ -54,10 +54,7 @@ import { WebSocketConnection } from '../realtime/WebSocketConnection'; import { ReferenceImpl } from './Reference_impl'; -export { - EmulatorMockTokenOptions, - getDefaultEmulatorHost -} from '@firebase/util'; +export { EmulatorMockTokenOptions } from '@firebase/util'; /** * This variable is also defined in the firebase Node.js Admin SDK. Before * modifying this definition, consult the definition in: From 4bc0bcf0654a55aaaef8ac5605b9d15cb9ed4255 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 15:10:52 -0700 Subject: [PATCH 11/15] Remove extra getFirestore() --- common/api-review/firestore.api.md | 3 --- packages/firestore/src/api/database.ts | 8 -------- 2 files changed, 11 deletions(-) diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 8cbe775d731..e3a60e64129 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -227,9 +227,6 @@ export function getDocsFromCache(query: Query): Promise>; // @public export function getDocsFromServer(query: Query): Promise>; -// @public -export function getFirestore(): Firestore; - // @public export function getFirestore(app: FirebaseApp): Firestore; diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index c9004a85426..d61b1ffe21a 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -188,14 +188,6 @@ export function initializeFirestore( }); } -/** - * Returns the existing default {@link Firestore} instance that is associated with the - * default {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new - * instance with default settings. - * - * @returns The {@link Firestore} instance of the provided app. - */ -export function getFirestore(): Firestore; /** * Returns the existing default {@link Firestore} instance that is associated with the * provided {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new From cf7b8fc49b6d89abb6bde6a961dfd85374234970 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Tue, 27 Sep 2022 17:22:13 -0700 Subject: [PATCH 12/15] Add comments and public annotations, fix promise --- common/api-review/util.api.md | 13 ++----- packages/auth/src/platform_browser/index.ts | 4 +- packages/util/src/defaults.ts | 42 +++++++++++++++++++-- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index f81a0575142..f4846456a07 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -195,21 +195,16 @@ export class FirebaseError extends Error { // @public export type FirebaseSignInProvider = 'custom' | 'email' | 'password' | 'phone' | 'anonymous' | 'google.com' | 'facebook.com' | 'github.com' | 'twitter.com' | 'microsoft.com' | 'apple.com'; -// 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) +// @public 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; +// @public +export const getDefaultEmulatorHost: (productName: 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) +// @public 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/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index 5d4feb313bc..8d9e675ecb8 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -89,9 +89,7 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { const authTokenSyncUrl = getExperimentalSetting('authTokenSyncURL'); if (authTokenSyncUrl) { const mintCookie = mintCookieFactory(authTokenSyncUrl); - beforeAuthStateChanged(auth, mintCookie, () => { - void mintCookie(auth.currentUser); - }); + beforeAuthStateChanged(auth, mintCookie, () => mintCookie(auth.currentUser)); onIdTokenChanged(auth, user => mintCookie(user)); } diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index 8b14cdee8b6..1b79b5d99a8 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -18,9 +18,23 @@ import { base64Decode } from './crypt'; import { getGlobal } from './environment'; -type ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'; +/** + * Keys for experimental properties on the `FirebaseDefaults` object. + * @public + */ +export type ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'; -interface FirebaseDefaults { +/** + * An object that can be injected into the environment as __FIREBASE_DEFAULTS__, + * either as a property of globalThis, a shell environment variable, or a + * cookie. + * + * This object can be used to automatically configure and initialize + * a Firebase app as well as any emulators. + * + * @public + */ +export interface FirebaseDefaults { config?: Record; emulatorHosts?: Record; _authTokenSyncURL?: string; @@ -81,17 +95,37 @@ const getDefaultsFromCookie = (): FirebaseDefaults | undefined => { return decoded && JSON.parse(decoded); }; +/** + * Get the __FIREBASE_DEFAULTS__ object. It checks in order: + * (1) if such an object exists as a property of `globalThis` + * (2) if such an object was provided on a shell environment variable + * (3) if such an object exists in a cookie + */ const getDefaults = (): FirebaseDefaults | undefined => getDefaultsFromGlobal() || getDefaultsFromEnvVariable() || getDefaultsFromCookie(); -export const getDefaultEmulatorHost = (name: string): string | undefined => - getDefaults()?.emulatorHosts?.[name]; +/** + * Returns emulator host stored in the __FIREBASE_DEFAULTS__ object + * for the given product. + * @public + */ +export const getDefaultEmulatorHost = (productName: string): string | undefined => + getDefaults()?.emulatorHosts?.[productName]; +/** + * Returns Firebase app config stored in the __FIREBASE_DEFAULTS__ object. + * @public + */ export const getDefaultAppConfig = (): Record | undefined => getDefaults()?.config; +/** + * Returns an experimental setting on the __FIREBASE_DEFAULTS__ object (properties + * prefixed by "_") + * @public + */ export const getExperimentalSetting = ( name: T ): FirebaseDefaults[`_${T}`] => From cd3f7e11785e4c85ac3a92e0dadafd57a40215d9 Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 28 Sep 2022 09:33:12 -0700 Subject: [PATCH 13/15] Formatting pass. --- packages/auth/src/platform_browser/index.ts | 4 +++- packages/util/src/defaults.ts | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/auth/src/platform_browser/index.ts b/packages/auth/src/platform_browser/index.ts index 8d9e675ecb8..9bfe4e3b01a 100644 --- a/packages/auth/src/platform_browser/index.ts +++ b/packages/auth/src/platform_browser/index.ts @@ -89,7 +89,9 @@ export function getAuth(app: FirebaseApp = getApp()): Auth { const authTokenSyncUrl = getExperimentalSetting('authTokenSyncURL'); if (authTokenSyncUrl) { const mintCookie = mintCookieFactory(authTokenSyncUrl); - beforeAuthStateChanged(auth, mintCookie, () => mintCookie(auth.currentUser)); + beforeAuthStateChanged(auth, mintCookie, () => + mintCookie(auth.currentUser) + ); onIdTokenChanged(auth, user => mintCookie(user)); } diff --git a/packages/util/src/defaults.ts b/packages/util/src/defaults.ts index 1b79b5d99a8..6acc5886b5c 100644 --- a/packages/util/src/defaults.ts +++ b/packages/util/src/defaults.ts @@ -28,10 +28,10 @@ export type ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'; * An object that can be injected into the environment as __FIREBASE_DEFAULTS__, * either as a property of globalThis, a shell environment variable, or a * cookie. - * + * * This object can be used to automatically configure and initialize * a Firebase app as well as any emulators. - * + * * @public */ export interface FirebaseDefaults { @@ -111,8 +111,9 @@ const getDefaults = (): FirebaseDefaults | undefined => * for the given product. * @public */ -export const getDefaultEmulatorHost = (productName: string): string | undefined => - getDefaults()?.emulatorHosts?.[productName]; +export const getDefaultEmulatorHost = ( + productName: string +): string | undefined => getDefaults()?.emulatorHosts?.[productName]; /** * Returns Firebase app config stored in the __FIREBASE_DEFAULTS__ object. From 28ab641175861dd37e1946d689ba407e4ae02d4e Mon Sep 17 00:00:00 2001 From: hsubox76 Date: Wed, 28 Sep 2022 17:01:58 +0000 Subject: [PATCH 14/15] Update API reports --- common/api-review/util.api.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/common/api-review/util.api.md b/common/api-review/util.api.md index f4846456a07..57e18bd3d2b 100644 --- a/common/api-review/util.api.md +++ b/common/api-review/util.api.md @@ -173,11 +173,28 @@ export function errorPrefix(fnName: string, argName: string): string; // @public (undocumented) export type Executor = (observer: Observer) => void; +// @public +export type ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'; + // Warning: (ae-missing-release-tag) "extractQuerystring" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public export function extractQuerystring(url: string): string; +// @public +export interface FirebaseDefaults { + // (undocumented) + [key: string]: unknown; + // (undocumented) + _authIdTokenMaxAge?: number; + // (undocumented) + _authTokenSyncURL?: string; + // (undocumented) + config?: Record; + // (undocumented) + emulatorHosts?: Record; +} + // Warning: (ae-missing-release-tag) "FirebaseError" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -201,9 +218,6 @@ export const getDefaultAppConfig: () => Record | undefined; // @public export const getDefaultEmulatorHost: (productName: 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 -// // @public export const getExperimentalSetting: (name: T) => FirebaseDefaults[`_${T}`]; From 87c03a4e9105ab0ead8b895f740f218a8b5d564c Mon Sep 17 00:00:00 2001 From: Christina Holland Date: Wed, 28 Sep 2022 10:25:37 -0700 Subject: [PATCH 15/15] retrigger checks