@splitsoftware/splitio-commons 1.2.1-rc.4 → 1.2.1-rc.5

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 (68) hide show
  1. package/cjs/listeners/browser.js +14 -10
  2. package/cjs/logger/constants.js +5 -3
  3. package/cjs/logger/messages/error.js +2 -1
  4. package/cjs/logger/messages/info.js +1 -0
  5. package/cjs/sdkClient/client.js +10 -4
  6. package/cjs/sdkFactory/index.js +5 -4
  7. package/cjs/sdkFactory/userConsentProps.js +34 -0
  8. package/cjs/storages/KeyBuilderCS.js +11 -1
  9. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -3
  10. package/cjs/sync/submitters/eventsSyncTask.js +6 -2
  11. package/cjs/sync/submitters/impressionsSyncTask.js +6 -2
  12. package/cjs/sync/syncManagerOnline.js +11 -7
  13. package/cjs/utils/consent.js +10 -0
  14. package/cjs/utils/constants/index.js +5 -1
  15. package/cjs/utils/settingsValidation/consent.js +16 -0
  16. package/cjs/utils/settingsValidation/impressionsMode.js +6 -6
  17. package/cjs/utils/settingsValidation/index.js +4 -1
  18. package/esm/listeners/browser.js +14 -10
  19. package/esm/logger/constants.js +3 -1
  20. package/esm/logger/messages/error.js +2 -1
  21. package/esm/logger/messages/info.js +1 -0
  22. package/esm/sdkClient/client.js +11 -5
  23. package/esm/sdkFactory/index.js +5 -4
  24. package/esm/sdkFactory/userConsentProps.js +30 -0
  25. package/esm/storages/KeyBuilderCS.js +11 -1
  26. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +23 -3
  27. package/esm/sync/submitters/eventsSyncTask.js +6 -2
  28. package/esm/sync/submitters/impressionsSyncTask.js +6 -2
  29. package/esm/sync/syncManagerOnline.js +11 -7
  30. package/esm/utils/consent.js +6 -0
  31. package/esm/utils/constants/index.js +4 -0
  32. package/esm/utils/settingsValidation/consent.js +12 -0
  33. package/esm/utils/settingsValidation/impressionsMode.js +7 -7
  34. package/esm/utils/settingsValidation/index.js +4 -1
  35. package/package.json +1 -1
  36. package/src/listeners/browser.ts +13 -9
  37. package/src/logger/constants.ts +3 -1
  38. package/src/logger/messages/error.ts +2 -1
  39. package/src/logger/messages/info.ts +1 -0
  40. package/src/sdkClient/client.ts +7 -5
  41. package/src/sdkFactory/index.ts +5 -4
  42. package/src/sdkFactory/types.ts +2 -0
  43. package/src/sdkFactory/userConsentProps.ts +37 -0
  44. package/src/storages/KeyBuilderCS.ts +13 -1
  45. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +23 -3
  46. package/src/sync/submitters/eventsSyncTask.ts +6 -2
  47. package/src/sync/submitters/impressionsSyncTask.ts +6 -2
  48. package/src/sync/syncManagerOnline.ts +13 -7
  49. package/src/sync/types.ts +4 -1
  50. package/src/types.ts +6 -0
  51. package/src/utils/consent.ts +8 -0
  52. package/src/utils/constants/index.ts +5 -0
  53. package/src/utils/settingsValidation/consent.ts +14 -0
  54. package/src/utils/settingsValidation/impressionsMode.ts +7 -8
  55. package/src/utils/settingsValidation/index.ts +5 -1
  56. package/src/utils/settingsValidation/types.ts +2 -0
  57. package/types/logger/constants.d.ts +3 -1
  58. package/types/sdkFactory/types.d.ts +1 -0
  59. package/types/sdkFactory/userConsentProps.d.ts +6 -0
  60. package/types/storages/KeyBuilderCS.d.ts +2 -0
  61. package/types/sync/types.d.ts +3 -0
  62. package/types/types.d.ts +6 -0
  63. package/types/utils/consent.d.ts +2 -0
  64. package/types/utils/constants/index.d.ts +3 -0
  65. package/types/utils/settingsValidation/consent.d.ts +5 -0
  66. package/types/utils/settingsValidation/impressionsMode.d.ts +1 -1
  67. package/types/utils/settingsValidation/types.d.ts +2 -0
  68. package/types/utils/settingsValidation/userConsent.d.ts +5 -0
@@ -6,6 +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
10
 
10
11
  /**
11
12
  * Online SyncManager factory.
@@ -25,7 +26,7 @@ export function syncManagerOnlineFactory(
25
26
  */
26
27
  return function (params: ISyncManagerFactoryParams): ISyncManagerCS {
27
28
 
28
- const { log, streamingEnabled } = params.settings;
29
+ const { settings, settings: { log, streamingEnabled } } = params;
29
30
 
30
31
  /** Polling Manager */
31
32
  const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
@@ -39,7 +40,6 @@ export function syncManagerOnlineFactory(
39
40
  // It is not inyected as push and polling managers, because at the moment it is required
40
41
  const submitter = submitterManagerFactory(params);
41
42
 
42
-
43
43
  /** Sync Manager logic */
44
44
 
45
45
  function startPolling() {
@@ -69,12 +69,18 @@ export function syncManagerOnlineFactory(
69
69
  let startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization
70
70
 
71
71
  return {
72
+ // Exposed for fine-grained control of synchronization.
73
+ // E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline).
74
+ pollingManager,
72
75
  pushManager,
76
+ submitter,
73
77
 
74
78
  /**
75
79
  * Method used to start the syncManager for the first time, or resume it after being stopped.
76
80
  */
77
81
  start() {
82
+ running = true;
83
+
78
84
  // start syncing splits and segments
79
85
  if (pollingManager) {
80
86
  if (pushManager) {
@@ -90,21 +96,21 @@ export function syncManagerOnlineFactory(
90
96
  }
91
97
 
92
98
  // start periodic data recording (events, impressions, telemetry).
93
- if (submitter) submitter.start();
94
- running = true;
99
+ if (isConsentGranted(settings)) submitter.start();
95
100
  },
96
101
 
97
102
  /**
98
103
  * Method used to stop/pause the syncManager.
99
104
  */
100
105
  stop() {
106
+ running = false;
107
+
101
108
  // stop syncing
102
109
  if (pushManager) pushManager.stop();
103
110
  if (pollingManager && pollingManager.isRunning()) pollingManager.stop();
104
111
 
105
112
  // stop periodic data recording (events, impressions, telemetry).
106
- if (submitter) submitter.stop();
107
- running = false;
113
+ submitter.stop();
108
114
  },
109
115
 
110
116
  isRunning() {
@@ -112,7 +118,7 @@ export function syncManagerOnlineFactory(
112
118
  },
113
119
 
114
120
  flush() {
115
- if (submitter) return submitter.execute();
121
+ if (isConsentGranted(settings)) return submitter.execute();
116
122
  else return Promise.resolve();
117
123
  },
118
124
 
package/src/sync/types.ts CHANGED
@@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types';
3
3
  import { ISplitApi } from '../services/types';
4
4
  import { IStorageSync } from '../storages/types';
5
5
  import { ISettings } from '../types';
6
+ import { IPollingManager } from './polling/types';
6
7
  import { IPushManager } from './streaming/types';
7
8
 
8
9
  export interface ITask<Input extends any[] = []> {
@@ -43,7 +44,9 @@ export interface ITimeTracker {
43
44
 
44
45
  export interface ISyncManager extends ITask {
45
46
  flush(): Promise<any>,
46
- pushManager?: IPushManager
47
+ pushManager?: IPushManager,
48
+ pollingManager?: IPollingManager,
49
+ submitter?: ISyncTask
47
50
  }
48
51
 
49
52
  export interface ISyncManagerCS extends ISyncManager {
package/src/types.ts CHANGED
@@ -54,6 +54,11 @@ type EventConsts = {
54
54
  * @typedef {string} SDKMode
55
55
  */
56
56
  export type SDKMode = 'standalone' | 'consumer' | 'localhost' | 'consumer_partial';
57
+ /**
58
+ * User consent status.
59
+ * @typedef {string} ConsentStatus
60
+ */
61
+ export type ConsentStatus = 'GRANTED' | 'DECLINED' | 'UNKNOWN';
57
62
  /**
58
63
  * Settings interface. This is a representation of the settings the SDK expose, that's why
59
64
  * most of it's props are readonly. Only features should be rewritten when localhost mode is active.
@@ -111,6 +116,7 @@ export interface ISettings {
111
116
  },
112
117
  readonly log: ILogger
113
118
  readonly impressionListener?: unknown
119
+ readonly userConsent?: ConsentStatus
114
120
  }
115
121
  /**
116
122
  * Log levels.
@@ -0,0 +1,8 @@
1
+ import { ISettings } from '../types';
2
+ import { CONSENT_GRANTED } from './constants';
3
+
4
+ export function isConsentGranted(settings: ISettings) {
5
+ const userConsent = settings.userConsent;
6
+ // undefined userConsent is handled as granted (default)
7
+ return !userConsent || userConsent === CONSENT_GRANTED;
8
+ }
@@ -32,3 +32,8 @@ export const STORAGE_MEMORY: StorageType = 'MEMORY';
32
32
  export const STORAGE_LOCALSTORAGE: StorageType = 'LOCALSTORAGE';
33
33
  export const STORAGE_REDIS: StorageType = 'REDIS';
34
34
  export const STORAGE_PLUGGABLE: StorageType = 'PLUGGABLE';
35
+
36
+ // User consent
37
+ export const CONSENT_GRANTED = 'GRANTED'; // The user has granted consent for tracking events and impressions
38
+ export const CONSENT_DECLINED = 'DECLINED'; // The user has declined consent for tracking events and impressions
39
+ export const CONSENT_UNKNOWN = 'UNKNOWN'; // The user has neither granted nor declined consent for tracking events and impressions
@@ -0,0 +1,14 @@
1
+ import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants';
2
+ import { ILogger } from '../../logger/types';
3
+ import { CONSENT_DECLINED, CONSENT_GRANTED, CONSENT_UNKNOWN } from '../constants';
4
+
5
+ const userConsentValues = [CONSENT_DECLINED, CONSENT_GRANTED, CONSENT_UNKNOWN];
6
+
7
+ export function validateConsent({ userConsent, log }: { userConsent: any, log: ILogger }) {
8
+ if (typeof userConsent === 'string') userConsent = userConsent.toUpperCase();
9
+
10
+ if (userConsentValues.indexOf(userConsent) > -1) return userConsent;
11
+
12
+ log.error(ERROR_INVALID_CONFIG_PARAM, ['userConsent', userConsentValues, CONSENT_GRANTED]);
13
+ return CONSENT_GRANTED;
14
+ }
@@ -1,14 +1,13 @@
1
- import { ERROR_INVALID_IMPRESSIONS_MODE } from '../../logger/constants';
1
+ import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants';
2
2
  import { ILogger } from '../../logger/types';
3
3
  import { SplitIO } from '../../types';
4
4
  import { DEBUG, OPTIMIZED } from '../constants';
5
5
 
6
- export function validImpressionsMode(log: ILogger, impressionsMode: string): SplitIO.ImpressionsMode {
7
- impressionsMode = impressionsMode.toUpperCase();
8
- if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) === -1) {
9
- log.error(ERROR_INVALID_IMPRESSIONS_MODE, [[DEBUG, OPTIMIZED], OPTIMIZED]);
10
- impressionsMode = OPTIMIZED;
11
- }
6
+ export function validImpressionsMode(log: ILogger, impressionsMode: any): SplitIO.ImpressionsMode {
7
+ if (typeof impressionsMode === 'string') impressionsMode = impressionsMode.toUpperCase();
12
8
 
13
- return impressionsMode as SplitIO.ImpressionsMode;
9
+ if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) > -1) return impressionsMode;
10
+
11
+ log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED], OPTIMIZED]);
12
+ return OPTIMIZED;
14
13
  }
@@ -97,7 +97,7 @@ function fromSecondsToMillis(n: number) {
97
97
  */
98
98
  export function settingsValidation(config: unknown, validationParams: ISettingsValidationParams) {
99
99
 
100
- const { defaults, runtime, storage, integrations, logger, localhost } = validationParams;
100
+ const { defaults, runtime, storage, integrations, logger, localhost, consent } = validationParams;
101
101
 
102
102
  // creates a settings object merging base, defaults and config objects.
103
103
  const withDefaults = merge({}, base, defaults, config) as ISettings;
@@ -161,5 +161,9 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
161
161
  // ensure a valid impressionsMode
162
162
  withDefaults.sync.impressionsMode = validImpressionsMode(log, withDefaults.sync.impressionsMode);
163
163
 
164
+ // ensure a valid user consent value
165
+ // @ts-ignore, modify readonly prop
166
+ withDefaults.userConsent = consent(withDefaults);
167
+
164
168
  return withDefaults;
165
169
  }
@@ -20,4 +20,6 @@ export interface ISettingsValidationParams {
20
20
  logger: (settings: ISettings) => ISettings['log'],
21
21
  /** Localhost mode validator (`settings.sync.localhostMode`) */
22
22
  localhost?: (settings: ISettings) => ISettings['sync']['localhostMode'],
23
+ /** User consent validator (`settings.userConsent`) */
24
+ consent: (settings: ISettings) => ISettings['userConsent'],
23
25
  }
@@ -67,6 +67,7 @@ export declare const SYNC_CONTINUE_POLLING = 118;
67
67
  export declare const SYNC_STOP_POLLING = 119;
68
68
  export declare const EVENTS_TRACKER_SUCCESS = 120;
69
69
  export declare const IMPRESSIONS_TRACKER_SUCCESS = 121;
70
+ export declare const USER_CONSENT_UPDATED = 122;
70
71
  export declare const ENGINE_VALUE_INVALID = 200;
71
72
  export declare const ENGINE_VALUE_NO_ATTRIBUTES = 201;
72
73
  export declare const CLIENT_NO_LISTENER = 202;
@@ -112,10 +113,11 @@ export declare const ERROR_INVALID_KEY_OBJECT = 317;
112
113
  export declare const ERROR_INVALID = 318;
113
114
  export declare const ERROR_EMPTY = 319;
114
115
  export declare const ERROR_EMPTY_ARRAY = 320;
115
- export declare const ERROR_INVALID_IMPRESSIONS_MODE = 321;
116
+ export declare const ERROR_INVALID_CONFIG_PARAM = 321;
116
117
  export declare const ERROR_HTTP = 322;
117
118
  export declare const ERROR_LOCALHOST_MODULE_REQUIRED = 323;
118
119
  export declare const ERROR_STORAGE_INVALID = 324;
120
+ export declare const ERROR_NOT_BOOLEAN = 325;
119
121
  export declare const LOG_PREFIX_SETTINGS = "settings";
120
122
  export declare const LOG_PREFIX_INSTANTIATION = "Factory instantiation";
121
123
  export declare const LOG_PREFIX_ENGINE = "engine";
@@ -39,4 +39,5 @@ export interface ISdkFactoryParams {
39
39
  impressionListener?: SplitIO.IImpressionListener;
40
40
  integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined;
41
41
  impressionsObserverFactory?: () => IImpressionObserver;
42
+ extraProps?: (settings: ISettings, syncManager?: ISyncManager) => object;
42
43
  }
@@ -0,0 +1,6 @@
1
+ import { ISyncManager } from '../sync/types';
2
+ import { ISettings } from '../types';
3
+ export declare function userConsentProps(settings: ISettings, syncManager?: ISyncManager): {
4
+ setUserConsent(consent: unknown): boolean;
5
+ getUserConsent(): import("../types").ConsentStatus | undefined;
6
+ };
@@ -8,6 +8,8 @@ export declare class KeyBuilderCS extends KeyBuilder {
8
8
  */
9
9
  buildSegmentNameKey(segmentName: string): string;
10
10
  extractSegmentName(builtSegmentKeyName: string): string | undefined;
11
+ buildOldSegmentNameKey(segmentName: string): string;
12
+ extractOldSegmentKey(builtSegmentKeyName: string): string | undefined;
11
13
  buildLastUpdatedKey(): string;
12
14
  isSplitsCacheKey(key: string): boolean;
13
15
  buildSplitsFilterQueryKey(): string;
@@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types';
3
3
  import { ISplitApi } from '../services/types';
4
4
  import { IStorageSync } from '../storages/types';
5
5
  import { ISettings } from '../types';
6
+ import { IPollingManager } from './polling/types';
6
7
  import { IPushManager } from './streaming/types';
7
8
  export interface ITask<Input extends any[] = []> {
8
9
  /**
@@ -39,6 +40,8 @@ export interface ITimeTracker {
39
40
  export interface ISyncManager extends ITask {
40
41
  flush(): Promise<any>;
41
42
  pushManager?: IPushManager;
43
+ pollingManager?: IPollingManager;
44
+ submitter?: ISyncTask;
42
45
  }
43
46
  export interface ISyncManagerCS extends ISyncManager {
44
47
  shared(matchingKey: string, readinessManager: IReadinessManager, storage: IStorageSync): ISyncManager | undefined;
package/types/types.d.ts CHANGED
@@ -48,6 +48,11 @@ declare type EventConsts = {
48
48
  * @typedef {string} SDKMode
49
49
  */
50
50
  export declare type SDKMode = 'standalone' | 'consumer' | 'localhost' | 'consumer_partial';
51
+ /**
52
+ * User consent status.
53
+ * @typedef {string} ConsentStatus
54
+ */
55
+ export declare type ConsentStatus = 'GRANTED' | 'DECLINED' | 'UNKNOWN';
51
56
  /**
52
57
  * Settings interface. This is a representation of the settings the SDK expose, that's why
53
58
  * most of it's props are readonly. Only features should be rewritten when localhost mode is active.
@@ -105,6 +110,7 @@ export interface ISettings {
105
110
  };
106
111
  readonly log: ILogger;
107
112
  readonly impressionListener?: unknown;
113
+ readonly userConsent?: ConsentStatus;
108
114
  }
109
115
  /**
110
116
  * Log levels.
@@ -0,0 +1,2 @@
1
+ import { ISettings } from '../types';
2
+ export declare function isConsentGranted(settings: ISettings): boolean;
@@ -20,3 +20,6 @@ export declare const STORAGE_MEMORY: StorageType;
20
20
  export declare const STORAGE_LOCALSTORAGE: StorageType;
21
21
  export declare const STORAGE_REDIS: StorageType;
22
22
  export declare const STORAGE_PLUGGABLE: StorageType;
23
+ export declare const CONSENT_GRANTED = "GRANTED";
24
+ export declare const CONSENT_DECLINED = "DECLINED";
25
+ export declare const CONSENT_UNKNOWN = "UNKNOWN";
@@ -0,0 +1,5 @@
1
+ import { ILogger } from '../../logger/types';
2
+ export declare function validateConsent({ userConsent, log }: {
3
+ userConsent: any;
4
+ log: ILogger;
5
+ }): any;
@@ -1,3 +1,3 @@
1
1
  import { ILogger } from '../../logger/types';
2
2
  import { SplitIO } from '../../types';
3
- export declare function validImpressionsMode(log: ILogger, impressionsMode: string): SplitIO.ImpressionsMode;
3
+ export declare function validImpressionsMode(log: ILogger, impressionsMode: any): SplitIO.ImpressionsMode;
@@ -23,4 +23,6 @@ export interface ISettingsValidationParams {
23
23
  logger: (settings: ISettings) => ISettings['log'];
24
24
  /** Localhost mode validator (`settings.sync.localhostMode`) */
25
25
  localhost?: (settings: ISettings) => ISettings['sync']['localhostMode'];
26
+ /** User consent validator (`settings.userConsent`) */
27
+ consent: (settings: ISettings) => ISettings['userConsent'];
26
28
  }
@@ -0,0 +1,5 @@
1
+ import { ILogger } from '../../logger/types';
2
+ export declare function validateUserConsent({ userConsent, log }: {
3
+ userConsent: any;
4
+ log: ILogger;
5
+ }): any;