react-native-onyx 2.0.100 → 2.0.102

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"));
@@ -41,7 +40,7 @@ const OnyxConnectionManager_1 = __importDefault(require("./OnyxConnectionManager
41
40
  const GlobalSettings = __importStar(require("./GlobalSettings"));
42
41
  const metrics_1 = __importDefault(require("./metrics"));
43
42
  /** Initialize the store with actions and listening for storage events */
44
- function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = Boolean(global.localStorage), debugSetState = false, enablePerformanceMetrics = false, skippableCollectionMemberIDs = [], }) {
43
+ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = !!global.localStorage, debugSetState = false, enablePerformanceMetrics = false, skippableCollectionMemberIDs = [], }) {
45
44
  var _a;
46
45
  if (enablePerformanceMetrics) {
47
46
  GlobalSettings.setPerformanceMetricsEnabled(true);
@@ -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);
package/dist/OnyxCache.js CHANGED
@@ -169,10 +169,9 @@ class OnyxCache {
169
169
  temp.push(value);
170
170
  numKeysToRemove--;
171
171
  }
172
- // eslint-disable-next-line @typescript-eslint/prefer-for-of
173
- for (let i = 0; i < temp.length; ++i) {
174
- delete this.storageMap[temp[i]];
175
- this.recentKeys.delete(temp[i]);
172
+ for (const key of temp) {
173
+ delete this.storageMap[key];
174
+ this.recentKeys.delete(key);
176
175
  }
177
176
  }
178
177
  /** Set the recent keys list size */
@@ -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
@@ -26,10 +26,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- /* eslint-disable @typescript-eslint/prefer-for-of */
30
29
  /* eslint-disable no-continue */
31
30
  const fast_equals_1 = require("fast-equals");
32
31
  const clone_1 = __importDefault(require("lodash/clone"));
32
+ const pick_1 = __importDefault(require("lodash/pick"));
33
33
  const DevTools_1 = __importDefault(require("./DevTools"));
34
34
  const Logger = __importStar(require("./Logger"));
35
35
  const OnyxCache_1 = __importStar(require("./OnyxCache"));
@@ -526,7 +526,7 @@ function getCachedCollection(collectionKey, collectionMemberKeys) {
526
526
  /**
527
527
  * When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
528
528
  */
529
- function keysChanged(collectionKey, partialCollection, partialPreviousCollection, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
529
+ function keysChanged(collectionKey, partialCollection, partialPreviousCollection, notifyConnectSubscribers = true, notifyWithOnyxSubscribers = true) {
530
530
  // We prepare the "cached collection" which is the entire collection + the new partial data that
531
531
  // was merged in via mergeCollection().
532
532
  const cachedCollection = getCachedCollection(collectionKey);
@@ -536,8 +536,8 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
536
536
  // and does not represent all of the combined keys and values for a collection key. It is just the "new" data that was merged in via mergeCollection().
537
537
  const stateMappingKeys = Object.keys(callbackToStateMapping);
538
538
  const collectionKeyLength = collectionKey.length;
539
- for (let i = 0; i < stateMappingKeys.length; i++) {
540
- const subscriber = callbackToStateMapping[stateMappingKeys[i]];
539
+ for (const stateMappingKey of stateMappingKeys) {
540
+ const subscriber = callbackToStateMapping[stateMappingKey];
541
541
  if (!subscriber) {
542
542
  continue;
543
543
  }
@@ -555,7 +555,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
555
555
  const isSubscribedToCollectionMemberKey = isCollectionMemberKey(collectionKey, subscriber.key, collectionKeyLength);
556
556
  // Regular Onyx.connect() subscriber found.
557
557
  if (typeof subscriber.callback === 'function') {
558
- if (!notifyRegularSubscibers) {
558
+ if (!notifyConnectSubscribers) {
559
559
  continue;
560
560
  }
561
561
  // If they are subscribed to the collection key and using waitForCollectionCallback then we'll
@@ -568,8 +568,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
568
568
  // If they are not using waitForCollectionCallback then we notify the subscriber with
569
569
  // the new merged data but only for any keys in the partial collection.
570
570
  const dataKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
571
- for (let j = 0; j < dataKeys.length; j++) {
572
- const dataKey = dataKeys[j];
571
+ for (const dataKey of dataKeys) {
573
572
  if ((0, fast_equals_1.deepEqual)(cachedCollection[dataKey], previousCollection[dataKey])) {
574
573
  continue;
575
574
  }
@@ -591,7 +590,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
591
590
  }
592
591
  // React component subscriber found.
593
592
  if (utils_1.default.hasWithOnyxInstance(subscriber)) {
594
- if (!notifyWithOnyxSubscibers) {
593
+ if (!notifyWithOnyxSubscribers) {
595
594
  continue;
596
595
  }
597
596
  // We are subscribed to a collection key so we must update the data in state with the new
@@ -618,8 +617,7 @@ function keysChanged(collectionKey, partialCollection, partialPreviousCollection
618
617
  const prevCollection = (_a = prevState === null || prevState === void 0 ? void 0 : prevState[subscriber.statePropertyName]) !== null && _a !== void 0 ? _a : {};
619
618
  const finalCollection = (0, clone_1.default)(prevCollection);
620
619
  const dataKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
621
- for (let j = 0; j < dataKeys.length; j++) {
622
- const dataKey = dataKeys[j];
620
+ for (const dataKey of dataKeys) {
623
621
  finalCollection[dataKey] = cachedCollection[dataKey];
624
622
  }
625
623
  if ((0, fast_equals_1.deepEqual)(prevCollection, finalCollection)) {
@@ -718,8 +716,8 @@ function keyChanged(key, value, previousValue, canUpdateSubscriber = () => true,
718
716
  }
719
717
  }
720
718
  const cachedCollections = {};
721
- for (let i = 0; i < stateMappingKeys.length; i++) {
722
- const subscriber = callbackToStateMapping[stateMappingKeys[i]];
719
+ for (const stateMappingKey of stateMappingKeys) {
720
+ const subscriber = callbackToStateMapping[stateMappingKey];
723
721
  if (!subscriber || !isKeyMatch(subscriber.key, key) || !canUpdateSubscriber(subscriber)) {
724
722
  continue;
725
723
  }
@@ -1092,7 +1090,7 @@ function subscribeToKey(connectOptions) {
1092
1090
  // Performance improvement
1093
1091
  // If the mapping is connected to an onyx key that is not a collection
1094
1092
  // we can skip the call to getAllKeys() and return an array with a single item
1095
- if (Boolean(mapping.key) && typeof mapping.key === 'string' && !isCollectionKey(mapping.key) && OnyxCache_1.default.getAllKeys().has(mapping.key)) {
1093
+ if (!!mapping.key && typeof mapping.key === 'string' && !isCollectionKey(mapping.key) && OnyxCache_1.default.getAllKeys().has(mapping.key)) {
1096
1094
  return new Set([mapping.key]);
1097
1095
  }
1098
1096
  return getAllKeys();
@@ -1171,6 +1169,51 @@ function unsubscribeFromKey(subscriptionID) {
1171
1169
  deleteKeyBySubscriptions(lastSubscriptionID);
1172
1170
  delete callbackToStateMapping[subscriptionID];
1173
1171
  }
1172
+ function updateSnapshots(data, mergeFn) {
1173
+ const snapshotCollectionKey = OnyxUtils.getSnapshotKey();
1174
+ if (!snapshotCollectionKey)
1175
+ return [];
1176
+ const promises = [];
1177
+ const snapshotCollection = OnyxUtils.getCachedCollection(snapshotCollectionKey);
1178
+ const snapshotCollectionKeyLength = snapshotCollectionKey.length;
1179
+ Object.entries(snapshotCollection).forEach(([snapshotEntryKey, snapshotEntryValue]) => {
1180
+ // Snapshots may not be present in cache. We don't know how to update them so we skip.
1181
+ if (!snapshotEntryValue) {
1182
+ return;
1183
+ }
1184
+ let updatedData = {};
1185
+ data.forEach(({ key, value }) => {
1186
+ // snapshots are normal keys so we want to skip update if they are written to Onyx
1187
+ if (OnyxUtils.isCollectionMemberKey(snapshotCollectionKey, key, snapshotCollectionKeyLength)) {
1188
+ return;
1189
+ }
1190
+ if (typeof snapshotEntryValue !== 'object' || !('data' in snapshotEntryValue)) {
1191
+ return;
1192
+ }
1193
+ const snapshotData = snapshotEntryValue.data;
1194
+ if (!snapshotData || !snapshotData[key]) {
1195
+ return;
1196
+ }
1197
+ if (Array.isArray(value) || Array.isArray(snapshotData[key])) {
1198
+ updatedData[key] = value || [];
1199
+ return;
1200
+ }
1201
+ if (value === null) {
1202
+ updatedData[key] = value;
1203
+ return;
1204
+ }
1205
+ const oldValue = updatedData[key] || {};
1206
+ const newValue = (0, pick_1.default)(value, Object.keys(snapshotData[key]));
1207
+ updatedData = Object.assign(Object.assign({}, updatedData), { [key]: Object.assign(oldValue, newValue) });
1208
+ });
1209
+ // Skip the update if there's no data to be merged
1210
+ if (utils_1.default.isEmptyObject(updatedData)) {
1211
+ return;
1212
+ }
1213
+ promises.push(() => mergeFn(snapshotEntryKey, { data: updatedData }));
1214
+ });
1215
+ return promises;
1216
+ }
1174
1217
  const OnyxUtils = {
1175
1218
  METHOD,
1176
1219
  getMergeQueue,
@@ -1220,6 +1263,11 @@ const OnyxUtils = {
1220
1263
  getEvictionBlocklist,
1221
1264
  getSkippableCollectionMemberIDs,
1222
1265
  setSkippableCollectionMemberIDs,
1266
+ storeKeyBySubscriptions,
1267
+ deleteKeyBySubscriptions,
1268
+ addKeyToRecentlyAccessedIfNeeded,
1269
+ reduceCollectionWithSelector,
1270
+ updateSnapshots,
1223
1271
  };
1224
1272
  GlobalSettings.addGlobalSettingsChangeListener(({ enablePerformanceMetrics }) => {
1225
1273
  if (!enablePerformanceMetrics) {
package/dist/utils.js CHANGED
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/prefer-for-of */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  /** Checks whether the given object is an object and not null/undefined. */
5
4
  function isEmptyObject(obj) {
@@ -132,8 +131,7 @@ function checkCompatibilityWithExistingValue(value, existingValue) {
132
131
  function filterObject(obj, condition, include) {
133
132
  const result = {};
134
133
  const entries = Object.entries(obj);
135
- for (let i = 0; i < entries.length; i++) {
136
- const [key, value] = entries[i];
134
+ for (const [key, value] of entries) {
137
135
  let shouldInclude;
138
136
  if (Array.isArray(condition)) {
139
137
  shouldInclude = condition.includes(key);
@@ -142,7 +140,7 @@ function filterObject(obj, condition, include) {
142
140
  shouldInclude = key === condition;
143
141
  }
144
142
  else {
145
- shouldInclude = condition(entries[i]);
143
+ shouldInclude = condition([key, value]);
146
144
  }
147
145
  if (include ? shouldInclude : !shouldInclude) {
148
146
  result[key] = value;
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.102",
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,8 +68,10 @@
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
- "eslint-config-expensify": "^2.0.44",
74
+ "eslint-config-expensify": "^2.0.81",
71
75
  "eslint-config-prettier": "^8.8.0",
72
76
  "eslint-plugin-import": "^2.29.1",
73
77
  "eslint-plugin-jsx-a11y": "^6.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"