@splitsoftware/splitio-commons 2.2.1-rc.3 → 2.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.
- package/CHANGES.txt +5 -2
- package/README.md +1 -0
- package/cjs/consent/sdkUserConsent.js +5 -3
- package/cjs/evaluator/combiners/and.js +2 -6
- package/cjs/evaluator/combiners/ifelseif.js +6 -6
- package/cjs/evaluator/condition/index.js +6 -5
- package/cjs/evaluator/index.js +7 -7
- package/cjs/evaluator/matchers/index.js +3 -1
- package/cjs/evaluator/matchers/matcherTypes.js +1 -0
- package/cjs/evaluator/matchers/rbsegment.js +56 -0
- package/cjs/evaluator/matchersTransform/index.js +4 -0
- package/cjs/evaluator/parser/index.js +2 -2
- package/cjs/evaluator/value/sanitize.js +1 -0
- package/cjs/listeners/browser.js +2 -5
- package/cjs/logger/constants.js +4 -3
- package/cjs/logger/messages/debug.js +3 -2
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/services/splitApi.js +3 -4
- package/cjs/services/splitHttpClient.js +3 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +5 -2
- package/cjs/storages/KeyBuilder.js +9 -0
- package/cjs/storages/KeyBuilderCS.js +3 -0
- package/cjs/storages/KeyBuilderSS.js +3 -0
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +117 -0
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
- package/cjs/storages/inLocalStorage/index.js +5 -1
- package/cjs/storages/inLocalStorage/validateCache.js +2 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +3 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +61 -0
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +1 -0
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +64 -0
- package/cjs/storages/inRedis/index.js +5 -3
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +64 -0
- package/cjs/storages/pluggable/index.js +2 -0
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +54 -4
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- 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 +59 -33
- package/cjs/sync/streaming/SSEHandler/index.js +1 -0
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +106 -77
- package/cjs/sync/streaming/constants.js +2 -1
- package/cjs/sync/streaming/pushManager.js +3 -16
- package/cjs/sync/syncManagerOnline.js +2 -2
- package/cjs/utils/constants/index.js +6 -2
- package/esm/consent/sdkUserConsent.js +5 -3
- package/esm/evaluator/combiners/and.js +2 -6
- package/esm/evaluator/combiners/ifelseif.js +7 -7
- package/esm/evaluator/condition/index.js +6 -5
- package/esm/evaluator/index.js +7 -7
- package/esm/evaluator/matchers/index.js +3 -1
- package/esm/evaluator/matchers/matcherTypes.js +1 -0
- package/esm/evaluator/matchers/rbsegment.js +52 -0
- package/esm/evaluator/matchersTransform/index.js +4 -0
- package/esm/evaluator/parser/index.js +2 -2
- package/esm/evaluator/value/sanitize.js +1 -0
- package/esm/listeners/browser.js +2 -5
- package/esm/logger/constants.js +1 -0
- package/esm/logger/messages/debug.js +3 -2
- package/esm/logger/messages/warn.js +1 -1
- package/esm/services/splitApi.js +3 -4
- package/esm/services/splitHttpClient.js +3 -1
- package/esm/storages/AbstractSplitsCacheSync.js +5 -2
- package/esm/storages/KeyBuilder.js +9 -0
- package/esm/storages/KeyBuilderCS.js +3 -0
- package/esm/storages/KeyBuilderSS.js +3 -0
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +114 -0
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +9 -14
- package/esm/storages/inLocalStorage/index.js +5 -1
- package/esm/storages/inLocalStorage/validateCache.js +2 -1
- package/esm/storages/inMemory/InMemoryStorage.js +3 -0
- package/esm/storages/inMemory/InMemoryStorageCS.js +4 -0
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +58 -0
- package/esm/storages/inMemory/SplitsCacheInMemory.js +1 -0
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +61 -0
- package/esm/storages/inRedis/index.js +5 -3
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +61 -0
- package/esm/storages/pluggable/index.js +2 -0
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +54 -4
- package/esm/sync/polling/pollingManagerCS.js +7 -7
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- 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 +59 -33
- package/esm/sync/streaming/SSEHandler/index.js +2 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +102 -73
- package/esm/sync/streaming/constants.js +1 -0
- package/esm/sync/streaming/pushManager.js +6 -19
- package/esm/sync/syncManagerOnline.js +2 -2
- package/esm/utils/constants/index.js +5 -1
- package/package.json +1 -1
- package/src/consent/sdkUserConsent.ts +3 -2
- package/src/dtos/types.ts +37 -8
- package/src/evaluator/Engine.ts +1 -1
- package/src/evaluator/combiners/and.ts +5 -4
- package/src/evaluator/combiners/ifelseif.ts +7 -9
- 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 +3 -1
- package/src/evaluator/matchers/matcherTypes.ts +1 -0
- package/src/evaluator/matchers/rbsegment.ts +74 -0
- package/src/evaluator/matchersTransform/index.ts +3 -0
- 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 +5 -4
- package/src/listeners/browser.ts +2 -3
- package/src/logger/constants.ts +1 -0
- package/src/logger/messages/debug.ts +3 -2
- package/src/logger/messages/warn.ts +1 -1
- package/src/sdkManager/index.ts +1 -1
- package/src/services/splitApi.ts +3 -4
- package/src/services/splitHttpClient.ts +3 -1
- package/src/services/types.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +6 -3
- package/src/storages/KeyBuilder.ts +12 -0
- package/src/storages/KeyBuilderCS.ts +4 -0
- package/src/storages/KeyBuilderSS.ts +4 -0
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +136 -0
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +10 -14
- package/src/storages/inLocalStorage/index.ts +5 -1
- package/src/storages/inLocalStorage/validateCache.ts +3 -1
- package/src/storages/inMemory/InMemoryStorage.ts +3 -0
- package/src/storages/inMemory/InMemoryStorageCS.ts +4 -0
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +68 -0
- package/src/storages/inMemory/SplitsCacheInMemory.ts +1 -0
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +79 -0
- package/src/storages/inRedis/index.ts +5 -3
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +76 -0
- package/src/storages/pluggable/index.ts +2 -0
- package/src/storages/types.ts +33 -1
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +65 -4
- package/src/sync/polling/fetchers/types.ts +1 -0
- package/src/sync/polling/pollingManagerCS.ts +7 -7
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -1
- 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 +70 -43
- package/src/sync/streaming/SSEHandler/index.ts +2 -1
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +98 -68
- package/src/sync/streaming/constants.ts +1 -0
- package/src/sync/streaming/parseUtils.ts +2 -2
- package/src/sync/streaming/pushManager.ts +6 -18
- package/src/sync/streaming/types.ts +3 -2
- package/src/sync/syncManagerOnline.ts +2 -2
- package/src/utils/constants/index.ts +6 -1
- package/src/utils/lang/index.ts +1 -1
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { ISegmentsCacheBase, IStorageBase } from '../../../storages/types';
|
|
2
2
|
import { ISplitChangesFetcher } from '../fetchers/types';
|
|
3
|
-
import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types';
|
|
3
|
+
import { IRBSegment, ISplit, ISplitChangesResponse, ISplitFiltersValidation, MaybeThenable } from '../../../dtos/types';
|
|
4
4
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
5
|
import { timeout } from '../../../utils/promise/timeout';
|
|
6
6
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
|
-
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
8
|
+
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_UPDATE, SYNC_RBS_UPDATE, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
9
9
|
import { startsWith } from '../../../utils/lang';
|
|
10
|
-
import { IN_SEGMENT } from '../../../utils/constants';
|
|
10
|
+
import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT } from '../../../utils/constants';
|
|
11
11
|
import { setToArray } from '../../../utils/lang/sets';
|
|
12
|
+
import { SPLIT_UPDATE } from '../../streaming/constants';
|
|
12
13
|
|
|
13
|
-
type
|
|
14
|
+
export type InstantUpdate = { payload: ISplit | IRBSegment, changeNumber: number, type: string };
|
|
15
|
+
type SplitChangesUpdater = (noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) => Promise<boolean>
|
|
14
16
|
|
|
15
17
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
16
18
|
// Returns a promise that could be rejected.
|
|
@@ -24,27 +26,35 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise<boolean> {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
|
-
* Collect segments from a raw
|
|
29
|
+
* Collect segments from a raw FF or RBS definition.
|
|
28
30
|
* Exported for testing purposes.
|
|
29
31
|
*/
|
|
30
|
-
export function parseSegments(
|
|
31
|
-
|
|
32
|
+
export function parseSegments(ruleEntity: ISplit | IRBSegment, matcherType: typeof IN_SEGMENT | typeof IN_RULE_BASED_SEGMENT = IN_SEGMENT): Set<string> {
|
|
33
|
+
const { conditions = [], excluded } = ruleEntity as IRBSegment;
|
|
34
|
+
|
|
35
|
+
const segments = new Set<string>();
|
|
36
|
+
if (excluded && excluded.segments) {
|
|
37
|
+
excluded.segments.forEach(({ type, name }) => {
|
|
38
|
+
if ((type === STANDARD_SEGMENT && matcherType === IN_SEGMENT) || (type === RULE_BASED_SEGMENT && matcherType === IN_RULE_BASED_SEGMENT)) {
|
|
39
|
+
segments.add(name);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
32
43
|
|
|
33
44
|
for (let i = 0; i < conditions.length; i++) {
|
|
34
45
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
35
46
|
|
|
36
47
|
matchers.forEach(matcher => {
|
|
37
|
-
if (matcher.matcherType ===
|
|
48
|
+
if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName);
|
|
38
49
|
});
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
return segments;
|
|
42
53
|
}
|
|
43
54
|
|
|
44
|
-
interface ISplitMutations {
|
|
45
|
-
added:
|
|
46
|
-
removed:
|
|
47
|
-
segments: string[]
|
|
55
|
+
interface ISplitMutations<T extends ISplit | IRBSegment> {
|
|
56
|
+
added: T[],
|
|
57
|
+
removed: T[]
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
/**
|
|
@@ -73,25 +83,21 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) {
|
|
|
73
83
|
* i.e., an object with added splits, removed splits and used segments.
|
|
74
84
|
* Exported for testing purposes.
|
|
75
85
|
*/
|
|
76
|
-
export function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
accum.added.push(
|
|
86
|
+
export function computeMutation<T extends ISplit | IRBSegment>(rules: Array<T>, segments: Set<string>, filters?: ISplitFiltersValidation): ISplitMutations<T> {
|
|
87
|
+
|
|
88
|
+
return rules.reduce((accum, ruleEntity) => {
|
|
89
|
+
if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) {
|
|
90
|
+
accum.added.push(ruleEntity);
|
|
81
91
|
|
|
82
|
-
parseSegments(
|
|
92
|
+
parseSegments(ruleEntity).forEach((segmentName: string) => {
|
|
83
93
|
segments.add(segmentName);
|
|
84
94
|
});
|
|
85
95
|
} else {
|
|
86
|
-
accum.removed.push(
|
|
96
|
+
accum.removed.push(ruleEntity);
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
return accum;
|
|
90
|
-
}, { added: [], removed: []
|
|
91
|
-
|
|
92
|
-
computed.segments = setToArray(segments);
|
|
93
|
-
|
|
94
|
-
return computed;
|
|
100
|
+
}, { added: [], removed: [] } as ISplitMutations<T>);
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
/**
|
|
@@ -111,14 +117,14 @@ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersV
|
|
|
111
117
|
export function splitChangesUpdaterFactory(
|
|
112
118
|
log: ILogger,
|
|
113
119
|
splitChangesFetcher: ISplitChangesFetcher,
|
|
114
|
-
storage: Pick<IStorageBase, 'splits' | 'segments'>,
|
|
120
|
+
storage: Pick<IStorageBase, 'splits' | 'rbSegments' | 'segments'>,
|
|
115
121
|
splitFiltersValidation: ISplitFiltersValidation,
|
|
116
122
|
splitsEventEmitter?: ISplitsEventEmitter,
|
|
117
123
|
requestTimeoutBeforeReady: number = 0,
|
|
118
124
|
retriesOnFailureBeforeReady: number = 0,
|
|
119
125
|
isClientSide?: boolean
|
|
120
|
-
):
|
|
121
|
-
const { splits, segments } = storage;
|
|
126
|
+
): SplitChangesUpdater {
|
|
127
|
+
const { splits, rbSegments, segments } = storage;
|
|
122
128
|
|
|
123
129
|
let startingUp = true;
|
|
124
130
|
|
|
@@ -135,32 +141,53 @@ export function splitChangesUpdaterFactory(
|
|
|
135
141
|
* @param noCache - true to revalidate data to fetch
|
|
136
142
|
* @param till - query param to bypass CDN requests
|
|
137
143
|
*/
|
|
138
|
-
return function splitChangesUpdater(noCache?: boolean, till?: number,
|
|
144
|
+
return function splitChangesUpdater(noCache?: boolean, till?: number, instantUpdate?: InstantUpdate) {
|
|
139
145
|
|
|
140
146
|
/**
|
|
141
147
|
* @param since - current changeNumber at splitsCache
|
|
142
148
|
* @param retry - current number of retry attempts
|
|
143
149
|
*/
|
|
144
|
-
function _splitChangesUpdater(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
function _splitChangesUpdater(sinces: [number, number], retry = 0): Promise<boolean> {
|
|
151
|
+
const [since, rbSince] = sinces;
|
|
152
|
+
log.debug(SYNC_SPLITS_FETCH, sinces);
|
|
153
|
+
return Promise.resolve(
|
|
154
|
+
instantUpdate ?
|
|
155
|
+
instantUpdate.type === SPLIT_UPDATE ?
|
|
156
|
+
// IFFU edge case: a change to a flag that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
|
|
157
|
+
Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
|
|
158
|
+
return contains ?
|
|
159
|
+
{ ff: { d: [instantUpdate.payload as ISplit], t: instantUpdate.changeNumber } } :
|
|
160
|
+
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
|
|
161
|
+
}) :
|
|
162
|
+
{ rbs: { d: [instantUpdate.payload as IRBSegment], t: instantUpdate.changeNumber } } :
|
|
163
|
+
splitChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
|
|
149
164
|
)
|
|
150
165
|
.then((splitChanges: ISplitChangesResponse) => {
|
|
151
166
|
startingUp = false;
|
|
152
167
|
|
|
153
|
-
const
|
|
168
|
+
const usedSegments = new Set<string>();
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
let ffUpdate: MaybeThenable<boolean> = false;
|
|
171
|
+
if (splitChanges.ff) {
|
|
172
|
+
const { added, removed } = computeMutation(splitChanges.ff.d, usedSegments, splitFiltersValidation);
|
|
173
|
+
log.debug(SYNC_SPLITS_UPDATE, [added.length, removed.length]);
|
|
174
|
+
ffUpdate = splits.update(added, removed, splitChanges.ff.t);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let rbsUpdate: MaybeThenable<boolean> = false;
|
|
178
|
+
if (splitChanges.rbs) {
|
|
179
|
+
const { added, removed } = computeMutation(splitChanges.rbs.d, usedSegments);
|
|
180
|
+
log.debug(SYNC_RBS_UPDATE, [added.length, removed.length]);
|
|
181
|
+
rbsUpdate = rbSegments.update(added, removed, splitChanges.rbs.t);
|
|
182
|
+
}
|
|
156
183
|
|
|
157
|
-
return Promise.all([
|
|
158
|
-
|
|
159
|
-
segments.registerSegments(
|
|
160
|
-
]).then(([
|
|
184
|
+
return Promise.all([ffUpdate, rbsUpdate,
|
|
185
|
+
// @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
|
|
186
|
+
segments.registerSegments(setToArray(usedSegments))
|
|
187
|
+
]).then(([ffChanged, rbsChanged]) => {
|
|
161
188
|
if (splitsEventEmitter) {
|
|
162
189
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
163
|
-
return Promise.resolve(!splitsEventEmitter.splitsArrived || (
|
|
190
|
+
return Promise.resolve(!splitsEventEmitter.splitsArrived || ((ffChanged || rbsChanged) && (isClientSide || checkAllSegmentsExist(segments))))
|
|
164
191
|
.catch(() => false /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
165
192
|
.then(emitSplitsArrivedEvent => {
|
|
166
193
|
// emit SDK events
|
|
@@ -177,7 +204,7 @@ export function splitChangesUpdaterFactory(
|
|
|
177
204
|
if (startingUp && retriesOnFailureBeforeReady > retry) {
|
|
178
205
|
retry += 1;
|
|
179
206
|
log.info(SYNC_SPLITS_FETCH_RETRY, [retry, error]);
|
|
180
|
-
return _splitChangesUpdater(
|
|
207
|
+
return _splitChangesUpdater(sinces, retry);
|
|
181
208
|
} else {
|
|
182
209
|
startingUp = false;
|
|
183
210
|
}
|
|
@@ -185,7 +212,7 @@ export function splitChangesUpdaterFactory(
|
|
|
185
212
|
});
|
|
186
213
|
}
|
|
187
214
|
|
|
188
|
-
|
|
189
|
-
return
|
|
215
|
+
// `getChangeNumber` never rejects or throws error
|
|
216
|
+
return Promise.all([splits.getChangeNumber(), rbSegments.getChangeNumber()]).then(_splitChangesUpdater);
|
|
190
217
|
};
|
|
191
218
|
}
|
|
@@ -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 } from '../constants';
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
|
|
4
4
|
import { IPushEventEmitter } from '../types';
|
|
5
5
|
import { ISseEventHandler } from '../SSEClient/types';
|
|
6
6
|
import { INotificationError, INotificationMessage } from './types';
|
|
@@ -84,6 +84,7 @@ 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:
|
|
87
88
|
pushEmitter.emit(parsedData.type, parsedData);
|
|
88
89
|
break;
|
|
89
90
|
|
|
@@ -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 } from '../types';
|
|
2
|
+
import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE, RB_SEGMENT_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 | RB_SEGMENT_UPDATE,
|
|
46
46
|
changeNumber: number,
|
|
47
47
|
pcn?: number,
|
|
48
48
|
d?: string,
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { ISplit } from '../../../dtos/types';
|
|
1
|
+
import { IRBSegment, ISplit } from '../../../dtos/types';
|
|
2
|
+
import { STREAMING_PARSING_SPLIT_UPDATE } from '../../../logger/constants';
|
|
2
3
|
import { ILogger } from '../../../logger/types';
|
|
3
4
|
import { SDK_SPLITS_ARRIVED } from '../../../readiness/constants';
|
|
4
5
|
import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
5
|
-
import { ISplitsCacheSync } from '../../../storages/types';
|
|
6
|
+
import { IRBSegmentsCacheSync, ISplitsCacheSync, IStorageSync } from '../../../storages/types';
|
|
6
7
|
import { ITelemetryTracker } from '../../../trackers/types';
|
|
7
8
|
import { Backoff } from '../../../utils/Backoff';
|
|
8
9
|
import { SPLITS } from '../../../utils/constants';
|
|
9
10
|
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';
|
|
10
14
|
import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
|
|
11
15
|
import { FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT, FETCH_BACKOFF_MAX_RETRIES } from './constants';
|
|
12
16
|
import { IUpdateWorker } from './types';
|
|
@@ -14,102 +18,128 @@ import { IUpdateWorker } from './types';
|
|
|
14
18
|
/**
|
|
15
19
|
* SplitsUpdateWorker factory
|
|
16
20
|
*/
|
|
17
|
-
export function SplitsUpdateWorker(log: ILogger,
|
|
21
|
+
export function SplitsUpdateWorker(log: ILogger, storage: IStorageSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData]> & { killSplit(event: ISplitKillData): void } {
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
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);
|
|
23
|
+
const ff = SplitsUpdateWorker(storage.splits);
|
|
24
|
+
const rbs = SplitsUpdateWorker(storage.rbSegments);
|
|
25
25
|
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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);
|
|
26
|
+
function SplitsUpdateWorker(cache: ISplitsCacheSync | IRBSegmentsCacheSync) {
|
|
27
|
+
let maxChangeNumber = -1;
|
|
28
|
+
let handleNewEvent = false;
|
|
29
|
+
let isHandlingEvent: boolean;
|
|
30
|
+
let cdnBypass: boolean;
|
|
31
|
+
let instantUpdate: InstantUpdate | undefined;
|
|
32
|
+
const backoff = new Backoff(__handleSplitUpdateCall, FETCH_BACKOFF_BASE, FETCH_BACKOFF_MAX_WAIT);
|
|
40
33
|
|
|
41
|
-
|
|
34
|
+
function __handleSplitUpdateCall() {
|
|
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);
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
45
|
-
isHandlingEvent = false;
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
+
const attempts = backoff.attempts + 1;
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
if (ff.isSync() && rbs.isSync()) {
|
|
51
|
+
log.debug(`Refresh completed${cdnBypass ? ' bypassing the CDN' : ''} in ${attempts} attempts.`);
|
|
52
|
+
isHandlingEvent = false;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
cdnBypass
|
|
60
|
-
|
|
56
|
+
if (attempts < FETCH_BACKOFF_MAX_RETRIES) {
|
|
57
|
+
backoff.scheduleCall();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (cdnBypass) {
|
|
62
|
+
log.debug(`No changes fetched after ${attempts} attempts with CDN bypassed.`);
|
|
63
|
+
isHandlingEvent = false;
|
|
64
|
+
} else {
|
|
65
|
+
backoff.reset();
|
|
66
|
+
cdnBypass = true;
|
|
67
|
+
__handleSplitUpdateCall();
|
|
68
|
+
}
|
|
61
69
|
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
isHandlingEvent = false;
|
|
73
|
+
}
|
|
66
74
|
}
|
|
67
|
-
}
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
return {
|
|
77
|
+
/**
|
|
78
|
+
* Invoked by NotificationProcessor on SPLIT_UPDATE or RB_SEGMENT_UPDATE event
|
|
79
|
+
*
|
|
80
|
+
* @param changeNumber - change number of the notification
|
|
81
|
+
*/
|
|
82
|
+
put({ changeNumber, pcn, type }: ISplitUpdateData, payload?: ISplit | IRBSegment) {
|
|
83
|
+
const currentChangeNumber = cache.getChangeNumber();
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
maxChangeNumber = changeNumber;
|
|
88
|
+
handleNewEvent = true;
|
|
89
|
+
cdnBypass = false;
|
|
90
|
+
instantUpdate = undefined;
|
|
83
91
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
if (payload && currentChangeNumber === pcn) {
|
|
93
|
+
instantUpdate = { payload, changeNumber, type };
|
|
94
|
+
}
|
|
87
95
|
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
if (backoff.timeoutID || !isHandlingEvent) __handleSplitUpdateCall();
|
|
97
|
+
backoff.reset();
|
|
98
|
+
},
|
|
99
|
+
stop() {
|
|
100
|
+
isHandlingEvent = false;
|
|
101
|
+
backoff.reset();
|
|
102
|
+
},
|
|
103
|
+
isSync() {
|
|
104
|
+
return maxChangeNumber <= cache.getChangeNumber();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
90
107
|
}
|
|
91
108
|
|
|
92
109
|
return {
|
|
93
|
-
put
|
|
110
|
+
put(parsedData) {
|
|
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
|
+
},
|
|
94
124
|
/**
|
|
95
125
|
* Invoked by NotificationProcessor on SPLIT_KILL event
|
|
96
126
|
*
|
|
97
|
-
* @param changeNumber - change number of the
|
|
127
|
+
* @param changeNumber - change number of the notification
|
|
98
128
|
* @param splitName - name of split to kill
|
|
99
129
|
* @param defaultTreatment - default treatment value
|
|
100
130
|
*/
|
|
101
131
|
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
102
|
-
if (
|
|
132
|
+
if (storage.splits.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
103
133
|
// trigger an SDK_UPDATE if Split was killed locally
|
|
104
134
|
splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
105
135
|
}
|
|
106
136
|
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
107
|
-
put({ changeNumber } as ISplitUpdateData);
|
|
137
|
+
ff.put({ changeNumber } as ISplitUpdateData);
|
|
108
138
|
},
|
|
109
139
|
|
|
110
140
|
stop() {
|
|
111
|
-
|
|
112
|
-
|
|
141
|
+
ff.stop();
|
|
142
|
+
rbs.stop();
|
|
113
143
|
}
|
|
114
144
|
};
|
|
115
145
|
}
|
|
@@ -30,6 +30,7 @@ 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';
|
|
33
34
|
|
|
34
35
|
// Control-type push notifications, handled by NotificationKeeper
|
|
35
36
|
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 { ISplit } from '../../dtos/types';
|
|
5
|
+
import { IRBSegment, 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 | undefined {
|
|
85
|
+
export function parseFFUpdatePayload(compression: Compression, data: string): ISplit | IRBSegment | 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, 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
|
|
14
|
+
import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, PUSH_SUBSYSTEM_DOWN, SECONDS_BEFORE_EXPIRATION, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, RB_SEGMENT_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 } from '../../logger/constants';
|
|
16
16
|
import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types';
|
|
17
|
-
import { getDelay, isInBitmap, parseBitmap,
|
|
17
|
+
import { getDelay, isInBitmap, parseBitmap, 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
|
|
59
|
+
const splitsUpdateWorker = SplitsUpdateWorker(log, storage, 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,20 +219,8 @@ 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
|
-
|
|
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
|
-
});
|
|
222
|
+
pushEmitter.on(SPLIT_UPDATE, splitsUpdateWorker.put);
|
|
223
|
+
pushEmitter.on(RB_SEGMENT_UPDATE, splitsUpdateWorker.put);
|
|
236
224
|
|
|
237
225
|
function handleMySegmentsUpdate(parsedData: IMembershipMSUpdateData | IMembershipLSUpdateData) {
|
|
238
226
|
switch (parsedData.u) {
|
|
@@ -16,18 +16,19 @@ 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';
|
|
19
20
|
|
|
20
21
|
// Control-type push notifications, handled by NotificationKeeper
|
|
21
22
|
export type CONTROL = 'CONTROL';
|
|
22
23
|
export type OCCUPANCY = 'OCCUPANCY';
|
|
23
24
|
|
|
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
|
|
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 | RB_SEGMENT_UPDATE | ControlType.STREAMING_RESET
|
|
25
26
|
|
|
26
27
|
type IParsedData<T extends IPushEvent> =
|
|
27
28
|
T extends MEMBERSHIPS_MS_UPDATE ? IMembershipMSUpdateData :
|
|
28
29
|
T extends MEMBERSHIPS_LS_UPDATE ? IMembershipLSUpdateData :
|
|
29
30
|
T extends SEGMENT_UPDATE ? ISegmentUpdateData :
|
|
30
|
-
T extends SPLIT_UPDATE ? ISplitUpdateData :
|
|
31
|
+
T extends SPLIT_UPDATE | RB_SEGMENT_UPDATE ? ISplitUpdateData :
|
|
31
32
|
T extends SPLIT_KILL ? ISplitKillData : INotificationData;
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -155,14 +155,14 @@ export function syncManagerOnlineFactory(
|
|
|
155
155
|
if (pushManager) {
|
|
156
156
|
if (pollingManager.isRunning()) {
|
|
157
157
|
// if doing polling, we must start the periodic fetch of data
|
|
158
|
-
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
158
|
+
if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) mySegmentsSyncTask.start();
|
|
159
159
|
} else {
|
|
160
160
|
// if not polling, we must execute the sync task for the initial fetch
|
|
161
161
|
// of segments since `syncAll` was already executed when starting the main client
|
|
162
162
|
mySegmentsSyncTask.execute();
|
|
163
163
|
}
|
|
164
164
|
} else {
|
|
165
|
-
if (storage.splits.usesSegments()) mySegmentsSyncTask.start();
|
|
165
|
+
if (storage.splits.usesSegments() || storage.rbSegments.usesSegments()) mySegmentsSyncTask.start();
|
|
166
166
|
}
|
|
167
167
|
} else {
|
|
168
168
|
if (!readinessManager.isReady()) mySegmentsSyncTask.execute();
|
|
@@ -104,8 +104,13 @@ 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.3';
|
|
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';
|
|
113
|
+
|
|
114
|
+
export const STANDARD_SEGMENT = 'standard';
|
|
115
|
+
export const LARGE_SEGMENT = 'large';
|
|
116
|
+
export const RULE_BASED_SEGMENT = 'rule-based';
|
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): boolean {
|
|
114
|
+
export function isBoolean(val: any): val is boolean {
|
|
115
115
|
return val === true || val === false;
|
|
116
116
|
}
|
|
117
117
|
|