@splitsoftware/splitio-commons 2.1.1-rc.0 → 2.1.1-rc.2

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 (187) hide show
  1. package/CHANGES.txt +7 -0
  2. package/README.md +0 -1
  3. package/cjs/evaluator/combiners/and.js +6 -2
  4. package/cjs/evaluator/combiners/ifelseif.js +6 -6
  5. package/cjs/evaluator/condition/index.js +5 -6
  6. package/cjs/evaluator/index.js +7 -7
  7. package/cjs/evaluator/matchers/index.js +1 -3
  8. package/cjs/evaluator/matchers/matcherTypes.js +0 -1
  9. package/cjs/evaluator/matchersTransform/index.js +0 -4
  10. package/cjs/evaluator/parser/index.js +2 -2
  11. package/cjs/evaluator/value/sanitize.js +0 -1
  12. package/cjs/logger/constants.js +3 -4
  13. package/cjs/logger/messages/debug.js +2 -3
  14. package/cjs/logger/messages/error.js +1 -1
  15. package/cjs/logger/messages/warn.js +2 -2
  16. package/cjs/readiness/readinessManager.js +6 -0
  17. package/cjs/sdkClient/client.js +29 -19
  18. package/cjs/sdkClient/clientAttributesDecoration.js +19 -25
  19. package/cjs/sdkClient/clientInputValidation.js +28 -26
  20. package/cjs/services/splitApi.js +2 -2
  21. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
  22. package/cjs/storages/AbstractSplitsCacheSync.js +2 -12
  23. package/cjs/storages/KeyBuilder.js +0 -9
  24. package/cjs/storages/KeyBuilderCS.js +4 -4
  25. package/cjs/storages/KeyBuilderSS.js +0 -3
  26. package/cjs/storages/dataLoader.js +3 -2
  27. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
  28. package/cjs/storages/inLocalStorage/index.js +5 -7
  29. package/cjs/storages/inLocalStorage/validateCache.js +79 -0
  30. package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
  31. package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -4
  32. package/cjs/storages/inRedis/index.js +0 -2
  33. package/cjs/storages/pluggable/index.js +2 -3
  34. package/cjs/storages/utils.js +1 -0
  35. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  36. package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  37. package/cjs/sync/polling/pollingManagerCS.js +7 -7
  38. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  39. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  40. package/cjs/sync/polling/updaters/splitChangesUpdater.js +33 -60
  41. package/cjs/sync/streaming/SSEHandler/index.js +0 -1
  42. package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +77 -106
  43. package/cjs/sync/streaming/constants.js +1 -2
  44. package/cjs/sync/streaming/pushManager.js +16 -3
  45. package/cjs/sync/submitters/impressionsSubmitter.js +3 -2
  46. package/cjs/sync/syncManagerOnline.js +10 -5
  47. package/cjs/trackers/strategy/strategyDebug.js +2 -0
  48. package/cjs/trackers/strategy/strategyOptimized.js +3 -0
  49. package/cjs/utils/constants/index.js +2 -3
  50. package/cjs/utils/inputValidation/eventProperties.js +12 -1
  51. package/cjs/utils/inputValidation/index.js +3 -1
  52. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  53. package/esm/evaluator/combiners/and.js +6 -2
  54. package/esm/evaluator/combiners/ifelseif.js +7 -7
  55. package/esm/evaluator/condition/index.js +5 -6
  56. package/esm/evaluator/index.js +7 -7
  57. package/esm/evaluator/matchers/index.js +1 -3
  58. package/esm/evaluator/matchers/matcherTypes.js +0 -1
  59. package/esm/evaluator/matchersTransform/index.js +0 -4
  60. package/esm/evaluator/parser/index.js +2 -2
  61. package/esm/evaluator/value/sanitize.js +0 -1
  62. package/esm/logger/constants.js +0 -1
  63. package/esm/logger/messages/debug.js +2 -3
  64. package/esm/logger/messages/error.js +1 -1
  65. package/esm/logger/messages/warn.js +2 -2
  66. package/esm/readiness/readinessManager.js +6 -0
  67. package/esm/sdkClient/client.js +29 -19
  68. package/esm/sdkClient/clientAttributesDecoration.js +19 -25
  69. package/esm/sdkClient/clientInputValidation.js +29 -27
  70. package/esm/services/splitApi.js +2 -2
  71. package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
  72. package/esm/storages/AbstractSplitsCacheSync.js +2 -12
  73. package/esm/storages/KeyBuilder.js +0 -9
  74. package/esm/storages/KeyBuilderCS.js +4 -4
  75. package/esm/storages/KeyBuilderSS.js +0 -3
  76. package/esm/storages/dataLoader.js +2 -1
  77. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
  78. package/esm/storages/inLocalStorage/index.js +5 -7
  79. package/esm/storages/inLocalStorage/validateCache.js +75 -0
  80. package/esm/storages/inMemory/InMemoryStorage.js +0 -3
  81. package/esm/storages/inMemory/InMemoryStorageCS.js +0 -4
  82. package/esm/storages/inRedis/index.js +0 -2
  83. package/esm/storages/pluggable/index.js +2 -3
  84. package/esm/storages/utils.js +1 -0
  85. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  86. package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
  87. package/esm/sync/polling/pollingManagerCS.js +7 -7
  88. package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
  89. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  90. package/esm/sync/polling/updaters/splitChangesUpdater.js +34 -61
  91. package/esm/sync/streaming/SSEHandler/index.js +1 -2
  92. package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +73 -102
  93. package/esm/sync/streaming/constants.js +0 -1
  94. package/esm/sync/streaming/pushManager.js +19 -6
  95. package/esm/sync/submitters/impressionsSubmitter.js +3 -2
  96. package/esm/sync/syncManagerOnline.js +10 -5
  97. package/esm/trackers/strategy/strategyDebug.js +2 -0
  98. package/esm/trackers/strategy/strategyOptimized.js +3 -0
  99. package/esm/utils/constants/index.js +1 -2
  100. package/esm/utils/inputValidation/eventProperties.js +10 -0
  101. package/esm/utils/inputValidation/index.js +1 -0
  102. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  103. package/package.json +1 -1
  104. package/src/dtos/types.ts +8 -32
  105. package/src/evaluator/Engine.ts +1 -1
  106. package/src/evaluator/combiners/and.ts +4 -5
  107. package/src/evaluator/combiners/ifelseif.ts +9 -7
  108. package/src/evaluator/condition/engineUtils.ts +1 -1
  109. package/src/evaluator/condition/index.ts +12 -12
  110. package/src/evaluator/index.ts +7 -7
  111. package/src/evaluator/matchers/index.ts +1 -3
  112. package/src/evaluator/matchers/matcherTypes.ts +0 -1
  113. package/src/evaluator/matchersTransform/index.ts +0 -3
  114. package/src/evaluator/parser/index.ts +3 -3
  115. package/src/evaluator/types.ts +2 -2
  116. package/src/evaluator/value/index.ts +2 -2
  117. package/src/evaluator/value/sanitize.ts +4 -5
  118. package/src/logger/constants.ts +0 -1
  119. package/src/logger/messages/debug.ts +2 -3
  120. package/src/logger/messages/error.ts +1 -1
  121. package/src/logger/messages/warn.ts +2 -2
  122. package/src/readiness/readinessManager.ts +5 -0
  123. package/src/sdkClient/client.ts +31 -21
  124. package/src/sdkClient/clientAttributesDecoration.ts +20 -27
  125. package/src/sdkClient/clientInputValidation.ts +30 -27
  126. package/src/sdkManager/index.ts +1 -1
  127. package/src/services/splitApi.ts +2 -2
  128. package/src/services/types.ts +1 -1
  129. package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
  130. package/src/storages/AbstractSplitsCacheSync.ts +3 -14
  131. package/src/storages/KeyBuilder.ts +0 -12
  132. package/src/storages/KeyBuilderCS.ts +5 -5
  133. package/src/storages/KeyBuilderSS.ts +0 -4
  134. package/src/storages/dataLoader.ts +3 -1
  135. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +26 -87
  136. package/src/storages/inLocalStorage/index.ts +8 -12
  137. package/src/storages/inLocalStorage/validateCache.ts +91 -0
  138. package/src/storages/inMemory/InMemoryStorage.ts +0 -3
  139. package/src/storages/inMemory/InMemoryStorageCS.ts +0 -4
  140. package/src/storages/inRedis/index.ts +0 -2
  141. package/src/storages/pluggable/index.ts +2 -3
  142. package/src/storages/types.ts +2 -37
  143. package/src/storages/utils.ts +1 -0
  144. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
  145. package/src/sync/polling/fetchers/splitChangesFetcher.ts +1 -2
  146. package/src/sync/polling/fetchers/types.ts +0 -1
  147. package/src/sync/polling/pollingManagerCS.ts +7 -7
  148. package/src/sync/polling/types.ts +2 -2
  149. package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
  150. package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
  151. package/src/sync/polling/updaters/splitChangesUpdater.ts +43 -71
  152. package/src/sync/streaming/SSEHandler/index.ts +1 -2
  153. package/src/sync/streaming/SSEHandler/types.ts +2 -2
  154. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +68 -98
  155. package/src/sync/streaming/constants.ts +0 -1
  156. package/src/sync/streaming/parseUtils.ts +2 -2
  157. package/src/sync/streaming/pushManager.ts +18 -6
  158. package/src/sync/streaming/types.ts +2 -3
  159. package/src/sync/submitters/impressionsSubmitter.ts +3 -2
  160. package/src/sync/submitters/types.ts +23 -33
  161. package/src/sync/syncManagerOnline.ts +11 -5
  162. package/src/trackers/strategy/strategyDebug.ts +2 -0
  163. package/src/trackers/strategy/strategyOptimized.ts +3 -0
  164. package/src/utils/constants/index.ts +1 -2
  165. package/src/utils/inputValidation/eventProperties.ts +10 -0
  166. package/src/utils/inputValidation/index.ts +1 -0
  167. package/src/utils/lang/index.ts +2 -2
  168. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  169. package/types/splitio.d.ts +128 -36
  170. package/cjs/evaluator/matchers/rbsegment.js +0 -43
  171. package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -117
  172. package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +0 -61
  173. package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +0 -64
  174. package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +0 -64
  175. package/cjs/utils/constants/browser.js +0 -5
  176. package/esm/evaluator/matchers/rbsegment.js +0 -39
  177. package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -114
  178. package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +0 -58
  179. package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +0 -61
  180. package/esm/storages/pluggable/RBSegmentsCachePluggable.js +0 -61
  181. package/esm/utils/constants/browser.js +0 -2
  182. package/src/evaluator/matchers/rbsegment.ts +0 -61
  183. package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +0 -136
  184. package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +0 -68
  185. package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +0 -79
  186. package/src/storages/pluggable/RBSegmentsCachePluggable.ts +0 -76
  187. package/src/utils/constants/browser.ts +0 -2
@@ -1,6 +1,6 @@
1
1
  import { errorParser, messageParser } from './NotificationParser';
2
2
  import { notificationKeeperFactory } from './NotificationKeeper';
3
- import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
3
+ import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE } from '../constants';
4
4
  import { IPushEventEmitter } from '../types';
5
5
  import { ISseEventHandler } from '../SSEClient/types';
6
6
  import { INotificationError, INotificationMessage } from './types';
@@ -84,7 +84,6 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
84
84
  case MEMBERSHIPS_MS_UPDATE:
85
85
  case MEMBERSHIPS_LS_UPDATE:
86
86
  case SPLIT_KILL:
87
- case RB_SEGMENT_UPDATE:
88
87
  pushEmitter.emit(parsedData.type, parsedData);
89
88
  break;
90
89
 
@@ -1,5 +1,5 @@
1
1
  import { ControlType } from '../constants';
2
- import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE, RB_SEGMENT_UPDATE } from '../types';
2
+ import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../types';
3
3
 
4
4
  export enum Compression {
5
5
  None = 0,
@@ -42,7 +42,7 @@ export interface ISegmentUpdateData {
42
42
  }
43
43
 
44
44
  export interface ISplitUpdateData {
45
- type: SPLIT_UPDATE | RB_SEGMENT_UPDATE,
45
+ type: SPLIT_UPDATE,
46
46
  changeNumber: number,
47
47
  pcn?: number,
48
48
  d?: string,
@@ -1,16 +1,12 @@
1
- import { IRBSegment, ISplit } from '../../../dtos/types';
2
- import { STREAMING_PARSING_SPLIT_UPDATE } from '../../../logger/constants';
1
+ import { ISplit } from '../../../dtos/types';
3
2
  import { ILogger } from '../../../logger/types';
4
3
  import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
5
4
  import { ISplitsEventEmitter } from '../../../readiness/types';
6
- import { IRBSegmentsCacheSync, ISplitsCacheSync, IStorageSync } from '../../../storages/types';
5
+ import { ISplitsCacheSync } from '../../../storages/types';
7
6
  import { ITelemetryTracker } from '../../../trackers/types';
8
7
  import { Backoff } from '../../../utils/Backoff';
9
8
  import { SPLITS } from '../../../utils/constants';
10
9
  import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
11
- import { InstantUpdate } from '../../polling/updaters/splitChangesUpdater';
12
- import { RB_SEGMENT_UPDATE } from '../constants';
13
- import { parseFFUpdatePayload } from '../parseUtils';
14
10
  import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
15
11
  import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
16
12
  import { IUpdateWorker } from './types';
@@ -18,128 +14,102 @@ import { IUpdateWorker } from './types';
18
14
  /**
19
15
  * SplitsUpdateWorker factory
20
16
  */
21
- export function SplitsUpdateWorker(log: ILogger, storage: IStorageSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData]> & { killSplit(event: ISplitKillData): void } {
17
+ export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData, payload?: ISplit]> & { killSplit(event: ISplitKillData): void } {
22
18
 
23
- const ff = SplitsUpdateWorker(storage.splits);
24
- const rbs = SplitsUpdateWorker(storage.rbSegments);
19
+ let maxChangeNumber = 0;
20
+ let handleNewEvent = false;
21
+ let isHandlingEvent: boolean;
22
+ let cdnBypass: boolean;
23
+ let payload: ISplit | undefined;
24
+ const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
25
25
 
26
- function SplitsUpdateWorker(cache: ISplitsCacheSync | IRBSegmentsCacheSync) {
27
- let maxChangeNumber = -1;
28
- let handleNewEvent = false;
29
- let isHandlingEvent: boolean;
30
- let cdnBypass: boolean;
31
- let instantUpdate: InstantUpdate | undefined;
32
- const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
26
+ function __handleSplitUpdateCall() {
27
+ isHandlingEvent = true;
28
+ if (maxChangeNumber > splitsCache.getChangeNumber()) {
29
+ handleNewEvent = false;
30
+ const splitUpdateNotification = payload ? { payload, changeNumber: maxChangeNumber } : undefined;
31
+ // fetch splits revalidating data if cached
32
+ splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, splitUpdateNotification).then(() => {
33
+ if (!isHandlingEvent) return; // halt if `stop` has been called
34
+ if (handleNewEvent) {
35
+ __handleSplitUpdateCall();
36
+ } else {
37
+ if (splitUpdateNotification) telemetryTracker.trackUpdatesFromSSE(SPLITS);
38
+ // fetch new registered segments for server-side API. Not retrying on error
39
+ if (segmentsSyncTask) segmentsSyncTask.execute(true);
33
40
 
34
- function __handleSplitUpdateCall() {
35
- isHandlingEvent = true;
36
- if (maxChangeNumber > cache.getChangeNumber()) {
37
- handleNewEvent = false;
38
- // fetch splits revalidating data if cached
39
- splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, instantUpdate).then(() => {
40
- if (!isHandlingEvent) return; // halt if `stop` has been called
41
- if (handleNewEvent) {
42
- __handleSplitUpdateCall();
43
- } else {
44
- if (instantUpdate) telemetryTracker.trackUpdatesFromSSE(SPLITS);
45
- // fetch new registered segments for server-side API. Not retrying on error
46
- if (segmentsSyncTask) segmentsSyncTask.execute(true);
47
-
48
- const attempts = backoff.attempts + 1;
41
+ const attempts = backoff.attempts + 1;
49
42
 
50
- if (ff.isSync() && rbs.isSync()) {
51
- log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
52
- isHandlingEvent = false;
53
- return;
54
- }
43
+ if (maxChangeNumber <= splitsCache.getChangeNumber()) {
44
+ log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
45
+ isHandlingEvent = false;
46
+ return;
47
+ }
55
48
 
56
- if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
57
- backoff.scheduleCall();
58
- return;
59
- }
49
+ if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
50
+ backoff.scheduleCall();
51
+ return;
52
+ }
60
53
 
61
- if (cdnBypass) {
62
- log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
63
- isHandlingEvent = false;
64
- } else {
65
- backoff.reset();
66
- cdnBypass = true;
67
- __handleSplitUpdateCall();
68
- }
54
+ if (cdnBypass) {
55
+ log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
56
+ isHandlingEvent = false;
57
+ } else {
58
+ backoff.reset();
59
+ cdnBypass = true;
60
+ __handleSplitUpdateCall();
69
61
  }
70
- });
71
- } else {
72
- isHandlingEvent = false;
73
- }
62
+ }
63
+ });
64
+ } else {
65
+ isHandlingEvent = false;
74
66
  }
67
+ }
75
68
 
76
- return {
77
- /**
78
- * Invoked by NotificationProcessor on SPLIT_UPDATE or RB_SEGMENT_UPDATE event
79
- *
80
- * @param changeNumber - change number of the notification
81
- */
82
- put({ changeNumber, pcn, type }: ISplitUpdateData, payload?: ISplit | IRBSegment) {
83
- const currentChangeNumber = cache.getChangeNumber();
69
+ /**
70
+ * Invoked by NotificationProcessor on SPLIT_UPDATE event
71
+ *
72
+ * @param changeNumber - change number of the SPLIT_UPDATE notification
73
+ */
74
+ function put({ changeNumber, pcn }: ISplitUpdateData, _payload?: ISplit) {
75
+ const currentChangeNumber = splitsCache.getChangeNumber();
84
76
 
85
- if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
77
+ if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
86
78
 
87
- maxChangeNumber = changeNumber;
88
- handleNewEvent = true;
89
- cdnBypass = false;
90
- instantUpdate = undefined;
79
+ maxChangeNumber = changeNumber;
80
+ handleNewEvent = true;
81
+ cdnBypass = false;
82
+ payload = undefined;
91
83
 
92
- if (payload && currentChangeNumber === pcn) {
93
- instantUpdate = { payload, changeNumber, type };
94
- }
84
+ if (_payload && currentChangeNumber === pcn) {
85
+ payload = _payload;
86
+ }
95
87
 
96
- if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
97
- backoff.reset();
98
- },
99
- stop() {
100
- isHandlingEvent = false;
101
- backoff.reset();
102
- },
103
- isSync() {
104
- return maxChangeNumber <= cache.getChangeNumber();
105
- }
106
- };
88
+ if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
89
+ backoff.reset();
107
90
  }
108
91
 
109
92
  return {
110
- put(parsedData) {
111
- if (parsedData.d && parsedData.c !== undefined) {
112
- try {
113
- const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
114
- if (payload) {
115
- (parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData, payload);
116
- return;
117
- }
118
- } catch (e) {
119
- log.warn(STREAMING_PARSING_SPLIT_UPDATE, [parsedData.type, e]);
120
- }
121
- }
122
- (parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData);
123
- },
93
+ put,
124
94
  /**
125
95
  * Invoked by NotificationProcessor on SPLIT_KILL event
126
96
  *
127
- * @param changeNumber - change number of the notification
97
+ * @param changeNumber - change number of the SPLIT_UPDATE notification
128
98
  * @param splitName - name of split to kill
129
99
  * @param defaultTreatment - default treatment value
130
100
  */
131
101
  killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
132
- if (storage.splits.killLocally(splitName, defaultTreatment, changeNumber)) {
102
+ if (splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
133
103
  // trigger an SDK_UPDATE if Split was killed locally
134
104
  splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
135
105
  }
136
106
  // queues the SplitChanges fetch (only if changeNumber is newer)
137
- ff.put({ changeNumber } as ISplitUpdateData);
107
+ put({ changeNumber } as ISplitUpdateData);
138
108
  },
139
109
 
140
110
  stop() {
141
- ff.stop();
142
- rbs.stop();
111
+ isHandlingEvent = false;
112
+ backoff.reset();
143
113
  }
144
114
  };
145
115
  }
@@ -30,7 +30,6 @@ export const MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
30
30
  export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
31
31
  export const SPLIT_KILL = 'SPLIT_KILL';
32
32
  export const SPLIT_UPDATE = 'SPLIT_UPDATE';
33
- export const RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
34
33
 
35
34
  // Control-type push notifications, handled by NotificationKeeper
36
35
  export const CONTROL = 'CONTROL';
@@ -2,7 +2,7 @@ import { algorithms } from '../../utils/decompress';
2
2
  import { decodeFromBase64 } from '../../utils/base64';
3
3
  import { hash } from '../../utils/murmur3/murmur3';
4
4
  import { Compression, IMembershipMSUpdateData, KeyList } from './SSEHandler/types';
5
- import { IRBSegment, ISplit } from '../../dtos/types';
5
+ import { ISplit } from '../../dtos/types';
6
6
 
7
7
  const GZIP = 1;
8
8
  const ZLIB = 2;
@@ -82,7 +82,7 @@ export function isInBitmap(bitmap: Uint8Array, hash64hex: string) {
82
82
  /**
83
83
  * Parse feature flags notifications for instant feature flag updates
84
84
  */
85
- export function parseFFUpdatePayload(compression: Compression, data: string): ISplit | IRBSegment | undefined {
85
+ export function parseFFUpdatePayload(compression: Compression, data: string): ISplit | undefined {
86
86
  return compression > 0 ?
87
87
  parseKeyList(data, compression, false) :
88
88
  JSON.parse(decodeFromBase64(data));
@@ -11,10 +11,10 @@ import { authenticateFactory, hashUserKey } from './AuthClient';
11
11
  import { forOwn } from '../../utils/lang';
12
12
  import { SSEClient } from './SSEClient';
13
13
  import { getMatching } from '../../utils/key';
14
- import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
15
- import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE } from '../../logger/constants';
14
+ import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
15
+ import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE, STREAMING_PARSING_SPLIT_UPDATE } from '../../logger/constants';
16
16
  import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types';
17
- import { getDelay, isInBitmap, parseBitmap, parseKeyList } from './parseUtils';
17
+ import { getDelay, isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
18
18
  import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
19
19
  import { IAuthTokenPushEnabled } from './AuthClient/types';
20
20
  import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
@@ -56,7 +56,7 @@ export function pushManagerFactory(
56
56
  // MySegmentsUpdateWorker (client-side) are initiated in `add` method
57
57
  const segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask as ISegmentsSyncTask, storage.segments);
58
58
  // For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
59
- const splitsUpdateWorker = SplitsUpdateWorker(log, storage, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
59
+ const splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
60
60
 
61
61
  // [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker
62
62
  const userKeyHashes: Record<string, string> = {};
@@ -219,8 +219,20 @@ export function pushManagerFactory(
219
219
  /** Functions related to synchronization (Queues and Workers in the spec) */
220
220
 
221
221
  pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
222
- pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
223
- pushEmitter.on(RB_SEGMENT_UPDATE, splitsUpdateWorker.put);
222
+ pushEmitter.on(SPLIT_UPDATE, (parsedData) => {
223
+ if (parsedData.d && parsedData.c !== undefined) {
224
+ try {
225
+ const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
226
+ if (payload) {
227
+ splitsUpdateWorker.put(parsedData, payload);
228
+ return;
229
+ }
230
+ } catch (e) {
231
+ log.warn(STREAMING_PARSING_SPLIT_UPDATE, [e]);
232
+ }
233
+ }
234
+ splitsUpdateWorker.put(parsedData);
235
+ });
224
236
 
225
237
  function handleMySegmentsUpdate(parsedData: IMembershipMSUpdateData | IMembershipLSUpdateData) {
226
238
  switch (parsedData.u) {
@@ -16,19 +16,18 @@ export type MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
16
16
  export type SEGMENT_UPDATE = 'SEGMENT_UPDATE';
17
17
  export type SPLIT_KILL = 'SPLIT_KILL';
18
18
  export type SPLIT_UPDATE = 'SPLIT_UPDATE';
19
- export type RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
20
19
 
21
20
  // Control-type push notifications, handled by NotificationKeeper
22
21
  export type CONTROL = 'CONTROL';
23
22
  export type OCCUPANCY = 'OCCUPANCY';
24
23
 
25
- export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | RB_SEGMENT_UPDATE | ControlType.STREAMING_RESET
24
+ export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | ControlType.STREAMING_RESET
26
25
 
27
26
  type IParsedData<T extends IPushEvent> =
28
27
  T extends MEMBERSHIPS_MS_UPDATE ? IMembershipMSUpdateData :
29
28
  T extends MEMBERSHIPS_LS_UPDATE ? IMembershipLSUpdateData :
30
29
  T extends SEGMENT_UPDATE ? ISegmentUpdateData :
31
- T extends SPLIT_UPDATE | RB_SEGMENT_UPDATE ? ISplitUpdateData :
30
+ T extends SPLIT_UPDATE ? ISplitUpdateData :
32
31
  T extends SPLIT_KILL ? ISplitKillData : INotificationData;
33
32
 
34
33
  /**
@@ -25,8 +25,9 @@ export function fromImpressionsCollector(sendLabels: boolean, data: SplitIO.Impr
25
25
  m: entry.time, // Timestamp
26
26
  c: entry.changeNumber, // ChangeNumber
27
27
  r: sendLabels ? entry.label : undefined, // Rule
28
- b: entry.bucketingKey ? entry.bucketingKey : undefined, // Bucketing Key
29
- pt: entry.pt ? entry.pt : undefined // Previous time
28
+ b: entry.bucketingKey, // Bucketing Key
29
+ pt: entry.pt, // Previous time
30
+ properties: entry.properties // Properties
30
31
  };
31
32
 
32
33
  return keyImpression;
@@ -3,26 +3,30 @@ import { IMetadata } from '../../dtos/types';
3
3
  import SplitIO from '../../../types/splitio';
4
4
  import { ISyncTask } from '../types';
5
5
 
6
+ type ImpressionPayload = {
7
+ /** Matching Key */
8
+ k: string;
9
+ /** Bucketing Key */
10
+ b?: string;
11
+ /** Treatment */
12
+ t: string;
13
+ /** Timestamp */
14
+ m: number;
15
+ /** Change number */
16
+ c: number;
17
+ /** Rule label */
18
+ r?: string;
19
+ /** Previous time */
20
+ pt?: number;
21
+ /** Stringified JSON object with properties */
22
+ properties?: string;
23
+ };
24
+
6
25
  export type ImpressionsPayload = {
7
26
  /** Split name */
8
27
  f: string,
9
28
  /** Key Impressions */
10
- i: {
11
- /** User Key */
12
- k: string;
13
- /** Treatment */
14
- t: string;
15
- /** Timestamp */
16
- m: number;
17
- /** ChangeNumber */
18
- c: number;
19
- /** Rule label */
20
- r?: string;
21
- /** Bucketing Key */
22
- b?: string;
23
- /** Previous time */
24
- pt?: number;
25
- }[]
29
+ i: ImpressionPayload[]
26
30
  }[]
27
31
 
28
32
  export type ImpressionCountsPayload = {
@@ -60,23 +64,9 @@ export type StoredImpressionWithMetadata = {
60
64
  /** Metadata */
61
65
  m: IMetadata,
62
66
  /** Stored impression */
63
- i: {
64
- /** keyName */
65
- k: string,
66
- /** bucketingKey */
67
- b?: string,
68
- /** Split name */
69
- f: string,
70
- /** treatment */
71
- t: string,
72
- /** label */
73
- r: string,
74
- /** changeNumber */
75
- c: number,
76
- /** time */
77
- m: number
78
- /** previous time */
79
- pt?: number
67
+ i: ImpressionPayload & {
68
+ /** Feature flag name */
69
+ f: string
80
70
  }
81
71
  }
82
72
 
@@ -9,6 +9,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '..
9
9
  import { isConsentGranted } from '../consent';
10
10
  import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
11
11
  import { ISdkFactoryContextSync } from '../sdkFactory/types';
12
+ import { SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
12
13
 
13
14
  /**
14
15
  * Online SyncManager factory.
@@ -28,7 +29,7 @@ export function syncManagerOnlineFactory(
28
29
  */
29
30
  return function (params: ISdkFactoryContextSync): ISyncManagerCS {
30
31
 
31
- const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } }, telemetryTracker } = params;
32
+ const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } }, telemetryTracker, storage, readiness } = params;
32
33
 
33
34
  /** Polling Manager */
34
35
  const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
@@ -87,6 +88,11 @@ export function syncManagerOnlineFactory(
87
88
  start() {
88
89
  running = true;
89
90
 
91
+ if (startFirstTime) {
92
+ const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
93
+ if (isCacheLoaded) Promise.resolve().then(() => { readiness.splits.emit(SDK_SPLITS_CACHE_LOADED); });
94
+ }
95
+
90
96
  // start syncing splits and segments
91
97
  if (pollingManager) {
92
98
 
@@ -96,7 +102,6 @@ export function syncManagerOnlineFactory(
96
102
  // Doesn't call `syncAll` when the syncManager is resuming
97
103
  if (startFirstTime) {
98
104
  pollingManager.syncAll();
99
- startFirstTime = false;
100
105
  }
101
106
  pushManager.start();
102
107
  } else {
@@ -105,13 +110,14 @@ export function syncManagerOnlineFactory(
105
110
  } else {
106
111
  if (startFirstTime) {
107
112
  pollingManager.syncAll();
108
- startFirstTime = false;
109
113
  }
110
114
  }
111
115
  }
112
116
 
113
117
  // start periodic data recording (events, impressions, telemetry).
114
118
  submitterManager.start(!isConsentGranted(settings));
119
+
120
+ startFirstTime = false;
115
121
  },
116
122
 
117
123
  /**
@@ -149,14 +155,14 @@ export function syncManagerOnlineFactory(
149
155
  if (pushManager) {
150
156
  if (pollingManager.isRunning()) {
151
157
  // if doing polling, we must start the periodic fetch of data
152
- if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) mySegmentsSyncTask.start();
158
+ if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
153
159
  } else {
154
160
  // if not polling, we must execute the sync task for the initial fetch
155
161
  // of segments since `syncAll` was already executed when starting the main client
156
162
  mySegmentsSyncTask.execute();
157
163
  }
158
164
  } else {
159
- if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) mySegmentsSyncTask.start();
165
+ if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
160
166
  }
161
167
  } else {
162
168
  if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
@@ -14,6 +14,8 @@ export function strategyDebugFactory(
14
14
 
15
15
  return {
16
16
  process(impression: SplitIO.ImpressionDTO) {
17
+ if (impression.properties) return true;
18
+
17
19
  impression.pt = impressionsObserver.testAndSet(impression);
18
20
  return true;
19
21
  }
@@ -18,6 +18,9 @@ export function strategyOptimizedFactory(
18
18
 
19
19
  return {
20
20
  process(impression: SplitIO.ImpressionDTO) {
21
+ // DEBUG mode without previous time, for impressions with properties
22
+ if (impression.properties) return true;
23
+
21
24
  impression.pt = impressionsObserver.testAndSet(impression);
22
25
 
23
26
  const now = Date.now();
@@ -104,9 +104,8 @@ export const DISABLED = 0;
104
104
  export const ENABLED = 1;
105
105
  export const PAUSED = 2;
106
106
 
107
- export const FLAG_SPEC_VERSION = '1.3';
107
+ export const FLAG_SPEC_VERSION = '1.2';
108
108
 
109
109
  // Matcher types
110
110
  export const IN_SEGMENT = 'IN_SEGMENT';
111
111
  export const IN_LARGE_SEGMENT = 'IN_LARGE_SEGMENT';
112
- export const IN_RULE_BASED_SEGMENT = 'IN_RULE_BASED_SEGMENT';
@@ -66,3 +66,13 @@ export function validateEventProperties(log: ILogger, maybeProperties: any, meth
66
66
 
67
67
  return output;
68
68
  }
69
+
70
+ export function validateEvaluationOptions(log: ILogger, maybeOptions: any, method: string): SplitIO.EvaluationOptions | undefined {
71
+ if (isObject(maybeOptions)) {
72
+ const properties = validateEventProperties(log, maybeOptions.properties, method).properties;
73
+ return properties && Object.keys(properties).length > 0 ? { properties } : undefined;
74
+ } else if (maybeOptions) {
75
+ log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']);
76
+ }
77
+ return undefined;
78
+ }
@@ -11,3 +11,4 @@ export { validateIfNotDestroyed, validateIfOperational } from './isOperational';
11
11
  export { validateSplitExistence } from './splitExistence';
12
12
  export { validateTrafficTypeExistence } from './trafficTypeExistence';
13
13
  export { validatePreloadedData } from './preloadedData';
14
+ export { validateEvaluationOptions } from './eventProperties';
@@ -111,7 +111,7 @@ export function groupBy<T extends Record<string, any>>(source: T[], prop: string
111
111
  /**
112
112
  * Checks if a given value is a boolean.
113
113
  */
114
- export function isBoolean(val: any): val is boolean {
114
+ export function isBoolean(val: any): boolean {
115
115
  return val === true || val === false;
116
116
  }
117
117
 
@@ -120,7 +120,7 @@ export function isBoolean(val: any): val is boolean {
120
120
  * Unlike `Number.isFinite`, it also tests Number object instances.
121
121
  * Unlike global `isFinite`, it returns false if the value is not a number or Number object instance.
122
122
  */
123
- export function isFiniteNumber(val: any): boolean {
123
+ export function isFiniteNumber(val: any): val is number {
124
124
  if (val instanceof Number) val = val.valueOf();
125
125
  return typeof val === 'number' ?
126
126
  Number.isFinite ? Number.isFinite(val) : isFinite(val) :
@@ -8,7 +8,7 @@ import { IStorageFactoryParams, IStorageSync } from '../../../storages/types';
8
8
 
9
9
  export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync {
10
10
  const result = InMemoryStorageCSFactory(params);
11
- result.splits.checkCache = () => true; // to emit SDK_READY_FROM_CACHE
11
+ result.validateCache = () => true; // to emit SDK_READY_FROM_CACHE
12
12
  return result;
13
13
  }
14
14
  __InLocalStorageMockFactory.type = STORAGE_MEMORY;