@featbit/js-client-sdk 3.0.0 → 3.0.1
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/dist/esm/Configuration.d.ts.map +1 -1
- package/dist/esm/Configuration.js +2 -2
- package/dist/esm/Configuration.js.map +1 -1
- package/dist/esm/FbClientCore.d.ts +2 -1
- 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.map +1 -1
- package/dist/esm/data-sources/DataSourceUpdates.js +33 -28
- package/dist/esm/data-sources/DataSourceUpdates.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logging/SafeLogger.d.ts +1 -1
- package/dist/esm/logging/SafeLogger.d.ts.map +1 -1
- package/dist/esm/logging/SafeLogger.js +2 -1
- package/dist/esm/logging/SafeLogger.js.map +1 -1
- package/dist/esm/platform/IInfo.d.ts +19 -0
- package/dist/esm/platform/IInfo.d.ts.map +1 -1
- package/dist/esm/platform/IStore.d.ts +3 -9
- package/dist/esm/platform/IStore.d.ts.map +1 -1
- package/dist/esm/platform/browser/BrowserPlatform.js +2 -2
- package/dist/esm/platform/browser/BrowserPlatform.js.map +1 -1
- package/dist/esm/platform/browser/BrowserRequests.d.ts +1 -1
- package/dist/esm/platform/browser/BrowserRequests.d.ts.map +1 -1
- package/dist/esm/platform/browser/BrowserRequests.js +2 -1
- package/dist/esm/platform/browser/BrowserRequests.js.map +1 -1
- package/dist/esm/platform/browser/FbClient.d.ts.map +1 -1
- package/dist/esm/platform/browser/FbClient.js +8 -3
- package/dist/esm/platform/browser/FbClient.js.map +1 -1
- package/dist/esm/platform/browser/LocalStorageStore.d.ts +6 -20
- package/dist/esm/platform/browser/LocalStorageStore.d.ts.map +1 -1
- package/dist/esm/platform/browser/LocalStorageStore.js +44 -101
- package/dist/esm/platform/browser/LocalStorageStore.js.map +1 -1
- package/dist/esm/platform/index.d.ts +4 -0
- package/dist/esm/platform/index.d.ts.map +1 -1
- package/dist/esm/platform/index.js +8 -0
- package/dist/esm/platform/index.js.map +1 -1
- package/dist/esm/store/BaseStore.d.ts +25 -0
- package/dist/esm/store/BaseStore.d.ts.map +1 -0
- package/dist/esm/store/BaseStore.js +125 -0
- package/dist/esm/store/BaseStore.js.map +1 -0
- package/dist/esm/store/InMemoryStore.d.ts +7 -20
- package/dist/esm/store/InMemoryStore.d.ts.map +1 -1
- package/dist/esm/store/InMemoryStore.js +31 -81
- package/dist/esm/store/InMemoryStore.js.map +1 -1
- package/dist/esm/store/index.d.ts +1 -0
- package/dist/esm/store/index.d.ts.map +1 -1
- package/dist/esm/store/index.js +1 -0
- package/dist/esm/store/index.js.map +1 -1
- package/dist/esm/utils/debounce.d.ts +22 -0
- package/dist/esm/utils/debounce.d.ts.map +1 -0
- package/dist/esm/utils/debounce.js +34 -0
- package/dist/esm/utils/debounce.js.map +1 -0
- package/dist/esm/utils/index.d.ts +2 -0
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +2 -0
- package/dist/esm/utils/index.js.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.1.js +2 -0
- package/dist/umd/featbit-js-client-sdk-3.0.1.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 +1 -1
- package/src/Configuration.ts +1 -2
- package/src/FbClientCore.ts +3 -3
- package/src/data-sources/DataSourceUpdates.ts +23 -27
- package/src/index.ts +1 -0
- package/src/logging/SafeLogger.ts +1 -1
- package/src/platform/IInfo.ts +21 -0
- package/src/platform/IStore.ts +3 -9
- package/src/platform/browser/BrowserPlatform.ts +1 -1
- package/src/platform/browser/BrowserRequests.ts +1 -1
- package/src/platform/browser/FbClient.ts +8 -2
- package/src/platform/browser/LocalStorageStore.ts +13 -106
- package/src/platform/index.ts +9 -1
- package/src/store/BaseStore.ts +123 -0
- package/src/store/InMemoryStore.ts +21 -99
- package/src/store/index.ts +2 -1
- package/src/utils/debounce.ts +33 -0
- package/src/utils/index.ts +3 -1
- package/src/version.ts +1 -1
- package/dist/umd/featbit-js-client-sdk-3.0.0.js +0 -2
- package/dist/umd/featbit-js-client-sdk-3.0.0.js.map +0 -1
package/package.json
CHANGED
package/src/Configuration.ts
CHANGED
|
@@ -16,7 +16,6 @@ import { NullBootstrapProvider } from "./bootstrap/NullBootstrapProvider";
|
|
|
16
16
|
import { EmptyString } from "./constants";
|
|
17
17
|
import { DataSyncModeEnum } from "./data-sync/DataSyncMode";
|
|
18
18
|
import { IUser } from "./options/IUser";
|
|
19
|
-
import LocalStorageStore from "./platform/browser/LocalStorageStore";
|
|
20
19
|
import { JsonBootstrapProvider } from "./bootstrap";
|
|
21
20
|
|
|
22
21
|
// Once things are internal to the implementation of the SDK we can depend on
|
|
@@ -63,7 +62,7 @@ export const defaultValues: IValidatedOptions = {
|
|
|
63
62
|
maxEventsInQueue: 10000,
|
|
64
63
|
pollingInterval: 30000,
|
|
65
64
|
offline: false,
|
|
66
|
-
store: (options: IOptions) => new
|
|
65
|
+
store: (options: IOptions) => new InMemoryStore(),
|
|
67
66
|
bootstrap: undefined,
|
|
68
67
|
user: undefined,
|
|
69
68
|
};
|
package/src/FbClientCore.ts
CHANGED
|
@@ -63,8 +63,6 @@ export class FbClientCore implements IFbClientCore {
|
|
|
63
63
|
|
|
64
64
|
private initializedPromise?: Promise<IFbClientCore>;
|
|
65
65
|
|
|
66
|
-
private logger?: ILogger;
|
|
67
|
-
|
|
68
66
|
private config: Configuration;
|
|
69
67
|
|
|
70
68
|
private dataSourceUpdates?: DataSourceUpdates;
|
|
@@ -75,6 +73,8 @@ export class FbClientCore implements IFbClientCore {
|
|
|
75
73
|
|
|
76
74
|
private onReady: () => void;
|
|
77
75
|
|
|
76
|
+
logger?: ILogger;
|
|
77
|
+
|
|
78
78
|
constructor(
|
|
79
79
|
private options: IOptions,
|
|
80
80
|
private platform: IPlatform,
|
|
@@ -104,7 +104,7 @@ export class FbClientCore implements IFbClientCore {
|
|
|
104
104
|
private async init(platform: IPlatform, onUpdate: (keys: string[]) => void, hasEventListeners: () => boolean) {
|
|
105
105
|
const clientContext = new ClientContext(this.config.sdkKey, this.config, platform);
|
|
106
106
|
this.store = this.config.storeFactory(clientContext);
|
|
107
|
-
this.store.identify(this.config.user);
|
|
107
|
+
await this.store.identify(this.config.user);
|
|
108
108
|
this.dataSourceUpdates = new DataSourceUpdates(this.store, hasEventListeners, onUpdate);
|
|
109
109
|
this.evaluator = new Evaluator(this.store);
|
|
110
110
|
|
|
@@ -27,24 +27,22 @@ export default class DataSourceUpdates implements IDataSourceUpdates {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const checkForChanges = this.hasEventListeners();
|
|
30
|
-
const doInit = (oldData?: IStoreDataStorage) => {
|
|
31
|
-
this.store.init(allData
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
callback?.();
|
|
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
|
+
}
|
|
47
44
|
});
|
|
45
|
+
callback?.();
|
|
48
46
|
};
|
|
49
47
|
|
|
50
48
|
if (checkForChanges) {
|
|
@@ -67,17 +65,15 @@ export default class DataSourceUpdates implements IDataSourceUpdates {
|
|
|
67
65
|
|
|
68
66
|
const {key} = data;
|
|
69
67
|
const checkForChanges = this.hasEventListeners();
|
|
70
|
-
const doUpsert = (oldItem?: IStoreItem) => {
|
|
71
|
-
this.store.upsert(kind, data
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
callback?.();
|
|
68
|
+
const doUpsert = async (oldItem?: IStoreItem) => {
|
|
69
|
+
await this.store.upsert(kind, data);
|
|
70
|
+
Promise.resolve().then(() => {
|
|
71
|
+
if (checkForChanges && this.isUpdated(oldItem, data[key])) {
|
|
72
|
+
this.onChange([key]);
|
|
73
|
+
}
|
|
80
74
|
});
|
|
75
|
+
|
|
76
|
+
callback?.();
|
|
81
77
|
};
|
|
82
78
|
if (checkForChanges) {
|
|
83
79
|
const item = this.store.get(kind, key);
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,7 @@ const loggerRequirements = {
|
|
|
18
18
|
* message to the SDK's default logger, and we can at least partly guard against the latter by
|
|
19
19
|
* checking for the presence of required methods at configuration time.
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
21
|
+
export class SafeLogger implements ILogger {
|
|
22
22
|
private logger: ILogger;
|
|
23
23
|
|
|
24
24
|
private fallback: ILogger;
|
package/src/platform/IInfo.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
export interface IFbDevice {
|
|
2
|
+
manufacturer?: string;
|
|
3
|
+
model?: string;
|
|
4
|
+
storageBytes?: string;
|
|
5
|
+
memoryBytes?: string;
|
|
6
|
+
os?: {
|
|
7
|
+
/**
|
|
8
|
+
* The family of operating system.
|
|
9
|
+
*/
|
|
10
|
+
family?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
/**
|
|
2
17
|
* Information about the platform of the SDK and the environment it is executing.
|
|
3
18
|
*/
|
|
@@ -31,6 +46,12 @@ export interface IPlatformData {
|
|
|
31
46
|
* Any additional attributes associated with the platform.
|
|
32
47
|
*/
|
|
33
48
|
additional?: Record<string, string>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Device hardware information. Should be populated when available. Not all
|
|
52
|
+
* platforms will have this data.
|
|
53
|
+
*/
|
|
54
|
+
fbDevice?: IFbDevice;
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
export interface ISdkData {
|
package/src/platform/IStore.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface IStore {
|
|
|
24
24
|
* The current user. The actual type of this parameter is
|
|
25
25
|
* {@link IUser}.
|
|
26
26
|
*/
|
|
27
|
-
identify(user: IUser): void
|
|
27
|
+
identify(user: IUser): Promise<void>;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Get the current user of the store.
|
|
@@ -66,11 +66,8 @@ export interface IStore {
|
|
|
66
66
|
* An object in which each key is the "namespace" of a collection (e.g. `"features"`) and
|
|
67
67
|
* the value is an object that maps keys to entities. The actual type of this parameter is
|
|
68
68
|
* `interfaces.FullDataSet<VersionedData>`.
|
|
69
|
-
*
|
|
70
|
-
* @param callback
|
|
71
|
-
* Will be called when the store has been initialized.
|
|
72
69
|
*/
|
|
73
|
-
init(allData: IStoreDataStorage
|
|
70
|
+
init(allData: IStoreDataStorage): Promise<void>;
|
|
74
71
|
|
|
75
72
|
/**
|
|
76
73
|
* Add an entity or update an existing entity.
|
|
@@ -84,11 +81,8 @@ export interface IStore {
|
|
|
84
81
|
* should check the `version` property of this object, and should *not* overwrite any
|
|
85
82
|
* existing data if the existing `version` is greater than or equal to that value.
|
|
86
83
|
* The actual type of this parameter is {@link IKeyedStoreItem}.
|
|
87
|
-
*
|
|
88
|
-
* @param callback
|
|
89
|
-
* Will be called after the upsert operation is complete.
|
|
90
84
|
*/
|
|
91
|
-
upsert(kind: IDataKind, data: IKeyedStoreItem
|
|
85
|
+
upsert(kind: IDataKind, data: IKeyedStoreItem): Promise<void>;
|
|
92
86
|
|
|
93
87
|
/**
|
|
94
88
|
* Tests whether the store is initialized.
|
|
@@ -3,7 +3,7 @@ import { IInfo } from "../IInfo";
|
|
|
3
3
|
import { IRequests } from "../requests";
|
|
4
4
|
import { IOptions } from "../../options/IOptions";
|
|
5
5
|
import BrowserInfo from "./BrowserInfo";
|
|
6
|
-
import BrowserRequests from "./BrowserRequests";
|
|
6
|
+
import { BrowserRequests } from "./BrowserRequests";
|
|
7
7
|
import { IWebSocketWithEvents } from "../IWebSocket";
|
|
8
8
|
import BrowserWebSocket from "./BrowserWebSocket";
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IRequestOptions, IRequests } from "../requests";
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export class BrowserRequests implements IRequests {
|
|
4
4
|
fetch(url: string, options: IRequestOptions = {}): Promise<any> {
|
|
5
5
|
return fetch(url, options);
|
|
6
6
|
}
|
|
@@ -2,10 +2,11 @@ import { FbClientCore } from "../../FbClientCore";
|
|
|
2
2
|
import { IOptions } from "../../options/IOptions";
|
|
3
3
|
import { BasicLogger } from "../../logging/BasicLogger";
|
|
4
4
|
import { EventEmitter } from "../../utils/EventEmitter";
|
|
5
|
-
import SafeLogger from "../../logging/SafeLogger";
|
|
5
|
+
import { SafeLogger } from "../../logging/SafeLogger";
|
|
6
6
|
import { Emits } from "../../utils/Emits";
|
|
7
7
|
import { IEventEmitter } from "../../utils/IEventEmitter";
|
|
8
8
|
import BrowserPlatform from "./BrowserPlatform";
|
|
9
|
+
import LocalStorageStore from "./LocalStorageStore";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @ignore
|
|
@@ -23,8 +24,13 @@ class FbClient extends FbClientCore {
|
|
|
23
24
|
|
|
24
25
|
const emitter = new EventEmitter(logger);
|
|
25
26
|
|
|
27
|
+
let { store } = options;
|
|
28
|
+
if (!store) {
|
|
29
|
+
store = new LocalStorageStore(options);
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
super(
|
|
27
|
-
{...options, logger},
|
|
33
|
+
{...options, logger, store },
|
|
28
34
|
new BrowserPlatform({...options, logger}),
|
|
29
35
|
{
|
|
30
36
|
onError: (err: Error) => {
|
|
@@ -1,134 +1,41 @@
|
|
|
1
|
-
import { IDataKind } from "../../IDataKind";
|
|
2
1
|
import {
|
|
3
2
|
StoreStorageKey,
|
|
4
|
-
|
|
5
|
-
IStoreDataStorage,
|
|
6
|
-
IStoreItem,
|
|
7
|
-
IStoreKindData,
|
|
8
|
-
CurrentUserStorageKey
|
|
3
|
+
IStoreDataStorage, CurrentUserStorageKey
|
|
9
4
|
} from "../../store/store";
|
|
10
|
-
import { IStore } from "../../platform/IStore";
|
|
11
|
-
import { IUser } from "../../options/IUser";
|
|
12
|
-
import { ILogger } from "../../logging/ILogger";
|
|
13
5
|
import { IOptions } from "../../options/IOptions";
|
|
6
|
+
import { BaseStore } from "../../store/BaseStore";
|
|
7
|
+
import { ILogger } from "../../logging";
|
|
14
8
|
import { serializeUser } from "../../utils/serializeUser";
|
|
15
9
|
|
|
16
|
-
export default class LocalStorageStore
|
|
17
|
-
private store: IStoreDataStorage = {} as IStoreDataStorage;
|
|
18
|
-
|
|
19
|
-
private initCalled = false;
|
|
20
|
-
|
|
21
|
-
private _user: IUser = {} as IUser;
|
|
22
|
-
|
|
10
|
+
export default class LocalStorageStore extends BaseStore {
|
|
23
11
|
private logger: ILogger;
|
|
24
12
|
|
|
25
13
|
constructor(options: IOptions) {
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
identify(user: IUser) {
|
|
30
|
-
this._user = {...user};
|
|
31
|
-
|
|
32
|
-
localStorage.setItem(CurrentUserStorageKey, serializeUser(this._user));
|
|
33
|
-
this.loadStoreFromStorage();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get user(): IUser {
|
|
37
|
-
return this._user;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
private addItem(kind: IDataKind, key: string, item: IStoreItem) {
|
|
41
|
-
let items = this.store[kind.namespace];
|
|
42
|
-
if (!items) {
|
|
43
|
-
items = {};
|
|
44
|
-
this.store[kind.namespace] = items;
|
|
45
|
-
}
|
|
46
|
-
if (Object.hasOwnProperty.call(items, key)) {
|
|
47
|
-
const old = items[key];
|
|
48
|
-
if (!old || old.version < item.version) {
|
|
49
|
-
items[key] = item;
|
|
50
|
-
}
|
|
51
|
-
} else {
|
|
52
|
-
items[key] = item;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (item.version > this.store.version) {
|
|
56
|
-
this.store.version = item.version;
|
|
57
|
-
}
|
|
14
|
+
super();
|
|
58
15
|
|
|
59
|
-
this.
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get(kind: IDataKind, key: string): IStoreItem | null {
|
|
63
|
-
const items = this.store[kind.namespace];
|
|
64
|
-
if (items) {
|
|
65
|
-
if (Object.prototype.hasOwnProperty.call(items, key)) {
|
|
66
|
-
const item = items[key];
|
|
67
|
-
if (item) {
|
|
68
|
-
return item;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
all(kind: IDataKind): [IStoreKindData, number] {
|
|
76
|
-
const result: IStoreKindData = {};
|
|
77
|
-
const items = this.store[kind.namespace] ?? {};
|
|
78
|
-
Object.entries(items).forEach(([key, item]) => {
|
|
79
|
-
if (item) {
|
|
80
|
-
result[key] = <IStoreItem>item;
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
return [result, this.store.version];
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
init(allData: IStoreDataStorage, callback: () => void): void {
|
|
88
|
-
this.store = allData as IStoreDataStorage;
|
|
89
|
-
|
|
90
|
-
Object.keys(allData).map(namespace => {
|
|
91
|
-
Object.entries(allData[namespace]).forEach(([_, item]) => {
|
|
92
|
-
const ele = item as IStoreItem;
|
|
93
|
-
if (ele.version > this.store.version) {
|
|
94
|
-
this.store.version = ele.version;
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
this.dumpStoreToStorage();
|
|
100
|
-
this.initCalled = true;
|
|
101
|
-
callback?.();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
upsert(kind: IDataKind, data: IKeyedStoreItem, callback: () => void): void {
|
|
105
|
-
this.addItem(kind, data.key, data);
|
|
106
|
-
callback?.();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
initialized(): boolean {
|
|
110
|
-
return this.initCalled;
|
|
16
|
+
this.logger = options.logger!;
|
|
111
17
|
}
|
|
112
18
|
|
|
113
19
|
/* eslint-disable class-methods-use-this */
|
|
114
20
|
close(): void {
|
|
115
|
-
// For the
|
|
21
|
+
// For the LocalStorage store this is a no-op.
|
|
116
22
|
}
|
|
117
23
|
|
|
118
|
-
|
|
24
|
+
get description(): string {
|
|
119
25
|
return 'local-storage-store'
|
|
120
26
|
}
|
|
121
27
|
|
|
122
|
-
|
|
123
|
-
|
|
28
|
+
// This method needs to be overridden in the child class
|
|
29
|
+
protected async saveUser(): Promise<void> {
|
|
30
|
+
localStorage.setItem(CurrentUserStorageKey, serializeUser(this._user));
|
|
124
31
|
}
|
|
125
32
|
|
|
126
|
-
|
|
33
|
+
protected override async dumpStoreToStorage() {
|
|
127
34
|
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
128
35
|
localStorage.setItem(storageKey, JSON.stringify(this.store));
|
|
129
36
|
}
|
|
130
37
|
|
|
131
|
-
|
|
38
|
+
protected override async loadStoreFromStorage() {
|
|
132
39
|
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
133
40
|
const dataStoreStr = localStorage.getItem(storageKey);
|
|
134
41
|
let store: IStoreDataStorage | null = null;
|
package/src/platform/index.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export * from './IInfo';
|
|
2
|
+
export * from './IStore';
|
|
2
3
|
export * from './IPlatform';
|
|
3
4
|
export * from './IWebSocket';
|
|
4
|
-
export * from './requests';
|
|
5
|
+
export * from './requests';
|
|
6
|
+
export * from './browser/BrowserRequests';
|
|
7
|
+
|
|
8
|
+
import BrowserWebSocket from './browser/BrowserWebSocket';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
BrowserWebSocket
|
|
12
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { IStore } from "../platform";
|
|
2
|
+
import { IKeyedStoreItem, IStoreDataStorage, IStoreItem, IStoreKindData } from "./store";
|
|
3
|
+
import { IUser } from "../options";
|
|
4
|
+
import { IDataKind } from "../IDataKind";
|
|
5
|
+
|
|
6
|
+
export class BaseStore implements IStore {
|
|
7
|
+
protected store: IStoreDataStorage = {} as IStoreDataStorage;
|
|
8
|
+
|
|
9
|
+
protected initCalled = false;
|
|
10
|
+
|
|
11
|
+
protected _user: IUser = {} as IUser;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async identify(user: IUser) {
|
|
17
|
+
this._user = {...user};
|
|
18
|
+
|
|
19
|
+
await this.saveUser();
|
|
20
|
+
await this.loadStoreFromStorage();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get user(): IUser {
|
|
24
|
+
return this._user;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected async addItem(kind: IDataKind, key: string, item: IStoreItem) {
|
|
28
|
+
let items = this.store[kind.namespace];
|
|
29
|
+
if (!items) {
|
|
30
|
+
items = {};
|
|
31
|
+
this.store[kind.namespace] = items;
|
|
32
|
+
}
|
|
33
|
+
if (Object.hasOwnProperty.call(items, key)) {
|
|
34
|
+
const old = items[key];
|
|
35
|
+
if (!old || old.version < item.version) {
|
|
36
|
+
items[key] = item;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
items[key] = item;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (item.version > this.store.version) {
|
|
43
|
+
this.store.version = item.version;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
await this.dumpStoreToStorage();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get(kind: IDataKind, key: string): IStoreItem | null {
|
|
50
|
+
const items = this.store[kind.namespace];
|
|
51
|
+
if (items) {
|
|
52
|
+
if (Object.prototype.hasOwnProperty.call(items, key)) {
|
|
53
|
+
const item = items[key];
|
|
54
|
+
if (item) {
|
|
55
|
+
return item;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
all(kind: IDataKind): [IStoreKindData, number] {
|
|
63
|
+
const result: IStoreKindData = {};
|
|
64
|
+
const items = this.store[kind.namespace] ?? {};
|
|
65
|
+
Object.entries(items).forEach(([key, item]) => {
|
|
66
|
+
if (item) {
|
|
67
|
+
result[key] = <IStoreItem>item;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return [result, this.store.version];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async init(allData: IStoreDataStorage) {
|
|
75
|
+
this.store = allData as IStoreDataStorage;
|
|
76
|
+
|
|
77
|
+
Object.keys(allData).map(namespace => {
|
|
78
|
+
Object.entries(allData[namespace]).forEach(([_, item]) => {
|
|
79
|
+
const ele = item as IStoreItem;
|
|
80
|
+
if (ele.version > this.store.version) {
|
|
81
|
+
this.store.version = ele.version;
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await this.dumpStoreToStorage();
|
|
87
|
+
this.initCalled = true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async upsert(kind: IDataKind, data: IKeyedStoreItem) {
|
|
91
|
+
await this.addItem(kind, data.key, data);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
initialized(): boolean {
|
|
95
|
+
return this.initCalled;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* eslint-disable class-methods-use-this */
|
|
99
|
+
close(): void {
|
|
100
|
+
// For the LocalStorage store this is a no-op.
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get version(): number {
|
|
104
|
+
return this.store.version;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// This getter needs to be overridden in the child class
|
|
108
|
+
get description(): string {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// This method needs to be overridden in the child class
|
|
113
|
+
protected async saveUser(): Promise<void> {
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// This method needs to be overridden in the child class
|
|
117
|
+
protected async loadStoreFromStorage(): Promise<void> {
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// This method needs to be overridden in the child class
|
|
121
|
+
protected async dumpStoreToStorage(): Promise<void> {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -1,115 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
export default class InMemoryStore implements IStore {
|
|
1
|
+
import {
|
|
2
|
+
StoreStorageKey,
|
|
3
|
+
IStoreDataStorage
|
|
4
|
+
} from "./store";
|
|
5
|
+
import { BaseStore } from "./BaseStore";
|
|
8
6
|
|
|
7
|
+
export default class InMemoryStore extends BaseStore {
|
|
9
8
|
private allStores: { [DataStoreStorageKey: string]: IStoreDataStorage } = {};
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
private initCalled = false;
|
|
14
|
-
|
|
15
|
-
private _user: IUser = {} as IUser;
|
|
16
|
-
|
|
17
|
-
constructor(options: IOptions) {}
|
|
18
|
-
|
|
19
|
-
identify(user: IUser) {
|
|
20
|
-
this._user = {...user};
|
|
21
|
-
|
|
22
|
-
this.store = this.allStores[`${StoreStorageKey}-${this._user.keyId}`] ?? { flags: {}, version: 0 };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
get user(): IUser {
|
|
26
|
-
return this._user;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
private addItem(kind: IDataKind, key: string, item: IStoreItem) {
|
|
30
|
-
let items = this.store[kind.namespace];
|
|
31
|
-
if (!items) {
|
|
32
|
-
items = {};
|
|
33
|
-
this.store[kind.namespace] = items;
|
|
34
|
-
}
|
|
35
|
-
if (Object.hasOwnProperty.call(items, key)) {
|
|
36
|
-
const old = items[key];
|
|
37
|
-
if (!old || old.version < item.version) {
|
|
38
|
-
items[key] = item;
|
|
39
|
-
}
|
|
40
|
-
} else {
|
|
41
|
-
items[key] = item;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (item.version > this.store.version) {
|
|
45
|
-
this.store.version = item.version;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.allStores[`${StoreStorageKey}-${this._user.keyId}`] = {...this.store};
|
|
10
|
+
constructor() {
|
|
11
|
+
super();
|
|
49
12
|
}
|
|
50
13
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (Object.prototype.hasOwnProperty.call(items, key)) {
|
|
55
|
-
const item = items[key];
|
|
56
|
-
if (item) {
|
|
57
|
-
return item;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
all(kind: IDataKind): [IStoreKindData, number] {
|
|
65
|
-
const result: IStoreKindData = {};
|
|
66
|
-
const items = this.store[kind.namespace] ?? {};
|
|
67
|
-
Object.entries(items).forEach(([key, item]) => {
|
|
68
|
-
if (item) {
|
|
69
|
-
result[key] = <IStoreItem>item;
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
return [result, this.store.version];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
init(allData: IStoreDataStorage, callback: () => void): void {
|
|
77
|
-
this.store = allData as IStoreDataStorage;
|
|
78
|
-
|
|
79
|
-
this.store.version = 0;
|
|
80
|
-
Object.keys(allData).map(namespace => {
|
|
81
|
-
Object.entries(allData[namespace]).forEach(([_, item]) => {
|
|
82
|
-
const ele = item as IStoreItem;
|
|
83
|
-
if (ele.version > this.store.version) {
|
|
84
|
-
this.store.version = ele.version;
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
this.allStores[`${StoreStorageKey}-${this._user.keyId}`] = {...this.store};
|
|
90
|
-
this.initCalled = true;
|
|
91
|
-
callback?.();
|
|
14
|
+
/* eslint-disable class-methods-use-this */
|
|
15
|
+
close(): void {
|
|
16
|
+
// For the LocalStorage store this is a no-op.
|
|
92
17
|
}
|
|
93
18
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
callback?.();
|
|
19
|
+
get description(): string {
|
|
20
|
+
return 'in-memory-store'
|
|
97
21
|
}
|
|
98
22
|
|
|
99
|
-
|
|
100
|
-
|
|
23
|
+
protected async saveUser(): Promise<void> {
|
|
24
|
+
// For in-memory store, this is a no-op.
|
|
101
25
|
}
|
|
102
26
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
27
|
+
protected override async dumpStoreToStorage() {
|
|
28
|
+
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
29
|
+
this.allStores[storageKey] = {...this.store};
|
|
106
30
|
}
|
|
107
31
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
32
|
+
protected override async loadStoreFromStorage() {
|
|
33
|
+
const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
|
|
111
34
|
|
|
112
|
-
|
|
113
|
-
return this.store.version;
|
|
35
|
+
this.store = this.allStores[storageKey] ?? { flags: {}, version: 0 };
|
|
114
36
|
}
|
|
115
37
|
}
|
package/src/store/index.ts
CHANGED