@splitsoftware/splitio-commons 1.4.2-rc.2 → 1.4.2-rc.5

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 (36) hide show
  1. package/CHANGES.txt +3 -0
  2. package/cjs/integrations/ga/GaToSplit.js +12 -11
  3. package/cjs/sdkFactory/index.js +2 -20
  4. package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
  5. package/cjs/storages/dataLoader.js +15 -23
  6. package/cjs/storages/inMemory/InMemoryStorage.js +1 -15
  7. package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -12
  8. package/cjs/sync/syncManagerOnline.js +2 -2
  9. package/cjs/trackers/impressionObserver/impressionObserverCS.js +1 -1
  10. package/cjs/utils/settingsValidation/index.js +1 -8
  11. package/esm/integrations/ga/GaToSplit.js +12 -11
  12. package/esm/sdkFactory/index.js +3 -21
  13. package/esm/storages/AbstractSplitsCacheSync.js +1 -1
  14. package/esm/storages/dataLoader.js +13 -21
  15. package/esm/storages/inMemory/InMemoryStorage.js +1 -15
  16. package/esm/storages/inMemory/InMemoryStorageCS.js +0 -12
  17. package/esm/sync/syncManagerOnline.js +2 -2
  18. package/esm/trackers/impressionObserver/impressionObserverCS.js +1 -1
  19. package/esm/utils/settingsValidation/index.js +1 -8
  20. package/package.json +1 -1
  21. package/src/integrations/ga/GaToSplit.ts +14 -8
  22. package/src/integrations/ga/autoRequire.js +33 -0
  23. package/src/integrations/ga/types.ts +13 -2
  24. package/src/sdkFactory/index.ts +4 -23
  25. package/src/storages/AbstractSplitsCacheSync.ts +1 -1
  26. package/src/storages/dataLoader.ts +13 -20
  27. package/src/storages/inMemory/InMemoryStorage.ts +1 -16
  28. package/src/storages/inMemory/InMemoryStorageCS.ts +0 -13
  29. package/src/sync/syncManagerOnline.ts +2 -2
  30. package/src/trackers/impressionObserver/impressionObserverCS.ts +1 -1
  31. package/src/types.ts +3 -5
  32. package/src/utils/settingsValidation/index.ts +1 -9
  33. package/types/integrations/ga/types.d.ts +13 -2
  34. package/types/storages/dataLoader.d.ts +2 -2
  35. package/types/trackers/impressionObserver/impressionObserverCS.d.ts +2 -2
  36. package/types/types.d.ts +2 -4
package/CHANGES.txt CHANGED
@@ -1,3 +1,6 @@
1
+ 1.6.0 (June XX, 2022)
2
+ - Added `autoRequire` configuration option to the Google Analytics to Split integration (See https://help.split.io/hc/en-us/articles/360040838752#set-up-with-gtm-and-gtag.js).
3
+
1
4
  1.5.0 (June 24, 2022)
2
5
  - Added a new config option to control the tasks that listen or poll for updates on feature flags and segments, via the new config sync.enabled . Running online Split will always pull the most recent updates upon initialization, this only affects updates fetching on a running instance. Useful when a consistent session experience is a must or to save resources when updates are not being used.
3
6
  - Updated telemetry logic to track the anonymous config for user consent flag set to declined or unknown.
@@ -10,23 +10,24 @@ var logNameMapper = 'ga-to-split:mapper';
10
10
  /**
11
11
  * Provides a plugin to use with analytics.js, accounting for the possibility
12
12
  * that the global command queue has been renamed or not yet defined.
13
- * @param {string} pluginName The plugin name identifier.
14
- * @param {Function} pluginConstructor The plugin constructor function.
13
+ * @param window Reference to global object.
14
+ * @param pluginName The plugin name identifier.
15
+ * @param pluginConstructor The plugin constructor function.
16
+ * @param log Logger instance.
17
+ * @param autoRequire If true, log error when auto-require script is not detected
15
18
  */
16
- function providePlugin(pluginName, pluginConstructor) {
19
+ function providePlugin(window, pluginName, pluginConstructor, log, autoRequire) {
17
20
  // get reference to global command queue. Init it if not defined yet.
18
- // @ts-expect-error
19
21
  var gaAlias = window.GoogleAnalyticsObject || 'ga';
20
22
  window[gaAlias] = window[gaAlias] || function () {
21
- var args = [];
22
- for (var _i = 0; _i < arguments.length; _i++) {
23
- args[_i] = arguments[_i];
24
- }
25
- (window[gaAlias].q = window[gaAlias].q || []).push(args);
23
+ (window[gaAlias].q = window[gaAlias].q || []).push(arguments);
26
24
  };
27
25
  // provides the plugin for use with analytics.js.
28
- // @ts-expect-error
29
26
  window[gaAlias]('provide', pluginName, pluginConstructor);
27
+ if (autoRequire && (!window[gaAlias].q || window[gaAlias].q.push === [].push)) {
28
+ // Expecting spy on ga.q push method but not found
29
+ log.error(logPrefix + 'integration is configured to autorequire the splitTracker plugin, but the necessary script does not seem to have run.');
30
+ }
30
31
  }
31
32
  // Default mapping: object used for building the default mapper from hits to Split events
32
33
  var defaultMapping = {
@@ -246,6 +247,6 @@ function GaToSplit(sdkOptions, params) {
246
247
  return SplitTracker;
247
248
  }());
248
249
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
249
- providePlugin('splitTracker', SplitTracker);
250
+ providePlugin(window, 'splitTracker', SplitTracker, log, sdkOptions.autoRequire);
250
251
  }
251
252
  exports.GaToSplit = GaToSplit;
@@ -25,14 +25,13 @@ function sdkFactory(params) {
25
25
  (0, apiKey_1.validateAndTrackApiKey)(log, settings.core.authorizationKey);
26
26
  var sdkReadinessManager = (0, sdkReadinessManager_1.sdkReadinessManagerFactory)(log, platform.EventEmitter, settings.startup.readyTimeout);
27
27
  var readiness = sdkReadinessManager.readinessManager;
28
- var matchingKey = (0, key_1.getMatching)(settings.core.key);
29
28
  // @TODO consider passing the settings object, so that each storage access only what it needs
30
29
  var storageFactoryParams = {
31
30
  impressionsQueueSize: settings.scheduler.impressionsQueueSize,
32
31
  eventsQueueSize: settings.scheduler.eventsQueueSize,
33
32
  optimize: (0, utils_1.shouldBeOptimized)(settings),
34
33
  // ATM, only used by InLocalStorage
35
- matchingKey: matchingKey,
34
+ matchingKey: (0, key_1.getMatching)(settings.core.key),
36
35
  splitFiltersValidation: settings.sync.__splitFiltersValidation,
37
36
  // ATM, only used by PluggableStorage
38
37
  mode: settings.mode,
@@ -48,21 +47,7 @@ function sdkFactory(params) {
48
47
  log: log
49
48
  };
50
49
  var storage = storageFactory(storageFactoryParams);
51
- // @TODO dataLoader requires validation
52
- if (settings.dataLoader) {
53
- settings.dataLoader(storage, matchingKey);
54
- Promise.resolve(storage.splits.checkCache()).then(function (cacheReady) {
55
- if (cacheReady) {
56
- if (settings.sync.onlySubmitters) { // emit SDK_READY to not timeout when not synchronizing splits & segments
57
- readiness.splits.emit(constants_2.SDK_SPLITS_ARRIVED);
58
- readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED);
59
- }
60
- else { // emit SDK_READY_FROM_CACHE
61
- readiness.splits.emit(constants_2.SDK_SPLITS_CACHE_LOADED);
62
- }
63
- }
64
- });
65
- }
50
+ // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
66
51
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage });
67
52
  // trackers
68
53
  var observer = impressionsObserverFactory && impressionsObserverFactory();
@@ -94,9 +79,6 @@ function sdkFactory(params) {
94
79
  // Logger wrapper API
95
80
  Logger: (0, sdkLogger_1.createLoggerAPI)(settings.log),
96
81
  settings: settings,
97
- // @TODO remove
98
- __storage: storage,
99
- __ctx: ctx
100
82
  }, extraProps && extraProps(ctx));
101
83
  }
102
84
  exports.sdkFactory = sdkFactory;
@@ -33,7 +33,7 @@ var AbstractSplitsCacheSync = /** @class */ (function () {
33
33
  * It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
34
34
  */
35
35
  AbstractSplitsCacheSync.prototype.checkCache = function () {
36
- return this.getChangeNumber() > -1;
36
+ return false;
37
37
  };
38
38
  /**
39
39
  * Kill `name` split and set `defaultTreatment` and `changeNumber`.
@@ -1,27 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DataLoaderFactory = void 0;
3
+ exports.dataLoaderFactory = void 0;
4
4
  var browser_1 = require("../utils/constants/browser");
5
5
  /**
6
- * Factory of storage loader
6
+ * Factory of client-side storage loader
7
7
  *
8
8
  * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
9
9
  * and extended with a `mySegmentsData` property.
10
10
  * @returns function to preload the storage
11
11
  */
12
- function DataLoaderFactory(preloadedData) {
12
+ function dataLoaderFactory(preloadedData) {
13
13
  /**
14
14
  * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
15
15
  * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
16
16
  *
17
17
  * @param storage object containing `splits` and `segments` cache (client-side variant)
18
- * @param userKey user key (matching key) of the provided MySegmentsCache
18
+ * @param userId user key string of the provided MySegmentsCache
19
19
  *
20
+ * @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
20
21
  * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
21
- * @TODO add logs, and input validation in this module, in favor of size reduction.
22
- * @TODO unit tests
23
22
  */
24
- return function loadData(storage, userKey) {
23
+ return function loadData(storage, userId) {
25
24
  // Do not load data if current preloadedData is empty
26
25
  if (Object.keys(preloadedData).length === 0)
27
26
  return;
@@ -37,23 +36,16 @@ function DataLoaderFactory(preloadedData) {
37
36
  storage.splits.setChangeNumber(since);
38
37
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
39
38
  storage.splits.addSplits(Object.keys(splitsData).map(function (splitName) { return [splitName, splitsData[splitName]]; }));
40
- if (userKey) { // add mySegments data (client-side)
41
- var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userKey];
42
- if (!mySegmentsData) {
43
- // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
44
- mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
45
- var userKeys = segmentsData[segmentName];
46
- return userKeys.indexOf(userKey) > -1;
47
- });
48
- }
49
- storage.segments.resetSegments(mySegmentsData);
50
- }
51
- else { // add segments data (server-side)
52
- Object.keys(segmentsData).filter(function (segmentName) {
53
- var userKeys = segmentsData[segmentName];
54
- storage.segments.addToSegment(segmentName, userKeys);
39
+ // add mySegments data
40
+ var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
41
+ if (!mySegmentsData) {
42
+ // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
43
+ mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
44
+ var userIds = JSON.parse(segmentsData[segmentName]).added;
45
+ return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
55
46
  });
56
47
  }
48
+ storage.segments.resetSegments(mySegmentsData);
57
49
  };
58
50
  }
59
- exports.DataLoaderFactory = DataLoaderFactory;
51
+ exports.dataLoaderFactory = dataLoaderFactory;
@@ -8,7 +8,6 @@ var EventsCacheInMemory_1 = require("./EventsCacheInMemory");
8
8
  var ImpressionCountsCacheInMemory_1 = require("./ImpressionCountsCacheInMemory");
9
9
  var constants_1 = require("../../utils/constants");
10
10
  var TelemetryCacheInMemory_1 = require("./TelemetryCacheInMemory");
11
- var sets_1 = require("../../utils/lang/sets");
12
11
  /**
13
12
  * InMemory storage factory for standalone server-side SplitFactory
14
13
  *
@@ -29,20 +28,7 @@ function InMemoryStorageFactory(params) {
29
28
  this.impressions.clear();
30
29
  this.impressionCounts && this.impressionCounts.clear();
31
30
  this.events.clear();
32
- },
33
- // @ts-ignore, private method, for POC
34
- getSnapshot: function () {
35
- var _this = this;
36
- return {
37
- lastUpdated: Date.now(),
38
- since: this.splits.changeNumber,
39
- splitsData: this.splits.splitsCache,
40
- segmentsData: Object.keys(this.segments.segmentCache).reduce(function (prev, cur) {
41
- prev[cur] = (0, sets_1.setToArray)(_this.segments.segmentCache[cur]);
42
- return prev;
43
- }, {})
44
- };
45
- },
31
+ }
46
32
  };
47
33
  }
48
34
  exports.InMemoryStorageFactory = InMemoryStorageFactory;
@@ -29,18 +29,6 @@ function InMemoryStorageCSFactory(params) {
29
29
  this.impressionCounts && this.impressionCounts.clear();
30
30
  this.events.clear();
31
31
  },
32
- // @ts-ignore, private method, for POC
33
- getSnapshot: function () {
34
- var _a;
35
- return {
36
- lastUpdated: Date.now(),
37
- since: this.splits.changeNumber,
38
- splitsData: this.splits.splitsCache,
39
- mySegmentsData: (_a = {},
40
- _a[params.matchingKey] = Object.keys(this.segments.segmentCache),
41
- _a)
42
- };
43
- },
44
32
  // When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
45
33
  shared: function () {
46
34
  return {
@@ -19,9 +19,9 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
19
19
  * SyncManager factory for modular SDK
20
20
  */
21
21
  return function (params) {
22
- var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, _b = _a.sync, syncEnabled = _b.enabled, onlySubmitters = _b.onlySubmitters, telemetryTracker = params.telemetryTracker;
22
+ var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker;
23
23
  /** Polling Manager */
24
- var pollingManager = onlySubmitters ? undefined : pollingManagerFactory && pollingManagerFactory(params);
24
+ var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
25
25
  /** Push Manager */
26
26
  var pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ?
27
27
  pushManagerFactory(params, pollingManager) :
@@ -5,7 +5,7 @@ var ImpressionObserver_1 = require("./ImpressionObserver");
5
5
  var murmur3_1 = require("../../utils/murmur3/murmur3");
6
6
  var buildKey_1 = require("./buildKey");
7
7
  function hashImpression32(impression) {
8
- return (0, murmur3_1.hash)((0, buildKey_1.buildKey)(impression)).toString();
8
+ return (0, murmur3_1.hash)((0, buildKey_1.buildKey)(impression));
9
9
  }
10
10
  exports.hashImpression32 = hashImpression32;
11
11
  var LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
@@ -127,9 +127,6 @@ function settingsValidation(config, validationParams) {
127
127
  // ensure a valid SDK mode
128
128
  // @ts-ignore, modify readonly prop
129
129
  withDefaults.mode = (0, mode_1.mode)(withDefaults.core.authorizationKey, withDefaults.mode);
130
- if (withDefaults.sync.onlySubmitters && withDefaults.mode === constants_1.STANDALONE_MODE && !withDefaults.dataLoader) {
131
- throw new Error('To use `onlySubmitters` param in standalone mode, DataLoader is required to preload data into the storage');
132
- }
133
130
  // ensure a valid Storage based on mode defined.
134
131
  // @ts-ignore, modify readonly prop
135
132
  if (storage)
@@ -172,13 +169,9 @@ function settingsValidation(config, validationParams) {
172
169
  scheduler.pushRetryBackoffBase = fromSecondsToMillis(scheduler.pushRetryBackoffBase);
173
170
  }
174
171
  // validate sync enabled
175
- if (withDefaults.sync.enabled !== false) {
172
+ if (withDefaults.sync.enabled !== false) { // @ts-ignore, modify readonly prop
176
173
  withDefaults.sync.enabled = true;
177
174
  }
178
- // validate sync onlySubmitters
179
- if (withDefaults.sync.onlySubmitters !== true) {
180
- withDefaults.sync.onlySubmitters = false;
181
- }
182
175
  // validate the `splitFilters` settings and parse splits query
183
176
  var splitFiltersValidation = (0, splitFilters_1.validateSplitFilters)(log, withDefaults.sync.splitFilters, withDefaults.mode);
184
177
  withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
@@ -7,23 +7,24 @@ var logNameMapper = 'ga-to-split:mapper';
7
7
  /**
8
8
  * Provides a plugin to use with analytics.js, accounting for the possibility
9
9
  * that the global command queue has been renamed or not yet defined.
10
- * @param {string} pluginName The plugin name identifier.
11
- * @param {Function} pluginConstructor The plugin constructor function.
10
+ * @param window Reference to global object.
11
+ * @param pluginName The plugin name identifier.
12
+ * @param pluginConstructor The plugin constructor function.
13
+ * @param log Logger instance.
14
+ * @param autoRequire If true, log error when auto-require script is not detected
12
15
  */
13
- function providePlugin(pluginName, pluginConstructor) {
16
+ function providePlugin(window, pluginName, pluginConstructor, log, autoRequire) {
14
17
  // get reference to global command queue. Init it if not defined yet.
15
- // @ts-expect-error
16
18
  var gaAlias = window.GoogleAnalyticsObject || 'ga';
17
19
  window[gaAlias] = window[gaAlias] || function () {
18
- var args = [];
19
- for (var _i = 0; _i < arguments.length; _i++) {
20
- args[_i] = arguments[_i];
21
- }
22
- (window[gaAlias].q = window[gaAlias].q || []).push(args);
20
+ (window[gaAlias].q = window[gaAlias].q || []).push(arguments);
23
21
  };
24
22
  // provides the plugin for use with analytics.js.
25
- // @ts-expect-error
26
23
  window[gaAlias]('provide', pluginName, pluginConstructor);
24
+ if (autoRequire && (!window[gaAlias].q || window[gaAlias].q.push === [].push)) {
25
+ // Expecting spy on ga.q push method but not found
26
+ log.error(logPrefix + 'integration is configured to autorequire the splitTracker plugin, but the necessary script does not seem to have run.');
27
+ }
27
28
  }
28
29
  // Default mapping: object used for building the default mapper from hits to Split events
29
30
  var defaultMapping = {
@@ -240,5 +241,5 @@ export function GaToSplit(sdkOptions, params) {
240
241
  return SplitTracker;
241
242
  }());
242
243
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
243
- providePlugin('splitTracker', SplitTracker);
244
+ providePlugin(window, 'splitTracker', SplitTracker, log, sdkOptions.autoRequire);
244
245
  }
@@ -8,7 +8,7 @@ import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
8
8
  import { createLoggerAPI } from '../logger/sdkLogger';
9
9
  import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
10
10
  import { metadataBuilder } from '../storages/metadataBuilder';
11
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
11
+ import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
12
12
  import { objectAssign } from '../utils/lang/objectAssign';
13
13
  /**
14
14
  * Modular SDK factory
@@ -22,14 +22,13 @@ export function sdkFactory(params) {
22
22
  validateAndTrackApiKey(log, settings.core.authorizationKey);
23
23
  var sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout);
24
24
  var readiness = sdkReadinessManager.readinessManager;
25
- var matchingKey = getMatching(settings.core.key);
26
25
  // @TODO consider passing the settings object, so that each storage access only what it needs
27
26
  var storageFactoryParams = {
28
27
  impressionsQueueSize: settings.scheduler.impressionsQueueSize,
29
28
  eventsQueueSize: settings.scheduler.eventsQueueSize,
30
29
  optimize: shouldBeOptimized(settings),
31
30
  // ATM, only used by InLocalStorage
32
- matchingKey: matchingKey,
31
+ matchingKey: getMatching(settings.core.key),
33
32
  splitFiltersValidation: settings.sync.__splitFiltersValidation,
34
33
  // ATM, only used by PluggableStorage
35
34
  mode: settings.mode,
@@ -45,21 +44,7 @@ export function sdkFactory(params) {
45
44
  log: log
46
45
  };
47
46
  var storage = storageFactory(storageFactoryParams);
48
- // @TODO dataLoader requires validation
49
- if (settings.dataLoader) {
50
- settings.dataLoader(storage, matchingKey);
51
- Promise.resolve(storage.splits.checkCache()).then(function (cacheReady) {
52
- if (cacheReady) {
53
- if (settings.sync.onlySubmitters) { // emit SDK_READY to not timeout when not synchronizing splits & segments
54
- readiness.splits.emit(SDK_SPLITS_ARRIVED);
55
- readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
56
- }
57
- else { // emit SDK_READY_FROM_CACHE
58
- readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
59
- }
60
- }
61
- });
62
- }
47
+ // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
63
48
  var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage });
64
49
  // trackers
65
50
  var observer = impressionsObserverFactory && impressionsObserverFactory();
@@ -91,8 +76,5 @@ export function sdkFactory(params) {
91
76
  // Logger wrapper API
92
77
  Logger: createLoggerAPI(settings.log),
93
78
  settings: settings,
94
- // @TODO remove
95
- __storage: storage,
96
- __ctx: ctx
97
79
  }, extraProps && extraProps(ctx));
98
80
  }
@@ -30,7 +30,7 @@ var AbstractSplitsCacheSync = /** @class */ (function () {
30
30
  * It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
31
31
  */
32
32
  AbstractSplitsCacheSync.prototype.checkCache = function () {
33
- return this.getChangeNumber() > -1;
33
+ return false;
34
34
  };
35
35
  /**
36
36
  * Kill `name` split and set `defaultTreatment` and `changeNumber`.
@@ -1,24 +1,23 @@
1
1
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../utils/constants/browser';
2
2
  /**
3
- * Factory of storage loader
3
+ * Factory of client-side storage loader
4
4
  *
5
5
  * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
6
6
  * and extended with a `mySegmentsData` property.
7
7
  * @returns function to preload the storage
8
8
  */
9
- export function DataLoaderFactory(preloadedData) {
9
+ export function dataLoaderFactory(preloadedData) {
10
10
  /**
11
11
  * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
12
12
  * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
13
13
  *
14
14
  * @param storage object containing `splits` and `segments` cache (client-side variant)
15
- * @param userKey user key (matching key) of the provided MySegmentsCache
15
+ * @param userId user key string of the provided MySegmentsCache
16
16
  *
17
+ * @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
17
18
  * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
18
- * @TODO add logs, and input validation in this module, in favor of size reduction.
19
- * @TODO unit tests
20
19
  */
21
- return function loadData(storage, userKey) {
20
+ return function loadData(storage, userId) {
22
21
  // Do not load data if current preloadedData is empty
23
22
  if (Object.keys(preloadedData).length === 0)
24
23
  return;
@@ -34,22 +33,15 @@ export function DataLoaderFactory(preloadedData) {
34
33
  storage.splits.setChangeNumber(since);
35
34
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
36
35
  storage.splits.addSplits(Object.keys(splitsData).map(function (splitName) { return [splitName, splitsData[splitName]]; }));
37
- if (userKey) { // add mySegments data (client-side)
38
- var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userKey];
39
- if (!mySegmentsData) {
40
- // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
41
- mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
42
- var userKeys = segmentsData[segmentName];
43
- return userKeys.indexOf(userKey) > -1;
44
- });
45
- }
46
- storage.segments.resetSegments(mySegmentsData);
47
- }
48
- else { // add segments data (server-side)
49
- Object.keys(segmentsData).filter(function (segmentName) {
50
- var userKeys = segmentsData[segmentName];
51
- storage.segments.addToSegment(segmentName, userKeys);
36
+ // add mySegments data
37
+ var mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
38
+ if (!mySegmentsData) {
39
+ // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
40
+ mySegmentsData = Object.keys(segmentsData).filter(function (segmentName) {
41
+ var userIds = JSON.parse(segmentsData[segmentName]).added;
42
+ return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
52
43
  });
53
44
  }
45
+ storage.segments.resetSegments(mySegmentsData);
54
46
  };
55
47
  }
@@ -5,7 +5,6 @@ import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
6
6
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
7
7
  import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
8
- import { setToArray } from '../../utils/lang/sets';
9
8
  /**
10
9
  * InMemory storage factory for standalone server-side SplitFactory
11
10
  *
@@ -26,20 +25,7 @@ export function InMemoryStorageFactory(params) {
26
25
  this.impressions.clear();
27
26
  this.impressionCounts && this.impressionCounts.clear();
28
27
  this.events.clear();
29
- },
30
- // @ts-ignore, private method, for POC
31
- getSnapshot: function () {
32
- var _this = this;
33
- return {
34
- lastUpdated: Date.now(),
35
- since: this.splits.changeNumber,
36
- splitsData: this.splits.splitsCache,
37
- segmentsData: Object.keys(this.segments.segmentCache).reduce(function (prev, cur) {
38
- prev[cur] = setToArray(_this.segments.segmentCache[cur]);
39
- return prev;
40
- }, {})
41
- };
42
- },
28
+ }
43
29
  };
44
30
  }
45
31
  InMemoryStorageFactory.type = STORAGE_MEMORY;
@@ -26,18 +26,6 @@ export function InMemoryStorageCSFactory(params) {
26
26
  this.impressionCounts && this.impressionCounts.clear();
27
27
  this.events.clear();
28
28
  },
29
- // @ts-ignore, private method, for POC
30
- getSnapshot: function () {
31
- var _a;
32
- return {
33
- lastUpdated: Date.now(),
34
- since: this.splits.changeNumber,
35
- splitsData: this.splits.splitsCache,
36
- mySegmentsData: (_a = {},
37
- _a[params.matchingKey] = Object.keys(this.segments.segmentCache),
38
- _a)
39
- };
40
- },
41
29
  // When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
42
30
  shared: function () {
43
31
  return {
@@ -16,9 +16,9 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
16
16
  * SyncManager factory for modular SDK
17
17
  */
18
18
  return function (params) {
19
- var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, _b = _a.sync, syncEnabled = _b.enabled, onlySubmitters = _b.onlySubmitters, telemetryTracker = params.telemetryTracker;
19
+ var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker;
20
20
  /** Polling Manager */
21
- var pollingManager = onlySubmitters ? undefined : pollingManagerFactory && pollingManagerFactory(params);
21
+ var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
22
22
  /** Push Manager */
23
23
  var pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ?
24
24
  pushManagerFactory(params, pollingManager) :
@@ -2,7 +2,7 @@ import { ImpressionObserver } from './ImpressionObserver';
2
2
  import { hash } from '../../utils/murmur3/murmur3';
3
3
  import { buildKey } from './buildKey';
4
4
  export function hashImpression32(impression) {
5
- return hash(buildKey(impression)).toString();
5
+ return hash(buildKey(impression));
6
6
  }
7
7
  var LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
8
8
  export function impressionObserverCSFactory() {
@@ -124,9 +124,6 @@ export function settingsValidation(config, validationParams) {
124
124
  // ensure a valid SDK mode
125
125
  // @ts-ignore, modify readonly prop
126
126
  withDefaults.mode = mode(withDefaults.core.authorizationKey, withDefaults.mode);
127
- if (withDefaults.sync.onlySubmitters && withDefaults.mode === STANDALONE_MODE && !withDefaults.dataLoader) {
128
- throw new Error('To use `onlySubmitters` param in standalone mode, DataLoader is required to preload data into the storage');
129
- }
130
127
  // ensure a valid Storage based on mode defined.
131
128
  // @ts-ignore, modify readonly prop
132
129
  if (storage)
@@ -169,13 +166,9 @@ export function settingsValidation(config, validationParams) {
169
166
  scheduler.pushRetryBackoffBase = fromSecondsToMillis(scheduler.pushRetryBackoffBase);
170
167
  }
171
168
  // validate sync enabled
172
- if (withDefaults.sync.enabled !== false) {
169
+ if (withDefaults.sync.enabled !== false) { // @ts-ignore, modify readonly prop
173
170
  withDefaults.sync.enabled = true;
174
171
  }
175
- // validate sync onlySubmitters
176
- if (withDefaults.sync.onlySubmitters !== true) {
177
- withDefaults.sync.onlySubmitters = false;
178
- }
179
172
  // validate the `splitFilters` settings and parse splits query
180
173
  var splitFiltersValidation = validateSplitFilters(log, withDefaults.sync.splitFilters, withDefaults.mode);
181
174
  withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.4.2-rc.2",
3
+ "version": "1.4.2-rc.5",
4
4
  "description": "Split Javascript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -19,20 +19,26 @@ const logNameMapper = 'ga-to-split:mapper';
19
19
  /**
20
20
  * Provides a plugin to use with analytics.js, accounting for the possibility
21
21
  * that the global command queue has been renamed or not yet defined.
22
- * @param {string} pluginName The plugin name identifier.
23
- * @param {Function} pluginConstructor The plugin constructor function.
22
+ * @param window Reference to global object.
23
+ * @param pluginName The plugin name identifier.
24
+ * @param pluginConstructor The plugin constructor function.
25
+ * @param log Logger instance.
26
+ * @param autoRequire If true, log error when auto-require script is not detected
24
27
  */
25
- function providePlugin(pluginName: string, pluginConstructor: Function) {
28
+ function providePlugin(window: any, pluginName: string, pluginConstructor: Function, log: ILogger, autoRequire?: boolean) {
26
29
  // get reference to global command queue. Init it if not defined yet.
27
- // @ts-expect-error
28
30
  const gaAlias = window.GoogleAnalyticsObject || 'ga';
29
- window[gaAlias] = window[gaAlias] || function (...args: any[]) { // @ts-expect-error
30
- (window[gaAlias].q = window[gaAlias].q || []).push(args);
31
+ window[gaAlias] = window[gaAlias] || function () {
32
+ (window[gaAlias].q = window[gaAlias].q || []).push(arguments);
31
33
  };
32
34
 
33
35
  // provides the plugin for use with analytics.js.
34
- // @ts-expect-error
35
36
  window[gaAlias]('provide', pluginName, pluginConstructor);
37
+
38
+ if (autoRequire && (!window[gaAlias].q || window[gaAlias].q.push === [].push)) {
39
+ // Expecting spy on ga.q push method but not found
40
+ log.error(logPrefix + 'integration is configured to autorequire the splitTracker plugin, but the necessary script does not seem to have run.');
41
+ }
36
42
  }
37
43
 
38
44
  // Default mapping: object used for building the default mapper from hits to Split events
@@ -284,5 +290,5 @@ export function GaToSplit(sdkOptions: GoogleAnalyticsToSplitOptions, params: IIn
284
290
  }
285
291
 
286
292
  // Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
287
- providePlugin('splitTracker', SplitTracker);
293
+ providePlugin(window, 'splitTracker', SplitTracker, log, sdkOptions.autoRequire);
288
294
  }
@@ -0,0 +1,33 @@
1
+ /* eslint-disable no-undef */
2
+ /**
3
+ * Auto-require script to use with GoogleAnalyticsToSplit integration
4
+ */
5
+ (function (i, r, s) {
6
+ i[s] = i[s] || r;
7
+ i[r] = i[r] || function () { i[r].q.push(arguments); };
8
+ i[r].q = i[r].q || [];
9
+
10
+ var ts = {}; // Tracker names
11
+ function n(arg) { return typeof arg === 'object' && typeof arg.name === 'string' && arg.name; }
12
+
13
+ function p(v) { // Queue a `require` command if v is a `create` command
14
+ if (v && v[0] === 'create') {
15
+ var t = n(v[1]) || n(v[2]) || n(v[3]) || (typeof v[3] === 'string' ? v[3] : undefined); // Get tracker name
16
+
17
+ if (!ts[t]) {
18
+ ts[t] = true;
19
+ i[r]((t ? t + '.' : '') + 'require', 'splitTracker'); // Auto-require
20
+ }
21
+ }
22
+ }
23
+
24
+ i[r].q.forEach(function (v) { p(v); }); // Process already queued commands
25
+
26
+ var o = i[r].q.push;
27
+ i[r].q.push = function (v) { // Spy new queued commands
28
+ var result = o.apply(this, arguments);
29
+ p(v);
30
+ return result;
31
+ };
32
+
33
+ })(window, 'ga', 'GoogleAnalyticsObject');
@@ -53,13 +53,24 @@ export interface GoogleAnalyticsToSplitOptions {
53
53
  * If not provided, events are sent using the key and traffic type provided at SDK config
54
54
  */
55
55
  identities?: Identity[],
56
+ /**
57
+ * Optional flag to log an error if the `auto-require` script is not detected.
58
+ * The auto-require script automatically requires the `splitTracker` plugin for created trackers,
59
+ * and should be placed right after your Google Analytics, GTM or gtag.js script tag.
60
+ *
61
+ * @see {@link https://help.split.io/hc/en-us/articles/360040838752#google-tag-manager}
62
+ *
63
+ * @property {boolean} autoRequire
64
+ * @default false
65
+ */
66
+ autoRequire?: boolean,
56
67
  }
57
68
 
58
69
  /**
59
70
  * Enable 'Google Analytics to Split' integration, to track Google Analytics hits as Split events.
60
71
  * Used by the browser variant of the isomorphic JS SDK.
61
72
  *
62
- * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#google-analytics-to-split}
73
+ * @see {@link https://help.split.io/hc/en-us/articles/360040838752#google-analytics-to-split}
63
74
  */
64
75
  export interface IGoogleAnalyticsToSplitConfig extends GoogleAnalyticsToSplitOptions {
65
76
  type: 'GOOGLE_ANALYTICS_TO_SPLIT'
@@ -129,7 +140,7 @@ export interface SplitToGoogleAnalyticsOptions {
129
140
  * Enable 'Split to Google Analytics' integration, to track Split impressions and events as Google Analytics hits.
130
141
  * Used by the browser variant of the isomorphic JS SDK.
131
142
  *
132
- * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#split-to-google-analytics}
143
+ * @see {@link https://help.split.io/hc/en-us/articles/360040838752#split-to-google-analytics}
133
144
  */
134
145
  export interface ISplitToGoogleAnalyticsConfig extends SplitToGoogleAnalyticsOptions {
135
146
  type: 'SPLIT_TO_GOOGLE_ANALYTICS'
@@ -3,7 +3,7 @@ import { sdkReadinessManagerFactory } from '../readiness/sdkReadinessManager';
3
3
  import { impressionsTrackerFactory } from '../trackers/impressionsTracker';
4
4
  import { eventTrackerFactory } from '../trackers/eventTracker';
5
5
  import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
6
- import { IStorageFactoryParams, IStorageSync } from '../storages/types';
6
+ import { IStorageFactoryParams } from '../storages/types';
7
7
  import { SplitIO } from '../types';
8
8
  import { getMatching } from '../utils/key';
9
9
  import { shouldBeOptimized } from '../trackers/impressionObserver/utils';
@@ -11,7 +11,7 @@ import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
11
11
  import { createLoggerAPI } from '../logger/sdkLogger';
12
12
  import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
13
13
  import { metadataBuilder } from '../storages/metadataBuilder';
14
- import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
14
+ import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
15
15
  import { objectAssign } from '../utils/lang/objectAssign';
16
16
 
17
17
  /**
@@ -32,7 +32,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
32
32
 
33
33
  const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout);
34
34
  const readiness = sdkReadinessManager.readinessManager;
35
- const matchingKey = getMatching(settings.core.key);
36
35
 
37
36
  // @TODO consider passing the settings object, so that each storage access only what it needs
38
37
  const storageFactoryParams: IStorageFactoryParams = {
@@ -41,7 +40,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
41
40
  optimize: shouldBeOptimized(settings),
42
41
 
43
42
  // ATM, only used by InLocalStorage
44
- matchingKey,
43
+ matchingKey: getMatching(settings.core.key),
45
44
  splitFiltersValidation: settings.sync.__splitFiltersValidation,
46
45
 
47
46
  // ATM, only used by PluggableStorage
@@ -59,21 +58,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
59
58
  };
60
59
 
61
60
  const storage = storageFactory(storageFactoryParams);
62
-
63
- // @TODO dataLoader requires validation
64
- if (settings.dataLoader) {
65
- settings.dataLoader(storage as IStorageSync, matchingKey);
66
- Promise.resolve(storage.splits.checkCache()).then(cacheReady => {
67
- if (cacheReady) {
68
- if (settings.sync.onlySubmitters) { // emit SDK_READY to not timeout when not synchronizing splits & segments
69
- readiness.splits.emit(SDK_SPLITS_ARRIVED);
70
- readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
71
- } else { // emit SDK_READY_FROM_CACHE
72
- readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
73
- }
74
- }
75
- });
76
- }
61
+ // @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
77
62
 
78
63
  const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage });
79
64
 
@@ -118,9 +103,5 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
118
103
  Logger: createLoggerAPI(settings.log),
119
104
 
120
105
  settings,
121
-
122
- // @TODO remove
123
- __storage: storage,
124
- __ctx: ctx
125
106
  }, extraProps && extraProps(ctx));
126
107
  }
@@ -50,7 +50,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
50
50
  * It is used as condition to emit SDK_SPLITS_CACHE_LOADED, and then SDK_READY_FROM_CACHE.
51
51
  */
52
52
  checkCache(): boolean {
53
- return this.getChangeNumber() > -1;
53
+ return false;
54
54
  }
55
55
 
56
56
  /**
@@ -3,26 +3,25 @@ import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../utils/constants/browser';
3
3
  import { DataLoader, ISegmentsCacheSync, ISplitsCacheSync } from './types';
4
4
 
5
5
  /**
6
- * Factory of storage loader
6
+ * Factory of client-side storage loader
7
7
  *
8
8
  * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
9
9
  * and extended with a `mySegmentsData` property.
10
10
  * @returns function to preload the storage
11
11
  */
12
- export function DataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader {
12
+ export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader {
13
13
 
14
14
  /**
15
15
  * Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
16
16
  * (https://github.com/godaddy/split-javascript-data-loader/blob/master/src/load-data.js)
17
17
  *
18
18
  * @param storage object containing `splits` and `segments` cache (client-side variant)
19
- * @param userKey user key (matching key) of the provided MySegmentsCache
19
+ * @param userId user key string of the provided MySegmentsCache
20
20
  *
21
+ * @TODO extend to support SegmentsCache (server-side variant) by making `userId` optional and adding the corresponding logic.
21
22
  * @TODO extend to load data on shared mySegments storages. Be specific when emitting SDK_READY_FROM_CACHE on shared clients. Maybe the serializer should provide the `useSegments` flag.
22
- * @TODO add logs, and input validation in this module, in favor of size reduction.
23
- * @TODO unit tests
24
23
  */
25
- return function loadData(storage: { splits: ISplitsCacheSync, segments: ISegmentsCacheSync }, userKey?: string) {
24
+ return function loadData(storage: { splits: ISplitsCacheSync, segments: ISegmentsCacheSync }, userId: string) {
26
25
  // Do not load data if current preloadedData is empty
27
26
  if (Object.keys(preloadedData).length === 0) return;
28
27
 
@@ -42,21 +41,15 @@ export function DataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoa
42
41
  // splitsData in an object where the property is the split name and the pertaining value is a stringified json of its data
43
42
  storage.splits.addSplits(Object.keys(splitsData).map(splitName => [splitName, splitsData[splitName]]));
44
43
 
45
- if (userKey) { // add mySegments data (client-side)
46
- let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userKey];
47
- if (!mySegmentsData) {
48
- // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
49
- mySegmentsData = Object.keys(segmentsData).filter(segmentName => {
50
- const userKeys = segmentsData[segmentName];
51
- return userKeys.indexOf(userKey) > -1;
52
- });
53
- }
54
- storage.segments.resetSegments(mySegmentsData);
55
- } else { // add segments data (server-side)
56
- Object.keys(segmentsData).filter(segmentName => {
57
- const userKeys = segmentsData[segmentName];
58
- storage.segments.addToSegment(segmentName, userKeys);
44
+ // add mySegments data
45
+ let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[userId];
46
+ if (!mySegmentsData) {
47
+ // segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
48
+ mySegmentsData = Object.keys(segmentsData).filter(segmentName => {
49
+ const userIds = JSON.parse(segmentsData[segmentName]).added;
50
+ return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
59
51
  });
60
52
  }
53
+ storage.segments.resetSegments(mySegmentsData);
61
54
  };
62
55
  }
@@ -6,8 +6,6 @@ import { IStorageFactoryParams, IStorageSync } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
- import { SplitIO } from '../../types';
10
- import { setToArray, ISet } from '../../utils/lang/sets';
11
9
 
12
10
  /**
13
11
  * InMemory storage factory for standalone server-side SplitFactory
@@ -31,20 +29,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
31
29
  this.impressions.clear();
32
30
  this.impressionCounts && this.impressionCounts.clear();
33
31
  this.events.clear();
34
- },
35
-
36
- // @ts-ignore, private method, for POC
37
- getSnapshot(): SplitIO.PreloadedData {
38
- return {
39
- lastUpdated: Date.now(), // @ts-ignore accessing private prop
40
- since: this.splits.changeNumber, // @ts-ignore accessing private prop
41
- splitsData: this.splits.splitsCache, // @ts-ignore accessing private prop
42
- segmentsData: Object.keys(this.segments.segmentCache).reduce((prev, cur) => { // @ts-ignore accessing private prop
43
- prev[cur] = setToArray(this.segments.segmentCache[cur] as ISet<string>);
44
- return prev;
45
- }, {})
46
- };
47
- },
32
+ }
48
33
  };
49
34
  }
50
35
 
@@ -6,7 +6,6 @@ import { IStorageSync, IStorageFactoryParams } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
7
  import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
- import { SplitIO } from '../../types';
10
9
 
11
10
  /**
12
11
  * InMemory storage factory for standalone client-side SplitFactory
@@ -32,18 +31,6 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
32
31
  this.events.clear();
33
32
  },
34
33
 
35
- // @ts-ignore, private method, for POC
36
- getSnapshot(): SplitIO.PreloadedData {
37
- return {
38
- lastUpdated: Date.now(), // @ts-ignore accessing private prop
39
- since: this.splits.changeNumber, // @ts-ignore accessing private prop
40
- splitsData: this.splits.splitsCache,
41
- mySegmentsData: { // @ts-ignore accessing private prop
42
- [params.matchingKey as string]: Object.keys(this.segments.segmentCache)
43
- }
44
- };
45
- },
46
-
47
34
  // When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
48
35
  shared() {
49
36
  return {
@@ -28,10 +28,10 @@ export function syncManagerOnlineFactory(
28
28
  */
29
29
  return function (params: ISdkFactoryContextSync): ISyncManagerCS {
30
30
 
31
- const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled, onlySubmitters } }, telemetryTracker } = params;
31
+ const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } }, telemetryTracker } = params;
32
32
 
33
33
  /** Polling Manager */
34
- const pollingManager = onlySubmitters ? undefined : pollingManagerFactory && pollingManagerFactory(params);
34
+ const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
35
35
 
36
36
  /** Push Manager */
37
37
  const pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ?
@@ -4,7 +4,7 @@ import { buildKey } from './buildKey';
4
4
  import { ImpressionDTO } from '../../types';
5
5
 
6
6
  export function hashImpression32(impression: ImpressionDTO) {
7
- return hash(buildKey(impression)).toString();
7
+ return hash(buildKey(impression));
8
8
  }
9
9
 
10
10
  const LAST_SEEN_CACHE_SIZE = 500; // cache up to 500 impression hashes
package/src/types.ts CHANGED
@@ -4,7 +4,7 @@ import { ILogger } from './logger/types';
4
4
  import { ISdkFactoryContext } from './sdkFactory/types';
5
5
  /* eslint-disable no-use-before-define */
6
6
 
7
- import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory, DataLoader } from './storages/types';
7
+ import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory } from './storages/types';
8
8
  import { ISyncManagerCS } from './sync/types';
9
9
 
10
10
  /**
@@ -98,7 +98,6 @@ export interface ISettings {
98
98
  eventsFirstPushWindow: number
99
99
  },
100
100
  readonly storage: IStorageSyncFactory | IStorageAsyncFactory,
101
- readonly dataLoader?: DataLoader,
102
101
  readonly integrations: Array<{
103
102
  readonly type: string,
104
103
  (params: IIntegrationFactoryParams): IIntegration | void
@@ -119,8 +118,7 @@ export interface ISettings {
119
118
  impressionsMode: SplitIO.ImpressionsMode,
120
119
  __splitFiltersValidation: ISplitFiltersValidation,
121
120
  localhostMode?: SplitIO.LocalhostFactory,
122
- enabled: boolean,
123
- onlySubmitters: boolean
121
+ enabled: boolean
124
122
  },
125
123
  readonly runtime: {
126
124
  ip: string | false
@@ -754,7 +752,7 @@ export namespace SplitIO {
754
752
  * This property is ignored if `mySegmentsData` was provided.
755
753
  */
756
754
  segmentsData?: {
757
- [segmentName: string]: string[]
755
+ [segmentName: string]: string
758
756
  },
759
757
  }
760
758
  /**
@@ -147,9 +147,6 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
147
147
  // ensure a valid SDK mode
148
148
  // @ts-ignore, modify readonly prop
149
149
  withDefaults.mode = mode(withDefaults.core.authorizationKey, withDefaults.mode);
150
- if (withDefaults.sync.onlySubmitters && withDefaults.mode === STANDALONE_MODE && !withDefaults.dataLoader) {
151
- throw new Error('To use `onlySubmitters` param in standalone mode, DataLoader is required to preload data into the storage');
152
- }
153
150
 
154
151
  // ensure a valid Storage based on mode defined.
155
152
  // @ts-ignore, modify readonly prop
@@ -196,15 +193,10 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
196
193
  }
197
194
 
198
195
  // validate sync enabled
199
- if (withDefaults.sync.enabled !== false) {
196
+ if (withDefaults.sync.enabled !== false) { // @ts-ignore, modify readonly prop
200
197
  withDefaults.sync.enabled = true;
201
198
  }
202
199
 
203
- // validate sync onlySubmitters
204
- if (withDefaults.sync.onlySubmitters !== true) {
205
- withDefaults.sync.onlySubmitters = false;
206
- }
207
-
208
200
  // validate the `splitFilters` settings and parse splits query
209
201
  const splitFiltersValidation = validateSplitFilters(log, withDefaults.sync.splitFilters, withDefaults.mode);
210
202
  withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
@@ -52,12 +52,23 @@ export interface GoogleAnalyticsToSplitOptions {
52
52
  * If not provided, events are sent using the key and traffic type provided at SDK config
53
53
  */
54
54
  identities?: Identity[];
55
+ /**
56
+ * Optional flag to log an error if the `auto-require` script is not detected.
57
+ * The auto-require script automatically requires the `splitTracker` plugin for created trackers,
58
+ * and should be placed right after your Google Analytics, GTM or gtag.js script tag.
59
+ *
60
+ * @see {@link https://help.split.io/hc/en-us/articles/360040838752#google-tag-manager}
61
+ *
62
+ * @property {boolean} autoRequire
63
+ * @default false
64
+ */
65
+ autoRequire?: boolean;
55
66
  }
56
67
  /**
57
68
  * Enable 'Google Analytics to Split' integration, to track Google Analytics hits as Split events.
58
69
  * Used by the browser variant of the isomorphic JS SDK.
59
70
  *
60
- * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#google-analytics-to-split}
71
+ * @see {@link https://help.split.io/hc/en-us/articles/360040838752#google-analytics-to-split}
61
72
  */
62
73
  export interface IGoogleAnalyticsToSplitConfig extends GoogleAnalyticsToSplitOptions {
63
74
  type: 'GOOGLE_ANALYTICS_TO_SPLIT';
@@ -125,7 +136,7 @@ export interface SplitToGoogleAnalyticsOptions {
125
136
  * Enable 'Split to Google Analytics' integration, to track Split impressions and events as Google Analytics hits.
126
137
  * Used by the browser variant of the isomorphic JS SDK.
127
138
  *
128
- * @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#split-to-google-analytics}
139
+ * @see {@link https://help.split.io/hc/en-us/articles/360040838752#split-to-google-analytics}
129
140
  */
130
141
  export interface ISplitToGoogleAnalyticsConfig extends SplitToGoogleAnalyticsOptions {
131
142
  type: 'SPLIT_TO_GOOGLE_ANALYTICS';
@@ -1,10 +1,10 @@
1
1
  import { SplitIO } from '../types';
2
2
  import { DataLoader } from './types';
3
3
  /**
4
- * Factory of storage loader
4
+ * Factory of client-side storage loader
5
5
  *
6
6
  * @param preloadedData validated data following the format proposed in https://github.com/godaddy/split-javascript-data-loader
7
7
  * and extended with a `mySegmentsData` property.
8
8
  * @returns function to preload the storage
9
9
  */
10
- export declare function DataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader;
10
+ export declare function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoader;
@@ -1,4 +1,4 @@
1
1
  import { ImpressionObserver } from './ImpressionObserver';
2
2
  import { ImpressionDTO } from '../../types';
3
- export declare function hashImpression32(impression: ImpressionDTO): string;
4
- export declare function impressionObserverCSFactory(): ImpressionObserver<string>;
3
+ export declare function hashImpression32(impression: ImpressionDTO): number;
4
+ export declare function impressionObserverCSFactory(): ImpressionObserver<number>;
package/types/types.d.ts CHANGED
@@ -2,7 +2,7 @@ import { ISplitFiltersValidation } from './dtos/types';
2
2
  import { IIntegration, IIntegrationFactoryParams } from './integrations/types';
3
3
  import { ILogger } from './logger/types';
4
4
  import { ISdkFactoryContext } from './sdkFactory/types';
5
- import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory, DataLoader } from './storages/types';
5
+ import { IStorageFactoryParams, IStorageSync, IStorageAsync, IStorageSyncFactory, IStorageAsyncFactory } from './storages/types';
6
6
  import { ISyncManagerCS } from './sync/types';
7
7
  /**
8
8
  * Reduced version of NodeJS.EventEmitter interface with the minimal methods used by the SDK
@@ -92,7 +92,6 @@ export interface ISettings {
92
92
  eventsFirstPushWindow: number;
93
93
  };
94
94
  readonly storage: IStorageSyncFactory | IStorageAsyncFactory;
95
- readonly dataLoader?: DataLoader;
96
95
  readonly integrations: Array<{
97
96
  readonly type: string;
98
97
  (params: IIntegrationFactoryParams): IIntegration | void;
@@ -114,7 +113,6 @@ export interface ISettings {
114
113
  __splitFiltersValidation: ISplitFiltersValidation;
115
114
  localhostMode?: SplitIO.LocalhostFactory;
116
115
  enabled: boolean;
117
- onlySubmitters: boolean;
118
116
  };
119
117
  readonly runtime: {
120
118
  ip: string | false;
@@ -751,7 +749,7 @@ export declare namespace SplitIO {
751
749
  * This property is ignored if `mySegmentsData` was provided.
752
750
  */
753
751
  segmentsData?: {
754
- [segmentName: string]: string[];
752
+ [segmentName: string]: string;
755
753
  };
756
754
  }
757
755
  /**