@featbit/js-client-sdk 3.0.11 → 3.0.13
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/LICENSE +21 -21
- package/README.md +301 -301
- package/dist/esm/FbClientCore.d.ts.map +1 -1
- package/dist/esm/FbClientCore.js +1 -1
- package/dist/esm/FbClientCore.js.map +1 -1
- package/dist/esm/data-sources/DataSourceUpdates.d.ts +2 -2
- package/dist/esm/data-sources/DataSourceUpdates.d.ts.map +1 -1
- package/dist/esm/data-sources/DataSourceUpdates.js +55 -51
- package/dist/esm/data-sources/DataSourceUpdates.js.map +1 -1
- package/dist/esm/data-sources/createStreamListeners.d.ts +0 -9
- package/dist/esm/data-sources/createStreamListeners.d.ts.map +1 -1
- package/dist/esm/data-sources/createStreamListeners.js +10 -10
- package/dist/esm/data-sources/createStreamListeners.js.map +1 -1
- package/dist/esm/data-sync/IDataSynchronizer.d.ts +1 -1
- package/dist/esm/data-sync/IDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/NullDataSynchronizer.d.ts +1 -1
- package/dist/esm/data-sync/NullDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/NullDataSynchronizer.js +11 -0
- package/dist/esm/data-sync/NullDataSynchronizer.js.map +1 -1
- package/dist/esm/data-sync/PollingDataSynchronizer.d.ts +1 -1
- package/dist/esm/data-sync/PollingDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/PollingDataSynchronizer.js +42 -17
- package/dist/esm/data-sync/PollingDataSynchronizer.js.map +1 -1
- package/dist/esm/data-sync/WebSocketDataSynchronizer.d.ts +2 -1
- package/dist/esm/data-sync/WebSocketDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/WebSocketDataSynchronizer.js +20 -6
- package/dist/esm/data-sync/WebSocketDataSynchronizer.js.map +1 -1
- package/dist/esm/data-sync/types.d.ts +1 -1
- package/dist/esm/data-sync/types.d.ts.map +1 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.d.ts +1 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.js +3 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.js.map +1 -1
- package/dist/esm/platform/browser/BrowserWebSocket.d.ts.map +1 -1
- package/dist/esm/platform/browser/BrowserWebSocket.js +4 -0
- package/dist/esm/platform/browser/BrowserWebSocket.js.map +1 -1
- package/dist/esm/store/IDataSourceUpdates.d.ts +2 -2
- package/dist/esm/store/IDataSourceUpdates.d.ts.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/{featbit-js-client-sdk-3.0.11.js → featbit-js-client-sdk-3.0.13.js} +2 -2
- package/dist/umd/featbit-js-client-sdk-3.0.13.js.map +1 -0
- package/dist/umd/featbit-js-client-sdk.js +1 -1
- package/dist/umd/featbit-js-client-sdk.js.map +1 -1
- package/package.json +46 -46
- package/src/Configuration.ts +232 -232
- package/src/Context.ts +61 -61
- package/src/FbClientBuilder.ts +167 -167
- package/src/FbClientCore.ts +405 -401
- package/src/IContextProperty.ts +3 -3
- package/src/IDataKind.ts +11 -11
- package/src/IFbClient.ts +29 -29
- package/src/IFbClientCore.ts +290 -290
- package/src/IVersionedData.ts +18 -18
- package/src/bootstrap/IBootstrapProvider.ts +4 -4
- package/src/bootstrap/JsonBootstrapProvider.ts +34 -34
- package/src/bootstrap/NullBootstrapProvider.ts +20 -20
- package/src/bootstrap/index.ts +2 -2
- package/src/constants.ts +1 -1
- package/src/data-sources/DataSourceUpdates.ts +116 -116
- package/src/data-sources/createStreamListeners.ts +67 -66
- package/src/data-sources/index.ts +1 -1
- package/src/data-sync/DataSyncMode.ts +3 -3
- package/src/data-sync/IDataSynchronizer.ts +15 -15
- package/src/data-sync/IRequestor.ts +10 -10
- package/src/data-sync/NullDataSynchronizer.ts +14 -14
- package/src/data-sync/PollingDataSynchronizer.ts +125 -111
- package/src/data-sync/Requestor.ts +61 -61
- package/src/data-sync/WebSocketDataSynchronizer.ts +77 -73
- package/src/data-sync/index.ts +8 -8
- package/src/data-sync/types.ts +19 -19
- package/src/data-sync/utils.ts +31 -31
- package/src/errors.ts +47 -47
- package/src/evaluation/EvalResult.ts +35 -35
- package/src/evaluation/Evaluator.ts +26 -26
- package/src/evaluation/IEvalDetail.ts +23 -23
- package/src/evaluation/ReasonKinds.ts +9 -9
- package/src/evaluation/data/IFlag.ts +29 -29
- package/src/evaluation/index.ts +4 -4
- package/src/events/DefaultEventProcessor.ts +83 -83
- package/src/events/DefaultEventQueue.ts +49 -49
- package/src/events/DefaultEventSender.ts +73 -73
- package/src/events/DefaultEventSerializer.ts +11 -11
- package/src/events/EventDispatcher.ts +127 -127
- package/src/events/EventSerializer.ts +4 -4
- package/src/events/IEventProcessor.ts +8 -8
- package/src/events/IEventQueue.ts +16 -16
- package/src/events/IEventSender.ts +13 -13
- package/src/events/NullEventProcessor.ts +15 -15
- package/src/events/event.ts +129 -129
- package/src/events/index.ts +11 -11
- package/src/index.ts +21 -21
- package/src/integrations/TestLogger.ts +24 -24
- package/src/integrations/index.ts +1 -1
- package/src/integrations/test_data/FlagBuilder.ts +59 -59
- package/src/integrations/test_data/TestData.ts +57 -57
- package/src/integrations/test_data/TestDataSynchronizer.ts +49 -49
- package/src/integrations/test_data/index.ts +4 -4
- package/src/logging/BasicLogger.ts +108 -108
- package/src/logging/IBasicLoggerOptions.ts +46 -46
- package/src/logging/ILogger.ts +49 -49
- package/src/logging/LogLevel.ts +8 -8
- package/src/logging/SafeLogger.ts +69 -69
- package/src/logging/format.ts +154 -154
- package/src/logging/index.ts +5 -5
- package/src/options/ClientContext.ts +39 -39
- package/src/options/IClientContext.ts +53 -53
- package/src/options/IOptions.ts +123 -123
- package/src/options/IUser.ts +6 -6
- package/src/options/IValidatedOptions.ts +29 -29
- package/src/options/OptionMessages.ts +35 -35
- package/src/options/UserBuilder.ts +35 -35
- package/src/options/Validators.ts +300 -300
- package/src/options/index.ts +7 -7
- package/src/platform/IInfo.ts +102 -102
- package/src/platform/IPlatform.ts +20 -20
- package/src/platform/IStore.ts +112 -112
- package/src/platform/IWebSocket.ts +22 -22
- package/src/platform/browser/BrowserInfo.ts +24 -24
- package/src/platform/browser/BrowserPlatform.ts +19 -19
- package/src/platform/browser/BrowserRequests.ts +6 -6
- package/src/platform/browser/BrowserWebSocket.ts +147 -142
- package/src/platform/browser/FbClient.ts +65 -65
- package/src/platform/browser/LocalStorageStore.ts +59 -59
- package/src/platform/index.ts +11 -11
- package/src/platform/requests.ts +76 -76
- package/src/store/BaseStore.ts +125 -125
- package/src/store/DataKinds.ts +6 -6
- package/src/store/IDataSourceUpdates.ts +68 -68
- package/src/store/InMemoryStore.ts +36 -36
- package/src/store/index.ts +5 -5
- package/src/store/serialization.ts +52 -52
- package/src/store/store.ts +37 -37
- package/src/utils/Emits.ts +75 -75
- package/src/utils/EventEmitter.ts +128 -128
- package/src/utils/IEventEmitter.ts +14 -14
- package/src/utils/Regex.ts +21 -21
- package/src/utils/ValueConverters.ts +55 -55
- package/src/utils/canonicalizeUri.ts +3 -3
- package/src/utils/debounce.ts +33 -33
- package/src/utils/http.ts +40 -40
- package/src/utils/index.ts +5 -5
- package/src/utils/isNullOrUndefined.ts +2 -2
- package/src/utils/serializeUser.ts +27 -27
- package/src/utils/sleep.ts +5 -5
- package/src/version.ts +1 -1
- package/dist/umd/featbit-js-client-sdk-3.0.11.js.map +0 -1
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { IBootstrapProvider } from "./IBootstrapProvider";
|
|
2
|
-
import { deserializeAll } from "../store/serialization";
|
|
3
|
-
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
4
|
-
import { IStoreDataStorage } from "../store/store";
|
|
5
|
-
import { isNullOrUndefined } from "../utils/isNullOrUndefined";
|
|
6
|
-
import { IFlag, IFlagBase } from "../evaluation/data/IFlag";
|
|
7
|
-
|
|
8
|
-
export class JsonBootstrapProvider implements IBootstrapProvider {
|
|
9
|
-
private dataSet?: IStoreDataStorage;
|
|
10
|
-
|
|
11
|
-
constructor(bootstrap: IFlagBase[]) {
|
|
12
|
-
const flags: IFlag[] = (bootstrap || []).map((flag: IFlagBase) => ({...flag, variationOptions: flag.variationOptions || [{id: null, variation: flag.variation}]})) as IFlag[];
|
|
13
|
-
|
|
14
|
-
const data = deserializeAll(flags);
|
|
15
|
-
this.dataSet = {
|
|
16
|
-
flags: data.flags,
|
|
17
|
-
version: 0
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
populate(userKeyId: string, dataSourceUpdates: IDataSourceUpdates, callback?: () => void): Promise<void> {
|
|
22
|
-
return new Promise((resolve, reject) => {
|
|
23
|
-
if (isNullOrUndefined(this.dataSet)) {
|
|
24
|
-
return resolve();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const internalCallback = () => {
|
|
28
|
-
resolve();
|
|
29
|
-
callback?.();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
dataSourceUpdates.init(userKeyId, this.dataSet!, internalCallback);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
1
|
+
import { IBootstrapProvider } from "./IBootstrapProvider";
|
|
2
|
+
import { deserializeAll } from "../store/serialization";
|
|
3
|
+
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
4
|
+
import { IStoreDataStorage } from "../store/store";
|
|
5
|
+
import { isNullOrUndefined } from "../utils/isNullOrUndefined";
|
|
6
|
+
import { IFlag, IFlagBase } from "../evaluation/data/IFlag";
|
|
7
|
+
|
|
8
|
+
export class JsonBootstrapProvider implements IBootstrapProvider {
|
|
9
|
+
private dataSet?: IStoreDataStorage;
|
|
10
|
+
|
|
11
|
+
constructor(bootstrap: IFlagBase[]) {
|
|
12
|
+
const flags: IFlag[] = (bootstrap || []).map((flag: IFlagBase) => ({...flag, variationOptions: flag.variationOptions || [{id: null, variation: flag.variation}]})) as IFlag[];
|
|
13
|
+
|
|
14
|
+
const data = deserializeAll(flags);
|
|
15
|
+
this.dataSet = {
|
|
16
|
+
flags: data.flags,
|
|
17
|
+
version: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
populate(userKeyId: string, dataSourceUpdates: IDataSourceUpdates, callback?: () => void): Promise<void> {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
if (isNullOrUndefined(this.dataSet)) {
|
|
24
|
+
return resolve();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const internalCallback = () => {
|
|
28
|
+
resolve();
|
|
29
|
+
callback?.();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dataSourceUpdates.init(userKeyId, this.dataSet!, internalCallback);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
35
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { IBootstrapProvider } from "./IBootstrapProvider";
|
|
2
|
-
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
3
|
-
import { IStoreDataStorage } from "../store/store";
|
|
4
|
-
|
|
5
|
-
export class NullBootstrapProvider implements IBootstrapProvider {
|
|
6
|
-
private dataSet?: IStoreDataStorage;
|
|
7
|
-
|
|
8
|
-
constructor() {
|
|
9
|
-
this.dataSet = {
|
|
10
|
-
flags: {},
|
|
11
|
-
version: 0
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
populate(userKeyId: string, dataSourceUpdates: IDataSourceUpdates, callback?: () => void): Promise<void> {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
resolve();
|
|
18
|
-
callback?.();
|
|
19
|
-
});
|
|
20
|
-
}
|
|
1
|
+
import { IBootstrapProvider } from "./IBootstrapProvider";
|
|
2
|
+
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
3
|
+
import { IStoreDataStorage } from "../store/store";
|
|
4
|
+
|
|
5
|
+
export class NullBootstrapProvider implements IBootstrapProvider {
|
|
6
|
+
private dataSet?: IStoreDataStorage;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.dataSet = {
|
|
10
|
+
flags: {},
|
|
11
|
+
version: 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
populate(userKeyId: string, dataSourceUpdates: IDataSourceUpdates, callback?: () => void): Promise<void> {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
resolve();
|
|
18
|
+
callback?.();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
21
|
}
|
package/src/bootstrap/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './IBootstrapProvider';
|
|
2
|
-
export * from './JsonBootstrapProvider';
|
|
1
|
+
export * from './IBootstrapProvider';
|
|
2
|
+
export * from './JsonBootstrapProvider';
|
|
3
3
|
export * from './NullBootstrapProvider';
|
package/src/constants.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const EmptyString = '';
|
|
1
|
+
export const EmptyString = '';
|
|
2
2
|
export const MinInt: number = 0x80000000;
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
2
|
-
import {
|
|
3
|
-
IStoreDataStorage,
|
|
4
|
-
IStoreItem,
|
|
5
|
-
IKeyedStoreItem
|
|
6
|
-
} from "../store/store";
|
|
7
|
-
import { IStore } from "../platform/IStore";
|
|
8
|
-
import { IDataKind } from "../IDataKind";
|
|
9
|
-
import DataKinds from "../store/DataKinds";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @internal
|
|
13
|
-
*/
|
|
14
|
-
export default class DataSourceUpdates implements IDataSourceUpdates {
|
|
15
|
-
|
|
16
|
-
constructor(
|
|
17
|
-
private readonly store: IStore,
|
|
18
|
-
private readonly hasEventListeners: () => boolean,
|
|
19
|
-
private readonly onChange: (keys: string[]) => void,
|
|
20
|
-
) {
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
init(userKeyId: string, allData: IStoreDataStorage, callback?: () => void): void {
|
|
24
|
-
if (userKeyId !== this.store.user.keyId) {
|
|
25
|
-
callback?.();
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const checkForChanges = this.hasEventListeners();
|
|
30
|
-
const doInit = async (oldData?: IStoreDataStorage) => {
|
|
31
|
-
await this.store.init(allData);
|
|
32
|
-
Promise.resolve().then(() => {
|
|
33
|
-
if (checkForChanges) {
|
|
34
|
-
const updatedKeys = Object.keys(allData)
|
|
35
|
-
.flatMap((namespace) => {
|
|
36
|
-
const oldDataForKind = oldData?.[namespace] || {};
|
|
37
|
-
const newDataForKind = allData[namespace];
|
|
38
|
-
const mergedData = {...oldDataForKind, ...newDataForKind};
|
|
39
|
-
return Object.keys(mergedData)
|
|
40
|
-
.filter((key: string) => this.isUpdated(oldDataForKind && oldDataForKind[key], newDataForKind && newDataForKind[key]));
|
|
41
|
-
});
|
|
42
|
-
updatedKeys.length > 0 && this.onChange(updatedKeys);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
callback?.();
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
if (checkForChanges) {
|
|
49
|
-
const [flags, version] = this.store.all(DataKinds.Flags);
|
|
50
|
-
const oldData = {
|
|
51
|
-
flags,
|
|
52
|
-
version
|
|
53
|
-
};
|
|
54
|
-
doInit(oldData);
|
|
55
|
-
} else {
|
|
56
|
-
doInit();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
checkUpdates(oldData: IStoreDataStorage, newData: IStoreDataStorage, callback?: () => void): void {
|
|
61
|
-
const checkForChanges = this.hasEventListeners();
|
|
62
|
-
|
|
63
|
-
if (!checkForChanges) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const updatedKeys = Object.keys(newData)
|
|
68
|
-
.flatMap((namespace) => {
|
|
69
|
-
const oldDataForKind = oldData?.[namespace] || {};
|
|
70
|
-
const newDataForKind = newData[namespace];
|
|
71
|
-
const mergedData = {...oldDataForKind, ...newDataForKind};
|
|
72
|
-
return Object.keys(mergedData)
|
|
73
|
-
.filter((key: string) => this.isUpdated(oldDataForKind && oldDataForKind[key], newDataForKind && newDataForKind[key]));
|
|
74
|
-
});
|
|
75
|
-
updatedKeys.length > 0 && this.onChange(updatedKeys);
|
|
76
|
-
|
|
77
|
-
callback?.();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
upsert(userKeyId: string, kind: IDataKind, data: IKeyedStoreItem, callback: () => void): void {
|
|
81
|
-
if (userKeyId !== this.store.user.keyId) {
|
|
82
|
-
callback?.();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const {key} = data;
|
|
87
|
-
const checkForChanges = this.hasEventListeners();
|
|
88
|
-
const doUpsert = async (oldItem?: IStoreItem) => {
|
|
89
|
-
await this.store.upsert(kind, data);
|
|
90
|
-
Promise.resolve().then(() => {
|
|
91
|
-
if (checkForChanges && this.isUpdated(oldItem, data[key])) {
|
|
92
|
-
this.onChange([key]);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
callback?.();
|
|
97
|
-
};
|
|
98
|
-
if (checkForChanges) {
|
|
99
|
-
const item = this.store.get(kind, key);
|
|
100
|
-
doUpsert(item || undefined);
|
|
101
|
-
} else {
|
|
102
|
-
doUpsert();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private isUpdated(oldData?: IStoreItem, newData?: IStoreItem): boolean {
|
|
107
|
-
if (!oldData && !newData) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!oldData || !newData) {
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return newData.version >= oldData.version && newData.variation !== oldData.variation;
|
|
116
|
-
}
|
|
1
|
+
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
2
|
+
import {
|
|
3
|
+
IStoreDataStorage,
|
|
4
|
+
IStoreItem,
|
|
5
|
+
IKeyedStoreItem
|
|
6
|
+
} from "../store/store";
|
|
7
|
+
import { IStore } from "../platform/IStore";
|
|
8
|
+
import { IDataKind } from "../IDataKind";
|
|
9
|
+
import DataKinds from "../store/DataKinds";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export default class DataSourceUpdates implements IDataSourceUpdates {
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly store: IStore,
|
|
18
|
+
private readonly hasEventListeners: () => boolean,
|
|
19
|
+
private readonly onChange: (keys: string[]) => void,
|
|
20
|
+
) {
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async init(userKeyId: string, allData: IStoreDataStorage, callback?: () => void): Promise<void> {
|
|
24
|
+
if (userKeyId !== this.store.user.keyId) {
|
|
25
|
+
callback?.();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const checkForChanges = this.hasEventListeners();
|
|
30
|
+
const doInit = async (oldData?: IStoreDataStorage) => {
|
|
31
|
+
await this.store.init(allData);
|
|
32
|
+
Promise.resolve().then(() => {
|
|
33
|
+
if (checkForChanges) {
|
|
34
|
+
const updatedKeys = Object.keys(allData)
|
|
35
|
+
.flatMap((namespace) => {
|
|
36
|
+
const oldDataForKind = oldData?.[namespace] || {};
|
|
37
|
+
const newDataForKind = allData[namespace];
|
|
38
|
+
const mergedData = {...oldDataForKind, ...newDataForKind};
|
|
39
|
+
return Object.keys(mergedData)
|
|
40
|
+
.filter((key: string) => this.isUpdated(oldDataForKind && oldDataForKind[key], newDataForKind && newDataForKind[key]));
|
|
41
|
+
});
|
|
42
|
+
updatedKeys.length > 0 && this.onChange(updatedKeys);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
callback?.();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (checkForChanges) {
|
|
49
|
+
const [flags, version] = this.store.all(DataKinds.Flags);
|
|
50
|
+
const oldData = {
|
|
51
|
+
flags,
|
|
52
|
+
version
|
|
53
|
+
};
|
|
54
|
+
await doInit(oldData);
|
|
55
|
+
} else {
|
|
56
|
+
await doInit();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
checkUpdates(oldData: IStoreDataStorage, newData: IStoreDataStorage, callback?: () => void): void {
|
|
61
|
+
const checkForChanges = this.hasEventListeners();
|
|
62
|
+
|
|
63
|
+
if (!checkForChanges) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const updatedKeys = Object.keys(newData)
|
|
68
|
+
.flatMap((namespace) => {
|
|
69
|
+
const oldDataForKind = oldData?.[namespace] || {};
|
|
70
|
+
const newDataForKind = newData[namespace];
|
|
71
|
+
const mergedData = {...oldDataForKind, ...newDataForKind};
|
|
72
|
+
return Object.keys(mergedData)
|
|
73
|
+
.filter((key: string) => this.isUpdated(oldDataForKind && oldDataForKind[key], newDataForKind && newDataForKind[key]));
|
|
74
|
+
});
|
|
75
|
+
updatedKeys.length > 0 && this.onChange(updatedKeys);
|
|
76
|
+
|
|
77
|
+
callback?.();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async upsert(userKeyId: string, kind: IDataKind, data: IKeyedStoreItem, callback: () => void): Promise<void> {
|
|
81
|
+
if (userKeyId !== this.store.user.keyId) {
|
|
82
|
+
callback?.();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const {key} = data;
|
|
87
|
+
const checkForChanges = this.hasEventListeners();
|
|
88
|
+
const doUpsert = async (oldItem?: IStoreItem) => {
|
|
89
|
+
await this.store.upsert(kind, data);
|
|
90
|
+
Promise.resolve().then(() => {
|
|
91
|
+
if (checkForChanges && this.isUpdated(oldItem, data[key])) {
|
|
92
|
+
this.onChange([key]);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
callback?.();
|
|
97
|
+
};
|
|
98
|
+
if (checkForChanges) {
|
|
99
|
+
const item = this.store.get(kind, key);
|
|
100
|
+
await doUpsert(item || undefined);
|
|
101
|
+
} else {
|
|
102
|
+
await doUpsert();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private isUpdated(oldData?: IStoreItem, newData?: IStoreItem): boolean {
|
|
107
|
+
if (!oldData && !newData) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!oldData || !newData) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return newData.version >= oldData.version && newData.variation !== oldData.variation;
|
|
116
|
+
}
|
|
117
117
|
}
|
|
@@ -1,66 +1,67 @@
|
|
|
1
|
-
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
2
|
-
import { ILogger } from "../logging/ILogger";
|
|
3
|
-
import { VoidFunction } from "../utils/VoidFunction";
|
|
4
|
-
import {
|
|
5
|
-
deserializeAll,
|
|
6
|
-
deserializePatch,
|
|
7
|
-
IPatchData,
|
|
8
|
-
Flags
|
|
9
|
-
} from "../store/serialization";
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
listeners
|
|
64
|
-
listeners.set('
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
import { IDataSourceUpdates } from "../store/IDataSourceUpdates";
|
|
2
|
+
import { ILogger } from "../logging/ILogger";
|
|
3
|
+
import { VoidFunction } from "../utils/VoidFunction";
|
|
4
|
+
import {
|
|
5
|
+
deserializeAll,
|
|
6
|
+
deserializePatch,
|
|
7
|
+
IPatchData,
|
|
8
|
+
Flags
|
|
9
|
+
} from "../store/serialization";
|
|
10
|
+
import { IStoreDataStorage } from "../store/store";
|
|
11
|
+
import { EventName, ProcessStreamResponse } from "../data-sync/types";
|
|
12
|
+
|
|
13
|
+
const createPutListener = (
|
|
14
|
+
dataSourceUpdates: IDataSourceUpdates,
|
|
15
|
+
logger?: ILogger,
|
|
16
|
+
onPutCompleteHandler: VoidFunction = () => {
|
|
17
|
+
},
|
|
18
|
+
) => ({
|
|
19
|
+
deserializeData: deserializeAll,
|
|
20
|
+
processJson: async (userKeyId: string, {flags}: Flags) => {
|
|
21
|
+
const initData: IStoreDataStorage = {
|
|
22
|
+
flags: flags,
|
|
23
|
+
version: 0
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
logger?.debug('Initializing all data');
|
|
27
|
+
await dataSourceUpdates.init(userKeyId, initData, onPutCompleteHandler);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const createPatchListener = (
|
|
32
|
+
dataSourceUpdates: IDataSourceUpdates,
|
|
33
|
+
logger?: ILogger,
|
|
34
|
+
onPatchCompleteHandler: VoidFunction = () => {
|
|
35
|
+
},
|
|
36
|
+
) => ({
|
|
37
|
+
deserializeData: deserializePatch,
|
|
38
|
+
processJson: async (userKeyId: string, data: IPatchData[]) => {
|
|
39
|
+
if (data?.length === 0) {
|
|
40
|
+
onPatchCompleteHandler?.();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (data?.length > 0) {
|
|
45
|
+
for(const item of data) {
|
|
46
|
+
logger?.debug(`Updating ${ item.data.key } in ${ item.kind.namespace }`);
|
|
47
|
+
await dataSourceUpdates.upsert(userKeyId, item.kind, item.data, onPatchCompleteHandler);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export const createStreamListeners = (
|
|
55
|
+
dataSourceUpdates: IDataSourceUpdates,
|
|
56
|
+
logger?: ILogger,
|
|
57
|
+
onCompleteHandlers?: {
|
|
58
|
+
put?: VoidFunction;
|
|
59
|
+
patch?: VoidFunction;
|
|
60
|
+
delete?: VoidFunction;
|
|
61
|
+
},
|
|
62
|
+
) => {
|
|
63
|
+
const listeners = new Map<EventName, ProcessStreamResponse>();
|
|
64
|
+
listeners.set('put', createPutListener(dataSourceUpdates, logger, onCompleteHandlers?.put));
|
|
65
|
+
listeners.set('patch', createPatchListener(dataSourceUpdates, logger, onCompleteHandlers?.patch));
|
|
66
|
+
return listeners;
|
|
67
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './createStreamListeners';
|
|
1
|
+
export * from './createStreamListeners';
|
|
2
2
|
export * from './DataSourceUpdates';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export enum DataSyncModeEnum {
|
|
2
|
-
POLLING = 'polling',
|
|
3
|
-
STREAMING = 'streaming'
|
|
1
|
+
export enum DataSyncModeEnum {
|
|
2
|
+
POLLING = 'polling',
|
|
3
|
+
STREAMING = 'streaming'
|
|
4
4
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { IUser } from "../options/IUser";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The client data synchronizer
|
|
5
|
-
*
|
|
6
|
-
* The client uses this internally to retrieve updates from the FeatBit server.
|
|
7
|
-
*
|
|
8
|
-
* @ignore
|
|
9
|
-
*/
|
|
10
|
-
export interface IDataSynchronizer {
|
|
11
|
-
start: () => void;
|
|
12
|
-
stop: () => void;
|
|
13
|
-
close: () => void;
|
|
14
|
-
identify: (user: IUser) => void
|
|
15
|
-
}
|
|
1
|
+
import { IUser } from "../options/IUser";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The client data synchronizer
|
|
5
|
+
*
|
|
6
|
+
* The client uses this internally to retrieve updates from the FeatBit server.
|
|
7
|
+
*
|
|
8
|
+
* @ignore
|
|
9
|
+
*/
|
|
10
|
+
export interface IDataSynchronizer {
|
|
11
|
+
start: () => void;
|
|
12
|
+
stop: () => void;
|
|
13
|
+
close: () => void;
|
|
14
|
+
identify: (user: IUser) => Promise<void>;
|
|
15
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The FeatBit client feature flag requestor
|
|
3
|
-
*
|
|
4
|
-
* The client uses this internally to retrieve feature flags from FeatBit.
|
|
5
|
-
*
|
|
6
|
-
* @ignore
|
|
7
|
-
*/
|
|
8
|
-
export interface IRequestor {
|
|
9
|
-
requestData: (timestamp: number, payload: any, cb: (err: any, body: any) => void) => void;
|
|
10
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* The FeatBit client feature flag requestor
|
|
3
|
+
*
|
|
4
|
+
* The client uses this internally to retrieve feature flags from FeatBit.
|
|
5
|
+
*
|
|
6
|
+
* @ignore
|
|
7
|
+
*/
|
|
8
|
+
export interface IRequestor {
|
|
9
|
+
requestData: (timestamp: number, payload: any, cb: (err: any, body: any) => void) => void;
|
|
10
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { IDataSynchronizer } from "./IDataSynchronizer";
|
|
2
|
-
|
|
3
|
-
export class NullDataSynchronizer implements IDataSynchronizer {
|
|
4
|
-
close(): void {
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
start(): void {
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
stop(): void {
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
identify(): void {
|
|
14
|
-
}
|
|
1
|
+
import { IDataSynchronizer } from "./IDataSynchronizer";
|
|
2
|
+
|
|
3
|
+
export class NullDataSynchronizer implements IDataSynchronizer {
|
|
4
|
+
close(): void {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
start(): void {
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
stop(): void {
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async identify(): Promise<void> {
|
|
14
|
+
}
|
|
15
15
|
}
|