@splitsoftware/splitio-commons 2.0.3-rc.0 → 2.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGES.txt +5 -2
  2. package/README.md +2 -2
  3. package/cjs/evaluator/index.js +0 -2
  4. package/cjs/evaluator/matchers/large_segment.js +0 -6
  5. package/cjs/evaluator/matchers/segment.js +0 -6
  6. package/cjs/listeners/browser.js +6 -4
  7. package/cjs/listeners/node.js +2 -2
  8. package/cjs/readiness/readinessManager.js +6 -0
  9. package/cjs/sdkClient/client.js +13 -13
  10. package/cjs/sdkClient/sdkClient.js +1 -1
  11. package/cjs/sdkFactory/index.js +14 -9
  12. package/cjs/sdkManager/index.js +1 -2
  13. package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
  14. package/cjs/storages/AbstractSplitsCacheSync.js +0 -7
  15. package/cjs/storages/KeyBuilderCS.js +3 -0
  16. package/cjs/storages/dataLoader.js +3 -2
  17. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
  18. package/cjs/storages/inLocalStorage/index.js +8 -7
  19. package/cjs/storages/inLocalStorage/validateCache.js +79 -0
  20. package/cjs/storages/inMemory/InMemoryStorage.js +3 -3
  21. package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -4
  22. package/cjs/storages/inRedis/SplitsCacheInRedis.js +1 -1
  23. package/cjs/storages/inRedis/index.js +13 -9
  24. package/cjs/storages/pluggable/index.js +21 -16
  25. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  26. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -10
  27. package/cjs/sync/streaming/pushManager.js +8 -6
  28. package/cjs/sync/submitters/impressionCountsSubmitter.js +4 -2
  29. package/cjs/sync/submitters/submitterManager.js +6 -3
  30. package/cjs/sync/syncManagerOnline.js +10 -4
  31. package/cjs/trackers/eventTracker.js +1 -1
  32. package/cjs/trackers/impressionsTracker.js +19 -18
  33. package/cjs/trackers/strategy/strategyDebug.js +11 -4
  34. package/cjs/trackers/strategy/strategyNone.js +16 -11
  35. package/cjs/trackers/strategy/strategyOptimized.js +21 -11
  36. package/cjs/utils/settingsValidation/index.js +1 -1
  37. package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
  38. package/esm/evaluator/index.js +0 -2
  39. package/esm/evaluator/matchers/large_segment.js +0 -6
  40. package/esm/evaluator/matchers/segment.js +0 -6
  41. package/esm/listeners/browser.js +3 -1
  42. package/esm/listeners/node.js +2 -2
  43. package/esm/readiness/readinessManager.js +6 -0
  44. package/esm/sdkClient/client.js +13 -13
  45. package/esm/sdkClient/sdkClient.js +1 -1
  46. package/esm/sdkFactory/index.js +15 -10
  47. package/esm/sdkManager/index.js +1 -2
  48. package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
  49. package/esm/storages/AbstractSplitsCacheSync.js +0 -7
  50. package/esm/storages/KeyBuilderCS.js +3 -0
  51. package/esm/storages/dataLoader.js +2 -1
  52. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -57
  53. package/esm/storages/inLocalStorage/index.js +9 -8
  54. package/esm/storages/inLocalStorage/validateCache.js +75 -0
  55. package/esm/storages/inMemory/InMemoryStorage.js +4 -4
  56. package/esm/storages/inMemory/InMemoryStorageCS.js +4 -5
  57. package/esm/storages/inRedis/SplitsCacheInRedis.js +1 -1
  58. package/esm/storages/inRedis/index.js +14 -10
  59. package/esm/storages/pluggable/index.js +22 -17
  60. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
  61. package/esm/sync/polling/updaters/splitChangesUpdater.js +2 -11
  62. package/esm/sync/streaming/pushManager.js +8 -6
  63. package/esm/sync/submitters/impressionCountsSubmitter.js +4 -2
  64. package/esm/sync/submitters/submitterManager.js +6 -3
  65. package/esm/sync/syncManagerOnline.js +10 -4
  66. package/esm/trackers/eventTracker.js +1 -1
  67. package/esm/trackers/impressionsTracker.js +19 -18
  68. package/esm/trackers/strategy/strategyDebug.js +11 -4
  69. package/esm/trackers/strategy/strategyNone.js +16 -11
  70. package/esm/trackers/strategy/strategyOptimized.js +21 -11
  71. package/esm/utils/settingsValidation/index.js +1 -1
  72. package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
  73. package/package.json +1 -1
  74. package/src/dtos/types.ts +1 -2
  75. package/src/evaluator/index.ts +0 -2
  76. package/src/evaluator/matchers/large_segment.ts +0 -7
  77. package/src/evaluator/matchers/segment.ts +0 -7
  78. package/src/evaluator/types.ts +1 -1
  79. package/src/listeners/browser.ts +3 -1
  80. package/src/listeners/node.ts +2 -2
  81. package/src/readiness/readinessManager.ts +5 -0
  82. package/src/sdkClient/client.ts +11 -11
  83. package/src/sdkClient/sdkClient.ts +1 -1
  84. package/src/sdkFactory/index.ts +16 -11
  85. package/src/sdkFactory/types.ts +1 -1
  86. package/src/sdkManager/index.ts +1 -2
  87. package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
  88. package/src/storages/AbstractSplitsCacheSync.ts +0 -8
  89. package/src/storages/KeyBuilderCS.ts +4 -0
  90. package/src/storages/dataLoader.ts +3 -1
  91. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +1 -66
  92. package/src/storages/inLocalStorage/index.ts +12 -13
  93. package/src/storages/inLocalStorage/validateCache.ts +91 -0
  94. package/src/storages/inMemory/InMemoryStorage.ts +4 -4
  95. package/src/storages/inMemory/InMemoryStorageCS.ts +4 -5
  96. package/src/storages/inRedis/SplitsCacheInRedis.ts +1 -1
  97. package/src/storages/inRedis/index.ts +10 -10
  98. package/src/storages/pluggable/index.ts +22 -17
  99. package/src/storages/types.ts +3 -6
  100. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
  101. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -11
  102. package/src/sync/streaming/pushManager.ts +8 -6
  103. package/src/sync/submitters/impressionCountsSubmitter.ts +4 -2
  104. package/src/sync/submitters/submitterManager.ts +4 -3
  105. package/src/sync/submitters/uniqueKeysSubmitter.ts +3 -2
  106. package/src/sync/syncManagerOnline.ts +11 -5
  107. package/src/trackers/eventTracker.ts +1 -1
  108. package/src/trackers/impressionsTracker.ts +19 -18
  109. package/src/trackers/strategy/strategyDebug.ts +11 -4
  110. package/src/trackers/strategy/strategyNone.ts +17 -11
  111. package/src/trackers/strategy/strategyOptimized.ts +20 -10
  112. package/src/trackers/types.ts +8 -2
  113. package/src/utils/lang/index.ts +1 -1
  114. package/src/utils/settingsValidation/index.ts +1 -1
  115. package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
  116. package/types/index.d.ts +1 -1
  117. package/types/splitio.d.ts +26 -6
  118. package/cjs/utils/constants/browser.js +0 -5
  119. package/esm/utils/constants/browser.js +0 -2
  120. package/src/utils/constants/browser.ts +0 -2
@@ -1,6 +1,6 @@
1
1
  import { forOwn } from '../../../utils/lang';
2
2
  import { IReadinessManager } from '../../../readiness/types';
3
- import { ISplitsCacheSync } from '../../../storages/types';
3
+ import { IStorageSync } from '../../../storages/types';
4
4
  import { ISplitsParser } from '../splitsParser/types';
5
5
  import { ISplit, ISplitPartial } from '../../../dtos/types';
6
6
  import { syncTaskFactory } from '../../syncTask';
@@ -15,7 +15,7 @@ import { SYNC_OFFLINE_DATA, ERROR_SYNC_OFFLINE_LOADING } from '../../../logger/c
15
15
  */
16
16
  export function fromObjectUpdaterFactory(
17
17
  splitsParser: ISplitsParser,
18
- storage: { splits: ISplitsCacheSync },
18
+ storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
19
19
  readiness: IReadinessManager,
20
20
  settings: ISettings,
21
21
  ): () => Promise<boolean> {
@@ -60,9 +60,10 @@ export function fromObjectUpdaterFactory(
60
60
 
61
61
  if (startingUp) {
62
62
  startingUp = false;
63
- Promise.resolve(splitsCache.checkCache()).then(cacheReady => {
63
+ const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
64
+ Promise.resolve().then(() => {
64
65
  // Emits SDK_READY_FROM_CACHE
65
- if (cacheReady) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
66
+ if (isCacheLoaded) readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
66
67
  // Emits SDK_READY
67
68
  readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
68
69
  });
@@ -80,7 +81,7 @@ export function fromObjectUpdaterFactory(
80
81
  */
81
82
  export function fromObjectSyncTaskFactory(
82
83
  splitsParser: ISplitsParser,
83
- storage: { splits: ISplitsCacheSync },
84
+ storage: Pick<IStorageSync, 'splits' | 'validateCache'>,
84
85
  readiness: IReadinessManager,
85
86
  settings: ISettings
86
87
  ): ISyncTask<[], boolean> {
@@ -3,7 +3,7 @@ import { ISplitChangesFetcher } from '../fetchers/types';
3
3
  import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
4
4
  import { ISplitsEventEmitter } from '../../../readiness/types';
5
5
  import { timeout } from '../../../utils/promise/timeout';
6
- import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
6
+ import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
7
7
  import { ILogger } from '../../../logger/types';
8
8
  import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
9
9
  import { startsWith } from '../../../utils/lang';
@@ -153,7 +153,7 @@ export function splitChangesUpdaterFactory(
153
153
  */
154
154
  function _splitChangesUpdater(since: number, retry = 0): Promise<boolean> {
155
155
  log.debug(SYNC_SPLITS_FETCH, [since]);
156
- const fetcherPromise = Promise.resolve(splitUpdateNotification ?
156
+ return Promise.resolve(splitUpdateNotification ?
157
157
  { splits: [splitUpdateNotification.payload], till: splitUpdateNotification.changeNumber } :
158
158
  splitChangesFetcher(since, noCache, till, _promiseDecorator)
159
159
  )
@@ -200,15 +200,6 @@ export function splitChangesUpdaterFactory(
200
200
  }
201
201
  return false;
202
202
  });
203
-
204
- // After triggering the requests, if we have cached splits information let's notify that to emit SDK_READY_FROM_CACHE.
205
- // Wrapping in a promise since checkCache can be async.
206
- if (splitsEventEmitter && startingUp) {
207
- Promise.resolve(splits.checkCache()).then(isCacheReady => {
208
- if (isCacheReady) splitsEventEmitter.emit(SDK_SPLITS_CACHE_LOADED);
209
- });
210
- }
211
- return fetcherPromise;
212
203
  }
213
204
 
214
205
  let sincePromise = Promise.resolve(splits.getChangeNumber()); // `getChangeNumber` never rejects or throws error
@@ -349,12 +349,14 @@ export function pushManagerFactory(
349
349
  // Reconnects in case of a new client.
350
350
  // Run in next event-loop cycle to save authentication calls
351
351
  // in case multiple clients are created in the current cycle.
352
- setTimeout(function checkForReconnect() {
353
- if (connectForNewClient) {
354
- connectForNewClient = false;
355
- connectPush();
356
- }
357
- }, 0);
352
+ if (this.isRunning()) {
353
+ setTimeout(function checkForReconnect() {
354
+ if (connectForNewClient) {
355
+ connectForNewClient = false;
356
+ connectPush();
357
+ }
358
+ }, 0);
359
+ }
358
360
  }
359
361
  },
360
362
  // [Only for client-side]
@@ -39,6 +39,8 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync)
39
39
  storage: { impressionCounts }
40
40
  } = params;
41
41
 
42
- // retry impressions counts only once.
43
- return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
42
+ if (impressionCounts) {
43
+ // retry impressions counts only once.
44
+ return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
45
+ }
44
46
  }
@@ -10,12 +10,13 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
10
10
 
11
11
  const submitters = [
12
12
  impressionsSubmitterFactory(params),
13
- eventsSubmitterFactory(params),
14
- impressionCountsSubmitterFactory(params),
15
- uniqueKeysSubmitterFactory(params)
13
+ eventsSubmitterFactory(params)
16
14
  ];
17
15
 
16
+ const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
17
+ if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
18
18
  const telemetrySubmitter = telemetrySubmitterFactory(params);
19
+ if (params.storage.uniqueKeys) submitters.push(uniqueKeysSubmitterFactory(params));
19
20
 
20
21
  return {
21
22
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
@@ -19,10 +19,10 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
19
19
  const isClientSide = key !== undefined;
20
20
  const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
21
21
 
22
- const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE, DATA_NAME);
22
+ const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, UNIQUE_KEYS_RATE, DATA_NAME);
23
23
 
24
24
  // register unique keys submitter to be executed when uniqueKeys cache is full
25
- uniqueKeys.setOnFullQueueCb(() => {
25
+ uniqueKeys!.setOnFullQueueCb(() => {
26
26
  if (syncTask.isRunning()) {
27
27
  log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
28
28
  syncTask.execute();
@@ -33,3 +33,4 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
33
33
 
34
34
  return syncTask;
35
35
  }
36
+
@@ -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
  /**
@@ -142,11 +148,12 @@ export function syncManagerOnlineFactory(
142
148
  if (!pollingManager) return;
143
149
 
144
150
  const mySegmentsSyncTask = (pollingManager as IPollingManagerCS).add(matchingKey, readinessManager, storage);
151
+ if (syncEnabled && pushManager) pushManager.add(matchingKey, mySegmentsSyncTask);
145
152
 
146
153
  if (running) {
147
154
  if (syncEnabled) {
148
155
  if (pushManager) {
149
- if (pollingManager!.isRunning()) {
156
+ if (pollingManager.isRunning()) {
150
157
  // if doing polling, we must start the periodic fetch of data
151
158
  if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
152
159
  } else {
@@ -154,7 +161,6 @@ export function syncManagerOnlineFactory(
154
161
  // of segments since `syncAll` was already executed when starting the main client
155
162
  mySegmentsSyncTask.execute();
156
163
  }
157
- pushManager.add(matchingKey, mySegmentsSyncTask);
158
164
  } else {
159
165
  if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
160
166
  }
@@ -36,7 +36,7 @@ export function eventTrackerFactory(
36
36
  whenInit(() => {
37
37
  // Wrap in a timeout because we don't want it to be blocking.
38
38
  setTimeout(() => {
39
- // copy of event, to avoid unexpected behaviour if modified by integrations
39
+ // copy of event, to avoid unexpected behavior if modified by integrations
40
40
  const eventDataCopy = objectAssign({}, eventData);
41
41
  if (properties) eventDataCopy.properties = objectAssign({}, properties);
42
42
  // integrationsManager does not throw errors (they are internally handled by each integration module)
@@ -9,11 +9,16 @@ import SplitIO from '../../types/splitio';
9
9
 
10
10
  /**
11
11
  * Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
12
+ *
13
+ * @param impressionsCache - cache to save impressions
14
+ * @param metadata - runtime metadata (ip, hostname and version)
15
+ * @param impressionListener - optional impression listener
16
+ * @param integrationsManager - optional integrations manager
17
+ * @param strategy - strategy for impressions tracking.
12
18
  */
13
19
  export function impressionsTrackerFactory(
14
20
  settings: ISettings,
15
21
  impressionsCache: IImpressionsCacheBase,
16
- noneStrategy: IStrategy,
17
22
  strategy: IStrategy,
18
23
  whenInit: (cb: () => void) => void,
19
24
  integrationsManager?: IImpressionsHandler,
@@ -23,44 +28,40 @@ export function impressionsTrackerFactory(
23
28
  const { log, impressionListener, runtime: { ip, hostname }, version } = settings;
24
29
 
25
30
  return {
26
- track(impressions: [impression: SplitIO.ImpressionDTO, track?: boolean][], attributes?: SplitIO.Attributes) {
31
+ track(impressions: SplitIO.ImpressionDTO[], attributes?: SplitIO.Attributes) {
27
32
  if (settings.userConsent === CONSENT_DECLINED) return;
28
33
 
29
- const impressionsToStore = impressions.filter(([impression, track]) => {
30
- return track === false ?
31
- noneStrategy.process(impression) :
32
- strategy.process(impression);
33
- });
34
+ const impressionsCount = impressions.length;
35
+ const { impressionsToStore, impressionsToListener, deduped } = strategy.process(impressions);
34
36
 
35
- const impressionsLength = impressions.length;
36
- const impressionsToStoreLength = impressionsToStore.length;
37
+ const impressionsToListenerCount = impressionsToListener.length;
37
38
 
38
- if (impressionsToStoreLength) {
39
- const res = impressionsCache.track(impressionsToStore.map((item) => item[0]));
39
+ if (impressionsToStore.length > 0) {
40
+ const res = impressionsCache.track(impressionsToStore);
40
41
 
41
42
  // If we're on an async storage, handle error and log it.
42
43
  if (thenable(res)) {
43
44
  res.then(() => {
44
- log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsLength]);
45
+ log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
45
46
  }).catch(err => {
46
- log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsLength, err]);
47
+ log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
47
48
  });
48
49
  } else {
49
50
  // Record when impressionsCache is sync only (standalone mode)
50
51
  // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
51
52
  if (telemetryCache) {
52
- (telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStoreLength);
53
- (telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, impressionsLength - impressionsToStoreLength);
53
+ (telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStore.length);
54
+ (telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, deduped);
54
55
  }
55
56
  }
56
57
  }
57
58
 
58
59
  // @TODO next block might be handled by the integration manager. In that case, the metadata object doesn't need to be passed in the constructor
59
60
  if (impressionListener || integrationsManager) {
60
- for (let i = 0; i < impressionsLength; i++) {
61
+ for (let i = 0; i < impressionsToListenerCount; i++) {
61
62
  const impressionData: SplitIO.ImpressionData = {
62
- // copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
63
- impression: objectAssign({}, impressions[i][0]),
63
+ // copy of impression, to avoid unexpected behavior if modified by integrations or impressionListener
64
+ impression: objectAssign({}, impressionsToListener[i]),
64
65
  attributes,
65
66
  ip,
66
67
  hostname,
@@ -6,16 +6,23 @@ import { IStrategy } from '../types';
6
6
  * Debug strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
7
7
  *
8
8
  * @param impressionsObserver - impression observer. Previous time (pt property) is included in impression instances
9
- * @returns Debug strategy
9
+ * @returns IStrategyResult
10
10
  */
11
11
  export function strategyDebugFactory(
12
12
  impressionsObserver: IImpressionObserver
13
13
  ): IStrategy {
14
14
 
15
15
  return {
16
- process(impression: SplitIO.ImpressionDTO) {
17
- impression.pt = impressionsObserver.testAndSet(impression);
18
- return true;
16
+ process(impressions: SplitIO.ImpressionDTO[]) {
17
+ impressions.forEach((impression) => {
18
+ // Adds previous time if it is enabled
19
+ impression.pt = impressionsObserver.testAndSet(impression);
20
+ });
21
+ return {
22
+ impressionsToStore: impressions,
23
+ impressionsToListener: impressions,
24
+ deduped: 0
25
+ };
19
26
  }
20
27
  };
21
28
  }
@@ -5,24 +5,30 @@ import { IStrategy, IUniqueKeysTracker } from '../types';
5
5
  /**
6
6
  * None strategy for impressions tracker.
7
7
  *
8
- * @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
8
+ * @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
9
9
  * @param uniqueKeysTracker - unique keys tracker in charge of tracking the unique keys per split.
10
- * @returns None strategy
10
+ * @returns IStrategyResult
11
11
  */
12
12
  export function strategyNoneFactory(
13
- impressionCounts: IImpressionCountsCacheBase,
13
+ impressionsCounter: IImpressionCountsCacheBase,
14
14
  uniqueKeysTracker: IUniqueKeysTracker
15
15
  ): IStrategy {
16
16
 
17
17
  return {
18
- process(impression: SplitIO.ImpressionDTO) {
19
- const now = Date.now();
20
- // Increments impression counter per featureName
21
- impressionCounts.track(impression.feature, now, 1);
22
- // Keep track by unique key
23
- uniqueKeysTracker.track(impression.keyName, impression.feature);
24
- // Do not store impressions
25
- return false;
18
+ process(impressions: SplitIO.ImpressionDTO[]) {
19
+ impressions.forEach((impression) => {
20
+ const now = Date.now();
21
+ // Increments impression counter per featureName
22
+ impressionsCounter.track(impression.feature, now, 1);
23
+ // Keep track by unique key
24
+ uniqueKeysTracker.track(impression.keyName, impression.feature);
25
+ });
26
+
27
+ return {
28
+ impressionsToStore: [],
29
+ impressionsToListener: impressions,
30
+ deduped: 0
31
+ };
26
32
  }
27
33
  };
28
34
  }
@@ -8,25 +8,35 @@ import { IStrategy } from '../types';
8
8
  * Optimized strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
9
9
  *
10
10
  * @param impressionsObserver - impression observer. previous time (pt property) is included in impression instances
11
- * @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
12
- * @returns Optimized strategy
11
+ * @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
12
+ * @returns IStrategyResult
13
13
  */
14
14
  export function strategyOptimizedFactory(
15
15
  impressionsObserver: IImpressionObserver,
16
- impressionCounts: IImpressionCountsCacheBase,
16
+ impressionsCounter: IImpressionCountsCacheBase,
17
17
  ): IStrategy {
18
18
 
19
19
  return {
20
- process(impression: SplitIO.ImpressionDTO) {
21
- impression.pt = impressionsObserver.testAndSet(impression);
20
+ process(impressions: SplitIO.ImpressionDTO[]) {
21
+ const impressionsToStore: SplitIO.ImpressionDTO[] = [];
22
+ impressions.forEach((impression) => {
23
+ impression.pt = impressionsObserver.testAndSet(impression);
22
24
 
23
- const now = Date.now();
25
+ const now = Date.now();
24
26
 
25
- // Increments impression counter per featureName
26
- if (impression.pt) impressionCounts.track(impression.feature, now, 1);
27
+ // Increments impression counter per featureName
28
+ if (impression.pt) impressionsCounter.track(impression.feature, now, 1);
27
29
 
28
- // Checks if the impression should be added in queue to be sent
29
- return (!impression.pt || impression.pt < truncateTimeFrame(now)) ? true : false;
30
+ // Checks if the impression should be added in queue to be sent
31
+ if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
32
+ impressionsToStore.push(impression);
33
+ }
34
+ });
35
+ return {
36
+ impressionsToStore: impressionsToStore,
37
+ impressionsToListener: impressions,
38
+ deduped: impressions.length - impressionsToStore.length
39
+ };
30
40
  }
31
41
  };
32
42
  }
@@ -18,7 +18,7 @@ export interface IImpressionsHandler {
18
18
  }
19
19
 
20
20
  export interface IImpressionsTracker {
21
- track(impressions: [impression: SplitIO.ImpressionDTO, track?: boolean][], attributes?: SplitIO.Attributes): void
21
+ track(impressions: SplitIO.ImpressionDTO[], attributes?: SplitIO.Attributes): void
22
22
  }
23
23
 
24
24
  /** Telemetry tracker */
@@ -70,6 +70,12 @@ export interface IUniqueKeysTracker {
70
70
  track(key: string, featureName: string): void;
71
71
  }
72
72
 
73
+ export interface IStrategyResult {
74
+ impressionsToStore: SplitIO.ImpressionDTO[],
75
+ impressionsToListener: SplitIO.ImpressionDTO[],
76
+ deduped: number
77
+ }
78
+
73
79
  export interface IStrategy {
74
- process(impression: SplitIO.ImpressionDTO): boolean
80
+ process(impressions: SplitIO.ImpressionDTO[]): IStrategyResult
75
81
  }
@@ -120,7 +120,7 @@ export function isBoolean(val: any): 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) :
@@ -159,7 +159,7 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
159
159
  if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
160
160
  withDefaults.core.key = 'localhost_key';
161
161
  } else {
162
- // Keeping same behaviour than JS SDK: if settings key or TT are invalid,
162
+ // Keeping same behavior than JS SDK: if settings key or TT are invalid,
163
163
  // `false` value is used as bound key/TT of the default client, which leads to some issues.
164
164
  // @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
165
165
  withDefaults.core.key = validateKey(log, maybeKey, LOG_PREFIX_CLIENT_INSTANTIATION);
@@ -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;
package/types/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Declaration file for JavaScript Browser Split Software SDK
2
- // Project: http://www.split.io/
2
+ // Project: https://www.split.io/
3
3
  // Definitions by: Nico Zelaya <https://github.com/NicoZelaya/>
4
4
 
5
5
  /// <reference path="./splitio.d.ts" />
@@ -1,5 +1,5 @@
1
1
  // Type definitions for Split Software SDKs
2
- // Project: http://www.split.io/
2
+ // Project: https://www.split.io/
3
3
 
4
4
  import { RedisOptions } from 'ioredis';
5
5
  import { RequestOptions } from 'http';
@@ -862,10 +862,6 @@ declare namespace SplitIO {
862
862
  * The default treatment of the feature flag.
863
863
  */
864
864
  defaultTreatment: string;
865
- /**
866
- * Whether the feature flag has impressions tracking enabled or not.
867
- */
868
- trackImpressions: boolean;
869
865
  };
870
866
  /**
871
867
  * A promise that resolves to a feature flag view or null if the feature flag is not found.
@@ -910,6 +906,18 @@ declare namespace SplitIO {
910
906
  * @defaultValue `'SPLITIO'`
911
907
  */
912
908
  prefix?: string;
909
+ /**
910
+ * Number of days before cached data expires if it was not updated. If cache expires, it is cleared on initialization.
911
+ *
912
+ * @defaultValue `10`
913
+ */
914
+ expirationDays?: number;
915
+ /**
916
+ * Optional settings to clear the cache. If set to `true`, the SDK clears the cached data on initialization, unless the cache was cleared within the last 24 hours.
917
+ *
918
+ * @defaultValue `false`
919
+ */
920
+ clearOnInit?: boolean;
913
921
  }
914
922
  /**
915
923
  * Storage for asynchronous (consumer) SDK.
@@ -1233,11 +1241,23 @@ declare namespace SplitIO {
1233
1241
  */
1234
1242
  type?: BrowserStorage;
1235
1243
  /**
1236
- * Optional prefix to prevent any kind of data collision between SDK versions.
1244
+ * Optional prefix to prevent any kind of data collision between SDK versions when using 'LOCALSTORAGE'.
1237
1245
  *
1238
1246
  * @defaultValue `'SPLITIO'`
1239
1247
  */
1240
1248
  prefix?: string;
1249
+ /**
1250
+ * Optional settings for the 'LOCALSTORAGE' storage type. It specifies the number of days before cached data expires if it was not updated. If cache expires, it is cleared on initialization.
1251
+ *
1252
+ * @defaultValue `10`
1253
+ */
1254
+ expirationDays?: number;
1255
+ /**
1256
+ * Optional settings for the 'LOCALSTORAGE' storage type. If set to `true`, the SDK clears the cached data on initialization, unless the cache was cleared within the last 24 hours.
1257
+ *
1258
+ * @defaultValue `false`
1259
+ */
1260
+ clearOnInit?: boolean;
1241
1261
  };
1242
1262
  }
1243
1263
  /**
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_CACHE_EXPIRATION_IN_MILLIS = void 0;
4
- // This value might be eventually set via a config parameter
5
- exports.DEFAULT_CACHE_EXPIRATION_IN_MILLIS = 864000000; // 10 days
@@ -1,2 +0,0 @@
1
- // This value might be eventually set via a config parameter
2
- export var DEFAULT_CACHE_EXPIRATION_IN_MILLIS = 864000000; // 10 days
@@ -1,2 +0,0 @@
1
- // This value might be eventually set via a config parameter
2
- export const DEFAULT_CACHE_EXPIRATION_IN_MILLIS = 864000000; // 10 days