@splitsoftware/splitio-commons 1.16.1-rc.1 → 1.16.1-rc.11

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 (144) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/logger/constants.js +5 -4
  3. package/cjs/logger/messages/info.js +2 -1
  4. package/cjs/logger/messages/warn.js +1 -1
  5. package/cjs/readiness/readinessManager.js +7 -12
  6. package/cjs/services/splitApi.js +5 -9
  7. package/cjs/storages/AbstractSegmentsCacheSync.js +41 -12
  8. package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
  9. package/cjs/storages/AbstractSplitsCacheSync.js +7 -5
  10. package/cjs/storages/KeyBuilder.js +0 -3
  11. package/cjs/storages/KeyBuilderCS.js +6 -0
  12. package/cjs/storages/dataLoader.js +1 -1
  13. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
  14. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +4 -16
  15. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
  16. package/cjs/storages/inMemory/SplitsCacheInMemory.js +6 -15
  17. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
  18. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  19. package/cjs/sync/polling/pollingManagerCS.js +33 -51
  20. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  21. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +14 -20
  22. package/cjs/sync/streaming/AuthClient/index.js +1 -1
  23. package/cjs/sync/streaming/SSEHandler/index.js +3 -6
  24. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -9
  25. package/cjs/sync/streaming/constants.js +3 -4
  26. package/cjs/sync/streaming/parseUtils.js +14 -9
  27. package/cjs/sync/streaming/pushManager.js +29 -55
  28. package/cjs/sync/submitters/telemetrySubmitter.js +0 -2
  29. package/cjs/sync/syncManagerOnline.js +14 -24
  30. package/cjs/utils/constants/index.js +4 -5
  31. package/cjs/utils/settingsValidation/index.js +1 -5
  32. package/esm/logger/constants.js +2 -1
  33. package/esm/logger/messages/info.js +2 -1
  34. package/esm/logger/messages/warn.js +1 -1
  35. package/esm/readiness/readinessManager.js +7 -12
  36. package/esm/services/splitApi.js +6 -10
  37. package/esm/storages/AbstractSegmentsCacheSync.js +41 -12
  38. package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
  39. package/esm/storages/AbstractSplitsCacheSync.js +5 -3
  40. package/esm/storages/KeyBuilder.js +0 -3
  41. package/esm/storages/KeyBuilderCS.js +6 -0
  42. package/esm/storages/dataLoader.js +1 -1
  43. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
  44. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -17
  45. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
  46. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -16
  47. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
  48. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  49. package/esm/sync/polling/pollingManagerCS.js +34 -52
  50. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  51. package/esm/sync/polling/updaters/mySegmentsUpdater.js +12 -18
  52. package/esm/sync/streaming/AuthClient/index.js +1 -1
  53. package/esm/sync/streaming/SSEHandler/index.js +4 -7
  54. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -9
  55. package/esm/sync/streaming/constants.js +2 -3
  56. package/esm/sync/streaming/parseUtils.js +12 -8
  57. package/esm/sync/streaming/pushManager.js +32 -57
  58. package/esm/sync/submitters/telemetrySubmitter.js +0 -2
  59. package/esm/sync/syncManagerOnline.js +15 -25
  60. package/esm/utils/constants/index.js +2 -3
  61. package/esm/utils/settingsValidation/index.js +1 -5
  62. package/package.json +1 -1
  63. package/src/dtos/types.ts +7 -8
  64. package/src/logger/constants.ts +2 -1
  65. package/src/logger/messages/info.ts +2 -1
  66. package/src/logger/messages/warn.ts +1 -1
  67. package/src/readiness/readinessManager.ts +7 -9
  68. package/src/readiness/types.ts +0 -1
  69. package/src/services/splitApi.ts +7 -12
  70. package/src/services/splitHttpClient.ts +1 -1
  71. package/src/services/types.ts +2 -3
  72. package/src/storages/AbstractSegmentsCacheSync.ts +53 -12
  73. package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
  74. package/src/storages/AbstractSplitsCacheSync.ts +7 -5
  75. package/src/storages/KeyBuilder.ts +0 -3
  76. package/src/storages/KeyBuilderCS.ts +9 -0
  77. package/src/storages/dataLoader.ts +1 -1
  78. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +26 -56
  79. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +5 -20
  80. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +10 -44
  81. package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -13
  82. package/src/storages/types.ts +10 -8
  83. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +7 -14
  84. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
  85. package/src/sync/polling/fetchers/types.ts +2 -2
  86. package/src/sync/polling/pollingManagerCS.ts +29 -61
  87. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +12 -13
  88. package/src/sync/polling/types.ts +8 -8
  89. package/src/sync/polling/updaters/mySegmentsUpdater.ts +17 -18
  90. package/src/sync/streaming/AuthClient/index.ts +1 -1
  91. package/src/sync/streaming/SSEClient/index.ts +4 -6
  92. package/src/sync/streaming/SSEHandler/index.ts +5 -9
  93. package/src/sync/streaming/SSEHandler/types.ts +13 -25
  94. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +17 -12
  95. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
  96. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
  97. package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
  98. package/src/sync/streaming/constants.ts +2 -3
  99. package/src/sync/streaming/parseUtils.ts +19 -11
  100. package/src/sync/streaming/pushManager.ts +38 -68
  101. package/src/sync/streaming/types.ts +11 -13
  102. package/src/sync/submitters/telemetrySubmitter.ts +0 -2
  103. package/src/sync/submitters/types.ts +3 -6
  104. package/src/sync/syncManagerOnline.ts +11 -19
  105. package/src/types.ts +1 -26
  106. package/src/utils/constants/index.ts +2 -3
  107. package/src/utils/settingsValidation/index.ts +1 -5
  108. package/types/dtos/types.d.ts +7 -8
  109. package/types/logger/constants.d.ts +2 -1
  110. package/types/readiness/types.d.ts +0 -1
  111. package/types/services/decorateHeaders.d.ts +2 -0
  112. package/types/services/splitApi.d.ts +1 -1
  113. package/types/services/splitHttpClient.d.ts +1 -1
  114. package/types/services/types.d.ts +2 -3
  115. package/types/storages/AbstractSegmentsCacheSync.d.ts +9 -11
  116. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  117. package/types/storages/AbstractSplitsCacheSync.d.ts +4 -4
  118. package/types/storages/KeyBuilder.d.ts +0 -1
  119. package/types/storages/KeyBuilderCS.d.ts +2 -0
  120. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -12
  121. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  122. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -9
  123. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -2
  124. package/types/storages/types.d.ts +8 -7
  125. package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
  126. package/types/sync/polling/fetchers/types.d.ts +2 -2
  127. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +4 -3
  128. package/types/sync/polling/types.d.ts +8 -12
  129. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +3 -2
  130. package/types/sync/streaming/SSEHandler/types.d.ts +13 -22
  131. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -3
  132. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  133. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  134. package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
  135. package/types/sync/streaming/constants.d.ts +2 -3
  136. package/types/sync/streaming/parseUtils.d.ts +4 -5
  137. package/types/sync/streaming/pushManager.d.ts +0 -2
  138. package/types/sync/streaming/pushManagerCS_Spec1_3.d.ts +9 -0
  139. package/types/sync/streaming/pushManager_Spec1_3.d.ts +9 -0
  140. package/types/sync/streaming/types.d.ts +9 -10
  141. package/types/sync/submitters/types.d.ts +3 -6
  142. package/types/types.d.ts +0 -25
  143. package/types/utils/constants/index.d.ts +2 -3
  144. package/types/utils/settingsValidation/index.d.ts +0 -2
@@ -12,7 +12,7 @@ const messageNoFetch = 'Global fetch API is not available.';
12
12
  * @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
13
13
  * @param platform object containing environment-specific dependencies
14
14
  */
15
- export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: IPlatform): ISplitHttpClient {
15
+ export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: Pick<IPlatform, 'getOptions' | 'getFetch'>): ISplitHttpClient {
16
16
 
17
17
  const { log, core: { authorizationKey }, version, runtime: { ip, hostname } } = settings;
18
18
  const options = getOptions && getOptions(settings);
@@ -39,7 +39,7 @@ export type IFetchSplitChanges = (since: number, noCache?: boolean, till?: numbe
39
39
 
40
40
  export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>
41
41
 
42
- export type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>
42
+ export type IFetchMemberships = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>
43
43
 
44
44
  export type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
45
45
 
@@ -61,8 +61,7 @@ export interface ISplitApi {
61
61
  fetchAuth: IFetchAuth
62
62
  fetchSplitChanges: IFetchSplitChanges
63
63
  fetchSegmentChanges: IFetchSegmentChanges
64
- fetchMySegments: IFetchMySegments
65
- fetchMyLargeSegments: IFetchMySegments
64
+ fetchMemberships: IFetchMemberships
66
65
  postEventsBulk: IPostEventsBulk
67
66
  postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
68
67
  postUniqueKeysBulkSs: IPostUniqueKeysBulkSs
@@ -1,5 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
2
  /* eslint-disable no-unused-vars */
3
+ import { IMySegmentsResponse } from '../dtos/types';
4
+ import { MySegmentsData } from '../sync/polling/types';
3
5
  import { ISegmentsCacheSync } from './types';
4
6
 
5
7
  /**
@@ -28,7 +30,9 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
28
30
  /**
29
31
  * clear the cache.
30
32
  */
31
- abstract clear(): void
33
+ clear() {
34
+ this.resetSegments({});
35
+ }
32
36
 
33
37
  /**
34
38
  * For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
@@ -49,20 +53,57 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
49
53
  abstract getKeysCount(): number
50
54
 
51
55
  /**
52
- * For server-side synchronizer: set the change number of `name` segment.
53
- * For client-side synchronizer: the method is not used.
54
- */
55
- setChangeNumber(name: string, changeNumber: number): boolean { return true; }
56
-
57
- /**
58
- * For server-side synchronizer: get the change number of `name` segment.
59
- * For client-side synchronizer: the method is not used.
56
+ * For server-side synchronizer: change number of `name` segment.
57
+ * For client-side synchronizer: change number of mySegments.
60
58
  */
61
- getChangeNumber(name: string): number { return -1; }
59
+ abstract setChangeNumber(name?: string, changeNumber?: number): boolean | void
60
+ abstract getChangeNumber(name: string): number
62
61
 
63
62
  /**
64
63
  * For server-side synchronizer: the method is not used.
65
- * For client-side synchronizer: reset the cache with the given list of segments.
64
+ * For client-side synchronizer: it resets or updates the cache.
66
65
  */
67
- resetSegments(names: string[]): boolean { return true; }
66
+ resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
67
+ this.setChangeNumber(undefined, segmentsData.cn);
68
+
69
+ const { added, removed } = segmentsData as MySegmentsData;
70
+
71
+ if (added && removed) {
72
+ let isDiff = false;
73
+
74
+ added.forEach(segment => {
75
+ isDiff = this.addToSegment(segment) || isDiff;
76
+ });
77
+
78
+ removed.forEach(segment => {
79
+ isDiff = this.removeFromSegment(segment) || isDiff;
80
+ });
81
+
82
+ return isDiff;
83
+ }
84
+
85
+ const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
86
+ const storedSegmentKeys = this.getRegisteredSegments().sort();
87
+
88
+ // Extreme fast => everything is empty
89
+ if (!names.length && !storedSegmentKeys.length) return false;
90
+
91
+ let index = 0;
92
+
93
+ while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
94
+
95
+ // Quick path => no changes
96
+ if (index === names.length && index === storedSegmentKeys.length) return false;
97
+
98
+ // Slowest path => add and/or remove segments
99
+ for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
100
+ this.removeFromSegment(storedSegmentKeys[removeIndex]);
101
+ }
102
+
103
+ for (let addIndex = index; addIndex < names.length; addIndex++) {
104
+ this.addToSegment(names[addIndex]);
105
+ }
106
+
107
+ return true;
108
+ }
68
109
  }
@@ -22,9 +22,9 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
22
22
  abstract trafficTypeExists(trafficType: string): Promise<boolean>
23
23
  abstract clear(): Promise<boolean | void>
24
24
 
25
- // @TODO revisit segment-related methods ('usesMatcher', 'getRegisteredSegments', 'registerSegments')
25
+ // @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
26
26
  // noop, just keeping the interface. This is used by standalone client-side API only, and so only implemented by InMemory and InLocalStorage.
27
- usesMatcher(): Promise<boolean> {
27
+ usesSegments(): Promise<boolean> {
28
28
  return Promise.resolve(true);
29
29
  }
30
30
 
@@ -2,6 +2,7 @@ import { ISplitsCacheSync } from './types';
2
2
  import { ISplit } from '../dtos/types';
3
3
  import { objectAssign } from '../utils/lang/objectAssign';
4
4
  import { ISet } from '../utils/lang/sets';
5
+ import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
5
6
 
6
7
  /**
7
8
  * This class provides a skeletal implementation of the ISplitsCacheSync interface
@@ -31,7 +32,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
31
32
  return splits;
32
33
  }
33
34
 
34
- abstract setChangeNumber(changeNumber: number): boolean
35
+ abstract setChangeNumber(changeNumber: number): boolean | void
35
36
 
36
37
  abstract getChangeNumber(): number
37
38
 
@@ -43,7 +44,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
43
44
 
44
45
  abstract trafficTypeExists(trafficType: string): boolean
45
46
 
46
- abstract usesMatcher(matcherType: string): boolean
47
+ abstract usesSegments(): boolean
47
48
 
48
49
  abstract clear(): void
49
50
 
@@ -85,15 +86,16 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
85
86
 
86
87
  /**
87
88
  * Given a parsed split, it returns a boolean flagging if its conditions use segments matchers (rules & whitelists).
88
- * This util is intended to simplify the implementation of `splitsCache::usesMatcher` method
89
+ * This util is intended to simplify the implementation of `splitsCache::usesSegments` method
89
90
  */
90
- export function usesMatcher(split: ISplit, matcherType: string) {
91
+ export function usesSegments(split: ISplit) {
91
92
  const conditions = split.conditions || [];
92
93
  for (let i = 0; i < conditions.length; i++) {
93
94
  const matchers = conditions[i].matcherGroup.matchers;
94
95
 
95
96
  for (let j = 0; j < matchers.length; j++) {
96
- if (matchers[j].matcherType === matcherType) return true;
97
+ const matcher = matchers[j].matcherType;
98
+ if (matcher === IN_SEGMENT || matcher === IN_LARGE_SEGMENT) return true;
97
99
  }
98
100
  }
99
101
 
@@ -51,9 +51,6 @@ export class KeyBuilder {
51
51
  buildSplitsWithSegmentCountKey() {
52
52
  return `${this.prefix}.splits.usingSegments`;
53
53
  }
54
- buildSplitsWithLargeSegmentCountKey() {
55
- return `${this.prefix}.splits.usingLargeSegments`;
56
- }
57
54
 
58
55
  buildSegmentNameKey(segmentName: string) {
59
56
  return `${this.prefix}.segment.${segmentName}`;
@@ -5,6 +5,7 @@ export interface MySegmentsKeyBuilder {
5
5
  buildSegmentNameKey(segmentName: string): string;
6
6
  extractSegmentName(builtSegmentKeyName: string): string | undefined;
7
7
  extractOldSegmentKey(builtSegmentKeyName: string): string | undefined;
8
+ buildTillKey(): string;
8
9
  }
9
10
 
10
11
  export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
@@ -47,6 +48,10 @@ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
47
48
  isSplitsCacheKey(key: string) {
48
49
  return this.regexSplitsCacheKey.test(key);
49
50
  }
51
+
52
+ buildTillKey() {
53
+ return `${this.prefix}.${this.matchingKey}.segments.till`;
54
+ }
50
55
  }
51
56
 
52
57
  export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string): MySegmentsKeyBuilder {
@@ -63,6 +68,10 @@ export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string):
63
68
 
64
69
  extractOldSegmentKey() {
65
70
  return undefined;
71
+ },
72
+
73
+ buildTillKey() {
74
+ return `${prefix}.${matchingKey}.largeSegments.till`;
66
75
  }
67
76
  };
68
77
  }
@@ -50,6 +50,6 @@ export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoa
50
50
  return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
51
51
  });
52
52
  }
53
- storage.segments.resetSegments(mySegmentsData);
53
+ storage.segments.resetSegments({ k: mySegmentsData.map(s => ({ n: s })) });
54
54
  };
55
55
  }
@@ -1,4 +1,5 @@
1
1
  import { ILogger } from '../../logger/types';
2
+ import { isNaNNumber } from '../../utils/lang';
2
3
  import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
3
4
  import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
4
5
  import { LOG_PREFIX, DEFINED } from './constants';
@@ -15,22 +16,11 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
15
16
  // There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
16
17
  }
17
18
 
18
- /**
19
- * Removes list of segments from localStorage
20
- * @NOTE this method is not being used at the moment.
21
- */
22
- clear() {
23
- this.log.info(LOG_PREFIX + 'Flushing MySegments data from localStorage');
24
-
25
- // We cannot simply call `localStorage.clear()` since that implies removing user items from the storage
26
- // We could optimize next sentence, since it implies iterating over all localStorage items
27
- this.resetSegments([]);
28
- }
29
-
30
19
  addToSegment(name: string): boolean {
31
20
  const segmentKey = this.keys.buildSegmentNameKey(name);
32
21
 
33
22
  try {
23
+ if (localStorage.getItem(segmentKey) === DEFINED) return false;
34
24
  localStorage.setItem(segmentKey, DEFINED);
35
25
  return true;
36
26
  } catch (e) {
@@ -43,6 +33,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
43
33
  const segmentKey = this.keys.buildSegmentNameKey(name);
44
34
 
45
35
  try {
36
+ if (localStorage.getItem(segmentKey) !== DEFINED) return false;
46
37
  localStorage.removeItem(segmentKey);
47
38
  return true;
48
39
  } catch (e) {
@@ -55,31 +46,22 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
55
46
  return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
56
47
  }
57
48
 
58
- /**
59
- * Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
60
- *
61
- * @param {string[]} segmentNames list of segment names
62
- * @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
63
- */
64
- resetSegments(names: string[]): boolean {
65
- let isDiff = false;
66
- let index;
67
-
49
+ getRegisteredSegments(): string[] {
68
50
  // Scan current values from localStorage
69
- const storedSegmentNames = Object.keys(localStorage).reduce((accum, key) => {
51
+ return Object.keys(localStorage).reduce((accum, key) => {
70
52
  let segmentName = this.keys.extractSegmentName(key);
71
53
 
72
54
  if (segmentName) {
73
55
  accum.push(segmentName);
74
56
  } else {
75
- // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block and reuse `getRegisteredSegments` method.
57
+ // @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block
76
58
  segmentName = this.keys.extractOldSegmentKey(key);
77
59
 
78
60
  if (segmentName) { // this was an old segment key, let's clean up.
79
61
  const newSegmentKey = this.keys.buildSegmentNameKey(segmentName);
80
62
  try {
81
63
  // If the new format key is not there, create it.
82
- if (!localStorage.getItem(newSegmentKey) && names.indexOf(segmentName) > -1) {
64
+ if (!localStorage.getItem(newSegmentKey)) {
83
65
  localStorage.setItem(newSegmentKey, DEFINED);
84
66
  // we are migrating a segment, let's track it.
85
67
  accum.push(segmentName);
@@ -93,44 +75,32 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
93
75
 
94
76
  return accum;
95
77
  }, [] as string[]);
78
+ }
96
79
 
97
- // Extreme fast => everything is empty
98
- if (names.length === 0 && storedSegmentNames.length === names.length)
99
- return isDiff;
80
+ getKeysCount() {
81
+ return 1;
82
+ }
100
83
 
101
- // Quick path
102
- if (storedSegmentNames.length !== names.length) {
103
- isDiff = true;
84
+ setChangeNumber(name?: string, changeNumber?: number) {
85
+ try {
86
+ if (changeNumber) localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
87
+ else localStorage.removeItem(this.keys.buildTillKey());
88
+ } catch (e) {
89
+ this.log.error(e);
90
+ }
91
+ }
104
92
 
105
- storedSegmentNames.forEach(name => this.removeFromSegment(name));
106
- names.forEach(name => this.addToSegment(name));
107
- } else {
108
- // Slowest path => we need to find at least 1 difference because
109
- for (index = 0; index < names.length && storedSegmentNames.indexOf(names[index]) !== -1; index++) {
110
- // TODO: why empty statement?
111
- }
93
+ getChangeNumber() {
94
+ const n = -1;
95
+ let value: string | number | null = localStorage.getItem(this.keys.buildTillKey());
112
96
 
113
- if (index < names.length) {
114
- isDiff = true;
97
+ if (value !== null) {
98
+ value = parseInt(value, 10);
115
99
 
116
- storedSegmentNames.forEach(name => this.removeFromSegment(name));
117
- names.forEach(name => this.addToSegment(name));
118
- }
100
+ return isNaNNumber(value) ? n : value;
119
101
  }
120
102
 
121
- return isDiff;
122
- }
123
-
124
- getRegisteredSegments(): string[] {
125
- return Object.keys(localStorage).reduce<string[]>((accum, key) => {
126
- const segmentName = this.keys.extractSegmentName(key);
127
- if (segmentName) accum.push(segmentName);
128
- return accum;
129
- }, []);
130
- }
131
-
132
- getKeysCount() {
133
- return 1;
103
+ return n;
134
104
  }
135
105
 
136
106
  }
@@ -1,5 +1,5 @@
1
1
  import { ISplit } from '../../dtos/types';
2
- import { AbstractSplitsCacheSync, usesMatcher } from '../AbstractSplitsCacheSync';
2
+ import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
3
3
  import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
4
4
  import { KeyBuilderCS } from '../KeyBuilderCS';
5
5
  import { ILogger } from '../../logger/types';
@@ -7,7 +7,6 @@ import { LOG_PREFIX } from './constants';
7
7
  import { ISet, _Set, setToArray } from '../../utils/lang/sets';
8
8
  import { ISettings } from '../../types';
9
9
  import { getStorageHash } from '../KeyBuilder';
10
- import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
11
10
 
12
11
  /**
13
12
  * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
@@ -51,15 +50,10 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
51
50
  const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
52
51
  this._decrementCount(ttKey);
53
52
 
54
- if (usesMatcher(split, IN_SEGMENT)) {
53
+ if (usesSegments(split)) {
55
54
  const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
56
55
  this._decrementCount(segmentsCountKey);
57
56
  }
58
-
59
- if (usesMatcher(split, IN_LARGE_SEGMENT)) {
60
- const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
61
- this._decrementCount(segmentsCountKey);
62
- }
63
57
  }
64
58
  } catch (e) {
65
59
  this.log.error(LOG_PREFIX + e);
@@ -73,17 +67,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
73
67
  // @ts-expect-error
74
68
  localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
75
69
 
76
- if (usesMatcher(split, IN_SEGMENT)) {
70
+ if (usesSegments(split)) {
77
71
  const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
78
72
  // @ts-expect-error
79
73
  localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
80
74
  }
81
-
82
- if (usesMatcher(split, IN_LARGE_SEGMENT)) {
83
- const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
84
- // @ts-expect-error
85
- localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
86
- }
87
75
  }
88
76
  } catch (e) {
89
77
  this.log.error(LOG_PREFIX + e);
@@ -215,14 +203,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
215
203
  return isFiniteNumber(ttCount) && ttCount > 0;
216
204
  }
217
205
 
218
- usesMatcher(matcherType: string) {
206
+ usesSegments() {
219
207
  // If cache hasn't been synchronized with the cloud, assume we need them.
220
208
  if (!this.hasSync) return true;
221
209
 
222
- const storedCount = localStorage.getItem(matcherType === IN_SEGMENT ?
223
- this.keys.buildSplitsWithSegmentCountKey() :
224
- this.keys.buildSplitsWithLargeSegmentCountKey()
225
- );
210
+ const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
226
211
  const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
227
212
 
228
213
  if (isFiniteNumber(splitsWithSegmentsCount)) {
@@ -7,18 +7,19 @@ import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
7
7
  export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
8
8
 
9
9
  private segmentCache: Record<string, boolean> = {};
10
-
11
- clear() {
12
- this.segmentCache = {};
13
- }
10
+ private cn?: number;
14
11
 
15
12
  addToSegment(name: string): boolean {
13
+ if (this.segmentCache[name]) return false;
14
+
16
15
  this.segmentCache[name] = true;
17
16
 
18
17
  return true;
19
18
  }
20
19
 
21
20
  removeFromSegment(name: string): boolean {
21
+ if (!this.segmentCache[name]) return false;
22
+
22
23
  delete this.segmentCache[name];
23
24
 
24
25
  return true;
@@ -28,48 +29,13 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
28
29
  return this.segmentCache[name] === true;
29
30
  }
30
31
 
31
- /**
32
- * Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
33
- * @NOTE based on the way we use segments in the browser, this way is the best option
34
- *
35
- * @param {string[]} names list of segment names
36
- * @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
37
- */
38
- resetSegments(names: string[]): boolean {
39
- let isDiff = false;
40
- let index;
41
-
42
- const storedSegmentKeys = Object.keys(this.segmentCache);
43
32
 
44
- // Extreme fast => everything is empty
45
- if (names.length === 0 && storedSegmentKeys.length === names.length)
46
- return isDiff;
47
-
48
- // Quick path
49
- if (storedSegmentKeys.length !== names.length) {
50
- isDiff = true;
51
-
52
- this.segmentCache = {};
53
- names.forEach(s => {
54
- this.addToSegment(s);
55
- });
56
- } else {
57
- // Slowest path => we need to find at least 1 difference because
58
- for (index = 0; index < names.length && this.isInSegment(names[index]); index++) {
59
- // TODO: why empty statement?
60
- }
61
-
62
- if (index < names.length) {
63
- isDiff = true;
64
-
65
- this.segmentCache = {};
66
- names.forEach(s => {
67
- this.addToSegment(s);
68
- });
69
- }
70
- }
33
+ setChangeNumber(name?: string, changeNumber?: number) {
34
+ this.cn = changeNumber;
35
+ }
71
36
 
72
- return isDiff;
37
+ getChangeNumber() {
38
+ return this.cn || -1;
73
39
  }
74
40
 
75
41
  getRegisteredSegments() {
@@ -1,8 +1,7 @@
1
1
  import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
2
- import { AbstractSplitsCacheSync, usesMatcher } from '../AbstractSplitsCacheSync';
2
+ import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
3
3
  import { isFiniteNumber } from '../../utils/lang';
4
4
  import { ISet, _Set } from '../../utils/lang/sets';
5
- import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
6
5
 
7
6
  /**
8
7
  * Default ISplitsCacheSync implementation that stores split definitions in memory.
@@ -15,7 +14,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
15
14
  private ttCache: Record<string, number> = {};
16
15
  private changeNumber: number = -1;
17
16
  private segmentsCount: number = 0;
18
- private largeSegmentsCount: number = 0;
19
17
  private flagSetsCache: Record<string, ISet<string>> = {};
20
18
 
21
19
  constructor(splitFiltersValidation?: ISplitFiltersValidation) {
@@ -28,7 +26,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
28
26
  this.ttCache = {};
29
27
  this.changeNumber = -1;
30
28
  this.segmentsCount = 0;
31
- this.largeSegmentsCount = 0;
32
29
  }
33
30
 
34
31
  addSplit(name: string, split: ISplit): boolean {
@@ -41,9 +38,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
41
38
 
42
39
  this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
43
40
 
44
- // Substract from segments count for the previous version of this Split.
45
- if (usesMatcher(previousSplit, IN_SEGMENT)) this.segmentsCount--;
46
- if (usesMatcher(previousSplit, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
41
+ // Subtract from segments count for the previous version of this Split
42
+ if (usesSegments(previousSplit)) this.segmentsCount--;
47
43
  }
48
44
 
49
45
  if (split) {
@@ -55,8 +51,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
55
51
  this.addToFlagSets(split);
56
52
 
57
53
  // Add to segments count for the new version of the Split
58
- if (usesMatcher(split, IN_SEGMENT)) this.segmentsCount++;
59
- if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount++;
54
+ if (usesSegments(split)) this.segmentsCount++;
60
55
 
61
56
  return true;
62
57
  } else {
@@ -76,8 +71,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
76
71
  this.removeFromFlagSets(split.name, split.sets);
77
72
 
78
73
  // Update the segments count.
79
- if (usesMatcher(split, IN_SEGMENT)) this.segmentsCount--;
80
- if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
74
+ if (usesSegments(split)) this.segmentsCount--;
81
75
 
82
76
  return true;
83
77
  } else {
@@ -106,8 +100,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
106
100
  return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
107
101
  }
108
102
 
109
- usesMatcher(matcherType: string): boolean {
110
- return this.getChangeNumber() === -1 || (matcherType === IN_SEGMENT ? this.segmentsCount > 0 : this.largeSegmentsCount > 0);
103
+ usesSegments(): boolean {
104
+ return this.getChangeNumber() === -1 || this.segmentsCount > 0;
111
105
  }
112
106
 
113
107
  getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
@@ -1,4 +1,5 @@
1
- import { MaybeThenable, ISplit } from '../dtos/types';
1
+ import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
2
+ import { MySegmentsData } from '../sync/polling/types';
2
3
  import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types';
3
4
  import { SplitIO, ImpressionDTO, ISettings } from '../types';
4
5
  import { ISet } from '../utils/lang/sets';
@@ -204,8 +205,8 @@ export interface ISplitsCacheBase {
204
205
  getSplitNames(): MaybeThenable<string[]>,
205
206
  // should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
206
207
  trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
207
- // only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === 1) or contains a FF using the given matcher
208
- usesMatcher(matcherType: string): MaybeThenable<boolean>,
208
+ // only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
209
+ usesSegments(): MaybeThenable<boolean>,
209
210
  clear(): MaybeThenable<boolean | void>,
210
211
  // should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
211
212
  checkCache(): MaybeThenable<boolean>,
@@ -218,12 +219,12 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
218
219
  removeSplits(names: string[]): boolean[],
219
220
  getSplit(name: string): ISplit | null,
220
221
  getSplits(names: string[]): Record<string, ISplit | null>,
221
- setChangeNumber(changeNumber: number): boolean,
222
+ setChangeNumber(changeNumber: number): boolean | void,
222
223
  getChangeNumber(): number,
223
224
  getAll(): ISplit[],
224
225
  getSplitNames(): string[],
225
226
  trafficTypeExists(trafficType: string): boolean,
226
- usesMatcher(matcherType: string): boolean,
227
+ usesSegments(): boolean,
227
228
  clear(): void,
228
229
  checkCache(): boolean,
229
230
  killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
@@ -240,7 +241,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
240
241
  getAll(): Promise<ISplit[]>,
241
242
  getSplitNames(): Promise<string[]>,
242
243
  trafficTypeExists(trafficType: string): Promise<boolean>,
243
- usesMatcher(matcherType: string): Promise<boolean>,
244
+ usesSegments(): Promise<boolean>,
244
245
  clear(): Promise<boolean | void>,
245
246
  checkCache(): Promise<boolean>,
246
247
  killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
@@ -268,9 +269,9 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase {
268
269
  registerSegments(names: string[]): boolean
269
270
  getRegisteredSegments(): string[]
270
271
  getKeysCount(): number // only used for telemetry
271
- setChangeNumber(name: string, changeNumber: number): boolean
272
+ setChangeNumber(name: string, changeNumber: number): boolean | void
272
273
  getChangeNumber(name: string): number
273
- resetSegments(names: string[]): boolean // only for Sync Client-Side
274
+ resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean // only for Sync Client-Side
274
275
  clear(): void
275
276
  }
276
277
 
@@ -478,6 +479,7 @@ export interface IStorageSync extends IStorageBase<
478
479
  ITelemetryCacheSync,
479
480
  IUniqueKeysCacheSync
480
481
  > {
482
+ // Defined in client-side
481
483
  largeSegments?: ISegmentsCacheSync,
482
484
  }
483
485