react-native-onyx 2.0.64 → 2.0.65

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.
@@ -1,6 +1,7 @@
1
1
  import type { ValueOf } from 'type-fest';
2
2
  import type Onyx from './Onyx';
3
- import type { CollectionKey, CollectionKeyBase, DeepRecord, KeyValueMapping, Mapping, OnyxCollection, OnyxEntry, OnyxInput, OnyxKey, OnyxMergeCollectionInput, OnyxValue, WithOnyxConnectOptions } from './types';
3
+ import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, Mapping, OnyxCollection, OnyxEntry, OnyxInput, OnyxKey, OnyxMergeCollectionInput, OnyxValue } from './types';
4
+ import type { DeferredTask } from './createDeferredTask';
4
5
  declare const METHOD: {
5
6
  readonly SET: "set";
6
7
  readonly MERGE: "merge";
@@ -18,14 +19,18 @@ declare function getMergeQueue(): Record<OnyxKey, Array<OnyxValue<OnyxKey>>>;
18
19
  * Getter - returns the merge queue promise.
19
20
  */
20
21
  declare function getMergeQueuePromise(): Record<OnyxKey, Promise<void>>;
21
- /**
22
- * Getter - returns the callback to state mapping.
23
- */
24
- declare function getCallbackToStateMapping(): Record<string, Mapping<OnyxKey>>;
25
22
  /**
26
23
  * Getter - returns the default key states.
27
24
  */
28
25
  declare function getDefaultKeyStates(): Record<OnyxKey, OnyxValue<OnyxKey>>;
26
+ /**
27
+ * Getter - returns the deffered init task.
28
+ */
29
+ declare function getDeferredInitTask(): DeferredTask;
30
+ /**
31
+ * Getter - returns the eviction block list.
32
+ */
33
+ declare function getEvictionBlocklist(): Record<OnyxKey, string[] | undefined>;
29
34
  /**
30
35
  * Sets the initial values for the Onyx store
31
36
  *
@@ -55,19 +60,6 @@ declare function batchUpdates(updates: () => void): Promise<void>;
55
60
  /** Get some data from the store */
56
61
  declare function get<TKey extends OnyxKey, TValue extends OnyxValue<TKey>>(key: TKey): Promise<TValue>;
57
62
  declare function multiGet<TKey extends OnyxKey>(keys: CollectionKeyBase[]): Promise<Map<OnyxKey, OnyxValue<TKey>>>;
58
- /**
59
- * Stores a connection ID associated with a given key.
60
- *
61
- * @param connectionID - a connection ID of the subscriber
62
- * @param key - a key that the subscriber is connected to
63
- */
64
- declare function storeKeyByConnections(key: OnyxKey, connectionID: number): void;
65
- /**
66
- * Deletes a connection ID associated with its corresponding key.
67
- *
68
- * @param {number} connectionID - The connection ID to be deleted.
69
- */
70
- declare function deleteKeyByConnections(connectionID: number): void;
71
63
  /** Returns current key names stored in persisted storage */
72
64
  declare function getAllKeys(): Promise<Set<OnyxKey>>;
73
65
  /**
@@ -109,7 +101,7 @@ declare function getCollectionKey(key: OnyxKey): string;
109
101
  * Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
110
102
  * If the requested key is a collection, it will return an object with all the collection members.
111
103
  */
112
- declare function tryGetCachedValue<TKey extends OnyxKey>(key: TKey, mapping?: Partial<WithOnyxConnectOptions<TKey>>): OnyxValue<OnyxKey>;
104
+ declare function tryGetCachedValue<TKey extends OnyxKey>(key: TKey, mapping?: Partial<Mapping<TKey>>): OnyxValue<OnyxKey>;
113
105
  /**
114
106
  * Remove a key from the recently accessed key list.
115
107
  */
@@ -120,13 +112,6 @@ declare function removeLastAccessedKey(key: OnyxKey): void;
120
112
  * recently accessed key at the tail.
121
113
  */
122
114
  declare function addLastAccessedKey(key: OnyxKey): void;
123
- /**
124
- * Removes a key previously added to this list
125
- * which will enable it to be deleted again.
126
- */
127
- declare function removeFromEvictionBlockList(key: OnyxKey, connectionID: number): void;
128
- /** Keys added to this list can never be deleted. */
129
- declare function addToEvictionBlockList(key: OnyxKey, connectionID: number): void;
130
115
  /**
131
116
  * Take all the keys that are safe to evict and add them to
132
117
  * the recently accessed list when initializing the app. This
@@ -152,11 +137,6 @@ declare function keyChanged<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TK
152
137
  * - triggers the callback function
153
138
  */
154
139
  declare function sendDataToConnection<TKey extends OnyxKey>(mapping: Mapping<TKey>, value: OnyxValue<TKey> | null, matchedKey: TKey | undefined, isBatched: boolean): void;
155
- /**
156
- * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
157
- * run out of storage the least recently accessed key can be removed.
158
- */
159
- declare function addKeyToRecentlyAccessedIfNeeded<TKey extends OnyxKey>(mapping: Mapping<TKey>): void;
160
140
  /**
161
141
  * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
162
142
  */
@@ -228,6 +208,19 @@ declare function isValidNonEmptyCollectionForMerge<TKey extends CollectionKeyBas
228
208
  * Verify if all the collection keys belong to the same parent
229
209
  */
230
210
  declare function doAllCollectionItemsBelongToSameParent<TKey extends CollectionKeyBase>(collectionKey: TKey, collectionKeys: string[]): boolean;
211
+ /**
212
+ * Subscribes to an Onyx key and listens to its changes.
213
+ *
214
+ * @param connectOptions The options object that will define the behavior of the connection.
215
+ * @returns The subscription ID to use when calling `OnyxUtils.unsubscribeFromKey()`.
216
+ */
217
+ declare function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOptions<TKey>): number;
218
+ /**
219
+ * Disconnects and removes the listener from the Onyx key.
220
+ *
221
+ * @param subscriptionID Subscription ID returned by calling `OnyxUtils.subscribeToKey()`.
222
+ */
223
+ declare function unsubscribeFromKey(subscriptionID: number): void;
231
224
  declare const OnyxUtils: {
232
225
  METHOD: {
233
226
  readonly SET: "set";
@@ -238,8 +231,8 @@ declare const OnyxUtils: {
238
231
  };
239
232
  getMergeQueue: typeof getMergeQueue;
240
233
  getMergeQueuePromise: typeof getMergeQueuePromise;
241
- getCallbackToStateMapping: typeof getCallbackToStateMapping;
242
234
  getDefaultKeyStates: typeof getDefaultKeyStates;
235
+ getDeferredInitTask: typeof getDeferredInitTask;
243
236
  initStoreValues: typeof initStoreValues;
244
237
  sendActionToDevTools: typeof sendActionToDevTools;
245
238
  maybeFlushBatchUpdates: typeof maybeFlushBatchUpdates;
@@ -255,14 +248,11 @@ declare const OnyxUtils: {
255
248
  tryGetCachedValue: typeof tryGetCachedValue;
256
249
  removeLastAccessedKey: typeof removeLastAccessedKey;
257
250
  addLastAccessedKey: typeof addLastAccessedKey;
258
- removeFromEvictionBlockList: typeof removeFromEvictionBlockList;
259
- addToEvictionBlockList: typeof addToEvictionBlockList;
260
251
  addAllSafeEvictionKeysToRecentlyAccessedList: typeof addAllSafeEvictionKeysToRecentlyAccessedList;
261
252
  getCachedCollection: typeof getCachedCollection;
262
253
  keysChanged: typeof keysChanged;
263
254
  keyChanged: typeof keyChanged;
264
255
  sendDataToConnection: typeof sendDataToConnection;
265
- addKeyToRecentlyAccessedIfNeeded: typeof addKeyToRecentlyAccessedIfNeeded;
266
256
  getCollectionKey: typeof getCollectionKey;
267
257
  getCollectionDataAndSendAsObject: typeof getCollectionDataAndSendAsObject;
268
258
  scheduleSubscriberUpdate: typeof scheduleSubscriberUpdate;
@@ -276,11 +266,12 @@ declare const OnyxUtils: {
276
266
  prepareKeyValuePairsForStorage: typeof prepareKeyValuePairsForStorage;
277
267
  applyMerge: typeof applyMerge;
278
268
  initializeWithDefaultKeyStates: typeof initializeWithDefaultKeyStates;
279
- storeKeyByConnections: typeof storeKeyByConnections;
280
- deleteKeyByConnections: typeof deleteKeyByConnections;
281
269
  getSnapshotKey: typeof getSnapshotKey;
282
270
  multiGet: typeof multiGet;
283
271
  isValidNonEmptyCollectionForMerge: typeof isValidNonEmptyCollectionForMerge;
284
272
  doAllCollectionItemsBelongToSameParent: typeof doAllCollectionItemsBelongToSameParent;
273
+ subscribeToKey: typeof subscribeToKey;
274
+ unsubscribeFromKey: typeof unsubscribeFromKey;
275
+ getEvictionBlocklist: typeof getEvictionBlocklist;
285
276
  };
286
277
  export default OnyxUtils;
package/dist/OnyxUtils.js CHANGED
@@ -38,6 +38,7 @@ const Str = __importStar(require("./Str"));
38
38
  const batch_1 = __importDefault(require("./batch"));
39
39
  const storage_1 = __importDefault(require("./storage"));
40
40
  const utils_1 = __importDefault(require("./utils"));
41
+ const createDeferredTask_1 = __importDefault(require("./createDeferredTask"));
41
42
  // Method constants
42
43
  const METHOD = {
43
44
  SET: 'set',
@@ -53,14 +54,14 @@ const mergeQueuePromise = {};
53
54
  const callbackToStateMapping = {};
54
55
  // Keeps a copy of the values of the onyx collection keys as a map for faster lookups
55
56
  let onyxCollectionKeySet = new Set();
56
- // Holds a mapping of the connected key to the connectionID for faster lookups
57
- const onyxKeyToConnectionIDs = new Map();
57
+ // Holds a mapping of the connected key to the subscriptionID for faster lookups
58
+ const onyxKeyToSubscriptionIDs = new Map();
58
59
  // Holds a list of keys that have been directly subscribed to or recently modified from least to most recent
59
60
  let recentlyAccessedKeys = [];
60
61
  // Holds a list of keys that are safe to remove when we reach max storage. If a key does not match with
61
62
  // whatever appears in this list it will NEVER be a candidate for eviction.
62
63
  let evictionAllowList = [];
63
- // Holds a map of keys and connectionID arrays whose keys will never be automatically evicted as
64
+ // Holds a map of keys and connection arrays whose keys will never be automatically evicted as
64
65
  // long as we have at least one subscriber that returns false for the canEvict property.
65
66
  const evictionBlocklist = {};
66
67
  // Optional user-provided key value states set when Onyx initializes or clears
@@ -70,6 +71,10 @@ let batchUpdatesQueue = [];
70
71
  // Used for comparison with a new update to avoid invoking the Onyx.connect callback with the same data.
71
72
  const lastConnectionCallbackData = new Map();
72
73
  let snapshotKey = null;
74
+ // Keeps track of the last subscriptionID that was used so we can keep incrementing it
75
+ let lastSubscriptionID = 0;
76
+ // Connections can be made before `Onyx.init`. They would wait for this task before resolving
77
+ const deferredInitTask = (0, createDeferredTask_1.default)();
73
78
  function getSnapshotKey() {
74
79
  return snapshotKey;
75
80
  }
@@ -85,18 +90,24 @@ function getMergeQueue() {
85
90
  function getMergeQueuePromise() {
86
91
  return mergeQueuePromise;
87
92
  }
88
- /**
89
- * Getter - returns the callback to state mapping.
90
- */
91
- function getCallbackToStateMapping() {
92
- return callbackToStateMapping;
93
- }
94
93
  /**
95
94
  * Getter - returns the default key states.
96
95
  */
97
96
  function getDefaultKeyStates() {
98
97
  return defaultKeyStates;
99
98
  }
99
+ /**
100
+ * Getter - returns the deffered init task.
101
+ */
102
+ function getDeferredInitTask() {
103
+ return deferredInitTask;
104
+ }
105
+ /**
106
+ * Getter - returns the eviction block list.
107
+ */
108
+ function getEvictionBlocklist() {
109
+ return evictionBlocklist;
110
+ }
100
111
  /**
101
112
  * Sets the initial values for the Onyx store
102
113
  *
@@ -258,29 +269,29 @@ function multiGet(keys) {
258
269
  }));
259
270
  }
260
271
  /**
261
- * Stores a connection ID associated with a given key.
272
+ * Stores a subscription ID associated with a given key.
262
273
  *
263
- * @param connectionID - a connection ID of the subscriber
264
- * @param key - a key that the subscriber is connected to
274
+ * @param subscriptionID - A subscription ID of the subscriber.
275
+ * @param key - A key that the subscriber is subscribed to.
265
276
  */
266
- function storeKeyByConnections(key, connectionID) {
267
- if (!onyxKeyToConnectionIDs.has(key)) {
268
- onyxKeyToConnectionIDs.set(key, []);
277
+ function storeKeyBySubscriptions(key, subscriptionID) {
278
+ if (!onyxKeyToSubscriptionIDs.has(key)) {
279
+ onyxKeyToSubscriptionIDs.set(key, []);
269
280
  }
270
- onyxKeyToConnectionIDs.get(key).push(connectionID);
281
+ onyxKeyToSubscriptionIDs.get(key).push(subscriptionID);
271
282
  }
272
283
  /**
273
- * Deletes a connection ID associated with its corresponding key.
284
+ * Deletes a subscription ID associated with its corresponding key.
274
285
  *
275
- * @param {number} connectionID - The connection ID to be deleted.
286
+ * @param subscriptionID - The subscription ID to be deleted.
276
287
  */
277
- function deleteKeyByConnections(connectionID) {
278
- const subscriber = callbackToStateMapping[connectionID];
279
- if (subscriber && onyxKeyToConnectionIDs.has(subscriber.key)) {
280
- const updatedConnectionIDs = onyxKeyToConnectionIDs.get(subscriber.key).filter((id) => id !== connectionID);
281
- onyxKeyToConnectionIDs.set(subscriber.key, updatedConnectionIDs);
288
+ function deleteKeyBySubscriptions(subscriptionID) {
289
+ const subscriber = callbackToStateMapping[subscriptionID];
290
+ if (subscriber && onyxKeyToSubscriptionIDs.has(subscriber.key)) {
291
+ const updatedSubscriptionsIDs = onyxKeyToSubscriptionIDs.get(subscriber.key).filter((id) => id !== subscriptionID);
292
+ onyxKeyToSubscriptionIDs.set(subscriber.key, updatedSubscriptionsIDs);
282
293
  }
283
- lastConnectionCallbackData.delete(connectionID);
294
+ lastConnectionCallbackData.delete(subscriptionID);
284
295
  }
285
296
  /** Returns current key names stored in persisted storage */
286
297
  function getAllKeys() {
@@ -411,26 +422,6 @@ function addLastAccessedKey(key) {
411
422
  removeLastAccessedKey(key);
412
423
  recentlyAccessedKeys.push(key);
413
424
  }
414
- /**
415
- * Removes a key previously added to this list
416
- * which will enable it to be deleted again.
417
- */
418
- function removeFromEvictionBlockList(key, connectionID) {
419
- var _a, _b, _c;
420
- evictionBlocklist[key] = (_b = (_a = evictionBlocklist[key]) === null || _a === void 0 ? void 0 : _a.filter((evictionKey) => evictionKey !== connectionID)) !== null && _b !== void 0 ? _b : [];
421
- // Remove the key if there are no more subscribers
422
- if (((_c = evictionBlocklist[key]) === null || _c === void 0 ? void 0 : _c.length) === 0) {
423
- delete evictionBlocklist[key];
424
- }
425
- }
426
- /** Keys added to this list can never be deleted. */
427
- function addToEvictionBlockList(key, connectionID) {
428
- removeFromEvictionBlockList(key, connectionID);
429
- if (!evictionBlocklist[key]) {
430
- evictionBlocklist[key] = [];
431
- }
432
- evictionBlocklist[key].push(connectionID);
433
- }
434
425
  /**
435
426
  * Take all the keys that are safe to evict and add them to
436
427
  * the recently accessed list when initializing the app. This
@@ -508,7 +499,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
508
499
  // send the whole cached collection.
509
500
  if (isSubscribedToCollectionKey) {
510
501
  if (subscriber.waitForCollectionCallback) {
511
- subscriber.callback(cachedCollection);
502
+ subscriber.callback(cachedCollection, subscriber.key);
512
503
  continue;
513
504
  }
514
505
  // If they are not using waitForCollectionCallback then we notify the subscriber with
@@ -536,7 +527,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
536
527
  continue;
537
528
  }
538
529
  // React component subscriber found.
539
- if ('withOnyxInstance' in subscriber && subscriber.withOnyxInstance) {
530
+ if (utils_1.default.hasWithOnyxInstance(subscriber)) {
540
531
  if (!notifyWithOnyxSubscibers) {
541
532
  continue;
542
533
  }
@@ -647,12 +638,12 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
647
638
  // Given the amount of times this function is called we need to make sure we are not iterating over all subscribers every time. On the other hand, we don't need to
648
639
  // do the same in keysChanged, because we only call that function when a collection key changes, and it doesn't happen that often.
649
640
  // For performance reason, we look for the given key and later if don't find it we look for the collection key, instead of checking if it is a collection key first.
650
- let stateMappingKeys = (_a = onyxKeyToConnectionIDs.get(key)) !== null && _a !== void 0 ? _a : [];
641
+ let stateMappingKeys = (_a = onyxKeyToSubscriptionIDs.get(key)) !== null && _a !== void 0 ? _a : [];
651
642
  const collectionKey = getCollectionKey(key);
652
643
  const plainCollectionKey = collectionKey.lastIndexOf('_') !== -1 ? collectionKey : undefined;
653
644
  if (plainCollectionKey) {
654
645
  // Getting the collection key from the specific key because only collection keys were stored in the mapping.
655
- stateMappingKeys = [...stateMappingKeys, ...((_b = onyxKeyToConnectionIDs.get(plainCollectionKey)) !== null && _b !== void 0 ? _b : [])];
646
+ stateMappingKeys = [...stateMappingKeys, ...((_b = onyxKeyToSubscriptionIDs.get(plainCollectionKey)) !== null && _b !== void 0 ? _b : [])];
656
647
  if (stateMappingKeys.length === 0) {
657
648
  return;
658
649
  }
@@ -668,7 +659,7 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
668
659
  if (!notifyConnectSubscribers) {
669
660
  continue;
670
661
  }
671
- if (lastConnectionCallbackData.has(subscriber.connectionID) && lastConnectionCallbackData.get(subscriber.connectionID) === value) {
662
+ if (lastConnectionCallbackData.has(subscriber.subscriptionID) && lastConnectionCallbackData.get(subscriber.subscriptionID) === value) {
672
663
  continue;
673
664
  }
674
665
  if (isCollectionKey(subscriber.key) && subscriber.waitForCollectionCallback) {
@@ -678,16 +669,16 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
678
669
  cachedCollections[subscriber.key] = cachedCollection;
679
670
  }
680
671
  cachedCollection[key] = value;
681
- subscriber.callback(cachedCollection);
672
+ subscriber.callback(cachedCollection, subscriber.key);
682
673
  continue;
683
674
  }
684
675
  const subscriberCallback = subscriber.callback;
685
676
  subscriberCallback(value, key);
686
- lastConnectionCallbackData.set(subscriber.connectionID, value);
677
+ lastConnectionCallbackData.set(subscriber.subscriptionID, value);
687
678
  continue;
688
679
  }
689
680
  // Subscriber connected via withOnyx() HOC
690
- if ('withOnyxInstance' in subscriber && subscriber.withOnyxInstance) {
681
+ if (utils_1.default.hasWithOnyxInstance(subscriber)) {
691
682
  if (!notifyWithOnyxSubscribers) {
692
683
  continue;
693
684
  }
@@ -770,10 +761,10 @@ function sendDataToConnection(mapping, value, matchedKey, isBatched) {
770
761
  var _a, _b;
771
762
  // If the mapping no longer exists then we should not send any data.
772
763
  // This means our subscriber disconnected or withOnyx wrapped component unmounted.
773
- if (!callbackToStateMapping[mapping.connectionID]) {
764
+ if (!callbackToStateMapping[mapping.subscriptionID]) {
774
765
  return;
775
766
  }
776
- if ('withOnyxInstance' in mapping && mapping.withOnyxInstance) {
767
+ if (utils_1.default.hasWithOnyxInstance(mapping)) {
777
768
  let newData = value;
778
769
  // If the mapping has a selector, then the component's state must only be updated with the data
779
770
  // returned by the selector.
@@ -801,10 +792,10 @@ function sendDataToConnection(mapping, value, matchedKey, isBatched) {
801
792
  // withOnyx will internally replace null values with undefined and never pass null values to wrapped components.
802
793
  // For regular callbacks, we never want to pass null values, but always just undefined if a value is not set in cache or storage.
803
794
  const valueToPass = value === null ? undefined : value;
804
- const lastValue = lastConnectionCallbackData.get(mapping.connectionID);
805
- lastConnectionCallbackData.get(mapping.connectionID);
795
+ const lastValue = lastConnectionCallbackData.get(mapping.subscriptionID);
796
+ lastConnectionCallbackData.get(mapping.subscriptionID);
806
797
  // If the value has not changed we do not need to trigger the callback
807
- if (lastConnectionCallbackData.has(mapping.connectionID) && valueToPass === lastValue) {
798
+ if (lastConnectionCallbackData.has(mapping.subscriptionID) && valueToPass === lastValue) {
808
799
  return;
809
800
  }
810
801
  (_b = (_a = mapping).callback) === null || _b === void 0 ? void 0 : _b.call(_a, valueToPass, matchedKey);
@@ -819,7 +810,7 @@ function addKeyToRecentlyAccessedIfNeeded(mapping) {
819
810
  }
820
811
  // Try to free some cache whenever we connect to a safe eviction key
821
812
  OnyxCache_1.default.removeLeastRecentlyUsedKeys();
822
- if ('withOnyxInstance' in mapping && mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
813
+ if (utils_1.default.hasWithOnyxInstance(mapping) && !isCollectionKey(mapping.key)) {
823
814
  // All React components subscribing to a key flagged as a safe eviction key must implement the canEvict property.
824
815
  if (mapping.canEvict === undefined) {
825
816
  throw new Error(`Cannot subscribe to safe eviction key '${mapping.key}' without providing a canEvict value.`);
@@ -996,7 +987,7 @@ function isValidNonEmptyCollectionForMerge(collection) {
996
987
  function doAllCollectionItemsBelongToSameParent(collectionKey, collectionKeys) {
997
988
  let hasCollectionKeyCheckFailed = false;
998
989
  collectionKeys.forEach((dataKey) => {
999
- if (OnyxUtils.isKeyMatch(collectionKey, dataKey)) {
990
+ if (isKeyMatch(collectionKey, dataKey)) {
1000
991
  return;
1001
992
  }
1002
993
  if (process.env.NODE_ENV === 'development') {
@@ -1007,12 +998,116 @@ function doAllCollectionItemsBelongToSameParent(collectionKey, collectionKeys) {
1007
998
  });
1008
999
  return !hasCollectionKeyCheckFailed;
1009
1000
  }
1001
+ /**
1002
+ * Subscribes to an Onyx key and listens to its changes.
1003
+ *
1004
+ * @param connectOptions The options object that will define the behavior of the connection.
1005
+ * @returns The subscription ID to use when calling `OnyxUtils.unsubscribeFromKey()`.
1006
+ */
1007
+ function subscribeToKey(connectOptions) {
1008
+ const mapping = connectOptions;
1009
+ const subscriptionID = lastSubscriptionID++;
1010
+ callbackToStateMapping[subscriptionID] = mapping;
1011
+ callbackToStateMapping[subscriptionID].subscriptionID = subscriptionID;
1012
+ // When keyChanged is called, a key is passed and the method looks through all the Subscribers in callbackToStateMapping for the matching key to get the subscriptionID
1013
+ // to avoid having to loop through all the Subscribers all the time (even when just one connection belongs to one key),
1014
+ // We create a mapping from key to lists of subscriptionIDs to access the specific list of subscriptionIDs.
1015
+ storeKeyBySubscriptions(mapping.key, callbackToStateMapping[subscriptionID].subscriptionID);
1016
+ if (mapping.initWithStoredValues === false) {
1017
+ return subscriptionID;
1018
+ }
1019
+ // Commit connection only after init passes
1020
+ deferredInitTask.promise
1021
+ .then(() => addKeyToRecentlyAccessedIfNeeded(mapping))
1022
+ .then(() => {
1023
+ // Performance improvement
1024
+ // If the mapping is connected to an onyx key that is not a collection
1025
+ // we can skip the call to getAllKeys() and return an array with a single item
1026
+ if (Boolean(mapping.key) && typeof mapping.key === 'string' && !mapping.key.endsWith('_') && OnyxCache_1.default.getAllKeys().has(mapping.key)) {
1027
+ return new Set([mapping.key]);
1028
+ }
1029
+ return getAllKeys();
1030
+ })
1031
+ .then((keys) => {
1032
+ // We search all the keys in storage to see if any are a "match" for the subscriber we are connecting so that we
1033
+ // can send data back to the subscriber. Note that multiple keys can match as a subscriber could either be
1034
+ // subscribed to a "collection key" or a single key.
1035
+ const matchingKeys = [];
1036
+ keys.forEach((key) => {
1037
+ if (!isKeyMatch(mapping.key, key)) {
1038
+ return;
1039
+ }
1040
+ matchingKeys.push(key);
1041
+ });
1042
+ // If the key being connected to does not exist we initialize the value with null. For subscribers that connected
1043
+ // directly via connect() they will simply get a null value sent to them without any information about which key matched
1044
+ // since there are none matched. In withOnyx() we wait for all connected keys to return a value before rendering the child
1045
+ // component. This null value will be filtered out so that the connected component can utilize defaultProps.
1046
+ if (matchingKeys.length === 0) {
1047
+ if (mapping.key && !isCollectionKey(mapping.key)) {
1048
+ OnyxCache_1.default.addNullishStorageKey(mapping.key);
1049
+ }
1050
+ // Here we cannot use batching because the nullish value is expected to be set immediately for default props
1051
+ // or they will be undefined.
1052
+ sendDataToConnection(mapping, null, undefined, false);
1053
+ return;
1054
+ }
1055
+ // When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
1056
+ // into an object and just make a single call. The latter behavior is enabled by providing a waitForCollectionCallback key
1057
+ // combined with a subscription to a collection key.
1058
+ if (typeof mapping.callback === 'function') {
1059
+ if (isCollectionKey(mapping.key)) {
1060
+ if (mapping.waitForCollectionCallback) {
1061
+ getCollectionDataAndSendAsObject(matchingKeys, mapping);
1062
+ return;
1063
+ }
1064
+ // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
1065
+ multiGet(matchingKeys).then((values) => {
1066
+ values.forEach((val, key) => {
1067
+ sendDataToConnection(mapping, val, key, true);
1068
+ });
1069
+ });
1070
+ return;
1071
+ }
1072
+ // If we are not subscribed to a collection key then there's only a single key to send an update for.
1073
+ get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
1074
+ return;
1075
+ }
1076
+ // If we have a withOnyxInstance that means a React component has subscribed via the withOnyx() HOC and we need to
1077
+ // group collection key member data into an object.
1078
+ if (utils_1.default.hasWithOnyxInstance(mapping)) {
1079
+ if (isCollectionKey(mapping.key)) {
1080
+ getCollectionDataAndSendAsObject(matchingKeys, mapping);
1081
+ return;
1082
+ }
1083
+ // If the subscriber is not using a collection key then we just send a single value back to the subscriber
1084
+ get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
1085
+ return;
1086
+ }
1087
+ console.error('Warning: Onyx.connect() was found without a callback or withOnyxInstance');
1088
+ });
1089
+ // The subscriptionID is returned back to the caller so that it can be used to clean up the connection when it's no longer needed
1090
+ // by calling OnyxUtils.unsubscribeFromKey(subscriptionID).
1091
+ return subscriptionID;
1092
+ }
1093
+ /**
1094
+ * Disconnects and removes the listener from the Onyx key.
1095
+ *
1096
+ * @param subscriptionID Subscription ID returned by calling `OnyxUtils.subscribeToKey()`.
1097
+ */
1098
+ function unsubscribeFromKey(subscriptionID) {
1099
+ if (!callbackToStateMapping[subscriptionID]) {
1100
+ return;
1101
+ }
1102
+ deleteKeyBySubscriptions(lastSubscriptionID);
1103
+ delete callbackToStateMapping[subscriptionID];
1104
+ }
1010
1105
  const OnyxUtils = {
1011
1106
  METHOD,
1012
1107
  getMergeQueue,
1013
1108
  getMergeQueuePromise,
1014
- getCallbackToStateMapping,
1015
1109
  getDefaultKeyStates,
1110
+ getDeferredInitTask,
1016
1111
  initStoreValues,
1017
1112
  sendActionToDevTools,
1018
1113
  maybeFlushBatchUpdates,
@@ -1028,14 +1123,11 @@ const OnyxUtils = {
1028
1123
  tryGetCachedValue,
1029
1124
  removeLastAccessedKey,
1030
1125
  addLastAccessedKey,
1031
- removeFromEvictionBlockList,
1032
- addToEvictionBlockList,
1033
1126
  addAllSafeEvictionKeysToRecentlyAccessedList,
1034
1127
  getCachedCollection,
1035
1128
  keysChanged,
1036
1129
  keyChanged,
1037
1130
  sendDataToConnection,
1038
- addKeyToRecentlyAccessedIfNeeded,
1039
1131
  getCollectionKey,
1040
1132
  getCollectionDataAndSendAsObject,
1041
1133
  scheduleSubscriberUpdate,
@@ -1049,11 +1141,12 @@ const OnyxUtils = {
1049
1141
  prepareKeyValuePairsForStorage,
1050
1142
  applyMerge,
1051
1143
  initializeWithDefaultKeyStates,
1052
- storeKeyByConnections,
1053
- deleteKeyByConnections,
1054
1144
  getSnapshotKey,
1055
1145
  multiGet,
1056
1146
  isValidNonEmptyCollectionForMerge,
1057
1147
  doAllCollectionItemsBelongToSameParent,
1148
+ subscribeToKey,
1149
+ unsubscribeFromKey,
1150
+ getEvictionBlocklist,
1058
1151
  };
1059
1152
  exports.default = OnyxUtils;
package/dist/Str.d.ts CHANGED
@@ -14,4 +14,8 @@ declare function startsWith(haystack: string, needle: string): boolean;
14
14
  */
15
15
  declare function result(parameter: string): string;
16
16
  declare function result<TFunction extends (...a: TArgs) => unknown, TArgs extends unknown[]>(parameter: TFunction, ...args: TArgs): ReturnType<TFunction>;
17
- export { startsWith, result };
17
+ /**
18
+ * A simple GUID generator taken from https://stackoverflow.com/a/32760401/9114791
19
+ */
20
+ declare function guid(): string;
21
+ export { guid, result, startsWith };
package/dist/Str.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.result = exports.startsWith = void 0;
3
+ exports.startsWith = exports.result = exports.guid = void 0;
4
4
  /**
5
5
  * Returns true if the haystack begins with the needle
6
6
  *
@@ -16,3 +16,15 @@ function result(parameter, ...args) {
16
16
  return typeof parameter === 'function' ? parameter(...args) : parameter;
17
17
  }
18
18
  exports.result = result;
19
+ /**
20
+ * A simple GUID generator taken from https://stackoverflow.com/a/32760401/9114791
21
+ */
22
+ function guid() {
23
+ function s4() {
24
+ return Math.floor((1 + Math.random()) * 0x10000)
25
+ .toString(16)
26
+ .substring(1);
27
+ }
28
+ return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
29
+ }
30
+ exports.guid = guid;
@@ -8,4 +8,4 @@ type DeferredTask = {
8
8
  * Useful when we want to wait for a tasks that is resolved from an external action
9
9
  */
10
10
  export default function createDeferredTask(): DeferredTask;
11
- export {};
11
+ export type { DeferredTask };
package/dist/index.d.ts CHANGED
@@ -2,9 +2,10 @@ import type { ConnectOptions, OnyxUpdate } from './Onyx';
2
2
  import Onyx from './Onyx';
3
3
  import type { CustomTypeOptions, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxValue, Selector, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput } from './types';
4
4
  import type { FetchStatus, ResultMetadata, UseOnyxResult } from './useOnyx';
5
+ import type { Connection } from './OnyxConnectionManager';
5
6
  import useOnyx from './useOnyx';
6
7
  import withOnyx from './withOnyx';
7
8
  import type { WithOnyxState } from './withOnyx/types';
8
9
  export default Onyx;
9
10
  export { useOnyx, withOnyx };
10
- export type { ConnectOptions, CustomTypeOptions, FetchStatus, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, ResultMetadata, Selector, UseOnyxResult, WithOnyxState, };
11
+ export type { ConnectOptions, CustomTypeOptions, FetchStatus, KeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, ResultMetadata, Selector, UseOnyxResult, WithOnyxState, Connection, };