461 lines
20 KiB
JavaScript
461 lines
20 KiB
JavaScript
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
|