react-native-onyx 2.0.114 → 2.0.116

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.
@@ -19,6 +19,8 @@ declare class OnyxCache {
19
19
  private recentKeys;
20
20
  /** A map of cached values */
21
21
  private storageMap;
22
+ /** Index mapping collection keys to their member keys for O(1) lookup */
23
+ private collectionIndex;
22
24
  /**
23
25
  * Captured pending tasks for already running storage methods
24
26
  * Using a map yields better performance on operations such a delete
@@ -32,6 +34,8 @@ declare class OnyxCache {
32
34
  private evictionBlocklist;
33
35
  /** List of keys that have been directly subscribed to or recently modified from least to most recent */
34
36
  private recentlyAccessedKeys;
37
+ /** Set of collection keys for fast lookup */
38
+ private collectionKeys;
35
39
  constructor();
36
40
  /** Get all the storage keys */
37
41
  getAllKeys(): Set<OnyxKey>;
@@ -145,6 +149,26 @@ declare class OnyxCache {
145
149
  * Finds a key that can be safely evicted
146
150
  */
147
151
  getKeyForEviction(): OnyxKey | undefined;
152
+ /**
153
+ * Set the collection keys for optimized storage
154
+ */
155
+ setCollectionKeys(collectionKeys: Set<OnyxKey>): void;
156
+ /**
157
+ * Check if a key is a collection key
158
+ */
159
+ isCollectionKey(key: OnyxKey): boolean;
160
+ /**
161
+ * Get the collection key for a given member key
162
+ */
163
+ getCollectionKey(key: OnyxKey): OnyxKey | null;
164
+ /**
165
+ * Get all data for a collection key
166
+ */
167
+ getCollectionData(collectionKey: OnyxKey): Record<OnyxKey, OnyxValue<OnyxKey>> | undefined;
168
+ /**
169
+ * Get all member keys for a collection key
170
+ */
171
+ getCollectionMemberKeys(collectionKey: OnyxKey): Set<OnyxKey> | undefined;
148
172
  }
149
173
  declare const instance: OnyxCache;
150
174
  export default instance;
package/dist/OnyxCache.js CHANGED
@@ -52,13 +52,16 @@ class OnyxCache {
52
52
  this.evictionBlocklist = {};
53
53
  /** List of keys that have been directly subscribed to or recently modified from least to most recent */
54
54
  this.recentlyAccessedKeys = new Set();
55
+ /** Set of collection keys for fast lookup */
56
+ this.collectionKeys = new Set();
55
57
  this.storageKeys = new Set();
56
58
  this.nullishStorageKeys = new Set();
57
59
  this.recentKeys = new Set();
58
60
  this.storageMap = {};
61
+ this.collectionIndex = {};
59
62
  this.pendingPromises = new Map();
60
63
  // bind all public methods to prevent problems with `this`
61
- (0, bindAll_1.default)(this, 'getAllKeys', 'get', 'hasCacheForKey', 'addKey', 'addNullishStorageKey', 'hasNullishStorageKey', 'clearNullishStorageKeys', 'set', 'drop', 'merge', 'hasPendingTask', 'getTaskPromise', 'captureTask', 'addToAccessedKeys', 'removeLeastRecentlyUsedKeys', 'setRecentKeysLimit', 'setAllKeys', 'setEvictionAllowList', 'getEvictionBlocklist', 'isEvictableKey', 'removeLastAccessedKey', 'addLastAccessedKey', 'addEvictableKeysToRecentlyAccessedList', 'getKeyForEviction');
64
+ (0, bindAll_1.default)(this, 'getAllKeys', 'get', 'hasCacheForKey', 'addKey', 'addNullishStorageKey', 'hasNullishStorageKey', 'clearNullishStorageKeys', 'set', 'drop', 'merge', 'hasPendingTask', 'getTaskPromise', 'captureTask', 'addToAccessedKeys', 'removeLeastRecentlyUsedKeys', 'setRecentKeysLimit', 'setAllKeys', 'setEvictionAllowList', 'getEvictionBlocklist', 'isEvictableKey', 'removeLastAccessedKey', 'addLastAccessedKey', 'addEvictableKeysToRecentlyAccessedList', 'getKeyForEviction', 'setCollectionKeys', 'isCollectionKey', 'getCollectionKey', 'getCollectionData', 'getCollectionMemberKeys');
62
65
  }
63
66
  /** Get all the storage keys */
64
67
  getAllKeys() {
@@ -120,16 +123,37 @@ class OnyxCache {
120
123
  // When a key is explicitly set in cache, we can remove it from the list of nullish keys,
121
124
  // since it will either be set to a non nullish value or removed from the cache completely.
122
125
  this.nullishStorageKeys.delete(key);
126
+ const collectionKey = this.getCollectionKey(key);
123
127
  if (value === null || value === undefined) {
124
128
  delete this.storageMap[key];
129
+ // Remove from collection index if it's a collection member
130
+ if (collectionKey && this.getCollectionMemberKeys(collectionKey)) {
131
+ this.collectionIndex[collectionKey].delete(key);
132
+ }
125
133
  return undefined;
126
134
  }
127
135
  this.storageMap[key] = value;
136
+ // Update collection index if this is a collection member
137
+ if (collectionKey) {
138
+ if (!this.getCollectionMemberKeys(collectionKey)) {
139
+ this.collectionIndex[collectionKey] = new Set();
140
+ }
141
+ this.collectionIndex[collectionKey].add(key);
142
+ }
128
143
  return value;
129
144
  }
130
145
  /** Forget the cached value for the given key */
131
146
  drop(key) {
132
147
  delete this.storageMap[key];
148
+ // Update collection index if this is a collection member
149
+ const collectionKey = this.getCollectionKey(key);
150
+ if (collectionKey && this.getCollectionMemberKeys(collectionKey)) {
151
+ this.collectionIndex[collectionKey].delete(key);
152
+ }
153
+ // If this is a collection key, clear its index
154
+ if (this.isCollectionKey(key)) {
155
+ delete this.collectionIndex[key];
156
+ }
133
157
  this.storageKeys.delete(key);
134
158
  this.recentKeys.delete(key);
135
159
  }
@@ -145,11 +169,23 @@ class OnyxCache {
145
169
  Object.entries(data).forEach(([key, value]) => {
146
170
  this.addKey(key);
147
171
  this.addToAccessedKeys(key);
172
+ const collectionKey = this.getCollectionKey(key);
148
173
  if (value === null || value === undefined) {
149
174
  this.addNullishStorageKey(key);
175
+ // Remove from collection index if it's a collection member
176
+ if (collectionKey && this.getCollectionMemberKeys(collectionKey)) {
177
+ this.collectionIndex[collectionKey].delete(key);
178
+ }
150
179
  }
151
180
  else {
152
181
  this.nullishStorageKeys.delete(key);
182
+ // Update collection index if this is a collection member
183
+ if (collectionKey) {
184
+ if (!this.getCollectionMemberKeys(collectionKey)) {
185
+ this.collectionIndex[collectionKey] = new Set();
186
+ }
187
+ this.collectionIndex[collectionKey].add(key);
188
+ }
153
189
  }
154
190
  });
155
191
  }
@@ -208,6 +244,11 @@ class OnyxCache {
208
244
  }
209
245
  for (const key of keysToRemove) {
210
246
  delete this.storageMap[key];
247
+ // Update collection index if this is a collection member
248
+ const collectionKey = this.getCollectionKey(key);
249
+ if (collectionKey && this.getCollectionMemberKeys(collectionKey)) {
250
+ this.collectionIndex[collectionKey].delete(key);
251
+ }
211
252
  this.recentKeys.delete(key);
212
253
  }
213
254
  }
@@ -217,7 +258,8 @@ class OnyxCache {
217
258
  }
218
259
  /** Check if the value has changed */
219
260
  hasValueChanged(key, value) {
220
- return !(0, fast_equals_1.deepEqual)(this.storageMap[key], value);
261
+ const currentValue = this.get(key, false);
262
+ return !(0, fast_equals_1.deepEqual)(currentValue, value);
221
263
  }
222
264
  /**
223
265
  * Sets the list of keys that are considered safe for eviction
@@ -298,6 +340,59 @@ class OnyxCache {
298
340
  }
299
341
  return undefined;
300
342
  }
343
+ /**
344
+ * Set the collection keys for optimized storage
345
+ */
346
+ setCollectionKeys(collectionKeys) {
347
+ this.collectionKeys = collectionKeys;
348
+ // Initialize collection indexes for existing collection keys
349
+ collectionKeys.forEach((collectionKey) => {
350
+ if (this.getCollectionMemberKeys(collectionKey)) {
351
+ return;
352
+ }
353
+ this.collectionIndex[collectionKey] = new Set();
354
+ });
355
+ }
356
+ /**
357
+ * Check if a key is a collection key
358
+ */
359
+ isCollectionKey(key) {
360
+ return this.collectionKeys.has(key);
361
+ }
362
+ /**
363
+ * Get the collection key for a given member key
364
+ */
365
+ getCollectionKey(key) {
366
+ for (const collectionKey of this.collectionKeys) {
367
+ if (key.startsWith(collectionKey) && key.length > collectionKey.length) {
368
+ return collectionKey;
369
+ }
370
+ }
371
+ return null;
372
+ }
373
+ /**
374
+ * Get all data for a collection key
375
+ */
376
+ getCollectionData(collectionKey) {
377
+ const memberKeys = this.getCollectionMemberKeys(collectionKey);
378
+ if (!memberKeys || memberKeys.size === 0) {
379
+ return undefined;
380
+ }
381
+ const collectionData = {};
382
+ memberKeys.forEach((memberKey) => {
383
+ const value = this.storageMap[memberKey];
384
+ if (value !== undefined) {
385
+ collectionData[memberKey] = value;
386
+ }
387
+ });
388
+ return collectionData;
389
+ }
390
+ /**
391
+ * Get all member keys for a collection key
392
+ */
393
+ getCollectionMemberKeys(collectionKey) {
394
+ return this.collectionIndex[collectionKey];
395
+ }
301
396
  }
302
397
  const instance = new OnyxCache();
303
398
  exports.default = instance;
package/dist/OnyxUtils.js CHANGED
@@ -135,6 +135,8 @@ function initStoreValues(keys, initialKeyStates, evictableKeys, fullyMergedSnaps
135
135
  DevTools_1.default.initState(initialKeyStates);
136
136
  // Let Onyx know about which keys are safe to evict
137
137
  OnyxCache_1.default.setEvictionAllowList(evictableKeys);
138
+ // Set collection keys in cache for optimized storage
139
+ OnyxCache_1.default.setCollectionKeys(onyxCollectionKeySet);
138
140
  if (typeof keys.COLLECTION === 'object' && typeof keys.COLLECTION.SNAPSHOT === 'string') {
139
141
  snapshotKey = keys.COLLECTION.SNAPSHOT;
140
142
  fullyMergedSnapshotKeys = new Set(fullyMergedSnapshotKeysParam !== null && fullyMergedSnapshotKeysParam !== void 0 ? fullyMergedSnapshotKeysParam : []);
@@ -428,20 +430,27 @@ function getCollectionKey(key) {
428
430
  function tryGetCachedValue(key, mapping) {
429
431
  let val = OnyxCache_1.default.get(key);
430
432
  if (isCollectionKey(key)) {
433
+ const collectionData = OnyxCache_1.default.getCollectionData(key);
431
434
  const allCacheKeys = OnyxCache_1.default.getAllKeys();
432
- // It is possible we haven't loaded all keys yet so we do not know if the
433
- // collection actually exists.
434
- if (allCacheKeys.size === 0) {
435
- return;
435
+ if (collectionData !== undefined && allCacheKeys.size > 0) {
436
+ val = collectionData;
436
437
  }
437
- const values = {};
438
- allCacheKeys.forEach((cacheKey) => {
439
- if (!cacheKey.startsWith(key)) {
438
+ else {
439
+ // Fallback to original logic
440
+ // It is possible we haven't loaded all keys yet so we do not know if the
441
+ // collection actually exists.
442
+ if (allCacheKeys.size === 0) {
440
443
  return;
441
444
  }
442
- values[cacheKey] = OnyxCache_1.default.get(cacheKey);
443
- });
444
- val = values;
445
+ const values = {};
446
+ allCacheKeys.forEach((cacheKey) => {
447
+ if (!cacheKey.startsWith(key)) {
448
+ return;
449
+ }
450
+ values[cacheKey] = OnyxCache_1.default.get(cacheKey);
451
+ });
452
+ val = values;
453
+ }
445
454
  }
446
455
  if (mapping === null || mapping === void 0 ? void 0 : mapping.selector) {
447
456
  const state = mapping.withOnyxInstance ? mapping.withOnyxInstance.state : undefined;
@@ -453,7 +462,27 @@ function tryGetCachedValue(key, mapping) {
453
462
  return val;
454
463
  }
455
464
  function getCachedCollection(collectionKey, collectionMemberKeys) {
465
+ // Use optimized collection data retrieval when cache is populated
466
+ const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
456
467
  const allKeys = collectionMemberKeys || OnyxCache_1.default.getAllKeys();
468
+ if (collectionData !== undefined && (Array.isArray(allKeys) ? allKeys.length > 0 : allKeys.size > 0)) {
469
+ // If we have specific member keys, filter the collection
470
+ if (collectionMemberKeys) {
471
+ const filteredCollection = {};
472
+ collectionMemberKeys.forEach((key) => {
473
+ if (collectionData[key] !== undefined) {
474
+ filteredCollection[key] = collectionData[key];
475
+ }
476
+ else if (OnyxCache_1.default.hasNullishStorageKey(key)) {
477
+ filteredCollection[key] = OnyxCache_1.default.get(key);
478
+ }
479
+ });
480
+ return filteredCollection;
481
+ }
482
+ // Return a copy to avoid mutations affecting the cache
483
+ return Object.assign({}, collectionData);
484
+ }
485
+ // Fallback to original implementation if collection data not available
457
486
  const collection = {};
458
487
  // forEach exists on both Set and Array
459
488
  allKeys.forEach((key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.114",
3
+ "version": "2.0.116",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",
@@ -89,8 +89,8 @@
89
89
  "react-dom": "18.2.0",
90
90
  "react-native": "0.76.3",
91
91
  "react-native-device-info": "^10.3.0",
92
- "react-native-nitro-modules": "^0.24.1",
93
- "react-native-nitro-sqlite": "^9.1.8",
92
+ "react-native-nitro-modules": "^0.26.2",
93
+ "react-native-nitro-sqlite": "^9.1.10",
94
94
  "react-native-performance": "^2.0.0",
95
95
  "react-test-renderer": "18.2.0",
96
96
  "reassure": "1.4.0",
@@ -104,8 +104,8 @@
104
104
  "react-dom": ">=18.1.0",
105
105
  "react-native": ">=0.75.0",
106
106
  "react-native-device-info": "^10.3.0",
107
- "react-native-nitro-modules": ">=0.24.1",
108
- "react-native-nitro-sqlite": "^9.1.4",
107
+ "react-native-nitro-modules": ">=0.26.2",
108
+ "react-native-nitro-sqlite": "^9.1.10",
109
109
  "react-native-performance": "^5.1.0"
110
110
  },
111
111
  "peerDependenciesMeta": {