From b84681fdc7d37645cd149f40e98cb18fd8bc393c Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 7 Dec 2021 08:56:27 +0100 Subject: [PATCH 01/76] v4 alpha (#3060) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(hydration): remove hydration package (#2936) * V4: streamline cancel refetch (#2937) * feat: streamline cancelRefetch the following functions now default to true for cancelRefetch: - refetchQueries (+invalidateQueries, + resetQueries) - query.refetch - fetchNextPage (unchanged) - fetchPreviousPage (unchanged) * feat: streamline cancelRefetch make sure that refetchOnReconnect and refetchOnWindowFocus do not cancel already running requests * feat: streamline cancelRefetch update tests refetch and invalidate now both cancel previous queries, which is intended, so we get more calls to the queryFn in these cases * feat: streamline cancelRefetch add more tests for cancelRefetch behavior * feat: streamline cancelRefetch update docs and migration guide * feat: streamline cancelRefetch simplify conditions by moving the ?? true default down to fetch on observer level; all 3 callers (fetchNextPage, fetchPreviousPage and refetch) just pass their options down and adhere to this default; refetch also only has 3 callers: - refetch from useQuery, where we want the default - onOnline and onFocus, where we now explicitly pass false to keep the previous behavior and add more tests * feat: streamline cancelRefetch we always call this.fetch() with options, so we can just as well make the mandatory also, streamline signatures by destructing values that can't be forwarded (and use empty object as default value) in options and just spread the rest * feat: streamline cancelRefetch fix types for refetch it was accidentally made too wide and allowed all refetchFilters, like `predicate`; but with `refetch` on an obserserver, there is nothing to filter for, except the page, so that is what we need to accept via `RefetchPageFilters` * feat: streamline cancelRefetch refetch never took a queryKey as param - it is always bound to the observer * feat: better query filters (#2938) * fix: rename react directory to reactjs (#2884) * fix: rename react directory to reactjs the directory being named "react" causes an error with the moduleDirectories option from jest * fix: update package.json files to match the updated reactjs directory name * fix: change react test utils imports to match new directory name * docs(v4): add renamed reactjs details to migration guide Co-authored-by: Eddy Vinck * feat: mutation cache duration (#2963) * feat: mutation cachetime stramline queryCache / mutationCache events by combining them into notifiable.ts * feat: mutation cachetime removable * feat: mutation cachetime add gc to mutations * feat: mutation cachetime streamline event types between queries and mutations * feat: mutation cachetime tests, and I forgot to implement optionalRemove, so make it abstract * feat: mutation cachetime replicate gc behavior from https://github.com/tannerlinsley/react-query/pull/2950 and add more tests * feat: mutation cachetime get test coverage back to 100% * feat: mutation cachetime docs * feat: mutation cachetime try to make tests more resilient * feat: mutation cachetime fix imports after merge conflict * refactor(persistQueryClient): Make persistQueryClient stable (#2961) * :truck: Remove experimental from persist-query-client * :truck: Rename persistor -> persister * ✏️ Fix Persistor -> Persister in imports * :truck: Update name in rollup config * :truck: Move createAsyncStoragePersister and createWebStoragePersister to stable version and rename persistor -> persister * 📝 Update documentation * 📝 Add migrating to v4 docs * Apply suggestions from code review Co-authored-by: Dominik Dorfmeister * 2964 changes to on success callback (#2969) * feat(useQuery): onSuccess callback do not call onSuccess if update was done manually from setQueryData * feat(useQuery): onSuccess callback test that onSuccess is not called when setQueryData is used * feat(useQuery): onSuccess callback docs changes * feat(useQuery): onSuccess callback options spread is wrong - `updatedAt` is actually `dataUpdatedAt`. Oddly we didn't have a test, so I added one * 2919 query key array (#2988) * feat: query key array remove code that internally ensures that we get an Array, because it is now the expected interface, ensured by TypeScript * feat: query key array update tests to the new syntax * feat: query key array fix assertions, because there is no array wrapping happening internally anymore. The key you receive from the context is exactly the key you passed in * feat: query key array this test doesn't make much sense anymore * feat: query key array wrapping in an extra array doesn't yield the same results anymore since v4 because keys need to be an array * feat: query key array make docs adhere to new array key syntax * feat: query key array migration docs * feat(QueryObserver): track queries as default (#2987) * feat(Query Options): remove notifyOnChangePropsExclusion - remove related code from queryObserver - remove type def - remove related tests * docs(Query Options): update notifyOnChangePropsExclusion sections - remove from api references - add to v4 migration guide * feat(QueryObserver): "tracked" as default behavior - remove "tracked" completely if notifyOnChangeProps is not defined, behave as v3 "tracked" - add `notifyOnChangeProps: 'all' to opt out of the smart tracking TODO: Now that default behavior has changed, work out the failed tests. Which parts to change for current ones and possibly write new ones. * test(useQuery): adjust tests to pass for notifyOnChangeProps udpate * test(useInfiniteQuery): adjust tests to pass for notifyOnChangeProps udpate * test(QueryResetErrorBoundary): adjust tests to pass for notifyOnChangeProps udpate * refactor(QueryObserver): use nullish coalescing operator much cleaner than the negated if I started with * test(QueryResetErrorBoundary): remove "tracked" from test * revert: test(QueryResetErrorBoundary): adjust tests to pass for notifyOnChaneProps udpate This reverts commit a34b4720675dad5ee6ebde401639f328c0c83122. The changes are not necessary after PR #2993 fix. * refactor(QueryObserver): combine prop checks * docs(notifyOnChangeProps): update docs to reflect new api * refactor: Remove deprecated promise cancel (#2996) * :fire: Remove the cancel method on promise for cancelling promise * ✅ Fix query client tests * ✅ Update query and useQuery tests * ✅ Update use infinite query tests * 📝 Update migartion guide * :bug: Fix linking in documentation * :pencil: Fix grammatical errors in docs Co-authored-by: Dominik Dorfmeister * :refactor: Use abortSignal for query cancellation in InfiniteQueryBehavior * 🚨 Fix lint errors * :recycle: Move define signal property to a separate function Co-authored-by: Dominik Dorfmeister * remove test that doesn't make sense anymore - we don't allow different falsy query keys now * 2927 offline queries (#3006) * feat(useQuery): offline queries remove defaultQueryObserverOptions because it is the same as defaultQueryOptions and we can just use that * feat(useQuery): offline queries setup dependent default values, to make it easier to work with them * feat(useQuery): offline queries basic changes to retryer: - pause the query before fetching depending upon networkMode - pause retries depending upon networkRetry * feat(useQuery): offline queries move networkRetry and networkMode defaults to the retryer creation, because we need the same for mutations * feat(useQuery): offline queries decouple focus and online manager: we're now informing caches of a focus when we're focussed, and about an online event if we come online; if the retryer continues, it can then decide to not fetch depending on our networkMode * feat(useQuery): offline queries expose isPaused on the queryResult and make sure isFetching is false when we are paused * feat(useQuery): offline queries knowing if we can fetch depends on if we are paused or not, as other conditions should apply also, rename options (not sure if that will stick though) * feat(useQuery): offline queries adjust existing tests for isPaused being exposed * feat(useQuery): offline queries fix existing test by setting options to emulate the previous behaviour, otherwise, with `mockNavigatorOnline` being set to false right from the start, the mutation would never fire off * feat(useQuery): offline queries adapt onOnline, onFocus tests to new behavior: they are now decoupled, and onOnline is always called even when not focused and vice versa. The retryer should make sure to not continue fetching if necessary * feat(useQuery): offline queries first test for networkMode * feat(useQuery): offline queries isFetching and isPaused are now derived and stored together in a fetchingState enum (idle, fetching, paused) * feat(useQuery): offline queries better networkMode api: online, always, offlineFirst (basically always but with paused retries) * feat(useQuery): offline queries more tests for networkMode: online * feat(useQuery): offline queries more tests for networkMode: online * feat(useQuery): offline queries tests for networkMode: always * feat(useQuery): offline queries fix tests that were influencing each other by using proper jest mocks for online and visibility state * add paused queries to the devtools.tsx * feat(useQuery): offline queries never stop pausing when continueFn is called. Initially, I only had this guard for when it's called from the outside, e.g. for onWindowFocus while still being offline, but we need this always because otherwise query cancellation can potentially continue a paused query * feat(useQuery): offline queries okay, pausing multiple times was a bad idea, continueFn() will be called eventually anyways * feat(useQuery): offline queries attempt at offline toggle button * feat(useQuery): offline queries different icons, padding, color * feat(useQuery): offline queries i messed up the icon order * feat(useQuery): offline queries guard against illegal state transitions: paused queries can unmount or get cancelled, in which case we shouldn't continue them, even if we dispatch the continue event * feat(useQuery): offline queries fix devtools tests, account for paused queries * Revert "feat(useQuery): offline queries" This reverts commit a647f64a051ca4c02a872e7871b4b2ce49aeda2c. * feat(useQuery): offline queries keep the do-not-start logic out of the run function, and thus out of promiseOrValue. if the promise has already been resolved in the meantime, e.g. because of a `cancel`, the run method will just do nothing, while the previous logic would've started to fetch * feat(useQuery): offline queries show inactive as higher priority than paused * feat(useQuery): offline queries make sure that optimistic results don't show an intermediate fetching state, but go opmistically to paused instead * feat(useQuery): offline queries real result needs to match optimistic result * feat(useQuery): offline queries stupid mistake * feat(useQuery): offline queries keep status color and status label in sync * feat(useQuery): offline queries make networkMode param mandatory for canFetch (and default to online internally) so that we can't screw that up again * feat(useQuery): offline queries make sure test "finishes" to avoid prints to the console if another test goes online again * feat(useQuery): offline queries move cancel function to the top, as it's no longer dependent on the promise since the `.cancel` function is gone; all we need is to abort the signal and reject the promise of the retryer * feat(useQuery): offline queries inline canContinue, because it's now only called if the query is in paused state anyways * feat(useQuery): offline queries avoid the impossible state by not calling config.onContinue for already resolved queries, as that would put them right into fetching state again, without actually fetching * feat(useQuery): offline queries let resolved querie continue, but don't put them in fetching state * feat(useQuery): offline queries fix merge conflict and invert condition because no-negated-condition * feat(useQuery): offline queries add test for abort signal consumed - different results expected for node < 15 where we don't have AbortController, thus can't consume the signal * feat(useQuery): offline queries online queries should not fetch if paused and we go online when cancelled and no refetchOnReconnect * feat(useQuery): offline queries gc test * feat(useQuery): offline queries offlineFirst test * feat(useQuery): offline queries mock useMediaQuery to get rid of unnecessary check in devtools - if window is defined, `matchMedia` is also defined * feat(useQuery): offline queries use a higher retryDelay to make test more stable, otherwise, it might start retrying before we "go offline" * feat(useQuery): offline queries improve devtools test: check if onClick props are being called * feat(useQuery): offline queries add devtools test for offline mock * feat(useQuery): offline queries offline mutations test * feat(useQuery): offline queries network mode docs (unfinished) * feat(useQuery): offline queries network mode docs * feat(useQuery): offline queries fix merge conflicts * refactor(queryClient): remove undocumented methods * fix: offline mutations fixes (#3051) * feat: offline mutations move reducer into Mutation class to avoid passing state (and options) around * feat: offline mutations optimistically set paused state depending on if we can fetch or not to avoid an intermediate state where we are loading but not paused * examples: fix query keys in basic examples because we need those for preview builds * fix(useMutation): make sure cacheCallbacks are always called even if the useMutation component unmounts and we have a cacheTime of 0; the fix was cherry-picked from the react-18 branch, where we also introduced this behavior * Feature/cachetime zero (#3054) * refactor: cacheTime-zero remove special handling for cacheTime: 0 and schedule a normal garbage collection for those queries. They will be eligible for gc after a setTimeout(0), but then they will only be optionally removed. This makes sure that paused queries are NOT gc'ed * refactor: cacheTime-zero remove special test "about online queries with cacheTime:0 should not fetch if paused and then unmounted". paused queries will now be kept until they continue, just like with every other query, unless query cancellation or abort signal was involved * refactor: cacheTime-zero adapt "remounting" test: if the same query with cacheTime 0 unmounts and remounts in the same cycle, the query will now be picked up and will not go to loading state again. I think this is okay * refactor: cacheTime-zero re-add instant query removal after fetching, because fetching via `queryClient.fetchQuery` will not remove the query otherwise, because the normal gc-mechanism now checks for `hadObservers` due to a suspense issue :/ * refactor: cacheTime-zero weird edge case: the previous logic was instantly removing the query _while_ it was still fetching, which is something we likely don't want. The data will stay in the currentQuery of the observer if the observer unsubscribes but still exists, and a new subscription will pick it up, unless the query was explicitly cancelled or the abort signal was consumed. * refactor: cacheTime-zero we need to wait a tick because even cacheTime 0 now waits at least a setTimeout(0) to be eligible for gc * refactor: cacheTime-zero schedule a new garbage collection after each new fetch; this won't do anything when you still have observers, but it fixes an edge case where prefetching took longer than the cacheTime, in which case the query was again never removed test needed adaption because we don't instantly remove, but deferred by a tick * refactor: cacheTime-zero stabilize test * refactor: cacheTime-zero apply a different suspense "workaround": do not garbage collect when fetching optimistically (done only by suspense) - gc will kick in once an observer subscribes; this will make sure we can still gc other fetches that don't have an observer consistently, like prefetching when the fetch takes longer than the gc time (which was leaking with the old workaround) * refactor: cacheTime-zero remove leftover * refactor: cacheTime-zero since every fetch triggers a new gc cycle, we don't need to do this in a loop anymore also, reset isFetchingOptimistic after every fetch * add publishing capabilities for alpha branch * fix(queryFilters): fetchStatus to queryFilters (#3061) Co-authored-by: Eddy Co-authored-by: Eddy Vinck Co-authored-by: Prateek Surana Co-authored-by: Rene Dellefont BREAKING CHANGE: new query filters, query key must be an array --- .github/workflows/test-and-publish.yml | 3 +- createAsyncStoragePersister/package.json | 6 + .../package.json | 6 - createWebStoragePersister/package.json | 6 + .../package.json | 6 - docs/src/manifests/manifest.json | 26 +- docs/src/pages/comparison.md | 2 +- docs/src/pages/guides/caching.md | 14 +- .../pages/guides/default-query-function.md | 5 +- docs/src/pages/guides/disabling-queries.md | 2 +- docs/src/pages/guides/filters.md | 25 +- docs/src/pages/guides/infinite-queries.md | 14 +- docs/src/pages/guides/initial-query-data.md | 18 +- .../guides/invalidations-from-mutations.md | 4 +- .../guides/migrating-to-react-query-3.md | 16 +- .../guides/migrating-to-react-query-4.md | 255 ++++ docs/src/pages/guides/mutations.md | 12 +- docs/src/pages/guides/network-mode.md | 46 + docs/src/pages/guides/optimistic-updates.md | 10 +- docs/src/pages/guides/parallel-queries.md | 6 +- .../pages/guides/placeholder-query-data.md | 6 +- docs/src/pages/guides/prefetching.md | 4 +- docs/src/pages/guides/queries.md | 8 +- docs/src/pages/guides/query-cancellation.md | 20 +- docs/src/pages/guides/query-functions.md | 16 +- docs/src/pages/guides/query-invalidation.md | 10 +- docs/src/pages/guides/query-keys.md | 15 +- docs/src/pages/guides/query-retries.md | 2 +- docs/src/pages/guides/ssr.md | 10 +- docs/src/pages/guides/testing.md | 4 +- .../pages/guides/window-focus-refetching.md | 2 +- docs/src/pages/overview.md | 2 +- ...stor.md => createAsyncStoragePersister.md} | 28 +- ...sistor.md => createWebStoragePersister.md} | 30 +- docs/src/pages/plugins/persistQueryClient.md | 46 +- docs/src/pages/quick-start.md | 4 +- .../pages/reference/InfiniteQueryObserver.md | 2 +- docs/src/pages/reference/MutationCache.md | 6 +- docs/src/pages/reference/QueryClient.md | 63 +- docs/src/pages/reference/QueryObserver.md | 2 +- docs/src/pages/reference/hydration.md | 8 - docs/src/pages/reference/useInfiniteQuery.md | 4 +- docs/src/pages/reference/useMutation.md | 14 +- docs/src/pages/reference/useQuery.md | 41 +- examples/basic-typescript/src/index.tsx | 2 +- examples/basic/src/index.js | 19 +- hydration/package.json | 6 - package.json | 26 +- persistQueryClient-experimental/package.json | 6 - persistQueryClient/package.json | 6 + react/package.json | 6 - reactjs/package.json | 6 + rollup.config.js | 19 +- .../index.ts | 12 +- src/core/infiniteQueryBehavior.ts | 16 +- src/core/infiniteQueryObserver.ts | 34 +- src/core/mutation.ts | 159 ++- src/core/mutationCache.ts | 57 +- src/core/notifiable.ts | 12 + src/core/queriesObserver.ts | 12 +- src/core/query.ts | 153 +-- src/core/queryCache.ts | 36 +- src/core/queryClient.ts | 108 +- src/core/queryObserver.ts | 105 +- src/core/removable.ts | 35 + src/core/retryer.ts | 76 +- src/core/tests/hydration.test.tsx | 133 +- src/core/tests/infiniteQueryBehavior.test.tsx | 8 +- src/core/tests/infiniteQueryObserver.test.tsx | 2 +- src/core/tests/mutationCache.test.tsx | 169 ++- src/core/tests/mutationObserver.test.tsx | 2 +- src/core/tests/mutations.test.tsx | 21 +- src/core/tests/queriesObserver.test.tsx | 38 +- src/core/tests/query.test.tsx | 78 +- src/core/tests/queryCache.test.tsx | 50 +- src/core/tests/queryClient.test.tsx | 289 +++-- src/core/tests/queryObserver.test.tsx | 4 +- src/core/tests/utils.test.tsx | 27 +- src/core/types.ts | 46 +- src/core/utils.ts | 80 +- .../index.ts | 8 +- .../index.ts | 8 +- .../tests/storageIsFull.test.ts | 70 +- src/devtools/devtools.tsx | 84 +- src/devtools/tests/devtools.test.tsx | 131 +- src/devtools/theme.tsx | 1 + src/devtools/useMediaQuery.ts | 6 +- src/devtools/utils.ts | 8 +- src/hydration/index.ts | 2 +- src/index.ts | 2 +- .../index.ts | 18 +- src/react/utils.ts | 16 - src/{react => reactjs}/Hydrate.tsx | 0 .../QueryClientProvider.tsx | 0 .../QueryErrorResetBoundary.tsx | 0 src/{react => reactjs}/index.ts | 0 src/{react => reactjs}/logger.native.ts | 0 src/{react => reactjs}/logger.ts | 0 .../reactBatchedUpdates.native.ts | 0 src/{react => reactjs}/reactBatchedUpdates.ts | 0 src/{react => reactjs}/setBatchUpdatesFn.ts | 0 src/{react => reactjs}/setLogger.ts | 0 src/{react => reactjs}/tests/Hydrate.test.tsx | 24 +- .../tests/QueryClientProvider.test.tsx | 0 .../tests/QueryResetErrorBoundary.test.tsx | 1 - .../tests/logger.native.test.tsx | 0 .../tests/ssr-hydration.test.tsx | 12 +- src/{react => reactjs}/tests/ssr.test.tsx | 0 .../tests/suspense.test.tsx | 6 +- .../tests/useInfiniteQuery.test.tsx | 43 +- .../tests/useIsFetching.test.tsx | 0 .../tests/useIsMutating.test.tsx | 19 +- .../tests/useMutation.test.tsx | 241 +++- .../tests/useQueries.test.tsx | 4 +- .../tests/useQuery.test.tsx | 1069 +++++++++++++++-- src/{react => reactjs}/tests/utils.tsx | 25 +- src/{react => reactjs}/types.ts | 3 + src/{react => reactjs}/useBaseQuery.ts | 16 +- src/{react => reactjs}/useInfiniteQuery.ts | 0 src/{react => reactjs}/useIsFetching.ts | 0 src/{react => reactjs}/useIsMutating.ts | 0 src/{react => reactjs}/useMutation.ts | 3 +- src/{react => reactjs}/useQueries.ts | 8 +- src/{react => reactjs}/useQuery.ts | 0 src/reactjs/utils.ts | 11 + src/ts3.8/useQueries.ts | 2 +- tsconfig.types.json | 7 +- 127 files changed, 3126 insertions(+), 1409 deletions(-) create mode 100644 createAsyncStoragePersister/package.json delete mode 100644 createAsyncStoragePersistor-experimental/package.json create mode 100644 createWebStoragePersister/package.json delete mode 100644 createWebStoragePersistor-experimental/package.json create mode 100644 docs/src/pages/guides/migrating-to-react-query-4.md create mode 100644 docs/src/pages/guides/network-mode.md rename docs/src/pages/plugins/{createAsyncStoragePersistor.md => createAsyncStoragePersister.md} (62%) rename docs/src/pages/plugins/{createWebStoragePersistor.md => createWebStoragePersister.md} (54%) delete mode 100644 hydration/package.json delete mode 100644 persistQueryClient-experimental/package.json create mode 100644 persistQueryClient/package.json delete mode 100644 react/package.json create mode 100644 reactjs/package.json create mode 100644 src/core/notifiable.ts create mode 100644 src/core/removable.ts rename src/{createAsyncStoragePersistor-experimental => createAsyncStoragePersister}/index.ts (91%) rename src/{createWebStoragePersistor-experimental => createWebStoragePersister}/index.ts (92%) rename src/{createWebStoragePersistor-experimental => createWebStoragePersister}/tests/storageIsFull.test.ts (61%) rename src/{persistQueryClient-experimental => persistQueryClient}/index.ts (87%) delete mode 100644 src/react/utils.ts rename src/{react => reactjs}/Hydrate.tsx (100%) rename src/{react => reactjs}/QueryClientProvider.tsx (100%) rename src/{react => reactjs}/QueryErrorResetBoundary.tsx (100%) rename src/{react => reactjs}/index.ts (100%) rename src/{react => reactjs}/logger.native.ts (100%) rename src/{react => reactjs}/logger.ts (100%) rename src/{react => reactjs}/reactBatchedUpdates.native.ts (100%) rename src/{react => reactjs}/reactBatchedUpdates.ts (100%) rename src/{react => reactjs}/setBatchUpdatesFn.ts (100%) rename src/{react => reactjs}/setLogger.ts (100%) rename src/{react => reactjs}/tests/Hydrate.test.tsx (87%) rename src/{react => reactjs}/tests/QueryClientProvider.test.tsx (100%) rename src/{react => reactjs}/tests/QueryResetErrorBoundary.test.tsx (99%) rename src/{react => reactjs}/tests/logger.native.test.tsx (100%) rename src/{react => reactjs}/tests/ssr-hydration.test.tsx (94%) rename src/{react => reactjs}/tests/ssr.test.tsx (100%) rename src/{react => reactjs}/tests/suspense.test.tsx (99%) rename src/{react => reactjs}/tests/useInfiniteQuery.test.tsx (97%) rename src/{react => reactjs}/tests/useIsFetching.test.tsx (100%) rename src/{react => reactjs}/tests/useIsMutating.test.tsx (84%) rename src/{react => reactjs}/tests/useMutation.test.tsx (70%) rename src/{react => reactjs}/tests/useQueries.test.tsx (99%) rename src/{react => reactjs}/tests/useQuery.test.tsx (81%) rename src/{react => reactjs}/tests/utils.tsx (74%) rename src/{react => reactjs}/types.ts (98%) rename src/{react => reactjs}/useBaseQuery.ts (88%) rename src/{react => reactjs}/useInfiniteQuery.ts (100%) rename src/{react => reactjs}/useIsFetching.ts (100%) rename src/{react => reactjs}/useIsMutating.ts (100%) rename src/{react => reactjs}/useMutation.ts (98%) rename src/{react => reactjs}/useQueries.ts (97%) rename src/{react => reactjs}/useQuery.ts (100%) create mode 100644 src/reactjs/utils.ts diff --git a/.github/workflows/test-and-publish.yml b/.github/workflows/test-and-publish.yml index 8c2e52ac94..24d468633e 100644 --- a/.github/workflows/test-and-publish.yml +++ b/.github/workflows/test-and-publish.yml @@ -5,6 +5,7 @@ on: branches: - 'master' - 'next' + - 'alpha' - 'beta' - '1.x' - '2.x' @@ -36,7 +37,7 @@ jobs: name: 'Publish Module to NPM' needs: test # publish only when merged in master on original repo, not on PR - if: github.repository == 'tannerlinsley/react-query' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/next' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/1.x' || github.ref == 'refs/heads/2.x') + if: github.repository == 'tannerlinsley/react-query' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/next' || github.ref == 'refs/heads/alpha' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/1.x' || github.ref == 'refs/heads/2.x') runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/createAsyncStoragePersister/package.json b/createAsyncStoragePersister/package.json new file mode 100644 index 0000000000..4d74825ea3 --- /dev/null +++ b/createAsyncStoragePersister/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/createAsyncStoragePersister/index.js", + "module": "../es/createAsyncStoragePersister/index.js", + "types": "../types/createAsyncStoragePersister/index.d.ts" +} diff --git a/createAsyncStoragePersistor-experimental/package.json b/createAsyncStoragePersistor-experimental/package.json deleted file mode 100644 index 6a1bf0b2cb..0000000000 --- a/createAsyncStoragePersistor-experimental/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "internal": true, - "main": "../lib/createAsyncStoragePersistor-experimental/index.js", - "module": "../es/createAsyncStoragePersistor-experimental/index.js", - "types": "../types/createAsyncStoragePersistor-experimental/index.d.ts" -} diff --git a/createWebStoragePersister/package.json b/createWebStoragePersister/package.json new file mode 100644 index 0000000000..21c28c7658 --- /dev/null +++ b/createWebStoragePersister/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/createWebStoragePersister/index.js", + "module": "../es/createWebStoragePersister/index.js", + "types": "../types/createWebStoragePersister/index.d.ts" +} diff --git a/createWebStoragePersistor-experimental/package.json b/createWebStoragePersistor-experimental/package.json deleted file mode 100644 index 2f4a1486c3..0000000000 --- a/createWebStoragePersistor-experimental/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "internal": true, - "main": "../lib/createWebStoragePersistor-experimental/index.js", - "module": "../es/createWebStoragePersistor-experimental/index.js", - "types": "../types/createWebStoragePersistor-experimental/index.d.ts" -} diff --git a/docs/src/manifests/manifest.json b/docs/src/manifests/manifest.json index 18ad4cd4e5..fd3cdf8274 100644 --- a/docs/src/manifests/manifest.json +++ b/docs/src/manifests/manifest.json @@ -75,6 +75,11 @@ "path": "/guides/query-functions", "editUrl": "/guides/query-functions.md" }, + { + "title": "Network Mode", + "path": "/guides/network-mode", + "editUrl": "/guides/network-mode.md" + }, { "title": "Parallel Queries", "path": "/guides/parallel-queries", @@ -204,6 +209,11 @@ "title": "Migrating to React Query 3", "path": "/guides/migrating-to-react-query-3", "editUrl": "/guides/migrating-to-react-query-3.md" + }, + { + "title": "Migrating to React Query 4", + "path": "/guides/migrating-to-react-query-4", + "editUrl": "/guides/migrating-to-react-query-4.md" } ] }, @@ -314,19 +324,19 @@ "heading": true, "routes": [ { - "title": "persistQueryClient (Experimental)", + "title": "persistQueryClient", "path": "/plugins/persistQueryClient", "editUrl": "/plugins/persistQueryClient.md" }, { - "title": "createWebStoragePersistor (Experimental)", - "path": "/plugins/createWebStoragePersistor", - "editUrl": "/plugins/createWebStoragePersistor.md" + "title": "createWebStoragePersister", + "path": "/plugins/createWebStoragePersister", + "editUrl": "/plugins/createWebStoragePersister.md" }, { - "title": "createAsyncStoragePersistor (Experimental)", - "path": "/plugins/createAsyncStoragePersistor", - "editUrl": "/plugins/createAsyncStoragePersistor.md" + "title": "createAsyncStoragePersister", + "path": "/plugins/createAsyncStoragePersister", + "editUrl": "/plugins/createAsyncStoragePersister.md" }, { "title": "broadcastQueryClient (Experimental)", @@ -442,4 +452,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/docs/src/pages/comparison.md b/docs/src/pages/comparison.md index 4a3691ed4c..dfa06c741d 100644 --- a/docs/src/pages/comparison.md +++ b/docs/src/pages/comparison.md @@ -64,7 +64,7 @@ Feature/Capability Key: > **1 Lagged Query Data** - React Query provides a way to continue to see an existing query's data while the next query loads (similar to the same UX that suspense will soon provide natively). This is extremely important when writing pagination UIs or infinite loading UIs where you do not want to show a hard loading state whenever a new query is requested. Other libraries do not have this capability and render a hard loading state for the new query (unless it has been prefetched), while the new query loads. -> **2 Render Optimization** - React Query has excellent rendering performance. It will only re-render your components when a query is updated. For example because it has new data, or to indicate it is fetching. React Query also batches updates together to make sure your application only re-renders once when multiple components are using the same query. If you are only interested in the `data` or `error` properties, you can reduce the number of renders even more by setting `notifyOnChangeProps` to `['data', 'error']`. Set `notifyOnChangeProps: 'tracked'` to automatically track which fields are accessed and only re-render if one of them changes. +> **2 Render Optimization** - React Query has excellent rendering performance. By default, it will automatically track which fields are accessed and only re-render if one of them changes. If you would like to opt-out of this optimization, setting `notifyOnChangeProps` to `'all'` will re-render your components whenever the query is updated. For example because it has new data, or to indicate it is fetching. React Query also batches updates together to make sure your application only re-renders once when multiple components are using the same query. If you are only interested in the `data` or `error` properties, you can reduce the number of renders even more by setting `notifyOnChangeProps` to `['data', 'error']`. > **3 Partial query matching** - Because React Query uses deterministic query key serialization, this allows you to manipulate variable groups of queries without having to know each individual query-key that you want to match, eg. you can refetch every query that starts with `todos` in its key, regardless of variables, or you can target specific queries with (or without) variables or nested properties, and even use a filter function to only match queries that pass your specific conditions. diff --git a/docs/src/pages/guides/caching.md b/docs/src/pages/guides/caching.md index 2f8b605328..e200b53721 100644 --- a/docs/src/pages/guides/caching.md +++ b/docs/src/pages/guides/caching.md @@ -16,17 +16,17 @@ This caching example illustrates the story and lifecycle of: Let's assume we are using the default `cacheTime` of **5 minutes** and the default `staleTime` of `0`. -- A new instance of `useQuery('todos', fetchTodos)` mounts. +- A new instance of `useQuery(['todos'], fetchTodos)` mounts. - Since no other queries have been made with this query + variable combination, this query will show a hard loading state and make a network request to fetch the data. - - It will then cache the data using `'todos'` and `fetchTodos` as the unique identifiers for that cache. + - It will then cache the data using `['todos']` as the unique identifiers for that cache. - The hook will mark itself as stale after the configured `staleTime` (defaults to `0`, or immediately). -- A second instance of `useQuery('todos', fetchTodos)` mounts elsewhere. +- A second instance of `useQuery(['todos'], fetchTodos)` mounts elsewhere. - Because this exact data exists in the cache from the first instance of this query, that data is immediately returned from the cache. - A background refetch is triggered for both queries (but only one request), since a new instance appeared on screen. - Both instances are updated with the new data if the fetch is successful -- Both instances of the `useQuery('todos', fetchTodos)` query are unmounted and no longer in use. +- Both instances of the `useQuery(['todos'], fetchTodos)` query are unmounted and no longer in use. - Since there are no more active instances of this query, a cache timeout is set using `cacheTime` to delete and garbage collect the query (defaults to **5 minutes**). -- Before the cache timeout has completed another instance of `useQuery('todos', fetchTodos)` mounts. The query immediately returns the available cached value while the `fetchTodos` function is being run in the background to populate the query with a fresh value. -- The final instance of `useQuery('todos', fetchTodos)` unmounts. -- No more instances of `useQuery('todos', fetchTodos)` appear within **5 minutes**. +- Before the cache timeout has completed another instance of `useQuery(['todos'], fetchTodos)` mounts. The query immediately returns the available cached value while the `fetchTodos` function is being run in the background to populate the query with a fresh value. +- The final instance of `useQuery(['todos'], fetchTodos)` unmounts. +- No more instances of `useQuery(['todos'], fetchTodos)` appear within **5 minutes**. - This query and its data are deleted and garbage collected. diff --git a/docs/src/pages/guides/default-query-function.md b/docs/src/pages/guides/default-query-function.md index 0ec3364c1f..7561ae375d 100644 --- a/docs/src/pages/guides/default-query-function.md +++ b/docs/src/pages/guides/default-query-function.md @@ -7,7 +7,6 @@ If you find yourself wishing for whatever reason that you could just share the s ```js // Define a default query function that will receive the query key -// the queryKey is guaranteed to be an Array here const defaultQueryFn = async ({ queryKey }) => { const { data } = await axios.get(`https://jsonplaceholder.typicode.com${queryKey[0]}`); return data; @@ -32,14 +31,14 @@ function App() { // All you have to do now is pass a key! function Posts() { - const { status, data, error, isFetching } = useQuery('/posts') + const { status, data, error, isFetching } = useQuery(['/posts']) // ... } // You can even leave out the queryFn and just go straight into options function Post({ postId }) { - const { status, data, error, isFetching } = useQuery(`/posts/${postId}`, { + const { status, data, error, isFetching } = useQuery([`/posts/${postId}`], { enabled: !!postId, }) diff --git a/docs/src/pages/guides/disabling-queries.md b/docs/src/pages/guides/disabling-queries.md index 8162e850b8..ec28592be2 100644 --- a/docs/src/pages/guides/disabling-queries.md +++ b/docs/src/pages/guides/disabling-queries.md @@ -26,7 +26,7 @@ function Todos() { error, refetch, isFetching, - } = useQuery('todos', fetchTodoList, { + } = useQuery(['todos'], fetchTodoList, { enabled: false, }) diff --git a/docs/src/pages/guides/filters.md b/docs/src/pages/guides/filters.md index 9419e44d11..79e6f1cda6 100644 --- a/docs/src/pages/guides/filters.md +++ b/docs/src/pages/guides/filters.md @@ -14,31 +14,30 @@ A query filter is an object with certain conditions to match a query with: await queryClient.cancelQueries() // Remove all inactive queries that begin with `posts` in the key -queryClient.removeQueries('posts', { inactive: true }) +queryClient.removeQueries(['posts'], { type: 'inactive' }) // Refetch all active queries -await queryClient.refetchQueries({ active: true }) +await queryClient.refetchQueries({ type: 'active' }) // Refetch all active queries that begin with `posts` in the key -await queryClient.refetchQueries('posts', { active: true }) +await queryClient.refetchQueries(['posts'], { type: 'active' }) ``` A query filter object supports the following properties: - `exact?: boolean` - If you don't want to search queries inclusively by query key, you can pass the `exact: true` option to return only the query with the exact query key you have passed. -- `active?: boolean` - - When set to `true` it will match active queries. - - When set to `false` it will match inactive queries. -- `inactive?: boolean` - - When set to `true` it will match inactive queries. - - When set to `false` it will match active queries. +- `type?: 'active' | 'inactive' | 'all'` + - Defaults to `all` + - When set to `active` it will match active queries. + - When set to `inactive` it will match inactive queries. - `stale?: boolean` - When set to `true` it will match stale queries. - When set to `false` it will match fresh queries. -- `fetching?: boolean` - - When set to `true` it will match queries that are currently fetching. - - When set to `false` it will match queries that are not fetching. +- `fetchStatus?: FetchStatus` + - When set to `fetching` it will match queries that are currently fetching. + - When set to `paused` it will match queries that wanted to fetch, but have been `paused`. + - When set to `idle` it will match queries that are not fetching. - `predicate?: (query: Query) => boolean` - This predicate function will be called for every single query in the cache and be expected to return truthy for queries that are `found`. - `queryKey?: QueryKey` @@ -53,7 +52,7 @@ A mutation filter is an object with certain conditions to match a mutation with: await queryClient.isMutating() // Filter mutations by mutationKey -await queryClient.isMutating({ mutationKey: "post" }) +await queryClient.isMutating({ mutationKey: ["post"] }) // Filter mutations using a predicate function await queryClient.isMutating({ predicate: (mutation) => mutation.options.variables?.id === 1 }) diff --git a/docs/src/pages/guides/infinite-queries.md b/docs/src/pages/guides/infinite-queries.md index 50d300bab6..7a5b812265 100644 --- a/docs/src/pages/guides/infinite-queries.md +++ b/docs/src/pages/guides/infinite-queries.md @@ -56,7 +56,7 @@ function Projects() { isFetching, isFetchingNextPage, status, - } = useInfiniteQuery('projects', fetchProjects, { + } = useInfiniteQuery(['projects'], fetchProjects, { getNextPageParam: (lastPage, pages) => lastPage.nextCursor, }) @@ -100,7 +100,7 @@ When an infinite query becomes `stale` and needs to be refetched, each group is If you only want to actively refetch a subset of all pages, you can pass the `refetchPage` function to `refetch` returned from `useInfiniteQuery`. ```js -const { refetch } = useInfiniteQuery('projects', fetchProjects, { +const { refetch } = useInfiniteQuery(['projects'], fetchProjects, { getNextPageParam: (lastPage, pages) => lastPage.nextCursor, }) @@ -132,7 +132,7 @@ function Projects() { isFetchingNextPage, fetchNextPage, hasNextPage, - } = useInfiniteQuery('projects', fetchProjects, { + } = useInfiniteQuery(['projects'], fetchProjects, { getNextPageParam: (lastPage, pages) => lastPage.nextCursor, }) @@ -146,7 +146,7 @@ function Projects() { Bi-directional lists can be implemented by using the `getPreviousPageParam`, `fetchPreviousPage`, `hasPreviousPage` and `isFetchingPreviousPage` properties and functions. ```js -useInfiniteQuery('projects', fetchProjects, { +useInfiniteQuery(['projects'], fetchProjects, { getNextPageParam: (lastPage, pages) => lastPage.nextCursor, getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor, }) @@ -157,7 +157,7 @@ useInfiniteQuery('projects', fetchProjects, { Sometimes you may want to show the pages in reversed order. If this is case, you can use the `select` option: ```js -useInfiniteQuery('projects', fetchProjects, { +useInfiniteQuery(['projects'], fetchProjects, { select: data => ({ pages: [...data.pages].reverse(), pageParams: [...data.pageParams].reverse(), @@ -170,7 +170,7 @@ useInfiniteQuery('projects', fetchProjects, { Manually removing first page: ```js -queryClient.setQueryData('projects', data => ({ +queryClient.setQueryData(['projects'], data => ({ pages: data.pages.slice(1), pageParams: data.pageParams.slice(1), })) @@ -183,7 +183,7 @@ const newPagesArray = oldPagesArray?.pages.map((page) => page.filter((val) => val.id !== updatedId) ) ?? [] -queryClient.setQueryData('projects', data => ({ +queryClient.setQueryData(['projects'], data => ({ pages: newPagesArray, pageParams: data.pageParams, })) diff --git a/docs/src/pages/guides/initial-query-data.md b/docs/src/pages/guides/initial-query-data.md index 5a3e9ef454..a634756112 100644 --- a/docs/src/pages/guides/initial-query-data.md +++ b/docs/src/pages/guides/initial-query-data.md @@ -19,7 +19,7 @@ There may be times when you already have the initial data for a query available ```js function Todos() { - const result = useQuery('todos', () => fetch('/todos'), { + const result = useQuery(['todos'], () => fetch('/todos'), { initialData: initialTodos, }) } @@ -34,7 +34,7 @@ By default, `initialData` is treated as totally fresh, as if it were just fetche ```js function Todos() { // Will show initialTodos immediately, but also immediately refetch todos after mount - const result = useQuery('todos', () => fetch('/todos'), { + const result = useQuery(['todos'], () => fetch('/todos'), { initialData: initialTodos, }) } @@ -45,7 +45,7 @@ By default, `initialData` is treated as totally fresh, as if it were just fetche ```js function Todos() { // Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms - const result = useQuery('todos', () => fetch('/todos'), { + const result = useQuery(['todos'], () => fetch('/todos'), { initialData: initialTodos, staleTime: 1000, }) @@ -56,7 +56,7 @@ By default, `initialData` is treated as totally fresh, as if it were just fetche ```js function Todos() { // Show initialTodos immediately, but won't refetch until another interaction event is encountered after 1000 ms - const result = useQuery('todos', () => fetch('/todos'), { + const result = useQuery(['todos'], () => fetch('/todos'), { initialData: initialTodos, staleTime: 60 * 1000 // 1 minute // This could be 10 seconds ago or 10 minutes ago @@ -74,7 +74,7 @@ If the process for accessing a query's initial data is intensive or just not som ```js function Todos() { - const result = useQuery('todos', () => fetch('/todos'), { + const result = useQuery(['todos'], () => fetch('/todos'), { initialData: () => { return getExpensiveTodos() }, @@ -91,7 +91,7 @@ function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch('/todos'), { initialData: () => { // Use a todo from the 'todos' query as the initial data for this todo query - return queryClient.getQueryData('todos')?.find(d => d.id === todoId) + return queryClient.getQueryData(['todos'])?.find(d => d.id === todoId) }, }) } @@ -105,9 +105,9 @@ Getting initial data from the cache means the source query you're using to look function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch(`/todos/${todoId}`), { initialData: () => - queryClient.getQueryData('todos')?.find(d => d.id === todoId), + queryClient.getQueryData(['todos'])?.find(d => d.id === todoId), initialDataUpdatedAt: () => - queryClient.getQueryState('todos')?.dataUpdatedAt, + queryClient.getQueryState(['todos'])?.dataUpdatedAt, }) } ``` @@ -121,7 +121,7 @@ function Todo({ todoId }) { const result = useQuery(['todo', todoId], () => fetch(`/todos/${todoId}`), { initialData: () => { // Get the query state - const state = queryClient.getQueryState('todos') + const state = queryClient.getQueryState(['todos']) // If the query exists and has data that is no older than 10 seconds... if (state && Date.now() - state.dataUpdatedAt <= 10 * 1000) { diff --git a/docs/src/pages/guides/invalidations-from-mutations.md b/docs/src/pages/guides/invalidations-from-mutations.md index a8000d216a..2aa38f48fb 100644 --- a/docs/src/pages/guides/invalidations-from-mutations.md +++ b/docs/src/pages/guides/invalidations-from-mutations.md @@ -21,8 +21,8 @@ const queryClient = useQueryClient() // When this mutation succeeds, invalidate any queries with the `todos` or `reminders` query key const mutation = useMutation(addTodo, { onSuccess: () => { - queryClient.invalidateQueries('todos') - queryClient.invalidateQueries('reminders') + queryClient.invalidateQueries(['todos']) + queryClient.invalidateQueries(['reminders']) }, }) ``` diff --git a/docs/src/pages/guides/migrating-to-react-query-3.md b/docs/src/pages/guides/migrating-to-react-query-3.md index 12aba57b35..31ce396ca3 100644 --- a/docs/src/pages/guides/migrating-to-react-query-3.md +++ b/docs/src/pages/guides/migrating-to-react-query-3.md @@ -244,7 +244,7 @@ const { This allows for easier manipulation of the data and the page params, like, for example, removing the first page of data along with it's params: ```js -queryClient.setQueryData('projects', data => ({ +queryClient.setQueryData(['projects'], data => ({ pages: data.pages.slice(1), pageParams: data.pageParams.slice(1), })) @@ -358,7 +358,7 @@ Only re-render when the `data` or `error` properties change: import { useQuery } from 'react-query' function User() { - const { data } = useQuery('user', fetchUser, { + const { data } = useQuery(['user'], fetchUser, { notifyOnChangeProps: ['data', 'error'], }) return
Username: {data.username}
@@ -371,7 +371,7 @@ Prevent re-render when the `isStale` property changes: import { useQuery } from 'react-query' function User() { - const { data } = useQuery('user', fetchUser, { + const { data } = useQuery(['user'], fetchUser, { notifyOnChangePropsExclusions: ['isStale'], }) return
Username: {data.username}
@@ -459,7 +459,7 @@ The `useQuery` and `useInfiniteQuery` hooks now have a `select` option to select import { useQuery } from 'react-query' function User() { - const { data } = useQuery('user', fetchUser, { + const { data } = useQuery(['user'], fetchUser, { select: user => user.username, }) return
Username: {data}
@@ -556,10 +556,10 @@ const unsubscribe = observer.subscribe(result => { The `QueryClient.setQueryDefaults()` method can be used to set default options for specific queries: ```js -queryClient.setQueryDefaults('posts', { queryFn: fetchPosts }) +queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts }) function Component() { - const { data } = useQuery('posts') + const { data } = useQuery(['posts']) } ``` @@ -568,10 +568,10 @@ function Component() { The `QueryClient.setMutationDefaults()` method can be used to set default options for specific mutations: ```js -queryClient.setMutationDefaults('addPost', { mutationFn: addPost }) +queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost }) function Component() { - const { mutate } = useMutation('addPost') + const { mutate } = useMutation(['addPost']) } ``` diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md new file mode 100644 index 0000000000..14002bec89 --- /dev/null +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -0,0 +1,255 @@ +--- +id: migrating-to-react-query-4 +title: Migrating to React Query 4 +--- + +## Breaking Changes + +### Query Keys (and Mutation Keys) need to be an Array + +In v3, Query and Mutation Keys could be a String or an Array. Internally, React Query has always worked with Array Keys only, and we've sometimes exposed this to consumers. For example, in the `queryFn`, you would always get the key as an Array to make working with [Default Query Functions](./default-query-function) easier. + +However, we have not followed this concept through to all apis. For example, when using the `predicate` function on [Query Filters](./filters) you would get the raw Query Key. This makes it difficult to work with such functions if you use Query Keys that are mixed Arrays and Strings. The same was true when using global callbacks. + +To streamline all apis, we've decided to make all keys Arrays only: + +```diff +- useQuery('todos', fetchTodos) ++ useQuery(['todos'], fetchTodos) +``` + +### Separate hydration exports have been removed + +With version [3.22.0](https://github.com/tannerlinsley/react-query/releases/tag/v3.22.0), hydration utilities moved into the React Query core. With v3, you could still use the old exports from `react-query/hydration`, but these exports have been removed with v4. + +```diff +- import { dehydrate, hydrate, useHydrate, Hydrate } from 'react-query/hydration' ++ import { dehydrate, hydrate, useHydrate, Hydrate } from 'react-query' +``` + +### `notifyOnChangeProps` property no longer accepts `"tracked"` as a value + +The `notifyOnChangeProps` option no longer accepts a `"tracked"` value. Instead, `useQuery` defaults to tracking properties. All queries using `notifyOnChangeProps: "tracked"` should be updated by removing this option. + +If you would like to bypass this in any queries to emulate the v3 default behavior of re-rendering whenever a query changes, `notifyOnChangeProps` now accepts an `"all"` value to opt-out of the default smart tracking optimization. + +### `notifyOnChangePropsExclusion` has been removed + +In v4, `notifyOnChangeProps` defaults to the `"tracked"` behavior of v3 instead of `undefined`. Now that `"tracked"` is the default behavior for v4, it no longer makes sense to include this config option. + +### Consistent behavior for `cancelRefetch` + +The `cancelRefetch` can be passed to all functions that imperatively fetch a query, namely: + +- `queryClient.refetchQueries` + - `queryClient.invalidateQueries` + - `queryClient.resetQueries` +- `refetch` returned from `useQuery` +- `fetchNetPage` and `fetchPreviousPage` returned from `useInfiniteQuery` + +Except for `fetchNetxPage` and `fetchPreviousPage`, this flag was defaulting to `false`, which was inconsistent and potentially troublesome: Calling `refetchQueries` or `invalidateQueries` after a mutation might not yield the latest result if a previous slow fetch was already ongoing, because this refetch would have been skipped. + +We believe that if a query is actively refetched by some code you write, it should, per default, re-start the fetch. + +That is why this flag now defaults to _true_ for all methods mentioned above. It also means that if you call `refetchQueries` twice in a row, without awaiting it, it will now cancel the first fetch and re-start it with the second one: + +``` +queryClient.refetchQueries({ queryKey: ['todos'] }) +// this will abort the previous refetch and start a new fetch +queryClient.refetchQueries({ queryKey: ['todos'] }) +``` + +You can opt-out of this behaviour by explicitly passing `cancelRefetch:false`: + +``` +queryClient.refetchQueries({ queryKey: ['todos'] }) +// this will not abort the previous refetch - it will just be ignored +queryClient.refetchQueries({ queryKey: ['todos'] }, { cancelRefetch: false }) +``` + +> Note: There is no change in behaviour for automatically triggered fetches, e.g. because a query mounts or because of a window focus refetch. + +### Query Filters + +A [query filter](../guides/filters) is an object with certain conditions to match a query. Historically, the filter options have mostly been a combination of boolean flags. However, combining those flags can lead to impossible states. Specifically: + +``` +active?: boolean + - When set to true it will match active queries. + - When set to false it will match inactive queries. +inactive?: boolean + - When set to true it will match inactive queries. + - When set to false it will match active queries. +``` + +Those flags don't work well when used together, because they are mutually exclusive. Setting `false` for both flags could match all queries, judging from the description, or no queries, which doesn't make much sense. + +With v4, those filters have been combined into a single filter to better show the intent: + +```diff +- active?: boolean +- inactive?: boolean ++ type?: 'active' | 'inactive' | 'all' +``` + +The filter defaults to `all`, and you can choose to only match `active` or `inactive` queries. + +#### refetchActive / refetchInactive + +[queryClient.invalidateQueries](../reference/QueryClient#queryclientinvalidatequeries) had two additional, similar flags: + +``` +refetchActive: Boolean + - Defaults to true + - When set to false, queries that match the refetch predicate and are actively being rendered via useQuery and friends will NOT be refetched in the background, and only marked as invalid. +refetchInactive: Boolean + - Defaults to false + - When set to true, queries that match the refetch predicate and are not being rendered via useQuery and friends will be both marked as invalid and also refetched in the background +``` + +For the same reason, those have also been combined: + +```diff +- active?: boolean +- inactive?: boolean ++ refetchType?: 'active' | 'inactive' | 'all' | 'none' +``` + +This flag defaults to `active` because `refetchActive` defaulted to `true`. This means we also need a way to tell `invalidateQueries` to not refetch at all, which is why a fourth option (`none`) is also allowed here. + +### Streamlined NotifyEvents + +Subscribing manually to the `QueryCache` has always given you a `QueryCacheNotifyEvent`, but this was not true for the `MutationCache`. We have streamlined the behavior and also adapted event names accordingly. + +#### QueryCacheNotifyEvent + +```diff +- type: 'queryAdded' ++ type: 'added' +- type: 'queryRemoved' ++ type: 'removed' +- type: 'queryUpdated' ++ type: 'updated' +``` + +#### MutationCacheNotifyEvent + +The `MutationCacheNotifyEvent` uses the same types as the `QueryCacheNotifyEvent`. + +> Note: This is only relevant if you manually subscribe to the caches via `queryCache.subscribe` or `mutationCache.subscribe` + +### The `src/react` directory was renamed to `src/reactjs` + +Previously, React Query had a directory named `react` which imported from the `react` module. This could cause problems with some Jest configurations, resulting in errors when running tests like: + +``` +TypeError: Cannot read property 'createContext' of undefined +``` + +With the renamed directory this no longer is an issue. + +If you were importing anything from `'react-query/react'` directly in your project (as opposed to just `'react-query'`), then you need to update your imports: + +```diff +- import { QueryClientProvider } from 'react-query/react'; ++ import { QueryClientProvider } from 'react-query/reactjs'; +``` + +### `onSuccess` is no longer called from `setQueryData` + +This was confusing to many and also created infinite loops if `setQueryData` was called from within `onSuccess`. It was also a frequent source of error when combined with `staleTime`, because if data was read from the cache only, `onSuccess` was _not_ called. + +Similar to `onError` and `onSettled`, the `onSuccess` callback is now tied to a request being made. No request -> no callback. + +If you want to listen to changes of the `data` field, you can best do this with a `useEffect`, where `data` is part of the dependency Array. Since React Query ensures stable data through structural sharing, the effect will not execute with every background refetch, but only if something within data has changed: + +``` +const { data } = useQuery({ queryKey, queryFn }) +React.useEffect(() => mySideEffectHere(data), [data]) +``` + +### `persistQueryClient` and the corresponding persister plugins are no longer experimental and have been renamed + +The plugins `createWebStoragePersistor` and `createAsyncStoragePersistor` have been renamed to [`createWebStoragePersister`](/plugins/createWebStoragePersister) and [`createAsyncStoragePersister`](/plugins/createAsyncStoragePersister) respectively. The interface `Persistor` in `persistQueryClient` has also been renamed to `Persister`. Checkout [this stackexchange](https://english.stackexchange.com/questions/206893/persister-or-persistor) for the motivation of this change. + +Since these plugins are no longer experimental, their import paths have also been updated: + +```diff +- import { persistQueryClient } from 'react-query/persistQueryClient-experimental' +- import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental' +- import { createAsyncStoragePersistor } from 'react-query/createAsyncStoragePersistor-experimental' + ++ import { persistQueryClient } from 'react-query/persistQueryClient' ++ import { createWebStoragePersister } from 'react-query/createWebStoragePersister' ++ import { createAsyncStoragePersister } from 'react-query/createAsyncStoragePersister' +``` + +### The `cancel` method on promises is no longer supported + +The [old `cancel` method](../guides/query-cancellation#old-cancel-function) that allowed you to define a `cancel` function on promises, which was then used by the library to support query cancellation, has been removed. We recommend to use the [newer API](../guides/query-cancellation) (introduced with v3.30.0) for query cancellation that uses the [`AbortController` API](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) internally and provides you with an [`AbortSignal` instance](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) for your query function to support query cancellation. + +### Queries and mutations, per default, need network connection to run + +Please read the [New Features announcement](#proper-offline-support) about online / offline support, and also the dedicated page about [Network mode](../guides/network-mode) + +Even though React Query is an Async State Manager that can be used for anything that produces a Promise, it is most often used for data fetching in combination with data fetching libraries. That is why, per default, queries and mutations will be `paused` if there is no network connection. If you want to opt-in to the previous behavior, you can globally set `networkMode: offlineFirst` for both queries and mutations: + +```js +new QueryClient({ + defaultOptions: { + queries: { + networkMode: 'offlineFirst' + }, + mutations: { + networkmode: 'offlineFirst' + } + } +}) +``` + +### Removed undocumented methods from the `queryClient` + +The methods `cancelMutatations` and `executeMutation` were undocumented and unused internally, so we removed them. Since they were just wrappers around methods available on the `mutationCache`, you can still use the functionality. + +```diff +- cancelMutations(): Promise { +- const promises = notifyManager.batch(() => +- this.mutationCache.getAll().map(mutation => mutation.cancel()) +- ) +- return Promise.all(promises).then(noop).catch(noop) +- } +``` + +```diff +- executeMutation< +- TData = unknown, +- TError = unknown, +- TVariables = void, +- TContext = unknown +- >( +- options: MutationOptions +- ): Promise { +- return this.mutationCache.build(this, options).execute() +- } +``` + +## New Features 🚀 + +### Proper offline support + +In v3, React Query has always fired off queries and mutations, but then taken the assumption that if you want to retry it, you need to be connected to the internet. This has led to several confusing situations: + +- You are offline and mount a query - it goes to loading state, the request fails, and it stays in loading state until you go online again, even though it is not really fetching. +- Similarly, if you are offline and have retries turned off, your query will just fire and fail, and the query goes to error state. +- You are offline and want to fire off a query that doesn't necessarily need network connection (because you _can_ use React Query for something other than data fetching), but it fails for some other reason. That query will now be paused until you go online again. +- Window focus refetching didn't do anything at all if you were offline. + +With v4, React Query introduces a new `networkMode` to tackle all these issues. Please read the dedicated page about the new [Network mode](../guides/network-mode) for more information. + +### Mutation Cache Garbage Collection + +Mutations can now also be garbage collected automatically, just like queries. The default `cacheTime` for mutations is also set to 5 minutes. + +### Tracked Queries per default + +React Query defaults to "tracking" query properties, which should give you a nice boost in render optimization. The feature has existed since [v3.6.0](https://github.com/tannerlinsley/react-query/releases/tag/v3.6.0) and has now become the default behavior with v4. diff --git a/docs/src/pages/guides/mutations.md b/docs/src/pages/guides/mutations.md index 379d7351ef..f592e0dc17 100644 --- a/docs/src/pages/guides/mutations.md +++ b/docs/src/pages/guides/mutations.md @@ -216,34 +216,34 @@ Mutations can be persisted to storage if needed and resumed at a later point. Th const queryClient = new QueryClient() // Define the "addTodo" mutation -queryClient.setMutationDefaults('addTodo', { +queryClient.setMutationDefaults(['addTodo'], { mutationFn: addTodo, onMutate: async (variables) => { // Cancel current queries for the todos list - await queryClient.cancelQueries('todos') + await queryClient.cancelQueries(['todos']) // Create optimistic todo const optimisticTodo = { id: uuid(), title: variables.title } // Add optimistic todo to todos list - queryClient.setQueryData('todos', old => [...old, optimisticTodo]) + queryClient.setQueryData(['todos'], old => [...old, optimisticTodo]) // Return context with the optimistic todo return { optimisticTodo } }, onSuccess: (result, variables, context) => { // Replace optimistic todo in the todos list with the result - queryClient.setQueryData('todos', old => old.map(todo => todo.id === context.optimisticTodo.id ? result : todo)) + queryClient.setQueryData(['todos'], old => old.map(todo => todo.id === context.optimisticTodo.id ? result : todo)) }, onError: (error, variables, context) => { // Remove optimistic todo from the todos list - queryClient.setQueryData('todos', old => old.filter(todo => todo.id !== context.optimisticTodo.id)) + queryClient.setQueryData(['todos'], old => old.filter(todo => todo.id !== context.optimisticTodo.id)) }, retry: 3, }) // Start mutation in some component: -const mutation = useMutation('addTodo') +const mutation = useMutation(['addTodo']) mutation.mutate({ title: 'title' }) // If the mutation has been paused because the device is for example offline, diff --git a/docs/src/pages/guides/network-mode.md b/docs/src/pages/guides/network-mode.md new file mode 100644 index 0000000000..3a977ff2d2 --- /dev/null +++ b/docs/src/pages/guides/network-mode.md @@ -0,0 +1,46 @@ +--- +id: network-mode +title: Network Mode +--- + +React Query provides three different network modes to distinguish how [Queries](./queries) and [Mutations](./mutations) should behave if you have no network connection. This mode can be set for each Query / Mutation individually, or globally via the query / mutation defaults. + +Since React Query is most often used for data fetching in combination with data fetching libraries, the default network mode is [online](#network-mode-online). + +## Network Mode: online + +In this mode, Queries and Mutations will not fire unless you have network connection. This is the default mode. If a fetch is initiated for a query, it will always stay in the `state` (`loading`, `idle`, `error`, `success`) it is in if the fetch cannot be made because there is no network connection. However, a `fetchStatus` is exposed additionally. This can be either: + +- `fetching`: The `queryFn` is really executing - a request is in-flight. +- `paused`: The query is not executing - it is `paused` until you have connection again +- `idle`: The query is not fetching and not paused + +The flags `isFetching` and `isPaused` are derived from this state and exposed for convenience. + +> Keep in mind that it might not be enough to check for `loading` state to show a loading spinner. Queries can be in `state: 'loading'`, but `fetchStatus: 'paused'` if they are mounting for the first time, and you have no network connection. + +If a query runs because you are online, but you go offline while the fetch is still happening, React Query will also pause the retry mechanism. Paused queries will then continue to run once you re-gain network connection. This is independent of `refetchOnReconnect` (which also defaults to `true` in this mode), because it is not a `refetch`, but rather a `continue`. If the query has been [cancelled](./query-cancellation) in the meantime, it will not continue. + +## Network Mode: always + +In this mode, React Query will always fetch and ignore the online / offline state. This is likely the mode you want to choose if you use React Query in an environment where you don't need an active network connection for your Queries to work - e.g. if you just read from `AsyncStorage`, or if you just want to return `Promise.resolve(5)` from your `queryFn`. + +- Queries will never be `paused` because you have no network connection. +- Retries will also not pause - your Query will go to `error` state if it fails. +- `refetchOnReconnect` defaults to `false` in this mode, because reconnecting to the network is not a good indicator anymore that stale queries should be refetched. You can still turn it on if you want. + +## Network Mode: offlineFirst + +This mode is the middle ground between the first two options, where React Query will run the `queryFn` once, but then pause retries. This is very handy if you have a serviceWorker that intercepts a request for caching like in an [offline-first PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers), or if you use HTTP caching via the [Cache-Control header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#the_cache-control_header). + +In those situations, the first fetch might succeed because it comes from an offline storage / cache. However, if there is a cache miss, the network request will go out and fail, in which case this mode behaves like an `online` query - pausing retries. + +## Devtools + +The [React Query Devtools](../devtools) will show Queries in a `paused` state if they would be fetching, but there is no network connection. There is also a toggle button to _Mock offline behavior_. Please note that this button will _not_ actually mess with your network connection (you can do that in the browser devtools), but it will set the [OnlineManager](../reference/onlineManager) in an offline state. + +## Signature + +- `networkMode: 'online' | 'always' | 'offlineFirst` + - optional + - defaults to `'online'` diff --git a/docs/src/pages/guides/optimistic-updates.md b/docs/src/pages/guides/optimistic-updates.md index b4bd3b4080..31518b3434 100644 --- a/docs/src/pages/guides/optimistic-updates.md +++ b/docs/src/pages/guides/optimistic-updates.md @@ -16,24 +16,24 @@ useMutation(updateTodo, { // When mutate is called: onMutate: async newTodo => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) - await queryClient.cancelQueries('todos') + await queryClient.cancelQueries(['todos']) // Snapshot the previous value - const previousTodos = queryClient.getQueryData('todos') + const previousTodos = queryClient.getQueryData(['todos']) // Optimistically update to the new value - queryClient.setQueryData('todos', old => [...old, newTodo]) + queryClient.setQueryData(['todos'], old => [...old, newTodo]) // Return a context object with the snapshotted value return { previousTodos } }, // If the mutation fails, use the context returned from onMutate to roll back onError: (err, newTodo, context) => { - queryClient.setQueryData('todos', context.previousTodos) + queryClient.setQueryData(['todos'], context.previousTodos) }, // Always refetch after error or success: onSettled: () => { - queryClient.invalidateQueries('todos') + queryClient.invalidateQueries(['todos']) }, }) ``` diff --git a/docs/src/pages/guides/parallel-queries.md b/docs/src/pages/guides/parallel-queries.md index 22926ee6e0..79dac5ce82 100644 --- a/docs/src/pages/guides/parallel-queries.md +++ b/docs/src/pages/guides/parallel-queries.md @@ -12,9 +12,9 @@ When the number of parallel queries does not change, there is **no extra effort* ```js function App () { // The following queries will execute in parallel - const usersQuery = useQuery('users', fetchUsers) - const teamsQuery = useQuery('teams', fetchTeams) - const projectsQuery = useQuery('projects', fetchProjects) + const usersQuery = useQuery(['users'], fetchUsers) + const teamsQuery = useQuery(['teams'], fetchTeams) + const projectsQuery = useQuery(['projects'], fetchProjects) ... } ``` diff --git a/docs/src/pages/guides/placeholder-query-data.md b/docs/src/pages/guides/placeholder-query-data.md index e9c2d24379..678197fa64 100644 --- a/docs/src/pages/guides/placeholder-query-data.md +++ b/docs/src/pages/guides/placeholder-query-data.md @@ -20,7 +20,7 @@ There are a few ways to supply placeholder data for a query to the cache before ```js function Todos() { - const result = useQuery('todos', () => fetch('/todos'), { + const result = useQuery(['todos'], () => fetch('/todos'), { placeholderData: placeholderTodos, }) } @@ -33,7 +33,7 @@ If the process for accessing a query's placeholder data is intensive or just not ```js function Todos() { const placeholderData = useMemo(() => generateFakeTodos(), []) - const result = useQuery('todos', () => fetch('/todos'), { placeholderData }) + const result = useQuery(['todos'], () => fetch('/todos'), { placeholderData }) } ``` @@ -47,7 +47,7 @@ function Todo({ blogPostId }) { placeholderData: () => { // Use the smaller/preview version of the blogPost from the 'blogPosts' query as the placeholder data for this blogPost query return queryClient - .getQueryData('blogPosts') + .getQueryData(['blogPosts']) ?.find(d => d.id === blogPostId) }, }) diff --git a/docs/src/pages/guides/prefetching.md b/docs/src/pages/guides/prefetching.md index 72ffdcd5e8..d1f04858cd 100644 --- a/docs/src/pages/guides/prefetching.md +++ b/docs/src/pages/guides/prefetching.md @@ -8,7 +8,7 @@ If you're lucky enough, you may know enough about what your users will do to be ```js const prefetchTodos = async () => { // The results of this query will be cached like a normal query - await queryClient.prefetchQuery('todos', fetchTodos) + await queryClient.prefetchQuery(['todos'], fetchTodos) } ``` @@ -21,5 +21,5 @@ const prefetchTodos = async () => { Alternatively, if you already have the data for your query synchronously available, you don't need to prefetch it. You can just use the [Query Client's `setQueryData` method](../reference/QueryClient#queryclientsetquerydata) to directly add or update a query's cached result by key. ```js -queryClient.setQueryData('todos', todos) +queryClient.setQueryData(['todos'], todos) ``` diff --git a/docs/src/pages/guides/queries.md b/docs/src/pages/guides/queries.md index 63c7b5eb7d..af83df3ba3 100644 --- a/docs/src/pages/guides/queries.md +++ b/docs/src/pages/guides/queries.md @@ -18,7 +18,7 @@ To subscribe to a query in your components or custom hooks, call the `useQuery` import { useQuery } from 'react-query' function App() { - const info = useQuery('todos', fetchTodoList) + const info = useQuery(['todos'], fetchTodoList) } ``` @@ -27,7 +27,7 @@ The **unique key** you provide is used internally for refetching, caching, and s The query results returned by `useQuery` contains all of the information about the query that you'll need for templating and any other usage of the data: ```js -const result = useQuery('todos', fetchTodoList) +const result = useQuery(['todos'], fetchTodoList) ``` The `result` object contains a few very important states you'll need to be aware of to be productive. A query can only be in one of the following states at any given moment: @@ -47,7 +47,7 @@ For **most** queries, it's usually sufficient to check for the `isLoading` state ```js function Todos() { - const { isLoading, isError, data, error } = useQuery('todos', fetchTodoList) + const { isLoading, isError, data, error } = useQuery(['todos'], fetchTodoList) if (isLoading) { return Loading... @@ -72,7 +72,7 @@ If booleans aren't your thing, you can always use the `status` state as well: ```js function Todos() { - const { status, data, error } = useQuery('todos', fetchTodoList) + const { status, data, error } = useQuery(['todos'], fetchTodoList) if (status === 'loading') { return Loading... diff --git a/docs/src/pages/guides/query-cancellation.md b/docs/src/pages/guides/query-cancellation.md index 2c00fe1625..0d36fced6e 100644 --- a/docs/src/pages/guides/query-cancellation.md +++ b/docs/src/pages/guides/query-cancellation.md @@ -20,7 +20,7 @@ However, if you consume the `AbortSignal` or attach a `cancel` function to your ## Using `fetch` ```js -const query = useQuery('todos', async ({ signal }) => { +const query = useQuery(['todos'], async ({ signal }) => { const todosResponse = await fetch('/todos', { // Pass the signal to one fetch signal, @@ -46,7 +46,7 @@ const query = useQuery('todos', async ({ signal }) => { ```js import axios from 'axios' -const query = useQuery('todos', ({ signal }) => +const query = useQuery(['todos'], ({ signal }) => axios.get('/todos', { // Pass the signal to `axios` signal, @@ -59,7 +59,7 @@ const query = useQuery('todos', ({ signal }) => ```js import axios from 'axios' -const query = useQuery('todos', ({ signal }) => { +const query = useQuery(['todos'], ({ signal }) => { // Create a new CancelToken source for this request const CancelToken = axios.CancelToken const source = CancelToken.source() @@ -81,7 +81,7 @@ const query = useQuery('todos', ({ signal }) => { ## Using `XMLHttpRequest` ```js -const query = useQuery('todos', ({ signal }) => { +const query = useQuery(['todos'], ({ signal }) => { return new Promise((resolve, reject) => { var oReq = new XMLHttpRequest() oReq.addEventListener('load', () => { @@ -102,7 +102,7 @@ const query = useQuery('todos', ({ signal }) => { An `AbortSignal` can be set in the `GraphQLClient` constructor. ```js -const query = useQuery('todos', ({ signal }) => { +const query = useQuery(['todos'], ({ signal }) => { const client = new GraphQLClient(endpoint, { signal, }); @@ -115,9 +115,7 @@ const query = useQuery('todos', ({ signal }) => { You might want to cancel a query manually. For example, if the request takes a long time to finish, you can allow the user to click a cancel button to stop the request. To do this, you just need to call `queryClient.cancelQueries(key)`, which will cancel the query and revert it back to its previous state. If `promise.cancel` is available, or you have consumed the `signal` passed to the query function, React Query will additionally also cancel the Promise. ```js -const [queryKey] = useState('todos') - -const query = useQuery(queryKey, await ({ signal }) => { +const query = useQuery(['todos'], await ({ signal }) => { const resp = fetch('/todos', { signal }) return resp.json() }) @@ -127,7 +125,7 @@ const queryClient = useQueryClient() return ( ) ``` @@ -143,7 +141,7 @@ To integrate with this feature, attach a `cancel` function to the promise return ```js import axios from 'axios' -const query = useQuery('todos', () => { +const query = useQuery(['todos'], () => { // Create a new CancelToken source for this request const CancelToken = axios.CancelToken const source = CancelToken.source() @@ -165,7 +163,7 @@ const query = useQuery('todos', () => { ## Using `fetch` with `cancel` function ```js -const query = useQuery('todos', () => { +const query = useQuery(['todos'], () => { // Create a new AbortController instance for this request const controller = new AbortController() // Get the abortController's signal diff --git a/docs/src/pages/guides/query-functions.md b/docs/src/pages/guides/query-functions.md index e31eb25489..a12d84e0b7 100644 --- a/docs/src/pages/guides/query-functions.md +++ b/docs/src/pages/guides/query-functions.md @@ -47,7 +47,7 @@ useQuery(['todos', todoId], async () => { ## Query Function Variables -Query keys are not just for uniquely identifying the data you are fetching, but are also conveniently passed into your query function and while not always necessary, this makes it possible to extract your query functions if needed: +Query keys are not just for uniquely identifying the data you are fetching, but are also conveniently passed into your query function as part of the QueryFunctionContext. While not always necessary, this makes it possible to extract your query functions if needed: ```js function Todos({ status, page }) { @@ -61,6 +61,20 @@ function fetchTodoList({ queryKey }) { } ``` +### QueryFunctionContext + +The `QueryFunctionContext` is the object passed to each query function. It consists of: + +- `queryKey: QueryKey`: [Query Keys](./query-keys) +- `pageParam: unknown | undefined` + - only for [Infinite Queries](./infinite-queries.md) + - the page parameter used to fetch the current page +- signal?: AbortSignal + - [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) instance provided by react-query + - Can be used for [Query Cancellation](./query-cancellation.md) +- `meta?: Record` + - an optional field you can fill with additional information about your query + ## Using a Query Object instead of parameters Anywhere the `[queryKey, queryFn, config]` signature is supported throughout React Query's API, you can also use an object to express the same configuration: diff --git a/docs/src/pages/guides/query-invalidation.md b/docs/src/pages/guides/query-invalidation.md index 2f6b2b6789..17f0d486a1 100644 --- a/docs/src/pages/guides/query-invalidation.md +++ b/docs/src/pages/guides/query-invalidation.md @@ -9,7 +9,7 @@ Waiting for queries to become stale before they are fetched again doesn't always // Invalidate every query in the cache queryClient.invalidateQueries() // Invalidate every query with a key that starts with `todos` -queryClient.invalidateQueries('todos') +queryClient.invalidateQueries(['todos']) ``` > Note: Where other libraries that use normalized caches would attempt to update local queries with the new data either imperatively or via schema inference, React Query gives you the tools to avoid the manual labor that comes with maintaining normalized caches and instead prescribes **targeted invalidation, background-refetching and ultimately atomic updates**. @@ -31,10 +31,10 @@ import { useQuery, useQueryClient } from 'react-query' // Get QueryClient from the context const queryClient = useQueryClient() -queryClient.invalidateQueries('todos') +queryClient.invalidateQueries(['todos']) // Both queries below will be invalidated -const todoListQuery = useQuery('todos', fetchTodoList) +const todoListQuery = useQuery(['todos'], fetchTodoList) const todoListQuery = useQuery(['todos', { page: 1 }], fetchTodoList) ``` @@ -47,13 +47,13 @@ queryClient.invalidateQueries(['todos', { type: 'done' }]) const todoListQuery = useQuery(['todos', { type: 'done' }], fetchTodoList) // However, the following query below will NOT be invalidated -const todoListQuery = useQuery('todos', fetchTodoList) +const todoListQuery = useQuery(['todos'], fetchTodoList) ``` The `invalidateQueries` API is very flexible, so even if you want to **only** invalidate `todos` queries that don't have any more variables or subkeys, you can pass an `exact: true` option to the `invalidateQueries` method: ```js -queryClient.invalidateQueries('todos', { exact: true }) +queryClient.invalidateQueries(['todos'], { exact: true }) // The query below will be invalidated const todoListQuery = useQuery(['todos'], fetchTodoList) diff --git a/docs/src/pages/guides/query-keys.md b/docs/src/pages/guides/query-keys.md index f9239ea91a..fab84b4c2c 100644 --- a/docs/src/pages/guides/query-keys.md +++ b/docs/src/pages/guides/query-keys.md @@ -3,24 +3,24 @@ id: query-keys title: Query Keys --- -At its core, React Query manages query caching for you based on query keys. Query keys can be as simple as a string, or as complex as an array of many strings and nested objects. As long as the query key is serializable, and **unique to the query's data**, you can use it! +At its core, React Query manages query caching for you based on query keys. Query keys have to be an Array at the top level, and can be as simple as an Array with a single string, or as complex as an array of many strings and nested objects. As long as the query key is serializable, and **unique to the query's data**, you can use it! -## String-Only Query Keys +## Simple Query Keys -The simplest form of a key is actually not an array, but an individual string. When a string query key is passed, it is converted to an array internally with the string as the only item in the query key. This format is useful for: +The simplest form of a key is an array with constants values. This format is useful for: - Generic List/Index resources - Non-hierarchical resources ```js // A list of todos -useQuery('todos', ...) // queryKey === ['todos'] +useQuery(['todos'], ...) // Something else, whatever! -useQuery('somethingSpecial', ...) // queryKey === ['somethingSpecial'] +useQuery(['something', 'special'], ...) ``` -## Array Keys +## Array Keys with variables When a query needs more information to uniquely describe its data, you can use an array with a string and any number of serializable objects to describe it. This is useful for: @@ -32,15 +32,12 @@ When a query needs more information to uniquely describe its data, you can use a ```js // An individual todo useQuery(['todo', 5], ...) -// queryKey === ['todo', 5] // An individual todo in a "preview" format useQuery(['todo', 5, { preview: true }], ...) -// queryKey === ['todo', 5, { preview: true }] // A list of todos that are "done" useQuery(['todos', { type: 'done' }], ...) -// queryKey === ['todos', { type: 'done' }] ``` ## Query Keys are hashed deterministically! diff --git a/docs/src/pages/guides/query-retries.md b/docs/src/pages/guides/query-retries.md index 8b87e5b1f5..df44db6920 100644 --- a/docs/src/pages/guides/query-retries.md +++ b/docs/src/pages/guides/query-retries.md @@ -47,7 +47,7 @@ function App() { Though it is not recommended, you can obviously override the `retryDelay` function/integer in both the Provider and individual query options. If set to an integer instead of a function the delay will always be the same amount of time: ```js -const result = useQuery('todos', fetchTodoList, { +const result = useQuery(['todos'], fetchTodoList, { retryDelay: 1000, // Will always wait 1000ms to retry, regardless of how many retries }) ``` diff --git a/docs/src/pages/guides/ssr.md b/docs/src/pages/guides/ssr.md index 623b60161e..675551927f 100644 --- a/docs/src/pages/guides/ssr.md +++ b/docs/src/pages/guides/ssr.md @@ -31,7 +31,7 @@ export async function getStaticProps() { } function Posts(props) { - const { data } = useQuery('posts', getPosts, { initialData: props.posts }) + const { data } = useQuery(['posts'], getPosts, { initialData: props.posts }) // ... } @@ -95,11 +95,11 @@ export async function getStaticProps() { function Posts() { // This useQuery could just as well happen in some deeper child to // the "Posts"-page, data will be available immediately either way - const { data } = useQuery('posts', getPosts) + const { data } = useQuery(['posts'], getPosts) // This query was not prefetched on the server and will not start // fetching until on the client, both patterns are fine to mix - const { data: otherData } = useQuery('posts-2', getPosts) + const { data: otherData } = useQuery(['posts-2'], getPosts) // ... } @@ -126,9 +126,9 @@ This guide is at-best, a high level overview of how SSR with React Query should ```js import { dehydrate, Hydrate, QueryClient, QueryClientProvider } from 'react-query'; -function handleRequest (req, res) { +async function handleRequest (req, res) { const queryClient = new QueryClient() - await queryClient.prefetchQuery('key', fn) + await queryClient.prefetchQuery(['key'], fn) const dehydratedState = dehydrate(queryClient) const html = ReactDOM.renderToString( diff --git a/docs/src/pages/guides/testing.md b/docs/src/pages/guides/testing.md index 892fadf5b4..bb7b352484 100644 --- a/docs/src/pages/guides/testing.md +++ b/docs/src/pages/guides/testing.md @@ -21,7 +21,7 @@ Once installed, a simple test can be written. Given the following custom hook: ``` export function useCustomHook() { - return useQuery('customHook', () => 'Hello'); + return useQuery(['customHook'], () => 'Hello'); } ``` @@ -99,7 +99,7 @@ Given the following custom hook: ``` function useFetchData() { - return useQuery('fetchData', () => request('/api/data')); + return useQuery(['fetchData'], () => request('/api/data')); } ``` diff --git a/docs/src/pages/guides/window-focus-refetching.md b/docs/src/pages/guides/window-focus-refetching.md index dc3b757d3c..0058831fec 100644 --- a/docs/src/pages/guides/window-focus-refetching.md +++ b/docs/src/pages/guides/window-focus-refetching.md @@ -25,7 +25,7 @@ function App() { #### Disabling Per-Query ```js -useQuery('todos', fetchTodos, { refetchOnWindowFocus: false }) +useQuery(['todos'], fetchTodos, { refetchOnWindowFocus: false }) ``` ## Custom Window Focus Event diff --git a/docs/src/pages/overview.md b/docs/src/pages/overview.md index 885e29a6a2..5e32e0a240 100644 --- a/docs/src/pages/overview.md +++ b/docs/src/pages/overview.md @@ -60,7 +60,7 @@ export default function App() { } function Example() { - const { isLoading, error, data } = useQuery('repoData', () => + const { isLoading, error, data } = useQuery(['repoData'], () => fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => res.json() ) diff --git a/docs/src/pages/plugins/createAsyncStoragePersistor.md b/docs/src/pages/plugins/createAsyncStoragePersister.md similarity index 62% rename from docs/src/pages/plugins/createAsyncStoragePersistor.md rename to docs/src/pages/plugins/createAsyncStoragePersister.md index 99f2a2d5f9..cdd8650b64 100644 --- a/docs/src/pages/plugins/createAsyncStoragePersistor.md +++ b/docs/src/pages/plugins/createAsyncStoragePersister.md @@ -1,25 +1,23 @@ --- -id: createAsyncStoragePersistor -title: createAsyncStoragePersistor (Experimental) +id: createAsyncStoragePersister +title: createAsyncStoragePersister --- -> VERY IMPORTANT: This utility is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Use at your own risk. If you choose to rely on this in production in an experimental stage, please lock your version to a patch-level version to avoid unexpected breakages. - ## Installation -This utility comes packaged with `react-query` and is available under the `react-query/createAsyncStoragePersistor-experimental` import. +This utility comes packaged with `react-query` and is available under the `react-query/createAsyncStoragePersister` import. ## Usage -- Import the `createAsyncStoragePersistor` function -- Create a new asyncStoragePersistor +- Import the `createAsyncStoragePersister` function +- Create a new asyncStoragePersister - you can pass any `storage` to it that adheres to the `AsyncStorage` interface - the example below uses the async-storage from React Native - Pass it to the [`persistQueryClient`](./persistQueryClient) function ```ts import AsyncStorage from '@react-native-async-storage/async-storage' -import { persistQueryClient } from 'react-query/persistQueryClient-experimental' -import { createAsyncStoragePersistor } from 'react-query/createAsyncStoragePersistor-experimental' +import { persistQueryClient } from 'react-query/persistQueryClient' +import { createAsyncStoragePersister } from 'react-query/createAsyncStoragePersister' const queryClient = new QueryClient({ defaultOptions: { @@ -29,30 +27,30 @@ const queryClient = new QueryClient({ }, }) -const asyncStoragePersistor = createAsyncStoragePersistor({ +const asyncStoragePersister = createAsyncStoragePersister({ storage: AsyncStorage }) persistQueryClient({ queryClient, - persistor: asyncStoragePersistor, + persister: asyncStoragePersister, }) ``` ## API -### `createAsyncStoragePersistor` +### `createAsyncStoragePersister` -Call this function to create an asyncStoragePersistor that you can use later with `persistQueryClient`. +Call this function to create an asyncStoragePersister that you can use later with `persistQueryClient`. ```js -createAsyncStoragePersistor(options: CreateAsyncStoragePersistorOptions) +createAsyncStoragePersister(options: CreateAsyncStoragePersisterOptions) ``` ### `Options` ```ts -interface CreateAsyncStoragePersistorOptions { +interface CreateAsyncStoragePersisterOptions { /** The storage client used for setting an retrieving items from cache */ storage: AsyncStorage /** The key to use when storing the cache to localStorage */ diff --git a/docs/src/pages/plugins/createWebStoragePersistor.md b/docs/src/pages/plugins/createWebStoragePersister.md similarity index 54% rename from docs/src/pages/plugins/createWebStoragePersistor.md rename to docs/src/pages/plugins/createWebStoragePersister.md index 983ac336bf..58f9d93168 100644 --- a/docs/src/pages/plugins/createWebStoragePersistor.md +++ b/docs/src/pages/plugins/createWebStoragePersister.md @@ -1,23 +1,21 @@ --- -id: createWebStoragePersistor -title: createWebStoragePersistor (Experimental) +id: createWebStoragePersister +title: createWebStoragePersister --- -> VERY IMPORTANT: This utility is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Use at your own risk. If you choose to rely on this in production in an experimental stage, please lock your version to a patch-level version to avoid unexpected breakages. - ## Installation -This utility comes packaged with `react-query` and is available under the `react-query/createWebStoragePersistor-experimental` import. +This utility comes packaged with `react-query` and is available under the `react-query/createWebStoragePersister` import. ## Usage -- Import the `createWebStoragePersistor` function -- Create a new webStoragePersistor +- Import the `createWebStoragePersister` function +- Create a new webStoragePersister - Pass it to the [`persistQueryClient`](./persistQueryClient) function ```ts -import { persistQueryClient } from 'react-query/persistQueryClient-experimental' -import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental' +import { persistQueryClient } from 'react-query/persistQueryClient' +import { createWebStoragePersister } from 'react-query/createWebStoragePersister' const queryClient = new QueryClient({ defaultOptions: { @@ -27,29 +25,29 @@ const queryClient = new QueryClient({ }, }) -const localStoragePersistor = createWebStoragePersistor({ storage: window.localStorage }) -// const sessionStoragePersistor = createWebStoragePersistor({ storage: window.sessionStorage }) +const localStoragePersister = createWebStoragePersister({ storage: window.localStorage }) +// const sessionStoragePersister = createWebStoragePersister({ storage: window.sessionStorage }) persistQueryClient({ queryClient, - persistor: localStoragePersistor, + persister: localStoragePersister, }) ``` ## API -### `createWebStoragePersistor` +### `createWebStoragePersister` -Call this function to create a webStoragePersistor that you can use later with `persistQueryClient`. +Call this function to create a webStoragePersister that you can use later with `persistQueryClient`. ```js -createWebStoragePersistor(options: CreateWebStoragePersistorOptions) +createWebStoragePersister(options: CreateWebStoragePersisterOptions) ``` ### `Options` ```ts -interface CreateWebStoragePersistorOptions { +interface CreateWebStoragePersisterOptions { /** The storage client used for setting an retrieving items from cache (window.localStorage or window.sessionStorage) */ storage: Storage /** The key to use when storing the cache */ diff --git a/docs/src/pages/plugins/persistQueryClient.md b/docs/src/pages/plugins/persistQueryClient.md index 1236d07867..969ea98804 100644 --- a/docs/src/pages/plugins/persistQueryClient.md +++ b/docs/src/pages/plugins/persistQueryClient.md @@ -1,28 +1,26 @@ --- id: persistQueryClient -title: persistQueryClient (Experimental) +title: persistQueryClient --- -> VERY IMPORTANT: This utility is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Use at your own risk. If you choose to rely on this in production in an experimental stage, please lock your version to a patch-level version to avoid unexpected breakages. +`persistQueryClient` is a utility for persisting the state of your queryClient and its caches for later use. Different **persisters** can be used to store your client and cache to many different storage layers. -`persistQueryClient` is a utility for persisting the state of your queryClient and its caches for later use. Different **persistors** can be used to store your client and cache to many different storage layers. +## Officially Supported Persisters -## Officially Supported Persistors - -- [createWebStoragePersistor (Experimental)](/plugins/createWebStoragePersistor) -- [createAsyncStoragePersistor (Experimental)](/plugins/createAsyncStoragePersistor) +- [createWebStoragePersister](/plugins/createWebStoragePersister) +- [createAsyncStoragePersister](/plugins/createAsyncStoragePersister) ## Installation -This utility comes packaged with `react-query` and is available under the `react-query/persistQueryClient-experimental` import. +This utility comes packaged with `react-query` and is available under the `react-query/persistQueryClient` import. ## Usage -Import the `persistQueryClient` function, and pass it your `QueryClient` instance (with a `cacheTime` set), and a Persistor interface (there are multiple persistor types you can use): +Import the `persistQueryClient` function, and pass it your `QueryClient` instance (with a `cacheTime` set), and a Persister interface (there are multiple persister types you can use): ```ts -import { persistQueryClient } from 'react-query/persistQueryClient-experimental' -import { createWebStoragePersistor } from 'react-query/createWebStoragePersistor-experimental' +import { persistQueryClient } from 'react-query/persistQueryClient' +import { createWebStoragePersister } from 'react-query/createWebStoragePersister' const queryClient = new QueryClient({ defaultOptions: { @@ -32,11 +30,11 @@ const queryClient = new QueryClient({ }, }) -const localStoragePersistor = createWebStoragePersistor({storage: window.localStorage}) +const localStoragePersister = createWebStoragePersister({storage: window.localStorage}) persistQueryClient({ queryClient, - persistor: localStoragePersistor, + persister: localStoragePersister, }) ``` @@ -52,11 +50,11 @@ You can also pass it `Infinity` to disable garbage collection behavior entirely. As you use your application: -- When your query/mutation cache is updated, it will be dehydrated and stored by the persistor you provided. **By default**, this action is throttled to happen at most every 1 second to save on potentially expensive writes to a persistor, but can be customized as you see fit. +- When your query/mutation cache is updated, it will be dehydrated and stored by the persister you provided. **By default**, this action is throttled to happen at most every 1 second to save on potentially expensive writes to a persister, but can be customized as you see fit. When you reload/bootstrap your app: -- Attempts to load a previously persisted dehydrated query/mutation cache from the persistor +- Attempts to load a previously persisted dehydrated query/mutation cache from the persister - If a cache is found that is older than the `maxAge` (which by default is 24 hours), it will be discarded. This can be customized as you see fit. ## Cache Busting @@ -64,17 +62,17 @@ When you reload/bootstrap your app: Sometimes you may make changes to your application or data that immediately invalidate any and all cached data. If and when this happens, you can pass a `buster` string option to `persistQueryClient`, and if the cache that is found does not also have that buster string, it will be discarded. ```ts -persistQueryClient({ queryClient, persistor, buster: buildHash }) +persistQueryClient({ queryClient, persister, buster: buildHash }) ``` ## API ### `persistQueryClient` -Pass this function a `QueryClient` instance and a persistor that will persist your cache. Both are **required** +Pass this function a `QueryClient` instance and a persister that will persist your cache. Both are **required** ```ts -persistQueryClient({ queryClient, persistor }) +persistQueryClient({ queryClient, persister }) ``` ### `Options` @@ -85,9 +83,9 @@ An object of options: interface PersistQueryClientOptions { /** The QueryClient to persist */ queryClient: QueryClient - /** The Persistor interface for storing and restoring the cache + /** The Persister interface for storing and restoring the cache * to/from a persisted location */ - persistor: Persistor + persister: Persister /** The max-allowed age of the cache. * If a persisted cache is found that is older than this * time, it will be discarded */ @@ -111,12 +109,12 @@ The default options are: } ``` -## Building a Persistor +## Building a Persister -Persistors have the following interface: +Persisters have the following interface: ```ts -export interface Persistor { +export interface Persister { persistClient(persistClient: PersistedClient): Promisable restoreClient(): Promisable removeClient(): Promisable @@ -133,4 +131,4 @@ export interface PersistedClient { } ``` -Satisfy all of these interfaces and you've got yourself a persistor! +Satisfy all of these interfaces and you've got yourself a persister! diff --git a/docs/src/pages/quick-start.md b/docs/src/pages/quick-start.md index c55349bd7d..d587a9a98d 100644 --- a/docs/src/pages/quick-start.md +++ b/docs/src/pages/quick-start.md @@ -36,13 +36,13 @@ function Todos() { const queryClient = useQueryClient() // Queries - const query = useQuery('todos', getTodos) + const query = useQuery(['todos'], getTodos) // Mutations const mutation = useMutation(postTodo, { onSuccess: () => { // Invalidate and refetch - queryClient.invalidateQueries('todos') + queryClient.invalidateQueries(['todos']) }, }) diff --git a/docs/src/pages/reference/InfiniteQueryObserver.md b/docs/src/pages/reference/InfiniteQueryObserver.md index 71d85f5811..45dd93744b 100644 --- a/docs/src/pages/reference/InfiniteQueryObserver.md +++ b/docs/src/pages/reference/InfiniteQueryObserver.md @@ -9,7 +9,7 @@ The `InfiniteQueryObserver` can be used to observe and switch between infinite q ```js const observer = new InfiniteQueryObserver(queryClient, { - queryKey: 'posts', + queryKey: ['posts'], queryFn: fetchPosts, getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor, diff --git a/docs/src/pages/reference/MutationCache.md b/docs/src/pages/reference/MutationCache.md index 06f1d52e99..404fce8e46 100644 --- a/docs/src/pages/reference/MutationCache.md +++ b/docs/src/pages/reference/MutationCache.md @@ -65,8 +65,8 @@ const mutations = mutationCache.getAll() The `subscribe` method can be used to subscribe to the mutation cache as a whole and be informed of safe/known updates to the cache like mutation states changing or mutations being updated, added or removed. ```js -const callback = mutation => { - console.log(mutation) +const callback = event => { + console.log(event.type, event.mutation) } const unsubscribe = mutationCache.subscribe(callback) @@ -74,7 +74,7 @@ const unsubscribe = mutationCache.subscribe(callback) **Options** -- `callback: (mutation?: Mutation) => void` +- `callback: (mutation?: MutationCacheNotifyEvent) => void` - This function will be called with the mutation cache any time it is updated. **Returns** diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index be3fa19d6a..b195e050d0 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -18,7 +18,7 @@ const queryClient = new QueryClient({ }, }) -await queryClient.prefetchQuery('posts', fetchPosts) +await queryClient.prefetchQuery(['posts'], fetchPosts) ``` Its available methods are: @@ -91,7 +91,7 @@ try { **Options** -The options for `fetchQuery` are exactly the same as those of [`useQuery`](./useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, notifyOnChangePropsExclusions, onSuccess, onError, onSettled, useErrorBoundary, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity. +The options for `fetchQuery` are exactly the same as those of [`useQuery`](./useQuery), except the following: `enabled, refetchInterval, refetchIntervalInBackground, refetchOnWindowFocus, refetchOnReconnect, notifyOnChangeProps, onSuccess, onError, onSettled, useErrorBoundary, select, suspense, keepPreviousData, placeholderData`; which are strictly for useQuery and useInfiniteQuery. You can check the [source code](https://github.com/tannerlinsley/react-query/blob/361935a12cec6f36d0bd6ba12e84136c405047c5/src/core/types.ts#L83) for more clarity. **Returns** @@ -205,8 +205,6 @@ This distinction is more a "convenience" for ts devs that know which structure w `setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**. -After successful changing query's cached data via `setQueryData`, it will also trigger `onSuccess` callback from that query. - > The difference between using `setQueryData` and `fetchQuery` is that `setQueryData` is sync and assumes that you already synchronously have the data available. If you need to fetch the data asynchronously, it's suggested that you either refetch the query key or use `fetchQuery` to handle the asynchronous fetch. ```js @@ -268,14 +266,13 @@ queryClient.setQueriesData(queryKey | filters, updater) The `invalidateQueries` method can be used to invalidate and refetch single or multiple queries in the cache based on their query keys or any other functionally accessible property/state of the query. By default, all matching queries are immediately marked as invalid and active queries are refetched in the background. -- If you **do not want active queries to refetch**, and simply be marked as invalid, you can use the `refetchActive: false` option. -- If you **want inactive queries to refetch** as well, use the `refetchInactive: true` option +- If you **do not want active queries to refetch**, and simply be marked as invalid, you can use the `refetchType: 'none'` option. +- If you **want inactive queries to refetch** as well, use the `refetchTye: 'all'` option ```js -await queryClient.invalidateQueries('posts', { +await queryClient.invalidateQueries(['posts'], { exact, - refetchActive: true, - refetchInactive: false + refetchType: 'active', }, { throwOnError, cancelRefetch }) ``` @@ -283,20 +280,22 @@ await queryClient.invalidateQueries('posts', { - `queryKey?: QueryKey`: [Query Keys](../guides/query-keys) - `filters?: QueryFilters`: [Query Filters](../guides/filters#query-filters) - - `refetchActive: Boolean` - - Defaults to `true` - - When set to `false`, queries that match the refetch predicate and are actively being rendered via `useQuery` and friends will NOT be refetched in the background, and only marked as invalid. - - `refetchInactive: Boolean` - - Defaults to `false` - - When set to `true`, queries that match the refetch predicate and are not being rendered via `useQuery` and friends will be both marked as invalid and also refetched in the background + - `refetchType?: 'active' | 'inactive' | 'all' | 'none'` + - Defaults to `'active'` + - When set to `active`, only queries that match the refetch predicate and are actively being rendered via `useQuery` and friends will be refetched in the background. + - When set to `inactive`, only queries that match the refetch predicate and are NOT actively being rendered via `useQuery` and friends will be refetched in the background. + - When set to `all`, all queries that match the refetch predicate will be refetched in the background. + - When set to `none`, no queries will be refetched, and those that match the refetch predicate will be marked as invalid only. - `refetchPage: (page: TData, index: number, allPages: TData[]) => boolean` - Only for [Infinite Queries](../guides/infinite-queries#refetchpage) - Use this function to specify which pages should be refetched - `options?: InvalidateOptions`: - `throwOnError?: boolean` - When set to `true`, this method will throw if any of the query refetch tasks fail. - - cancelRefetch?: boolean - - When set to `true`, then the current request will be cancelled before a new request is made + - `cancelRefetch?: boolean` + - Defaults to `true` + - Per default, a currently running request will be cancelled before a new request is made + - When set to `false`, no refetch will be made if there is already a request running. ## `queryClient.refetchQueries` @@ -312,10 +311,10 @@ await queryClient.refetchQueries() await queryClient.refetchQueries({ stale: true }) // refetch all active queries partially matching a query key: -await queryClient.refetchQueries(['posts'], { active: true }) +await queryClient.refetchQueries(['posts'], { type: 'active' }) // refetch all active queries exactly matching a query key: -await queryClient.refetchQueries(['posts', 1], { active: true, exact: true }) +await queryClient.refetchQueries(['posts', 1], { type: 'active', exact: true }) ``` **Options** @@ -328,8 +327,10 @@ await queryClient.refetchQueries(['posts', 1], { active: true, exact: true }) - `options?: RefetchOptions`: - `throwOnError?: boolean` - When set to `true`, this method will throw if any of the query refetch tasks fail. - - cancelRefetch?: boolean - - When set to `true`, then the current request will be cancelled before a new request is made + - `cancelRefetch?: boolean` + - Defaults to `true` + - Per default, a currently running request will be cancelled before a new request is made + - When set to `false`, no refetch will be made if there is already a request running. **Returns** @@ -342,7 +343,7 @@ The `cancelQueries` method can be used to cancel outgoing queries based on their This is most useful when performing optimistic updates since you will likely need to cancel any outgoing query refetches so they don't clobber your optimistic update when they resolve. ```js -await queryClient.cancelQueries('posts', { exact: true }) +await queryClient.cancelQueries(['posts'], { exact: true }) ``` **Options** @@ -396,8 +397,10 @@ queryClient.resetQueries(queryKey, { exact: true }) - `options?: ResetOptions`: - `throwOnError?: boolean` - When set to `true`, this method will throw if any of the query refetch tasks fail. - - cancelRefetch?: boolean - - When set to `true`, then the current request will be cancelled before a new request is made + - `cancelRefetch?: boolean` + - Defaults to `true` + - Per default, a currently running request will be cancelled before a new request is made + - When set to `false`, no refetch will be made if there is already a request running. **Returns** @@ -468,7 +471,7 @@ queryClient.setDefaultOptions({ The `getQueryDefaults` method returns the default options which have been set for specific queries: ```js -const defaultOptions = queryClient.getQueryDefaults('posts') +const defaultOptions = queryClient.getQueryDefaults(['posts']) ``` ## `queryClient.setQueryDefaults` @@ -476,10 +479,10 @@ const defaultOptions = queryClient.getQueryDefaults('posts') `setQueryDefaults` can be used to set default options for specific queries: ```js -queryClient.setQueryDefaults('posts', { queryFn: fetchPosts }) +queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts }) function Component() { - const { data } = useQuery('posts') + const { data } = useQuery(['posts']) } ``` @@ -493,7 +496,7 @@ function Component() { The `getMutationDefaults` method returns the default options which have been set for specific mutations: ```js -const defaultOptions = queryClient.getMutationDefaults('addPost') +const defaultOptions = queryClient.getMutationDefaults(['addPost']) ``` ## `queryClient.setMutationDefaults` @@ -501,10 +504,10 @@ const defaultOptions = queryClient.getMutationDefaults('addPost') `setMutationDefaults` can be used to set default options for specific mutations: ```js -queryClient.setMutationDefaults('addPost', { mutationFn: addPost }) +queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost }) function Component() { - const { data } = useMutation('addPost') + const { data } = useMutation(['addPost']) } ``` diff --git a/docs/src/pages/reference/QueryObserver.md b/docs/src/pages/reference/QueryObserver.md index 51813453d9..a2f625b905 100644 --- a/docs/src/pages/reference/QueryObserver.md +++ b/docs/src/pages/reference/QueryObserver.md @@ -8,7 +8,7 @@ title: QueryObserver The `QueryObserver` can be used to observe and switch between queries. ```js -const observer = new QueryObserver(queryClient, { queryKey: 'posts' }) +const observer = new QueryObserver(queryClient, { queryKey: ['posts'] }) const unsubscribe = observer.subscribe(result => { console.log(result) diff --git a/docs/src/pages/reference/hydration.md b/docs/src/pages/reference/hydration.md index 0ea0040063..f1694fb634 100644 --- a/docs/src/pages/reference/hydration.md +++ b/docs/src/pages/reference/hydration.md @@ -15,8 +15,6 @@ const dehydratedState = dehydrate(queryClient, { }) ``` -> Note: Since version `3.22.0` hydration utilities moved into to core. If you using lower version your should import `dehydrate` from `react-query/hydration` - **Options** - `client: QueryClient` @@ -72,8 +70,6 @@ import { hydrate } from 'react-query' hydrate(queryClient, dehydratedState, options) ``` -> Note: Since version `3.22.0` hydration utilities moved into to core. If you using lower version your should import `hydrate` from `react-query/hydration` - **Options** - `client: QueryClient` @@ -99,8 +95,6 @@ import { useHydrate } from 'react-query' useHydrate(dehydratedState, options) ``` -> Note: Since version `3.22.0` hydration utilities moved into to core. If you using lower version your should import `useHydrate` from `react-query/hydration` - **Options** - `dehydratedState: DehydratedState` @@ -123,8 +117,6 @@ function App() { } ``` -> Note: Since version `3.22.0` hydration utilities moved into to core. If you using lower version your should import `Hydrate` from `react-query/hydration` - **Options** - `state: DehydratedState` diff --git a/docs/src/pages/reference/useInfiniteQuery.md b/docs/src/pages/reference/useInfiniteQuery.md index 8ffaab4255..08d6c9cbf0 100644 --- a/docs/src/pages/reference/useInfiniteQuery.md +++ b/docs/src/pages/reference/useInfiniteQuery.md @@ -26,9 +26,7 @@ The options for `useInfiniteQuery` are identical to the [`useQuery` hook](/refer - `queryFn: (context: QueryFunctionContext) => Promise` - **Required, but only if no default query function has been defined** [`defaultQueryFn`](/guides/default-query-function) - The function that the query will use to request data. - - Receives a `QueryFunctionContext` object with the following variables: - - `queryKey: EnsuredQueryKey`: the queryKey, guaranteed to be an Array - - `pageParam: unknown | undefined` + - Receives a [QueryFunctionContext](../guides/query-functions#queryfunctioncontext) - Must return a promise that will either resolve data or throw an error. - Make sure you return the data *and* the `pageParam` if needed for use in the props below. - `getNextPageParam: (lastPage, allPages) => unknown | undefined` diff --git a/docs/src/pages/reference/useMutation.md b/docs/src/pages/reference/useMutation.md index e8b2e9cdcb..07d82110ca 100644 --- a/docs/src/pages/reference/useMutation.md +++ b/docs/src/pages/reference/useMutation.md @@ -17,13 +17,15 @@ const { reset, status, } = useMutation(mutationFn, { + cacheTime, mutationKey, + networkMode, onError, onMutate, onSettled, onSuccess, useErrorBoundary, - meta, + meta }) mutate(variables, { @@ -39,9 +41,16 @@ mutate(variables, { - **Required** - A function that performs an asynchronous task and returns a promise. - `variables` is an object that `mutate` will pass to your `mutationFn` +- `cacheTime: number | Infinity` + - The time in milliseconds that unused/inactive cache data remains in memory. When a mutation's cache becomes unused or inactive, that cache data will be garbage collected after this duration. When different cache times are specified, the longest one will be used. + - If set to `Infinity`, will disable garbage collection - `mutationKey: string` - Optional - A mutation key can be set to inherit defaults set with `queryClient.setMutationDefaults` or to identify the mutation in the devtools. +- `networkMode: 'online' | 'always' | 'offlineFirst` + - optional + - defaults to `'online'` + - see [Network Mode](../guides/network-mode) for more information. - `onMutate: (variables: TVariables) => Promise | TContext | void` - Optional - This function will fire before the mutation function is fired and is passed the same variables the mutation function would receive @@ -94,6 +103,9 @@ mutate(variables, { - `error` if the last mutation attempt resulted in an error. - `success` if the last mutation attempt was successful. - `isIdle`, `isLoading`, `isSuccess`, `isError`: boolean variables derived from `status` +- `isPaused: boolean` + - will be `true` if the mutation has been `paused` + - see [Network Mode](../guides/network-mode) for more information. - `data: undefined | unknown` - Defaults to `undefined` - The last successfully resolved data for the query. diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index 0b5588acc7..972a6fe419 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -14,6 +14,7 @@ const { isFetched, isFetchedAfterMount, isFetching, + isPaused, isIdle, isLoading, isLoadingError, @@ -26,16 +27,17 @@ const { refetch, remove, status, + fetchStatus, } = useQuery(queryKey, queryFn?, { cacheTime, enabled, + networkMode, initialData, - initialDataUpdatedAt + initialDataUpdatedAt, isDataEqual, keepPreviousData, meta, notifyOnChangeProps, - notifyOnChangePropsExclusions, onError, onSettled, onSuccess, @@ -66,7 +68,7 @@ const result = useQuery({ **Options** -- `queryKey: string | unknown[]` +- `queryKey: unknown[]` - **Required** - The query key to use for this query. - The query key will be hashed into a stable hash. See [Query Keys](../guides/query-keys) for more information. @@ -74,12 +76,15 @@ const result = useQuery({ - `queryFn: (context: QueryFunctionContext) => Promise` - **Required, but only if no default query function has been defined** See [Default Query Function](../guides/default-query-function) for more information. - The function that the query will use to request data. - - Receives a `QueryFunctionContext` object with the following variables: - - `queryKey: EnsuredQueryKey`: the queryKey, guaranteed to be an Array + - Receives a [QueryFunctionContext](../guides/query-functions#queryfunctioncontext) - Must return a promise that will either resolve data or throw an error. - `enabled: boolean` - Set this to `false` to disable this query from automatically running. - Can be used for [Dependent Queries](../guides/dependent-queries). +- `networkMode: 'online' | 'always' | 'offlineFirst` + - optional + - defaults to `'online'` + - see [Network Mode](../guides/network-mode) for more information. - `retry: boolean | number | (failureCount: number, error: TError) => boolean` - If `false`, failed queries will not retry by default. - If `true`, failed queries will retry infinitely. @@ -126,18 +131,15 @@ const result = useQuery({ - If set to `true`, the query will refetch on reconnect if the data is stale. - If set to `false`, the query will not refetch on reconnect. - If set to `"always"`, the query will always refetch on reconnect. -- `notifyOnChangeProps: string[] | "tracked"` +- `notifyOnChangeProps: string[] | "all"` - Optional - If set, the component will only re-render if any of the listed properties change. - If set to `['data', 'error']` for example, the component will only re-render when the `data` or `error` properties change. - - If set to `"tracked"`, access to properties will be tracked, and the component will only re-render when one of the tracked properties change. -- `notifyOnChangePropsExclusions: string[]` - - Optional - - If set, the component will not re-render if any of the listed properties change. - - If set to `['isStale']` for example, the component will not re-render when the `isStale` property changes. + - If set to `"all"`, the component will opt-out of smart tracking and re-render whenever a query is updated. + - By default, access to properties will be tracked, and the component will only re-render when one of the tracked properties change. - `onSuccess: (data: TData) => void` - Optional - - This function will fire any time the query successfully fetches new data or the cache is updated via `setQueryData`. + - This function will fire any time the query successfully fetches new data. - `onError: (error: TError) => void` - Optional - This function will fire if the query encounters an error and will be passed the error. @@ -223,9 +225,15 @@ const result = useQuery({ - `isFetchedAfterMount: boolean` - Will be `true` if the query has been fetched after the component mounted. - This property can be used to not show any previously cached data. +- `fetchStatus: FetchStatus` + - `fetching`: Is `true` whenever the queryFn is executing, which includes initial `loading` as well as background refetches. + - `paused`: The query wanted to fetch, but has been `paused` + - `idle`: The query is not fetching + - see [Network Mode](../guides/network-mode) for more information. - `isFetching: boolean` - - Is `true` whenever a request is in-flight, which includes initial `loading` as well as background refetches. - - Will be `true` if the query is currently fetching, including background fetching. + - A derived boolean from the `fetchStatus` variable above, provided for convenience. +- `isPaused: boolean` + - A derived boolean from the `fetchStatus` variable above, provided for convenience. - `isRefetching: boolean` - Is `true` whenever a background refetch is in-flight, which _does not_ include initial `loading` - Is the same as `isFetching && !isLoading` @@ -236,6 +244,9 @@ const result = useQuery({ - `refetch: (options: { throwOnError: boolean, cancelRefetch: boolean }) => Promise` - A function to manually refetch the query. - If the query errors, the error will only be logged. If you want an error to be thrown, pass the `throwOnError: true` option - - If `cancelRefetch` is `true`, then the current request will be cancelled before a new request is made + - `cancelRefetch?: boolean` + - Defaults to `true` + - Per default, a currently running request will be cancelled before a new request is made + - When set to `false`, no refetch will be made if there is already a request running. - `remove: () => void` - A function to remove the query from the cache. diff --git a/examples/basic-typescript/src/index.tsx b/examples/basic-typescript/src/index.tsx index 1b4c8e9b29..4a969879e7 100644 --- a/examples/basic-typescript/src/index.tsx +++ b/examples/basic-typescript/src/index.tsx @@ -20,7 +20,7 @@ type Post = { function usePosts() { return useQuery( - "posts", + ["posts"], async (): Promise> => { const { data } = await axios.get( "https://jsonplaceholder.typicode.com/posts" diff --git a/examples/basic/src/index.js b/examples/basic/src/index.js index 722e2bcc7c..638eef5425 100644 --- a/examples/basic/src/index.js +++ b/examples/basic/src/index.js @@ -9,8 +9,23 @@ import { QueryClientProvider, } from "react-query"; import { ReactQueryDevtools } from "react-query/devtools"; +import { persistQueryClient } from 'react-query/persistQueryClient'; +import { createWebStoragePersister } from 'react-query/createWebStoragePersister' -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + cacheTime: 1000 * 60 * 60 * 24, // 24 hours + }, + }, +}) + +const localStoragePersister = createWebStoragePersister({storage: window.localStorage}) + +persistQueryClient({ + queryClient, + persister: localStoragePersister, +}) function App() { const [postId, setPostId] = React.useState(-1); @@ -38,7 +53,7 @@ function App() { } function usePosts() { - return useQuery("posts", async () => { + return useQuery(["posts"], async () => { const { data } = await axios.get( "https://jsonplaceholder.typicode.com/posts" ); diff --git a/hydration/package.json b/hydration/package.json deleted file mode 100644 index 804509cb4d..0000000000 --- a/hydration/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "internal": true, - "main": "../lib/hydration/index.js", - "module": "../es/hydration/index.js", - "types": "../types/hydration/index.d.ts" -} diff --git a/package.json b/package.json index 560a5dcec6..0c927899a0 100644 --- a/package.json +++ b/package.json @@ -16,13 +16,13 @@ "module": "es/index.js", "sideEffects": [ "es/index.js", - "es/react/index.js", - "es/react/setBatchUpdatesFn.js", - "es/react/setLogger.js", + "es/reactjs/index.js", + "es/reactjs/setBatchUpdatesFn.js", + "es/reactjs/setLogger.js", "lib/index.js", - "lib/react/index.js", - "lib/react/setBatchUpdatesFn.js", - "lib/react/setLogger.js" + "lib/reactjs/index.js", + "lib/reactjs/setBatchUpdatesFn.js", + "lib/reactjs/setLogger.js" ], "scripts": { "test": "is-ci \"test:ci\" \"test:dev\"", @@ -55,12 +55,12 @@ "es", "hydration", "devtools", - "persistQueryClient-experimental", - "createWebStoragePersistor-experimental", - "createAsyncStoragePersistor-experimental", + "persistQueryClient", + "createWebStoragePersister", + "createAsyncStoragePersister", "broadcastQueryClient-experimental", "lib", - "react", + "reactjs", "scripts", "types" ], @@ -81,7 +81,11 @@ } }, "typesVersions": { - "<4.1": { "types/*": ["types/ts3.8/*"] } + "<4.1": { + "types/*": [ + "types/ts3.8/*" + ] + } }, "devDependencies": { "@babel/cli": "^7.11.6", diff --git a/persistQueryClient-experimental/package.json b/persistQueryClient-experimental/package.json deleted file mode 100644 index f58433722d..0000000000 --- a/persistQueryClient-experimental/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "internal": true, - "main": "../lib/persistQueryClient-experimental/index.js", - "module": "../es/persistQueryClient-experimental/index.js", - "types": "../types/persistQueryClient-experimental/index.d.ts" -} diff --git a/persistQueryClient/package.json b/persistQueryClient/package.json new file mode 100644 index 0000000000..576ea12afb --- /dev/null +++ b/persistQueryClient/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/persistQueryClient/index.js", + "module": "../es/persistQueryClient/index.js", + "types": "../types/persistQueryClient/index.d.ts" +} diff --git a/react/package.json b/react/package.json deleted file mode 100644 index 02f74bc64a..0000000000 --- a/react/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "internal": true, - "main": "../lib/react/index.js", - "module": "../es/react/index.js", - "types": "../types/react/index.d.ts" -} diff --git a/reactjs/package.json b/reactjs/package.json new file mode 100644 index 0000000000..8d0e431ae3 --- /dev/null +++ b/reactjs/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/reactjs/index.js", + "module": "../es/reactjs/index.js", + "types": "../types/reactjs/index.d.ts" +} diff --git a/rollup.config.js b/rollup.config.js index 528bebbaf5..35fdd215aa 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -19,21 +19,20 @@ const inputSrcs = [ ['src/index.ts', 'ReactQuery', 'react-query'], ['src/core/index.ts', 'ReactQueryCore', 'react-query-core'], ['src/devtools/index.ts', 'ReactQueryDevtools', 'react-query-devtools'], - ['src/hydration/index.ts', 'ReactQueryHydration', 'react-query-hydration'], [ - 'src/persistQueryClient-experimental/index.ts', - 'ReactQueryPersistQueryClientExperimental', - 'persistQueryClient-experimental', + 'src/persistQueryClient/index.ts', + 'ReactQueryPersistQueryClient', + 'persistQueryClient', ], [ - 'src/createWebStoragePersistor-experimental/index.ts', - 'ReactQueryCreateWebStoragePersistorExperimental', - 'createWebStoragePersistor-experimental', + 'src/createWebStoragePersister/index.ts', + 'ReactQueryCreateWebStoragePersister', + 'createWebStoragePersister', ], [ - 'src/createAsyncStoragePersistor-experimental/index.ts', - 'ReactQueryCreateAsyncStoragePersistorExperimental', - 'createAsyncStoragePersistor-experimental', + 'src/createAsyncStoragePersister/index.ts', + 'ReactQueryCreateAsyncStoragePersister', + 'createAsyncStoragePersister', ], [ 'src/broadcastQueryClient-experimental/index.ts', diff --git a/src/broadcastQueryClient-experimental/index.ts b/src/broadcastQueryClient-experimental/index.ts index 9f6181553a..6a36d1c339 100644 --- a/src/broadcastQueryClient-experimental/index.ts +++ b/src/broadcastQueryClient-experimental/index.ts @@ -33,20 +33,20 @@ export function broadcastQueryClient({ } = queryEvent if ( - queryEvent.type === 'queryUpdated' && + queryEvent.type === 'updated' && queryEvent.action?.type === 'success' ) { channel.postMessage({ - type: 'queryUpdated', + type: 'updated', queryHash, queryKey, state, }) } - if (queryEvent.type === 'queryRemoved') { + if (queryEvent.type === 'removed') { channel.postMessage({ - type: 'queryRemoved', + type: 'removed', queryHash, queryKey, }) @@ -61,7 +61,7 @@ export function broadcastQueryClient({ tx(() => { const { type, queryHash, queryKey, state } = action - if (type === 'queryUpdated') { + if (type === 'updated') { const query = queryCache.get(queryHash) if (query) { @@ -77,7 +77,7 @@ export function broadcastQueryClient({ }, state ) - } else if (type === 'queryRemoved') { + } else if (type === 'removed') { const query = queryCache.get(queryHash) if (query) { diff --git a/src/core/infiniteQueryBehavior.ts b/src/core/infiniteQueryBehavior.ts index 0bd46be8e0..3fe93a0377 100644 --- a/src/core/infiniteQueryBehavior.ts +++ b/src/core/infiniteQueryBehavior.ts @@ -1,5 +1,5 @@ import type { QueryBehavior } from './query' -import { isCancelable } from './retryer' + import type { InfiniteData, QueryFunctionContext, @@ -73,11 +73,6 @@ export function infiniteQueryBehavior< buildNewPages(pages, param, page, previous) ) - if (isCancelable(queryFnResult)) { - const promiseAsAny = promise as any - promiseAsAny.cancel = queryFnResult.cancel - } - return promise } @@ -148,15 +143,10 @@ export function infiniteQueryBehavior< pageParams: newPageParams, })) - const finalPromiseAsAny = finalPromise as any - - finalPromiseAsAny.cancel = () => { + context.signal?.addEventListener('abort', () => { cancelled = true abortController?.abort() - if (isCancelable(promise)) { - promise.cancel() - } - } + }) return finalPromise } diff --git a/src/core/infiniteQueryObserver.ts b/src/core/infiniteQueryObserver.ts index 19ef264650..dc0a6a6021 100644 --- a/src/core/infiniteQueryObserver.ts +++ b/src/core/infiniteQueryObserver.ts @@ -39,7 +39,7 @@ export class InfiniteQueryObserver< // Type override protected fetch!: ( - fetchOptions?: ObserverFetchOptions + fetchOptions: ObserverFetchOptions ) => Promise> // eslint-disable-next-line @typescript-eslint/no-useless-constructor @@ -90,28 +90,27 @@ export class InfiniteQueryObserver< > } - fetchNextPage( - options?: FetchNextPageOptions - ): Promise> { + fetchNextPage({ pageParam, ...options }: FetchNextPageOptions = {}): Promise< + InfiniteQueryObserverResult + > { return this.fetch({ - // TODO consider removing `?? true` in future breaking change, to be consistent with `refetch` API (see https://github.com/tannerlinsley/react-query/issues/2617) - cancelRefetch: options?.cancelRefetch ?? true, - throwOnError: options?.throwOnError, + ...options, meta: { - fetchMore: { direction: 'forward', pageParam: options?.pageParam }, + fetchMore: { direction: 'forward', pageParam }, }, }) } - fetchPreviousPage( - options?: FetchPreviousPageOptions - ): Promise> { + fetchPreviousPage({ + pageParam, + ...options + }: FetchPreviousPageOptions = {}): Promise< + InfiniteQueryObserverResult + > { return this.fetch({ - // TODO consider removing `?? true` in future breaking change, to be consistent with `refetch` API (see https://github.com/tannerlinsley/react-query/issues/2617) - cancelRefetch: options?.cancelRefetch ?? true, - throwOnError: options?.throwOnError, + ...options, meta: { - fetchMore: { direction: 'backward', pageParam: options?.pageParam }, + fetchMore: { direction: 'backward', pageParam }, }, }) } @@ -134,9 +133,10 @@ export class InfiniteQueryObserver< hasNextPage: hasNextPage(options, state.data?.pages), hasPreviousPage: hasPreviousPage(options, state.data?.pages), isFetchingNextPage: - state.isFetching && state.fetchMeta?.fetchMore?.direction === 'forward', + state.fetchStatus === 'fetching' && + state.fetchMeta?.fetchMore?.direction === 'forward', isFetchingPreviousPage: - state.isFetching && + state.fetchStatus === 'fetching' && state.fetchMeta?.fetchMore?.direction === 'backward', } } diff --git a/src/core/mutation.ts b/src/core/mutation.ts index 58796a3a6b..093f4107ee 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -3,7 +3,8 @@ import type { MutationCache } from './mutationCache' import type { MutationObserver } from './mutationObserver' import { getLogger } from './logger' import { notifyManager } from './notifyManager' -import { Retryer } from './retryer' +import { Removable } from './removable' +import { canFetch, Retryer } from './retryer' import { noop } from './utils' // TYPES @@ -81,7 +82,7 @@ export class Mutation< TError = unknown, TVariables = void, TContext = unknown -> { +> extends Removable { state: MutationState options: MutationOptions mutationId: number @@ -92,6 +93,8 @@ export class Mutation< private retryer?: Retryer constructor(config: MutationConfig) { + super() + this.options = { ...config.defaultOptions, ...config.options, @@ -101,6 +104,9 @@ export class Mutation< this.observers = [] this.state = config.state || getDefaultState() this.meta = config.meta + + this.updateCacheTime(this.options.cacheTime) + this.scheduleGc() } setState(state: MutationState): void { @@ -110,11 +116,38 @@ export class Mutation< addObserver(observer: MutationObserver): void { if (this.observers.indexOf(observer) === -1) { this.observers.push(observer) + + // Stop the mutation from being garbage collected + this.clearGcTimeout() + + this.mutationCache.notify({ + type: 'observerAdded', + mutation: this, + observer, + }) } } removeObserver(observer: MutationObserver): void { this.observers = this.observers.filter(x => x !== observer) + + this.scheduleGc() + + this.mutationCache.notify({ + type: 'observerRemoved', + mutation: this, + observer, + }) + } + + protected optionalRemove() { + if (!this.observers.length) { + if (this.state.status === 'loading') { + this.scheduleGc() + } else { + this.mutationCache.remove(this) + } + } } cancel(): Promise { @@ -247,21 +280,82 @@ export class Mutation< }, retry: this.options.retry ?? 0, retryDelay: this.options.retryDelay, + networkMode: this.options.networkMode, }) return this.retryer.promise } private dispatch(action: Action): void { - this.state = reducer(this.state, action) + this.state = this.reducer(action) notifyManager.batch(() => { this.observers.forEach(observer => { observer.onMutationUpdate(action) }) - this.mutationCache.notify(this) + this.mutationCache.notify({ + mutation: this, + type: 'updated', + action, + }) }) } + + private reducer( + action: Action + ): MutationState { + switch (action.type) { + case 'failed': + return { + ...this.state, + failureCount: this.state.failureCount + 1, + } + case 'pause': + return { + ...this.state, + isPaused: true, + } + case 'continue': + return { + ...this.state, + isPaused: false, + } + case 'loading': + return { + ...this.state, + context: action.context, + data: undefined, + error: null, + isPaused: !canFetch(this.options.networkMode), + status: 'loading', + variables: action.variables, + } + case 'success': + return { + ...this.state, + data: action.data, + error: null, + status: 'success', + isPaused: false, + } + case 'error': + return { + ...this.state, + data: undefined, + error: action.error, + failureCount: this.state.failureCount + 1, + isPaused: false, + status: 'error', + } + case 'setState': + return { + ...this.state, + ...action.state, + } + default: + return this.state + } + } } export function getDefaultState< @@ -280,60 +374,3 @@ export function getDefaultState< variables: undefined, } } - -function reducer( - state: MutationState, - action: Action -): MutationState { - switch (action.type) { - case 'failed': - return { - ...state, - failureCount: state.failureCount + 1, - } - case 'pause': - return { - ...state, - isPaused: true, - } - case 'continue': - return { - ...state, - isPaused: false, - } - case 'loading': - return { - ...state, - context: action.context, - data: undefined, - error: null, - isPaused: false, - status: 'loading', - variables: action.variables, - } - case 'success': - return { - ...state, - data: action.data, - error: null, - status: 'success', - isPaused: false, - } - case 'error': - return { - ...state, - data: undefined, - error: action.error, - failureCount: state.failureCount + 1, - isPaused: false, - status: 'error', - } - case 'setState': - return { - ...state, - ...action.state, - } - default: - return state - } -} diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts index 624e6972a2..d333ccb26f 100644 --- a/src/core/mutationCache.ts +++ b/src/core/mutationCache.ts @@ -1,9 +1,10 @@ +import { MutationObserver } from './mutationObserver' import type { MutationOptions } from './types' import type { QueryClient } from './queryClient' import { notifyManager } from './notifyManager' -import { Mutation, MutationState } from './mutation' +import { Action, Mutation, MutationState } from './mutation' import { matchMutation, MutationFilters, noop } from './utils' -import { Subscribable } from './subscribable' +import { Notifiable } from './notifiable' // TYPES @@ -12,13 +13,13 @@ interface MutationCacheConfig { error: unknown, variables: unknown, context: unknown, - mutation: Mutation + mutation: Mutation ) => void onSuccess?: ( data: unknown, variables: unknown, context: unknown, - mutation: Mutation + mutation: Mutation ) => void onMutate?: ( variables: unknown, @@ -26,11 +27,43 @@ interface MutationCacheConfig { ) => void } -type MutationCacheListener = (mutation?: Mutation) => void +interface NotifyEventMutationAdded { + type: 'added' + mutation: Mutation +} +interface NotifyEventMutationRemoved { + type: 'removed' + mutation: Mutation +} + +interface NotifyEventMutationObserverAdded { + type: 'observerAdded' + mutation: Mutation + observer: MutationObserver +} + +interface NotifyEventMutationObserverRemoved { + type: 'observerRemoved' + mutation: Mutation + observer: MutationObserver +} + +interface NotifyEventMutationUpdated { + type: 'updated' + mutation: Mutation + action: Action +} + +type MutationCacheNotifyEvent = + | NotifyEventMutationAdded + | NotifyEventMutationRemoved + | NotifyEventMutationObserverAdded + | NotifyEventMutationObserverRemoved + | NotifyEventMutationUpdated // CLASS -export class MutationCache extends Subscribable { +export class MutationCache extends Notifiable { config: MutationCacheConfig private mutations: Mutation[] @@ -66,13 +99,13 @@ export class MutationCache extends Subscribable { add(mutation: Mutation): void { this.mutations.push(mutation) - this.notify(mutation) + this.notify({ type: 'added', mutation }) } remove(mutation: Mutation): void { this.mutations = this.mutations.filter(x => x !== mutation) mutation.cancel() - this.notify(mutation) + this.notify({ type: 'removed', mutation }) } clear(): void { @@ -101,14 +134,6 @@ export class MutationCache extends Subscribable { return this.mutations.filter(mutation => matchMutation(filters, mutation)) } - notify(mutation?: Mutation) { - notifyManager.batch(() => { - this.listeners.forEach(listener => { - listener(mutation) - }) - }) - } - onFocus(): void { this.resumePausedMutations() } diff --git a/src/core/notifiable.ts b/src/core/notifiable.ts new file mode 100644 index 0000000000..3d2bcd857f --- /dev/null +++ b/src/core/notifiable.ts @@ -0,0 +1,12 @@ +import { Subscribable } from './subscribable' +import { notifyManager } from '../core/notifyManager' + +export class Notifiable extends Subscribable<(event: TEvent) => void> { + notify(event: TEvent) { + notifyManager.batch(() => { + this.listeners.forEach(listener => { + listener(event) + }) + }) + } +} diff --git a/src/core/queriesObserver.ts b/src/core/queriesObserver.ts index 2eb22ef8b5..79d1bbc168 100644 --- a/src/core/queriesObserver.ts +++ b/src/core/queriesObserver.ts @@ -1,6 +1,10 @@ import { difference, replaceAt } from './utils' import { notifyManager } from './notifyManager' -import type { QueryObserverOptions, QueryObserverResult } from './types' +import type { + QueryObserverOptions, + QueryObserverResult, + DefaultedQueryObserverOptions, +} from './types' import type { QueryClient } from './queryClient' import { NotifyOptions, QueryObserver } from './queryObserver' import { Subscribable } from './subscribable' @@ -74,7 +78,7 @@ export class QueriesObserver extends Subscribable { ): QueryObserverMatch[] { const prevObservers = this.observers const defaultedQueryOptions = queries.map(options => - this.client.defaultQueryObserverOptions(options) + this.client.defaultQueryOptions(options) ) const matchingObservers: QueryObserverMatch[] = defaultedQueryOptions.flatMap( @@ -134,7 +138,7 @@ export class QueriesObserver extends Subscribable { } private getObserver(options: QueryObserverOptions): QueryObserver { - const defaultedOptions = this.client.defaultQueryObserverOptions(options) + const defaultedOptions = this.client.defaultQueryOptions(options) const currentObserver = this.observersMap[defaultedOptions.queryHash!] return currentObserver ?? new QueryObserver(this.client, defaultedOptions) } @@ -205,6 +209,6 @@ export class QueriesObserver extends Subscribable { } type QueryObserverMatch = { - defaultedQueryOptions: QueryObserverOptions + defaultedQueryOptions: DefaultedQueryObserverOptions observer: QueryObserver } diff --git a/src/core/query.ts b/src/core/query.ts index 1b5a3854ad..12e99622ed 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -2,11 +2,9 @@ import { getAbortController, Updater, functionalUpdate, - isValidTimeout, noop, replaceEqualDeep, timeUntilStale, - ensureQueryKeyArray, } from './utils' import type { InitialDataFunction, @@ -14,16 +12,17 @@ import type { QueryOptions, QueryStatus, QueryFunctionContext, - EnsuredQueryKey, QueryMeta, CancelOptions, SetDataOptions, + FetchStatus, } from './types' import type { QueryCache } from './queryCache' import type { QueryObserver } from './queryObserver' import { notifyManager } from './notifyManager' import { getLogger } from './logger' -import { Retryer, isCancelledError } from './retryer' +import { Retryer, isCancelledError, canFetch } from './retryer' +import { Removable } from './removable' // TYPES @@ -51,10 +50,9 @@ export interface QueryState { errorUpdatedAt: number fetchFailureCount: number fetchMeta: any - isFetching: boolean isInvalidated: boolean - isPaused: boolean status: QueryStatus + fetchStatus: FetchStatus } export interface FetchContext< @@ -65,8 +63,9 @@ export interface FetchContext< > { fetchFn: () => unknown | Promise fetchOptions?: FetchOptions + signal?: AbortSignal options: QueryOptions - queryKey: EnsuredQueryKey + queryKey: TQueryKey state: QueryState meta: QueryMeta | undefined } @@ -100,6 +99,7 @@ interface SuccessAction { data: TData | undefined type: 'success' dataUpdatedAt?: number + notifySuccess?: boolean } interface ErrorAction { @@ -146,28 +146,27 @@ export class Query< TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey -> { +> extends Removable { queryKey: TQueryKey queryHash: string options!: QueryOptions initialState: QueryState revertState?: QueryState state: QueryState - cacheTime!: number meta: QueryMeta | undefined + isFetchingOptimistic?: boolean private cache: QueryCache private promise?: Promise - private gcTimeout?: number private retryer?: Retryer private observers: QueryObserver[] private defaultOptions?: QueryOptions private abortSignalConsumed: boolean - private hadObservers: boolean constructor(config: QueryConfig) { + super() + this.abortSignalConsumed = false - this.hadObservers = false this.defaultOptions = config.defaultOptions this.setOptions(config.options) this.observers = [] @@ -177,7 +176,6 @@ export class Query< this.initialState = config.state || this.getDefaultState(this.options) this.state = this.initialState this.meta = config.meta - this.scheduleGc() } private setOptions( @@ -187,11 +185,7 @@ export class Query< this.meta = options?.meta - // Default to 5 minutes if not cache time is set - this.cacheTime = Math.max( - this.cacheTime || 0, - this.options.cacheTime ?? 5 * 60 * 1000 - ) + this.updateCacheTime(this.options.cacheTime) } setDefaultOptions( @@ -200,36 +194,15 @@ export class Query< this.defaultOptions = options } - private scheduleGc(): void { - this.clearGcTimeout() - - if (isValidTimeout(this.cacheTime)) { - this.gcTimeout = setTimeout(() => { - this.optionalRemove() - }, this.cacheTime) - } - } - - private clearGcTimeout() { - clearTimeout(this.gcTimeout) - this.gcTimeout = undefined - } - - private optionalRemove() { - if (!this.observers.length) { - if (this.state.isFetching) { - if (this.hadObservers) { - this.scheduleGc() - } - } else { - this.cache.remove(this) - } + protected optionalRemove() { + if (!this.observers.length && this.state.fetchStatus === 'idle') { + this.cache.remove(this) } } setData( updater: Updater, - options?: SetDataOptions + options?: SetDataOptions & { notifySuccess: boolean } ): TData { const prevData = this.state.data @@ -249,6 +222,7 @@ export class Query< data, type: 'success', dataUpdatedAt: options?.updatedAt, + notifySuccess: options?.notifySuccess, }) return data @@ -268,7 +242,8 @@ export class Query< } destroy(): void { - this.clearGcTimeout() + super.destroy() + this.cancel({ silent: true }) } @@ -281,10 +256,6 @@ export class Query< return this.observers.some(observer => observer.options.enabled !== false) } - isFetching(): boolean { - return this.state.isFetching - } - isStale(): boolean { return ( this.state.isInvalidated || @@ -305,7 +276,7 @@ export class Query< const observer = this.observers.find(x => x.shouldFetchOnWindowFocus()) if (observer) { - observer.refetch() + observer.refetch({ cancelRefetch: false }) } // Continue fetch if currently paused @@ -316,7 +287,7 @@ export class Query< const observer = this.observers.find(x => x.shouldFetchOnReconnect()) if (observer) { - observer.refetch() + observer.refetch({ cancelRefetch: false }) } // Continue fetch if currently paused @@ -326,7 +297,6 @@ export class Query< addObserver(observer: QueryObserver): void { if (this.observers.indexOf(observer) === -1) { this.observers.push(observer) - this.hadObservers = true // Stop the query from being garbage collected this.clearGcTimeout() @@ -343,18 +313,14 @@ export class Query< // If the transport layer does not support cancellation // we'll let the query continue so the result can be cached if (this.retryer) { - if (this.retryer.isTransportCancelable || this.abortSignalConsumed) { + if (this.abortSignalConsumed) { this.retryer.cancel({ revert: true }) } else { this.retryer.cancelRetry() } } - if (this.cacheTime) { - this.scheduleGc() - } else { - this.cache.remove(this) - } + this.scheduleGc() } this.cache.notify({ type: 'observerRemoved', query: this, observer }) @@ -375,7 +341,7 @@ export class Query< options?: QueryOptions, fetchOptions?: FetchOptions ): Promise { - if (this.state.isFetching) { + if (this.state.fetchStatus !== 'idle') { if (this.state.dataUpdatedAt && fetchOptions?.cancelRefetch) { // Silently cancel current fetch if the user wants to cancel refetches this.cancel({ silent: true }) @@ -401,26 +367,32 @@ export class Query< } } - const queryKey = ensureQueryKeyArray(this.queryKey) const abortController = getAbortController() // Create query function context const queryFnContext: QueryFunctionContext = { - queryKey, + queryKey: this.queryKey, pageParam: undefined, meta: this.meta, } - Object.defineProperty(queryFnContext, 'signal', { - enumerable: true, - get: () => { - if (abortController) { - this.abortSignalConsumed = true - return abortController.signal - } - return undefined - }, - }) + // Adds an enumerable signal property to the object that + // which sets abortSignalConsumed to true when the signal + // is read. + const addSignalProperty = (object: unknown) => { + Object.defineProperty(object, 'signal', { + enumerable: true, + get: () => { + if (abortController) { + this.abortSignalConsumed = true + return abortController.signal + } + return undefined + }, + }) + } + + addSignalProperty(queryFnContext) // Create fetch function const fetchFn = () => { @@ -435,12 +407,14 @@ export class Query< const context: FetchContext = { fetchOptions, options: this.options, - queryKey: queryKey, + queryKey: this.queryKey, state: this.state, fetchFn, meta: this.meta, } + addSignalProperty(context) + if (this.options.behavior?.onFetch) { this.options.behavior?.onFetch(context) } @@ -450,7 +424,7 @@ export class Query< // Set to fetching state if not already in it if ( - !this.state.isFetching || + this.state.fetchStatus === 'idle' || this.state.fetchMeta !== context.fetchOptions?.meta ) { this.dispatch({ type: 'fetch', meta: context.fetchOptions?.meta }) @@ -466,10 +440,11 @@ export class Query< // Notify cache callback this.cache.config.onSuccess?.(data, this as Query) - // Remove query after fetching if cache time is 0 - if (this.cacheTime === 0) { - this.optionalRemove() + if (!this.isFetchingOptimistic) { + // Schedule query gc after fetching + this.scheduleGc() } + this.isFetchingOptimistic = false }, onError: (error: TError | { silent?: boolean }) => { // Optimistically update state if needed @@ -488,10 +463,11 @@ export class Query< getLogger().error(error) } - // Remove query after fetching if cache time is 0 - if (this.cacheTime === 0) { - this.optionalRemove() + if (!this.isFetchingOptimistic) { + // Schedule query gc after fetching + this.scheduleGc() } + this.isFetchingOptimistic = false }, onFail: () => { this.dispatch({ type: 'failed' }) @@ -504,6 +480,7 @@ export class Query< }, retry: context.options.retry, retryDelay: context.options.retryDelay, + networkMode: context.options.networkMode, }) this.promise = this.retryer.promise @@ -519,7 +496,7 @@ export class Query< observer.onQueryUpdate(action) }) - this.cache.notify({ query: this, type: 'queryUpdated', action }) + this.cache.notify({ query: this, type: 'updated', action }) }) } @@ -550,10 +527,9 @@ export class Query< errorUpdatedAt: 0, fetchFailureCount: 0, fetchMeta: null, - isFetching: false, isInvalidated: false, - isPaused: false, status: hasData ? 'success' : 'idle', + fetchStatus: 'idle', } } @@ -570,20 +546,21 @@ export class Query< case 'pause': return { ...state, - isPaused: true, + fetchStatus: 'paused', } case 'continue': return { ...state, - isPaused: false, + fetchStatus: 'fetching', } case 'fetch': return { ...state, fetchFailureCount: 0, fetchMeta: action.meta ?? null, - isFetching: true, - isPaused: false, + fetchStatus: canFetch(this.options.networkMode) + ? 'fetching' + : 'paused', status: !state.dataUpdatedAt ? 'loading' : state.status, } case 'success': @@ -594,9 +571,8 @@ export class Query< dataUpdatedAt: action.dataUpdatedAt ?? Date.now(), error: null, fetchFailureCount: 0, - isFetching: false, isInvalidated: false, - isPaused: false, + fetchStatus: 'idle', status: 'success', } case 'error': @@ -612,8 +588,7 @@ export class Query< errorUpdateCount: state.errorUpdateCount + 1, errorUpdatedAt: Date.now(), fetchFailureCount: state.fetchFailureCount + 1, - isFetching: false, - isPaused: false, + fetchStatus: 'idle', status: 'error', } case 'invalidate': diff --git a/src/core/queryCache.ts b/src/core/queryCache.ts index f57b970ead..a5dadc7022 100644 --- a/src/core/queryCache.ts +++ b/src/core/queryCache.ts @@ -8,7 +8,7 @@ import { Action, Query, QueryState } from './query' import type { QueryKey, QueryOptions } from './types' import { notifyManager } from './notifyManager' import type { QueryClient } from './queryClient' -import { Subscribable } from './subscribable' +import { Notifiable } from './notifiable' import { QueryObserver } from './queryObserver' // TYPES @@ -23,34 +23,34 @@ interface QueryHashMap { } interface NotifyEventQueryAdded { - type: 'queryAdded' + type: 'added' query: Query } interface NotifyEventQueryRemoved { - type: 'queryRemoved' + type: 'removed' query: Query } interface NotifyEventQueryUpdated { - type: 'queryUpdated' + type: 'updated' query: Query action: Action } -interface NotifyEventObserverAdded { +interface NotifyEventQueryObserverAdded { type: 'observerAdded' query: Query observer: QueryObserver } -interface NotifyEventObserverRemoved { +interface NotifyEventQueryObserverRemoved { type: 'observerRemoved' query: Query observer: QueryObserver } -interface NotifyEventObserverResultsUpdated { +interface NotifyEventQueryObserverResultsUpdated { type: 'observerResultsUpdated' query: Query } @@ -59,15 +59,13 @@ type QueryCacheNotifyEvent = | NotifyEventQueryAdded | NotifyEventQueryRemoved | NotifyEventQueryUpdated - | NotifyEventObserverAdded - | NotifyEventObserverRemoved - | NotifyEventObserverResultsUpdated - -type QueryCacheListener = (event?: QueryCacheNotifyEvent) => void + | NotifyEventQueryObserverAdded + | NotifyEventQueryObserverRemoved + | NotifyEventQueryObserverResultsUpdated // CLASS -export class QueryCache extends Subscribable { +export class QueryCache extends Notifiable { config: QueryCacheConfig private queries: Query[] @@ -111,7 +109,7 @@ export class QueryCache extends Subscribable { this.queriesMap[query.queryHash] = query this.queries.push(query) this.notify({ - type: 'queryAdded', + type: 'added', query, }) } @@ -129,7 +127,7 @@ export class QueryCache extends Subscribable { delete this.queriesMap[query.queryHash] } - this.notify({ type: 'queryRemoved', query }) + this.notify({ type: 'removed', query }) } } @@ -179,14 +177,6 @@ export class QueryCache extends Subscribable { : this.queries } - notify(event: QueryCacheNotifyEvent) { - notifyManager.batch(() => { - this.listeners.forEach(listener => { - listener(event) - }) - }) - } - onFocus(): void { notifyManager.batch(() => { this.queries.forEach(query => { diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index cb9d6ba181..eae1f3302d 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -36,7 +36,7 @@ import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { notifyManager } from './notifyManager' import { infiniteQueryBehavior } from './infiniteQueryBehavior' -import { CancelOptions } from './types' +import { CancelOptions, DefaultedQueryObserverOptions } from './types' // TYPES @@ -77,13 +77,13 @@ export class QueryClient { mount(): void { this.unsubscribeFocus = focusManager.subscribe(() => { - if (focusManager.isFocused() && onlineManager.isOnline()) { + if (focusManager.isFocused()) { this.mutationCache.onFocus() this.queryCache.onFocus() } }) this.unsubscribeOnline = onlineManager.subscribe(() => { - if (focusManager.isFocused() && onlineManager.isOnline()) { + if (onlineManager.isOnline()) { this.mutationCache.onOnline() this.queryCache.onOnline() } @@ -99,7 +99,7 @@ export class QueryClient { isFetching(queryKey?: QueryKey, filters?: QueryFilters): number isFetching(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): number { const [filters] = parseFilterArgs(arg1, arg2) - filters.fetching = true + filters.fetchStatus = 'fetching' return this.queryCache.findAll(filters).length } @@ -136,7 +136,7 @@ export class QueryClient { const defaultedOptions = this.defaultQueryOptions(parsedOptions) return this.queryCache .build(this, defaultedOptions) - .setData(updater, options) + .setData(updater, { ...options, notifySuccess: false }) } setQueriesData( @@ -203,8 +203,8 @@ export class QueryClient { const queryCache = this.queryCache const refetchFilters: RefetchQueryFilters = { + type: 'active', ...filters, - active: true, } return notifyManager.batch(() => { @@ -255,18 +255,18 @@ export class QueryClient { ): Promise { const [filters, options] = parseFilterArgs(arg1, arg2, arg3) - const refetchFilters: RefetchQueryFilters = { - ...filters, - // if filters.refetchActive is not provided and filters.active is explicitly false, - // e.g. invalidateQueries({ active: false }), we don't want to refetch active queries - active: filters.refetchActive ?? filters.active ?? true, - inactive: filters.refetchInactive ?? false, - } - return notifyManager.batch(() => { this.queryCache.findAll(filters).forEach(query => { query.invalidate() }) + + if (filters?.refetchType === 'none') { + return Promise.resolve() + } + const refetchFilters: RefetchQueryFilters = { + ...filters, + type: filters?.refetchType ?? filters?.type ?? 'active', + } return this.refetchQueries(refetchFilters, options) }) } @@ -291,6 +291,7 @@ export class QueryClient { this.queryCache.findAll(filters).map(query => query.fetch(undefined, { ...options, + cancelRefetch: options?.cancelRefetch ?? true, meta: { refetchPage: filters?.refetchPage }, }) ) @@ -499,28 +500,10 @@ export class QueryClient { .catch(noop) } - cancelMutations(): Promise { - const promises = notifyManager.batch(() => - this.mutationCache.getAll().map(mutation => mutation.cancel()) - ) - return Promise.all(promises).then(noop).catch(noop) - } - resumePausedMutations(): Promise { return this.getMutationCache().resumePausedMutations() } - executeMutation< - TData = unknown, - TError = unknown, - TVariables = void, - TContext = unknown - >( - options: MutationOptions - ): Promise { - return this.mutationCache.build(this, options).execute() - } - getQueryCache(): QueryCache { return this.queryCache } @@ -591,16 +574,30 @@ export class QueryClient { TQueryData, TQueryKey extends QueryKey >( - options?: QueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > - ): QueryObserverOptions { + options?: + | QueryObserverOptions + | DefaultedQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > + ): DefaultedQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > { if (options?._defaulted) { - return options + return options as DefaultedQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > } const defaultedOptions = { @@ -608,13 +605,7 @@ export class QueryClient { ...this.getQueryDefaults(options?.queryKey), ...options, _defaulted: true, - } as QueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > + } if (!defaultedOptions.queryHash && defaultedOptions.queryKey) { defaultedOptions.queryHash = hashQueryKeyByOptions( @@ -623,25 +614,22 @@ export class QueryClient { ) } - return defaultedOptions - } + // dependent default values + if (typeof defaultedOptions.refetchOnReconnect === 'undefined') { + defaultedOptions.refetchOnReconnect = + defaultedOptions.networkMode !== 'always' + } + if (typeof defaultedOptions.useErrorBoundary === 'undefined') { + defaultedOptions.useErrorBoundary = !!defaultedOptions.suspense + } - defaultQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey extends QueryKey - >( - options?: QueryObserverOptions< + return defaultedOptions as DefaultedQueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey > - ): QueryObserverOptions { - return this.defaultQueryOptions(options) } defaultMutationOptions>( diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index aa36cfd2cd..6ed455d60f 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -1,4 +1,4 @@ -import { RefetchQueryFilters } from './types' +import { DefaultedQueryObserverOptions, RefetchPageFilters } from './types' import { isServer, isValidTimeout, @@ -23,7 +23,7 @@ import type { QueryClient } from './queryClient' import { focusManager } from './focusManager' import { Subscribable } from './subscribable' import { getLogger } from './logger' -import { isCancelledError } from './retryer' +import { canFetch, isCancelledError } from './retryer' type QueryObserverListener = ( result: QueryObserverResult @@ -144,7 +144,7 @@ export class QueryObserver< const prevOptions = this.options const prevQuery = this.currentQuery - this.options = this.client.defaultQueryObserverOptions(options) + this.options = this.client.defaultQueryOptions(options) if ( typeof this.options.enabled !== 'undefined' && @@ -210,19 +210,11 @@ export class QueryObserver< TQueryKey > ): QueryObserverResult { - const defaultedOptions = this.client.defaultQueryObserverOptions(options) + const defaultedOptions = this.client.defaultQueryOptions(options) const query = this.client .getQueryCache() - .build( - this.client, - defaultedOptions as QueryOptions< - TQueryFnData, - TError, - TQueryData, - TQueryKey - > - ) + .build(this.client, defaultedOptions) return this.createResult(query, defaultedOptions) } @@ -233,7 +225,7 @@ export class QueryObserver< trackResult( result: QueryObserverResult, - defaultedOptions: QueryObserverOptions< + defaultedOptions: DefaultedQueryObserverOptions< TQueryFnData, TError, TData, @@ -260,7 +252,7 @@ export class QueryObserver< }) }) - if (defaultedOptions.useErrorBoundary || defaultedOptions.suspense) { + if (defaultedOptions.useErrorBoundary) { trackProp('error') } @@ -292,12 +284,15 @@ export class QueryObserver< this.client.getQueryCache().remove(this.currentQuery) } - refetch( - options?: RefetchOptions & RefetchQueryFilters - ): Promise> { + refetch({ + refetchPage, + ...options + }: RefetchOptions & RefetchPageFilters = {}): Promise< + QueryObserverResult + > { return this.fetch({ ...options, - meta: { refetchPage: options?.refetchPage }, + meta: { refetchPage }, }) } @@ -310,27 +305,23 @@ export class QueryObserver< TQueryKey > ): Promise> { - const defaultedOptions = this.client.defaultQueryObserverOptions(options) + const defaultedOptions = this.client.defaultQueryOptions(options) const query = this.client .getQueryCache() - .build( - this.client, - defaultedOptions as QueryOptions< - TQueryFnData, - TError, - TQueryData, - TQueryKey - > - ) + .build(this.client, defaultedOptions) + query.isFetchingOptimistic = true return query.fetch().then(() => this.createResult(query, defaultedOptions)) } protected fetch( - fetchOptions?: ObserverFetchOptions + fetchOptions: ObserverFetchOptions ): Promise> { - return this.executeFetch(fetchOptions).then(() => { + return this.executeFetch({ + ...fetchOptions, + cancelRefetch: fetchOptions.cancelRefetch ?? true, + }).then(() => { this.updateResult() return this.currentResult }) @@ -456,7 +447,7 @@ export class QueryObserver< : this.previousQueryResult const { state } = query - let { dataUpdatedAt, error, errorUpdatedAt, isFetching, status } = state + let { dataUpdatedAt, error, errorUpdatedAt, fetchStatus, status } = state let isPreviousData = false let isPlaceholderData = false let data: TData | undefined @@ -471,7 +462,9 @@ export class QueryObserver< mounted && shouldFetchOptionally(query, prevQuery, options, prevOptions) if (fetchOnMount || fetchOptionally) { - isFetching = true + fetchStatus = canFetch(query.options.networkMode) + ? 'fetching' + : 'paused' if (!dataUpdatedAt) { status = 'loading' } @@ -567,8 +560,11 @@ export class QueryObserver< } } + const isFetching = fetchStatus === 'fetching' + const result: QueryObserverBaseResult = { status, + fetchStatus, isLoading: status === 'loading', isSuccess: status === 'success', isError: status === 'error', @@ -582,9 +578,10 @@ export class QueryObserver< isFetchedAfterMount: state.dataUpdateCount > queryInitialState.dataUpdateCount || state.errorUpdateCount > queryInitialState.errorUpdateCount, - isFetching, + isFetching: isFetching, isRefetching: isFetching && status !== 'loading', isLoadingError: status === 'error' && state.dataUpdatedAt === 0, + isPaused: fetchStatus === 'paused', isPlaceholderData, isPreviousData, isRefetchError: status === 'error' && state.dataUpdatedAt !== 0, @@ -604,27 +601,22 @@ export class QueryObserver< return true } - const { notifyOnChangeProps, notifyOnChangePropsExclusions } = this.options - - if (!notifyOnChangeProps && !notifyOnChangePropsExclusions) { - return true - } + const { notifyOnChangeProps } = this.options - if (notifyOnChangeProps === 'tracked' && !this.trackedProps.length) { + if ( + notifyOnChangeProps === 'all' || + (!notifyOnChangeProps && !this.trackedProps.length) + ) { return true } - const includedProps = - notifyOnChangeProps === 'tracked' - ? this.trackedProps - : notifyOnChangeProps + const includedProps = notifyOnChangeProps ?? this.trackedProps return Object.keys(result).some(key => { const typedKey = key as keyof QueryObserverResult const changed = result[typedKey] !== prevResult[typedKey] const isIncluded = includedProps?.some(x => x === key) - const isExcluded = notifyOnChangePropsExclusions?.some(x => x === key) - return changed && !isExcluded && (!includedProps || isIncluded) + return changed && (!includedProps || isIncluded) }) } @@ -656,17 +648,7 @@ export class QueryObserver< } private updateQuery(): void { - const query = this.client - .getQueryCache() - .build( - this.client, - this.options as QueryOptions< - TQueryFnData, - TError, - TQueryData, - TQueryKey - > - ) + const query = this.client.getQueryCache().build(this.client, this.options) if (query === this.currentQuery) { return @@ -687,7 +669,7 @@ export class QueryObserver< const notifyOptions: NotifyOptions = {} if (action.type === 'success') { - notifyOptions.onSuccess = true + notifyOptions.onSuccess = action.notifySuccess ?? true } else if (action.type === 'error' && !isCancelledError(action.error)) { notifyOptions.onError = true } @@ -719,9 +701,10 @@ export class QueryObserver< // Then the cache listeners if (notifyOptions.cache) { - this.client - .getQueryCache() - .notify({ query: this.currentQuery, type: 'observerResultsUpdated' }) + this.client.getQueryCache().notify({ + query: this.currentQuery, + type: 'observerResultsUpdated', + }) } }) } diff --git a/src/core/removable.ts b/src/core/removable.ts new file mode 100644 index 0000000000..c33f08202c --- /dev/null +++ b/src/core/removable.ts @@ -0,0 +1,35 @@ +import { isValidTimeout } from './utils' + +export abstract class Removable { + cacheTime!: number + private gcTimeout?: number + + destroy(): void { + this.clearGcTimeout() + } + + protected scheduleGc(): void { + this.clearGcTimeout() + + if (isValidTimeout(this.cacheTime)) { + this.gcTimeout = setTimeout(() => { + this.optionalRemove() + }, this.cacheTime) + } + } + + protected updateCacheTime(newCacheTime: number | undefined): void { + // Default to 5 minutes if no cache time is set + this.cacheTime = Math.max( + this.cacheTime || 0, + newCacheTime ?? 5 * 60 * 1000 + ) + } + + protected clearGcTimeout() { + clearTimeout(this.gcTimeout) + this.gcTimeout = undefined + } + + protected abstract optionalRemove(): void +} diff --git a/src/core/retryer.ts b/src/core/retryer.ts index 07621123d4..537f1b28ab 100644 --- a/src/core/retryer.ts +++ b/src/core/retryer.ts @@ -1,7 +1,7 @@ import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { sleep } from './utils' -import { CancelOptions } from './types' +import { CancelOptions, NetworkMode } from './types' // TYPES @@ -15,11 +15,12 @@ interface RetryerConfig { onContinue?: () => void retry?: RetryValue retryDelay?: RetryDelayValue + networkMode: NetworkMode | undefined } export type RetryValue = boolean | number | ShouldRetryFunction -type ShouldRetryFunction = ( +type ShouldRetryFunction = ( failureCount: number, error: TError ) => boolean @@ -35,12 +36,10 @@ function defaultRetryDelay(failureCount: number) { return Math.min(1000 * 2 ** failureCount, 30000) } -interface Cancelable { - cancel(): void -} - -export function isCancelable(value: any): value is Cancelable { - return typeof value?.cancel === 'function' +export function canFetch(networkMode: NetworkMode | undefined): boolean { + return (networkMode ?? 'online') === 'online' + ? onlineManager.isOnline() + : true } export class CancelledError { @@ -66,31 +65,39 @@ export class Retryer { failureCount: number isPaused: boolean isResolved: boolean - isTransportCancelable: boolean promise: Promise - private abort?: () => void - constructor(config: RetryerConfig) { let cancelRetry = false - let cancelFn: ((options?: CancelOptions) => void) | undefined let continueFn: ((value?: unknown) => void) | undefined let promiseResolve: (data: TData) => void let promiseReject: (error: TError) => void - this.abort = config.abort - this.cancel = cancelOptions => cancelFn?.(cancelOptions) + this.cancel = (cancelOptions?: CancelOptions): void => { + if (!this.isResolved) { + reject(new CancelledError(cancelOptions)) + + config.abort?.() + } + } this.cancelRetry = () => { cancelRetry = true } + this.continueRetry = () => { cancelRetry = false } - this.continue = () => continueFn?.() + + const shouldPause = () => + !focusManager.isFocused() || + (config.networkMode !== 'always' && !onlineManager.isOnline()) + + this.continue = () => { + continueFn?.() + } this.failureCount = 0 this.isPaused = false this.isResolved = false - this.isTransportCancelable = false this.promise = new Promise((outerResolve, outerReject) => { promiseResolve = outerResolve promiseReject = outerReject @@ -116,13 +123,19 @@ export class Retryer { const pause = () => { return new Promise(continueResolve => { - continueFn = continueResolve + continueFn = value => { + if (this.isResolved || !shouldPause()) { + return continueResolve(value) + } + } this.isPaused = true config.onPause?.() }).then(() => { continueFn = undefined this.isPaused = false - config.onContinue?.() + if (!this.isResolved) { + config.onContinue?.() + } }) } @@ -142,25 +155,6 @@ export class Retryer { promiseOrValue = Promise.reject(error) } - // Create callback to cancel this fetch - cancelFn = cancelOptions => { - if (!this.isResolved) { - reject(new CancelledError(cancelOptions)) - - this.abort?.() - - // Cancel transport if supported - if (isCancelable(promiseOrValue)) { - try { - promiseOrValue.cancel() - } catch {} - } - } - } - - // Check if the transport layer support cancellation - this.isTransportCancelable = isCancelable(promiseOrValue) - Promise.resolve(promiseOrValue) .then(resolve) .catch(error => { @@ -196,7 +190,7 @@ export class Retryer { sleep(delay) // Pause if the document is not visible or when the device is offline .then(() => { - if (!focusManager.isFocused() || !onlineManager.isOnline()) { + if (shouldPause()) { return pause() } }) @@ -211,6 +205,10 @@ export class Retryer { } // Start loop - run() + if (canFetch(config.networkMode)) { + run() + } else { + pause().then(run) + } } } diff --git a/src/core/tests/hydration.test.tsx b/src/core/tests/hydration.test.tsx index 2de31d1132..77a5e58924 100644 --- a/src/core/tests/hydration.test.tsx +++ b/src/core/tests/hydration.test.tsx @@ -1,4 +1,8 @@ -import { mockNavigatorOnLine, sleep } from '../../react/tests/utils' +import { + executeMutation, + mockNavigatorOnLine, + sleep, +} from '../../reactjs/tests/utils' import { QueryCache } from '../queryCache' import { QueryClient } from '../queryClient' import { dehydrate, hydrate } from '../hydration' @@ -12,12 +16,12 @@ describe('dehydration and rehydration', () => { test('should work with serializeable values', async () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => fetchData('string')) - await queryClient.prefetchQuery('number', () => fetchData(1)) - await queryClient.prefetchQuery('boolean', () => fetchData(true)) - await queryClient.prefetchQuery('null', () => fetchData(null)) - await queryClient.prefetchQuery('array', () => fetchData(['string', 0])) - await queryClient.prefetchQuery('nested', () => + await queryClient.prefetchQuery(['string'], () => fetchData('string')) + await queryClient.prefetchQuery(['number'], () => fetchData(1)) + await queryClient.prefetchQuery(['boolean'], () => fetchData(true)) + await queryClient.prefetchQuery(['null'], () => fetchData(null)) + await queryClient.prefetchQuery(['array'], () => fetchData(['string', 0])) + await queryClient.prefetchQuery(['nested'], () => fetchData({ key: [{ nestedKey: 1 }] }) ) const dehydrated = dehydrate(queryClient) @@ -29,32 +33,32 @@ describe('dehydration and rehydration', () => { const hydrationCache = new QueryCache() const hydrationClient = new QueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find('string')?.state.data).toBe('string') - expect(hydrationCache.find('number')?.state.data).toBe(1) - expect(hydrationCache.find('boolean')?.state.data).toBe(true) - expect(hydrationCache.find('null')?.state.data).toBe(null) - expect(hydrationCache.find('array')?.state.data).toEqual(['string', 0]) - expect(hydrationCache.find('nested')?.state.data).toEqual({ + expect(hydrationCache.find(['string'])?.state.data).toBe('string') + expect(hydrationCache.find(['number'])?.state.data).toBe(1) + expect(hydrationCache.find(['boolean'])?.state.data).toBe(true) + expect(hydrationCache.find(['null'])?.state.data).toBe(null) + expect(hydrationCache.find(['array'])?.state.data).toEqual(['string', 0]) + expect(hydrationCache.find(['nested'])?.state.data).toEqual({ key: [{ nestedKey: 1 }], }) const fetchDataAfterHydration = jest.fn() - await hydrationClient.prefetchQuery('string', fetchDataAfterHydration, { + await hydrationClient.prefetchQuery(['string'], fetchDataAfterHydration, { staleTime: 1000, }) - await hydrationClient.prefetchQuery('number', fetchDataAfterHydration, { + await hydrationClient.prefetchQuery(['number'], fetchDataAfterHydration, { staleTime: 1000, }) - await hydrationClient.prefetchQuery('boolean', fetchDataAfterHydration, { + await hydrationClient.prefetchQuery(['boolean'], fetchDataAfterHydration, { staleTime: 1000, }) - await hydrationClient.prefetchQuery('null', fetchDataAfterHydration, { + await hydrationClient.prefetchQuery(['null'], fetchDataAfterHydration, { staleTime: 1000, }) - await hydrationClient.prefetchQuery('array', fetchDataAfterHydration, { + await hydrationClient.prefetchQuery(['array'], fetchDataAfterHydration, { staleTime: 1000, }) - await hydrationClient.prefetchQuery('nested', fetchDataAfterHydration, { + await hydrationClient.prefetchQuery(['nested'], fetchDataAfterHydration, { staleTime: 1000, }) expect(fetchDataAfterHydration).toHaveBeenCalledTimes(0) @@ -66,7 +70,7 @@ describe('dehydration and rehydration', () => { test('should not dehydrate queries if dehydrateQueries is set to false', async () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => fetchData('string')) + await queryClient.prefetchQuery(['string'], () => fetchData('string')) const dehydrated = dehydrate(queryClient, { dehydrateQueries: false }) @@ -78,7 +82,7 @@ describe('dehydration and rehydration', () => { test('should use the cache time from the client', async () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => fetchData('string'), { + await queryClient.prefetchQuery(['string'], () => fetchData('string'), { cacheTime: 50, }) const dehydrated = dehydrate(queryClient) @@ -92,9 +96,9 @@ describe('dehydration and rehydration', () => { const hydrationCache = new QueryCache() const hydrationClient = new QueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find('string')?.state.data).toBe('string') + expect(hydrationCache.find(['string'])?.state.data).toBe('string') await sleep(100) - expect(hydrationCache.find('string')).toBeTruthy() + expect(hydrationCache.find(['string'])).toBeTruthy() queryClient.clear() hydrationClient.clear() @@ -103,7 +107,7 @@ describe('dehydration and rehydration', () => { test('should be able to provide default options for the hydrated queries', async () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => fetchData('string')) + await queryClient.prefetchQuery(['string'], () => fetchData('string')) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) const parsed = JSON.parse(stringified) @@ -112,7 +116,7 @@ describe('dehydration and rehydration', () => { hydrate(hydrationClient, parsed, { defaultOptions: { queries: { retry: 10 } }, }) - expect(hydrationCache.find('string')?.options.retry).toBe(10) + expect(hydrationCache.find(['string'])?.options.retry).toBe(10) queryClient.clear() hydrationClient.clear() }) @@ -155,9 +159,9 @@ describe('dehydration and rehydration', () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('success', () => fetchData('success')) - queryClient.prefetchQuery('loading', () => fetchData('loading', 10000)) - await queryClient.prefetchQuery('error', () => { + await queryClient.prefetchQuery(['success'], () => fetchData('success')) + queryClient.prefetchQuery(['loading'], () => fetchData('loading', 10000)) + await queryClient.prefetchQuery(['error'], () => { throw new Error() }) const dehydrated = dehydrate(queryClient) @@ -170,9 +174,9 @@ describe('dehydration and rehydration', () => { const hydrationClient = new QueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find('success')).toBeTruthy() - expect(hydrationCache.find('loading')).toBeFalsy() - expect(hydrationCache.find('error')).toBeFalsy() + expect(hydrationCache.find(['success'])).toBeTruthy() + expect(hydrationCache.find(['loading'])).toBeFalsy() + expect(hydrationCache.find(['error'])).toBeFalsy() queryClient.clear() hydrationClient.clear() @@ -182,16 +186,16 @@ describe('dehydration and rehydration', () => { test('should filter queries via shouldDehydrateQuery', async () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => fetchData('string')) - await queryClient.prefetchQuery('number', () => fetchData(1)) + await queryClient.prefetchQuery(['string'], () => fetchData('string')) + await queryClient.prefetchQuery(['number'], () => fetchData(1)) const dehydrated = dehydrate(queryClient, { - shouldDehydrateQuery: query => query.queryKey !== 'string', + shouldDehydrateQuery: query => query.queryKey[0] !== 'string', }) // This is testing implementation details that can change and are not // part of the public API, but is important for keeping the payload small const dehydratedQuery = dehydrated?.queries.find( - query => query?.queryKey === 'string' + query => query?.queryKey[0] === 'string' ) expect(dehydratedQuery).toBeUndefined() @@ -203,8 +207,8 @@ describe('dehydration and rehydration', () => { const hydrationCache = new QueryCache() const hydrationClient = new QueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) - expect(hydrationCache.find('string')).toBeUndefined() - expect(hydrationCache.find('number')?.state.data).toBe(1) + expect(hydrationCache.find(['string'])).toBeUndefined() + expect(hydrationCache.find(['number'])?.state.data).toBe(1) queryClient.clear() hydrationClient.clear() @@ -213,7 +217,7 @@ describe('dehydration and rehydration', () => { test('should not overwrite query in cache if hydrated query is older', async () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => + await queryClient.prefetchQuery(['string'], () => fetchData('string-older', 5) ) const dehydrated = dehydrate(queryClient) @@ -224,12 +228,12 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() const hydrationClient = new QueryClient({ queryCache: hydrationCache }) - await hydrationClient.prefetchQuery('string', () => + await hydrationClient.prefetchQuery(['string'], () => fetchData('string-newer', 5) ) hydrate(hydrationClient, parsed) - expect(hydrationCache.find('string')?.state.data).toBe('string-newer') + expect(hydrationCache.find(['string'])?.state.data).toBe('string-newer') queryClient.clear() hydrationClient.clear() @@ -238,7 +242,7 @@ describe('dehydration and rehydration', () => { test('should overwrite query in cache if hydrated query is newer', async () => { const hydrationCache = new QueryCache() const hydrationClient = new QueryClient({ queryCache: hydrationCache }) - await hydrationClient.prefetchQuery('string', () => + await hydrationClient.prefetchQuery(['string'], () => fetchData('string-older', 5) ) @@ -246,7 +250,7 @@ describe('dehydration and rehydration', () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) - await queryClient.prefetchQuery('string', () => + await queryClient.prefetchQuery(['string'], () => fetchData('string-newer', 5) ) const dehydrated = dehydrate(queryClient) @@ -256,7 +260,7 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) hydrate(hydrationClient, parsed) - expect(hydrationCache.find('string')?.state.data).toBe('string-newer') + expect(hydrationCache.find(['string'])?.state.data).toBe('string-newer') queryClient.clear() hydrationClient.clear() @@ -265,7 +269,7 @@ describe('dehydration and rehydration', () => { test('should be able to dehydrate mutations and continue on hydration', async () => { const consoleMock = jest.spyOn(console, 'error') consoleMock.mockImplementation(() => undefined) - mockNavigatorOnLine(false) + const onlineMock = mockNavigatorOnLine(false) const serverAddTodo = jest .fn() @@ -278,7 +282,7 @@ describe('dehydration and rehydration', () => { const serverClient = new QueryClient() - serverClient.setMutationDefaults('addTodo', { + serverClient.setMutationDefaults(['addTodo'], { mutationFn: serverAddTodo, onMutate: serverOnMutate, onSuccess: serverOnSuccess, @@ -286,12 +290,10 @@ describe('dehydration and rehydration', () => { retryDelay: 10, }) - serverClient - .executeMutation({ - mutationKey: 'addTodo', - variables: { text: 'text' }, - }) - .catch(() => undefined) + executeMutation(serverClient, { + mutationKey: ['addTodo'], + variables: { text: 'text' }, + }).catch(() => undefined) await sleep(50) @@ -302,7 +304,7 @@ describe('dehydration and rehydration', () => { // --- - mockNavigatorOnLine(true) + onlineMock.mockReturnValue(true) const parsed = JSON.parse(stringified) const client = new QueryClient() @@ -316,7 +318,7 @@ describe('dehydration and rehydration', () => { }) const clientOnSuccess = jest.fn() - client.setMutationDefaults('addTodo', { + client.setMutationDefaults(['addTodo'], { mutationFn: clientAddTodo, onMutate: clientOnMutate, onSuccess: clientOnSuccess, @@ -339,6 +341,7 @@ describe('dehydration and rehydration', () => { client.clear() consoleMock.mockRestore() + onlineMock.mockRestore() }) test('should not dehydrate mutations if dehydrateMutations is set to false', async () => { @@ -351,17 +354,15 @@ describe('dehydration and rehydration', () => { const queryClient = new QueryClient() - queryClient.setMutationDefaults('addTodo', { + queryClient.setMutationDefaults(['addTodo'], { mutationFn: serverAddTodo, retry: false, }) - queryClient - .executeMutation({ - mutationKey: 'addTodo', - variables: { text: 'text' }, - }) - .catch(() => undefined) + executeMutation(queryClient, { + mutationKey: ['addTodo'], + variables: { text: 'text' }, + }).catch(() => undefined) await sleep(1) const dehydrated = dehydrate(queryClient, { dehydrateMutations: false }) @@ -382,18 +383,16 @@ describe('dehydration and rehydration', () => { const queryClient = new QueryClient() - queryClient.setMutationDefaults('addTodo', { + queryClient.setMutationDefaults(['addTodo'], { mutationFn: serverAddTodo, retry: 1, retryDelay: 20, }) - queryClient - .executeMutation({ - mutationKey: 'addTodo', - variables: { text: 'text' }, - }) - .catch(() => undefined) + executeMutation(queryClient, { + mutationKey: ['addTodo'], + variables: { text: 'text' }, + }).catch(() => undefined) // Dehydrate mutation between retries await sleep(1) diff --git a/src/core/tests/infiniteQueryBehavior.test.tsx b/src/core/tests/infiniteQueryBehavior.test.tsx index 92d5c6c9f9..a3e57e30e4 100644 --- a/src/core/tests/infiniteQueryBehavior.test.tsx +++ b/src/core/tests/infiniteQueryBehavior.test.tsx @@ -1,5 +1,5 @@ import { waitFor } from '@testing-library/react' -import { queryKey, mockConsoleError } from '../../react/tests/utils' +import { queryKey, mockConsoleError } from '../../reactjs/tests/utils' import { QueryClient, InfiniteQueryObserver, @@ -80,7 +80,7 @@ describe('InfiniteQueryBehavior', () => { ) expect(queryFnSpy).toHaveBeenNthCalledWith(1, { - queryKey: [key], + queryKey: key, pageParam: undefined, meta: undefined, signal: abortSignal, @@ -92,7 +92,7 @@ describe('InfiniteQueryBehavior', () => { await observer.fetchNextPage() expect(queryFnSpy).toHaveBeenNthCalledWith(1, { - queryKey: [key], + queryKey: key, pageParam: 2, meta: undefined, signal: abortSignal, @@ -111,7 +111,7 @@ describe('InfiniteQueryBehavior', () => { }) expect(queryFnSpy).toHaveBeenNthCalledWith(1, { - queryKey: [key], + queryKey: key, pageParam: 2, meta: undefined, signal: abortSignal, diff --git a/src/core/tests/infiniteQueryObserver.test.tsx b/src/core/tests/infiniteQueryObserver.test.tsx index e0fff2a28b..27094fe615 100644 --- a/src/core/tests/infiniteQueryObserver.test.tsx +++ b/src/core/tests/infiniteQueryObserver.test.tsx @@ -1,4 +1,4 @@ -import { sleep, queryKey } from '../../react/tests/utils' +import { sleep, queryKey } from '../../reactjs/tests/utils' import { QueryClient, InfiniteQueryObserver } from '../..' describe('InfiniteQueryObserver', () => { diff --git a/src/core/tests/mutationCache.test.tsx b/src/core/tests/mutationCache.test.tsx index c0bd84f487..ff3ca5cea5 100644 --- a/src/core/tests/mutationCache.test.tsx +++ b/src/core/tests/mutationCache.test.tsx @@ -1,5 +1,11 @@ -import { queryKey, mockConsoleError } from '../../react/tests/utils' -import { MutationCache, QueryClient } from '../..' +import { waitFor } from '@testing-library/react' +import { + queryKey, + mockConsoleError, + sleep, + executeMutation, +} from '../../reactjs/tests/utils' +import { MutationCache, MutationObserver, QueryClient } from '../..' describe('mutationCache', () => { describe('MutationCacheConfig.onError', () => { @@ -11,7 +17,7 @@ describe('mutationCache', () => { const testClient = new QueryClient({ mutationCache: testCache }) try { - await testClient.executeMutation({ + await executeMutation(testClient, { mutationKey: key, variables: 'vars', mutationFn: () => Promise.reject('error'), @@ -34,7 +40,7 @@ describe('mutationCache', () => { const testClient = new QueryClient({ mutationCache: testCache }) try { - await testClient.executeMutation({ + await executeMutation(testClient, { mutationKey: key, variables: 'vars', mutationFn: () => Promise.resolve({ data: 5 }), @@ -62,7 +68,7 @@ describe('mutationCache', () => { const testClient = new QueryClient({ mutationCache: testCache }) try { - await testClient.executeMutation({ + await executeMutation(testClient, { mutationKey: key, variables: 'vars', mutationFn: () => Promise.resolve({ data: 5 }), @@ -82,17 +88,17 @@ describe('mutationCache', () => { const testCache = new MutationCache() const testClient = new QueryClient({ mutationCache: testCache }) const key = ['mutation', 'vars'] - await testClient.executeMutation({ + await executeMutation(testClient, { mutationKey: key, variables: 'vars', mutationFn: () => Promise.resolve(), }) const [mutation] = testCache.getAll() expect(testCache.find({ mutationKey: key })).toEqual(mutation) - expect(testCache.find({ mutationKey: 'mutation', exact: false })).toEqual( - mutation - ) - expect(testCache.find({ mutationKey: 'unknown' })).toEqual(undefined) + expect( + testCache.find({ mutationKey: ['mutation'], exact: false }) + ).toEqual(mutation) + expect(testCache.find({ mutationKey: ['unknown'] })).toEqual(undefined) expect( testCache.find({ predicate: m => m.options.variables === 'vars' }) ).toEqual(mutation) @@ -103,30 +109,161 @@ describe('mutationCache', () => { test('should filter correctly', async () => { const testCache = new MutationCache() const testClient = new QueryClient({ mutationCache: testCache }) - await testClient.executeMutation({ + await executeMutation(testClient, { mutationKey: ['a', 1], variables: 1, mutationFn: () => Promise.resolve(), }) - await testClient.executeMutation({ + await executeMutation(testClient, { mutationKey: ['a', 2], variables: 2, mutationFn: () => Promise.resolve(), }) - await testClient.executeMutation({ - mutationKey: 'b', + await executeMutation(testClient, { + mutationKey: ['b'], mutationFn: () => Promise.resolve(), }) const [mutation1, mutation2] = testCache.getAll() expect( - testCache.findAll({ mutationKey: 'a', exact: false }) + testCache.findAll({ mutationKey: ['a'], exact: false }) ).toHaveLength(2) expect(testCache.find({ mutationKey: ['a', 1] })).toEqual(mutation1) - expect(testCache.findAll({ mutationKey: 'unknown' })).toEqual([]) + expect(testCache.findAll({ mutationKey: ['unknown'] })).toEqual([]) expect( testCache.findAll({ predicate: m => m.options.variables === 2 }) ).toEqual([mutation2]) }) }) + + describe('garbage collection', () => { + test('should remove unused mutations after cacheTime has elapsed', async () => { + const testCache = new MutationCache() + const testClient = new QueryClient({ mutationCache: testCache }) + const onSuccess = jest.fn() + await executeMutation(testClient, { + mutationKey: ['a', 1], + variables: 1, + cacheTime: 10, + mutationFn: () => Promise.resolve(), + onSuccess, + }) + + expect(testCache.getAll()).toHaveLength(1) + await sleep(10) + await waitFor(() => { + expect(testCache.getAll()).toHaveLength(0) + }) + expect(onSuccess).toHaveBeenCalledTimes(1) + }) + + test('should not remove mutations if there are active observers', async () => { + const queryClient = new QueryClient() + const observer = new MutationObserver(queryClient, { + variables: 1, + cacheTime: 10, + mutationFn: () => Promise.resolve(), + }) + const unsubscribe = observer.subscribe() + + expect(queryClient.getMutationCache().getAll()).toHaveLength(0) + observer.mutate(1) + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + await sleep(10) + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + unsubscribe() + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + await sleep(10) + await waitFor(() => { + expect(queryClient.getMutationCache().getAll()).toHaveLength(0) + }) + }) + + test('should only remove when the last observer unsubscribes', async () => { + const queryClient = new QueryClient() + const observer1 = new MutationObserver(queryClient, { + variables: 1, + cacheTime: 10, + mutationFn: async () => { + await sleep(10) + return 'update1' + }, + }) + + const observer2 = new MutationObserver(queryClient, { + cacheTime: 10, + mutationFn: async () => { + await sleep(10) + return 'update2' + }, + }) + + await observer1.mutate() + + // we currently have no way to add multiple observers to the same mutation + const currentMutation = observer1['currentMutation']! + currentMutation?.addObserver(observer1) + currentMutation?.addObserver(observer2) + + expect(currentMutation['observers'].length).toEqual(2) + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + + currentMutation?.removeObserver(observer1) + currentMutation?.removeObserver(observer2) + expect(currentMutation['observers'].length).toEqual(0) + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + // wait for cacheTime to gc + await sleep(10) + await waitFor(() => { + expect(queryClient.getMutationCache().getAll()).toHaveLength(0) + }) + }) + + test('should be garbage collected later when unsubscribed and mutation is loading', async () => { + const queryClient = new QueryClient() + const onSuccess = jest.fn() + const observer = new MutationObserver(queryClient, { + variables: 1, + cacheTime: 10, + mutationFn: async () => { + await sleep(20) + return 'data' + }, + onSuccess, + }) + const unsubscribe = observer.subscribe() + observer.mutate(1) + unsubscribe() + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + await sleep(10) + // unsubscribe should not remove even though cacheTime has elapsed b/c mutation is still loading + expect(queryClient.getMutationCache().getAll()).toHaveLength(1) + await sleep(10) + // should be removed after an additional cacheTime wait + await waitFor(() => { + expect(queryClient.getMutationCache().getAll()).toHaveLength(0) + }) + expect(onSuccess).toHaveBeenCalledTimes(1) + }) + + test('should call callbacks even with cacheTime 0 and mutation still loading', async () => { + const queryClient = new QueryClient() + const onSuccess = jest.fn() + const observer = new MutationObserver(queryClient, { + variables: 1, + cacheTime: 0, + mutationFn: async () => { + return 'data' + }, + onSuccess, + }) + const unsubscribe = observer.subscribe() + observer.mutate(1) + unsubscribe() + await waitFor(() => { + expect(queryClient.getMutationCache().getAll()).toHaveLength(0) + }) + expect(onSuccess).toHaveBeenCalledTimes(1) + }) + }) }) diff --git a/src/core/tests/mutationObserver.test.tsx b/src/core/tests/mutationObserver.test.tsx index 68521fb4fa..c1f577f936 100644 --- a/src/core/tests/mutationObserver.test.tsx +++ b/src/core/tests/mutationObserver.test.tsx @@ -1,5 +1,5 @@ import { waitFor } from '@testing-library/react' -import { sleep } from '../../react/tests/utils' +import { sleep } from '../../reactjs/tests/utils' import { QueryClient, MutationObserver } from '../..' describe('mutationObserver', () => { diff --git a/src/core/tests/mutations.test.tsx b/src/core/tests/mutations.test.tsx index f606ee6028..14260c38db 100644 --- a/src/core/tests/mutations.test.tsx +++ b/src/core/tests/mutations.test.tsx @@ -1,5 +1,10 @@ import { QueryClient } from '../..' -import { mockConsoleError, queryKey, sleep } from '../../react/tests/utils' +import { + executeMutation, + mockConsoleError, + queryKey, + sleep, +} from '../../reactjs/tests/utils' import { MutationState } from '../mutation' import { MutationObserver } from '../mutationObserver' @@ -16,7 +21,7 @@ describe('mutations', () => { }) test('mutate should trigger a mutation', async () => { - const result = await queryClient.executeMutation({ + const result = await executeMutation(queryClient, { mutationFn: async (text: string) => text, variables: 'todo', }) @@ -48,7 +53,7 @@ describe('mutations', () => { mutationFn: async (text: string) => text, }) - const result = await queryClient.executeMutation({ + const result = await executeMutation(queryClient, { mutationKey: key, variables: 'todo', }) @@ -345,7 +350,7 @@ describe('mutations', () => { expect(currentMutation['observers'].length).toEqual(1) }) - test('executeMutation should throw an error if no mutationFn found', async () => { + test('mutate should throw an error if no mutationFn found', async () => { const consoleMock = mockConsoleError() const mutation = new MutationObserver(queryClient, { @@ -371,14 +376,14 @@ describe('mutations', () => { }) const observer = new MutationObserver(queryClient, { - mutationKey: 'key', + mutationKey: ['key'], mutationFn, }) observer.mutate() const mutation = queryClient .getMutationCache() - .find({ mutationKey: 'key' })! + .find({ mutationKey: ['key'] })! await sleep(10) // Force current mutation retryer to be undefined @@ -393,7 +398,7 @@ describe('mutations', () => { test('reducer should return the state for an unknown action type', async () => { const observer = new MutationObserver(queryClient, { - mutationKey: 'key', + mutationKey: ['key'], mutationFn: async () => 'data', }) @@ -402,7 +407,7 @@ describe('mutations', () => { observer.mutate() const mutation = queryClient .getMutationCache() - .find({ mutationKey: 'key' })! + .find({ mutationKey: ['key'] })! const prevState = observer.getCurrentResult() spy.mockReset() diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx index f2fcae9846..c4e5739735 100644 --- a/src/core/tests/queriesObserver.test.tsx +++ b/src/core/tests/queriesObserver.test.tsx @@ -1,5 +1,5 @@ import { waitFor } from '@testing-library/react' -import { sleep, queryKey } from '../../react/tests/utils' +import { sleep, queryKey } from '../../reactjs/tests/utils' import { QueryClient, QueriesObserver, @@ -55,34 +55,6 @@ describe('queriesObserver', () => { expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }]) }) - test('should return same value for multiple falsy query keys', async () => { - const queryFn1 = jest.fn().mockReturnValue(1) - const queryFn2 = jest.fn().mockReturnValue(2) - const observer = new QueriesObserver(queryClient, [ - { queryKey: undefined, queryFn: queryFn1 }, - ]) - const results: QueryObserverResult[][] = [] - results.push(observer.getCurrentResult()) - const unsubscribe = observer.subscribe(result => { - results.push(result) - }) - await sleep(1) - observer.setQueries([ - { queryKey: undefined, queryFn: queryFn1 }, - { queryKey: '', queryFn: queryFn2 }, - ]) - await sleep(1) - unsubscribe() - expect(results.length).toBe(4) - expect(results[0]).toMatchObject([{ status: 'idle', data: undefined }]) - expect(results[1]).toMatchObject([{ status: 'loading', data: undefined }]) - expect(results[2]).toMatchObject([{ status: 'success', data: 1 }]) - expect(results[3]).toMatchObject([ - { status: 'success', data: 1 }, - { status: 'success', data: 1 }, - ]) - }) - test('should update when a query updates', async () => { const key1 = queryKey() const key2 = queryKey() @@ -146,11 +118,11 @@ describe('queriesObserver', () => { observer.setQueries([{ queryKey: key2, queryFn: queryFn2 }]) await sleep(1) const queryCache = queryClient.getQueryCache() - expect(queryCache.find(key1, { active: true })).toBeUndefined() - expect(queryCache.find(key2, { active: true })).toBeDefined() + expect(queryCache.find(key1, { type: 'active' })).toBeUndefined() + expect(queryCache.find(key2, { type: 'active' })).toBeDefined() unsubscribe() - expect(queryCache.find(key1, { active: true })).toBeUndefined() - expect(queryCache.find(key2, { active: true })).toBeUndefined() + expect(queryCache.find(key1, { type: 'active' })).toBeUndefined() + expect(queryCache.find(key2, { type: 'active' })).toBeUndefined() expect(results.length).toBe(6) expect(results[0]).toMatchObject([ { status: 'idle', data: undefined }, diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index efdc4894e6..118ac6662b 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -3,7 +3,7 @@ import { queryKey, mockVisibilityState, mockConsoleError, -} from '../../react/tests/utils' +} from '../../reactjs/tests/utils' import { QueryCache, QueryClient, @@ -13,6 +13,7 @@ import { onlineManager, QueryFunctionContext, } from '../..' +import { waitFor } from '@testing-library/react' describe('query', () => { let queryClient: QueryClient @@ -46,10 +47,8 @@ describe('query', () => { it('should continue retry after focus regain and resolve all promises', async () => { const key = queryKey() - const originalVisibilityState = document.visibilityState - // make page unfocused - mockVisibilityState('hidden') + const visibilityMock = mockVisibilityState('hidden') let count = 0 let result @@ -83,7 +82,7 @@ describe('query', () => { expect(result).toBeUndefined() // Reset visibilityState to original value - mockVisibilityState(originalVisibilityState) + visibilityMock.mockRestore() window.dispatchEvent(new FocusEvent('focus')) // There should not be a result yet @@ -144,10 +143,8 @@ describe('query', () => { it('should throw a CancelledError when a paused query is cancelled', async () => { const key = queryKey() - const originalVisibilityState = document.visibilityState - // make page unfocused - mockVisibilityState('hidden') + const visibilityMock = mockVisibilityState('hidden') let count = 0 let result @@ -182,7 +179,7 @@ describe('query', () => { expect(isCancelledError(result)).toBe(true) // Reset visibilityState to original value - mockVisibilityState(originalVisibilityState) + visibilityMock.mockRestore() window.dispatchEvent(new FocusEvent('focus')) }) @@ -190,7 +187,10 @@ describe('query', () => { const key = queryKey() const queryFn = jest - .fn, [QueryFunctionContext]>() + .fn< + Promise<'data'>, + [QueryFunctionContext>] + >() .mockResolvedValue('data') queryClient.prefetchQuery(key, queryFn) @@ -201,7 +201,7 @@ describe('query', () => { const args = queryFn.mock.calls[0]![0] expect(args).toBeDefined() expect(args.pageParam).toBeUndefined() - expect(args.queryKey).toEqual([key]) + expect(args.queryKey).toEqual(key) if (typeof AbortSignal === 'function') { expect(args.signal).toBeInstanceOf(AbortSignal) } else { @@ -277,7 +277,10 @@ describe('query', () => { test('should provide an AbortSignal to the queryFn that provides info about the cancellation state', async () => { const key = queryKey() - const queryFn = jest.fn, [QueryFunctionContext]>() + const queryFn = jest.fn< + Promise, + [QueryFunctionContext>] + >() const onAbort = jest.fn() const abortListener = jest.fn() let error @@ -329,43 +332,6 @@ describe('query', () => { expect(isCancelledError(error)).toBe(true) }) - test('should call cancel() fn if it was provided and not continue when last observer unsubscribed', async () => { - const key = queryKey() - - const cancel = jest.fn() - - queryClient.prefetchQuery(key, async () => { - const promise = new Promise((resolve, reject) => { - sleep(100).then(() => resolve('data')) - cancel.mockImplementation(() => { - reject(new Error('Cancelled')) - }) - }) as any - promise.cancel = cancel - return promise - }) - - await sleep(10) - - // Subscribe and unsubscribe to simulate cancellation because the last observer unsubscribed - const observer = new QueryObserver(queryClient, { - queryKey: key, - enabled: false, - }) - const unsubscribe = observer.subscribe() - unsubscribe() - - await sleep(100) - - const query = queryCache.find(key)! - - expect(cancel).toHaveBeenCalled() - expect(query.state).toMatchObject({ - data: undefined, - status: 'idle', - }) - }) - test('should not continue if explicitly cancelled', async () => { const key = queryKey() @@ -507,7 +473,6 @@ describe('query', () => { }) test('queries with cacheTime 0 should be removed immediately after unsubscribing', async () => { - const consoleMock = mockConsoleError() const key = queryKey() let count = 0 const observer = new QueryObserver(queryClient, { @@ -521,13 +486,12 @@ describe('query', () => { }) const unsubscribe1 = observer.subscribe() unsubscribe1() - await sleep(10) + await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) const unsubscribe2 = observer.subscribe() unsubscribe2() - await sleep(10) - expect(count).toBe(2) - expect(queryCache.find(key)).toBeUndefined() - consoleMock.mockRestore() + + await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) + expect(count).toBe(1) }) test('should be garbage collected when unsubscribed to', async () => { @@ -541,7 +505,7 @@ describe('query', () => { const unsubscribe = observer.subscribe() expect(queryCache.find(key)).toBeDefined() unsubscribe() - expect(queryCache.find(key)).toBeUndefined() + await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) }) test('should be garbage collected later when unsubscribed and query is fetching', async () => { @@ -564,7 +528,7 @@ describe('query', () => { expect(queryCache.find(key)).toBeDefined() await sleep(10) // should be removed after an additional staleTime wait - expect(queryCache.find(key)).toBeUndefined() + await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) }) test('should not be garbage collected unless there are no subscribers', async () => { diff --git a/src/core/tests/queryCache.test.tsx b/src/core/tests/queryCache.test.tsx index cf26eb3ef7..e9fcba22cc 100644 --- a/src/core/tests/queryCache.test.tsx +++ b/src/core/tests/queryCache.test.tsx @@ -1,4 +1,4 @@ -import { sleep, queryKey, mockConsoleError } from '../../react/tests/utils' +import { sleep, queryKey, mockConsoleError } from '../../reactjs/tests/utils' import { QueryCache, QueryClient } from '../..' import { Query } from '.././query' @@ -23,7 +23,7 @@ describe('queryCache', () => { queryClient.setQueryData(key, 'foo') const query = queryCache.find(key) await sleep(1) - expect(subscriber).toHaveBeenCalledWith({ query, type: 'queryAdded' }) + expect(subscriber).toHaveBeenCalledWith({ query, type: 'added' }) unsubscribe() }) @@ -43,7 +43,7 @@ describe('queryCache', () => { queryClient.prefetchQuery(key, () => 'data') const query = queryCache.find(key) await sleep(100) - expect(callback).toHaveBeenCalledWith({ query, type: 'queryAdded' }) + expect(callback).toHaveBeenCalledWith({ query, type: 'added' }) }) test('should notify subscribers when new query with initialData is added', async () => { @@ -78,6 +78,7 @@ describe('queryCache', () => { test('should filter correctly', async () => { const key1 = queryKey() const key2 = queryKey() + const keyFetching = queryKey() await queryClient.prefetchQuery(key1, () => 'data1') await queryClient.prefetchQuery(key2, () => 'data2') await queryClient.prefetchQuery([{ a: 'a', b: 'b' }], () => 'data3') @@ -89,23 +90,24 @@ describe('queryCache', () => { const query4 = queryCache.find(['posts', 1])! expect(queryCache.findAll(key1)).toEqual([query1]) - expect(queryCache.findAll([key1])).toEqual([query1]) + // wrapping in an extra array doesn't yield the same results anymore since v4 because keys need to be an array + expect(queryCache.findAll([key1])).toEqual([]) expect(queryCache.findAll()).toEqual([query1, query2, query3, query4]) expect(queryCache.findAll({})).toEqual([query1, query2, query3, query4]) - expect(queryCache.findAll(key1, { active: false })).toEqual([query1]) - expect(queryCache.findAll(key1, { active: true })).toEqual([]) + expect(queryCache.findAll(key1, { type: 'inactive' })).toEqual([query1]) + expect(queryCache.findAll(key1, { type: 'active' })).toEqual([]) expect(queryCache.findAll(key1, { stale: true })).toEqual([]) expect(queryCache.findAll(key1, { stale: false })).toEqual([query1]) - expect(queryCache.findAll(key1, { stale: false, active: true })).toEqual( - [] - ) expect( - queryCache.findAll(key1, { stale: false, active: false }) + queryCache.findAll(key1, { stale: false, type: 'active' }) + ).toEqual([]) + expect( + queryCache.findAll(key1, { stale: false, type: 'inactive' }) ).toEqual([query1]) expect( queryCache.findAll(key1, { stale: false, - active: false, + type: 'inactive', exact: true, }) ).toEqual([query1]) @@ -128,14 +130,34 @@ describe('queryCache', () => { query3, ]) expect(queryCache.findAll([{ a: 'a' }], { stale: true })).toEqual([]) - expect(queryCache.findAll([{ a: 'a' }], { active: true })).toEqual([]) - expect(queryCache.findAll([{ a: 'a' }], { inactive: true })).toEqual([ + expect(queryCache.findAll([{ a: 'a' }], { type: 'active' })).toEqual([]) + expect(queryCache.findAll([{ a: 'a' }], { type: 'inactive' })).toEqual([ query3, ]) expect( queryCache.findAll({ predicate: query => query === query3 }) ).toEqual([query3]) - expect(queryCache.findAll('posts')).toEqual([query4]) + expect(queryCache.findAll(['posts'])).toEqual([query4]) + + expect(queryCache.findAll({ fetchStatus: 'idle' })).toEqual([ + query1, + query2, + query3, + query4, + ]) + expect(queryCache.findAll(key2, { fetchStatus: undefined })).toEqual([ + query2, + ]) + + const promise = queryClient.prefetchQuery(keyFetching, async () => { + await sleep(20) + return 'dataFetching' + }) + expect(queryCache.findAll({ fetchStatus: 'fetching' })).toEqual([ + queryCache.find(keyFetching), + ]) + await promise + expect(queryCache.findAll({ fetchStatus: 'fetching' })).toEqual([]) }) test('should return all the queries when no filters are defined', async () => { diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index 30f8e2996c..53de7d72ae 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -1,11 +1,20 @@ -import { sleep, queryKey, mockConsoleError } from '../../react/tests/utils' +import { waitFor } from '@testing-library/react' +import '@testing-library/jest-dom' +import React from 'react' + +import { + sleep, + queryKey, + mockConsoleError, + renderWithClient, +} from '../../reactjs/tests/utils' import { + useQuery, InfiniteQueryObserver, QueryCache, QueryClient, QueryFunction, QueryObserver, - MutationObserver, } from '../..' import { focusManager, onlineManager } from '..' @@ -166,19 +175,6 @@ describe('queryClient', () => { expect(queryClient.getQueryData(key)).toBe('qux') }) - test('should use the same query when using similar string or array query keys', () => { - const key = queryKey() - queryClient.setQueryData(key, '1') - expect(queryClient.getQueryData(key)).toBe('1') - expect(queryClient.getQueryData([key])).toBe('1') - queryClient.setQueryData([key], '2') - expect(queryClient.getQueryData(key)).toBe('2') - expect(queryClient.getQueryData([key])).toBe('2') - queryClient.setQueryData(key, '1') - expect(queryClient.getQueryData(key)).toBe('1') - expect(queryClient.getQueryData([key])).toBe('1') - }) - test('should accept an update function', () => { const key = queryKey() @@ -219,6 +215,62 @@ describe('queryClient', () => { expect(queryCache.find(key)!.state.data).toBe(newData) }) + + test('should not call onSuccess callback of active observers', async () => { + const key = queryKey() + const onSuccess = jest.fn() + + function Page() { + const state = useQuery(key, () => 'data', { onSuccess }) + return ( +
+
data: {state.data}
+ +
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('data: data')) + rendered.getByRole('button', { name: /setQueryData/i }).click() + await waitFor(() => rendered.getByText('data: newData')) + + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(onSuccess).toHaveBeenCalledWith('data') + }) + + test('should respect updatedAt', async () => { + const key = queryKey() + + function Page() { + const state = useQuery(key, () => 'data') + return ( +
+
data: {state.data}
+
dataUpdatedAt: {state.dataUpdatedAt}
+ +
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('data: data')) + rendered.getByRole('button', { name: /setQueryData/i }).click() + await waitFor(() => rendered.getByText('data: newData')) + await waitFor(() => { + expect(rendered.getByText('dataUpdatedAt: 100')).toBeInTheDocument() + }) + }) }) describe('setQueriesData', () => { @@ -226,7 +278,10 @@ describe('queryClient', () => { queryClient.setQueryData(['key', 1], 1) queryClient.setQueryData(['key', 2], 2) - const result = queryClient.setQueriesData('key', old => old! + 5) + const result = queryClient.setQueriesData( + ['key'], + old => old! + 5 + ) expect(result).toEqual([ [['key', 1], 6], @@ -252,10 +307,10 @@ describe('queryClient', () => { }) test('should not update non existing queries', () => { - const result = queryClient.setQueriesData('key', 'data') + const result = queryClient.setQueriesData(['key'], 'data') expect(result).toEqual([]) - expect(queryClient.getQueryData('key')).toBe(undefined) + expect(queryClient.getQueryData(['key'])).toBe(undefined) }) }) @@ -312,8 +367,8 @@ describe('queryClient', () => { describe('fetchQuery', () => { test('should not type-error with strict query key', async () => { type StrictData = 'data' - type StrictQueryKey = ['strict', string] - const key: StrictQueryKey = ['strict', queryKey()] + type StrictQueryKey = ['strict', ...ReturnType] + const key: StrictQueryKey = ['strict', ...queryKey()] const fetchFn: QueryFunction = () => Promise.resolve('data') @@ -361,9 +416,10 @@ describe('queryClient', () => { }, { cacheTime: 0 } ) - const result2 = queryClient.getQueryData(key1) expect(result).toEqual(1) - expect(result2).toEqual(undefined) + await waitFor(() => + expect(queryClient.getQueryData(key1)).toEqual(undefined) + ) }) test('should keep a query in cache if cache time is Infinity', async () => { @@ -424,8 +480,8 @@ describe('queryClient', () => { describe('fetchInfiniteQuery', () => { test('should not type-error with strict query key', async () => { type StrictData = string - type StrictQueryKey = ['strict', string] - const key: StrictQueryKey = ['strict', queryKey()] + type StrictQueryKey = ['strict', ...ReturnType] + const key: StrictQueryKey = ['strict', ...queryKey()] const data = { pages: ['data'], @@ -466,8 +522,8 @@ describe('queryClient', () => { describe('prefetchInfiniteQuery', () => { test('should not type-error with strict query key', async () => { type StrictData = 'data' - type StrictQueryKey = ['strict', string] - const key: StrictQueryKey = ['strict', queryKey()] + type StrictQueryKey = ['strict', ...ReturnType] + const key: StrictQueryKey = ['strict', ...queryKey()] const fetchFn: QueryFunction = () => Promise.resolve('data') @@ -506,8 +562,8 @@ describe('queryClient', () => { describe('prefetchQuery', () => { test('should not type-error with strict query key', async () => { type StrictData = 'data' - type StrictQueryKey = ['strict', string] - const key: StrictQueryKey = ['strict', queryKey()] + type StrictQueryKey = ['strict', ...ReturnType] + const key: StrictQueryKey = ['strict', ...queryKey()] const fetchFn: QueryFunction = () => Promise.resolve('data') @@ -688,7 +744,7 @@ describe('queryClient', () => { staleTime: Infinity, }) const unsubscribe = observer.subscribe() - await queryClient.refetchQueries({ active: true, stale: false }) + await queryClient.refetchQueries({ type: 'active', stale: false }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(1) @@ -709,7 +765,8 @@ describe('queryClient', () => { queryClient.invalidateQueries(key1) await queryClient.refetchQueries({ stale: true }) unsubscribe() - expect(queryFn1).toHaveBeenCalledTimes(2) + // fetchQuery, observer mount, invalidation (cancels observer mount) and refetch + expect(queryFn1).toHaveBeenCalledTimes(4) expect(queryFn2).toHaveBeenCalledTimes(1) }) @@ -726,7 +783,10 @@ describe('queryClient', () => { queryFn: queryFn1, }) const unsubscribe = observer.subscribe() - await queryClient.refetchQueries({ active: true, stale: true }) + await queryClient.refetchQueries( + { type: 'active', stale: true }, + { cancelRefetch: false } + ) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(1) @@ -764,7 +824,7 @@ describe('queryClient', () => { staleTime: Infinity, }) const unsubscribe = observer.subscribe() - await queryClient.refetchQueries({ active: true, inactive: true }) + await queryClient.refetchQueries({ type: 'all' }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(2) @@ -783,7 +843,7 @@ describe('queryClient', () => { staleTime: Infinity, }) const unsubscribe = observer.subscribe() - await queryClient.refetchQueries({ active: true }) + await queryClient.refetchQueries({ type: 'active' }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(1) @@ -802,31 +862,12 @@ describe('queryClient', () => { staleTime: Infinity, }) const unsubscribe = observer.subscribe() - await queryClient.refetchQueries({ inactive: true }) + await queryClient.refetchQueries({ type: 'inactive' }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(1) expect(queryFn2).toHaveBeenCalledTimes(2) }) - test('should skip refetch for all active and inactive queries', async () => { - const key1 = queryKey() - const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() - await queryClient.fetchQuery(key1, queryFn1) - await queryClient.fetchQuery(key2, queryFn2) - const observer = new QueryObserver(queryClient, { - queryKey: key1, - queryFn: queryFn1, - staleTime: Infinity, - }) - const unsubscribe = observer.subscribe() - await queryClient.refetchQueries({ active: false, inactive: false }) - unsubscribe() - expect(queryFn1).toHaveBeenCalledTimes(1) - expect(queryFn2).toHaveBeenCalledTimes(1) - }) - test('should throw an error if throwOnError option is set to true', async () => { const consoleMock = mockConsoleError() const key1 = queryKey() @@ -891,7 +932,7 @@ describe('queryClient', () => { expect(queryFn2).toHaveBeenCalledTimes(1) }) - test('should not refetch active queries when "refetchActive" is false', async () => { + test('should not refetch active queries when "refetch" is "none"', async () => { const key1 = queryKey() const key2 = queryKey() const queryFn1 = jest.fn() @@ -905,14 +946,14 @@ describe('queryClient', () => { }) const unsubscribe = observer.subscribe() queryClient.invalidateQueries(key1, { - refetchActive: false, + refetchType: 'none', }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(1) expect(queryFn2).toHaveBeenCalledTimes(1) }) - test('should refetch inactive queries when "refetchInactive" is true', async () => { + test('should refetch inactive queries when "refetch" is "inactive"', async () => { const key1 = queryKey() const key2 = queryKey() const queryFn1 = jest.fn() @@ -927,14 +968,14 @@ describe('queryClient', () => { }) const unsubscribe = observer.subscribe() queryClient.invalidateQueries(key1, { - refetchInactive: true, + refetchType: 'inactive', }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(1) }) - test('should not refetch active queries when "refetchActive" is not provided and "active" is false', async () => { + test('should refetch active and inactive queries when "refetch" is "all"', async () => { const key1 = queryKey() const key2 = queryKey() const queryFn1 = jest.fn() @@ -947,17 +988,49 @@ describe('queryClient', () => { staleTime: Infinity, }) const unsubscribe = observer.subscribe() - queryClient.invalidateQueries(key1, { - active: false, + queryClient.invalidateQueries({ + refetchType: 'all', }) unsubscribe() - expect(queryFn1).toHaveBeenCalledTimes(1) - expect(queryFn2).toHaveBeenCalledTimes(1) + expect(queryFn1).toHaveBeenCalledTimes(2) + expect(queryFn2).toHaveBeenCalledTimes(2) + }) + + test('should cancel ongoing fetches if cancelRefetch option is set (default value)', async () => { + const key = queryKey() + const abortFn = jest.fn() + let fetchCount = 0 + const observer = new QueryObserver(queryClient, { + queryKey: key, + enabled: false, + initialData: 1, + }) + observer.subscribe() + + queryClient.fetchQuery(key, ({ signal }) => { + const promise = new Promise(resolve => { + fetchCount++ + setTimeout(() => resolve(5), 10) + if (signal) { + signal.addEventListener('abort', abortFn) + } + }) + + return promise + }) + + await queryClient.refetchQueries() + observer.destroy() + if (typeof AbortSignal === 'function') { + expect(abortFn).toHaveBeenCalledTimes(1) + } + expect(fetchCount).toBe(2) }) - test('should cancel ongoing fetches if cancelRefetch option is passed', async () => { + test('should not cancel ongoing fetches if cancelRefetch option is set to false', async () => { const key = queryKey() - const cancelFn = jest.fn() + const abortFn = jest.fn() + let fetchCount = 0 const observer = new QueryObserver(queryClient, { queryKey: key, enabled: false, @@ -965,18 +1038,24 @@ describe('queryClient', () => { }) observer.subscribe() - queryClient.fetchQuery(key, () => { + queryClient.fetchQuery(key, ({ signal }) => { const promise = new Promise(resolve => { + fetchCount++ setTimeout(() => resolve(5), 10) + if (signal) { + signal.addEventListener('abort', abortFn) + } }) - // @ts-expect-error - promise.cancel = cancelFn + return promise }) - await queryClient.refetchQueries(undefined, { cancelRefetch: true }) + await queryClient.refetchQueries(undefined, { cancelRefetch: false }) observer.destroy() - expect(cancelFn).toHaveBeenCalledTimes(1) + if (typeof AbortSignal === 'function') { + expect(abortFn).toHaveBeenCalledTimes(0) + } + expect(fetchCount).toBe(1) }) }) @@ -1104,7 +1183,7 @@ describe('queryClient', () => { await queryClient.invalidateQueries({ queryKey: key, - refetchInactive: true, + refetchType: 'all', refetchPage: (page, _, allPages) => { return page === allPages[0] }, @@ -1136,7 +1215,7 @@ describe('queryClient', () => { await queryClient.resetQueries({ queryKey: key, - inactive: true, + type: 'inactive', refetchPage: (page, _, allPages) => { return page === allPages[0] }, @@ -1149,7 +1228,7 @@ describe('queryClient', () => { }) describe('focusManager and onlineManager', () => { - test('should not notify queryCache and mutationCache if not focused or online', async () => { + test('should notify queryCache and mutationCache if focused', async () => { const testClient = new QueryClient() testClient.mount() @@ -1175,23 +1254,50 @@ describe('queryClient', () => { expect(mutationCacheOnFocusSpy).not.toHaveBeenCalled() focusManager.setFocused(true) - onlineManager.setOnline(false) + expect(queryCacheOnFocusSpy).toHaveBeenCalledTimes(1) + expect(queryCacheOnFocusSpy).toHaveBeenCalledTimes(1) + expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled() expect(mutationCacheOnOnlineSpy).not.toHaveBeenCalled() - focusManager.setFocused(true) + queryCacheOnFocusSpy.mockRestore() + mutationCacheOnFocusSpy.mockRestore() + queryCacheOnOnlineSpy.mockRestore() + mutationCacheOnOnlineSpy.mockRestore() + }) + + test('should notify queryCache and mutationCache if online', async () => { + const testClient = new QueryClient() + testClient.mount() + + const queryCacheOnFocusSpy = jest.spyOn( + testClient.getQueryCache(), + 'onFocus' + ) + const queryCacheOnOnlineSpy = jest.spyOn( + testClient.getQueryCache(), + 'onOnline' + ) + const mutationCacheOnFocusSpy = jest.spyOn( + testClient.getMutationCache(), + 'onFocus' + ) + const mutationCacheOnOnlineSpy = jest.spyOn( + testClient.getMutationCache(), + 'onOnline' + ) + onlineManager.setOnline(false) expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled() - expect(mutationCacheOnOnlineSpy).not.toHaveBeenCalled() - - focusManager.setFocused(false) - onlineManager.setOnline(true) expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled() - expect(mutationCacheOnOnlineSpy).not.toHaveBeenCalled() - testClient.unmount() onlineManager.setOnline(true) - focusManager.setFocused(true) + expect(queryCacheOnOnlineSpy).toHaveBeenCalledTimes(1) + expect(queryCacheOnOnlineSpy).toHaveBeenCalledTimes(1) + + expect(mutationCacheOnFocusSpy).not.toHaveBeenCalled() + expect(mutationCacheOnFocusSpy).not.toHaveBeenCalled() + queryCacheOnFocusSpy.mockRestore() mutationCacheOnFocusSpy.mockRestore() queryCacheOnOnlineSpy.mockRestore() @@ -1199,27 +1305,6 @@ describe('queryClient', () => { }) }) - describe('cancelMutations', () => { - test('should cancel mutations', async () => { - const key = queryKey() - const mutationObserver = new MutationObserver(queryClient, { - mutationKey: key, - mutationFn: async () => { - await sleep(20) - return 'data' - }, - onMutate: text => text, - }) - await mutationObserver.mutate() - const mutation = queryClient - .getMutationCache() - .find({ mutationKey: key })! - const mutationSpy = jest.spyOn(mutation, 'cancel') - queryClient.cancelMutations() - expect(mutationSpy).toHaveBeenCalled() - mutationSpy.mockRestore() - }) - }) describe('setMutationDefaults', () => { test('should update existing mutation defaults', () => { const key = queryKey() diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index 1cba155cbd..31fb1ea27b 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -3,7 +3,7 @@ import { queryKey, mockConsoleError, expectType, -} from '../../react/tests/utils' +} from '../../reactjs/tests/utils' import { QueryClient, QueryObserver, @@ -622,7 +622,7 @@ describe('queryObserver', () => { select: () => selectedData, }) - await observer.refetch({ queryKey: key }) + await observer.refetch() expect(observer.getCurrentResult().data).toBe(selectedData) unsubscribe() diff --git a/src/core/tests/utils.test.tsx b/src/core/tests/utils.test.tsx index e237215270..2297c2a6b7 100644 --- a/src/core/tests/utils.test.tsx +++ b/src/core/tests/utils.test.tsx @@ -2,13 +2,12 @@ import { replaceEqualDeep, partialDeepEqual, isPlainObject, - mapQueryStatusFilter, parseMutationArgs, matchMutation, scheduleMicrotask, } from '../utils' import { QueryClient, QueryCache, setLogger, Logger } from '../..' -import { queryKey } from '../../react/tests/utils' +import { queryKey } from '../../reactjs/tests/utils' import { Mutation } from '../mutation' import { waitFor } from '@testing-library/dom' @@ -340,36 +339,16 @@ describe('core/utils', () => { }) }) - describe('mapQueryStatusFilter', () => { - it.each` - active | inactive | statusFilter - ${true} | ${true} | ${'all'} - ${undefined} | ${undefined} | ${'all'} - ${false} | ${false} | ${'none'} - ${true} | ${false} | ${'active'} - ${true} | ${undefined} | ${'active'} - ${undefined} | ${false} | ${'active'} - ${false} | ${true} | ${'inactive'} - ${undefined} | ${true} | ${'inactive'} - ${false} | ${undefined} | ${'inactive'} - `( - 'returns "$statusFilter" when active is $active, and inactive is $inactive', - ({ active, inactive, statusFilter }) => { - expect(mapQueryStatusFilter(active, inactive)).toBe(statusFilter) - } - ) - }) - describe('parseMutationArgs', () => { it('should return mutation options', () => { - const options = { mutationKey: 'key' } + const options = { mutationKey: ['key'] } expect(parseMutationArgs(options)).toMatchObject(options) }) }) describe('matchMutation', () => { it('should return false if mutationKey options is undefined', () => { - const filters = { mutationKey: 'key1' } + const filters = { mutationKey: ['key1'] } const queryClient = new QueryClient() const mutation = new Mutation({ mutationId: 1, diff --git a/src/core/types.ts b/src/core/types.ts index 583b34a157..e57e06d79d 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -1,13 +1,9 @@ import type { MutationState } from './mutation' import type { QueryBehavior, Query } from './query' import type { RetryValue, RetryDelayValue } from './retryer' -import type { QueryFilters } from './utils' - -export type QueryKey = string | readonly unknown[] -export type EnsuredQueryKey = T extends string - ? [T] - : Exclude +import type { QueryFilters, QueryTypeFilter } from './utils' +export type QueryKey = readonly unknown[] export type QueryFunction< T = unknown, TQueryKey extends QueryKey = QueryKey @@ -17,7 +13,7 @@ export interface QueryFunctionContext< TQueryKey extends QueryKey = QueryKey, TPageParam = any > { - queryKey: EnsuredQueryKey + queryKey: TQueryKey signal?: AbortSignal pageParam?: TPageParam meta: QueryMeta | undefined @@ -48,6 +44,8 @@ export interface InfiniteData { export type QueryMeta = Record +export type NetworkMode = 'online' | 'always' | 'offlineFirst' + export interface QueryOptions< TQueryFnData = unknown, TError = unknown, @@ -62,6 +60,7 @@ export interface QueryOptions< */ retry?: RetryValue retryDelay?: RetryDelayValue + networkMode?: NetworkMode cacheTime?: number isDataEqual?: (oldData: TData | undefined, newData: TData) => boolean queryFn?: QueryFunction @@ -140,7 +139,7 @@ export interface QueryObserverOptions< * If set to `true`, the query will refetch on reconnect if the data is stale. * If set to `false`, the query will not refetch on reconnect. * If set to `'always'`, the query will always refetch on reconnect. - * Defaults to `true`. + * Defaults to the value of `networkOnline` (`true`) */ refetchOnReconnect?: boolean | 'always' /** @@ -158,13 +157,10 @@ export interface QueryObserverOptions< /** * If set, the component will only re-render if any of the listed properties change. * When set to `['data', 'error']`, the component will only re-render when the `data` or `error` properties change. - * When set to `tracked`, access to properties will be tracked, and the component will only re-render when one of the tracked properties change. + * When set to `'all'`, the component will re-render whenever a query is updated. + * By default, access to properties will be tracked, and the component will only re-render when one of the tracked properties change. */ - notifyOnChangeProps?: Array | 'tracked' - /** - * If set, the component will not re-render if any of the listed properties change. - */ - notifyOnChangePropsExclusions?: Array + notifyOnChangeProps?: Array | 'all' /** * This callback will fire any time the query successfully fetches new data or the cache is updated via `setQueryData`. */ @@ -212,6 +208,18 @@ export interface QueryObserverOptions< optimisticResults?: boolean } +type WithRequired = Omit & Required> +export type DefaultedQueryObserverOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey +> = WithRequired< + QueryObserverOptions, + 'useErrorBoundary' | 'refetchOnReconnect' +> + export interface InfiniteQueryObserverOptions< TQueryFnData = unknown, TError = unknown, @@ -270,8 +278,7 @@ export interface RefetchOptions extends ResultOptions { export interface InvalidateQueryFilters extends QueryFilters, RefetchPageFilters { - refetchActive?: boolean - refetchInactive?: boolean + refetchType?: QueryTypeFilter | 'none' } export interface RefetchQueryFilters @@ -296,6 +303,7 @@ export interface FetchPreviousPageOptions extends ResultOptions { } export type QueryStatus = 'idle' | 'loading' | 'error' | 'success' +export type FetchStatus = 'fetching' | 'paused' | 'idle' export interface QueryObserverBaseResult { data: TData | undefined @@ -310,6 +318,7 @@ export interface QueryObserverBaseResult { isIdle: boolean isLoading: boolean isLoadingError: boolean + isPaused: boolean isPlaceholderData: boolean isPreviousData: boolean isRefetchError: boolean @@ -321,6 +330,7 @@ export interface QueryObserverBaseResult { ) => Promise> remove: () => void status: QueryStatus + fetchStatus: FetchStatus } export interface QueryObserverIdleResult @@ -497,7 +507,7 @@ export type InfiniteQueryObserverResult = | InfiniteQueryObserverRefetchErrorResult | InfiniteQueryObserverSuccessResult -export type MutationKey = string | readonly unknown[] +export type MutationKey = readonly unknown[] export type MutationStatus = 'idle' | 'loading' | 'success' | 'error' @@ -537,6 +547,8 @@ export interface MutationOptions< ) => Promise | void retry?: RetryValue retryDelay?: RetryDelayValue + networkMode?: NetworkMode + cacheTime?: number _defaulted?: boolean meta?: MutationMeta } diff --git a/src/core/utils.ts b/src/core/utils.ts index 1b01474502..0c4c9b0fcf 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -1,7 +1,7 @@ import type { Mutation } from './mutation' import type { Query } from './query' -import { EnsuredQueryKey } from './types' import type { + FetchStatus, MutationFunction, MutationKey, MutationOptions, @@ -14,17 +14,13 @@ import type { export interface QueryFilters { /** - * Include or exclude active queries + * Filter to active queries, inactive queries or all queries */ - active?: boolean + type?: QueryTypeFilter /** * Match query key exactly */ exact?: boolean - /** - * Include or exclude inactive queries - */ - inactive?: boolean /** * Include queries matching this predicate function */ @@ -38,9 +34,9 @@ export interface QueryFilters { */ stale?: boolean /** - * Include or exclude fetching queries + * Include queries matching their fetchStatus */ - fetching?: boolean + fetchStatus?: FetchStatus } export interface MutationFilters { @@ -68,7 +64,7 @@ export type Updater = | TOutput | DataUpdateFunction -export type QueryStatusFilter = 'all' | 'active' | 'inactive' | 'none' +export type QueryTypeFilter = 'all' | 'active' | 'inactive' // UTILS @@ -91,14 +87,6 @@ export function isValidTimeout(value: unknown): value is number { return typeof value === 'number' && value >= 0 && value !== Infinity } -export function ensureQueryKeyArray( - value: T -): EnsuredQueryKey { - return (Array.isArray(value) - ? value - : ([value] as unknown)) as EnsuredQueryKey -} - export function difference(array1: T[], array2: T[]): T[] { return array1.filter(x => array2.indexOf(x) === -1) } @@ -173,34 +161,14 @@ export function parseMutationFilterArgs( return isQueryKey(arg1) ? { ...arg2, mutationKey: arg1 } : arg1 } -export function mapQueryStatusFilter( - active?: boolean, - inactive?: boolean -): QueryStatusFilter { - if ( - (active === true && inactive === true) || - (active == null && inactive == null) - ) { - return 'all' - } else if (active === false && inactive === false) { - return 'none' - } else { - // At this point, active|inactive can only be true|false or false|true - // so, when only one value is provided, the missing one has to be the negated value - const isActive = active ?? !inactive - return isActive ? 'active' : 'inactive' - } -} - export function matchQuery( filters: QueryFilters, query: Query ): boolean { const { - active, + type = 'all', exact, - fetching, - inactive, + fetchStatus, predicate, queryKey, stale, @@ -216,16 +184,12 @@ export function matchQuery( } } - const queryStatusFilter = mapQueryStatusFilter(active, inactive) - - if (queryStatusFilter === 'none') { - return false - } else if (queryStatusFilter !== 'all') { + if (type !== 'all') { const isActive = query.isActive() - if (queryStatusFilter === 'active' && !isActive) { + if (type === 'active' && !isActive) { return false } - if (queryStatusFilter === 'inactive' && isActive) { + if (type === 'inactive' && isActive) { return false } } @@ -234,7 +198,10 @@ export function matchQuery( return false } - if (typeof fetching === 'boolean' && query.isFetching() !== fetching) { + if ( + typeof fetchStatus !== 'undefined' && + fetchStatus !== query.state.fetchStatus + ) { return false } @@ -289,17 +256,10 @@ export function hashQueryKeyByOptions( /** * Default query keys hash function. - */ -export function hashQueryKey(queryKey: QueryKey): string { - const asArray = ensureQueryKeyArray(queryKey) - return stableValueHash(asArray) -} - -/** * Hashes the value into a stable hash. */ -export function stableValueHash(value: any): string { - return JSON.stringify(value, (_, val) => +export function hashQueryKey(queryKey: QueryKey): string { + return JSON.stringify(queryKey, (_, val) => isPlainObject(val) ? Object.keys(val) .sort() @@ -315,7 +275,7 @@ export function stableValueHash(value: any): string { * Checks if key `b` partially matches with key `a`. */ export function partialMatchKey(a: QueryKey, b: QueryKey): boolean { - return partialDeepEqual(ensureQueryKeyArray(a), ensureQueryKeyArray(b)) + return partialDeepEqual(a, b) } /** @@ -420,8 +380,8 @@ function hasObjectPrototype(o: any): boolean { return Object.prototype.toString.call(o) === '[object Object]' } -export function isQueryKey(value: any): value is QueryKey { - return typeof value === 'string' || Array.isArray(value) +export function isQueryKey(value: unknown): value is QueryKey { + return Array.isArray(value) } export function isError(value: any): value is Error { diff --git a/src/createAsyncStoragePersistor-experimental/index.ts b/src/createAsyncStoragePersister/index.ts similarity index 91% rename from src/createAsyncStoragePersistor-experimental/index.ts rename to src/createAsyncStoragePersister/index.ts index a9d43a3c76..96731076c6 100644 --- a/src/createAsyncStoragePersistor-experimental/index.ts +++ b/src/createAsyncStoragePersister/index.ts @@ -1,4 +1,4 @@ -import { PersistedClient, Persistor } from '../persistQueryClient-experimental' +import { PersistedClient, Persister } from '../persistQueryClient' interface AsyncStorage { getItem: (key: string) => Promise @@ -6,7 +6,7 @@ interface AsyncStorage { removeItem: (key: string) => Promise } -interface CreateAsyncStoragePersistorOptions { +interface CreateAsyncStoragePersisterOptions { /** The storage client used for setting an retrieving items from cache */ storage: AsyncStorage /** The key to use when storing the cache */ @@ -26,13 +26,13 @@ interface CreateAsyncStoragePersistorOptions { deserialize?: (cachedString: string) => PersistedClient } -export const createAsyncStoragePersistor = ({ +export const createAsyncStoragePersister = ({ storage, key = `REACT_QUERY_OFFLINE_CACHE`, throttleTime = 1000, serialize = JSON.stringify, deserialize = JSON.parse, -}: CreateAsyncStoragePersistorOptions): Persistor => { +}: CreateAsyncStoragePersisterOptions): Persister => { return { persistClient: asyncThrottle( persistedClient => storage.setItem(key, serialize(persistedClient)), diff --git a/src/createWebStoragePersistor-experimental/index.ts b/src/createWebStoragePersister/index.ts similarity index 92% rename from src/createWebStoragePersistor-experimental/index.ts rename to src/createWebStoragePersister/index.ts index 5c4b8a0de6..e2f61c1d65 100644 --- a/src/createWebStoragePersistor-experimental/index.ts +++ b/src/createWebStoragePersister/index.ts @@ -1,7 +1,7 @@ import { noop } from '../core/utils' -import { PersistedClient, Persistor } from '../persistQueryClient-experimental' +import { PersistedClient, Persister } from '../persistQueryClient' -interface CreateWebStoragePersistorOptions { +interface CreateWebStoragePersisterOptions { /** The storage client used for setting an retrieving items from cache */ storage: Storage /** The key to use when storing the cache */ @@ -21,13 +21,13 @@ interface CreateWebStoragePersistorOptions { deserialize?: (cachedString: string) => PersistedClient } -export function createWebStoragePersistor({ +export function createWebStoragePersister({ storage, key = `REACT_QUERY_OFFLINE_CACHE`, throttleTime = 1000, serialize = JSON.stringify, deserialize = JSON.parse, -}: CreateWebStoragePersistorOptions): Persistor { +}: CreateWebStoragePersisterOptions): Persister { //try to save data to storage function trySave(persistedClient: PersistedClient) { try { diff --git a/src/createWebStoragePersistor-experimental/tests/storageIsFull.test.ts b/src/createWebStoragePersister/tests/storageIsFull.test.ts similarity index 61% rename from src/createWebStoragePersistor-experimental/tests/storageIsFull.test.ts rename to src/createWebStoragePersister/tests/storageIsFull.test.ts index 2b19a1d45b..d071c5d87d 100644 --- a/src/createWebStoragePersistor-experimental/tests/storageIsFull.test.ts +++ b/src/createWebStoragePersister/tests/storageIsFull.test.ts @@ -1,6 +1,6 @@ import { dehydrate, MutationCache, QueryCache, QueryClient } from '../../core' -import { sleep } from '../../react/tests/utils' -import { createWebStoragePersistor } from '../index' +import { createWebStoragePersister } from '../index' +import { sleep } from '../../reactjs/tests/utils' function getMockStorage(limitSize?: number) { const dataSet = new Map() @@ -33,23 +33,23 @@ function getMockStorage(limitSize?: number) { } as any) as Storage } -describe('createWebStoragePersistor ', () => { +describe('createWebStoragePersister ', () => { test('basic store and recover', async () => { const queryCache = new QueryCache() const mutationCache = new MutationCache() const queryClient = new QueryClient({ queryCache, mutationCache }) const storage = getMockStorage() - const webStoragePersistor = createWebStoragePersistor({ + const webStoragePersister = createWebStoragePersister({ throttleTime: 0, storage, }) - await queryClient.prefetchQuery('string', () => Promise.resolve('string')) - await queryClient.prefetchQuery('number', () => Promise.resolve(1)) - await queryClient.prefetchQuery('boolean', () => Promise.resolve(true)) - await queryClient.prefetchQuery('null', () => Promise.resolve(null)) - await queryClient.prefetchQuery('array', () => + await queryClient.prefetchQuery(['string'], () => Promise.resolve('string')) + await queryClient.prefetchQuery(['number'], () => Promise.resolve(1)) + await queryClient.prefetchQuery(['boolean'], () => Promise.resolve(true)) + await queryClient.prefetchQuery(['null'], () => Promise.resolve(null)) + await queryClient.prefetchQuery(['array'], () => Promise.resolve(['string', 0]) ) @@ -58,9 +58,9 @@ describe('createWebStoragePersistor ', () => { timestamp: Date.now(), clientState: dehydrate(queryClient), } - webStoragePersistor.persistClient(persistClient) + webStoragePersister.persistClient(persistClient) await sleep(1) - const restoredClient = await webStoragePersistor.restoreClient() + const restoredClient = await webStoragePersister.restoreClient() expect(restoredClient).toEqual(persistClient) }) @@ -71,54 +71,54 @@ describe('createWebStoragePersistor ', () => { const N = 2000 const storage = getMockStorage(N * 5) // can save 4 items; - const webStoragePersistor = createWebStoragePersistor({ + const webStoragePersister = createWebStoragePersister({ throttleTime: 0, storage, }) - await queryClient.prefetchQuery('A', () => Promise.resolve('A'.repeat(N))) + await queryClient.prefetchQuery(['A'], () => Promise.resolve('A'.repeat(N))) await sleep(1) - await queryClient.prefetchQuery('B', () => Promise.resolve('B'.repeat(N))) + await queryClient.prefetchQuery(['B'], () => Promise.resolve('B'.repeat(N))) await sleep(1) - await queryClient.prefetchQuery('C', () => Promise.resolve('C'.repeat(N))) + await queryClient.prefetchQuery(['C'], () => Promise.resolve('C'.repeat(N))) await sleep(1) - await queryClient.prefetchQuery('D', () => Promise.resolve('D'.repeat(N))) + await queryClient.prefetchQuery(['D'], () => Promise.resolve('D'.repeat(N))) await sleep(1) - await queryClient.prefetchQuery('E', () => Promise.resolve('E'.repeat(N))) + await queryClient.prefetchQuery(['E'], () => Promise.resolve('E'.repeat(N))) const persistClient = { buster: 'test-limit', timestamp: Date.now(), clientState: dehydrate(queryClient), } - webStoragePersistor.persistClient(persistClient) + webStoragePersister.persistClient(persistClient) await sleep(10) - const restoredClient = await webStoragePersistor.restoreClient() + const restoredClient = await webStoragePersister.restoreClient() expect(restoredClient?.clientState.queries.length).toEqual(4) expect( - restoredClient?.clientState.queries.find(q => q.queryKey === 'A') + restoredClient?.clientState.queries.find(q => q.queryKey[0] === 'A') ).toBeUndefined() expect( - restoredClient?.clientState.queries.find(q => q.queryKey === 'B') + restoredClient?.clientState.queries.find(q => q.queryKey[0] === 'B') ).not.toBeUndefined() // update query Data - await queryClient.prefetchQuery('A', () => Promise.resolve('a'.repeat(N))) + await queryClient.prefetchQuery(['A'], () => Promise.resolve('a'.repeat(N))) const updatedPersistClient = { buster: 'test-limit', timestamp: Date.now(), clientState: dehydrate(queryClient), } - webStoragePersistor.persistClient(updatedPersistClient) + webStoragePersister.persistClient(updatedPersistClient) await sleep(10) - const restoredClient2 = await webStoragePersistor.restoreClient() + const restoredClient2 = await webStoragePersister.restoreClient() expect(restoredClient2?.clientState.queries.length).toEqual(4) expect( - restoredClient2?.clientState.queries.find(q => q.queryKey === 'A') + restoredClient2?.clientState.queries.find(q => q.queryKey[0] === 'A') ).toHaveProperty('state.data', 'a'.repeat(N)) expect( - restoredClient2?.clientState.queries.find(q => q.queryKey === 'B') + restoredClient2?.clientState.queries.find(q => q.queryKey[0] === 'B') ).toBeUndefined() }) @@ -129,7 +129,7 @@ describe('createWebStoragePersistor ', () => { const N = 2000 const storage = getMockStorage(N * 5) // can save 4 items; - const webStoragePersistor = createWebStoragePersistor({ + const webStoragePersister = createWebStoragePersister({ throttleTime: 0, storage, }) @@ -137,7 +137,7 @@ describe('createWebStoragePersistor ', () => { mutationCache.build( queryClient, { - mutationKey: 'MUTATIONS', + mutationKey: ['MUTATIONS'], mutationFn: () => Promise.resolve('M'.repeat(N)), }, { @@ -151,25 +151,25 @@ describe('createWebStoragePersistor ', () => { } ) await sleep(1) - await queryClient.prefetchQuery('A', () => Promise.resolve('A'.repeat(N))) + await queryClient.prefetchQuery(['A'], () => Promise.resolve('A'.repeat(N))) await sleep(1) - await queryClient.prefetchQuery('B', () => Promise.resolve('B'.repeat(N))) - await queryClient.prefetchQuery('C', () => Promise.resolve('C'.repeat(N))) + await queryClient.prefetchQuery(['B'], () => Promise.resolve('B'.repeat(N))) + await queryClient.prefetchQuery(['C'], () => Promise.resolve('C'.repeat(N))) await sleep(1) - await queryClient.prefetchQuery('D', () => Promise.resolve('D'.repeat(N))) + await queryClient.prefetchQuery(['D'], () => Promise.resolve('D'.repeat(N))) const persistClient = { buster: 'test-limit-mutations', timestamp: Date.now(), clientState: dehydrate(queryClient), } - webStoragePersistor.persistClient(persistClient) + webStoragePersister.persistClient(persistClient) await sleep(10) - const restoredClient = await webStoragePersistor.restoreClient() + const restoredClient = await webStoragePersister.restoreClient() expect(restoredClient?.clientState.mutations.length).toEqual(1) expect(restoredClient?.clientState.queries.length).toEqual(3) expect( - restoredClient?.clientState.queries.find(q => q.queryKey === 'A') + restoredClient?.clientState.queries.find(q => q.queryKey === ['A']) ).toBeUndefined() }) }) diff --git a/src/devtools/devtools.tsx b/src/devtools/devtools.tsx index 00a226de10..7476205a77 100644 --- a/src/devtools/devtools.tsx +++ b/src/devtools/devtools.tsx @@ -1,6 +1,6 @@ import React from 'react' -import { Query, useQueryClient } from 'react-query' +import { Query, useQueryClient, onlineManager } from 'react-query' import { matchSorter } from 'match-sorter' import useLocalStorage from './useLocalStorage' import { useIsMounted, useSafeState } from './utils' @@ -275,7 +275,7 @@ export function ReactQueryDevtools({ {...(otherCloseButtonProps as unknown)} onClick={e => { setIsOpen(false) - onCloseClick && onCloseClick(e) + onCloseClick?.(e) }} style={{ position: 'fixed', @@ -314,7 +314,7 @@ export function ReactQueryDevtools({ aria-expanded="false" onClick={e => { setIsOpen(true) - onToggleClick && onToggleClick(e) + onToggleClick?.(e) }} style={{ background: 'none', @@ -357,7 +357,13 @@ export function ReactQueryDevtools({ } const getStatusRank = (q: Query) => - q.state.isFetching ? 0 : !q.getObserversCount() ? 3 : q.isStale() ? 2 : 1 + q.state.fetchStatus !== 'idle' + ? 0 + : !q.getObserversCount() + ? 3 + : q.isStale() + ? 2 + : 1 const sortFns: Record number> = { 'Status > Last Updated': (a, b) => @@ -433,6 +439,8 @@ export const ReactQueryDevtoolsPanel = React.forwardRef< .length const hasFetching = queries.filter(q => getQueryStatusLabel(q) === 'fetching') .length + const hasPaused = queries.filter(q => getQueryStatusLabel(q) === 'paused') + .length const hasStale = queries.filter(q => getQueryStatusLabel(q) === 'stale') .length const hasInactive = queries.filter(q => getQueryStatusLabel(q) === 'inactive') @@ -457,6 +465,8 @@ export const ReactQueryDevtoolsPanel = React.forwardRef< promise?.catch(noop) } + const [isMockOffline, setMockOffline] = React.useState(false) + return ( fetching ({hasFetching}) {' '} + + paused ({hasPaused}) + {' '} setSortDesc(old => !old)} style={{ padding: '.3em .4em', + marginRight: '.5em', }} > {sortDesc ? '⬇ Desc' : '⬆ Asc'} + ) : null} @@ -799,7 +873,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef< +
+ error:{' '} + {mutation.error instanceof Error ? mutation.error.message : 'null'}, + status: {mutation.status}, isPaused: {String(mutation.isPaused)} +
+ + ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => { + expect( + rendered.getByText('error: null, status: idle, isPaused: false') + ).toBeInTheDocument() + }) + + rendered.getByRole('button', { name: /mutate/i }).click() + + await waitFor(() => { + expect( + rendered.getByText('error: null, status: loading, isPaused: true') + ).toBeInTheDocument() + }) + + expect(count).toBe(0) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(100) + + await waitFor(() => { + expect( + rendered.getByText('error: oops, status: error, isPaused: false') + ).toBeInTheDocument() + }) + + expect(count).toBe(2) + + consoleMock.mockRestore() + onlineMock.mockRestore() + }) + + it('should call onMutate even if paused', async () => { + const onlineMock = mockNavigatorOnLine(false) + const onMutate = jest.fn() + let count = 0 + + function Page() { + const mutation = useMutation( + async (_text: string) => { + count++ + await sleep(10) + return count + }, + { + onMutate, + } + ) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status}, + isPaused: {String(mutation.isPaused)} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('data: null, status: idle, isPaused: false') + + rendered.getByRole('button', { name: /mutate/i }).click() + + await rendered.findByText('data: null, status: loading, isPaused: true') + + expect(onMutate).toHaveBeenCalledTimes(1) + expect(onMutate).toHaveBeenCalledWith('todo') + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await rendered.findByText('data: 1, status: success, isPaused: false') + + expect(onMutate).toHaveBeenCalledTimes(1) + expect(count).toBe(1) + + onlineMock.mockRestore() + }) + + it('should optimistically go to paused state if offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + let count = 0 + const states: Array = [] + + function Page() { + const mutation = useMutation(async (_text: string) => { + count++ + await sleep(10) + return count + }) + + states.push(`${mutation.status}, ${mutation.isPaused}`) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status}, + isPaused: {String(mutation.isPaused)} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('data: null, status: idle, isPaused: false') + + rendered.getByRole('button', { name: /mutate/i }).click() + + await rendered.findByText('data: null, status: loading, isPaused: true') + + // no intermediate 'loading, false' state is expected because we don't start mutating! + expect(states[0]).toBe('idle, false') + expect(states[1]).toBe('loading, true') + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await rendered.findByText('data: 1, status: success, isPaused: false') + + onlineMock.mockRestore() + }) + it('should be able to retry a mutation when online', async () => { const consoleMock = mockConsoleError() - mockNavigatorOnLine(false) + const onlineMock = mockNavigatorOnLine(false) let count = 0 const states: UseMutationResult[] = [] @@ -395,6 +556,7 @@ describe('useMutation', () => { { retry: 1, retryDelay: 5, + networkMode: 'offlineFirst', } ) @@ -437,7 +599,7 @@ describe('useMutation', () => { failureCount: 1, }) - mockNavigatorOnLine(true) + onlineMock.mockReturnValue(true) window.dispatchEvent(new Event('online')) await sleep(50) @@ -456,6 +618,7 @@ describe('useMutation', () => { }) consoleMock.mockRestore() + onlineMock.mockRestore() }) it('should not change state if unmounted', async () => { @@ -583,4 +746,78 @@ describe('useMutation', () => { consoleMock.mockRestore() }) + + it('should call cache callbacks when unmounted', async () => { + const onSuccess = jest.fn() + const onSuccessMutate = jest.fn() + const onSettled = jest.fn() + const onSettledMutate = jest.fn() + const mutationKey = queryKey() + let count = 0 + + function Page() { + const [show, setShow] = React.useState(true) + return ( +
+ + {show && } +
+ ) + } + + function Component() { + const mutation = useMutation( + async (_text: string) => { + count++ + await sleep(10) + return count + }, + { + mutationKey, + cacheTime: 0, + onSuccess, + onSettled, + } + ) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status}, + isPaused: {String(mutation.isPaused)} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('data: null, status: idle, isPaused: false') + + rendered.getByRole('button', { name: /mutate/i }).click() + rendered.getByRole('button', { name: /hide/i }).click() + + await waitFor(() => { + expect( + queryClient.getMutationCache().findAll({ mutationKey }) + ).toHaveLength(0) + }) + + expect(count).toBe(1) + + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(onSettled).toHaveBeenCalledTimes(1) + expect(onSuccessMutate).toHaveBeenCalledTimes(0) + expect(onSettledMutate).toHaveBeenCalledTimes(0) + }) }) diff --git a/src/react/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx similarity index 99% rename from src/react/tests/useQueries.test.tsx rename to src/reactjs/tests/useQueries.test.tsx index 305e7af5cd..0b549747dc 100644 --- a/src/react/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -868,11 +868,11 @@ describe('useQueries', () => { // Array as const does not throw error const result5 = useQueries([ { - queryKey: 'key1', + queryKey: ['key1'], queryFn: () => 'string', }, { - queryKey: 'key1', + queryKey: ['key1'], queryFn: () => 123, }, ] as const) diff --git a/src/react/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx similarity index 81% rename from src/react/tests/useQuery.test.tsx rename to src/reactjs/tests/useQuery.test.tsx index 54b1f40a15..b8b04fbc13 100644 --- a/src/react/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -1,4 +1,5 @@ import { act, waitFor, fireEvent } from '@testing-library/react' +import '@testing-library/jest-dom' import React from 'react' import { @@ -10,6 +11,7 @@ import { renderWithClient, setActTimeout, Blink, + mockNavigatorOnLine, } from './utils' import { useQuery, @@ -91,13 +93,16 @@ describe('useQuery', () => { queryFn: getMyDataArrayKey, }) - const getMyDataStringKey: QueryFunction = async context => { + const getMyDataStringKey: QueryFunction< + MyData, + ['1'] + > = async context => { expectType<['1']>(context.queryKey) return Number(context.queryKey[0]) + 42 } useQuery({ - queryKey: '1', + queryKey: ['1'], queryFn: getMyDataStringKey, }) } @@ -173,6 +178,7 @@ describe('useQuery', () => { isFetched: false, isFetchedAfterMount: false, isFetching: true, + isPaused: false, isIdle: false, isLoading: true, isLoadingError: false, @@ -185,6 +191,7 @@ describe('useQuery', () => { refetch: expect.any(Function), remove: expect.any(Function), status: 'loading', + fetchStatus: 'fetching', }) expect(states[1]).toEqual({ @@ -197,6 +204,7 @@ describe('useQuery', () => { isFetched: true, isFetchedAfterMount: true, isFetching: false, + isPaused: false, isIdle: false, isLoading: false, isLoadingError: false, @@ -209,6 +217,7 @@ describe('useQuery', () => { refetch: expect.any(Function), remove: expect.any(Function), status: 'success', + fetchStatus: 'idle', }) }) @@ -233,6 +242,7 @@ describe('useQuery', () => { return (

Status: {state.status}

+
Failure Count: {state.failureCount}
) } @@ -251,6 +261,7 @@ describe('useQuery', () => { isFetched: false, isFetchedAfterMount: false, isFetching: true, + isPaused: false, isIdle: false, isLoading: true, isLoadingError: false, @@ -263,6 +274,7 @@ describe('useQuery', () => { refetch: expect.any(Function), remove: expect.any(Function), status: 'loading', + fetchStatus: 'fetching', }) expect(states[1]).toEqual({ @@ -275,6 +287,7 @@ describe('useQuery', () => { isFetched: false, isFetchedAfterMount: false, isFetching: true, + isPaused: false, isIdle: false, isLoading: true, isLoadingError: false, @@ -287,6 +300,7 @@ describe('useQuery', () => { refetch: expect.any(Function), remove: expect.any(Function), status: 'loading', + fetchStatus: 'fetching', }) expect(states[2]).toEqual({ @@ -299,6 +313,7 @@ describe('useQuery', () => { isFetched: true, isFetchedAfterMount: true, isFetching: false, + isPaused: false, isIdle: false, isLoading: false, isLoadingError: true, @@ -311,6 +326,7 @@ describe('useQuery', () => { refetch: expect.any(Function), remove: expect.any(Function), status: 'error', + fetchStatus: 'idle', }) consoleMock.mockRestore() @@ -370,7 +386,10 @@ describe('useQuery', () => { const onSuccess = jest.fn() function Page() { - const state = useQuery(key, () => 'data', { onSuccess }) + const state = useQuery(key, () => 'data', { + onSuccess, + notifyOnChangeProps: 'all', + }) states.push(state) @@ -549,6 +568,108 @@ describe('useQuery', () => { consoleMock.mockRestore() }) + it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => { + const key = queryKey() + let fetchCount = 0 + + function Page() { + const { refetch } = useQuery( + key, + async () => { + fetchCount++ + await sleep(10) + return 'data' + }, + { enabled: false, initialData: 'initialData' } + ) + + React.useEffect(() => { + setActTimeout(() => { + refetch() + }, 5) + setActTimeout(() => { + refetch({ cancelRefetch: false }) + }, 5) + }, [refetch]) + + return null + } + + renderWithClient(queryClient, ) + + await sleep(20) + // first refetch only, second refetch is ignored + expect(fetchCount).toBe(1) + }) + + it('should cancel an ongoing fetch when refetch is called (cancelRefetch=true) if we have data already', async () => { + const key = queryKey() + let fetchCount = 0 + + function Page() { + const { refetch } = useQuery( + key, + async () => { + fetchCount++ + await sleep(10) + return 'data' + }, + { enabled: false, initialData: 'initialData' } + ) + + React.useEffect(() => { + setActTimeout(() => { + refetch() + }, 5) + setActTimeout(() => { + refetch() + }, 5) + }, [refetch]) + + return null + } + + renderWithClient(queryClient, ) + + await sleep(20) + // first refetch (gets cancelled) and second refetch + expect(fetchCount).toBe(2) + }) + + it('should not cancel an ongoing fetch when refetch is called (cancelRefetch=true) if we do not have data yet', async () => { + const key = queryKey() + let fetchCount = 0 + + function Page() { + const { refetch } = useQuery( + key, + async () => { + fetchCount++ + await sleep(10) + return 'data' + }, + { enabled: false } + ) + + React.useEffect(() => { + setActTimeout(() => { + refetch() + }, 5) + setActTimeout(() => { + refetch() + }, 5) + }, [refetch]) + + return null + } + + renderWithClient(queryClient, ) + + await sleep(20) + // first refetch will not get cancelled, second one gets skipped + expect(fetchCount).toBe(1) + }) + it('should be able to watch a query without providing a query function', async () => { const key = queryKey() const states: UseQueryResult[] = [] @@ -570,52 +691,78 @@ describe('useQuery', () => { expect(states[1]).toMatchObject({ data: 'data' }) }) - it('should create a new query when re-mounting with cacheTime 0', async () => { + it('should pick up a query when re-mounting with cacheTime 0', async () => { const key = queryKey() const states: UseQueryResult[] = [] function Page() { const [toggle, setToggle] = React.useState(false) - React.useEffect(() => { - setActTimeout(() => { - setToggle(true) - }, 20) - }, [setToggle]) - - return toggle ? : + return ( +
+ + {toggle ? ( + + ) : ( + + )} +
+ ) } - function Component() { + function Component({ value }: { value: string }) { const state = useQuery( key, async () => { - await sleep(5) - return 'data' + await sleep(10) + return 'data: ' + value }, { cacheTime: 0, + notifyOnChangeProps: 'all', } ) states.push(state) - return null + return ( +
+
{state.data}
+
+ ) } - renderWithClient(queryClient, ) + const rendered = renderWithClient(queryClient, ) - await sleep(100) + await rendered.findByText('data: 1') - expect(states.length).toBe(5) + rendered.getByRole('button', { name: /toggle/i }).click() + + await rendered.findByText('data: 2') + + expect(states.length).toBe(4) // First load - expect(states[0]).toMatchObject({ isLoading: true, isSuccess: false }) + expect(states[0]).toMatchObject({ + isLoading: true, + isSuccess: false, + isFetching: true, + }) // First success - expect(states[1]).toMatchObject({ isLoading: false, isSuccess: true }) - // Switch - expect(states[2]).toMatchObject({ isLoading: false, isSuccess: true }) - // Second load - expect(states[3]).toMatchObject({ isLoading: true, isSuccess: false }) + expect(states[1]).toMatchObject({ + isLoading: false, + isSuccess: true, + isFetching: false, + }) + // Switch, goes to fetching + expect(states[2]).toMatchObject({ + isLoading: false, + isSuccess: true, + isFetching: true, + }) // Second success - expect(states[4]).toMatchObject({ isLoading: false, isSuccess: true }) + expect(states[3]).toMatchObject({ + isLoading: false, + isSuccess: true, + isFetching: false, + }) }) it('should not get into an infinite loop when removing a query with cacheTime 0 and rerendering', async () => { @@ -633,6 +780,7 @@ describe('useQuery', () => { }, { cacheTime: 0, + notifyOnChangeProps: 'all', } ) @@ -815,54 +963,12 @@ describe('useQuery', () => { consoleMock.mockRestore() }) - it('should re-render when dataUpdatedAt changes but data remains the same', async () => { - const key = queryKey() - const states: UseQueryResult[] = [] - - function Page() { - const state = useQuery(key, () => 'test', { - notifyOnChangePropsExclusions: [ - 'data', - 'isFetching', - 'isLoading', - 'isRefetching', - 'isSuccess', - 'status', - ], - }) - - states.push(state) - - const { refetch } = state - - React.useEffect(() => { - setActTimeout(() => { - refetch() - }, 5) - }, [refetch]) - - return null - } - - renderWithClient(queryClient, ) - - await sleep(10) - - expect(states.length).toBe(3) - expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) - expect(states[1]).toMatchObject({ data: 'test', isFetching: false }) - expect(states[2]).toMatchObject({ data: 'test', isFetching: false }) - expect(states[1]?.dataUpdatedAt).not.toBe(states[2]?.dataUpdatedAt) - }) - it('should track properties and only re-render when a tracked property changes', async () => { const key = queryKey() const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'test', { - notifyOnChangeProps: 'tracked', - }) + const state = useQuery(key, () => 'test') states.push(state) @@ -890,46 +996,13 @@ describe('useQuery', () => { expect(states[1]).toMatchObject({ data: 'test' }) }) - it('should not re-render if a tracked prop changes, but it was excluded', async () => { - const key = queryKey() - const states: UseQueryResult[] = [] - - function Page() { - const state = useQuery(key, () => 'test', { - notifyOnChangeProps: 'tracked', - notifyOnChangePropsExclusions: ['data'], - }) - - states.push(state) - - return ( -
-

{state.data ?? 'null'}

-
- ) - } - - const rendered = renderWithClient(queryClient, ) - - await waitFor(() => rendered.getByText('null')) - expect(states.length).toBe(1) - expect(states[0]).toMatchObject({ data: undefined }) - - await queryClient.refetchQueries(key) - await waitFor(() => rendered.getByText('null')) - expect(states.length).toBe(1) - expect(states[0]).toMatchObject({ data: undefined }) - }) - it('should always re-render if we are tracking props but not using any', async () => { const key = queryKey() let renderCount = 0 const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'test', { - notifyOnChangeProps: 'tracked', - }) + const state = useQuery(key, () => 'test') states.push(state) @@ -960,7 +1033,7 @@ describe('useQuery', () => { function Page() { const [, rerender] = React.useState({}) - const state = useQuery(key, () => ++count) + const state = useQuery(key, () => ++count, { notifyOnChangeProps: 'all' }) states.push(state) @@ -1001,7 +1074,7 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery(key, () => ++count) + const state = useQuery(key, () => ++count, { notifyOnChangeProps: 'all' }) states.push(state) @@ -1052,10 +1125,14 @@ describe('useQuery', () => { let count = 0 function Page() { - const state = useQuery(key, () => { - count++ - return count === 1 ? result1 : result2 - }) + const state = useQuery( + key, + () => { + count++ + return count === 1 ? result1 : result2 + }, + { notifyOnChangeProps: 'all' } + ) states.push(state) @@ -1428,6 +1505,7 @@ describe('useQuery', () => {

data: {state.data}

error: {state.error?.message}

+

previous data: {state.isPreviousData}

) } @@ -1589,7 +1667,7 @@ describe('useQuery', () => { await sleep(10) return count }, - { enabled: false, keepPreviousData: true } + { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' } ) states.push(state) @@ -1678,7 +1756,7 @@ describe('useQuery', () => { await sleep(10) return count }, - { enabled: false, keepPreviousData: true } + { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' } ) states.push(state) @@ -1748,7 +1826,7 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function FirstComponent() { - const state = useQuery(key, () => 1) + const state = useQuery(key, () => 1, { notifyOnChangeProps: 'all' }) const refetch = state.refetch states.push(state) @@ -1763,7 +1841,7 @@ describe('useQuery', () => { } function SecondComponent() { - useQuery(key, () => 2) + useQuery(key, () => 2, { notifyOnChangeProps: 'all' }) return null } @@ -2324,10 +2402,10 @@ describe('useQuery', () => { it('should not pass stringified variables to query function', async () => { const key = queryKey() const variables = { number: 5, boolean: false, object: {}, array: [] } - type QueryKey = [string, typeof variables] - const states: UseQueryResult[] = [] + type CustomQueryKey = [typeof key, typeof variables] + const states: UseQueryResult[] = [] - const queryFn = async (ctx: QueryFunctionContext) => { + const queryFn = async (ctx: QueryFunctionContext) => { await sleep(10) return ctx.queryKey } @@ -3097,10 +3175,8 @@ describe('useQuery', () => { const consoleMock = mockConsoleError() - const originalVisibilityState = document.visibilityState - // make page unfocused - mockVisibilityState('hidden') + const visibilityMock = mockVisibilityState('hidden') let count = 0 @@ -3139,7 +3215,7 @@ describe('useQuery', () => { act(() => { // reset visibilityState to original value - mockVisibilityState(originalVisibilityState) + visibilityMock.mockRestore() window.dispatchEvent(new FocusEvent('focus')) }) @@ -3195,8 +3271,7 @@ describe('useQuery', () => { const consoleMock = mockConsoleError() // make page unfocused - const originalVisibilityState = document.visibilityState - mockVisibilityState('hidden') + const visibilityMock = mockVisibilityState('hidden') // set data in cache to check if the hook query fn is actually called queryClient.setQueryData(key, 'prefetched') @@ -3216,7 +3291,7 @@ describe('useQuery', () => { act(() => { // reset visibilityState to original value - mockVisibilityState(originalVisibilityState) + visibilityMock.mockRestore() window.dispatchEvent(new FocusEvent('focus')) }) @@ -3637,7 +3712,12 @@ describe('useQuery', () => { states.push(queryInfo) - return
count: {queryInfo.data}
+ return ( +
+

count: {queryInfo.data}

+

refetch: {queryInfo.isRefetching}

+
+ ) } const rendered = renderWithClient(queryClient, ) @@ -3718,7 +3798,7 @@ describe('useQuery', () => { it('should accept an empty string as query key', async () => { function Page() { - const result = useQuery('', ctx => ctx.queryKey) + const result = useQuery([''], ctx => ctx.queryKey) return <>{JSON.stringify(result.data)} } @@ -3955,14 +4035,13 @@ describe('useQuery', () => { const key = queryKey() let cancelFn: jest.Mock = jest.fn() - const queryFn = () => { + const queryFn = ({ signal }: { signal?: AbortSignal }) => { const promise = new Promise((resolve, reject) => { cancelFn = jest.fn(() => reject('Cancelled')) + signal?.addEventListener('abort', cancelFn) sleep(10).then(() => resolve('OK')) }) - ;(promise as any).cancel = cancelFn - return promise } @@ -3984,14 +4063,16 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('off')) - expect(cancelFn).toHaveBeenCalled() + if (typeof AbortSignal === 'function') { + expect(cancelFn).toHaveBeenCalled() + } }) it('should cancel the query if the signal was consumed and there are no more subscriptions', async () => { const key = queryKey() const states: UseQueryResult[] = [] - const queryFn: QueryFunction = async ctx => { + const queryFn: QueryFunction = async ctx => { const [, limit] = ctx.queryKey const value = limit % 2 && ctx.signal ? 'abort' : `data ${limit}` await sleep(10) @@ -4195,7 +4276,7 @@ describe('useQuery', () => { count++ return count }, - { staleTime: Infinity, enabled: false } + { staleTime: Infinity, enabled: false, notifyOnChangeProps: 'all' } ) states.push(state) @@ -4458,4 +4539,732 @@ describe('useQuery', () => { consoleMock.mockRestore() }) + + describe('networkMode online', () => { + it('online queries should not start fetching if you are offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + const states: Array = [] + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'data' + }, + }) + + React.useEffect(() => { + states.push(state.fetchStatus) + }) + + return ( +
+
+ status: {state.status}, isPaused: {String(state.isPaused)} +
+
data: {state.data}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('status: loading, isPaused: true')) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + rendered.getByText('status: success, isPaused: false') + ) + await waitFor(() => { + expect(rendered.getByText('data: data')).toBeInTheDocument() + }) + + expect(states).toEqual(['paused', 'fetching', 'idle']) + + onlineMock.mockRestore() + }) + + it('online queries should not refetch if you are offline', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus}, + failureCount: {state.failureCount} +
+
data: {state.data}
+ +
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('data: data1')) + + const onlineMock = mockNavigatorOnLine(false) + rendered.getByRole('button', { name: /invalidate/i }).click() + + await waitFor(() => + rendered.getByText( + 'status: success, fetchStatus: paused, failureCount: 0' + ) + ) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + rendered.getByText( + 'status: success, fetchStatus: fetching, failureCount: 0' + ) + ) + await waitFor(() => + rendered.getByText( + 'status: success, fetchStatus: idle, failureCount: 0' + ) + ) + + await waitFor(() => { + expect(rendered.getByText('data: data2')).toBeInTheDocument() + }) + + onlineMock.mockRestore() + }) + + it('online queries should not refetch if you are offline and refocus', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('data: data1')) + + const onlineMock = mockNavigatorOnLine(false) + rendered.getByRole('button', { name: /invalidate/i }).click() + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: paused') + ) + + window.dispatchEvent(new FocusEvent('focus')) + await sleep(15) + + await waitFor(() => + expect(rendered.queryByText('data: data2')).not.toBeInTheDocument() + ) + expect(count).toBe(1) + onlineMock.mockRestore() + }) + + it('online queries should not refetch while already paused', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: paused') + ) + + rendered.getByRole('button', { name: /invalidate/i }).click() + + await sleep(15) + + // invalidation should not trigger a refetch + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: paused') + ) + + expect(count).toBe(0) + onlineMock.mockRestore() + }) + + it('online queries should not refetch while already paused if data is in the cache', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + initialData: 'initial', + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: paused') + ) + await waitFor(() => { + expect(rendered.getByText('data: initial')).toBeInTheDocument() + }) + + rendered.getByRole('button', { name: /invalidate/i }).click() + + await sleep(15) + + // invalidation should not trigger a refetch + await waitFor(() => + rendered.getByText('status: success, fetchStatus: paused') + ) + + expect(count).toBe(0) + onlineMock.mockRestore() + }) + + it('online queries should not get stuck in fetching state when pausing multiple times', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + initialData: 'initial', + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: paused') + ) + await waitFor(() => { + expect(rendered.getByText('data: initial')).toBeInTheDocument() + }) + + // triggers one pause + rendered.getByRole('button', { name: /invalidate/i }).click() + + await sleep(15) + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: paused') + ) + + // triggers a second pause + act(() => { + window.dispatchEvent(new FocusEvent('focus')) + }) + + onlineMock.mockReturnValue(true) + act(() => { + window.dispatchEvent(new Event('online')) + }) + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: idle') + ) + await waitFor(() => { + expect(rendered.getByText('data: data1')).toBeInTheDocument() + }) + + expect(count).toBe(1) + onlineMock.mockRestore() + }) + + it('online queries should pause retries if you are offline', async () => { + const key = queryKey() + const consoleMock = mockConsoleError() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + throw new Error('failed' + count) + }, + retry: 2, + retryDelay: 10, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus}, + failureCount: {state.failureCount} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText( + 'status: loading, fetchStatus: fetching, failureCount: 1' + ) + ) + + const onlineMock = mockNavigatorOnLine(false) + + await sleep(20) + + await waitFor(() => + rendered.getByText( + 'status: loading, fetchStatus: paused, failureCount: 1' + ) + ) + + expect(count).toBe(1) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + rendered.getByText('status: error, fetchStatus: idle, failureCount: 3') + ) + + expect(count).toBe(3) + + onlineMock.mockRestore() + consoleMock.mockRestore() + }) + + it('online queries should fetch if paused and we go online even if already unmounted (because not cancelled)', async () => { + const key = queryKey() + let count = 0 + + function Component() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+
+ ) + } + + function Page() { + const [show, setShow] = React.useState(true) + + return ( +
+ {show && } + +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: paused') + ) + + rendered.getByRole('button', { name: /hide/i }).click() + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(15) + + expect(queryClient.getQueryState(key)).toMatchObject({ + fetchStatus: 'idle', + status: 'success', + }) + + expect(count).toBe(1) + + onlineMock.mockRestore() + }) + + it('online queries should not fetch if paused and we go online when cancelled and no refetchOnReconnect', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + refetchOnReconnect: false, + }) + + return ( +
+ +
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: paused') + ) + + rendered.getByRole('button', { name: /cancel/i }).click() + + await waitFor(() => rendered.getByText('status: idle, fetchStatus: idle')) + + expect(count).toBe(0) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(15) + + await waitFor(() => rendered.getByText('status: idle, fetchStatus: idle')) + + expect(count).toBe(0) + + onlineMock.mockRestore() + }) + + it('online queries should not fetch if paused and we go online if already unmounted when signal consumed', async () => { + const key = queryKey() + let count = 0 + + function Component() { + const state = useQuery({ + queryKey: key, + queryFn: async ({ signal }) => { + count++ + await sleep(10) + return `${signal ? 'signal' : 'data'}${count}` + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+
+ ) + } + + function Page() { + const [show, setShow] = React.useState(true) + + return ( +
+ {show && } + + +
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: idle') + ) + + const onlineMock = mockNavigatorOnLine(false) + + rendered.getByRole('button', { name: /invalidate/i }).click() + + await waitFor(() => + rendered.getByText('status: success, fetchStatus: paused') + ) + + rendered.getByRole('button', { name: /hide/i }).click() + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(15) + + expect(queryClient.getQueryState(key)).toMatchObject({ + fetchStatus: 'idle', + status: 'success', + }) + expect(count).toBe(typeof AbortSignal === 'function' ? 1 : 2) + + onlineMock.mockRestore() + }) + }) + + describe('networkMode always', () => { + it('always queries should start fetching even if you are offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data ' + count + }, + networkMode: 'always', + }) + + return ( +
+
+ status: {state.status}, isPaused: {String(state.isPaused)} +
+
data: {state.data}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText('status: success, isPaused: false') + ) + + await waitFor(() => { + expect(rendered.getByText('data: data 1')).toBeInTheDocument() + }) + + onlineMock.mockRestore() + }) + + it('always queries should not pause retries', async () => { + const onlineMock = mockNavigatorOnLine(false) + const consoleMock = mockConsoleError() + + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + throw new Error('error ' + count) + }, + networkMode: 'always', + retry: 1, + retryDelay: 5, + }) + + return ( +
+
+ status: {state.status}, isPaused: {String(state.isPaused)} +
+
+ error: {state.error instanceof Error && state.error.message} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('status: error, isPaused: false')) + + await waitFor(() => { + expect(rendered.getByText('error: error 2')).toBeInTheDocument() + }) + + expect(count).toBe(2) + + consoleMock.mockRestore() + onlineMock.mockRestore() + }) + }) + + describe('networkMode offlineFirst', () => { + it('offlineFirst queries should start fetching if you are offline, but pause retries', async () => { + const consoleMock = mockConsoleError() + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + let count = 0 + + function Page() { + const state = useQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + throw new Error('failed' + count) + }, + retry: 2, + retryDelay: 1, + networkMode: 'offlineFirst', + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus}, + failureCount: {state.failureCount} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => + rendered.getByText( + 'status: loading, fetchStatus: paused, failureCount: 1' + ) + ) + + expect(count).toBe(1) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + rendered.getByText('status: error, fetchStatus: idle, failureCount: 3') + ) + + expect(count).toBe(3) + + onlineMock.mockRestore() + consoleMock.mockRestore() + }) + }) }) diff --git a/src/react/tests/utils.tsx b/src/reactjs/tests/utils.tsx similarity index 74% rename from src/react/tests/utils.tsx rename to src/reactjs/tests/utils.tsx index 314c3183be..39d41cb36b 100644 --- a/src/react/tests/utils.tsx +++ b/src/reactjs/tests/utils.tsx @@ -1,7 +1,7 @@ import { act, render } from '@testing-library/react' import React from 'react' -import { QueryClient, QueryClientProvider } from '../..' +import { MutationOptions, QueryClient, QueryClientProvider } from '../..' export function renderWithClient(client: QueryClient, ui: React.ReactElement) { const { rerender, ...result } = render( @@ -16,18 +16,12 @@ export function renderWithClient(client: QueryClient, ui: React.ReactElement) { } } -export function mockVisibilityState(value: string) { - Object.defineProperty(document, 'visibilityState', { - value, - configurable: true, - }) +export function mockVisibilityState(value: VisibilityState) { + return jest.spyOn(document, 'visibilityState', 'get').mockReturnValue(value) } export function mockNavigatorOnLine(value: boolean) { - Object.defineProperty(navigator, 'onLine', { - value, - configurable: true, - }) + return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) } export function mockConsoleError() { @@ -37,9 +31,9 @@ export function mockConsoleError() { } let queryKeyCount = 0 -export function queryKey(): string { +export function queryKey(): Array { queryKeyCount++ - return `query_${queryKeyCount}` + return [`query_${queryKeyCount}`] } export function sleep(timeout: number): Promise { @@ -83,3 +77,10 @@ export const Blink: React.FC<{ duration: number }> = ({ return shouldShow ? <>{children} : <>off } + +export const executeMutation = ( + queryClient: QueryClient, + options: MutationOptions +): Promise => { + return queryClient.getMutationCache().build(queryClient, options).execute() +} diff --git a/src/react/types.ts b/src/reactjs/types.ts similarity index 98% rename from src/react/types.ts rename to src/reactjs/types.ts index 79aa3d4507..3d2079d110 100644 --- a/src/react/types.ts +++ b/src/reactjs/types.ts @@ -10,6 +10,7 @@ import { MutationFunction, MutateOptions, MutationMeta, + NetworkMode, } from '../core/types' export interface UseBaseQueryOptions< @@ -76,6 +77,7 @@ export interface UseMutationOptions< > { mutationFn?: MutationFunction mutationKey?: MutationKey + cacheTime?: number onMutate?: ( variables: TVariables ) => Promise | TContext | undefined @@ -97,6 +99,7 @@ export interface UseMutationOptions< ) => Promise | void retry?: RetryValue retryDelay?: RetryDelayValue + networkMode?: NetworkMode useErrorBoundary?: boolean | ((error: TError) => boolean) meta?: MutationMeta } diff --git a/src/react/useBaseQuery.ts b/src/reactjs/useBaseQuery.ts similarity index 88% rename from src/react/useBaseQuery.ts rename to src/reactjs/useBaseQuery.ts index 3adb64e053..a5f4d5f5dd 100644 --- a/src/react/useBaseQuery.ts +++ b/src/reactjs/useBaseQuery.ts @@ -29,7 +29,7 @@ export function useBaseQuery< const queryClient = useQueryClient() const errorResetBoundary = useQueryErrorResetBoundary() - const defaultedOptions = queryClient.defaultQueryObserverOptions(options) + const defaultedOptions = queryClient.defaultQueryOptions(options) // Make sure results are optimistically set in fetching state before subscribing or updating options defaultedOptions.optimisticResults = true @@ -59,12 +59,6 @@ export function useBaseQuery< if (typeof defaultedOptions.staleTime !== 'number') { defaultedOptions.staleTime = 1000 } - - // Set cache time to 1 if the option has been set to 0 - // when using suspense to prevent infinite loop of fetches - if (defaultedOptions.cacheTime === 0) { - defaultedOptions.cacheTime = 1 - } } if (defaultedOptions.suspense || defaultedOptions.useErrorBoundary) { @@ -133,17 +127,13 @@ export function useBaseQuery< result.isError && !errorResetBoundary.isReset() && !result.isFetching && - shouldThrowError( - defaultedOptions.suspense, - defaultedOptions.useErrorBoundary, - result.error - ) + shouldThrowError(defaultedOptions.useErrorBoundary, result.error) ) { throw result.error } // Handle result property usage tracking - if (defaultedOptions.notifyOnChangeProps === 'tracked') { + if (!defaultedOptions.notifyOnChangeProps) { result = observer.trackResult(result, defaultedOptions) } diff --git a/src/react/useInfiniteQuery.ts b/src/reactjs/useInfiniteQuery.ts similarity index 100% rename from src/react/useInfiniteQuery.ts rename to src/reactjs/useInfiniteQuery.ts diff --git a/src/react/useIsFetching.ts b/src/reactjs/useIsFetching.ts similarity index 100% rename from src/react/useIsFetching.ts rename to src/reactjs/useIsFetching.ts diff --git a/src/react/useIsMutating.ts b/src/reactjs/useIsMutating.ts similarity index 100% rename from src/react/useIsMutating.ts rename to src/reactjs/useIsMutating.ts diff --git a/src/react/useMutation.ts b/src/reactjs/useMutation.ts similarity index 98% rename from src/react/useMutation.ts rename to src/reactjs/useMutation.ts index 264950c080..5e48928726 100644 --- a/src/react/useMutation.ts +++ b/src/reactjs/useMutation.ts @@ -115,8 +115,7 @@ export function useMutation< if ( currentResult.error && shouldThrowError( - undefined, - obsRef.current.options.useErrorBoundary, + !!obsRef.current.options.useErrorBoundary, currentResult.error ) ) { diff --git a/src/react/useQueries.ts b/src/reactjs/useQueries.ts similarity index 97% rename from src/react/useQueries.ts rename to src/reactjs/useQueries.ts index cd2d5d0ad3..339f6a0937 100644 --- a/src/react/useQueries.ts +++ b/src/reactjs/useQueries.ts @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react' +import React from 'react' import { QueryFunction } from '../core/types' import { notifyManager } from '../core/notifyManager' @@ -118,12 +118,10 @@ export function useQueries( const queryClient = useQueryClient() - const defaultedQueries = useMemo( + const defaultedQueries = React.useMemo( () => queries.map(options => { - const defaultedOptions = queryClient.defaultQueryObserverOptions( - options - ) + const defaultedOptions = queryClient.defaultQueryOptions(options) // Make sure the results are already in fetching state before subscribing or updating options defaultedOptions.optimisticResults = true diff --git a/src/react/useQuery.ts b/src/reactjs/useQuery.ts similarity index 100% rename from src/react/useQuery.ts rename to src/reactjs/useQuery.ts diff --git a/src/reactjs/utils.ts b/src/reactjs/utils.ts new file mode 100644 index 0000000000..955afb6cf2 --- /dev/null +++ b/src/reactjs/utils.ts @@ -0,0 +1,11 @@ +export function shouldThrowError( + _useErrorBoundary: boolean | ((err: TError) => boolean), + error: TError +): boolean { + // Allow useErrorBoundary function to override throwing behavior on a per-error basis + if (typeof _useErrorBoundary === 'function') { + return _useErrorBoundary(error) + } + + return _useErrorBoundary +} diff --git a/src/ts3.8/useQueries.ts b/src/ts3.8/useQueries.ts index b710556d35..a143b5359f 100644 --- a/src/ts3.8/useQueries.ts +++ b/src/ts3.8/useQueries.ts @@ -1,4 +1,4 @@ -import { UseQueryOptions, UseQueryResult } from '../react/types' +import { UseQueryOptions, UseQueryResult } from '../reactjs/types' /** * Backwards-compatible definition for TS < 4.1 diff --git a/tsconfig.types.json b/tsconfig.types.json index f95e1f4c19..6f6510ecd5 100644 --- a/tsconfig.types.json +++ b/tsconfig.types.json @@ -11,11 +11,10 @@ }, "files": [ "./src/index.ts", - "./src/hydration/index.ts", "./src/devtools/index.ts", - "./src/persistQueryClient-experimental/index.ts", - "./src/createWebStoragePersistor-experimental/index.ts", - "./src/createAsyncStoragePersistor-experimental/index.ts", + "./src/persistQueryClient/index.ts", + "./src/createWebStoragePersister/index.ts", + "./src/createAsyncStoragePersister/index.ts", "./src/broadcastQueryClient-experimental/index.ts", "./src/ts3.8/index.ts" ], From 1247d7beab39ea4cae1ecb2d59c6f6b49ccd9fec Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 10 Dec 2021 17:40:36 +0100 Subject: [PATCH 02/76] refactor(core): refactor internals from classes to a functions (#3066) * refactor(core): refactor retryer from a class to a function * refactor(core): refactor notifyManager from a class to a function * refactor(core): refactor notifyManager from a class to a function remove outdated comment --- src/core/mutation.ts | 6 +- src/core/notifyManager.ts | 82 ++++---- src/core/query.ts | 6 +- src/core/retryer.ts | 274 +++++++++++++------------- src/core/tests/notifyManager.test.tsx | 6 +- 5 files changed, 185 insertions(+), 189 deletions(-) diff --git a/src/core/mutation.ts b/src/core/mutation.ts index 093f4107ee..04533c11ea 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -4,7 +4,7 @@ import type { MutationObserver } from './mutationObserver' import { getLogger } from './logger' import { notifyManager } from './notifyManager' import { Removable } from './removable' -import { canFetch, Retryer } from './retryer' +import { canFetch, Retryer, createRetryer } from './retryer' import { noop } from './utils' // TYPES @@ -90,7 +90,7 @@ export class Mutation< private observers: MutationObserver[] private mutationCache: MutationCache - private retryer?: Retryer + private retryer?: Retryer constructor(config: MutationConfig) { super() @@ -262,7 +262,7 @@ export class Mutation< } private executeMutation(): Promise { - this.retryer = new Retryer({ + this.retryer = createRetryer({ fn: () => { if (!this.options.mutationFn) { return Promise.reject('No mutationFn found') diff --git a/src/core/notifyManager.ts b/src/core/notifyManager.ts index b584f7bf84..dc1e7a0cab 100644 --- a/src/core/notifyManager.ts +++ b/src/core/notifyManager.ts @@ -8,43 +8,32 @@ type NotifyFunction = (callback: () => void) => void type BatchNotifyFunction = (callback: () => void) => void -// CLASS - -export class NotifyManager { - private queue: NotifyCallback[] - private transactions: number - private notifyFn: NotifyFunction - private batchNotifyFn: BatchNotifyFunction - - constructor() { - this.queue = [] - this.transactions = 0 - - this.notifyFn = (callback: () => void) => { - callback() - } - - this.batchNotifyFn = (callback: () => void) => { - callback() - } +export function createNotifyManager() { + let queue: NotifyCallback[] = [] + let transactions = 0 + let notifyFn: NotifyFunction = callback => { + callback() + } + let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => { + callback() } - batch(callback: () => T): T { - this.transactions++ + const batch = (callback: () => T): T => { + transactions++ const result = callback() - this.transactions-- - if (!this.transactions) { - this.flush() + transactions-- + if (!transactions) { + flush() } return result } - schedule(callback: NotifyCallback): void { - if (this.transactions) { - this.queue.push(callback) + const schedule = (callback: NotifyCallback): void => { + if (transactions) { + queue.push(callback) } else { scheduleMicrotask(() => { - this.notifyFn(callback) + notifyFn(callback) }) } } @@ -52,22 +41,22 @@ export class NotifyManager { /** * All calls to the wrapped function will be batched. */ - batchCalls(callback: T): T { + const batchCalls = (callback: T): T => { return ((...args: any[]) => { - this.schedule(() => { + schedule(() => { callback(...args) }) }) as any } - flush(): void { - const queue = this.queue - this.queue = [] - if (queue.length) { + const flush = (): void => { + const originalQueue = queue + queue = [] + if (originalQueue.length) { scheduleMicrotask(() => { - this.batchNotifyFn(() => { - queue.forEach(callback => { - this.notifyFn(callback) + batchNotifyFn(() => { + originalQueue.forEach(callback => { + notifyFn(callback) }) }) }) @@ -78,19 +67,26 @@ export class NotifyManager { * Use this method to set a custom notify function. * This can be used to for example wrap notifications with `React.act` while running tests. */ - setNotifyFunction(fn: NotifyFunction) { - this.notifyFn = fn + const setNotifyFunction = (fn: NotifyFunction) => { + notifyFn = fn } /** * Use this method to set a custom function to batch notifications together into a single tick. * By default React Query will use the batch function provided by ReactDOM or React Native. */ - setBatchNotifyFunction(fn: BatchNotifyFunction) { - this.batchNotifyFn = fn + const setBatchNotifyFunction = (fn: BatchNotifyFunction) => { + batchNotifyFn = fn } + + return { + batch, + batchCalls, + schedule, + setNotifyFunction, + setBatchNotifyFunction, + } as const } // SINGLETON - -export const notifyManager = new NotifyManager() +export const notifyManager = createNotifyManager() diff --git a/src/core/query.ts b/src/core/query.ts index 12e99622ed..07e171ecc7 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -21,7 +21,7 @@ import type { QueryCache } from './queryCache' import type { QueryObserver } from './queryObserver' import { notifyManager } from './notifyManager' import { getLogger } from './logger' -import { Retryer, isCancelledError, canFetch } from './retryer' +import { Retryer, isCancelledError, canFetch, createRetryer } from './retryer' import { Removable } from './removable' // TYPES @@ -158,7 +158,7 @@ export class Query< private cache: QueryCache private promise?: Promise - private retryer?: Retryer + private retryer?: Retryer private observers: QueryObserver[] private defaultOptions?: QueryOptions private abortSignalConsumed: boolean @@ -431,7 +431,7 @@ export class Query< } // Try to fetch the data - this.retryer = new Retryer({ + this.retryer = createRetryer({ fn: context.fetchFn as () => TData, abort: abortController?.abort?.bind(abortController), onSuccess: data => { diff --git a/src/core/retryer.ts b/src/core/retryer.ts index 537f1b28ab..0b5f8bf85c 100644 --- a/src/core/retryer.ts +++ b/src/core/retryer.ts @@ -18,6 +18,14 @@ interface RetryerConfig { networkMode: NetworkMode | undefined } +export interface Retryer { + promise: Promise + cancel: (cancelOptions?: CancelOptions) => void + continue: () => void + cancelRetry: () => void + continueRetry: () => void +} + export type RetryValue = boolean | number | ShouldRetryFunction type ShouldRetryFunction = ( @@ -55,160 +63,152 @@ export function isCancelledError(value: any): value is CancelledError { return value instanceof CancelledError } -// CLASS - -export class Retryer { - cancel: (options?: CancelOptions) => void - cancelRetry: () => void - continueRetry: () => void - continue: () => void - failureCount: number - isPaused: boolean - isResolved: boolean - promise: Promise - - constructor(config: RetryerConfig) { - let cancelRetry = false - let continueFn: ((value?: unknown) => void) | undefined - let promiseResolve: (data: TData) => void - let promiseReject: (error: TError) => void - - this.cancel = (cancelOptions?: CancelOptions): void => { - if (!this.isResolved) { - reject(new CancelledError(cancelOptions)) - - config.abort?.() - } - } - this.cancelRetry = () => { - cancelRetry = true +export function createRetryer( + config: RetryerConfig +): Retryer { + let isRetryCancelled = false + let failureCount = 0 + let isResolved = false + const promise = new Promise((outerResolve, outerReject) => { + promiseResolve = outerResolve + promiseReject = outerReject + }) + let continueFn: ((value?: unknown) => void) | undefined + let promiseResolve: (data: TData) => void + let promiseReject: (error: TError) => void + + const cancel = (cancelOptions?: CancelOptions): void => { + if (!isResolved) { + reject(new CancelledError(cancelOptions)) + + config.abort?.() } + } + const cancelRetry = () => { + isRetryCancelled = true + } - this.continueRetry = () => { - cancelRetry = false - } + const continueRetry = () => { + isRetryCancelled = false + } - const shouldPause = () => - !focusManager.isFocused() || - (config.networkMode !== 'always' && !onlineManager.isOnline()) + const shouldPause = () => + !focusManager.isFocused() || + (config.networkMode !== 'always' && !onlineManager.isOnline()) - this.continue = () => { + const resolve = (value: any) => { + if (!isResolved) { + isResolved = true + config.onSuccess?.(value) continueFn?.() + promiseResolve(value) } - this.failureCount = 0 - this.isPaused = false - this.isResolved = false - this.promise = new Promise((outerResolve, outerReject) => { - promiseResolve = outerResolve - promiseReject = outerReject - }) + } - const resolve = (value: any) => { - if (!this.isResolved) { - this.isResolved = true - config.onSuccess?.(value) - continueFn?.() - promiseResolve(value) - } + const reject = (value: any) => { + if (!isResolved) { + isResolved = true + config.onError?.(value) + continueFn?.() + promiseReject(value) } + } - const reject = (value: any) => { - if (!this.isResolved) { - this.isResolved = true - config.onError?.(value) - continueFn?.() - promiseReject(value) + const pause = () => { + return new Promise(continueResolve => { + continueFn = value => { + if (isResolved || !shouldPause()) { + return continueResolve(value) + } + } + config.onPause?.() + }).then(() => { + continueFn = undefined + if (!isResolved) { + config.onContinue?.() } + }) + } + + // Create loop function + const run = () => { + // Do nothing if already resolved + if (isResolved) { + return } - const pause = () => { - return new Promise(continueResolve => { - continueFn = value => { - if (this.isResolved || !shouldPause()) { - return continueResolve(value) - } - } - this.isPaused = true - config.onPause?.() - }).then(() => { - continueFn = undefined - this.isPaused = false - if (!this.isResolved) { - config.onContinue?.() - } - }) + let promiseOrValue: any + + // Execute query + try { + promiseOrValue = config.fn() + } catch (error) { + promiseOrValue = Promise.reject(error) } - // Create loop function - const run = () => { - // Do nothing if already resolved - if (this.isResolved) { - return - } + Promise.resolve(promiseOrValue) + .then(resolve) + .catch(error => { + // Stop if the fetch is already resolved + if (isResolved) { + return + } - let promiseOrValue: any + // Do we need to retry the request? + const retry = config.retry ?? 3 + const retryDelay = config.retryDelay ?? defaultRetryDelay + const delay = + typeof retryDelay === 'function' + ? retryDelay(failureCount, error) + : retryDelay + const shouldRetry = + retry === true || + (typeof retry === 'number' && failureCount < retry) || + (typeof retry === 'function' && retry(failureCount, error)) + + if (isRetryCancelled || !shouldRetry) { + // We are done if the query does not need to be retried + reject(error) + return + } - // Execute query - try { - promiseOrValue = config.fn() - } catch (error) { - promiseOrValue = Promise.reject(error) - } + failureCount++ + + // Notify on fail + config.onFail?.(failureCount, error) + + // Delay + sleep(delay) + // Pause if the document is not visible or when the device is offline + .then(() => { + if (shouldPause()) { + return pause() + } + }) + .then(() => { + if (isRetryCancelled) { + reject(error) + } else { + run() + } + }) + }) + } - Promise.resolve(promiseOrValue) - .then(resolve) - .catch(error => { - // Stop if the fetch is already resolved - if (this.isResolved) { - return - } - - // Do we need to retry the request? - const retry = config.retry ?? 3 - const retryDelay = config.retryDelay ?? defaultRetryDelay - const delay = - typeof retryDelay === 'function' - ? retryDelay(this.failureCount, error) - : retryDelay - const shouldRetry = - retry === true || - (typeof retry === 'number' && this.failureCount < retry) || - (typeof retry === 'function' && retry(this.failureCount, error)) - - if (cancelRetry || !shouldRetry) { - // We are done if the query does not need to be retried - reject(error) - return - } - - this.failureCount++ - - // Notify on fail - config.onFail?.(this.failureCount, error) - - // Delay - sleep(delay) - // Pause if the document is not visible or when the device is offline - .then(() => { - if (shouldPause()) { - return pause() - } - }) - .then(() => { - if (cancelRetry) { - reject(error) - } else { - run() - } - }) - }) - } + // Start loop + if (canFetch(config.networkMode)) { + run() + } else { + pause().then(run) + } - // Start loop - if (canFetch(config.networkMode)) { - run() - } else { - pause().then(run) - } + return { + promise, + cancel, + continue: () => { + continueFn?.() + }, + cancelRetry, + continueRetry, } } diff --git a/src/core/tests/notifyManager.test.tsx b/src/core/tests/notifyManager.test.tsx index 53816aa767..fc24c2c8c1 100644 --- a/src/core/tests/notifyManager.test.tsx +++ b/src/core/tests/notifyManager.test.tsx @@ -1,9 +1,9 @@ -import { NotifyManager } from '../notifyManager' +import { createNotifyManager } from '../notifyManager' import { sleep } from '../../devtools/tests/utils' describe('notifyManager', () => { it('should use default notifyFn', async () => { - const notifyManagerTest = new NotifyManager() + const notifyManagerTest = createNotifyManager() const callbackSpy = jest.fn() notifyManagerTest.schedule(callbackSpy) await sleep(1) @@ -11,7 +11,7 @@ describe('notifyManager', () => { }) it('should use default batchNotifyFn', async () => { - const notifyManagerTest = new NotifyManager() + const notifyManagerTest = createNotifyManager() const callbackScheduleSpy = jest .fn() .mockImplementation(async () => await sleep(20)) From 942805f8e64bc0981de7b1578afdbc9644d81521 Mon Sep 17 00:00:00 2001 From: Rene Dellefont Date: Fri, 10 Dec 2021 11:41:04 -0500 Subject: [PATCH 03/76] feat(useQueries): v4 api to take an object as input (#3071) * feat(useQueries): update API to use object syntax New v4 API - instead of taking an array of queries, `useQueries` now accepts an object with a `queries` key. The value of this key is an array of queries (this array is unchanged from v3). * test(useQueries): update tests for new API * docs(useQueries): update docs for v4 API --- .../guides/migrating-to-react-query-4.md | 10 + docs/src/pages/guides/parallel-queries.md | 8 +- docs/src/pages/reference/useQueries.md | 12 +- src/reactjs/tests/useQueries.test.tsx | 782 +++++++++--------- src/reactjs/useQueries.ts | 6 +- 5 files changed, 434 insertions(+), 384 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 14002bec89..d8ee9beb50 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -207,6 +207,16 @@ new QueryClient({ }) ``` +### new API for `useQueries` + +The `useQueries` hook now accepts an object with a `queries` prop as its input. The value of the `queries` prop is an array of queries (this array is identical to what was passed into `useQueries` in v3). + +```diff +- useQueries([{ queryKey1, queryFn1, options1 }, { queryKey2, queryFn2, options2 }]) ++ useQueries({ queries: [{ queryKey1, queryFn1, options1 }, { queryKey2, queryFn2, options2 }] }) +``` + + ### Removed undocumented methods from the `queryClient` The methods `cancelMutatations` and `executeMutation` were undocumented and unused internally, so we removed them. Since they were just wrappers around methods available on the `mutationCache`, you can still use the functionality. diff --git a/docs/src/pages/guides/parallel-queries.md b/docs/src/pages/guides/parallel-queries.md index 79dac5ce82..74b20a7015 100644 --- a/docs/src/pages/guides/parallel-queries.md +++ b/docs/src/pages/guides/parallel-queries.md @@ -25,17 +25,17 @@ function App () { If the number of queries you need to execute is changing from render to render, you cannot use manual querying since that would violate the rules of hooks. Instead, React Query provides a `useQueries` hook, which you can use to dynamically execute as many queries in parallel as you'd like. -`useQueries` accepts an **array of query options objects** and returns an **array of query results**: +`useQueries` accepts an **options object** with a **queries key** whose value is an **array of query objects**. It returns an **array of query results**: ```js function App({ users }) { - const userQueries = useQueries( - users.map(user => { + const userQueries = useQueries({ + queries: users.map(user => { return { queryKey: ['user', user.id], queryFn: () => fetchUserById(user.id), } }) - ) + }) } ``` diff --git a/docs/src/pages/reference/useQueries.md b/docs/src/pages/reference/useQueries.md index f69e5fe3af..0849033ef2 100644 --- a/docs/src/pages/reference/useQueries.md +++ b/docs/src/pages/reference/useQueries.md @@ -6,15 +6,17 @@ title: useQueries The `useQueries` hook can be used to fetch a variable number of queries: ```js -const results = useQueries([ - { queryKey: ['post', 1], queryFn: fetchPost }, - { queryKey: ['post', 2], queryFn: fetchPost }, -]) +const results = useQueries({ + queries: [ + { queryKey: ['post', 1], queryFn: fetchPost }, + { queryKey: ['post', 2], queryFn: fetchPost } + ] +}) ``` **Options** -The `useQueries` hook accepts an array with query option objects identical to the [`useQuery` hook](/reference/useQuery). +The `useQueries` hook accepts an options object with a **queries** key whose value is an array with query option objects identical to the [`useQuery` hook](/reference/useQuery). **Returns** diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index 0b549747dc..f6d260cbbe 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -30,22 +30,24 @@ describe('useQueries', () => { const results: UseQueryResult[][] = [] function Page() { - const result = useQueries([ - { - queryKey: key1, - queryFn: async () => { - await sleep(5) - return 1 + const result = useQueries({ + queries: [ + { + queryKey: key1, + queryFn: async () => { + await sleep(5) + return 1 + }, }, - }, - { - queryKey: key2, - queryFn: async () => { - await sleep(10) - return 2 + { + queryKey: key2, + queryFn: async () => { + await sleep(10) + return 2 + }, }, - }, - ]) + ], + }) results.push(result) return null } @@ -67,24 +69,26 @@ describe('useQueries', () => { function Page() { const [count, setCount] = React.useState(1) - const result = useQueries([ - { - queryKey: [key1, count], - keepPreviousData: true, - queryFn: async () => { - await sleep(5) - return count * 2 + const result = useQueries({ + queries: [ + { + queryKey: [key1, count], + keepPreviousData: true, + queryFn: async () => { + await sleep(5) + return count * 2 + }, }, - }, - { - queryKey: [key2, count], - keepPreviousData: true, - queryFn: async () => { - await sleep(10) - return count * 5 + { + queryKey: [key2, count], + keepPreviousData: true, + queryFn: async () => { + await sleep(10) + return count * 5 + }, }, - }, - ]) + ], + }) states.push(result) React.useEffect(() => { @@ -151,16 +155,16 @@ describe('useQueries', () => { function Page() { const [count, setCount] = React.useState(2) - const result = useQueries( - Array.from({ length: count }, (_, i) => ({ + const result = useQueries({ + queries: Array.from({ length: count }, (_, i) => ({ queryKey: [key, count, i + 1], keepPreviousData: true, queryFn: async () => { await sleep(5 * (i + 1)) return (i + 1) * count * 2 }, - })) - ) + })), + }) states.push(result) @@ -261,8 +265,8 @@ describe('useQueries', () => { const [series2, setSeries2] = React.useState(2) const ids = [series1, series2] - const result = useQueries( - ids.map(id => { + const result = useQueries({ + queries: ids.map(id => { return { queryKey: [key, id], queryFn: async () => { @@ -271,8 +275,8 @@ describe('useQueries', () => { }, keepPreviousData: true, } - }) - ) + }), + }) states.push(result) @@ -356,8 +360,8 @@ describe('useQueries', () => { const [enableId1, setEnableId1] = React.useState(true) const ids = enableId1 ? [1, 2] : [2] - const result = useQueries( - ids.map(id => { + const result = useQueries({ + queries: ids.map(id => { return { queryKey: [key, id], queryFn: async () => { @@ -366,8 +370,8 @@ describe('useQueries', () => { }, keepPreviousData: true, } - }) - ) + }), + }) states.push(result) @@ -443,20 +447,22 @@ describe('useQueries', () => { // @ts-expect-error (Page component is not rendered) // eslint-disable-next-line function Page() { - const result1 = useQueries<[[number], [string], [string[], boolean]]>([ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - }, - ]) + const result1 = useQueries<[[number], [string], [string[], boolean]]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }) expectType>(result1[0]) expectType>(result1[1]) expectType>(result1[2]) @@ -468,80 +474,86 @@ describe('useQueries', () => { // TData (3rd element) takes precedence over TQueryFnData (1st element) const result2 = useQueries< [[string, unknown, string], [string, unknown, number]] - >([ - { - queryKey: key1, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return a.toLowerCase() - }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return parseInt(a) - }, - }, - ]) + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + }, + ], + }) expectType>(result2[0]) expectType>(result2[1]) expectType(result2[0].data) expectType(result2[1].data) // types should be enforced - useQueries<[[string, unknown, string], [string, boolean, number]]>([ - { - queryKey: key1, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return a.toLowerCase() - }, - onSuccess: a => { - expectType(a) - expectTypeNotAny(a) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return parseInt(a) - }, - onSuccess: a => { - expectType(a) - expectTypeNotAny(a) - }, - onError: e => { - expectType(e) - expectTypeNotAny(e) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - ]) + useQueries<[[string, unknown, string], [string, boolean, number]]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + onSuccess: a => { + expectType(a) + expectTypeNotAny(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + onSuccess: a => { + expectType(a) + expectTypeNotAny(a) + }, + onError: e => { + expectType(e) + expectTypeNotAny(e) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + ], + }) // field names should be enforced - useQueries<[[string]]>([ - { - queryKey: key1, - queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], - }, - ]) + useQueries<[[string]]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) } }) @@ -559,20 +571,22 @@ describe('useQueries', () => { { queryFnData: string }, { queryFnData: string[]; error: boolean } ] - >([ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - }, - ]) + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }) expectType>(result1[0]) expectType>(result1[1]) expectType>(result1[2]) @@ -587,52 +601,56 @@ describe('useQueries', () => { { queryFnData: string; data: string }, { queryFnData: string; data: number } ] - >([ - { - queryKey: key1, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return a.toLowerCase() - }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return parseInt(a) - }, - }, - ]) + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + }, + ], + }) expectType>(result2[0]) expectType>(result2[1]) expectType(result2[0].data) expectType(result2[1].data) // can pass only TData (data prop) although TQueryFnData will be left unknown - const result3 = useQueries<[{ data: string }, { data: number }]>([ - { - queryKey: key1, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return a as string - }, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return a as number - }, - }, - ]) + const result3 = useQueries<[{ data: string }, { data: number }]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return a as string + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return a as number + }, + }, + ], + }) expectType>(result3[0]) expectType>(result3[1]) expectType(result3[0].data) @@ -644,54 +662,58 @@ describe('useQueries', () => { { queryFnData: string; data: string }, { queryFnData: string; data: number; error: boolean } ] - >([ - { - queryKey: key1, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return a.toLowerCase() - }, - onSuccess: a => { - expectType(a) - expectTypeNotAny(a) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 'string', - select: a => { - expectType(a) - expectTypeNotAny(a) - return parseInt(a) - }, - onSuccess: a => { - expectType(a) - expectTypeNotAny(a) - }, - onError: e => { - expectType(e) - expectTypeNotAny(e) - }, - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - ]) + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + onSuccess: a => { + expectType(a) + expectTypeNotAny(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: a => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + onSuccess: a => { + expectType(a) + expectTypeNotAny(a) + }, + onError: e => { + expectType(e) + expectTypeNotAny(e) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + ], + }) // field names should be enforced - useQueries<[{ queryFnData: string }]>([ - { - queryKey: key1, - queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], - }, - ]) + useQueries<[{ queryFnData: string }]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) } }) @@ -705,40 +727,42 @@ describe('useQueries', () => { // eslint-disable-next-line function Page() { // Array.map preserves TQueryFnData - const result1 = useQueries( - Array(50).map((_, i) => ({ + const result1 = useQueries({ + queries: Array(50).map((_, i) => ({ queryKey: ['key', i] as const, queryFn: () => i + 10, - })) - ) + })), + }) expectType[]>(result1) expectType(result1[0]?.data) // Array.map preserves TData - const result2 = useQueries( - Array(50).map((_, i) => ({ + const result2 = useQueries({ + queries: Array(50).map((_, i) => ({ queryKey: ['key', i] as const, queryFn: () => i + 10, select: (data: number) => data.toString(), - })) - ) + })), + }) expectType[]>(result2) - const result3 = useQueries([ - { - queryKey: key1, - queryFn: () => 1, - }, - { - queryKey: key2, - queryFn: () => 'string', - }, - { - queryKey: key3, - queryFn: () => ['string[]'], - select: () => 123, - }, - ]) + const result3 = useQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + select: () => 123, + }, + ], + }) expectType>(result3[0]) expectType>(result3[1]) expectType>(result3[2]) @@ -748,154 +772,164 @@ describe('useQueries', () => { expectType(result3[2].data) // initialData/placeholderData are enforced - useQueries([ - { - queryKey: key1, - queryFn: () => 'string', - placeholderData: 'string', - // @ts-expect-error (initialData: string) - initialData: 123, - }, - { - queryKey: key2, - queryFn: () => 123, - // @ts-expect-error (placeholderData: number) - placeholderData: 'string', - initialData: 123, - }, - ]) + useQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 123, + // @ts-expect-error (placeholderData: number) + placeholderData: 'string', + initialData: 123, + }, + ], + }) // select / onSuccess / onSettled params are "indirectly" enforced - useQueries([ - // unfortunately TS will not suggest the type for you - { - queryKey: key1, - queryFn: () => 'string', - // @ts-expect-error (noImplicitAny) - onSuccess: a => null, - // @ts-expect-error (noImplicitAny) - onSettled: a => null, - }, - // however you can add a type to the callback - { - queryKey: key2, - queryFn: () => 'string', - onSuccess: (a: string) => { - expectType(a) - expectTypeNotAny(a) - }, - onSettled: (a: string | undefined) => { - expectType(a) - expectTypeNotAny(a) - }, - }, - // the type you do pass is enforced - { - queryKey: key3, - queryFn: () => 'string', - // @ts-expect-error (only accepts string) - onSuccess: (a: number) => null, - }, - { - queryKey: key4, - queryFn: () => 'string', - select: (a: string) => parseInt(a), - // @ts-expect-error (select is defined => only accepts number) - onSuccess: (a: string) => null, - onSettled: (a: number | undefined) => { - expectType(a) - expectTypeNotAny(a) - }, - }, - ]) + useQueries({ + queries: [ + // unfortunately TS will not suggest the type for you + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (noImplicitAny) + onSuccess: a => null, + // @ts-expect-error (noImplicitAny) + onSettled: a => null, + }, + // however you can add a type to the callback + { + queryKey: key2, + queryFn: () => 'string', + onSuccess: (a: string) => { + expectType(a) + expectTypeNotAny(a) + }, + onSettled: (a: string | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + // the type you do pass is enforced + { + queryKey: key3, + queryFn: () => 'string', + // @ts-expect-error (only accepts string) + onSuccess: (a: number) => null, + }, + { + queryKey: key4, + queryFn: () => 'string', + select: (a: string) => parseInt(a), + // @ts-expect-error (select is defined => only accepts number) + onSuccess: (a: string) => null, + onSettled: (a: number | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + ], + }) // callbacks are also indirectly enforced with Array.map - useQueries( + useQueries({ // @ts-expect-error (onSuccess only accepts string) - Array(50).map((_, i) => ({ + queries: Array(50).map((_, i) => ({ queryKey: ['key', i] as const, queryFn: () => i + 10, select: (data: number) => data.toString(), onSuccess: (_data: number) => null, - })) - ) - useQueries( - Array(50).map((_, i) => ({ + })), + }) + useQueries({ + queries: Array(50).map((_, i) => ({ queryKey: ['key', i] as const, queryFn: () => i + 10, select: (data: number) => data.toString(), onSuccess: (_data: string) => null, - })) - ) + })), + }) // results inference works when all the handlers are defined - const result4 = useQueries([ - { - queryKey: key1, - queryFn: () => 'string', - // @ts-expect-error (noImplicitAny) - onSuccess: a => null, - // @ts-expect-error (noImplicitAny) - onSettled: a => null, - }, - { - queryKey: key2, - queryFn: () => 'string', - onSuccess: (a: string) => { - expectType(a) - expectTypeNotAny(a) - }, - onSettled: (a: string | undefined) => { - expectType(a) - expectTypeNotAny(a) - }, - }, - { - queryKey: key4, - queryFn: () => 'string', - select: (a: string) => parseInt(a), - onSuccess: (_a: number) => null, - onSettled: (a: number | undefined) => { - expectType(a) - expectTypeNotAny(a) - }, - }, - ]) + const result4 = useQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (noImplicitAny) + onSuccess: a => null, + // @ts-expect-error (noImplicitAny) + onSettled: a => null, + }, + { + queryKey: key2, + queryFn: () => 'string', + onSuccess: (a: string) => { + expectType(a) + expectTypeNotAny(a) + }, + onSettled: (a: string | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + { + queryKey: key4, + queryFn: () => 'string', + select: (a: string) => parseInt(a), + onSuccess: (_a: number) => null, + onSettled: (a: number | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + ], + }) expectType>(result4[0]) expectType>(result4[1]) expectType>(result4[2]) // Array as const does not throw error - const result5 = useQueries([ - { - queryKey: ['key1'], - queryFn: () => 'string', - }, - { - queryKey: ['key1'], - queryFn: () => 123, - }, - ] as const) + const result5 = useQueries({ + queries: [ + { + queryKey: ['key1'], + queryFn: () => 'string', + }, + { + queryKey: ['key1'], + queryFn: () => 123, + }, + ], + } as const) expectType>(result5[0]) expectType>(result5[1]) // field names should be enforced - array literal - useQueries([ - { - queryKey: key1, - queryFn: () => 'string', - // @ts-expect-error (invalidField) - someInvalidField: [], - }, - ]) + useQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) // field names should be enforced - Array.map() result - useQueries( + useQueries({ // @ts-expect-error (invalidField) - Array(10).map(() => ({ + queries: Array(10).map(() => ({ someInvalidField: '', - })) - ) + })), + }) } }) @@ -918,15 +952,17 @@ describe('useQueries', () => { }) function Queries() { - useQueries([ - { - queryKey: key1, - queryFn: async () => { - await sleep(10) - return 1 + useQueries({ + queries: [ + { + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 1 + }, }, - }, - ]) + ], + }) return (
diff --git a/src/reactjs/useQueries.ts b/src/reactjs/useQueries.ts index 339f6a0937..8f5001e123 100644 --- a/src/reactjs/useQueries.ts +++ b/src/reactjs/useQueries.ts @@ -110,9 +110,11 @@ type QueriesResults< : // Fallback UseQueryResult[] -export function useQueries( +export function useQueries({ + queries, +}: { queries: readonly [...QueriesOptions] -): QueriesResults { +}): QueriesResults { const mountedRef = React.useRef(false) const [, forceUpdate] = React.useState(0) From 95922e7443f7700d5bb5bf65e870c1a746e4722e Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 10 Dec 2021 17:57:27 +0100 Subject: [PATCH 04/76] refactor(tests): adapt useInfiniteQuery test copied over from the react-18 branch, because that impl is less flaky --- src/reactjs/tests/useInfiniteQuery.test.tsx | 31 +++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/reactjs/tests/useInfiniteQuery.test.tsx b/src/reactjs/tests/useInfiniteQuery.test.tsx index 1b15cdb5e2..1e45200f0e 100644 --- a/src/reactjs/tests/useInfiniteQuery.test.tsx +++ b/src/reactjs/tests/useInfiniteQuery.test.tsx @@ -197,25 +197,28 @@ describe('useInfiniteQuery', () => { states.push(state) - const { fetchNextPage } = state + return ( +
+ + +
data: {state.data?.pages.join(',') ?? 'null'}
+
isFetching: {String(state.isFetching)}
+
+ ) + } - React.useEffect(() => { - setActTimeout(() => { - fetchNextPage() - }, 50) - setActTimeout(() => { - setOrder('asc') - }, 100) - }, [fetchNextPage]) + const rendered = renderWithClient(queryClient, ) - return null - } + await waitFor(() => rendered.getByText('data: 0-desc')) + rendered.getByRole('button', { name: /fetchNextPage/i }).click() - renderWithClient(queryClient, ) + await waitFor(() => rendered.getByText('data: 0-desc,1-desc')) + rendered.getByRole('button', { name: /order/i }).click() - await sleep(300) + await waitFor(() => rendered.getByText('data: 0-asc')) + await waitFor(() => rendered.getByText('isFetching: false')) + await waitFor(() => expect(states.length).toBe(7)) - expect(states.length).toBe(7) expect(states[0]).toMatchObject({ data: undefined, isFetching: true, From e200bf775757d3ffd29a028515525e6b547ad768 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 10 Dec 2021 18:04:15 +0100 Subject: [PATCH 05/76] refactor(tests): adapt useInfiniteQuery test another one copied over from the react-18 branch, because that impl is less flaky --- src/reactjs/tests/useQueries.test.tsx | 84 +++++++-------------------- 1 file changed, 22 insertions(+), 62 deletions(-) diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index f6d260cbbe..ff168dd87a 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -280,73 +280,33 @@ describe('useQueries', () => { states.push(result) - React.useEffect(() => { - setActTimeout(() => { - setSeries2(3) - }, 20) - }, []) - - React.useEffect(() => { - setActTimeout(() => { - setSeries1(2) - }, 50) - }, []) + const isFetching = result.some(r => r.isFetching) - return null + return ( +
+
+ data1: {result[0]?.data ?? 'null'}, data2:{' '} + {result[1]?.data ?? 'null'} +
+
isFetching: {String(isFetching)}
+ + +
+ ) } - renderWithClient(queryClient, ) + const rendered = renderWithClient(queryClient, ) - await waitFor(() => expect(states.length).toBe(9)) + await waitFor(() => rendered.getByText('data1: 5, data2: 10')) + rendered.getByRole('button', { name: /setSeries2/i }).click() - expect(states[0]).toMatchObject([ - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - ]) - expect(states[1]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { - status: 'loading', - data: undefined, - isPreviousData: false, - isFetching: true, - }, - ]) - expect(states[2]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: false, isFetching: false }, - ]) - expect(states[3]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: true, isFetching: true }, - ]) - expect(states[4]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 10, isPreviousData: true, isFetching: true }, - ]) - expect(states[5]).toMatchObject([ - { status: 'success', data: 5, isPreviousData: false, isFetching: false }, - { status: 'success', data: 15, isPreviousData: false, isFetching: false }, - ]) - expect(states[6]).toMatchObject([ - { status: 'success', data: 10, isPreviousData: false, isFetching: true }, - { status: 'success', data: 15, isPreviousData: false, isFetching: false }, - ]) - expect(states[7]).toMatchObject([ - { status: 'success', data: 10, isPreviousData: false, isFetching: true }, - { status: 'success', data: 15, isPreviousData: false, isFetching: false }, - ]) - expect(states[8]).toMatchObject([ + await waitFor(() => rendered.getByText('data1: 5, data2: 15')) + rendered.getByRole('button', { name: /setSeries1/i }).click() + + await waitFor(() => rendered.getByText('data1: 10, data2: 15')) + await waitFor(() => rendered.getByText('isFetching: false')) + + expect(states[states.length - 1]).toMatchObject([ { status: 'success', data: 10, isPreviousData: false, isFetching: false }, { status: 'success', data: 15, isPreviousData: false, isFetching: false }, ]) From 3722b6b2cecb76bbc2dfc0187ec6f3b87b38d878 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 11 Dec 2021 07:56:06 +0100 Subject: [PATCH 06/76] refactor: use a set for tracked props (#3085) * refactor: make listeners required because the only place where we pass no listener in is in tests * refactor: make notifyOnChangeProps a Set --- src/core/queryObserver.ts | 21 +++++---------- src/core/subscribable.ts | 8 +++--- src/core/tests/focusManager.test.tsx | 4 +-- src/core/tests/mutationCache.test.tsx | 6 ++--- src/core/tests/onlineManager.test.tsx | 4 +-- src/core/tests/queriesObserver.test.tsx | 2 +- src/core/tests/query.test.tsx | 24 ++++++++--------- src/core/tests/queryClient.test.tsx | 36 ++++++++++++------------- src/core/tests/queryObserver.test.tsx | 14 +++++----- 9 files changed, 55 insertions(+), 64 deletions(-) diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 6ed455d60f..c1688865a3 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -72,7 +72,7 @@ export class QueryObserver< private staleTimeoutId?: number private refetchIntervalId?: number private currentRefetchInterval?: number | false - private trackedProps!: Array + private trackedProps!: Set constructor( client: QueryClient, @@ -88,7 +88,7 @@ export class QueryObserver< this.client = client this.options = options - this.trackedProps = [] + this.trackedProps = new Set() this.previousSelectError = null this.bindMethods() this.setOptions(options) @@ -235,25 +235,19 @@ export class QueryObserver< ): QueryObserverResult { const trackedResult = {} as QueryObserverResult - const trackProp = (key: keyof QueryObserverResult) => { - if (!this.trackedProps.includes(key)) { - this.trackedProps.push(key) - } - } - Object.keys(result).forEach(key => { Object.defineProperty(trackedResult, key, { configurable: false, enumerable: true, get: () => { - trackProp(key as keyof QueryObserverResult) + this.trackedProps.add(key as keyof QueryObserverResult) return result[key as keyof QueryObserverResult] }, }) }) if (defaultedOptions.useErrorBoundary) { - trackProp('error') + this.trackedProps.add('error') } return trackedResult @@ -605,18 +599,17 @@ export class QueryObserver< if ( notifyOnChangeProps === 'all' || - (!notifyOnChangeProps && !this.trackedProps.length) + (!notifyOnChangeProps && !this.trackedProps.size) ) { return true } - const includedProps = notifyOnChangeProps ?? this.trackedProps + const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps) return Object.keys(result).some(key => { const typedKey = key as keyof QueryObserverResult const changed = result[typedKey] !== prevResult[typedKey] - const isIncluded = includedProps?.some(x => x === key) - return changed && (!includedProps || isIncluded) + return changed && includedProps.has(typedKey) }) } diff --git a/src/core/subscribable.ts b/src/core/subscribable.ts index 866d8aba7f..2f574ff1b0 100644 --- a/src/core/subscribable.ts +++ b/src/core/subscribable.ts @@ -7,15 +7,13 @@ export class Subscribable { this.listeners = [] } - subscribe(listener?: TListener): () => void { - const callback = listener || (() => undefined) - - this.listeners.push(callback as TListener) + subscribe(listener: TListener): () => void { + this.listeners.push(listener as TListener) this.onSubscribe() return () => { - this.listeners = this.listeners.filter(x => x !== callback) + this.listeners = this.listeners.filter(x => x !== listener) this.onUnsubscribe() } } diff --git a/src/core/tests/focusManager.test.tsx b/src/core/tests/focusManager.test.tsx index ca071ec16d..368ac62d91 100644 --- a/src/core/tests/focusManager.test.tsx +++ b/src/core/tests/focusManager.test.tsx @@ -69,7 +69,7 @@ describe('focusManager', () => { const setEventListenerSpy = jest.spyOn(focusManager, 'setEventListener') - const unsubscribe = focusManager.subscribe() + const unsubscribe = focusManager.subscribe(() => undefined) expect(setEventListenerSpy).toHaveBeenCalledTimes(0) unsubscribe() @@ -88,7 +88,7 @@ describe('focusManager', () => { ) // Should set the default event listener with window event listeners - const unsubscribe = focusManager.subscribe() + const unsubscribe = focusManager.subscribe(() => undefined) expect(addEventListenerSpy).toHaveBeenCalledTimes(2) // Should replace the window default event listener by a new one diff --git a/src/core/tests/mutationCache.test.tsx b/src/core/tests/mutationCache.test.tsx index ff3ca5cea5..5bafcc2e35 100644 --- a/src/core/tests/mutationCache.test.tsx +++ b/src/core/tests/mutationCache.test.tsx @@ -164,7 +164,7 @@ describe('mutationCache', () => { cacheTime: 10, mutationFn: () => Promise.resolve(), }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) expect(queryClient.getMutationCache().getAll()).toHaveLength(0) observer.mutate(1) @@ -231,7 +231,7 @@ describe('mutationCache', () => { }, onSuccess, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) observer.mutate(1) unsubscribe() expect(queryClient.getMutationCache().getAll()).toHaveLength(1) @@ -257,7 +257,7 @@ describe('mutationCache', () => { }, onSuccess, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) observer.mutate(1) unsubscribe() await waitFor(() => { diff --git a/src/core/tests/onlineManager.test.tsx b/src/core/tests/onlineManager.test.tsx index 2e7d35c3f4..d46f3f2389 100644 --- a/src/core/tests/onlineManager.test.tsx +++ b/src/core/tests/onlineManager.test.tsx @@ -64,7 +64,7 @@ describe('onlineManager', () => { const setEventListenerSpy = jest.spyOn(onlineManager, 'setEventListener') - const unsubscribe = onlineManager.subscribe() + const unsubscribe = onlineManager.subscribe(() => undefined) expect(setEventListenerSpy).toHaveBeenCalledTimes(0) unsubscribe() @@ -83,7 +83,7 @@ describe('onlineManager', () => { ) // Should set the default event listener with window event listeners - const unsubscribe = onlineManager.subscribe() + const unsubscribe = onlineManager.subscribe(() => undefined) expect(addEventListenerSpy).toHaveBeenCalledTimes(2) // Should replace the window default event listener by a new one diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx index c4e5739735..7bf5eef376 100644 --- a/src/core/tests/queriesObserver.test.tsx +++ b/src/core/tests/queriesObserver.test.tsx @@ -248,7 +248,7 @@ describe('queriesObserver', () => { { queryKey: key1, queryFn: queryFn1 }, { queryKey: key2, queryFn: queryFn2 }, ]) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(1) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(1) diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index 118ac6662b..243225ae18 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -224,7 +224,7 @@ describe('query', () => { queryKey: key, enabled: false, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) unsubscribe() await sleep(100) @@ -253,7 +253,7 @@ describe('query', () => { queryKey: key, enabled: false, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) unsubscribe() await sleep(100) @@ -484,10 +484,10 @@ describe('query', () => { cacheTime: 0, staleTime: Infinity, }) - const unsubscribe1 = observer.subscribe() + const unsubscribe1 = observer.subscribe(() => undefined) unsubscribe1() await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) - const unsubscribe2 = observer.subscribe() + const unsubscribe2 = observer.subscribe(() => undefined) unsubscribe2() await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) @@ -502,7 +502,7 @@ describe('query', () => { cacheTime: 0, }) expect(queryCache.find(key)).toBeDefined() - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) expect(queryCache.find(key)).toBeDefined() unsubscribe() await waitFor(() => expect(queryCache.find(key)).toBeUndefined()) @@ -518,7 +518,7 @@ describe('query', () => { }, cacheTime: 10, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(20) expect(queryCache.find(key)).toBeDefined() observer.refetch() @@ -539,7 +539,7 @@ describe('query', () => { cacheTime: 0, }) expect(queryCache.find(key)).toBeDefined() - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(100) expect(queryCache.find(key)).toBeDefined() unsubscribe() @@ -560,9 +560,9 @@ describe('query', () => { expect(query?.getObserversCount()).toEqual(0) - const unsubscribe1 = observer.subscribe() - const unsubscribe2 = observer2.subscribe() - const unsubscribe3 = observer3.subscribe() + const unsubscribe1 = observer.subscribe(() => undefined) + const unsubscribe2 = observer2.subscribe(() => undefined) + const unsubscribe3 = observer3.subscribe(() => undefined) expect(query?.getObserversCount()).toEqual(3) unsubscribe3() @@ -658,7 +658,7 @@ describe('query', () => { }) const refetchSpy = jest.spyOn(observer, 'refetch') - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryCache.onOnline() // Should refetch the observer @@ -821,7 +821,7 @@ describe('query', () => { retry: false, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(10) expect(consoleMock).toHaveBeenCalledWith('Missing queryFn') diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index 53de7d72ae..b72f64ded9 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -722,8 +722,8 @@ describe('queryClient', () => { queryKey: key1, enabled: false, }) - observer1.subscribe() - observer2.subscribe() + observer1.subscribe(() => undefined) + observer2.subscribe(() => undefined) await queryClient.refetchQueries() observer1.destroy() observer2.destroy() @@ -743,7 +743,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await queryClient.refetchQueries({ type: 'active', stale: false }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) @@ -761,7 +761,7 @@ describe('queryClient', () => { queryKey: key1, queryFn: queryFn1, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryClient.invalidateQueries(key1) await queryClient.refetchQueries({ stale: true }) unsubscribe() @@ -782,7 +782,7 @@ describe('queryClient', () => { queryKey: key1, queryFn: queryFn1, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await queryClient.refetchQueries( { type: 'active', stale: true }, { cancelRefetch: false } @@ -804,7 +804,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await queryClient.refetchQueries() unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) @@ -823,7 +823,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await queryClient.refetchQueries({ type: 'all' }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) @@ -842,7 +842,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await queryClient.refetchQueries({ type: 'active' }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) @@ -861,7 +861,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await queryClient.refetchQueries({ type: 'inactive' }) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(1) @@ -906,7 +906,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryClient.invalidateQueries(key1) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) @@ -925,7 +925,7 @@ describe('queryClient', () => { enabled: false, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryClient.invalidateQueries(key1) unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(1) @@ -944,7 +944,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryClient.invalidateQueries(key1, { refetchType: 'none', }) @@ -966,7 +966,7 @@ describe('queryClient', () => { staleTime: Infinity, enabled: false, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryClient.invalidateQueries(key1, { refetchType: 'inactive', }) @@ -987,7 +987,7 @@ describe('queryClient', () => { queryFn: queryFn1, staleTime: Infinity, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) queryClient.invalidateQueries({ refetchType: 'all', }) @@ -1005,7 +1005,7 @@ describe('queryClient', () => { enabled: false, initialData: 1, }) - observer.subscribe() + observer.subscribe(() => undefined) queryClient.fetchQuery(key, ({ signal }) => { const promise = new Promise(resolve => { @@ -1036,7 +1036,7 @@ describe('queryClient', () => { enabled: false, initialData: 1, }) - observer.subscribe() + observer.subscribe(() => undefined) queryClient.fetchQuery(key, ({ signal }) => { const promise = new Promise(resolve => { @@ -1125,8 +1125,8 @@ describe('queryClient', () => { queryFn: queryFn2, enabled: false, }) - observer1.subscribe() - observer2.subscribe() + observer1.subscribe(() => undefined) + observer2.subscribe(() => undefined) await queryClient.resetQueries() observer2.destroy() observer1.destroy() diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index 31fb1ea27b..9daf7d8207 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -27,7 +27,7 @@ describe('queryObserver', () => { const key = queryKey() const queryFn = jest.fn() const observer = new QueryObserver(queryClient, { queryKey: key, queryFn }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(1) unsubscribe() expect(queryFn).toHaveBeenCalledTimes(1) @@ -315,7 +315,7 @@ describe('queryObserver', () => { queryFn, enabled: false, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(1) unsubscribe() expect(queryFn).toHaveBeenCalledTimes(0) @@ -441,7 +441,7 @@ describe('queryObserver', () => { retry: 10, retryDelay: 50, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(70) unsubscribe() await sleep(200) @@ -459,7 +459,7 @@ describe('queryObserver', () => { cacheTime: 0, refetchInterval: 1, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) // @ts-expect-error expect(observer.refetchIntervalId).not.toBeUndefined() unsubscribe() @@ -511,7 +511,7 @@ describe('queryObserver', () => { retryDelay: 20, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) // Simulate a race condition when an unsubscribe and a retry occur. await sleep(20) @@ -588,7 +588,7 @@ describe('queryObserver', () => { refetchInterval: 10, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(30) expect(queryFn).toHaveBeenCalledTimes(1) @@ -610,7 +610,7 @@ describe('queryObserver', () => { select: () => data, }) - const unsubscribe = observer.subscribe() + const unsubscribe = observer.subscribe(() => undefined) await sleep(10) expect(observer.getCurrentResult().data).toBe(data) From 5e2f2c2b644c03433c15e3ba61f884e142098fc7 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 11 Dec 2021 09:37:20 +0100 Subject: [PATCH 07/76] chore: update tooling (#3086) * chore: update caniuselite * chore: remove const-enum babel plugin because we don't use const-enums anymore * chore: require ts 4.1 * chore: update rollup and plugins * chore: update typescript and ts-eslint * refactor: remove notifiable base class this abstraction costs more than the simple duplication, and we also have other notify functions which were not unified --- .babelrc.js | 6 - .browserslistrc | 3 +- .../guides/migrating-to-react-query-4.md | 4 + docs/src/pages/typescript.md | 2 +- package.json | 32 +- rollup.config.js | 1 + src/core/mutationCache.ts | 14 +- src/core/notifiable.ts | 12 - src/core/queryCache.ts | 14 +- src/core/queryObserver.ts | 10 +- src/reactjs/useQueries.ts | 2 +- src/ts3.8/index.ts | 4 - src/ts3.8/useQueries.ts | 6 - tsconfig.types.json | 1 - yarn.lock | 2462 ++++++++++------- 15 files changed, 1486 insertions(+), 1087 deletions(-) delete mode 100644 src/core/notifiable.ts delete mode 100644 src/ts3.8/index.ts delete mode 100644 src/ts3.8/useQueries.ts diff --git a/.babelrc.js b/.babelrc.js index bdc7423b3f..6218e87224 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -16,12 +16,6 @@ module.exports = { '@babel/react', ], plugins: [ - [ - 'const-enum', - { - transform: 'constObject', - }, - ], 'babel-plugin-transform-async-to-promises', cjs && ['@babel/transform-modules-commonjs', { loose }], [ diff --git a/.browserslistrc b/.browserslistrc index 41bcaa1d38..914ab12d22 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -4,6 +4,5 @@ Chrome >= 73 ChromeAndroid >= 75 Firefox >= 67 Edge >= 17 -IE 11 Safari >= 12.1 -iOS >= 11.3 \ No newline at end of file +iOS >= 11.3 diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index d8ee9beb50..630d4b225f 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -243,6 +243,10 @@ The methods `cancelMutatations` and `executeMutation` were undocumented and unus - } ``` +### TypeScript + +Types now require using TypeScript v4.1 or greater + ## New Features 🚀 ### Proper offline support diff --git a/docs/src/pages/typescript.md b/docs/src/pages/typescript.md index 0e7926bd19..3d07b79ca8 100644 --- a/docs/src/pages/typescript.md +++ b/docs/src/pages/typescript.md @@ -7,7 +7,7 @@ React Query is now written in **TypeScript** to make sure the library and your p Things to keep in mind: -- Types currently require using TypeScript v3.8 or greater +- Types currently require using TypeScript v4.1 or greater - for [useQueries](../reference/useQueries), TypeScript v4.1 or greater is required to get the correct return type for each individual query (below v4.1 will mean the type of each returned `data` property is left as `unknown`) - Changes to types in this repository are considered **non-breaking** and are usually released as **patch** semver changes (otherwise every type enhancement would be a major version!). - It is **highly recommended that you lock your react-query package version to a specific patch release and upgrade with the expectation that types may be fixed or upgraded between any release** diff --git a/package.json b/package.json index 0c927899a0..eefa599c04 100644 --- a/package.json +++ b/package.json @@ -80,13 +80,6 @@ "optional": true } }, - "typesVersions": { - "<4.1": { - "types/*": [ - "types/ts3.8/*" - ] - } - }, "devDependencies": { "@babel/cli": "^7.11.6", "@babel/core": "^7.11.6", @@ -94,18 +87,17 @@ "@babel/preset-env": "^7.11.5", "@babel/preset-react": "^7.10.4", "@babel/preset-typescript": "^7.10.4", - "@rollup/plugin-replace": "^2.3.3", - "@svgr/rollup": "^5.4.0", + "@rollup/plugin-replace": "^3.0.0", + "@svgr/rollup": "^6.1.1", "@testing-library/react": "^10.4.7", "@testing-library/jest-dom": "^5.14.1", "@types/jest": "^26.0.4", "@types/react": "^16.9.41", "@types/react-dom": "^16.9.8", - "@typescript-eslint/eslint-plugin": "^4.8.1", - "@typescript-eslint/parser": "^4.8.1", + "@typescript-eslint/eslint-plugin": "^5.6.0", + "@typescript-eslint/parser": "^5.6.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.0.1", - "babel-plugin-const-enum": "^1.0.1", "babel-plugin-transform-async-to-promises": "^0.8.15", "bundlewatch": "^0.3.2", "cross-env": "^7.0.2", @@ -121,7 +113,7 @@ "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-react": "7.20.0", - "eslint-plugin-react-hooks": "4.2.0", + "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-standard": "^4.0.1", "is-ci-cli": "^2.1.1", "jest": "^26.0.1", @@ -131,18 +123,18 @@ "react-error-boundary": "^2.2.2", "replace": "^1.2.0", "rimraf": "^3.0.2", - "rollup": "^2.16.1", + "rollup": "^2.61.1", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-commonjs": "^10.1.0", - "rollup-plugin-jscc": "^1.0.0", + "rollup-plugin-jscc": "^2.0.0", "rollup-plugin-node-resolve": "^5.2.0", - "rollup-plugin-peer-deps-external": "^2.2.2", - "rollup-plugin-prettier": "^2.1.0", + "rollup-plugin-peer-deps-external": "^2.2.4", + "rollup-plugin-prettier": "^2.2.0", "rollup-plugin-size": "^0.2.2", - "rollup-plugin-terser": "^6.1.0", - "rollup-plugin-visualizer": "^4.0.4", + "rollup-plugin-terser": "^7.0.2", + "rollup-plugin-visualizer": "^5.5.2", "type-fest": "^0.21.0", - "typescript": "^4.1.2" + "typescript": "4.5.3" }, "resolutions": { "@types/react": "^16.9.41", diff --git a/rollup.config.js b/rollup.config.js index 35fdd215aa..b54e73efaa 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -79,6 +79,7 @@ export default inputSrcs replace({ 'process.env.NODE_ENV': `"production"`, delimiters: ['', ''], + preventAssignment: true, }), resolve(resolveConfig), babel(babelConfig), diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts index d333ccb26f..be44e44b7b 100644 --- a/src/core/mutationCache.ts +++ b/src/core/mutationCache.ts @@ -4,7 +4,7 @@ import type { QueryClient } from './queryClient' import { notifyManager } from './notifyManager' import { Action, Mutation, MutationState } from './mutation' import { matchMutation, MutationFilters, noop } from './utils' -import { Notifiable } from './notifiable' +import { Subscribable } from './subscribable' // TYPES @@ -61,9 +61,11 @@ type MutationCacheNotifyEvent = | NotifyEventMutationObserverRemoved | NotifyEventMutationUpdated +type MutationCacheListener = (event: MutationCacheNotifyEvent) => void + // CLASS -export class MutationCache extends Notifiable { +export class MutationCache extends Subscribable { config: MutationCacheConfig private mutations: Mutation[] @@ -134,6 +136,14 @@ export class MutationCache extends Notifiable { return this.mutations.filter(mutation => matchMutation(filters, mutation)) } + notify(event: MutationCacheNotifyEvent) { + notifyManager.batch(() => { + this.listeners.forEach(listener => { + listener(event) + }) + }) + } + onFocus(): void { this.resumePausedMutations() } diff --git a/src/core/notifiable.ts b/src/core/notifiable.ts deleted file mode 100644 index 3d2bcd857f..0000000000 --- a/src/core/notifiable.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Subscribable } from './subscribable' -import { notifyManager } from '../core/notifyManager' - -export class Notifiable extends Subscribable<(event: TEvent) => void> { - notify(event: TEvent) { - notifyManager.batch(() => { - this.listeners.forEach(listener => { - listener(event) - }) - }) - } -} diff --git a/src/core/queryCache.ts b/src/core/queryCache.ts index a5dadc7022..730e42a7de 100644 --- a/src/core/queryCache.ts +++ b/src/core/queryCache.ts @@ -8,7 +8,7 @@ import { Action, Query, QueryState } from './query' import type { QueryKey, QueryOptions } from './types' import { notifyManager } from './notifyManager' import type { QueryClient } from './queryClient' -import { Notifiable } from './notifiable' +import { Subscribable } from './subscribable' import { QueryObserver } from './queryObserver' // TYPES @@ -63,9 +63,11 @@ type QueryCacheNotifyEvent = | NotifyEventQueryObserverRemoved | NotifyEventQueryObserverResultsUpdated +type QueryCacheListener = (event: QueryCacheNotifyEvent) => void + // CLASS -export class QueryCache extends Notifiable { +export class QueryCache extends Subscribable { config: QueryCacheConfig private queries: Query[] @@ -177,6 +179,14 @@ export class QueryCache extends Notifiable { : this.queries } + notify(event: QueryCacheNotifyEvent) { + notifyManager.batch(() => { + this.listeners.forEach(listener => { + listener(event) + }) + }) + } + onFocus(): void { notifyManager.batch(() => { this.queries.forEach(query => { diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index c1688865a3..526bb07593 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -68,7 +68,7 @@ export class QueryObserver< TQueryKey > private previousQueryResult?: QueryObserverResult - private previousSelectError: Error | null + private previousSelectError: TError | null private staleTimeoutId?: number private refetchIntervalId?: number private currentRefetchInterval?: number | false @@ -496,8 +496,8 @@ export class QueryObserver< this.previousSelectError = null } catch (selectError) { getLogger().error(selectError) - error = selectError - this.previousSelectError = selectError + error = selectError as TError + this.previousSelectError = selectError as TError errorUpdatedAt = Date.now() status = 'error' } @@ -539,8 +539,8 @@ export class QueryObserver< this.previousSelectError = null } catch (selectError) { getLogger().error(selectError) - error = selectError - this.previousSelectError = selectError + error = selectError as TError + this.previousSelectError = selectError as TError errorUpdatedAt = Date.now() status = 'error' } diff --git a/src/reactjs/useQueries.ts b/src/reactjs/useQueries.ts index 8f5001e123..49f87f2f1f 100644 --- a/src/reactjs/useQueries.ts +++ b/src/reactjs/useQueries.ts @@ -9,7 +9,7 @@ import { UseQueryOptions, UseQueryResult } from './types' // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 -type GetOptions = +type GetOptions = // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData } T extends { queryFnData: infer TQueryFnData diff --git a/src/ts3.8/index.ts b/src/ts3.8/index.ts deleted file mode 100644 index 38671e3b63..0000000000 --- a/src/ts3.8/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { useQueries } from './useQueries' -export { useQueries } - -export * from '..' diff --git a/src/ts3.8/useQueries.ts b/src/ts3.8/useQueries.ts deleted file mode 100644 index a143b5359f..0000000000 --- a/src/ts3.8/useQueries.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { UseQueryOptions, UseQueryResult } from '../reactjs/types' - -/** - * Backwards-compatible definition for TS < 4.1 - */ -export declare function useQueries(queries: UseQueryOptions[]): UseQueryResult[] diff --git a/tsconfig.types.json b/tsconfig.types.json index 6f6510ecd5..6544e6c808 100644 --- a/tsconfig.types.json +++ b/tsconfig.types.json @@ -16,7 +16,6 @@ "./src/createWebStoragePersister/index.ts", "./src/createAsyncStoragePersister/index.ts", "./src/broadcastQueryClient-experimental/index.ts", - "./src/ts3.8/index.ts" ], "exclude": ["./src/**/*"] } diff --git a/yarn.lock b/yarn.lock index 0c399fcdcb..433e909f92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,21 +39,12 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/compat-data@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.1.tgz#b1085ffe72cd17bf2c0ee790fc09f9626011b2db" - integrity sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw== +"@babel/code-frame@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" + integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== dependencies: - browserslist "^4.12.0" - invariant "^2.2.4" - semver "^5.5.0" + "@babel/highlight" "^7.16.0" "@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": version "7.11.0" @@ -64,6 +55,11 @@ invariant "^2.2.4" semver "^5.5.0" +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.0", "@babel/compat-data@^7.16.4": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" + integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== + "@babel/core@^7.1.0": version "7.7.4" resolved "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz#37e864532200cb6b50ee9a4045f5f817840166ab" @@ -106,6 +102,27 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.15.5": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" + integrity sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/generator" "^7.16.0" + "@babel/helper-compilation-targets" "^7.16.0" + "@babel/helper-module-transforms" "^7.16.0" + "@babel/helpers" "^7.16.0" + "@babel/parser" "^7.16.0" + "@babel/template" "^7.16.0" + "@babel/traverse" "^7.16.0" + "@babel/types" "^7.16.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + "@babel/core@^7.7.5": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" @@ -156,6 +173,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" + integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew== + dependencies: + "@babel/types" "^7.16.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.6.3": version "7.6.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" @@ -190,13 +216,12 @@ dependencies: "@babel/types" "^7.10.4" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz#0ec7d9be8174934532661f87783eb18d72290059" - integrity sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw== +"@babel/helper-annotate-as-pure@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz#9a1f0ebcda53d9a2d00108c4ceace6a5d5f1f08d" + integrity sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg== dependencies: - "@babel/helper-explode-assignable-expression" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/types" "^7.16.0" "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": version "7.10.4" @@ -206,14 +231,13 @@ "@babel/helper-explode-assignable-expression" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helper-builder-react-jsx-experimental@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz#9a7d58ad184d3ac3bafb1a452cec2bad7e4a0bc8" - integrity sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz#f1a686b92da794020c26582eb852e9accd0d7882" + integrity sha512-9KuleLT0e77wFUku6TUkqZzCEymBdtuQQ27MhEKzf9UOOJu3cYj98kyaDAzxpC7lV6DGiZFuC8XqDsq8/Kl6aQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-module-imports" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-explode-assignable-expression" "^7.16.0" + "@babel/types" "^7.16.0" "@babel/helper-builder-react-jsx-experimental@^7.10.4", "@babel/helper-builder-react-jsx-experimental@^7.11.5": version "7.11.5" @@ -224,14 +248,6 @@ "@babel/helper-module-imports" "^7.10.4" "@babel/types" "^7.11.5" -"@babel/helper-builder-react-jsx@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz#a327f0cf983af5554701b1215de54a019f09b532" - integrity sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/types" "^7.10.1" - "@babel/helper-builder-react-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" @@ -240,17 +256,6 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helper-compilation-targets@^7.10.2": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz#a17d9723b6e2c750299d2a14d4637c76936d8285" - integrity sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA== - dependencies: - "@babel/compat-data" "^7.10.1" - browserslist "^4.12.0" - invariant "^2.2.4" - levenary "^1.1.1" - semver "^5.5.0" - "@babel/helper-compilation-targets@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" @@ -262,17 +267,15 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/helper-create-class-features-plugin@^7.10.1": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz#7474295770f217dbcf288bf7572eb213db46ee67" - integrity sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ== +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.0", "@babel/helper-compilation-targets@^7.16.3": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" + integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-member-expression-to-functions" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" + "@babel/compat-data" "^7.16.0" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.17.5" + semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": version "7.10.5" @@ -286,7 +289,19 @@ "@babel/helper-replace-supers" "^7.10.4" "@babel/helper-split-export-declaration" "^7.10.4" -"@babel/helper-create-regexp-features-plugin@^7.10.1", "@babel/helper-create-regexp-features-plugin@^7.8.3": +"@babel/helper-create-class-features-plugin@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz#090d4d166b342a03a9fec37ef4fd5aeb9c7c6a4b" + integrity sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-member-expression-to-functions" "^7.16.0" + "@babel/helper-optimise-call-expression" "^7.16.0" + "@babel/helper-replace-supers" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + +"@babel/helper-create-regexp-features-plugin@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" integrity sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA== @@ -304,14 +319,13 @@ "@babel/helper-regex" "^7.10.4" regexpu-core "^4.7.0" -"@babel/helper-define-map@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz#5e69ee8308648470dd7900d159c044c10285221d" - integrity sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg== +"@babel/helper-create-regexp-features-plugin@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz#06b2348ce37fccc4f5e18dcd8d75053f2a7c44ff" + integrity sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/types" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-annotate-as-pure" "^7.16.0" + regexpu-core "^4.7.1" "@babel/helper-define-map@^7.10.4": version "7.10.5" @@ -322,13 +336,19 @@ "@babel/types" "^7.10.5" lodash "^4.17.19" -"@babel/helper-explode-assignable-expression@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz#e9d76305ee1162ca467357ae25df94f179af2b7e" - integrity sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg== +"@babel/helper-define-polyfill-provider@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz#c5b10cf4b324ff840140bb07e05b8564af2ae971" + integrity sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg== dependencies: - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" "@babel/helper-explode-assignable-expression@^7.10.4": version "7.11.4" @@ -337,6 +357,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-explode-assignable-expression@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz#753017337a15f46f9c09f674cff10cee9b9d7778" + integrity sha512-Hk2SLxC9ZbcOhLpg/yMznzJ11W++lg5GMbxt1ev6TXUiJB0N42KPC+7w8a+eWGuqDnUYuwStJoZHM7RgmIOaGQ== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-function-name@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" @@ -364,6 +391,15 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-function-name@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" + integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== + dependencies: + "@babel/helper-get-function-arity" "^7.16.0" + "@babel/template" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helper-function-name@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" @@ -394,6 +430,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-get-function-arity@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" + integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-get-function-arity@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" @@ -401,13 +444,6 @@ dependencies: "@babel/types" "^7.7.4" -"@babel/helper-hoist-variables@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz#7e77c82e5dcae1ebf123174c385aaadbf787d077" - integrity sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg== - dependencies: - "@babel/types" "^7.10.1" - "@babel/helper-hoist-variables@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" @@ -415,6 +451,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-hoist-variables@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" + integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-member-expression-to-functions@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" @@ -429,6 +472,13 @@ dependencies: "@babel/types" "^7.10.5" +"@babel/helper-member-expression-to-functions@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz#29287040efd197c77636ef75188e81da8bccd5a4" + integrity sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-module-imports@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" @@ -450,6 +500,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3" + integrity sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-module-transforms@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" @@ -476,6 +533,20 @@ "@babel/types" "^7.11.0" lodash "^4.17.19" +"@babel/helper-module-transforms@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" + integrity sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA== + dependencies: + "@babel/helper-module-imports" "^7.16.0" + "@babel/helper-replace-supers" "^7.16.0" + "@babel/helper-simple-access" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/helper-validator-identifier" "^7.15.7" + "@babel/template" "^7.16.0" + "@babel/traverse" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helper-optimise-call-expression@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" @@ -490,6 +561,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-optimise-call-expression@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz#cecdb145d70c54096b1564f8e9f10cd7d193b338" + integrity sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" @@ -505,6 +583,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + "@babel/helper-plugin-utils@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" @@ -524,17 +607,6 @@ dependencies: lodash "^4.17.19" -"@babel/helper-remap-async-to-generator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz#bad6aaa4ff39ce8d4b82ccaae0bfe0f7dbb5f432" - integrity sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-wrap-function" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" - "@babel/helper-remap-async-to-generator@^7.10.4": version "7.11.4" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d" @@ -545,6 +617,15 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-remap-async-to-generator@^7.16.0", "@babel/helper-remap-async-to-generator@^7.16.4": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.4.tgz#5d7902f61349ff6b963e07f06a389ce139fbfe6e" + integrity sha512-vGERmmhR+s7eH5Y/cp8PCVzj4XEjerq8jooMfxFdA5xVtAk9Sh4AQsrWgiErUEBjtGrBtOFKDUcWQFW4/dFwMA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-wrap-function" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helper-replace-supers@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" @@ -565,6 +646,16 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-replace-supers@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz#73055e8d3cf9bcba8ddb55cad93fedc860f68f17" + integrity sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.16.0" + "@babel/helper-optimise-call-expression" "^7.16.0" + "@babel/traverse" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helper-simple-access@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" @@ -581,6 +672,13 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-simple-access@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz#21d6a27620e383e37534cf6c10bba019a6f90517" + integrity sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-skip-transparent-expression-wrappers@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" @@ -588,6 +686,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" + integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-split-export-declaration@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz#c6f4be1cbc15e3a868e4c64a17d5d31d754da35f" @@ -609,6 +714,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-split-export-declaration@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" + integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== + dependencies: + "@babel/types" "^7.16.0" + "@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" @@ -628,25 +740,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz#5770b0c1a826c4f53f5ede5e153163e0318e94b5" integrity sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw== -"@babel/helper-validator-identifier@^7.10.3": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz#60d9847f98c4cea1b279e005fdb7c28be5412d15" - integrity sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw== - "@babel/helper-validator-identifier@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/helper-wrap-function@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz#956d1310d6696257a7afd47e4c42dfda5dfcedc9" - integrity sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ== - dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/template" "^7.10.1" - "@babel/traverse" "^7.10.1" - "@babel/types" "^7.10.1" +"@babel/helper-validator-identifier@^7.15.7": + version "7.15.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" + integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== "@babel/helper-wrap-function@^7.10.4": version "7.10.4" @@ -658,6 +765,16 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-wrap-function@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz#b3cf318afce774dfe75b86767cd6d68f3482e57c" + integrity sha512-VVMGzYY3vkWgCJML+qVLvGIam902mJW0FvT7Avj1zEe0Gn7D93aWdLblYARTxEw+6DhZmtzhBM2zv0ekE5zg1g== + dependencies: + "@babel/helper-function-name" "^7.16.0" + "@babel/template" "^7.16.0" + "@babel/traverse" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/helpers@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" @@ -676,6 +793,15 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helpers@^7.16.0": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" + integrity sha512-Xn8IhDlBPhvYTvgewPKawhADichOsbkZuzN7qz2BusOM0brChsyXMDJvldWaYMMUNiCQdQzNEioXTp3sC8Nt8w== + dependencies: + "@babel/template" "^7.16.0" + "@babel/traverse" "^7.16.3" + "@babel/types" "^7.16.0" + "@babel/helpers@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" @@ -712,12 +838,12 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.8.3": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.3.tgz#c633bb34adf07c5c13156692f5922c81ec53f28d" - integrity sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw== +"@babel/highlight@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" + integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== dependencies: - "@babel/helper-validator-identifier" "^7.10.3" + "@babel/helper-validator-identifier" "^7.15.7" chalk "^2.0.0" js-tokens "^4.0.0" @@ -741,19 +867,31 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.16.0", "@babel/parser@^7.16.3": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" + integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== + "@babel/parser@^7.6.0", "@babel/parser@^7.6.3": version "7.6.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A== -"@babel/plugin-proposal-async-generator-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz#6911af5ba2e615c4ff3c497fe2f47b35bf6d7e55" - integrity sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.2": + version "7.16.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.2.tgz#2977fca9b212db153c195674e57cfab807733183" + integrity sha512-h37CvpLSf8gb2lIJ2CgC3t+EjFbi0t8qS7LCS1xcJIlEXE4czlofwaW7W1HA8zpgOCzI9C1nmoqNR1zWkk0pQg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-remap-async-to-generator" "^7.10.1" - "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz#358972eaab006f5eb0826183b0c93cbcaf13e1e2" + integrity sha512-4tcFwwicpWTrpl9qjf7UsoosaArgImF85AxqCRZlgc3IQDvkUHjJpruXAL58Wmj+T6fypWTC/BakfEkwIL/pwA== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.0" "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" @@ -764,13 +902,14 @@ "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-class-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz#046bc7f6550bb08d9bd1d4f060f5f5a4f1087e01" - integrity sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw== +"@babel/plugin-proposal-async-generator-functions@^7.16.4": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.4.tgz#e606eb6015fec6fa5978c940f315eae4e300b081" + integrity sha512-/CUekqaAaZCQHleSK/9HajvcD/zdnJiKRiuUFq8ITE+0HsPzquf53cpFiqAwl/UfmJbR6n5uGPQSPdrmKOvHHg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-remap-async-to-generator" "^7.16.4" + "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.10.4": version "7.10.4" @@ -780,13 +919,22 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-proposal-dynamic-import@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz#e36979dc1dc3b73f6d6816fc4951da2363488ef0" - integrity sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA== +"@babel/plugin-proposal-class-properties@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz#c029618267ddebc7280fa286e0f8ca2a278a2d1a" + integrity sha512-mCF3HcuZSY9Fcx56Lbn+CGdT44ioBMMvjNVldpKtj8tpniETdLjnxdHI1+sDWXIM1nNt+EanJOZ3IG9lzVjs7A== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/helper-create-class-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-proposal-class-static-block@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.0.tgz#5296942c564d8144c83eea347d0aa8a0b89170e7" + integrity sha512-mAy3sdcY9sKAkf3lQbDiv3olOfiLqI51c9DR9b19uMoR2Z6r5pmGl7dfNFqEvqOyqbf1ta4lknK4gc5PJn3mfA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.10.4": version "7.10.4" @@ -796,6 +944,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" +"@babel/plugin-proposal-dynamic-import@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.0.tgz#783eca61d50526202f9b296095453977e88659f1" + integrity sha512-QGSA6ExWk95jFQgwz5GQ2Dr95cf7eI7TKutIXXTb7B1gCLTCz5hTjFTQGfLFBBiC5WSNi7udNwWsqbbMh1c4yQ== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-export-namespace-from@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" @@ -804,13 +960,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz#b1e691ee24c651b5a5e32213222b2379734aff09" - integrity sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg== +"@babel/plugin-proposal-export-namespace-from@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.0.tgz#9c01dee40b9d6b847b656aaf4a3976a71740f222" + integrity sha512-CjI4nxM/D+5wCnhD11MHB1AwRSAYeDT+h8gCdcVJZ/OK7+wRzFsf7PFPWVpVpNRkHMmMkQWAHpTq+15IXQ1diA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.10.4": version "7.10.4" @@ -820,6 +976,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" +"@babel/plugin-proposal-json-strings@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.0.tgz#cae35a95ed1d2a7fa29c4dc41540b84a72e9ab25" + integrity sha512-kouIPuiv8mSi5JkEhzApg5Gn6hFyKPnlkO0a9YSzqRurH8wYzSlf6RJdzluAsbqecdW5pBvDJDfyDIUR/vLxvg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-proposal-logical-assignment-operators@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" @@ -828,13 +992,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz#02dca21673842ff2fe763ac253777f235e9bbf78" - integrity sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA== +"@babel/plugin-proposal-logical-assignment-operators@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.0.tgz#a711b8ceb3ffddd3ef88d3a49e86dbd3cc7db3fd" + integrity sha512-pbW0fE30sVTYXXm9lpVQQ/Vc+iTeQKiXlaNRZPPN2A2VdlWyAtsUrsQ3xydSlDW00TFMK7a8m3cDTkBF5WnV3Q== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": version "7.10.4" @@ -844,13 +1008,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz#a9a38bc34f78bdfd981e791c27c6fdcec478c123" - integrity sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA== +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz#44e1cce08fe2427482cf446a91bb451528ed0596" + integrity sha512-3bnHA8CAFm7cG93v8loghDYyQ8r97Qydf63BeYiGgYbjKKB/XP53W15wfRC7dvKfoiJ34f6Rbyyx2btExc8XsQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-numeric-separator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" "@babel/plugin-proposal-numeric-separator@^7.10.4": version "7.10.4" @@ -860,14 +1024,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz#cba44908ac9f142650b4a65b8aa06bf3478d5fb6" - integrity sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ== +"@babel/plugin-proposal-numeric-separator@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz#5d418e4fbbf8b9b7d03125d3a52730433a373734" + integrity sha512-FAhE2I6mjispy+vwwd6xWPyEx3NYFS13pikDBWUAFGZvq6POGs5eNchw8+1CYoEgBl9n11I3NkzD7ghn25PQ9Q== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@^7.11.0": version "7.11.0" @@ -878,13 +1041,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.10.4" -"@babel/plugin-proposal-optional-catch-binding@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz#c9f86d99305f9fa531b568ff5ab8c964b8b223d2" - integrity sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA== +"@babel/plugin-proposal-object-rest-spread@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.0.tgz#5fb32f6d924d6e6712810362a60e12a2609872e6" + integrity sha512-LU/+jp89efe5HuWJLmMmFG0+xbz+I2rSI7iLc1AlaeSMDMOGzWlc5yJrMN1d04osXN4sSfpo4O+azkBNBes0jg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/compat-data" "^7.16.0" + "@babel/helper-compilation-targets" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.16.0" "@babel/plugin-proposal-optional-catch-binding@^7.10.4": version "7.10.4" @@ -894,13 +1060,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz#15f5d6d22708629451a91be28f8facc55b0e818c" - integrity sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA== +"@babel/plugin-proposal-optional-catch-binding@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.0.tgz#5910085811ab4c28b00d6ebffa4ab0274d1e5f16" + integrity sha512-kicDo0A/5J0nrsCPbn89mTG3Bm4XgYi0CZtvex9Oyw7gGZE3HXGD0zpQNH+mo+tEfbo8wbmMvJftOwpmPy7aVw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-proposal-optional-chaining@^7.11.0": version "7.11.0" @@ -911,13 +1077,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" -"@babel/plugin-proposal-private-methods@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz#ed85e8058ab0fe309c3f448e5e1b73ca89cdb598" - integrity sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg== +"@babel/plugin-proposal-optional-chaining@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz#56dbc3970825683608e9efb55ea82c2a2d6c8dc0" + integrity sha512-Y4rFpkZODfHrVo70Uaj6cC1JJOt3Pp0MdWSwIKtb8z1/lsjl9AmnB7ErRFV+QNGIfcY1Eruc2UMx5KaRnXjMyg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-proposal-private-methods@^7.10.4": version "7.10.4" @@ -927,13 +1094,23 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-proposal-unicode-property-regex@^7.10.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" - integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== +"@babel/plugin-proposal-private-methods@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz#b4dafb9c717e4301c5776b30d080d6383c89aff6" + integrity sha512-IvHmcTHDFztQGnn6aWq4t12QaBXTKr1whF/dgp9kz84X6GUcwq9utj7z2wFCUfeOup/QKnOlt2k0zxkGFx9ubg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-class-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-proposal-private-property-in-object@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz#69e935b2c5c79d2488112d886f0c4e2790fee76f" + integrity sha512-3jQUr/HBbMVZmi72LpjQwlZ55i1queL8KcDTQEkAHihttJnAPrcvG9ZNXIfsd2ugpizZo595egYV6xy+pv4Ofw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-create-class-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.10.4": version "7.10.4" @@ -943,6 +1120,22 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-unicode-property-regex@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz#890482dfc5ea378e42e19a71e709728cabf18612" + integrity sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" + integrity sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -957,21 +1150,35 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.10.1", "@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-properties@^7.8.3": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz#d5bc0645913df5b17ad7eda0fa2308330bde34c5" integrity sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ== dependencies: "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-syntax-class-properties@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" - integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-dynamic-import@^7.8.0": +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== @@ -992,13 +1199,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz#0ae371134a42b91d5418feb3c8c8d43e1565d2da" - integrity sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" @@ -1006,6 +1206,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-jsx@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz#f9624394317365a9a88c82358d3f8471154698f1" + integrity sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -1027,13 +1234,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.10.1", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" - integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" @@ -1041,6 +1241,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz#25761ee7410bc8cf97327ba741ee94e4a61b7d99" + integrity sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.1" + "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" @@ -1062,12 +1269,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz#8b8733f8c57397b3eaa47ddba8841586dcaef362" - integrity sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ== +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.10.4": version "7.10.4" @@ -1076,20 +1283,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-typescript@^7.10.4", "@babel/plugin-syntax-typescript@^7.3.3": +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-arrow-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz#cb5ee3a36f0863c06ead0b409b4cc43a889b295b" - integrity sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-transform-arrow-functions@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" @@ -1097,14 +1304,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-async-to-generator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz#e5153eb1a3e028f79194ed8a7a4bf55f862b2062" - integrity sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg== +"@babel/plugin-transform-arrow-functions@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz#951706f8b449c834ed07bd474c0924c944b95a8e" + integrity sha512-vIFb5250Rbh7roWARvCLvIJ/PtAU5Lhv7BtZ1u24COwpI9Ypjsh+bZcKk6rlIyalK+r0jOc1XQ8I4ovNxNrWrA== dependencies: - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-remap-async-to-generator" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-async-to-generator@^7.10.4": version "7.10.4" @@ -1115,12 +1320,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-remap-async-to-generator" "^7.10.4" -"@babel/plugin-transform-block-scoped-functions@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz#146856e756d54b20fff14b819456b3e01820b85d" - integrity sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q== +"@babel/plugin-transform-async-to-generator@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.0.tgz#df12637f9630ddfa0ef9d7a11bc414d629d38604" + integrity sha512-PbIr7G9kR8tdH6g8Wouir5uVjklETk91GMVSUq+VaOgiinbCkBP6Q7NN/suM/QutZkMJMvcyAriogcYAdhg8Gw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-imports" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-remap-async-to-generator" "^7.16.0" "@babel/plugin-transform-block-scoped-functions@^7.10.4": version "7.10.4" @@ -1129,13 +1336,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoping@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz#47092d89ca345811451cd0dc5d91605982705d5e" - integrity sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw== +"@babel/plugin-transform-block-scoped-functions@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.0.tgz#c618763233ad02847805abcac4c345ce9de7145d" + integrity sha512-V14As3haUOP4ZWrLJ3VVx5rCnrYhMSHN/jX7z6FAt5hjRkLsb0snPCmJwSOML5oxkKO4FNoNv7V5hw/y2bjuvg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - lodash "^4.17.13" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-block-scoping@^7.10.4": version "7.11.1" @@ -1144,19 +1350,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-classes@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz#6e11dd6c4dfae70f540480a4702477ed766d733f" - integrity sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ== +"@babel/plugin-transform-block-scoping@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.0.tgz#bcf433fb482fe8c3d3b4e8a66b1c4a8e77d37c16" + integrity sha512-27n3l67/R3UrXfizlvHGuTwsRIFyce3D/6a37GRxn28iyTPvNXaW4XvznexRh1zUNLPjbLL22Id0XQElV94ruw== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-define-map" "^7.10.1" - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-optimise-call-expression" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" - "@babel/helper-split-export-declaration" "^7.10.1" - globals "^11.1.0" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-classes@^7.10.4": version "7.10.4" @@ -1172,12 +1371,18 @@ "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz#59aa399064429d64dce5cf76ef9b90b7245ebd07" - integrity sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" +"@babel/plugin-transform-classes@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.0.tgz#54cf5ff0b2242c6573d753cd4bfc7077a8b282f5" + integrity sha512-HUxMvy6GtAdd+GKBNYDWCIA776byUQH8zjnfjxwT1P1ARv/wFu8eBDpmXQcLS/IwRtrxIReGiplOwMeyO7nsDQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-optimise-call-expression" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-replace-supers" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.10.4": version "7.10.4" @@ -1186,12 +1391,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-destructuring@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz#abd58e51337815ca3a22a336b85f62b998e71907" - integrity sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA== +"@babel/plugin-transform-computed-properties@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.0.tgz#e0c385507d21e1b0b076d66bed6d5231b85110b7" + integrity sha512-63l1dRXday6S8V3WFY5mXJwcRAnPYxvFfTlt67bwV1rTyVTM5zrp0DBBb13Kl7+ehkCVwIZPumPpFP/4u70+Tw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-destructuring@^7.10.4": version "7.10.4" @@ -1200,13 +1405,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-dotall-regex@^7.10.1", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" - integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== +"@babel/plugin-transform-destructuring@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.0.tgz#ad3d7e74584ad5ea4eadb1e6642146c590dee33c" + integrity sha512-Q7tBUwjxLTsHEoqktemHBMtb3NYwyJPTJdM+wDwb0g8PZ3kQUIzNvwD5lPaqW/p54TXBc/MXZu9Jr7tbUEUM8Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-dotall-regex@^7.10.4": version "7.10.4" @@ -1216,11 +1420,20 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-duplicate-keys@^7.10.1": +"@babel/plugin-transform-dotall-regex@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.0.tgz#50bab00c1084b6162d0a58a818031cf57798e06f" + integrity sha512-FXlDZfQeLILfJlC6I1qyEwcHK5UpRCFkaoVyA1nk9A1L1Yu583YO4un2KsLBsu3IJb4CUbctZks8tD9xPQubLw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-transform-dotall-regex@^7.4.4": version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz#c900a793beb096bc9d4d0a9d0cde19518ffc83b9" - integrity sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA== + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" + integrity sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA== dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.1" "@babel/helper-plugin-utils" "^7.10.1" "@babel/plugin-transform-duplicate-keys@^7.10.4": @@ -1230,13 +1443,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-exponentiation-operator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz#279c3116756a60dd6e6f5e488ba7957db9c59eb3" - integrity sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA== +"@babel/plugin-transform-duplicate-keys@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz#8bc2e21813e3e89e5e5bf3b60aa5fc458575a176" + integrity sha512-LIe2kcHKAZOJDNxujvmp6z3mfN6V9lJxubU4fJIGoQCkKe3Ec2OcbdlYP+vW++4MpxwG0d1wSDOJtQW5kLnkZQ== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-exponentiation-operator@^7.10.4": version "7.10.4" @@ -1246,12 +1458,13 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-for-of@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz#ff01119784eb0ee32258e8646157ba2501fcfda5" - integrity sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w== +"@babel/plugin-transform-exponentiation-operator@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.0.tgz#a180cd2881e3533cef9d3901e48dad0fbeff4be4" + integrity sha512-OwYEvzFI38hXklsrbNivzpO3fh87skzx8Pnqi4LoSYeav0xHlueSoCJrSgTPfnbyzopo5b3YVAJkFIcUpK2wsw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-for-of@^7.10.4": version "7.10.4" @@ -1260,13 +1473,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-function-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz#4ed46fd6e1d8fde2a2ec7b03c66d853d2c92427d" - integrity sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw== +"@babel/plugin-transform-for-of@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz#f7abaced155260e2461359bbc7c7248aca5e6bd2" + integrity sha512-5QKUw2kO+GVmKr2wMYSATCTTnHyscl6sxFRAY+rvN7h7WB0lcG0o4NoV6ZQU32OZGVsYUsfLGgPQpDFdkfjlJQ== dependencies: - "@babel/helper-function-name" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-function-name@^7.10.4": version "7.10.4" @@ -1276,12 +1488,13 @@ "@babel/helper-function-name" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz#5794f8da82846b22e4e6631ea1658bce708eb46a" - integrity sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw== +"@babel/plugin-transform-function-name@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.0.tgz#02e3699c284c6262236599f751065c5d5f1f400e" + integrity sha512-lBzMle9jcOXtSOXUpc7tvvTpENu/NuekNJVova5lCCWCV9/U1ho2HH2y0p6mBg8fPm/syEAbfaaemYGOHCY3mg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-literals@^7.10.4": version "7.10.4" @@ -1290,12 +1503,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-member-expression-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz#90347cba31bca6f394b3f7bd95d2bbfd9fce2f39" - integrity sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA== +"@babel/plugin-transform-literals@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.0.tgz#79711e670ffceb31bd298229d50f3621f7980cac" + integrity sha512-gQDlsSF1iv9RU04clgXqRjrPyyoJMTclFt3K1cjLmTKikc0s/6vE3hlDeEVC71wLTRu72Fq7650kABrdTc2wMQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-member-expression-literals@^7.10.4": version "7.10.4" @@ -1304,14 +1517,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-modules-amd@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz#65950e8e05797ebd2fe532b96e19fc5482a1d52a" - integrity sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw== +"@babel/plugin-transform-member-expression-literals@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.0.tgz#5251b4cce01eaf8314403d21aedb269d79f5e64b" + integrity sha512-WRpw5HL4Jhnxw8QARzRvwojp9MIE7Tdk3ez6vRyUk1MwgjJN0aNpRoXainLR5SgxmoXx/vsXGZ6OthP6t/RbUg== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-modules-amd@^7.10.4": version "7.10.5" @@ -1322,14 +1533,13 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz#d5ff4b4413ed97ffded99961056e1fb980fb9301" - integrity sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg== +"@babel/plugin-transform-modules-amd@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.0.tgz#09abd41e18dcf4fd479c598c1cef7bd39eb1337e" + integrity sha512-rWFhWbCJ9Wdmzln1NmSCqn7P0RAD+ogXG/bd9Kg5c7PKWkJtkiXmYsMBeXjDlzHpVTJ4I/hnjs45zX4dEv81xw== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-simple-access" "^7.10.1" + "@babel/helper-module-transforms" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-commonjs@^7.10.4": @@ -1342,14 +1552,14 @@ "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz#9962e4b0ac6aaf2e20431ada3d8ec72082cbffb6" - integrity sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA== +"@babel/plugin-transform-modules-commonjs@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz#add58e638c8ddc4875bd9a9ecb5c594613f6c922" + integrity sha512-Dzi+NWqyEotgzk/sb7kgQPJQf7AJkQBWsVp1N6JWc1lBVo0vkElUnGdr1PzUBmfsCCN5OOFya3RtpeHk15oLKQ== dependencies: - "@babel/helper-hoist-variables" "^7.10.1" - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-module-transforms" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-simple-access" "^7.16.0" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-systemjs@^7.10.4": @@ -1362,13 +1572,16 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz#ea080911ffc6eb21840a5197a39ede4ee67b1595" - integrity sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA== +"@babel/plugin-transform-modules-systemjs@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz#a92cf240afeb605f4ca16670453024425e421ea4" + integrity sha512-yuGBaHS3lF1m/5R+6fjIke64ii5luRUg97N2wr+z1sF0V+sNSXPxXDdEEL/iYLszsN5VKxVB1IPfEqhzVpiqvg== dependencies: - "@babel/helper-module-transforms" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-hoist-variables" "^7.16.0" + "@babel/helper-module-transforms" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-identifier" "^7.15.7" + babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-umd@^7.10.4": version "7.10.4" @@ -1378,6 +1591,14 @@ "@babel/helper-module-transforms" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-modules-umd@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.0.tgz#195f26c2ad6d6a391b70880effce18ce625e06a7" + integrity sha512-nx4f6no57himWiHhxDM5pjwhae5vLpTK2zCnDH8+wNLJy0TVER/LJRHl2bkt6w9Aad2sPD5iNNoUpY3X9sTGDg== + dependencies: + "@babel/helper-module-transforms" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" @@ -1385,19 +1606,12 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.10.4" -"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" - integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.0.tgz#d3db61cc5d5b97986559967cd5ea83e5c32096ca" + integrity sha512-LogN88uO+7EhxWc8WZuQ8vxdSyVGxhkh8WTC3tzlT8LccMuQdA81e9SGV6zY7kY2LjDhhDOFdQVxdGwPyBCnvg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - -"@babel/plugin-transform-new-target@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz#6ee41a5e648da7632e22b6fb54012e87f612f324" - integrity sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-create-regexp-features-plugin" "^7.16.0" "@babel/plugin-transform-new-target@^7.10.4": version "7.10.4" @@ -1406,13 +1620,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-object-super@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz#2e3016b0adbf262983bf0d5121d676a5ed9c4fde" - integrity sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw== +"@babel/plugin-transform-new-target@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.0.tgz#af823ab576f752215a49937779a41ca65825ab35" + integrity sha512-fhjrDEYv2DBsGN/P6rlqakwRwIp7rBGLPbrKxwh7oVt5NNkIhZVOY2GRV+ULLsQri1bDqwDWnU3vhlmx5B2aCw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-replace-supers" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-object-super@^7.10.4": version "7.10.4" @@ -1422,13 +1635,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-replace-supers" "^7.10.4" -"@babel/plugin-transform-parameters@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz#b25938a3c5fae0354144a720b07b32766f683ddd" - integrity sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg== +"@babel/plugin-transform-object-super@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.0.tgz#fb20d5806dc6491a06296ac14ea8e8d6fedda72b" + integrity sha512-fds+puedQHn4cPLshoHcR1DTMN0q1V9ou0mUjm8whx9pGcNvDrVVrgw+KJzzCaiTdaYhldtrUps8DWVMgrSEyg== dependencies: - "@babel/helper-get-function-arity" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-replace-supers" "^7.16.0" "@babel/plugin-transform-parameters@^7.10.4": version "7.10.5" @@ -1438,12 +1651,12 @@ "@babel/helper-get-function-arity" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-property-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz#cffc7315219230ed81dc53e4625bf86815b6050d" - integrity sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA== +"@babel/plugin-transform-parameters@^7.16.0", "@babel/plugin-transform-parameters@^7.16.3": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.3.tgz#fa9e4c874ee5223f891ee6fa8d737f4766d31d15" + integrity sha512-3MaDpJrOXT1MZ/WCmkOFo7EtmVVC8H4EUZVrHvFOsmwkk4lOjQj8rzv8JKUZV4YoQKeoIgk07GO+acPU9IMu/w== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-property-literals@^7.10.4": version "7.10.4" @@ -1452,19 +1665,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-constant-elements@^7.7.4": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.10.1.tgz#c7f117a54657cba3f9d32012e050fc89982df9e1" - integrity sha512-V4os6bkWt/jbrzfyVcZn2ZpuHZkvj3vyBU0U/dtS8SZuMS7Rfx5oknTrtfyXJ2/QZk8gX7Yls5Z921ItNpE30Q== +"@babel/plugin-transform-property-literals@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.0.tgz#a95c552189a96a00059f6776dc4e00e3690c78d1" + integrity sha512-XLldD4V8+pOqX2hwfWhgwXzGdnDOThxaNTgqagOcpBgIxbUvpgU2FMvo5E1RyHbk756WYgdbS0T8y0Cj9FKkWQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-react-display-name@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz#e6a33f6d48dfb213dda5e007d0c7ff82b6a3d8ef" - integrity sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ== +"@babel/plugin-transform-react-constant-elements@^7.14.5": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.0.tgz#1483b894b8e6ef0709d260532fbd4db9fc27a0e6" + integrity sha512-OgtklS+p9t1X37eWA4XdvvbZG/3gqzX569gqmo3q4/Ui6qjfTQmOs5UTSrfdD9nVByHhX6Gbm/Pyc4KbwUXGWA== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-react-display-name@^7.10.4": version "7.10.4" @@ -1473,14 +1686,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-react-jsx-development@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz#1ac6300d8b28ef381ee48e6fec430cc38047b7f3" - integrity sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg== +"@babel/plugin-transform-react-display-name@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.0.tgz#9a0ad8aa8e8790883a7bd2736f66229a58125676" + integrity sha512-FJFdJAqaCpndL+pIf0aeD/qlQwT7QXOvR6Cc8JPvNhKJBi2zc/DPc4g05Y3fbD/0iWAMQFGij4+Xw+4L/BMpTg== dependencies: - "@babel/helper-builder-react-jsx-experimental" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-react-jsx-development@^7.10.4": version "7.11.5" @@ -1491,13 +1702,12 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" -"@babel/plugin-transform-react-jsx-self@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz#22143e14388d72eb88649606bb9e46f421bc3821" - integrity sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA== +"@babel/plugin-transform-react-jsx-development@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.0.tgz#1cb52874678d23ab11d0d16488d54730807303ef" + integrity sha512-qq65iSqBRq0Hr3wq57YG2AmW0H6wgTnIzpffTphrUWUgLCOK+zf1f7G0vuOiXrp7dU1qq+fQBoqZ3wCDAkhFzw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" + "@babel/plugin-transform-react-jsx" "^7.16.0" "@babel/plugin-transform-react-jsx-self@^7.10.4": version "7.10.4" @@ -1507,14 +1717,6 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" -"@babel/plugin-transform-react-jsx-source@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz#30db3d4ee3cdebbb26a82a9703673714777a4273" - integrity sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" - "@babel/plugin-transform-react-jsx-source@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz#34f1779117520a779c054f2cdd9680435b9222b4" @@ -1523,16 +1725,6 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" -"@babel/plugin-transform-react-jsx@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz#91f544248ba131486decb5d9806da6a6e19a2896" - integrity sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw== - dependencies: - "@babel/helper-builder-react-jsx" "^7.10.1" - "@babel/helper-builder-react-jsx-experimental" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-syntax-jsx" "^7.10.1" - "@babel/plugin-transform-react-jsx@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" @@ -1543,13 +1735,16 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" -"@babel/plugin-transform-react-pure-annotations@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz#f5e7c755d3e7614d4c926e144f501648a5277b70" - integrity sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA== +"@babel/plugin-transform-react-jsx@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.0.tgz#55b797d4960c3de04e07ad1c0476e2bc6a4889f1" + integrity sha512-rqDgIbukZ44pqq7NIRPGPGNklshPkvlmvqjdx3OZcGPk4zGIenYkxDTvl3LsSL8gqcc3ZzGmXPE6hR/u/voNOw== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-module-imports" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-syntax-jsx" "^7.16.0" + "@babel/types" "^7.16.0" "@babel/plugin-transform-react-pure-annotations@^7.10.4": version "7.10.4" @@ -1559,12 +1754,13 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-regenerator@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz#10e175cbe7bdb63cc9b39f9b3f823c5c7c5c5490" - integrity sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw== +"@babel/plugin-transform-react-pure-annotations@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.0.tgz#23db6ddf558d8abde41b8ad9d59f48ad5532ccab" + integrity sha512-NC/Bj2MG+t8Ef5Pdpo34Ay74X4Rt804h5y81PwOpfPtmAK3i6CizmQqwyBQzIepz1Yt8wNr2Z2L7Lu3qBMfZMA== dependencies: - regenerator-transform "^0.14.2" + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-regenerator@^7.10.4": version "7.10.4" @@ -1573,12 +1769,12 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz#0fc1027312b4d1c3276a57890c8ae3bcc0b64a86" - integrity sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ== +"@babel/plugin-transform-regenerator@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.0.tgz#eaee422c84b0232d03aea7db99c97deeaf6125a4" + integrity sha512-JAvGxgKuwS2PihiSFaDrp94XOzzTUeDeOQlcKzVAyaPap7BnZXK/lvMDiubkPTdotPKOIZq9xWXWnggUMYiExg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + regenerator-transform "^0.14.2" "@babel/plugin-transform-reserved-words@^7.10.4": version "7.10.4" @@ -1587,6 +1783,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-reserved-words@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.0.tgz#fff4b9dcb19e12619394bda172d14f2d04c0379c" + integrity sha512-Dgs8NNCehHSvXdhEhln8u/TtJxfVwGYCgP2OOr5Z3Ar+B+zXicEOKNTyc+eca2cuEOMtjW6m9P9ijOt8QdqWkg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-transform-runtime@^7.11.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.5.tgz#f108bc8e0cf33c37da031c097d1df470b3a293fc" @@ -1597,13 +1800,6 @@ resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-shorthand-properties@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz#e8b54f238a1ccbae482c4dce946180ae7b3143f3" - integrity sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-transform-shorthand-properties@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" @@ -1611,12 +1807,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-spread@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz#0c6d618a0c4461a274418460a28c9ccf5239a7c8" - integrity sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw== +"@babel/plugin-transform-shorthand-properties@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.0.tgz#090372e3141f7cc324ed70b3daf5379df2fa384d" + integrity sha512-iVb1mTcD8fuhSv3k99+5tlXu5N0v8/DPm2mO3WACLG6al1CGZH7v09HJyUb1TtYl/Z+KrM6pHSIJdZxP5A+xow== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-spread@^7.11.0": version "7.11.0" @@ -1626,13 +1822,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" -"@babel/plugin-transform-sticky-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz#90fc89b7526228bed9842cff3588270a7a393b00" - integrity sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA== +"@babel/plugin-transform-spread@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.0.tgz#d21ca099bbd53ab307a8621e019a7bd0f40cdcfb" + integrity sha512-Ao4MSYRaLAQczZVp9/7E7QHsCuK92yHRrmVNRe/SlEJjhzivq0BSn8mEraimL8wizHZ3fuaHxKH0iwzI13GyGg== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/helper-regex" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.10.4": version "7.10.4" @@ -1642,13 +1838,12 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-regex" "^7.10.4" -"@babel/plugin-transform-template-literals@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz#914c7b7f4752c570ea00553b4284dad8070e8628" - integrity sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg== +"@babel/plugin-transform-sticky-regex@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.0.tgz#c35ea31a02d86be485f6aa510184b677a91738fd" + integrity sha512-/ntT2NljR9foobKk4E/YyOSwcGUXtYWv5tinMK/3RkypyNBNdhHUaq6Orw5DWq9ZcNlS03BIlEALFeQgeVAo4Q== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-template-literals@^7.10.4": version "7.10.5" @@ -1658,12 +1853,12 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-typeof-symbol@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz#60c0239b69965d166b80a84de7315c1bc7e0bb0e" - integrity sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g== +"@babel/plugin-transform-template-literals@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.0.tgz#a8eced3a8e7b8e2d40ec4ec4548a45912630d302" + integrity sha512-Rd4Ic89hA/f7xUSJQk5PnC+4so50vBoBfxjdQAdvngwidM8jYIBVxBZ/sARxD4e0yMXRbJVDrYf7dyRtIIKT6Q== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-typeof-symbol@^7.10.4": version "7.10.4" @@ -1672,6 +1867,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typeof-symbol@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.0.tgz#8b19a244c6f8c9d668dca6a6f754ad6ead1128f2" + integrity sha512-++V2L8Bdf4vcaHi2raILnptTBjGEFxn5315YU+e8+EqXIucA+q349qWngCLpUYqqv233suJ6NOienIVUpS9cqg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/plugin-transform-typescript@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.5.tgz#edf353944e979f40d8ff9fe4e9975d0a465037c5" @@ -1681,13 +1883,6 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-typescript" "^7.10.4" -"@babel/plugin-transform-unicode-escapes@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz#add0f8483dab60570d9e03cecef6c023aa8c9940" - integrity sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -1695,13 +1890,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-unicode-regex@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz#6b58f2aea7b68df37ac5025d9c88752443a6b43f" - integrity sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw== +"@babel/plugin-transform-unicode-escapes@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz#1a354064b4c45663a32334f46fa0cf6100b5b1f3" + integrity sha512-VFi4dhgJM7Bpk8lRc5CMaRGlKZ29W9C3geZjt9beuzSUrlJxsNwX7ReLwaL6WEvsOf2EQkyIJEPtF8EXjB/g2A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-transform-unicode-regex@^7.10.4": version "7.10.4" @@ -1711,6 +1905,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-regex@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.0.tgz#293b80950177c8c85aede87cef280259fb995402" + integrity sha512-jHLK4LxhHjvCeZDWyA9c+P9XH1sOxRd1RO9xMtDVRAOND/PczPqizEtVdx4TQF/wyPaewqpT+tgQFYMnN/P94A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.0" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/preset-env@^7.11.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272" @@ -1785,75 +1987,85 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/preset-env@^7.9.5": - version "7.10.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.2.tgz#715930f2cf8573b0928005ee562bed52fb65fdfb" - integrity sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA== - dependencies: - "@babel/compat-data" "^7.10.1" - "@babel/helper-compilation-targets" "^7.10.2" - "@babel/helper-module-imports" "^7.10.1" - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-proposal-async-generator-functions" "^7.10.1" - "@babel/plugin-proposal-class-properties" "^7.10.1" - "@babel/plugin-proposal-dynamic-import" "^7.10.1" - "@babel/plugin-proposal-json-strings" "^7.10.1" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.1" - "@babel/plugin-proposal-numeric-separator" "^7.10.1" - "@babel/plugin-proposal-object-rest-spread" "^7.10.1" - "@babel/plugin-proposal-optional-catch-binding" "^7.10.1" - "@babel/plugin-proposal-optional-chaining" "^7.10.1" - "@babel/plugin-proposal-private-methods" "^7.10.1" - "@babel/plugin-proposal-unicode-property-regex" "^7.10.1" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-class-properties" "^7.10.1" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.10.1" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.10.1" - "@babel/plugin-transform-arrow-functions" "^7.10.1" - "@babel/plugin-transform-async-to-generator" "^7.10.1" - "@babel/plugin-transform-block-scoped-functions" "^7.10.1" - "@babel/plugin-transform-block-scoping" "^7.10.1" - "@babel/plugin-transform-classes" "^7.10.1" - "@babel/plugin-transform-computed-properties" "^7.10.1" - "@babel/plugin-transform-destructuring" "^7.10.1" - "@babel/plugin-transform-dotall-regex" "^7.10.1" - "@babel/plugin-transform-duplicate-keys" "^7.10.1" - "@babel/plugin-transform-exponentiation-operator" "^7.10.1" - "@babel/plugin-transform-for-of" "^7.10.1" - "@babel/plugin-transform-function-name" "^7.10.1" - "@babel/plugin-transform-literals" "^7.10.1" - "@babel/plugin-transform-member-expression-literals" "^7.10.1" - "@babel/plugin-transform-modules-amd" "^7.10.1" - "@babel/plugin-transform-modules-commonjs" "^7.10.1" - "@babel/plugin-transform-modules-systemjs" "^7.10.1" - "@babel/plugin-transform-modules-umd" "^7.10.1" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.10.1" - "@babel/plugin-transform-object-super" "^7.10.1" - "@babel/plugin-transform-parameters" "^7.10.1" - "@babel/plugin-transform-property-literals" "^7.10.1" - "@babel/plugin-transform-regenerator" "^7.10.1" - "@babel/plugin-transform-reserved-words" "^7.10.1" - "@babel/plugin-transform-shorthand-properties" "^7.10.1" - "@babel/plugin-transform-spread" "^7.10.1" - "@babel/plugin-transform-sticky-regex" "^7.10.1" - "@babel/plugin-transform-template-literals" "^7.10.1" - "@babel/plugin-transform-typeof-symbol" "^7.10.1" - "@babel/plugin-transform-unicode-escapes" "^7.10.1" - "@babel/plugin-transform-unicode-regex" "^7.10.1" - "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.10.2" - browserslist "^4.12.0" - core-js-compat "^3.6.2" - invariant "^2.2.2" - levenary "^1.1.1" - semver "^5.5.0" +"@babel/preset-env@^7.15.6": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.4.tgz#4f6ec33b2a3fe72d6bfdcdf3859500232563a2e3" + integrity sha512-v0QtNd81v/xKj4gNKeuAerQ/azeNn/G1B1qMLeXOcV8+4TWlD2j3NV1u8q29SDFBXx/NBq5kyEAO+0mpRgacjA== + dependencies: + "@babel/compat-data" "^7.16.4" + "@babel/helper-compilation-targets" "^7.16.3" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.2" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.0" + "@babel/plugin-proposal-async-generator-functions" "^7.16.4" + "@babel/plugin-proposal-class-properties" "^7.16.0" + "@babel/plugin-proposal-class-static-block" "^7.16.0" + "@babel/plugin-proposal-dynamic-import" "^7.16.0" + "@babel/plugin-proposal-export-namespace-from" "^7.16.0" + "@babel/plugin-proposal-json-strings" "^7.16.0" + "@babel/plugin-proposal-logical-assignment-operators" "^7.16.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" + "@babel/plugin-proposal-numeric-separator" "^7.16.0" + "@babel/plugin-proposal-object-rest-spread" "^7.16.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.0" + "@babel/plugin-proposal-private-methods" "^7.16.0" + "@babel/plugin-proposal-private-property-in-object" "^7.16.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.16.0" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.16.0" + "@babel/plugin-transform-async-to-generator" "^7.16.0" + "@babel/plugin-transform-block-scoped-functions" "^7.16.0" + "@babel/plugin-transform-block-scoping" "^7.16.0" + "@babel/plugin-transform-classes" "^7.16.0" + "@babel/plugin-transform-computed-properties" "^7.16.0" + "@babel/plugin-transform-destructuring" "^7.16.0" + "@babel/plugin-transform-dotall-regex" "^7.16.0" + "@babel/plugin-transform-duplicate-keys" "^7.16.0" + "@babel/plugin-transform-exponentiation-operator" "^7.16.0" + "@babel/plugin-transform-for-of" "^7.16.0" + "@babel/plugin-transform-function-name" "^7.16.0" + "@babel/plugin-transform-literals" "^7.16.0" + "@babel/plugin-transform-member-expression-literals" "^7.16.0" + "@babel/plugin-transform-modules-amd" "^7.16.0" + "@babel/plugin-transform-modules-commonjs" "^7.16.0" + "@babel/plugin-transform-modules-systemjs" "^7.16.0" + "@babel/plugin-transform-modules-umd" "^7.16.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.0" + "@babel/plugin-transform-new-target" "^7.16.0" + "@babel/plugin-transform-object-super" "^7.16.0" + "@babel/plugin-transform-parameters" "^7.16.3" + "@babel/plugin-transform-property-literals" "^7.16.0" + "@babel/plugin-transform-regenerator" "^7.16.0" + "@babel/plugin-transform-reserved-words" "^7.16.0" + "@babel/plugin-transform-shorthand-properties" "^7.16.0" + "@babel/plugin-transform-spread" "^7.16.0" + "@babel/plugin-transform-sticky-regex" "^7.16.0" + "@babel/plugin-transform-template-literals" "^7.16.0" + "@babel/plugin-transform-typeof-symbol" "^7.16.0" + "@babel/plugin-transform-unicode-escapes" "^7.16.0" + "@babel/plugin-transform-unicode-regex" "^7.16.0" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.16.0" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.4.0" + babel-plugin-polyfill-regenerator "^0.3.0" + core-js-compat "^3.19.1" + semver "^6.3.0" "@babel/preset-modules@^0.1.3": version "0.1.3" @@ -1866,6 +2078,17 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + "@babel/preset-react@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" @@ -1879,18 +2102,17 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" -"@babel/preset-react@^7.9.4": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.1.tgz#e2ab8ae9a363ec307b936589f07ed753192de041" - integrity sha512-Rw0SxQ7VKhObmFjD/cUcKhPTtzpeviEFX1E6PgP+cYOhQ98icNqtINNFANlsdbQHrmeWnqdxA4Tmnl1jy5tp3Q== +"@babel/preset-react@^7.14.5": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.0.tgz#f71d3e8dff5218478011df037fad52660ee6d82a" + integrity sha512-d31IFW2bLRB28uL1WoElyro8RH5l6531XfxMtCeCmp6RVAF1uTfxxUA0LH1tXl+psZdwfmIbwoG4U5VwgbhtLw== dependencies: - "@babel/helper-plugin-utils" "^7.10.1" - "@babel/plugin-transform-react-display-name" "^7.10.1" - "@babel/plugin-transform-react-jsx" "^7.10.1" - "@babel/plugin-transform-react-jsx-development" "^7.10.1" - "@babel/plugin-transform-react-jsx-self" "^7.10.1" - "@babel/plugin-transform-react-jsx-source" "^7.10.1" - "@babel/plugin-transform-react-pure-annotations" "^7.10.1" + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + "@babel/plugin-transform-react-display-name" "^7.16.0" + "@babel/plugin-transform-react-jsx" "^7.16.0" + "@babel/plugin-transform-react-jsx-development" "^7.16.0" + "@babel/plugin-transform-react-pure-annotations" "^7.16.0" "@babel/preset-typescript@^7.10.4": version "7.10.4" @@ -1999,6 +2221,15 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/template@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" + integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/parser" "^7.16.0" + "@babel/types" "^7.16.0" + "@babel/template@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" @@ -2068,6 +2299,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" + integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/generator" "^7.16.0" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-hoist-variables" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/parser" "^7.16.3" + "@babel/types" "^7.16.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" @@ -2092,7 +2338,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.3.3", "@babel/types@^7.7.0", "@babel/types@^7.9.5": +"@babel/types@^7.10.1", "@babel/types@^7.10.2", "@babel/types@^7.3.3", "@babel/types@^7.7.0": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.2.tgz#30283be31cad0dbf6fb00bd40641ca0ea675172d" integrity sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng== @@ -2119,6 +2365,14 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.15.6", "@babel/types@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" + integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== + dependencies: + "@babel/helper-validator-identifier" "^7.15.7" + to-fast-properties "^2.0.0" + "@babel/types@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" @@ -2349,7 +2603,7 @@ resolved "https://registry.yarnpkg.com/@jsbits/escape-regex-str/-/escape-regex-str-1.0.3.tgz#d35a2d21dfdc81a0e5ebeb68b6a16e17ca36ad20" integrity sha512-0800vYI2fg1nuUq/T9Tqv8DMOLLNiRAltxFbKIbR7szrvW6qTuI2+zGK51hV7NAAmUr4G83Kvpj2R6Yyg07iIw== -"@jsbits/get-package-version@^1.0.1", "@jsbits/get-package-version@^1.0.2": +"@jsbits/get-package-version@^1.0.2", "@jsbits/get-package-version@^1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@jsbits/get-package-version/-/get-package-version-1.0.3.tgz#a47dfd077420beee435580c3bc197931fe3c694c" integrity sha512-IJy1jRL01x7p6UEpgKa1lVLstMUx8EiIR8pPoS5sBfsHEoeLkzYiNpAfxPx8zLDUJyS1yBbChJjcWdPqyH285w== @@ -2375,15 +2629,15 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@rollup/plugin-replace@^2.3.3": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.3.3.tgz#cd6bae39444de119f5d905322b91ebd4078562e7" - integrity sha512-XPmVXZ7IlaoWaJLkSCDaa0Y6uVo5XQYHhiMFzOd5qSv5rE+t/UJToPIOE56flKIxBFQI27ONsxb7dqHnwSsjKQ== +"@rollup/plugin-replace@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-3.0.0.tgz#3a4c9665d4e7a4ce2c360cf021232784892f3fac" + integrity sha512-3c7JCbMuYXM4PbPWT4+m/4Y6U60SgsnDT/cCyAyUKwFHg7pTSfsSQzIpETha3a3ig6OdOKzZz87D9ZXIK3qsDg== dependencies: - "@rollup/pluginutils" "^3.0.8" - magic-string "^0.25.5" + "@rollup/pluginutils" "^3.1.0" + magic-string "^0.25.7" -"@rollup/pluginutils@^3.0.8": +"@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== @@ -2406,107 +2660,108 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" - integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== +"@svgr/babel-plugin-add-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" + integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== -"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" - integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== +"@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" + integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== -"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" - integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== +"@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" + integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== -"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" - integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== - -"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" - integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== - -"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" - integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== - -"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" - integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== - -"@svgr/babel-plugin-transform-svg-component@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.4.0.tgz#a2212b4d018e6075a058bb7e220a66959ef7a03c" - integrity sha512-zLl4Fl3NvKxxjWNkqEcpdSOpQ3LGVH2BNFQ6vjaK6sFo2IrSznrhURIPI0HAphKiiIwNYjAfE0TNoQDSZv0U9A== - -"@svgr/babel-preset@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.4.0.tgz#da21854643e1c4ad2279239baa7d5a8b128c1f15" - integrity sha512-Gyx7cCxua04DBtyILTYdQxeO/pwfTBev6+eXTbVbxe4HTGhOUW6yo7PSbG2p6eJMl44j6XSequ0ZDP7bl0nu9A== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" - "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" - "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" - "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" - "@svgr/babel-plugin-transform-svg-component" "^5.4.0" - -"@svgr/core@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.4.0.tgz#655378ee43679eb94fee3d4e1976e38252dff8e7" - integrity sha512-hWGm1DCCvd4IEn7VgDUHYiC597lUYhFau2lwJBYpQWDirYLkX4OsXu9IslPgJ9UpP7wsw3n2Ffv9sW7SXJVfqQ== - dependencies: - "@svgr/plugin-jsx" "^5.4.0" - camelcase "^6.0.0" - cosmiconfig "^6.0.0" +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" + integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== -"@svgr/hast-util-to-babel-ast@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.4.0.tgz#bb5d002e428f510aa5b53ec0a02377a95b367715" - integrity sha512-+U0TZZpPsP2V1WvVhqAOSTk+N+CjYHdZx+x9UBa1eeeZDXwH8pt0CrQf2+SvRl/h2CAPRFkm+Ey96+jKP8Bsgg== - dependencies: - "@babel/types" "^7.9.5" +"@svgr/babel-plugin-svg-dynamic-title@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" + integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + +"@svgr/babel-plugin-svg-em-dimensions@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" + integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" + integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + +"@svgr/babel-plugin-transform-svg-component@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.1.0.tgz#39f92954f7611c269a4ca6906d19e66cdc12babe" + integrity sha512-1zacrn08K5RyV2NtXahOZ5Im/+aB1Y0LVh6QpzwgQV05sY7H5Npq+OcW/UqXbfB2Ua/WnHsFossFQqigCjarYg== -"@svgr/plugin-jsx@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.4.0.tgz#ab47504c55615833c6db70fca2d7e489f509787c" - integrity sha512-SGzO4JZQ2HvGRKDzRga9YFSqOqaNrgLlQVaGvpZ2Iht2gwRp/tq+18Pvv9kS9ZqOMYgyix2LLxZMY1LOe9NPqw== +"@svgr/babel-preset@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.1.0.tgz#b8a6b0019537bcd75b3e23fd33c180476c1ef446" + integrity sha512-f9XrTqcwhHLVkjvXBw6QJVxuIfmW22z8iTdGqGvUGGxWoeRV2EzSHstWMBgIVd7t+TmkerqowRvBYiT0OEx3cw== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^6.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "^6.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "^6.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" + "@svgr/babel-plugin-transform-svg-component" "^6.1.0" + +"@svgr/core@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.1.1.tgz#04a7c0ce9fab2f8671ab1d99199778cc869091b6" + integrity sha512-/NP+24cQmcEdJYptoFWO34SHkNx2x4KOGAMcnTpzOPQifvb1ecupWNEPeHBDY18utd0OMpl2kWf0ZnyN5VsVlg== + dependencies: + "@svgr/plugin-jsx" "^6.1.0" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.0.0.tgz#423329ad866b6c169009cc82b5e28ffee80c857c" + integrity sha512-S+TxtCdDyRGafH1VG1t/uPZ87aOYOHzWL8kqz4FoSZcIbzWA6rnOmjNViNiDzqmEpzp2PW5o5mZfvC9DiVZhTQ== dependencies: - "@babel/core" "^7.7.5" - "@svgr/babel-preset" "^5.4.0" - "@svgr/hast-util-to-babel-ast" "^5.4.0" - svg-parser "^2.0.2" + "@babel/types" "^7.15.6" + entities "^3.0.1" -"@svgr/plugin-svgo@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.4.0.tgz#45d9800b7099a6f7b4d85ebac89ab9abe8592f64" - integrity sha512-3Cgv3aYi1l6SHyzArV9C36yo4kgwVdF3zPQUC6/aCDUeXAofDYwE5kk3e3oT5ZO2a0N3lB+lLGvipBG6lnG8EA== +"@svgr/plugin-jsx@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.1.0.tgz#2ba2d3cbb02d4f6e988fdfe5615baa04b8d0524b" + integrity sha512-grAeVnwjr4eyzzscX6d5dK202nQcKGt5STry3BrCNuw8RZkMVFYumjH/qLnInO2ugc0ESSo8eCkb+30vXVxSTg== dependencies: - cosmiconfig "^6.0.0" - merge-deep "^3.0.2" - svgo "^1.2.2" + "@babel/core" "^7.15.5" + "@svgr/babel-preset" "^6.1.0" + "@svgr/hast-util-to-babel-ast" "^6.0.0" + svg-parser "^2.0.2" -"@svgr/rollup@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/rollup/-/rollup-5.4.0.tgz#b1946c8d2c13870397af014a105d40945b7371e5" - integrity sha512-hwYjrTddW6mFU9vwqRr1TULNvxiIxGdIbqrD5J7vtoATSfWazq/2JSnT4BmiH+/4kFXLEtjKuSKoDUotkOIAkg== +"@svgr/plugin-svgo@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.1.0.tgz#30af6b64326a2c104c493de7c9b3ddda266c4e2e" + integrity sha512-NQtYlvPg6yk3wxm1EeeI3kNh/NoSxwW/Zjkvy3vf4CJJHKryB3TnkLsY8Z5hDOERT3Hadksh9RyZnSszEG7q5w== dependencies: - "@babel/core" "^7.7.5" - "@babel/plugin-transform-react-constant-elements" "^7.7.4" - "@babel/preset-env" "^7.9.5" - "@babel/preset-react" "^7.9.4" - "@svgr/core" "^5.4.0" - "@svgr/plugin-jsx" "^5.4.0" - "@svgr/plugin-svgo" "^5.4.0" + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.5.0" + +"@svgr/rollup@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@svgr/rollup/-/rollup-6.1.1.tgz#b991d26f34fb9fd6b5ce096c3baa8b90d873f8c5" + integrity sha512-8rcoAYAfIocej3nCtuZognrF914GAetvwTQ4zAQDHYyQPLQYYXYoctSKicgc4tbi2OakXZigEXNcP31LI9h5tA== + dependencies: + "@babel/core" "^7.15.5" + "@babel/plugin-transform-react-constant-elements" "^7.14.5" + "@babel/preset-env" "^7.15.6" + "@babel/preset-react" "^7.14.5" + "@svgr/core" "^6.1.1" + "@svgr/plugin-jsx" "^6.1.0" + "@svgr/plugin-svgo" "^6.1.0" rollup-pluginutils "^2.8.2" "@testing-library/dom@^7.17.1": @@ -2542,6 +2797,11 @@ "@babel/runtime" "^7.10.3" "@testing-library/dom" "^7.17.1" +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "@types/babel__core@^7.1.7": version "7.1.8" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.8.tgz#057f725aca3641f49fc11c7a87a9de5ec588a5d7" @@ -2635,10 +2895,10 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/json-schema@^7.0.3": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" - integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== +"@types/json-schema@^7.0.9": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== "@types/json5@^0.0.29": version "0.0.29" @@ -2660,6 +2920,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/prettier@^1.0.0 || ^2.0.0": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" + integrity sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA== + "@types/prettier@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.1.tgz#b6e98083f13faa1e5231bfa3bdb1b0feff536b6d" @@ -2670,11 +2935,6 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/q@^1.5.1": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" - integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== - "@types/react-dom@^16.9.8": version "16.9.14" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.14.tgz#674b8f116645fe5266b40b525777fc6bb8eb3bcd" @@ -2734,75 +2994,75 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.1.tgz#b362abe0ee478a6c6d06c14552a6497f0b480769" - integrity sha512-d7LeQ7dbUrIv5YVFNzGgaW3IQKMmnmKFneRWagRlGYOSfLJVaRbj/FrBNOBC1a3tVO+TgNq1GbHvRtg1kwL0FQ== +"@typescript-eslint/eslint-plugin@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.6.0.tgz#efd8668b3d6627c46ce722c2afe813928fe120a0" + integrity sha512-MIbeMy5qfLqtgs1hWd088k1hOuRsN9JrHUPwVVKCD99EOUqScd7SrwoZl4Gso05EAP9w1kvLWUVGJOVpRPkDPA== dependencies: - "@typescript-eslint/experimental-utils" "4.8.1" - "@typescript-eslint/scope-manager" "4.8.1" - debug "^4.1.1" + "@typescript-eslint/experimental-utils" "5.6.0" + "@typescript-eslint/scope-manager" "5.6.0" + debug "^4.3.2" functional-red-black-tree "^1.0.1" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/experimental-utils@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.8.1.tgz#27275c20fa4336df99ebcf6195f7d7aa7aa9f22d" - integrity sha512-WigyLn144R3+lGATXW4nNcDJ9JlTkG8YdBWHkDlN0lC3gUGtDi7Pe3h5GPvFKMcRz8KbZpm9FJV9NTW8CpRHpg== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.8.1" - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/typescript-estree" "4.8.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/parser@^4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.8.1.tgz#4fe2fbdbb67485bafc4320b3ae91e34efe1219d1" - integrity sha512-QND8XSVetATHK9y2Ltc/XBl5Ro7Y62YuZKnPEwnNPB8E379fDsvzJ1dMJ46fg/VOmk0hXhatc+GXs5MaXuL5Uw== - dependencies: - "@typescript-eslint/scope-manager" "4.8.1" - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/typescript-estree" "4.8.1" - debug "^4.1.1" - -"@typescript-eslint/scope-manager@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.8.1.tgz#e343c475f8f1d15801b546cb17d7f309b768fdce" - integrity sha512-r0iUOc41KFFbZdPAdCS4K1mXivnSZqXS5D9oW+iykQsRlTbQRfuFRSW20xKDdYiaCoH+SkSLeIF484g3kWzwOQ== - dependencies: - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/visitor-keys" "4.8.1" - -"@typescript-eslint/types@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.8.1.tgz#23829c73c5fc6f4fcd5346a7780b274f72fee222" - integrity sha512-ave2a18x2Y25q5K05K/U3JQIe2Av4+TNi/2YuzyaXLAsDx6UZkz1boZ7nR/N6Wwae2PpudTZmHFXqu7faXfHmA== - -"@typescript-eslint/typescript-estree@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.8.1.tgz#7307e3f2c9e95df7daa8dc0a34b8c43b7ec0dd32" - integrity sha512-bJ6Fn/6tW2g7WIkCWh3QRlaSU7CdUUK52shx36/J7T5oTQzANvi6raoTsbwGM11+7eBbeem8hCCKbyvAc0X3sQ== - dependencies: - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/visitor-keys" "4.8.1" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/visitor-keys@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.8.1.tgz#794f68ee292d1b2e3aa9690ebedfcb3a8c90e3c3" - integrity sha512-3nrwXFdEYALQh/zW8rFwP4QltqsanCDz4CwWMPiIZmwlk9GlvBeueEIbq05SEq4ganqM0g9nh02xXgv5XI3PeQ== - dependencies: - "@typescript-eslint/types" "4.8.1" - eslint-visitor-keys "^2.0.0" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.6.0.tgz#f3a5960f2004abdcac7bb81412bafc1560841c23" + integrity sha512-VDoRf3Qj7+W3sS/ZBXZh3LBzp0snDLEgvp6qj0vOAIiAPM07bd5ojQ3CTzF/QFl5AKh7Bh1ycgj6lFBJHUt/DA== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.6.0" + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/typescript-estree" "5.6.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/parser@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.6.0.tgz#11677324659641400d653253c03dcfbed468d199" + integrity sha512-YVK49NgdUPQ8SpCZaOpiq1kLkYRPMv9U5gcMrywzI8brtwZjr/tG3sZpuHyODt76W/A0SufNjYt9ZOgrC4tLIQ== + dependencies: + "@typescript-eslint/scope-manager" "5.6.0" + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/typescript-estree" "5.6.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.6.0.tgz#9dd7f007dc8f3a34cdff6f79f5eaab27ae05157e" + integrity sha512-1U1G77Hw2jsGWVsO2w6eVCbOg0HZ5WxL/cozVSTfqnL/eB9muhb8THsP0G3w+BB5xAHv9KptwdfYFAUfzcIh4A== + dependencies: + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/visitor-keys" "5.6.0" + +"@typescript-eslint/types@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.6.0.tgz#745cb1b59daadcc1f32f7be95f0f68accf38afdd" + integrity sha512-OIZffked7mXv4mXzWU5MgAEbCf9ecNJBKi+Si6/I9PpTaj+cf2x58h2oHW5/P/yTnPkKaayfjhLvx+crnl5ubA== + +"@typescript-eslint/typescript-estree@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.6.0.tgz#dfbb19c9307fdd81bd9c650c67e8397821d7faf0" + integrity sha512-92vK5tQaE81rK7fOmuWMrSQtK1IMonESR+RJR2Tlc7w4o0MeEdjgidY/uO2Gobh7z4Q1hhS94Cr7r021fMVEeA== + dependencies: + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/visitor-keys" "5.6.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/visitor-keys@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.6.0.tgz#3e36509e103fe9713d8f035ac977235fd63cb6e6" + integrity sha512-1p7hDp5cpRFUyE3+lvA74egs+RWSgumrBpzBCDzfTFv0aQ7lIeay80yU0hIxgAhwQ6PcasW35kaOCyDOv6O/Ng== + dependencies: + "@typescript-eslint/types" "5.6.0" + eslint-visitor-keys "^3.0.0" abab@^2.0.3: version "2.0.3" @@ -3078,14 +3338,6 @@ babel-jest@^26.0.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-plugin-const-enum@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-const-enum/-/babel-plugin-const-enum-1.0.1.tgz#0d742faf9731be4f213c4d01d61fc4e93c44d159" - integrity sha512-6oGu63g1FS9psUPQyLCJM08ty6kGihGKTbzWGbAKHfUuCzCh7y9twh516cR6v0lM4d4NOoR+DgLb7uKVytyp6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-typescript" "^7.3.3" - babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -3113,6 +3365,30 @@ babel-plugin-jest-hoist@^26.0.0: "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" +babel-plugin-polyfill-corejs2@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz#407082d0d355ba565af24126fb6cb8e9115251fd" + integrity sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.0" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz#0b571f4cf3d67f911512f5c04842a7b8e8263087" + integrity sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.0" + core-js-compat "^3.18.0" + +babel-plugin-polyfill-regenerator@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz#9ebbcd7186e1a33e21c5e20cae4e7983949533be" + integrity sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.0" + babel-plugin-transform-async-to-promises@^0.8.15: version "0.8.15" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-promises/-/babel-plugin-transform-async-to-promises-0.8.15.tgz#13b6d8ef13676b4e3c576d3600b85344bb1ba346" @@ -3184,7 +3460,7 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -boolbase@^1.0.0, boolbase@~1.0.0: +boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= @@ -3255,6 +3531,17 @@ browserslist@^4.12.0, browserslist@^4.8.5: node-releases "^1.1.53" pkg-up "^2.0.0" +browserslist@^4.17.5, browserslist@^4.18.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" + integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== + dependencies: + caniuse-lite "^1.0.30001280" + electron-to-chromium "^1.3.896" + escalade "^3.1.1" + node-releases "^2.0.1" + picocolors "^1.0.0" + bser@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3323,10 +3610,15 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== -caniuse-lite@^1.0.30001043: - version "1.0.30001083" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001083.tgz#52410c20c6f029f604f0d45eca0439a82e712442" - integrity sha512-CnYJ27awX4h7yj5glfK7r1TOI13LBytpLzEgfj0s4mY75/F8pnQcYjL+oVpmS38FB59+vU0gscQ9D8tc+lIXvA== +camelcase@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" + integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== + +caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001280: + version "1.0.30001286" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz" + integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ== capture-exit@^2.0.0: version "2.0.0" @@ -3340,7 +3632,7 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3440,31 +3732,20 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-deep@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" - integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: - for-own "^0.1.3" - is-plain-object "^2.0.1" - kind-of "^3.0.2" - lazy-cache "^1.0.3" - shallow-clone "^0.1.2" + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -3524,6 +3805,11 @@ commander@^5.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -3556,6 +3842,14 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +core-js-compat@^3.18.0, core-js-compat@^3.19.1: + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.3.tgz#de75e5821c5ce924a0a1e7b7d5c2cb973ff388aa" + integrity sha512-59tYzuWgEEVU9r+SRgceIGXSSUn47JknoiXW6Oq7RW8QHjXWz3/vp8pa7dbtuVu40sewz3OP3JmQEcDdztrLhA== + dependencies: + browserslist "^4.18.1" + semver "7.0.0" + core-js-compat@^3.6.2: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" @@ -3574,16 +3868,16 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" + import-fresh "^3.2.1" parse-json "^5.0.0" path-type "^4.0.0" - yaml "^1.7.2" + yaml "^1.10.0" cross-env@^7.0.2: version "7.0.2" @@ -3612,33 +3906,29 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" - integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== dependencies: boolbase "^1.0.0" - css-what "^2.1.2" - domutils "^1.7.0" - nth-check "^1.0.2" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== dependencies: - mdn-data "2.0.4" + mdn-data "2.0.14" source-map "^0.6.1" -css-what@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +css-what@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" + integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== css.escape@^1.5.1: version "1.5.1" @@ -3654,12 +3944,12 @@ css@^3.0.0: source-map "^0.6.1" source-map-resolve "^0.6.0" -csso@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d" - integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg== +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== dependencies: - css-tree "1.0.0-alpha.37" + css-tree "^1.1.2" cssom@^0.4.4: version "0.4.4" @@ -3725,6 +4015,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" +debug@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3809,10 +4106,10 @@ diff-sequences@^27.0.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== -diff@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== dir-glob@^3.0.1: version "3.0.1" @@ -3853,24 +4150,25 @@ dom-accessibility-api@^0.5.6: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.7.tgz#8c2aa6325968f2933160a0b7dbb380893ddf3e7d" integrity sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA== -dom-serializer@0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.1.tgz#13650c850daffea35d8b626a4cfc4d3a17643fdb" - integrity sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q== +dom-serializer@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== dependencies: domelementtype "^2.0.1" + domhandler "^4.2.0" entities "^2.0.0" -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - domelementtype@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== +domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -3878,13 +4176,21 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== +domhandler@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== dependencies: - dom-serializer "0" - domelementtype "1" + domelementtype "^2.2.0" + +domutils@^2.6.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" duplexer@0.1.1, duplexer@^0.1.1: version "0.1.1" @@ -3904,6 +4210,11 @@ electron-to-chromium@^1.3.413: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.473.tgz#d0cd5fe391046fb70674ec98149f0f97609d29b8" integrity sha512-smevlzzMNz3vMz6OLeeCq5HRWEj2AcgccNPYnAx4Usx0IOciq9DU36RJcICcS09hXoY7t7deRfVYKD14IrGb9A== +electron-to-chromium@^1.3.896: + version "1.4.16" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.16.tgz#38ddecc616385e6f101359d1b978c802664157d2" + integrity sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA== + emoji-regex@^7.0.1, emoji-regex@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -3926,6 +4237,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -3933,22 +4249,6 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" - integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" - object-keys "^1.1.1" - string.prototype.trimleft "^2.1.0" - string.prototype.trimright "^2.1.0" - es-abstract@^1.17.0, es-abstract@^1.17.0-next.1: version "1.17.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" @@ -3966,6 +4266,22 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-abstract@^1.5.1, es-abstract@^1.7.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" + integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.1.0" + string.prototype.trimright "^2.1.0" + es-to-primitive@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" @@ -3984,10 +4300,10 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@^1.0.5: version "1.0.5" @@ -4132,10 +4448,10 @@ eslint-plugin-promise@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== -eslint-plugin-react-hooks@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556" - integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ== +eslint-plugin-react-hooks@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" + integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== eslint-plugin-react@7.20.0: version "7.20.0" @@ -4159,7 +4475,7 @@ eslint-plugin-standard@^4.0.1: resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz#ff0519f7ffaff114f76d1bd7c3996eef0f6e20b4" integrity sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ== -eslint-scope@^5.0.0, eslint-scope@^5.1.0: +eslint-scope@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== @@ -4167,6 +4483,14 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + eslint-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" @@ -4174,6 +4498,13 @@ eslint-utils@^2.0.0: dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" @@ -4189,6 +4520,11 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== +eslint-visitor-keys@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz#eee4acea891814cda67a7d8812d9647dd0179af2" + integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA== + eslint@7.x: version "7.2.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.2.0.tgz#d41b2e47804b30dbabb093a967fb283d560082e6" @@ -4259,6 +4595,13 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -4269,10 +4612,10 @@ estraverse@^5.1.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== -estree-walker@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e" - integrity sha1-va/oCVOD2EFNXcLs9MkXO225QS4= +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== estree-walker@^0.6.1: version "0.6.1" @@ -4533,23 +4876,11 @@ follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== -for-in@^0.1.3: - version "0.1.8" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" - integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= - -for-in@^1.0.1, for-in@^1.0.2: +for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -for-own@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -4598,11 +4929,16 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2, fsevents@~2.1.2: +fsevents@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -4618,7 +4954,12 @@ gensync@^1.0.0-beta.1: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.1: +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -4710,10 +5051,10 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -globby@^11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" - integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== +globby@^11.0.4: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -4864,6 +5205,11 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.1.8: + version "5.1.9" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" + integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== + import-fresh@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" @@ -4872,10 +5218,10 @@ import-fresh@^3.0.0: parent-module "^1.0.0" resolve-from "^4.0.0" -import-fresh@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -4977,7 +5323,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -5012,6 +5358,13 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" +is-core-module@^2.2.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" + integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -5100,6 +5453,13 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -5117,7 +5477,7 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -5636,6 +5996,15 @@ jest-worker@^26.0.0: merge-stream "^2.0.0" supports-color "^7.0.0" +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + jest@^26.0.1: version "26.0.1" resolved "https://registry.yarnpkg.com/jest/-/jest-26.0.1.tgz#5c51a2e58dff7525b65f169721767173bf832694" @@ -5668,7 +6037,7 @@ jsbn@~0.1.0: resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jscc@^1.1.0: +jscc@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/jscc/-/jscc-1.1.1.tgz#fb329325773a80bcf3e3db3acec6a3f21e61ed0e" integrity sha512-anpZkTXwZbxfxLEBMciKxXMHx2xOLK2qhynIhTnoSyC+wGOEPrAoofxnADgblbarn0kijVMt1U71cQGmRF/1Og== @@ -5790,13 +6159,6 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: array-includes "^3.0.3" object.assign "^4.1.0" -kind-of@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" - integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= - dependencies: - is-buffer "^1.0.2" - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -5826,16 +6188,6 @@ kleur@^3.0.3: resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" - integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -5894,6 +6246,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + lodash.hasin@4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/lodash.hasin/-/lodash.hasin-4.5.2.tgz#f91e352378d21ef7090b9e7687c2ca35c5b4d52a" @@ -5941,7 +6298,14 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -magic-string@0.25.7, magic-string@^0.25.5: +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@0.25.7, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== @@ -6004,19 +6368,10 @@ match-sorter@^6.0.2: "@babel/runtime" "^7.12.5" remove-accents "0.4.2" -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -merge-deep@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" - integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== - dependencies: - arr-union "^3.1.0" - clone-deep "^0.2.4" - kind-of "^3.0.2" +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== merge-stream@^2.0.0: version "2.0.0" @@ -6082,7 +6437,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -6112,15 +6467,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mixin-object@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" - integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= - dependencies: - for-in "^0.1.3" - is-extendable "^0.1.1" - -mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -6132,7 +6479,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -6154,10 +6501,10 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" -nanoid@^3.0.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.3.tgz#b2bcfcfda4b4d6838bc22a0c8dd3c0a17a204c20" - integrity sha512-Zw8rTOUfh6FlKgkEbHiB1buOF2zOPOQyGirABUWn+9Z7m9PpyoLVkh6Ksc53vBjndINQ2+9LfRPaHxb/u45EGg== +nanoid@^3.1.22: + version "3.1.30" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" + integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== nanomatch@^1.2.9: version "1.2.13" @@ -6213,6 +6560,11 @@ node-releases@^1.1.53: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935" integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg== +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -6249,12 +6601,12 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== +nth-check@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" + integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== dependencies: - boolbase "~1.0.0" + boolbase "^1.0.0" nwsapi@^2.2.0: version "2.2.0" @@ -6347,16 +6699,6 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.12.0" - function-bind "^1.1.1" - has "^1.0.3" - object.values@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" @@ -6381,10 +6723,10 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" - integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== dependencies: is-docker "^2.0.0" is-wsl "^2.1.1" @@ -6557,6 +6899,11 @@ performance-now@^2.1.0: resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1, picomatch@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" @@ -6712,18 +7059,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -pupa@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" - integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== - dependencies: - escape-goat "^2.0.0" - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - qs@~6.5.2: version "6.5.2" resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -6850,11 +7185,23 @@ regenerate-unicode-properties@^8.2.0: dependencies: regenerate "^1.4.0" +regenerate-unicode-properties@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326" + integrity sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA== + dependencies: + regenerate "^1.4.2" + regenerate@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f" integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A== +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" @@ -6889,6 +7236,11 @@ regexpp@^3.0.0, regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + regexpu-core@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" @@ -6901,7 +7253,19 @@ regexpu-core@^4.7.0: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" -regjsgen@^0.5.1: +regexpu-core@^4.7.1: + version "4.8.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.8.0.tgz#e5605ba361b67b1718478501327502f4479a98f0" + integrity sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^9.0.0" + regjsgen "^0.5.2" + regjsparser "^0.7.0" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +regjsgen@^0.5.1, regjsgen@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== @@ -6913,6 +7277,13 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" +regjsparser@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.7.0.tgz#a6b667b54c885e18b52554cb4960ef71187e9968" + integrity sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ== + dependencies: + jsesc "~0.5.0" + remove-accents@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" @@ -7030,6 +7401,14 @@ resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0, resolve@^1.8 dependencies: path-parse "^1.0.6" +resolve@^1.14.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -7081,14 +7460,14 @@ rollup-plugin-commonjs@^10.1.0: resolve "^1.11.0" rollup-pluginutils "^2.8.1" -rollup-plugin-jscc@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-jscc/-/rollup-plugin-jscc-1.0.0.tgz#675fa51c65efcaaeaa28feb03cfac6497360229e" - integrity sha512-A35Se384xsBfm7eoMoayAfx6PNY7EHo0CT6SzcUCtQRizOuzBP0SEQ6HqbiVgdAtOBr7q6sBOKicCsDVvetcRA== +rollup-plugin-jscc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-jscc/-/rollup-plugin-jscc-2.0.0.tgz#1ad2f74ab3248dcaaa5ebff0b838418e22243ce6" + integrity sha512-5jG9q79K2u5uRBTKA+GA4gqt1zA7qHQRpcabZMoVs913gr75s428O7K3r58n2vADDzwIhiOKMo7rCMhOyks6dw== dependencies: - "@jsbits/get-package-version" "^1.0.1" - jscc "^1.1.0" - rollup-pluginutils "^1.5.2" + "@jsbits/get-package-version" "^1.0.3" + jscc "^1.1.1" + rollup-pluginutils "^2.8.2" rollup-plugin-node-resolve@^5.2.0: version "5.2.0" @@ -7101,17 +7480,18 @@ rollup-plugin-node-resolve@^5.2.0: resolve "^1.11.1" rollup-pluginutils "^2.8.1" -rollup-plugin-peer-deps-external@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.2.tgz#506cef67fb68f41cca3ec08ca6ff7936b190f568" - integrity sha512-XcHH4UW9exRTAf73d8rk2dw2UgF//cWbilhRI4Ni/n+t0zA1eBtohKyJROn0fxa4/+WZ5R3onAyIDiwRQL+59A== +rollup-plugin-peer-deps-external@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz#8a420bbfd6dccc30aeb68c9bf57011f2f109570d" + integrity sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g== -rollup-plugin-prettier@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-prettier/-/rollup-plugin-prettier-2.1.0.tgz#06b42169e28cb23163d4648bef412e57ff70acb4" - integrity sha512-2uPhHkBMVBZeb4cruQrYrjOTtoqHNvnUB5UIzmMu8JBXCfQ+0/JxDclEmnBd44MjO14VmXvfr0GanQaZjCxSbQ== +rollup-plugin-prettier@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-prettier/-/rollup-plugin-prettier-2.2.0.tgz#5690e2fd4311bd33f8ab5760de5d5792daf9089b" + integrity sha512-elavFmdKju3KqoZ49XBDxcXdyANxAQf7fRvvRTVpeG17NUEp+OZxCMVyLWnEpz9ua1wxawR66moYo33yhsyDaw== dependencies: - diff "4.0.2" + "@types/prettier" "^1.0.0 || ^2.0.0" + diff "5.0.0" lodash.hasin "4.5.2" lodash.isempty "4.4.0" lodash.isnil "4.0.0" @@ -7125,34 +7505,25 @@ rollup-plugin-size@^0.2.2: dependencies: size-plugin-core "^0.0.7" -rollup-plugin-terser@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz#071866585aea104bfbb9dd1019ac523e63c81e45" - integrity sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw== +rollup-plugin-terser@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== dependencies: - "@babel/code-frame" "^7.8.3" - jest-worker "^26.0.0" - serialize-javascript "^3.0.0" - terser "^4.7.0" + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" -rollup-plugin-visualizer@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-4.0.4.tgz#69b9140c6faf46328997ed2d08b974252bf9d683" - integrity sha512-odkyLiVxCEXh4AWFSl75+pbIapzhEZkOVww8pKUgraOHicSH67MYMnAOHWQVK/BYeD1cCiF/0kk8/XNX2+LM9A== +rollup-plugin-visualizer@^5.5.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.5.2.tgz#ae2130ee5ae4a2d901e764e492b71357cb95eed7" + integrity sha512-sh+P9KhuWTzeStyRA5yNZpoEFGuj5Ph34JLMa9+muhU8CneFf9L0XE4fmAwAojJLWp//uLUEyytBPSCdZEg5AA== dependencies: - nanoid "^3.0.1" - open "^7.0.3" - pupa "^2.0.0" + nanoid "^3.1.22" + open "^7.4.2" source-map "^0.7.3" - yargs "^15.0.0" - -rollup-pluginutils@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408" - integrity sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg= - dependencies: - estree-walker "^0.2.1" - minimatch "^3.0.2" + yargs "^16.2.0" rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: version "2.8.2" @@ -7161,12 +7532,12 @@ rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.16.1.tgz#97805e88071e2c6727bd0b64904976d14495c873" - integrity sha512-UYupMcbFtoWLB6ZtL4hPZNUTlkXjJfGT33Mmhz3hYLNmRj/cOvX2B26ZxDQuEpwtLdcyyyraBGQ7EfzmMJnXXg== +rollup@^2.61.1: + version "2.61.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.61.1.tgz#1a5491f84543cf9e4caf6c61222d9a3f8f2ba454" + integrity sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA== optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.2" rsvp@^4.8.4: version "4.8.5" @@ -7232,11 +7603,6 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - saxes@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -7262,7 +7628,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -7272,10 +7638,17 @@ semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -serialize-javascript@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" - integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== +semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== dependencies: randombytes "^2.1.0" @@ -7294,16 +7667,6 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" - integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= - dependencies: - is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" - mixin-object "^2.0.1" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -7460,10 +7823,10 @@ source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -7483,7 +7846,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: +source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -7726,24 +8089,18 @@ svg-parser@^2.0.2: resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== -svgo@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" +svgo@^2.5.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" symbol-tree@^3.2.4: version "3.2.4" @@ -7768,14 +8125,14 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" -terser@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006" - integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw== +terser@^5.0.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" + integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== dependencies: commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map "~0.7.2" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -7894,10 +8251,10 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -7959,16 +8316,21 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9" - integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ== +typescript@4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" + integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + unicode-match-property-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" @@ -7977,16 +8339,34 @@ unicode-match-property-ecmascript@^1.0.4: unicode-canonical-property-names-ecmascript "^1.0.4" unicode-property-aliases-ecmascript "^1.0.4" +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + unicode-property-aliases-ecmascript@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -8010,11 +8390,6 @@ unload@2.2.0: "@babel/runtime" "^7.6.2" detect-node "^2.0.4" -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -8050,7 +8425,7 @@ util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@^1.0.0, util.promisify@~1.0.0: +util.promisify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== @@ -8184,6 +8559,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -8233,10 +8617,20 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yaml@^1.7.2: - version "1.10.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@^18.1.1: version "18.1.3" @@ -8246,7 +8640,12 @@ yargs-parser@^18.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^15.0.0, yargs@^15.3.1: +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@^15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== @@ -8262,3 +8661,16 @@ yargs@^15.0.0, yargs@^15.3.1: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^18.1.1" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" From c50b63014bd201ec8f7da410ca9c600337cba0ae Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 11 Dec 2021 19:59:08 +0100 Subject: [PATCH 08/76] fix(queryObserver): defer tracking of error prop when useErrorBoundary is on (#3087) adding "error" to the list of tracked properties will result in us _only_ tracking error if we want to track all properties implicitly by _not_ observing any properties (because we check for trackedProps.size). Moving the adding of "error" to _after_ the size check fixes this --- src/core/queryObserver.ts | 19 ++++++------------- src/reactjs/tests/useQuery.test.tsx | 24 ++++++++++++++++++++++++ src/reactjs/useBaseQuery.ts | 2 +- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 526bb07593..df0dd565bf 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -1,4 +1,4 @@ -import { DefaultedQueryObserverOptions, RefetchPageFilters } from './types' +import { RefetchPageFilters } from './types' import { isServer, isValidTimeout, @@ -224,14 +224,7 @@ export class QueryObserver< } trackResult( - result: QueryObserverResult, - defaultedOptions: DefaultedQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > + result: QueryObserverResult ): QueryObserverResult { const trackedResult = {} as QueryObserverResult @@ -246,10 +239,6 @@ export class QueryObserver< }) }) - if (defaultedOptions.useErrorBoundary) { - this.trackedProps.add('error') - } - return trackedResult } @@ -606,6 +595,10 @@ export class QueryObserver< const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps) + if (this.options.useErrorBoundary) { + includedProps.add('error') + } + return Object.keys(result).some(key => { const typedKey = key as keyof QueryObserverResult const changed = result[typedKey] !== prevResult[typedKey] diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index b8b04fbc13..29cc7666f3 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -2672,6 +2672,30 @@ describe('useQuery', () => { consoleMock.mockRestore() }) + it('should update with data if we observe no properties and useErrorBoundary', async () => { + const key = queryKey() + + let result: UseQueryResult | undefined + + function Page() { + const query = useQuery(key, () => Promise.resolve('data'), { + useErrorBoundary: true, + }) + + React.useEffect(() => { + result = query + }) + + return null + } + + renderWithClient(queryClient, ) + + await sleep(10) + + expect(result?.data).toBe('data') + }) + it('should set status to error instead of throwing when error should not be thrown', async () => { const key = queryKey() const consoleMock = mockConsoleError() diff --git a/src/reactjs/useBaseQuery.ts b/src/reactjs/useBaseQuery.ts index a5f4d5f5dd..3a99bd1938 100644 --- a/src/reactjs/useBaseQuery.ts +++ b/src/reactjs/useBaseQuery.ts @@ -134,7 +134,7 @@ export function useBaseQuery< // Handle result property usage tracking if (!defaultedOptions.notifyOnChangeProps) { - result = observer.trackResult(result, defaultedOptions) + result = observer.trackResult(result) } return result From 84b06ec1942a5c0170b006b5c6befa5fd9ff8a14 Mon Sep 17 00:00:00 2001 From: Anthony Fertil Date: Wed, 15 Dec 2021 12:13:17 +0100 Subject: [PATCH 09/76] docs: Update migrating-to-react-query-4.md (#3096) --- docs/src/pages/guides/migrating-to-react-query-4.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 630d4b225f..6d1535a19e 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -39,15 +39,15 @@ In v4, `notifyOnChangeProps` defaults to the `"tracked"` behavior of v3 instead ### Consistent behavior for `cancelRefetch` -The `cancelRefetch` can be passed to all functions that imperatively fetch a query, namely: +The `cancelRefetch` option can be passed to all functions that imperatively fetch a query, namely: - `queryClient.refetchQueries` - - `queryClient.invalidateQueries` - - `queryClient.resetQueries` +- `queryClient.invalidateQueries` +- `queryClient.resetQueries` - `refetch` returned from `useQuery` -- `fetchNetPage` and `fetchPreviousPage` returned from `useInfiniteQuery` +- `fetchNextPage` and `fetchPreviousPage` returned from `useInfiniteQuery` -Except for `fetchNetxPage` and `fetchPreviousPage`, this flag was defaulting to `false`, which was inconsistent and potentially troublesome: Calling `refetchQueries` or `invalidateQueries` after a mutation might not yield the latest result if a previous slow fetch was already ongoing, because this refetch would have been skipped. +Except for `fetchNextPage` and `fetchPreviousPage`, this flag was defaulting to `false`, which was inconsistent and potentially troublesome: Calling `refetchQueries` or `invalidateQueries` after a mutation might not yield the latest result if a previous slow fetch was already ongoing, because this refetch would have been skipped. We believe that if a query is actively refetched by some code you write, it should, per default, re-start the fetch. From 9a20452f7139de90a5d94021cfe57dbd87cc9256 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Thu, 16 Dec 2021 20:19:42 +0100 Subject: [PATCH 10/76] refactor: remove logging in prod mode (#3103) to not log to the console per default; to access process.ENV, we need to enable node typings, which screws with setTimeout / setInterval, as typings are different for node and the browser; I tried to keep the changes to type-level only --- .../pages/guides/migrating-to-react-query-4.md | 5 +++++ src/core/mutation.ts | 5 +++-- src/core/query.ts | 15 +++++++++++++-- src/core/queryObserver.ts | 16 ++++++++++------ src/core/removable.ts | 4 ++-- src/core/tests/queriesObserver.test.tsx | 13 +++++++++++++ src/core/tests/utils.test.tsx | 2 +- src/createAsyncStoragePersister/index.ts | 2 +- src/createWebStoragePersister/index.ts | 2 +- tsconfig.json | 2 +- 10 files changed, 50 insertions(+), 16 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 6d1535a19e..8bffe466f3 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -247,6 +247,11 @@ The methods `cancelMutatations` and `executeMutation` were undocumented and unus Types now require using TypeScript v4.1 or greater +### Logging + +Starting with v4, react-query will no longer log errors (e.g. failed fetches) to the console in production mode, as this was confusing to many. +Errors will still show up in development mode. + ## New Features 🚀 ### Proper offline support diff --git a/src/core/mutation.ts b/src/core/mutation.ts index 04533c11ea..cc0f1e80d5 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -235,8 +235,9 @@ export class Mutation< this as Mutation ) - // Log error - getLogger().error(error) + if (process.env.NODE_ENV !== 'production') { + getLogger().error(error) + } return Promise.resolve() .then(() => diff --git a/src/core/query.ts b/src/core/query.ts index 07e171ecc7..f82b0281f6 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -367,6 +367,16 @@ export class Query< } } + if ( + process.env.NODE_ENV !== 'production' && + !Array.isArray(this.options.queryKey) + ) { + getLogger().error( + 'As of v4, queryKey needs to be an Array, but the queryKey used was:', + JSON.stringify(this.options.queryKey) + ) + } + const abortController = getAbortController() // Create query function context @@ -459,8 +469,9 @@ export class Query< // Notify cache callback this.cache.config.onError?.(error, this as Query) - // Log error - getLogger().error(error) + if (process.env.NODE_ENV !== 'production') { + getLogger().error(error) + } } if (!this.isFetchingOptimistic) { diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 062499912d..c2a249a9aa 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -70,8 +70,8 @@ export class QueryObserver< private previousQueryResult?: QueryObserverResult private previousSelectError: TError | null private previousSelectFn?: (data: TQueryData) => TData - private staleTimeoutId?: number - private refetchIntervalId?: number + private staleTimeoutId?: ReturnType + private refetchIntervalId?: ReturnType private currentRefetchInterval?: number | false private trackedProps!: Set @@ -398,12 +398,12 @@ export class QueryObserver< } private clearStaleTimeout(): void { - clearTimeout(this.staleTimeoutId) + clearTimeout(this.staleTimeoutId!) this.staleTimeoutId = undefined } private clearRefetchInterval(): void { - clearInterval(this.refetchIntervalId) + clearInterval(this.refetchIntervalId!) this.refetchIntervalId = undefined } @@ -486,7 +486,9 @@ export class QueryObserver< } this.previousSelectError = null } catch (selectError) { - getLogger().error(selectError) + if (process.env.NODE_ENV !== 'production') { + getLogger().error(selectError) + } error = selectError as TError this.previousSelectError = selectError as TError errorUpdatedAt = Date.now() @@ -529,7 +531,9 @@ export class QueryObserver< } this.previousSelectError = null } catch (selectError) { - getLogger().error(selectError) + if (process.env.NODE_ENV !== 'production') { + getLogger().error(selectError) + } error = selectError as TError this.previousSelectError = selectError as TError errorUpdatedAt = Date.now() diff --git a/src/core/removable.ts b/src/core/removable.ts index c33f08202c..eea3e682bc 100644 --- a/src/core/removable.ts +++ b/src/core/removable.ts @@ -2,7 +2,7 @@ import { isValidTimeout } from './utils' export abstract class Removable { cacheTime!: number - private gcTimeout?: number + private gcTimeout?: ReturnType destroy(): void { this.clearGcTimeout() @@ -27,7 +27,7 @@ export abstract class Removable { } protected clearGcTimeout() { - clearTimeout(this.gcTimeout) + clearTimeout(this.gcTimeout!) this.gcTimeout = undefined } diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx index 7bf5eef376..27c1d0fdfd 100644 --- a/src/core/tests/queriesObserver.test.tsx +++ b/src/core/tests/queriesObserver.test.tsx @@ -5,6 +5,8 @@ import { QueriesObserver, QueryObserverResult, QueryObserver, + Logger, + setLogger, } from '../..' import { QueryKey } from '..' @@ -39,6 +41,14 @@ describe('queriesObserver', () => { }) test('should still return value for undefined query key', async () => { + const logger: Logger = { + error: jest.fn(), + log: jest.fn(), + warn: jest.fn(), + } + + setLogger(logger) + const key1 = queryKey() const queryFn1 = jest.fn().mockReturnValue(1) const queryFn2 = jest.fn().mockReturnValue(2) @@ -53,6 +63,9 @@ describe('queriesObserver', () => { await sleep(1) unsubscribe() expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }]) + + expect(logger.error).toHaveBeenCalledTimes(1) + setLogger(console) }) test('should update when a query updates', async () => { diff --git a/src/core/tests/utils.test.tsx b/src/core/tests/utils.test.tsx index 2297c2a6b7..af8b618a82 100644 --- a/src/core/tests/utils.test.tsx +++ b/src/core/tests/utils.test.tsx @@ -379,7 +379,7 @@ describe('core/utils', () => { // Do no throw an uncaught exception that cannot be tested with // this jest version } - return 0 + return 0 as any }) scheduleMicrotask(callback) jest.runAllTimers() diff --git a/src/createAsyncStoragePersister/index.ts b/src/createAsyncStoragePersister/index.ts index 96731076c6..9a1b7b6677 100644 --- a/src/createAsyncStoragePersister/index.ts +++ b/src/createAsyncStoragePersister/index.ts @@ -58,7 +58,7 @@ function asyncThrottle( if (typeof func !== 'function') throw new Error('argument is not function.') const running = { current: false } let lastTime = 0 - let timeout: number + let timeout: ReturnType const queue: Array = [] return (...args: Args) => (async () => { diff --git a/src/createWebStoragePersister/index.ts b/src/createWebStoragePersister/index.ts index e2f61c1d65..a6493b707f 100644 --- a/src/createWebStoragePersister/index.ts +++ b/src/createWebStoragePersister/index.ts @@ -96,7 +96,7 @@ function throttle( func: (...args: TArgs) => any, wait = 100 ) { - let timer: number | null = null + let timer: ReturnType | null = null let params: TArgs return function (...args: TArgs) { params = args diff --git a/tsconfig.json b/tsconfig.json index c88aaec9bd..34a78d8b10 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,7 @@ "noUncheckedIndexedAccess": true, "skipLibCheck": true, "strict": true, - "types": ["jest"], + "types": ["jest", "node"], "paths": { "react-query": ["./src/index.ts"] } From e21ef6cb5bde82b3f0fccc253ff24cd5ded47b6a Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Tue, 28 Dec 2021 22:37:26 +0100 Subject: [PATCH 11/76] refactor: remove cancel method as it no longer exists (#3142) * refactor: remove cancel method as it no longer exists it wasn't doing anything in that test * refactor: remove cancel method as it no longer exists use signal in playground example instead of cancel fn --- examples/playground/src/index.js | 17 ++++++++++------- src/reactjs/tests/useQuery.test.tsx | 14 +++----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/examples/playground/src/index.js b/examples/playground/src/index.js index 475e1ca0c7..886d24c182 100644 --- a/examples/playground/src/index.js +++ b/examples/playground/src/index.js @@ -181,7 +181,7 @@ function Todos({ initialFilter = "", setEditingIndex }) { const { status, data, isFetching, error, failureCount, refetch } = useQuery( ["todos", { filter }], - () => fetchTodos({ filter }) + fetchTodos ); return ( @@ -370,9 +370,16 @@ function AddTodo() { ); } -function fetchTodos({ filter } = {}) { +function fetchTodos({ signal, queryKey: [, { filter }] }) { console.info("fetchTodos", { filter }); - const promise = new Promise((resolve, reject) => { + + if (signal) { + signal.addEventListener("abort", () => { + console.info("cancelled", filter); + }); + } + + return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < errorRate) { return reject( @@ -382,10 +389,6 @@ function fetchTodos({ filter } = {}) { resolve(list.filter((d) => d.name.includes(filter))); }, queryTimeMin + Math.random() * (queryTimeMax - queryTimeMin)); }); - - promise.cancel = () => console.info("cancelled", filter); - - return promise; } function fetchTodoById({ id }) { diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index 44d0ac4caf..c0803b4b97 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -4268,17 +4268,9 @@ describe('useQuery', () => { const key = queryKey() const states: UseQueryResult[] = [] - const queryFn = () => { - let cancelFn = jest.fn() - - const promise = new Promise((resolve, reject) => { - cancelFn = jest.fn(() => reject('Cancelled')) - sleep(50).then(() => resolve('OK')) - }) - - ;(promise as any).cancel = cancelFn - - return promise + const queryFn = async () => { + await sleep(50) + return 'OK' } function Page() { From 047d6cfd3fb2f2d98c46d20118c80024c38b033d Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Fri, 31 Dec 2021 09:12:13 +0100 Subject: [PATCH 12/76] refactor: remove unused query.setDefaultOptions --- .../pages/guides/migrating-to-react-query-4.md | 6 ++++-- src/core/query.ts | 6 ------ src/core/tests/query.test.tsx | 15 --------------- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 8bffe466f3..a2ac3d02ef 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -217,9 +217,9 @@ The `useQueries` hook now accepts an object with a `queries` prop as its input. ``` -### Removed undocumented methods from the `queryClient` +### Removed undocumented methods from the `queryClient` and `query` -The methods `cancelMutatations` and `executeMutation` were undocumented and unused internally, so we removed them. Since they were just wrappers around methods available on the `mutationCache`, you can still use the functionality. +The methods `cancelMutatations` and `executeMutation` on the `QueryClient` were undocumented and unused internally, so we removed them. Since they were just wrappers around methods available on the `mutationCache`, you can still use the functionality. ```diff - cancelMutations(): Promise { @@ -243,6 +243,8 @@ The methods `cancelMutatations` and `executeMutation` were undocumented and unus - } ``` +Additionally, `query.setDefaultOptions` was removed because it was also unused. + ### TypeScript Types now require using TypeScript v4.1 or greater diff --git a/src/core/query.ts b/src/core/query.ts index 2627c43184..195cfa5569 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -188,12 +188,6 @@ export class Query< this.updateCacheTime(this.options.cacheTime) } - setDefaultOptions( - options: QueryOptions - ): void { - this.defaultOptions = options - } - protected optionalRemove() { if (!this.observers.length && this.state.fetchStatus === 'idle') { this.cache.remove(this) diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index 243225ae18..cc120b933d 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -634,21 +634,6 @@ describe('query', () => { ) }) - test('should set default options', async () => { - const key = queryKey() - - await queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key)! - - query.setDefaultOptions({ retryDelay: 20 }) - - await queryClient.prefetchQuery(key, () => 'data', { - cacheTime: 100, - }) - - expect(query.options).toMatchObject({ cacheTime: 100, retryDelay: 20 }) - }) - test('should refetch the observer when online method is called', async () => { const key = queryKey() From 1abb3714637c3f9044a4294cc737cc41966a2f49 Mon Sep 17 00:00:00 2001 From: Jonathan Stanley Date: Fri, 14 Jan 2022 03:08:46 -0500 Subject: [PATCH 13/76] feat(persistQueryClient): improve persist controls (#3141) * feat(persistQueryClient): improve persist controls add restore/save/subscribe * docs: update persistQueryClient and hydration * docs: describe new persist features * docs(persistQueryClient): correct option defaults * feat(persistQueryClient): enable unsubscribe * docs(persistQueryClient): clarify restoration * docs(persistQueryClient): enable unsubscribe note * fix(persistQueryClient): subscribe awaits restore --- docs/src/pages/plugins/persistQueryClient.md | 94 +++++++++++++++----- docs/src/pages/reference/hydration.md | 10 ++- src/persistQueryClient/index.ts | 93 ++++++++++++++----- 3 files changed, 153 insertions(+), 44 deletions(-) diff --git a/docs/src/pages/plugins/persistQueryClient.md b/docs/src/pages/plugins/persistQueryClient.md index 969ea98804..75aaac23af 100644 --- a/docs/src/pages/plugins/persistQueryClient.md +++ b/docs/src/pages/plugins/persistQueryClient.md @@ -30,7 +30,9 @@ const queryClient = new QueryClient({ }, }) -const localStoragePersister = createWebStoragePersister({storage: window.localStorage}) +const localStoragePersister = createWebStoragePersister({ + storage: window.localStorage, +}) persistQueryClient({ queryClient, @@ -48,16 +50,15 @@ You can also pass it `Infinity` to disable garbage collection behavior entirely. ## How does it work? -As you use your application: +- A check for window `undefined` is performed prior to saving/restoring/removing your data (avoids build errors). -- When your query/mutation cache is updated, it will be dehydrated and stored by the persister you provided. **By default**, this action is throttled to happen at most every 1 second to save on potentially expensive writes to a persister, but can be customized as you see fit. +### Storing -When you reload/bootstrap your app: +As you use your application: -- Attempts to load a previously persisted dehydrated query/mutation cache from the persister -- If a cache is found that is older than the `maxAge` (which by default is 24 hours), it will be discarded. This can be customized as you see fit. +- When your query/mutation cache is updated, it will be [`dehydrated`](../reference/hydration#dehydrate) and stored by the persister you provided. The officially supported persisters throttle this action to happen at most every 1 second to save on potentially expensive writes, but can be customized as you see fit. -## Cache Busting +#### Cache Busting Sometimes you may make changes to your application or data that immediately invalidate any and all cached data. If and when this happens, you can pass a `buster` string option to `persistQueryClient`, and if the cache that is found does not also have that buster string, it will be discarded. @@ -65,14 +66,75 @@ Sometimes you may make changes to your application or data that immediately inva persistQueryClient({ queryClient, persister, buster: buildHash }) ``` +### Restoring + +When you reload/bootstrap your app: + +- Attempts to [`hydrate`](../reference/hydration#hydrate) a previously persisted dehydrated query/mutation cache from the persister back into the query cache of the passed query client. +- If a cache is found that is older than the `maxAge` (which by default is 24 hours), it will be discarded. This can be customized as you see fit. + +### Removal + +- If data is found to be expired (see `maxAge`), busted (see `buster`), error (ex: `throws ...`), or empty (ex: `undefined`), the persister `removeClient()` is called and the cache is immediately discarded. + ## API +### `persistQueryClientRestore` + +This will attempt to restore a persister's stored cached to the query cache of the passed queryClient. + +```ts +persistQueryClientRestore({ + queryClient, + persister, + maxAge = 1000 * 60 * 60 * 24, // 24 hours + buster = '', + hydrateOptions = undefined, +}) +``` + +### `persistQueryClientSave` + +This will attempt to save the current query cache with the persister. You can use this to explicitly persist the cache at the moments you choose. + +```ts +persistQueryClientSave({ + queryClient, + persister, + buster = '', + dehydrateOptions = undefined, +}) +``` + +### `persistQueryClientSubscribe` + +This will subscribe to query cache updates which will run `persistQueryClientSave`. For example: you might initiate the `subscribe` when a user logs-in and checks "Remember me". + +- It returns an `unsubscribe` function which you can use to discontinue the monitor; ending the updates to the persisted cache. +- If you want to erase the persisted cache after the `unsubscribe`, you can send a new `buster` to `persistQueryClientRestore` which will trigger the persister's `removeClient` function and discard the persisted cache. + +```ts +persistQueryClientSubscribe({ + queryClient, + persister, + buster = '', + dehydrateOptions = undefined, +}) +``` + ### `persistQueryClient` -Pass this function a `QueryClient` instance and a persister that will persist your cache. Both are **required** +This will automatically restore any persisted cache and subscribes to the query cache to persist any changes from the query cache to the persister. It returns an `unsubscribe` function which you can use to discontinue the monitor; ending the updates to the persisted cache. ```ts -persistQueryClient({ queryClient, persister }) +persistQueryClient({ + queryClient, + persister, + maxAge = 1000 * 60 * 60 * 24, // 24 hours + buster = '', + hydrateOptions = undefined, + dehydrateOptions = undefined, +}) ``` ### `Options` @@ -86,9 +148,10 @@ interface PersistQueryClientOptions { /** The Persister interface for storing and restoring the cache * to/from a persisted location */ persister: Persister - /** The max-allowed age of the cache. + /** The max-allowed age of the cache in milliseconds. * If a persisted cache is found that is older than this - * time, it will be discarded */ + * time, it will be **silently** discarded + * (defaults to 24 hours) */ maxAge?: number /** A unique string that can be used to forcefully * invalidate existing caches if they do not share the same buster string */ @@ -100,15 +163,6 @@ interface PersistQueryClientOptions { } ``` -The default options are: - -```ts -{ - maxAge = 1000 * 60 * 60 * 24, // 24 hours - buster = '', -} -``` - ## Building a Persister Persisters have the following interface: diff --git a/docs/src/pages/reference/hydration.md b/docs/src/pages/reference/hydration.md index f1694fb634..a815432756 100644 --- a/docs/src/pages/reference/hydration.md +++ b/docs/src/pages/reference/hydration.md @@ -48,7 +48,7 @@ const dehydratedState = dehydrate(queryClient, { ### limitations -The hydration API requires values to be JSON serializable. If you need to dehydrate values that are not automatically serializable to JSON (like `Error` or `undefined`), you have to serialize them for yourself. Since only successful queries are included per default, to also include `Errors`, you have to provide `shouldDehydrateQuery`, e.g.: +Some storage systems (such as browser [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)) require values to be JSON serializable. If you need to dehydrate values that are not automatically serializable to JSON (like `Error` or `undefined`), you have to serialize them for yourself. Since only successful queries are included per default, to also include `Errors`, you have to provide `shouldDehydrateQuery`, e.g.: ```js // server @@ -56,13 +56,13 @@ const state = dehydrate(client, { shouldDehydrateQuery: () => true }) // to also const serializedState = mySerialize(state) // transform Error instances to objects // client -const state = myDeserialize(serializedState) // transform objects back to Error instances +const state = myDeserialize(serializedState) // transform objects back to Error instances hydrate(client, state) ``` ## `hydrate` -`hydrate` adds a previously dehydrated state into a `cache`. If the queries included in dehydration already exist in the queryCache, `hydrate` does not overwrite them. +`hydrate` adds a previously dehydrated state into a `cache`. ```js import { hydrate } from 'react-query' @@ -85,6 +85,10 @@ hydrate(queryClient, dehydratedState, options) - `mutations: MutationOptions` The default mutation options to use for the hydrated mutations. - `queries: QueryOptions` The default query options to use for the hydrated queries. +### Limitations + +If the queries included in dehydration already exist in the queryCache, `hydrate` does not overwrite them and they will be **silently** discarded. + ## `useHydrate` `useHydrate` adds a previously dehydrated state into the `queryClient` that would be returned by `useQueryClient()`. If the client already contains data, the new queries will be intelligently merged based on update timestamp. diff --git a/src/persistQueryClient/index.ts b/src/persistQueryClient/index.ts index 8380228d50..d38ed924b7 100644 --- a/src/persistQueryClient/index.ts +++ b/src/persistQueryClient/index.ts @@ -21,46 +21,52 @@ export interface PersistedClient { clientState: DehydratedState } -export interface PersistQueryClientOptions { +export interface PersistQueryClienRootOptions { /** The QueryClient to persist */ queryClient: QueryClient /** The Persister interface for storing and restoring the cache * to/from a persisted location */ persister: Persister - /** The max-allowed age of the cache. - * If a persisted cache is found that is older than this - * time, it will be discarded */ - maxAge?: number /** A unique string that can be used to forcefully * invalidate existing caches if they do not share the same buster string */ buster?: string +} + +export interface PersistedQueryClientRestoreOptions + extends PersistQueryClienRootOptions { + /** The max-allowed age of the cache in milliseconds. + * If a persisted cache is found that is older than this + * time, it will be discarded */ + maxAge?: number /** The options passed to the hydrate function */ hydrateOptions?: HydrateOptions +} + +export interface PersistedQueryClientSaveOptions + extends PersistQueryClienRootOptions { /** The options passed to the dehydrate function */ dehydrateOptions?: DehydrateOptions } -export async function persistQueryClient({ +export interface PersistQueryClientOptions + extends PersistedQueryClientRestoreOptions, + PersistedQueryClientSaveOptions, + PersistQueryClienRootOptions {} + +/** + * Restores persisted data to the QueryCache + * - data obtained from persister.restoreClient + * - data is hydrated using hydrateOptions + * If data is expired, busted, empty, or throws, it runs persister.removeClient + */ +export async function persistQueryClientRestore({ queryClient, persister, maxAge = 1000 * 60 * 60 * 24, buster = '', hydrateOptions, - dehydrateOptions, -}: PersistQueryClientOptions) { +}: PersistedQueryClientRestoreOptions) { if (typeof window !== 'undefined') { - // Subscribe to changes - const saveClient = () => { - const persistClient: PersistedClient = { - buster, - timestamp: Date.now(), - clientState: dehydrate(queryClient, dehydrateOptions), - } - - persister.persistClient(persistClient) - } - - // Attempt restore try { const persistedClient = await persister.restoreClient() @@ -84,8 +90,53 @@ export async function persistQueryClient({ ) persister.removeClient() } + } +} + +/** + * Persists data from the QueryCache + * - data dehydrated using dehydrateOptions + * - data is persisted using persister.persistClient + */ +export async function persistQueryClientSave({ + queryClient, + persister, + buster = '', + dehydrateOptions, +}: PersistedQueryClientSaveOptions) { + if (typeof window !== 'undefined') { + const persistClient: PersistedClient = { + buster, + timestamp: Date.now(), + clientState: dehydrate(queryClient, dehydrateOptions), + } + + await persister.persistClient(persistClient) + } +} + +/** + * Subscribe to QueryCache updates (for persisting) + * @returns an unsubscribe function (to discontinue monitoring) + */ +export function persistQueryClientSubscribe( + props: PersistedQueryClientSaveOptions +) { + return props.queryClient.getQueryCache().subscribe(() => { + persistQueryClientSave(props) + }) +} + +/** + * Restores persisted data to QueryCache and persists further changes. + * (Retained for backwards compatibility) + */ +export async function persistQueryClient(props: PersistQueryClientOptions) { + if (typeof window !== 'undefined') { + // Attempt restore + await persistQueryClientRestore(props) // Subscribe to changes in the query cache to trigger the save - queryClient.getQueryCache().subscribe(saveClient) + return persistQueryClientSubscribe(props) } } From 4afd0c319aead10d4a93791947ae2479ebcb8832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduard=20Bardaj=C3=AD=20Puig?= Date: Sat, 22 Jan 2022 21:16:45 +0200 Subject: [PATCH 14/76] docs: clarify caching behavior (#3221) The example contains at least one inacurate statement, > It will then cache the data using `'todos'` and `fetchTodos` as the unique identifiers for that cache. and could benefit from more precise language. --- docs/src/pages/guides/caching.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/src/pages/guides/caching.md b/docs/src/pages/guides/caching.md index e200b53721..1b31bbc494 100644 --- a/docs/src/pages/guides/caching.md +++ b/docs/src/pages/guides/caching.md @@ -17,16 +17,17 @@ This caching example illustrates the story and lifecycle of: Let's assume we are using the default `cacheTime` of **5 minutes** and the default `staleTime` of `0`. - A new instance of `useQuery(['todos'], fetchTodos)` mounts. - - Since no other queries have been made with this query + variable combination, this query will show a hard loading state and make a network request to fetch the data. - - It will then cache the data using `['todos']` as the unique identifiers for that cache. - - The hook will mark itself as stale after the configured `staleTime` (defaults to `0`, or immediately). + - Since no other queries have been made with the `['todos']` query key, this query will show a hard loading state and make a network request to fetch the data. + - When the network request has completed, the returned data will be cached under the `['todos']` key. + - The hook will mark the data as stale after the configured `staleTime` (defaults to `0`, or immediately). - A second instance of `useQuery(['todos'], fetchTodos)` mounts elsewhere. - - Because this exact data exists in the cache from the first instance of this query, that data is immediately returned from the cache. -- A background refetch is triggered for both queries (but only one request), since a new instance appeared on screen. - - Both instances are updated with the new data if the fetch is successful + - Since the cache already has data for the `['todos']` key from the first query, that data is immediately returned from the cache. + - The new instance triggers a new network request using its query function. + - Note that regardless of whether both `fetchTodos` query functions are identical or not, both queries' [`status`](../reference/useQuery) are updated (including `isFetching`, `isLoading`, and other related values) because they have the same query key. + - When the request completes successfully, the cache's data under the `['todos']` key is updated with the new data, and both instances are updated with the new data. - Both instances of the `useQuery(['todos'], fetchTodos)` query are unmounted and no longer in use. - Since there are no more active instances of this query, a cache timeout is set using `cacheTime` to delete and garbage collect the query (defaults to **5 minutes**). -- Before the cache timeout has completed another instance of `useQuery(['todos'], fetchTodos)` mounts. The query immediately returns the available cached value while the `fetchTodos` function is being run in the background to populate the query with a fresh value. +- Before the cache timeout has completed, another instance of `useQuery(['todos'], fetchTodos)` mounts. The query immediately returns the available cached data while the `fetchTodos` function is being run in the background. When it completes successfully, it will populate the cache with fresh data. - The final instance of `useQuery(['todos'], fetchTodos)` unmounts. - No more instances of `useQuery(['todos'], fetchTodos)` appear within **5 minutes**. - - This query and its data are deleted and garbage collected. + - The cached data under the `['todos']` key is deleted and garbage collected. From c0fc916cb3e7d32a05e9356b2f1359c3cd5de573 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sun, 23 Jan 2022 17:11:01 +0100 Subject: [PATCH 15/76] fix(core): do not refetch disabled queries (#3223) * fix(core): do not refetch disabled queries with refetchQueries or invalidateQueries + refetchType "inactive" disabled queries (=queries that have observers which are all enabled:false) are matched as "inactive"; this is okay when searching for them via findAll or for removeQueries, but the docs clearly state that refetchQueries / invalidateQueries do not refetch disabled queries, and that the only way to refetch them is via refetch returned from useQuery; this is important when using enabled to signal that some dependencies are not yet ready some tests needed to be adapted because we used disabled observer + refetchQueries a lot. The easiest way to emulate the observers we wanted here was mostly with initialData + staleTime, and to get a real inactive query, we just need to subscribe + unsubscribe immediately * fix(core): do not refetch disabled queries add tests for refetchQueries + disabled * fix(core): do not refetch disabled queries update test to make more sense - title said disabled queries, but we had no disabled query; test now does the opposite of what it did before, but that's what this PR does :) --- src/core/query.ts | 4 ++ src/core/queryClient.ts | 17 +++--- src/core/tests/queryClient.test.tsx | 92 +++++++++++++++++++---------- src/devtools/devtools.tsx | 4 +- src/reactjs/tests/useQuery.test.tsx | 23 +++----- 5 files changed, 83 insertions(+), 57 deletions(-) diff --git a/src/core/query.ts b/src/core/query.ts index 195cfa5569..06935088dc 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -250,6 +250,10 @@ export class Query< return this.observers.some(observer => observer.options.enabled !== false) } + isDisabled(): boolean { + return this.getObserversCount() > 0 && !this.isActive() + } + isStale(): boolean { return ( this.state.isInvalidated || diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index d5f7b2662e..7e2fbc02ab 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -283,13 +283,16 @@ export class QueryClient { const [filters, options] = parseFilterArgs(arg1, arg2, arg3) const promises = notifyManager.batch(() => - this.queryCache.findAll(filters).map(query => - query.fetch(undefined, { - ...options, - cancelRefetch: options?.cancelRefetch ?? true, - meta: { refetchPage: filters?.refetchPage }, - }) - ) + this.queryCache + .findAll(filters) + .filter(query => !query.isDisabled()) + .map(query => + query.fetch(undefined, { + ...options, + cancelRefetch: options?.cancelRefetch ?? true, + meta: { refetchPage: filters?.refetchPage }, + }) + ) ) let promise = Promise.all(promises).then(noop) diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index b72f64ded9..0331415032 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -707,6 +707,41 @@ describe('queryClient', () => { }) describe('refetchQueries', () => { + test('should not refetch if all observers are disabled', async () => { + const key = queryKey() + const queryFn = jest.fn() + await queryClient.fetchQuery(key, queryFn) + const observer1 = new QueryObserver(queryClient, { + queryKey: key, + queryFn, + enabled: false, + }) + observer1.subscribe(() => undefined) + await queryClient.refetchQueries() + observer1.destroy() + expect(queryFn).toHaveBeenCalledTimes(1) + }) + test('should refetch if at least one observer is enabled', async () => { + const key = queryKey() + const queryFn = jest.fn() + await queryClient.fetchQuery(key, queryFn) + const observer1 = new QueryObserver(queryClient, { + queryKey: key, + queryFn, + enabled: false, + }) + const observer2 = new QueryObserver(queryClient, { + queryKey: key, + queryFn, + refetchOnMount: false, + }) + observer1.subscribe(() => undefined) + observer2.subscribe(() => undefined) + await queryClient.refetchQueries() + observer1.destroy() + observer2.destroy() + expect(queryFn).toHaveBeenCalledTimes(2) + }) test('should refetch all queries when no arguments are given', async () => { const key1 = queryKey() const key2 = queryKey() @@ -716,11 +751,13 @@ describe('queryClient', () => { await queryClient.fetchQuery(key2, queryFn2) const observer1 = new QueryObserver(queryClient, { queryKey: key1, - enabled: false, + staleTime: Infinity, + initialData: 'initial', }) const observer2 = new QueryObserver(queryClient, { queryKey: key1, - enabled: false, + staleTime: Infinity, + initialData: 'initial', }) observer1.subscribe(() => undefined) observer2.subscribe(() => undefined) @@ -964,13 +1001,14 @@ describe('queryClient', () => { queryKey: key1, queryFn: queryFn1, staleTime: Infinity, - enabled: false, + refetchOnMount: false, }) const unsubscribe = observer.subscribe(() => undefined) - queryClient.invalidateQueries(key1, { + unsubscribe() + + await queryClient.invalidateQueries(key1, { refetchType: 'inactive', }) - unsubscribe() expect(queryFn1).toHaveBeenCalledTimes(2) expect(queryFn2).toHaveBeenCalledTimes(1) }) @@ -1002,23 +1040,19 @@ describe('queryClient', () => { let fetchCount = 0 const observer = new QueryObserver(queryClient, { queryKey: key, - enabled: false, + queryFn: ({ signal }) => { + return new Promise(resolve => { + fetchCount++ + setTimeout(() => resolve(5), 10) + if (signal) { + signal.addEventListener('abort', abortFn) + } + }) + }, initialData: 1, }) observer.subscribe(() => undefined) - queryClient.fetchQuery(key, ({ signal }) => { - const promise = new Promise(resolve => { - fetchCount++ - setTimeout(() => resolve(5), 10) - if (signal) { - signal.addEventListener('abort', abortFn) - } - }) - - return promise - }) - await queryClient.refetchQueries() observer.destroy() if (typeof AbortSignal === 'function') { @@ -1033,23 +1067,19 @@ describe('queryClient', () => { let fetchCount = 0 const observer = new QueryObserver(queryClient, { queryKey: key, - enabled: false, + queryFn: ({ signal }) => { + return new Promise(resolve => { + fetchCount++ + setTimeout(() => resolve(5), 10) + if (signal) { + signal.addEventListener('abort', abortFn) + } + }) + }, initialData: 1, }) observer.subscribe(() => undefined) - queryClient.fetchQuery(key, ({ signal }) => { - const promise = new Promise(resolve => { - fetchCount++ - setTimeout(() => resolve(5), 10) - if (signal) { - signal.addEventListener('abort', abortFn) - } - }) - - return promise - }) - await queryClient.refetchQueries(undefined, { cancelRefetch: false }) observer.destroy() if (typeof AbortSignal === 'function') { diff --git a/src/devtools/devtools.tsx b/src/devtools/devtools.tsx index 7476205a77..808d590426 100644 --- a/src/devtools/devtools.tsx +++ b/src/devtools/devtools.tsx @@ -703,8 +703,6 @@ export const ReactQueryDevtoolsPanel = React.forwardRef< }} > {queries.map((query, i) => { - const isDisabled = - query.getObserversCount() > 0 && !query.isActive() return (
{query.getObserversCount()}
- {isDisabled ? ( + {query.isDisabled() ? (
{ await sleep(1) return 'fetched' }, - { enabled: false } + { + initialData: 'initial', + staleTime: Infinity, + } ) results.push(result) @@ -1266,7 +1269,7 @@ describe('useQuery', () => { }) }) - it('should update disabled query when updated with invalidateQueries', async () => { + it('should not update disabled query when refetched with refetchQueries', async () => { const key = queryKey() const states: UseQueryResult[] = [] let count = 0 @@ -1295,27 +1298,15 @@ describe('useQuery', () => { renderWithClient(queryClient, ) - await sleep(100) + await sleep(50) - expect(states.length).toBe(3) + expect(states.length).toBe(1) expect(states[0]).toMatchObject({ data: undefined, isFetching: false, isSuccess: false, isStale: true, }) - expect(states[1]).toMatchObject({ - data: undefined, - isFetching: true, - isSuccess: false, - isStale: true, - }) - expect(states[2]).toMatchObject({ - data: 1, - isFetching: false, - isSuccess: true, - isStale: true, - }) }) it('should not refetch disabled query when invalidated with invalidateQueries', async () => { From 52ad9ccb12bee8f5cafe2d6a02df96f2fb36af5b Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sun, 6 Feb 2022 11:14:55 +0100 Subject: [PATCH 16/76] refactor(mutation): remove mutation.cancel (#3225) as it wasn't really aborting the request - there is no AbortSignal for Mutations atm. --- .../guides/migrating-to-react-query-4.md | 15 +++-------- src/core/mutation.ts | 9 ------- src/core/mutationCache.ts | 1 - src/core/tests/mutations.test.tsx | 27 ------------------- 4 files changed, 3 insertions(+), 49 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index a2ac3d02ef..4856dbbeb6 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -217,18 +217,9 @@ The `useQueries` hook now accepts an object with a `queries` prop as its input. ``` -### Removed undocumented methods from the `queryClient` and `query` +### Removed undocumented methods from the `queryClient`, `query` and `mutation` -The methods `cancelMutatations` and `executeMutation` on the `QueryClient` were undocumented and unused internally, so we removed them. Since they were just wrappers around methods available on the `mutationCache`, you can still use the functionality. - -```diff -- cancelMutations(): Promise { -- const promises = notifyManager.batch(() => -- this.mutationCache.getAll().map(mutation => mutation.cancel()) -- ) -- return Promise.all(promises).then(noop).catch(noop) -- } -``` +The methods `cancelMutatations` and `executeMutation` on the `QueryClient` were undocumented and unused internally, so we removed them. Since it was just a wrapper around a method available on the `mutationCache`, you can still use the functionality of `executeMutation` ```diff - executeMutation< @@ -243,7 +234,7 @@ The methods `cancelMutatations` and `executeMutation` on the `QueryClient` were - } ``` -Additionally, `query.setDefaultOptions` was removed because it was also unused. +Additionally, `query.setDefaultOptions` was removed because it was also unused. `mutation.cancel` was removed because it didn't actually cancel the outgoing request. ### TypeScript diff --git a/src/core/mutation.ts b/src/core/mutation.ts index cc0f1e80d5..6d303b91bc 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -5,7 +5,6 @@ import { getLogger } from './logger' import { notifyManager } from './notifyManager' import { Removable } from './removable' import { canFetch, Retryer, createRetryer } from './retryer' -import { noop } from './utils' // TYPES @@ -150,14 +149,6 @@ export class Mutation< } } - cancel(): Promise { - if (this.retryer) { - this.retryer.cancel() - return this.retryer.promise.then(noop).catch(noop) - } - return Promise.resolve() - } - continue(): Promise { if (this.retryer) { this.retryer.continue() diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts index be44e44b7b..a101013d3f 100644 --- a/src/core/mutationCache.ts +++ b/src/core/mutationCache.ts @@ -106,7 +106,6 @@ export class MutationCache extends Subscribable { remove(mutation: Mutation): void { this.mutations = this.mutations.filter(x => x !== mutation) - mutation.cancel() this.notify({ type: 'removed', mutation }) } diff --git a/src/core/tests/mutations.test.tsx b/src/core/tests/mutations.test.tsx index 14260c38db..8f624ad64e 100644 --- a/src/core/tests/mutations.test.tsx +++ b/src/core/tests/mutations.test.tsx @@ -369,33 +369,6 @@ describe('mutations', () => { consoleMock.mockRestore() }) - test('cancel mutation should not call mutationFn if the current retrier is undefined', async () => { - const mutationFn = jest.fn().mockImplementation(async () => { - await sleep(20) - return 'data' - }) - - const observer = new MutationObserver(queryClient, { - mutationKey: ['key'], - mutationFn, - }) - - observer.mutate() - const mutation = queryClient - .getMutationCache() - .find({ mutationKey: ['key'] })! - await sleep(10) - - // Force current mutation retryer to be undefined - // because not use case has been found - mutation['retryer'] = undefined - mutationFn.mockReset() - await mutation.cancel() - - await sleep(30) - expect(mutationFn).toHaveBeenCalledTimes(0) - }) - test('reducer should return the state for an unknown action type', async () => { const observer = new MutationObserver(queryClient, { mutationKey: ['key'], From e90e7f84e999645db44c430cfd7af4347f737dd1 Mon Sep 17 00:00:00 2001 From: GLabat Date: Sun, 6 Feb 2022 18:38:21 +0100 Subject: [PATCH 17/76] refactor(queryClient): add dev warning with queryDefaults (#3249) * refactor(QueryClient): add dev warning Warn when several query defaults match a given key. Could be error prone if the returned defaults are not the expected ones. The order of registration does matter. * test(QueryClient): warning with defaults options Highlight how query defaults registration order matters. * doc(QueryClient): add notes about query defaults In `getQueryDefaults`, the **first** matching default is returned. In `setQueryDefaults`, highlight how the registration order is important. * doc(QueryClient): fix link to documentation * test(QueryClient): better test * refactor(QueryClient): use internal logger * doc(QueryClient): fix markup * doc(QueryClient): remove extra entry * refacto(QueryClient): warn about several query defaults Warning must be displayed any time a conflict is detected, not just for dev build. The warning is aimed at helping developers *using* react-query, not those *developping* react-query. * Update src/core/queryClient.ts Remove useless optional chaining. Co-authored-by: Dominik Dorfmeister * feat(utils): add assert helper * refactor(QueryClient): add dev warning for mutation defaults * Revert "feat(utils): add assert helper" This reverts commit 05c3fe19cd3277bd99740564c2e771412b7cf109. * refactor(QueryClient): error when several defaults Review how the check for multiple defaults on a key is raised. Ensure it remains fast in release build. * refactor(QueryClient): inline code Co-authored-by: Guillaume Labat Co-authored-by: Dominik Dorfmeister --- docs/src/pages/reference/QueryClient.md | 8 ++ src/core/queryClient.ts | 62 ++++++++++-- src/core/tests/queryClient.test.tsx | 126 ++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 9 deletions(-) diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index b195e050d0..7aad8ff00a 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -474,6 +474,9 @@ The `getQueryDefaults` method returns the default options which have been set fo const defaultOptions = queryClient.getQueryDefaults(['posts']) ``` +> Note that if several query defaults match the given query key, the **first** matching one is returned. +> This could lead to unexpected behaviours. See [`setQueryDefaults`](#queryclientsetquerydefaults). + ## `queryClient.setQueryDefaults` `setQueryDefaults` can be used to set default options for specific queries: @@ -491,6 +494,9 @@ function Component() { - `queryKey: QueryKey`: [Query Keys](../guides/query-keys) - `options: QueryOptions` +> As stated in [`getQueryDefaults`](#queryclientgetquerydefaults), the order of registration of query defaults does matter. +> Since the **first** matching defaults are returned by `getQueryDefaults`, the registration should be made in the following order: from the **least generic key** to the **most generic one**. This way, in case of specific key, the first matching one would be the expected one. + ## `queryClient.getMutationDefaults` The `getMutationDefaults` method returns the default options which have been set for specific mutations: @@ -516,6 +522,8 @@ function Component() { - `mutationKey: string | unknown[]` - `options: MutationOptions` +> Similar to [`setQueryDefaults`](#queryclientsetquerydefaults), the order of registration does matter here. + ## `queryClient.getQueryCache` The `getQueryCache` method returns the query cache this client is connected to. diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index 7e2fbc02ab..d09461ef83 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -38,6 +38,7 @@ import { onlineManager } from './onlineManager' import { notifyManager } from './notifyManager' import { infiniteQueryBehavior } from './infiniteQueryBehavior' import { CancelOptions, DefaultedQueryObserverOptions } from './types' +import { getLogger } from './logger' // TYPES @@ -535,10 +536,32 @@ export class QueryClient { getQueryDefaults( queryKey?: QueryKey ): QueryObserverOptions | undefined { - return queryKey - ? this.queryDefaults.find(x => partialMatchKey(queryKey, x.queryKey)) - ?.defaultOptions - : undefined + if (!queryKey) { + return undefined + } + + // Get the first matching defaults + const firstMatchingDefaults = this.queryDefaults.find(x => + partialMatchKey(queryKey, x.queryKey) + ) + + // Additional checks and error in dev mode + if (process.env.NODE_ENV !== 'production') { + // Retrieve all matching defaults for the given key + const matchingDefaults = this.queryDefaults.filter(x => + partialMatchKey(queryKey, x.queryKey) + ) + // It is ok not having defaults, but it is error prone to have more than 1 default for a given key + if (matchingDefaults.length > 1) { + getLogger().error( + `[QueryClient] Several query defaults match with key '${JSON.stringify( + queryKey + )}'. The first matching query defaults are used. Please check how query defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetquerydefaults.` + ) + } + } + + return firstMatchingDefaults?.defaultOptions } setMutationDefaults( @@ -558,11 +581,32 @@ export class QueryClient { getMutationDefaults( mutationKey?: MutationKey ): MutationObserverOptions | undefined { - return mutationKey - ? this.mutationDefaults.find(x => - partialMatchKey(mutationKey, x.mutationKey) - )?.defaultOptions - : undefined + if (!mutationKey) { + return undefined + } + + // Get the first matching defaults + const firstMatchingDefaults = this.mutationDefaults.find(x => + partialMatchKey(mutationKey, x.mutationKey) + ) + + // Additional checks and error in dev mode + if (process.env.NODE_ENV !== 'production') { + // Retrieve all matching defaults for the given key + const matchingDefaults = this.mutationDefaults.filter(x => + partialMatchKey(mutationKey, x.mutationKey) + ) + // It is ok not having defaults, but it is error prone to have more than 1 default for a given key + if (matchingDefaults.length > 1) { + getLogger().error( + `[QueryClient] Several mutation defaults match with key '${JSON.stringify( + mutationKey + )}'. The first matching mutation defaults are used. Please check how mutation defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetmutationdefaults.` + ) + } + } + + return firstMatchingDefaults?.defaultOptions } defaultQueryOptions< diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index 0331415032..c32cf97cc0 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -130,6 +130,132 @@ describe('queryClient', () => { queryClient.setQueryDefaults(key, queryOptions2) expect(queryClient.getQueryDefaults(key)).toMatchObject(queryOptions2) }) + + test('should warn in dev if several query defaults match a given key', () => { + // Check discussion here: https://github.com/tannerlinsley/react-query/discussions/3199 + const consoleErrorMock = jest.spyOn(console, 'error') + consoleErrorMock.mockImplementation(() => true) + + const keyABCD = [ + { + a: 'a', + b: 'b', + c: 'c', + d: 'd', + }, + ] + + // The key below "contains" keyABCD => it is more generic + const keyABC = [ + { + a: 'a', + b: 'b', + c: 'c', + }, + ] + + // The defaults for query matching key "ABCD" (least generic) + const defaultsOfABCD = { + queryFn: function ABCDQueryFn() { + return 'ABCD' + }, + } + + // The defaults for query matching key "ABC" (most generic) + const defaultsOfABC = { + queryFn: function ABCQueryFn() { + return 'ABC' + }, + } + + // No defaults, no warning + const noDefaults = queryClient.getQueryDefaults(keyABCD) + expect(noDefaults).toBeUndefined() + expect(consoleErrorMock).not.toHaveBeenCalled() + + // If defaults for key ABCD are registered **before** the ones of key ABC (more generic)… + queryClient.setQueryDefaults(keyABCD, defaultsOfABCD) + queryClient.setQueryDefaults(keyABC, defaultsOfABC) + // … then the "good" defaults are retrieved: we get the ones for key "ABCD" + const goodDefaults = queryClient.getQueryDefaults(keyABCD) + expect(goodDefaults).toBe(defaultsOfABCD) + // The warning is still raised since several defaults are matching + expect(consoleErrorMock).toHaveBeenCalledTimes(1) + + // Let's create another queryClient and change the order of registration + const newQueryClient = new QueryClient() + // The defaults for key ABC (more generic) are registered **before** the ones of key ABCD… + newQueryClient.setQueryDefaults(keyABC, defaultsOfABC) + newQueryClient.setQueryDefaults(keyABCD, defaultsOfABCD) + // … then the "wrong" defaults are retrieved: we get the ones for key "ABC" + const badDefaults = newQueryClient.getQueryDefaults(keyABCD) + expect(badDefaults).not.toBe(defaultsOfABCD) + expect(badDefaults).toBe(defaultsOfABC) + expect(consoleErrorMock).toHaveBeenCalledTimes(2) + + consoleErrorMock.mockRestore() + }) + + test('should warn in dev if several mutation defaults match a given key', () => { + // Check discussion here: https://github.com/tannerlinsley/react-query/discussions/3199 + const consoleErrorMock = jest.spyOn(console, 'error') + consoleErrorMock.mockImplementation(() => true) + + const keyABCD = [ + { + a: 'a', + b: 'b', + c: 'c', + d: 'd', + }, + ] + + // The key below "contains" keyABCD => it is more generic + const keyABC = [ + { + a: 'a', + b: 'b', + c: 'c', + }, + ] + + // The defaults for mutation matching key "ABCD" (least generic) + const defaultsOfABCD = { + mutationFn: Promise.resolve, + } + + // The defaults for mutation matching key "ABC" (most generic) + const defaultsOfABC = { + mutationFn: Promise.resolve, + } + + // No defaults, no warning + const noDefaults = queryClient.getMutationDefaults(keyABCD) + expect(noDefaults).toBeUndefined() + expect(consoleErrorMock).not.toHaveBeenCalled() + + // If defaults for key ABCD are registered **before** the ones of key ABC (more generic)… + queryClient.setMutationDefaults(keyABCD, defaultsOfABCD) + queryClient.setMutationDefaults(keyABC, defaultsOfABC) + // … then the "good" defaults are retrieved: we get the ones for key "ABCD" + const goodDefaults = queryClient.getMutationDefaults(keyABCD) + expect(goodDefaults).toBe(defaultsOfABCD) + // The warning is still raised since several defaults are matching + expect(consoleErrorMock).toHaveBeenCalledTimes(1) + + // Let's create another queryClient and change the order of registration + const newQueryClient = new QueryClient() + // The defaults for key ABC (more generic) are registered **before** the ones of key ABCD… + newQueryClient.setMutationDefaults(keyABC, defaultsOfABC) + newQueryClient.setMutationDefaults(keyABCD, defaultsOfABCD) + // … then the "wrong" defaults are retrieved: we get the ones for key "ABC" + const badDefaults = newQueryClient.getMutationDefaults(keyABCD) + expect(badDefaults).not.toBe(defaultsOfABCD) + expect(badDefaults).toBe(defaultsOfABC) + expect(consoleErrorMock).toHaveBeenCalledTimes(2) + + consoleErrorMock.mockRestore() + }) }) describe('setQueryData', () => { From 2b5c33752645c3f3c842491684cdd43b1e654838 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sun, 6 Feb 2022 18:49:05 +0100 Subject: [PATCH 18/76] chore: fix silent merge conflicts --- src/reactjs/tests/useQueries.test.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index f014ca6425..1ca4f48c3b 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -22,7 +22,7 @@ import { UseQueryOptions, QueryKey, } from '../..' -import { EnsuredQueryKey, QueryFunctionContext } from '../../core' +import { QueryFunctionContext } from '../../core' describe('useQueries', () => { const queryCache = new QueryCache() @@ -924,8 +924,8 @@ describe('useQueries', () => { TData, TQueryKey extends QueryKey >(queries: UseQueryOptions[]) { - return useQueries( - queries.map( + return useQueries({ + queries: queries.map( // no need to type the mapped query query => { const { queryFn: fn, queryKey: key, onError: err } = query @@ -934,13 +934,13 @@ describe('useQueries', () => { queryKey: key, onError: err, queryFn: (ctx: QueryFunctionContext) => { - expectType>(ctx.queryKey) + expectType(ctx.queryKey) return fn?.call({}, ctx) }, } } - ) - ) + ), + }) } // @ts-expect-error (Page component is not rendered) From 7ee784f08a670d367de1fa5938d16c816f1f2f7e Mon Sep 17 00:00:00 2001 From: phatmann Date: Wed, 9 Feb 2022 11:09:15 -0800 Subject: [PATCH 19/76] feat: Bail out if query data undefined (#3271) * Bail out if query data undefined * Fix failing test * docs: migration guide for undefined data * docs: update setQueryData reference * Update docs/src/pages/guides/migrating-to-react-query-4.md Co-authored-by: Louis Law Co-authored-by: Dominik Dorfmeister --- .../guides/migrating-to-react-query-4.md | 26 ++++++ docs/src/pages/reference/QueryClient.md | 6 +- docs/src/pages/reference/useQuery.md | 4 +- src/core/query.ts | 62 ++++++------- src/core/queryClient.ts | 17 +++- src/core/tests/query.test.tsx | 27 ++++++ src/core/tests/queryClient.test.tsx | 86 ++++++++++++------- src/core/tests/queryObserver.test.tsx | 4 +- src/core/types.ts | 6 +- src/reactjs/tests/ssr.test.tsx | 5 +- src/reactjs/tests/suspense.test.tsx | 29 +++++-- src/reactjs/tests/useQueries.test.tsx | 10 ++- src/reactjs/tests/useQuery.test.tsx | 4 +- 13 files changed, 204 insertions(+), 82 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 4856dbbeb6..6cb9773cf7 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -245,6 +245,21 @@ Types now require using TypeScript v4.1 or greater Starting with v4, react-query will no longer log errors (e.g. failed fetches) to the console in production mode, as this was confusing to many. Errors will still show up in development mode. +### Undefined is an illegale cache value for successful queries + +In order to make bailing out of updates possible by returning `undefined`, we had to make `undefined` an illegal cache value. This is in-line with other concepts of react-query, for example, returning `undefined` from the [initialData function](guides/initial-query-data#initial-data-function) will also _not_ set data. + +Further, it is an easy bug to produce `Promise` by adding logging in the queryFn: + +```js +useQuery( + ['key'], + () => axios.get(url).then(result => console.log(result.data)) +) +``` + +This is now disallowed on type level; at runtime, `undefined` will be transformed to a _failed Promise_, which means you will get an `error`, which will also be logged to the console in development mode. + ## New Features 🚀 ### Proper offline support @@ -265,3 +280,14 @@ Mutations can now also be garbage collected automatically, just like queries. Th ### Tracked Queries per default React Query defaults to "tracking" query properties, which should give you a nice boost in render optimization. The feature has existed since [v3.6.0](https://github.com/tannerlinsley/react-query/releases/tag/v3.6.0) and has now become the default behavior with v4. + +### Bailing out of updates with setQueryData + +When using the [functional updater form of setQueryData](../reference/QueryClient#queryclientsetquerydata), you can now bail out of the update by returning `undefined`. This is helpful if `undefined` is given to you as `previousValue`, which means that currently, no cached entry exists and you don't want to / cannot create one, like in the example of toggling a todo: + +```js + queryClient.setQueryData( + ['todo', id], + (previousTodo) => previousTodo ? { ...previousTodo, done: true } : undefined +) + ``` diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index 69e1448327..2c4ab70462 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -214,7 +214,7 @@ queryClient.setQueryData(queryKey, updater) **Options** - `queryKey: QueryKey`: [Query Keys](../guides/query-keys) -- `updater: TData | (oldData: TData | undefined) => TData` +- `updater: TData | (oldData: TData | undefined) => TData | undefined` - If non-function is passed, the data will be updated to this value - If a function is passed, it will receive the old data value and be expected to return a new one. @@ -224,6 +224,8 @@ queryClient.setQueryData(queryKey, updater) setQueryData(queryKey, newData) ``` +If the value is `undefined`, the query data is not updated. + **Using an updater function** For convenience in syntax, you can also pass an updater function which receives the current data value and returns the new one: @@ -232,6 +234,8 @@ For convenience in syntax, you can also pass an updater function which receives setQueryData(queryKey, oldData => newData) ``` +If the updater function returns `undefined`, the query data will not be updated. If the updater function receives `undefined` as input, you can return `undefined` to bail out of the update and thus _not_ create a new cache entry. + ## `queryClient.getQueryState` `getQueryState` is a synchronous function that can be used to get an existing query's state. If the query does not exist, `undefined` will be returned. diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index 4f628bebbc..e826bed046 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -68,7 +68,7 @@ const result = useQuery({ **Options** -- `queryKey: unknown[]` +- `queryKey: unknown[]` - **Required** - The query key to use for this query. - The query key will be hashed into a stable hash. See [Query Keys](../guides/query-keys) for more information. @@ -77,7 +77,7 @@ const result = useQuery({ - **Required, but only if no default query function has been defined** See [Default Query Function](../guides/default-query-function) for more information. - The function that the query will use to request data. - Receives a [QueryFunctionContext](../guides/query-functions#queryfunctioncontext) - - Must return a promise that will either resolve data or throw an error. + - Must return a promise that will either resolve data or throw an error. The data cannot be `undefined`. - `enabled: boolean` - Set this to `false` to disable this query from automatically running. - Can be used for [Dependent Queries](../guides/dependent-queries). diff --git a/src/core/query.ts b/src/core/query.ts index 06935088dc..be087dbbea 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -1,7 +1,5 @@ import { getAbortController, - Updater, - functionalUpdate, noop, replaceEqualDeep, timeUntilStale, @@ -195,14 +193,11 @@ export class Query< } setData( - updater: Updater, + data: TData, options?: SetDataOptions & { notifySuccess: boolean } ): TData { const prevData = this.state.data - // Get the new data - let data = functionalUpdate(updater, prevData) - // Use prev data if an isDataEqual function is defined and returns `true` if (this.options.isDataEqual?.(prevData, data)) { data = prevData as TData @@ -438,11 +433,41 @@ export class Query< this.dispatch({ type: 'fetch', meta: context.fetchOptions?.meta }) } + const onError = (error: TError | { silent?: boolean }) => { + // Optimistically update state if needed + if (!(isCancelledError(error) && error.silent)) { + this.dispatch({ + type: 'error', + error: error as TError, + }) + } + + if (!isCancelledError(error)) { + // Notify cache callback + this.cache.config.onError?.(error, this as Query) + + if (process.env.NODE_ENV !== 'production') { + getLogger().error(error) + } + } + + if (!this.isFetchingOptimistic) { + // Schedule query gc after fetching + this.scheduleGc() + } + this.isFetchingOptimistic = false + } + // Try to fetch the data this.retryer = createRetryer({ fn: context.fetchFn as () => TData, abort: abortController?.abort?.bind(abortController), onSuccess: data => { + if (typeof data === 'undefined') { + onError(new Error('Query data cannot be undefined') as any) + return + } + this.setData(data as TData) // Notify cache callback @@ -454,30 +479,7 @@ export class Query< } this.isFetchingOptimistic = false }, - onError: (error: TError | { silent?: boolean }) => { - // Optimistically update state if needed - if (!(isCancelledError(error) && error.silent)) { - this.dispatch({ - type: 'error', - error: error as TError, - }) - } - - if (!isCancelledError(error)) { - // Notify cache callback - this.cache.config.onError?.(error, this as Query) - - if (process.env.NODE_ENV !== 'production') { - getLogger().error(error) - } - } - - if (!this.isFetchingOptimistic) { - // Schedule query gc after fetching - this.scheduleGc() - } - this.isFetchingOptimistic = false - }, + onError, onFail: () => { this.dispatch({ type: 'failed' }) }, diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index d09461ef83..589b723504 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -8,6 +8,7 @@ import { partialMatchKey, hashQueryKeyByOptions, MutationFilters, + functionalUpdate, } from './utils' import type { QueryClientConfig, @@ -125,14 +126,22 @@ export class QueryClient { setQueryData( queryKey: QueryKey, - updater: Updater, + updater: Updater | undefined, options?: SetDataOptions - ): TData { + ): TData | undefined { + const query = this.queryCache.find(queryKey) + const prevData = query?.state.data + const data = functionalUpdate(updater, prevData) + + if (typeof data === 'undefined') { + return undefined + } + const parsedOptions = parseQueryArgs(queryKey) const defaultedOptions = this.defaultQueryOptions(parsedOptions) return this.queryCache .build(this, defaultedOptions) - .setData(updater, { ...options, notifySuccess: false }) + .setData(data, { ...options, notifySuccess: false }) } setQueriesData( @@ -151,7 +160,7 @@ export class QueryClient { queryKeyOrFilters: QueryKey | QueryFilters, updater: Updater, options?: SetDataOptions - ): [QueryKey, TData][] { + ): [QueryKey, TData | undefined][] { return notifyManager.batch(() => this.getQueryCache() .findAll(queryKeyOrFilters) diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index cc120b933d..b5aefa4b14 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -12,6 +12,7 @@ import { isError, onlineManager, QueryFunctionContext, + QueryObserverResult, } from '../..' import { waitFor } from '@testing-library/react' @@ -787,6 +788,7 @@ describe('query', () => { let signalTest: any await queryClient.prefetchQuery(key, ({ signal }) => { signalTest = signal + return 'data' }) expect(signalTest).toBeUndefined() @@ -814,6 +816,31 @@ describe('query', () => { consoleMock.mockRestore() }) + test('fetch should dispatch an error if the queryFn returns undefined', async () => { + const key = queryKey() + + const observer = new QueryObserver(queryClient, { + queryKey: key, + queryFn: (() => undefined) as any, + retry: false, + }) + + let observerResult: QueryObserverResult | undefined + + const unsubscribe = observer.subscribe(result => { + observerResult = result + }) + + await sleep(10) + + expect(observerResult).toMatchObject({ + isError: true, + error: new Error('Query data cannot be undefined'), + }) + + unsubscribe() + }) + test('fetch should dispatch fetch if is fetching and current promise is undefined', async () => { const key = queryKey() diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index c32cf97cc0..0d60ff4206 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -53,7 +53,7 @@ describe('queryClient', () => { }, }) - const fetchData = () => Promise.resolve(undefined) + const fetchData = () => Promise.resolve('data') await testClient.prefetchQuery(key, fetchData) const newQuery = testClient.getQueryCache().find(key) expect(newQuery?.options.cacheTime).toBe(Infinity) @@ -301,6 +301,34 @@ describe('queryClient', () => { expect(queryClient.getQueryData(key)).toBe('qux') }) + test('should not create a new query if query was not found and data is undefined', () => { + const key = queryKey() + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + queryClient.setQueryData(key, undefined) + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + }) + + test('should not create a new query if query was not found and updater returns undefined', () => { + const key = queryKey() + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + queryClient.setQueryData(key, () => undefined) + expect(queryClient.getQueryCache().find(key)).toBe(undefined) + }) + + test('should not update query data if data is undefined', () => { + const key = queryKey() + queryClient.setQueryData(key, 'qux') + queryClient.setQueryData(key, undefined) + expect(queryClient.getQueryData(key)).toBe('qux') + }) + + test('should not update query data if updater returns undefined', () => { + const key = queryKey() + queryClient.setQueryData(key, 'qux') + queryClient.setQueryData(key, () => undefined) + expect(queryClient.getQueryData(key)).toBe('qux') + }) + test('should accept an update function', () => { const key = queryKey() @@ -871,8 +899,8 @@ describe('queryClient', () => { test('should refetch all queries when no arguments are given', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer1 = new QueryObserver(queryClient, { @@ -897,8 +925,8 @@ describe('queryClient', () => { test('should be able to refetch all fresh queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -916,8 +944,8 @@ describe('queryClient', () => { test('should be able to refetch all stale queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -936,8 +964,8 @@ describe('queryClient', () => { test('should be able to refetch all stale and active queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) queryClient.invalidateQueries(key1) @@ -958,8 +986,8 @@ describe('queryClient', () => { test('should be able to refetch all active and inactive queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -977,8 +1005,8 @@ describe('queryClient', () => { test('should be able to refetch all active and inactive queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -996,8 +1024,8 @@ describe('queryClient', () => { test('should be able to refetch only active queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1015,8 +1043,8 @@ describe('queryClient', () => { test('should be able to refetch only inactive queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1060,8 +1088,8 @@ describe('queryClient', () => { test('should refetch active queries by default', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1079,8 +1107,8 @@ describe('queryClient', () => { test('should not refetch inactive queries by default', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1098,8 +1126,8 @@ describe('queryClient', () => { test('should not refetch active queries when "refetch" is "none"', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1119,8 +1147,8 @@ describe('queryClient', () => { test('should refetch inactive queries when "refetch" is "inactive"', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1142,8 +1170,8 @@ describe('queryClient', () => { test('should refetch active and inactive queries when "refetch" is "all"', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') await queryClient.fetchQuery(key1, queryFn1) await queryClient.fetchQuery(key2, queryFn2) const observer = new QueryObserver(queryClient, { @@ -1269,8 +1297,8 @@ describe('queryClient', () => { test('should refetch all active queries', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue('data1') + const queryFn2 = jest.fn().mockReturnValue('data2') const observer1 = new QueryObserver(queryClient, { queryKey: key1, queryFn: queryFn1, diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index 9daf7d8207..ed95023aaf 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -331,7 +331,7 @@ describe('queryObserver', () => { test('should be able to watch a query without defining a query function', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const callback = jest.fn() const observer = new QueryObserver(queryClient, { queryKey: key, @@ -346,7 +346,7 @@ describe('queryObserver', () => { test('should accept unresolved query config in update function', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const observer = new QueryObserver(queryClient, { queryKey: key, enabled: false, diff --git a/src/core/types.ts b/src/core/types.ts index f177030963..9df4e6a954 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -6,10 +6,14 @@ import type { QueryCache } from './queryCache' import type { MutationCache } from './mutationCache' export type QueryKey = readonly unknown[] +export type QueryFunctionData = T extends undefined ? never : T + export type QueryFunction< T = unknown, TQueryKey extends QueryKey = QueryKey -> = (context: QueryFunctionContext) => T | Promise +> = ( + context: QueryFunctionContext +) => QueryFunctionData | Promise> export interface QueryFunctionContext< TQueryKey extends QueryKey = QueryKey, diff --git a/src/reactjs/tests/ssr.test.tsx b/src/reactjs/tests/ssr.test.tsx index 2363a160c0..1a2c738331 100644 --- a/src/reactjs/tests/ssr.test.tsx +++ b/src/reactjs/tests/ssr.test.tsx @@ -60,7 +60,10 @@ describe('Server Side Rendering', () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) const key = queryKey() - const queryFn = jest.fn(() => sleep(10)) + const queryFn = jest.fn(() => { + sleep(10) + return 'data' + }) function Page() { const query = useQuery(key, queryFn) diff --git a/src/reactjs/tests/suspense.test.tsx b/src/reactjs/tests/suspense.test.tsx index a4b68d4210..6bf53b56ea 100644 --- a/src/reactjs/tests/suspense.test.tsx +++ b/src/reactjs/tests/suspense.test.tsx @@ -114,7 +114,10 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() const queryFn = jest.fn() - queryFn.mockImplementation(() => sleep(10)) + queryFn.mockImplementation(() => { + sleep(10) + return 'data' + }) function Page() { useQuery([key], queryFn, { suspense: true }) @@ -138,7 +141,14 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() function Page() { - useQuery(key, () => sleep(10), { suspense: true }) + useQuery( + key, + () => { + sleep(10) + return 'data' + }, + { suspense: true } + ) return <>rendered } @@ -212,10 +222,17 @@ describe("useQuery's in Suspense mode", () => { const successFn2 = jest.fn() function FirstComponent() { - useQuery(key, () => sleep(10), { - suspense: true, - onSuccess: successFn1, - }) + useQuery( + key, + () => { + sleep(10) + return 'data' + }, + { + suspense: true, + onSuccess: successFn1, + } + ) return first } diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index 1ca4f48c3b..4a0bb9d286 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -933,10 +933,12 @@ describe('useQueries', () => { return { queryKey: key, onError: err, - queryFn: (ctx: QueryFunctionContext) => { - expectType(ctx.queryKey) - return fn?.call({}, ctx) - }, + queryFn: fn + ? (ctx: QueryFunctionContext) => { + expectType(ctx.queryKey) + return fn.call({}, ctx) + } + : undefined, } } ), diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index 93e1ea3e52..6a68bd126d 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -3447,7 +3447,7 @@ describe('useQuery', () => { const [enabled, setEnabled] = React.useState(false) const [isPrefetched, setPrefetched] = React.useState(false) - const query = useQuery(key, () => undefined, { + const query = useQuery(key, () => 'data', { enabled, }) @@ -3609,7 +3609,7 @@ describe('useQuery', () => { const key = queryKey() function Page() { - const query = useQuery(key, () => undefined, { + const query = useQuery(key, () => 'data', { enabled: false, }) From 6ec681147ba0d13ae41adba96ed26c54d83b2d2d Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 9 Feb 2022 20:16:37 +0100 Subject: [PATCH 20/76] chore: add a test to ensure that callback on .mutate are only called for the last observer --- src/reactjs/tests/useMutation.test.tsx | 63 ++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/reactjs/tests/useMutation.test.tsx b/src/reactjs/tests/useMutation.test.tsx index b3b2e14981..0cab2ec28b 100644 --- a/src/reactjs/tests/useMutation.test.tsx +++ b/src/reactjs/tests/useMutation.test.tsx @@ -820,4 +820,67 @@ describe('useMutation', () => { expect(onSuccessMutate).toHaveBeenCalledTimes(0) expect(onSettledMutate).toHaveBeenCalledTimes(0) }) + + it('should call mutate callbacks only for the last observer', async () => { + const onSuccess = jest.fn() + const onSuccessMutate = jest.fn() + const onSettled = jest.fn() + const onSettledMutate = jest.fn() + let count = 0 + + function Page() { + const mutation = useMutation( + async (_text: string) => { + count++ + await sleep(10) + return `result${count}` + }, + { + onSuccess, + onSettled, + } + ) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('data: null, status: idle') + + rendered.getByRole('button', { name: /mutate/i }).click() + rendered.getByRole('button', { name: /mutate/i }).click() + + await rendered.findByText('data: result2, status: success') + + expect(count).toBe(2) + + expect(onSuccess).toHaveBeenCalledTimes(2) + expect(onSettled).toHaveBeenCalledTimes(2) + expect(onSuccessMutate).toHaveBeenCalledTimes(1) + expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined) + expect(onSettledMutate).toHaveBeenCalledTimes(1) + expect(onSettledMutate).toHaveBeenCalledWith( + 'result2', + null, + 'todo', + undefined + ) + }) }) From 7064c694839d4d21dd61cfa3195073562f0a49b0 Mon Sep 17 00:00:00 2001 From: hverlin Date: Thu, 10 Feb 2022 20:47:46 +0100 Subject: [PATCH 21/76] docs: Document compression use case for `createWebStoragePersister` (#3285) Add an example on how to `compress`/`decompress` data from local storage in case you need to cache large payloads. Context: https://github.com/tannerlinsley/react-query/pull/2864#issuecomment-1034604428 --- .../plugins/createWebStoragePersister.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/src/pages/plugins/createWebStoragePersister.md b/docs/src/pages/plugins/createWebStoragePersister.md index 58f9d93168..ced3302e20 100644 --- a/docs/src/pages/plugins/createWebStoragePersister.md +++ b/docs/src/pages/plugins/createWebStoragePersister.md @@ -72,3 +72,27 @@ The default options are: deserialize = JSON.parse, } ``` + +#### `serialize` and `deserialize` options +There is a limit to the amount of data which can be stored in `localStorage`. +If you need to store more data in `localStorage`, you can override the `serialize` and `deserialize` functions to compress and decrompress the data using a library like [lz-string](https://github.com/pieroxy/lz-string/). + +```js +import { QueryClient } from 'react-query'; +import { persistQueryClient } from 'react-query/persistQueryClient' +import { createWebStoragePersister } from 'react-query/createWebStoragePersister' + +import { compress, decompress } from 'lz-string'; + +const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity } } }); + +persistQueryClient({ + queryClient: connectionsQueryClient, + persistor: createWebStoragePersister({ + storage: window.localStorage, + serialize: data => compress(JSON.stringify(data)), + deserialize: data => JSON.parse(decompress(data)), + }), + maxAge: Infinity, +}); +``` From 00d0c527bc67cb0db7be9682565c60ffafc1ad51 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 12 Feb 2022 13:58:06 +0100 Subject: [PATCH 22/76] refactor: inline / remove some functions for size improvements (#3289) * refactor: size inline reducers in query and mutation because they are only used from dispatch * refactor: remove default case in reducers actions are only produced by our own code, which is in TypeScript, so we'll get a type error if there is an action not handled. Unknown actions can't exist, as the tests show: we'd need a ts-expects-error to go this way * refactor: inline executeMutation because it's only used once * refactor: inline getObserver and updateObservers in useQueries because it's only used once * refactor: extract getDefaultState getDefaultState is a pure function, with no access to `this`, so we can extract it. This is in-line with what `mutation` is doing * refactor: remove getNextResult it was only used in tests, and it's also not documented * refactor: inline clearTimers because it's only used once * refactor: inline shouldNotifyListeners because it's only used once * refactor: inline resumePausedMutations * refactor: tests every stubbed queryFn needs a mock implementation to avoid console errors: Error: Query data cannot be undefined --- src/core/mutation.ts | 163 +++++++++--------- src/core/mutationCache.ts | 8 - src/core/queriesObserver.ts | 109 ++++++------ src/core/query.ts | 220 ++++++++++++------------ src/core/queryClient.ts | 6 +- src/core/queryObserver.ts | 88 ++++------ src/core/tests/mutations.test.tsx | 24 --- src/core/tests/queriesObserver.test.tsx | 4 +- src/core/tests/query.test.tsx | 12 -- src/core/tests/queryClient.test.tsx | 40 ++--- src/core/tests/queryObserver.test.tsx | 42 +---- src/reactjs/tests/ssr.test.tsx | 2 +- src/reactjs/tests/useQuery.test.tsx | 4 +- 13 files changed, 301 insertions(+), 421 deletions(-) diff --git a/src/core/mutation.ts b/src/core/mutation.ts index 6d303b91bc..f8cecc5d3d 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -186,8 +186,33 @@ export class Mutation< }) } + const executeMutation = () => { + this.retryer = createRetryer({ + fn: () => { + if (!this.options.mutationFn) { + return Promise.reject('No mutationFn found') + } + return this.options.mutationFn(this.state.variables!) + }, + onFail: () => { + this.dispatch({ type: 'failed' }) + }, + onPause: () => { + this.dispatch({ type: 'pause' }) + }, + onContinue: () => { + this.dispatch({ type: 'continue' }) + }, + retry: this.options.retry ?? 0, + retryDelay: this.options.retryDelay, + networkMode: this.options.networkMode, + }) + + return this.retryer.promise + } + return promise - .then(() => this.executeMutation()) + .then(executeMutation) .then(result => { data = result // Notify cache callback @@ -253,33 +278,61 @@ export class Mutation< }) } - private executeMutation(): Promise { - this.retryer = createRetryer({ - fn: () => { - if (!this.options.mutationFn) { - return Promise.reject('No mutationFn found') - } - return this.options.mutationFn(this.state.variables!) - }, - onFail: () => { - this.dispatch({ type: 'failed' }) - }, - onPause: () => { - this.dispatch({ type: 'pause' }) - }, - onContinue: () => { - this.dispatch({ type: 'continue' }) - }, - retry: this.options.retry ?? 0, - retryDelay: this.options.retryDelay, - networkMode: this.options.networkMode, - }) - - return this.retryer.promise - } - private dispatch(action: Action): void { - this.state = this.reducer(action) + const reducer = ( + state: MutationState + ): MutationState => { + switch (action.type) { + case 'failed': + return { + ...state, + failureCount: state.failureCount + 1, + } + case 'pause': + return { + ...state, + isPaused: true, + } + case 'continue': + return { + ...state, + isPaused: false, + } + case 'loading': + return { + ...state, + context: action.context, + data: undefined, + error: null, + isPaused: !canFetch(this.options.networkMode), + status: 'loading', + variables: action.variables, + } + case 'success': + return { + ...state, + data: action.data, + error: null, + status: 'success', + isPaused: false, + } + case 'error': + return { + ...state, + data: undefined, + error: action.error, + failureCount: state.failureCount + 1, + isPaused: false, + status: 'error', + } + case 'setState': + return { + ...state, + ...action.state, + } + } + } + this.state = reducer(this.state) notifyManager.batch(() => { this.observers.forEach(observer => { @@ -292,62 +345,6 @@ export class Mutation< }) }) } - - private reducer( - action: Action - ): MutationState { - switch (action.type) { - case 'failed': - return { - ...this.state, - failureCount: this.state.failureCount + 1, - } - case 'pause': - return { - ...this.state, - isPaused: true, - } - case 'continue': - return { - ...this.state, - isPaused: false, - } - case 'loading': - return { - ...this.state, - context: action.context, - data: undefined, - error: null, - isPaused: !canFetch(this.options.networkMode), - status: 'loading', - variables: action.variables, - } - case 'success': - return { - ...this.state, - data: action.data, - error: null, - status: 'success', - isPaused: false, - } - case 'error': - return { - ...this.state, - data: undefined, - error: action.error, - failureCount: this.state.failureCount + 1, - isPaused: false, - status: 'error', - } - case 'setState': - return { - ...this.state, - ...action.state, - } - default: - return this.state - } - } } export function getDefaultState< diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts index a101013d3f..91eb0ac9b9 100644 --- a/src/core/mutationCache.ts +++ b/src/core/mutationCache.ts @@ -143,14 +143,6 @@ export class MutationCache extends Subscribable { }) } - onFocus(): void { - this.resumePausedMutations() - } - - onOnline(): void { - this.resumePausedMutations() - } - resumePausedMutations(): Promise { const pausedMutations = this.mutations.filter(x => x.state.isPaused) return notifyManager.batch(() => diff --git a/src/core/queriesObserver.ts b/src/core/queriesObserver.ts index 79d1bbc168..a582a703a0 100644 --- a/src/core/queriesObserver.ts +++ b/src/core/queriesObserver.ts @@ -60,7 +60,52 @@ export class QueriesObserver extends Subscribable { notifyOptions?: NotifyOptions ): void { this.queries = queries - this.updateObservers(notifyOptions) + + notifyManager.batch(() => { + const prevObservers = this.observers + + const newObserverMatches = this.findMatchingObservers(this.queries) + + // set options for the new observers to notify of changes + newObserverMatches.forEach(match => + match.observer.setOptions(match.defaultedQueryOptions, notifyOptions) + ) + + const newObservers = newObserverMatches.map(match => match.observer) + const newObserversMap = Object.fromEntries( + newObservers.map(observer => [observer.options.queryHash, observer]) + ) + const newResult = newObservers.map(observer => + observer.getCurrentResult() + ) + + const hasIndexChange = newObservers.some( + (observer, index) => observer !== prevObservers[index] + ) + if (prevObservers.length === newObservers.length && !hasIndexChange) { + return + } + + this.observers = newObservers + this.observersMap = newObserversMap + this.result = newResult + + if (!this.hasListeners()) { + return + } + + difference(prevObservers, newObservers).forEach(observer => { + observer.destroy() + }) + + difference(newObservers, prevObservers).forEach(observer => { + observer.subscribe(result => { + this.onUpdate(observer, result) + }) + }) + + this.notify() + }) } getCurrentResult(): QueryObserverResult[] { @@ -106,6 +151,12 @@ export class QueriesObserver extends Subscribable { !matchingObservers.some(match => match.observer === prevObserver) ) + const getObserver = (options: QueryObserverOptions): QueryObserver => { + const defaultedOptions = this.client.defaultQueryOptions(options) + const currentObserver = this.observersMap[defaultedOptions.queryHash!] + return currentObserver ?? new QueryObserver(this.client, defaultedOptions) + } + const newOrReusedObservers: QueryObserverMatch[] = unmatchedQueries.map( (options, index) => { if (options.keepPreviousData) { @@ -120,7 +171,7 @@ export class QueriesObserver extends Subscribable { } return { defaultedQueryOptions: options, - observer: this.getObserver(options), + observer: getObserver(options), } } ) @@ -137,60 +188,6 @@ export class QueriesObserver extends Subscribable { .sort(sortMatchesByOrderOfQueries) } - private getObserver(options: QueryObserverOptions): QueryObserver { - const defaultedOptions = this.client.defaultQueryOptions(options) - const currentObserver = this.observersMap[defaultedOptions.queryHash!] - return currentObserver ?? new QueryObserver(this.client, defaultedOptions) - } - - private updateObservers(notifyOptions?: NotifyOptions): void { - notifyManager.batch(() => { - const prevObservers = this.observers - - const newObserverMatches = this.findMatchingObservers(this.queries) - - // set options for the new observers to notify of changes - newObserverMatches.forEach(match => - match.observer.setOptions(match.defaultedQueryOptions, notifyOptions) - ) - - const newObservers = newObserverMatches.map(match => match.observer) - const newObserversMap = Object.fromEntries( - newObservers.map(observer => [observer.options.queryHash, observer]) - ) - const newResult = newObservers.map(observer => - observer.getCurrentResult() - ) - - const hasIndexChange = newObservers.some( - (observer, index) => observer !== prevObservers[index] - ) - if (prevObservers.length === newObservers.length && !hasIndexChange) { - return - } - - this.observers = newObservers - this.observersMap = newObserversMap - this.result = newResult - - if (!this.hasListeners()) { - return - } - - difference(prevObservers, newObservers).forEach(observer => { - observer.destroy() - }) - - difference(newObservers, prevObservers).forEach(observer => { - observer.subscribe(result => { - this.onUpdate(observer, result) - }) - }) - - this.notify() - }) - } - private onUpdate(observer: QueryObserver, result: QueryObserverResult): void { const index = this.observers.indexOf(observer) if (index !== -1) { diff --git a/src/core/query.ts b/src/core/query.ts index be087dbbea..8832272818 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -171,7 +171,7 @@ export class Query< this.cache = config.cache this.queryKey = config.queryKey this.queryHash = config.queryHash - this.initialState = config.state || this.getDefaultState(this.options) + this.initialState = config.state || getDefaultState(this.options) this.state = this.initialState this.meta = config.meta } @@ -500,7 +500,80 @@ export class Query< } private dispatch(action: Action): void { - this.state = this.reducer(this.state, action) + const reducer = ( + state: QueryState + ): QueryState => { + switch (action.type) { + case 'failed': + return { + ...state, + fetchFailureCount: state.fetchFailureCount + 1, + } + case 'pause': + return { + ...state, + fetchStatus: 'paused', + } + case 'continue': + return { + ...state, + fetchStatus: 'fetching', + } + case 'fetch': + return { + ...state, + fetchFailureCount: 0, + fetchMeta: action.meta ?? null, + fetchStatus: canFetch(this.options.networkMode) + ? 'fetching' + : 'paused', + ...(!state.dataUpdatedAt && { + error: null, + status: 'loading', + }), + } + case 'success': + return { + ...state, + data: action.data, + dataUpdateCount: state.dataUpdateCount + 1, + dataUpdatedAt: action.dataUpdatedAt ?? Date.now(), + error: null, + fetchFailureCount: 0, + isInvalidated: false, + fetchStatus: 'idle', + status: 'success', + } + case 'error': + const error = action.error as unknown + + if (isCancelledError(error) && error.revert && this.revertState) { + return { ...this.revertState } + } + + return { + ...state, + error: error as TError, + errorUpdateCount: state.errorUpdateCount + 1, + errorUpdatedAt: Date.now(), + fetchFailureCount: state.fetchFailureCount + 1, + fetchStatus: 'idle', + status: 'error', + } + case 'invalidate': + return { + ...state, + isInvalidated: true, + } + case 'setState': + return { + ...state, + ...action.state, + } + } + } + + this.state = reducer(this.state) notifyManager.batch(() => { this.observers.forEach(observer => { @@ -510,113 +583,42 @@ export class Query< this.cache.notify({ query: this, type: 'updated', action }) }) } +} - protected getDefaultState( - options: QueryOptions - ): QueryState { - const data = - typeof options.initialData === 'function' - ? (options.initialData as InitialDataFunction)() - : options.initialData - - const hasInitialData = typeof options.initialData !== 'undefined' - - const initialDataUpdatedAt = hasInitialData - ? typeof options.initialDataUpdatedAt === 'function' - ? (options.initialDataUpdatedAt as () => number | undefined)() - : options.initialDataUpdatedAt - : 0 - - const hasData = typeof data !== 'undefined' - - return { - data, - dataUpdateCount: 0, - dataUpdatedAt: hasData ? initialDataUpdatedAt ?? Date.now() : 0, - error: null, - errorUpdateCount: 0, - errorUpdatedAt: 0, - fetchFailureCount: 0, - fetchMeta: null, - isInvalidated: false, - status: hasData ? 'success' : 'idle', - fetchStatus: 'idle', - } - } - - protected reducer( - state: QueryState, - action: Action - ): QueryState { - switch (action.type) { - case 'failed': - return { - ...state, - fetchFailureCount: state.fetchFailureCount + 1, - } - case 'pause': - return { - ...state, - fetchStatus: 'paused', - } - case 'continue': - return { - ...state, - fetchStatus: 'fetching', - } - case 'fetch': - return { - ...state, - fetchFailureCount: 0, - fetchMeta: action.meta ?? null, - fetchStatus: canFetch(this.options.networkMode) - ? 'fetching' - : 'paused', - ...(!state.dataUpdatedAt && { - error: null, - status: 'loading', - }), - } - case 'success': - return { - ...state, - data: action.data, - dataUpdateCount: state.dataUpdateCount + 1, - dataUpdatedAt: action.dataUpdatedAt ?? Date.now(), - error: null, - fetchFailureCount: 0, - isInvalidated: false, - fetchStatus: 'idle', - status: 'success', - } - case 'error': - const error = action.error as unknown - - if (isCancelledError(error) && error.revert && this.revertState) { - return { ...this.revertState } - } - - return { - ...state, - error: error as TError, - errorUpdateCount: state.errorUpdateCount + 1, - errorUpdatedAt: Date.now(), - fetchFailureCount: state.fetchFailureCount + 1, - fetchStatus: 'idle', - status: 'error', - } - case 'invalidate': - return { - ...state, - isInvalidated: true, - } - case 'setState': - return { - ...state, - ...action.state, - } - default: - return state - } +function getDefaultState< + TQueryFnData, + TError, + TData, + TQueryKey extends QueryKey +>( + options: QueryOptions +): QueryState { + const data = + typeof options.initialData === 'function' + ? (options.initialData as InitialDataFunction)() + : options.initialData + + const hasInitialData = typeof options.initialData !== 'undefined' + + const initialDataUpdatedAt = hasInitialData + ? typeof options.initialDataUpdatedAt === 'function' + ? (options.initialDataUpdatedAt as () => number | undefined)() + : options.initialDataUpdatedAt + : 0 + + const hasData = typeof data !== 'undefined' + + return { + data, + dataUpdateCount: 0, + dataUpdatedAt: hasData ? initialDataUpdatedAt ?? Date.now() : 0, + error: null, + errorUpdateCount: 0, + errorUpdatedAt: 0, + fetchFailureCount: 0, + fetchMeta: null, + isInvalidated: false, + status: hasData ? 'success' : 'idle', + fetchStatus: 'idle', } } diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index 589b723504..af0cf460af 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -75,13 +75,13 @@ export class QueryClient { mount(): void { this.unsubscribeFocus = focusManager.subscribe(() => { if (focusManager.isFocused()) { - this.mutationCache.onFocus() + this.resumePausedMutations() this.queryCache.onFocus() } }) this.unsubscribeOnline = onlineManager.subscribe(() => { if (onlineManager.isOnline()) { - this.mutationCache.onOnline() + this.resumePausedMutations() this.queryCache.onOnline() } }) @@ -509,7 +509,7 @@ export class QueryClient { } resumePausedMutations(): Promise { - return this.getMutationCache().resumePausedMutations() + return this.mutationCache.resumePausedMutations() } getQueryCache(): QueryCache { diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 9dacf75669..7cf4c28965 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -16,7 +16,6 @@ import type { QueryObserverResult, QueryOptions, RefetchOptions, - ResultOptions, } from './types' import type { Query, QueryState, Action, FetchOptions } from './query' import type { QueryClient } from './queryClient' @@ -131,7 +130,8 @@ export class QueryObserver< destroy(): void { this.listeners = [] - this.clearTimers() + this.clearStaleTimeout() + this.clearRefetchInterval() this.currentQuery.removeObserver(this) } @@ -246,23 +246,6 @@ export class QueryObserver< return trackedResult } - getNextResult( - options?: ResultOptions - ): Promise> { - return new Promise((resolve, reject) => { - const unsubscribe = this.subscribe(result => { - if (!result.isFetching) { - unsubscribe() - if (result.isError && options?.throwOnError) { - reject(result.error) - } else { - resolve(result) - } - } - }) - }) - } - getCurrentQuery(): Query { return this.currentQuery } @@ -395,11 +378,6 @@ export class QueryObserver< this.updateRefetchInterval(this.computeRefetchInterval()) } - private clearTimers(): void { - this.clearStaleTimeout() - this.clearRefetchInterval() - } - private clearStaleTimeout(): void { clearTimeout(this.staleTimeoutId!) this.staleTimeoutId = undefined @@ -588,36 +566,6 @@ export class QueryObserver< return result as QueryObserverResult } - private shouldNotifyListeners( - result: QueryObserverResult, - prevResult?: QueryObserverResult - ): boolean { - if (!prevResult) { - return true - } - - const { notifyOnChangeProps } = this.options - - if ( - notifyOnChangeProps === 'all' || - (!notifyOnChangeProps && !this.trackedProps.size) - ) { - return true - } - - const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps) - - if (this.options.useErrorBoundary) { - includedProps.add('error') - } - - return Object.keys(result).some(key => { - const typedKey = key as keyof QueryObserverResult - const changed = result[typedKey] !== prevResult[typedKey] - return changed && includedProps.has(typedKey) - }) - } - updateResult(notifyOptions?: NotifyOptions): void { const prevResult = this.currentResult as | QueryObserverResult @@ -635,10 +583,34 @@ export class QueryObserver< // Determine which callbacks to trigger const defaultNotifyOptions: NotifyOptions = { cache: true } - if ( - notifyOptions?.listeners !== false && - this.shouldNotifyListeners(this.currentResult, prevResult) - ) { + const shouldNotifyListeners = (): boolean => { + if (!prevResult) { + return true + } + + const { notifyOnChangeProps } = this.options + + if ( + notifyOnChangeProps === 'all' || + (!notifyOnChangeProps && !this.trackedProps.size) + ) { + return true + } + + const includedProps = new Set(notifyOnChangeProps ?? this.trackedProps) + + if (this.options.useErrorBoundary) { + includedProps.add('error') + } + + return Object.keys(this.currentResult).some(key => { + const typedKey = key as keyof QueryObserverResult + const changed = this.currentResult[typedKey] !== prevResult[typedKey] + return changed && includedProps.has(typedKey) + }) + } + + if (notifyOptions?.listeners !== false && shouldNotifyListeners()) { defaultNotifyOptions.listeners = true } diff --git a/src/core/tests/mutations.test.tsx b/src/core/tests/mutations.test.tsx index 8f624ad64e..71e491bdea 100644 --- a/src/core/tests/mutations.test.tsx +++ b/src/core/tests/mutations.test.tsx @@ -368,28 +368,4 @@ describe('mutations', () => { consoleMock.mockRestore() }) - - test('reducer should return the state for an unknown action type', async () => { - const observer = new MutationObserver(queryClient, { - mutationKey: ['key'], - mutationFn: async () => 'data', - }) - - const spy = jest.fn() - const unsubscribe = observer.subscribe(spy) - observer.mutate() - const mutation = queryClient - .getMutationCache() - .find({ mutationKey: ['key'] })! - const prevState = observer.getCurrentResult() - spy.mockReset() - - // Force dispatch unknown action type - // because no use case has been found - //@ts-expect-error - mutation.dispatch({ type: 'unknown' }) - expect(spy).toHaveBeenCalledWith(prevState) - - unsubscribe() - }) }) diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx index 27c1d0fdfd..8345a2a63c 100644 --- a/src/core/tests/queriesObserver.test.tsx +++ b/src/core/tests/queriesObserver.test.tsx @@ -255,8 +255,8 @@ describe('queriesObserver', () => { test('should trigger all fetches when subscribed', async () => { const key1 = queryKey() const key2 = queryKey() - const queryFn1 = jest.fn() - const queryFn2 = jest.fn() + const queryFn1 = jest.fn().mockReturnValue(1) + const queryFn2 = jest.fn().mockReturnValue(2) const observer = new QueriesObserver(queryClient, [ { queryKey: key1, queryFn: queryFn1 }, { queryKey: key2, queryFn: queryFn2 }, diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index b5aefa4b14..1fdee7d1ff 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -711,18 +711,6 @@ describe('query', () => { query['dispatch'] = dispatchOriginal }) - test('reducer should return the state for an unknown action type', async () => { - const key = queryKey() - - await queryClient.prefetchQuery(key, () => 'data') - const query = queryCache.find(key)! - - // Force unknown action type - //@ts-expect-error - const reducedState = query['reducer'](query.state, { type: 'unknown' }) - expect(reducedState).toEqual(query.state) - }) - test('fetch should not dispatch "fetch" if state meta and fetchOptions meta are the same object', async () => { const key = queryKey() diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index 0d60ff4206..1ed9de02ef 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -863,7 +863,7 @@ describe('queryClient', () => { describe('refetchQueries', () => { test('should not refetch if all observers are disabled', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') await queryClient.fetchQuery(key, queryFn) const observer1 = new QueryObserver(queryClient, { queryKey: key, @@ -877,7 +877,7 @@ describe('queryClient', () => { }) test('should refetch if at least one observer is enabled', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') await queryClient.fetchQuery(key, queryFn) const observer1 = new QueryObserver(queryClient, { queryKey: key, @@ -1424,30 +1424,25 @@ describe('queryClient', () => { testClient.getQueryCache(), 'onOnline' ) - const mutationCacheOnFocusSpy = jest.spyOn( + const mutationCacheResumePausedMutationsSpy = jest.spyOn( testClient.getMutationCache(), - 'onFocus' - ) - const mutationCacheOnOnlineSpy = jest.spyOn( - testClient.getMutationCache(), - 'onOnline' + 'resumePausedMutations' ) focusManager.setFocused(false) expect(queryCacheOnFocusSpy).not.toHaveBeenCalled() - expect(mutationCacheOnFocusSpy).not.toHaveBeenCalled() + expect(mutationCacheResumePausedMutationsSpy).not.toHaveBeenCalled() focusManager.setFocused(true) expect(queryCacheOnFocusSpy).toHaveBeenCalledTimes(1) - expect(queryCacheOnFocusSpy).toHaveBeenCalledTimes(1) + expect(mutationCacheResumePausedMutationsSpy).toHaveBeenCalledTimes(1) expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled() - expect(mutationCacheOnOnlineSpy).not.toHaveBeenCalled() queryCacheOnFocusSpy.mockRestore() - mutationCacheOnFocusSpy.mockRestore() + mutationCacheResumePausedMutationsSpy.mockRestore() queryCacheOnOnlineSpy.mockRestore() - mutationCacheOnOnlineSpy.mockRestore() + focusManager.setFocused(undefined) }) test('should notify queryCache and mutationCache if online', async () => { @@ -1462,30 +1457,25 @@ describe('queryClient', () => { testClient.getQueryCache(), 'onOnline' ) - const mutationCacheOnFocusSpy = jest.spyOn( - testClient.getMutationCache(), - 'onFocus' - ) - const mutationCacheOnOnlineSpy = jest.spyOn( + const mutationCacheResumePausedMutationsSpy = jest.spyOn( testClient.getMutationCache(), - 'onOnline' + 'resumePausedMutations' ) onlineManager.setOnline(false) expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled() - expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled() + expect(mutationCacheResumePausedMutationsSpy).not.toHaveBeenCalled() onlineManager.setOnline(true) expect(queryCacheOnOnlineSpy).toHaveBeenCalledTimes(1) - expect(queryCacheOnOnlineSpy).toHaveBeenCalledTimes(1) + expect(mutationCacheResumePausedMutationsSpy).toHaveBeenCalledTimes(1) - expect(mutationCacheOnFocusSpy).not.toHaveBeenCalled() - expect(mutationCacheOnFocusSpy).not.toHaveBeenCalled() + expect(queryCacheOnFocusSpy).not.toHaveBeenCalled() queryCacheOnFocusSpy.mockRestore() - mutationCacheOnFocusSpy.mockRestore() queryCacheOnOnlineSpy.mockRestore() - mutationCacheOnOnlineSpy.mockRestore() + mutationCacheResumePausedMutationsSpy.mockRestore() + onlineManager.setOnline(undefined) }) }) diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index ed95023aaf..af89f3d576 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -25,7 +25,7 @@ describe('queryObserver', () => { test('should trigger a fetch when subscribed', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const observer = new QueryObserver(queryClient, { queryKey: key, queryFn }) const unsubscribe = observer.subscribe(() => undefined) await sleep(1) @@ -309,7 +309,7 @@ describe('queryObserver', () => { test('should not trigger a fetch when subscribed and disabled', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const observer = new QueryObserver(queryClient, { queryKey: key, queryFn, @@ -323,7 +323,7 @@ describe('queryObserver', () => { test('should not trigger a fetch when not subscribed', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') new QueryObserver(queryClient, { queryKey: key, queryFn }) await sleep(1) expect(queryFn).toHaveBeenCalledTimes(0) @@ -394,40 +394,6 @@ describe('queryObserver', () => { expect(results2[1]).toMatchObject({ data: 'data' }) }) - test('should be able to resolve a promise', async () => { - const key = queryKey() - const queryFn = jest.fn().mockReturnValue('data') - const observer = new QueryObserver(queryClient, { - queryKey: key, - enabled: false, - }) - let value - observer.getNextResult().then(x => { - value = x - }) - queryClient.prefetchQuery(key, queryFn) - await sleep(50) - expect(queryFn).toHaveBeenCalledTimes(1) - expect(value).toMatchObject({ data: 'data' }) - }) - - test('should be able to resolve a promise with an error', async () => { - const consoleMock = mockConsoleError() - const key = queryKey() - const observer = new QueryObserver(queryClient, { - queryKey: key, - enabled: false, - }) - let error - observer.getNextResult({ throwOnError: true }).catch(e => { - error = e - }) - queryClient.prefetchQuery(key, () => Promise.reject('reject')) - await sleep(50) - expect(error).toEqual('reject') - consoleMock.mockRestore() - }) - test('should stop retry when unsubscribing', async () => { const consoleMock = mockConsoleError() const key = queryKey() @@ -578,7 +544,7 @@ describe('queryObserver', () => { test('should not refetch in background if refetchIntervalInBackground is false', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') focusManager.setFocused(false) const observer = new QueryObserver(queryClient, { diff --git a/src/reactjs/tests/ssr.test.tsx b/src/reactjs/tests/ssr.test.tsx index 1a2c738331..c136e42f72 100644 --- a/src/reactjs/tests/ssr.test.tsx +++ b/src/reactjs/tests/ssr.test.tsx @@ -20,7 +20,7 @@ describe('Server Side Rendering', () => { const queryCache = new QueryCache() const queryClient = new QueryClient({ queryCache }) const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') function Page() { const query = useQuery(key, queryFn) diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index 6a68bd126d..c01af84af3 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -2416,7 +2416,7 @@ describe('useQuery', () => { it('should not refetch query on focus when `enabled` is set to `false`', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') function Page() { const { data = 'default' } = useQuery(key, queryFn, { @@ -3648,7 +3648,7 @@ describe('useQuery', () => { it('should not cause memo churn when data does not change', async () => { const key = queryKey() - const queryFn = jest.fn() + const queryFn = jest.fn().mockReturnValue('data') const memoFn = jest.fn() function Page() { From 293e5e9891e56f1b420475b5af4cb30b3a44acce Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Mon, 14 Feb 2022 20:59:58 +0100 Subject: [PATCH 23/76] refactor: do not log undefined query data warning to the console --- src/core/tests/query.test.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index 1fdee7d1ff..966aeb32e3 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -805,6 +805,8 @@ describe('query', () => { }) test('fetch should dispatch an error if the queryFn returns undefined', async () => { + const consoleMock = mockConsoleError() + const key = queryKey() const observer = new QueryObserver(queryClient, { @@ -821,12 +823,16 @@ describe('query', () => { await sleep(10) + const error = new Error('Query data cannot be undefined') + expect(observerResult).toMatchObject({ isError: true, - error: new Error('Query data cannot be undefined'), + error, }) + expect(consoleMock).toHaveBeenCalledWith(error) unsubscribe() + consoleMock.mockRestore() }) test('fetch should dispatch fetch if is fetching and current promise is undefined', async () => { From ada0e341c8ffbd748cf3b2c91ff3c0b3bbaa00e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20M=C3=A1t=C3=A9=20Petr=C3=B3?= Date: Fri, 18 Feb 2022 10:39:30 +0100 Subject: [PATCH 24/76] feat: Version 4 codemods (#3195) * chore: add `jscodeshift` library to dev-dependencies We're planning to deliver codemods for the next release, so we will need this package to write codemods. * chore: add `@types/jscodeshift` library to dev-dependencies * chore: add an empty `jest` config file under the `codemods` directory For some reason the `moduleNameMapper` causes an error when running the codemod tests, it results in a `transformer is a not function` type error. In order to avoid this now, I add an empty config file and will investigate the root cause of the issue later. * chore: add a very basic codemod implementation and tests The current codemod is able to change the usages of `useQuery` according to the new API, so it will put the existing parameters into an object and pass it as the only parameter, but that's all. More changes coming soon. * chore: fix `prettier` errors in `use_query` codemod * chore: add missing `export` keywords to test suites in case of `use_query` codemod I just want to avoid `eslint` errors in the IDE. * feat(codemod): add basic codemods for query client methods * feat(codemod): add basic `useQueries` codemods * refactor(codemod): rework `useQuery` related codemods * feat(codemod): move files under `v4` directory We decided to move the version 4 related codemods under a separate directory. * chore(codemod): move `jscodeshift` library from `dependencies` to `devDependencies` Accidentally I added this package under `dependencies` but it should be placed under `devDependencies`. * chore(codemod): pick up codemods test by `npm test` From now the `npm test` command will execute the codemod tests as well. * refactor(codemod): remove `without-parameter` test case in case of `queryClient` related codemods This test case is not useful at all, it just adds extra code. * chore(codemod): remove duplications from `queryClient` codemod testfixtures The number of duplications was just simply too much. It doesn't make any sense to maintain repetitive boilerplate code. * chore(codemod): rename `first-parameter-is-identifier` and `object-expression-parameter` test case * chore(codemod): rework `first-parameter-is-identifier` and `object-expression-parameter` test suites In these two test cases, we should try to collect as many test cases as possible. * chore(codemod): remove duplications from `useQuery` codemod testfixtures The number of duplications was just simply too much. It doesn't make any sense to maintain repetitive boilerplate code. * chore(codemod): add missing methods to the object syntax aware query client codemods The following methods were missing: - fetchInfiniteQuery - fetchQuery - prefetchInfiniteQuery - prefetchQuery * chore(codemod): add some more test cases to the `parameter-is-object` test suite * feat(codemod): add basic `useMutation` codemods * refactor(codemod): rename `queryKey` replacer and error, because now the key name can be parameterized * feat(codemod): add support for template literals * refactor(codemod): rename `object-syntax-aware` test to `query-client-methods` I want to combine two tests into a single one. * refactor(codemod): move testfixtures to the root level of `__testfixtures__` directory * refactor(codemod): combine the two `queryClient` codemods and test suites into a single one * refactor(codemod): move `useMutation` related parts and testfixutes to `useQuery` codemod The plan is to combine all existing codemods into a single one. * refactor(codemod): remove the whole `use_mutation` library Regarding the necessary parts that were moved to the `useQuery` codemod, we don't need the leftover code anymore. * feat(codemod): add support for the following hook calls: - `useIsFetching` - `useIsMutating` * refactor(codemod): move `useQueries` related parts and testfixutes to `useQuery` codemod The plan is to combine all existing codemods into a single one. * refactor(codemod): remove the whole `use_queries` library Regarding the necessary parts that were moved to the `useQuery` codemod, we don't need the leftover code anymore. * refactor(codemod): introduce `transformUseQueryLikeUsages` function Maybe this change will make the code read easier a bit. * refactor(codemod): do some refinements on the following tests: - `parameter-is-identifier` - `parameter-is-object-expression` * fix(codemod): in the case of JS, the string literal might be interpreted as `Literal` So without the additional `Literal` check, the codemod wouldn't be applied. * fix(codemod): remove `type` filter from import declarations This filter seems to be too strict because the codemods were not applied to the `examples` directory. * refactor(codemod): move `QueryClient` related parts and testfixutes to `useQuery` codemod The plan is to combine all existing codemods into a single one. * refactor(codemod): remove the whole `query_client_methods` library Regarding the necessary parts that were moved to the `useQuery` codemod, we don't need the leftover code anymore. * refactor(codemod): simplify entry point of the codemod It makes the code a bit slower, but on the other hand, it makes the code easier to read and understand. * feat(codemod): add basic `QueryCache` codemods * fix(codemod): do not transform array expression query keys We want arrays as query keys, so when the current query key is an array, we don't need to transform it. * fix(examples/playground): fix invalid usage of `invalidateQueries` * feat(codemod): show file path in console warnings * refactor(codemod): remove `use_query` directory We decided to group the codemods by major version numbers instead of types. The reason is simple: we will bundle all necessary transformations into a single file, so the consumers will have to apply only one codemod. Hopefully, it will make the DX better. * refactor(codemod): rename `use-query` to `key-transformation` The original `use-query` name is not valid anymore, because the codemod changes the signature of other function/method calls as well. * refactor(codemod): replace the transformer name in `key-transformation` codemod I renamed the transformer with the test, so the transformer name also needs to be updated in the test file. * chore(codemod): add codemod and utilities to the bundle We want the code mod to be part of the bundle. If it's part of the bundle, the consumers can directly access it through the `node_modules` directory and run it. We just simply would like to increase the DX. * refactor(codemod): rename function I shouldn't have committed this. :') * fix(codemod): keep `typeArguments` of the node It will prevent the removal of type annotations on a function/method call. * refactor(codemod): move `query-cache` test cases to `default-import` We want to cover the default, named, and namespaced imports in the case of `QueryCache` as well. * feat(codemod): add support for named imports in case of `QueryCache` and `useQueryCache` The codemod must be able to transform usages in the case of named imports as well. * fix(codemod): always return `Identifier` instance in case of `findImportIdentifier` of function Previously it returned a string as a default value and it could lead to errors in the code. * feat(codemod): add support for namespaced imports in case of `QueryCache` and `useQueryCache` The codemod must be able to transform usages in the case of namespaced imports as well. * refactor(codemod): re-use the previously written utility functions in the hook call transformer By this, we can achieve the same functionality with less code repetition. * refactor(codemod): re-use the previously written utility functions in the query-client transformer By this, we can achieve the same functionality with less code repetition. * refactor(codemod): rename `hook-call-transformer` to `use-query-like` transformer I think this name describes better the purpose of this transformer. * chore(codemod): add todo about the `react-query` import check We shouldn't transform files that don't contain `react-query` imports. * refactor(codemod): make the `use-query-like-transformer` to look the same as other transformers I just want to follow the structure of other transformers. * fix(codemod): do not log warnings in the test environment Warning the user about the unprocessable query keys is useful in production, but in the test environment, it just makes it more difficult to read the output. * docs(codemod): add instructions how to apply codemods * chore(examples): apply codemod on the `examples` directory * docs(codemod): update `Codemod` section The suggested text sounds better. :) Co-authored-by: Dominik Dorfmeister Co-authored-by: Dominik Dorfmeister --- codemods/jest.config.js | 3 + .../__testfixtures__/default-import.input.tsx | 94 ++++ .../default-import.output.tsx | 96 ++++ .../__testfixtures__/named-import.input.tsx | 96 ++++ .../__testfixtures__/named-import.output.tsx | 98 ++++ .../namespaced-import.input.tsx | 86 +++ .../namespaced-import.output.tsx | 88 +++ .../parameter-is-identifier.input.tsx | 49 ++ .../parameter-is-identifier.output.tsx | 49 ++ .../parameter-is-object-expression.input.tsx | 128 +++++ .../parameter-is-object-expression.output.tsx | 175 ++++++ .../__testfixtures__/type-arguments.input.tsx | 25 + .../type-arguments.output.tsx | 31 ++ .../v4/__tests__/key-transformation.test.js | 34 ++ codemods/v4/key-transformation.js | 138 +++++ codemods/v4/utils/index.js | 166 ++++++ codemods/v4/utils/replacers/key-replacer.js | 160 ++++++ .../transformers/query-cache-transformer.js | 115 ++++ .../transformers/query-client-transformer.js | 49 ++ .../use-query-like-transformer.js | 32 ++ codemods/v4/utils/unprocessable-key-error.js | 8 + .../guides/migrating-to-react-query-4.md | 20 + examples/auto-refetching/pages/index.js | 6 +- examples/basic-graphql-request/src/index.js | 2 +- examples/custom-hooks/src/hooks/usePosts.js | 2 +- examples/default-query-function/src/index.js | 4 +- examples/focus-refetching/pages/index.js | 6 +- .../load-more-infinite-scroll/pages/index.js | 2 +- .../pages/index.tsx | 12 +- examples/optimistic-updates/pages/index.js | 12 +- examples/playground/src/index.js | 6 +- examples/prefetching/pages/index.js | 2 +- examples/rick-morty/src/Character.js | 6 +- examples/rick-morty/src/Characters.js | 2 +- examples/rick-morty/src/Episode.js | 4 +- examples/rick-morty/src/Episodes.js | 2 +- examples/simple/src/index.js | 2 +- examples/star-wars/src/Character.js | 6 +- examples/star-wars/src/Characters.js | 2 +- examples/star-wars/src/Film.js | 4 +- examples/star-wars/src/Films.js | 2 +- examples/suspense/src/components/Projects.js | 2 +- examples/suspense/src/index.js | 2 +- package.json | 15 +- yarn.lock | 514 +++++++++++++++++- 45 files changed, 2296 insertions(+), 61 deletions(-) create mode 100644 codemods/jest.config.js create mode 100644 codemods/v4/__testfixtures__/default-import.input.tsx create mode 100644 codemods/v4/__testfixtures__/default-import.output.tsx create mode 100644 codemods/v4/__testfixtures__/named-import.input.tsx create mode 100644 codemods/v4/__testfixtures__/named-import.output.tsx create mode 100644 codemods/v4/__testfixtures__/namespaced-import.input.tsx create mode 100644 codemods/v4/__testfixtures__/namespaced-import.output.tsx create mode 100644 codemods/v4/__testfixtures__/parameter-is-identifier.input.tsx create mode 100644 codemods/v4/__testfixtures__/parameter-is-identifier.output.tsx create mode 100644 codemods/v4/__testfixtures__/parameter-is-object-expression.input.tsx create mode 100644 codemods/v4/__testfixtures__/parameter-is-object-expression.output.tsx create mode 100644 codemods/v4/__testfixtures__/type-arguments.input.tsx create mode 100644 codemods/v4/__testfixtures__/type-arguments.output.tsx create mode 100644 codemods/v4/__tests__/key-transformation.test.js create mode 100644 codemods/v4/key-transformation.js create mode 100644 codemods/v4/utils/index.js create mode 100644 codemods/v4/utils/replacers/key-replacer.js create mode 100644 codemods/v4/utils/transformers/query-cache-transformer.js create mode 100644 codemods/v4/utils/transformers/query-client-transformer.js create mode 100644 codemods/v4/utils/transformers/use-query-like-transformer.js create mode 100644 codemods/v4/utils/unprocessable-key-error.js diff --git a/codemods/jest.config.js b/codemods/jest.config.js new file mode 100644 index 0000000000..5b36187188 --- /dev/null +++ b/codemods/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + testMatch: ['/**/*.test.js'], +} diff --git a/codemods/v4/__testfixtures__/default-import.input.tsx b/codemods/v4/__testfixtures__/default-import.input.tsx new file mode 100644 index 0000000000..23c1c4c490 --- /dev/null +++ b/codemods/v4/__testfixtures__/default-import.input.tsx @@ -0,0 +1,94 @@ +import * as React from 'react' +import { + QueryCache, + QueryClient, + useInfiniteQuery, + useIsFetching, + useIsMutating, + useMutation, + useQueries, + useQuery, + useQueryClient, +} from 'react-query' + +export const Examples = () => { + useQuery('todos') + useInfiniteQuery('todos') + useMutation('todos') + useIsFetching('todos') + useIsMutating('todos') + useQueries([query1, query2]) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.getMutationDefaults('todos') + queryClient.getQueriesData('todos') + queryClient.getQueryData('todos') + queryClient.getQueryDefaults('todos') + queryClient.getQueryState('todos') + queryClient.isFetching('todos') + queryClient.setMutationDefaults('todos', { mutationFn: async () => null }) + queryClient.setQueriesData('todos', () => null) + queryClient.setQueryData('todos', () => null) + queryClient.setQueryDefaults('todos', { queryFn: async () => null }) + queryClient.cancelQueries('todos') + queryClient.fetchInfiniteQuery('todos') + queryClient.fetchQuery('todos') + queryClient.invalidateQueries('todos') + queryClient.prefetchInfiniteQuery('todos') + queryClient.prefetchQuery('todos') + queryClient.refetchQueries('todos') + queryClient.removeQueries('todos') + queryClient.resetQueries('todos') + // --- Direct hook call. + useQueryClient().getMutationDefaults('todos') + useQueryClient().getQueriesData('todos') + useQueryClient().getQueryData('todos') + useQueryClient().getQueryDefaults('todos') + useQueryClient().getQueryState('todos') + useQueryClient().isFetching('todos') + useQueryClient().setMutationDefaults('todos', { + mutationFn: async () => null, + }) + useQueryClient().setQueriesData('todos', () => null) + useQueryClient().setQueryData('todos', () => null) + useQueryClient().setQueryDefaults('todos', { queryFn: async () => null }) + useQueryClient().cancelQueries('todos') + useQueryClient().fetchInfiniteQuery('todos') + useQueryClient().fetchQuery('todos') + useQueryClient().invalidateQueries('todos') + useQueryClient().prefetchInfiniteQuery('todos') + useQueryClient().prefetchQuery('todos') + useQueryClient().refetchQueries('todos') + useQueryClient().removeQueries('todos') + useQueryClient().resetQueries('todos') + // QueryCache + // --- NewExpression + const queryCache1 = new QueryCache({ + onError: (error) => console.log(error), + onSuccess: (success) => console.log(success) + }) + queryCache1.find('todos') + queryCache1.findAll('todos') + // --- Instantiated hook call. + const queryClient1 = useQueryClient() + queryClient1.getQueryCache().find('todos') + queryClient1.getQueryCache().findAll('todos') + // + const queryClient2 = new QueryClient({}) + queryClient2.getQueryCache().find('todos') + queryClient2.getQueryCache().findAll('todos') + // + const queryCache2 = queryClient1.getQueryCache() + queryCache2.find('todos') + queryCache2.findAll('todos') + // --- Direct hook call. + useQueryClient().getQueryCache().find('todos') + useQueryClient().getQueryCache().findAll('todos') + // + const queryCache3 = useQueryClient().getQueryCache() + queryCache3.find('todos') + queryCache3.findAll('todos') + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/default-import.output.tsx b/codemods/v4/__testfixtures__/default-import.output.tsx new file mode 100644 index 0000000000..56cee68386 --- /dev/null +++ b/codemods/v4/__testfixtures__/default-import.output.tsx @@ -0,0 +1,96 @@ +import * as React from 'react' +import { + QueryCache, + QueryClient, + useInfiniteQuery, + useIsFetching, + useIsMutating, + useMutation, + useQueries, + useQuery, + useQueryClient, +} from 'react-query' + +export const Examples = () => { + useQuery(['todos']) + useInfiniteQuery(['todos']) + useMutation(['todos']) + useIsFetching(['todos']) + useIsMutating(['todos']) + useQueries({ + queries: [query1, query2] + }) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.getMutationDefaults(['todos']) + queryClient.getQueriesData(['todos']) + queryClient.getQueryData(['todos']) + queryClient.getQueryDefaults(['todos']) + queryClient.getQueryState(['todos']) + queryClient.isFetching(['todos']) + queryClient.setMutationDefaults(['todos'], { mutationFn: async () => null }) + queryClient.setQueriesData(['todos'], () => null) + queryClient.setQueryData(['todos'], () => null) + queryClient.setQueryDefaults(['todos'], { queryFn: async () => null }) + queryClient.cancelQueries(['todos']) + queryClient.fetchInfiniteQuery(['todos']) + queryClient.fetchQuery(['todos']) + queryClient.invalidateQueries(['todos']) + queryClient.prefetchInfiniteQuery(['todos']) + queryClient.prefetchQuery(['todos']) + queryClient.refetchQueries(['todos']) + queryClient.removeQueries(['todos']) + queryClient.resetQueries(['todos']) + // --- Direct hook call. + useQueryClient().getMutationDefaults(['todos']) + useQueryClient().getQueriesData(['todos']) + useQueryClient().getQueryData(['todos']) + useQueryClient().getQueryDefaults(['todos']) + useQueryClient().getQueryState(['todos']) + useQueryClient().isFetching(['todos']) + useQueryClient().setMutationDefaults(['todos'], { + mutationFn: async () => null, + }) + useQueryClient().setQueriesData(['todos'], () => null) + useQueryClient().setQueryData(['todos'], () => null) + useQueryClient().setQueryDefaults(['todos'], { queryFn: async () => null }) + useQueryClient().cancelQueries(['todos']) + useQueryClient().fetchInfiniteQuery(['todos']) + useQueryClient().fetchQuery(['todos']) + useQueryClient().invalidateQueries(['todos']) + useQueryClient().prefetchInfiniteQuery(['todos']) + useQueryClient().prefetchQuery(['todos']) + useQueryClient().refetchQueries(['todos']) + useQueryClient().removeQueries(['todos']) + useQueryClient().resetQueries(['todos']) + // QueryCache + // --- NewExpression + const queryCache1 = new QueryCache({ + onError: (error) => console.log(error), + onSuccess: (success) => console.log(success) + }) + queryCache1.find(['todos']) + queryCache1.findAll(['todos']) + // --- Instantiated hook call. + const queryClient1 = useQueryClient() + queryClient1.getQueryCache().find(['todos']) + queryClient1.getQueryCache().findAll(['todos']) + // + const queryClient2 = new QueryClient({}) + queryClient2.getQueryCache().find(['todos']) + queryClient2.getQueryCache().findAll(['todos']) + // + const queryCache2 = queryClient1.getQueryCache() + queryCache2.find(['todos']) + queryCache2.findAll(['todos']) + // --- Direct hook call. + useQueryClient().getQueryCache().find(['todos']) + useQueryClient().getQueryCache().findAll(['todos']) + // + const queryCache3 = useQueryClient().getQueryCache() + queryCache3.find(['todos']) + queryCache3.findAll(['todos']) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/named-import.input.tsx b/codemods/v4/__testfixtures__/named-import.input.tsx new file mode 100644 index 0000000000..2d8d594762 --- /dev/null +++ b/codemods/v4/__testfixtures__/named-import.input.tsx @@ -0,0 +1,96 @@ +import * as React from 'react' +import { + QueryCache as RenamedQueryCache, + QueryClient as RenamedQueryClient, + useInfiniteQuery as useRenamedInfiniteQuery, + useIsFetching as useRenamedIsFetching, + useIsMutating as useRenamedIsMutating, + useMutation as useRenamedMutation, + useQueries as useRenamedQueries, + useQuery as useRenamedQuery, + useQueryClient as useRenamedQueryClient, +} from 'react-query' + +export const Examples = () => { + useRenamedQuery('todos') + useRenamedInfiniteQuery('todos') + useRenamedMutation('todos') + useRenamedIsFetching('todos') + useRenamedIsMutating('todos') + useRenamedQueries([query1, query2]) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useRenamedQueryClient() + queryClient.getMutationDefaults('todos') + queryClient.getQueriesData('todos') + queryClient.getQueryData('todos') + queryClient.getQueryDefaults('todos') + queryClient.getQueryState('todos') + queryClient.isFetching('todos') + queryClient.setMutationDefaults('todos', { mutationFn: async () => null }) + queryClient.setQueriesData('todos', () => null) + queryClient.setQueryData('todos', () => null) + queryClient.setQueryDefaults('todos', { queryFn: async () => null }) + queryClient.cancelQueries('todos') + queryClient.fetchInfiniteQuery('todos') + queryClient.fetchQuery('todos') + queryClient.invalidateQueries('todos') + queryClient.prefetchInfiniteQuery('todos') + queryClient.prefetchQuery('todos') + queryClient.refetchQueries('todos') + queryClient.removeQueries('todos') + queryClient.resetQueries('todos') + // --- Direct hook call. + useRenamedQueryClient().getMutationDefaults('todos') + useRenamedQueryClient().getQueriesData('todos') + useRenamedQueryClient().getQueryData('todos') + useRenamedQueryClient().getQueryDefaults('todos') + useRenamedQueryClient().getQueryState('todos') + useRenamedQueryClient().isFetching('todos') + useRenamedQueryClient().setMutationDefaults('todos', { + mutationFn: async () => null, + }) + useRenamedQueryClient().setQueriesData('todos', () => null) + useRenamedQueryClient().setQueryData('todos', () => null) + useRenamedQueryClient().setQueryDefaults('todos', { + queryFn: async () => null, + }) + useRenamedQueryClient().cancelQueries('todos') + useRenamedQueryClient().fetchInfiniteQuery('todos') + useRenamedQueryClient().fetchQuery('todos') + useRenamedQueryClient().invalidateQueries('todos') + useRenamedQueryClient().prefetchInfiniteQuery('todos') + useRenamedQueryClient().prefetchQuery('todos') + useRenamedQueryClient().refetchQueries('todos') + useRenamedQueryClient().removeQueries('todos') + useRenamedQueryClient().resetQueries('todos') + // QueryCache + // --- NewExpression + const queryCache1 = new RenamedQueryCache({ + onError: (error) => console.log(error), + onSuccess: (success) => console.log(success) + }) + queryCache1.find('todos') + queryCache1.findAll('todos') + // --- Instantiated hook call. + const queryClient1 = useRenamedQueryClient() + queryClient1.getQueryCache().find('todos') + queryClient1.getQueryCache().findAll('todos') + // + const queryClient2 = new RenamedQueryClient({}) + queryClient2.getQueryCache().find('todos') + queryClient2.getQueryCache().findAll('todos') + // + const queryCache2 = queryClient1.getQueryCache() + queryCache2.find('todos') + queryCache2.findAll('todos') + // --- Direct hook call. + useRenamedQueryClient().getQueryCache().find('todos') + useRenamedQueryClient().getQueryCache().findAll('todos') + // + const queryCache3 = useRenamedQueryClient().getQueryCache() + queryCache3.find('todos') + queryCache3.findAll('todos') + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/named-import.output.tsx b/codemods/v4/__testfixtures__/named-import.output.tsx new file mode 100644 index 0000000000..716f56be06 --- /dev/null +++ b/codemods/v4/__testfixtures__/named-import.output.tsx @@ -0,0 +1,98 @@ +import * as React from 'react' +import { + QueryCache as RenamedQueryCache, + QueryClient as RenamedQueryClient, + useInfiniteQuery as useRenamedInfiniteQuery, + useIsFetching as useRenamedIsFetching, + useIsMutating as useRenamedIsMutating, + useMutation as useRenamedMutation, + useQueries as useRenamedQueries, + useQuery as useRenamedQuery, + useQueryClient as useRenamedQueryClient, +} from 'react-query' + +export const Examples = () => { + useRenamedQuery(['todos']) + useRenamedInfiniteQuery(['todos']) + useRenamedMutation(['todos']) + useRenamedIsFetching(['todos']) + useRenamedIsMutating(['todos']) + useRenamedQueries({ + queries: [query1, query2] + }) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useRenamedQueryClient() + queryClient.getMutationDefaults(['todos']) + queryClient.getQueriesData(['todos']) + queryClient.getQueryData(['todos']) + queryClient.getQueryDefaults(['todos']) + queryClient.getQueryState(['todos']) + queryClient.isFetching(['todos']) + queryClient.setMutationDefaults(['todos'], { mutationFn: async () => null }) + queryClient.setQueriesData(['todos'], () => null) + queryClient.setQueryData(['todos'], () => null) + queryClient.setQueryDefaults(['todos'], { queryFn: async () => null }) + queryClient.cancelQueries(['todos']) + queryClient.fetchInfiniteQuery(['todos']) + queryClient.fetchQuery(['todos']) + queryClient.invalidateQueries(['todos']) + queryClient.prefetchInfiniteQuery(['todos']) + queryClient.prefetchQuery(['todos']) + queryClient.refetchQueries(['todos']) + queryClient.removeQueries(['todos']) + queryClient.resetQueries(['todos']) + // --- Direct hook call. + useRenamedQueryClient().getMutationDefaults(['todos']) + useRenamedQueryClient().getQueriesData(['todos']) + useRenamedQueryClient().getQueryData(['todos']) + useRenamedQueryClient().getQueryDefaults(['todos']) + useRenamedQueryClient().getQueryState(['todos']) + useRenamedQueryClient().isFetching(['todos']) + useRenamedQueryClient().setMutationDefaults(['todos'], { + mutationFn: async () => null, + }) + useRenamedQueryClient().setQueriesData(['todos'], () => null) + useRenamedQueryClient().setQueryData(['todos'], () => null) + useRenamedQueryClient().setQueryDefaults(['todos'], { + queryFn: async () => null, + }) + useRenamedQueryClient().cancelQueries(['todos']) + useRenamedQueryClient().fetchInfiniteQuery(['todos']) + useRenamedQueryClient().fetchQuery(['todos']) + useRenamedQueryClient().invalidateQueries(['todos']) + useRenamedQueryClient().prefetchInfiniteQuery(['todos']) + useRenamedQueryClient().prefetchQuery(['todos']) + useRenamedQueryClient().refetchQueries(['todos']) + useRenamedQueryClient().removeQueries(['todos']) + useRenamedQueryClient().resetQueries(['todos']) + // QueryCache + // --- NewExpression + const queryCache1 = new RenamedQueryCache({ + onError: (error) => console.log(error), + onSuccess: (success) => console.log(success) + }) + queryCache1.find(['todos']) + queryCache1.findAll(['todos']) + // --- Instantiated hook call. + const queryClient1 = useRenamedQueryClient() + queryClient1.getQueryCache().find(['todos']) + queryClient1.getQueryCache().findAll(['todos']) + // + const queryClient2 = new RenamedQueryClient({}) + queryClient2.getQueryCache().find(['todos']) + queryClient2.getQueryCache().findAll(['todos']) + // + const queryCache2 = queryClient1.getQueryCache() + queryCache2.find(['todos']) + queryCache2.findAll(['todos']) + // --- Direct hook call. + useRenamedQueryClient().getQueryCache().find(['todos']) + useRenamedQueryClient().getQueryCache().findAll(['todos']) + // + const queryCache3 = useRenamedQueryClient().getQueryCache() + queryCache3.find(['todos']) + queryCache3.findAll(['todos']) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/namespaced-import.input.tsx b/codemods/v4/__testfixtures__/namespaced-import.input.tsx new file mode 100644 index 0000000000..95d2b282ac --- /dev/null +++ b/codemods/v4/__testfixtures__/namespaced-import.input.tsx @@ -0,0 +1,86 @@ +import * as React from 'react' +import * as RQ from 'react-query' + +export const Examples = () => { + RQ.useQuery('todos') + RQ.useInfiniteQuery('todos') + RQ.useMutation('todos') + RQ.useIsFetching('todos') + RQ.useIsMutating('todos') + RQ.useQueries([query1, query2]) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = RQ.useQueryClient() + queryClient.getMutationDefaults('todos') + queryClient.getQueriesData('todos') + queryClient.getQueryData('todos') + queryClient.getQueryDefaults('todos') + queryClient.getQueryState('todos') + queryClient.isFetching('todos') + queryClient.setMutationDefaults('todos', { mutationFn: async () => null }) + queryClient.setQueriesData('todos', () => null) + queryClient.setQueryData('todos', () => null) + queryClient.setQueryDefaults('todos', { queryFn: async () => null }) + queryClient.cancelQueries('todos') + queryClient.fetchInfiniteQuery('todos') + queryClient.fetchQuery('todos') + queryClient.invalidateQueries('todos') + queryClient.prefetchInfiniteQuery('todos') + queryClient.prefetchQuery('todos') + queryClient.refetchQueries('todos') + queryClient.removeQueries('todos') + queryClient.resetQueries('todos') + // --- Direct hook call. + RQ.useQueryClient().getMutationDefaults('todos') + RQ.useQueryClient().getQueriesData('todos') + RQ.useQueryClient().getQueryData('todos') + RQ.useQueryClient().getQueryDefaults('todos') + RQ.useQueryClient().getQueryState('todos') + RQ.useQueryClient().isFetching('todos') + RQ.useQueryClient().setMutationDefaults('todos', { + mutationFn: async () => null, + }) + RQ.useQueryClient().setQueriesData('todos', () => null) + RQ.useQueryClient().setQueryData('todos', () => null) + RQ.useQueryClient().setQueryDefaults('todos', { + queryFn: async () => null, + }) + RQ.useQueryClient().cancelQueries('todos') + RQ.useQueryClient().fetchInfiniteQuery('todos') + RQ.useQueryClient().fetchQuery('todos') + RQ.useQueryClient().invalidateQueries('todos') + RQ.useQueryClient().prefetchInfiniteQuery('todos') + RQ.useQueryClient().prefetchQuery('todos') + RQ.useQueryClient().refetchQueries('todos') + RQ.useQueryClient().removeQueries('todos') + RQ.useQueryClient().resetQueries('todos') + // QueryCache + // --- NewExpression + const queryCache1 = new RQ.QueryCache({ + onError: (error) => console.log(error), + onSuccess: (success) => console.log(success) + }) + queryCache1.find('todos') + queryCache1.findAll('todos') + // --- Instantiated hook call. + const queryClient1 = RQ.useQueryClient() + queryClient1.getQueryCache().find('todos') + queryClient1.getQueryCache().findAll('todos') + // + const queryClient2 = new RQ.QueryClient({}) + queryClient2.getQueryCache().find('todos') + queryClient2.getQueryCache().findAll('todos') + // + const queryCache2 = queryClient1.getQueryCache() + queryCache2.find('todos') + queryCache2.findAll('todos') + // --- Direct hook call. + RQ.useQueryClient().getQueryCache().find('todos') + RQ.useQueryClient().getQueryCache().findAll('todos') + // + const queryCache3 = RQ.useQueryClient().getQueryCache() + queryCache3.find('todos') + queryCache3.findAll('todos') + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/namespaced-import.output.tsx b/codemods/v4/__testfixtures__/namespaced-import.output.tsx new file mode 100644 index 0000000000..b6b13f36cf --- /dev/null +++ b/codemods/v4/__testfixtures__/namespaced-import.output.tsx @@ -0,0 +1,88 @@ +import * as React from 'react' +import * as RQ from 'react-query' + +export const Examples = () => { + RQ.useQuery(['todos']) + RQ.useInfiniteQuery(['todos']) + RQ.useMutation(['todos']) + RQ.useIsFetching(['todos']) + RQ.useIsMutating(['todos']) + RQ.useQueries({ + queries: [query1, query2] + }) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = RQ.useQueryClient() + queryClient.getMutationDefaults(['todos']) + queryClient.getQueriesData(['todos']) + queryClient.getQueryData(['todos']) + queryClient.getQueryDefaults(['todos']) + queryClient.getQueryState(['todos']) + queryClient.isFetching(['todos']) + queryClient.setMutationDefaults(['todos'], { mutationFn: async () => null }) + queryClient.setQueriesData(['todos'], () => null) + queryClient.setQueryData(['todos'], () => null) + queryClient.setQueryDefaults(['todos'], { queryFn: async () => null }) + queryClient.cancelQueries(['todos']) + queryClient.fetchInfiniteQuery(['todos']) + queryClient.fetchQuery(['todos']) + queryClient.invalidateQueries(['todos']) + queryClient.prefetchInfiniteQuery(['todos']) + queryClient.prefetchQuery(['todos']) + queryClient.refetchQueries(['todos']) + queryClient.removeQueries(['todos']) + queryClient.resetQueries(['todos']) + // --- Direct hook call. + RQ.useQueryClient().getMutationDefaults(['todos']) + RQ.useQueryClient().getQueriesData(['todos']) + RQ.useQueryClient().getQueryData(['todos']) + RQ.useQueryClient().getQueryDefaults(['todos']) + RQ.useQueryClient().getQueryState(['todos']) + RQ.useQueryClient().isFetching(['todos']) + RQ.useQueryClient().setMutationDefaults(['todos'], { + mutationFn: async () => null, + }) + RQ.useQueryClient().setQueriesData(['todos'], () => null) + RQ.useQueryClient().setQueryData(['todos'], () => null) + RQ.useQueryClient().setQueryDefaults(['todos'], { + queryFn: async () => null, + }) + RQ.useQueryClient().cancelQueries(['todos']) + RQ.useQueryClient().fetchInfiniteQuery(['todos']) + RQ.useQueryClient().fetchQuery(['todos']) + RQ.useQueryClient().invalidateQueries(['todos']) + RQ.useQueryClient().prefetchInfiniteQuery(['todos']) + RQ.useQueryClient().prefetchQuery(['todos']) + RQ.useQueryClient().refetchQueries(['todos']) + RQ.useQueryClient().removeQueries(['todos']) + RQ.useQueryClient().resetQueries(['todos']) + // QueryCache + // --- NewExpression + const queryCache1 = new RQ.QueryCache({ + onError: (error) => console.log(error), + onSuccess: (success) => console.log(success) + }) + queryCache1.find(['todos']) + queryCache1.findAll(['todos']) + // --- Instantiated hook call. + const queryClient1 = RQ.useQueryClient() + queryClient1.getQueryCache().find(['todos']) + queryClient1.getQueryCache().findAll(['todos']) + // + const queryClient2 = new RQ.QueryClient({}) + queryClient2.getQueryCache().find(['todos']) + queryClient2.getQueryCache().findAll(['todos']) + // + const queryCache2 = queryClient1.getQueryCache() + queryCache2.find(['todos']) + queryCache2.findAll(['todos']) + // --- Direct hook call. + RQ.useQueryClient().getQueryCache().find(['todos']) + RQ.useQueryClient().getQueryCache().findAll(['todos']) + // + const queryCache3 = RQ.useQueryClient().getQueryCache() + queryCache3.find(['todos']) + queryCache3.findAll(['todos']) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/parameter-is-identifier.input.tsx b/codemods/v4/__testfixtures__/parameter-is-identifier.input.tsx new file mode 100644 index 0000000000..a6126bdcef --- /dev/null +++ b/codemods/v4/__testfixtures__/parameter-is-identifier.input.tsx @@ -0,0 +1,49 @@ +import * as React from 'react' +import { useMutation, useQuery, useQueryClient } from 'react-query' + +export const ExampleWithStringLiteralKey = () => { + const stringLiteralKey = 'todos' + useQuery(stringLiteralKey) + useMutation(stringLiteralKey) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries(stringLiteralKey) + // --- Direct hook call. + useQueryClient().cancelQueries(stringLiteralKey) + + return
Example Component
+} + +export const ExampleWithTemplateLiteral = () => { + const templateLiteralKey = `todos` + useQuery(templateLiteralKey) + useMutation(templateLiteralKey) +} + +export const ExampleWithArrayExpressionKey = () => { + const arrayExpressionKey = ['todos'] + useQuery(arrayExpressionKey) + useMutation(arrayExpressionKey) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries(queryKey2) + // --- Direct hook call. + useQueryClient().cancelQueries(queryKey2) + + return
Example Component
+} + +export const ExampleWithUnknownKey = () => { + useQuery(unknownQueryKey) + useMutation(unknownQueryKey) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries(unknownQueryKey) + // --- Direct hook call. + useQueryClient().cancelQueries(unknownQueryKey) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/parameter-is-identifier.output.tsx b/codemods/v4/__testfixtures__/parameter-is-identifier.output.tsx new file mode 100644 index 0000000000..94c2ca1e75 --- /dev/null +++ b/codemods/v4/__testfixtures__/parameter-is-identifier.output.tsx @@ -0,0 +1,49 @@ +import * as React from 'react' +import { useMutation, useQuery, useQueryClient } from 'react-query' + +export const ExampleWithStringLiteralKey = () => { + const stringLiteralKey = 'todos' + useQuery([stringLiteralKey]) + useMutation([stringLiteralKey]) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries([stringLiteralKey]) + // --- Direct hook call. + useQueryClient().cancelQueries([stringLiteralKey]) + + return
Example Component
+} + +export const ExampleWithTemplateLiteral = () => { + const templateLiteralKey = `todos` + useQuery([templateLiteralKey]) + useMutation([templateLiteralKey]) +} + +export const ExampleWithArrayExpressionKey = () => { + const arrayExpressionKey = ['todos'] + useQuery(arrayExpressionKey) + useMutation(arrayExpressionKey) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries(queryKey2) + // --- Direct hook call. + useQueryClient().cancelQueries(queryKey2) + + return
Example Component
+} + +export const ExampleWithUnknownKey = () => { + useQuery(unknownQueryKey) + useMutation(unknownQueryKey) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries(unknownQueryKey) + // --- Direct hook call. + useQueryClient().cancelQueries(unknownQueryKey) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/parameter-is-object-expression.input.tsx b/codemods/v4/__testfixtures__/parameter-is-object-expression.input.tsx new file mode 100644 index 0000000000..4319c965ec --- /dev/null +++ b/codemods/v4/__testfixtures__/parameter-is-object-expression.input.tsx @@ -0,0 +1,128 @@ +import * as React from 'react' +import { useMutation, useQuery, useQueryClient } from 'react-query' + +const options = {} + +export const ExampleWithStringLiteral = () => { + useQuery({ queryKey: 'todos', exact: true }, options) + useMutation({ mutationKey: 'todos', exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: 'todos', exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: 'todos', exact: true }, options) + + return
Example Component
+} + +export const ExampleWithStringLiteralIdentifier = () => { + const stringLiteralKey = 'todos' + useQuery({ queryKey: stringLiteralKey, exact: true }, options) + useMutation({ mutationKey: stringLiteralKey, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: stringLiteralKey, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: stringLiteralKey, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithTemplateLiteral = () => { + useQuery({ queryKey: `todos`, exact: true }, options) + useMutation({ mutationKey: `todos`, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: `todos`, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: `todos`, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithTemplateLiteralIdentifier = () => { + const templateLiteralKey = `todos` + useQuery({ queryKey: templateLiteralKey, exact: true }, options) + useMutation({ mutationKey: templateLiteralKey, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: templateLiteralKey, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: templateLiteralKey, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithArrayExpression = () => { + useQuery({ queryKey: ['todos'], exact: true }, options) + useMutation({ mutationKey: ['todos'], exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: ['todos'], exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: ['todos'], exact: true }, options) + + return
Example Component
+} + +export const ExampleWithArrayExpressionIdentifier = () => { + const arrayExpressionKey = ['todos'] + useQuery({ queryKey: arrayExpressionKey, exact: true }, options) + useMutation({ mutationKey: arrayExpressionKey, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: arrayExpressionKey, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: arrayExpressionKey, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithUnknownIdentifier1 = () => { + const createKey = (id) => ['todos', id] + const createdKey1 = createKey(1) + useQuery({ queryKey: createdKey1, exact: true }, options) + useMutation({ mutationKey: createdKey1, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: createdKey1, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: createdKey1, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithUnknownIdentifier2 = () => { + const createdKey2 = createKey() + useQuery({ queryKey: createdKey2, exact: true }, options) + useMutation({ mutationKey: createdKey2, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: createdKey2, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: createdKey2, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithUnknownIdentifier3 = () => { + useQuery({ queryKey: unknownQueryKey, exact: true }, options) + useMutation({ mutationKey: unknownQueryKey, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: unknownQueryKey, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: unknownQueryKey, exact: true }, options) + + return
Example Component
+} + diff --git a/codemods/v4/__testfixtures__/parameter-is-object-expression.output.tsx b/codemods/v4/__testfixtures__/parameter-is-object-expression.output.tsx new file mode 100644 index 0000000000..232a61e4c0 --- /dev/null +++ b/codemods/v4/__testfixtures__/parameter-is-object-expression.output.tsx @@ -0,0 +1,175 @@ +import * as React from 'react' +import { useMutation, useQuery, useQueryClient } from 'react-query' + +const options = {} + +export const ExampleWithStringLiteral = () => { + useQuery({ + queryKey: ['todos'], + exact: true + }, options) + useMutation({ + mutationKey: ['todos'], + exact: true + }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ + queryKey: ['todos'], + exact: true + }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ + queryKey: ['todos'], + exact: true + }, options) + + return
Example Component
+} + +export const ExampleWithStringLiteralIdentifier = () => { + const stringLiteralKey = 'todos' + useQuery({ + queryKey: [stringLiteralKey], + exact: true + }, options) + useMutation({ + mutationKey: [stringLiteralKey], + exact: true + }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ + queryKey: [stringLiteralKey], + exact: true + }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ + queryKey: [stringLiteralKey], + exact: true + }, options) + + return
Example Component
+} + +export const ExampleWithTemplateLiteral = () => { + useQuery({ + queryKey: [`todos`], + exact: true + }, options) + useMutation({ + mutationKey: [`todos`], + exact: true + }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ + queryKey: [`todos`], + exact: true + }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ + queryKey: [`todos`], + exact: true + }, options) + + return
Example Component
+} + +export const ExampleWithTemplateLiteralIdentifier = () => { + const templateLiteralKey = `todos` + useQuery({ + queryKey: [templateLiteralKey], + exact: true + }, options) + useMutation({ + mutationKey: [templateLiteralKey], + exact: true + }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ + queryKey: [templateLiteralKey], + exact: true + }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ + queryKey: [templateLiteralKey], + exact: true + }, options) + + return
Example Component
+} + +export const ExampleWithArrayExpression = () => { + useQuery({ queryKey: ['todos'], exact: true }, options) + useMutation({ mutationKey: ['todos'], exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: ['todos'], exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: ['todos'], exact: true }, options) + + return
Example Component
+} + +export const ExampleWithArrayExpressionIdentifier = () => { + const arrayExpressionKey = ['todos'] + useQuery({ queryKey: arrayExpressionKey, exact: true }, options) + useMutation({ mutationKey: arrayExpressionKey, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: arrayExpressionKey, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: arrayExpressionKey, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithUnknownIdentifier1 = () => { + const createKey = (id) => ['todos', id] + const createdKey1 = createKey(1) + useQuery({ queryKey: createdKey1, exact: true }, options) + useMutation({ mutationKey: createdKey1, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: createdKey1, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: createdKey1, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithUnknownIdentifier2 = () => { + const createdKey2 = createKey() + useQuery({ queryKey: createdKey2, exact: true }, options) + useMutation({ mutationKey: createdKey2, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: createdKey2, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: createdKey2, exact: true }, options) + + return
Example Component
+} + +export const ExampleWithUnknownIdentifier3 = () => { + useQuery({ queryKey: unknownQueryKey, exact: true }, options) + useMutation({ mutationKey: unknownQueryKey, exact: true }, options) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.cancelQueries({ queryKey: unknownQueryKey, exact: true }, options) + // --- Direct hook call. + useQueryClient().cancelQueries({ queryKey: unknownQueryKey, exact: true }, options) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/type-arguments.input.tsx b/codemods/v4/__testfixtures__/type-arguments.input.tsx new file mode 100644 index 0000000000..cb81fb81b9 --- /dev/null +++ b/codemods/v4/__testfixtures__/type-arguments.input.tsx @@ -0,0 +1,25 @@ +import * as React from 'react' +import { useQueries, useQuery, useQueryClient } from 'react-query' + +type Todos = { + items: readonly { + id: string + text: string + }[] + ts: number +} + +export const Examples = () => { + useQuery('todos') + useQueries>([query1, query2]) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.getQueriesData('todos') + queryClient.getQueriesData({ queryKey: 'todos' }) + // --- Direct hook call. + useQueryClient().getQueriesData('todos') + useQueryClient().getQueriesData({ queryKey: 'todos' }) + + return
Example Component
+} diff --git a/codemods/v4/__testfixtures__/type-arguments.output.tsx b/codemods/v4/__testfixtures__/type-arguments.output.tsx new file mode 100644 index 0000000000..619c7b95ac --- /dev/null +++ b/codemods/v4/__testfixtures__/type-arguments.output.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' +import { useQueries, useQuery, useQueryClient } from 'react-query' + +type Todos = { + items: readonly { + id: string + text: string + }[] + ts: number +} + +export const Examples = () => { + useQuery(['todos']) + useQueries>({ + queries: [query1, query2] + }) + // QueryClient methods + // --- Instantiated hook call. + const queryClient = useQueryClient() + queryClient.getQueriesData(['todos']) + queryClient.getQueriesData({ + queryKey: ['todos'] + }) + // --- Direct hook call. + useQueryClient().getQueriesData(['todos']) + useQueryClient().getQueriesData({ + queryKey: ['todos'] + }) + + return
Example Component
+} diff --git a/codemods/v4/__tests__/key-transformation.test.js b/codemods/v4/__tests__/key-transformation.test.js new file mode 100644 index 0000000000..b22431aec4 --- /dev/null +++ b/codemods/v4/__tests__/key-transformation.test.js @@ -0,0 +1,34 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const defineTest = require('jscodeshift/dist/testUtils').defineTest + +jest.autoMockOff() + +defineTest(__dirname, 'key-transformation', null, 'default-import', { + parser: 'tsx', +}) + +defineTest(__dirname, 'key-transformation', null, 'named-import', { + parser: 'tsx', +}) + +defineTest(__dirname, 'key-transformation', null, 'namespaced-import', { + parser: 'tsx', +}) + +defineTest(__dirname, 'key-transformation', null, 'parameter-is-identifier', { + parser: 'tsx', +}) + +defineTest( + __dirname, + 'key-transformation', + null, + 'parameter-is-object-expression', + { + parser: 'tsx', + } +) + +defineTest(__dirname, 'key-transformation', null, 'type-arguments', { + parser: 'tsx', +}) diff --git a/codemods/v4/key-transformation.js b/codemods/v4/key-transformation.js new file mode 100644 index 0000000000..8c6b1bc1b5 --- /dev/null +++ b/codemods/v4/key-transformation.js @@ -0,0 +1,138 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const createUtilsObject = require('./utils') +// eslint-disable-next-line @typescript-eslint/no-var-requires +const createKeyReplacer = require('./utils/replacers/key-replacer') +// eslint-disable-next-line @typescript-eslint/no-var-requires +const createUseQueryLikeTransformer = require('./utils/transformers/use-query-like-transformer') +// eslint-disable-next-line @typescript-eslint/no-var-requires +const createQueryClientTransformer = require('./utils/transformers/query-client-transformer') +// eslint-disable-next-line @typescript-eslint/no-var-requires +const createQueryCacheTransformer = require('./utils/transformers/query-cache-transformer') + +const transformQueryClientUsages = ({ jscodeshift, utils, root, filePath }) => { + const transformer = createQueryClientTransformer({ jscodeshift, utils, root }) + const replacer = createKeyReplacer({ jscodeshift, root, filePath }) + + transformer.execute( + [ + // Not object syntax-aware methods. + 'getMutationDefaults', + 'getQueriesData', + 'getQueryData', + 'getQueryDefaults', + 'getQueryState', + 'isFetching', + 'setMutationDefaults', + 'setQueriesData', + 'setQueryData', + 'setQueryDefaults', + // Object syntax-aware methods. + 'cancelQueries', + 'fetchInfiniteQuery', + 'fetchQuery', + 'invalidateQueries', + 'prefetchInfiniteQuery', + 'prefetchQuery', + 'refetchQueries', + 'removeQueries', + 'resetQueries', + ], + replacer + ) +} + +const transformUseQueriesUsages = ({ jscodeshift, utils, root }) => { + const transformer = createUseQueryLikeTransformer({ + jscodeshift, + utils, + root, + }) + const replacer = ({ node }) => { + /** + * When the node doesn't have the 'original' property, that means the codemod has been already applied, + * so we don't need to do any changes. + */ + if (!node.original) { + return node + } + + const newCallExpression = jscodeshift.callExpression(node.original.callee, [ + jscodeshift.objectExpression([ + jscodeshift.property( + 'init', + jscodeshift.identifier('queries'), + node.original.arguments[0] + ), + ]), + ]) + + // TODO: This should be part of one function! + if (node.typeParameters) { + newCallExpression.typeArguments = node.typeParameters + } + + return newCallExpression + } + + transformer.execute(['useQueries'], replacer) +} + +const transformUseQueryLikeUsages = ({ + jscodeshift, + utils, + root, + filePath, +}) => { + const transformer = createUseQueryLikeTransformer({ + jscodeshift, + utils, + root, + }) + + transformer.execute( + ['useQuery', 'useInfiniteQuery', 'useIsFetching', 'useIsMutating'], + createKeyReplacer({ + jscodeshift, + root, + filePath, + keyName: 'queryKey', + }) + ) + transformer.execute( + ['useMutation'], + createKeyReplacer({ + jscodeshift, + root, + filePath, + keyName: 'mutationKey', + }) + ) +} + +const transformQueryCacheUsages = ({ jscodeshift, utils, root, filePath }) => { + const transformer = createQueryCacheTransformer({ jscodeshift, utils, root }) + const replacer = createKeyReplacer({ jscodeshift, root, filePath }) + + transformer.execute(replacer) +} + +module.exports = (file, api) => { + const jscodeshift = api.jscodeshift + const root = jscodeshift(file.source) + + // TODO: Execute the transformers only when it contains a `react-query` import! + + const utils = createUtilsObject({ root, jscodeshift }) + const filePath = file.path + + // This function transforms usages like `useQuery` and `useMutation`. + transformUseQueryLikeUsages({ jscodeshift, utils, root, filePath }) + // This function transforms usages of `useQueries`. + transformUseQueriesUsages({ jscodeshift, utils, root }) + // This function transforms usages of `QueryClient`. + transformQueryClientUsages({ jscodeshift, utils, root, filePath }) + // This function transforms usages of `QueryCache`. + transformQueryCacheUsages({ jscodeshift, utils, root, filePath }) + + return root.toSource({ quote: 'single' }) +} diff --git a/codemods/v4/utils/index.js b/codemods/v4/utils/index.js new file mode 100644 index 0000000000..4706ade268 --- /dev/null +++ b/codemods/v4/utils/index.js @@ -0,0 +1,166 @@ +module.exports = ({ root, jscodeshift }) => { + const findImportIdentifierOf = (importSpecifiers, identifier) => { + const specifier = importSpecifiers + .filter(node => node.value.imported.name === identifier) + .paths() + + if (specifier.length > 0) { + return specifier[0].value.local + } + + return jscodeshift.identifier(identifier) + } + + const findImportSpecifiers = () => + root + .find(jscodeshift.ImportDeclaration, { + source: { + value: 'react-query', + }, + }) + .find(jscodeshift.ImportSpecifier, {}) + + const locateImports = identifiers => { + const findNamespaceImportIdentifier = () => { + const specifier = root + .find(jscodeshift.ImportDeclaration, { + source: { + value: 'react-query', + }, + }) + .find(jscodeshift.ImportNamespaceSpecifier) + .paths() + + return specifier.length > 0 ? specifier[0].value.local : null + } + + /** + * First, we search for the namespace import identifier because if we have any, we assume the consumer uses + * namespace imports. In this case, we won't search for named imports at all. + */ + const namespaceImportIdentifier = findNamespaceImportIdentifier() + + if (namespaceImportIdentifier) { + const identifierMap = {} + + for (const identifier of identifiers) { + identifierMap[identifier] = jscodeshift.identifier(identifier) + } + + return { + namespace: namespaceImportIdentifier, + ...identifierMap, + } + } + + const importSpecifiers = findImportSpecifiers() + const identifierMap = {} + + for (const identifier of identifiers) { + identifierMap[identifier] = findImportIdentifierOf( + importSpecifiers, + identifier + ) + } + + return { + namespace: null, + ...identifierMap, + } + } + + const findAllMethodCalls = () => + root + // First, we need to find all method calls. + .find(jscodeshift.CallExpression, { + callee: { + type: jscodeshift.MemberExpression.name, + property: { + type: jscodeshift.Identifier.name, + }, + }, + }) + + const findQueryClientIdentifiers = importIdentifiers => + root + .find(jscodeshift.VariableDeclarator, {}) + .filter(node => { + if (node.value.init) { + const initializer = node.value.init + + return ( + isClassInstantiationOf( + initializer, + getSelectorByImports(importIdentifiers, 'QueryClient') + ) || + isFunctionCallOf( + initializer, + getSelectorByImports(importIdentifiers, 'useQueryClient') + ) + ) + } + + return false + }) + .paths() + .map(node => node.value.id.name) + + const isCallExpression = node => + jscodeshift.match(node, { type: jscodeshift.CallExpression.name }) + + const isIdentifier = node => + jscodeshift.match(node, { type: jscodeshift.Identifier.name }) + + const isMemberExpression = node => + jscodeshift.match(node, { type: jscodeshift.MemberExpression.name }) + + const isNewExpression = node => + jscodeshift.match(node, { type: jscodeshift.NewExpression.name }) + + const isClassInstantiationOf = (node, selector) => { + if (!isNewExpression(node)) { + return false + } + + const parts = selector.split('.') + + return parts.length === 1 + ? isIdentifier(node.callee) && node.callee.name === parts[0] + : isMemberExpression(node.callee) && + node.callee.object.name === parts[0] && + node.callee.property.name === parts[1] + } + + const isFunctionCallOf = (node, selector) => { + if (!isCallExpression(node)) { + return false + } + + const parts = selector.split('.') + + return parts.length === 1 + ? isIdentifier(node.callee) && node.callee.name === parts[0] + : isMemberExpression(node.callee) && + node.callee.object.name === parts[0] && + node.callee.property.name === parts[1] + } + + const getSelectorByImports = (imports, path) => + imports.namespace + ? `${imports.namespace.name}.${imports[path].name}` + : imports[path].name + + return { + findAllMethodCalls, + getSelectorByImports, + isCallExpression, + isClassInstantiationOf, + isFunctionCallOf, + isIdentifier, + isMemberExpression, + locateImports, + queryClient: { + findQueryClientIdentifiers, + }, + } +} diff --git a/codemods/v4/utils/replacers/key-replacer.js b/codemods/v4/utils/replacers/key-replacer.js new file mode 100644 index 0000000000..621902edb5 --- /dev/null +++ b/codemods/v4/utils/replacers/key-replacer.js @@ -0,0 +1,160 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const UnprocessableKeyError = require('../unprocessable-key-error') + +module.exports = ({ jscodeshift, root, filePath, keyName = 'queryKey' }) => { + const isArrayExpression = node => + jscodeshift.match(node, { type: jscodeshift.ArrayExpression.name }) + + const isStringLiteral = node => + jscodeshift.match(node, { type: jscodeshift.StringLiteral.name }) || + jscodeshift.match(node, { type: jscodeshift.Literal.name }) + + const isTemplateLiteral = node => + jscodeshift.match(node, { type: jscodeshift.TemplateLiteral.name }) + + const findVariableDeclaration = node => { + const declarations = root + .find(jscodeshift.VariableDeclarator, { + id: { + type: jscodeshift.Identifier.name, + name: node.name, + }, + }) + .paths() + + return declarations.length > 0 ? declarations[0] : null + } + + const createKeyValue = node => { + // When the node is a string literal we convert it into an array of strings. + if (isStringLiteral(node)) { + return jscodeshift.arrayExpression([ + jscodeshift.stringLiteral(node.value), + ]) + } + + // When the node is a template literal we convert it into an array of template literals. + if (isTemplateLiteral(node)) { + return jscodeshift.arrayExpression([ + jscodeshift.templateLiteral(node.quasis, node.expressions), + ]) + } + + if (jscodeshift.match(node, { type: jscodeshift.Identifier.name })) { + // When the node is an identifier at first, we try to find its declaration, because we will try + // to guess its type. + const variableDeclaration = findVariableDeclaration(node) + + if (!variableDeclaration) { + throw new UnprocessableKeyError( + `In file ${filePath} at line ${node.loc.start.line} the type of identifier \`${node.name}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.` + ) + } + + const initializer = variableDeclaration.value.init + + // When it's a string, we just wrap it into an array expression. + if (isStringLiteral(initializer) || isTemplateLiteral(initializer)) { + return jscodeshift.arrayExpression([node]) + } + } + + throw new UnprocessableKeyError( + `In file ${filePath} at line ${node.loc.start.line} the type of the \`${keyName}\` couldn't be recognized, so the codemod couldn't be applied. Please migrate manually.` + ) + } + + const createKeyProperty = node => + jscodeshift.property( + 'init', + jscodeshift.identifier(keyName), + createKeyValue(node) + ) + + const getPropertyFromObjectExpression = (objectExpression, propertyName) => + objectExpression.properties.find( + property => property.key.name === propertyName + ) ?? null + + const buildWithTypeArguments = (node, builder) => { + const newNode = builder(node) + + if (node.typeParameters) { + newNode.typeArguments = node.typeParameters + } + + return newNode + } + + return ({ node }) => { + // When the node doesn't have the 'original' property, that means the codemod has been already applied, + // so we don't need to do any changes. + if (!node.original) { + return node + } + + const methodArguments = node.arguments + + // The method call doesn't have any arguments, we have nothing to do in this case. + if (methodArguments.length === 0) { + return node + } + + try { + const [firstArgument, ...restOfTheArguments] = methodArguments + + if ( + jscodeshift.match(firstArgument, { + type: jscodeshift.ObjectExpression.name, + }) + ) { + const originalKey = getPropertyFromObjectExpression( + firstArgument, + keyName + ) + + if (!originalKey) { + throw new UnprocessableKeyError( + `In file ${filePath} at line ${node.loc.start.line} the \`${keyName}\` couldn't be found. Did you forget to add it?` + ) + } + + const restOfTheProperties = firstArgument.properties.filter( + item => item.key.name !== keyName + ) + + return buildWithTypeArguments(node, originalNode => + jscodeshift.callExpression(originalNode.original.callee, [ + jscodeshift.objectExpression([ + createKeyProperty(originalKey.value), + ...restOfTheProperties, + ]), + ...restOfTheArguments, + ]) + ) + } + + // When the node is an array expression we just simply return it because we want query keys to be arrays. + if (isArrayExpression(firstArgument)) { + return node + } + + return buildWithTypeArguments(node, originalNode => + jscodeshift.callExpression(originalNode.original.callee, [ + createKeyValue(firstArgument), + ...restOfTheArguments, + ]) + ) + } catch (error) { + if (error.name === 'UnprocessableKeyError') { + if (process.env.NODE_ENV !== 'test') { + console.warn(error.message) + } + + return node + } + + throw error + } + } +} diff --git a/codemods/v4/utils/transformers/query-cache-transformer.js b/codemods/v4/utils/transformers/query-cache-transformer.js new file mode 100644 index 0000000000..acdea9550c --- /dev/null +++ b/codemods/v4/utils/transformers/query-cache-transformer.js @@ -0,0 +1,115 @@ +module.exports = ({ jscodeshift, utils, root }) => { + const isGetQueryCacheMethodCall = ( + initializer, + importIdentifiers, + knownQueryClientIds + ) => { + const isKnownQueryClient = node => + utils.isIdentifier(node) && knownQueryClientIds.includes(node.name) + + const isGetQueryCacheIdentifier = node => + utils.isIdentifier(node) && node.name === 'getQueryCache' + + const isValidInitializer = node => + utils.isCallExpression(node) && utils.isMemberExpression(node.callee) + + if (isValidInitializer(initializer)) { + const instance = initializer.callee.object + + return ( + isGetQueryCacheIdentifier(initializer.callee.property) && + (isKnownQueryClient(instance) || + utils.isFunctionCallOf( + instance, + utils.getSelectorByImports(importIdentifiers, 'useQueryClient') + )) + ) + } + + return false + } + + const findQueryCacheInstantiations = ( + importIdentifiers, + knownQueryClientIds + ) => + root.find(jscodeshift.VariableDeclarator, {}).filter(node => { + if (node.value.init) { + const initializer = node.value.init + + return ( + utils.isClassInstantiationOf( + initializer, + utils.getSelectorByImports(importIdentifiers, 'QueryCache') + ) || + isGetQueryCacheMethodCall( + initializer, + importIdentifiers, + knownQueryClientIds + ) + ) + } + + return false + }) + + const filterQueryCacheMethodCalls = node => + utils.isIdentifier(node) && ['find', 'findAll'].includes(node.name) + + const findQueryCacheMethodCalls = importIdentifiers => { + /** + * Here we collect all query client instantiations. We have to make aware of them because the query cache can be + * accessed by the query client as well. + */ + const queryClientIdentifiers = utils.queryClient.findQueryClientIdentifiers( + importIdentifiers + ) + /** + * Here we collect all query cache instantiations. The reason is simple: the methods can be called on query cache + * instances, to locate the possible usages we need to be aware of the identifier names. + */ + const queryCacheIdentifiers = findQueryCacheInstantiations( + importIdentifiers, + queryClientIdentifiers + ) + .paths() + .map(node => node.value.id.name) + + return ( + utils + // First, we need to find all method calls. + .findAllMethodCalls() + // Then we narrow the collection to all `fetch` and `fetchAll` methods. + .filter(node => filterQueryCacheMethodCalls(node.value.callee.property)) + .filter(node => { + const object = node.value.callee.object + + // If the method is called on a `QueryCache` instance, we keep it in the collection. + if (utils.isIdentifier(object)) { + return queryCacheIdentifiers.includes(object.name) + } + + // If the method is called on a `QueryClient` instance, we keep it in the collection. + if (utils.isCallExpression(object)) { + return isGetQueryCacheMethodCall( + object, + importIdentifiers, + queryClientIdentifiers + ) + } + + return false + }) + ) + } + + const execute = replacer => { + findQueryCacheMethodCalls( + utils.locateImports(['QueryCache', 'QueryClient', 'useQueryClient']) + ).replaceWith(replacer) + } + + return { + execute, + } +} diff --git a/codemods/v4/utils/transformers/query-client-transformer.js b/codemods/v4/utils/transformers/query-client-transformer.js new file mode 100644 index 0000000000..a1fd70f232 --- /dev/null +++ b/codemods/v4/utils/transformers/query-client-transformer.js @@ -0,0 +1,49 @@ +module.exports = ({ jscodeshift, utils, root }) => { + const filterQueryClientMethodCalls = (node, methods) => + utils.isIdentifier(node) && methods.includes(node.name) + + const findQueryClientMethodCalls = (importIdentifiers, methods) => { + /** + * Here we collect all query client instantiations. We have to make aware of them because some method calls might + * be invoked on these instances. + */ + const queryClientIdentifiers = utils.queryClient.findQueryClientIdentifiers( + importIdentifiers + ) + + return ( + utils + // First, we need to find all method calls. + .findAllMethodCalls() + // Then we narrow the collection to `QueryClient` methods. + .filter(node => + filterQueryClientMethodCalls(node.value.callee.property, methods) + ) + .filter(node => { + const object = node.value.callee.object + + // If the method is called on a `QueryClient` instance, we keep it in the collection. + if (utils.isIdentifier(object)) { + return queryClientIdentifiers.includes(object.name) + } + + // If the method is called on the return value of `useQueryClient` hook, we keep it in the collection. + return utils.isFunctionCallOf( + object, + utils.getSelectorByImports(importIdentifiers, 'useQueryClient') + ) + }) + ) + } + + const execute = (methods, replacer) => { + findQueryClientMethodCalls( + utils.locateImports(['QueryClient', 'useQueryClient']), + methods + ).replaceWith(replacer) + } + + return { + execute, + } +} diff --git a/codemods/v4/utils/transformers/use-query-like-transformer.js b/codemods/v4/utils/transformers/use-query-like-transformer.js new file mode 100644 index 0000000000..da3a6d527d --- /dev/null +++ b/codemods/v4/utils/transformers/use-query-like-transformer.js @@ -0,0 +1,32 @@ +module.exports = ({ jscodeshift, utils, root }) => { + const filterUseQueryLikeHookCalls = (node, importIdentifiers, hooks) => { + for (const hook of hooks) { + const selector = utils.getSelectorByImports(importIdentifiers, hook) + + if (utils.isFunctionCallOf(node, selector)) { + return true + } + } + + return false + } + + const findUseQueryLikeHookCalls = (importIdentifiers, hooks) => + root + // First, we need to find all call expressions. + .find(jscodeshift.CallExpression, {}) + // Then we narrow the collection to the `useQuery` like hook calls. + .filter(node => + filterUseQueryLikeHookCalls(node.value, importIdentifiers, hooks) + ) + + const execute = (hooks, replacer) => { + findUseQueryLikeHookCalls(utils.locateImports(hooks), hooks).replaceWith( + replacer + ) + } + + return { + execute, + } +} diff --git a/codemods/v4/utils/unprocessable-key-error.js b/codemods/v4/utils/unprocessable-key-error.js new file mode 100644 index 0000000000..87935c584b --- /dev/null +++ b/codemods/v4/utils/unprocessable-key-error.js @@ -0,0 +1,8 @@ +class UnprocessableKeyError extends Error { + constructor(message) { + super(message) + this.name = 'UnprocessableKeyError' + } +} + +module.exports = UnprocessableKeyError diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 6cb9773cf7..ac5cedc7ff 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -18,6 +18,26 @@ To streamline all apis, we've decided to make all keys Arrays only: + useQuery(['todos'], fetchTodos) ``` +#### Codemod + +To make this migration easier, we decided to deliver a codemod. + +> The codemod is a best efforts attempt to help you migrate the breaking change. Please review the generated code thoroughly! Also, there are edge cases that cannot be found by the code mod, so please keep an eye on the log output. + +You can easily apply it by using one (or both) of the following commands: + +If you want to run it against `.js` or `.jsx` files, please use the command below: + +`npx jscodeshift --extensions=js,jsx --transform=./node_modules/react-query/codemods/v4/key-transformation.js ./path/to/src/` + +If you want to run it against `.ts` or `.tsx` files, please use the command below: + +`npx jscodeshift --extensions=ts,tsx --parser=tsx --transform=./node_modules/react-query/codemods/v4/key-transformation.js ./path/to/src/` + +Please note in the case of `TypeScript` you need to use `tsx` as the parser otherwise, the codemod won't be applied properly! + +**Note:** Applying the codemod might break your code formatting, so please don't forget to run `prettier` and/or `eslint` after you've applied the codemod! + ### Separate hydration exports have been removed With version [3.22.0](https://github.com/tannerlinsley/react-query/releases/tag/v3.22.0), hydration utilities moved into the React Query core. With v3, you could still use the old exports from `react-query/hydration`, but these exports have been removed with v4. diff --git a/examples/auto-refetching/pages/index.js b/examples/auto-refetching/pages/index.js index d8a87e237f..22285b2cb3 100755 --- a/examples/auto-refetching/pages/index.js +++ b/examples/auto-refetching/pages/index.js @@ -28,7 +28,7 @@ function Example() { const [value, setValue] = React.useState('') const { status, data, error, isFetching } = useQuery( - 'todos', + ['todos'], async () => { const res = await axios.get('/api/data') return res.data @@ -40,11 +40,11 @@ function Example() { ) const addMutation = useMutation(value => fetch(`/api/data?add=${value}`), { - onSuccess: () => queryClient.invalidateQueries('todos'), + onSuccess: () => queryClient.invalidateQueries(['todos']), }) const clearMutation = useMutation(() => fetch(`/api/data?clear=1`), { - onSuccess: () => queryClient.invalidateQueries('todos'), + onSuccess: () => queryClient.invalidateQueries(['todos']), }) if (status === 'loading') return

Loading...

diff --git a/examples/basic-graphql-request/src/index.js b/examples/basic-graphql-request/src/index.js index bdf8581d64..e8724bfd47 100644 --- a/examples/basic-graphql-request/src/index.js +++ b/examples/basic-graphql-request/src/index.js @@ -40,7 +40,7 @@ function App() { } function usePosts() { - return useQuery("posts", async () => { + return useQuery(['posts'], async () => { const { posts: { data }, } = await request( diff --git a/examples/custom-hooks/src/hooks/usePosts.js b/examples/custom-hooks/src/hooks/usePosts.js index cf49ca98ab..4fb688149b 100755 --- a/examples/custom-hooks/src/hooks/usePosts.js +++ b/examples/custom-hooks/src/hooks/usePosts.js @@ -9,5 +9,5 @@ const getPosts = async () => { }; export default function usePosts() { - return useQuery("posts", getPosts); + return useQuery(['posts'], getPosts); } diff --git a/examples/default-query-function/src/index.js b/examples/default-query-function/src/index.js index 3a5a5c406a..51836d9698 100644 --- a/examples/default-query-function/src/index.js +++ b/examples/default-query-function/src/index.js @@ -56,7 +56,7 @@ function Posts({ setPostId }) { const queryClient = useQueryClient(); // All you have to do now is pass a key! - const { status, data, error, isFetching } = useQuery("/posts"); + const { status, data, error, isFetching } = useQuery(['/posts']); return (
@@ -100,7 +100,7 @@ function Posts({ setPostId }) { function Post({ postId, setPostId }) { // You can even leave out the queryFn and just go straight into options - const { status, data, error, isFetching } = useQuery(`/posts/${postId}`, { + const { status, data, error, isFetching } = useQuery([`/posts/${postId}`], { enabled: !!postId, }); diff --git a/examples/focus-refetching/pages/index.js b/examples/focus-refetching/pages/index.js index 7e17c034d0..816a0259ab 100755 --- a/examples/focus-refetching/pages/index.js +++ b/examples/focus-refetching/pages/index.js @@ -23,17 +23,17 @@ export default function App() { function Example() { const queryClient = useQueryClient() - const { status, data, error } = useQuery('user', async () => { + const { status, data, error } = useQuery(['user'], async () => { const res = await axios.get('/api/user') return res.data }) const logoutMutation = useMutation(logout, { - onSuccess: () => queryClient.invalidateQueries('user'), + onSuccess: () => queryClient.invalidateQueries(['user']), }) const loginMutation = useMutation(login, { - onSuccess: () => queryClient.invalidateQueries('user'), + onSuccess: () => queryClient.invalidateQueries(['user']), }) return ( diff --git a/examples/load-more-infinite-scroll/pages/index.js b/examples/load-more-infinite-scroll/pages/index.js index d63d83fe59..c870597ddb 100755 --- a/examples/load-more-infinite-scroll/pages/index.js +++ b/examples/load-more-infinite-scroll/pages/index.js @@ -32,7 +32,7 @@ function Example() { hasNextPage, hasPreviousPage, } = useInfiniteQuery( - 'projects', + ['projects'], async ({ pageParam = 0 }) => { const res = await axios.get('/api/projects?cursor=' + pageParam) return res.data diff --git a/examples/optimistic-updates-typescript/pages/index.tsx b/examples/optimistic-updates-typescript/pages/index.tsx index f4988b34eb..5113d51520 100755 --- a/examples/optimistic-updates-typescript/pages/index.tsx +++ b/examples/optimistic-updates-typescript/pages/index.tsx @@ -29,7 +29,7 @@ async function fetchTodos(): Promise { function useTodos( options?: UseQueryOptions ) { - return useQuery('todos', fetchTodos, options) + return useQuery(['todos'], fetchTodos, options) } function TodoCounter() { @@ -59,14 +59,14 @@ function Example() { onMutate: async (newTodo: string) => { setText('') // Cancel any outgoing refetches (so they don't overwrite our optimistic update) - await queryClient.cancelQueries('todos') + await queryClient.cancelQueries(['todos']) // Snapshot the previous value - const previousTodos = queryClient.getQueryData('todos') + const previousTodos = queryClient.getQueryData(['todos']) // Optimistically update to the new value if (previousTodos) { - queryClient.setQueryData('todos', { + queryClient.setQueryData(['todos'], { ...previousTodos, items: [ ...previousTodos.items, @@ -80,12 +80,12 @@ function Example() { // If the mutation fails, use the context returned from onMutate to roll back onError: (err, variables, context) => { if (context?.previousTodos) { - queryClient.setQueryData('todos', context.previousTodos) + queryClient.setQueryData(['todos'], context.previousTodos) } }, // Always refetch after error or success: onSettled: () => { - queryClient.invalidateQueries('todos') + queryClient.invalidateQueries(['todos']) }, } ) diff --git a/examples/optimistic-updates/pages/index.js b/examples/optimistic-updates/pages/index.js index c720be41d9..06dad6e0d8 100755 --- a/examples/optimistic-updates/pages/index.js +++ b/examples/optimistic-updates/pages/index.js @@ -24,7 +24,7 @@ function Example() { const queryClient = useQueryClient() const [text, setText] = React.useState('') - const { status, data, error, isFetching } = useQuery('todos', async () => { + const { status, data, error, isFetching } = useQuery(['todos'], async () => { const res = await axios.get('/api/data') return res.data }) @@ -37,11 +37,11 @@ function Example() { // an error onMutate: async text => { setText('') - await queryClient.cancelQueries('todos') + await queryClient.cancelQueries(['todos']) - const previousValue = queryClient.getQueryData('todos') + const previousValue = queryClient.getQueryData(['todos']) - queryClient.setQueryData('todos', old => ({ + queryClient.setQueryData(['todos'], old => ({ ...old, items: [...old.items, text], })) @@ -50,10 +50,10 @@ function Example() { }, // On failure, roll back to the previous value onError: (err, variables, previousValue) => - queryClient.setQueryData('todos', previousValue), + queryClient.setQueryData(['todos'], previousValue), // After success or failure, refetch the todos query onSettled: () => { - queryClient.invalidateQueries('todos') + queryClient.invalidateQueries(['todos']) }, } ) diff --git a/examples/playground/src/index.js b/examples/playground/src/index.js index 886d24c182..8ae322a4f2 100644 --- a/examples/playground/src/index.js +++ b/examples/playground/src/index.js @@ -136,7 +136,7 @@ function App() { return (
-
@@ -254,7 +254,7 @@ function EditTodo({ editingIndex, setEditingIndex }) { const saveMutation = useMutation(patchTodo, { onSuccess: (data) => { // Update `todos` and the individual todo queries when this mutation succeeds - queryClient.invalidateQueries("todos"); + queryClient.invalidateQueries(['todos']); queryClient.setQueryData(["todo", { id: editingIndex }], data); }, }); @@ -340,7 +340,7 @@ function AddTodo() { const addMutation = useMutation(postTodo, { onSuccess: () => { - queryClient.invalidateQueries("todos"); + queryClient.invalidateQueries(['todos']); }, }); diff --git a/examples/prefetching/pages/index.js b/examples/prefetching/pages/index.js index cd4d3a4af6..6bfe7f0e64 100755 --- a/examples/prefetching/pages/index.js +++ b/examples/prefetching/pages/index.js @@ -37,7 +37,7 @@ function Example() { const rerender = React.useState(0)[1] const [selectedChar, setSelectedChar] = React.useState(1) - const charactersQuery = useQuery('characters', getCharacters) + const charactersQuery = useQuery(['characters'], getCharacters) const characterQuery = useQuery(['character', selectedChar], () => getCharacter(selectedChar) diff --git a/examples/rick-morty/src/Character.js b/examples/rick-morty/src/Character.js index a75bcd6e06..5c7a66a0d3 100644 --- a/examples/rick-morty/src/Character.js +++ b/examples/rick-morty/src/Character.js @@ -17,7 +17,7 @@ import fetch from "./fetch"; function Character() { const { characterId } = useParams(); - const { status, data } = useQuery(`character-${characterId}`, () => + const { status, data } = useQuery([`character-${characterId}`], () => fetch(`https://rickandmortyapi.com/api/character/${characterId}`) ); @@ -77,7 +77,7 @@ function Character() { } function Episode({ id }) { - const { data, status } = useQuery(`episode-${id}`, () => + const { data, status } = useQuery([`episode-${id}`], () => fetch(`https://rickandmortyapi.com/api/episode/${id}`) ); @@ -97,7 +97,7 @@ function Episode({ id }) { } function Location({ id }) { - const { data, status } = useQuery(`location-${id}`, () => + const { data, status } = useQuery([`location-${id}`], () => fetch(`https://rickandmortyapi.com/api/location/${id}`) ); diff --git a/examples/rick-morty/src/Characters.js b/examples/rick-morty/src/Characters.js index 39706a93af..0fca9f84fc 100644 --- a/examples/rick-morty/src/Characters.js +++ b/examples/rick-morty/src/Characters.js @@ -5,7 +5,7 @@ import { useQuery } from "react-query"; import fetch from "./fetch"; export default function Characters() { - const { status, data } = useQuery("characters", () => + const { status, data } = useQuery(["characters"], () => fetch("https://rickandmortyapi.com/api/character/") ); diff --git a/examples/rick-morty/src/Episode.js b/examples/rick-morty/src/Episode.js index 47872f0189..6fad12ccb2 100644 --- a/examples/rick-morty/src/Episode.js +++ b/examples/rick-morty/src/Episode.js @@ -7,7 +7,7 @@ import fetch from "./fetch"; function Episode() { const { episodeId } = useParams(); - const { data, status } = useQuery(`episode-${episodeId}`, () => + const { data, status } = useQuery([`episode-${episodeId}`], () => fetch(`https://rickandmortyapi.com/api/episode/${episodeId}`) ); @@ -30,7 +30,7 @@ function Episode() { } function Character({ id }) { - const { data, status } = useQuery(`character-${id}`, () => + const { data, status } = useQuery([`character-${id}`], () => fetch(`https://rickandmortyapi.com/api/character/${id}`) ); diff --git a/examples/rick-morty/src/Episodes.js b/examples/rick-morty/src/Episodes.js index 88cf6b0552..cc228a0eee 100644 --- a/examples/rick-morty/src/Episodes.js +++ b/examples/rick-morty/src/Episodes.js @@ -5,7 +5,7 @@ import { useQuery } from "react-query"; import fetch from "./fetch"; export default function Episodes() { - const { data, status } = useQuery("episodes", () => + const { data, status } = useQuery(["episodes"], () => fetch("https://rickandmortyapi.com/api/episode") ); diff --git a/examples/simple/src/index.js b/examples/simple/src/index.js index a20a52a4ac..1d63f87aeb 100644 --- a/examples/simple/src/index.js +++ b/examples/simple/src/index.js @@ -15,7 +15,7 @@ export default function App() { } function Example() { - const { isLoading, error, data, isFetching } = useQuery("repoData", () => + const { isLoading, error, data, isFetching } = useQuery(["repoData"], () => fetch( "https://api.github.com/repos/tannerlinsley/react-query" ).then((res) => res.json()) diff --git a/examples/star-wars/src/Character.js b/examples/star-wars/src/Character.js index 6de9faf173..ab04544926 100644 --- a/examples/star-wars/src/Character.js +++ b/examples/star-wars/src/Character.js @@ -17,7 +17,7 @@ import fetch from "./fetch"; function Character(props) { const characterId = props.match.params.characterId; - const { status, error, data } = useQuery(`character-${characterId}`, () => + const { status, error, data } = useQuery([`character-${characterId}`], () => fetch(`https://swapi.dev/api/people/${characterId}/`) ); @@ -85,7 +85,7 @@ function Character(props) { function Film(props) { const { id } = props; - const { data, status, error } = useQuery(`film-${id}`, () => + const { data, status, error } = useQuery([`film-${id}`], () => fetch(`https://swapi.dev/api/films/${id}/`) ); @@ -105,7 +105,7 @@ function Film(props) { function Homeworld(props) { const { id } = props; - const { data, status } = useQuery(`homeworld-${id}`, () => + const { data, status } = useQuery([`homeworld-${id}`], () => fetch(`https://swapi.dev/api/planets/${id}/`) ); diff --git a/examples/star-wars/src/Characters.js b/examples/star-wars/src/Characters.js index a93eaaef23..a350dad2a9 100644 --- a/examples/star-wars/src/Characters.js +++ b/examples/star-wars/src/Characters.js @@ -5,7 +5,7 @@ import { useQuery } from "react-query"; import fetch from "./fetch"; export default function Characters(props) { - const { status, error, data } = useQuery("characters", () => + const { status, error, data } = useQuery(["characters"], () => fetch(`https://swapi.dev/api/people/`) ); diff --git a/examples/star-wars/src/Film.js b/examples/star-wars/src/Film.js index 82155934ec..7d730550f9 100644 --- a/examples/star-wars/src/Film.js +++ b/examples/star-wars/src/Film.js @@ -7,7 +7,7 @@ import fetch from "./fetch"; function Film(props) { const filmId = props.match.params.filmId; - const { data, status, error } = useQuery(`film-${filmId}`, () => + const { data, status, error } = useQuery([`film-${filmId}`], () => fetch(`https://swapi.dev/api/films/${filmId}/`) ); @@ -35,7 +35,7 @@ function Film(props) { function Character(props) { const { id } = props; - const { data, status, error } = useQuery(`character-${id}`, () => + const { data, status, error } = useQuery([`character-${id}`], () => fetch(`https://swapi.dev/api/people/${props.id}/`) ); diff --git a/examples/star-wars/src/Films.js b/examples/star-wars/src/Films.js index 1f254b83f1..5fc0e5f2d0 100644 --- a/examples/star-wars/src/Films.js +++ b/examples/star-wars/src/Films.js @@ -5,7 +5,7 @@ import { useQuery } from "react-query"; import fetch from "./fetch"; export default function Films(props) { - const { data, status, error } = useQuery("films", () => + const { data, status, error } = useQuery(["films"], () => fetch("https://swapi.dev/api/films/") ); diff --git a/examples/suspense/src/components/Projects.js b/examples/suspense/src/components/Projects.js index 2d449c71a3..f95f316a54 100644 --- a/examples/suspense/src/components/Projects.js +++ b/examples/suspense/src/components/Projects.js @@ -8,7 +8,7 @@ import { fetchProjects, fetchProject } from "../queries"; export default function Projects({ setActiveProject }) { const queryClient = useQueryClient(); - const { data, isFetching } = useQuery("projects", fetchProjects); + const { data, isFetching } = useQuery(['projects'], fetchProjects); return (
diff --git a/examples/suspense/src/index.js b/examples/suspense/src/index.js index c25576061d..da1f194823 100755 --- a/examples/suspense/src/index.js +++ b/examples/suspense/src/index.js @@ -46,7 +46,7 @@ function Example() { onClick={() => { setShowProjects((old) => { if (!old) { - queryClient.prefetchQuery("projects", fetchProjects); + queryClient.prefetchQuery(["projects"], fetchProjects); } return !old; }); diff --git a/package.json b/package.json index eefa599c04..326031572d 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,14 @@ ], "scripts": { "test": "is-ci \"test:ci\" \"test:dev\"", - "test:dev": "npm run test:types && npm run test:format && npm run test:eslint && jest --watch", - "test:ci": "npm run test:types && npm run test:format && npm run test:eslint && jest", + "test:dev": "npm run test:types && npm run test:format && npm run test:eslint && npm run test:codemod && jest --watch", + "test:ci": "npm run test:types && npm run test:format && npm run test:eslint && npm run test:codemod && jest", "test:coverage": "yarn test:ci; open coverage/lcov-report/index.html", "test:format": "yarn prettier --check", "test:types": "tsc", "test:eslint": "eslint --ext .ts,.tsx ./src", "test:size": "yarn build:umd && bundlewatch", + "test:codemod": "jest --config codemods/jest.config.js", "build": "yarn build:commonjs && yarn build:es && yarn build:umd && yarn build:types", "build:commonjs": "rimraf ./lib && cross-env BABEL_ENV=commonjs babel --extensions .ts,.tsx --ignore ./src/**/tests/**/* ./src --out-dir lib", "build:es": "rimraf ./es && babel --extensions .ts,.tsx --ignore ./src/**/tests/**/* ./src --out-dir es", @@ -62,7 +63,11 @@ "lib", "reactjs", "scripts", - "types" + "types", + "codemods", + "!codemods/jest.config.js", + "!codemods/**/__testfixtures__", + "!codemods/**/__tests__" ], "dependencies": { "@babel/runtime": "^7.5.5", @@ -89,9 +94,10 @@ "@babel/preset-typescript": "^7.10.4", "@rollup/plugin-replace": "^3.0.0", "@svgr/rollup": "^6.1.1", - "@testing-library/react": "^10.4.7", "@testing-library/jest-dom": "^5.14.1", + "@testing-library/react": "^10.4.7", "@types/jest": "^26.0.4", + "@types/jscodeshift": "^0.11.3", "@types/react": "^16.9.41", "@types/react-dom": "^16.9.8", "@typescript-eslint/eslint-plugin": "^5.6.0", @@ -117,6 +123,7 @@ "eslint-plugin-standard": "^4.0.1", "is-ci-cli": "^2.1.1", "jest": "^26.0.1", + "jscodeshift": "^0.13.1", "prettier": "2.2.1", "react": "^16.13.0", "react-dom": "^16.13.1", diff --git a/yarn.lock b/yarn.lock index 433e909f92..404b8357c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,6 +46,13 @@ dependencies: "@babel/highlight" "^7.16.0" +"@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + "@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" @@ -102,6 +109,27 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.13.16": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf" + integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.7" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + "@babel/core@^7.15.5": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" @@ -182,6 +210,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.16.7", "@babel/generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" + integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== + dependencies: + "@babel/types" "^7.16.8" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.6.3": version "7.6.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" @@ -223,6 +260,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" @@ -277,6 +321,16 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== + dependencies: + "@babel/compat-data" "^7.16.4" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" @@ -301,6 +355,19 @@ "@babel/helper-replace-supers" "^7.16.0" "@babel/helper-split-export-declaration" "^7.16.0" +"@babel/helper-create-class-features-plugin@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.7.tgz#9c5b34b53a01f2097daf10678d65135c1b9f84ba" + integrity sha512-kIFozAvVfK05DM4EVQYKK+zteWvY85BFdGBRQBytRyY3y+6PX0DkDOn/CZ3lEuczCfrCxEzwt0YtP/87YPTWSw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-create-regexp-features-plugin@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz#1b8feeab1594cbcfbf3ab5a3bbcabac0468efdbd" @@ -350,6 +417,13 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-explode-assignable-expression@^7.10.4": version "7.11.4" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b" @@ -400,6 +474,15 @@ "@babel/template" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== + dependencies: + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helper-function-name@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e" @@ -437,6 +520,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-get-function-arity@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz#cb46348d2f8808e632f0ab048172130e636005f0" @@ -458,6 +548,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-member-expression-to-functions@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz#432967fd7e12a4afef66c4687d4ca22bc0456f15" @@ -479,6 +576,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-module-imports@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" @@ -507,6 +611,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-module-transforms@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz#24e2f08ee6832c60b157bb0936c86bef7210c622" @@ -547,6 +658,20 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-module-transforms@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" + integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helper-optimise-call-expression@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz#b4a1f2561870ce1247ceddb02a3860fa96d72543" @@ -568,6 +693,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-plugin-utils@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" @@ -588,6 +720,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== +"@babel/helper-plugin-utils@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + "@babel/helper-plugin-utils@^7.8.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" @@ -656,6 +793,17 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helper-simple-access@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz#08fb7e22ace9eb8326f7e3920a1c2052f13d851e" @@ -679,6 +827,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-simple-access@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" @@ -721,6 +876,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-split-export-declaration@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" @@ -750,11 +912,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + "@babel/helper-wrap-function@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" @@ -802,6 +974,15 @@ "@babel/traverse" "^7.16.3" "@babel/types" "^7.16.0" +"@babel/helpers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" + integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/helpers@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" @@ -847,6 +1028,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" + integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.7.4.tgz#75ab2d7110c2cf2fa949959afb05fa346d2231bb" @@ -867,6 +1057,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.13.16", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17" + integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw== + "@babel/parser@^7.16.0", "@babel/parser@^7.16.3": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" @@ -919,6 +1114,14 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-class-properties@^7.13.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-proposal-class-properties@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.0.tgz#c029618267ddebc7280fa286e0f8ca2a278a2d1a" @@ -1008,6 +1211,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" + integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.0.tgz#44e1cce08fe2427482cf446a91bb451528ed0596" @@ -1077,6 +1288,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" +"@babel/plugin-proposal-optional-chaining@^7.13.12": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" + integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-optional-chaining@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.0.tgz#56dbc3970825683608e9efb55ea82c2a2d6c8dc0" @@ -1192,6 +1412,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-flow@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -1297,6 +1524,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-typescript@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-arrow-functions@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" @@ -1466,6 +1700,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-flow-strip-types@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-flow" "^7.16.7" + "@babel/plugin-transform-for-of@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" @@ -1552,6 +1794,16 @@ "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.13.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" + integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + dependencies: + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.0.tgz#add58e638c8ddc4875bd9a9ecb5c594613f6c922" @@ -1883,6 +2135,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-typescript" "^7.10.4" +"@babel/plugin-transform-typescript@^7.16.7": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" + integrity sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-typescript" "^7.16.7" + "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -2067,6 +2328,15 @@ core-js-compat "^3.19.1" semver "^6.3.0" +"@babel/preset-flow@^7.13.13": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" + integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-flow-strip-types" "^7.16.7" + "@babel/preset-modules@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" @@ -2122,6 +2392,26 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-transform-typescript" "^7.10.4" +"@babel/preset-typescript@^7.13.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" + +"@babel/register@^7.13.16": + version "7.16.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.16.9.tgz#fcfb23cfdd9ad95c9771e58183de83b513857806" + integrity sha512-jJ72wcghdRIlENfvALcyODhNoGE5j75cYHdC+aQMh6cU/P86tiiXTp9XYZct1UxUMo/4+BgQRyNZEGx0KWGS+g== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + "@babel/runtime-corejs3@^7.10.2": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz#931ed6941d3954924a7aa967ee440e60c507b91a" @@ -2230,6 +2520,15 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + "@babel/template@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b" @@ -2314,6 +2613,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.16.7": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.8.tgz#bab2f2b09a5fe8a8d9cad22cbfe3ba1d126fef9c" + integrity sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.16.8" + "@babel/types" "^7.16.8" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" @@ -2373,6 +2688,14 @@ "@babel/helper-validator-identifier" "^7.15.7" to-fast-properties "^2.0.0" +"@babel/types@^7.16.7", "@babel/types@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@babel/types@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" @@ -2895,6 +3218,14 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" +"@types/jscodeshift@^0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@types/jscodeshift/-/jscodeshift-0.11.3.tgz#8dcab24ced39dcab1c8ff3461b3d171aafee3d48" + integrity sha512-pM0JD9kWVDH9DQp5Y6td16924V3MwZHei8P3cTeuFhXpzpk0K+iWraBZz8wF61QkFs9fZeAQNX0q8SG0+TFm2w== + dependencies: + ast-types "^0.14.1" + recast "^0.20.3" + "@types/json-schema@^7.0.9": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -3260,6 +3591,13 @@ ast-types-flow@0.0.7, ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= +ast-types@0.14.2, ast-types@^0.14.1: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -3312,6 +3650,11 @@ axobject-query@^2.0.2: dependencies: ast-types-flow "0.0.7" +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + babel-eslint@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -3657,6 +4000,14 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -3741,6 +4092,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -3810,6 +4170,11 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -4576,7 +4941,7 @@ espree@^7.1.0: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.2.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -4835,6 +5200,15 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -4842,6 +5216,13 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -4864,6 +5245,11 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== +flow-parser@0.*: + version "0.169.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.169.0.tgz#4f77d61dd4241f8063f734ef1f71c684ec03ab52" + integrity sha512-X1DFb6wxXpZLLqM9NX0Wm+4xoN6xAyJn8OwuiHsV0JJvLfD18Z+wbgJ1lM7ykTVINdu8v7Mu0gIzWMvnhKWBkA== + follow-redirects@1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" @@ -6048,6 +6434,31 @@ jscc@^1.1.1: perf-regexes "^1.0.1" skip-regex "^1.0.2" +jscodeshift@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.13.1.tgz#69bfe51e54c831296380585c6d9e733512aecdef" + integrity sha512-lGyiEbGOvmMRKgWk4vf+lUrCWO/8YR8sUR3FKF1Cq5fovjZDlIcw3Hu5ppLHAnEXshVffvaM0eyuY/AbOeYpnQ== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" + babel-core "^7.0.0-bridge.0" + chalk "^4.1.2" + flow-parser "0.*" + graceful-fs "^4.2.4" + micromatch "^3.1.10" + neo-async "^2.5.0" + node-dir "^0.1.17" + recast "^0.20.4" + temp "^0.8.4" + write-file-atomic "^2.3.0" + jsdom@^16.2.2: version "16.2.2" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.2.2.tgz#76f2f7541646beb46a938f5dc476b88705bedf2b" @@ -6239,6 +6650,14 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -6326,7 +6745,7 @@ magic-string@^0.25.2: dependencies: sourcemap-codec "^1.4.4" -make-dir@^2.1.0: +make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -6437,7 +6856,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -6528,11 +6947,23 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +neo-async@^2.5.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-dir@^0.1.17: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + dependencies: + minimatch "^3.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -6777,7 +7208,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.2.0: +p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -6791,6 +7222,13 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -6919,6 +7357,11 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pirates@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.4.tgz#07df81e61028e402735cdd49db701e4885b4e6e6" + integrity sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw== + pirates@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" @@ -6933,6 +7376,13 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -7170,6 +7620,16 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +recast@^0.20.3, recast@^0.20.4: + version "0.20.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.5.tgz#8e2c6c96827a1b339c634dd232957d230553ceae" + integrity sha512-E5qICoPoNL4yU0H0NoBDntNB0Q5oMSNh9usFctYniLBluTthi3RsQVBXIJNbApOlvSwW/RGxIuokPcAc59J5fQ== + dependencies: + ast-types "0.14.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -7427,7 +7887,7 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2.6.3: +rimraf@2.6.3, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -7667,6 +8127,13 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -7815,18 +8282,18 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@^0.5.6: - version "0.5.16" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== +source-map-support@^0.5.16, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +source-map-support@^0.5.6: + version "0.5.16" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -8117,6 +8584,13 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +temp@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== + dependencies: + rimraf "~2.6.2" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -8251,6 +8725,11 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== +tslib@^2.0.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -8573,6 +9052,15 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write-file-atomic@^2.3.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + write-file-atomic@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" From fe98d533cc62fab8ab88166a31707d6cfc02d058 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Thu, 24 Feb 2022 21:40:39 +0100 Subject: [PATCH 25/76] feat: remove idle state (#3302) * feat: remove idle state in favor of status: loading & fetchStatus: idle * feat: remove idle state remove isIdle boolean * feat: remove idle state documentation around the removed idle state and the new fetchingStatus * feat: remove idle state add missing ' to docs --- docs/src/pages/guides/dependent-queries.md | 24 ++++++- docs/src/pages/guides/disabling-queries.md | 65 +++++++++++++----- docs/src/pages/guides/network-mode.md | 4 +- docs/src/pages/guides/queries.md | 26 ++++++- docs/src/pages/reference/useQuery.md | 7 +- src/core/query.ts | 2 +- src/core/queryObserver.ts | 3 +- src/core/tests/queriesObserver.test.tsx | 56 ++++++++-------- src/core/tests/query.test.tsx | 6 +- src/core/tests/queryClient.test.tsx | 9 ++- src/core/types.ts | 41 +----------- .../tests/QueryResetErrorBoundary.test.tsx | 10 ++- src/reactjs/tests/useInfiniteQuery.test.tsx | 2 - src/reactjs/tests/useQuery.test.tsx | 67 ++++++++++--------- src/reactjs/useBaseQuery.ts | 2 +- 15 files changed, 180 insertions(+), 144 deletions(-) diff --git a/docs/src/pages/guides/dependent-queries.md b/docs/src/pages/guides/dependent-queries.md index bf4535fb16..4ad77b7000 100644 --- a/docs/src/pages/guides/dependent-queries.md +++ b/docs/src/pages/guides/dependent-queries.md @@ -12,7 +12,7 @@ const { data: user } = useQuery(['user', email], getUserByEmail) const userId = user?.id // Then get the user's projects -const { isIdle, data: projects } = useQuery( +const { status, fetchStatus, data: projects } = useQuery( ['projects', userId], getProjectsByUser, { @@ -20,7 +20,25 @@ const { isIdle, data: projects } = useQuery( enabled: !!userId, } ) +``` + +The `projects` query will start in: + +```js +status: 'loading' +fetchStatus: 'idle' +``` + +As soon as the `user` is available, the `projects` query will be `enabled` and will then transition to: -// isIdle will be `true` until `enabled` is true and the query begins to fetch. -// It will then go to the `isLoading` stage and hopefully the `isSuccess` stage :) +```js +status: 'loading' +fetchStatus: 'fetching' +``` + +Once we have the projects, it will go to: + +```js +status: 'success' +fetchStatus: 'idle' ``` diff --git a/docs/src/pages/guides/disabling-queries.md b/docs/src/pages/guides/disabling-queries.md index ec28592be2..b1badf5ed3 100644 --- a/docs/src/pages/guides/disabling-queries.md +++ b/docs/src/pages/guides/disabling-queries.md @@ -10,47 +10,80 @@ When `enabled` is `false`: - If the query has cached data - The query will be initialized in the `status === 'success'` or `isSuccess` state. - If the query does not have cached data - - The query will start in the `status === 'idle'` or `isIdle` state. + - The query will start in the `status === 'loading'` and `fetchStatus === 'idle'` - The query will not automatically fetch on mount. -- The query will not automatically refetch in the background when new instances mount or new instances appearing +- The query will not automatically refetch in the background - The query will ignore query client `invalidateQueries` and `refetchQueries` calls that would normally result in the query refetching. -- `refetch` can be used to manually trigger the query to fetch. +- `refetch` returned from `useQuery` can be used to manually trigger the query to fetch. -```js +```jsx function Todos() { const { - isIdle, isLoading, isError, data, error, refetch, - isFetching, + isFetching } = useQuery(['todos'], fetchTodoList, { enabled: false, }) return ( - <> +
- {isIdle ? ( - 'Not ready...' - ) : isLoading ? ( - Loading... - ) : isError ? ( - Error: {error.message} - ) : ( + {data ? ( <>
    {data.map(todo => (
  • {todo.title}
  • ))}
-
{isFetching ? 'Fetching...' : null}
+ ) : ( + isError ? ( + Error: {error.message} + ) : ( + (isLoading && !isFetching) ? ( + Not ready ... + ) : ( + Loading... + ) + ) )} - + +
{isFetching ? 'Fetching...' : null}
+
+ ) +} +``` + +Permanently disabling a query opts out of many great features that react-query has to offer (like background refetches), and it's also not the idiomatic way. It takes you from the declartive approach (defining dependencies when your query should run) into an imperative mode (fetch whenever I click here). It is also not possible to pass parameters to `refetch`. Oftentimes, all you want is a lazy query that defers the initial fetch: + +## Lazy Queries + +The enabled option can not only be used to permenantly disable a query, but also to enable / disable it at a later time. A good example would be a filter form where you only want to fire off the first request once the user has entered a filter value: + +```jsx +function Todos() { + const [filter, setFilter] = React.useState('') + + const { data } = useQuery( + ['todos', filter], + () => fetchTodos(filter), + { + // ⬇️ disabled as long as the filter is empty + enabled: !!filter + } + ) + + return ( +
+ // 🚀 applying the filter will enable and execute the query + + {data && +
) } ``` diff --git a/docs/src/pages/guides/network-mode.md b/docs/src/pages/guides/network-mode.md index 3a977ff2d2..64f6434141 100644 --- a/docs/src/pages/guides/network-mode.md +++ b/docs/src/pages/guides/network-mode.md @@ -9,7 +9,7 @@ Since React Query is most often used for data fetching in combination with data ## Network Mode: online -In this mode, Queries and Mutations will not fire unless you have network connection. This is the default mode. If a fetch is initiated for a query, it will always stay in the `state` (`loading`, `idle`, `error`, `success`) it is in if the fetch cannot be made because there is no network connection. However, a `fetchStatus` is exposed additionally. This can be either: +In this mode, Queries and Mutations will not fire unless you have network connection. This is the default mode. If a fetch is initiated for a query, it will always stay in the `state` (`loading`, `error`, `success`) it is in if the fetch cannot be made because there is no network connection. However, a [fetchStatus](./queries#fetchstatus) is exposed additionally. This can be either: - `fetching`: The `queryFn` is really executing - a request is in-flight. - `paused`: The query is not executing - it is `paused` until you have connection again @@ -41,6 +41,6 @@ The [React Query Devtools](../devtools) will show Queries in a `paused` state if ## Signature -- `networkMode: 'online' | 'always' | 'offlineFirst` +- `networkMode: 'online' | 'always' | 'offlineFirst'` - optional - defaults to `'online'` diff --git a/docs/src/pages/guides/queries.md b/docs/src/pages/guides/queries.md index af83df3ba3..da3518e8df 100644 --- a/docs/src/pages/guides/queries.md +++ b/docs/src/pages/guides/queries.md @@ -32,16 +32,14 @@ const result = useQuery(['todos'], fetchTodoList) The `result` object contains a few very important states you'll need to be aware of to be productive. A query can only be in one of the following states at any given moment: -- `isLoading` or `status === 'loading'` - The query has no data and is currently fetching +- `isLoading` or `status === 'loading'` - The query has no data yet - `isError` or `status === 'error'` - The query encountered an error - `isSuccess` or `status === 'success'` - The query was successful and data is available -- `isIdle` or `status === 'idle'` - The query is currently disabled (you'll learn more about this in a bit) Beyond those primary states, more information is available depending on the state of the query: - `error` - If the query is in an `isError` state, the error is available via the `error` property. - `data` - If the query is in a `success` state, the data is available via the `data` property. -- `isFetching` - In any state, if the query is fetching at any time (including background refetching) `isFetching` will be `true`. For **most** queries, it's usually sufficient to check for the `isLoading` state, then the `isError` state, then finally, assume that the data is available and render the successful state: @@ -92,6 +90,28 @@ function Todos() { ) } ``` + +TypeScript will also narrow the type of `data` correctly if you've checked for `loading` and `error` before accessing it. + +### FetchStatus + +In addition to the `status` field, the `result` object, you will also get an additional `fetchStatus`property with the following options: + +- `fetchStatus === 'fetching'` - The query is currently fetching. +- `fetchStatus === 'paused'` - The query wanted to fetch, but it is paused. Read more about this in the [Network Mode](./network-mode) guide. +- `fetchStatus === 'idle'` - The query is not doing anything at the moment. + +### Why two different states? + +Background refetches and stale-while-revalidate logic make all combinations for `status` and `fetchStatus` possible. For example: +- a query in `success` status will usually be in `idle` fetchStatus, but it could also be in `fetching` if a background refetch is happening. +- a query that mounts and has no data will usually be in `loading` status and `fetching` fetchStatus, but it could also be `paused` if there is no network connection. + +So keep in mind that a query can be in `loading` state without actually fetching data. As a rule of thumb: + +- The `status` gives information about the `data`: Do we have any or not? +- The `fetchStatus` gives information about the `queryFn`: Is it running or not? + ## Further Reading For an alternative way of performing status checks, have a look at the [Community Resources](../community/tkdodos-blog#4-status-checks-in-react-query). diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index 7d76b442fe..8040e1a159 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -190,12 +190,9 @@ const result = useQuery({ - `status: String` - Will be: - - `idle` if the query is idle. This only happens if a query is initialized with `enabled: false` and no initial data is available. - `loading` if the query is in a "hard" loading state. This means there is no cached data and the query is currently fetching, eg `isFetching === true` - `error` if the query attempt resulted in an error. The corresponding `error` property has the error received from the attempted fetch - `success` if the query has received a response with no errors and is ready to display its data. The corresponding `data` property on the query is the data received from the successful fetch or if the query's `enabled` property is set to `false` and has not been fetched yet `data` is the first `initialData` supplied to the query on initialization. -- `isIdle: boolean` - - A derived boolean from the `status` variable above, provided for convenience. - `isLoading: boolean` - A derived boolean from the `status` variable above, provided for convenience. - `isSuccess: boolean` @@ -229,8 +226,8 @@ const result = useQuery({ - This property can be used to not show any previously cached data. - `fetchStatus: FetchStatus` - `fetching`: Is `true` whenever the queryFn is executing, which includes initial `loading` as well as background refetches. - - `paused`: The query wanted to fetch, but has been `paused` - - `idle`: The query is not fetching + - `paused`: The query wanted to fetch, but has been `paused`. + - `idle`: The query is not fetching. - see [Network Mode](../guides/network-mode) for more information. - `isFetching: boolean` - A derived boolean from the `fetchStatus` variable above, provided for convenience. diff --git a/src/core/query.ts b/src/core/query.ts index 8832272818..0389d45270 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -618,7 +618,7 @@ function getDefaultState< fetchFailureCount: 0, fetchMeta: null, isInvalidated: false, - status: hasData ? 'success' : 'idle', + status: hasData ? 'success' : 'loading', fetchStatus: 'idle', } } diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 7cf4c28965..704c9cb887 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -489,7 +489,7 @@ export class QueryObserver< if ( typeof options.placeholderData !== 'undefined' && typeof data === 'undefined' && - (status === 'loading' || status === 'idle') + status === 'loading' ) { let placeholderData @@ -541,7 +541,6 @@ export class QueryObserver< isLoading: status === 'loading', isSuccess: status === 'success', isError: status === 'error', - isIdle: status === 'idle', data, dataUpdatedAt, error, diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx index 8345a2a63c..e8cd4ca550 100644 --- a/src/core/tests/queriesObserver.test.tsx +++ b/src/core/tests/queriesObserver.test.tsx @@ -88,20 +88,20 @@ describe('queriesObserver', () => { unsubscribe() expect(results.length).toBe(6) expect(results[0]).toMatchObject([ - { status: 'idle', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[1]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[2]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[3]).toMatchObject([ { status: 'success', data: 1 }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[4]).toMatchObject([ { status: 'success', data: 1 }, @@ -138,20 +138,20 @@ describe('queriesObserver', () => { expect(queryCache.find(key2, { type: 'active' })).toBeUndefined() expect(results.length).toBe(6) expect(results[0]).toMatchObject([ - { status: 'idle', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[1]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[2]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[3]).toMatchObject([ { status: 'success', data: 1 }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[4]).toMatchObject([ { status: 'success', data: 1 }, @@ -183,20 +183,20 @@ describe('queriesObserver', () => { unsubscribe() expect(results.length).toBe(6) expect(results[0]).toMatchObject([ - { status: 'idle', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[1]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[2]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[3]).toMatchObject([ { status: 'success', data: 1 }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[4]).toMatchObject([ { status: 'success', data: 1 }, @@ -231,20 +231,20 @@ describe('queriesObserver', () => { unsubscribe() expect(results.length).toBe(5) expect(results[0]).toMatchObject([ - { status: 'idle', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[1]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'idle', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'idle', data: undefined }, ]) expect(results[2]).toMatchObject([ - { status: 'loading', data: undefined }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[3]).toMatchObject([ { status: 'success', data: 1 }, - { status: 'loading', data: undefined }, + { status: 'loading', fetchStatus: 'fetching', data: undefined }, ]) expect(results[4]).toMatchObject([ { status: 'success', data: 1 }, diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index 966aeb32e3..e60f2b6222 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -264,12 +264,14 @@ describe('query', () => { if (typeof AbortSignal === 'function') { expect(query.state).toMatchObject({ data: undefined, - status: 'idle', + status: 'loading', + fetchStatus: 'idle', }) } else { expect(query.state).toMatchObject({ data: 'data', status: 'success', + fetchStatus: 'idle', dataUpdateCount: 1, }) } @@ -390,7 +392,7 @@ describe('query', () => { // The query should expect(queryFn).toHaveBeenCalledTimes(1) // have been called, expect(query.state.error).toBe(null) // not have an error, and - expect(query.state.status).toBe('idle') // not be loading any longer + expect(query.state.fetchStatus).toBe('idle') // not be loading any longer }) test('should be able to refetch a cancelled query', async () => { diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index 1ed9de02ef..aca0a77264 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -119,7 +119,8 @@ describe('queryClient', () => { const observer = new QueryObserver(queryClient, { queryKey: [key], }) - expect(observer.getCurrentResult().status).toBe('idle') + expect(observer.getCurrentResult().status).toBe('loading') + expect(observer.getCurrentResult().fetchStatus).toBe('idle') }) test('should update existing query defaults', async () => { @@ -835,7 +836,8 @@ describe('queryClient', () => { }) expect(state3).toMatchObject({ data: undefined, - status: 'idle', + status: 'loading', + fetchStatus: 'idle', }) consoleMock.mockRestore() }) @@ -1273,7 +1275,8 @@ describe('queryClient', () => { expect(state).toBeTruthy() expect(state?.data).toBeUndefined() - expect(state?.status).toEqual('idle') + expect(state?.status).toEqual('loading') + expect(state?.fetchStatus).toEqual('idle') }) test('should reset query data to initial data if set', async () => { diff --git a/src/core/types.ts b/src/core/types.ts index c1f60c0e8c..f630423d3d 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -313,7 +313,7 @@ export interface FetchPreviousPageOptions extends ResultOptions { pageParam?: unknown } -export type QueryStatus = 'idle' | 'loading' | 'error' | 'success' +export type QueryStatus = 'loading' | 'error' | 'success' export type FetchStatus = 'fetching' | 'paused' | 'idle' export interface QueryObserverBaseResult { @@ -326,7 +326,6 @@ export interface QueryObserverBaseResult { isFetched: boolean isFetchedAfterMount: boolean isFetching: boolean - isIdle: boolean isLoading: boolean isLoadingError: boolean isPaused: boolean @@ -344,25 +343,11 @@ export interface QueryObserverBaseResult { fetchStatus: FetchStatus } -export interface QueryObserverIdleResult - extends QueryObserverBaseResult { - data: undefined - error: null - isError: false - isIdle: true - isLoading: false - isLoadingError: false - isRefetchError: false - isSuccess: false - status: 'idle' -} - export interface QueryObserverLoadingResult extends QueryObserverBaseResult { data: undefined error: null isError: false - isIdle: false isLoading: true isLoadingError: false isRefetchError: false @@ -377,7 +362,6 @@ export interface QueryObserverLoadingErrorResult< data: undefined error: TError isError: true - isIdle: false isLoading: false isLoadingError: true isRefetchError: false @@ -392,7 +376,6 @@ export interface QueryObserverRefetchErrorResult< data: TData error: TError isError: true - isIdle: false isLoading: false isLoadingError: false isRefetchError: true @@ -405,7 +388,6 @@ export interface QueryObserverSuccessResult data: TData error: null isError: false - isIdle: false isLoading: false isLoadingError: false isRefetchError: false @@ -414,7 +396,6 @@ export interface QueryObserverSuccessResult } export type QueryObserverResult = - | QueryObserverIdleResult | QueryObserverLoadingErrorResult | QueryObserverLoadingResult | QueryObserverRefetchErrorResult @@ -436,21 +417,6 @@ export interface InfiniteQueryObserverBaseResult< isFetchingPreviousPage: boolean } -export interface InfiniteQueryObserverIdleResult< - TData = unknown, - TError = unknown -> extends InfiniteQueryObserverBaseResult { - data: undefined - error: null - isError: false - isIdle: true - isLoading: false - isLoadingError: false - isRefetchError: false - isSuccess: false - status: 'idle' -} - export interface InfiniteQueryObserverLoadingResult< TData = unknown, TError = unknown @@ -458,7 +424,6 @@ export interface InfiniteQueryObserverLoadingResult< data: undefined error: null isError: false - isIdle: false isLoading: true isLoadingError: false isRefetchError: false @@ -473,7 +438,6 @@ export interface InfiniteQueryObserverLoadingErrorResult< data: undefined error: TError isError: true - isIdle: false isLoading: false isLoadingError: true isRefetchError: false @@ -488,7 +452,6 @@ export interface InfiniteQueryObserverRefetchErrorResult< data: InfiniteData error: TError isError: true - isIdle: false isLoading: false isLoadingError: false isRefetchError: true @@ -503,7 +466,6 @@ export interface InfiniteQueryObserverSuccessResult< data: InfiniteData error: null isError: false - isIdle: false isLoading: false isLoadingError: false isRefetchError: false @@ -512,7 +474,6 @@ export interface InfiniteQueryObserverSuccessResult< } export type InfiniteQueryObserverResult = - | InfiniteQueryObserverIdleResult | InfiniteQueryObserverLoadingErrorResult | InfiniteQueryObserverLoadingResult | InfiniteQueryObserverRefetchErrorResult diff --git a/src/reactjs/tests/QueryResetErrorBoundary.test.tsx b/src/reactjs/tests/QueryResetErrorBoundary.test.tsx index 5e304c6f99..65d0c10299 100644 --- a/src/reactjs/tests/QueryResetErrorBoundary.test.tsx +++ b/src/reactjs/tests/QueryResetErrorBoundary.test.tsx @@ -210,7 +210,7 @@ describe('QueryErrorResetBoundary', () => { const consoleMock = mockConsoleError() function Page() { - const { data, refetch, status } = useQuery( + const { data, refetch, status, fetchStatus } = useQuery( key, async () => { throw new Error('Error') @@ -225,7 +225,9 @@ describe('QueryErrorResetBoundary', () => { return (
-
status: {status}
+
+ status: {status}, fetchStatus: {fetchStatus} +
{data}
) @@ -256,7 +258,9 @@ describe('QueryErrorResetBoundary', () => { ) - await waitFor(() => rendered.getByText('status: idle')) + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: idle') + ) rendered.getByRole('button', { name: /refetch/i }).click() await waitFor(() => rendered.getByText('error boundary')) diff --git a/src/reactjs/tests/useInfiniteQuery.test.tsx b/src/reactjs/tests/useInfiniteQuery.test.tsx index 1e45200f0e..a9480415a6 100644 --- a/src/reactjs/tests/useInfiniteQuery.test.tsx +++ b/src/reactjs/tests/useInfiniteQuery.test.tsx @@ -84,7 +84,6 @@ describe('useInfiniteQuery', () => { isPaused: false, isFetchingNextPage: false, isFetchingPreviousPage: false, - isIdle: false, isLoading: true, isLoadingError: false, isPlaceholderData: false, @@ -116,7 +115,6 @@ describe('useInfiniteQuery', () => { isPaused: false, isFetchingNextPage: false, isFetchingPreviousPage: false, - isIdle: false, isLoading: false, isLoadingError: false, isPlaceholderData: false, diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index c01af84af3..5e99ae409b 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -141,12 +141,6 @@ describe('useQuery', () => { states.push(state) - if (state.isIdle) { - expectType(state.data) - expectType(state.error) - return idle - } - if (state.isLoading) { expectType(state.data) expectType(state.error) @@ -179,7 +173,6 @@ describe('useQuery', () => { isFetchedAfterMount: false, isFetching: true, isPaused: false, - isIdle: false, isLoading: true, isLoadingError: false, isPlaceholderData: false, @@ -205,7 +198,6 @@ describe('useQuery', () => { isFetchedAfterMount: true, isFetching: false, isPaused: false, - isIdle: false, isLoading: false, isLoadingError: false, isPlaceholderData: false, @@ -262,7 +254,6 @@ describe('useQuery', () => { isFetchedAfterMount: false, isFetching: true, isPaused: false, - isIdle: false, isLoading: true, isLoadingError: false, isPlaceholderData: false, @@ -288,7 +279,6 @@ describe('useQuery', () => { isFetchedAfterMount: false, isFetching: true, isPaused: false, - isIdle: false, isLoading: true, isLoadingError: false, isPlaceholderData: false, @@ -314,7 +304,6 @@ describe('useQuery', () => { isFetchedAfterMount: true, isFetching: false, isPaused: false, - isIdle: false, isLoading: false, isLoadingError: true, isPlaceholderData: false, @@ -2344,7 +2333,7 @@ describe('useQuery', () => { }) // See https://github.com/tannerlinsley/react-query/issues/170 - it('should start with status idle if enabled is false', async () => { + it('should start with status loading, fetchStatus idle if enabled is false', async () => { const key1 = queryKey() const key2 = queryKey() @@ -2356,8 +2345,12 @@ describe('useQuery', () => { return (
-
First Status: {first.status}
-
Second Status: {second.status}
+
+ First Status: {first.status}, {first.fetchStatus} +
+
+ Second Status: {second.status}, {second.fetchStatus} +
) } @@ -2366,9 +2359,9 @@ describe('useQuery', () => { // use "act" to wait for state update and prevent console warning - rendered.getByText('First Status: idle') - await waitFor(() => rendered.getByText('Second Status: loading')) - await waitFor(() => rendered.getByText('Second Status: success')) + rendered.getByText('First Status: loading, idle') + await waitFor(() => rendered.getByText('Second Status: loading, fetching')) + await waitFor(() => rendered.getByText('Second Status: success, idle')) }) // See https://github.com/tannerlinsley/react-query/issues/144 @@ -3489,7 +3482,7 @@ describe('useQuery', () => { return (
-
Status: {query.status}
+
FetchStatus: {query.fetchStatus}

Data: {query.data || 'no data'}

{query.isStale ? ( @@ -3500,14 +3493,14 @@ describe('useQuery', () => { const rendered = renderWithClient(queryClient, ) - rendered.getByText('Status: idle') + rendered.getByText('FetchStatus: idle') rendered.getByText('Data: no data') fireEvent.click(rendered.getByText('fetch')) - await waitFor(() => rendered.getByText('Status: loading')) + await waitFor(() => rendered.getByText('FetchStatus: fetching')) await waitFor(() => [ - rendered.getByText('Status: success'), + rendered.getByText('FetchStatus: idle'), rendered.getByText('Data: data'), ]) }) @@ -3589,23 +3582,23 @@ describe('useQuery', () => { queryFn.mockImplementation(() => 'data') function Page() { - const { status } = useQuery({ + const { fetchStatus } = useQuery({ queryKey: key, queryFn, enabled: false, }) - return
status: {status}
+ return
fetchStatus: {fetchStatus}
} const rendered = renderWithClient(queryClient, ) expect(queryFn).not.toHaveBeenCalled() expect(queryCache.find(key)).not.toBeUndefined() - rendered.getByText('status: idle') + rendered.getByText('fetchStatus: idle') }) // See https://github.com/tannerlinsley/react-query/issues/360 - test('should init to status:idle when enabled is falsey', async () => { + test('should init to status:loading, fetchStatus:idle when enabled is false', async () => { const key = queryKey() function Page() { @@ -3615,14 +3608,16 @@ describe('useQuery', () => { return (
-
status: {query.status}
+
+ status: {query.status}, {query.fetchStatus} +
) } const rendered = renderWithClient(queryClient, ) - await waitFor(() => rendered.getByText('status: idle')) + await waitFor(() => rendered.getByText('status: loading, idle')) }) test('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => { @@ -4271,7 +4266,8 @@ describe('useQuery', () => { if (typeof AbortSignal === 'function') { expect(queryCache.find([key, 1])?.state).toMatchObject({ data: undefined, - status: 'idle', + status: 'loading', + fetchStatus: 'idle', }) } else { expect(queryCache.find([key, 1])?.state).toMatchObject({ @@ -4290,7 +4286,8 @@ describe('useQuery', () => { if (typeof AbortSignal === 'function') { expect(queryCache.find([key, 3])?.state).toMatchObject({ data: undefined, - status: 'idle', + status: 'loading', + fetchStatus: 'idle', }) } else { expect(queryCache.find([key, 3])?.state).toMatchObject({ @@ -4452,7 +4449,7 @@ describe('useQuery', () => { expect(states.length).toBe(4) expect(states[0]).toMatchObject({ data: undefined, - isLoading: false, + isLoading: true, isFetching: false, isSuccess: false, isStale: true, @@ -4473,7 +4470,7 @@ describe('useQuery', () => { }) expect(states[3]).toMatchObject({ data: undefined, - isLoading: false, + isLoading: true, isFetching: false, isSuccess: false, isStale: true, @@ -5264,7 +5261,9 @@ describe('useQuery', () => { rendered.getByRole('button', { name: /cancel/i }).click() - await waitFor(() => rendered.getByText('status: idle, fetchStatus: idle')) + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: idle') + ) expect(count).toBe(0) @@ -5273,7 +5272,9 @@ describe('useQuery', () => { await sleep(15) - await waitFor(() => rendered.getByText('status: idle, fetchStatus: idle')) + await waitFor(() => + rendered.getByText('status: loading, fetchStatus: idle') + ) expect(count).toBe(0) diff --git a/src/reactjs/useBaseQuery.ts b/src/reactjs/useBaseQuery.ts index e89db9c3bf..9a11ce89af 100644 --- a/src/reactjs/useBaseQuery.ts +++ b/src/reactjs/useBaseQuery.ts @@ -108,7 +108,7 @@ export function useBaseQuery< }, [defaultedOptions, observer]) // Handle suspense - if (defaultedOptions.suspense && result.isLoading) { + if (defaultedOptions.suspense && result.isLoading && result.isFetching) { throw observer .fetchOptimistic(defaultedOptions) .then(({ data }) => { From 315f0cf4698640a77f1910d2e478893d3e41a258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C4=B1l=20Anar?= Date: Sat, 26 Feb 2022 15:35:28 +0100 Subject: [PATCH 26/76] feat(*): add optional logger prop to QueryClient config (#3246) - remove setLogger - add optional `logger` prop to QueryClientConfig - add getLogger public method to QueryClient - add optional `logger` prop to QueryConfig and MutationConfig - add getDefaultLogger function which returns a default logger based on environment, which is used by everything that takes an optional logger in their config - add createQueryClient test util function that uses a mock logger - replace all `new QueryClient` calls with createQueryClient calls - remove mockConsoleError and usages from tests, which are not necessary anymore BREAKING CHANGE: remove setLogger --- docs/src/manifests/manifest.json | 10 +-- docs/src/pages/guides/custom-logger.md | 22 +++++ .../guides/migrating-to-react-query-4.md | 15 +++- docs/src/pages/guides/testing.md | 20 +++-- docs/src/pages/reference/QueryClient.md | 14 +++- docs/src/pages/reference/setLogger.md | 43 ---------- jest.config.js | 1 + package.json | 4 +- src/core/index.ts | 1 - src/core/logger.native.ts | 11 +++ src/core/logger.ts | 14 +--- src/core/mutation.ts | 7 +- src/core/mutationCache.ts | 1 + src/core/query.ts | 22 ++--- src/core/queryCache.ts | 1 + src/core/queryClient.ts | 32 +++++--- src/core/queryObserver.ts | 5 +- src/core/tests/hydration.test.tsx | 50 +++++------ src/core/tests/infiniteQueryBehavior.test.tsx | 6 +- src/core/tests/infiniteQueryObserver.test.tsx | 4 +- src/core/tests/mutationCache.test.tsx | 39 ++++----- src/core/tests/mutationObserver.test.tsx | 4 +- src/core/tests/mutations.test.tsx | 12 +-- src/core/tests/queriesObserver.test.tsx | 22 ++--- src/core/tests/query.test.tsx | 21 ++--- src/core/tests/queryCache.test.tsx | 12 +-- src/core/tests/queryClient.test.tsx | 61 ++++---------- src/core/tests/queryObserver.test.tsx | 22 ++--- src/core/tests/utils.test.tsx | 34 +------- src/core/types.ts | 2 + src/persistQueryClient/index.ts | 13 +-- src/reactjs/index.ts | 1 - src/reactjs/logger.native.ts | 7 -- src/reactjs/logger.ts | 3 - src/reactjs/setLogger.ts | 4 - src/reactjs/tests/Hydrate.test.tsx | 19 ++--- .../tests/QueryClientProvider.test.tsx | 14 ++-- .../tests/QueryResetErrorBoundary.test.tsx | 42 +--------- src/reactjs/tests/logger.native.test.tsx | 9 -- src/reactjs/tests/ssr-hydration.test.tsx | 45 +++++----- src/reactjs/tests/ssr.test.tsx | 13 ++- src/reactjs/tests/suspense.test.tsx | 39 +-------- src/reactjs/tests/useInfiniteQuery.test.tsx | 7 +- src/reactjs/tests/useIsFetching.test.tsx | 17 ++-- src/reactjs/tests/useIsMutating.test.tsx | 16 ++-- src/reactjs/tests/useMutation.test.tsx | 36 +------- src/reactjs/tests/useQueries.test.tsx | 4 +- src/reactjs/tests/useQuery.test.tsx | 82 ++----------------- src/reactjs/tests/utils.tsx | 19 +++-- 49 files changed, 318 insertions(+), 584 deletions(-) create mode 100644 docs/src/pages/guides/custom-logger.md delete mode 100644 docs/src/pages/reference/setLogger.md create mode 100644 src/core/logger.native.ts delete mode 100644 src/reactjs/logger.native.ts delete mode 100644 src/reactjs/logger.ts delete mode 100644 src/reactjs/setLogger.ts delete mode 100644 src/reactjs/tests/logger.native.test.tsx diff --git a/docs/src/manifests/manifest.json b/docs/src/manifests/manifest.json index 748b8ba7a3..ac3a029884 100644 --- a/docs/src/manifests/manifest.json +++ b/docs/src/manifests/manifest.json @@ -195,6 +195,11 @@ "path": "/guides/suspense", "editUrl": "/guides/suspense.md" }, + { + "title": "Custom Logger", + "path": "/guides/custom-logger", + "editUrl": "/guides/custom-logger.md" + }, { "title": "Testing", "path": "/guides/testing", @@ -444,11 +449,6 @@ "path": "/reference/onlineManager", "editUrl": "/reference/onlineManager.md" }, - { - "title": "setLogger", - "path": "/reference/setLogger", - "editUrl": "/reference/setLogger.md" - }, { "title": "hydration", "path": "/reference/hydration", diff --git a/docs/src/pages/guides/custom-logger.md b/docs/src/pages/guides/custom-logger.md new file mode 100644 index 0000000000..8730ac3309 --- /dev/null +++ b/docs/src/pages/guides/custom-logger.md @@ -0,0 +1,22 @@ +--- +id: custom-logger +title: Custom Logger +--- + +If you want to change how information is logged by React Query, you can set a custom logger when creating a `QueryClient`. + +```js +const queryClient = new QueryClient({ + logger: { + log: (...args) => { + // Log debugging information + }, + warn: (...args) => { + // Log warning + }, + error: (...args) => { + // Log error + }, + }, +}) +``` diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index ac5cedc7ff..a5a77d2e39 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -260,11 +260,24 @@ Additionally, `query.setDefaultOptions` was removed because it was also unused. Types now require using TypeScript v4.1 or greater -### Logging +### Logging in production Starting with v4, react-query will no longer log errors (e.g. failed fetches) to the console in production mode, as this was confusing to many. Errors will still show up in development mode. +### `setLogger` is removed + +It was possible to change the logger globally by calling `setLogger`. In v4, that function is replaced with an optional field when creating a `QueryClient`. + +```diff +- import { QueryClient, setLogger } from 'react-query'; ++ import { QueryClient } from 'react-query'; + +- setLogger(customLogger) +- const queryClient = new QueryClient(); ++ const queryClient = new QueryClient({ logger: customLogger }) +``` + ### Undefined is an illegale cache value for successful queries In order to make bailing out of updates possible by returning `undefined`, we had to make `undefined` an illegal cache value. This is in-line with other concepts of react-query, for example, returning `undefined` from the [initialData function](guides/initial-query-data#initial-data-function) will also _not_ set data. diff --git a/docs/src/pages/guides/testing.md b/docs/src/pages/guides/testing.md index 3f1ed66106..fc3a323569 100644 --- a/docs/src/pages/guides/testing.md +++ b/docs/src/pages/guides/testing.md @@ -71,17 +71,18 @@ This will set the defaults for all queries in the component tree to "no retries" ## Turn off network error logging When testing we want to suppress network errors being logged to the console. -To do this, we can use `react-query`'s `setLogger()` function. +To do this, we can pass a custom logger to `QueryClient`: ```ts -// as part of your test setup -import { setLogger } from 'react-query' - -setLogger({ - log: console.log, - warn: console.warn, - // ✅ no more errors on the console - error: () => {}, +import { QueryClient } from 'react-query' + +const queryClient = new QueryClient({ + logger: { + log: console.log, + warn: console.warn, + // ✅ no more errors on the console for tests + error: process.env.NODE_ENV === 'test' ? () => {} : console.error, + }, }) ``` @@ -181,6 +182,7 @@ expect(result.current.data.pages).toStrictEqual([ expectation.done(); ``` + ## Further reading For additional tips and an alternative setup using `mock-service-worker`, have a look at [Testing React Query](../community/tkdodos-blog#5-testing-react-query) from diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index 2c4ab70462..3e4c2daf74 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -39,6 +39,7 @@ Its available methods are: - [`queryClient.resetQueries`](#queryclientresetqueries) - [`queryClient.isFetching`](#queryclientisfetching) - [`queryClient.isMutating`](#queryclientismutating) +- [`queryClient.getLogger`](#queryclientgetlogger) - [`queryClient.getDefaultOptions`](#queryclientgetdefaultoptions) - [`queryClient.setDefaultOptions`](#queryclientsetdefaultoptions) - [`queryClient.getQueryDefaults`](#queryclientgetquerydefaults) @@ -57,6 +58,9 @@ Its available methods are: - `mutationCache?: MutationCache` - Optional - The mutation cache this client is connected to. +- `logger?: Logger` + - Optional + - The logger this client uses to log debugging information, warnings and errors. If not set, `console` is the default logger. - `defaultOptions?: DefaultOptions` - Optional - Define defaults for all queries and mutations using this queryClient. @@ -203,7 +207,7 @@ This distinction is more a "convenience" for ts devs that know which structure w ## `queryClient.setQueryData` -`setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**. To update multiple queries at once and match query keys partially, you need to use [`queryClient.setQueriesData`](#queryclientsetqueriesdata) instead. +`setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**. To update multiple queries at once and match query keys partially, you need to use [`queryClient.setQueriesData`](#queryclientsetqueriesdata) instead. > The difference between using `setQueryData` and `fetchQuery` is that `setQueryData` is sync and assumes that you already synchronously have the data available. If you need to fetch the data asynchronously, it's suggested that you either refetch the query key or use `fetchQuery` to handle the asynchronous fetch. @@ -450,6 +454,14 @@ React Query also exports a handy [`useIsMutating`](./useIsMutating) hook that wi **Returns** This method returns the number of fetching mutations. +## `queryClient.getLogger` + +The `getLogger` method returns the logger which have been set when creating the client. + +```js +const logger = queryClient.getLogger() +``` + ## `queryClient.getDefaultOptions` The `getDefaultOptions` method returns the default options which have been set when creating the client or with `setDefaultOptions`. diff --git a/docs/src/pages/reference/setLogger.md b/docs/src/pages/reference/setLogger.md deleted file mode 100644 index fddb136a6c..0000000000 --- a/docs/src/pages/reference/setLogger.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: setLogger -title: setLogger ---- - -## `setLogger` - -`setLogger` is an optional function that allows you to replace the default `logger` used by React Query to log errors. By default, the `window.console` object is used. If no global `console` object is found in the environment, nothing will be logged. - -Examples: - -```js -import { setLogger } from 'react-query' -import { printLog, printWarn, printError } from 'custom-logger' - -// Custom logger -setLogger({ - log: printLog, - warn: printWarn, - error: printError, -}) - -// Sentry logger -setLogger({ - log: message => { - Sentry.captureMessage(message) - }, - warn: message => { - Sentry.captureMessage(message) - }, - error: error => { - Sentry.captureException(error) - }, -}) - -// Winston logger -setLogger(winston.createLogger()) -``` - -**Options** - -- `logger: Logger` - - Must implement the `log`, `warn`, and `error` methods. diff --git a/jest.config.js b/jest.config.js index 2bb4897dd3..a231855984 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,4 +7,5 @@ module.exports = { moduleNameMapper: { 'react-query': '/src/index.ts', }, + clearMocks: true, } diff --git a/package.json b/package.json index 326031572d..26e4f56525 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,9 @@ "es/index.js", "es/reactjs/index.js", "es/reactjs/setBatchUpdatesFn.js", - "es/reactjs/setLogger.js", "lib/index.js", "lib/reactjs/index.js", - "lib/reactjs/setBatchUpdatesFn.js", - "lib/reactjs/setLogger.js" + "lib/reactjs/setBatchUpdatesFn.js" ], "scripts": { "test": "is-ci \"test:ci\" \"test:dev\"", diff --git a/src/core/index.ts b/src/core/index.ts index 5a6035735f..d6b2d64f05 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -6,7 +6,6 @@ export { QueriesObserver } from './queriesObserver' export { InfiniteQueryObserver } from './infiniteQueryObserver' export { MutationCache } from './mutationCache' export { MutationObserver } from './mutationObserver' -export { setLogger } from './logger' export { notifyManager } from './notifyManager' export { focusManager } from './focusManager' export { onlineManager } from './onlineManager' diff --git a/src/core/logger.native.ts b/src/core/logger.native.ts new file mode 100644 index 0000000000..161b5b72f4 --- /dev/null +++ b/src/core/logger.native.ts @@ -0,0 +1,11 @@ +import type { Logger } from './logger' + +/** + * See https://github.com/tannerlinsley/react-query/issues/795 + * and https://github.com/tannerlinsley/react-query/pull/3246/#discussion_r795105707 + */ +export const defaultLogger: Logger = { + log: console.log, + warn: console.warn, + error: console.warn, +} diff --git a/src/core/logger.ts b/src/core/logger.ts index eb33ebc985..8025b966b1 100644 --- a/src/core/logger.ts +++ b/src/core/logger.ts @@ -1,5 +1,3 @@ -// TYPES - export interface Logger { log: LogFunction warn: LogFunction @@ -8,14 +6,4 @@ export interface Logger { type LogFunction = (...args: any[]) => void -// FUNCTIONS - -let logger: Logger = console - -export function getLogger(): Logger { - return logger -} - -export function setLogger(newLogger: Logger) { - logger = newLogger -} +export const defaultLogger: Logger = console diff --git a/src/core/mutation.ts b/src/core/mutation.ts index f8cecc5d3d..2db6a609bf 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -1,7 +1,7 @@ import type { MutationOptions, MutationStatus, MutationMeta } from './types' import type { MutationCache } from './mutationCache' import type { MutationObserver } from './mutationObserver' -import { getLogger } from './logger' +import { defaultLogger, Logger } from './logger' import { notifyManager } from './notifyManager' import { Removable } from './removable' import { canFetch, Retryer, createRetryer } from './retryer' @@ -12,6 +12,7 @@ interface MutationConfig { mutationId: number mutationCache: MutationCache options: MutationOptions + logger?: Logger defaultOptions?: MutationOptions state?: MutationState meta?: MutationMeta @@ -89,6 +90,7 @@ export class Mutation< private observers: MutationObserver[] private mutationCache: MutationCache + private logger: Logger private retryer?: Retryer constructor(config: MutationConfig) { @@ -100,6 +102,7 @@ export class Mutation< } this.mutationId = config.mutationId this.mutationCache = config.mutationCache + this.logger = config.logger || defaultLogger this.observers = [] this.state = config.state || getDefaultState() this.meta = config.meta @@ -252,7 +255,7 @@ export class Mutation< ) if (process.env.NODE_ENV !== 'production') { - getLogger().error(error) + this.logger.error(error) } return Promise.resolve() diff --git a/src/core/mutationCache.ts b/src/core/mutationCache.ts index 91eb0ac9b9..11664cbeba 100644 --- a/src/core/mutationCache.ts +++ b/src/core/mutationCache.ts @@ -85,6 +85,7 @@ export class MutationCache extends Subscribable { ): Mutation { const mutation = new Mutation({ mutationCache: this, + logger: client.getLogger(), mutationId: ++this.mutationId, options: client.defaultMutationOptions(options), state, diff --git a/src/core/query.ts b/src/core/query.ts index 0389d45270..6eb0707870 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -17,8 +17,8 @@ import type { } from './types' import type { QueryCache } from './queryCache' import type { QueryObserver } from './queryObserver' +import { defaultLogger, Logger } from './logger' import { notifyManager } from './notifyManager' -import { getLogger } from './logger' import { Retryer, isCancelledError, canFetch, createRetryer } from './retryer' import { Removable } from './removable' @@ -33,6 +33,7 @@ interface QueryConfig< cache: QueryCache queryKey: TQueryKey queryHash: string + logger?: Logger options?: QueryOptions defaultOptions?: QueryOptions state?: QueryState @@ -155,6 +156,7 @@ export class Query< isFetchingOptimistic?: boolean private cache: QueryCache + private logger: Logger private promise?: Promise private retryer?: Retryer private observers: QueryObserver[] @@ -169,6 +171,7 @@ export class Query< this.setOptions(config.options) this.observers = [] this.cache = config.cache + this.logger = config.logger || defaultLogger this.queryKey = config.queryKey this.queryHash = config.queryHash this.initialState = config.state || getDefaultState(this.options) @@ -360,14 +363,13 @@ export class Query< } } - if ( - process.env.NODE_ENV !== 'production' && - !Array.isArray(this.options.queryKey) - ) { - getLogger().error( - 'As of v4, queryKey needs to be an Array, but the queryKey used was:', - JSON.stringify(this.options.queryKey) - ) + if (!Array.isArray(this.options.queryKey)) { + if (process.env.NODE_ENV !== 'production') { + this.logger.error( + 'As of v4, queryKey needs to be an Array, but the queryKey used was:', + JSON.stringify(this.options.queryKey) + ) + } } const abortController = getAbortController() @@ -447,7 +449,7 @@ export class Query< this.cache.config.onError?.(error, this as Query) if (process.env.NODE_ENV !== 'production') { - getLogger().error(error) + this.logger.error(error) } } diff --git a/src/core/queryCache.ts b/src/core/queryCache.ts index 730e42a7de..8f91147e7c 100644 --- a/src/core/queryCache.ts +++ b/src/core/queryCache.ts @@ -93,6 +93,7 @@ export class QueryCache extends Subscribable { if (!query) { query = new Query({ cache: this, + logger: client.getLogger(), queryKey, queryHash, options: client.defaultQueryOptions(options), diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index af0cf460af..cd96553ef6 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -39,7 +39,7 @@ import { onlineManager } from './onlineManager' import { notifyManager } from './notifyManager' import { infiniteQueryBehavior } from './infiniteQueryBehavior' import { CancelOptions, DefaultedQueryObserverOptions } from './types' -import { getLogger } from './logger' +import { defaultLogger, Logger } from './logger' // TYPES @@ -58,6 +58,7 @@ interface MutationDefaults { export class QueryClient { private queryCache: QueryCache private mutationCache: MutationCache + private logger: Logger private defaultOptions: DefaultOptions private queryDefaults: QueryDefaults[] private mutationDefaults: MutationDefaults[] @@ -67,6 +68,7 @@ export class QueryClient { constructor(config: QueryClientConfig = {}) { this.queryCache = config.queryCache || new QueryCache() this.mutationCache = config.mutationCache || new MutationCache() + this.logger = config.logger || defaultLogger this.defaultOptions = config.defaultOptions || {} this.queryDefaults = [] this.mutationDefaults = [] @@ -520,6 +522,10 @@ export class QueryClient { return this.mutationCache } + getLogger(): Logger { + return this.logger + } + getDefaultOptions(): DefaultOptions { return this.defaultOptions } @@ -562,11 +568,13 @@ export class QueryClient { ) // It is ok not having defaults, but it is error prone to have more than 1 default for a given key if (matchingDefaults.length > 1) { - getLogger().error( - `[QueryClient] Several query defaults match with key '${JSON.stringify( - queryKey - )}'. The first matching query defaults are used. Please check how query defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetquerydefaults.` - ) + if (process.env.NODE_ENV !== 'production') { + this.logger.error( + `[QueryClient] Several query defaults match with key '${JSON.stringify( + queryKey + )}'. The first matching query defaults are used. Please check how query defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetquerydefaults.` + ) + } } } @@ -607,11 +615,13 @@ export class QueryClient { ) // It is ok not having defaults, but it is error prone to have more than 1 default for a given key if (matchingDefaults.length > 1) { - getLogger().error( - `[QueryClient] Several mutation defaults match with key '${JSON.stringify( - mutationKey - )}'. The first matching mutation defaults are used. Please check how mutation defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetmutationdefaults.` - ) + if (process.env.NODE_ENV !== 'production') { + this.logger.error( + `[QueryClient] Several mutation defaults match with key '${JSON.stringify( + mutationKey + )}'. The first matching mutation defaults are used. Please check how mutation defaults are registered. Order does matter here. cf. https://react-query.tanstack.com/reference/QueryClient#queryclientsetmutationdefaults.` + ) + } } } diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 704c9cb887..8cc02210c2 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -21,7 +21,6 @@ import type { Query, QueryState, Action, FetchOptions } from './query' import type { QueryClient } from './queryClient' import { focusManager } from './focusManager' import { Subscribable } from './subscribable' -import { getLogger } from './logger' import { canFetch, isCancelledError } from './retryer' type QueryObserverListener = ( @@ -471,7 +470,7 @@ export class QueryObserver< this.previousSelectError = null } catch (selectError) { if (process.env.NODE_ENV !== 'production') { - getLogger().error(selectError) + this.client.getLogger().error(selectError) } error = selectError as TError this.previousSelectError = selectError as TError @@ -516,7 +515,7 @@ export class QueryObserver< this.previousSelectError = null } catch (selectError) { if (process.env.NODE_ENV !== 'production') { - getLogger().error(selectError) + this.client.getLogger().error(selectError) } error = selectError as TError this.previousSelectError = selectError as TError diff --git a/src/core/tests/hydration.test.tsx b/src/core/tests/hydration.test.tsx index 77a5e58924..aeda287386 100644 --- a/src/core/tests/hydration.test.tsx +++ b/src/core/tests/hydration.test.tsx @@ -1,10 +1,10 @@ import { + createQueryClient, executeMutation, mockNavigatorOnLine, sleep, } from '../../reactjs/tests/utils' import { QueryCache } from '../queryCache' -import { QueryClient } from '../queryClient' import { dehydrate, hydrate } from '../hydration' async function fetchData(value: TData, ms?: number): Promise { @@ -15,7 +15,7 @@ async function fetchData(value: TData, ms?: number): Promise { describe('dehydration and rehydration', () => { test('should work with serializeable values', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string')) await queryClient.prefetchQuery(['number'], () => fetchData(1)) await queryClient.prefetchQuery(['boolean'], () => fetchData(true)) @@ -31,7 +31,9 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ + queryCache: hydrationCache, + }) hydrate(hydrationClient, parsed) expect(hydrationCache.find(['string'])?.state.data).toBe('string') expect(hydrationCache.find(['number'])?.state.data).toBe(1) @@ -69,7 +71,7 @@ describe('dehydration and rehydration', () => { test('should not dehydrate queries if dehydrateQueries is set to false', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string')) const dehydrated = dehydrate(queryClient, { dehydrateQueries: false }) @@ -81,7 +83,7 @@ describe('dehydration and rehydration', () => { test('should use the cache time from the client', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string'), { cacheTime: 50, }) @@ -94,7 +96,7 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) expect(hydrationCache.find(['string'])?.state.data).toBe('string') await sleep(100) @@ -106,13 +108,13 @@ describe('dehydration and rehydration', () => { test('should be able to provide default options for the hydrated queries', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string')) const dehydrated = dehydrate(queryClient) const stringified = JSON.stringify(dehydrated) const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed, { defaultOptions: { queries: { retry: 10 } }, }) @@ -123,7 +125,7 @@ describe('dehydration and rehydration', () => { test('should work with complex keys', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery( ['string', { key: ['string'], key2: 0 }], () => fetchData('string') @@ -135,7 +137,7 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) expect( hydrationCache.find(['string', { key: ['string'], key2: 0 }])?.state.data @@ -158,7 +160,7 @@ describe('dehydration and rehydration', () => { consoleMock.mockImplementation(() => undefined) const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['success'], () => fetchData('success')) queryClient.prefetchQuery(['loading'], () => fetchData('loading', 10000)) await queryClient.prefetchQuery(['error'], () => { @@ -171,7 +173,7 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) expect(hydrationCache.find(['success'])).toBeTruthy() @@ -185,7 +187,7 @@ describe('dehydration and rehydration', () => { test('should filter queries via shouldDehydrateQuery', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string')) await queryClient.prefetchQuery(['number'], () => fetchData(1)) const dehydrated = dehydrate(queryClient, { @@ -205,7 +207,7 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) hydrate(hydrationClient, parsed) expect(hydrationCache.find(['string'])).toBeUndefined() expect(hydrationCache.find(['number'])?.state.data).toBe(1) @@ -216,7 +218,7 @@ describe('dehydration and rehydration', () => { test('should not overwrite query in cache if hydrated query is older', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string-older', 5) ) @@ -227,7 +229,7 @@ describe('dehydration and rehydration', () => { const parsed = JSON.parse(stringified) const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) await hydrationClient.prefetchQuery(['string'], () => fetchData('string-newer', 5) ) @@ -241,7 +243,7 @@ describe('dehydration and rehydration', () => { test('should overwrite query in cache if hydrated query is newer', async () => { const hydrationCache = new QueryCache() - const hydrationClient = new QueryClient({ queryCache: hydrationCache }) + const hydrationClient = createQueryClient({ queryCache: hydrationCache }) await hydrationClient.prefetchQuery(['string'], () => fetchData('string-older', 5) ) @@ -249,7 +251,7 @@ describe('dehydration and rehydration', () => { // --- const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => fetchData('string-newer', 5) ) @@ -280,7 +282,7 @@ describe('dehydration and rehydration', () => { }) const serverOnSuccess = jest.fn() - const serverClient = new QueryClient() + const serverClient = createQueryClient() serverClient.setMutationDefaults(['addTodo'], { mutationFn: serverAddTodo, @@ -307,7 +309,7 @@ describe('dehydration and rehydration', () => { onlineMock.mockReturnValue(true) const parsed = JSON.parse(stringified) - const client = new QueryClient() + const client = createQueryClient() const clientAddTodo = jest.fn().mockImplementation(variables => { return { id: 2, text: variables.text } @@ -352,7 +354,7 @@ describe('dehydration and rehydration', () => { .fn() .mockImplementation(() => Promise.reject('offline')) - const queryClient = new QueryClient() + const queryClient = createQueryClient() queryClient.setMutationDefaults(['addTodo'], { mutationFn: serverAddTodo, @@ -381,7 +383,7 @@ describe('dehydration and rehydration', () => { .fn() .mockImplementation(() => Promise.reject('offline')) - const queryClient = new QueryClient() + const queryClient = createQueryClient() queryClient.setMutationDefaults(['addTodo'], { mutationFn: serverAddTodo, @@ -407,7 +409,7 @@ describe('dehydration and rehydration', () => { test('should not hydrate if the hydratedState is null or is not an object', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) expect(() => hydrate(queryClient, null)).not.toThrow() expect(() => hydrate(queryClient, 'invalid')).not.toThrow() @@ -417,7 +419,7 @@ describe('dehydration and rehydration', () => { test('should support hydratedState with undefined queries and mutations', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) expect(() => hydrate(queryClient, {})).not.toThrow() expect(() => hydrate(queryClient, {})).not.toThrow() diff --git a/src/core/tests/infiniteQueryBehavior.test.tsx b/src/core/tests/infiniteQueryBehavior.test.tsx index a3e57e30e4..6a056e1727 100644 --- a/src/core/tests/infiniteQueryBehavior.test.tsx +++ b/src/core/tests/infiniteQueryBehavior.test.tsx @@ -1,5 +1,5 @@ import { waitFor } from '@testing-library/react' -import { queryKey, mockConsoleError } from '../../reactjs/tests/utils' +import { createQueryClient, queryKey } from '../../reactjs/tests/utils' import { QueryClient, InfiniteQueryObserver, @@ -10,7 +10,7 @@ describe('InfiniteQueryBehavior', () => { let queryClient: QueryClient beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryClient.mount() }) @@ -19,7 +19,6 @@ describe('InfiniteQueryBehavior', () => { }) test('InfiniteQueryBehavior should throw an error if the queryFn is not defined', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const observer = new InfiniteQueryObserver(queryClient, { @@ -43,7 +42,6 @@ describe('InfiniteQueryBehavior', () => { }) unsubscribe() - consoleMock.mockRestore() }) test('InfiniteQueryBehavior should not refetch the first page if another page refetched', async () => { diff --git a/src/core/tests/infiniteQueryObserver.test.tsx b/src/core/tests/infiniteQueryObserver.test.tsx index 27094fe615..5d367f3bf9 100644 --- a/src/core/tests/infiniteQueryObserver.test.tsx +++ b/src/core/tests/infiniteQueryObserver.test.tsx @@ -1,11 +1,11 @@ -import { sleep, queryKey } from '../../reactjs/tests/utils' +import { sleep, queryKey, createQueryClient } from '../../reactjs/tests/utils' import { QueryClient, InfiniteQueryObserver } from '../..' describe('InfiniteQueryObserver', () => { let queryClient: QueryClient beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryClient.mount() }) diff --git a/src/core/tests/mutationCache.test.tsx b/src/core/tests/mutationCache.test.tsx index 5bafcc2e35..8c1b8098ac 100644 --- a/src/core/tests/mutationCache.test.tsx +++ b/src/core/tests/mutationCache.test.tsx @@ -1,20 +1,19 @@ import { waitFor } from '@testing-library/react' import { queryKey, - mockConsoleError, sleep, executeMutation, + createQueryClient, } from '../../reactjs/tests/utils' -import { MutationCache, MutationObserver, QueryClient } from '../..' +import { MutationCache, MutationObserver } from '../..' describe('mutationCache', () => { describe('MutationCacheConfig.onError', () => { test('should be called when a mutation errors', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const onError = jest.fn() const testCache = new MutationCache({ onError }) - const testClient = new QueryClient({ mutationCache: testCache }) + const testClient = createQueryClient({ mutationCache: testCache }) try { await executeMutation(testClient, { @@ -23,9 +22,7 @@ describe('mutationCache', () => { mutationFn: () => Promise.reject('error'), onMutate: () => 'context', }) - } catch { - consoleMock.mockRestore() - } + } catch {} const mutation = testCache.getAll()[0] expect(onError).toHaveBeenCalledWith('error', 'vars', 'context', mutation) @@ -33,11 +30,10 @@ describe('mutationCache', () => { }) describe('MutationCacheConfig.onSuccess', () => { test('should be called when a mutation is successful', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const onSuccess = jest.fn() const testCache = new MutationCache({ onSuccess }) - const testClient = new QueryClient({ mutationCache: testCache }) + const testClient = createQueryClient({ mutationCache: testCache }) try { await executeMutation(testClient, { @@ -46,9 +42,7 @@ describe('mutationCache', () => { mutationFn: () => Promise.resolve({ data: 5 }), onMutate: () => 'context', }) - } catch { - consoleMock.mockRestore() - } + } catch {} const mutation = testCache.getAll()[0] expect(onSuccess).toHaveBeenCalledWith( @@ -61,11 +55,10 @@ describe('mutationCache', () => { }) describe('MutationCacheConfig.onMutate', () => { test('should be called before a mutation executes', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const onMutate = jest.fn() const testCache = new MutationCache({ onMutate }) - const testClient = new QueryClient({ mutationCache: testCache }) + const testClient = createQueryClient({ mutationCache: testCache }) try { await executeMutation(testClient, { @@ -74,9 +67,7 @@ describe('mutationCache', () => { mutationFn: () => Promise.resolve({ data: 5 }), onMutate: () => 'context', }) - } catch { - consoleMock.mockRestore() - } + } catch {} const mutation = testCache.getAll()[0] expect(onMutate).toHaveBeenCalledWith('vars', mutation) @@ -86,7 +77,7 @@ describe('mutationCache', () => { describe('find', () => { test('should filter correctly', async () => { const testCache = new MutationCache() - const testClient = new QueryClient({ mutationCache: testCache }) + const testClient = createQueryClient({ mutationCache: testCache }) const key = ['mutation', 'vars'] await executeMutation(testClient, { mutationKey: key, @@ -108,7 +99,7 @@ describe('mutationCache', () => { describe('findAll', () => { test('should filter correctly', async () => { const testCache = new MutationCache() - const testClient = new QueryClient({ mutationCache: testCache }) + const testClient = createQueryClient({ mutationCache: testCache }) await executeMutation(testClient, { mutationKey: ['a', 1], variables: 1, @@ -139,7 +130,7 @@ describe('mutationCache', () => { describe('garbage collection', () => { test('should remove unused mutations after cacheTime has elapsed', async () => { const testCache = new MutationCache() - const testClient = new QueryClient({ mutationCache: testCache }) + const testClient = createQueryClient({ mutationCache: testCache }) const onSuccess = jest.fn() await executeMutation(testClient, { mutationKey: ['a', 1], @@ -158,7 +149,7 @@ describe('mutationCache', () => { }) test('should not remove mutations if there are active observers', async () => { - const queryClient = new QueryClient() + const queryClient = createQueryClient() const observer = new MutationObserver(queryClient, { variables: 1, cacheTime: 10, @@ -180,7 +171,7 @@ describe('mutationCache', () => { }) test('should only remove when the last observer unsubscribes', async () => { - const queryClient = new QueryClient() + const queryClient = createQueryClient() const observer1 = new MutationObserver(queryClient, { variables: 1, cacheTime: 10, @@ -220,7 +211,7 @@ describe('mutationCache', () => { }) test('should be garbage collected later when unsubscribed and mutation is loading', async () => { - const queryClient = new QueryClient() + const queryClient = createQueryClient() const onSuccess = jest.fn() const observer = new MutationObserver(queryClient, { variables: 1, @@ -247,7 +238,7 @@ describe('mutationCache', () => { }) test('should call callbacks even with cacheTime 0 and mutation still loading', async () => { - const queryClient = new QueryClient() + const queryClient = createQueryClient() const onSuccess = jest.fn() const observer = new MutationObserver(queryClient, { variables: 1, diff --git a/src/core/tests/mutationObserver.test.tsx b/src/core/tests/mutationObserver.test.tsx index c1f577f936..d011beda46 100644 --- a/src/core/tests/mutationObserver.test.tsx +++ b/src/core/tests/mutationObserver.test.tsx @@ -1,12 +1,12 @@ import { waitFor } from '@testing-library/react' -import { sleep } from '../../reactjs/tests/utils' +import { createQueryClient, sleep } from '../../reactjs/tests/utils' import { QueryClient, MutationObserver } from '../..' describe('mutationObserver', () => { let queryClient: QueryClient beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryClient.mount() }) diff --git a/src/core/tests/mutations.test.tsx b/src/core/tests/mutations.test.tsx index 71e491bdea..acf9fb79b5 100644 --- a/src/core/tests/mutations.test.tsx +++ b/src/core/tests/mutations.test.tsx @@ -1,7 +1,7 @@ import { QueryClient } from '../..' import { + createQueryClient, executeMutation, - mockConsoleError, queryKey, sleep, } from '../../reactjs/tests/utils' @@ -12,7 +12,7 @@ describe('mutations', () => { let queryClient: QueryClient beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryClient.mount() }) @@ -151,8 +151,6 @@ describe('mutations', () => { }) test('mutation should set correct error states', async () => { - const consoleMock = mockConsoleError() - const mutation = new MutationObserver(queryClient, { mutationFn: async () => { await sleep(20) @@ -243,8 +241,6 @@ describe('mutations', () => { status: 'error', variables: 'todo', }) - - consoleMock.mockRestore() }) test('should be able to restore a mutation', async () => { @@ -351,8 +347,6 @@ describe('mutations', () => { }) test('mutate should throw an error if no mutationFn found', async () => { - const consoleMock = mockConsoleError() - const mutation = new MutationObserver(queryClient, { mutationFn: undefined, retry: false, @@ -365,7 +359,5 @@ describe('mutations', () => { error = err } expect(error).toEqual('No mutationFn found') - - consoleMock.mockRestore() }) }) diff --git a/src/core/tests/queriesObserver.test.tsx b/src/core/tests/queriesObserver.test.tsx index e8cd4ca550..b75f25e12a 100644 --- a/src/core/tests/queriesObserver.test.tsx +++ b/src/core/tests/queriesObserver.test.tsx @@ -1,12 +1,15 @@ import { waitFor } from '@testing-library/react' -import { sleep, queryKey } from '../../reactjs/tests/utils' +import { + sleep, + queryKey, + createQueryClient, + mockLogger, +} from '../../reactjs/tests/utils' import { QueryClient, QueriesObserver, QueryObserverResult, QueryObserver, - Logger, - setLogger, } from '../..' import { QueryKey } from '..' @@ -14,7 +17,7 @@ describe('queriesObserver', () => { let queryClient: QueryClient beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryClient.mount() }) @@ -41,14 +44,6 @@ describe('queriesObserver', () => { }) test('should still return value for undefined query key', async () => { - const logger: Logger = { - error: jest.fn(), - log: jest.fn(), - warn: jest.fn(), - } - - setLogger(logger) - const key1 = queryKey() const queryFn1 = jest.fn().mockReturnValue(1) const queryFn2 = jest.fn().mockReturnValue(2) @@ -64,8 +59,7 @@ describe('queriesObserver', () => { unsubscribe() expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }]) - expect(logger.error).toHaveBeenCalledTimes(1) - setLogger(console) + expect(mockLogger.error).toHaveBeenCalledTimes(1) }) test('should update when a query updates', async () => { diff --git a/src/core/tests/query.test.tsx b/src/core/tests/query.test.tsx index e60f2b6222..f53ff9ad85 100644 --- a/src/core/tests/query.test.tsx +++ b/src/core/tests/query.test.tsx @@ -2,7 +2,8 @@ import { sleep, queryKey, mockVisibilityState, - mockConsoleError, + mockLogger, + createQueryClient, } from '../../reactjs/tests/utils' import { QueryCache, @@ -21,7 +22,7 @@ describe('query', () => { let queryCache: QueryCache beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryCache = queryClient.getQueryCache() queryClient.mount() }) @@ -429,8 +430,6 @@ describe('query', () => { }) test('cancelling a rejected query should not have any effect', async () => { - const consoleMock = mockConsoleError() - const key = queryKey() await queryClient.prefetchQuery(key, async () => { @@ -442,12 +441,9 @@ describe('query', () => { expect(isError(query.state.error)).toBe(true) expect(isCancelledError(query.state.error)).toBe(false) - - consoleMock.mockRestore() }) test('the previous query status should be kept when refetching', async () => { - const consoleMock = mockConsoleError() const key = queryKey() await queryClient.prefetchQuery(key, () => 'data') @@ -471,8 +467,6 @@ describe('query', () => { await sleep(100) expect(query.state.status).toBe('error') - - consoleMock.mockRestore() }) test('queries with cacheTime 0 should be removed immediately after unsubscribing', async () => { @@ -789,7 +783,6 @@ describe('query', () => { }) test('fetch should throw an error if the queryFn is not defined', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const observer = new QueryObserver(queryClient, { @@ -800,15 +793,12 @@ describe('query', () => { const unsubscribe = observer.subscribe(() => undefined) await sleep(10) - expect(consoleMock).toHaveBeenCalledWith('Missing queryFn') + expect(mockLogger.error).toHaveBeenCalledWith('Missing queryFn') unsubscribe() - consoleMock.mockRestore() }) test('fetch should dispatch an error if the queryFn returns undefined', async () => { - const consoleMock = mockConsoleError() - const key = queryKey() const observer = new QueryObserver(queryClient, { @@ -832,9 +822,8 @@ describe('query', () => { error, }) - expect(consoleMock).toHaveBeenCalledWith(error) + expect(mockLogger.error).toHaveBeenCalledWith(error) unsubscribe() - consoleMock.mockRestore() }) test('fetch should dispatch fetch if is fetching and current promise is undefined', async () => { diff --git a/src/core/tests/queryCache.test.tsx b/src/core/tests/queryCache.test.tsx index e9fcba22cc..1097164d2e 100644 --- a/src/core/tests/queryCache.test.tsx +++ b/src/core/tests/queryCache.test.tsx @@ -1,4 +1,4 @@ -import { sleep, queryKey, mockConsoleError } from '../../reactjs/tests/utils' +import { sleep, queryKey, createQueryClient } from '../../reactjs/tests/utils' import { QueryCache, QueryClient } from '../..' import { Query } from '.././query' @@ -7,7 +7,7 @@ describe('queryCache', () => { let queryCache: QueryCache beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryCache = queryClient.getQueryCache() }) @@ -171,13 +171,11 @@ describe('queryCache', () => { describe('QueryCacheConfig.onError', () => { test('should be called when a query errors', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const onError = jest.fn() const testCache = new QueryCache({ onError }) - const testClient = new QueryClient({ queryCache: testCache }) + const testClient = createQueryClient({ queryCache: testCache }) await testClient.prefetchQuery(key, () => Promise.reject('error')) - consoleMock.mockRestore() const query = testCache.find(key) expect(onError).toHaveBeenCalledWith('error', query) }) @@ -185,13 +183,11 @@ describe('queryCache', () => { describe('QueryCacheConfig.onSuccess', () => { test('should be called when a query is successful', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const onSuccess = jest.fn() const testCache = new QueryCache({ onSuccess }) - const testClient = new QueryClient({ queryCache: testCache }) + const testClient = createQueryClient({ queryCache: testCache }) await testClient.prefetchQuery(key, () => Promise.resolve({ data: 5 })) - consoleMock.mockRestore() const query = testCache.find(key) expect(onSuccess).toHaveBeenCalledWith({ data: 5 }, query) }) diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index aca0a77264..dde571992c 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -5,8 +5,9 @@ import React from 'react' import { sleep, queryKey, - mockConsoleError, renderWithClient, + mockLogger, + createQueryClient, } from '../../reactjs/tests/utils' import { useQuery, @@ -23,7 +24,7 @@ describe('queryClient', () => { let queryCache: QueryCache beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryCache = queryClient.getQueryCache() queryClient.mount() }) @@ -37,7 +38,7 @@ describe('queryClient', () => { const key = queryKey() const queryFn = () => 'data' - const testClient = new QueryClient({ + const testClient = createQueryClient({ defaultOptions: { queries: { queryFn } }, }) @@ -47,7 +48,7 @@ describe('queryClient', () => { test('should merge defaultOptions when query is added to cache', async () => { const key = queryKey() - const testClient = new QueryClient({ + const testClient = createQueryClient({ defaultOptions: { queries: { cacheTime: Infinity }, }, @@ -62,7 +63,7 @@ describe('queryClient', () => { test('should get defaultOptions', async () => { const queryFn = () => 'data' const defaultOptions = { queries: { queryFn } } - const testClient = new QueryClient({ + const testClient = createQueryClient({ defaultOptions, }) expect(testClient.getDefaultOptions()).toMatchObject(defaultOptions) @@ -97,7 +98,6 @@ describe('queryClient', () => { }) test('should not match if the query key is a subset', async () => { - const consoleMock = mockConsoleError() const key = queryKey() queryClient.setQueryDefaults([key, 'a'], { queryFn: () => 'data' }) const observer = new QueryObserver(queryClient, { @@ -107,7 +107,6 @@ describe('queryClient', () => { }) const { status } = await observer.refetch() expect(status).toBe('error') - consoleMock.mockRestore() }) test('should also set defaults for observers', async () => { @@ -134,9 +133,6 @@ describe('queryClient', () => { test('should warn in dev if several query defaults match a given key', () => { // Check discussion here: https://github.com/tannerlinsley/react-query/discussions/3199 - const consoleErrorMock = jest.spyOn(console, 'error') - consoleErrorMock.mockImplementation(() => true) - const keyABCD = [ { a: 'a', @@ -172,7 +168,7 @@ describe('queryClient', () => { // No defaults, no warning const noDefaults = queryClient.getQueryDefaults(keyABCD) expect(noDefaults).toBeUndefined() - expect(consoleErrorMock).not.toHaveBeenCalled() + expect(mockLogger.error).not.toHaveBeenCalled() // If defaults for key ABCD are registered **before** the ones of key ABC (more generic)… queryClient.setQueryDefaults(keyABCD, defaultsOfABCD) @@ -181,10 +177,10 @@ describe('queryClient', () => { const goodDefaults = queryClient.getQueryDefaults(keyABCD) expect(goodDefaults).toBe(defaultsOfABCD) // The warning is still raised since several defaults are matching - expect(consoleErrorMock).toHaveBeenCalledTimes(1) + expect(mockLogger.error).toHaveBeenCalledTimes(1) // Let's create another queryClient and change the order of registration - const newQueryClient = new QueryClient() + const newQueryClient = createQueryClient() // The defaults for key ABC (more generic) are registered **before** the ones of key ABCD… newQueryClient.setQueryDefaults(keyABC, defaultsOfABC) newQueryClient.setQueryDefaults(keyABCD, defaultsOfABCD) @@ -192,16 +188,11 @@ describe('queryClient', () => { const badDefaults = newQueryClient.getQueryDefaults(keyABCD) expect(badDefaults).not.toBe(defaultsOfABCD) expect(badDefaults).toBe(defaultsOfABC) - expect(consoleErrorMock).toHaveBeenCalledTimes(2) - - consoleErrorMock.mockRestore() + expect(mockLogger.error).toHaveBeenCalledTimes(2) }) test('should warn in dev if several mutation defaults match a given key', () => { // Check discussion here: https://github.com/tannerlinsley/react-query/discussions/3199 - const consoleErrorMock = jest.spyOn(console, 'error') - consoleErrorMock.mockImplementation(() => true) - const keyABCD = [ { a: 'a', @@ -233,7 +224,7 @@ describe('queryClient', () => { // No defaults, no warning const noDefaults = queryClient.getMutationDefaults(keyABCD) expect(noDefaults).toBeUndefined() - expect(consoleErrorMock).not.toHaveBeenCalled() + expect(mockLogger.error).not.toHaveBeenCalled() // If defaults for key ABCD are registered **before** the ones of key ABC (more generic)… queryClient.setMutationDefaults(keyABCD, defaultsOfABCD) @@ -242,10 +233,10 @@ describe('queryClient', () => { const goodDefaults = queryClient.getMutationDefaults(keyABCD) expect(goodDefaults).toBe(defaultsOfABCD) // The warning is still raised since several defaults are matching - expect(consoleErrorMock).toHaveBeenCalledTimes(1) + expect(mockLogger.error).toHaveBeenCalledTimes(1) // Let's create another queryClient and change the order of registration - const newQueryClient = new QueryClient() + const newQueryClient = createQueryClient() // The defaults for key ABC (more generic) are registered **before** the ones of key ABCD… newQueryClient.setMutationDefaults(keyABC, defaultsOfABC) newQueryClient.setMutationDefaults(keyABCD, defaultsOfABCD) @@ -253,9 +244,7 @@ describe('queryClient', () => { const badDefaults = newQueryClient.getMutationDefaults(keyABCD) expect(badDefaults).not.toBe(defaultsOfABCD) expect(badDefaults).toBe(defaultsOfABC) - expect(consoleErrorMock).toHaveBeenCalledTimes(2) - - consoleErrorMock.mockRestore() + expect(mockLogger.error).toHaveBeenCalledTimes(2) }) }) @@ -281,7 +270,7 @@ describe('queryClient', () => { test('should use default options', () => { const key = queryKey() - const testClient = new QueryClient({ + const testClient = createQueryClient({ defaultOptions: { queries: { queryKeyHashFn: () => 'someKey' } }, }) const testCache = testClient.getQueryCache() @@ -538,8 +527,6 @@ describe('queryClient', () => { // https://github.com/tannerlinsley/react-query/issues/652 test('should not retry by default', async () => { - const consoleMock = mockConsoleError() - const key = queryKey() await expect( @@ -547,8 +534,6 @@ describe('queryClient', () => { throw new Error('error') }) ).rejects.toEqual(new Error('error')) - - consoleMock.mockRestore() }) test('should return the cached data on cache hit', async () => { @@ -736,8 +721,6 @@ describe('queryClient', () => { }) test('should return undefined when an error is thrown', async () => { - const consoleMock = mockConsoleError() - const key = queryKey() const result = await queryClient.prefetchQuery( @@ -751,9 +734,7 @@ describe('queryClient', () => { ) expect(result).toBeUndefined() - expect(consoleMock).toHaveBeenCalled() - - consoleMock.mockRestore() + expect(mockLogger.error).toHaveBeenCalled() }) test('should be garbage collected after cacheTime if unused', async () => { @@ -794,7 +775,6 @@ describe('queryClient', () => { describe('cancelQueries', () => { test('should revert queries to their previous state', async () => { - const consoleMock = mockConsoleError() const key1 = queryKey() const key2 = queryKey() const key3 = queryKey() @@ -839,11 +819,9 @@ describe('queryClient', () => { status: 'loading', fetchStatus: 'idle', }) - consoleMock.mockRestore() }) test('should not revert if revert option is set to false', async () => { - const consoleMock = mockConsoleError() const key1 = queryKey() await queryClient.fetchQuery(key1, async () => { return 'data' @@ -858,7 +836,6 @@ describe('queryClient', () => { expect(state1).toMatchObject({ status: 'error', }) - consoleMock.mockRestore() }) }) @@ -1062,7 +1039,6 @@ describe('queryClient', () => { }) test('should throw an error if throwOnError option is set to true', async () => { - const consoleMock = mockConsoleError() const key1 = queryKey() const queryFnError = () => Promise.reject('error') try { @@ -1082,7 +1058,6 @@ describe('queryClient', () => { error = err } expect(error).toEqual('error') - consoleMock.mockRestore() }) }) @@ -1416,7 +1391,7 @@ describe('queryClient', () => { describe('focusManager and onlineManager', () => { test('should notify queryCache and mutationCache if focused', async () => { - const testClient = new QueryClient() + const testClient = createQueryClient() testClient.mount() const queryCacheOnFocusSpy = jest.spyOn( @@ -1449,7 +1424,7 @@ describe('queryClient', () => { }) test('should notify queryCache and mutationCache if online', async () => { - const testClient = new QueryClient() + const testClient = createQueryClient() testClient.mount() const queryCacheOnFocusSpy = jest.spyOn( diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index af89f3d576..2bee1ca21e 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -1,8 +1,9 @@ import { sleep, queryKey, - mockConsoleError, expectType, + mockLogger, + createQueryClient, } from '../../reactjs/tests/utils' import { QueryClient, @@ -15,7 +16,7 @@ describe('queryObserver', () => { let queryClient: QueryClient beforeEach(() => { - queryClient = new QueryClient() + queryClient = createQueryClient() queryClient.mount() }) @@ -246,7 +247,6 @@ describe('queryObserver', () => { }) test('should always run the selector again if selector throws an error', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const results: QueryObserverResult[] = [] const select = () => { @@ -290,7 +290,6 @@ describe('queryObserver', () => { isFetching: false, data: undefined, }) - consoleMock.mockRestore() }) test('should structurally share the selector', async () => { @@ -395,7 +394,6 @@ describe('queryObserver', () => { }) test('should stop retry when unsubscribing', async () => { - const consoleMock = mockConsoleError() const key = queryKey() let count = 0 const observer = new QueryObserver(queryClient, { @@ -412,7 +410,6 @@ describe('queryObserver', () => { unsubscribe() await sleep(200) expect(count).toBe(2) - consoleMock.mockRestore() }) test('should clear interval when unsubscribing to a refetchInterval query', async () => { @@ -463,7 +460,6 @@ describe('queryObserver', () => { }) test('the retrier should not throw an error when reject if the retrier is already resolved', async () => { - const consoleMock = mockConsoleError() const key = queryKey() let count = 0 @@ -490,9 +486,7 @@ describe('queryObserver', () => { // Should not log an error queryClient.clear() await sleep(40) - expect(consoleMock).not.toHaveBeenNthCalledWith(1, 'reject 1') - - consoleMock.mockRestore() + expect(mockLogger.error).not.toHaveBeenNthCalledWith(1, 'reject 1') }) test('should throw an error if enabled option type is not valid', async () => { @@ -522,7 +516,6 @@ describe('queryObserver', () => { test('should throw an error if throwOnError option is true', async () => { const key = queryKey() - const consoleMock = mockConsoleError() const observer = new QueryObserver(queryClient, { queryKey: key, @@ -538,8 +531,6 @@ describe('queryObserver', () => { } expect(error).toEqual('error') - - consoleMock.mockRestore() }) test('should not refetch in background if refetchIntervalInBackground is false', async () => { @@ -596,7 +587,6 @@ describe('queryObserver', () => { test('select function error using placeholderdata should log an error', () => { const key = queryKey() - const consoleMock = mockConsoleError() new QueryObserver(queryClient, { queryKey: key, @@ -607,9 +597,7 @@ describe('queryObserver', () => { }, }) - expect(consoleMock).toHaveBeenNthCalledWith(1, new Error('error')) - - consoleMock.mockRestore() + expect(mockLogger.error).toHaveBeenNthCalledWith(1, new Error('error')) }) test('should not use replaceEqualDeep for select value when structuralSharing option is true and placeholderdata is defined', () => { diff --git a/src/core/tests/utils.test.tsx b/src/core/tests/utils.test.tsx index af8b618a82..4e4079063d 100644 --- a/src/core/tests/utils.test.tsx +++ b/src/core/tests/utils.test.tsx @@ -6,41 +6,11 @@ import { matchMutation, scheduleMicrotask, } from '../utils' -import { QueryClient, QueryCache, setLogger, Logger } from '../..' -import { queryKey } from '../../reactjs/tests/utils' import { Mutation } from '../mutation' import { waitFor } from '@testing-library/dom' +import { createQueryClient } from '../../reactjs/tests/utils' describe('core/utils', () => { - it('setLogger should override the default logger', async () => { - const key = queryKey() - - const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) - - const logger: Logger = { - error: jest.fn(), - log: jest.fn(), - warn: jest.fn(), - } - - setLogger(logger) - - await queryClient.prefetchQuery( - key, - async () => { - throw new Error('Test') - }, - { - retry: 0, - } - ) - - expect(logger.error).toHaveBeenCalled() - - setLogger(console) - }) - describe('isPlainObject', () => { it('should return `true` for a plain object', () => { expect(isPlainObject({})).toEqual(true) @@ -349,7 +319,7 @@ describe('core/utils', () => { describe('matchMutation', () => { it('should return false if mutationKey options is undefined', () => { const filters = { mutationKey: ['key1'] } - const queryClient = new QueryClient() + const queryClient = createQueryClient() const mutation = new Mutation({ mutationId: 1, mutationCache: queryClient.getMutationCache(), diff --git a/src/core/types.ts b/src/core/types.ts index f630423d3d..0dab1232ca 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -4,6 +4,7 @@ import type { RetryValue, RetryDelayValue } from './retryer' import type { QueryFilters, QueryTypeFilter } from './utils' import type { QueryCache } from './queryCache' import type { MutationCache } from './mutationCache' +import { Logger } from './logger' export type QueryKey = readonly unknown[] export type QueryFunctionData = T extends undefined ? never : T @@ -656,6 +657,7 @@ export type MutationObserverResult< export interface QueryClientConfig { queryCache?: QueryCache mutationCache?: MutationCache + logger?: Logger defaultOptions?: DefaultOptions } diff --git a/src/persistQueryClient/index.ts b/src/persistQueryClient/index.ts index d38ed924b7..407e58f4ed 100644 --- a/src/persistQueryClient/index.ts +++ b/src/persistQueryClient/index.ts @@ -1,5 +1,4 @@ import { QueryClient } from '../core' -import { getLogger } from '../core/logger' import { dehydrate, DehydratedState, @@ -84,10 +83,14 @@ export async function persistQueryClientRestore({ } } } catch (err) { - getLogger().error(err) - getLogger().warn( - 'Encountered an error attempting to restore client cache from persisted location. As a precaution, the persisted cache will be discarded.' - ) + if (process.env.NODE_ENV !== 'production') { + queryClient.getLogger().error(err) + queryClient + .getLogger() + .warn( + 'Encountered an error attempting to restore client cache from persisted location. As a precaution, the persisted cache will be discarded.' + ) + } persister.removeClient() } } diff --git a/src/reactjs/index.ts b/src/reactjs/index.ts index 54d5ba0d2d..b428b5fe8c 100644 --- a/src/reactjs/index.ts +++ b/src/reactjs/index.ts @@ -1,6 +1,5 @@ // Side effects import './setBatchUpdatesFn' -import './setLogger' export { QueryClientProvider, useQueryClient } from './QueryClientProvider' export { diff --git a/src/reactjs/logger.native.ts b/src/reactjs/logger.native.ts deleted file mode 100644 index b0fc86bf0f..0000000000 --- a/src/reactjs/logger.native.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Logger } from '../core/logger' - -export const logger: Logger = { - log: console.log, - warn: console.warn, - error: console.warn, -} diff --git a/src/reactjs/logger.ts b/src/reactjs/logger.ts deleted file mode 100644 index b0b275d9f6..0000000000 --- a/src/reactjs/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { Logger } from '../core/logger' - -export const logger: Logger = console diff --git a/src/reactjs/setLogger.ts b/src/reactjs/setLogger.ts deleted file mode 100644 index ce65933fce..0000000000 --- a/src/reactjs/setLogger.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { setLogger } from '../core' -import { logger } from './logger' - -setLogger(logger) diff --git a/src/reactjs/tests/Hydrate.test.tsx b/src/reactjs/tests/Hydrate.test.tsx index 12924afa9d..c3a8278482 100644 --- a/src/reactjs/tests/Hydrate.test.tsx +++ b/src/reactjs/tests/Hydrate.test.tsx @@ -2,7 +2,6 @@ import React from 'react' import { render } from '@testing-library/react' import { - QueryClient, QueryClientProvider, QueryCache, useQuery, @@ -10,7 +9,7 @@ import { useHydrate, Hydrate, } from '../..' -import { sleep } from './utils' +import { createQueryClient, sleep } from './utils' import * as coreModule from '../../core/index' describe('React hydration', () => { @@ -21,7 +20,7 @@ describe('React hydration', () => { beforeAll(async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) await queryClient.prefetchQuery(['string'], () => dataQuery(['string'])) const dehydrated = dehydrate(queryClient) stringifiedState = JSON.stringify(dehydrated) @@ -32,7 +31,7 @@ describe('React hydration', () => { test('should hydrate queries to the cache on context', async () => { const dehydratedState = JSON.parse(stringifiedState) const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) function Page() { useHydrate(dehydratedState) @@ -60,7 +59,7 @@ describe('React hydration', () => { test('should hydrate new queries if queries change', async () => { const dehydratedState = JSON.parse(stringifiedState) const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) function Page({ queryKey }: { queryKey: [string] }) { const { data } = useQuery(queryKey, () => dataQuery(queryKey)) @@ -83,7 +82,7 @@ describe('React hydration', () => { rendered.getByText('string') const intermediateCache = new QueryCache() - const intermediateClient = new QueryClient({ + const intermediateClient = createQueryClient({ queryCache: intermediateCache, }) await intermediateClient.prefetchQuery(['string'], () => @@ -117,7 +116,7 @@ describe('React hydration', () => { test('should hydrate queries to new cache if cache changes', async () => { const dehydratedState = JSON.parse(stringifiedState) const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) function Page() { const { data } = useQuery(['string'], () => dataQuery(['string'])) @@ -140,7 +139,7 @@ describe('React hydration', () => { rendered.getByText('string') const newClientQueryCache = new QueryCache() - const newClientQueryClient = new QueryClient({ + const newClientQueryClient = createQueryClient({ queryCache: newClientQueryCache, }) @@ -162,7 +161,7 @@ describe('React hydration', () => { test('should not hydrate queries if state is null', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const hydrateSpy = jest.spyOn(coreModule, 'hydrate') @@ -185,7 +184,7 @@ describe('React hydration', () => { test('should not hydrate queries if state is undefined', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const hydrateSpy = jest.spyOn(coreModule, 'hydrate') diff --git a/src/reactjs/tests/QueryClientProvider.test.tsx b/src/reactjs/tests/QueryClientProvider.test.tsx index deeb7c8ea4..e451cd97c3 100644 --- a/src/reactjs/tests/QueryClientProvider.test.tsx +++ b/src/reactjs/tests/QueryClientProvider.test.tsx @@ -2,7 +2,7 @@ import React from 'react' import { render, waitFor } from '@testing-library/react' import { renderToString } from 'react-dom/server' -import { sleep, queryKey } from './utils' +import { sleep, queryKey, createQueryClient } from './utils' import { QueryClient, QueryClientProvider, @@ -16,7 +16,7 @@ describe('QueryClientProvider', () => { const key = queryKey() const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) function Page() { const { data } = useQuery(key, async () => { @@ -49,8 +49,8 @@ describe('QueryClientProvider', () => { const queryCache1 = new QueryCache() const queryCache2 = new QueryCache() - const queryClient1 = new QueryClient({ queryCache: queryCache1 }) - const queryClient2 = new QueryClient({ queryCache: queryCache2 }) + const queryClient1 = createQueryClient({ queryCache: queryCache1 }) + const queryClient2 = createQueryClient({ queryCache: queryCache2 }) function Page1() { const { data } = useQuery(key1, async () => { @@ -101,7 +101,7 @@ describe('QueryClientProvider', () => { const key = queryKey() const queryCache = new QueryCache() - const queryClient = new QueryClient({ + const queryClient = createQueryClient({ queryCache, defaultOptions: { queries: { @@ -155,7 +155,7 @@ describe('QueryClientProvider', () => { test('should use window to get the context when contextSharing is true', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) let queryClientFromHook: QueryClient | undefined let queryClientFromWindow: QueryClient | undefined @@ -182,7 +182,7 @@ describe('QueryClientProvider', () => { test('should not use window to get the context when contextSharing is true and window does not exist', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) // Mock a non web browser environment const windowSpy = jest diff --git a/src/reactjs/tests/QueryResetErrorBoundary.test.tsx b/src/reactjs/tests/QueryResetErrorBoundary.test.tsx index 65d0c10299..90f40a8f10 100644 --- a/src/reactjs/tests/QueryResetErrorBoundary.test.tsx +++ b/src/reactjs/tests/QueryResetErrorBoundary.test.tsx @@ -2,23 +2,17 @@ import { waitFor, fireEvent } from '@testing-library/react' import { ErrorBoundary } from 'react-error-boundary' import React from 'react' -import { sleep, queryKey, mockConsoleError, renderWithClient } from './utils' -import { - useQuery, - QueryClient, - QueryCache, - QueryErrorResetBoundary, -} from '../..' +import { sleep, queryKey, renderWithClient, createQueryClient } from './utils' +import { useQuery, QueryCache, QueryErrorResetBoundary } from '../..' describe('QueryErrorResetBoundary', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) it('should retry fetch if the reset error boundary has been reset', async () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { const { data } = useQuery( @@ -69,15 +63,12 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) - - consoleMock.mockRestore() }) it('should not throw error if query is disabled', async () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { const { data, status } = useQuery( @@ -134,15 +125,12 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('status: error')) - - consoleMock.mockRestore() }) it('should not throw error if query is disabled, and refetch if query becomes enabled again', async () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { const [enabled, setEnabled] = React.useState(false) @@ -200,15 +188,11 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) - - consoleMock.mockRestore() }) it('should throw error if query is disabled and manually refetched', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - function Page() { const { data, refetch, status, fetchStatus } = useQuery( key, @@ -263,15 +247,12 @@ describe('QueryErrorResetBoundary', () => { ) rendered.getByRole('button', { name: /refetch/i }).click() await waitFor(() => rendered.getByText('error boundary')) - - consoleMock.mockRestore() }) it('should not retry fetch if the reset error boundary has not been reset', async () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { const { data } = useQuery( @@ -321,15 +302,12 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('error boundary')) - - consoleMock.mockRestore() }) it('should retry fetch if the reset error boundary has been reset and the query contains data from a previous fetch', async () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { const { data } = useQuery( @@ -381,8 +359,6 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) - - consoleMock.mockRestore() }) it('should not retry fetch if the reset error boundary has not been reset after a previous reset', async () => { @@ -390,7 +366,6 @@ describe('QueryErrorResetBoundary', () => { let succeed = false let shouldReset = true - const consoleMock = mockConsoleError() function Page() { const { data } = useQuery( @@ -449,13 +424,10 @@ describe('QueryErrorResetBoundary', () => { shouldReset = false fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('error boundary')) - - consoleMock.mockRestore() }) it('should throw again on error after the reset error boundary has been reset', async () => { const key = queryKey() - const consoleMock = mockConsoleError() let fetchCount = 0 function Page() { @@ -507,13 +479,10 @@ describe('QueryErrorResetBoundary', () => { fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('error boundary')) expect(fetchCount).toBe(3) - - consoleMock.mockRestore() }) it('should never render the component while the query is in error state', async () => { const key = queryKey() - const consoleMock = mockConsoleError() let fetchCount = 0 let renders = 0 @@ -574,8 +543,6 @@ describe('QueryErrorResetBoundary', () => { await waitFor(() => rendered.getByText('data')) expect(fetchCount).toBe(3) expect(renders).toBe(1) - - consoleMock.mockRestore() }) it('should render children', async () => { @@ -601,7 +568,6 @@ describe('QueryErrorResetBoundary', () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { const { data } = useQuery( @@ -652,7 +618,5 @@ describe('QueryErrorResetBoundary', () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('data')) - - consoleMock.mockRestore() }) }) diff --git a/src/reactjs/tests/logger.native.test.tsx b/src/reactjs/tests/logger.native.test.tsx deleted file mode 100644 index 799f73aabb..0000000000 --- a/src/reactjs/tests/logger.native.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { logger } from '../logger.native' - -describe('logger native', () => { - it('should expose logger properties', () => { - expect(logger).toHaveProperty('log') - expect(logger).toHaveProperty('error') - expect(logger).toHaveProperty('warn') - }) -}) diff --git a/src/reactjs/tests/ssr-hydration.test.tsx b/src/reactjs/tests/ssr-hydration.test.tsx index d165543f28..3ea1111a22 100644 --- a/src/reactjs/tests/ssr-hydration.test.tsx +++ b/src/reactjs/tests/ssr-hydration.test.tsx @@ -4,14 +4,13 @@ import ReactDOMServer from 'react-dom/server' import { useQuery, - QueryClient, QueryClientProvider, QueryCache, dehydrate, hydrate, } from '../..' import * as utils from '../../core/utils' -import { mockConsoleError, sleep } from './utils' +import { createQueryClient, mockLogger, sleep } from './utils' // This monkey-patches the isServer-value from utils, // so that we can pretend to be in a server environment @@ -31,7 +30,6 @@ function PrintStateComponent({ componentName, result }: any): any { describe('Server side rendering with de/rehydration', () => { it('should not mismatch on success', async () => { - const consoleMock = mockConsoleError() const fetchDataSuccess = jest.fn(fetchData) // -- Shared part -- @@ -46,13 +44,17 @@ describe('Server side rendering with de/rehydration', () => { setIsServer(true) const prefetchCache = new QueryCache() - const prefetchClient = new QueryClient({ queryCache: prefetchCache }) + const prefetchClient = createQueryClient({ + queryCache: prefetchCache, + }) await prefetchClient.prefetchQuery(['success'], () => fetchDataSuccess('success') ) const dehydratedStateServer = dehydrate(prefetchClient) const renderCache = new QueryCache() - const renderClient = new QueryClient({ queryCache: renderCache }) + const renderClient = createQueryClient({ + queryCache: renderCache, + }) hydrate(renderClient, dehydratedStateServer) const markup = ReactDOMServer.renderToString( @@ -73,7 +75,7 @@ describe('Server side rendering with de/rehydration', () => { el.innerHTML = markup const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) hydrate(queryClient, JSON.parse(stringifiedState)) ReactDOM.hydrate( @@ -84,17 +86,15 @@ describe('Server side rendering with de/rehydration', () => { ) // Check that we have no React hydration mismatches - expect(consoleMock).not.toHaveBeenCalled() + expect(mockLogger.error).not.toHaveBeenCalled() expect(fetchDataSuccess).toHaveBeenCalledTimes(1) expect(el.innerHTML).toBe(expectedMarkup) ReactDOM.unmountComponentAtNode(el) - consoleMock.mockRestore() queryClient.clear() }) it('should not mismatch on error', async () => { - const consoleMock = mockConsoleError() const fetchDataError = jest.fn(() => { throw new Error('fetchDataError') }) @@ -112,11 +112,15 @@ describe('Server side rendering with de/rehydration', () => { // -- Server part -- setIsServer(true) const prefetchCache = new QueryCache() - const prefetchClient = new QueryClient({ queryCache: prefetchCache }) + const prefetchClient = createQueryClient({ + queryCache: prefetchCache, + }) await prefetchClient.prefetchQuery(['error'], () => fetchDataError()) const dehydratedStateServer = dehydrate(prefetchClient) const renderCache = new QueryCache() - const renderClient = new QueryClient({ queryCache: renderCache }) + const renderClient = createQueryClient({ + queryCache: renderCache, + }) hydrate(renderClient, dehydratedStateServer) const markup = ReactDOMServer.renderToString( @@ -137,7 +141,7 @@ describe('Server side rendering with de/rehydration', () => { el.innerHTML = markup const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) hydrate(queryClient, JSON.parse(stringifiedState)) ReactDOM.hydrate( @@ -148,7 +152,7 @@ describe('Server side rendering with de/rehydration', () => { ) // We expect exactly one console.error here, which is from the - expect(consoleMock).toHaveBeenCalledTimes(1) + expect(mockLogger.error).toHaveBeenCalledTimes(1) expect(fetchDataError).toHaveBeenCalledTimes(1) expect(el.innerHTML).toBe(expectedMarkup) await sleep(50) @@ -158,12 +162,10 @@ describe('Server side rendering with de/rehydration', () => { ) ReactDOM.unmountComponentAtNode(el) - consoleMock.mockRestore() queryClient.clear() }) it('should not mismatch on queries that were not prefetched', async () => { - const consoleMock = mockConsoleError() const fetchDataSuccess = jest.fn(fetchData) // -- Shared part -- @@ -178,10 +180,14 @@ describe('Server side rendering with de/rehydration', () => { setIsServer(true) const prefetchCache = new QueryCache() - const prefetchClient = new QueryClient({ queryCache: prefetchCache }) + const prefetchClient = createQueryClient({ + queryCache: prefetchCache, + }) const dehydratedStateServer = dehydrate(prefetchClient) const renderCache = new QueryCache() - const renderClient = new QueryClient({ queryCache: renderCache }) + const renderClient = createQueryClient({ + queryCache: renderCache, + }) hydrate(renderClient, dehydratedStateServer) const markup = ReactDOMServer.renderToString( @@ -202,7 +208,7 @@ describe('Server side rendering with de/rehydration', () => { el.innerHTML = markup const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) hydrate(queryClient, JSON.parse(stringifiedState)) ReactDOM.hydrate( @@ -213,7 +219,7 @@ describe('Server side rendering with de/rehydration', () => { ) // Check that we have no React hydration mismatches - expect(consoleMock).not.toHaveBeenCalled() + expect(mockLogger.error).not.toHaveBeenCalled() expect(fetchDataSuccess).toHaveBeenCalledTimes(0) expect(el.innerHTML).toBe(expectedMarkup) await sleep(50) @@ -223,7 +229,6 @@ describe('Server side rendering with de/rehydration', () => { ) ReactDOM.unmountComponentAtNode(el) - consoleMock.mockRestore() queryClient.clear() }) }) diff --git a/src/reactjs/tests/ssr.test.tsx b/src/reactjs/tests/ssr.test.tsx index c136e42f72..7259a3f960 100644 --- a/src/reactjs/tests/ssr.test.tsx +++ b/src/reactjs/tests/ssr.test.tsx @@ -6,10 +6,9 @@ import React from 'react' // @ts-ignore import { renderToString } from 'react-dom/server' -import { sleep, queryKey } from './utils' +import { sleep, queryKey, createQueryClient } from './utils' import { useQuery, - QueryClient, QueryClientProvider, QueryCache, useInfiniteQuery, @@ -18,7 +17,7 @@ import { describe('Server Side Rendering', () => { it('should not trigger fetch', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key = queryKey() const queryFn = jest.fn().mockReturnValue('data') @@ -47,7 +46,7 @@ describe('Server Side Rendering', () => { it('should add prefetched data to cache', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key = queryKey() const fetchFn = () => Promise.resolve('data') const data = await queryClient.fetchQuery(key, fetchFn) @@ -58,7 +57,7 @@ describe('Server Side Rendering', () => { it('should return existing data from the cache', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key = queryKey() const queryFn = jest.fn(() => { sleep(10) @@ -94,7 +93,7 @@ describe('Server Side Rendering', () => { const key = queryKey() const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) function Page() { const [page, setPage] = React.useState(1) @@ -124,7 +123,7 @@ describe('Server Side Rendering', () => { it('useInfiniteQuery should return the correct state', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key = queryKey() const queryFn = jest.fn(async () => { await sleep(5) diff --git a/src/reactjs/tests/suspense.test.tsx b/src/reactjs/tests/suspense.test.tsx index 6bf53b56ea..c913bef0a9 100644 --- a/src/reactjs/tests/suspense.test.tsx +++ b/src/reactjs/tests/suspense.test.tsx @@ -2,10 +2,9 @@ import { waitFor, fireEvent } from '@testing-library/react' import { ErrorBoundary } from 'react-error-boundary' import React from 'react' -import { sleep, queryKey, mockConsoleError, renderWithClient } from './utils' +import { sleep, queryKey, renderWithClient, createQueryClient } from './utils' import { useQuery, - QueryClient, QueryCache, QueryErrorResetBoundary, useQueryErrorResetBoundary, @@ -16,7 +15,7 @@ import { describe("useQuery's in Suspense mode", () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) it('should render the correct amount of times in Suspense mode', async () => { const key = queryKey() @@ -265,7 +264,6 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { useQuery( @@ -325,15 +323,12 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('rendered')) - - consoleMock.mockRestore() }) it('should retry fetch if the reset error boundary has been reset', async () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { useQuery( @@ -390,8 +385,6 @@ describe("useQuery's in Suspense mode", () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('rendered')) - - consoleMock.mockRestore() }) it('should refetch when re-mounting', async () => { @@ -506,7 +499,6 @@ describe("useQuery's in Suspense mode", () => { const key = queryKey() let succeed = false - const consoleMock = mockConsoleError() function Page() { useQuery( @@ -563,15 +555,11 @@ describe("useQuery's in Suspense mode", () => { succeed = true fireEvent.click(rendered.getByText('retry')) await waitFor(() => rendered.getByText('rendered')) - - consoleMock.mockRestore() }) it('should throw errors to the error boundary by default', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - function Page() { useQuery( key, @@ -607,15 +595,11 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText('error boundary')) - - consoleMock.mockRestore() }) it('should not throw errors to the error boundary when useErrorBoundary: false', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - function Page() { useQuery( key, @@ -652,15 +636,11 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText('rendered')) - - consoleMock.mockRestore() }) it('should not throw errors to the error boundary when a useErrorBoundary function returns true', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - function Page() { useQuery( key, @@ -697,15 +677,11 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText('error boundary')) - - consoleMock.mockRestore() }) it('should not throw errors to the error boundary when a useErrorBoundary function returns false', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - function Page() { useQuery( key, @@ -742,8 +718,6 @@ describe("useQuery's in Suspense mode", () => { await waitFor(() => rendered.getByText('Loading...')) await waitFor(() => rendered.getByText('rendered')) - - consoleMock.mockRestore() }) it('should not call the queryFn when not enabled', async () => { @@ -777,8 +751,6 @@ describe("useQuery's in Suspense mode", () => { it('should error catched in error boundary without infinite loop', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - let succeed = true function Page() { @@ -841,12 +813,9 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByLabelText('fail')) // render error boundary fallback (error boundary) await waitFor(() => rendered.getByText('error boundary')) - consoleMock.mockRestore() }) it('should error catched in error boundary without infinite loop when query keys changed', async () => { - const consoleMock = mockConsoleError() - let succeed = true function Page() { @@ -905,12 +874,9 @@ describe("useQuery's in Suspense mode", () => { fireEvent.click(rendered.getByLabelText('fail')) // render error boundary fallback (error boundary) await waitFor(() => rendered.getByText('error boundary')) - consoleMock.mockRestore() }) it('should error catched in error boundary without infinite loop when enabled changed', async () => { - const consoleMock = mockConsoleError() - const succeed = false function Page() { @@ -975,7 +941,6 @@ describe("useQuery's in Suspense mode", () => { // render error boundary fallback (error boundary) await waitFor(() => rendered.getByText('error boundary')) - consoleMock.mockRestore() }) it('should render the correct amount of times in Suspense mode when cacheTime is set to 0', async () => { diff --git a/src/reactjs/tests/useInfiniteQuery.test.tsx b/src/reactjs/tests/useInfiniteQuery.test.tsx index a9480415a6..411f6af727 100644 --- a/src/reactjs/tests/useInfiniteQuery.test.tsx +++ b/src/reactjs/tests/useInfiniteQuery.test.tsx @@ -4,15 +4,14 @@ import React from 'react' import { queryKey, sleep, - mockConsoleError, renderWithClient, setActTimeout, Blink, + createQueryClient, } from './utils' import { useInfiniteQuery, UseInfiniteQueryResult, - QueryClient, QueryCache, QueryFunctionContext, InfiniteData, @@ -44,7 +43,7 @@ const fetchItems = async ( describe('useInfiniteQuery', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) it('should return the correct states for a successful query', async () => { const key = queryKey() @@ -131,7 +130,6 @@ describe('useInfiniteQuery', () => { }) it('should not throw when fetchNextPage returns an error', async () => { - const consoleMock = mockConsoleError() const key = queryKey() let noThrow: boolean @@ -170,7 +168,6 @@ describe('useInfiniteQuery', () => { renderWithClient(queryClient, ) await waitFor(() => expect(noThrow).toBe(true)) - consoleMock.mockRestore() }) it('should keep the previous data when keepPreviousData is set', async () => { diff --git a/src/reactjs/tests/useIsFetching.test.tsx b/src/reactjs/tests/useIsFetching.test.tsx index 085242cbfd..9c91ca25bd 100644 --- a/src/reactjs/tests/useIsFetching.test.tsx +++ b/src/reactjs/tests/useIsFetching.test.tsx @@ -2,19 +2,20 @@ import { fireEvent, waitFor } from '@testing-library/react' import React from 'react' import { - mockConsoleError, + createQueryClient, + mockLogger, queryKey, renderWithClient, setActTimeout, sleep, } from './utils' -import { useQuery, useIsFetching, QueryClient, QueryCache } from '../..' +import { useQuery, useIsFetching, QueryCache } from '../..' describe('useIsFetching', () => { // See https://github.com/tannerlinsley/react-query/issues/105 it('should update as queries start and stop fetching', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key = queryKey() function Page() { @@ -50,9 +51,8 @@ describe('useIsFetching', () => { }) it('should not update state while rendering', async () => { - const consoleMock = mockConsoleError() const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key1 = queryKey() const key2 = queryKey() @@ -101,15 +101,12 @@ describe('useIsFetching', () => { renderWithClient(queryClient, ) await waitFor(() => expect(isFetchings).toEqual([0, 0, 1, 2, 1, 0])) - expect(consoleMock).not.toHaveBeenCalled() - expect(consoleMock.mock.calls[0]?.[0] ?? '').not.toMatch('setState') - - consoleMock.mockRestore() + expect(mockLogger.error).not.toHaveBeenCalled() }) it('should be able to filter', async () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) const key1 = queryKey() const key2 = queryKey() diff --git a/src/reactjs/tests/useIsMutating.test.tsx b/src/reactjs/tests/useIsMutating.test.tsx index 33af902593..19212513d7 100644 --- a/src/reactjs/tests/useIsMutating.test.tsx +++ b/src/reactjs/tests/useIsMutating.test.tsx @@ -1,15 +1,19 @@ import { waitFor, fireEvent } from '@testing-library/react' import React from 'react' -import { QueryClient } from '../../core' import { useIsMutating } from '../useIsMutating' import { useMutation } from '../useMutation' -import { renderWithClient, setActTimeout, sleep } from './utils' +import { + createQueryClient, + renderWithClient, + setActTimeout, + sleep, +} from './utils' import * as MutationCacheModule from '../../core/mutationCache' describe('useIsMutating', () => { it('should return the number of fetching mutations', async () => { const isMutatings: number[] = [] - const queryClient = new QueryClient() + const queryClient = createQueryClient() function IsMutating() { const isMutating = useIsMutating() @@ -43,7 +47,7 @@ describe('useIsMutating', () => { it('should filter correctly by mutationKey', async () => { const isMutatings: number[] = [] - const queryClient = new QueryClient() + const queryClient = createQueryClient() function IsMutating() { const isMutating = useIsMutating(['mutation1']) @@ -75,7 +79,7 @@ describe('useIsMutating', () => { it('should filter correctly by predicate', async () => { const isMutatings: number[] = [] - const queryClient = new QueryClient() + const queryClient = createQueryClient() function IsMutating() { const isMutating = useIsMutating({ @@ -124,7 +128,7 @@ describe('useIsMutating', () => { return new MutationCacheMock(fn) }) - const queryClient = new QueryClient() + const queryClient = createQueryClient() function IsMutating() { useIsMutating() diff --git a/src/reactjs/tests/useMutation.test.tsx b/src/reactjs/tests/useMutation.test.tsx index 0cab2ec28b..8bf5e06b33 100644 --- a/src/reactjs/tests/useMutation.test.tsx +++ b/src/reactjs/tests/useMutation.test.tsx @@ -3,10 +3,10 @@ import '@testing-library/jest-dom' import React from 'react' import { ErrorBoundary } from 'react-error-boundary' -import { useMutation, QueryClient, QueryCache, MutationCache } from '../..' +import { useMutation, QueryCache, MutationCache } from '../..' import { UseMutationResult } from '../types' import { - mockConsoleError, + createQueryClient, mockNavigatorOnLine, queryKey, renderWithClient, @@ -17,7 +17,7 @@ import { describe('useMutation', () => { const queryCache = new QueryCache() const mutationCache = new MutationCache() - const queryClient = new QueryClient({ queryCache, mutationCache }) + const queryClient = createQueryClient({ queryCache, mutationCache }) it('should be able to reset `data`', async () => { function Page() { @@ -52,8 +52,6 @@ describe('useMutation', () => { }) it('should be able to reset `error`', async () => { - const consoleMock = mockConsoleError() - function Page() { const { mutate, error, reset } = useMutation(() => { const err = new Error('Expected mock error. All is well!') @@ -88,8 +86,6 @@ describe('useMutation', () => { fireEvent.click(getByText('reset')) await waitFor(() => expect(queryByTestId('error')).toBeNull()) - - consoleMock.mockRestore() }) it('should be able to call `onSuccess` and `onSettled` after each successful mutate', async () => { @@ -142,8 +138,6 @@ describe('useMutation', () => { }) it('should be able to call `onError` and `onSettled` after each failed mutate', async () => { - const consoleMock = mockConsoleError() - const onErrorMock = jest.fn() const onSettledMock = jest.fn() let count = 0 @@ -208,8 +202,6 @@ describe('useMutation', () => { ) expect(getByTestId('title').textContent).toBe('3') - - consoleMock.mockRestore() }) it('should be able to override the useMutation success callbacks', async () => { @@ -258,8 +250,6 @@ describe('useMutation', () => { }) it('should be able to override the error callbacks when using mutateAsync', async () => { - const consoleMock = mockConsoleError() - const callbacks: string[] = [] function Page() { @@ -306,8 +296,6 @@ describe('useMutation', () => { 'mutateAsync.onSettled', 'mutateAsync.error:oops', ]) - - consoleMock.mockRestore() }) it('should be able to use mutation defaults', async () => { @@ -346,8 +334,6 @@ describe('useMutation', () => { }) it('should be able to retry a failed mutation', async () => { - const consoleMock = mockConsoleError() - let count = 0 function Page() { @@ -376,12 +362,9 @@ describe('useMutation', () => { await sleep(100) expect(count).toBe(2) - - consoleMock.mockRestore() }) it('should not retry mutations while offline', async () => { - const consoleMock = mockConsoleError() const onlineMock = mockNavigatorOnLine(false) let count = 0 @@ -441,7 +424,6 @@ describe('useMutation', () => { expect(count).toBe(2) - consoleMock.mockRestore() onlineMock.mockRestore() }) @@ -541,7 +523,6 @@ describe('useMutation', () => { }) it('should be able to retry a mutation when online', async () => { - const consoleMock = mockConsoleError() const onlineMock = mockNavigatorOnLine(false) let count = 0 @@ -617,7 +598,6 @@ describe('useMutation', () => { data: 'data', }) - consoleMock.mockRestore() onlineMock.mockRestore() }) @@ -642,8 +622,6 @@ describe('useMutation', () => { }) it('should be able to throw an error when useErrorBoundary is set to true', async () => { - const consoleMock = mockConsoleError() - function Page() { const { mutate } = useMutation( () => { @@ -679,17 +657,13 @@ describe('useMutation', () => { await waitFor(() => { expect(queryByText('error')).not.toBeNull() }) - - consoleMock.mockRestore() }) it('should pass meta to mutation', async () => { - const consoleMock = mockConsoleError() - const errorMock = jest.fn() const successMock = jest.fn() - const queryClientMutationMeta = new QueryClient({ + const queryClientMutationMeta = createQueryClient({ mutationCache: new MutationCache({ onSuccess: (_, __, ___, mutation) => { successMock(mutation.meta?.metaSuccessMessage) @@ -743,8 +717,6 @@ describe('useMutation', () => { expect(successMock).toHaveBeenCalledWith(metaSuccessMessage) expect(errorMock).toHaveBeenCalledTimes(1) expect(errorMock).toHaveBeenCalledWith(metaErrorMessage) - - consoleMock.mockRestore() }) it('should call cache callbacks when unmounted', async () => { diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index 4a0bb9d286..3ac5e19556 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -4,6 +4,7 @@ import React from 'react' import * as QueriesObserverModule from '../../core/queriesObserver' import { + createQueryClient, expectType, expectTypeNotAny, queryKey, @@ -13,7 +14,6 @@ import { } from './utils' import { useQueries, - QueryClient, UseQueryResult, QueryCache, QueryObserverResult, @@ -26,7 +26,7 @@ import { QueryFunctionContext } from '../../core' describe('useQueries', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) it('should return the correct states', async () => { const key1 = queryKey() diff --git a/src/reactjs/tests/useQuery.test.tsx b/src/reactjs/tests/useQuery.test.tsx index 5e99ae409b..6951a81e0c 100644 --- a/src/reactjs/tests/useQuery.test.tsx +++ b/src/reactjs/tests/useQuery.test.tsx @@ -6,16 +6,16 @@ import { expectType, queryKey, mockVisibilityState, - mockConsoleError, sleep, renderWithClient, setActTimeout, Blink, mockNavigatorOnLine, + mockLogger, + createQueryClient, } from './utils' import { useQuery, - QueryClient, UseQueryResult, QueryCache, QueryFunction, @@ -25,7 +25,7 @@ import { ErrorBoundary } from 'react-error-boundary' describe('useQuery', () => { const queryCache = new QueryCache() - const queryClient = new QueryClient({ queryCache }) + const queryClient = createQueryClient({ queryCache }) it('should return the correct types', () => { const key = queryKey() @@ -215,7 +215,6 @@ describe('useQuery', () => { it('should return the correct states for an unsuccessful query', async () => { const key = queryKey() - const consoleMock = mockConsoleError() const states: UseQueryResult[] = [] @@ -317,8 +316,6 @@ describe('useQuery', () => { status: 'error', fetchStatus: 'idle', }) - - consoleMock.mockRestore() }) it('should set isFetchedAfterMount to true after a query has been fetched', async () => { @@ -467,7 +464,6 @@ describe('useQuery', () => { const key = queryKey() const states: UseQueryResult[] = [] const onError = jest.fn() - const consoleMock = mockConsoleError() function Page() { const state = useQuery(key, () => Promise.reject('error'), { @@ -484,13 +480,11 @@ describe('useQuery', () => { expect(states.length).toBe(2) expect(onError).toHaveBeenCalledTimes(1) expect(onError).toHaveBeenCalledWith('error') - consoleMock.mockRestore() }) it('should not call onError when receiving a CancelledError', async () => { const key = queryKey() const onError = jest.fn() - const consoleMock = mockConsoleError() function Page() { useQuery( @@ -511,7 +505,6 @@ describe('useQuery', () => { await sleep(5) await queryClient.cancelQueries(key) expect(onError).not.toHaveBeenCalled() - consoleMock.mockRestore() }) it('should call onSettled after a query has been fetched', async () => { @@ -537,7 +530,6 @@ describe('useQuery', () => { const key = queryKey() const states: UseQueryResult[] = [] const onSettled = jest.fn() - const consoleMock = mockConsoleError() function Page() { const state = useQuery(key, () => Promise.reject('error'), { @@ -554,7 +546,6 @@ describe('useQuery', () => { expect(states.length).toBe(2) expect(onSettled).toHaveBeenCalledTimes(1) expect(onSettled).toHaveBeenCalledWith(undefined, 'error') - consoleMock.mockRestore() }) it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => { @@ -924,7 +915,6 @@ describe('useQuery', () => { }) it('should throw an error when a selector throws', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const states: UseQueryResult[] = [] const error = new Error('Select Error') @@ -943,13 +933,11 @@ describe('useQuery', () => { await sleep(10) - expect(consoleMock).toHaveBeenCalledWith(error) + expect(mockLogger.error).toHaveBeenCalledWith(error) expect(states.length).toBe(2) expect(states[0]).toMatchObject({ status: 'loading', data: undefined }) expect(states[1]).toMatchObject({ status: 'error', error }) - - consoleMock.mockRestore() }) it('should track properties and only re-render when a tracked property changes', async () => { @@ -1461,7 +1449,6 @@ describe('useQuery', () => { it('should transition to error state when keepPreviousData is set', async () => { const key = queryKey() - const consoleMock = mockConsoleError() const states: UseQueryResult[] = [] function Page({ count }: { count: number }) { @@ -1562,8 +1549,6 @@ describe('useQuery', () => { isPreviousData: false, }) expect(states[7]?.error).toHaveProperty('message', 'Error test') - - consoleMock.mockRestore() }) it('should not show initial data from next query if keepPreviousData is set', async () => { @@ -2598,7 +2583,6 @@ describe('useQuery', () => { it('should set status to error if queryFn throws', async () => { const key = queryKey() - const consoleMock = mockConsoleError() function Page() { const { status, error } = useQuery( @@ -2621,13 +2605,10 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('error')) await waitFor(() => rendered.getByText('Error test jaylen')) - - consoleMock.mockRestore() }) it('should throw error if queryFn throws and useErrorBoundary is in use', async () => { const key = queryKey() - const consoleMock = mockConsoleError() function Page() { const { status, error } = useQuery( @@ -2652,8 +2633,6 @@ describe('useQuery', () => { ) await waitFor(() => rendered.getByText('error boundary')) - - consoleMock.mockRestore() }) it('should update with data if we observe no properties and useErrorBoundary', async () => { @@ -2682,7 +2661,6 @@ describe('useQuery', () => { it('should set status to error instead of throwing when error should not be thrown', async () => { const key = queryKey() - const consoleMock = mockConsoleError() function Page() { const { status, error } = useQuery( @@ -2711,13 +2689,10 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('error')) await waitFor(() => rendered.getByText('Local Error')) - - consoleMock.mockRestore() }) it('should throw error instead of setting status when error should be thrown', async () => { const key = queryKey() - const consoleMock = mockConsoleError() function Page() { const { status, error } = useQuery( @@ -2753,13 +2728,10 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('error boundary')) await waitFor(() => rendered.getByText('Remote Error')) - - consoleMock.mockRestore() }) it('should continue retries when observers unmount and remount while waiting for a retry (#3031)', async () => { const key = queryKey() - const consoleMock = mockConsoleError() let count = 0 function Page() { @@ -2803,13 +2775,10 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('error: some error')) expect(count).toBe(3) - - consoleMock.mockRestore() }) it('should restart when observers unmount and remount while waiting for a retry when query was cancelled in between (#3031)', async () => { const key = queryKey() - const consoleMock = mockConsoleError() let count = 0 function Page() { @@ -2858,8 +2827,6 @@ describe('useQuery', () => { // initial fetch (1), which will be cancelled, followed by new mount(2) + 2 retries = 4 expect(count).toBe(4) - - consoleMock.mockRestore() }) it('should always fetch if refetchOnMount is set to always', async () => { @@ -3061,7 +3028,6 @@ describe('useQuery', () => { it('should retry specified number of times', async () => { const key = queryKey() - const consoleMock = mockConsoleError() const queryFn = jest.fn() queryFn.mockImplementation(() => { @@ -3091,14 +3057,11 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('Failed 2 times')) expect(queryFn).toHaveBeenCalledTimes(2) - consoleMock.mockRestore() }) it('should not retry if retry function `false`', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - const queryFn = jest.fn() queryFn.mockImplementationOnce(() => { @@ -3137,12 +3100,10 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('NoRetry')) expect(queryFn).toHaveBeenCalledTimes(2) - consoleMock.mockRestore() }) it('should extract retryDelay from error', async () => { const key = queryKey() - const consoleMock = mockConsoleError() type DelayError = { delay: number } @@ -3174,15 +3135,12 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('Failed 2 times')) expect(queryFn).toHaveBeenCalledTimes(2) - consoleMock.mockRestore() }) // See https://github.com/tannerlinsley/react-query/issues/160 it('should continue retry after focus regain', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - // make page unfocused const visibilityMock = mockVisibilityState('hidden') @@ -3237,9 +3195,7 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('failureCount 4')) // Check if the error has been logged in the console - expect(consoleMock).toHaveBeenCalledWith('fetching error 4') - - consoleMock.mockRestore() + expect(mockLogger.error).toHaveBeenCalledWith('fetching error 4') }) it('should fetch on mount when a query was already created with setQueryData', async () => { @@ -3276,7 +3232,6 @@ describe('useQuery', () => { it('should refetch after focus regain', async () => { const key = queryKey() const states: UseQueryResult[] = [] - const consoleMock = mockConsoleError() // make page unfocused const visibilityMock = mockVisibilityState('hidden') @@ -3327,8 +3282,6 @@ describe('useQuery', () => { isStale: true, }, ]) - - consoleMock.mockRestore() }) // See https://github.com/tannerlinsley/react-query/issues/195 @@ -3399,8 +3352,6 @@ describe('useQuery', () => { it('should reset failureCount on successful fetch', async () => { const key = queryKey() - const consoleMock = mockConsoleError() - function Page() { let counter = 0 @@ -3428,8 +3379,6 @@ describe('useQuery', () => { await waitFor(() => rendered.getByText('failureCount 2')) await waitFor(() => rendered.getByText('failureCount 0')) - - consoleMock.mockRestore() }) // See https://github.com/tannerlinsley/react-query/issues/199 @@ -4503,8 +4452,6 @@ describe('useQuery', () => { }) it('should refetch when changed enabled to true in error state', async () => { - const consoleMock = mockConsoleError() - const queryFn = jest.fn() queryFn.mockImplementation(async () => { await sleep(10) @@ -4559,13 +4506,9 @@ describe('useQuery', () => { // // change to enabled to true fireEvent.click(rendered.getByLabelText('retry')) expect(queryFn).toBeCalledTimes(2) - - consoleMock.mockRestore() }) it('should refetch when query key changed when previous status is error', async () => { - const consoleMock = mockConsoleError() - function Page({ id }: { id: number }) { const { error, isLoading } = useQuery( [id], @@ -4622,13 +4565,9 @@ describe('useQuery', () => { // change to mount new query fireEvent.click(rendered.getByLabelText('change')) await waitFor(() => rendered.getByText('error')) - - consoleMock.mockRestore() }) it('should refetch when query key changed when switching between erroneous queries', async () => { - const consoleMock = mockConsoleError() - function Page({ id }: { id: boolean }) { const { error, isFetching } = useQuery( [id], @@ -4683,12 +4622,9 @@ describe('useQuery', () => { fireEvent.click(rendered.getByLabelText('change')) await waitFor(() => rendered.getByText('status: fetching')) await waitFor(() => rendered.getByText('error')) - - consoleMock.mockRestore() }) it('should have no error in loading state when refetching after error occurred', async () => { - const consoleMock = mockConsoleError() const key = queryKey() const states: UseQueryResult[] = [] const error = new Error('oops') @@ -4759,8 +4695,6 @@ describe('useQuery', () => { data: 5, error: null, }) - - consoleMock.mockRestore() }) describe('networkMode online', () => { @@ -5103,7 +5037,6 @@ describe('useQuery', () => { it('online queries should pause retries if you are offline', async () => { const key = queryKey() - const consoleMock = mockConsoleError() let count = 0 function Page() { @@ -5158,7 +5091,6 @@ describe('useQuery', () => { expect(count).toBe(3) onlineMock.mockRestore() - consoleMock.mockRestore() }) it('online queries should fetch if paused and we go online even if already unmounted (because not cancelled)', async () => { @@ -5395,7 +5327,6 @@ describe('useQuery', () => { it('always queries should not pause retries', async () => { const onlineMock = mockNavigatorOnLine(false) - const consoleMock = mockConsoleError() const key = queryKey() let count = 0 @@ -5435,14 +5366,12 @@ describe('useQuery', () => { expect(count).toBe(2) - consoleMock.mockRestore() onlineMock.mockRestore() }) }) describe('networkMode offlineFirst', () => { it('offlineFirst queries should start fetching if you are offline, but pause retries', async () => { - const consoleMock = mockConsoleError() const onlineMock = mockNavigatorOnLine(false) const key = queryKey() @@ -5491,7 +5420,6 @@ describe('useQuery', () => { expect(count).toBe(3) onlineMock.mockRestore() - consoleMock.mockRestore() }) }) }) diff --git a/src/reactjs/tests/utils.tsx b/src/reactjs/tests/utils.tsx index 39d41cb36b..e0add20f32 100644 --- a/src/reactjs/tests/utils.tsx +++ b/src/reactjs/tests/utils.tsx @@ -1,7 +1,16 @@ import { act, render } from '@testing-library/react' import React from 'react' -import { MutationOptions, QueryClient, QueryClientProvider } from '../..' +import { + MutationOptions, + QueryClient, + QueryClientConfig, + QueryClientProvider, +} from '../..' + +export function createQueryClient(config?: QueryClientConfig): QueryClient { + return new QueryClient({ logger: mockLogger, ...config }) +} export function renderWithClient(client: QueryClient, ui: React.ReactElement) { const { rerender, ...result } = render( @@ -24,10 +33,10 @@ export function mockNavigatorOnLine(value: boolean) { return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) } -export function mockConsoleError() { - const consoleMock = jest.spyOn(console, 'error') - consoleMock.mockImplementation(() => undefined) - return consoleMock +export const mockLogger = { + log: jest.fn(), + warn: jest.fn(), + error: jest.fn(), } let queryKeyCount = 0 From 4253ff3f715bea277eccfebee5daf62320fbe8db Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 26 Feb 2022 16:34:20 +0100 Subject: [PATCH 27/76] refactor: turn off error logging in tests the global `clearMocks: true` should make sure that this gets reset after every test --- src/reactjs/tests/utils.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/reactjs/tests/utils.tsx b/src/reactjs/tests/utils.tsx index e0add20f32..73851a98b7 100644 --- a/src/reactjs/tests/utils.tsx +++ b/src/reactjs/tests/utils.tsx @@ -9,6 +9,7 @@ import { } from '../..' export function createQueryClient(config?: QueryClientConfig): QueryClient { + jest.spyOn(console, 'error').mockImplementation(() => undefined) return new QueryClient({ logger: mockLogger, ...config }) } From 61fd5397a31e83c3ac274c19f4baef6f57ae32a5 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 26 Feb 2022 17:14:43 +0100 Subject: [PATCH 28/76] docs(examples): fix optimistic-updates-typescript example --- .../pages/index.tsx | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/examples/optimistic-updates-typescript/pages/index.tsx b/examples/optimistic-updates-typescript/pages/index.tsx index 5113d51520..29bcd91b7b 100755 --- a/examples/optimistic-updates-typescript/pages/index.tsx +++ b/examples/optimistic-updates-typescript/pages/index.tsx @@ -26,25 +26,8 @@ async function fetchTodos(): Promise { return res.data } -function useTodos( - options?: UseQueryOptions -) { - return useQuery(['todos'], fetchTodos, options) -} - -function TodoCounter() { - // subscribe only to changes in the 'data' prop, which will be the - // amount of todos because of the select function - const counterQuery = useTodos({ - select: data => data.items.length, - notifyOnChangeProps: ['data'], - }) - - React.useEffect(() => { - console.log('rendering counter') - }) - - return
TodoCounter: {counterQuery.data ?? 0}
+function useTodos() { + return useQuery(['todos'], fetchTodos) } function Example() { @@ -129,7 +112,7 @@ function Example() { )} {queryInfo.isLoading && 'Loading'} - {queryInfo.error?.message} + {queryInfo.error instanceof Error && queryInfo.error.message}
) } @@ -138,7 +121,6 @@ export default function App() { return ( - ) From ead3d69949b9ac2b126dd260d15c8c4e249b7e70 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 26 Feb 2022 19:25:44 +0100 Subject: [PATCH 29/76] chore(bundling): bundle for modern browsers (#3342) * chore(bundling): update babel dependencies * chore(bundling): remove babel-plugin-transform-async-to-promises we don't use async functions, and we also want to target envs that support async functions * chore(bundling): update supported browsers for a more modern output * chore(bundling): tweak browserslist and exclude transform-parameters plugin, which comes in because of any safari version, even though it should not be needed according to caniuse. This plugin transforms ES2015 parameters to ES5, this includes: Destructuring parameters, Default parameters and Rest parameters, all of which are supported by Safari * chore(bundling): update terser * chore(bundling): fix tests ReferenceError: Cannot access 'promiseResolve' before initialization * chore(bundling): docs about supported browsers * chore(bundling): small docs fixes --- .babelrc.js | 8 +- .browserslistrc | 11 +- .../guides/migrating-to-react-query-4.md | 4 + docs/src/pages/installation.md | 15 + package.json | 19 +- src/core/retryer.ts | 7 +- yarn.lock | 1832 +++++++---------- 7 files changed, 840 insertions(+), 1056 deletions(-) diff --git a/.babelrc.js b/.babelrc.js index 6218e87224..c368ebb657 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -5,18 +5,20 @@ const loose = true module.exports = { presets: [ [ - '@babel/env', + '@babel/preset-env', { loose, modules: false, - exclude: ['@babel/plugin-transform-regenerator'], + exclude: [ + '@babel/plugin-transform-regenerator', + '@babel/plugin-transform-parameters', + ], }, ], '@babel/preset-typescript', '@babel/react', ], plugins: [ - 'babel-plugin-transform-async-to-promises', cjs && ['@babel/transform-modules-commonjs', { loose }], [ '@babel/transform-runtime', diff --git a/.browserslistrc b/.browserslistrc index 914ab12d22..01b924286a 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,8 +1,7 @@ # Browsers we support -> 0.5% Chrome >= 73 -ChromeAndroid >= 75 -Firefox >= 67 -Edge >= 17 -Safari >= 12.1 -iOS >= 11.3 +Firefox >= 78 +Edge >= 79 +Safari >= 12.0 +iOS >= 12.0 +opera >= 53 diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index a5a77d2e39..3d0d580b31 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -293,6 +293,10 @@ useQuery( This is now disallowed on type level; at runtime, `undefined` will be transformed to a _failed Promise_, which means you will get an `error`, which will also be logged to the console in development mode. +### Supported Browsers + +As of v4, React Query is optimized for modern browsers. We have updated our browserslist to produce a more modern, performant and smaller bundle. You can read about the requirements [here](../installation#requirements). + ## New Features 🚀 ### Proper offline support diff --git a/docs/src/pages/installation.md b/docs/src/pages/installation.md index a8953babb8..7017251b1d 100644 --- a/docs/src/pages/installation.md +++ b/docs/src/pages/installation.md @@ -30,3 +30,18 @@ If you're not using a module bundler or package manager we also have a global (" Once you've added this you will have access to the `window.ReactQuery` object and its exports. > This installation/usage requires the [React CDN script bundles](https://reactjs.org/docs/cdn-links.html) to be on the page as well. + +### Requirements + +React Query is optimized for modern browsers. It is compatible with the following browsers config + +``` +Chrome >= 73 +Firefox >= 78 +Edge >= 79 +Safari >= 12.0 +iOS >= 12.0 +opera >= 53 +``` + +> Depending on your environment, you might need to add polyfills. If you want to support older browsers, you need to transpile the library from `node_modules` yourselves. diff --git a/package.json b/package.json index 26e4f56525..ab812e9dd1 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ } }, "devDependencies": { - "@babel/cli": "^7.11.6", - "@babel/core": "^7.11.6", - "@babel/plugin-transform-runtime": "^7.11.5", - "@babel/preset-env": "^7.11.5", - "@babel/preset-react": "^7.10.4", - "@babel/preset-typescript": "^7.10.4", + "@babel/cli": "^7.17.6", + "@babel/core": "^7.17.5", + "@babel/plugin-transform-runtime": "^7.17.0", + "@babel/preset-env": "^7.16.11", + "@babel/preset-react": "^7.16.7", + "@babel/preset-typescript": "^7.16.7", "@rollup/plugin-replace": "^3.0.0", "@svgr/rollup": "^6.1.1", "@testing-library/jest-dom": "^5.14.1", @@ -102,7 +102,6 @@ "@typescript-eslint/parser": "^5.6.0", "babel-eslint": "^10.1.0", "babel-jest": "^26.0.1", - "babel-plugin-transform-async-to-promises": "^0.8.15", "bundlewatch": "^0.3.2", "cross-env": "^7.0.2", "eslint": "7.x", @@ -128,16 +127,16 @@ "react-error-boundary": "^2.2.2", "replace": "^1.2.0", "rimraf": "^3.0.2", - "rollup": "^2.61.1", + "rollup": "^2.68.0", "rollup-plugin-babel": "^4.4.0", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-jscc": "^2.0.0", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-peer-deps-external": "^2.2.4", - "rollup-plugin-prettier": "^2.2.0", + "rollup-plugin-prettier": "^2.2.2", "rollup-plugin-size": "^0.2.2", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-visualizer": "^5.5.2", + "rollup-plugin-visualizer": "^5.6.0", "type-fest": "^0.21.0", "typescript": "4.5.3" }, diff --git a/src/core/retryer.ts b/src/core/retryer.ts index 0b5f8bf85c..3dac2ee847 100644 --- a/src/core/retryer.ts +++ b/src/core/retryer.ts @@ -69,13 +69,14 @@ export function createRetryer( let isRetryCancelled = false let failureCount = 0 let isResolved = false + let continueFn: ((value?: unknown) => void) | undefined + let promiseResolve: (data: TData) => void + let promiseReject: (error: TError) => void + const promise = new Promise((outerResolve, outerReject) => { promiseResolve = outerResolve promiseReject = outerReject }) - let continueFn: ((value?: unknown) => void) | undefined - let promiseResolve: (data: TData) => void - let promiseReject: (error: TError) => void const cancel = (cancelOptions?: CancelOptions): void => { if (!isResolved) { diff --git a/yarn.lock b/yarn.lock index 404b8357c1..4d817e5864 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,21 +2,29 @@ # yarn lockfile v1 -"@babel/cli@^7.11.6": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.11.6.tgz#1fcbe61c2a6900c3539c06ee58901141f3558482" - integrity sha512-+w7BZCvkewSmaRM6H4L2QM3RL90teqEIHDIFXAmrW33+0jhlymnDAEdqVeCZATvxhQuio1ifoGVlJJbIiH9Ffg== +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + +"@babel/cli@^7.17.6": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.17.6.tgz#169e5935f1795f0b62ded5a2accafeedfe5c5363" + integrity sha512-l4w608nsDNlxZhiJ5tE3DbNmr61fIKMZ6fTBo171VEFuFMIYuJ3mHRhTLEkKKyvx2Mizkkv/0a8OJOnZqkKYNA== dependencies: + "@jridgewell/trace-mapping" "^0.3.4" commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" glob "^7.0.0" - lodash "^4.17.19" make-dir "^2.1.0" slash "^2.0.0" source-map "^0.5.0" optionalDependencies: - chokidar "^2.1.8" + "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" + chokidar "^3.4.0" "@babel/code-frame@^7.0.0": version "7.5.5" @@ -53,20 +61,16 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" - integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ== - dependencies: - browserslist "^4.12.0" - invariant "^2.2.4" - semver "^5.5.0" - "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.0", "@babel/compat-data@^7.16.4": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== +"@babel/compat-data@^7.16.8", "@babel/compat-data@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" + integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== + "@babel/core@^7.1.0": version "7.7.4" resolved "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz#37e864532200cb6b50ee9a4045f5f817840166ab" @@ -87,28 +91,6 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.11.6": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" - integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.6" - "@babel/helper-module-transforms" "^7.11.0" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.5" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.11.5" - "@babel/types" "^7.11.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - "@babel/core@^7.13.16": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf" @@ -151,6 +133,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.17.5": + version "7.17.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" + integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.17.2" + "@babel/parser" "^7.17.3" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + "@babel/core@^7.7.5": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" @@ -183,24 +186,6 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.5.tgz#1b903554bc8c583ee8d25f1e8969732e6b829a69" - integrity sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig== - dependencies: - "@babel/types" "^7.10.5" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" - integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== - dependencies: - "@babel/types" "^7.11.5" - jsesc "^2.5.1" - source-map "^0.5.0" - "@babel/generator@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" @@ -219,6 +204,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" + integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.6.3": version "7.6.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" @@ -246,13 +240,6 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-annotate-as-pure@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" - integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== - dependencies: - "@babel/types" "^7.10.4" - "@babel/helper-annotate-as-pure@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz#9a1f0ebcda53d9a2d00108c4ceace6a5d5f1f08d" @@ -267,14 +254,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" - integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.0.tgz#f1a686b92da794020c26582eb852e9accd0d7882" @@ -283,33 +262,13 @@ "@babel/helper-explode-assignable-expression" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/helper-builder-react-jsx-experimental@^7.10.4", "@babel/helper-builder-react-jsx-experimental@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.11.5.tgz#4ea43dd63857b0a35cd1f1b161dc29b43414e79f" - integrity sha512-Vc4aPJnRZKWfzeCBsqTBnzulVNjABVdahSPhtdMD3Vs80ykx4a87jTHtF/VR+alSrDmNvat7l13yrRHauGcHVw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-module-imports" "^7.10.4" - "@babel/types" "^7.11.5" - -"@babel/helper-builder-react-jsx@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d" - integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helper-compilation-targets@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" - integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== +"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" + integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== dependencies: - "@babel/compat-data" "^7.10.4" - browserslist "^4.12.0" - invariant "^2.2.4" - levenary "^1.1.1" - semver "^5.5.0" + "@babel/helper-explode-assignable-expression" "^7.16.7" + "@babel/types" "^7.16.7" "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.0", "@babel/helper-compilation-targets@^7.16.3": version "7.16.3" @@ -331,18 +290,6 @@ browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" - integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== - dependencies: - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-member-expression-to-functions" "^7.10.5" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - "@babel/helper-create-class-features-plugin@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz#090d4d166b342a03a9fec37ef4fd5aeb9c7c6a4b" @@ -355,6 +302,19 @@ "@babel/helper-replace-supers" "^7.16.0" "@babel/helper-split-export-declaration" "^7.16.0" +"@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.17.6": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" + integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-create-class-features-plugin@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.7.tgz#9c5b34b53a01f2097daf10678d65135c1b9f84ba" @@ -377,15 +337,6 @@ "@babel/helper-regex" "^7.10.1" regexpu-core "^4.7.0" -"@babel/helper-create-regexp-features-plugin@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" - integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-regex" "^7.10.4" - regexpu-core "^4.7.0" - "@babel/helper-create-regexp-features-plugin@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz#06b2348ce37fccc4f5e18dcd8d75053f2a7c44ff" @@ -394,14 +345,13 @@ "@babel/helper-annotate-as-pure" "^7.16.0" regexpu-core "^4.7.1" -"@babel/helper-define-map@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" - integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== +"@babel/helper-create-regexp-features-plugin@^7.16.7": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" + integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA== dependencies: - "@babel/helper-function-name" "^7.10.4" - "@babel/types" "^7.10.5" - lodash "^4.17.19" + "@babel/helper-annotate-as-pure" "^7.16.7" + regexpu-core "^5.0.1" "@babel/helper-define-polyfill-provider@^0.3.0": version "0.3.0" @@ -417,6 +367,20 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" @@ -424,13 +388,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-explode-assignable-expression@^7.10.4": - version "7.11.4" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b" - integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ== - dependencies: - "@babel/types" "^7.10.4" - "@babel/helper-explode-assignable-expression@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz#753017337a15f46f9c09f674cff10cee9b9d7778" @@ -438,6 +395,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-explode-assignable-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" + integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-function-name@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" @@ -456,15 +420,6 @@ "@babel/template" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/helper-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" - integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== - dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helper-function-name@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" @@ -506,13 +461,6 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-get-function-arity@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" - integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== - dependencies: - "@babel/types" "^7.10.4" - "@babel/helper-get-function-arity@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" @@ -534,13 +482,6 @@ dependencies: "@babel/types" "^7.7.4" -"@babel/helper-hoist-variables@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" - integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== - dependencies: - "@babel/types" "^7.10.4" - "@babel/helper-hoist-variables@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" @@ -562,13 +503,6 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz#172f56e7a63e78112f3a04055f24365af702e7ee" - integrity sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA== - dependencies: - "@babel/types" "^7.10.5" - "@babel/helper-member-expression-to-functions@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz#29287040efd197c77636ef75188e81da8bccd5a4" @@ -597,13 +531,6 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-module-imports@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" - integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== - dependencies: - "@babel/types" "^7.10.4" - "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3" @@ -631,19 +558,6 @@ "@babel/types" "^7.10.1" lodash "^4.17.13" -"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" - integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== - dependencies: - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-simple-access" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/template" "^7.10.4" - "@babel/types" "^7.11.0" - lodash "^4.17.19" - "@babel/helper-module-transforms@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" @@ -679,13 +593,6 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-optimise-call-expression@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" - integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== - dependencies: - "@babel/types" "^7.10.4" - "@babel/helper-optimise-call-expression@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz#cecdb145d70c54096b1564f8e9f10cd7d193b338" @@ -737,23 +644,6 @@ dependencies: lodash "^4.17.13" -"@babel/helper-regex@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" - integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== - dependencies: - lodash "^4.17.19" - -"@babel/helper-remap-async-to-generator@^7.10.4": - version "7.11.4" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d" - integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-wrap-function" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helper-remap-async-to-generator@^7.16.0", "@babel/helper-remap-async-to-generator@^7.16.4": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.4.tgz#5d7902f61349ff6b963e07f06a389ce139fbfe6e" @@ -763,6 +653,15 @@ "@babel/helper-wrap-function" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-remap-async-to-generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" + integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-wrap-function" "^7.16.8" + "@babel/types" "^7.16.8" + "@babel/helper-replace-supers@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" @@ -773,16 +672,6 @@ "@babel/traverse" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/helper-replace-supers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" - integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helper-replace-supers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz#73055e8d3cf9bcba8ddb55cad93fedc860f68f17" @@ -812,14 +701,6 @@ "@babel/template" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/helper-simple-access@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" - integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== - dependencies: - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helper-simple-access@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz#21d6a27620e383e37534cf6c10bba019a6f90517" @@ -834,13 +715,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" - integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== - dependencies: - "@babel/types" "^7.11.0" - "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" @@ -855,20 +729,6 @@ dependencies: "@babel/types" "^7.10.1" -"@babel/helper-split-export-declaration@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz#2c70576eaa3b5609b24cb99db2888cc3fc4251d1" - integrity sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-split-export-declaration@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" - integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== - dependencies: - "@babel/types" "^7.11.0" - "@babel/helper-split-export-declaration@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" @@ -927,16 +787,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helper-wrap-function@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" - integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== - dependencies: - "@babel/helper-function-name" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helper-wrap-function@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz#b3cf318afce774dfe75b86767cd6d68f3482e57c" @@ -947,6 +797,16 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-wrap-function@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" + integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== + dependencies: + "@babel/helper-function-name" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.8" + "@babel/types" "^7.16.8" + "@babel/helpers@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.1.tgz#a6827b7cb975c9d9cef5fd61d919f60d8844a973" @@ -956,15 +816,6 @@ "@babel/traverse" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/helpers@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" - integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== - dependencies: - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/helpers@^7.16.0": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" @@ -983,6 +834,15 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helpers@^7.17.2": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" + integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.0" + "@babel/types" "^7.17.0" + "@babel/helpers@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz#62c215b9e6c712dadc15a9a0dcab76c92a940302" @@ -1047,16 +907,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" integrity sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ== -"@babel/parser@^7.10.4", "@babel/parser@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" - integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== - -"@babel/parser@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" - integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== - "@babel/parser@^7.13.16", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17" @@ -1067,6 +917,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== +"@babel/parser@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" + integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== + "@babel/parser@^7.6.0", "@babel/parser@^7.6.3": version "7.6.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" @@ -1079,6 +934,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" + integrity sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.0.tgz#358972eaab006f5eb0826183b0c93cbcaf13e1e2" @@ -1088,14 +950,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.0" -"@babel/plugin-proposal-async-generator-functions@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" - integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" + integrity sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-remap-async-to-generator" "^7.10.4" - "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" "@babel/plugin-proposal-async-generator-functions@^7.16.4": version "7.16.4" @@ -1106,15 +968,16 @@ "@babel/helper-remap-async-to-generator" "^7.16.4" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" - integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== +"@babel/plugin-proposal-async-generator-functions@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" + integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" + "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-class-properties@^7.13.0": +"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== @@ -1139,13 +1002,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-dynamic-import@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" - integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== +"@babel/plugin-proposal-class-static-block@^7.16.7": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz#164e8fd25f0d80fa48c5a4d1438a6629325ad83c" + integrity sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/helper-create-class-features-plugin" "^7.17.6" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-dynamic-import@^7.16.0": version "7.16.0" @@ -1155,13 +1019,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-proposal-export-namespace-from@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" - integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg== +"@babel/plugin-proposal-dynamic-import@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" + integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" "@babel/plugin-proposal-export-namespace-from@^7.16.0": version "7.16.0" @@ -1171,13 +1035,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" - integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== +"@babel/plugin-proposal-export-namespace-from@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" + integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" "@babel/plugin-proposal-json-strings@^7.16.0": version "7.16.0" @@ -1187,13 +1051,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" - integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== +"@babel/plugin-proposal-json-strings@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" + integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-proposal-logical-assignment-operators@^7.16.0": version "7.16.0" @@ -1203,15 +1067,15 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" - integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== +"@babel/plugin-proposal-logical-assignment-operators@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" + integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8": +"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ== @@ -1227,14 +1091,6 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" - integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-proposal-numeric-separator@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.0.tgz#5d418e4fbbf8b9b7d03125d3a52730433a373734" @@ -1243,14 +1099,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-object-rest-spread@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" - integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== +"@babel/plugin-proposal-numeric-separator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" + integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-proposal-object-rest-spread@^7.16.0": version "7.16.0" @@ -1263,13 +1118,16 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.0" -"@babel/plugin-proposal-optional-catch-binding@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" - integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== +"@babel/plugin-proposal-object-rest-spread@^7.16.7": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" + integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/compat-data" "^7.17.0" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.16.7" "@babel/plugin-proposal-optional-catch-binding@^7.16.0": version "7.16.0" @@ -1279,16 +1137,15 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" - integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== +"@babel/plugin-proposal-optional-catch-binding@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" + integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.13.12": +"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA== @@ -1306,14 +1163,6 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-private-methods@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" - integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-proposal-private-methods@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.0.tgz#b4dafb9c717e4301c5776b30d080d6383c89aff6" @@ -1322,6 +1171,14 @@ "@babel/helper-create-class-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-proposal-private-methods@^7.16.11": + version "7.16.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" + integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.10" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-proposal-private-property-in-object@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.0.tgz#69e935b2c5c79d2488112d886f0c4e2790fee76f" @@ -1332,13 +1189,15 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-unicode-property-regex@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" - integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== +"@babel/plugin-proposal-private-property-in-object@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" + integrity sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-proposal-unicode-property-regex@^7.16.0": version "7.16.0" @@ -1348,6 +1207,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-proposal-unicode-property-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" + integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz#dc04feb25e2dd70c12b05d680190e138fa2c0c6f" @@ -1356,7 +1223,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.1" "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": +"@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== @@ -1370,13 +1237,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" - integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" @@ -1398,7 +1258,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": +"@babel/plugin-syntax-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== @@ -1419,20 +1279,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": +"@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" - integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz#f9624394317365a9a88c82358d3f8471154698f1" @@ -1440,6 +1293,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-jsx@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -1454,7 +1314,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== @@ -1475,21 +1335,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": +"@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== @@ -1503,13 +1363,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-top-level-await@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" - integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" @@ -1517,13 +1370,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" - integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-typescript@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" @@ -1531,13 +1377,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-arrow-functions@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" - integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-arrow-functions@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz#951706f8b449c834ed07bd474c0924c944b95a8e" @@ -1545,14 +1384,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-async-to-generator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" - integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== +"@babel/plugin-transform-arrow-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-remap-async-to-generator" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-async-to-generator@^7.16.0": version "7.16.0" @@ -1563,12 +1400,14 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-remap-async-to-generator" "^7.16.0" -"@babel/plugin-transform-block-scoped-functions@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" - integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== +"@babel/plugin-transform-async-to-generator@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" + integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-transform-block-scoped-functions@^7.16.0": version "7.16.0" @@ -1577,12 +1416,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-block-scoping@^7.10.4": - version "7.11.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" - integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== +"@babel/plugin-transform-block-scoped-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.16.0": version "7.16.0" @@ -1591,19 +1430,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-classes@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" - integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== +"@babel/plugin-transform-block-scoping@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-define-map" "^7.10.4" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - globals "^11.1.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.16.0": version "7.16.0" @@ -1618,12 +1450,19 @@ "@babel/helper-split-export-declaration" "^7.16.0" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" - integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== +"@babel/plugin-transform-classes@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.16.0": version "7.16.0" @@ -1632,12 +1471,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-destructuring@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" - integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== +"@babel/plugin-transform-computed-properties@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.16.0": version "7.16.0" @@ -1646,13 +1485,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-dotall-regex@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" - integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== +"@babel/plugin-transform-destructuring@^7.16.7": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz#c445f75819641788a27a0a3a759d9df911df6abc" + integrity sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-dotall-regex@^7.16.0": version "7.16.0" @@ -1662,6 +1500,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-dotall-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" + integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz#920b9fec2d78bb57ebb64a644d5c2ba67cc104ee" @@ -1670,13 +1516,6 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.1" "@babel/helper-plugin-utils" "^7.10.1" -"@babel/plugin-transform-duplicate-keys@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" - integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-duplicate-keys@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.0.tgz#8bc2e21813e3e89e5e5bf3b60aa5fc458575a176" @@ -1684,13 +1523,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-exponentiation-operator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" - integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== +"@babel/plugin-transform-duplicate-keys@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" + integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw== dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-exponentiation-operator@^7.16.0": version "7.16.0" @@ -1700,6 +1538,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-exponentiation-operator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" + integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-flow-strip-types@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" @@ -1708,13 +1554,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-flow" "^7.16.7" -"@babel/plugin-transform-for-of@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" - integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-for-of@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.0.tgz#f7abaced155260e2461359bbc7c7248aca5e6bd2" @@ -1722,13 +1561,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" - integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== +"@babel/plugin-transform-for-of@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.16.0": version "7.16.0" @@ -1738,12 +1576,14 @@ "@babel/helper-function-name" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-literals@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" - integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== +"@babel/plugin-transform-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.16.0": version "7.16.0" @@ -1752,12 +1592,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-member-expression-literals@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" - integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== +"@babel/plugin-transform-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.16.0": version "7.16.0" @@ -1766,14 +1606,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-modules-amd@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" - integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== +"@babel/plugin-transform-member-expression-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== dependencies: - "@babel/helper-module-transforms" "^7.10.5" - "@babel/helper-plugin-utils" "^7.10.4" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-amd@^7.16.0": version "7.16.0" @@ -1784,17 +1622,16 @@ "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" - integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== +"@babel/plugin-transform-modules-amd@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" + integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g== dependencies: - "@babel/helper-module-transforms" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.13.8": +"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== @@ -1814,16 +1651,6 @@ "@babel/helper-simple-access" "^7.16.0" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" - integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== - dependencies: - "@babel/helper-hoist-variables" "^7.10.4" - "@babel/helper-module-transforms" "^7.10.5" - "@babel/helper-plugin-utils" "^7.10.4" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-systemjs@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.0.tgz#a92cf240afeb605f4ca16670453024425e421ea4" @@ -1835,13 +1662,16 @@ "@babel/helper-validator-identifier" "^7.15.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-umd@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" - integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== +"@babel/plugin-transform-modules-systemjs@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7" + integrity sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw== dependencies: - "@babel/helper-module-transforms" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-modules-umd@^7.16.0": version "7.16.0" @@ -1851,12 +1681,13 @@ "@babel/helper-module-transforms" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" - integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== +"@babel/plugin-transform-modules-umd@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" + integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-named-capturing-groups-regex@^7.16.0": version "7.16.0" @@ -1865,12 +1696,12 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.16.0" -"@babel/plugin-transform-new-target@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" - integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== +"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252" + integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/plugin-transform-new-target@^7.16.0": version "7.16.0" @@ -1879,13 +1710,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-object-super@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" - integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== +"@babel/plugin-transform-new-target@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" + integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-object-super@^7.16.0": version "7.16.0" @@ -1895,13 +1725,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-replace-supers" "^7.16.0" -"@babel/plugin-transform-parameters@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" - integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== +"@babel/plugin-transform-object-super@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" "@babel/plugin-transform-parameters@^7.16.0", "@babel/plugin-transform-parameters@^7.16.3": version "7.16.3" @@ -1910,12 +1740,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-property-literals@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" - integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== +"@babel/plugin-transform-parameters@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.16.0": version "7.16.0" @@ -1924,6 +1754,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-transform-property-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-transform-react-constant-elements@^7.14.5": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.0.tgz#1483b894b8e6ef0709d260532fbd4db9fc27a0e6" @@ -1931,13 +1768,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-react-display-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.4.tgz#b5795f4e3e3140419c3611b7a2a3832b9aef328d" - integrity sha512-Zd4X54Mu9SBfPGnEcaGcOrVAYOtjT2on8QZkLKEq1S/tHexG39d9XXGZv19VfRrDjPJzFmPfTAqOQS1pfFOujw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-react-display-name@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.0.tgz#9a0ad8aa8e8790883a7bd2736f66229a58125676" @@ -1945,14 +1775,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-react-jsx-development@^7.10.4": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.11.5.tgz#e1439e6a57ee3d43e9f54ace363fb29cefe5d7b6" - integrity sha512-cImAmIlKJ84sDmpQzm4/0q/2xrXlDezQoixy3qoz1NJeZL/8PRon6xZtluvr4H4FzwlDGI5tCcFupMnXGtr+qw== +"@babel/plugin-transform-react-display-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== dependencies: - "@babel/helper-builder-react-jsx-experimental" "^7.11.5" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx-development@^7.16.0": version "7.16.0" @@ -1961,31 +1789,12 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.16.0" -"@babel/plugin-transform-react-jsx-self@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369" - integrity sha512-yOvxY2pDiVJi0axdTWHSMi5T0DILN+H+SaeJeACHKjQLezEzhLx9nEF9xgpBLPtkZsks9cnb5P9iBEi21En3gg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx" "^7.10.4" - -"@babel/plugin-transform-react-jsx-source@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.5.tgz#34f1779117520a779c054f2cdd9680435b9222b4" - integrity sha512-wTeqHVkN1lfPLubRiZH3o73f4rfon42HpgxUSs86Nc+8QIcm/B9s8NNVXu/gwGcOyd7yDib9ikxoDLxJP0UiDA== +"@babel/plugin-transform-react-jsx-development@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" + integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx" "^7.10.4" - -"@babel/plugin-transform-react-jsx@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" - integrity sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A== - dependencies: - "@babel/helper-builder-react-jsx" "^7.10.4" - "@babel/helper-builder-react-jsx-experimental" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx" "^7.10.4" + "@babel/plugin-transform-react-jsx" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.16.0": version "7.16.0" @@ -1998,13 +1807,16 @@ "@babel/plugin-syntax-jsx" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/plugin-transform-react-pure-annotations@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501" - integrity sha512-+njZkqcOuS8RaPakrnR9KvxjoG1ASJWpoIv/doyWngId88JoFlPlISenGXjrVacZUIALGUr6eodRs1vmPnF23A== +"@babel/plugin-transform-react-jsx@^7.16.7": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" + integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/plugin-transform-react-pure-annotations@^7.16.0": version "7.16.0" @@ -2014,12 +1826,13 @@ "@babel/helper-annotate-as-pure" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-regenerator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" - integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== +"@babel/plugin-transform-react-pure-annotations@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67" + integrity sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA== dependencies: - regenerator-transform "^0.14.2" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-regenerator@^7.16.0": version "7.16.0" @@ -2028,12 +1841,12 @@ dependencies: regenerator-transform "^0.14.2" -"@babel/plugin-transform-reserved-words@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" - integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== +"@babel/plugin-transform-regenerator@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb" + integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + regenerator-transform "^0.14.2" "@babel/plugin-transform-reserved-words@^7.16.0": version "7.16.0" @@ -2042,22 +1855,24 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-runtime@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.5.tgz#f108bc8e0cf33c37da031c097d1df470b3a293fc" - integrity sha512-9aIoee+EhjySZ6vY5hnLjigHzunBlscx9ANKutkeWTJTx6m5Rbq6Ic01tLvO54lSusR+BxV7u4UDdCmXv5aagg== +"@babel/plugin-transform-reserved-words@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" + integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg== dependencies: - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - resolve "^1.8.1" - semver "^5.5.1" + "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-shorthand-properties@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" - integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== +"@babel/plugin-transform-runtime@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz#0a2e08b5e2b2d95c4b1d3b3371a2180617455b70" + integrity sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.16.0": version "7.16.0" @@ -2066,13 +1881,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-spread@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" - integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== +"@babel/plugin-transform-shorthand-properties@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.16.0": version "7.16.0" @@ -2082,13 +1896,13 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" -"@babel/plugin-transform-sticky-regex@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" - integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== +"@babel/plugin-transform-spread@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-regex" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-sticky-regex@^7.16.0": version "7.16.0" @@ -2097,13 +1911,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-template-literals@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" - integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== +"@babel/plugin-transform-sticky-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" + integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== dependencies: - "@babel/helper-annotate-as-pure" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-template-literals@^7.16.0": version "7.16.0" @@ -2112,12 +1925,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typeof-symbol@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" - integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== +"@babel/plugin-transform-template-literals@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typeof-symbol@^7.16.0": version "7.16.0" @@ -2126,14 +1939,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-typescript@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.5.tgz#edf353944e979f40d8ff9fe4e9975d0a465037c5" - integrity sha512-YCyYsFrrRMZ3qR7wRwtSSJovPG5vGyG4ZdcSAivGwTfoasMp3VOB/AKhohu3dFtmB4cCDcsndCSxGtrdliCsZQ== +"@babel/plugin-transform-typeof-symbol@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" + integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.10.5" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-typescript" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-typescript@^7.16.7": version "7.16.8" @@ -2144,13 +1955,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-typescript" "^7.16.7" -"@babel/plugin-transform-unicode-escapes@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" - integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-unicode-escapes@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.0.tgz#1a354064b4c45663a32334f46fa0cf6100b5b1f3" @@ -2158,13 +1962,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-transform-unicode-regex@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" - integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== +"@babel/plugin-transform-unicode-escapes@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" + integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-unicode-regex@^7.16.0": version "7.16.0" @@ -2174,79 +1977,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.0" "@babel/helper-plugin-utils" "^7.14.5" -"@babel/preset-env@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272" - integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA== +"@babel/plugin-transform-unicode-regex@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" + integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== dependencies: - "@babel/compat-data" "^7.11.0" - "@babel/helper-compilation-targets" "^7.10.4" - "@babel/helper-module-imports" "^7.10.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-proposal-async-generator-functions" "^7.10.4" - "@babel/plugin-proposal-class-properties" "^7.10.4" - "@babel/plugin-proposal-dynamic-import" "^7.10.4" - "@babel/plugin-proposal-export-namespace-from" "^7.10.4" - "@babel/plugin-proposal-json-strings" "^7.10.4" - "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" - "@babel/plugin-proposal-numeric-separator" "^7.10.4" - "@babel/plugin-proposal-object-rest-spread" "^7.11.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" - "@babel/plugin-proposal-optional-chaining" "^7.11.0" - "@babel/plugin-proposal-private-methods" "^7.10.4" - "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-class-properties" "^7.10.4" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.10.4" - "@babel/plugin-transform-arrow-functions" "^7.10.4" - "@babel/plugin-transform-async-to-generator" "^7.10.4" - "@babel/plugin-transform-block-scoped-functions" "^7.10.4" - "@babel/plugin-transform-block-scoping" "^7.10.4" - "@babel/plugin-transform-classes" "^7.10.4" - "@babel/plugin-transform-computed-properties" "^7.10.4" - "@babel/plugin-transform-destructuring" "^7.10.4" - "@babel/plugin-transform-dotall-regex" "^7.10.4" - "@babel/plugin-transform-duplicate-keys" "^7.10.4" - "@babel/plugin-transform-exponentiation-operator" "^7.10.4" - "@babel/plugin-transform-for-of" "^7.10.4" - "@babel/plugin-transform-function-name" "^7.10.4" - "@babel/plugin-transform-literals" "^7.10.4" - "@babel/plugin-transform-member-expression-literals" "^7.10.4" - "@babel/plugin-transform-modules-amd" "^7.10.4" - "@babel/plugin-transform-modules-commonjs" "^7.10.4" - "@babel/plugin-transform-modules-systemjs" "^7.10.4" - "@babel/plugin-transform-modules-umd" "^7.10.4" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" - "@babel/plugin-transform-new-target" "^7.10.4" - "@babel/plugin-transform-object-super" "^7.10.4" - "@babel/plugin-transform-parameters" "^7.10.4" - "@babel/plugin-transform-property-literals" "^7.10.4" - "@babel/plugin-transform-regenerator" "^7.10.4" - "@babel/plugin-transform-reserved-words" "^7.10.4" - "@babel/plugin-transform-shorthand-properties" "^7.10.4" - "@babel/plugin-transform-spread" "^7.11.0" - "@babel/plugin-transform-sticky-regex" "^7.10.4" - "@babel/plugin-transform-template-literals" "^7.10.4" - "@babel/plugin-transform-typeof-symbol" "^7.10.4" - "@babel/plugin-transform-unicode-escapes" "^7.10.4" - "@babel/plugin-transform-unicode-regex" "^7.10.4" - "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.11.5" - browserslist "^4.12.0" - core-js-compat "^3.6.2" - invariant "^2.2.2" - levenary "^1.1.1" - semver "^5.5.0" + "@babel/helper-create-regexp-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/preset-env@^7.15.6": version "7.16.4" @@ -2328,6 +2065,86 @@ core-js-compat "^3.19.1" semver "^6.3.0" +"@babel/preset-env@^7.16.11": + version "7.16.11" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" + integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== + dependencies: + "@babel/compat-data" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-async-generator-functions" "^7.16.8" + "@babel/plugin-proposal-class-properties" "^7.16.7" + "@babel/plugin-proposal-class-static-block" "^7.16.7" + "@babel/plugin-proposal-dynamic-import" "^7.16.7" + "@babel/plugin-proposal-export-namespace-from" "^7.16.7" + "@babel/plugin-proposal-json-strings" "^7.16.7" + "@babel/plugin-proposal-logical-assignment-operators" "^7.16.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.7" + "@babel/plugin-proposal-numeric-separator" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread" "^7.16.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-private-methods" "^7.16.11" + "@babel/plugin-proposal-private-property-in-object" "^7.16.7" + "@babel/plugin-proposal-unicode-property-regex" "^7.16.7" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.16.7" + "@babel/plugin-transform-async-to-generator" "^7.16.8" + "@babel/plugin-transform-block-scoped-functions" "^7.16.7" + "@babel/plugin-transform-block-scoping" "^7.16.7" + "@babel/plugin-transform-classes" "^7.16.7" + "@babel/plugin-transform-computed-properties" "^7.16.7" + "@babel/plugin-transform-destructuring" "^7.16.7" + "@babel/plugin-transform-dotall-regex" "^7.16.7" + "@babel/plugin-transform-duplicate-keys" "^7.16.7" + "@babel/plugin-transform-exponentiation-operator" "^7.16.7" + "@babel/plugin-transform-for-of" "^7.16.7" + "@babel/plugin-transform-function-name" "^7.16.7" + "@babel/plugin-transform-literals" "^7.16.7" + "@babel/plugin-transform-member-expression-literals" "^7.16.7" + "@babel/plugin-transform-modules-amd" "^7.16.7" + "@babel/plugin-transform-modules-commonjs" "^7.16.8" + "@babel/plugin-transform-modules-systemjs" "^7.16.7" + "@babel/plugin-transform-modules-umd" "^7.16.7" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8" + "@babel/plugin-transform-new-target" "^7.16.7" + "@babel/plugin-transform-object-super" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-property-literals" "^7.16.7" + "@babel/plugin-transform-regenerator" "^7.16.7" + "@babel/plugin-transform-reserved-words" "^7.16.7" + "@babel/plugin-transform-shorthand-properties" "^7.16.7" + "@babel/plugin-transform-spread" "^7.16.7" + "@babel/plugin-transform-sticky-regex" "^7.16.7" + "@babel/plugin-transform-template-literals" "^7.16.7" + "@babel/plugin-transform-typeof-symbol" "^7.16.7" + "@babel/plugin-transform-unicode-escapes" "^7.16.7" + "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.16.8" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + core-js-compat "^3.20.2" + semver "^6.3.0" + "@babel/preset-flow@^7.13.13": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" @@ -2337,17 +2154,6 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-flow-strip-types" "^7.16.7" -"@babel/preset-modules@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" - integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - "@babel/preset-modules@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" @@ -2359,19 +2165,6 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" - integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-react-display-name" "^7.10.4" - "@babel/plugin-transform-react-jsx" "^7.10.4" - "@babel/plugin-transform-react-jsx-development" "^7.10.4" - "@babel/plugin-transform-react-jsx-self" "^7.10.4" - "@babel/plugin-transform-react-jsx-source" "^7.10.4" - "@babel/plugin-transform-react-pure-annotations" "^7.10.4" - "@babel/preset-react@^7.14.5": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.0.tgz#f71d3e8dff5218478011df037fad52660ee6d82a" @@ -2384,15 +2177,19 @@ "@babel/plugin-transform-react-jsx-development" "^7.16.0" "@babel/plugin-transform-react-pure-annotations" "^7.16.0" -"@babel/preset-typescript@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" - integrity sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ== +"@babel/preset-react@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" + integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-typescript" "^7.10.4" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-react-display-name" "^7.16.7" + "@babel/plugin-transform-react-jsx" "^7.16.7" + "@babel/plugin-transform-react-jsx-development" "^7.16.7" + "@babel/plugin-transform-react-pure-annotations" "^7.16.7" -"@babel/preset-typescript@^7.13.0": +"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== @@ -2502,15 +2299,6 @@ "@babel/parser" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/template@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" - "@babel/template@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" @@ -2568,36 +2356,6 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.5.tgz#77ce464f5b258be265af618d8fddf0536f20b564" - integrity sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.10.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - "@babel/parser" "^7.10.5" - "@babel/types" "^7.10.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/traverse@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" - integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.11.5" - "@babel/types" "^7.11.5" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" @@ -2629,6 +2387,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + "@babel/traverse@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558" @@ -2662,24 +2436,6 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.10.4", "@babel/types@^7.10.5": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.5.tgz#d88ae7e2fde86bfbfe851d4d81afa70a997b5d15" - integrity sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.11.0", "@babel/types@^7.11.5": - version "7.11.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" - integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - "@babel/types@^7.15.6", "@babel/types@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" @@ -2696,6 +2452,14 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@babel/types@^7.7.4": version "7.7.4" resolved "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz#516570d539e44ddf308c07569c258ff94fde9193" @@ -2921,6 +2685,24 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.4": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jsbits/escape-regex-str@^1.0.2": version "1.0.3" resolved "https://registry.yarnpkg.com/@jsbits/escape-regex-str/-/escape-regex-str-1.0.3.tgz#d35a2d21dfdc81a0e5ebeb68b6a16e17ca36ad20" @@ -2931,6 +2713,11 @@ resolved "https://registry.yarnpkg.com/@jsbits/get-package-version/-/get-package-version-1.0.3.tgz#a47dfd077420beee435580c3bc197931fe3c694c" integrity sha512-IJy1jRL01x7p6UEpgKa1lVLstMUx8EiIR8pPoS5sBfsHEoeLkzYiNpAfxPx8zLDUJyS1yBbChJjcWdPqyH285w== +"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": + version "2.1.8-no-fsevents.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" + integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -3460,6 +3247,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -3496,6 +3288,14 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3603,11 +3403,6 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -3725,6 +3520,14 @@ babel-plugin-polyfill-corejs3@^0.4.0: "@babel/helper-define-polyfill-provider" "^0.3.0" core-js-compat "^3.18.0" +babel-plugin-polyfill-corejs3@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" + integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.21.0" + babel-plugin-polyfill-regenerator@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz#9ebbcd7186e1a33e21c5e20cae4e7983949533be" @@ -3732,11 +3535,6 @@ babel-plugin-polyfill-regenerator@^0.3.0: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.0" -babel-plugin-transform-async-to-promises@^0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-promises/-/babel-plugin-transform-async-to-promises-0.8.15.tgz#13b6d8ef13676b4e3c576d3600b85344bb1ba346" - integrity sha512-fDXP68ZqcinZO2WCiimCL9zhGjGXOnn3D33zvbh+yheZ/qOrNVVDDIBtAaM3Faz8TRvQzHiRKsu3hfrBAhEncQ== - babel-preset-current-node-syntax@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" @@ -3791,17 +3589,10 @@ big-integer@^1.6.16: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== boolbase@^1.0.0: version "1.0.0" @@ -3816,7 +3607,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^2.3.1, braces@^2.3.2: +braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -3832,7 +3623,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -3864,16 +3655,6 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.12.0, browserslist@^4.8.5: - version "4.12.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" - integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== - dependencies: - caniuse-lite "^1.0.30001043" - electron-to-chromium "^1.3.413" - node-releases "^1.1.53" - pkg-up "^2.0.0" - browserslist@^4.17.5, browserslist@^4.18.1: version "4.18.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" @@ -3885,6 +3666,17 @@ browserslist@^4.17.5, browserslist@^4.18.1: node-releases "^2.0.1" picocolors "^1.0.0" +browserslist@^4.19.1: + version "4.19.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.3.tgz#29b7caad327ecf2859485f696f9604214bedd383" + integrity sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg== + dependencies: + caniuse-lite "^1.0.30001312" + electron-to-chromium "^1.4.71" + escalade "^3.1.1" + node-releases "^2.0.2" + picocolors "^1.0.0" + bser@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3958,11 +3750,16 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== -caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001280: +caniuse-lite@^1.0.30001280: version "1.0.30001286" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz" integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ== +caniuse-lite@^1.0.30001312: + version "1.0.30001312" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f" + integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -4018,24 +3815,20 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" +chokidar@^3.4.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "^1.2.7" + fsevents "~2.3.2" ci-env@^1.11.0: version "1.12.0" @@ -4215,12 +4008,12 @@ core-js-compat@^3.18.0, core-js-compat@^3.19.1: browserslist "^4.18.1" semver "7.0.0" -core-js-compat@^3.6.2: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" - integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== +core-js-compat@^3.20.2, core-js-compat@^3.21.0: + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.1.tgz#cac369f67c8d134ff8f9bd1623e3bc2c42068c82" + integrity sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g== dependencies: - browserslist "^4.8.5" + browserslist "^4.19.1" semver "7.0.0" core-js-pure@^3.0.0: @@ -4228,7 +4021,7 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== -core-util-is@1.0.2, core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= @@ -4412,6 +4205,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -4570,16 +4368,16 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.3.413: - version "1.3.473" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.473.tgz#d0cd5fe391046fb70674ec98149f0f97609d29b8" - integrity sha512-smevlzzMNz3vMz6OLeeCq5HRWEj2AcgccNPYnAx4Usx0IOciq9DU36RJcICcS09hXoY7t7deRfVYKD14IrGb9A== - electron-to-chromium@^1.3.896: version "1.4.16" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.16.tgz#38ddecc616385e6f101359d1b978c802664157d2" integrity sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA== +electron-to-chromium@^1.4.71: + version "1.4.73" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.73.tgz#422f6f514315bcace9615903e4a9b6b9fa283137" + integrity sha512-RlCffXkE/LliqfA5m29+dVDPB2r72y2D2egMMfIy3Le8ODrxjuZNVo4NIC2yPL01N4xb4nZQLwzi6Z5tGIGLnA== + emoji-regex@^7.0.1, emoji-regex@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5178,11 +4976,6 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -5307,14 +5100,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - fsevents@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" @@ -5386,14 +5171,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - glob-parent@^5.0.0, glob-parent@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" @@ -5401,6 +5178,13 @@ glob-parent@^5.0.0, glob-parent@^5.1.0: dependencies: is-glob "^4.0.1" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@^7.0.0, glob@^7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -5638,7 +5422,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5671,13 +5455,6 @@ internal-slot@^1.0.2: has "^1.0.3" side-channel "^1.0.2" -invariant@^2.2.2, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -5702,12 +5479,12 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: - binary-extensions "^1.0.0" + binary-extensions "^2.0.0" is-buffer@^1.1.5: version "1.1.6" @@ -5793,6 +5570,11 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== +is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -5805,7 +5587,7 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= @@ -5825,13 +5607,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -5839,7 +5614,7 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" -is-glob@^4.0.3: +is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -5933,7 +5708,14 @@ is-wsl@^2.1.1: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0, isarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -6604,13 +6386,6 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== -levenary@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" - integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== - dependencies: - leven "^3.1.0" - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -6705,12 +6480,7 @@ lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.19: - version "4.17.19" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" - integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== - -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -6908,11 +6678,6 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1: - version "2.14.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" - integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== - nano-time@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" @@ -6920,10 +6685,10 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" -nanoid@^3.1.22: - version "3.1.30" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" - integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== +nanoid@^3.1.32: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== nanomatch@^1.2.9: version "1.2.13" @@ -6986,16 +6751,16 @@ node-notifier@^7.0.0: uuid "^7.0.3" which "^2.0.2" -node-releases@^1.1.53: - version "1.1.58" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935" - integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg== - node-releases@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" + integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -7013,7 +6778,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -7154,13 +6919,14 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@^7.4.2: - version "7.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" - integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== +open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" optionator@^0.8.1: version "0.8.3" @@ -7280,11 +7046,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -7390,13 +7151,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" - integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= - dependencies: - find-up "^2.1.0" - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -7464,11 +7218,6 @@ private@^0.1.8: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -7598,27 +7347,12 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^2.0.2: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" + picomatch "^2.2.1" recast@^0.20.3, recast@^0.20.4: version "0.20.5" @@ -7638,6 +7372,13 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -7725,11 +7466,28 @@ regexpu-core@^4.7.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" +regexpu-core@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" + integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + regjsgen@^0.5.1, regjsgen@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== +regjsgen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== + regjsparser@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" @@ -7744,6 +7502,13 @@ regjsparser@^0.7.0: dependencies: jsesc "~0.5.0" +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== + dependencies: + jsesc "~0.5.0" + remove-accents@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" @@ -7854,7 +7619,7 @@ resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.11.1, resolve@^1.3 dependencies: path-parse "^1.0.6" -resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0, resolve@^1.8.1: +resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -7945,10 +7710,10 @@ rollup-plugin-peer-deps-external@^2.2.4: resolved "https://registry.yarnpkg.com/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz#8a420bbfd6dccc30aeb68c9bf57011f2f109570d" integrity sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g== -rollup-plugin-prettier@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-prettier/-/rollup-plugin-prettier-2.2.0.tgz#5690e2fd4311bd33f8ab5760de5d5792daf9089b" - integrity sha512-elavFmdKju3KqoZ49XBDxcXdyANxAQf7fRvvRTVpeG17NUEp+OZxCMVyLWnEpz9ua1wxawR66moYo33yhsyDaw== +rollup-plugin-prettier@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-prettier/-/rollup-plugin-prettier-2.2.2.tgz#733c25a3cea2ce65b14635729bd1eb3a2a147ebc" + integrity sha512-dj5RhTuW8vTVrjxGsKy7rO/ywhKgo8Wihm776e/Po0ZYj/Yu4kr+B6vmnYmZ46Y1kslE1UGoXvBygwmlJ0ifrw== dependencies: "@types/prettier" "^1.0.0 || ^2.0.0" diff "5.0.0" @@ -7975,15 +7740,15 @@ rollup-plugin-terser@^7.0.2: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup-plugin-visualizer@^5.5.2: - version "5.5.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.5.2.tgz#ae2130ee5ae4a2d901e764e492b71357cb95eed7" - integrity sha512-sh+P9KhuWTzeStyRA5yNZpoEFGuj5Ph34JLMa9+muhU8CneFf9L0XE4fmAwAojJLWp//uLUEyytBPSCdZEg5AA== +rollup-plugin-visualizer@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.6.0.tgz#06aa7cf3fd504a29d404335700f2a3f28ebb33f3" + integrity sha512-CKcc8GTUZjC+LsMytU8ocRr/cGZIfMR7+mdy4YnlyetlmIl/dM8BMnOEpD4JPIGt+ZVW7Db9ZtSsbgyeBH3uTA== dependencies: - nanoid "^3.1.22" - open "^7.4.2" + nanoid "^3.1.32" + open "^8.4.0" source-map "^0.7.3" - yargs "^16.2.0" + yargs "^17.3.1" rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: version "2.8.2" @@ -7992,10 +7757,10 @@ rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.61.1: - version "2.61.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.61.1.tgz#1a5491f84543cf9e4caf6c61222d9a3f8f2ba454" - integrity sha512-BbTXlEvB8d+XFbK/7E5doIcRtxWPRiqr0eb5vQ0+2paMM04Ye4PZY5nHOQef2ix24l/L0SpLd5hwcH15QHPdvA== +rollup@^2.68.0: + version "2.68.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.68.0.tgz#6ccabfd649447f8f21d62bf41662e5caece3bd66" + integrity sha512-XrMKOYK7oQcTio4wyTz466mucnd8LzkiZLozZ4Rz0zQD+HeX4nUK4B8GrTX/2EvN2/vBF/i2WnaXboPxo0JylA== optionalDependencies: fsevents "~2.3.2" @@ -8031,7 +7796,7 @@ safe-buffer@^5.1.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -8078,7 +7843,7 @@ scheduler@^0.19.1: loose-envify "^1.1.0" object-assign "^4.1.1" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -8432,6 +8197,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.matchall@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" @@ -8476,13 +8250,6 @@ string.prototype.trimright@^2.1.1: define-properties "^1.1.3" function-bind "^1.1.1" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -8497,6 +8264,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -8877,11 +8651,6 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -8899,11 +8668,6 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - util.promisify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" @@ -9128,10 +8892,10 @@ yargs-parser@^18.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.0.0: + version "21.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55" + integrity sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA== yargs@^15.3.1: version "15.3.1" @@ -9150,15 +8914,15 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +yargs@^17.3.1: + version "17.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" + integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== dependencies: cliui "^7.0.2" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" - string-width "^4.2.0" + string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^20.2.2" + yargs-parser "^21.0.0" From f53d8b18c0396dee1052b3bbdbbf3e68b589dc99 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 26 Feb 2022 20:53:53 +0100 Subject: [PATCH 30/76] chore: eslint no-unnecessary-condition (#3344) * chore(eslint): turn on no-unnecessary-condition rule * chore(eslint): turn on no-unnecessary-condition rule remove unnecessary optional chainings in tests * chore(eslint): keep the matchMedia check even though it can never be undefined, except in tests, and I couldn't figure out how to mock that properly * chore(eslint): remove unnecessary checks in devtools * chore(eslint): addEventListener should exist on window if window is defined, which is checked by isServer * chore(eslint): assign default object to options instead of re-assigning it. In any case, the optional chaining is unnecessary * chore(eslint): action.type should always be defined * chore(eslint): keep the fallback for console * chore(eslint): one rule always complains so up-casting false to boolean * chore(eslint): if we have a behaviour, we also have na onFetch * chore(eslint): parseFilters always returns an object as it falls back to an empty object internally, so the falsy check didn't do anything * chore(eslint): upcast previous result to be potentially undefined to make the optinal chains necessary * fix issues after updating to alpha --- .eslintrc | 2 ++ src/broadcastQueryClient-experimental/index.ts | 7 ++----- src/core/focusManager.ts | 2 +- src/core/hydration.ts | 10 +++++----- src/core/onlineManager.ts | 2 +- src/core/query.ts | 6 ++---- src/core/queryClient.ts | 6 +++--- src/core/queryObserver.ts | 8 ++++++-- src/core/tests/focusManager.test.tsx | 11 +++++------ src/core/tests/hydration.test.tsx | 4 ++-- src/core/tests/mutationCache.test.tsx | 8 ++++---- src/core/tests/mutations.test.tsx | 2 +- src/core/tests/onlineManager.test.tsx | 10 ++++------ src/core/tests/queryObserver.test.tsx | 4 ++-- src/devtools/devtools.tsx | 8 ++++---- src/reactjs/tests/ssr-hydration.test.tsx | 10 +--------- src/reactjs/tests/ssr.test.tsx | 2 +- src/reactjs/tests/suspense.test.tsx | 8 +------- src/reactjs/tests/utils.tsx | 14 ++++++++++++++ 19 files changed, 61 insertions(+), 63 deletions(-) diff --git a/.eslintrc b/.eslintrc index fcf0d28e86..a39b84e27b 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,6 +12,7 @@ "es6": true }, "parserOptions": { + "project": "./tsconfig.json", "sourceType": "module" }, "rules": { @@ -21,6 +22,7 @@ "@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unnecessary-condition": "error", "@typescript-eslint/no-inferrable-types": [ "error", { diff --git a/src/broadcastQueryClient-experimental/index.ts b/src/broadcastQueryClient-experimental/index.ts index 6a36d1c339..89823ba9d7 100644 --- a/src/broadcastQueryClient-experimental/index.ts +++ b/src/broadcastQueryClient-experimental/index.ts @@ -24,7 +24,7 @@ export function broadcastQueryClient({ const queryCache = queryClient.getQueryCache() queryClient.getQueryCache().subscribe(queryEvent => { - if (transaction || !queryEvent?.query) { + if (transaction) { return } @@ -32,10 +32,7 @@ export function broadcastQueryClient({ query: { queryHash, queryKey, state }, } = queryEvent - if ( - queryEvent.type === 'updated' && - queryEvent.action?.type === 'success' - ) { + if (queryEvent.type === 'updated' && queryEvent.action.type === 'success') { channel.postMessage({ type: 'updated', queryHash, diff --git a/src/core/focusManager.ts b/src/core/focusManager.ts index db08d0fb9a..2ffb62eaaf 100644 --- a/src/core/focusManager.ts +++ b/src/core/focusManager.ts @@ -14,7 +14,7 @@ export class FocusManager extends Subscribable { constructor() { super() this.setup = onFocus => { - if (!isServer && window?.addEventListener) { + if (!isServer) { const listener = () => onFocus() // Listen to visibillitychange and focus window.addEventListener('visibilitychange', listener, false) diff --git a/src/core/hydration.ts b/src/core/hydration.ts index d10e1c83f0..c45dd86ab2 100644 --- a/src/core/hydration.ts +++ b/src/core/hydration.ts @@ -75,14 +75,12 @@ function defaultShouldDehydrateQuery(query: Query) { export function dehydrate( client: QueryClient, - options?: DehydrateOptions + options: DehydrateOptions = {} ): DehydratedState { - options = options || {} - const mutations: DehydratedMutation[] = [] const queries: DehydratedQuery[] = [] - if (options?.dehydrateMutations !== false) { + if (options.dehydrateMutations !== false) { const shouldDehydrateMutation = options.shouldDehydrateMutation || defaultShouldDehydrateMutation @@ -96,7 +94,7 @@ export function dehydrate( }) } - if (options?.dehydrateQueries !== false) { + if (options.dehydrateQueries !== false) { const shouldDehydrateQuery = options.shouldDehydrateQuery || defaultShouldDehydrateQuery @@ -125,7 +123,9 @@ export function hydrate( const mutationCache = client.getMutationCache() const queryCache = client.getQueryCache() + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const mutations = (dehydratedState as DehydratedState).mutations || [] + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const queries = (dehydratedState as DehydratedState).queries || [] mutations.forEach(dehydratedMutation => { diff --git a/src/core/onlineManager.ts b/src/core/onlineManager.ts index 6efcf75aa6..7c3d832b63 100644 --- a/src/core/onlineManager.ts +++ b/src/core/onlineManager.ts @@ -14,7 +14,7 @@ export class OnlineManager extends Subscribable { constructor() { super() this.setup = onOnline => { - if (!isServer && window?.addEventListener) { + if (!isServer) { const listener = () => onOnline() // Listen to online window.addEventListener('online', listener, false) diff --git a/src/core/query.ts b/src/core/query.ts index 6eb0707870..ac3738b97e 100644 --- a/src/core/query.ts +++ b/src/core/query.ts @@ -420,9 +420,7 @@ export class Query< addSignalProperty(context) - if (this.options.behavior?.onFetch) { - this.options.behavior?.onFetch(context) - } + this.options.behavior?.onFetch(context) // Store state in case the current fetch needs to be reverted this.revertState = this.state @@ -463,7 +461,7 @@ export class Query< // Try to fetch the data this.retryer = createRetryer({ fn: context.fetchFn as () => TData, - abort: abortController?.abort?.bind(abortController), + abort: abortController?.abort.bind(abortController), onSuccess: data => { if (typeof data === 'undefined') { onError(new Error('Query data cannot be undefined') as any) diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts index cd96553ef6..2a730473d9 100644 --- a/src/core/queryClient.ts +++ b/src/core/queryClient.ts @@ -267,12 +267,12 @@ export class QueryClient { query.invalidate() }) - if (filters?.refetchType === 'none') { + if (filters.refetchType === 'none') { return Promise.resolve() } const refetchFilters: RefetchQueryFilters = { ...filters, - type: filters?.refetchType ?? filters?.type ?? 'active', + type: filters.refetchType ?? filters.type ?? 'active', } return this.refetchQueries(refetchFilters, options) }) @@ -302,7 +302,7 @@ export class QueryClient { query.fetch(undefined, { ...options, cancelRefetch: options?.cancelRefetch ?? true, - meta: { refetchPage: filters?.refetchPage }, + meta: { refetchPage: filters.refetchPage }, }) ) ) diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 8cc02210c2..f228606671 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -399,7 +399,9 @@ export class QueryObserver< ): QueryObserverResult { const prevQuery = this.currentQuery const prevOptions = this.options - const prevResult = this.currentResult + const prevResult = this.currentResult as + | QueryObserverResult + | undefined const prevResultState = this.currentResultState const prevResultOptions = this.currentResultOptions const queryChange = query !== prevQuery @@ -622,7 +624,9 @@ export class QueryObserver< return } - const prevQuery = this.currentQuery + const prevQuery = this.currentQuery as + | Query + | undefined this.currentQuery = query this.currentQueryInitialState = query.state this.previousQueryResult = this.currentResult diff --git a/src/core/tests/focusManager.test.tsx b/src/core/tests/focusManager.test.tsx index f747835638..b3086179ce 100644 --- a/src/core/tests/focusManager.test.tsx +++ b/src/core/tests/focusManager.test.tsx @@ -1,9 +1,11 @@ import { sleep } from '../utils' import { FocusManager } from '../focusManager' +import { setIsServer } from '../../reactjs/tests/utils' describe('focusManager', () => { let focusManager: FocusManager beforeEach(() => { + jest.resetModules() focusManager = new FocusManager() }) @@ -61,17 +63,14 @@ describe('focusManager', () => { globalThis.document = document }) - test('cleanup should still be undefined if window.addEventListener is not defined', async () => { - const { addEventListener } = globalThis.window - - // @ts-expect-error - globalThis.window.addEventListener = undefined + test('cleanup should still be undefined if window is not defined', async () => { + const restoreIsServer = setIsServer(true) const unsubscribe = focusManager.subscribe(() => undefined) expect(focusManager['cleanup']).toBeUndefined() unsubscribe() - globalThis.window.addEventListener = addEventListener + restoreIsServer() }) it('should replace default window listener when a new event listener is set', async () => { diff --git a/src/core/tests/hydration.test.tsx b/src/core/tests/hydration.test.tsx index aeda287386..b2730d9740 100644 --- a/src/core/tests/hydration.test.tsx +++ b/src/core/tests/hydration.test.tsx @@ -196,8 +196,8 @@ describe('dehydration and rehydration', () => { // This is testing implementation details that can change and are not // part of the public API, but is important for keeping the payload small - const dehydratedQuery = dehydrated?.queries.find( - query => query?.queryKey[0] === 'string' + const dehydratedQuery = dehydrated.queries.find( + query => query.queryKey[0] === 'string' ) expect(dehydratedQuery).toBeUndefined() diff --git a/src/core/tests/mutationCache.test.tsx b/src/core/tests/mutationCache.test.tsx index 8c1b8098ac..a094786e29 100644 --- a/src/core/tests/mutationCache.test.tsx +++ b/src/core/tests/mutationCache.test.tsx @@ -193,14 +193,14 @@ describe('mutationCache', () => { // we currently have no way to add multiple observers to the same mutation const currentMutation = observer1['currentMutation']! - currentMutation?.addObserver(observer1) - currentMutation?.addObserver(observer2) + currentMutation.addObserver(observer1) + currentMutation.addObserver(observer2) expect(currentMutation['observers'].length).toEqual(2) expect(queryClient.getMutationCache().getAll()).toHaveLength(1) - currentMutation?.removeObserver(observer1) - currentMutation?.removeObserver(observer2) + currentMutation.removeObserver(observer1) + currentMutation.removeObserver(observer2) expect(currentMutation['observers'].length).toEqual(0) expect(queryClient.getMutationCache().getAll()).toHaveLength(1) // wait for cacheTime to gc diff --git a/src/core/tests/mutations.test.tsx b/src/core/tests/mutations.test.tsx index acf9fb79b5..ab6494e6d5 100644 --- a/src/core/tests/mutations.test.tsx +++ b/src/core/tests/mutations.test.tsx @@ -341,7 +341,7 @@ describe('mutations', () => { // because no use case has been found const currentMutation = mutation['currentMutation']! expect(currentMutation['observers'].length).toEqual(1) - currentMutation?.addObserver(mutation) + currentMutation.addObserver(mutation) expect(currentMutation['observers'].length).toEqual(1) }) diff --git a/src/core/tests/onlineManager.test.tsx b/src/core/tests/onlineManager.test.tsx index e9d15cf1fd..78def64c60 100644 --- a/src/core/tests/onlineManager.test.tsx +++ b/src/core/tests/onlineManager.test.tsx @@ -1,5 +1,6 @@ import { OnlineManager } from '../onlineManager' import { sleep } from '../utils' +import { setIsServer } from '../../reactjs/tests/utils' describe('onlineManager', () => { let onlineManager: OnlineManager @@ -56,17 +57,14 @@ describe('onlineManager', () => { expect(remove2Spy).not.toHaveBeenCalled() }) - test('cleanup should still be undefined if window.addEventListener is not defined', async () => { - const { addEventListener } = globalThis.window - - // @ts-expect-error - globalThis.window.addEventListener = undefined + test('cleanup should still be undefined if window is not defined', async () => { + const restoreIsServer = setIsServer(true) const unsubscribe = onlineManager.subscribe(() => undefined) expect(onlineManager['cleanup']).toBeUndefined() unsubscribe() - globalThis.window.addEventListener = addEventListener + restoreIsServer() }) test('it should replace default window listener when a new event listener is set', async () => { diff --git a/src/core/tests/queryObserver.test.tsx b/src/core/tests/queryObserver.test.tsx index 2bee1ca21e..82af00c784 100644 --- a/src/core/tests/queryObserver.test.tsx +++ b/src/core/tests/queryObserver.test.tsx @@ -619,7 +619,7 @@ describe('queryObserver', () => { queryKey: key, queryFn: () => data, select: () => { - if (true) return selectedData1 + return selectedData1 }, placeholderData: placeholderData1, }) @@ -655,7 +655,7 @@ describe('queryObserver', () => { queryKey: key, queryFn: () => data, select: () => { - if (true) return selectedData + return selectedData }, placeholderData: placeholderData1, }) diff --git a/src/devtools/devtools.tsx b/src/devtools/devtools.tsx index 808d590426..b102a46ca3 100644 --- a/src/devtools/devtools.tsx +++ b/src/devtools/devtools.tsx @@ -122,7 +122,7 @@ export function ReactQueryDevtools({ const run = (moveEvent: MouseEvent) => { const delta = dragInfo.pageY - moveEvent.pageY - const newHeight = dragInfo?.originalHeight + delta + const newHeight = dragInfo.originalHeight + delta setDevtoolsHeight(newHeight) @@ -153,13 +153,13 @@ export function ReactQueryDevtools({ const ref = panelRef.current if (ref) { const handlePanelTransitionStart = () => { - if (ref && isResolvedOpen) { + if (isResolvedOpen) { ref.style.visibility = 'visible' } } const handlePanelTransitionEnd = () => { - if (ref && !isResolvedOpen) { + if (!isResolvedOpen) { ref.style.visibility = 'hidden' } } @@ -925,7 +925,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef< >
diff --git a/src/reactjs/tests/ssr-hydration.test.tsx b/src/reactjs/tests/ssr-hydration.test.tsx index 3ea1111a22..0dbfacf248 100644 --- a/src/reactjs/tests/ssr-hydration.test.tsx +++ b/src/reactjs/tests/ssr-hydration.test.tsx @@ -9,15 +9,7 @@ import { dehydrate, hydrate, } from '../..' -import * as utils from '../../core/utils' -import { createQueryClient, mockLogger, sleep } from './utils' - -// This monkey-patches the isServer-value from utils, -// so that we can pretend to be in a server environment -function setIsServer(isServer: boolean) { - // @ts-ignore - utils.isServer = isServer -} +import { createQueryClient, mockLogger, setIsServer, sleep } from './utils' async function fetchData(value: TData, ms?: number): Promise { await sleep(ms || 1) diff --git a/src/reactjs/tests/ssr.test.tsx b/src/reactjs/tests/ssr.test.tsx index 7259a3f960..009c361f06 100644 --- a/src/reactjs/tests/ssr.test.tsx +++ b/src/reactjs/tests/ssr.test.tsx @@ -134,7 +134,7 @@ describe('Server Side Rendering', () => { const query = useInfiniteQuery(key, queryFn) return (
    - {query.data?.pages?.map(page => ( + {query.data?.pages.map(page => (
  • {page}
  • ))}
diff --git a/src/reactjs/tests/suspense.test.tsx b/src/reactjs/tests/suspense.test.tsx index c913bef0a9..d6d4263317 100644 --- a/src/reactjs/tests/suspense.test.tsx +++ b/src/reactjs/tests/suspense.test.tsx @@ -877,8 +877,6 @@ describe("useQuery's in Suspense mode", () => { }) it('should error catched in error boundary without infinite loop when enabled changed', async () => { - const succeed = false - function Page() { const queryKeys = '1' const [enabled, setEnabled] = React.useState(false) @@ -887,11 +885,7 @@ describe("useQuery's in Suspense mode", () => { [queryKeys], async () => { await sleep(10) - if (!succeed) { - throw new Error('Suspense Error Bingo') - } else { - return 'data' - } + throw new Error('Suspense Error Bingo') }, { retry: false, diff --git a/src/reactjs/tests/utils.tsx b/src/reactjs/tests/utils.tsx index 73851a98b7..e6e0bd77fd 100644 --- a/src/reactjs/tests/utils.tsx +++ b/src/reactjs/tests/utils.tsx @@ -7,6 +7,7 @@ import { QueryClientConfig, QueryClientProvider, } from '../..' +import * as utils from '../../core/utils' export function createQueryClient(config?: QueryClientConfig): QueryClient { jest.spyOn(console, 'error').mockImplementation(() => undefined) @@ -94,3 +95,16 @@ export const executeMutation = ( ): Promise => { return queryClient.getMutationCache().build(queryClient, options).execute() } + +// This monkey-patches the isServer-value from utils, +// so that we can pretend to be in a server environment +export function setIsServer(isServer: boolean) { + const original = utils.isServer + // @ts-ignore + utils.isServer = isServer + + return () => { + // @ts-ignore + utils.isServer = original + } +} From f42689951de3ef5c56150bcc90fffb0cdd54bbd5 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 26 Feb 2022 21:20:24 +0100 Subject: [PATCH 31/76] fix(mutations): avoid infinite loading states if callbacks return an error (#3343) * fix(mutations): avoid infinite loading states if callbacks return an error add failing test cases * fix(mutations): avoid infinite loading states if callbacks return an error by making sure we always dispatch the error to go to error state internally; re-writing to async-await because it has better support than promise.finally, and the flow is also easier to reason about here * fix(mutations): fix merge conflicts --- src/core/mutation.ts | 133 +++++++++++-------------- src/reactjs/tests/useMutation.test.tsx | 111 +++++++++++++++++++++ 2 files changed, 168 insertions(+), 76 deletions(-) diff --git a/src/core/mutation.ts b/src/core/mutation.ts index 2db6a609bf..3880d75cba 100644 --- a/src/core/mutation.ts +++ b/src/core/mutation.ts @@ -160,35 +160,7 @@ export class Mutation< return this.execute() } - execute(): Promise { - let data: TData - - const restored = this.state.status === 'loading' - - let promise = Promise.resolve() - - if (!restored) { - this.dispatch({ type: 'loading', variables: this.options.variables! }) - promise = promise - .then(() => { - // Notify cache callback - this.mutationCache.config.onMutate?.( - this.state.variables, - this as Mutation - ) - }) - .then(() => this.options.onMutate?.(this.state.variables!)) - .then(context => { - if (context !== this.state.context) { - this.dispatch({ - type: 'loading', - context, - variables: this.state.variables, - }) - } - }) - } - + async execute(): Promise { const executeMutation = () => { this.retryer = createRetryer({ fn: () => { @@ -214,38 +186,51 @@ export class Mutation< return this.retryer.promise } - return promise - .then(executeMutation) - .then(result => { - data = result + const restored = this.state.status === 'loading' + try { + if (!restored) { + this.dispatch({ type: 'loading', variables: this.options.variables! }) // Notify cache callback - this.mutationCache.config.onSuccess?.( - data, + this.mutationCache.config.onMutate?.( this.state.variables, - this.state.context, this as Mutation ) - }) - .then(() => - this.options.onSuccess?.( - data, - this.state.variables!, - this.state.context! - ) + const context = await this.options.onMutate?.(this.state.variables!) + if (context !== this.state.context) { + this.dispatch({ + type: 'loading', + context, + variables: this.state.variables, + }) + } + } + const data = await executeMutation() + + // Notify cache callback + this.mutationCache.config.onSuccess?.( + data, + this.state.variables, + this.state.context, + this as Mutation ) - .then(() => - this.options.onSettled?.( - data, - null, - this.state.variables!, - this.state.context - ) + + await this.options.onSuccess?.( + data, + this.state.variables!, + this.state.context! ) - .then(() => { - this.dispatch({ type: 'success', data }) - return data - }) - .catch(error => { + + await this.options.onSettled?.( + data, + null, + this.state.variables!, + this.state.context + ) + + this.dispatch({ type: 'success', data }) + return data + } catch (error) { + try { // Notify cache callback this.mutationCache.config.onError?.( error, @@ -258,27 +243,23 @@ export class Mutation< this.logger.error(error) } - return Promise.resolve() - .then(() => - this.options.onError?.( - error, - this.state.variables!, - this.state.context - ) - ) - .then(() => - this.options.onSettled?.( - undefined, - error, - this.state.variables!, - this.state.context - ) - ) - .then(() => { - this.dispatch({ type: 'error', error }) - throw error - }) - }) + await this.options.onError?.( + error as TError, + this.state.variables!, + this.state.context + ) + + await this.options.onSettled?.( + undefined, + error as TError, + this.state.variables!, + this.state.context + ) + throw error + } finally { + this.dispatch({ type: 'error', error: error as TError }) + } + } } private dispatch(action: Action): void { diff --git a/src/reactjs/tests/useMutation.test.tsx b/src/reactjs/tests/useMutation.test.tsx index 8bf5e06b33..24b9f41c2c 100644 --- a/src/reactjs/tests/useMutation.test.tsx +++ b/src/reactjs/tests/useMutation.test.tsx @@ -855,4 +855,115 @@ describe('useMutation', () => { undefined ) }) + + test('should go to error state if onSuccess callback errors', async () => { + const error = new Error('error from onSuccess') + const onError = jest.fn() + + function Page() { + const mutation = useMutation( + async (_text: string) => { + await sleep(10) + return 'result' + }, + { + onSuccess: () => Promise.reject(error), + onError, + } + ) + + return ( +
+ +
status: {mutation.status}
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('status: idle') + + rendered.getByRole('button', { name: /mutate/i }).click() + + await rendered.findByText('status: error') + + expect(onError).toHaveBeenCalledWith(error, 'todo', undefined) + }) + + test('should go to error state if onError callback errors', async () => { + const error = new Error('error from onError') + const mutateFnError = new Error('mutateFnError') + + function Page() { + const mutation = useMutation( + async (_text: string) => { + await sleep(10) + throw mutateFnError + }, + { + onError: () => Promise.reject(error), + } + ) + + return ( +
+ +
+ error:{' '} + {mutation.error instanceof Error ? mutation.error.message : 'null'}, + status: {mutation.status} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('error: null, status: idle') + + rendered.getByRole('button', { name: /mutate/i }).click() + + await rendered.findByText('error: mutateFnError, status: error') + }) + + test('should go to error state if onSettled callback errors', async () => { + const error = new Error('error from onSettled') + const mutateFnError = new Error('mutateFnError') + const onError = jest.fn() + + function Page() { + const mutation = useMutation( + async (_text: string) => { + await sleep(10) + throw mutateFnError + }, + { + onSettled: () => Promise.reject(error), + onError, + } + ) + + return ( +
+ +
+ error:{' '} + {mutation.error instanceof Error ? mutation.error.message : 'null'}, + status: {mutation.status} +
+
+ ) + } + + const rendered = renderWithClient(queryClient, ) + + await rendered.findByText('error: null, status: idle') + + rendered.getByRole('button', { name: /mutate/i }).click() + + await rendered.findByText('error: mutateFnError, status: error') + + expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) + }) }) From 02fd7939f505d1c5878666c8208bd487eeabcdfd Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sun, 27 Feb 2022 08:41:29 +0100 Subject: [PATCH 32/76] fix(react-native): bring back check for window.addEventListener (#3345) because it does not exist in RN, but window is defined --- src/core/focusManager.ts | 4 +++- src/core/onlineManager.ts | 4 +++- src/core/tests/focusManager.test.tsx | 13 +++++++++++++ src/core/tests/onlineManager.test.tsx | 13 +++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/core/focusManager.ts b/src/core/focusManager.ts index 2ffb62eaaf..7bd68b6781 100644 --- a/src/core/focusManager.ts +++ b/src/core/focusManager.ts @@ -14,7 +14,9 @@ export class FocusManager extends Subscribable { constructor() { super() this.setup = onFocus => { - if (!isServer) { + // addEventListener does not exist in React Native, but window does + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!isServer && window.addEventListener) { const listener = () => onFocus() // Listen to visibillitychange and focus window.addEventListener('visibilitychange', listener, false) diff --git a/src/core/onlineManager.ts b/src/core/onlineManager.ts index 7c3d832b63..844e232e03 100644 --- a/src/core/onlineManager.ts +++ b/src/core/onlineManager.ts @@ -14,7 +14,9 @@ export class OnlineManager extends Subscribable { constructor() { super() this.setup = onOnline => { - if (!isServer) { + // addEventListener does not exist in React Native, but window does + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!isServer && window.addEventListener) { const listener = () => onOnline() // Listen to online window.addEventListener('online', listener, false) diff --git a/src/core/tests/focusManager.test.tsx b/src/core/tests/focusManager.test.tsx index b3086179ce..3089abfc94 100644 --- a/src/core/tests/focusManager.test.tsx +++ b/src/core/tests/focusManager.test.tsx @@ -73,6 +73,19 @@ describe('focusManager', () => { restoreIsServer() }) + test('cleanup should still be undefined if window.addEventListener is not defined', async () => { + const { addEventListener } = globalThis.window + + // @ts-expect-error + globalThis.window.addEventListener = undefined + + const unsubscribe = focusManager.subscribe(() => undefined) + expect(focusManager['cleanup']).toBeUndefined() + + unsubscribe() + globalThis.window.addEventListener = addEventListener + }) + it('should replace default window listener when a new event listener is set', async () => { const addEventListenerSpy = jest.spyOn( globalThis.window, diff --git a/src/core/tests/onlineManager.test.tsx b/src/core/tests/onlineManager.test.tsx index 78def64c60..cdf6a0ce54 100644 --- a/src/core/tests/onlineManager.test.tsx +++ b/src/core/tests/onlineManager.test.tsx @@ -67,6 +67,19 @@ describe('onlineManager', () => { restoreIsServer() }) + test('cleanup should still be undefined if window.addEventListener is not defined', async () => { + const { addEventListener } = globalThis.window + + // @ts-expect-error + globalThis.window.addEventListener = undefined + + const unsubscribe = onlineManager.subscribe(() => undefined) + expect(onlineManager['cleanup']).toBeUndefined() + + unsubscribe() + globalThis.window.addEventListener = addEventListener + }) + test('it should replace default window listener when a new event listener is set', async () => { const addEventListenerSpy = jest.spyOn( globalThis.window, From 9d260e78b45e93ec6b5043de3bb61cac2ddb4345 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Mon, 28 Feb 2022 09:04:32 +0100 Subject: [PATCH 33/76] refactor(queryObserver): get rid of double-defaulting of options in getOptimisticResult every call to getOptimisticResult already contains defaulted options, which we can enforce on type level by only accepting DefaultedQueryObserverOptions --- src/core/infiniteQueryObserver.ts | 3 ++- src/core/queryObserver.ts | 12 ++++-------- src/core/types.ts | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/core/infiniteQueryObserver.ts b/src/core/infiniteQueryObserver.ts index 063b01ae89..1622ed0777 100644 --- a/src/core/infiniteQueryObserver.ts +++ b/src/core/infiniteQueryObserver.ts @@ -1,4 +1,5 @@ import type { + DefaultedInfiniteQueryObserverOptions, FetchNextPageOptions, FetchPreviousPageOptions, InfiniteData, @@ -84,7 +85,7 @@ export class InfiniteQueryObserver< } getOptimisticResult( - options: InfiniteQueryObserverOptions< + options: DefaultedInfiniteQueryObserverOptions< TQueryFnData, TError, TData, diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index f228606671..95b0d080bf 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -1,4 +1,4 @@ -import { RefetchPageFilters } from './types' +import { DefaultedQueryObserverOptions, RefetchPageFilters } from './types' import { isServer, isValidTimeout, @@ -205,7 +205,7 @@ export class QueryObserver< } getOptimisticResult( - options: QueryObserverOptions< + options: DefaultedQueryObserverOptions< TQueryFnData, TError, TData, @@ -213,13 +213,9 @@ export class QueryObserver< TQueryKey > ): QueryObserverResult { - const defaultedOptions = this.client.defaultQueryOptions(options) - - const query = this.client - .getQueryCache() - .build(this.client, defaultedOptions) + const query = this.client.getQueryCache().build(this.client, options) - return this.createResult(query, defaultedOptions) + return this.createResult(query, options) } getCurrentResult(): QueryObserverResult { diff --git a/src/core/types.ts b/src/core/types.ts index 0dab1232ca..8ec2927b9b 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -246,6 +246,23 @@ export interface InfiniteQueryObserverOptions< TQueryKey > {} +export type DefaultedInfiniteQueryObserverOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey +> = WithRequired< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + 'useErrorBoundary' | 'refetchOnReconnect' +> + export interface FetchQueryOptions< TQueryFnData = unknown, TError = unknown, From 338da80d09e337faefbab8312892d38cf369a7a6 Mon Sep 17 00:00:00 2001 From: Alvin Huang Date: Tue, 1 Mar 2022 18:37:01 +0900 Subject: [PATCH 34/76] fix(createAsyncStoragePersister): persistClient respects throttleTime (#3331) (#3336) --- .../asyncThrottle.ts | 58 ++++++++ src/createAsyncStoragePersister/index.ts | 41 +----- .../tests/asyncThrottle.test.ts | 127 ++++++++++++++++++ 3 files changed, 186 insertions(+), 40 deletions(-) create mode 100644 src/createAsyncStoragePersister/asyncThrottle.ts create mode 100644 src/createAsyncStoragePersister/tests/asyncThrottle.test.ts diff --git a/src/createAsyncStoragePersister/asyncThrottle.ts b/src/createAsyncStoragePersister/asyncThrottle.ts new file mode 100644 index 0000000000..940f03df66 --- /dev/null +++ b/src/createAsyncStoragePersister/asyncThrottle.ts @@ -0,0 +1,58 @@ +export interface AsyncThrottleOptions { + interval?: number + onError?: (error: unknown) => void +} + +const noop = () => { + /* do nothing */ +} + +export function asyncThrottle( + func: (...args: Args) => Promise, + { interval = 1000, onError = noop }: AsyncThrottleOptions = {} +) { + if (typeof func !== 'function') throw new Error('argument is not function.') + + let running = false + let lastTime = 0 + let timeout: ReturnType + let currentArgs: Args | null = null + + const execFunc = async () => { + if (currentArgs) { + const args = currentArgs + currentArgs = null + try { + running = true + await func(...args) + } catch (error) { + onError(error) + } finally { + lastTime = Date.now() // this line must after 'func' executed to avoid two 'func' running in concurrent. + running = false + } + } + } + + const delayFunc = async () => { + clearTimeout(timeout) + timeout = setTimeout(() => { + if (running) { + delayFunc() // Will come here when 'func' execution time is greater than the interval. + } else { + execFunc() + } + }, interval) + } + + return (...args: Args) => { + currentArgs = args + + const tooSoon = Date.now() - lastTime < interval + if (running || tooSoon) { + delayFunc() + } else { + execFunc() + } + } +} diff --git a/src/createAsyncStoragePersister/index.ts b/src/createAsyncStoragePersister/index.ts index 9a1b7b6677..57122af0be 100644 --- a/src/createAsyncStoragePersister/index.ts +++ b/src/createAsyncStoragePersister/index.ts @@ -1,4 +1,5 @@ import { PersistedClient, Persister } from '../persistQueryClient' +import { asyncThrottle } from './asyncThrottle' interface AsyncStorage { getItem: (key: string) => Promise @@ -50,43 +51,3 @@ export const createAsyncStoragePersister = ({ removeClient: () => storage.removeItem(key), } } - -function asyncThrottle( - func: (...args: Args) => Promise, - { interval = 1000, limit = 1 }: { interval?: number; limit?: number } = {} -) { - if (typeof func !== 'function') throw new Error('argument is not function.') - const running = { current: false } - let lastTime = 0 - let timeout: ReturnType - const queue: Array = [] - return (...args: Args) => - (async () => { - if (running.current) { - lastTime = Date.now() - if (queue.length > limit) { - queue.shift() - } - - queue.push(args) - clearTimeout(timeout) - } - if (Date.now() - lastTime > interval) { - running.current = true - await func(...args) - lastTime = Date.now() - running.current = false - } else { - if (queue.length > 0) { - const lastArgs = queue[queue.length - 1]! - timeout = setTimeout(async () => { - if (!running.current) { - running.current = true - await func(...lastArgs) - running.current = false - } - }, interval) - } - } - })() -} diff --git a/src/createAsyncStoragePersister/tests/asyncThrottle.test.ts b/src/createAsyncStoragePersister/tests/asyncThrottle.test.ts new file mode 100644 index 0000000000..54f7ee3dd0 --- /dev/null +++ b/src/createAsyncStoragePersister/tests/asyncThrottle.test.ts @@ -0,0 +1,127 @@ +import { asyncThrottle } from '../asyncThrottle' +import { sleep as delay } from '../../reactjs/tests/utils' + +describe('asyncThrottle', () => { + test('basic', async () => { + const interval = 10 + const execTimeStamps: number[] = [] + const mockFunc = jest.fn( + async (id: number, complete?: (value?: unknown) => void) => { + await delay(1) + execTimeStamps.push(Date.now()) + if (complete) { + complete(id) + } + } + ) + const testFunc = asyncThrottle(mockFunc, { interval }) + + testFunc(1) + await delay(1) + testFunc(2) + await delay(1) + await new Promise(resolve => testFunc(3, resolve)) + + expect(mockFunc).toBeCalledTimes(2) + expect(mockFunc.mock.calls[1]?.[0]).toBe(3) + expect(execTimeStamps.length).toBe(2) + expect(execTimeStamps[1]! - execTimeStamps[0]!).toBeGreaterThanOrEqual( + interval + ) + }) + + test('Bug #3331 case 1: Special timing', async () => { + const interval = 1000 + const execTimeStamps: number[] = [] + const mockFunc = jest.fn( + async (id: number, complete?: (value?: unknown) => void) => { + await delay(30) + execTimeStamps.push(Date.now()) + if (complete) { + complete(id) + } + } + ) + const testFunc = asyncThrottle(mockFunc, { interval }) + + testFunc(1) + testFunc(2) + await delay(35) + testFunc(3) + await delay(35) + await new Promise(resolve => testFunc(4, resolve)) + + expect(mockFunc).toBeCalledTimes(2) + expect(mockFunc.mock.calls[1]?.[0]).toBe(4) + expect(execTimeStamps.length).toBe(2) + expect(execTimeStamps[1]! - execTimeStamps[0]!).toBeGreaterThanOrEqual( + interval + ) + }) + + test('Bug #3331 case 2: "func" execution time is greater than the interval.', async () => { + const interval = 1000 + const execTimeStamps: number[] = [] + const mockFunc = jest.fn( + async (id: number, complete?: (value?: unknown) => void) => { + await delay(interval + 10) + execTimeStamps.push(Date.now()) + if (complete) { + complete(id) + } + } + ) + const testFunc = asyncThrottle(mockFunc, { interval }) + + testFunc(1) + testFunc(2) + await new Promise(resolve => testFunc(3, resolve)) + + expect(mockFunc).toBeCalledTimes(2) + expect(mockFunc.mock.calls[1]?.[0]).toBe(3) + expect(execTimeStamps.length).toBe(2) + expect(execTimeStamps[1]! - execTimeStamps[0]!).toBeGreaterThanOrEqual( + interval + ) + }) + + test('"func" throw error not break next invoke', async () => { + const mockFunc = jest.fn( + async (id: number, complete?: (value?: unknown) => void) => { + if (id === 1) throw new Error('error') + await delay(1) + if (complete) { + complete(id) + } + } + ) + const testFunc = asyncThrottle(mockFunc, { interval: 10 }) + + testFunc(1) + await delay(1) + await new Promise(resolve => testFunc(2, resolve)) + + expect(mockFunc).toBeCalledTimes(2) + expect(mockFunc.mock.calls[1]?.[0]).toBe(2) + }) + + test('"onError" should be called when "func" throw error', done => { + const err = new Error('error') + const handleError = (e: unknown) => { + expect(e).toBe(err) + done() + } + + const testFunc = asyncThrottle( + () => { + throw err + }, + { onError: handleError } + ) + testFunc() + }) + + test('should throw error when "func" is not a function', () => { + expect(() => asyncThrottle(1 as any)).toThrowError() + }) +}) From 154b5c1ee04471d0b32ad38b8e0375dc39da760d Mon Sep 17 00:00:00 2001 From: Jonathan Stanley Date: Thu, 3 Mar 2022 16:07:23 -0500 Subject: [PATCH 35/76] docs: update persistQueryClient.md with Persister docs (#3356) * docs: add idb example * docs: consolidate sections storing ~> persistQueryClientSave restoring ~> persistQueryClientRestore * docs: create section for persisters * docs: focus cacheTime docs persistQueryClient and createWebStoragePersister are unrelated * docs: add tip for indexed db * docs: cleanup intro * docs: note additional interfaces available * docs: reorder api to be more intuitive * docs: improve wording --- docs/src/pages/plugins/persistQueryClient.md | 157 +++++++++++-------- 1 file changed, 92 insertions(+), 65 deletions(-) diff --git a/docs/src/pages/plugins/persistQueryClient.md b/docs/src/pages/plugins/persistQueryClient.md index 75aaac23af..aa31b064dc 100644 --- a/docs/src/pages/plugins/persistQueryClient.md +++ b/docs/src/pages/plugins/persistQueryClient.md @@ -3,25 +3,25 @@ id: persistQueryClient title: persistQueryClient --- -`persistQueryClient` is a utility for persisting the state of your queryClient and its caches for later use. Different **persisters** can be used to store your client and cache to many different storage layers. +This is set of utilities for interacting with "persisters" which save your queryClient for later use. Different **persisters** can be used to store your client and cache to many different storage layers. -## Officially Supported Persisters +## Build Persisters - [createWebStoragePersister](/plugins/createWebStoragePersister) - [createAsyncStoragePersister](/plugins/createAsyncStoragePersister) +- [create a custom persister](#persisters) -## Installation +## How It Works -This utility comes packaged with `react-query` and is available under the `react-query/persistQueryClient` import. +**IMPORTANT** - for persist to work properly, you probably want to pass `QueryClient` a `cacheTime` value to override the default during hydration (as shown above). -## Usage +If it is not set when creating the `QueryClient` instance, it will default to `300000` (5 minutes) for hydration, and the stored cache will be discarded after 5 minutes of inactivity. This is the default garbage collection behavior. -Import the `persistQueryClient` function, and pass it your `QueryClient` instance (with a `cacheTime` set), and a Persister interface (there are multiple persister types you can use): +It should be set as the same value or higher than persistQueryClient's `maxAge` option. E.g. if `maxAge` is 24 hours (the default) then `cacheTime` should be 24 hours or higher. If lower than `maxAge`, garbage collection will kick in and discard the stored cache earlier than expected. -```ts -import { persistQueryClient } from 'react-query/persistQueryClient' -import { createWebStoragePersister } from 'react-query/createWebStoragePersister' +You can also pass it `Infinity` to disable garbage collection behavior entirely. +```ts const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -29,76 +29,59 @@ const queryClient = new QueryClient({ }, }, }) - -const localStoragePersister = createWebStoragePersister({ - storage: window.localStorage, -}) - -persistQueryClient({ - queryClient, - persister: localStoragePersister, -}) ``` -**IMPORTANT** - for persist to work properly, you need to pass `QueryClient` a `cacheTime` value to override the default during hydration (as shown above). - -If it is not set when creating the `QueryClient` instance, it will default to `300000` (5 minutes) for hydration, and the stored cache will be discarded after 5 minutes of inactivity. This is the default garbage collection behavior. - -It should be set as the same value or higher than persistQueryClient's `maxAge` option. E.g. if `maxAge` is 24 hours (the default) then `cacheTime` should be 24 hours or higher. If lower than `maxAge`, garbage collection will kick in and discard the stored cache earlier than expected. - -You can also pass it `Infinity` to disable garbage collection behavior entirely. +### Environment Checking +A check for window `undefined` is performed prior to saving/restoring/removing your data (avoids build errors). -## How does it work? +### Cache Busting -- A check for window `undefined` is performed prior to saving/restoring/removing your data (avoids build errors). - -### Storing - -As you use your application: - -- When your query/mutation cache is updated, it will be [`dehydrated`](../reference/hydration#dehydrate) and stored by the persister you provided. The officially supported persisters throttle this action to happen at most every 1 second to save on potentially expensive writes, but can be customized as you see fit. - -#### Cache Busting - -Sometimes you may make changes to your application or data that immediately invalidate any and all cached data. If and when this happens, you can pass a `buster` string option to `persistQueryClient`, and if the cache that is found does not also have that buster string, it will be discarded. +Sometimes you may make changes to your application or data that immediately invalidate any and all cached data. If and when this happens, you can pass a `buster` string option. If the cache that is found does not also have that buster string, it will be discarded. The following several functions accept this option: ```ts persistQueryClient({ queryClient, persister, buster: buildHash }) +persistQueryClientSave({ queryClient, persister, buster: buildHash }) +persistQueryClientRestore({ queryClient, persister, buster: buildHash }) ``` -### Restoring - -When you reload/bootstrap your app: +### Removal -- Attempts to [`hydrate`](../reference/hydration#hydrate) a previously persisted dehydrated query/mutation cache from the persister back into the query cache of the passed query client. -- If a cache is found that is older than the `maxAge` (which by default is 24 hours), it will be discarded. This can be customized as you see fit. +If data is found to be any of the following: -### Removal +1. expired (see `maxAge`) +2. busted (see `buster`) +3. error (ex: `throws ...`) +4. empty (ex: `undefined`) -- If data is found to be expired (see `maxAge`), busted (see `buster`), error (ex: `throws ...`), or empty (ex: `undefined`), the persister `removeClient()` is called and the cache is immediately discarded. +the persister `removeClient()` is called and the cache is immediately discarded. ## API -### `persistQueryClientRestore` +### `persistQueryClientSave` + +- Your query/mutation are [`dehydrated`](../reference/hydration#dehydrate) and stored by the persister you provided. +- `createWebStoragePersister` and `createAsyncStoragePersister` throttle this action to happen at most every 1 second to save on potentially expensive writes. Review their documentation to see how to customize their throttle timing. -This will attempt to restore a persister's stored cached to the query cache of the passed queryClient. +You can use this to explicitly persist the cache at the moment(s) you choose. ```ts -persistQueryClientRestore({ +persistQueryClientSave({ queryClient, persister, - maxAge = 1000 * 60 * 60 * 24, // 24 hours buster = '', - hydrateOptions = undefined, + dehydrateOptions = undefined, }) ``` -### `persistQueryClientSave` +### `persistQueryClientSubscribe` + +Runs `persistQueryClientSave` whenever the cache changes for your `queryClient`. For example: you might initiate the `subscribe` when a user logs-in and checks "Remember me". -This will attempt to save the current query cache with the persister. You can use this to explicitly persist the cache at the moments you choose. +- It returns an `unsubscribe` function which you can use to discontinue the monitor; ending the updates to the persisted cache. +- If you want to erase the persisted cache after the `unsubscribe`, you can send a new `buster` to `persistQueryClientRestore` which will trigger the persister's `removeClient` function and discard the persisted cache. ```ts -persistQueryClientSave({ +persistQueryClientSubscribe({ queryClient, persister, buster = '', @@ -106,25 +89,31 @@ persistQueryClientSave({ }) ``` -### `persistQueryClientSubscribe` +### `persistQueryClientRestore` -This will subscribe to query cache updates which will run `persistQueryClientSave`. For example: you might initiate the `subscribe` when a user logs-in and checks "Remember me". +- Attempts to [`hydrate`](../reference/hydration#hydrate) a previously persisted dehydrated query/mutation cache from the persister back into the query cache of the passed query client. +- If a cache is found that is older than the `maxAge` (which by default is 24 hours), it will be discarded. This timing can be customized as you see fit. -- It returns an `unsubscribe` function which you can use to discontinue the monitor; ending the updates to the persisted cache. -- If you want to erase the persisted cache after the `unsubscribe`, you can send a new `buster` to `persistQueryClientRestore` which will trigger the persister's `removeClient` function and discard the persisted cache. +You can use this to restore the cache at moment(s) you choose. ```ts -persistQueryClientSubscribe({ +persistQueryClientRestore({ queryClient, persister, + maxAge = 1000 * 60 * 60 * 24, // 24 hours buster = '', - dehydrateOptions = undefined, + hydrateOptions = undefined, }) ``` ### `persistQueryClient` -This will automatically restore any persisted cache and subscribes to the query cache to persist any changes from the query cache to the persister. It returns an `unsubscribe` function which you can use to discontinue the monitor; ending the updates to the persisted cache. +Takes the following actions: + +1. Immediately restores any persisted cache ([see `persistQueryClientRestore`](#persistqueryclientrestore)) +2. Subscribes to the query cache and returns the `unsubscribe` function ([see `persistQueryClientSubscribe`](#persistqueryclientsubscribe)). + +This functionality is preserved from version 3.x. ```ts persistQueryClient({ @@ -139,7 +128,7 @@ persistQueryClient({ ### `Options` -An object of options: +All options available are as follows: ```ts interface PersistQueryClientOptions { @@ -156,16 +145,25 @@ interface PersistQueryClientOptions { /** A unique string that can be used to forcefully * invalidate existing caches if they do not share the same buster string */ buster?: string - /** The options passed to the hydrate function */ + /** The options passed to the hydrate function + * Not used on `persistQueryClientSave` or `persistQueryClientSubscribe` */ hydrateOptions?: HydrateOptions - /** The options passed to the dehydrate function */ + /** The options passed to the dehydrate function + * Not used on `persistQueryClientRestore` */ dehydrateOptions?: DehydrateOptions } ``` -## Building a Persister +There are actually three interfaces available: +- `PersistedQueryClientSaveOptions` is used for `persistQueryClientSave` and `persistQueryClientSubscribe` (doesn't use `hydrateOptions`). +- `PersistedQueryClientRestoreOptions` is used for `persistQueryClientRestore` (doesn't use `dehydrateOptions`). +- `PersistQueryClientOptions` is used for `persistQueryClient` -Persisters have the following interface: +## Persisters + +### Persisters Interface + +Persisters have the following interfaces: ```ts export interface Persister { @@ -185,4 +183,33 @@ export interface PersistedClient { } ``` -Satisfy all of these interfaces and you've got yourself a persister! +You can import these (to build a persister): +```ts +import { PersistedClient, Persister } from "react-query/persistQueryClient"; +``` + +### Building A Persister +You can persist however you like. Here is an example of how to build an [Indexed DB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) persister. Compared to `Web Storage API`, Indexed DB is faster, stores more than 5MB, and doesn't require serialization. That means it can readily store Javascript native types, such as `Date` and `File`. + +```ts +import { get, set, del } from "idb-keyval"; +import { PersistedClient, Persister } from "react-query/persistQueryClient"; + +/** + * Creates an Indexed DB persister + * @see https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API + */ +export function createIDBPersister(idbValidKey: IDBValidKey = "reactQuery") { + return { + persistClient: async (client: PersistedClient) => { + set(idbValidKey, client); + }, + restoreClient: async () => { + return await get(idbValidKey); + }, + removeClient: async () => { + await del(idbValidKey); + }, + } as Persister; +} +``` From 49f29230fe2e0b05fa1b1c37275a2fafe6586de2 Mon Sep 17 00:00:00 2001 From: Mostafa Nawara Date: Thu, 3 Mar 2022 23:17:09 +0200 Subject: [PATCH 36/76] docs: add Offline React Query post (#3320) --- docs/src/pages/community/tkdodos-blog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/src/pages/community/tkdodos-blog.md b/docs/src/pages/community/tkdodos-blog.md index 044125d212..c214463e3d 100644 --- a/docs/src/pages/community/tkdodos-blog.md +++ b/docs/src/pages/community/tkdodos-blog.md @@ -53,3 +53,8 @@ React Query maintainer [TkDodo](https://twitter.com/tkdodo) has a series of blog ## [#12: Mastering Mutations in React Query](https://tkdodo.eu/blog/mastering-mutations-in-react-query) > Mutations are the important, second part necessary to work with server data - for situations where you need to update it. This blog post covers what mutations are and how they are different from queries. You'll learn the difference between `mutate` and `mutateAsync` as well as how you can tie queries and mutations together. [Read more...](https://tkdodo.eu/blog/mastering-mutations-in-react-query) + + +## [#13: Offline React Query](https://tkdodo.eu/blog/offline-react-query) + +> There are many ways to produce promises - which is everything React Query needs - but by far the biggest use-case is data fetching. Very often, that requires an active network connection. But sometimes, especially on mobile devices where, the network connection can be unreliable, you need your app to also work without it. In this article, you'll learn about the different offline strategies React Query offers. [Read more...](https://tkdodo.eu/blog/offline-react-query) From bc7263d43ad628dfe09e244676d625963ec350db Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sat, 5 Mar 2022 08:18:58 +0100 Subject: [PATCH 37/76] docs: multi-line code snippet for layout on mobile --- docs/src/pages/guides/migrating-to-react-query-4.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index 3d0d580b31..e106cba9c2 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -28,11 +28,15 @@ You can easily apply it by using one (or both) of the following commands: If you want to run it against `.js` or `.jsx` files, please use the command below: -`npx jscodeshift --extensions=js,jsx --transform=./node_modules/react-query/codemods/v4/key-transformation.js ./path/to/src/` +``` +npx jscodeshift --extensions=js,jsx --transform=./node_modules/react-query/codemods/v4/key-transformation.js ./path/to/src/ +``` If you want to run it against `.ts` or `.tsx` files, please use the command below: -`npx jscodeshift --extensions=ts,tsx --parser=tsx --transform=./node_modules/react-query/codemods/v4/key-transformation.js ./path/to/src/` +``` +npx jscodeshift --extensions=ts,tsx --parser=tsx --transform=./node_modules/react-query/codemods/v4/key-transformation.js ./path/to/src/ +``` Please note in the case of `TypeScript` you need to use `tsx` as the parser otherwise, the codemod won't be applied properly! From fee4a14f180ca4e26c80a735ff7c27b99c1d5174 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Sun, 13 Mar 2022 20:22:37 +0100 Subject: [PATCH 38/76] fix(mutations): allow passing a function to useErrorBoundary (#3390) --- src/reactjs/tests/useMutation.test.tsx | 51 ++++++++++++++++++++++++++ src/reactjs/useMutation.ts | 2 +- src/reactjs/utils.ts | 4 +- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/reactjs/tests/useMutation.test.tsx b/src/reactjs/tests/useMutation.test.tsx index 24b9f41c2c..ff5597ba6e 100644 --- a/src/reactjs/tests/useMutation.test.tsx +++ b/src/reactjs/tests/useMutation.test.tsx @@ -659,6 +659,57 @@ describe('useMutation', () => { }) }) + it('should be able to throw an error when useErrorBoundary is a function that returns true', async () => { + let boundary = false + function Page() { + const { mutate, error } = useMutation( + () => { + const err = new Error('mock error') + err.stack = '' + return Promise.reject(err) + }, + { + useErrorBoundary: () => { + boundary = !boundary + return !boundary + }, + } + ) + + return ( +
+ + {error && error.message} +
+ ) + } + + const { getByText, queryByText } = renderWithClient( + queryClient, + ( +
+ error boundary +
+ )} + > + +
+ ) + + // first error goes to component + fireEvent.click(getByText('mutate')) + await waitFor(() => { + expect(queryByText('mock error')).not.toBeNull() + }) + + // second error goes to boundary + fireEvent.click(getByText('mutate')) + await waitFor(() => { + expect(queryByText('error boundary')).not.toBeNull() + }) + }) + it('should pass meta to mutation', async () => { const errorMock = jest.fn() const successMock = jest.fn() diff --git a/src/reactjs/useMutation.ts b/src/reactjs/useMutation.ts index 0427befc69..63a13ef403 100644 --- a/src/reactjs/useMutation.ts +++ b/src/reactjs/useMutation.ts @@ -116,7 +116,7 @@ export function useMutation< if ( currentResult.error && - shouldThrowError(!!obsRef.current.options.useErrorBoundary, [ + shouldThrowError(obsRef.current.options.useErrorBoundary, [ currentResult.error, ]) ) { diff --git a/src/reactjs/utils.ts b/src/reactjs/utils.ts index 4b69f2a972..916982036d 100644 --- a/src/reactjs/utils.ts +++ b/src/reactjs/utils.ts @@ -1,5 +1,5 @@ export function shouldThrowError boolean>( - _useErrorBoundary: boolean | T, + _useErrorBoundary: boolean | T | undefined, params: Parameters ): boolean { // Allow useErrorBoundary function to override throwing behavior on a per-error basis @@ -7,5 +7,5 @@ export function shouldThrowError boolean>( return _useErrorBoundary(...params) } - return _useErrorBoundary + return !!_useErrorBoundary } From cba45035d408e61ad12f1472605f2211bf98c75f Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Wed, 23 Mar 2022 20:50:54 +0100 Subject: [PATCH 39/76] docs: idle state is removed --- .../pages/guides/migrating-to-react-query-4.md | 16 +++++++++++++++- docs/src/pages/reference/useQuery.md | 1 - 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index e106cba9c2..d2d6f27a8e 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -282,7 +282,7 @@ It was possible to change the logger globally by calling `setLogger`. In v4, tha + const queryClient = new QueryClient({ logger: customLogger }) ``` -### Undefined is an illegale cache value for successful queries +### Undefined is an illegal cache value for successful queries In order to make bailing out of updates possible by returning `undefined`, we had to make `undefined` an illegal cache value. This is in-line with other concepts of react-query, for example, returning `undefined` from the [initialData function](guides/initial-query-data#initial-data-function) will also _not_ set data. @@ -301,6 +301,20 @@ This is now disallowed on type level; at runtime, `undefined` will be transforme As of v4, React Query is optimized for modern browsers. We have updated our browserslist to produce a more modern, performant and smaller bundle. You can read about the requirements [here](../installation#requirements). +### The idle state has been removed + +With the introduction of the new [fetchStatus](../guides/queries#fetchstatus) for better offline support, the `idle` state became irrelevant, because `fetchStatus: 'idle'` captures the same state better. For more information, please read [Why two different states](../guides/queries#why-two-different-states). + +This will mostly affect `disabled` queries that don't have any `data` yet, as those were in `idle` state before: + +```diff +- status: 'idle' ++ status: 'loading' ++ fetchStatus: 'idle' +``` + +Also, have a look at [the guide on dependent queries](../guides/dependent-queries) + ## New Features 🚀 ### Proper offline support diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index 8040e1a159..cba9076944 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -15,7 +15,6 @@ const { isFetchedAfterMount, isFetching, isPaused, - isIdle, isLoading, isLoadingError, isPlaceholderData, From f31e1ed90441149bd27f26243929e817ce1c4672 Mon Sep 17 00:00:00 2001 From: Nick Galloway Date: Wed, 23 Mar 2022 13:03:06 -0700 Subject: [PATCH 40/76] feat: Support the ability to provide a context (#2548) * Support providing a context * Addressing comments and merge conflicts from rebase --- .vscode/settings.json | 8 -- docs/src/pages/devtools.md | 3 + .../guides/migrating-to-react-query-4.md | 87 ++++++++++++ .../pages/reference/QueryClientProvider.md | 5 +- docs/src/pages/reference/hydration.md | 6 + docs/src/pages/reference/useIsFetching.md | 2 + docs/src/pages/reference/useIsMutating.md | 2 + docs/src/pages/reference/useMutation.md | 2 + docs/src/pages/reference/useQueries.md | 5 +- docs/src/pages/reference/useQuery.md | 2 + src/core/hydration.ts | 3 +- src/core/utils.ts | 16 ++- src/devtools/devtools.tsx | 25 +++- src/devtools/tests/devtools.test.tsx | 132 +++++++++++++++++- src/devtools/tests/utils.tsx | 6 +- src/reactjs/Hydrate.tsx | 4 +- src/reactjs/QueryClientProvider.tsx | 41 ++++-- src/reactjs/index.ts | 6 +- src/reactjs/tests/Hydrate.test.tsx | 45 +++++- .../tests/QueryClientProvider.test.tsx | 65 +++++++++ src/reactjs/tests/useIsFetching.test.tsx | 83 ++++++++++- src/reactjs/tests/useIsMutating.test.tsx | 84 +++++++++++ src/reactjs/tests/useMutation.test.tsx | 67 ++++++++- src/reactjs/tests/useQueries.test.tsx | 83 +++++++++++ src/reactjs/tests/utils.tsx | 15 +- src/reactjs/types.ts | 41 +++--- src/reactjs/useBaseQuery.ts | 2 +- src/reactjs/useIsFetching.ts | 16 ++- src/reactjs/useIsMutating.ts | 18 ++- src/reactjs/useMutation.ts | 2 +- src/reactjs/useQueries.ts | 48 ++++--- 31 files changed, 833 insertions(+), 91 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 420d141d57..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "workbench.colorCustomizations": { - "titleBar.activeBackground": "#00da63", // change this color! - "titleBar.inactiveBackground": "#00da63", // change this color! - "titleBar.activeForeground": "#ffffff", // change this color! - "titleBar.inactiveForeground": "#ffffff" // change this color! - } -} diff --git a/docs/src/pages/devtools.md b/docs/src/pages/devtools.md index 4b4f2642a9..121b8f6cae 100644 --- a/docs/src/pages/devtools.md +++ b/docs/src/pages/devtools.md @@ -18,6 +18,7 @@ import { ReactQueryDevtools } from 'react-query/devtools' ``` By default, React Query Devtools are only included in bundles when `process.env.NODE_ENV === 'development'`, so you don't need to worry about excluding them during a production build. + ## Floating Mode Floating Mode will mount the devtools as a fixed, floating element in your app and provide a toggle in the corner of the screen to show and hide the devtools. This toggle state will be stored and remembered in localStorage across reloads. @@ -50,6 +51,8 @@ function App() { - `position?: "top-left" | "top-right" | "bottom-left" | "bottom-right"` - Defaults to `bottom-left` - The position of the React Query logo to open and close the devtools panel +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. ## Embedded Mode diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md index d2d6f27a8e..049fd91b27 100644 --- a/docs/src/pages/guides/migrating-to-react-query-4.md +++ b/docs/src/pages/guides/migrating-to-react-query-4.md @@ -346,3 +346,90 @@ When using the [functional updater form of setQueryData](../reference/QueryClien (previousTodo) => previousTodo ? { ...previousTodo, done: true } : undefined ) ``` + + ### Custom Contexts for Multiple Providers + +Custom contexts can now be specified to pair hooks with their matching `Provider`. This is critical when there may be multiple React Query `Provider` instances in the component tree and you need to ensure your hook uses the correct `Provider` instance. + +An example: + +1) Create a data package. + +```tsx +// Our first data package: @my-scope/container-data + +const context = React.createContext(); +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache, context }) + + +export const useUser = () => { + return useQuery(USER_KEY, USER_FETCHER, { + context, + }) +} + +export const ContainerDataProvider: React.FC = ({ children }) => { + return ( + + {children} + + ); +} + +``` + +2) Create a second data package. + +```tsx +// Our second data package: @my-scope/my-component-data + +const context = React.createContext(); +const queryCache = new QueryCache() +const queryClient = new QueryClient({ queryCache, context }) + + +export const useItems = () => { + return useQuery(ITEMS_KEY, ITEMS_FETCHER, { + context, + }) +} + +export const MyComponentDataProvider: React.FC = ({ children }) => { + return ( + + {children} + + ); +} + +``` + +3) Use these two data packages in your application. + +```tsx +// Our application + +import { ContainerDataProvider, useUser } from "@my-scope/container-data"; +import { AppDataProvider } from "@my-scope/app-data"; +import { MyComponentDataProvider, useItems } from "@my-scope/my-component-data"; + + // <-- Provides container data (like "user") using its own React Query provider + ... + // <-- Provides app data using its own React Query provider (unused in this example) + ... + // <-- Provides component data (like "items") using its own React Query provider + + + ... + + ... + + +// Example of hooks provided by the "DataProvider" components above: +const MyComponent = () => { + const user = useUser(); // <-- Uses the context specified in ContainerDataProvider. + const items = useItems(); // <-- Uses the context specified in MyComponentDataProvider + ... +} +``` diff --git a/docs/src/pages/reference/QueryClientProvider.md b/docs/src/pages/reference/QueryClientProvider.md index e31338de68..f9ea75ba68 100644 --- a/docs/src/pages/reference/QueryClientProvider.md +++ b/docs/src/pages/reference/QueryClientProvider.md @@ -14,6 +14,7 @@ function App() { return ... } ``` + **Options** - `client: QueryClient` @@ -21,4 +22,6 @@ function App() { - the QueryClient instance to provide - `contextSharing: boolean` - defaults to `false` - - Set this to `true` to enable context sharing, which will share the first and at least one instance of the context across the window to ensure that if React Query is used across different bundles or microfrontends they will all use the same **instance** of context, regardless of module scoping. + - Set this to `true` to enable context sharing, which will share the first and at least one instance of the context across the window to ensure that if React Query is used across different bundles or microfrontends they will all use the same **instance** of context, regardless of module scoping. +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. diff --git a/docs/src/pages/reference/hydration.md b/docs/src/pages/reference/hydration.md index a815432756..ce3899c93c 100644 --- a/docs/src/pages/reference/hydration.md +++ b/docs/src/pages/reference/hydration.md @@ -84,6 +84,8 @@ hydrate(queryClient, dehydratedState, options) - Optional - `mutations: MutationOptions` The default mutation options to use for the hydrated mutations. - `queries: QueryOptions` The default query options to use for the hydrated queries. + - `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. ### Limitations @@ -108,6 +110,8 @@ useHydrate(dehydratedState, options) - Optional - `defaultOptions: QueryOptions` - The default query options to use for the hydrated queries. + - `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. ## `Hydrate` @@ -129,3 +133,5 @@ function App() { - Optional - `defaultOptions: QueryOptions` - The default query options to use for the hydrated queries. + - `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. diff --git a/docs/src/pages/reference/useIsFetching.md b/docs/src/pages/reference/useIsFetching.md index 569c133443..fa0c793bd8 100644 --- a/docs/src/pages/reference/useIsFetching.md +++ b/docs/src/pages/reference/useIsFetching.md @@ -17,6 +17,8 @@ const isFetchingPosts = useIsFetching(['posts']) - `queryKey?: QueryKey`: [Query Keys](../guides/query-keys) - `filters?: QueryFilters`: [Query Filters](../guides/filters#query-filters) +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. **Returns** diff --git a/docs/src/pages/reference/useIsMutating.md b/docs/src/pages/reference/useIsMutating.md index d9a6ad4cc5..d75876f839 100644 --- a/docs/src/pages/reference/useIsMutating.md +++ b/docs/src/pages/reference/useIsMutating.md @@ -17,6 +17,8 @@ const isMutatingPosts = useIsMutating(['posts']) - `mutationKey?: string | unknown[]` - `filters?: MutationFilters`: [Mutation Filters](../guides/filters#mutation-filters) +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. **Returns** diff --git a/docs/src/pages/reference/useMutation.md b/docs/src/pages/reference/useMutation.md index cb3c611d7a..70793e17a2 100644 --- a/docs/src/pages/reference/useMutation.md +++ b/docs/src/pages/reference/useMutation.md @@ -87,6 +87,8 @@ mutate(variables, { - `meta: Record` - Optional - If set, stores additional information on the mutation cache entry that can be used as needed. It will be accessible wherever the `mutation` is available (eg. `onError`, `onSuccess` functions of the `MutationCache`). +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. **Returns** diff --git a/docs/src/pages/reference/useQueries.md b/docs/src/pages/reference/useQueries.md index 0849033ef2..bda41f3fa0 100644 --- a/docs/src/pages/reference/useQueries.md +++ b/docs/src/pages/reference/useQueries.md @@ -16,7 +16,10 @@ const results = useQueries({ **Options** -The `useQueries` hook accepts an options object with a **queries** key whose value is an array with query option objects identical to the [`useQuery` hook](/reference/useQuery). +The `useQueries` hook accepts an options object with a **queries** key whose value is an array with query option objects identical to the [`useQuery` hook](/reference/useQuery) (excluding the `context` option). + +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. **Returns** diff --git a/docs/src/pages/reference/useQuery.md b/docs/src/pages/reference/useQuery.md index cba9076944..4045b3e0c0 100644 --- a/docs/src/pages/reference/useQuery.md +++ b/docs/src/pages/reference/useQuery.md @@ -184,6 +184,8 @@ const result = useQuery({ - `meta: Record` - Optional - If set, stores additional information on the query cache entry that can be used as needed. It will be accessible wherever the `query` is available, and is also part of the `QueryFunctionContext` provided to the `queryFn`. +- `context?: React.Context` + - Use this to use a custom React Query context. Otherwise, `defaultContext` will be used. **Returns** diff --git a/src/core/hydration.ts b/src/core/hydration.ts index c45dd86ab2..830d64bb2f 100644 --- a/src/core/hydration.ts +++ b/src/core/hydration.ts @@ -1,3 +1,4 @@ +import type { ContextOptions } from '../reactjs/types' import type { QueryClient } from './queryClient' import type { Query, QueryState } from './query' import type { @@ -17,7 +18,7 @@ export interface DehydrateOptions { shouldDehydrateQuery?: ShouldDehydrateQueryFunction } -export interface HydrateOptions { +export interface HydrateOptions extends ContextOptions { defaultOptions?: { queries?: QueryOptions mutations?: MutationOptions diff --git a/src/core/utils.ts b/src/core/utils.ts index 0c4c9b0fcf..14773db39b 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -154,11 +154,17 @@ export function parseFilterArgs< : [arg1 || {}, arg2]) as [TFilters, TOptions] } -export function parseMutationFilterArgs( - arg1?: QueryKey | MutationFilters, - arg2?: MutationFilters -): MutationFilters | undefined { - return isQueryKey(arg1) ? { ...arg2, mutationKey: arg1 } : arg1 +export function parseMutationFilterArgs< + TFilters extends MutationFilters, + TOptions = unknown +>( + arg1?: QueryKey | TFilters, + arg2?: TFilters | TOptions, + arg3?: TOptions +): [TFilters, TOptions | undefined] { + return (isQueryKey(arg1) + ? [{ ...arg2, mutationKey: arg1 }, arg3] + : [arg1 || {}, arg2]) as [TFilters, TOptions] } export function matchQuery( diff --git a/src/devtools/devtools.tsx b/src/devtools/devtools.tsx index b102a46ca3..fc632fae90 100644 --- a/src/devtools/devtools.tsx +++ b/src/devtools/devtools.tsx @@ -1,6 +1,11 @@ import React from 'react' -import { Query, useQueryClient, onlineManager } from 'react-query' +import { + Query, + ContextOptions, + useQueryClient, + onlineManager, +} from 'react-query' import { matchSorter } from 'match-sorter' import useLocalStorage from './useLocalStorage' import { useIsMounted, useSafeState } from './utils' @@ -21,7 +26,7 @@ import Explorer from './Explorer' import Logo from './Logo' import { noop } from '../core/utils' -interface DevtoolsOptions { +interface DevtoolsOptions extends ContextOptions { /** * Set this true if you want the dev tools to default to being open */ @@ -60,7 +65,7 @@ interface DevtoolsOptions { containerElement?: string | any } -interface DevtoolsPanelOptions { +interface DevtoolsPanelOptions extends ContextOptions { /** * The standard React style object used to style a component with inline styles */ @@ -92,6 +97,7 @@ export function ReactQueryDevtools({ toggleButtonProps = {}, position = 'bottom-left', containerElement: Container = 'aside', + context, }: DevtoolsOptions): React.ReactElement | null { const rootRef = React.useRef(null) const panelRef = React.useRef(null) @@ -229,6 +235,7 @@ export function ReactQueryDevtools({ (function ReactQueryDevtoolsPanel(props, ref): React.ReactElement { - const { isOpen = true, setIsOpen, handleDragStart, ...panelProps } = props - - const queryClient = useQueryClient() + const { + isOpen = true, + setIsOpen, + handleDragStart, + context, + ...panelProps + } = props + + const queryClient = useQueryClient({ context }) const queryCache = queryClient.getQueryCache() const [sort, setSort] = useLocalStorage( diff --git a/src/devtools/tests/devtools.test.tsx b/src/devtools/tests/devtools.test.tsx index 3ffe6e16df..30f2ac5263 100644 --- a/src/devtools/tests/devtools.test.tsx +++ b/src/devtools/tests/devtools.test.tsx @@ -1,4 +1,5 @@ import React from 'react' +import { ErrorBoundary } from 'react-error-boundary' import { fireEvent, screen, @@ -7,7 +8,7 @@ import { waitForElementToBeRemoved, } from '@testing-library/react' import '@testing-library/jest-dom' -import { useQuery } from '../..' +import { useQuery, QueryClient } from '../..' import { getByTextContent, renderWithClient, @@ -77,6 +78,39 @@ describe('ReactQueryDevtools', () => { expect(onCloseClick).toHaveBeenCalledTimes(1) }) + it('should be able to drag devtools without error', async () => { + const { queryClient } = createQueryClient() + + function Page() { + const { data = 'default' } = useQuery(['check'], async () => { + await sleep(10) + return 'test' + }) + + return ( +
+

{data}

+
+ ) + } + + const result = renderWithClient(queryClient, , { + initialIsOpen: false, + }) + + const draggableElement = result.container + .querySelector('#ReactQueryDevtoolsPanel') + ?.querySelector('div') + + if (!draggableElement) { + throw new Error('Could not find the draggable element') + } + + await act(async () => { + fireEvent.mouseDown(draggableElement) + }) + }) + it('should display the correct query states', async () => { const { queryClient, queryCache } = createQueryClient() @@ -470,4 +504,100 @@ describe('ReactQueryDevtools', () => { expect(queries[1]?.textContent).toEqual(query2Hash) expect(queries[2]?.textContent).toEqual(query3Hash) }) + + describe('with custom context', () => { + it('should render without error when the custom context aligns', async () => { + const context = React.createContext(undefined) + const { queryClient } = createQueryClient() + + function Page() { + const { data = 'default' } = useQuery(['check'], async () => 'test', { + context, + }) + + return ( +
+

{data}

+
+ ) + } + + renderWithClient(queryClient, , { + initialIsOpen: false, + context, + }) + + await screen.findByRole('button', { name: /open react query devtools/i }) + }) + + it('should render with error when the custom context is not passed to useQuery', async () => { + const consoleErrorMock = jest.spyOn(console, 'error') + consoleErrorMock.mockImplementation(() => undefined) + + const context = React.createContext(undefined) + const { queryClient } = createQueryClient() + + function Page() { + const { data = 'default' } = useQuery(['check'], async () => 'test', { + useErrorBoundary: true, + }) + + return ( +
+

{data}

+
+ ) + } + + const rendered = renderWithClient( + queryClient, +
error boundary
}> + +
, + { + initialIsOpen: false, + context, + } + ) + + await waitFor(() => rendered.getByText('error boundary')) + + consoleErrorMock.mockRestore() + }) + + it('should render with error when the custom context is not passed to ReactQueryDevtools', async () => { + const consoleErrorMock = jest.spyOn(console, 'error') + consoleErrorMock.mockImplementation(() => undefined) + + const context = React.createContext(undefined) + const { queryClient } = createQueryClient() + + function Page() { + const { data = 'default' } = useQuery(['check'], async () => 'test', { + useErrorBoundary: true, + context, + }) + + return ( +
+

{data}

+
+ ) + } + + const rendered = renderWithClient( + queryClient, +
error boundary
}> + +
, + { + initialIsOpen: false, + } + ) + + await waitFor(() => rendered.getByText('error boundary')) + + consoleErrorMock.mockRestore() + }) + }) }) diff --git a/src/devtools/tests/utils.tsx b/src/devtools/tests/utils.tsx index d0d2afebd6..0c0a36b166 100644 --- a/src/devtools/tests/utils.tsx +++ b/src/devtools/tests/utils.tsx @@ -7,10 +7,10 @@ import { QueryClient, QueryClientProvider, QueryCache } from '../..' export function renderWithClient( client: QueryClient, ui: React.ReactElement, - devtoolsOptions?: Parameters[number] + devtoolsOptions: Parameters[number] = {} ) { const { rerender, ...result } = render( - + {ui} @@ -19,7 +19,7 @@ export function renderWithClient( ...result, rerender: (rerenderUi: React.ReactElement) => rerender( - + {rerenderUi} diff --git a/src/reactjs/Hydrate.tsx b/src/reactjs/Hydrate.tsx index 5221897403..d5b172e514 100644 --- a/src/reactjs/Hydrate.tsx +++ b/src/reactjs/Hydrate.tsx @@ -3,8 +3,8 @@ import React from 'react' import { hydrate, HydrateOptions } from '../core' import { useQueryClient } from './QueryClientProvider' -export function useHydrate(state: unknown, options?: HydrateOptions) { - const queryClient = useQueryClient() +export function useHydrate(state: unknown, options: HydrateOptions = {}) { + const queryClient = useQueryClient({ context: options.context }) const optionsRef = React.useRef(options) optionsRef.current = options diff --git a/src/reactjs/QueryClientProvider.tsx b/src/reactjs/QueryClientProvider.tsx index e0ea8baa52..f5d52c178e 100644 --- a/src/reactjs/QueryClientProvider.tsx +++ b/src/reactjs/QueryClientProvider.tsx @@ -1,6 +1,7 @@ import React from 'react' import { QueryClient } from '../core' +import { ContextOptions } from '../reactjs/types' declare global { interface Window { @@ -8,16 +9,25 @@ declare global { } } -const defaultContext = React.createContext(undefined) +export const defaultContext = React.createContext( + undefined +) const QueryClientSharingContext = React.createContext(false) -// if contextSharing is on, we share the first and at least one +// If we are given a context, we will use it. +// Otherwise, if contextSharing is on, we share the first and at least one // instance of the context across the window // to ensure that if React Query is used across // different bundles or microfrontends they will // all use the same **instance** of context, regardless // of module scoping. -function getQueryClientContext(contextSharing: boolean) { +function getQueryClientContext( + context: React.Context | undefined, + contextSharing: boolean +) { + if (context) { + return context + } if (contextSharing && typeof window !== 'undefined') { if (!window.ReactQueryClientContext) { window.ReactQueryClientContext = defaultContext @@ -29,9 +39,9 @@ function getQueryClientContext(contextSharing: boolean) { return defaultContext } -export const useQueryClient = () => { +export const useQueryClient = ({ context }: ContextOptions = {}) => { const queryClient = React.useContext( - getQueryClientContext(React.useContext(QueryClientSharingContext)) + getQueryClientContext(context, React.useContext(QueryClientSharingContext)) ) if (!queryClient) { @@ -41,15 +51,26 @@ export const useQueryClient = () => { return queryClient } -export interface QueryClientProviderProps { +type QueryClientProviderPropsBase = { client: QueryClient - contextSharing?: boolean } +type QueryClientProviderPropsWithContext = ContextOptions & { + contextSharing?: never +} & QueryClientProviderPropsBase +type QueryClientProviderPropsWithContextSharing = { + context?: never + contextSharing?: boolean +} & QueryClientProviderPropsBase + +export type QueryClientProviderProps = + | QueryClientProviderPropsWithContext + | QueryClientProviderPropsWithContextSharing export const QueryClientProvider: React.FC = ({ client, - contextSharing = false, children, + context, + contextSharing = false, }) => { React.useEffect(() => { client.mount() @@ -58,10 +79,10 @@ export const QueryClientProvider: React.FC = ({ } }, [client]) - const Context = getQueryClientContext(contextSharing) + const Context = getQueryClientContext(context, contextSharing) return ( - + {children} ) diff --git a/src/reactjs/index.ts b/src/reactjs/index.ts index b428b5fe8c..1a678571a0 100644 --- a/src/reactjs/index.ts +++ b/src/reactjs/index.ts @@ -1,7 +1,11 @@ // Side effects import './setBatchUpdatesFn' -export { QueryClientProvider, useQueryClient } from './QueryClientProvider' +export { + defaultContext, + QueryClientProvider, + useQueryClient, +} from './QueryClientProvider' export { QueryErrorResetBoundary, useQueryErrorResetBoundary, diff --git a/src/reactjs/tests/Hydrate.test.tsx b/src/reactjs/tests/Hydrate.test.tsx index c3a8278482..1bfd2d0c97 100644 --- a/src/reactjs/tests/Hydrate.test.tsx +++ b/src/reactjs/tests/Hydrate.test.tsx @@ -2,6 +2,7 @@ import React from 'react' import { render } from '@testing-library/react' import { + QueryClient, QueryClientProvider, QueryCache, useQuery, @@ -21,7 +22,9 @@ describe('React hydration', () => { beforeAll(async () => { const queryCache = new QueryCache() const queryClient = createQueryClient({ queryCache }) - await queryClient.prefetchQuery(['string'], () => dataQuery(['string'])) + await queryClient.prefetchQuery(['string'], () => + dataQuery(['stringCached']) + ) const dehydrated = dehydrate(queryClient) stringifiedState = JSON.stringify(dehydrated) queryClient.clear() @@ -49,10 +52,50 @@ describe('React hydration', () => { ) + rendered.getByText('stringCached') await sleep(10) rendered.getByText('string') queryClient.clear() }) + + test('should hydrate queries to the cache on custom context', async () => { + const context = React.createContext(undefined) + + const queryCacheOuter = new QueryCache() + const queryCacheInner = new QueryCache() + + const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) + const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) + + const dehydratedState = JSON.parse(stringifiedState) + + function Page() { + useHydrate(dehydratedState, { context }) + const { data } = useQuery(['string'], () => dataQuery(['string']), { + context, + }) + return ( +
+

{data}

+
+ ) + } + + const rendered = render( + + + + + + ) + + rendered.getByText('stringCached') + await sleep(10) + rendered.getByText('string') + + queryClientInner.clear() + queryClientOuter.clear() + }) }) describe('ReactQueryCacheProvider with hydration support', () => { diff --git a/src/reactjs/tests/QueryClientProvider.test.tsx b/src/reactjs/tests/QueryClientProvider.test.tsx index e451cd97c3..e9acb2dc7c 100644 --- a/src/reactjs/tests/QueryClientProvider.test.tsx +++ b/src/reactjs/tests/QueryClientProvider.test.tsx @@ -135,6 +135,71 @@ describe('QueryClientProvider', () => { expect(queryCache.find(key)?.options.cacheTime).toBe(Infinity) }) + describe('with custom context', () => { + it('uses the correct context', async () => { + const key = queryKey() + + const contextOuter = React.createContext( + undefined + ) + const contextInner = React.createContext( + undefined + ) + + const queryCacheOuter = new QueryCache() + const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) + + const queryCacheInner = new QueryCache() + const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) + + const queryCacheInnerInner = new QueryCache() + const queryClientInnerInner = new QueryClient({ + queryCache: queryCacheInnerInner, + }) + + function Page() { + const { data: testOuter } = useQuery(key, async () => 'testOuter', { + context: contextOuter, + }) + const { data: testInner } = useQuery(key, async () => 'testInner', { + context: contextInner, + }) + const { data: testInnerInner } = useQuery( + key, + async () => 'testInnerInner' + ) + + return ( +
+

+ {testOuter} {testInner} {testInnerInner} +

+
+ ) + } + + // contextSharing should be ignored when passing a custom context. + const contextSharing = true + + const rendered = render( + + + + + + + + ) + + await waitFor(() => + rendered.getByText('testOuter testInner testInnerInner') + ) + }) + }) + describe('useQueryClient', () => { test('should throw an error if no query client has been set', () => { const consoleMock = jest diff --git a/src/reactjs/tests/useIsFetching.test.tsx b/src/reactjs/tests/useIsFetching.test.tsx index 9c91ca25bd..cb16bade80 100644 --- a/src/reactjs/tests/useIsFetching.test.tsx +++ b/src/reactjs/tests/useIsFetching.test.tsx @@ -1,5 +1,6 @@ import { fireEvent, waitFor } from '@testing-library/react' import React from 'react' +import { ErrorBoundary } from 'react-error-boundary' import { createQueryClient, @@ -9,7 +10,7 @@ import { setActTimeout, sleep, } from './utils' -import { useQuery, useIsFetching, QueryCache } from '../..' +import { QueryClient, useQuery, useIsFetching, QueryCache } from '../..' describe('useIsFetching', () => { // See https://github.com/tannerlinsley/react-query/issues/105 @@ -156,4 +157,84 @@ describe('useIsFetching', () => { await sleep(100) expect(isFetchings).toEqual([0, 0, 1, 0]) }) + + describe('with custom context', () => { + it('should update as queries start and stop fetching', async () => { + const context = React.createContext(undefined) + + const queryCache = new QueryCache() + const queryClient = new QueryClient({ queryCache }) + const key = queryKey() + + function Page() { + const [ready, setReady] = React.useState(false) + + const isFetching = useIsFetching(undefined, { context: context }) + + useQuery( + key, + async () => { + await sleep(1000) + return 'test' + }, + { + enabled: ready, + context, + } + ) + + return ( +
+
isFetching: {isFetching}
+ +
+ ) + } + + const rendered = renderWithClient(queryClient, , { + context, + }) + + await waitFor(() => rendered.getByText('isFetching: 0')) + fireEvent.click(rendered.getByText('setReady')) + await waitFor(() => rendered.getByText('isFetching: 1')) + await waitFor(() => rendered.getByText('isFetching: 0')) + }) + + it('should throw if the context is not passed to useIsFetching', async () => { + const context = React.createContext(undefined) + + const queryCache = new QueryCache() + const queryClient = new QueryClient({ queryCache }) + const key = queryKey() + + function Page() { + const isFetching = useIsFetching() + + useQuery(key, async () => 'test', { + enabled: true, + context, + useErrorBoundary: true, + }) + + return ( +
+
isFetching: {isFetching}
+
+ ) + } + + const rendered = renderWithClient( + queryClient, +
error boundary
}> + +
, + { + context, + } + ) + + await waitFor(() => rendered.getByText('error boundary')) + }) + }) }) diff --git a/src/reactjs/tests/useIsMutating.test.tsx b/src/reactjs/tests/useIsMutating.test.tsx index 19212513d7..cf08f96c00 100644 --- a/src/reactjs/tests/useIsMutating.test.tsx +++ b/src/reactjs/tests/useIsMutating.test.tsx @@ -8,6 +8,8 @@ import { setActTimeout, sleep, } from './utils' +import { ErrorBoundary } from 'react-error-boundary' +import { QueryClient } from '../../core' import * as MutationCacheModule from '../../core/mutationCache' describe('useIsMutating', () => { @@ -163,4 +165,86 @@ describe('useIsMutating', () => { await sleep(20) MutationCacheSpy.mockRestore() }) + + describe('with custom context', () => { + it('should return the number of fetching mutations', async () => { + const context = React.createContext(undefined) + + const isMutatings: number[] = [] + const queryClient = new QueryClient() + + function IsMutating() { + const isMutating = useIsMutating(undefined, { context }) + isMutatings.push(isMutating) + return null + } + + function Page() { + const { mutate: mutate1 } = useMutation( + ['mutation1'], + async () => { + await sleep(150) + return 'data' + }, + { context } + ) + const { mutate: mutate2 } = useMutation( + ['mutation2'], + async () => { + await sleep(50) + return 'data' + }, + { context } + ) + + React.useEffect(() => { + mutate1() + setActTimeout(() => { + mutate2() + }, 50) + }, [mutate1, mutate2]) + + return + } + + renderWithClient(queryClient, , { context }) + await waitFor(() => expect(isMutatings).toEqual([0, 1, 1, 2, 2, 1, 0])) + }) + + it('should throw if the context is not passed to useIsMutating', async () => { + const context = React.createContext(undefined) + + const isMutatings: number[] = [] + const queryClient = new QueryClient() + + function IsMutating() { + const isMutating = useIsMutating(undefined) + isMutatings.push(isMutating) + return null + } + + function Page() { + const { mutate } = useMutation(['mutation'], async () => 'data', { + useErrorBoundary: true, + context, + }) + + React.useEffect(() => { + mutate() + }, [mutate]) + + return + } + + const rendered = renderWithClient( + queryClient, +
error boundary
}> + +
, + { context } + ) + + await waitFor(() => rendered.getByText('error boundary')) + }) + }) }) diff --git a/src/reactjs/tests/useMutation.test.tsx b/src/reactjs/tests/useMutation.test.tsx index ff5597ba6e..5f973b5e88 100644 --- a/src/reactjs/tests/useMutation.test.tsx +++ b/src/reactjs/tests/useMutation.test.tsx @@ -3,7 +3,7 @@ import '@testing-library/jest-dom' import React from 'react' import { ErrorBoundary } from 'react-error-boundary' -import { useMutation, QueryCache, MutationCache } from '../..' +import { QueryClient, useMutation, QueryCache, MutationCache } from '../..' import { UseMutationResult } from '../types' import { createQueryClient, @@ -844,6 +844,71 @@ describe('useMutation', () => { expect(onSettledMutate).toHaveBeenCalledTimes(0) }) + describe('with custom context', () => { + it('should be able to reset `data`', async () => { + const context = React.createContext(undefined) + + function Page() { + const { mutate, data = '', reset } = useMutation( + () => Promise.resolve('mutation'), + { context } + ) + + return ( +
+

{data}

+ + +
+ ) + } + + const { getByTestId, getByText } = renderWithClient( + queryClient, + , + { context } + ) + + expect(getByTestId('title').textContent).toBe('') + + fireEvent.click(getByText('mutate')) + + await waitFor(() => getByTestId('title')) + + expect(getByTestId('title').textContent).toBe('mutation') + + fireEvent.click(getByText('reset')) + + await waitFor(() => getByTestId('title')) + + expect(getByTestId('title').textContent).toBe('') + }) + + it('should throw if the context is not passed to useMutation', async () => { + const context = React.createContext(undefined) + + function Page() { + const { data = '' } = useMutation(() => Promise.resolve('mutation')) + + return ( +
+

{data}

+
+ ) + } + + const rendered = renderWithClient( + queryClient, +
error boundary
}> + +
, + { context } + ) + + await waitFor(() => rendered.getByText('error boundary')) + }) + }) + it('should call mutate callbacks only for the last observer', async () => { const onSuccess = jest.fn() const onSuccessMutate = jest.fn() diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx index 3ac5e19556..c132a75d2f 100644 --- a/src/reactjs/tests/useQueries.test.tsx +++ b/src/reactjs/tests/useQueries.test.tsx @@ -1,5 +1,6 @@ import { waitFor, fireEvent } from '@testing-library/react' import React from 'react' +import { ErrorBoundary } from 'react-error-boundary' import * as QueriesObserverModule from '../../core/queriesObserver' @@ -13,6 +14,7 @@ import { sleep, } from './utils' import { + QueryClient, useQueries, UseQueryResult, QueryCache, @@ -1056,4 +1058,85 @@ describe('useQueries', () => { await sleep(20) QueriesObserverSpy.mockRestore() }) + + describe('with custom context', () => { + it('should return the correct states', async () => { + const context = React.createContext(undefined) + + const key1 = queryKey() + const key2 = queryKey() + const results: UseQueryResult[][] = [] + + function Page() { + const result = useQueries({ + context, + queries: [ + { + queryKey: key1, + queryFn: async () => { + await sleep(5) + return 1 + }, + }, + { + queryKey: key2, + queryFn: async () => { + await sleep(10) + return 2 + }, + }, + ], + }) + results.push(result) + return null + } + + renderWithClient(queryClient, , { context }) + + await sleep(30) + + expect(results.length).toBe(3) + expect(results[0]).toMatchObject([ + { data: undefined }, + { data: undefined }, + ]) + expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }]) + expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }]) + }) + + it('should throw if the context is necessary and is not passed to useQueries', async () => { + const context = React.createContext(undefined) + + const key1 = queryKey() + const key2 = queryKey() + const results: UseQueryResult[][] = [] + + function Page() { + const result = useQueries({ + queries: [ + { + queryKey: key1, + queryFn: async () => 1, + }, + { + queryKey: key2, + queryFn: async () => 2, + }, + ], + }) + results.push(result) + return null + } + + const rendered = renderWithClient( + queryClient, +
error boundary
}> + +
, + { context } + ) + + await waitFor(() => rendered.getByText('error boundary')) + }) + }) }) diff --git a/src/reactjs/tests/utils.tsx b/src/reactjs/tests/utils.tsx index e6e0bd77fd..95d94238ad 100644 --- a/src/reactjs/tests/utils.tsx +++ b/src/reactjs/tests/utils.tsx @@ -6,6 +6,7 @@ import { QueryClient, QueryClientConfig, QueryClientProvider, + ContextOptions, } from '../..' import * as utils from '../../core/utils' @@ -14,15 +15,23 @@ export function createQueryClient(config?: QueryClientConfig): QueryClient { return new QueryClient({ logger: mockLogger, ...config }) } -export function renderWithClient(client: QueryClient, ui: React.ReactElement) { +export function renderWithClient( + client: QueryClient, + ui: React.ReactElement, + options: ContextOptions = {} +) { const { rerender, ...result } = render( - {ui} + + {ui} + ) return { ...result, rerender: (rerenderUi: React.ReactElement) => rerender( - {rerenderUi} + + {rerenderUi} + ), } } diff --git a/src/reactjs/types.ts b/src/reactjs/types.ts index 13ac8c4510..5bf97177be 100644 --- a/src/reactjs/types.ts +++ b/src/reactjs/types.ts @@ -8,6 +8,14 @@ import { MutationObserverOptions, MutateFunction, } from '../core/types' +import type { QueryClient } from '../core/queryClient' + +export interface ContextOptions { + /** + * Use this to pass your React Query context. Otherwise, `defaultContext` will be used. + */ + context?: React.Context +} export interface UseBaseQueryOptions< TQueryFnData = unknown, @@ -15,13 +23,8 @@ export interface UseBaseQueryOptions< TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey -> extends QueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > {} +> extends ContextOptions, + QueryObserverOptions {} export interface UseQueryOptions< TQueryFnData = unknown, @@ -42,13 +45,14 @@ export interface UseInfiniteQueryOptions< TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey -> extends InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > {} +> extends ContextOptions, + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + > {} export type UseBaseQueryResult< TData = unknown, @@ -70,10 +74,11 @@ export interface UseMutationOptions< TError = unknown, TVariables = void, TContext = unknown -> extends Omit< - MutationObserverOptions, - '_defaulted' | 'variables' - > {} +> extends ContextOptions, + Omit< + MutationObserverOptions, + '_defaulted' | 'variables' + > {} export type UseMutateFunction< TData = unknown, diff --git a/src/reactjs/useBaseQuery.ts b/src/reactjs/useBaseQuery.ts index 9a11ce89af..3378661cd1 100644 --- a/src/reactjs/useBaseQuery.ts +++ b/src/reactjs/useBaseQuery.ts @@ -27,7 +27,7 @@ export function useBaseQuery< const mountedRef = React.useRef(false) const [, forceUpdate] = React.useState(0) - const queryClient = useQueryClient() + const queryClient = useQueryClient({ context: options.context }) const errorResetBoundary = useQueryErrorResetBoundary() const defaultedOptions = queryClient.defaultQueryOptions(options) diff --git a/src/reactjs/useIsFetching.ts b/src/reactjs/useIsFetching.ts index a1319c53dd..8552881e12 100644 --- a/src/reactjs/useIsFetching.ts +++ b/src/reactjs/useIsFetching.ts @@ -2,23 +2,29 @@ import React from 'react' import { notifyManager } from '../core/notifyManager' import { QueryKey } from '../core/types' +import { ContextOptions } from '../reactjs/types' import { parseFilterArgs, QueryFilters } from '../core/utils' import { useQueryClient } from './QueryClientProvider' -export function useIsFetching(filters?: QueryFilters): number +interface Options extends ContextOptions {} + +export function useIsFetching(filters?: QueryFilters, options?: Options): number export function useIsFetching( queryKey?: QueryKey, - filters?: QueryFilters + filters?: QueryFilters, + options?: Options ): number export function useIsFetching( arg1?: QueryKey | QueryFilters, - arg2?: QueryFilters + arg2?: QueryFilters | Options, + arg3?: Options ): number { const mountedRef = React.useRef(false) - const queryClient = useQueryClient() + const [filters, options = {}] = parseFilterArgs(arg1, arg2, arg3) + + const queryClient = useQueryClient({ context: options.context }) - const [filters] = parseFilterArgs(arg1, arg2) const [isFetching, setIsFetching] = React.useState( queryClient.isFetching(filters) ) diff --git a/src/reactjs/useIsMutating.ts b/src/reactjs/useIsMutating.ts index b3baaec00f..013525ba2c 100644 --- a/src/reactjs/useIsMutating.ts +++ b/src/reactjs/useIsMutating.ts @@ -2,22 +2,30 @@ import React from 'react' import { notifyManager } from '../core/notifyManager' import { MutationKey } from '../core/types' +import { ContextOptions } from '../reactjs/types' import { MutationFilters, parseMutationFilterArgs } from '../core/utils' import { useQueryClient } from './QueryClientProvider' -export function useIsMutating(filters?: MutationFilters): number +interface Options extends ContextOptions {} + +export function useIsMutating( + filters?: MutationFilters, + options?: Options +): number export function useIsMutating( mutationKey?: MutationKey, - filters?: Omit + filters?: Omit, + options?: Options ): number export function useIsMutating( arg1?: MutationKey | MutationFilters, - arg2?: Omit + arg2?: Omit | Options, + arg3?: Options ): number { const mountedRef = React.useRef(false) - const filters = parseMutationFilterArgs(arg1, arg2) + const [filters, options = {}] = parseMutationFilterArgs(arg1, arg2, arg3) - const queryClient = useQueryClient() + const queryClient = useQueryClient({ context: options.context }) const [isMutating, setIsMutating] = React.useState( queryClient.isMutating(filters) diff --git a/src/reactjs/useMutation.ts b/src/reactjs/useMutation.ts index 63a13ef403..71c0744d40 100644 --- a/src/reactjs/useMutation.ts +++ b/src/reactjs/useMutation.ts @@ -78,7 +78,7 @@ export function useMutation< const [, forceUpdate] = React.useState(0) const options = parseMutationArgs(arg1, arg2, arg3) - const queryClient = useQueryClient() + const queryClient = useQueryClient({ context: options.context }) const obsRef = React.useRef< MutationObserver diff --git a/src/reactjs/useQueries.ts b/src/reactjs/useQueries.ts index f25c2511bf..ab30050799 100644 --- a/src/reactjs/useQueries.ts +++ b/src/reactjs/useQueries.ts @@ -1,11 +1,20 @@ import React from 'react' -import { QueryFunction } from '../core/types' +import { QueryKey, QueryFunction } from '../core/types' import { notifyManager } from '../core/notifyManager' import { QueriesObserver } from '../core/queriesObserver' import { useQueryClient } from './QueryClientProvider' import { UseQueryOptions, UseQueryResult } from './types' +// This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. +// - `context` is omitted as it is passed as a root-level option to `useQueries` instead. +type UseQueryOptionsForUseQueries< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey +> = Omit, 'context'> + // Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH = 20 @@ -16,28 +25,33 @@ type GetOptions = error?: infer TError data: infer TData } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends { queryFnData: infer TQueryFnData; error?: infer TError } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends { data: infer TData; error?: infer TError } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData] T extends [infer TQueryFnData, infer TError, infer TData] - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends [infer TQueryFnData, infer TError] - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends [infer TQueryFnData] - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided T extends { queryFn?: QueryFunction select: (data: any) => infer TData } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries : T extends { queryFn?: QueryFunction } - ? UseQueryOptions + ? UseQueryOptionsForUseQueries< + TQueryFnData, + unknown, + TQueryFnData, + TQueryKey + > : // Fallback - UseQueryOptions + UseQueryOptionsForUseQueries type GetResults = // Part 1: responsible for mapping explicit type parameter to function result, if object @@ -73,7 +87,7 @@ export type QueriesOptions< Result extends any[] = [], Depth extends ReadonlyArray = [] > = Depth['length'] extends MAXIMUM_DEPTH - ? UseQueryOptions[] + ? UseQueryOptionsForUseQueries[] : T extends [] ? [] : T extends [infer Head] @@ -84,15 +98,15 @@ export type QueriesOptions< ? T : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type! // use this to infer the param types in the case of Array.map() argument - T extends UseQueryOptions< + T extends UseQueryOptionsForUseQueries< infer TQueryFnData, infer TError, infer TData, infer TQueryKey >[] - ? UseQueryOptions[] + ? UseQueryOptionsForUseQueries[] : // Fallback - UseQueryOptions[] + UseQueryOptionsForUseQueries[] /** * QueriesResults reducer recursively maps type param to results @@ -109,7 +123,7 @@ export type QueriesResults< ? [...Result, GetResults] : T extends [infer Head, ...infer Tail] ? QueriesResults<[...Tail], [...Result, GetResults], [...Depth, 1]> - : T extends UseQueryOptions< + : T extends UseQueryOptionsForUseQueries< infer TQueryFnData, infer TError, infer TData, @@ -122,13 +136,15 @@ export type QueriesResults< export function useQueries({ queries, + context, }: { queries: readonly [...QueriesOptions] + context?: UseQueryOptions['context'] }): QueriesResults { const mountedRef = React.useRef(false) const [, forceUpdate] = React.useState(0) - const queryClient = useQueryClient() + const queryClient = useQueryClient({ context }) const defaultedQueries = React.useMemo( () => From ba782560a715705942fd9137bfb284ac5f0445d9 Mon Sep 17 00:00:00 2001 From: Dominik Dorfmeister Date: Thu, 24 Mar 2022 16:35:54 +0100 Subject: [PATCH 41/76] feat(persistQueryClient): PersistQueryClientProvider (#3248) * feat(persistQueryClient): PersistQueryClientProvider * feat(persistQueryClient): PersistQueryClientProvider defer subscription if we are hydrating * feat(persistQueryClient): PersistQueryClientProvider make sure we do not subscribe if the component unmounts before restoring has finished * feat(persistQueryClient): PersistQueryClientProvider make unsubscribe a const so that we don't mutate what we've exposed * feat(persistQueryClient): PersistQueryClientProvider make hydrating queries go in fetchStatus: 'idle' instead of paused because paused means we have started fetching and are pausing, and we will also continue, while with hydration, we haven't started fetching, and we also might not start if we get "fresh" data from hydration * feat(persistQueryClient): PersistQueryClientProvider don't export IsHydratingProvider, as it shouldn't be needed by consumers * feat(persistQueryClient): PersistQueryClientProvider provide onSuccess and onError callbacks to PersistQueryClientProvider so that you can react to the persisting having finished, to e.g. have a point where you can resumePausedMutations * feat(persistQueryClient): PersistQueryClientProvider tests for onSuccess callback, and remove onError callback, because the persister itself catches errors and removes the store * feat(persistQueryClient): PersistQueryClientProvider test for useQueries * feat(persistQueryClient): PersistQueryClientProvider docs * make restore in mockPersister a bit slower to stabilize tests * better persistQueryClient docs * feat(PersistQueryClientProvider): make sure we can hydrate into multiple clients and error handling * offline example * extract to custom hook * remove onError callback because errors are caught internally by persistQueryClient and the persisted client is then removed * just ignore stale hydrations if the client changes * Revert "just ignore stale hydrations if the client changes" This reverts commit 91e2afb7ba5b08dc86a188d7b9b21d07a8e3c440. * just ignore stale hydrations if the client changes this makes sure we only call onSuccess once, for the "latest" client * since QueryClientProviderProps is now a union type, we can't extend it from an interface --- docs/src/manifests/manifest.json | 5 + docs/src/pages/examples/offline.mdx | 23 + docs/src/pages/plugins/persistQueryClient.md | 62 ++ docs/src/pages/reference/QueryClient.md | 9 + examples/offline/.babelrc | 3 + examples/offline/.eslintrc | 7 + examples/offline/.gitignore | 26 + examples/offline/.prettierrc | 1 + examples/offline/.rescriptsrc.js | 37 ++ examples/offline/README.md | 6 + examples/offline/package.json | 36 ++ examples/offline/public/favicon.ico | 1 + examples/offline/public/index.html | 38 ++ examples/offline/public/manifest.json | 15 + examples/offline/public/mockServiceWorker.js | 338 +++++++++++ examples/offline/src/App.js | 215 +++++++ examples/offline/src/api.js | 67 +++ examples/offline/src/index.js | 16 + examples/offline/src/movies.js | 61 ++ src/core/queryObserver.ts | 5 +- src/core/types.ts | 8 +- .../PersistQueryClientProvider.tsx | 52 ++ src/persistQueryClient/index.ts | 147 +---- src/persistQueryClient/persist.ts | 159 ++++++ .../tests/PersistQueryClientProvider.test.tsx | 538 ++++++++++++++++++ src/reactjs/Hydrate.tsx | 5 + src/reactjs/QueryClientProvider.tsx | 5 +- src/reactjs/index.ts | 2 +- src/reactjs/useBaseQuery.ts | 43 +- src/reactjs/useQueries.ts | 30 +- 30 files changed, 1779 insertions(+), 181 deletions(-) create mode 100644 docs/src/pages/examples/offline.mdx create mode 100644 examples/offline/.babelrc create mode 100644 examples/offline/.eslintrc create mode 100644 examples/offline/.gitignore create mode 100644 examples/offline/.prettierrc create mode 100644 examples/offline/.rescriptsrc.js create mode 100644 examples/offline/README.md create mode 100644 examples/offline/package.json create mode 100644 examples/offline/public/favicon.ico create mode 100644 examples/offline/public/index.html create mode 100644 examples/offline/public/manifest.json create mode 100644 examples/offline/public/mockServiceWorker.js create mode 100644 examples/offline/src/App.js create mode 100644 examples/offline/src/api.js create mode 100644 examples/offline/src/index.js create mode 100644 examples/offline/src/movies.js create mode 100644 src/persistQueryClient/PersistQueryClientProvider.tsx create mode 100644 src/persistQueryClient/persist.ts create mode 100644 src/persistQueryClient/tests/PersistQueryClientProvider.test.tsx diff --git a/docs/src/manifests/manifest.json b/docs/src/manifests/manifest.json index ac3a029884..7a6f9d746d 100644 --- a/docs/src/manifests/manifest.json +++ b/docs/src/manifests/manifest.json @@ -326,6 +326,11 @@ "title": "React Native", "path": "/examples/react-native", "editUrl": "/examples/react-native.mdx" + }, + { + "title": "Offline Queries and Mutations", + "path": "/examples/offline", + "editUrl": "/examples/offline.mdx" } ] }, diff --git a/docs/src/pages/examples/offline.mdx b/docs/src/pages/examples/offline.mdx new file mode 100644 index 0000000000..d7dc18a4f7 --- /dev/null +++ b/docs/src/pages/examples/offline.mdx @@ -0,0 +1,23 @@ +--- +id: offline +title: Offline Queries and Mutations +toc: false +--- + +- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-query/tree/alpha/examples/offline) +- [View Source](https://github.com/tannerlinsley/react-query/tree/alpha/examples/offline) + + diff --git a/docs/src/pages/plugins/persistQueryClient.md b/docs/src/pages/plugins/persistQueryClient.md index aa31b064dc..cbd29513f5 100644 --- a/docs/src/pages/plugins/persistQueryClient.md +++ b/docs/src/pages/plugins/persistQueryClient.md @@ -159,6 +159,68 @@ There are actually three interfaces available: - `PersistedQueryClientRestoreOptions` is used for `persistQueryClientRestore` (doesn't use `dehydrateOptions`). - `PersistQueryClientOptions` is used for `persistQueryClient` +## Usage with React + +[persistQueryClient](#persistQueryClient) will try to restore the cache and automatically subscribes to further changes, thus syncing your client to the provided storage. + +However, restoring is asynchronous, because all persisters are async by nature, which means that if you render your App while you are restoring, you might get into race conditions if a query mounts and fetches at the same time. + +Further, if you subscribe to changes outside of the React component lifecycle, you have no way of unsubscribing: + +```js +// 🚨 never unsubscribes from syncing +persistQueryClient({ + queryClient, + persister: localStoragePersister, +}) + +// 🚨 happens at the same time as restoring +ReactDOM.render(, rootElement) +``` + +### PeristQueryClientProvider + +For this use-case, you can use the `PersistQueryClientProvider`. It will make sure to subscribe / unsubscribe correctly according to the React component lifecycle, and it will also make sure that queries will not start fetching while we are still restoring. Queries will still render though, they will just be put into `fetchingState: 'idle'` until data has been restored. Then, they will refetch unless the restored data is _fresh_ enough, and _initialData_ will also be respected. It can be used _instead of_ the normal [QueryClientProvider](../reference/QueryClientProvider): + +```jsx + +import { PersistQueryClientProvider } from 'react-query/persistQueryClient' +import { createWebStoragePersister } from 'react-query/createWebStoragePersister' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + cacheTime: 1000 * 60 * 60 * 24, // 24 hours + }, + }, +}) + +const persister = createWebStoragePersister({ + storage: window.localStorage, +}) + +ReactDOM.render( + + + , + rootElement +) +``` + +#### Props + +`PersistQueryClientProvider` takes the same props as [QueryClientProvider](../reference/QueryClientProvider), and additionally: + +- `persistOptions: PersistQueryClientOptions` + - all [options](#options) you cann pass to [persistQueryClient](#persistqueryclient) minus the QueryClient itself +- `onSuccess?: () => void` + - optional + - will be called when the initial restore is finished + - can be used to [resumePausedMutations](../reference/QueryClient#queryclientresumepausedmutations) + ## Persisters ### Persisters Interface diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md index 3e4c2daf74..2996693544 100644 --- a/docs/src/pages/reference/QueryClient.md +++ b/docs/src/pages/reference/QueryClient.md @@ -49,6 +49,7 @@ Its available methods are: - [`queryClient.getQueryCache`](#queryclientgetquerycache) - [`queryClient.getMutationCache`](#queryclientgetmutationcache) - [`queryClient.clear`](#queryclientclear) +- - [`queryClient.resumePausedMutations`](#queryclientresumepausedmutations) **Options** @@ -563,3 +564,11 @@ The `clear` method clears all connected caches. ```js queryClient.clear() ``` + +## `queryClient.resumePausedMutations` + +Can be used to resume mutations that have been paused because there was no network connection. + +```js +queryClient.resumePausedMutations() +``` diff --git a/examples/offline/.babelrc b/examples/offline/.babelrc new file mode 100644 index 0000000000..c14b2828d1 --- /dev/null +++ b/examples/offline/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react-app"] +} diff --git a/examples/offline/.eslintrc b/examples/offline/.eslintrc new file mode 100644 index 0000000000..404725ad66 --- /dev/null +++ b/examples/offline/.eslintrc @@ -0,0 +1,7 @@ +{ + "extends": ["react-app", "prettier"], + "rules": { + // "eqeqeq": 0, + // "jsx-a11y/anchor-is-valid": 0 + } +} diff --git a/examples/offline/.gitignore b/examples/offline/.gitignore new file mode 100644 index 0000000000..613b2de638 --- /dev/null +++ b/examples/offline/.gitignore @@ -0,0 +1,26 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +yarn.lock +package-lock.json + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/examples/offline/.prettierrc b/examples/offline/.prettierrc new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/examples/offline/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/examples/offline/.rescriptsrc.js b/examples/offline/.rescriptsrc.js new file mode 100644 index 0000000000..433b258d85 --- /dev/null +++ b/examples/offline/.rescriptsrc.js @@ -0,0 +1,37 @@ +const path = require("path"); +const resolveFrom = require("resolve-from"); + +const fixLinkedDependencies = (config) => { + config.resolve = { + ...config.resolve, + alias: { + ...config.resolve.alias, + react$: resolveFrom(path.resolve("node_modules"), "react"), + "react-dom$": resolveFrom(path.resolve("node_modules"), "react-dom"), + }, + }; + return config; +}; + +const includeSrcDirectory = (config) => { + config.resolve = { + ...config.resolve, + modules: [path.resolve("src"), ...config.resolve.modules], + }; + return config; +}; + +const allowOutsideSrc = (config) => { + config.resolve.plugins = config.resolve.plugins.filter( + (p) => p.constructor.name !== "ModuleScopePlugin" + ); + return config; +}; + +module.exports = [ + ["use-babel-config", ".babelrc"], + ["use-eslint-config", ".eslintrc"], + fixLinkedDependencies, + allowOutsideSrc, + // includeSrcDirectory, +]; diff --git a/examples/offline/README.md b/examples/offline/README.md new file mode 100644 index 0000000000..b168d3c4b1 --- /dev/null +++ b/examples/offline/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` or `yarn` +- `npm run start` or `yarn start` diff --git a/examples/offline/package.json b/examples/offline/package.json new file mode 100644 index 0000000000..ce0f6a88ce --- /dev/null +++ b/examples/offline/package.json @@ -0,0 +1,36 @@ +{ + "private": true, + "scripts": { + "start": "rescripts start", + "build": "rescripts build", + "test": "rescripts test", + "eject": "rescripts eject" + }, + "dependencies": { + "@tanstack/react-location": "^3.7.0", + "ky": "^0.30.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-hot-toast": "^2.2.0", + "react-query": "^4.0.0-alpha.19", + "react-scripts": "3.0.1" + }, + "devDependencies": { + "@rescripts/cli": "^0.0.11", + "@rescripts/rescript-use-babel-config": "^0.0.8", + "@rescripts/rescript-use-eslint-config": "^0.0.9", + "babel-eslint": "10.0.1" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/examples/offline/public/favicon.ico b/examples/offline/public/favicon.ico new file mode 100644 index 0000000000..41cff3b9db --- /dev/null +++ b/examples/offline/public/favicon.ico @@ -0,0 +1 @@ +https://rawcdn.githack.com/tannerlinsley/react-query/master/examples/simple/public/favicon.ico \ No newline at end of file diff --git a/examples/offline/public/index.html b/examples/offline/public/index.html new file mode 100644 index 0000000000..dd1ccfd4cd --- /dev/null +++ b/examples/offline/public/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + React App + + + +
+ + + diff --git a/examples/offline/public/manifest.json b/examples/offline/public/manifest.json new file mode 100644 index 0000000000..1f2f141faf --- /dev/null +++ b/examples/offline/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/offline/public/mockServiceWorker.js b/examples/offline/public/mockServiceWorker.js new file mode 100644 index 0000000000..ba0c013b83 --- /dev/null +++ b/examples/offline/public/mockServiceWorker.js @@ -0,0 +1,338 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (0.39.1). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '02f4ad4a2797f85668baf196e553d929' +const bypassHeaderName = 'x-msw-bypass' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + return self.skipWaiting() +}) + +self.addEventListener('activate', async function (event) { + return self.clients.claim() +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll() + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +// Resolve the "main" client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll() + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: serializeHeaders(clonedResponse.headers), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +async function getResponse(event, client, requestId) { + const { request } = event + const requestClone = request.clone() + const getOriginalResponse = () => fetch(requestClone) + + // Bypass mocking when the request client is not active. + if (!client) { + return getOriginalResponse() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return await getOriginalResponse() + } + + // Bypass requests with the explicit bypass header + if (requestClone.headers.get(bypassHeaderName) === 'true') { + const cleanRequestHeaders = serializeHeaders(requestClone.headers) + + // Remove the bypass header to comply with the CORS preflight check. + delete cleanRequestHeaders[bypassHeaderName] + + const originalRequest = new Request(requestClone, { + headers: new Headers(cleanRequestHeaders), + }) + + return fetch(originalRequest) + } + + // Send the request to the client-side MSW. + const reqHeaders = serializeHeaders(request.headers) + const body = await request.text() + + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: reqHeaders, + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body, + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_SUCCESS': { + return delayPromise( + () => respondWithMock(clientMessage), + clientMessage.payload.delay, + ) + } + + case 'MOCK_NOT_FOUND': { + return getOriginalResponse() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.payload + const networkError = new Error(message) + networkError.name = name + + // Rejecting a request Promise emulates a network error. + throw networkError + } + + case 'INTERNAL_ERROR': { + const parsedBody = JSON.parse(clientMessage.payload.body) + + console.error( + `\ +[MSW] Uncaught exception in the request handler for "%s %s": + +${parsedBody.location} + +This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\ +`, + request.method, + request.url, + ) + + return respondWithMock(clientMessage) + } + } + + return getOriginalResponse() +} + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = uuidv4() + + return event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +function serializeHeaders(headers) { + const reqHeaders = {} + headers.forEach((value, name) => { + reqHeaders[name] = reqHeaders[name] + ? [].concat(reqHeaders[name]).concat(value) + : value + }) + return reqHeaders +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(JSON.stringify(message), [channel.port2]) + }) +} + +function delayPromise(cb, duration) { + return new Promise((resolve) => { + setTimeout(() => resolve(cb()), duration) + }) +} + +function respondWithMock(clientMessage) { + return new Response(clientMessage.payload.body, { + ...clientMessage.payload, + headers: clientMessage.payload.headers, + }) +} + +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = (Math.random() * 16) | 0 + const v = c == 'x' ? r : (r & 0x3) | 0x8 + return v.toString(16) + }) +} diff --git a/examples/offline/src/App.js b/examples/offline/src/App.js new file mode 100644 index 0000000000..41f56b632b --- /dev/null +++ b/examples/offline/src/App.js @@ -0,0 +1,215 @@ +import * as React from "react"; +import "./App.css"; + +import { + useQuery, + QueryClient, + MutationCache, + useMutation, + onlineManager, +} from "react-query"; +import { ReactQueryDevtools } from "react-query/devtools"; +import toast, { Toaster } from "react-hot-toast"; + +import { PersistQueryClientProvider } from "react-query/persistQueryClient"; +import { createWebStoragePersister } from "react-query/createWebStoragePersister"; +import { + Link, + Outlet, + ReactLocation, + Router, + useMatch, +} from "@tanstack/react-location"; + +import * as api from "api"; +import { movieKeys, useMovie } from "./movies"; + +const persister = createWebStoragePersister({ + storage: window.localStorage, +}); + +const location = new ReactLocation(); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + cacheTime: 1000 * 60 * 60 * 24, // 24 hours + staleTime: 2000, + retry: 0, + }, + }, + // configure global cache callbacks to show toast notifications + mutationCache: new MutationCache({ + onSuccess: (data) => { + toast.success(data.message); + }, + onError: (error) => { + toast.error(error.message); + }, + }), +}); + +// we need a default mutation function so that paused mutations can resume after a page reload +queryClient.setMutationDefaults(movieKeys.all(), { + mutationFn: async ({ id, comment }) => { + // to avoid clashes with our optimistic update when an offline mutation continues + await queryClient.cancelQueries(movieKeys.detail(id)); + return api.updateMovie(id, comment); + }, +}); + +export default function App() { + return ( + { + // resume mutations after initial restore from localStorage was successful + queryClient.resumePausedMutations().then(() => { + queryClient.invalidateQueries(); + }); + }} + > + , + }, + { + path: ":movieId", + element: , + errorElement: , + loader: ({ params: { movieId } }) => + queryClient.getQueryData(movieKeys.detail(movieId)) ?? + // do not load if we are offline because it returns a promise that is pending until we go online again + // we just let the Detail component handle it + (onlineManager.isOnline() + ? queryClient.fetchQuery(movieKeys.detail(movieId), () => + api.fetchMovie(movieId) + ) + : undefined), + }, + ]} + > + + + + + + ); +} + +function List() { + const moviesQuery = useQuery(movieKeys.list(), api.fetchMovies); + + if (moviesQuery.isLoading && moviesQuery.isFetching) { + return "Loading..."; + } + + if (moviesQuery.data) { + return ( +
+

Movies

+

+ Try to mock offline behaviour with the button in the devtools. You can + navigate around as long as there is already data in the cache. You'll + get a refetch as soon as you go online again. +

+
    + {moviesQuery.data.movies.map((movie) => ( +
  • + + {movie.title} + +
  • + ))} +
+
+ Updated at: {new Date(moviesQuery.data.ts).toLocaleTimeString()} +
+
{moviesQuery.isFetching && "fetching..."}
+
+ ); + } + + // query will be in 'idle' fetchStatus while restoring from localStorage + return null; +} + +function MovieError() { + const { error } = useMatch(); + + return ( +
+ Back +

Couldn't load movie!

+
{error.message}
+
+ ); +} + +function Detail() { + const { + params: { movieId }, + } = useMatch(); + const { comment, setComment, updateMovie, movieQuery } = useMovie(movieId); + + if (movieQuery.isLoading && movieQuery.isFetching) { + return "Loading..."; + } + + function submitForm(event) { + event.preventDefault(); + + updateMovie.mutate({ + id: movieId, + comment, + }); + } + + if (movieQuery.data) { + return ( +
+ Back +

Movie: {movieQuery.data.movie.title}

+

+ Try to mock offline behaviour with the button in the devtools, then + update the comment. The optimistic update will succeed, but the actual + mutation will be paused and resumed once you go online again. +

+

+ You can also reload the page, which will make the persisted mutation + resume, as you will be online again when you "come back". +

+

+