@splitsoftware/splitio-commons 2.1.1-rc.0 → 2.1.1-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/CHANGES.txt +7 -0
- package/README.md +0 -1
- package/cjs/evaluator/combiners/and.js +6 -2
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +5 -6
- package/cjs/evaluator/index.js +7 -7
- package/cjs/evaluator/matchers/index.js +1 -3
- package/cjs/evaluator/matchers/matcherTypes.js +0 -1
- package/cjs/evaluator/matchersTransform/index.js +0 -4
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +0 -1
- package/cjs/logger/constants.js +3 -4
- package/cjs/logger/messages/debug.js +2 -3
- package/cjs/logger/messages/error.js +1 -1
- package/cjs/logger/messages/warn.js +2 -2
- package/cjs/readiness/readinessManager.js +6 -0
- package/cjs/sdkClient/client.js +29 -19
- package/cjs/sdkClient/clientAttributesDecoration.js +19 -25
- package/cjs/sdkClient/clientInputValidation.js +28 -26
- package/cjs/services/splitApi.js +2 -2
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -7
- package/cjs/storages/AbstractSplitsCacheSync.js +2 -12
- package/cjs/storages/KeyBuilder.js +0 -9
- package/cjs/storages/KeyBuilderCS.js +4 -4
- package/cjs/storages/KeyBuilderSS.js +0 -3
- package/cjs/storages/dataLoader.js +3 -2
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
- package/cjs/storages/inLocalStorage/index.js +5 -7
- package/cjs/storages/inLocalStorage/validateCache.js +79 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
- package/cjs/storages/inMemory/InMemoryStorageCS.js +0 -4
- package/cjs/storages/inRedis/index.js +0 -2
- package/cjs/storages/pluggable/index.js +2 -3
- package/cjs/storages/utils.js +1 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +33 -60
- package/cjs/sync/streaming/SSEHandler/index.js +0 -1
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +77 -106
- package/cjs/sync/streaming/constants.js +1 -2
- package/cjs/sync/streaming/pushManager.js +16 -3
- package/cjs/sync/submitters/impressionsSubmitter.js +3 -2
- package/cjs/sync/syncManagerOnline.js +10 -5
- package/cjs/trackers/strategy/strategyDebug.js +2 -0
- package/cjs/trackers/strategy/strategyOptimized.js +3 -0
- package/cjs/utils/constants/index.js +2 -3
- package/cjs/utils/inputValidation/eventProperties.js +12 -1
- package/cjs/utils/inputValidation/index.js +3 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +1 -1
- package/esm/evaluator/combiners/and.js +6 -2
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +5 -6
- package/esm/evaluator/index.js +7 -7
- package/esm/evaluator/matchers/index.js +1 -3
- package/esm/evaluator/matchers/matcherTypes.js +0 -1
- package/esm/evaluator/matchersTransform/index.js +0 -4
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +0 -1
- package/esm/logger/constants.js +0 -1
- package/esm/logger/messages/debug.js +2 -3
- package/esm/logger/messages/error.js +1 -1
- package/esm/logger/messages/warn.js +2 -2
- package/esm/readiness/readinessManager.js +6 -0
- package/esm/sdkClient/client.js +29 -19
- package/esm/sdkClient/clientAttributesDecoration.js +19 -25
- package/esm/sdkClient/clientInputValidation.js +29 -27
- package/esm/services/splitApi.js +2 -2
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -7
- package/esm/storages/AbstractSplitsCacheSync.js +2 -12
- package/esm/storages/KeyBuilder.js +0 -9
- package/esm/storages/KeyBuilderCS.js +4 -4
- package/esm/storages/KeyBuilderSS.js +0 -3
- package/esm/storages/dataLoader.js +2 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +23 -76
- package/esm/storages/inLocalStorage/index.js +5 -7
- package/esm/storages/inLocalStorage/validateCache.js +75 -0
- package/esm/storages/inMemory/InMemoryStorage.js +0 -3
- package/esm/storages/inMemory/InMemoryStorageCS.js +0 -4
- package/esm/storages/inRedis/index.js +0 -2
- package/esm/storages/pluggable/index.js +2 -3
- package/esm/storages/utils.js +1 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +3 -2
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +2 -2
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +34 -61
- package/esm/sync/streaming/SSEHandler/index.js +1 -2
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +73 -102
- package/esm/sync/streaming/constants.js +0 -1
- package/esm/sync/streaming/pushManager.js +19 -6
- package/esm/sync/submitters/impressionsSubmitter.js +3 -2
- package/esm/sync/syncManagerOnline.js +10 -5
- package/esm/trackers/strategy/strategyDebug.js +2 -0
- package/esm/trackers/strategy/strategyOptimized.js +3 -0
- package/esm/utils/constants/index.js +1 -2
- package/esm/utils/inputValidation/eventProperties.js +10 -0
- package/esm/utils/inputValidation/index.js +1 -0
- package/esm/utils/settingsValidation/storage/storageCS.js +1 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +8 -32
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/combiners/and.ts +4 -5
- package/src/evaluator/combiners/ifelseif.ts +9 -7
- package/src/evaluator/condition/engineUtils.ts +1 -1
- package/src/evaluator/condition/index.ts +12 -12
- package/src/evaluator/index.ts +7 -7
- package/src/evaluator/matchers/index.ts +1 -3
- package/src/evaluator/matchers/matcherTypes.ts +0 -1
- package/src/evaluator/matchersTransform/index.ts +0 -3
- package/src/evaluator/parser/index.ts +3 -3
- package/src/evaluator/types.ts +2 -2
- package/src/evaluator/value/index.ts +2 -2
- package/src/evaluator/value/sanitize.ts +4 -5
- package/src/logger/constants.ts +0 -1
- package/src/logger/messages/debug.ts +2 -3
- package/src/logger/messages/error.ts +1 -1
- package/src/logger/messages/warn.ts +2 -2
- package/src/readiness/readinessManager.ts +5 -0
- package/src/sdkClient/client.ts +31 -21
- package/src/sdkClient/clientAttributesDecoration.ts +20 -27
- package/src/sdkClient/clientInputValidation.ts +30 -27
- package/src/sdkManager/index.ts +1 -1
- package/src/services/splitApi.ts +2 -2
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheAsync.ts +0 -8
- package/src/storages/AbstractSplitsCacheSync.ts +3 -14
- package/src/storages/KeyBuilder.ts +0 -12
- package/src/storages/KeyBuilderCS.ts +5 -5
- package/src/storages/KeyBuilderSS.ts +0 -4
- package/src/storages/dataLoader.ts +3 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +26 -87
- package/src/storages/inLocalStorage/index.ts +8 -12
- package/src/storages/inLocalStorage/validateCache.ts +91 -0
- package/src/storages/inMemory/InMemoryStorage.ts +0 -3
- package/src/storages/inMemory/InMemoryStorageCS.ts +0 -4
- package/src/storages/inRedis/index.ts +0 -2
- package/src/storages/pluggable/index.ts +2 -3
- package/src/storages/types.ts +2 -37
- package/src/storages/utils.ts +1 -0
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +6 -5
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +1 -2
- package/src/sync/polling/fetchers/types.ts +0 -1
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/types.ts +2 -2
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -2
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +43 -71
- package/src/sync/streaming/SSEHandler/index.ts +1 -2
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +68 -98
- package/src/sync/streaming/constants.ts +0 -1
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +18 -6
- package/src/sync/streaming/types.ts +2 -3
- package/src/sync/submitters/impressionsSubmitter.ts +3 -2
- package/src/sync/submitters/types.ts +23 -33
- package/src/sync/syncManagerOnline.ts +11 -5
- package/src/trackers/strategy/strategyDebug.ts +2 -0
- package/src/trackers/strategy/strategyOptimized.ts +3 -0
- package/src/utils/constants/index.ts +1 -2
- package/src/utils/inputValidation/eventProperties.ts +10 -0
- package/src/utils/inputValidation/index.ts +1 -0
- package/src/utils/lang/index.ts +2 -2
- package/src/utils/settingsValidation/storage/storageCS.ts +1 -1
- package/types/splitio.d.ts +128 -36
- package/cjs/evaluator/matchers/rbsegment.js +0 -43
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -117
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +0 -61
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +0 -64
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +0 -64
- package/cjs/utils/constants/browser.js +0 -5
- package/esm/evaluator/matchers/rbsegment.js +0 -39
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +0 -114
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +0 -58
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +0 -61
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +0 -61
- package/esm/utils/constants/browser.js +0 -2
- package/src/evaluator/matchers/rbsegment.ts +0 -61
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +0 -136
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +0 -68
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +0 -79
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +0 -76
- package/src/utils/constants/browser.ts +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { errorParser, messageParser } from './NotificationParser';
|
|
2
2
|
import { notificationKeeperFactory } from './NotificationKeeper';
|
|
3
|
-
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE } from '../constants';
|
|
4
4
|
import { IPushEventEmitter } from '../types';
|
|
5
5
|
import { ISseEventHandler } from '../SSEClient/types';
|
|
6
6
|
import { INotificationError, INotificationMessage } from './types';
|
|
@@ -84,7 +84,6 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
|
|
|
84
84
|
case MEMBERSHIPS_MS_UPDATE:
|
|
85
85
|
case MEMBERSHIPS_LS_UPDATE:
|
|
86
86
|
case SPLIT_KILL:
|
|
87
|
-
case RB_SEGMENT_UPDATE:
|
|
88
87
|
pushEmitter.emit(parsedData.type, parsedData);
|
|
89
88
|
break;
|
|
90
89
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ControlType } from '../constants';
|
|
2
|
-
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE
|
|
2
|
+
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE } from '../types';
|
|
3
3
|
|
|
4
4
|
export enum Compression {
|
|
5
5
|
None = 0,
|
|
@@ -42,7 +42,7 @@ export interface ISegmentUpdateData {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export interface ISplitUpdateData {
|
|
45
|
-
type: SPLIT_UPDATE
|
|
45
|
+
type: SPLIT_UPDATE,
|
|
46
46
|
changeNumber: number,
|
|
47
47
|
pcn?: number,
|
|
48
48
|
d?: string,
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { STREAMING_PARSING_SPLIT_UPDATE } from '../../../logger/constants';
|
|
1
|
+
import { ISplit } from '../../../dtos/types';
|
|
3
2
|
import { ILogger } from '../../../logger/types';
|
|
4
3
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
5
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
6
|
-
import {
|
|
5
|
+
import { ISplitsCacheSync } from '../../../storages/types';
|
|
7
6
|
import { ITelemetryTracker } from '../../../trackers/types';
|
|
8
7
|
import { Backoff } from '../../../utils/Backoff';
|
|
9
8
|
import { SPLITS } from '../../../utils/constants';
|
|
10
9
|
import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
|
|
11
|
-
import { InstantUpdate } from '../../polling/updaters/splitChangesUpdater';
|
|
12
|
-
import { RB_SEGMENT_UPDATE } from '../constants';
|
|
13
|
-
import { parseFFUpdatePayload } from '../parseUtils';
|
|
14
10
|
import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
|
|
15
11
|
import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
|
|
16
12
|
import { IUpdateWorker } from './types';
|
|
@@ -18,128 +14,102 @@ import { IUpdateWorker } from './types';
|
|
|
18
14
|
/**
|
|
19
15
|
* SplitsUpdateWorker factory
|
|
20
16
|
*/
|
|
21
|
-
export function SplitsUpdateWorker(log: ILogger,
|
|
17
|
+
export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData, payload?: ISplit]> & { killSplit(event: ISplitKillData): void } {
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
let maxChangeNumber = 0;
|
|
20
|
+
let handleNewEvent = false;
|
|
21
|
+
let isHandlingEvent: boolean;
|
|
22
|
+
let cdnBypass: boolean;
|
|
23
|
+
let payload: ISplit | undefined;
|
|
24
|
+
const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
|
|
25
25
|
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
function __handleSplitUpdateCall() {
|
|
27
|
+
isHandlingEvent = true;
|
|
28
|
+
if (maxChangeNumber > splitsCache.getChangeNumber()) {
|
|
29
|
+
handleNewEvent = false;
|
|
30
|
+
const splitUpdateNotification = payload ? { payload, changeNumber: maxChangeNumber } : undefined;
|
|
31
|
+
// fetch splits revalidating data if cached
|
|
32
|
+
splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, splitUpdateNotification).then(() => {
|
|
33
|
+
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
34
|
+
if (handleNewEvent) {
|
|
35
|
+
__handleSplitUpdateCall();
|
|
36
|
+
} else {
|
|
37
|
+
if (splitUpdateNotification) telemetryTracker.trackUpdatesFromSSE(SPLITS);
|
|
38
|
+
// fetch new registered segments for server-side API. Not retrying on error
|
|
39
|
+
if (segmentsSyncTask) segmentsSyncTask.execute(true);
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
isHandlingEvent = true;
|
|
36
|
-
if (maxChangeNumber > cache.getChangeNumber()) {
|
|
37
|
-
handleNewEvent = false;
|
|
38
|
-
// fetch splits revalidating data if cached
|
|
39
|
-
splitsSyncTask.execute(true, cdnBypass ? maxChangeNumber : undefined, instantUpdate).then(() => {
|
|
40
|
-
if (!isHandlingEvent) return; // halt if `stop` has been called
|
|
41
|
-
if (handleNewEvent) {
|
|
42
|
-
__handleSplitUpdateCall();
|
|
43
|
-
} else {
|
|
44
|
-
if (instantUpdate) telemetryTracker.trackUpdatesFromSSE(SPLITS);
|
|
45
|
-
// fetch new registered segments for server-side API. Not retrying on error
|
|
46
|
-
if (segmentsSyncTask) segmentsSyncTask.execute(true);
|
|
47
|
-
|
|
48
|
-
const attempts = backoff.attempts + 1;
|
|
41
|
+
const attempts = backoff.attempts + 1;
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
if (maxChangeNumber <= splitsCache.getChangeNumber()) {
|
|
44
|
+
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
45
|
+
isHandlingEvent = false;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
55
48
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
|
|
50
|
+
backoff.scheduleCall();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
54
|
+
if (cdnBypass) {
|
|
55
|
+
log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
|
|
56
|
+
isHandlingEvent = false;
|
|
57
|
+
} else {
|
|
58
|
+
backoff.reset();
|
|
59
|
+
cdnBypass = true;
|
|
60
|
+
__handleSplitUpdateCall();
|
|
69
61
|
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
isHandlingEvent = false;
|
|
74
66
|
}
|
|
67
|
+
}
|
|
75
68
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const currentChangeNumber = cache.getChangeNumber();
|
|
69
|
+
/**
|
|
70
|
+
* Invoked by NotificationProcessor on SPLIT_UPDATE event
|
|
71
|
+
*
|
|
72
|
+
* @param changeNumber - change number of the SPLIT_UPDATE notification
|
|
73
|
+
*/
|
|
74
|
+
function put({ changeNumber, pcn }: ISplitUpdateData, _payload?: ISplit) {
|
|
75
|
+
const currentChangeNumber = splitsCache.getChangeNumber();
|
|
84
76
|
|
|
85
|
-
|
|
77
|
+
if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
|
|
86
78
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
maxChangeNumber = changeNumber;
|
|
80
|
+
handleNewEvent = true;
|
|
81
|
+
cdnBypass = false;
|
|
82
|
+
payload = undefined;
|
|
91
83
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
if (_payload && currentChangeNumber === pcn) {
|
|
85
|
+
payload = _payload;
|
|
86
|
+
}
|
|
95
87
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
stop() {
|
|
100
|
-
isHandlingEvent = false;
|
|
101
|
-
backoff.reset();
|
|
102
|
-
},
|
|
103
|
-
isSync() {
|
|
104
|
-
return maxChangeNumber <= cache.getChangeNumber();
|
|
105
|
-
}
|
|
106
|
-
};
|
|
88
|
+
if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
|
|
89
|
+
backoff.reset();
|
|
107
90
|
}
|
|
108
91
|
|
|
109
92
|
return {
|
|
110
|
-
put
|
|
111
|
-
if (parsedData.d && parsedData.c !== undefined) {
|
|
112
|
-
try {
|
|
113
|
-
const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
|
|
114
|
-
if (payload) {
|
|
115
|
-
(parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData, payload);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
} catch (e) {
|
|
119
|
-
log.warn(STREAMING_PARSING_SPLIT_UPDATE, [parsedData.type, e]);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
(parsedData.type === RB_SEGMENT_UPDATE ? rbs : ff).put(parsedData);
|
|
123
|
-
},
|
|
93
|
+
put,
|
|
124
94
|
/**
|
|
125
95
|
* Invoked by NotificationProcessor on SPLIT_KILL event
|
|
126
96
|
*
|
|
127
|
-
* @param changeNumber - change number of the notification
|
|
97
|
+
* @param changeNumber - change number of the SPLIT_UPDATE notification
|
|
128
98
|
* @param splitName - name of split to kill
|
|
129
99
|
* @param defaultTreatment - default treatment value
|
|
130
100
|
*/
|
|
131
101
|
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
132
|
-
if (
|
|
102
|
+
if (splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
133
103
|
// trigger an SDK_UPDATE if Split was killed locally
|
|
134
104
|
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
135
105
|
}
|
|
136
106
|
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
137
|
-
|
|
107
|
+
put({ changeNumber } as ISplitUpdateData);
|
|
138
108
|
},
|
|
139
109
|
|
|
140
110
|
stop() {
|
|
141
|
-
|
|
142
|
-
|
|
111
|
+
isHandlingEvent = false;
|
|
112
|
+
backoff.reset();
|
|
143
113
|
}
|
|
144
114
|
};
|
|
145
115
|
}
|
|
@@ -30,7 +30,6 @@ export const MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
|
|
|
30
30
|
export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
31
31
|
export const SPLIT_KILL = 'SPLIT_KILL';
|
|
32
32
|
export const SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
33
|
-
export const RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
|
|
34
33
|
|
|
35
34
|
// Control-type push notifications, handled by NotificationKeeper
|
|
36
35
|
export const CONTROL = 'CONTROL';
|
|
@@ -2,7 +2,7 @@ import { algorithms } from '../../utils/decompress';
|
|
|
2
2
|
import { decodeFromBase64 } from '../../utils/base64';
|
|
3
3
|
import { hash } from '../../utils/murmur3/murmur3';
|
|
4
4
|
import { Compression, IMembershipMSUpdateData, KeyList } from './SSEHandler/types';
|
|
5
|
-
import {
|
|
5
|
+
import { ISplit } from '../../dtos/types';
|
|
6
6
|
|
|
7
7
|
const GZIP = 1;
|
|
8
8
|
const ZLIB = 2;
|
|
@@ -82,7 +82,7 @@ export function isInBitmap(bitmap: Uint8Array, hash64hex: string) {
|
|
|
82
82
|
/**
|
|
83
83
|
* Parse feature flags notifications for instant feature flag updates
|
|
84
84
|
*/
|
|
85
|
-
export function parseFFUpdatePayload(compression: Compression, data: string): ISplit |
|
|
85
|
+
export function parseFFUpdatePayload(compression: Compression, data: string): ISplit | undefined {
|
|
86
86
|
return compression > 0 ?
|
|
87
87
|
parseKeyList(data, compression, false) :
|
|
88
88
|
JSON.parse(decodeFromBase64(data));
|
|
@@ -11,10 +11,10 @@ import { authenticateFactory, hashUserKey } from './AuthClient';
|
|
|
11
11
|
import { forOwn } from '../../utils/lang';
|
|
12
12
|
import { SSEClient } from './SSEClient';
|
|
13
13
|
import { getMatching } from '../../utils/key';
|
|
14
|
-
import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE,
|
|
15
|
-
import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE } from '../../logger/constants';
|
|
14
|
+
import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, PUSH_RETRYABLE_ERROR, PUSH_SUBSYSTEM_UP, ControlType } from './constants';
|
|
15
|
+
import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE, STREAMING_PARSING_SPLIT_UPDATE } from '../../logger/constants';
|
|
16
16
|
import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types';
|
|
17
|
-
import { getDelay, isInBitmap, parseBitmap, parseKeyList } from './parseUtils';
|
|
17
|
+
import { getDelay, isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils';
|
|
18
18
|
import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64';
|
|
19
19
|
import { IAuthTokenPushEnabled } from './AuthClient/types';
|
|
20
20
|
import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants';
|
|
@@ -56,7 +56,7 @@ export function pushManagerFactory(
|
|
|
56
56
|
// MySegmentsUpdateWorker (client-side) are initiated in `add` method
|
|
57
57
|
const segmentsUpdateWorker = userKey ? undefined : SegmentsUpdateWorker(log, pollingManager.segmentsSyncTask as ISegmentsSyncTask, storage.segments);
|
|
58
58
|
// For server-side we pass the segmentsSyncTask, used by SplitsUpdateWorker to fetch new segments
|
|
59
|
-
const splitsUpdateWorker = SplitsUpdateWorker(log, storage, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
|
|
59
|
+
const splitsUpdateWorker = SplitsUpdateWorker(log, storage.splits, pollingManager.splitsSyncTask, readiness.splits, telemetryTracker, userKey ? undefined : pollingManager.segmentsSyncTask as ISegmentsSyncTask);
|
|
60
60
|
|
|
61
61
|
// [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker
|
|
62
62
|
const userKeyHashes: Record<string, string> = {};
|
|
@@ -219,8 +219,20 @@ export function pushManagerFactory(
|
|
|
219
219
|
/** Functions related to synchronization (Queues and Workers in the spec) */
|
|
220
220
|
|
|
221
221
|
pushEmitter.on(SPLIT_KILL, splitsUpdateWorker.killSplit);
|
|
222
|
-
pushEmitter.on(SPLIT_UPDATE,
|
|
223
|
-
|
|
222
|
+
pushEmitter.on(SPLIT_UPDATE, (parsedData) => {
|
|
223
|
+
if (parsedData.d && parsedData.c !== undefined) {
|
|
224
|
+
try {
|
|
225
|
+
const payload = parseFFUpdatePayload(parsedData.c, parsedData.d);
|
|
226
|
+
if (payload) {
|
|
227
|
+
splitsUpdateWorker.put(parsedData, payload);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
} catch (e) {
|
|
231
|
+
log.warn(STREAMING_PARSING_SPLIT_UPDATE, [e]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
splitsUpdateWorker.put(parsedData);
|
|
235
|
+
});
|
|
224
236
|
|
|
225
237
|
function handleMySegmentsUpdate(parsedData: IMembershipMSUpdateData | IMembershipLSUpdateData) {
|
|
226
238
|
switch (parsedData.u) {
|
|
@@ -16,19 +16,18 @@ export type MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
|
|
|
16
16
|
export type SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
17
17
|
export type SPLIT_KILL = 'SPLIT_KILL';
|
|
18
18
|
export type SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
19
|
-
export type RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
|
|
20
19
|
|
|
21
20
|
// Control-type push notifications, handled by NotificationKeeper
|
|
22
21
|
export type CONTROL = 'CONTROL';
|
|
23
22
|
export type OCCUPANCY = 'OCCUPANCY';
|
|
24
23
|
|
|
25
|
-
export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL |
|
|
24
|
+
export type IPushEvent = PUSH_SUBSYSTEM_UP | PUSH_SUBSYSTEM_DOWN | PUSH_NONRETRYABLE_ERROR | PUSH_RETRYABLE_ERROR | MEMBERSHIPS_MS_UPDATE | MEMBERSHIPS_LS_UPDATE | SEGMENT_UPDATE | SPLIT_UPDATE | SPLIT_KILL | ControlType.STREAMING_RESET
|
|
26
25
|
|
|
27
26
|
type IParsedData<T extends IPushEvent> =
|
|
28
27
|
T extends MEMBERSHIPS_MS_UPDATE ? IMembershipMSUpdateData :
|
|
29
28
|
T extends MEMBERSHIPS_LS_UPDATE ? IMembershipLSUpdateData :
|
|
30
29
|
T extends SEGMENT_UPDATE ? ISegmentUpdateData :
|
|
31
|
-
T extends SPLIT_UPDATE
|
|
30
|
+
T extends SPLIT_UPDATE ? ISplitUpdateData :
|
|
32
31
|
T extends SPLIT_KILL ? ISplitKillData : INotificationData;
|
|
33
32
|
|
|
34
33
|
/**
|
|
@@ -25,8 +25,9 @@ export function fromImpressionsCollector(sendLabels: boolean, data: SplitIO.Impr
|
|
|
25
25
|
m: entry.time, // Timestamp
|
|
26
26
|
c: entry.changeNumber, // ChangeNumber
|
|
27
27
|
r: sendLabels ? entry.label : undefined, // Rule
|
|
28
|
-
b: entry.bucketingKey
|
|
29
|
-
pt: entry.pt
|
|
28
|
+
b: entry.bucketingKey, // Bucketing Key
|
|
29
|
+
pt: entry.pt, // Previous time
|
|
30
|
+
properties: entry.properties // Properties
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
return keyImpression;
|
|
@@ -3,26 +3,30 @@ import { IMetadata } from '../../dtos/types';
|
|
|
3
3
|
import SplitIO from '../../../types/splitio';
|
|
4
4
|
import { ISyncTask } from '../types';
|
|
5
5
|
|
|
6
|
+
type ImpressionPayload = {
|
|
7
|
+
/** Matching Key */
|
|
8
|
+
k: string;
|
|
9
|
+
/** Bucketing Key */
|
|
10
|
+
b?: string;
|
|
11
|
+
/** Treatment */
|
|
12
|
+
t: string;
|
|
13
|
+
/** Timestamp */
|
|
14
|
+
m: number;
|
|
15
|
+
/** Change number */
|
|
16
|
+
c: number;
|
|
17
|
+
/** Rule label */
|
|
18
|
+
r?: string;
|
|
19
|
+
/** Previous time */
|
|
20
|
+
pt?: number;
|
|
21
|
+
/** Stringified JSON object with properties */
|
|
22
|
+
properties?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
6
25
|
export type ImpressionsPayload = {
|
|
7
26
|
/** Split name */
|
|
8
27
|
f: string,
|
|
9
28
|
/** Key Impressions */
|
|
10
|
-
i:
|
|
11
|
-
/** User Key */
|
|
12
|
-
k: string;
|
|
13
|
-
/** Treatment */
|
|
14
|
-
t: string;
|
|
15
|
-
/** Timestamp */
|
|
16
|
-
m: number;
|
|
17
|
-
/** ChangeNumber */
|
|
18
|
-
c: number;
|
|
19
|
-
/** Rule label */
|
|
20
|
-
r?: string;
|
|
21
|
-
/** Bucketing Key */
|
|
22
|
-
b?: string;
|
|
23
|
-
/** Previous time */
|
|
24
|
-
pt?: number;
|
|
25
|
-
}[]
|
|
29
|
+
i: ImpressionPayload[]
|
|
26
30
|
}[]
|
|
27
31
|
|
|
28
32
|
export type ImpressionCountsPayload = {
|
|
@@ -60,23 +64,9 @@ export type StoredImpressionWithMetadata = {
|
|
|
60
64
|
/** Metadata */
|
|
61
65
|
m: IMetadata,
|
|
62
66
|
/** Stored impression */
|
|
63
|
-
i: {
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
/** bucketingKey */
|
|
67
|
-
b?: string,
|
|
68
|
-
/** Split name */
|
|
69
|
-
f: string,
|
|
70
|
-
/** treatment */
|
|
71
|
-
t: string,
|
|
72
|
-
/** label */
|
|
73
|
-
r: string,
|
|
74
|
-
/** changeNumber */
|
|
75
|
-
c: number,
|
|
76
|
-
/** time */
|
|
77
|
-
m: number
|
|
78
|
-
/** previous time */
|
|
79
|
-
pt?: number
|
|
67
|
+
i: ImpressionPayload & {
|
|
68
|
+
/** Feature flag name */
|
|
69
|
+
f: string
|
|
80
70
|
}
|
|
81
71
|
}
|
|
82
72
|
|
|
@@ -9,6 +9,7 @@ import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '..
|
|
|
9
9
|
import { isConsentGranted } from '../consent';
|
|
10
10
|
import { POLLING, STREAMING, SYNC_MODE_UPDATE } from '../utils/constants';
|
|
11
11
|
import { ISdkFactoryContextSync } from '../sdkFactory/types';
|
|
12
|
+
import { SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Online SyncManager factory.
|
|
@@ -28,7 +29,7 @@ export function syncManagerOnlineFactory(
|
|
|
28
29
|
*/
|
|
29
30
|
return function (params: ISdkFactoryContextSync): ISyncManagerCS {
|
|
30
31
|
|
|
31
|
-
const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } },
|
|
32
|
+
const { settings, settings: { log, streamingEnabled, sync: { enabled: syncEnabled } }, telemetryTracker, storage, readiness } = params;
|
|
32
33
|
|
|
33
34
|
/** Polling Manager */
|
|
34
35
|
const pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
@@ -87,6 +88,11 @@ export function syncManagerOnlineFactory(
|
|
|
87
88
|
start() {
|
|
88
89
|
running = true;
|
|
89
90
|
|
|
91
|
+
if (startFirstTime) {
|
|
92
|
+
const isCacheLoaded = storage.validateCache ? storage.validateCache() : false;
|
|
93
|
+
if (isCacheLoaded) Promise.resolve().then(() => { readiness.splits.emit(SDK_SPLITS_CACHE_LOADED); });
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
// start syncing splits and segments
|
|
91
97
|
if (pollingManager) {
|
|
92
98
|
|
|
@@ -96,7 +102,6 @@ export function syncManagerOnlineFactory(
|
|
|
96
102
|
// Doesn't call `syncAll` when the syncManager is resuming
|
|
97
103
|
if (startFirstTime) {
|
|
98
104
|
pollingManager.syncAll();
|
|
99
|
-
startFirstTime = false;
|
|
100
105
|
}
|
|
101
106
|
pushManager.start();
|
|
102
107
|
} else {
|
|
@@ -105,13 +110,14 @@ export function syncManagerOnlineFactory(
|
|
|
105
110
|
} else {
|
|
106
111
|
if (startFirstTime) {
|
|
107
112
|
pollingManager.syncAll();
|
|
108
|
-
startFirstTime = false;
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
// start periodic data recording (events, impressions, telemetry).
|
|
114
118
|
submitterManager.start(!isConsentGranted(settings));
|
|
119
|
+
|
|
120
|
+
startFirstTime = false;
|
|
115
121
|
},
|
|
116
122
|
|
|
117
123
|
/**
|
|
@@ -149,14 +155,14 @@ export function syncManagerOnlineFactory(
|
|
|
149
155
|
if (pushManager) {
|
|
150
156
|
if (pollingManager.isRunning()) {
|
|
151
157
|
// if doing polling, we must start the periodic fetch of data
|
|
152
|
-
if (storage.splits.usesSegments()
|
|
158
|
+
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
153
159
|
} else {
|
|
154
160
|
// if not polling, we must execute the sync task for the initial fetch
|
|
155
161
|
// of segments since `syncAll` was already executed when starting the main client
|
|
156
162
|
mySegmentsSyncTask.execute();
|
|
157
163
|
}
|
|
158
164
|
} else {
|
|
159
|
-
if (storage.splits.usesSegments()
|
|
165
|
+
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
160
166
|
}
|
|
161
167
|
} else {
|
|
162
168
|
if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
|
|
@@ -18,6 +18,9 @@ export function strategyOptimizedFactory(
|
|
|
18
18
|
|
|
19
19
|
return {
|
|
20
20
|
process(impression: SplitIO.ImpressionDTO) {
|
|
21
|
+
// DEBUG mode without previous time, for impressions with properties
|
|
22
|
+
if (impression.properties) return true;
|
|
23
|
+
|
|
21
24
|
impression.pt = impressionsObserver.testAndSet(impression);
|
|
22
25
|
|
|
23
26
|
const now = Date.now();
|
|
@@ -104,9 +104,8 @@ export const DISABLED = 0;
|
|
|
104
104
|
export const ENABLED = 1;
|
|
105
105
|
export const PAUSED = 2;
|
|
106
106
|
|
|
107
|
-
export const FLAG_SPEC_VERSION = '1.
|
|
107
|
+
export const FLAG_SPEC_VERSION = '1.2';
|
|
108
108
|
|
|
109
109
|
// Matcher types
|
|
110
110
|
export const IN_SEGMENT = 'IN_SEGMENT';
|
|
111
111
|
export const IN_LARGE_SEGMENT = 'IN_LARGE_SEGMENT';
|
|
112
|
-
export const IN_RULE_BASED_SEGMENT = 'IN_RULE_BASED_SEGMENT';
|
|
@@ -66,3 +66,13 @@ export function validateEventProperties(log: ILogger, maybeProperties: any, meth
|
|
|
66
66
|
|
|
67
67
|
return output;
|
|
68
68
|
}
|
|
69
|
+
|
|
70
|
+
export function validateEvaluationOptions(log: ILogger, maybeOptions: any, method: string): SplitIO.EvaluationOptions | undefined {
|
|
71
|
+
if (isObject(maybeOptions)) {
|
|
72
|
+
const properties = validateEventProperties(log, maybeOptions.properties, method).properties;
|
|
73
|
+
return properties && Object.keys(properties).length > 0 ? { properties } : undefined;
|
|
74
|
+
} else if (maybeOptions) {
|
|
75
|
+
log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'evaluation options']);
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
@@ -11,3 +11,4 @@ export { validateIfNotDestroyed, validateIfOperational } from './isOperational';
|
|
|
11
11
|
export { validateSplitExistence } from './splitExistence';
|
|
12
12
|
export { validateTrafficTypeExistence } from './trafficTypeExistence';
|
|
13
13
|
export { validatePreloadedData } from './preloadedData';
|
|
14
|
+
export { validateEvaluationOptions } from './eventProperties';
|
package/src/utils/lang/index.ts
CHANGED
|
@@ -111,7 +111,7 @@ export function groupBy<T extends Record<string, any>>(source: T[], prop: string
|
|
|
111
111
|
/**
|
|
112
112
|
* Checks if a given value is a boolean.
|
|
113
113
|
*/
|
|
114
|
-
export function isBoolean(val: any):
|
|
114
|
+
export function isBoolean(val: any): boolean {
|
|
115
115
|
return val === true || val === false;
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -120,7 +120,7 @@ export function isBoolean(val: any): val is boolean {
|
|
|
120
120
|
* Unlike `Number.isFinite`, it also tests Number object instances.
|
|
121
121
|
* Unlike global `isFinite`, it returns false if the value is not a number or Number object instance.
|
|
122
122
|
*/
|
|
123
|
-
export function isFiniteNumber(val: any):
|
|
123
|
+
export function isFiniteNumber(val: any): val is number {
|
|
124
124
|
if (val instanceof Number) val = val.valueOf();
|
|
125
125
|
return typeof val === 'number' ?
|
|
126
126
|
Number.isFinite ? Number.isFinite(val) : isFinite(val) :
|
|
@@ -8,7 +8,7 @@ import { IStorageFactoryParams, IStorageSync } from '../../../storages/types';
|
|
|
8
8
|
|
|
9
9
|
export function __InLocalStorageMockFactory(params: IStorageFactoryParams): IStorageSync {
|
|
10
10
|
const result = InMemoryStorageCSFactory(params);
|
|
11
|
-
result.
|
|
11
|
+
result.validateCache = () => true; // to emit SDK_READY_FROM_CACHE
|
|
12
12
|
return result;
|
|
13
13
|
}
|
|
14
14
|
__InLocalStorageMockFactory.type = STORAGE_MEMORY;
|