UNPKG

voluptasmollitia

Version:
309 lines (275 loc) 9.38 kB
/** * @license * Copyright 2019 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 { FirebaseApp, FirebaseOptions, FirebaseNamespace, FirebaseAppConfig } from '@firebase/app-types'; import { _FirebaseApp, _FirebaseNamespace, FirebaseService, FirebaseServiceNamespace } from '@firebase/app-types/private'; import { deepExtend, contains } from '@firebase/util'; import { FirebaseAppImpl } from './firebaseApp'; import { ERROR_FACTORY, AppError } from './errors'; import { FirebaseAppLiteImpl } from './lite/firebaseAppLite'; import { DEFAULT_ENTRY_NAME, PLATFORM_LOG_STRING } from './constants'; import { version } from '../../firebase/package.json'; import { logger } from './logger'; import { setUserLogHandler, setLogLevel, LogCallback, LogOptions } from '@firebase/logger'; import { Component, ComponentType, Name } from '@firebase/component'; /** * Because auth can't share code with other components, we attach the utility functions * in an internal namespace to share code. * This function return a firebase namespace object without * any utility functions, so it can be shared between the regular firebaseNamespace and * the lite version. */ export function createFirebaseNamespaceCore( firebaseAppImpl: typeof FirebaseAppImpl | typeof FirebaseAppLiteImpl ): FirebaseNamespace { const apps: { [name: string]: FirebaseApp } = {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any const components = new Map<string, Component<any>>(); // A namespace is a plain JavaScript Object. const namespace: FirebaseNamespace = { // Hack to prevent Babel from modifying the object returned // as the firebase namespace. // @ts-ignore __esModule: true, initializeApp, // @ts-ignore app, registerVersion, setLogLevel, onLog, // @ts-ignore apps: null, SDK_VERSION: version, INTERNAL: { registerComponent, removeApp, components, useAsService } }; // Inject a circular default export to allow Babel users who were previously // using: // // import firebase from 'firebase'; // which becomes: var firebase = require('firebase').default; // // instead of // // import * as firebase from 'firebase'; // which becomes: var firebase = require('firebase'); // eslint-disable-next-line @typescript-eslint/no-explicit-any (namespace as any)['default'] = namespace; // firebase.apps is a read-only getter. Object.defineProperty(namespace, 'apps', { get: getApps }); /** * Called by App.delete() - but before any services associated with the App * are deleted. */ function removeApp(name: string): void { delete apps[name]; } /** * Get the App object for a given name (or DEFAULT). */ function app(name?: string): FirebaseApp { name = name || DEFAULT_ENTRY_NAME; if (!contains(apps, name)) { throw ERROR_FACTORY.create(AppError.NO_APP, { appName: name }); } return apps[name]; } // @ts-ignore app['App'] = firebaseAppImpl; /** * Create a new App instance (name must be unique). */ function initializeApp( options: FirebaseOptions, config?: FirebaseAppConfig ): FirebaseApp; function initializeApp(options: FirebaseOptions, name?: string): FirebaseApp; function initializeApp( options: FirebaseOptions, rawConfig = {} ): FirebaseApp { if (typeof rawConfig !== 'object' || rawConfig === null) { const name = rawConfig; rawConfig = { name }; } const config = rawConfig as FirebaseAppConfig; if (config.name === undefined) { config.name = DEFAULT_ENTRY_NAME; } const { name } = config; if (typeof name !== 'string' || !name) { throw ERROR_FACTORY.create(AppError.BAD_APP_NAME, { appName: String(name) }); } if (contains(apps, name)) { throw ERROR_FACTORY.create(AppError.DUPLICATE_APP, { appName: name }); } const app = new firebaseAppImpl( options, config, namespace as _FirebaseNamespace ); apps[name] = app; return app; } /* * Return an array of all the non-deleted FirebaseApps. */ function getApps(): FirebaseApp[] { // Make a copy so caller cannot mutate the apps list. return Object.keys(apps).map(name => apps[name]); } function registerComponent( component: Component ): FirebaseServiceNamespace<FirebaseService> | null { const componentName = component.name; if (components.has(componentName)) { logger.debug( `There were multiple attempts to register component ${componentName}.` ); return component.type === ComponentType.PUBLIC ? // eslint-disable-next-line @typescript-eslint/no-explicit-any (namespace as any)[componentName] : null; } components.set(componentName, component); // create service namespace for public components if (component.type === ComponentType.PUBLIC) { // The Service namespace is an accessor function ... const serviceNamespace = ( appArg: FirebaseApp = app() ): FirebaseService => { // eslint-disable-next-line @typescript-eslint/no-explicit-any if (typeof (appArg as any)[componentName] !== 'function') { // Invalid argument. // This happens in the following case: firebase.storage('gs:/') throw ERROR_FACTORY.create(AppError.INVALID_APP_ARGUMENT, { appName: componentName }); } // Forward service instance lookup to the FirebaseApp. // eslint-disable-next-line @typescript-eslint/no-explicit-any return (appArg as any)[componentName](); }; // ... and a container for service-level properties. if (component.serviceProps !== undefined) { deepExtend(serviceNamespace, component.serviceProps); } // eslint-disable-next-line @typescript-eslint/no-explicit-any (namespace as any)[componentName] = serviceNamespace; // Patch the FirebaseAppImpl prototype // eslint-disable-next-line @typescript-eslint/no-explicit-any (firebaseAppImpl.prototype as any)[componentName] = // TODO: The eslint disable can be removed and the 'ignoreRestArgs' // option added to the no-explicit-any rule when ESlint releases it. // eslint-disable-next-line @typescript-eslint/no-explicit-any function (...args: any) { const serviceFxn = this._getService.bind(this, componentName); return serviceFxn.apply( this, component.multipleInstances ? args : [] ); }; } // add the component to existing app instances for (const appName of Object.keys(apps)) { (apps[appName] as _FirebaseApp)._addComponent(component); } return component.type === ComponentType.PUBLIC ? // eslint-disable-next-line @typescript-eslint/no-explicit-any (namespace as any)[componentName] : null; } function registerVersion( libraryKeyOrName: string, version: string, variant?: string ): void { // TODO: We can use this check to whitelist strings when/if we set up // a good whitelist system. let library = PLATFORM_LOG_STRING[libraryKeyOrName] ?? libraryKeyOrName; if (variant) { library += `-${variant}`; } const libraryMismatch = library.match(/\s|\//); const versionMismatch = version.match(/\s|\//); if (libraryMismatch || versionMismatch) { const warning = [ `Unable to register library "${library}" with version "${version}":` ]; if (libraryMismatch) { warning.push( `library name "${library}" contains illegal characters (whitespace or "/")` ); } if (libraryMismatch && versionMismatch) { warning.push('and'); } if (versionMismatch) { warning.push( `version name "${version}" contains illegal characters (whitespace or "/")` ); } logger.warn(warning.join(' ')); return; } registerComponent( new Component( `${library}-version` as Name, () => ({ library, version }), ComponentType.VERSION ) ); } function onLog(logCallback: LogCallback | null, options?: LogOptions): void { if (logCallback !== null && typeof logCallback !== 'function') { throw ERROR_FACTORY.create(AppError.INVALID_LOG_ARGUMENT); } setUserLogHandler(logCallback, options); } // Map the requested service to a registered service name // (used to map auth to serverAuth service when needed). function useAsService(app: FirebaseApp, name: string): string | null { if (name === 'serverAuth') { return null; } const useService = name; return useService; } return namespace; }