UNPKG

@amo-tm/wsc

Version:

The amo WSC component of the amo JS SDK

658 lines (640 loc) 26.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var tslib = require('tslib'); var Deferred = /** @class */ (function () { function Deferred() { var _this = this; this.reject = function () { }; this.resolve = function () { }; this.promise = new Promise(function (resolve, reject) { _this.resolve = resolve; _this.reject = reject; }); } return Deferred; }()); var DEFAULT_INSTANCE_NAME = '[DEFAULT_INSTANCE_NAME]'; /** * Provider for instance for service name T, e.g. 'wsc', 'wsc-connector-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(); } Provider.prototype.get = function () { var normalizedIdentifier = this.normalizeInstanceIdentifier(); if (this.instances.has(normalizedIdentifier)) { return Promise.resolve(this.instances.get(normalizedIdentifier)); } if (!this.instancesDeferred.has(normalizedIdentifier)) { if (this.isInitialized(normalizedIdentifier) || this.shouldAutoInitialize()) { // initialize the service if it can be auto-initialized try { void this.getOrInitializeService({ instanceIdentifier: normalizedIdentifier, }); } 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. } } else { var deferred = new Deferred(); this.instancesDeferred.set(normalizedIdentifier, deferred); } } return this.instancesDeferred.get(normalizedIdentifier).promise; }; Provider.prototype.getImmediate = function (options) { var _a; var normalizedIdentifier = this.normalizeInstanceIdentifier(); 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 { var instanceOrInstancePromise = this.getOrInitializeService({ instanceIdentifier: normalizedIdentifier, }); if (instanceOrInstancePromise instanceof Promise) { if (optional) { return null; } else { throw Error("Service ".concat(this.name, " is not ready.")); } } return instanceOrInstancePromise; } 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 ".concat(this.name, " is not available.")); } } }; Provider.prototype.setComponent = function (component) { if (component.name !== this.name) { throw Error("Mismatching Component ".concat(component.name, " for Provider ").concat(this.name, ".")); } if (this.component) { throw Error("Component for ".concat(this.name, " has already been provided")); } this.component = component; // return early without attempting to initialize the component if (!this.shouldAutoInitialize()) { return; } // if the service is eager, initialize the default instance if (isComponentEager(component)) { try { void this.getOrInitializeService({ instanceIdentifier: DEFAULT_INSTANCE_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. } } }; Provider.prototype.isComponentSet = function () { return this.component !== null; }; Provider.prototype.isInitialized = function (identifier) { if (identifier === void 0) { identifier = DEFAULT_INSTANCE_NAME; } return this.instances.has(identifier); }; Provider.prototype.getOrInitializeService = function (_a) { var _this = this; var instanceIdentifier = _a.instanceIdentifier, _b = _a.options, options = _b === void 0 ? {} : _b; var instance = this.instances.get(instanceIdentifier); var instanceDeferred = this.instancesDeferred.get(instanceIdentifier); if (this.component && !(instance || instanceDeferred)) { var instanceOrInstancePromise = this.component.instanceFactory(this.container, { options: options, }); if (instanceOrInstancePromise instanceof Promise) { instanceDeferred = instanceDeferred || new Deferred(); this.instancesDeferred.set(instanceIdentifier, instanceDeferred); instanceOrInstancePromise.then(function (instance) { var currentInstanceDeferred = _this.instancesDeferred.get(instanceIdentifier); if (!currentInstanceDeferred || currentInstanceDeferred !== instanceDeferred) { return; } _this.instances.set(instanceIdentifier, instance); _this.instancesDeferred.delete(instanceIdentifier); currentInstanceDeferred.resolve(instance); }, instanceDeferred.reject); } else { instance = instanceOrInstancePromise; this.instances.set(instanceIdentifier, instance); } } return instance || (instanceDeferred === null || instanceDeferred === void 0 ? void 0 : instanceDeferred.promise) || null; }; Provider.prototype.normalizeInstanceIdentifier = function () { return DEFAULT_INSTANCE_NAME; }; Provider.prototype.shouldAutoInitialize = function () { return Boolean(this.component); }; return Provider; }()); function isComponentEager(component) { return component.instantiationMode === "EAGER" /* EAGER */; } /** * ComponentContainer that provides Providers for service name T, e.g. `wsc`, `wsc-connector-internal` */ var ComponentContainer = /** @class */ (function () { function ComponentContainer(name) { this.name = name; this.providers = new Map(); } /** * @param component Component being added * @description When a component with the same name has already been registered throw an exception */ ComponentContainer.prototype.addComponent = function (component) { var provider = this.getProvider(component.name); if (provider.isComponentSet()) { throw new Error("Component ".concat(component.name, " has already been registered with ").concat(this.name)); } provider.setComponent(component); }; /** * getProvider provides a type safe interface where it can only be called with a field name * present in NameServiceMapping interface. * * Amo 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 Amo var provider = new Provider(name, this); this.providers.set(name, provider); return provider; }; return ComponentContainer; }()); var _a; /** * The JS SDK supports 5 log levels and also allows a user the ability to * silence the logs altogether. * * The order is a follows: * DEBUG < VERBOSE < INFO < WARN < ERROR * * All of the log types above the current log level will be captured (i.e. if * you set the log level to `INFO`, errors will still be logged, but `DEBUG` and * `VERBOSE` logs will not) */ var LogLevel; (function (LogLevel) { LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG"; LogLevel[LogLevel["VERBOSE"] = 1] = "VERBOSE"; LogLevel[LogLevel["INFO"] = 2] = "INFO"; LogLevel[LogLevel["WARN"] = 3] = "WARN"; LogLevel[LogLevel["ERROR"] = 4] = "ERROR"; LogLevel[LogLevel["SILENT"] = 5] = "SILENT"; })(LogLevel || (LogLevel = {})); var levelStringToEnum = { debug: LogLevel.DEBUG, verbose: LogLevel.VERBOSE, info: LogLevel.INFO, warn: LogLevel.WARN, error: LogLevel.ERROR, silent: LogLevel.SILENT, }; /** * The default log level */ var defaultLogLevel = LogLevel.INFO; /** * By default, `console.debug` is not displayed in the developer console (in * chrome). To avoid forcing users to have to opt-in to these logs twice * (i.e. once for amo, and once in the console), we are sending `DEBUG` * logs to the `console.log` function. */ var ConsoleMethod = (_a = {}, _a[LogLevel.DEBUG] = 'log', _a[LogLevel.VERBOSE] = 'log', _a[LogLevel.INFO] = 'info', _a[LogLevel.WARN] = 'warn', _a[LogLevel.ERROR] = 'error', _a); /** * The default log handler will forward DEBUG, VERBOSE, INFO, WARN, and ERROR * messages on to their corresponding console counterparts (if the log method * is supported by the current log level) */ var defaultLogHandler = function (instance, logType) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } if (logType < instance.logLevel) { return; } var now = new Date().toISOString(); var method = ConsoleMethod[logType]; if (method) { console[method].apply(console, tslib.__spreadArray(["[".concat(now, "] ").concat(instance.name, ":")], tslib.__read(args), false)); } else { throw new Error("Attempted to log a message with an invalid logType (value: ".concat(logType, ")")); } }; var Logger = /** @class */ (function () { /** * Gives you an instance of a Logger to capture messages according to * Amo's logging scheme. * * @param name The name that the logs will be associated with */ function Logger(name) { this.name = name; /** * The log level of the given Logger instance. */ this._logLevel = defaultLogLevel; /** * The main (internal) log handler for the Logger instance. * Can be set to a new function in internal package code but not by user. */ this._logHandler = defaultLogHandler; } Object.defineProperty(Logger.prototype, "logLevel", { get: function () { return this._logLevel; }, set: function (val) { if (!(val in LogLevel)) { throw new TypeError("Invalid value \"".concat(val, "\" assigned to `logLevel`")); } this._logLevel = val; }, enumerable: false, configurable: true }); // Workaround for setter/getter having to be the same type. Logger.prototype.setLogLevel = function (val) { this._logLevel = typeof val === 'string' ? levelStringToEnum[val] : val; }; Object.defineProperty(Logger.prototype, "logHandler", { get: function () { return this._logHandler; }, set: function (val) { if (typeof val !== 'function') { throw new TypeError('Value assigned to `logHandler` must be a function'); } this._logHandler = val; }, enumerable: false, configurable: true }); /** * The functions below are all based on the `console` interface */ Logger.prototype.debug = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } this._logHandler.apply(this, tslib.__spreadArray([this, LogLevel.DEBUG], tslib.__read(args), false)); }; Logger.prototype.log = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } this._logHandler.apply(this, tslib.__spreadArray([this, LogLevel.VERBOSE], tslib.__read(args), false)); }; Logger.prototype.info = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } this._logHandler.apply(this, tslib.__spreadArray([this, LogLevel.INFO], tslib.__read(args), false)); }; Logger.prototype.warn = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } this._logHandler.apply(this, tslib.__spreadArray([this, LogLevel.WARN], tslib.__read(args), false)); }; Logger.prototype.error = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } this._logHandler.apply(this, tslib.__spreadArray([this, LogLevel.ERROR], tslib.__read(args), false)); }; return Logger; }()); // eslint-disable-next-line @typescript-eslint/no-explicit-any var instanceFactoriesDeferred = new Map(); var _registerRemoteInstanceFactory = function (instanceFactory) { var currentScript = document.currentScript; if (!currentScript || !(currentScript instanceof HTMLScriptElement)) { return; } var remoteInstanceUrl = currentScript.src; var instanceFactoryDeferred = instanceFactoriesDeferred.get(remoteInstanceUrl); if (!instanceFactoryDeferred) { return; } instanceFactoryDeferred.resolve(instanceFactory); }; var RemoteInstanceLoader = /** @class */ (function () { function RemoteInstanceLoader(url) { this.url = url; } RemoteInstanceLoader.prototype.getInstanceFactory = function () { var _this = this; return function (container, options) { var instanceFactoryDeferred = instanceFactoriesDeferred.get(_this.url); if (!instanceFactoryDeferred) { instanceFactoryDeferred = new Deferred(); instanceFactoriesDeferred.set(_this.url, instanceFactoryDeferred); _this.initializeRemoteScript(_this.url); } return instanceFactoryDeferred.promise.then(function (instanceFactory) { return instanceFactory(container, options); }); }; }; RemoteInstanceLoader.prototype.initializeRemoteScript = function (url) { var scriptElement = document.createElement('script'); scriptElement.type = 'text/javascript'; scriptElement.async = true; scriptElement.src = url; var anchorElement = document.getElementsByTagName('script')[0]; anchorElement.parentNode.insertBefore(scriptElement, anchorElement); }; return RemoteInstanceLoader; }()); /** * The default container name * * @internal */ var DEFAULT_CONTAINER_NAME = '[DEFAULT_CONTAINER_NAME]'; var container = new ComponentContainer(DEFAULT_CONTAINER_NAME); var logger = new Logger('app'); window.AmoJsSdk = tslib.__assign(tslib.__assign({}, (window.AmoJsSdk || {})), { _registerInstanceFactory: _registerRemoteInstanceFactory }); /** * Registered components. * * @internal */ // eslint-disable-next-line @typescript-eslint/no-explicit-any var _components = new Map(); /** * @param component - the component being added to the default container * @internal */ function _addComponent(component) { try { container.addComponent(component); } catch (e) { logger.debug("Component ".concat(component.name, " failed to register with ComponentContainer ").concat(container.name), e); } } /** * * @param component - the component to register * @returns whether or not the component is registered successfully * * @internal */ function _registerComponent(component) { var componentName = component.name; if (_components.has(componentName)) { logger.debug("There were multiple attempts to register component ".concat(componentName, ".")); return false; } _components.set(componentName, component); // add the component to existing container instances _addComponent(component); return true; } /** * @param name - service name * * @returns the provider for the service with the matching name * * @internal */ function _getProvider(name) { return container.getProvider(name); } /** * Component for service name T, e.g. `wsc` */ var Component = /** @class */ (function () { /** * * @param name The public service name, e.g. wsc, wsc-connector-internal * @param instanceFactory Service factory responsible for creating the public interface */ function Component(name, instanceFactory) { this.name = name; this.instanceFactory = instanceFactory; this.instantiationMode = "LAZY" /* LAZY */; } Component.prototype.setInstantiationMode = function (mode) { this.instantiationMode = mode; return this; }; return Component; }()); var WscService = /** @class */ (function () { function WscService(wscConnectorInnerProvider) { this.wscConnectorInnerProvider = wscConnectorInnerProvider; this.currentWscParams = null; this.iframeElementDeffered = null; this.connectingPromise = null; } WscService.prototype.updateParams = function (wscParams) { // Preload wscConnectorInner void this.wscConnectorInnerProvider.get(); this.currentWscParams = wscParams; this.connectingPromise = null; this.iframeElementDeffered = null; }; WscService.prototype.getCurrentWscParams = function () { return this.currentWscParams; }; WscService.prototype.connectIframe = function (wscParams) { return tslib.__awaiter(this, void 0, void 0, function () { var connectingPromise_1; var _this = this; return tslib.__generator(this, function (_a) { if (!this.connectingPromise) { connectingPromise_1 = new Promise(function (resolve) { return tslib.__awaiter(_this, void 0, void 0, function () { var _a, iframeElement, wscConnectorInner; return tslib.__generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, Promise.all([ this.getIframeElement(), this.wscConnectorInnerProvider.get(), ])]; case 1: _a = tslib.__read.apply(void 0, [_b.sent(), 2]), iframeElement = _a[0], wscConnectorInner = _a[1]; if (connectingPromise_1 !== this.connectingPromise) { return [2 /*return*/]; } if (iframeElement instanceof Error) { resolve(iframeElement); return [2 /*return*/]; } if (wscParams !== this.currentWscParams) { resolve(new Error('The Wsc was reinitialized with other params.')); return [2 /*return*/]; } return [4 /*yield*/, wscConnectorInner.initializeIframe(iframeElement, wscParams)]; case 2: _b.sent(); if (connectingPromise_1 !== this.connectingPromise) { return [2 /*return*/]; } resolve(); return [2 /*return*/]; } }); }); }); this.connectingPromise = connectingPromise_1; } return [2 /*return*/, this.connectingPromise]; }); }); }; WscService.prototype.getIframeElement = function () { return tslib.__awaiter(this, void 0, void 0, function () { var deffered_1; var _this = this; return tslib.__generator(this, function (_a) { if (!this.currentWscParams) { return [2 /*return*/, new Error('The Wsc should be initialized before.')]; } if (!this.iframeElementDeffered) { deffered_1 = new Deferred(); this.iframeElementDeffered = deffered_1; void this.wscConnectorInnerProvider .get() .then(function (wscConnectorInner) { return wscConnectorInner.createIframeElement(); }) .then(function (iframeElement) { if (deffered_1 === _this.iframeElementDeffered) { deffered_1.resolve(iframeElement); } }); } return [2 /*return*/, this.iframeElementDeffered.promise]; }); }); }; return WscService; }()); var WscFactory = function (container) { return new WscService(container.getProvider('wsc-connector-inner')); }; function registerWsc() { _registerComponent(new Component('wsc-connector-inner', new RemoteInstanceLoader('https://js.amo.tm/v1.3/wsc/connector.js').getInstanceFactory())); _registerComponent(new Component('wsc', WscFactory)); } var initializeWsc$1 = function (wscParams) { var wsc = _getProvider('wsc').getImmediate(); wsc.updateParams(wscParams); }; var mountWsc$1 = function (options) { return tslib.__awaiter(void 0, void 0, void 0, function () { var wsc, wscParams, containerElement, iframeElement, wscParamsAfterIframeElementGet, connectIframeResult; return tslib.__generator(this, function (_a) { switch (_a.label) { case 0: wsc = _getProvider('wsc').getImmediate(); wscParams = wsc.getCurrentWscParams(); if (!wscParams) { if (options.onError) { options.onError(new Error('The Wsc should be initialized before mount.')); } return [2 /*return*/]; } containerElement = null; if (options.container instanceof HTMLElement) { containerElement = options.container; } else { containerElement = document.querySelector(options.container); } if (!containerElement) { if (options.onError) { options.onError(new Error('The container element is not found.')); } return [2 /*return*/]; } return [4 /*yield*/, wsc.getIframeElement()]; case 1: iframeElement = _a.sent(); wscParamsAfterIframeElementGet = wsc.getCurrentWscParams(); if (wscParamsAfterIframeElementGet !== wscParams) { return [2 /*return*/]; } if (iframeElement instanceof Error) { if (options.onError) { options.onError(iframeElement); } return [2 /*return*/]; } containerElement.innerHTML = ''; containerElement.append(iframeElement); return [4 /*yield*/, wsc.connectIframe(wscParams)]; case 2: connectIframeResult = _a.sent(); if (connectIframeResult instanceof Error) { if (options.onError) { options.onError(connectIframeResult); } return [2 /*return*/]; } if (options.onSuccess) { options.onSuccess(); } return [2 /*return*/]; } }); }); }; var initializeWsc = function (wscParams) { initializeWsc$1(wscParams); }; var mountWsc = function (options) { void mountWsc$1(options); }; registerWsc(); exports.initializeWsc = initializeWsc; exports.mountWsc = mountWsc; //# sourceMappingURL=index.cjs.js.map