diff --git a/API.md b/API.md
index 82b7777f..3ac3a0eb 100644
--- a/API.md
+++ b/API.md
@@ -319,6 +319,7 @@ Initialize the store with actions and listening for storage events
| [options.captureMetrics] | Boolean
| | Enables Onyx benchmarking and exposes the get/print/reset functions |
| [options.shouldSyncMultipleInstances] | Boolean
| | Auto synchronize storage events between multiple instances of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop) |
| [options.debugSetState] | Boolean
| | Enables debugging setState() calls to connected components. |
+| [options.enableDevTools] | Boolean
| | Enables debugging using Redux DevTools extension. |
**Example**
```js
diff --git a/README.md b/README.md
index f1f34d1c..4ce8a2c2 100644
--- a/README.md
+++ b/README.md
@@ -321,8 +321,18 @@ Sample output of `Onyx.printMetrics()`
# Debug mode
+## Using debugSetState
+
It can be useful to log why Onyx is calling `setState()` on a particular React component so that we can understand which key changed, what changed about the value, and the connected component that ultimately rendered as a result. When used correctly this can help isolate problem areas and unnecessary renders in the code. To enable this feature, pass `debugSetState: true` to the config and grep JS console logs for `[Onyx-Debug]`.
+## Using Redux DevTools extension
+
+It can be useful to check the order of writes to the storage and it's state at a specific point in time.
+
+First, install Redux DevTools through your favorite browser ([Edge](https://microsoftedge.microsoft.com/addons/detail/redux-devtools/nnkgneoiohoecpdiaponcejilbhhikei), [Chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd), [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/))
+
+Then, you can enable this type of debugging by passing `enableDevTools: true` to `Onyx.init({...})`.
+
# Development
`react-native` bundles source using the `metro` bundler. `metro` does not follow symlinks, so we can't use `npm link` to
diff --git a/lib/DevTools.js b/lib/DevTools.js
new file mode 100644
index 00000000..2282c4aa
--- /dev/null
+++ b/lib/DevTools.js
@@ -0,0 +1,49 @@
+import {connectViaExtension} from 'remotedev';
+import _ from 'underscore';
+
+class DevTools {
+ /**
+ * @callback onStateChange
+ * @param {object} state
+ */
+ /**
+ * Creates an instance of DevTools, with an internal state that mirrors the storage.
+ *
+ * @param {object} initialState - initial state of the storage
+ * @param {onStateChange} onStateChange - callback which is triggered when we timetravel to a different registered action
+ */
+ constructor(initialState = {}) {
+ this.state = initialState;
+ this.remotedev = connectViaExtension();
+ this.remotedev.init(this.state);
+ }
+
+ /**
+ * Registers an action that updated the current state of the storage
+ *
+ * @param {string} type - name of the action
+ * @param {any} payload - data written to the storage
+ * @param {object} stateChanges - partial state that got updated after the changes
+ */
+ registerAction(type, payload = undefined, stateChanges = {}) {
+ const newState = {
+ ...this.state,
+ ...stateChanges,
+ };
+
+ this.remotedev.send({type, payload}, newState);
+ this.state = newState;
+ }
+
+ /**
+ * This clears the internal state of the DevTools, preserving the keys not included in `keyToBeRemoved`
+ *
+ * @param {string[]} keysToBeRemoved
+ */
+ clearState(keysToBeRemoved = []) {
+ const pairs = _.map(keysToBeRemoved, key => [key, undefined]);
+ this.registerAction('CLEAR', undefined, _.object((pairs)));
+ }
+}
+
+export default DevTools;
diff --git a/lib/Onyx.js b/lib/Onyx.js
index 27f49aec..600ea1e7 100644
--- a/lib/Onyx.js
+++ b/lib/Onyx.js
@@ -8,6 +8,7 @@ import createDeferredTask from './createDeferredTask';
import fastMerge from './fastMerge';
import * as PerformanceUtils from './metrics/PerformanceUtils';
import Storage from './storage';
+import DevTools from './DevTools';
// Method constants
const METHOD = {
@@ -37,6 +38,11 @@ let recentlyAccessedKeys = [];
// whatever appears in this list it will NEVER be a candidate for eviction.
let evictionAllowList = [];
+let devTools = {
+ registerAction: () => {},
+ clearState: () => {},
+};
+
// Holds a map of keys and connectionID arrays whose keys will never be automatically evicted as
// long as we have at least one subscriber that returns false for the canEvict property.
const evictionBlocklist = {};
@@ -842,7 +848,10 @@ function notifyCollectionSubscribersOnNextTick(key, value) {
function remove(key) {
cache.drop(key);
notifySubscribersOnNextTick(key, null);
- return Storage.removeItem(key);
+ return Storage.removeItem(key).then((result) => {
+ devTools.registerAction(`REMOVE/${key.toUpperCase()}`, undefined, {[key]: undefined});
+ return result;
+ });
}
/**
@@ -966,6 +975,7 @@ function set(key, value) {
const hasChanged = cache.hasValueChanged(key, valueWithNullRemoved);
// This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
+ broadcastUpdate(key, value, 'set');
broadcastUpdate(key, valueWithNullRemoved, hasChanged, 'set');
// If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
@@ -974,6 +984,10 @@ function set(key, value) {
}
return Storage.setItem(key, valueWithNullRemoved)
+ .then((result) => {
+ devTools.registerAction(`SET/${key.toUpperCase()}`, valueWithNullRemoved, {[key]: valueWithNullRemoved});
+ return result;
+ })
.catch(error => evictStorageAndRetry(error, set, key, valueWithNullRemoved));
}
@@ -1004,6 +1018,11 @@ function multiSet(data) {
// Update cache and optimistically inform subscribers on the next tick
cache.set(key, val);
notifySubscribersOnNextTick(key, val);
+ if (_.isNull(val)) {
+ devTools.registerAction(`REMOVE/${key.toUpperCase()}`, val, {[key]: undefined});
+ } else {
+ devTools.registerAction(`SET/${key.toUpperCase()}`, val, {[key]: val});
+ }
});
return Storage.multiSet(keyValuePairs)
@@ -1098,7 +1117,10 @@ function merge(key, changes) {
return Promise.resolve();
}
- return Storage.mergeItem(key, batchedChanges, modifiedData);
+ return Storage.mergeItem(key, batchedChanges, modifiedData).then((results) => {
+ devTools.registerAction(`MERGE/${key.toUpperCase()}`, modifiedData, {[key]: modifiedData});
+ return results;
+ });
} catch (error) {
Logger.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`);
}
@@ -1110,9 +1132,10 @@ function merge(key, changes) {
/**
* Merge user provided default key value pairs.
* @private
+ * @param {boolean} enableDevTools
* @returns {Promise}
*/
-function initializeWithDefaultKeyStates() {
+function initializeWithDefaultKeyStates(enableDevTools = false) {
return Storage.multiGet(_.keys(defaultKeyStates))
.then((pairs) => {
const asObject = _.object(pairs);
@@ -1120,6 +1143,9 @@ function initializeWithDefaultKeyStates() {
const merged = fastMerge(asObject, defaultKeyStates);
cache.merge(merged);
_.each(merged, (val, key) => keyChanged(key, val));
+ if (enableDevTools) {
+ devTools = new DevTools(merged);
+ }
});
}
@@ -1167,7 +1193,7 @@ function clear(keysToPreserve = []) {
// since collection key subscribers need to be updated differently
if (!isKeyToPreserve) {
const oldValue = cache.getValue(key);
- const newValue = _.get(defaultKeyStates, key, null);
+ const newValue = _.get(defaultKeyStates, key, undefined);
if (newValue !== oldValue) {
cache.set(key, newValue);
const collectionKey = key.substring(0, key.indexOf('_') + 1);
@@ -1198,11 +1224,15 @@ function clear(keysToPreserve = []) {
notifyCollectionSubscribersOnNextTick(key, value);
});
- const defaultKeyValuePairs = _.pairs(_.omit(defaultKeyStates, keysToPreserve));
+ const defaultKeyValueState = _.omit(defaultKeyStates, keysToPreserve);
+ const defaultKeyValuePairs = _.pairs(defaultKeyValueState);
// Remove only the items that we want cleared from storage, and reset others to default
_.each(keysToBeClearedFromStorage, key => cache.drop(key));
- return Storage.removeItems(keysToBeClearedFromStorage).then(() => Storage.multiSet(defaultKeyValuePairs));
+ return Storage.removeItems(keysToBeClearedFromStorage).then(() => {
+ devTools.clearState(keysToBeClearedFromStorage);
+ return Storage.multiSet(defaultKeyValuePairs);
+ });
});
}
@@ -1276,6 +1306,11 @@ function mergeCollection(collectionKey, collection) {
Promise.all(_.map(existingKeys, get)).then(() => {
cache.merge(collection);
keysChanged(collectionKey, collection);
+ if (_.isNull(collection)) {
+ devTools.registerAction(`REMOVE/${collectionKey.toUpperCase()}`, collection, {[collectionKey]: undefined});
+ } else {
+ devTools.registerAction(`SET/${collectionKey.toUpperCase()}`, collection, {[collectionKey]: collection});
+ }
});
return Promise.all(promises)
@@ -1377,6 +1412,7 @@ function init({
captureMetrics = false,
shouldSyncMultipleInstances = Boolean(global.localStorage),
debugSetState = false,
+ enableDevTools = false,
} = {}) {
if (captureMetrics) {
// The code here is only bundled and applied when the captureMetrics is set
@@ -1404,7 +1440,7 @@ function init({
// Initialize all of our keys with data provided then give green light to any pending connections
Promise.all([
addAllSafeEvictionKeysToRecentlyAccessedList(),
- initializeWithDefaultKeyStates(),
+ initializeWithDefaultKeyStates(enableDevTools),
])
.then(deferredInitTask.resolve);
@@ -1412,6 +1448,11 @@ function init({
Storage.keepInstancesSync((key, value) => {
cache.set(key, value);
keyChanged(key, value);
+ if (_.isNull(value)) {
+ devTools.registerAction(`REMOVE/${key.toUpperCase()}`, value, {[key]: undefined});
+ } else {
+ devTools.registerAction(`SET/${key.toUpperCase()}`, value, {[key]: value});
+ }
});
}
}
diff --git a/package-lock.json b/package-lock.json
index ae5101be..adb134b6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,6 +41,7 @@
"react-native-quick-sqlite": "^8.0.0-beta.2",
"react-test-renderer": "18.1.0",
"type-fest": "^3.12.0",
+ "remotedev": "^0.2.9",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-merge": "^5.8.0"
@@ -4676,6 +4677,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/base-64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
+ "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==",
+ "dev": true
+ },
"node_modules/base/node_modules/define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
@@ -10564,6 +10571,12 @@
"xmlcreate": "^2.0.3"
}
},
+ "node_modules/jsan": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/jsan/-/jsan-3.1.14.tgz",
+ "integrity": "sha512-wStfgOJqMv4QKktuH273f5fyi3D3vy2pHOiSDGPvpcS/q+wb/M7AK3vkCcaHbkZxDOlDU/lDJgccygKSG2OhtA==",
+ "dev": true
+ },
"node_modules/jsc-android": {
"version": "250230.2.1",
"resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-250230.2.1.tgz",
@@ -11075,6 +11088,12 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
+ "node_modules/linked-list": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/linked-list/-/linked-list-0.1.0.tgz",
+ "integrity": "sha512-Zr4ovrd0ODzF3ut2TWZMdHIxb8iFdJc/P3QM4iCJdlxxGHXo69c9hGIHzLo8/FtuR9E6WUZc5irKhtPUgOKMAg==",
+ "dev": true
+ },
"node_modules/linkify-it": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
@@ -13310,6 +13329,16 @@
"node": ">=6"
}
},
+ "node_modules/querystring": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz",
+ "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==",
+ "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@@ -14240,6 +14269,18 @@
"jsesc": "bin/jsesc"
}
},
+ "node_modules/remotedev": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/remotedev/-/remotedev-0.2.9.tgz",
+ "integrity": "sha512-W8dHOv9BcFnetFEd08yNb5O9Hd+zkTFFnf9FRjNCkb4u+JgQ/U152Aw4q83AmY3m34d6KZwhK5ip/Qc331+4vA==",
+ "dev": true,
+ "dependencies": {
+ "jsan": "^3.1.3",
+ "querystring": "^0.2.0",
+ "rn-host-detect": "^1.0.1",
+ "socketcluster-client": "^13.0.0"
+ }
+ },
"node_modules/remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -14382,6 +14423,12 @@
"rimraf": "bin.js"
}
},
+ "node_modules/rn-host-detect": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz",
+ "integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==",
+ "dev": true
+ },
"node_modules/rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -14626,6 +14673,33 @@
"node": ">=10"
}
},
+ "node_modules/sc-channel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/sc-channel/-/sc-channel-1.2.0.tgz",
+ "integrity": "sha512-M3gdq8PlKg0zWJSisWqAsMmTVxYRTpVRqw4CWAdKBgAfVKumFcTjoCV0hYu7lgUXccCtCD8Wk9VkkE+IXCxmZA==",
+ "dev": true,
+ "dependencies": {
+ "component-emitter": "1.2.1"
+ }
+ },
+ "node_modules/sc-channel/node_modules/component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ },
+ "node_modules/sc-errors": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-1.4.1.tgz",
+ "integrity": "sha512-dBn92iIonpChTxYLgKkIT/PCApvmYT6EPIbRvbQKTgY6tbEbIy8XVUv4pGyKwEK4nCmvX4TKXcN0iXC6tNW6rQ==",
+ "dev": true
+ },
+ "node_modules/sc-formatter": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sc-formatter/-/sc-formatter-3.0.3.tgz",
+ "integrity": "sha512-lYI/lTs1u1c0geKElcj+bmEUfcP/HuKg2iDeTijPSjiTNFzN3Cf8Qh6tVd65oi7Qn+2/oD7LP4s6GC13v/9NiQ==",
+ "dev": true
+ },
"node_modules/scheduler": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.22.0.tgz",
@@ -15068,6 +15142,68 @@
"node": ">=0.10.0"
}
},
+ "node_modules/socketcluster-client": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/socketcluster-client/-/socketcluster-client-13.0.1.tgz",
+ "integrity": "sha512-hxiE2xz6mgaBlhXbtBa4POgWVEvIcjCoHzf5LTUVhI9IL8V2ltV3Ze8pQsi9egqTjSz4RHPfyrJ7BiETe5Kthw==",
+ "dev": true,
+ "dependencies": {
+ "base-64": "0.1.0",
+ "clone": "2.1.1",
+ "component-emitter": "1.2.1",
+ "linked-list": "0.1.0",
+ "querystring": "0.2.0",
+ "sc-channel": "^1.2.0",
+ "sc-errors": "^1.4.0",
+ "sc-formatter": "^3.0.1",
+ "uuid": "3.2.1",
+ "ws": "5.1.1"
+ }
+ },
+ "node_modules/socketcluster-client/node_modules/clone": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha512-h5FLmEMFHeuzqmpVRcDayNlVZ+k4uK1niyKQN6oUMe7ieJihv44Vc3dY/kDnnWX4PDQSwes48s965PG/D4GntQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/socketcluster-client/node_modules/component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ },
+ "node_modules/socketcluster-client/node_modules/querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+ "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/socketcluster-client/node_modules/uuid": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
+ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/socketcluster-client/node_modules/ws": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-5.1.1.tgz",
+ "integrity": "sha512-bOusvpCb09TOBLbpMKszd45WKC2KPtxiyiHanv+H2DE3Az+1db5a/L7sVJZVDPUC1Br8f0SKRr1KjLpD1U/IAw==",
+ "dev": true,
+ "dependencies": {
+ "async-limiter": "~1.0.0"
+ }
+ },
"node_modules/sort-array": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz",
@@ -20689,6 +20825,12 @@
}
}
},
+ "base-64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
+ "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==",
+ "dev": true
+ },
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -25205,6 +25347,12 @@
"xmlcreate": "^2.0.3"
}
},
+ "jsan": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/jsan/-/jsan-3.1.14.tgz",
+ "integrity": "sha512-wStfgOJqMv4QKktuH273f5fyi3D3vy2pHOiSDGPvpcS/q+wb/M7AK3vkCcaHbkZxDOlDU/lDJgccygKSG2OhtA==",
+ "dev": true
+ },
"jsc-android": {
"version": "250230.2.1",
"resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-250230.2.1.tgz",
@@ -25621,6 +25769,12 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
+ "linked-list": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/linked-list/-/linked-list-0.1.0.tgz",
+ "integrity": "sha512-Zr4ovrd0ODzF3ut2TWZMdHIxb8iFdJc/P3QM4iCJdlxxGHXo69c9hGIHzLo8/FtuR9E6WUZc5irKhtPUgOKMAg==",
+ "dev": true
+ },
"linkify-it": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
@@ -27420,6 +27574,12 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
+ "querystring": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz",
+ "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==",
+ "dev": true
+ },
"querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@@ -28185,6 +28345,18 @@
}
}
},
+ "remotedev": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/remotedev/-/remotedev-0.2.9.tgz",
+ "integrity": "sha512-W8dHOv9BcFnetFEd08yNb5O9Hd+zkTFFnf9FRjNCkb4u+JgQ/U152Aw4q83AmY3m34d6KZwhK5ip/Qc331+4vA==",
+ "dev": true,
+ "requires": {
+ "jsan": "^3.1.3",
+ "querystring": "^0.2.0",
+ "rn-host-detect": "^1.0.1",
+ "socketcluster-client": "^13.0.0"
+ }
+ },
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -28295,6 +28467,12 @@
"glob": "^7.1.3"
}
},
+ "rn-host-detect": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz",
+ "integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==",
+ "dev": true
+ },
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -28498,6 +28676,35 @@
"xmlchars": "^2.2.0"
}
},
+ "sc-channel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/sc-channel/-/sc-channel-1.2.0.tgz",
+ "integrity": "sha512-M3gdq8PlKg0zWJSisWqAsMmTVxYRTpVRqw4CWAdKBgAfVKumFcTjoCV0hYu7lgUXccCtCD8Wk9VkkE+IXCxmZA==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ }
+ }
+ },
+ "sc-errors": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/sc-errors/-/sc-errors-1.4.1.tgz",
+ "integrity": "sha512-dBn92iIonpChTxYLgKkIT/PCApvmYT6EPIbRvbQKTgY6tbEbIy8XVUv4pGyKwEK4nCmvX4TKXcN0iXC6tNW6rQ==",
+ "dev": true
+ },
+ "sc-formatter": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/sc-formatter/-/sc-formatter-3.0.3.tgz",
+ "integrity": "sha512-lYI/lTs1u1c0geKElcj+bmEUfcP/HuKg2iDeTijPSjiTNFzN3Cf8Qh6tVd65oi7Qn+2/oD7LP4s6GC13v/9NiQ==",
+ "dev": true
+ },
"scheduler": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.22.0.tgz",
@@ -28858,6 +29065,59 @@
}
}
},
+ "socketcluster-client": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/socketcluster-client/-/socketcluster-client-13.0.1.tgz",
+ "integrity": "sha512-hxiE2xz6mgaBlhXbtBa4POgWVEvIcjCoHzf5LTUVhI9IL8V2ltV3Ze8pQsi9egqTjSz4RHPfyrJ7BiETe5Kthw==",
+ "dev": true,
+ "requires": {
+ "base-64": "0.1.0",
+ "clone": "2.1.1",
+ "component-emitter": "1.2.1",
+ "linked-list": "0.1.0",
+ "querystring": "0.2.0",
+ "sc-channel": "^1.2.0",
+ "sc-errors": "^1.4.0",
+ "sc-formatter": "^3.0.1",
+ "uuid": "3.2.1",
+ "ws": "5.1.1"
+ },
+ "dependencies": {
+ "clone": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz",
+ "integrity": "sha512-h5FLmEMFHeuzqmpVRcDayNlVZ+k4uK1niyKQN6oUMe7ieJihv44Vc3dY/kDnnWX4PDQSwes48s965PG/D4GntQ==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
+ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
+ "dev": true
+ },
+ "ws": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-5.1.1.tgz",
+ "integrity": "sha512-bOusvpCb09TOBLbpMKszd45WKC2KPtxiyiHanv+H2DE3Az+1db5a/L7sVJZVDPUC1Br8f0SKRr1KjLpD1U/IAw==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ }
+ }
+ },
"sort-array": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz",
diff --git a/package.json b/package.json
index 1fc8f2c3..aa0afdac 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,7 @@
"react-native-performance": "^2.0.0",
"react-native-quick-sqlite": "^8.0.0-beta.2",
"react-test-renderer": "18.1.0",
+ "remotedev": "^0.2.9",
"type-fest": "^3.12.0",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
diff --git a/tests/unit/onyxMetricsDecorationTest.js b/tests/unit/onyxMetricsDecorationTest.js
index 151835c0..56c943d1 100644
--- a/tests/unit/onyxMetricsDecorationTest.js
+++ b/tests/unit/onyxMetricsDecorationTest.js
@@ -63,15 +63,36 @@ describe('Onyx', () => {
});
// When calling decorated methods through Onyx[methodName]
- const methods = ['set', 'multiSet', 'clear', 'merge', 'mergeCollection'];
- methods.forEach(name => Onyx[name]('mockKey', {mockKey: {mockValue: 'mockValue'}}));
+ const methods = [
+ {
+ name: 'set',
+ params: ['mockKey', {mockValue: 'mockValue'}],
+ },
+ {
+ name: 'multiSet',
+ params: [{mockKey: {mockValue: 'mockValue'}}],
+ },
+ {
+ name: 'clear',
+ params: [[]],
+ },
+ {
+ name: 'merge',
+ params: ['mockKey', {mockKey: {mockValue: 'mockValue'}}],
+ },
+ {
+ name: 'mergeCollection',
+ params: ['mockKey', {mockKey: {mockValue: 'mockValue'}}],
+ },
+ ];
+ methods.forEach(({name, params}) => Onyx[name](...params));
return waitForPromisesToResolve()
.then(() => {
// Then metrics should have captured data for each method
const summaries = Onyx.getMetrics().summaries;
- methods.forEach((name) => {
+ methods.forEach(({name}) => {
expect(summaries[`Onyx:${name}`].total).toBeGreaterThan(0);
});
});
diff --git a/tests/unit/onyxTest.js b/tests/unit/onyxTest.js
index 5b10e258..fb076103 100644
--- a/tests/unit/onyxTest.js
+++ b/tests/unit/onyxTest.js
@@ -118,7 +118,7 @@ describe('Onyx', () => {
})
.then(() => {
// Test key should be cleared
- expect(testKeyValue).toBeNull();
+ expect(testKeyValue).toBeUndefined();
// Other test key should be returned to its default state
expect(otherTestValue).toBe(42);