@splitsoftware/splitio-commons 2.10.2-rc.3 → 2.10.2-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +1 -1
- package/cjs/readiness/constants.js +4 -1
- package/cjs/readiness/readinessManager.js +13 -13
- package/cjs/sdkFactory/index.js +3 -3
- package/cjs/storages/inLocalStorage/index.js +4 -1
- package/cjs/storages/inLocalStorage/validateCache.js +13 -3
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
- package/cjs/sync/polling/types.js +0 -15
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +1 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +6 -6
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +6 -6
- package/cjs/sync/syncManagerOnline.js +4 -3
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/readiness/constants.js +3 -0
- package/esm/readiness/readinessManager.js +13 -13
- package/esm/sdkFactory/index.js +3 -3
- package/esm/storages/inLocalStorage/index.js +4 -1
- package/esm/storages/inLocalStorage/validateCache.js +13 -3
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +7 -7
- package/esm/sync/polling/types.js +1 -14
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -3
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +7 -7
- package/esm/sync/polling/updaters/splitChangesUpdater.js +7 -7
- package/esm/sync/syncManagerOnline.js +4 -3
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/readiness/constants.ts +4 -0
- package/src/readiness/readinessManager.ts +14 -13
- package/src/sdkFactory/index.ts +3 -3
- package/src/storages/inLocalStorage/index.ts +6 -3
- package/src/storages/inLocalStorage/validateCache.ts +20 -4
- package/src/storages/types.ts +1 -1
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +7 -6
- package/src/sync/polling/types.ts +0 -14
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -3
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +8 -7
- package/src/sync/polling/updaters/splitChangesUpdater.ts +8 -7
- package/src/sync/syncManagerOnline.ts +4 -2
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +1 -1
package/CHANGES.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
2.11.0 (January XX, 2026)
|
|
2
2
|
- Added metadata to SDK_UPDATE events to indicate the type of update (FLAGS_UPDATE or SEGMENTS_UPDATE) and the names of updated flags or segments.
|
|
3
|
-
- Added metadata to SDK_READY and SDK_READY_FROM_CACHE events, including `initialCacheLoad` (boolean
|
|
3
|
+
- Added metadata to SDK_READY and SDK_READY_FROM_CACHE events, including `initialCacheLoad` (boolean: `true` for fresh install/first app launch, `false` for warm cache/second app launch) and `lastUpdateTimestamp` (Int64 milliseconds since epoch).
|
|
4
4
|
|
|
5
5
|
2.10.1 (December 18, 2025)
|
|
6
6
|
- Bugfix - Handle `null` prerequisites properly.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SDK_UPDATE = exports.SDK_READY_FROM_CACHE = exports.SDK_READY = exports.SDK_READY_TIMED_OUT = exports.SDK_SEGMENTS_ARRIVED = exports.SDK_SPLITS_CACHE_LOADED = exports.SDK_SPLITS_ARRIVED = void 0;
|
|
3
|
+
exports.SEGMENTS_UPDATE = exports.FLAGS_UPDATE = exports.SDK_UPDATE = exports.SDK_READY_FROM_CACHE = exports.SDK_READY = exports.SDK_READY_TIMED_OUT = exports.SDK_SEGMENTS_ARRIVED = exports.SDK_SPLITS_CACHE_LOADED = exports.SDK_SPLITS_ARRIVED = void 0;
|
|
4
4
|
// Splits events:
|
|
5
5
|
exports.SDK_SPLITS_ARRIVED = 'state::splits-arrived';
|
|
6
6
|
exports.SDK_SPLITS_CACHE_LOADED = 'state::splits-cache-loaded';
|
|
@@ -11,3 +11,6 @@ exports.SDK_READY_TIMED_OUT = 'init::timeout';
|
|
|
11
11
|
exports.SDK_READY = 'init::ready';
|
|
12
12
|
exports.SDK_READY_FROM_CACHE = 'init::cache-ready';
|
|
13
13
|
exports.SDK_UPDATE = 'state::update';
|
|
14
|
+
// SdkUpdateMetadata types:
|
|
15
|
+
exports.FLAGS_UPDATE = 'FLAGS_UPDATE';
|
|
16
|
+
exports.SEGMENTS_UPDATE = 'SEGMENTS_UPDATE';
|
|
@@ -41,6 +41,7 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
|
|
|
41
41
|
}
|
|
42
42
|
// emit SDK_READY_FROM_CACHE
|
|
43
43
|
var isReadyFromCache = false;
|
|
44
|
+
var cacheLastUpdateTimestamp = null;
|
|
44
45
|
if (splits.splitsCacheLoaded)
|
|
45
46
|
isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
|
|
46
47
|
else
|
|
@@ -68,17 +69,17 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
|
|
|
68
69
|
splits.initCallbacks.push(__init);
|
|
69
70
|
if (splits.hasInit)
|
|
70
71
|
__init();
|
|
71
|
-
function checkIsReadyFromCache() {
|
|
72
|
+
function checkIsReadyFromCache(cacheMetadata) {
|
|
72
73
|
isReadyFromCache = true;
|
|
74
|
+
cacheLastUpdateTimestamp = cacheMetadata.lastUpdateTimestamp;
|
|
73
75
|
// Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
|
|
74
76
|
if (!isReady && !isDestroyed) {
|
|
75
77
|
try {
|
|
76
78
|
syncLastUpdate();
|
|
77
|
-
|
|
78
|
-
initialCacheLoad:
|
|
79
|
-
lastUpdateTimestamp:
|
|
80
|
-
};
|
|
81
|
-
gate.emit(constants_1.SDK_READY_FROM_CACHE, metadata);
|
|
79
|
+
gate.emit(constants_1.SDK_READY_FROM_CACHE, {
|
|
80
|
+
initialCacheLoad: !cacheMetadata.isCacheValid,
|
|
81
|
+
lastUpdateTimestamp: cacheLastUpdateTimestamp
|
|
82
|
+
});
|
|
82
83
|
}
|
|
83
84
|
catch (e) {
|
|
84
85
|
// throws user callback exceptions in next tick
|
|
@@ -104,19 +105,18 @@ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
|
|
|
104
105
|
clearTimeout(readyTimeoutId);
|
|
105
106
|
isReady = true;
|
|
106
107
|
try {
|
|
107
|
-
syncLastUpdate();
|
|
108
108
|
var wasReadyFromCache = isReadyFromCache;
|
|
109
109
|
if (!isReadyFromCache) {
|
|
110
110
|
isReadyFromCache = true;
|
|
111
|
-
var
|
|
112
|
-
initialCacheLoad:
|
|
113
|
-
lastUpdateTimestamp:
|
|
111
|
+
var metadataReadyFromCache = {
|
|
112
|
+
initialCacheLoad: true,
|
|
113
|
+
lastUpdateTimestamp: null // No cache timestamp when fresh install
|
|
114
114
|
};
|
|
115
|
-
gate.emit(constants_1.SDK_READY_FROM_CACHE,
|
|
115
|
+
gate.emit(constants_1.SDK_READY_FROM_CACHE, metadataReadyFromCache);
|
|
116
116
|
}
|
|
117
117
|
var metadataReady = {
|
|
118
|
-
initialCacheLoad: wasReadyFromCache,
|
|
119
|
-
lastUpdateTimestamp:
|
|
118
|
+
initialCacheLoad: !wasReadyFromCache,
|
|
119
|
+
lastUpdateTimestamp: wasReadyFromCache ? cacheLastUpdateTimestamp : null
|
|
120
120
|
};
|
|
121
121
|
gate.emit(constants_1.SDK_READY, metadataReady);
|
|
122
122
|
}
|
package/cjs/sdkFactory/index.js
CHANGED
|
@@ -46,17 +46,17 @@ function sdkFactory(params) {
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED);
|
|
49
|
-
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
|
|
49
|
+
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
50
50
|
},
|
|
51
51
|
onReadyFromCacheCb: function () {
|
|
52
|
-
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
52
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
var fallbackTreatmentsCalculator = new fallbackTreatmentsCalculator_1.FallbackTreatmentsCalculator(settings.fallbackTreatments);
|
|
56
56
|
if (initialRolloutPlan) {
|
|
57
57
|
(0, setRolloutPlan_1.setRolloutPlan)(log, initialRolloutPlan, storage, key && (0, key_1.getMatching)(key));
|
|
58
58
|
if (storage.splits.getChangeNumber() > -1)
|
|
59
|
-
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
59
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
60
60
|
}
|
|
61
61
|
var clients = {};
|
|
62
62
|
var telemetryTracker = (0, telemetryTracker_1.telemetryTrackerFactory)(storage.telemetry, platform.now);
|
|
@@ -60,7 +60,10 @@ function InLocalStorage(options) {
|
|
|
60
60
|
telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
|
|
61
61
|
uniqueKeys: new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS(),
|
|
62
62
|
validateCache: function () {
|
|
63
|
-
|
|
63
|
+
if (!validateCachePromise) {
|
|
64
|
+
validateCachePromise = (0, validateCache_1.validateCache)(options, storage, settings, keys, splits, rbSegments, segments, largeSegments);
|
|
65
|
+
}
|
|
66
|
+
return validateCachePromise;
|
|
64
67
|
},
|
|
65
68
|
save: function () {
|
|
66
69
|
return storage.save && storage.save();
|
|
@@ -55,12 +55,16 @@ function validateExpiration(options, storage, settings, keys, currentTimestamp,
|
|
|
55
55
|
* - its hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
|
|
56
56
|
* - `clearOnInit` was set and cache was not cleared in the last 24 hours
|
|
57
57
|
*
|
|
58
|
-
* @returns `
|
|
58
|
+
* @returns Metadata object with `isCacheValid` (true if cache is ready to be used, false otherwise) and `lastUpdateTimestamp` (timestamp of last cache update or null)
|
|
59
59
|
*/
|
|
60
60
|
function validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments) {
|
|
61
61
|
return Promise.resolve(storage.load && storage.load()).then(function () {
|
|
62
62
|
var currentTimestamp = Date.now();
|
|
63
63
|
var isThereCache = splits.getChangeNumber() > -1;
|
|
64
|
+
// Get lastUpdateTimestamp from storage
|
|
65
|
+
var lastUpdatedTimestampStr = storage.getItem(keys.buildLastUpdatedKey());
|
|
66
|
+
var lastUpdatedTimestamp = lastUpdatedTimestampStr ? parseInt(lastUpdatedTimestampStr, 10) : null;
|
|
67
|
+
var lastUpdateTimestamp = (!(0, lang_1.isNaNNumber)(lastUpdatedTimestamp) && lastUpdatedTimestamp !== null) ? lastUpdatedTimestamp : null;
|
|
64
68
|
if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
|
|
65
69
|
splits.clear();
|
|
66
70
|
rbSegments.clear();
|
|
@@ -76,10 +80,16 @@ function validateCache(options, storage, settings, keys, splits, rbSegments, seg
|
|
|
76
80
|
// Persist clear
|
|
77
81
|
if (storage.save)
|
|
78
82
|
storage.save();
|
|
79
|
-
return
|
|
83
|
+
return {
|
|
84
|
+
isCacheValid: false,
|
|
85
|
+
lastUpdateTimestamp: null
|
|
86
|
+
};
|
|
80
87
|
}
|
|
81
88
|
// Check if ready from cache
|
|
82
|
-
return
|
|
89
|
+
return {
|
|
90
|
+
isCacheValid: isThereCache,
|
|
91
|
+
lastUpdateTimestamp: lastUpdateTimestamp
|
|
92
|
+
};
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
95
|
exports.validateCache = validateCache;
|
|
@@ -6,7 +6,6 @@ var syncTask_1 = require("../../syncTask");
|
|
|
6
6
|
var constants_1 = require("../../../utils/constants");
|
|
7
7
|
var constants_2 = require("../../../readiness/constants");
|
|
8
8
|
var constants_3 = require("../../../logger/constants");
|
|
9
|
-
var types_1 = require("../../polling/types");
|
|
10
9
|
/**
|
|
11
10
|
* Offline equivalent of `splitChangesUpdaterFactory`
|
|
12
11
|
*/
|
|
@@ -43,15 +42,16 @@ function fromObjectUpdaterFactory(splitsParser, storage, readiness, settings) {
|
|
|
43
42
|
splitsCache.clear(),
|
|
44
43
|
splitsCache.update(splits, [], Date.now())
|
|
45
44
|
]).then(function () {
|
|
46
|
-
readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED, { type:
|
|
45
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED, { type: constants_2.FLAGS_UPDATE, names: [] });
|
|
47
46
|
if (startingUp) {
|
|
48
47
|
startingUp = false;
|
|
49
|
-
Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (
|
|
48
|
+
Promise.resolve(storage.validateCache ? storage.validateCache() : { isCacheValid: false, lastUpdateTimestamp: null }).then(function (cacheMetadata) {
|
|
50
49
|
// Emits SDK_READY_FROM_CACHE
|
|
51
|
-
if (
|
|
52
|
-
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
|
|
50
|
+
if (cacheMetadata.isCacheValid) {
|
|
51
|
+
readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED, cacheMetadata);
|
|
52
|
+
}
|
|
53
53
|
// Emits SDK_READY
|
|
54
|
-
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED, { type:
|
|
54
|
+
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED, { type: constants_2.SEGMENTS_UPDATE, names: [] });
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
return true;
|
|
@@ -1,17 +1,2 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SdkUpdateMetadataKeys = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Metadata keys for SDK update events.
|
|
6
|
-
*/
|
|
7
|
-
var SdkUpdateMetadataKeys;
|
|
8
|
-
(function (SdkUpdateMetadataKeys) {
|
|
9
|
-
/**
|
|
10
|
-
* The update event emitted when the SDK cache is updated with new data for flags.
|
|
11
|
-
*/
|
|
12
|
-
SdkUpdateMetadataKeys["FLAGS_UPDATE"] = "FLAGS_UPDATE";
|
|
13
|
-
/**
|
|
14
|
-
* The update event emitted when the SDK cache is updated with new data for segments.
|
|
15
|
-
*/
|
|
16
|
-
SdkUpdateMetadataKeys["SEGMENTS_UPDATE"] = "SEGMENTS_UPDATE";
|
|
17
|
-
})(SdkUpdateMetadataKeys = exports.SdkUpdateMetadataKeys || (exports.SdkUpdateMetadataKeys = {}));
|
|
@@ -6,7 +6,6 @@ var constants_1 = require("../../../readiness/constants");
|
|
|
6
6
|
var constants_2 = require("../../../logger/constants");
|
|
7
7
|
var constants_3 = require("../../streaming/constants");
|
|
8
8
|
var AbstractSplitsCacheSync_1 = require("../../../storages/AbstractSplitsCacheSync");
|
|
9
|
-
var types_1 = require("../types");
|
|
10
9
|
/**
|
|
11
10
|
* factory of MySegments updater, a task that:
|
|
12
11
|
* - fetches mySegments using `mySegmentsFetcher`
|
|
@@ -40,7 +39,7 @@ function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmentsEvent
|
|
|
40
39
|
// Notify update if required
|
|
41
40
|
if ((0, AbstractSplitsCacheSync_1.usesSegmentsSync)(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
42
41
|
readyOnAlreadyExistentState = false;
|
|
43
|
-
segmentsEventEmitter.emit(constants_1.SDK_SEGMENTS_ARRIVED, { type:
|
|
42
|
+
segmentsEventEmitter.emit(constants_1.SDK_SEGMENTS_ARRIVED, { type: constants_1.SEGMENTS_UPDATE, names: [] });
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
function _mySegmentsUpdater(retry, segmentsData, noCache, till) {
|
|
@@ -4,7 +4,6 @@ exports.segmentChangesUpdaterFactory = void 0;
|
|
|
4
4
|
var constants_1 = require("../../../readiness/constants");
|
|
5
5
|
var constants_2 = require("../../../logger/constants");
|
|
6
6
|
var timeout_1 = require("../../../utils/promise/timeout");
|
|
7
|
-
var types_1 = require("../types");
|
|
8
7
|
/**
|
|
9
8
|
* Factory of SegmentChanges updater, a task that:
|
|
10
9
|
* - fetches segment changes using `segmentChangesFetcher`
|
|
@@ -67,12 +66,13 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
|
|
|
67
66
|
// if at least one segment fetch succeeded, mark segments ready
|
|
68
67
|
if (shouldUpdateFlags.some(function (update) { return update; }) || readyOnAlreadyExistentState) {
|
|
69
68
|
readyOnAlreadyExistentState = false;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
if (readiness) {
|
|
70
|
+
var metadata = {
|
|
71
|
+
type: constants_1.SEGMENTS_UPDATE,
|
|
72
|
+
names: []
|
|
73
|
+
};
|
|
75
74
|
readiness.segments.emit(constants_1.SDK_SEGMENTS_ARRIVED, metadata);
|
|
75
|
+
}
|
|
76
76
|
}
|
|
77
77
|
return true;
|
|
78
78
|
});
|
|
@@ -8,7 +8,6 @@ var lang_1 = require("../../../utils/lang");
|
|
|
8
8
|
var constants_3 = require("../../../utils/constants");
|
|
9
9
|
var sets_1 = require("../../../utils/lang/sets");
|
|
10
10
|
var constants_4 = require("../../streaming/constants");
|
|
11
|
-
var types_1 = require("../types");
|
|
12
11
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
13
12
|
// Returns a promise that could be rejected.
|
|
14
13
|
// @TODO review together with Segments and MySegments storage APIs
|
|
@@ -165,13 +164,14 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, splitFilt
|
|
|
165
164
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
166
165
|
.catch(function () { return false; } /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
167
166
|
.then(function (emitSplitsArrivedEvent) {
|
|
168
|
-
var metadata = {
|
|
169
|
-
type: updatedFlags.length > 0 ? types_1.SdkUpdateMetadataKeys.FLAGS_UPDATE : types_1.SdkUpdateMetadataKeys.SEGMENTS_UPDATE,
|
|
170
|
-
names: updatedFlags.length > 0 ? updatedFlags : []
|
|
171
|
-
};
|
|
172
167
|
// emit SDK events
|
|
173
|
-
if (emitSplitsArrivedEvent)
|
|
168
|
+
if (emitSplitsArrivedEvent) {
|
|
169
|
+
var metadata = {
|
|
170
|
+
type: updatedFlags.length > 0 ? constants_1.FLAGS_UPDATE : constants_1.SEGMENTS_UPDATE,
|
|
171
|
+
names: updatedFlags.length > 0 ? updatedFlags : []
|
|
172
|
+
};
|
|
174
173
|
splitsEventEmitter.emit(constants_1.SDK_SPLITS_ARRIVED, metadata);
|
|
174
|
+
}
|
|
175
175
|
return true;
|
|
176
176
|
});
|
|
177
177
|
}
|
|
@@ -71,13 +71,14 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
|
71
71
|
running = true;
|
|
72
72
|
// @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
|
|
73
73
|
submitterManager.start(!(0, consent_1.isConsentGranted)(settings));
|
|
74
|
-
return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (
|
|
74
|
+
return Promise.resolve(storage.validateCache ? storage.validateCache() : { isCacheValid: false, lastUpdateTimestamp: null }).then(function (cacheMetadata) {
|
|
75
75
|
if (!running)
|
|
76
76
|
return;
|
|
77
77
|
if (startFirstTime) {
|
|
78
78
|
// Emits SDK_READY_FROM_CACHE
|
|
79
|
-
if (
|
|
80
|
-
readiness.splits.emit(constants_4.SDK_SPLITS_CACHE_LOADED);
|
|
79
|
+
if (cacheMetadata.isCacheValid) {
|
|
80
|
+
readiness.splits.emit(constants_4.SDK_SPLITS_CACHE_LOADED, cacheMetadata);
|
|
81
|
+
}
|
|
81
82
|
}
|
|
82
83
|
// start syncing splits and segments
|
|
83
84
|
if (pollingManager) {
|
|
@@ -6,7 +6,7 @@ var constants_1 = require("../../../logger/constants");
|
|
|
6
6
|
var constants_2 = require("../../../utils/constants");
|
|
7
7
|
function __InLocalStorageMockFactory(params) {
|
|
8
8
|
var result = (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
|
|
9
|
-
result.validateCache = function () { return Promise.resolve(true); }; // to emit SDK_READY_FROM_CACHE
|
|
9
|
+
result.validateCache = function () { return Promise.resolve({ isCacheValid: true, lastUpdateTimestamp: null }); }; // to emit SDK_READY_FROM_CACHE
|
|
10
10
|
return result;
|
|
11
11
|
}
|
|
12
12
|
exports.__InLocalStorageMockFactory = __InLocalStorageMockFactory;
|
|
@@ -8,3 +8,6 @@ export var SDK_READY_TIMED_OUT = 'init::timeout';
|
|
|
8
8
|
export var SDK_READY = 'init::ready';
|
|
9
9
|
export var SDK_READY_FROM_CACHE = 'init::cache-ready';
|
|
10
10
|
export var SDK_UPDATE = 'state::update';
|
|
11
|
+
// SdkUpdateMetadata types:
|
|
12
|
+
export var FLAGS_UPDATE = 'FLAGS_UPDATE';
|
|
13
|
+
export var SEGMENTS_UPDATE = 'SEGMENTS_UPDATE';
|
|
@@ -38,6 +38,7 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
|
|
|
38
38
|
}
|
|
39
39
|
// emit SDK_READY_FROM_CACHE
|
|
40
40
|
var isReadyFromCache = false;
|
|
41
|
+
var cacheLastUpdateTimestamp = null;
|
|
41
42
|
if (splits.splitsCacheLoaded)
|
|
42
43
|
isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
|
|
43
44
|
else
|
|
@@ -65,17 +66,17 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
|
|
|
65
66
|
splits.initCallbacks.push(__init);
|
|
66
67
|
if (splits.hasInit)
|
|
67
68
|
__init();
|
|
68
|
-
function checkIsReadyFromCache() {
|
|
69
|
+
function checkIsReadyFromCache(cacheMetadata) {
|
|
69
70
|
isReadyFromCache = true;
|
|
71
|
+
cacheLastUpdateTimestamp = cacheMetadata.lastUpdateTimestamp;
|
|
70
72
|
// Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
|
|
71
73
|
if (!isReady && !isDestroyed) {
|
|
72
74
|
try {
|
|
73
75
|
syncLastUpdate();
|
|
74
|
-
|
|
75
|
-
initialCacheLoad:
|
|
76
|
-
lastUpdateTimestamp:
|
|
77
|
-
};
|
|
78
|
-
gate.emit(SDK_READY_FROM_CACHE, metadata);
|
|
76
|
+
gate.emit(SDK_READY_FROM_CACHE, {
|
|
77
|
+
initialCacheLoad: !cacheMetadata.isCacheValid,
|
|
78
|
+
lastUpdateTimestamp: cacheLastUpdateTimestamp
|
|
79
|
+
});
|
|
79
80
|
}
|
|
80
81
|
catch (e) {
|
|
81
82
|
// throws user callback exceptions in next tick
|
|
@@ -101,19 +102,18 @@ export function readinessManagerFactory(EventEmitter, settings, splits, isShared
|
|
|
101
102
|
clearTimeout(readyTimeoutId);
|
|
102
103
|
isReady = true;
|
|
103
104
|
try {
|
|
104
|
-
syncLastUpdate();
|
|
105
105
|
var wasReadyFromCache = isReadyFromCache;
|
|
106
106
|
if (!isReadyFromCache) {
|
|
107
107
|
isReadyFromCache = true;
|
|
108
|
-
var
|
|
109
|
-
initialCacheLoad:
|
|
110
|
-
lastUpdateTimestamp:
|
|
108
|
+
var metadataReadyFromCache = {
|
|
109
|
+
initialCacheLoad: true,
|
|
110
|
+
lastUpdateTimestamp: null // No cache timestamp when fresh install
|
|
111
111
|
};
|
|
112
|
-
gate.emit(SDK_READY_FROM_CACHE,
|
|
112
|
+
gate.emit(SDK_READY_FROM_CACHE, metadataReadyFromCache);
|
|
113
113
|
}
|
|
114
114
|
var metadataReady = {
|
|
115
|
-
initialCacheLoad: wasReadyFromCache,
|
|
116
|
-
lastUpdateTimestamp:
|
|
115
|
+
initialCacheLoad: !wasReadyFromCache,
|
|
116
|
+
lastUpdateTimestamp: wasReadyFromCache ? cacheLastUpdateTimestamp : null
|
|
117
117
|
};
|
|
118
118
|
gate.emit(SDK_READY, metadataReady);
|
|
119
119
|
}
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -43,17 +43,17 @@ export function sdkFactory(params) {
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
46
|
-
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
46
|
+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
47
47
|
},
|
|
48
48
|
onReadyFromCacheCb: function () {
|
|
49
|
-
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
49
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
50
50
|
}
|
|
51
51
|
});
|
|
52
52
|
var fallbackTreatmentsCalculator = new FallbackTreatmentsCalculator(settings.fallbackTreatments);
|
|
53
53
|
if (initialRolloutPlan) {
|
|
54
54
|
setRolloutPlan(log, initialRolloutPlan, storage, key && getMatching(key));
|
|
55
55
|
if (storage.splits.getChangeNumber() > -1)
|
|
56
|
-
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
56
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
57
57
|
}
|
|
58
58
|
var clients = {};
|
|
59
59
|
var telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
@@ -57,7 +57,10 @@ export function InLocalStorage(options) {
|
|
|
57
57
|
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
|
|
58
58
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
59
59
|
validateCache: function () {
|
|
60
|
-
|
|
60
|
+
if (!validateCachePromise) {
|
|
61
|
+
validateCachePromise = validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments);
|
|
62
|
+
}
|
|
63
|
+
return validateCachePromise;
|
|
61
64
|
},
|
|
62
65
|
save: function () {
|
|
63
66
|
return storage.save && storage.save();
|
|
@@ -52,12 +52,16 @@ function validateExpiration(options, storage, settings, keys, currentTimestamp,
|
|
|
52
52
|
* - its hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
|
|
53
53
|
* - `clearOnInit` was set and cache was not cleared in the last 24 hours
|
|
54
54
|
*
|
|
55
|
-
* @returns `
|
|
55
|
+
* @returns Metadata object with `isCacheValid` (true if cache is ready to be used, false otherwise) and `lastUpdateTimestamp` (timestamp of last cache update or null)
|
|
56
56
|
*/
|
|
57
57
|
export function validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments) {
|
|
58
58
|
return Promise.resolve(storage.load && storage.load()).then(function () {
|
|
59
59
|
var currentTimestamp = Date.now();
|
|
60
60
|
var isThereCache = splits.getChangeNumber() > -1;
|
|
61
|
+
// Get lastUpdateTimestamp from storage
|
|
62
|
+
var lastUpdatedTimestampStr = storage.getItem(keys.buildLastUpdatedKey());
|
|
63
|
+
var lastUpdatedTimestamp = lastUpdatedTimestampStr ? parseInt(lastUpdatedTimestampStr, 10) : null;
|
|
64
|
+
var lastUpdateTimestamp = (!isNaNNumber(lastUpdatedTimestamp) && lastUpdatedTimestamp !== null) ? lastUpdatedTimestamp : null;
|
|
61
65
|
if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
|
|
62
66
|
splits.clear();
|
|
63
67
|
rbSegments.clear();
|
|
@@ -73,9 +77,15 @@ export function validateCache(options, storage, settings, keys, splits, rbSegmen
|
|
|
73
77
|
// Persist clear
|
|
74
78
|
if (storage.save)
|
|
75
79
|
storage.save();
|
|
76
|
-
return
|
|
80
|
+
return {
|
|
81
|
+
isCacheValid: false,
|
|
82
|
+
lastUpdateTimestamp: null
|
|
83
|
+
};
|
|
77
84
|
}
|
|
78
85
|
// Check if ready from cache
|
|
79
|
-
return
|
|
86
|
+
return {
|
|
87
|
+
isCacheValid: isThereCache,
|
|
88
|
+
lastUpdateTimestamp: lastUpdateTimestamp
|
|
89
|
+
};
|
|
80
90
|
});
|
|
81
91
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { forOwn } from '../../../utils/lang';
|
|
2
2
|
import { syncTaskFactory } from '../../syncTask';
|
|
3
3
|
import { CONTROL } from '../../../utils/constants';
|
|
4
|
-
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
|
|
4
|
+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED, FLAGS_UPDATE, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
5
5
|
import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/constants';
|
|
6
|
-
import { SdkUpdateMetadataKeys } from '../../polling/types';
|
|
7
6
|
/**
|
|
8
7
|
* Offline equivalent of `splitChangesUpdaterFactory`
|
|
9
8
|
*/
|
|
@@ -40,15 +39,16 @@ export function fromObjectUpdaterFactory(splitsParser, storage, readiness, setti
|
|
|
40
39
|
splitsCache.clear(),
|
|
41
40
|
splitsCache.update(splits, [], Date.now())
|
|
42
41
|
]).then(function () {
|
|
43
|
-
readiness.splits.emit(SDK_SPLITS_ARRIVED, { type:
|
|
42
|
+
readiness.splits.emit(SDK_SPLITS_ARRIVED, { type: FLAGS_UPDATE, names: [] });
|
|
44
43
|
if (startingUp) {
|
|
45
44
|
startingUp = false;
|
|
46
|
-
Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (
|
|
45
|
+
Promise.resolve(storage.validateCache ? storage.validateCache() : { isCacheValid: false, lastUpdateTimestamp: null }).then(function (cacheMetadata) {
|
|
47
46
|
// Emits SDK_READY_FROM_CACHE
|
|
48
|
-
if (
|
|
49
|
-
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
47
|
+
if (cacheMetadata.isCacheValid) {
|
|
48
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, cacheMetadata);
|
|
49
|
+
}
|
|
50
50
|
// Emits SDK_READY
|
|
51
|
-
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { type:
|
|
51
|
+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { type: SEGMENTS_UPDATE, names: [] });
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
return true;
|
|
@@ -1,14 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Metadata keys for SDK update events.
|
|
3
|
-
*/
|
|
4
|
-
export var SdkUpdateMetadataKeys;
|
|
5
|
-
(function (SdkUpdateMetadataKeys) {
|
|
6
|
-
/**
|
|
7
|
-
* The update event emitted when the SDK cache is updated with new data for flags.
|
|
8
|
-
*/
|
|
9
|
-
SdkUpdateMetadataKeys["FLAGS_UPDATE"] = "FLAGS_UPDATE";
|
|
10
|
-
/**
|
|
11
|
-
* The update event emitted when the SDK cache is updated with new data for segments.
|
|
12
|
-
*/
|
|
13
|
-
SdkUpdateMetadataKeys["SEGMENTS_UPDATE"] = "SEGMENTS_UPDATE";
|
|
14
|
-
})(SdkUpdateMetadataKeys || (SdkUpdateMetadataKeys = {}));
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { timeout } from '../../../utils/promise/timeout';
|
|
2
|
-
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
|
|
2
|
+
import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
3
3
|
import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
|
|
4
4
|
import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
|
|
5
5
|
import { usesSegmentsSync } from '../../../storages/AbstractSplitsCacheSync';
|
|
6
|
-
import { SdkUpdateMetadataKeys } from '../types';
|
|
7
6
|
/**
|
|
8
7
|
* factory of MySegments updater, a task that:
|
|
9
8
|
* - fetches mySegments using `mySegmentsFetcher`
|
|
@@ -37,7 +36,7 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmen
|
|
|
37
36
|
// Notify update if required
|
|
38
37
|
if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
39
38
|
readyOnAlreadyExistentState = false;
|
|
40
|
-
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED, { type:
|
|
39
|
+
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED, { type: SEGMENTS_UPDATE, names: [] });
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
42
|
function _mySegmentsUpdater(retry, segmentsData, noCache, till) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
|
|
1
|
+
import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
2
2
|
import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
|
|
3
3
|
import { timeout } from '../../../utils/promise/timeout';
|
|
4
|
-
import { SdkUpdateMetadataKeys } from '../types';
|
|
5
4
|
/**
|
|
6
5
|
* Factory of SegmentChanges updater, a task that:
|
|
7
6
|
* - fetches segment changes using `segmentChangesFetcher`
|
|
@@ -64,12 +63,13 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
|
|
|
64
63
|
// if at least one segment fetch succeeded, mark segments ready
|
|
65
64
|
if (shouldUpdateFlags.some(function (update) { return update; }) || readyOnAlreadyExistentState) {
|
|
66
65
|
readyOnAlreadyExistentState = false;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
if (readiness) {
|
|
67
|
+
var metadata = {
|
|
68
|
+
type: SEGMENTS_UPDATE,
|
|
69
|
+
names: []
|
|
70
|
+
};
|
|
72
71
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, metadata);
|
|
72
|
+
}
|
|
73
73
|
}
|
|
74
74
|
return true;
|
|
75
75
|
});
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { timeout } from '../../../utils/promise/timeout';
|
|
2
|
-
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
2
|
+
import { SDK_SPLITS_ARRIVED, FLAGS_UPDATE, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
3
3
|
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
4
4
|
import { startsWith } from '../../../utils/lang';
|
|
5
5
|
import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT } from '../../../utils/constants';
|
|
6
6
|
import { setToArray } from '../../../utils/lang/sets';
|
|
7
7
|
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
8
|
-
import { SdkUpdateMetadataKeys } from '../types';
|
|
9
8
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
10
9
|
// Returns a promise that could be rejected.
|
|
11
10
|
// @TODO review together with Segments and MySegments storage APIs
|
|
@@ -160,13 +159,14 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
|
|
|
160
159
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
161
160
|
.catch(function () { return false; } /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
162
161
|
.then(function (emitSplitsArrivedEvent) {
|
|
163
|
-
var metadata = {
|
|
164
|
-
type: updatedFlags.length > 0 ? SdkUpdateMetadataKeys.FLAGS_UPDATE : SdkUpdateMetadataKeys.SEGMENTS_UPDATE,
|
|
165
|
-
names: updatedFlags.length > 0 ? updatedFlags : []
|
|
166
|
-
};
|
|
167
162
|
// emit SDK events
|
|
168
|
-
if (emitSplitsArrivedEvent)
|
|
163
|
+
if (emitSplitsArrivedEvent) {
|
|
164
|
+
var metadata = {
|
|
165
|
+
type: updatedFlags.length > 0 ? FLAGS_UPDATE : SEGMENTS_UPDATE,
|
|
166
|
+
names: updatedFlags.length > 0 ? updatedFlags : []
|
|
167
|
+
};
|
|
169
168
|
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, metadata);
|
|
169
|
+
}
|
|
170
170
|
return true;
|
|
171
171
|
});
|
|
172
172
|
}
|
|
@@ -68,13 +68,14 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
68
68
|
running = true;
|
|
69
69
|
// @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
|
|
70
70
|
submitterManager.start(!isConsentGranted(settings));
|
|
71
|
-
return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (
|
|
71
|
+
return Promise.resolve(storage.validateCache ? storage.validateCache() : { isCacheValid: false, lastUpdateTimestamp: null }).then(function (cacheMetadata) {
|
|
72
72
|
if (!running)
|
|
73
73
|
return;
|
|
74
74
|
if (startFirstTime) {
|
|
75
75
|
// Emits SDK_READY_FROM_CACHE
|
|
76
|
-
if (
|
|
77
|
-
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
76
|
+
if (cacheMetadata.isCacheValid) {
|
|
77
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, cacheMetadata);
|
|
78
|
+
}
|
|
78
79
|
}
|
|
79
80
|
// start syncing splits and segments
|
|
80
81
|
if (pollingManager) {
|
|
@@ -3,7 +3,7 @@ import { ERROR_STORAGE_INVALID } from '../../../logger/constants';
|
|
|
3
3
|
import { LOCALHOST_MODE, STANDALONE_MODE, STORAGE_PLUGGABLE, STORAGE_LOCALSTORAGE, STORAGE_MEMORY } from '../../../utils/constants';
|
|
4
4
|
export function __InLocalStorageMockFactory(params) {
|
|
5
5
|
var result = InMemoryStorageCSFactory(params);
|
|
6
|
-
result.validateCache = function () { return Promise.resolve(true); }; // to emit SDK_READY_FROM_CACHE
|
|
6
|
+
result.validateCache = function () { return Promise.resolve({ isCacheValid: true, lastUpdateTimestamp: null }); }; // to emit SDK_READY_FROM_CACHE
|
|
7
7
|
return result;
|
|
8
8
|
}
|
|
9
9
|
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
|
package/package.json
CHANGED
|
@@ -10,3 +10,7 @@ export const SDK_READY_TIMED_OUT = 'init::timeout';
|
|
|
10
10
|
export const SDK_READY = 'init::ready';
|
|
11
11
|
export const SDK_READY_FROM_CACHE = 'init::cache-ready';
|
|
12
12
|
export const SDK_UPDATE = 'state::update';
|
|
13
|
+
|
|
14
|
+
// SdkUpdateMetadata types:
|
|
15
|
+
export const FLAGS_UPDATE = 'FLAGS_UPDATE';
|
|
16
|
+
export const SEGMENTS_UPDATE = 'SEGMENTS_UPDATE';
|
|
@@ -3,6 +3,7 @@ import { ISettings } from '../types';
|
|
|
3
3
|
import SplitIO from '../../types/splitio';
|
|
4
4
|
import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED, SDK_SEGMENTS_ARRIVED, SDK_READY_TIMED_OUT, SDK_READY_FROM_CACHE, SDK_UPDATE, SDK_READY } from './constants';
|
|
5
5
|
import { IReadinessEventEmitter, IReadinessManager, ISegmentsEventEmitter, ISplitsEventEmitter } from './types';
|
|
6
|
+
import { CacheValidationMetadata } from '../storages/inLocalStorage/validateCache';
|
|
6
7
|
|
|
7
8
|
function splitsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitter): ISplitsEventEmitter {
|
|
8
9
|
const splitsEventEmitter = objectAssign(new EventEmitter(), {
|
|
@@ -55,6 +56,7 @@ export function readinessManagerFactory(
|
|
|
55
56
|
|
|
56
57
|
// emit SDK_READY_FROM_CACHE
|
|
57
58
|
let isReadyFromCache = false;
|
|
59
|
+
let cacheLastUpdateTimestamp: number | null = null;
|
|
58
60
|
if (splits.splitsCacheLoaded) isReadyFromCache = true; // ready from cache, but doesn't emit SDK_READY_FROM_CACHE
|
|
59
61
|
else splits.once(SDK_SPLITS_CACHE_LOADED, checkIsReadyFromCache);
|
|
60
62
|
|
|
@@ -84,17 +86,17 @@ export function readinessManagerFactory(
|
|
|
84
86
|
splits.initCallbacks.push(__init);
|
|
85
87
|
if (splits.hasInit) __init();
|
|
86
88
|
|
|
87
|
-
function checkIsReadyFromCache() {
|
|
89
|
+
function checkIsReadyFromCache(cacheMetadata: CacheValidationMetadata) {
|
|
88
90
|
isReadyFromCache = true;
|
|
91
|
+
cacheLastUpdateTimestamp = cacheMetadata.lastUpdateTimestamp;
|
|
89
92
|
// Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
|
|
90
93
|
if (!isReady && !isDestroyed) {
|
|
91
94
|
try {
|
|
92
95
|
syncLastUpdate();
|
|
93
|
-
|
|
94
|
-
initialCacheLoad:
|
|
95
|
-
lastUpdateTimestamp:
|
|
96
|
-
};
|
|
97
|
-
gate.emit(SDK_READY_FROM_CACHE, metadata);
|
|
96
|
+
gate.emit(SDK_READY_FROM_CACHE, {
|
|
97
|
+
initialCacheLoad: !cacheMetadata.isCacheValid,
|
|
98
|
+
lastUpdateTimestamp: cacheLastUpdateTimestamp
|
|
99
|
+
});
|
|
98
100
|
} catch (e) {
|
|
99
101
|
// throws user callback exceptions in next tick
|
|
100
102
|
setTimeout(() => { throw e; }, 0);
|
|
@@ -117,19 +119,18 @@ export function readinessManagerFactory(
|
|
|
117
119
|
clearTimeout(readyTimeoutId);
|
|
118
120
|
isReady = true;
|
|
119
121
|
try {
|
|
120
|
-
syncLastUpdate();
|
|
121
122
|
const wasReadyFromCache = isReadyFromCache;
|
|
122
123
|
if (!isReadyFromCache) {
|
|
123
124
|
isReadyFromCache = true;
|
|
124
|
-
const
|
|
125
|
-
initialCacheLoad:
|
|
126
|
-
lastUpdateTimestamp:
|
|
125
|
+
const metadataReadyFromCache: SplitIO.SdkReadyMetadata = {
|
|
126
|
+
initialCacheLoad: true,
|
|
127
|
+
lastUpdateTimestamp: null // No cache timestamp when fresh install
|
|
127
128
|
};
|
|
128
|
-
gate.emit(SDK_READY_FROM_CACHE,
|
|
129
|
+
gate.emit(SDK_READY_FROM_CACHE, metadataReadyFromCache);
|
|
129
130
|
}
|
|
130
131
|
const metadataReady: SplitIO.SdkReadyMetadata = {
|
|
131
|
-
initialCacheLoad: wasReadyFromCache,
|
|
132
|
-
lastUpdateTimestamp:
|
|
132
|
+
initialCacheLoad: !wasReadyFromCache,
|
|
133
|
+
lastUpdateTimestamp: wasReadyFromCache ? cacheLastUpdateTimestamp : null
|
|
133
134
|
};
|
|
134
135
|
gate.emit(SDK_READY, metadataReady);
|
|
135
136
|
} catch (e) {
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -54,10 +54,10 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
57
|
-
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
57
|
+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
58
58
|
},
|
|
59
59
|
onReadyFromCacheCb() {
|
|
60
|
-
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
60
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
61
61
|
}
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -65,7 +65,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
65
65
|
|
|
66
66
|
if (initialRolloutPlan) {
|
|
67
67
|
setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
|
|
68
|
-
if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
68
|
+
if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, { isCacheValid: true, lastUpdateTimestamp: null });
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const clients: Record<string, SplitIO.IBasicClient> = {};
|
|
@@ -14,7 +14,7 @@ import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
|
14
14
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
15
15
|
import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
|
|
16
16
|
import { getMatching } from '../../utils/key';
|
|
17
|
-
import { validateCache } from './validateCache';
|
|
17
|
+
import { validateCache, CacheValidationMetadata } from './validateCache';
|
|
18
18
|
import { ILogger } from '../../logger/types';
|
|
19
19
|
import SplitIO from '../../../types/splitio';
|
|
20
20
|
import { storageAdapter } from './storageAdapter';
|
|
@@ -54,7 +54,7 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
54
54
|
const rbSegments = new RBSegmentsCacheInLocal(settings, keys, storage);
|
|
55
55
|
const segments = new MySegmentsCacheInLocal(log, keys, storage);
|
|
56
56
|
const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage);
|
|
57
|
-
let validateCachePromise: Promise<
|
|
57
|
+
let validateCachePromise: Promise<CacheValidationMetadata> | undefined;
|
|
58
58
|
|
|
59
59
|
return {
|
|
60
60
|
splits,
|
|
@@ -68,7 +68,10 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
68
68
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
69
69
|
|
|
70
70
|
validateCache() {
|
|
71
|
-
|
|
71
|
+
if (!validateCachePromise) {
|
|
72
|
+
validateCachePromise = validateCache(options, storage, settings, keys, splits, rbSegments, segments, largeSegments);
|
|
73
|
+
}
|
|
74
|
+
return validateCachePromise;
|
|
72
75
|
},
|
|
73
76
|
|
|
74
77
|
save() {
|
|
@@ -12,6 +12,11 @@ import { StorageAdapter } from '../types';
|
|
|
12
12
|
const DEFAULT_CACHE_EXPIRATION_IN_DAYS = 10;
|
|
13
13
|
const MILLIS_IN_A_DAY = 86400000;
|
|
14
14
|
|
|
15
|
+
export interface CacheValidationMetadata {
|
|
16
|
+
isCacheValid: boolean;
|
|
17
|
+
lastUpdateTimestamp: number | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Validates if cache should be cleared and sets the cache `hash` if needed.
|
|
17
22
|
*
|
|
@@ -66,14 +71,19 @@ function validateExpiration(options: SplitIO.InLocalStorageOptions, storage: Sto
|
|
|
66
71
|
* - its hash has changed, i.e., the SDK key, flags filter criteria or flags spec version was modified
|
|
67
72
|
* - `clearOnInit` was set and cache was not cleared in the last 24 hours
|
|
68
73
|
*
|
|
69
|
-
* @returns `
|
|
74
|
+
* @returns Metadata object with `isCacheValid` (true if cache is ready to be used, false otherwise) and `lastUpdateTimestamp` (timestamp of last cache update or null)
|
|
70
75
|
*/
|
|
71
|
-
export function validateCache(options: SplitIO.InLocalStorageOptions, storage: StorageAdapter, settings: ISettings, keys: KeyBuilderCS, splits: SplitsCacheInLocal, rbSegments: RBSegmentsCacheInLocal, segments: MySegmentsCacheInLocal, largeSegments: MySegmentsCacheInLocal): Promise<
|
|
76
|
+
export function validateCache(options: SplitIO.InLocalStorageOptions, storage: StorageAdapter, settings: ISettings, keys: KeyBuilderCS, splits: SplitsCacheInLocal, rbSegments: RBSegmentsCacheInLocal, segments: MySegmentsCacheInLocal, largeSegments: MySegmentsCacheInLocal): Promise<CacheValidationMetadata> {
|
|
72
77
|
|
|
73
78
|
return Promise.resolve(storage.load && storage.load()).then(() => {
|
|
74
79
|
const currentTimestamp = Date.now();
|
|
75
80
|
const isThereCache = splits.getChangeNumber() > -1;
|
|
76
81
|
|
|
82
|
+
// Get lastUpdateTimestamp from storage
|
|
83
|
+
const lastUpdatedTimestampStr = storage.getItem(keys.buildLastUpdatedKey());
|
|
84
|
+
const lastUpdatedTimestamp = lastUpdatedTimestampStr ? parseInt(lastUpdatedTimestampStr, 10) : null;
|
|
85
|
+
const lastUpdateTimestamp = (!isNaNNumber(lastUpdatedTimestamp) && lastUpdatedTimestamp !== null) ? lastUpdatedTimestamp : null;
|
|
86
|
+
|
|
77
87
|
if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
|
|
78
88
|
splits.clear();
|
|
79
89
|
rbSegments.clear();
|
|
@@ -90,10 +100,16 @@ export function validateCache(options: SplitIO.InLocalStorageOptions, storage: S
|
|
|
90
100
|
// Persist clear
|
|
91
101
|
if (storage.save) storage.save();
|
|
92
102
|
|
|
93
|
-
return
|
|
103
|
+
return {
|
|
104
|
+
isCacheValid: false,
|
|
105
|
+
lastUpdateTimestamp: null
|
|
106
|
+
};
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
// Check if ready from cache
|
|
97
|
-
return
|
|
110
|
+
return {
|
|
111
|
+
isCacheValid: isThereCache,
|
|
112
|
+
lastUpdateTimestamp
|
|
113
|
+
};
|
|
98
114
|
});
|
|
99
115
|
}
|
package/src/storages/types.ts
CHANGED
|
@@ -499,7 +499,7 @@ export interface IStorageSync extends IStorageBase<
|
|
|
499
499
|
IUniqueKeysCacheSync
|
|
500
500
|
> {
|
|
501
501
|
// Defined in client-side
|
|
502
|
-
validateCache?: () => Promise<boolean>,
|
|
502
|
+
validateCache?: () => Promise<{ isCacheValid: boolean; lastUpdateTimestamp: number | null }>,
|
|
503
503
|
largeSegments?: ISegmentsCacheSync,
|
|
504
504
|
}
|
|
505
505
|
|
|
@@ -7,9 +7,8 @@ import { syncTaskFactory } from '../../syncTask';
|
|
|
7
7
|
import { ISyncTask } from '../../types';
|
|
8
8
|
import { ISettings } from '../../../types';
|
|
9
9
|
import { CONTROL } from '../../../utils/constants';
|
|
10
|
-
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
|
|
10
|
+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED, FLAGS_UPDATE, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
11
11
|
import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/constants';
|
|
12
|
-
import { SdkUpdateMetadataKeys } from '../../polling/types';
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Offline equivalent of `splitChangesUpdaterFactory`
|
|
@@ -56,15 +55,17 @@ export function fromObjectUpdaterFactory(
|
|
|
56
55
|
splitsCache.clear(), // required to sync removed splits from mock
|
|
57
56
|
splitsCache.update(splits, [], Date.now())
|
|
58
57
|
]).then(() => {
|
|
59
|
-
readiness.splits.emit(SDK_SPLITS_ARRIVED, { type:
|
|
58
|
+
readiness.splits.emit(SDK_SPLITS_ARRIVED, { type: FLAGS_UPDATE, names: [] });
|
|
60
59
|
|
|
61
60
|
if (startingUp) {
|
|
62
61
|
startingUp = false;
|
|
63
|
-
Promise.resolve(storage.validateCache ? storage.validateCache() : false).then((
|
|
62
|
+
Promise.resolve(storage.validateCache ? storage.validateCache() : { isCacheValid: false, lastUpdateTimestamp: null }).then((cacheMetadata) => {
|
|
64
63
|
// Emits SDK_READY_FROM_CACHE
|
|
65
|
-
if (
|
|
64
|
+
if (cacheMetadata.isCacheValid) {
|
|
65
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, cacheMetadata);
|
|
66
|
+
}
|
|
66
67
|
// Emits SDK_READY
|
|
67
|
-
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { type:
|
|
68
|
+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, { type: SEGMENTS_UPDATE, names: [] });
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
71
|
return true;
|
|
@@ -4,20 +4,6 @@ import { IStorageSync } from '../../storages/types';
|
|
|
4
4
|
import { MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../streaming/types';
|
|
5
5
|
import { ITask, ISyncTask } from '../types';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Metadata keys for SDK update events.
|
|
9
|
-
*/
|
|
10
|
-
export enum SdkUpdateMetadataKeys {
|
|
11
|
-
/**
|
|
12
|
-
* The update event emitted when the SDK cache is updated with new data for flags.
|
|
13
|
-
*/
|
|
14
|
-
FLAGS_UPDATE = 'FLAGS_UPDATE',
|
|
15
|
-
/**
|
|
16
|
-
* The update event emitted when the SDK cache is updated with new data for segments.
|
|
17
|
-
*/
|
|
18
|
-
SEGMENTS_UPDATE = 'SEGMENTS_UPDATE'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
7
|
export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit | IRBSegment, changeNumber: number }], boolean> { }
|
|
22
8
|
|
|
23
9
|
export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
|
|
@@ -2,14 +2,13 @@ import { IMySegmentsFetcher } from '../fetchers/types';
|
|
|
2
2
|
import { IStorageSync } from '../../../storages/types';
|
|
3
3
|
import { ISegmentsEventEmitter } from '../../../readiness/types';
|
|
4
4
|
import { timeout } from '../../../utils/promise/timeout';
|
|
5
|
-
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
|
|
5
|
+
import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
6
6
|
import { ILogger } from '../../../logger/types';
|
|
7
7
|
import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
|
|
8
8
|
import { MySegmentsData } from '../types';
|
|
9
9
|
import { IMembershipsResponse } from '../../../dtos/types';
|
|
10
10
|
import { MEMBERSHIPS_LS_UPDATE } from '../../streaming/constants';
|
|
11
11
|
import { usesSegmentsSync } from '../../../storages/AbstractSplitsCacheSync';
|
|
12
|
-
import { SdkUpdateMetadataKeys } from '../types';
|
|
13
12
|
|
|
14
13
|
type IMySegmentsUpdater = (segmentsData?: MySegmentsData, noCache?: boolean, till?: number) => Promise<boolean>
|
|
15
14
|
|
|
@@ -57,7 +56,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
57
56
|
// Notify update if required
|
|
58
57
|
if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
|
|
59
58
|
readyOnAlreadyExistentState = false;
|
|
60
|
-
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED, { type:
|
|
59
|
+
segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED, { type: SEGMENTS_UPDATE, names: [] });
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ISegmentChangesFetcher } from '../fetchers/types';
|
|
2
2
|
import { ISegmentsCacheBase } from '../../../storages/types';
|
|
3
3
|
import { IReadinessManager } from '../../../readiness/types';
|
|
4
|
-
import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
|
|
4
|
+
import { SDK_SEGMENTS_ARRIVED, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
5
5
|
import { ILogger } from '../../../logger/types';
|
|
6
6
|
import { LOG_PREFIX_INSTANTIATION, LOG_PREFIX_SYNC_SEGMENTS } from '../../../logger/constants';
|
|
7
7
|
import { timeout } from '../../../utils/promise/timeout';
|
|
8
8
|
import { SdkUpdateMetadata } from '../../../../types/splitio';
|
|
9
|
-
import { SdkUpdateMetadataKeys } from '../types';
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
type ISegmentChangesUpdater = (fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number) => Promise<boolean>
|
|
@@ -86,11 +85,13 @@ export function segmentChangesUpdaterFactory(
|
|
|
86
85
|
// if at least one segment fetch succeeded, mark segments ready
|
|
87
86
|
if (shouldUpdateFlags.some(update => update) || readyOnAlreadyExistentState) {
|
|
88
87
|
readyOnAlreadyExistentState = false;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
if (readiness) {
|
|
89
|
+
const metadata: SdkUpdateMetadata = {
|
|
90
|
+
type: SEGMENTS_UPDATE,
|
|
91
|
+
names: []
|
|
92
|
+
};
|
|
93
|
+
readiness.segments.emit(SDK_SEGMENTS_ARRIVED, metadata);
|
|
94
|
+
}
|
|
94
95
|
}
|
|
95
96
|
return true;
|
|
96
97
|
});
|
|
@@ -3,7 +3,7 @@ import { ISplitChangesFetcher } from '../fetchers/types';
|
|
|
3
3
|
import { IRBSegment, ISplit, ISplitChangesResponse, ISplitFiltersValidation, MaybeThenable } from '../../../dtos/types';
|
|
4
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
5
|
import { timeout } from '../../../utils/promise/timeout';
|
|
6
|
-
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
6
|
+
import { SDK_SPLITS_ARRIVED, FLAGS_UPDATE, SEGMENTS_UPDATE } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
8
|
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
9
9
|
import { startsWith } from '../../../utils/lang';
|
|
@@ -11,7 +11,6 @@ import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT
|
|
|
11
11
|
import { setToArray } from '../../../utils/lang/sets';
|
|
12
12
|
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
13
13
|
import { SdkUpdateMetadata } from '../../../../types/splitio';
|
|
14
|
-
import { SdkUpdateMetadataKeys } from '../types';
|
|
15
14
|
|
|
16
15
|
export type InstantUpdate = { payload: ISplit | IRBSegment, changeNumber: number, type: string };
|
|
17
16
|
type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
|
|
@@ -198,12 +197,14 @@ export function splitChangesUpdaterFactory(
|
|
|
198
197
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
199
198
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
200
199
|
.then(emitSplitsArrivedEvent => {
|
|
201
|
-
const metadata: SdkUpdateMetadata = {
|
|
202
|
-
type: updatedFlags.length > 0 ? SdkUpdateMetadataKeys.FLAGS_UPDATE : SdkUpdateMetadataKeys.SEGMENTS_UPDATE,
|
|
203
|
-
names: updatedFlags.length > 0 ? updatedFlags : []
|
|
204
|
-
};
|
|
205
200
|
// emit SDK events
|
|
206
|
-
if (emitSplitsArrivedEvent)
|
|
201
|
+
if (emitSplitsArrivedEvent) {
|
|
202
|
+
const metadata: SdkUpdateMetadata = {
|
|
203
|
+
type: updatedFlags.length > 0 ? FLAGS_UPDATE : SEGMENTS_UPDATE,
|
|
204
|
+
names: updatedFlags.length > 0 ? updatedFlags : []
|
|
205
|
+
};
|
|
206
|
+
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, metadata);
|
|
207
|
+
}
|
|
207
208
|
return true;
|
|
208
209
|
});
|
|
209
210
|
}
|
|
@@ -92,12 +92,14 @@ export function syncManagerOnlineFactory(
|
|
|
92
92
|
// @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
|
|
93
93
|
submitterManager.start(!isConsentGranted(settings));
|
|
94
94
|
|
|
95
|
-
return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then((
|
|
95
|
+
return Promise.resolve(storage.validateCache ? storage.validateCache() : { isCacheValid: false, lastUpdateTimestamp: null }).then((cacheMetadata) => {
|
|
96
96
|
if (!running) return;
|
|
97
97
|
|
|
98
98
|
if (startFirstTime) {
|
|
99
99
|
// Emits SDK_READY_FROM_CACHE
|
|
100
|
-
if (
|
|
100
|
+
if (cacheMetadata.isCacheValid) {
|
|
101
|
+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED, cacheMetadata);
|
|
102
|
+
}
|
|
101
103
|
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -8,7 +8,7 @@ import { IStorageFactoryParams, IStorageSync } from '../../../storages/types';
|
|
|
8
8
|
|
|
9
9
|
export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync {
|
|
10
10
|
const result = InMemoryStorageCSFactory(params);
|
|
11
|
-
result.validateCache = () => Promise.resolve(true); // to emit SDK_READY_FROM_CACHE
|
|
11
|
+
result.validateCache = () => Promise.resolve({ isCacheValid: true, lastUpdateTimestamp: null }); // to emit SDK_READY_FROM_CACHE
|
|
12
12
|
return result;
|
|
13
13
|
}
|
|
14
14
|
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
|