@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.
Files changed (86) hide show
  1. package/dist/esm/Configuration.d.ts.map +1 -1
  2. package/dist/esm/Configuration.js +2 -2
  3. package/dist/esm/Configuration.js.map +1 -1
  4. package/dist/esm/FbClientCore.d.ts +2 -1
  5. package/dist/esm/FbClientCore.d.ts.map +1 -1
  6. package/dist/esm/FbClientCore.js +1 -1
  7. package/dist/esm/FbClientCore.js.map +1 -1
  8. package/dist/esm/data-sources/DataSourceUpdates.d.ts.map +1 -1
  9. package/dist/esm/data-sources/DataSourceUpdates.js +33 -28
  10. package/dist/esm/data-sources/DataSourceUpdates.js.map +1 -1
  11. package/dist/esm/index.d.ts +1 -0
  12. package/dist/esm/index.d.ts.map +1 -1
  13. package/dist/esm/index.js +1 -0
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/logging/SafeLogger.d.ts +1 -1
  16. package/dist/esm/logging/SafeLogger.d.ts.map +1 -1
  17. package/dist/esm/logging/SafeLogger.js +2 -1
  18. package/dist/esm/logging/SafeLogger.js.map +1 -1
  19. package/dist/esm/platform/IInfo.d.ts +19 -0
  20. package/dist/esm/platform/IInfo.d.ts.map +1 -1
  21. package/dist/esm/platform/IStore.d.ts +3 -9
  22. package/dist/esm/platform/IStore.d.ts.map +1 -1
  23. package/dist/esm/platform/browser/BrowserPlatform.js +2 -2
  24. package/dist/esm/platform/browser/BrowserPlatform.js.map +1 -1
  25. package/dist/esm/platform/browser/BrowserRequests.d.ts +1 -1
  26. package/dist/esm/platform/browser/BrowserRequests.d.ts.map +1 -1
  27. package/dist/esm/platform/browser/BrowserRequests.js +2 -1
  28. package/dist/esm/platform/browser/BrowserRequests.js.map +1 -1
  29. package/dist/esm/platform/browser/FbClient.d.ts.map +1 -1
  30. package/dist/esm/platform/browser/FbClient.js +8 -3
  31. package/dist/esm/platform/browser/FbClient.js.map +1 -1
  32. package/dist/esm/platform/browser/LocalStorageStore.d.ts +6 -20
  33. package/dist/esm/platform/browser/LocalStorageStore.d.ts.map +1 -1
  34. package/dist/esm/platform/browser/LocalStorageStore.js +44 -101
  35. package/dist/esm/platform/browser/LocalStorageStore.js.map +1 -1
  36. package/dist/esm/platform/index.d.ts +4 -0
  37. package/dist/esm/platform/index.d.ts.map +1 -1
  38. package/dist/esm/platform/index.js +8 -0
  39. package/dist/esm/platform/index.js.map +1 -1
  40. package/dist/esm/store/BaseStore.d.ts +25 -0
  41. package/dist/esm/store/BaseStore.d.ts.map +1 -0
  42. package/dist/esm/store/BaseStore.js +125 -0
  43. package/dist/esm/store/BaseStore.js.map +1 -0
  44. package/dist/esm/store/InMemoryStore.d.ts +7 -20
  45. package/dist/esm/store/InMemoryStore.d.ts.map +1 -1
  46. package/dist/esm/store/InMemoryStore.js +31 -81
  47. package/dist/esm/store/InMemoryStore.js.map +1 -1
  48. package/dist/esm/store/index.d.ts +1 -0
  49. package/dist/esm/store/index.d.ts.map +1 -1
  50. package/dist/esm/store/index.js +1 -0
  51. package/dist/esm/store/index.js.map +1 -1
  52. package/dist/esm/utils/debounce.d.ts +22 -0
  53. package/dist/esm/utils/debounce.d.ts.map +1 -0
  54. package/dist/esm/utils/debounce.js +34 -0
  55. package/dist/esm/utils/debounce.js.map +1 -0
  56. package/dist/esm/utils/index.d.ts +2 -0
  57. package/dist/esm/utils/index.d.ts.map +1 -1
  58. package/dist/esm/utils/index.js +2 -0
  59. package/dist/esm/utils/index.js.map +1 -1
  60. package/dist/esm/version.d.ts +1 -1
  61. package/dist/esm/version.js +1 -1
  62. package/dist/umd/featbit-js-client-sdk-3.0.1.js +2 -0
  63. package/dist/umd/featbit-js-client-sdk-3.0.1.js.map +1 -0
  64. package/dist/umd/featbit-js-client-sdk.js +1 -1
  65. package/dist/umd/featbit-js-client-sdk.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/Configuration.ts +1 -2
  68. package/src/FbClientCore.ts +3 -3
  69. package/src/data-sources/DataSourceUpdates.ts +23 -27
  70. package/src/index.ts +1 -0
  71. package/src/logging/SafeLogger.ts +1 -1
  72. package/src/platform/IInfo.ts +21 -0
  73. package/src/platform/IStore.ts +3 -9
  74. package/src/platform/browser/BrowserPlatform.ts +1 -1
  75. package/src/platform/browser/BrowserRequests.ts +1 -1
  76. package/src/platform/browser/FbClient.ts +8 -2
  77. package/src/platform/browser/LocalStorageStore.ts +13 -106
  78. package/src/platform/index.ts +9 -1
  79. package/src/store/BaseStore.ts +123 -0
  80. package/src/store/InMemoryStore.ts +21 -99
  81. package/src/store/index.ts +2 -1
  82. package/src/utils/debounce.ts +33 -0
  83. package/src/utils/index.ts +3 -1
  84. package/src/version.ts +1 -1
  85. package/dist/umd/featbit-js-client-sdk-3.0.0.js +0 -2
  86. package/dist/umd/featbit-js-client-sdk-3.0.0.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featbit/js-client-sdk",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "https://github.com/featbit/featbit-js-client-sdk",
5
5
  "main": "./dist/esm/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -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 LocalStorageStore(options),
65
+ store: (options: IOptions) => new InMemoryStore(),
67
66
  bootstrap: undefined,
68
67
  user: undefined,
69
68
  };
@@ -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
- // Defer change events so they execute after the callback.
33
- Promise.resolve().then(() => {
34
- if (checkForChanges) {
35
- const updatedKeys = Object.keys(allData)
36
- .flatMap((namespace) => {
37
- const oldDataForKind = oldData?.[namespace] || {};
38
- const newDataForKind = allData[namespace];
39
- const mergedData = {...oldDataForKind, ...newDataForKind};
40
- return Object.keys(mergedData)
41
- .filter((key: string) => this.isUpdated(oldDataForKind && oldDataForKind[key], newDataForKind && newDataForKind[key]));
42
- });
43
- updatedKeys.length > 0 && this.onChange(updatedKeys);
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
- // Defer change events so they execute after the callback.
73
- Promise.resolve().then(() => {
74
- if (checkForChanges && this.isUpdated(oldItem, data[key])) {
75
- this.onChange([key]);
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,4 +18,5 @@ export * from './IContextProperty';
18
18
  export * from './IDataKind';
19
19
  export * from './IFbClient';
20
20
  export * from './IVersionedData';
21
+ export * from './FbClientCore';
21
22
 
@@ -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 default class SafeLogger implements ILogger {
21
+ export class SafeLogger implements ILogger {
22
22
  private logger: ILogger;
23
23
 
24
24
  private fallback: ILogger;
@@ -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 {
@@ -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, callback: () => void): void;
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, callback: () => void): void;
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 default class BrowserRequests implements IRequests {
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
- IKeyedStoreItem,
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 implements IStore {
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
- this.logger = options.logger!;
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.dumpStoreToStorage();
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 memory store this is a no-op.
21
+ // For the LocalStorage store this is a no-op.
116
22
  }
117
23
 
118
- getDescription(): string {
24
+ get description(): string {
119
25
  return 'local-storage-store'
120
26
  }
121
27
 
122
- get version(): number {
123
- return this.store.version;
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
- private dumpStoreToStorage(): void {
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
- private loadStoreFromStorage(): void {
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;
@@ -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 { IDataKind } from "../IDataKind";
2
- import { StoreStorageKey, IKeyedStoreItem, IStoreDataStorage, IStoreItem, IStoreKindData } from "./store";
3
- import { IStore } from "../platform/IStore";
4
- import { IUser } from "../options/IUser";
5
- import { IOptions } from "../options/IOptions";
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
- private store: IStoreDataStorage = {} as IStoreDataStorage;
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
- get(kind: IDataKind, key: string): IStoreItem | null {
52
- const items = this.store[kind.namespace];
53
- if (items) {
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
- upsert(kind: IDataKind, data: IKeyedStoreItem, callback: () => void): void {
95
- this.addItem(kind, data.key, data);
96
- callback?.();
19
+ get description(): string {
20
+ return 'in-memory-store'
97
21
  }
98
22
 
99
- initialized(): boolean {
100
- return this.initCalled;
23
+ protected async saveUser(): Promise<void> {
24
+ // For in-memory store, this is a no-op.
101
25
  }
102
26
 
103
- /* eslint-disable class-methods-use-this */
104
- close(): void {
105
- // For the memory store this is a no-op.
27
+ protected override async dumpStoreToStorage() {
28
+ const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
29
+ this.allStores[storageKey] = {...this.store};
106
30
  }
107
31
 
108
- getDescription(): string {
109
- return 'in-memory-store';
110
- }
32
+ protected override async loadStoreFromStorage() {
33
+ const storageKey = `${StoreStorageKey}-${this._user.keyId}`;
111
34
 
112
- get version(): number {
113
- return this.store.version;
35
+ this.store = this.allStores[storageKey] ?? { flags: {}, version: 0 };
114
36
  }
115
37
  }
@@ -2,4 +2,5 @@ export * from './DataKinds';
2
2
  export * from './IDataSourceUpdates';
3
3
  export * from './InMemoryStore';
4
4
  export * from './serialization';
5
- export * from './store';
5
+ export * from './store';
6
+ export * from './BaseStore';