@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.
Files changed (74) hide show
  1. package/CHANGES.txt +3 -11
  2. package/cjs/evaluator/convertions/index.js +9 -1
  3. package/cjs/evaluator/matchersTransform/index.js +2 -3
  4. package/cjs/sdkClient/sdkClientMethodCS.js +5 -1
  5. package/cjs/sdkFactory/index.js +8 -2
  6. package/cjs/storages/getRolloutPlan.js +69 -0
  7. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
  8. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +21 -17
  9. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +37 -33
  10. package/cjs/storages/inLocalStorage/index.js +13 -31
  11. package/cjs/storages/inLocalStorage/validateCache.js +25 -30
  12. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -0
  13. package/cjs/storages/setRolloutPlan.js +66 -0
  14. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  15. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +0 -2
  16. package/cjs/sync/polling/updaters/splitChangesUpdater.js +0 -2
  17. package/cjs/sync/syncManagerOnline.js +24 -28
  18. package/cjs/utils/env/isLocalStorageAvailable.js +5 -28
  19. package/cjs/utils/inputValidation/index.js +1 -3
  20. package/cjs/utils/settingsValidation/index.js +4 -0
  21. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  22. package/esm/evaluator/convertions/index.js +7 -0
  23. package/esm/evaluator/matchersTransform/index.js +3 -4
  24. package/esm/sdkClient/sdkClientMethodCS.js +5 -1
  25. package/esm/sdkFactory/index.js +8 -2
  26. package/esm/storages/getRolloutPlan.js +65 -0
  27. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +16 -16
  28. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +21 -17
  29. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +37 -33
  30. package/esm/storages/inLocalStorage/index.js +14 -32
  31. package/esm/storages/inLocalStorage/validateCache.js +25 -30
  32. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +4 -0
  33. package/esm/storages/setRolloutPlan.js +61 -0
  34. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  35. package/esm/sync/polling/updaters/mySegmentsUpdater.js +0 -2
  36. package/esm/sync/polling/updaters/splitChangesUpdater.js +0 -2
  37. package/esm/sync/syncManagerOnline.js +24 -28
  38. package/esm/utils/env/isLocalStorageAvailable.js +3 -24
  39. package/esm/utils/inputValidation/index.js +0 -1
  40. package/esm/utils/settingsValidation/index.js +4 -0
  41. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  42. package/package.json +1 -1
  43. package/src/evaluator/convertions/index.ts +10 -0
  44. package/src/evaluator/matchersTransform/index.ts +3 -4
  45. package/src/sdkClient/sdkClientMethodCS.ts +7 -1
  46. package/src/sdkFactory/index.ts +12 -4
  47. package/src/storages/getRolloutPlan.ts +72 -0
  48. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +17 -18
  49. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +22 -19
  50. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +37 -34
  51. package/src/storages/inLocalStorage/index.ts +16 -37
  52. package/src/storages/inLocalStorage/validateCache.ts +25 -31
  53. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +4 -0
  54. package/src/storages/setRolloutPlan.ts +71 -0
  55. package/src/storages/types.ts +25 -23
  56. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +2 -1
  57. package/src/sync/polling/updaters/mySegmentsUpdater.ts +0 -2
  58. package/src/sync/polling/updaters/splitChangesUpdater.ts +1 -3
  59. package/src/sync/syncManagerOnline.ts +22 -27
  60. package/src/types.ts +2 -35
  61. package/src/utils/env/isLocalStorageAvailable.ts +3 -24
  62. package/src/utils/inputValidation/index.ts +0 -1
  63. package/src/utils/settingsValidation/index.ts +4 -0
  64. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  65. package/types/splitio.d.ts +36 -44
  66. package/cjs/storages/dataLoader.js +0 -50
  67. package/cjs/storages/inLocalStorage/storageAdapter.js +0 -54
  68. package/cjs/utils/inputValidation/preloadedData.js +0 -59
  69. package/esm/storages/dataLoader.js +0 -46
  70. package/esm/storages/inLocalStorage/storageAdapter.js +0 -50
  71. package/esm/utils/inputValidation/preloadedData.js +0 -55
  72. package/src/storages/dataLoader.ts +0 -55
  73. package/src/storages/inLocalStorage/storageAdapter.ts +0 -62
  74. package/src/utils/inputValidation/preloadedData.ts +0 -57
@@ -31,8 +31,6 @@ export function mySegmentsUpdaterFactory(log, mySegmentsFetcher, storage, segmen
31
31
  shouldNotifyUpdate = segments.resetSegments(segmentsData.ms || {});
32
32
  shouldNotifyUpdate = largeSegments.resetSegments(segmentsData.ls || {}) || shouldNotifyUpdate;
33
33
  }
34
- if (storage.save)
35
- storage.save();
36
34
  // Notify update if required
37
35
  if (usesSegmentsSync(storage) && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
38
36
  readyOnAlreadyExistentState = false;
@@ -149,8 +149,6 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, storage, sp
149
149
  segments.registerSegments(setToArray(usedSegments))
150
150
  ]).then(function (_a) {
151
151
  var ffChanged = _a[0], rbsChanged = _a[1];
152
- if (storage.save)
153
- storage.save();
154
152
  if (splitsEventEmitter) {
155
153
  // To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
156
154
  return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
@@ -66,39 +66,35 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
66
66
  */
67
67
  start: function () {
68
68
  running = true;
69
- // @TODO once event, impression and telemetry storages support persistence, call when `validateCache` promise is resolved
70
- submitterManager.start(!isConsentGranted(settings));
71
- return Promise.resolve(storage.validateCache ? storage.validateCache() : false).then(function (isCacheLoaded) {
72
- if (!running)
73
- return;
74
- if (startFirstTime) {
75
- // Emits SDK_READY_FROM_CACHE
76
- if (isCacheLoaded)
77
- readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
78
- }
79
- // start syncing splits and segments
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
- startFirstTime = false;
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
- wrapper.setItem(mod, mod);
14
- wrapper.getItem(mod);
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
- }
@@ -10,5 +10,4 @@ export { validateTrafficType } from './trafficType';
10
10
  export { validateIfNotDestroyed, validateIfOperational } from './isOperational';
11
11
  export { validateSplitExistence } from './splitExistence';
12
12
  export { validateTrafficTypeExistence } from './trafficTypeExistence';
13
- export { validatePreloadedData } from './preloadedData';
14
13
  export { validateEvaluationOptions } from './eventProperties';
@@ -5,6 +5,7 @@ import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE, DEBUG, FLAG_SPEC_VERSION }
5
5
  import { validImpressionsMode } from './impressionsMode';
6
6
  import { validateKey } from '../inputValidation/key';
7
7
  import { ERROR_MIN_CONFIG_PARAM, LOG_PREFIX_CLIENT_INSTANTIATION } from '../../logger/constants';
8
+ import { validateRolloutPlan } from '../../storages/setRolloutPlan';
8
9
  // Exported for telemetry
9
10
  export var base = {
10
11
  // Define which kind of object you want to retrieve from SplitFactory
@@ -128,6 +129,9 @@ export function settingsValidation(config, validationParams) {
128
129
  // @ts-ignore, modify readonly prop
129
130
  if (storage)
130
131
  withDefaults.storage = storage(withDefaults);
132
+ // @ts-ignore, modify readonly prop
133
+ if (withDefaults.initialRolloutPlan)
134
+ withDefaults.initialRolloutPlan = validateRolloutPlan(log, withDefaults);
131
135
  // Validate key and TT (for client-side)
132
136
  var maybeKey = withDefaults.core.key;
133
137
  if (validationParams.acceptKey) {
@@ -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 true; }; // to emit SDK_READY_FROM_CACHE
7
7
  return result;
8
8
  }
9
9
  __InLocalStorageMockFactory.type = STORAGE_MEMORY;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.4.2-rc.3",
3
+ "version": "2.5.0-rc.1",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -1,3 +1,5 @@
1
+ import { IBetweenMatcherData } from '../../dtos/types';
2
+
1
3
  export function zeroSinceHH(millisSinceEpoch: number): number {
2
4
  return new Date(millisSinceEpoch).setUTCHours(0, 0, 0, 0);
3
5
  }
@@ -5,3 +7,11 @@ export function zeroSinceHH(millisSinceEpoch: number): number {
5
7
  export function zeroSinceSS(millisSinceEpoch: number): number {
6
8
  return new Date(millisSinceEpoch).setUTCSeconds(0, 0);
7
9
  }
10
+
11
+ export function betweenDateTimeTransform(betweenMatcherData: IBetweenMatcherData): IBetweenMatcherData {
12
+ return {
13
+ dataType: betweenMatcherData.dataType,
14
+ start: zeroSinceSS(betweenMatcherData.start),
15
+ end: zeroSinceSS(betweenMatcherData.end)
16
+ };
17
+ }
@@ -3,7 +3,7 @@ import { matcherTypes, matcherTypesMapper, matcherDataTypes } from '../matchers/
3
3
  import { segmentTransform } from './segment';
4
4
  import { whitelistTransform } from './whitelist';
5
5
  import { numericTransform } from './unaryNumeric';
6
- import { zeroSinceHH, zeroSinceSS } from '../convertions';
6
+ import { zeroSinceHH, zeroSinceSS, betweenDateTimeTransform } from '../convertions';
7
7
  import { IBetweenMatcherData, IInLargeSegmentMatcherData, IInSegmentMatcherData, ISplitMatcher, IUnaryNumericMatcherData } from '../../dtos/types';
8
8
  import { IMatcherDto } from '../types';
9
9
 
@@ -32,7 +32,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
32
32
  let type = matcherTypesMapper(matcherType);
33
33
  // As default input data type we use string (even for ALL_KEYS)
34
34
  let dataType = matcherDataTypes.STRING;
35
- let value = undefined;
35
+ let value;
36
36
 
37
37
  if (type === matcherTypes.IN_SEGMENT) {
38
38
  value = segmentTransform(userDefinedSegmentMatcherData as IInSegmentMatcherData);
@@ -60,8 +60,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
60
60
  dataType = matcherDataTypes.NUMBER;
61
61
 
62
62
  if (value.dataType === 'DATETIME') {
63
- value.start = zeroSinceSS(value.start);
64
- value.end = zeroSinceSS(value.end);
63
+ value = betweenDateTimeTransform(value);
65
64
  dataType = matcherDataTypes.DATETIME;
66
65
  }
67
66
  } else if (type === matcherTypes.BETWEEN_SEMVER) {
@@ -9,13 +9,15 @@ import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, L
9
9
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10
10
  import { ISdkFactoryContext } from '../sdkFactory/types';
11
11
  import { buildInstanceId } from './identity';
12
+ import { setRolloutPlan } from '../storages/setRolloutPlan';
13
+ import { ISegmentsCacheSync } from '../storages/types';
12
14
 
13
15
  /**
14
16
  * Factory of client method for the client-side API variant where TT is ignored.
15
17
  * Therefore, clients don't have a bound TT for the track method.
16
18
  */
17
19
  export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey) => SplitIO.IBrowserClient {
18
- const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
20
+ const { clients, storage, syncManager, sdkReadinessManager, settings: { core: { key }, log, initialRolloutPlan } } = params;
19
21
 
20
22
  const mainClientInstance = clientCSDecorator(
21
23
  log,
@@ -56,6 +58,10 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
56
58
  sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
57
59
  });
58
60
 
61
+ if (sharedStorage && initialRolloutPlan) {
62
+ setRolloutPlan(log, initialRolloutPlan, { segments: sharedStorage.segments as ISegmentsCacheSync, largeSegments: sharedStorage.largeSegments as ISegmentsCacheSync }, matchingKey);
63
+ }
64
+
59
65
  // 3 possibilities:
60
66
  // - Standalone mode: both syncManager and sharedSyncManager are defined
61
67
  // - Consumer mode: both syncManager and sharedSyncManager are undefined
@@ -14,6 +14,9 @@ import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized
14
14
  import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
15
15
  import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
16
16
  import { DEBUG, OPTIMIZED } from '../utils/constants';
17
+ import { setRolloutPlan } from '../storages/setRolloutPlan';
18
+ import { IStorageSync } from '../storages/types';
19
+ import { getMatching } from '../utils/key';
17
20
 
18
21
  /**
19
22
  * Modular SDK factory
@@ -24,7 +27,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
24
27
  syncManagerFactory, SignalListener, impressionsObserverFactory,
25
28
  integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
26
29
  filterAdapterFactory, lazyInit } = params;
27
- const { log, sync: { impressionsMode } } = settings;
30
+ const { log, sync: { impressionsMode }, initialRolloutPlan, core: { key } } = settings;
28
31
 
29
32
  // @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
30
33
  // On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
@@ -43,7 +46,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
43
46
 
44
47
  const storage = storageFactory({
45
48
  settings,
46
- onReadyCb: (error) => {
49
+ onReadyCb(error) {
47
50
  if (error) {
48
51
  // If storage fails to connect, SDK_READY_TIMED_OUT event is emitted immediately. Review when timeout and non-recoverable errors are reworked
49
52
  readiness.timeout();
@@ -52,11 +55,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
52
55
  readiness.splits.emit(SDK_SPLITS_ARRIVED);
53
56
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
54
57
  },
55
- onReadyFromCacheCb: () => {
58
+ onReadyFromCacheCb() {
56
59
  readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
57
60
  }
58
61
  });
59
- // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
62
+
63
+ if (initialRolloutPlan) {
64
+ setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
65
+ if ((storage as IStorageSync).splits.getChangeNumber() > -1) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
66
+ }
67
+
60
68
  const clients: Record<string, SplitIO.IBasicClient> = {};
61
69
  const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
62
70
  const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
@@ -0,0 +1,72 @@
1
+ import SplitIO from '../../types/splitio';
2
+ import { IStorageSync } from './types';
3
+ import { setToArray } from '../utils/lang/sets';
4
+ import { getMatching } from '../utils/key';
5
+ import { ILogger } from '../logger/types';
6
+ import { RolloutPlan } from './types';
7
+ import { IMembershipsResponse, IMySegmentsResponse } from '../dtos/types';
8
+
9
+ /**
10
+ * Gets the rollout plan snapshot from the given synchronous storage.
11
+ */
12
+ export function getRolloutPlan(log: ILogger, storage: IStorageSync, options: SplitIO.RolloutPlanOptions = {}): RolloutPlan {
13
+
14
+ const { keys, exposeSegments } = options;
15
+ const { splits, segments, rbSegments } = storage;
16
+
17
+ log.debug(`storage: get feature flags${keys ? `, and memberships for keys ${keys}` : ''}${exposeSegments ? ', and segments' : ''}`);
18
+
19
+ return {
20
+ splitChanges: {
21
+ ff: {
22
+ t: splits.getChangeNumber(),
23
+ s: -1,
24
+ d: splits.getAll(),
25
+ },
26
+ rbs: {
27
+ t: rbSegments.getChangeNumber(),
28
+ s: -1,
29
+ d: rbSegments.getAll(),
30
+ }
31
+ },
32
+ segmentChanges: exposeSegments ? // @ts-ignore accessing private prop
33
+ Object.keys(segments.segmentCache).map(segmentName => ({
34
+ name: segmentName, // @ts-ignore
35
+ added: setToArray(segments.segmentCache[segmentName] as Set<string>),
36
+ removed: [],
37
+ since: -1,
38
+ till: segments.getChangeNumber(segmentName)!
39
+ })) :
40
+ undefined,
41
+ memberships: keys ?
42
+ keys.reduce<Record<string, IMembershipsResponse>>((prev, key) => {
43
+ const matchingKey = getMatching(key);
44
+ if (storage.shared) { // Client-side segments
45
+ const sharedStorage = storage.shared(matchingKey);
46
+ prev[matchingKey] = {
47
+ ms: { // @ts-ignore
48
+ k: Object.keys(sharedStorage.segments.segmentCache).map(segmentName => ({ n: segmentName })),
49
+ },
50
+ ls: sharedStorage.largeSegments ? { // @ts-ignore
51
+ k: Object.keys(sharedStorage.largeSegments.segmentCache).map(segmentName => ({ n: segmentName })),
52
+ } : undefined
53
+ };
54
+ } else { // Server-side segments
55
+ prev[matchingKey] = {
56
+ ms: { // @ts-ignore
57
+ k: Object.keys(storage.segments.segmentCache).reduce<IMySegmentsResponse['k']>((prev, segmentName) => { // @ts-ignore
58
+ return storage.segments.segmentCache[segmentName].has(matchingKey) ?
59
+ prev!.concat({ n: segmentName }) :
60
+ prev;
61
+ }, [])
62
+ },
63
+ ls: {
64
+ k: []
65
+ }
66
+ };
67
+ }
68
+ return prev;
69
+ }, {}) :
70
+ undefined
71
+ };
72
+ }
@@ -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, storage: StorageAdapter) {
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 (this.storage.getItem(segmentKey) === DEFINED) return false;
27
- this.storage.setItem(segmentKey, DEFINED);
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 (this.storage.getItem(segmentKey) !== DEFINED) return false;
40
- this.storage.removeItem(segmentKey);
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 this.storage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
46
+ return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
50
47
  }
51
48
 
52
49
  getRegisteredSegments(): string[] {
53
- const registeredSegments: string[] = [];
54
- for (let i = 0, len = this.storage.length; i < len; i++) {
55
- const segmentName = this.keys.extractSegmentName(this.storage.key(i)!);
56
- if (segmentName) registeredSegments.push(segmentName);
57
- }
58
- return registeredSegments;
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) this.storage.setItem(this.keys.buildTillKey(), changeNumber + '');
68
- else this.storage.removeItem(this.keys.buildTillKey());
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 = this.storage.getItem(this.keys.buildTillKey());
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, StorageAdapter } from '../types';
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, storage: StorageAdapter) {
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
- this.storage.removeItem(this.keys.buildRBSegmentsTillKey());
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
- this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
37
- this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
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(this.storage.getItem(segmentsCountKey)) + diff;
46
- if (count > 0) this.storage.setItem(segmentsCountKey, count + '');
47
- else this.storage.removeItem(segmentsCountKey);
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 rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
55
- const previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
53
+ const rbSegmentFromLocalStorage = localStorage.getItem(rbSegmentKey);
54
+ const previous = rbSegmentFromLocalStorage ? JSON.parse(rbSegmentFromLocalStorage) : null;
56
55
 
57
- this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
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
- this.storage.removeItem(this.keys.buildRBSegmentKey(name));
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 = this.storage.length;
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 = this.storage.key(cur);
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 = this.storage.getItem(this.keys.buildRBSegmentKey(name));
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 = this.storage.getItem(this.keys.buildRBSegmentsTillKey());
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 = this.storage.getItem(this.keys.buildSplitsWithSegmentCountKey());
132
+ const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
130
133
  const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
131
134
 
132
135
  return isFiniteNumber(splitsWithSegmentsCount) ?