UNPKG

@splitsoftware/splitio-commons

Version:
155 lines (154 loc) 6.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readinessManagerFactory = void 0; var objectAssign_1 = require("../utils/lang/objectAssign"); var constants_1 = require("./constants"); var constants_2 = require("../utils/constants"); function splitsEventEmitterFactory(EventEmitter) { var splitsEventEmitter = (0, objectAssign_1.objectAssign)(new EventEmitter(), { splitsArrived: false, splitsCacheLoaded: false, hasInit: false, initCallbacks: [] }); // `isSplitKill` condition avoids an edge-case of wrongly emitting SDK_READY if: // - `/memberships` fetch and SPLIT_KILL occurs before `/splitChanges` fetch, and // - storage has cached splits (for which case `splitsStorage.killLocally` can return true) splitsEventEmitter.on(constants_1.SDK_SPLITS_ARRIVED, function (isSplitKill) { if (!isSplitKill) splitsEventEmitter.splitsArrived = true; }); splitsEventEmitter.once(constants_1.SDK_SPLITS_CACHE_LOADED, function () { splitsEventEmitter.splitsCacheLoaded = true; }); return splitsEventEmitter; } function segmentsEventEmitterFactory(EventEmitter) { var segmentsEventEmitter = (0, objectAssign_1.objectAssign)(new EventEmitter(), { segmentsArrived: false }); segmentsEventEmitter.once(constants_1.SDK_SEGMENTS_ARRIVED, function () { segmentsEventEmitter.segmentsArrived = true; }); return segmentsEventEmitter; } /** * Factory of readiness manager, which handles the ready / update event propagation. */ function readinessManagerFactory(EventEmitter, settings, splits, isShared) { if (splits === void 0) { splits = splitsEventEmitterFactory(EventEmitter); } var readyTimeout = settings.startup.readyTimeout; var segments = segmentsEventEmitterFactory(EventEmitter); var gate = new EventEmitter(); var lastUpdate = 0; function syncLastUpdate() { var dateNow = Date.now(); // ensure lastUpdate is always increasing per event, is case Date.now() is mocked or its value is the same lastUpdate = dateNow > lastUpdate ? dateNow : lastUpdate + 1; } // emit SDK_READY_FROM_CACHE var isReadyFromCache = false; if (splits.splitsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE else splits.once(constants_1.SDK_SPLITS_CACHE_LOADED, checkIsReadyFromCache); // emit SDK_READY_TIMED_OUT var hasTimedout = false; function timeout() { if (hasTimedout || isReady) return; hasTimedout = true; syncLastUpdate(); gate.emit(constants_1.SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.'); } // emit SDK_READY and SDK_UPDATE var isReady = false; splits.on(constants_1.SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate); segments.on(constants_1.SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate); var isDestroyed = false; var readyTimeoutId; function __init() { isDestroyed = false; if (readyTimeout > 0 && !isReady) readyTimeoutId = setTimeout(timeout, readyTimeout); } splits.initCallbacks.push(__init); if (splits.hasInit) __init(); function checkIsReadyFromCache() { isReadyFromCache = true; // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted if (!isReady && !isDestroyed) { try { syncLastUpdate(); gate.emit(constants_1.SDK_READY_FROM_CACHE); } catch (e) { // throws user callback exceptions in next tick setTimeout(function () { throw e; }, 0); } } } function checkIsReadyOrUpdate(diff) { var _a; if (isDestroyed) return; if (isReady) { try { syncLastUpdate(); gate.emit(constants_1.SDK_UPDATE, diff); } catch (e) { // throws user callback exceptions in next tick setTimeout(function () { throw e; }, 0); } } else { if (splits.splitsArrived && segments.segmentsArrived) { clearTimeout(readyTimeoutId); isReady = true; try { syncLastUpdate(); if (!isReadyFromCache && ((_a = settings.storage) === null || _a === void 0 ? void 0 : _a.type) === constants_2.STORAGE_LOCALSTORAGE) { isReadyFromCache = true; gate.emit(constants_1.SDK_READY_FROM_CACHE); } gate.emit(constants_1.SDK_READY); } catch (e) { // throws user callback exceptions in next tick setTimeout(function () { throw e; }, 0); } } } } return { splits: splits, segments: segments, gate: gate, shared: function () { return readinessManagerFactory(EventEmitter, settings, splits, true); }, // @TODO review/remove next methods when non-recoverable errors are reworked // Called on consumer mode, when storage fails to connect timeout: timeout, // Called on 403 error (client-side SDK key on server-side), to set the SDK as destroyed for // tracking and evaluations, while keeping event listeners to emit SDK_READY_TIMED_OUT event setDestroyed: function () { isDestroyed = true; }, init: function () { if (splits.hasInit) return; splits.hasInit = true; splits.initCallbacks.forEach(function (cb) { return cb(); }); }, destroy: function () { isDestroyed = true; syncLastUpdate(); clearTimeout(readyTimeoutId); if (!isShared) splits.hasInit = false; }, isReady: function () { return isReady; }, isReadyFromCache: function () { return isReadyFromCache; }, isTimedout: function () { return hasTimedout && !isReady; }, hasTimedout: function () { return hasTimedout; }, isDestroyed: function () { return isDestroyed; }, isOperational: function () { return (isReady || isReadyFromCache) && !isDestroyed; }, lastUpdate: function () { return lastUpdate; } }; } exports.readinessManagerFactory = readinessManagerFactory;