UNPKG

@webex/webex-core

Version:

Plugin handling for Cisco Webex

219 lines (205 loc) • 8.45 kB
"use strict"; var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/define-property"); var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault"); _Object$defineProperty(exports, "__esModule", { value: true }); exports.persist = persist; exports.waitForValue = waitForValue; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/defineProperty")); var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/typeof")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/toConsumableArray")); var _apply = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/reflect/apply")); var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise")); var _map = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/map")); var _set = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/set")); var _lodash = require("lodash"); var _common = require("@webex/common"); var _errors = require("./errors"); /*! * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file. */ /* eslint no-invalid-this: [0] */ /** * Stores the result of fn before returning it * @param {string} key * @private * @returns {Promise} resolves with the result of fn */ function persist() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (args.length === 3) { return persist('@').apply(void 0, args); } var key = args[0], decider = args[1]; return function persistDecorator(target, prop, descriptor) { if (prop !== 'initialize') { // Once we have class-based alternative to AmpersandState, it should be // detected here. throw new TypeError('@persist can only currently be applied to AmpersandState objects or their derivatives and must be applied to the initialize method'); } descriptor.value = (0, _lodash.wrap)(descriptor.value, function persistExecutor(fn) { var _this = this; for (var _len2 = arguments.length, initializeArgs = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { initializeArgs[_key2 - 1] = arguments[_key2]; } var ret = (0, _apply.default)(fn, this, initializeArgs); var changeEvent = key === '@' ? 'change' : "change:".concat(key); // Some scenarios will lead to lots of change events on a single tick; we // really only care about writing once things have stopped changing. with // a debounce of zero, we're effectively coalescing all the changes // triggered by a single call to set() and commiting them on the next tick // eslint-disable-next-line no-invalid-this this.on(changeEvent, (0, _lodash.debounce)(function () { var shouldPersist = !decider || _apply.default.apply(Reflect, [decider, _this].concat(initializeArgs)); if (!shouldPersist) { return _promise.default.resolve(); } if (key === '@') { // eslint-disable-next-line no-invalid-this return _this.boundedStorage.put(key, _this); } // eslint-disable-next-line no-invalid-this return _this.boundedStorage.put(key, _this[key]); }, 0)); return ret; }); prepareInitialize(target, prop); }; } var M = _map.default; var S = _set.default; var BlockingKeyMap = (0, _common.make)(M, M, S); var blockingKeys = new BlockingKeyMap(); /** * Prevents fn from executing until key has been (attempted to be) loaded * @param {string} key * @param {Function} fn * @private * @returns {Promise} result of fn */ function waitForValue(key) { if (!key) { throw new Error('`key` is required'); } return function waitForValueDecorator(target, prop, descriptor) { blockingKeys.add(target, prop, key); descriptor.value = (0, _lodash.wrap)(descriptor.value, function waitForValueExecutor(fn) { var _this2 = this; for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { args[_key3 - 1] = arguments[_key3]; } var keys = blockingKeys.get(target, prop); return _promise.default.all((0, _toConsumableArray2.default)(keys).map(function (k) { return _this2.boundedStorage.waitFor(k); })).then(function () { return (0, _apply.default)(fn, _this2, args); }); }); // This *should* make decorators compatible with AmpersandState class // definitions if ((0, _typeof2.default)(target) === 'object' && !target.prototype) { target[prop] = descriptor.value; } prepareInitialize(target, prop); return descriptor; }; } var inited = new _set.default(); /** * finds a means of identitying the `target` param passed to * `prepareInitialize()`. When possible, avoids duplicate `init()` calls if * namespaces collide * * @param {Object|Constructor} target * @private * @returns {String|Constructor} */ function identifyTarget(target) { if (target.namespace) { return target.namespace; } return target; } var stack = new _set.default(); /** * @param {Function} target * @param {string} prop * @private * @returns {undefined} */ function prepareInitialize(target, prop) { var id = identifyTarget(target); if (!inited.has(id)) { inited.add(id); if (target.initialize) { target.initialize = (0, _lodash.wrap)(target.initialize, function applyInit(fn) { for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { args[_key4 - 1] = arguments[_key4]; } var ret = (0, _apply.default)(fn, this, args); (0, _apply.default)(init, this, args); return ret; }); return; } target.initialize = init; } /** * @private * @returns {undefined} */ function init() { var self = this; var namespace = this.getNamespace(); this.webex.initialize = (0, _lodash.wrap)(this.webex.initialize || _lodash.identity, function applyInit(fn) { var _this3 = this; // Call webex's initalize method first // Reminder: in order for MockWebex to accept initial storage data, the // wrapped initialize() must be invoked before attempting to load data. // Reminder: context here is `webex`, not `self`. stack.add(namespace); for (var _len5 = arguments.length, args = new Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { args[_key5 - 1] = arguments[_key5]; } (0, _apply.default)(fn, this, args); // Then prepare a function for setting values retrieved from storage var set = (0, _lodash.curry)(function (key, value) { _this3.logger.debug("storage:(".concat(namespace, "): got `").concat(key, "` for first time")); if (key === '@') { self.parent.set((0, _defineProperty2.default)({}, namespace.toLowerCase(), value)); } else if ((0, _lodash.result)(self[key], 'isState')) { self[key].set(value); } else { self.set(key, value); } _this3.logger.debug("storage:(".concat(namespace, "): set `").concat(key, "` for first time")); }); // And prepare an error handler for when those keys can't be found var handle = (0, _lodash.curry)(function (key, reason) { if (reason instanceof _errors.NotFoundError || process.env.NODE_ENV !== 'production' && reason.toString().includes('MockNotFoundError')) { _this3.logger.debug("storage(".concat(namespace, "): no data for `").concat(key, "`, continuing")); return _promise.default.resolve(); } _this3.logger.error("storage(".concat(namespace, "): failed to init `").concat(key, "`"), reason); return _promise.default.reject(reason); }); // Iterate over the list of keys marked as blocking via `@waitForValue` var keys = blockingKeys.get(target, prop); var promises = []; keys.forEach(function (key) { promises.push(_this3.boundedStorage.get(namespace, key).then(set(key)).catch(handle(key))); }); _promise.default.all(promises).then(function () { stack.delete(namespace); if (stack.size === 0) { _this3.loaded = true; } }); }); } } //# sourceMappingURL=decorators.js.map