Skip to content

Commit 8af4d44

Browse files
committed
Add more comments
1 parent 7f3c4d9 commit 8af4d44

10 files changed

Lines changed: 92 additions & 77 deletions

File tree

API-INTERNAL.md

Lines changed: 39 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
<dt><a href="#getDeferredInitTask">getDeferredInitTask()</a></dt>
1818
<dd><p>Getter - returns the deffered init task.</p>
1919
</dd>
20-
<dt><a href="#getEvictionBlocklist">getEvictionBlocklist()</a></dt>
21-
<dd><p>Getter - returns the eviction block list.</p>
22-
</dd>
2320
<dt><a href="#getSkippableCollectionMemberIDs">getSkippableCollectionMemberIDs()</a></dt>
2421
<dd><p>Getter - returns the skippable collection member IDs.</p>
2522
</dd>
@@ -71,9 +68,6 @@ is associated with a collection of keys.</p>
7168
<dd><p>Checks to see if a provided key is the exact configured key of our connected subscriber
7269
or if the provided key is a collection member key (in case our configured key is a &quot;collection key&quot;)</p>
7370
</dd>
74-
<dt><a href="#isEvictableKey">isEvictableKey()</a></dt>
75-
<dd><p>Checks to see if this key has been flagged as safe for removal.</p>
76-
</dd>
7771
<dt><a href="#getCollectionKey">getCollectionKey(key)</a> ⇒</dt>
7872
<dd><p>Extracts the collection identifier of a given collection member key.</p>
7973
<p>For example:</p>
@@ -88,20 +82,6 @@ or if the provided key is a collection member key (in case our configured key is
8882
<dd><p>Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
8983
If the requested key is a collection, it will return an object with all the collection members.</p>
9084
</dd>
91-
<dt><a href="#removeLastAccessedKey">removeLastAccessedKey()</a></dt>
92-
<dd><p>Remove a key from the recently accessed key list.</p>
93-
</dd>
94-
<dt><a href="#addLastAccessedKey">addLastAccessedKey()</a></dt>
95-
<dd><p>Add a key to the list of recently accessed keys. The least
96-
recently accessed key should be at the head and the most
97-
recently accessed key at the tail.</p>
98-
</dd>
99-
<dt><a href="#addEvictableKeysToRecentlyAccessedList">addEvictableKeysToRecentlyAccessedList()</a></dt>
100-
<dd><p>Take all the keys that are safe to evict and add them to
101-
the recently accessed list when initializing the app. This
102-
enables keys that have not recently been accessed to be
103-
removed.</p>
104-
</dd>
10585
<dt><a href="#keysChanged">keysChanged()</a></dt>
10686
<dd><p>When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks</p>
10787
</dd>
@@ -144,8 +124,15 @@ whatever it is we attempted to do.</p>
144124
This method transforms an object like {&#39;@MyApp_user&#39;: myUserValue, &#39;@MyApp_key&#39;: myKeyValue}
145125
to an array of key-value pairs in the above format and removes key-value pairs that are being set to null</p>
146126
</dd>
147-
<dt><a href="#mergeChanges">mergeChanges(changes)</a></dt>
148-
<dd><p>Merges an array of changes with an existing value or creates a single change</p>
127+
<dt><a href="#mergeChanges">mergeChanges(changes, existingValue)</a></dt>
128+
<dd><p>Merges an array of changes with an existing value or creates a single change.</p>
129+
</dd>
130+
<dt><a href="#mergeAndMarkChanges">mergeAndMarkChanges(changes, existingValue)</a></dt>
131+
<dd><p>Merges an array of changes with an existing value or creates a single change.
132+
It will also mark deep nested objects that need to be entirely replaced during the merge.</p>
133+
</dd>
134+
<dt><a href="#applyMerge">applyMerge(changes, existingValue)</a></dt>
135+
<dd><p>Merges an array of changes with an existing value or creates a single change.</p>
149136
</dd>
150137
<dt><a href="#initializeWithDefaultKeyStates">initializeWithDefaultKeyStates()</a></dt>
151138
<dd><p>Merge user provided default key value pairs.</p>
@@ -187,12 +174,6 @@ Getter - returns the default key states.
187174
## getDeferredInitTask()
188175
Getter - returns the deffered init task.
189176

190-
**Kind**: global function
191-
<a name="getEvictionBlocklist"></a>
192-
193-
## getEvictionBlocklist()
194-
Getter - returns the eviction block list.
195-
196177
**Kind**: global function
197178
<a name="getSkippableCollectionMemberIDs"></a>
198179

@@ -217,7 +198,7 @@ Sets the initial values for the Onyx store
217198
| --- | --- |
218199
| keys | `ONYXKEYS` constants object from Onyx.init() |
219200
| initialKeyStates | initial data to set when `init()` and `clear()` are called |
220-
| evictableKeys | This is an array of keys (individual or collection patterns) that are eligible for automatic removal when storage limits are reached. |
201+
| evictableKeys | This is an array of keys (individual or collection patterns) that when provided to Onyx are flagged as "safe" for removal. |
221202

222203
<a name="maybeFlushBatchUpdates"></a>
223204

@@ -313,12 +294,6 @@ or throws an Error if the key is not a collection one.
313294
Checks to see if a provided key is the exact configured key of our connected subscriber
314295
or if the provided key is a collection member key (in case our configured key is a "collection key")
315296

316-
**Kind**: global function
317-
<a name="isEvictableKey"></a>
318-
319-
## isEvictableKey()
320-
Checks to see if this key has been flagged as safe for removal.
321-
322297
**Kind**: global function
323298
<a name="getCollectionKey"></a>
324299

@@ -344,29 +319,6 @@ For example:
344319
Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
345320
If the requested key is a collection, it will return an object with all the collection members.
346321

347-
**Kind**: global function
348-
<a name="removeLastAccessedKey"></a>
349-
350-
## removeLastAccessedKey()
351-
Remove a key from the recently accessed key list.
352-
353-
**Kind**: global function
354-
<a name="addLastAccessedKey"></a>
355-
356-
## addLastAccessedKey()
357-
Add a key to the list of recently accessed keys. The least
358-
recently accessed key should be at the head and the most
359-
recently accessed key at the tail.
360-
361-
**Kind**: global function
362-
<a name="addEvictableKeysToRecentlyAccessedList"></a>
363-
364-
## addEvictableKeysToRecentlyAccessedList()
365-
Take all the keys that are safe to evict and add them to
366-
the recently accessed list when initializing the app. This
367-
enables keys that have not recently been accessed to be
368-
removed.
369-
370322
**Kind**: global function
371323
<a name="keysChanged"></a>
372324

@@ -460,7 +412,6 @@ whatever it is we attempted to do.
460412
Notifies subscribers and writes current value to cache
461413

462414
**Kind**: global function
463-
**Returns**: The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
464415
<a name="prepareKeyValuePairsForStorage"></a>
465416

466417
## prepareKeyValuePairsForStorage() ⇒
@@ -472,14 +423,40 @@ to an array of key-value pairs in the above format and removes key-value pairs t
472423
**Returns**: an array of key - value pairs <[key, value]>
473424
<a name="mergeChanges"></a>
474425

475-
## mergeChanges(changes)
476-
Merges an array of changes with an existing value or creates a single change
426+
## mergeChanges(changes, existingValue)
427+
Merges an array of changes with an existing value or creates a single change.
428+
429+
**Kind**: global function
430+
431+
| Param | Description |
432+
| --- | --- |
433+
| changes | Array of changes that should be merged |
434+
| existingValue | The existing value that should be merged with the changes |
435+
436+
<a name="mergeAndMarkChanges"></a>
437+
438+
## mergeAndMarkChanges(changes, existingValue)
439+
Merges an array of changes with an existing value or creates a single change.
440+
It will also mark deep nested objects that need to be entirely replaced during the merge.
441+
442+
**Kind**: global function
443+
444+
| Param | Description |
445+
| --- | --- |
446+
| changes | Array of changes that should be merged |
447+
| existingValue | The existing value that should be merged with the changes |
448+
449+
<a name="applyMerge"></a>
450+
451+
## applyMerge(changes, existingValue)
452+
Merges an array of changes with an existing value or creates a single change.
477453

478454
**Kind**: global function
479455

480456
| Param | Description |
481457
| --- | --- |
482-
| changes | Array of changes that should be applied to the existing value |
458+
| changes | Array of changes that should be merged |
459+
| existingValue | The existing value that should be merged with the changes |
483460

484461
<a name="initializeWithDefaultKeyStates"></a>
485462

API.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Values of type <code>Object</code> get merged with the old value, whilst for <co
2828
applied in the order they were called. Note: <code>Onyx.set()</code> calls do not work this way so use caution when mixing
2929
<code>Onyx.merge()</code> and <code>Onyx.set()</code>.</p>
3030
</dd>
31-
<dt><a href="#mergeCollection">mergeCollection(collectionKey, collection)</a></dt>
31+
<dt><a href="#mergeCollection">mergeCollection(collectionKey, collection, mergeReplaceNullPatches)</a></dt>
3232
<dd><p>Merges a collection based on their keys</p>
3333
</dd>
3434
<dt><a href="#clear">clear(keysToPreserve)</a></dt>
@@ -157,7 +157,7 @@ Onyx.merge(ONYXKEYS.POLICY, {name: 'My Workspace'}); // -> {id: 1, name: 'My Wor
157157
```
158158
<a name="mergeCollection"></a>
159159

160-
## mergeCollection(collectionKey, collection)
160+
## mergeCollection(collectionKey, collection, mergeReplaceNullPatches)
161161
Merges a collection based on their keys
162162

163163
**Kind**: global function
@@ -166,6 +166,7 @@ Merges a collection based on their keys
166166
| --- | --- |
167167
| collectionKey | e.g. `ONYXKEYS.COLLECTION.REPORT` |
168168
| collection | Object collection keyed by individual collection member keys and values |
169+
| mergeReplaceNullPatches | Record where the key is a collection member key and the value is a list of tuples that we'll use to replace the nested objects of that collection member record with something else. |
169170

170171
**Example**
171172
```js

lib/Onyx.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ function merge<TKey extends OnyxKey>(key: TKey, changes: OnyxMergeInput<TKey>):
356356
*
357357
* @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
358358
* @param collection Object collection keyed by individual collection member keys and values
359+
* @param mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
360+
* tuples that we'll use to replace the nested objects of that collection member record with something else.
359361
*/
360362
function mergeCollection<TKey extends CollectionKeyBase, TMap>(
361363
collectionKey: TKey,

lib/OnyxMerge/index.native.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const applyMerge: ApplyMerge = <TKey extends OnyxKey>(key: TKey, existingValue:
3131
return Promise.resolve({mergedValue, updatePromise});
3232
}
3333

34+
// For native platforms we use `mergeItem` that will take advantage of JSON_PATCH and JSON_REPLACE SQL operations to
35+
// merge the object in a performant way.
3436
return Storage.mergeItem(key, batchedChanges as OnyxValue<TKey>, replaceNullPatches).then(() => ({
3537
mergedValue,
3638
updatePromise,

lib/OnyxMerge/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const applyMerge: ApplyMerge = <TKey extends OnyxKey>(key: TKey, existingValue:
2323
return Promise.resolve({mergedValue, updatePromise});
2424
}
2525

26+
// For web platforms we use `setItem` since the object was already merged with its changes before.
2627
return Storage.setItem(key, mergedValue as OnyxValue<TKey>).then(() => ({
2728
mergedValue,
2829
updatePromise,

lib/OnyxUtils.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,8 +1147,8 @@ function hasPendingMergeForKey(key: OnyxKey): boolean {
11471147
* Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
11481148
* This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
11491149
* to an array of key-value pairs in the above format and removes key-value pairs that are being set to null
1150-
1151-
* @return an array of key - value pairs <[key, value]>
1150+
*
1151+
* @return an array of key - value pairs <[key, value]>
11521152
*/
11531153
function prepareKeyValuePairsForStorage(
11541154
data: Record<OnyxKey, OnyxInput<OnyxKey>>,
@@ -1173,10 +1173,23 @@ function prepareKeyValuePairsForStorage(
11731173
return pairs;
11741174
}
11751175

1176+
/**
1177+
* Merges an array of changes with an existing value or creates a single change.
1178+
*
1179+
* @param changes Array of changes that should be merged
1180+
* @param existingValue The existing value that should be merged with the changes
1181+
*/
11761182
function mergeChanges<TValue extends OnyxInput<OnyxKey> | undefined, TChange extends OnyxInput<OnyxKey> | undefined>(changes: TChange[], existingValue?: TValue): FastMergeResult<TChange> {
11771183
return applyMerge('merge', changes, existingValue);
11781184
}
11791185

1186+
/**
1187+
* Merges an array of changes with an existing value or creates a single change.
1188+
* It will also mark deep nested objects that need to be entirely replaced during the merge.
1189+
*
1190+
* @param changes Array of changes that should be merged
1191+
* @param existingValue The existing value that should be merged with the changes
1192+
*/
11801193
function mergeAndMarkChanges<TValue extends OnyxInput<OnyxKey> | undefined, TChange extends OnyxInput<OnyxKey> | undefined>(
11811194
changes: TChange[],
11821195
existingValue?: TValue,
@@ -1185,7 +1198,7 @@ function mergeAndMarkChanges<TValue extends OnyxInput<OnyxKey> | undefined, TCha
11851198
}
11861199

11871200
/**
1188-
* Merges an array of changes with an existing value or creates a single change
1201+
* Merges an array of changes with an existing value or creates a single change.
11891202
*
11901203
* @param changes Array of changes that should be merged
11911204
* @param existingValue The existing value that should be merged with the changes

lib/storage/providers/SQLiteProvider.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,17 @@ type PageCountResult = {
4444
const DB_NAME = 'OnyxDB';
4545
let db: NitroSQLiteConnection;
4646

47-
function replacer(key: string, value: unknown) {
47+
/**
48+
* Prevents the stringifying of the object markers.
49+
*/
50+
function objectMarkRemover(key: string, value: unknown) {
4851
if (key === utils.ONYX_INTERNALS__REPLACE_OBJECT_MARK) return undefined;
4952
return value;
5053
}
5154

55+
/**
56+
* Transforms the replace null patches into SQL queries to be passed to JSON_REPLACE.
57+
*/
5258
function generateJSONReplaceSQLQueries(key: string, patches: FastMergeReplaceNullPatch[]): string[][] {
5359
const queries = patches.map(([pathArray, value]) => {
5460
const jsonPath = `$.${pathArray.join('.')}`;
@@ -114,12 +120,15 @@ const provider: StorageProvider = {
114120
multiMerge(pairs) {
115121
const commands: BatchQueryCommand[] = [];
116122

123+
// Query to merge the change into the DB value.
117124
const patchQuery = `INSERT INTO keyvaluepairs (record_key, valueJSON)
118125
VALUES (:key, JSON(:value))
119126
ON CONFLICT DO UPDATE
120127
SET valueJSON = JSON_PATCH(valueJSON, JSON(:value));
121128
`;
122129
const patchQueryArguments: string[][] = [];
130+
131+
// Query to fully replace the nested objects of the DB value.
123132
const replaceQuery = `UPDATE keyvaluepairs
124133
SET valueJSON = JSON_REPLACE(valueJSON, ?, JSON(?))
125134
WHERE record_key = ?;
@@ -128,12 +137,9 @@ const provider: StorageProvider = {
128137

129138
const nonNullishPairs = pairs.filter((pair) => pair[1] !== undefined);
130139

131-
// eslint-disable-next-line @typescript-eslint/prefer-for-of
132-
for (let i = 0; i < nonNullishPairs.length; i++) {
133-
const [key, value, replaceNullPatches] = nonNullishPairs[i];
134-
135-
const valueAfterReplace = JSON.stringify(value, replacer);
136-
patchQueryArguments.push([key, valueAfterReplace]);
140+
for (const [key, value, replaceNullPatches] of nonNullishPairs) {
141+
const changeWithoutMarkers = JSON.stringify(value, objectMarkRemover);
142+
patchQueryArguments.push([key, changeWithoutMarkers]);
137143

138144
const patches = replaceNullPatches ?? [];
139145
if (patches.length > 0) {

lib/types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,13 @@ type InitOptions = {
486486
// eslint-disable-next-line @typescript-eslint/no-explicit-any
487487
type GenericFunction = (...args: any[]) => any;
488488

489-
type MultiMergeReplaceNullPatches = {[TKey in OnyxKey]: FastMergeReplaceNullPatch[]};
489+
/**
490+
* Represents a record where the key is a collection member key and the value is a list of
491+
* tuples that we'll use to replace the nested objects of that collection member record with something else.
492+
*/
493+
type MultiMergeReplaceNullPatches = {
494+
[TKey in OnyxKey]: FastMergeReplaceNullPatch[];
495+
};
490496

491497
/**
492498
* Represents a combination of Merge and Set operations that should be executed in Onyx

lib/utils.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ import type {ConnectOptions, OnyxInput, OnyxKey} from './types';
33
type EmptyObject = Record<string, never>;
44
type EmptyValue = EmptyObject | null | undefined;
55

6+
/**
7+
* A tuple where the first value is the path to the nested object that contains the
8+
* internal `ONYX_INTERNALS__REPLACE_OBJECT_MARK` flag, and the second value is the data we want to replace
9+
* in that path.
10+
*
11+
* This tuple will be used in SQLiteProvider to replace the nested object using `JSON_REPLACE`.
12+
* */
613
type FastMergeReplaceNullPatch = [string[], unknown];
714

815
type FastMergeOptions = {
@@ -18,7 +25,7 @@ type FastMergeOptions = {
1825
};
1926

2027
type FastMergeMetadata = {
21-
/** The path to the object that contains the internal "ONYX_INTERNALS__REPLACE_OBJECT_MARK" flag. */
28+
/** The list of tuples that will be used in SQLiteProvider to replace the nested objects using `JSON_REPLACE`. */
2229
replaceNullPatches: FastMergeReplaceNullPatch[];
2330
};
2431

tests/unit/fastMergeTest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ describe('fastMerge', () => {
138138
expect(result.replaceNullPatches).toEqual([[['b', 'd'], {h: 'h'}]]);
139139
});
140140

141-
it('should completely replace the target object with its source when the source has the "ONYX_INTERNALS__REPLACE_OBJECT_MARK" flag and "shouldReplaceMarkedObjects" is true', () => {
141+
it('should completely replace the target object with its source when the source has the "ONYX_INTERNALS__REPLACE_OBJECT_MARK" flag and "objectRemovalMode" is set to "replace"', () => {
142142
const result = utils.fastMerge(
143143
testObject,
144144
{

0 commit comments

Comments
 (0)