react-native-onyx 2.0.117 → 2.0.118
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 +2 -2
- package/dist/Onyx.d.ts +1 -1
- package/dist/Onyx.js +36 -152
- package/dist/OnyxCache.js +4 -1
- package/dist/OnyxMerge/index.d.ts +5 -0
- package/dist/OnyxMerge/index.js +30 -0
- package/dist/OnyxMerge/index.native.d.ts +5 -0
- package/dist/OnyxMerge/index.native.js +37 -0
- package/dist/OnyxMerge/types.d.ts +7 -0
- package/dist/OnyxMerge/types.js +2 -0
- package/dist/OnyxUtils.d.ts +36 -21
- package/dist/OnyxUtils.js +175 -45
- package/dist/storage/InstanceSync/index.web.d.ts +2 -2
- package/dist/storage/__mocks__/index.d.ts +9 -9
- package/dist/storage/index.js +2 -2
- package/dist/storage/providers/IDBKeyValProvider.js +8 -5
- package/dist/storage/providers/MemoryOnlyProvider.js +8 -5
- package/dist/storage/providers/NoopProvider.js +1 -1
- package/dist/storage/providers/SQLiteProvider.js +56 -22
- package/dist/storage/providers/types.d.ts +20 -22
- package/dist/types.d.ts +10 -1
- package/dist/utils.d.ts +34 -6
- package/dist/utils.js +92 -64
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { BuiltIns } from 'type-fest/source/internal';
|
|
|
3
3
|
import type OnyxUtils from './OnyxUtils';
|
|
4
4
|
import type { WithOnyxInstance, WithOnyxState } from './withOnyx/types';
|
|
5
5
|
import type { OnyxMethod } from './OnyxUtils';
|
|
6
|
+
import type { FastMergeReplaceNullPatch } from './utils';
|
|
6
7
|
/**
|
|
7
8
|
* Utility type that excludes `null` from the type `TValue`.
|
|
8
9
|
*/
|
|
@@ -412,11 +413,19 @@ type InitOptions = {
|
|
|
412
413
|
fullyMergedSnapshotKeys?: string[];
|
|
413
414
|
};
|
|
414
415
|
type GenericFunction = (...args: any[]) => any;
|
|
416
|
+
/**
|
|
417
|
+
* Represents a record where the key is a collection member key and the value is a list of
|
|
418
|
+
* tuples that we'll use to replace the nested objects of that collection member record with something else.
|
|
419
|
+
*/
|
|
420
|
+
type MultiMergeReplaceNullPatches = {
|
|
421
|
+
[TKey in OnyxKey]: FastMergeReplaceNullPatch[];
|
|
422
|
+
};
|
|
415
423
|
/**
|
|
416
424
|
* Represents a combination of Merge and Set operations that should be executed in Onyx
|
|
417
425
|
*/
|
|
418
426
|
type MixedOperationsQueue = {
|
|
419
427
|
merge: OnyxInputKeyValueMapping;
|
|
428
|
+
mergeReplaceNullPatches: MultiMergeReplaceNullPatches;
|
|
420
429
|
set: OnyxInputKeyValueMapping;
|
|
421
430
|
};
|
|
422
|
-
export type { BaseConnectOptions, Collection, CollectionConnectCallback, CollectionConnectOptions, CollectionKey, CollectionKeyBase, ConnectOptions, CustomTypeOptions, DeepRecord, DefaultConnectCallback, DefaultConnectOptions, ExtractOnyxCollectionValue, GenericFunction, InitOptions, Key, KeyValueMapping, Mapping, NonNull, NonUndefined, OnyxInputKeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput, OnyxMethod, OnyxMethodMap, OnyxUpdate, OnyxValue, Selector, WithOnyxConnectOptions, MixedOperationsQueue, };
|
|
431
|
+
export type { BaseConnectOptions, Collection, CollectionConnectCallback, CollectionConnectOptions, CollectionKey, CollectionKeyBase, ConnectOptions, CustomTypeOptions, DeepRecord, DefaultConnectCallback, DefaultConnectOptions, ExtractOnyxCollectionValue, GenericFunction, InitOptions, Key, KeyValueMapping, Mapping, NonNull, NonUndefined, OnyxInputKeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput, OnyxMethod, OnyxMethodMap, OnyxUpdate, OnyxValue, Selector, WithOnyxConnectOptions, MultiMergeReplaceNullPatches, MixedOperationsQueue, };
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
import type { ConnectOptions, OnyxInput, OnyxKey } from './types';
|
|
2
2
|
type EmptyObject = Record<string, never>;
|
|
3
3
|
type EmptyValue = EmptyObject | null | undefined;
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* A tuple where the first value is the path to the nested object that contains the
|
|
6
|
+
* internal `ONYX_INTERNALS__REPLACE_OBJECT_MARK` flag, and the second value is the data we want to replace
|
|
7
|
+
* in that path.
|
|
8
|
+
*
|
|
9
|
+
* This tuple will be used in SQLiteProvider to replace the nested object using `JSON_REPLACE`.
|
|
10
|
+
* */
|
|
11
|
+
type FastMergeReplaceNullPatch = [string[], unknown];
|
|
12
|
+
type FastMergeOptions = {
|
|
13
|
+
/** If true, null object values will be removed. */
|
|
14
|
+
shouldRemoveNestedNulls?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* If set to "mark", we will mark objects that are set to null instead of simply removing them,
|
|
17
|
+
* so that we can batch changes together, without losing information about the object removal.
|
|
18
|
+
* If set to "replace", we will completely replace the marked objects with the new value instead of merging them.
|
|
19
|
+
*/
|
|
20
|
+
objectRemovalMode?: 'mark' | 'replace' | 'none';
|
|
21
|
+
};
|
|
22
|
+
type FastMergeMetadata = {
|
|
23
|
+
/** The list of tuples that will be used in SQLiteProvider to replace the nested objects using `JSON_REPLACE`. */
|
|
24
|
+
replaceNullPatches: FastMergeReplaceNullPatch[];
|
|
25
|
+
};
|
|
26
|
+
type FastMergeResult<TValue> = {
|
|
27
|
+
/** The result of the merge. */
|
|
28
|
+
result: TValue;
|
|
29
|
+
/** The list of tuples that will be used in SQLiteProvider to replace the nested objects using `JSON_REPLACE`. */
|
|
30
|
+
replaceNullPatches: FastMergeReplaceNullPatch[];
|
|
31
|
+
};
|
|
6
32
|
/**
|
|
7
33
|
* Merges two objects and removes null values if "shouldRemoveNestedNulls" is set to true
|
|
8
34
|
*
|
|
9
35
|
* We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
|
|
10
|
-
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
|
|
11
|
-
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
|
|
12
36
|
*/
|
|
13
|
-
declare function fastMerge<TValue>(target: TValue, source: TValue,
|
|
37
|
+
declare function fastMerge<TValue>(target: TValue, source: TValue, options?: FastMergeOptions, metadata?: FastMergeMetadata, basePath?: string[]): FastMergeResult<TValue>;
|
|
38
|
+
/** Checks whether the given object is an object and not null/undefined. */
|
|
39
|
+
declare function isEmptyObject<T>(obj: T | EmptyValue): obj is EmptyValue;
|
|
14
40
|
/** Deep removes the nested null values from the given value. */
|
|
15
41
|
declare function removeNestedNullValues<TValue extends OnyxInput<OnyxKey> | null>(value: TValue): TValue;
|
|
16
42
|
/** Formats the action name by uppercasing and adding the key if provided. */
|
|
@@ -42,13 +68,15 @@ declare function omit<TValue>(obj: Record<string, TValue>, condition: string | s
|
|
|
42
68
|
*/
|
|
43
69
|
declare function hasWithOnyxInstance<TKey extends OnyxKey>(mapping: ConnectOptions<TKey>): unknown;
|
|
44
70
|
declare const _default: {
|
|
45
|
-
isEmptyObject: typeof isEmptyObject;
|
|
46
71
|
fastMerge: typeof fastMerge;
|
|
72
|
+
isEmptyObject: typeof isEmptyObject;
|
|
47
73
|
formatActionName: typeof formatActionName;
|
|
48
74
|
removeNestedNullValues: typeof removeNestedNullValues;
|
|
49
75
|
checkCompatibilityWithExistingValue: typeof checkCompatibilityWithExistingValue;
|
|
50
76
|
pick: typeof pick;
|
|
51
77
|
omit: typeof omit;
|
|
52
78
|
hasWithOnyxInstance: typeof hasWithOnyxInstance;
|
|
79
|
+
ONYX_INTERNALS__REPLACE_OBJECT_MARK: string;
|
|
53
80
|
};
|
|
54
81
|
export default _default;
|
|
82
|
+
export type { FastMergeResult, FastMergeReplaceNullPatch, FastMergeOptions };
|
package/dist/utils.js
CHANGED
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
function isEmptyObject(obj) {
|
|
5
|
-
return typeof obj === 'object' && Object.keys(obj || {}).length === 0;
|
|
6
|
-
}
|
|
7
|
-
// Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1
|
|
3
|
+
const ONYX_INTERNALS__REPLACE_OBJECT_MARK = 'ONYX_INTERNALS__REPLACE_OBJECT_MARK';
|
|
8
4
|
/**
|
|
9
|
-
*
|
|
5
|
+
* Merges two objects and removes null values if "shouldRemoveNestedNulls" is set to true
|
|
6
|
+
*
|
|
7
|
+
* We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
|
|
10
8
|
*/
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
function fastMerge(target, source, options, metadata, basePath = []) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
if (!metadata) {
|
|
12
|
+
// eslint-disable-next-line no-param-reassign
|
|
13
|
+
metadata = {
|
|
14
|
+
replaceNullPatches: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
// We have to ignore arrays and nullish values here,
|
|
18
|
+
// otherwise "mergeObject" will throw an error,
|
|
19
|
+
// because it expects an object as "source"
|
|
20
|
+
if (Array.isArray(source) || source === null || source === undefined) {
|
|
21
|
+
return { result: source, replaceNullPatches: metadata.replaceNullPatches };
|
|
22
|
+
}
|
|
23
|
+
const optionsWithDefaults = {
|
|
24
|
+
shouldRemoveNestedNulls: (_a = options === null || options === void 0 ? void 0 : options.shouldRemoveNestedNulls) !== null && _a !== void 0 ? _a : false,
|
|
25
|
+
objectRemovalMode: (_b = options === null || options === void 0 ? void 0 : options.objectRemovalMode) !== null && _b !== void 0 ? _b : 'none',
|
|
26
|
+
};
|
|
27
|
+
const mergedValue = mergeObject(target, source, optionsWithDefaults, metadata, basePath);
|
|
28
|
+
return { result: mergedValue, replaceNullPatches: metadata.replaceNullPatches };
|
|
14
29
|
}
|
|
15
30
|
/**
|
|
16
31
|
* Merges the source object into the target object.
|
|
17
32
|
* @param target - The target object.
|
|
18
33
|
* @param source - The source object.
|
|
19
|
-
* @param
|
|
34
|
+
* @param options - The options for the merge.
|
|
35
|
+
* @param metadata - The metadata for the merge.
|
|
36
|
+
* @param basePath - The base path for the merge.
|
|
20
37
|
* @returns - The merged object.
|
|
21
38
|
*/
|
|
22
|
-
function mergeObject(target, source,
|
|
39
|
+
function mergeObject(target, source, options, metadata, basePath) {
|
|
23
40
|
const destination = {};
|
|
24
41
|
const targetObject = isMergeableObject(target) ? target : undefined;
|
|
25
42
|
// First we want to copy over all keys from the target into the destination object,
|
|
@@ -27,66 +44,67 @@ function mergeObject(target, source, shouldRemoveNestedNulls = true) {
|
|
|
27
44
|
// If "shouldRemoveNestedNulls" is true, we want to remove null values from the merged object
|
|
28
45
|
// and therefore we need to omit keys where either the source or target value is null.
|
|
29
46
|
if (targetObject) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
// If
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// the key explicitly to prevent loose undefined values in objects in cache and storage.
|
|
39
|
-
const isSourceOrTargetNull = targetValue === undefined || targetValue === null || sourceValue === null;
|
|
40
|
-
const shouldOmitTargetKey = shouldRemoveNestedNulls && isSourceOrTargetNull;
|
|
41
|
-
if (!shouldOmitTargetKey) {
|
|
42
|
-
destination[key] = targetValue;
|
|
47
|
+
Object.keys(targetObject).forEach((key) => {
|
|
48
|
+
const targetProperty = targetObject === null || targetObject === void 0 ? void 0 : targetObject[key];
|
|
49
|
+
const sourceProperty = source === null || source === void 0 ? void 0 : source[key];
|
|
50
|
+
// If "shouldRemoveNestedNulls" is true, we want to remove (nested) null values from the merged object.
|
|
51
|
+
// If either the source or target value is null, we want to omit the key from the merged object.
|
|
52
|
+
const shouldOmitNullishProperty = options.shouldRemoveNestedNulls && (targetProperty === null || sourceProperty === null);
|
|
53
|
+
if (targetProperty === undefined || shouldOmitNullishProperty) {
|
|
54
|
+
return;
|
|
43
55
|
}
|
|
44
|
-
|
|
56
|
+
destination[key] = targetProperty;
|
|
57
|
+
});
|
|
45
58
|
}
|
|
46
59
|
// After copying over all keys from the target object, we want to merge the source object into the destination object.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
// If
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const shouldOmitSourceKey = sourceValue === undefined || shouldIgnoreNullSourceValue;
|
|
56
|
-
if (!shouldOmitSourceKey) {
|
|
57
|
-
// If the source value is a mergable object, we want to merge it into the target value.
|
|
58
|
-
// If "shouldRemoveNestedNulls" is true, "fastMerge" will recursively
|
|
59
|
-
// remove nested null values from the merged object.
|
|
60
|
-
// If source value is any other value we need to set the source value it directly.
|
|
61
|
-
if (isMergeableObject(sourceValue)) {
|
|
62
|
-
// If the target value is null or undefined, we need to fallback to an empty object,
|
|
63
|
-
// so that we can still use "fastMerge" to merge the source value,
|
|
64
|
-
// to ensure that nested null values are removed from the merged object.
|
|
65
|
-
const targetValueWithFallback = (targetValue !== null && targetValue !== void 0 ? targetValue : {});
|
|
66
|
-
destination[key] = fastMerge(targetValueWithFallback, sourceValue, shouldRemoveNestedNulls);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
destination[key] = sourceValue;
|
|
70
|
-
}
|
|
60
|
+
Object.keys(source).forEach((key) => {
|
|
61
|
+
let targetProperty = targetObject === null || targetObject === void 0 ? void 0 : targetObject[key];
|
|
62
|
+
const sourceProperty = source === null || source === void 0 ? void 0 : source[key];
|
|
63
|
+
// If "shouldRemoveNestedNulls" is true, we want to remove (nested) null values from the merged object.
|
|
64
|
+
// If the source value is null, we want to omit the key from the merged object.
|
|
65
|
+
const shouldOmitNullishProperty = options.shouldRemoveNestedNulls && sourceProperty === null;
|
|
66
|
+
if (sourceProperty === undefined || shouldOmitNullishProperty) {
|
|
67
|
+
return;
|
|
71
68
|
}
|
|
72
|
-
|
|
69
|
+
// If the source value is not a mergable object, we need to set the key directly.
|
|
70
|
+
if (!isMergeableObject(sourceProperty)) {
|
|
71
|
+
destination[key] = sourceProperty;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// If "shouldMarkRemovedObjects" is enabled and the previous merge change (targetProperty) is null,
|
|
75
|
+
// it means we want to fully replace this object when merging the batched changes with the Onyx value.
|
|
76
|
+
// To achieve this, we first mark these nested objects with an internal flag.
|
|
77
|
+
// When calling fastMerge again with "mark" removal mode, the marked objects will be removed.
|
|
78
|
+
if (options.objectRemovalMode === 'mark' && targetProperty === null) {
|
|
79
|
+
targetProperty = { [ONYX_INTERNALS__REPLACE_OBJECT_MARK]: true };
|
|
80
|
+
metadata.replaceNullPatches.push([[...basePath, key], Object.assign({}, sourceProperty)]);
|
|
81
|
+
}
|
|
82
|
+
// Later, when merging the batched changes with the Onyx value, if a nested object of the batched changes
|
|
83
|
+
// has the internal flag set, we replace the entire destination object with the source one and remove
|
|
84
|
+
// the flag.
|
|
85
|
+
if (options.objectRemovalMode === 'replace' && sourceProperty[ONYX_INTERNALS__REPLACE_OBJECT_MARK]) {
|
|
86
|
+
// We do a spread here in order to have a new object reference and allow us to delete the internal flag
|
|
87
|
+
// of the merged object only.
|
|
88
|
+
const sourcePropertyWithoutMark = Object.assign({}, sourceProperty);
|
|
89
|
+
delete sourcePropertyWithoutMark.ONYX_INTERNALS__REPLACE_OBJECT_MARK;
|
|
90
|
+
destination[key] = sourcePropertyWithoutMark;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
destination[key] = fastMerge(targetProperty, sourceProperty, options, metadata, [...basePath, key]).result;
|
|
94
|
+
});
|
|
73
95
|
return destination;
|
|
74
96
|
}
|
|
97
|
+
/** Checks whether the given object is an object and not null/undefined. */
|
|
98
|
+
function isEmptyObject(obj) {
|
|
99
|
+
return typeof obj === 'object' && Object.keys(obj || {}).length === 0;
|
|
100
|
+
}
|
|
75
101
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* We generally want to remove null values from objects written to disk and cache, because it decreases the amount of data stored in memory and on disk.
|
|
79
|
-
* On native, when merging an existing value with new changes, SQLite will use JSON_PATCH, which removes top-level nullish values.
|
|
80
|
-
* To be consistent with the behaviour for merge, we'll also want to remove null values for "set" operations.
|
|
102
|
+
* Checks whether the given value can be merged. It has to be an object, but not an array, RegExp or Date.
|
|
103
|
+
* Mostly copied from https://medium.com/@lubaka.a/how-to-remove-lodash-performance-improvement-b306669ad0e1.
|
|
81
104
|
*/
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// because it expects an object as "source"
|
|
86
|
-
if (Array.isArray(source) || source === null || source === undefined) {
|
|
87
|
-
return source;
|
|
88
|
-
}
|
|
89
|
-
return mergeObject(target, source, shouldRemoveNestedNulls);
|
|
105
|
+
function isMergeableObject(value) {
|
|
106
|
+
const isNonNullObject = value != null ? typeof value === 'object' : false;
|
|
107
|
+
return isNonNullObject && !(value instanceof RegExp) && !(value instanceof Date) && !Array.isArray(value);
|
|
90
108
|
}
|
|
91
109
|
/** Deep removes the nested null values from the given value. */
|
|
92
110
|
function removeNestedNullValues(value) {
|
|
@@ -192,4 +210,14 @@ function omit(obj, condition) {
|
|
|
192
210
|
function hasWithOnyxInstance(mapping) {
|
|
193
211
|
return 'withOnyxInstance' in mapping && mapping.withOnyxInstance;
|
|
194
212
|
}
|
|
195
|
-
exports.default = {
|
|
213
|
+
exports.default = {
|
|
214
|
+
fastMerge,
|
|
215
|
+
isEmptyObject,
|
|
216
|
+
formatActionName,
|
|
217
|
+
removeNestedNullValues,
|
|
218
|
+
checkCompatibilityWithExistingValue,
|
|
219
|
+
pick,
|
|
220
|
+
omit,
|
|
221
|
+
hasWithOnyxInstance,
|
|
222
|
+
ONYX_INTERNALS__REPLACE_OBJECT_MARK,
|
|
223
|
+
};
|