@splitsoftware/splitio-commons 2.0.0 → 2.0.1-rc.0
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 +6 -3
- package/README.md +1 -1
- package/cjs/listeners/node.js +1 -1
- package/cjs/readiness/readinessManager.js +16 -18
- package/cjs/sdkClient/sdkClient.js +3 -6
- package/cjs/sdkFactory/index.js +1 -0
- 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/cjs/storages/inRedis/SplitsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/index.js +1 -1
- package/cjs/sync/syncTask.js +2 -2
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/esm/listeners/node.js +1 -1
- package/esm/readiness/readinessManager.js +16 -18
- package/esm/sdkClient/sdkClient.js +3 -6
- package/esm/sdkFactory/index.js +1 -0
- 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/esm/storages/inRedis/SplitsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/index.js +1 -1
- package/esm/sync/syncTask.js +2 -2
- package/esm/utils/settingsValidation/index.js +1 -1
- package/package.json +1 -1
- package/src/listeners/node.ts +2 -2
- package/src/readiness/readinessManager.ts +15 -16
- package/src/sdkClient/sdkClient.ts +3 -7
- package/src/sdkFactory/index.ts +1 -0
- package/src/sdkFactory/types.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/src/storages/inRedis/SplitsCacheInRedis.ts +1 -1
- package/src/storages/inRedis/index.ts +1 -1
- package/src/sync/syncTask.ts +2 -2
- package/src/utils/settingsValidation/index.ts +1 -1
- package/src/utils/settingsValidation/types.ts +1 -1
- package/types/splitio.d.ts +17 -17
package/CHANGES.txt
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
2.0.1 (XXX XX, 2024)
|
|
2
|
+
- Updated the factory `init` and `destroy` methods to support re-initialization after destruction, enabling repeated cycles of initialization and cleanup. 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
|
+
|
|
1
4
|
2.0.0 (November 1, 2024)
|
|
2
5
|
- Added support for targeting rules based on large segments.
|
|
3
6
|
- Added `factory.destroy()` method, which invokes the `destroy` method on all SDK clients created by the factory.
|
|
@@ -20,7 +23,7 @@
|
|
|
20
23
|
- Updated some transitive dependencies for vulnerability fixes.
|
|
21
24
|
|
|
22
25
|
1.16.0 (June 13, 2024)
|
|
23
|
-
- Added the `getOptions` method to the `IPlatform` interface to allow the SDK to pass request options to the `fetch` function and `EventSource` constructor when fetching data from the Split servers. The method is optional and, if provided, it is called twice: first for the `fetch` options and then for the `EventSource` options. Useful for advanced use cases like configuring a proxy or validating HTTPS certificates in
|
|
26
|
+
- Added the `getOptions` method to the `IPlatform` interface to allow the SDK to pass request options to the `fetch` function and `EventSource` constructor when fetching data from the Split servers. The method is optional and, if provided, it is called twice: first for the `fetch` options and then for the `EventSource` options. Useful for advanced use cases like configuring a proxy or validating HTTPS certificates in Node.js.
|
|
24
27
|
- Updated the Redis storage to lazily import the `ioredis` dependency when the storage is created. This prevents errors when the SDK is imported or bundled in a .mjs file, as `ioredis` is a CommonJS module.
|
|
25
28
|
- Bugfixing - Restored some input validation error logs that were removed in version 1.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name.
|
|
26
29
|
- Bugfixing - Fixed localhost mode to emit SDK_UPDATE when mocked feature flags are updated in the `features` object map of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119).
|
|
@@ -57,7 +60,7 @@
|
|
|
57
60
|
- Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload.
|
|
58
61
|
- Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init.
|
|
59
62
|
- Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views.
|
|
60
|
-
- Bugfixing - Fixed SDK key validation in
|
|
63
|
+
- Bugfixing - Fixed SDK key validation in Node.js to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768).
|
|
61
64
|
|
|
62
65
|
1.10.0 (October 20, 2023)
|
|
63
66
|
- Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225).
|
|
@@ -135,7 +138,7 @@
|
|
|
135
138
|
1.3.0 (April 6, 2022)
|
|
136
139
|
- Added user consent feature to allow delaying or disabling the data tracking from SDK until user consent is explicitly granted or declined. Read more in our docs.
|
|
137
140
|
- Added `scheduler.impressionsQueueSize` property to SDK configuration to limit the amount of impressions tracked in memory. Read more in our docs.
|
|
138
|
-
- Added support to accept TLS configuration options to the Redis storage in
|
|
141
|
+
- Added support to accept TLS configuration options to the Redis storage in Node.js. Read more in our docs.
|
|
139
142
|
- Updated format for MySegments keys in LocalStorage, keeping backwards compatibility (issue https://github.com/splitio/javascript-client/issues/638).
|
|
140
143
|
- Updated some modules due to general polishing and refactors, including updates in some log messages.
|
|
141
144
|
- Updated some dependencies for vulnerability fixes.
|
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ Split has built and maintains SDKs for:
|
|
|
33
33
|
* Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
|
|
34
34
|
* JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
|
|
35
35
|
* JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK)
|
|
36
|
-
* Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
|
|
36
|
+
* Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
|
|
37
37
|
* PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
|
|
38
38
|
* PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://help.split.io/hc/en-us/articles/18305128673933-PHP-Thin-Client-SDK)
|
|
39
39
|
* Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
|
package/cjs/listeners/node.js
CHANGED
|
@@ -15,7 +15,7 @@ var EVENT_NAME = 'for SIGTERM signal.';
|
|
|
15
15
|
var NodeSignalListener = /** @class */ (function () {
|
|
16
16
|
function NodeSignalListener(syncManager, // private handler: () => MaybeThenable<void>,
|
|
17
17
|
settings) {
|
|
18
|
-
// @TODO review handler logic when implementing Node SDK
|
|
18
|
+
// @TODO review handler logic when implementing Node.js SDK
|
|
19
19
|
this.handler = function () {
|
|
20
20
|
if (syncManager) {
|
|
21
21
|
// syncManager.stop();
|
|
@@ -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; },
|
|
@@ -45,19 +45,16 @@ function sdkClientFactory(params, isSharedClient) {
|
|
|
45
45
|
destroy: function () {
|
|
46
46
|
// Mark the SDK as destroyed immediately
|
|
47
47
|
sdkReadinessManager.readinessManager.destroy();
|
|
48
|
-
// For main client,
|
|
48
|
+
// For main client, cleanup the SDK Key, listeners and scheduled jobs, and record stat before flushing data
|
|
49
49
|
if (!isSharedClient) {
|
|
50
50
|
(0, apiKey_1.releaseApiKey)(settings.core.authorizationKey);
|
|
51
51
|
telemetryTracker.sessionLength();
|
|
52
|
+
signalListener && signalListener.stop();
|
|
53
|
+
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
52
54
|
}
|
|
53
55
|
// Stop background jobs
|
|
54
56
|
syncManager && syncManager.stop();
|
|
55
57
|
return __flush().then(function () {
|
|
56
|
-
// For main client, cleanup event listeners and scheduled jobs
|
|
57
|
-
if (!isSharedClient) {
|
|
58
|
-
signalListener && signalListener.stop();
|
|
59
|
-
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
60
|
-
}
|
|
61
58
|
// Cleanup storage
|
|
62
59
|
return storage.destroy();
|
|
63
60
|
});
|
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());
|
|
@@ -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
|
};
|
|
@@ -18,7 +18,7 @@ function processPipelineAnswer(results) {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* ISplitsCacheAsync implementation that stores split definitions in Redis.
|
|
21
|
-
* Supported by Node.
|
|
21
|
+
* Supported by Node.js
|
|
22
22
|
*/
|
|
23
23
|
var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
24
24
|
(0, tslib_1.__extends)(SplitsCacheInRedis, _super);
|
|
@@ -13,7 +13,7 @@ var UniqueKeysCacheInRedis_1 = require("./UniqueKeysCacheInRedis");
|
|
|
13
13
|
var ImpressionCountsCacheInRedis_1 = require("./ImpressionCountsCacheInRedis");
|
|
14
14
|
var utils_1 = require("../utils");
|
|
15
15
|
/**
|
|
16
|
-
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
16
|
+
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.js
|
|
17
17
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
18
18
|
*/
|
|
19
19
|
function InRedisStorage(options) {
|
package/cjs/sync/syncTask.js
CHANGED
|
@@ -69,8 +69,8 @@ function syncTaskFactory(log, task, period, taskName) {
|
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
stop: function () {
|
|
72
|
-
running
|
|
73
|
-
|
|
72
|
+
if (running) {
|
|
73
|
+
running = false;
|
|
74
74
|
log.debug(constants_1.SYNC_TASK_STOP, [taskName]);
|
|
75
75
|
clearTimeout(timeoutID);
|
|
76
76
|
timeoutID = undefined;
|
|
@@ -58,7 +58,7 @@ exports.base = {
|
|
|
58
58
|
storage: undefined,
|
|
59
59
|
// Defines if the logs are enabled, SDK wide.
|
|
60
60
|
debug: undefined,
|
|
61
|
-
// Defines the impression listener
|
|
61
|
+
// Defines the impression listener.
|
|
62
62
|
impressionListener: undefined,
|
|
63
63
|
// Instance version.
|
|
64
64
|
version: undefined,
|
package/esm/listeners/node.js
CHANGED
|
@@ -12,7 +12,7 @@ var EVENT_NAME = 'for SIGTERM signal.';
|
|
|
12
12
|
var NodeSignalListener = /** @class */ (function () {
|
|
13
13
|
function NodeSignalListener(syncManager, // private handler: () => MaybeThenable<void>,
|
|
14
14
|
settings) {
|
|
15
|
-
// @TODO review handler logic when implementing Node SDK
|
|
15
|
+
// @TODO review handler logic when implementing Node.js SDK
|
|
16
16
|
this.handler = function () {
|
|
17
17
|
if (syncManager) {
|
|
18
18
|
// syncManager.stop();
|
|
@@ -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; },
|
|
@@ -42,19 +42,16 @@ export function sdkClientFactory(params, isSharedClient) {
|
|
|
42
42
|
destroy: function () {
|
|
43
43
|
// Mark the SDK as destroyed immediately
|
|
44
44
|
sdkReadinessManager.readinessManager.destroy();
|
|
45
|
-
// For main client,
|
|
45
|
+
// For main client, cleanup the SDK Key, listeners and scheduled jobs, and record stat before flushing data
|
|
46
46
|
if (!isSharedClient) {
|
|
47
47
|
releaseApiKey(settings.core.authorizationKey);
|
|
48
48
|
telemetryTracker.sessionLength();
|
|
49
|
+
signalListener && signalListener.stop();
|
|
50
|
+
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
49
51
|
}
|
|
50
52
|
// Stop background jobs
|
|
51
53
|
syncManager && syncManager.stop();
|
|
52
54
|
return __flush().then(function () {
|
|
53
|
-
// For main client, cleanup event listeners and scheduled jobs
|
|
54
|
-
if (!isSharedClient) {
|
|
55
|
-
signalListener && signalListener.stop();
|
|
56
|
-
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
57
|
-
}
|
|
58
55
|
// Cleanup storage
|
|
59
56
|
return storage.destroy();
|
|
60
57
|
});
|
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());
|
|
@@ -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
|
};
|
|
@@ -15,7 +15,7 @@ function processPipelineAnswer(results) {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* ISplitsCacheAsync implementation that stores split definitions in Redis.
|
|
18
|
-
* Supported by Node.
|
|
18
|
+
* Supported by Node.js
|
|
19
19
|
*/
|
|
20
20
|
var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
21
21
|
__extends(SplitsCacheInRedis, _super);
|
|
@@ -10,7 +10,7 @@ import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
|
|
|
10
10
|
import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
|
|
11
11
|
import { metadataBuilder } from '../utils';
|
|
12
12
|
/**
|
|
13
|
-
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
13
|
+
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.js
|
|
14
14
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
15
15
|
*/
|
|
16
16
|
export function InRedisStorage(options) {
|
package/esm/sync/syncTask.js
CHANGED
|
@@ -66,8 +66,8 @@ export function syncTaskFactory(log, task, period, taskName) {
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
stop: function () {
|
|
69
|
-
running
|
|
70
|
-
|
|
69
|
+
if (running) {
|
|
70
|
+
running = false;
|
|
71
71
|
log.debug(SYNC_TASK_STOP, [taskName]);
|
|
72
72
|
clearTimeout(timeoutID);
|
|
73
73
|
timeoutID = undefined;
|
|
@@ -55,7 +55,7 @@ export var base = {
|
|
|
55
55
|
storage: undefined,
|
|
56
56
|
// Defines if the logs are enabled, SDK wide.
|
|
57
57
|
debug: undefined,
|
|
58
|
-
// Defines the impression listener
|
|
58
|
+
// Defines the impression listener.
|
|
59
59
|
impressionListener: undefined,
|
|
60
60
|
// Instance version.
|
|
61
61
|
version: undefined,
|
package/package.json
CHANGED
package/src/listeners/node.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @TODO eventually migrate to
|
|
1
|
+
// @TODO eventually migrate to Node.js SDK package.
|
|
2
2
|
import { ISignalListener } from './types';
|
|
3
3
|
import { thenable } from '../utils/promise/thenable';
|
|
4
4
|
import { MaybeThenable } from '../dtos/types';
|
|
@@ -25,7 +25,7 @@ export class NodeSignalListener implements ISignalListener {
|
|
|
25
25
|
syncManager: ISyncManager | undefined, // private handler: () => MaybeThenable<void>,
|
|
26
26
|
settings: ISettings
|
|
27
27
|
) {
|
|
28
|
-
// @TODO review handler logic when implementing Node SDK
|
|
28
|
+
// @TODO review handler logic when implementing Node.js SDK
|
|
29
29
|
this.handler = function () {
|
|
30
30
|
if (syncManager) {
|
|
31
31
|
// syncManager.stop();
|
|
@@ -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; },
|
|
@@ -56,22 +56,18 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
56
56
|
// Mark the SDK as destroyed immediately
|
|
57
57
|
sdkReadinessManager.readinessManager.destroy();
|
|
58
58
|
|
|
59
|
-
// For main client,
|
|
59
|
+
// For main client, cleanup the SDK Key, listeners and scheduled jobs, and record stat before flushing data
|
|
60
60
|
if (!isSharedClient) {
|
|
61
61
|
releaseApiKey(settings.core.authorizationKey);
|
|
62
62
|
telemetryTracker.sessionLength();
|
|
63
|
+
signalListener && signalListener.stop();
|
|
64
|
+
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
// Stop background jobs
|
|
66
68
|
syncManager && syncManager.stop();
|
|
67
69
|
|
|
68
70
|
return __flush().then(() => {
|
|
69
|
-
// For main client, cleanup event listeners and scheduled jobs
|
|
70
|
-
if (!isSharedClient) {
|
|
71
|
-
signalListener && signalListener.stop();
|
|
72
|
-
uniqueKeysTracker && uniqueKeysTracker.stop();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
71
|
// Cleanup storage
|
|
76
72
|
return storage.destroy();
|
|
77
73
|
});
|
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());
|
package/src/sdkFactory/types.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface IPlatform {
|
|
|
28
28
|
*/
|
|
29
29
|
getEventSource?: (settings: ISettings) => (IEventSourceConstructor | undefined)
|
|
30
30
|
/**
|
|
31
|
-
* EventEmitter constructor, like
|
|
31
|
+
* EventEmitter constructor, like Node.js EventEmitter or a polyfill.
|
|
32
32
|
*/
|
|
33
33
|
EventEmitter: new () => SplitIO.IEventEmitter,
|
|
34
34
|
/**
|
|
@@ -104,7 +104,7 @@ export interface ISdkFactoryParams {
|
|
|
104
104
|
filterAdapterFactory?: () => IFilterAdapter
|
|
105
105
|
|
|
106
106
|
// Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed.
|
|
107
|
-
// Pass only if `syncManager` (used by
|
|
107
|
+
// Pass only if `syncManager` (used by NodeSignalListener) and `splitApi` (used by Browser listener) are passed.
|
|
108
108
|
SignalListener?: new (
|
|
109
109
|
syncManager: ISyncManager | undefined, // Used by NodeSignalListener to flush data, and by BrowserSignalListener to close streaming connection.
|
|
110
110
|
settings: ISettings, // Used by BrowserSignalListener
|
|
@@ -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
|
};
|
|
@@ -19,7 +19,7 @@ function processPipelineAnswer(results: Array<[Error | null, string]>): string[]
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* ISplitsCacheAsync implementation that stores split definitions in Redis.
|
|
22
|
-
* Supported by Node.
|
|
22
|
+
* Supported by Node.js
|
|
23
23
|
*/
|
|
24
24
|
export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
25
25
|
|
|
@@ -18,7 +18,7 @@ export interface InRedisStorageOptions {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.
|
|
21
|
+
* InRedis storage factory for consumer server-side SplitFactory, that uses `Ioredis` Redis client for Node.js
|
|
22
22
|
* @see {@link https://www.npmjs.com/package/ioredis}
|
|
23
23
|
*/
|
|
24
24
|
export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsyncFactory {
|
package/src/sync/syncTask.ts
CHANGED
|
@@ -68,8 +68,8 @@ export function syncTaskFactory<Input extends any[], Output = any>(log: ILogger,
|
|
|
68
68
|
},
|
|
69
69
|
|
|
70
70
|
stop() {
|
|
71
|
-
running
|
|
72
|
-
|
|
71
|
+
if (running) {
|
|
72
|
+
running = false;
|
|
73
73
|
log.debug(SYNC_TASK_STOP, [taskName]);
|
|
74
74
|
clearTimeout(timeoutID);
|
|
75
75
|
timeoutID = undefined;
|
|
@@ -64,7 +64,7 @@ export const base = {
|
|
|
64
64
|
// Defines if the logs are enabled, SDK wide.
|
|
65
65
|
debug: undefined,
|
|
66
66
|
|
|
67
|
-
// Defines the impression listener
|
|
67
|
+
// Defines the impression listener.
|
|
68
68
|
impressionListener: undefined,
|
|
69
69
|
|
|
70
70
|
// Instance version.
|
|
@@ -2,7 +2,7 @@ import { ISettings } from '../../types';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Parameters used to specialize the settings validation for each API variant
|
|
5
|
-
* (client-side, server-side) and environment (Node
|
|
5
|
+
* (client-side, server-side) and environment (Node.js, Browser, etc)
|
|
6
6
|
*/
|
|
7
7
|
export interface ISettingsValidationParams {
|
|
8
8
|
/**
|
package/types/splitio.d.ts
CHANGED
|
@@ -164,7 +164,7 @@ interface INonPluggableSharedSettings {
|
|
|
164
164
|
*/
|
|
165
165
|
interface IServerSideSharedSettings {
|
|
166
166
|
/**
|
|
167
|
-
* SDK Core settings for
|
|
167
|
+
* SDK Core settings for Node.js.
|
|
168
168
|
*/
|
|
169
169
|
core: {
|
|
170
170
|
/**
|
|
@@ -187,7 +187,7 @@ interface IServerSideSharedSettings {
|
|
|
187
187
|
IPAddressesEnabled?: boolean;
|
|
188
188
|
};
|
|
189
189
|
/**
|
|
190
|
-
* SDK Startup settings for
|
|
190
|
+
* SDK Startup settings for Node.js.
|
|
191
191
|
*/
|
|
192
192
|
startup?: {
|
|
193
193
|
/**
|
|
@@ -450,7 +450,7 @@ interface IClientSideSyncSharedSettings extends IClientSideSharedSettings, ISync
|
|
|
450
450
|
declare namespace SplitIO {
|
|
451
451
|
|
|
452
452
|
/**
|
|
453
|
-
* EventEmitter interface based on a subset of the
|
|
453
|
+
* EventEmitter interface based on a subset of the Node.js EventEmitter methods.
|
|
454
454
|
*/
|
|
455
455
|
interface IEventEmitter {
|
|
456
456
|
addListener(event: string, listener: (...args: any[]) => void): this;
|
|
@@ -462,7 +462,7 @@ declare namespace SplitIO {
|
|
|
462
462
|
emit(event: string, ...args: any[]): boolean;
|
|
463
463
|
}
|
|
464
464
|
/**
|
|
465
|
-
*
|
|
465
|
+
* Node.js EventEmitter interface
|
|
466
466
|
* @see {@link https://nodejs.org/api/events.html}
|
|
467
467
|
*/
|
|
468
468
|
interface EventEmitter extends IEventEmitter {
|
|
@@ -478,7 +478,7 @@ declare namespace SplitIO {
|
|
|
478
478
|
listeners(event: string | symbol): Function[];
|
|
479
479
|
rawListeners(event: string | symbol): Function[];
|
|
480
480
|
listenerCount(type: string | symbol): number;
|
|
481
|
-
// Added in Node 6...
|
|
481
|
+
// Added in Node.js 6...
|
|
482
482
|
prependListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
483
483
|
prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
484
484
|
eventNames(): Array<string | symbol>;
|
|
@@ -557,7 +557,7 @@ declare namespace SplitIO {
|
|
|
557
557
|
readonly debug: boolean | LogLevel | ILogger;
|
|
558
558
|
readonly version: string;
|
|
559
559
|
/**
|
|
560
|
-
* Mocked features map if using in client-side, or mocked features file path string if using in server-side (
|
|
560
|
+
* Mocked features map if using in client-side, or mocked features file path string if using in server-side (Node.js).
|
|
561
561
|
*/
|
|
562
562
|
features: MockedFeaturesMap | MockedFeaturesFilePath;
|
|
563
563
|
readonly streamingEnabled: boolean;
|
|
@@ -576,7 +576,7 @@ declare namespace SplitIO {
|
|
|
576
576
|
};
|
|
577
577
|
readonly impressionListener?: IImpressionListener;
|
|
578
578
|
/**
|
|
579
|
-
* User consent status if using in client-side. Undefined if using in server-side (
|
|
579
|
+
* User consent status if using in client-side. Undefined if using in server-side (Node.js).
|
|
580
580
|
*/
|
|
581
581
|
readonly userConsent?: ConsentStatus;
|
|
582
582
|
}
|
|
@@ -936,11 +936,11 @@ declare namespace SplitIO {
|
|
|
936
936
|
wrapper: Object;
|
|
937
937
|
}
|
|
938
938
|
/**
|
|
939
|
-
* Synchronous storage valid types for
|
|
939
|
+
* Synchronous storage valid types for Node.js.
|
|
940
940
|
*/
|
|
941
941
|
type NodeSyncStorage = 'MEMORY';
|
|
942
942
|
/**
|
|
943
|
-
* Asynchronous storages valid types for
|
|
943
|
+
* Asynchronous storages valid types for Node.js.
|
|
944
944
|
*/
|
|
945
945
|
type NodeAsyncStorage = 'REDIS';
|
|
946
946
|
/**
|
|
@@ -1237,14 +1237,14 @@ declare namespace SplitIO {
|
|
|
1237
1237
|
};
|
|
1238
1238
|
}
|
|
1239
1239
|
/**
|
|
1240
|
-
* Settings interface for JavaScript SDK instances created on
|
|
1240
|
+
* Settings interface for JavaScript SDK instances created on Node.js, with server-side API and synchronous in-memory storage.
|
|
1241
1241
|
* If your storage is asynchronous (Redis for example) use SplitIO.INodeAsyncSettings instead.
|
|
1242
1242
|
*
|
|
1243
1243
|
* @see {@link https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK#configuration}
|
|
1244
1244
|
*/
|
|
1245
1245
|
interface INodeSettings extends IServerSideSharedSettings, ISyncSharedSettings, INonPluggableSharedSettings {
|
|
1246
1246
|
/**
|
|
1247
|
-
* Defines which kind of storage we can instantiate on
|
|
1247
|
+
* Defines which kind of storage we can instantiate on Node.js for 'standalone' mode.
|
|
1248
1248
|
* The only possible storage type is 'MEMORY', which is the default.
|
|
1249
1249
|
*/
|
|
1250
1250
|
storage?: {
|
|
@@ -1263,7 +1263,7 @@ declare namespace SplitIO {
|
|
|
1263
1263
|
};
|
|
1264
1264
|
sync?: ISyncSharedSettings['sync'] & {
|
|
1265
1265
|
/**
|
|
1266
|
-
* Custom options object for HTTP(S) requests in
|
|
1266
|
+
* Custom options object for HTTP(S) requests in Node.js.
|
|
1267
1267
|
* If provided, this object is merged with the options object passed by the SDK for EventSource and Node-Fetch calls.
|
|
1268
1268
|
* @see {@link https://www.npmjs.com/package/node-fetch#options}
|
|
1269
1269
|
*/
|
|
@@ -1291,7 +1291,7 @@ declare namespace SplitIO {
|
|
|
1291
1291
|
*/
|
|
1292
1292
|
getHeaderOverrides?: (context: { headers: Record<string, string> }) => Record<string, string>;
|
|
1293
1293
|
/**
|
|
1294
|
-
* Custom
|
|
1294
|
+
* Custom Node.js HTTP(S) Agent used by the SDK for HTTP(S) requests.
|
|
1295
1295
|
*
|
|
1296
1296
|
* You can use it, for example, for certificate pinning or setting a network proxy:
|
|
1297
1297
|
*
|
|
@@ -1319,7 +1319,7 @@ declare namespace SplitIO {
|
|
|
1319
1319
|
};
|
|
1320
1320
|
}
|
|
1321
1321
|
/**
|
|
1322
|
-
* Settings interface for JavaScript SDK instances created on
|
|
1322
|
+
* Settings interface for JavaScript SDK instances created on Node.js, with asynchronous storage like Redis.
|
|
1323
1323
|
* If your storage is synchronous (by default we use memory, which is sync) use SplitIO.INodeSettings instead.
|
|
1324
1324
|
*
|
|
1325
1325
|
* @see {@link https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK#configuration}
|
|
@@ -1332,7 +1332,7 @@ declare namespace SplitIO {
|
|
|
1332
1332
|
*/
|
|
1333
1333
|
mode: 'consumer';
|
|
1334
1334
|
/**
|
|
1335
|
-
* Defines which kind of async storage we can instantiate on
|
|
1335
|
+
* Defines which kind of async storage we can instantiate on Node.js for 'consumer' mode.
|
|
1336
1336
|
* The only possible storage type is 'REDIS'.
|
|
1337
1337
|
*/
|
|
1338
1338
|
storage: {
|
|
@@ -1501,7 +1501,7 @@ declare namespace SplitIO {
|
|
|
1501
1501
|
}
|
|
1502
1502
|
/**
|
|
1503
1503
|
* This represents the interface for the Client instance on server-side, where the user key is not bound to the instance and must be provided on each method call.
|
|
1504
|
-
* This interface is available in
|
|
1504
|
+
* This interface is available in Node.js, or when importing the 'server' sub-package of JS SDK (e.g., `import { SplitFactory } from '@splitsoftware/splitio/server'`).
|
|
1505
1505
|
*/
|
|
1506
1506
|
interface IClient extends IBasicClient {
|
|
1507
1507
|
/**
|
|
@@ -1592,7 +1592,7 @@ declare namespace SplitIO {
|
|
|
1592
1592
|
/**
|
|
1593
1593
|
* This represents the interface for the Client instance on server-side with asynchronous storage, like REDIS.
|
|
1594
1594
|
* User key is not bound to the instance and must be provided on each method call, which returns a promise.
|
|
1595
|
-
* This interface is available in
|
|
1595
|
+
* This interface is available in Node.js, or when importing the 'server' sub-package in JS SDK (e.g., `import { SplitFactory } from '@splitsoftware/splitio/server'`).
|
|
1596
1596
|
*/
|
|
1597
1597
|
interface IAsyncClient extends IBasicClient {
|
|
1598
1598
|
/**
|