import { createSubscribe, deepCopy, deepExtend, ErrorFactory, patchProperty } from '@firebase/util'; /** * Copyright 2017 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 contains = function (obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); }; var DEFAULT_ENTRY_NAME = '[DEFAULT]'; // An array to capture listeners before the true auth functions // exist var tokenListeners = []; /** * Global context object for a collection of services using * a shared authentication state. */ var FirebaseAppImpl = /** @class */ (function () { function FirebaseAppImpl(options, config, firebase_) { this.firebase_ = firebase_; this.isDeleted_ = false; this.services_ = {}; this.name_ = config.name; this._automaticDataCollectionEnabled = config.automaticDataCollectionEnabled || false; this.options_ = deepCopy(options); this.INTERNAL = { getUid: function () { return null; }, getToken: function () { return Promise.resolve(null); }, addAuthTokenListener: function (callback) { tokenListeners.push(callback); // Make sure callback is called, asynchronously, in the absence of the auth module setTimeout(function () { return callback(null); }, 0); }, removeAuthTokenListener: function (callback) { tokenListeners = tokenListeners.filter(function (listener) { return listener !== callback; }); } }; } Object.defineProperty(FirebaseAppImpl.prototype, "automaticDataCollectionEnabled", { get: function () { this.checkDestroyed_(); return this._automaticDataCollectionEnabled; }, set: function (val) { this.checkDestroyed_(); this._automaticDataCollectionEnabled = val; }, enumerable: true, configurable: true }); Object.defineProperty(FirebaseAppImpl.prototype, "name", { get: function () { this.checkDestroyed_(); return this.name_; }, enumerable: true, configurable: true }); Object.defineProperty(FirebaseAppImpl.prototype, "options", { get: function () { this.checkDestroyed_(); return this.options_; }, enumerable: true, configurable: true }); FirebaseAppImpl.prototype.delete = function () { var _this = this; return new Promise(function (resolve) { _this.checkDestroyed_(); resolve(); }) .then(function () { _this.firebase_.INTERNAL.removeApp(_this.name_); var services = []; Object.keys(_this.services_).forEach(function (serviceKey) { Object.keys(_this.services_[serviceKey]).forEach(function (instanceKey) { services.push(_this.services_[serviceKey][instanceKey]); }); }); return Promise.all(services.map(function (service) { return service.INTERNAL.delete(); })); }) .then(function () { _this.isDeleted_ = true; _this.services_ = {}; }); }; /** * Return a service instance associated with this app (creating it * on demand), identified by the passed instanceIdentifier. * * NOTE: Currently storage is the only one that is leveraging this * functionality. They invoke it by calling: * * ```javascript * firebase.app().storage('STORAGE BUCKET ID') * ``` * * The service name is passed to this already * @internal */ FirebaseAppImpl.prototype._getService = function (name, instanceIdentifier) { if (instanceIdentifier === void 0) { instanceIdentifier = DEFAULT_ENTRY_NAME; } this.checkDestroyed_(); if (!this.services_[name]) { this.services_[name] = {}; } if (!this.services_[name][instanceIdentifier]) { /** * If a custom instance has been defined (i.e. not '[DEFAULT]') * then we will pass that instance on, otherwise we pass `null` */ var instanceSpecifier = instanceIdentifier !== DEFAULT_ENTRY_NAME ? instanceIdentifier : undefined; var service = this.firebase_.INTERNAL.factories[name](this, this.extendApp.bind(this), instanceSpecifier); this.services_[name][instanceIdentifier] = service; } return this.services_[name][instanceIdentifier]; }; /** * Callback function used to extend an App instance at the time * of service instance creation. */ FirebaseAppImpl.prototype.extendApp = function (props) { var _this = this; // Copy the object onto the FirebaseAppImpl prototype deepExtend(this, props); /** * If the app has overwritten the addAuthTokenListener stub, forward * the active token listeners on to the true fxn. * * TODO: This function is required due to our current module * structure. Once we are able to rely strictly upon a single module * implementation, this code should be refactored and Auth should * provide these stubs and the upgrade logic */ if (props.INTERNAL && props.INTERNAL.addAuthTokenListener) { tokenListeners.forEach(function (listener) { _this.INTERNAL.addAuthTokenListener(listener); }); tokenListeners = []; } }; /** * This function will throw an Error if the App has already been deleted - * use before performing API actions on the App. */ FirebaseAppImpl.prototype.checkDestroyed_ = function () { if (this.isDeleted_) { error('app-deleted', { name: this.name_ }); } }; return FirebaseAppImpl; }()); // Prevent dead-code elimination of these methods w/o invalid property // copying. (FirebaseAppImpl.prototype.name && FirebaseAppImpl.prototype.options) || FirebaseAppImpl.prototype.delete || console.log('dc'); /** * Return a firebase namespace object. * * In production, this will be called exactly once and the result * assigned to the 'firebase' global. It may be called multiple times * in unit tests. */ function createFirebaseNamespace() { var apps_ = {}; var factories = {}; var appHooks = {}; // A namespace is a plain JavaScript Object. var namespace = { // Hack to prevent Babel from modifying the object returned // as the firebase namespace. __esModule: true, initializeApp: initializeApp, app: app, apps: null, Promise: Promise, SDK_VERSION: '5.5.9', INTERNAL: { registerService: registerService, createFirebaseNamespace: createFirebaseNamespace, extendNamespace: extendNamespace, createSubscribe: createSubscribe, ErrorFactory: ErrorFactory, removeApp: removeApp, factories: factories, useAsService: useAsService, Promise: Promise, deepExtend: deepExtend } }; // 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'); patchProperty(namespace, '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) { var app = apps_[name]; callAppHooks(app, 'delete'); delete apps_[name]; } /** * Get the App object for a given name (or DEFAULT). */ function app(name) { name = name || DEFAULT_ENTRY_NAME; if (!contains(apps_, name)) { error('no-app', { name: name }); } return apps_[name]; } patchProperty(app, 'App', FirebaseAppImpl); function initializeApp(options, rawConfig) { if (rawConfig === void 0) { rawConfig = {}; } if (typeof rawConfig !== 'object' || rawConfig === null) { var name_1 = rawConfig; rawConfig = { name: name_1 }; } var config = rawConfig; if (config.name === undefined) { config.name = DEFAULT_ENTRY_NAME; } var name = config.name; if (typeof name !== 'string' || !name) { error('bad-app-name', { name: name + '' }); } if (contains(apps_, name)) { error('duplicate-app', { name: name }); } var app = new FirebaseAppImpl(options, config, namespace); apps_[name] = app; callAppHooks(app, 'create'); return app; } /* * Return an array of all the non-deleted FirebaseApps. */ function getApps() { // Make a copy so caller cannot mutate the apps list. return Object.keys(apps_).map(function (name) { return apps_[name]; }); } /* * Register a Firebase Service. * * firebase.INTERNAL.registerService() * * TODO: Implement serviceProperties. */ function registerService(name, createService, serviceProperties, appHook, allowMultipleInstances) { // Cannot re-register a service that already exists if (factories[name]) { error('duplicate-service', { name: name }); } // Capture the service factory for later service instantiation factories[name] = createService; // Capture the appHook, if passed if (appHook) { appHooks[name] = appHook; // Run the **new** app hook on all existing apps getApps().forEach(function (app) { appHook('create', app); }); } // The Service namespace is an accessor function ... var serviceNamespace = function (appArg) { if (appArg === void 0) { appArg = app(); } if (typeof appArg[name] !== 'function') { // Invalid argument. // This happens in the following case: firebase.storage('gs:/') error('invalid-app-argument', { name: name }); } // Forward service instance lookup to the FirebaseApp. return appArg[name](); }; // ... and a container for service-level properties. if (serviceProperties !== undefined) { deepExtend(serviceNamespace, serviceProperties); } // Monkey-patch the serviceNamespace onto the firebase namespace namespace[name] = serviceNamespace; // Patch the FirebaseAppImpl prototype FirebaseAppImpl.prototype[name] = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var serviceFxn = this._getService.bind(this, name); return serviceFxn.apply(this, allowMultipleInstances ? args : []); }; return serviceNamespace; } /** * Patch the top-level firebase namespace with additional properties. * * firebase.INTERNAL.extendNamespace() */ function extendNamespace(props) { deepExtend(namespace, props); } function callAppHooks(app, eventName) { Object.keys(factories).forEach(function (serviceName) { // Ignore virtual services var factoryName = useAsService(app, serviceName); if (factoryName === null) { return; } if (appHooks[factoryName]) { appHooks[factoryName](eventName, app); } }); } // Map the requested service to a registered service name // (used to map auth to serverAuth service when needed). function useAsService(app, name) { if (name === 'serverAuth') { return null; } var useService = name; var options = app.options; return useService; } return namespace; } function error(code, args) { throw appErrors.create(code, args); } // TypeScript does not support non-string indexes! // let errors: {[code: AppError: string} = { var errors = { 'no-app': "No Firebase App '{$name}' has been created - " + 'call Firebase App.initializeApp()', 'bad-app-name': "Illegal App name: '{$name}", 'duplicate-app': "Firebase App named '{$name}' already exists", 'app-deleted': "Firebase App named '{$name}' already deleted", 'duplicate-service': "Firebase service named '{$name}' already registered", 'sa-not-supported': 'Initializing the Firebase SDK with a service ' + 'account is only allowed in a Node.js environment. On client ' + 'devices, you should instead initialize the SDK with an api key and ' + 'auth domain', 'invalid-app-argument': 'firebase.{$name}() takes either no argument or a ' + 'Firebase App instance.' }; var appErrors = new ErrorFactory('app', 'Firebase', errors); /** * Copyright 2017 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 firebase = createFirebaseNamespace(); export default firebase; export { firebase };