@splitsoftware/splitio-commons 3.1.1-rc.6 → 3.1.1-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGES.txt CHANGED
@@ -1,6 +1,5 @@
1
1
  3.2.0 (September 18, 2026)
2
2
  - Added support for AI configs.
3
- - Updated submitters to allow starting and executing them when the Configs SDK is ready.
4
3
  - Removed Configs SDK-related types and modules and moved them to the Configs SDK repository.
5
4
 
6
5
  3.1.0 (July 23, 2026)
@@ -61,6 +61,10 @@ function sdkLifecycleFactory(params, isSharedClient) {
61
61
  }
62
62
  // Stop background jobs
63
63
  syncManager && syncManager.stop();
64
+ // Stop the service API (the auth provider backoff retries specifically) before flushing,
65
+ // so that a pending/never-resolved auth retry (e.g. backend unreachable since startup)
66
+ // doesn't block the flush forever. Any already-cached credential is still usable within
67
+ // the flush, since `stop()` only prevents scheduling further retries.
64
68
  serviceApi && serviceApi.stop();
65
69
  return __flush().then(function () {
66
70
  // Cleanup storage
@@ -45,7 +45,7 @@ function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
45
45
  }
46
46
  return {
47
47
  credential: function () {
48
- if (cachedCredential && !isExpired(cachedCredential)) {
48
+ if (stopped || (cachedCredential && !isExpired(cachedCredential))) {
49
49
  return Promise.resolve(cachedCredential);
50
50
  }
51
51
  if (cachedCredential)
@@ -57,7 +57,8 @@ function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
57
57
  },
58
58
  stop: function () {
59
59
  stopped = true;
60
- cachedCredential = undefined;
60
+ // Keep any already-cached credential usable (e.g. by a final flush on destroy);
61
+ // just stop scheduling further retries for an in-flight/failed fetch.
61
62
  inFlightPromise = undefined;
62
63
  backoff.reset();
63
64
  }
@@ -10,18 +10,20 @@ var tslib_1 = require("tslib");
10
10
  * @param authProvider - `authProviderFactory` to use for authentication
11
11
  */
12
12
  function secureSplitHttpClientFactory(splitHttpClient, authProvider) {
13
- function makeRequest(url, options, latencyTracker, logErrorsAsInfo, token) {
14
- return splitHttpClient(url, tslib_1.__assign(tslib_1.__assign({}, options), { headers: tslib_1.__assign(tslib_1.__assign({}, options === null || options === void 0 ? void 0 : options.headers), { Authorization: "Bearer ".concat(token) }) }), latencyTracker, logErrorsAsInfo, true);
13
+ function makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, token) {
14
+ return splitHttpClient(url, token ? tslib_1.__assign(tslib_1.__assign({}, options), { headers: tslib_1.__assign(tslib_1.__assign({}, options === null || options === void 0 ? void 0 : options.headers), { Authorization: "Bearer ".concat(token) }) }) : options, latencyTracker, logErrorsAsInfo, newVersionHeader);
15
15
  }
16
- var httpClient = function (url, options, latencyTracker, logErrorsAsInfo) {
16
+ var httpClient = function (url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt) {
17
+ if (newVersionHeader === void 0) { newVersionHeader = true; }
18
+ if (useJwt === void 0) { useJwt = true; }
17
19
  return authProvider.credential().then(function (credential) {
18
- return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
20
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? credential.token : undefined)
19
21
  .catch(function (error) {
20
22
  if (error.statusCode === 401) {
21
23
  // retry once for 401, in case the token has just expired
22
24
  authProvider.invalidate();
23
25
  return authProvider.credential().then(function (newCredential) {
24
- return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
26
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? newCredential.token : undefined);
25
27
  });
26
28
  }
27
29
  throw error;
@@ -84,7 +84,7 @@ function serviceApiFactory(settings, platform, telemetryTracker) {
84
84
  */
85
85
  postEventsBulk: function (body, headers) {
86
86
  var url = "".concat(urls.events, "/api/events/bulk");
87
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.EVENTS));
87
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.EVENTS), false, false, false);
88
88
  },
89
89
  /**
90
90
  * Post impressions.
@@ -94,12 +94,12 @@ function serviceApiFactory(settings, platform, telemetryTracker) {
94
94
  */
95
95
  postTestImpressionsBulk: function (body, headers) {
96
96
  var url = "".concat(urls.events, "/api/testImpressions/bulk");
97
- return splitHttpClient(url, {
97
+ return secureSplitHttpClient(url, {
98
98
  // Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
99
99
  method: 'POST',
100
100
  body: body,
101
101
  headers: (0, objectAssign_1.objectAssign)({ SplitSDKImpressionsMode: SplitSDKImpressionsMode }, headers)
102
- }, telemetryTracker.trackHttp(constants_1.IMPRESSIONS));
102
+ }, telemetryTracker.trackHttp(constants_1.IMPRESSIONS), false, false, false);
103
103
  },
104
104
  /**
105
105
  * Post impressions counts.
@@ -109,7 +109,7 @@ function serviceApiFactory(settings, platform, telemetryTracker) {
109
109
  */
110
110
  postTestImpressionsCount: function (body, headers) {
111
111
  var url = "".concat(urls.events, "/api/testImpressions/count");
112
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.IMPRESSIONS_COUNT));
112
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.IMPRESSIONS_COUNT), false, false, false);
113
113
  },
114
114
  /**
115
115
  * Post unique keys for client side.
@@ -119,7 +119,7 @@ function serviceApiFactory(settings, platform, telemetryTracker) {
119
119
  */
120
120
  postUniqueKeysBulkCs: function (body, headers) {
121
121
  var url = "".concat(urls.telemetry, "/api/v1/keys/cs");
122
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.TELEMETRY));
122
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.TELEMETRY), false, false, false);
123
123
  },
124
124
  /**
125
125
  * Post unique keys for server side.
@@ -129,7 +129,7 @@ function serviceApiFactory(settings, platform, telemetryTracker) {
129
129
  */
130
130
  postUniqueKeysBulkSs: function (body, headers) {
131
131
  var url = "".concat(urls.telemetry, "/api/v1/keys/ss");
132
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.TELEMETRY));
132
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(constants_1.TELEMETRY), false, false, false);
133
133
  },
134
134
  postMetricsConfig: function (body, headers) {
135
135
  var url = "".concat(urls.telemetry, "/api/v1/metrics/config");
@@ -6,64 +6,41 @@ var impressionsSubmitter_1 = require("./impressionsSubmitter");
6
6
  var impressionCountsSubmitter_1 = require("./impressionCountsSubmitter");
7
7
  var telemetrySubmitter_1 = require("./telemetrySubmitter");
8
8
  var uniqueKeysSubmitter_1 = require("./uniqueKeysSubmitter");
9
- var constants_1 = require("../../readiness/constants");
10
- /**
11
- * Creates a submitter manager.
12
- *
13
- * @param startWhenReady - If true, the submitters will only start and execute the underlying submitters when the SDK is ready.
14
- * `true` for Configs SDK, `false` for Flags SDKs.
15
- */
16
- function submitterManagerFactory(startWhenReady) {
17
- return function (params) {
18
- var readiness = params.readiness;
19
- var submitters = [
20
- (0, impressionsSubmitter_1.impressionsSubmitterFactory)(params),
21
- (0, eventsSubmitter_1.eventsSubmitterFactory)(params),
22
- (0, impressionCountsSubmitter_1.impressionCountsSubmitterFactory)(params),
23
- (0, uniqueKeysSubmitter_1.uniqueKeysSubmitterFactory)(params)
24
- ];
25
- var telemetrySubmitter = (0, telemetrySubmitter_1.telemetrySubmitterFactory)(params);
26
- var isRunning = false;
27
- function startSubmitters(onlyTelemetry) {
28
- if (!isRunning)
29
- return;
9
+ function submitterManagerFactory(params) {
10
+ var submitters = [
11
+ (0, impressionsSubmitter_1.impressionsSubmitterFactory)(params),
12
+ (0, eventsSubmitter_1.eventsSubmitterFactory)(params),
13
+ (0, impressionCountsSubmitter_1.impressionCountsSubmitterFactory)(params),
14
+ (0, uniqueKeysSubmitter_1.uniqueKeysSubmitterFactory)(params)
15
+ ];
16
+ var telemetrySubmitter = (0, telemetrySubmitter_1.telemetrySubmitterFactory)(params);
17
+ return {
18
+ // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
19
+ start: function (onlyTelemetry) {
30
20
  if (!onlyTelemetry)
31
21
  submitters.forEach(function (submitter) { return submitter.start(); });
32
22
  if (telemetrySubmitter)
33
23
  telemetrySubmitter.start();
24
+ },
25
+ // `allExceptTelemetry` true if userConsent is changed to DECLINED
26
+ stop: function (allExceptTelemetry) {
27
+ submitters.forEach(function (submitter) { return submitter.stop(); });
28
+ if (!allExceptTelemetry && telemetrySubmitter)
29
+ telemetrySubmitter.stop();
30
+ },
31
+ isRunning: function () {
32
+ return submitters.some(function (submitter) { return submitter.isRunning(); });
33
+ },
34
+ // Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
35
+ execute: function (onlyTelemetry) {
36
+ var promises = onlyTelemetry ? [] : submitters.map(function (submitter) { return submitter.execute(); });
37
+ if (telemetrySubmitter)
38
+ promises.push(telemetrySubmitter.execute());
39
+ return Promise.all(promises).then(function () { });
40
+ },
41
+ isExecuting: function () {
42
+ return submitters.some(function (submitter) { return submitter.isExecuting(); });
34
43
  }
35
- return {
36
- // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
37
- start: function (onlyTelemetry) {
38
- isRunning = true;
39
- if (!startWhenReady || readiness.isReady())
40
- startSubmitters(onlyTelemetry);
41
- else
42
- readiness.gate.once(constants_1.SDK_READY, function () { return startSubmitters(onlyTelemetry); });
43
- },
44
- // `allExceptTelemetry` true if userConsent is changed to DECLINED
45
- stop: function (allExceptTelemetry) {
46
- isRunning = false;
47
- submitters.forEach(function (submitter) { return submitter.stop(); });
48
- if (!allExceptTelemetry && telemetrySubmitter)
49
- telemetrySubmitter.stop();
50
- },
51
- isRunning: function () {
52
- return isRunning;
53
- },
54
- // Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
55
- execute: function (onlyTelemetry) {
56
- if (startWhenReady && !readiness.isReady())
57
- return Promise.resolve();
58
- var promises = onlyTelemetry ? [] : submitters.map(function (submitter) { return submitter.execute(); });
59
- if (telemetrySubmitter)
60
- promises.push(telemetrySubmitter.execute());
61
- return Promise.all(promises).then(function () { });
62
- },
63
- isExecuting: function () {
64
- return submitters.some(function (submitter) { return submitter.isExecuting(); });
65
- }
66
- };
67
44
  };
68
45
  }
69
46
  exports.submitterManagerFactory = submitterManagerFactory;
@@ -15,10 +15,8 @@ var AbstractDefinitionsCacheSync_1 = require("../storages/AbstractDefinitionsCac
15
15
  * @param pollingManagerFactory - allows to specialize the SyncManager for server-side or client-side API by passing
16
16
  * `pollingManagerSSFactory` or `pollingManagerCSFactory` respectively.
17
17
  * @param pushManagerFactory - optional to build a SyncManager with or without streaming support
18
- * @param submitterManagerFactory - specify the submitter manager factory to use
19
18
  */
20
- function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory, _submitterManagerFactory) {
21
- if (_submitterManagerFactory === void 0) { _submitterManagerFactory = (0, submitterManager_1.submitterManagerFactory)(); }
19
+ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
22
20
  /**
23
21
  * SyncManager factory for modular SDK
24
22
  */
@@ -32,7 +30,7 @@ function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory, _su
32
30
  undefined;
33
31
  /** Submitter Manager */
34
32
  // It is not injected as push and polling managers, because at the moment it is required
35
- var submitterManager = _submitterManagerFactory(params);
33
+ var submitterManager = (0, submitterManager_1.submitterManagerFactory)(params);
36
34
  /** Sync Manager logic */
37
35
  function startPolling() {
38
36
  if (pollingManager.isRunning()) {
@@ -58,6 +58,10 @@ export function sdkLifecycleFactory(params, isSharedClient) {
58
58
  }
59
59
  // Stop background jobs
60
60
  syncManager && syncManager.stop();
61
+ // Stop the service API (the auth provider backoff retries specifically) before flushing,
62
+ // so that a pending/never-resolved auth retry (e.g. backend unreachable since startup)
63
+ // doesn't block the flush forever. Any already-cached credential is still usable within
64
+ // the flush, since `stop()` only prevents scheduling further retries.
61
65
  serviceApi && serviceApi.stop();
62
66
  return __flush().then(function () {
63
67
  // Cleanup storage
@@ -42,7 +42,7 @@ export function authProviderFactory(settings, splitHttpClient, telemetryTracker)
42
42
  }
43
43
  return {
44
44
  credential: function () {
45
- if (cachedCredential && !isExpired(cachedCredential)) {
45
+ if (stopped || (cachedCredential && !isExpired(cachedCredential))) {
46
46
  return Promise.resolve(cachedCredential);
47
47
  }
48
48
  if (cachedCredential)
@@ -54,7 +54,8 @@ export function authProviderFactory(settings, splitHttpClient, telemetryTracker)
54
54
  },
55
55
  stop: function () {
56
56
  stopped = true;
57
- cachedCredential = undefined;
57
+ // Keep any already-cached credential usable (e.g. by a final flush on destroy);
58
+ // just stop scheduling further retries for an in-flight/failed fetch.
58
59
  inFlightPromise = undefined;
59
60
  backoff.reset();
60
61
  }
@@ -7,18 +7,20 @@ import { __assign } from "tslib";
7
7
  * @param authProvider - `authProviderFactory` to use for authentication
8
8
  */
9
9
  export function secureSplitHttpClientFactory(splitHttpClient, authProvider) {
10
- function makeRequest(url, options, latencyTracker, logErrorsAsInfo, token) {
11
- return splitHttpClient(url, __assign(__assign({}, options), { headers: __assign(__assign({}, options === null || options === void 0 ? void 0 : options.headers), { Authorization: "Bearer ".concat(token) }) }), latencyTracker, logErrorsAsInfo, true);
10
+ function makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, token) {
11
+ return splitHttpClient(url, token ? __assign(__assign({}, options), { headers: __assign(__assign({}, options === null || options === void 0 ? void 0 : options.headers), { Authorization: "Bearer ".concat(token) }) }) : options, latencyTracker, logErrorsAsInfo, newVersionHeader);
12
12
  }
13
- var httpClient = function (url, options, latencyTracker, logErrorsAsInfo) {
13
+ var httpClient = function (url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt) {
14
+ if (newVersionHeader === void 0) { newVersionHeader = true; }
15
+ if (useJwt === void 0) { useJwt = true; }
14
16
  return authProvider.credential().then(function (credential) {
15
- return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
17
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? credential.token : undefined)
16
18
  .catch(function (error) {
17
19
  if (error.statusCode === 401) {
18
20
  // retry once for 401, in case the token has just expired
19
21
  authProvider.invalidate();
20
22
  return authProvider.credential().then(function (newCredential) {
21
- return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
23
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? newCredential.token : undefined);
22
24
  });
23
25
  }
24
26
  throw error;
@@ -81,7 +81,7 @@ export function serviceApiFactory(settings, platform, telemetryTracker) {
81
81
  */
82
82
  postEventsBulk: function (body, headers) {
83
83
  var url = "".concat(urls.events, "/api/events/bulk");
84
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(EVENTS));
84
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(EVENTS), false, false, false);
85
85
  },
86
86
  /**
87
87
  * Post impressions.
@@ -91,12 +91,12 @@ export function serviceApiFactory(settings, platform, telemetryTracker) {
91
91
  */
92
92
  postTestImpressionsBulk: function (body, headers) {
93
93
  var url = "".concat(urls.events, "/api/testImpressions/bulk");
94
- return splitHttpClient(url, {
94
+ return secureSplitHttpClient(url, {
95
95
  // Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
96
96
  method: 'POST',
97
97
  body: body,
98
98
  headers: objectAssign({ SplitSDKImpressionsMode: SplitSDKImpressionsMode }, headers)
99
- }, telemetryTracker.trackHttp(IMPRESSIONS));
99
+ }, telemetryTracker.trackHttp(IMPRESSIONS), false, false, false);
100
100
  },
101
101
  /**
102
102
  * Post impressions counts.
@@ -106,7 +106,7 @@ export function serviceApiFactory(settings, platform, telemetryTracker) {
106
106
  */
107
107
  postTestImpressionsCount: function (body, headers) {
108
108
  var url = "".concat(urls.events, "/api/testImpressions/count");
109
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
109
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT), false, false, false);
110
110
  },
111
111
  /**
112
112
  * Post unique keys for client side.
@@ -116,7 +116,7 @@ export function serviceApiFactory(settings, platform, telemetryTracker) {
116
116
  */
117
117
  postUniqueKeysBulkCs: function (body, headers) {
118
118
  var url = "".concat(urls.telemetry, "/api/v1/keys/cs");
119
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY));
119
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
120
120
  },
121
121
  /**
122
122
  * Post unique keys for server side.
@@ -126,7 +126,7 @@ export function serviceApiFactory(settings, platform, telemetryTracker) {
126
126
  */
127
127
  postUniqueKeysBulkSs: function (body, headers) {
128
128
  var url = "".concat(urls.telemetry, "/api/v1/keys/ss");
129
- return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY));
129
+ return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
130
130
  },
131
131
  postMetricsConfig: function (body, headers) {
132
132
  var url = "".concat(urls.telemetry, "/api/v1/metrics/config");
@@ -3,63 +3,40 @@ import { impressionsSubmitterFactory } from './impressionsSubmitter';
3
3
  import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
4
4
  import { telemetrySubmitterFactory } from './telemetrySubmitter';
5
5
  import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
6
- import { SDK_READY } from '../../readiness/constants';
7
- /**
8
- * Creates a submitter manager.
9
- *
10
- * @param startWhenReady - If true, the submitters will only start and execute the underlying submitters when the SDK is ready.
11
- * `true` for Configs SDK, `false` for Flags SDKs.
12
- */
13
- export function submitterManagerFactory(startWhenReady) {
14
- return function (params) {
15
- var readiness = params.readiness;
16
- var submitters = [
17
- impressionsSubmitterFactory(params),
18
- eventsSubmitterFactory(params),
19
- impressionCountsSubmitterFactory(params),
20
- uniqueKeysSubmitterFactory(params)
21
- ];
22
- var telemetrySubmitter = telemetrySubmitterFactory(params);
23
- var isRunning = false;
24
- function startSubmitters(onlyTelemetry) {
25
- if (!isRunning)
26
- return;
6
+ export function submitterManagerFactory(params) {
7
+ var submitters = [
8
+ impressionsSubmitterFactory(params),
9
+ eventsSubmitterFactory(params),
10
+ impressionCountsSubmitterFactory(params),
11
+ uniqueKeysSubmitterFactory(params)
12
+ ];
13
+ var telemetrySubmitter = telemetrySubmitterFactory(params);
14
+ return {
15
+ // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
16
+ start: function (onlyTelemetry) {
27
17
  if (!onlyTelemetry)
28
18
  submitters.forEach(function (submitter) { return submitter.start(); });
29
19
  if (telemetrySubmitter)
30
20
  telemetrySubmitter.start();
21
+ },
22
+ // `allExceptTelemetry` true if userConsent is changed to DECLINED
23
+ stop: function (allExceptTelemetry) {
24
+ submitters.forEach(function (submitter) { return submitter.stop(); });
25
+ if (!allExceptTelemetry && telemetrySubmitter)
26
+ telemetrySubmitter.stop();
27
+ },
28
+ isRunning: function () {
29
+ return submitters.some(function (submitter) { return submitter.isRunning(); });
30
+ },
31
+ // Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
32
+ execute: function (onlyTelemetry) {
33
+ var promises = onlyTelemetry ? [] : submitters.map(function (submitter) { return submitter.execute(); });
34
+ if (telemetrySubmitter)
35
+ promises.push(telemetrySubmitter.execute());
36
+ return Promise.all(promises).then(function () { });
37
+ },
38
+ isExecuting: function () {
39
+ return submitters.some(function (submitter) { return submitter.isExecuting(); });
31
40
  }
32
- return {
33
- // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
34
- start: function (onlyTelemetry) {
35
- isRunning = true;
36
- if (!startWhenReady || readiness.isReady())
37
- startSubmitters(onlyTelemetry);
38
- else
39
- readiness.gate.once(SDK_READY, function () { return startSubmitters(onlyTelemetry); });
40
- },
41
- // `allExceptTelemetry` true if userConsent is changed to DECLINED
42
- stop: function (allExceptTelemetry) {
43
- isRunning = false;
44
- submitters.forEach(function (submitter) { return submitter.stop(); });
45
- if (!allExceptTelemetry && telemetrySubmitter)
46
- telemetrySubmitter.stop();
47
- },
48
- isRunning: function () {
49
- return isRunning;
50
- },
51
- // Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
52
- execute: function (onlyTelemetry) {
53
- if (startWhenReady && !readiness.isReady())
54
- return Promise.resolve();
55
- var promises = onlyTelemetry ? [] : submitters.map(function (submitter) { return submitter.execute(); });
56
- if (telemetrySubmitter)
57
- promises.push(telemetrySubmitter.execute());
58
- return Promise.all(promises).then(function () { });
59
- },
60
- isExecuting: function () {
61
- return submitters.some(function (submitter) { return submitter.isExecuting(); });
62
- }
63
- };
64
41
  };
65
42
  }
@@ -12,10 +12,8 @@ import { usesSegmentsSync } from '../storages/AbstractDefinitionsCacheSync';
12
12
  * @param pollingManagerFactory - allows to specialize the SyncManager for server-side or client-side API by passing
13
13
  * `pollingManagerSSFactory` or `pollingManagerCSFactory` respectively.
14
14
  * @param pushManagerFactory - optional to build a SyncManager with or without streaming support
15
- * @param submitterManagerFactory - specify the submitter manager factory to use
16
15
  */
17
- export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory, _submitterManagerFactory) {
18
- if (_submitterManagerFactory === void 0) { _submitterManagerFactory = submitterManagerFactory(); }
16
+ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
19
17
  /**
20
18
  * SyncManager factory for modular SDK
21
19
  */
@@ -29,7 +27,7 @@ export function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFacto
29
27
  undefined;
30
28
  /** Submitter Manager */
31
29
  // It is not injected as push and polling managers, because at the moment it is required
32
- var submitterManager = _submitterManagerFactory(params);
30
+ var submitterManager = submitterManagerFactory(params);
33
31
  /** Sync Manager logic */
34
32
  function startPolling() {
35
33
  if (pollingManager.isRunning()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "3.1.1-rc.6",
3
+ "version": "3.1.1-rc.8",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -69,6 +69,9 @@ export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?:
69
69
 
70
70
  // Stop background jobs
71
71
  syncManager && syncManager.stop();
72
+
73
+ // Stop the service API (the auth provider backoff retries specifically) before flushing,
74
+ // so that a pending/never-resolved auth retry doesn't block the flush forever
72
75
  serviceApi && serviceApi.stop();
73
76
 
74
77
  return __flush().then(() => {
@@ -69,8 +69,8 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
69
69
 
70
70
  return {
71
71
  credential(): Promise<IJwtCredential> {
72
- if (cachedCredential && !isExpired(cachedCredential)) {
73
- return Promise.resolve(cachedCredential);
72
+ if (stopped || (cachedCredential && !isExpired(cachedCredential))) {
73
+ return Promise.resolve(cachedCredential!);
74
74
  }
75
75
 
76
76
  if (cachedCredential) log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
@@ -84,7 +84,7 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
84
84
 
85
85
  stop() {
86
86
  stopped = true;
87
- cachedCredential = undefined;
87
+ // Keep any already-cached credential usable (e.g. by a final flush on destroy)
88
88
  inFlightPromise = undefined;
89
89
  backoff.reset();
90
90
  }
@@ -10,19 +10,19 @@ import { IAuthProvider } from './authProvider';
10
10
  */
11
11
  export function secureSplitHttpClientFactory(splitHttpClient: ISplitHttpClient, authProvider: IAuthProvider): ISecureSplitHttpClient {
12
12
 
13
- function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker: ((error?: NetworkError) => void) | undefined, logErrorsAsInfo: boolean | undefined, token: string): Promise<IResponse> {
14
- return splitHttpClient(url, { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } }, latencyTracker, logErrorsAsInfo, true);
13
+ function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean, token?: string): Promise<IResponse> {
14
+ return splitHttpClient(url, token ? { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } } : options, latencyTracker, logErrorsAsInfo, newVersionHeader);
15
15
  }
16
16
 
17
- const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean): Promise<IResponse> {
17
+ const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader = true, useJwt = true): Promise<IResponse> {
18
18
  return authProvider.credential().then(credential => {
19
- return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
19
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? credential.token : undefined)
20
20
  .catch((error: NetworkError) => {
21
21
  if (error.statusCode === 401) {
22
22
  // retry once for 401, in case the token has just expired
23
23
  authProvider.invalidate();
24
24
  return authProvider.credential().then(newCredential => {
25
- return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
25
+ return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? newCredential.token : undefined);
26
26
  });
27
27
  }
28
28
  throw error;
@@ -102,7 +102,7 @@ export function serviceApiFactory(
102
102
  */
103
103
  postEventsBulk(body: string, headers?: Record<string, string>) {
104
104
  const url = `${urls.events}/api/events/bulk`;
105
- return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(EVENTS));
105
+ return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(EVENTS), false, false, false);
106
106
  },
107
107
 
108
108
  /**
@@ -113,10 +113,10 @@ export function serviceApiFactory(
113
113
  */
114
114
  postTestImpressionsBulk(body: string, headers?: Record<string, string>) {
115
115
  const url = `${urls.events}/api/testImpressions/bulk`;
116
- return splitHttpClient(url, {
116
+ return secureSplitHttpClient(url, {
117
117
  // Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
118
118
  method: 'POST', body, headers: objectAssign({ SplitSDKImpressionsMode }, headers)
119
- }, telemetryTracker.trackHttp(IMPRESSIONS));
119
+ }, telemetryTracker.trackHttp(IMPRESSIONS), false, false, false);
120
120
  },
121
121
 
122
122
  /**
@@ -127,7 +127,7 @@ export function serviceApiFactory(
127
127
  */
128
128
  postTestImpressionsCount(body: string, headers?: Record<string, string>) {
129
129
  const url = `${urls.events}/api/testImpressions/count`;
130
- return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
130
+ return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT), false, false, false);
131
131
  },
132
132
 
133
133
  /**
@@ -138,7 +138,7 @@ export function serviceApiFactory(
138
138
  */
139
139
  postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
140
140
  const url = `${urls.telemetry}/api/v1/keys/cs`;
141
- return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
141
+ return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
142
142
  },
143
143
 
144
144
  /**
@@ -149,7 +149,7 @@ export function serviceApiFactory(
149
149
  */
150
150
  postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
151
151
  const url = `${urls.telemetry}/api/v1/keys/ss`;
152
- return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
152
+ return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
153
153
  },
154
154
 
155
155
  postMetricsConfig(body: string, headers?: Record<string, string>) {
@@ -35,7 +35,7 @@ export type IHealthCheckAPI = () => Promise<boolean>
35
35
 
36
36
  export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean) => Promise<IResponse>
37
37
 
38
- export type ISecureSplitHttpClient = ISplitHttpClient & { stop(): void }
38
+ export type ISecureSplitHttpClient = ((url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean, useJwt?: boolean) => Promise<IResponse>) & { stop(): void }
39
39
 
40
40
  export type IFetchDefinitionChanges = (since: number, noCache?: boolean, till?: number, rbSince?: number) => Promise<IResponse>
41
41
 
@@ -73,7 +73,7 @@ export interface IServiceApi {
73
73
  postTestImpressionsCount: IPostTestImpressionsCount
74
74
  postMetricsConfig: IPostMetricsConfig
75
75
  postMetricsUsage: IPostMetricsUsage
76
- // lifecycle
76
+ // lifecycle: stops authProvider backoff retries
77
77
  stop(): void
78
78
  }
79
79
 
@@ -5,70 +5,44 @@ import { telemetrySubmitterFactory } from './telemetrySubmitter';
5
5
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
6
6
  import { ISubmitterManager } from './types';
7
7
  import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
8
- import { SDK_READY } from '../../readiness/constants';
9
8
 
10
- /**
11
- * Creates a submitter manager.
12
- *
13
- * @param startWhenReady - If true, the submitters will only start and execute the underlying submitters when the SDK is ready.
14
- * `true` for Configs SDK, `false` for Flags SDKs.
15
- */
16
- export function submitterManagerFactory(startWhenReady?: boolean) {
9
+ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager {
17
10
 
18
- return function (params: ISdkFactoryContextSync): ISubmitterManager {
11
+ const submitters = [
12
+ impressionsSubmitterFactory(params),
13
+ eventsSubmitterFactory(params),
14
+ impressionCountsSubmitterFactory(params),
15
+ uniqueKeysSubmitterFactory(params)
16
+ ];
19
17
 
20
- const { readiness } = params;
21
-
22
- const submitters = [
23
- impressionsSubmitterFactory(params),
24
- eventsSubmitterFactory(params),
25
- impressionCountsSubmitterFactory(params),
26
- uniqueKeysSubmitterFactory(params)
27
- ];
28
-
29
- const telemetrySubmitter = telemetrySubmitterFactory(params);
30
- let isRunning = false;
31
-
32
- function startSubmitters(onlyTelemetry?: boolean) {
33
- if (!isRunning) return;
18
+ const telemetrySubmitter = telemetrySubmitterFactory(params);
34
19
 
20
+ return {
21
+ // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
22
+ start(onlyTelemetry?: boolean) {
35
23
  if (!onlyTelemetry) submitters.forEach(submitter => submitter.start());
36
24
  if (telemetrySubmitter) telemetrySubmitter.start();
25
+ },
26
+
27
+ // `allExceptTelemetry` true if userConsent is changed to DECLINED
28
+ stop(allExceptTelemetry?: boolean) {
29
+ submitters.forEach(submitter => submitter.stop());
30
+ if (!allExceptTelemetry && telemetrySubmitter) telemetrySubmitter.stop();
31
+ },
32
+
33
+ isRunning() {
34
+ return submitters.some(submitter => submitter.isRunning());
35
+ },
36
+
37
+ // Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
38
+ execute(onlyTelemetry?: boolean) {
39
+ const promises = onlyTelemetry ? [] : submitters.map(submitter => submitter.execute());
40
+ if (telemetrySubmitter) promises.push(telemetrySubmitter.execute());
41
+ return Promise.all(promises).then(() => { });
42
+ },
43
+
44
+ isExecuting() {
45
+ return submitters.some(submitter => submitter.isExecuting());
37
46
  }
38
-
39
- return {
40
- // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
41
- start(onlyTelemetry?: boolean) {
42
- isRunning = true;
43
-
44
- if (!startWhenReady || readiness.isReady()) startSubmitters(onlyTelemetry);
45
- else readiness.gate.once(SDK_READY, () => startSubmitters(onlyTelemetry));
46
- },
47
-
48
- // `allExceptTelemetry` true if userConsent is changed to DECLINED
49
- stop(allExceptTelemetry?: boolean) {
50
- isRunning = false;
51
-
52
- submitters.forEach(submitter => submitter.stop());
53
- if (!allExceptTelemetry && telemetrySubmitter) telemetrySubmitter.stop();
54
- },
55
-
56
- isRunning() {
57
- return isRunning;
58
- },
59
-
60
- // Flush data. Called with `onlyTelemetry` true if SDK is destroyed with userConsent not GRANTED
61
- execute(onlyTelemetry?: boolean) {
62
- if (startWhenReady && !readiness.isReady()) return Promise.resolve();
63
-
64
- const promises = onlyTelemetry ? [] : submitters.map(submitter => submitter.execute());
65
- if (telemetrySubmitter) promises.push(telemetrySubmitter.execute());
66
- return Promise.all(promises).then(() => { });
67
- },
68
-
69
- isExecuting() {
70
- return submitters.some(submitter => submitter.isExecuting());
71
- }
72
- };
73
47
  };
74
48
  }
@@ -19,12 +19,10 @@ import { usesSegmentsSync } from '../storages/AbstractDefinitionsCacheSync';
19
19
  * @param pollingManagerFactory - allows to specialize the SyncManager for server-side or client-side API by passing
20
20
  * `pollingManagerSSFactory` or `pollingManagerCSFactory` respectively.
21
21
  * @param pushManagerFactory - optional to build a SyncManager with or without streaming support
22
- * @param submitterManagerFactory - specify the submitter manager factory to use
23
22
  */
24
23
  export function syncManagerOnlineFactory(
25
24
  pollingManagerFactory?: (params: ISdkFactoryContextSync) => IPollingManager,
26
25
  pushManagerFactory?: (params: ISdkFactoryContextSync, pollingManager: IPollingManager) => IPushManager | undefined,
27
- _submitterManagerFactory = submitterManagerFactory(),
28
26
  ): (params: ISdkFactoryContextSync) => ISyncManagerCS {
29
27
 
30
28
  /**
@@ -44,7 +42,7 @@ export function syncManagerOnlineFactory(
44
42
 
45
43
  /** Submitter Manager */
46
44
  // It is not injected as push and polling managers, because at the moment it is required
47
- const submitterManager = _submitterManagerFactory(params);
45
+ const submitterManager = submitterManagerFactory(params);
48
46
 
49
47
  /** Sync Manager logic */
50
48