@splitsoftware/splitio-commons 1.15.1-rc.1 → 1.15.1-rc.3
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 -1
- package/cjs/services/splitApi.js +1 -1
- package/cjs/services/splitHttpClient.js +8 -6
- package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +2 -2
- package/cjs/sync/streaming/SSEClient/index.js +10 -7
- package/cjs/sync/streaming/pushManager.js +1 -1
- package/cjs/utils/settingsValidation/index.js +2 -0
- package/esm/services/splitApi.js +1 -1
- package/esm/services/splitHttpClient.js +8 -6
- package/esm/sync/offline/splitsParser/splitsParserFromSettings.js +3 -3
- package/esm/sync/streaming/SSEClient/index.js +10 -7
- package/esm/sync/streaming/pushManager.js +1 -1
- package/esm/utils/settingsValidation/index.js +2 -0
- package/package.json +1 -1
- package/src/sdkFactory/types.ts +6 -2
- package/src/services/splitApi.ts +2 -2
- package/src/services/splitHttpClient.ts +6 -5
- package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +4 -4
- package/src/sync/streaming/SSEClient/index.ts +11 -7
- package/src/sync/streaming/pushManager.ts +1 -1
- package/src/utils/settingsValidation/index.ts +2 -0
- package/types/sdkFactory/types.d.ts +6 -2
- package/types/services/splitApi.d.ts +1 -1
- package/types/services/splitHttpClient.d.ts +2 -2
- package/types/sync/offline/splitsParser/splitsParserFromSettings.d.ts +1 -1
- package/types/sync/streaming/SSEClient/index.d.ts +5 -3
package/CHANGES.txt
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.16.0 (June 13, 2024)
|
|
2
|
+
- 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.
|
|
3
|
+
Useful for advanced use cases like configuring a proxy or validating certificates in NodeJS.
|
|
2
4
|
- 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.
|
|
3
5
|
- 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.
|
|
6
|
+
- Bugfixing - Fixed localhost mode for client-side SDKs to emit SDK_UPDATE when mocked feature flags are updated in the `features` property of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119).
|
|
4
7
|
|
|
5
8
|
1.15.0 (May 13, 2024)
|
|
6
9
|
- Added an optional settings validation parameter to let overwrite the default flag spec version, used by the JS Synchronizer.
|
package/cjs/services/splitApi.js
CHANGED
|
@@ -21,7 +21,7 @@ function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
21
21
|
var filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
22
22
|
var SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
23
23
|
var flagSpecVersion = settings.sync.flagSpecVersion;
|
|
24
|
-
var splitHttpClient = (0, splitHttpClient_1.splitHttpClientFactory)(settings, platform
|
|
24
|
+
var splitHttpClient = (0, splitHttpClient_1.splitHttpClientFactory)(settings, platform);
|
|
25
25
|
return {
|
|
26
26
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
27
27
|
getSdkAPIHealthCheck: function () {
|
|
@@ -8,11 +8,13 @@ var messageNoFetch = 'Global fetch API is not available.';
|
|
|
8
8
|
* Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
|
|
9
9
|
*
|
|
10
10
|
* @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
|
|
11
|
-
* @param
|
|
11
|
+
* @param platform object containing environment-specific dependencies
|
|
12
12
|
*/
|
|
13
|
-
function splitHttpClientFactory(settings,
|
|
14
|
-
var
|
|
15
|
-
var
|
|
13
|
+
function splitHttpClientFactory(settings, _a) {
|
|
14
|
+
var getOptions = _a.getOptions, getFetch = _a.getFetch;
|
|
15
|
+
var log = settings.log, authorizationKey = settings.core.authorizationKey, version = settings.version, _b = settings.runtime, ip = _b.ip, hostname = _b.hostname;
|
|
16
|
+
var options = getOptions && getOptions(settings);
|
|
17
|
+
var fetch = getFetch && getFetch(settings);
|
|
16
18
|
// if fetch is not available, log Error
|
|
17
19
|
if (!fetch)
|
|
18
20
|
log.error(constants_1.ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
|
|
@@ -30,11 +32,11 @@ function splitHttpClientFactory(settings, getFetch) {
|
|
|
30
32
|
if (reqOpts === void 0) { reqOpts = {}; }
|
|
31
33
|
if (latencyTracker === void 0) { latencyTracker = function () { }; }
|
|
32
34
|
if (logErrorsAsInfo === void 0) { logErrorsAsInfo = false; }
|
|
33
|
-
var request = {
|
|
35
|
+
var request = (0, objectAssign_1.objectAssign)({
|
|
34
36
|
headers: reqOpts.headers ? (0, objectAssign_1.objectAssign)({}, headers, reqOpts.headers) : headers,
|
|
35
37
|
method: reqOpts.method || 'GET',
|
|
36
38
|
body: reqOpts.body
|
|
37
|
-
};
|
|
39
|
+
}, options);
|
|
38
40
|
// using `fetch(url, options)` signature to work with unfetch, a lightweight ponyfill of fetch API.
|
|
39
41
|
return fetch ? fetch(url, request)
|
|
40
42
|
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
|
|
@@ -19,7 +19,7 @@ function splitsParserFromSettingsFactory() {
|
|
|
19
19
|
var names = Object.keys(currentData);
|
|
20
20
|
// Different amount of items
|
|
21
21
|
if (names.length !== Object.keys(previousMock).length) {
|
|
22
|
-
previousMock = currentData;
|
|
22
|
+
previousMock = (0, lang_1.merge)({}, currentData);
|
|
23
23
|
return true;
|
|
24
24
|
}
|
|
25
25
|
return names.some(function (name) {
|
|
@@ -27,7 +27,7 @@ function splitsParserFromSettingsFactory() {
|
|
|
27
27
|
var newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
|
|
28
28
|
var changed = newSplit || newTreatment;
|
|
29
29
|
if (changed)
|
|
30
|
-
previousMock = currentData;
|
|
30
|
+
previousMock = (0, lang_1.merge)({}, currentData);
|
|
31
31
|
return changed;
|
|
32
32
|
});
|
|
33
33
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SSEClient = void 0;
|
|
4
4
|
var lang_1 = require("../../../utils/lang");
|
|
5
|
+
var objectAssign_1 = require("../../../utils/lang/objectAssign");
|
|
5
6
|
var ABLY_API_VERSION = '1.1';
|
|
6
7
|
var CONTROL_CHANNEL_REGEX = /^control_/;
|
|
7
8
|
/**
|
|
@@ -32,18 +33,20 @@ var SSEClient = /** @class */ (function () {
|
|
|
32
33
|
*
|
|
33
34
|
* @param settings Validated settings.
|
|
34
35
|
* @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
|
|
35
|
-
* @param
|
|
36
|
-
* @throws 'EventSource API is not available.
|
|
36
|
+
* @param platform object containing environment-specific dependencies
|
|
37
|
+
* @throws 'EventSource API is not available.' if EventSource is not available.
|
|
37
38
|
*/
|
|
38
|
-
function SSEClient(settings, useHeaders,
|
|
39
|
-
|
|
39
|
+
function SSEClient(settings, useHeaders, _a) {
|
|
40
|
+
var getEventSource = _a.getEventSource, getOptions = _a.getOptions;
|
|
41
|
+
this.eventSource = getEventSource && getEventSource(settings);
|
|
40
42
|
// if eventSource is not available, throw an exception
|
|
41
43
|
if (!this.eventSource)
|
|
42
|
-
throw new Error('EventSource API is not available.
|
|
44
|
+
throw new Error('EventSource API is not available.');
|
|
43
45
|
this.streamingUrl = settings.urls.streaming + '/sse';
|
|
44
46
|
// @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
|
|
45
47
|
this.useHeaders = useHeaders;
|
|
46
48
|
this.headers = buildSSEHeaders(settings);
|
|
49
|
+
this.options = getOptions && getOptions(settings);
|
|
47
50
|
}
|
|
48
51
|
SSEClient.prototype.setEventHandler = function (handler) {
|
|
49
52
|
this.handler = handler;
|
|
@@ -65,8 +68,8 @@ var SSEClient = /** @class */ (function () {
|
|
|
65
68
|
// For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
|
|
66
69
|
// because native EventSource implementations for browser doesn't support headers.
|
|
67
70
|
this.useHeaders ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
|
|
68
|
-
//
|
|
69
|
-
this.useHeaders ? { headers: this.headers } :
|
|
71
|
+
// For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
|
|
72
|
+
(0, objectAssign_1.objectAssign)(this.useHeaders ? { headers: this.headers } : {}, this.options));
|
|
70
73
|
if (this.handler) { // no need to check if SSEClient is used only by PushManager
|
|
71
74
|
this.connection.addEventListener('open', this.handler.handleOpen);
|
|
72
75
|
this.connection.addEventListener('message', this.handler.handleMessage);
|
|
@@ -32,7 +32,7 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
32
32
|
var sseClient;
|
|
33
33
|
try {
|
|
34
34
|
// `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
|
|
35
|
-
sseClient = new SSEClient_1.SSEClient(settings, userKey ? false : true, platform
|
|
35
|
+
sseClient = new SSEClient_1.SSEClient(settings, userKey ? false : true, platform);
|
|
36
36
|
}
|
|
37
37
|
catch (e) {
|
|
38
38
|
log.warn(constants_2.STREAMING_FALLBACK, [e]);
|
|
@@ -94,6 +94,8 @@ function settingsValidation(config, validationParams) {
|
|
|
94
94
|
var defaults = validationParams.defaults, runtime = validationParams.runtime, storage = validationParams.storage, integrations = validationParams.integrations, logger = validationParams.logger, localhost = validationParams.localhost, consent = validationParams.consent, flagSpec = validationParams.flagSpec;
|
|
95
95
|
// creates a settings object merging base, defaults and config objects.
|
|
96
96
|
var withDefaults = (0, lang_1.merge)({}, exports.base, defaults, config);
|
|
97
|
+
// Keeps reference to the `features` property
|
|
98
|
+
withDefaults.features = (0, lang_1.get)(config, 'features');
|
|
97
99
|
// ensure a valid logger.
|
|
98
100
|
// First thing to validate, since other validators might use the logger.
|
|
99
101
|
var log = logger(withDefaults); // @ts-ignore, modify readonly prop
|
package/esm/services/splitApi.js
CHANGED
|
@@ -18,7 +18,7 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
18
18
|
var filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
19
19
|
var SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
20
20
|
var flagSpecVersion = settings.sync.flagSpecVersion;
|
|
21
|
-
var splitHttpClient = splitHttpClientFactory(settings, platform
|
|
21
|
+
var splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
22
22
|
return {
|
|
23
23
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
24
24
|
getSdkAPIHealthCheck: function () {
|
|
@@ -5,11 +5,13 @@ var messageNoFetch = 'Global fetch API is not available.';
|
|
|
5
5
|
* Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
|
|
6
6
|
*
|
|
7
7
|
* @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
|
|
8
|
-
* @param
|
|
8
|
+
* @param platform object containing environment-specific dependencies
|
|
9
9
|
*/
|
|
10
|
-
export function splitHttpClientFactory(settings,
|
|
11
|
-
var
|
|
12
|
-
var
|
|
10
|
+
export function splitHttpClientFactory(settings, _a) {
|
|
11
|
+
var getOptions = _a.getOptions, getFetch = _a.getFetch;
|
|
12
|
+
var log = settings.log, authorizationKey = settings.core.authorizationKey, version = settings.version, _b = settings.runtime, ip = _b.ip, hostname = _b.hostname;
|
|
13
|
+
var options = getOptions && getOptions(settings);
|
|
14
|
+
var fetch = getFetch && getFetch(settings);
|
|
13
15
|
// if fetch is not available, log Error
|
|
14
16
|
if (!fetch)
|
|
15
17
|
log.error(ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
|
|
@@ -27,11 +29,11 @@ export function splitHttpClientFactory(settings, getFetch) {
|
|
|
27
29
|
if (reqOpts === void 0) { reqOpts = {}; }
|
|
28
30
|
if (latencyTracker === void 0) { latencyTracker = function () { }; }
|
|
29
31
|
if (logErrorsAsInfo === void 0) { logErrorsAsInfo = false; }
|
|
30
|
-
var request = {
|
|
32
|
+
var request = objectAssign({
|
|
31
33
|
headers: reqOpts.headers ? objectAssign({}, headers, reqOpts.headers) : headers,
|
|
32
34
|
method: reqOpts.method || 'GET',
|
|
33
35
|
body: reqOpts.body
|
|
34
|
-
};
|
|
36
|
+
}, options);
|
|
35
37
|
// using `fetch(url, options)` signature to work with unfetch, a lightweight ponyfill of fetch API.
|
|
36
38
|
return fetch ? fetch(url, request)
|
|
37
39
|
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isObject, forOwn } from '../../../utils/lang';
|
|
1
|
+
import { isObject, forOwn, merge } from '../../../utils/lang';
|
|
2
2
|
import { parseCondition } from './parseCondition';
|
|
3
3
|
function hasTreatmentChanged(prev, curr) {
|
|
4
4
|
if (typeof prev !== typeof curr)
|
|
@@ -16,7 +16,7 @@ export function splitsParserFromSettingsFactory() {
|
|
|
16
16
|
var names = Object.keys(currentData);
|
|
17
17
|
// Different amount of items
|
|
18
18
|
if (names.length !== Object.keys(previousMock).length) {
|
|
19
|
-
previousMock = currentData;
|
|
19
|
+
previousMock = merge({}, currentData);
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
22
|
return names.some(function (name) {
|
|
@@ -24,7 +24,7 @@ export function splitsParserFromSettingsFactory() {
|
|
|
24
24
|
var newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
|
|
25
25
|
var changed = newSplit || newTreatment;
|
|
26
26
|
if (changed)
|
|
27
|
-
previousMock = currentData;
|
|
27
|
+
previousMock = merge({}, currentData);
|
|
28
28
|
return changed;
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isString } from '../../../utils/lang';
|
|
2
|
+
import { objectAssign } from '../../../utils/lang/objectAssign';
|
|
2
3
|
var ABLY_API_VERSION = '1.1';
|
|
3
4
|
var CONTROL_CHANNEL_REGEX = /^control_/;
|
|
4
5
|
/**
|
|
@@ -29,18 +30,20 @@ var SSEClient = /** @class */ (function () {
|
|
|
29
30
|
*
|
|
30
31
|
* @param settings Validated settings.
|
|
31
32
|
* @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
|
|
32
|
-
* @param
|
|
33
|
-
* @throws 'EventSource API is not available.
|
|
33
|
+
* @param platform object containing environment-specific dependencies
|
|
34
|
+
* @throws 'EventSource API is not available.' if EventSource is not available.
|
|
34
35
|
*/
|
|
35
|
-
function SSEClient(settings, useHeaders,
|
|
36
|
-
|
|
36
|
+
function SSEClient(settings, useHeaders, _a) {
|
|
37
|
+
var getEventSource = _a.getEventSource, getOptions = _a.getOptions;
|
|
38
|
+
this.eventSource = getEventSource && getEventSource(settings);
|
|
37
39
|
// if eventSource is not available, throw an exception
|
|
38
40
|
if (!this.eventSource)
|
|
39
|
-
throw new Error('EventSource API is not available.
|
|
41
|
+
throw new Error('EventSource API is not available.');
|
|
40
42
|
this.streamingUrl = settings.urls.streaming + '/sse';
|
|
41
43
|
// @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
|
|
42
44
|
this.useHeaders = useHeaders;
|
|
43
45
|
this.headers = buildSSEHeaders(settings);
|
|
46
|
+
this.options = getOptions && getOptions(settings);
|
|
44
47
|
}
|
|
45
48
|
SSEClient.prototype.setEventHandler = function (handler) {
|
|
46
49
|
this.handler = handler;
|
|
@@ -62,8 +65,8 @@ var SSEClient = /** @class */ (function () {
|
|
|
62
65
|
// For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
|
|
63
66
|
// because native EventSource implementations for browser doesn't support headers.
|
|
64
67
|
this.useHeaders ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
|
|
65
|
-
//
|
|
66
|
-
this.useHeaders ? { headers: this.headers } :
|
|
68
|
+
// For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
|
|
69
|
+
objectAssign(this.useHeaders ? { headers: this.headers } : {}, this.options));
|
|
67
70
|
if (this.handler) { // no need to check if SSEClient is used only by PushManager
|
|
68
71
|
this.connection.addEventListener('open', this.handler.handleOpen);
|
|
69
72
|
this.connection.addEventListener('message', this.handler.handleMessage);
|
|
@@ -29,7 +29,7 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
29
29
|
var sseClient;
|
|
30
30
|
try {
|
|
31
31
|
// `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
|
|
32
|
-
sseClient = new SSEClient(settings, userKey ? false : true, platform
|
|
32
|
+
sseClient = new SSEClient(settings, userKey ? false : true, platform);
|
|
33
33
|
}
|
|
34
34
|
catch (e) {
|
|
35
35
|
log.warn(STREAMING_FALLBACK, [e]);
|
|
@@ -91,6 +91,8 @@ export function settingsValidation(config, validationParams) {
|
|
|
91
91
|
var defaults = validationParams.defaults, runtime = validationParams.runtime, storage = validationParams.storage, integrations = validationParams.integrations, logger = validationParams.logger, localhost = validationParams.localhost, consent = validationParams.consent, flagSpec = validationParams.flagSpec;
|
|
92
92
|
// creates a settings object merging base, defaults and config objects.
|
|
93
93
|
var withDefaults = merge({}, base, defaults, config);
|
|
94
|
+
// Keeps reference to the `features` property
|
|
95
|
+
withDefaults.features = get(config, 'features');
|
|
94
96
|
// ensure a valid logger.
|
|
95
97
|
// First thing to validate, since other validators might use the logger.
|
|
96
98
|
var log = logger(withDefaults); // @ts-ignore, modify readonly prop
|
package/package.json
CHANGED
package/src/sdkFactory/types.ts
CHANGED
|
@@ -17,11 +17,15 @@ export interface IPlatform {
|
|
|
17
17
|
/**
|
|
18
18
|
* If provided, it is used to retrieve the Fetch API for HTTP requests. Otherwise, the global fetch is used.
|
|
19
19
|
*/
|
|
20
|
-
getFetch?: () => (IFetch | undefined)
|
|
20
|
+
getFetch?: (settings: ISettings) => (IFetch | undefined)
|
|
21
|
+
/**
|
|
22
|
+
* If provided, it is used to pass additional options to fetch and eventsource calls.
|
|
23
|
+
*/
|
|
24
|
+
getOptions?: (settings: ISettings) => object
|
|
21
25
|
/**
|
|
22
26
|
* If provided, it is used to retrieve the EventSource constructor for streaming support.
|
|
23
27
|
*/
|
|
24
|
-
getEventSource?: () => (IEventSourceConstructor | undefined)
|
|
28
|
+
getEventSource?: (settings: ISettings) => (IEventSourceConstructor | undefined)
|
|
25
29
|
/**
|
|
26
30
|
* EventEmitter constructor, like NodeJS.EventEmitter or a polyfill.
|
|
27
31
|
*/
|
package/src/services/splitApi.ts
CHANGED
|
@@ -22,7 +22,7 @@ function userKeyToQueryParam(userKey: string) {
|
|
|
22
22
|
*/
|
|
23
23
|
export function splitApiFactory(
|
|
24
24
|
settings: ISettings,
|
|
25
|
-
platform:
|
|
25
|
+
platform: IPlatform,
|
|
26
26
|
telemetryTracker: ITelemetryTracker
|
|
27
27
|
): ISplitApi {
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@ export function splitApiFactory(
|
|
|
30
30
|
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
31
31
|
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
32
32
|
const flagSpecVersion = settings.sync.flagSpecVersion;
|
|
33
|
-
const splitHttpClient = splitHttpClientFactory(settings, platform
|
|
33
|
+
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
34
34
|
|
|
35
35
|
return {
|
|
36
36
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
@@ -10,12 +10,13 @@ const messageNoFetch = 'Global fetch API is not available.';
|
|
|
10
10
|
* Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
|
|
11
11
|
*
|
|
12
12
|
* @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
|
|
13
|
-
* @param
|
|
13
|
+
* @param platform object containing environment-specific dependencies
|
|
14
14
|
*/
|
|
15
|
-
export function splitHttpClientFactory(settings: ISettings, getFetch
|
|
15
|
+
export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: IPlatform): ISplitHttpClient {
|
|
16
16
|
|
|
17
17
|
const { log, core: { authorizationKey }, version, runtime: { ip, hostname } } = settings;
|
|
18
|
-
const
|
|
18
|
+
const options = getOptions && getOptions(settings);
|
|
19
|
+
const fetch = getFetch && getFetch(settings);
|
|
19
20
|
|
|
20
21
|
// if fetch is not available, log Error
|
|
21
22
|
if (!fetch) log.error(ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
|
|
@@ -32,11 +33,11 @@ export function splitHttpClientFactory(settings: ISettings, getFetch?: IPlatform
|
|
|
32
33
|
|
|
33
34
|
return function httpClient(url: string, reqOpts: IRequestOptions = {}, latencyTracker: (error?: NetworkError) => void = () => { }, logErrorsAsInfo: boolean = false): Promise<IResponse> {
|
|
34
35
|
|
|
35
|
-
const request = {
|
|
36
|
+
const request = objectAssign({
|
|
36
37
|
headers: reqOpts.headers ? objectAssign({}, headers, reqOpts.headers) : headers,
|
|
37
38
|
method: reqOpts.method || 'GET',
|
|
38
39
|
body: reqOpts.body
|
|
39
|
-
};
|
|
40
|
+
}, options);
|
|
40
41
|
|
|
41
42
|
// using `fetch(url, options)` signature to work with unfetch, a lightweight ponyfill of fetch API.
|
|
42
43
|
return fetch ? fetch(url, request)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ISplitPartial } from '../../../dtos/types';
|
|
2
2
|
import { ISettings, SplitIO } from '../../../types';
|
|
3
|
-
import { isObject, forOwn } from '../../../utils/lang';
|
|
3
|
+
import { isObject, forOwn, merge } from '../../../utils/lang';
|
|
4
4
|
import { parseCondition } from './parseCondition';
|
|
5
5
|
|
|
6
6
|
function hasTreatmentChanged(prev: string | SplitIO.TreatmentWithConfig, curr: string | SplitIO.TreatmentWithConfig) {
|
|
@@ -22,7 +22,7 @@ export function splitsParserFromSettingsFactory() {
|
|
|
22
22
|
|
|
23
23
|
// Different amount of items
|
|
24
24
|
if (names.length !== Object.keys(previousMock).length) {
|
|
25
|
-
previousMock = currentData;
|
|
25
|
+
previousMock = merge({}, currentData) as SplitIO.MockedFeaturesMap;
|
|
26
26
|
return true;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ export function splitsParserFromSettingsFactory() {
|
|
|
31
31
|
const newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
|
|
32
32
|
const changed = newSplit || newTreatment;
|
|
33
33
|
|
|
34
|
-
if (changed) previousMock = currentData;
|
|
34
|
+
if (changed) previousMock = merge({}, currentData) as SplitIO.MockedFeaturesMap;
|
|
35
35
|
|
|
36
36
|
return changed;
|
|
37
37
|
});
|
|
@@ -41,7 +41,7 @@ export function splitsParserFromSettingsFactory() {
|
|
|
41
41
|
*
|
|
42
42
|
* @param settings validated object with mocked features mapping.
|
|
43
43
|
*/
|
|
44
|
-
return function splitsParserFromSettings(settings: ISettings): false | Record<string, ISplitPartial> {
|
|
44
|
+
return function splitsParserFromSettings(settings: Pick<ISettings, 'features'>): false | Record<string, ISplitPartial> {
|
|
45
45
|
const features = settings.features as SplitIO.MockedFeaturesMap || {};
|
|
46
46
|
|
|
47
47
|
if (!mockUpdated(features)) return false;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { IPlatform } from '../../../sdkFactory/types';
|
|
1
2
|
import { IEventSourceConstructor } from '../../../services/types';
|
|
2
3
|
import { ISettings } from '../../../types';
|
|
3
4
|
import { isString } from '../../../utils/lang';
|
|
5
|
+
import { objectAssign } from '../../../utils/lang/objectAssign';
|
|
4
6
|
import { IAuthTokenPushEnabled } from '../AuthClient/types';
|
|
5
7
|
import { ISSEClient, ISseEventHandler } from './types';
|
|
6
8
|
|
|
@@ -39,24 +41,26 @@ export class SSEClient implements ISSEClient {
|
|
|
39
41
|
handler?: ISseEventHandler;
|
|
40
42
|
useHeaders?: boolean;
|
|
41
43
|
headers: Record<string, string>;
|
|
44
|
+
options?: object;
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* SSEClient constructor.
|
|
45
48
|
*
|
|
46
49
|
* @param settings Validated settings.
|
|
47
50
|
* @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
|
|
48
|
-
* @param
|
|
49
|
-
* @throws 'EventSource API is not available.
|
|
51
|
+
* @param platform object containing environment-specific dependencies
|
|
52
|
+
* @throws 'EventSource API is not available.' if EventSource is not available.
|
|
50
53
|
*/
|
|
51
|
-
constructor(settings: ISettings, useHeaders
|
|
52
|
-
this.eventSource = getEventSource && getEventSource();
|
|
54
|
+
constructor(settings: ISettings, useHeaders: boolean, { getEventSource, getOptions }: IPlatform) {
|
|
55
|
+
this.eventSource = getEventSource && getEventSource(settings);
|
|
53
56
|
// if eventSource is not available, throw an exception
|
|
54
|
-
if (!this.eventSource) throw new Error('EventSource API is not available.
|
|
57
|
+
if (!this.eventSource) throw new Error('EventSource API is not available.');
|
|
55
58
|
|
|
56
59
|
this.streamingUrl = settings.urls.streaming + '/sse';
|
|
57
60
|
// @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
|
|
58
61
|
this.useHeaders = useHeaders;
|
|
59
62
|
this.headers = buildSSEHeaders(settings);
|
|
63
|
+
this.options = getOptions && getOptions(settings);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
setEventHandler(handler: ISseEventHandler) {
|
|
@@ -84,8 +88,8 @@ export class SSEClient implements ISSEClient {
|
|
|
84
88
|
// For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
|
|
85
89
|
// because native EventSource implementations for browser doesn't support headers.
|
|
86
90
|
this.useHeaders ? url : url + `&SplitSDKVersion=${this.headers.SplitSDKVersion}&SplitSDKClientKey=${this.headers.SplitSDKClientKey}`,
|
|
87
|
-
//
|
|
88
|
-
this.useHeaders ? { headers: this.headers } :
|
|
91
|
+
// For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
|
|
92
|
+
objectAssign(this.useHeaders ? { headers: this.headers } : {}, this.options)
|
|
89
93
|
);
|
|
90
94
|
|
|
91
95
|
if (this.handler) { // no need to check if SSEClient is used only by PushManager
|
|
@@ -42,7 +42,7 @@ export function pushManagerFactory(
|
|
|
42
42
|
let sseClient: ISSEClient;
|
|
43
43
|
try {
|
|
44
44
|
// `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
|
|
45
|
-
sseClient = new SSEClient(settings, userKey ? false : true, platform
|
|
45
|
+
sseClient = new SSEClient(settings, userKey ? false : true, platform);
|
|
46
46
|
} catch (e) {
|
|
47
47
|
log.warn(STREAMING_FALLBACK, [e]);
|
|
48
48
|
return;
|
|
@@ -109,6 +109,8 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
|
|
|
109
109
|
|
|
110
110
|
// creates a settings object merging base, defaults and config objects.
|
|
111
111
|
const withDefaults = merge({}, base, defaults, config) as ISettings;
|
|
112
|
+
// Keeps reference to the `features` property
|
|
113
|
+
withDefaults.features = get(config, 'features');
|
|
112
114
|
|
|
113
115
|
// ensure a valid logger.
|
|
114
116
|
// First thing to validate, since other validators might use the logger.
|
|
@@ -16,11 +16,15 @@ export interface IPlatform {
|
|
|
16
16
|
/**
|
|
17
17
|
* If provided, it is used to retrieve the Fetch API for HTTP requests. Otherwise, the global fetch is used.
|
|
18
18
|
*/
|
|
19
|
-
getFetch?: () => (IFetch | undefined);
|
|
19
|
+
getFetch?: (settings: ISettings) => (IFetch | undefined);
|
|
20
|
+
/**
|
|
21
|
+
* If provided, it is used to pass additional options to fetch and eventsource calls.
|
|
22
|
+
*/
|
|
23
|
+
getOptions?: (settings: ISettings) => object;
|
|
20
24
|
/**
|
|
21
25
|
* If provided, it is used to retrieve the EventSource constructor for streaming support.
|
|
22
26
|
*/
|
|
23
|
-
getEventSource?: () => (IEventSourceConstructor | undefined);
|
|
27
|
+
getEventSource?: (settings: ISettings) => (IEventSourceConstructor | undefined);
|
|
24
28
|
/**
|
|
25
29
|
* EventEmitter constructor, like NodeJS.EventEmitter or a polyfill.
|
|
26
30
|
*/
|
|
@@ -9,4 +9,4 @@ import { ITelemetryTracker } from '../trackers/types';
|
|
|
9
9
|
* @param platform object containing environment-specific dependencies
|
|
10
10
|
* @param telemetryTracker telemetry tracker
|
|
11
11
|
*/
|
|
12
|
-
export declare function splitApiFactory(settings: ISettings, platform:
|
|
12
|
+
export declare function splitApiFactory(settings: ISettings, platform: IPlatform, telemetryTracker: ITelemetryTracker): ISplitApi;
|
|
@@ -5,6 +5,6 @@ import { IPlatform } from '../sdkFactory/types';
|
|
|
5
5
|
* Factory of Split HTTP clients, which are HTTP clients with predefined headers for Split endpoints.
|
|
6
6
|
*
|
|
7
7
|
* @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
|
|
8
|
-
* @param
|
|
8
|
+
* @param platform object containing environment-specific dependencies
|
|
9
9
|
*/
|
|
10
|
-
export declare function splitHttpClientFactory(settings: ISettings, getFetch
|
|
10
|
+
export declare function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: IPlatform): ISplitHttpClient;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ISplitPartial } from '../../../dtos/types';
|
|
2
2
|
import { ISettings } from '../../../types';
|
|
3
|
-
export declare function splitsParserFromSettingsFactory(): (settings: ISettings) => false | Record<string, ISplitPartial>;
|
|
3
|
+
export declare function splitsParserFromSettingsFactory(): (settings: Pick<ISettings, 'features'>) => false | Record<string, ISplitPartial>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IPlatform } from '../../../sdkFactory/types';
|
|
1
2
|
import { IEventSourceConstructor } from '../../../services/types';
|
|
2
3
|
import { ISettings } from '../../../types';
|
|
3
4
|
import { IAuthTokenPushEnabled } from '../AuthClient/types';
|
|
@@ -12,15 +13,16 @@ export declare class SSEClient implements ISSEClient {
|
|
|
12
13
|
handler?: ISseEventHandler;
|
|
13
14
|
useHeaders?: boolean;
|
|
14
15
|
headers: Record<string, string>;
|
|
16
|
+
options?: object;
|
|
15
17
|
/**
|
|
16
18
|
* SSEClient constructor.
|
|
17
19
|
*
|
|
18
20
|
* @param settings Validated settings.
|
|
19
21
|
* @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
|
|
20
|
-
* @param
|
|
21
|
-
* @throws 'EventSource API is not available.
|
|
22
|
+
* @param platform object containing environment-specific dependencies
|
|
23
|
+
* @throws 'EventSource API is not available.' if EventSource is not available.
|
|
22
24
|
*/
|
|
23
|
-
constructor(settings: ISettings, useHeaders
|
|
25
|
+
constructor(settings: ISettings, useHeaders: boolean, { getEventSource, getOptions }: IPlatform);
|
|
24
26
|
setEventHandler(handler: ISseEventHandler): void;
|
|
25
27
|
/**
|
|
26
28
|
* Open the connection with a given authToken
|