@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
155 lines (154 loc) • 6.5 kB
JavaScript
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;
;