import { __values, __read, __awaiter, __generator, __spreadArray } from 'tslib'; import { Deferred } from '@firebase/util'; /** * Component for service name T, e.g. `auth`, `auth-internal` */ var Component = /** @class */ (function () { /** * * @param name The public service name, e.g. app, auth, firestore, database * @param instanceFactory Service factory responsible for creating the public interface * @param type whether the service provided by the component is public or private */ function Component(name, instanceFactory, type) { this.name = name; this.instanceFactory = instanceFactory; this.type = type; this.multipleInstances = false; /** * Properties to be added to the service namespace */ this.serviceProps = {}; this.instantiationMode = "LAZY" /* LAZY */; this.onInstanceCreated = null; } Component.prototype.setInstantiationMode = function (mode) { this.instantiationMode = mode; return this; }; Component.prototype.setMultipleInstances = function (multipleInstances) { this.multipleInstances = multipleInstances; return this; }; Component.prototype.setServiceProps = function (props) { this.serviceProps = props; return this; }; Component.prototype.setInstanceCreatedCallback = function (callback) { this.onInstanceCreated = callback; return this; }; return Component; }()); /** * @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. */ var DEFAULT_ENTRY_NAME = '[DEFAULT]'; /** * @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. */ /** * Provider for instance for service name T, e.g. 'auth', 'auth-internal' * NameServiceMapping[T] is an alias for the type of the instance */ var Provider = /** @class */ (function () { function Provider(name, container) { this.name = name; this.container = container; this.component = null; this.instances = new Map(); this.instancesDeferred = new Map(); this.instancesOptions = new Map(); this.onInitCallbacks = new Map(); } /** * @param identifier A provider can provide mulitple instances of a service * if this.component.multipleInstances is true. */ Provider.prototype.get = function (identifier) { // if multipleInstances is not supported, use the default name var normalizedIdentifier = this.normalizeInstanceIdentifier(identifier); if (!this.instancesDeferred.has(normalizedIdentifier)) { var deferred = new Deferred(); this.instancesDeferred.set(normalizedIdentifier, deferred); if (this.isInitialized(normalizedIdentifier) || this.shouldAutoInitialize()) { // initialize the service if it can be auto-initialized try { var instance = this.getOrInitializeService({ instanceIdentifier: normalizedIdentifier }); if (instance) { deferred.resolve(instance); } } catch (e) { // when the instance factory throws an exception during get(), it should not cause // a fatal error. We just return the unresolved promise in this case. } } } return this.instancesDeferred.get(normalizedIdentifier).promise; }; Provider.prototype.getImmediate = function (options) { var _a; // if multipleInstances is not supported, use the default name var normalizedIdentifier = this.normalizeInstanceIdentifier(options === null || options === void 0 ? void 0 : options.identifier); var optional = (_a = options === null || options === void 0 ? void 0 : options.optional) !== null && _a !== void 0 ? _a : false; if (this.isInitialized(normalizedIdentifier) || this.shouldAutoInitialize()) { try { return this.getOrInitializeService({ instanceIdentifier: normalizedIdentifier }); } catch (e) { if (optional) { return null; } else { throw e; } } } else { // In case a component is not initialized and should/can not be auto-initialized at the moment, return null if the optional flag is set, or throw if (optional) { return null; } else { throw Error("Service " + this.name + " is not available"); } } }; Provider.prototype.getComponent = function () { return this.component; }; Provider.prototype.setComponent = function (component) { var e_1, _a; if (component.name !== this.name) { throw Error("Mismatching Component " + component.name + " for Provider " + this.name + "."); } if (this.component) { throw Error("Component for " + this.name + " has already been provided"); } this.component = component; // return early without attempting to initialize the component if the component requires explicit initialization (calling `Provider.initialize()`) if (!this.shouldAutoInitialize()) { return; } // if the service is eager, initialize the default instance if (isComponentEager(component)) { try { this.getOrInitializeService({ instanceIdentifier: DEFAULT_ENTRY_NAME }); } catch (e) { // when the instance factory for an eager Component throws an exception during the eager // initialization, it should not cause a fatal error. // TODO: Investigate if we need to make it configurable, because some component may want to cause // a fatal error in this case? } } try { // Create service instances for the pending promises and resolve them // NOTE: if this.multipleInstances is false, only the default instance will be created // and all promises with resolve with it regardless of the identifier. for (var _b = __values(this.instancesDeferred.entries()), _c = _b.next(); !_c.done; _c = _b.next()) { var _d = __read(_c.value, 2), instanceIdentifier = _d[0], instanceDeferred = _d[1]; var normalizedIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier); try { // `getOrInitializeService()` should always return a valid instance since a component is guaranteed. use ! to make typescript happy. var instance = this.getOrInitializeService({ instanceIdentifier: normalizedIdentifier }); instanceDeferred.resolve(instance); } catch (e) { // when the instance factory throws an exception, it should not cause // a fatal error. We just leave the promise unresolved. } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } }; Provider.prototype.clearInstance = function (identifier) { if (identifier === void 0) { identifier = DEFAULT_ENTRY_NAME; } this.instancesDeferred.delete(identifier); this.instancesOptions.delete(identifier); this.instances.delete(identifier); }; // app.delete() will call this method on every provider to delete the services // TODO: should we mark the provider as deleted? Provider.prototype.delete = function () { return __awaiter(this, void 0, void 0, function () { var services; return __generator(this, function (_a) { switch (_a.label) { case 0: services = Array.from(this.instances.values()); return [4 /*yield*/, Promise.all(__spreadArray(__spreadArray([], __read(services .filter(function (service) { return 'INTERNAL' in service; }) // legacy services // eslint-disable-next-line @typescript-eslint/no-explicit-any .map(function (service) { return service.INTERNAL.delete(); }))), __read(services .filter(function (service) { return '_delete' in service; }) // modularized services // eslint-disable-next-line @typescript-eslint/no-explicit-any .map(function (service) { return service._delete(); }))))]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; Provider.prototype.isComponentSet = function () { return this.component != null; }; Provider.prototype.isInitialized = function (identifier) { if (identifier === void 0) { identifier = DEFAULT_ENTRY_NAME; } return this.instances.has(identifier); }; Provider.prototype.getOptions = function (identifier) { if (identifier === void 0) { identifier = DEFAULT_ENTRY_NAME; } return this.instancesOptions.get(identifier) || {}; }; Provider.prototype.initialize = function (opts) { var e_2, _a; if (opts === void 0) { opts = {}; } var _b = opts.options, options = _b === void 0 ? {} : _b; var normalizedIdentifier = this.normalizeInstanceIdentifier(opts.instanceIdentifier); if (this.isInitialized(normalizedIdentifier)) { throw Error(this.name + "(" + normalizedIdentifier + ") has already been initialized"); } if (!this.isComponentSet()) { throw Error("Component " + this.name + " has not been registered yet"); } var instance = this.getOrInitializeService({ instanceIdentifier: normalizedIdentifier, options: options }); try { // resolve any pending promise waiting for the service instance for (var _c = __values(this.instancesDeferred.entries()), _d = _c.next(); !_d.done; _d = _c.next()) { var _e = __read(_d.value, 2), instanceIdentifier = _e[0], instanceDeferred = _e[1]; var normalizedDeferredIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier); if (normalizedIdentifier === normalizedDeferredIdentifier) { instanceDeferred.resolve(instance); } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_2) throw e_2.error; } } return instance; }; /** * * @param callback - a function that will be invoked after the provider has been initialized by calling provider.initialize(). * The function is invoked SYNCHRONOUSLY, so it should not execute any longrunning tasks in order to not block the program. * * @param identifier An optional instance identifier * @returns a function to unregister the callback */ Provider.prototype.onInit = function (callback, identifier) { var _a; var normalizedIdentifier = this.normalizeInstanceIdentifier(identifier); var existingCallbacks = (_a = this.onInitCallbacks.get(normalizedIdentifier)) !== null && _a !== void 0 ? _a : new Set(); existingCallbacks.add(callback); this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks); var existingInstance = this.instances.get(normalizedIdentifier); if (existingInstance) { callback(existingInstance, normalizedIdentifier); } return function () { existingCallbacks.delete(callback); }; }; /** * Invoke onInit callbacks synchronously * @param instance the service instance` */ Provider.prototype.invokeOnInitCallbacks = function (instance, identifier) { var e_3, _a; var callbacks = this.onInitCallbacks.get(identifier); if (!callbacks) { return; } try { for (var callbacks_1 = __values(callbacks), callbacks_1_1 = callbacks_1.next(); !callbacks_1_1.done; callbacks_1_1 = callbacks_1.next()) { var callback = callbacks_1_1.value; try { callback(instance, identifier); } catch (_b) { // ignore errors in the onInit callback } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (callbacks_1_1 && !callbacks_1_1.done && (_a = callbacks_1.return)) _a.call(callbacks_1); } finally { if (e_3) throw e_3.error; } } }; Provider.prototype.getOrInitializeService = function (_a) { var instanceIdentifier = _a.instanceIdentifier, _b = _a.options, options = _b === void 0 ? {} : _b; var instance = this.instances.get(instanceIdentifier); if (!instance && this.component) { instance = this.component.instanceFactory(this.container, { instanceIdentifier: normalizeIdentifierForFactory(instanceIdentifier), options: options }); this.instances.set(instanceIdentifier, instance); this.instancesOptions.set(instanceIdentifier, options); /** * Invoke onInit listeners. * Note this.component.onInstanceCreated is different, which is used by the component creator, * while onInit listeners are registered by consumers of the provider. */ this.invokeOnInitCallbacks(instance, instanceIdentifier); /** * Order is important * onInstanceCreated() should be called after this.instances.set(instanceIdentifier, instance); which * makes `isInitialized()` return true. */ if (this.component.onInstanceCreated) { try { this.component.onInstanceCreated(this.container, instanceIdentifier, instance); } catch (_c) { // ignore errors in the onInstanceCreatedCallback } } } return instance || null; }; Provider.prototype.normalizeInstanceIdentifier = function (identifier) { if (identifier === void 0) { identifier = DEFAULT_ENTRY_NAME; } if (this.component) { return this.component.multipleInstances ? identifier : DEFAULT_ENTRY_NAME; } else { return identifier; // assume multiple instances are supported before the component is provided. } }; Provider.prototype.shouldAutoInitialize = function () { return (!!this.component && this.component.instantiationMode !== "EXPLICIT" /* EXPLICIT */); }; return Provider; }()); // undefined should be passed to the service factory for the default instance function normalizeIdentifierForFactory(identifier) { return identifier === DEFAULT_ENTRY_NAME ? undefined : identifier; } function isComponentEager(component) { return component.instantiationMode === "EAGER" /* EAGER */; } /** * @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. */ /** * ComponentContainer that provides Providers for service name T, e.g. `auth`, `auth-internal` */ var ComponentContainer = /** @class */ (function () { function ComponentContainer(name) { this.name = name; this.providers = new Map(); } /** * * @param component Component being added * @param overwrite When a component with the same name has already been registered, * if overwrite is true: overwrite the existing component with the new component and create a new * provider with the new component. It can be useful in tests where you want to use different mocks * for different tests. * if overwrite is false: throw an exception */ ComponentContainer.prototype.addComponent = function (component) { var provider = this.getProvider(component.name); if (provider.isComponentSet()) { throw new Error("Component " + component.name + " has already been registered with " + this.name); } provider.setComponent(component); }; ComponentContainer.prototype.addOrOverwriteComponent = function (component) { var provider = this.getProvider(component.name); if (provider.isComponentSet()) { // delete the existing provider from the container, so we can register the new component this.providers.delete(component.name); } this.addComponent(component); }; /** * getProvider provides a type safe interface where it can only be called with a field name * present in NameServiceMapping interface. * * Firebase SDKs providing services should extend NameServiceMapping interface to register * themselves. */ ComponentContainer.prototype.getProvider = function (name) { if (this.providers.has(name)) { return this.providers.get(name); } // create a Provider for a service that hasn't registered with Firebase var provider = new Provider(name, this); this.providers.set(name, provider); return provider; }; ComponentContainer.prototype.getProviders = function () { return Array.from(this.providers.values()); }; return ComponentContainer; }()); export { Component, ComponentContainer, Provider }; //# sourceMappingURL=index.esm5.js.map