react-native-onyx 2.0.21 → 2.0.23
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/API.md +49 -64
- package/dist/DevTools.js +0 -1
- package/dist/Onyx.d.ts +49 -258
- package/dist/Onyx.js +192 -1165
- package/dist/OnyxCache.d.ts +14 -15
- package/dist/OnyxUtils.d.ts +320 -0
- package/dist/OnyxUtils.js +1061 -0
- package/dist/PerformanceUtils.d.ts +3 -5
- package/dist/index.d.ts +3 -2
- package/dist/storage/InstanceSync/index.d.ts +14 -0
- package/dist/storage/InstanceSync/index.js +20 -0
- package/dist/storage/InstanceSync/index.web.d.ts +27 -0
- package/dist/storage/InstanceSync/index.web.js +59 -0
- package/dist/storage/__mocks__/index.d.ts +15 -13
- package/dist/storage/__mocks__/index.js +43 -81
- package/dist/storage/index.d.ts +6 -2
- package/dist/storage/index.js +170 -2
- package/dist/storage/platforms/index.d.ts +2 -0
- package/dist/storage/{NativeStorage.js → platforms/index.js} +2 -2
- package/dist/storage/platforms/index.native.d.ts +2 -0
- package/dist/storage/{index.native.js → platforms/index.native.js} +2 -2
- package/dist/storage/providers/{IDBKeyVal.js → IDBKeyValProvider.js} +23 -19
- package/dist/storage/providers/MemoryOnlyProvider.d.ts +9 -0
- package/dist/storage/providers/MemoryOnlyProvider.js +124 -0
- package/dist/storage/providers/NoopProvider.js +85 -0
- package/dist/storage/providers/SQLiteProvider.d.ts +3 -0
- package/dist/storage/providers/{SQLiteStorage.js → SQLiteProvider.js} +17 -11
- package/dist/storage/providers/types.d.ts +17 -14
- package/dist/types.d.ts +128 -55
- package/dist/types.js +2 -0
- package/dist/useOnyx.js +11 -10
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +29 -16
- package/dist/withOnyx.js +6 -5
- package/package.json +1 -1
- package/dist/storage/NativeStorage.d.ts +0 -2
- package/dist/storage/WebStorage.d.ts +0 -3
- package/dist/storage/WebStorage.js +0 -62
- package/dist/storage/index.native.d.ts +0 -2
- /package/dist/storage/providers/{IDBKeyVal.d.ts → IDBKeyValProvider.d.ts} +0 -0
- /package/dist/storage/providers/{SQLiteStorage.d.ts → NoopProvider.d.ts} +0 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
type
|
|
2
|
-
|
|
3
|
-
displayName: string;
|
|
4
|
-
};
|
|
1
|
+
import type { OnyxKey } from './types';
|
|
2
|
+
import type { Mapping } from './Onyx';
|
|
5
3
|
declare function setShouldDebugSetState(debug: boolean): void;
|
|
6
4
|
/**
|
|
7
5
|
* Provide insights into why a setState() call occurred by diffing the before and after values.
|
|
8
6
|
*/
|
|
9
|
-
declare function logSetStateCall(mapping: Mapping
|
|
7
|
+
declare function logSetStateCall<TKey extends OnyxKey>(mapping: Mapping<TKey>, previousValue: unknown, newValue: unknown, caller: string, keyThatChanged?: string): void;
|
|
10
8
|
export { logSetStateCall, setShouldDebugSetState };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import Onyx from './Onyx';
|
|
2
2
|
import type { OnyxUpdate, ConnectOptions } from './Onyx';
|
|
3
|
-
import type { CustomTypeOptions, OnyxCollection, OnyxEntry, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState } from './types';
|
|
3
|
+
import type { CustomTypeOptions, OnyxCollection, OnyxEntry, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState, OnyxValue } from './types';
|
|
4
|
+
import type { UseOnyxResult, FetchStatus } from './useOnyx';
|
|
4
5
|
import useOnyx from './useOnyx';
|
|
5
6
|
import withOnyx from './withOnyx';
|
|
6
7
|
export default Onyx;
|
|
7
8
|
export { withOnyx, useOnyx };
|
|
8
|
-
export type { CustomTypeOptions, OnyxCollection, OnyxEntry, OnyxUpdate, ConnectOptions, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState };
|
|
9
|
+
export type { CustomTypeOptions, OnyxCollection, OnyxEntry, OnyxUpdate, ConnectOptions, NullishDeep, KeyValueMapping, OnyxKey, Selector, WithOnyxInstanceState, UseOnyxResult, OnyxValue, FetchStatus, };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is used to keep multiple browser tabs in sync, therefore only needed on web
|
|
3
|
+
* On native platforms, we omit this syncing logic by setting this to mock implementation.
|
|
4
|
+
*/
|
|
5
|
+
declare const InstanceSync: {
|
|
6
|
+
shouldBeUsed: boolean;
|
|
7
|
+
init: (...args: any[]) => void;
|
|
8
|
+
setItem: (...args: any[]) => void;
|
|
9
|
+
removeItem: (...args: any[]) => void;
|
|
10
|
+
removeItems: (...args: any[]) => void;
|
|
11
|
+
mergeItem: (...args: any[]) => void;
|
|
12
|
+
clear: <T extends () => void>(callback: T) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
export default InstanceSync;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const noop_1 = __importDefault(require("lodash/noop"));
|
|
7
|
+
/**
|
|
8
|
+
* This is used to keep multiple browser tabs in sync, therefore only needed on web
|
|
9
|
+
* On native platforms, we omit this syncing logic by setting this to mock implementation.
|
|
10
|
+
*/
|
|
11
|
+
const InstanceSync = {
|
|
12
|
+
shouldBeUsed: false,
|
|
13
|
+
init: noop_1.default,
|
|
14
|
+
setItem: noop_1.default,
|
|
15
|
+
removeItem: noop_1.default,
|
|
16
|
+
removeItems: noop_1.default,
|
|
17
|
+
mergeItem: noop_1.default,
|
|
18
|
+
clear: (callback) => Promise.resolve(callback()),
|
|
19
|
+
};
|
|
20
|
+
exports.default = InstanceSync;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The InstancesSync object provides data-changed events like the ones that exist
|
|
3
|
+
* when using LocalStorage APIs in the browser. These events are great because multiple tabs can listen for when
|
|
4
|
+
* data changes and then stay up-to-date with everything happening in Onyx.
|
|
5
|
+
*/
|
|
6
|
+
import type { OnyxKey } from '../../types';
|
|
7
|
+
import type { KeyList, OnStorageKeyChanged } from '../providers/types';
|
|
8
|
+
import type StorageProvider from '../providers/types';
|
|
9
|
+
/**
|
|
10
|
+
* Raise an event through `localStorage` to let other tabs know a value changed
|
|
11
|
+
* @param {String} onyxKey
|
|
12
|
+
*/
|
|
13
|
+
declare function raiseStorageSyncEvent(onyxKey: OnyxKey): void;
|
|
14
|
+
declare function raiseStorageSyncManyKeysEvent(onyxKeys: KeyList): void;
|
|
15
|
+
declare const InstanceSync: {
|
|
16
|
+
shouldBeUsed: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* @param {Function} onStorageKeyChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
19
|
+
*/
|
|
20
|
+
init: (onStorageKeyChanged: OnStorageKeyChanged, store: StorageProvider) => void;
|
|
21
|
+
setItem: typeof raiseStorageSyncEvent;
|
|
22
|
+
removeItem: typeof raiseStorageSyncEvent;
|
|
23
|
+
removeItems: typeof raiseStorageSyncManyKeysEvent;
|
|
24
|
+
mergeItem: typeof raiseStorageSyncEvent;
|
|
25
|
+
clear: (clearImplementation: () => void) => Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
export default InstanceSync;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const NoopProvider_1 = __importDefault(require("../providers/NoopProvider"));
|
|
7
|
+
const SYNC_ONYX = 'SYNC_ONYX';
|
|
8
|
+
/**
|
|
9
|
+
* Raise an event through `localStorage` to let other tabs know a value changed
|
|
10
|
+
* @param {String} onyxKey
|
|
11
|
+
*/
|
|
12
|
+
function raiseStorageSyncEvent(onyxKey) {
|
|
13
|
+
global.localStorage.setItem(SYNC_ONYX, onyxKey);
|
|
14
|
+
global.localStorage.removeItem(SYNC_ONYX);
|
|
15
|
+
}
|
|
16
|
+
function raiseStorageSyncManyKeysEvent(onyxKeys) {
|
|
17
|
+
onyxKeys.forEach((onyxKey) => {
|
|
18
|
+
raiseStorageSyncEvent(onyxKey);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
let storage = NoopProvider_1.default;
|
|
22
|
+
const InstanceSync = {
|
|
23
|
+
shouldBeUsed: true,
|
|
24
|
+
/**
|
|
25
|
+
* @param {Function} onStorageKeyChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
26
|
+
*/
|
|
27
|
+
init: (onStorageKeyChanged, store) => {
|
|
28
|
+
storage = store;
|
|
29
|
+
// This listener will only be triggered by events coming from other tabs
|
|
30
|
+
global.addEventListener('storage', (event) => {
|
|
31
|
+
// Ignore events that don't originate from the SYNC_ONYX logic
|
|
32
|
+
if (event.key !== SYNC_ONYX || !event.newValue) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const onyxKey = event.newValue;
|
|
36
|
+
storage.getItem(onyxKey).then((value) => onStorageKeyChanged(onyxKey, value));
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
setItem: raiseStorageSyncEvent,
|
|
40
|
+
removeItem: raiseStorageSyncEvent,
|
|
41
|
+
removeItems: raiseStorageSyncManyKeysEvent,
|
|
42
|
+
mergeItem: raiseStorageSyncEvent,
|
|
43
|
+
clear: (clearImplementation) => {
|
|
44
|
+
let allKeys;
|
|
45
|
+
// The keys must be retrieved before storage is cleared or else the list of keys would be empty
|
|
46
|
+
return storage
|
|
47
|
+
.getAllKeys()
|
|
48
|
+
.then((keys) => {
|
|
49
|
+
allKeys = keys;
|
|
50
|
+
})
|
|
51
|
+
.then(() => clearImplementation())
|
|
52
|
+
.then(() => {
|
|
53
|
+
// Now that storage is cleared, the storage sync event can happen which is a more atomic action
|
|
54
|
+
// for other browser tabs
|
|
55
|
+
raiseStorageSyncManyKeysEvent(allKeys);
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
exports.default = InstanceSync;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
/// <reference types="jest" />
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
declare const StorageMock: {
|
|
3
|
+
init: jest.Mock<void, []>;
|
|
4
|
+
getItem: jest.Mock<Promise<unknown>, [key: any]>;
|
|
5
|
+
multiGet: jest.Mock<Promise<import("../providers/types").KeyValuePairList>, [keys: import("../providers/types").KeyList]>;
|
|
6
|
+
setItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, [key: any, value: unknown]>;
|
|
7
|
+
multiSet: jest.Mock<Promise<void | import("react-native-quick-sqlite").BatchQueryResult>, [pairs: import("../providers/types").KeyValuePairList]>;
|
|
8
|
+
mergeItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").BatchQueryResult>, [key: any, changes: unknown, modifiedData: unknown]>;
|
|
9
|
+
multiMerge: jest.Mock<Promise<import("react-native-quick-sqlite").BatchQueryResult | IDBValidKey[]>, [pairs: import("../providers/types").KeyValuePairList]>;
|
|
7
10
|
removeItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, [key: string]>;
|
|
8
11
|
removeItems: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, [keys: import("../providers/types").KeyList]>;
|
|
9
12
|
clear: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, []>;
|
|
10
13
|
getAllKeys: jest.Mock<Promise<import("../providers/types").KeyList>, []>;
|
|
11
|
-
multiGet: jest.Mock<Promise<KeyValuePairList>, [keys: import("../providers/types").KeyList]>;
|
|
12
|
-
multiSet: jest.Mock<Promise<void | import("react-native-quick-sqlite").BatchQueryResult>, [pairs: KeyValuePairList]>;
|
|
13
|
-
multiMerge: jest.Mock<Promise<import("react-native-quick-sqlite").BatchQueryResult | IDBValidKey[]>, [pairs: KeyValuePairList]>;
|
|
14
|
-
mergeItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").BatchQueryResult>, [key: string, changes: IDBValidKey, modifiedData: IDBValidKey]>;
|
|
15
|
-
getStorageMap: jest.Mock<Record<string, IDBValidKey>, []>;
|
|
16
|
-
setInitialMockData: jest.Mock<void, [data: any]>;
|
|
17
14
|
getDatabaseSize: jest.Mock<Promise<{
|
|
18
15
|
bytesUsed: number;
|
|
19
16
|
bytesRemaining: number;
|
|
20
17
|
}>, []>;
|
|
21
|
-
|
|
18
|
+
keepInstancesSync: jest.Mock<any, any>;
|
|
19
|
+
mockSet: (key: string, value: unknown) => Promise<unknown>;
|
|
20
|
+
getMockStore: jest.Mock<{
|
|
21
|
+
[x: string]: unknown;
|
|
22
|
+
}, []>;
|
|
23
|
+
setMockStore: jest.Mock<void, [data: any]>;
|
|
22
24
|
};
|
|
23
|
-
export default
|
|
25
|
+
export default StorageMock;
|
|
@@ -1,85 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
11
17
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return new Promise((resolve) => {
|
|
19
|
-
Promise.all(setPromises).then(() => resolve(storageMapInternal));
|
|
20
|
-
});
|
|
21
|
-
},
|
|
22
|
-
getItem(key) {
|
|
23
|
-
return Promise.resolve(storageMapInternal[key]);
|
|
24
|
-
},
|
|
25
|
-
multiGet(keys) {
|
|
26
|
-
const getPromises = keys.map((key) => new Promise((resolve) => {
|
|
27
|
-
this.getItem(key).then((value) => resolve([key, value]));
|
|
28
|
-
}));
|
|
29
|
-
return Promise.all(getPromises);
|
|
30
|
-
},
|
|
31
|
-
multiMerge(pairs) {
|
|
32
|
-
pairs.forEach(([key, value]) => {
|
|
33
|
-
const existingValue = storageMapInternal[key];
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
const newValue = utils_1.default.fastMerge(existingValue, value);
|
|
36
|
-
set(key, newValue);
|
|
37
|
-
});
|
|
38
|
-
return Promise.resolve(storageMapInternal);
|
|
39
|
-
},
|
|
40
|
-
mergeItem(key, _changes, modifiedData) {
|
|
41
|
-
return this.setItem(key, modifiedData);
|
|
42
|
-
},
|
|
43
|
-
removeItem(key) {
|
|
44
|
-
delete storageMapInternal[key];
|
|
45
|
-
return Promise.resolve();
|
|
46
|
-
},
|
|
47
|
-
removeItems(keys) {
|
|
48
|
-
keys.forEach((key) => {
|
|
49
|
-
delete storageMapInternal[key];
|
|
50
|
-
});
|
|
51
|
-
return Promise.resolve();
|
|
52
|
-
},
|
|
53
|
-
clear() {
|
|
54
|
-
storageMapInternal = {};
|
|
55
|
-
return Promise.resolve();
|
|
56
|
-
},
|
|
57
|
-
getAllKeys() {
|
|
58
|
-
return Promise.resolve(Object.keys(storageMapInternal));
|
|
59
|
-
},
|
|
60
|
-
getDatabaseSize() {
|
|
61
|
-
return Promise.resolve({ bytesRemaining: 0, bytesUsed: 99999 });
|
|
62
|
-
},
|
|
63
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
64
|
-
setMemoryOnlyKeys() { },
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
65
24
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
multiSet: jest.fn(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
getDatabaseSize: jest.fn(
|
|
83
|
-
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const MemoryOnlyProvider_1 = __importStar(require("../providers/MemoryOnlyProvider"));
|
|
27
|
+
const init = jest.fn(MemoryOnlyProvider_1.default.init);
|
|
28
|
+
init();
|
|
29
|
+
const StorageMock = {
|
|
30
|
+
init,
|
|
31
|
+
getItem: jest.fn(MemoryOnlyProvider_1.default.getItem),
|
|
32
|
+
multiGet: jest.fn(MemoryOnlyProvider_1.default.multiGet),
|
|
33
|
+
setItem: jest.fn(MemoryOnlyProvider_1.default.setItem),
|
|
34
|
+
multiSet: jest.fn(MemoryOnlyProvider_1.default.multiSet),
|
|
35
|
+
mergeItem: jest.fn(MemoryOnlyProvider_1.default.mergeItem),
|
|
36
|
+
multiMerge: jest.fn(MemoryOnlyProvider_1.default.multiMerge),
|
|
37
|
+
removeItem: jest.fn(MemoryOnlyProvider_1.default.removeItem),
|
|
38
|
+
removeItems: jest.fn(MemoryOnlyProvider_1.default.removeItems),
|
|
39
|
+
clear: jest.fn(MemoryOnlyProvider_1.default.clear),
|
|
40
|
+
getAllKeys: jest.fn(MemoryOnlyProvider_1.default.getAllKeys),
|
|
41
|
+
getDatabaseSize: jest.fn(MemoryOnlyProvider_1.default.getDatabaseSize),
|
|
42
|
+
keepInstancesSync: jest.fn(),
|
|
43
|
+
mockSet: MemoryOnlyProvider_1.mockSet,
|
|
44
|
+
getMockStore: jest.fn(() => MemoryOnlyProvider_1.mockStore),
|
|
45
|
+
setMockStore: jest.fn((data) => (0, MemoryOnlyProvider_1.setMockStore)(data)),
|
|
84
46
|
};
|
|
85
|
-
exports.default =
|
|
47
|
+
exports.default = StorageMock;
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import type StorageProvider from './providers/types';
|
|
2
|
+
type Storage = {
|
|
3
|
+
getStorageProvider: () => StorageProvider;
|
|
4
|
+
} & Omit<StorageProvider, 'name'>;
|
|
5
|
+
declare const Storage: Storage;
|
|
6
|
+
export default Storage;
|
package/dist/storage/index.js
CHANGED
|
@@ -1,7 +1,175 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
|
|
29
|
+
const Logger = __importStar(require("../Logger"));
|
|
30
|
+
const platforms_1 = __importDefault(require("./platforms"));
|
|
31
|
+
const InstanceSync_1 = __importDefault(require("./InstanceSync"));
|
|
32
|
+
const MemoryOnlyProvider_1 = __importDefault(require("./providers/MemoryOnlyProvider"));
|
|
33
|
+
let provider = platforms_1.default;
|
|
34
|
+
let shouldKeepInstancesSync = false;
|
|
35
|
+
let finishInitalization;
|
|
36
|
+
const initPromise = new Promise((resolve) => {
|
|
37
|
+
finishInitalization = resolve;
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Degrade performance by removing the storage provider and only using cache
|
|
41
|
+
*/
|
|
42
|
+
function degradePerformance(error) {
|
|
43
|
+
Logger.logAlert(`Error while using ${provider.name}. Falling back to only using cache and dropping storage.`);
|
|
44
|
+
console.error(error);
|
|
45
|
+
provider = MemoryOnlyProvider_1.default;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Runs a piece of code and degrades performance if certain errors are thrown
|
|
49
|
+
*/
|
|
50
|
+
function tryOrDegradePerformance(fn, waitForInitialization = true) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const promise = waitForInitialization ? initPromise : Promise.resolve();
|
|
53
|
+
promise.then(() => {
|
|
54
|
+
try {
|
|
55
|
+
resolve(fn());
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Test for known critical errors that the storage provider throws, e.g. when storage is full
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
// IndexedDB error when storage is full (https://github.com/Expensify/App/issues/29403)
|
|
61
|
+
if (error.message.includes('Internal error opening backing store for indexedDB.open')) {
|
|
62
|
+
degradePerformance(error);
|
|
63
|
+
}
|
|
64
|
+
// catch the error if DB connection can not be established/DB can not be created
|
|
65
|
+
if (error.message.includes('IDBKeyVal store could not be created')) {
|
|
66
|
+
degradePerformance(error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
reject(error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const Storage = {
|
|
75
|
+
/**
|
|
76
|
+
* Returns the storage provider currently in use
|
|
77
|
+
*/
|
|
78
|
+
getStorageProvider() {
|
|
79
|
+
return provider;
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* Initializes all providers in the list of storage providers
|
|
83
|
+
* and enables fallback providers if necessary
|
|
84
|
+
*/
|
|
85
|
+
init() {
|
|
86
|
+
tryOrDegradePerformance(provider.init, false).finally(() => {
|
|
87
|
+
finishInitalization();
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Get the value of a given key or return `null` if it's not available
|
|
92
|
+
*/
|
|
93
|
+
getItem: (key) => tryOrDegradePerformance(() => provider.getItem(key)),
|
|
94
|
+
/**
|
|
95
|
+
* Get multiple key-value pairs for the give array of keys in a batch
|
|
96
|
+
*/
|
|
97
|
+
multiGet: (keys) => tryOrDegradePerformance(() => provider.multiGet(keys)),
|
|
98
|
+
/**
|
|
99
|
+
* Sets the value for a given key. The only requirement is that the value should be serializable to JSON string
|
|
100
|
+
*/
|
|
101
|
+
setItem: (key, value) => tryOrDegradePerformance(() => {
|
|
102
|
+
const promise = provider.setItem(key, value);
|
|
103
|
+
if (shouldKeepInstancesSync) {
|
|
104
|
+
return promise.then(() => InstanceSync_1.default.setItem(key));
|
|
105
|
+
}
|
|
106
|
+
return promise;
|
|
107
|
+
}),
|
|
108
|
+
/**
|
|
109
|
+
* Stores multiple key-value pairs in a batch
|
|
110
|
+
*/
|
|
111
|
+
multiSet: (pairs) => tryOrDegradePerformance(() => provider.multiSet(pairs)),
|
|
112
|
+
/**
|
|
113
|
+
* Merging an existing value with a new one
|
|
114
|
+
*/
|
|
115
|
+
mergeItem: (key, changes, modifiedData) => tryOrDegradePerformance(() => {
|
|
116
|
+
const promise = provider.mergeItem(key, changes, modifiedData);
|
|
117
|
+
if (shouldKeepInstancesSync) {
|
|
118
|
+
return promise.then(() => InstanceSync_1.default.mergeItem(key));
|
|
119
|
+
}
|
|
120
|
+
return promise;
|
|
121
|
+
}),
|
|
122
|
+
/**
|
|
123
|
+
* Multiple merging of existing and new values in a batch
|
|
124
|
+
* This function also removes all nested null values from an object.
|
|
125
|
+
*/
|
|
126
|
+
multiMerge: (pairs) => tryOrDegradePerformance(() => provider.multiMerge(pairs)),
|
|
127
|
+
/**
|
|
128
|
+
* Removes given key and its value
|
|
129
|
+
*/
|
|
130
|
+
removeItem: (key) => tryOrDegradePerformance(() => {
|
|
131
|
+
const promise = provider.removeItem(key);
|
|
132
|
+
if (shouldKeepInstancesSync) {
|
|
133
|
+
return promise.then(() => InstanceSync_1.default.removeItem(key));
|
|
134
|
+
}
|
|
135
|
+
return promise;
|
|
136
|
+
}),
|
|
137
|
+
/**
|
|
138
|
+
* Remove given keys and their values
|
|
139
|
+
*/
|
|
140
|
+
removeItems: (keys) => tryOrDegradePerformance(() => {
|
|
141
|
+
const promise = provider.removeItems(keys);
|
|
142
|
+
if (shouldKeepInstancesSync) {
|
|
143
|
+
return promise.then(() => InstanceSync_1.default.removeItems(keys));
|
|
144
|
+
}
|
|
145
|
+
return promise;
|
|
146
|
+
}),
|
|
147
|
+
/**
|
|
148
|
+
* Clears everything
|
|
149
|
+
*/
|
|
150
|
+
clear: () => tryOrDegradePerformance(() => {
|
|
151
|
+
if (shouldKeepInstancesSync) {
|
|
152
|
+
return InstanceSync_1.default.clear(() => provider.clear());
|
|
153
|
+
}
|
|
154
|
+
return provider.clear();
|
|
155
|
+
}),
|
|
156
|
+
/**
|
|
157
|
+
* Returns all available keys
|
|
158
|
+
*/
|
|
159
|
+
getAllKeys: () => tryOrDegradePerformance(() => provider.getAllKeys()),
|
|
160
|
+
/**
|
|
161
|
+
* Gets the total bytes of the store
|
|
162
|
+
*/
|
|
163
|
+
getDatabaseSize: () => tryOrDegradePerformance(() => provider.getDatabaseSize()),
|
|
164
|
+
/**
|
|
165
|
+
* @param onStorageKeyChanged - Storage synchronization mechanism keeping all opened tabs in sync (web only)
|
|
166
|
+
*/
|
|
167
|
+
keepInstancesSync(onStorageKeyChanged) {
|
|
168
|
+
// If InstanceSync shouldn't be used, it means we're on a native platform and we don't need to keep instances in sync
|
|
169
|
+
if (!InstanceSync_1.default.shouldBeUsed)
|
|
170
|
+
return;
|
|
171
|
+
shouldKeepInstancesSync = true;
|
|
172
|
+
InstanceSync_1.default.init(onStorageKeyChanged, this);
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
exports.default = Storage;
|
|
@@ -3,5 +3,5 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
exports.default =
|
|
6
|
+
const IDBKeyValProvider_1 = __importDefault(require("../providers/IDBKeyValProvider"));
|
|
7
|
+
exports.default = IDBKeyValProvider_1.default;
|
|
@@ -3,5 +3,5 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
exports.default =
|
|
6
|
+
const SQLiteProvider_1 = __importDefault(require("../providers/SQLiteProvider"));
|
|
7
|
+
exports.default = SQLiteProvider_1.default;
|
|
@@ -7,24 +7,30 @@ const idb_keyval_1 = require("idb-keyval");
|
|
|
7
7
|
const utils_1 = __importDefault(require("../../utils"));
|
|
8
8
|
// We don't want to initialize the store while the JS bundle loads as idb-keyval will try to use global.indexedDB
|
|
9
9
|
// which might not be available in certain environments that load the bundle (e.g. electron main process).
|
|
10
|
-
let
|
|
11
|
-
function getCustomStore() {
|
|
12
|
-
if (!customStoreInstance) {
|
|
13
|
-
customStoreInstance = (0, idb_keyval_1.createStore)('OnyxDB', 'keyvaluepairs');
|
|
14
|
-
}
|
|
15
|
-
return customStoreInstance;
|
|
16
|
-
}
|
|
10
|
+
let idbKeyValStore;
|
|
17
11
|
const provider = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The name of the provider that can be printed to the logs
|
|
14
|
+
*/
|
|
15
|
+
name: 'IDBKeyValProvider',
|
|
16
|
+
/**
|
|
17
|
+
* Initializes the storage provider
|
|
18
|
+
*/
|
|
19
|
+
init() {
|
|
20
|
+
const newIdbKeyValStore = (0, idb_keyval_1.createStore)('OnyxDB', 'keyvaluepairs');
|
|
21
|
+
if (newIdbKeyValStore == null)
|
|
22
|
+
throw Error('IDBKeyVal store could not be created');
|
|
23
|
+
idbKeyValStore = newIdbKeyValStore;
|
|
24
|
+
},
|
|
25
|
+
setItem: (key, value) => (0, idb_keyval_1.set)(key, value, idbKeyValStore),
|
|
26
|
+
multiGet: (keysParam) => (0, idb_keyval_1.getMany)(keysParam, idbKeyValStore).then((values) => values.map((value, index) => [keysParam[index], value])),
|
|
27
|
+
multiMerge: (pairs) => idbKeyValStore('readwrite', (store) => {
|
|
21
28
|
// Note: we are using the manual store transaction here, to fit the read and update
|
|
22
29
|
// of the items in one transaction to achieve best performance.
|
|
23
30
|
const getValues = Promise.all(pairs.map(([key]) => (0, idb_keyval_1.promisifyRequest)(store.get(key))));
|
|
24
31
|
return getValues.then((values) => {
|
|
25
32
|
const upsertMany = pairs.map(([key, value], index) => {
|
|
26
33
|
const prev = values[index];
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
34
|
const newValue = utils_1.default.fastMerge(prev, value);
|
|
29
35
|
return (0, idb_keyval_1.promisifyRequest)(store.put(newValue, key));
|
|
30
36
|
});
|
|
@@ -35,16 +41,14 @@ const provider = {
|
|
|
35
41
|
// Since Onyx also merged the existing value with the changes, we can just set the value directly
|
|
36
42
|
return provider.setItem(key, modifiedData);
|
|
37
43
|
},
|
|
38
|
-
multiSet: (pairs) => (0, idb_keyval_1.setMany)(pairs,
|
|
39
|
-
clear: () => (0, idb_keyval_1.clear)(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
getAllKeys: () => (0, idb_keyval_1.keys)(getCustomStore()),
|
|
43
|
-
getItem: (key) => (0, idb_keyval_1.get)(key, getCustomStore())
|
|
44
|
+
multiSet: (pairs) => (0, idb_keyval_1.setMany)(pairs, idbKeyValStore),
|
|
45
|
+
clear: () => (0, idb_keyval_1.clear)(idbKeyValStore),
|
|
46
|
+
getAllKeys: () => (0, idb_keyval_1.keys)(idbKeyValStore),
|
|
47
|
+
getItem: (key) => (0, idb_keyval_1.get)(key, idbKeyValStore)
|
|
44
48
|
// idb-keyval returns undefined for missing items, but this needs to return null so that idb-keyval does the same thing as SQLiteStorage.
|
|
45
49
|
.then((val) => (val === undefined ? null : val)),
|
|
46
|
-
removeItem: (key) => (0, idb_keyval_1.del)(key,
|
|
47
|
-
removeItems: (keysParam) => (0, idb_keyval_1.delMany)(keysParam,
|
|
50
|
+
removeItem: (key) => (0, idb_keyval_1.del)(key, idbKeyValStore),
|
|
51
|
+
removeItems: (keysParam) => (0, idb_keyval_1.delMany)(keysParam, idbKeyValStore),
|
|
48
52
|
getDatabaseSize() {
|
|
49
53
|
if (!window.navigator || !window.navigator.storage) {
|
|
50
54
|
throw new Error('StorageManager browser API unavailable');
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type StorageProvider from './types';
|
|
2
|
+
import type { OnyxKey, OnyxValue } from '../../types';
|
|
3
|
+
type Store = Record<OnyxKey, OnyxValue<OnyxKey>>;
|
|
4
|
+
declare let store: Store;
|
|
5
|
+
declare const set: (key: OnyxKey, value: OnyxValue<OnyxKey>) => Promise<unknown>;
|
|
6
|
+
declare const provider: StorageProvider;
|
|
7
|
+
declare const setMockStore: (data: Store) => void;
|
|
8
|
+
export default provider;
|
|
9
|
+
export { store as mockStore, set as mockSet, setMockStore };
|