@splitsoftware/splitio-commons 0.1.1-canary.6 → 0.1.1-rc.18
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/evaluator/matchers/matcherTypes.js +4 -4
- package/cjs/evaluator/matchersTransform/index.js +11 -11
- package/cjs/evaluator/value/sanitize.js +6 -6
- package/cjs/listeners/browser.js +5 -2
- package/cjs/listeners/node.js +9 -2
- package/cjs/logger/constants.js +3 -1
- package/cjs/logger/messages/error.js +3 -2
- package/cjs/logger/messages/info.js +2 -2
- package/cjs/logger/messages/warn.js +2 -1
- package/cjs/readiness/readinessManager.js +10 -7
- package/cjs/sdkFactory/index.js +1 -1
- package/cjs/services/splitApi.js +9 -1
- package/cjs/services/splitHttpClient.js +5 -4
- package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
- package/cjs/storages/inLocalStorage/index.js +5 -2
- package/cjs/storages/inMemory/InMemoryStorage.js +2 -0
- package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -0
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +6 -2
- package/cjs/storages/inRedis/index.js +5 -2
- package/cjs/storages/pluggable/SplitsCachePluggable.js +6 -2
- package/cjs/storages/pluggable/inMemoryWrapper.js +6 -7
- package/cjs/storages/pluggable/index.js +5 -2
- package/cjs/storages/pluggable/wrapperAdapter.js +0 -1
- package/cjs/sync/offline/splitsParser/splitsParserFromFile.js +92 -89
- package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +45 -42
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +14 -4
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +0 -8
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +30 -10
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +2 -4
- package/cjs/sync/streaming/SSEClient/index.js +38 -20
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
- package/cjs/sync/streaming/SSEHandler/NotificationParser.js +4 -1
- package/cjs/sync/streaming/SSEHandler/index.js +8 -9
- package/cjs/sync/streaming/SSEHandler/types.js +14 -0
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
- package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
- package/cjs/sync/streaming/constants.js +3 -1
- package/cjs/sync/streaming/mySegmentsV2utils.js +75 -0
- package/cjs/sync/streaming/pushManager.js +148 -40
- package/cjs/sync/submitters/metricsSyncTask.js +1 -1
- package/cjs/sync/submitters/submitterSyncTask.js +2 -2
- package/cjs/sync/syncManagerFromFile.js +15 -0
- package/cjs/sync/syncManagerFromObject.js +14 -0
- package/cjs/sync/syncManagerOffline.js +3 -3
- package/cjs/sync/syncManagerOnline.js +18 -5
- package/cjs/sync/syncTask.js +1 -1
- package/cjs/trackers/impressionObserver/ImpressionObserver.js +0 -2
- package/cjs/trackers/impressionObserver/buildKey.js +3 -9
- package/cjs/trackers/impressionObserver/impressionObserverCS.js +2 -2
- package/cjs/trackers/impressionObserver/impressionObserverSS.js +3 -3
- package/cjs/utils/constants/index.js +4 -1
- package/cjs/utils/decompress/index.js +427 -0
- package/cjs/utils/murmur3/{commons.js → common.js} +2 -6
- package/cjs/utils/murmur3/murmur3.js +11 -12
- package/cjs/utils/murmur3/murmur3_128.js +7 -142
- package/cjs/utils/murmur3/murmur3_128_x86.js +154 -0
- package/cjs/utils/murmur3/murmur3_64.js +36 -0
- package/cjs/utils/murmur3/utfx.js +100 -106
- package/cjs/utils/promise/wrapper.js +14 -11
- package/cjs/utils/settingsValidation/index.js +5 -2
- package/cjs/utils/settingsValidation/localhost/index.js +20 -0
- package/cjs/utils/settingsValidation/splitFilters.js +0 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +18 -8
- package/cjs/utils/settingsValidation/url.js +1 -1
- package/esm/evaluator/matchers/matcherTypes.js +2 -2
- package/esm/evaluator/matchersTransform/index.js +12 -12
- package/esm/evaluator/value/sanitize.js +7 -7
- package/esm/listeners/browser.js +5 -2
- package/esm/listeners/node.js +9 -2
- package/esm/logger/constants.js +2 -0
- package/esm/logger/messages/error.js +3 -2
- package/esm/logger/messages/info.js +2 -2
- package/esm/logger/messages/warn.js +2 -1
- package/esm/readiness/readinessManager.js +10 -7
- package/esm/sdkFactory/index.js +1 -1
- package/esm/services/splitApi.js +9 -1
- package/esm/services/splitHttpClient.js +5 -4
- package/esm/storages/AbstractSplitsCacheSync.js +1 -1
- package/esm/storages/inLocalStorage/index.js +5 -2
- package/esm/storages/inMemory/InMemoryStorage.js +2 -0
- package/esm/storages/inMemory/InMemoryStorageCS.js +2 -0
- package/esm/storages/inRedis/SplitsCacheInRedis.js +6 -2
- package/esm/storages/inRedis/index.js +5 -2
- package/esm/storages/pluggable/SplitsCachePluggable.js +6 -2
- package/esm/storages/pluggable/inMemoryWrapper.js +6 -7
- package/esm/storages/pluggable/index.js +5 -2
- package/esm/storages/pluggable/wrapperAdapter.js +0 -1
- package/esm/sync/offline/splitsParser/splitsParserFromFile.js +90 -88
- package/esm/sync/offline/splitsParser/splitsParserFromSettings.js +43 -41
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +15 -5
- package/esm/sync/polling/fetchers/segmentChangesFetcher.js +0 -8
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +30 -10
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +2 -4
- package/esm/sync/streaming/SSEClient/index.js +38 -20
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +7 -0
- package/esm/sync/streaming/SSEHandler/NotificationParser.js +4 -1
- package/esm/sync/streaming/SSEHandler/index.js +9 -10
- package/esm/sync/streaming/SSEHandler/types.js +13 -1
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +5 -5
- package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +5 -3
- package/esm/sync/streaming/constants.js +2 -0
- package/esm/sync/streaming/mySegmentsV2utils.js +69 -0
- package/esm/sync/streaming/pushManager.js +150 -42
- package/esm/sync/submitters/metricsSyncTask.js +1 -1
- package/esm/sync/submitters/submitterSyncTask.js +2 -2
- package/esm/sync/syncManagerFromFile.js +11 -0
- package/esm/sync/syncManagerFromObject.js +10 -0
- package/esm/sync/syncManagerOffline.js +3 -3
- package/esm/sync/syncManagerOnline.js +18 -5
- package/esm/sync/syncTask.js +1 -1
- package/esm/trackers/impressionObserver/ImpressionObserver.js +0 -2
- package/esm/trackers/impressionObserver/buildKey.js +2 -9
- package/esm/trackers/impressionObserver/impressionObserverCS.js +2 -2
- package/esm/trackers/impressionObserver/impressionObserverSS.js +3 -3
- package/esm/utils/constants/index.js +3 -0
- package/esm/utils/decompress/index.js +424 -0
- package/esm/utils/murmur3/{commons.js → common.js} +1 -4
- package/esm/utils/murmur3/murmur3.js +1 -2
- package/esm/utils/murmur3/murmur3_128.js +7 -142
- package/esm/utils/murmur3/murmur3_128_x86.js +150 -0
- package/esm/utils/murmur3/murmur3_64.js +32 -0
- package/esm/utils/murmur3/utfx.js +96 -106
- package/esm/utils/promise/wrapper.js +14 -11
- package/esm/utils/settingsValidation/index.js +5 -2
- package/esm/utils/settingsValidation/localhost/index.js +16 -0
- package/esm/utils/settingsValidation/splitFilters.js +0 -1
- package/esm/utils/settingsValidation/storage/storageCS.js +16 -7
- package/esm/utils/settingsValidation/url.js +1 -1
- package/package.json +5 -5
- package/src/evaluator/matchers/matcherTypes.ts +2 -2
- package/src/evaluator/matchersTransform/index.ts +12 -12
- package/src/evaluator/value/sanitize.ts +7 -7
- package/src/listeners/browser.ts +5 -2
- package/src/listeners/node.ts +14 -2
- package/src/logger/constants.ts +2 -0
- package/src/logger/messages/error.ts +3 -2
- package/src/logger/messages/info.ts +2 -2
- package/src/logger/messages/warn.ts +3 -1
- package/src/readiness/readinessManager.ts +9 -7
- package/src/sdkFactory/index.ts +1 -1
- package/src/sdkFactory/types.ts +4 -5
- package/src/services/splitApi.ts +12 -3
- package/src/services/splitHttpClient.ts +6 -5
- package/src/services/types.ts +7 -3
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/inLocalStorage/index.ts +8 -4
- package/src/storages/inMemory/InMemoryStorage.ts +3 -0
- package/src/storages/inMemory/InMemoryStorageCS.ts +3 -0
- package/src/storages/inRedis/SplitsCacheInRedis.ts +3 -1
- package/src/storages/inRedis/index.ts +8 -4
- package/src/storages/pluggable/SegmentsCachePluggable.ts +1 -1
- package/src/storages/pluggable/SplitsCachePluggable.ts +3 -1
- package/src/storages/pluggable/inMemoryWrapper.ts +6 -7
- package/src/storages/pluggable/index.ts +8 -4
- package/src/storages/pluggable/wrapperAdapter.ts +0 -1
- package/src/storages/types.ts +18 -15
- package/src/sync/offline/splitsParser/splitsParserFromFile.ts +110 -105
- package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +45 -41
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +15 -5
- package/src/sync/polling/fetchers/segmentChangesFetcher.ts +0 -7
- package/src/sync/polling/types.ts +2 -1
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +28 -10
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +2 -3
- package/src/sync/streaming/AuthClient/types.ts +3 -0
- package/src/sync/streaming/SSEClient/index.ts +43 -23
- package/src/sync/streaming/SSEClient/types.ts +0 -1
- package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +8 -0
- package/src/sync/streaming/SSEHandler/NotificationParser.ts +4 -2
- package/src/sync/streaming/SSEHandler/index.ts +11 -20
- package/src/sync/streaming/SSEHandler/types.ts +37 -3
- package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +7 -6
- package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +2 -1
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +4 -3
- package/src/sync/streaming/UpdateWorkers/types.ts +1 -1
- package/src/sync/streaming/constants.ts +2 -0
- package/src/sync/streaming/mySegmentsV2utils.ts +77 -0
- package/src/sync/streaming/pushManager.ts +145 -42
- package/src/sync/streaming/types.ts +14 -22
- package/src/sync/submitters/metricsSyncTask.ts +1 -1
- package/src/sync/submitters/submitterSyncTask.ts +2 -1
- package/src/sync/syncManagerFromFile.ts +13 -0
- package/src/sync/syncManagerFromObject.ts +12 -0
- package/src/sync/syncManagerOffline.ts +3 -3
- package/src/sync/syncManagerOnline.ts +19 -5
- package/src/sync/syncTask.ts +1 -1
- package/src/sync/types.ts +3 -1
- package/src/trackers/impressionObserver/ImpressionObserver.ts +4 -6
- package/src/trackers/impressionObserver/buildKey.ts +2 -16
- package/src/trackers/impressionObserver/impressionObserverCS.ts +2 -2
- package/src/trackers/impressionObserver/impressionObserverSS.ts +3 -3
- package/src/types.ts +16 -2
- package/src/utils/constants/index.ts +6 -1
- package/src/utils/decompress/index.ts +429 -0
- package/src/utils/lang/index.ts +1 -1
- package/src/utils/murmur3/{commons.ts → common.ts} +1 -5
- package/src/utils/murmur3/murmur3.ts +5 -5
- package/src/utils/murmur3/murmur3_128.ts +7 -180
- package/src/utils/murmur3/murmur3_128_x86.ts +188 -0
- package/src/utils/murmur3/murmur3_64.ts +36 -0
- package/src/utils/murmur3/utfx.ts +92 -110
- package/src/utils/promise/wrapper.ts +12 -9
- package/src/utils/settingsValidation/index.ts +8 -4
- package/src/utils/settingsValidation/localhost/index.ts +19 -0
- package/src/utils/settingsValidation/splitFilters.ts +0 -1
- package/src/utils/settingsValidation/storage/storageCS.ts +21 -8
- package/src/utils/settingsValidation/types.ts +2 -11
- package/src/utils/settingsValidation/url.ts +1 -1
- package/types/evaluator/matchers/matcherTypes.d.ts +2 -2
- package/types/listeners/browser.d.ts +3 -3
- package/types/listeners/node.d.ts +3 -2
- package/types/logger/constants.d.ts +2 -0
- package/types/sdkFactory/types.d.ts +4 -5
- package/types/services/types.d.ts +4 -0
- package/types/storages/inLocalStorage/index.d.ts +2 -2
- package/types/storages/inMemory/InMemoryStorage.d.ts +3 -0
- package/types/storages/inMemory/InMemoryStorageCS.d.ts +3 -0
- package/types/storages/inRedis/index.d.ts +2 -2
- package/types/storages/pluggable/index.d.ts +2 -2
- package/types/storages/types.d.ts +15 -15
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +2 -7
- package/types/sync/offline/splitsParser/splitsParserFromSettings.d.ts +1 -5
- package/types/sync/polling/types.d.ts +2 -1
- package/types/sync/streaming/AuthClient/indexV1.d.ts +12 -0
- package/types/sync/streaming/AuthClient/indexV2.d.ts +8 -0
- package/types/sync/streaming/AuthClient/types.d.ts +2 -0
- package/types/sync/streaming/SSEClient/index.d.ts +9 -12
- package/types/sync/streaming/SSEClient/types.d.ts +0 -1
- package/types/sync/streaming/SSEHandler/NotificationParser.d.ts +3 -2
- package/types/sync/streaming/SSEHandler/types.d.ts +30 -2
- package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +4 -3
- package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
- package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
- package/types/sync/streaming/UpdateWorkers/types.d.ts +1 -1
- package/types/sync/streaming/constants.d.ts +3 -1
- package/types/sync/streaming/mySegmentsV2utils.d.ts +27 -0
- package/types/sync/streaming/pushManagerNoUsers.d.ts +13 -0
- package/types/sync/streaming/types.d.ts +9 -5
- package/types/sync/submitters/submitterSyncTask.d.ts +1 -1
- package/types/sync/syncManagerFromFile.d.ts +2 -0
- package/types/sync/syncManagerFromObject.d.ts +2 -0
- package/types/sync/syncManagerOffline.d.ts +1 -1
- package/types/sync/syncTask.d.ts +1 -1
- package/types/sync/types.d.ts +2 -0
- package/types/trackers/impressionObserver/ImpressionObserver.d.ts +2 -2
- package/types/trackers/impressionObserver/buildKey.d.ts +1 -1
- package/types/trackers/impressionObserver/impressionObserverCS.d.ts +2 -2
- package/types/trackers/impressionObserver/impressionObserverSS.d.ts +2 -2
- package/types/types.d.ts +16 -2
- package/types/utils/constants/index.d.ts +5 -1
- package/types/utils/decompress/index.d.ts +16 -0
- package/types/utils/lang/index.d.ts +1 -1
- package/types/utils/murmur3/common.d.ts +12 -0
- package/types/utils/murmur3/murmur3.d.ts +2 -2
- package/types/utils/murmur3/murmur3_128.d.ts +5 -0
- package/types/utils/murmur3/murmur3_128_x86.d.ts +7 -0
- package/types/utils/murmur3/murmur3_64.d.ts +10 -0
- package/types/utils/murmur3/utfx.d.ts +24 -6
- package/types/utils/settingsValidation/index.d.ts +3 -2
- package/types/utils/settingsValidation/localhost/index.d.ts +9 -0
- package/types/utils/settingsValidation/storage/storageCS.d.ts +7 -1
- package/types/utils/settingsValidation/types.d.ts +2 -10
- package/cjs/sync/streaming/pushManagerCS.js +0 -178
- package/cjs/sync/streaming/pushManagerSS.js +0 -128
- package/esm/sync/streaming/pushManagerCS.js +0 -174
- package/esm/sync/streaming/pushManagerSS.js +0 -124
- package/src/sync/streaming/pushManagerCS.ts +0 -237
- package/src/sync/streaming/pushManagerSS.ts +0 -177
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ISettings } from '../../../types';
|
|
1
2
|
import { IAuthTokenPushEnabled } from '../AuthClient/types';
|
|
2
3
|
import { ISSEClient, ISseEventHandler } from './types';
|
|
3
4
|
|
|
@@ -5,6 +6,26 @@ const VERSION = '1.1';
|
|
|
5
6
|
|
|
6
7
|
const CONTROL_CHANNEL_REGEX = /^control_/;
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Build metadata headers for SSE connection.
|
|
11
|
+
*
|
|
12
|
+
* @param {ISettings} settings Validated settings.
|
|
13
|
+
* @returns {Record<string, string>} Headers object
|
|
14
|
+
*/
|
|
15
|
+
function buildSSEHeaders(settings: ISettings) {
|
|
16
|
+
const headers: Record<string, string> = {
|
|
17
|
+
SplitSDKClientKey: settings.core.authorizationKey.slice(-4),
|
|
18
|
+
SplitSDKVersion: settings.version,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// ip and hostname are false if IPAddressesEnabled is false
|
|
22
|
+
const { ip, hostname } = settings.runtime;
|
|
23
|
+
if (ip) headers['SplitSDKMachineIP'] = ip;
|
|
24
|
+
if (hostname) headers['SplitSDKMachineName'] = hostname;
|
|
25
|
+
|
|
26
|
+
return headers;
|
|
27
|
+
}
|
|
28
|
+
|
|
8
29
|
/**
|
|
9
30
|
* Handles streaming connections with EventSource API
|
|
10
31
|
*/
|
|
@@ -14,23 +35,27 @@ export default class SSEClient implements ISSEClient {
|
|
|
14
35
|
streamingUrl: string;
|
|
15
36
|
connection?: InstanceType<typeof EventSource>;
|
|
16
37
|
handler?: ISseEventHandler;
|
|
17
|
-
|
|
38
|
+
useHeaders?: boolean;
|
|
39
|
+
headers: Record<string, string>;
|
|
18
40
|
|
|
19
41
|
/**
|
|
20
42
|
* SSEClient constructor.
|
|
21
43
|
*
|
|
22
|
-
* @param
|
|
23
|
-
* @param
|
|
24
|
-
* @
|
|
44
|
+
* @param settings Validated settings.
|
|
45
|
+
* @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
|
|
46
|
+
* @param getEventSource Function to get the EventSource constructor.
|
|
47
|
+
* @throws 'EventSource API is not available. ' if EventSource is not available.
|
|
25
48
|
*/
|
|
26
|
-
constructor(
|
|
49
|
+
constructor(settings: ISettings, useHeaders?: boolean, getEventSource?: () => (typeof EventSource | undefined)) {
|
|
27
50
|
// @ts-expect-error
|
|
28
51
|
this.eventSource = getEventSource && getEventSource();
|
|
29
52
|
// if eventSource is not available, throw an exception
|
|
30
53
|
if (!this.eventSource) throw new Error('EventSource API is not available. ');
|
|
31
54
|
|
|
32
|
-
this.streamingUrl =
|
|
33
|
-
|
|
55
|
+
this.streamingUrl = settings.urls.streaming + '/sse';
|
|
56
|
+
// @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
|
|
57
|
+
this.useHeaders = useHeaders;
|
|
58
|
+
this.headers = buildSSEHeaders(settings);
|
|
34
59
|
}
|
|
35
60
|
|
|
36
61
|
setEventHandler(handler: ISseEventHandler) {
|
|
@@ -41,13 +66,11 @@ export default class SSEClient implements ISSEClient {
|
|
|
41
66
|
* Open the connection with a given authToken
|
|
42
67
|
*
|
|
43
68
|
* @param {IAuthTokenPushEnabled} authToken
|
|
44
|
-
* @throws {TypeError} if `authToken` is undefined
|
|
69
|
+
* @throws {TypeError} Will throw an error if `authToken` is undefined
|
|
45
70
|
*/
|
|
46
71
|
open(authToken: IAuthTokenPushEnabled) {
|
|
47
72
|
this.close(); // it closes connection if previously opened
|
|
48
73
|
|
|
49
|
-
this.authToken = authToken;
|
|
50
|
-
|
|
51
74
|
const channelsQueryParam = Object.keys(authToken.channels).map(
|
|
52
75
|
function (channel) {
|
|
53
76
|
const params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
|
|
@@ -56,12 +79,18 @@ export default class SSEClient implements ISSEClient {
|
|
|
56
79
|
).join(',');
|
|
57
80
|
const url = `${this.streamingUrl}?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${VERSION}&heartbeats=true`; // same results using `&heartbeats=false`
|
|
58
81
|
|
|
59
|
-
this.connection = new this.eventSource(
|
|
82
|
+
this.connection = new this.eventSource(
|
|
83
|
+
// For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
|
|
84
|
+
// because native EventSource implementations for browser doesn't support headers.
|
|
85
|
+
this.useHeaders ? url : url + `&SplitSDKVersion=${this.headers.SplitSDKVersion}&SplitSDKClientKey=${this.headers.SplitSDKClientKey}`,
|
|
86
|
+
// @ts-ignore. For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
|
|
87
|
+
this.useHeaders ? { headers: this.headers } : undefined
|
|
88
|
+
);
|
|
60
89
|
|
|
61
90
|
if (this.handler) { // no need to check if SSEClient is used only by PushManager
|
|
62
|
-
this.connection.
|
|
63
|
-
this.connection.
|
|
64
|
-
this.connection.
|
|
91
|
+
this.connection.addEventListener('open', this.handler.handleOpen);
|
|
92
|
+
this.connection.addEventListener('message', this.handler.handleMessage);
|
|
93
|
+
this.connection.addEventListener('error', this.handler.handleError);
|
|
65
94
|
}
|
|
66
95
|
}
|
|
67
96
|
|
|
@@ -69,13 +98,4 @@ export default class SSEClient implements ISSEClient {
|
|
|
69
98
|
close() {
|
|
70
99
|
if (this.connection) this.connection.close();
|
|
71
100
|
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Re-open the connection with the last given authToken.
|
|
75
|
-
*
|
|
76
|
-
* @throws {TypeError} if `open` has not been previously called with an authToken
|
|
77
|
-
*/
|
|
78
|
-
reopen() {
|
|
79
|
-
this.open(this.authToken as IAuthTokenPushEnabled);
|
|
80
|
-
}
|
|
81
101
|
}
|
|
@@ -62,6 +62,14 @@ export default function notificationKeeperFactory(pushEmitter: IPushEventEmitter
|
|
|
62
62
|
},
|
|
63
63
|
|
|
64
64
|
handleControlEvent(controlType: ControlType, channel: string, timestamp: number) {
|
|
65
|
+
/* STREAMING_RESET control event is handled by PushManager directly since it doesn't require
|
|
66
|
+
* tracking timestamp and state like OCCUPANCY or CONTROL. It also ignores previous
|
|
67
|
+
* OCCUPANCY and CONTROL notifications, and whether PUSH_SUBSYSTEM_DOWN has been emitted or not */
|
|
68
|
+
if (controlType === ControlType.STREAMING_RESET) {
|
|
69
|
+
pushEmitter.emit(controlType);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
for (let i = 0; i < channels.length; i++) {
|
|
66
74
|
const c = channels[i];
|
|
67
75
|
if (c.regex.test(channel)) {
|
|
@@ -22,10 +22,12 @@ export function errorParser(error: Event): INotificationError {
|
|
|
22
22
|
* Also assigns the type OCCUPANCY, if it corresponds, so that all supported messages (e.g., SPLIT_UPDATE, CONTROL) have a type.
|
|
23
23
|
*
|
|
24
24
|
* @param message
|
|
25
|
-
* @returns parsed notification message
|
|
25
|
+
* @returns parsed notification message or undefined if the given event data is falsy (e.g, '' or undefined).
|
|
26
|
+
* For example, the EventSource implementation of React-Native for iOS emits a message event with empty data for Ably keepalive comments.
|
|
26
27
|
* @throws {SyntaxError} if `message.data` or `JSON.parse(message.data).data` are invalid JSON strings
|
|
27
28
|
*/
|
|
28
|
-
export function messageParser(message: MessageEvent): INotificationMessage {
|
|
29
|
+
export function messageParser(message: MessageEvent): INotificationMessage | undefined {
|
|
30
|
+
if (!message.data) return;
|
|
29
31
|
const messageData = JSON.parse(message.data);
|
|
30
32
|
messageData.parsedData = JSON.parse(messageData.data);
|
|
31
33
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { errorParser, messageParser } from './NotificationParser';
|
|
2
2
|
import notificationKeeperFactory from './NotificationKeeper';
|
|
3
|
-
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
|
|
3
|
+
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE } from '../constants';
|
|
4
4
|
import { IPushEventEmitter } from '../types';
|
|
5
5
|
import { ISseEventHandler } from '../SSEClient/types';
|
|
6
|
-
import { INotificationError } from './types';
|
|
6
|
+
import { INotificationError, INotificationMessage } from './types';
|
|
7
7
|
import { ILogger } from '../../../logger/types';
|
|
8
8
|
import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
|
|
9
9
|
|
|
@@ -43,7 +43,7 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
43
43
|
log.warn(STREAMING_PARSING_ERROR_FAILS, [err]);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
let errorMessage = errorWithParsedData.parsedData && errorWithParsedData.parsedData.message;
|
|
46
|
+
let errorMessage = (errorWithParsedData.parsedData && errorWithParsedData.parsedData.message) || errorWithParsedData.message;
|
|
47
47
|
log.error(ERROR_STREAMING_SSE, [errorMessage]);
|
|
48
48
|
|
|
49
49
|
if (isRetryableError(errorWithParsedData)) {
|
|
@@ -55,9 +55,10 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
55
55
|
|
|
56
56
|
/* NotificationProcessor */
|
|
57
57
|
handleMessage(message) {
|
|
58
|
-
let messageWithParsedData;
|
|
58
|
+
let messageWithParsedData: INotificationMessage | undefined;
|
|
59
59
|
try {
|
|
60
60
|
messageWithParsedData = messageParser(message);
|
|
61
|
+
if (!messageWithParsedData) return; // Messages with empty data are ignored
|
|
61
62
|
} catch (err) {
|
|
62
63
|
log.warn(STREAMING_PARSING_MESSAGE_FAILS, [err]);
|
|
63
64
|
return;
|
|
@@ -67,30 +68,19 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
67
68
|
log.debug(STREAMING_NEW_MESSAGE, [data]);
|
|
68
69
|
|
|
69
70
|
// we only handle update events if streaming is up.
|
|
70
|
-
if (!notificationKeeper.isStreamingUp() &&
|
|
71
|
+
if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1)
|
|
71
72
|
return;
|
|
72
73
|
|
|
73
74
|
switch (parsedData.type) {
|
|
74
75
|
/* update events */
|
|
75
76
|
case SPLIT_UPDATE:
|
|
76
|
-
pushEmitter.emit(SPLIT_UPDATE,
|
|
77
|
-
parsedData.changeNumber);
|
|
78
|
-
break;
|
|
79
77
|
case SEGMENT_UPDATE:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
case MY_SEGMENTS_UPDATE_V2:
|
|
79
|
+
case SPLIT_KILL:
|
|
80
|
+
pushEmitter.emit(parsedData.type, parsedData);
|
|
83
81
|
break;
|
|
84
82
|
case MY_SEGMENTS_UPDATE:
|
|
85
|
-
pushEmitter.emit(
|
|
86
|
-
parsedData,
|
|
87
|
-
channel);
|
|
88
|
-
break;
|
|
89
|
-
case SPLIT_KILL:
|
|
90
|
-
pushEmitter.emit(SPLIT_KILL,
|
|
91
|
-
parsedData.changeNumber,
|
|
92
|
-
parsedData.splitName,
|
|
93
|
-
parsedData.defaultTreatment);
|
|
83
|
+
pushEmitter.emit(parsedData.type, parsedData, channel);
|
|
94
84
|
break;
|
|
95
85
|
|
|
96
86
|
/* occupancy & control events, handled by NotificationManagerKeeper */
|
|
@@ -100,6 +90,7 @@ export default function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventE
|
|
|
100
90
|
case CONTROL:
|
|
101
91
|
notificationKeeper.handleControlEvent(parsedData.controlType, channel, timestamp);
|
|
102
92
|
break;
|
|
93
|
+
|
|
103
94
|
default:
|
|
104
95
|
break;
|
|
105
96
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ControlType } from '../constants';
|
|
2
|
-
import { MY_SEGMENTS_UPDATE, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY } from '../types';
|
|
2
|
+
import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY } from '../types';
|
|
3
3
|
|
|
4
4
|
export interface IMySegmentsUpdateData {
|
|
5
5
|
type: MY_SEGMENTS_UPDATE,
|
|
@@ -8,6 +8,33 @@ export interface IMySegmentsUpdateData {
|
|
|
8
8
|
segmentList?: string[]
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export enum Compression {
|
|
12
|
+
None = 0,
|
|
13
|
+
Gzip = 1,
|
|
14
|
+
Zlib = 2
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export enum UpdateStrategy {
|
|
18
|
+
UnboundedFetchRequest = 0,
|
|
19
|
+
BoundedFetchRequest = 1,
|
|
20
|
+
KeyList = 2,
|
|
21
|
+
SegmentRemoval = 3
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface KeyList {
|
|
25
|
+
a?: string[], // decimal hash64 of user keys
|
|
26
|
+
r?: string[], // decimal hash64 of user keys
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IMySegmentsUpdateV2Data {
|
|
30
|
+
type: MY_SEGMENTS_UPDATE_V2,
|
|
31
|
+
changeNumber: number,
|
|
32
|
+
segmentName: string,
|
|
33
|
+
c: Compression,
|
|
34
|
+
d: string,
|
|
35
|
+
u: UpdateStrategy,
|
|
36
|
+
}
|
|
37
|
+
|
|
11
38
|
export interface ISegmentUpdateData {
|
|
12
39
|
type: SEGMENT_UPDATE,
|
|
13
40
|
changeNumber: number,
|
|
@@ -38,6 +65,13 @@ export interface IOccupancyData {
|
|
|
38
65
|
}
|
|
39
66
|
}
|
|
40
67
|
|
|
41
|
-
export type INotificationData = IMySegmentsUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
|
|
68
|
+
export type INotificationData = IMySegmentsUpdateData | IMySegmentsUpdateV2Data | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
|
|
42
69
|
export type INotificationMessage = { parsedData: INotificationData, channel: string, timestamp: number, data: string }
|
|
43
|
-
export type INotificationError = Event & { parsedData?: any }
|
|
70
|
+
export type INotificationError = Event & { parsedData?: any, message?: string }
|
|
71
|
+
|
|
72
|
+
export type SegmentsData = string[] | {
|
|
73
|
+
/* segment name */
|
|
74
|
+
name: string,
|
|
75
|
+
/* action: `true` for add, and `false` for delete */
|
|
76
|
+
add: boolean
|
|
77
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ISegmentsSyncTask } from '../../polling/types';
|
|
2
2
|
import Backoff from '../../../utils/Backoff';
|
|
3
3
|
import { IUpdateWorker } from './types';
|
|
4
|
+
import { SegmentsData } from '../SSEHandler/types';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* MySegmentsUpdateWorker class
|
|
@@ -10,7 +11,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
10
11
|
private readonly mySegmentsSyncTask: ISegmentsSyncTask;
|
|
11
12
|
private maxChangeNumber: number;
|
|
12
13
|
private handleNewEvent: boolean;
|
|
13
|
-
private
|
|
14
|
+
private segmentsData?: SegmentsData;
|
|
14
15
|
private currentChangeNumber: number;
|
|
15
16
|
readonly backoff: Backoff;
|
|
16
17
|
|
|
@@ -21,7 +22,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
21
22
|
this.mySegmentsSyncTask = mySegmentsSyncTask;
|
|
22
23
|
this.maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
|
|
23
24
|
this.handleNewEvent = false;
|
|
24
|
-
this.
|
|
25
|
+
this.segmentsData = undefined; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
|
|
25
26
|
this.currentChangeNumber = -1; // @TODO: remove once `/mySegments` endpoint provides the changeNumber
|
|
26
27
|
this.put = this.put.bind(this);
|
|
27
28
|
this.__handleMySegmentsUpdateCall = this.__handleMySegmentsUpdateCall.bind(this);
|
|
@@ -36,7 +37,7 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
36
37
|
const currentMaxChangeNumber = this.maxChangeNumber;
|
|
37
38
|
|
|
38
39
|
// fetch mySegments revalidating data if cached
|
|
39
|
-
this.mySegmentsSyncTask.execute(this.
|
|
40
|
+
this.mySegmentsSyncTask.execute(this.segmentsData, true).then((result) => {
|
|
40
41
|
if (result !== false) // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
|
|
41
42
|
this.currentChangeNumber = Math.max(this.currentChangeNumber, currentMaxChangeNumber); // use `currentMaxChangeNumber`, in case that `this.maxChangeNumber` was updated during fetch.
|
|
42
43
|
if (this.handleNewEvent) {
|
|
@@ -52,15 +53,15 @@ export default class MySegmentsUpdateWorker implements IUpdateWorker {
|
|
|
52
53
|
* Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
|
|
53
54
|
*
|
|
54
55
|
* @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
|
|
55
|
-
* @param {
|
|
56
|
+
* @param {SegmentsData | undefined} segmentsData might be undefined
|
|
56
57
|
*/
|
|
57
|
-
put(changeNumber: number,
|
|
58
|
+
put(changeNumber: number, segmentsData?: SegmentsData) {
|
|
58
59
|
if (changeNumber <= this.currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
|
|
59
60
|
|
|
60
61
|
this.maxChangeNumber = changeNumber;
|
|
61
62
|
this.handleNewEvent = true;
|
|
62
63
|
this.backoff.reset();
|
|
63
|
-
this.
|
|
64
|
+
this.segmentsData = segmentsData;
|
|
64
65
|
|
|
65
66
|
if (this.mySegmentsSyncTask.isExecuting()) return;
|
|
66
67
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ISegmentsCacheSync } from '../../../storages/types';
|
|
2
2
|
import Backoff from '../../../utils/Backoff';
|
|
3
3
|
import { ISegmentsSyncTask } from '../../polling/types';
|
|
4
|
+
import { ISegmentUpdateData } from '../SSEHandler/types';
|
|
4
5
|
import { IUpdateWorker } from './types';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -66,7 +67,7 @@ export default class SegmentsUpdateWorker implements IUpdateWorker {
|
|
|
66
67
|
* @param {number} changeNumber change number of the SEGMENT_UPDATE notification
|
|
67
68
|
* @param {string} segmentName segment name of the SEGMENT_UPDATE notification
|
|
68
69
|
*/
|
|
69
|
-
put(changeNumber
|
|
70
|
+
put({ changeNumber, segmentName }: ISegmentUpdateData) {
|
|
70
71
|
const currentChangeNumber = this.segmentsCache.getChangeNumber(segmentName);
|
|
71
72
|
|
|
72
73
|
if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumbers[segmentName]) return;
|
|
@@ -3,6 +3,7 @@ import { ISplitsEventEmitter } from '../../../readiness/types';
|
|
|
3
3
|
import { ISplitsCacheSync } from '../../../storages/types';
|
|
4
4
|
import Backoff from '../../../utils/Backoff';
|
|
5
5
|
import { ISegmentsSyncTask, ISplitsSyncTask } from '../../polling/types';
|
|
6
|
+
import { ISplitKillData, ISplitUpdateData } from '../SSEHandler/types';
|
|
6
7
|
import { IUpdateWorker } from './types';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -60,7 +61,7 @@ export default class SplitsUpdateWorker implements IUpdateWorker {
|
|
|
60
61
|
*
|
|
61
62
|
* @param {number} changeNumber change number of the SPLIT_UPDATE notification
|
|
62
63
|
*/
|
|
63
|
-
put(changeNumber:
|
|
64
|
+
put({ changeNumber }: Pick<ISplitUpdateData, 'changeNumber'>) {
|
|
64
65
|
const currentChangeNumber = this.splitsCache.getChangeNumber();
|
|
65
66
|
|
|
66
67
|
if (changeNumber <= currentChangeNumber || changeNumber <= this.maxChangeNumber) return;
|
|
@@ -81,14 +82,14 @@ export default class SplitsUpdateWorker implements IUpdateWorker {
|
|
|
81
82
|
* @param {string} splitName name of split to kill
|
|
82
83
|
* @param {string} defaultTreatment default treatment value
|
|
83
84
|
*/
|
|
84
|
-
killSplit(changeNumber
|
|
85
|
+
killSplit({ changeNumber, splitName, defaultTreatment }: ISplitKillData) {
|
|
85
86
|
// @TODO handle retry due to errors in storage, once we allow the definition of custom async storages
|
|
86
87
|
if (this.splitsCache.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
87
88
|
// trigger an SDK_UPDATE if Split was killed locally
|
|
88
89
|
this.splitsEventEmitter.emit(SDK_SPLITS_ARRIVED, true);
|
|
89
90
|
}
|
|
90
91
|
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
91
|
-
this.put(changeNumber);
|
|
92
|
+
this.put({ changeNumber });
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
}
|
|
@@ -26,6 +26,7 @@ export const PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN';
|
|
|
26
26
|
|
|
27
27
|
// Update-type push notifications, handled by NotificationProcessor
|
|
28
28
|
export const MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
|
|
29
|
+
export const MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
|
|
29
30
|
export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
30
31
|
export const SPLIT_KILL = 'SPLIT_KILL';
|
|
31
32
|
export const SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
@@ -38,4 +39,5 @@ export enum ControlType {
|
|
|
38
39
|
STREAMING_DISABLED = 'STREAMING_DISABLED',
|
|
39
40
|
STREAMING_PAUSED = 'STREAMING_PAUSED',
|
|
40
41
|
STREAMING_RESUMED = 'STREAMING_RESUMED',
|
|
42
|
+
STREAMING_RESET = 'STREAMING_RESET',
|
|
41
43
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { algorithms } from '../../utils/decompress';
|
|
2
|
+
import { decodeFromBase64 } from '../../utils/base64';
|
|
3
|
+
import { Compression, KeyList } from './SSEHandler/types';
|
|
4
|
+
|
|
5
|
+
const GZIP = 1;
|
|
6
|
+
const ZLIB = 2;
|
|
7
|
+
|
|
8
|
+
function Uint8ArrayToString(myUint8Arr: Uint8Array) { // @ts-ignore
|
|
9
|
+
return String.fromCharCode.apply(null, myUint8Arr);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function StringToUint8Array(myString: string) {
|
|
13
|
+
const charCodes = myString.split('').map((e) => e.charCodeAt(0));
|
|
14
|
+
return new Uint8Array(charCodes);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Decode and decompress 'data' with 'compression' algorithm
|
|
19
|
+
*
|
|
20
|
+
* @param {string} data
|
|
21
|
+
* @param {number} compression 1 GZIP, 2 ZLIB
|
|
22
|
+
* @returns {Uint8Array}
|
|
23
|
+
* @throws if data string cannot be decoded, decompressed or the provided compression value is invalid (not 1 or 2)
|
|
24
|
+
*/
|
|
25
|
+
function decompress(data: string, compression: Compression) {
|
|
26
|
+
let compressData = decodeFromBase64(data);
|
|
27
|
+
const binData = StringToUint8Array(compressData);
|
|
28
|
+
|
|
29
|
+
if (typeof algorithms === 'string') throw new Error(algorithms);
|
|
30
|
+
if (compression === GZIP) return algorithms.gunzipSync(binData);
|
|
31
|
+
if (compression === ZLIB) return algorithms.unzlibSync(binData);
|
|
32
|
+
throw new Error(`Invalid compression algorithm #${compression}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Decode, decompress and parse the provided 'data' into a KeyList object
|
|
37
|
+
*
|
|
38
|
+
* @param {string} data
|
|
39
|
+
* @param {number} compression
|
|
40
|
+
* @returns {{a?: string[], r?: string[] }}
|
|
41
|
+
* @throws if data string cannot be decoded, decompressed or parsed
|
|
42
|
+
*/
|
|
43
|
+
export function parseKeyList(data: string, compression: Compression): KeyList {
|
|
44
|
+
const binKeyList = decompress(data, compression);
|
|
45
|
+
const strKeyList = Uint8ArrayToString(binKeyList);
|
|
46
|
+
|
|
47
|
+
// replace numbers to strings, to avoid losing precision
|
|
48
|
+
return JSON.parse(strKeyList.replace(/\d+/g, '"$&"'));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Decode, decompress and parse the provided 'data' into a Bitmap object
|
|
53
|
+
*
|
|
54
|
+
* @param {string} data
|
|
55
|
+
* @param {number} compression
|
|
56
|
+
* @returns {Uint8Array}
|
|
57
|
+
* @throws if data string cannot be decoded or decompressed
|
|
58
|
+
*/
|
|
59
|
+
export function parseBitmap(data: string, compression: Compression) {
|
|
60
|
+
return decompress(data, compression);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if the 'bitmap' bit at 'hash64hex' position is 1
|
|
65
|
+
*
|
|
66
|
+
* @param {Uint8Array} bitmap
|
|
67
|
+
* @param {string} hash64hex 16-chars string, representing a number in hexa
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
*/
|
|
70
|
+
export function isInBitmap(bitmap: Uint8Array, hash64hex: string) {
|
|
71
|
+
// using the lowest 32 bits as index, to avoid losing precision when converting to number
|
|
72
|
+
const index = parseInt(hash64hex.slice(8), 16) % (bitmap.length * 8);
|
|
73
|
+
|
|
74
|
+
const internal = Math.floor(index / 8);
|
|
75
|
+
const offset = index % 8;
|
|
76
|
+
return (bitmap[internal] & 1 << offset) > 0;
|
|
77
|
+
}
|