@splitsoftware/splitio-commons 1.2.1-rc.9 → 1.3.1-rc.1

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.
Files changed (80) hide show
  1. package/CHANGES.txt +18 -0
  2. package/cjs/{utils/consent.js → consent/index.js} +1 -1
  3. package/cjs/consent/sdkUserConsent.js +58 -0
  4. package/cjs/listeners/browser.js +1 -1
  5. package/cjs/logger/constants.js +3 -2
  6. package/cjs/logger/messages/info.js +1 -0
  7. package/cjs/readiness/sdkReadinessManager.js +5 -3
  8. package/cjs/sdkClient/sdkClient.js +3 -3
  9. package/cjs/sdkClient/sdkClientMethodCS.js +3 -5
  10. package/cjs/sdkClient/sdkClientMethodCSWithTT.js +3 -5
  11. package/cjs/sdkFactory/index.js +3 -2
  12. package/cjs/sync/syncManagerOnline.js +1 -1
  13. package/cjs/utils/inputValidation/attributes.js +1 -1
  14. package/cjs/utils/lang/index.js +4 -2
  15. package/cjs/utils/settingsValidation/index.js +8 -6
  16. package/esm/{utils/consent.js → consent/index.js} +1 -1
  17. package/esm/consent/sdkUserConsent.js +54 -0
  18. package/esm/listeners/browser.js +1 -1
  19. package/esm/logger/constants.js +1 -0
  20. package/esm/logger/messages/info.js +1 -0
  21. package/esm/readiness/sdkReadinessManager.js +5 -3
  22. package/esm/sdkClient/sdkClient.js +3 -3
  23. package/esm/sdkClient/sdkClientMethodCS.js +3 -5
  24. package/esm/sdkClient/sdkClientMethodCSWithTT.js +3 -5
  25. package/esm/sdkFactory/index.js +3 -2
  26. package/esm/sync/syncManagerOnline.js +1 -1
  27. package/esm/utils/inputValidation/attributes.js +1 -1
  28. package/esm/utils/lang/index.js +4 -2
  29. package/esm/utils/settingsValidation/index.js +8 -6
  30. package/package.json +14 -2
  31. package/src/{utils/consent.ts → consent/index.ts} +1 -1
  32. package/src/consent/sdkUserConsent.ts +58 -0
  33. package/src/evaluator/parser/index.ts +1 -1
  34. package/src/evaluator/types.ts +2 -2
  35. package/src/evaluator/value/index.ts +2 -2
  36. package/src/evaluator/value/sanitize.ts +2 -2
  37. package/src/listeners/browser.ts +1 -1
  38. package/src/logger/constants.ts +1 -0
  39. package/src/logger/messages/info.ts +1 -0
  40. package/src/readiness/sdkReadinessManager.ts +7 -5
  41. package/src/sdkClient/client.ts +2 -3
  42. package/src/sdkClient/sdkClient.ts +4 -4
  43. package/src/sdkClient/sdkClientMethod.ts +2 -2
  44. package/src/sdkClient/sdkClientMethodCS.ts +4 -5
  45. package/src/sdkClient/sdkClientMethodCSWithTT.ts +4 -5
  46. package/src/sdkFactory/index.ts +3 -2
  47. package/src/sdkFactory/types.ts +13 -3
  48. package/src/sync/syncManagerOnline.ts +1 -1
  49. package/src/trackers/impressionsTracker.ts +2 -2
  50. package/src/types.ts +2 -2
  51. package/src/utils/inputValidation/attributes.ts +1 -2
  52. package/src/utils/lang/index.ts +7 -3
  53. package/src/utils/settingsValidation/index.ts +8 -6
  54. package/src/utils/settingsValidation/types.ts +4 -2
  55. package/types/consent/index.d.ts +2 -0
  56. package/types/consent/sdkUserConsent.d.ts +13 -0
  57. package/types/evaluator/types.d.ts +2 -2
  58. package/types/evaluator/value/index.d.ts +1 -1
  59. package/types/evaluator/value/sanitize.d.ts +1 -1
  60. package/types/logger/constants.d.ts +1 -0
  61. package/types/sdkClient/client.d.ts +2 -2
  62. package/types/sdkClient/sdkClient.d.ts +2 -2
  63. package/types/sdkClient/sdkClientMethod.d.ts +2 -2
  64. package/types/sdkClient/sdkClientMethodCS.d.ts +2 -2
  65. package/types/sdkClient/sdkClientMethodCSWithTT.d.ts +2 -2
  66. package/types/sdkFactory/types.d.ts +12 -3
  67. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +8 -10
  68. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +19 -0
  69. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  70. package/types/sync/submitters/telemetrySyncTask.d.ts +16 -6
  71. package/types/trackers/telemetryTracker.d.ts +4 -0
  72. package/types/types.d.ts +2 -2
  73. package/types/utils/lang/index.d.ts +2 -1
  74. package/types/utils/settingsValidation/types.d.ts +4 -2
  75. package/cjs/sdkClient/types.js +0 -2
  76. package/cjs/sdkFactory/userConsentProps.js +0 -37
  77. package/esm/sdkClient/types.js +0 -1
  78. package/esm/sdkFactory/userConsentProps.js +0 -33
  79. package/src/sdkClient/types.ts +0 -21
  80. package/src/sdkFactory/userConsentProps.ts +0 -40
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.2.1-rc.9",
3
+ "version": "1.3.1-rc.1",
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",
@@ -1,5 +1,5 @@
1
1
  import { ISettings } from '../types';
2
- import { CONSENT_GRANTED } from './constants';
2
+ import { CONSENT_GRANTED } from '../utils/constants';
3
3
 
4
4
  export function isConsentGranted(settings: ISettings) {
5
5
  const userConsent = settings.userConsent;
@@ -0,0 +1,58 @@
1
+ import { ERROR_NOT_BOOLEAN, USER_CONSENT_UPDATED, USER_CONSENT_NOT_UPDATED, USER_CONSENT_INITIAL } from '../logger/constants';
2
+ import { isConsentGranted } from './index';
3
+ import { CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../utils/constants';
4
+ import { isBoolean } from '../utils/lang';
5
+ import { ISdkFactoryContext } from '../sdkFactory/types';
6
+
7
+ // User consent enum
8
+ const ConsentStatus = {
9
+ GRANTED: CONSENT_GRANTED,
10
+ DECLINED: CONSENT_DECLINED,
11
+ UNKNOWN: CONSENT_UNKNOWN,
12
+ };
13
+
14
+ /**
15
+ * The public user consent API exposed via SplitFactory, used to control if the SDK tracks and sends impressions and events or not.
16
+ */
17
+ export function createUserConsentAPI(params: ISdkFactoryContext) {
18
+ const { settings, settings: { log }, syncManager, storage: { events, impressions, impressionCounts } } = params;
19
+
20
+ if (!isConsentGranted(settings)) log.info(USER_CONSENT_INITIAL, [settings.userConsent]);
21
+
22
+ return {
23
+ setStatus(consent: unknown) {
24
+ // validate input param
25
+ if (!isBoolean(consent)) {
26
+ log.warn(ERROR_NOT_BOOLEAN, ['setUserConsent']);
27
+ return false;
28
+ }
29
+
30
+ const newConsentStatus = consent ? CONSENT_GRANTED : CONSENT_DECLINED;
31
+
32
+ if (settings.userConsent !== newConsentStatus) {
33
+ log.info(USER_CONSENT_UPDATED, [settings.userConsent, newConsentStatus]); // @ts-ignore, modify readonly prop
34
+ settings.userConsent = newConsentStatus;
35
+
36
+ if (consent) { // resumes submitters if transitioning to GRANTED
37
+ syncManager?.submitter?.start();
38
+ } else { // pauses submitters and drops tracked data if transitioning to DECLINED
39
+ syncManager?.submitter?.stop();
40
+ // @ts-ignore, clear method is present in storage for standalone and partial consumer mode
41
+ if (events.clear) events.clear(); // @ts-ignore
42
+ if (impressions.clear) impressions.clear();
43
+ if (impressionCounts) impressionCounts.clear();
44
+ }
45
+ } else {
46
+ log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
47
+ }
48
+
49
+ return true;
50
+ },
51
+
52
+ getStatus() {
53
+ return settings.userConsent;
54
+ },
55
+
56
+ Status: ConsentStatus
57
+ };
58
+ }
@@ -31,7 +31,7 @@ export function parser(log: ILogger, conditions: ISplitCondition[], storage: ISt
31
31
  const matcher = matcherFactory(log, matcherDto, storage);
32
32
 
33
33
  // Evaluator function.
34
- return (key: string, attributes: SplitIO.Attributes, splitEvaluator: ISplitEvaluator) => {
34
+ return (key: string, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator) => {
35
35
  const value = sanitizeValue(log, key, matcherDto, attributes);
36
36
  const result = value !== undefined && matcher ? matcher(value, splitEvaluator) : false;
37
37
 
@@ -6,7 +6,7 @@ import { ILogger } from '../logger/types';
6
6
 
7
7
  export interface IDependencyMatcherValue {
8
8
  key: SplitIO.SplitKey,
9
- attributes: SplitIO.Attributes
9
+ attributes?: SplitIO.Attributes
10
10
  }
11
11
 
12
12
  export interface IMatcherDto {
@@ -27,7 +27,7 @@ export interface IEvaluation {
27
27
 
28
28
  export type IEvaluationResult = IEvaluation & { treatment: string }
29
29
 
30
- export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
30
+ export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
31
31
 
32
32
  export type IEvaluator = (key: SplitIO.SplitKey, seed: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable<IEvaluation | undefined>
33
33
 
@@ -4,7 +4,7 @@ import { ILogger } from '../../logger/types';
4
4
  import { sanitize } from './sanitize';
5
5
  import { ENGINE_VALUE, ENGINE_VALUE_NO_ATTRIBUTES, ENGINE_VALUE_INVALID } from '../../logger/constants';
6
6
 
7
- function parseValue(log: ILogger, key: string, attributeName: string | null, attributes: SplitIO.Attributes) {
7
+ function parseValue(log: ILogger, key: string, attributeName: string | null, attributes?: SplitIO.Attributes) {
8
8
  let value = undefined;
9
9
  if (attributeName) {
10
10
  if (attributes) {
@@ -23,7 +23,7 @@ function parseValue(log: ILogger, key: string, attributeName: string | null, att
23
23
  /**
24
24
  * Defines value to be matched (key / attribute).
25
25
  */
26
- export function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes: SplitIO.Attributes) {
26
+ export function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes?: SplitIO.Attributes) {
27
27
  const attributeName = matcherDto.attribute;
28
28
  const valueToMatch = parseValue(log, key, attributeName, attributes);
29
29
  const sanitizedValue = sanitize(log, matcherDto.type, valueToMatch, matcherDto.dataType, attributes);
@@ -41,7 +41,7 @@ function sanitizeBoolean(val: any): boolean | undefined {
41
41
  return undefined;
42
42
  }
43
43
 
44
- function dependencyProcessor(sanitizedValue: string, attributes: SplitIO.Attributes): IDependencyMatcherValue {
44
+ function dependencyProcessor(sanitizedValue: string, attributes?: SplitIO.Attributes): IDependencyMatcherValue {
45
45
  return {
46
46
  key: sanitizedValue,
47
47
  attributes
@@ -69,7 +69,7 @@ function getProcessingFunction(matcherTypeID: number, dataType: string) {
69
69
  /**
70
70
  * Sanitize matcher value
71
71
  */
72
- export function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array<string | number> | undefined, dataType: string, attributes: SplitIO.Attributes) {
72
+ export function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array<string | number> | undefined, dataType: string, attributes?: SplitIO.Attributes) {
73
73
  const processor = getProcessingFunction(matcherTypeID, dataType);
74
74
  let sanitizedValue: string | number | boolean | Array<string | number> | IDependencyMatcherValue | undefined;
75
75
 
@@ -11,7 +11,7 @@ import { OPTIMIZED, DEBUG } from '../utils/constants';
11
11
  import { objectAssign } from '../utils/lang/objectAssign';
12
12
  import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
13
13
  import { ISyncManager } from '../sync/types';
14
- import { isConsentGranted } from '../utils/consent';
14
+ import { isConsentGranted } from '../consent';
15
15
 
16
16
  // 'unload' event is used instead of 'beforeunload', since 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
17
17
  const UNLOAD_DOM_EVENT = 'unload';
@@ -70,6 +70,7 @@ export const EVENTS_TRACKER_SUCCESS = 120;
70
70
  export const IMPRESSIONS_TRACKER_SUCCESS = 121;
71
71
  export const USER_CONSENT_UPDATED = 122;
72
72
  export const USER_CONSENT_NOT_UPDATED = 123;
73
+ export const USER_CONSENT_INITIAL = 124;
73
74
 
74
75
  export const ENGINE_VALUE_INVALID = 200;
75
76
  export const ENGINE_VALUE_NO_ATTRIBUTES = 201;
@@ -16,6 +16,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
16
16
  [c.IMPRESSIONS_TRACKER_SUCCESS, c.LOG_PREFIX_IMPRESSIONS_TRACKER + 'Successfully stored %s impression(s).'],
17
17
  [c.USER_CONSENT_UPDATED, 'setUserConsent: consent status changed from %s to %s.'],
18
18
  [c.USER_CONSENT_NOT_UPDATED, 'setUserConsent: call had no effect because it was the current consent status (%s).'],
19
+ [c.USER_CONSENT_INITIAL, 'Starting the SDK with %s user consent. No data will be sent.'],
19
20
 
20
21
  // synchronizer
21
22
  [c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
@@ -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(),
@@ -5,17 +5,16 @@ import { validateSplitExistance } from '../utils/inputValidation/splitExistance'
5
5
  import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance';
6
6
  import { SDK_NOT_READY } from '../utils/labels';
7
7
  import { CONTROL } from '../utils/constants';
8
- import { IClientFactoryParams } from './types';
9
8
  import { IEvaluationResult } from '../evaluator/types';
10
9
  import { SplitIO, ImpressionDTO } from '../types';
11
10
  import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
12
-
11
+ import { ISdkFactoryContext } from '../sdkFactory/types';
13
12
 
14
13
  /**
15
14
  * Creator of base client with getTreatments and track methods.
16
15
  */
17
16
  // @TODO missing time tracking to collect telemetry
18
- export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | SplitIO.IAsyncClient {
17
+ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
19
18
  const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker } = params;
20
19
  const { log, mode } = settings;
21
20
 
@@ -3,13 +3,13 @@ import { IStatusInterface, SplitIO } from '../types';
3
3
  import { releaseApiKey } from '../utils/inputValidation/apiKey';
4
4
  import { clientFactory } from './client';
5
5
  import { clientInputValidationDecorator } from './clientInputValidation';
6
- import { ISdkClientFactoryParams } from './types';
6
+ import { ISdkFactoryContext } from '../sdkFactory/types';
7
7
 
8
8
  /**
9
9
  * Creates an Sdk client, i.e., a base client with status and destroy interface
10
10
  */
11
- export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClient | SplitIO.IAsyncClient {
12
- const { sdkReadinessManager, syncManager, storage, signalListener, settings, sharedClient } = params;
11
+ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: boolean): SplitIO.IClient | SplitIO.IAsyncClient {
12
+ const { sdkReadinessManager, syncManager, storage, signalListener, settings } = params;
13
13
 
14
14
  return objectAssign(
15
15
  // Proto-linkage of the readiness Event Emitter
@@ -35,7 +35,7 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie
35
35
  signalListener && signalListener.stop();
36
36
 
37
37
  // Release the API Key if it is the main client
38
- if (!sharedClient) releaseApiKey(settings.core.authorizationKey);
38
+ if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
39
39
 
40
40
  // Cleanup storage
41
41
  return storage.destroy();
@@ -1,12 +1,12 @@
1
- import { ISdkClientFactoryParams } from './types';
2
1
  import { SplitIO } from '../types';
3
2
  import { sdkClientFactory } from './sdkClient';
4
3
  import { RETRIEVE_CLIENT_DEFAULT } from '../logger/constants';
4
+ import { ISdkFactoryContext } from '../sdkFactory/types';
5
5
 
6
6
  /**
7
7
  * Factory of client method for server-side SDKs (ISDK and IAsyncSDK)
8
8
  */
9
- export function sdkClientMethodFactory(params: ISdkClientFactoryParams): () => SplitIO.IClient | SplitIO.IAsyncClient {
9
+ export function sdkClientMethodFactory(params: ISdkFactoryContext): () => SplitIO.IClient | SplitIO.IAsyncClient {
10
10
  const log = params.settings.log;
11
11
  const clientInstance = sdkClientFactory(params);
12
12
 
@@ -1,5 +1,4 @@
1
1
  import { clientCSDecorator } from './clientCS';
2
- import { ISdkClientFactoryParams } from './types';
3
2
  import { SplitIO } from '../types';
4
3
  import { validateKey } from '../utils/inputValidation/key';
5
4
  import { getMatching, keyParser } from '../utils/key';
@@ -8,6 +7,7 @@ import { ISyncManagerCS } from '../sync/types';
8
7
  import { objectAssign } from '../utils/lang/objectAssign';
9
8
  import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
10
9
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10
+ import { ISdkFactoryContext } from '../sdkFactory/types';
11
11
 
12
12
  function buildInstanceId(key: SplitIO.SplitKey) {
13
13
  // @ts-ignore
@@ -20,12 +20,12 @@ const method = 'Client instantiation';
20
20
  * Factory of client method for the client-side API variant where TT is ignored and thus
21
21
  * clients don't have a binded TT for the track method.
22
22
  */
23
- export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?: SplitIO.SplitKey) => SplitIO.ICsClient {
23
+ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey) => SplitIO.ICsClient {
24
24
  const { storage, syncManager, sdkReadinessManager, settings: { core: { key }, startup: { readyTimeout }, log } } = params;
25
25
 
26
26
  const mainClientInstance = clientCSDecorator(
27
27
  log,
28
- sdkClientFactory(params) as SplitIO.IClient, // @ts-ignore
28
+ sdkClientFactory(params) as SplitIO.IClient,
29
29
  key
30
30
  );
31
31
 
@@ -76,8 +76,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
76
76
  storage: sharedStorage || storage,
77
77
  syncManager: sharedSyncManager,
78
78
  signalListener: undefined, // only the main client "destroy" method stops the signal listener
79
- sharedClient: true
80
- })) as SplitIO.IClient,
79
+ }), true) as SplitIO.IClient,
81
80
  validKey
82
81
  );
83
82
 
@@ -1,5 +1,4 @@
1
1
  import { clientCSDecorator } from './clientCS';
2
- import { ISdkClientFactoryParams } from './types';
3
2
  import { SplitIO } from '../types';
4
3
  import { validateKey } from '../utils/inputValidation/key';
5
4
  import { validateTrafficType } from '../utils/inputValidation/trafficType';
@@ -9,6 +8,7 @@ import { ISyncManagerCS } from '../sync/types';
9
8
  import { objectAssign } from '../utils/lang/objectAssign';
10
9
  import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
11
10
  import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
11
+ import { ISdkFactoryContext } from '../sdkFactory/types';
12
12
 
13
13
  function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) {
14
14
  // @ts-ignore
@@ -22,12 +22,12 @@ const method = 'Client instantiation';
22
22
  * where clients can have a binded TT for the track method, which is provided via the settings
23
23
  * (default client) or the client method (shared clients).
24
24
  */
25
- export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?: SplitIO.SplitKey, trafficType?: string) => SplitIO.ICsClient {
25
+ export function sdkClientMethodCSFactory(params: ISdkFactoryContext): (key?: SplitIO.SplitKey, trafficType?: string) => SplitIO.ICsClient {
26
26
  const { storage, syncManager, sdkReadinessManager, settings: { core: { key, trafficType }, startup: { readyTimeout }, log } } = params;
27
27
 
28
28
  const mainClientInstance = clientCSDecorator(
29
29
  log,
30
- sdkClientFactory(params) as SplitIO.IClient, // @ts-ignore
30
+ sdkClientFactory(params) as SplitIO.IClient,
31
31
  key,
32
32
  trafficType
33
33
  );
@@ -86,8 +86,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
86
86
  storage: sharedStorage || storage,
87
87
  syncManager: sharedSyncManager,
88
88
  signalListener: undefined, // only the main client "destroy" method stops the signal listener
89
- sharedClient: true
90
- })) as SplitIO.IClient,
89
+ }), true) as SplitIO.IClient,
91
90
  validKey,
92
91
  validTrafficType
93
92
  );
@@ -81,7 +81,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
81
81
  const signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
82
82
 
83
83
  // Sdk client and manager
84
- const clientMethod = sdkClientMethodFactory({ eventTracker, impressionsTracker, sdkReadinessManager, settings, storage, syncManager, signalListener });
84
+ const ctx = { eventTracker, impressionsTracker, sdkReadinessManager, settings, storage, syncManager, signalListener };
85
+ const clientMethod = sdkClientMethodFactory(ctx);
85
86
  const managerInstance = sdkManagerFactory(log, storage.splits, sdkReadinessManager);
86
87
 
87
88
  syncManager && syncManager.start();
@@ -104,5 +105,5 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
104
105
  Logger: createLoggerAPI(settings.log),
105
106
 
106
107
  settings,
107
- }, extraProps && extraProps(settings, syncManager));
108
+ }, extraProps && extraProps(ctx));
108
109
  }
@@ -2,13 +2,23 @@ import { IIntegrationManager, IIntegrationFactoryParams } from '../integrations/
2
2
  import { ISignalListener } from '../listeners/types';
3
3
  import { ILogger } from '../logger/types';
4
4
  import { ISdkReadinessManager } from '../readiness/types';
5
- import { ISdkClientFactoryParams } from '../sdkClient/types';
6
5
  import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
7
6
  import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types';
8
7
  import { ISyncManager, ISyncManagerFactoryParams } from '../sync/types';
9
8
  import { IImpressionObserver } from '../trackers/impressionObserver/types';
9
+ import { IImpressionsTracker, IEventTracker } from '../trackers/types';
10
10
  import { SplitIO, ISettings, IEventEmitter } from '../types';
11
11
 
12
+ export interface ISdkFactoryContext {
13
+ storage: IStorageSync | IStorageAsync,
14
+ sdkReadinessManager: ISdkReadinessManager,
15
+ settings: ISettings
16
+ impressionsTracker: IImpressionsTracker,
17
+ eventTracker: IEventTracker,
18
+ signalListener?: ISignalListener
19
+ syncManager?: ISyncManager,
20
+ }
21
+
12
22
  /**
13
23
  * Environment related dependencies.
14
24
  * These getters are called a fixed number of times per factory instantiation.
@@ -53,7 +63,7 @@ export interface ISdkFactoryParams {
53
63
 
54
64
  // Sdk client method factory (ISDK::client method).
55
65
  // It Allows to distinguish SDK clients with the client-side API (`ICsSDK`) or server-side API (`ISDK` or `IAsyncSDK`).
56
- sdkClientMethodFactory: (params: ISdkClientFactoryParams) => ({ (): SplitIO.ICsClient; (key: SplitIO.SplitKey, trafficType?: string | undefined): SplitIO.ICsClient; } | (() => SplitIO.IClient) | (() => SplitIO.IAsyncClient))
66
+ sdkClientMethodFactory: (params: ISdkFactoryContext) => ({ (): SplitIO.ICsClient; (key: SplitIO.SplitKey, trafficType?: string | undefined): SplitIO.ICsClient; } | (() => SplitIO.IClient) | (() => SplitIO.IAsyncClient))
57
67
 
58
68
  // Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed.
59
69
  // Pass only if `syncManager` (used by Node listener) and `splitApi` (used by Browser listener) are passed.
@@ -70,5 +80,5 @@ export interface ISdkFactoryParams {
70
80
  impressionsObserverFactory?: () => IImpressionObserver
71
81
 
72
82
  // Optional function to assign additional properties to the factory instance
73
- extraProps?: (settings: ISettings, syncManager?: ISyncManager) => object
83
+ extraProps?: (params: ISdkFactoryContext) => object
74
84
  }
@@ -6,7 +6,7 @@ import { IPushManager } from './streaming/types';
6
6
  import { IPollingManager, IPollingManagerCS } from './polling/types';
7
7
  import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants';
8
8
  import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants';
9
- import { isConsentGranted } from '../utils/consent';
9
+ import { isConsentGranted } from '../consent';
10
10
 
11
11
  /**
12
12
  * Online SyncManager factory.
@@ -74,8 +74,8 @@ 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,
77
+ ip,
78
+ hostname,
79
79
  sdkLanguageVersion: version
80
80
  };
81
81
 
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
  /**
@@ -6,7 +6,7 @@ import { ERROR_NOT_PLAIN_OBJECT } from '../../logger/constants';
6
6
 
7
7
  export function validateAttributes(log: ILogger, maybeAttrs: any, method: string): SplitIO.Attributes | undefined | false {
8
8
  // Attributes are optional
9
- if (isObject(maybeAttrs) || maybeAttrs == undefined) // eslint-disable-line eqeqeq
9
+ if (maybeAttrs == undefined || isObject(maybeAttrs)) // eslint-disable-line eqeqeq
10
10
  return maybeAttrs;
11
11
 
12
12
  log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'attributes']);
@@ -23,5 +23,4 @@ export function validateAttributesDeep(log: ILogger, maybeAttributes: Record<str
23
23
  });
24
24
 
25
25
  return result;
26
-
27
26
  }
@@ -151,10 +151,14 @@ export function isNaNNumber(val: any): boolean {
151
151
  }
152
152
 
153
153
  /**
154
- * Validates if a value is an object with the Object prototype (map object).
154
+ * Validates if a value is an object created by the Object constructor (plain object).
155
+ * It checks `constructor.name` to avoid false negatives when validating values on a separate VM context, which has its own global built-ins.
155
156
  */
156
- export function isObject(obj: any): boolean {
157
- return obj !== null && typeof obj === 'object' && obj.constructor === Object;
157
+ export function isObject(obj: any) {
158
+ return obj !== null && typeof obj === 'object' && (
159
+ obj.constructor === Object ||
160
+ (obj.constructor != null && obj.constructor.name === 'Object')
161
+ );
158
162
  }
159
163
 
160
164
  /**
@@ -99,7 +99,7 @@ function fromSecondsToMillis(n: number) {
99
99
  */
100
100
  export function settingsValidation(config: unknown, validationParams: ISettingsValidationParams) {
101
101
 
102
- const { defaults, isClientSide, runtime, storage, integrations, logger, localhost, consent } = validationParams;
102
+ const { defaults, runtime, storage, integrations, logger, localhost, consent } = validationParams;
103
103
 
104
104
  // creates a settings object merging base, defaults and config objects.
105
105
  const withDefaults = merge({}, base, defaults, config) as ISettings;
@@ -131,8 +131,8 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
131
131
  // @ts-ignore, modify readonly prop
132
132
  if (storage) withDefaults.storage = storage(withDefaults);
133
133
 
134
- // In client-side, validate key and TT
135
- if (isClientSide) {
134
+ // Validate key and TT (for client-side)
135
+ if (validationParams.acceptKey) {
136
136
  const maybeKey = withDefaults.core.key;
137
137
  // Although `key` is required in client-side, it can be omitted in LOCALHOST mode. In that case, the value `localhost_key` is used.
138
138
  if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
@@ -144,9 +144,11 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
144
144
  withDefaults.core.key = validateKey(log, maybeKey, 'Client instantiation');
145
145
  }
146
146
 
147
- const maybeTT = withDefaults.core.trafficType;
148
- if (maybeTT !== undefined) { // @ts-ignore, assigning false
149
- withDefaults.core.trafficType = validateTrafficType(log, maybeTT, 'Client instantiation');
147
+ if (validationParams.acceptTT) {
148
+ const maybeTT = withDefaults.core.trafficType;
149
+ if (maybeTT !== undefined) { // @ts-ignore
150
+ withDefaults.core.trafficType = validateTrafficType(log, maybeTT, 'Client instantiation');
151
+ }
150
152
  }
151
153
  }
152
154
 
@@ -10,8 +10,10 @@ export interface ISettingsValidationParams {
10
10
  * Version and startup properties are required, because they are not defined in the base settings.
11
11
  */
12
12
  defaults: Partial<ISettings> & { version: string } & { startup: ISettings['startup'] },
13
- /** If true, validates core.key and core.trafficType */
14
- isClientSide?: boolean,
13
+ /** If true, validates core.key */
14
+ acceptKey?: boolean,
15
+ /** If true, validates core.trafficType */
16
+ acceptTT?: boolean,
15
17
  /** Define runtime values (`settings.runtime`) */
16
18
  runtime: (settings: ISettings) => ISettings['runtime'],
17
19
  /** Storage validator (`settings.storage`) */
@@ -0,0 +1,2 @@
1
+ import { ISettings } from '../types';
2
+ export declare function isConsentGranted(settings: ISettings): boolean;
@@ -0,0 +1,13 @@
1
+ import { ISdkFactoryContext } from '../sdkFactory/types';
2
+ /**
3
+ * The public user consent API exposed via SplitFactory, used to control if the SDK tracks and sends impressions and events or not.
4
+ */
5
+ export declare function createUserConsentAPI(params: ISdkFactoryContext): {
6
+ setStatus(consent: unknown): boolean;
7
+ getStatus(): import("../types").ConsentStatus | undefined;
8
+ Status: {
9
+ GRANTED: string;
10
+ DECLINED: string;
11
+ UNKNOWN: string;
12
+ };
13
+ };
@@ -5,7 +5,7 @@ import { SplitIO } from '../types';
5
5
  import { ILogger } from '../logger/types';
6
6
  export interface IDependencyMatcherValue {
7
7
  key: SplitIO.SplitKey;
8
- attributes: SplitIO.Attributes;
8
+ attributes?: SplitIO.Attributes;
9
9
  }
10
10
  export interface IMatcherDto {
11
11
  type: number;
@@ -23,6 +23,6 @@ export interface IEvaluation {
23
23
  export declare type IEvaluationResult = IEvaluation & {
24
24
  treatment: string;
25
25
  };
26
- export declare type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>;
26
+ export declare type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>;
27
27
  export declare type IEvaluator = (key: SplitIO.SplitKey, seed: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable<IEvaluation | undefined>;
28
28
  export declare type IMatcher = (...args: any) => MaybeThenable<boolean>;
@@ -4,4 +4,4 @@ import { ILogger } from '../../logger/types';
4
4
  /**
5
5
  * Defines value to be matched (key / attribute).
6
6
  */
7
- export declare function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes: SplitIO.Attributes): string | number | boolean | (string | number)[] | import("../types").IDependencyMatcherValue | undefined;
7
+ export declare function sanitizeValue(log: ILogger, key: string, matcherDto: IMatcherDto, attributes?: SplitIO.Attributes): string | number | boolean | (string | number)[] | import("../types").IDependencyMatcherValue | undefined;
@@ -4,4 +4,4 @@ import { ILogger } from '../../logger/types';
4
4
  /**
5
5
  * Sanitize matcher value
6
6
  */
7
- export declare function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array<string | number> | undefined, dataType: string, attributes: SplitIO.Attributes): string | number | boolean | (string | number)[] | IDependencyMatcherValue | undefined;
7
+ export declare function sanitize(log: ILogger, matcherTypeID: number, value: string | number | boolean | Array<string | number> | undefined, dataType: string, attributes?: SplitIO.Attributes): string | number | boolean | (string | number)[] | IDependencyMatcherValue | undefined;
@@ -69,6 +69,7 @@ export declare const EVENTS_TRACKER_SUCCESS = 120;
69
69
  export declare const IMPRESSIONS_TRACKER_SUCCESS = 121;
70
70
  export declare const USER_CONSENT_UPDATED = 122;
71
71
  export declare const USER_CONSENT_NOT_UPDATED = 123;
72
+ export declare const USER_CONSENT_INITIAL = 124;
72
73
  export declare const ENGINE_VALUE_INVALID = 200;
73
74
  export declare const ENGINE_VALUE_NO_ATTRIBUTES = 201;
74
75
  export declare const CLIENT_NO_LISTENER = 202;
@@ -1,6 +1,6 @@
1
- import { IClientFactoryParams } from './types';
2
1
  import { SplitIO } from '../types';
2
+ import { ISdkFactoryContext } from '../sdkFactory/types';
3
3
  /**
4
4
  * Creator of base client with getTreatments and track methods.
5
5
  */
6
- export declare function clientFactory(params: IClientFactoryParams): SplitIO.IClient | SplitIO.IAsyncClient;
6
+ export declare function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient;