@splitsoftware/splitio-commons 1.3.0 → 1.3.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 CHANGED
@@ -1,3 +1,6 @@
1
+ 1.3.1 (TBD)
2
+ - Bugfixing - Updated `ready` method to rejects the promise with an Error object instead of a string value (Related to issue https://github.com/splitio/javascript-client/issues/654).
3
+
1
4
  1.3.0 (April 6, 2022)
2
5
  - 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.
3
6
  - Added `scheduler.impressionsQueueSize` property to SDK configuration to limit the amount of impressions tracked in memory. Read more in our docs.
@@ -44,7 +44,7 @@ function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, internalRea
44
44
  });
45
45
  // default onRejected handler, that just logs the error, if ready promise doesn't have one.
46
46
  function defaultOnRejected(err) {
47
- log.error(err);
47
+ log.error(err && err.message);
48
48
  }
49
49
  function generateReadyPromise() {
50
50
  var promise = (0, wrapper_1.promiseWrapper)(new Promise(function (resolve, reject) {
@@ -54,7 +54,9 @@ function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, internalRea
54
54
  log.warn(constants_2.CLIENT_NO_LISTENER);
55
55
  resolve();
56
56
  });
57
- readinessManager.gate.once(constants_1.SDK_READY_TIMED_OUT, reject);
57
+ readinessManager.gate.once(constants_1.SDK_READY_TIMED_OUT, function (message) {
58
+ reject(new Error(message));
59
+ });
58
60
  }), defaultOnRejected);
59
61
  return promise;
60
62
  }
@@ -97,7 +99,7 @@ function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, internalRea
97
99
  ready: function () {
98
100
  if (readinessManager.hasTimedout()) {
99
101
  if (!readinessManager.isReady()) {
100
- return (0, wrapper_1.promiseWrapper)(Promise.reject('Split SDK has emitted SDK_READY_TIMED_OUT event.'), defaultOnRejected);
102
+ return (0, wrapper_1.promiseWrapper)(Promise.reject(new Error('Split SDK has emitted SDK_READY_TIMED_OUT event.')), defaultOnRejected);
101
103
  }
102
104
  else {
103
105
  return Promise.resolve();
@@ -12,25 +12,26 @@ var messageNoFetch = 'Global fetch API is not available.';
12
12
  * @param fetch optional http client to use instead of the global Fetch (for environments where Fetch API is not available such as Node)
13
13
  */
14
14
  function splitHttpClientFactory(settings, getFetch, getOptions) {
15
- var log = settings.log, authorizationKey = settings.core.authorizationKey, version = settings.version, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
15
+ var log = settings.log;
16
16
  var options = getOptions && getOptions();
17
17
  var fetch = getFetch && getFetch();
18
18
  // if fetch is not available, log Error
19
19
  if (!fetch)
20
20
  log.error(constants_1.ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
21
- var headers = {
22
- 'Accept': 'application/json',
23
- 'Content-Type': 'application/json',
24
- 'Authorization': "Bearer " + authorizationKey,
25
- 'SplitSDKVersion': version
26
- };
27
- if (ip)
28
- headers['SplitSDKMachineIP'] = ip;
29
- if (hostname)
30
- headers['SplitSDKMachineName'] = hostname;
31
21
  return function httpClient(url, reqOpts, logErrorsAsInfo) {
32
22
  if (reqOpts === void 0) { reqOpts = {}; }
33
23
  if (logErrorsAsInfo === void 0) { logErrorsAsInfo = false; }
24
+ var authorizationKey = settings.core.authorizationKey, version = settings.version, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
25
+ var headers = {
26
+ 'Accept': 'application/json',
27
+ 'Content-Type': 'application/json',
28
+ 'Authorization': "Bearer " + authorizationKey,
29
+ 'SplitSDKVersion': version
30
+ };
31
+ if (ip)
32
+ headers['SplitSDKMachineIP'] = ip;
33
+ if (hostname)
34
+ headers['SplitSDKMachineName'] = hostname;
34
35
  var request = (0, objectAssign_1.objectAssign)({
35
36
  headers: reqOpts.headers ? (0, objectAssign_1.objectAssign)({}, headers, reqOpts.headers) : headers,
36
37
  method: reqOpts.method || 'GET',
@@ -21,7 +21,7 @@ function impressionsTrackerFactory(settings, impressionsCache, integrationsManag
21
21
  observer,
22
22
  // if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
23
23
  countsCache) {
24
- var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
24
+ var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
25
25
  return {
26
26
  track: function (impressions, attributes) {
27
27
  if (settings.userConsent === constants_2.CONSENT_DECLINED)
@@ -62,7 +62,7 @@ countsCache) {
62
62
  attributes: attributes,
63
63
  ip: ip,
64
64
  hostname: hostname,
65
- sdkLanguageVersion: version
65
+ sdkLanguageVersion: settings.version
66
66
  };
67
67
  // Wrap in a timeout because we don't want it to be blocking.
68
68
  setTimeout(function () {
@@ -41,7 +41,7 @@ export function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, inte
41
41
  });
42
42
  // default onRejected handler, that just logs the error, if ready promise doesn't have one.
43
43
  function defaultOnRejected(err) {
44
- log.error(err);
44
+ log.error(err && err.message);
45
45
  }
46
46
  function generateReadyPromise() {
47
47
  var promise = promiseWrapper(new Promise(function (resolve, reject) {
@@ -51,7 +51,9 @@ export function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, inte
51
51
  log.warn(CLIENT_NO_LISTENER);
52
52
  resolve();
53
53
  });
54
- readinessManager.gate.once(SDK_READY_TIMED_OUT, reject);
54
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, function (message) {
55
+ reject(new Error(message));
56
+ });
55
57
  }), defaultOnRejected);
56
58
  return promise;
57
59
  }
@@ -94,7 +96,7 @@ export function sdkReadinessManagerFactory(log, EventEmitter, readyTimeout, inte
94
96
  ready: function () {
95
97
  if (readinessManager.hasTimedout()) {
96
98
  if (!readinessManager.isReady()) {
97
- return promiseWrapper(Promise.reject('Split SDK has emitted SDK_READY_TIMED_OUT event.'), defaultOnRejected);
99
+ return promiseWrapper(Promise.reject(new Error('Split SDK has emitted SDK_READY_TIMED_OUT event.')), defaultOnRejected);
98
100
  }
99
101
  else {
100
102
  return Promise.resolve();
@@ -9,25 +9,26 @@ var messageNoFetch = 'Global fetch API is not available.';
9
9
  * @param fetch optional http client to use instead of the global Fetch (for environments where Fetch API is not available such as Node)
10
10
  */
11
11
  export function splitHttpClientFactory(settings, getFetch, getOptions) {
12
- var log = settings.log, authorizationKey = settings.core.authorizationKey, version = settings.version, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
12
+ var log = settings.log;
13
13
  var options = getOptions && getOptions();
14
14
  var fetch = getFetch && getFetch();
15
15
  // if fetch is not available, log Error
16
16
  if (!fetch)
17
17
  log.error(ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
18
- var headers = {
19
- 'Accept': 'application/json',
20
- 'Content-Type': 'application/json',
21
- 'Authorization': "Bearer " + authorizationKey,
22
- 'SplitSDKVersion': version
23
- };
24
- if (ip)
25
- headers['SplitSDKMachineIP'] = ip;
26
- if (hostname)
27
- headers['SplitSDKMachineName'] = hostname;
28
18
  return function httpClient(url, reqOpts, logErrorsAsInfo) {
29
19
  if (reqOpts === void 0) { reqOpts = {}; }
30
20
  if (logErrorsAsInfo === void 0) { logErrorsAsInfo = false; }
21
+ var authorizationKey = settings.core.authorizationKey, version = settings.version, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
22
+ var headers = {
23
+ 'Accept': 'application/json',
24
+ 'Content-Type': 'application/json',
25
+ 'Authorization': "Bearer " + authorizationKey,
26
+ 'SplitSDKVersion': version
27
+ };
28
+ if (ip)
29
+ headers['SplitSDKMachineIP'] = ip;
30
+ if (hostname)
31
+ headers['SplitSDKMachineName'] = hostname;
31
32
  var request = objectAssign({
32
33
  headers: reqOpts.headers ? objectAssign({}, headers, reqOpts.headers) : headers,
33
34
  method: reqOpts.method || 'GET',
@@ -18,7 +18,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, integratio
18
18
  observer,
19
19
  // if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
20
20
  countsCache) {
21
- var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
21
+ var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname;
22
22
  return {
23
23
  track: function (impressions, attributes) {
24
24
  if (settings.userConsent === CONSENT_DECLINED)
@@ -59,7 +59,7 @@ countsCache) {
59
59
  attributes: attributes,
60
60
  ip: ip,
61
61
  hostname: hostname,
62
- sdkLanguageVersion: version
62
+ sdkLanguageVersion: settings.version
63
63
  };
64
64
  // Wrap in a timeout because we don't want it to be blocking.
65
65
  setTimeout(function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.3.0",
3
+ "version": "1.3.1-rc.0",
4
4
  "description": "Split Javascript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -46,6 +46,18 @@
46
46
  "dependencies": {
47
47
  "tslib": "^2.3.1"
48
48
  },
49
+ "peerDependencies": {
50
+ "js-yaml": "^3.13.1",
51
+ "ioredis": "^4.28.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "js-yaml": {
55
+ "optional": true
56
+ },
57
+ "ioredis": {
58
+ "optional": true
59
+ }
60
+ },
49
61
  "devDependencies": {
50
62
  "@types/google.analytics": "0.0.40",
51
63
  "@types/ioredis": "^4.28.0",
@@ -62,7 +74,7 @@
62
74
  "ioredis": "^4.28.0",
63
75
  "jest": "^27.2.3",
64
76
  "jest-localstorage-mock": "^2.4.3",
65
- "js-yaml": "^3.14.0",
77
+ "js-yaml": "^3.13.1",
66
78
  "lodash": "^4.17.21",
67
79
  "node-fetch": "^2.6.7",
68
80
  "redis-server": "1.2.2",
@@ -51,7 +51,7 @@ export function sdkReadinessManagerFactory(
51
51
 
52
52
  // default onRejected handler, that just logs the error, if ready promise doesn't have one.
53
53
  function defaultOnRejected(err: any) {
54
- log.error(err);
54
+ log.error(err && err.message);
55
55
  }
56
56
 
57
57
  function generateReadyPromise() {
@@ -62,7 +62,9 @@ export function sdkReadinessManagerFactory(
62
62
  if (readyCbCount === internalReadyCbCount && !promise.hasOnFulfilled()) log.warn(CLIENT_NO_LISTENER);
63
63
  resolve();
64
64
  });
65
- readinessManager.gate.once(SDK_READY_TIMED_OUT, reject);
65
+ readinessManager.gate.once(SDK_READY_TIMED_OUT, (message: string) => {
66
+ reject(new Error(message));
67
+ });
66
68
  }), defaultOnRejected);
67
69
 
68
70
  return promise;
@@ -106,10 +108,10 @@ export function sdkReadinessManagerFactory(
106
108
  * @function ready
107
109
  * @returns {Promise<void>}
108
110
  */
109
- ready: () => {
111
+ ready() {
110
112
  if (readinessManager.hasTimedout()) {
111
113
  if (!readinessManager.isReady()) {
112
- return promiseWrapper(Promise.reject('Split SDK has emitted SDK_READY_TIMED_OUT event.'), defaultOnRejected);
114
+ return promiseWrapper(Promise.reject(new Error('Split SDK has emitted SDK_READY_TIMED_OUT event.')), defaultOnRejected);
113
115
  } else {
114
116
  return Promise.resolve();
115
117
  }
@@ -118,7 +120,7 @@ export function sdkReadinessManagerFactory(
118
120
  },
119
121
 
120
122
  // Expose status for internal purposes only. Not considered part of the public API, and might be updated eventually.
121
- __getStatus: () => {
123
+ __getStatus() {
122
124
  return {
123
125
  isReady: readinessManager.isReady(),
124
126
  isReadyFromCache: readinessManager.isReadyFromCache(),
@@ -14,24 +14,26 @@ const messageNoFetch = 'Global fetch API is not available.';
14
14
  */
15
15
  export function splitHttpClientFactory(settings: Pick<ISettings, 'log' | 'version' | 'runtime' | 'core'>, getFetch?: () => (IFetch | undefined), getOptions?: () => object): ISplitHttpClient {
16
16
 
17
- const { log, core: { authorizationKey }, version, runtime: { ip, hostname } } = settings;
17
+ const log = settings.log;
18
18
  const options = getOptions && getOptions();
19
19
  const fetch = getFetch && getFetch();
20
20
 
21
21
  // if fetch is not available, log Error
22
22
  if (!fetch) log.error(ERROR_CLIENT_CANNOT_GET_READY, [messageNoFetch]);
23
23
 
24
- const headers: Record<string, string> = {
25
- 'Accept': 'application/json',
26
- 'Content-Type': 'application/json',
27
- 'Authorization': `Bearer ${authorizationKey}`,
28
- 'SplitSDKVersion': version
29
- };
24
+ return function httpClient(url: string, reqOpts: IRequestOptions = {}, logErrorsAsInfo: boolean = false): Promise<IResponse> {
30
25
 
31
- if (ip) headers['SplitSDKMachineIP'] = ip;
32
- if (hostname) headers['SplitSDKMachineName'] = hostname;
26
+ const { core: { authorizationKey }, version, runtime: { ip, hostname } } = settings;
33
27
 
34
- return function httpClient(url: string, reqOpts: IRequestOptions = {}, logErrorsAsInfo: boolean = false): Promise<IResponse> {
28
+ const headers: Record<string, string> = {
29
+ 'Accept': 'application/json',
30
+ 'Content-Type': 'application/json',
31
+ 'Authorization': `Bearer ${authorizationKey}`,
32
+ 'SplitSDKVersion': version
33
+ };
34
+
35
+ if (ip) headers['SplitSDKMachineIP'] = ip;
36
+ if (hostname) headers['SplitSDKMachineName'] = hostname;
35
37
 
36
38
  const request = objectAssign({
37
39
  headers: reqOpts.headers ? objectAssign({}, headers, reqOpts.headers) : headers,
@@ -28,7 +28,7 @@ export function impressionsTrackerFactory(
28
28
  countsCache?: IImpressionCountsCacheSync
29
29
  ): IImpressionsTracker {
30
30
 
31
- const { log, impressionListener, runtime: { ip, hostname }, version } = settings;
31
+ const { log, impressionListener, runtime: { ip, hostname } } = settings;
32
32
 
33
33
  return {
34
34
  track(impressions: ImpressionDTO[], attributes?: SplitIO.Attributes) {
@@ -74,9 +74,9 @@ export function impressionsTrackerFactory(
74
74
  // copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
75
75
  impression: objectAssign({}, impressions[i]),
76
76
  attributes,
77
- ip: ip as string,
78
- hostname: hostname as string,
79
- sdkLanguageVersion: version
77
+ ip,
78
+ hostname,
79
+ sdkLanguageVersion: settings.version
80
80
  };
81
81
 
82
82
  // Wrap in a timeout because we don't want it to be blocking.
package/src/types.ts CHANGED
@@ -535,8 +535,8 @@ export namespace SplitIO {
535
535
  export type ImpressionData = {
536
536
  impression: ImpressionDTO,
537
537
  attributes?: SplitIO.Attributes,
538
- ip: string,
539
- hostname: string,
538
+ ip: string| false,
539
+ hostname: string | false,
540
540
  sdkLanguageVersion: string
541
541
  };
542
542
  /**
@@ -1,4 +1,4 @@
1
- import { ImpressionDataType, EventDataType, StreamingEvent, Method, OperationType } from '../../sync/submitters/types';
1
+ import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies, StreamingEvent, Method, OperationType, MethodExceptions, MethodLatencies } from '../../sync/submitters/types';
2
2
  import { TelemetryCacheSync } from '../types';
3
3
  export declare class TelemetryCacheInMemory implements TelemetryCacheSync {
4
4
  private timeUntilReady?;
@@ -17,16 +17,14 @@ export declare class TelemetryCacheInMemory implements TelemetryCacheSync {
17
17
  getEventStats(type: EventDataType): number;
18
18
  recordEventStats(type: EventDataType, count: number): void;
19
19
  private lastSync;
20
- getLastSynchronization(): Record<OperationType, number | undefined>;
20
+ getLastSynchronization(): LastSync;
21
21
  recordSuccessfulSync(resource: OperationType, timeMs: number): void;
22
22
  private httpErrors;
23
- popHttpErrors(): Record<OperationType, {
24
- [statusCode: string]: number;
25
- }>;
26
- recordSyncError(resource: OperationType, status: number): void;
23
+ popHttpErrors(): HttpErrors;
24
+ recordHttpError(resource: OperationType, status: number): void;
27
25
  private httpLatencies;
28
- popHttpLatencies(): Record<OperationType, number[]>;
29
- recordSyncLatency(resource: OperationType, latencyMs: number): void;
26
+ popHttpLatencies(): HttpLatencies;
27
+ recordHttpLatency(resource: OperationType, latencyMs: number): void;
30
28
  private authRejections;
31
29
  popAuthRejections(): number;
32
30
  recordAuthRejections(): void;
@@ -43,9 +41,9 @@ export declare class TelemetryCacheInMemory implements TelemetryCacheSync {
43
41
  getSessionLength(): number | undefined;
44
42
  recordSessionLength(ms: number): void;
45
43
  private exceptions;
46
- popExceptions(): Record<Method, number>;
44
+ popExceptions(): MethodExceptions;
47
45
  recordException(method: Method): void;
48
46
  private latencies;
49
- popLatencies(): Record<Method, number[]>;
47
+ popLatencies(): MethodLatencies;
50
48
  recordLatency(method: Method, latencyMs: number): void;
51
49
  }
@@ -0,0 +1,21 @@
1
+ import { ILogger } from '../../logger/types';
2
+ import { Method, MethodExceptions, MethodLatencies } from '../../sync/submitters/types';
3
+ import { KeyBuilderSS } from '../KeyBuilderSS';
4
+ import { TelemetryCacheAsync } from '../types';
5
+ import { Redis } from 'ioredis';
6
+ export declare class TelemetryCacheInRedis implements TelemetryCacheAsync {
7
+ private readonly log;
8
+ private readonly keys;
9
+ private readonly redis;
10
+ /**
11
+ * Create a Telemetry cache that uses a storage wrapper.
12
+ * @param log Logger instance.
13
+ * @param keys Key builder.
14
+ * @param redis Redis client.
15
+ */
16
+ constructor(log: ILogger, keys: KeyBuilderSS, redis: Redis);
17
+ recordLatency(method: Method, latencyMs: number): Promise<number>;
18
+ recordException(method: Method): Promise<number>;
19
+ popExceptions(): Promise<MethodExceptions>;
20
+ popLatencies(): Promise<MethodLatencies>;
21
+ }
@@ -1,2 +1,20 @@
1
- export declare class TelemetryCachePluggable {
1
+ import { ILogger } from '../../logger/types';
2
+ import { Method } from '../../sync/submitters/types';
3
+ import { KeyBuilderSS } from '../KeyBuilderSS';
4
+ import { IPluggableStorageWrapper, TelemetryCacheAsync } from '../types';
5
+ export declare class TelemetryCachePluggable implements TelemetryCacheAsync {
6
+ private readonly log;
7
+ private readonly keys;
8
+ private readonly wrapper;
9
+ /**
10
+ * Create a Telemetry cache that uses a storage wrapper.
11
+ * @param log Logger instance.
12
+ * @param keys Key builder.
13
+ * @param wrapper Adapted wrapper storage.
14
+ */
15
+ constructor(log: ILogger, keys: KeyBuilderSS, wrapper: IPluggableStorageWrapper);
16
+ recordLatency(method: Method, latencyMs: number): Promise<number>;
17
+ recordException(method: Method): Promise<number>;
18
+ popExceptions(): void;
19
+ popLatencies(): void;
2
20
  }
@@ -1,17 +1,27 @@
1
1
  import { ISyncTask, ITimeTracker } from '../types';
2
- import { IPostMetricsUsage } from '../../services/types';
2
+ import { ISplitApi } from '../../services/types';
3
3
  import { IStorageSync, TelemetryCacheSync } from '../../storages/types';
4
- import { TelemetryStatsPayload } from './types';
5
- import { ILogger } from '../../logger/types';
4
+ import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload } from './types';
5
+ import { IReadinessManager } from '../../readiness/types';
6
+ import { ISettings } from '../../types';
6
7
  /**
7
8
  * Converts `impressions` data from cache into request payload.
8
9
  */
9
- export declare function telemetryCacheAdapter(telemetryCache: TelemetryCacheSync, storage: IStorageSync): {
10
+ export declare function telemetryCacheStatsAdapter({ splits, segments, telemetry }: IStorageSync & {
11
+ telemetry: TelemetryCacheSync;
12
+ }): {
10
13
  isEmpty(): boolean;
11
14
  clear(): void;
12
- state(): TelemetryStatsPayload;
15
+ state(): TelemetryUsageStatsPayload;
16
+ };
17
+ export declare function telemetryCacheConfigAdapter(settings: ISettings, telemetryCache: TelemetryCacheSync): {
18
+ isEmpty(): boolean;
19
+ clear(): void;
20
+ state(): TelemetryConfigStatsPayload;
13
21
  };
14
22
  /**
15
23
  * Sync task that periodically posts impressions data
16
24
  */
17
- export declare function telemetrySyncTaskFactory(log: ILogger, postMetricsUsage: IPostMetricsUsage, telemetryCache: TelemetryCacheSync, telemetryRefreshRate: number, latencyTracker: ITimeTracker, storage: IStorageSync): ISyncTask;
25
+ export declare function telemetrySyncTaskFactory(settings: ISettings, { postMetricsUsage, postMetricsConfig }: ISplitApi, storage: IStorageSync & {
26
+ telemetry: TelemetryCacheSync;
27
+ }, telemetryRefreshRate: number, readiness: IReadinessManager, latencyTracker?: ITimeTracker): ISyncTask;
package/types/types.d.ts CHANGED
@@ -526,8 +526,8 @@ export declare namespace SplitIO {
526
526
  type ImpressionData = {
527
527
  impression: ImpressionDTO;
528
528
  attributes?: SplitIO.Attributes;
529
- ip: string;
530
- hostname: string;
529
+ ip: string | false;
530
+ hostname: string | false;
531
531
  sdkLanguageVersion: string;
532
532
  };
533
533
  /**