react-native-onyx 2.0.100 → 2.0.101

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/dist/Onyx.js CHANGED
@@ -28,7 +28,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  /* eslint-disable no-continue */
30
30
  const underscore_1 = __importDefault(require("underscore"));
31
- const pick_1 = __importDefault(require("lodash/pick"));
32
31
  const Logger = __importStar(require("./Logger"));
33
32
  const OnyxCache_1 = __importStar(require("./OnyxCache"));
34
33
  const PerformanceUtils = __importStar(require("./PerformanceUtils"));
@@ -540,51 +539,6 @@ function clear(keysToPreserve = []) {
540
539
  .then(() => undefined);
541
540
  return OnyxCache_1.default.captureTask(OnyxCache_1.TASK.CLEAR, promise);
542
541
  }
543
- function updateSnapshots(data) {
544
- const snapshotCollectionKey = OnyxUtils_1.default.getSnapshotKey();
545
- if (!snapshotCollectionKey)
546
- return [];
547
- const promises = [];
548
- const snapshotCollection = OnyxUtils_1.default.getCachedCollection(snapshotCollectionKey);
549
- const snapshotCollectionKeyLength = snapshotCollectionKey.length;
550
- Object.entries(snapshotCollection).forEach(([snapshotKey, snapshotValue]) => {
551
- // Snapshots may not be present in cache. We don't know how to update them so we skip.
552
- if (!snapshotValue) {
553
- return;
554
- }
555
- let updatedData = {};
556
- data.forEach(({ key, value }) => {
557
- // snapshots are normal keys so we want to skip update if they are written to Onyx
558
- if (OnyxUtils_1.default.isCollectionMemberKey(snapshotCollectionKey, key, snapshotCollectionKeyLength)) {
559
- return;
560
- }
561
- if (typeof snapshotValue !== 'object' || !('data' in snapshotValue)) {
562
- return;
563
- }
564
- const snapshotData = snapshotValue.data;
565
- if (!snapshotData || !snapshotData[key]) {
566
- return;
567
- }
568
- if (Array.isArray(value) || Array.isArray(snapshotData[key])) {
569
- updatedData[key] = value || [];
570
- return;
571
- }
572
- if (value === null) {
573
- updatedData[key] = value;
574
- return;
575
- }
576
- const oldValue = updatedData[key] || {};
577
- const newValue = (0, pick_1.default)(value, Object.keys(snapshotData[key]));
578
- updatedData = Object.assign(Object.assign({}, updatedData), { [key]: Object.assign(oldValue, newValue) });
579
- });
580
- // Skip the update if there's no data to be merged
581
- if (utils_1.default.isEmptyObject(updatedData)) {
582
- return;
583
- }
584
- promises.push(() => merge(snapshotKey, { data: updatedData }));
585
- });
586
- return promises;
587
- }
588
542
  /**
589
543
  * Insert API responses and lifecycle data into Onyx
590
544
  *
@@ -699,7 +653,7 @@ function update(data) {
699
653
  promises.push(() => merge(key, batchedChanges));
700
654
  }
701
655
  });
702
- const snapshotPromises = updateSnapshots(data);
656
+ const snapshotPromises = OnyxUtils_1.default.updateSnapshots(data, merge);
703
657
  // We need to run the snapshot updates before the other updates so the snapshot data can be updated before the loading state in the snapshot
704
658
  const finalPromises = snapshotPromises.concat(promises);
705
659
  return clearPromise.then(() => Promise.all(finalPromises.map((p) => p()))).then(() => undefined);
@@ -1,6 +1,7 @@
1
1
  import type { ValueOf } from 'type-fest';
2
2
  import type Onyx from './Onyx';
3
- import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, Mapping, OnyxCollection, OnyxEntry, OnyxInput, OnyxKey, OnyxMergeCollectionInput, OnyxValue } from './types';
3
+ import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, Mapping, OnyxCollection, OnyxEntry, OnyxInput, OnyxKey, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, Selector } from './types';
4
+ import type { WithOnyxState } from './withOnyx/types';
4
5
  import type { DeferredTask } from './createDeferredTask';
5
6
  declare const METHOD: {
6
7
  readonly SET: "set";
@@ -66,6 +67,12 @@ declare function sendActionToDevTools(method: Exclude<OnyxMethod, typeof METHOD.
66
67
  */
67
68
  declare function maybeFlushBatchUpdates(): Promise<void>;
68
69
  declare function batchUpdates(updates: () => void): Promise<void>;
70
+ /**
71
+ * Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}})
72
+ * and runs it through a reducer function to return a subset of the data according to a selector.
73
+ * The resulting collection will only contain items that are returned by the selector.
74
+ */
75
+ declare function reduceCollectionWithSelector<TKey extends CollectionKeyBase, TMap, TReturn>(collection: OnyxCollection<KeyValueMapping[TKey]>, selector: Selector<TKey, TMap, TReturn>, withOnyxInstanceState: WithOnyxState<TMap> | undefined): Record<string, TReturn>;
69
76
  /** Get some data from the store */
70
77
  declare function get<TKey extends OnyxKey, TValue extends OnyxValue<TKey>>(key: TKey): Promise<TValue>;
71
78
  declare function multiGet<TKey extends OnyxKey>(keys: CollectionKeyBase[]): Promise<Map<OnyxKey, OnyxValue<TKey>>>;
@@ -78,6 +85,19 @@ declare function multiGet<TKey extends OnyxKey>(keys: CollectionKeyBase[]): Prom
78
85
  declare function tupleGet<Keys extends readonly OnyxKey[]>(keys: Keys): Promise<{
79
86
  [Index in keyof Keys]: OnyxValue<Keys[Index]>;
80
87
  }>;
88
+ /**
89
+ * Stores a subscription ID associated with a given key.
90
+ *
91
+ * @param subscriptionID - A subscription ID of the subscriber.
92
+ * @param key - A key that the subscriber is subscribed to.
93
+ */
94
+ declare function storeKeyBySubscriptions(key: OnyxKey, subscriptionID: number): void;
95
+ /**
96
+ * Deletes a subscription ID associated with its corresponding key.
97
+ *
98
+ * @param subscriptionID - The subscription ID to be deleted.
99
+ */
100
+ declare function deleteKeyBySubscriptions(subscriptionID: number): void;
81
101
  /** Returns current key names stored in persisted storage */
82
102
  declare function getAllKeys(): Promise<Set<OnyxKey>>;
83
103
  /**
@@ -144,7 +164,7 @@ declare function getCachedCollection<TKey extends CollectionKeyBase>(collectionK
144
164
  /**
145
165
  * When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
146
166
  */
147
- declare function keysChanged<TKey extends CollectionKeyBase>(collectionKey: TKey, partialCollection: OnyxCollection<KeyValueMapping[TKey]>, partialPreviousCollection: OnyxCollection<KeyValueMapping[TKey]> | undefined, notifyRegularSubscibers?: boolean, notifyWithOnyxSubscibers?: boolean): void;
167
+ declare function keysChanged<TKey extends CollectionKeyBase>(collectionKey: TKey, partialCollection: OnyxCollection<KeyValueMapping[TKey]>, partialPreviousCollection: OnyxCollection<KeyValueMapping[TKey]> | undefined, notifyConnectSubscribers?: boolean, notifyWithOnyxSubscribers?: boolean): void;
148
168
  /**
149
169
  * When a key change happens, search for any callbacks matching the key or collection key and trigger those callbacks
150
170
  *
@@ -158,6 +178,11 @@ declare function keyChanged<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TK
158
178
  * - triggers the callback function
159
179
  */
160
180
  declare function sendDataToConnection<TKey extends OnyxKey>(mapping: Mapping<TKey>, value: OnyxValue<TKey> | null, matchedKey: TKey | undefined, isBatched: boolean): void;
181
+ /**
182
+ * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
183
+ * run out of storage the least recently accessed key can be removed.
184
+ */
185
+ declare function addKeyToRecentlyAccessedIfNeeded<TKey extends OnyxKey>(mapping: Mapping<TKey>): void;
161
186
  /**
162
187
  * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
163
188
  */
@@ -242,6 +267,7 @@ declare function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOpt
242
267
  * @param subscriptionID Subscription ID returned by calling `OnyxUtils.subscribeToKey()`.
243
268
  */
244
269
  declare function unsubscribeFromKey(subscriptionID: number): void;
270
+ declare function updateSnapshots(data: OnyxUpdate[], mergeFn: typeof Onyx.merge): Array<() => Promise<void>>;
245
271
  declare const OnyxUtils: {
246
272
  METHOD: {
247
273
  readonly SET: "set";
@@ -298,6 +324,11 @@ declare const OnyxUtils: {
298
324
  getEvictionBlocklist: typeof getEvictionBlocklist;
299
325
  getSkippableCollectionMemberIDs: typeof getSkippableCollectionMemberIDs;
300
326
  setSkippableCollectionMemberIDs: typeof setSkippableCollectionMemberIDs;
327
+ storeKeyBySubscriptions: typeof storeKeyBySubscriptions;
328
+ deleteKeyBySubscriptions: typeof deleteKeyBySubscriptions;
329
+ addKeyToRecentlyAccessedIfNeeded: typeof addKeyToRecentlyAccessedIfNeeded;
330
+ reduceCollectionWithSelector: typeof reduceCollectionWithSelector;
331
+ updateSnapshots: typeof updateSnapshots;
301
332
  };
302
333
  export type { OnyxMethod };
303
334
  export default OnyxUtils;
package/dist/OnyxUtils.js CHANGED
@@ -30,6 +30,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
30
30
  /* eslint-disable no-continue */
31
31
  const fast_equals_1 = require("fast-equals");
32
32
  const clone_1 = __importDefault(require("lodash/clone"));
33
+ const pick_1 = __importDefault(require("lodash/pick"));
33
34
  const DevTools_1 = __importDefault(require("./DevTools"));
34
35
  const Logger = __importStar(require("./Logger"));
35
36
  const OnyxCache_1 = __importStar(require("./OnyxCache"));
@@ -526,7 +527,7 @@ function getCachedCollection(collectionKey, collectionMemberKeys) {
526
527
  /**
527
528
  * When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
528
529
  */
529
- function keysChanged(collectionKey, partialCollection, partialPreviousCollection, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
530
+ function keysChanged(collectionKey, partialCollection, partialPreviousCollection, notifyConnectSubscribers = true, notifyWithOnyxSubscribers = true) {
530
531
  // We prepare the "cached collection" which is the entire collection + the new partial data that
531
532
  // was merged in via mergeCollection().
532
533
  const cachedCollection = getCachedCollection(collectionKey);
@@ -555,7 +556,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
555
556
  const isSubscribedToCollectionMemberKey = isCollectionMemberKey(collectionKey, subscriber.key, collectionKeyLength);
556
557
  // Regular Onyx.connect() subscriber found.
557
558
  if (typeof subscriber.callback === 'function') {
558
- if (!notifyRegularSubscibers) {
559
+ if (!notifyConnectSubscribers) {
559
560
  continue;
560
561
  }
561
562
  // If they are subscribed to the collection key and using waitForCollectionCallback then we'll
@@ -591,7 +592,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
591
592
  }
592
593
  // React component subscriber found.
593
594
  if (utils_1.default.hasWithOnyxInstance(subscriber)) {
594
- if (!notifyWithOnyxSubscibers) {
595
+ if (!notifyWithOnyxSubscribers) {
595
596
  continue;
596
597
  }
597
598
  // We are subscribed to a collection key so we must update the data in state with the new
@@ -1171,6 +1172,51 @@ function unsubscribeFromKey(subscriptionID) {
1171
1172
  deleteKeyBySubscriptions(lastSubscriptionID);
1172
1173
  delete callbackToStateMapping[subscriptionID];
1173
1174
  }
1175
+ function updateSnapshots(data, mergeFn) {
1176
+ const snapshotCollectionKey = OnyxUtils.getSnapshotKey();
1177
+ if (!snapshotCollectionKey)
1178
+ return [];
1179
+ const promises = [];
1180
+ const snapshotCollection = OnyxUtils.getCachedCollection(snapshotCollectionKey);
1181
+ const snapshotCollectionKeyLength = snapshotCollectionKey.length;
1182
+ Object.entries(snapshotCollection).forEach(([snapshotEntryKey, snapshotEntryValue]) => {
1183
+ // Snapshots may not be present in cache. We don't know how to update them so we skip.
1184
+ if (!snapshotEntryValue) {
1185
+ return;
1186
+ }
1187
+ let updatedData = {};
1188
+ data.forEach(({ key, value }) => {
1189
+ // snapshots are normal keys so we want to skip update if they are written to Onyx
1190
+ if (OnyxUtils.isCollectionMemberKey(snapshotCollectionKey, key, snapshotCollectionKeyLength)) {
1191
+ return;
1192
+ }
1193
+ if (typeof snapshotEntryValue !== 'object' || !('data' in snapshotEntryValue)) {
1194
+ return;
1195
+ }
1196
+ const snapshotData = snapshotEntryValue.data;
1197
+ if (!snapshotData || !snapshotData[key]) {
1198
+ return;
1199
+ }
1200
+ if (Array.isArray(value) || Array.isArray(snapshotData[key])) {
1201
+ updatedData[key] = value || [];
1202
+ return;
1203
+ }
1204
+ if (value === null) {
1205
+ updatedData[key] = value;
1206
+ return;
1207
+ }
1208
+ const oldValue = updatedData[key] || {};
1209
+ const newValue = (0, pick_1.default)(value, Object.keys(snapshotData[key]));
1210
+ updatedData = Object.assign(Object.assign({}, updatedData), { [key]: Object.assign(oldValue, newValue) });
1211
+ });
1212
+ // Skip the update if there's no data to be merged
1213
+ if (utils_1.default.isEmptyObject(updatedData)) {
1214
+ return;
1215
+ }
1216
+ promises.push(() => mergeFn(snapshotEntryKey, { data: updatedData }));
1217
+ });
1218
+ return promises;
1219
+ }
1174
1220
  const OnyxUtils = {
1175
1221
  METHOD,
1176
1222
  getMergeQueue,
@@ -1220,6 +1266,11 @@ const OnyxUtils = {
1220
1266
  getEvictionBlocklist,
1221
1267
  getSkippableCollectionMemberIDs,
1222
1268
  setSkippableCollectionMemberIDs,
1269
+ storeKeyBySubscriptions,
1270
+ deleteKeyBySubscriptions,
1271
+ addKeyToRecentlyAccessedIfNeeded,
1272
+ reduceCollectionWithSelector,
1273
+ updateSnapshots,
1223
1274
  };
1224
1275
  GlobalSettings.addGlobalSettingsChangeListener(({ enablePerformanceMetrics }) => {
1225
1276
  if (!enablePerformanceMetrics) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.100",
3
+ "version": "2.0.101",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",
@@ -31,12 +31,14 @@
31
31
  "lint": "eslint .",
32
32
  "typecheck": "tsc --noEmit",
33
33
  "test": "jest",
34
+ "perf-test": "npx reassure",
34
35
  "build": "tsc -p tsconfig.build.json",
35
36
  "build:watch": "nodemon --watch lib --ext js,json,ts,tsx --exec \"npm run build && npm pack\"",
36
37
  "prebuild:docs": "npm run build",
37
38
  "build:docs": "ts-node buildDocs.ts",
38
39
  "lint-tests": "eslint tests/**",
39
- "prettier": "prettier --write ."
40
+ "prettier": "prettier --write .",
41
+ "gh-actions-build": "./.github/scripts/buildActions.sh"
40
42
  },
41
43
  "dependencies": {
42
44
  "ascii-table": "0.0.9",
@@ -66,6 +68,8 @@
66
68
  "@types/underscore": "^1.11.15",
67
69
  "@typescript-eslint/eslint-plugin": "^6.19.0",
68
70
  "@typescript-eslint/parser": "^6.19.0",
71
+ "@vercel/ncc": "0.38.1",
72
+ "date-fns": "^4.1.0",
69
73
  "eslint": "^8.56.0",
70
74
  "eslint-config-expensify": "^2.0.44",
71
75
  "eslint-config-prettier": "^8.8.0",
@@ -87,7 +91,7 @@
87
91
  "react-native-performance": "^2.0.0",
88
92
  "react-native-quick-sqlite": "^8.0.6",
89
93
  "react-test-renderer": "18.1.0",
90
- "reassure": "^0.11.0",
94
+ "reassure": "1.4.0",
91
95
  "ts-node": "^10.9.2",
92
96
  "type-fest": "^3.12.0",
93
97
  "typescript": "^5.4.5"