@splitsoftware/splitio-commons 1.16.0 → 1.16.1-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 (148) hide show
  1. package/CHANGES.txt +3 -0
  2. package/cjs/evaluator/matchers/index.js +3 -1
  3. package/cjs/evaluator/matchers/large_segment.js +16 -0
  4. package/cjs/evaluator/matchers/matcherTypes.js +1 -0
  5. package/cjs/evaluator/matchersTransform/index.js +1 -1
  6. package/cjs/logger/constants.js +4 -4
  7. package/cjs/logger/messages/info.js +0 -1
  8. package/cjs/readiness/readinessManager.js +14 -10
  9. package/cjs/readiness/sdkReadinessManager.js +5 -6
  10. package/cjs/sdkClient/sdkClientMethodCS.js +3 -4
  11. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +4 -5
  12. package/cjs/sdkFactory/index.js +1 -1
  13. package/cjs/services/splitApi.js +4 -0
  14. package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
  15. package/cjs/storages/AbstractSplitsCacheSync.js +5 -5
  16. package/cjs/storages/KeyBuilder.js +3 -0
  17. package/cjs/storages/KeyBuilderCS.js +17 -5
  18. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +16 -4
  19. package/cjs/storages/inLocalStorage/index.js +6 -2
  20. package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -0
  21. package/cjs/storages/inMemory/SplitsCacheInMemory.js +20 -11
  22. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +7 -10
  23. package/cjs/storages/pluggable/inMemoryWrapper.js +1 -1
  24. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +5 -1
  25. package/cjs/sync/polling/pollingManagerCS.js +51 -33
  26. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  27. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +5 -6
  28. package/cjs/sync/polling/updaters/splitChangesUpdater.js +2 -1
  29. package/cjs/sync/streaming/SSEHandler/index.js +1 -0
  30. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -5
  31. package/cjs/sync/streaming/constants.js +2 -1
  32. package/cjs/sync/streaming/pushManager.js +95 -64
  33. package/cjs/sync/submitters/telemetrySubmitter.js +2 -0
  34. package/cjs/sync/syncManagerOnline.js +24 -14
  35. package/cjs/utils/constants/index.js +5 -1
  36. package/cjs/utils/settingsValidation/index.js +9 -4
  37. package/esm/evaluator/matchers/index.js +3 -1
  38. package/esm/evaluator/matchers/large_segment.js +12 -0
  39. package/esm/evaluator/matchers/matcherTypes.js +1 -0
  40. package/esm/evaluator/matchersTransform/index.js +1 -1
  41. package/esm/logger/constants.js +1 -1
  42. package/esm/logger/messages/info.js +0 -1
  43. package/esm/readiness/readinessManager.js +14 -10
  44. package/esm/readiness/sdkReadinessManager.js +5 -6
  45. package/esm/sdkClient/sdkClientMethodCS.js +4 -5
  46. package/esm/sdkClient/sdkClientMethodCSWithTT.js +5 -6
  47. package/esm/sdkFactory/index.js +1 -1
  48. package/esm/services/splitApi.js +5 -1
  49. package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
  50. package/esm/storages/AbstractSplitsCacheSync.js +3 -3
  51. package/esm/storages/KeyBuilder.js +3 -0
  52. package/esm/storages/KeyBuilderCS.js +15 -4
  53. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +17 -5
  54. package/esm/storages/inLocalStorage/index.js +7 -3
  55. package/esm/storages/inMemory/InMemoryStorageCS.js +5 -0
  56. package/esm/storages/inMemory/SplitsCacheInMemory.js +21 -12
  57. package/esm/storages/inMemory/TelemetryCacheInMemory.js +7 -10
  58. package/esm/storages/pluggable/inMemoryWrapper.js +1 -1
  59. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +5 -1
  60. package/esm/sync/polling/pollingManagerCS.js +52 -34
  61. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  62. package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -4
  63. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -1
  64. package/esm/sync/streaming/SSEHandler/index.js +2 -1
  65. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -5
  66. package/esm/sync/streaming/constants.js +1 -0
  67. package/esm/sync/streaming/pushManager.js +95 -65
  68. package/esm/sync/submitters/telemetrySubmitter.js +2 -0
  69. package/esm/sync/syncManagerOnline.js +25 -15
  70. package/esm/utils/constants/index.js +4 -0
  71. package/esm/utils/settingsValidation/index.js +10 -5
  72. package/package.json +1 -1
  73. package/src/dtos/types.ts +17 -7
  74. package/src/evaluator/matchers/index.ts +2 -0
  75. package/src/evaluator/matchers/large_segment.ts +18 -0
  76. package/src/evaluator/matchers/matcherTypes.ts +1 -0
  77. package/src/evaluator/matchersTransform/index.ts +1 -1
  78. package/src/logger/constants.ts +1 -1
  79. package/src/logger/messages/info.ts +0 -1
  80. package/src/readiness/readinessManager.ts +13 -9
  81. package/src/readiness/sdkReadinessManager.ts +7 -7
  82. package/src/readiness/types.ts +3 -2
  83. package/src/sdkClient/sdkClientMethodCS.ts +4 -6
  84. package/src/sdkClient/sdkClientMethodCSWithTT.ts +5 -7
  85. package/src/sdkFactory/index.ts +1 -1
  86. package/src/services/splitApi.ts +6 -1
  87. package/src/services/types.ts +1 -0
  88. package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
  89. package/src/storages/AbstractSplitsCacheSync.ts +4 -4
  90. package/src/storages/KeyBuilder.ts +3 -0
  91. package/src/storages/KeyBuilderCS.ts +25 -5
  92. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +3 -3
  93. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +20 -5
  94. package/src/storages/inLocalStorage/index.ts +8 -4
  95. package/src/storages/inMemory/InMemoryStorageCS.ts +5 -0
  96. package/src/storages/inMemory/SplitsCacheInMemory.ts +15 -10
  97. package/src/storages/inMemory/TelemetryCacheInMemory.ts +7 -11
  98. package/src/storages/pluggable/inMemoryWrapper.ts +1 -1
  99. package/src/storages/types.ts +7 -5
  100. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +6 -2
  101. package/src/sync/polling/pollingManagerCS.ts +61 -29
  102. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +10 -10
  103. package/src/sync/polling/types.ts +3 -2
  104. package/src/sync/polling/updaters/mySegmentsUpdater.ts +5 -8
  105. package/src/sync/polling/updaters/splitChangesUpdater.ts +4 -3
  106. package/src/sync/streaming/SSEHandler/index.ts +2 -1
  107. package/src/sync/streaming/SSEHandler/types.ts +14 -2
  108. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +17 -5
  109. package/src/sync/streaming/constants.ts +1 -0
  110. package/src/sync/streaming/pushManager.ts +100 -63
  111. package/src/sync/streaming/types.ts +5 -3
  112. package/src/sync/submitters/telemetrySubmitter.ts +2 -0
  113. package/src/sync/submitters/types.ts +10 -4
  114. package/src/sync/syncManagerOnline.ts +19 -11
  115. package/src/types.ts +26 -1
  116. package/src/utils/constants/index.ts +5 -0
  117. package/src/utils/settingsValidation/index.ts +11 -6
  118. package/src/utils/settingsValidation/types.ts +1 -1
  119. package/types/dtos/types.d.ts +14 -6
  120. package/types/evaluator/matchers/large_segment.d.ts +5 -0
  121. package/types/logger/constants.d.ts +1 -1
  122. package/types/readiness/readinessManager.d.ts +2 -2
  123. package/types/readiness/sdkReadinessManager.d.ts +2 -3
  124. package/types/readiness/types.d.ts +3 -2
  125. package/types/services/types.d.ts +1 -0
  126. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  127. package/types/storages/AbstractSplitsCacheSync.d.ts +3 -3
  128. package/types/storages/KeyBuilder.d.ts +1 -0
  129. package/types/storages/KeyBuilderCS.d.ts +7 -2
  130. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -2
  131. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  132. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
  133. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +4 -6
  134. package/types/storages/pluggable/inMemoryWrapper.d.ts +1 -1
  135. package/types/storages/types.d.ts +4 -3
  136. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +2 -3
  137. package/types/sync/polling/types.d.ts +9 -2
  138. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +4 -4
  139. package/types/sync/streaming/SSEHandler/types.d.ts +13 -2
  140. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -1
  141. package/types/sync/streaming/constants.d.ts +1 -0
  142. package/types/sync/streaming/pushManager.d.ts +2 -0
  143. package/types/sync/streaming/types.d.ts +5 -4
  144. package/types/sync/submitters/types.d.ts +9 -3
  145. package/types/types.d.ts +25 -0
  146. package/types/utils/constants/index.d.ts +3 -0
  147. package/types/utils/settingsValidation/index.d.ts +2 -0
  148. package/types/utils/settingsValidation/types.d.ts +1 -1
@@ -45,6 +45,7 @@ export interface IReadinessManager {
45
45
  /** Event emitters */
46
46
  splits: ISplitsEventEmitter,
47
47
  segments: ISegmentsEventEmitter,
48
+ largeSegments?: ISegmentsEventEmitter, // Undefined if largeSegmentsEnabled or waitForLargeSegments are false
48
49
  gate: IReadinessEventEmitter,
49
50
 
50
51
  /** Readiness status */
@@ -59,7 +60,7 @@ export interface IReadinessManager {
59
60
  destroy(): void,
60
61
 
61
62
  /** for client-side */
62
- shared(readyTimeout?: number): IReadinessManager,
63
+ shared(): IReadinessManager,
63
64
  }
64
65
 
65
66
  /** SDK readiness manager */
@@ -75,5 +76,5 @@ export interface ISdkReadinessManager {
75
76
  incInternalReadyCbCount(): void
76
77
 
77
78
  /** for client-side */
78
- shared(readyTimeout?: number): ISdkReadinessManager
79
+ shared(): ISdkReadinessManager
79
80
  }
@@ -5,7 +5,7 @@ import { getMatching, keyParser } from '../utils/key';
5
5
  import { sdkClientFactory } from './sdkClient';
6
6
  import { ISyncManagerCS } from '../sync/types';
7
7
  import { objectAssign } from '../utils/lang/objectAssign';
8
- import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
8
+ import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
9
9
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10
10
  import { ISdkFactoryContext } from '../sdkFactory/types';
11
11
 
@@ -14,14 +14,12 @@ function buildInstanceId(key: SplitIO.SplitKey) {
14
14
  return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-`;
15
15
  }
16
16
 
17
- const method = 'Client instantiation';
18
-
19
17
  /**
20
18
  * Factory of client method for the client-side API variant where TT is ignored.
21
19
  * Therefore, clients don't have a bound TT for the track method.
22
20
  */
23
21
  export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey) => SplitIO.ICsClient {
24
- const { storage, syncManager, sdkReadinessManager, settings: { core: { key }, startup: { readyTimeout }, log } } = params;
22
+ const { storage, syncManager, sdkReadinessManager, settings: { core: { key }, log } } = params;
25
23
 
26
24
  const mainClientInstance = clientCSDecorator(
27
25
  log,
@@ -43,7 +41,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
43
41
  }
44
42
 
45
43
  // Validate the key value. The trafficType (2nd argument) is ignored
46
- const validKey = validateKey(log, key, method);
44
+ const validKey = validateKey(log, key, LOG_PREFIX_CLIENT_INSTANTIATION);
47
45
  if (validKey === false) {
48
46
  throw new Error('Shared Client needs a valid key.');
49
47
  }
@@ -53,7 +51,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
53
51
  if (!clientInstances[instanceId]) {
54
52
  const matchingKey = getMatching(validKey);
55
53
 
56
- const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout);
54
+ const sharedSdkReadiness = sdkReadinessManager.shared();
57
55
  const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => {
58
56
  if (err) {
59
57
  sharedSdkReadiness.readinessManager.timeout();
@@ -6,7 +6,7 @@ import { getMatching, keyParser } from '../utils/key';
6
6
  import { sdkClientFactory } from './sdkClient';
7
7
  import { ISyncManagerCS } from '../sync/types';
8
8
  import { objectAssign } from '../utils/lang/objectAssign';
9
- import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
9
+ import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING, LOG_PREFIX_CLIENT_INSTANTIATION } from '../logger/constants';
10
10
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
11
11
  import { ISdkFactoryContext } from '../sdkFactory/types';
12
12
 
@@ -15,15 +15,13 @@ function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) {
15
15
  return `${key.matchingKey ? key.matchingKey : key}-${key.bucketingKey ? key.bucketingKey : key}-${trafficType !== undefined ? trafficType : ''}`;
16
16
  }
17
17
 
18
- const method = 'Client instantiation';
19
-
20
18
  /**
21
19
  * Factory of client method for the client-side (browser) variant of the Isomorphic JS SDK,
22
20
  * where clients can have a bound TT for the track method, which is provided via the settings
23
21
  * (default client) or the client method (shared clients).
24
22
  */
25
23
  export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey, trafficType?: string) => SplitIO.ICsClient {
26
- const { storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, startup: { readyTimeout }, log } } = params;
24
+ const { storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, log } } = params;
27
25
 
28
26
  const mainClientInstance = clientCSDecorator(
29
27
  log,
@@ -46,14 +44,14 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
46
44
  }
47
45
 
48
46
  // Validate the key value
49
- const validKey = validateKey(log, key, `Shared ${method}`);
47
+ const validKey = validateKey(log, key, LOG_PREFIX_CLIENT_INSTANTIATION);
50
48
  if (validKey === false) {
51
49
  throw new Error('Shared Client needs a valid key.');
52
50
  }
53
51
 
54
52
  let validTrafficType;
55
53
  if (trafficType !== undefined) {
56
- validTrafficType = validateTrafficType(log, trafficType, `Shared ${method}`);
54
+ validTrafficType = validateTrafficType(log, trafficType, LOG_PREFIX_CLIENT_INSTANTIATION);
57
55
  if (validTrafficType === false) {
58
56
  throw new Error('Shared Client needs a valid traffic type or no traffic type at all.');
59
57
  }
@@ -63,7 +61,7 @@ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: Spl
63
61
  if (!clientInstances[instanceId]) {
64
62
  const matchingKey = getMatching(validKey);
65
63
 
66
- const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout);
64
+ const sharedSdkReadiness = sdkReadinessManager.shared();
67
65
  const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => {
68
66
  if (err) {
69
67
  sharedSdkReadiness.readinessManager.timeout();
@@ -32,7 +32,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
32
32
  // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
33
33
  validateAndTrackApiKey(log, settings.core.authorizationKey);
34
34
 
35
- const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout);
35
+ const sdkReadinessManager = sdkReadinessManagerFactory(platform.EventEmitter, settings);
36
36
  const readiness = sdkReadinessManager.readinessManager;
37
37
 
38
38
  const storage = storageFactory({
@@ -4,7 +4,7 @@ import { splitHttpClientFactory } from './splitHttpClient';
4
4
  import { ISplitApi } from './types';
5
5
  import { objectAssign } from '../utils/lang/objectAssign';
6
6
  import { ITelemetryTracker } from '../trackers/types';
7
- import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
7
+ import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT, MY_LARGE_SEGMENT } from '../utils/constants';
8
8
  import { ERROR_TOO_MANY_SETS } from '../logger/constants';
9
9
 
10
10
  const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
@@ -78,6 +78,11 @@ export function splitApiFactory(
78
78
  return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(MY_SEGMENT));
79
79
  },
80
80
 
81
+ fetchMyLargeSegments(userMatchingKey: string, noCache?: boolean) {
82
+ const url = `${urls.sdk}/myLargeSegments/${encodeURIComponent(userMatchingKey)}`;
83
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(MY_LARGE_SEGMENT));
84
+ },
85
+
81
86
  /**
82
87
  * Post events.
83
88
  *
@@ -62,6 +62,7 @@ export interface ISplitApi {
62
62
  fetchSplitChanges: IFetchSplitChanges
63
63
  fetchSegmentChanges: IFetchSegmentChanges
64
64
  fetchMySegments: IFetchMySegments
65
+ fetchMyLargeSegments: IFetchMySegments
65
66
  postEventsBulk: IPostEventsBulk
66
67
  postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
67
68
  postUniqueKeysBulkSs: IPostUniqueKeysBulkSs
@@ -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 ('usesSegments', 'getRegisteredSegments', 'registerSegments')
25
+ // @TODO revisit segment-related methods ('usesMatcher', '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
- usesSegments(): Promise<boolean> {
27
+ usesMatcher(): Promise<boolean> {
28
28
  return Promise.resolve(true);
29
29
  }
30
30
 
@@ -43,7 +43,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
43
43
 
44
44
  abstract trafficTypeExists(trafficType: string): boolean
45
45
 
46
- abstract usesSegments(): boolean
46
+ abstract usesMatcher(matcherType: string): boolean
47
47
 
48
48
  abstract clear(): void
49
49
 
@@ -85,15 +85,15 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
85
85
 
86
86
  /**
87
87
  * 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::usesSegments` method
88
+ * This util is intended to simplify the implementation of `splitsCache::usesMatcher` method
89
89
  */
90
- export function usesSegments(split: ISplit) {
90
+ export function usesMatcher(split: ISplit, matcherType: string) {
91
91
  const conditions = split.conditions || [];
92
92
  for (let i = 0; i < conditions.length; i++) {
93
93
  const matchers = conditions[i].matcherGroup.matchers;
94
94
 
95
95
  for (let j = 0; j < matchers.length; j++) {
96
- if (matchers[j].matcherType === 'IN_SEGMENT') return true;
96
+ if (matchers[j].matcherType === matcherType) return true;
97
97
  }
98
98
  }
99
99
 
@@ -51,6 +51,9 @@ export class KeyBuilder {
51
51
  buildSplitsWithSegmentCountKey() {
52
52
  return `${this.prefix}.splits.usingSegments`;
53
53
  }
54
+ buildSplitsWithLargeSegmentCountKey() {
55
+ return `${this.prefix}.splits.usingLargeSegments`;
56
+ }
54
57
 
55
58
  buildSegmentNameKey(segmentName: string) {
56
59
  return `${this.prefix}.segment.${segmentName}`;
@@ -1,7 +1,13 @@
1
1
  import { startsWith } from '../utils/lang';
2
2
  import { KeyBuilder } from './KeyBuilder';
3
3
 
4
- export class KeyBuilderCS extends KeyBuilder {
4
+ export interface MySegmentsKeyBuilder {
5
+ buildSegmentNameKey(segmentName: string): string;
6
+ extractSegmentName(builtSegmentKeyName: string): string | undefined;
7
+ extractOldSegmentKey(builtSegmentKeyName: string): string | undefined;
8
+ }
9
+
10
+ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
5
11
 
6
12
  protected readonly regexSplitsCacheKey: RegExp;
7
13
  protected readonly matchingKey: string;
@@ -26,10 +32,6 @@ export class KeyBuilderCS extends KeyBuilder {
26
32
  return builtSegmentKeyName.substr(prefix.length);
27
33
  }
28
34
 
29
- // @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
30
- buildOldSegmentNameKey(segmentName: string) {
31
- return `${this.matchingKey}.${this.prefix}.segment.${segmentName}`;
32
- }
33
35
  // @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3
34
36
  extractOldSegmentKey(builtSegmentKeyName: string) {
35
37
  const prefix = `${this.matchingKey}.${this.prefix}.segment.`;
@@ -46,3 +48,21 @@ export class KeyBuilderCS extends KeyBuilder {
46
48
  return this.regexSplitsCacheKey.test(key);
47
49
  }
48
50
  }
51
+
52
+ export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string): MySegmentsKeyBuilder {
53
+ return {
54
+ buildSegmentNameKey(segmentName: string) {
55
+ return `${prefix}.${matchingKey}.largeSegment.${segmentName}`;
56
+ },
57
+
58
+ extractSegmentName(builtSegmentKeyName: string) {
59
+ const p = `${prefix}.${matchingKey}.largeSegment.`;
60
+
61
+ if (startsWith(builtSegmentKeyName, p)) return builtSegmentKeyName.substr(p.length);
62
+ },
63
+
64
+ extractOldSegmentKey() {
65
+ return undefined;
66
+ }
67
+ };
68
+ }
@@ -1,14 +1,14 @@
1
1
  import { ILogger } from '../../logger/types';
2
2
  import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
3
- import { KeyBuilderCS } from '../KeyBuilderCS';
3
+ import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
4
4
  import { LOG_PREFIX, DEFINED } from './constants';
5
5
 
6
6
  export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
7
7
 
8
- private readonly keys: KeyBuilderCS;
8
+ private readonly keys: MySegmentsKeyBuilder;
9
9
  private readonly log: ILogger;
10
10
 
11
- constructor(log: ILogger, keys: KeyBuilderCS) {
11
+ constructor(log: ILogger, keys: MySegmentsKeyBuilder) {
12
12
  super();
13
13
  this.log = log;
14
14
  this.keys = keys;
@@ -1,5 +1,5 @@
1
1
  import { ISplit } from '../../dtos/types';
2
- import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
2
+ import { AbstractSplitsCacheSync, usesMatcher } 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,6 +7,7 @@ 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';
10
11
 
11
12
  /**
12
13
  * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
@@ -50,10 +51,15 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
50
51
  const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
51
52
  this._decrementCount(ttKey);
52
53
 
53
- if (usesSegments(split)) {
54
+ if (usesMatcher(split, IN_SEGMENT)) {
54
55
  const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
55
56
  this._decrementCount(segmentsCountKey);
56
57
  }
58
+
59
+ if (usesMatcher(split, IN_LARGE_SEGMENT)) {
60
+ const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
61
+ this._decrementCount(segmentsCountKey);
62
+ }
57
63
  }
58
64
  } catch (e) {
59
65
  this.log.error(LOG_PREFIX + e);
@@ -67,11 +73,17 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
67
73
  // @ts-expect-error
68
74
  localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
69
75
 
70
- if (usesSegments(split)) {
76
+ if (usesMatcher(split, IN_SEGMENT)) {
71
77
  const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
72
78
  // @ts-expect-error
73
79
  localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
74
80
  }
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
+ }
75
87
  }
76
88
  } catch (e) {
77
89
  this.log.error(LOG_PREFIX + e);
@@ -203,11 +215,14 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
203
215
  return isFiniteNumber(ttCount) && ttCount > 0;
204
216
  }
205
217
 
206
- usesSegments() {
218
+ usesMatcher(matcherType: string) {
207
219
  // If cache hasn't been synchronized with the cloud, assume we need them.
208
220
  if (!this.hasSync) return true;
209
221
 
210
- const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
222
+ const storedCount = localStorage.getItem(matcherType === IN_SEGMENT ?
223
+ this.keys.buildSplitsWithSegmentCountKey() :
224
+ this.keys.buildSplitsWithLargeSegmentCountKey()
225
+ );
211
226
  const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
212
227
 
213
228
  if (isFiniteNumber(splitsWithSegmentsCount)) {
@@ -3,7 +3,7 @@ import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCache
3
3
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
4
4
  import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types';
5
5
  import { validatePrefix } from '../KeyBuilder';
6
- import { KeyBuilderCS } from '../KeyBuilderCS';
6
+ import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
7
7
  import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
8
8
  import { SplitsCacheInLocal } from './SplitsCacheInLocal';
9
9
  import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
@@ -38,15 +38,17 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
38
38
 
39
39
  const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
40
40
  const matchingKey = getMatching(settings.core.key);
41
- const keys = new KeyBuilderCS(prefix, matchingKey as string);
41
+ const keys = new KeyBuilderCS(prefix, matchingKey);
42
42
  const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
43
43
 
44
44
  const splits = new SplitsCacheInLocal(settings, keys, expirationTimestamp);
45
45
  const segments = new MySegmentsCacheInLocal(log, keys);
46
+ const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey));
46
47
 
47
48
  return {
48
49
  splits,
49
50
  segments,
51
+ largeSegments,
50
52
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
51
53
  impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
52
54
  events: new EventsCacheInMemory(eventsQueueSize),
@@ -56,6 +58,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
56
58
  destroy() {
57
59
  this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
58
60
  this.segments = new MySegmentsCacheInMemory();
61
+ this.largeSegments = new MySegmentsCacheInMemory();
59
62
  this.impressions.clear();
60
63
  this.impressionCounts && this.impressionCounts.clear();
61
64
  this.events.clear();
@@ -64,11 +67,11 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
64
67
 
65
68
  // When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).
66
69
  shared(matchingKey: string) {
67
- const childKeysBuilder = new KeyBuilderCS(prefix, matchingKey);
68
70
 
69
71
  return {
70
72
  splits: this.splits,
71
- segments: new MySegmentsCacheInLocal(log, childKeysBuilder),
73
+ segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey)),
74
+ largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey)),
72
75
  impressions: this.impressions,
73
76
  impressionCounts: this.impressionCounts,
74
77
  events: this.events,
@@ -77,6 +80,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
77
80
  destroy() {
78
81
  this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
79
82
  this.segments = new MySegmentsCacheInMemory();
83
+ this.largeSegments = new MySegmentsCacheInMemory();
80
84
  }
81
85
  };
82
86
  },
@@ -18,10 +18,12 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
18
18
 
19
19
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
20
20
  const segments = new MySegmentsCacheInMemory();
21
+ const largeSegments = new MySegmentsCacheInMemory();
21
22
 
22
23
  const storage = {
23
24
  splits,
24
25
  segments,
26
+ largeSegments,
25
27
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
26
28
  impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
27
29
  events: new EventsCacheInMemory(eventsQueueSize),
@@ -32,6 +34,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
32
34
  destroy() {
33
35
  this.splits.clear();
34
36
  this.segments.clear();
37
+ this.largeSegments.clear();
35
38
  this.impressions.clear();
36
39
  this.impressionCounts && this.impressionCounts.clear();
37
40
  this.events.clear();
@@ -43,6 +46,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
43
46
  return {
44
47
  splits: this.splits,
45
48
  segments: new MySegmentsCacheInMemory(),
49
+ largeSegments: new MySegmentsCacheInMemory(),
46
50
  impressions: this.impressions,
47
51
  impressionCounts: this.impressionCounts,
48
52
  events: this.events,
@@ -52,6 +56,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
52
56
  destroy() {
53
57
  this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
54
58
  this.segments.clear();
59
+ this.largeSegments.clear();
55
60
  }
56
61
  };
57
62
  },
@@ -1,7 +1,8 @@
1
1
  import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
2
- import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
2
+ import { AbstractSplitsCacheSync, usesMatcher } 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';
5
6
 
6
7
  /**
7
8
  * Default ISplitsCacheSync implementation that stores split definitions in memory.
@@ -13,7 +14,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
13
14
  private splitsCache: Record<string, ISplit> = {};
14
15
  private ttCache: Record<string, number> = {};
15
16
  private changeNumber: number = -1;
16
- private splitsWithSegmentsCount: number = 0;
17
+ private segmentsCount: number = 0;
18
+ private largeSegmentsCount: number = 0;
17
19
  private flagSetsCache: Record<string, ISet<string>> = {};
18
20
 
19
21
  constructor(splitFiltersValidation?: ISplitFiltersValidation) {
@@ -25,7 +27,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
25
27
  this.splitsCache = {};
26
28
  this.ttCache = {};
27
29
  this.changeNumber = -1;
28
- this.splitsWithSegmentsCount = 0;
30
+ this.segmentsCount = 0;
31
+ this.largeSegmentsCount = 0;
29
32
  }
30
33
 
31
34
  addSplit(name: string, split: ISplit): boolean {
@@ -38,9 +41,9 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
38
41
 
39
42
  this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
40
43
 
41
- if (usesSegments(previousSplit)) { // Substract from segments count for the previous version of this Split.
42
- this.splitsWithSegmentsCount--;
43
- }
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--;
44
47
  }
45
48
 
46
49
  if (split) {
@@ -52,7 +55,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
52
55
  this.addToFlagSets(split);
53
56
 
54
57
  // Add to segments count for the new version of the Split
55
- if (usesSegments(split)) this.splitsWithSegmentsCount++;
58
+ if (usesMatcher(split, IN_SEGMENT)) this.segmentsCount++;
59
+ if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount++;
56
60
 
57
61
  return true;
58
62
  } else {
@@ -72,7 +76,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
72
76
  this.removeFromFlagSets(split.name, split.sets);
73
77
 
74
78
  // Update the segments count.
75
- if (usesSegments(split)) this.splitsWithSegmentsCount--;
79
+ if (usesMatcher(split, IN_SEGMENT)) this.segmentsCount--;
80
+ if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
76
81
 
77
82
  return true;
78
83
  } else {
@@ -101,8 +106,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
101
106
  return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
102
107
  }
103
108
 
104
- usesSegments(): boolean {
105
- return this.getChangeNumber() === -1 || this.splitsWithSegmentsCount > 0;
109
+ usesMatcher(matcherType: string): boolean {
110
+ return this.getChangeNumber() === -1 || (matcherType === IN_SEGMENT ? this.segmentsCount > 0 : this.largeSegmentsCount > 0);
106
111
  }
107
112
 
108
113
  getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
@@ -1,4 +1,4 @@
1
- import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies, StreamingEvent, Method, OperationType, MethodExceptions, MethodLatencies, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../../sync/submitters/types';
1
+ import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies, StreamingEvent, Method, OperationType, MethodExceptions, MethodLatencies, TelemetryUsageStatsPayload, UpdatesFromSSEEnum, UpdatesFromSSE } from '../../sync/submitters/types';
2
2
  import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
3
3
  import { findLatencyIndex } from '../findLatencyIndex';
4
4
  import { ISegmentsCacheSync, ISplitsCacheSync, IStorageFactoryParams, ITelemetryCacheSync } from '../types';
@@ -25,7 +25,7 @@ export function shouldRecordTelemetry({ settings }: IStorageFactoryParams) {
25
25
 
26
26
  export class TelemetryCacheInMemory implements ITelemetryCacheSync {
27
27
 
28
- constructor(private splits?: ISplitsCacheSync, private segments?: ISegmentsCacheSync) { }
28
+ constructor(private splits?: ISplitsCacheSync, private segments?: ISegmentsCacheSync, private largeSegments?: ISegmentsCacheSync) { }
29
29
 
30
30
  // isEmpty flag
31
31
  private e = true;
@@ -51,6 +51,8 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
51
51
  spC: this.splits && this.splits.getSplitNames().length,
52
52
  seC: this.segments && this.segments.getRegisteredSegments().length,
53
53
  skC: this.segments && this.segments.getKeysCount(),
54
+ lseC: this.largeSegments && this.largeSegments.getRegisteredSegments().length,
55
+ lskC: this.largeSegments && this.largeSegments.getKeysCount(),
54
56
  sL: this.getSessionLength(),
55
57
  eQ: this.getEventStats(QUEUED),
56
58
  eD: this.getEventStats(DROPPED),
@@ -245,22 +247,16 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
245
247
  this.e = false;
246
248
  }
247
249
 
248
- private updatesFromSSE = {
249
- sp: 0,
250
- ms: 0
251
- };
250
+ private updatesFromSSE: UpdatesFromSSE = {};
252
251
 
253
252
  popUpdatesFromSSE() {
254
253
  const result = this.updatesFromSSE;
255
- this.updatesFromSSE = {
256
- sp: 0,
257
- ms: 0,
258
- };
254
+ this.updatesFromSSE = {};
259
255
  return result;
260
256
  }
261
257
 
262
258
  recordUpdatesFromSSE(type: UpdatesFromSSEEnum) {
263
- this.updatesFromSSE[type]++;
259
+ this.updatesFromSSE[type] = (this.updatesFromSSE[type] || 0) + 1;
264
260
  this.e = false;
265
261
  }
266
262
 
@@ -7,7 +7,7 @@ import { ISet, setToArray, _Set } from '../../utils/lang/sets';
7
7
  * The `_cache` property is the object were items are stored.
8
8
  * Intended for testing purposes.
9
9
  *
10
- * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves inmediatelly.
10
+ * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves immediately.
11
11
  */
12
12
  export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWrapper & { _cache: Record<string, string | string[] | ISet<string>>, _setConnDelay(connDelay: number): void } {
13
13
 
@@ -204,8 +204,8 @@ export interface ISplitsCacheBase {
204
204
  getSplitNames(): MaybeThenable<string[]>,
205
205
  // should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
206
206
  trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
207
- // only for Client-Side
208
- usesSegments(): 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>,
209
209
  clear(): MaybeThenable<boolean | void>,
210
210
  // should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
211
211
  checkCache(): MaybeThenable<boolean>,
@@ -223,7 +223,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
223
223
  getAll(): ISplit[],
224
224
  getSplitNames(): string[],
225
225
  trafficTypeExists(trafficType: string): boolean,
226
- usesSegments(): boolean,
226
+ usesMatcher(matcherType: string): boolean,
227
227
  clear(): void,
228
228
  checkCache(): boolean,
229
229
  killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
@@ -240,7 +240,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
240
240
  getAll(): Promise<ISplit[]>,
241
241
  getSplitNames(): Promise<string[]>,
242
242
  trafficTypeExists(trafficType: string): Promise<boolean>,
243
- usesSegments(): Promise<boolean>,
243
+ usesMatcher(matcherType: string): Promise<boolean>,
244
244
  clear(): Promise<boolean | void>,
245
245
  checkCache(): Promise<boolean>,
246
246
  killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
@@ -477,7 +477,9 @@ export interface IStorageSync extends IStorageBase<
477
477
  IEventsCacheSync,
478
478
  ITelemetryCacheSync,
479
479
  IUniqueKeysCacheSync
480
- > { }
480
+ > {
481
+ largeSegments?: ISegmentsCacheSync,
482
+ }
481
483
 
482
484
  export interface IStorageAsync extends IStorageBase<
483
485
  ISplitsCacheAsync,
@@ -1,5 +1,5 @@
1
1
  import { IFetchMySegments, IResponse } from '../../../services/types';
2
- import { IMySegmentsResponseItem } from '../../../dtos/types';
2
+ import { IMySegmentsResponse, IMyLargeSegmentsResponse } from '../../../dtos/types';
3
3
  import { IMySegmentsFetcher } from './types';
4
4
 
5
5
  /**
@@ -21,7 +21,11 @@ export function mySegmentsFetcherFactory(fetchMySegments: IFetchMySegments): IMy
21
21
  // Extract segment names
22
22
  return mySegmentsPromise
23
23
  .then(resp => resp.json())
24
- .then(json => json.mySegments.map((segment: IMySegmentsResponseItem) => segment.name));
24
+ .then((json: IMySegmentsResponse | IMyLargeSegmentsResponse) => {
25
+ return (json as IMySegmentsResponse).mySegments ?
26
+ (json as IMySegmentsResponse).mySegments.map((segment) => segment.name) :
27
+ (json as IMyLargeSegmentsResponse).myLargeSegments;
28
+ });
25
29
  };
26
30
 
27
31
  }