@splitsoftware/splitio-commons 1.6.1 → 1.6.2-rc.2
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/cjs/listeners/browser.js +2 -1
- package/cjs/logger/constants.js +2 -1
- package/cjs/sdkFactory/index.js +14 -5
- package/cjs/services/splitApi.js +20 -0
- package/cjs/storages/inLocalStorage/index.js +4 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +5 -1
- package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -1
- package/cjs/storages/inMemory/uniqueKeysCacheInMemory.js +73 -0
- package/cjs/storages/inMemory/uniqueKeysCacheInMemoryCS.js +78 -0
- package/cjs/sync/submitters/submitterManager.js +3 -0
- package/cjs/sync/submitters/telemetrySubmitter.js +1 -0
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +26 -0
- package/cjs/trackers/impressionsTracker.js +7 -28
- package/cjs/trackers/strategy/strategyDebug.js +25 -0
- package/cjs/trackers/strategy/strategyNone.js +29 -0
- package/cjs/trackers/strategy/strategyOptimized.js +34 -0
- package/cjs/trackers/uniqueKeysTracker.js +31 -0
- package/cjs/utils/constants/index.js +4 -2
- package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
- package/cjs/utils/settingsValidation/index.js +3 -0
- package/esm/listeners/browser.js +3 -2
- package/esm/logger/constants.js +1 -0
- package/esm/sdkFactory/index.js +14 -5
- package/esm/services/splitApi.js +20 -0
- package/esm/storages/inLocalStorage/index.js +5 -1
- package/esm/storages/inMemory/InMemoryStorage.js +6 -2
- package/esm/storages/inMemory/InMemoryStorageCS.js +6 -2
- package/esm/storages/inMemory/uniqueKeysCacheInMemory.js +70 -0
- package/esm/storages/inMemory/uniqueKeysCacheInMemoryCS.js +75 -0
- package/esm/sync/submitters/submitterManager.js +3 -0
- package/esm/sync/submitters/telemetrySubmitter.js +2 -1
- package/esm/sync/submitters/uniqueKeysSubmitter.js +22 -0
- package/esm/trackers/impressionsTracker.js +7 -28
- package/esm/trackers/strategy/strategyDebug.js +21 -0
- package/esm/trackers/strategy/strategyNone.js +25 -0
- package/esm/trackers/strategy/strategyOptimized.js +30 -0
- package/esm/trackers/uniqueKeysTracker.js +27 -0
- package/esm/utils/constants/index.js +2 -0
- package/esm/utils/settingsValidation/impressionsMode.js +3 -3
- package/esm/utils/settingsValidation/index.js +3 -0
- package/package.json +1 -1
- package/src/listeners/browser.ts +3 -2
- package/src/logger/constants.ts +1 -0
- package/src/sdkFactory/index.ts +16 -5
- package/src/sdkFactory/types.ts +7 -4
- package/src/services/splitApi.ts +22 -0
- package/src/services/types.ts +6 -0
- package/src/storages/inLocalStorage/index.ts +4 -1
- package/src/storages/inMemory/InMemoryStorage.ts +5 -2
- package/src/storages/inMemory/InMemoryStorageCS.ts +6 -2
- package/src/storages/inMemory/uniqueKeysCacheInMemory.ts +83 -0
- package/src/storages/inMemory/uniqueKeysCacheInMemoryCS.ts +89 -0
- package/src/storages/types.ts +22 -6
- package/src/sync/submitters/submitterManager.ts +2 -0
- package/src/sync/submitters/telemetrySubmitter.ts +4 -3
- package/src/sync/submitters/types.ts +20 -1
- package/src/sync/submitters/uniqueKeysSubmitter.ts +35 -0
- package/src/trackers/impressionsTracker.ts +12 -35
- package/src/trackers/strategy/strategyDebug.ts +28 -0
- package/src/trackers/strategy/strategyNone.ts +34 -0
- package/src/trackers/strategy/strategyOptimized.ts +42 -0
- package/src/trackers/types.ts +26 -0
- package/src/trackers/uniqueKeysTracker.ts +37 -0
- package/src/types.ts +3 -1
- package/src/utils/constants/index.ts +2 -0
- package/src/utils/settingsValidation/impressionsMode.ts +3 -3
- package/src/utils/settingsValidation/index.ts +4 -0
- package/types/logger/browser/{debugLogger.d.ts → DebugLogger.d.ts} +0 -0
- package/types/logger/browser/{errorLogger.d.ts → ErrorLogger.d.ts} +0 -0
- package/types/logger/browser/{infoLogger.d.ts → InfoLogger.d.ts} +0 -0
- package/types/logger/browser/{warnLogger.d.ts → WarnLogger.d.ts} +0 -0
- package/types/logger/constants.d.ts +1 -0
- package/types/sdkFactory/types.d.ts +4 -2
- package/types/services/types.d.ts +4 -0
- package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +32 -0
- package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +37 -0
- package/types/storages/types.d.ts +14 -4
- package/types/sync/submitters/types.d.ts +18 -1
- package/types/sync/submitters/uniqueKeysSubmitter.d.ts +5 -0
- package/types/trackers/filter/bloomFilter.d.ts +10 -0
- package/types/trackers/filter/dictionaryFilter.d.ts +8 -0
- package/types/trackers/filter/types.d.ts +5 -0
- package/types/trackers/impressionsTracker.d.ts +4 -6
- package/types/trackers/strategy/strategyDebug.d.ts +9 -0
- package/types/trackers/strategy/strategyNone.d.ts +10 -0
- package/types/trackers/strategy/strategyOptimized.d.ts +11 -0
- package/types/trackers/types.d.ts +21 -0
- package/types/trackers/uniqueKeysTracker.d.ts +13 -0
- package/types/types.d.ts +3 -1
- package/types/utils/constants/index.d.ts +2 -0
- package/types/utils/settingsValidation/index.d.ts +1 -0
- package/types/utils/timeTracker/index.d.ts +70 -1
- package/src/logger/.DS_Store +0 -0
- package/types/integrations/ga/GaToSplitPlugin.d.ts +0 -3
- package/types/integrations/ga/SplitToGaPlugin.d.ts +0 -4
- package/types/integrations/ga/autoRequire.d.ts +0 -4
- package/types/logger/codes.d.ts +0 -2
- package/types/logger/codesConstants.d.ts +0 -117
- package/types/logger/codesConstantsBrowser.d.ts +0 -2
- package/types/logger/codesConstantsNode.d.ts +0 -14
- package/types/logger/codesDebug.d.ts +0 -1
- package/types/logger/codesDebugBrowser.d.ts +0 -1
- package/types/logger/codesDebugNode.d.ts +0 -1
- package/types/logger/codesError.d.ts +0 -1
- package/types/logger/codesErrorNode.d.ts +0 -1
- package/types/logger/codesInfo.d.ts +0 -1
- package/types/logger/codesWarn.d.ts +0 -1
- package/types/logger/codesWarnNode.d.ts +0 -1
- package/types/logger/debugLogger.d.ts +0 -2
- package/types/logger/errorLogger.d.ts +0 -2
- package/types/logger/infoLogger.d.ts +0 -2
- package/types/logger/messages/debugBrowser.d.ts +0 -1
- package/types/logger/messages/debugNode.d.ts +0 -1
- package/types/logger/messages/errorNode.d.ts +0 -1
- package/types/logger/messages/warnNode.d.ts +0 -1
- package/types/logger/noopLogger.d.ts +0 -2
- package/types/logger/warnLogger.d.ts +0 -2
- package/types/sdkFactory/userConsentProps.d.ts +0 -6
- package/types/sdkManager/sdkManagerMethod.d.ts +0 -6
- package/types/storages/getRegisteredSegments.d.ts +0 -10
- package/types/storages/inMemory/index.d.ts +0 -10
- package/types/storages/parseSegments.d.ts +0 -6
- package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +0 -35
- package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +0 -35
- package/types/sync/streaming/AuthClient/indexV1.d.ts +0 -12
- package/types/sync/streaming/AuthClient/indexV2.d.ts +0 -8
- package/types/sync/streaming/pushManagerCS.d.ts +0 -1
- package/types/sync/streaming/pushManagerNoUsers.d.ts +0 -13
- package/types/sync/streaming/pushManagerSS.d.ts +0 -1
- package/types/sync/submitters/telemetrySyncTask.d.ts +0 -0
- package/types/sync/syncManagerFromFile.d.ts +0 -2
- package/types/sync/syncManagerFromObject.d.ts +0 -2
- package/types/sync/syncManagerOffline.d.ts +0 -9
- package/types/trackers/telemetryRecorder.d.ts +0 -0
- package/types/utils/EventEmitter.d.ts +0 -4
- package/types/utils/consent.d.ts +0 -2
- package/types/utils/lang/errors.d.ts +0 -10
- package/types/utils/murmur3/commons.d.ts +0 -12
- package/types/utils/settingsValidation/buildMetadata.d.ts +0 -3
- package/types/utils/settingsValidation/localhost/index.d.ts +0 -9
- package/types/utils/settingsValidation/logger.d.ts +0 -11
- package/types/utils/settingsValidation/runtime/browser.d.ts +0 -2
- package/types/utils/settingsValidation/runtime/node.d.ts +0 -2
- package/types/utils/settingsValidation/userConsent.d.ts +0 -5
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants';
|
|
2
|
-
import { DEBUG, OPTIMIZED } from '../constants';
|
|
2
|
+
import { DEBUG, OPTIMIZED, NONE } from '../constants';
|
|
3
3
|
import { stringToUpperCase } from '../lang';
|
|
4
4
|
export function validImpressionsMode(log, impressionsMode) {
|
|
5
5
|
impressionsMode = stringToUpperCase(impressionsMode);
|
|
6
|
-
if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) > -1)
|
|
6
|
+
if ([DEBUG, OPTIMIZED, NONE].indexOf(impressionsMode) > -1)
|
|
7
7
|
return impressionsMode;
|
|
8
|
-
log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED], OPTIMIZED]);
|
|
8
|
+
log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED, NONE], OPTIMIZED]);
|
|
9
9
|
return OPTIMIZED;
|
|
10
10
|
}
|
|
@@ -31,6 +31,8 @@ export var base = {
|
|
|
31
31
|
telemetryRefreshRate: 3600,
|
|
32
32
|
// publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
|
|
33
33
|
impressionsRefreshRate: 300,
|
|
34
|
+
// publish unique Keys each 900 sec (15 min)
|
|
35
|
+
uniqueKeysRefreshRate: 900,
|
|
34
36
|
// fetch offline changes each 15 sec
|
|
35
37
|
offlineRefreshRate: 15,
|
|
36
38
|
// publish events every 60 seconds after the first flush
|
|
@@ -109,6 +111,7 @@ export function settingsValidation(config, validationParams) {
|
|
|
109
111
|
scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
|
|
110
112
|
scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
|
|
111
113
|
scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
|
|
114
|
+
scheduler.uniqueKeysRefreshRate = fromSecondsToMillis(scheduler.uniqueKeysRefreshRate);
|
|
112
115
|
scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
|
|
113
116
|
// Default impressionsRefreshRate for DEBUG mode is 60 secs
|
|
114
117
|
if (get(config, 'scheduler.impressionsRefreshRate') === undefined && withDefaults.sync.impressionsMode === DEBUG)
|
package/package.json
CHANGED
package/src/listeners/browser.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { fromImpressionCountsCollector } from '../sync/submitters/impressionCoun
|
|
|
7
7
|
import { IResponse, ISplitApi } from '../services/types';
|
|
8
8
|
import { ImpressionDTO, ISettings } from '../types';
|
|
9
9
|
import { ImpressionsPayload } from '../sync/submitters/types';
|
|
10
|
-
import { OPTIMIZED, DEBUG } from '../utils/constants';
|
|
10
|
+
import { OPTIMIZED, DEBUG, NONE } 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';
|
|
@@ -88,9 +88,10 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
88
88
|
// Flush impressions & events data if there is user consent
|
|
89
89
|
if (isConsentGranted(this.settings)) {
|
|
90
90
|
const eventsUrl = this.settings.urls.events;
|
|
91
|
+
const sim = this.settings.sync.impressionsMode;
|
|
91
92
|
const extraMetadata = {
|
|
92
93
|
// sim stands for Sync/Split Impressions Mode
|
|
93
|
-
sim:
|
|
94
|
+
sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
|
|
94
95
|
};
|
|
95
96
|
|
|
96
97
|
this._flushData(eventsUrl + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
|
package/src/logger/constants.ts
CHANGED
|
@@ -143,4 +143,5 @@ export const LOG_PREFIX_SYNC_POLLING = LOG_PREFIX_SYNC + ':polling-manager: ';
|
|
|
143
143
|
export const LOG_PREFIX_SYNC_SUBMITTERS = LOG_PREFIX_SYNC + ':submitter: ';
|
|
144
144
|
export const LOG_PREFIX_IMPRESSIONS_TRACKER = 'impressions-tracker: ';
|
|
145
145
|
export const LOG_PREFIX_EVENTS_TRACKER = 'events-tracker: ';
|
|
146
|
+
export const LOG_PREFIX_UNIQUE_KEYS_TRACKER = 'unique-keys-tracker: ';
|
|
146
147
|
export const LOG_PREFIX_CLEANUP = 'cleanup: ';
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -13,6 +13,11 @@ import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
|
|
|
13
13
|
import { metadataBuilder } from '../storages/metadataBuilder';
|
|
14
14
|
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
|
|
15
15
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
16
|
+
import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
|
|
17
|
+
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
|
|
18
|
+
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
|
|
19
|
+
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
|
|
20
|
+
import { NONE } from '../utils/constants';
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* Modular SDK factory
|
|
@@ -21,7 +26,8 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
21
26
|
|
|
22
27
|
const { settings, platform, storageFactory, splitApiFactory, extraProps,
|
|
23
28
|
syncManagerFactory, SignalListener, impressionsObserverFactory,
|
|
24
|
-
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory
|
|
29
|
+
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
|
|
30
|
+
filterAdapterFactory } = params;
|
|
25
31
|
const log = settings.log;
|
|
26
32
|
|
|
27
33
|
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid API Key, etc.
|
|
@@ -37,6 +43,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
37
43
|
const storageFactoryParams: IStorageFactoryParams = {
|
|
38
44
|
impressionsQueueSize: settings.scheduler.impressionsQueueSize,
|
|
39
45
|
eventsQueueSize: settings.scheduler.eventsQueueSize,
|
|
46
|
+
uniqueKeysCacheSize: settings.scheduler.uniqueKeysCacheSize,
|
|
40
47
|
optimize: shouldBeOptimized(settings),
|
|
41
48
|
|
|
42
49
|
// ATM, only used by InLocalStorage
|
|
@@ -45,6 +52,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
45
52
|
|
|
46
53
|
// ATM, only used by PluggableStorage
|
|
47
54
|
mode: settings.mode,
|
|
55
|
+
impressionsMode: settings.sync.impressionsMode,
|
|
48
56
|
|
|
49
57
|
// Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined,
|
|
50
58
|
// or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events.
|
|
@@ -62,16 +70,19 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
62
70
|
|
|
63
71
|
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage });
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
73
|
+
const observer = impressionsObserverFactory();
|
|
74
|
+
const uniqueKeysTracker = storageFactoryParams.impressionsMode === NONE ? uniqueKeysTrackerFactory(log, storage.uniqueKeys!, filterAdapterFactory && filterAdapterFactory()) : undefined;
|
|
75
|
+
const strategy = (storageFactoryParams.optimize) ? strategyOptimizedFactory(observer, storage.impressionCounts!) :
|
|
76
|
+
(storageFactoryParams.impressionsMode === NONE) ? strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!) : strategyDebugFactory(observer);
|
|
77
|
+
|
|
78
|
+
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
|
|
68
79
|
const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
|
|
69
80
|
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
70
81
|
|
|
71
82
|
// splitApi is used by SyncManager and Browser signal listener
|
|
72
83
|
const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
|
|
73
84
|
|
|
74
|
-
const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, readiness, settings, storage, platform };
|
|
85
|
+
const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, uniqueKeysTracker, sdkReadinessManager, readiness, settings, storage, platform };
|
|
75
86
|
|
|
76
87
|
const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
|
|
77
88
|
ctx.syncManager = syncManager;
|
package/src/sdkFactory/types.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
|
|
|
6
6
|
import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types';
|
|
7
7
|
import { ISyncManager } from '../sync/types';
|
|
8
8
|
import { IImpressionObserver } from '../trackers/impressionObserver/types';
|
|
9
|
-
import { IImpressionsTracker, IEventTracker, ITelemetryTracker } from '../trackers/types';
|
|
9
|
+
import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter, IUniqueKeysTracker } from '../trackers/types';
|
|
10
10
|
import { SplitIO, ISettings, IEventEmitter } from '../types';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -44,6 +44,7 @@ export interface ISdkFactoryContext {
|
|
|
44
44
|
eventTracker: IEventTracker,
|
|
45
45
|
telemetryTracker: ITelemetryTracker,
|
|
46
46
|
storage: IStorageSync | IStorageAsync,
|
|
47
|
+
uniqueKeysTracker?: IUniqueKeysTracker,
|
|
47
48
|
signalListener?: ISignalListener
|
|
48
49
|
splitApi?: ISplitApi
|
|
49
50
|
syncManager?: ISyncManager,
|
|
@@ -96,6 +97,11 @@ export interface ISdkFactoryParams {
|
|
|
96
97
|
// It Allows to distinguish SDK clients with the client-side API (`ICsSDK`) or server-side API (`ISDK` or `IAsyncSDK`).
|
|
97
98
|
sdkClientMethodFactory: (params: ISdkFactoryContext) => ({ (): SplitIO.ICsClient; (key: SplitIO.SplitKey, trafficType?: string | undefined): SplitIO.ICsClient; } | (() => SplitIO.IClient) | (() => SplitIO.IAsyncClient))
|
|
98
99
|
|
|
100
|
+
// Impression observer factory. If provided, will be used for impressions dedupe
|
|
101
|
+
impressionsObserverFactory: () => IImpressionObserver
|
|
102
|
+
|
|
103
|
+
filterAdapterFactory?: () => IFilterAdapter
|
|
104
|
+
|
|
99
105
|
// Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed.
|
|
100
106
|
// Pass only if `syncManager` (used by Node listener) and `splitApi` (used by Browser listener) are passed.
|
|
101
107
|
SignalListener?: new (
|
|
@@ -107,9 +113,6 @@ export interface ISdkFactoryParams {
|
|
|
107
113
|
// @TODO review impressionListener and integrations interfaces. What about handling impressionListener as an integration ?
|
|
108
114
|
integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined,
|
|
109
115
|
|
|
110
|
-
// Impression observer factory. If provided, will be used for impressions dedupe
|
|
111
|
-
impressionsObserverFactory?: () => IImpressionObserver
|
|
112
|
-
|
|
113
116
|
// Optional function to assign additional properties to the factory instance
|
|
114
117
|
extraProps?: (params: ISdkFactoryContext) => object
|
|
115
118
|
}
|
package/src/services/splitApi.ts
CHANGED
|
@@ -106,6 +106,28 @@ export function splitApiFactory(
|
|
|
106
106
|
const url = `${urls.events}/testImpressions/count`;
|
|
107
107
|
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
|
|
108
108
|
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Post unique keys for client side.
|
|
112
|
+
*
|
|
113
|
+
* @param body unique keys payload
|
|
114
|
+
* @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
115
|
+
*/
|
|
116
|
+
postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
|
|
117
|
+
const url = `${urls.telemetry}/v1/keys/cs`;
|
|
118
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Post unique keys for server side.
|
|
123
|
+
*
|
|
124
|
+
* @param body unique keys payload
|
|
125
|
+
* @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
126
|
+
*/
|
|
127
|
+
postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
|
|
128
|
+
const url = `${urls.telemetry}/v1/keys/ss`;
|
|
129
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
|
|
130
|
+
},
|
|
109
131
|
|
|
110
132
|
postMetricsConfig(body: string) {
|
|
111
133
|
const url = `${urls.telemetry}/v1/metrics/config`;
|
package/src/services/types.ts
CHANGED
|
@@ -43,6 +43,10 @@ export type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => P
|
|
|
43
43
|
|
|
44
44
|
export type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
45
45
|
|
|
46
|
+
export type IPostUniqueKeysBulkCs = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
47
|
+
|
|
48
|
+
export type IPostUniqueKeysBulkSs = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
49
|
+
|
|
46
50
|
export type IPostTestImpressionsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
47
51
|
|
|
48
52
|
export type IPostTestImpressionsCount = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
@@ -59,6 +63,8 @@ export interface ISplitApi {
|
|
|
59
63
|
fetchSegmentChanges: IFetchSegmentChanges
|
|
60
64
|
fetchMySegments: IFetchMySegments
|
|
61
65
|
postEventsBulk: IPostEventsBulk
|
|
66
|
+
postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
|
|
67
|
+
postUniqueKeysBulkSs: IPostUniqueKeysBulkSs
|
|
62
68
|
postTestImpressionsBulk: IPostTestImpressionsBulk
|
|
63
69
|
postTestImpressionsCount: IPostTestImpressionsCount
|
|
64
70
|
postMetricsConfig: IPostMetricsConfig
|
|
@@ -12,8 +12,9 @@ import { SplitsCacheInMemory } from '../inMemory/SplitsCacheInMemory';
|
|
|
12
12
|
import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
|
|
13
13
|
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
|
|
14
14
|
import { LOG_PREFIX } from './constants';
|
|
15
|
-
import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
15
|
+
import { LOCALHOST_MODE, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
|
|
16
16
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
17
|
+
import { UniqueKeysCacheInMemoryCS } from '../inMemory/uniqueKeysCacheInMemoryCS';
|
|
17
18
|
|
|
18
19
|
export interface InLocalStorageOptions {
|
|
19
20
|
prefix?: string
|
|
@@ -45,6 +46,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
45
46
|
impressionCounts: params.optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
46
47
|
events: new EventsCacheInMemory(params.eventsQueueSize),
|
|
47
48
|
telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
|
|
49
|
+
uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
|
|
48
50
|
|
|
49
51
|
destroy() {
|
|
50
52
|
this.splits = new SplitsCacheInMemory();
|
|
@@ -52,6 +54,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
52
54
|
this.impressions.clear();
|
|
53
55
|
this.impressionCounts && this.impressionCounts.clear();
|
|
54
56
|
this.events.clear();
|
|
57
|
+
this.uniqueKeys?.clear();
|
|
55
58
|
},
|
|
56
59
|
|
|
57
60
|
// When using shared instanciation with MEMORY we reuse everything but segments (they are customer per key).
|
|
@@ -4,8 +4,9 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
5
5
|
import { IStorageFactoryParams, IStorageSync } from '../types';
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
|
-
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
|
+
import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
|
+
import { UniqueKeysCacheInMemory } from './uniqueKeysCacheInMemory';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* InMemory storage factory for standalone server-side SplitFactory
|
|
@@ -18,9 +19,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
18
19
|
splits: new SplitsCacheInMemory(),
|
|
19
20
|
segments: new SegmentsCacheInMemory(),
|
|
20
21
|
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
|
|
21
|
-
impressionCounts: params.
|
|
22
|
+
impressionCounts: params.impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
22
23
|
events: new EventsCacheInMemory(params.eventsQueueSize),
|
|
23
24
|
telemetry: params.mode !== LOCALHOST_MODE ? new TelemetryCacheInMemory() : undefined, // Always track telemetry in standalone mode on server-side
|
|
25
|
+
uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemory(params.uniqueKeysCacheSize) : undefined,
|
|
24
26
|
|
|
25
27
|
// When using MEMORY we should clean all the caches to leave them empty
|
|
26
28
|
destroy() {
|
|
@@ -29,6 +31,7 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
|
|
|
29
31
|
this.impressions.clear();
|
|
30
32
|
this.impressionCounts && this.impressionCounts.clear();
|
|
31
33
|
this.events.clear();
|
|
34
|
+
this.uniqueKeys?.clear();
|
|
32
35
|
}
|
|
33
36
|
};
|
|
34
37
|
}
|
|
@@ -4,8 +4,9 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
5
5
|
import { IStorageSync, IStorageFactoryParams } from '../types';
|
|
6
6
|
import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
|
|
7
|
-
import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
|
|
7
|
+
import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
|
|
9
|
+
import { UniqueKeysCacheInMemoryCS } from './uniqueKeysCacheInMemoryCS';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* InMemory storage factory for standalone client-side SplitFactory
|
|
@@ -18,9 +19,11 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
18
19
|
splits: new SplitsCacheInMemory(),
|
|
19
20
|
segments: new MySegmentsCacheInMemory(),
|
|
20
21
|
impressions: new ImpressionsCacheInMemory(params.impressionsQueueSize),
|
|
21
|
-
impressionCounts: params.
|
|
22
|
+
impressionCounts: params.impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
|
|
22
23
|
events: new EventsCacheInMemory(params.eventsQueueSize),
|
|
23
24
|
telemetry: params.mode !== LOCALHOST_MODE && shouldRecordTelemetry() ? new TelemetryCacheInMemory() : undefined,
|
|
25
|
+
uniqueKeys: params.impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS(params.uniqueKeysCacheSize) : undefined,
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
// When using MEMORY we should clean all the caches to leave them empty
|
|
26
29
|
destroy() {
|
|
@@ -29,6 +32,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
29
32
|
this.impressions.clear();
|
|
30
33
|
this.impressionCounts && this.impressionCounts.clear();
|
|
31
34
|
this.events.clear();
|
|
35
|
+
this.uniqueKeys?.clear();
|
|
32
36
|
},
|
|
33
37
|
|
|
34
38
|
// When using shared instanciation with MEMORY we reuse everything but segments (they are unique per key)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { IUniqueKeysCacheBase } from '../types';
|
|
2
|
+
import { ISet, setToArray, _Set } from '../../utils/lang/sets';
|
|
3
|
+
import { UniqueKeysPayloadSs } from '../../sync/submitters/types';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_CACHE_SIZE = 30000;
|
|
6
|
+
|
|
7
|
+
export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
|
|
8
|
+
|
|
9
|
+
private onFullQueue?: () => void;
|
|
10
|
+
private readonly maxStorage: number;
|
|
11
|
+
private uniqueTrackerSize = 0;
|
|
12
|
+
private uniqueKeysTracker: { [keys: string]: ISet<string> };
|
|
13
|
+
|
|
14
|
+
constructor(uniqueKeysQueueSize: number = DEFAULT_CACHE_SIZE) {
|
|
15
|
+
this.maxStorage = uniqueKeysQueueSize;
|
|
16
|
+
this.uniqueKeysTracker = {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setOnFullQueueCb(cb: () => void) {
|
|
20
|
+
this.onFullQueue = cb;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Store unique keys in sequential order
|
|
25
|
+
* key: string = feature name.
|
|
26
|
+
* value: Set<string> = set of unique keys.
|
|
27
|
+
*/
|
|
28
|
+
track(key: string, featureName: string) {
|
|
29
|
+
if (!this.uniqueKeysTracker[featureName]) this.uniqueKeysTracker[featureName] = new _Set();
|
|
30
|
+
const tracker = this.uniqueKeysTracker[featureName];
|
|
31
|
+
if (!tracker.has(key)) {
|
|
32
|
+
tracker.add(key);
|
|
33
|
+
this.uniqueTrackerSize++;
|
|
34
|
+
}
|
|
35
|
+
if (this.uniqueTrackerSize >= this.maxStorage && this.onFullQueue) {
|
|
36
|
+
this.uniqueTrackerSize = 0;
|
|
37
|
+
this.onFullQueue();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clear the data stored on the cache.
|
|
43
|
+
*/
|
|
44
|
+
clear() {
|
|
45
|
+
this.uniqueKeysTracker = {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Pop the collected data, used as payload for posting.
|
|
50
|
+
*/
|
|
51
|
+
pop() {
|
|
52
|
+
const data = this.uniqueKeysTracker;
|
|
53
|
+
this.uniqueKeysTracker = {};
|
|
54
|
+
return this.fromUniqueKeysCollector(data);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if the cache is empty.
|
|
59
|
+
*/
|
|
60
|
+
isEmpty() {
|
|
61
|
+
return Object.keys(this.uniqueKeysTracker).length === 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Converts `uniqueKeys` data from cache into request payload for SS.
|
|
66
|
+
*/
|
|
67
|
+
private fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadSs {
|
|
68
|
+
const payload = [];
|
|
69
|
+
const featureNames = Object.keys(uniqueKeys);
|
|
70
|
+
for (let i = 0; i < featureNames.length; i++) {
|
|
71
|
+
const featureName = featureNames[i];
|
|
72
|
+
const featureKeys = setToArray(uniqueKeys[featureName]);
|
|
73
|
+
const uniqueKeysPayload = {
|
|
74
|
+
f: featureName,
|
|
75
|
+
ks: featureKeys
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
payload.push(uniqueKeysPayload);
|
|
79
|
+
}
|
|
80
|
+
return { keys: payload };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { IUniqueKeysCacheBase } from '../types';
|
|
2
|
+
import { ISet, setToArray, _Set } from '../../utils/lang/sets';
|
|
3
|
+
import { UniqueKeysPayloadCs } from '../../sync/submitters/types';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_CACHE_SIZE = 30000;
|
|
6
|
+
|
|
7
|
+
export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase {
|
|
8
|
+
|
|
9
|
+
private onFullQueue?: () => void;
|
|
10
|
+
private readonly maxStorage: number;
|
|
11
|
+
private uniqueTrackerSize = 0;
|
|
12
|
+
private uniqueKeysTracker: { [keys: string]: ISet<string> };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param impressionsQueueSize number of queued impressions to call onFullQueueCb.
|
|
17
|
+
* Default value is 0, that means no maximum value, in case we want to avoid this being triggered.
|
|
18
|
+
*/
|
|
19
|
+
constructor(uniqueKeysQueueSize: number = DEFAULT_CACHE_SIZE) {
|
|
20
|
+
this.maxStorage = uniqueKeysQueueSize;
|
|
21
|
+
this.uniqueKeysTracker = {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setOnFullQueueCb(cb: () => void) {
|
|
25
|
+
this.onFullQueue = cb;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Store unique keys in sequential order
|
|
30
|
+
* key: string = key.
|
|
31
|
+
* value: HashSet<string> = set of split names.
|
|
32
|
+
*/
|
|
33
|
+
track(key: string, featureName: string) {
|
|
34
|
+
|
|
35
|
+
if (!this.uniqueKeysTracker[key]) this.uniqueKeysTracker[key] = new _Set();
|
|
36
|
+
const tracker = this.uniqueKeysTracker[key];
|
|
37
|
+
if (!tracker.has(featureName)) {
|
|
38
|
+
tracker.add(featureName);
|
|
39
|
+
this.uniqueTrackerSize++;
|
|
40
|
+
}
|
|
41
|
+
if (this.uniqueTrackerSize >= this.maxStorage && this.onFullQueue) {
|
|
42
|
+
this.uniqueTrackerSize = 0;
|
|
43
|
+
this.onFullQueue();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Clear the data stored on the cache.
|
|
49
|
+
*/
|
|
50
|
+
clear() {
|
|
51
|
+
this.uniqueKeysTracker = {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pop the collected data, used as payload for posting.
|
|
56
|
+
*/
|
|
57
|
+
pop() {
|
|
58
|
+
const data = this.uniqueKeysTracker;
|
|
59
|
+
this.uniqueKeysTracker = {};
|
|
60
|
+
return this.fromUniqueKeysCollector(data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if the cache is empty.
|
|
65
|
+
*/
|
|
66
|
+
isEmpty() {
|
|
67
|
+
return Object.keys(this.uniqueKeysTracker).length === 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Converts `uniqueKeys` data from cache into request payload.
|
|
72
|
+
*/
|
|
73
|
+
private fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadCs {
|
|
74
|
+
const payload = [];
|
|
75
|
+
const featureKeys = Object.keys(uniqueKeys);
|
|
76
|
+
for (let k = 0; k < featureKeys.length; k++) {
|
|
77
|
+
const featureKey = featureKeys[k];
|
|
78
|
+
const featureNames = setToArray(uniqueKeys[featureKey]);
|
|
79
|
+
const uniqueKeysPayload = {
|
|
80
|
+
k: featureKey,
|
|
81
|
+
fs: featureNames
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
payload.push(uniqueKeysPayload);
|
|
85
|
+
}
|
|
86
|
+
return { keys: payload };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
package/src/storages/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types';
|
|
2
2
|
import { ILogger } from '../logger/types';
|
|
3
|
-
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent } from '../sync/submitters/types';
|
|
3
|
+
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
|
|
4
4
|
import { SplitIO, ImpressionDTO, SDKMode } from '../types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -355,6 +355,17 @@ export interface IImpressionCountsCacheSync extends IRecorderCacheProducerSync<R
|
|
|
355
355
|
pop(toMerge?: Record<string, number> ): Record<string, number> // pop cache data
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
export interface IUniqueKeysCacheBase {
|
|
359
|
+
// Used by unique Keys tracker
|
|
360
|
+
track(key: string, value: string): void
|
|
361
|
+
|
|
362
|
+
// Used by unique keys submitter in standalone and producer mode
|
|
363
|
+
isEmpty(): boolean // check if cache is empty. Return true if the cache was just created or cleared.
|
|
364
|
+
pop(): UniqueKeysPayloadSs | UniqueKeysPayloadCs // pop cache data
|
|
365
|
+
/* Registers callback for full queue */
|
|
366
|
+
setOnFullQueueCb(cb: () => void): void,
|
|
367
|
+
clear(): void
|
|
368
|
+
}
|
|
358
369
|
|
|
359
370
|
/**
|
|
360
371
|
* Telemetry storage interface for standalone and partial consumer modes.
|
|
@@ -445,14 +456,16 @@ export interface IStorageBase<
|
|
|
445
456
|
TSegmentsCache extends ISegmentsCacheBase,
|
|
446
457
|
TImpressionsCache extends IImpressionsCacheBase,
|
|
447
458
|
TEventsCache extends IEventsCacheBase,
|
|
448
|
-
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync
|
|
459
|
+
TTelemetryCache extends ITelemetryCacheSync | ITelemetryCacheAsync,
|
|
460
|
+
TUniqueKeysCache extends IUniqueKeysCacheBase
|
|
449
461
|
> {
|
|
450
462
|
splits: TSplitsCache,
|
|
451
463
|
segments: TSegmentsCache,
|
|
452
464
|
impressions: TImpressionsCache,
|
|
453
465
|
impressionCounts?: IImpressionCountsCacheSync,
|
|
454
466
|
events: TEventsCache,
|
|
455
|
-
telemetry?: TTelemetryCache
|
|
467
|
+
telemetry?: TTelemetryCache,
|
|
468
|
+
uniqueKeys?: TUniqueKeysCache,
|
|
456
469
|
destroy(): void | Promise<void>,
|
|
457
470
|
shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
|
|
458
471
|
}
|
|
@@ -462,7 +475,8 @@ export interface IStorageSync extends IStorageBase<
|
|
|
462
475
|
ISegmentsCacheSync,
|
|
463
476
|
IImpressionsCacheSync,
|
|
464
477
|
IEventsCacheSync,
|
|
465
|
-
ITelemetryCacheSync
|
|
478
|
+
ITelemetryCacheSync,
|
|
479
|
+
IUniqueKeysCacheBase
|
|
466
480
|
> { }
|
|
467
481
|
|
|
468
482
|
export interface IStorageAsync extends IStorageBase<
|
|
@@ -470,7 +484,8 @@ export interface IStorageAsync extends IStorageBase<
|
|
|
470
484
|
ISegmentsCacheAsync,
|
|
471
485
|
IImpressionsCacheAsync | IImpressionsCacheSync,
|
|
472
486
|
IEventsCacheAsync | IEventsCacheSync,
|
|
473
|
-
ITelemetryCacheAsync
|
|
487
|
+
ITelemetryCacheAsync,
|
|
488
|
+
IUniqueKeysCacheBase
|
|
474
489
|
> { }
|
|
475
490
|
|
|
476
491
|
/** StorageFactory */
|
|
@@ -480,10 +495,11 @@ export type DataLoader = (storage: IStorageSync, matchingKey: string) => void
|
|
|
480
495
|
export interface IStorageFactoryParams {
|
|
481
496
|
log: ILogger,
|
|
482
497
|
impressionsQueueSize?: number,
|
|
498
|
+
uniqueKeysCacheSize?: number;
|
|
483
499
|
eventsQueueSize?: number,
|
|
484
500
|
optimize?: boolean /* whether create the `impressionCounts` cache (OPTIMIZED impression mode) or not (DEBUG impression mode) */,
|
|
485
501
|
mode: SDKMode,
|
|
486
|
-
|
|
502
|
+
impressionsMode?: string,
|
|
487
503
|
// ATM, only used by InLocalStorage
|
|
488
504
|
matchingKey?: string, /* undefined on server-side SDKs */
|
|
489
505
|
splitFiltersValidation?: ISplitFiltersValidation,
|
|
@@ -4,6 +4,7 @@ import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
|
|
|
4
4
|
import { telemetrySubmitterFactory } from './telemetrySubmitter';
|
|
5
5
|
import { ISdkFactoryContextSync } from '../../sdkFactory/types';
|
|
6
6
|
import { ISubmitterManager } from './types';
|
|
7
|
+
import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
|
|
7
8
|
|
|
8
9
|
export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmitterManager {
|
|
9
10
|
|
|
@@ -15,6 +16,7 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
|
|
|
15
16
|
const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
|
|
16
17
|
if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
|
|
17
18
|
const telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
19
|
+
if (params.uniqueKeysTracker) submitters.push(uniqueKeysSubmitterFactory(params));
|
|
18
20
|
|
|
19
21
|
return {
|
|
20
22
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types';
|
|
2
2
|
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
3
3
|
import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload, TelemetryConfigStats } from './types';
|
|
4
|
-
import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
|
|
4
|
+
import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, NONE, DEBUG_ENUM, OPTIMIZED_ENUM, NONE_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
|
|
5
5
|
import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
|
|
6
6
|
import { ConsentStatus, ISettings, SDKMode } from '../../types';
|
|
7
7
|
import { base } from '../../utils/settingsValidation';
|
|
@@ -52,8 +52,9 @@ const OPERATION_MODE_MAP = {
|
|
|
52
52
|
|
|
53
53
|
const IMPRESSIONS_MODE_MAP = {
|
|
54
54
|
[OPTIMIZED]: OPTIMIZED_ENUM,
|
|
55
|
-
[DEBUG]: DEBUG_ENUM
|
|
56
|
-
|
|
55
|
+
[DEBUG]: DEBUG_ENUM,
|
|
56
|
+
[NONE]: NONE_ENUM
|
|
57
|
+
} as Record<ISettings['sync']['impressionsMode'], (0 | 1 | 2)>;
|
|
57
58
|
|
|
58
59
|
const USER_CONSENT_MAP = {
|
|
59
60
|
[CONSENT_UNKNOWN]: 1,
|
|
@@ -35,6 +35,24 @@ export type ImpressionCountsPayload = {
|
|
|
35
35
|
}[]
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export type UniqueKeysPayloadSs = {
|
|
39
|
+
keys: {
|
|
40
|
+
/** Split name */
|
|
41
|
+
f: string
|
|
42
|
+
/** keyNames */
|
|
43
|
+
ks: string[]
|
|
44
|
+
}[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type UniqueKeysPayloadCs = {
|
|
48
|
+
keys: {
|
|
49
|
+
/** keyNames */
|
|
50
|
+
k: string
|
|
51
|
+
/** Split name */
|
|
52
|
+
fs: string[]
|
|
53
|
+
}[]
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
export type StoredImpressionWithMetadata = {
|
|
39
57
|
/** Metadata */
|
|
40
58
|
m: IMetadata,
|
|
@@ -148,7 +166,8 @@ export type OperationMode = STANDALONE_ENUM | CONSUMER_ENUM | CONSUMER_PARTIAL_E
|
|
|
148
166
|
|
|
149
167
|
export type OPTIMIZED_ENUM = 0;
|
|
150
168
|
export type DEBUG_ENUM = 1;
|
|
151
|
-
export type
|
|
169
|
+
export type NONE_ENUM = 2;
|
|
170
|
+
export type ImpressionsMode = OPTIMIZED_ENUM | DEBUG_ENUM | NONE_ENUM;
|
|
152
171
|
|
|
153
172
|
export type RefreshRates = {
|
|
154
173
|
sp: number, // splits
|