@splitsoftware/splitio-commons 2.0.1 → 2.0.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.
package/CHANGES.txt CHANGED
@@ -1,3 +1,7 @@
1
+ 2.0.2 (December 3, 2024)
2
+ - Updated the factory `init` and `destroy` methods to support re-initialization after destruction. This update ensures compatibility of the React SDK with React Strict Mode, where the factory's `init` and `destroy` effects are executed an extra time to validate proper resource cleanup.
3
+ - Bugfixing - Sanitize the `SplitSDKMachineName` header value to avoid exceptions on HTTP/S requests when it contains non ISO-8859-1 characters (Related to issue https://github.com/splitio/javascript-client/issues/847).
4
+
1
5
  2.0.1 (November 25, 2024)
2
6
  - Bugfixing - Fixed an issue with the SDK_UPDATE event on server-side, where it was not being emitted if there was an empty segment and the SDK received a feature flag update notification.
3
7
 
@@ -28,7 +28,7 @@ function segmentsEventEmitterFactory(EventEmitter) {
28
28
  /**
29
29
  * Factory of readiness manager, which handles the ready / update event propagation.
30
30
  */
31
- function readinessManagerFactory(EventEmitter, settings, splits) {
31
+ function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
32
32
  if (splits === void 0) { splits = splitsEventEmitterFactory(EventEmitter); }
33
33
  var readyTimeout = settings.startup.readyTimeout;
34
34
  var segments = segmentsEventEmitterFactory(EventEmitter);
@@ -54,22 +54,24 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
54
54
  syncLastUpdate();
55
55
  gate.emit(constants_1.SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
56
56
  }
57
- var readyTimeoutId;
58
- if (readyTimeout > 0) {
59
- if (splits.hasInit)
60
- readyTimeoutId = setTimeout(timeout, readyTimeout);
61
- else
62
- splits.initCallbacks.push(function () { readyTimeoutId = setTimeout(timeout, readyTimeout); });
63
- }
64
57
  // emit SDK_READY and SDK_UPDATE
65
58
  var isReady = false;
66
59
  splits.on(constants_1.SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate);
67
60
  segments.on(constants_1.SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
68
61
  var isDestroyed = false;
62
+ var readyTimeoutId;
63
+ function __init() {
64
+ isDestroyed = false;
65
+ if (readyTimeout > 0 && !isReady)
66
+ readyTimeoutId = setTimeout(timeout, readyTimeout);
67
+ }
68
+ splits.initCallbacks.push(__init);
69
+ if (splits.hasInit)
70
+ __init();
69
71
  function checkIsReadyFromCache() {
70
72
  isReadyFromCache = true;
71
73
  // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
72
- if (!isReady) {
74
+ if (!isReady && !isDestroyed) {
73
75
  try {
74
76
  syncLastUpdate();
75
77
  gate.emit(constants_1.SDK_READY_FROM_CACHE);
@@ -81,6 +83,8 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
81
83
  }
82
84
  }
83
85
  function checkIsReadyOrUpdate(diff) {
86
+ if (isDestroyed)
87
+ return;
84
88
  if (isReady) {
85
89
  try {
86
90
  syncLastUpdate();
@@ -106,14 +110,12 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
106
110
  }
107
111
  }
108
112
  }
109
- var refCount = 1;
110
113
  return {
111
114
  splits: splits,
112
115
  segments: segments,
113
116
  gate: gate,
114
117
  shared: function () {
115
- refCount++;
116
- return readinessManagerFactory(EventEmitter, settings, splits);
118
+ return readinessManagerFactory(EventEmitter, settings, splits, true);
117
119
  },
118
120
  // @TODO review/remove next methods when non-recoverable errors are reworked
119
121
  // Called on consumer mode, when storage fails to connect
@@ -130,13 +132,9 @@ function readinessManagerFactory(EventEmitter, settings, splits) {
130
132
  destroy: function () {
131
133
  isDestroyed = true;
132
134
  syncLastUpdate();
133
- segments.removeAllListeners();
134
- gate.removeAllListeners();
135
135
  clearTimeout(readyTimeoutId);
136
- if (refCount > 0)
137
- refCount--;
138
- if (refCount === 0)
139
- splits.removeAllListeners();
136
+ if (!isShared)
137
+ splits.hasInit = false;
140
138
  },
141
139
  isReady: function () { return isReady; },
142
140
  isReadyFromCache: function () { return isReadyFromCache; },
@@ -102,6 +102,7 @@ function sdkFactory(params) {
102
102
  Logger: (0, sdkLogger_1.createLoggerAPI)(log),
103
103
  settings: settings,
104
104
  destroy: function () {
105
+ hasInit = false;
105
106
  return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
106
107
  }
107
108
  }, extraProps && extraProps(ctx), lazyInit ? { init: init } : init());
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.decorateHeaders = void 0;
3
+ exports.removeNonISO88591 = exports.decorateHeaders = void 0;
4
4
  var objectAssign_1 = require("../utils/lang/objectAssign");
5
5
  var FORBIDDEN_HEADERS = new Set([
6
6
  'splitsdkclientkey',
@@ -33,3 +33,8 @@ function decorateHeaders(settings, headers) {
33
33
  return headers;
34
34
  }
35
35
  exports.decorateHeaders = decorateHeaders;
36
+ function removeNonISO88591(input) {
37
+ // eslint-disable-next-line no-control-regex
38
+ return input.replace(/[^\x00-\xFF]/g, '');
39
+ }
40
+ exports.removeNonISO88591 = removeNonISO88591;
@@ -28,7 +28,7 @@ function splitHttpClientFactory(settings, _a) {
28
28
  if (ip)
29
29
  commonHeaders['SplitSDKMachineIP'] = ip;
30
30
  if (hostname)
31
- commonHeaders['SplitSDKMachineName'] = hostname;
31
+ commonHeaders['SplitSDKMachineName'] = (0, decorateHeaders_1.removeNonISO88591)(hostname);
32
32
  return function httpClient(url, reqOpts, latencyTracker, logErrorsAsInfo) {
33
33
  if (reqOpts === void 0) { reqOpts = {}; }
34
34
  if (latencyTracker === void 0) { latencyTracker = function () { }; }
@@ -9,8 +9,6 @@ var KeyBuilderCS_1 = require("../KeyBuilderCS");
9
9
  var isLocalStorageAvailable_1 = require("../../utils/env/isLocalStorageAvailable");
10
10
  var SplitsCacheInLocal_1 = require("./SplitsCacheInLocal");
11
11
  var MySegmentsCacheInLocal_1 = require("./MySegmentsCacheInLocal");
12
- var MySegmentsCacheInMemory_1 = require("../inMemory/MySegmentsCacheInMemory");
13
- var SplitsCacheInMemory_1 = require("../inMemory/SplitsCacheInMemory");
14
12
  var browser_1 = require("../../utils/constants/browser");
15
13
  var InMemoryStorageCS_1 = require("../inMemory/InMemoryStorageCS");
16
14
  var constants_1 = require("./constants");
@@ -30,7 +28,7 @@ function InLocalStorage(options) {
30
28
  params.settings.log.warn(constants_1.LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
31
29
  return (0, InMemoryStorageCS_1.InMemoryStorageCSFactory)(params);
32
30
  }
33
- var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
31
+ var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, impressionsMode = _a.sync.impressionsMode;
34
32
  var matchingKey = (0, key_1.getMatching)(settings.core.key);
35
33
  var keys = new KeyBuilderCS_1.KeyBuilderCS(prefix, matchingKey);
36
34
  var expirationTimestamp = Date.now() - browser_1.DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -46,16 +44,7 @@ function InLocalStorage(options) {
46
44
  events: new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize),
47
45
  telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
48
46
  uniqueKeys: impressionsMode === constants_2.NONE ? new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() : undefined,
49
- destroy: function () {
50
- var _a;
51
- this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
52
- this.segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
53
- this.largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
54
- this.impressions.clear();
55
- this.impressionCounts && this.impressionCounts.clear();
56
- this.events.clear();
57
- (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
58
- },
47
+ destroy: function () { },
59
48
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
60
49
  shared: function (matchingKey) {
61
50
  return {
@@ -66,11 +55,7 @@ function InLocalStorage(options) {
66
55
  impressionCounts: this.impressionCounts,
67
56
  events: this.events,
68
57
  telemetry: this.telemetry,
69
- destroy: function () {
70
- this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
71
- this.segments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
72
- this.largeSegments = new MySegmentsCacheInMemory_1.MySegmentsCacheInMemory();
73
- }
58
+ destroy: function () { }
74
59
  };
75
60
  },
76
61
  };
@@ -26,15 +26,7 @@ function InMemoryStorageFactory(params) {
26
26
  events: new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize),
27
27
  telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
28
28
  uniqueKeys: impressionsMode === constants_1.NONE ? new UniqueKeysCacheInMemory_1.UniqueKeysCacheInMemory() : undefined,
29
- // When using MEMORY we should clean all the caches to leave them empty
30
- destroy: function () {
31
- this.splits.clear();
32
- this.segments.clear();
33
- this.impressions.clear();
34
- this.impressionCounts && this.impressionCounts.clear();
35
- this.events.clear();
36
- this.uniqueKeys && this.uniqueKeys.clear();
37
- }
29
+ destroy: function () { }
38
30
  };
39
31
  // @TODO revisit storage logic in localhost mode
40
32
  // No tracking data in localhost mode to avoid memory leaks
@@ -28,16 +28,7 @@ function InMemoryStorageCSFactory(params) {
28
28
  events: new EventsCacheInMemory_1.EventsCacheInMemory(eventsQueueSize),
29
29
  telemetry: (0, TelemetryCacheInMemory_1.shouldRecordTelemetry)(params) ? new TelemetryCacheInMemory_1.TelemetryCacheInMemory(splits, segments) : undefined,
30
30
  uniqueKeys: impressionsMode === constants_1.NONE ? new UniqueKeysCacheInMemoryCS_1.UniqueKeysCacheInMemoryCS() : undefined,
31
- // When using MEMORY we should clean all the caches to leave them empty
32
- destroy: function () {
33
- this.splits.clear();
34
- this.segments.clear();
35
- this.largeSegments.clear();
36
- this.impressions.clear();
37
- this.impressionCounts && this.impressionCounts.clear();
38
- this.events.clear();
39
- this.uniqueKeys && this.uniqueKeys.clear();
40
- },
31
+ destroy: function () { },
41
32
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
42
33
  shared: function () {
43
34
  return {
@@ -48,12 +39,7 @@ function InMemoryStorageCSFactory(params) {
48
39
  impressionCounts: this.impressionCounts,
49
40
  events: this.events,
50
41
  telemetry: this.telemetry,
51
- // Set a new splits cache to clean it for the client without affecting other clients
52
- destroy: function () {
53
- this.splits = new SplitsCacheInMemory_1.SplitsCacheInMemory(__splitFiltersValidation);
54
- this.segments.clear();
55
- this.largeSegments.clear();
56
- }
42
+ destroy: function () { }
57
43
  };
58
44
  },
59
45
  };
@@ -25,7 +25,7 @@ function segmentsEventEmitterFactory(EventEmitter) {
25
25
  /**
26
26
  * Factory of readiness manager, which handles the ready / update event propagation.
27
27
  */
28
- export function readinessManagerFactory(EventEmitter, settings, splits) {
28
+ export function readinessManagerFactory(EventEmitter, settings, splits, isShared) {
29
29
  if (splits === void 0) { splits = splitsEventEmitterFactory(EventEmitter); }
30
30
  var readyTimeout = settings.startup.readyTimeout;
31
31
  var segments = segmentsEventEmitterFactory(EventEmitter);
@@ -51,22 +51,24 @@ export function readinessManagerFactory(EventEmitter, settings, splits) {
51
51
  syncLastUpdate();
52
52
  gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
53
53
  }
54
- var readyTimeoutId;
55
- if (readyTimeout > 0) {
56
- if (splits.hasInit)
57
- readyTimeoutId = setTimeout(timeout, readyTimeout);
58
- else
59
- splits.initCallbacks.push(function () { readyTimeoutId = setTimeout(timeout, readyTimeout); });
60
- }
61
54
  // emit SDK_READY and SDK_UPDATE
62
55
  var isReady = false;
63
56
  splits.on(SDK_SPLITS_ARRIVED, checkIsReadyOrUpdate);
64
57
  segments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
65
58
  var isDestroyed = false;
59
+ var readyTimeoutId;
60
+ function __init() {
61
+ isDestroyed = false;
62
+ if (readyTimeout > 0 && !isReady)
63
+ readyTimeoutId = setTimeout(timeout, readyTimeout);
64
+ }
65
+ splits.initCallbacks.push(__init);
66
+ if (splits.hasInit)
67
+ __init();
66
68
  function checkIsReadyFromCache() {
67
69
  isReadyFromCache = true;
68
70
  // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
69
- if (!isReady) {
71
+ if (!isReady && !isDestroyed) {
70
72
  try {
71
73
  syncLastUpdate();
72
74
  gate.emit(SDK_READY_FROM_CACHE);
@@ -78,6 +80,8 @@ export function readinessManagerFactory(EventEmitter, settings, splits) {
78
80
  }
79
81
  }
80
82
  function checkIsReadyOrUpdate(diff) {
83
+ if (isDestroyed)
84
+ return;
81
85
  if (isReady) {
82
86
  try {
83
87
  syncLastUpdate();
@@ -103,14 +107,12 @@ export function readinessManagerFactory(EventEmitter, settings, splits) {
103
107
  }
104
108
  }
105
109
  }
106
- var refCount = 1;
107
110
  return {
108
111
  splits: splits,
109
112
  segments: segments,
110
113
  gate: gate,
111
114
  shared: function () {
112
- refCount++;
113
- return readinessManagerFactory(EventEmitter, settings, splits);
115
+ return readinessManagerFactory(EventEmitter, settings, splits, true);
114
116
  },
115
117
  // @TODO review/remove next methods when non-recoverable errors are reworked
116
118
  // Called on consumer mode, when storage fails to connect
@@ -127,13 +129,9 @@ export function readinessManagerFactory(EventEmitter, settings, splits) {
127
129
  destroy: function () {
128
130
  isDestroyed = true;
129
131
  syncLastUpdate();
130
- segments.removeAllListeners();
131
- gate.removeAllListeners();
132
132
  clearTimeout(readyTimeoutId);
133
- if (refCount > 0)
134
- refCount--;
135
- if (refCount === 0)
136
- splits.removeAllListeners();
133
+ if (!isShared)
134
+ splits.hasInit = false;
137
135
  },
138
136
  isReady: function () { return isReady; },
139
137
  isReadyFromCache: function () { return isReadyFromCache; },
@@ -99,6 +99,7 @@ export function sdkFactory(params) {
99
99
  Logger: createLoggerAPI(log),
100
100
  settings: settings,
101
101
  destroy: function () {
102
+ hasInit = false;
102
103
  return Promise.all(Object.keys(clients).map(function (key) { return clients[key].destroy(); })).then(function () { });
103
104
  }
104
105
  }, extraProps && extraProps(ctx), lazyInit ? { init: init } : init());
@@ -29,3 +29,7 @@ export function decorateHeaders(settings, headers) {
29
29
  }
30
30
  return headers;
31
31
  }
32
+ export function removeNonISO88591(input) {
33
+ // eslint-disable-next-line no-control-regex
34
+ return input.replace(/[^\x00-\xFF]/g, '');
35
+ }
@@ -1,6 +1,6 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { ERROR_HTTP, ERROR_CLIENT_CANNOT_GET_READY } from '../logger/constants';
3
- import { decorateHeaders } from './decorateHeaders';
3
+ import { decorateHeaders, removeNonISO88591 } from './decorateHeaders';
4
4
  var messageNoFetch = 'Global fetch API is not available.';
5
5
  /**
6
6
  * Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
@@ -25,7 +25,7 @@ export function splitHttpClientFactory(settings, _a) {
25
25
  if (ip)
26
26
  commonHeaders['SplitSDKMachineIP'] = ip;
27
27
  if (hostname)
28
- commonHeaders['SplitSDKMachineName'] = hostname;
28
+ commonHeaders['SplitSDKMachineName'] = removeNonISO88591(hostname);
29
29
  return function httpClient(url, reqOpts, latencyTracker, logErrorsAsInfo) {
30
30
  if (reqOpts === void 0) { reqOpts = {}; }
31
31
  if (latencyTracker === void 0) { latencyTracker = function () { }; }
@@ -6,8 +6,6 @@ import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
6
6
  import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
7
7
  import { SplitsCacheInLocal } from './SplitsCacheInLocal';
8
8
  import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
9
- import { MySegmentsCacheInMemory } from '../inMemory/MySegmentsCacheInMemory';
10
- import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
11
9
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
12
10
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
13
11
  import { LOG_PREFIX } from './constants';
@@ -27,7 +25,7 @@ export function InLocalStorage(options) {
27
25
  params.settings.log.warn(LOG_PREFIX + 'LocalStorage API is unavailable. Falling back to default MEMORY storage');
28
26
  return InMemoryStorageCSFactory(params);
29
27
  }
30
- var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
28
+ var settings = params.settings, _a = params.settings, log = _a.log, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, impressionsMode = _a.sync.impressionsMode;
31
29
  var matchingKey = getMatching(settings.core.key);
32
30
  var keys = new KeyBuilderCS(prefix, matchingKey);
33
31
  var expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -43,16 +41,7 @@ export function InLocalStorage(options) {
43
41
  events: new EventsCacheInMemory(eventsQueueSize),
44
42
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
45
43
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
46
- destroy: function () {
47
- var _a;
48
- this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
49
- this.segments = new MySegmentsCacheInMemory();
50
- this.largeSegments = new MySegmentsCacheInMemory();
51
- this.impressions.clear();
52
- this.impressionCounts && this.impressionCounts.clear();
53
- this.events.clear();
54
- (_a = this.uniqueKeys) === null || _a === void 0 ? void 0 : _a.clear();
55
- },
44
+ destroy: function () { },
56
45
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
57
46
  shared: function (matchingKey) {
58
47
  return {
@@ -63,11 +52,7 @@ export function InLocalStorage(options) {
63
52
  impressionCounts: this.impressionCounts,
64
53
  events: this.events,
65
54
  telemetry: this.telemetry,
66
- destroy: function () {
67
- this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
68
- this.segments = new MySegmentsCacheInMemory();
69
- this.largeSegments = new MySegmentsCacheInMemory();
70
- }
55
+ destroy: function () { }
71
56
  };
72
57
  },
73
58
  };
@@ -23,15 +23,7 @@ export function InMemoryStorageFactory(params) {
23
23
  events: new EventsCacheInMemory(eventsQueueSize),
24
24
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
25
25
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
26
- // When using MEMORY we should clean all the caches to leave them empty
27
- destroy: function () {
28
- this.splits.clear();
29
- this.segments.clear();
30
- this.impressions.clear();
31
- this.impressionCounts && this.impressionCounts.clear();
32
- this.events.clear();
33
- this.uniqueKeys && this.uniqueKeys.clear();
34
- }
26
+ destroy: function () { }
35
27
  };
36
28
  // @TODO revisit storage logic in localhost mode
37
29
  // No tracking data in localhost mode to avoid memory leaks
@@ -25,16 +25,7 @@ export function InMemoryStorageCSFactory(params) {
25
25
  events: new EventsCacheInMemory(eventsQueueSize),
26
26
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
27
27
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
28
- // When using MEMORY we should clean all the caches to leave them empty
29
- destroy: function () {
30
- this.splits.clear();
31
- this.segments.clear();
32
- this.largeSegments.clear();
33
- this.impressions.clear();
34
- this.impressionCounts && this.impressionCounts.clear();
35
- this.events.clear();
36
- this.uniqueKeys && this.uniqueKeys.clear();
37
- },
28
+ destroy: function () { },
38
29
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
39
30
  shared: function () {
40
31
  return {
@@ -45,12 +36,7 @@ export function InMemoryStorageCSFactory(params) {
45
36
  impressionCounts: this.impressionCounts,
46
37
  events: this.events,
47
38
  telemetry: this.telemetry,
48
- // Set a new splits cache to clean it for the client without affecting other clients
49
- destroy: function () {
50
- this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
51
- this.segments.clear();
52
- this.largeSegments.clear();
53
- }
39
+ destroy: function () { }
54
40
  };
55
41
  },
56
42
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -37,7 +37,9 @@ function segmentsEventEmitterFactory(EventEmitter: new () => SplitIO.IEventEmitt
37
37
  export function readinessManagerFactory(
38
38
  EventEmitter: new () => SplitIO.IEventEmitter,
39
39
  settings: ISettings,
40
- splits: ISplitsEventEmitter = splitsEventEmitterFactory(EventEmitter)): IReadinessManager {
40
+ splits: ISplitsEventEmitter = splitsEventEmitterFactory(EventEmitter),
41
+ isShared?: boolean
42
+ ): IReadinessManager {
41
43
 
42
44
  const readyTimeout = settings.startup.readyTimeout;
43
45
 
@@ -66,11 +68,6 @@ export function readinessManagerFactory(
66
68
  gate.emit(SDK_READY_TIMED_OUT, 'Split SDK emitted SDK_READY_TIMED_OUT event.');
67
69
  }
68
70
 
69
- let readyTimeoutId: ReturnType<typeof setTimeout>;
70
- if (readyTimeout > 0) {
71
- if (splits.hasInit) readyTimeoutId = setTimeout(timeout, readyTimeout);
72
- else splits.initCallbacks.push(() => { readyTimeoutId = setTimeout(timeout, readyTimeout); });
73
- }
74
71
 
75
72
  // emit SDK_READY and SDK_UPDATE
76
73
  let isReady = false;
@@ -78,11 +75,19 @@ export function readinessManagerFactory(
78
75
  segments.on(SDK_SEGMENTS_ARRIVED, checkIsReadyOrUpdate);
79
76
 
80
77
  let isDestroyed = false;
78
+ let readyTimeoutId: ReturnType<typeof setTimeout>;
79
+ function __init() {
80
+ isDestroyed = false;
81
+ if (readyTimeout > 0 && !isReady) readyTimeoutId = setTimeout(timeout, readyTimeout);
82
+ }
83
+
84
+ splits.initCallbacks.push(__init);
85
+ if (splits.hasInit) __init();
81
86
 
82
87
  function checkIsReadyFromCache() {
83
88
  isReadyFromCache = true;
84
89
  // Don't emit SDK_READY_FROM_CACHE if SDK_READY has been emitted
85
- if (!isReady) {
90
+ if (!isReady && !isDestroyed) {
86
91
  try {
87
92
  syncLastUpdate();
88
93
  gate.emit(SDK_READY_FROM_CACHE);
@@ -94,6 +99,7 @@ export function readinessManagerFactory(
94
99
  }
95
100
 
96
101
  function checkIsReadyOrUpdate(diff: any) {
102
+ if (isDestroyed) return;
97
103
  if (isReady) {
98
104
  try {
99
105
  syncLastUpdate();
@@ -117,16 +123,13 @@ export function readinessManagerFactory(
117
123
  }
118
124
  }
119
125
 
120
- let refCount = 1;
121
-
122
126
  return {
123
127
  splits,
124
128
  segments,
125
129
  gate,
126
130
 
127
131
  shared() {
128
- refCount++;
129
- return readinessManagerFactory(EventEmitter, settings, splits);
132
+ return readinessManagerFactory(EventEmitter, settings, splits, true);
130
133
  },
131
134
 
132
135
  // @TODO review/remove next methods when non-recoverable errors are reworked
@@ -145,13 +148,9 @@ export function readinessManagerFactory(
145
148
  destroy() {
146
149
  isDestroyed = true;
147
150
  syncLastUpdate();
148
-
149
- segments.removeAllListeners();
150
- gate.removeAllListeners();
151
151
  clearTimeout(readyTimeoutId);
152
152
 
153
- if (refCount > 0) refCount--;
154
- if (refCount === 0) splits.removeAllListeners();
153
+ if (!isShared) splits.hasInit = false;
155
154
  },
156
155
 
157
156
  isReady() { return isReady; },
@@ -126,6 +126,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
126
126
  settings,
127
127
 
128
128
  destroy() {
129
+ hasInit = false;
129
130
  return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => { });
130
131
  }
131
132
  }, extraProps && extraProps(ctx), lazyInit ? { init } : init());
@@ -30,3 +30,8 @@ export function decorateHeaders(settings: ISettings, headers: Record<string, str
30
30
  }
31
31
  return headers;
32
32
  }
33
+
34
+ export function removeNonISO88591(input: string) {
35
+ // eslint-disable-next-line no-control-regex
36
+ return input.replace(/[^\x00-\xFF]/g, '');
37
+ }
@@ -3,7 +3,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
3
3
  import { ERROR_HTTP, ERROR_CLIENT_CANNOT_GET_READY } from '../logger/constants';
4
4
  import { ISettings } from '../types';
5
5
  import { IPlatform } from '../sdkFactory/types';
6
- import { decorateHeaders } from './decorateHeaders';
6
+ import { decorateHeaders, removeNonISO88591 } from './decorateHeaders';
7
7
 
8
8
  const messageNoFetch = 'Global fetch API is not available.';
9
9
 
@@ -30,7 +30,7 @@ export function splitHttpClientFactory(settings: ISettings, { getOptions, getFet
30
30
  };
31
31
 
32
32
  if (ip) commonHeaders['SplitSDKMachineIP'] = ip;
33
- if (hostname) commonHeaders['SplitSDKMachineName'] = hostname;
33
+ if (hostname) commonHeaders['SplitSDKMachineName'] = removeNonISO88591(hostname);
34
34
 
35
35
  return function httpClient(url: string, reqOpts: IRequestOptions = {}, latencyTracker: (error?: NetworkError) => void = () => { }, logErrorsAsInfo: boolean = false): Promise<IResponse> {
36
36
 
@@ -7,8 +7,6 @@ import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
7
7
  import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
8
8
  import { SplitsCacheInLocal } from './SplitsCacheInLocal';
9
9
  import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
10
- import { MySegmentsCacheInMemory } from '../inMemory/MySegmentsCacheInMemory';
11
- import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
12
10
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
13
11
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
14
12
  import { LOG_PREFIX } from './constants';
@@ -36,7 +34,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
36
34
  return InMemoryStorageCSFactory(params);
37
35
  }
38
36
 
39
- const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
37
+ const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
40
38
  const matchingKey = getMatching(settings.core.key);
41
39
  const keys = new KeyBuilderCS(prefix, matchingKey);
42
40
  const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -55,15 +53,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
55
53
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
56
54
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
57
55
 
58
- destroy() {
59
- this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
60
- this.segments = new MySegmentsCacheInMemory();
61
- this.largeSegments = new MySegmentsCacheInMemory();
62
- this.impressions.clear();
63
- this.impressionCounts && this.impressionCounts.clear();
64
- this.events.clear();
65
- this.uniqueKeys?.clear();
66
- },
56
+ destroy() { },
67
57
 
68
58
  // When using shared instantiation with MEMORY we reuse everything but segments (they are customer per key).
69
59
  shared(matchingKey: string) {
@@ -77,11 +67,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
77
67
  events: this.events,
78
68
  telemetry: this.telemetry,
79
69
 
80
- destroy() {
81
- this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
82
- this.segments = new MySegmentsCacheInMemory();
83
- this.largeSegments = new MySegmentsCacheInMemory();
84
- }
70
+ destroy() { }
85
71
  };
86
72
  },
87
73
  };
@@ -28,15 +28,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
28
28
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
29
29
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
30
30
 
31
- // When using MEMORY we should clean all the caches to leave them empty
32
- destroy() {
33
- this.splits.clear();
34
- this.segments.clear();
35
- this.impressions.clear();
36
- this.impressionCounts && this.impressionCounts.clear();
37
- this.events.clear();
38
- this.uniqueKeys && this.uniqueKeys.clear();
39
- }
31
+ destroy() { }
40
32
  };
41
33
 
42
34
  // @TODO revisit storage logic in localhost mode
@@ -30,16 +30,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
30
30
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
31
31
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
32
32
 
33
- // When using MEMORY we should clean all the caches to leave them empty
34
- destroy() {
35
- this.splits.clear();
36
- this.segments.clear();
37
- this.largeSegments.clear();
38
- this.impressions.clear();
39
- this.impressionCounts && this.impressionCounts.clear();
40
- this.events.clear();
41
- this.uniqueKeys && this.uniqueKeys.clear();
42
- },
33
+ destroy() { },
43
34
 
44
35
  // When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
45
36
  shared() {
@@ -52,12 +43,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
52
43
  events: this.events,
53
44
  telemetry: this.telemetry,
54
45
 
55
- // Set a new splits cache to clean it for the client without affecting other clients
56
- destroy() {
57
- this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
58
- this.segments.clear();
59
- this.largeSegments.clear();
60
- }
46
+ destroy() { }
61
47
  };
62
48
  },
63
49
  };