@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 +4 -0
- package/cjs/readiness/readinessManager.js +16 -18
- package/cjs/sdkFactory/index.js +1 -0
- package/cjs/services/decorateHeaders.js +6 -1
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/inLocalStorage/index.js +3 -18
- package/cjs/storages/inMemory/InMemoryStorage.js +1 -9
- package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -16
- package/esm/readiness/readinessManager.js +16 -18
- package/esm/sdkFactory/index.js +1 -0
- package/esm/services/decorateHeaders.js +4 -0
- package/esm/services/splitHttpClient.js +2 -2
- package/esm/storages/inLocalStorage/index.js +3 -18
- package/esm/storages/inMemory/InMemoryStorage.js +1 -9
- package/esm/storages/inMemory/InMemoryStorageCS.js +2 -16
- package/package.json +1 -1
- package/src/readiness/readinessManager.ts +15 -16
- package/src/sdkFactory/index.ts +1 -0
- package/src/services/decorateHeaders.ts +5 -0
- package/src/services/splitHttpClient.ts +2 -2
- package/src/storages/inLocalStorage/index.ts +3 -17
- package/src/storages/inMemory/InMemoryStorage.ts +1 -9
- package/src/storages/inMemory/InMemoryStorageCS.ts +2 -16
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
|
-
|
|
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 (
|
|
137
|
-
|
|
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; },
|
package/cjs/sdkFactory/index.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
134
|
-
|
|
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; },
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -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());
|
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -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)
|
|
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
|
-
|
|
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 (
|
|
154
|
-
if (refCount === 0) splits.removeAllListeners();
|
|
153
|
+
if (!isShared) splits.hasInit = false;
|
|
155
154
|
},
|
|
156
155
|
|
|
157
156
|
isReady() { return isReady; },
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|