@splitsoftware/splitio-commons 2.4.2-rc.3 → 2.5.0-rc.1
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 +3 -11
- package/cjs/evaluator/convertions/index.js +9 -1
- package/cjs/evaluator/matchersTransform/index.js +2 -3
- package/cjs/sdkClient/sdkClientMethodCS.js +5 -1
- package/cjs/sdkFactory/index.js +8 -2
- package/cjs/storages/getRolloutPlan.js +69 -0
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +21 -17
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +37 -33
- package/cjs/storages/inLocalStorage/index.js +13 -31
- package/cjs/storages/inLocalStorage/validateCache.js +25 -30
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -0
- package/cjs/storages/setRolloutPlan.js +66 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +0 -2
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +0 -2
- package/cjs/sync/syncManagerOnline.js +24 -28
- package/cjs/utils/env/isLocalStorageAvailable.js +5 -28
- package/cjs/utils/inputValidation/index.js +1 -3
- package/cjs/utils/settingsValidation/index.js +4 -0
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/evaluator/convertions/index.js +7 -0
- package/esm/evaluator/matchersTransform/index.js +3 -4
- package/esm/sdkClient/sdkClientMethodCS.js +5 -1
- package/esm/sdkFactory/index.js +8 -2
- package/esm/storages/getRolloutPlan.js +65 -0
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +21 -17
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +37 -33
- package/esm/storages/inLocalStorage/index.js +14 -32
- package/esm/storages/inLocalStorage/validateCache.js +25 -30
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +4 -0
- package/esm/storages/setRolloutPlan.js +61 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +0 -2
- package/esm/sync/polling/updaters/splitChangesUpdater.js +0 -2
- package/esm/sync/syncManagerOnline.js +24 -28
- package/esm/utils/env/isLocalStorageAvailable.js +3 -24
- package/esm/utils/inputValidation/index.js +0 -1
- package/esm/utils/settingsValidation/index.js +4 -0
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/evaluator/convertions/index.ts +10 -0
- package/src/evaluator/matchersTransform/index.ts +3 -4
- package/src/sdkClient/sdkClientMethodCS.ts +7 -1
- package/src/sdkFactory/index.ts +12 -4
- package/src/storages/getRolloutPlan.ts +72 -0
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +17 -18
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +22 -19
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +37 -34
- package/src/storages/inLocalStorage/index.ts +16 -37
- package/src/storages/inLocalStorage/validateCache.ts +25 -31
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +4 -0
- package/src/storages/setRolloutPlan.ts +71 -0
- package/src/storages/types.ts +25 -23
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +2 -1
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +0 -2
- package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -3
- package/src/sync/syncManagerOnline.ts +22 -27
- package/src/types.ts +2 -35
- package/src/utils/env/isLocalStorageAvailable.ts +3 -24
- package/src/utils/inputValidation/index.ts +0 -1
- package/src/utils/settingsValidation/index.ts +4 -0
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +36 -44
- package/cjs/storages/dataLoader.js +0 -50
- package/cjs/storages/inLocalStorage/storageAdapter.js +0 -54
- package/cjs/utils/inputValidation/preloadedData.js +0 -59
- package/esm/storages/dataLoader.js +0 -46
- package/esm/storages/inLocalStorage/storageAdapter.js +0 -50
- package/esm/utils/inputValidation/preloadedData.js +0 -55
- package/src/storages/dataLoader.ts +0 -55
- package/src/storages/inLocalStorage/storageAdapter.ts +0 -62
- package/src/utils/inputValidation/preloadedData.ts +0 -57
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { PreloadedData } from '../types';
|
|
2
|
-
import { DataLoader, ISegmentsCacheSync, ISplitsCacheSync } from './types';
|
|
3
|
-
|
|
4
|
-
// This value might be eventually set via a config parameter
|
|
5
|
-
const DEFAULT_CACHE_EXPIRATION_IN_MILLIS = 864000000; // 10 days
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Factory of client-side storage loader
|
|
9
|
-
*
|
|
10
|
-
* @param preloadedData - validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
|
|
11
|
-
* and extended with a `mySegmentsData` property.
|
|
12
|
-
* @returns function to preload the storage
|
|
13
|
-
*/
|
|
14
|
-
export function dataLoaderFactory(preloadedData: PreloadedData): DataLoader {
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
|
|
18
|
-
* (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
|
|
19
|
-
*
|
|
20
|
-
* @param storage - object containing `splits` and `segments` cache (client-side variant)
|
|
21
|
-
* @param userId - user key string of the provided MySegmentsCache
|
|
22
|
-
*/
|
|
23
|
-
// @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
|
|
24
|
-
// @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
|
|
25
|
-
return function loadData(storage: { splits: ISplitsCacheSync, segments: ISegmentsCacheSync }, userId: string) {
|
|
26
|
-
// Do not load data if current preloadedData is empty
|
|
27
|
-
if (Object.keys(preloadedData).length === 0) return;
|
|
28
|
-
|
|
29
|
-
const { lastUpdated = -1, segmentsData = {}, since = -1, splitsData = {} } = preloadedData;
|
|
30
|
-
|
|
31
|
-
const storedSince = storage.splits.getChangeNumber();
|
|
32
|
-
const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
|
|
33
|
-
|
|
34
|
-
// Do not load data if current localStorage data is more recent,
|
|
35
|
-
// or if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
|
|
36
|
-
if (storedSince > since || lastUpdated < expirationTimestamp) return;
|
|
37
|
-
|
|
38
|
-
// cleaning up the localStorage data, since some cached splits might need be part of the preloaded data
|
|
39
|
-
storage.splits.clear();
|
|
40
|
-
|
|
41
|
-
// splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
|
|
42
|
-
storage.splits.update(Object.keys(splitsData).map(splitName => JSON.parse(splitsData[splitName])), [], since);
|
|
43
|
-
|
|
44
|
-
// add mySegments data
|
|
45
|
-
let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
|
|
46
|
-
if (!mySegmentsData) {
|
|
47
|
-
// segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
|
|
48
|
-
mySegmentsData = Object.keys(segmentsData).filter(segmentName => {
|
|
49
|
-
const userIds = JSON.parse(segmentsData[segmentName]).added;
|
|
50
|
-
return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
storage.segments.resetSegments({ k: mySegmentsData.map(s => ({ n: s })) });
|
|
54
|
-
};
|
|
55
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { ILogger } from '../../logger/types';
|
|
2
|
-
import SplitIO from '../../../types/splitio';
|
|
3
|
-
import { LOG_PREFIX } from './constants';
|
|
4
|
-
import { StorageAdapter } from '../types';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export function storageAdapter(log: ILogger, prefix: string, wrapper: SplitIO.SyncStorageWrapper | SplitIO.AsyncStorageWrapper): Required<StorageAdapter> {
|
|
8
|
-
let keys: string[] = [];
|
|
9
|
-
let cache: Record<string, string> = {};
|
|
10
|
-
|
|
11
|
-
let loadPromise: Promise<void> | undefined;
|
|
12
|
-
let savePromise = Promise.resolve();
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
load() {
|
|
16
|
-
return loadPromise || (loadPromise = Promise.resolve().then(() => {
|
|
17
|
-
return wrapper.getItem(prefix);
|
|
18
|
-
}).then((storedCache) => {
|
|
19
|
-
cache = JSON.parse(storedCache || '{}');
|
|
20
|
-
keys = Object.keys(cache);
|
|
21
|
-
}).catch((e) => {
|
|
22
|
-
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `getItem` method, with error: ' + e);
|
|
23
|
-
}));
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
save() {
|
|
27
|
-
return savePromise = savePromise.then(() => {
|
|
28
|
-
return Promise.resolve(wrapper.setItem(prefix, JSON.stringify(cache)));
|
|
29
|
-
}).catch((e) => {
|
|
30
|
-
log.error(LOG_PREFIX + 'Rejected promise calling wrapper `setItem` method, with error: ' + e);
|
|
31
|
-
});
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
whenSaved() {
|
|
35
|
-
return savePromise;
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
get length() {
|
|
39
|
-
return keys.length;
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
getItem(key: string) {
|
|
43
|
-
return cache[key] || null;
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
key(index: number) {
|
|
47
|
-
return keys[index] || null;
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
removeItem(key: string) {
|
|
51
|
-
const index = keys.indexOf(key);
|
|
52
|
-
if (index === -1) return;
|
|
53
|
-
keys.splice(index, 1);
|
|
54
|
-
delete cache[key];
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
setItem(key: string, value: string) {
|
|
58
|
-
if (keys.indexOf(key) === -1) keys.push(key);
|
|
59
|
-
cache[key] = value;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { isObject, isString, isFiniteNumber } from '../lang';
|
|
2
|
-
import { validateSplit } from './split';
|
|
3
|
-
import { ILogger } from '../../logger/types';
|
|
4
|
-
|
|
5
|
-
function validateTimestampData(log: ILogger, maybeTimestamp: any, method: string, item: string) {
|
|
6
|
-
if (isFiniteNumber(maybeTimestamp) && maybeTimestamp > -1) return true;
|
|
7
|
-
log.error(`${method}: preloadedData.${item} must be a positive number.`);
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function validateSplitsData(log: ILogger, maybeSplitsData: any, method: string) {
|
|
12
|
-
if (isObject(maybeSplitsData)) {
|
|
13
|
-
const splitNames = Object.keys(maybeSplitsData);
|
|
14
|
-
if (splitNames.length === 0) log.warn(`${method}: preloadedData.splitsData doesn't contain feature flag definitions.`);
|
|
15
|
-
// @TODO in the future, consider handling the possibility of having parsed definitions of splits
|
|
16
|
-
if (splitNames.every(splitName => validateSplit(log, splitName, method) && isString(maybeSplitsData[splitName]))) return true;
|
|
17
|
-
}
|
|
18
|
-
log.error(`${method}: preloadedData.splitsData must be a map of feature flag names to their stringified definitions.`);
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function validateMySegmentsData(log: ILogger, maybeMySegmentsData: any, method: string) {
|
|
23
|
-
if (isObject(maybeMySegmentsData)) {
|
|
24
|
-
const userKeys = Object.keys(maybeMySegmentsData);
|
|
25
|
-
if (userKeys.every(userKey => {
|
|
26
|
-
const segmentNames = maybeMySegmentsData[userKey];
|
|
27
|
-
// an empty list is valid
|
|
28
|
-
return Array.isArray(segmentNames) && segmentNames.every(segmentName => isString(segmentName));
|
|
29
|
-
})) return true;
|
|
30
|
-
}
|
|
31
|
-
log.error(`${method}: preloadedData.mySegmentsData must be a map of user keys to their list of segment names.`);
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function validateSegmentsData(log: ILogger, maybeSegmentsData: any, method: string) {
|
|
36
|
-
if (isObject(maybeSegmentsData)) {
|
|
37
|
-
const segmentNames = Object.keys(maybeSegmentsData);
|
|
38
|
-
if (segmentNames.every(segmentName => isString(maybeSegmentsData[segmentName]))) return true;
|
|
39
|
-
}
|
|
40
|
-
log.error(`${method}: preloadedData.segmentsData must be a map of segment names to their stringified definitions.`);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function validatePreloadedData(log: ILogger, maybePreloadedData: any, method: string) {
|
|
45
|
-
if (!isObject(maybePreloadedData)) {
|
|
46
|
-
log.error(`${method}: preloadedData must be an object.`);
|
|
47
|
-
} else if (
|
|
48
|
-
validateTimestampData(log, maybePreloadedData.lastUpdated, method, 'lastUpdated') &&
|
|
49
|
-
validateTimestampData(log, maybePreloadedData.since, method, 'since') &&
|
|
50
|
-
validateSplitsData(log, maybePreloadedData.splitsData, method) &&
|
|
51
|
-
(!maybePreloadedData.mySegmentsData || validateMySegmentsData(log, maybePreloadedData.mySegmentsData, method)) &&
|
|
52
|
-
(!maybePreloadedData.segmentsData || validateSegmentsData(log, maybePreloadedData.segmentsData, method))
|
|
53
|
-
) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
}
|