@splitsoftware/splitio-commons 2.4.2-rc.3 → 2.5.0-rc.0
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 +2 -10
- package/cjs/storages/dataLoader.js +102 -43
- 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 +23 -28
- package/cjs/storages/inMemory/InMemoryStorageCS.js +32 -14
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -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/settingsValidation/storage/storageCS.js +1 -1
- package/esm/storages/dataLoader.js +99 -41
- 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 +23 -28
- package/esm/storages/inMemory/InMemoryStorageCS.js +32 -14
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +4 -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/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/sdkFactory/index.ts +3 -2
- package/src/storages/dataLoader.ts +107 -49
- 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 +23 -29
- package/src/storages/inMemory/InMemoryStorageCS.ts +37 -14
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +4 -0
- package/src/storages/types.ts +6 -22
- 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 +0 -35
- package/src/utils/env/isLocalStorageAvailable.ts +3 -24
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +46 -42
- package/cjs/storages/inLocalStorage/storageAdapter.js +0 -54
- package/esm/storages/inLocalStorage/storageAdapter.js +0 -50
- package/src/storages/inLocalStorage/storageAdapter.ts +0 -62
|
@@ -66,39 +66,35 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
|
|
|
66
66
|
*/
|
|
67
67
|
start: function () {
|
|
68
68
|
running = true;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (pollingManager) {
|
|
81
|
-
// If synchronization is disabled pushManager and pollingManager should not start
|
|
82
|
-
if (syncEnabled) {
|
|
83
|
-
if (pushManager) {
|
|
84
|
-
// Doesn't call `syncAll` when the syncManager is resuming
|
|
85
|
-
if (startFirstTime) {
|
|
86
|
-
pollingManager.syncAll();
|
|
87
|
-
}
|
|
88
|
-
pushManager.start();
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
pollingManager.start();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
69
|
+
if (startFirstTime) {
|
|
70
|
+
var isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
|
|
71
|
+
if (isCacheLoaded)
|
|
72
|
+
Promise.resolve().then(function () { readiness.splits.emit(SDK_SPLITS_CACHE_LOADED); });
|
|
73
|
+
}
|
|
74
|
+
// start syncing splits and segments
|
|
75
|
+
if (pollingManager) {
|
|
76
|
+
// If synchronization is disabled pushManager and pollingManager should not start
|
|
77
|
+
if (syncEnabled) {
|
|
78
|
+
if (pushManager) {
|
|
79
|
+
// Doesn't call `syncAll` when the syncManager is resuming
|
|
95
80
|
if (startFirstTime) {
|
|
96
81
|
pollingManager.syncAll();
|
|
97
82
|
}
|
|
83
|
+
pushManager.start();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
pollingManager.start();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (startFirstTime) {
|
|
91
|
+
pollingManager.syncAll();
|
|
98
92
|
}
|
|
99
93
|
}
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
}
|
|
95
|
+
// start periodic data recording (events, impressions, telemetry).
|
|
96
|
+
submitterManager.start(!isConsentGranted(settings));
|
|
97
|
+
startFirstTime = false;
|
|
102
98
|
},
|
|
103
99
|
/**
|
|
104
100
|
* Method used to stop/pause the syncManager.
|
|
@@ -1,33 +1,12 @@
|
|
|
1
|
+
/* eslint-disable no-undef */
|
|
1
2
|
export function isLocalStorageAvailable() {
|
|
2
|
-
try {
|
|
3
|
-
// eslint-disable-next-line no-undef
|
|
4
|
-
return isValidStorageWrapper(localStorage);
|
|
5
|
-
}
|
|
6
|
-
catch (e) {
|
|
7
|
-
return false;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
export function isValidStorageWrapper(wrapper) {
|
|
11
3
|
var mod = '__SPLITSOFTWARE__';
|
|
12
4
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
wrapper.removeItem(mod);
|
|
5
|
+
localStorage.setItem(mod, mod);
|
|
6
|
+
localStorage.removeItem(mod);
|
|
16
7
|
return true;
|
|
17
8
|
}
|
|
18
9
|
catch (e) {
|
|
19
10
|
return false;
|
|
20
11
|
}
|
|
21
12
|
}
|
|
22
|
-
export function isWebStorage(wrapper) {
|
|
23
|
-
if (typeof wrapper.length === 'number') {
|
|
24
|
-
try {
|
|
25
|
-
wrapper.key(0);
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
catch (e) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
@@ -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
|
|
6
|
+
result.validateCache = function () { return true; }; // to emit SDK_READY_FROM_CACHE
|
|
7
7
|
return result;
|
|
8
8
|
}
|
|
9
9
|
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
|
package/package.json
CHANGED
package/src/sdkFactory/index.ts
CHANGED
|
@@ -43,7 +43,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
43
43
|
|
|
44
44
|
const storage = storageFactory({
|
|
45
45
|
settings,
|
|
46
|
-
onReadyCb
|
|
46
|
+
onReadyCb(error) {
|
|
47
47
|
if (error) {
|
|
48
48
|
// If storage fails to connect, SDK_READY_TIMED_OUT event is emitted immediately. Review when timeout and non-recoverable errors are reworked
|
|
49
49
|
readiness.timeout();
|
|
@@ -52,10 +52,11 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
52
52
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
53
53
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
54
54
|
},
|
|
55
|
-
onReadyFromCacheCb
|
|
55
|
+
onReadyFromCacheCb() {
|
|
56
56
|
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
|
+
|
|
59
60
|
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
|
|
60
61
|
const clients: Record<string, SplitIO.IBasicClient> = {};
|
|
61
62
|
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
@@ -1,55 +1,113 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import SplitIO from '../../types/splitio';
|
|
2
|
+
import { IRBSegmentsCacheSync, ISegmentsCacheSync, ISplitsCacheSync, IStorageSync } from './types';
|
|
3
|
+
import { setToArray } from '../utils/lang/sets';
|
|
4
|
+
import { getMatching } from '../utils/key';
|
|
5
|
+
import { IMembershipsResponse, IMySegmentsResponse, IRBSegment, ISplit } from '../dtos/types';
|
|
6
|
+
import { ILogger } from '../logger/types';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* and extended with a `mySegmentsData` property.
|
|
12
|
-
* @returns function to preload the storage
|
|
9
|
+
* Sets the given synchronous storage with the provided preloaded data snapshot.
|
|
10
|
+
* If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
|
|
11
|
+
* Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
|
|
13
12
|
*/
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const userIds = JSON.parse(segmentsData[segmentName]).added;
|
|
50
|
-
return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
|
|
51
|
-
});
|
|
13
|
+
export function setCache(log: ILogger, preloadedData: SplitIO.PreloadedData, storage: { splits?: ISplitsCacheSync, rbSegments?: IRBSegmentsCacheSync, segments: ISegmentsCacheSync, largeSegments?: ISegmentsCacheSync }, matchingKey?: string) {
|
|
14
|
+
// Do not load data if current preloadedData is empty
|
|
15
|
+
if (Object.keys(preloadedData).length === 0) return;
|
|
16
|
+
|
|
17
|
+
const { splits, rbSegments, segments, largeSegments } = storage;
|
|
18
|
+
|
|
19
|
+
log.debug(`set cache${matchingKey ? ` for key ${matchingKey}` : ''}`);
|
|
20
|
+
|
|
21
|
+
if (splits) {
|
|
22
|
+
splits.clear();
|
|
23
|
+
splits.update(preloadedData.flags as ISplit[] || [], [], preloadedData.since || -1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (rbSegments) {
|
|
27
|
+
rbSegments.clear();
|
|
28
|
+
rbSegments.update(preloadedData.rbSegments as IRBSegment[] || [], [], preloadedData.rbSince || -1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const segmentsData = preloadedData.segments || {};
|
|
32
|
+
if (matchingKey) { // add memberships data (client-side)
|
|
33
|
+
let memberships = preloadedData.memberships && preloadedData.memberships[matchingKey];
|
|
34
|
+
if (!memberships && segmentsData) {
|
|
35
|
+
memberships = {
|
|
36
|
+
ms: {
|
|
37
|
+
k: Object.keys(segmentsData).filter(segmentName => {
|
|
38
|
+
const segmentKeys = segmentsData[segmentName];
|
|
39
|
+
return segmentKeys.indexOf(matchingKey) > -1;
|
|
40
|
+
}).map(segmentName => ({ n: segmentName }))
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (memberships) {
|
|
46
|
+
if ((memberships as IMembershipsResponse).ms) segments.resetSegments((memberships as IMembershipsResponse).ms!);
|
|
47
|
+
if ((memberships as IMembershipsResponse).ls && largeSegments) largeSegments.resetSegments((memberships as IMembershipsResponse).ls!);
|
|
52
48
|
}
|
|
53
|
-
|
|
49
|
+
} else { // add segments data (server-side)
|
|
50
|
+
Object.keys(segmentsData).forEach(segmentName => {
|
|
51
|
+
const segmentKeys = segmentsData[segmentName];
|
|
52
|
+
segments.update(segmentName, segmentKeys, [], -1);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Gets the preloaded data snapshot from the given synchronous storage.
|
|
59
|
+
* If `keys` are provided, the memberships for those keys is returned, to protect segments data.
|
|
60
|
+
* Otherwise, the segments data is returned.
|
|
61
|
+
*/
|
|
62
|
+
export function getCache(log: ILogger, storage: IStorageSync, keys?: SplitIO.SplitKey[]): SplitIO.PreloadedData {
|
|
63
|
+
|
|
64
|
+
log.debug(`get cache${keys ? ` for keys ${keys}` : ''}`);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
since: storage.splits.getChangeNumber(),
|
|
68
|
+
flags: storage.splits.getAll(),
|
|
69
|
+
rbSince: storage.rbSegments.getChangeNumber(),
|
|
70
|
+
rbSegments: storage.rbSegments.getAll(),
|
|
71
|
+
segments: keys ?
|
|
72
|
+
undefined : // @ts-ignore accessing private prop
|
|
73
|
+
Object.keys(storage.segments.segmentCache).reduce((prev, cur) => { // @ts-ignore accessing private prop
|
|
74
|
+
prev[cur] = setToArray(storage.segments.segmentCache[cur] as Set<string>);
|
|
75
|
+
return prev;
|
|
76
|
+
}, {}),
|
|
77
|
+
memberships: keys ?
|
|
78
|
+
keys.reduce<Record<string, IMembershipsResponse>>((prev, key) => {
|
|
79
|
+
if (storage.shared) {
|
|
80
|
+
// Client-side segments
|
|
81
|
+
// @ts-ignore accessing private prop
|
|
82
|
+
const sharedStorage = storage.shared(key);
|
|
83
|
+
prev[getMatching(key)] = {
|
|
84
|
+
ms: {
|
|
85
|
+
// @ts-ignore accessing private prop
|
|
86
|
+
k: Object.keys(sharedStorage.segments.segmentCache).map(segmentName => ({ n: segmentName })),
|
|
87
|
+
},
|
|
88
|
+
ls: sharedStorage.largeSegments ? {
|
|
89
|
+
// @ts-ignore accessing private prop
|
|
90
|
+
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(segmentName => ({ n: segmentName })),
|
|
91
|
+
} : undefined
|
|
92
|
+
};
|
|
93
|
+
} else {
|
|
94
|
+
prev[getMatching(key)] = {
|
|
95
|
+
ms: {
|
|
96
|
+
// Server-side segments
|
|
97
|
+
// @ts-ignore accessing private prop
|
|
98
|
+
k: Object.keys(storage.segments.segmentCache).reduce<IMySegmentsResponse['k']>((prev, segmentName) => { // @ts-ignore accessing private prop
|
|
99
|
+
return storage.segments.segmentCache[segmentName].has(key) ?
|
|
100
|
+
prev!.concat({ n: segmentName }) :
|
|
101
|
+
prev;
|
|
102
|
+
}, [])
|
|
103
|
+
},
|
|
104
|
+
ls: {
|
|
105
|
+
k: []
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return prev;
|
|
110
|
+
}, {}) :
|
|
111
|
+
undefined
|
|
54
112
|
};
|
|
55
113
|
}
|
|
@@ -3,19 +3,16 @@ import { isNaNNumber } from '../../utils/lang';
|
|
|
3
3
|
import { AbstractMySegmentsCacheSync } from '../AbstractMySegmentsCacheSync';
|
|
4
4
|
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
5
5
|
import { LOG_PREFIX, DEFINED } from './constants';
|
|
6
|
-
import { StorageAdapter } from '../types';
|
|
7
6
|
|
|
8
7
|
export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
9
8
|
|
|
10
9
|
private readonly keys: MySegmentsKeyBuilder;
|
|
11
10
|
private readonly log: ILogger;
|
|
12
|
-
private readonly storage: StorageAdapter;
|
|
13
11
|
|
|
14
|
-
constructor(log: ILogger, keys: MySegmentsKeyBuilder
|
|
12
|
+
constructor(log: ILogger, keys: MySegmentsKeyBuilder) {
|
|
15
13
|
super();
|
|
16
14
|
this.log = log;
|
|
17
15
|
this.keys = keys;
|
|
18
|
-
this.storage = storage;
|
|
19
16
|
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
20
17
|
}
|
|
21
18
|
|
|
@@ -23,8 +20,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
23
20
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
24
21
|
|
|
25
22
|
try {
|
|
26
|
-
if (
|
|
27
|
-
|
|
23
|
+
if (localStorage.getItem(segmentKey) === DEFINED) return false;
|
|
24
|
+
localStorage.setItem(segmentKey, DEFINED);
|
|
28
25
|
return true;
|
|
29
26
|
} catch (e) {
|
|
30
27
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -36,8 +33,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
36
33
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
37
34
|
|
|
38
35
|
try {
|
|
39
|
-
if (
|
|
40
|
-
|
|
36
|
+
if (localStorage.getItem(segmentKey) !== DEFINED) return false;
|
|
37
|
+
localStorage.removeItem(segmentKey);
|
|
41
38
|
return true;
|
|
42
39
|
} catch (e) {
|
|
43
40
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -46,16 +43,18 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
isInSegment(name: string): boolean {
|
|
49
|
-
return
|
|
46
|
+
return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
getRegisteredSegments(): string[] {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
// Scan current values from localStorage
|
|
51
|
+
return Object.keys(localStorage).reduce((accum, key) => {
|
|
52
|
+
let segmentName = this.keys.extractSegmentName(key);
|
|
53
|
+
|
|
54
|
+
if (segmentName) accum.push(segmentName);
|
|
55
|
+
|
|
56
|
+
return accum;
|
|
57
|
+
}, [] as string[]);
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
getKeysCount() {
|
|
@@ -64,8 +63,8 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
64
63
|
|
|
65
64
|
protected setChangeNumber(changeNumber?: number) {
|
|
66
65
|
try {
|
|
67
|
-
if (changeNumber)
|
|
68
|
-
else
|
|
66
|
+
if (changeNumber) localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
67
|
+
else localStorage.removeItem(this.keys.buildTillKey());
|
|
69
68
|
} catch (e) {
|
|
70
69
|
this.log.error(e);
|
|
71
70
|
}
|
|
@@ -73,7 +72,7 @@ export class MySegmentsCacheInLocal extends AbstractMySegmentsCacheSync {
|
|
|
73
72
|
|
|
74
73
|
getChangeNumber() {
|
|
75
74
|
const n = -1;
|
|
76
|
-
let value: string | number | null =
|
|
75
|
+
let value: string | number | null = localStorage.getItem(this.keys.buildTillKey());
|
|
77
76
|
|
|
78
77
|
if (value !== null) {
|
|
79
78
|
value = parseInt(value, 10);
|
|
@@ -5,24 +5,22 @@ import { isFiniteNumber, isNaNNumber, toNumber } from '../../utils/lang';
|
|
|
5
5
|
import { setToArray } from '../../utils/lang/sets';
|
|
6
6
|
import { usesSegments } from '../AbstractSplitsCacheSync';
|
|
7
7
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
8
|
-
import { IRBSegmentsCacheSync
|
|
8
|
+
import { IRBSegmentsCacheSync } from '../types';
|
|
9
9
|
import { LOG_PREFIX } from './constants';
|
|
10
10
|
|
|
11
11
|
export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
12
12
|
|
|
13
13
|
private readonly keys: KeyBuilderCS;
|
|
14
14
|
private readonly log: ILogger;
|
|
15
|
-
private readonly storage: StorageAdapter;
|
|
16
15
|
|
|
17
|
-
constructor(settings: ISettings, keys: KeyBuilderCS
|
|
16
|
+
constructor(settings: ISettings, keys: KeyBuilderCS) {
|
|
18
17
|
this.keys = keys;
|
|
19
18
|
this.log = settings.log;
|
|
20
|
-
this.storage = storage;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
clear() {
|
|
24
22
|
this.getNames().forEach(name => this.remove(name));
|
|
25
|
-
|
|
23
|
+
localStorage.removeItem(this.keys.buildRBSegmentsTillKey());
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
update(toAdd: IRBSegment[], toRemove: IRBSegment[], changeNumber: number): boolean {
|
|
@@ -33,8 +31,8 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
33
31
|
|
|
34
32
|
private setChangeNumber(changeNumber: number) {
|
|
35
33
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
localStorage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
35
|
+
localStorage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
38
36
|
} catch (e) {
|
|
39
37
|
this.log.error(LOG_PREFIX + e);
|
|
40
38
|
}
|
|
@@ -42,19 +40,20 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
42
40
|
|
|
43
41
|
private updateSegmentCount(diff: number) {
|
|
44
42
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
45
|
-
const count = toNumber(
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
const count = toNumber(localStorage.getItem(segmentsCountKey)) + diff;
|
|
44
|
+
// @ts-expect-error
|
|
45
|
+
if (count > 0) localStorage.setItem(segmentsCountKey, count);
|
|
46
|
+
else localStorage.removeItem(segmentsCountKey);
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
private add(rbSegment: IRBSegment): boolean {
|
|
51
50
|
try {
|
|
52
51
|
const name = rbSegment.name;
|
|
53
52
|
const rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
54
|
-
const
|
|
55
|
-
const previous =
|
|
53
|
+
const rbSegmentFromLocalStorage = localStorage.getItem(rbSegmentKey);
|
|
54
|
+
const previous = rbSegmentFromLocalStorage ? JSON.parse(rbSegmentFromLocalStorage) : null;
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
localStorage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
58
57
|
|
|
59
58
|
let usesSegmentsDiff = 0;
|
|
60
59
|
if (previous && usesSegments(previous)) usesSegmentsDiff--;
|
|
@@ -73,7 +72,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
73
72
|
const rbSegment = this.get(name);
|
|
74
73
|
if (!rbSegment) return false;
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
localStorage.removeItem(this.keys.buildRBSegmentKey(name));
|
|
77
76
|
|
|
78
77
|
if (usesSegments(rbSegment)) this.updateSegmentCount(-1);
|
|
79
78
|
|
|
@@ -85,13 +84,13 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
private getNames(): string[] {
|
|
88
|
-
const len =
|
|
87
|
+
const len = localStorage.length;
|
|
89
88
|
const accum = [];
|
|
90
89
|
|
|
91
90
|
let cur = 0;
|
|
92
91
|
|
|
93
92
|
while (cur < len) {
|
|
94
|
-
const key =
|
|
93
|
+
const key = localStorage.key(cur);
|
|
95
94
|
|
|
96
95
|
if (key != null && this.keys.isRBSegmentKey(key)) accum.push(this.keys.extractKey(key));
|
|
97
96
|
|
|
@@ -102,10 +101,14 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
get(name: string): IRBSegment | null {
|
|
105
|
-
const item =
|
|
104
|
+
const item = localStorage.getItem(this.keys.buildRBSegmentKey(name));
|
|
106
105
|
return item && JSON.parse(item);
|
|
107
106
|
}
|
|
108
107
|
|
|
108
|
+
getAll(): IRBSegment[] {
|
|
109
|
+
return this.getNames().map(key => this.get(key)!);
|
|
110
|
+
}
|
|
111
|
+
|
|
109
112
|
contains(names: Set<string>): boolean {
|
|
110
113
|
const namesArray = setToArray(names);
|
|
111
114
|
const namesInStorage = this.getNames();
|
|
@@ -114,7 +117,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
114
117
|
|
|
115
118
|
getChangeNumber(): number {
|
|
116
119
|
const n = -1;
|
|
117
|
-
let value: string | number | null =
|
|
120
|
+
let value: string | number | null = localStorage.getItem(this.keys.buildRBSegmentsTillKey());
|
|
118
121
|
|
|
119
122
|
if (value !== null) {
|
|
120
123
|
value = parseInt(value, 10);
|
|
@@ -126,7 +129,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
usesSegments(): boolean {
|
|
129
|
-
const storedCount =
|
|
132
|
+
const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
130
133
|
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
131
134
|
|
|
132
135
|
return isFiniteNumber(splitsWithSegmentsCount) ?
|