@featbit/js-client-sdk 3.0.12 → 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 -22
- 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.13.js +2 -0
- 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 -116
- 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.12.js +0 -2
- package/dist/umd/featbit-js-client-sdk-3.0.12.js.map +0 -1
|
@@ -1,143 +1,148 @@
|
|
|
1
|
-
import { IWebSocket, IWebSocketConfig } from "../IWebSocket";
|
|
2
|
-
import { Emits } from "../../utils/Emits";
|
|
3
|
-
import { IEventEmitter } from "../../utils/IEventEmitter";
|
|
4
|
-
import { EventEmitter } from "../../utils/EventEmitter";
|
|
5
|
-
import { generateConnectionToken } from "../../data-sync/utils";
|
|
6
|
-
import { StreamResponseEventType } from "../../data-sync/types";
|
|
7
|
-
import { IUser } from "../../options/IUser";
|
|
8
|
-
|
|
9
|
-
const socketConnectionIntervals = [1000, 3000, 5000, 7000, 11000, 13000, 30000, 60000];
|
|
10
|
-
|
|
11
|
-
class BrowserWebSocket implements IWebSocket {
|
|
12
|
-
emitter: IEventEmitter;
|
|
13
|
-
private ws?: WebSocket;
|
|
14
|
-
private retryCounter = 0;
|
|
15
|
-
private closed: boolean = false;
|
|
16
|
-
|
|
17
|
-
private _config: IWebSocketConfig = {} as IWebSocketConfig;
|
|
18
|
-
|
|
19
|
-
constructor() {
|
|
20
|
-
this.emitter = new EventEmitter();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
identify(user: IUser) {
|
|
24
|
-
this._config.user = user;
|
|
25
|
-
this.doDataSync();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
connect() {
|
|
29
|
-
let that = this;
|
|
30
|
-
const startTime = Date.now();
|
|
31
|
-
const url = this._config.streamingUri.replace(/^http/, 'ws') + `?type=client&token=${ generateConnectionToken(this._config.sdkKey) }`;
|
|
32
|
-
this.ws = new WebSocket(url);
|
|
33
|
-
|
|
34
|
-
// Connection opened
|
|
35
|
-
that.ws?.addEventListener('open', function (this: WebSocket, event) {
|
|
36
|
-
// this is the websocket instance to which the current listener is binded to, it's different from that.socket
|
|
37
|
-
that._config.logger.info(`WebSocket connection succeeded, connection time: ${ Date.now() - startTime } ms`);
|
|
38
|
-
that.doDataSync();
|
|
39
|
-
that.sendPingMessage();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Connection closed
|
|
43
|
-
that.ws?.addEventListener('close', function (event) {
|
|
44
|
-
that._config.logger.warn('WebSocket closed');
|
|
45
|
-
if (event.code === 4003) { // do not reconnect when 4003
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
that.reconnect();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Connection error
|
|
53
|
-
that.ws?.addEventListener('error', function (event) {
|
|
54
|
-
// reconnect
|
|
55
|
-
that._config.logger.debug('error');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// Listen for messages
|
|
59
|
-
that.ws?.addEventListener('message', function (event) {
|
|
60
|
-
const message = JSON.parse(event.data as string);
|
|
61
|
-
if (message.messageType === 'data-sync') {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
1
|
+
import { IWebSocket, IWebSocketConfig } from "../IWebSocket";
|
|
2
|
+
import { Emits } from "../../utils/Emits";
|
|
3
|
+
import { IEventEmitter } from "../../utils/IEventEmitter";
|
|
4
|
+
import { EventEmitter } from "../../utils/EventEmitter";
|
|
5
|
+
import { generateConnectionToken } from "../../data-sync/utils";
|
|
6
|
+
import { StreamResponseEventType } from "../../data-sync/types";
|
|
7
|
+
import { IUser } from "../../options/IUser";
|
|
8
|
+
|
|
9
|
+
const socketConnectionIntervals = [1000, 3000, 5000, 7000, 11000, 13000, 30000, 60000];
|
|
10
|
+
|
|
11
|
+
class BrowserWebSocket implements IWebSocket {
|
|
12
|
+
emitter: IEventEmitter;
|
|
13
|
+
private ws?: WebSocket;
|
|
14
|
+
private retryCounter = 0;
|
|
15
|
+
private closed: boolean = false;
|
|
16
|
+
|
|
17
|
+
private _config: IWebSocketConfig = {} as IWebSocketConfig;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
this.emitter = new EventEmitter();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
identify(user: IUser) {
|
|
24
|
+
this._config.user = user;
|
|
25
|
+
this.doDataSync();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
connect() {
|
|
29
|
+
let that = this;
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
const url = this._config.streamingUri.replace(/^http/, 'ws') + `?type=client&token=${ generateConnectionToken(this._config.sdkKey) }`;
|
|
32
|
+
this.ws = new WebSocket(url);
|
|
33
|
+
|
|
34
|
+
// Connection opened
|
|
35
|
+
that.ws?.addEventListener('open', function (this: WebSocket, event) {
|
|
36
|
+
// this is the websocket instance to which the current listener is binded to, it's different from that.socket
|
|
37
|
+
that._config.logger.info(`WebSocket connection succeeded, connection time: ${ Date.now() - startTime } ms`);
|
|
38
|
+
that.doDataSync();
|
|
39
|
+
that.sendPingMessage();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Connection closed
|
|
43
|
+
that.ws?.addEventListener('close', function (event) {
|
|
44
|
+
that._config.logger.warn('WebSocket closed');
|
|
45
|
+
if (event.code === 4003) { // do not reconnect when 4003
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
that.reconnect();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Connection error
|
|
53
|
+
that.ws?.addEventListener('error', function (event) {
|
|
54
|
+
// reconnect
|
|
55
|
+
that._config.logger.debug('error');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Listen for messages
|
|
59
|
+
that.ws?.addEventListener('message', function (event) {
|
|
60
|
+
const message = JSON.parse(event.data as string);
|
|
61
|
+
if (message.messageType === 'data-sync') {
|
|
62
|
+
if (message.data.userKeyId !== that._config.user.keyId) {
|
|
63
|
+
// abort the message if message is not for the current user
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
switch (message.data.eventType) {
|
|
68
|
+
case StreamResponseEventType.patch:
|
|
69
|
+
that.emitter.emit('patch', message);
|
|
70
|
+
break;
|
|
71
|
+
case StreamResponseEventType.full:
|
|
72
|
+
that.emitter.emit('put', message);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
close() {
|
|
80
|
+
this.closed = true;
|
|
81
|
+
this.ws?.close(4003, 'The client is closed by user');
|
|
82
|
+
this.ws = undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
config(param: IWebSocketConfig) {
|
|
86
|
+
if (param.emitter) {
|
|
87
|
+
this.emitter = param.emitter;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this._config = {...param};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private sendPingMessage() {
|
|
94
|
+
const payload = {
|
|
95
|
+
messageType: 'ping',
|
|
96
|
+
data: null
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
try {
|
|
101
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
102
|
+
this._config.logger.debug('sending ping')
|
|
103
|
+
this.ws.send(JSON.stringify(payload));
|
|
104
|
+
this.sendPingMessage();
|
|
105
|
+
} else {
|
|
106
|
+
this._config.logger.debug(`socket closed at ${ new Date() }`);
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
this._config.logger.debug(err);
|
|
110
|
+
}
|
|
111
|
+
}, this._config.pingInterval);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private doDataSync() {
|
|
115
|
+
const payload = {
|
|
116
|
+
messageType: 'data-sync',
|
|
117
|
+
data: {
|
|
118
|
+
timestamp: this._config.getStoreTimestamp(),
|
|
119
|
+
user: this._config.user
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
125
|
+
this._config.logger.debug('requesting data');
|
|
126
|
+
this.ws?.send(JSON.stringify(payload));
|
|
127
|
+
} else {
|
|
128
|
+
this._config.logger.error(`not requesting data because socket not open`);
|
|
129
|
+
}
|
|
130
|
+
} catch (err) {
|
|
131
|
+
this._config.logger.debug(err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private reconnect() {
|
|
136
|
+
if (!this.closed) {
|
|
137
|
+
this.ws = undefined;
|
|
138
|
+
const waitTime = socketConnectionIntervals[Math.min(this.retryCounter++, socketConnectionIntervals.length - 1)];
|
|
139
|
+
this._config.logger.info(`The client will try to reconnect in ${ waitTime } milliseconds.`);
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
this._config.logger.info(`The client is trying to reconnect, flag evaluation results may be stale until reconnected, waited for: ${ waitTime } milliseconds`);
|
|
142
|
+
this.connect();
|
|
143
|
+
}, waitTime);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
143
148
|
export default Emits(BrowserWebSocket);
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
import { FbClientCore } from "../../FbClientCore";
|
|
2
|
-
import { IOptions } from "../../options/IOptions";
|
|
3
|
-
import { BasicLogger } from "../../logging/BasicLogger";
|
|
4
|
-
import { EventEmitter } from "../../utils/EventEmitter";
|
|
5
|
-
import { SafeLogger } from "../../logging/SafeLogger";
|
|
6
|
-
import { Emits } from "../../utils/Emits";
|
|
7
|
-
import { IEventEmitter } from "../../utils/IEventEmitter";
|
|
8
|
-
import { BrowserPlatform } from "./BrowserPlatform";
|
|
9
|
-
import LocalStorageStore from "./LocalStorageStore";
|
|
10
|
-
import { IPlatform } from "../IPlatform";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @ignore
|
|
14
|
-
*/
|
|
15
|
-
class FbClient extends FbClientCore {
|
|
16
|
-
emitter: IEventEmitter;
|
|
17
|
-
|
|
18
|
-
constructor(options: IOptions, platform: IPlatform | undefined = undefined) {
|
|
19
|
-
const fallbackLogger = new BasicLogger({
|
|
20
|
-
level: 'none',
|
|
21
|
-
destination: console.log
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const logger = options.logger ? new SafeLogger(options.logger, fallbackLogger) : fallbackLogger;
|
|
25
|
-
|
|
26
|
-
const emitter = new EventEmitter(logger);
|
|
27
|
-
|
|
28
|
-
let { store } = options;
|
|
29
|
-
if (!store) {
|
|
30
|
-
store = new LocalStorageStore(options);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
super(
|
|
34
|
-
{...options, logger, store },
|
|
35
|
-
platform ?? new BrowserPlatform({...options, logger}),
|
|
36
|
-
{
|
|
37
|
-
onError: (err: Error) => {
|
|
38
|
-
if (emitter.listenerCount('error')) {
|
|
39
|
-
emitter.emit('error', err);
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
onFailed: (err: Error) => {
|
|
43
|
-
emitter.emit('failed', err);
|
|
44
|
-
},
|
|
45
|
-
onReady: () => {
|
|
46
|
-
emitter.emit('ready');
|
|
47
|
-
},
|
|
48
|
-
onUpdate: (keys: string[]) => {
|
|
49
|
-
emitter.emit('update', [keys]);
|
|
50
|
-
keys.forEach((key) => emitter.emit(`update:${ key }`, key));
|
|
51
|
-
},
|
|
52
|
-
hasEventListeners: () =>
|
|
53
|
-
emitter
|
|
54
|
-
.eventNames()
|
|
55
|
-
.some(
|
|
56
|
-
(name) =>
|
|
57
|
-
name === 'update' || (typeof name === 'string' && name.startsWith('update:')),
|
|
58
|
-
),
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
this.emitter = emitter;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
1
|
+
import { FbClientCore } from "../../FbClientCore";
|
|
2
|
+
import { IOptions } from "../../options/IOptions";
|
|
3
|
+
import { BasicLogger } from "../../logging/BasicLogger";
|
|
4
|
+
import { EventEmitter } from "../../utils/EventEmitter";
|
|
5
|
+
import { SafeLogger } from "../../logging/SafeLogger";
|
|
6
|
+
import { Emits } from "../../utils/Emits";
|
|
7
|
+
import { IEventEmitter } from "../../utils/IEventEmitter";
|
|
8
|
+
import { BrowserPlatform } from "./BrowserPlatform";
|
|
9
|
+
import LocalStorageStore from "./LocalStorageStore";
|
|
10
|
+
import { IPlatform } from "../IPlatform";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @ignore
|
|
14
|
+
*/
|
|
15
|
+
class FbClient extends FbClientCore {
|
|
16
|
+
emitter: IEventEmitter;
|
|
17
|
+
|
|
18
|
+
constructor(options: IOptions, platform: IPlatform | undefined = undefined) {
|
|
19
|
+
const fallbackLogger = new BasicLogger({
|
|
20
|
+
level: 'none',
|
|
21
|
+
destination: console.log
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const logger = options.logger ? new SafeLogger(options.logger, fallbackLogger) : fallbackLogger;
|
|
25
|
+
|
|
26
|
+
const emitter = new EventEmitter(logger);
|
|
27
|
+
|
|
28
|
+
let { store } = options;
|
|
29
|
+
if (!store) {
|
|
30
|
+
store = new LocalStorageStore(options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
super(
|
|
34
|
+
{...options, logger, store },
|
|
35
|
+
platform ?? new BrowserPlatform({...options, logger}),
|
|
36
|
+
{
|
|
37
|
+
onError: (err: Error) => {
|
|
38
|
+
if (emitter.listenerCount('error')) {
|
|
39
|
+
emitter.emit('error', err);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
onFailed: (err: Error) => {
|
|
43
|
+
emitter.emit('failed', err);
|
|
44
|
+
},
|
|
45
|
+
onReady: () => {
|
|
46
|
+
emitter.emit('ready');
|
|
47
|
+
},
|
|
48
|
+
onUpdate: (keys: string[]) => {
|
|
49
|
+
emitter.emit('update', [keys]);
|
|
50
|
+
keys.forEach((key) => emitter.emit(`update:${ key }`, key));
|
|
51
|
+
},
|
|
52
|
+
hasEventListeners: () =>
|
|
53
|
+
emitter
|
|
54
|
+
.eventNames()
|
|
55
|
+
.some(
|
|
56
|
+
(name) =>
|
|
57
|
+
name === 'update' || (typeof name === 'string' && name.startsWith('update:')),
|
|
58
|
+
),
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
this.emitter = emitter;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
66
|
export default Emits(FbClient);
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import {
|
|
2
|
-
StoreStorageKey,
|
|
3
|
-
IStoreDataStorage, CurrentUserStorageKey
|
|
4
|
-
} from "../../store/store";
|
|
5
|
-
import { IOptions } from "../../options/IOptions";
|
|
6
|
-
import { BaseStore } from "../../store/BaseStore";
|
|
7
|
-
import { ILogger } from "../../logging";
|
|
8
|
-
import { serializeUser } from "../../utils/serializeUser";
|
|
9
|
-
|
|
10
|
-
export default class LocalStorageStore extends BaseStore {
|
|
11
|
-
private logger: ILogger;
|
|
12
|
-
|
|
13
|
-
constructor(options: IOptions) {
|
|
14
|
-
super();
|
|
15
|
-
|
|
16
|
-
this.logger = options.logger!;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/* eslint-disable class-methods-use-this */
|
|
20
|
-
close(): void {
|
|
21
|
-
// For the LocalStorage store this is a no-op.
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
get description(): string {
|
|
25
|
-
return 'local-storage-store'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// This method needs to be overridden in the child class
|
|
29
|
-
protected async saveUser(): Promise<void> {
|
|
30
|
-
localStorage.setItem(CurrentUserStorageKey, serializeUser(this._user));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
protected override async dumpStoreToStorage() {
|
|
34
|
-
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
35
|
-
localStorage.setItem(storageKey, JSON.stringify(this.store));
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
protected override async loadStoreFromStorage() {
|
|
39
|
-
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
40
|
-
const dataStoreStr = localStorage.getItem(storageKey);
|
|
41
|
-
let store: IStoreDataStorage | null = null;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
if (dataStoreStr && dataStoreStr.trim().length > 0) {
|
|
45
|
-
store = JSON.parse(dataStoreStr);
|
|
46
|
-
}
|
|
47
|
-
} catch (err) {
|
|
48
|
-
this.logger.error(`error while loading local data store: ${storageKey}`, err);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!!store) {
|
|
52
|
-
this.store = store;
|
|
53
|
-
} else {
|
|
54
|
-
this.store = {
|
|
55
|
-
flags: {},
|
|
56
|
-
version: 0
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
StoreStorageKey,
|
|
3
|
+
IStoreDataStorage, CurrentUserStorageKey
|
|
4
|
+
} from "../../store/store";
|
|
5
|
+
import { IOptions } from "../../options/IOptions";
|
|
6
|
+
import { BaseStore } from "../../store/BaseStore";
|
|
7
|
+
import { ILogger } from "../../logging";
|
|
8
|
+
import { serializeUser } from "../../utils/serializeUser";
|
|
9
|
+
|
|
10
|
+
export default class LocalStorageStore extends BaseStore {
|
|
11
|
+
private logger: ILogger;
|
|
12
|
+
|
|
13
|
+
constructor(options: IOptions) {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
this.logger = options.logger!;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* eslint-disable class-methods-use-this */
|
|
20
|
+
close(): void {
|
|
21
|
+
// For the LocalStorage store this is a no-op.
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get description(): string {
|
|
25
|
+
return 'local-storage-store'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// This method needs to be overridden in the child class
|
|
29
|
+
protected async saveUser(): Promise<void> {
|
|
30
|
+
localStorage.setItem(CurrentUserStorageKey, serializeUser(this._user));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
protected override async dumpStoreToStorage() {
|
|
34
|
+
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
35
|
+
localStorage.setItem(storageKey, JSON.stringify(this.store));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected override async loadStoreFromStorage() {
|
|
39
|
+
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
40
|
+
const dataStoreStr = localStorage.getItem(storageKey);
|
|
41
|
+
let store: IStoreDataStorage | null = null;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
if (dataStoreStr && dataStoreStr.trim().length > 0) {
|
|
45
|
+
store = JSON.parse(dataStoreStr);
|
|
46
|
+
}
|
|
47
|
+
} catch (err) {
|
|
48
|
+
this.logger.error(`error while loading local data store: ${storageKey}`, err);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!!store) {
|
|
52
|
+
this.store = store;
|
|
53
|
+
} else {
|
|
54
|
+
this.store = {
|
|
55
|
+
flags: {},
|
|
56
|
+
version: 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
60
|
}
|
package/src/platform/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export * from './IInfo';
|
|
2
|
-
export * from './IStore';
|
|
3
|
-
export * from './IPlatform';
|
|
4
|
-
export * from './IWebSocket';
|
|
5
|
-
export * from './requests';
|
|
6
|
-
export * from './browser/BrowserRequests';
|
|
7
|
-
|
|
8
|
-
import BrowserWebSocket from './browser/BrowserWebSocket';
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
BrowserWebSocket
|
|
1
|
+
export * from './IInfo';
|
|
2
|
+
export * from './IStore';
|
|
3
|
+
export * from './IPlatform';
|
|
4
|
+
export * from './IWebSocket';
|
|
5
|
+
export * from './requests';
|
|
6
|
+
export * from './browser/BrowserRequests';
|
|
7
|
+
|
|
8
|
+
import BrowserWebSocket from './browser/BrowserWebSocket';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
BrowserWebSocket
|
|
12
12
|
};
|